Permalink
Browse files

Fixed code blocks auto generated links handling.

If appledoc creates a cross reference link in a text that's later on converted to code block by Markdown processor, all such links are cleaned up when creating HTML value. This is required as Markdown processor doesn't process links inside these blocks.

Note that this is not the most elegant solution: first we process the text and convert to links, then we process generated HTML again and remove inserted links. Ideally, this situation should be detected while processing string and prevent creating links there in the first place. However we would require reimplementing detection of code blocks there, so at least for now, this should be good enough - as code blocks are not that common this should not affect performance too much...
  • Loading branch information...
1 parent 00c740b commit 604dffbd9182016a6207363657448902d63234d6 @tomaz committed Feb 23, 2011
@@ -334,12 +334,36 @@
/// @name Application-wide HTML helpers
///---------------------------------------------------------------------------------------
+/** Specifies whether cross references should be embedded to special strings when processing Markdown.
+
+ This should be left to default value, however it's useful to prevent embedding for unit testing.
+
+ @see stringByEmbeddingCrossReference:
+ */
+@property (assign) BOOL embedCrossReferencesWhenProcessingMarkdown;
+
+/** Returns a new string with the given Markdown reference embedded in special markers.
+
+ This should be used for all generated cross references, so that we can later detect them when converting HTML with `stringByConvertingMarkdownToHTML:`.
+
+ @warning *Important:* Behavior of this method depends on `embedCrossReferencesWhenProcessingMarkdown` value. If it's `YES`, strings are embedded, otherwise the given value is returned without enmbedding.
+
+ @param value The string to embedd.
+ @return Returns embedded string.
+ @see stringByConvertingMarkdownToHTML:
+ @see embedCrossReferencesWhenProcessingMarkdown
+ */
+- (NSString *)stringByEmbeddingCrossReference:(NSString *)value;
+
/** Returns a new string containing HTML representation of the given Markdown string.
- The resulting string is not escaped!
+ This is the main method for converting Markdown to HTML. It works in two phases: first the Markdown engine is asked to convert the given string to HTML, then the string is cleaned up so that it contains proper HTML code. Cleaning up phase consists of:
+
+ - Cleaning any appledoc generated cross reference inside `<pre>` blocks. Markdown doesn't process links here, so in case appledoc detects known object and converts it to Markdown style link, the Markdown syntaxt is left untouched. This phase makes sure all such occurences are cleaned up to original text. This is only invoked if `embedCrossReferencesWhenProcessingMarkdown` value is `YES`!
@param markdown Markdown source string to convert.
@return Returns converted string.
+ @see stringByEmbeddingCrossReference:
@see stringByConvertingMarkdownToText:
@see stringByEscapingHTML:
*/
@@ -83,6 +83,7 @@ - (id)init {
self.prefixMergedCategoriesSectionsWithCategoryName = NO;
self.prefixLocalMembersInRelatedItemsList = YES;
+ self.embedCrossReferencesWhenProcessingMarkdown = YES;
self.warnOnMissingOutputPathArgument = YES;
self.warnOnMissingCompanyIdentifier = YES;
@@ -148,7 +149,13 @@ - (void)replaceAllOccurencesOfPlaceholderStringsInSettingsValues {
#pragma mark Common HTML handling
+- (NSString *)stringByEmbeddingCrossReference:(NSString *)value {
+ if (!self.embedCrossReferencesWhenProcessingMarkdown) return value;
+ return [NSString stringWithFormat:@"~!@%@@!~", value];
+}
+
- (NSString *)stringByConvertingMarkdownToHTML:(NSString *)markdown {
+ // First pass the markdown to discount to get it converted to HTML.
NSString *result = nil;
MMIOT *document = mkd_string((char *)[markdown cStringUsingEncoding:NSUTF8StringEncoding], (int)[markdown length], 0);
mkd_compile(document, 0);
@@ -160,7 +167,32 @@ - (NSString *)stringByConvertingMarkdownToHTML:(NSString *)markdown {
result = [NSString stringWithCString:html encoding:NSASCIIStringEncoding];
}
mkd_cleanup(document);
- return result;
+
+ // Post process embedded cross references if needed.
+ if (!self.embedCrossReferencesWhenProcessingMarkdown) return result;
+ __block BOOL insideExampleBlock = NO;
+ NSString *regex = @"<pre>|</pre>|~!@(.+?)@!~";
+ NSString *clean = [result stringByReplacingOccurrencesOfRegex:regex usingBlock:^NSString *(NSInteger captureCount, NSString *const *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
+ // Change flag when inside example block - we need to handle strings differently there!
+ NSString *matchedText = capturedStrings[0];
+ if ([matchedText isEqualToString:@"<pre>"]) {
+ insideExampleBlock = YES;
+ return matchedText;
+ } else if ([matchedText isEqualToString:@"</pre>"]) {
+ insideExampleBlock = NO;
+ return matchedText;
+ }
+
+ // If outside example block, just return cross reference without embedded prefix and suffix!
+ NSString *linkText = capturedStrings[1];
+ if (!insideExampleBlock) return linkText;
+
+ // If inside example block, we need to extract description from Markdown text and only use that part! If we don't match Markdown style reference, just use whole text...
+ NSArray *components = [linkText captureComponentsMatchedByRegex:self.commentComponents.markdownInlineLinkRegex];
+ if ([components count] < 1) return linkText;
+ return [components objectAtIndex:1];
+ }];
+ return clean;
}
- (NSString *)stringByConvertingMarkdownToText:(NSString *)markdown {
@@ -497,6 +529,7 @@ - (NSString *)versionIdentifier {
@synthesize prefixMergedCategoriesSectionsWithCategoryName;
@synthesize prefixLocalMembersInRelatedItemsList;
+@synthesize embedCrossReferencesWhenProcessingMarkdown;
@synthesize createHTML;
@synthesize createDocSet;
@@ -472,7 +472,7 @@ - (NSString *)stringByPreprocessingString:(NSString *)string withFlags:(GBProces
}
// Finally replace all embedded code span Markdown links to proper ones. Embedded links look like: `[`desc`](address)`.
- NSString *regex = @"`(\\[`[^`]*`\\]\\(.+?\\))`";
+ NSString *regex = @"`((?:~!@)?\\[`[^`]*`\\]\\(.+?\\)(?:@!~)?)`";
NSString *clean = [result stringByReplacingOccurrencesOfRegex:regex usingBlock:^NSString *(NSInteger captureCount, NSString *const *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
return capturedStrings[1];
}];
@@ -612,9 +612,12 @@ - (NSString *)stringByConvertingSimpleCrossReferencesInString:(NSString *)string
- (NSString *)markdownLinkWithDescription:(NSString *)description address:(NSString *)address flags:(GBProcessingFlag)flags {
// Creates Markdown inline style link using the given components. This should be used when converting text to Markdown links as it will prepare special format so that we can later properly format links embedded in code spans!
+ NSString *result = nil;
if ((flags & GBProcessingFlagEmbedMarkdownLink) > 0)
- return [NSString stringWithFormat:@"[`%@`](%@)", description, address];
- return [NSString stringWithFormat:@"[%@](%@)", description, address];
+ result = [NSString stringWithFormat:@"[`%@`](%@)", description, address];
+ else
+ result = [NSString stringWithFormat:@"[%@](%@)", description, address];
+ return [self.settings stringByEmbeddingCrossReference:result];
}
- (NSString *)stringByConvertingLinesToBlockquoteFromString:(NSString *)string class:(NSString *)className {
@@ -413,6 +413,45 @@ - (void)testTemplateFilenameForOutputPath_shuoldReturnCorrectResults {
#pragma mark Text conversion methods
+- (void)testStringByEmbeddingCrossReference_shouldEmbeddCrossReferenceIfRequired {
+ // setup
+ GBApplicationSettingsProvider *settings1 = [GBApplicationSettingsProvider provider];
+ GBApplicationSettingsProvider *settings2 = [GBApplicationSettingsProvider provider];
+ settings2.embedCrossReferencesWhenProcessingMarkdown = NO;
+ // execute
+ NSString *result11 = [settings1 stringByEmbeddingCrossReference:@"[description](address \"title\")"];
+ NSString *result12 = [settings1 stringByEmbeddingCrossReference:@"[`description`](address \"title\")"];
+ NSString *result21 = [settings2 stringByEmbeddingCrossReference:@"[description](address \"title\")"];
+ NSString *result22 = [settings2 stringByEmbeddingCrossReference:@"[`description`](address \"title\")"];
+ // verify
+ assertThat(result11, is(@"~!@[description](address \"title\")@!~"));
+ assertThat(result12, is(@"~!@[`description`](address \"title\")@!~"));
+ assertThat(result21, is(@"[description](address \"title\")"));
+ assertThat(result22, is(@"[`description`](address \"title\")"));
+}
+
+- (void)testStringByConvertingToHTML_shouldConvertEmbeddedCrossReferencesInText {
+ // setup
+ GBApplicationSettingsProvider *settings = [GBApplicationSettingsProvider provider];
+ // execute
+ NSString *result1 = [settings stringByConvertingMarkdownToHTML:@"~!@[description](address)@!~"];
+ NSString *result2 = [settings stringByConvertingMarkdownToHTML:@"[description](address)"];
+ // verify - Discount converts any kind of link, we just need to strip embedded prefix and suffix!
+ assertThat(result1, is(@"<p><a href=\"address\">description</a></p>"));
+ assertThat(result2, is(@"<p><a href=\"address\">description</a></p>"));
+}
+
+- (void)testStringByConvertingToHTML_shouldConvertEmbeddedCrossReferencesInExampleBlock {
+ // setup
+ GBApplicationSettingsProvider *settings = [GBApplicationSettingsProvider provider];
+ // execute
+ NSString *result1 = [settings stringByConvertingMarkdownToHTML:@"\t~!@[description](address)@!~"];
+ NSString *result2 = [settings stringByConvertingMarkdownToHTML:@"\t[description](address)"];
+ // verify - Discount doesn't process links here, but we need to return auto generated to deafult! Note that Discount adds new line!
+ assertThat(result1, is(@"<pre><code>description\n</code></pre>"));
+ assertThat(result2, is(@"<pre><code>[description](address)\n</code></pre>"));
+}
+
- (void)testStringByConvertingToText_shouldConvertMarkdownReferences {
// setup
GBApplicationSettingsProvider *settings = [GBApplicationSettingsProvider provider];
@@ -137,6 +137,23 @@ - (void)testProcessCommentWithContextStore_markdown_shouldProperlyFormatInlineLi
[self assertComment:comment4 matchesLongDescMarkdown:@"_[Document](docs/Document.html)_", nil];
}
+- (void)testProcessCommentWithContextStore_markdown_shouldProperlyFormatInlineLinksWhenEmbeddingIsTurnedOn {
+ // setup
+ GBStore *store = [self storeWithDefaultObjects];
+ GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBComment *comment1 = [GBComment commentWithStringValue:@"Class"];
+ GBComment *comment2 = [GBComment commentWithStringValue:@"`Class`"];
+ GBComment *comment3 = [GBComment commentWithStringValue:@"@see Class"];
+ // execute
+ [processor processComment:comment1 withContext:nil store:store];
+ [processor processComment:comment2 withContext:nil store:store];
+ [processor processComment:comment3 withContext:nil store:store];
+ // verify
+ [self assertComment:comment1 matchesLongDescMarkdown:@"~!@[Class](Classes/Class.html)@!~", nil];
+ [self assertComment:comment2 matchesLongDescMarkdown:@"~!@[`Class`](Classes/Class.html)@!~", nil];
+ [self assertComponents:comment3.relatedItems matchMarkdown:@"~!@[Class](Classes/Class.html)@!~", nil];
+}
+
#pragma mark Related items cross references handling
- (void)testProcessCommentWithContextStore_markdown_shouldKeepRelatedItemsTopLevelObjectsCrossRefsTexts {
@@ -197,7 +214,10 @@ - (void)testProcessCommentWithContextStore_markdown_shouldKeepRelatedItemsRemote
#pragma mark Creation methods
- (GBCommentsProcessor *)defaultProcessor {
- return [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ // Creates a new GBCommentsProcessor using real settings. Note that we disable embedding cross references to make test strings more readable.
+ id settings = [GBTestObjectsRegistry realSettingsProvider];
+ [settings setEmbedCrossReferencesWhenProcessingMarkdown:NO];
+ return [GBCommentsProcessor processorWithSettingsProvider:settings];
}
- (GBStore *)defaultStore {
@@ -20,6 +20,7 @@ - (NSString *)stringByConvertingCrossReferencesInString:(NSString *)string withF
@interface GBCommentsProcessorPreprocessingTesting : GBObjectsAssertor
+- (GBCommentsProcessor *)defaultProcessor;
- (GBCommentsProcessor *)processorWithStore:(id)store;
- (GBCommentsProcessor *)processorWithStore:(id)store context:(id)context;
@@ -33,7 +34,7 @@ @implementation GBCommentsProcessorPreprocessingTesting
- (void)testStringByPreprocessingString_shouldHandleBoldMarkers {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result1 = [processor stringByPreprocessingString:@"*bold1* *bold text* * bolder text *" withFlags:0];
NSString *result2 = [processor stringByPreprocessingString:@"*bold1* Middle *bold text*" withFlags:0];
@@ -44,7 +45,7 @@ - (void)testStringByPreprocessingString_shouldHandleBoldMarkers {
- (void)testStringByPreprocessingString_shouldHandleItalicsMarkers {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result1 = [processor stringByPreprocessingString:@"_bold1_ _bold text_ _ bolder text _" withFlags:0];
NSString *result2 = [processor stringByPreprocessingString:@"_bold1_ Middle _bold text_" withFlags:0];
@@ -55,7 +56,7 @@ - (void)testStringByPreprocessingString_shouldHandleItalicsMarkers {
- (void)testStringByPreprocessingString_shouldHandleBoldItalicsMarkers {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result = [processor stringByPreprocessingString:@"_*text1*_ *_marked text_* _* text2 *_" withFlags:0];
// verify
@@ -64,7 +65,7 @@ - (void)testStringByPreprocessingString_shouldHandleBoldItalicsMarkers {
- (void)testStringByPreprocessingString_shouldHandleMonospaceMarkers {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result = [processor stringByPreprocessingString:@"`mono` ` monoer `" withFlags:0];
// verify
@@ -73,7 +74,7 @@ - (void)testStringByPreprocessingString_shouldHandleMonospaceMarkers {
- (void)testStringByPreprocessingString_shouldHandleMarkdownBoldMarkers {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result1 = [processor stringByPreprocessingString:@"__text1__ __ marked __" withFlags:0];
NSString *result2 = [processor stringByPreprocessingString:@"**text1** ** marked **" withFlags:0];
@@ -84,7 +85,7 @@ - (void)testStringByPreprocessingString_shouldHandleMarkdownBoldMarkers {
- (void)testStringByPreprocessingString_shouldHandleMarkdownBoldItalicsMarkers {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result1 = [processor stringByPreprocessingString:@"__*text1*__ __* marked *__" withFlags:0];
NSString *result2 = [processor stringByPreprocessingString:@"_**text1**_ _** marked **_" withFlags:0];
@@ -392,7 +393,7 @@ - (void)testStringByConvertingCrossReferencesInString_shouldConvertDocument {
- (void)testStringByConvertingCrossReferencesInString_shouldConvertHTML {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result1 = [processor stringByConvertingCrossReferencesInString:@"http://gentlebytes.com" withFlags:0];
NSString *result2 = [processor stringByConvertingCrossReferencesInString:@"https://gentlebytes.com" withFlags:0];
@@ -411,7 +412,7 @@ - (void)testStringByConvertingCrossReferencesInString_shouldConvertHTML {
- (void)testStringByConvertingCrossReferencesInString_shouldConvertFTP {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result1 = [processor stringByConvertingCrossReferencesInString:@"ftp://gentlebytes.com" withFlags:0];
NSString *result2 = [processor stringByConvertingCrossReferencesInString:@"ftps://gentlebytes.com" withFlags:0];
@@ -430,7 +431,7 @@ - (void)testStringByConvertingCrossReferencesInString_shouldConvertFTP {
- (void)testStringByConvertingCrossReferencesInString_shouldConvertNewsAndRSS {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result1 = [processor stringByConvertingCrossReferencesInString:@"news://gentlebytes.com" withFlags:0];
NSString *result2 = [processor stringByConvertingCrossReferencesInString:@"rss://gentlebytes.com" withFlags:0];
@@ -449,7 +450,7 @@ - (void)testStringByConvertingCrossReferencesInString_shouldConvertNewsAndRSS {
- (void)testStringByConvertingCrossReferencesInString_shouldConvertFile {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result1 = [processor stringByConvertingCrossReferencesInString:@"file://gentlebytes.com" withFlags:0];
NSString *result2 = [processor stringByConvertingCrossReferencesInString:@"<file://gentlebytes.com>" withFlags:0];
@@ -462,7 +463,7 @@ - (void)testStringByConvertingCrossReferencesInString_shouldConvertFile {
- (void)testStringByConvertingCrossReferencesInString_shouldConvertMailto {
// setup
- GBCommentsProcessor *processor = [GBCommentsProcessor processorWithSettingsProvider:[GBTestObjectsRegistry realSettingsProvider]];
+ GBCommentsProcessor *processor = [self defaultProcessor];
// execute
NSString *result1 = [processor stringByConvertingCrossReferencesInString:@"mailto:appledoc@gentlebytes.com" withFlags:0];
NSString *result2 = [processor stringByConvertingCrossReferencesInString:@"<mailto:appledoc@gentlebytes.com>" withFlags:0];
@@ -605,15 +606,23 @@ - (void)testStringByConvertingCrossReferencesInString_shouldHandleMarkdownLinkRe
#pragma mark Creation methods
+- (GBCommentsProcessor *)defaultProcessor {
+ // Creates a new GBCommentsProcessor using real settings. Note that we disable embedding cross references to make test strings more readable.
+ id settings = [GBTestObjectsRegistry realSettingsProvider];
+ [settings setEmbedCrossReferencesWhenProcessingMarkdown:NO];
+ return [GBCommentsProcessor processorWithSettingsProvider:settings];
+}
+
- (GBCommentsProcessor *)processorWithStore:(id)store {
// Creates a new GBCommentsProcessor using real settings and the given store.
return [self processorWithStore:store context:nil];
}
- (GBCommentsProcessor *)processorWithStore:(id)store context:(id)context {
- // Creates a new GBCommentsProcessor using real settings and the given store and context.
+ // Creates a new GBCommentsProcessor using real settings and the given store and context. Note that we disable embedding cross references to make test strings more readable.
id settings = [GBTestObjectsRegistry realSettingsProvider];
- GBCommentsProcessor *result = [GBCommentsProcessor processorWithSettingsProvider:settings];
+ [settings setEmbedCrossReferencesWhenProcessingMarkdown:NO];
+ GBCommentsProcessor *result = [self defaultProcessor];
[result setValue:store forKey:@"store"];
[result setValue:context forKey:@"currentContext"];
return result;

0 comments on commit 604dffb

Please sign in to comment.