From 231f4a4e70807e9824fd3c14c3bd258b37e0dbe4 Mon Sep 17 00:00:00 2001 From: Bibiko Date: Fri, 21 Jan 2011 09:05:03 +0000 Subject: =?UTF-8?q?=E2=80=A2=20implemented=20user-definable=20Soft=20Inden?= =?UTF-8?q?tion=20-=20instead=20of=20using=20\t=20as=20indention=20string?= =?UTF-8?q?=20the=20user=20can=20choose=20how=20many=20spaces=20should=20b?= =?UTF-8?q?e=20used=20instead=20-=20addresses=20issue=20956?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/SPConstants.h | 2 + Source/SPConstants.m | 2 + Source/SPTextView.h | 1 + Source/SPTextView.m | 133 ++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 116 insertions(+), 22 deletions(-) (limited to 'Source') diff --git a/Source/SPConstants.h b/Source/SPConstants.h index feb7e699..5a11300a 100644 --- a/Source/SPConstants.h +++ b/Source/SPConstants.h @@ -329,6 +329,8 @@ extern NSString *SPCustomQueryAutoComplete; extern NSString *SPCustomQueryAutoCompleteDelay; extern NSString *SPCustomQueryFunctionCompletionInsertsArguments; extern NSString *SPCustomQueryEditorThemeName; +extern NSString *SPCustomQuerySoftIndent; +extern NSString *SPCustomQuerySoftIndentWidth; // AutoUpdate Prefpane extern NSString *SPLastUsedVersion; diff --git a/Source/SPConstants.m b/Source/SPConstants.m index e5020abd..366832de 100644 --- a/Source/SPConstants.m +++ b/Source/SPConstants.m @@ -130,6 +130,8 @@ NSString *SPCustomQueryAutoComplete = @"CustomQueryAutoComplete"; NSString *SPCustomQueryAutoCompleteDelay = @"CustomQueryAutoCompleteDelay"; NSString *SPCustomQueryFunctionCompletionInsertsArguments = @"CustomQueryFunctionCompletionInsertsArguments"; NSString *SPCustomQueryEditorThemeName = @"CustomQueryEditorThemeName"; +NSString *SPCustomQuerySoftIndent = @"CustomQuerySoftIndent"; +NSString *SPCustomQuerySoftIndentWidth = @"CustomQuerySoftIndentWidth"; // AutoUpdate Prefpane NSString *SPLastUsedVersion = @"LastUsedVersion"; diff --git a/Source/SPTextView.h b/Source/SPTextView.h index 797fff34..b15030b4 100644 --- a/Source/SPTextView.h +++ b/Source/SPTextView.h @@ -107,6 +107,7 @@ - (BOOL) isNextCharMarkedBy:(id)attribute withValue:(id)aValue; - (BOOL) areAdjacentCharsLinked; - (BOOL) isCaretAdjacentToAlphanumCharWithInsertionOf:(unichar)aChar; +- (BOOL) isCaretAtIndentPositionIgnoreLineStart:(BOOL)ignoreLineStart; - (BOOL) wrapSelectionWithPrefix:(NSString *)prefix suffix:(NSString *)suffix; - (BOOL) shiftSelectionRight; - (BOOL) shiftSelectionLeft; diff --git a/Source/SPTextView.m b/Source/SPTextView.m index 95d959c6..b4090cb9 100644 --- a/Source/SPTextView.m +++ b/Source/SPTextView.m @@ -916,6 +916,24 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) return (leftIsAlphanum ^ rightIsAlphanum || leftIsAlphanum && rightIsAlphanum); } +/** + * Checks if all the characters left from the caret are white spaces or caret is at the line begin. + */ +- (BOOL) isCaretAtIndentPositionIgnoreLineStart:(BOOL)ignoreLineStart +{ + NSString *textViewString = [[self textStorage] string]; + NSUInteger caretPosition = [self selectedRange].location; + NSUInteger currentLineStartPosition = [textViewString lineRangeForRange:NSMakeRange(caretPosition, 0)].location; + + // Check if caret is at the beginning of a line + // - used for deleteBackward: to allow to delete leading \n + if(!ignoreLineStart && caretPosition == currentLineStartPosition) + return NO; + + NSString *lineHeadToCaret = [textViewString substringWithRange:NSMakeRange(currentLineStartPosition, caretPosition-currentLineStartPosition)]; + return (![lineHeadToCaret length] || [lineHeadToCaret isMatchedByRegex:@"^\\s+$"]); +} + /** * Checks if the caret is wrapped by auto-paired characters. * e.g. [| := caret]: "|" @@ -1105,27 +1123,39 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) { NSString *textViewString = [[self textStorage] string]; NSRange currentLineRange; - - if ([self selectedRange].location == NSNotFound || ![self isEditable]) return NO; + NSRange selectedRange = [self selectedRange]; + + if (selectedRange.location == NSNotFound || ![self isEditable]) return NO; + + NSString *indentString = @"\t"; + if ([prefs boolForKey:SPCustomQuerySoftIndent]) { + NSUInteger numberOfSpaces = [prefs integerForKey:SPCustomQuerySoftIndentWidth]; + if(numberOfSpaces < 1) numberOfSpaces = 1; + if(numberOfSpaces > 32) numberOfSpaces = 32; + NSMutableString *spaces = [NSMutableString string]; + for(NSInteger i = 0; i < numberOfSpaces; i++) + [spaces appendString:@" "]; + indentString = [NSString stringWithString:spaces]; + } // Indent the currently selected line if the caret is within a single line - if ([self selectedRange].length == 0) { + if (selectedRange.length == 0) { // Extract the current line range based on the text caret - currentLineRange = [textViewString lineRangeForRange:[self selectedRange]]; + currentLineRange = [textViewString lineRangeForRange:selectedRange]; // Register the indent for undo - [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 0) replacementString:@"\t"]; + [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 0) replacementString:indentString]; // Insert the new tab - [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 0) withString:@"\t"]; + [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 0) withString:indentString]; return YES; } // Otherwise, something is selected - NSRange firstLineRange = [textViewString lineRangeForRange:NSMakeRange([self selectedRange].location,0)]; - NSUInteger lastLineMaxRange = NSMaxRange([textViewString lineRangeForRange:NSMakeRange(NSMaxRange([self selectedRange])-1,0)]); + NSRange firstLineRange = [textViewString lineRangeForRange:NSMakeRange(selectedRange.location,0)]; + NSUInteger lastLineMaxRange = NSMaxRange([textViewString lineRangeForRange:NSMakeRange(NSMaxRange(selectedRange)-1,0)]); // Expand selection for first and last line to begin and end resp. but not the last line ending NSRange blockRange = NSMakeRange(firstLineRange.location, lastLineMaxRange - firstLineRange.location); @@ -1136,13 +1166,13 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) NSString *newString; // check for line ending if([textViewString characterAtIndex:NSMaxRange(firstLineRange)-1] == '\r') - newString = [[NSString stringWithString:@"\t"] stringByAppendingString: + newString = [indentString stringByAppendingString: [[textViewString substringWithRange:blockRange] - stringByReplacingOccurrencesOfString:@"\r" withString:@"\r\t"]]; + stringByReplacingOccurrencesOfString:@"\r" withString:[NSString stringWithFormat:@"\r%@", indentString]]]; else - newString = [[NSString stringWithString:@"\t"] stringByAppendingString: + newString = [indentString stringByAppendingString: [[textViewString substringWithRange:blockRange] - stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]]; + stringByReplacingOccurrencesOfString:@"\n" withString:[NSString stringWithFormat:@"\n%@", indentString]]]; // Register the indent for undo [self shouldChangeTextInRange:blockRange replacementString:newString]; @@ -1183,11 +1213,30 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) || ([textViewString characterAtIndex:currentLineRange.location] != '\t' && [textViewString characterAtIndex:currentLineRange.location] != ' ')) return NO; + NSRange replaceRange; + + // Check for soft indention + NSUInteger indentStringLength = 1; + if ([prefs boolForKey:SPCustomQuerySoftIndent]) { + NSUInteger numberOfSpaces = [prefs integerForKey:SPCustomQuerySoftIndentWidth]; + if(numberOfSpaces < 1) numberOfSpaces = 1; + if(numberOfSpaces > 32) numberOfSpaces = 32; + indentStringLength = numberOfSpaces; + replaceRange = NSIntersectionRange(NSMakeRange(currentLineRange.location, indentStringLength), NSMakeRange(0,[[self string] length])); + // Correct length for only white spaces + NSString *possibleIndentString = [[[self textStorage] string] substringWithRange:replaceRange]; + NSUInteger numberOfLeadingWhiteSpaces = [possibleIndentString rangeOfRegex:@"^(\\s*)" capture:1L].length; + if(numberOfLeadingWhiteSpaces == NSNotFound) numberOfLeadingWhiteSpaces = 0; + replaceRange = NSMakeRange(currentLineRange.location, numberOfLeadingWhiteSpaces); + } else { + replaceRange = NSMakeRange(currentLineRange.location, indentStringLength); + } + // Register the undent for undo - [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 1) replacementString:@""]; + [self shouldChangeTextInRange:replaceRange replacementString:@""]; // Remove the tab - [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 1) withString:@""]; + [self replaceCharactersInRange:replaceRange withString:@""]; return YES; } @@ -1201,25 +1250,36 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) if([textViewString characterAtIndex:NSMaxRange(blockRange)-1] == '\n' || [textViewString characterAtIndex:NSMaxRange(blockRange)-1] == '\r') blockRange.length--; + // Check for soft or hard indention + NSString *indentString = @"\t"; + NSUInteger indentStringLength = 1; + if ([prefs boolForKey:SPCustomQuerySoftIndent]) { + indentStringLength = [prefs integerForKey:SPCustomQuerySoftIndentWidth]; + if(indentStringLength < 1) indentStringLength = 1; + if(indentStringLength > 32) indentStringLength = 32; + NSMutableString *spaces = [NSMutableString string]; + for(NSInteger i = 0; i < indentStringLength; i++) + [spaces appendString:@" "]; + indentString = [NSString stringWithString:spaces]; + } + // Check if blockRange starts with SPACE or TAB // (this also catches the first line of the entire text buffer or // if only one line is selected) NSInteger leading = 0; if([textViewString characterAtIndex:blockRange.location] == ' ' || [textViewString characterAtIndex:blockRange.location] == '\t') - leading++; + leading += indentStringLength; // Replace \n[ \t] by \n of all lines in blockRange NSString *newString; // check for line ending if([textViewString characterAtIndex:NSMaxRange(firstLineRange)-1] == '\r') - newString = [[[textViewString substringWithRange:NSMakeRange(blockRange.location+leading, blockRange.length-leading)] - stringByReplacingOccurrencesOfString:@"\r\t" withString:@"\r"] - stringByReplacingOccurrencesOfString:@"\r " withString:@"\r"]; + newString = [[textViewString substringWithRange:NSMakeRange(blockRange.location+leading, blockRange.length-leading)] + stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"\r%@", indentString] withString:@"\r"]; else - newString = [[[textViewString substringWithRange:NSMakeRange(blockRange.location+leading, blockRange.length-leading)] - stringByReplacingOccurrencesOfString:@"\n\t" withString:@"\n"] - stringByReplacingOccurrencesOfString:@"\n " withString:@"\n"]; + newString = [[textViewString substringWithRange:NSMakeRange(blockRange.location+leading, blockRange.length-leading)] + stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"\n%@", indentString] withString:@"\n"]; // Register the unindent for undo [self shouldChangeTextInRange:blockRange replacementString:newString]; @@ -2026,6 +2086,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) } // Check for {SHIFT}TAB to try to insert query favorite via TAB trigger if SPTextView belongs to SPCustomQuery + // and TAB as soft indention if ([theEvent keyCode] == 48 && [self isEditable] && [[self delegate] isKindOfClass:[SPCustomQuery class]]){ NSRange targetRange = [self getRangeForCurrentWord]; NSString *tabTrigger = [[self string] substringWithRange:targetRange]; @@ -2072,13 +2133,19 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) } // Check if tab trigger is defined; if so insert it, otherwise pass through event - if(snippetControlCounter < 0 && [tableDocumentInstance fileURL]) { + if(snippetControlCounter < 0 && [tabTrigger length] && [tableDocumentInstance fileURL]) { NSArray *snippets = [[SPQueryController sharedQueryController] queryFavoritesForFileURL:[tableDocumentInstance fileURL] andTabTrigger:tabTrigger includeGlobals:YES]; if([snippets count] > 0 && [(NSString*)[(NSDictionary*)[snippets objectAtIndex:0] objectForKey:@"query"] length]) { [self insertAsSnippet:[(NSDictionary*)[snippets objectAtIndex:0] objectForKey:@"query"] atRange:targetRange]; return; } } + + // Check for TAB as indention for current line, i.e. left of the caret there are only white spaces + // but only if Soft Indent is set + if([prefs boolForKey:SPCustomQuerySoftIndent] && [self isCaretAtIndentPositionIgnoreLineStart:YES]) { + if([self shiftSelectionRight]) return; + } } // Note: switch(insertedCharacter) {} does not work instead use charactersIgnoringModifiers @@ -2358,6 +2425,28 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) // Return to avoid the original implementation, preventing double linebreaks return; } + + // Remove soft indent if active and left from caret are only white spaces + if (aSelector == @selector(deleteBackward:) + && ![self selectedRange].length + && [prefs boolForKey:SPCustomQuerySoftIndent] + && [self isCaretAtIndentPositionIgnoreLineStart:NO]) + { + [self shiftSelectionLeft]; + return; + } + + // Remove soft indent if active and left from caret are only white spaces + if (aSelector == @selector(deleteForward:) + && ![self selectedRange].length + && [prefs boolForKey:SPCustomQuerySoftIndent] + && [self isCaretAtIndentPositionIgnoreLineStart:YES] + && ![self isCaretAdjacentToAlphanumCharWithInsertionOf:'-']) + { + [self shiftSelectionLeft]; + return; + } + [super doCommandBySelector:aSelector]; } -- cgit v1.2.3