Index: trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEParser.m
===================================================================
--- trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEParser.m	(revision 375)
+++ trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEParser.m	(revision 383)
@@ -23,12 +23,12 @@
         [set formUnionWithCharacterSet: [NSCharacterSet punctuationCharacterSet]];
         // XXX obsoleted by RFC 3986 now... use §2.1, 2.2, 2.3
-        [set removeCharactersInString: @";/?:@&=+$,-_.!~*'()%#"]; // RFC 2396 §2.2, 2.3, 2.4, plus % and # from "delims" set
+        [set removeCharactersInString: @";/?:@&=+$,-_.!~*'(){}[]%#"]; // RFC 2396 §2.2, 2.3, 2.4, plus % and # from "delims" set and {}, []
 	
         tmpSet = [[set mutableCopy] autorelease];
-        [tmpSet formUnionWithCharacterSet: [NSCharacterSet characterSetWithCharactersInString: @"><("]];
+        [tmpSet formUnionWithCharacterSet: [NSCharacterSet characterSetWithCharactersInString: @"><(["]];
         urlLeftDelimiters = [tmpSet copy]; // make immutable again - for efficiency
 	
         tmpSet = [[set mutableCopy] autorelease];
-        [tmpSet formUnionWithCharacterSet: [NSCharacterSet characterSetWithCharactersInString: @"><)"]];
+        [tmpSet formUnionWithCharacterSet: [NSCharacterSet characterSetWithCharactersInString: @"><)]"]];
         urlRightDelimiters = [tmpSet copy]; // make immutable again - for efficiency
     }
@@ -114,4 +114,21 @@
 }
 
+static BOOL ICCF_StringIncludesCharacter(NSString *s, unichar character, NSRange range) {
+    return ([s rangeOfCharacterFromSet: [NSCharacterSet characterSetWithCharactersInString:
+					 [NSString stringWithCharacters: &character length: 1]]
+			       options: NSLiteralSearch range: range].location != NSNotFound);
+}
+
+static BOOL ICCF_IsLikelyURI(NSString *s, NSRange range) {
+    return ([s rangeOfCharacterFromSet: [NSCharacterSet characterSetWithCharactersInString: @":/.@"]
+			       options: NSLiteralSearch range: range].location != NSNotFound);
+}
+
+static BOOL ICCF_IsLikelyIPv6Address(NSString *s, NSRange range) {
+    return ([s rangeOfCharacterFromSet:
+	     [[NSCharacterSet characterSetWithCharactersInString: @"ABCDEFabcdef0123456789:"] invertedSet]
+			       options: NSLiteralSearch range: range].location == NSNotFound);
+}
+
 NSRange ICCF_URLEnclosingRange(NSString *s, NSRange range) {
     NSCharacterSet *urlLeftDelimiters = nil, *urlRightDelimiters = nil;
@@ -123,10 +140,15 @@
     ICCF_Delimiters(&urlLeftDelimiters, &urlRightDelimiters);
     
+    // right delimiter selected?  Yes, this can break with ...)URL(....  Oh well.
+    if (range.location > 0 && [urlRightDelimiters characterIsMember: [s characterAtIndex: range.location]]) {
+	--range.location;
+	++range.length;
+	ICLog(@"expanding past initial %c, now |%@|", [s characterAtIndex: range.location + 1],
+	      [s substringWithRange: range]);
+    }
+	
 expandFront:
     // XXX instead of 0, make this stop at the max URL length to prevent protracted searches
-    
-    // XXX here's how this is supposed to work:
-    // (http://web.sabi.net/) and <http://web.sabi.net/> should work if they are the entire document, even if clicking at the end/beginning of the document, not barfing with "no URL" (correct, as now) or selecting the final >, or ) (what would happen if we remove this "add 1" accommodation).  But how about "http://web.sabi.net/(foo)"?  That should work too, as long as it's not preceded by a (.
-    // Should probably backport to ICeCoffEETerminal, now I finally understand the method to this madness.
+    // XXX backport to ICeCoffEETerminal
     // add 1 to range to trap delimiters that are on the edge of the selection (i.e., <...)
     delimiterRange = [s rangeOfCharacterFromSet: urlLeftDelimiters
@@ -141,20 +163,5 @@
 	range.length += range.location - delimiterRange.location - 1;
 	range.location = delimiterRange.location + 1;
-	
-	// in url/(parens)stuff, handle clicking inside or after (parens).
-	/*if ([s characterAtIndex: delimiterRange.location] == '(' &&
-	    range.location > 2) { // prevent wrapping, ordinarily not necessary
-	    if ([s rangeOfString: @")" options: NSLiteralSearch range: range].location != NSNotFound ||
-		[s rangeOfCharacterFromSet: [NSCharacterSet characterSetWithCharactersInString: @"/."]
-				   options: NSLiteralSearch range: range].location == NSNotFound) {
-		range.location -= 2;
-		range.length += 2;
-		ICLog(@"expanding past (, now |%@|", [s substringWithRange: range]);
-		goto expandFront;
-	    }
-	} */       
-    }
-    
-    ICCF_CheckRange(range);
+    }
     
 expandBack:
