Created
July 8, 2020 20:44
-
-
Save mraleph/bbd8a6edf173d0050d56d34459043cff to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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