diff --git a/src/css/editor-css.js b/src/css/editor-css.js index cf7768f4a7b..4541f2d2075 100644 --- a/src/css/editor-css.js +++ b/src/css/editor-css.js @@ -705,4 +705,8 @@ module.exports = ` .ace_hidden_token { display: none; +} + +.ace_invisible_hidden { + color: transparent !important; }`; diff --git a/src/ext/whitespaces_in_selection.js b/src/ext/whitespaces_in_selection.js index 8595078bd4a..f051acde73b 100644 --- a/src/ext/whitespaces_in_selection.js +++ b/src/ext/whitespaces_in_selection.js @@ -15,11 +15,11 @@ var dom = require("../lib/dom"); dom.importCssString(` .ace_whitespaces_in_selection { - color: rgba(0,0,0,0.29); + color: rgba(0,0,0,0.29) !important; } .ace_dark .ace_whitespaces_in_selection { - color: rgba(187, 181, 181, 0.5); + color: rgba(187, 181, 181, 0.5) !important; } `, "ace_whitespaces_in_selection", false); @@ -33,15 +33,14 @@ config.defineOptions(Editor.prototype, "editor", { this.$boundChangeSelectionForWhitespace = $onChangeSelectionForWhitespace.bind(this); } this.on("changeSelection", this.$boundChangeSelectionForWhitespace); + $setRenderWhitespaceMarkers(this, true); } else { this.off("changeSelection", this.$boundChangeSelectionForWhitespace); - if (this.session && this.session.$invisibleMarkerId) { - this.session.removeTextMarker(this.session.$invisibleMarkerId); - this.session.$invisibleMarkerId = null; - } + $removeWhitespaceMarkers(this.session); this.$boundChangeSelectionForWhitespace = null; + $setRenderWhitespaceMarkers(this, false); } }, get: function() { @@ -51,19 +50,35 @@ config.defineOptions(Editor.prototype, "editor", { } }); -function $onChangeSelectionForWhitespace() { - let invisibleMarkerId = this.session.$invisibleMarkerId; - if (invisibleMarkerId) { - this.session.removeTextMarker(invisibleMarkerId); - this.session.$invisibleMarkerId = null; +function $setRenderWhitespaceMarkers(editor, render) { + var textLayer = editor.renderer && editor.renderer.$textLayer; + if (!textLayer || typeof textLayer.setRenderWhitespaceMarkers !== "function") + return; + + textLayer.setRenderWhitespaceMarkers(render); + editor.renderer.updateText(); +} + +function $removeWhitespaceMarkers(session) { + if (!session) return; + + var invisibleMarkerIds = session.$invisibleMarkerIds || []; + for (var i = 0; i < invisibleMarkerIds.length; i++) { + session.removeTextMarker(invisibleMarkerIds[i]); } + session.$invisibleMarkerIds = []; +} + +function $onChangeSelectionForWhitespace() { + $removeWhitespaceMarkers(this.session); - var currentRange = this.selection.getRange(); - if (!currentRange.isEmpty()) { - this.session.$invisibleMarkerId = this.session.addTextMarker( - currentRange, - "ace_whitespaces_in_selection", - "invisible" - ); + var ranges = typeof this.selection.getAllRanges === "function" ? this.selection.getAllRanges() + : [this.selection.getRange()]; + + for (var j = 0; j < ranges.length; j++) { + if (!ranges[j].isEmpty()) { + this.session.$invisibleMarkerIds.push( + this.session.addTextMarker(ranges[j], "ace_whitespaces_in_selection", "invisible")); + } } -} \ No newline at end of file +} diff --git a/src/ext/whitespaces_in_selection_test.js b/src/ext/whitespaces_in_selection_test.js index 47552c5a56e..f4d162d0cd7 100644 --- a/src/ext/whitespaces_in_selection_test.js +++ b/src/ext/whitespaces_in_selection_test.js @@ -1,9 +1,11 @@ "use strict"; require("../test/mockdom"); +require("../multi_select"); var assert = require("assert"); var EditSession = require("../edit_session").EditSession; var Editor = require("../editor").Editor; +var Range = require("../range").Range; var MockRenderer = require("../test/mockrenderer").MockRenderer; require("./whitespaces_in_selection"); @@ -25,15 +27,45 @@ module.exports = { assert.ok(this.editor.$boundChangeSelectionForWhitespace); }, + "test: toggling extension updates whitespace marker rendering": function() { + var renderValues = []; + var updateCount = 0; + this.editor.renderer.$textLayer = { + setRenderWhitespaceMarkers: function(render) { + renderValues.push(render); + } + }; + this.editor.renderer.updateText = function() { + updateCount++; + }; + + this.editor.setOption("showWhitespacesInSelection", true); + this.editor.setOption("showWhitespacesInSelection", false); + + assert.deepEqual(renderValues, [true, false]); + assert.equal(updateCount, 2); + }, + "test: turning off extension": function() { this.editor.setOption("showWhitespacesInSelection", true); assert.equal(this.editor.getOption("showWhitespacesInSelection"), true); this.editor.selection.setRange({start: {row: 0, column: 0}, end: {row: 0, column: 5}}); + this.editor.selection.addRange(new Range(1, 4, 1, 8)); + + var markerIds = this.session.$invisibleMarkerIds.slice(); + assert.equal(markerIds.length, 2); + markerIds.forEach(function(markerId) { + assert.ok(this.session.getTextMarkers()[markerId]); + }, this); this.editor.setOption("showWhitespacesInSelection", false); assert.equal(this.editor.getOption("showWhitespacesInSelection"), false); assert.equal(this.editor.$boundChangeSelectionForWhitespace, null); + assert.deepEqual(this.session.$invisibleMarkerIds, []); + markerIds.forEach(function(markerId) { + assert.ok(!this.session.getTextMarkers()[markerId]); + }, this); }, "test: marker present after selection": function() { @@ -41,12 +73,28 @@ module.exports = { this.editor.selection.setRange({start: {row: 0, column: 0}, end: {row: 0, column: 5}}); - assert.ok(this.session.$invisibleMarkerId); + assert.equal(this.session.$invisibleMarkerIds.length, 1); + + var markers = this.session.getTextMarkers(); + var marker = markers[this.session.$invisibleMarkerIds[0]]; + assert.ok(marker); + assert.equal(marker.className, "ace_whitespaces_in_selection"); + }, + + "test: markers present after multiple selections": function() { + this.editor.setOption("showWhitespacesInSelection", true); + + this.editor.selection.setRange(new Range(0, 0, 0, 5)); + this.editor.selection.addRange(new Range(1, 4, 1, 8)); + + assert.equal(this.session.$invisibleMarkerIds.length, 2); var markers = this.session.getTextMarkers(); - assert.ok(markers[this.session.$invisibleMarkerId]); - assert.equal(markers[this.session.$invisibleMarkerId].className, "ace_whitespaces_in_selection"); + this.session.$invisibleMarkerIds.forEach(function(markerId) { + assert.ok(markers[markerId]); + assert.equal(markers[markerId].className, "ace_whitespaces_in_selection"); + }); } }; -require("../test/run")(module); \ No newline at end of file +require("../test/run")(module); diff --git a/src/layer/text.js b/src/layer/text.js index 286e1303509..017cfb0c7ed 100644 --- a/src/layer/text.js +++ b/src/layer/text.js @@ -132,11 +132,15 @@ class Text { $computeTabString() { var tabSize = this.session.getTabSize(); this.tabSize = tabSize; + var renderTabGlyphs = this.showTabs || this.$renderWhitespaceMarkers; + var renderSpaceGlyphs = this.showSpaces || this.$renderWhitespaceMarkers; /**@type{any}*/var tabStr = this.$tabStrings = [0]; for (var i = 1; i < tabSize + 1; i++) { - if (this.showTabs) { + if (renderTabGlyphs) { var span = this.dom.createElement("span"); span.className = "ace_invisible ace_invisible_tab"; + if (!this.showTabs) + span.className += " ace_invisible_hidden"; span.textContent = lang.stringRepeat(this.TAB_CHAR, i); tabStr.push(span); } else { @@ -146,13 +150,17 @@ class Text { if (this.displayIndentGuides) { this.$indentGuideRe = /\s\S| \t|\t |\s$/; var className = "ace_indent-guide"; - var spaceClass = this.showSpaces ? " ace_invisible ace_invisible_space" : ""; - var spaceContent = this.showSpaces + var spaceClass = renderSpaceGlyphs ? " ace_invisible ace_invisible_space" : ""; + if (renderSpaceGlyphs && !this.showSpaces) + spaceClass += " ace_invisible_hidden"; + var spaceContent = renderSpaceGlyphs ? lang.stringRepeat(this.SPACE_CHAR, this.tabSize) : lang.stringRepeat(" ", this.tabSize); - var tabClass = this.showTabs ? " ace_invisible ace_invisible_tab" : ""; - var tabContent = this.showTabs + var tabClass = renderTabGlyphs ? " ace_invisible ace_invisible_tab" : ""; + if (renderTabGlyphs && !this.showTabs) + tabClass += " ace_invisible_hidden"; + var tabContent = renderTabGlyphs ? lang.stringRepeat(this.TAB_CHAR, this.tabSize) : spaceContent; @@ -363,7 +371,7 @@ class Text { var controlCharacter = m[3]; var cjkSpace = m[4]; - if (!self.showSpaces && simpleSpace) + if (!self.showSpaces && !self.$renderWhitespaceMarkers && simpleSpace) continue; var before = i != m.index ? value.slice(i, m.index) : ""; @@ -381,9 +389,11 @@ class Text { valueFragment.appendChild(text); screenColumn += tabSize - 1; } else if (simpleSpace) { - if (self.showSpaces) { + if (self.showSpaces || self.$renderWhitespaceMarkers) { var span = this.dom.createElement("span"); span.className = "ace_invisible ace_invisible_space"; + if (!self.showSpaces) + span.className += " ace_invisible_hidden"; span.textContent = lang.stringRepeat(self.SPACE_CHAR, simpleSpace.length); valueFragment.appendChild(span); } else { @@ -395,9 +405,11 @@ class Text { span.textContent = lang.stringRepeat(self.SPACE_CHAR, controlCharacter.length); valueFragment.appendChild(span); } else if (cjkSpace) { - if (self.showSpaces) { + if (self.showSpaces || self.$renderWhitespaceMarkers) { var span = this.dom.createElement("span"); span.className = "ace_invisible ace_invisible_space"; + if (!self.showSpaces) + span.className += " ace_invisible_hidden"; span.textContent = lang.stringRepeat(self.CJK_SPACE_CHAR, cjkSpace.length); valueFragment.appendChild(span); } else { @@ -533,12 +545,10 @@ class Text { return; } } - var childNodes = element.childNodes; - if (childNodes) { - let node = childNodes[indentLevel - 1]; - if (node && node.classList && node.classList.contains("ace_indent-guide")) node.classList.add( - "ace_indent-guide-active"); - } + var indentGuides = element.querySelectorAll(".ace_indent-guide"); + var node = indentGuides[indentLevel - 1]; + if (node) + node.classList.add("ace_indent-guide-active"); } } @@ -792,6 +802,7 @@ Text.prototype.showInvisibles = false; Text.prototype.showSpaces = false; Text.prototype.showTabs = false; Text.prototype.showEOL = false; +Text.prototype.$renderWhitespaceMarkers = false; Text.prototype.displayIndentGuides = true; Text.prototype.$highlightIndentGuides = true; Text.prototype.$tabStrings = []; diff --git a/src/layer/text_markers.js b/src/layer/text_markers.js index 2256a8ce077..ea75239c21b 100644 --- a/src/layer/text_markers.js +++ b/src/layer/text_markers.js @@ -1,5 +1,4 @@ var Text = require("./text").Text; -var lang = require("../lib/lang"); /** * @typedef TextMarker * @property {import("../../ace-internal").Ace.IRange} range @@ -16,6 +15,20 @@ var lang = require("../lib/lang"); */ var textMarkerMixin = { + /** + * Pre-renders whitespace glyphs so text markers can reveal them without + * changing the text layout as the marker range changes. + * + * @param {boolean} render + */ + setRenderWhitespaceMarkers(render) { + render = !!render; + if (this.$renderWhitespaceMarkers === render) + return; + + this.$renderWhitespaceMarkers = render; + this.$computeTabString(); + }, /** * @param {string} className * @this {Text} @@ -27,12 +40,10 @@ var textMarkerMixin = { var element = selectedElements[i]; element.classList.remove(className); - if (element.hasAttribute('data-whitespace')) { - var originalWhitespace = element.getAttribute('data-whitespace'); - var textNode = this.dom.createTextNode(originalWhitespace, this.element); - textNode["charCount"] = element["charCount"]; - element.parentNode.replaceChild(textNode, element); - } + if (element.classList.contains("ace_invisible_tab") && !this.showTabs) + element.classList.add("ace_invisible_hidden"); + else if (element.classList.contains("ace_invisible_space") && !this.showSpaces) + element.classList.add("ace_invisible_hidden"); } }, /** @@ -150,59 +161,50 @@ var textMarkerMixin = { * @param {Node} node - The DOM node to process * @param {Node} parentNode - The parent node * @param {SelectionSegment} selectionSegment - * @param {object} marker - The marker being applied + * @param {object} marker - The marker being applied */ $processInvisibleMarker(node, parentNode, selectionSegment, marker) { - var nodeText = node.textContent || ''; - if (node.nodeType === 3) { // Text node - var fragment = this.dom.createFragment(this.element); - - if (selectionSegment.beforeSelection > 0) { - fragment.appendChild( - this.dom.createTextNode(nodeText.substring(0, selectionSegment.beforeSelection), this.element)); - } + var element = /** @type {HTMLElement} */ (node.nodeType === 1 ? node : parentNode); + if (!element.classList || !element.classList.contains("ace_invisible_hidden")) + return; - if (selectionSegment.selectionLength > 0) { - var selectedText = selectionSegment.beforeSelection === 0 && selectionSegment.afterSelection === 0 - ? nodeText : nodeText.substring( - selectionSegment.beforeSelection, - selectionSegment.beforeSelection + selectionSegment.selectionLength - ); + if (selectionSegment.beforeSelection === 0 && selectionSegment.afterSelection === 0) { + element.classList.remove("ace_invisible_hidden"); + element.classList.add(marker.className); + return; + } - var segments = selectedText.match(/\s+|[^\s]+/g) || []; - - for (let k = 0; k < segments.length; k++) { - var segment = segments[k]; - let span; - if (/^\s+$/.test(segment)) { - span = this.dom.createElement("span"); - span.className = marker.className; - span.textContent = - node["charCount"] ? this.TAB_CHAR.repeat(segment.length) - : segment.replace(/\u3000/g, this.CJK_SPACE_CHAR).replace(/ /g, this.SPACE_CHAR); - span.setAttribute("data-whitespace", segment); - fragment.appendChild(span); - } - else { - span = this.dom.createElement("span"); - span.textContent = segment; - } - if (node["charCount"] && segments.length === 1) { //this is for real tabs - span["charCount"] = node["charCount"]; - } - fragment.appendChild(span); - } + var elementText = element.textContent || ""; + var fragment = this.dom.createFragment(this.element); + var appendSegment = (start, length, selected) => { + if (length <= 0) + return; + + var segment = /** @type {HTMLElement} */ (element.cloneNode(false)); + segment.textContent = elementText.substring(start, start + length); + if (element.classList.contains("ace_indent-guide") && start + length < elementText.length) { + segment.classList.remove("ace_indent-guide"); + segment.classList.remove("ace_indent-guide-active"); } - - if (selectionSegment.afterSelection > 0) { - fragment.appendChild(this.dom.createTextNode( - nodeText.substring(selectionSegment.beforeSelection + selectionSegment.selectionLength), - this.element - )); + if (selected) { + segment.classList.remove("ace_invisible_hidden"); + segment.classList.add(marker.className); } + fragment.appendChild(segment); + }; - parentNode.replaceChild(fragment, node); - } + appendSegment(0, selectionSegment.beforeSelection, false); + appendSegment( + selectionSegment.beforeSelection, + selectionSegment.selectionLength, + true + ); + appendSegment( + selectionSegment.beforeSelection + selectionSegment.selectionLength, + selectionSegment.afterSelection, + false + ); + element.parentNode.replaceChild(fragment, element); }, /** @@ -385,4 +387,4 @@ require("../config").defineOptions(Editor.prototype, "editor", { }); exports.textMarkerMixin = textMarkerMixin; -exports.editSessionTextMarkerMixin = editSessionTextMarkerMixin; \ No newline at end of file +exports.editSessionTextMarkerMixin = editSessionTextMarkerMixin; diff --git a/src/layer/text_markers_test.js b/src/layer/text_markers_test.js index 1a75379e17b..96e16b6cd81 100644 --- a/src/layer/text_markers_test.js +++ b/src/layer/text_markers_test.js @@ -8,7 +8,6 @@ var assert = require("../test/assertions"); var EditSession = require("../edit_session").EditSession; var TextLayer = require("./text").Text; var JavaScriptMode = require("../mode/javascript").Mode; -var dom = require("../lib/dom"); var Range = require("../range").Range; require("./text_markers"); @@ -185,6 +184,7 @@ module.exports = { "test: invisible marker with mixed whitespace and complete cleanup verification": function() { var value = "function\t test() { //test comment\n var x = 1;\n}"; this.session.setValue(value); + this.textLayer.setRenderWhitespaceMarkers(true); this.textLayer.update(this.textLayer.config); @@ -202,18 +202,17 @@ module.exports = { assert.ok(hasTabSymbol, "Should contain TAB_CHAR symbol"); assert.ok(hasSpaceSymbol, "Should contain SPACE_CHAR symbol"); - var result = normalize(`function - ———— - ·· - test - ( - ) - · - {· - //test - ···· comment`); - var actual = normalize(this.textLayer.element.childNodes[0].innerHTML); - assert.equal(actual, result); + markerElements.forEach(element => { + assert.ok(element.classList.contains("ace_invisible")); + assert.ok(!element.classList.contains("ace_invisible_hidden")); + }); + + var tabMarker = line.querySelector(".ace_invisible_tab.invisible-marker"); + assert.ok(tabMarker, "Tab marker should be applied"); + assert.equal(tabMarker["charCount"], 1, "Tab marker should preserve its logical length"); + + var hiddenElements = line.querySelectorAll(".ace_invisible_hidden"); + assert.ok(hiddenElements.length > 0, "Whitespace outside the marker should remain hidden"); this.session.removeTextMarker(markerId); this.textLayer.$applyTextMarkers(); @@ -222,14 +221,97 @@ module.exports = { assert.equal(markerElements.length, 0, "Marker class should be removed"); var finalLine = this.textLayer.element.childNodes[0]; - assert.ok(!finalLine.innerHTML.includes(this.textLayer.TAB_CHAR), - "TAB_CHAR should be removed from DOM"); - assert.ok(!finalLine.innerHTML.includes(this.textLayer.SPACE_CHAR), - "SPACE_CHAR should be removed from DOM"); + assert.ok(finalLine.innerHTML.includes(this.textLayer.TAB_CHAR), + "TAB_CHAR should remain in the stable DOM"); + assert.ok(finalLine.innerHTML.includes(this.textLayer.SPACE_CHAR), + "SPACE_CHAR should remain in the stable DOM"); + + var visibleWhitespace = Array.from(finalLine.querySelectorAll(".ace_invisible")).filter(element => { + return !element.classList.contains("ace_invisible_hidden") + && (element.classList.contains("ace_invisible_space") + || element.classList.contains("ace_invisible_tab")); + }); + assert.equal(visibleWhitespace.length, 0, "All whitespace glyphs should be hidden after cleanup"); + }, + + "test: invisible marker splits a pre-rendered whitespace run": function() { + this.session.setValue("//a b"); + this.textLayer.setRenderWhitespaceMarkers(true); + this.textLayer.update(this.textLayer.config); + + this.session.addTextMarker(new Range(0, 5, 0, 7), "invisible-marker", "invisible"); + this.textLayer.$applyTextMarkers(); + + var line = this.textLayer.element.childNodes[0]; + var markerElements = line.querySelectorAll(".invisible-marker"); + assert.equal(markerElements.length, 1); + assert.equal(markerElements[0].textContent, this.textLayer.SPACE_CHAR.repeat(2)); + + var hiddenElements = line.querySelectorAll(".ace_invisible_hidden"); + assert.equal(hiddenElements.length, 2); + assert.equal(hiddenElements[0].textContent, this.textLayer.SPACE_CHAR.repeat(2)); + assert.equal(hiddenElements[1].textContent, this.textLayer.SPACE_CHAR); + }, + + "test: splitting an indent guide preserves one guide decoration": function() { + this.session.setValue(" value"); + this.textLayer.setRenderWhitespaceMarkers(true); + this.textLayer.update(this.textLayer.config); + + this.session.addTextMarker(new Range(0, 1, 0, 3), "invisible-marker", "invisible"); + this.textLayer.$applyTextMarkers(); + + var line = this.textLayer.element.childNodes[0]; + assert.equal(line.querySelectorAll(".ace_indent-guide").length, 1); + assert.equal( + line.querySelector(".invisible-marker").textContent, + this.textLayer.SPACE_CHAR.repeat(2) + ); + this.textLayer.$setIndentGuideActive(this.textLayer.$lines.cells[0], 1); + assert.equal(line.querySelectorAll(".ace_indent-guide-active").length, 1); + }, + + "test: invisible marker does not modify globally visible whitespace": function() { + this.session.setValue("a b"); + this.textLayer.setRenderWhitespaceMarkers(true); + this.textLayer.setShowInvisibles(true); + this.textLayer.update(this.textLayer.config); + + this.session.addTextMarker(new Range(0, 1, 0, 3), "invisible-marker", "invisible"); + this.textLayer.$applyTextMarkers(); + + var line = this.textLayer.element.childNodes[0]; + assert.equal(line.querySelectorAll(".invisible-marker").length, 0); + assert.equal(line.querySelectorAll(".ace_invisible_hidden").length, 0); + assert.equal( + line.querySelector(".ace_invisible_space").textContent, + this.textLayer.SPACE_CHAR.repeat(2) + ); + + this.textLayer.setShowInvisibles(false); + this.textLayer.update(this.textLayer.config); + this.textLayer.$applyTextMarkers(); + + assert.equal( + this.textLayer.element.querySelectorAll(".invisible-marker").length, + 1, + "The retained marker should reveal whitespace when global invisibles are hidden" + ); + }, + + "test: invisible marker supports pre-rendered CJK spaces": function() { + this.session.setValue("a b"); + this.textLayer.setRenderWhitespaceMarkers(true); + this.textLayer.update(this.textLayer.config); + + this.session.addTextMarker(new Range(0, 1, 0, 2), "invisible-marker", "invisible"); + this.textLayer.$applyTextMarkers(); - var elementsWithWhitespace = this.textLayer.element.querySelectorAll('[data-whitespace]'); - assert.equal(elementsWithWhitespace.length, 0, "No data-whitespace attributes should remain"); + var marker = this.textLayer.element.querySelector(".invisible-marker"); + assert.ok(marker); + assert.equal(marker.textContent, this.textLayer.CJK_SPACE_CHAR); + assert.ok(!marker.classList.contains("ace_invisible_hidden")); }, }; -require("../test/run")(module); \ No newline at end of file +require("../test/run")(module); diff --git a/types/ace-modules.d.ts b/types/ace-modules.d.ts index 08e4c19aebf..81c7f7c4bdb 100644 --- a/types/ace-modules.d.ts +++ b/types/ace-modules.d.ts @@ -47,7 +47,7 @@ declare module "ace-code/src/layer/font_metrics" { bottom: number; }; /** - * Calculates the width of the text up to a specific scrrenColumn on a given screen row. + * Calculates the width of the text up to a specific screenColumn on a given screen row. * * @param {number} screenRow - The row index on the screen for which the text width is calculated. * @param {number} screenColumn - The column index up to which the text width is measured. @@ -594,6 +594,7 @@ declare module "ace-code/src/layer/text" { export type LayerConfig = import("ace-code").Ace.LayerConfig; export type EditSession = import("ace-code/src/edit_session").EditSession; type TextMarkersMixin = { + setRenderWhitespaceMarkers(render: boolean): void; }; import dom = require("ace-code/src/lib/dom"); import { Lines } from "ace-code/src/layer/lines"; @@ -3817,6 +3818,12 @@ declare module "ace-code/src/layer/text_markers" { afterSelection: number; }; export namespace textMarkerMixin { + /** + * Pre-renders whitespace glyphs so text markers can reveal them without + * changing the text layout as the marker range changes. + * + */ + function setRenderWhitespaceMarkers(render: boolean): void; function $removeClass(this: Text, className: string): void; function $applyTextMarkers(this: Text): void; /** @@ -3830,7 +3837,7 @@ declare module "ace-code/src/layer/text_markers" { * Process text nodes for invisible markers (whitespace visualization) * @param {Node} node - The DOM node to process * @param {Node} parentNode - The parent node - * @param {object} marker - The marker being applied + * @param {object} marker - The marker being applied */ function $processInvisibleMarker(node: Node, parentNode: Node, selectionSegment: SelectionSegment, marker: object): void; /**