Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

July 16 2024 stable updates #12

Merged
merged 56 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
0ceed35
[SuperEditor][DemoApp] Fix content being deleted on iOS textfield dem…
angelosilvestre May 16, 2024
d78e77b
[SuperEditor][iOS] Fix IME mapping error after pressing the newline b…
angelosilvestre May 17, 2024
3f7c970
[SuperEditor][Website] Fix formatting toolbar (Resolves #1925) (#2013)
angelosilvestre May 18, 2024
c21e04f
[SuperEditor][SuperReader][SuperTextField] Update iOS magnifier (Reso…
angelosilvestre May 18, 2024
8a8b2ba
[SuperEditor] Show software keyboard upon tap (Resolves #1816) (#1978)
angelosilvestre May 20, 2024
4a063e1
[SuperEditor][Android] Hide expanded drag handles after deleting the …
angelosilvestre May 20, 2024
fa72e08
Document usage of density independent pixels (#2018)
angelosilvestre May 20, 2024
7082b08
[SuperTextField][iOS] Ignore auto-corrections after handling input ac…
angelosilvestre May 21, 2024
1a59e5e
[SuperEditor][Android] Show expanded handles when expanding the selec…
angelosilvestre May 22, 2024
d93f868
[SuperEditor][Mobile] Add caret customization (Resolves #1935) (#2020)
angelosilvestre May 22, 2024
cf78545
[SuperEditor] Add list item customization via stylesheet (Resolves #1…
angelosilvestre May 22, 2024
8592c1f
[SuperEditor] Fix composing region mapping when using markdown plugin…
angelosilvestre May 23, 2024
8ae3866
[SuperTextField][iOS] Don't show the mobile toolbar upon first tap (R…
angelosilvestre May 25, 2024
8df98c0
[SuperEditor] Convert empty task to paragraph on ENTER (Resolves #189…
angelosilvestre May 25, 2024
e0444e8
[SuperEditor][Android] Fix scrolling when dragging at empty space (Re…
angelosilvestre May 25, 2024
b5740c7
[SuperEditor][Android] Fix auto-scrolling start region (Resolves #200…
angelosilvestre May 26, 2024
728689f
Fix lint warnings (#2047)
matthew-carroll May 27, 2024
2845563
[super_editor_markdown] Prevent empty syntax from being converted (Re…
angelosilvestre May 28, 2024
1d6245b
[SuperEditor] Fix missing default task component builder (Resolves #2…
angelosilvestre May 28, 2024
c6364ff
Changed linkification on paste to match URL parsing within the linkif…
matthew-carroll May 28, 2024
416240b
Allow emojis at beginning of paragraph with tagging enabled (Resolves…
matthew-carroll May 28, 2024
314e233
[SuperEditor] Allow continuing list item sequence by typing (Resolves…
angelosilvestre Jun 1, 2024
86ca9d2
[SuperEditor] Split incompatible attributions (Resolves #1983) (#2035)
angelosilvestre Jun 1, 2024
d761133
[SuperEditor][Web] Prevent Cmd + Z and Ctrl + Z shortcuts from bubbli…
angelosilvestre Jun 1, 2024
8fbcbd6
[SuperEditor] Allow custom view models to be aware of node selection …
angelosilvestre Jun 4, 2024
d85ec49
[SuperEditor][Mobile] Fix caret position after rotation (Resolves #20…
angelosilvestre Jun 7, 2024
bfd6d5f
[SuperEditor] Prevent long-press to resolve when swiping to pop (Reso…
angelosilvestre Jun 7, 2024
6e1bc68
[SuperEditor][iOS] Don't show toolbar upon first tap (Resolves #2073)…
angelosilvestre Jun 9, 2024
5275fed
Release super_editor v0.0.3 dev.1 (#2082)
matthew-carroll Jun 11, 2024
add1c7b
Cherry Pick: [golden_tester] Remove docker image after running the co…
github-actions[bot] Jun 12, 2024
3fd24a9
[SuperEditor] Fix crash due to conflicting attributions in SelectedTe…
angelosilvestre Jun 12, 2024
174f14a
[SuperTextField] Re-generate failing caret test goldens (Resolves #20…
angelosilvestre Jun 12, 2024
8a55bc2
Fix crash in linkification reaction, also add linkification to inline…
matthew-carroll Jun 13, 2024
d0fead9
[SuperEditor][SuperTextField][iOS] - When user taps, place caret at w…
matthew-carroll Jun 17, 2024
c66c271
[SuperEditor] - Tasks are now indentable (Resolves #1895) (#2101)
matthew-carroll Jun 18, 2024
8c8004a
Fix: Can now convert paragraph to task and begin with task completed …
matthew-carroll Jun 20, 2024
fe47387
[SuperEditor][SuperReader] - Added font family attribution, superscri…
matthew-carroll Jun 20, 2024
5d0dbe9
[SuperEditor][Quill] - Create super_editor_quill package - parse Quil…
matthew-carroll Jun 21, 2024
75f3886
Release attributed_text v0.3.2
matthew-carroll Jun 22, 2024
60b657e
[SuperEditor] - Upgraded attributed_text dependency to 0.3.2
matthew-carroll Jun 22, 2024
0743988
[Quill] - Serialize MutableDocument to Quill Deltas document (Resolve…
matthew-carroll Jun 23, 2024
35fdfc1
[SuperEditor] - Ignore spurious metrics change notification (#2127)
knopp Jun 27, 2024
0522c5a
[Quill] - Create Quill editor clone called Feather (Resolves #2120) (…
matthew-carroll Jul 2, 2024
6f7868a
Expand touch area for Android selection handles (Resolves #2075) (#2079)
matthew-carroll Jul 2, 2024
ad63d43
[SuperTextLayout] Paint text selection with BorderRadius (#2131)
domesticmouse Jul 5, 2024
ea18da6
Cherry Pick: [SuperEditor][SuperTextField][iOS] Update selection hand…
github-actions[bot] Jul 8, 2024
60a7f50
[SuperEditor][Mobile] Fix crash on long press over non-text node (Res…
angelosilvestre Jul 8, 2024
0b3dc5c
[AttributedText] - Replace clear() with methods that explicitly state…
CillianMyles Jul 3, 2024
7d7ed20
Cherry Pick: Undo/Redo (with snapshots + one-way commands) (#2147)
github-actions[bot] Jul 9, 2024
2dc3742
Fix: switch super_editor_quill to local super_editor and add missing …
matthew-carroll Jul 9, 2024
7a29b29
Merge branch 'stable' of https://github.com/superlistapp/super_editor…
BazinC Jul 9, 2024
39323f9
Remove nodes list from Document to make Document immutable at API lev…
matthew-carroll Jul 10, 2024
493c4ee
Publish extension methods to access MutableDocument and MutableCompos…
matthew-carroll Jul 10, 2024
ea02805
Cherry Pick: Removed document and composer from SuperEditor widget pr…
matthew-carroll Jul 10, 2024
42b32f2
Merge branch 'stable' of https://github.com/superlistapp/super_editor…
BazinC Jul 16, 2024
69a8394
Merge branch 'stable_clickup' into july_16_2024_stable_updates
BazinC Jul 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 22 additions & 0 deletions .github/workflows/pr_validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,28 @@ jobs:
# Run all tests
- run: flutter test

test_super_editor_quill:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./super_editor_quill
steps:
# Checkout the PR branch
- uses: actions/checkout@v3

# Setup Flutter environment
- uses: subosito/flutter-action@v2
with:
channel: "master"

# Download all the packages that the app uses
- run: flutter pub get

# TODO: Enforce static analysis

# Run all tests
- run: flutter test

test_super_text_layout:
runs-on: ubuntu-latest
defaults:
Expand Down
8 changes: 8 additions & 0 deletions attributed_text/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## [0.3.2] - June, 2024
* Fix crash when adding attributions that overlap others - you can now control whether a new attribution overwrites conflicting spans when you add it.

## [0.3.1] - June, 2024
* Added query `getAllAttributionsThroughout()` to `AttributedText`.
* Added `copy()` to `AttributedText()`.
* Added ability to insert an attribution that splits an existing attribution.

## [0.3.0] - Feb, 2024
* [BREAKING] - `AttributedText` and `SpanRange` constructors now use positional parameters istead of named parameters.
* [FIX] - `AttributedText` now supports differents links for different URLs in the same text blob - previously all links were sent to the same URL withing a single `AttributedText`.
Expand Down
174 changes: 154 additions & 20 deletions attributed_text/lib/src/attributed_spans.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class AttributedSpans {
final matchingAttributions = <Attribution>{};
for (int i = start; i <= end; ++i) {
for (final attribution in attributions) {
final otherAttributions = getAllAttributionsAt(start);
final otherAttributions = getAllAttributionsAt(i);
for (final otherAttribution in otherAttributions) {
if (otherAttribution.id == attribution.id) {
matchingAttributions.add(otherAttribution);
Expand Down Expand Up @@ -280,18 +280,48 @@ class AttributedSpans {

/// Applies the [newAttribution] from [start] to [end], inclusive.
///
/// When [autoMerge] is `true`, the new attribution is merged with any
/// preceding or following attribution whose [Attribution.canMergeWith] returns
/// `true`.
/// If [start] is less than `0`, nothing happens.
///
/// It [newAttribution] overlaps a conflicting span, or if [newAttribution]
/// overlaps a merge-able span but [autoMerge] is `false`, a
/// [IncompatibleOverlappingAttributionsException] is thrown.
/// [AttributedSpans] doesn't have any knowledge about content length, so [end] can
/// take any value that's desired. However, users of [AttributedSpans] should take
/// care to avoid values for [end] that exceed the content length.
///
/// The effect of adding an attribution is straight forward when the text doesn't
/// contain any other attributions with the same ID. However, there are various
/// situations where [newAttribution] can't necessarily co-exist with other
/// attribution spans that already exist in the text.
///
/// Attribution overlaps can take one of two forms: mergeable or conflicting.
///
/// ## Mergeable Attribution Spans
/// An example of a mergeable overlap is where two bold spans overlap each
/// other. All bold attributions are interchangeable, so when two bold spans
/// overlap, those spans can be merged together into a single span.
///
/// However, mergeable overlapping spans are not automatically merged. Instead,
/// this decision is left to the user of this class. If you want [AttributedSpans] to
/// merge overlapping mergeable spans, pass `true` for [autoMerge]. Otherwise,
/// if [autoMerge] is `false`, an exception is thrown when two mergeable spans
/// overlap each other.
///
///
/// ## Conflicting Attribution Spans
/// An example of a conflicting overlap is where a black text color overlaps a red
/// text color. Text is either black, OR red, but never both. Therefore, the black
/// attribution cannot co-exist with the red attribution. Something must be done
/// to resolve this.
///
/// There are two possible ways to handle conflicting overlaps. The new attribution
/// can overwrite the existing attribution where they overlap. Or, an exception can be
/// thrown. To overwrite the existing attribution with the new attribution, pass `true`
/// for [overwriteConflictingSpans]. Otherwise, if [overwriteConflictingSpans]
/// is `false`, an exception is thrown.
void addAttribution({
required Attribution newAttribution,
required int start,
required int end,
bool autoMerge = true,
bool overwriteConflictingSpans = true,
}) {
if (start < 0 || start > end) {
_log.warning("Tried to add an attribution ($newAttribution) at an invalid start/end: $start -> $end");
Expand All @@ -301,32 +331,94 @@ class AttributedSpans {
_log.info("Adding attribution ($newAttribution) from $start to $end");
_log.finer("Has ${_markers.length} markers before addition");

// Ensure that no conflicting attribution overlaps the new attribution.
// If a conflict exists, throw an exception.
final conflicts = <_AttributionConflict>[];

// Check if conflicting attributions overlap the new attribution.
final matchingAttributions = getMatchingAttributionsWithin(attributions: {newAttribution}, start: start, end: end);
if (matchingAttributions.isNotEmpty) {
for (final matchingAttribution in matchingAttributions) {
if (!newAttribution.canMergeWith(matchingAttribution) || !autoMerge) {
late int conflictStart;
bool areAttributionsMergeable = newAttribution.canMergeWith(matchingAttribution);
if (!areAttributionsMergeable || !autoMerge) {
int? conflictStart;
int? conflictEnd;

for (int i = start; i <= end; ++i) {
if (hasAttributionAt(i, attribution: matchingAttribution)) {
conflictStart = i;
break;
conflictStart ??= i;
conflictEnd = i;

if (areAttributionsMergeable) {
// Both attributions are mergeable, but the caller doesn't want to merge them.
throw IncompatibleOverlappingAttributionsException(
existingAttribution: matchingAttribution,
newAttribution: newAttribution,
conflictStart: conflictStart,
);
}
} else if (conflictStart != null) {
// We found the end of the conflict.
conflicts.add(_AttributionConflict(
newAttribution: newAttribution,
existingAttribution: matchingAttribution,
conflictStart: conflictStart,
conflictEnd: conflictEnd!,
));

// Reset so we can find the next conflict.
conflictStart = null;
conflictEnd = null;
}
}

throw IncompatibleOverlappingAttributionsException(
existingAttribution: matchingAttribution,
newAttribution: newAttribution,
conflictStart: conflictStart,
);
if (conflictStart != null && conflictEnd != null) {
// We found a conflict that extends to the end of the range.
conflicts.add(_AttributionConflict(
newAttribution: newAttribution,
existingAttribution: matchingAttribution,
conflictStart: conflictStart,
conflictEnd: conflictEnd,
));
}
}
}

if (conflicts.isNotEmpty && !overwriteConflictingSpans) {
// We found conflicting attributions and we are configured not to overwrite them.
// For example, the user tried to apply a blue color attribution to a range of text
// that already has another color attribution.
throw IncompatibleOverlappingAttributionsException(
existingAttribution: conflicts.first.existingAttribution,
newAttribution: newAttribution,
conflictStart: conflicts.first.conflictStart,
);
}
}

// Removes any conflicting attributions. For example, consider the following text,
// with a blue color attribution that spans the entire text:
//
// one two three
// |bbbbbbbbbbbbb|
//
// We can't apply a green color attribution to the word "two", because it's already
// attributed with blue. So, we need to remove the blue attribution from the word "two",
// which results in the following text:
//
// one two three
// |bbbb---bbbbbb|
//
// After that, we can apply the desired attribution, because there isn't a conflicting attribution
// in this range anymore.
for (final conflict in conflicts) {
removeAttribution(
attributionToRemove: conflict.existingAttribution,
start: conflict.conflictStart,
end: conflict.conflictEnd,
);
}

if (!autoMerge) {
// There are not conflicting attributions in the desired range, and we don't
// want to merge this new attribution with any other nearby attribution.
// We don't want to merge this new attribution with any other nearby attribution.
// Therefore, we can blindly create the new attribution range without any
// further adjustments, and then be done.
_insertMarker(SpanMarker(
Expand Down Expand Up @@ -1197,3 +1289,45 @@ class IncompatibleOverlappingAttributionsException implements Exception {
return 'Tried to insert attribution ($newAttribution) over a conflicting existing attribution ($existingAttribution). The overlap began at index $conflictStart';
}
}

/// A conflict between the [newAttribution] and [existingAttribution] between [conflictStart] and [conflictEnd] (inclusive).
///
/// This means [newAttribution] and [existingAttribution] have the same id, but they can't be merged.
class _AttributionConflict {
_AttributionConflict({
required this.newAttribution,
required this.existingAttribution,
required this.conflictStart,
required this.conflictEnd,
});

/// The new attribution that conflicts with the existing attribution.
final Attribution newAttribution;

/// The conflicting attribution.
final Attribution existingAttribution;

/// The first conflicting index.
final int conflictStart;

/// The last conflicting index (inclusive).
final int conflictEnd;

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is _AttributionConflict &&
runtimeType == other.runtimeType &&
newAttribution == other.newAttribution &&
existingAttribution == other.existingAttribution &&
conflictStart == other.conflictStart &&
conflictEnd == other.conflictEnd;

@override
int get hashCode =>
newAttribution.hashCode ^ existingAttribution.hashCode ^ conflictStart.hashCode ^ conflictEnd.hashCode;

@override
String toString() =>
'[AttributionConflict] - newAttribution: $newAttribution existingAttribution: $existingAttribution, conflictStart: $conflictStart, conflictEnd: $conflictEnd';
}
42 changes: 38 additions & 4 deletions attributed_text/lib/src/attributed_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,49 @@ class AttributedText {
/// Adds the given [attribution] to all characters within the given
/// [range], inclusive.
///
/// When [autoMerge] is `true`, the new attribution is merged with any
/// preceding or following attribution whose [Attribution.canMergeWith] returns
/// `true`.
/// The effect of adding an attribution is straight forward when the text doesn't
/// contain any other attributions with the same ID. However, there are various
/// situations where the [attribution] can't necessarily co-exist with other
/// attribution spans that already exist in the text.
///
/// Attribution overlaps can take one of two forms: mergeable or conflicting.
///
/// ## Mergeable Attribution Spans
/// An example of a mergeable overlap is where two bold spans overlap each
/// other. All bold attributions are interchangeable, so when two bold spans
/// overlap, those spans can be merged together into a single span.
///
/// However, mergeable overlapping spans are not automatically merged. Instead,
/// this decision is left to the user of this class. If you want [AttributedText] to
/// merge overlapping mergeable spans, pass `true` for [autoMerge]. Otherwise,
/// if [autoMerge] is `false`, an exception is thrown when two mergeable spans
/// overlap each other.
///
///
/// ## Conflicting Attribution Spans
/// An example of a conflicting overlap is where a black text color overlaps a red
/// text color. Text is either black, OR red, but never both. Therefore, the black
/// attribution cannot co-exist with the red attribution. Something must be done
/// to resolve this.
///
/// There are two possible ways to handle conflicting overlaps. The new attribution
/// can overwrite the existing attribution where they overlap. Or, an exception can be
/// thrown. To overwrite the existing attribution with the new attribution, pass `true`
/// for [overwriteConflictingSpans]. Otherwise, if [overwriteConflictingSpans]
/// is `false`, an exception is thrown.
void addAttribution(
Attribution attribution,
SpanRange range, {
bool autoMerge = true,
bool overwriteConflictingSpans = false,
}) {
spans.addAttribution(newAttribution: attribution, start: range.start, end: range.end, autoMerge: autoMerge);
spans.addAttribution(
newAttribution: attribution,
start: range.start,
end: range.end,
autoMerge: autoMerge,
overwriteConflictingSpans: overwriteConflictingSpans,
);
_notifyListeners();
}

Expand Down
4 changes: 2 additions & 2 deletions attributed_text/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: attributed_text
description: Text with metadata spans for easy text editing and styling.
version: 0.3.0
version: 0.3.2
homepage: https://github.com/superlistapp/super_editor

environment:
sdk: ">=2.12.0 <4.0.0"
sdk: ">=3.0.0 <4.0.0"

dependencies:
characters: ^1.2.0
Expand Down
Loading
Loading