Skip to content

Commit

Permalink
Adding glorious TextMate 2 support.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr0grog committed Jul 29, 2012
1 parent 2ccc7ab commit 71f5171
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 36 deletions.
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ EditorConfig—TextMate PlugIn

This is a TextMate plug-in that provides support for [Editor Config](http://editorconfig.org/). It will cause TextMate to set its editing features (e.g. tab style and size) according to a `.editorconfig` file you include alongside your source code.

Download it now at: https://github.com/downloads/Mr0grog/editorconfig-textmate/editorconfig-textmate-0.1.tar.gz
Download it now at: https://github.com/downloads/Mr0grog/editorconfig-textmate/editorconfig-textmate-0.2.tar.gz

*Note: this plug-in does not support TextMate 2. See below for details.*
Works in both TextMate and TextMate 2 Alpha (as of build 9147).


Feature Support
Expand Down Expand Up @@ -42,7 +42,4 @@ Support for `end_of_line` is also needed, but I have to figure out exactly how t
TextMate 2
----------

Unfortunately, this plug-in probably won't be coming to TextMate 2 any time in the near future. There are a few issues:

- TM2 will only load plug-ins from Macromates (this is easy to work around, but is a clear sign you're doing something not-so-good).
- As of July 25, 2012, the (soft)tab and tab size settings in TM2 are actually hardcoded. You can modify them, but as soon as you close and re-open the file, you're back to the defaults. It's been this way for 6 months; I'm not sure it's changing any time soon.
While this plug-in does support the TextMate 2 Alpha, please keep in mind that alpha software is subject to great change. It could break in future releases of TextMate. If this happens, please file an issue: https://github.com/Mr0grog/editorconfig-textmate/issues
7 changes: 7 additions & 0 deletions editorconfig-textmate.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
CB47C5C315C05BD800E068C7 /* NSWindow+EditorConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = CB47C5C215C05BD800E068C7 /* NSWindow+EditorConfig.m */; };
CB47C5C715C05DCA00E068C7 /* ECConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = CB47C5C615C05DCA00E068C7 /* ECConstants.m */; };
CB47C5E815C0876400E068C7 /* LICENSE in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB47C5E315C0874C00E068C7 /* LICENSE */; };
CBFF551715C35A8E0075328B /* NSView+EditorConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = CBFF551615C35A8E0075328B /* NSView+EditorConfig.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -79,6 +80,8 @@
CB47C5DE15C0850A00E068C7 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
CB47C5E315C0874C00E068C7 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
CB47C5E415C0874C00E068C7 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = "<group>"; };
CBFF551515C35A8E0075328B /* NSView+EditorConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSView+EditorConfig.h"; sourceTree = "<group>"; };
CBFF551615C35A8E0075328B /* NSView+EditorConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSView+EditorConfig.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -191,6 +194,8 @@
CB47C5BF15C05ABD00E068C7 /* NSObject+ECSwizzle.m */,
CB47C5C115C05BD800E068C7 /* NSWindow+EditorConfig.h */,
CB47C5C215C05BD800E068C7 /* NSWindow+EditorConfig.m */,
CBFF551515C35A8E0075328B /* NSView+EditorConfig.h */,
CBFF551615C35A8E0075328B /* NSView+EditorConfig.m */,
);
path = additions;
sourceTree = "<group>";
Expand Down Expand Up @@ -294,6 +299,7 @@
CB47C5C015C05ABD00E068C7 /* NSObject+ECSwizzle.m in Sources */,
CB47C5C315C05BD800E068C7 /* NSWindow+EditorConfig.m in Sources */,
CB47C5C715C05DCA00E068C7 /* ECConstants.m in Sources */,
CBFF551715C35A8E0075328B /* NSView+EditorConfig.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -446,6 +452,7 @@
CB47C5D715C07CFC00E068C7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
2 changes: 1 addition & 1 deletion editorconfig-textmate/editorconfig-textmate-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>org.robbrackett.${PRODUCT_NAME:rfc1034identifier}</string>
<string>com.macromates.HACK.org.robbrackett.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
Expand Down
1 change: 1 addition & 0 deletions source/ECConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
#endif

extern NSString * const kECDocumentDidChange;
extern NSString * const kECTextViewDidSetDocument;
1 change: 1 addition & 0 deletions source/ECConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
#import "ECConstants.h"

NSString * const kECDocumentDidChange = @"ECDocumentDidChange";
NSString * const kECTextViewDidSetDocument = @"ECTextViewDidSetDocument";
39 changes: 31 additions & 8 deletions source/ECEditorConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
#import "editorconfig.h"
#import "ECConstants.h"
#import "NSWindow+EditorConfig.h"
#import "NSView+EditorConfig.h"
#import "ECEditorConfig.h"

@interface ECEditorConfig()

- (void)documentDidChange:(NSNotification *)notification;
- (void)windowDocumentDidChange:(NSNotification *)notification;
- (void)textViewDidSetDocument:(NSNotification *)notification;

- (void)updateWindow:(NSWindow *)window withConfig:(NSDictionary *)config;

Expand All @@ -27,13 +29,26 @@ @implementation ECEditorConfig

- (id)initWithPlugInController:(id <TMPlugInController>)aController {
if(self = [self init]) {
[NSWindow ec_init];

// Notification from our custom NSWindow for when a new document is shown in a window
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(documentDidChange:)
name:kECDocumentDidChange
object:nil];
if (aController.version < 2.0) {
// Make the window fire a notification when a new document is shown
[NSWindow ec_init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowDocumentDidChange:)
name:kECDocumentDidChange
object:nil];
}
else {
// In TM2, actually showing the document may happen much later than on NSWindow -setRepresentedFile:
// One example of this is if a window is not frontmost.
// Instead, we check the actual text view. This is more of a hack, but seems to be the only reliable way.
Class oakTextView = NSClassFromString(@"OakTextView");
[oakTextView ec_init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(textViewDidSetDocument:)
name:kECTextViewDidSetDocument
object:nil];
}
}
return self;
}
Expand All @@ -46,14 +61,22 @@ - (void)dealloc {

#pragma mark Notifications

- (void)documentDidChange:(NSNotification *)notification {
- (void)windowDocumentDidChange:(NSNotification *)notification {
NSDictionary *info = notification.userInfo;
DebugLog(@"File changed to: %@\n in: %@", [info objectForKey:@"fileName"], [notification object]);

NSDictionary *config = [self configForPath:[info objectForKey:@"fileName"]];
[self updateWindow:notification.object withConfig:config];
}

- (void)textViewDidSetDocument:(NSNotification *)notification {
NSDictionary *info = notification.userInfo;
DebugLog(@"Text View set to: %@\n in: %@", [info objectForKey:@"fileName"], [notification object]);

NSDictionary *config = [self configForPath:[info objectForKey:@"fileName"]];
[self updateWindow:[notification.object window] withConfig:config];
}


#pragma mark Utilities

Expand Down
18 changes: 18 additions & 0 deletions source/additions/NSView+EditorConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// NSView+EditorConfig.h
// editorconfig-textmate
//
// Created by Rob Brackett on 7/27/12.
// Copyright (c) 2012 Rob Brackett. All rights reserved.
//



@interface NSView (EditorConfig)

// This MUST be called at plugin initialization time; it swizzles things.
+ (void)ec_init;

- (void)ec_setDocument:(id)document;

@end
37 changes: 37 additions & 0 deletions source/additions/NSView+EditorConfig.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// NSView+EditorConfig.m
// editorconfig-textmate
//
// Created by Rob Brackett on 7/27/12.
// Copyright (c) 2012 Rob Brackett. All rights reserved.
//

#import "ECConstants.h"
#import "NSObject+ECSwizzle.h"
#import "NSView+EditorConfig.h"

@implementation NSView (EditorConfig)

+ (void)ec_init {
[self ec_swizzleMethod:@selector(setDocument:)
withMethod:@selector(ec_setDocument:)];
}

- (void)ec_setDocument:(id)document {
[self ec_setDocument:document];

NSString *fileName = self.window.representedFilename;
if (fileName == nil) {
fileName = [self.window.windowController representedFilename];
if (fileName == nil) {
fileName = @"";
}
}

NSDictionary *info = [NSDictionary dictionaryWithObject:fileName forKey:@"fileName"];
[[NSNotificationCenter defaultCenter] postNotificationName:kECTextViewDidSetDocument
object:self
userInfo:info];
}

@end
86 changes: 65 additions & 21 deletions source/additions/NSWindow+EditorConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,42 +34,86 @@ - (void)ec_setRepresentedFilename:(NSString *)fileName {
}
}

- (BOOL)ec_setSoftTabs:(BOOL)softTabs {
- (NSView *)ec_statusBar {
// windowController.documentView is an ivar, so we need to catch for NSUndefinedKeyException
@try {
return [self valueForKeyPath:@"windowController.documentView.statusBar"];
}
@catch (NSException *exception) {
return nil;
}
}

- (NSView *)ec_textView {
SEL textViewSelector = @selector(textView);
SEL softTabSelector = @selector(setSoftTabs:);

NSWindowController *controller = self.windowController;

// in TM1, we can get to the text view pretty easily from the window controller
if ([controller respondsToSelector:textViewSelector]) {
id textView = [controller performSelector:textViewSelector];
if ([textView respondsToSelector:softTabSelector]) {
IMP setter = [textView methodForSelector:softTabSelector];
setter(textView, softTabSelector, softTabs);
DebugLog(@"Setting softabs: %@", softTabs ? @"ON" : @"OFF");
return YES;
return [controller performSelector:textViewSelector];
}
// in TM2, it's on the documentView, which is only an ivar on the window controller
else {
@try {
return [controller valueForKey:@"textView"];
}
@catch (NSException *exception) {
return nil;
}
}
}

- (BOOL)ec_setSoftTabs:(BOOL)softTabs {
BOOL success = NO;
SEL softTabSelector = @selector(setSoftTabs:);

NSView *textView = self.ec_textView;
if ([textView respondsToSelector:softTabSelector]) {
IMP setter = [textView methodForSelector:softTabSelector];
setter(textView, softTabSelector, softTabs);
DebugLog(@"(text view) Setting softabs: %@", softTabs ? @"ON" : @"OFF");
success = YES;
}

return NO;
NSView *statusBar = self.ec_statusBar;
if ([statusBar respondsToSelector:softTabSelector]) {
IMP setter = [statusBar methodForSelector:softTabSelector];
setter(statusBar, softTabSelector, softTabs);
DebugLog(@"(status bar) Setting softabs: %@", softTabs ? @"ON" : @"OFF");
}
else {
success = NO;
}

return success;
}

- (BOOL)ec_setTabSize:(int)tabSize {
BOOL success = NO;

if (tabSize > 0) {
SEL textViewSelector = @selector(textView);
SEL tabSizeSelector = @selector(setTabSize:);

NSWindowController *controller = self.windowController;
if ([controller respondsToSelector:textViewSelector]) {
id textView = [controller performSelector:textViewSelector];
if ([textView respondsToSelector:tabSizeSelector]) {
IMP setter = [textView methodForSelector:tabSizeSelector];
setter(textView, tabSizeSelector, tabSize);
DebugLog(@"Setting tabsize: %d", tabSize);
return YES;
}
NSView *textView = self.ec_textView;
if ([textView respondsToSelector:tabSizeSelector]) {
IMP setter = [textView methodForSelector:tabSizeSelector];
setter(textView, tabSizeSelector, tabSize);
DebugLog(@"(text view) Setting tab size: %d", tabSize);
success = YES;
}

NSView *statusBar = self.ec_statusBar;
if ([statusBar respondsToSelector:tabSizeSelector]) {
IMP setter = [statusBar methodForSelector:tabSizeSelector];
setter(statusBar, tabSizeSelector, tabSize);
DebugLog(@"(status bar) Setting tab size: %d", tabSize);
}
else {
success = NO;
}
}

return NO;
return success;
}

@end

0 comments on commit 71f5171

Please sign in to comment.