singerdmx / flutter-quill

Rich text editor for Flutter
https://pub.dev/packages/flutter_quill
MIT License
2.43k stars 767 forks source link

What's the correct way to abort an insertion? #1959

Open oligazar opened 1 week ago

oligazar commented 1 week ago

Is there an existing issue for this?

The question

I need to implement text length limit in quill editor. As there's no way to set the limit afaik, I created an InsertRule, which should not allow to insert data into the document if the length limit is reached. The code looks like this

@override
  Delta? applyRule(
    Delta document,
    int index, {
    int? len,
    Object? data,
    Attribute<dynamic>? attribute,
  }) {
    final dataLength = data is String ? data.length : 0;
    final shouldAbort = textLength + dataLength > contentLengthLimit;
    if (shouldAbort) {
      // abort the changes
      return Delta();
    }
    // allow the changes
    return null;
  }

To avoid the document change an empty Delta is returned. But this results in Failed assertion

The following assertion was thrown during method call TextInputClient.updateEditingState:
'package:flutter_quill/src/models/documents/document.dart': Failed assertion: line 309 pos 12: 'delta.isNotEmpty': is not true.

So what would be the intended way of implementing this?

CatHood0 commented 1 week ago

Delta always need a new line at last operation. If you want a empty document, just clear it, and insert a new line. All should be fine

oligazar commented 1 week ago

@CatHood0 But I don't need a new line. I need to leave the document exactly the way it was before

CatHood0 commented 1 week ago

The new line is just a necessary thing, and it doesn't have any effect on the view. If you still want to maintain your document empty, we cannot do nothing about it. Delta works of that way

oligazar commented 1 week ago

@CatHood0 can you please give me a code example? Because I'm trying to do it like this, and it's definitely inserting a new line, which a want to avoid

if (shouldAbort) {
      if (dataLength > 1) {
        AppToasts.info(
          message: 'Unable to insert.\nContent length limit is reached.',
        ).show(get<AppRouter>().navigatorContext!);
      }
      return Delta()
        ..retain(index)
        ..insert('\n');
    }
CatHood0 commented 1 week ago

Which version are you using? Could be an error from the version

if (shouldAbort) { if (dataLength > 1) { AppToasts.info( message: 'Unable to insert.\nContent length limit is reached.', ).show(get().navigatorContext!); } return Delta() ..retain(index) ..insert('\n'); }

It looks like if the Delta just don't do the insertion. I think could be or a version error, or a wrong logic implementation at your code

This is the line that throws the error

assert(delta.isNotEmpty)

And this is how delta verify if your operations aren't empty

/// Returns `true` if this delta is not empty.
bool get isNotEmpty => operations.isNotEmpty;

I suggest first watch if the delta is inserting your new line as should be

oligazar commented 6 days ago

You're correct, returning Delta() fails the assertion as it doesn't have operations. You suggested inserting a new line saying it will leave the document as it was before. I did it like this:

eturn Delta()
..retain(index)
..insert('\n');

But it's inserting a new line. How can I avoid any unwanted changes to the document not getting any exceptions?

Maybe it's better to create PR, replacing assert(delta.isNotEmpty) with if (delta.isEmpty) return;? Would that work?

CatHood0 commented 6 days ago

@ellet0 can you see this?

ellet0 commented 6 days ago

@ellet0 can you see this?

I'm unfamiliar with the code that's related to how Delta rules work, I think we could provide an option to ignore this rule of a line at the end. It doesn't confirm to Quill though. Developers expect to have the compatible underline data with similar behavior, so I would suggest having the current rule as default.

The issue of having one line in the last delta operation, it shouldn't change the view or user experience. I remember there was a static const that represents an empty document that already has one line in the last operation.