diff --git a/Sources/HTMLRange.h b/Sources/HTMLRange.h index 44e90ec..a35c468 100644 --- a/Sources/HTMLRange.h +++ b/Sources/HTMLRange.h @@ -240,6 +240,13 @@ typedef NS_ENUM(unsigned short, HTMLRangeComparisonMethod) */ - (void)surroundContents:(HTMLNode *)newParent; +/** + The stringifier of the range. + + @return The text contents of the range. + */ +- (NSString *)textContent; + @end NS_ASSUME_NONNULL_END diff --git a/Sources/HTMLRange.m b/Sources/HTMLRange.m index b1cf9b7..57c2ade 100644 --- a/Sources/HTMLRange.m +++ b/Sources/HTMLRange.m @@ -710,4 +710,44 @@ NS_INLINE void CheckValidSurroundNodeType(HTMLNode *node, NSString *cmd) [self selectNode:newParent]; } +#pragma mark - Description & Stringifier + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p (%@, %lu), (%@, %lu)>", self.class, self, + _startContainer, (unsigned long)_startOffset, + _endContainer, (unsigned long)_endOffset]; +} + +- (NSString *)textContent +{ + HTMLNode *lastNode = nil; + if ([_endContainer isKindOfClass:[HTMLCharacterData class]]) { + lastNode = FollowingNodeSkippingChildren(_endContainer, _ownerDocument); + } else if (_endContainer.childNodesCount > _endOffset) { + lastNode = [_endContainer childNodeAtIndex:_endOffset]; + } else { + lastNode = FollowingNodeSkippingChildren(_endContainer, _ownerDocument); + } + + NSMutableString *content = [NSMutableString string]; + for (HTMLNode *node = _startContainer; node != lastNode; node = FollowingNode(node, _ownerDocument)) { + if (node.nodeType == HTMLNodeText) { + HTMLText *text = (HTMLText *)node; + + if (node == _startContainer) { + NSString *string = [text substringDataWithRange:NSMakeRange(_startOffset, _startContainer.length - _startOffset)]; + [content appendString:string]; + } else if (node == _endContainer) { + NSString *string = [text substringDataWithRange:NSMakeRange(0, _endOffset)]; + [content appendString:string]; + } else { + [content appendString:text.data]; + } + } + } + + return content; +} + @end diff --git a/Tests/HTMLRangeTests.m b/Tests/HTMLRangeTests.m index 51bfd8a..7c657fc 100644 --- a/Tests/HTMLRangeTests.m +++ b/Tests/HTMLRangeTests.m @@ -1949,4 +1949,48 @@ @"")); } +#pragma mark - Stringifier + +- (void)testRangeStringifier +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:2]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:3]; + XCTAssertEqualObjects([range textContent], @"is is a te"); + + start = [document querySelector:@"#P1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#P1"]; + [range setEndNode:end endOffset:2]; + XCTAssertEqualObjects([range textContent], @"This is a"); + + start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#D1"]; + [range setEndNode:end endOffset:1]; + XCTAssertEqualObjects([range textContent], @"This is a text"); + + start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#D1"]; + [range setEndNode:end endOffset:2]; + XCTAssertEqualObjects([range textContent], @"This is a textHello"); + + start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#D2"]; + [range setEndNode:end endOffset:0]; + XCTAssertEqualObjects([range textContent], @"This is a textHelloWorld"); + + start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#D2"]; + [range setEndNode:end endOffset:1]; + XCTAssertEqualObjects([range textContent], @"This is a textHelloWorldAnother text"); +} + @end