Skip to content

Commit f7659fd

Browse files
committed
docs: revamp README and add CONTRIBUTING.md (v1.2.1)
- Completely revamped README with cleaner structure and API tables - Added CONTRIBUTING.md for contributor guidelines - Removed codemagic badge - Code formatting improvements 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent d08f3a6 commit f7659fd

11 files changed

Lines changed: 189 additions & 86 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## [1.2.1] - 2025
2+
3+
### Documentation
4+
* Revamped README with cleaner structure and API tables
5+
* Added CONTRIBUTING.md for contributors
6+
* Removed codemagic badge
7+
8+
### Code Quality
9+
* Code formatting improvements across source files
10+
111
## [1.2.0] - 2025
212

313
### New Features

CONTRIBUTING.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Contributing to stringr
2+
3+
Thank you for your interest in contributing to stringr! This document provides guidelines for contributing.
4+
5+
## Getting Started
6+
7+
1. Fork the repository
8+
2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/stringr.git`
9+
3. Install dependencies: `dart pub get`
10+
4. Run tests: `dart test`
11+
12+
## Development Workflow
13+
14+
### Making Changes
15+
16+
1. Create a feature branch: `git checkout -b feature/your-feature`
17+
2. Make your changes
18+
3. Run tests: `dart test`
19+
4. Run analysis: `dart analyze`
20+
5. Commit your changes with a descriptive message
21+
22+
### Code Style
23+
24+
- Follow the [Dart style guide](https://dart.dev/guides/language/effective-dart/style)
25+
- Run `dart format .` before committing
26+
- Ensure `dart analyze` passes with no issues
27+
28+
### Testing
29+
30+
- Add tests for new features
31+
- Ensure all existing tests pass
32+
- Test with both Latin and non-Latin strings when applicable
33+
34+
### Commit Messages
35+
36+
Use clear, descriptive commit messages:
37+
- `feat: add new method for X`
38+
- `fix: handle edge case in Y`
39+
- `docs: update README examples`
40+
- `refactor: improve performance of Z`
41+
42+
## Pull Request Process
43+
44+
1. Ensure all tests pass
45+
2. Update documentation if needed
46+
3. Update CHANGELOG.md with your changes
47+
4. Submit a pull request with a clear description
48+
49+
## Reporting Issues
50+
51+
When reporting issues, please include:
52+
- Dart/Flutter version
53+
- Minimal code to reproduce the issue
54+
- Expected vs actual behavior
55+
56+
## Questions?
57+
58+
Feel free to open an issue for questions or discussions.

README.md

Lines changed: 99 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,122 @@
1-
# stringr 🧵
1+
# stringr
22

3-
String manipulation in dart/flutter on steroids! ⚡⚡
4-
This dart plugin is null safety compliant 😎😎
5-
6-
![codecov](https://codecov.io/gh/Chinmay-KB/stringr/branch/master/graph/badge.svg?token=UIWY4OF2TE)
7-
![codemagic](https://api.codemagic.io/apps/5fecda726b96ea001cebef4a/flutter-package/status_badge.svg)
3+
[![pub package](https://img.shields.io/pub/v/stringr.svg)](https://pub.dev/packages/stringr)
4+
[![codecov](https://codecov.io/gh/Chinmay-KB/stringr/branch/master/graph/badge.svg?token=UIWY4OF2TE)](https://codecov.io/gh/Chinmay-KB/stringr)
85
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
96

7+
A comprehensive string manipulation library for Dart and Flutter. Inspired by [VocaJS](https://vocajs.com/), stringr provides powerful string operations that work seamlessly with Latin, non-Latin, and Unicode strings.
108

11-
This dart plugin is inspired 🧠 by [Voca js](https://vocajs.com/#) library. This plugin includes all the nifty functions required for powerful string manipulations.
9+
## Features
1210

13-
How powerful? Glad you asked!!
11+
- **Case Conversion** - camelCase, snake_case, kebab-case, Title Case, PascalCase
12+
- **Unicode Support** - Full support for non-Latin scripts, diacritics, and grapheme clusters
13+
- **Smart Word Detection** - Uses regex patterns instead of simple whitespace splitting
14+
- **Null Safety** - Fully null-safe and compatible with Dart 3.0+
15+
- **Zero Dependencies** - Only depends on the `characters` package for grapheme support
1416

15-
This plugin works on both latin and non latin strings, and uses complex regular expressions to extract words from strings on manipulations, rather than just calling `split(" ")` on a string.
16-
```dart
17+
## Installation
1718

18-
import 'package:stringr/stringr.dart';
19+
Add to your `pubspec.yaml`:
1920

