diff --git a/frontend/app_flowy/packages/flowy_editor/lib/document/path.dart b/frontend/app_flowy/packages/flowy_editor/lib/document/path.dart index 35ac1f467e..a8163f094d 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/document/path.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/document/path.dart @@ -1 +1,7 @@ +import 'package:flutter/foundation.dart'; + typedef Path = List<int>; + +bool pathEquals(Path path1, Path path2) { + return listEquals(path1, path2); +} diff --git a/frontend/app_flowy/packages/flowy_editor/lib/document/position.dart b/frontend/app_flowy/packages/flowy_editor/lib/document/position.dart new file mode 100644 index 0000000000..2c7d85f908 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_editor/lib/document/position.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +import './path.dart'; + +class Position { + final Path path; + final int offset; + + Position({ + required this.path, + this.offset = 0, + }); + + @override + bool operator==(Object other) { + if (other is! Position) { + return false; + } + return pathEquals(path, other.path) && offset == other.offset; + } + + @override + int get hashCode { + final pathHash = hashList(path); + return pathHash ^ offset; + } + +} diff --git a/frontend/app_flowy/packages/flowy_editor/lib/document/selection.dart b/frontend/app_flowy/packages/flowy_editor/lib/document/selection.dart new file mode 100644 index 0000000000..a03ccae37f --- /dev/null +++ b/frontend/app_flowy/packages/flowy_editor/lib/document/selection.dart @@ -0,0 +1,28 @@ +import './position.dart'; + +class Selection { + final Position start; + final Position end; + + Selection({ + required this.start, + required this.end, + }); + + factory Selection.collapsed(Position pos) { + return Selection(start: pos, end: pos); + } + + Selection collapse({ bool atStart = false }) { + if (atStart) { + return Selection(start: start, end: start); + } else { + return Selection(start: end, end: end); + } + } + + bool isCollapsed() { + return start == end; + } + +} diff --git a/frontend/app_flowy/packages/flowy_editor/test/flowy_editor_test.dart b/frontend/app_flowy/packages/flowy_editor/test/flowy_editor_test.dart index e8f14bc9c7..cb67f61d96 100644 --- a/frontend/app_flowy/packages/flowy_editor/test/flowy_editor_test.dart +++ b/frontend/app_flowy/packages/flowy_editor/test/flowy_editor_test.dart @@ -2,6 +2,10 @@ import 'dart:convert'; import 'package:flowy_editor/document/node.dart'; import 'package:flowy_editor/document/state_tree.dart'; +import 'package:flowy_editor/document/path.dart'; +import 'package:flowy_editor/document/position.dart'; +import 'package:flowy_editor/document/selection.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -61,4 +65,69 @@ void main() { expect(updatedNode != null, true); expect(updatedNode!.attributes['text-type'], 'heading1'); }); + + test('test path utils 1', () { + final path1 = <int>[1]; + final path2 = <int>[1]; + expect(pathEquals(path1, path2), true); + + expect(hashList(path1), hashList(path2)); + }); + + test('test path utils 2', () { + final path1 = <int>[1]; + final path2 = <int>[2]; + expect(pathEquals(path1, path2), false); + + expect(hashList(path1) != hashList(path2), true); + }); + + test('test position comparator', () { + final pos1 = Position(path: [1], offset: 0); + final pos2 = Position(path: [1], offset: 0); + expect(pos1 == pos2, true); + expect(pos1.hashCode == pos2.hashCode, true); + }); + + test('test position comparator with offset', () { + final pos1 = Position(path: [1, 1, 1, 1, 1], offset: 100); + final pos2 = Position(path: [1, 1, 1, 1, 1], offset: 100); + expect(pos1, pos2); + expect(pos1.hashCode, pos2.hashCode); + }); + + test('test position comparator false', () { + final pos1 = Position(path: [1, 1, 1, 1, 1], offset: 100); + final pos2 = Position(path: [1, 1, 2, 1, 1], offset: 100); + expect(pos1 == pos2, false); + expect(pos1.hashCode == pos2.hashCode, false); + }); + + test('test position comparator with offset false', () { + final pos1 = Position(path: [1, 1, 1, 1, 1], offset: 100); + final pos2 = Position(path: [1, 1, 1, 1, 1], offset: 101); + expect(pos1 == pos2, false); + expect(pos1.hashCode == pos2.hashCode, false); + }); + + test('test selection comparator', () { + final pos = Position(path: [0], offset: 0); + final sel = Selection.collapsed(pos); + expect(sel.start, sel.end); + expect(sel.isCollapsed(), true); + }); + + test('test selection collapse', () { + final start = Position(path: [0], offset: 0); + final end = Position(path: [0], offset: 10); + final sel = Selection(start: start, end: end); + + final collapsedSelAtStart = sel.collapse(atStart: true); + expect(collapsedSelAtStart.start, start); + expect(collapsedSelAtStart.end, start); + + final collapsedSelAtEnd = sel.collapse(); + expect(collapsedSelAtEnd.start, end); + expect(collapsedSelAtEnd.end, end); + }); }