Permalink
Browse files

🚧 Add EditorKit

  • Loading branch information...
1 parent 894202c commit ecada77ff2db4996f25aa3fc00dff50a8da40e74 @andreyvit andreyvit committed Oct 1, 2015
Oops, something went wrong.
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>$(CURRENT_PROJECT_VERSION)</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright Β© 2015 Andrey Tarantsov. All rights reserved.</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+</dict>
+</plist>
@@ -0,0 +1,5 @@
+
+#import "EKEditor.h"
+
+@interface Coda2Editor : EKEditor
+@end
@@ -0,0 +1,60 @@
+#import "EKCoda2Editor.h"
+#import "EKJumpRequest.h"
+@import ExpressiveCocoa;
+
+
+
+static NSString *CodaJumpScript =
+ @"on jump(charOffset)\n"
+ @" tell application \"Coda 2\"\n"
+ @" set selected range of selected split of selected tab of front window to {charOffset, 0}\n"
+ @" end tell\n"
+ @"end jump\n";
+
+
+
+@implementation Coda2Editor
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ self.identifier = @"com.panic.Coda2";
+ self.cocoaBundleId = @"com.panic.Coda2";
+ self.displayName = @"Coda 2";
+ self.defaultPriority = 3;
+ }
+ return self;
+}
+
+- (void)doUpdateStateInBackground {
+ NSURL *url = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:self.cocoaBundleId];
+ if (!url)
+ return [self updateState:EKEditorStateNotFound error:nil];
+
+ if ([[NSRunningApplication runningApplicationsWithBundleIdentifier:self.cocoaBundleId] count] > 0)
+ return [self updateState:EKEditorStateRunning error:nil];
+
+ [self updateState:EKEditorStateFound error:nil];
+}
+
+- (void)jumpWithRequest:(EKJumpRequest *)request completionHandler:(void(^)(NSError *error))completionHandler {
+ if (![[NSWorkspace sharedWorkspace] openURLs:@[request.fileURL] withAppBundleIdentifier:self.cocoaBundleId options:0 additionalEventParamDescriptor:nil launchIdentifiers:NULL])
+ return completionHandler([NSError errorWithDomain:EKErrorDomain code:EKErrorCodeLaunchFailed userInfo:nil]);
+
+ if (request.line != EKJumpRequestValueUnknown) {
+ NSError *error;
+ int offset = [request computeLinearOffsetWithError:&error];
+ if (offset == EKJumpRequestValueUnknown)
+ return completionHandler([NSError errorWithDomain:EKErrorDomain code:EKErrorCodeJumpFailed userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to jump to line in Coda 2: cannot read file, error: %@", error.localizedDescription], NSUnderlyingErrorKey: error}]);
+
+ NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:CodaJumpScript];
+
+ NSDictionary *errors = [NSDictionary dictionary];
+ if (![appleScript executeHandlerNamed:@"jump" withArguments:@[[NSAppleEventDescriptor descriptorWithInt32:offset]] error:&errors])
+ return completionHandler([NSError errorWithDomain:EKErrorDomain code:EKErrorCodeJumpFailed userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to jump to line in Coda 2: cannot read file, errors: %@", errors]}]);
+ }
+
+ return completionHandler(nil);
+}
+
+@end
@@ -0,0 +1,45 @@
+@import Foundation;
+#import "EKGlobals.h"
+
+
+@class EKJumpRequest;
+
+
+typedef enum {
+ EKEditorStateNotFound,
+ EKEditorStateBroken,
+ EKEditorStateFound,
+ EKEditorStateRunning,
+} EKEditorState;
+
+
+@interface EKEditor : NSObject
+
+@property(nonatomic, copy) NSString *identifier;
+@property(nonatomic, copy) NSString *displayName;
+@property(nonatomic, copy) NSString *cocoaBundleId;
+
+@property(nonatomic, readonly, assign) EKEditorState state;
+@property(nonatomic, readonly, assign, getter=isStateStale) BOOL stateStale;
+@property(nonatomic, readonly, assign, getter=isRunning) BOOL running;
+
+- (void)updateStateSoon;
+- (BOOL)jumpToFile:(NSString *)file line:(NSInteger)line;
+- (void)jumpWithRequest:(EKJumpRequest *)request completionHandler:(void(^)(NSError *error))completionHandler;
+
+// override point
+- (void)doUpdateStateInBackground;
+
+// API for subclasses (call on main queue!)
+- (void)updateState:(EKEditorState)state error:(NSError *)error;
+
+@property(nonatomic, assign) NSInteger mruPosition; // 0..500 or NSNotFound
+@property(nonatomic, assign) NSInteger defaultPriority; // -1 = legacy, 0 = default, 1 = modern, 2 = modern preferred (aka Sublime), 3 = specialized (Coda)
+@property(nonatomic, readonly) NSInteger effectivePriority;
+
+@end
+
+
+@interface InternalEditor : EKEditor
+
+@end
@@ -0,0 +1,103 @@
+
+#import "EKEditor.h"
+#import "EKJumpRequest.h"
+
+
+static NSString *EditorStateStrings[] = {
+ @"EditorStateNotFound",
+ @"EditorStateBroken",
+ @"EditorStateFound",
+ @"EditorStateRunning",
+};
+
+
+@interface EKEditor ()
+
+@property(nonatomic, assign) EKEditorState state;
+@property(nonatomic, assign, getter=isStateStale) BOOL stateStale;
+
+@end
+
+
+@implementation EKEditor
+
+@synthesize identifier = _identifier;
+@synthesize displayName = _displayName;
+@synthesize cocoaBundleId = _cocoaBundleId;
+
+@synthesize state = _state;
+@synthesize stateStale = _stateStale;
+
+@synthesize mruPosition = _mruPosition;
+@synthesize defaultPriority = _defaultPriority;
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ _mruPosition = NSNotFound;
+
+ [self updateStateSoon];
+ }
+ return self;
+}
+
+- (void)jumpWithRequest:(EKJumpRequest *)request completionHandler:(void(^)(NSError *error))completionHandler {
+ abort(); // must override
+}
+
+- (BOOL)jumpToFile:(NSString *)file line:(NSInteger)line {
+ [self jumpWithRequest:[[EKJumpRequest alloc] initWithFileURL:[NSURL fileURLWithPath:file] line:(int)(line > 0 ? line : EKJumpRequestValueUnknown) column:EKJumpRequestValueUnknown] completionHandler:^(NSError *error) {
+ if (error)
+ NSLog(@"Failed to jump to the error position: %@", error.localizedDescription);
+ }];
+ return YES;
+}
+
+- (BOOL)isRunning {
+ return self.state == EKEditorStateRunning;
+}
+
+- (void)setAttributesDictionary:(NSDictionary *)attributes {
+ self.identifier = attributes[@"id"];
+ self.displayName = attributes[@"name"] ?: self.displayName;
+
+ [self updateStateSoon];
+}
+
+- (void)updateStateSoon {
+ if (self.stateStale)
+ return;
+ self.stateStale = YES;
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
+ [self doUpdateStateInBackground];
+ });
+}
+
+- (void)updateState:(EKEditorState)state error:(NSError *)error {
+ NSLog(@"Editor '%@' state is %@, error = %@", self.displayName, EditorStateStrings[state], [error localizedDescription]);
+ self.state = state;
+ self.stateStale = NO;
+}
+
+- (void)doUpdateStateInBackground {
+ abort(); // must override
+}
+
+- (NSInteger)effectivePriority {
+ NSInteger base = 0;
+ if (self.state == EKEditorStateRunning)
+ base = 10000;
+ else if (self.state != EKEditorStateFound)
+ base = -10000;
+
+ if (_mruPosition != NSNotFound)
+ return base + 1000 - _mruPosition;
+ else
+ return base + _defaultPriority;
+}
+
+@end
+
+@implementation InternalEditor
+
+@end
@@ -0,0 +1,9 @@
+
+#import <Foundation/Foundation.h>
+
+extern NSString *EKErrorDomain;
+
+typedef enum {
+ EKErrorCodeLaunchFailed = 1,
+ EKErrorCodeJumpFailed = 2,
+} EKErrorCode;
@@ -0,0 +1,4 @@
+
+#import "EKGlobals.h"
+
+NSString *EKErrorDomain = @"EditorKitErrorDomain";
@@ -0,0 +1,22 @@
+
+#import <Foundation/Foundation.h>
+
+
+enum {
+ EKJumpRequestValueUnknown = -1
+};
+
+
+@interface EKJumpRequest : NSObject
+
+- (id)initWithFileURL:(NSURL *)fileURL line:(int)line column:(int)column;
+
+@property(nonatomic, strong) NSURL *fileURL;
+@property(nonatomic, assign) int line;
+@property(nonatomic, assign) int column;
+
+- (NSString *)componentsJoinedByString:(NSString *)separator;
+
+- (int)computeLinearOffsetWithError:(NSError **)outError;
+
+@end
@@ -0,0 +1,72 @@
+
+#import "EKJumpRequest.h"
+
+@implementation EKJumpRequest
+
+@synthesize fileURL = _fileURL;
+@synthesize line = _line;
+@synthesize column = _column;
+
+- (id)initWithFileURL:(NSURL*)fileURL line:(int)line column:(int)column {
+ self = [super init];
+ if (self) {
+ _fileURL = fileURL;
+ _line = line;
+ _column = column;
+ }
+ return self;
+}
+
+- (NSString *)componentsJoinedByString:(NSString *)separator {
+ NSMutableArray *array = [NSMutableArray arrayWithObject:self.fileURL.path];
+ if (self.line != EKJumpRequestValueUnknown) {
+ [array addObject:[NSString stringWithFormat:@"%d", self.line]];
+
+ if (self.column != EKJumpRequestValueUnknown) {
+ [array addObject:[NSString stringWithFormat:@"%d", self.column]];
+ }
+ }
+ return [array componentsJoinedByString:separator];
+}
+
+- (NSString *)description {
+ return [self componentsJoinedByString:@":"];
+}
+
+- (int)computeLinearOffsetWithError:(NSError **)outError {
+ NSError *error;
+ NSString *content = [NSString stringWithContentsOfURL:self.fileURL usedEncoding:NULL error:&error];
+ if (!content) {
+ if (outError)
+ *outError = error;
+ return EKJumpRequestValueUnknown;
+ }
+
+ int line = self.line;
+ int column = self.column;
+ __block int curline = 0;
+ __block int offset = -1;
+ __block int lastOffset = -1;
+ [content enumerateSubstringsInRange:NSMakeRange(0, content.length) options:NSStringEnumerationByLines|NSStringEnumerationSubstringNotRequired usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
+ ++curline;
+ if (curline == line) {
+ offset = (int)substringRange.location;
+ if (column != EKJumpRequestValueUnknown) {
+ offset += MIN((int)substringRange.length, (column - 1));
+ }
+ *stop = YES;
+ }
+ lastOffset = (int)substringRange.location;
+ }];
+
+ if (offset == -1) {
+ if (lastOffset >= 0)
+ offset = lastOffset; // if the specified line does not exist (the number is too large), jump to the start of the last line
+ else
+ offset = 0; // oops, no lines in the file at all
+ }
+
+ return offset;
+}
+
+@end
@@ -0,0 +1,6 @@
+
+#import <Foundation/Foundation.h>
+#import "EKEditor.h"
+
+@interface SublimeText2Editor : EKEditor
+@end
Oops, something went wrong.

0 comments on commit ecada77

Please sign in to comment.