wrapWithStartAndEnd method

void wrapWithStartAndEnd(
  1. TagOperation op
)

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,
    ),
  );
}