aboutsummaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Source')
-rw-r--r--Source/CMTextView.h18
-rw-r--r--Source/CMTextView.m134
-rw-r--r--Source/CustomQuery.h7
-rw-r--r--Source/CustomQuery.m53
-rw-r--r--Source/SPStringAdditions.h1
-rw-r--r--Source/SPStringAdditions.m33
6 files changed, 233 insertions, 13 deletions
diff --git a/Source/CMTextView.h b/Source/CMTextView.h
index 761e1189..4dadb33d 100644
--- a/Source/CMTextView.h
+++ b/Source/CMTextView.h
@@ -32,13 +32,15 @@
- (BOOL) isNextCharMarkedBy:(id)attribute;
- (BOOL) areAdjacentCharsLinked;
- (BOOL) wrapSelectionWithPrefix:(NSString *)prefix suffix:(NSString *)suffix;
-- (NSArray *)completionsForPartialWordRange:(NSRange)charRange indexOfSelectedItem:(int *)index;
-- (NSArray *)keywords;
-- (void)setAutoindent:(BOOL)enableAutoindent;
-- (BOOL)autoindent;
-- (void)setAutoindentIgnoresEnter:(BOOL)enableAutoindentIgnoresEnter;
-- (BOOL)autoindentIgnoresEnter;
-- (void)setAutopair:(BOOL)enableAutopair;
-- (BOOL)autopair;
+- (BOOL) shiftSelectionRight;
+- (BOOL) shiftSelectionLeft;
+- (NSArray *) completionsForPartialWordRange:(NSRange)charRange indexOfSelectedItem:(int *)index;
+- (NSArray *) keywords;
+- (void) setAutoindent:(BOOL)enableAutoindent;
+- (BOOL) autoindent;
+- (void) setAutoindentIgnoresEnter:(BOOL)enableAutoindentIgnoresEnter;
+- (BOOL) autoindentIgnoresEnter;
+- (void) setAutopair:(BOOL)enableAutopair;
+- (BOOL) autopair;
@end
diff --git a/Source/CMTextView.m b/Source/CMTextView.m
index 2532cf1e..cfd42884 100644
--- a/Source/CMTextView.m
+++ b/Source/CMTextView.m
@@ -252,15 +252,18 @@ YY_BUFFER_STATE yy_scan_string (const char *);
{
// Handle newlines, adding any indentation found on the current line to the new line - ignoring the enter key if appropriate
- if (aSelector == @selector(insertNewline:) && (!autoindentIgnoresEnter || [[NSApp currentEvent] keyCode] != 0x4C)) {
+ if (aSelector == @selector(insertNewline:)
+ && autoindentEnabled
+ && (!autoindentIgnoresEnter || [[NSApp currentEvent] keyCode] != 0x4C))
+ {
NSString *textViewString = [[self textStorage] string];
NSString *currentLine, *indentString = nil;
NSScanner *whitespaceScanner;
- NSUInteger lineStart, lineEnd;
+ NSRange currentLineRange;
// Extract the current line based on the text caret or selection start position
- [textViewString getLineStart:&lineStart end:NULL contentsEnd:&lineEnd forRange:NSMakeRange([self selectedRange].location, 0)];
- currentLine = [[NSString alloc] initWithString:[textViewString substringWithRange:NSMakeRange(lineStart, lineEnd - lineStart)]];
+ currentLineRange = [textViewString lineRangeForRange:NSMakeRange([self selectedRange].location, 0)];
+ currentLine = [[NSString alloc] initWithString:[textViewString substringWithRange:currentLineRange]];
// Scan all indentation characters on the line into a string
whitespaceScanner = [[NSScanner alloc] initWithString:currentLine];
@@ -283,6 +286,129 @@ YY_BUFFER_STATE yy_scan_string (const char *);
/*
+ * 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";
+ int 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;
+ int 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.
*/
- (NSArray *)completionsForPartialWordRange:(NSRange)charRange indexOfSelectedItem:(int *)index
diff --git a/Source/CustomQuery.h b/Source/CustomQuery.h
index 1abc5e57..908dd594 100644
--- a/Source/CustomQuery.h
+++ b/Source/CustomQuery.h
@@ -46,6 +46,12 @@
IBOutlet id copyQueryFavoriteButton;
IBOutlet id runSelectionButton;
IBOutlet id runAllButton;
+ IBOutlet NSMenuItem *runSelectionMenuItem;
+ IBOutlet NSMenuItem *shiftLeftMenuItem;
+ IBOutlet NSMenuItem *shiftRightMenuItem;
+ IBOutlet NSMenuItem *completionListMenuItem;
+ IBOutlet NSMenuItem *autoindentMenuItem;
+ IBOutlet NSMenuItem *autopairMenuItem;
NSArray *queryResult;
NSUserDefaults *prefs;
@@ -60,6 +66,7 @@
- (IBAction)chooseQueryFavorite:(id)sender;
- (IBAction)chooseQueryHistory:(id)sender;
- (IBAction)closeSheet:(id)sender;
+- (IBAction)gearMenuItemSelected:(id)sender;
// queryFavoritesSheet methods
- (IBAction)addQueryFavorite:(id)sender;
diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m
index 115c4d36..baa81170 100644
--- a/Source/CustomQuery.m
+++ b/Source/CustomQuery.m
@@ -155,6 +155,49 @@ closes the sheet
}
+/*
+ * Perform simple actions (which don't require their own method), triggered by selecting the appropriate menu item
+ * in the "gear" action menu displayed beneath the cusotm query view.
+ */
+- (IBAction)gearMenuItemSelected:(id)sender
+{
+
+ // "Shift Right" menu item - indent the selection with an additional tab.
+ if (sender == shiftRightMenuItem) {
+ [textView shiftSelectionRight];
+ }
+
+ // "Shift Left" menu item - un-indent the selection by one tab if possible.
+ if (sender == shiftLeftMenuItem) {
+ [textView shiftSelectionLeft];
+ }
+
+ // "Completion List" menu item - used to autocomplete. Uses a different shortcut to avoid the menu button flickering
+ // on normal autocomplete usage.
+ if (sender == completionListMenuItem) {
+ [textView complete:self];
+ }
+
+ // "Indent new lines" toggle
+ if (sender == autoindentMenuItem) {
+ BOOL enableAutoindent = ([autoindentMenuItem state] == NSOffState);
+ [prefs setBool:enableAutoindent forKey:@"CustomQueryAutoindent"];
+ [prefs synchronize];
+ [autoindentMenuItem setState:enableAutoindent?NSOnState:NSOffState];
+ [textView setAutoindent:enableAutoindent];
+ }
+
+ // "Auto-pair characters" toggle
+ if (sender == autopairMenuItem) {
+ BOOL enableAutopair = ([autopairMenuItem state] == NSOffState);
+ [prefs setBool:enableAutopair forKey:@"CustomQueryAutopair"];
+ [prefs synchronize];
+ [autopairMenuItem setState:enableAutopair?NSOnState:NSOffState];
+ [textView setAutopair:enableAutopair];
+ }
+}
+
+
#pragma mark -
#pragma mark queryFavoritesSheet methods
@@ -549,8 +592,10 @@ sets the connection (received from TableDocument) and makes things that have to
[textView setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
}
[textView setContinuousSpellCheckingEnabled:NO];
+ [autoindentMenuItem setState:([prefs boolForKey:@"CustomQueryAutoindent"]?NSOnState:NSOffState)];
[textView setAutoindent:[prefs boolForKey:@"CustomQueryAutoindent"]];
[textView setAutoindentIgnoresEnter:YES];
+ [autopairMenuItem setState:([prefs boolForKey:@"CustomQueryAutopair"]?NSOnState:NSOffState)];
[textView setAutopair:[prefs boolForKey:@"CustomQueryAutopair"]];
[queryFavoritesView registerForDraggedTypes:[NSArray arrayWithObjects:@"SequelProPasteboard", nil]];
while ( (column = [enumerator nextObject]) )
@@ -891,9 +936,10 @@ traps enter key and
// Ensure that the notification is from the custom query text view
if ( [aNotification object] != textView ) return;
- // If no text is selected, disable the button.
+ // If no text is selected, disable the button and action menu.
if ( [textView selectedRange].location == NSNotFound ) {
[runSelectionButton setEnabled:NO];
+ [runSelectionMenuItem setEnabled:NO];
return;
}
@@ -922,12 +968,15 @@ traps enter key and
) {
[runSelectionButton setTitle:NSLocalizedString(@"Run Current", @"Title of button to run current query in custom query view")];
+ [runSelectionMenuItem setTitle:NSLocalizedString(@"Run Current Query", @"Title of action menu item to run current query in custom query view")];
// If a valid query is present at the cursor position, enable the button
if ([self queryAtPosition:selectionPosition]) {
[runSelectionButton setEnabled:YES];
+ [runSelectionMenuItem setEnabled:YES];
} else {
[runSelectionButton setEnabled:NO];
+ [runSelectionMenuItem setEnabled:NO];
}
}
@@ -935,6 +984,8 @@ traps enter key and
} else {
[runSelectionButton setTitle:NSLocalizedString(@"Run Selection", @"Title of button to run selected text in custom query view")];
[runSelectionButton setEnabled:YES];
+ [runSelectionMenuItem setTitle:NSLocalizedString(@"Run Selected Text", @"Title of action menu item to run selected text in custom query view")];
+ [runSelectionMenuItem setEnabled:YES];
}
}
diff --git a/Source/SPStringAdditions.h b/Source/SPStringAdditions.h
index be999ba4..1d706666 100644
--- a/Source/SPStringAdditions.h
+++ b/Source/SPStringAdditions.h
@@ -28,6 +28,7 @@
+ (NSString *)stringForTimeInterval:(float)timeInterval;
- (NSString *)backtickQuotedString;
+- (NSArray *)lineRangesForRange:(NSRange)aRange;
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
- (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)set;
diff --git a/Source/SPStringAdditions.m b/Source/SPStringAdditions.m
index 74c98b4f..2c719f6c 100644
--- a/Source/SPStringAdditions.m
+++ b/Source/SPStringAdditions.m
@@ -136,6 +136,39 @@
return quotedString;
}
+// -------------------------------------------------------------------------------
+// lineRangesForRange
+//
+// Returns an array of serialised NSRanges, each representing a line within the string
+// which is at least partially covered by the NSRange supplied.
+// Each line includes the line termination character(s) for the line. As per
+// lineRangeForRange, lines are split by CR, LF, CRLF, U+2028 (Unicode line separator),
+// or U+2029 (Unicode paragraph separator).
+// -------------------------------------------------------------------------------
+- (NSArray *)lineRangesForRange:(NSRange)aRange
+{
+ NSMutableArray *lineRangesArray = [NSMutableArray array];
+ NSRange currentLineRange;
+
+ // Check that the range supplied is valid - if not return an empty array.
+ if (aRange.location == NSNotFound || aRange.location + aRange.length > [self length])
+ return lineRangesArray;
+
+ // Get the range of the first string covered by the specified range, and add it to the array
+ currentLineRange = [self lineRangeForRange:NSMakeRange(aRange.location, 0)];
+ [lineRangesArray addObject:NSStringFromRange(currentLineRange)];
+
+ // Loop through until the line end matches or surpasses the end of the specified range
+ while (currentLineRange.location + currentLineRange.length < aRange.location + aRange.length) {
+ currentLineRange = [self lineRangeForRange:NSMakeRange(currentLineRange.location + currentLineRange.length, 0)];
+ [lineRangesArray addObject:NSStringFromRange(currentLineRange)];
+ }
+
+ // Return the constructed array of ranges
+ return lineRangesArray;
+}
+
+
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
/*
* componentsSeparatedByCharactersInSet: