Skip to content

Commit

Permalink
Change replace to keep its position in the new result. Add a few memb…
Browse files Browse the repository at this point in the history
…ers. (dart-archive/characters#8)

Add move-operations to character range for selecting everything before or after the current range.

Add getters for the characters or strings before and after the current range (and characters of the current range, to complement `current` which returns a string).

Change replace operations to return a range on the replaced result which contains the original range post-modification.
  • Loading branch information
lrhn authored Oct 22, 2019
1 parent 9545eae commit 265a6f1
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 54 deletions.
2 changes: 1 addition & 1 deletion pkgs/characters/.travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: dart
dart:
- dev
- 2.4.0
- 2.5.0
# Only building master means that we don't run two builds for each pull request.
dart_task:
- test: --platform vm,chrome
Expand Down
97 changes: 74 additions & 23 deletions pkgs/characters/lib/src/characters.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,20 @@ abstract class Characters implements Iterable<String> {

/// Replaces [pattern] with [replacement].
///
/// Returns a new [GrapehemeClusters] where all occurrences of the
/// [pattern] character sequence are replaced by [replacement],
/// unless the occurrence overlaps a prior replaced sequence.
/// Returns a new [Characters] sequence where all occurrences of the
/// [pattern] characters are replaced by [replacement],
/// unless the occurrence overlaps a prior
/// replaced occurrence of [pattern].
///
/// Returns the current characters if there is no occurrence of [pattern].
Characters replaceAll(Characters pattern, Characters replacement);

/// Replaces the first [pattern] with [replacement].
/// Replaces the first occurrence of [pattern] with [replacement].
///
/// Returns a new [Characters] where the first occurence of the
/// [pattern] character sequence, if any, is replaced by [replacement].
///
/// Returns the current characters if there is no occurrence of [pattern].
Characters replaceFirst(Characters pattern, Characters replacement);

/// The characters of the lower-case version of [string].
Expand Down Expand Up @@ -250,7 +255,22 @@ abstract class CharacterRange implements Iterator<String> {
/// The code points of the current character range.
Runes get runes;

/// Creates a copy of this [Character].
/// The characters of this range.
Characters get currentCharacters;

/// The characters before the current range.
Characters get charactersBefore;

/// The characters after the current range.
Characters get charactersAfter;

/// The string of the characters before the current range.
String get stringBefore;

/// The string of the characters after the current range.
String get stringAfter;

/// Creates a copy of this [CharacterRange].
///
/// The copy is in the exact same state as this iterator.
/// Can be used to iterate the following characters more than once
Expand Down Expand Up @@ -283,6 +303,9 @@ abstract class CharacterRange implements Iterator<String> {
/// and `false` if not.
bool moveNext([int count = 1]);

/// Moves the range to be everything after the current range.
void moveNextAll();

/// Moves the range to the next occurrence of [target]
/// after the current range.
///
Expand Down Expand Up @@ -325,6 +348,9 @@ abstract class CharacterRange implements Iterator<String> {
/// and `false` if not.
bool moveBack([int count = 1]);

/// Moves the range to be everything before the current range.
void moveBackAll();

/// Moves the range to the last occurrence of [target]
/// before the current range.
///
Expand Down Expand Up @@ -587,33 +613,58 @@ abstract class CharacterRange implements Iterator<String> {
/// which gives the same effect as [collapseToStart].
void dropBackWhile(bool Function(String) test);

/// Creates a new [Characters] sequence by replacing the current range.
/// Replaces the current range with [replacement] and returns the result.
///
/// Replaces the current range in [source] with [replacement].
/// Replaces the current range in [source] with [replacement]
/// and returns a range of the resulting characters
/// which contains the replacement characters.
///
/// Returns a new [Characters] instance. Since the inserted characters
/// may combine with the preceding or following characters, grapheme cluster
/// boundaries need to be recomputed from scratch.
Characters replaceRange(Characters replacement);
/// The inserted characters may combine with
/// the preceding or following code points,
/// so that the start and end of the original range
/// are no longer grapheme cluster boundaries.
/// In that case, the returned range may extend into into the code points
/// before and after the original range.
CharacterRange replaceRange(Characters replacement);

/// Replaces all occurrences of [pattern] in the range with [replacement].
/// Replaces [pattern] in the current range with [replacement].
///
/// Replaces the first occurrence of [pattern] in the range, then repeatedly
/// finds and replaces the next occurrence which does not overlap with
/// the earlier, already replaced, occurrence.
/// Replaces all occurrences of [pattern] in the current range with
/// [replacement], unless they overlap with an earlier occurrence of
/// [pattern] which was replaced.
/// Then returns a range on the resulting characters
/// which contains all inserted replacement characters
/// and any remaining characters of the original range.
///
/// Returns new [Characters] instance for the resulting string.
Characters replaceAll(Characters pattern, Characters replacement);
/// The inserted characters may combine with
/// the preceding or following code points,
/// so that the start and end of the original range
/// are no longer grapheme cluster boundaries.
/// In that case, the returned range may extend into into the code points
/// before and after the original range.
///
/// Returns `null` if there are no occurrences of [pattern]
/// in the current range.
CharacterRange /*?*/ replaceAll(Characters pattern, Characters replacement);

/// Replaces the first occurrence of [pattern] with [replacement].
///
/// Finds the first occurrence of [pattern] in the current range,
/// then replaces that occurrence with [replacement] and returns
/// the [Characters] of that string.
///
/// If there is no first occurrence of [pattern], then the
/// characters of the source string is returned.
Characters replaceFirst(Characters pattern, Characters replacement);
/// then replaces that occurrence with [replacement].
/// Then returns a range on the resulting characters
/// which contains the inserted replacement characters
/// and any remaining characters of the original range.
///
/// The inserted characters may combine with
/// the preceding or following code points,
/// so that the start and end of the original range
/// are no longer grapheme cluster boundaries.
/// In that case, the returned range may extend into into the code points
/// before and after the original range.
///
/// Returns `null` if there are no occurrences of [pattern]
/// in the current range.
CharacterRange /*?*/ replaceFirst(Characters pattern, Characters replacement);

/// Whether the current range starts with [characters].
///
Expand Down
86 changes: 63 additions & 23 deletions pkgs/characters/lib/src/characters_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,11 @@ class StringCharacters extends Iterable<String> implements Characters {

@override
Characters replaceAll(Characters pattern, Characters replacement) =>
_rangeAll.replaceAll(pattern, replacement);
_rangeAll.replaceAll(pattern, replacement)?.source ?? this;

@override
Characters replaceFirst(Characters pattern, Characters replacement) {
var range = _rangeAll;
if (!range.collapseToFirst(pattern)) return this;
return range.replaceRange(replacement);
}
Characters replaceFirst(Characters pattern, Characters replacement) =>
_rangeAll.replaceFirst(pattern, replacement)?.source ?? this;

@override
bool containsAll(Characters other) =>
Expand Down Expand Up @@ -750,32 +747,37 @@ class StringCharacterRange implements CharacterRange {
}

@override
Characters replaceFirst(Characters pattern, Characters replacement) {
CharacterRange /*?*/ replaceFirst(
Characters pattern, Characters replacement) {
String patternString = pattern.string;
String replacementString = replacement.string;
String replaced;
if (patternString.isEmpty) {
return StringCharacters(
_string.replaceRange(_start, _start, replacementString));
}
int index = _indexOf(_string, patternString, _start, _end);
String result = _string;
if (index >= 0) {
result = _string.replaceRange(
index, index + patternString.length, replacementString);
replaced = _string.replaceRange(_start, _start, replacementString);
} else {
int index = _indexOf(_string, patternString, _start, _end);
if (index >= 0) {
replaced = _string.replaceRange(
index, index + patternString.length, replacementString);
} else {
return null;
}
}
return StringCharacters(result);
int newEnd = replaced.length - _string.length + _end;
return _expandRange(replaced, _start, newEnd);
}

@override
Characters replaceAll(Characters pattern, Characters replacement) {
CharacterRange /*?*/ replaceAll(Characters pattern, Characters replacement) {
var patternString = pattern.string;
var replacementString = replacement.string;
if (patternString.isEmpty) {
var replaced = _explodeReplace(
_string, _start, _end, replacementString, replacementString);
return StringCharacters(replaced);
int newEnd = replaced.length - (_string.length - _end);
return _expandRange(replaced, _start, newEnd);
}
if (_start == _end) return Characters(_string);
if (_start == _end) return null;
int start = 0;
int cursor = _start;
StringBuffer buffer;
Expand All @@ -786,14 +788,26 @@ class StringCharacterRange implements CharacterRange {
cursor += patternString.length;
start = cursor;
}
if (buffer == null) return Characters(_string);
if (buffer == null) return null;
buffer.write(_string.substring(start));
return Characters(buffer.toString());
String replaced = buffer.toString();
int newEnd = replaced.length - (_string.length - _end);
return _expandRange(replaced, _start, newEnd);
}

@override
Characters replaceRange(Characters replacement) {
return Characters(_string.replaceRange(_start, _end, replacement.string));
CharacterRange replaceRange(Characters replacement) {
String replacementString = replacement.string;
String resultString = _string.replaceRange(_start, _end, replacementString);
return _expandRange(
resultString, _start, _start + replacementString.length);
}

// Expands a range if its start or end are not grapheme cluster boundaries.
StringCharacterRange _expandRange(String string, int start, int end) {
start = previousBreak(string, 0, string.length, start);
end = nextBreak(string, 0, string.length, end);
return StringCharacterRange._(string, start, end);
}

@override
Expand Down Expand Up @@ -856,6 +870,32 @@ class StringCharacterRange implements CharacterRange {
}
return false;
}

@override
Characters get charactersAfter => StringCharacters(_string.substring(_end));

@override
Characters get charactersBefore =>
StringCharacters(_string.substring(0, _start));

@override
Characters get currentCharacters => StringCharacters(current);

@override
void moveBackAll() {
_move(0, _start);
}

@override
void moveNextAll() {
_move(_end, _string.length);
}

@override
String get stringAfter => _string.substring(_end);

@override
String get stringBefore => _string.substring(0, _start);
}

class _CodeUnits extends ListBase<int> {
Expand Down
4 changes: 2 additions & 2 deletions pkgs/characters/lib/src/grapheme_clusters/breaks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ bool isGraphemeClusterBoundary(String text, int start, int end, int index) {
return start != end;
}

/// The most recent break no later than [position] in
/// The most recent break no later than [index] in
/// `string.substring(start, end)`.
int previousBreak(String text, int start, int end, int index) {
assert(0 <= start);
Expand Down Expand Up @@ -328,7 +328,7 @@ int previousBreak(String text, int start, int end, int index) {
.nextBreak();
}

/// The next break no earlier than [position] in `string.substring(start, end)`.
/// The next break no earlier than [index] in `string.substring(start, end)`.
///
/// The index need not be at a grapheme cluster boundary.
int nextBreak(String text, int start, int end, int index) {
Expand Down
4 changes: 2 additions & 2 deletions pkgs/characters/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: characters
version: 0.2.0
version: 0.3.0
environment:
sdk: "^2.4.0"
sdk: "^2.5.0"
dev_dependencies:
test: "^1.6.0"
Loading

0 comments on commit 265a6f1

Please sign in to comment.