custom rules & optionally auto add newline for image embeds (#205)

pull/226/head^2
hyouuu 4 years ago committed by GitHub
parent 08412c167a
commit f816ad7ec8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 31
      lib/models/documents/document.dart
  2. 8
      lib/models/rules/rule.dart
  3. 4
      lib/widgets/controller.dart

@ -40,6 +40,10 @@ class Document {
final Rules _rules = Rules.getInstance();
void setCustomRules(List<Rule> customRules) {
_rules.setCustomRules(customRules);
}
final StreamController<Tuple3<Delta, Delta, ChangeSource>> _observer =
StreamController.broadcast();
@ -47,7 +51,7 @@ class Document {
Stream<Tuple3<Delta, Delta, ChangeSource>> get changes => _observer.stream;
Delta insert(int index, Object? data, {int replaceLength = 0}) {
Delta insert(int index, Object? data, {int replaceLength = 0, bool autoAppendNewlineAfterImage = true}) {
assert(index >= 0);
assert(data is String || data is Embeddable);
if (data is Embeddable) {
@ -58,7 +62,7 @@ class Document {
final delta = _rules.apply(RuleType.INSERT, this, index,
data: data, len: replaceLength);
compose(delta, ChangeSource.LOCAL);
compose(delta, ChangeSource.LOCAL, autoAppendNewlineAfterImage: autoAppendNewlineAfterImage);
return delta;
}
@ -71,7 +75,7 @@ class Document {
return delta;
}
Delta replace(int index, int len, Object? data) {
Delta replace(int index, int len, Object? data, {bool autoAppendNewlineAfterImage = true}) {
assert(index >= 0);
assert(data is String || data is Embeddable);
@ -84,7 +88,8 @@ class Document {
// We have to insert before applying delete rules
// Otherwise delete would be operating on stale document snapshot.
if (dataIsNotEmpty) {
delta = insert(index, data, replaceLength: len);
delta = insert(index, data, replaceLength: len,
autoAppendNewlineAfterImage: autoAppendNewlineAfterImage);
}
if (len > 0) {
@ -124,13 +129,13 @@ class Document {
return block.queryChild(res.offset, true);
}
void compose(Delta delta, ChangeSource changeSource) {
void compose(Delta delta, ChangeSource changeSource, {bool autoAppendNewlineAfterImage = true}) {
assert(!_observer.isClosed);
delta.trim();
assert(delta.isNotEmpty);
var offset = 0;
delta = _transform(delta);
delta = _transform(delta, autoAppendNewlineAfterImage: autoAppendNewlineAfterImage);
final originalDelta = toDelta();
for (final op in delta.toList()) {
final style =
@ -174,22 +179,28 @@ class Document {
bool get hasRedo => _history.hasRedo;
static Delta _transform(Delta delta) {
static Delta _transform(Delta delta, {bool autoAppendNewlineAfterImage = true}) {
final res = Delta();
final ops = delta.toList();
for (var i = 0; i < ops.length; i++) {
final op = ops[i];
res.push(op);
_handleImageInsert(i, ops, op, res);
if (autoAppendNewlineAfterImage) {
_autoAppendNewlineAfterImage(i, ops, op, res);
}
}
return res;
}
static void _handleImageInsert(
static void _autoAppendNewlineAfterImage(
int i, List<Operation> ops, Operation op, Delta res) {
final nextOpIsImage =
i + 1 < ops.length && ops[i + 1].isInsert && ops[i + 1].data is! String;
if (nextOpIsImage && !(op.data as String).endsWith('\n')) {
if (nextOpIsImage &&
op.data is String &&
(op.data as String).isNotEmpty &&
!(op.data as String).endsWith('\n'))
{
res.push(Operation.insert('\n'));
}
// Currently embed is equivalent to image and hence `is! String`

@ -28,6 +28,8 @@ abstract class Rule {
class Rules {
Rules(this._rules);
List<Rule> _customRules = [];
final List<Rule> _rules;
static final Rules _instance = Rules([
const FormatLinkAtCaretPositionRule(),
@ -49,10 +51,14 @@ class Rules {
static Rules getInstance() => _instance;
void setCustomRules(List<Rule> customRules) {
_customRules = customRules;
}
Delta apply(RuleType ruleType, Document document, int index,
{int? len, Object? data, Attribute? attribute}) {
final delta = document.toDelta();
for (final rule in _rules) {
for (final rule in _customRules + _rules) {
if (rule.type != ruleType) {
continue;
}

@ -92,12 +92,12 @@ class QuillController extends ChangeNotifier {
void replaceText(
int index, int len, Object? data, TextSelection? textSelection,
{bool ignoreFocus = false}) {
{bool ignoreFocus = false, bool autoAppendNewlineAfterImage = true}) {
assert(data is String || data is Embeddable);
Delta? delta;
if (len > 0 || data is! String || data.isNotEmpty) {
delta = document.replace(index, len, data);
delta = document.replace(index, len, data, autoAppendNewlineAfterImage: autoAppendNewlineAfterImage);
var shouldRetainDelta = toggledStyle.isNotEmpty &&
delta.isNotEmpty &&
delta.length <= 2 &&

Loading…
Cancel
Save