mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
attribute parser preserves indexes from original string
This commit is contained in:
parent
f245d51746
commit
ef1d062745
2
libraries/ckeditor/ckeditor.js
vendored
2
libraries/ckeditor/ckeditor.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -3,32 +3,38 @@ import {describe, it, expect, execute} from './mini_test.js';
|
|||||||
|
|
||||||
describe("Lexer", () => {
|
describe("Lexer", () => {
|
||||||
it("simple label", () => {
|
it("simple label", () => {
|
||||||
expect(attributeParser.lexer("#label")).toEqual(["#label"]);
|
expect(attributeParser.lexer("#label").map(t => t.text))
|
||||||
|
.toEqual(["#label"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("label with value", () => {
|
it("label with value", () => {
|
||||||
expect(attributeParser.lexer("#label=Hallo")).toEqual(["#label", "=", "Hallo"]);
|
expect(attributeParser.lexer("#label=Hallo").map(t => t.text))
|
||||||
|
.toEqual(["#label", "=", "Hallo"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("relation with value", () => {
|
it("relation with value", () => {
|
||||||
expect(attributeParser.lexer('~relation=<a class="reference-link" href="#root/RclIpMauTOKS/NFi2gL4xtPxM" data-note-path="root/RclIpMauTOKS/NFi2gL4xtPxM">note</a>')).toEqual(["~relation", "=", "#root/RclIpMauTOKS/NFi2gL4xtPxM"]);
|
expect(attributeParser.lexer('~relation=<a class="reference-link" href="#root/RclIpMauTOKS/NFi2gL4xtPxM" data-note-path="root/RclIpMauTOKS/NFi2gL4xtPxM">note</a>').map(t => t.text))
|
||||||
|
.toEqual(["~relation", "=", "#root/RclIpMauTOKS/NFi2gL4xtPxM"]);
|
||||||
|
|
||||||
|
expect(attributeParser.lexer('~relation=<a class="reference-link" id="abc" href="#NFi2gL4xtPxM">note</a>').map(t => t.text))
|
||||||
|
.toEqual(["~relation", "=", "#NFi2gL4xtPxM"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("use quotes to define value", () => {
|
it("use quotes to define value", () => {
|
||||||
expect(attributeParser.lexer("#'label a'='hello\"` world'"))
|
expect(attributeParser.lexer("#'label a'='hello\"` world'").map(t => t.text))
|
||||||
.toEqual(["#label a", "=", 'hello"` world']);
|
.toEqual(["#label a", "=", 'hello"` world']);
|
||||||
|
|
||||||
expect(attributeParser.lexer('#"label a" = "hello\'` world"'))
|
expect(attributeParser.lexer('#"label a" = "hello\'` world"').map(t => t.text))
|
||||||
.toEqual(["#label a", "=", "hello'` world"]);
|
.toEqual(["#label a", "=", "hello'` world"]);
|
||||||
|
|
||||||
expect(attributeParser.lexer('#`label a` = `hello\'" world`'))
|
expect(attributeParser.lexer('#`label a` = `hello\'" world`').map(t => t.text))
|
||||||
.toEqual(["#label a", "=", "hello'\" world"]);
|
.toEqual(["#label a", "=", "hello'\" world"]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Parser", () => {
|
describe("Parser", () => {
|
||||||
it("simple label", () => {
|
it("simple label", () => {
|
||||||
const attrs = attributeParser.parser(["#token"]);
|
const attrs = attributeParser.parser(["#token"].map(t => ({text: t})));
|
||||||
|
|
||||||
expect(attrs.length).toEqual(1);
|
expect(attrs.length).toEqual(1);
|
||||||
expect(attrs[0].type).toEqual('label');
|
expect(attrs[0].type).toEqual('label');
|
||||||
@ -37,7 +43,7 @@ describe("Parser", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("label with value", () => {
|
it("label with value", () => {
|
||||||
const attrs = attributeParser.parser(["#token", "=", "val"]);
|
const attrs = attributeParser.parser(["#token", "=", "val"].map(t => ({text: t})));
|
||||||
|
|
||||||
expect(attrs.length).toEqual(1);
|
expect(attrs.length).toEqual(1);
|
||||||
expect(attrs[0].type).toEqual('label');
|
expect(attrs[0].type).toEqual('label');
|
||||||
@ -46,16 +52,24 @@ describe("Parser", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("relation", () => {
|
it("relation", () => {
|
||||||
const attrs = attributeParser.parser(["~token", "=", "#root/RclIpMauTOKS/NFi2gL4xtPxM"]);
|
let attrs = attributeParser.parser(["~token", "=", "#root/RclIpMauTOKS/NFi2gL4xtPxM"].map(t => ({text: t})));
|
||||||
|
|
||||||
expect(attrs.length).toEqual(1);
|
expect(attrs.length).toEqual(1);
|
||||||
expect(attrs[0].type).toEqual('relation');
|
expect(attrs[0].type).toEqual('relation');
|
||||||
expect(attrs[0].name).toEqual("token");
|
expect(attrs[0].name).toEqual("token");
|
||||||
expect(attrs[0].value).toEqual('#root/RclIpMauTOKS/NFi2gL4xtPxM');
|
expect(attrs[0].value).toEqual('NFi2gL4xtPxM');
|
||||||
|
|
||||||
|
attrs = attributeParser.parser(["~token", "=", "#NFi2gL4xtPxM"].map(t => ({text: t})));
|
||||||
|
|
||||||
|
expect(attrs.length).toEqual(1);
|
||||||
|
expect(attrs[0].type).toEqual('relation');
|
||||||
|
expect(attrs[0].name).toEqual("token");
|
||||||
|
expect(attrs[0].value).toEqual('NFi2gL4xtPxM');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("error cases", () => {
|
it("error cases", () => {
|
||||||
expect(() => attributeParser.parser(["~token"])).toThrow('Relation "~token" should point to a note.');
|
expect(() => attributeParser.parser(["~token"].map(t => ({text: t}))))
|
||||||
|
.toThrow('Relation "~token" should point to a note.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ function preprocess(str) {
|
|||||||
function lexer(str) {
|
function lexer(str) {
|
||||||
str = preprocess(str);
|
str = preprocess(str);
|
||||||
|
|
||||||
const expressionTokens = [];
|
const tokens = [];
|
||||||
|
|
||||||
let quotes = false;
|
let quotes = false;
|
||||||
let currentWord = '';
|
let currentWord = '';
|
||||||
@ -33,12 +33,19 @@ function lexer(str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishWord() {
|
/**
|
||||||
|
* @param endIndex - index of the last character of the token
|
||||||
|
*/
|
||||||
|
function finishWord(endIndex) {
|
||||||
if (currentWord === '') {
|
if (currentWord === '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
expressionTokens.push(currentWord);
|
tokens.push({
|
||||||
|
text: currentWord,
|
||||||
|
startIndex: endIndex - currentWord.length,
|
||||||
|
endIndex: endIndex
|
||||||
|
});
|
||||||
|
|
||||||
currentWord = '';
|
currentWord = '';
|
||||||
}
|
}
|
||||||
@ -61,7 +68,7 @@ function lexer(str) {
|
|||||||
else if (['"', "'", '`'].includes(chr)) {
|
else if (['"', "'", '`'].includes(chr)) {
|
||||||
if (!quotes) {
|
if (!quotes) {
|
||||||
if (previousOperatorSymbol()) {
|
if (previousOperatorSymbol()) {
|
||||||
finishWord();
|
finishWord(i - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
quotes = chr;
|
quotes = chr;
|
||||||
@ -69,7 +76,7 @@ function lexer(str) {
|
|||||||
else if (quotes === chr) {
|
else if (quotes === chr) {
|
||||||
quotes = false;
|
quotes = false;
|
||||||
|
|
||||||
finishWord();
|
finishWord(i - 1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// it's a quote but within other kind of quotes so it's valid as a literal character
|
// it's a quote but within other kind of quotes so it's valid as a literal character
|
||||||
@ -84,17 +91,11 @@ function lexer(str) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (chr === ' ') {
|
else if (chr === ' ') {
|
||||||
finishWord();
|
finishWord(i - 1);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (['(', ')', '.'].includes(chr)) {
|
|
||||||
finishWord();
|
|
||||||
currentWord += chr;
|
|
||||||
finishWord();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (previousOperatorSymbol() !== isOperatorSymbol(chr)) {
|
else if (previousOperatorSymbol() !== isOperatorSymbol(chr)) {
|
||||||
finishWord();
|
finishWord(i - 1);
|
||||||
|
|
||||||
currentWord += chr;
|
currentWord += chr;
|
||||||
continue;
|
continue;
|
||||||
@ -104,44 +105,46 @@ function lexer(str) {
|
|||||||
currentWord += chr;
|
currentWord += chr;
|
||||||
}
|
}
|
||||||
|
|
||||||
finishWord();
|
finishWord(str.length - 1);
|
||||||
|
|
||||||
return expressionTokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parser(tokens) {
|
function parser(tokens) {
|
||||||
const attrs = [];
|
const attrs = [];
|
||||||
|
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
const token = tokens[i];
|
const {text, startIndex, endIndex} = tokens[i];
|
||||||
|
|
||||||
if (token.startsWith('#')) {
|
if (text.startsWith('#')) {
|
||||||
const attr = {
|
const attr = {
|
||||||
type: 'label',
|
type: 'label',
|
||||||
name: token.substr(1),
|
name: text.substr(1),
|
||||||
isInheritable: false // FIXME
|
isInheritable: false, // FIXME
|
||||||
|
startIndex,
|
||||||
|
endIndex
|
||||||
};
|
};
|
||||||
|
|
||||||
if (tokens[i + 1] === "=") {
|
if (i + 1 < tokens.length && tokens[i + 1].text === "=") {
|
||||||
if (i + 2 >= tokens.length) {
|
if (i + 2 >= tokens.length) {
|
||||||
throw new Error(`Missing value for label "${token}"`);
|
throw new Error(`Missing value for label "${text}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 2;
|
i += 2;
|
||||||
|
|
||||||
attr.value = tokens[i];
|
attr.value = tokens[i].text;
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs.push(attr);
|
attrs.push(attr);
|
||||||
}
|
}
|
||||||
else if (token.startsWith('~')) {
|
else if (text.startsWith('~')) {
|
||||||
if (i + 2 >= tokens.length || tokens[i + 1] !== '=') {
|
if (i + 2 >= tokens.length || tokens[i + 1].text !== '=') {
|
||||||
throw new Error(`Relation "${token}" should point to a note.`);
|
throw new Error(`Relation "${text}" should point to a note.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 2;
|
i += 2;
|
||||||
|
|
||||||
let notePath = tokens[i];
|
let notePath = tokens[i].text;
|
||||||
if (notePath.startsWith("#")) {
|
if (notePath.startsWith("#")) {
|
||||||
notePath = notePath.substr(1);
|
notePath = notePath.substr(1);
|
||||||
}
|
}
|
||||||
@ -150,15 +153,17 @@ function parser(tokens) {
|
|||||||
|
|
||||||
const attr = {
|
const attr = {
|
||||||
type: 'relation',
|
type: 'relation',
|
||||||
name: token.substr(1),
|
name: text.substr(1),
|
||||||
isInheritable: false, // FIXME
|
isInheritable: false, // FIXME
|
||||||
value: noteId
|
value: noteId,
|
||||||
|
startIndex,
|
||||||
|
endIndex
|
||||||
};
|
};
|
||||||
|
|
||||||
attrs.push(attr);
|
attrs.push(attr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new Error(`Unrecognized attribute "${token}"`);
|
throw new Error(`Unrecognized attribute "${text}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +114,11 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
|||||||
// display of $widget in both branches.
|
// display of $widget in both branches.
|
||||||
this.$widget.show();
|
this.$widget.show();
|
||||||
|
|
||||||
|
this.$editor.on("click", () => {
|
||||||
|
const pos = this.textEditor.model.document.selection.getFirstPosition();
|
||||||
|
console.log(pos.textNode && pos.textNode.data, pos.parent.textNode && pos.parent.textNode.data, pos.offset);
|
||||||
|
});
|
||||||
|
|
||||||
this.textEditor = await BalloonEditor.create(this.$editor[0], {
|
this.textEditor = await BalloonEditor.create(this.$editor[0], {
|
||||||
removePlugins: [
|
removePlugins: [
|
||||||
'Enter',
|
'Enter',
|
||||||
@ -168,6 +173,9 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate());
|
this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate());
|
||||||
|
|
||||||
|
await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector.js');
|
||||||
|
CKEditorInspector.attach(this.textEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadReferenceLinkTitle(noteId, $el) {
|
async loadReferenceLinkTitle(noteId, $el) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user