Compare commits

...

12 Commits

Author SHA1 Message Date
Riccardo Ferretti
af0c2bbaa3 v0.13.3 2021-05-11 21:40:56 +02:00
Riccardo Ferretti
700bfc1b26 addressing windows issue with provider matching (related to #617) 2021-05-11 21:25:25 +02:00
Riccardo Ferretti
a6d5c04453 improve bootstrap performance
we wait to create the graph, as changes to the workspace will cause it to be recomputed.
so, first load all resources from the initial providers, then compute the graph.
2021-05-11 21:22:19 +02:00
Riccardo Ferretti
b0c42cead2 setting logging level to error for all tests 2021-05-11 21:12:38 +02:00
Riccardo Ferretti
6c643adb9d Prepare for 0.13.3 2021-05-09 23:13:27 +02:00
Riccardo Ferretti
ca7fdefaae improvemets in matcher and linting 2021-05-09 23:13:27 +02:00
Riccardo Ferretti
149d5f5a7c fix #617 - fixed error in file matching in MarkdownProvider 2021-05-09 22:50:42 +02:00
Michael Overmeyer
be80857fd1 Handle users cancelling "Create New Note" commands (#623) 2021-05-09 22:50:18 +02:00
Michael Overmeyer
611fa7359d Completely ignore unknown Foam variables (#622)
If we don't know it we shouldn't touch it
Previously it replaced the variable with the variables name
2021-05-09 18:23:06 +02:00
Riccardo Ferretti
08b7e7a231 fixed isMarkdown function to check the .md extension (related to #617) 2021-05-07 23:53:14 +02:00
Riccardo Ferretti
0a259168c7 fix #618: properly printing file name 2021-05-07 22:58:44 +02:00
Riccardo Ferretti
f3d0569c76 fixed Barabazs contributor name 2021-05-06 12:26:22 +02:00
20 changed files with 155 additions and 52 deletions

View File

@@ -4,5 +4,5 @@
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.13.2"
"version": "0.13.3"
}

View File

@@ -1,7 +1,7 @@
{
"name": "foam-core",
"repository": "https://github.com/foambubble/foam",
"version": "0.13.2",
"version": "0.13.3",
"license": "MIT",
"files": [
"dist"

View File

@@ -16,6 +16,8 @@ export const bootstrap = async (
config.ignoreGlobs
);
const workspace = new FoamWorkspace();
await Promise.all(initialProviders.map(p => workspace.registerProvider(p)));
const graph = FoamGraph.fromWorkspace(workspace, true);
const foam: Foam = {
@@ -33,7 +35,5 @@ export const bootstrap = async (
},
};
await Promise.all(initialProviders.map(p => workspace.registerProvider(p)));
return foam;
};

View File

@@ -70,6 +70,6 @@ const parseConfig = (path: URI) => {
try {
return JSON.parse(readFileSync(URI.toFsPath(path), 'utf8'));
} catch {
Logger.debug('Could not read configuration from ' + path);
Logger.debug('Could not read configuration from ' + URI.toString(path));
}
};

View File

@@ -50,16 +50,16 @@ export class MarkdownResourceProvider implements ResourceProvider {
const filesByFolder = await Promise.all(
this.matcher.include.map(glob => this.dataStore.list(glob))
);
const files = this.matcher.match(filesByFolder.flat());
const files = this.matcher
.match(filesByFolder.flat())
.filter(this.supports);
await Promise.all(
files.map(async uri => {
Logger.info('Found: ' + URI.toString(uri));
if (this.match(uri)) {
const content = await this.dataStore.read(uri);
if (isSome(content)) {
workspace.set(this.parser.parse(uri, content));
}
const content = await this.dataStore.read(uri);
if (isSome(content)) {
workspace.set(this.parser.parse(uri, content));
}
})
);
@@ -67,26 +67,26 @@ export class MarkdownResourceProvider implements ResourceProvider {
this.disposables =
this.watcherInit?.({
onDidChange: async uri => {
if (this.matcher.isMatch(uri)) {
if (this.matcher.isMatch(uri) && this.supports(uri)) {
const content = await this.dataStore.read(uri);
isSome(content) &&
workspace.set(await this.parser.parse(uri, content));
}
},
onDidCreate: async uri => {
if (this.matcher.isMatch(uri)) {
if (this.matcher.isMatch(uri) && this.supports(uri)) {
const content = await this.dataStore.read(uri);
isSome(content) &&
workspace.set(await this.parser.parse(uri, content));
}
},
onDidDelete: uri => {
this.matcher.isMatch(uri) && workspace.delete(uri);
this.supports(uri) && workspace.delete(uri);
},
}) ?? [];
}
match(uri: URI) {
supports(uri: URI) {
return URI.isMarkdownFile(uri);
}

View File

@@ -5,7 +5,7 @@ import { FoamWorkspace } from './workspace';
export interface ResourceProvider extends IDisposable {
init: (workspace: FoamWorkspace) => Promise<void>;
match: (uri: URI) => boolean;
supports: (uri: URI) => boolean;
read: (uri: URI) => Promise<string | null>;
readAsMarkdown: (uri: URI) => Promise<string | null>;
fetch: (uri: URI) => Promise<Resource | null>;

View File

@@ -249,7 +249,7 @@ export abstract class URI {
);
}
static isMarkdownFile(uri: URI): boolean {
return uri.path.endsWith('md');
return uri.path.endsWith('.md');
}
}

View File

@@ -139,7 +139,7 @@ export class FoamWorkspace implements IDisposable {
public resolveLink(resource: Resource, link: ResourceLink): URI {
// TODO add tests
const provider = this.providers.find(p => p.match(resource.uri));
const provider = this.providers.find(p => p.supports(resource.uri));
return (
provider?.resolveLink(this, resource, link) ??
URI.placeholder(link.target)
@@ -147,12 +147,12 @@ export class FoamWorkspace implements IDisposable {
}
public read(uri: URI): Promise<string | null> {
const provider = this.providers.find(p => p.match(uri));
const provider = this.providers.find(p => p.supports(uri));
return provider?.read(uri) ?? Promise.resolve(null);
}
public readAsMarkdown(uri: URI): Promise<string | null> {
const provider = this.providers.find(p => p.match(uri));
const provider = this.providers.find(p => p.supports(uri));
return provider?.readAsMarkdown(uri) ?? Promise.resolve(null);
}

View File

@@ -63,9 +63,6 @@ export class Matcher implements IMatcher {
const withFolder = folderPlusGlob(folder);
this.include.push(
...include.map(glob => {
// if (glob.endsWith('*')) {
// glob = `${glob}\\.{md,mdx,markdown}`;
// }
return withFolder(glob);
})
);
@@ -141,5 +138,5 @@ export const folderPlusGlob = (folder: string) => (glob: string): string => {
if (glob.startsWith('/')) {
glob = glob.slice(1);
}
return `${folder}/${glob}`;
return folder.length > 0 ? `${folder}/${glob}` : glob;
};

View File

@@ -61,6 +61,14 @@ describe('Matcher', () => {
expect(matcher.isMatch(files[3])).toEqual(false);
});
it('happy path', () => {
const matcher = new Matcher([URI.file('/')], ['**/*'], ['**/*.pdf']);
expect(matcher.isMatch(URI.file('/file.md'))).toBeTruthy();
expect(matcher.isMatch(URI.file('/file.pdf'))).toBeFalsy();
expect(matcher.isMatch(URI.file('/dir/file.md'))).toBeTruthy();
expect(matcher.isMatch(URI.file('/dir/file.pdf'))).toBeFalsy();
});
it('ignores files in the exclude list', () => {
const matcher = new Matcher([testFolder], ['*.md'], ['file1.*']);
const files = [

View File

@@ -10,7 +10,7 @@ import { Resource } from '../../src/model/note';
import { Range } from '../../src/model/range';
import { MarkdownResourceProvider } from '../../src';
Logger.setLevel('info');
Logger.setLevel('error');
describe('generateLinkReferences', () => {
let _workspace: FoamWorkspace;

View File

@@ -1,6 +1,9 @@
import { Logger } from '../src';
import { URI } from '../src/model/uri';
import { uriToSlug } from '../src/utils/slug';
Logger.setLevel('error');
describe('Foam URIs', () => {
describe('URI parsing', () => {
const base = URI.file('/path/to/file.md');

View File

@@ -4,12 +4,20 @@ 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.13.3] - 2021-05-09
Fixes and Improvements:
- Improved Foam template variables resolution: unknown variables are now ignored (#622 - thanks @movermeyer)
- Fixed file matching in MarkdownProvider (#617)
- Fixed cancelling `Foam: Create New Note` and `Foam: Create New Note From Template` behavior (#623 - thanks @movermeyer)
## [0.13.2] - 2021-05-06
Fixes and Improvements:
- Fixed wikilink completion bug (#592 - thanks @RobinKing)
- Added support for stylable tags (#598 - thanks @Barabas)
- Added support for stylable tags (#598 - thanks @Barabazs)
- Added "Create new note" command (#601 - thanks @movermeyer)
- Fixed navigation from placeholder and orphan panel (#600)

View File

@@ -8,7 +8,7 @@
"type": "git"
},
"homepage": "https://github.com/foambubble/foam",
"version": "0.13.2",
"version": "0.13.3",
"license": "MIT",
"publisher": "foam",
"engines": {
@@ -395,7 +395,7 @@
},
"dependencies": {
"dateformat": "^3.0.3",
"foam-core": "^0.13.2",
"foam-core": "^0.13.3",
"gray-matter": "^4.0.2",
"markdown-it-regex": "^0.2.0",
"micromatch": "^4.0.2",

View File

@@ -20,6 +20,33 @@ describe('createFromTemplate', () => {
});
});
});
describe('create-note-from-default-template', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('can be cancelled while resolving FOAM_TITLE', async () => {
const spy = jest
.spyOn(window, 'showInputBox')
.mockImplementation(jest.fn(() => Promise.resolve(undefined)));
const fileWriteSpy = jest.spyOn(workspace.fs, 'writeFile');
await commands.executeCommand(
'foam-vscode.create-note-from-default-template'
);
expect(spy).toBeCalledWith({
prompt: `Enter a title for the new note`,
value: 'Title of my New Note',
validateInput: expect.anything(),
});
expect(fileWriteSpy).toHaveBeenCalledTimes(0);
});
});
describe('create-new-template', () => {
afterEach(() => {
jest.clearAllMocks();

View File

@@ -1,6 +1,7 @@
import { window } from 'vscode';
import { SnippetString, window } from 'vscode';
import {
resolveFoamVariables,
resolveFoamTemplateVariables,
substituteFoamVariables,
} from './create-from-template';
@@ -11,7 +12,7 @@ describe('substituteFoamVariables', () => {
# \${AnotherVariable:default_value} <-- Unrelated to foam
# \${AnotherVariable:default_value/(.*)/\${1:/upcase}/}} <-- Unrelated to foam
# $AnotherVariable} <-- Unrelated to foam
# #CURRENT_YEAR-\${CURRENT_MONTH}-$CURRENT_DAY <-- Unrelated to foam
# $CURRENT_YEAR-\${CURRENT_MONTH}-$CURRENT_DAY <-- Unrelated to foam
`;
const givenValues = new Map<string, string>();
@@ -87,3 +88,36 @@ describe('resolveFoamVariables', () => {
);
});
});
describe('resolveFoamTemplateVariables', () => {
test('Does nothing for template without Foam-specific variables', async () => {
const input = `
# \${AnotherVariable} <-- Unrelated to foam
# \${AnotherVariable:default_value} <-- Unrelated to foam
# \${AnotherVariable:default_value/(.*)/\${1:/upcase}/}} <-- Unrelated to foam
# $AnotherVariable} <-- Unrelated to foam
# $CURRENT_YEAR-\${CURRENT_MONTH}-$CURRENT_DAY <-- Unrelated to foam
`;
const expectedMap = new Map<string, string>();
const expectedSnippet = new SnippetString(input);
const expected = [expectedMap, expectedSnippet];
expect(await resolveFoamTemplateVariables(input)).toEqual(expected);
});
test('Does nothing for unknown Foam-specific variables', async () => {
const input = `
# $FOAM_FOO
# \${FOAM_FOO}
# \${FOAM_FOO:default_value}
# \${FOAM_FOO:default_value/(.*)/\${1:/upcase}/}}
`;
const expectedMap = new Map<string, string>();
const expectedSnippet = new SnippetString(input);
const expected = [expectedMap, expectedSnippet];
expect(await resolveFoamTemplateVariables(input)).toEqual(expected);
});
});

View File

@@ -18,6 +18,17 @@ const templatesDir = Uri.joinPath(
'templates'
);
export class UserCancelledOperation extends Error {
constructor(message?: string) {
super('UserCancelledOperation');
if (message) {
this.message = message;
}
}
}
const knownFoamVariables = new Set(['FOAM_TITLE']);
const defaultTemplateDefaultText: string = '# ${FOAM_TITLE}'; // eslint-disable-line no-template-curly-in-string
const defaultTemplateUri = Uri.joinPath(templatesDir, 'new-note.md');
@@ -64,16 +75,21 @@ function findFoamVariables(templateText: string): string[] {
output.push(matches[1] || matches[2]);
}
const uniqVariables = [...new Set(output)];
return uniqVariables;
const knownVariables = uniqVariables.filter(x => knownFoamVariables.has(x));
return knownVariables;
}
function resolveFoamTitle() {
return window.showInputBox({
async function resolveFoamTitle() {
const title = await window.showInputBox({
prompt: `Enter a title for the new note`,
value: 'Title of my New Note',
validateInput: value =>
value.trim().length === 0 ? 'Please enter a title' : undefined,
});
if (title === undefined) {
throw new UserCancelledOperation();
}
return title;
}
class Resolver {
promises = new Map<string, Thenable<string>>();
@@ -167,7 +183,7 @@ async function askUserForFilepathConfirmation(
});
}
async function resolveFoamTemplateVariables(
export async function resolveFoamTemplateVariables(
templateText: string
): Promise<[Map<string, string>, SnippetString]> {
const givenValues = new Map<string, string>();
@@ -204,9 +220,18 @@ async function createNoteFromDefaultTemplate(): Promise<void> {
? await workspace.fs.readFile(templateUri).then(bytes => bytes.toString())
: defaultTemplateDefaultText;
const [resolvedValues, templateSnippet] = await resolveFoamTemplateVariables(
templateText
);
let resolvedValues, templateSnippet;
try {
[resolvedValues, templateSnippet] = await resolveFoamTemplateVariables(
templateText
);
} catch (err) {
if (err instanceof UserCancelledOperation) {
return;
} else {
throw err;
}
}
const defaultSlug = resolvedValues.get('FOAM_TITLE') || 'New Note';
const defaultFilename = `${defaultSlug}.md`;
@@ -240,9 +265,18 @@ async function createNoteFromTemplate(
.readFile(templateUri)
.then(bytes => bytes.toString());
const [resolvedValues, templateSnippet] = await resolveFoamTemplateVariables(
templateText
);
let resolvedValues, templateSnippet;
try {
[resolvedValues, templateSnippet] = await resolveFoamTemplateVariables(
templateText
);
} catch (err) {
if (err instanceof UserCancelledOperation) {
return;
} else {
throw err;
}
}
const defaultSlug = resolvedValues.get('FOAM_TITLE') || 'New Note';
const defaultFilename = `${defaultSlug}.md`;

View File

@@ -11,10 +11,13 @@ import {
FoamWorkspace,
Matcher,
MarkdownResourceProvider,
Logger,
} from 'foam-core';
import { TextEncoder } from 'util';
import { toVsCodeUri } from '../utils/vsc-utils';
Logger.setLevel('error');
const position = Range.create(0, 0, 0, 100);
const documentStart = position.start;

View File

@@ -12,14 +12,6 @@ import {
} from './grouped-resources-tree-data-provider';
describe('GroupedResourcesTreeDataProvider', () => {
const isMatch = (
uri: URI,
_: number,
_g: FoamGraph,
workspace: FoamWorkspace
) => {
return workspace.get(uri).title.length === 3;
};
const matchingNote1 = createTestNote({ uri: '/path/ABC.md', title: 'ABC' });
const matchingNote2 = createTestNote({
uri: '/path-bis/XYZ.md',
@@ -39,9 +31,6 @@ describe('GroupedResourcesTreeDataProvider', () => {
.set(matchingNote2)
.set(excludedPathNote)
.set(notMatchingNote);
const graph = FoamGraph.fromWorkspace(workspace);
const dataStore = { read: () => '' } as any;
// Mock config
const config: GroupedResourcesConfig = {

View File

@@ -6,7 +6,7 @@ import {
GroupedResourcesConfig,
GroupedResoucesConfigGroupBy,
} from '../settings';
import { getContainsTooltip, getNoteTooltip, isSome, isNone } from '../utils';
import { getContainsTooltip, getNoteTooltip, isSome } from '../utils';
import { OPEN_COMMAND } from '../features/utility-commands';
import { toVsCodeUri } from './vsc-utils';