wrapWithStartAndEnd method
Wraps the current text-selection with the provided tags. If no text is selected, an empty tag-pair is inserted at the current cursor position. If the field is not focused, the empty tag-pair is appended to the current text.
Start- and End-Tag do not have to be the same, allowing properties in the tag.
Implementation
void wrapWithStartAndEnd(TagOperation op) {
_cache();
int opStart = selection.start;
int opEnd = selection.end;
NodeV3 tree = LightHtmlParserV3().parse(text);
if (opStart == opEnd) {
var startTag = "<${op.tagName}>";
var endTag = "</${op.tagName}>";
text = text.substring(0, opStart) +
"$startTag$endTag" +
text.substring(opStart);
opStart += startTag.length;
opEnd += startTag.length;
tree = LightHtmlParserV3().parse(text);
} else {
bool wasOffset = false;
// pass 1: split only partly affected nodes into new simple nodes
List<NodeV3> affectedNodes = tree.select(opStart, opEnd);
affectedNodes.forEach((element) {
element.splitForSelection(opStart, opEnd);
});
affectedNodes = tree.select(opStart, opEnd);
// snap selection to closest content node
opStart = affectedNodes
.map((e) => e.scopeStart)
.reduce((value, element) => element > value ? value : element);
opEnd = affectedNodes
.map((e) => e.scopeEnd)
.reduce((value, element) => element < value ? value : element);
// pass 2: apply new tag to all (now only full-selection) nodes
Tag tagToInsert = Tag.decodeTag(op.startTag);
for (var affectedNode in affectedNodes) {
var subtreeRootNew = affectedNode.insertTagNodeAbove(tagToInsert);
if (!wasOffset) {
opStart += subtreeRootNew.tag!.size;
opEnd = opStart + affectedNode.content!.length;
wasOffset = true;
}
}
if (editorFocusNode != null) {
editorFocusNode!.requestFocus();
}
}
value = value.copyWith(
text: tree.toHtml(),
selection: TextSelection(
baseOffset: opStart,
extentOffset: opEnd,
),
);
}