@@ -169,19 +176,22 @@
 	// extend to end of string
 	range.length += extraLen;
+	extraLen = 0;
     } else {
 	NSCAssert(delimiterRange.length == 1, @"Internal error: delimiter matched range is not of length 1");
 	range.length += delimiterRange.location - range.location - range.length;
-	
-	// grow URL past closing paren/brace if we've seen an open paren/brace
-	unichar closing = [s characterAtIndex: delimiterRange.location];
-	NSString *opening;
-	if (closing == ')') opening = @"(";
-	else if (closing == '}') opening = @"{";
-	else goto checkRange;
-	if ([s rangeOfString: opening options: NSLiteralSearch range: range].location == NSNotFound)
-	    goto checkRange;
-	
-	if (extraLen == 0) {
+	extraLen = [s length] - NSMaxRange(range);
+
+	// grow URL past closing paren/brace/bracket if we've seen an open paren/brace/bracket
+	unichar opening, closing = [s characterAtIndex: delimiterRange.location];
+	if (closing == ')') opening = '(';
+	else if (closing == '}') opening = '{';
+	else if (closing == ']') opening = '[';
+	else goto expandBoth;
+	if (!ICCF_StringIncludesCharacter(s, opening, range))
+	    goto expandBoth;
+	
+	if (extraLen == 1) {
 	    range.length += 1;
+	    --extraLen;
 	    ICLog(@"expanding past %c, now |%@|", closing, [s substringWithRange: range]);
 	} else {
@@ -192,4 +202,40 @@
     }
     
+expandBoth:
+    if (range.location <= 1)
+	goto checkRange; // nowhere to expand
+    unichar opening = [s characterAtIndex: range.location - 1], closing;
+    if (opening == '(') closing = ')';
+    else if (opening == '{') closing = '}';
+    else if (opening == '[') closing = ']';
+    else goto checkRange;
+
+    ICLog(@"extraLen = %d", extraLen);
+    // check if we're inside a partial delimited URL: not foolproof, but handles (foo), {UUID} and [IPv6]
+    if (delimiterRange.location != NSNotFound && [s characterAtIndex: delimiterRange.location] == closing && 
+	((opening == '[' && ICCF_IsLikelyIPv6Address(s, range)) || !ICCF_IsLikelyURI(s, range))) {
+	ICLog(@"expanding past %c...%c, was |%@|", opening, closing, [s substringWithRange: range]);
+	range.location -= 2;
+	if (extraLen > 1)
+	    range.length += 4;
+	else
+	    range.length += 2 + extraLen;
+	ICLog(@"expanding past %c...%c, now |%@|", opening, closing, [s substringWithRange: range]);
+	goto expandFront;
+    }
+
+    if (ICCF_StringIncludesCharacter(s, closing, range) &&
+	((opening == '[' &&
+	  ICCF_IsLikelyIPv6Address(s, NSMakeRange(range.location, 
+						  [s rangeOfString: @"]"].location - range.location)))
+	 || !ICCF_IsLikelyURI(s, range))) {
+	ICLog(@"expanding past %c, was |%@|", opening, [s substringWithRange: range]);
+	ICLog(@"remaining: |%@|", [s substringWithRange: NSMakeRange(range.location, [s rangeOfString: [NSString stringWithCharacters: &closing length: 1]].location - range.location)]);
+	range.location -= 2;
+	range.length += 2;
+	ICLog(@"expanding past %c, now |%@|", opening, [s substringWithRange: range]);
+	goto expandFront;
+    }
+
 checkRange:
     ICCF_CheckRange(range);
