Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

External Workspaces #1418

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/user/features/workspaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Workspaces

It is possible to choose between two basic types of workspaces for the notes and templates:

1. **"internal" VSCode workspace:** Watch and access the note and template folders within "internal" workspace (exclusive VSCode workspace)
2. **"external" workspace:** Watch and access the note and template folders within "external" workspace ("external" with respect to VSCode workspace; INFO: this may include folders from the "internal" VSCode workspace)

The switch may be done via the config. property `foam.files.workspaceType` with possible values `[
"internal",
"external",
"combined"
]`.

This feature allows to decouple the essential foam workspace from the VSCode workspace and provides better flexibility and less workflow complexity, particularly when working with multiple VSCode workspaces and projects.

The "external" and absolute note folder paths to be watched can be defined via the config. property
`foam.files.externalWatchPaths` with the path to be set within an array.
The external and absolute template root directory path may be defined via the config. property `foam.files.externalTemplatesRoot` .

**NOTE:** The "external" path definitions may be defined such that either only a respective root directory or, in addition, also a glob pattern is provided.
4 changes: 4 additions & 0 deletions packages/foam-vscode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ With references you can also make your notes navigable both in GitHub UI as well
- This becomes very powerful when combined with [note templates](https://foambubble.github.io/foam/user/features/note-templates) and the `Foam: Create New Note from Template` command
- See your workspace as a connected graph with the `Foam: Show Graph` command

### Workspaces

- Explore how to work with either the "internal" VSCode workspace or an "external" one: [workspaces](https://foambubble.github.io/foam/user/features/workspaces) .

## Recipes

People use Foam in different ways for different use cases, check out the [recipes](https://foambubble.github.io/foam/user/recipes/recipes) page for inspiration!
Expand Down
29 changes: 29 additions & 0 deletions packages/foam-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,35 @@
"Use the directory of the file in the current editor"
]
},
"foam.files.workspaceType": {
"type": "string",
"default": "internal",
"description": "Specifies the 'workspace' type",
"enum": [
"internal",
"external",
"combined"
],
"enumDescriptions": [
"Watch and access the note and template folders within 'internal' workspace (exclusive VSCode workspace)",
"Watch and access the note and template folders within 'external' workspace ('external' with respect to VSCode workspace; INFO: this may include folders from 'internal' workspace)",
"POTENTIALLY DEPRECATED: Watch note folders of both, 'internal' and 'external' workspace"
]
},
"foam.files.externalWatchPaths": {
"type": "array",
"items": {
"type": "string",
"default": "**/*"
},
"default": [],
"description": "The array of root paths to be watched by the note file watcher"
},
"foam.files.externalTemplatesRoot": {
"type": "string",
"default": "",
"description": "'External' (as to VSCode workspace) root path for note templates"
},
"foam.logging.level": {
"type": "string",
"default": "info",
Expand Down
32 changes: 17 additions & 15 deletions packages/foam-vscode/src/core/model/foam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface Foam extends IDisposable {

export const bootstrap = async (
matcher: IMatcher,
watcher: IWatcher | undefined,
watchers: IWatcher[] | undefined,
dataStore: IDataStore,
parser: ResourceParser,
initialProviders: ResourceProvider[],
Expand All @@ -48,20 +48,22 @@ export const bootstrap = async (
ms => Logger.info(`Tags loaded in ${ms}ms`)
);

watcher?.onDidChange(async uri => {
if (matcher.isMatch(uri)) {
await workspace.fetchAndSet(uri);
}
});
watcher?.onDidCreate(async uri => {
await matcher.refresh();
if (matcher.isMatch(uri)) {
await workspace.fetchAndSet(uri);
}
});
watcher?.onDidDelete(uri => {
workspace.delete(uri);
});
for (const watcher of watchers) {
watcher?.onDidChange(async uri => {
if (matcher.isMatch(uri)) {
await workspace.fetchAndSet(uri);
}
});
watcher?.onDidCreate(async uri => {
await matcher.refresh();
if (matcher.isMatch(uri)) {
await workspace.fetchAndSet(uri);
}
});
watcher?.onDidDelete(uri => {
workspace.delete(uri);
});
}

const foam: Foam = {
workspace,
Expand Down
86 changes: 54 additions & 32 deletions packages/foam-vscode/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*global markdownit:readonly*/

import { workspace, ExtensionContext, window, commands } from 'vscode';
import { externalGlobPattern } from './utils/vsc-utils';
import { MarkdownResourceProvider } from './core/services/markdown-provider';
import { bootstrap } from './core/model/foam';
import { Logger } from './core/utils/log';
Expand All @@ -11,6 +12,8 @@ import {
getAttachmentsExtensions,
getIgnoredFilesSetting,
getNotesExtensions,
getExternalWatchPaths,
getWorkspaceType
} from './settings';
import { AttachmentResourceProvider } from './core/services/attachment-provider';
import { VsCodeWatcher } from './services/watcher';
Expand All @@ -36,16 +39,31 @@ export async function activate(context: ExtensionContext) {
const { matcher, dataStore, excludePatterns } =
await createMatcherAndDataStore(excludes);

Logger.info('Loading from directories:');
for (const folder of workspace.workspaceFolders) {
Logger.info('- ' + folder.uri.fsPath);
Logger.info(' Include: **/*');
Logger.info(' Exclude: ' + excludePatterns.get(folder.name).join(','));
let watchers: VsCodeWatcher[] = [];

const workspaceType = getWorkspaceType();
if(workspaceType == 'internal' || workspaceType == 'combined'){
Logger.info('Loading from directories:');
for (const folder of workspace.workspaceFolders) {
Logger.info('- ' + folder.uri.fsPath);
Logger.info(' Include: **/*');
Logger.info(' Exclude: ' + excludePatterns.get(folder.name).join(','));
}

const watcher = new VsCodeWatcher(
workspace.createFileSystemWatcher('**/*')
);
watchers.push(watcher);
}
if(workspaceType == 'external' || workspaceType == 'combined') {
for (const folder of getExternalWatchPaths()) {
const watcher = new VsCodeWatcher(
workspace.createFileSystemWatcher(externalGlobPattern(folder))
);
watchers.push(watcher);
}
}

const watcher = new VsCodeWatcher(
workspace.createFileSystemWatcher('**/*')
);

const parserCache = new VsCodeBasedParserCache(context);
const parser = createMarkdownParser([], parserCache);

Expand All @@ -64,7 +82,7 @@ export async function activate(context: ExtensionContext) {

const foamPromise = bootstrap(
matcher,
watcher,
watchers,
dataStore,
parser,
[markdownProvider, attachmentProvider],
Expand All @@ -79,29 +97,33 @@ export async function activate(context: ExtensionContext) {
const foam = await foamPromise;
Logger.info(`Loaded ${foam.workspace.list().length} resources`);

context.subscriptions.push(
foam,
watcher,
markdownProvider,
attachmentProvider,
commands.registerCommand('foam-vscode.clear-cache', () =>
parserCache.clear()
),
workspace.onDidChangeConfiguration(e => {
if (
[
'foam.files.ignore',
'foam.files.attachmentExtensions',
'foam.files.noteExtensions',
'foam.files.defaultNoteExtension',
].some(setting => e.affectsConfiguration(setting))
) {
window.showInformationMessage(
'Foam: Reload the window to use the updated settings'
);
}
})
const clearCacheRegCmdDisposable = commands.registerCommand('foam-vscode.clear-cache', () =>
parserCache.clear()
);
const onDidChangeConfigDisposable = workspace.onDidChangeConfiguration(e => {
if (
[
'foam.files.ignore',
'foam.files.attachmentExtensions',
'foam.files.noteExtensions',
'foam.files.defaultNoteExtension',
].some(setting => e.affectsConfiguration(setting))
) {
window.showInformationMessage(
'Foam: Reload the window to use the updated settings'
);
}
});
for (const watcher of watchers) {
context.subscriptions.push(
foam,
watcher,
markdownProvider,
attachmentProvider,
clearCacheRegCmdDisposable,
onDidChangeConfigDisposable
);
}

const feats = (await Promise.all(featuresPromises)).filter(r => r != null);

Expand Down
67 changes: 51 additions & 16 deletions packages/foam-vscode/src/services/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
WorkspaceEdit,
MarkdownString,
} from 'vscode';
import { externalGlobPattern } from '../utils/vsc-utils';
import { getExcerpt, stripFrontMatter, stripImages } from '../core/utils/md';
import { isSome } from '../core/utils/core';
import { fromVsCodeUri, toVsCodeUri } from '../utils/vsc-utils';
Expand All @@ -25,6 +26,13 @@ import {
IDataStore,
IMatcher,
} from '../core/services/datastore';
import {
getExternalWatchPaths,
getExternalTemplatesRoot,
getWorkspaceType
} from '../settings';
import { externalRelativePatternRootPath } from '../utils/vsc-utils';


interface SelectionInfo {
document: TextDocument;
Expand Down Expand Up @@ -183,13 +191,27 @@ export function deleteFile(uri: URI) {
* @param uri the uri to evaluate
* @returns an absolute uri
*/
export function asAbsoluteWorkspaceUri(uri: URI): URI {
if (workspace.workspaceFolders === undefined) {
throw new Error('An open folder or workspace is required');
export function asAbsoluteWorkspaceUri(uri: URI): URI {
let folders: URI[] = [];
const workspaceType = getWorkspaceType();
if(workspaceType == 'internal'){
if (workspace.workspaceFolders === undefined) {
throw new Error('An open folder or workspace is required');
}
folders = workspace.workspaceFolders.map(folder =>
fromVsCodeUri(folder.uri)
);
} else {
for (const folder of getExternalWatchPaths()) {
folders.push(
fromVsCodeUri(
Uri.file(externalRelativePatternRootPath(folder)
)
)
);
}
}
const folders = workspace.workspaceFolders.map(folder =>
fromVsCodeUri(folder.uri)
);

const res = asAbsoluteUri(uri, folders);
return res;
}
Expand Down Expand Up @@ -218,17 +240,30 @@ export async function createMatcherAndDataStore(excludes: string[]): Promise<{

const listFiles = async () => {
let files: Uri[] = [];
for (const folder of workspace.workspaceFolders) {
const uris = await workspace.findFiles(
new RelativePattern(folder.uri, '**/*'),
new RelativePattern(
folder.uri,
`{${excludePatterns.get(folder.name).join(',')}}`
)
);
files = [...files, ...uris];
}

const workspaceType = getWorkspaceType();
if(workspaceType == 'internal' || workspaceType == 'combined'){
//--- collect files of internal VSCode workspace
for (const folder of workspace.workspaceFolders) {
const uris = await workspace.findFiles(
new RelativePattern(folder.uri, '**/*'),
new RelativePattern(
folder.uri,
`{${excludePatterns.get(folder.name).join(',')}}`
)
);
files = [...files, ...uris];
}
}
if(workspaceType == 'external' || workspaceType == 'combined') {
//--- collect files of external foam workspace
for (const folder of getExternalWatchPaths()) {
const uris = await workspace.findFiles(
externalGlobPattern(folder)
);
files = [...files, ...uris];
}
}
return files.map(fromVsCodeUri);
};

Expand Down
Loading