20-
void main(){
21-
print("camel case".camelCase()); // Output - "camelCase"
22-
print("HTMLview".camelCase(preserveAcronym=true)); // Output - "HTMLView"
23-
print("/this/is/a/directory".camelCase()); // Output - "thisIsADirectory"
24-
print("полет птицы".kebabCase()); // Output - "полет_птицы"
25-
print("La variété la plus fréquente est la blanche".prune(12)); // Output - La variété
26-
print("2infinity*@#@AND93beyond".words()); // Output - ["2", "infinity", "AND", "93", "beyond"]
27-
print("gravity".words(pattern: r"\w{1,2}")); // Output - ['gr', 'av', 'it', 'y']
28-
}
29-
21+
```yaml
22+
dependencies:
23+
stringr: ^1.2.0
3024
```
31-
Convert a string to its latin counterpart! Can reverse not only pure strings, but strings with grapheme clusters as well😎
3225
33-
```dart
34-
35-
import 'package:stringr/stringr.dart';
36-
37-
void main(){
38-
print("cafe\u0301".latinize()); // Output - cafe
39-
print("anglikonų šiurkščios užrašinėti".latinize());// Output - 'anglikonu siurkscios uzrasineti'
40-
print("𝌆 bar mañana mañana".reverse()); // Output - 'anañam anañam rab 𝌆'
41-
// Splitting and reversing normally would give this as output
42-
// anãnam anañam rab ��
43-
}
44-
45-
```
46-
Support for graphemes is also included in this plugin. [characters](https://pub.dev/packages/characters) plugin is used to facilitate some of the functionalities.
26+
## Quick Start
4727
4828
```dart
49-
5029
import 'package:stringr/stringr.dart';
5130

52-
void main(){
53-
print("cafe\u0301".countGrapheme()); // Output - 4
54-
print("\uD835\uDC00\uD835\uDC01".countGrapheme()); // Output - 2
55-
print("\uD835\uDC00\uD835\uDC01".graphemeAt(1)); // Output - "\uD835\uDC01"
56-
print("stellar bomb".codePoints()); // Output - [0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x20, 0x62, 0x6f, 0x6d, 0x62]
57-
print('man\u0303ana'.graphemes()); // Output - ['m','a', 'n\u0303', 'a', 'n', 'a']
31+
void main() {
32+
// Case conversions
33+
print('hello world'.toCamelCase); // helloWorld
34+
print('hello world'.toSnakeCase); // hello_world
35+
print('hello world'.toKebabCase); // hello-world
36+
print('hello world'.toPascalCase); // HelloWorld
37+
38+
// Smart word extraction
39+
print('XMLHttpRequest'.camelCase()); // xmlHttpRequest
40+
print('/path/to/file'.camelCase()); // pathToFile
41+
print('2infinity&beyond'.words()); // [2, infinity, beyond]
5842
}
59-
6043
```
61-
HTML strings can be escaped!
6244

63-
```dart
45+
## Unicode & Non-Latin Support
6446

65-
import 'package:stringr/stringr.dart';
47+
stringr shines with complex Unicode strings:
6648

67-
void main(){
68-
print('<>&"\'`'.escapeHTML()); // Output - '&lt;&gt;&amp;&quot;&#x27;&#x60;'
69-
print('<p>wonderful world</p>'.escapeHTML());// Output - '&lt;p&gt;wonderful world&lt;/p&gt;'
70-
}
71-
72-
```
73-
You can count on stringr to count the words, latin, non latin, or strings with grapheme clusters!
7449
```dart
50+
// Non-Latin scripts
51+
print('полет птицы'.kebabCase()); // полет-птицы
52+
print('La variété française'.prune(12)); // La variété
7553
76-
import 'package:stringr/stringr.dart';
54+
// Diacritics and latinization
55+
print('café'.latinize()); // cafe
56+
print('anglikonų šiurkščios'.latinize()); // anglikonu siurkscios
7757
78-
void main(){
79-
print("a year without rain".count()); // Output - 19
80-
print("foo\uD834\uDF06\u0303\u035C\u035D\u035Ebar".countGrapheme());// Output - 7
81-
print("to be or not to be".countOccurences("to")); // Output - 2
82-
print("aBCdeFgH".countWhere((character) => character.isAlphabet())); // Output - 8
83-
print("NewYork".countWords()); // Output - 2
84-
}
85-
58+
// Grapheme-aware operations
59+
print('café'.countGrapheme()); // 4 (not 5!)
60+
print('👨‍👩‍👧‍👦'.reverse()); // 👨‍👩‍👧‍👦 (family emoji stays intact)
8661
```
87-
* Some of the functionalities, like `char()` and `codePoints` are either simple enough to implement or were available in the characters package. But still I decided to add them to make bundle everything under a similar syntax ( i.e- calling the extension functions on string).
88-
* For functions like `padLeft()` and `trim()` which already are extension functions and inbuilt in dart, I have not included them in this plugin as the syntax would be similar to that of this plugin.
62+
63+
## API Overview
64+
65+
### Case Manipulation
66+
| Method | Description | Example |
67+
|--------|-------------|---------|
68+
| `camelCase()` | Convert to camelCase | `'foo bar'``'fooBar'` |
69+
| `snakeCase()` | Convert to snake_case | `'foo bar'``'foo_bar'` |
70+
| `kebabCase()` | Convert to kebab-case | `'foo bar'``'foo-bar'` |
71+
| `titleCase()` | Capitalize each word | `'foo bar'``'Foo Bar'` |
72+
| `capitalize()` | Capitalize first char | `'foo'``'Foo'` |
73+
| `swapCase()` | Toggle case | `'FoO'``'fOo'` |
74+
75+
### String Chopping
76+
| Method | Description | Example |
77+
|--------|-------------|---------|
78+
| `truncate(n)` | Truncate to n chars | `'hello world'``'hello...'` |
79+
| `prune(n)` | Truncate without breaking words | `'hello world'``'hello'` |
80+
| `first(n)` | Get first n chars | `'hello'``'hel'` |
81+
| `last(n)` | Get last n chars | `'hello'``'llo'` |
82+
| `slice(start, end)` | Extract substring | `'hello'``'ell'` |
83+
84+
### Counting
85+
| Method | Description | Example |
86+
|--------|-------------|---------|
87+
| `countGrapheme()` | Count visible characters | `'café'``4` |
88+
| `countOccurrences(s)` | Count substring occurrences | `'banana'.countOccurrences('a')``3` |
89+
| `countWords()` | Count words | `'hello world'``2` |
90+
| `countWhere(fn)` | Count matching chars | Custom predicate |
91+
92+
### Manipulation
93+
| Method | Description | Example |
94+
|--------|-------------|---------|
95+
| `reverse()` | Reverse (grapheme-aware) | `'hello'``'olleh'` |
96+
| `slugify()` | URL-friendly slug | `'Hello World!'``'hello-world'` |
97+
| `latinize()` | Remove diacritics | `'café'``'cafe'` |
98+
| `insert(s, i)` | Insert at position | `'hllo'.insert('e', 1)``'hello'` |
99+
100+
### Escaping
101+
| Method | Description |
102+
|--------|-------------|
103+
| `escapeHtml()` | Escape HTML entities |
104+
| `unescapeHtml()` | Unescape HTML entities |
105+
| `escapeRegExp()` | Escape regex special chars |
106+
107+
## Why stringr?
108+
109+
Most string libraries fail with complex Unicode. stringr handles:
110+
111+
- **Grapheme clusters**: `'café'` is 4 characters, not 5
112+
- **Surrogate pairs**: Emoji and special symbols work correctly
113+
- **Non-Latin scripts**: Cyrillic, Greek, accented Latin, and more
114+
- **Smart word detection**: `'XMLHttpRequest'``['XML', 'Http', 'Request']`
115+
116+
## Contributing
117+
118+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
119+
120+
## License
121+
122+
MIT License - see [LICENSE](LICENSE) for details.

lib/src/case/case.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ extension Case on String {
3333
///
3434
/// If [decapitalizeAll] is set to `true`, the whole string is converted to
3535
/// lowercase. It is set to `false` by default
36-
String decapitalize({bool decapitalizeAll = false}) => switch (decapitalizeAll) {
36+
String decapitalize({bool decapitalizeAll = false}) =>
37+
switch (decapitalizeAll) {
3738
true => toLowerCase(),
3839
false => isEmpty ? '' : this[0].toLowerCase() + substring(1),
3940
};

lib/src/chop/chop.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,5 @@ extension Chop on String {
6262
String truncate(int truncateLength, {String endString = "..."}) =>
6363
truncateLength >= length
6464
? this
65-
: slice(0,truncateLength - endString.length) +
66-
endString;
65+
: slice(0, truncateLength - endString.length) + endString;
6766
}

lib/src/escape/escape.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ extension Escape on String {
4949
/// Return the unescaped version of a regex-escaped string
5050
///
5151
/// Removes escape sequences: `\\x` becomes `x`
52-
String unescapeRegExp() =>
53-
replaceAll(r'\\', r'\').replaceAll(r'\', '');
52+
String unescapeRegExp() => replaceAll(r'\\', r'\').replaceAll(r'\', '');
5453

5554
/// Return the un-escaped version of a regex
5655
@Deprecated('Use unescapeRegExp instead (corrected implementation)')

lib/src/manipulate/manipulate.dart

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ extension Manipulate on String {
1818
String latinize() => String.fromCharCodes(replaceCodeUnits(codeUnits));
1919

2020
/// Reverses a string. Works with strings containing graphemes
21-
String reverse() =>
22-
characters.split(''.characters).toList().reversed.join();
21+
String reverse() => characters.split(''.characters).toList().reversed.join();
2322

2423
/// Slugifies the string and replaces diacritics with corresponding latin
2524
/// characters
@@ -66,13 +65,12 @@ extension Manipulate on String {
6665
return translated;
6766
}
6867

69-
// / Wraps a string to a given number of characters using a string break
68+
// / Wraps a string to a given number of characters using a string break
7069
// / character
71-
// /
72-
// / [width] is the number of characters to wrap at. [newLine] is the string
70+
// /
71+
// / [width] is the number of characters to wrap at. [newLine] is the string
7372
// / added at the end of a line. [indent] is the string used to indent the line.
74-
// / When [cut] is `false`(default), it does not split the word even if the
75-
// / word length is bigger than `width`. When `true`, words having bigger
73+
// / When [cut] is `false`(default), it does not split the word even if the
74+
// / word length is bigger than `width`. When `true`, words having bigger
7675
// / length than `width` are broken.
77-
78-
}
76+
}

lib/src/util/object/extended_iterable.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ extension ExtendedIterable<E> on Iterable<E> {
1111
var i = 0;
1212
forEach((e) => f(e, i++));
1313
}
14-
}
14+
}

lib/src/util/regex/const_regex.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ const upperCaseLetter =
1818
'\x41-\x5a\xc0-\xd6\xd8-\xde\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178\u0179\u017b\u017d\u0181\u0182\u0184\u0186\u0187\u0189-\u018b\u018e-\u0191\u0193\u0194\u0196-\u0198\u019c\u019d\u019f\u01a0\u01a2\u01a4\u01a6\u01a7\u01a9\u01ac\u01ae\u01af\u01b1-\u01b3\u01b5\u01b7\u01b8\u01bc\u01c4\u01c5\u01c7\u01c8\u01ca\u01cb\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f2\u01f4\u01f6-\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a\u023b\u023d\u023e\u0241\u0243-\u0246\u0248\u024a\u024c\u024e';
1919

2020
/// Regular expression to match Unicode words
21-
const regexpWord = '(?:[$upperCaseLetter][$diacriticalMark]*)?(?:[$lowerCaseLetter][$diacriticalMark]*)+|(?:[$upperCaseLetter][$diacriticalMark]*)+(?![$lowerCaseLetter])|[$digit]+|[$dingbatBlock]|[^$nonCharacter$generalPunctuationBlock$whitespace]+';
21+
const regexpWord =
22+
'(?:[$upperCaseLetter][$diacriticalMark]*)?(?:[$lowerCaseLetter][$diacriticalMark]*)+|(?:[$upperCaseLetter][$diacriticalMark]*)+(?![$lowerCaseLetter])|[$digit]+|[$dingbatBlock]|[^$nonCharacter$generalPunctuationBlock$whitespace]+';
2223

2324
/// Regular expression to match words from Basic Latin and Latin-1 Supplement blocks
2425
const regexpLatinWord =
@@ -27,12 +28,14 @@ const regexpLatinWord =
2728
/// Regular expression to match alpha characters
2829
///
2930
/// @see http://stackoverflow.com/a/22075070/1894471
30-
final regexpAlpha = RegExp('^(?:[$lowerCaseLetter$upperCaseLetter][$diacriticalMark]*)+\$');
31+
final regexpAlpha =
32+
RegExp('^(?:[$lowerCaseLetter$upperCaseLetter][$diacriticalMark]*)+\$');
3133

3234
/// Regular expression to match alpha and digit characters
3335
///
3436
/// @see http://stackoverflow.com/a/22075070/1894471
35-
final regexpAlphaDigit = RegExp('^((?:[$lowerCaseLetter$upperCaseLetter][$diacriticalMark]*)|[$digit])+\$');
37+
final regexpAlphaDigit = RegExp(
38+
'^((?:[$lowerCaseLetter$upperCaseLetter][$diacriticalMark]*)|[$digit])+\$');
3639

3740
/// Regular expression to match Extended ASCII characters, i.e. the first 255
38-
const regexpExtendedAscii = r'^[\x01-\xFF]*$';
41+
const regexpExtendedAscii = r'^[\x01-\xFF]*$';

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: stringr
22
description: Comprehensive string manipulation plugin for dart. Handles operations on latin, non latin and grapheme clusters alike! Features inspured from VocaJs
3-
version: 1.2.0
3+
version: 1.2.1
44
homepage: https://github.com/Chinmay-KB/stringr
55

66
environment:

0 commit comments

Comments
 (0)