From 72906b3ee78a10aec04c6e2ee3e07be47f697960 Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Wed, 19 Dec 2018 16:34:16 +0000 Subject: [PATCH] Corrections to make line highlighting robust, added tests Lines now save correctly with different inputs, making sure that different inputs like enter, delete, paste and where it's deleted stay consistent when saving. Included in the create/update snippet/note tests the structure from lines highlighting saved to the files. --- browser/components/CodeEditor.js | 69 +++++++++++++++++++++++++++++ tests/dataApi/createNote-test.js | 13 +++++- tests/dataApi/createSnippet-test.js | 1 + tests/dataApi/updateNote-test.js | 19 ++++++-- 4 files changed, 96 insertions(+), 6 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 2f4f1c8a..a438009f 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -511,12 +511,50 @@ export default class CodeEditor extends React.Component { handleChange (editor, changeObject) { spellcheck.handleChange(editor, changeObject) + + this.updateHighlight(editor, changeObject) + this.value = editor.getValue() if (this.props.onChange) { this.props.onChange(editor) } } + incrementLines (start, linesAdded, linesRemoved, editor) { + let highlightedLines = editor.options.linesHighlighted + + const totalHighlightedLines = highlightedLines.length + + let offset = linesAdded - linesRemoved + + // Store new items to be added as we're changing the lines + let newLines = [] + + let i = totalHighlightedLines + + while (i--) { + const lineNumber = highlightedLines[i] + + // Interval that will need to be updated + // Between start and (start + offset) remove highlight + if (lineNumber >= start) { + highlightedLines.splice(highlightedLines.indexOf(lineNumber), 1) + + // Lines that need to be relocated + if (lineNumber >= (start + linesRemoved)) { + newLines.push(lineNumber + offset) + } + } + } + + // Adding relocated lines + highlightedLines.push(...newLines) + + if (this.props.onChange) { + this.props.onChange(editor) + } + } + handleHighlight (editor, changeObject) { const lines = editor.options.linesHighlighted @@ -532,6 +570,37 @@ export default class CodeEditor extends React.Component { } } + updateHighlight (editor, changeObject) { + const linesAdded = changeObject.text.length - 1 + const linesRemoved = changeObject.removed.length - 1 + + // If no lines added or removed return + if (linesAdded === 0 && linesRemoved === 0) { + return + } + + let start = changeObject.from.line + + switch (changeObject.origin) { + case '+insert", "undo': + start += 1 + break + + case 'paste': + case '+delete': + case '+input': + if (changeObject.to.ch !== 0 || changeObject.from.ch !== 0) { + start += 1 + } + break + + default: + return + } + + this.incrementLines(start, linesAdded, linesRemoved, editor) + } + moveCursorTo (row, col) {} scrollToLine (event, num) { diff --git a/tests/dataApi/createNote-test.js b/tests/dataApi/createNote-test.js index 47446aab..3606dfd4 100644 --- a/tests/dataApi/createNote-test.js +++ b/tests/dataApi/createNote-test.js @@ -25,13 +25,16 @@ test.serial('Create a note', (t) => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key + const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10)) + const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), snippets: [{ name: faker.system.fileName(), mode: 'text', - content: faker.lorem.lines() + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray }], tags: faker.lorem.words().split(' '), folder: folderKey @@ -42,7 +45,8 @@ test.serial('Create a note', (t) => { type: 'MARKDOWN_NOTE', content: faker.lorem.lines(), tags: faker.lorem.words().split(' '), - folder: folderKey + folder: folderKey, + linesHighlighted: randLinesHighlightedArray } input2.title = input2.content.split('\n').shift() @@ -59,6 +63,7 @@ test.serial('Create a note', (t) => { t.is(storageKey, data1.storage) const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson')) + t.is(input1.title, data1.title) t.is(input1.title, jsonData1.title) t.is(input1.description, data1.description) @@ -71,6 +76,8 @@ test.serial('Create a note', (t) => { t.is(input1.snippets[0].content, jsonData1.snippets[0].content) t.is(input1.snippets[0].name, data1.snippets[0].name) t.is(input1.snippets[0].name, jsonData1.snippets[0].name) + t.deepEqual(input1.snippets[0].linesHighlighted, data1.snippets[0].linesHighlighted) + t.deepEqual(input1.snippets[0].linesHighlighted, jsonData1.snippets[0].linesHighlighted) t.is(storageKey, data2.storage) const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson')) @@ -80,6 +87,8 @@ test.serial('Create a note', (t) => { t.is(input2.content, jsonData2.content) t.is(input2.tags.length, data2.tags.length) t.is(input2.tags.length, jsonData2.tags.length) + t.deepEqual(input2.linesHighlighted, data2.linesHighlighted) + t.deepEqual(input2.linesHighlighted, jsonData2.linesHighlighted) }) }) diff --git a/tests/dataApi/createSnippet-test.js b/tests/dataApi/createSnippet-test.js index f06cf861..638b76ca 100644 --- a/tests/dataApi/createSnippet-test.js +++ b/tests/dataApi/createSnippet-test.js @@ -26,6 +26,7 @@ test.serial('Create a snippet', (t) => { t.is(snippet.name, data.name) t.deepEqual(snippet.prefix, data.prefix) t.is(snippet.content, data.content) + t.deepEqual(snippet.linesHighlighted, data.linesHighlighted) }) }) diff --git a/tests/dataApi/updateNote-test.js b/tests/dataApi/updateNote-test.js index 6043ee1e..da47c30c 100644 --- a/tests/dataApi/updateNote-test.js +++ b/tests/dataApi/updateNote-test.js @@ -26,13 +26,17 @@ test.serial('Update a note', (t) => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key + const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10)) + const randLinesHighlightedArray2 = new Array(15).fill().map(() => Math.round(Math.random() * 15)) + const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), snippets: [{ name: faker.system.fileName(), mode: 'text', - content: faker.lorem.lines() + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray }], tags: faker.lorem.words().split(' '), folder: folderKey @@ -43,7 +47,8 @@ test.serial('Update a note', (t) => { type: 'MARKDOWN_NOTE', content: faker.lorem.lines(), tags: faker.lorem.words().split(' '), - folder: folderKey + folder: folderKey, + linesHighlighted: randLinesHighlightedArray } input2.title = input2.content.split('\n').shift() @@ -53,7 +58,8 @@ test.serial('Update a note', (t) => { snippets: [{ name: faker.system.fileName(), mode: 'text', - content: faker.lorem.lines() + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray2 }], tags: faker.lorem.words().split(' ') } @@ -62,7 +68,8 @@ test.serial('Update a note', (t) => { const input4 = { type: 'MARKDOWN_NOTE', content: faker.lorem.lines(), - tags: faker.lorem.words().split(' ') + tags: faker.lorem.words().split(' '), + linesHighlighted: randLinesHighlightedArray2 } input4.title = input4.content.split('\n').shift() @@ -99,6 +106,8 @@ test.serial('Update a note', (t) => { t.is(input3.snippets[0].content, jsonData1.snippets[0].content) t.is(input3.snippets[0].name, data1.snippets[0].name) t.is(input3.snippets[0].name, jsonData1.snippets[0].name) + t.deepEqual(input3.snippets[0].linesHighlighted, data1.snippets[0].linesHighlighted) + t.deepEqual(input3.snippets[0].linesHighlighted, jsonData1.snippets[0].linesHighlighted) const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson')) t.is(input4.title, data2.title) @@ -107,6 +116,8 @@ test.serial('Update a note', (t) => { t.is(input4.content, jsonData2.content) t.is(input4.tags.length, data2.tags.length) t.is(input4.tags.length, jsonData2.tags.length) + t.deepEqual(input4.linesHighlighted, data2.linesHighlighted) + t.deepEqual(input4.linesHighlighted, jsonData2.linesHighlighted) }) })