From 01df964ac79740a098d766e3c46d1055445a0aa5 Mon Sep 17 00:00:00 2001 From: mattpass Date: Fri, 9 Apr 2021 16:07:41 +0100 Subject: [PATCH] Updates to JS and multiple results to handle more regex --- assets/js/icecoder.js | 139 +++++++++++++++++++++++---------------- lib/multiple-results.php | 37 ++++++++--- 2 files changed, 109 insertions(+), 67 deletions(-) diff --git a/assets/js/icecoder.js b/assets/js/icecoder.js index a2a8b1c..dd9ac41 100644 --- a/assets/js/icecoder.js +++ b/assets/js/icecoder.js @@ -2794,8 +2794,8 @@ var ICEcoder = { // FIND & REPLACE // ============== - // Backslash escape regex chars in string - escapeRegExp: function(string) { + // Backslash escape regex special chars in string + escapeRegex: function(string) { // $& means the whole matched string return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }, @@ -2829,81 +2829,43 @@ var ICEcoder = { // Find & replace text according to user selections findReplace: function(find, selectNext, canActionChanges, findPrevious) { - let replace, results, thisCM, avgBlockH, addPadding, rBlocks, haveMatch, blockColor, replaceQS, targetQS, filesQS; + let replace, results, thisCM, thisSelection, rBlocks, replaceQS, targetQS, filesQS; // Determine our find rExp, replace value and results display - const rExp = new RegExp(true === parent.ICEcoder.findRegex ? find : ICEcoder.escapeRegExp(find), "gi"); + const rExp = new RegExp(true === parent.ICEcoder.findRegex ? find : ICEcoder.escapeRegex(find), "gi"); replace = get('replace').value; results = get('results'); // Get CM pane thisCM = this.getThisCM(); + // Get this selection, either as regex escaped version or regular, for comparisons + thisSelection = true === parent.ICEcoder.findRegex ? ICEcoder.escapeRegex(thisCM.getSelection()) : thisCM.getSelection(); + + // Finding in this document only if (thisCM && 0 < find.length && t['this document'] === document.findAndReplace.target.value) { // Replacing? if (t['and'] === document.findAndReplace.connector.value && true === canActionChanges) { // Find & replace the next instance, or all? - if (t['replace'] === document.findAndReplace.replaceAction.value && thisCM.getSelection().toLowerCase() === find.toLowerCase()) { + if (t['replace'] === document.findAndReplace.replaceAction.value && thisSelection.toLowerCase() === find.toLowerCase()) { thisCM.replaceSelection(replace, "around"); } else if (t['replace all'] === document.findAndReplace.replaceAction.value) { thisCM.setValue(thisCM.getValue().replace(rExp, replace)); } } - // Set results, resultsLines and findResult back to defaults - this.results = []; - this.resultsLines = []; - this.findResult = 0; - - // Start new iterators for line & last line - let i = 0; - let lastLine = -1; - - // Get lineNum and chNum from cursor - const lineNum = thisCM.getCursor(true === selectNext ? "anchor" : "head").line + 1; - const chNum = thisCM.getCursor(true === selectNext ? "anchor" : "head").ch; - - // Work out the avg block is either line height or fraction of space available - avgBlockH = !this.scrollBarVisible ? thisCM.defaultTextHeight() : parseInt(this.content.style.height, 10) / thisCM.lineCount(); - - // Need to add padding if there's no scrollbar, so current line highlighting lines up with it - addPadding = !this.scrollBarVisible ? thisCM.heightAtLine(0) : 0; - - // Result blocks string empty to start, ready to hold DOM elems to show in results bar - rBlocks = ""; - // Start looking for results - thisCM.eachLine(function(line) { - i++; - haveMatch = false; - // If we have matches for our regex for this line - while ((match = rExp.exec(line.text)) !== null) { - haveMatch = true; - // Not the same as last line, add to resultsLines - if (lastLine !== i) { - ICEcoder.resultsLines.push(match.index); - lastLine = i; - } - // If the line containing a result is less than than the cursors line or - // if the character position of the match is less than the cursor position, increment findResult - if (i < lineNum || (i === lineNum && match.index < chNum)) { - ICEcoder.findResult++; - } - // Push the line & char position coords into results - ICEcoder.results.push([i, match.index]); - } - // If the avg block height for results in results bar is above 0.5 pixels high, we can add a DOM elem - if (0.5 <= avgBlockH) { - // Red for current line, grey for another line, transparent if no match - blockColor = haveMatch ? thisCM.getCursor().line + 1 == i ? "rgba(192,0,0,0.3)" : "rgba(128,128,128,0.3)" : "transparent"; - // Add the DOM elem into our rBlocks string - rBlocks += '
'; - } - }); + rData = ICEcoder.findInCMContent(thisCM, rExp, selectNext); + + // Set results, resultsLines and findResult plus rBlocks which shows DOM elems in results bar + this.results = rData.results; + this.resultsLines = rData.resultsLines; + this.findResult = rData.findResult; + rBlocks = rData.rBlocks; // Increment findResult one more if our selection is what we want to find and we want to find next - if (find.toLowerCase() === thisCM.getSelection().toLowerCase() && false === findPrevious) { - ICEcoder.findResult++; + if (find.toLowerCase() === thisSelection.toLowerCase() && false === findPrevious) { + this.findResult++; } if (findPrevious) { @@ -2940,9 +2902,11 @@ var ICEcoder = { this.goToLine(this.results[this.findResult][0], this.results[this.findResult][1], true); // Finally, highlight our selection and focus on CM pane + // Note when setting the end of the selection we need to deduct extra chars added (the regex escaping backslashes) + // TODO: This idea doesn't really work if you say have "^\$x" and $x's in docs thisCM.setSelection( {"line": this.results[this.findResult][0]-1, "ch": this.results[this.findResult][1]}, - {"line": this.results[this.findResult][0]-1, "ch": this.results[this.findResult][1] + find.length} + {"line": this.results[this.findResult][0]-1, "ch": this.results[this.findResult][1] + find.length - parseInt((ICEcoder.escapeRegex(find).length - find.length) / 2, 10)} ); this.focus(); } @@ -3003,6 +2967,67 @@ var ICEcoder = { } }, + findInCMContent: function(thisCM, rExp, selectNext) { + let avgBlockH, addPadding, rBlocks, blockColor, haveMatch; + + // Start new iterators for line & last line + let i = 0; + let lastLine = -1; + + // Set results, resultsLines and findResult to defaults + let results = []; + let resultsLines = []; + let findResult = 0; + + // Get lineNum and chNum from cursor + const lineNum = thisCM.getCursor(true === selectNext ? "anchor" : "head").line + 1; + const chNum = thisCM.getCursor(true === selectNext ? "anchor" : "head").ch; + + // Work out the avg block is either line height or fraction of space available + avgBlockH = !this.scrollBarVisible ? thisCM.defaultTextHeight() : parseInt(this.content.style.height, 10) / thisCM.lineCount(); + + // Need to add padding if there's no scrollbar, so current line highlighting lines up with it + addPadding = !this.scrollBarVisible ? thisCM.heightAtLine(0) : 0; + + // Result blocks string empty to start, ready to hold DOM elems to show in results bar + rBlocks = ""; + + thisCM.eachLine(function(line) { + i++; + haveMatch = false; + // If we have matches for our regex for this line + while ((match = rExp.exec(line.text)) !== null) { + haveMatch = true; + // Not the same as last line, add to resultsLines + if (lastLine !== i) { + resultsLines.push(match.index); + lastLine = i; + } + // If the line containing a result is less than than the cursors line or + // if the character position of the match is less than the cursor position, increment findResult + if (i < lineNum || (i === lineNum && match.index < chNum)) { + findResult++; + } + // Push the line & char position coords into results + results.push([i, match.index]); + } + // If the avg block height for results in results bar is above 0.5 pixels high, we can add a DOM elem + if (0.5 <= avgBlockH) { + // Red for current line, grey for another line, transparent if no match + blockColor = haveMatch ? thisCM.getCursor().line + 1 == i ? "rgba(192,0,0,0.3)" : "rgba(128,128,128,0.3)" : "transparent"; + // Add the DOM elem into our rBlocks string + rBlocks += '
'; + } + }); + + return { + "results": results, + "resultsLines": resultsLines, + "findResult": findResult, + "rBlocks": rBlocks + } + }, + // Replace text in a file replaceInFile: function(fileRef, find, replace) { this.serverQueue( diff --git a/lib/multiple-results.php b/lib/multiple-results.php index a0793b6..53bf944 100644 --- a/lib/multiple-results.php +++ b/lib/multiple-results.php @@ -48,24 +48,27 @@ if (true === isset($_GET['target']) && false !== strpos($_GET['target'], "filena let foundInSelected = false; const userTarget = parent.document.findAndReplace.target.value; const findText = parent.document.findAndReplace.find.value; + const rExp = new RegExp(true === parent.ICEcoder.findRegex ? findText : parent.ICEcoder.escapeRegex(findText), "gi"); let startTab = parent.ICEcoder.selectedTab; - const rExp = new RegExp(findText, "gi"); + for (let i = 1; i <= parent.ICEcoder.openFiles.length; i++) { parent.ICEcoder.switchTab(i); const cM = parent.ICEcoder.getcMInstance(); - const content = cM.getValue(); - if (content.match(rExp)) { + const selectNext = true; + rData = parent.ICEcoder.findInCMContent(cM, rExp, selectNext); + if (0 < rData.results.length) { resultsDisplay += '' + parent.ICEcoder.openFiles[i - 1] + '
' + - content.match(rExp).length + + rData.results.length + '
'; resultsDisplay += @@ -79,8 +82,9 @@ if (true === isset($_GET['target']) && false !== strpos($_GET['target'], "filena parent.ICEcoder.switchTab(startTab); } @@ -88,11 +92,18 @@ if (true === isset($_GET['target']) && false !== strpos($_GET['target'], "filena for (let i = 0; i < spansArray.length; i++) { let foundInSelected = false; const targetURL = spansArray[i].id.replace(/\|/g, "/").toLowerCase(); + const targetName = targetURL.substring(targetURL.lastIndexOf("/") + 1); + let haveMatch = false; + while ((match = rExp.exec(targetName)) !== null) { + console.log(match); + haveMatch = true; + } if ( - targetURL.lastIndexOf(findText.toLowerCase()) > targetURL.lastIndexOf("/") - && -1 < targetURL.indexOf(findText.toLowerCase()) && targetURL.indexOf('_perms')>-1) { + // TODO: Find in filenames not working with regex, see all instances of findText and $findText below + true === haveMatch && -1 < targetURL.indexOf('_perms')) { if (-1 < userTarget.indexOf("selected")) { for (let j = 0; j < parent.ICEcoder.selectedFiles.length; j++) { + // TODO: This whole file needs comments - what does the below do?! if ( 0 === targetURL.replace(/\//g, "|").indexOf(parent.ICEcoder.selectedFiles[j].replace(/\//g, "|").replace(/_perms/g, "")) && ( @@ -110,6 +121,7 @@ if (true === isset($_GET['target']) && false !== strpos($_GET['target'], "filena '\');parent.ICEcoder.goFindAfterOpenInt = setInterval(function(){goFindAfterOpen(\'' + targetURL.replace(/\|/g, "/").replace(/_perms/g, "") + '\')}, 20);parent.ICEcoder.showHide(\'hide\', parent.document.getElementById(\'blackMask\'))">'; + // TODO: get this line working resultsDisplay += targetURL.replace(/\|/g, "/").replace(/_perms/g, "").replace(//g, "" + @@ -118,6 +130,7 @@ if (true === isset($_GET['target']) && false !== strpos($_GET['target'], "filena resultsDisplay += '
' + spansArray[i].innerHTML + '
'; + // TODO: get this line working resultsDisplay += '
' + spansArray[i].innerHTML + ', ' + @@ -135,6 +148,7 @@ if (true === isset($_GET['target']) && false !== strpos($_GET['target'], "filena } } @@ -237,7 +252,8 @@ if (true === isset($_GET['target']) && false !== strpos($_GET['target'], "filena }; const replaceInFileSingle = function(fileRef) { - parent.ICEcoder.replaceInFile(fileRef, findText, ''); + // TODO: findText in this line + parent.ICEcoder.replaceInFile(fileRef, true === parent.ICEcoder.findRegex ? findText : parent.ICEcoder.escapeRegex(findText), ''); }; const replaceInFilesAll = function() { @@ -249,7 +265,8 @@ if (true === isset($_GET['target']) && false !== strpos($_GET['target'], "filena const renameSingle = function(arrayRef) { fileRef = spansArray[arrayRef].id.replace(/\|/g, "/").replace(/_perms/g, ""); - const rExp = new RegExp(findText, "gi"); + const rExp = new RegExp(true === parent.ICEcoder.findRegex ? findText : parent.ICEcoder.escapeRegex(findText), "gi"); + // TODO: get this working newName = spansArray[arrayRef].id.replace(/\|/g, "/").replace(/_perms/g, "").replace(rExp, ""); parent.ICEcoder.renameFile(fileRef,newName); }; @@ -277,7 +294,7 @@ if (true === isset($_GET['target']) && false !== strpos($_GET['target'], "filena parent.document.getElementById('results').style.display = 'inline-block'; // Action the find and then focus on find input box setTimeout(function() { - parent.ICEcoder.findReplace(findText, true, false, false); + parent.ICEcoder.findReplace(true === parent.ICEcoder.findRegex ? findText : parent.ICEcoder.escapeRegex(findText), true, false, false); parent.document.getElementById("find").focus(); }, 0); };