diff --git a/package.json b/package.json
index 8ba5f3983..a955eebfe 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,8 @@
"build-frontend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/collapsible_widget.js",
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
"webpack": "npx webpack -c webpack-desktop.config.js && npx webpack -c webpack-mobile.config.js && npx webpack -c webpack-setup.config.js",
- "test": "jasmine"
+ "test": "jasmine",
+ "test-es6": "node -r esm spec-es6/attribute_parser.spec.js "
},
"dependencies": {
"async-mutex": "0.2.2",
diff --git a/spec-es6/attribute_parser.spec.js b/spec-es6/attribute_parser.spec.js
index a69118589..5c16d513a 100644
--- a/spec-es6/attribute_parser.spec.js
+++ b/spec-es6/attribute_parser.spec.js
@@ -1,10 +1,62 @@
import attributeParser from '../src/public/app/services/attribute_parser.js';
import {describe, it, expect, execute} from './mini_test.js';
-describe("Lexer fulltext", () => {
+describe("Lexer", () => {
it("simple label", () => {
expect(attributeParser.lexer("#label")).toEqual(["#label"]);
});
+
+ it("label with value", () => {
+ expect(attributeParser.lexer("#label=Hallo")).toEqual(["#label", "=", "Hallo"]);
+ });
+
+ it("relation with value", () => {
+ expect(attributeParser.lexer('~relation=note')).toEqual(["~relation", "=", "#root/RclIpMauTOKS/NFi2gL4xtPxM"]);
+ });
+
+ it("use quotes to define value", () => {
+ expect(attributeParser.lexer("#'label a'='hello\"` world'"))
+ .toEqual(["#label a", "=", 'hello"` world']);
+
+ expect(attributeParser.lexer('#"label a" = "hello\'` world"'))
+ .toEqual(["#label a", "=", "hello'` world"]);
+
+ expect(attributeParser.lexer('#`label a` = `hello\'" world`'))
+ .toEqual(["#label a", "=", "hello'\" world"]);
+ });
+});
+
+describe("Parser", () => {
+ it("simple label", () => {
+ const attrs = attributeParser.parser(["#token"]);
+
+ expect(attrs.length).toEqual(1);
+ expect(attrs[0].type).toEqual('label');
+ expect(attrs[0].name).toEqual('token');
+ expect(attrs[0].value).toBeFalsy();
+ });
+
+ it("label with value", () => {
+ const attrs = attributeParser.parser(["#token", "=", "val"]);
+
+ expect(attrs.length).toEqual(1);
+ expect(attrs[0].type).toEqual('label');
+ expect(attrs[0].name).toEqual('token');
+ expect(attrs[0].value).toEqual("val");
+ });
+
+ it("relation", () => {
+ const attrs = attributeParser.parser(["~token", "=", "#root/RclIpMauTOKS/NFi2gL4xtPxM"]);
+
+ expect(attrs.length).toEqual(1);
+ expect(attrs[0].type).toEqual('relation');
+ expect(attrs[0].name).toEqual("token");
+ expect(attrs[0].value).toEqual('#root/RclIpMauTOKS/NFi2gL4xtPxM');
+ });
+
+ it("error cases", () => {
+ expect(() => attributeParser.parser(["~token"])).toThrow('Relation "~token" should point to a note.');
+ });
});
execute();
diff --git a/spec-es6/mini_test.js b/spec-es6/mini_test.js
index 58b743115..525cdc50b 100644
--- a/spec-es6/mini_test.js
+++ b/spec-es6/mini_test.js
@@ -25,6 +25,36 @@ export function expect(val) {
errorCount++;
}
+ },
+ toBeFalsy: () => {
+ if (!!val) {
+ console.trace("toBeFalsy failed.");
+ console.error(`expected: null, false, undefined, 0 or empty string`);
+ console.error(`got: ${val}`);
+
+ errorCount++;
+ }
+ },
+ toThrow: errorMessage => {
+ try {
+ val();
+ }
+ catch (e) {
+ if (e.message !== errorMessage) {
+ console.trace("toThrow caught exception, but messages differ");
+ console.error(`expected: ${errorMessage}`);
+ console.error(`got: ${e.message}`);
+
+ errorCount++;
+ }
+
+ return;
+ }
+
+ console.trace("toThrow did not catch any exception.");
+ console.error(`expected: ${errorMessage}`);
+ console.error(`got: [none]`);
+ errorCount++;
}
}
}
diff --git a/src/public/app/services/attribute_parser.js b/src/public/app/services/attribute_parser.js
index dd99b0eb0..3b5aa4c29 100644
--- a/src/public/app/services/attribute_parser.js
+++ b/src/public/app/services/attribute_parser.js
@@ -1,5 +1,9 @@
+function preprocessRelations(str) {
+ return str.replace(/]+href="(#root[A-Za-z0-9/]*)"[^>]*>[^<]*<\/a>/g, "$1");
+}
+
function lexer(str) {
- str = str.toLowerCase();
+ str = preprocessRelations(str);
const expressionTokens = [];
@@ -95,6 +99,55 @@ function lexer(str) {
return expressionTokens;
}
-export default {
- lexer
+function parser(tokens) {
+ const attrs = [];
+
+ for (let i = 0; i < tokens.length; i++) {
+ const token = tokens[i];
+
+ if (token.startsWith('#')) {
+ const attr = {
+ type: 'label',
+ name: token.substr(1)
+ };
+
+ if (tokens[i + 1] === "=") {
+ if (i + 2 >= tokens.length) {
+ throw new Error(`Missing value for label "${token}"`);
+ }
+
+ i += 2;
+
+ attr.value = tokens[i];
+ }
+
+ attrs.push(attr);
+ }
+ else if (token.startsWith('~')) {
+ const attr = {
+ type: 'relation',
+ name: token.substr(1)
+ };
+
+ if (i + 2 >= tokens.length || tokens[i + 1] !== '=') {
+ throw new Error(`Relation "${token}" should point to a note.`);
+ }
+
+ i += 2;
+
+ attr.value = tokens[i];
+
+ attrs.push(attr);
+ }
+ else {
+ throw new Error(`Unrecognized attribute "${token}"`);
+ }
+ }
+
+ return attrs;
+}
+
+export default {
+ lexer,
+ parser
}
diff --git a/src/services/note_cache/note_cache_service.js b/src/services/note_cache/note_cache_service.js
index 8007c9cb3..972683a40 100644
--- a/src/services/note_cache/note_cache_service.js
+++ b/src/services/note_cache/note_cache_service.js
@@ -2,6 +2,7 @@
const noteCache = require('./note_cache');
const hoistedNoteService = require('../hoisted_note');
+const protectedSessionService = require('../protected_session');
const stringSimilarity = require('string-similarity');
function isNotePathArchived(notePath) {