From 72caf1bffd891758fe80ccbcc270194a4d454667 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Thu, 5 Sep 2024 23:44:48 +0900 Subject: [PATCH 01/16] WIP: selective heading levels --- example/index.html | 6 +++++- src/js/easymde.js | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/example/index.html b/example/index.html index ce03794..f464984 100644 --- a/example/index.html +++ b/example/index.html @@ -13,7 +13,11 @@ diff --git a/src/js/easymde.js b/src/js/easymde.js index c038d1c..f39c5e7 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -1822,7 +1822,15 @@ function EasyMDE(options) { options.parsingConfig = extend({ highlightFormatting: true, // needed for toggleCodeBlock to detect types of code }, options.parsingConfig || {}); - + if ( options.parsingConfig.headingLevels ) { + var headingLevels = []; + for ( var l = 0, requestedLevels = options.parsingConfig.headingLevels; l < requestedLevels.length; l++ ) { + if ( ! isNaN( requestedLevels[ l ] ) && headingLevels.indexOf( requestedLevels[ l ] ) === -1 ) { + headingLevels[ l ] = parseInt( requestedLevels[ l ], 10 ); + } + } + options.parsingConfig.headingLevels = headingLevels; + } // Merging the insertTexts, with the given options options.insertTexts = extend({}, insertTexts, options.insertTexts || {}); @@ -2190,6 +2198,12 @@ EasyMDE.prototype.render = function (el) { cm.save(); }); } + if (options.parsingConfig.headingLevels) { + var cm = this.codemirror; + cm.on('beforeChange', function (cm, obj) { + console.log( obj ); + }); + } this.gui = {}; From 6af700c333592dbfa7fbc05a9e76a26803176b21 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Fri, 6 Sep 2024 10:50:01 +0900 Subject: [PATCH 02/16] WIP: keyup heading level check --- src/js/easymde.js | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index f39c5e7..f1268aa 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -1829,7 +1829,7 @@ function EasyMDE(options) { headingLevels[ l ] = parseInt( requestedLevels[ l ], 10 ); } } - options.parsingConfig.headingLevels = headingLevels; + options.parsingConfig.headingLevels = headingLevels.sort(); } // Merging the insertTexts, with the given options @@ -2199,9 +2199,28 @@ EasyMDE.prototype.render = function (el) { }); } if (options.parsingConfig.headingLevels) { - var cm = this.codemirror; - cm.on('beforeChange', function (cm, obj) { - console.log( obj ); + this.codemirror.on('beforeChange', function (cm, obj) { + if (!obj || !obj.from || !obj.to) { + return false; + } + if (obj.from.line !== obj.to.line) { + return false; + } + if (!obj.from.ch || !obj.to.ch || obj.to.ch > 6) { + return false; + } + if (!obj.text || obj.text.length !== 1 || obj.text[0] !== ' ') { + return false; + } + var myText = cm.getRange({line: obj.from.line, ch: 0}, {line: obj.to.line, ch: obj.to.ch}); + if (!/^#+$/.test(myText.trim())){ + return false; + } + var allowedHeadlingLevels = cm.options.backdrop.headingLevels, + currHeadlingLevel = (myText.match(/#/g) || []).length; + if (allowedHeadlingLevels.indexOf(currHeadlingLevel) !== -1) { + return false; + } }); } From 9f93fc115c7c03492630dfed91d492bd40d4a484 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Sun, 8 Sep 2024 01:25:33 +0900 Subject: [PATCH 03/16] WIP: Braincracking with headers... --- example/index.html | 2 +- src/js/easymde.js | 176 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 160 insertions(+), 18 deletions(-) diff --git a/example/index.html b/example/index.html index f464984..484ad97 100644 --- a/example/index.html +++ b/example/index.html @@ -15,7 +15,7 @@ diff --git a/src/js/easymde.js b/src/js/easymde.js index f1268aa..199c1ae 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -1825,9 +1825,14 @@ function EasyMDE(options) { if ( options.parsingConfig.headingLevels ) { var headingLevels = []; for ( var l = 0, requestedLevels = options.parsingConfig.headingLevels; l < requestedLevels.length; l++ ) { - if ( ! isNaN( requestedLevels[ l ] ) && headingLevels.indexOf( requestedLevels[ l ] ) === -1 ) { - headingLevels[ l ] = parseInt( requestedLevels[ l ], 10 ); + requestedLevels[ l ] = parseInt( requestedLevels[ l ], 10 ); + if ( isNaN( requestedLevels[ l ] ) || headingLevels.indexOf( requestedLevels[ l ] ) !== -1 ) { + continue; } + if ( requestedLevels[ l ] < 1 && requestedLevels[ l ] > 6 ) { + continue; + } + headingLevels[ l ] = requestedLevels[ l ]; } options.parsingConfig.headingLevels = headingLevels.sort(); } @@ -2199,28 +2204,165 @@ EasyMDE.prototype.render = function (el) { }); } if (options.parsingConfig.headingLevels) { - this.codemirror.on('beforeChange', function (cm, obj) { - if (!obj || !obj.from || !obj.to) { + // If the *headingLevels* argument is present, set our custom modifiers + var makeBiggerHeadline = function(headline, from, to) { + headline = headline || ''; + if (!from || !to) { + return ''; + } + var level = ''; + while (from < to) { + level += '#'; + from++; + } + level += ' '; console.log(level); + return /#/.test(headline) ? headline.replace(/#\s*/, level) : level; + } + var headlineNeedUpdate = function(myCurrHeadline, allowedHeadlingLevels) { + if (!myCurrHeadline || !allowedHeadlingLevels) { return false; } - if (obj.from.line !== obj.to.line) { + if (!/^[#]+/.test(myCurrHeadline.trim())){ return false; } - if (!obj.from.ch || !obj.to.ch || obj.to.ch > 6) { - return false; - } - if (!obj.text || obj.text.length !== 1 || obj.text[0] !== ' ') { - return false; - } - var myText = cm.getRange({line: obj.from.line, ch: 0}, {line: obj.to.line, ch: obj.to.ch}); - if (!/^#+$/.test(myText.trim())){ - return false; - } - var allowedHeadlingLevels = cm.options.backdrop.headingLevels, - currHeadlingLevel = (myText.match(/#/g) || []).length; + var currHeadlingLevel = ((myCurrHeadline || '').match(/#/g) || []).length; if (allowedHeadlingLevels.indexOf(currHeadlingLevel) !== -1) { return false; } + var newHeadingLevel = 0, n = 0; + while (n < allowedHeadlingLevels.length) { + if (allowedHeadlingLevels[n] > currHeadlingLevel) { + newHeadingLevel = allowedHeadlingLevels[n]; + break; + } + n++; + } + if (!newHeadingLevel) { + n = allowedHeadlingLevels.length - 1; + while (n > -1) { + if (allowedHeadlingLevels[n] < currHeadlingLevel) { + newHeadingLevel = allowedHeadlingLevels[n]; + break; + } + n--; + } + } + if (!newHeadingLevel || newHeadingLevel === currHeadlingLevel) { + return false; + } + return { + from: currHeadlingLevel, + to: newHeadingLevel, + }; + }; + var checkNewHeadline = function(cm, obj) { + var myHeadline = cm.getRange({ + line: obj.from.line, + ch: 0, + }, { + line: obj.to.line, + ch: obj.to.ch, + }); + var levels = headlineNeedUpdate(myHeadline, cm.options.backdrop.headingLevels); + if (!levels || !levels.from || !levels.to) { + return false; + } + if (obj.from.line === obj.to.line) { + // Most simple case when a modification has occured on a single line + if (levels.to > levels.from) { + // Current level is forbidden so we jump to the closest upper level allowed + // We only need to update the text value before the modification is applied + obj.text[0] = makeBiggerHeadline('', levels.from, levels.to); + } + else { + // The current level is forbidden and we jump to the closest lower level available + // A bit more complicated: we have to cancel the update and trigger a replacement with the existing string + obj.cancel(); + var newHeadline = ''; + while (levels.to > 0) { + newHeadline += '#'; + levels.to--; + } + newHeadline += ' '; + cm.doc.replaceRange(newHeadline, { + line: obj.from.line, + ch: 0, + }, { + line: obj.to.line, + ch: obj.to.ch, + }, myHeadline ); + } + } + return true; + }; + var checkExistingHeadline = function(cm, obj) { + var myChar = cm.getRange({ + line: obj.from.line, + ch: obj.from.ch, + }, { + line: obj.to.line, + ch: obj.to.ch + 1, + }); + console.log( '==' + myChar + '==' ); + if (!/\s|#/.test(myChar || '')) { + return false; + } + var myText = cm.getRange({line: obj.from.line, ch: 0}, {line: obj.to.line, ch: 8}); + console.log( myText ); + if ((obj.from.line === obj.to.line) && obj.text.length === 1 && obj.text[0] === '#') { + if (!/[^\s#]/.test(myText)) { + // Newly created, skip the check for now + return false; + } + var levels = headlineNeedUpdate(myText.replace(/#/, '##'), cm.options.backdrop.headingLevels); + if (!levels || !levels.from || !levels.to) { + return false; + } + if (levels.to > levels.from) { + obj.text[0] = '#'; + while (levels.from < levels.to) { + obj.text[0] += '#'; + levels.from++; + } + return true; + } + } + }; + this.codemirror.on('beforeChange', function (cm, obj) { + console.log(obj); + if (!obj || !obj.from || !obj.to || !obj.text) { + // Don't go further 'cause a required argument is missing... + return false; + } + if ((obj.from.line === obj.to.line) && obj.to.ch > 6) { + // No need to trigger a check if we are on the same line at character 7 or upper + // As we are sure the cursor is not inside a markdown header + return false; + } + if (/input/.test(obj.origin || '+input')) { // Something was added + if (!obj.text.length || !obj.text[0].length) { + // Just in case + return false; + } + if (obj.text.length === 1 && obj.text[0].length === 1) { + // Only one character in one line is being updated + if (obj.text[0] === ' ') { + return checkNewHeadline(cm, obj); + } + else if (obj.text[0] === '#') { + return checkExistingHeadline(cm, obj); + } + else { + return false; + } + } + else { + + } + } + else if (/delete/.test(obj.origin)) { // Something was removed + return checkExistingHeadline(cm, obj); + } }); } From 93c60bcf282da3aab85e6205f2e76aaaaf7e5608 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Sun, 8 Sep 2024 23:07:09 +0900 Subject: [PATCH 04/16] WIP: Selective header levels filter working with single line / character input --- src/js/easymde.js | 153 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 107 insertions(+), 46 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index 199c1ae..8e73d6e 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -2215,29 +2215,33 @@ EasyMDE.prototype.render = function (el) { level += '#'; from++; } - level += ' '; console.log(level); + level += ' '; return /#/.test(headline) ? headline.replace(/#\s*/, level) : level; - } - var headlineNeedUpdate = function(myCurrHeadline, allowedHeadlingLevels) { + }; + var headlineNeedUpdate = function(myCurrHeadline, allowedHeadlingLevels, levelSearchDir) { if (!myCurrHeadline || !allowedHeadlingLevels) { return false; } - if (!/^[#]+/.test(myCurrHeadline.trim())){ + myCurrHeadline = myCurrHeadline.trim(); + if (!/^#+/.test(myCurrHeadline)){ return false; } - var currHeadlingLevel = ((myCurrHeadline || '').match(/#/g) || []).length; + var currHeadlingLevel = (myCurrHeadline.match(/^([#]+)/g) || [''])[0].length; if (allowedHeadlingLevels.indexOf(currHeadlingLevel) !== -1) { return false; } - var newHeadingLevel = 0, n = 0; - while (n < allowedHeadlingLevels.length) { - if (allowedHeadlingLevels[n] > currHeadlingLevel) { - newHeadingLevel = allowedHeadlingLevels[n]; - break; + levelSearchDir = levelSearchDir || 'asc'; + var newHeadingLevel = -1, n = 0; + if (levelSearchDir === 'asc') { + while (n < allowedHeadlingLevels.length) { + if (allowedHeadlingLevels[n] > currHeadlingLevel) { + newHeadingLevel = allowedHeadlingLevels[n]; + break; + } + n++; } - n++; } - if (!newHeadingLevel) { + if (newHeadingLevel < 0) { n = allowedHeadlingLevels.length - 1; while (n > -1) { if (allowedHeadlingLevels[n] < currHeadlingLevel) { @@ -2246,13 +2250,20 @@ EasyMDE.prototype.render = function (el) { } n--; } + if (levelSearchDir === 'dsc') { + currHeadlingLevel += 1; + if (newHeadingLevel < 0) { + newHeadingLevel = 0; + } + } } - if (!newHeadingLevel || newHeadingLevel === currHeadlingLevel) { + if (newHeadingLevel < 0 || newHeadingLevel === currHeadlingLevel) { return false; } return { from: currHeadlingLevel, to: newHeadingLevel, + diff: Math.abs(newHeadingLevel - currHeadlingLevel), }; }; var checkNewHeadline = function(cm, obj) { @@ -2271,12 +2282,12 @@ EasyMDE.prototype.render = function (el) { // Most simple case when a modification has occured on a single line if (levels.to > levels.from) { // Current level is forbidden so we jump to the closest upper level allowed - // We only need to update the text value before the modification is applied + // We only need to reset the sharp numbers with the appropriate value before the modification is applied obj.text[0] = makeBiggerHeadline('', levels.from, levels.to); } else { // The current level is forbidden and we jump to the closest lower level available - // A bit more complicated: we have to cancel the update and trigger a replacement with the existing string + // A bit more is needed: we have to cancel the requested update and trigger a replacement with the existing sharp signs obj.cancel(); var newHeadline = ''; while (levels.to > 0) { @@ -2290,7 +2301,7 @@ EasyMDE.prototype.render = function (el) { }, { line: obj.to.line, ch: obj.to.ch, - }, myHeadline ); + }, myHeadline ); // 4th arguments to keep a trace in the history when possible } } return true; @@ -2303,27 +2314,66 @@ EasyMDE.prototype.render = function (el) { line: obj.to.line, ch: obj.to.ch + 1, }); - console.log( '==' + myChar + '==' ); + // console.log( '==' + myChar + '==' ); if (!/\s|#/.test(myChar || '')) { + // Don't bother to go further if no headlines were detected return false; } - var myText = cm.getRange({line: obj.from.line, ch: 0}, {line: obj.to.line, ch: 8}); - console.log( myText ); - if ((obj.from.line === obj.to.line) && obj.text.length === 1 && obj.text[0] === '#') { - if (!/[^\s#]/.test(myText)) { - // Newly created, skip the check for now - return false; - } - var levels = headlineNeedUpdate(myText.replace(/#/, '##'), cm.options.backdrop.headingLevels); - if (!levels || !levels.from || !levels.to) { - return false; - } - if (levels.to > levels.from) { - obj.text[0] = '#'; - while (levels.from < levels.to) { - obj.text[0] += '#'; - levels.from++; + if ((obj.from.line === obj.to.line) && obj.text.length === 1) { + var myLevels, myText = cm.getRange({line: obj.from.line, ch: 0}, {line: obj.to.line, ch: 8}); + if (/input/.test(obj.origin) && obj.text[0] === '#') { + if (!/[^\s#]/.test(myText)) { + // Newly created, skip the check for now + return false; } + if (!/#/.test(myText)) { + myText = '# ' + myText.trim(); // Wasn't headline + } + else { + myText = myText.replace(/#/, '##'); // Increment one sharp sign + }; + myLevels = headlineNeedUpdate(myText, cm.options.backdrop.headingLevels); + if (!myLevels) { + return false; + } + if (myLevels.to < myLevels.from) { + obj.cancel(); + return false; + } + obj.text[0] = '#'; + while (myLevels.from < myLevels.to) { + obj.text[0] += '#'; + myLevels.from++; + } + return true; + } + else if (/delete/.test(obj.origin) && obj.text[0] === '') { + myLevels = headlineNeedUpdate(myText.replace(/#/, ''), cm.options.backdrop.headingLevels, 'dsc'); + if (!myLevels) { + return false; + } + obj.cancel(); + if (myLevels.to > myLevels.from) { + return false; + } + obj.text[0] = ''; + while (myLevels.to > 0) { + obj.text[0] += '#'; + myLevels.to--; + } + var newText = myText.replace(new RegExp('^#' + '{' + myLevels.from + '}'), obj.text[0]); + cm.doc.replaceRange(newText.replace(/^\s*/, ''), { + line: obj.from.line, + ch: 0, + }, { + line: obj.to.line, + ch: 8, + }); + // Be gentle and set back the cursor at the appropriate position + cm.doc.setCursor({ + line: obj.to.line, + ch: obj.to.ch - myLevels.diff, + }); return true; } } @@ -2334,18 +2384,29 @@ EasyMDE.prototype.render = function (el) { // Don't go further 'cause a required argument is missing... return false; } - if ((obj.from.line === obj.to.line) && obj.to.ch > 6) { - // No need to trigger a check if we are on the same line at character 7 or upper - // As we are sure the cursor is not inside a markdown header - return false; - } - if (/input/.test(obj.origin || '+input')) { // Something was added - if (!obj.text.length || !obj.text[0].length) { - // Just in case + if ((obj.from.line === obj.to.line) ) { + if (obj.to.ch > 6) { + // No need to trigger a check if we are on the same line at character 7 or upper + // As we are sure the cursor is not inside a range containing a sharp sign return false; } + if (obj.from.ch === 0 && obj.to.ch === 0 && /\s|\b/.test(obj.text[0] || '')) { + // Prevent space at the beginning of the line + obj.cancel(); + return false; + } + } + if (!obj.text.length) { + // Just in case + return false; + } + if (!obj.origin) { + // Just in case + obj.origin = '+none'; + } + if (/input/.test(obj.origin)) { // Something was added if (obj.text.length === 1 && obj.text[0].length === 1) { - // Only one character in one line is being updated + // Only one character on one line is being updated if (obj.text[0] === ' ') { return checkNewHeadline(cm, obj); } @@ -2356,12 +2417,12 @@ EasyMDE.prototype.render = function (el) { return false; } } - else { - - } } else if (/delete/.test(obj.origin)) { // Something was removed - return checkExistingHeadline(cm, obj); + if (obj.text.length === 1 && !obj.text[0].length) { + // Only one character on one line has been removed + return checkExistingHeadline(cm, obj); + } } }); } From 6d0174fd4648335930264dea79ebcd19ea149f08 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Thu, 12 Sep 2024 09:41:10 +0900 Subject: [PATCH 05/16] WIP: Working prototype with selective levels --- src/js/easymde.js | 139 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 110 insertions(+), 29 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index 8e73d6e..f574d7e 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -2205,18 +2205,51 @@ EasyMDE.prototype.render = function (el) { } if (options.parsingConfig.headingLevels) { // If the *headingLevels* argument is present, set our custom modifiers - var makeBiggerHeadline = function(headline, from, to) { + var headlineMakeBigger = function(headline, from, to) { headline = headline || ''; - if (!from || !to) { + if (!from || !to || from >= to) { return ''; } var level = ''; - while (from < to) { - level += '#'; - from++; + if (!headline.length) { + while (from < to) { + level += '#'; + from++; + } + level += ' '; + return level; + } + else { + while (to > 0) { + level += '#'; + to--; + } + level += ' '; + return /#/.test(headline) ? headline.replace(/^[#]+\s*/, level) : level; + } + }; + var headlineMakeSmaller = function(headline, from, to) { + headline = headline || ''; + if (!from || !to || from <= to) { + return ''; + } + var level = ''; + if (!headline.length) { + while (from > to) { + level += '#'; + from--; + } + level += ' '; + return level; + } + else { + while (to > 0) { + level += '#'; + to--; + } + level += ' '; + return /#/.test(headline) ? headline.replace(/^[#]+\s*/, level) : level; } - level += ' '; - return /#/.test(headline) ? headline.replace(/#\s*/, level) : level; }; var headlineNeedUpdate = function(myCurrHeadline, allowedHeadlingLevels, levelSearchDir) { if (!myCurrHeadline || !allowedHeadlingLevels) { @@ -2266,7 +2299,7 @@ EasyMDE.prototype.render = function (el) { diff: Math.abs(newHeadingLevel - currHeadlingLevel), }; }; - var checkNewHeadline = function(cm, obj) { + var headlineCheckNew = function(cm, obj) { var myHeadline = cm.getRange({ line: obj.from.line, ch: 0, @@ -2283,7 +2316,7 @@ EasyMDE.prototype.render = function (el) { if (levels.to > levels.from) { // Current level is forbidden so we jump to the closest upper level allowed // We only need to reset the sharp numbers with the appropriate value before the modification is applied - obj.text[0] = makeBiggerHeadline('', levels.from, levels.to); + obj.text[0] = headlineMakeBigger('', levels.from, levels.to); } else { // The current level is forbidden and we jump to the closest lower level available @@ -2306,7 +2339,7 @@ EasyMDE.prototype.render = function (el) { } return true; }; - var checkExistingHeadline = function(cm, obj) { + var headlineCheckExisting = function(cm, obj) { var myChar = cm.getRange({ line: obj.from.line, ch: obj.from.ch, @@ -2314,7 +2347,6 @@ EasyMDE.prototype.render = function (el) { line: obj.to.line, ch: obj.to.ch + 1, }); - // console.log( '==' + myChar + '==' ); if (!/\s|#/.test(myChar || '')) { // Don't bother to go further if no headlines were detected return false; @@ -2331,7 +2363,7 @@ EasyMDE.prototype.render = function (el) { } else { myText = myText.replace(/#/, '##'); // Increment one sharp sign - }; + } myLevels = headlineNeedUpdate(myText, cm.options.backdrop.headingLevels); if (!myLevels) { return false; @@ -2378,50 +2410,99 @@ EasyMDE.prototype.render = function (el) { } } }; + var headingCheckRow = function(row, cm) { + if (!row || !/^#/.test(row.trim())) { + return row; + } + row = row.replace(/^(\s*)#/, '#'); + var myLevels = headlineNeedUpdate(row, cm.options.backdrop.headingLevels); + if (!myLevels || !myLevels.from || !myLevels.to) { + return row; + } + else if (myLevels.from < myLevels.to) { + return headlineMakeBigger(row, myLevels.from, myLevels.to); + } + else if (myLevels.to < myLevels.from) { + return headlineMakeSmaller(row, myLevels.from, myLevels.to); + } + return row; + }; this.codemirror.on('beforeChange', function (cm, obj) { - console.log(obj); - if (!obj || !obj.from || !obj.to || !obj.text) { + // console.log(obj); + if (!obj || !obj.from || !obj.to || !obj.text || !obj.text.length) { // Don't go further 'cause a required argument is missing... return false; } - if ((obj.from.line === obj.to.line) ) { + if ((obj.from.line === obj.to.line) && obj.text.length < 2) { if (obj.to.ch > 6) { // No need to trigger a check if we are on the same line at character 7 or upper - // As we are sure the cursor is not inside a range containing a sharp sign + // As we are sure the cursor was not inside a range containing a sharp sign return false; } - if (obj.from.ch === 0 && obj.to.ch === 0 && /\s|\b/.test(obj.text[0] || '')) { - // Prevent space at the beginning of the line + if (obj.from.ch === 0 && obj.to.ch === 0 && /\s/.test(obj.text[0] || '')) { + // (Force) Prevent space at the beginning of the line obj.cancel(); return false; } } - if (!obj.text.length) { - // Just in case - return false; - } if (!obj.origin) { - // Just in case + // If a modification was triggered by a code and not an human + // The origin can be "undefined", so just in case set one. obj.origin = '+none'; } if (/input/.test(obj.origin)) { // Something was added if (obj.text.length === 1 && obj.text[0].length === 1) { // Only one character on one line is being updated if (obj.text[0] === ' ') { - return checkNewHeadline(cm, obj); + return headlineCheckNew(cm, obj); } else if (obj.text[0] === '#') { - return checkExistingHeadline(cm, obj); - } - else { - return false; + return headlineCheckExisting(cm, obj); } } } else if (/delete/.test(obj.origin)) { // Something was removed if (obj.text.length === 1 && !obj.text[0].length) { // Only one character on one line has been removed - return checkExistingHeadline(cm, obj); + return headlineCheckExisting(cm, obj); + } + } + if (obj.text.length < 2) { + return false; + } + // Multilines modification like a paste + var r; + if (obj.from.ch === 0) { + // Loop each row and check for headers + for (r=0; r 7) { + // We are sure an existing header is not involved + // So exclude the first row from the loop + for (r=1; r Date: Thu, 12 Sep 2024 23:30:33 +0900 Subject: [PATCH 06/16] WIP: Typo updated --- src/js/easymde.js | 125 ++++++++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 60 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index f574d7e..043a8ac 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -1124,6 +1124,13 @@ function _toggleHeading(cm, direction, size) { var startPoint = cm.getCursor('start'); var endPoint = cm.getCursor('end'); + var sharpLevels = cm.options.backdrop.headingLevels || [], + minLevel = sharpLevels.length ? sharpLevels[0] : 1, + maxLevel = sharpLevels.length ? sharpLevels[sharpLevels.length-1] : 6; + if (size && sharpLevels.length && sharpLevels.indexOf(size) === -1) { + cm.focus(); + return false; + } for (var i = startPoint.line; i <= endPoint.line; i++) { (function (i) { var text = cm.getLine(i); @@ -1132,14 +1139,20 @@ function _toggleHeading(cm, direction, size) { if (direction !== undefined) { if (currHeadingLevel <= 0) { if (direction == 'bigger') { - text = '###### ' + text; + text = '#'.repeat(maxLevel) + ' ' + text; } else { - text = '# ' + text; + text = '#'.repeat(minLevel) + ' ' + text; + } + } else if (currHeadingLevel == maxLevel && direction == 'smaller') { + text = text.substr(maxLevel + 1); + } else if (currHeadingLevel == minLevel && direction == 'bigger') { + text = text.substr(minLevel + 1); + } else if (sharpLevels.length) { + if (direction == 'bigger') { + text = '#'.repeat(sharpLevels[sharpLevels.indexOf(currHeadingLevel)-1]) + text.replace(/^[#]+/, '' ); + } else { + text = '#'.repeat(sharpLevels[sharpLevels.indexOf(currHeadingLevel)+1]) + text.replace(/^[#]+/, '' ); } - } else if (currHeadingLevel == 6 && direction == 'smaller') { - text = text.substr(7); - } else if (currHeadingLevel == 1 && direction == 'bigger') { - text = text.substr(2); } else { if (direction == 'bigger') { text = text.substr(1); @@ -2205,141 +2218,138 @@ EasyMDE.prototype.render = function (el) { } if (options.parsingConfig.headingLevels) { // If the *headingLevels* argument is present, set our custom modifiers - var headlineMakeBigger = function(headline, from, to) { - headline = headline || ''; + var headingMakeBigger = function(heading, from, to) { + heading = heading || ''; if (!from || !to || from >= to) { return ''; } var level = ''; - if (!headline.length) { + if (!heading.length) { while (from < to) { level += '#'; from++; } level += ' '; return level; - } - else { + } else { while (to > 0) { level += '#'; to--; } level += ' '; - return /#/.test(headline) ? headline.replace(/^[#]+\s*/, level) : level; + return /#/.test(heading) ? heading.replace(/^[#]+\s*/, level) : level; } }; - var headlineMakeSmaller = function(headline, from, to) { - headline = headline || ''; + var headingMakeSmaller = function(heading, from, to) { + heading = heading || ''; if (!from || !to || from <= to) { return ''; } var level = ''; - if (!headline.length) { + if (!heading.length) { while (from > to) { level += '#'; from--; } level += ' '; return level; - } - else { + } else { while (to > 0) { level += '#'; to--; } level += ' '; - return /#/.test(headline) ? headline.replace(/^[#]+\s*/, level) : level; + return /#/.test(heading) ? heading.replace(/^[#]+\s*/, level) : level; } }; - var headlineNeedUpdate = function(myCurrHeadline, allowedHeadlingLevels, levelSearchDir) { - if (!myCurrHeadline || !allowedHeadlingLevels) { + var headingNeedUpdate = function(currHeading, allowedHeadingLevels, levelSearchDir) { + if (!currHeading || !allowedHeadingLevels) { return false; } - myCurrHeadline = myCurrHeadline.trim(); - if (!/^#+/.test(myCurrHeadline)){ + currHeading = currHeading.trim(); + if (!/^#+/.test(currHeading)){ return false; } - var currHeadlingLevel = (myCurrHeadline.match(/^([#]+)/g) || [''])[0].length; - if (allowedHeadlingLevels.indexOf(currHeadlingLevel) !== -1) { + var currHeadingLevel = (currHeading.match(/^([#]+)/g) || [''])[0].length; + if (allowedHeadingLevels.indexOf(currHeadingLevel) !== -1) { return false; } levelSearchDir = levelSearchDir || 'asc'; var newHeadingLevel = -1, n = 0; if (levelSearchDir === 'asc') { - while (n < allowedHeadlingLevels.length) { - if (allowedHeadlingLevels[n] > currHeadlingLevel) { - newHeadingLevel = allowedHeadlingLevels[n]; + while (n < allowedHeadingLevels.length) { + if (allowedHeadingLevels[n] > currHeadingLevel) { + newHeadingLevel = allowedHeadingLevels[n]; break; } n++; } } if (newHeadingLevel < 0) { - n = allowedHeadlingLevels.length - 1; + n = allowedHeadingLevels.length - 1; while (n > -1) { - if (allowedHeadlingLevels[n] < currHeadlingLevel) { - newHeadingLevel = allowedHeadlingLevels[n]; + if (allowedHeadingLevels[n] < currHeadingLevel) { + newHeadingLevel = allowedHeadingLevels[n]; break; } n--; } if (levelSearchDir === 'dsc') { - currHeadlingLevel += 1; + currHeadingLevel += 1; if (newHeadingLevel < 0) { newHeadingLevel = 0; } } } - if (newHeadingLevel < 0 || newHeadingLevel === currHeadlingLevel) { + if (newHeadingLevel < 0 || newHeadingLevel === currHeadingLevel) { return false; } return { - from: currHeadlingLevel, + from: currHeadingLevel, to: newHeadingLevel, - diff: Math.abs(newHeadingLevel - currHeadlingLevel), + diff: Math.abs(newHeadingLevel - currHeadingLevel), }; }; - var headlineCheckNew = function(cm, obj) { - var myHeadline = cm.getRange({ + var headingCheckNew = function(cm, obj) { + var currHeading = cm.getRange({ line: obj.from.line, ch: 0, }, { line: obj.to.line, ch: obj.to.ch, }); - var levels = headlineNeedUpdate(myHeadline, cm.options.backdrop.headingLevels); - if (!levels || !levels.from || !levels.to) { + var myLevels = headingNeedUpdate(currHeading, cm.options.backdrop.headingLevels); + if (!myLevels || !myLevels.from || !myLevels.to) { return false; } if (obj.from.line === obj.to.line) { // Most simple case when a modification has occured on a single line - if (levels.to > levels.from) { + if (myLevels.to > myLevels.from) { // Current level is forbidden so we jump to the closest upper level allowed // We only need to reset the sharp numbers with the appropriate value before the modification is applied - obj.text[0] = headlineMakeBigger('', levels.from, levels.to); - } - else { + obj.text[0] = headingMakeBigger('', myLevels.from, myLevels.to); + } else { // The current level is forbidden and we jump to the closest lower level available // A bit more is needed: we have to cancel the requested update and trigger a replacement with the existing sharp signs obj.cancel(); - var newHeadline = ''; - while (levels.to > 0) { - newHeadline += '#'; - levels.to--; + var newHeading = ''; + while (myLevels.to > 0) { + newHeading += '#'; + myLevels.to--; } - newHeadline += ' '; - cm.doc.replaceRange(newHeadline, { + newHeading += ' '; + cm.doc.replaceRange(newHeading, { line: obj.from.line, ch: 0, }, { line: obj.to.line, ch: obj.to.ch, - }, myHeadline ); // 4th arguments to keep a trace in the history when possible + }, currHeading ); // 4th arguments to keep a trace in the history when possible } } return true; }; - var headlineCheckExisting = function(cm, obj) { + var headingCheckExisting = function(cm, obj) { var myChar = cm.getRange({ line: obj.from.line, ch: obj.from.ch, @@ -2360,8 +2370,7 @@ EasyMDE.prototype.render = function (el) { } if (!/#/.test(myText)) { myText = '# ' + myText.trim(); // Wasn't headline - } - else { + } else { myText = myText.replace(/#/, '##'); // Increment one sharp sign } myLevels = headlineNeedUpdate(myText, cm.options.backdrop.headingLevels); @@ -2418,11 +2427,9 @@ EasyMDE.prototype.render = function (el) { var myLevels = headlineNeedUpdate(row, cm.options.backdrop.headingLevels); if (!myLevels || !myLevels.from || !myLevels.to) { return row; - } - else if (myLevels.from < myLevels.to) { + } else if (myLevels.from < myLevels.to) { return headlineMakeBigger(row, myLevels.from, myLevels.to); - } - else if (myLevels.to < myLevels.from) { + } else if (myLevels.to < myLevels.from) { return headlineMakeSmaller(row, myLevels.from, myLevels.to); } return row; @@ -2455,8 +2462,7 @@ EasyMDE.prototype.render = function (el) { // Only one character on one line is being updated if (obj.text[0] === ' ') { return headlineCheckNew(cm, obj); - } - else if (obj.text[0] === '#') { + } else if (obj.text[0] === '#') { return headlineCheckExisting(cm, obj); } } @@ -2499,8 +2505,7 @@ EasyMDE.prototype.render = function (el) { obj.from.ch = 0; } } - } - else { // 2nd and next rows + } else { // 2nd and next rows obj.text[r] = headingCheckRow(obj.text[r], cm); } } From b89be938ddac56f71c779c3be9d963b1f2c1d9be Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Fri, 13 Sep 2024 11:30:22 +0900 Subject: [PATCH 07/16] Hotfix: Fixing Typo --- src/js/easymde.js | 232 +++++++++++++++++++++++++++++++--------------- 1 file changed, 159 insertions(+), 73 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index 8e73d6e..5eb37c8 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -1124,6 +1124,13 @@ function _toggleHeading(cm, direction, size) { var startPoint = cm.getCursor('start'); var endPoint = cm.getCursor('end'); + var sharpLevels = cm.options.backdrop.headingLevels || [], + minLevel = sharpLevels.length ? sharpLevels[0] : 1, + maxLevel = sharpLevels.length ? sharpLevels[sharpLevels.length-1] : 6; + if (size && sharpLevels.length && sharpLevels.indexOf(size) === -1) { + cm.focus(); + return false; + } for (var i = startPoint.line; i <= endPoint.line; i++) { (function (i) { var text = cm.getLine(i); @@ -1132,14 +1139,20 @@ function _toggleHeading(cm, direction, size) { if (direction !== undefined) { if (currHeadingLevel <= 0) { if (direction == 'bigger') { - text = '###### ' + text; + text = '#'.repeat(maxLevel) + ' ' + text; } else { - text = '# ' + text; + text = '#'.repeat(minLevel) + ' ' + text; + } + } else if (currHeadingLevel == maxLevel && direction == 'smaller') { + text = text.substr(maxLevel + 1); + } else if (currHeadingLevel == minLevel && direction == 'bigger') { + text = text.substr(minLevel + 1); + } else if (sharpLevels.length) { + if (direction == 'bigger') { + text = '#'.repeat(sharpLevels[sharpLevels.indexOf(currHeadingLevel)-1]) + text.replace(/^[#]+/, '' ); + } else { + text = '#'.repeat(sharpLevels[sharpLevels.indexOf(currHeadingLevel)+1]) + text.replace(/^[#]+/, '' ); } - } else if (currHeadingLevel == 6 && direction == 'smaller') { - text = text.substr(7); - } else if (currHeadingLevel == 1 && direction == 'bigger') { - text = text.substr(2); } else { if (direction == 'bigger') { text = text.substr(1); @@ -2205,108 +2218,138 @@ EasyMDE.prototype.render = function (el) { } if (options.parsingConfig.headingLevels) { // If the *headingLevels* argument is present, set our custom modifiers - var makeBiggerHeadline = function(headline, from, to) { - headline = headline || ''; - if (!from || !to) { + var headingMakeBigger = function(heading, from, to) { + heading = heading || ''; + if (!from || !to || from >= to) { return ''; } var level = ''; - while (from < to) { - level += '#'; - from++; + if (!heading.length) { + while (from < to) { + level += '#'; + from++; + } + level += ' '; + return level; + } else { + while (to > 0) { + level += '#'; + to--; + } + level += ' '; + return /#/.test(heading) ? heading.replace(/^[#]+\s*/, level) : level; } - level += ' '; - return /#/.test(headline) ? headline.replace(/#\s*/, level) : level; }; - var headlineNeedUpdate = function(myCurrHeadline, allowedHeadlingLevels, levelSearchDir) { - if (!myCurrHeadline || !allowedHeadlingLevels) { + var headingMakeSmaller = function(heading, from, to) { + heading = heading || ''; + if (!from || !to || from <= to) { + return ''; + } + var level = ''; + if (!heading.length) { + while (from > to) { + level += '#'; + from--; + } + level += ' '; + return level; + } else { + while (to > 0) { + level += '#'; + to--; + } + level += ' '; + return /#/.test(heading) ? heading.replace(/^[#]+\s*/, level) : level; + } + }; + var headingNeedUpdate = function(currHeading, allowedHeadingLevels, levelSearchDir) { + if (!currHeading || !allowedHeadingLevels) { return false; } - myCurrHeadline = myCurrHeadline.trim(); - if (!/^#+/.test(myCurrHeadline)){ + currHeading = currHeading.trim(); + if (!/^#+/.test(currHeading)){ return false; } - var currHeadlingLevel = (myCurrHeadline.match(/^([#]+)/g) || [''])[0].length; - if (allowedHeadlingLevels.indexOf(currHeadlingLevel) !== -1) { + var currHeadingLevel = (currHeading.match(/^([#]+)/g) || [''])[0].length; + if (allowedHeadingLevels.indexOf(currHeadingLevel) !== -1) { return false; } levelSearchDir = levelSearchDir || 'asc'; var newHeadingLevel = -1, n = 0; if (levelSearchDir === 'asc') { - while (n < allowedHeadlingLevels.length) { - if (allowedHeadlingLevels[n] > currHeadlingLevel) { - newHeadingLevel = allowedHeadlingLevels[n]; + while (n < allowedHeadingLevels.length) { + if (allowedHeadingLevels[n] > currHeadingLevel) { + newHeadingLevel = allowedHeadingLevels[n]; break; } n++; } } if (newHeadingLevel < 0) { - n = allowedHeadlingLevels.length - 1; + n = allowedHeadingLevels.length - 1; while (n > -1) { - if (allowedHeadlingLevels[n] < currHeadlingLevel) { - newHeadingLevel = allowedHeadlingLevels[n]; + if (allowedHeadingLevels[n] < currHeadingLevel) { + newHeadingLevel = allowedHeadingLevels[n]; break; } n--; } if (levelSearchDir === 'dsc') { - currHeadlingLevel += 1; + currHeadingLevel += 1; if (newHeadingLevel < 0) { newHeadingLevel = 0; } } } - if (newHeadingLevel < 0 || newHeadingLevel === currHeadlingLevel) { + if (newHeadingLevel < 0 || newHeadingLevel === currHeadingLevel) { return false; } return { - from: currHeadlingLevel, + from: currHeadingLevel, to: newHeadingLevel, - diff: Math.abs(newHeadingLevel - currHeadlingLevel), + diff: Math.abs(newHeadingLevel - currHeadingLevel), }; }; - var checkNewHeadline = function(cm, obj) { - var myHeadline = cm.getRange({ + var headingCheckNew = function(cm, obj) { + var currHeading = cm.getRange({ line: obj.from.line, ch: 0, }, { line: obj.to.line, ch: obj.to.ch, }); - var levels = headlineNeedUpdate(myHeadline, cm.options.backdrop.headingLevels); - if (!levels || !levels.from || !levels.to) { + var myLevels = headingNeedUpdate(currHeading, cm.options.backdrop.headingLevels); + if (!myLevels || !myLevels.from || !myLevels.to) { return false; } if (obj.from.line === obj.to.line) { // Most simple case when a modification has occured on a single line - if (levels.to > levels.from) { + if (myLevels.to > myLevels.from) { // Current level is forbidden so we jump to the closest upper level allowed // We only need to reset the sharp numbers with the appropriate value before the modification is applied - obj.text[0] = makeBiggerHeadline('', levels.from, levels.to); - } - else { + obj.text[0] = headingMakeBigger('', myLevels.from, myLevels.to); + } else { // The current level is forbidden and we jump to the closest lower level available // A bit more is needed: we have to cancel the requested update and trigger a replacement with the existing sharp signs obj.cancel(); - var newHeadline = ''; - while (levels.to > 0) { - newHeadline += '#'; - levels.to--; + var newHeading = ''; + while (myLevels.to > 0) { + newHeading += '#'; + myLevels.to--; } - newHeadline += ' '; - cm.doc.replaceRange(newHeadline, { + newHeading += ' '; + cm.doc.replaceRange(newHeading, { line: obj.from.line, ch: 0, }, { line: obj.to.line, ch: obj.to.ch, - }, myHeadline ); // 4th arguments to keep a trace in the history when possible + }, currHeading ); // 4th arguments to keep a trace in the history when possible } } return true; }; - var checkExistingHeadline = function(cm, obj) { + var headingCheckExisting = function(cm, obj) { var myChar = cm.getRange({ line: obj.from.line, ch: obj.from.ch, @@ -2314,9 +2357,8 @@ EasyMDE.prototype.render = function (el) { line: obj.to.line, ch: obj.to.ch + 1, }); - // console.log( '==' + myChar + '==' ); if (!/\s|#/.test(myChar || '')) { - // Don't bother to go further if no headlines were detected + // Don't bother to go further if no headling were detected return false; } if ((obj.from.line === obj.to.line) && obj.text.length === 1) { @@ -2327,12 +2369,11 @@ EasyMDE.prototype.render = function (el) { return false; } if (!/#/.test(myText)) { - myText = '# ' + myText.trim(); // Wasn't headline - } - else { + myText = '# ' + myText.trim(); // Wasn't heading + } else { myText = myText.replace(/#/, '##'); // Increment one sharp sign - }; - myLevels = headlineNeedUpdate(myText, cm.options.backdrop.headingLevels); + } + myLevels = headingNeedUpdate(myText, cm.options.backdrop.headingLevels); if (!myLevels) { return false; } @@ -2348,7 +2389,7 @@ EasyMDE.prototype.render = function (el) { return true; } else if (/delete/.test(obj.origin) && obj.text[0] === '') { - myLevels = headlineNeedUpdate(myText.replace(/#/, ''), cm.options.backdrop.headingLevels, 'dsc'); + myLevels = headingNeedUpdate(myText.replace(/#/, ''), cm.options.backdrop.headingLevels, 'dsc'); if (!myLevels) { return false; } @@ -2378,50 +2419,95 @@ EasyMDE.prototype.render = function (el) { } } }; + var headingCheckRow = function(row, cm) { + if (!row || !/^#/.test(row.trim())) { + return row; + } + row = row.replace(/^(\s*)#/, '#'); + var myLevels = headingNeedUpdate(row, cm.options.backdrop.headingLevels); + if (!myLevels || !myLevels.from || !myLevels.to) { + return row; + } else if (myLevels.from < myLevels.to) { + return headingMakeBigger(row, myLevels.from, myLevels.to); + } else if (myLevels.to < myLevels.from) { + return headingMakeSmaller(row, myLevels.from, myLevels.to); + } + return row; + }; this.codemirror.on('beforeChange', function (cm, obj) { - console.log(obj); - if (!obj || !obj.from || !obj.to || !obj.text) { + // console.log(obj); + if (!obj || !obj.from || !obj.to || !obj.text || !obj.text.length) { // Don't go further 'cause a required argument is missing... return false; } - if ((obj.from.line === obj.to.line) ) { + if ((obj.from.line === obj.to.line) && obj.text.length < 2) { if (obj.to.ch > 6) { // No need to trigger a check if we are on the same line at character 7 or upper - // As we are sure the cursor is not inside a range containing a sharp sign + // As we are sure the cursor was not inside a range containing a sharp sign return false; } - if (obj.from.ch === 0 && obj.to.ch === 0 && /\s|\b/.test(obj.text[0] || '')) { - // Prevent space at the beginning of the line + if (obj.from.ch === 0 && obj.to.ch === 0 && /\s/.test(obj.text[0] || '')) { + // (Force) Prevent space at the beginning of the line obj.cancel(); return false; } } - if (!obj.text.length) { - // Just in case - return false; - } if (!obj.origin) { - // Just in case + // If a modification was triggered by a code and not an human + // The origin can be "undefined", so just in case set one. obj.origin = '+none'; } if (/input/.test(obj.origin)) { // Something was added if (obj.text.length === 1 && obj.text[0].length === 1) { // Only one character on one line is being updated if (obj.text[0] === ' ') { - return checkNewHeadline(cm, obj); - } - else if (obj.text[0] === '#') { - return checkExistingHeadline(cm, obj); - } - else { - return false; + return headingCheckNew(cm, obj); + } else if (obj.text[0] === '#') { + return headingCheckExisting(cm, obj); } } } else if (/delete/.test(obj.origin)) { // Something was removed if (obj.text.length === 1 && !obj.text[0].length) { // Only one character on one line has been removed - return checkExistingHeadline(cm, obj); + return headingCheckExisting(cm, obj); + } + } + if (obj.text.length < 2) { + return false; + } + // Multilines modification like a paste + var r; + if (obj.from.ch === 0) { + // Loop each row and check for headers + for (r=0; r 7) { + // We are sure an existing header is not involved + // So exclude the first row from the loop + for (r=1; r Date: Sat, 14 Sep 2024 14:46:37 +0900 Subject: [PATCH 08/16] WIP: Updating the delete algo --- src/js/easymde.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index 5eb37c8..bbf656e 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -2361,7 +2361,7 @@ EasyMDE.prototype.render = function (el) { // Don't bother to go further if no headling were detected return false; } - if ((obj.from.line === obj.to.line) && obj.text.length === 1) { + if ((obj.from.line === obj.to.line) && obj.text.length < 2) { var myLevels, myText = cm.getRange({line: obj.from.line, ch: 0}, {line: obj.to.line, ch: 8}); if (/input/.test(obj.origin) && obj.text[0] === '#') { if (!/[^\s#]/.test(myText)) { @@ -2389,21 +2389,30 @@ EasyMDE.prototype.render = function (el) { return true; } else if (/delete/.test(obj.origin) && obj.text[0] === '') { - myLevels = headingNeedUpdate(myText.replace(/#/, ''), cm.options.backdrop.headingLevels, 'dsc'); - if (!myLevels) { + var myTextPart1 = myText.substring(0, obj.from.ch), + myTextPart2 = myText.substring(obj.from.ch + 1), + isPart1Heading = /^#/.test(myTextPart1) ? true : false, + isPart2Heading = /^#/.test(myTextPart2) ? true : false; + if (!isPart1Heading && !isPart2Heading) { + return false; + } + myText = myTextPart1 + myTextPart2; + var delChar = cm.getRange({line: obj.from.line, ch: obj.from.ch}, {line: obj.to.line, ch: obj.to.ch}), + searchDir = delChar === '#' ? 'dsc' : 'asc'; + myLevels = headingNeedUpdate(myText, cm.options.backdrop.headingLevels, searchDir); + if (!myLevels || !myLevels.diff) { return false; } obj.cancel(); - if (myLevels.to > myLevels.from) { - return false; - } obj.text[0] = ''; - while (myLevels.to > 0) { - obj.text[0] += '#'; - myLevels.to--; + if (myLevels.to > 0) { + while (myLevels.to > 0) { + obj.text[0] += '#'; + myLevels.to--; + } } var newText = myText.replace(new RegExp('^#' + '{' + myLevels.from + '}'), obj.text[0]); - cm.doc.replaceRange(newText.replace(/^\s*/, ''), { + cm.doc.replaceRange(newText, { line: obj.from.line, ch: 0, }, { From 1b98dd833e809c31830fc3f9b7cf32a6e28c0949 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Sat, 14 Sep 2024 18:00:42 +0900 Subject: [PATCH 09/16] WIP: Need more tuning to handle multi characters in pasting --- src/js/easymde.js | 177 ++++++++++++++++++++++++++++------------------ 1 file changed, 110 insertions(+), 67 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index bbf656e..e2103e2 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -2362,12 +2362,19 @@ EasyMDE.prototype.render = function (el) { return false; } if ((obj.from.line === obj.to.line) && obj.text.length < 2) { - var myLevels, myText = cm.getRange({line: obj.from.line, ch: 0}, {line: obj.to.line, ch: 8}); + var myLevels, myText; if (/input/.test(obj.origin) && obj.text[0] === '#') { if (!/[^\s#]/.test(myText)) { // Newly created, skip the check for now return false; } + myText = cm.getRange({ + line: obj.from.line, + ch: 0, + }, { + line: obj.to.line, + ch: 8, + }); if (!/#/.test(myText)) { myText = '# ' + myText.trim(); // Wasn't heading } else { @@ -2389,16 +2396,49 @@ EasyMDE.prototype.render = function (el) { return true; } else if (/delete/.test(obj.origin) && obj.text[0] === '') { - var myTextPart1 = myText.substring(0, obj.from.ch), - myTextPart2 = myText.substring(obj.from.ch + 1), - isPart1Heading = /^#/.test(myTextPart1) ? true : false, - isPart2Heading = /^#/.test(myTextPart2) ? true : false; - if (!isPart1Heading && !isPart2Heading) { + var delChar = cm.getRange({ + line: obj.from.line, + ch: obj.from.ch, + }, { + line: obj.to.line, + ch: obj.to.ch, + }); + if (!delChar || !delChar.length) { return false; } - myText = myTextPart1 + myTextPart2; - var delChar = cm.getRange({line: obj.from.line, ch: obj.from.ch}, {line: obj.to.line, ch: obj.to.ch}), + var searchDir = 'asc', myStart = { + line: obj.from.line, + ch: 0, + }, myEnd; + if (delChar.length === 1) { + myText = cm.getRange({line: obj.from.line, ch: 0}, {line: obj.to.line, ch: 8}); + var myTextPart1 = myText.substring(0, obj.from.ch), + myTextPart2 = myText.substring(obj.from.ch + 1), + isPart1Heading = /^#/.test(myTextPart1) ? true : false, + isPart2Heading = /^#/.test(myTextPart2) ? true : false; + if (!isPart1Heading && !isPart2Heading) { + return false; + } + myText = myTextPart1 + myTextPart2; searchDir = delChar === '#' ? 'dsc' : 'asc'; + myEnd = { + line: obj.to.line, + ch: 8, + }; + } else { + myText = cm.getRange({ + line: obj.from.line, + ch: 0, + }, { + line: obj.to.line, + ch: obj.to.ch + 8, + }); + myText = myText.replace(delChar, ''); + myEnd = { + line: obj.to.line, + ch: obj.to.ch + 8, + }; + } myLevels = headingNeedUpdate(myText, cm.options.backdrop.headingLevels, searchDir); if (!myLevels || !myLevels.diff) { return false; @@ -2411,18 +2451,17 @@ EasyMDE.prototype.render = function (el) { myLevels.to--; } } + if (delChar === '#') { + myLevels.from--; + } var newText = myText.replace(new RegExp('^#' + '{' + myLevels.from + '}'), obj.text[0]); - cm.doc.replaceRange(newText, { - line: obj.from.line, - ch: 0, - }, { - line: obj.to.line, - ch: 8, - }); + // Just in case do not trim on both side, only trim the space that could remain next to the cursor + newText = newText.replace(/^\s*/, ''); + cm.doc.replaceRange(newText, myStart, myEnd); // Be gentle and set back the cursor at the appropriate position cm.doc.setCursor({ line: obj.to.line, - ch: obj.to.ch - myLevels.diff, + ch: (delChar === 1 ? obj.to.ch : obj.to.ch - 8) - myLevels.diff, }); return true; } @@ -2449,8 +2488,13 @@ EasyMDE.prototype.render = function (el) { // Don't go further 'cause a required argument is missing... return false; } + if (!obj.origin) { + // If a modification was triggered by a code and not an human + // The origin can be "undefined", so just in case set one. + obj.origin = '+none'; + } if ((obj.from.line === obj.to.line) && obj.text.length < 2) { - if (obj.to.ch > 6) { + if (!/delete/.test(obj.origin) && obj.to.ch > 6) { // No need to trigger a check if we are on the same line at character 7 or upper // As we are sure the cursor was not inside a range containing a sharp sign return false; @@ -2460,64 +2504,63 @@ EasyMDE.prototype.render = function (el) { obj.cancel(); return false; } - } - if (!obj.origin) { - // If a modification was triggered by a code and not an human - // The origin can be "undefined", so just in case set one. - obj.origin = '+none'; - } - if (/input/.test(obj.origin)) { // Something was added - if (obj.text.length === 1 && obj.text[0].length === 1) { - // Only one character on one line is being updated - if (obj.text[0] === ' ') { - return headingCheckNew(cm, obj); - } else if (obj.text[0] === '#') { + if (/input/.test(obj.origin)) { // Something was added + if (obj.text.length === 1 && obj.text[0].length === 1) { + // Only one character on one line is being updated + if (obj.text[0] === ' ') { + return headingCheckNew(cm, obj); + } else if (obj.text[0] === '#') { + return headingCheckExisting(cm, obj); + } + } + } else if (/delete/.test(obj.origin)) { // Something was removed + if (obj.text.length === 1 && !obj.text[0].length) { + // Only one character on one line has been removed return headingCheckExisting(cm, obj); } } } - else if (/delete/.test(obj.origin)) { // Something was removed - if (obj.text.length === 1 && !obj.text[0].length) { - // Only one character on one line has been removed - return headingCheckExisting(cm, obj); + if (!/delete/.test(obj.origin)) { + if (obj.text.length < 2 && obj.text[0].length < 2) { + return false; } - } - if (obj.text.length < 2) { - return false; - } - // Multilines modification like a paste - var r; - if (obj.from.ch === 0) { - // Loop each row and check for headers - for (r=0; r 7) { - // We are sure an existing header is not involved - // So exclude the first row from the loop - for (r=1; r 7) { + // We are sure an new heading is not involved with conflicting an existing one + // So we can safely exclude the first row from the loop + for (r=1; r Date: Mon, 16 Sep 2024 23:51:24 +0900 Subject: [PATCH 10/16] WIP : Refactoring ajout multi-lignes --- example/index.html | 6 +- .../index_parsingConfig_headingLevels.html | 27 ++++++++ src/js/easymde.js | 65 +++++++++++-------- 3 files changed, 65 insertions(+), 33 deletions(-) create mode 100644 example/index_parsingConfig_headingLevels.html diff --git a/example/index.html b/example/index.html index 484ad97..ce03794 100644 --- a/example/index.html +++ b/example/index.html @@ -13,11 +13,7 @@ diff --git a/example/index_parsingConfig_headingLevels.html b/example/index_parsingConfig_headingLevels.html new file mode 100644 index 0000000..587cffa --- /dev/null +++ b/example/index_parsingConfig_headingLevels.html @@ -0,0 +1,27 @@ + + + + + + + + Example / Preview / parsingConfig / headingLevels + + + + + + + + + + diff --git a/src/js/easymde.js b/src/js/easymde.js index e2103e2..3da0059 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -2520,43 +2520,52 @@ EasyMDE.prototype.render = function (el) { } } } + // Multilines modification like a paste if (!/delete/.test(obj.origin)) { if (obj.text.length < 2 && obj.text[0].length < 2) { return false; } - // Multilines modification like a paste - var r; - if (obj.from.ch === 0) { - // Loop each row and check for headers - for (r=0; r 7) { + // We are sure an new heading is not involved or conflicting with an existing one + // So we can safely exclude the first row from the verification loop + r = 1; } - else if (obj.from.ch > 7) { - // We are sure an new heading is not involved with conflicting an existing one - // So we can safely exclude the first row from the loop - for (r=1; r 0) { + // We need to check the first row in case an existing heading exists or is updated + var startText = cm.getRange({ + line: obj.from.line, + ch: 0, + }, { + line: obj.from.line, + ch: obj.from.ch, + }); + if (/#/.test(startText)) { + var oldText = startText + obj.text[r], newText = headingCheckRow(oldText, cm); - if (oldText !== newText) { // A modification has been made - obj.text[r] = newText; - obj.from.ch = 0; - } + if (oldText !== newText) { // A modification has been made + obj.text[r] = newText.substring(obj.from.ch); + // obj.from.ch = 0; } - } else { // 2nd and next rows - obj.text[r] = headingCheckRow(obj.text[r], cm); } } + else if (r === rEnd - 1) { + var endText = cm.getRange({ + line: obj.from.line, + ch: obj.from.ch, + }, { + line: obj.from.line, + ch: obj.from.ch + 8, + }); + console.log( obj.txt[r] ); + console.log( endText ); + obj.text[r] = headingCheckRow(obj.text[r], cm); + } + else { // 2nd and next rows + obj.text[r] = headingCheckRow(obj.text[r], cm); + } + r++; } } else { // Multilines / multicharacters were removed From 7404f73ba5ad26f4058a2d9ff306e4e5fb6f6692 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Tue, 17 Sep 2024 01:22:23 +0900 Subject: [PATCH 11/16] WIP: Multilines paste input fixed --- src/js/easymde.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index 3da0059..aaaf75b 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -2546,7 +2546,6 @@ EasyMDE.prototype.render = function (el) { newText = headingCheckRow(oldText, cm); if (oldText !== newText) { // A modification has been made obj.text[r] = newText.substring(obj.from.ch); - // obj.from.ch = 0; } } } @@ -2558,9 +2557,16 @@ EasyMDE.prototype.render = function (el) { line: obj.from.line, ch: obj.from.ch + 8, }); - console.log( obj.txt[r] ); - console.log( endText ); - obj.text[r] = headingCheckRow(obj.text[r], cm); + if (/#/.test(endText)) { + var oldText = obj.text[r] + endText, + newText = headingCheckRow(oldText, cm); + if (oldText !== newText) { // A modification has been made + obj.text[r] = newText.replace(endText, ''); + } + } + else { + obj.text[r] = headingCheckRow(obj.text[r], cm); + } } else { // 2nd and next rows obj.text[r] = headingCheckRow(obj.text[r], cm); From bb99ddc81c5d17007c63aba820d088b090fba5cc Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Wed, 18 Sep 2024 00:39:11 +0900 Subject: [PATCH 12/16] WIP: Multilines removed patch --- src/js/easymde.js | 62 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index 3da0059..e924176 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -2521,6 +2521,7 @@ EasyMDE.prototype.render = function (el) { } } // Multilines modification like a paste + var startText = '', endText = '', oldText = '', newText = ''; if (!/delete/.test(obj.origin)) { if (obj.text.length < 2 && obj.text[0].length < 2) { return false; @@ -2534,7 +2535,7 @@ EasyMDE.prototype.render = function (el) { while (r < rEnd) { if (!r && obj.from.ch > 0) { // We need to check the first row in case an existing heading exists or is updated - var startText = cm.getRange({ + startText = cm.getRange({ line: obj.from.line, ch: 0, }, { @@ -2542,25 +2543,31 @@ EasyMDE.prototype.render = function (el) { ch: obj.from.ch, }); if (/#/.test(startText)) { - var oldText = startText + obj.text[r], - newText = headingCheckRow(oldText, cm); + oldText = startText + obj.text[r]; + newText = headingCheckRow(oldText, cm); if (oldText !== newText) { // A modification has been made obj.text[r] = newText.substring(obj.from.ch); - // obj.from.ch = 0; } } } else if (r === rEnd - 1) { - var endText = cm.getRange({ + endText = cm.getRange({ line: obj.from.line, ch: obj.from.ch, }, { line: obj.from.line, ch: obj.from.ch + 8, }); - console.log( obj.txt[r] ); - console.log( endText ); - obj.text[r] = headingCheckRow(obj.text[r], cm); + if (/#/.test(endText)) { + oldText = obj.text[r] + endText; + newText = headingCheckRow(oldText, cm); + if (oldText !== newText) { // A modification has been made + obj.text[r] = newText.replace(endText, ''); + } + } + else { + obj.text[r] = headingCheckRow(obj.text[r], cm); + } } else { // 2nd and next rows obj.text[r] = headingCheckRow(obj.text[r], cm); @@ -2569,7 +2576,44 @@ EasyMDE.prototype.render = function (el) { } } else { // Multilines / multicharacters were removed - console.log( 'Fix me' ); + if (obj.from.ch > 7 || obj.text.length > 1 || obj.text[0].length > 1) { + return false; + } + startText = cm.getRange({ + line: obj.from.line, + ch: 0, + }, { + line: obj.from.line, + ch: obj.from.ch, + }); + endText = cm.getRange({ + line: obj.to.line, + ch: obj.to.ch, + }, { + line: obj.to.line, + ch: obj.to.ch + 8, + }); + oldText = startText + endText; + if (/#/.test(oldText)) { + newText = headingCheckRow(oldText, cm); + if (oldText !== newText) { // A modification has been made + obj.cancel(); + cm.doc.replaceRange('', { + line: obj.from.line, + ch: obj.from.ch, + }, { + line: obj.to.line, + ch: obj.to.ch, + }); + cm.doc.replaceRange(newText, { + line: obj.from.line, + ch: 0, + }, { + line: obj.from.line, + ch: obj.from.ch + 8, + }); + } + } } }); } From 6a4ca8629c0dda7e5fd6c66cd9f7ea33215f82e5 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Wed, 18 Sep 2024 09:07:59 +0900 Subject: [PATCH 13/16] WIP: Tiny patches --- src/js/easymde.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index e924176..7d2ddb3 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -2461,7 +2461,7 @@ EasyMDE.prototype.render = function (el) { // Be gentle and set back the cursor at the appropriate position cm.doc.setCursor({ line: obj.to.line, - ch: (delChar === 1 ? obj.to.ch : obj.to.ch - 8) - myLevels.diff, + ch: obj.text[0].length + 1, }); return true; } @@ -2544,6 +2544,7 @@ EasyMDE.prototype.render = function (el) { }); if (/#/.test(startText)) { oldText = startText + obj.text[r]; + oldText = oldText.replace(/#\s+#/, '##'); newText = headingCheckRow(oldText, cm); if (oldText !== newText) { // A modification has been made obj.text[r] = newText.substring(obj.from.ch); @@ -2560,6 +2561,7 @@ EasyMDE.prototype.render = function (el) { }); if (/#/.test(endText)) { oldText = obj.text[r] + endText; + oldText = oldText.replace(/#\s+#/, '##'); newText = headingCheckRow(oldText, cm); if (oldText !== newText) { // A modification has been made obj.text[r] = newText.replace(endText, ''); @@ -2594,6 +2596,7 @@ EasyMDE.prototype.render = function (el) { ch: obj.to.ch + 8, }); oldText = startText + endText; + oldText = oldText.replace(/#\s+#/, '##'); if (/#/.test(oldText)) { newText = headingCheckRow(oldText, cm); if (oldText !== newText) { // A modification has been made From a704a21d6cebed300e98e82b4e30cc82f9858308 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Wed, 18 Sep 2024 09:14:28 +0900 Subject: [PATCH 14/16] WIP: Tiny Patch --- src/js/easymde.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index 7d2ddb3..01dda28 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -2461,7 +2461,7 @@ EasyMDE.prototype.render = function (el) { // Be gentle and set back the cursor at the appropriate position cm.doc.setCursor({ line: obj.to.line, - ch: obj.text[0].length + 1, + ch: obj.text[0].length ? obj.text[0].length + 1 : 0, }); return true; } From 4d1d282adf1f704a3cb56f7e5b4f8d1f25afa260 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Sun, 29 Sep 2024 23:24:45 +0900 Subject: [PATCH 15/16] WIP: Bug Fix --- src/js/easymde.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index 01dda28..a3e5ec3 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -1124,7 +1124,7 @@ function _toggleHeading(cm, direction, size) { var startPoint = cm.getCursor('start'); var endPoint = cm.getCursor('end'); - var sharpLevels = cm.options.backdrop.headingLevels || [], + var sharpLevels = cm.options.backdrop ? (cm.options.backdrop.headingLevels || []) : (cm.mode ? (cm.mode.headingLevels || []) : []), minLevel = sharpLevels.length ? sharpLevels[0] : 1, maxLevel = sharpLevels.length ? sharpLevels[sharpLevels.length-1] : 6; if (size && sharpLevels.length && sharpLevels.indexOf(size) === -1) { @@ -2318,7 +2318,7 @@ EasyMDE.prototype.render = function (el) { line: obj.to.line, ch: obj.to.ch, }); - var myLevels = headingNeedUpdate(currHeading, cm.options.backdrop.headingLevels); + var myLevels = headingNeedUpdate(currHeading, cm.options.backdrop ? cm.options.backdrop.headingLevels : cm.mode.headingLevels); if (!myLevels || !myLevels.from || !myLevels.to) { return false; } @@ -2380,7 +2380,7 @@ EasyMDE.prototype.render = function (el) { } else { myText = myText.replace(/#/, '##'); // Increment one sharp sign } - myLevels = headingNeedUpdate(myText, cm.options.backdrop.headingLevels); + myLevels = headingNeedUpdate(myText, cm.options.backdrop ? cm.options.backdrop.headingLevels : cm.mode.headingLevels); if (!myLevels) { return false; } @@ -2439,7 +2439,7 @@ EasyMDE.prototype.render = function (el) { ch: obj.to.ch + 8, }; } - myLevels = headingNeedUpdate(myText, cm.options.backdrop.headingLevels, searchDir); + myLevels = headingNeedUpdate(myText, cm.options.backdrop ? cm.options.backdrop.headingLevels : cm.mode.headingLevels, searchDir); if (!myLevels || !myLevels.diff) { return false; } @@ -2472,7 +2472,7 @@ EasyMDE.prototype.render = function (el) { return row; } row = row.replace(/^(\s*)#/, '#'); - var myLevels = headingNeedUpdate(row, cm.options.backdrop.headingLevels); + var myLevels = headingNeedUpdate(row, cm.mode.headingLevels || cm.options.backdrop.headingLevels); if (!myLevels || !myLevels.from || !myLevels.to) { return row; } else if (myLevels.from < myLevels.to) { From 0e494d61563dce05d78aa92afd8dcb26471739d8 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Lavigne Date: Mon, 30 Sep 2024 10:00:25 +0900 Subject: [PATCH 16/16] Hotfix: Patching options --- src/js/easymde.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/js/easymde.js b/src/js/easymde.js index a3e5ec3..d2117fb 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -1124,7 +1124,7 @@ function _toggleHeading(cm, direction, size) { var startPoint = cm.getCursor('start'); var endPoint = cm.getCursor('end'); - var sharpLevels = cm.options.backdrop ? (cm.options.backdrop.headingLevels || []) : (cm.mode ? (cm.mode.headingLevels || []) : []), + var sharpLevels = cm.options.backdrop ? (cm.options.backdrop.headingLevels || []) : (cm.options.mode ? (cm.options.mode.headingLevels || []) : []), minLevel = sharpLevels.length ? sharpLevels[0] : 1, maxLevel = sharpLevels.length ? sharpLevels[sharpLevels.length-1] : 6; if (size && sharpLevels.length && sharpLevels.indexOf(size) === -1) { @@ -2318,7 +2318,7 @@ EasyMDE.prototype.render = function (el) { line: obj.to.line, ch: obj.to.ch, }); - var myLevels = headingNeedUpdate(currHeading, cm.options.backdrop ? cm.options.backdrop.headingLevels : cm.mode.headingLevels); + var myLevels = headingNeedUpdate(currHeading, cm.options.backdrop ? cm.options.backdrop.headingLevels : cm.options.mode.headingLevels); if (!myLevels || !myLevels.from || !myLevels.to) { return false; } @@ -2380,7 +2380,7 @@ EasyMDE.prototype.render = function (el) { } else { myText = myText.replace(/#/, '##'); // Increment one sharp sign } - myLevels = headingNeedUpdate(myText, cm.options.backdrop ? cm.options.backdrop.headingLevels : cm.mode.headingLevels); + myLevels = headingNeedUpdate(myText, cm.options.backdrop ? cm.options.backdrop.headingLevels : cm.options.mode.headingLevels); if (!myLevels) { return false; } @@ -2439,7 +2439,7 @@ EasyMDE.prototype.render = function (el) { ch: obj.to.ch + 8, }; } - myLevels = headingNeedUpdate(myText, cm.options.backdrop ? cm.options.backdrop.headingLevels : cm.mode.headingLevels, searchDir); + myLevels = headingNeedUpdate(myText, cm.options.backdrop ? cm.options.backdrop.headingLevels : cm.options.mode.headingLevels, searchDir); if (!myLevels || !myLevels.diff) { return false; } @@ -2472,7 +2472,7 @@ EasyMDE.prototype.render = function (el) { return row; } row = row.replace(/^(\s*)#/, '#'); - var myLevels = headingNeedUpdate(row, cm.mode.headingLevels || cm.options.backdrop.headingLevels); + var myLevels = headingNeedUpdate(row, cm.options.backdrop ? cm.options.backdrop.headingLevels : cm.options.mode.headingLevels); if (!myLevels || !myLevels.from || !myLevels.to) { return row; } else if (myLevels.from < myLevels.to) {