Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Grouping for indented single line comments #421

Merged
merged 1 commit into from

2 participants

@idpaterson

Allows single-line comments spanning multiple lines to be grouped if they have the same indentation.

I have always used single-line comments because historically it has been less prone to issues like these in Xcode and Appledoc. However, I noticed when documenting enums or any other code that is commonly indented such as ivars that only the last line of each comment block was included in documentation.

In the following example, due to the indentation of the comments under each enum value, the comments were not being grouped. This resulted in the documentation for NTXUIWebViewUnknownState reading simply cannot yet be determined. with a similar problem affecting the other comments that fill multiple lines.

/// A representation of the DOM `readyState` for the current request in a web
/// view. Implementation of this state allows inspection of the web view
/// progress at a much more meaningful level than is provided in the
/// `UIWebViewDelegate`.
typedef NS_ENUM(NSUInteger, NTXUIWebViewReadyState)
{
   /// The web view has not been yet been populated with a request, or the state
   /// cannot yet be determined.
   NTXUIWebViewUnknownState,
   /// A request is still pending for the document. At this time the web view is
   /// still displaying the previous document.
   NTXUIWebViewUninitializedState,
   /// The document has started to load but is not yet fully loaded and parsed.
   NTXUIWebViewLoadingState,
   /// The document has been loaded and parsed, the web view is ready to receive
   /// input such as JS commands, but there are still some resources that have
   /// not finished loading.
   NTXUIWebViewInteractiveState,
   /// The document and all dependent resources have finished loading.
   NTXUIWebViewCompleteState
};

Implementation

This pull request addresses that issue by allowing consecutive single-line comments to be grouped if they have the same indentation. The indentation is determined by finding the index at the start of the line containing the comment, then testing for any non-whitespace characters in the original input between the start of the line and the start of the comment. If there are any non-whitespace characters, the comment is considered to have no indentation and cannot be grouped (though this seems to have already been the case). Otherwise, the indentation is considered to be the number of whitespace characters between the start of the line and the offset of the comment.

Private helper methods have been added for determining the start offset of the line containing any given offset, as well as to determine the indentation of any particular offset as defined by the rules above.

Tests

Two new tests are introduced to ensure that single-line comments with matching indentation are grouped and that those with mismatched indentation are not grouped. All tests pass.

Caveats

Spaces and tabs each count as one unit when determining whether indentation matches. Code with mixed tabs and spaces within different lines of the same comment will not be grouped.

@tomaz
Owner

nice!

@tomaz tomaz merged commit b02fe29 into tomaz:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 9, 2014
  1. @idpaterson

    Allows single-line comments spanning multiple lines to be grouped if …

    idpaterson authored
    …they have the same indentation.
This page is out of date. Refresh to see the latest.
Showing with 61 additions and 2 deletions.
  1. +47 −2 Parsing/GBTokenizer.m
  2. +14 −0 Testing/GBTokenizerTesting.m
