zefyr

block.dart 2.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. // Copyright (c) 2018, the Zefyr project authors. Please see the AUTHORS file
  2. // for details. All rights reserved. Use of this source code is governed by a
  3. // BSD-style license that can be found in the LICENSE file.
  4. import 'package:quill_delta/quill_delta.dart';
  5. import 'attributes.dart';
  6. import 'line.dart';
  7. import 'node.dart';
  8. /// A block represents a group of adjacent [LineNode]s with the same block
  9. /// style.
  10. ///
  11. /// Block examples: lists, quotes, code snippets.
  12. class BlockNode extends ContainerNode<LineNode>
  13. with StyledNodeMixin
  14. implements StyledNode {
  15. /// Creates new unmounted [BlockNode] with the same attributes.
  16. BlockNode clone() {
  17. final node = BlockNode();
  18. node.applyStyle(style);
  19. return node;
  20. }
  21. /// Unwraps [line] from this block.
  22. void unwrapLine(LineNode line) {
  23. assert(children.contains(line));
  24. if (line.isFirst) {
  25. line.unlink();
  26. insertBefore(line);
  27. } else if (line.isLast) {
  28. line.unlink();
  29. insertAfter(line);
  30. } else {
  31. /// need to split this block into two as [line] is in the middle.
  32. final before = clone();
  33. insertBefore(before);
  34. LineNode child = first;
  35. while (child != line) {
  36. child.unlink();
  37. before.add(child);
  38. child = first as LineNode;
  39. }
  40. line.unlink();
  41. insertBefore(line);
  42. }
  43. optimize();
  44. }
  45. @override
  46. LineNode get defaultChild => LineNode();
  47. @override
  48. Delta toDelta() {
  49. // Line nodes take care of incorporating block style into their delta.
  50. return children
  51. .map((child) => child.toDelta())
  52. .fold(Delta(), (a, b) => a.concat(b));
  53. }
  54. @override
  55. String toString() {
  56. final block = style.value(NotusAttribute.block);
  57. final buffer = StringBuffer('§ {$block}\n');
  58. for (var child in children) {
  59. final tree = child.isLast ? '└' : '├';
  60. buffer.write(' $tree $child');
  61. if (!child.isLast) buffer.writeln();
  62. }
  63. return buffer.toString();
  64. }
  65. @override
  66. void optimize() {
  67. if (isEmpty) {
  68. final sibling = previous;
  69. unlink();
  70. if (sibling != null) sibling.optimize();
  71. return;
  72. }
  73. var block = this;
  74. if (!block.isFirst && block.previous is BlockNode) {
  75. BlockNode prev = block.previous;
  76. if (prev.style == block.style) {
  77. block.moveChildren(prev);
  78. block.unlink();
  79. block = prev;
  80. }
  81. }
  82. if (!block.isLast && block.next is BlockNode) {
  83. BlockNode nextBlock = block.next;
  84. if (nextBlock.style == block.style) {
  85. nextBlock.moveChildren(block);
  86. nextBlock.unlink();
  87. }
  88. }
  89. }
  90. }