Skip to content

Instantly share code, notes, and snippets.

@mraleph
Created July 8, 2020 20:44
Show Gist options
  • Save mraleph/bbd8a6edf173d0050d56d34459043cff to your computer and use it in GitHub Desktop.
Save mraleph/bbd8a6edf173d0050d56d34459043cff to your computer and use it in GitHub Desktop.
diff --git a/lib/recase.dart b/lib/recase.dart
index da14cec..42a1251 100644
--- a/lib/recase.dart
+++ b/lib/recase.dart
@@ -1,44 +1,94 @@
+import 'dart:typed_data';
+
/// An instance of text to be re-cased.
class ReCase {
- final RegExp _upperAlphaRegex = new RegExp(r'[A-Z]');
- final RegExp _symbolRegex = new RegExp(r'[ ./_\-]');
-
- String originalText;
- List<String> _words;
-
- ReCase(String text) {
- this.originalText = text;
- this._words = _groupIntoWords(text);
+ static const isLowerCase = 1;
+ static const isUpperCase = 2;
+ static const isSymbol = 3;
+ static final Uint8List mapping = (() {
+ final result = Uint8List(256);
+ for (var i = 'a'.codeUnitAt(0); i <= 'z'.codeUnitAt(0); i++) {
+ result[i] = isLowerCase;
+ }
+ for (var i = 'A'.codeUnitAt(0); i <= 'Z'.codeUnitAt(0); i++) {
+ result[i] = isUpperCase;
+ }
+ for (var i in ' ./_\-'.split('').map((e) => e.codeUnitAt(0))) {
+ result[i] = isSymbol;
+ }
+ return result;
+ })();
+
+ final String originalText;
+ final Uint16List codeUnits;
+ final List<int> _wordBoundaries;
+ final int _totalLength;
+ List<String> get _words {
+ List<String> words = List(_wordBoundaries.length >> 1);
+ for (var i = 0; i < _wordBoundaries.length; i += 2) {
+ words[i >> 1] =
+ (originalText.substring(_wordBoundaries[i], _wordBoundaries[i + 1]));
+ }
+ return words;
}
- List<String> _groupIntoWords(String text) {
- StringBuffer sb = new StringBuffer();
- List<String> words = [];
- bool isAllCaps = !text.contains(RegExp('[a-z]'));
+ ReCase._(this.originalText, this.codeUnits, this._wordBoundaries, this._totalLength);
+ factory ReCase(String text) {
+ final Uint16List codeUnits = Uint16List(text.length);
for (int i = 0; i < text.length; i++) {
- String char = new String.fromCharCode(text.codeUnitAt(i));
- String nextChar = (i + 1 == text.length
- ? null
- : new String.fromCharCode(text.codeUnitAt(i + 1)));
-
- if (_symbolRegex.hasMatch(char)) {
- continue;
- }
+ codeUnits[i] = text.codeUnitAt(i);
+ }
- sb.write(char);
+ List<int> words = [];
+ int total = 0;
- bool isEndOfWord = nextChar == null ||
- (_upperAlphaRegex.hasMatch(nextChar) && !isAllCaps) ||
- _symbolRegex.hasMatch(nextChar);
+ bool isAllCaps = true;
+ for (int i = 0; i < codeUnits.length; i++) {
+ final ch = codeUnits[i];
+ if (ch < 256 && mapping[ch] == isLowerCase) {
+ isAllCaps = false;
+ break;
+ }
+ }
- if (isEndOfWord) {
- words.add(sb.toString());
- sb.clear();
+ int wordStart = -1;
+ for (int i = 0; i < codeUnits.length; i++) {
+ final char = codeUnits[i];
+ if (char < 256) {
+ final type = mapping[char];
+ if (type == isSymbol) {
+ if (wordStart != -1) {
+ // We have a word, flush it out.
+ words.add(wordStart);
+ words.add(i);
+ total += (i - wordStart);
+ wordStart = -1;
+ }
+ continue;
+ } else if (type == isUpperCase && !isAllCaps) {
+ if (wordStart != -1) {
+ // We have a word, flush it out.
+ words.add(wordStart);
+ words.add(i);
+ total += (i - wordStart);
+
+ }
+ wordStart = i; // New word begins here.
+ continue;
+ }
+ }
+ if (wordStart == -1) {
+ wordStart = i;
}
}
+ if (wordStart != -1) {
+ words.add(wordStart);
+ words.add(codeUnits.length);
+ total += (codeUnits.length - wordStart);
+ }
- return words;
+ return ReCase._(text, codeUnits, words, total);
}
/// camelCase
@@ -85,9 +135,43 @@ class ReCase {
}
String _getPascalCase({String separator: ''}) {
- List<String> words = this._words.map(_upperCaseFirstLetter).toList();
-
- return words.join(separator);
+ List<String> words =
+ List(_wordBoundaries.length + (_wordBoundaries.length >> 1) - 1);
+ if (separator.length == 1) {
+ final separatorChar = separator.codeUnitAt(0);
+ final result = Uint16List(_totalLength + (_wordBoundaries.length >> 1) - 1);
+ var out = 0;
+ for (var i = 0; i < _wordBoundaries.length; i += 2) {
+ if (i != 0) result[out++] = separatorChar;
+
+ final start = _wordBoundaries[i];
+ final end = _wordBoundaries[i+1];
+
+ int firstChar = codeUnits[start];
+ if (firstChar < 256 && mapping[firstChar] == isLowerCase) {
+ firstChar = firstChar ^ 32;
+ }
+ result[out++] = firstChar;
+
+ for (var j = start + 1; j < end; j++) {
+ int char = codeUnits[j];
+ if (char < 256 && mapping[char] == isUpperCase) {
+ char = char ^ 32;
+ }
+ result[out++] = char;
+ }
+ }
+ return String.fromCharCodes(result);
+ } else {
+ for (var i = 0, j = 0; i < _wordBoundaries.length; i += 2, j += 3) {
+ if (i != 0) words[j - 1] = separator;
+ words[j] = originalText[_wordBoundaries[i]].toUpperCase();
+ words[j + 1] = originalText
+ .substring(_wordBoundaries[i] + 1, _wordBoundaries[i + 1])
+ .toLowerCase();
+ }
+ }
+ return words.join('');
}
String _getSentenceCase({String separator: ' '}) {
@@ -109,7 +193,6 @@ class ReCase {
}
extension StringReCase on String {
-
String get camelCase => ReCase(this).camelCase;
String get constantCase => ReCase(this).constantCase;
diff --git a/pubspec.lock b/pubspec.lock
index 770e733..22625fd 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -7,49 +7,49 @@ packages:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
- version: "1.0.1"
+ version: "5.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
- version: "0.39.2+1"
+ version: "0.39.12"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
- version: "1.5.2"
+ version: "1.6.0"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
- version: "2.4.0"
+ version: "2.4.2"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
- version: "1.0.5"
+ version: "2.0.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.2"
+ version: "1.1.3"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
- version: "1.14.12"
+ version: "1.14.13"
convert:
dependency: transitive
description:
@@ -63,14 +63,14 @@ packages:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
- version: "0.13.3+3"
+ version: "0.14.0"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
- version: "2.1.4"
+ version: "2.1.5"
csslib:
dependency: transitive
description:
@@ -98,56 +98,56 @@ packages:
name: http
url: "https://pub.dartlang.org"
source: hosted
- version: "0.12.0+3"
+ version: "0.12.1"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
source: hosted
- version: "2.1.0"
+ version: "2.2.0"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
- version: "3.1.3"
+ version: "3.1.4"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
source: hosted
- version: "0.3.3"
+ version: "0.3.4"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
- version: "0.6.1+1"
+ version: "0.6.2"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
- version: "0.11.3+2"
+ version: "0.11.4"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
- version: "0.12.6"
+ version: "0.12.8"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.8"
+ version: "1.2.1"
mime:
dependency: transitive
description:
@@ -155,62 +155,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.6+3"
- multi_server_socket:
- dependency: transitive
- description:
- name: multi_server_socket
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.0.2"
node_interop:
dependency: transitive
description:
name: node_interop
url: "https://pub.dartlang.org"
source: hosted
- version: "1.0.3"
+ version: "1.1.1"
node_io:
dependency: transitive
description:
name: node_io
url: "https://pub.dartlang.org"
source: hosted
- version: "1.0.1+2"
+ version: "1.1.1"
node_preamble:
dependency: transitive
description:
name: node_preamble
url: "https://pub.dartlang.org"
source: hosted
- version: "1.4.8"
+ version: "1.4.12"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.0"
- package_resolver:
- dependency: transitive
- description:
- name: package_resolver
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.0.10"
+ version: "1.9.3"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
- version: "1.6.4"
+ version: "1.7.0"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
- version: "1.9.0"
+ version: "1.9.1"
pool:
dependency: transitive
description:
@@ -224,21 +210,21 @@ packages:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
- version: "1.4.2"
+ version: "1.4.4"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
- version: "0.7.5"
+ version: "0.7.7"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
url: "https://pub.dartlang.org"
source: hosted
- version: "1.0.4"
+ version: "2.0.0"
shelf_static:
dependency: transitive
description:
@@ -259,28 +245,28 @@ packages:
name: source_map_stack_trace
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.5"
+ version: "2.0.0"
source_maps:
dependency: transitive
description:
name: source_maps
url: "https://pub.dartlang.org"
source: hosted
- version: "0.10.8"
+ version: "0.10.9"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
- version: "1.5.5"
+ version: "1.7.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
- version: "1.9.3"
+ version: "1.9.5"
stream_channel:
dependency: transitive
description:
@@ -308,42 +294,42 @@ packages:
name: test
url: "https://pub.dartlang.org"
source: hosted
- version: "1.9.4"
+ version: "1.15.2"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
- version: "0.2.11"
+ version: "0.2.17"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
- version: "0.2.15"
+ version: "0.3.10"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.6"
+ version: "1.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
url: "https://pub.dartlang.org"
source: hosted
- version: "2.2.1"
+ version: "4.1.0"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
- version: "0.9.7+13"
+ version: "0.9.7+15"
web_socket_channel:
dependency: transitive
description:
@@ -351,12 +337,19 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
+ webkit_inspection_protocol:
+ dependency: transitive
+ description:
+ name: webkit_inspection_protocol
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.7.3"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
- version: "2.2.0"
+ version: "2.2.1"
sdks:
- dart: ">=2.6.0 <3.0.0"
+ dart: ">=2.7.0 <3.0.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 0cb2c76..d9b1b1f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -5,4 +5,4 @@ homepage: https://github.com/techniboogie-dart/recase
environment:
sdk: '>=2.6.0 <3.0.0'
dev_dependencies:
- test: '^1.0.0'
+ test: any
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment