Implement -[NSString enumerateLinesUsingBlock:] (#407)

* Implement -[NSString enumerateLinesUsingBlock:]

* Fix formatting

* Use GNUstep CALL_BLOCK macro
This commit is contained in:
Hugo Melder 2024-06-04 22:19:45 +02:00 committed by GitHub
parent c498475110
commit 4d3926d250
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 133 additions and 1 deletions

View file

@ -5,6 +5,13 @@
Improve implementation of BETWEEN operator in NSPredicate
for data types other than NSNumber. Proper support
for constant values.
2024-05-18 Hugo Melder <hugo@algoriddim.com>
* Source/NSString.m:
* Headers/Foundation/NSString.h:
* Tests/base/NSString/enumerateLinesUsingBlock.m:
Implemented -[NSString enumerateLinesUsingBlock:].
2024-04-30 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -466,6 +466,7 @@ enum {
typedef NSUInteger NSStringEnumerationOptions;
DEFINE_BLOCK_TYPE(GSNSStringEnumerationBlock, void, NSString* substring, NSRange substringRange, NSRange enclosingRange, BOOL* stop);
DEFINE_BLOCK_TYPE(GSNSStringLineEnumerationBlock, void, NSString *line, BOOL *stop);
#endif
/**
@ -1093,6 +1094,9 @@ GS_EXPORT_CLASS
#endif /* GS_API_NONE */
#if OS_API_VERSION(MAC_OS_X_VERSION_10_6,GS_API_LATEST)
- (void) enumerateLinesUsingBlock: (GSNSStringLineEnumerationBlock)block;
- (void) enumerateSubstringsInRange: (NSRange)range
options: (NSStringEnumerationOptions)opts
usingBlock: (GSNSStringEnumerationBlock)block;

View file

@ -552,7 +552,6 @@ NSString:
- lowercaseStringWithLocale:
- capitalizedStringWithLocale:
- enumerateSubstringsInRange:options:usingBlock:
- enumerateLinesUsingBlock:
- getBytes:maxLength:usedLength:encoding:options:range:remainingRange:
- decomposedStringWithCanonicalMapping
- precomposedStringWithCanonicalMapping

View file

@ -6271,6 +6271,39 @@ static NSFileManager *fm = nil;
return [self rangeOfString: string].location != NSNotFound;
}
- (void) enumerateLinesUsingBlock: (GSNSStringLineEnumerationBlock)block
{
NSUInteger length;
NSUInteger lineStart, lineEnd, contentsEnd;
NSRange currentLocationRange;
BOOL stop;
length = [self length];
lineStart = lineEnd = contentsEnd = 0;
stop = NO;
// Enumerate through the string line by line
while (lineStart < length && !stop) {
NSString *line;
NSRange lineRange;
currentLocationRange = NSMakeRange(lineStart, 0);
[self getLineStart: &lineStart
end: &lineEnd
contentsEnd: &contentsEnd
forRange: currentLocationRange];
lineRange = NSMakeRange(lineStart, contentsEnd - lineStart);
line = [self substringWithRange: lineRange];
// Execute the block
CALL_BLOCK(block, line, &stop);
// Move to the next line
lineStart = lineEnd;
}
}
- (void) enumerateSubstringsInRange: (NSRange)range
options: (NSStringEnumerationOptions)opts
usingBlock: (GSNSStringEnumerationBlock)block

View file

@ -0,0 +1,89 @@
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
#import "Testing.h"
#if defined(__has_extension) && __has_extension(blocks)
BOOL testEnumerateSimpleLines() {
NSString *testString = @"First line\nSecond line\nThird line";
NSMutableArray *collectedLines = [NSMutableArray array];
NSArray *expectedLines = @[@"First line", @"Second line", @"Third line"];
[testString enumerateLinesUsingBlock: ^(NSString *line, BOOL *stop) {
[collectedLines addObject: line];
}];
return [collectedLines isEqualToArray: expectedLines];
}
BOOL testEnumerateCRLFLines() {
NSString *testString = @"First line\r\nSecond line\r\nThird line";
NSMutableArray *collectedLines = [NSMutableArray array];
NSArray *expectedLines = @[@"First line", @"Second line", @"Third line"];
[testString enumerateLinesUsingBlock: ^(NSString *line, BOOL *stop) {
[collectedLines addObject: line];
}];
return [collectedLines isEqualToArray: expectedLines];
}
BOOL testStopEarly() {
NSString *testString = @"First line\nSecond line\nThird line";
__block int lineCount = 0;
[testString enumerateLinesUsingBlock:^(NSString *line, BOOL *stop) {
lineCount++;
if ([line isEqualToString:@"Second line"]) {
*stop = YES;
}
}];
return lineCount == 2; // Should stop after the second line
}
BOOL testEmptyString() {
NSString *testString = @"";
__block BOOL blockCalled = NO;
[testString enumerateLinesUsingBlock:^(NSString *line, BOOL *stop) {
blockCalled = YES;
}];
return !blockCalled; // Block should not be called
}
BOOL testSingleLineNoBreaks() {
NSString *testString = @"Single line without line breaks";
__block NSString *receivedLine = nil;
[testString enumerateLinesUsingBlock:^(NSString *line, BOOL *stop) {
receivedLine = line;
}];
return [receivedLine isEqualToString:testString];
}
int main() {
NSAutoreleasePool *arp = [NSAutoreleasePool new];
PASS(testEnumerateSimpleLines(), "Should enumerate all lines correctly.");
PASS(testEnumerateCRLFLines(), "Should enumerate all CRLF lines correctly.");
PASS(testStopEarly(), "Should stop enumeration early as directed.");
PASS(testEmptyString(), "Should not call block for empty string.");
PASS(testSingleLineNoBreaks(), "Should handle single line without line breaks correctly.");
[arp release];
return 0;
}
#else
int main (int argc, const char * argv[])
{
return 0;
}
#endif