mirror of
https://github.com/textmate/textmate.git
synced 2026-01-21 04:38:13 -05:00
committed by
Allan Odgaard
parent
d021d97b43
commit
37e98655ec
@@ -2,6 +2,7 @@
|
||||
#import <OakFoundation/OakHistoryList.h>
|
||||
#import <OakFoundation/OakFoundation.h>
|
||||
#import <OakAppKit/OakUIConstructionFunctions.h>
|
||||
#import <OakTextView/OakTextView.h>
|
||||
|
||||
@interface OakRunCommandWindowController () <NSWindowDelegate>
|
||||
@property (nonatomic) NSTextField* commandLabel;
|
||||
@@ -13,6 +14,7 @@
|
||||
@property (nonatomic) NSObjectController* objectController;
|
||||
@property (nonatomic) OakHistoryList* commandHistoryList;
|
||||
@property (nonatomic) NSMutableArray* myConstraints;
|
||||
@property (nonatomic) output::type outputType;
|
||||
@end
|
||||
|
||||
#ifndef CONSTRAINT
|
||||
@@ -30,6 +32,7 @@
|
||||
{
|
||||
if((self = [super initWithWindow:[[NSPanel alloc] initWithContentRect:NSZeroRect styleMask:(NSTitledWindowMask|NSClosableWindowMask|NSResizableWindowMask|NSMiniaturizableWindowMask) backing:NSBackingStoreBuffered defer:NO]]))
|
||||
{
|
||||
self.outputType = output::replace_input;
|
||||
self.myConstraints = [NSMutableArray new];
|
||||
|
||||
self.commandLabel = OakCreateLabel(@"Command:");
|
||||
@@ -39,8 +42,21 @@
|
||||
self.executeButton = OakCreateButton(@"Execute");
|
||||
self.cancelButton = OakCreateButton(@"Cancel");
|
||||
|
||||
for(NSString* title in @[ @"Replace Input", @"Insert After Input" ])
|
||||
[self.resultPopUpButton addItemWithTitle:title];
|
||||
NSDictionary* outputOptions = @{
|
||||
@(output::replace_input) : @"Replace Input",
|
||||
@(output::after_input) : @"Insert After Input",
|
||||
// @(output::new_window) : @"New Window",
|
||||
@(output::tool_tip) : @"Tool Tip",
|
||||
};
|
||||
|
||||
NSMenu* menu = [self.resultPopUpButton menu];
|
||||
[menu removeAllItems];
|
||||
char key = '0';
|
||||
for(NSNumber* type in outputOptions)
|
||||
[[menu addItemWithTitle:outputOptions[type] action:@selector(takeOutputTypeFrom:) keyEquivalent:[NSString stringWithFormat:@"%c", ++key]] setTag:[type intValue]];
|
||||
|
||||
[self.resultLabel setAlignment:NSRightTextAlignment];
|
||||
[self.commandLabel setAlignment:NSRightTextAlignment];
|
||||
|
||||
self.executeButton.action = @selector(execute:);
|
||||
self.cancelButton.action = @selector(cancel:);
|
||||
@@ -54,6 +70,9 @@
|
||||
[self.commandComboBox bind:NSValueBinding toObject:_objectController withKeyPath:@"content.commandHistoryList.head" options:nil];
|
||||
[self.commandComboBox bind:NSContentValuesBinding toObject:_objectController withKeyPath:@"content.commandHistoryList.list" options:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(commandChanged:) name:NSControlTextDidChangeNotification object:self.commandComboBox];
|
||||
[self commandChanged:nil];
|
||||
|
||||
NSDictionary* views = @{
|
||||
@"commandLabel" : self.commandLabel,
|
||||
@"command" : self.commandComboBox,
|
||||
@@ -70,8 +89,8 @@
|
||||
[contentView addSubview:view];
|
||||
}
|
||||
|
||||
CONSTRAINT(@"H:|-(>=20,==20@75)-[commandLabel]-[command(>=250)]-|", NSLayoutFormatAlignAllBaseline);
|
||||
CONSTRAINT(@"H:|-(>=20,==20@75)-[resultLabel]-[result]-(>=20)-|", NSLayoutFormatAlignAllBaseline);
|
||||
CONSTRAINT(@"H:|-[commandLabel]-[command(>=250)]-|", NSLayoutFormatAlignAllBaseline);
|
||||
CONSTRAINT(@"H:|-[resultLabel(==commandLabel)]-[result]-(>=20)-|", NSLayoutFormatAlignAllBaseline);
|
||||
CONSTRAINT(@"H:|-(>=20)-[cancel]-[execute]-|", NSLayoutFormatAlignAllBaseline);
|
||||
CONSTRAINT(@"V:|-[command]-[result]", NSLayoutFormatAlignAllLeft);
|
||||
CONSTRAINT(@"V:[result]-[execute]-|", 0);
|
||||
@@ -88,22 +107,30 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)takeOutputTypeFrom:(id)sender
|
||||
{
|
||||
self.outputType = (output::type)[sender tag];
|
||||
}
|
||||
|
||||
- (void)commandChanged:(NSNotification*)notification
|
||||
{
|
||||
self.executeButton.enabled = NSNotEmptyString([self.commandComboBox.stringValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]);
|
||||
}
|
||||
|
||||
- (IBAction)execute:(id)sender
|
||||
{
|
||||
NSLog(@"%s", sel_getName(_cmd));
|
||||
if(![self.objectController commitEditing])
|
||||
return;
|
||||
|
||||
NSString* command = self.commandComboBox.stringValue;
|
||||
if(id textView = [NSApp targetForAction:@selector(filterDocumentThroughCommand:input:output:)])
|
||||
[textView filterDocumentThroughCommand:command input:input::selection output:self.outputType];
|
||||
|
||||
if([self.objectController commitEditing])
|
||||
{
|
||||
NSString* command = self.commandComboBox.stringValue;
|
||||
if(NSNotEmptyString(command))
|
||||
NSLog(@"%s run ‘%@’", sel_getName(_cmd), command);
|
||||
}
|
||||
[self close];
|
||||
}
|
||||
|
||||
- (IBAction)cancel:(id)sender
|
||||
{
|
||||
NSLog(@"%s", sel_getName(_cmd));
|
||||
[self close];
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -120,6 +120,8 @@ PUBLIC @interface OakTextView : OakView <NSTextInput, NSTextFieldDelegate>
|
||||
- (GVLineRecord const&)lineRecordForPosition:(CGFloat)yPos;
|
||||
- (GVLineRecord const&)lineFragmentForLine:(NSUInteger)aLine column:(NSUInteger)aColumn;
|
||||
|
||||
- (BOOL)filterDocumentThroughCommand:(NSString*)commandString input:(input::type)inputUnit output:(output::type)outputUnit;
|
||||
|
||||
- (NSPoint)positionForWindowUnderCaret;
|
||||
- (scope::context_t const&)scopeContext;
|
||||
- (folding_state_t)foldingStateForLine:(NSUInteger)lineNumber;
|
||||
|
||||
@@ -27,9 +27,12 @@
|
||||
#import <ns/spellcheck.h>
|
||||
#import <text/classification.h>
|
||||
#import <text/format.h>
|
||||
#import <text/trim.h>
|
||||
#import <text/utf16.h>
|
||||
#import <text/utf8.h>
|
||||
#import <oak/debug.h>
|
||||
#import <editor/write.h>
|
||||
#import <io/exec.h>
|
||||
|
||||
OAK_DEBUG_VAR(OakTextView_TextInput);
|
||||
OAK_DEBUG_VAR(OakTextView_Accessibility);
|
||||
@@ -2275,6 +2278,68 @@ static char const* kOakMenuItemTitle = "OakMenuItemTitle";
|
||||
return res = GVLineRecord(record.line, record.softline, record.top, record.bottom, record.baseline);
|
||||
}
|
||||
|
||||
- (BOOL)filterDocumentThroughCommand:(NSString*)commandString input:(input::type)inputUnit output:(output::type)outputUnit
|
||||
{
|
||||
auto environment = editor->variables(std::map<std::string, std::string>(), to_s([self scopeAttributes]));
|
||||
if(io::process_t process = io::spawn(std::vector<std::string>{ "/bin/sh", "-c", to_s(commandString) }, environment))
|
||||
{
|
||||
bool inputWasSelection = false;
|
||||
text::range_t inputRange = ng::write_unit_to_fd(document->buffer(), editor->ranges().last(), document->buffer().indent().tab_size(), process.in, inputUnit, input::entire_document, input_format::text, scope::selector_t(), environment, &inputWasSelection);
|
||||
close(process.in);
|
||||
|
||||
__block std::string output, error;
|
||||
__block bool success = false;
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
|
||||
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
io::exhaust_fd(process.out, &output);
|
||||
});
|
||||
|
||||
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
io::exhaust_fd(process.err, &error);
|
||||
});
|
||||
|
||||
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
int status = 0;
|
||||
if(waitpid(process.pid, &status, 0) != process.pid)
|
||||
perror("waitpid");
|
||||
else if(!WIFEXITED(status))
|
||||
NSLog(@"*** abnormal exit (%d) from ‘%@’\n", status, commandString);
|
||||
else if(WEXITSTATUS(status) != 0)
|
||||
NSLog(@"*** exit code %d from ‘%@’\n", WEXITSTATUS(status), commandString);
|
||||
else
|
||||
success = true;
|
||||
});
|
||||
|
||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||
dispatch_release(group);
|
||||
|
||||
error = text::trim(error);
|
||||
if(!error.empty())
|
||||
{
|
||||
OakShowToolTip([NSString stringWithCxxString:error], [self positionForWindowUnderCaret]);
|
||||
if(success && output.empty())
|
||||
return YES;
|
||||
}
|
||||
|
||||
if(success)
|
||||
{
|
||||
if(outputUnit == output::tool_tip)
|
||||
{
|
||||
OakShowToolTip([NSString stringWithCxxString:text::trim(output)], [self positionForWindowUnderCaret]);
|
||||
}
|
||||
else
|
||||
{
|
||||
AUTO_REFRESH;
|
||||
editor->handle_result(output, outputUnit, output_format::text, output_caret::after_output, inputRange, environment);
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
// ===================
|
||||
// = Macro Recording =
|
||||
// ===================
|
||||
|
||||
Reference in New Issue
Block a user