aboutsummaryrefslogtreecommitdiffstats
path: root/Source/CMTextView.m
diff options
context:
space:
mode:
authorBibiko <bibiko@eva.mpg.de>2010-01-26 14:59:03 +0000
committerBibiko <bibiko@eva.mpg.de>2010-01-26 14:59:03 +0000
commita832db6dd09ca68207cbe28697fbf9107afe2ae4 (patch)
tree61dcc5691a404028ad01c492abf2cc619264aecb /Source/CMTextView.m
parentfcc453852ad3693590d42fedfe5280e74948cd0b (diff)
downloadsequelpro-a832db6dd09ca68207cbe28697fbf9107afe2ae4.tar.gz
sequelpro-a832db6dd09ca68207cbe28697fbf9107afe2ae4.tar.bz2
sequelpro-a832db6dd09ca68207cbe28697fbf9107afe2ae4.zip
• allow user to define nested snippets inside of the query favorites
- eg: SELECT ${1:${2:`mysql`.`user`.} AS ${5:a}} FROM ${7:`mysql`.`user`} ${9:WHERE } • improvements of the general control of the a snippet session • code cosmetics
Diffstat (limited to 'Source/CMTextView.m')
-rw-r--r--Source/CMTextView.m577
1 files changed, 325 insertions, 252 deletions
diff --git a/Source/CMTextView.m b/Source/CMTextView.m
index c8016451..7787f41e 100644
--- a/Source/CMTextView.m
+++ b/Source/CMTextView.m
@@ -74,6 +74,14 @@ YY_BUFFER_STATE yy_scan_string (const char *);
@implementation CMTextView
+/*
+ * Sort function (mainly used to sort the words in the textView)
+ */
+NSInteger alphabeticSort(id string1, id string2, void *reverse)
+{
+ return [string1 localizedCaseInsensitiveCompare:string2];
+}
+
- (void) awakeFromNib
{
// Set self as delegate for the textView's textStorage to enable syntax highlighting,
@@ -148,14 +156,6 @@ YY_BUFFER_STATE yy_scan_string (const char *);
}
/*
- * Sort function (mainly used to sort the words in the textView)
- */
-NSInteger alphabeticSort(id string1, id string2, void *reverse)
-{
- return [string1 localizedCaseInsensitiveCompare:string2];
-}
-
-/*
* Return an array of NSDictionary containing the sorted strings representing
* the set of unique words, SQL keywords, user-defined funcs/procs, tables etc.
* NSDic key "display" := the displayed and to be inserted word
@@ -613,15 +613,6 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
return [lineNumberView lineNumberForCharacterIndex:anIndex inText:[self string]]+1;
}
-
-/*
- * Search for the current selection or current word in the MySQL Help
- */
-- (IBAction) showMySQLHelpForCurrentWord:(id)sender
-{
- [[[[self window] delegate] valueForKeyPath:@"customQueryInstance"] showHelpForCurrentWord:self];
-}
-
/*
* Checks if the char after the current caret position/selection matches a supplied attribute
*/
@@ -697,6 +688,17 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
return NO;
}
+#pragma mark -
+#pragma mark user actions
+
+/*
+ * Search for the current selection or current word in the MySQL Help
+ */
+- (IBAction) showMySQLHelpForCurrentWord:(id)sender
+{
+ [[[[self window] delegate] valueForKeyPath:@"customQueryInstance"] showHelpForCurrentWord:self];
+}
+
/*
* If the textview has a selection, wrap it with the supplied prefix and suffix strings;
* return whether or not any wrap was performed.
@@ -779,25 +781,145 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
/*
- * Used for autoHelp update if the user changed the caret position by using the mouse.
+ * Shifts the selection, if any, rightwards by indenting any selected lines with one tab.
+ * If the caret is within a line, the selection is not changed after the index; if the selection
+ * has length, all lines crossed by the length are indented and fully selected.
+ * Returns whether or not an indentation was performed.
*/
-- (void) mouseDown:(NSEvent *)theEvent
+- (BOOL) shiftSelectionRight
{
-
- // Cancel autoHelp timer
- if([prefs boolForKey:SPCustomQueryUpdateAutoHelp])
- [NSObject cancelPreviousPerformRequestsWithTarget:self
- selector:@selector(autoHelp)
- object:nil];
+ NSString *textViewString = [[self textStorage] string];
+ NSRange currentLineRange;
+ NSArray *lineRanges;
+ NSString *tabString = @"\t";
+ NSInteger i, indentedLinesLength = 0;
- [super mouseDown:theEvent];
+ if ([self selectedRange].location == NSNotFound) return NO;
- // Start autoHelp timer
- if([prefs boolForKey:SPCustomQueryUpdateAutoHelp])
- [self performSelector:@selector(autoHelp) withObject:nil afterDelay:[[prefs valueForKey:SPCustomQueryAutoHelpDelay] doubleValue]];
-
+ // Indent the currently selected line if the caret is within a single line
+ if ([self selectedRange].length == 0) {
+ NSRange currentLineRange;
+
+ // Extract the current line range based on the text caret
+ currentLineRange = [textViewString lineRangeForRange:[self selectedRange]];
+
+ // Register the indent for undo
+ [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 0) replacementString:tabString];
+
+ // Insert the new tab
+ [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 0) withString:tabString];
+
+ return YES;
+ }
+
+ // Otherwise, the selection has a length - get an array of current line ranges for the specified selection
+ lineRanges = [textViewString lineRangesForRange:[self selectedRange]];
+
+ // Loop through the ranges, storing a count of the overall length.
+ for (i = 0; i < [lineRanges count]; i++) {
+ currentLineRange = NSRangeFromString([lineRanges objectAtIndex:i]);
+ indentedLinesLength += currentLineRange.length + 1;
+
+ // Register the indent for undo
+ [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location+i, 0) replacementString:tabString];
+
+ // Insert the new tab
+ [self replaceCharactersInRange:NSMakeRange(currentLineRange.location+i, 0) withString:tabString];
+ }
+
+ // Select the entirety of the new range
+ [self setSelectedRange:NSMakeRange(NSRangeFromString([lineRanges objectAtIndex:0]).location, indentedLinesLength)];
+
+ return YES;
+}
+
+
+/*
+ * Shifts the selection, if any, leftwards by un-indenting any selected lines by one tab if possible.
+ * If the caret is within a line, the selection is not changed after the undent; if the selection has
+ * length, all lines crossed by the length are un-indented and fully selected.
+ * Returns whether or not an indentation was performed.
+ */
+- (BOOL) shiftSelectionLeft
+{
+ NSString *textViewString = [[self textStorage] string];
+ NSRange currentLineRange;
+ NSArray *lineRanges;
+ NSInteger i, unindentedLines = 0, unindentedLinesLength = 0;
+
+ if ([self selectedRange].location == NSNotFound) return NO;
+
+ // Undent the currently selected line if the caret is within a single line
+ if ([self selectedRange].length == 0) {
+ NSRange currentLineRange;
+
+ // Extract the current line range based on the text caret
+ currentLineRange = [textViewString lineRangeForRange:[self selectedRange]];
+
+ // Ensure that the line has length and that the first character is a tab
+ if (currentLineRange.length < 1
+ || [textViewString characterAtIndex:currentLineRange.location] != '\t')
+ return NO;
+
+ // Register the undent for undo
+ [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 1) replacementString:@""];
+
+ // Remove the tab
+ [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 1) withString:@""];
+
+ return YES;
+ }
+
+ // Otherwise, the selection has a length - get an array of current line ranges for the specified selection
+ lineRanges = [textViewString lineRangesForRange:[self selectedRange]];
+
+ // Loop through the ranges, storing a count of the total lines changed and the new length.
+ for (i = 0; i < [lineRanges count]; i++) {
+ currentLineRange = NSRangeFromString([lineRanges objectAtIndex:i]);
+ unindentedLinesLength += currentLineRange.length;
+
+ // Ensure that the line has length and that the first character is a tab
+ if (currentLineRange.length < 1
+ || [textViewString characterAtIndex:currentLineRange.location-unindentedLines] != '\t')
+ continue;
+
+ // Register the undent for undo
+ [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location-unindentedLines, 1) replacementString:@""];
+
+ // Remove the tab
+ [self replaceCharactersInRange:NSMakeRange(currentLineRange.location-unindentedLines, 1) withString:@""];
+
+ // As a line has been unindented, modify counts and lengths
+ unindentedLines++;
+ unindentedLinesLength--;
+ }
+
+ // If a change was made, select the entirety of the new range and return success
+ if (unindentedLines) {
+ [self setSelectedRange:NSMakeRange(NSRangeFromString([lineRanges objectAtIndex:0]).location, unindentedLinesLength)];
+ return YES;
+ }
+
+ return NO;
+}
+
+#pragma mark -
+#pragma mark snippet handler
+
+/*
+ * Reset snippet controller variables to end a snippet session
+ */
+- (void)endSnippetSession
+{
+ snippetControlCounter = -1;
+ currentSnippetIndex = -1;
+ snippetControlMax = -1;
+ snippetWasJustInserted = NO;
}
+/*
+ * Selects the current snippet defined by “currentSnippetIndex”
+ */
- (void)selectCurrentSnippet
{
if( snippetControlCounter > -1
@@ -805,32 +927,39 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
&& currentSnippetIndex <= snippetControlMax
)
{
+ // Place the caret at the end of the query favorite snippet
+ // and finish snippet editing
if(currentSnippetIndex == snippetControlMax) {
[self setSelectedRange:NSMakeRange(snippetControlArray[snippetControlMax][0] + snippetControlArray[snippetControlMax][1], 0)];
- snippetControlCounter = -1;
- currentSnippetIndex = -1;
- snippetControlMax = -1;
+ [self endSnippetSession];
return;
}
- while(snippetControlArray[currentSnippetIndex][0] == -1 && currentSnippetIndex < 20) {
- currentSnippetIndex++;
+
+ if(currentSnippetIndex >= 0 && currentSnippetIndex < 20) {
+ if(snippetControlArray[currentSnippetIndex][2] == 0) {
+ [self setSelectedRange:NSMakeRange(snippetControlArray[currentSnippetIndex][0], snippetControlArray[currentSnippetIndex][1])];
+ }
+ } else { // for safety reasons
+ [self endSnippetSession];
}
- if(currentSnippetIndex >= 0 && currentSnippetIndex < 20)
- [self setSelectedRange:NSMakeRange(snippetControlArray[currentSnippetIndex][0], snippetControlArray[currentSnippetIndex][1])];
- else
- snippetControlCounter = -1;
- } else {
- snippetControlCounter = -1;
+ } else { // for safety reasons
+ [self endSnippetSession];
}
}
+/*
+ * Inserts a chosen query favorite and initialze a snippet session if user defined any
+ */
- (void)insertFavoriteAsSnippet:(NSString*)theSnippet atRange:(NSRange)targetRange
{
+
NSInteger i;
+
+ // reset snippet array
for(i=0; i<20; i++) {
- snippetControlArray[i][0] = -1;
- snippetControlArray[i][1] = -1;
- snippetControlArray[i][2] = -1;
+ snippetControlArray[i][0] = -1; // snippet location
+ snippetControlArray[i][1] = -1; // snippet length
+ snippetControlArray[i][2] = -1; // snippet task : -1 not valid, 0 select snippet
}
if(theSnippet == nil || ![theSnippet length]) return;
@@ -838,8 +967,10 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSMutableString *snip = [[NSMutableString alloc] initWithCapacity:[theSnippet length]];
@try{
NSString *re = @"(?<!\\\\)\\$\\{(1?\\d):([^\\{\\}]*)\\}";
+
targetRange = NSIntersectionRange(NSMakeRange(0,[[self string] length]), targetRange);
[snip setString:theSnippet];
+
if(snip == nil || ![snip length]) return;
// Replace `${x:…}` by ${x:`…`} for convience
@@ -847,7 +978,9 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[snip flushCachedRegexData];
snippetControlCounter = -1;
- snippetControlMax = -1;
+ snippetControlMax = -1;
+ currentSnippetIndex = -1;
+
while([snip isMatchedByRegex:re]) {
[snip flushCachedRegexData];
snippetControlCounter++;
@@ -855,85 +988,141 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSInteger snipCnt = [[snip substringWithRange:[snip rangeOfRegex:re capture:1L]] intValue];
NSRange hintRange = [snip rangeOfRegex:re capture:2L];
+ // Check for snippet number 19 (to simplify regexp)
if(snipCnt>18 || snipCnt<0) {
NSLog(@"Only snippets in the range of 0…18 allowed.");
- NSBeep();
- snippetControlCounter = -1;
+ [self endSnippetSession];
break;
}
+ // Remember the maximal snippet number defined by user
if(snipCnt>snippetControlMax)
snippetControlMax = snipCnt;
[snip replaceCharactersInRange:snipRange withString:[snip substringWithRange:hintRange]];
[snip flushCachedRegexData];
+
+ // Store found snippet range
snippetControlArray[snipCnt][0] = snipRange.location + targetRange.location;
snippetControlArray[snipCnt][1] = snipRange.length-4-((snipCnt>9)?2:1);
snippetControlArray[snipCnt][2] = 0;
- // Adjust nested snippets
- for(i=0; i<snipCnt; i++) {
- if(snippetControlArray[i][0] > -1
- && snippetControlArray[i][0] >= snippetControlArray[snipCnt][0]
- && snippetControlArray[i][1] <= snippetControlArray[snipCnt][1]
- ) {
- snippetControlArray[i][0] -= 3+((snipCnt>9)?2:1);
- snippetControlArray[i][1] += 3+((snipCnt>9)?2:1);
- }
- }
+
+ // Adjust successive snippets
+ for(i=0; i<20; i++)
+ if(snippetControlArray[i][0] > -1 && i != snipCnt && snippetControlArray[i][0] > snippetControlArray[snipCnt][0])
+ snippetControlArray[i][0] -= 3+((snipCnt>9)?2:1); // 3 := length(${:)
+
}
if(snippetControlCounter > -1) {
- // Store the end for eventually tab out
+ // Store the end for tab out
snippetControlMax++;
snippetControlArray[snippetControlMax][0] = targetRange.location + [snip length];
snippetControlArray[snippetControlMax][1] = 0;
snippetControlArray[snippetControlMax][2] = 0;
}
+ // Registering for undo
[self breakUndoCoalescing];
+
+ // Insert favorite query as snippet if any
[self setSelectedRange:targetRange];
+
+ // Suppress snippet range calculation in [self textStorageDidProcessEditing] while initial insertion
snippetWasJustInserted = YES;
[self insertText:snip];
- currentSnippetIndex = 0;
- if(snippetControlCounter > -1)
+ // Any snippets defined?
+ if(snippetControlCounter > -1) {
+ // Find and select first defined snippet
+ currentSnippetIndex = 0;
+ // Look for next defined snippet since snippet numbers must not serial like 1, 5, and 12 e.g.
+ while(snippetControlArray[currentSnippetIndex][0] == -1 && currentSnippetIndex < 20)
+ currentSnippetIndex++;
[self selectCurrentSnippet];
+ }
snippetWasJustInserted = NO;
}
- @catch(id ae) {
+ @catch(id ae) { // For safety reasons catch exceptions
NSLog(@"Snippet Error: %@", [ae description]);
- snippetControlCounter = -1;
+ [self endSnippetSession];
snippetWasJustInserted = NO;
}
+
if(snip)[snip release];
+
}
+/*
+ * Checks whether the current caret position in inside of a defined snippet range
+ */
- (BOOL)checkForCaretInsideSnippet
{
+ if(snippetWasJustInserted) return YES;
+
+ BOOL isCaretInsideASnippet = NO;
+
if(snippetControlCounter < 0 || currentSnippetIndex == snippetControlMax) {
- snippetControlCounter = -1;
+ [self endSnippetSession];
return NO;
}
- BOOL isCaretInsideASnippet = NO;
NSInteger caretPos = [self selectedRange].location;
- NSInteger i;
+ NSInteger i, j;
+ NSInteger foundSnippetIndices[20]; // array to hold nested snippets
- for(i=0; i<snippetControlMax; i++) {
+ j = -1;
+
+ // Go through all snippet ranges and check whether the caret is inside of the
+ // current snippet range. Remember matches
+ // in foundSnippetIndices array to test for nested snippets.
+ for(i=0; i<=snippetControlMax; i++) {
+ j++;
+ foundSnippetIndices[j] = 0;
if(snippetControlArray[i][0] != -1
&& caretPos >= snippetControlArray[i][0]
&& caretPos <= snippetControlArray[i][0] + snippetControlArray[i][1]) {
- isCaretInsideASnippet = YES;
- break;
+ foundSnippetIndices[j] = 1;
+ if(i == currentSnippetIndex) {
+ isCaretInsideASnippet = YES;
+ break;
+ }
}
}
- // if(!isCaretInsideASnippet)
- // snippetControlCounter = -1;
+ if(!isCaretInsideASnippet && foundSnippetIndices[currentSnippetIndex] == 1) {
+ isCaretInsideASnippet = YES;
+ }
+
return isCaretInsideASnippet;
+
+}
+
+#pragma mark -
+#pragma mark event management
+
+/*
+ * Used for autoHelp update if the user changed the caret position by using the mouse.
+ */
+- (void) mouseDown:(NSEvent *)theEvent
+{
+
+ // Cancel autoHelp timer
+ if([prefs boolForKey:SPCustomQueryUpdateAutoHelp])
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(autoHelp)
+ object:nil];
+
+ [super mouseDown:theEvent];
+
+ // Start autoHelp timer
+ if([prefs boolForKey:SPCustomQueryUpdateAutoHelp])
+ [self performSelector:@selector(autoHelp) withObject:nil afterDelay:[[prefs valueForKey:SPCustomQueryAutoHelpDelay] doubleValue]];
+
}
+
/*
* Handle some keyDown events in order to provide autopairing functionality (if enabled).
*/
@@ -977,46 +1166,51 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
return;
}
- // Check for {SHIFT}TAB to insert favorites via TAB-trigger if CMTextView belongs to CustomQuery
+ // Check for {SHIFT}TAB to try to insert query favorite via TAB trigger if CMTextView belongs to CustomQuery
if ([theEvent keyCode] == 48 && [self isEditable] && [[self delegate] isKindOfClass:[CustomQuery class]]){
NSRange targetRange = [self getRangeForCurrentWord];
NSString *tabTrigger = [[self string] substringWithRange:targetRange];
// Is TAB trigger active change selection according to {SHIFT}TAB
if([self checkForCaretInsideSnippet] && snippetControlCounter > -1){
- if(curFlags==(NSShiftKeyMask)) {
+
+ if(curFlags==(NSShiftKeyMask)) { // select previous snippet
currentSnippetIndex--;
+ // Look for previous defined snippet since snippet numbers must not serial like 1, 5, and 12 e.g.
while(snippetControlArray[currentSnippetIndex][0] == -1 && currentSnippetIndex > -2)
currentSnippetIndex--;
if(currentSnippetIndex < 0) {
- snippetControlCounter = -1;
- } else {
- [self selectCurrentSnippet];
- return;
+ currentSnippetIndex++;
+ NSBeep();
}
- } else {
+
+ [self selectCurrentSnippet];
+ return;
+
+ } else { // select next snippet
currentSnippetIndex++;
+ // Look for next defined snippet since snippet numbers must not serial like 1, 5, and 12 e.g.
while(snippetControlArray[currentSnippetIndex][0] == -1 && currentSnippetIndex < 20)
currentSnippetIndex++;
- if(currentSnippetIndex > snippetControlMax) {
- snippetControlCounter = -1;
- } else if(currentSnippetIndex == snippetControlMax) {
- [self selectCurrentSnippet];
- snippetControlCounter = -1;
- return;
+ if(currentSnippetIndex > snippetControlMax) { // for safety reasons
+ [self endSnippetSession];
} else {
[self selectCurrentSnippet];
return;
}
}
+
+ [self endSnippetSession];
+
}
- // Check if tab-trigger is defined; if so insert it
+
+ // Check if tab trigger is defined; if so insert it, otherwise pass through event
if(snippetControlCounter < 0 && [[[self window] delegate] fileURL]) {
NSArray *snippets = [[SPQueryController sharedQueryController] queryFavoritesForFileURL:[[[self window] delegate] fileURL] andTabTrigger:tabTrigger includeGlobals:YES];
if([snippets count] > 0 && [(NSString*)[(NSDictionary*)[snippets objectAtIndex:0] objectForKey:@"query"] length]) {
@@ -1259,130 +1453,6 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[super doCommandBySelector:aSelector];
}
-
-/*
- * Shifts the selection, if any, rightwards by indenting any selected lines with one tab.
- * If the caret is within a line, the selection is not changed after the index; if the selection
- * has length, all lines crossed by the length are indented and fully selected.
- * Returns whether or not an indentation was performed.
- */
-- (BOOL) shiftSelectionRight
-{
- NSString *textViewString = [[self textStorage] string];
- NSRange currentLineRange;
- NSArray *lineRanges;
- NSString *tabString = @"\t";
- NSInteger i, indentedLinesLength = 0;
-
- if ([self selectedRange].location == NSNotFound) return NO;
-
- // Indent the currently selected line if the caret is within a single line
- if ([self selectedRange].length == 0) {
- NSRange currentLineRange;
-
- // Extract the current line range based on the text caret
- currentLineRange = [textViewString lineRangeForRange:[self selectedRange]];
-
- // Register the indent for undo
- [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 0) replacementString:tabString];
-
- // Insert the new tab
- [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 0) withString:tabString];
-
- return YES;
- }
-
- // Otherwise, the selection has a length - get an array of current line ranges for the specified selection
- lineRanges = [textViewString lineRangesForRange:[self selectedRange]];
-
- // Loop through the ranges, storing a count of the overall length.
- for (i = 0; i < [lineRanges count]; i++) {
- currentLineRange = NSRangeFromString([lineRanges objectAtIndex:i]);
- indentedLinesLength += currentLineRange.length + 1;
-
- // Register the indent for undo
- [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location+i, 0) replacementString:tabString];
-
- // Insert the new tab
- [self replaceCharactersInRange:NSMakeRange(currentLineRange.location+i, 0) withString:tabString];
- }
-
- // Select the entirety of the new range
- [self setSelectedRange:NSMakeRange(NSRangeFromString([lineRanges objectAtIndex:0]).location, indentedLinesLength)];
-
- return YES;
-}
-
-
-/*
- * Shifts the selection, if any, leftwards by un-indenting any selected lines by one tab if possible.
- * If the caret is within a line, the selection is not changed after the undent; if the selection has
- * length, all lines crossed by the length are un-indented and fully selected.
- * Returns whether or not an indentation was performed.
- */
-- (BOOL) shiftSelectionLeft
-{
- NSString *textViewString = [[self textStorage] string];
- NSRange currentLineRange;
- NSArray *lineRanges;
- NSInteger i, unindentedLines = 0, unindentedLinesLength = 0;
-
- if ([self selectedRange].location == NSNotFound) return NO;
-
- // Undent the currently selected line if the caret is within a single line
- if ([self selectedRange].length == 0) {
- NSRange currentLineRange;
-
- // Extract the current line range based on the text caret
- currentLineRange = [textViewString lineRangeForRange:[self selectedRange]];
-
- // Ensure that the line has length and that the first character is a tab
- if (currentLineRange.length < 1
- || [textViewString characterAtIndex:currentLineRange.location] != '\t')
- return NO;
-
- // Register the undent for undo
- [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 1) replacementString:@""];
-
- // Remove the tab
- [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 1) withString:@""];
-
- return YES;
- }
-
- // Otherwise, the selection has a length - get an array of current line ranges for the specified selection
- lineRanges = [textViewString lineRangesForRange:[self selectedRange]];
-
- // Loop through the ranges, storing a count of the total lines changed and the new length.
- for (i = 0; i < [lineRanges count]; i++) {
- currentLineRange = NSRangeFromString([lineRanges objectAtIndex:i]);
- unindentedLinesLength += currentLineRange.length;
-
- // Ensure that the line has length and that the first character is a tab
- if (currentLineRange.length < 1
- || [textViewString characterAtIndex:currentLineRange.location-unindentedLines] != '\t')
- continue;
-
- // Register the undent for undo
- [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location-unindentedLines, 1) replacementString:@""];
-
- // Remove the tab
- [self replaceCharactersInRange:NSMakeRange(currentLineRange.location-unindentedLines, 1) withString:@""];
-
- // As a line has been unindented, modify counts and lengths
- unindentedLines++;
- unindentedLinesLength--;
- }
-
- // If a change was made, select the entirety of the new range and return success
- if (unindentedLines) {
- [self setSelectedRange:NSMakeRange(NSRangeFromString([lineRanges objectAtIndex:0]).location, unindentedLinesLength)];
- return YES;
- }
-
- return NO;
-}
-
/*
* Handle autocompletion, returning a list of suggested completions for the supplied character range.
*/
@@ -2896,14 +2966,14 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
/*
- * Performs syntax highlighting after a text change.
+ * Performs syntax highlighting, re-init autohelp, and re-calculation of snippets after a text change
*/
- (void)textStorageDidProcessEditing:(NSNotification *)notification
{
NSTextStorage *textStore = [notification object];
- //make sure that the notification is from the correct textStorage object
+ // Make sure that the notification is from the correct textStorage object
if (textStore!=[self textStorage]) return;
NSInteger editedMask = [textStore editedMask];
@@ -2913,60 +2983,63 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[self performSelector:@selector(autoHelp) withObject:nil afterDelay:[[[prefs valueForKey:SPCustomQueryAutoHelpDelay] retain] doubleValue]];
}
+ // Cancel calling doSyntaxHighlighting for large text
if([[self string] length] > SP_TEXT_SIZE_TRIGGER_FOR_PARTLY_PARSING)
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:@selector(doSyntaxHighlighting)
object:nil];
- // Do syntax highlighting only if the user really changed the text
+ // Do syntax highlighting/re-calculate snippet ranges only if the user really changed the text
if(editedMask != 1) {
+ // Re-calculate snippet ranges if snippet session is active
if(snippetControlCounter > -1 && !snippetWasJustInserted) {
- NSInteger editStartPosition = [textStore editedRange].location;
- NSInteger changeInLength = [textStore changeInLength];
- NSInteger i, j, k;
- BOOL isCaretInsideASnippet = NO;
- for(i=0; i<=snippetControlMax; i++) {
- if(snippetControlArray[i][0] > -1
- && editStartPosition >= snippetControlArray[i][0]
- && editStartPosition <= snippetControlArray[i][0] + snippetControlArray[i][1]) {
-
- if(i!=snippetControlMax)
- isCaretInsideASnippet = YES;
- snippetControlArray[i][1] += changeInLength;
- if(snippetControlArray[i][1] < 0) {
- snippetControlCounter = -1;
- break;
+
+ if([self checkForCaretInsideSnippet]) {
+
+ NSInteger editStartPosition = [textStore editedRange].location;
+ NSInteger changeInLength = [textStore changeInLength];
+ NSInteger i;
+
+ // Remove any fully nested snippets relative to the current snippet which is was edited
+ if(snippetControlArray[i][0] > -1 && i != snippetControlMax) {
+ NSInteger currentSnippetLocation = snippetControlArray[currentSnippetIndex][0];
+ NSInteger currentSnippetMaxRange = snippetControlArray[currentSnippetIndex][0] + snippetControlArray[currentSnippetIndex][1];
+ for(i=0; i<snippetControlMax; i++) {
+ if(snippetControlArray[i][0] > -1
+ && i != currentSnippetIndex
+ && snippetControlArray[i][0] >= currentSnippetLocation
+ && snippetControlArray[i][0] <= currentSnippetMaxRange
+ && snippetControlArray[i][0] + snippetControlArray[i][1] >= currentSnippetLocation
+ && snippetControlArray[i][0] + snippetControlArray[i][1] <= currentSnippetMaxRange
+ ) {
+ snippetControlArray[i][0] = -1;
+ snippetControlArray[i][1] = -1;
+ snippetControlArray[i][2] = -1;
+ }
}
- // Adjust start position of snippets after caret position
- } else if(snippetControlArray[i][0] > -1 && editStartPosition < snippetControlArray[i][0]) {
- snippetControlArray[i][0] += changeInLength;
- if(snippetControlArray[i][1] < 0) {
- snippetControlCounter = -1;
- break;
+ }
+
+ // Adjust length change to current snippet
+ snippetControlArray[currentSnippetIndex][1] += changeInLength;
+ // If length < 0 break snippet input
+ if(snippetControlArray[currentSnippetIndex][1] < 0) {
+ [self endSnippetSession];
+ } else {
+ // Adjust start position of snippets after caret position
+ for(i=0; i<=snippetControlMax; i++) {
+ if(snippetControlArray[i][0] > -1 && i != currentSnippetIndex) {
+ if(editStartPosition <= snippetControlArray[i][0]) {
+ snippetControlArray[i][0] += changeInLength;
+ } else if(editStartPosition >= snippetControlArray[i][0] && editStartPosition <= snippetControlArray[i][0] + snippetControlArray[i][1]) {
+ snippetControlArray[i][1] += changeInLength;
+ }
+ }
}
}
+ } else {
+ [self endSnippetSession];
}
- // If current snippet contains nested snippets then
- // delete nested snippet definitions
- // if(snippetControlArray[currentSnippetIndex][0] > -1 && currentSnippetIndex != snippetControlMax)
- // for(j=0; j<snippetControlMax; j++) {
- // if(snippetControlArray[j][0] > -1
- // && currentSnippetIndex != j
- // && snippetControlArray[j][0] >= snippetControlArray[currentSnippetIndex][0]
- // && snippetControlArray[j][0] <= snippetControlArray[currentSnippetIndex][0] + snippetControlArray[currentSnippetIndex][1]
- // && snippetControlArray[j][0] + snippetControlArray[j][1] >= snippetControlArray[currentSnippetIndex][0]
- // && snippetControlArray[j][0] + snippetControlArray[j][1] <= snippetControlArray[currentSnippetIndex][0] + snippetControlArray[currentSnippetIndex][1]
- // ) {
- // NSLog(@"%ld-%ld", currentSnippetIndex,j);
- // snippetControlArray[j][0] = -1;
- // snippetControlArray[j][1] = -1;
- // snippetControlArray[j][2] = -1;
- // }
- // }
- if(!isCaretInsideASnippet)
- snippetControlCounter = -1;
-
}
[self doSyntaxHighlighting];