View
49 Parsing/GBTokenizer.m
@@ -20,6 +20,8 @@ - (NSString *)commentValueFromString:(NSString *)value isMultiline:(BOOL)multili
- (NSString *)lineByPreprocessingHeaderDocDirectives:(NSString *)line;
- (NSArray *)linesByReorderingHeaderDocDirectives:(NSArray *)lines;
- (NSArray *)allTokensFromTokenizer:(PKTokenizer *)tokenizer;
+- (NSUInteger)offsetOfLineContainingOffset:(NSUInteger)offset;
+- (NSInteger)indentationAtOffset:(NSUInteger)offset;
@property (retain) NSString *filename;
@property (retain) NSString *input;
@property (retain) NSArray *tokens;
@@ -182,17 +184,22 @@ - (BOOL)consumeComments {
PKToken *startingPreviousToken = nil;
PKToken *startingLastToken = nil;
- NSUInteger previousSingleLineEndOffset = 0;
+ NSUInteger previousSingleLineEndOffset = NSNotFound;
+ NSInteger previousSingleLineIndentation = -1;
while (![self eof] && [[self currentToken] isComment]) {
PKToken *token = [self currentToken];
NSString *value = nil;
- // Match single line comments. Note that we can simplify the code with assumption that there's only one single line comment per match. If regex finds more (should never happen though), we simply combine them together. Then we check if the comment is a continuation of previous single liner by testing the string offset. If so we group the values together, otherwise we create a new single line comment. Finally we remember current comment offset to allow grouping of next single line comment. CAUTION: this algorithm won't group comments unless they start at the beginning of the line!
+ // Match single line comments. Note that we can simplify the code with assumption that there's only one single line comment per match. If regex finds more (should never happen though), we simply combine them together. Then we check if the comment is a continuation of previous single liner by testing the string offset and indentation. If so we group the values together, otherwise we create a new single line comment. Finally we remember current comment offset to allow grouping of next single line comment.
NSArray *singleLiners = [[token stringValue] componentsMatchedByRegex:self.singleLineCommentRegex capture:1];
if ([singleLiners count] > 0) {
value = [NSString string];
for (NSString *match in singleLiners) value = [value stringByAppendingString:match];
+ NSInteger tokenIndentation = [self indentationAtOffset:[token offset]];
BOOL isContinuingPreviousSingleLiner = ([token offset] == previousSingleLineEndOffset + 1);
+ if (!isContinuingPreviousSingleLiner && previousSingleLineIndentation > 0 && tokenIndentation == previousSingleLineIndentation) {
+ isContinuingPreviousSingleLiner = ([token offset] == previousSingleLineEndOffset + previousSingleLineIndentation + 1);
+ }
if (isContinuingPreviousSingleLiner) {
[self.lastCommentBuilder appendString:@"\n"];
} else {
@@ -204,6 +211,7 @@ - (BOOL)consumeComments {
startingLastToken = token;
}
previousSingleLineEndOffset = [token offset] + [[token stringValue] length];
+ previousSingleLineIndentation = tokenIndentation;
}
// Match multiple line comments and only process last (in reality we should only have one comment in each mutliline comment token, but let's handle any strange cases graceosly).
@@ -403,6 +411,43 @@ - (NSArray *)allTokensFromTokenizer:(PKTokenizer *)tokenizer {
return result;
}
+- (NSUInteger)offsetOfLineContainingOffset:(NSUInteger)offset {
+ // This method returns the offset of the first character in the line
+ // containing the character at the specific offset.
+ NSRange newlineRange = [self.input rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]
+ options:NSBackwardsSearch
+ range:NSMakeRange(0, offset)];
+ if (newlineRange.location != NSNotFound) {
+ return newlineRange.location + 1;
+ }
+ // First line
+ return 0;
+}
+
+- (NSInteger)indentationAtOffset:(NSUInteger)offset {
+ // This method returns the number of tab or space characters preceding the
+ // offset if and only if it is only preceded by such indentation characters,
+ // otherwise returns -1.
+ NSUInteger lineOffset = [self offsetOfLineContainingOffset:offset];
+ NSRange lineToOffsetRange = NSMakeRange(lineOffset, offset - lineOffset);
+
+ // Short-circuit logic if offset is at the start of the line
+ if (lineToOffsetRange.length == 0) {
+ return 0;
+ }
+
+ NSCharacterSet * nonWhitespace = [[NSCharacterSet whitespaceCharacterSet] invertedSet];
+ NSRange nonWhitespaceRange = [self.input rangeOfCharacterFromSet:nonWhitespace
+ options:0
+ range:lineToOffsetRange];
+ // Line contains only whitespace preceding the offset: indentation
+ if (nonWhitespaceRange.location == NSNotFound) {
+ return lineToOffsetRange.length;
+ }
+ return -1;
+}
+
+
#pragma mark Properties
@synthesize filename;
View
14 Testing/GBTokenizerTesting.m
@@ -294,6 +294,20 @@ - (void)testLastCommentString_shouldGroupSingleLineComments {
assertThat([tokenizer.lastComment stringValue], is(@"line1\nline2"));
}
+- (void)testLastCommentString_shouldGroupSingleLineCommentsIfIndentationMatches {
+ // setup & execute
+ GBTokenizer *tokenizer = [GBTokenizer tokenizerWithSource:[PKTokenizer tokenizerWithString:@" /// line1\n /// line2\n ONE"] filename:@"file"];
+ // verify
+ assertThat([tokenizer.lastComment stringValue], is(@"line1\nline2"));
+}
+
+- (void)testLastCommentString_shouldIgnoreSingleLineCommentsIfIndentationDoesNotMatch {
+ // setup & execute
+ GBTokenizer *tokenizer = [GBTokenizer tokenizerWithSource:[PKTokenizer tokenizerWithString:@" /// line1\n /// line2\n ONE"] filename:@"file"];
+ // verify
+ assertThat([tokenizer.lastComment stringValue], is(@"line2"));
+}
+
- (void)testLastCommentString_shouldIgnoreSingleLineCommentsIfEmptyLineFoundInBetween {
// setup & execute
GBTokenizer *tokenizer = [GBTokenizer tokenizerWithSource:[PKTokenizer tokenizerWithString:@"/// line1\n\n/// line2\n ONE"] filename:@"file"];
Something went wrong with that request. Please try again.