Compare commits

..

38 Commits

Author SHA1 Message Date
Jani Eväkallio
08cfbed2ba Add WikiDocumentLinkProvider 2020-07-13 22:52:09 +01:00
Jani Eväkallio
26b162d540 Add NoteLink.position to keep track of link ranges 2020-07-13 22:45:05 +01:00
Riccardo
3c5a81e7d6 Merge pull request #102 from foambubble/config-and-bootstrap
factored features from bootstrap and introduced config
2020-07-13 13:02:52 +02:00
Jani Eväkallio
9a3e2a0b10 Random associations 2020-07-12 19:25:05 +01:00
Riccardo Ferretti
49c6da07f0 factored features from bootstrap and introduced config
extracted create references feature in own file
updated bootstrap sequence to include feature specific initialization
introduced config object
2020-07-12 18:26:30 +02:00
Jani Eväkallio
75431c89ba Add missing all-contributors 2020-07-12 16:54:33 +01:00
Ayush Baweja
9e064be3f0 Add custom CSS recipe for Markdown preview (#101)
Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-12 16:49:03 +01:00
Ankit Tiwari
2b3f351330 Add VS Code Marketplace shield badges (#99) 2020-07-12 16:36:10 +01:00
Ankit Tiwari
1c01c266d9 Remove references to foam-workspace-manager (#100) 2020-07-12 16:35:11 +01:00
Jani Eväkallio
2501638a55 v0.2.0 2020-07-12 15:16:58 +01:00
Jani Eväkallio
20ae1f2ad9 Prepare for 0.2.0 release 2020-07-12 15:16:28 +01:00
Jani Eväkallio
a94110898b Add lerna 2020-07-12 14:50:43 +01:00
Riccardo
3191d7ddf3 Merge pull request #83 from foambubble/spike-core-graph
Factored out graph operations and moved some code into core
2020-07-12 13:12:31 +02:00
Jani Eväkallio
ab097e10d7 Handle undefined Note nodes gracefully 2020-07-12 11:34:47 +01:00
Jani Eväkallio
d09ccdbd29 Don't generate reference headers if file has no wiki links' 2020-07-12 11:19:32 +01:00
Jani Eväkallio
df3b657d58 Cleanup: Remove unused imports 2020-07-12 10:38:54 +01:00
Jani Eväkallio
f1424c3a73 Formatting: Run prettier 2020-07-12 10:37:12 +01:00
Ankit Tiwari
086884ff5c Fix all contributors badge (#97) 2020-07-12 09:18:07 +01:00
Saurav Khdoolia
05cbdc1c04 Update Contributor Image Link 🧐 in Readme (#96) 2020-07-12 08:21:02 +01:00
Riccardo Ferretti
5b423852d6 Update docs/meeting-notes/foam-core-2020-07-11.md with notes from PR 2020-07-12 02:43:53 +02:00
Riccardo Ferretti
e1ac54c7e2 renamed core.ts => note-graph.ts 2020-07-12 02:39:05 +02:00
Riccardo Ferretti
8bbf9727f6 renamed packages
renamed @foam/core => foam-core and @foam/cli => foam-cli (see https://github.com/foambubble/foam/pull/83#discussion_r453232485)
2020-07-12 02:39:05 +02:00
Riccardo Ferretti
debe94882f update note in graph when saving file 2020-07-12 02:39:05 +02:00
Riccardo Ferretti
639edafe52 extracted foam-core and created @foam modules 2020-07-12 02:38:05 +02:00
Jani Eväkallio
d7425f88e4 foam-core: Meeting notes 2020-07-12 02:36:57 +02:00
Riccardo Ferretti
99d49a0120 using more sensible names
renaming Foam => NoteGraph, Bubble => Note, and related renaming
2020-07-12 02:36:57 +02:00
Riccardo Ferretti
e9150fb8ea minor adjustments 2020-07-12 02:36:57 +02:00
Riccardo Ferretti
57c34db6df factored out graph operations and moved some code into core 2020-07-12 02:36:57 +02:00
Jani Eväkallio
3bdf90c8b4 Setup structure for blogs 2020-07-11 14:52:07 +01:00
Jani Eväkallio
566d85ec62 Reduce all-contributors image size 2020-07-11 14:24:48 +01:00
Jani Eväkallio
be704d7494 Fix external links 2020-07-11 14:24:39 +01:00
Jani Eväkallio
3c3da97190 Integrate all-contributors 2020-07-11 14:11:28 +01:00
Simon Knott
0cdb875221 Add prettify-links recipe (#69)
Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-11 08:00:06 +01:00
Janne Ojanaho
f2efd14186 Changed to unifiedjs-based markdown parsing (#78) 2020-07-11 07:59:05 +01:00
Jani Eväkallio
b86edc4660 make-backlinks-more-prominent: Further suggestion 2020-07-10 07:55:10 +01:00
Jani Eväkallio
652dc7d845 Merge branch 'master' of github.com:foambubble/foam 2020-07-10 07:40:09 +01:00
Jani Eväkallio
d5fd5410f0 Docs: Make backlinks more prominent 2020-07-10 07:40:08 +01:00
Jani Eväkallio
efb913028f Merge pull request #81 from foambubble/sort-link-references
Sorting link references
2020-07-09 13:08:20 +01:00
62 changed files with 4488 additions and 877 deletions

192
.all-contributorsrc Normal file
View 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
View File

@@ -1,4 +1,7 @@
node_modules
.DS_Store
.vscode-test/
*.tsbuildinfo
*.vsix
*.log
dist

9
.vscode/launch.json vendored
View File

@@ -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"
}
]
}

View File

@@ -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();

View File

@@ -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();

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 KiB

View 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
![Demo](./assets/images/prettify-links-demo.gif)
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)

View File

@@ -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"

View File

@@ -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
View 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"

View File

@@ -0,0 +1,3 @@
# Three Weeks In
Todo

View File

@@ -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.

View 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"]
}
```

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View 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:
![Demo of dragging and dropping the pane](assets/images/demo-backlinks-explorer.gif)
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"

View 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"

View File

@@ -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 kitchen sink, showing a few of the key features](assets/images/foam-features-dark-mode-demo.png)
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"

View File

@@ -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"

View File

@@ -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
View File

@@ -0,0 +1,8 @@
{
"packages": [
"packages/*"
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.2.0"
}

View File

@@ -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"
}
}

View File

@@ -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.

View File

@@ -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"
},

View File

@@ -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'));

View File

@@ -10,5 +10,6 @@
},
"include": [
"src/**/*"
]
],
"references": [{ "path": "../foam-core" }]
}

View File

@@ -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

View File

@@ -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"
}

View 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,
})

View 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[];
}

View 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,
});

View File

@@ -0,0 +1,5 @@
export function dropExtension(path: string): string {
const parts = path.split('.');
parts.pop();
return parts.join('.');
}

View 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']);
});
});

View 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',
]);
});
});

View File

@@ -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",
},
}

View File

@@ -1,4 +1,3 @@
out
node_modules
.vscode-test/
*.vsix

View File

@@ -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

View File

@@ -1,5 +1,11 @@
# foam-vscode
[![Version](https://vsmarketplacebadge.apphb.com/version/foam.foam-vscode.svg)](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
[![Downloads](https://img.shields.io/visual-studio-marketplace/d/foam.foam-vscode)](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
[![Installs](https://img.shields.io/visual-studio-marketplace/i/foam.foam-vscode)](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
[![Ratings](https://img.shields.io/visual-studio-marketplace/r/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.

View File

@@ -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"
}
}

View File

@@ -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 {};
};

View 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
];

View File

@@ -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;

View File

@@ -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
View File

@@ -0,0 +1,6 @@
import { ExtensionContext } from "vscode";
import { Foam } from "foam-core";
export interface FoamFeature {
activate: (context: ExtensionContext, foamPromise: Promise<Foam>) => void
}

View 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(".");
}

View File

@@ -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);
});
})
);
})();

View File

@@ -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"
}]
}

View File

@@ -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

View File

@@ -1,4 +0,0 @@
*.log
.DS_Store
node_modules
dist

View File

@@ -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.

View File

@@ -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');
}
*/
}

View File

@@ -1 +0,0 @@
export { WorkspaceManager, Note, NoteWithLinks } from './WorkspaceManager';

View File

@@ -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, '&lt; omit in toc &gt;') //// 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);
}

View File

@@ -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);
}

View File

@@ -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']);
});
*/
});

View File

@@ -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 -->
[![All Contributors](https://img.shields.io/badge/all_contributors-19-orange.svg?style=flat-square)](#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
View 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"]
}
}
}

3096
yarn.lock

File diff suppressed because it is too large Load Diff