mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	attribute parser preserves indexes from original string
This commit is contained in:
		
							
								
								
									
										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) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user