Skip to content

Commit 49adb25

Browse files
CatHood0CatHood0
andauthored
Chore: improved general API (#6)
Co-authored-by: CatHood0 <santiagowmar@gmail.com>
1 parent e4dc564 commit 49adb25

14 files changed

+160
-28
lines changed

analysis_options.yaml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
1+
include: package:flutter_lints/flutter.yaml
2+
13
analyzer:
24
errors:
35
depend_on_referenced_packages: ignore
46
prefer_const_literals_to_create_immutables: ignore
5-
include: package:flutter_lints/flutter.yaml
7+
language:
8+
strict-raw-types: true
9+
10+
linter:
11+
rules:
12+
hash_and_equals: true
13+
implicit_reopen: true
14+
cancel_subscriptions: true
15+
close_sinks: true
16+
avoid_web_libraries_in_flutter: true
17+
avoid_type_to_string: true
18+
avoid_relative_lib_imports: true
19+
avoid_empty_else: true
20+
avoid_dynamic_calls: false
21+
unnecessary_statements: true
22+
use_key_in_widget_constructors: true
23+
prefer_conditional_assignment: true
24+
prefer_const_declarations: true
25+
use_string_buffers: true
26+
valid_regexps: true
27+
control_flow_in_finally: true
28+
always_specify_types: false
Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,21 @@ class Line {
4242
String? id,
4343
Map<String, dynamic>? attributes,
4444
}) : _fragments = List.from(
45-
[
45+
<dynamic>[
4646
TextFragment(
4747
data: data,
4848
attributes: attributes,
4949
)
5050
],
5151
),
5252
id = id == null || id.trim().isEmpty ? nanoid(8) : id,
53-
_sealed = data == '\n' || data is Map ? true : false;
53+
_sealed = data == '\n' || data is Map ? true : false;
5454

55-
Line.newLine({String? id})
56-
: _fragments = List.from(
57-
[
55+
Line.newLine({
56+
String? id,
57+
@visibleForTesting bool enableTesting = false,
58+
}) : _fragments = List.from(
59+
<dynamic>[
5860
TextFragment(
5961
data: '\n',
6062
)
@@ -134,7 +136,7 @@ class Line {
134136
throw StateError(
135137
'Element of type ${fragment.runtimeType} cannot be inserted before at $index when $runtimeType is sealed');
136138
}
137-
_fragments.insert(index - 1, fragment);
139+
insertAt(index - 1, fragment);
138140
return;
139141
}
140142

@@ -159,6 +161,8 @@ class Line {
159161
if (areAttributesEquals) {
160162
final String previousData = previous.data as String;
161163
final String newData = '$previousData${fragment.data}';
164+
// does not require a reorganization of siblings
165+
// since we're merging two fragments in just one
162166
_fragments[lastIndex] = TextFragment(
163167
data: newData,
164168
attributes: previous.attributes,
@@ -183,26 +187,48 @@ class Line {
183187
);
184188
}
185189

190+
/// Get a secure copy of the fragments into this Line
186191
List<TextFragment> get fragments =>
187192
List<TextFragment>.unmodifiable(_fragments);
193+
194+
/// Get all direct instances of the fragments into this Line
195+
///
196+
/// This is called `unsafeLines` because this ones can be modified
197+
/// but, all the changes won't be notified to this Line (like reorganizing
198+
/// siblings)
199+
List<TextFragment> unsafeFragments() => _fragments;
200+
188201
@visibleForTesting
202+
@Deprecated('rawFragments is no longer '
203+
'used and will be removed '
204+
'in future releases. '
205+
'Please, use unsafeFragments instead')
189206
List<TextFragment> get rawFragments => _fragments;
207+
190208
int get length => _fragments.length;
191-
bool get isSingle => _fragments.length == 1;
209+
210+
bool get isSingle => length == 1;
211+
192212
String get toPlainText => _fragments
193213
.map<String>(
194214
(TextFragment e) => e.data is! String ? '' : e.data.toString(),
195215
)
196216
.join();
217+
197218
int get textLength => _fragments
198219
.map<int>(
199220
(TextFragment e) => e.data is! String ? 1 : e.data.toString().length,
200221
)
201222
.fold(0, (int a, int b) => a + b);
223+
202224
bool get isNewLine => isSingle ? _fragments.single.data == '\n' : false;
225+
203226
bool get isSealed => _sealed;
227+
204228
bool get isEmbedFragment => _fragments.single.data is Map<String, dynamic>;
229+
205230
bool get isTextInsert => _fragments.isEmpty || _fragments.first.data != '\n';
231+
206232
TextFragment? get first => _fragments.firstOrNull;
207233
TextFragment? get last => _fragments.lastOrNull;
208234
bool get isEmpty => _fragments.isEmpty;
@@ -224,15 +250,16 @@ class Line {
224250
..write(indent);
225251
return str;
226252
}).join();
227-
return '${indent}Line: <Sealed value:$_sealed> [\n$rawFragments${'$indent '}]';
253+
final String sealedStr = _sealed ? ' <Sealed>' : "";
254+
return '${indent}Line:$sealedStr [\n$rawFragments${'$indent '}]';
228255
}
229256

230257
TextFragment elementAt(int index) {
231-
return _fragments.elementAt(index);
258+
return _fragments[index];
232259
}
233260

234261
TextFragment? elementAtOrNull(int index) {
235-
return _fragments.elementAtOrNull(index);
262+
return index < 0 || index >= length ? null : _fragments[index];
236263
}
237264

238265
TextFragment operator [](int index) {
@@ -243,12 +270,37 @@ class Line {
243270
_fragments[index] = fragment;
244271
}
245272

273+
bool equals(covariant Line other, {bool full = false}) {
274+
if (full) {
275+
return id == other.id &&
276+
_equality.equals(
277+
_fragments,
278+
other._fragments,
279+
) &&
280+
fullHashCode == other.fullHashCode;
281+
}
282+
283+
return this == other;
284+
}
285+
246286
@override
247287
bool operator ==(covariant Line other) {
248288
if (identical(this, other)) return true;
249-
return const ListEquality().equals(_fragments, other._fragments);
289+
return _equality.equals(
290+
_fragments,
291+
other._fragments,
292+
);
250293
}
251294

252295
@override
253-
int get hashCode => Object.hashAll([_fragments]);
296+
int get hashCode => Object.hashAll(<Object?>[
297+
_fragments,
298+
]);
299+
300+
int get fullHashCode => Object.hashAll(<Object?>[
301+
id,
302+
_fragments,
303+
]);
254304
}
305+
306+
const ListEquality<TextFragment> _equality = ListEquality();
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@ class Paragraph {
312312
throw StateError(
313313
'Element of type ${fragment.runtimeType} cannot be inserted when $runtimeType is sealed');
314314
}
315-
_lines[_lines.length - 1].addFragment(fragment);
315+
final Line line = _lines[_lines.length - 1];
316+
line.addFragment(fragment);
316317
}
317318

318319
void removeLastLineIfNeeded() {
Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:collection/collection.dart';
2+
import 'package:flutter_quill_delta_easy_parser/extensions/extensions.dart';
23

34
class TextFragment {
45
/// The main data object associated with the line.
@@ -13,6 +14,18 @@ class TextFragment {
1314
this.attributes,
1415
});
1516

17+
/// Determines if this contains a custom object
18+
bool get isEmbedFragment => data is! String;
19+
20+
/// Determines if this contains plain text
21+
bool get isText => data is String;
22+
23+
/// Determines if this `TextFragment` is fully empty
24+
/// with no data into it
25+
bool get isBlank => isText
26+
? data.cast<String>().trim().isEmpty
27+
: data.cast<dynamic>().isEmpty;
28+
1629
/// Sets the attributes of the line to [attrs].
1730
///
1831
/// If [attrs] is `null`, no changes are made to the current attributes.
@@ -31,22 +44,52 @@ class TextFragment {
3144
/// Creates a deep copy of the current [TextFragment] instance.
3245
TextFragment get clone => TextFragment(data: data, attributes: attributes);
3346

47+
/// Get the string contained by this fragment
48+
///
49+
/// Return an empty string if the element is an embed fragment
50+
String getTextValue() {
51+
if (isEmbedFragment) return "";
52+
return data.cast<String>();
53+
}
54+
55+
/// Get the object contained by this fragment
56+
T? getValue<T extends Object>() {
57+
return data.castOrNull<T>();
58+
}
59+
60+
/// Get the object map contained by this fragment
61+
Map<String, dynamic> getEmbedValue() {
62+
return getValue<Map<String, dynamic>>()!;
63+
}
64+
3465
/// Clears the data and attributes of the line, setting them to `null`.
66+
@Deprecated(
67+
'cleanLine is no longer used and will be removed in future releases. Use cleanAttributes instead')
3568
void cleanLine() {
69+
data is String ? data = '' : data = <String, dynamic>{};
70+
attributes = null;
71+
}
72+
73+
/// Clears the data and attributes of the line, setting them to `null`.
74+
void cleanAttributes() {
3675
attributes = null;
3776
}
3877

3978
@override
4079
String toString() {
4180
attributes ??= null;
42-
return 'TextFragment: "${data is String ? '$data'.replaceAll('\n', '\\n') : data}"${attributes == null ? '' : ', attributes: $attributes'}';
81+
return 'TextFragment: "${data is String ? '$data'.replaceAll('\n', '\\n') : data}'
82+
'"${attributes == null ? '' : ', attributes: $attributes'}';
4383
}
4484

4585
@override
4686
bool operator ==(covariant TextFragment other) {
4787
if (identical(this, other)) return true;
4888
return (data is Map && other.data is Map
49-
? _equality.equals(data as Map, other.data as Map)
89+
? _equality.equals(
90+
data as Map,
91+
other.data as Map,
92+
)
5093
: data == other.data) &&
5194
_equality.equals(
5295
attributes,
@@ -58,4 +101,5 @@ class TextFragment {
58101
int get hashCode => Object.hash(data, attributes);
59102
}
60103

104+
// ignore: always_specify_types, strict_raw_type
61105
const MapEquality _equality = MapEquality();
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)