From 552460a62616cee32290f0f9204b398a80a4fd2b Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Sun, 17 Nov 2013 15:22:31 +0100 Subject: [PATCH 01/44] Make XVimBuffer a reality This object will now be the main entry point to manipulate the content of the textStorage, so that we don't have to extend the NSTextStorage with categories. XVimBuffer is now created each time we learn about a new Document that responds to -textStorage which XCode documents do. XVimBuffer also knows about the undo manager, and is used to be the target of the specific vim action we mean to undo, which means that undoing the cursor positions won't crash anymore (Layout managers and textviews come and go, and it's not correct to make them the target of an undo operation). --- XVim.xcodeproj/project.pbxproj | 8 +++++ XVim/NSTextStorage+VimOperation.m | 1 + XVim/NSTextView+VimOperation.h | 2 +- XVim/NSTextView+VimOperation.m | 60 ++++++++++++++----------------- XVim/XVim.h | 4 +-- XVim/XVim.m | 33 ++++++++++------- XVim/XVimBuffer.h | 42 +++++++++++++++++++++- XVim/XVimBuffer.m | 59 ++++++++++++++++++++++++++++++ XVim/XVimStatusLine.m | 15 ++++---- XVim/XVimTextStoring.h | 1 - XVim/XVimUndo.h | 25 +++++++++++++ XVim/XVimUndo.m | 48 +++++++++++++++++++++++++ XVim/XVimWindow.m | 6 ++-- 13 files changed, 245 insertions(+), 59 deletions(-) create mode 100644 XVim/XVimUndo.h create mode 100644 XVim/XVimUndo.m diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 25410201..1f805b47 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 6E2B332C1836E42F00EFE4E2 /* XVimBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2B332B1836E42F00EFE4E2 /* XVimBuffer.m */; }; 6E2B332D1836E42F00EFE4E2 /* XVimBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2B332B1836E42F00EFE4E2 /* XVimBuffer.m */; }; 6E2B33341836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2B33331836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m */; }; + 6E6865D618390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; + 6E6865D718390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; A2126B7516FB30B0000BE21C /* XVimMark.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7416FB30B0000BE21C /* XVimMark.m */; }; A2126B7816FB4806000BE21C /* XVimMarks.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7716FB4806000BE21C /* XVimMarks.m */; }; A216F3A1156560FE00AD2529 /* IDEEditorArea+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A216F3A0156560FE00AD2529 /* IDEEditorArea+XVim.m */; }; @@ -209,6 +211,8 @@ 6E2B332E1836E47500EFE4E2 /* XVimTextStoring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTextStoring.h; path = XVim/XVimTextStoring.h; sourceTree = SOURCE_ROOT; }; 6E2B33321836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "DVTTextStorage+XVimTextStoring.h"; path = "XVim/DVTTextStorage+XVimTextStoring.h"; sourceTree = SOURCE_ROOT; }; 6E2B33331836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DVTTextStorage+XVimTextStoring.m"; path = "XVim/DVTTextStorage+XVimTextStoring.m"; sourceTree = SOURCE_ROOT; }; + 6E6865D418390702008D5FBB /* XVimUndo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimUndo.h; path = XVim/XVimUndo.h; sourceTree = SOURCE_ROOT; }; + 6E6865D518390702008D5FBB /* XVimUndo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimUndo.m; path = XVim/XVimUndo.m; sourceTree = SOURCE_ROOT; }; 6EAFF976183777A6003EADAE /* XVimStringBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimStringBuffer.h; path = XVim/XVimStringBuffer.h; sourceTree = SOURCE_ROOT; }; A2126B7316FB30B0000BE21C /* XVimMark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimMark.h; path = XVim/XVimMark.h; sourceTree = SOURCE_ROOT; }; A2126B7416FB30B0000BE21C /* XVimMark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimMark.m; path = XVim/XVimMark.m; sourceTree = SOURCE_ROOT; }; @@ -421,6 +425,8 @@ 6E2B332E1836E47500EFE4E2 /* XVimTextStoring.h */, A26E5F6217A6BCAF0087C9B6 /* NSTextStorage+VimOperation.h */, A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */, + 6E6865D418390702008D5FBB /* XVimUndo.h */, + 6E6865D518390702008D5FBB /* XVimUndo.m */, 6E2B332A1836E42F00EFE4E2 /* XVimBuffer.h */, 6E2B332B1836E42F00EFE4E2 /* XVimBuffer.m */, ); @@ -916,6 +922,7 @@ A28F425817EEDBC200A3F7AE /* NSEvent+VimHelper.m in Sources */, A28F425917EEDBC200A3F7AE /* XVimMotion.m in Sources */, A28F425A17EEDBC200A3F7AE /* XVimDebug.m in Sources */, + 6E6865D718390702008D5FBB /* XVimUndo.m in Sources */, A28F425B17EEDBC200A3F7AE /* Utils.m in Sources */, A28F425C17EEDBC200A3F7AE /* XVimMark.m in Sources */, A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */, @@ -1016,6 +1023,7 @@ A250950D1787B3340090F857 /* XVimTester+Register.m in Sources */, A2AFD7341790F300009B442B /* XVimRecordingEvaluator.m in Sources */, A2AFD73717914ACE009B442B /* XVimTester+Recording.m in Sources */, + 6E6865D618390702008D5FBB /* XVimUndo.m in Sources */, A2771E6B179EF520003B621E /* XVimTester+Issues.m in Sources */, A26E5F6417A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m in Sources */, A2640CC917ACE651003D197D /* NSTextView+VimOperation.m in Sources */, diff --git a/XVim/NSTextStorage+VimOperation.m b/XVim/NSTextStorage+VimOperation.m index 7e604f72..d9bbdd5b 100644 --- a/XVim/NSTextStorage+VimOperation.m +++ b/XVim/NSTextStorage+VimOperation.m @@ -10,6 +10,7 @@ #import "NSString+VimHelper.h" #import "NSTextStorage+VimOperation.h" #import "Logger.h" +#import "XVimUndo.h" @implementation NSTextStorage (VimOperation) diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h index f8730739..40a2082d 100644 --- a/XVim/NSTextView+VimOperation.h +++ b/XVim/NSTextView+VimOperation.h @@ -150,7 +150,7 @@ - (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt; - (void)xvim_clearHighlightText; - (NSRange)xvim_currentWord:(MOTION_OPTION)opt; -- (NSRange)xvim_currentNumber; +- (NSRange)xvim_numberAtIndex:(NSUInteger)index; #pragma mark Searching positions // TODO: Thses method should be internal. Create abstracted interface to achieve the operation uses these methods. diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index 821dd9b3..adc7cc1e 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -23,6 +23,8 @@ #import "NSTextView+VimOperation.h" #import "NSTextStorage+VimOperation.h" #import "Logger.h" +#import "XVimUndo.h" +#import "XVimBuffer.h" #define LOG_STATE() TRACE_LOG(@"mode:%d length:%d cursor:%d ip:%d begin:%d line:%d column:%d preservedColumn:%d", \ self.selectionMode, \ @@ -70,7 +72,7 @@ - (NSUInteger)xvim_lineNumberFromTop:(NSUInteger)count; - (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward; - (void)xvim_swapCaseForRange:(NSRange)range; - (void)xvim_registerInsertionPointForUndo; -- (void)xvim_registerPositionForUndo:(NSUInteger)pos; +- (void)xvim_registerPositionForUndo:(XVimPosition)pos; @end @implementation NSTextView (VimOperation) @@ -1228,7 +1230,8 @@ - (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right [undoManager beginUndoGrouping]; if (!blockMode) { - [self xvim_registerPositionForUndo:[ts xvim_firstNonblankInLineAtIndex:pos allowEOL:YES]]; + NSUInteger col = [ts xvim_columnOfIndex:[ts xvim_firstNonblankInLineAtIndex:pos allowEOL:YES]]; + [self xvim_registerPositionForUndo:XVimMakePosition(lines.begin, col)]; } if (right) { @@ -1404,22 +1407,22 @@ - (BOOL)xvim_incrementNumber:(int64_t)offset{ NSUInteger ip = self.insertionPoint; NSRange range; - range = [self xvim_currentNumber]; + range = [self xvim_numberAtIndex:ip]; if (range.location == NSNotFound) { NSUInteger pos = [self.textStorage xvim_nextDigitInLine:ip]; if (pos == NSNotFound) { return NO; } - self.insertionPoint = pos; - range = [self xvim_currentNumber]; + range = [self xvim_numberAtIndex:pos]; if (range.location == NSNotFound) { // should not happen self.insertionPoint = ip; return NO; } + ip = pos; } - [self xvim_registerPositionForUndo:ip]; + [self xvim_registerInsertionPointForUndo]; const char *s = [[self.xvim_string substringWithRange:range] UTF8String]; NSString *repl; @@ -1872,22 +1875,22 @@ - (NSRange)xvim_currentWord:(MOTION_OPTION)opt{ return [self.textStorage currentWord:self.insertionPoint count:1 option:opt|TEXTOBJECT_INNER]; } -- (NSRange)xvim_currentNumber{ - NSUInteger insertPoint = self.insertionPoint; +- (NSRange)xvim_numberAtIndex:(NSUInteger)index +{ NSUInteger n_start, n_end; NSUInteger x_start, x_end; NSString *s = self.xvim_string; unichar c; BOOL isOctal = YES; - n_start = insertPoint; + n_start = index; while (n_start > 0 && [s isDigit:n_start - 1]) { if (![s isOctDigit:n_start]) { isOctal = NO; } n_start--; } - n_end = insertPoint; + n_end = index; while (n_end < s.length && [s isDigit:n_end]) { if (![s isOctDigit:n_end]) { isOctal = NO; @@ -1912,7 +1915,7 @@ - (NSRange)xvim_currentNumber{ do { end++; } while (end < s.length && [s isHexDigit:end]); - if (insertPoint < end && end - x_start > 2) { + if (index < end && end - x_start > 2) { // YAY it's hex for real!!! return NSMakeRange(x_start, end - x_start); } @@ -1920,13 +1923,13 @@ - (NSRange)xvim_currentNumber{ } // case 2: check whether we're after 0x - if (insertPoint < x_end && x_end - x_start >= 1) { + if (index < x_end && x_end - x_start >= 1) { if (x_start >= 2 && [s characterAtIndex:x_start - 1] == 'x' && [s characterAtIndex:x_start - 2] == '0') { return NSMakeRange(x_start - 2, x_end - x_start + 2); } } - if (insertPoint == n_end || n_start - n_end == 0) { + if (index == n_end || n_start - n_end == 0) { return NSMakeRange(NSNotFound, 0); } @@ -2478,29 +2481,20 @@ - (void)xvim_swapCaseForRange:(NSRange)range { [substring release]; } -- (void)xvim_registerPositionForUndo:(NSUInteger)pos{ - [[self undoManager] registerUndoWithTarget:self selector:@selector(xvim_undoCursorPos:) object:[NSNumber numberWithUnsignedInteger:pos]]; -} - -- (void)xvim_registerInsertionPointForUndo{ - [self xvim_registerPositionForUndo:self.selectedRange.location]; -} +- (void)xvim_registerPositionForUndo:(XVimPosition)pos +{ + XVimBuffer *buffer = self.textStorage.xvim_buffer; + NSUndoManager *undoManager = buffer.undoManager; + XVimUndoCursorPositionOperation *op; -- (void)xvim_undoCursorPos:(NSNumber*)num{ - [self xvim_moveCursor:[num unsignedIntegerValue] preserveColumn:NO]; - [self xvim_syncState]; -} -/* May be used later -- (void)hideCompletions { - [[[self xview] completionController] hideCompletions]; + op = [[XVimUndoCursorPositionOperation alloc] initWithPosition:pos undoManager:undoManager]; + [undoManager registerUndoWithTarget:buffer selector:@selector(undoRedo:) object:op]; + [op release]; } -- (void)selectNextPlaceholder { - [[self xview] selectNextPlaceholder:self]; +- (void)xvim_registerInsertionPointForUndo +{ + [self xvim_registerPositionForUndo:self.insertionPosition]; } -- (void)selectPreviousPlaceholder { - [[self xview] selectPreviousPlaceholder:self]; -} - */ @end diff --git a/XVim/XVim.h b/XVim/XVim.h index 0319ebe6..f29acd1f 100644 --- a/XVim/XVim.h +++ b/XVim/XVim.h @@ -27,8 +27,8 @@ @class XVimTester; -extern NSString * const XVimDocumentChangedNotification; -extern NSString * const XVimDocumentPathKey; +extern NSString * const XVimBufferChangedNotification; +extern NSString * const XVimBufferKey; @interface XVim : NSObject diff --git a/XVim/XVim.m b/XVim/XVim.m index 8f878459..55e044b6 100644 --- a/XVim/XVim.m +++ b/XVim/XVim.m @@ -22,6 +22,7 @@ // #import "XVim.h" +#import "XVimBuffer.h" #import "Logger.h" #import "XVimSearch.h" #import "XVimExCommand.h" @@ -40,8 +41,8 @@ #import "IDEKit.h" #import "objc/runtime.h" -NSString * const XVimDocumentChangedNotification = @"XVimDocumentChangedNotification"; -NSString * const XVimDocumentPathKey = @"XVimDocumentPathKey"; +NSString * const XVimBufferChangedNotification = @"XVimBufferChangedNotification"; +NSString * const XVimBufferKey = @"XVimBufferKey"; @interface XVim() { XVimHistoryHandler *_exCommandHistory; @@ -233,26 +234,34 @@ -(void)dealloc{ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if( [keyPath isEqualToString:@"debug"]) { if( [[XVim instance] options].debug ){ - NSString *homeDir = NSHomeDirectoryForUser(NSUserName()); - NSString *logPath = [homeDir stringByAppendingString: @"/.xvimlog"]; + NSString *logPath = [@"~/.xvimlog" stringByExpandingTildeInPath]; [[Logger defaultLogger] setLogFile:logPath]; }else{ [[Logger defaultLogger] setLogFile:nil]; } } else if( [keyPath isEqualToString:@"document"] ){ - NSString *documentPath = [[[object document] fileURL] path]; - self.document = documentPath; - - if (documentPath != nil) { - NSDictionary *userInfo = [NSDictionary dictionaryWithObject:documentPath forKey:XVimDocumentPathKey]; - [[NSNotificationCenter defaultCenter] postNotificationName:XVimDocumentChangedNotification object:nil userInfo:userInfo]; + NSDocument *document = [object document]; + + if (![document respondsToSelector:@selector(textStorage)]) { + return; + } + + NSTextStorage *textStorage = [[object document] textStorage]; + XVimBuffer *buffer = document.xvim_buffer; + + self.document = document.fileURL.path; + if (!buffer) { + buffer = [XVimBuffer makeBufferForDocument:document textStorage:textStorage]; + } + if (buffer) { + NSDictionary *userInfo = @{ XVimBufferKey: buffer }; + [[NSNotificationCenter defaultCenter] postNotificationName:XVimBufferChangedNotification object:nil userInfo:userInfo]; } } } - (void)parseRcFile { - NSString *homeDir = NSHomeDirectoryForUser(NSUserName()); - NSString *keymapPath = [homeDir stringByAppendingString: @"/.xvimrc"]; + NSString *keymapPath = [@"~/.xvimrc" stringByExpandingTildeInPath]; NSString *keymapData = [[[NSString alloc] initWithContentsOfFile:keymapPath encoding:NSUTF8StringEncoding error:NULL] autorelease]; diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 69005048..1fe1de52 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -8,8 +8,48 @@ #import "XVimTextStoring.h" +@protocol XVimUndoing; + +/** @brief class to represent an XVim Buffer + * + * If we were writing a vi clone, this would be the object owning the document + * and the text storage. But since XVim is meant to be a plugin to add vim + * bindings to existing apps like XCode, this is done in the reverse way. + * + * The XVimBuffer is an associated object of the NSDocument and NSTextStorage + * that the App we're hooking into is supposed to use. + * + * XVimBuffer supposes that there's a 1:1 mapping between the document + * and the storage and owns no reference to either of those. + * + * This means that when the document and textstorage get deallocated, + * either one may be stale, and make the app crash if we try to use them. + * + * FIXME: hook into NSDocument/NSTextStorage to invalidate + * the XVimBuffer in the rigth places. + * This isn't urgent though because IDE uses an NSDocument subclass + * that owns its textStorage, hence their lifetime is tied. + */ + @interface XVimBuffer : NSObject -@property (nonatomic, readonly) XVimTextStorage *textStorage; +@property (nonatomic, readonly) NSDocument *document; +@property (nonatomic, readonly) NSTextStorage *textStorage; +@property (nonatomic, readonly) NSUndoManager *undoManager; + ++ (XVimBuffer *)makeBufferForDocument:(NSDocument *)document + textStorage:(NSTextStorage *)textStorage; + +#pragma mark Support for modifications + +- (void)undoRedo:(id)op; + +@end + +@interface NSTextStorage (XVimBuffer) +@property (nonatomic, readonly) XVimBuffer *xvim_buffer; +@end +@interface NSDocument (XVimBuffer) +@property (nonatomic, readonly) XVimBuffer *xvim_buffer; @end diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index faf55e49..1da94607 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -6,10 +6,69 @@ // // +#import #import "XVimBuffer.h" +#import "XVimUndo.h" + +static char const * const XVIM_KEY_BUFFER = "xvim_buffer"; @implementation XVimBuffer { + NSDocument *__unsafe_unretained _document; + NSTextStorage *__unsafe_unretained _textStorage; } +@synthesize document = _document; @synthesize textStorage = _textStorage; +- (NSUndoManager *)undoManager +{ + return _document.undoManager; +} + +- (instancetype)initWithDocument:(NSDocument *)document + textStorage:(NSTextStorage *)textStorage +{ + if ((self = [super init])) { + _document = document; + _textStorage = textStorage; + objc_setAssociatedObject(document, XVIM_KEY_BUFFER, self, OBJC_ASSOCIATION_RETAIN); + objc_setAssociatedObject(textStorage, XVIM_KEY_BUFFER, self, OBJC_ASSOCIATION_RETAIN); + } + return self; +} + ++ (XVimBuffer *)makeBufferForDocument:(NSDocument *)document + textStorage:(NSTextStorage *)textStorage +{ + XVimBuffer *buffer = document.xvim_buffer; + + if (buffer) return buffer; + + return [[[[self class] alloc] initWithDocument:document textStorage:textStorage] autorelease]; +} + +#pragma mark Support for modifications + +- (void)undoRedo:(id)op +{ + [op undoRedo:self]; +} + +@end + +@implementation NSTextStorage (XVimBuffer) + +- (XVimBuffer *)xvim_buffer +{ + return objc_getAssociatedObject(self, XVIM_KEY_BUFFER); +} + +@end + +@implementation NSDocument (XVimBuffer) + +- (XVimBuffer *)xvim_buffer +{ + return objc_getAssociatedObject(self, XVIM_KEY_BUFFER); +} + @end diff --git a/XVim/XVimStatusLine.m b/XVim/XVimStatusLine.m index 3a97338e..4d18cbc7 100644 --- a/XVim/XVimStatusLine.m +++ b/XVim/XVimStatusLine.m @@ -14,12 +14,13 @@ #import #import "XVim.h" #import "XVimOptions.h" +#import "XVimBuffer.h" #define STATUS_LINE_HEIGHT 18 @interface XVimStatusLine () -- (void)_documentChangedNotification:(NSNotification *)notification; +- (void)_bufferChangedNotification:(NSNotification *)notification; @end @@ -39,7 +40,7 @@ - (id)initWithFrame:(NSRect)frame _status.backgroundColor = [NSColor clearColor]; [_status setEditable:NO]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_documentChangedNotification:) name:XVimDocumentChangedNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_bufferChangedNotification:) name:XVimBufferChangedNotification object:nil]; [self addSubview:_background]; [self addSubview:_status]; @@ -98,11 +99,13 @@ - (void)didContainerFrameChanged:(NSNotification*)notification{ } -- (void)_documentChangedNotification:(NSNotification *)notification +- (void)_bufferChangedNotification:(NSNotification *)notification { - NSString *documentPath = [[notification userInfo] objectForKey:XVimDocumentPathKey]; - if (documentPath != nil) { - [_status setString:documentPath]; + XVimBuffer *buffer = [notification.userInfo objectForKey:XVimBufferKey]; + NSString *path = buffer.document.fileURL.path; + + if (path) { + [_status setString:path]; } } diff --git a/XVim/XVimTextStoring.h b/XVim/XVimTextStoring.h index d1a55419..1009ecd9 100644 --- a/XVim/XVimTextStoring.h +++ b/XVim/XVimTextStoring.h @@ -241,7 +241,6 @@ */ - (NSUInteger)xvim_nextDigitInLine:(NSUInteger)index; - @end typedef NSTextStorage XVimTextStorage; diff --git a/XVim/XVimUndo.h b/XVim/XVimUndo.h new file mode 100644 index 00000000..f2d12357 --- /dev/null +++ b/XVim/XVimUndo.h @@ -0,0 +1,25 @@ +// +// XVimUndo.h +// XVim +// +// Created by John AppleSeed on 17/11/13. +// +// + +#import +#import "XVimDefs.h" + +@class XVimBuffer; + +@protocol XVimUndoing + +- (void)undoRedo:(XVimBuffer *)text; + +@end + +@interface XVimUndoCursorPositionOperation : NSObject + +- (instancetype)initWithPosition:(XVimPosition)pos + undoManager:(NSUndoManager *)undoManager; + +@end diff --git a/XVim/XVimUndo.m b/XVim/XVimUndo.m new file mode 100644 index 00000000..2577ac1b --- /dev/null +++ b/XVim/XVimUndo.m @@ -0,0 +1,48 @@ +// +// XVimUndo.m +// XVim +// +// Created by John AppleSeed on 17/11/13. +// +// + +#import "XVimUndo.h" +#import "NSTextView+VimOperation.h" +#import "XVimBuffer.h" + +@implementation XVimUndoCursorPositionOperation { + NSUndoManager *_undoManager; + XVimPosition _pos; +} + +- (instancetype)initWithPosition:(XVimPosition)pos + undoManager:(NSUndoManager *)undoManager +{ + if ((self = [super init])) { + _pos = pos; + _undoManager = [undoManager retain]; + } + return self; +} + +- (void)dealloc +{ + [_undoManager release]; + [super dealloc]; +} + +- (void)undoRedo:(XVimBuffer *)buffer +{ + NSTextStorage *text = buffer.textStorage; + + for (NSLayoutManager *mgr in text.layoutManagers) { + NSTextView *view = mgr.firstTextView; + + if (view.textStorage == text) { + [view xvim_moveToPosition:_pos]; + return; + } + } +} + +@end diff --git a/XVim/XVimWindow.m b/XVim/XVimWindow.m index 85628b12..df71c778 100644 --- a/XVim/XVimWindow.m +++ b/XVim/XVimWindow.m @@ -52,8 +52,8 @@ - (instancetype)initWithIDEEditorArea:(IDEEditorArea *)editorArea [self _resetEvaluatorStack:_evaluatorStack activateNormalHandler:YES]; _commandLine = [[XVimCommandLine alloc] init]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_documentChangedNotification:) - name:XVimDocumentChangedNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_bufferChangedNotification:) + name:XVimBufferChangedNotification object:nil]; } return self; } @@ -116,7 +116,7 @@ - (void)_resetEvaluatorStack:(NSMutableArray *)stack activateNormalHandler:(BOOL } } -- (void)_documentChangedNotification:(NSNotification *)notification +- (void)_bufferChangedNotification:(NSNotification *)notification { DEBUG_LOG("Document changed, reset evaluator stack"); [self.currentEvaluator cancelHandler]; From 04a77f21dfb53f0566583bf6740c7a93d6424c42 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Mon, 18 Nov 2013 00:50:30 +0100 Subject: [PATCH 02/44] Migrate most of XVimTextStoring to XVimBuffer XVimTextStoring becomes a protocol that you implement when you intend to give better alternatives than the default implementations from XVimBuffer. This commit doesn't alter code, it merely moves some around, and adpt callers. --- XVim/DVTTextStorage+XVimTextStoring.m | 10 +- XVim/NSTextStorage+VimOperation.h | 63 +--- XVim/NSTextStorage+VimOperation.m | 467 ++++---------------------- XVim/NSTextView+VimOperation.h | 1 - XVim/NSTextView+VimOperation.m | 285 ++++++++-------- XVim/XVimBuffer.h | 230 ++++++++++++- XVim/XVimBuffer.m | 360 ++++++++++++++++++++ XVim/XVimExCommand.m | 26 +- XVim/XVimGActionEvaluator.m | 3 +- XVim/XVimInsertEvaluator.m | 10 +- XVim/XVimMarkSetEvaluator.m | 5 +- XVim/XVimMotionEvaluator.m | 14 +- XVim/XVimNormalEvaluator.m | 24 +- XVim/XVimSearch.m | 7 +- XVim/XVimTextStoring.h | 223 +----------- XVim/XVimVisualEvaluator.m | 10 +- XVim/XVimWindow.h | 2 + XVim/XVimWindow.m | 5 + 18 files changed, 879 insertions(+), 866 deletions(-) diff --git a/XVim/DVTTextStorage+XVimTextStoring.m b/XVim/DVTTextStorage+XVimTextStoring.m index 73c6d12d..2c94f822 100644 --- a/XVim/DVTTextStorage+XVimTextStoring.m +++ b/XVim/DVTTextStorage+XVimTextStoring.m @@ -10,6 +10,7 @@ #import "NSString+VimHelper.h" #import "DVTTextStorage+XVimTextStoring.h" #import "Logger.h" +#import "XVimBuffer.h" #if XVIM_XCODE_VERSION != 5 #define DVTTextStorage DVTSourceTextStorage @@ -17,6 +18,11 @@ @implementation DVTTextStorage (XVimTextStoring) +- (NSString *)xvim_string +{ + return self.string; +} + - (NSUInteger)xvim_numberOfLines { return self.numberOfLines; @@ -36,7 +42,6 @@ - (NSRange)xvim_indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUIntege { xvim_string_buffer_t sb; - NSAssert(num > 0, @"Line numbers start at 1"); if (num <= self.numberOfLines) { NSRange range = [self characterRangeForLineRange:NSMakeRange(num - 1, 1)]; @@ -53,15 +58,12 @@ - (NSRange)xvim_indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUIntege - (NSRange)xvim_indexRangeForLines:(NSRange)range { - NSAssert(range.location > 0, @"Line numbers start at 1"); - range.location--; return [self characterRangeForLineRange:range]; } - (NSUInteger)xvim_lineNumberAtIndex:(NSUInteger)index { - ASSERT_VALID_RANGE_WITH_EOF(index); if (index >= self.length) { return self.numberOfLines; } diff --git a/XVim/NSTextStorage+VimOperation.h b/XVim/NSTextStorage+VimOperation.h index ba434da4..a16a9b80 100644 --- a/XVim/NSTextStorage+VimOperation.h +++ b/XVim/NSTextStorage+VimOperation.h @@ -23,56 +23,7 @@ typedef enum { Adds Vim-like functionality to NSTextStorage. **/ - -#pragma mark Term Definitions -/** - * Note that the terms here are not the same definition as Cocoa or Xcode classes uses. - * - * "Character" - * Character is a one unichar value. (any value including tabs,spaces) - * - * "EOF" - * EOF is the position at the end of document(text). - * If we have NSTextView with string "abc" the EOF is just AFTER the 'c'. - * The index of EOF is 3 in this case ( index is 0 based ). - * What we have to think about is a cursor can be on the EOF(when the previous letter is newline) but characterAtIndex: with index of EOF cause an exception. - * We have to be careful about it when calculate and find the position of some motions. - * - * "Newline" - * Newline is defined as "unichar determined by isNewline function". Usually "\n" or "\r". - * - * "Line" - * Line is a sequence of characters terminated by newline or EOF. "Line" includes the last newline character. - * - * "Blankline" - * Blankline is a line which has only newline or EOF. In other words, it is newline character or EOF after newline character. - * - * "Last of Line(LOL)" - * Last of line is the last character of a line EXCLUDING newline character. - * This means that blankline does NOT have an Last of line. - * - * "First of Line(FOL)" - * First of line is the first character of a line excluding newline character. - * This means that blankline does NOT have a First of line. - * - * "First Nonblank of Line" - * First Nonblank of Line is the first printable character in a line. - * - * "End of Line(EOL)" - * End of Line is newline or EOF character at the end of a line. - * A line always has an EOL. - * - * "Beginning of Line (BOL)" - * First character of a line including newline and EOF - * - **/ - -/** - * Line number starts from 1. - * Column number starts from 0. - **/ - -@interface NSTextStorage (VimOperation) +@interface NSTextStorage (VimOperation) #pragma mark Definitions @@ -189,23 +140,11 @@ typedef enum { // These will be integreted into NSTextView category. // ======================= -// Return the location of the start of indentation on current line. '^' -NSInteger xv_caret(NSString *string, NSInteger index); -// Return the beginning of line location. '0' -NSInteger xv_0(NSString *string, NSInteger index); - // Unlike vim, this function won't ignore indent before the current character // even if what is '{' NSRange xv_current_block(NSString *string, NSUInteger index, NSUInteger repeatCount, BOOL inclusive, char what, char other); NSRange xv_current_quote(NSString *string, NSUInteger index, NSUInteger repeatCount, BOOL inclusive, char what); -// Find char in current line. -// Return the current index if nothing found. -// If inclusive is YES : -// 'fx' returns the index after 'x' -// 'Fx' returns the index before 'x' -NSInteger xv_findChar(NSString *string, NSInteger index, int repeatCount, char command, unichar what, BOOL inclusive); - #pragma mark Conversions diff --git a/XVim/NSTextStorage+VimOperation.m b/XVim/NSTextStorage+VimOperation.m index d9bbdd5b..bcbd524f 100644 --- a/XVim/NSTextStorage+VimOperation.m +++ b/XVim/NSTextStorage+VimOperation.m @@ -11,316 +11,15 @@ #import "NSTextStorage+VimOperation.h" #import "Logger.h" #import "XVimUndo.h" +#import "XVimBuffer.h" @implementation NSTextStorage (VimOperation) -#pragma mark XVimTextStoring Properties - -- (NSString *)xvim_string -{ - return self.string; -} - -- (NSUInteger)xvim_numberOfLines -{ - return [self xvim_lineNumberAtIndex:self.length]; -} - -#pragma mark Settings - -- (NSUInteger)xvim_indentWidth -{ - return 8; -} - -- (NSUInteger)xvim_tabWidth -{ - return 8; -} - -#pragma mark Converting between Indexes and Line Numbers - -// TODO: we may need to keep track line number and position by hooking insertText: method. -// FIXME: this code is actually never called in XVim for XCode, it probably has bugs, it's not tested -- (NSRange)xvim_indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUInteger *)newLineLength -{ - NSAssert(num > 0, @"line number starts at 1"); - - NSString *string = self.xvim_string; - NSUInteger length = self.length; - NSUInteger lineNum = 0, end = 0, contentsEnd; - - do { - [string getLineStart:NULL end:&end contentsEnd:&contentsEnd forRange:NSMakeRange(end, 0)]; - lineNum++; - if (lineNum == num) { - if (newLineLength) *newLineLength = end - contentsEnd; - return NSMakeRange(end, contentsEnd - end); - } - } while (end < length); - - if (newLineLength) *newLineLength = 0; - - // we have a last empty line after \n - if (contentsEnd < end) { - lineNum++; - if (lineNum == num) { - return NSMakeRange(end, 0); - } - } - - return NSMakeRange(NSNotFound, 0); -} - -// TODO: we may need to keep track line number and position by hooking insertText: method. -// FIXME: this code is actually never called in XVim for XCode, it probably has bugs, it's not tested -- (NSRange)xvim_indexRangeForLines:(NSRange)range -{ - NSString *string = self.xvim_string; - NSUInteger length = self.length, start; - NSUInteger lineNum = 0, end = 0, contentsEnd; - - NSAssert(range.location > 0, @"line number starts at 1"); - - do { - [string getLineStart:NULL end:&end contentsEnd:&contentsEnd forRange:NSMakeRange(end, 0)]; - lineNum++; - if (lineNum == range.location) { - start = end; - } - if (lineNum == NSMaxRange(range)) { - return NSMakeRange(start, end - start); - } - } while (end < length); - - // we have a last empty line after \n - if (contentsEnd < end) { - lineNum++; - if (lineNum == range.location) { - start = end; - } - if (lineNum == NSMaxRange(range)) { - return NSMakeRange(start, end - start); - } - } - - return NSMakeRange(0, length); -} - -- (NSRange)xvim_indexRangeForLineAtIndex:(NSUInteger)index newLineLength:(NSUInteger *)newLineLength -{ - ASSERT_VALID_RANGE_WITH_EOF(index); - NSString *string = self.xvim_string; - NSUInteger len = self.length; - NSUInteger end, contentEnd; - - if (index > len) { - index = len; - } - - [string getLineStart:&index end:&end contentsEnd:&contentEnd forRange:NSMakeRange(index, 0)]; - if (newLineLength) *newLineLength = contentEnd - end; - return NSMakeRange(index, contentEnd - index); -} - -- (NSUInteger)xvim_indexOfLineNumber:(NSUInteger)num -{ - if (num == 1) { - return 0; - } - return [self xvim_indexRangeForLineNumber:num newLineLength:NULL].location; -} - -- (NSUInteger)xvim_lineNumberAtIndex:(NSUInteger)index -{ - ASSERT_VALID_RANGE_WITH_EOF(index); - - NSString *string = self.xvim_string; - NSUInteger len = self.length; - NSUInteger num = 1, pos = 0; - - if (index > len) { - index = len; - } - - do { - num++; - if (index == pos) { - return num; - } - [string getLineStart:NULL end:&pos contentsEnd:NULL forRange:NSMakeRange(pos, 0)]; - } while (pos < index); - - return num; -} - -#pragma mark Converting between Indexes and Line Numbers + Columns - -static NSUInteger xvim_sb_count_columns(xvim_string_buffer_t *sb, NSUInteger tabWidth) -{ - NSUInteger col = 0; - - if (!xvim_sb_at_end(sb)) { - do { - if (xvim_sb_peek(sb) == '\t') { - col += tabWidth; - if (tabWidth) col -= col % tabWidth; - } else { - col++; - } - } while (xvim_sb_next(sb)); - } - - return col; -} - -- (NSUInteger)xvim_columnOfIndex:(NSUInteger)index -{ - NSRange range = [self xvim_indexRangeForLineAtIndex:index newLineLength:NULL]; - xvim_string_buffer_t sb; - - if (index < NSMaxRange(range)) { - range.length = index - range.location; - } - if (range.length == 0) { - return 0; - } - - xvim_sb_init(&sb, self.xvim_string, range.location, range); - return xvim_sb_count_columns(&sb, self.xvim_tabWidth); -} - -- (NSUInteger)xvim_numberOfColumnsInLineAtIndex:(NSUInteger)index -{ - NSRange range = [self xvim_indexRangeForLineAtIndex:index newLineLength:NULL]; - xvim_string_buffer_t sb; - - xvim_sb_init(&sb, self.xvim_string, range.location, range); - return xvim_sb_count_columns(&sb, self.xvim_tabWidth); -} - -- (NSUInteger)xvim_indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column -{ - NSUInteger index = [self xvim_indexOfLineNumber:num]; - - if (column == 0 || index == NSNotFound) { - return index; - } - - NSRange range = [self xvim_indexRangeForLineAtIndex:index newLineLength:NULL]; - NSUInteger tabWidth = self.xvim_tabWidth; - NSUInteger col = 0; - xvim_string_buffer_t sb; - - xvim_sb_init(&sb, self.xvim_string, range.location, range); - do { - if (xvim_sb_peek(&sb) == '\t') { - col += tabWidth; - if (tabWidth) col -= col % tabWidth; - } else { - col++; - } - if (col > column) { - return xvim_sb_index(&sb); - } - } while (xvim_sb_next(&sb) && col < column); - - return xvim_sb_index(&sb); -} - - -#pragma mark Searching particular positions on the current line - -- (NSUInteger)xvim_startOfLine:(NSUInteger)index -{ - ASSERT_VALID_RANGE_WITH_EOF(index); - NSString *string = self.xvim_string; - NSUInteger len = self.length; - - if (index > len) { - index = len; - } - [string getLineStart:&index end:NULL contentsEnd:NULL forRange:NSMakeRange(index, 0)]; - return index; -} - -- (NSUInteger)xvim_firstOfLine:(NSUInteger)index -{ - NSUInteger pos = [self xvim_startOfLine:index]; - - if (pos == index && isNewline([self.xvim_string characterAtIndex:pos])) { - return NSNotFound; - } - return pos; -} - -- (NSUInteger)xvim_endOfLine:(NSUInteger)index -{ - ASSERT_VALID_RANGE_WITH_EOF(index); - NSString *string = self.xvim_string; - NSUInteger len = self.length; - - if (index > len) { - index = len; - } - [string getLineStart:NULL end:NULL contentsEnd:&index forRange:NSMakeRange(index, 0)]; - return index; -} - -- (NSUInteger)xvim_lastOfLine:(NSUInteger)index -{ - NSUInteger pos = [self xvim_endOfLine:index]; - - if (pos <= index && (pos == 0 || isNewline([self.xvim_string characterAtIndex:pos - 1]))) { - return NSNotFound; - } - return pos - 1; -} - -- (NSUInteger)xvim_nextNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL -{ - NSString *s = self.xvim_string; - NSUInteger length = s.length; - xvim_string_buffer_t sb; - unichar c; - - ASSERT_VALID_RANGE_WITH_EOF(index); - - xvim_sb_init(&sb, s, index, NSMakeRange(index, length - index)); - xvim_sb_skip_forward(&sb, [NSCharacterSet whitespaceCharacterSet]); - c = xvim_sb_peek(&sb); - - if (c == XVimInvalidChar || isNewline(c)) { - return allowEOL ? xvim_sb_index(&sb) : NSNotFound; - } - return xvim_sb_index(&sb); -} - -- (NSUInteger)xvim_firstNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL -{ - index = [self xvim_startOfLine:index]; - return [self xvim_nextNonblankInLineAtIndex:index allowEOL:allowEOL]; -} - -- (NSUInteger)xvim_nextDigitInLine:(NSUInteger)index -{ - xvim_string_buffer_t sb; - NSRange range; - - range = [self xvim_indexRangeForLineAtIndex:index newLineLength:NULL]; - xvim_sb_init(&sb, self.xvim_string, index, range); - if (xvim_sb_find_forward(&sb, [NSCharacterSet decimalDigitCharacterSet])) { - return xvim_sb_index(&sb); - } - - return NSNotFound; -} - #pragma mark Definitions - (BOOL) isEOF:(NSUInteger)index{ ASSERT_VALID_RANGE_WITH_EOF(index); - return [[self xvim_string] length] == index; + return self.length == index; } - (BOOL) isLOL:(NSUInteger)index{ @@ -348,23 +47,24 @@ - (BOOL) isBOL:(NSUInteger)index{ - (BOOL) isNewline:(NSUInteger)index{ ASSERT_VALID_RANGE_WITH_EOF(index); - if( index == [[self xvim_string] length] ){ + if( index == self.length ){ return NO; // EOF is not a newline } - return isNewline([[self xvim_string] characterAtIndex:index]); + return isNewline([self.xvim_buffer.string characterAtIndex:index]); } - (BOOL) isWhitespace:(NSUInteger)index{ ASSERT_VALID_RANGE_WITH_EOF(index); - if( index == [[self xvim_string] length] ){ + if( index == self.length ){ return NO; // EOF is not whitespace } - return isWhitespace([[self xvim_string] characterAtIndex:index]); + return isWhitespace([self.xvim_buffer.string characterAtIndex:index]); } - (BOOL) isLastLine:(NSUInteger)index{ ASSERT_VALID_RANGE_WITH_EOF(index); - return [self xvim_lineNumberAtIndex:index] == [self xvim_numberOfLines]; + XVimBuffer *buffer = self.xvim_buffer; + return [buffer lineNumberAtIndex:index] == [buffer numberOfLines]; } - (BOOL) isNonblank:(NSUInteger)index{ @@ -372,12 +72,13 @@ - (BOOL) isNonblank:(NSUInteger)index{ if( [self isEOF:index]){ return YES; } - return isNonblank([[self xvim_string] characterAtIndex:index]); + return isNonblank([self.xvim_buffer.string characterAtIndex:index]); } - (BOOL)isBlankline:(NSUInteger)index { - return [self xvim_indexRangeForLineAtIndex:index newLineLength:NULL].length == 0; + XVimBuffer *buffer = self.xvim_buffer; + return [buffer indexRangeForLineAtIndex:index newLineLength:NULL].length == 0; } - (BOOL) isValidCursorPosition:(NSUInteger)index @@ -410,13 +111,14 @@ - (BOOL) isValidCursorPosition:(NSUInteger)index **/ - (NSUInteger)positionOfMatchedPair:(NSUInteger)pos { - NSString *s = self.xvim_string; + XVimBuffer *buffer = self.xvim_buffer; + NSString *s = buffer.string; // find matching bracketing character and go to it // as long as the nesting level matches up xvim_string_buffer_t sb; - xvim_sb_init(&sb, s, pos, NSMakeRange(pos, [self xvim_endOfLine:pos] - pos)); + xvim_sb_init(&sb, s, pos, NSMakeRange(pos, [buffer endOfLine:pos] - pos)); #define pairs "{}[]()" NSCharacterSet *charset = [NSCharacterSet characterSetWithCharactersInString:@pairs]; @@ -464,14 +166,14 @@ - (NSUInteger)prev:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTI return 0; } - NSString* string = [self xvim_string]; + NSString* string = self.xvim_buffer.string; NSUInteger pos = index; for (NSUInteger i = 0; i < count && pos != 0 ; i++) { //Try move to prev position and check if its valid position. NSUInteger prev = pos-1; //This is the position where we are trying to move to. // If the position is new line and its not wrapable we stop moving - if( opt == LEFT_RIGHT_NOWRAP && isNewline([[self xvim_string] characterAtIndex:prev]) ){ + if( opt == LEFT_RIGHT_NOWRAP && isNewline([self.xvim_buffer.string characterAtIndex:prev]) ){ break; // not update the position } @@ -507,10 +209,10 @@ - (NSUInteger)prev:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTI - (NSUInteger)next:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimMotionInfo*)info{ info->reachedEndOfLine = NO; - if( index == [[self xvim_string] length] ) - return [[self xvim_string] length]; + if( index == [self.xvim_buffer.string length] ) + return [self.xvim_buffer.string length]; - NSString* string = [self xvim_string]; + NSString* string = self.xvim_buffer.string; NSUInteger pos = index; // If the currenct cursor position is on a newline (blank line) and not wrappable never move the cursor if( opt == LEFT_RIGHT_NOWRAP && [self isBlankline:pos]){ @@ -526,7 +228,7 @@ - (NSUInteger)next:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTI break; } - if( opt == LEFT_RIGHT_NOWRAP && isNewline([[self xvim_string] characterAtIndex:next]) ){ + if( opt == LEFT_RIGHT_NOWRAP && isNewline([string characterAtIndex:next]) ){ info->reachedEndOfLine = YES; break; } @@ -545,23 +247,25 @@ - (NSUInteger)next:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTI - (NSUInteger)prevLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInteger)count option:(MOTION_OPTION)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); + XVimBuffer *buffer = self.xvim_buffer; - NSUInteger lno = [self xvim_lineNumberAtIndex:index]; + NSUInteger lno = [buffer lineNumberAtIndex:index]; lno = lno < count ? 1 : lno - count; - return [self xvim_indexOfLineNumber:lno column:column]; + return [buffer indexOfLineNumber:lno column:column]; } - (NSUInteger)nextLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInteger)count option:(MOTION_OPTION)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); - - NSUInteger lno = [self xvim_lineNumberAtIndex:index] + count; - NSUInteger lines = self.xvim_numberOfLines; + XVimBuffer *buffer = self.xvim_buffer; + + NSUInteger lno = [buffer lineNumberAtIndex:index] + count; + NSUInteger lines = buffer.numberOfLines; if (lno > lines) { lno = lines; } - return [self xvim_indexOfLineNumber:lno column:column]; + return [buffer indexOfLineNumber:lno column:column]; } /** @@ -598,10 +302,10 @@ - (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOT return index; } - NSString* str = [self xvim_string]; + NSString* str = self.xvim_buffer.string; unichar lastChar= [str characterAtIndex:index]; BOOL wordInLineFound = NO; - for(NSUInteger i = index+1 ; i <= [[self xvim_string] length]; i++ ){ + for(NSUInteger i = index+1 ; i <= [self length]; i++ ){ // Each time we encounter new word decrement "counter". // Remember blankline is a word unichar curChar; @@ -695,7 +399,7 @@ - (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MO return 0; NSUInteger indexBoundary = NSNotFound; NSUInteger pos = index-1; - unichar lastChar = [[self xvim_string] characterAtIndex:pos]; + unichar lastChar = [self.xvim_buffer.string characterAtIndex:pos]; // FIXME: This must consider the placeholders // The reason currently commented out is that this method is in NSTextStorage // but the placeholder related codes are in DVTSourceTextView @@ -723,7 +427,7 @@ - (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MO } } */ - unichar curChar = [[self xvim_string] characterAtIndex:i]; + unichar curChar = [self.xvim_buffer.string characterAtIndex:i]; // this branch handles the case that we found a placeholder. // must update the pointer into the string and update the current character found to be at the current index. @@ -734,7 +438,7 @@ - (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MO pos = i; break; } - curChar = [[self xvim_string] characterAtIndex:i]; + curChar = [self.xvim_buffer.string characterAtIndex:i]; } // new word starts between followings.( keyword is determined by 'iskeyword' in Vim ) // - Whitespace(including newline) and Non-Blank @@ -786,7 +490,7 @@ - (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option } else { p = index+1; // We start searching end of word from next character } - NSString *string = [self xvim_string]; + NSString *string = self.xvim_buffer.string; while (p < length - 1) { unichar curChar = [string characterAtIndex:p]; unichar nextChar = [string characterAtIndex:p+1]; @@ -845,7 +549,7 @@ - (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count optio - (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( NSUInteger pos = index+1; NSUInteger sentence_head = NSNotFound; - NSString* s = [self xvim_string]; + NSString* s = self.xvim_buffer.string; NSUInteger sentence_found = 0; if( pos >= s.length-1 ){ return NSNotFound; @@ -902,7 +606,7 @@ - (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option NSUInteger pos = index-1; NSUInteger lastSearchBase = index; NSUInteger sentence_head = NSNotFound; - NSString* s = [self xvim_string]; + NSString* s = self.xvim_buffer.string; NSUInteger sentence_found = 0; // Search "." or "!" or "?" backwards and check if it is followed by spaces(and closing characters) for( ; NSNotFound == sentence_head ; pos-- ){ @@ -969,7 +673,9 @@ Note that a blank line (only containing white space) is NOT a paragraph - (NSUInteger)paragraphsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( NSUInteger pos = index; - NSString* s = [self xvim_string]; + NSString* s = self.xvim_buffer.string; + XVimBuffer *buffer = self.xvim_buffer; + if( 0 == pos ){ pos = 1; } @@ -982,7 +688,7 @@ - (NSUInteger)paragraphsForward:(NSUInteger)index count:(NSUInteger)count option unichar c = [s characterAtIndex:pos]; unichar prevc = [s characterAtIndex:prevpos]; if(isNewline(prevc) && !isNewline(c)){ - if([self xvim_nextNonblankInLineAtIndex:pos allowEOL:NO] == NSNotFound && opt == MOPT_PARA_BOUND_BLANKLINE){ + if([buffer nextNonblankInLineAtIndex:pos allowEOL:NO] == NSNotFound && opt == MOPT_PARA_BOUND_BLANKLINE){ paragraph_found++; if(count == paragraph_found){ paragraph_head = pos; @@ -1021,7 +727,7 @@ - (NSUInteger)paragraphsForward:(NSUInteger)index count:(NSUInteger)count option - (NSUInteger)paragraphsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( NSUInteger pos = index; - NSString* s = [self xvim_string]; + NSString* s = self.xvim_buffer.string; if( pos == 0 ){ return NSNotFound; } @@ -1077,13 +783,13 @@ - (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count char return NSNotFound; } NSUInteger p = index+1; - NSUInteger end = [self xvim_endOfLine:p]; + NSUInteger end = [self.xvim_buffer endOfLine:p]; if( NSNotFound == end ){ return NSNotFound; } for( ; p <= end; p++ ){ - if( [[self xvim_string] characterAtIndex:p] == character ){ + if( [self.xvim_buffer.string characterAtIndex:p] == character ){ count--; if( 0 == count ){ return p; @@ -1099,13 +805,13 @@ - (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count char return NSNotFound; } NSUInteger p = index-1; - NSUInteger head = [self xvim_firstOfLine:p]; + NSUInteger head = [self.xvim_buffer firstOfLine:p]; if( NSNotFound == head ){ return NSNotFound; } for( ; p >= head ; p-- ){ - if( [[self xvim_string] characterAtIndex:p] == character ){ + if( [self.xvim_buffer.string characterAtIndex:p] == character ){ count--; if( 0 == count ){ return p; @@ -1229,12 +935,12 @@ - (NSRange)searchRegexBackward:(NSString*)pattern from:(NSUInteger)index count:( NSInteger index; } NSStringHelper; -void initNSStringHelper(NSStringHelper*, NSString* string, NSUInteger strLen); -void initNSStringHelperBackward(NSStringHelper*, NSString* string, NSUInteger strLen); -unichar characterAtIndex(NSStringHelper*, NSInteger index); +static void initNSStringHelper(NSStringHelper*, NSString* string, NSUInteger strLen); +static void initNSStringHelperBackward(NSStringHelper*, NSString* string, NSUInteger strLen); +static unichar characterAtIndex(NSStringHelper*, NSInteger index); - (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ - NSString* string = [self xvim_string]; + NSString* string = self.xvim_buffer.string; NSInteger maxIndex = self.length - 1; if (index > maxIndex) { return NSMakeRange(NSNotFound, 0); } @@ -1321,22 +1027,21 @@ - (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION // These will be integreted into NSTextView category. // ========== // NSStringHelper -void initNSStringHelper(NSStringHelper* h, NSString* string, NSUInteger strLen) +static void initNSStringHelper(NSStringHelper* h, NSString* string, NSUInteger strLen) { h->string = string; h->strLen = strLen; h->index = -ITERATE_STRING_BUFFER_SIZE; } -void initNSStringHelperBackward(NSStringHelper* h, NSString* string, NSUInteger strLen) +static void initNSStringHelperBackward(NSStringHelper* h, NSString* string, NSUInteger strLen) { h->string = string; h->strLen = strLen; h->index = strLen; } -NSInteger fetchSubStringFrom(NSStringHelper* h, NSInteger index); -NSInteger fetchSubStringFrom(NSStringHelper* h, NSInteger index) +static NSInteger fetchSubStringFrom(NSStringHelper* h, NSInteger index) { NSInteger copyBegin = index; NSInteger size = (index + ITERATE_STRING_BUFFER_SIZE) > h->strLen ? h->strLen - index : ITERATE_STRING_BUFFER_SIZE; @@ -1344,8 +1049,7 @@ NSInteger fetchSubStringFrom(NSStringHelper* h, NSInteger index) return copyBegin; } -NSInteger fetchSubStringEnds(NSStringHelper* h, NSInteger index); -NSInteger fetchSubStringEnds(NSStringHelper* h, NSInteger index) +static NSInteger fetchSubStringEnds(NSStringHelper* h, NSInteger index) { NSInteger copyBegin = (index + 1) >= ITERATE_STRING_BUFFER_SIZE ? index + 1 - ITERATE_STRING_BUFFER_SIZE : 0; NSInteger size = (index + ITERATE_STRING_BUFFER_SIZE) > h->strLen ? h->strLen - index : ITERATE_STRING_BUFFER_SIZE; @@ -1353,7 +1057,7 @@ NSInteger fetchSubStringEnds(NSStringHelper* h, NSInteger index) return copyBegin; } -unichar characterAtIndex(NSStringHelper* h, NSInteger index) +static unichar characterAtIndex(NSStringHelper* h, NSInteger index) { if (h->index > index) { @@ -1365,7 +1069,7 @@ unichar characterAtIndex(NSStringHelper* h, NSInteger index) return h->buffer[index - h->index]; } -NSInteger xv_caret(NSString *string, NSInteger index) +static NSInteger xv_caret(NSString *string, NSInteger index) { NSInteger resultIndex = index; NSInteger seekingIndex = index; @@ -1394,7 +1098,7 @@ NSInteger xv_caret(NSString *string, NSInteger index) return resultIndex; } -NSInteger xv_0(NSString *string, NSInteger index) +static NSInteger xv_0(NSString *string, NSInteger index) { while (index > 0) { @@ -1412,8 +1116,7 @@ NSInteger xv_0(NSString *string, NSInteger index) #define MAYBE 2 #define FORWARD 1 #define BACKWARD -1 -int findmatchlimit(NSString* string, NSInteger pos, unichar initc, BOOL cpo_match); -int findmatchlimit(NSString* string, NSInteger pos, unichar initc, BOOL cpo_match) +static int findmatchlimit(NSString* string, NSInteger pos, unichar initc, BOOL cpo_match) { // ---------- unichar findc = 0; // The char to find. @@ -1798,8 +1501,7 @@ static NSInteger seek_forwards(NSString *string, NSInteger end, NSCharacterSet * return searchSet; } -NSInteger find_next_quote(NSString* string, NSInteger start, NSInteger max, unichar quote, BOOL ignoreEscape); -NSInteger find_next_quote(NSString* string, NSInteger start, NSInteger max, unichar quote, BOOL ignoreEscape) +static NSInteger find_next_quote(NSString* string, NSInteger start, NSInteger max, unichar quote, BOOL ignoreEscape) { BOOL ignoreNextChar = NO; @@ -1824,8 +1526,7 @@ NSInteger find_next_quote(NSString* string, NSInteger start, NSInteger max, unic return -1; } -NSInteger find_prev_quote(NSString* string, NSInteger start, unichar quote, BOOL ignoreEscape); -NSInteger find_prev_quote(NSString* string, NSInteger start, unichar quote, BOOL ignoreEscape) +static NSInteger find_prev_quote(NSString* string, NSInteger start, unichar quote, BOOL ignoreEscape) { NSInteger pendingi = -1; NSInteger pendingQuote = -1; @@ -1909,58 +1610,8 @@ NSRange xv_current_quote(NSString *string, NSUInteger index, NSUInteger repeatCo return NSMakeRange(begin, end - begin); } -NSInteger xv_findChar(NSString *string, NSInteger index, int repeatCount, char command, unichar what, BOOL inclusive) -{ - int increment = command <= 'Z' ? -1 : 1; // Capital means backward. - - NSInteger maxIdx = [string length] - 1; - NSInteger idx = index; - NSInteger result = idx; - - NSStringHelper help; - NSStringHelper* h = &help; - - if (increment == -1) - initNSStringHelperBackward(h, string, maxIdx + 1); - else - initNSStringHelper(h, string, maxIdx + 1); - - idx += increment; - while (idx >= 0 && idx <= maxIdx) - { - unichar ch = characterAtIndex(h, idx); - if (ch == what) - { - if ((--repeatCount) == 0) { - result = idx; // Found - break; - } - } else if (isNewline(ch)) - { - break; // Only search in current line. - } - idx += increment; - } - - if (result == idx) - { - if (command == 't') { - --result; - } else if (command == 'T') { - ++result; - } - - if (inclusive && increment == 1) // Include the position we found. - { - ++result; - } - } - - return result; -} #pragma GCC diagnostic pop - #pragma mark Conversions - (NSUInteger)convertToValidCursorPositionForNormalMode:(NSUInteger)index{ diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h index 40a2082d..b2e4ae50 100644 --- a/XVim/NSTextView+VimOperation.h +++ b/XVim/NSTextView+VimOperation.h @@ -68,7 +68,6 @@ @property(readonly) NSArray* foundRanges; @property(readonly) long long currentLineNumber; -- (NSString*)xvim_string; #pragma mark Changing state - (void)xvim_changeSelectionMode:(XVIM_VISUAL_MODE)mode; - (void)xvim_escapeFromInsert; diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index adc7cc1e..268aebe0 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -89,8 +89,9 @@ - (void)_xvim_insertSpaces:(NSUInteger)count replacementRange:(NSRange)replaceme - (void)_xvim_removeSpacesAtLine:(NSUInteger)line column:(NSUInteger)column count:(NSUInteger)count { NSTextStorage *ts = self.textStorage; - NSUInteger tabWidth = ts.xvim_tabWidth; - NSUInteger pos = [ts xvim_indexOfLineNumber:line column:column]; + XVimBuffer *buffer = ts.xvim_buffer; + NSUInteger tabWidth = buffer.tabWidth; + NSUInteger pos = [buffer indexOfLineNumber:line column:column]; NSUInteger end = pos; NSUInteger width = 0; NSString *s = self.xvim_string; @@ -100,7 +101,7 @@ - (void)_xvim_removeSpacesAtLine:(NSUInteger)line column:(NSUInteger)column coun } if ([s characterAtIndex:pos] == '\t') { - NSUInteger col = [ts xvim_columnOfIndex:pos]; + NSUInteger col = [buffer columnOfIndex:pos]; if (col < column) { [self _xvim_insertSpaces:tabWidth - (col % tabWidth) replacementRange:NSMakeRange(pos, 1)]; @@ -138,8 +139,9 @@ - (XVimRange)_xvim_selectedLines{ if (self.selectionMode == XVIM_VISUAL_NONE) { // its not in selecting mode return (XVimRange){ NSNotFound, NSNotFound }; } else { - NSUInteger l1 = [self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]; - NSUInteger l2 = [self.textStorage xvim_lineNumberAtIndex:self.selectionBegin]; + XVimBuffer *buffer = self.textStorage.xvim_buffer; + NSUInteger l1 = [buffer lineNumberAtIndex:self.insertionPoint]; + NSUInteger l2 = [buffer lineNumberAtIndex:self.selectionBegin]; return (XVimRange){ MIN(l1, l2), MAX(l1, l2) }; } @@ -164,10 +166,11 @@ - (NSRange)_xvim_selectedRange{ if (self.selectionMode == XVIM_VISUAL_LINE) { XVimRange lines = [self _xvim_selectedLines]; - NSUInteger begin = [self.textStorage xvim_indexOfLineNumber:lines.begin]; - NSUInteger end = [self.textStorage xvim_indexOfLineNumber:lines.end]; + XVimBuffer *buffer = self.textStorage.xvim_buffer; + NSUInteger begin = [buffer indexOfLineNumber:lines.begin]; + NSUInteger end = [buffer indexOfLineNumber:lines.end]; - end = [self.textStorage xvim_endOfLine:end]; + end = [buffer endOfLine:end]; if ([self.textStorage isEOF:end]) { end--; } @@ -186,14 +189,15 @@ - (XVimSelection)_xvim_selectedBlock{ } NSTextStorage *ts = self.textStorage; + XVimBuffer *buffer = self.textStorage.xvim_buffer; NSUInteger l1, c11, c12; NSUInteger l2, c21, c22; - NSUInteger tabWidth = ts.xvim_tabWidth; + NSUInteger tabWidth = buffer.tabWidth; NSUInteger pos; pos = self.selectionBegin; - l1 = [ts xvim_lineNumberAtIndex:pos]; - c11 = [ts xvim_columnOfIndex:pos]; + l1 = [buffer lineNumberAtIndex:pos]; + c11 = [buffer columnOfIndex:pos]; if (!tabWidth || [ts isEOF:pos] || [self.xvim_string characterAtIndex:pos] != '\t') { c12 = c11; } else { @@ -201,8 +205,8 @@ - (XVimSelection)_xvim_selectedBlock{ } pos = self.insertionPoint; - l2 = [ts xvim_lineNumberAtIndex:pos]; - c21 = [ts xvim_columnOfIndex:pos]; + l2 = [buffer lineNumberAtIndex:pos]; + c21 = [buffer columnOfIndex:pos]; if (!tabWidth || [ts isEOF:pos] || [self.xvim_string characterAtIndex:pos] != '\t') { c22 = c21; } else { @@ -270,14 +274,15 @@ - (void)_xvim_yankSelection:(XVimSelection)sel { NSTextStorage *ts = self.textStorage; NSString *s = self.xvim_string; - NSUInteger tabWidth = ts.xvim_tabWidth; + XVimBuffer *buffer = ts.xvim_buffer; + NSUInteger tabWidth = buffer.tabWidth; NSMutableString *ybuf = [[NSMutableString alloc] init]; self.lastYankedType = TEXT_TYPE_BLOCK; for (NSUInteger line = sel.top; line <= sel.bottom; line++) { - NSUInteger lpos = [ts xvim_indexOfLineNumber:line column:sel.left]; - NSUInteger rpos = [ts xvim_indexOfLineNumber:line column:sel.right]; + NSUInteger lpos = [buffer indexOfLineNumber:line column:sel.left]; + NSUInteger rpos = [buffer indexOfLineNumber:line column:sel.right]; /* if lpos points in the middle of a tab, split it and advance lpos */ if (![ts isEOF:lpos] && [s characterAtIndex:lpos] == '\t') { @@ -305,17 +310,17 @@ - (void)_xvim_yankSelection:(XVimSelection)sel BOOL mustPad = NO; if ([ts isEOF:rpos]) { - rcol = [ts xvim_columnOfIndex:rpos]; + rcol = [buffer columnOfIndex:rpos]; mustPad = YES; r.length--; } else { unichar c = [s characterAtIndex:rpos]; if (isNewline(c)) { - rcol = [ts xvim_columnOfIndex:rpos]; + rcol = [buffer columnOfIndex:rpos]; mustPad = YES; r.length--; } else if (c == '\t') { - rcol = [ts xvim_columnOfIndex:rpos]; + rcol = [buffer columnOfIndex:rpos]; if (sel.right - rcol + 1 < tabWidth) { mustPad = YES; r.length--; @@ -344,11 +349,12 @@ - (void)_xvim_killSelection:(XVimSelection)sel { NSTextStorage *ts = self.textStorage; NSString *s = self.xvim_string; - NSUInteger tabWidth = ts.xvim_tabWidth; + XVimBuffer *buffer = ts.xvim_buffer; + NSUInteger tabWidth = buffer.tabWidth; for (NSUInteger line = sel.bottom; line >= sel.top; line--) { - NSUInteger lpos = [ts xvim_indexOfLineNumber:line column:sel.left]; - NSUInteger rpos = [ts xvim_indexOfLineNumber:line column:sel.right]; + NSUInteger lpos = [buffer indexOfLineNumber:line column:sel.left]; + NSUInteger rpos = [buffer indexOfLineNumber:line column:sel.right]; NSUInteger nspaces = 0; if ([ts isEOF:lpos]) { @@ -356,7 +362,7 @@ - (void)_xvim_killSelection:(XVimSelection)sel } if ([s characterAtIndex:lpos] == '\t') { - NSUInteger lcol = [ts xvim_columnOfIndex:lpos]; + NSUInteger lcol = [buffer columnOfIndex:lpos]; if (lcol < sel.left) { nspaces = sel.left - lcol; @@ -370,7 +376,7 @@ - (void)_xvim_killSelection:(XVimSelection)sel rpos--; } else if (lpos < rpos) { if ([s characterAtIndex:rpos] == '\t') { - nspaces += tabWidth - (sel.right - [ts xvim_columnOfIndex:rpos] + 1); + nspaces += tabWidth - (sel.right - [buffer columnOfIndex:rpos] + 1); } } @@ -410,11 +416,11 @@ - (void)setInsertionPosition:(XVimPosition)pos{ } - (NSUInteger)insertionColumn{ - return [self.textStorage xvim_columnOfIndex:self.insertionPoint]; + return [self.textStorage.xvim_buffer columnOfIndex:self.insertionPoint]; } - (NSUInteger)insertionLine{ - return [self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]; + return [self.textStorage.xvim_buffer lineNumberAtIndex:self.insertionPoint]; } - (NSUInteger)preservedColumn{ @@ -437,7 +443,8 @@ - (void)setSelectionBegin:(NSUInteger)selectionBegin{ } - (XVimPosition)selectionBeginPosition{ - return XVimMakePosition([self.textStorage xvim_lineNumberAtIndex:self.selectionBegin], [self.textStorage xvim_columnOfIndex:self.selectionBegin]); + XVimBuffer *buffer = self.textStorage.xvim_buffer; + return XVimMakePosition([buffer lineNumberAtIndex:self.selectionBegin], [buffer columnOfIndex:self.selectionBegin]); } - (NSUInteger)numberOfSelectedLines{ @@ -547,8 +554,9 @@ - (long long)currentLineNumber { } -- (NSString*)xvim_string{ - return [self.textStorage xvim_string]; +- (NSString *)xvim_string +{ + return self.textStorage.xvim_buffer.string; } #pragma mark Status @@ -617,7 +625,8 @@ - (void)xvim_adjustCursorPosition{ } - (void)xvim_moveToPosition:(XVimPosition)pos{ - [self xvim_moveCursor:[self.textStorage xvim_indexOfLineNumber:pos.line column:pos.column] preserveColumn:NO]; + NSUInteger index = [self.textStorage.xvim_buffer indexOfLineNumber:pos.line column:pos.column]; + [self xvim_moveCursor:index preserveColumn:NO]; [self xvim_syncState]; } @@ -654,7 +663,7 @@ - (void)xvim_move:(XVimMotion*)motion{ case MOTION_LINENUMBER: // TODO: Preserve column option can be included in motion object if (self.selectionMode == XVIM_VISUAL_BLOCK && self.selectionToEOL) { - r.end = [self.textStorage xvim_endOfLine:r.end]; + r.end = [self.textStorage.xvim_buffer endOfLine:r.end]; } [self xvim_moveCursor:r.end preserveColumn:YES]; break; @@ -703,9 +712,9 @@ - (void)xvim_selectSwapEndsOnSameLine:(BOOL)onSameLine{ start.column = sel.right; } - pos = [self.textStorage xvim_indexOfLineNumber:start.line column:start.column]; + pos = [self.textStorage.xvim_buffer indexOfLineNumber:start.line column:start.column]; self.selectionBegin = pos; - pos = [self.textStorage xvim_indexOfLineNumber:end.line column:end.column]; + pos = [self.textStorage.xvim_buffer indexOfLineNumber:end.line column:end.column]; [self xvim_moveCursor:pos preserveColumn:NO]; } else if (self.selectionMode != XVIM_VISUAL_NONE) { NSUInteger begin = self.selectionBegin; @@ -772,7 +781,7 @@ - (void)xvim_delete:(XVimMotion*)motion andYank:(BOOL)yank } [self insertText:@"" replacementRange:r]; if (motion.type == LINEWISE) { - newPos = [self.textStorage xvim_firstNonblankInLineAtIndex:self.insertionPoint allowEOL:YES]; + newPos = [self.textStorage.xvim_buffer firstNonblankInLineAtIndex:self.insertionPoint allowEOL:YES]; } } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { BOOL toFirstNonBlank = (self.selectionMode == XVIM_VISUAL_LINE); @@ -786,7 +795,7 @@ - (void)xvim_delete:(XVimMotion*)motion andYank:(BOOL)yank } [self insertText:@"" replacementRange:range]; if (toFirstNonBlank) { - newPos = [self.textStorage xvim_firstNonblankInLineAtIndex:range.location allowEOL:YES]; + newPos = [self.textStorage.xvim_buffer firstNonblankInLineAtIndex:range.location allowEOL:YES]; } else { newPos = range.location; } @@ -797,7 +806,7 @@ - (void)xvim_delete:(XVimMotion*)motion andYank:(BOOL)yank } [self _xvim_killSelection:sel]; - newPos = [self.textStorage xvim_indexOfLineNumber:sel.top column:sel.left]; + newPos = [self.textStorage.xvim_buffer indexOfLineNumber:sel.top column:sel.left]; } [self.xvimDelegate textView:self didDelete:self.lastYankedText withType:self.lastYankedType]; @@ -827,10 +836,10 @@ - (void)xvim_change:(XVimMotion*)motion{ self.cursorMode = CURSOR_MODE_INSERT; [self xvim_delete:motion andYank:YES]; if( motion.info->deleteLastLine){ - [self xvim_insertNewlineAboveLine:[self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]]; + [self xvim_insertNewlineAboveLine:[self.textStorage.xvim_buffer lineNumberAtIndex:self.insertionPoint]]; } else if( insertNewline ){ - [self xvim_insertNewlineAboveLine:[self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]]; + [self xvim_insertNewlineAboveLine:[self.textStorage.xvim_buffer lineNumberAtIndex:self.insertionPoint]]; }else{ } @@ -884,7 +893,7 @@ - (void)xvim_yank:(XVimMotion*)motion{ } else { XVimSelection sel = [self _xvim_selectedBlock]; - newPos = [self.textStorage xvim_indexOfLineNumber:sel.top column:sel.left]; + newPos = [self.textStorage.xvim_buffer indexOfLineNumber:sel.top column:sel.left]; [self _xvim_yankSelection:sel]; } @@ -925,7 +934,7 @@ - (void)xvim_put:(NSString*)text withType:(TEXT_TYPE)type afterCursor:(bool)afte [self xvim_insertNewlineBelowCurrentLine]; targetPos = self.insertionPoint; }else{ - targetPos= [self.textStorage xvim_startOfLine:self.insertionPoint]; + targetPos= [self.textStorage.xvim_buffer startOfLine:self.insertionPoint]; } insertionPointAfterPut = targetPos; for(NSUInteger i = 0; i < count ; i++ ){ @@ -944,26 +953,26 @@ - (void)xvim_put:(NSString*)text withType:(TEXT_TYPE)type afterCursor:(bool)afte } insertionPointAfterPut = self.insertionPoint; NSUInteger insertPos = self.insertionPoint; - NSUInteger column = [self.textStorage xvim_columnOfIndex:insertPos]; - NSUInteger startLine = [self.textStorage xvim_lineNumberAtIndex:insertPos]; + NSUInteger column = [self.textStorage.xvim_buffer columnOfIndex:insertPos]; + NSUInteger startLine = [self.textStorage.xvim_buffer lineNumberAtIndex:insertPos]; NSArray* lines = [text componentsSeparatedByString:@"\n"]; for( NSUInteger i = 0 ; i < lines.count ; i++){ NSString* line = [lines objectAtIndex:i]; NSUInteger targetLine = startLine + i; - NSUInteger head = [self.textStorage xvim_indexOfLineNumber:targetLine]; + NSUInteger head = [self.textStorage.xvim_buffer indexOfLineNumber:targetLine]; if( NSNotFound == head ){ NSAssert( targetLine != 0, @"This should not be happen"); [self xvim_insertNewlineBelowLine:targetLine-1]; - head = [self.textStorage xvim_indexOfLineNumber:targetLine]; + head = [self.textStorage.xvim_buffer indexOfLineNumber:targetLine]; } NSAssert( NSNotFound != head , @"Head of the target line must be found at this point"); // Find next insertion point - NSUInteger max = [self.textStorage xvim_numberOfColumnsInLineAtIndex:head]; + NSUInteger max = [self.textStorage.xvim_buffer numberOfColumnsInLineAtIndex:head]; NSAssert( max != NSNotFound , @"Should not be NSNotFound"); if( column > max ){ // If the line does not have enough column pad it with spaces - NSUInteger end = [self.textStorage xvim_endOfLine:head]; + NSUInteger end = [self.textStorage.xvim_buffer endOfLine:head]; [self _xvim_insertSpaces:column - max replacementRange:NSMakeRange(end, 0)]; } @@ -1076,7 +1085,7 @@ - (void)xvim_makeUpperCase:(XVimMotion*)motion{ } - (BOOL)xvim_replaceCharacters:(unichar)c count:(NSUInteger)count{ - NSUInteger end = [self.textStorage xvim_endOfLine:self.insertionPoint]; + NSUInteger end = [self.textStorage.xvim_buffer endOfLine:self.insertionPoint]; // Note : endOfLine may return one less than self.insertionPoint if self.insertionPoint is on newline if( NSNotFound == end ){ return NO; @@ -1095,19 +1104,19 @@ - (BOOL)xvim_replaceCharacters:(unichar)c count:(NSUInteger)count{ - (void)xvim_joinAtLineNumber:(NSUInteger)line{ BOOL needSpace = NO; - NSUInteger headOfLine = [self.textStorage xvim_indexOfLineNumber:line]; + NSUInteger headOfLine = [self.textStorage.xvim_buffer indexOfLineNumber:line]; if( headOfLine == NSNotFound){ return; } - NSUInteger tail = [self.textStorage xvim_endOfLine:headOfLine]; + NSUInteger tail = [self.textStorage.xvim_buffer endOfLine:headOfLine]; if( [self.textStorage isEOF:tail] ){ // This is the last line and nothing to join return; } // Check if we need to insert space between lines. - NSUInteger lastOfLine = [self.textStorage xvim_lastOfLine:headOfLine]; + NSUInteger lastOfLine = [self.textStorage.xvim_buffer lastOfLine:headOfLine]; if( lastOfLine != NSNotFound ){ // This is not blank line so we check if the last character is space or not . if( ![self.textStorage isWhitespace:lastOfLine] ){ @@ -1118,7 +1127,7 @@ - (void)xvim_joinAtLineNumber:(NSUInteger)line{ // Search in next line for the position to join(skip white spaces in next line) NSUInteger posToJoin = [self.textStorage nextLine:headOfLine column:0 count:1 option:MOTION_OPTION_NONE]; - posToJoin = [self.textStorage xvim_nextNonblankInLineAtIndex:posToJoin allowEOL:YES]; + posToJoin = [self.textStorage.xvim_buffer nextNonblankInLineAtIndex:posToJoin allowEOL:YES]; if (![self.textStorage isEOF:posToJoin] && [self.string characterAtIndex:posToJoin] == ')') { needSpace = NO; } @@ -1153,13 +1162,13 @@ - (void)xvim_join:(NSUInteger)count addSpace:(BOOL)addSpace{ [self xvim_joinAtLineNumber:line]; } } else { - NSTextStorage *ts = self.textStorage; - NSUInteger pos = [ts xvim_indexOfLineNumber:line]; + XVimBuffer *buffer = self.textStorage.xvim_buffer; + NSUInteger pos = [buffer indexOfLineNumber:line]; for (NSUInteger i = 0; i < count; i++) { - NSUInteger tail = [ts xvim_endOfLine:pos]; + NSUInteger tail = [buffer endOfLine:pos]; - if (tail != NSNotFound && ![ts isEOF:tail]) { + if (tail != NSNotFound && ![self.textStorage isEOF:tail]) { [self insertText:@"" replacementRange:NSMakeRange(tail, 1)]; [self xvim_moveCursor:tail preserveColumn:NO]; } @@ -1184,8 +1193,8 @@ - (void)xvim_filter:(XVimMotion*)motion{ filterRange = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:LINEWISE]; } else { XVimRange lines = [self _xvim_selectedLines]; - NSUInteger from = [self.textStorage xvim_indexOfLineNumber:lines.begin]; - NSUInteger to = [self.textStorage xvim_indexOfLineNumber:lines.end]; + NSUInteger from = [self.textStorage.xvim_buffer indexOfLineNumber:lines.begin]; + NSUInteger to = [self.textStorage.xvim_buffer indexOfLineNumber:lines.end]; filterRange = [self xvim_getOperationRangeFrom:from To:to Type:LINEWISE]; } @@ -1200,8 +1209,8 @@ - (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right return ; } - NSTextStorage *ts = self.textStorage; - NSUInteger shiftWidth = ts.xvim_indentWidth; + XVimBuffer *buffer = self.textStorage.xvim_buffer; + NSUInteger shiftWidth = buffer.indentWidth; NSUInteger column = 0; XVimRange lines; BOOL blockMode = NO; @@ -1212,7 +1221,7 @@ - (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right if (to.end == NSNotFound) { return; } - lines = XVimMakeRange([ts xvim_lineNumberAtIndex:to.begin], [ts xvim_lineNumberAtIndex:to.end]); + lines = XVimMakeRange([buffer lineNumberAtIndex:to.begin], [buffer lineNumberAtIndex:to.end]); } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { lines = [self _xvim_selectedLines]; shiftWidth *= motion.count; @@ -1225,12 +1234,12 @@ - (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right shiftWidth *= motion.count; } - NSUInteger pos = [ts xvim_indexOfLineNumber:lines.begin column:0]; + NSUInteger pos = [buffer indexOfLineNumber:lines.begin column:0]; [undoManager setGroupsByEvent:NO]; [undoManager beginUndoGrouping]; if (!blockMode) { - NSUInteger col = [ts xvim_columnOfIndex:[ts xvim_firstNonblankInLineAtIndex:pos allowEOL:YES]]; + NSUInteger col = [buffer columnOfIndex:[buffer firstNonblankInLineAtIndex:pos allowEOL:YES]]; [self xvim_registerPositionForUndo:XVimMakePosition(lines.begin, col)]; } @@ -1244,9 +1253,9 @@ - (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right } if (blockMode) { - pos = [ts xvim_indexOfLineNumber:lines.begin column:column]; + pos = [buffer indexOfLineNumber:lines.begin column:column]; } else { - pos = [ts xvim_firstNonblankInLineAtIndex:pos allowEOL:YES]; + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; } [self xvim_moveCursor:pos preserveColumn:NO]; @@ -1265,7 +1274,7 @@ - (void)xvim_shiftLeft:(XVimMotion*)motion{ } - (void)xvim_insertText:(NSString*)str line:(NSUInteger)line column:(NSUInteger)column{ - NSUInteger pos = [self.textStorage xvim_indexOfLineNumber:line column:column]; + NSUInteger pos = [self.textStorage.xvim_buffer indexOfLineNumber:line column:column]; if( pos == NSNotFound ){ return; } @@ -1274,29 +1283,29 @@ - (void)xvim_insertText:(NSString*)str line:(NSUInteger)line column:(NSUInteger) - (void)xvim_insertNewlineBelowLine:(NSUInteger)line{ NSAssert( line != 0, @"line number starts from 1"); - NSUInteger pos = [self.textStorage xvim_indexOfLineNumber:line]; + NSUInteger pos = [self.textStorage.xvim_buffer indexOfLineNumber:line]; if( NSNotFound == pos ){ return; } - pos = [self.textStorage xvim_endOfLine:pos]; + pos = [self.textStorage.xvim_buffer endOfLine:pos]; [self insertText:@"\n" replacementRange:NSMakeRange(pos ,0)]; [self xvim_moveCursor:pos+1 preserveColumn:NO]; [self xvim_syncState]; } - (void)xvim_insertNewlineBelowCurrentLine{ - [self xvim_insertNewlineBelowLine:[self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]]; + [self xvim_insertNewlineBelowLine:[self.textStorage.xvim_buffer lineNumberAtIndex:self.insertionPoint]]; } - (void)xvim_insertNewlineBelowCurrentLineWithIndent{ - NSUInteger tail = [self.textStorage xvim_endOfLine:self.insertionPoint]; + NSUInteger tail = [self.textStorage.xvim_buffer endOfLine:self.insertionPoint]; [self setSelectedRange:NSMakeRange(tail,0)]; [self insertNewline:self]; } - (void)xvim_insertNewlineAboveLine:(NSUInteger)line{ NSAssert( line != 0, @"line number starts from 1"); - NSUInteger pos = [self.textStorage xvim_indexOfLineNumber:line]; + NSUInteger pos = [self.textStorage.xvim_buffer indexOfLineNumber:line]; if( NSNotFound == pos ){ return; } @@ -1309,11 +1318,11 @@ - (void)xvim_insertNewlineAboveLine:(NSUInteger)line{ } - (void)xvim_insertNewlineAboveCurrentLine{ - [self xvim_insertNewlineAboveLine:[self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]]; + [self xvim_insertNewlineAboveLine:[self.textStorage.xvim_buffer lineNumberAtIndex:self.insertionPoint]]; } - (void)xvim_insertNewlineAboveCurrentLineWithIndent{ - NSUInteger head = [self.textStorage xvim_firstOfLine:self.insertionPoint]; + NSUInteger head = [self.textStorage.xvim_buffer firstOfLine:self.insertionPoint]; if( NSNotFound == head ){ head = self.insertionPoint; } @@ -1339,6 +1348,7 @@ - (void)xvim_insertNewlineBelowAndInsertWithIndent{ - (void)xvim_insert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column blockLines:(XVimRange *)lines{ NSTextStorage *ts = self.textStorage; + XVimBuffer *buffer = ts.xvim_buffer; if (column) *column = NSNotFound; if (lines) *lines = XVimMakeRange(NSNotFound, NSNotFound); @@ -1356,14 +1366,14 @@ - (void)xvim_insert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column bl [self _xvim_killSelection:sel]; /* falltrhough */ case XVIM_INSERT_DEFAULT: - self.insertionPoint = [ts xvim_indexOfLineNumber:sel.top column:sel.left]; + self.insertionPoint = [buffer indexOfLineNumber:sel.top column:sel.left]; if (column) *column = sel.left; break; case XVIM_INSERT_APPEND: if (sel.right != XVimSelectionEOL) { sel.right++; } - self.insertionPoint = [ts xvim_indexOfLineNumber:sel.top column:sel.right]; + self.insertionPoint = [buffer indexOfLineNumber:sel.top column:sel.right]; if (column) *column = sel.right; break; default: @@ -1374,7 +1384,7 @@ - (void)xvim_insert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column bl NSUInteger pos = self.insertionPoint; switch (mode) { case XVIM_INSERT_APPEND_EOL: - self.insertionPoint = [ts xvim_endOfLine:pos]; + self.insertionPoint = [buffer endOfLine:pos]; break; case XVIM_INSERT_APPEND: NSAssert(self.cursorMode == CURSOR_MODE_COMMAND, @"self.cursorMode shoud be CURSOR_MODE_COMMAND"); @@ -1383,7 +1393,7 @@ - (void)xvim_insert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column bl } break; case XVIM_INSERT_BEFORE_FIRST_NONBLANK: - self.insertionPoint = [ts xvim_firstNonblankInLineAtIndex:pos allowEOL:YES]; + self.insertionPoint = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; break; default: NSAssert(false, @"unreachable"); @@ -1409,7 +1419,7 @@ - (BOOL)xvim_incrementNumber:(int64_t)offset{ range = [self xvim_numberAtIndex:ip]; if (range.location == NSNotFound) { - NSUInteger pos = [self.textStorage xvim_nextDigitInLine:ip]; + NSUInteger pos = [self.textStorage.xvim_buffer nextDigitInLine:ip]; if (pos == NSNotFound) { return NO; } @@ -1450,6 +1460,7 @@ - (void)xvim_blockInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint) NSMutableString *buf = nil; NSTextStorage *ts; NSUInteger tabWidth; + XVimBuffer *buffer; if (count == 0 || lines.begin > lines.end || text.length == 0) { return; @@ -1466,24 +1477,25 @@ - (void)xvim_blockInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint) } ts = self.textStorage; - tabWidth = ts.xvim_tabWidth; + buffer = ts.xvim_buffer; + tabWidth = buffer.tabWidth; for (NSUInteger line = lines.begin; line <= lines.end; line++) { - NSUInteger pos = [ts xvim_indexOfLineNumber:line column:column]; + NSUInteger pos = [buffer indexOfLineNumber:line column:column]; if (column != XVimSelectionEOL && [ts isEOL:pos]) { if (mode == XVIM_INSERT_SPACES && column == 0) { continue; } - if ([ts xvim_columnOfIndex:pos] < column) { + if ([buffer columnOfIndex:pos] < column) { if (mode != XVIM_INSERT_APPEND) { continue; } - [self _xvim_insertSpaces:column - [ts xvim_columnOfIndex:pos] replacementRange:NSMakeRange(pos, 0)]; + [self _xvim_insertSpaces:column - [buffer columnOfIndex:pos] replacementRange:NSMakeRange(pos, 0)]; } } if (tabWidth && [self.xvim_string characterAtIndex:pos] == '\t') { - NSUInteger col = [ts xvim_columnOfIndex:pos]; + NSUInteger col = [buffer columnOfIndex:pos]; if (col < column) { [self _xvim_insertSpaces:tabWidth - (col % tabWidth) replacementRange:NSMakeRange(pos, 1)]; @@ -1507,7 +1519,7 @@ - (void)xvim_sortLinesFrom:(NSUInteger)line1 to:(NSUInteger)line2 withOptions:(X line2 = tmp; } - NSRange characterRange = [self.textStorage xvim_indexRangeForLines:NSMakeRange(line1, line2 - line1 + 1)]; + NSRange characterRange = [self.textStorage.xvim_buffer indexRangeForLines:NSMakeRange(line1, line2 - line1 + 1)]; NSString *str = [[self xvim_string] substringWithRange:characterRange]; NSMutableArray *lines = [[[str componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] mutableCopy] autorelease]; @@ -1637,8 +1649,8 @@ - (void)xvim_scroll:(CGFloat)ratio count:(NSUInteger)count{ [[scrollView contentView] scrollToPoint:scrollPoint]; [scrollView reflectScrolledClipView:[scrollView contentView]]; - - cursorIndexAfterScroll = [self.textStorage xvim_firstNonblankInLineAtIndex:cursorIndexAfterScroll allowEOL:YES]; + + cursorIndexAfterScroll = [self.textStorage.xvim_buffer firstNonblankInLineAtIndex:cursorIndexAfterScroll allowEOL:YES]; [self xvim_moveCursor:cursorIndexAfterScroll preserveColumn:NO]; [self xvim_syncState]; @@ -1972,7 +1984,7 @@ - (NSRect)xvim_boundingRectForGlyphIndex:(NSUInteger)glyphIndex { */ -(NSArray*)xvim_placeholdersInLine:(NSUInteger)position{ NSMutableArray* placeholders = [[NSMutableArray alloc] initWithCapacity:2]; - NSUInteger p = [self.textStorage xvim_firstOfLine:position]; + NSUInteger p = [self.textStorage.xvim_buffer firstOfLine:position]; for(NSUInteger curPos = p; curPos < [[self xvim_string] length]; curPos++){ NSRange retval = [(DVTCompletingTextView*)self rangeOfPlaceholderFromCharacterIndex:curPos forward:YES wrap:NO limit:50]; @@ -2038,7 +2050,7 @@ - (void)xvim_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve{ } if( !preserve ){ - self.preservedColumn = [self.textStorage xvim_columnOfIndex:self.insertionPoint]; + self.preservedColumn = [self.textStorage.xvim_buffer columnOfIndex:self.insertionPoint]; } DEBUG_LOG(@"New Insertion Point:%d Preserved Column:%d", self.insertionPoint, self.preservedColumn); @@ -2121,11 +2133,12 @@ - (NSArray*)xvim_selectedRanges{ NSMutableArray *rangeArray = [[[NSMutableArray alloc] init] autorelease]; NSTextStorage *ts = self.textStorage; + XVimBuffer *buffer = ts.xvim_buffer; XVimSelection sel = [self _xvim_selectedBlock]; for (NSUInteger line = sel.top; line <= sel.bottom; line++) { - NSUInteger begin = [ts xvim_indexOfLineNumber:line column:sel.left]; - NSUInteger end = [ts xvim_indexOfLineNumber:line column:sel.right]; + NSUInteger begin = [buffer indexOfLineNumber:line column:sel.left]; + NSUInteger end = [buffer indexOfLineNumber:line column:sel.right]; if ([ts isEOF:begin]) { continue; @@ -2147,127 +2160,129 @@ - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion{ NSUInteger tmpPos = NSNotFound; NSUInteger start = NSNotFound; NSUInteger starts_end = NSNotFound; + NSTextStorage *ts = self.textStorage; + XVimBuffer *buffer = ts.xvim_buffer; switch (motion.motion) { case MOTION_NONE: // Do nothing break; case MOTION_FORWARD: - end = [self.textStorage next:begin count:motion.count option:motion.option info:motion.info]; + end = [ts next:begin count:motion.count option:motion.option info:motion.info]; break; case MOTION_BACKWARD: - end = [self.textStorage prev:begin count:motion.count option:motion.option ]; + end = [ts prev:begin count:motion.count option:motion.option ]; break; case MOTION_WORD_FORWARD: - end = [self.textStorage wordsForward:begin count:motion.count option:motion.option info:motion.info]; + end = [ts wordsForward:begin count:motion.count option:motion.option info:motion.info]; break; case MOTION_WORD_BACKWARD: - end = [self.textStorage wordsBackward:begin count:motion.count option:motion.option]; + end = [ts wordsBackward:begin count:motion.count option:motion.option]; break; case MOTION_END_OF_WORD_FORWARD: - end = [self.textStorage endOfWordsForward:begin count:motion.count option:motion.option]; + end = [ts endOfWordsForward:begin count:motion.count option:motion.option]; break; case MOTION_END_OF_WORD_BACKWARD: - end = [self.textStorage endOfWordsBackward:begin count:motion.count option:motion.option]; + end = [ts endOfWordsBackward:begin count:motion.count option:motion.option]; break; case MOTION_LINE_FORWARD: - end = [self.textStorage nextLine:begin column:self.preservedColumn count:motion.count option:motion.option]; + end = [ts nextLine:begin column:self.preservedColumn count:motion.count option:motion.option]; break; case MOTION_LINE_BACKWARD: - end = [self.textStorage prevLine:begin column:self.preservedColumn count:motion.count option:motion.option]; + end = [ts prevLine:begin column:self.preservedColumn count:motion.count option:motion.option]; break; case MOTION_BEGINNING_OF_LINE: - end = [self.textStorage xvim_startOfLine:begin]; + end = [ts.xvim_buffer startOfLine:begin]; if( end == NSNotFound){ end = current; } break; case MOTION_END_OF_LINE: - tmpPos = [self.textStorage nextLine:begin column:0 count:motion.count-1 option:MOTION_OPTION_NONE]; - end = [self.textStorage xvim_endOfLine:tmpPos]; + tmpPos = [ts nextLine:begin column:0 count:motion.count-1 option:MOTION_OPTION_NONE]; + end = [ts.xvim_buffer endOfLine:tmpPos]; if( end == NSNotFound){ end = tmpPos; } break; case MOTION_SENTENCE_FORWARD: - end = [self.textStorage sentencesForward:begin count:motion.count option:motion.option]; + end = [ts sentencesForward:begin count:motion.count option:motion.option]; break; case MOTION_SENTENCE_BACKWARD: - end = [self.textStorage sentencesBackward:begin count:motion.count option:motion.option]; + end = [ts sentencesBackward:begin count:motion.count option:motion.option]; break; case MOTION_PARAGRAPH_FORWARD: - end = [self.textStorage paragraphsForward:begin count:motion.count option:motion.option]; + end = [ts paragraphsForward:begin count:motion.count option:motion.option]; break; case MOTION_PARAGRAPH_BACKWARD: - end = [self.textStorage paragraphsBackward:begin count:motion.count option:motion.option]; + end = [ts paragraphsBackward:begin count:motion.count option:motion.option]; break; case MOTION_NEXT_CHARACTER: - end = [self.textStorage nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; break; case MOTION_PREV_CHARACTER: - end = [self.textStorage prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; break; case MOTION_TILL_NEXT_CHARACTER: - end = [self.textStorage nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; if(end != NSNotFound){ end--; } break; case MOTION_TILL_PREV_CHARACTER: - end = [self.textStorage prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; if(end != NSNotFound){ end++; } break; case MOTION_NEXT_FIRST_NONBLANK: - end = [self.textStorage nextLine:begin column:0 count:motion.count option:motion.option]; - tmpPos = [self.textStorage xvim_nextNonblankInLineAtIndex:end allowEOL:NO]; + end = [ts nextLine:begin column:0 count:motion.count option:motion.option]; + tmpPos = [ts.xvim_buffer nextNonblankInLineAtIndex:end allowEOL:NO]; if( NSNotFound != tmpPos ){ end = tmpPos; } break; case MOTION_PREV_FIRST_NONBLANK: - end = [self.textStorage prevLine:begin column:0 count:motion.count option:motion.option]; - tmpPos = [self.textStorage xvim_nextNonblankInLineAtIndex:end allowEOL:NO]; + end = [ts prevLine:begin column:0 count:motion.count option:motion.option]; + tmpPos = [ts.xvim_buffer nextNonblankInLineAtIndex:end allowEOL:NO]; if( NSNotFound != tmpPos ){ end = tmpPos; } break; case MOTION_FIRST_NONBLANK: - end = [self.textStorage xvim_firstNonblankInLineAtIndex:begin allowEOL:NO]; + end = [ts.xvim_buffer firstNonblankInLineAtIndex:begin allowEOL:NO]; break; case MOTION_LINENUMBER: - end = [self.textStorage xvim_indexOfLineNumber:motion.line column:self.preservedColumn]; + end = [ts.xvim_buffer indexOfLineNumber:motion.line column:self.preservedColumn]; if( NSNotFound == end ){ - end = [self.textStorage xvim_indexOfLineNumber:[self.textStorage xvim_numberOfLines] column:self.preservedColumn]; + end = [buffer indexOfLineNumber:[buffer numberOfLines] column:self.preservedColumn]; } break; case MOTION_PERCENT: - end = [self.textStorage xvim_indexOfLineNumber:1 + ([self.textStorage xvim_numberOfLines]-1) * motion.count/100]; + end = [buffer indexOfLineNumber:1 + ([buffer numberOfLines]-1) * motion.count/100]; break; case MOTION_NEXT_MATCHED_ITEM: - end = [self.textStorage positionOfMatchedPair:begin]; + end = [ts positionOfMatchedPair:begin]; break; case MOTION_LASTLINE: - end = [self.textStorage xvim_indexOfLineNumber:[self.textStorage xvim_numberOfLines] column:self.preservedColumn]; + end = [buffer indexOfLineNumber:[buffer numberOfLines] column:self.preservedColumn]; break; case MOTION_HOME: - end = [self.textStorage xvim_firstNonblankInLineAtIndex:[self.textStorage xvim_indexOfLineNumber:[self xvim_lineNumberFromTop:motion.count]] allowEOL:YES]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:[self xvim_lineNumberFromTop:motion.count]] allowEOL:YES]; break; case MOTION_MIDDLE: - end = [self.textStorage xvim_firstNonblankInLineAtIndex:[self.textStorage xvim_indexOfLineNumber:[self xvim_lineNumberAtMiddle]] allowEOL:YES]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:[self xvim_lineNumberAtMiddle]] allowEOL:YES]; break; case MOTION_BOTTOM: - end = [self.textStorage xvim_firstNonblankInLineAtIndex:[self.textStorage xvim_indexOfLineNumber:[self xvim_lineNumberFromBottom:motion.count]] allowEOL:YES]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:[self xvim_lineNumberFromBottom:motion.count]] allowEOL:YES]; break; case MOTION_SEARCH_FORWARD: - end = [self.textStorage searchRegexForward:motion.regex from:self.insertionPoint count:motion.count option:motion.option].location; + end = [ts searchRegexForward:motion.regex from:self.insertionPoint count:motion.count option:motion.option].location; break; case MOTION_SEARCH_BACKWARD: - end = [self.textStorage searchRegexBackward:motion.regex from:self.insertionPoint count:motion.count option:motion.option].location; + end = [ts searchRegexBackward:motion.regex from:self.insertionPoint count:motion.count option:motion.option].location; break; case TEXTOBJECT_WORD: - range = [self.textStorage currentWord:begin count:motion.count option:motion.option]; + range = [ts currentWord:begin count:motion.count option:motion.option]; break; case TEXTOBJECT_BRACES: range = xv_current_block([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '{', '}'); @@ -2276,10 +2291,10 @@ - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion{ // Not supported start = self.insertionPoint; if(start != 0){ - start = [self.textStorage paragraphsBackward:self.insertionPoint count:1 option:MOPT_PARA_BOUND_BLANKLINE]; + start = [ts paragraphsBackward:self.insertionPoint count:1 option:MOPT_PARA_BOUND_BLANKLINE]; } - starts_end = [self.textStorage paragraphsForward:start count:1 option:MOPT_PARA_BOUND_BLANKLINE]; - end = [self.textStorage paragraphsForward:self.insertionPoint count:motion.count option:MOPT_PARA_BOUND_BLANKLINE]; + starts_end = [ts paragraphsForward:start count:1 option:MOPT_PARA_BOUND_BLANKLINE]; + end = [ts paragraphsForward:self.insertionPoint count:motion.count option:MOPT_PARA_BOUND_BLANKLINE]; if(starts_end != end){ start = starts_end; @@ -2311,7 +2326,7 @@ - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion{ range = xv_current_block([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '[', ']'); break; case MOTION_LINE_COLUMN: - end = [self.textStorage xvim_indexOfLineNumber:motion.line column:motion.column]; + end = [buffer indexOfLineNumber:motion.line column:motion.column]; if( NSNotFound == end ){ end = current; } @@ -2362,11 +2377,11 @@ - (NSRange)xvim_getOperationRangeFrom:(NSUInteger)from To:(NSUInteger)to Type:(M }else if( type == CHARACTERWISE_INCLUSIVE ){ // Nothing special }else if( type == LINEWISE ){ - to = [self.textStorage xvim_endOfLine:to]; + to = [self.textStorage.xvim_buffer endOfLine:to]; if( [self.textStorage isEOF:to] ){ to--; } - NSUInteger head = [self.textStorage xvim_firstOfLine:from]; + NSUInteger head = [self.textStorage.xvim_buffer firstOfLine:from]; if( NSNotFound != head ){ from = head; } @@ -2399,7 +2414,7 @@ - (void)xvim_indentCharacterRange:(NSRange)range{ // This is used by scrollBottom,Top,Center as a common method - (void)xvim_scrollCommon_moveCursorPos:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb{ if( lineNumber != 0 ){ - NSUInteger pos = [self.textStorage xvim_indexOfLineNumber:lineNumber]; + NSUInteger pos = [self.textStorage.xvim_buffer indexOfLineNumber:lineNumber]; if( NSNotFound == pos ){ pos = self.textStorage.length; } @@ -2407,7 +2422,7 @@ - (void)xvim_scrollCommon_moveCursorPos:(NSUInteger)lineNumber firstNonblank:(BO [self xvim_syncState]; } if( fnb ){ - NSUInteger pos = [self.textStorage xvim_firstNonblankInLineAtIndex:self.insertionPoint allowEOL:YES]; + NSUInteger pos = [self.textStorage.xvim_buffer firstNonblankInLineAtIndex:self.insertionPoint allowEOL:YES]; [self xvim_moveCursor:pos preserveColumn:NO]; [self xvim_syncState]; } @@ -2424,14 +2439,14 @@ - (NSUInteger)xvim_lineNumberFromBottom:(NSUInteger)count { // L NSPoint bottom = [[scrollView contentView] bounds].origin; // This calculate the position of the bottom line and substruct height of "count" of lines to upwards bottom.y += [[scrollView contentView] bounds].size.height - (NSHeight(glyphRect) / 2.0f) - (NSHeight(glyphRect) * (count-1)); - return [self.textStorage xvim_lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:bottom]]; + return [self.textStorage.xvim_buffer lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:bottom]]; } - (NSUInteger)xvim_lineNumberAtMiddle{ NSScrollView *scrollView = [self enclosingScrollView]; NSPoint center = [[scrollView contentView] bounds].origin; center.y += [[scrollView contentView] bounds].size.height / 2; - return [self.textStorage xvim_lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:center]]; + return [self.textStorage.xvim_buffer lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:center]]; } - (NSUInteger)xvim_lineNumberFromTop:(NSUInteger)count{ @@ -2445,7 +2460,7 @@ - (NSUInteger)xvim_lineNumberFromTop:(NSUInteger)count{ NSPoint top = [[scrollView contentView] bounds].origin; // Add height of "count" of lines to downwards top.y += (NSHeight(glyphRect) / 2.0f) + (NSHeight(glyphRect) * (count-1)); - return [self.textStorage xvim_lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:top]]; + return [self.textStorage.xvim_buffer lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:top]]; } - (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward{ diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 1fe1de52..d8828b86 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -6,7 +6,24 @@ // // -#import "XVimTextStoring.h" +#import + +#pragma mark Macros + +#ifdef DEBUG +// The methods here often take index as current interest position and index can be at EOF +// The following macros asserts the range of index. +// WITH_EOF permits the index at EOF position. +// WITHOUT_EOF doesn't permit the index at EOF position. +#define ASSERT_VALID_RANGE_WITH_EOF(x) NSAssert( x <= [self length] || [self length] == 0, @"index can not exceed the length of string. TextLength:%lu SpecifiedIndex:%lu", (long)[self length], x) + +// Some methods assume that "index" is at valid cursor position in Normal mode. +// See isValidCursorPosition's description the condition of the valid cursor position. +#define ASSERT_VALID_CURSOR_POS(x) NSAssert( [self isValidCursorPosition:x], @"index can not be invalid cursor position" ) +#else +#define ASSERT_VALID_RANGE_WITH_EOF(x) +#define ASSERT_VALID_CURSOR_POS(x) +#endif @protocol XVimUndoing; @@ -29,6 +46,60 @@ * the XVimBuffer in the rigth places. * This isn't urgent though because IDE uses an NSDocument subclass * that owns its textStorage, hence their lifetime is tied. + * + ****************************************************************************** + * + * Note that the terms here do not have the usual Cocoah meaning + * + * "Character" + * Character is a one unichar value. (any value including tabs,spaces) + * + * "index" + * This is the 0-based location of a given character within -xvim_string. + * + * "Position" + * This is an XVimPosition (line + column) + * + * "EOF" + * EOF is the position of the end of the document (-length), + * so for a text of "abc", EOF is just after the 'c', at position 3. + * + * What we have to think about is that a cursor can be at EOF, + * but -[xvim_string characterAtIndex:] at this position raises. + * + * We have to be careful about it when computing motions effects. + * + * "Newline" + * Newline is defined as "unichar determined by isNewline function". + * Usually "\n" or "\r". + * + * "Line" + * Line is a sequence of characters terminated by newline or EOF. + * "Line" includes the last newline character. + * Line numbers start at 1 + * + * "Blankline" + * Blankline is a line which has only newline or EOF. + * In other words, it is newline character or EOF after newline character. + * + * "Last of Line(LOL)" + * Last of line is the last character of a line EXCLUDING newline character. + * This means that blankline does NOT have an Last of line. + * + * "First of Line(FOL)" + * First of line is the first character of a line excluding newline character. + * This means that blankline does NOT have a First of line. + * + * "First Nonblank of Line" + * First Nonblank of Line is the first printable character in a line. + * + * "End of Line(EOL)" + * End of Line is newline or EOF character at the end of a line. + * A line always has an EOL. + * + * "Beginning of Line (BOL)" + * First character of a line including newline and EOF + * */ @interface XVimBuffer : NSObject @@ -40,6 +111,163 @@ + (XVimBuffer *)makeBufferForDocument:(NSDocument *)document textStorage:(NSTextStorage *)textStorage; +#pragma mark Properties + +@property (nonatomic, readonly) NSString *string; +@property (nonatomic, readonly) NSUInteger numberOfLines; +@property (nonatomic, readonly) NSUInteger length; +@property (nonatomic, readonly) NSUInteger tabWidth; +@property (nonatomic, readonly) NSUInteger indentWidth; + +#pragma mark Converting between Indexes and Line Numbers + +/** @brief returns the index range for the given line number + * + * @param[in] num + * The line number + * @param[out] newLineLength + * The number of characters after the returned range forming the end of line + * @returns + * - {NSNotFound, 0} if the index is beyond the end of the document. + * - the range of indexes forming the line, excluding trailing newLine characters + */ +- (NSRange)indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUInteger *)newLineLength; + +/** @brief returns the index range for the given line range + * + * @param[in] range the line range. + * + * @returns + * the range of indexes forming the line, including trailing newLine characters + * Never returns NSNotFound + */ +- (NSRange)indexRangeForLines:(NSRange)range; + +/** @brief returns the line range around the given index + * + * @param[in] index + * The index within -xvim_string + * @param[out] newLineLength + * The number of characters after the returned range forming the end of line + * @returns + * the range of indexes forming the line, exclugint trailing newLine characters + * Note that if the index is within a CRLF for example, the range may end before index + */ +- (NSRange)indexRangeForLineAtIndex:(NSUInteger)index newLineLength:(NSUInteger *)newLineLength; + +/** @brief starting position of line @a num within -xvim_string. + * @returns the starting index for that line number or NSNotFound. + * @see -xvim_indexRangeForLineNumber:newLineLength: + */ +- (NSUInteger)indexOfLineNumber:(NSUInteger)num; + +/** @brief get the line number of a given position. + * + * @returns + * the line number of specified index. + * This never returns NSNotFound. + */ +- (NSUInteger)lineNumberAtIndex:(NSUInteger)index; + +#pragma mark Converting between Indexes and Line Numbers + Columns + +/** @brief returns the column number of \a index within the line. + * + * Column numbers starts at 0. + * + * This never returns NSNotFound. + */ +- (NSUInteger)columnOfIndex:(NSUInteger)index; + +/** @brief returns number of columns for the line containing \a index. + * + * If the specified line does not exist in the current document it returns NSNotFound + */ +- (NSUInteger)numberOfColumnsInLineAtIndex:(NSUInteger)index; + +/** @brief returns the index for the given line number and column. + * + * @returns + * NSNotFound if \a num exceeds the number of lines in the document + * If \a column is larger than the number of columns in that line, + * it returns the index of the endOfLine for that line + */ +- (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column; + +#pragma mark Searching particular positions on the current line + +/** @brief position of the first character of the line containing \a index. + * + * @param index the index to search backwards from + */ +- (NSUInteger)startOfLine:(NSUInteger)index; // never returns NSNotFound + +/** @brief returns the firstOfLine for the line containing \a index. + * + * If the line is blank, this returns NSNotFound + * else this is the same as -beginningOfLine: + */ +- (NSUInteger)firstOfLine:(NSUInteger)index; // May return NSNotFound + +/** @brief position of the end of the line containing \a index. + * + * @param index the index to search from + * + * @returns + * the position of the end of the line. + * end of the line is either: + * - a newline character at the end of the line + * - end of the document + * + * Note that for files with \r\n if index points to \n + * this returns a position before index. + */ +- (NSUInteger)endOfLine:(NSUInteger)index; // never returns NSNotFound + +/** @brief returns the lastOfLine for the line containing \a index. + * + * If the line is blank, this returns NSNotFound + * else this is the same as -endOfLine:index - 1 + */ +- (NSUInteger)lastOfLine:(NSUInteger)index; // May return NSNotFound + +/** @brief returns the next non blank position on the same line. + * + * @param index the index to search from + * @param allowEOL whether reaching EOL is allowed or not + * + * @returns + * the position of the first non blank character, starting at index. + * + * if \a allowEOL is NO and that no non blank character is found, + * this returns NSNotFound. + */ +- (NSUInteger)nextNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL; + +/** @brief returns the first non blank character on the line, possibly EOL. + * + * @param index searches on the line containing that index. + * + * @returns + * the position of the first non blank character + * on the line containing \a index. + * + * if \a allowEOL is NO and that no non blank character is found, + * this returns NSNotFound. + */ +- (NSUInteger)firstNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL; + +/** @brief returns the next digit position on the same line. + * + * @param index the index to search from + * + * @returns + * the position of the first decimal digit character starting at \a index + * + * this returns NSNotFound if none is found. + */ +- (NSUInteger)nextDigitInLine:(NSUInteger)index; + #pragma mark Support for modifications - (void)undoRedo:(id)op; diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index 1da94607..d2a2aa1f 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -7,14 +7,31 @@ // #import +#import "XVimStringBuffer.h" #import "XVimBuffer.h" #import "XVimUndo.h" +#import "XVimTextStoring.h" static char const * const XVIM_KEY_BUFFER = "xvim_buffer"; +NS_INLINE BOOL isNewline(unichar ch) +{ + return [[NSCharacterSet newlineCharacterSet] characterIsMember:ch]; +} + @implementation XVimBuffer { NSDocument *__unsafe_unretained _document; NSTextStorage *__unsafe_unretained _textStorage; + + struct { + unsigned has_xvim_string : 1; + unsigned has_xvim_numberOfLines : 1; + unsigned has_xvim_tabWidth : 1; + unsigned has_xvim_indentWidth : 1; + unsigned has_xvim_indexRangeForLineNumber : 1; + unsigned has_xvim_indexRangeForLines : 1; + unsigned has_xvim_lineNumberAtIndex : 1; + } _flags; } @synthesize document = _document; @synthesize textStorage = _textStorage; @@ -32,6 +49,18 @@ - (instancetype)initWithDocument:(NSDocument *)document _textStorage = textStorage; objc_setAssociatedObject(document, XVIM_KEY_BUFFER, self, OBJC_ASSOCIATION_RETAIN); objc_setAssociatedObject(textStorage, XVIM_KEY_BUFFER, self, OBJC_ASSOCIATION_RETAIN); + + if ([_textStorage conformsToProtocol:@protocol(XVimTextStoring)]) { +#define CHECK(k, sel) _flags.has_xvim_##k = (bool)[_textStorage respondsToSelector:sel] + CHECK(string, @selector(xvim_string)); + CHECK(numberOfLines, @selector(xvim_numberOfLines)); + CHECK(tabWidth, @selector(xvim_tabWidth)); + CHECK(indentWidth, @selector(xvim_indentWidth)); + CHECK(indexRangeForLineNumber, @selector(xvim_indexRangeForLineNumber:newLineLength:)); + CHECK(indexRangeForLines, @selector(xvim_indexRangeForLines:)); + CHECK(lineNumberAtIndex, @selector(xvim_lineNumberAtIndex:)); +#undef CHECK + } } return self; } @@ -46,6 +75,337 @@ + (XVimBuffer *)makeBufferForDocument:(NSDocument *)document return [[[[self class] alloc] initWithDocument:document textStorage:textStorage] autorelease]; } +#pragma mark Properties +#define _XVimTextStorage ((NSTextStorage *)_textStorage) + +- (NSString *)string +{ + if (_flags.has_xvim_string) { + return _XVimTextStorage.xvim_string; + } + return _textStorage.string; +} + +- (NSUInteger)numberOfLines +{ + if (_flags.has_xvim_numberOfLines) { + return _XVimTextStorage.xvim_numberOfLines; + } + return [self lineNumberAtIndex:self.length]; +} + +- (NSUInteger)length +{ + return _textStorage.length; +} + +- (NSUInteger)tabWidth +{ + if (_flags.has_xvim_tabWidth) { + return _XVimTextStorage.xvim_tabWidth; + } + return 8; +} + +- (NSUInteger)indentWidth +{ + if (_flags.has_xvim_indentWidth) { + return _XVimTextStorage.xvim_indentWidth; + } + return 8; +} + +#pragma mark Converting between Indexes and Line Numbers + +- (NSRange)indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUInteger *)newLineLength +{ + NSAssert(num > 0, @"line number starts at 1"); + + if (_flags.has_xvim_indexRangeForLineNumber) { + return [_XVimTextStorage xvim_indexRangeForLineNumber:num newLineLength:newLineLength]; + } + + // TODO: we may need to keep track line number and position by hooking insertText: method. + // FIXME: this code is actually never called in XVim for XCode, it probably has bugs, it's not tested + + NSString *string = self.string; + NSUInteger length = self.length; + NSUInteger lineNum = 0, end = 0, contentsEnd; + + do { + [string getLineStart:NULL end:&end contentsEnd:&contentsEnd forRange:NSMakeRange(end, 0)]; + lineNum++; + if (lineNum == num) { + if (newLineLength) *newLineLength = end - contentsEnd; + return NSMakeRange(end, contentsEnd - end); + } + } while (end < length); + + if (newLineLength) *newLineLength = 0; + + // we have a last empty line after \n + if (contentsEnd < end) { + lineNum++; + if (lineNum == num) { + return NSMakeRange(end, 0); + } + } + + return NSMakeRange(NSNotFound, 0); +} + +- (NSRange)indexRangeForLines:(NSRange)range +{ + NSAssert(range.location > 0, @"line number starts at 1"); + + if (_flags.has_xvim_indexRangeForLines) { + return [_XVimTextStorage xvim_indexRangeForLines:range]; + } + + // TODO: we may need to keep track line number and position by hooking insertText: method. + // FIXME: this code is actually never called in XVim for XCode, it probably has bugs, it's not tested + NSString *string = self.string; + NSUInteger length = self.length, start; + NSUInteger lineNum = 0, end = 0, contentsEnd; + + do { + [string getLineStart:NULL end:&end contentsEnd:&contentsEnd forRange:NSMakeRange(end, 0)]; + lineNum++; + if (lineNum == range.location) { + start = end; + } + if (lineNum == NSMaxRange(range)) { + return NSMakeRange(start, end - start); + } + } while (end < length); + + // we have a last empty line after \n + if (contentsEnd < end) { + lineNum++; + if (lineNum == range.location) { + start = end; + } + if (lineNum == NSMaxRange(range)) { + return NSMakeRange(start, end - start); + } + } + + return NSMakeRange(0, length); +} + +- (NSRange)indexRangeForLineAtIndex:(NSUInteger)index newLineLength:(NSUInteger *)newLineLength +{ + ASSERT_VALID_RANGE_WITH_EOF(index); + NSString *string = self.string; + NSUInteger len = self.length; + NSUInteger end, contentEnd; + + if (index > len) { + index = len; + } + + [string getLineStart:&index end:&end contentsEnd:&contentEnd forRange:NSMakeRange(index, 0)]; + if (newLineLength) *newLineLength = contentEnd - end; + return NSMakeRange(index, contentEnd - index); +} + +- (NSUInteger)indexOfLineNumber:(NSUInteger)num +{ + if (num == 1) { + return 0; + } + return [self indexRangeForLineNumber:num newLineLength:NULL].location; +} + +- (NSUInteger)lineNumberAtIndex:(NSUInteger)index +{ + ASSERT_VALID_RANGE_WITH_EOF(index); + + if (_flags.has_xvim_lineNumberAtIndex) { + return [_XVimTextStorage xvim_lineNumberAtIndex:index]; + } + + NSString *string = self.string; + NSUInteger len = self.length; + NSUInteger num = 1, pos = 0; + + if (index > len) { + index = len; + } + + do { + num++; + if (index == pos) { + return num; + } + [string getLineStart:NULL end:&pos contentsEnd:NULL forRange:NSMakeRange(pos, 0)]; + } while (pos < index); + + return num; +} + +#pragma mark Converting between Indexes and Line Numbers + Columns + +static NSUInteger xvim_sb_count_columns(xvim_string_buffer_t *sb, NSUInteger tabWidth) +{ + NSUInteger col = 0; + + if (!xvim_sb_at_end(sb)) { + do { + if (xvim_sb_peek(sb) == '\t') { + col += tabWidth; + if (tabWidth) col -= col % tabWidth; + } else { + col++; + } + } while (xvim_sb_next(sb)); + } + + return col; +} + +- (NSUInteger)columnOfIndex:(NSUInteger)index +{ + NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; + xvim_string_buffer_t sb; + + if (index < NSMaxRange(range)) { + range.length = index - range.location; + } + if (range.length == 0) { + return 0; + } + + xvim_sb_init(&sb, self.string, range.location, range); + return xvim_sb_count_columns(&sb, self.tabWidth); +} + +- (NSUInteger)numberOfColumnsInLineAtIndex:(NSUInteger)index +{ + NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; + xvim_string_buffer_t sb; + + xvim_sb_init(&sb, self.string, range.location, range); + return xvim_sb_count_columns(&sb, self.tabWidth); +} + +- (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column +{ + NSUInteger index = [self indexOfLineNumber:num]; + + if (column == 0 || index == NSNotFound) { + return index; + } + + NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; + NSUInteger tabWidth = self.tabWidth; + NSUInteger col = 0; + xvim_string_buffer_t sb; + + xvim_sb_init(&sb, self.string, range.location, range); + do { + if (xvim_sb_peek(&sb) == '\t') { + col += tabWidth; + if (tabWidth) col -= col % tabWidth; + } else { + col++; + } + if (col > column) { + return xvim_sb_index(&sb); + } + } while (xvim_sb_next(&sb) && col < column); + + return xvim_sb_index(&sb); +} + + +#pragma mark Searching particular positions on the current line + +- (NSUInteger)startOfLine:(NSUInteger)index +{ + ASSERT_VALID_RANGE_WITH_EOF(index); + NSString *string = self.string; + NSUInteger len = self.length; + + if (index > len) { + index = len; + } + [string getLineStart:&index end:NULL contentsEnd:NULL forRange:NSMakeRange(index, 0)]; + return index; +} + +- (NSUInteger)firstOfLine:(NSUInteger)index +{ + NSUInteger pos = [self startOfLine:index]; + + if (pos == index && isNewline([self.string characterAtIndex:pos])) { + return NSNotFound; + } + return pos; +} + +- (NSUInteger)endOfLine:(NSUInteger)index +{ + ASSERT_VALID_RANGE_WITH_EOF(index); + NSString *string = self.string; + NSUInteger len = self.length; + + if (index > len) { + index = len; + } + [string getLineStart:NULL end:NULL contentsEnd:&index forRange:NSMakeRange(index, 0)]; + return index; +} + +- (NSUInteger)lastOfLine:(NSUInteger)index +{ + NSUInteger pos = [self endOfLine:index]; + + if (pos <= index && (pos == 0 || isNewline([self.string characterAtIndex:pos - 1]))) { + return NSNotFound; + } + return pos - 1; +} + +- (NSUInteger)nextNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL +{ + NSString *s = self.string; + NSUInteger length = s.length; + xvim_string_buffer_t sb; + unichar c; + + ASSERT_VALID_RANGE_WITH_EOF(index); + + xvim_sb_init(&sb, s, index, NSMakeRange(index, length - index)); + xvim_sb_skip_forward(&sb, [NSCharacterSet whitespaceCharacterSet]); + c = xvim_sb_peek(&sb); + + if (c == XVimInvalidChar || isNewline(c)) { + return allowEOL ? xvim_sb_index(&sb) : NSNotFound; + } + return xvim_sb_index(&sb); +} + +- (NSUInteger)firstNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL +{ + index = [self startOfLine:index]; + return [self nextNonblankInLineAtIndex:index allowEOL:allowEOL]; +} + +- (NSUInteger)nextDigitInLine:(NSUInteger)index +{ + xvim_string_buffer_t sb; + NSRange range; + + range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; + xvim_sb_init(&sb, self.string, index, range); + if (xvim_sb_find_forward(&sb, [NSCharacterSet decimalDigitCharacterSet])) { + return xvim_sb_index(&sb); + } + + return NSNotFound; +} + #pragma mark Support for modifications - (void)undoRedo:(id)op diff --git a/XVim/XVimExCommand.m b/XVim/XVimExCommand.m index 1b95e96f..784cb92e 100644 --- a/XVim/XVimExCommand.m +++ b/XVim/XVimExCommand.m @@ -594,26 +594,27 @@ - (NSUInteger)getAddress:(unichar*)parsing :(unichar**)cmdLeft inWindow:(XVimWin unichar* tmp; NSUInteger count; unichar mark; + XVimBuffer *buffer = window.currentBuffer; // Parse base addr (line number) switch (*parsing) { case '.': parsing++; - addr = [view.textStorage xvim_lineNumberAtIndex:begin]; + addr = [buffer lineNumberAtIndex:begin]; break; case '$': /* '$' - last line */ parsing++; - addr = [view.textStorage xvim_numberOfLines]; + addr = [buffer numberOfLines]; break; case '\'': // XVim does support only '< '> marks for visual mode mark = parsing[1]; if( '<' == mark ){ - addr = [view.textStorage xvim_lineNumberAtIndex:begin]; + addr = [buffer lineNumberAtIndex:begin]; parsing+=2; }else if( '>' == mark ){ - addr = [view.textStorage xvim_lineNumberAtIndex:end]; + addr = [buffer lineNumberAtIndex:end]; parsing+=2; }else{ // Other marks or invalid character. XVim does not support this. @@ -733,12 +734,13 @@ - (XVimExArg*)parseCommand:(NSString*)cmd inWindow:(XVimWindow*)window exarg.lineEnd = NSNotFound; NSTextView* view = [window sourceView]; + XVimBuffer *buffer = window.currentBuffer; for(;;){ NSUInteger addr = [self getAddress:parsing :&parsing inWindow:window]; if( NSNotFound == addr ){ if( *parsing == '%' ){ // XVim only supports % exarg.lineBegin = 1; - exarg.lineEnd = [view.textStorage xvim_numberOfLines]; + exarg.lineEnd = buffer.numberOfLines; parsing++; } }else{ @@ -758,7 +760,7 @@ - (XVimExArg*)parseCommand:(NSString*)cmd inWindow:(XVimWindow*)window if( exarg.lineBegin == NSNotFound ){ // No range expression found. Use current line as range - exarg.lineBegin = [view.textStorage xvim_lineNumberAtIndex:view.insertionPoint]; + exarg.lineBegin = [buffer lineNumberAtIndex:view.insertionPoint]; exarg.lineEnd = exarg.lineBegin; } @@ -815,14 +817,14 @@ - (void)executeCommand:(NSString*)cmd inWindow:(XVimWindow*)window XVimExArg* exarg = [self parseCommand:cmd inWindow:window]; if( exarg.cmd == nil ) { NSTextView* srcView = [window sourceView]; - NSTextStorage* storage = srcView.textStorage; - + XVimBuffer *buffer = window.currentBuffer; + // Jump to location - NSUInteger pos = [storage xvim_indexOfLineNumber:exarg.lineBegin column:0]; - if( NSNotFound == pos ){ - pos = [srcView.textStorage xvim_indexOfLineNumber:[srcView.textStorage xvim_numberOfLines] column:0]; + NSUInteger pos = [buffer indexOfLineNumber:exarg.lineBegin column:0]; + if (NSNotFound == pos) { + pos = [buffer startOfLine:buffer.length]; } - NSUInteger pos_wo_space = [srcView.textStorage xvim_nextNonblankInLineAtIndex:pos allowEOL:NO]; + NSUInteger pos_wo_space = [buffer nextNonblankInLineAtIndex:pos allowEOL:NO]; if( NSNotFound == pos_wo_space ){ pos_wo_space = pos; } diff --git a/XVim/XVimGActionEvaluator.m b/XVim/XVimGActionEvaluator.m index be099206..84f8412c 100644 --- a/XVim/XVimGActionEvaluator.m +++ b/XVim/XVimGActionEvaluator.m @@ -43,9 +43,10 @@ - (XVimEvaluator*)f{ - (XVimEvaluator*)i{ XVimMark* mark = [[XVim instance].marks markForName:@"^" forDocument:self.sourceView.documentURL.path]; XVimInsertionPoint mode = XVIM_INSERT_DEFAULT; + XVimBuffer *buffer = self.window.currentBuffer; if ( mark.line != NSNotFound) { - NSUInteger newPos = [self.sourceView.textStorage xvim_indexOfLineNumber:mark.line column:mark.column]; + NSUInteger newPos = [buffer indexOfLineNumber:mark.line column:mark.column]; if( NSNotFound != newPos ){ XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 0); m.position = newPos; diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index b017e5c8..08cae2d1 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -206,7 +206,8 @@ - (void)didEndHandler{ // Position for "^" is before escaped from insert mode NSUInteger pos = self.sourceView.insertionPoint; - XVimMark* mark = XVimMakeMark([self.sourceView.textStorage xvim_lineNumberAtIndex:pos], [self.sourceView.textStorage xvim_columnOfIndex:pos], self.sourceView.documentURL.path); + XVimBuffer *buffer = self.window.currentBuffer; + XVimMark *mark = XVimMakeMark([buffer lineNumberAtIndex:pos], [buffer columnOfIndex:pos], buffer.document.fileURL.path); if( nil != mark.document ){ [[XVim instance].marks setMark:mark forName:@"^"]; } @@ -215,7 +216,7 @@ - (void)didEndHandler{ // Position for "." is after escaped from insert mode pos = self.sourceView.insertionPoint; - mark = XVimMakeMark([self.sourceView.textStorage xvim_lineNumberAtIndex:pos], [self.sourceView.textStorage xvim_columnOfIndex:pos], self.sourceView.documentURL.path); + mark = XVimMakeMark([buffer lineNumberAtIndex:pos], [buffer columnOfIndex:pos], buffer.document.fileURL.path); if( nil != mark.document ){ [[XVim instance].marks setMark:mark forName:@"."]; } @@ -286,15 +287,16 @@ - (XVimEvaluator*)C_c{ } - (void)C_yC_eHelper:(BOOL)handlingC_y { + XVimBuffer *buffer = self.window.currentBuffer; NSUInteger currentCursorIndex = [self.sourceView selectedRange].location; - NSUInteger currentColumnIndex = [self.sourceView.textStorage xvim_columnOfIndex:currentCursorIndex]; + NSUInteger currentColumnIndex = [buffer columnOfIndex:currentCursorIndex]; NSUInteger newCharIndex; if (handlingC_y) { newCharIndex = [self.sourceView.textStorage prevLine:currentCursorIndex column:currentColumnIndex count:[self numericArg] option:MOTION_OPTION_NONE]; } else { newCharIndex = [self.sourceView.textStorage nextLine:currentCursorIndex column:currentColumnIndex count:[self numericArg] option:MOTION_OPTION_NONE]; } - NSUInteger newColumnIndex = [self.sourceView.textStorage xvim_columnOfIndex:newCharIndex]; + NSUInteger newColumnIndex = [buffer columnOfIndex:newCharIndex]; NSLog(@"Old column: %ld\tNew column: %ld", currentColumnIndex, newColumnIndex); if (currentColumnIndex == newColumnIndex) { unichar u = [[[self sourceView] string] characterAtIndex:newCharIndex]; diff --git a/XVim/XVimMarkSetEvaluator.m b/XVim/XVimMarkSetEvaluator.m index 516ba602..89a91de2 100644 --- a/XVim/XVimMarkSetEvaluator.m +++ b/XVim/XVimMarkSetEvaluator.m @@ -22,6 +22,7 @@ - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ + XVimBuffer *buffer = self.window.currentBuffer; NSString* keyStr = [keyStroke toSelectorString]; if ([keyStr length] != 1) { return [XVimEvaluator invalidEvaluator]; @@ -29,8 +30,8 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ XVimMark* mark = [[[XVimMark alloc] init] autorelease]; NSRange r = [self.sourceView selectedRange]; - mark.line = [self.sourceView.textStorage xvim_lineNumberAtIndex:r.location]; - mark.column = [self.sourceView.textStorage xvim_columnOfIndex:r.location]; + mark.line = [buffer lineNumberAtIndex:r.location]; + mark.column = [buffer columnOfIndex:r.location]; mark.document = [[self.sourceView documentURL] path]; if( nil != mark.document ){ [[XVim instance].marks setMark:mark forName:keyStr]; diff --git a/XVim/XVimMotionEvaluator.m b/XVim/XVimMotionEvaluator.m index 603e2563..2462a021 100644 --- a/XVim/XVimMotionEvaluator.m +++ b/XVim/XVimMotionEvaluator.m @@ -351,6 +351,7 @@ - (XVimEvaluator*)NUMBER{ // This is internal method used by SQUOTE, BACKQUOTE // TODO: rename firstOfLine -> firstNonblankOfLine - (XVimEvaluator*)jumpToMark:(XVimMark*)mark firstOfLine:(BOOL)fol{ + XVimBuffer *buffer = self.window.currentBuffer; NSUInteger cur_pos = self.sourceView.insertionPoint; MOTION_TYPE motionType = fol?LINEWISE:CHARACTERWISE_EXCLUSIVE; @@ -365,19 +366,19 @@ - (XVimEvaluator*)jumpToMark:(XVimMark*)mark firstOfLine:(BOOL)fol{ [ctrl openDocumentWithContentsOfURL:doc display:YES error:&error]; } - NSUInteger to = [self.sourceView.textStorage xvim_indexOfLineNumber:mark.line column:mark.column]; + NSUInteger to = [buffer indexOfLineNumber:mark.line column:mark.column]; if( NSNotFound == to ){ return [XVimEvaluator invalidEvaluator]; } if( fol ){ - to = [self.sourceView.textStorage xvim_firstNonblankInLineAtIndex:to allowEOL:YES]; // This never returns NSNotFound + to = [buffer firstNonblankInLineAtIndex:to allowEOL:YES]; // This never returns NSNotFound } // set the position before the jump XVimMark* cur_mark = [[[XVimMark alloc] init] autorelease]; - cur_mark.line = [self.sourceView.textStorage xvim_lineNumberAtIndex:cur_pos]; - cur_mark.column = [self.sourceView.textStorage xvim_columnOfIndex:cur_pos]; + cur_mark.line = [buffer lineNumberAtIndex:cur_pos]; + cur_mark.column = [buffer columnOfIndex:cur_pos]; cur_mark.document = [self.sourceView documentURL].path; if( nil != mark.document ){ [[XVim instance].marks setMark:cur_mark forName:@"'"]; @@ -438,11 +439,12 @@ - (XVimEvaluator*)DOLLAR{ // it will moves to start of the numeric argument - 1 lines down. - (XVimEvaluator*)UNDERSCORE{ // TODO add this motion interface to NSTextView - NSTextView* view = [self.window sourceView]; + NSTextView *view = [self.window sourceView]; + XVimBuffer *buffer = self.window.currentBuffer; NSRange r = [view selectedRange]; NSUInteger repeat = self.numericArg; NSUInteger linesUpCursorloc = [view.textStorage nextLine:r.location column:0 count:(repeat - 1) option:MOTION_OPTION_NONE]; - NSUInteger head = [view.textStorage xvim_firstNonblankInLineAtIndex:linesUpCursorloc allowEOL:NO]; + NSUInteger head = [buffer firstNonblankInLineAtIndex:linesUpCursorloc allowEOL:NO]; if( NSNotFound == head && linesUpCursorloc != NSNotFound){ head = linesUpCursorloc; }else if(NSNotFound == head){ diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index 9ef2bf5f..a1313850 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -143,18 +143,22 @@ - (XVimEvaluator*)C_f{ return nil; } -- (XVimEvaluator*)C_g{ +- (XVimEvaluator*)C_g +{ // process - XVimWindow* window = self.window; - NSRange range = [[window sourceView] selectedRange]; - NSUInteger numberOfLines = [window.sourceView.textStorage xvim_numberOfLines]; + XVimWindow *window = self.window; + XVimBuffer *buffer = window.currentBuffer; + NSRange range = window.sourceView.selectedRange; + + NSUInteger numberOfLines = [buffer numberOfLines]; long long lineNumber = [window.sourceView currentLineNumber]; - NSUInteger columnNumber = [window.sourceView.textStorage xvim_columnOfIndex:range.location]; - NSURL* documentURL = [[window sourceView] documentURL]; - if( [documentURL isFileURL] ) { - NSString* filename = [documentURL path]; - NSString* text = [NSString stringWithFormat:@"%@ line %lld of %ld --%d%%-- col %ld", - filename, lineNumber, numberOfLines, (int)((float)lineNumber*100.0/(float)numberOfLines), columnNumber+1 ]; + NSUInteger columnNumber = [buffer columnOfIndex:range.location]; + NSURL *documentURL = buffer.document.fileURL; + + if ([documentURL isFileURL]) { + NSString *text = [NSString stringWithFormat:@"%@ line %lld of %ld --%d%%-- col %ld", + documentURL.path, lineNumber, numberOfLines, + (int)((float)lineNumber*100.0/(float)numberOfLines), columnNumber+1 ]; [window statusMessage:text]; } diff --git a/XVim/XVimSearch.m b/XVim/XVimSearch.m index 2bddcd42..5f4d7c35 100644 --- a/XVim/XVimSearch.m +++ b/XVim/XVimSearch.m @@ -466,15 +466,16 @@ - (void)substitute:(NSString*)ex_command from:(NSUInteger)from to:(NSUInteger)to self.lastSearchCmd = replaced; self.lastSearchDisplayString = replaced; self.lastReplacementString = replacement; - + + XVimBuffer *buffer = window.currentBuffer; // Find the position to start searching - NSUInteger replace_start_location = [window.sourceView.textStorage xvim_indexOfLineNumber:from column:0]; + NSUInteger replace_start_location = [buffer indexOfLineNumber:from column:0]; if( NSNotFound == replace_start_location){ return; } // Find the position to end the searching - NSUInteger endOfReplacement = [window.sourceView.textStorage xvim_indexOfLineNumber:to+1 column:0]; // Next line of the end of range. + NSUInteger endOfReplacement = [buffer indexOfLineNumber:to+1 column:0]; // Next line of the end of range. if( NSNotFound == endOfReplacement ){ endOfReplacement = [[[window sourceView] string] length]; } diff --git a/XVim/XVimTextStoring.h b/XVim/XVimTextStoring.h index 1009ecd9..6ee72119 100644 --- a/XVim/XVimTextStoring.h +++ b/XVim/XVimTextStoring.h @@ -6,89 +6,28 @@ // // -#import +#import -/** @brief Protocol that must be implemented by the NSTextStorage you intend to hook +/** @brief Protocol that can be implemented by your NSTextStorage. * - * Note that the terms here do not have the usual Cocoah meaning + * Implementing it will likely boost XVimBuffer performance significantly + * All selectors are optional, and serve as the direct backend for + * XVimBuffer selectors of the same name without the xvim_ prefix. * - * "Character" - * Character is a one unichar value. (any value including tabs,spaces) - * - * "index" - * This is the 0-based location of a given character within -xvim_string. - * - * "Position" - * This is an XVimPosition (line + column) - * - * "EOF" - * EOF is the position of the end of the document (-length), - * so for a text of "abc", EOF is just after the 'c', at position 3. - * - * What we have to think about is that a cursor can be at EOF, - * but -[xvim_string characterAtIndex:] at this position raises. - * - * We have to be careful about it when computing motions effects. - * - * "Newline" - * Newline is defined as "unichar determined by isNewline function". - * Usually "\n" or "\r". - * - * "Line" - * Line is a sequence of characters terminated by newline or EOF. - * "Line" includes the last newline character. - * Line numbers start at 1 - * - * "Blankline" - * Blankline is a line which has only newline or EOF. - * In other words, it is newline character or EOF after newline character. - * - * "Last of Line(LOL)" - * Last of line is the last character of a line EXCLUDING newline character. - * This means that blankline does NOT have an Last of line. - * - * "First of Line(FOL)" - * First of line is the first character of a line excluding newline character. - * This means that blankline does NOT have a First of line. - * - * "First Nonblank of Line" - * First Nonblank of Line is the first printable character in a line. - * - * "End of Line(EOL)" - * End of Line is newline or EOF character at the end of a line. - * A line always has an EOL. - * - * "Beginning of Line (BOL)" - * First character of a line including newline and EOF - * - ****************************************************************************** - * - * Implementation notes for developpers - * - * XVim provides a default implementation of this protocol - * on an NSTextStorage Category. - * - * The functions that are reused to implement other of the protocol are marked - * as "XVIM PRIMITIVE" which means that if you want to adapt for a given - * NSTextStorage subclass, those are the only one you probably need to - * consider for reimplementing. */ @protocol XVimTextStoring -@property (nonatomic, readonly) NSString *xvim_string; +@optional -@property (nonatomic, readonly) NSUInteger xvim_numberOfLines; +@property (nonatomic, readonly) NSString *xvim_string; -#pragma mark Settings +@property (nonatomic, readonly) NSUInteger xvim_numberOfLines; @property (nonatomic, readonly) NSUInteger xvim_tabWidth; -@property (nonatomic, readonly) NSUInteger xvim_indentWidth; -#pragma mark Converting between Indexes and Line Numbers +@property (nonatomic, readonly) NSUInteger xvim_indentWidth; /** @brief returns the index range for the given line number - * - * XVIM PRIMITIVE * * @param[in] num * The line number @@ -101,8 +40,6 @@ - (NSRange)xvim_indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUInteger *)newLineLength; /** @brief returns the index range for the given line range - * - * XVIM PRIMITIVE * * @param[in] range the line range. * @@ -112,29 +49,7 @@ */ - (NSRange)xvim_indexRangeForLines:(NSRange)range; -/** @brief returns the line range around the given index - * - * XVIM PRIMITIVE - * - * @param[in] index - * The index within -xvim_string - * @param[out] newLineLength - * The number of characters after the returned range forming the end of line - * @returns - * the range of indexes forming the line, exclugint trailing newLine characters - * Note that if the index is within a CRLF for example, the range may end before index - */ -- (NSRange)xvim_indexRangeForLineAtIndex:(NSUInteger)index newLineLength:(NSUInteger *)newLineLength; - -/** @brief starting position of line @a num within -xvim_string. - * @returns the starting index for that line number or NSNotFound. - * @see -xvim_indexRangeForLineNumber:newLineLength: - */ -- (NSUInteger)xvim_indexOfLineNumber:(NSUInteger)num; - -/** @brief get the line number of a given position. - * - * XVIM PRIMITIVE +/** @brief get the line number of a given index. * * @returns * the line number of specified index. @@ -142,122 +57,4 @@ */ - (NSUInteger)xvim_lineNumberAtIndex:(NSUInteger)index; -#pragma mark Converting between Indexes and Line Numbers + Columns - -/** @brief returns the column number of \a index within the line. - * - * Column numbers starts at 0. - * - * This never returns NSNotFound. - */ -- (NSUInteger)xvim_columnOfIndex:(NSUInteger)index; - -/** @brief returns number of columns for the line containing \a index. - * - * If the specified line does not exist in the current document it returns NSNotFound - */ -- (NSUInteger)xvim_numberOfColumnsInLineAtIndex:(NSUInteger)index; - -/** @brief returns the index for the given line number and column. - * - * @returns - * NSNotFound if \a num exceeds the number of lines in the document - * If \a column is larger than the number of columns in that line, - * it returns the index of the endOfLine for that line - */ -- (NSUInteger)xvim_indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column; - -#pragma mark Searching particular positions on the current line - -/** @brief position of the first character of the line containing \a index. - * - * @param index the index to search backwards from - */ -- (NSUInteger)xvim_startOfLine:(NSUInteger)index; // never returns NSNotFound - -/** @brief returns the firstOfLine for the line containing \a index. - * - * If the line is blank, this returns NSNotFound - * else this is the same as -beginningOfLine: - */ -- (NSUInteger)xvim_firstOfLine:(NSUInteger)index; // May return NSNotFound - -/** @brief position of the end of the line containing \a index. - * - * @param index the index to search from - * - * @returns - * the position of the end of the line. - * end of the line is either: - * - a newline character at the end of the line - * - end of the document - * - * Note that for files with \r\n if index points to \n - * this returns a position before index. - */ -- (NSUInteger)xvim_endOfLine:(NSUInteger)index; // never returns NSNotFound - -/** @brief returns the lastOfLine for the line containing \a index. - * - * If the line is blank, this returns NSNotFound - * else this is the same as -endOfLine:index - 1 - */ -- (NSUInteger)xvim_lastOfLine:(NSUInteger)index; // May return NSNotFound - -/** @brief returns the next non blank position on the same line. - * - * @param index the index to search from - * @param allowEOL whether reaching EOL is allowed or not - * - * @returns - * the position of the first non blank character, starting at index. - * - * if \a allowEOL is NO and that no non blank character is found, - * this returns NSNotFound. - */ -- (NSUInteger)xvim_nextNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL; - -/** @brief returns the first non blank character on the line, possibly EOL. - * - * @param index searches on the line containing that index. - * - * @returns - * the position of the first non blank character - * on the line containing \a index. - * - * if \a allowEOL is NO and that no non blank character is found, - * this returns NSNotFound. - */ -- (NSUInteger)xvim_firstNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL; - -/** @brief returns the next digit position on the same line. - * - * @param index the index to search from - * - * @returns - * the position of the first decimal digit character starting at \a index - * - * this returns NSNotFound if none is found. - */ -- (NSUInteger)xvim_nextDigitInLine:(NSUInteger)index; - @end - -typedef NSTextStorage XVimTextStorage; - -#pragma mark Macros - -#ifdef DEBUG -// The methods here often take index as current interest position and index can be at EOF -// The following macros asserts the range of index. -// WITH_EOF permits the index at EOF position. -// WITHOUT_EOF doesn't permit the index at EOF position. -#define ASSERT_VALID_RANGE_WITH_EOF(x) NSAssert( x <= [self length] || [self length] == 0, @"index can not exceed the length of string. TextLength:%lu SpecifiedIndex:%lu", (long)[self length], x) - -// Some methods assume that "index" is at valid cursor position in Normal mode. -// See isValidCursorPosition's description the condition of the valid cursor position. -#define ASSERT_VALID_CURSOR_POS(x) NSAssert( [self isValidCursorPosition:x], @"index can not be invalid cursor position" ) -#else -#define ASSERT_VALID_RANGE_WITH_EOF(x) -#define ASSERT_VALID_CURSOR_POS(x) -#endif diff --git a/XVim/XVimVisualEvaluator.m b/XVim/XVimVisualEvaluator.m index 80a1ee12..d3553592 100644 --- a/XVim/XVimVisualEvaluator.m +++ b/XVim/XVimVisualEvaluator.m @@ -52,6 +52,8 @@ - (id)initWithLastVisualStateWithWindow:(XVimWindow *)window{ } - (id)initWithWindow:(XVimWindow *)window mode:(XVIM_VISUAL_MODE)mode { + XVimBuffer *buffer = window.currentBuffer; + if (self = [self initWithWindow:window]) { _waitForArgument = NO; _visual_mode = mode; @@ -62,16 +64,16 @@ - (id)initWithWindow:(XVimWindow *)window mode:(XVIM_VISUAL_MODE)mode { }else{ NSUInteger start = [window.sourceView selectedRange].location; NSUInteger end = [window.sourceView selectedRange].location + [window.sourceView selectedRange].length - 1; - self.initialFromPos = XVimMakePosition([window.sourceView.textStorage xvim_lineNumberAtIndex:start], [window.sourceView.textStorage xvim_columnOfIndex:start]); - self.initialToPos = XVimMakePosition([window.sourceView.textStorage xvim_lineNumberAtIndex:end], [window.sourceView.textStorage xvim_columnOfIndex:end]); + self.initialFromPos = XVimMakePosition([buffer lineNumberAtIndex:start], [buffer columnOfIndex:start]); + self.initialToPos = XVimMakePosition([buffer lineNumberAtIndex:end], [buffer columnOfIndex:end]); } }else{ // Treat it as block selection _visual_mode = XVIM_VISUAL_BLOCK; NSUInteger start = [[[window.sourceView selectedRanges] objectAtIndex:0] rangeValue].location; NSUInteger end = [[[window.sourceView selectedRanges] lastObject] rangeValue].location + [[[window.sourceView selectedRanges] lastObject] rangeValue].length - 1; - self.initialFromPos = XVimMakePosition([window.sourceView.textStorage xvim_lineNumberAtIndex:start], [window.sourceView.textStorage xvim_columnOfIndex:start]); - self.initialToPos = XVimMakePosition([window.sourceView.textStorage xvim_lineNumberAtIndex:end], [window.sourceView.textStorage xvim_columnOfIndex:end]); + self.initialFromPos = XVimMakePosition([buffer lineNumberAtIndex:start], [buffer columnOfIndex:start]); + self.initialToPos = XVimMakePosition([buffer lineNumberAtIndex:end], [buffer columnOfIndex:end]); } } return self; diff --git a/XVim/XVimWindow.h b/XVim/XVimWindow.h index 6b55ced0..0c81fbb0 100644 --- a/XVim/XVimWindow.h +++ b/XVim/XVimWindow.h @@ -9,6 +9,7 @@ #import "XVimCommandLine.h" #import "XVimTextViewProtocol.h" #import "XVimKeyStroke.h" +#import "XVimBuffer.h" /* * This class manages 1 window. (The term "window" here is different from NSWindow) @@ -28,6 +29,7 @@ @interface XVimWindow : NSObject @property(readonly) NSTextView *sourceView; // This represents currently focused sourceView @property(readonly) XVimCommandLine *commandLine; +@property(readonly) XVimBuffer *currentBuffer; - (instancetype)initWithIDEEditorArea:(IDEEditorArea *)editorArea; diff --git a/XVim/XVimWindow.m b/XVim/XVimWindow.m index df71c778..be80d4e5 100644 --- a/XVim/XVimWindow.m +++ b/XVim/XVimWindow.m @@ -76,6 +76,11 @@ - (NSTextView *)sourceView return editor.mainScrollView.documentView; } +- (XVimBuffer *)currentBuffer +{ + return self.sourceView.textStorage.xvim_buffer; +} + - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; From 0211582b51508a464ae13e0f13b303eb1eaf043a Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Mon, 18 Nov 2013 21:51:50 +0100 Subject: [PATCH 03/44] Have initializer that makes more sense --- XVim/DVTTextStorage+XVimTextStoring.m | 2 +- XVim/NSTextStorage+VimOperation.m | 4 +- XVim/NSTextView+VimOperation.h | 1 + XVim/NSTextView+VimOperation.m | 112 +++++++++++++------------- XVim/XVimBuffer.h | 8 +- XVim/XVimBuffer.m | 34 +++++--- XVim/XVimStringBuffer.h | 17 ++-- XVim/XVimUndo.h | 18 +++-- XVim/XVimUndo.m | 98 ++++++++++++++++++---- 9 files changed, 194 insertions(+), 100 deletions(-) diff --git a/XVim/DVTTextStorage+XVimTextStoring.m b/XVim/DVTTextStorage+XVimTextStoring.m index 2c94f822..f3c13291 100644 --- a/XVim/DVTTextStorage+XVimTextStoring.m +++ b/XVim/DVTTextStorage+XVimTextStoring.m @@ -45,7 +45,7 @@ - (NSRange)xvim_indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUIntege if (num <= self.numberOfLines) { NSRange range = [self characterRangeForLineRange:NSMakeRange(num - 1, 1)]; - xvim_sb_init(&sb, self.xvim_string, range.location, range); + xvim_sb_init_range(&sb, self.xvim_string, range); xvim_sb_find_backward(&sb, [NSCharacterSet newlineCharacterSet]); if (newLineLength) *newLineLength = xvim_sb_range_to_end(&sb).length; diff --git a/XVim/NSTextStorage+VimOperation.m b/XVim/NSTextStorage+VimOperation.m index bcbd524f..0124b0cb 100644 --- a/XVim/NSTextStorage+VimOperation.m +++ b/XVim/NSTextStorage+VimOperation.m @@ -118,7 +118,7 @@ - (NSUInteger)positionOfMatchedPair:(NSUInteger)pos // as long as the nesting level matches up xvim_string_buffer_t sb; - xvim_sb_init(&sb, s, pos, NSMakeRange(pos, [buffer endOfLine:pos] - pos)); + xvim_sb_init(&sb, s, pos, pos, [buffer endOfLine:pos]); #define pairs "{}[]()" NSCharacterSet *charset = [NSCharacterSet characterSetWithCharactersInString:@pairs]; @@ -129,7 +129,7 @@ - (NSUInteger)positionOfMatchedPair:(NSUInteger)pos if (xvim_sb_find_forward(&sb, charset)) { start_with_c = xvim_sb_peek(&sb); - xvim_sb_init(&sb, s, xvim_sb_index(&sb), NSMakeRange(0, s.length)); + xvim_sb_init(&sb, s, xvim_sb_index(&sb), 0, s.length); NSUInteger pos = (NSUInteger)(strchr(pairs, start_with_c) - pairs); diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h index b2e4ae50..3dd331cb 100644 --- a/XVim/NSTextView+VimOperation.h +++ b/XVim/NSTextView+VimOperation.h @@ -75,6 +75,7 @@ #pragma mark Operations (Has effect to internal state) - (void)xvim_adjustCursorPosition; +- (void)xvim_moveToIndex:(NSUInteger)index; - (void)xvim_moveToPosition:(XVimPosition)pos; - (void)xvim_move:(XVimMotion*)motion; - (void)xvim_selectSwapEndsOnSameLine:(BOOL)onSameLine; diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index 268aebe0..c548a395 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -25,6 +25,7 @@ #import "Logger.h" #import "XVimUndo.h" #import "XVimBuffer.h" +#import "XVimStringBuffer.h" #define LOG_STATE() TRACE_LOG(@"mode:%d length:%d cursor:%d ip:%d begin:%d line:%d column:%d preservedColumn:%d", \ self.selectionMode, \ @@ -72,7 +73,7 @@ - (NSUInteger)xvim_lineNumberFromTop:(NSUInteger)count; - (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward; - (void)xvim_swapCaseForRange:(NSRange)range; - (void)xvim_registerInsertionPointForUndo; -- (void)xvim_registerPositionForUndo:(XVimPosition)pos; +- (void)xvim_registerIndexForUndo:(NSUInteger)index; @end @implementation NSTextView (VimOperation) @@ -87,52 +88,45 @@ - (void)_xvim_insertSpaces:(NSUInteger)count replacementRange:(NSRange)replaceme } - (void)_xvim_removeSpacesAtLine:(NSUInteger)line column:(NSUInteger)column count:(NSUInteger)count + undoOperation:(XVimUndoOperation *)op { - NSTextStorage *ts = self.textStorage; - XVimBuffer *buffer = ts.xvim_buffer; - NSUInteger tabWidth = buffer.tabWidth; - NSUInteger pos = [buffer indexOfLineNumber:line column:column]; - NSUInteger end = pos; - NSUInteger width = 0; - NSString *s = self.xvim_string; - - if ([ts isEOL:pos]) { - return; - } + XVimBuffer *buffer = self.textStorage.xvim_buffer; + NSUInteger tabWidth = buffer.tabWidth; + NSUInteger spaces = 0, width = 0, start; + xvim_string_buffer_t sb; - if ([s characterAtIndex:pos] == '\t') { - NSUInteger col = [buffer columnOfIndex:pos]; + start = [buffer indexOfLineNumber:line column:column]; - if (col < column) { - [self _xvim_insertSpaces:tabWidth - (col % tabWidth) replacementRange:NSMakeRange(pos, 1)]; - pos += column - col; - } + xvim_sb_init(&sb, buffer.string, start, start, buffer.length); + if (xvim_sb_peek(&sb) == '\t') { + spaces = column - [buffer columnOfIndex:start]; + } else if (xvim_sb_peek(&sb) != ' ') { + return; } while (width < count) { - unichar c = [s characterAtIndex:end]; + unichar c = xvim_sb_peek(&sb); - if (c == ' ') { - end++; - width++; - } else if (c == '\t') { - NSUInteger col = column + width; - NSUInteger tw = tabWidth - (col % tabWidth); - - if (width + tw > count) { - [self _xvim_insertSpaces:tw replacementRange:NSMakeRange(end, 1)]; - end += count - width; - width = count; - } else { - end += tw; - width += tw; - } + if (c != ' ' && c != '\t') { + break; + } + if (c == '\t') { + width += tabWidth - ((column + width) % tabWidth); } else { + width++; + } + + if (!xvim_sb_next(&sb)) { break; } } - [self insertText:@"" replacementRange:NSMakeRange(pos, end - pos)]; + if (width > count) { + spaces += width - count; + } + + NSRange range = xvim_sb_range_to_start(&sb); + [buffer replaceCharactersInRange:range withString:[NSString stringMadeOfSpaces:spaces] undoObject:op]; } - (XVimRange)_xvim_selectedLines{ @@ -624,12 +618,16 @@ - (void)xvim_adjustCursorPosition{ return; } -- (void)xvim_moveToPosition:(XVimPosition)pos{ - NSUInteger index = [self.textStorage.xvim_buffer indexOfLineNumber:pos.line column:pos.column]; +- (void)xvim_moveToIndex:(NSUInteger)index{ [self xvim_moveCursor:index preserveColumn:NO]; [self xvim_syncState]; } +- (void)xvim_moveToPosition:(XVimPosition)pos{ + NSUInteger index = [self.textStorage.xvim_buffer indexOfLineNumber:pos.line column:pos.column]; + [self xvim_moveToIndex:index]; +} + - (void)xvim_move:(XVimMotion*)motion{ XVimRange r = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; if( r.end == NSNotFound ){ @@ -1214,7 +1212,6 @@ - (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right NSUInteger column = 0; XVimRange lines; BOOL blockMode = NO; - NSUndoManager *undoManager = self.undoManager; if (self.selectionMode == XVIM_VISUAL_NONE) { XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; @@ -1235,20 +1232,25 @@ - (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right } NSUInteger pos = [buffer indexOfLineNumber:lines.begin column:0]; - [undoManager setGroupsByEvent:NO]; - [undoManager beginUndoGrouping]; + XVimUndoOperation *op = nil; - if (!blockMode) { - NSUInteger col = [buffer columnOfIndex:[buffer firstNonblankInLineAtIndex:pos allowEOL:YES]]; - [self xvim_registerPositionForUndo:XVimMakePosition(lines.begin, col)]; + if (blockMode) { + pos = [buffer indexOfLineNumber:lines.begin column:column]; + } else { + pos = [buffer indexOfLineNumber:lines.begin column:0]; + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; } if (right) { NSString *s = [NSString stringMadeOfSpaces:shiftWidth]; + + [self xvim_registerIndexForUndo:[buffer firstNonblankInLineAtIndex:pos allowEOL:YES]]; [self xvim_blockInsertFixupWithText:s mode:XVIM_INSERT_SPACES count:1 column:column lines:lines]; } else { + op = [[XVimUndoOperation alloc] initWithIndex:pos]; + [buffer.textStorage beginEditing]; for (NSUInteger line = lines.begin; line <= lines.end; line++) { - [self _xvim_removeSpacesAtLine:line column:column count:shiftWidth]; + [self _xvim_removeSpacesAtLine:line column:column count:shiftWidth undoOperation:op]; } } @@ -1257,10 +1259,14 @@ - (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right } else { pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; } - [self xvim_moveCursor:pos preserveColumn:NO]; - [undoManager endUndoGrouping]; - [undoManager setGroupsByEvent:YES]; + + if (op) { + [op setEndIndex:pos]; + [op registerForBuffer:buffer]; + [op release]; + [buffer.textStorage endEditing]; + } [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; } @@ -2496,20 +2502,16 @@ - (void)xvim_swapCaseForRange:(NSRange)range { [substring release]; } -- (void)xvim_registerPositionForUndo:(XVimPosition)pos +- (void)xvim_registerIndexForUndo:(NSUInteger)index { - XVimBuffer *buffer = self.textStorage.xvim_buffer; - NSUndoManager *undoManager = buffer.undoManager; - XVimUndoCursorPositionOperation *op; - - op = [[XVimUndoCursorPositionOperation alloc] initWithPosition:pos undoManager:undoManager]; - [undoManager registerUndoWithTarget:buffer selector:@selector(undoRedo:) object:op]; + XVimUndoOperation *op = [[XVimUndoOperation alloc] initWithIndex:index]; + [op registerForBuffer:self.textStorage.xvim_buffer]; [op release]; } - (void)xvim_registerInsertionPointForUndo { - [self xvim_registerPositionForUndo:self.insertionPosition]; + [self xvim_registerIndexForUndo:self.insertionPoint]; } @end diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index d8828b86..695d03b9 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -25,7 +25,7 @@ #define ASSERT_VALID_CURSOR_POS(x) #endif -@protocol XVimUndoing; +@class XVimUndoOperation; /** @brief class to represent an XVim Buffer * @@ -270,7 +270,11 @@ #pragma mark Support for modifications -- (void)undoRedo:(id)op; +- (void)undoRedo:(XVimUndoOperation *)op; + +- (void)replaceCharactersInRange:(NSRange)range + withString:(NSString *)string + undoObject:(XVimUndoOperation *)op; @end diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index d2a2aa1f..da87f7f1 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -276,7 +276,7 @@ - (NSUInteger)columnOfIndex:(NSUInteger)index return 0; } - xvim_sb_init(&sb, self.string, range.location, range); + xvim_sb_init_range(&sb, self.string, range); return xvim_sb_count_columns(&sb, self.tabWidth); } @@ -285,7 +285,7 @@ - (NSUInteger)numberOfColumnsInLineAtIndex:(NSUInteger)index NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; xvim_string_buffer_t sb; - xvim_sb_init(&sb, self.string, range.location, range); + xvim_sb_init_range(&sb, self.string, range); return xvim_sb_count_columns(&sb, self.tabWidth); } @@ -302,7 +302,7 @@ - (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column NSUInteger col = 0; xvim_string_buffer_t sb; - xvim_sb_init(&sb, self.string, range.location, range); + xvim_sb_init_range(&sb, self.string, range); do { if (xvim_sb_peek(&sb) == '\t') { col += tabWidth; @@ -370,13 +370,12 @@ - (NSUInteger)lastOfLine:(NSUInteger)index - (NSUInteger)nextNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL { NSString *s = self.string; - NSUInteger length = s.length; xvim_string_buffer_t sb; unichar c; ASSERT_VALID_RANGE_WITH_EOF(index); - xvim_sb_init(&sb, s, index, NSMakeRange(index, length - index)); + xvim_sb_init(&sb, s, index, index, s.length); xvim_sb_skip_forward(&sb, [NSCharacterSet whitespaceCharacterSet]); c = xvim_sb_peek(&sb); @@ -395,10 +394,8 @@ - (NSUInteger)firstNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowE - (NSUInteger)nextDigitInLine:(NSUInteger)index { xvim_string_buffer_t sb; - NSRange range; + xvim_sb_init(&sb, self.string, index, index, [self endOfLine:index]); - range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; - xvim_sb_init(&sb, self.string, index, range); if (xvim_sb_find_forward(&sb, [NSCharacterSet decimalDigitCharacterSet])) { return xvim_sb_index(&sb); } @@ -408,9 +405,26 @@ - (NSUInteger)nextDigitInLine:(NSUInteger)index #pragma mark Support for modifications -- (void)undoRedo:(id)op +- (void)undoRedo:(XVimUndoOperation *)op { - [op undoRedo:self]; + for (NSLayoutManager *mgr in _textStorage.layoutManagers) { + NSTextView *view = mgr.firstTextView; + + if (view.textStorage == _textStorage) { + [op undoRedo:self view:view]; + return; + } + } + + [op undoRedo:self view:nil]; +} + +- (void)replaceCharactersInRange:(NSRange)range + withString:(NSString *)string + undoObject:(XVimUndoOperation *)op +{ + [op addUndoRange:range replacementRange:NSMakeRange(range.location, string.length) buffer:self]; + [_textStorage replaceCharactersInRange:range withString:string]; } @end diff --git a/XVim/XVimStringBuffer.h b/XVim/XVimStringBuffer.h index 856c9b78..6dcdb30b 100644 --- a/XVim/XVimStringBuffer.h +++ b/XVim/XVimStringBuffer.h @@ -51,15 +51,17 @@ NS_INLINE void _xvim_sb_load(xvim_string_buffer_t *sb) } /* returns NO if at end */ -NS_INLINE void xvim_sb_init(xvim_string_buffer_t *sb, NSString *s, NSUInteger index, NSRange forRange) +NS_INLINE void xvim_sb_init(xvim_string_buffer_t *sb, NSString *s, + NSUInteger index, NSUInteger min, NSUInteger max) { sb->s = s; - sb->s_min = forRange.location; - sb->s_max = sb->s_min + forRange.length; + sb->s_min = min; + sb->s_max = max; - NSCAssert(index >= sb->s_min && index <= sb->s_max, @"bad caller"); + NSCAssert(min <= max, @"Bad xvim_sb_init"); + NSCAssert(index >= sb->s_min && index <= sb->s_max, @"bad xvim_sb_init"); - if (forRange.length < _xvim_sb_size() || index - _xvim_sb_size() / 2 < sb->s_min) { + if (max - min < _xvim_sb_size() || index - _xvim_sb_size() / 2 < sb->s_min) { sb->s_index = sb->s_min; } else if (index + _xvim_sb_size() >= sb->s_max) { sb->s_index = sb->s_max - _xvim_sb_size() + 1; @@ -70,6 +72,11 @@ NS_INLINE void xvim_sb_init(xvim_string_buffer_t *sb, NSString *s, NSUInteger in _xvim_sb_load(sb); } +NS_INLINE void xvim_sb_init_range(xvim_string_buffer_t *sb, NSString *s, NSRange range) +{ + xvim_sb_init(sb, s, range.location, range.location, NSMaxRange(range)); +} + NS_INLINE NSUInteger xvim_sb_index(xvim_string_buffer_t *sb) { return sb->s_index + sb->b_index; diff --git a/XVim/XVimUndo.h b/XVim/XVimUndo.h index f2d12357..e3d553f9 100644 --- a/XVim/XVimUndo.h +++ b/XVim/XVimUndo.h @@ -7,19 +7,21 @@ // #import -#import "XVimDefs.h" @class XVimBuffer; -@protocol XVimUndoing +@interface XVimUndoOperation : NSObject -- (void)undoRedo:(XVimBuffer *)text; +- (instancetype)initWithIndex:(NSUInteger)index; -@end +- (void)undoRedo:(XVimBuffer *)buffer view:(NSTextView *)view; -@interface XVimUndoCursorPositionOperation : NSObject +- (void)addUndoRange:(NSRange)range + replacementRange:(NSRange)replacementRange + buffer:(XVimBuffer *)buffer; -- (instancetype)initWithPosition:(XVimPosition)pos - undoManager:(NSUndoManager *)undoManager; +- (void)setEndIndex:(NSUInteger)index; -@end +- (void)registerForBuffer:(XVimBuffer *)buffer; + +@end \ No newline at end of file diff --git a/XVim/XVimUndo.m b/XVim/XVimUndo.m index 2577ac1b..8d739f29 100644 --- a/XVim/XVimUndo.m +++ b/XVim/XVimUndo.m @@ -10,17 +10,34 @@ #import "NSTextView+VimOperation.h" #import "XVimBuffer.h" -@implementation XVimUndoCursorPositionOperation { - NSUndoManager *_undoManager; - XVimPosition _pos; +// AppKit class dump on 10.9 +@interface NSTextView (NSPrivate) +- (void)_setUndoRedoInProgress:(BOOL)arg1; +@end + +// AppKit class dump on 10.9 +@interface NSTextStorage (NSUndo) +// return type is presumably an NSAttributedString +- (id)_undoRedoAttributedSubstringFromRange:(NSRange)arg1; +@end + +@implementation XVimUndoOperation { +@protected + NSUndoManager *_undoManager; + NSUInteger _startIndex; + NSUInteger _endIndex; + /* index 3 * i + 0: initial range + * index 3 * i + 1: replacement range + * index 3 * i + 2: text + */ + NSMutableArray *_values; } -- (instancetype)initWithPosition:(XVimPosition)pos - undoManager:(NSUndoManager *)undoManager +- (instancetype)initWithIndex:(NSUInteger)index { if ((self = [super init])) { - _pos = pos; - _undoManager = [undoManager retain]; + _startIndex = index; + _endIndex = index; } return self; } @@ -28,21 +45,68 @@ - (instancetype)initWithPosition:(XVimPosition)pos - (void)dealloc { [_undoManager release]; + [_values release]; [super dealloc]; } -- (void)undoRedo:(XVimBuffer *)buffer +- (void)setEndIndex:(NSUInteger)index { - NSTextStorage *text = buffer.textStorage; - - for (NSLayoutManager *mgr in text.layoutManagers) { - NSTextView *view = mgr.firstTextView; + _endIndex = index; +} - if (view.textStorage == text) { - [view xvim_moveToPosition:_pos]; - return; - } +- (void)addUndoRange:(NSRange)range + replacementRange:(NSRange)replacementRange + buffer:(XVimBuffer *)buffer +{ + if (!_values) { + _values = [[NSMutableArray alloc] initWithCapacity:3]; } + [_values addObject:[NSValue valueWithRange:range]]; + [_values addObject:[NSValue valueWithRange:replacementRange]]; + [_values addObject:[buffer.textStorage _undoRedoAttributedSubstringFromRange:range]]; } -@end +- (void)_undoOp:(NSUInteger)i textStorage:(NSTextStorage *)ts range:(NSRange)range +{ + NSUInteger index= 3 * i + 2; + NSAttributedString *oldText = [_values objectAtIndex:index]; + NSAttributedString *newText = [ts _undoRedoAttributedSubstringFromRange:range]; + + [ts replaceCharactersInRange:range withAttributedString:oldText]; + [_values replaceObjectAtIndex:index withObject:newText]; + } + + - (void)undoRedo:(XVimBuffer *)buffer view:(NSTextView *)view + { + NSTextStorage *ts = buffer.textStorage; + NSUInteger count = _values.count / 3; + + [view _setUndoRedoInProgress:YES]; + if (!view || [view shouldChangeTextInRange:NSMakeRange(NSNotFound, 0) replacementString:@""]) { + [ts beginEditing]; + if ([_undoManager isUndoing]) { + for (NSUInteger i = count; i-- > 0; ) { + [self _undoOp:i textStorage:ts range:[[_values objectAtIndex:3 * i + 1] rangeValue]]; + } + } else { + for (NSUInteger i = 0; i < count; i ++ ) { + [self _undoOp:i textStorage:ts range:[[_values objectAtIndex:3 * i + 0] rangeValue]]; + } + } + [ts endEditing]; + [self registerForBuffer:buffer]; + } + NSUInteger index = [_undoManager isUndoing] ? _startIndex : _endIndex; + if (index != NSNotFound) { + [view xvim_moveToIndex:index]; + } + [view _setUndoRedoInProgress:NO]; + } + + - (void)registerForBuffer:(XVimBuffer *)buffer + { + _undoManager = [buffer.undoManager retain]; + [_undoManager registerUndoWithTarget:buffer selector:@selector(undoRedo:) object:self]; + } + + @end From e3bcb6239a986630a9c03cbf4eca64afdfe3887a Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Mon, 18 Nov 2013 21:52:01 +0100 Subject: [PATCH 04/44] Rework undo to take complete ownership of some operations This makes editing the text-storage faster than going through the view obviously. Use this as a proof of concept to reimplement the bulk of xvim_shift as -[XVimBuffer shiftLines:column:count:right:block:] The performance that this code reaches now is bluffing compared to the previous one. It's still not nearly as fast as vim, but it's decent. Note that the cursor position handling code is still messy, which makes the number of parameters to that selector properly disgusting, but we're getting there. --- XVim/NSTextView+VimOperation.m | 83 ++------------------- XVim/XVimBuffer.h | 10 ++- XVim/XVimBuffer.m | 127 ++++++++++++++++++++++++++++++--- XVim/XVimDefs.h | 1 - XVim/XVimUndo.m | 78 ++++++++++---------- 5 files changed, 175 insertions(+), 124 deletions(-) diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index c548a395..81f18036 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -87,48 +87,6 @@ - (void)_xvim_insertSpaces:(NSUInteger)count replacementRange:(NSRange)replaceme } } -- (void)_xvim_removeSpacesAtLine:(NSUInteger)line column:(NSUInteger)column count:(NSUInteger)count - undoOperation:(XVimUndoOperation *)op -{ - XVimBuffer *buffer = self.textStorage.xvim_buffer; - NSUInteger tabWidth = buffer.tabWidth; - NSUInteger spaces = 0, width = 0, start; - xvim_string_buffer_t sb; - - start = [buffer indexOfLineNumber:line column:column]; - - xvim_sb_init(&sb, buffer.string, start, start, buffer.length); - if (xvim_sb_peek(&sb) == '\t') { - spaces = column - [buffer columnOfIndex:start]; - } else if (xvim_sb_peek(&sb) != ' ') { - return; - } - - while (width < count) { - unichar c = xvim_sb_peek(&sb); - - if (c != ' ' && c != '\t') { - break; - } - if (c == '\t') { - width += tabWidth - ((column + width) % tabWidth); - } else { - width++; - } - - if (!xvim_sb_next(&sb)) { - break; - } - } - - if (width > count) { - spaces += width - count; - } - - NSRange range = xvim_sb_range_to_start(&sb); - [buffer replaceCharactersInRange:range withString:[NSString stringMadeOfSpaces:spaces] undoObject:op]; -} - - (XVimRange)_xvim_selectedLines{ if (self.selectionMode == XVIM_VISUAL_NONE) { // its not in selecting mode return (XVimRange){ NSNotFound, NSNotFound }; @@ -1209,7 +1167,7 @@ - (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right XVimBuffer *buffer = self.textStorage.xvim_buffer; NSUInteger shiftWidth = buffer.indentWidth; - NSUInteger column = 0; + NSUInteger column = 0, pos; XVimRange lines; BOOL blockMode = NO; @@ -1227,47 +1185,23 @@ - (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right column = sel.left; lines = XVimMakeRange(sel.top, sel.bottom); - blockMode = YES; shiftWidth *= motion.count; + blockMode = YES; } - NSUInteger pos = [buffer indexOfLineNumber:lines.begin column:0]; - XVimUndoOperation *op = nil; - if (blockMode) { pos = [buffer indexOfLineNumber:lines.begin column:column]; } else { - pos = [buffer indexOfLineNumber:lines.begin column:0]; + pos = [buffer indexOfLineNumber:lines.begin]; pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; } - if (right) { - NSString *s = [NSString stringMadeOfSpaces:shiftWidth]; - - [self xvim_registerIndexForUndo:[buffer firstNonblankInLineAtIndex:pos allowEOL:YES]]; - [self xvim_blockInsertFixupWithText:s mode:XVIM_INSERT_SPACES count:1 column:column lines:lines]; - } else { - op = [[XVimUndoOperation alloc] initWithIndex:pos]; - [buffer.textStorage beginEditing]; - for (NSUInteger line = lines.begin; line <= lines.end; line++) { - [self _xvim_removeSpacesAtLine:line column:column count:shiftWidth undoOperation:op]; - } - } + [buffer beginEditingAtIndex:pos]; + pos = [buffer shiftLines:lines column:column + count:shiftWidth right:right block:blockMode]; + [buffer endEditingAtIndex:pos]; - if (blockMode) { - pos = [buffer indexOfLineNumber:lines.begin column:column]; - } else { - pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; - } [self xvim_moveCursor:pos preserveColumn:NO]; - - if (op) { - [op setEndIndex:pos]; - [op registerForBuffer:buffer]; - [op release]; - [buffer.textStorage endEditing]; - } - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; } @@ -1490,9 +1424,6 @@ - (void)xvim_blockInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint) NSUInteger pos = [buffer indexOfLineNumber:line column:column]; if (column != XVimSelectionEOL && [ts isEOL:pos]) { - if (mode == XVIM_INSERT_SPACES && column == 0) { - continue; - } if ([buffer columnOfIndex:pos] < column) { if (mode != XVIM_INSERT_APPEND) { continue; diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 695d03b9..4ded962c 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -7,6 +7,7 @@ // #import +#import "XVimDefs.h" #pragma mark Macros @@ -272,9 +273,14 @@ - (void)undoRedo:(XVimUndoOperation *)op; +- (void)beginEditingAtIndex:(NSUInteger)index; +- (void)endEditingAtIndex:(NSUInteger)index; + - (void)replaceCharactersInRange:(NSRange)range - withString:(NSString *)string - undoObject:(XVimUndoOperation *)op; + withString:(NSString *)string; + +- (NSUInteger)shiftLines:(XVimRange)lines column:(NSUInteger)column + count:(NSUInteger)count right:(BOOL)right block:(BOOL)block; @end diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index da87f7f1..5f93b25a 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -11,17 +11,14 @@ #import "XVimBuffer.h" #import "XVimUndo.h" #import "XVimTextStoring.h" +#import "NSString+VimHelper.h" static char const * const XVIM_KEY_BUFFER = "xvim_buffer"; -NS_INLINE BOOL isNewline(unichar ch) -{ - return [[NSCharacterSet newlineCharacterSet] characterIsMember:ch]; -} - @implementation XVimBuffer { NSDocument *__unsafe_unretained _document; NSTextStorage *__unsafe_unretained _textStorage; + XVimUndoOperation *_curOp; struct { unsigned has_xvim_string : 1; @@ -65,6 +62,12 @@ - (instancetype)initWithDocument:(NSDocument *)document return self; } +- (void)dealloc +{ + [_curOp release]; + [super dealloc]; +} + + (XVimBuffer *)makeBufferForDocument:(NSDocument *)document textStorage:(NSTextStorage *)textStorage { @@ -419,14 +422,120 @@ - (void)undoRedo:(XVimUndoOperation *)op [op undoRedo:self view:nil]; } -- (void)replaceCharactersInRange:(NSRange)range - withString:(NSString *)string - undoObject:(XVimUndoOperation *)op +- (void)beginEditingAtIndex:(NSUInteger)index { - [op addUndoRange:range replacementRange:NSMakeRange(range.location, string.length) buffer:self]; + NSAssert(!_curOp, @"you can't call -beginEditingAtIndex: twice"); + [_curOp release]; + _curOp = [[XVimUndoOperation alloc] initWithIndex:index]; +} + +- (void)endEditingAtIndex:(NSUInteger)index +{ + [_curOp setEndIndex:index]; + [_curOp registerForBuffer:self]; + [_curOp release]; + _curOp = nil; +} + +- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string +{ + NSAssert(_curOp, @"you must call -beginEditingAtIndex: first"); + [_curOp addUndoRange:range replacementRange:NSMakeRange(range.location, string.length) buffer:self]; [_textStorage replaceCharactersInRange:range withString:string]; } +- (void)_removeSpacesAtLine:(NSUInteger)line column:(NSUInteger)column count:(NSUInteger)count +{ + NSUInteger tabWidth = self.tabWidth; + NSUInteger spaces = 0, width = 0, start; + xvim_string_buffer_t sb; + + start = [self indexOfLineNumber:line column:column]; + + xvim_sb_init(&sb, self.string, start, start, self.length); + if (xvim_sb_peek(&sb) == '\t') { + spaces = column - [self columnOfIndex:start]; + } else if (xvim_sb_peek(&sb) != ' ') { + return; + } + + while (width < count) { + unichar c = xvim_sb_peek(&sb); + + if (c != ' ' && c != '\t') { + break; + } + if (c == '\t') { + width += tabWidth - ((column + width) % tabWidth); + } else { + width++; + } + + if (!xvim_sb_next(&sb)) { + break; + } + } + + if (width > count) { + spaces += width - count; + } + + NSRange range = xvim_sb_range_to_start(&sb); + [self replaceCharactersInRange:range withString:[NSString stringMadeOfSpaces:spaces]]; +} + +- (NSUInteger)shiftLines:(XVimRange)lines column:(NSUInteger)column + count:(NSUInteger)count right:(BOOL)right block:(BOOL)blockMode +{ + NSString *string = self.string; + + [_textStorage beginEditing]; + + if (right) { + NSString *s = [NSString stringMadeOfSpaces:count]; + NSUInteger tabWidth = self.tabWidth; + + for (NSUInteger line = lines.begin; line <= lines.end; line++) { + NSUInteger index, spaces = 0; + + index = [self indexOfLineNumber:line column:column]; + if (index >= self.length || isNewline([string characterAtIndex:index])) { + if (column == 0 || [self columnOfIndex:column] < column) { + continue; + } + } + + if (tabWidth && [string characterAtIndex:index] == '\t') { + NSUInteger col = [self columnOfIndex:index]; + + spaces = tabWidth - (col % tabWidth); + } + + if (spaces) { + NSString *s2 = [NSString stringMadeOfSpaces:count + spaces]; + [self replaceCharactersInRange:NSMakeRange(index, 1) withString:s2]; + } else { + [self replaceCharactersInRange:NSMakeRange(index, 0) withString:s]; + } + } + } else { + for (NSUInteger line = lines.begin; line <= lines.end; line++) { + [self _removeSpacesAtLine:line column:column count:count]; + } + } + + NSUInteger pos; + if (blockMode) { + pos = [self indexOfLineNumber:lines.begin column:column]; + } else { + pos = [self indexOfLineNumber:lines.begin]; + pos = [self firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + + [_textStorage endEditing]; + return pos; +} + @end @implementation NSTextStorage (XVimBuffer) diff --git a/XVim/XVimDefs.h b/XVim/XVimDefs.h index a80f8fde..f56abeb0 100644 --- a/XVim/XVimDefs.h +++ b/XVim/XVimDefs.h @@ -21,7 +21,6 @@ typedef NS_ENUM(NSUInteger, XVimInsertionPoint) { XVIM_INSERT_DEFAULT, - XVIM_INSERT_SPACES, XVIM_INSERT_APPEND, XVIM_INSERT_BEFORE_FIRST_NONBLANK, XVIM_INSERT_APPEND_EOL, diff --git a/XVim/XVimUndo.m b/XVim/XVimUndo.m index 8d739f29..858678cd 100644 --- a/XVim/XVimUndo.m +++ b/XVim/XVimUndo.m @@ -10,14 +10,20 @@ #import "NSTextView+VimOperation.h" #import "XVimBuffer.h" -// AppKit class dump on 10.9 @interface NSTextView (NSPrivate) +/* Cocoa Internal: + * Cocoa seems to call that when -undo is called, + * presumably to prevent the view from refreshing. + */ - (void)_setUndoRedoInProgress:(BOOL)arg1; @end -// AppKit class dump on 10.9 @interface NSTextStorage (NSUndo) -// return type is presumably an NSAttributedString +/* Cocoa Internal: + * Looking at a class dump, this NSUndo category looks intersting + * and breaking on it in a debugger shows that it's called to capture + * the text being replaced in the undo operation, let's mimic that. + */ - (id)_undoRedoAttributedSubstringFromRange:(NSRange)arg1; @end @@ -69,44 +75,44 @@ - (void)addUndoRange:(NSRange)range - (void)_undoOp:(NSUInteger)i textStorage:(NSTextStorage *)ts range:(NSRange)range { NSUInteger index= 3 * i + 2; - NSAttributedString *oldText = [_values objectAtIndex:index]; - NSAttributedString *newText = [ts _undoRedoAttributedSubstringFromRange:range]; + NSAttributedString *oldText = [_values objectAtIndex:index]; + NSAttributedString *newText = [ts _undoRedoAttributedSubstringFromRange:range]; - [ts replaceCharactersInRange:range withAttributedString:oldText]; - [_values replaceObjectAtIndex:index withObject:newText]; - } + [ts replaceCharactersInRange:range withAttributedString:oldText]; + [_values replaceObjectAtIndex:index withObject:newText]; +} - - (void)undoRedo:(XVimBuffer *)buffer view:(NSTextView *)view - { - NSTextStorage *ts = buffer.textStorage; - NSUInteger count = _values.count / 3; +- (void)undoRedo:(XVimBuffer *)buffer view:(NSTextView *)view +{ + NSTextStorage *ts = buffer.textStorage; + NSUInteger count = _values.count / 3; - [view _setUndoRedoInProgress:YES]; - if (!view || [view shouldChangeTextInRange:NSMakeRange(NSNotFound, 0) replacementString:@""]) { - [ts beginEditing]; - if ([_undoManager isUndoing]) { - for (NSUInteger i = count; i-- > 0; ) { - [self _undoOp:i textStorage:ts range:[[_values objectAtIndex:3 * i + 1] rangeValue]]; - } - } else { - for (NSUInteger i = 0; i < count; i ++ ) { - [self _undoOp:i textStorage:ts range:[[_values objectAtIndex:3 * i + 0] rangeValue]]; - } - } - [ts endEditing]; - [self registerForBuffer:buffer]; + [view _setUndoRedoInProgress:YES]; + if (!view || [view shouldChangeTextInRange:NSMakeRange(NSNotFound, 0) replacementString:@""]) { + [ts beginEditing]; + if ([_undoManager isUndoing]) { + for (NSUInteger i = count; i-- > 0; ) { + [self _undoOp:i textStorage:ts range:[[_values objectAtIndex:3 * i + 1] rangeValue]]; } - NSUInteger index = [_undoManager isUndoing] ? _startIndex : _endIndex; - if (index != NSNotFound) { - [view xvim_moveToIndex:index]; + } else { + for (NSUInteger i = 0; i < count; i ++ ) { + [self _undoOp:i textStorage:ts range:[[_values objectAtIndex:3 * i + 0] rangeValue]]; } - [view _setUndoRedoInProgress:NO]; } + [ts endEditing]; + [self registerForBuffer:buffer]; + } + NSUInteger index = [_undoManager isUndoing] ? _startIndex : _endIndex; + if (index != NSNotFound) { + [view xvim_moveToIndex:index]; + } + [view _setUndoRedoInProgress:NO]; +} - - (void)registerForBuffer:(XVimBuffer *)buffer - { - _undoManager = [buffer.undoManager retain]; - [_undoManager registerUndoWithTarget:buffer selector:@selector(undoRedo:) object:self]; - } +- (void)registerForBuffer:(XVimBuffer *)buffer +{ + _undoManager = [buffer.undoManager retain]; + [_undoManager registerUndoWithTarget:buffer selector:@selector(undoRedo:) object:self]; +} - @end +@end From 694d02ec85cbfb5d949a7de8ee21ffb700eb5f6c Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Tue, 19 Nov 2013 01:38:28 +0100 Subject: [PATCH 05/44] Move xvim_incrementNumber to XVimBuffer, where it belongs --- XVim/NSTextView+VimOperation.h | 3 +- XVim/NSTextView+VimOperation.m | 109 ++---------------------------- XVim/XVimBuffer.h | 4 ++ XVim/XVimBuffer.m | 118 ++++++++++++++++++++++++++++++++- 4 files changed, 128 insertions(+), 106 deletions(-) diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h index 3dd331cb..f7214f29 100644 --- a/XVim/NSTextView+VimOperation.h +++ b/XVim/NSTextView+VimOperation.h @@ -150,8 +150,7 @@ - (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt; - (void)xvim_clearHighlightText; - (NSRange)xvim_currentWord:(MOTION_OPTION)opt; -- (NSRange)xvim_numberAtIndex:(NSUInteger)index; - + #pragma mark Searching positions // TODO: Thses method should be internal. Create abstracted interface to achieve the operation uses these methods. /** diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index 81f18036..05a8a05b 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -1353,44 +1353,15 @@ - (void)xvim_overwriteCharacter:(unichar)c{ return; } -- (BOOL)xvim_incrementNumber:(int64_t)offset{ +- (BOOL)xvim_incrementNumber:(int64_t)offset +{ NSUInteger ip = self.insertionPoint; - NSRange range; - - range = [self xvim_numberAtIndex:ip]; - if (range.location == NSNotFound) { - NSUInteger pos = [self.textStorage.xvim_buffer nextDigitInLine:ip]; - if (pos == NSNotFound) { - return NO; - } - range = [self xvim_numberAtIndex:pos]; - if (range.location == NSNotFound) { - // should not happen - self.insertionPoint = ip; - return NO; - } - ip = pos; - } - [self xvim_registerInsertionPointForUndo]; - - const char *s = [[self.xvim_string substringWithRange:range] UTF8String]; - NSString *repl; - uint64_t u = strtoull(s, NULL, 0); - int64_t i = strtoll(s, NULL, 0); - - if (strncmp(s, "0x", 2) == 0) { - repl = [NSString stringWithFormat:@"0x%0*llx", (int)strlen(s) - 2, u + (uint64_t)offset]; - } else if (u && *s == '0' && s[1] && !strchr(s, '9') && !strchr(s, '8')) { - repl = [NSString stringWithFormat:@"0%0*llo", (int)strlen(s) - 1, u + (uint64_t)offset]; - } else if (u && *s == '+') { - repl = [NSString stringWithFormat:@"%+lld", i + offset]; - } else { - repl = [NSString stringWithFormat:@"%lld", i + offset]; + ip = [self.textStorage.xvim_buffer incrementNumberAtIndex:ip by:offset]; + if (ip == NSNotFound) { + return NO; } - - [self insertText:repl replacementRange:range]; - [self xvim_moveCursor:range.location + repl.length - 1 preserveColumn:NO]; + [self xvim_moveCursor:ip preserveColumn:NO]; return YES; } @@ -1824,74 +1795,6 @@ - (NSRange)xvim_currentWord:(MOTION_OPTION)opt{ return [self.textStorage currentWord:self.insertionPoint count:1 option:opt|TEXTOBJECT_INNER]; } -- (NSRange)xvim_numberAtIndex:(NSUInteger)index -{ - NSUInteger n_start, n_end; - NSUInteger x_start, x_end; - NSString *s = self.xvim_string; - unichar c; - BOOL isOctal = YES; - - n_start = index; - while (n_start > 0 && [s isDigit:n_start - 1]) { - if (![s isOctDigit:n_start]) { - isOctal = NO; - } - n_start--; - } - n_end = index; - while (n_end < s.length && [s isDigit:n_end]) { - if (![s isOctDigit:n_end]) { - isOctal = NO; - } - n_end++; - } - - x_start = n_start; - while (x_start > 0 && [s isHexDigit:x_start - 1]) { - x_start--; - } - x_end = n_end; - while (x_end < s.length && [s isHexDigit:x_end]) { - x_end++; - } - - // first deal with Hex: 0xNNNNN - // case 1: check for insertion point on the '0' or 'x' - if (x_end - x_start == 1) { - NSUInteger end = x_end; - if (end < s.length && [s characterAtIndex:end] == 'x') { - do { - end++; - } while (end < s.length && [s isHexDigit:end]); - if (index < end && end - x_start > 2) { - // YAY it's hex for real!!! - return NSMakeRange(x_start, end - x_start); - } - } - } - - // case 2: check whether we're after 0x - if (index < x_end && x_end - x_start >= 1) { - if (x_start >= 2 && [s characterAtIndex:x_start - 1] == 'x' && [s characterAtIndex:x_start - 2] == '0') { - return NSMakeRange(x_start - 2, x_end - x_start + 2); - } - } - - if (index == n_end || n_start - n_end == 0) { - return NSMakeRange(NSNotFound, 0); - } - - // okay it's not hex, if it's not octal, check for leading +/- - if (n_start > 0 && !(isOctal && [s characterAtIndex:n_start] == '0')) { - c = [s characterAtIndex:n_start - 1]; - if (c == '+' || c == '-') { - n_start--; - } - } - return NSMakeRange(n_start, n_end - n_start); -} - #pragma mark Search Position /** * Takes point in view and returns its index. diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 4ded962c..507eb48e 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -279,9 +279,13 @@ - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string; +// Never fails - (NSUInteger)shiftLines:(XVimRange)lines column:(NSUInteger)column count:(NSUInteger)count right:(BOOL)right block:(BOOL)block; +// May return NSNotFound +- (NSUInteger)incrementNumberAtIndex:(NSUInteger)index by:(int64_t)offset; + @end @interface NSTextStorage (XVimBuffer) diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index 5f93b25a..795c1c19 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -429,9 +429,15 @@ - (void)beginEditingAtIndex:(NSUInteger)index _curOp = [[XVimUndoOperation alloc] initWithIndex:index]; } +- (void)cancelEditing +{ + [_curOp release]; + _curOp = nil; +} + - (void)endEditingAtIndex:(NSUInteger)index { - [_curOp setEndIndex:index]; + _curOp.endIndex = index; [_curOp registerForBuffer:self]; [_curOp release]; _curOp = nil; @@ -536,6 +542,116 @@ - (NSUInteger)shiftLines:(XVimRange)lines column:(NSUInteger)column return pos; } +- (NSRange)_numberAtIndex:(NSUInteger)index +{ + NSUInteger n_start, n_end; + NSUInteger x_start, x_end; + NSString *s = self.string; + unichar c; + BOOL isOctal = YES; + + n_start = index; + while (n_start > 0 && [s isDigit:n_start - 1]) { + if (![s isOctDigit:n_start]) { + isOctal = NO; + } + n_start--; + } + n_end = index; + while (n_end < s.length && [s isDigit:n_end]) { + if (![s isOctDigit:n_end]) { + isOctal = NO; + } + n_end++; + } + + x_start = n_start; + while (x_start > 0 && [s isHexDigit:x_start - 1]) { + x_start--; + } + x_end = n_end; + while (x_end < s.length && [s isHexDigit:x_end]) { + x_end++; + } + + // first deal with Hex: 0xNNNNN + // case 1: check for insertion point on the '0' or 'x' + if (x_end - x_start == 1) { + NSUInteger end = x_end; + if (end < s.length && [s characterAtIndex:end] == 'x') { + do { + end++; + } while (end < s.length && [s isHexDigit:end]); + if (index < end && end - x_start > 2) { + // YAY it's hex for real!!! + return NSMakeRange(x_start, end - x_start); + } + } + } + + // case 2: check whether we're after 0x + if (index < x_end && x_end - x_start >= 1) { + if (x_start >= 2 && [s characterAtIndex:x_start - 1] == 'x' && [s characterAtIndex:x_start - 2] == '0') { + return NSMakeRange(x_start - 2, x_end - x_start + 2); + } + } + + if (index == n_end || n_start - n_end == 0) { + return NSMakeRange(NSNotFound, 0); + } + + // okay it's not hex, if it's not octal, check for leading +/- + if (n_start > 0 && !(isOctal && [s characterAtIndex:n_start] == '0')) { + c = [s characterAtIndex:n_start - 1]; + if (c == '+' || c == '-') { + n_start--; + } + } + return NSMakeRange(n_start, n_end - n_start); +} + +- (NSUInteger)incrementNumberAtIndex:(NSUInteger)index by:(int64_t)offset +{ + NSRange range; + BOOL doOp = _curOp == nil; + + range = [self _numberAtIndex:index]; + if (range.location == NSNotFound) { + NSUInteger pos = [self.textStorage.xvim_buffer nextDigitInLine:index]; + if (pos == NSNotFound) { + return NSNotFound; + } + range = [self _numberAtIndex:pos]; + if (range.location == NSNotFound) { + // should not happen + return NSNotFound; + } + } + + const char *s = [[self.string substringWithRange:range] UTF8String]; + NSString *repl; + uint64_t u = strtoull(s, NULL, 0); + int64_t i = strtoll(s, NULL, 0); + + if (strncmp(s, "0x", 2) == 0) { + repl = [NSString stringWithFormat:@"0x%0*llx", (int)strlen(s) - 2, u + (uint64_t)offset]; + } else if (u && *s == '0' && s[1] && !strchr(s, '9') && !strchr(s, '8')) { + repl = [NSString stringWithFormat:@"0%0*llo", (int)strlen(s) - 1, u + (uint64_t)offset]; + } else if (u && *s == '+') { + repl = [NSString stringWithFormat:@"%+lld", i + offset]; + } else { + repl = [NSString stringWithFormat:@"%lld", i + offset]; + } + + if (doOp) [self beginEditingAtIndex:index]; + [_textStorage beginEditing]; + [self replaceCharactersInRange:range withString:repl]; + [_textStorage endEditing]; + index = range.location + repl.length - 1; + if (doOp) [self endEditingAtIndex:index]; + return index; +} + @end @implementation NSTextStorage (XVimBuffer) From 01d91008b3da74c92c17d9ffd3cfd7bcd0b05ffc Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Tue, 19 Nov 2013 10:56:22 +0100 Subject: [PATCH 06/44] Disable GC on the XCode5 plugin --- XVim/Info_Xcode5.plist | 2 -- XVim/XVimEvaluator.h | 4 ++-- XVim/XVimOptions.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/XVim/Info_Xcode5.plist b/XVim/Info_Xcode5.plist index 5c601dc1..c8f3d5f8 100644 --- a/XVim/Info_Xcode5.plist +++ b/XVim/Info_Xcode5.plist @@ -29,8 +29,6 @@ ${PRODUCT_NAME} XC4Compatible - XCGCReady - XCPluginHasUI diff --git a/XVim/XVimEvaluator.h b/XVim/XVimEvaluator.h index f2699496..4b85e8e0 100644 --- a/XVim/XVimEvaluator.h +++ b/XVim/XVimEvaluator.h @@ -47,10 +47,10 @@ XVimMotionEvaluator @interface XVimEvaluator : NSObject @property(strong) XVimWindow* window; @property(strong) XVimEvaluator* parent; -@property NSUInteger numericArg; +@property(nonatomic) NSUInteger numericArg; @property BOOL numericMode; @property(strong) NSMutableString* argumentString; -@property(strong) NSString* yankRegister; +@property(nonatomic, strong) NSString* yankRegister; @property SEL onChildCompleteHandler; - (id)initWithWindow:(XVimWindow*)window; diff --git a/XVim/XVimOptions.h b/XVim/XVimOptions.h index 2d03e5f1..1fa6c322 100644 --- a/XVim/XVimOptions.h +++ b/XVim/XVimOptions.h @@ -17,7 +17,7 @@ @property BOOL smartcase; @property BOOL debug; @property BOOL hlsearch; -@property BOOL number; +@property (nonatomic) BOOL number; @property (copy) NSString *clipboard; @property (copy) NSString *guioptions; @property (copy) NSString *timeoutlen; From b9709a6a6276748fdd3632f2ac8378fc272a9405 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Tue, 19 Nov 2013 21:56:43 +0100 Subject: [PATCH 07/44] Remove useless __USE_DVTKIT__ dependency --- XVim/NSTextView+VimOperation.h | 1 - XVim/NSTextView+VimOperation.m | 23 +---------------------- XVim/XVimNormalEvaluator.m | 4 ++-- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h index f7214f29..c331aa94 100644 --- a/XVim/NSTextView+VimOperation.h +++ b/XVim/NSTextView+VimOperation.h @@ -66,7 +66,6 @@ @property(strong) id xvimDelegate; @property BOOL needsUpdateFoundRanges; @property(readonly) NSArray* foundRanges; -@property(readonly) long long currentLineNumber; #pragma mark Changing state - (void)xvim_changeSelectionMode:(XVIM_VISUAL_MODE)mode; diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index 05a8a05b..5b8f5c4a 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -438,15 +438,7 @@ - (void)setCursorMode:(CURSOR_MODE)cursorMode{ } - (NSURL*)documentURL{ -#ifdef __USE_DVTKIT__ - if( [self.delegate isKindOfClass:[IDEEditor class]] ){ - return [(IDEEditorDocument*)((IDEEditor*)self.delegate).document fileURL]; - }else{ - return nil; - } -#else - return nil; -#endif + return self.textStorage.xvim_buffer.document.fileURL; } - (void)setXvimDelegate:(id)xvimDelegate{ @@ -493,19 +485,6 @@ - (void) setLastYankedType:(TEXT_TYPE)type{ [self setInteger:type forName:@"lastYankedType"]; } -- (long long)currentLineNumber { -#ifdef __USE_DVTKIT__ - if( [self isKindOfClass:[DVTSourceTextView class]] ){ - return [(DVTSourceTextView*)self _currentLineNumber]; - } -#else -#error You must implement here. -#endif - NSAssert(NO, @"You must implement here if you do not use this with DVTSourceTextView"); - return -1; -} - - - (NSString *)xvim_string { return self.textStorage.xvim_buffer.string; diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index a1313850..0d61474c 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -151,12 +151,12 @@ - (XVimEvaluator*)C_g NSRange range = window.sourceView.selectedRange; NSUInteger numberOfLines = [buffer numberOfLines]; - long long lineNumber = [window.sourceView currentLineNumber]; + NSUInteger lineNumber = [buffer lineNumberAtIndex:range.location]; NSUInteger columnNumber = [buffer columnOfIndex:range.location]; NSURL *documentURL = buffer.document.fileURL; if ([documentURL isFileURL]) { - NSString *text = [NSString stringWithFormat:@"%@ line %lld of %ld --%d%%-- col %ld", + NSString *text = [NSString stringWithFormat:@"%@ line %ld of %ld --%d%%-- col %ld", documentURL.path, lineNumber, numberOfLines, (int)((float)lineNumber*100.0/(float)numberOfLines), columnNumber+1 ]; From 17155262e006dd0d30d2c25909eb96ef8cb927de Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Tue, 19 Nov 2013 23:18:46 +0100 Subject: [PATCH 08/44] Get rid of the lastVisual* properties on the XVim instance Move them to a proper ivar of the XVimBuffer instead This is the beginning of moving a lot of stuff there, so that we can capture the buffer state on undo/redo properly. --- XVim/NSTextView+VimOperation.h | 2 - XVim/NSTextView+VimOperation.m | 16 ---- XVim/XVim.h | 4 - XVim/XVim.m | 2 - XVim/XVimBuffer.h | 13 +++- XVim/XVimBuffer.m | 5 ++ XVim/XVimDefs.h | 20 +++++ XVim/XVimInsertEvaluator.m | 1 - XVim/XVimVisualEvaluator.h | 1 - XVim/XVimVisualEvaluator.m | 129 ++++++++++++++++++--------------- 10 files changed, 108 insertions(+), 85 deletions(-) diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h index c331aa94..4ddc0d3f 100644 --- a/XVim/NSTextView+VimOperation.h +++ b/XVim/NSTextView+VimOperation.h @@ -57,8 +57,6 @@ @property(readonly) NSUInteger insertionLine; @property(readonly) NSUInteger preservedColumn; @property(readonly) NSUInteger selectionBegin; -@property(readonly) XVimPosition selectionBeginPosition; -@property(readonly) NSUInteger numberOfSelectedLines; @property(readonly) XVIM_VISUAL_MODE selectionMode; @property(readonly) BOOL selectionToEOL; @property(readonly) CURSOR_MODE cursorMode; diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index 5b8f5c4a..6d609472 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -45,7 +45,6 @@ @interface NSTextView () //@property NSUInteger insertionLine; // This is readonly also internally @property NSUInteger preservedColumn; @property NSUInteger selectionBegin; -//@property XVimPosition selectionBeginPosition; // This is readonly also internally @property XVIM_VISUAL_MODE selectionMode; @property BOOL selectionToEOL; @property CURSOR_MODE cursorode; @@ -394,20 +393,6 @@ - (void)setSelectionBegin:(NSUInteger)selectionBegin{ [self setUnsignedInteger:selectionBegin forName:@"selectionBegin"]; } -- (XVimPosition)selectionBeginPosition{ - XVimBuffer *buffer = self.textStorage.xvim_buffer; - return XVimMakePosition([buffer lineNumberAtIndex:self.selectionBegin], [buffer columnOfIndex:self.selectionBegin]); -} - -- (NSUInteger)numberOfSelectedLines{ - if (XVIM_VISUAL_NONE == self.selectionMode) { - return 0; - } - - XVimRange lines = [self _xvim_selectedLines]; - return lines.end - lines.begin + 1; -} - - (BOOL)selectionToEOL{ return [[self dataForName:@"selectionToEOL"] boolValue]; } @@ -423,7 +408,6 @@ - (XVIM_VISUAL_MODE) selectionMode{ - (void)setSelectionMode:(XVIM_VISUAL_MODE)selectionMode{ if (self.selectionMode != selectionMode) { - self.selectionToEOL = NO; [self setInteger:selectionMode forName:@"selectionMode"]; } } diff --git a/XVim/XVim.h b/XVim/XVim.h index f29acd1f..eed4f544 100644 --- a/XVim/XVim.h +++ b/XVim/XVim.h @@ -43,10 +43,6 @@ extern NSString * const XVimBufferKey; @property (readonly) XVimHistoryHandler* exCommandHistory; @property (readonly) XVimHistoryHandler* searchHistory; @property (readonly) XVimMutableString *lastOperationCommands; -@property XVIM_VISUAL_MODE lastVisualMode; -@property XVimPosition lastVisualPosition; -@property XVimPosition lastVisualSelectionBegin; -@property BOOL lastVisualSelectionToEOL; @property (nonatomic) BOOL isRepeating; // For dot(.) command repeat @property (copy) NSString* lastPlaybackRegister; diff --git a/XVim/XVim.m b/XVim/XVim.m index 55e044b6..6a600a21 100644 --- a/XVim/XVim.m +++ b/XVim/XVim.m @@ -195,8 +195,6 @@ - (void)init2{ self.lastPlaybackRegister = nil; self.registerManager = [[[XVimRegisterManager alloc] init] autorelease]; self.lastOperationCommands = [[[XVimMutableString alloc] init] autorelease]; - self.lastVisualPosition = XVimMakePosition(NSNotFound, NSNotFound); - self.lastVisualSelectionBegin = XVimMakePosition(NSNotFound, NSNotFound); self.tempRepeatRegister = [[[XVimMutableString alloc] init] autorelease]; self.isRepeating = NO; self.isExecuting = NO; diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 507eb48e..4bfcd458 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -103,7 +103,10 @@ * */ -@interface XVimBuffer : NSObject +@interface XVimBuffer : NSObject { +@public + XVimVisualInfo visualInfo; +} @property (nonatomic, readonly) NSDocument *document; @property (nonatomic, readonly) NSTextStorage *textStorage; @@ -180,6 +183,14 @@ */ - (NSUInteger)columnOfIndex:(NSUInteger)index; +/** @brief returns the XVim Posiion of \a index + * + * Column numbers starts at 0. + * + * This never returns NSNotFound. + */ +- (XVimPosition)positionOfIndex:(NSUInteger)index; + /** @brief returns number of columns for the line containing \a index. * * If the specified line does not exist in the current document it returns NSNotFound diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index 795c1c19..2bfe52db 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -283,6 +283,11 @@ - (NSUInteger)columnOfIndex:(NSUInteger)index return xvim_sb_count_columns(&sb, self.tabWidth); } +- (XVimPosition)positionOfIndex:(NSUInteger)index +{ + return XVimMakePosition([self lineNumberAtIndex:index], [self columnOfIndex:index]); +} + - (NSUInteger)numberOfColumnsInLineAtIndex:(NSUInteger)index { NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; diff --git a/XVim/XVimDefs.h b/XVim/XVimDefs.h index f56abeb0..09b61c5d 100644 --- a/XVim/XVimDefs.h +++ b/XVim/XVimDefs.h @@ -74,8 +74,28 @@ typedef struct _XVimSelection { NSUInteger right; } XVimSelection; +typedef struct { + XVIM_VISUAL_MODE mode; + NSUInteger colwant; + XVimPosition start; + XVimPosition end; +} XVimVisualInfo; + #define XVimSelectionEOL (NSIntegerMax - 1) +NS_INLINE NSUInteger XVimVisualInfoColumns(XVimVisualInfo *vi) +{ + if (vi->end.column == XVimSelectionEOL) { + return XVimSelectionEOL; + } + return MAX(vi->end.column, vi->start.column) - MIN(vi->end.column, vi->start.column) + 1; +} + +NS_INLINE NSUInteger XVimVisualInfoLines(XVimVisualInfo *vi) +{ + return MAX(vi->end.line, vi->start.line) - MIN(vi->end.line, vi->start.line) + 1; +} + NS_INLINE XVimRange XVimMakeRange(NSUInteger begin, NSUInteger end) { XVimRange r; r.begin = begin; diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index 08cae2d1..c619339b 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -193,7 +193,6 @@ - (void)didEndHandler{ // Store off any needed text XVim *xvim = [XVim instance]; - xvim.lastVisualMode = self.sourceView.selectionMode; [xvim fixOperationCommands]; if( _oneCharMode ){ }else if (!self.movementKeyPressed){ diff --git a/XVim/XVimVisualEvaluator.h b/XVim/XVimVisualEvaluator.h index 9814d149..2cb71cd4 100644 --- a/XVim/XVimVisualEvaluator.h +++ b/XVim/XVimVisualEvaluator.h @@ -16,6 +16,5 @@ - (id)initWithWindow:(XVimWindow*)window mode:(XVIM_VISUAL_MODE)mode; - (id)initWithLastVisualStateWithWindow:(XVimWindow *)window; - @end diff --git a/XVim/XVimVisualEvaluator.m b/XVim/XVimVisualEvaluator.m index d3553592..526d415d 100644 --- a/XVim/XVimVisualEvaluator.m +++ b/XVim/XVimVisualEvaluator.m @@ -33,54 +33,55 @@ @interface XVimVisualEvaluator(){ BOOL _waitForArgument; - NSRange _operationRange; - XVIM_VISUAL_MODE _visual_mode; + XVimVisualInfo _initial; } -@property XVimPosition initialFromPos; -@property XVimPosition initialToPos; -@property BOOL initialToEOL; @end @implementation XVimVisualEvaluator - (id)initWithLastVisualStateWithWindow:(XVimWindow *)window{ - if( self = [self initWithWindow:window mode:[XVim instance].lastVisualMode] ){ - self.initialFromPos = [XVim instance].lastVisualSelectionBegin; - self.initialToPos = [XVim instance].lastVisualPosition; - self.initialToEOL = [XVim instance].lastVisualSelectionToEOL; + XVimBuffer *buffer = window.currentBuffer; + XVimVisualInfo *vi = &buffer->visualInfo; + + if (vi->mode == XVIM_VISUAL_NONE) { + [window errorMessage:@"no previous visual state" ringBell:YES]; + [self release]; + return nil; + } + + if ((self = [self initWithWindow:window])) { + _initial = *vi; } return self; } - (id)initWithWindow:(XVimWindow *)window mode:(XVIM_VISUAL_MODE)mode { + NSUInteger start = [[[window.sourceView selectedRanges] objectAtIndex:0] rangeValue].location; + NSUInteger end = NSMaxRange([[[window.sourceView selectedRanges] lastObject] rangeValue]); XVimBuffer *buffer = window.currentBuffer; + if (end > start) { + end--; + } + if (self = [self initWithWindow:window]) { _waitForArgument = NO; - _visual_mode = mode; - if( [window.sourceView selectedRanges].count == 1 ){ - if( [window.sourceView selectedRange].length == 0 ){ - self.initialFromPos = XVimMakePosition(NSNotFound, NSNotFound);; - self.initialToPos = XVimMakePosition(NSNotFound, NSNotFound);; - }else{ - NSUInteger start = [window.sourceView selectedRange].location; - NSUInteger end = [window.sourceView selectedRange].location + [window.sourceView selectedRange].length - 1; - self.initialFromPos = XVimMakePosition([buffer lineNumberAtIndex:start], [buffer columnOfIndex:start]); - self.initialToPos = XVimMakePosition([buffer lineNumberAtIndex:end], [buffer columnOfIndex:end]); - } - }else{ + _initial.mode = mode; + _initial.start.line = [buffer lineNumberAtIndex:start]; + _initial.start.column = [buffer columnOfIndex:start]; + _initial.end.line = [buffer lineNumberAtIndex:end]; + _initial.end.column = [buffer columnOfIndex:end]; + _initial.colwant = _initial.end.column; + + if ([window.sourceView selectedRanges].count > 1) { // Treat it as block selection - _visual_mode = XVIM_VISUAL_BLOCK; - NSUInteger start = [[[window.sourceView selectedRanges] objectAtIndex:0] rangeValue].location; - NSUInteger end = [[[window.sourceView selectedRanges] lastObject] rangeValue].location + [[[window.sourceView selectedRanges] lastObject] rangeValue].length - 1; - self.initialFromPos = XVimMakePosition([buffer lineNumberAtIndex:start], [buffer columnOfIndex:start]); - self.initialToPos = XVimMakePosition([buffer lineNumberAtIndex:end], [buffer columnOfIndex:end]); + _initial.mode = XVIM_VISUAL_BLOCK; } } return self; } - (NSString*)modeString { - return MODE_STRINGS[_visual_mode]; + return MODE_STRINGS[_initial.mode]; } - (XVIM_MODE)mode{ @@ -89,39 +90,43 @@ - (XVIM_MODE)mode{ - (void)becameHandler{ [super becameHandler]; - if( self.initialToPos.line != NSNotFound ){ - if( XVim.instance.isRepeating ){ - [self.sourceView xvim_changeSelectionMode:_visual_mode]; + + if (_initial.mode) { + if (XVim.instance.isRepeating) { + [self.sourceView xvim_changeSelectionMode:_initial.mode]; + + NSUInteger columns = XVimVisualInfoColumns(&_initial); + NSUInteger lines = XVimVisualInfoLines(&_initial); + XVimPosition pos = self.sourceView.insertionPosition; + // When repeating we have to set initial selected range - if( _visual_mode == XVIM_VISUAL_CHARACTER ){ - if( self.initialFromPos.line == self.initialToPos.line ){ + if (_initial.mode == XVIM_VISUAL_CHARACTER) { + if (lines == 1) { // Same number of character if in one line - NSUInteger numberOfColumns = self.initialToPos.column > self.initialFromPos.column ? (self.initialToPos.column - self.initialFromPos.column) : (self.initialFromPos.column - self.initialToPos.column); - [self.sourceView xvim_moveToPosition:XVimMakePosition(self.sourceView.insertionLine, self.sourceView.insertionColumn+numberOfColumns)]; - }else{ - NSUInteger numberOfLines = self.initialToPos.line > self.initialFromPos.line ? (self.initialToPos.line - self.initialFromPos.line) : (self.initialFromPos.line - self.initialToPos.line); - [self.sourceView xvim_moveToPosition:XVimMakePosition(self.sourceView.insertionLine+numberOfLines, self.initialToPos.column)]; + pos.column += columns - 1; + } else { + pos.line += lines - 1; + pos.column = _initial.start.line < _initial.end.line ? _initial.end.column : _initial.start.column; } - }else if( _visual_mode == XVIM_VISUAL_LINE ){ - // Same number of lines - NSUInteger numberOfLines = self.initialToPos.line > self.initialFromPos.line ? (self.initialToPos.line - self.initialFromPos.line) : (self.initialFromPos.line - self.initialToPos.line); - [self.sourceView xvim_moveToPosition:XVimMakePosition(self.sourceView.insertionLine+numberOfLines, self.sourceView.insertionColumn)]; - }else if( _visual_mode == XVIM_VISUAL_BLOCK ){ + } else if (_initial.mode == XVIM_VISUAL_LINE) { + pos.line += lines - 1; + } else { // Use same number of lines/colums - NSUInteger numberOfLines = self.initialToPos.line > self.initialFromPos.line ? (self.initialToPos.line - self.initialFromPos.line) : (self.initialFromPos.line - self.initialToPos.line); - NSUInteger numberOfColumns = self.initialToPos.column > self.initialFromPos.column ? (self.initialToPos.column - self.initialFromPos.column) : (self.initialFromPos.column - self.initialToPos.column); - [self.sourceView xvim_moveToPosition:XVimMakePosition(self.sourceView.insertionLine+numberOfLines, self.sourceView.insertionColumn+numberOfColumns)]; + pos.column += columns - 1; + pos.line += lines - 1; } - }else{ - [self.sourceView xvim_moveToPosition:self.initialFromPos]; - [self.sourceView xvim_changeSelectionMode:_visual_mode]; - [self.sourceView xvim_moveToPosition:self.initialToPos]; + + [self.sourceView xvim_moveToPosition:pos]; + } else { + [self.sourceView xvim_moveToPosition:_initial.start]; + [self.sourceView xvim_changeSelectionMode:_initial.mode]; + [self.sourceView xvim_moveToPosition:_initial.end]; + // TODO: self.sourceView.preservedColumn = _initial.colwant; } - }else{ - [self.sourceView xvim_changeSelectionMode:_visual_mode]; - } - if (self.initialToEOL) { - [self performSelector:@selector(DOLLAR)]; + if (_initial.end.column == XVimSelectionEOL) { + [self performSelector:@selector(DOLLAR)]; + } + _initial.mode = XVIM_VISUAL_NONE; } } @@ -139,10 +144,18 @@ - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ - [XVim instance].lastVisualMode = self.sourceView.selectionMode; - [XVim instance].lastVisualPosition = self.sourceView.insertionPosition; - [XVim instance].lastVisualSelectionBegin = self.sourceView.selectionBeginPosition; - [XVim instance].lastVisualSelectionToEOL = self.sourceView.selectionToEOL; + if (!XVim.instance.isRepeating) { + XVimBuffer *buffer = self.window.currentBuffer; + XVimVisualInfo *vi = &buffer->visualInfo; + + vi->mode = self.sourceView.selectionMode; + vi->end = self.sourceView.insertionPosition; + vi->start = [buffer positionOfIndex:self.sourceView.selectionBegin]; + vi->colwant = self.sourceView.preservedColumn; + if (self.sourceView.selectionToEOL) { + vi->end.column = XVimSelectionEOL; + } + } XVimEvaluator *nextEvaluator = [super eval:keyStroke]; From 030a7951d795c1ca0a75219be32325a0caac54d8 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Wed, 20 Nov 2013 08:22:21 +0100 Subject: [PATCH 09/44] Reimplement paragraph moves using XVimBuffer clean methods As a side effect, this means we now support CRLF properly for this command, which should help for #530 Fix a couple of issues in the XVimBuffer methods that this new code spotted. --- XVim/NSTextStorage+VimOperation.h | 3 +- XVim/NSTextStorage+VimOperation.m | 119 +++++++++--------------------- XVim/NSTextView+VimOperation.m | 17 +---- XVim/XVimBuffer.m | 9 ++- XVim/XVimMotion.h | 1 + XVim/XVimMotion.m | 5 ++ 6 files changed, 49 insertions(+), 105 deletions(-) diff --git a/XVim/NSTextStorage+VimOperation.h b/XVim/NSTextStorage+VimOperation.h index a16a9b80..52fe64e6 100644 --- a/XVim/NSTextStorage+VimOperation.h +++ b/XVim/NSTextStorage+VimOperation.h @@ -115,8 +115,7 @@ typedef enum { - (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; //ge,gE - (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; - (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)paragraphsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)paragraphsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; +- (NSUInteger)moveFromIndex:(NSUInteger)index paragraphs:(NSInteger)count option:(MOTION_OPTION)opt; - (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(MOTION_OPTION)opt; - (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(MOTION_OPTION)opt; diff --git a/XVim/NSTextStorage+VimOperation.m b/XVim/NSTextStorage+VimOperation.m index 0124b0cb..1fcfd082 100644 --- a/XVim/NSTextStorage+VimOperation.m +++ b/XVim/NSTextStorage+VimOperation.m @@ -671,104 +671,51 @@ Note that a blank line (only containing white space) is NOT a paragraph paragraph boundary |posix|. */ -- (NSUInteger)paragraphsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( - NSUInteger pos = index; - NSString* s = self.xvim_buffer.string; +- (NSUInteger)moveFromIndex:(NSUInteger)index paragraphs:(NSInteger)count option:(MOTION_OPTION)opt +{ XVimBuffer *buffer = self.xvim_buffer; + NSUInteger length = buffer.length; + NSUInteger nlLen; + BOOL skippingBlankLines = YES, forward; + NSRange range; - if( 0 == pos ){ - pos = 1; + if (count > 0) { + forward = YES; + } else { + forward = NO; + count = -count; } - NSUInteger prevpos = pos - 1; - - NSUInteger paragraph_head = NSNotFound; - int paragraph_found = 0; - BOOL newlines_skipped = NO; - for( ; pos < s.length && NSNotFound == paragraph_head ; pos++,prevpos++ ){ - unichar c = [s characterAtIndex:pos]; - unichar prevc = [s characterAtIndex:prevpos]; - if(isNewline(prevc) && !isNewline(c)){ - if([buffer nextNonblankInLineAtIndex:pos allowEOL:NO] == NSNotFound && opt == MOPT_PARA_BOUND_BLANKLINE){ - paragraph_found++; - if(count == paragraph_found){ - paragraph_head = pos; - break; - } + + while (forward ? index < length : index-- > 0) { + BOOL isParaSep = NO; + + range = [buffer indexRangeForLineAtIndex:index newLineLength:&nlLen]; + if (!forward) { + index = range.location; + } + if (range.length == 0) { + isParaSep = YES; + } else if (opt & MOPT_PARA_BOUND_BLANKLINE) { + if ([buffer firstNonblankInLineAtIndex:index allowEOL:NO] == NSNotFound) { + isParaSep = YES; } } - if( (isNewline(c) && isNewline(prevc)) ){ - if( newlines_skipped ){ - paragraph_found++; - if( count == paragraph_found ){ - paragraph_head = pos; - break; - }else{ - newlines_skipped = NO; - } - }else{ - // skip continuous newlines - continue; + if (skippingBlankLines) { + skippingBlankLines = isParaSep; + } else if (isParaSep) { + if (--count == 0) { + return index; } - }else{ - newlines_skipped = YES; + skippingBlankLines = YES; } - } - - if( NSNotFound == paragraph_head ){ - // end of document - paragraph_head = s.length; - while( ![self isValidCursorPosition:paragraph_head] ){ - paragraph_head--; + if (forward) { + index = NSMaxRange(range) + nlLen; } } - - return paragraph_head; -} -- (NSUInteger)paragraphsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( - NSUInteger pos = index; - NSString* s = self.xvim_buffer.string; - if( pos == 0 ){ - return NSNotFound; - } - if( pos == s.length ) - { - pos = pos - 1; - } - NSUInteger prevpos = pos - 1; - NSUInteger paragraph_head = NSNotFound; - int paragraph_found = 0; - BOOL newlines_skipped = NO; - for( ; pos > 0 && NSNotFound == paragraph_head ; pos--,prevpos-- ){ - unichar c = [s characterAtIndex:pos]; - unichar prevc = [s characterAtIndex:prevpos]; - if(isNewline(c) && isNewline(prevc)){ - if( newlines_skipped ){ - paragraph_found++; - if( count == paragraph_found ){ - paragraph_head = pos; - break; - }else{ - newlines_skipped = NO; - } - }else{ - // skip continuous newlines - continue; - } - }else{ - newlines_skipped = YES; - } - } - - if( NSNotFound == paragraph_head ){ - // begining of document - paragraph_head = 0; - } - - return paragraph_head; + return forward ? length : 0; } - - (NSUInteger)sectionsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( return 0; } diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index 6d609472..08cb5925 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -1962,7 +1962,6 @@ - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion{ NSUInteger end = NSNotFound; NSUInteger tmpPos = NSNotFound; NSUInteger start = NSNotFound; - NSUInteger starts_end = NSNotFound; NSTextStorage *ts = self.textStorage; XVimBuffer *buffer = ts.xvim_buffer; @@ -2014,10 +2013,10 @@ - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion{ end = [ts sentencesBackward:begin count:motion.count option:motion.option]; break; case MOTION_PARAGRAPH_FORWARD: - end = [ts paragraphsForward:begin count:motion.count option:motion.option]; + end = [ts moveFromIndex:begin paragraphs:motion.scount option:motion.option]; break; case MOTION_PARAGRAPH_BACKWARD: - end = [ts paragraphsBackward:begin count:motion.count option:motion.option]; + end = [ts moveFromIndex:begin paragraphs:-motion.scount option:motion.option]; break; case MOTION_NEXT_CHARACTER: end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; @@ -2092,16 +2091,8 @@ - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion{ break; case TEXTOBJECT_PARAGRAPH: // Not supported - start = self.insertionPoint; - if(start != 0){ - start = [ts paragraphsBackward:self.insertionPoint count:1 option:MOPT_PARA_BOUND_BLANKLINE]; - } - starts_end = [ts paragraphsForward:start count:1 option:MOPT_PARA_BOUND_BLANKLINE]; - end = [ts paragraphsForward:self.insertionPoint count:motion.count option:MOPT_PARA_BOUND_BLANKLINE]; - - if(starts_end != end){ - start = starts_end; - } + start = [ts moveFromIndex:self.insertionPoint paragraphs:-1 option:MOPT_PARA_BOUND_BLANKLINE]; + end = [ts moveFromIndex:self.insertionPoint paragraphs:motion.scount option:MOPT_PARA_BOUND_BLANKLINE]; range = NSMakeRange(start, end - start); break; case TEXTOBJECT_PARENTHESES: diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index 2bfe52db..a578fb60 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -133,14 +133,15 @@ - (NSRange)indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUInteger *)n NSString *string = self.string; NSUInteger length = self.length; - NSUInteger lineNum = 0, end = 0, contentsEnd; + NSUInteger lineNum = 0, end = 0, pos, contentsEnd; do { + pos = end; [string getLineStart:NULL end:&end contentsEnd:&contentsEnd forRange:NSMakeRange(end, 0)]; lineNum++; if (lineNum == num) { if (newLineLength) *newLineLength = end - contentsEnd; - return NSMakeRange(end, contentsEnd - end); + return NSMakeRange(pos, contentsEnd - pos); } } while (end < length); @@ -168,7 +169,7 @@ - (NSRange)indexRangeForLines:(NSRange)range // TODO: we may need to keep track line number and position by hooking insertText: method. // FIXME: this code is actually never called in XVim for XCode, it probably has bugs, it's not tested NSString *string = self.string; - NSUInteger length = self.length, start; + NSUInteger length = self.length, start = 0; NSUInteger lineNum = 0, end = 0, contentsEnd; do { @@ -208,7 +209,7 @@ - (NSRange)indexRangeForLineAtIndex:(NSUInteger)index newLineLength:(NSUInteger } [string getLineStart:&index end:&end contentsEnd:&contentEnd forRange:NSMakeRange(index, 0)]; - if (newLineLength) *newLineLength = contentEnd - end; + if (newLineLength) *newLineLength = end - contentEnd; return NSMakeRange(index, contentEnd - index); } diff --git a/XVim/XVimMotion.h b/XVim/XVimMotion.h index 0348ea7f..4e88240a 100644 --- a/XVim/XVimMotion.h +++ b/XVim/XVimMotion.h @@ -73,6 +73,7 @@ typedef enum _MOTION{ @property MOTION_TYPE type; @property MOTION_OPTION option; @property NSUInteger count; +@property NSInteger scount; @property NSUInteger line; @property NSUInteger column; @property NSUInteger position; diff --git a/XVim/XVimMotion.m b/XVim/XVimMotion.m index f24d141e..489003af 100644 --- a/XVim/XVimMotion.m +++ b/XVim/XVimMotion.m @@ -10,6 +10,11 @@ @implementation XVimMotion +- (NSInteger)scount +{ + return (NSInteger)self.count; +} + - (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(MOTION_OPTION)option count:(NSUInteger)count{ if( self = [super init]){ _motion = motion; From eebc66a99504b3ebbf5addb36f085ff4492b9379 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Wed, 20 Nov 2013 19:55:30 +0100 Subject: [PATCH 10/44] Dead code --- XVim/XVimEvaluator.m | 1 - XVim/XVimMotionEvaluator.m | 16 ++++++---------- XVim/XVimNormalEvaluator.m | 1 - 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/XVim/XVimEvaluator.m b/XVim/XVimEvaluator.m index 826f5b8c..f5de825e 100644 --- a/XVim/XVimEvaluator.m +++ b/XVim/XVimEvaluator.m @@ -20,7 +20,6 @@ #import "NSTextView+VimOperation.h" #import "XVimSearch.h" #import "XVimCommandLineEvaluator.h" -#import "NSString+VimHelper.h" static XVimEvaluator *_invalidEvaluator = nil; static XVimEvaluator *_noOperationEvaluator = nil; diff --git a/XVim/XVimMotionEvaluator.m b/XVim/XVimMotionEvaluator.m index 2462a021..9b1a2289 100644 --- a/XVim/XVimMotionEvaluator.m +++ b/XVim/XVimMotionEvaluator.m @@ -19,7 +19,6 @@ #import "Logger.h" #import "XVimYankEvaluator.h" #import "NSTextStorage+VimOperation.h" -#import "NSString+VimHelper.h" #import "XVimMark.h" #import "XVimMarks.h" #import "XVimCommandLineEvaluator.h" @@ -35,11 +34,8 @@ // How the motion is treated depends on a subclass of the XVimMotionEvaluator. // For example, XVimDeleteEvaluator will delete the letters represented by motion. - - @interface XVimMotionEvaluator() { MOTION_TYPE _forcedMotionType; - BOOL _toggleInclusiveExclusive; } @end @@ -89,7 +85,9 @@ - (XVimEvaluator*)_motionFixedFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTI */ -(XVimEvaluator*)_motionFixed:(XVimMotion*)motion{ - if( _forcedMotionType == CHARACTERWISE_EXCLUSIVE){ // CHARACTERWISE_EXCLUSIVE means 'v' is pressed and it means toggle inclusive/exclusive. So its not always "exclusive" + if( _forcedMotionType == CHARACTERWISE_EXCLUSIVE){ + // CHARACTERWISE_EXCLUSIVE means 'v' is pressed and it means toggle inclusive/exclusive. + // So its not always "exclusive" if( motion.type == LINEWISE ){ motion.type = CHARACTERWISE_EXCLUSIVE; }else{ @@ -285,20 +283,18 @@ - (XVimEvaluator*)T{ } - (XVimEvaluator*)v{ - _forcedMotionType = CHARACTERWISE_EXCLUSIVE; // This does not mean the motion will always be "exclusive". This is just for remembering that its type is "characterwise" forced. - // Actual motion is decided by motions' default inclusive/exclusive attribute and _toggleInclusiveExclusive flag. - _toggleInclusiveExclusive = !_toggleInclusiveExclusive; + // This does not mean the motion will always be "exclusive". + // This is just for remembering that its type is "characterwise" forced. + _forcedMotionType = CHARACTERWISE_EXCLUSIVE; return self; } - (XVimEvaluator*)V{ - _toggleInclusiveExclusive = NO; _forcedMotionType = LINEWISE; return self; } - (XVimEvaluator*)C_v{ - _toggleInclusiveExclusive = NO; _forcedMotionType = BLOCKWISE; return self; } diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index 0d61474c..13d6f9ac 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -23,7 +23,6 @@ #import "XVimKeyStroke.h" #import "XVimWindow.h" #import "XVim.h" -#import "NSString+VimHelper.h" #import "XVimKeymapProvider.h" #import "Logger.h" #import "XVimCommandLineEvaluator.h" From 98bb8689587e3e764d689b12fb8216652d6cfb16 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Thu, 21 Nov 2013 17:45:40 +0100 Subject: [PATCH 11/44] fix Off-by-one --- XVim/NSTextStorage+VimOperation.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XVim/NSTextStorage+VimOperation.m b/XVim/NSTextStorage+VimOperation.m index 1fcfd082..e88a2cc9 100644 --- a/XVim/NSTextStorage+VimOperation.m +++ b/XVim/NSTextStorage+VimOperation.m @@ -251,7 +251,7 @@ - (NSUInteger)prevLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInt NSUInteger lno = [buffer lineNumberAtIndex:index]; - lno = lno < count ? 1 : lno - count; + lno = lno <= count ? 1 : lno - count; return [buffer indexOfLineNumber:lno column:column]; } From a776bfdd04aa82a08cd957342b1d03f09a84d3a1 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Wed, 20 Nov 2013 23:06:42 +0100 Subject: [PATCH 12/44] Try not to change -[XVimMotion motion] gratuitously --- XVim/XVimEvaluator.m | 2 +- XVim/XVimMotionEvaluator.h | 1 - XVim/XVimMotionEvaluator.m | 33 +++++++++++++++------------------ 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/XVim/XVimEvaluator.m b/XVim/XVimEvaluator.m index f5de825e..f57127a3 100644 --- a/XVim/XVimEvaluator.m +++ b/XVim/XVimEvaluator.m @@ -223,7 +223,7 @@ - (XVimCommandLineEvaluator*)searchEvaluatorForward:(BOOL)forward{ BOOL forward = [command characterAtIndex:0] == '/'; if( command.length == 1 ){ // Repeat search - XVimMotion* m = [XVim.instance.searcher motionForRepeatSearch]; + XVimMotion *m = [XVim.instance.searcher motionForRepeatSearch]; m.motion = forward ? MOTION_SEARCH_FORWARD : MOTION_SEARCH_BACKWARD; m.count = self.numericArg; *result = m; diff --git a/XVim/XVimMotionEvaluator.h b/XVim/XVimMotionEvaluator.h index 2adb9e50..d610b2d8 100644 --- a/XVim/XVimMotionEvaluator.h +++ b/XVim/XVimMotionEvaluator.h @@ -16,7 +16,6 @@ // Make subclass of this to implement operation on which takes motions as argument (deletion,yank...and so on.) @interface XVimMotionEvaluator : XVimNumericEvaluator -@property (strong) XVimMotion* motion; - (XVimEvaluator*)commonMotion:(SEL)motion Type:(MOTION_TYPE)type; diff --git a/XVim/XVimMotionEvaluator.m b/XVim/XVimMotionEvaluator.m index 9b1a2289..27d11f79 100644 --- a/XVim/XVimMotionEvaluator.m +++ b/XVim/XVimMotionEvaluator.m @@ -36,17 +36,16 @@ @interface XVimMotionEvaluator() { MOTION_TYPE _forcedMotionType; + XVimMotion *_motion; } @end @implementation XVimMotionEvaluator -@synthesize motion = _motion; - (id)initWithWindow:(XVimWindow *)window{ self = [super initWithWindow:window]; if (self) { _forcedMotionType = DEFAULT_MOTION_TYPE; - _motion = [XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 1) retain]; } return self; } @@ -155,25 +154,23 @@ - (XVimEvaluator*)onComplete_fFtT:(XVimArgumentEvaluator*)childEvaluator{ } */ - self.motion.count = self.numericArg; - self.motion.character = childEvaluator.keyStroke.character; - [XVim instance].lastCharacterSearchMotion = self.motion; - return [self _motionFixed:self.motion]; + _motion.count = self.numericArg; + _motion.character = childEvaluator.keyStroke.character; + [XVim instance].lastCharacterSearchMotion = _motion; + return [self _motionFixed:_motion]; } - (XVimEvaluator*)f{ [self.argumentString appendString:@"f"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - self.motion.motion = MOTION_NEXT_CHARACTER; - self.motion.type = CHARACTERWISE_INCLUSIVE; + _motion = [XVIM_MAKE_MOTION(MOTION_NEXT_CHARACTER, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } - (XVimEvaluator*)F{ [self.argumentString appendString:@"F"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - self.motion.motion = MOTION_PREV_CHARACTER; - self.motion.type = CHARACTERWISE_EXCLUSIVE; + _motion = [XVIM_MAKE_MOTION(MOTION_PREV_CHARACTER, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } @@ -202,11 +199,12 @@ - (XVimEvaluator*)onComplete_g:(XVimGMotionEvaluator*)childEvaluator{ - (XVimEvaluator*)G{ - XVimMotion* m =XVIM_MAKE_MOTION(MOTION_LINENUMBER, LINEWISE, LEFT_RIGHT_NOWRAP, [self numericArg]); - if([self numericMode]){ + XVimMotion *m; + if ([self numericMode]){ + m = XVIM_MAKE_MOTION(MOTION_LINENUMBER, LINEWISE, LEFT_RIGHT_NOWRAP, [self numericArg]); m.line = [self numericArg]; }else{ - m.motion = MOTION_LASTLINE; + m = XVIM_MAKE_MOTION(MOTION_LASTLINE, LINEWISE, LEFT_RIGHT_NOWRAP, [self numericArg]); } return [self _motionFixed:m]; } @@ -245,7 +243,8 @@ - (XVimEvaluator*)nN_impl:(BOOL)opposite{ if( opposite ){ m.motion = (m.motion == MOTION_SEARCH_FORWARD) ? MOTION_SEARCH_BACKWARD : MOTION_SEARCH_FORWARD; } - self.motion = m; + [_motion release]; + _motion = [m retain]; return [self _motionFixed:m]; } @@ -269,16 +268,14 @@ - (XVimEvaluator*)C_u{ - (XVimEvaluator*)t{ [self.argumentString appendString:@"t"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - self.motion.motion = MOTION_TILL_NEXT_CHARACTER; - self.motion.type = CHARACTERWISE_INCLUSIVE; + _motion = [XVIM_MAKE_MOTION(MOTION_TILL_NEXT_CHARACTER, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } - (XVimEvaluator*)T{ [self.argumentString appendString:@"T"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - self.motion.motion = MOTION_TILL_PREV_CHARACTER; - self.motion.type = CHARACTERWISE_EXCLUSIVE; + _motion = [XVIM_MAKE_MOTION(MOTION_TILL_PREV_CHARACTER, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } From 4917b21e4a9549486fa2dd88eea7f024eb946ad0 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Thu, 21 Nov 2013 10:06:33 +0100 Subject: [PATCH 13/44] +load really isn't the place to initialize a bundle http://www.blackdogfoundry.com/blog/creating-an-xcode4-plugin/ says -pluginDidLoad: is. --- XVim/XVim.m | 60 ++++++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/XVim/XVim.m b/XVim/XVim.m index 6a600a21..f857d42d 100644 --- a/XVim/XVim.m +++ b/XVim/XVim.m @@ -117,7 +117,8 @@ + (void) addXVimMenu{ } -+ (void) load{ ++ (void) pluginDidLoad:(NSBundle *)plugin +{ NSBundle* app = [NSBundle mainBundle]; NSString* identifier = [app bundleIdentifier]; @@ -128,13 +129,9 @@ + (void) load{ // Entry Point of the Plugin. [Logger defaultLogger].level = LogTrace; - - // This looks strange but this is what intended to. - // [XVim instance] part initialize all the internal objects which does not depends on each other - // (If some initialization of a object which is held by XVim class(such as XVimSearch) access - // [XVim instance] inside it, it causes dead lock because of dispatch_once in [XVim instance] method. - // So after initializing all the independent object we do initialize dependent objects in init2 - [[XVim instance] init2]; + + // be sure XVim is initialized + (void)[XVim instance]; //Caution: parseRcFile can potentially invoke +instance on XVim (e.g. if "set ..." is //used in .ximvrc) so we must be sure to call it _AFTER_ +instance has completed @@ -180,32 +177,29 @@ + (XVim*)instance{ - (id)init { if (self = [super init]) { self.options = [[[XVimOptions alloc] init] autorelease]; - } - return self; -} - -- (void)init2{ - _searchHistory = [[XVimHistoryHandler alloc] init]; - _searcher = [[XVimSearch alloc] init]; - _lastCharacterSearchMotion = nil; - _marks = [[XVimMarks alloc] init]; - _testRunner= [[XVimTester alloc] init]; - - self.excmd = [[[XVimExCommand alloc] init] autorelease]; - self.lastPlaybackRegister = nil; - self.registerManager = [[[XVimRegisterManager alloc] init] autorelease]; - self.lastOperationCommands = [[[XVimMutableString alloc] init] autorelease]; - self.tempRepeatRegister = [[[XVimMutableString alloc] init] autorelease]; - self.isRepeating = NO; - self.isExecuting = NO; - _logFile = nil; - _exCommandHistory = [[XVimHistoryHandler alloc] init]; - - for (int i = 0; i < XVIM_MODE_COUNT; ++i) { - _keymaps[i] = [[XVimKeymap alloc] init]; + _searchHistory = [[XVimHistoryHandler alloc] init]; + _searcher = [[XVimSearch alloc] init]; + _lastCharacterSearchMotion = nil; + _marks = [[XVimMarks alloc] init]; + _testRunner= [[XVimTester alloc] init]; + + self.excmd = [[[XVimExCommand alloc] init] autorelease]; + self.lastPlaybackRegister = nil; + self.registerManager = [[[XVimRegisterManager alloc] init] autorelease]; + self.lastOperationCommands = [[[XVimMutableString alloc] init] autorelease]; + self.tempRepeatRegister = [[[XVimMutableString alloc] init] autorelease]; + self.isRepeating = NO; + self.isExecuting = NO; + _logFile = nil; + _exCommandHistory = [[XVimHistoryHandler alloc] init]; + + for (int i = 0; i < XVIM_MODE_COUNT; ++i) { + _keymaps[i] = [[XVimKeymap alloc] init]; + } + + [_options addObserver:self forKeyPath:@"debug" options:NSKeyValueObservingOptionNew context:nil]; } - - [_options addObserver:self forKeyPath:@"debug" options:NSKeyValueObservingOptionNew context:nil]; + return self; } -(void)dealloc{ From 51f6b2dcc839b2a3ab1857ce308169ddbc8ad9b6 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Thu, 21 Nov 2013 12:58:49 +0100 Subject: [PATCH 14/44] Add an NSObject(XVimAdditions) category for swizzling Hooker is really a disgusting hack which uses Hook classes for no good purposes and does no compile time check. Let's use this to modify the behavior of (typically) NSTextView and NSTextStorage if they have xvim_* objects associated with them. --- XVim.xcodeproj/project.pbxproj | 16 ++++++++ XVim/Hooker.m | 2 +- XVim/NSObject+XVimAdditions.h | 30 +++++++++++++++ XVim/NSObject+XVimAdditions.m | 69 ++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 XVim/NSObject+XVimAdditions.h create mode 100644 XVim/NSObject+XVimAdditions.m diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 1f805b47..83f43f0b 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 6E2B33341836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2B33331836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m */; }; 6E6865D618390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; 6E6865D718390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; + 6EF220D6183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */; }; + 6EF220D7183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */; }; A2126B7516FB30B0000BE21C /* XVimMark.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7416FB30B0000BE21C /* XVimMark.m */; }; A2126B7816FB4806000BE21C /* XVimMarks.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7716FB4806000BE21C /* XVimMarks.m */; }; A216F3A1156560FE00AD2529 /* IDEEditorArea+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A216F3A0156560FE00AD2529 /* IDEEditorArea+XVim.m */; }; @@ -214,6 +216,8 @@ 6E6865D418390702008D5FBB /* XVimUndo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimUndo.h; path = XVim/XVimUndo.h; sourceTree = SOURCE_ROOT; }; 6E6865D518390702008D5FBB /* XVimUndo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimUndo.m; path = XVim/XVimUndo.m; sourceTree = SOURCE_ROOT; }; 6EAFF976183777A6003EADAE /* XVimStringBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimStringBuffer.h; path = XVim/XVimStringBuffer.h; sourceTree = SOURCE_ROOT; }; + 6EF220D4183E2A1400B814B8 /* NSObject+XVimAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+XVimAdditions.h"; path = "XVim/NSObject+XVimAdditions.h"; sourceTree = SOURCE_ROOT; }; + 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+XVimAdditions.m"; path = "XVim/NSObject+XVimAdditions.m"; sourceTree = SOURCE_ROOT; }; A2126B7316FB30B0000BE21C /* XVimMark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimMark.h; path = XVim/XVimMark.h; sourceTree = SOURCE_ROOT; }; A2126B7416FB30B0000BE21C /* XVimMark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimMark.m; path = XVim/XVimMark.m; sourceTree = SOURCE_ROOT; }; A2126B7616FB4806000BE21C /* XVimMarks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimMarks.h; path = XVim/XVimMarks.h; sourceTree = SOURCE_ROOT; }; @@ -433,6 +437,15 @@ name = Buffer; sourceTree = ""; }; + 6EF220D3183E29EC00B814B8 /* Foundation */ = { + isa = PBXGroup; + children = ( + 6EF220D4183E2A1400B814B8 /* NSObject+XVimAdditions.h */, + 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */, + ); + name = Foundation; + sourceTree = ""; + }; A222B5E31514DFD5005E8802 /* Operators */ = { isa = PBXGroup; children = ( @@ -554,6 +567,7 @@ A2B4BAC114D59F6600D817B0 /* XVim */ = { isa = PBXGroup; children = ( + 6EF220D3183E29EC00B814B8 /* Foundation */, A2165BFC17A3762C00AB18FD /* XVimDefs.h */, A24782B714D6F56E003B6433 /* XVim.h */, A24782B814D6F56E003B6433 /* XVim.m */, @@ -895,6 +909,7 @@ A28F423E17EEDBC200A3F7AE /* XVimNumericEvaluator.m in Sources */, A28F423F17EEDBC200A3F7AE /* XVimKeymap.m in Sources */, A28F424017EEDBC200A3F7AE /* XVimExCommand.m in Sources */, + 6EF220D7183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */, A28F424117EEDBC200A3F7AE /* XVimSearch.m in Sources */, A28F424217EEDBC200A3F7AE /* XVimOptions.m in Sources */, A28F424317EEDBC200A3F7AE /* XVimTildeEvaluator.m in Sources */, @@ -1012,6 +1027,7 @@ A2126B7816FB4806000BE21C /* XVimMarks.m in Sources */, A2702B0216FE537D005ADD76 /* IDEWorkspaceWindowHook.m in Sources */, A2702B0B16FEA313005ADD76 /* NSObject+ExtraData.m in Sources */, + 6EF220D6183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */, A22A00A01708914E0003046C /* XVimUtil.m in Sources */, A22A00A31709CBE50003046C /* XVimTestCase.m in Sources */, A2575400172F5F0E003D8A97 /* XVimTester+TextObject.m in Sources */, diff --git a/XVim/Hooker.m b/XVim/Hooker.m index 69f32b84..35e0a7fc 100644 --- a/XVim/Hooker.m +++ b/XVim/Hooker.m @@ -1,4 +1,4 @@ -// + // Hooker.m // XVim // diff --git a/XVim/NSObject+XVimAdditions.h b/XVim/NSObject+XVimAdditions.h new file mode 100644 index 00000000..d8d49740 --- /dev/null +++ b/XVim/NSObject+XVimAdditions.h @@ -0,0 +1,30 @@ +// +// NSObject+XVimAdditions.h +// XVim +// +// Created by John AppleSeed on 21/11/13. +// +// + +#import + +@interface NSObject (XVimAdditions) + +/** @brief swizzles class selector \a origSel with \a newSel. + * + * @param origSel the name of the class method selector to swizzle + * @param newSel the name of the class method selector to use as a replacement + */ ++ (void)xvim_swizzleClassMethod:(SEL)origSel with:(SEL)newSel; ++ (void)xvim_swizzleClassMethod:(SEL)origSel with:(SEL)newSel imp:(IMP)imp; + +/** @brief swizzles instance selector \a origSel with \a newSel. + * + * @param origSel the name of the instance method selector to swizzle + * @param newSel the name of the instance method selector to use as a replacement + * + */ ++ (void)xvim_swizzleInstanceMethod:(SEL)origSel with:(SEL)newSel; ++ (void)xvim_swizzleInstanceMethod:(SEL)origSel with:(SEL)newSel imp:(IMP)imp; + +@end diff --git a/XVim/NSObject+XVimAdditions.m b/XVim/NSObject+XVimAdditions.m new file mode 100644 index 00000000..29265139 --- /dev/null +++ b/XVim/NSObject+XVimAdditions.m @@ -0,0 +1,69 @@ +// +// NSObject+XVimAdditions.m +// XVim +// +// Created by John AppleSeed on 21/11/13. +// +// + +#import +#import "NSObject+XVimAdditions.h" + +@implementation NSObject (XVimAdditions) + ++ (void)xvim_swizzleClassMethod:(SEL)origSel with:(SEL)newSel +{ + Method origMethod = class_getClassMethod(self, origSel); + Method newMethod = class_getClassMethod(self, newSel); + Class class = object_getClass(self); + + NSAssert(origMethod, @"+[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(origSel)); + NSAssert(newMethod, @"+[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(newSel)); + + if (class_addMethod(class, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { + class_replaceMethod(class, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)); + } else { + method_exchangeImplementations(newMethod, origMethod); + } +} + ++ (void)xvim_swizzleClassMethod:(SEL)origSel with:(SEL)newSel imp:(IMP)imp +{ + Method origMethod = class_getClassMethod(self, origSel); + Method newMethod = class_getClassMethod(self, newSel); + + NSAssert(origMethod, @"+[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(origSel)); + NSAssert(!newMethod, @"+[%@ %@] exists", NSStringFromClass(self), NSStringFromSelector(newSel)); + + class_addMethod(self, newSel, imp, method_getTypeEncoding(origMethod)); + [self xvim_swizzleClassMethod:origSel with:newSel]; +} + ++ (void)xvim_swizzleInstanceMethod:(SEL)origSel with:(SEL)newSel +{ + Method origMethod = class_getInstanceMethod(self, origSel); + Method newMethod = class_getInstanceMethod(self, newSel); + + NSAssert(origMethod, @"-[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(origSel)); + NSAssert(newMethod, @"-[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(newSel)); + + if (class_addMethod(self, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { + class_replaceMethod(self, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)); + } else { + method_exchangeImplementations(newMethod, origMethod); + } +} + ++ (void)xvim_swizzleInstanceMethod:(SEL)origSel with:(SEL)newSel imp:(IMP)imp +{ + Method origMethod = class_getInstanceMethod(self, origSel); + Method newMethod = class_getInstanceMethod(self, newSel); + + NSAssert(origMethod, @"-[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(origSel)); + NSAssert(!newMethod, @"-[%@ %@] exists", NSStringFromClass(self), NSStringFromSelector(newSel)); + + class_addMethod(self, newSel, imp, method_getTypeEncoding(origMethod)); + [self xvim_swizzleInstanceMethod:origSel with:newSel]; +} + +@end From 09bc13218b4ff6a32f55a001946e47a911d2e24e Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Thu, 21 Nov 2013 17:05:52 +0100 Subject: [PATCH 15/44] Have an XVimView much like we have an XVimBuffer +[XVimView initialize] swizzles NSTextView like we used to do on the DVT one. Nothing was really DVT speciic in the DVTSourceTextViewHook in the first place. The swizzling does nothing if there is no XVimView associated with the NSTextView, or if XVim is disabled. All that is non specific to XCode. Rewrite IDEEditorHook as an IDEEditor+XVim category with swizzling. In the -didSetupEditor override, detect when there are DVTSourceTextView's that are the one we know what to do with. Check their document has a file:// URL (so that the script editor in the Xcode rules are ignored). If the textStorage happens to already be created, allocate the XVimBuffer right away. In theory we would have to check when the document for a given TextView changes whether we still support XVim, but XCode actually destroys and recreates the view each time you switch documents, so it doesn't matter. --- XVim.xcodeproj/project.pbxproj | 44 +-- XVim/DVTSourceTextViewHook.h | 43 --- XVim/DVTSourceTextViewHook.m | 318 -------------------- XVim/{IDEEditorHook.h => IDEEditor+XVim.h} | 12 +- XVim/IDEEditor+XVim.m | 153 ++++++++++ XVim/IDEEditorHook.m | 77 ----- XVim/NSTextView+VimOperation.h | 1 - XVim/NSTextView+VimOperation.m | 32 -- XVim/Test/XVimTestCase.m | 9 +- XVim/XVim.h | 3 + XVim/XVim.m | 24 +- XVim/XVimBuffer.h | 18 +- XVim/XVimBuffer.m | 45 +-- XVim/XVimCommandLineEvaluator.m | 4 +- XVim/XVimDebug.m | 24 +- XVim/XVimEvaluator.m | 5 +- XVim/XVimExCommand.m | 14 +- XVim/XVimGActionEvaluator.m | 4 +- XVim/XVimHookManager.m | 16 +- XVim/XVimInsertEvaluator.m | 4 +- XVim/XVimJoinEvaluator.m | 2 +- XVim/XVimMark.h | 2 +- XVim/XVimMarkSetEvaluator.m | 2 +- XVim/XVimMarks.h | 2 +- XVim/XVimMarks.m | 4 +- XVim/XVimMotionEvaluator.m | 12 +- XVim/XVimNormalEvaluator.m | 11 +- XVim/XVimOperatorEvaluator.h | 1 - XVim/XVimOperatorEvaluator.m | 29 +- XVim/XVimSearch.m | 11 +- XVim/XVimView.h | 25 ++ XVim/XVimView.m | 324 +++++++++++++++++++++ XVim/XVimVisualEvaluator.m | 7 +- XVim/XVimWindow+Xcode.h | 13 - XVim/XVimWindow+Xcode.m | 13 - XVim/XVimWindow.h | 8 +- XVim/XVimWindow.m | 27 +- 37 files changed, 684 insertions(+), 659 deletions(-) delete mode 100644 XVim/DVTSourceTextViewHook.h delete mode 100644 XVim/DVTSourceTextViewHook.m rename XVim/{IDEEditorHook.h => IDEEditor+XVim.h} (54%) create mode 100644 XVim/IDEEditor+XVim.m delete mode 100644 XVim/IDEEditorHook.m create mode 100644 XVim/XVimView.h create mode 100644 XVim/XVimView.m delete mode 100644 XVim/XVimWindow+Xcode.h delete mode 100644 XVim/XVimWindow+Xcode.m diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 83f43f0b..0147dd8d 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 6E2B332C1836E42F00EFE4E2 /* XVimBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2B332B1836E42F00EFE4E2 /* XVimBuffer.m */; }; 6E2B332D1836E42F00EFE4E2 /* XVimBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2B332B1836E42F00EFE4E2 /* XVimBuffer.m */; }; 6E2B33341836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2B33331836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m */; }; + 6E2C392F183E4BA20056E30F /* XVimView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2C392E183E4BA20056E30F /* XVimView.m */; }; + 6E2C3930183E4BA20056E30F /* XVimView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2C392E183E4BA20056E30F /* XVimView.m */; }; 6E6865D618390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; 6E6865D718390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; 6EF220D6183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */; }; @@ -42,7 +44,7 @@ A257540C1732A58F003D8A97 /* XVimTester+Visual.m in Sources */ = {isa = PBXBuildFile; fileRef = A257540B1732A58E003D8A97 /* XVimTester+Visual.m */; }; A257C28F156567250098CA09 /* DVTSourceTextView+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A257C28E156567250098CA09 /* DVTSourceTextView+XVim.m */; }; A2640CC917ACE651003D197D /* NSTextView+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */; }; - A26ACC4E154F2D6800B27D69 /* IDEEditorHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditorHook.m */; }; + A26ACC4E154F2D6800B27D69 /* IDEEditor+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */; }; A26DC88615DF33C600779CB4 /* XVimTester.m in Sources */ = {isa = PBXBuildFile; fileRef = A26DC88515DF33C600779CB4 /* XVimTester.m */; }; A26E5F6417A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */; }; A2702B0216FE537D005ADD76 /* IDEWorkspaceWindowHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0116FE537D005ADD76 /* IDEWorkspaceWindowHook.m */; }; @@ -71,7 +73,6 @@ A28F423917EEDBC200A3F7AE /* XVimRegisterEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = F100DC2D150BB6BC002C703C /* XVimRegisterEvaluator.m */; }; A28F423A17EEDBC200A3F7AE /* XVimOperatorEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A222B5E01514DFCD005E8802 /* XVimOperatorEvaluator.m */; }; A28F423B17EEDBC200A3F7AE /* XVimKeyStroke.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */; }; - A28F423C17EEDBC200A3F7AE /* DVTSourceTextViewHook.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4015272B0500E1448D /* DVTSourceTextViewHook.m */; }; A28F423D17EEDBC200A3F7AE /* IDESourceCodeEditorHook.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */; }; A28F423E17EEDBC200A3F7AE /* XVimNumericEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4C1527CEA400E1448D /* XVimNumericEvaluator.m */; }; A28F423F17EEDBC200A3F7AE /* XVimKeymap.m in Sources */ = {isa = PBXBuildFile; fileRef = C366C23A15287EE30008C58E /* XVimKeymap.m */; }; @@ -94,7 +95,7 @@ A28F425017EEDBC200A3F7AE /* IDEEditorAreaHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ADAB821545C18F0093A908 /* IDEEditorAreaHook.m */; }; A28F425117EEDBC200A3F7AE /* XVimStatusLine.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ABFE1215497A3C002220E8 /* XVimStatusLine.m */; }; A28F425217EEDBC200A3F7AE /* XVimHookManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C345DDBA154CE12A009F232E /* XVimHookManager.m */; }; - A28F425317EEDBC200A3F7AE /* IDEEditorHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditorHook.m */; }; + A28F425317EEDBC200A3F7AE /* IDEEditor+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */; }; A28F425417EEDBC200A3F7AE /* NSInsetTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = C30886591554C8380024161D /* NSInsetTextView.m */; }; A28F425517EEDBC200A3F7AE /* IDEEditorArea+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A216F3A0156560FE00AD2529 /* IDEEditorArea+XVim.m */; }; A28F425617EEDBC200A3F7AE /* DVTSourceTextView+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A257C28E156567250098CA09 /* DVTSourceTextView+XVim.m */; }; @@ -166,7 +167,6 @@ C366C23B15287EE30008C58E /* XVimKeymap.m in Sources */ = {isa = PBXBuildFile; fileRef = C366C23A15287EE30008C58E /* XVimKeymap.m */; }; C36C104E153131C600CE1D62 /* XVimTextObjectEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */; }; C38A5B301526C6B100E1448D /* XVimKeyStroke.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */; }; - C38A5B4115272B0500E1448D /* DVTSourceTextViewHook.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4015272B0500E1448D /* DVTSourceTextViewHook.m */; }; C38A5B4715272DCD00E1448D /* IDESourceCodeEditorHook.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */; }; C38A5B4D1527CEA500E1448D /* XVimNumericEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4C1527CEA400E1448D /* XVimNumericEvaluator.m */; }; C3AA7226152F186A00C61D97 /* XVimTildeEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7225152F186A00C61D97 /* XVimTildeEvaluator.m */; }; @@ -213,6 +213,8 @@ 6E2B332E1836E47500EFE4E2 /* XVimTextStoring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTextStoring.h; path = XVim/XVimTextStoring.h; sourceTree = SOURCE_ROOT; }; 6E2B33321836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "DVTTextStorage+XVimTextStoring.h"; path = "XVim/DVTTextStorage+XVimTextStoring.h"; sourceTree = SOURCE_ROOT; }; 6E2B33331836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DVTTextStorage+XVimTextStoring.m"; path = "XVim/DVTTextStorage+XVimTextStoring.m"; sourceTree = SOURCE_ROOT; }; + 6E2C392D183E4BA20056E30F /* XVimView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimView.h; path = XVim/XVimView.h; sourceTree = SOURCE_ROOT; }; + 6E2C392E183E4BA20056E30F /* XVimView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimView.m; path = XVim/XVimView.m; sourceTree = SOURCE_ROOT; }; 6E6865D418390702008D5FBB /* XVimUndo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimUndo.h; path = XVim/XVimUndo.h; sourceTree = SOURCE_ROOT; }; 6E6865D518390702008D5FBB /* XVimUndo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimUndo.m; path = XVim/XVimUndo.m; sourceTree = SOURCE_ROOT; }; 6EAFF976183777A6003EADAE /* XVimStringBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimStringBuffer.h; path = XVim/XVimStringBuffer.h; sourceTree = SOURCE_ROOT; }; @@ -271,8 +273,8 @@ A257C28E156567250098CA09 /* DVTSourceTextView+XVim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DVTSourceTextView+XVim.m"; path = "XVim/DVTSourceTextView+XVim.m"; sourceTree = SOURCE_ROOT; }; A2640CC717ACE651003D197D /* NSTextView+VimOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSTextView+VimOperation.h"; path = "XVim/NSTextView+VimOperation.h"; sourceTree = SOURCE_ROOT; }; A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSTextView+VimOperation.m"; path = "XVim/NSTextView+VimOperation.m"; sourceTree = SOURCE_ROOT; }; - A26ACC4C154F2D6600B27D69 /* IDEEditorHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDEEditorHook.h; path = XVim/IDEEditorHook.h; sourceTree = SOURCE_ROOT; }; - A26ACC4D154F2D6700B27D69 /* IDEEditorHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IDEEditorHook.m; path = XVim/IDEEditorHook.m; sourceTree = SOURCE_ROOT; }; + A26ACC4C154F2D6600B27D69 /* IDEEditor+XVim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "IDEEditor+XVim.h"; path = "XVim/IDEEditor+XVim.h"; sourceTree = SOURCE_ROOT; }; + A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "IDEEditor+XVim.m"; path = "XVim/IDEEditor+XVim.m"; sourceTree = SOURCE_ROOT; }; A26DC88415DF33C600779CB4 /* XVimTester.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTester.h; path = XVim/XVimTester.h; sourceTree = SOURCE_ROOT; }; A26DC88515DF33C600779CB4 /* XVimTester.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimTester.m; path = XVim/XVimTester.m; sourceTree = SOURCE_ROOT; }; A26E5F6217A6BCAF0087C9B6 /* NSTextStorage+VimOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSTextStorage+VimOperation.h"; path = "XVim/NSTextStorage+VimOperation.h"; sourceTree = SOURCE_ROOT; }; @@ -361,8 +363,6 @@ C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimTextObjectEvaluator.m; path = XVim/XVimTextObjectEvaluator.m; sourceTree = SOURCE_ROOT; }; C38A5B2E1526C6B100E1448D /* XVimKeyStroke.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimKeyStroke.h; path = XVim/XVimKeyStroke.h; sourceTree = SOURCE_ROOT; }; C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimKeyStroke.m; path = XVim/XVimKeyStroke.m; sourceTree = SOURCE_ROOT; }; - C38A5B3F15272B0500E1448D /* DVTSourceTextViewHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DVTSourceTextViewHook.h; path = XVim/DVTSourceTextViewHook.h; sourceTree = SOURCE_ROOT; }; - C38A5B4015272B0500E1448D /* DVTSourceTextViewHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DVTSourceTextViewHook.m; path = XVim/DVTSourceTextViewHook.m; sourceTree = SOURCE_ROOT; }; C38A5B4515272DCC00E1448D /* IDESourceCodeEditorHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDESourceCodeEditorHook.h; path = XVim/IDESourceCodeEditorHook.h; sourceTree = SOURCE_ROOT; }; C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IDESourceCodeEditorHook.m; path = XVim/IDESourceCodeEditorHook.m; sourceTree = SOURCE_ROOT; }; C38A5B4B1527CEA400E1448D /* XVimNumericEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimNumericEvaluator.h; path = XVim/XVimNumericEvaluator.h; sourceTree = SOURCE_ROOT; }; @@ -437,6 +437,17 @@ name = Buffer; sourceTree = ""; }; + 6E2C392C183E4B880056E30F /* View */ = { + isa = PBXGroup; + children = ( + 6E2C392D183E4BA20056E30F /* XVimView.h */, + 6E2C392E183E4BA20056E30F /* XVimView.m */, + A2640CC717ACE651003D197D /* NSTextView+VimOperation.h */, + A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */, + ); + name = View; + sourceTree = ""; + }; 6EF220D3183E29EC00B814B8 /* Foundation */ = { isa = PBXGroup; children = ( @@ -699,6 +710,7 @@ children = ( C345DDB6154CCE7E009F232E /* Strings */, 6E08A8781836DC9F00905508 /* Buffer */, + 6E2C392C183E4B880056E30F /* View */, C3552DA6153AC6F800D57577 /* XVimHistoryHandler.h */, C3552DA7153AC6F800D57577 /* XVimHistoryHandler.m */, C36C1045153104EC00CE1D62 /* XVimMotionType.h */, @@ -724,8 +736,6 @@ A2126B7416FB30B0000BE21C /* XVimMark.m */, A2126B7616FB4806000BE21C /* XVimMarks.h */, A2126B7716FB4806000BE21C /* XVimMarks.m */, - A2640CC717ACE651003D197D /* NSTextView+VimOperation.h */, - A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */, ); name = "XCode Independent Classes"; sourceTree = ""; @@ -768,16 +778,14 @@ C345DDB8154CCF79009F232E /* Event Dispatchers */ = { isa = PBXGroup; children = ( - C38A5B3F15272B0500E1448D /* DVTSourceTextViewHook.h */, - C38A5B4015272B0500E1448D /* DVTSourceTextViewHook.m */, A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.h */, A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */, C38A5B4515272DCC00E1448D /* IDESourceCodeEditorHook.h */, C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */, A2ADAB811545C18F0093A908 /* IDEEditorAreaHook.h */, A2ADAB821545C18F0093A908 /* IDEEditorAreaHook.m */, - A26ACC4C154F2D6600B27D69 /* IDEEditorHook.h */, - A26ACC4D154F2D6700B27D69 /* IDEEditorHook.m */, + A26ACC4C154F2D6600B27D69 /* IDEEditor+XVim.h */, + A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */, A2702B0016FE537D005ADD76 /* IDEWorkspaceWindowHook.h */, A2702B0116FE537D005ADD76 /* IDEWorkspaceWindowHook.m */, C345DDB9154CE12A009F232E /* XVimHookManager.h */, @@ -904,7 +912,6 @@ A28F423917EEDBC200A3F7AE /* XVimRegisterEvaluator.m in Sources */, A28F423A17EEDBC200A3F7AE /* XVimOperatorEvaluator.m in Sources */, A28F423B17EEDBC200A3F7AE /* XVimKeyStroke.m in Sources */, - A28F423C17EEDBC200A3F7AE /* DVTSourceTextViewHook.m in Sources */, A28F423D17EEDBC200A3F7AE /* IDESourceCodeEditorHook.m in Sources */, A28F423E17EEDBC200A3F7AE /* XVimNumericEvaluator.m in Sources */, A28F423F17EEDBC200A3F7AE /* XVimKeymap.m in Sources */, @@ -929,7 +936,7 @@ A28F425117EEDBC200A3F7AE /* XVimStatusLine.m in Sources */, A28F425217EEDBC200A3F7AE /* XVimHookManager.m in Sources */, 6E2B332D1836E42F00EFE4E2 /* XVimBuffer.m in Sources */, - A28F425317EEDBC200A3F7AE /* IDEEditorHook.m in Sources */, + A28F425317EEDBC200A3F7AE /* IDEEditor+XVim.m in Sources */, A28F425417EEDBC200A3F7AE /* NSInsetTextView.m in Sources */, A28F425517EEDBC200A3F7AE /* IDEEditorArea+XVim.m in Sources */, A28F425617EEDBC200A3F7AE /* DVTSourceTextView+XVim.m in Sources */, @@ -950,6 +957,7 @@ A28F426317EEDBC200A3F7AE /* XVimTester+Motion.m in Sources */, A28F426417EEDBC200A3F7AE /* XVimTester+Operator.m in Sources */, A28F426517EEDBC200A3F7AE /* XVimTester+Mark.m in Sources */, + 6E2C3930183E4BA20056E30F /* XVimView.m in Sources */, A28F426617EEDBC200A3F7AE /* XVimTester+Visual.m in Sources */, A28F426717EEDBC200A3F7AE /* XVimTester+map.m in Sources */, 6E2B33341836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m in Sources */, @@ -989,7 +997,6 @@ F100DC2E150BB6BC002C703C /* XVimRegisterEvaluator.m in Sources */, A222B5E11514DFCD005E8802 /* XVimOperatorEvaluator.m in Sources */, C38A5B301526C6B100E1448D /* XVimKeyStroke.m in Sources */, - C38A5B4115272B0500E1448D /* DVTSourceTextViewHook.m in Sources */, C38A5B4715272DCD00E1448D /* IDESourceCodeEditorHook.m in Sources */, C38A5B4D1527CEA500E1448D /* XVimNumericEvaluator.m in Sources */, C366C23B15287EE30008C58E /* XVimKeymap.m in Sources */, @@ -1013,7 +1020,7 @@ A2ABFE1315497A3C002220E8 /* XVimStatusLine.m in Sources */, C345DDBB154CE12A009F232E /* XVimHookManager.m in Sources */, 6E2B332C1836E42F00EFE4E2 /* XVimBuffer.m in Sources */, - A26ACC4E154F2D6800B27D69 /* IDEEditorHook.m in Sources */, + A26ACC4E154F2D6800B27D69 /* IDEEditor+XVim.m in Sources */, C308865A1554C8380024161D /* NSInsetTextView.m in Sources */, A216F3A1156560FE00AD2529 /* IDEEditorArea+XVim.m in Sources */, A257C28F156567250098CA09 /* DVTSourceTextView+XVim.m in Sources */, @@ -1045,6 +1052,7 @@ A2640CC917ACE651003D197D /* NSTextView+VimOperation.m in Sources */, A24BE47517AD0D31001F797B /* XVimTester+ExCmd.m in Sources */, A28D895717BFF434002709D8 /* XVimTester+Search.m in Sources */, + 6E2C392F183E4BA20056E30F /* XVimView.m in Sources */, A23D002417DA0C7D007EBB21 /* XVimJoinEvaluator.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/XVim/DVTSourceTextViewHook.h b/XVim/DVTSourceTextViewHook.h deleted file mode 100644 index fea67e71..00000000 --- a/XVim/DVTSourceTextViewHook.h +++ /dev/null @@ -1,43 +0,0 @@ -// -// DVTSourceTextView.h -// XVim -// -// Created by Tomas Lundell on 31/03/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import -#import "DVTKit.h" - -@class DVTSourceTextView; -@class XVimStatusLine; - -@interface DVTSourceTextViewHook : NSObject -+ (void)hook; -+ (void)unhook; -@end - -@interface DVTSourceTextView(Hook) -// When initialize Xcode calls initWithCoder for Xcode4 and initWithFrame:textContainer: for Xcode5 -- (id)initWithCoder_:(NSCoder*)rect; -// - (id)initWithFrame_:(NSRect)rect; // We do not need to hook this -- (id)initWithFrame_:(NSRect)rect textContainer:(NSTextContainer *)container; -- (void)dealloc_; -- (void)setSelectedRanges_:(NSArray*)array affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)flag; -- (void)selectAll_:(id)sender; -- (void)cut_:(id)sender; -- (void)copy_:(id)sender; -- (void)paste_:(id)sender; -- (void)delete_:(id)sender; -- (void)keyDown_:(NSEvent *)theEvent; -- (void)mouseDown_:(NSEvent *)theEvent; -- (void)drawRect_:(NSRect)dirtyRect; -- (BOOL) performKeyEquivalent_:(NSEvent *)theEvent; -- (BOOL)shouldDrawInsertionPoint_; -- (void)_drawInsertionPointInRect_:(NSRect)aRect color:(NSColor*)aColor; -- (void)drawInsertionPointInRect_:(NSRect)aRect color:(NSColor*)aColor turnedOn:(BOOL)flag; -- (BOOL)becomeFirstResponder_; -- (void)didChangeText_; -- (void)viewDidMoveToSuperview_; -- (void)observeValueForKeyPath_:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; -@end \ No newline at end of file diff --git a/XVim/DVTSourceTextViewHook.m b/XVim/DVTSourceTextViewHook.m deleted file mode 100644 index 4cb48bd5..00000000 --- a/XVim/DVTSourceTextViewHook.m +++ /dev/null @@ -1,318 +0,0 @@ -// -// DVTSourceTextView.m -// XVim -// -// Created by Tomas Lundell on 31/03/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// -#define __XCODE5__ - -#import "DVTFoundation.h" -#import "DVTKit.h" -#import "DVTSourceTextViewHook.h" -#import "XVimEvaluator.h" -#import "XVimWindow.h" -#import "Hooker.h" -#import "Logger.h" -#import "DVTKit.h" -#import "XVimStatusLine.h" -#import "XVim.h" -#import "XVimOptions.h" -#import "IDEKit.h" -#import "IDEEditorArea+XVim.h" -#import "DVTSourceTextView+XVim.h" -#import "NSEvent+VimHelper.h" -#import "NSObject+ExtraData.h" -#import "XVim.h" -#import "XVimUtil.h" -#import "XVimSearch.h" -#import -#import -#import "NSTextView+VimOperation.h" - -@implementation DVTSourceTextViewHook - -+ (void)hook:(NSString*)method{ - NSString* cls = @"DVTSourceTextView"; - NSString* thisCls = NSStringFromClass([self class]); - [Hooker hookClass:cls method:method byClass:thisCls method:method]; -} - -+ (void)unhook:(NSString*)method{ - NSString* cls = @"DVTSourceTextView"; - [Hooker unhookClass:cls method:method]; -} - -+ (void)hook{ - [self hook:@"initWithCoder:"]; - [self hook:@"initWithFrame:textContainer:"]; - [self hook:@"dealloc"]; - [self hook:@"setSelectedRanges:affinity:stillSelecting:"]; - [self hook:@"selectAll:"]; - // [self hook:@"cut:"]; // Cut calls delete: after all. Do not need to hook - // [self hook:@"copy:"]; // Does not change any state. Do not need to hook - [self hook:@"paste:"]; - [self hook:@"delete:"]; - [self hook:@"keyDown:"]; - [self hook:@"mouseDown:"]; - [self hook:@"drawRect:"]; - [self hook:@"_drawInsertionPointInRect:color:"]; - [self hook:@"drawInsertionPointInRect:color:turnedOn:"]; - [self hook:@"didChangeText"]; - [self hook:@"viewDidMoveToSuperview"]; - [self hook:@"shouldChangeTextInRange:replacementString"]; - [self hook:@"observeValueForKeyPath:ofObject:change:context:"]; -} - -+ (void)unhook{ - // We never unhook these two methods. - // This is because we always have to control observers for XVimOptions - // If we unhook these, these may be a memory leak or it leads crash when a removing observer which has not been added as a observer in dealloc method. - // [self unhook:@"initWithCoder:"]; - // [self unhook:@"initWithFrame:textContainer:"]; - // [self unhook:@"dealloc"]; - [self unhook:@"setSelectedRanges:affinity:stillSelecting"]; - [self unhook:@"selectAll:"]; - [self unhook:@"cut:"]; - [self unhook:@"copy:"]; - [self unhook:@"paste:"]; - [self unhook:@"delete:"]; - [self unhook:@"keyDown:"]; - [self unhook:@"mouseDown:"]; - [self unhook:@"drawRect:"]; - [self unhook:@"_drawInsertionPointInRect:color:"]; - [self unhook:@"_drawInsertionPointInRect:color:turnedOn:"]; - [self unhook:@"didChangeText"]; - [self unhook:@"viewDidMoveToSuperview"]; - [self unhook:@"shouldChangeTextInRange:replacementString"]; - // We do not unhook this too. Since "addObserver" is called in initWithCoder we should keep this hook - // (Calling observerValueForKeyPath in NSObject results in throwing exception) - //[self unhook:@"observeValueForKeyPath:ofObject:change:context:"]; -} - -#ifdef __XCODE5__ -- (id)initWithFrame:(NSRect)rect textContainer:(NSTextContainer *)container{ - TRACE_LOG(@"ENTER"); - DVTSourceTextView *base = (DVTSourceTextView*)self; - id obj = (DVTSourceTextViewHook*)[base initWithFrame_:rect textContainer:container]; - if( nil != obj ){ - [XVim.instance.options addObserver:obj forKeyPath:@"hlsearch" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.options addObserver:obj forKeyPath:@"ignorecase" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.searcher addObserver:obj forKeyPath:@"lastSearchString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - } - return obj; -} - -- (id)initWithCoder:(NSCoder*)coder{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - id obj = (DVTSourceTextViewHook*)[base initWithCoder_:coder]; - if( nil != obj ){ - [XVim.instance.options addObserver:obj forKeyPath:@"hlsearch" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.options addObserver:obj forKeyPath:@"ignorecase" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.searcher addObserver:obj forKeyPath:@"lastSearchString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - } - return (DVTSourceTextViewHook*)[base initWithCoder_:coder]; -} -#else -- (id)initWithFrame:(NSRect)rect textContainer:(NSTextContainer *)container{ - TRACE_LOG(@"ENTER"); - DVTSourceTextView *base = (DVTSourceTextView*)self; - return (DVTSourceTextViewHook*)[base initWithFrame_:rect]; -} -- (id)initWithCoder:(NSCoder*)coder{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - id obj = (DVTSourceTextViewHook*)[base initWithCoder_:coder]; - if( nil != obj ){ - [XVim.instance.options addObserver:obj forKeyPath:@"hlsearch" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.options addObserver:obj forKeyPath:@"ignorecase" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.searcher addObserver:obj forKeyPath:@"lastSearchString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - } - return obj; -} -#endif - -// This pragma is for suppressing warning that the dealloc method does not call [super dealloc]. ([base dealloc_] calls [super dealloc] so we do not need it) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wall" -- (void)dealloc{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - @try{ - [XVim.instance.options removeObserver:self forKeyPath:@"hlsearch"]; - [XVim.instance.options removeObserver:self forKeyPath:@"ignorecase"]; - [XVim.instance.searcher removeObserver:self forKeyPath:@"lastSearchString"]; - } - @catch (NSException* exception){ - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - [base dealloc_]; - return; -} -#pragma GCC diagnostic pop - -- (void)setSelectedRanges:(NSArray *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)flag{ - [(DVTSourceTextView*)self setSelectedRanges_:ranges affinity:affinity stillSelecting:flag]; - [(NSTextView*)self xvim_syncStateFromView]; -} - -- (void)selectAll:(id)sender{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - XVimWindow* window = [base xvimWindow]; - [base selectAll_:sender]; - [window syncEvaluatorStack]; -} - -- (void)paste:(id)sender{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - XVimWindow* window = [base xvimWindow]; - [base paste_:sender]; - [window syncEvaluatorStack]; - -} - -- (void)delete:(id)sender{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - XVimWindow* window = [base xvimWindow]; - [base delete_:sender]; - [window syncEvaluatorStack]; -} - -- (void)keyDown:(NSEvent *)theEvent{ - @try{ - TRACE_LOG(@"Event:%@, XVimNotation:%@", theEvent.description, XVimKeyNotationFromXVimString([theEvent toXVimString])); - DVTSourceTextView *base = (DVTSourceTextView*)self; - XVimWindow* window = [base xvimWindow]; - if( nil == window ){ - [base keyDown_:theEvent]; - return; - } - - if( [window handleKeyEvent:theEvent] ){ - [base updateInsertionPointStateAndRestartTimer:YES]; - return; - } - // Call Original keyDown: - [base keyDown_:theEvent]; - return; - }@catch (NSException* exception) { - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - return; -} - -- (void)mouseDown:(NSEvent *)theEvent{ - @try{ - TRACE_LOG(@"Event:%@", theEvent.description); - DVTSourceTextView *base = (DVTSourceTextView*)self; - [base mouseDown_:theEvent]; - // When mouse down, NSTextView ( base in this case) takes the control of event loop internally - // and the method call above does not return immidiately and block until mouse up. mouseDragged: method is called from inside it but - // it never calls mouseUp: event. After mouseUp event is handled internally it returns the control. - // So the code here is executed AFTER mouseUp event is handled. - // At this point NSTextView changes its selectedRange so we usually have to sync XVim state. - - // TODO: To make it simple we should forward mouse events - // to handleKeyStroke as a special key stroke - // and the key stroke should be handled by the current evaluator. - XVimWindow* window = [base xvimWindow]; - [window syncEvaluatorStack]; - }@catch (NSException* exception) { - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - return; -} - - -- (void)drawRect:(NSRect)dirtyRect{ - @try{ - NSTextView* view = (NSTextView*)self; - - if( XVim.instance.options.hlsearch ){ - XVimMotion* lastSearch = [XVim.instance.searcher motionForRepeatSearch]; - if( nil != lastSearch.regex ){ - [view xvim_updateFoundRanges:lastSearch.regex withOption:lastSearch.option]; - } - }else{ - [view xvim_clearHighlightText]; - } - - - DVTSourceTextView *base = (DVTSourceTextView*)self; - [base drawRect_:dirtyRect]; - if( base.selectionMode != XVIM_VISUAL_NONE ){ - // NSTextView does not draw insertion point when selecting text. We have to draw insertion point by ourselves. - NSUInteger glyphIndex = [base insertionPoint]; - NSRect glyphRect = [base xvim_boundingRectForGlyphIndex:glyphIndex]; - [[[base insertionPointColor] colorWithAlphaComponent:0.5] set]; - NSRectFillUsingOperation( glyphRect, NSCompositeSourceOver); - } - }@catch (NSException* exception) { - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - return; -} - -// Drawing Caret -- (void)_drawInsertionPointInRect:(NSRect)aRect color:(NSColor*)aColor{ - @try{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - XVimWindow* window = [base xvimWindow]; - - // We do not call original _darawInsertionPointRect here - // Because it uses NSRectFill to draw the caret which overrides the character entirely. - // We want some tranceparency for the caret. - - // [base _drawInsertionPointInRect_:glyphRect color:aColor]; - - // Call our drawing method - [window drawInsertionPointInRect:aRect color:aColor]; - - }@catch (NSException* exception) { - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } -} - -- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color turnedOn:(BOOL)flag{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - // Call super class first. - [base drawInsertionPointInRect_:rect color:color turnedOn:flag]; - // Then tell the view to redraw to clear a caret. - if( !flag ){ - [base setNeedsDisplay:YES]; - } -} - -- (void)didChangeText{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - [base setNeedsUpdateFoundRanges:YES]; - [base didChangeText_]; -} - -- (void)viewDidMoveToSuperview { - @try{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - [base viewDidMoveToSuperview_]; - - // Hide scroll bars according to options - NSScrollView * scrollView = [base enclosingScrollView]; - [scrollView setPostsBoundsChangedNotifications:YES]; - }@catch (NSException* exception) { - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if([keyPath isEqualToString:@"ignorecase"] || [keyPath isEqualToString:@"hlsearch"] || [keyPath isEqualToString:@"lastSearchString"]){ - NSTextView* view = (NSTextView*)self; - [view setNeedsUpdateFoundRanges:YES]; - [view setNeedsDisplayInRect:[view visibleRect] avoidAdditionalLayout:YES]; - } -} - -@end - diff --git a/XVim/IDEEditorHook.h b/XVim/IDEEditor+XVim.h similarity index 54% rename from XVim/IDEEditorHook.h rename to XVim/IDEEditor+XVim.h index 6a6edc2e..81fc373f 100644 --- a/XVim/IDEEditorHook.h +++ b/XVim/IDEEditor+XVim.h @@ -9,11 +9,9 @@ #import #import "IDEKit.h" -@interface IDEEditorHook : NSViewController -+(void) hook; -@end +@interface IDEEditor (XVim) -@interface IDEEditor(Hook) -- (void)didSetupEditor_; -- (void)primitiveInvalidate_; -@end ++ (void)xvim_initialize; + +- (void)xvim_tryToSetupXVimView; +@end \ No newline at end of file diff --git a/XVim/IDEEditor+XVim.m b/XVim/IDEEditor+XVim.m new file mode 100644 index 00000000..767b80dc --- /dev/null +++ b/XVim/IDEEditor+XVim.m @@ -0,0 +1,153 @@ +// +// IDEEditor.m +// XVim +// +// Created by Suzuki Shuichiro on 5/1/12. +// Copyright (c) 2012 JugglerShu.Net. All rights reserved. +// + +#import +#import "XVim.h" +#import "XVimWindow.h" +#import "XVimStatusLine.h" +#import "IDESourceEditor.h" + +#import "IDEKit.h" +#import "IDEEditor+XVim.h" +#import "NSObject+XVimAdditions.h" +#import "DVTSourceTextView+XVim.h" +#import "Logger.h" +#import "Hooker.h" + +static char const * const DID_REGISTER_OBSERVER_KEY = "net.JugglerShu.IDEEditorHook._didRegisterObserver"; + + +@interface IDESourceCodeVersionsLogSubmode () +- (void)xvim_setPrimaryEditor:(IDEEditor *)primaryEditor; +@end + +@interface IDESourceCodeVersionsBlameSubmode () +- (void)xvim_setPrimaryEditor:(IDEEditor *)primaryEditor; +@end + +@interface IDESourceCodeVersionsTwoUpSubmode () +- (void)xvim_setPrimaryEditor:(IDEEditor *)primaryEditor; +- (void)xvim_setSecondaryEditor:(IDEEditor *)secondaryEditor; +@end + +static void xvim_setPrimaryEditor(id self, SEL _cmd, IDEEditor *editor) +{ + [self xvim_setPrimaryEditor:editor]; + [editor xvim_tryToSetupXVimView]; +} + +static void xvim_setSecondaryEditor(id self, SEL _cmd, IDEEditor *editor) +{ + [self xvim_setSecondaryEditor:editor]; + [editor xvim_tryToSetupXVimView]; +} + +@implementation IDEEditor (XVim) + ++ (void)xvim_initialize +{ + if (self == [IDEEditor class]) { + [NSClassFromString(@"IDESourceCodeVersionsLogSubmode") + xvim_swizzleInstanceMethod:@selector(setPrimaryEditor:) + with:@selector(xvim_setPrimaryEditor:) + imp:(IMP)xvim_setPrimaryEditor]; + + [NSClassFromString(@"IDESourceCodeVersionsBlameSubmode") + xvim_swizzleInstanceMethod:@selector(setPrimaryEditor:) + with:@selector(xvim_setPrimaryEditor:) + imp:(IMP)xvim_setPrimaryEditor]; + + [NSClassFromString(@"IDESourceCodeVersionsTwoUpSubmode") + xvim_swizzleInstanceMethod:@selector(setPrimaryEditor:) + with:@selector(xvim_setPrimaryEditor:) + imp:(IMP)xvim_setPrimaryEditor]; + + [NSClassFromString(@"IDESourceCodeVersionsTwoUpSubmode") + xvim_swizzleInstanceMethod:@selector(setSecondaryEditor:) + with:@selector(xvim_setSecondaryEditor:) + imp:(IMP)xvim_setSecondaryEditor]; + + [self xvim_swizzleInstanceMethod:@selector(didSetupEditor) + with:@selector(xvim_didSetupEditor)]; + [self xvim_swizzleInstanceMethod:@selector(primitiveInvalidate) + with:@selector(xvim_primitiveInvalidate)]; + } +} + +- (void)xvim_tryToSetupXVimView +{ + if ([self isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { + DVTSourceTextView *tv = (DVTSourceTextView *)[(id)self textView]; + XVimWindow *window = tv.xvimWindow; + + if (window && self.document.fileURL.isFileURL) { + DVTTextStorage *ts = [tv textStorage]; + if (ts && !ts.xvim_buffer) { + [XVimBuffer makeBufferForDocument:self.document textStorage:ts]; + } + if (tv && !tv.xvim_view) { + [tv xvim_makeXVimViewInWindow:window]; + } + } + } +} + +- (void)xvim_didSetupEditor +{ + [self xvim_didSetupEditor]; + + if ([self isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { + [self xvim_tryToSetupXVimView]; + } else if ([self isKindOfClass:[IDEComparisonEditor class]]) { + [[[(IDEComparisonEditor *)self submode] primaryEditor] xvim_tryToSetupXVimView]; + [[[(IDEComparisonEditor *)self submode] secondaryEditor] xvim_tryToSetupXVimView]; + } + + // If you do not like status line comment out folloing. + // ---- FROM HERE ---- + NSView *container = nil; + + if ([self isKindOfClass:NSClassFromString(@"IDESourceCodeComparisonEditor")]) { + container = [(IDESourceCodeComparisonEditor*)self layoutView]; + } else if ([self isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { + container = [(IDESourceCodeEditor*)self containerView]; + } else { + return; + } + + if (container != nil) { + XVimStatusLine *status = [XVimStatusLine associateOf:container]; + if (status == nil) { + // Insert status line + [container setPostsFrameChangedNotifications:YES]; + status = [[[XVimStatusLine alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)] autorelease]; + [container addSubview:status]; + [status associateWith:container]; + + // Layout + [[NSNotificationCenter defaultCenter] addObserver:status selector:@selector(didContainerFrameChanged:) name:NSViewFrameDidChangeNotification object:container]; + [status layoutStatus:container]; + [container performSelector:@selector(invalidateLayout)]; + + // For % register and to notify contents of editor is changed + [self addObserver:[XVim instance] forKeyPath:@"document" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; + objc_setAssociatedObject(self, DID_REGISTER_OBSERVER_KEY, @YES, OBJC_ASSOCIATION_ASSIGN); + } + } + //---- TO HERE ---- +} + +- (void)xvim_primitiveInvalidate +{ + if (objc_getAssociatedObject(self, DID_REGISTER_OBSERVER_KEY)) { + [self removeObserver:[XVim instance] forKeyPath:@"document"]; + } + [self xvim_primitiveInvalidate]; +} + +@end diff --git a/XVim/IDEEditorHook.m b/XVim/IDEEditorHook.m deleted file mode 100644 index b867c928..00000000 --- a/XVim/IDEEditorHook.m +++ /dev/null @@ -1,77 +0,0 @@ -// -// IDEEditor.m -// XVim -// -// Created by Suzuki Shuichiro on 5/1/12. -// Copyright (c) 2012 JugglerShu.Net. All rights reserved. -// - -#import "IDEEditorHook.h" -#import "IDEKit.h" -#import "IDESourceEditor.h" -#import "Hooker.h" -#import "Logger.h" -#import "XVim.h" -#import "XVimStatusLine.h" -#import - -#define DID_REGISTER_OBSERVER_KEY "net.JugglerShu.IDEEditorHook._didRegisterObserver" - -@implementation IDEEditorHook -+(void)hook{ - Class c = NSClassFromString(@"IDEEditor"); - - [Hooker hookMethod:@selector(didSetupEditor) ofClass:c withMethod:class_getInstanceMethod([self class], @selector(didSetupEditor) ) keepingOriginalWith:@selector(didSetupEditor_)]; - [Hooker hookMethod:@selector(primitiveInvalidate) ofClass:c withMethod:class_getInstanceMethod([self class], @selector(primitiveInvalidate)) keepingOriginalWith:@selector(primitiveInvalidate_)]; -} - -- (void)didSetupEditor{ - - IDEEditor* editor = (IDEEditor*)self; - [editor didSetupEditor_]; - - // If you do not like status line comment out folloing. - // ---- FROM HERE ---- - NSView* container = nil; - if( [NSStringFromClass([editor class]) isEqualToString:@"IDESourceCodeComparisonEditor"] ){ - container = [(IDESourceCodeComparisonEditor*)editor layoutView]; - } - else if( [NSStringFromClass([editor class]) isEqualToString:@"IDESourceCodeEditor"] ){ - container = [(IDESourceCodeEditor*)editor containerView]; - }else{ - return; - } - - if (container != nil) { - XVimStatusLine *status = [XVimStatusLine associateOf:container]; - if (status == nil) { - // Insert status line - [container setPostsFrameChangedNotifications:YES]; - status = [[[XVimStatusLine alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)] autorelease]; - [container addSubview:status]; - [status associateWith:container]; - - // Layout - [[NSNotificationCenter defaultCenter] addObserver:status selector:@selector(didContainerFrameChanged:) name:NSViewFrameDidChangeNotification object:container]; - [status layoutStatus:container]; - [container performSelector:@selector(invalidateLayout)]; - - // For % register and to notify contents of editor is changed - [editor addObserver:[XVim instance] forKeyPath:@"document" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; - objc_setAssociatedObject(editor, DID_REGISTER_OBSERVER_KEY, [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN); - } - } - //---- TO HERE ---- -} - -- (void)primitiveInvalidate { - IDEEditor *editor = (IDEEditor *)self; - NSNumber *didRegisterObserver = objc_getAssociatedObject(editor, DID_REGISTER_OBSERVER_KEY); - if ([didRegisterObserver boolValue]) { - [editor removeObserver:[XVim instance] forKeyPath:@"document"]; - } - - [editor primitiveInvalidate_]; -} - -@end diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h index 4ddc0d3f..ba6414a4 100644 --- a/XVim/NSTextView+VimOperation.h +++ b/XVim/NSTextView+VimOperation.h @@ -60,7 +60,6 @@ @property(readonly) XVIM_VISUAL_MODE selectionMode; @property(readonly) BOOL selectionToEOL; @property(readonly) CURSOR_MODE cursorMode; -@property(readonly) NSURL* documentURL; @property(strong) id xvimDelegate; @property BOOL needsUpdateFoundRanges; @property(readonly) NSArray* foundRanges; diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index 08cb5925..7dea3e05 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -48,7 +48,6 @@ @interface NSTextView () @property XVIM_VISUAL_MODE selectionMode; @property BOOL selectionToEOL; @property CURSOR_MODE cursorode; -@property(strong) NSURL* documentURL; @property(readonly) NSMutableArray* foundRanges; // Internal properties @@ -61,7 +60,6 @@ @interface NSTextView(VimOperationPrivate) - (void)xvim_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve; - (void)xvim_syncState; // update self's properties with our variables - (NSArray*)xvim_selectedRanges; -- (void)xvim_setSelectedRange:(NSRange)range; - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion; - (NSRange)xvim_getOperationRangeFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type; - (void)xvim_indentCharacterRange:(NSRange)range; @@ -421,10 +419,6 @@ - (void)setCursorMode:(CURSOR_MODE)cursorMode{ [self setInteger:cursorMode forName:@"cursorMode"]; } -- (NSURL*)documentURL{ - return self.textStorage.xvim_buffer.document.fileURL; -} - - (void)setXvimDelegate:(id)xvimDelegate{ [self setData:xvimDelegate forName:@"xvimDelegate"]; } @@ -1903,32 +1897,6 @@ - (void)dumpState{ LOG_STATE(); } -// xvim_setSelectedRange is an internal method -// This is used when you want to call [self setSelectedRrange]; -// The difference is that this checks the bounds(range can not be include EOF) and protect from Assersion -// Cursor can be on EOF but EOF can not be selected. -// It means that -// - setSelectedRange:NSMakeRange( indexOfEOF, 0 ) is allowed -// - setSelectedRange:NSMakeRange( indexOfEOF, 1 ) is not allowed -- (void)xvim_setSelectedRange:(NSRange)range{ - if( [self.textStorage isEOF:range.location] ){ - [self setSelectedRange:NSMakeRange(range.location,0)]; - return; - } - if( 0 == range.length ){ - // No need to check bounds - }else{ - NSUInteger lastIndex = range.location + range.length - 1; - if( [self.textStorage isEOF:lastIndex] ){ - range.length--; - }else{ - // No need to change the selection area - } - } - [self setSelectedRange:range]; - LOG_STATE(); -} - - (NSArray*)xvim_selectedRanges{ if (self.selectionMode != XVIM_VISUAL_BLOCK) { return [NSArray arrayWithObject:[NSValue valueWithRange:[self _xvim_selectedRange]]]; diff --git a/XVim/Test/XVimTestCase.m b/XVim/Test/XVimTestCase.m index 2138e2f7..939ef093 100644 --- a/XVim/Test/XVimTestCase.m +++ b/XVim/Test/XVimTestCase.m @@ -62,9 +62,12 @@ - (void)dealloc{ } - (void)setUp{ - [[[XVimLastActiveSourceView() xvimWindow] sourceView] xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - [XVimLastActiveSourceView() setString:self.initialText]; - [XVimLastActiveSourceView() setSelectedRange:self.initialRange]; + XVimWindow *window = XVimLastActiveSourceView().xvimWindow; + NSTextView *view = window.currentView.textView; + + [view xvim_changeSelectionMode:XVIM_VISUAL_NONE]; + [view setString:self.initialText]; + [view setSelectedRange:self.initialRange]; } - (BOOL)assert{ diff --git a/XVim/XVim.h b/XVim/XVim.h index eed4f544..2fdd6a72 100644 --- a/XVim/XVim.h +++ b/XVim/XVim.h @@ -28,11 +28,14 @@ extern NSString * const XVimBufferChangedNotification; +extern NSString * const XVimEnabledStatusChangedNotification; extern NSString * const XVimBufferKey; @interface XVim : NSObject + (XVim*)instance; +@property (nonatomic, readonly) BOOL disabled; + @property (strong) XVimOptions* options; @property (strong) XVimSearch* searcher; @property (strong) XVimMotion* lastCharacterSearchMotion; diff --git a/XVim/XVim.m b/XVim/XVim.m index f857d42d..3f177713 100644 --- a/XVim/XVim.m +++ b/XVim/XVim.m @@ -33,7 +33,6 @@ #import "XVimHistoryHandler.h" #import "XVimHookManager.h" #import "XVimCommandLine.h" -#import "DVTSourceTextViewHook.h" #import "XVimMarks.h" #import "XVimMotion.h" #import "XVimTester.h" @@ -42,6 +41,7 @@ #import "objc/runtime.h" NSString * const XVimBufferChangedNotification = @"XVimBufferChangedNotification"; +NSString * const XVimEnabledStatusChangedNotification = @"XVimBufferEnableNotification"; NSString * const XVimBufferKey = @"XVimBufferKey"; @interface XVim() { @@ -57,6 +57,7 @@ - (void)parseRcFile; @end @implementation XVim +@synthesize disabled = _disabled; // For reverse engineering purpose. +(void)receiveNotification:(NSNotification*)notification{ @@ -241,8 +242,8 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N NSTextStorage *textStorage = [[object document] textStorage]; XVimBuffer *buffer = document.xvim_buffer; - self.document = document.fileURL.path; - if (!buffer) { + if (!buffer && [document.fileURL isFileURL]) { + self.document = document.fileURL.path; buffer = [XVimBuffer makeBufferForDocument:document textStorage:textStorage]; } if (buffer) { @@ -337,14 +338,17 @@ - (void)runTest:(id)sender{ [self.testRunner runTest]; } -- (void)toggleXVim:(id)sender{ - if( [(NSCell*)sender state] == NSOnState ){ - [DVTSourceTextViewHook unhook]; - [(NSCell*)sender setState:NSOffState]; - }else{ - [DVTSourceTextViewHook hook]; - [(NSCell*)sender setState:NSOnState]; +- (void)toggleXVim:(NSCell *)sender{ + if ([sender state] == NSOnState) { + _disabled = YES; + [sender setState:NSOffState]; + } else { + _disabled = NO; + [sender setState:NSOnState]; } + + [[NSNotificationCenter defaultCenter] postNotificationName:XVimEnabledStatusChangedNotification + object:nil userInfo:nil]; } @end diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 4bfcd458..96161a71 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -26,7 +26,15 @@ #define ASSERT_VALID_CURSOR_POS(x) #endif -@class XVimUndoOperation; +@class XVimUndoOperation, XVimBuffer; + +@interface NSTextStorage (XVimBuffer) +@property (nonatomic, readonly) XVimBuffer *xvim_buffer; +@end + +@interface NSDocument (XVimBuffer) +@property (nonatomic, readonly) XVimBuffer *xvim_buffer; +@end /** @brief class to represent an XVim Buffer * @@ -298,11 +306,3 @@ - (NSUInteger)incrementNumberAtIndex:(NSUInteger)index by:(int64_t)offset; @end - -@interface NSTextStorage (XVimBuffer) -@property (nonatomic, readonly) XVimBuffer *xvim_buffer; -@end - -@interface NSDocument (XVimBuffer) -@property (nonatomic, readonly) XVimBuffer *xvim_buffer; -@end diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index a578fb60..e35faca7 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -12,9 +12,28 @@ #import "XVimUndo.h" #import "XVimTextStoring.h" #import "NSString+VimHelper.h" +#import "Logger.h" static char const * const XVIM_KEY_BUFFER = "xvim_buffer"; +@implementation NSTextStorage (XVimBuffer) + +- (XVimBuffer *)xvim_buffer +{ + return objc_getAssociatedObject(self, XVIM_KEY_BUFFER); +} + +@end + +@implementation NSDocument (XVimBuffer) + +- (XVimBuffer *)xvim_buffer +{ + return objc_getAssociatedObject(self, XVIM_KEY_BUFFER); +} + +@end + @implementation XVimBuffer { NSDocument *__unsafe_unretained _document; NSTextStorage *__unsafe_unretained _textStorage; @@ -47,6 +66,8 @@ - (instancetype)initWithDocument:(NSDocument *)document objc_setAssociatedObject(document, XVIM_KEY_BUFFER, self, OBJC_ASSOCIATION_RETAIN); objc_setAssociatedObject(textStorage, XVIM_KEY_BUFFER, self, OBJC_ASSOCIATION_RETAIN); + DEBUG_LOG("Buffer %p created for %@, backed by %@", self, document, textStorage); + if ([_textStorage conformsToProtocol:@protocol(XVimTextStoring)]) { #define CHECK(k, sel) _flags.has_xvim_##k = (bool)[_textStorage respondsToSelector:sel] CHECK(string, @selector(xvim_string)); @@ -64,6 +85,8 @@ - (instancetype)initWithDocument:(NSDocument *)document - (void)dealloc { + DEBUG_LOG("Buffer %p destroyed", self); + [_curOp release]; [super dealloc]; } @@ -71,10 +94,6 @@ - (void)dealloc + (XVimBuffer *)makeBufferForDocument:(NSDocument *)document textStorage:(NSTextStorage *)textStorage { - XVimBuffer *buffer = document.xvim_buffer; - - if (buffer) return buffer; - return [[[[self class] alloc] initWithDocument:document textStorage:textStorage] autorelease]; } @@ -659,21 +678,3 @@ - (NSUInteger)incrementNumberAtIndex:(NSUInteger)index by:(int64_t)offset } @end - -@implementation NSTextStorage (XVimBuffer) - -- (XVimBuffer *)xvim_buffer -{ - return objc_getAssociatedObject(self, XVIM_KEY_BUFFER); -} - -@end - -@implementation NSDocument (XVimBuffer) - -- (XVimBuffer *)xvim_buffer -{ - return objc_getAssociatedObject(self, XVIM_KEY_BUFFER); -} - -@end diff --git a/XVim/XVimCommandLineEvaluator.m b/XVim/XVimCommandLineEvaluator.m index ce5dc199..03fdfd7e 100644 --- a/XVim/XVimCommandLineEvaluator.m +++ b/XVim/XVimCommandLineEvaluator.m @@ -42,7 +42,7 @@ - (id)initWithWindow:(XVimWindow *)window _onKeyPress = [keyPressHandler copy]; _historyNo = 0; _evalutionResult = nil; - self.lastTextView = window.sourceView; + self.lastTextView = self.sourceView; XVimCommandField *commandField = self.window.commandLine.commandField; [commandField setString:_firstLetter]; [commandField moveToEndOfLine:self]; @@ -94,7 +94,7 @@ - (XVimEvaluator*)execute{ - (void)takeFocusFromWindow{ XVimCommandField *commandField = self.window.commandLine.commandField; [commandField setDelegate:self.window]; - [[[self.window sourceView] window] makeFirstResponder:commandField]; + [self.sourceView.window makeFirstResponder:commandField]; } - (void)relinquishFocusToWindow{ diff --git a/XVim/XVimDebug.m b/XVim/XVimDebug.m index 9e341191..e6b04c05 100644 --- a/XVim/XVimDebug.m +++ b/XVim/XVimDebug.m @@ -27,18 +27,22 @@ - (void)trace:(NSArray*)params withWindow:(XVimWindow*)window{ } } -- (void)highlight:(NSArray*)params withWindow:(XVimWindow*)window{ - NSTextView* view = window.sourceView; - [[view textStorage] beginEditing]; - [[view textStorage] addAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] range:NSMakeRange(0,5)]; - [[view textStorage] endEditing]; +- (void)highlight:(NSArray*)params withWindow:(XVimWindow*)window +{ + NSTextStorage *ts = window.currentBuffer.textStorage; + + [ts beginEditing]; + [ts addAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] range:NSMakeRange(0,5)]; + [ts endEditing]; } -- (void)highlightclear:(NSArray*)params withWindow:(XVimWindow*)window{ - NSTextView* view = window.sourceView; - [[view textStorage] beginEditing]; - [[view textStorage] addAttribute:NSBackgroundColorAttributeName value:[NSColor clearColor] range:NSMakeRange(0, view.string.length)]; - [[view textStorage] endEditing]; +- (void)highlightclear:(NSArray*)params withWindow:(XVimWindow*)window +{ + NSTextStorage *ts = window.currentBuffer.textStorage; + + [ts beginEditing]; + [ts addAttribute:NSBackgroundColorAttributeName value:[NSColor clearColor] range:NSMakeRange(0, ts.length)]; + [ts endEditing]; } @end diff --git a/XVim/XVimEvaluator.m b/XVim/XVimEvaluator.m index f57127a3..298d0bdc 100644 --- a/XVim/XVimEvaluator.m +++ b/XVim/XVimEvaluator.m @@ -55,6 +55,7 @@ - (id)init { - (id)initWithWindow:(XVimWindow*)window{ NSAssert( nil != window, @"window must not be nil"); + DEBUG_LOG("created %@ evaluator with window %@", self.class, window); if(self = [super init]){ self.window = window; self.parent = nil; @@ -75,9 +76,9 @@ - (void)dealloc{ [super dealloc]; } -- (NSTextView*)sourceView +- (NSTextView *)sourceView { - return self.window.sourceView; + return self.window.currentView.textView; } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ diff --git a/XVim/XVimExCommand.m b/XVim/XVimExCommand.m index 784cb92e..30f06dac 100644 --- a/XVim/XVimExCommand.m +++ b/XVim/XVimExCommand.m @@ -585,7 +585,7 @@ - (void)dealloc{ // This method correnspons parsing part of get_address in ex_cmds.c - (NSUInteger)getAddress:(unichar*)parsing :(unichar**)cmdLeft inWindow:(XVimWindow*)window { - NSTextView* view = [window sourceView]; + NSTextView* view = window.currentView.textView; //DVTFoldingTextStorage* storage = [view textStorage]; //TRACE_LOG(@"Storage Class:%@", NSStringFromClass([storage class])); NSUInteger addr = NSNotFound; @@ -733,7 +733,7 @@ - (XVimExArg*)parseCommand:(NSString*)cmd inWindow:(XVimWindow*)window exarg.lineBegin = NSNotFound; exarg.lineEnd = NSNotFound; - NSTextView* view = [window sourceView]; + NSTextView* view = window.currentView.textView; XVimBuffer *buffer = window.currentBuffer; for(;;){ NSUInteger addr = [self getAddress:parsing :&parsing inWindow:window]; @@ -816,7 +816,7 @@ - (void)executeCommand:(NSString*)cmd inWindow:(XVimWindow*)window // Actual parsing is done in following method. XVimExArg* exarg = [self parseCommand:cmd inWindow:window]; if( exarg.cmd == nil ) { - NSTextView* srcView = [window sourceView]; + NSTextView* srcView = window.currentView.textView; XVimBuffer *buffer = window.currentBuffer; // Jump to location @@ -829,7 +829,7 @@ - (void)executeCommand:(NSString*)cmd inWindow:(XVimWindow*)window pos_wo_space = pos; } [srcView setSelectedRange:NSMakeRange(pos_wo_space,0)]; - [srcView xvim_scrollTo:[window.sourceView insertionPoint]]; + [srcView xvim_scrollTo:[window.currentView.textView insertionPoint]]; return; } @@ -1001,7 +1001,7 @@ - (void)mapClearMode:(XVIM_MODE)mode{ } - (void)marks:(XVimExArg*)args inWindow:(XVimWindow*)window{ // This is currently impelemented for debugging purpose - NSString* local = [[XVim instance].marks dumpMarksForDocument:window.sourceView.documentURL.path]; + NSString* local = [[XVim instance].marks dumpMarksForDocument:window.currentBuffer.document.fileURL.path]; NSString* file = [[XVim instance].marks dumpFileMarks]; [[XVim instance] writeToConsole:@"----LOCAL MARKS----\n%@", local]; [[XVim instance] writeToConsole:@"----FILE MARKS----\n%@", file]; @@ -1097,7 +1097,7 @@ - (void)run:(XVimExArg*)args inWindow:(XVimWindow*)window{ - (void)set:(XVimExArg*)args inWindow:(XVimWindow*)window{ NSString* setCommand = [args.arg stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - NSTextView* srcView = [window sourceView]; + NSTextView* srcView = window.currentView.textView; XVimOptions* options = [[XVim instance] options]; if( [setCommand rangeOfString:@"="].location != NSNotFound ){ @@ -1127,7 +1127,7 @@ - (void)set:(XVimExArg*)args inWindow:(XVimWindow*)window{ } - (void)sort:(XVimExArg *)args inWindow:(XVimWindow *)window{ - NSTextView *view = [window sourceView]; + NSTextView *view = window.currentView.textView; NSString *cmdString = [[args cmd] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; NSString *argsString = [args arg]; diff --git a/XVim/XVimGActionEvaluator.m b/XVim/XVimGActionEvaluator.m index 84f8412c..33aeb593 100644 --- a/XVim/XVimGActionEvaluator.m +++ b/XVim/XVimGActionEvaluator.m @@ -41,7 +41,7 @@ - (XVimEvaluator*)f{ } - (XVimEvaluator*)i{ - XVimMark* mark = [[XVim instance].marks markForName:@"^" forDocument:self.sourceView.documentURL.path]; + XVimMark* mark = [[XVim instance].marks markForName:@"^" forDocument:self.window.currentBuffer.document]; XVimInsertionPoint mode = XVIM_INSERT_DEFAULT; XVimBuffer *buffer = self.window.currentBuffer; @@ -55,7 +55,7 @@ - (XVimEvaluator*)i{ XVimMark* cur_mark = [[[XVimMark alloc] init] autorelease]; cur_mark.line = [self.sourceView insertionLine]; cur_mark.column = [self.sourceView insertionColumn]; - cur_mark.document = [self.sourceView documentURL].path; + cur_mark.document = self.window.currentBuffer.document.fileURL.path; if( nil != mark.document){ [[XVim instance].marks setMark:cur_mark forName:@"'"]; } diff --git a/XVim/XVimHookManager.m b/XVim/XVimHookManager.m index 5cd7d179..9460cbcc 100644 --- a/XVim/XVimHookManager.m +++ b/XVim/XVimHookManager.m @@ -8,21 +8,23 @@ #import "XVimHookManager.h" #import "IDEEditorAreaHook.h" -#import "DVTSourceTextViewHook.h" #import "IDESourceCodeEditorHook.h" -#import "IDEEditorHook.h" +#import "IDEEditor+XVim.h" #import "IDEWorkspaceWindowHook.h" #import "DVTSourceTextScrollViewHook.h" +#import "XVimView.h" @implementation XVimHookManager -+ (void)hookWhenPluginLoaded{ ++ (void)hookWhenPluginLoaded +{ [IDEEditorAreaHook hook]; [IDEWorkspaceWindowHook hook]; - [DVTSourceTextViewHook hook]; - [DVTSourceTextScrollViewHook hook]; - [IDESourceCodeEditorHook hook]; - [IDEEditorHook hook]; + [DVTSourceTextScrollViewHook hook]; + [IDESourceCodeEditorHook hook]; + [IDEEditor xvim_initialize]; + [IDEComparisonEditor xvim_initialize]; + [XVimView class]; } @end diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index c619339b..1d3a4bd5 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -206,7 +206,7 @@ - (void)didEndHandler{ // Position for "^" is before escaped from insert mode NSUInteger pos = self.sourceView.insertionPoint; XVimBuffer *buffer = self.window.currentBuffer; - XVimMark *mark = XVimMakeMark([buffer lineNumberAtIndex:pos], [buffer columnOfIndex:pos], buffer.document.fileURL.path); + XVimMark *mark = XVimMakeMark([buffer lineNumberAtIndex:pos], [buffer columnOfIndex:pos], buffer.document); if( nil != mark.document ){ [[XVim instance].marks setMark:mark forName:@"^"]; } @@ -215,7 +215,7 @@ - (void)didEndHandler{ // Position for "." is after escaped from insert mode pos = self.sourceView.insertionPoint; - mark = XVimMakeMark([buffer lineNumberAtIndex:pos], [buffer columnOfIndex:pos], buffer.document.fileURL.path); + mark = XVimMakeMark([buffer lineNumberAtIndex:pos], [buffer columnOfIndex:pos], buffer.document); if( nil != mark.document ){ [[XVim instance].marks setMark:mark forName:@"."]; } diff --git a/XVim/XVimJoinEvaluator.m b/XVim/XVimJoinEvaluator.m index 79eb1fa5..0bbe4e7e 100644 --- a/XVim/XVimJoinEvaluator.m +++ b/XVim/XVimJoinEvaluator.m @@ -27,7 +27,7 @@ - (XVimEvaluator*)motionFixed:(XVimMotion*)motion{ // J and 2J is the same motion.count--; } - [self.window.sourceView xvim_join:motion.count addSpace:_addSpace]; + [self.sourceView xvim_join:motion.count addSpace:_addSpace]; return nil; } diff --git a/XVim/XVimMark.h b/XVim/XVimMark.h index 0da51de5..881586e9 100644 --- a/XVim/XVimMark.h +++ b/XVim/XVimMark.h @@ -18,4 +18,4 @@ @end -#define XVimMakeMark(line, col, doc) [[[XVimMark alloc] initWithLine:line column:col document:doc] autorelease] \ No newline at end of file +#define XVimMakeMark(line, col, doc) [[[XVimMark alloc] initWithLine:line column:col document:doc.fileURL.path] autorelease] \ No newline at end of file diff --git a/XVim/XVimMarkSetEvaluator.m b/XVim/XVimMarkSetEvaluator.m index 89a91de2..b27ffa9f 100644 --- a/XVim/XVimMarkSetEvaluator.m +++ b/XVim/XVimMarkSetEvaluator.m @@ -32,7 +32,7 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ NSRange r = [self.sourceView selectedRange]; mark.line = [buffer lineNumberAtIndex:r.location]; mark.column = [buffer columnOfIndex:r.location]; - mark.document = [[self.sourceView documentURL] path]; + mark.document = buffer.document.fileURL.path; if( nil != mark.document ){ [[XVim instance].marks setMark:mark forName:keyStr]; } diff --git a/XVim/XVimMarks.h b/XVim/XVimMarks.h index 6ffaf83a..eb8d52a2 100644 --- a/XVim/XVimMarks.h +++ b/XVim/XVimMarks.h @@ -51,7 +51,7 @@ * This automatically detects if it is file mark. * If the character is not supported as a mark this returns nil **/ -- (XVimMark*)markForName:(NSString*)name forDocument:(NSString*)documentPath; +- (XVimMark*)markForName:(NSString*)name forDocument:(NSDocument *)document; /** * Set mark. diff --git a/XVim/XVimMarks.m b/XVim/XVimMarks.m index 79f60d98..fd9309d4 100644 --- a/XVim/XVimMarks.m +++ b/XVim/XVimMarks.m @@ -92,7 +92,9 @@ - (NSString*)dumpFileMarks{ } -- (XVimMark*)markForName:(NSString*)name forDocument:(NSString *)documentPath{ +- (XVimMark*)markForName:(NSString*)name forDocument:(NSDocument *)document +{ + NSString *documentPath = document.fileURL.path; NSAssert(nil != name, @"name can not be nil"); NSAssert(nil != documentPath, @"documentPath can not be nil"); diff --git a/XVim/XVimMotionEvaluator.m b/XVim/XVimMotionEvaluator.m index 27d11f79..cd2c93c5 100644 --- a/XVim/XVimMotionEvaluator.m +++ b/XVim/XVimMotionEvaluator.m @@ -190,7 +190,7 @@ - (XVimEvaluator*)g{ - (XVimEvaluator*)onComplete_g:(XVimGMotionEvaluator*)childEvaluator{ if( [childEvaluator.key.toSelectorString isEqualToString:@"SEMICOLON"] ){ - XVimMark* mark = [[XVim instance].marks markForName:@"." forDocument:[self.sourceView documentURL].path]; + XVimMark* mark = [[XVim instance].marks markForName:@"." forDocument:self.window.currentBuffer.document]; return [self jumpToMark:mark firstOfLine:NO]; }else{ return [self _motionFixed:childEvaluator.motion]; @@ -352,7 +352,7 @@ - (XVimEvaluator*)jumpToMark:(XVimMark*)mark firstOfLine:(BOOL)fol{ return [XVimEvaluator invalidEvaluator]; } - if( ![mark.document isEqualToString:self.sourceView.documentURL.path]){ + if( ![mark.document isEqualToString:buffer.document.fileURL.path]){ IDEDocumentController* ctrl = [IDEDocumentController sharedDocumentController]; NSError* error; NSURL* doc = [NSURL fileURLWithPath:mark.document]; @@ -372,7 +372,7 @@ - (XVimEvaluator*)jumpToMark:(XVimMark*)mark firstOfLine:(BOOL)fol{ XVimMark* cur_mark = [[[XVimMark alloc] init] autorelease]; cur_mark.line = [buffer lineNumberAtIndex:cur_pos]; cur_mark.column = [buffer columnOfIndex:cur_pos]; - cur_mark.document = [self.sourceView documentURL].path; + cur_mark.document = buffer.document.fileURL.path; if( nil != mark.document ){ [[XVim instance].marks setMark:cur_mark forName:@"'"]; } @@ -398,7 +398,7 @@ - (XVimEvaluator*)onComplete_SQUOTE:(XVimArgumentEvaluator*)childEvaluator{ // This will work for Ctrl-c as register c but it should not //NSString* key = [childEvaluator.keyStroke toString]; NSString* key = [NSString stringWithFormat:@"%c", childEvaluator.keyStroke.character]; - XVimMark* mark = [[XVim instance].marks markForName:key forDocument:[self.sourceView documentURL].path]; + XVimMark* mark = [[XVim instance].marks markForName:key forDocument:self.window.currentBuffer.document]; return [self jumpToMark:mark firstOfLine:YES]; } @@ -413,7 +413,7 @@ - (XVimEvaluator*)onComplete_BACKQUOTE:(XVimArgumentEvaluator*)childEvaluator{ // This will work for Ctrl-c as register c but it should not // NSString* key = [childEvaluator.keyStroke toString]; NSString* key = [NSString stringWithFormat:@"%c", childEvaluator.keyStroke.character]; - XVimMark* mark = [[XVim instance].marks markForName:key forDocument:[self.sourceView documentURL].path]; + XVimMark* mark = [[XVim instance].marks markForName:key forDocument:self.window.currentBuffer.document]; return [self jumpToMark:mark firstOfLine:NO]; } @@ -432,7 +432,7 @@ - (XVimEvaluator*)DOLLAR{ // it will moves to start of the numeric argument - 1 lines down. - (XVimEvaluator*)UNDERSCORE{ // TODO add this motion interface to NSTextView - NSTextView *view = [self.window sourceView]; + NSTextView *view = self.sourceView; XVimBuffer *buffer = self.window.currentBuffer; NSRange r = [view selectedRange]; NSUInteger repeat = self.numericArg; diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index 13d6f9ac..43fc75be 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -44,13 +44,6 @@ @interface XVimNormalEvaluator() { @implementation XVimNormalEvaluator --(id)initWithWindow:(XVimWindow *)window{ - self = [super initWithWindow:window]; - if (self) { - } - return self; -} - - (void)becameHandler{ [super becameHandler]; [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_NONE]; @@ -147,7 +140,7 @@ - (XVimEvaluator*)C_g // process XVimWindow *window = self.window; XVimBuffer *buffer = window.currentBuffer; - NSRange range = window.sourceView.selectedRange; + NSRange range = self.sourceView.selectedRange; NSUInteger numberOfLines = [buffer numberOfLines]; NSUInteger lineNumber = [buffer lineNumberAtIndex:range.location]; @@ -172,7 +165,7 @@ - (XVimEvaluator*)g{ - (XVimEvaluator*)onComplete_g:(XVimGActionEvaluator*)childEvaluator{ if( [childEvaluator.key.toSelectorString isEqualToString:@"SEMICOLON"] ){ - XVimMark* mark = [[XVim instance].marks markForName:@"." forDocument:[self.sourceView documentURL].path]; + XVimMark* mark = [[XVim instance].marks markForName:@"." forDocument:self.window.currentBuffer.document]; return [self jumpToMark:mark firstOfLine:NO]; }else{ if( childEvaluator.motion != nil ){ diff --git a/XVim/XVimOperatorEvaluator.h b/XVim/XVimOperatorEvaluator.h index f5bc533b..3feea8d3 100644 --- a/XVim/XVimOperatorEvaluator.h +++ b/XVim/XVimOperatorEvaluator.h @@ -16,7 +16,6 @@ @class XVimOperatorAction; @interface XVimOperatorEvaluator : XVimMotionEvaluator -- (id)initWithWindow:(XVimWindow *)window; - (XVimEvaluator*)executeOperationWithMotion:(XVimMotion*)motion; /* - (XVimEvaluator*)b; diff --git a/XVim/XVimOperatorEvaluator.m b/XVim/XVimOperatorEvaluator.m index 17fb69d4..d7e87901 100644 --- a/XVim/XVimOperatorEvaluator.m +++ b/XVim/XVimOperatorEvaluator.m @@ -16,6 +16,9 @@ #import "XVimMark.h" #import "XVimMarks.h" #import "NSTextView+VimOperation.h" +#import "XVimYankEvaluator.h" +#import "XVimShiftEvaluator.h" +#import "XVimJoinEvaluator.h" @interface XVimOperatorEvaluator() { } @@ -29,12 +32,6 @@ + (XVimEvaluator*)doOperationWithMotion:(XVimMotion*)motion onView:(NSTextView*) return nil; } -- (id)initWithWindow:window{ - if (self = [super initWithWindow:window]){ - } - return self; -} - - (void)dealloc { [super dealloc]; } @@ -71,20 +68,20 @@ - (XVimEvaluator*)_motionFixed:(XVimMotion *)motion{ // We do not fix the change here if next evaluator is not nil becaust it waits more input for fix the command. // This happens for a command like "cw..." if( nil == evaluator ){ - NSTextView* view = self.window.sourceView; - NSString* className = NSStringFromClass([self class]); - if( ![className isEqualToString:@"XVimYankEvaluator"]){ + XVimBuffer *buffer = self.window.currentBuffer; + Class aClass = self.class; + + if (aClass != [XVimYankEvaluator class]) { [[XVim instance] fixOperationCommands]; XVimMark* mark = nil; - if( [className isEqualToString:@"XVimJoinEvaluator"]){ + if (aClass == [XVimJoinEvaluator class]) { // This is specical case for join operation. // The mark is set at the head of next line of the insertion point after the operation - mark = XVimMakeMark([self.sourceView insertionLine]+1, 0, view.documentURL.path); - }else if( [className isEqualToString:@"XVimShiftEvaluator"] ){ - mark = XVimMakeMark([self.sourceView insertionLine], 0, view.documentURL.path); - } - else{ - mark = XVimMakeMark([self.sourceView insertionLine], [self.sourceView insertionColumn], view.documentURL.path); + mark = XVimMakeMark([self.sourceView insertionLine]+1, 0, buffer.document); + } else if (aClass == [XVimShiftEvaluator class]) { + mark = XVimMakeMark([self.sourceView insertionLine], 0, buffer.document); + } else { + mark = XVimMakeMark([self.sourceView insertionLine], [self.sourceView insertionColumn], buffer.document); } if( nil != mark.document){ diff --git a/XVim/XVimSearch.m b/XVim/XVimSearch.m index 5f4d7c35..0058a2fb 100644 --- a/XVim/XVimSearch.m +++ b/XVim/XVimSearch.m @@ -14,7 +14,6 @@ #import "XVimOptions.h" #import "Logger.h" #import "XVimUtil.h" -#import "IDEKit.h" @implementation XVimSearch @@ -153,7 +152,7 @@ - (NSRange)searchForwardFrom:(NSUInteger)from inWindow:(XVimWindow*)window{ #ifdef __MAC_10_7 XVimOptions *options = [[XVim instance] options]; - NSTextView* srcView = [window sourceView]; + NSTextView* srcView = window.currentView.textView; NSUInteger search_base = from; NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines | NSRegularExpressionUseUnicodeWordBoundaries; @@ -223,7 +222,7 @@ - (NSRange)searchBackwardFrom:(NSUInteger)from inWindow:(XVimWindow*)window NSRange found = {NSNotFound, 0}; #ifdef __MAC_10_7 XVimOptions *options = [[XVim instance] options]; - NSTextView* srcView = [window sourceView]; + NSTextView* srcView = window.currentView.textView; NSUInteger search_base = from; NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines; @@ -307,7 +306,7 @@ - (NSRange)searchCurrentWordFrom:(NSUInteger)from forward:(BOOL)forward matchWho { NSRange found = {NSNotFound,0}; #ifdef __MAC_10_7 - NSTextView *view = [window sourceView]; + NSTextView *view = window.currentView.textView; NSRange begin = [view selectedRange]; NSString *string = [view string]; @@ -395,7 +394,7 @@ - (NSRange)replaceForwardFrom:(NSUInteger)from to:(NSUInteger)to inWindow:(XVimW #ifdef __MAC_10_7 - NSTextView* srcView = [window sourceView]; + NSTextView* srcView = window.currentView.textView; NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines|NSRegularExpressionUseUnicodeWordBoundaries; if ([self isCaseInsensitive]) @@ -477,7 +476,7 @@ - (void)substitute:(NSString*)ex_command from:(NSUInteger)from to:(NSUInteger)to // Find the position to end the searching NSUInteger endOfReplacement = [buffer indexOfLineNumber:to+1 column:0]; // Next line of the end of range. if( NSNotFound == endOfReplacement ){ - endOfReplacement = [[[window sourceView] string] length]; + endOfReplacement = buffer.length; } // This is lazy implementation. // When text is substituted the end location may be smaller or greater than original end position. diff --git a/XVim/XVimView.h b/XVim/XVimView.h new file mode 100644 index 00000000..e31aefbe --- /dev/null +++ b/XVim/XVimView.h @@ -0,0 +1,25 @@ +// +// XVimView.h +// XVim +// +// Created by John AppleSeed on 21/11/13. +// +// + +#import + +@class XVimView, XVimBuffer, XVimWindow; + +@interface NSTextView (XVimView) +@property (readonly, nonatomic) XVimView *xvim_view; + +- (XVimView *)xvim_makeXVimViewInWindow:(XVimWindow *)window; +@end + +@interface XVimView : NSObject +@property (readonly, nonatomic) XVimWindow *window; +@property (readonly, nonatomic) NSTextView *textView; + +- (instancetype)initWithView:(NSTextView *)view window:(XVimWindow *)window; + +@end diff --git a/XVim/XVimView.m b/XVim/XVimView.m new file mode 100644 index 00000000..ed7320ee --- /dev/null +++ b/XVim/XVimView.m @@ -0,0 +1,324 @@ +// +// XVimView.m +// XVim +// +// Created by John AppleSeed on 21/11/13. +// +// + +#import +#import "XVim.h" +#import "XVimView.h" +#import "XVimWindow.h" +#import "Logger.h" +#import "NSObject+XVimAdditions.h" +#import "NSTextView+VimOperation.h" +#import "XVimOptions.h" +#import "XVimSearch.h" + +static char const * const XVIM_KEY_VIEW = "xvim_view"; + +@implementation NSTextView (XVimView) + ++ (void)xvim_initialize +{ + if (self == [NSTextView class]) { + DEBUG_LOG("Swizzling NSTextView"); + +#define swizzle(sel) \ + [self xvim_swizzleInstanceMethod:@selector(sel) with:@selector(xvim_##sel)] + + swizzle(dealloc); + + swizzle(setSelectedRanges:affinity:stillSelecting:); + swizzle(selectAll:); + swizzle(paste:); + swizzle(delete:); + swizzle(keyDown:); + swizzle(mouseDown:); + swizzle(drawRect:); + swizzle(_drawInsertionPointInRect:color:); + swizzle(drawInsertionPointInRect:color:turnedOn:); + swizzle(didChangeText); + swizzle(viewDidMoveToSuperview); + swizzle(observeValueForKeyPath:ofObject:change:context:); + } + +#undef swizzle +} + +- (XVimView *)xvim_view +{ + return objc_getAssociatedObject(self, XVIM_KEY_VIEW); +} + +- (XVimView *)xvim_makeXVimViewInWindow:(XVimWindow *)window +{ + return [[[XVimView alloc] initWithView:self window:window] autorelease]; +} + +- (void)xvim_setupForXVimView:(XVimView *)view +{ + if (!self.xvim_view) { + [XVim.instance.options addObserver:self forKeyPath:@"hlsearch" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + [XVim.instance.options addObserver:self forKeyPath:@"ignorecase" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + [XVim.instance.searcher addObserver:self forKeyPath:@"lastSearchString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + } + objc_setAssociatedObject(self, XVIM_KEY_VIEW, view, OBJC_ASSOCIATION_RETAIN); +} + +- (void)xvim_dealloc +{ + if (self.xvim_view) { + @try { + [XVim.instance.options removeObserver:self forKeyPath:@"hlsearch"]; + [XVim.instance.options removeObserver:self forKeyPath:@"ignorecase"]; + [XVim.instance.searcher removeObserver:self forKeyPath:@"lastSearchString"]; + } + @catch (NSException* exception){ + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } + } + [self xvim_dealloc]; +} + + +- (void)xvim_setSelectedRanges:(NSArray *)ranges + affinity:(NSSelectionAffinity)affinity + stillSelecting:(BOOL)flag +{ + [self xvim_setSelectedRanges:ranges affinity:affinity stillSelecting:flag]; + if (self.xvim_view && !XVim.instance.disabled) { + [self xvim_syncStateFromView]; + } +} + +- (void)xvim_selectAll:(id)sender +{ + [self xvim_selectAll:sender]; + if (!XVim.instance.disabled) { + [self.xvim_view.window syncEvaluatorStack]; + } +} + +- (void)xvim_paste:(id)sender +{ + [self xvim_paste:sender]; + if (!XVim.instance.disabled) { + [self.xvim_view.window syncEvaluatorStack]; + } +} + +- (void)xvim_delete:(id)sender +{ + [self xvim_delete:sender]; + if (!XVim.instance.disabled) { + [self.xvim_view.window syncEvaluatorStack]; + } +} + +- (void)xvim_keyDown:(NSEvent *)theEvent +{ + XVimWindow *window = self.xvim_view.window; + + if (!window || XVim.instance.disabled) { + return [self xvim_keyDown:theEvent]; + } + + @try { + TRACE_LOG(@"Event:%@, XVimNotation:%@", theEvent.description, XVimKeyNotationFromXVimString([theEvent toXVimString])); + if ([window handleKeyEvent:theEvent]) { + [self updateInsertionPointStateAndRestartTimer:YES]; + return; + } + // Call Original keyDown: + [self xvim_keyDown:theEvent]; + } + @catch (NSException* exception) { + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } +} + +- (void)xvim_mouseDown:(NSEvent *)theEvent +{ + XVimWindow *window = self.xvim_view.window; + + if (!window || XVim.instance.disabled) { + return [self xvim_mouseDown:theEvent]; + } + + @try { + TRACE_LOG(@"Event:%@", theEvent.description); + + + // When mouse down, NSTextView ( base in this case) takes the control of event loop internally + // and the method call above does not return immidiately and block until mouse up. mouseDragged: method is called from inside it but + // it never calls mouseUp: event. After mouseUp event is handled internally it returns the control. + // So the code here is executed AFTER mouseUp event is handled. + // At this point NSTextView changes its selectedRange so we usually have to sync XVim state. + + // TODO: To make it simple we should forward mouse events + // to handleKeyStroke as a special key stroke + // and the key stroke should be handled by the current evaluator. + + [self xvim_mouseDown:theEvent]; + [window syncEvaluatorStack]; + } + @catch (NSException* exception) { + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } +} + +- (void)xvim_drawRect:(NSRect)dirtyRect +{ + if (XVim.instance.disabled || !self.xvim_view) { + return [self xvim_drawRect:dirtyRect]; + } + + @try { + if (XVim.instance.options.hlsearch) { + XVimMotion *lastSearch = [XVim.instance.searcher motionForRepeatSearch]; + + if (nil != lastSearch.regex) { + [self xvim_updateFoundRanges:lastSearch.regex withOption:lastSearch.option]; + } + } else { + [self xvim_clearHighlightText]; + } + + [self xvim_drawRect:dirtyRect]; + + if (self.selectionMode != XVIM_VISUAL_NONE) { + // NSTextView does not draw insertion point when selecting text. + // We have to draw insertion point by ourselves. + NSRect glyphRect = [self xvim_boundingRectForGlyphIndex:self.insertionPoint]; + [[[self insertionPointColor] colorWithAlphaComponent:0.5] set]; + NSRectFillUsingOperation(glyphRect, NSCompositeSourceOver); + } + } + @catch (NSException* exception) { + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } +} + +// Drawing Caret +- (void)xvim__drawInsertionPointInRect:(NSRect)aRect color:(NSColor*)aColor +{ + XVimWindow *window = self.xvim_view.window; + + if (!window || XVim.instance.disabled) { + return [self xvim__drawInsertionPointInRect:aRect color:aColor]; + } + + @try { + // We do not call original _darawInsertionPointRect here + // Because it uses NSRectFill to draw the caret which overrides the character entirely. + // We want some tranceparency for the caret. + + // Call our drawing method + [window drawInsertionPointInRect:aRect color:aColor]; + + } + @catch (NSException* exception) { + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } +} + +- (void)xvim_drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color turnedOn:(BOOL)flag +{ + [self xvim_drawInsertionPointInRect:rect color:color turnedOn:flag]; + + if (!flag && self.xvim_view && !XVim.instance.disabled) { + // Then tell the view to redraw to clear a caret. + [self setNeedsDisplay:YES]; + } +} + +- (void)xvim_didChangeText +{ + if (self.xvim_view && !XVim.instance.disabled) { + [self setNeedsUpdateFoundRanges:YES]; + } + [self xvim_didChangeText]; +} + +- (void)xvim_viewDidMoveToSuperview +{ + if (!self.xvim_view || XVim.instance.disabled) { + return [self xvim_viewDidMoveToSuperview]; + } + + @try { + // Hide scroll bars according to options + [self xvim_viewDidMoveToSuperview]; + [self.enclosingScrollView setPostsBoundsChangedNotifications:YES]; + } + @catch (NSException* exception) { + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } +} + +- (void)xvim_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary *)change context:(void *)context +{ + if ([@[ @"ignorecase", @"hlsearch", @"lastSearchString"] containsObject:keyPath]) { + [self setNeedsUpdateFoundRanges:YES]; + [self setNeedsDisplayInRect:self.visibleRect avoidAdditionalLayout:YES]; + } +} + +@end + +@implementation XVimView { + NSTextView *__unsafe_unretained _textView; +} +@synthesize window = _window; +@synthesize textView = _textView; + ++ (void)initialize +{ + if (self == [XVimView class]) { + [NSTextView xvim_initialize]; + } +} + +- (instancetype)initWithView:(NSTextView *)view window:(XVimWindow *)window +{ + if ((self = [super init])) { + DEBUG_LOG("View %p created for %@", self, view); + + _textView = view; + _window = [window retain]; + [view xvim_setupForXVimView:self]; + [self _xvim_statusChanged:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_xvim_statusChanged:) + name:XVimEnabledStatusChangedNotification object:nil]; + } + return self; +} + +- (void)dealloc +{ + DEBUG_LOG("View %p deleted", self); + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_window release]; + [super dealloc]; +} + +- (void)_xvim_statusChanged:(id)sender +{ + if (!XVim.instance.disabled) { + [_textView xvim_syncStateFromView]; + } + [_textView setNeedsDisplay:YES]; +} + +@end diff --git a/XVim/XVimVisualEvaluator.m b/XVim/XVimVisualEvaluator.m index 526d415d..a991f0b0 100644 --- a/XVim/XVimVisualEvaluator.m +++ b/XVim/XVimVisualEvaluator.m @@ -55,8 +55,9 @@ - (id)initWithLastVisualStateWithWindow:(XVimWindow *)window{ } - (id)initWithWindow:(XVimWindow *)window mode:(XVIM_VISUAL_MODE)mode { - NSUInteger start = [[[window.sourceView selectedRanges] objectAtIndex:0] rangeValue].location; - NSUInteger end = NSMaxRange([[[window.sourceView selectedRanges] lastObject] rangeValue]); + NSTextView *sourceView = window.currentView.textView; + NSUInteger start = [[sourceView.selectedRanges objectAtIndex:0] rangeValue].location; + NSUInteger end = NSMaxRange([sourceView.selectedRanges.lastObject rangeValue]); XVimBuffer *buffer = window.currentBuffer; if (end > start) { @@ -72,7 +73,7 @@ - (id)initWithWindow:(XVimWindow *)window mode:(XVIM_VISUAL_MODE)mode { _initial.end.column = [buffer columnOfIndex:end]; _initial.colwant = _initial.end.column; - if ([window.sourceView selectedRanges].count > 1) { + if (sourceView.selectedRanges.count > 1) { // Treat it as block selection _initial.mode = XVIM_VISUAL_BLOCK; } diff --git a/XVim/XVimWindow+Xcode.h b/XVim/XVimWindow+Xcode.h deleted file mode 100644 index 651b87cc..00000000 --- a/XVim/XVimWindow+Xcode.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// XVimWindow+Xcode.h -// XVim -// -// Created by Suzuki Shuichiro on 9/18/12. -// -// - -#import "XVimWindow.h" - -@interface XVimWindow (Xcode) - -@end diff --git a/XVim/XVimWindow+Xcode.m b/XVim/XVimWindow+Xcode.m deleted file mode 100644 index 9c89e8a7..00000000 --- a/XVim/XVimWindow+Xcode.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// XVimWindow+Xcode.m -// XVim -// -// Created by Suzuki Shuichiro on 9/18/12. -// -// - -#import "XVimWindow+Xcode.h" - -@implementation XVimWindow (Xcode) - -@end diff --git a/XVim/XVimWindow.h b/XVim/XVimWindow.h index 0c81fbb0..7fd89ad8 100644 --- a/XVim/XVimWindow.h +++ b/XVim/XVimWindow.h @@ -10,6 +10,7 @@ #import "XVimTextViewProtocol.h" #import "XVimKeyStroke.h" #import "XVimBuffer.h" +#import "XVimView.h" /* * This class manages 1 window. (The term "window" here is different from NSWindow) @@ -18,7 +19,6 @@ * the associated XVimWindow object first and it handles the event. */ -@class XVimSourceView; @class XVimEvaluator; @class XVimRegister; @class IDEEditorArea; @@ -27,9 +27,9 @@ @class IDEEditorArea; @interface XVimWindow : NSObject -@property(readonly) NSTextView *sourceView; // This represents currently focused sourceView -@property(readonly) XVimCommandLine *commandLine; -@property(readonly) XVimBuffer *currentBuffer; +@property(nonatomic, readonly) XVimCommandLine *commandLine; +@property(nonatomic, readonly) XVimBuffer *currentBuffer; +@property(nonatomic, readonly) XVimView *currentView; - (instancetype)initWithIDEEditorArea:(IDEEditorArea *)editorArea; diff --git a/XVim/XVimWindow.m b/XVim/XVimWindow.m index be80d4e5..4dff5623 100644 --- a/XVim/XVimWindow.m +++ b/XVim/XVimWindow.m @@ -44,6 +44,7 @@ @implementation XVimWindow - (instancetype)initWithIDEEditorArea:(IDEEditorArea *)editorArea { if (self = [super init]){ + DEBUG_LOG("Window %p created on %@", self, editorArea); _staticString = [@"" retain]; _keymapContext = [[XVimKeymapContext alloc] init]; _editorArea = [editorArea retain]; @@ -61,28 +62,30 @@ - (instancetype)initWithIDEEditorArea:(IDEEditorArea *)editorArea - (NSTextView *)sourceView { IDEEditor *editor = _editorArea.lastActiveEditorContext.editor; - id obj; - obj = _editorArea.workspaceTabController.windowController.window.firstResponder; - if ([obj isKindOfClass:[DVTSourceTextView class]]){ - return obj; + if ([editor isKindOfClass:[IDEComparisonEditor class]]) { + editor = [(id)editor keyEditor]; } - if (_editorArea.editorMode == 2 && [editor isKindOfClass:[IDEComparisonEditor class]]) { - obj = [[(IDEComparisonEditor *)editor keyEditor] mainScrollView].documentView; - return obj; + if ([editor isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { + return [(id)editor textView]; } + return nil; +} - return editor.mainScrollView.documentView; +- (XVimView *)currentView +{ + return self.sourceView.xvim_view; } - (XVimBuffer *)currentBuffer { - return self.sourceView.textStorage.xvim_buffer; + return self.currentView.textView.textStorage.xvim_buffer; } - (void)dealloc { + DEBUG_LOG("Window %p deleted", self); [[NSNotificationCenter defaultCenter] removeObserver:self]; [_keymapContext release]; [_staticString release]; @@ -237,9 +240,11 @@ - (void)handleKeyStroke:(XVimKeyStroke *)keyStroke onStack:(NSMutableArray *)eva [xvim appendOperationKeyStroke:[keyStroke xvimString]]; // Evaluate key stroke - XVimEvaluator* currentEvaluator = [evaluatorStack lastObject]; + XVimEvaluator* currentEvaluator = [[evaluatorStack lastObject] retain]; currentEvaluator.window = self; - XVimEvaluator* nextEvaluator = [currentEvaluator eval:keyStroke]; + + XVimEvaluator* nextEvaluator = [currentEvaluator eval:keyStroke]; + [currentEvaluator release]; // Manipulate evaluator stack while(YES){ From f93d3a692d1b009b3a37f46939c580671cb8f6a5 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Fri, 22 Nov 2013 00:41:22 +0100 Subject: [PATCH 16/44] Get rid of IDEEditorAreaHook and just have a category with swizzling --- XVim.xcodeproj/project.pbxproj | 8 ------ XVim/DVTSourceTextView+XVim.m | 2 +- XVim/IDEEditorArea+XVim.h | 9 +++--- XVim/IDEEditorArea+XVim.m | 47 ++++++++++++++++++++++++-------- XVim/IDEEditorAreaHook.h | 20 -------------- XVim/IDEEditorAreaHook.m | 50 ---------------------------------- XVim/XVimHookManager.m | 4 +-- 7 files changed, 44 insertions(+), 96 deletions(-) delete mode 100644 XVim/IDEEditorAreaHook.h delete mode 100644 XVim/IDEEditorAreaHook.m diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 0147dd8d..aaaa30b9 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -92,7 +92,6 @@ A28F424D17EEDBC200A3F7AE /* XVimHistoryHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = C3552DA7153AC6F800D57577 /* XVimHistoryHandler.m */; }; A28F424E17EEDBC200A3F7AE /* XVimMarkSetEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E8F114154267BA007410ED /* XVimMarkSetEvaluator.m */; }; A28F424F17EEDBC200A3F7AE /* XVimArgumentEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E8F11715427AE6007410ED /* XVimArgumentEvaluator.m */; }; - A28F425017EEDBC200A3F7AE /* IDEEditorAreaHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ADAB821545C18F0093A908 /* IDEEditorAreaHook.m */; }; A28F425117EEDBC200A3F7AE /* XVimStatusLine.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ABFE1215497A3C002220E8 /* XVimStatusLine.m */; }; A28F425217EEDBC200A3F7AE /* XVimHookManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C345DDBA154CE12A009F232E /* XVimHookManager.m */; }; A28F425317EEDBC200A3F7AE /* IDEEditor+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */; }; @@ -133,7 +132,6 @@ A2A736381527484B0051E8E4 /* XVimExCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A736351527484B0051E8E4 /* XVimExCommand.m */; }; A2A8A50014E41C66002EA6C8 /* XVimCommandLine.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A8A4FF14E41C66002EA6C8 /* XVimCommandLine.m */; }; A2ABFE1315497A3C002220E8 /* XVimStatusLine.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ABFE1215497A3C002220E8 /* XVimStatusLine.m */; }; - A2ADAB831545C18F0093A908 /* IDEEditorAreaHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ADAB821545C18F0093A908 /* IDEEditorAreaHook.m */; }; A2AFD7341790F300009B442B /* XVimRecordingEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AFD7331790F2FF009B442B /* XVimRecordingEvaluator.m */; }; A2AFD73717914ACE009B442B /* XVimTester+Recording.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AFD73617914ACE009B442B /* XVimTester+Recording.m */; }; A2BA3EA2152E372A00C18FB4 /* XVimOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = A2BA3EA1152E372A00C18FB4 /* XVimOptions.m */; }; @@ -300,8 +298,6 @@ A2A8A4FF14E41C66002EA6C8 /* XVimCommandLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimCommandLine.m; path = XVim/XVimCommandLine.m; sourceTree = SOURCE_ROOT; }; A2ABFE1115497A3C002220E8 /* XVimStatusLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimStatusLine.h; path = XVim/XVimStatusLine.h; sourceTree = SOURCE_ROOT; }; A2ABFE1215497A3C002220E8 /* XVimStatusLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimStatusLine.m; path = XVim/XVimStatusLine.m; sourceTree = SOURCE_ROOT; }; - A2ADAB811545C18F0093A908 /* IDEEditorAreaHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDEEditorAreaHook.h; path = XVim/IDEEditorAreaHook.h; sourceTree = SOURCE_ROOT; }; - A2ADAB821545C18F0093A908 /* IDEEditorAreaHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IDEEditorAreaHook.m; path = XVim/IDEEditorAreaHook.m; sourceTree = SOURCE_ROOT; }; A2AFD7321790F2FD009B442B /* XVimRecordingEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimRecordingEvaluator.h; path = XVim/XVimRecordingEvaluator.h; sourceTree = SOURCE_ROOT; }; A2AFD7331790F2FF009B442B /* XVimRecordingEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimRecordingEvaluator.m; path = XVim/XVimRecordingEvaluator.m; sourceTree = SOURCE_ROOT; }; A2AFD73617914ACE009B442B /* XVimTester+Recording.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XVimTester+Recording.m"; path = "XVim/XVimTester+Recording.m"; sourceTree = SOURCE_ROOT; }; @@ -782,8 +778,6 @@ A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */, C38A5B4515272DCC00E1448D /* IDESourceCodeEditorHook.h */, C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */, - A2ADAB811545C18F0093A908 /* IDEEditorAreaHook.h */, - A2ADAB821545C18F0093A908 /* IDEEditorAreaHook.m */, A26ACC4C154F2D6600B27D69 /* IDEEditor+XVim.h */, A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */, A2702B0016FE537D005ADD76 /* IDEWorkspaceWindowHook.h */, @@ -932,7 +926,6 @@ A28F424D17EEDBC200A3F7AE /* XVimHistoryHandler.m in Sources */, A28F424E17EEDBC200A3F7AE /* XVimMarkSetEvaluator.m in Sources */, A28F424F17EEDBC200A3F7AE /* XVimArgumentEvaluator.m in Sources */, - A28F425017EEDBC200A3F7AE /* IDEEditorAreaHook.m in Sources */, A28F425117EEDBC200A3F7AE /* XVimStatusLine.m in Sources */, A28F425217EEDBC200A3F7AE /* XVimHookManager.m in Sources */, 6E2B332D1836E42F00EFE4E2 /* XVimBuffer.m in Sources */, @@ -1016,7 +1009,6 @@ C3552DA8153AC6F800D57577 /* XVimHistoryHandler.m in Sources */, C3E8F115154267BA007410ED /* XVimMarkSetEvaluator.m in Sources */, C3E8F11815427AE6007410ED /* XVimArgumentEvaluator.m in Sources */, - A2ADAB831545C18F0093A908 /* IDEEditorAreaHook.m in Sources */, A2ABFE1315497A3C002220E8 /* XVimStatusLine.m in Sources */, C345DDBB154CE12A009F232E /* XVimHookManager.m in Sources */, 6E2B332C1836E42F00EFE4E2 /* XVimBuffer.m in Sources */, diff --git a/XVim/DVTSourceTextView+XVim.m b/XVim/DVTSourceTextView+XVim.m index 0eeffcdd..48f982c0 100644 --- a/XVim/DVTSourceTextView+XVim.m +++ b/XVim/DVTSourceTextView+XVim.m @@ -17,6 +17,6 @@ - (IDEEditorArea*)editorArea{ } - (XVimWindow*)xvimWindow{ - return [[self editorArea] xvimWindow]; + return [[self editorArea] xvim_window]; } @end diff --git a/XVim/IDEEditorArea+XVim.h b/XVim/IDEEditorArea+XVim.h index 801b162f..bd7861e6 100644 --- a/XVim/IDEEditorArea+XVim.h +++ b/XVim/IDEEditorArea+XVim.h @@ -7,7 +7,8 @@ // #import "IDEKit.h" -#import "XVimCommandLine.h" + +@class XVimWindow; /* * This is the extension of IDEEditorArea class in Xcode. @@ -15,9 +16,9 @@ * See IDEKit.h to refer original IDEEditorArea class. */ @interface IDEEditorArea (XVim) -@property (readonly, strong) XVimWindow *xvimWindow; -- (void)setupXVim; -- (void)teardownXVim; +@property (nonatomic, readonly) XVimWindow *xvim_window; + ++ (void)xvim_initialize; @end diff --git a/XVim/IDEEditorArea+XVim.m b/XVim/IDEEditorArea+XVim.m index 4bf59270..a99a6402 100644 --- a/XVim/IDEEditorArea+XVim.m +++ b/XVim/IDEEditorArea+XVim.m @@ -8,18 +8,39 @@ #import #import "IDEEditorArea+XVim.h" +#import "NSObject+XVimAdditions.h" #import "XVimWindow.h" -static const char *KEY_WINDOW = "xvimwindow"; - +static const char *KEY_WINDOW = "xvimwindow"; + +/** + * IDEEditorArea is a area including primary editor and assistant editor and debug area (The view right of the navigator) + * This class hooks IDEEditorArea and does some works. + * "viewDidInstall" is called when the view setup is done ( as far as I saw the behaviour ). + * This class has private instance variable named "_editorAreaAutoLayoutView" which is the view + * contains source code editores and border view between editors and debug area. + * We insert command line view between editors and debug area. + * + * IDEEdiatorArea exists in every Xcode tabs so if you have 4 tabs in a Xcode window there are 4 command line and XVimWindow views we insert. + */ @implementation IDEEditorArea (XVim) -- (XVimWindow *)xvimWindow ++ (void)xvim_initialize +{ + if (self == [IDEEditorArea class]) { + [self xvim_swizzleInstanceMethod:@selector(viewDidInstall) + with:@selector(xvim_viewDidInstall)]; + [self xvim_swizzleInstanceMethod:@selector(primitiveInvalidate) + with:@selector(xvim_primitiveInvalidate)]; + } +} + +- (XVimWindow *)xvim_window { return objc_getAssociatedObject(self, KEY_WINDOW); } -- (NSView *)textViewArea +- (NSView *)_xvim_editorAreaAutoLayoutView { NSView *layoutView; @@ -28,7 +49,7 @@ - (NSView *)textViewArea return layoutView; } -- (DVTBorderedView *)debuggerBarBorderedView +- (DVTBorderedView *)_xvim_debuggerBarBorderedView { DVTBorderedView *border; @@ -37,10 +58,12 @@ - (DVTBorderedView *)debuggerBarBorderedView return border; } -- (void)setupXVim +- (void)xvim_viewDidInstall { + [self xvim_viewDidInstall]; + XVimWindow *xvim = [[XVimWindow alloc] initWithIDEEditorArea:self]; - NSView *layoutView = [self textViewArea]; + NSView *layoutView = [self _xvim_editorAreaAutoLayoutView]; XVimCommandLine *cmd = xvim.commandLine; [layoutView addSubview:cmd]; @@ -51,7 +74,7 @@ - (void)setupXVim name:NSViewFrameDidChangeNotification object:layoutView]; if (layoutView.subviews.count > 0) { - DVTBorderedView *border = [self debuggerBarBorderedView]; + DVTBorderedView *border = [self _xvim_debuggerBarBorderedView]; // We need to know if border view is hidden or not to place editors and command line correctly. [border addObserver:cmd forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew context:nil]; @@ -61,13 +84,15 @@ - (void)setupXVim [xvim release]; } -- (void)teardownXVim +- (void)xvim_primitiveInvalidate { - XVimCommandLine *cmd = self.xvimWindow.commandLine; - DVTBorderedView *border = [self debuggerBarBorderedView]; + XVimCommandLine *cmd = self.xvim_window.commandLine; + DVTBorderedView *border = [self _xvim_debuggerBarBorderedView]; [border removeObserver:cmd forKeyPath:@"hidden"]; [[NSNotificationCenter defaultCenter] removeObserver:cmd]; + + [self xvim_primitiveInvalidate]; } @end diff --git a/XVim/IDEEditorAreaHook.h b/XVim/IDEEditorAreaHook.h deleted file mode 100644 index 57c40366..00000000 --- a/XVim/IDEEditorAreaHook.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// XVimEditorArea.h -// XVim -// -// Created by Shuichiro Suzuki on 4/23/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import -#import "IDEKit.h" - -@interface IDEEditorAreaHook : NSObject -+ (void)hook; -@end - - -@interface IDEEditorArea(Hook) -- (void)viewDidInstall_; -- (void)primitiveInvalidate_; -@end \ No newline at end of file diff --git a/XVim/IDEEditorAreaHook.m b/XVim/IDEEditorAreaHook.m deleted file mode 100644 index aaabc93c..00000000 --- a/XVim/IDEEditorAreaHook.m +++ /dev/null @@ -1,50 +0,0 @@ -// -// XVimEditorArea.m -// XVim -// -// Created by Shuichiro Suzuki on 4/23/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "IDEEditorAreaHook.h" -#import "Hooker.h" -#import "DVTKit.h" -#import "IDEEditorArea+XVim.h" - -@implementation IDEEditorAreaHook -/** - * IDEEditorArea is a area including primary editor and assistant editor and debug area (The view right of the navigator) - * This class hooks IDEEditorArea and does some works. - * "viewDidInstall" is called when the view setup is done ( as far as I saw the behaviour ). - * This class has private instance variable named "_editorAreaAutoLayoutView" which is the view - * contains source code editores and border view between editors and debug area. - * We insert command line view between editors and debug area. - * - * IDEEdiatorArea exists in every Xcode tabs so if you have 4 tabs in a Xcode window there are 4 command line and XVimWindow views we insert. - **/ - -+(void)hook -{ - Class c = NSClassFromString(@"IDEEditorArea"); - - [Hooker hookMethod:@selector(viewDidInstall) ofClass:c withMethod:class_getInstanceMethod([self class], @selector(viewDidInstall) ) keepingOriginalWith:@selector(viewDidInstall_)]; - [Hooker hookMethod:@selector(primitiveInvalidate) ofClass:c withMethod:class_getInstanceMethod([self class], @selector(primitiveInvalidate)) keepingOriginalWith:@selector(primitiveInvalidate_)]; -} - -- (void)viewDidInstall -{ - IDEEditorArea *base = (IDEEditorArea *)self; - - [base viewDidInstall_]; - [base setupXVim]; -} - -- (void)primitiveInvalidate -{ - IDEEditorArea *base = (IDEEditorArea*)self; - - [base teardownXVim]; - [base primitiveInvalidate_]; -} - -@end diff --git a/XVim/XVimHookManager.m b/XVim/XVimHookManager.m index 9460cbcc..55bdf9f8 100644 --- a/XVim/XVimHookManager.m +++ b/XVim/XVimHookManager.m @@ -7,9 +7,9 @@ // #import "XVimHookManager.h" -#import "IDEEditorAreaHook.h" #import "IDESourceCodeEditorHook.h" #import "IDEEditor+XVim.h" +#import "IDEEditorArea+XVim.h" #import "IDEWorkspaceWindowHook.h" #import "DVTSourceTextScrollViewHook.h" #import "XVimView.h" @@ -18,7 +18,7 @@ @implementation XVimHookManager + (void)hookWhenPluginLoaded { - [IDEEditorAreaHook hook]; + [IDEEditorArea xvim_initialize]; [IDEWorkspaceWindowHook hook]; [DVTSourceTextScrollViewHook hook]; [IDESourceCodeEditorHook hook]; From b7b911cf4eadd5184ad2c3a7d6120645c5629b68 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Fri, 22 Nov 2013 00:48:07 +0100 Subject: [PATCH 17/44] Remove now completely useless Hook --- XVim.xcodeproj/project.pbxproj | 8 -------- XVim/IDESourceCodeEditorHook.h | 17 ----------------- XVim/IDESourceCodeEditorHook.m | 32 -------------------------------- XVim/XVimHookManager.m | 2 -- 4 files changed, 59 deletions(-) delete mode 100644 XVim/IDESourceCodeEditorHook.h delete mode 100644 XVim/IDESourceCodeEditorHook.m diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index aaaa30b9..05049290 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -73,7 +73,6 @@ A28F423917EEDBC200A3F7AE /* XVimRegisterEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = F100DC2D150BB6BC002C703C /* XVimRegisterEvaluator.m */; }; A28F423A17EEDBC200A3F7AE /* XVimOperatorEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A222B5E01514DFCD005E8802 /* XVimOperatorEvaluator.m */; }; A28F423B17EEDBC200A3F7AE /* XVimKeyStroke.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */; }; - A28F423D17EEDBC200A3F7AE /* IDESourceCodeEditorHook.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */; }; A28F423E17EEDBC200A3F7AE /* XVimNumericEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4C1527CEA400E1448D /* XVimNumericEvaluator.m */; }; A28F423F17EEDBC200A3F7AE /* XVimKeymap.m in Sources */ = {isa = PBXBuildFile; fileRef = C366C23A15287EE30008C58E /* XVimKeymap.m */; }; A28F424017EEDBC200A3F7AE /* XVimExCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A736351527484B0051E8E4 /* XVimExCommand.m */; }; @@ -165,7 +164,6 @@ C366C23B15287EE30008C58E /* XVimKeymap.m in Sources */ = {isa = PBXBuildFile; fileRef = C366C23A15287EE30008C58E /* XVimKeymap.m */; }; C36C104E153131C600CE1D62 /* XVimTextObjectEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */; }; C38A5B301526C6B100E1448D /* XVimKeyStroke.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */; }; - C38A5B4715272DCD00E1448D /* IDESourceCodeEditorHook.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */; }; C38A5B4D1527CEA500E1448D /* XVimNumericEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4C1527CEA400E1448D /* XVimNumericEvaluator.m */; }; C3AA7226152F186A00C61D97 /* XVimTildeEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7225152F186A00C61D97 /* XVimTildeEvaluator.m */; }; C3AA722A152F1B1800C61D97 /* XVimLowercaseEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7229152F1B1700C61D97 /* XVimLowercaseEvaluator.m */; }; @@ -359,8 +357,6 @@ C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimTextObjectEvaluator.m; path = XVim/XVimTextObjectEvaluator.m; sourceTree = SOURCE_ROOT; }; C38A5B2E1526C6B100E1448D /* XVimKeyStroke.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimKeyStroke.h; path = XVim/XVimKeyStroke.h; sourceTree = SOURCE_ROOT; }; C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimKeyStroke.m; path = XVim/XVimKeyStroke.m; sourceTree = SOURCE_ROOT; }; - C38A5B4515272DCC00E1448D /* IDESourceCodeEditorHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDESourceCodeEditorHook.h; path = XVim/IDESourceCodeEditorHook.h; sourceTree = SOURCE_ROOT; }; - C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IDESourceCodeEditorHook.m; path = XVim/IDESourceCodeEditorHook.m; sourceTree = SOURCE_ROOT; }; C38A5B4B1527CEA400E1448D /* XVimNumericEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimNumericEvaluator.h; path = XVim/XVimNumericEvaluator.h; sourceTree = SOURCE_ROOT; }; C38A5B4C1527CEA400E1448D /* XVimNumericEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimNumericEvaluator.m; path = XVim/XVimNumericEvaluator.m; sourceTree = SOURCE_ROOT; }; C3AA7224152F186A00C61D97 /* XVimTildeEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTildeEvaluator.h; path = XVim/XVimTildeEvaluator.h; sourceTree = SOURCE_ROOT; }; @@ -776,8 +772,6 @@ children = ( A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.h */, A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */, - C38A5B4515272DCC00E1448D /* IDESourceCodeEditorHook.h */, - C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */, A26ACC4C154F2D6600B27D69 /* IDEEditor+XVim.h */, A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */, A2702B0016FE537D005ADD76 /* IDEWorkspaceWindowHook.h */, @@ -906,7 +900,6 @@ A28F423917EEDBC200A3F7AE /* XVimRegisterEvaluator.m in Sources */, A28F423A17EEDBC200A3F7AE /* XVimOperatorEvaluator.m in Sources */, A28F423B17EEDBC200A3F7AE /* XVimKeyStroke.m in Sources */, - A28F423D17EEDBC200A3F7AE /* IDESourceCodeEditorHook.m in Sources */, A28F423E17EEDBC200A3F7AE /* XVimNumericEvaluator.m in Sources */, A28F423F17EEDBC200A3F7AE /* XVimKeymap.m in Sources */, A28F424017EEDBC200A3F7AE /* XVimExCommand.m in Sources */, @@ -990,7 +983,6 @@ F100DC2E150BB6BC002C703C /* XVimRegisterEvaluator.m in Sources */, A222B5E11514DFCD005E8802 /* XVimOperatorEvaluator.m in Sources */, C38A5B301526C6B100E1448D /* XVimKeyStroke.m in Sources */, - C38A5B4715272DCD00E1448D /* IDESourceCodeEditorHook.m in Sources */, C38A5B4D1527CEA500E1448D /* XVimNumericEvaluator.m in Sources */, C366C23B15287EE30008C58E /* XVimKeymap.m in Sources */, A2A736381527484B0051E8E4 /* XVimExCommand.m in Sources */, diff --git a/XVim/IDESourceCodeEditorHook.h b/XVim/IDESourceCodeEditorHook.h deleted file mode 100644 index 16d1f5c0..00000000 --- a/XVim/IDESourceCodeEditorHook.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// XVimSourceCodeEditor.h -// XVim -// -// Created by Tomas Lundell on 31/03/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import -#import "IDESourceEditor.h" -@interface IDESourceCodeEditorHook : NSObject -+ (void)hook; -@end - -@interface IDESourceCodeEditor(Hook) -- (id)initWithNibName_:nibName bundle:nibBundle document:nibDocument; -@end \ No newline at end of file diff --git a/XVim/IDESourceCodeEditorHook.m b/XVim/IDESourceCodeEditorHook.m deleted file mode 100644 index 09f7fe26..00000000 --- a/XVim/IDESourceCodeEditorHook.m +++ /dev/null @@ -1,32 +0,0 @@ -// -// XVimSourceCodeEditor.m -// XVim -// -// Created by Tomas Lundell on 31/03/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "IDESourceCodeEditorHook.h" -#import "IDEKit.h" -#import "XVimWindow.h" -#import "Hooker.h" -#import "Logger.h" -#import "XVimStatusLine.h" -#import "XVim.h" - -@implementation IDESourceCodeEditorHook - -+ (void) hook -{ - Class delegate = NSClassFromString(@"IDESourceCodeEditor"); - [Hooker hookMethod:@selector(textView:willChangeSelectionFromCharacterRanges:toCharacterRanges:) - ofClass:delegate - withMethod:class_getInstanceMethod([self class], @selector(textView:willChangeSelectionFromCharacterRanges:toCharacterRanges:)) - keepingOriginalWith:@selector(textView_:willChangeSelectionFromCharacterRanges:toCharacterRanges:)]; -} - -- (NSArray*) textView:(NSTextView *)textView willChangeSelectionFromCharacterRanges:(NSArray *)oldSelectedCharRanges toCharacterRanges:(NSArray *)newSelectedCharRanges -{ - return newSelectedCharRanges; -} -@end \ No newline at end of file diff --git a/XVim/XVimHookManager.m b/XVim/XVimHookManager.m index 55bdf9f8..868ca12a 100644 --- a/XVim/XVimHookManager.m +++ b/XVim/XVimHookManager.m @@ -7,7 +7,6 @@ // #import "XVimHookManager.h" -#import "IDESourceCodeEditorHook.h" #import "IDEEditor+XVim.h" #import "IDEEditorArea+XVim.h" #import "IDEWorkspaceWindowHook.h" @@ -21,7 +20,6 @@ + (void)hookWhenPluginLoaded [IDEEditorArea xvim_initialize]; [IDEWorkspaceWindowHook hook]; [DVTSourceTextScrollViewHook hook]; - [IDESourceCodeEditorHook hook]; [IDEEditor xvim_initialize]; [IDEComparisonEditor xvim_initialize]; [XVimView class]; From 3afd25bf7f77991a2497c11071e43131fe747025 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Fri, 22 Nov 2013 00:57:17 +0100 Subject: [PATCH 18/44] Move the IDEWorkspaceWindowHook as a swizzle in XVimHookManager It's disabled by default because it only make sense for debugging purposes. --- XVim.xcodeproj/project.pbxproj | 8 -------- XVim/IDEWorkspaceWindowHook.h | 17 ----------------- XVim/IDEWorkspaceWindowHook.m | 27 --------------------------- XVim/XVimHookManager.m | 31 ++++++++++++++++++++++++++++--- 4 files changed, 28 insertions(+), 55 deletions(-) delete mode 100644 XVim/IDEWorkspaceWindowHook.h delete mode 100644 XVim/IDEWorkspaceWindowHook.m diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 05049290..9fb745af 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -47,7 +47,6 @@ A26ACC4E154F2D6800B27D69 /* IDEEditor+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */; }; A26DC88615DF33C600779CB4 /* XVimTester.m in Sources */ = {isa = PBXBuildFile; fileRef = A26DC88515DF33C600779CB4 /* XVimTester.m */; }; A26E5F6417A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */; }; - A2702B0216FE537D005ADD76 /* IDEWorkspaceWindowHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0116FE537D005ADD76 /* IDEWorkspaceWindowHook.m */; }; A2702B0B16FEA313005ADD76 /* NSObject+ExtraData.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */; }; A2771E6B179EF520003B621E /* XVimTester+Issues.m in Sources */ = {isa = PBXBuildFile; fileRef = A2771E6A179EF520003B621E /* XVimTester+Issues.m */; }; A28D42F514FE87AF004BC121 /* XVimGMotionEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A28D42F414FE87AF004BC121 /* XVimGMotionEvaluator.m */; }; @@ -104,7 +103,6 @@ A28F425B17EEDBC200A3F7AE /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C1BB5216CEAC7F0066F420 /* Utils.m */; }; A28F425C17EEDBC200A3F7AE /* XVimMark.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7416FB30B0000BE21C /* XVimMark.m */; }; A28F425D17EEDBC200A3F7AE /* XVimMarks.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7716FB4806000BE21C /* XVimMarks.m */; }; - A28F425E17EEDBC200A3F7AE /* IDEWorkspaceWindowHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0116FE537D005ADD76 /* IDEWorkspaceWindowHook.m */; }; A28F425F17EEDBC200A3F7AE /* NSObject+ExtraData.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */; }; A28F426017EEDBC200A3F7AE /* XVimUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = A22A009F1708914D0003046C /* XVimUtil.m */; }; A28F426117EEDBC200A3F7AE /* XVimTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = A22A00A21709CBE50003046C /* XVimTestCase.m */; }; @@ -275,8 +273,6 @@ A26DC88515DF33C600779CB4 /* XVimTester.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimTester.m; path = XVim/XVimTester.m; sourceTree = SOURCE_ROOT; }; A26E5F6217A6BCAF0087C9B6 /* NSTextStorage+VimOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSTextStorage+VimOperation.h"; path = "XVim/NSTextStorage+VimOperation.h"; sourceTree = SOURCE_ROOT; }; A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSTextStorage+VimOperation.m"; path = "XVim/NSTextStorage+VimOperation.m"; sourceTree = SOURCE_ROOT; }; - A2702B0016FE537D005ADD76 /* IDEWorkspaceWindowHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDEWorkspaceWindowHook.h; path = XVim/IDEWorkspaceWindowHook.h; sourceTree = SOURCE_ROOT; }; - A2702B0116FE537D005ADD76 /* IDEWorkspaceWindowHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IDEWorkspaceWindowHook.m; path = XVim/IDEWorkspaceWindowHook.m; sourceTree = SOURCE_ROOT; }; A2702B0916FEA313005ADD76 /* NSObject+ExtraData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+ExtraData.h"; path = "XVim/NSObject+ExtraData.h"; sourceTree = ""; }; A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+ExtraData.m"; path = "XVim/NSObject+ExtraData.m"; sourceTree = ""; }; A2771E6A179EF520003B621E /* XVimTester+Issues.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XVimTester+Issues.m"; path = "XVim/Test/XVimTester+Issues.m"; sourceTree = SOURCE_ROOT; }; @@ -774,8 +770,6 @@ A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */, A26ACC4C154F2D6600B27D69 /* IDEEditor+XVim.h */, A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */, - A2702B0016FE537D005ADD76 /* IDEWorkspaceWindowHook.h */, - A2702B0116FE537D005ADD76 /* IDEWorkspaceWindowHook.m */, C345DDB9154CE12A009F232E /* XVimHookManager.h */, C345DDBA154CE12A009F232E /* XVimHookManager.m */, ); @@ -935,7 +929,6 @@ A28F425C17EEDBC200A3F7AE /* XVimMark.m in Sources */, A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */, A28F425D17EEDBC200A3F7AE /* XVimMarks.m in Sources */, - A28F425E17EEDBC200A3F7AE /* IDEWorkspaceWindowHook.m in Sources */, A28F425F17EEDBC200A3F7AE /* NSObject+ExtraData.m in Sources */, A28F426017EEDBC200A3F7AE /* XVimUtil.m in Sources */, A28F426117EEDBC200A3F7AE /* XVimTestCase.m in Sources */, @@ -1016,7 +1009,6 @@ A2126B7516FB30B0000BE21C /* XVimMark.m in Sources */, A2F2621B182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */, A2126B7816FB4806000BE21C /* XVimMarks.m in Sources */, - A2702B0216FE537D005ADD76 /* IDEWorkspaceWindowHook.m in Sources */, A2702B0B16FEA313005ADD76 /* NSObject+ExtraData.m in Sources */, 6EF220D6183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */, A22A00A01708914E0003046C /* XVimUtil.m in Sources */, diff --git a/XVim/IDEWorkspaceWindowHook.h b/XVim/IDEWorkspaceWindowHook.h deleted file mode 100644 index 15cf15ef..00000000 --- a/XVim/IDEWorkspaceWindowHook.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// IDEWorkspaceWindowHook.h -// XVim -// -// Created by Suzuki Shuichiro on 3/24/13. -// -// - -#import - -@interface IDEWorkspaceWindowHook : NSObject -+(void)hook; -@end - -@interface IDEWorkspaceWindow(hook) -- (void)sendEvent_:(NSEvent*)event; -@end \ No newline at end of file diff --git a/XVim/IDEWorkspaceWindowHook.m b/XVim/IDEWorkspaceWindowHook.m deleted file mode 100644 index 8ed96ca0..00000000 --- a/XVim/IDEWorkspaceWindowHook.m +++ /dev/null @@ -1,27 +0,0 @@ -// -// IDEWorkspaceWindowHook.m -// XVim -// -// Created by Suzuki Shuichiro on 3/24/13. -// -// - -#import "IDEKit.h" -#import "IDEWorkspaceWindowHook.h" -#import "Hooker.h" -#import "Logger.h" -#import "NSEvent+VimHelper.h" - -@implementation IDEWorkspaceWindowHook -+(void)hook{ - [Hooker hookClass:@"IDEWorkspaceWindow" method:@"sendEvent:" byClass:@"IDEWorkspaceWindowHook" method:@"sendEvent:"]; -} - --(void)sendEvent:(NSEvent*)event{ - IDEWorkspaceWindow* base = (IDEWorkspaceWindow*)self; - if( event.type == NSKeyDown ){ - TRACE_LOG(@"Window:%p keyCode:%d characters:%@ charsIgnoreMod:%@ cASCII:%d", self, event.keyCode, event.characters, event.charactersIgnoringModifiers, event.unmodifiedKeyCode); - } - [base sendEvent_:event]; -} -@end diff --git a/XVim/XVimHookManager.m b/XVim/XVimHookManager.m index 868ca12a..d9dbec85 100644 --- a/XVim/XVimHookManager.m +++ b/XVim/XVimHookManager.m @@ -6,19 +6,44 @@ // Copyright (c) 2012 __MyCompanyName__. All rights reserved. // +#import "Logger.h" +#import "XVimView.h" #import "XVimHookManager.h" #import "IDEEditor+XVim.h" #import "IDEEditorArea+XVim.h" -#import "IDEWorkspaceWindowHook.h" #import "DVTSourceTextScrollViewHook.h" -#import "XVimView.h" +#import "NSEvent+VimHelper.h" +#import "NSObject+XVimAdditions.h" + +@implementation IDEWorkspaceWindow (XVim) + ++ (void)xvim_initialize +{ +#if 0 // Only useful for debugging purposes + if (self == [IDEWorkspaceWindow class]) { + [self xvim_swizzleInstanceMethod:@selector(sendEvent:) + with:@selector(xvim_sendEvent:)]; + } +#endif +} + +- (void)xvim_sendEvent:(NSEvent *)event +{ + if (event.type == NSKeyDown) { + TRACE_LOG(@"Window:%p keyCode:%d characters:%@ charsIgnoreMod:%@ cASCII:%d", + self, event.keyCode, event.characters, event.charactersIgnoringModifiers, event.unmodifiedKeyCode); + } + [self xvim_sendEvent:event]; +} + +@end @implementation XVimHookManager + (void)hookWhenPluginLoaded { + [IDEWorkspaceWindow xvim_initialize]; [IDEEditorArea xvim_initialize]; - [IDEWorkspaceWindowHook hook]; [DVTSourceTextScrollViewHook hook]; [IDEEditor xvim_initialize]; [IDEComparisonEditor xvim_initialize]; From e73d9d55fef0587c447b06b65fad73b52a8bf47c Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Fri, 22 Nov 2013 01:03:53 +0100 Subject: [PATCH 19/44] wibble --- XVim.xcodeproj/project.pbxproj | 4 ++-- XVim/DVTSourceTextView+XVim.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 9fb745af..1c5a737f 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -753,6 +753,8 @@ A24782BA14D6F56E003B6433 /* XVimCommandField.m */, A2A8A4FE14E41C66002EA6C8 /* XVimCommandLine.h */, A2A8A4FF14E41C66002EA6C8 /* XVimCommandLine.m */, + A26ACC4C154F2D6600B27D69 /* IDEEditor+XVim.h */, + A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */, A216F39F156560FE00AD2529 /* IDEEditorArea+XVim.h */, A216F3A0156560FE00AD2529 /* IDEEditorArea+XVim.m */, A257C28D156567250098CA09 /* DVTSourceTextView+XVim.h */, @@ -768,8 +770,6 @@ children = ( A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.h */, A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */, - A26ACC4C154F2D6600B27D69 /* IDEEditor+XVim.h */, - A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */, C345DDB9154CE12A009F232E /* XVimHookManager.h */, C345DDBA154CE12A009F232E /* XVimHookManager.m */, ); diff --git a/XVim/DVTSourceTextView+XVim.h b/XVim/DVTSourceTextView+XVim.h index 7e400566..c48dbded 100644 --- a/XVim/DVTSourceTextView+XVim.h +++ b/XVim/DVTSourceTextView+XVim.h @@ -13,6 +13,5 @@ #import "DVTKit.h" @interface DVTSourceTextView (XVim) -- (IDEEditorArea*)editorArea; - (XVimWindow*)xvimWindow; @end From e234ef00604893ffd23f7e423a8394163ada56c9 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Fri, 22 Nov 2013 08:49:02 +0100 Subject: [PATCH 20/44] Get rid of Hooker, move DVTSourceTextScrollView to a swizzling class too And fix a silly bug with the swizzling category when the class doesn't override the selector we're swizzling. --- XVim.xcodeproj/project.pbxproj | 40 +++-------- XVim/DVTSourceTextScrollView+XVim.h | 14 ++++ XVim/DVTSourceTextScrollView+XVim.m | 97 +++++++++++++++++++++++++ XVim/DVTSourceTextScrollViewHook.h | 22 ------ XVim/DVTSourceTextScrollViewHook.m | 107 ---------------------------- XVim/Hooker.h | 41 ----------- XVim/Hooker.m | 79 -------------------- XVim/IDEEditor+XVim.m | 1 - XVim/Logger.m | 1 - XVim/NSObject+XVimAdditions.m | 4 +- XVim/XVim.m | 3 +- XVim/XVimHookManager.h | 13 ---- XVim/XVimHookManager.m | 53 -------------- XVim/XVimWindow.m | 43 ++++++++++- 14 files changed, 163 insertions(+), 355 deletions(-) create mode 100644 XVim/DVTSourceTextScrollView+XVim.h create mode 100644 XVim/DVTSourceTextScrollView+XVim.m delete mode 100644 XVim/DVTSourceTextScrollViewHook.h delete mode 100644 XVim/DVTSourceTextScrollViewHook.m delete mode 100644 XVim/Hooker.h delete mode 100644 XVim/Hooker.m delete mode 100644 XVim/XVimHookManager.h delete mode 100644 XVim/XVimHookManager.m diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 1c5a737f..1698de33 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -56,7 +56,6 @@ A28F422917EEDBC200A3F7AE /* XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A24782B814D6F56E003B6433 /* XVim.m */; }; A28F422A17EEDBC200A3F7AE /* XVimCommandField.m in Sources */ = {isa = PBXBuildFile; fileRef = A24782BA14D6F56E003B6433 /* XVimCommandField.m */; }; A28F422B17EEDBC200A3F7AE /* XVimCommandLine.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A8A4FF14E41C66002EA6C8 /* XVimCommandLine.m */; }; - A28F422C17EEDBC200A3F7AE /* Hooker.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C4E79714E7F35300751199 /* Hooker.m */; }; A28F422D17EEDBC200A3F7AE /* XVimEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C4E79B14E7F36B00751199 /* XVimEvaluator.m */; }; A28F422E17EEDBC200A3F7AE /* XVimNormalEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F4A33D14F00B25006DA5A5 /* XVimNormalEvaluator.m */; }; A28F422F17EEDBC200A3F7AE /* XVimVisualEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F4A34214F00BD3006DA5A5 /* XVimVisualEvaluator.m */; }; @@ -91,7 +90,6 @@ A28F424E17EEDBC200A3F7AE /* XVimMarkSetEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E8F114154267BA007410ED /* XVimMarkSetEvaluator.m */; }; A28F424F17EEDBC200A3F7AE /* XVimArgumentEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E8F11715427AE6007410ED /* XVimArgumentEvaluator.m */; }; A28F425117EEDBC200A3F7AE /* XVimStatusLine.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ABFE1215497A3C002220E8 /* XVimStatusLine.m */; }; - A28F425217EEDBC200A3F7AE /* XVimHookManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C345DDBA154CE12A009F232E /* XVimHookManager.m */; }; A28F425317EEDBC200A3F7AE /* IDEEditor+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */; }; A28F425417EEDBC200A3F7AE /* NSInsetTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = C30886591554C8380024161D /* NSInsetTextView.m */; }; A28F425517EEDBC200A3F7AE /* IDEEditorArea+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A216F3A0156560FE00AD2529 /* IDEEditorArea+XVim.m */; }; @@ -133,7 +131,6 @@ A2AFD73717914ACE009B442B /* XVimTester+Recording.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AFD73617914ACE009B442B /* XVimTester+Recording.m */; }; A2BA3EA2152E372A00C18FB4 /* XVimOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = A2BA3EA1152E372A00C18FB4 /* XVimOptions.m */; }; A2C1BB5316CEAC7F0066F420 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C1BB5216CEAC7F0066F420 /* Utils.m */; }; - A2C4E79814E7F35300751199 /* Hooker.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C4E79714E7F35300751199 /* Hooker.m */; }; A2C4E79C14E7F36B00751199 /* XVimEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C4E79B14E7F36B00751199 /* XVimEvaluator.m */; }; A2D48B1F15347B5C0088AB71 /* NSString+VimHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = A2D48B1E15347B5C0088AB71 /* NSString+VimHelper.m */; }; A2E7E45717F03113008F045A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2B4BABB14D59F6600D817B0 /* Cocoa.framework */; }; @@ -147,15 +144,14 @@ A2E7E46417F0382E008F045A /* IDEKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45817F037EF008F045A /* IDEKit.framework */; }; A2E7E46517F03835008F045A /* DVTFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45E17F03802008F045A /* DVTFoundation.framework */; }; A2E7E46617F03835008F045A /* DVTKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45C17F037FF008F045A /* DVTKit.framework */; }; - A2F2621B182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */; }; - A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */; }; + A2F2621B182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m */; }; + A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m */; }; A2F4A33E14F00B25006DA5A5 /* XVimNormalEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F4A33D14F00B25006DA5A5 /* XVimNormalEvaluator.m */; }; A2F4A34314F00BD3006DA5A5 /* XVimVisualEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F4A34214F00BD3006DA5A5 /* XVimVisualEvaluator.m */; }; A2FF17D11502B292003FE648 /* XVimMotionEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2FF17D01502B291003FE648 /* XVimMotionEvaluator.m */; }; BF07A94215EBF404006E6984 /* NSEvent+VimHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = BF050D7A15EBF3E100CD0D12 /* NSEvent+VimHelper.m */; }; C308865A1554C8380024161D /* NSInsetTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = C30886591554C8380024161D /* NSInsetTextView.m */; }; C324BE0115390E8500C13558 /* XVimGVisualEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C324BE0015390E8500C13558 /* XVimGVisualEvaluator.m */; }; - C345DDBB154CE12A009F232E /* XVimHookManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C345DDBA154CE12A009F232E /* XVimHookManager.m */; }; C3552D9B153948D200D57577 /* XVimCommandLineEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3552D9A153948D200D57577 /* XVimCommandLineEvaluator.m */; }; C3552DA8153AC6F800D57577 /* XVimHistoryHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = C3552DA7153AC6F800D57577 /* XVimHistoryHandler.m */; }; C361DE681538F18400037B4B /* XVimGActionEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C361DE671538F18400037B4B /* XVimGActionEvaluator.m */; }; @@ -303,8 +299,6 @@ A2BA3EA1152E372A00C18FB4 /* XVimOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimOptions.m; path = XVim/XVimOptions.m; sourceTree = SOURCE_ROOT; }; A2C1BB5116CEAC7F0066F420 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = XVim/Utils.h; sourceTree = ""; }; A2C1BB5216CEAC7F0066F420 /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = XVim/Utils.m; sourceTree = ""; }; - A2C4E79614E7F35300751199 /* Hooker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hooker.h; path = XVim/Hooker.h; sourceTree = ""; }; - A2C4E79714E7F35300751199 /* Hooker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Hooker.m; path = XVim/Hooker.m; sourceTree = ""; }; A2C4E79A14E7F36B00751199 /* XVimEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimEvaluator.h; path = XVim/XVimEvaluator.h; sourceTree = SOURCE_ROOT; }; A2C4E79B14E7F36B00751199 /* XVimEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = XVimEvaluator.m; path = XVim/XVimEvaluator.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; A2C55FC917EE9F49000EBEA7 /* DVTFoundation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DVTFoundation.h; path = XcodeClasses/Xcode4/DVTFoundation.h; sourceTree = SOURCE_ROOT; }; @@ -322,8 +316,8 @@ A2E7E45C17F037FF008F045A /* DVTKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DVTKit.framework; path = "../../../../Applications/Xcode 2.app/Contents/SharedFrameworks/DVTKit.framework"; sourceTree = ""; }; A2E7E45E17F03802008F045A /* DVTFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DVTFoundation.framework; path = "../../../../Applications/Xcode 2.app/Contents/SharedFrameworks/DVTFoundation.framework"; sourceTree = ""; }; A2E7E46017F0380F008F045A /* IDESourceEditor.ideplugin */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = IDESourceEditor.ideplugin; path = "../../../../Applications/Xcode 2.app/Contents/PlugIns/IDESourceEditor.ideplugin"; sourceTree = ""; }; - A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DVTSourceTextScrollViewHook.h; path = XVim/DVTSourceTextScrollViewHook.h; sourceTree = SOURCE_ROOT; }; - A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DVTSourceTextScrollViewHook.m; path = XVim/DVTSourceTextScrollViewHook.m; sourceTree = SOURCE_ROOT; }; + A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "DVTSourceTextScrollView+XVim.h"; path = "XVim/DVTSourceTextScrollView+XVim.h"; sourceTree = SOURCE_ROOT; }; + A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DVTSourceTextScrollView+XVim.m"; path = "XVim/DVTSourceTextScrollView+XVim.m"; sourceTree = SOURCE_ROOT; }; A2F4A33C14F00B25006DA5A5 /* XVimNormalEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimNormalEvaluator.h; path = XVim/XVimNormalEvaluator.h; sourceTree = SOURCE_ROOT; }; A2F4A33D14F00B25006DA5A5 /* XVimNormalEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = XVimNormalEvaluator.m; path = XVim/XVimNormalEvaluator.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; A2F4A34114F00BD3006DA5A5 /* XVimVisualEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimVisualEvaluator.h; path = XVim/XVimVisualEvaluator.h; sourceTree = SOURCE_ROOT; }; @@ -337,8 +331,6 @@ C324BDFF15390E8500C13558 /* XVimGVisualEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimGVisualEvaluator.h; path = XVim/XVimGVisualEvaluator.h; sourceTree = SOURCE_ROOT; }; C324BE0015390E8500C13558 /* XVimGVisualEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimGVisualEvaluator.m; path = XVim/XVimGVisualEvaluator.m; sourceTree = SOURCE_ROOT; }; C32CE8621532F0E5002BCE2B /* XVimKeymapProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = XVimKeymapProvider.h; path = XVim/XVimKeymapProvider.h; sourceTree = SOURCE_ROOT; }; - C345DDB9154CE12A009F232E /* XVimHookManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimHookManager.h; path = XVim/XVimHookManager.h; sourceTree = SOURCE_ROOT; }; - C345DDBA154CE12A009F232E /* XVimHookManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimHookManager.m; path = XVim/XVimHookManager.m; sourceTree = SOURCE_ROOT; }; C3552D99153948D200D57577 /* XVimCommandLineEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimCommandLineEvaluator.h; path = XVim/XVimCommandLineEvaluator.h; sourceTree = SOURCE_ROOT; }; C3552D9A153948D200D57577 /* XVimCommandLineEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimCommandLineEvaluator.m; path = XVim/XVimCommandLineEvaluator.m; sourceTree = SOURCE_ROOT; }; C3552DA6153AC6F800D57577 /* XVimHistoryHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimHistoryHandler.h; path = XVim/XVimHistoryHandler.h; sourceTree = SOURCE_ROOT; }; @@ -572,7 +564,6 @@ A24782B814D6F56E003B6433 /* XVim.m */, A22A009E1708914D0003046C /* XVimUtil.h */, A22A009F1708914D0003046C /* XVimUtil.m */, - C345DDB8154CCF79009F232E /* Event Dispatchers */, A2F4A34014F00B35006DA5A5 /* Event Handlers(Evaluators) */, C345DDB7154CCF1F009F232E /* Xcode Dependent Classes */, C345DDB4154CCE5E009F232E /* XCode Independent Classes */, @@ -599,8 +590,6 @@ A2C4E78F14E7EA0B00751199 /* Support */ = { isa = PBXGroup; children = ( - A2C4E79614E7F35300751199 /* Hooker.h */, - A2C4E79714E7F35300751199 /* Hooker.m */, A2C1BB5116CEAC7F0066F420 /* Utils.h */, A2C1BB5216CEAC7F0066F420 /* Utils.m */, A2702B0916FEA313005ADD76 /* NSObject+ExtraData.h */, @@ -757,6 +746,8 @@ A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */, A216F39F156560FE00AD2529 /* IDEEditorArea+XVim.h */, A216F3A0156560FE00AD2529 /* IDEEditorArea+XVim.m */, + A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.h */, + A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m */, A257C28D156567250098CA09 /* DVTSourceTextView+XVim.h */, A257C28E156567250098CA09 /* DVTSourceTextView+XVim.m */, 6E2B33321836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.h */, @@ -765,17 +756,6 @@ name = "Xcode Dependent Classes"; sourceTree = ""; }; - C345DDB8154CCF79009F232E /* Event Dispatchers */ = { - isa = PBXGroup; - children = ( - A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.h */, - A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */, - C345DDB9154CE12A009F232E /* XVimHookManager.h */, - C345DDBA154CE12A009F232E /* XVimHookManager.m */, - ); - name = "Event Dispatchers"; - sourceTree = ""; - }; C38A5B481527C9EE00E1448D /* Xcode Class Definitions */ = { isa = PBXGroup; children = ( @@ -878,7 +858,6 @@ A28F422917EEDBC200A3F7AE /* XVim.m in Sources */, A28F422A17EEDBC200A3F7AE /* XVimCommandField.m in Sources */, A28F422B17EEDBC200A3F7AE /* XVimCommandLine.m in Sources */, - A28F422C17EEDBC200A3F7AE /* Hooker.m in Sources */, A28F422D17EEDBC200A3F7AE /* XVimEvaluator.m in Sources */, A28F422E17EEDBC200A3F7AE /* XVimNormalEvaluator.m in Sources */, A28F422F17EEDBC200A3F7AE /* XVimVisualEvaluator.m in Sources */, @@ -914,7 +893,6 @@ A28F424E17EEDBC200A3F7AE /* XVimMarkSetEvaluator.m in Sources */, A28F424F17EEDBC200A3F7AE /* XVimArgumentEvaluator.m in Sources */, A28F425117EEDBC200A3F7AE /* XVimStatusLine.m in Sources */, - A28F425217EEDBC200A3F7AE /* XVimHookManager.m in Sources */, 6E2B332D1836E42F00EFE4E2 /* XVimBuffer.m in Sources */, A28F425317EEDBC200A3F7AE /* IDEEditor+XVim.m in Sources */, A28F425417EEDBC200A3F7AE /* NSInsetTextView.m in Sources */, @@ -927,7 +905,7 @@ 6E6865D718390702008D5FBB /* XVimUndo.m in Sources */, A28F425B17EEDBC200A3F7AE /* Utils.m in Sources */, A28F425C17EEDBC200A3F7AE /* XVimMark.m in Sources */, - A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */, + A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m in Sources */, A28F425D17EEDBC200A3F7AE /* XVimMarks.m in Sources */, A28F425F17EEDBC200A3F7AE /* NSObject+ExtraData.m in Sources */, A28F426017EEDBC200A3F7AE /* XVimUtil.m in Sources */, @@ -960,7 +938,6 @@ A24782C114D6F56E003B6433 /* XVim.m in Sources */, A24782C214D6F56E003B6433 /* XVimCommandField.m in Sources */, A2A8A50014E41C66002EA6C8 /* XVimCommandLine.m in Sources */, - A2C4E79814E7F35300751199 /* Hooker.m in Sources */, A2C4E79C14E7F36B00751199 /* XVimEvaluator.m in Sources */, A2F4A33E14F00B25006DA5A5 /* XVimNormalEvaluator.m in Sources */, A2F4A34314F00BD3006DA5A5 /* XVimVisualEvaluator.m in Sources */, @@ -995,7 +972,6 @@ C3E8F115154267BA007410ED /* XVimMarkSetEvaluator.m in Sources */, C3E8F11815427AE6007410ED /* XVimArgumentEvaluator.m in Sources */, A2ABFE1315497A3C002220E8 /* XVimStatusLine.m in Sources */, - C345DDBB154CE12A009F232E /* XVimHookManager.m in Sources */, 6E2B332C1836E42F00EFE4E2 /* XVimBuffer.m in Sources */, A26ACC4E154F2D6800B27D69 /* IDEEditor+XVim.m in Sources */, C308865A1554C8380024161D /* NSInsetTextView.m in Sources */, @@ -1007,7 +983,7 @@ A293A38816CE8A8000E1E827 /* XVimDebug.m in Sources */, A2C1BB5316CEAC7F0066F420 /* Utils.m in Sources */, A2126B7516FB30B0000BE21C /* XVimMark.m in Sources */, - A2F2621B182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */, + A2F2621B182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m in Sources */, A2126B7816FB4806000BE21C /* XVimMarks.m in Sources */, A2702B0B16FEA313005ADD76 /* NSObject+ExtraData.m in Sources */, 6EF220D6183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */, diff --git a/XVim/DVTSourceTextScrollView+XVim.h b/XVim/DVTSourceTextScrollView+XVim.h new file mode 100644 index 00000000..d339750a --- /dev/null +++ b/XVim/DVTSourceTextScrollView+XVim.h @@ -0,0 +1,14 @@ +// +// DVTSourceTextScrollViewHook.h +// XVim +// +// Created by Suzuki Shuichiro on 11/8/13. +// +// + +#import +#import "DVTKit.h" + +@interface DVTSourceTextScrollView (XVim) ++ (void)xvim_initialize; +@end \ No newline at end of file diff --git a/XVim/DVTSourceTextScrollView+XVim.m b/XVim/DVTSourceTextScrollView+XVim.m new file mode 100644 index 00000000..2700b5f1 --- /dev/null +++ b/XVim/DVTSourceTextScrollView+XVim.m @@ -0,0 +1,97 @@ +// +// DVTSourceTextScrollViewHook.m +// XVim +// +// Created by Suzuki Shuichiro on 11/8/13. +// +// + +#import "Logger.h" +#import "XVim.h" +#import "XVimOptions.h" +#import "DVTSourceTextScrollView+XVim.h" +#import "NSObject+XVimAdditions.h" + +@implementation DVTSourceTextScrollView (XVim) + ++ (void)xvim_initialize +{ + if (self == [DVTSourceTextScrollView class]) { +#define swizzle(sel) \ + [self xvim_swizzleInstanceMethod:@selector(sel) with:@selector(xvim_##sel)] + + swizzle(initWithFrame:); + swizzle(dealloc); + swizzle(hasVerticalScroller); + swizzle(hasHorizontalScroller); + swizzle(observeValueForKeyPath:ofObject:change:context:); + +#undef swizzle + } +} + +- (instancetype)xvim_initWithFrame:(NSRect)rect +{ + if ((self = [self xvim_initWithFrame:rect])) { + TRACE_LOG(@"%p initWithFrame", self); + [XVim.instance.options addObserver:self forKeyPath:@"guioptions" + options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_xvim_statusChanged:) + name:XVimEnabledStatusChangedNotification object:nil]; + } + return self; +} + +- (void)xvim_dealloc +{ + @try { + TRACE_LOG(@"%p dealloc", self); + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [XVim.instance.options removeObserver:self forKeyPath:@"guioptions"]; + } + @catch (NSException* exception){ + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } + [self xvim_dealloc]; +} + +- (void)_xvim_statusChanged:(id)unused +{ + if (XVim.instance.disabled) { + self.hasHorizontalScroller = YES; + self.hasVerticalScroller = YES; + } +} + +- (BOOL)xvim_hasVerticalScroller +{ + if (XVim.instance.disabled) { + return [self xvim_hasVerticalScroller]; + } + return [XVim.instance.options.guioptions rangeOfString:@"r"].location != NSNotFound; +} + +- (BOOL)xvim_hasHorizontalScroller +{ + if (XVim.instance.disabled) { + return [self xvim_hasHorizontalScroller]; + } + return [XVim.instance.options.guioptions rangeOfString:@"b"].location != NSNotFound; +} + +- (void)xvim_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary *)change context:(void *)context +{ + if (object != XVim.instance.options) { + return [self xvim_observeValueForKeyPath:keyPath ofObject:object + change:change context:context]; + } + if ([keyPath isEqualToString:@"guioptions"]) { + // Just updating the scrollers state. + self.hasHorizontalScroller = self.hasHorizontalScroller; + self.hasVerticalScroller = self.hasVerticalScroller; + } +} + +@end diff --git a/XVim/DVTSourceTextScrollViewHook.h b/XVim/DVTSourceTextScrollViewHook.h deleted file mode 100644 index 317b77af..00000000 --- a/XVim/DVTSourceTextScrollViewHook.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// DVTSourceTextScrollViewHook.h -// XVim -// -// Created by Suzuki Shuichiro on 11/8/13. -// -// - -#import -#import "DVTKit.h" - -@interface DVTSourceTextScrollViewHook : NSObject -+ (void)hook; -+ (void)unhook; -@end - -@interface DVTSourceTextScrollView(Hook) -- (id)initWithFrame_:(NSRect)rect; -- (void)dealloc_; -- (BOOL)hasHorizontalScroller_; -- (BOOL)hasVerticalScroller_; -@end \ No newline at end of file diff --git a/XVim/DVTSourceTextScrollViewHook.m b/XVim/DVTSourceTextScrollViewHook.m deleted file mode 100644 index 495e7f02..00000000 --- a/XVim/DVTSourceTextScrollViewHook.m +++ /dev/null @@ -1,107 +0,0 @@ -// -// DVTSourceTextScrollViewHook.m -// XVim -// -// Created by Suzuki Shuichiro on 11/8/13. -// -// - -#import "Logger.h" -#import "XVim.h" -#import "XVimOptions.h" -#import "Hooker.h" -#import "DVTSourceTextScrollViewHook.h" - - -@implementation DVTSourceTextScrollViewHook -+ (void)hook:(NSString*)method{ - NSString* cls = @"DVTSourceTextScrollView"; - NSString* thisCls = NSStringFromClass([self class]); - [Hooker hookClass:cls method:method byClass:thisCls method:method]; -} - -+ (void)unhook:(NSString*)method{ - NSString* cls = @"DVTSourceTextScrollView"; - [Hooker unhookClass:cls method:method]; -} - -+ (void)hook{ - [self hook:@"initWithFrame:"]; - [self hook:@"dealloc"]; - [self hook:@"hasVerticalScroller"]; - [self hook:@"hasHorizontalScroller"]; - [self hook:@"observeValueForKeyPath:ofObject:change:context:"]; -} - -+ (void)unhook{ - // Never unhook comment outed methods because this class observes XVimOption value on init and remove observe on dealloc. - // Unhooking between init and dealloc leads inconsistent state (and crash) - // [self unhook:@"initWithFrame:"]; - // [self unhook:@"dealloc"]; - [self unhook:@"hasVerticalScroller"]; - [self unhook:@"hasHorizontalScroller"]; - // [self unhook:@"observeValueForKeyPath:ofObject:change:context:"]; -} - -- (id)initWithFrame:(NSRect)rect{ - DVTSourceTextScrollView* base = (DVTSourceTextScrollView*)self; - id obj = [base initWithFrame_:rect]; - if( nil != obj ){ - TRACE_LOG(@"%p initWithFrame", obj); - [XVim.instance.options addObserver:obj forKeyPath:@"guioptions" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - } - return (DVTSourceTextScrollViewHook*)obj; -} - -// This pragma is for suppressing warning that the dealloc method does not call [super dealloc]. ([base dealloc_] calls [super dealloc] so we do not need it) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wall" -- (void)dealloc{ - DVTSourceTextScrollView *base = (DVTSourceTextScrollView*)self; - @try{ - TRACE_LOG(@"%p dealloc", base); - [XVim.instance.options removeObserver:self forKeyPath:@"guioptions"]; - } - @catch (NSException* exception){ - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - [base dealloc_]; - return; -} -#pragma GCC diagnostic pop - -- (BOOL)hasVerticalScroller{ - if( [XVim.instance.options.guioptions rangeOfString:@"r"].location == NSNotFound) { - return NO; - }else{ - return YES; - } -} - -- (BOOL)hasHorizontalScroller{ - if( [XVim.instance.options.guioptions rangeOfString:@"b"].location == NSNotFound) { - return NO; - }else{ - return YES; - } -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if([keyPath isEqualToString:@"guioptions"]){ - NSScrollView* view = (NSScrollView*)self; - // Just updating the scrollers state. - if( [XVim.instance.options.guioptions rangeOfString:@"r"].location == NSNotFound) { - [view setHasVerticalScroller:NO]; - }else{ - [view setHasVerticalScroller:YES]; - } - if( [XVim.instance.options.guioptions rangeOfString:@"b"].location == NSNotFound) { - [view setHasHorizontalScroller:NO]; - }else{ - [view setHasHorizontalScroller:YES]; - } - } -} - -@end diff --git a/XVim/Hooker.h b/XVim/Hooker.h deleted file mode 100644 index 7e16921f..00000000 --- a/XVim/Hooker.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Hooker.h -// XVim -// -// Created by Shuichiro Suzuki on 2/3/12. -// Copyright 2012 JugglerShu.Net. All rights reserved. -// - -#import - -@interface Hooker : NSObject - -/** - * Hook method specified by "cls" and "mtd" by "cls2"-"mtd2. - * All the calls to the "cls"-"mtd" will be redirected to "cls2", "mtd2" - * When "mtd2" is called the "self" is "cls" object even its method in "cls2". - * If you want to call original method in "mtd2" write [self mtd_]; - * Here "mtd_" is the name of method generated automatically to keep original method and genereted with following rule. - * Originam Method Name Rule: - * If it does not have any arguments the method name is created by appending "_" to original method name. - * - "length" method would be "length_" - * - "textStorage" method would be "textStorage_". - * If it has arguments the method name is created by inserting "_" before the first ":" of original method name. - * - "initWithFrame:" would be "initWithFrame_:" - * - "setSelectedRange:affinity:stillSelecting:" would be "setSelectedRange_:affinity:stillSelecting:" - **/ -+ (void) hookClass:(NSString*)cls method:(NSString*)mtd byClass:(NSString*)cls2 method:(NSString*)mtd2; - -/** - * Unhook "cls"'s "mtd" method. - * This method must be hooked by hookClass:... method. - **/ -+ (void) unhookClass:(NSString*)cls method:(NSString*)mtd; - -/** - * This is actual implemenatation of hookClass:... method. - * Do not use this unless you need hook with special names ( not obeying the rule above ) - **/ -+ (void) hookMethod:(SEL)sel ofClass:(Class)cls withMethod:(Method)newMethod keepingOriginalWith:(SEL)selOriginal; - -@end diff --git a/XVim/Hooker.m b/XVim/Hooker.m deleted file mode 100644 index 35e0a7fc..00000000 --- a/XVim/Hooker.m +++ /dev/null @@ -1,79 +0,0 @@ - -// Hooker.m -// XVim -// -// Created by Shuichiro Suzuki on 2/3/12. -// Copyright 2012 JugglerShu.Net. All rights reserved. -// - -#import "Hooker.h" - -@implementation Hooker - -- (id)init{ - self = [super init]; - if (self) { - // Initialization code here. - } - return self; -} - -// clsに渡されたクラスのselセレクタを呼び出したときに実行されるメソッドをフックし -// newMethodに転送するように設定する。 -// このとき、オリジナルのメソッドはselOriginalで渡されたセレクタで呼び出せるように設定する。 -// 例: hookMethod:@selector(keDown:) ofClass:[NSTextView class] withMethod:methodWrittenByMe keepingOriginalWith:@selector(originalKeyDown:) -// NSTextViewのkeyDown:セレクタでメソッドが呼ばれた時、methodWrittenByMe出指定されたメソッドが呼び出されるようになる。 -// methodWrittenByMeが呼び出されたとき、オリジナルのものを呼び出したければ、[self originalKeyDown:...]とすればよい -+ (void) hookMethod:(SEL)sel ofClass:(Class)cls withMethod:(Method)newMethod keepingOriginalWith:(SEL)selOriginal{ - //オリジナルメソッド superクラスも見に行きメソッドを探す - Method origMethod = class_getInstanceMethod(cls, sel); - //オリジナルIMP by セレクタ - IMP origImp_stret = class_getMethodImplementation_stret(cls, sel); - class_replaceMethod(cls, sel, method_getImplementation(newMethod), method_getTypeEncoding(origMethod)); - // origImpはnilが帰る可能性がある。(サブクラスがそのメソッドを持たない場合) origImp_stretをselOriginalで呼び出せるようにする - //NSTextViewの実装をkeyDown_:で呼び出せるようにしておく(keyDownをフックしたときに、転送できるように) - if( nil != selOriginal ){ - class_addMethod(cls, selOriginal, origImp_stret, method_getTypeEncoding(origMethod)); - } -} - -+ (void) hookClass:(NSString*)cls method:(NSString*)mtd byClass:(NSString*)cls2 method:(NSString*)mtd2{ - Class c1 = NSClassFromString(cls); - Class c2 = NSClassFromString(cls2); - Method m2 = class_getInstanceMethod(c2, NSSelectorFromString(mtd2)); - - SEL preservedSelector = [Hooker createPreserveSelectorName:mtd]; - - [Hooker hookMethod:NSSelectorFromString(mtd) ofClass:c1 withMethod:m2 keepingOriginalWith:preservedSelector]; -} - -+ (void) unhookClass:(NSString*)cls method:(NSString*)mtd{ - Class c1 = NSClassFromString(cls); - SEL preservedSelector = [Hooker createPreserveSelectorName:mtd]; - Method m2 = class_getInstanceMethod(c1, preservedSelector); - - [Hooker hookMethod:NSSelectorFromString(mtd) ofClass:c1 withMethod:m2 keepingOriginalWith:nil]; -} - -/** - * Internal method. - * This convert a method name by a rule explained in hookClass:... method's explanation. - * It is like... - * Method "foo" will be "foo_" - * Method "foo:" will be "foo_:" - * Method "foo:bar:" will be "foo_:bar" - **/ -+ (SEL) createPreserveSelectorName:(NSString*)origSelector{ - NSRange r = [origSelector rangeOfString:@":"]; - if( NSNotFound == r.location ){ - // Just appeend "_" at the end. - return NSSelectorFromString([origSelector stringByAppendingString:@"_"]); - }else{ - // Insert "_" before first ":" - NSMutableString *newSel = [NSMutableString stringWithString:[origSelector substringToIndex:r.location]]; - [newSel appendString:@"_"]; - [newSel appendString:[origSelector substringFromIndex:r.location]]; - return NSSelectorFromString(newSel); - } -} -@end diff --git a/XVim/IDEEditor+XVim.m b/XVim/IDEEditor+XVim.m index 767b80dc..28c871b0 100644 --- a/XVim/IDEEditor+XVim.m +++ b/XVim/IDEEditor+XVim.m @@ -17,7 +17,6 @@ #import "NSObject+XVimAdditions.h" #import "DVTSourceTextView+XVim.h" #import "Logger.h" -#import "Hooker.h" static char const * const DID_REGISTER_OBSERVER_KEY = "net.JugglerShu.IDEEditorHook._didRegisterObserver"; diff --git a/XVim/Logger.m b/XVim/Logger.m index 7508482e..a623a4e1 100644 --- a/XVim/Logger.m +++ b/XVim/Logger.m @@ -7,7 +7,6 @@ // #import "Logger.h" -#import "Hooker.h" #import #import diff --git a/XVim/NSObject+XVimAdditions.m b/XVim/NSObject+XVimAdditions.m index 29265139..19f53edc 100644 --- a/XVim/NSObject+XVimAdditions.m +++ b/XVim/NSObject+XVimAdditions.m @@ -21,7 +21,7 @@ + (void)xvim_swizzleClassMethod:(SEL)origSel with:(SEL)newSel NSAssert(newMethod, @"+[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(newSel)); if (class_addMethod(class, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { - class_replaceMethod(class, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)); + class_replaceMethod(class, newSel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); } else { method_exchangeImplementations(newMethod, origMethod); } @@ -48,7 +48,7 @@ + (void)xvim_swizzleInstanceMethod:(SEL)origSel with:(SEL)newSel NSAssert(newMethod, @"-[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(newSel)); if (class_addMethod(self, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { - class_replaceMethod(self, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)); + class_replaceMethod(self, newSel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); } else { method_exchangeImplementations(newMethod, origMethod); } diff --git a/XVim/XVim.m b/XVim/XVim.m index 3f177713..d629d784 100644 --- a/XVim/XVim.m +++ b/XVim/XVim.m @@ -31,7 +31,6 @@ #import "XVimKeyStroke.h" #import "XVimOptions.h" #import "XVimHistoryHandler.h" -#import "XVimHookManager.h" #import "XVimCommandLine.h" #import "XVimMarks.h" #import "XVimMotion.h" @@ -147,7 +146,7 @@ + (void) pluginDidLoad:(NSBundle *)plugin // Otherwise, we may miss some classes. // Command line window is not setuped if hook is too late. - [XVimHookManager hookWhenPluginLoaded]; + [XVimWindow class]; // We used to observer NSApplicationDidFinishLaunchingNotification to wait for all the classes in Xcode are loaded. // When notification comes we hook some classes so that we do not miss any classes. diff --git a/XVim/XVimHookManager.h b/XVim/XVimHookManager.h deleted file mode 100644 index 9e37143a..00000000 --- a/XVim/XVimHookManager.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// XVimHookManager.h -// XVim -// -// Created by Tomas Lundell on 29/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import - -@interface XVimHookManager : NSObject -+ (void)hookWhenPluginLoaded; -@end diff --git a/XVim/XVimHookManager.m b/XVim/XVimHookManager.m deleted file mode 100644 index d9dbec85..00000000 --- a/XVim/XVimHookManager.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// XVimHookManager.m -// XVim -// -// Created by Tomas Lundell on 29/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "Logger.h" -#import "XVimView.h" -#import "XVimHookManager.h" -#import "IDEEditor+XVim.h" -#import "IDEEditorArea+XVim.h" -#import "DVTSourceTextScrollViewHook.h" -#import "NSEvent+VimHelper.h" -#import "NSObject+XVimAdditions.h" - -@implementation IDEWorkspaceWindow (XVim) - -+ (void)xvim_initialize -{ -#if 0 // Only useful for debugging purposes - if (self == [IDEWorkspaceWindow class]) { - [self xvim_swizzleInstanceMethod:@selector(sendEvent:) - with:@selector(xvim_sendEvent:)]; - } -#endif -} - -- (void)xvim_sendEvent:(NSEvent *)event -{ - if (event.type == NSKeyDown) { - TRACE_LOG(@"Window:%p keyCode:%d characters:%@ charsIgnoreMod:%@ cASCII:%d", - self, event.keyCode, event.characters, event.charactersIgnoringModifiers, event.unmodifiedKeyCode); - } - [self xvim_sendEvent:event]; -} - -@end - -@implementation XVimHookManager - -+ (void)hookWhenPluginLoaded -{ - [IDEWorkspaceWindow xvim_initialize]; - [IDEEditorArea xvim_initialize]; - [DVTSourceTextScrollViewHook hook]; - [IDEEditor xvim_initialize]; - [IDEComparisonEditor xvim_initialize]; - [XVimView class]; -} - -@end diff --git a/XVim/XVimWindow.m b/XVim/XVimWindow.m index 4dff5623..8c554f86 100644 --- a/XVim/XVimWindow.m +++ b/XVim/XVimWindow.m @@ -5,6 +5,7 @@ // Created by Tomas Lundell on 9/04/12. // +#import #import "XVimWindow.h" #import "XVim.h" #import "XVimUtil.h" @@ -14,13 +15,39 @@ #import "XVimKeymap.h" #import "XVimOptions.h" #import "Logger.h" -#import -#import "IDEEditorArea+XVim.h" #import "XVimSearch.h" + +#import "IDEEditor+XVim.h" +#import "IDEEditorArea+XVim.h" +#import "DVTSourceTextScrollView+XVim.h" #import "NSTextView+VimOperation.h" +#import "NSEvent+VimHelper.h" #import "XVimCommandLineEvaluator.h" #import "XVimInsertEvaluator.h" +@implementation IDEWorkspaceWindow (XVim) + ++ (void)xvim_initialize +{ +#if 0 // Only useful for debugging purposes + if (self == [IDEWorkspaceWindow class]) { + [self xvim_swizzleInstanceMethod:@selector(sendEvent:) + with:@selector(xvim_sendEvent:)]; + } +#endif +} + +- (void)xvim_sendEvent:(NSEvent *)event +{ + if (event.type == NSKeyDown) { + TRACE_LOG(@"Window:%p keyCode:%d characters:%@ charsIgnoreMod:%@ cASCII:%d", + self, event.keyCode, event.characters, event.charactersIgnoringModifiers, event.unmodifiedKeyCode); + } + [self xvim_sendEvent:event]; +} + +@end + @interface XVimWindow () { NSMutableArray *_evaluatorStack; XVimKeymapContext *_keymapContext; @@ -41,6 +68,18 @@ @implementation XVimWindow @synthesize commandLine = _commandLine; @synthesize tmpBuffer = _tmpBuffer; ++ (void)initialize +{ + if (self == [XVimWindow class]) { + [IDEWorkspaceWindow xvim_initialize]; + [IDEEditorArea xvim_initialize]; + [DVTSourceTextScrollView xvim_initialize]; + [IDEEditor xvim_initialize]; + [IDEComparisonEditor xvim_initialize]; + [XVimView class]; + } +} + - (instancetype)initWithIDEEditorArea:(IDEEditorArea *)editorArea { if (self = [super init]){ From 559770fe5bdc03c1b9cd9b5e190952a014e1ee7f Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Fri, 22 Nov 2013 09:22:17 +0100 Subject: [PATCH 21/44] Get rid of XVimTextViewProtocol it's replaced by XVimView/XVimBuffer. It's a nice reference of the things to have though. --- XVim.xcodeproj/project.pbxproj | 2 - XVim/NSTextView+VimOperation.h | 3 +- XVim/XVim.h | 1 - XVim/XVimArgumentEvaluator.m | 1 + XVim/XVimDefs.h | 13 +++- XVim/XVimRegister.h | 7 +- XVim/XVimTextViewProtocol.h | 113 --------------------------------- XVim/XVimView.h | 11 ++++ XVim/XVimWindow.h | 1 - XVim/XVimYankEvaluator.h | 1 - 10 files changed, 26 insertions(+), 127 deletions(-) delete mode 100644 XVim/XVimTextViewProtocol.h diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 1698de33..ab9cda25 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -279,7 +279,6 @@ A28D895617BFF434002709D8 /* XVimTester+Search.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XVimTester+Search.m"; path = "XVim/Test/XVimTester+Search.m"; sourceTree = SOURCE_ROOT; }; A293A38616CE8A8000E1E827 /* XVimDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimDebug.h; path = XVim/XVimDebug.h; sourceTree = SOURCE_ROOT; }; A293A38716CE8A8000E1E827 /* XVimDebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimDebug.m; path = XVim/XVimDebug.m; sourceTree = SOURCE_ROOT; }; - A2A193FF1608E40000809FBE /* XVimTextViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = XVimTextViewProtocol.h; path = XVim/XVimTextViewProtocol.h; sourceTree = SOURCE_ROOT; }; A2A6BA2C152A544B00F0EB5F /* XVimSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimSearch.h; path = XVim/XVimSearch.h; sourceTree = SOURCE_ROOT; }; A2A6BA2D152A544B00F0EB5F /* XVimSearch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimSearch.m; path = XVim/XVimSearch.m; sourceTree = SOURCE_ROOT; }; A2A736341527484B0051E8E4 /* XVimExCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimExCommand.h; path = XVim/XVimExCommand.h; sourceTree = SOURCE_ROOT; }; @@ -570,7 +569,6 @@ A26DC88315DF339A00779CB4 /* Testing */, C38A5B481527C9EE00E1448D /* Xcode Class Definitions */, A2B4BAC214D59F6600D817B0 /* Supporting Files */, - A2A193FF1608E40000809FBE /* XVimTextViewProtocol.h */, ); name = XVim; path = XVim_lite2; diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h index ba6414a4..f4385aef 100644 --- a/XVim/NSTextView+VimOperation.h +++ b/XVim/NSTextView+VimOperation.h @@ -43,9 +43,8 @@ // All the method in this caregory must have a prefix "xvim_" // This is to avoid a conflict with method names in NSTextView. -#import "XVimTextViewProtocol.h" +#import "XVimView.h" #import "NSTextStorage+VimOperation.h" -#import @interface NSTextView (VimOperation) // TODO: Method names in category should have prefix like xvim_ diff --git a/XVim/XVim.h b/XVim/XVim.h index 2fdd6a72..5b291bcb 100644 --- a/XVim/XVim.h +++ b/XVim/XVim.h @@ -9,7 +9,6 @@ #import #import "XVimDefs.h" #import "XVimKeymapProvider.h" -#import "XVimTextViewProtocol.h" #import "XVimKeyStroke.h" @class XVimKeymap; diff --git a/XVim/XVimArgumentEvaluator.m b/XVim/XVimArgumentEvaluator.m index 7aaa8fd7..7df444a3 100644 --- a/XVim/XVimArgumentEvaluator.m +++ b/XVim/XVimArgumentEvaluator.m @@ -9,6 +9,7 @@ #import "XVimArgumentEvaluator.h" #import "XVimKeyStroke.h" #import "XVimWindow.h" +#import "XVimKeymapProvider.h" @implementation XVimArgumentEvaluator @synthesize keyStroke = _keyStroke; diff --git a/XVim/XVimDefs.h b/XVim/XVimDefs.h index 09b61c5d..e2f2c290 100644 --- a/XVim/XVimDefs.h +++ b/XVim/XVimDefs.h @@ -28,6 +28,17 @@ typedef NS_ENUM(NSUInteger, XVimInsertionPoint) { XVIM_INSERT_BLOCK_KILL_EOL, }; +typedef enum{ + TEXT_TYPE_CHARACTERS, + TEXT_TYPE_BLOCK, + TEXT_TYPE_LINES +} TEXT_TYPE; + +typedef enum { + CURSOR_MODE_INSERT, + CURSOR_MODE_COMMAND +} CURSOR_MODE; + typedef enum { XVIM_MODE_NONE, XVIM_MODE_NORMAL, @@ -44,7 +55,7 @@ typedef enum { XVIM_VISUAL_CHARACTER, // for 'v' XVIM_VISUAL_LINE, // for 'V' XVIM_VISUAL_BLOCK, // for 'CTRL-V' -}XVIM_VISUAL_MODE; +} XVIM_VISUAL_MODE; typedef enum { _XVIM_VISUAL_RIGHT = 1, diff --git a/XVim/XVimRegister.h b/XVim/XVimRegister.h index b346c0e3..54709a51 100644 --- a/XVim/XVimRegister.h +++ b/XVim/XVimRegister.h @@ -7,14 +7,9 @@ // #import +#import "XVimDefs.h" #import "XVimKeyStroke.h" -typedef enum{ - TEXT_TYPE_CHARACTERS, - TEXT_TYPE_BLOCK, - TEXT_TYPE_LINES -}TEXT_TYPE; - @interface XVimRegister : NSObject -(void) appendXVimString:(XVimString*)string; -(void) setXVimString:(XVimString*)string; diff --git a/XVim/XVimTextViewProtocol.h b/XVim/XVimTextViewProtocol.h deleted file mode 100644 index 3585a1e1..00000000 --- a/XVim/XVimTextViewProtocol.h +++ /dev/null @@ -1,113 +0,0 @@ -// -// XVimTextView.h -// XVim - -// -// Created by Suzuki Shuichiro on 9/19/12. -// -// - -#import "XVim.h" -#import "XVimMotion.h" -#import "XVimRegister.h" -#import - -/** - * This is the interface to operate on text view used in XVim. - * Text views want to communicate with XVim handlers(evaluators) must implement this protocol. - **/ - -@protocol XVimTextViewDelegateProtocol -- (void)textView:(NSTextView*)view didYank:(NSString*)yankedText withType:(TEXT_TYPE)type; -- (void)textView:(NSTextView*)view didDelete:(NSString*)deletedText withType:(TEXT_TYPE)type; -@end - -typedef enum { - CURSOR_MODE_INSERT, - CURSOR_MODE_COMMAND -}CURSOR_MODE; - -typedef enum { - OPTION_NONE, -}OPERATION_OPTION; - -@protocol XVimTextViewProtocol -@property(readonly) NSUInteger insertionPoint; -@property(readonly) NSUInteger insertionColumn; -@property(readonly) NSUInteger insertionLine; -@property(readonly) NSUInteger selectionBegin; -@property(readonly) XVIM_VISUAL_MODE selectionMode; -@property(readonly) NSUInteger selectedLines; // Number of lines selected (0 if not in selection mode) -@property(readonly) NSUInteger selectedColumns; // Number of columns selected (0 if not in selection mode) -@property(readonly) CURSOR_MODE cursorMode; -@property(readonly) NSUInteger preservedColumn; - -// Selection Mode -- (void)changeSelectionMode:(XVIM_VISUAL_MODE)mode; - - - - -// Top Level Operation Interface -- (void)move:(XVimMotion*)motion; -- (void)delete:(XVimMotion*)motion; -- (void)change:(XVimMotion*)motion; -- (void)yank:(XVimMotion*)motion; -- (void)put:(NSString*)text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)times; -- (void)swapCase:(XVimMotion*)motion; // Previously this is named "toggleCase" in XVim -- (void)makeLowerCase:(XVimMotion*)motion; // Previously this is named "lowerCase" in XVim -- (void)makeUpperCase:(XVimMotion*)motion; // Previously this is named "lowerCase" in XVim -- (void)filter:(XVimMotion*)motion; -- (void)shiftRight:(XVimMotion*)motion; -- (void)shiftLeft:(XVimMotion*)motion; -- (void)join:(NSUInteger)count; -- (void)insertNewlineBelowLine:(NSUInteger)line; -- (void)insertNewlineAboveLine:(NSUInteger)line; -- (void)insertNewlineAbove; -- (void)insertNewlineBelow; -- (void)insertNewlineAboveAndInsert; -- (void)insertNewlineBelowAndInsert; -- (void)insertText:(NSString*)str line:(NSUInteger)line column:(NSUInteger)column; -- (BOOL)replaceCharacters:(unichar)c count:(NSUInteger)count; - -// Insert or Command -- (void)escapeFromInsert; -- (void)append; -- (void)insert; -- (void)appendAtEndOfLine; -- (void)insertBeforeFirstNonblank; - -// for keydown in insertion -- (void)passThroughKeyDown:(NSEvent*)event; - -// Premitive Operations ( Avoid using following. Consider use or make Top Level Operation Interface instead ) -- (void)moveBack:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveFoward:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveDown:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveUp:(NSUInteger)count option:(MOTION_OPTION)opt; -//- (void)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimWordInfo*)info; -- (void)moveWordsBackward:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveSentencesForward:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveSentencesBackward:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveParagraphsForward:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveParagraphsBackward:(NSUInteger)count option:(MOTION_OPTION)opt; - -- (void)overwriteCharacter:(unichar)c; - - -- (void)moveToPosition:(XVimPosition)pos; - -// Scrolls -- (void)scrollPageForward:(NSUInteger)count; -- (void)scrollPageBackward:(NSUInteger)count; -- (void)scrollHalfPageForward:(NSUInteger)count; -- (void)scrollHalfPageBackward:(NSUInteger)count; -- (void)scrollLineForward:(NSUInteger)count; -- (void)scrollLineBackward:(NSUInteger)count; -- (void)scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; -- (void)scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb;; -- (void)scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb;; - - - -@end diff --git a/XVim/XVimView.h b/XVim/XVimView.h index e31aefbe..2f6f28c4 100644 --- a/XVim/XVimView.h +++ b/XVim/XVimView.h @@ -7,6 +7,7 @@ // #import +#import "XVimDefs.h" @class XVimView, XVimBuffer, XVimWindow; @@ -16,6 +17,16 @@ - (XVimView *)xvim_makeXVimViewInWindow:(XVimWindow *)window; @end +/** + * This is the interface to operate on text view used in XVim. + * Text views want to communicate with XVim handlers(evaluators) must implement this protocol. + **/ + +@protocol XVimTextViewDelegateProtocol +- (void)textView:(NSTextView*)view didYank:(NSString*)yankedText withType:(TEXT_TYPE)type; +- (void)textView:(NSTextView*)view didDelete:(NSString*)deletedText withType:(TEXT_TYPE)type; +@end + @interface XVimView : NSObject @property (readonly, nonatomic) XVimWindow *window; @property (readonly, nonatomic) NSTextView *textView; diff --git a/XVim/XVimWindow.h b/XVim/XVimWindow.h index 7fd89ad8..57dcf6bb 100644 --- a/XVim/XVimWindow.h +++ b/XVim/XVimWindow.h @@ -7,7 +7,6 @@ #import #import "XVimCommandLine.h" -#import "XVimTextViewProtocol.h" #import "XVimKeyStroke.h" #import "XVimBuffer.h" #import "XVimView.h" diff --git a/XVim/XVimYankEvaluator.h b/XVim/XVimYankEvaluator.h index 957e4142..991b6d21 100644 --- a/XVim/XVimYankEvaluator.h +++ b/XVim/XVimYankEvaluator.h @@ -7,7 +7,6 @@ // #import "XVimOperatorEvaluator.h" -#import "XVimTextViewProtocol.h" @interface XVimYankEvaluator : XVimOperatorEvaluator - (XVimEvaluator*)y; From c068d4aa16759581f55d08e5e678859893a070e9 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Fri, 22 Nov 2013 10:38:40 +0100 Subject: [PATCH 22/44] Migrate scrolling and some drawing code to XVimView --- XVim/NSTextView+VimOperation.h | 28 --- XVim/NSTextView+VimOperation.m | 299 ++----------------------------- XVim/XVimCommandLineEvaluator.m | 5 +- XVim/XVimEvaluator.h | 9 +- XVim/XVimEvaluator.m | 11 +- XVim/XVimExCommand.m | 7 +- XVim/XVimInsertEvaluator.m | 6 +- XVim/XVimNormalEvaluator.m | 14 +- XVim/XVimOperatorEvaluator.m | 2 +- XVim/XVimRecordingEvaluator.m | 6 +- XVim/XVimTester.m | 6 +- XVim/XVimTextObjectEvaluator.m | 2 +- XVim/XVimView.h | 22 +++ XVim/XVimView.m | 302 +++++++++++++++++++++++++++++++- XVim/XVimVisualEvaluator.m | 8 +- XVim/XVimWindow.h | 2 +- XVim/XVimWindow.m | 26 +-- XVim/XVimZEvaluator.m | 12 +- 18 files changed, 387 insertions(+), 380 deletions(-) diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h index f4385aef..eb635b2c 100644 --- a/XVim/NSTextView+VimOperation.h +++ b/XVim/NSTextView+VimOperation.h @@ -116,25 +116,6 @@ - (void)xvim_selectPreviousPlaceholder; - (void)xvim_hideCompletions; -#pragma mark Scroll -- (NSUInteger)xvim_lineUp:(NSUInteger)index count:(NSUInteger)count; -- (NSUInteger)xvim_lineDown:(NSUInteger)index count:(NSUInteger)count; -- (void)xvim_scroll:(CGFloat)ratio count:(NSUInteger)count; -- (void)xvim_scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; -- (void)xvim_scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; -- (void)xvim_scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; -- (void)xvim_scrollTo:(NSUInteger)location; -- (void)xvim_pageForward:(NSUInteger)index count:(NSUInteger)count; -- (void)xvim_pageBackward:(NSUInteger)index count:(NSUInteger)count; -- (void)xvim_halfPageForward:(NSUInteger)index count:(NSUInteger)count; -- (void)xvim_halfPageBackward:(NSUInteger)index count:(NSUInteger)count; -- (void)xvim_scrollPageForward:(NSUInteger)count; -- (void)xvim_scrollPageBackward:(NSUInteger)count; -- (void)xvim_scrollHalfPageForward:(NSUInteger)count; -- (void)xvim_scrollHalfPageBackward:(NSUInteger)count; -- (void)xvim_scrollLineForward:(NSUInteger)count; -- (void)xvim_scrollLineBackward:(NSUInteger)count; - #pragma mark Search /** * Shows the candidate of the next search result. @@ -147,15 +128,6 @@ - (NSRange)xvim_currentWord:(MOTION_OPTION)opt; #pragma mark Searching positions -// TODO: Thses method should be internal. Create abstracted interface to achieve the operation uses these methods. -/** - * Takes point in view and returns its index. - * This method automatically convert the "folded index" to "real index" - * When some characters are folded( like placeholders) the pure index for a specifix point is - * less than real index in the string. - **/ -- (NSUInteger)xvim_glyphIndexForPoint:(NSPoint)point; -- (NSRect)xvim_boundingRectForGlyphIndex:(NSUInteger)glyphIndex; /** * Return number of lines in current visible view. diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index 7dea3e05..36d96d42 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -63,10 +63,6 @@ - (NSArray*)xvim_selectedRanges; - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion; - (NSRange)xvim_getOperationRangeFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type; - (void)xvim_indentCharacterRange:(NSRange)range; -- (void)xvim_scrollCommon_moveCursorPos:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; -- (NSUInteger)xvim_lineNumberFromBottom:(NSUInteger)count; -- (NSUInteger)xvim_lineNumberAtMiddle; -- (NSUInteger)xvim_lineNumberFromTop:(NSUInteger)count; - (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward; - (void)xvim_swapCaseForRange:(NSRange)range; - (void)xvim_registerInsertionPointForUndo; @@ -1455,214 +1451,6 @@ - (void)xvim_hideCompletions { #endif } -#pragma mark Scroll -- (NSUInteger)xvim_lineUp:(NSUInteger)index count:(NSUInteger)count { - [self scrollLineUp:self]; - NSRect visibleRect = [[self enclosingScrollView] contentView].bounds; - NSRect currentInsertionRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(index,0) inTextContainer:[self textContainer]]; - NSPoint relativeInsertionPoint = SubPoint(currentInsertionRect.origin, visibleRect.origin); - if (relativeInsertionPoint.y > visibleRect.size.height) { - [self moveUp:self]; - NSPoint newPoint = [[self layoutManager] boundingRectForGlyphRange:[self selectedRange] inTextContainer:[self textContainer]].origin; - index = [self xvim_glyphIndexForPoint:newPoint]; - } - return index; -} - -- (NSUInteger)xvim_lineDown:(NSUInteger)index count:(NSUInteger)count { - [self scrollLineDown:self]; - NSRect visibleRect = [[self enclosingScrollView] contentView].bounds; - NSRect currentInsertionRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(index,0) inTextContainer:[self textContainer]]; - if (currentInsertionRect.origin.y < visibleRect.origin.y) { - [self moveDown:self]; - NSPoint newPoint = NSMakePoint(currentInsertionRect.origin.x, visibleRect.origin.y); - index = [self xvim_glyphIndexForPoint:newPoint]; - } - return index; -} - -- (void)xvim_scroll:(CGFloat)ratio count:(NSUInteger)count{ - NSScrollView *scrollView = [self enclosingScrollView]; - NSRect visibleRect = [scrollView contentView].bounds; - CGFloat scrollSize = visibleRect.size.height * ratio * count; - NSPoint scrollPoint = NSMakePoint(visibleRect.origin.x, visibleRect.origin.y + scrollSize ); // This may be beyond the beginning or end of document (intentionally) - - // Cursor position relative to left-top origin shold be kept after scroll ( Exception is when it scrolls beyond the beginning or end of document) - NSRect currentInsertionRect = [self xvim_boundingRectForGlyphIndex:self.insertionPoint]; - NSPoint relativeInsertionPoint = SubPoint(currentInsertionRect.origin, visibleRect.origin); - //TRACE_LOG(@"Rect:%f %f realIndex:%d foldedIndex:%d", currentInsertionRect.origin.x, currentInsertionRect.origin.y, self.insertionPoint, index); - - // Cursor Position after scroll - NSPoint cursorAfterScroll = AddPoint(scrollPoint,relativeInsertionPoint); - - // Nearest character index to the cursor position after scroll - // TODO: consider blank-EOF line. Xcode does not return blank-EOF index with following method... - NSUInteger cursorIndexAfterScroll= [self xvim_glyphIndexForPoint:cursorAfterScroll]; - - // We do not want to change the insert point relative position from top of visible rect - // We have to calc the distance between insertion point befor/after scrolling to keep the position. - NSRect insertionRectAfterScroll = [self xvim_boundingRectForGlyphIndex:cursorIndexAfterScroll]; - NSPoint relativeInsertionPointAfterScroll = SubPoint(insertionRectAfterScroll.origin, scrollPoint); - CGFloat heightDiff = relativeInsertionPointAfterScroll.y - relativeInsertionPoint.y; - scrollPoint.y += heightDiff; - // Prohibit scroll beyond the bounds of document - if( scrollPoint.y > [[scrollView documentView] frame].size.height - visibleRect.size.height ){ - scrollPoint.y = [[scrollView documentView] frame].size.height - visibleRect.size.height ; - } else if (scrollPoint.y < 0.0) { - scrollPoint.y = 0.0; - } - - [[scrollView contentView] scrollToPoint:scrollPoint]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; - - cursorIndexAfterScroll = [self.textStorage.xvim_buffer firstNonblankInLineAtIndex:cursorIndexAfterScroll allowEOL:YES]; - [self xvim_moveCursor:cursorIndexAfterScroll preserveColumn:NO]; - [self xvim_syncState]; - -} - -- (void)xvim_scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb{ // zb / z- - [self xvim_scrollCommon_moveCursorPos:lineNumber firstNonblank:fnb]; - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(self.insertionPoint,0) inTextContainer:container]; - NSPoint bottom = NSMakePoint(0.0f, NSMidY(glyphRect) + NSHeight(glyphRect) / 2.0f); - bottom.y -= NSHeight([[scrollView contentView] bounds]); - if( bottom.y < 0.0 ){ - bottom.y = 0.0; - } - [[scrollView contentView] scrollToPoint:bottom]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; -} - -- (void)xvim_scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb{ // zz / z. - [self xvim_scrollCommon_moveCursorPos:lineNumber firstNonblank:fnb]; - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(self.insertionPoint,0) inTextContainer:container]; - NSPoint center = NSMakePoint(0.0f, NSMidY(glyphRect) - NSHeight(glyphRect) / 2.0f); - center.y -= NSHeight([[scrollView contentView] bounds]) / 2.0f; - if( center.y < 0.0 ){ - center.y = 0.0; - } - [[scrollView contentView] scrollToPoint:center]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; -} - -- (void)xvim_scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb{ // zt / z - [self xvim_scrollCommon_moveCursorPos:lineNumber firstNonblank:fnb]; - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(self.insertionPoint,0) inTextContainer:container]; - NSPoint top = NSMakePoint(0.0f, NSMidY(glyphRect) - NSHeight(glyphRect) / 2.0f); - [[scrollView contentView] scrollToPoint:top]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; -} - -- (void)xvim_scrollTo:(NSUInteger)location { - // Update: I do not know if we really need Following block. - // It looks that they need it to call ensureLayoutForGlyphRange but do not know when it needed - // What I changed was the way calc "glyphRec". Not its using [self boundingRectForGlyphIndex] which coniders - // text folding when calc the rect. - /* - BOOL isBlankline = - (location == [[self string] length] || isNewline([[self string] characterAtIndex:location])) && - (location == 0 || isNewline([[self string] characterAtIndex:location-1])); - - NSRange characterRange; - characterRange.location = location; - characterRange.length = isBlankline ? 0 : 1; - - // Must call ensureLayoutForGlyphRange: to fix a bug where it will not scroll - // to the appropriate glyph due to non contiguous layout - NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:characterRange actualCharacterRange:NULL]; - [[self layoutManager] ensureLayoutForGlyphRange:NSMakeRange(0, glyphRange.location + glyphRange.length)]; - */ - - NSScrollView *scrollView = [self enclosingScrollView]; - NSRect glyphRect = [self xvim_boundingRectForGlyphIndex:location]; - - CGFloat glyphLeft = NSMidX(glyphRect) - NSWidth(glyphRect) / 2.0f; - CGFloat glyphRight = NSMidX(glyphRect) + NSWidth(glyphRect) / 2.0f; - - NSRect contentRect = [[scrollView contentView] bounds]; - CGFloat viewLeft = contentRect.origin.x; - CGFloat viewRight = contentRect.origin.x + NSWidth(contentRect); - - NSPoint scrollPoint = contentRect.origin; - if (glyphRight > viewRight){ - scrollPoint.x = glyphLeft - NSWidth(contentRect) / 2.0f; - }else if (glyphLeft < viewLeft){ - scrollPoint.x = glyphRight - NSWidth(contentRect) / 2.0f; - } - - CGFloat glyphBottom = NSMidY(glyphRect) + NSHeight(glyphRect) / 2.0f; - CGFloat glyphTop = NSMidY(glyphRect) - NSHeight(glyphRect) / 2.0f; - - CGFloat viewTop = contentRect.origin.y; - CGFloat viewBottom = contentRect.origin.y + NSHeight(contentRect); - - if (glyphTop < viewTop){ - if (viewTop - glyphTop > NSHeight(contentRect)){ - scrollPoint.y = glyphBottom - NSHeight(contentRect) / 2.0f; - }else{ - scrollPoint.y = glyphTop; - } - }else if (glyphBottom > viewBottom){ - if (glyphBottom - viewBottom > NSHeight(contentRect)) { - scrollPoint.y = glyphBottom - NSHeight(contentRect) / 2.0f; - }else{ - scrollPoint.y = glyphBottom - NSHeight(contentRect); - } - } - - scrollPoint.x = MAX(0, scrollPoint.x); - scrollPoint.y = MAX(0, scrollPoint.y); - - [[scrollView contentView] scrollToPoint:scrollPoint]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; -} - -- (void)xvim_pageForward:(NSUInteger)index count:(NSUInteger)count { // C-f - [self xvim_scroll:1.0 count:count]; -} - -- (void)xvim_pageBackward:(NSUInteger)index count:(NSUInteger)count { // C-b - [self xvim_scroll:-1.0 count:count]; -} - -- (void)xvim_halfPageForward:(NSUInteger)index count:(NSUInteger)count { // C-d - [self xvim_scroll:0.5 count:count]; -} - -- (void)xvim_halfPageBackward:(NSUInteger)index count:(NSUInteger)count { // C-u - [self xvim_scroll:-0.5 count:count]; -} - -- (void)xvim_scrollPageForward:(NSUInteger)count{ - [self xvim_pageForward:self.insertionPoint count:count]; -} - -- (void)xvim_scrollPageBackward:(NSUInteger)count{ - [self xvim_pageBackward:self.insertionPoint count:count]; -} - -- (void)xvim_scrollHalfPageForward:(NSUInteger)count{ - [self xvim_halfPageForward:self.insertionPoint count:count]; -} - -- (void)xvim_scrollHalfPageBackward:(NSUInteger)count{ - [self xvim_halfPageBackward:self.insertionPoint count:count]; -} - -- (void)xvim_scrollLineForward:(NSUInteger)count{ - [self xvim_lineDown:self.insertionPoint count:count]; -} - -- (void)xvim_scrollLineBackward:(NSUInteger)count{ - [self xvim_lineUp:self.insertionPoint count:count]; -} - #pragma mark Search // Thanks to http://lists.apple.com/archives/cocoa-dev/2005/Jun/msg01909.html - (NSRange)xvim_visibleRange:(NSTextView *)tv{ @@ -1688,7 +1476,7 @@ - (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)co range = [self.textStorage searchRegexBackward:regex from:self.insertionPoint count:count option:opt]; } if( range.location != NSNotFound ){ - [self scrollRectToVisible:[self xvim_boundingRectForGlyphIndex:range.location]]; + [self.xvim_view scrollTo:range.location]; [self showFindIndicatorForRange:range]; } } @@ -1753,26 +1541,6 @@ - (NSRange)xvim_currentWord:(MOTION_OPTION)opt{ } #pragma mark Search Position -/** - * Takes point in view and returns its index. - * This method automatically convert the "folded index" to "real index" - * When some characters are folded( like placeholders) the pure index for a specifix point is - * less than real index in the string. - **/ -- (NSUInteger)xvim_glyphIndexForPoint:(NSPoint)point { - return [[self layoutManager] glyphIndexForPoint:point inTextContainer:[self textContainer]]; -} - -- (NSRect)xvim_boundingRectForGlyphIndex:(NSUInteger)glyphIndex { - NSRect glyphRect; - if( [self.textStorage isEOF:glyphIndex] ){ - // When the index is EOF the range to specify here can not be grater than 0. If it is greater than 0 it returns (0,0) as a glyph rect. - glyphRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(glyphIndex, 0) inTextContainer:[self textContainer]]; - }else{ - glyphRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(glyphIndex, 1) inTextContainer:[self textContainer]]; - } - return glyphRect; -} /** *Find and return an NSArray* with the placeholders in a current line. @@ -1889,7 +1657,7 @@ - (void)xvim_syncState{ [self setSelectedRanges:[self xvim_selectedRanges] affinity:NSSelectionAffinityDownstream stillSelecting:NO]; [(DVTFoldingTextStorage*)self.textStorage decreaseUsingFoldedRanges]; #endif - [self xvim_scrollTo:self.insertionPoint]; + [self.xvim_view scrollTo:self.insertionPoint]; self.xvim_lockSyncStateFromView = NO; } @@ -1932,7 +1700,8 @@ - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion{ NSUInteger start = NSNotFound; NSTextStorage *ts = self.textStorage; XVimBuffer *buffer = ts.xvim_buffer; - + XVimView *xview = self.xvim_view; + switch (motion.motion) { case MOTION_NONE: // Do nothing @@ -2037,13 +1806,16 @@ - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion{ end = [buffer indexOfLineNumber:[buffer numberOfLines] column:self.preservedColumn]; break; case MOTION_HOME: - end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:[self xvim_lineNumberFromTop:motion.count]] allowEOL:YES]; + tmpPos = [xview lineNumberInScrollView:0.0 offset:motion.scount - 1]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; break; case MOTION_MIDDLE: - end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:[self xvim_lineNumberAtMiddle]] allowEOL:YES]; + tmpPos = [xview lineNumberInScrollView:0.5 offset:0]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; break; case MOTION_BOTTOM: - end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:[self xvim_lineNumberFromBottom:motion.count]] allowEOL:YES]; + tmpPos = [xview lineNumberInScrollView:1.0 offset:1 - motion.scount]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; break; case MOTION_SEARCH_FORWARD: end = [ts searchRegexForward:motion.regex from:self.insertionPoint count:motion.count option:motion.option].location; @@ -2173,57 +1945,6 @@ - (void)xvim_indentCharacterRange:(NSRange)range{ } #pragma mark scrolling -// This is used by scrollBottom,Top,Center as a common method -- (void)xvim_scrollCommon_moveCursorPos:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb{ - if( lineNumber != 0 ){ - NSUInteger pos = [self.textStorage.xvim_buffer indexOfLineNumber:lineNumber]; - if( NSNotFound == pos ){ - pos = self.textStorage.length; - } - [self xvim_moveCursor:pos preserveColumn:NO]; - [self xvim_syncState]; - } - if( fnb ){ - NSUInteger pos = [self.textStorage.xvim_buffer firstNonblankInLineAtIndex:self.insertionPoint allowEOL:YES]; - [self xvim_moveCursor:pos preserveColumn:NO]; - [self xvim_syncState]; - } -} - -- (NSUInteger)xvim_lineNumberFromBottom:(NSUInteger)count { // L - NSAssert( 0 != count , @"count starts from 1" ); - if( count > [self xvim_numberOfLinesInVisibleRect] ){ - count = [self xvim_numberOfLinesInVisibleRect]; - } - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:[self selectedRange] inTextContainer:container]; - NSPoint bottom = [[scrollView contentView] bounds].origin; - // This calculate the position of the bottom line and substruct height of "count" of lines to upwards - bottom.y += [[scrollView contentView] bounds].size.height - (NSHeight(glyphRect) / 2.0f) - (NSHeight(glyphRect) * (count-1)); - return [self.textStorage.xvim_buffer lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:bottom]]; -} - -- (NSUInteger)xvim_lineNumberAtMiddle{ - NSScrollView *scrollView = [self enclosingScrollView]; - NSPoint center = [[scrollView contentView] bounds].origin; - center.y += [[scrollView contentView] bounds].size.height / 2; - return [self.textStorage.xvim_buffer lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:center]]; -} - -- (NSUInteger)xvim_lineNumberFromTop:(NSUInteger)count{ - NSAssert( 0 != count , @"count starts from 1" ); - if( count > [self xvim_numberOfLinesInVisibleRect] ){ - count = [self xvim_numberOfLinesInVisibleRect]; - } - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:[self selectedRange] inTextContainer:container]; - NSPoint top = [[scrollView contentView] bounds].origin; - // Add height of "count" of lines to downwards - top.y += (NSHeight(glyphRect) / 2.0f) + (NSHeight(glyphRect) * (count-1)); - return [self.textStorage.xvim_buffer lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:top]]; -} - (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward{ NSRange ret = NSMakeRange(NSNotFound, 0); diff --git a/XVim/XVimCommandLineEvaluator.m b/XVim/XVimCommandLineEvaluator.m index 03fdfd7e..019546d8 100644 --- a/XVim/XVimCommandLineEvaluator.m +++ b/XVim/XVimCommandLineEvaluator.m @@ -138,7 +138,7 @@ - (XVimEvaluator*)defaultNextEvaluatorInWindow:(XVimWindow*)window{ return nil; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ return 0.0; } @@ -167,8 +167,7 @@ - (XVimEvaluator*)CR{ } - (XVimEvaluator*)ESC{ - NSTextView *sourceView = [self sourceView]; - [sourceView xvim_scrollTo:[sourceView insertionPoint]]; + [self.currentView scrollTo:self.sourceView.insertionPoint]; return nil; } diff --git a/XVim/XVimEvaluator.h b/XVim/XVimEvaluator.h index 4b85e8e0..83ec6244 100644 --- a/XVim/XVimEvaluator.h +++ b/XVim/XVimEvaluator.h @@ -117,9 +117,9 @@ XVimMotionEvaluator **/ - (void)didEndHandler NS_REQUIRES_SUPER; - (XVimEvaluator*)defaultNextEvaluator; -- (float)insertionPointHeightRatio; -- (float)insertionPointWidthRatio; -- (float)insertionPointAlphaRatio; +- (CGFloat)insertionPointHeightRatio; +- (CGFloat)insertionPointWidthRatio; +- (CGFloat)insertionPointAlphaRatio; - (NSString*)modeString; - (XVIM_MODE)mode; @@ -127,7 +127,8 @@ XVimMotionEvaluator - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider; -- (NSTextView*)sourceView; +- (NSTextView *)sourceView; +- (XVimView *)currentView; - (void)resetCompletionHandler; diff --git a/XVim/XVimEvaluator.m b/XVim/XVimEvaluator.m index 298d0bdc..2ec58256 100644 --- a/XVim/XVimEvaluator.m +++ b/XVim/XVimEvaluator.m @@ -76,6 +76,11 @@ - (void)dealloc{ [super dealloc]; } +- (XVimView *)currentView +{ + return self.window.currentView; +} + - (NSTextView *)sourceView { return self.window.currentView.textView; @@ -123,15 +128,15 @@ - (XVimEvaluator*)defaultNextEvaluator{ return [XVimEvaluator invalidEvaluator]; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ return 1.0; } -- (float)insertionPointWidthRatio{ +- (CGFloat)insertionPointWidthRatio{ return 1.0; } -- (float)insertionPointAlphaRatio{ +- (CGFloat)insertionPointAlphaRatio{ return 0.5; } diff --git a/XVim/XVimExCommand.m b/XVim/XVimExCommand.m index 30f06dac..d50548d1 100644 --- a/XVim/XVimExCommand.m +++ b/XVim/XVimExCommand.m @@ -816,8 +816,9 @@ - (void)executeCommand:(NSString*)cmd inWindow:(XVimWindow*)window // Actual parsing is done in following method. XVimExArg* exarg = [self parseCommand:cmd inWindow:window]; if( exarg.cmd == nil ) { - NSTextView* srcView = window.currentView.textView; - XVimBuffer *buffer = window.currentBuffer; + XVimBuffer *buffer = window.currentBuffer; + XVimView *xview = window.currentView; + NSTextView *srcView = xview.textView; // Jump to location NSUInteger pos = [buffer indexOfLineNumber:exarg.lineBegin column:0]; @@ -829,7 +830,7 @@ - (void)executeCommand:(NSString*)cmd inWindow:(XVimWindow*)window pos_wo_space = pos; } [srcView setSelectedRange:NSMakeRange(pos_wo_space,0)]; - [srcView xvim_scrollTo:[window.currentView.textView insertionPoint]]; + [xview scrollTo:srcView.insertionPoint]; return; } diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index 1d3a4bd5..dca37b06 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -96,21 +96,21 @@ - (void)becameHandler{ self.startRange = [[self sourceView] selectedRange]; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ if(_oneCharMode){ return 0.25; } return 1.0; } -- (float)insertionPointWidthRatio{ +- (CGFloat)insertionPointWidthRatio{ if(_oneCharMode){ return 1.0; } return 0.15; } -- (float)insertionPointAlphaRatio{ +- (CGFloat)insertionPointAlphaRatio{ if(_oneCharMode){ return 0.5; } diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index 43fc75be..95b27818 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -87,7 +87,7 @@ - (XVimEvaluator*)C_a{ // This is not motion but scroll. That's the reason the implementation is here. - (XVimEvaluator*)C_b{ - [[self sourceView] xvim_scrollPageBackward:[self numericArg]]; + [self.currentView scrollPageBackward:self.numericArg]; return nil; } @@ -107,7 +107,7 @@ - (XVimEvaluator*)C{ // This is not motion but scroll. That's the reason the implementation is here. - (XVimEvaluator*)C_d{ - [[self sourceView] xvim_scrollHalfPageForward:[self numericArg]]; + [self.currentView scrollHalfPageForward:self.numericArg]; return nil; } @@ -125,13 +125,13 @@ - (XVimEvaluator*)D{ } - (XVimEvaluator*)C_e{ - [[self sourceView] xvim_scrollLineForward:[self numericArg]]; + [self.currentView scrollLineForward:self.numericArg]; return nil; } // This is not motion but scroll. That's the reason the implementation is here. - (XVimEvaluator*)C_f{ - [[self sourceView] xvim_scrollPageForward:[self numericArg]]; + [self.currentView scrollPageForward:self.numericArg]; return nil; } @@ -150,7 +150,7 @@ - (XVimEvaluator*)C_g if ([documentURL isFileURL]) { NSString *text = [NSString stringWithFormat:@"%@ line %ld of %ld --%d%%-- col %ld", documentURL.path, lineNumber, numberOfLines, - (int)((float)lineNumber*100.0/(float)numberOfLines), columnNumber+1 ]; + (int)((CGFloat)lineNumber*100.0/(CGFloat)numberOfLines), columnNumber+1 ]; [window statusMessage:text]; } @@ -295,7 +295,7 @@ - (XVimEvaluator*)u{ // This is not motion but scroll. That's the reason the implementation is here. - (XVimEvaluator*)C_u{ - [[self sourceView] xvim_scrollHalfPageBackward:[self numericArg]]; + [self.currentView scrollHalfPageBackward:self.numericArg]; return nil; } @@ -365,7 +365,7 @@ - (XVimEvaluator*)y{ } - (XVimEvaluator*)C_y{ - [[self sourceView] xvim_scrollLineBackward:[self numericArg]]; + [self.currentView scrollLineBackward:self.numericArg]; return nil; } diff --git a/XVim/XVimOperatorEvaluator.m b/XVim/XVimOperatorEvaluator.m index d7e87901..ce80cb13 100644 --- a/XVim/XVimOperatorEvaluator.m +++ b/XVim/XVimOperatorEvaluator.m @@ -36,7 +36,7 @@ - (void)dealloc { [super dealloc]; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ return 0.5; } diff --git a/XVim/XVimRecordingEvaluator.m b/XVim/XVimRecordingEvaluator.m index 9408e90a..86bac170 100644 --- a/XVim/XVimRecordingEvaluator.m +++ b/XVim/XVimRecordingEvaluator.m @@ -48,15 +48,15 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ return self; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ return [[self.evaluatorStack lastObject] insertionPointHeightRatio]; } -- (float)insertionPointWidthRatio{ +- (CGFloat)insertionPointWidthRatio{ return [[self.evaluatorStack lastObject] insertionPointWidthRatio]; } -- (float)insertionPointAlphaRatio{ +- (CGFloat)insertionPointAlphaRatio{ return [[self.evaluatorStack lastObject] insertionPointAlphaRatio]; } diff --git a/XVim/XVimTester.m b/XVim/XVimTester.m index ee550345..9706ad39 100644 --- a/XVim/XVimTester.m +++ b/XVim/XVimTester.m @@ -278,7 +278,7 @@ -(NSInteger) getIndexOfNthFailingTestcase:(NSInteger)nth{ return retval; } -- (float)heightForString:(NSString*)myString withFont:(NSFont*)myFont withWidth:(float)myWidth{ +- (CGFloat)heightForString:(NSString*)myString withFont:(NSFont*)myFont withWidth:(CGFloat)myWidth{ NSTextStorage *textStorage = [[[NSTextStorage alloc] initWithString:myString] autorelease]; NSTextContainer *textContainer = [[[NSTextContainer alloc] initWithContainerSize:NSMakeSize(myWidth, FLT_MAX)] autorelease]; NSLayoutManager *layoutManager = [[[NSLayoutManager alloc] init] autorelease]; @@ -300,7 +300,7 @@ - (CGFloat)tableView:(NSTableView *)tv heightOfRow:(NSInteger)row{ NSCell* cell = (NSCell*)[column dataCell]; font = [NSFont fontWithName:@"Menlo" size:13]; [cell setFont:font]; // FIXME: This should not be done here. - float width = column.width; + CGFloat width = column.width; NSString* msg; if(showPassing){ msg = ((XVimTestCase*)[self.testCases objectAtIndex:(NSUInteger)row]).message; @@ -311,7 +311,7 @@ - (CGFloat)tableView:(NSTableView *)tv heightOfRow:(NSInteger)row{ if( nil == msg || [msg isEqualToString:@""] ){ msg = @" "; } - float ret = [self heightForString:msg withFont:font withWidth:width]; + CGFloat ret = [self heightForString:msg withFont:font withWidth:width]; return ret + 10; } return 13.0; diff --git a/XVim/XVimTextObjectEvaluator.m b/XVim/XVimTextObjectEvaluator.m index b5b8d691..12661722 100644 --- a/XVim/XVimTextObjectEvaluator.m +++ b/XVim/XVimTextObjectEvaluator.m @@ -52,7 +52,7 @@ - (void)didEndHandler [super didEndHandler]; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ return 0.5; } diff --git a/XVim/XVimView.h b/XVim/XVimView.h index 2f6f28c4..bdfb1733 100644 --- a/XVim/XVimView.h +++ b/XVim/XVimView.h @@ -33,4 +33,26 @@ - (instancetype)initWithView:(NSTextView *)view window:(XVimWindow *)window; +#pragma mark *** Drawing *** + +- (NSUInteger)lineNumberInScrollView:(CGFloat)ratio offset:(NSInteger)offset; + +- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color + heightRatio:(CGFloat)heightRatio + widthRatio:(CGFloat)widthRatio + alpha:(CGFloat)alpha; + +#pragma mark *** Scrolling *** + +- (void)scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; +- (void)scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; +- (void)scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; +- (void)scrollTo:(NSUInteger)location; +- (void)scrollPageForward:(NSUInteger)count; +- (void)scrollPageBackward:(NSUInteger)count; +- (void)scrollHalfPageForward:(NSUInteger)count; +- (void)scrollHalfPageBackward:(NSUInteger)count; +- (void)scrollLineForward:(NSUInteger)count; +- (void)scrollLineBackward:(NSUInteger)count; + @end diff --git a/XVim/XVimView.m b/XVim/XVimView.m index ed7320ee..9f873f5a 100644 --- a/XVim/XVimView.m +++ b/XVim/XVimView.m @@ -15,7 +15,28 @@ #import "NSTextView+VimOperation.h" #import "XVimOptions.h" #import "XVimSearch.h" +#import "Utils.h" +@interface XVimView () + +- (NSRect)_glyphRectAtIndex:(NSUInteger)index length:(NSUInteger)length; + +@end + +// disgusting copy, get rid of it eventually +@interface NSTextView(VimOperationPrivate) +@property BOOL xvim_lockSyncStateFromView; +- (void)xvim_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve; +- (void)xvim_syncState; // update self's properties with our variables +- (NSArray*)xvim_selectedRanges; +- (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion; +- (NSRange)xvim_getOperationRangeFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type; +- (void)xvim_indentCharacterRange:(NSRange)range; +- (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward; +- (void)xvim_swapCaseForRange:(NSRange)range; +- (void)xvim_registerInsertionPointForUndo; +- (void)xvim_registerIndexForUndo:(NSUInteger)index; +@end static char const * const XVIM_KEY_VIEW = "xvim_view"; @implementation NSTextView (XVimView) @@ -194,7 +215,7 @@ - (void)xvim_drawRect:(NSRect)dirtyRect if (self.selectionMode != XVIM_VISUAL_NONE) { // NSTextView does not draw insertion point when selecting text. // We have to draw insertion point by ourselves. - NSRect glyphRect = [self xvim_boundingRectForGlyphIndex:self.insertionPoint]; + NSRect glyphRect = [self.xvim_view _glyphRectAtIndex:self.insertionPoint length:1]; [[[self insertionPointColor] colorWithAlphaComponent:0.5] set]; NSRectFillUsingOperation(glyphRect, NSCompositeSourceOver); } @@ -321,4 +342,283 @@ - (void)_xvim_statusChanged:(id)sender [_textView setNeedsDisplay:YES]; } +#pragma mark *** Drawing *** + +- (NSRect)_glyphRectAtIndex:(NSUInteger)index length:(NSUInteger)length +{ + if (length && index + length >= _textView.textStorage.length) { + // When the index is EOF the range to specify here can not be grater than 0. + // If it is greater than 0 it returns (0,0) as a glyph rect. + length = 0; + } + return [_textView.layoutManager boundingRectForGlyphRange:NSMakeRange(index, length) + inTextContainer:_textView.textContainer]; +} + +- (NSUInteger)_glyphHeightAtIndex:(NSUInteger)index +{ + return NSHeight([self _glyphRectAtIndex:index length:0]); +} + +- (NSUInteger)_lineNumberAtPoint:(NSPoint)point +{ + NSUInteger index; + + index = [_textView.enclosingScrollView.documentView characterIndexForInsertionAtPoint:point]; + return [_textView.textStorage.xvim_buffer lineNumberAtIndex:index]; +} + +- (NSUInteger)lineNumberInScrollView:(CGFloat)ratio offset:(NSInteger)offset +{ + NSScrollView *scrollView = _textView.enclosingScrollView; + NSRect visibleRect = scrollView.contentView.bounds; + NSPoint point = visibleRect.origin; + CGFloat glyphHeight = [self _glyphHeightAtIndex:_textView.insertionPoint]; + + NSInteger minLine, line, maxLine; + + minLine = (NSInteger)[self _lineNumberAtPoint:point]; + point.y += ratio * (NSHeight(visibleRect) - glyphHeight); + line = (NSInteger)[self _lineNumberAtPoint:point]; + point.y = NSMaxY(visibleRect) - glyphHeight; + maxLine = (NSInteger)[self _lineNumberAtPoint:point]; + + return (NSUInteger)MIN(maxLine, MAX(minLine, line + offset)); +} + +- (NSUInteger)_glyphIndexForPoint:(NSPoint)point +{ + return [_textView.layoutManager glyphIndexForPoint:point inTextContainer:_textView.textContainer]; +} + +- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color + heightRatio:(CGFloat)heightRatio + widthRatio:(CGFloat)widthRatio + alpha:(CGFloat)alpha +{ + NSUInteger glyphIndex = [_textView insertionPoint]; + NSRect glyphRect = [self _glyphRectAtIndex:glyphIndex length:1]; + + [[color colorWithAlphaComponent:alpha] set]; + rect.size.width = rect.size.height/2; + if (glyphRect.size.width > 0 && glyphRect.size.width < rect.size.width) { + rect.size.width = glyphRect.size.width; + } + + rect.origin.y += (1 - heightRatio) * rect.size.height; + rect.size.height *= heightRatio; + rect.size.width *= widthRatio; + + NSRectFillUsingOperation(rect, NSCompositeSourceOver); +} + +#pragma mark *** Scrolling *** + +- (void)_lineUp:(NSUInteger)index count:(NSUInteger)count +{ + [_textView scrollLineUp:_textView]; + + NSRect visibleRect = _textView.enclosingScrollView.contentView.bounds; + NSRect cursorRect = [self _glyphRectAtIndex:index length:0]; + if (NSMaxY(cursorRect) > NSMaxY(visibleRect)) { + [_textView moveUp:self]; + } +} + +- (void)scrollLineBackward:(NSUInteger)count +{ + [self _lineUp:_textView.insertionPoint count:count]; +} + +- (void)_lineDown:(NSUInteger)index count:(NSUInteger)count +{ + [_textView scrollLineDown:_textView]; + + NSRect visibleRect = _textView.enclosingScrollView.contentView.bounds; + NSRect cursorRect = [self _glyphRectAtIndex:index length:0]; + if (NSMinY(cursorRect) < NSMinY(visibleRect)) { + [_textView moveDown:_textView]; + } +} + +- (void)scrollLineForward:(NSUInteger)count +{ + [self _lineDown:_textView.insertionPoint count:count]; +} + +- (void)_scroll:(CGFloat)ratio count:(NSUInteger)count +{ + NSScrollView *scrollView = _textView.enclosingScrollView; + NSClipView *clipView = scrollView.contentView; + XVimBuffer *buffer = _textView.textStorage.xvim_buffer; + + NSRect visibleRect = clipView.bounds; + CGFloat scrollSize = NSHeight(visibleRect) * ratio * count; + // This may be beyond the beginning or end of document (intentionally) + NSPoint scrollPoint = NSMakePoint(visibleRect.origin.x, visibleRect.origin.y + scrollSize); + + // Cursor position relative to left-top origin shold be kept after scroll + // (Exception is when it scrolls beyond the beginning or end of document) + + NSRect currentInsertionRect = [self _glyphRectAtIndex:_textView.insertionPoint length:1]; + NSPoint relativeInsertionPoint = SubPoint(currentInsertionRect.origin, visibleRect.origin); + + // Cursor Position after scroll + NSPoint cursorAfterScroll = AddPoint(scrollPoint, relativeInsertionPoint); + + // Nearest character index to the cursor position after scroll + // TODO: consider blank-EOF line. Xcode does not return blank-EOF index with following method... + NSUInteger cursorIndexAfterScroll = [self _glyphIndexForPoint:cursorAfterScroll]; + + // We do not want to change the insert point relative position from top of visible rect + // We have to calc the distance between insertion point befor/after scrolling to keep the position. + NSRect insertionRectAfterScroll = [self _glyphRectAtIndex:cursorIndexAfterScroll length:1]; + NSPoint relativeInsertionPointAfterScroll = SubPoint(insertionRectAfterScroll.origin, scrollPoint); + CGFloat maxScrollY = NSHeight([scrollView.documentView frame]) - NSHeight(visibleRect); + + scrollPoint.y += relativeInsertionPointAfterScroll.y - relativeInsertionPoint.y; + if (scrollPoint.y > maxScrollY) { + // Prohibit scroll beyond the bounds of document + scrollPoint.y = maxScrollY; + } else if (scrollPoint.y < 0.0) { + scrollPoint.y = 0.0; + } + + [[scrollView contentView] scrollToPoint:scrollPoint]; + [scrollView reflectScrolledClipView:[scrollView contentView]]; + + cursorIndexAfterScroll = [buffer firstNonblankInLineAtIndex:cursorIndexAfterScroll allowEOL:YES]; + [_textView xvim_moveCursor:cursorIndexAfterScroll preserveColumn:NO]; + [_textView xvim_syncState]; +} + +- (void)scrollPageForward:(NSUInteger)count +{ + [self _scroll:1.0 count:count]; +} + +- (void)scrollPageBackward:(NSUInteger)count +{ + [self _scroll:-1.0 count:count]; +} + +- (void)scrollHalfPageForward:(NSUInteger)count +{ + [self _scroll:0.5 count:count]; +} + +- (void)scrollHalfPageBackward:(NSUInteger)count +{ + [self _scroll:-0.5 count:count]; +} + +- (void)_scrollCommon_moveCursorPos:(NSUInteger)lineNumber + ratio:(CGFloat)ratio + firstNonblank:(BOOL)fnb +{ + XVimBuffer *buffer = _textView.textStorage.xvim_buffer; + NSUInteger pos = _textView.insertionPoint; + + if (lineNumber) { + if ((pos = [buffer indexOfLineNumber:lineNumber]) == NSNotFound) { + pos = buffer.length; + } + } + if (fnb) { + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + [_textView xvim_moveCursor:pos preserveColumn:NO]; + [_textView xvim_syncState]; + + NSRect glyphRect = [self _glyphRectAtIndex:_textView.insertionPoint length:0]; + + NSScrollView *scrollView = _textView.enclosingScrollView; + NSClipView *clipView = scrollView.contentView; + + NSPoint point = NSMakePoint(0., NSMaxY(glyphRect)); + CGFloat deltay = ratio * NSHeight(_textView.enclosingScrollView.contentView.bounds); + point.y = point.y > deltay ? point.y - deltay : 0.; + + [clipView scrollToPoint:point]; + [scrollView reflectScrolledClipView:clipView]; +} + +- (void)scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zb / z- +{ + [self _scrollCommon_moveCursorPos:lineNumber ratio:1. firstNonblank:fnb]; +} + +- (void)scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zz / z. +{ + [self _scrollCommon_moveCursorPos:lineNumber ratio:.5 firstNonblank:fnb]; +} + +- (void)scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zt / z +{ + [self _scrollCommon_moveCursorPos:lineNumber ratio:0. firstNonblank:fnb]; +} + +- (void)scrollTo:(NSUInteger)location +{ + // Update: I do not know if we really need Following block. + // It looks that they need it to call ensureLayoutForGlyphRange but do not know when it needed + // What I changed was the way calc "glyphRec". Not its using [self boundingRectForGlyphIndex] which coniders + // text folding when calc the rect. + /* + BOOL isBlankline = + (location == [[self string] length] || isNewline([[self string] characterAtIndex:location])) && + (location == 0 || isNewline([[self string] characterAtIndex:location-1])); + + NSRange characterRange; + characterRange.location = location; + characterRange.length = isBlankline ? 0 : 1; + + // Must call ensureLayoutForGlyphRange: to fix a bug where it will not scroll + // to the appropriate glyph due to non contiguous layout + NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:characterRange actualCharacterRange:NULL]; + [[self layoutManager] ensureLayoutForGlyphRange:NSMakeRange(0, glyphRange.location + glyphRange.length)]; + */ + + NSScrollView *scrollView = _textView.enclosingScrollView; + NSClipView *clipView = scrollView.contentView; + + NSRect glyphRect = [self _glyphRectAtIndex:location length:1]; + CGFloat glyphLeft = NSMinX(glyphRect); + CGFloat glyphRight = NSMaxX(glyphRect); + CGFloat glyphBottom = NSMaxY(glyphRect); + CGFloat glyphTop = NSMinY(glyphRect); + + NSRect contentRect = clipView.bounds; + CGFloat viewLeft = NSMinX(contentRect); + CGFloat viewRight = NSMaxX(contentRect); + CGFloat viewTop = NSMinY(contentRect); + CGFloat viewBottom = NSMaxY(contentRect); + + NSPoint scrollPoint = contentRect.origin; + if (glyphRight > viewRight) { + scrollPoint.x = glyphLeft - NSWidth(contentRect) / 2.; + } else if (glyphLeft < viewLeft) { + scrollPoint.x = glyphRight - NSWidth(contentRect) / 2.; + } + scrollPoint.x = MAX(0, scrollPoint.x); + + if (glyphTop < viewTop) { + if (viewTop - glyphTop > NSHeight(contentRect)){ + scrollPoint.y = glyphBottom - NSHeight(contentRect) / 2.; + } else { + scrollPoint.y = glyphTop; + } + } else if (glyphBottom > viewBottom) { + if (glyphBottom - viewBottom > NSHeight(contentRect)) { + scrollPoint.y = glyphBottom - NSHeight(contentRect) / 2.; + } else { + scrollPoint.y = glyphBottom - NSHeight(contentRect); + } + } + scrollPoint.y = MAX(0, scrollPoint.y); + + [clipView scrollToPoint:scrollPoint]; + [scrollView reflectScrolledClipView:clipView]; +} + @end diff --git a/XVim/XVimVisualEvaluator.m b/XVim/XVimVisualEvaluator.m index a991f0b0..605db793 100644 --- a/XVim/XVimVisualEvaluator.m +++ b/XVim/XVimVisualEvaluator.m @@ -213,12 +213,12 @@ - (XVimEvaluator *)C{ } - (XVimEvaluator*)C_b{ - [[self sourceView] xvim_scrollPageBackward:[self numericArg]]; + [self.currentView scrollPageBackward:self.numericArg]; return self; } - (XVimEvaluator*)C_d{ - [[self sourceView] xvim_scrollHalfPageForward:[self numericArg]]; + [self.currentView scrollHalfPageForward:self.numericArg]; return self; } @@ -243,7 +243,7 @@ - (XVimEvaluator*)D{ } - (XVimEvaluator*)C_f{ - [[self sourceView] xvim_scrollPageForward:[self numericArg]]; + [self.currentView scrollPageForward:self.numericArg]; return self; } @@ -341,7 +341,7 @@ - (XVimEvaluator*)U{ } - (XVimEvaluator*)C_u{ - [[self sourceView] xvim_scrollHalfPageBackward:[self numericArg]]; + [self.currentView scrollHalfPageBackward:self.numericArg]; return self; } diff --git a/XVim/XVimWindow.h b/XVim/XVimWindow.h index 57dcf6bb..04ab2fb0 100644 --- a/XVim/XVimWindow.h +++ b/XVim/XVimWindow.h @@ -34,7 +34,7 @@ - (void)handleKeyStroke:(XVimKeyStroke*)keyStroke onStack:(NSMutableArray*)stack; - (BOOL)handleKeyEvent:(NSEvent*)event; -- (NSRect)drawInsertionPointInRect:(NSRect)rect color:(NSColor*)color; +- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor*)color; - (void)errorMessage:(NSString *)message ringBell:(BOOL)ringBell; - (void)statusMessage:(NSString*)message; diff --git a/XVim/XVimWindow.m b/XVim/XVimWindow.m index 8c554f86..2ed41ba9 100644 --- a/XVim/XVimWindow.m +++ b/XVim/XVimWindow.m @@ -359,29 +359,15 @@ - (void)syncEvaluatorStack #pragma mark - Visual gimmicks -- (NSRect)drawInsertionPointInRect:(NSRect)rect color:(NSColor*)color +- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor*)color { XVimEvaluator *current = self.currentEvaluator; - float heightRatio = [current insertionPointHeightRatio]; - float widthRatio = [current insertionPointWidthRatio]; - float alphaRatio = [current insertionPointAlphaRatio]; - - NSTextView *sourceView = [self sourceView]; - NSUInteger glyphIndex = [sourceView insertionPoint]; - NSRect glyphRect = [sourceView xvim_boundingRectForGlyphIndex:glyphIndex]; - - [[color colorWithAlphaComponent:alphaRatio] set]; - rect.size.width = rect.size.height/2; - if (glyphRect.size.width > 0 && glyphRect.size.width < rect.size.width) { - rect.size.width = glyphRect.size.width; - } - - rect.origin.y += (1 - heightRatio) * rect.size.height; - rect.size.height *= heightRatio; - rect.size.width *= widthRatio; + CGFloat heightRatio = [current insertionPointHeightRatio]; + CGFloat widthRatio = [current insertionPointWidthRatio]; + CGFloat alphaRatio = [current insertionPointAlphaRatio]; - NSRectFillUsingOperation(rect, NSCompositeSourceOver); - return rect; + [self.currentView drawInsertionPointInRect:rect color:color + heightRatio:heightRatio widthRatio:widthRatio alpha:alphaRatio]; } - (void)errorMessage:(NSString *)message ringBell:(BOOL)ringBell diff --git a/XVim/XVimZEvaluator.m b/XVim/XVimZEvaluator.m index c55c3f44..16af8dab 100644 --- a/XVim/XVimZEvaluator.m +++ b/XVim/XVimZEvaluator.m @@ -13,32 +13,32 @@ @implementation XVimZEvaluator - (XVimEvaluator*)b{ - [self.sourceView xvim_scrollBottom:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; + [self.currentView scrollBottom:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; return nil; } - (XVimEvaluator*)t{ - [self.sourceView xvim_scrollTop:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; + [self.currentView scrollTop:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; return nil; } - (XVimEvaluator*)z{ - [self.sourceView xvim_scrollCenter:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; + [self.currentView scrollCenter:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; return nil; } - (XVimEvaluator*)MINUS{ - [self.sourceView xvim_scrollBottom:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; + [self.currentView scrollBottom:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; return nil; } - (XVimEvaluator*)DOT{ - [self.sourceView xvim_scrollCenter:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; + [self.currentView scrollCenter:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; return nil; } - (XVimEvaluator*)CR{ - [self.sourceView xvim_scrollTop:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; + [self.currentView scrollTop:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; return nil; } From c55fcb657acace956f6f127337e719f40a72f61a Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Sun, 17 Nov 2013 00:08:55 +0100 Subject: [PATCH 23/44] Make the XCode project happier Do not hardcode XCode path to /Applications/Xcode 2.app, and find the framework relative the $DEVELOPER_DIR, which will do what's right. Upgrade warnings for XCode 5 --- XVim.xcodeproj/project.pbxproj | 51 +++++++------------ .../xcschemes/XVim for Xcode4.xcscheme | 2 +- .../xcschemes/XVim for Xcode5.xcscheme | 2 +- 3 files changed, 20 insertions(+), 35 deletions(-) diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index ab9cda25..3a59f879 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -14,13 +14,13 @@ 6E2C3930183E4BA20056E30F /* XVimView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2C392E183E4BA20056E30F /* XVimView.m */; }; 6E6865D618390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; 6E6865D718390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; + 6EE2579E183F81C3006E4E1A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A24782C414D6F6DC003B6433 /* InfoPlist.strings */; }; 6EF220D6183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */; }; 6EF220D7183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */; }; A2126B7516FB30B0000BE21C /* XVimMark.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7416FB30B0000BE21C /* XVimMark.m */; }; A2126B7816FB4806000BE21C /* XVimMarks.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7716FB4806000BE21C /* XVimMarks.m */; }; A216F3A1156560FE00AD2529 /* IDEEditorArea+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A216F3A0156560FE00AD2529 /* IDEEditorArea+XVim.m */; }; A222B5E11514DFCD005E8802 /* XVimOperatorEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A222B5E01514DFCD005E8802 /* XVimOperatorEvaluator.m */; }; - A2243B7017F03BFC00DD92A1 /* Info_Xcode5.plist in Resources */ = {isa = PBXBuildFile; fileRef = A2E7E45517EFF677008F045A /* Info_Xcode5.plist */; }; A2277ED414F80BCB00A6B70C /* XVimDeleteEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2277ED314F80BCB00A6B70C /* XVimDeleteEvaluator.m */; }; A2277ED814F80BFE00A6B70C /* XVimShiftEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2277ED714F80BFE00A6B70C /* XVimShiftEvaluator.m */; }; A2277EDB14F80D5000A6B70C /* XVimYankEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2277EDA14F80D5000A6B70C /* XVimYankEvaluator.m */; }; @@ -138,8 +138,6 @@ A2E7E45B17F037F4008F045A /* IDEFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45A17F037F4008F045A /* IDEFoundation.framework */; }; A2E7E45D17F037FF008F045A /* DVTKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45C17F037FF008F045A /* DVTKit.framework */; }; A2E7E45F17F03802008F045A /* DVTFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45E17F03802008F045A /* DVTFoundation.framework */; }; - A2E7E46117F0380F008F045A /* IDESourceEditor.ideplugin in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E46017F0380F008F045A /* IDESourceEditor.ideplugin */; }; - A2E7E46217F03828008F045A /* IDESourceEditor.ideplugin in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E46017F0380F008F045A /* IDESourceEditor.ideplugin */; }; A2E7E46317F0382E008F045A /* IDEFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45A17F037F4008F045A /* IDEFoundation.framework */; }; A2E7E46417F0382E008F045A /* IDEKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45817F037EF008F045A /* IDEKit.framework */; }; A2E7E46517F03835008F045A /* DVTFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45E17F03802008F045A /* DVTFoundation.framework */; }; @@ -310,11 +308,11 @@ A2D5ADFF17A0172F00430761 /* UserDefaultsList.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = UserDefaultsList.txt; sourceTree = ""; }; A2E7E45217EF0219008F045A /* XVim.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XVim.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; }; A2E7E45517EFF677008F045A /* Info_Xcode5.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info_Xcode5.plist; path = XVim/Info_Xcode5.plist; sourceTree = SOURCE_ROOT; }; - A2E7E45817F037EF008F045A /* IDEKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IDEKit.framework; path = "../../../../Applications/Xcode 2.app/Contents/Frameworks/IDEKit.framework"; sourceTree = ""; }; - A2E7E45A17F037F4008F045A /* IDEFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IDEFoundation.framework; path = "../../../../Applications/Xcode 2.app/Contents/Frameworks/IDEFoundation.framework"; sourceTree = ""; }; - A2E7E45C17F037FF008F045A /* DVTKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DVTKit.framework; path = "../../../../Applications/Xcode 2.app/Contents/SharedFrameworks/DVTKit.framework"; sourceTree = ""; }; - A2E7E45E17F03802008F045A /* DVTFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DVTFoundation.framework; path = "../../../../Applications/Xcode 2.app/Contents/SharedFrameworks/DVTFoundation.framework"; sourceTree = ""; }; - A2E7E46017F0380F008F045A /* IDESourceEditor.ideplugin */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = IDESourceEditor.ideplugin; path = "../../../../Applications/Xcode 2.app/Contents/PlugIns/IDESourceEditor.ideplugin"; sourceTree = ""; }; + A2E7E45817F037EF008F045A /* IDEKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IDEKit.framework; path = ../Frameworks/IDEKit.framework; sourceTree = DEVELOPER_DIR; }; + A2E7E45A17F037F4008F045A /* IDEFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IDEFoundation.framework; path = ../Frameworks/IDEFoundation.framework; sourceTree = DEVELOPER_DIR; }; + A2E7E45C17F037FF008F045A /* DVTKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DVTKit.framework; path = ../SharedFrameworks/DVTKit.framework; sourceTree = DEVELOPER_DIR; }; + A2E7E45E17F03802008F045A /* DVTFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DVTFoundation.framework; path = ../SharedFrameworks/DVTFoundation.framework; sourceTree = DEVELOPER_DIR; }; + A2E7E46017F0380F008F045A /* IDESourceEditor.ideplugin */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = IDESourceEditor.ideplugin; path = ../PlugIns/IDESourceEditor.ideplugin; sourceTree = DEVELOPER_DIR; }; A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "DVTSourceTextScrollView+XVim.h"; path = "XVim/DVTSourceTextScrollView+XVim.h"; sourceTree = SOURCE_ROOT; }; A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DVTSourceTextScrollView+XVim.m"; path = "XVim/DVTSourceTextScrollView+XVim.m"; sourceTree = SOURCE_ROOT; }; A2F4A33C14F00B25006DA5A5 /* XVimNormalEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimNormalEvaluator.h; path = XVim/XVimNormalEvaluator.h; sourceTree = SOURCE_ROOT; }; @@ -377,7 +375,6 @@ files = ( A28F427217EEDBC200A3F7AE /* AppKit.framework in Frameworks */, A28F427317EEDBC200A3F7AE /* Cocoa.framework in Frameworks */, - A2E7E46117F0380F008F045A /* IDESourceEditor.ideplugin in Frameworks */, A2E7E45D17F037FF008F045A /* DVTKit.framework in Frameworks */, A2E7E45F17F03802008F045A /* DVTFoundation.framework in Frameworks */, A2E7E45917F037EF008F045A /* IDEKit.framework in Frameworks */, @@ -395,7 +392,6 @@ A2E7E46617F03835008F045A /* DVTKit.framework in Frameworks */, A2E7E46317F0382E008F045A /* IDEFoundation.framework in Frameworks */, A2E7E46417F0382E008F045A /* IDEKit.framework in Frameworks */, - A2E7E46217F03828008F045A /* IDESourceEditor.ideplugin in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -523,6 +519,7 @@ A223956116653ACD00CD26AE /* Documents */, A2C4E78F14E7EA0B00751199 /* Support */, A2F41B0F14D70BD000A0314F /* Debug */, + C38A5B481527C9EE00E1448D /* Xcode Class Definitions */, A2B4BAC114D59F6600D817B0 /* XVim */, A2B4BABA14D59F6600D817B0 /* Frameworks */, A234821814E7E26A003E0FF3 /* XVim.xcplugin */, @@ -567,7 +564,6 @@ C345DDB7154CCF1F009F232E /* Xcode Dependent Classes */, C345DDB4154CCE5E009F232E /* XCode Independent Classes */, A26DC88315DF339A00779CB4 /* Testing */, - C38A5B481527C9EE00E1448D /* Xcode Class Definitions */, A2B4BAC214D59F6600D817B0 /* Supporting Files */, ); name = XVim; @@ -761,6 +757,7 @@ A2C55FC817EE9F29000EBEA7 /* Xcode4 */, ); name = "Xcode Class Definitions"; + path = XVim_lite2; sourceTree = ""; }; /* End PBXGroup section */ @@ -808,7 +805,7 @@ A2B4BAAF14D59F6600D817B0 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0460; + LastUpgradeCheck = 0500; }; buildConfigurationList = A2B4BAB214D59F6600D817B0 /* Build configuration list for PBXProject "XVim" */; compatibilityVersion = "Xcode 3.2"; @@ -833,7 +830,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - A2243B7017F03BFC00DD92A1 /* Info_Xcode5.plist in Resources */, + 6EE2579E183F81C3006E4E1A /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1026,11 +1023,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/Frameworks", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/SharedFrameworks", - ); GCC_ENABLE_OBJC_GC = supported; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "XVim/XVim-Prefix.pch"; @@ -1053,11 +1045,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/Frameworks", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/SharedFrameworks", - ); GCC_ENABLE_OBJC_GC = supported; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "XVim/XVim-Prefix.pch"; @@ -1077,7 +1064,9 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; @@ -1100,9 +1089,11 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(inherited)"; MACOSX_DEPLOYMENT_TARGET = 10.6; @@ -1117,7 +1108,9 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; @@ -1135,9 +1128,11 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(inherited)"; MACOSX_DEPLOYMENT_TARGET = 10.6; @@ -1151,11 +1146,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/Frameworks", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/SharedFrameworks", - ); GCC_ENABLE_OBJC_GC = supported; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "XVim/XVim-Prefix.pch"; @@ -1178,11 +1168,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/Frameworks", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/SharedFrameworks", - ); GCC_ENABLE_OBJC_GC = supported; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "XVim/XVim-Prefix.pch"; diff --git a/XVim.xcodeproj/xcshareddata/xcschemes/XVim for Xcode4.xcscheme b/XVim.xcodeproj/xcshareddata/xcschemes/XVim for Xcode4.xcscheme index e87551b9..15bc299b 100644 --- a/XVim.xcodeproj/xcshareddata/xcschemes/XVim for Xcode4.xcscheme +++ b/XVim.xcodeproj/xcshareddata/xcschemes/XVim for Xcode4.xcscheme @@ -1,6 +1,6 @@ Date: Fri, 22 Nov 2013 13:20:39 +0100 Subject: [PATCH 24/44] To help debugging: log exceptions to the console right away It's been a huge help for development for me. --- XVim/Logger.m | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/XVim/Logger.m b/XVim/Logger.m index a623a4e1..60cb68ce 100644 --- a/XVim/Logger.m +++ b/XVim/Logger.m @@ -6,6 +6,7 @@ // Copyright 2011 JugglerShu.Net. All rights reserved. // +#import "XVim.h" #import "Logger.h" #import #import @@ -157,9 +158,21 @@ - (void) setLogFile:(NSString *)path{ } + (void) logStackTrace:(NSException*)ex{ + NSMutableString *s = [[NSMutableString alloc] init]; + + [s appendFormat:@"Exception %@ thrown { reason = %@ }\n", ex.name, ex.reason]; + [ex.userInfo enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [s appendFormat:@" %@: %@\n", key, obj]; + }]; + [s appendFormat:@"Call Stack:\n"]; for( NSString* e in [ex callStackSymbols]){ TRACE_LOG(@"%@", e); + [s appendFormat:@" %@\n", e]; } +#ifdef DEBUG + [[XVim instance] writeToConsole:s]; +#endif + [s release]; } + (void) traceMethodList:(NSString*)class{ From 7533e0856127824278f4ff31201516c13245d455 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Fri, 22 Nov 2013 14:42:10 +0100 Subject: [PATCH 25/44] Refactor character swaps operators into XVimBuffer Fold the Tilde/Lower/Upper (and Rot13) into a single SwapChar evaluator. Fix various repeat issues with those. Use it in the normal, visual, GAction and GVisual evaluators as required. This obviously uses the new undo architecture, hence should be faster --- XVim.xcodeproj/project.pbxproj | 32 +--- XVim/NSString+VimHelper.h | 4 + XVim/NSString+VimHelper.m | 21 +++ XVim/NSTextView+VimOperation.h | 4 +- XVim/NSTextView+VimOperation.m | 141 +++++------------- XVim/XVimBuffer.h | 9 +- XVim/XVimBuffer.m | 82 ++++++++++ XVim/XVimGActionEvaluator.m | 15 +- XVim/XVimGVisualEvaluator.m | 26 +++- XVim/XVimLowercaseEvaluator.m | 30 ---- XVim/XVimNormalEvaluator.m | 7 +- XVim/XVimStringBuffer.h | 32 ++-- ...seEvaluator.h => XVimSwapCharsEvaluator.h} | 3 +- XVim/XVimSwapCharsEvaluator.m | 65 ++++++++ XVim/XVimTildeEvaluator.h | 13 -- XVim/XVimTildeEvaluator.m | 33 ---- XVim/XVimUppercaseEvaluator.h | 13 -- XVim/XVimUppercaseEvaluator.m | 29 ---- XVim/XVimVisualEvaluator.m | 10 +- 19 files changed, 277 insertions(+), 292 deletions(-) delete mode 100644 XVim/XVimLowercaseEvaluator.m rename XVim/{XVimLowercaseEvaluator.h => XVimSwapCharsEvaluator.h} (60%) create mode 100644 XVim/XVimSwapCharsEvaluator.m delete mode 100644 XVim/XVimTildeEvaluator.h delete mode 100644 XVim/XVimTildeEvaluator.m delete mode 100644 XVim/XVimUppercaseEvaluator.h delete mode 100644 XVim/XVimUppercaseEvaluator.m diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 3a59f879..39be876d 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -76,9 +76,7 @@ A28F424017EEDBC200A3F7AE /* XVimExCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A736351527484B0051E8E4 /* XVimExCommand.m */; }; A28F424117EEDBC200A3F7AE /* XVimSearch.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A6BA2D152A544B00F0EB5F /* XVimSearch.m */; }; A28F424217EEDBC200A3F7AE /* XVimOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = A2BA3EA1152E372A00C18FB4 /* XVimOptions.m */; }; - A28F424317EEDBC200A3F7AE /* XVimTildeEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7225152F186A00C61D97 /* XVimTildeEvaluator.m */; }; - A28F424417EEDBC200A3F7AE /* XVimLowercaseEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7229152F1B1700C61D97 /* XVimLowercaseEvaluator.m */; }; - A28F424517EEDBC200A3F7AE /* XVimUppercaseEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA722C152F1B2700C61D97 /* XVimUppercaseEvaluator.m */; }; + A28F424417EEDBC200A3F7AE /* XVimSwapCharsEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7229152F1B1700C61D97 /* XVimSwapCharsEvaluator.m */; }; A28F424617EEDBC200A3F7AE /* XVimWindowEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = F15805C61530B4000031175A /* XVimWindowEvaluator.m */; }; A28F424717EEDBC200A3F7AE /* XVimTextObjectEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */; }; A28F424817EEDBC200A3F7AE /* XVimWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = C3FA1A1C1532648700059BF6 /* XVimWindow.m */; }; @@ -157,9 +155,7 @@ C36C104E153131C600CE1D62 /* XVimTextObjectEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */; }; C38A5B301526C6B100E1448D /* XVimKeyStroke.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */; }; C38A5B4D1527CEA500E1448D /* XVimNumericEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4C1527CEA400E1448D /* XVimNumericEvaluator.m */; }; - C3AA7226152F186A00C61D97 /* XVimTildeEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7225152F186A00C61D97 /* XVimTildeEvaluator.m */; }; - C3AA722A152F1B1800C61D97 /* XVimLowercaseEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7229152F1B1700C61D97 /* XVimLowercaseEvaluator.m */; }; - C3AA722D152F1B2800C61D97 /* XVimUppercaseEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA722C152F1B2700C61D97 /* XVimUppercaseEvaluator.m */; }; + C3AA722A152F1B1800C61D97 /* XVimSwapCharsEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7229152F1B1700C61D97 /* XVimSwapCharsEvaluator.m */; }; C3E8F115154267BA007410ED /* XVimMarkSetEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E8F114154267BA007410ED /* XVimMarkSetEvaluator.m */; }; C3E8F11815427AE6007410ED /* XVimArgumentEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E8F11715427AE6007410ED /* XVimArgumentEvaluator.m */; }; C3FA1A1D1532648700059BF6 /* XVimWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = C3FA1A1C1532648700059BF6 /* XVimWindow.m */; }; @@ -344,12 +340,8 @@ C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimKeyStroke.m; path = XVim/XVimKeyStroke.m; sourceTree = SOURCE_ROOT; }; C38A5B4B1527CEA400E1448D /* XVimNumericEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimNumericEvaluator.h; path = XVim/XVimNumericEvaluator.h; sourceTree = SOURCE_ROOT; }; C38A5B4C1527CEA400E1448D /* XVimNumericEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimNumericEvaluator.m; path = XVim/XVimNumericEvaluator.m; sourceTree = SOURCE_ROOT; }; - C3AA7224152F186A00C61D97 /* XVimTildeEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTildeEvaluator.h; path = XVim/XVimTildeEvaluator.h; sourceTree = SOURCE_ROOT; }; - C3AA7225152F186A00C61D97 /* XVimTildeEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimTildeEvaluator.m; path = XVim/XVimTildeEvaluator.m; sourceTree = SOURCE_ROOT; }; - C3AA7228152F1B1700C61D97 /* XVimLowercaseEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimLowercaseEvaluator.h; path = XVim/XVimLowercaseEvaluator.h; sourceTree = SOURCE_ROOT; }; - C3AA7229152F1B1700C61D97 /* XVimLowercaseEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimLowercaseEvaluator.m; path = XVim/XVimLowercaseEvaluator.m; sourceTree = SOURCE_ROOT; }; - C3AA722B152F1B2700C61D97 /* XVimUppercaseEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimUppercaseEvaluator.h; path = XVim/XVimUppercaseEvaluator.h; sourceTree = SOURCE_ROOT; }; - C3AA722C152F1B2700C61D97 /* XVimUppercaseEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimUppercaseEvaluator.m; path = XVim/XVimUppercaseEvaluator.m; sourceTree = SOURCE_ROOT; }; + C3AA7228152F1B1700C61D97 /* XVimSwapCharsEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimSwapCharsEvaluator.h; path = XVim/XVimSwapCharsEvaluator.h; sourceTree = SOURCE_ROOT; }; + C3AA7229152F1B1700C61D97 /* XVimSwapCharsEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimSwapCharsEvaluator.m; path = XVim/XVimSwapCharsEvaluator.m; sourceTree = SOURCE_ROOT; }; C3E8F113154267BA007410ED /* XVimMarkSetEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimMarkSetEvaluator.h; path = XVim/XVimMarkSetEvaluator.h; sourceTree = SOURCE_ROOT; }; C3E8F114154267BA007410ED /* XVimMarkSetEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimMarkSetEvaluator.m; path = XVim/XVimMarkSetEvaluator.m; sourceTree = SOURCE_ROOT; }; C3E8F11615427AE6007410ED /* XVimArgumentEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimArgumentEvaluator.h; path = XVim/XVimArgumentEvaluator.h; sourceTree = SOURCE_ROOT; }; @@ -445,12 +437,8 @@ A2277EDA14F80D5000A6B70C /* XVimYankEvaluator.m */, A2277ED214F80BCB00A6B70C /* XVimDeleteEvaluator.h */, A2277ED314F80BCB00A6B70C /* XVimDeleteEvaluator.m */, - C3AA7224152F186A00C61D97 /* XVimTildeEvaluator.h */, - C3AA7225152F186A00C61D97 /* XVimTildeEvaluator.m */, - C3AA7228152F1B1700C61D97 /* XVimLowercaseEvaluator.h */, - C3AA7229152F1B1700C61D97 /* XVimLowercaseEvaluator.m */, - C3AA722B152F1B2700C61D97 /* XVimUppercaseEvaluator.h */, - C3AA722C152F1B2700C61D97 /* XVimUppercaseEvaluator.m */, + C3AA7228152F1B1700C61D97 /* XVimSwapCharsEvaluator.h */, + C3AA7229152F1B1700C61D97 /* XVimSwapCharsEvaluator.m */, C36C104C153131C500CE1D62 /* XVimTextObjectEvaluator.h */, C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */, A23D002217DA0C7D007EBB21 /* XVimJoinEvaluator.h */, @@ -874,9 +862,7 @@ 6EF220D7183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */, A28F424117EEDBC200A3F7AE /* XVimSearch.m in Sources */, A28F424217EEDBC200A3F7AE /* XVimOptions.m in Sources */, - A28F424317EEDBC200A3F7AE /* XVimTildeEvaluator.m in Sources */, - A28F424417EEDBC200A3F7AE /* XVimLowercaseEvaluator.m in Sources */, - A28F424517EEDBC200A3F7AE /* XVimUppercaseEvaluator.m in Sources */, + A28F424417EEDBC200A3F7AE /* XVimSwapCharsEvaluator.m in Sources */, A28F424617EEDBC200A3F7AE /* XVimWindowEvaluator.m in Sources */, A28F424717EEDBC200A3F7AE /* XVimTextObjectEvaluator.m in Sources */, A28F424817EEDBC200A3F7AE /* XVimWindow.m in Sources */, @@ -953,9 +939,7 @@ A2A736381527484B0051E8E4 /* XVimExCommand.m in Sources */, A2A6BA2E152A544C00F0EB5F /* XVimSearch.m in Sources */, A2BA3EA2152E372A00C18FB4 /* XVimOptions.m in Sources */, - C3AA7226152F186A00C61D97 /* XVimTildeEvaluator.m in Sources */, - C3AA722A152F1B1800C61D97 /* XVimLowercaseEvaluator.m in Sources */, - C3AA722D152F1B2800C61D97 /* XVimUppercaseEvaluator.m in Sources */, + C3AA722A152F1B1800C61D97 /* XVimSwapCharsEvaluator.m in Sources */, F15805C71530B4000031175A /* XVimWindowEvaluator.m in Sources */, C36C104E153131C600CE1D62 /* XVimTextObjectEvaluator.m in Sources */, C3FA1A1D1532648700059BF6 /* XVimWindow.m in Sources */, diff --git a/XVim/NSString+VimHelper.h b/XVim/NSString+VimHelper.h index f9399e32..8b5b55e8 100644 --- a/XVim/NSString+VimHelper.h +++ b/XVim/NSString+VimHelper.h @@ -35,9 +35,13 @@ BOOL isKeyword(unichar ch); **/ - (NSString*)convertToICURegex:(NSRegularExpressionOptions*)options; +- (NSMutableString *)newMutableSubstringWithRange:(NSRange)range; + + (NSString *)stringMadeOfSpaces:(NSUInteger)count; + @end @interface NSMutableString (VimHelper) + (NSMutableString *)mutableStringMadeOfSpaces:(NSUInteger)count; +- (void)appendCharacters:(const unichar *)chars length:(NSUInteger)length; @end diff --git a/XVim/NSString+VimHelper.m b/XVim/NSString+VimHelper.m index 1593d683..97ebdb0b 100644 --- a/XVim/NSString+VimHelper.m +++ b/XVim/NSString+VimHelper.m @@ -97,6 +97,22 @@ - (NSString*)convertToICURegex:(NSRegularExpressionOptions*)options{ return tmp; } +- (NSMutableString *)newMutableSubstringWithRange:(NSRange)range +{ + NSMutableString *s = [[NSMutableString alloc] initWithCapacity:NSMaxRange(range)]; + unichar buf[1024]; + + while (range.length) { + NSUInteger count = MIN(1024, range.length); + + [self getCharacters:buf range:NSMakeRange(range.location, count)]; + [s appendCharacters:buf length:count]; + range.location += count; + range.length -= count; + } + return s; +} + + (NSString *)stringMadeOfSpaces:(NSUInteger)count { if (count <= 8) { @@ -122,4 +138,9 @@ + (NSMutableString *)mutableStringMadeOfSpaces:(NSUInteger)count return [s autorelease]; } +- (void)appendCharacters:(const unichar *)chars length:(NSUInteger)length +{ + CFStringAppendCharacters((CFMutableStringRef)self, chars, (CFIndex)length); +} + @end diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h index eb635b2c..53e4a6b4 100644 --- a/XVim/NSTextView+VimOperation.h +++ b/XVim/NSTextView+VimOperation.h @@ -78,9 +78,7 @@ - (void)xvim_change:(XVimMotion*)motion; - (void)xvim_yank:(XVimMotion*)motion; - (void)xvim_put:(NSString*)text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)count; -- (void)xvim_swapCase:(XVimMotion*)motion; -- (void)xvim_makeLowerCase:(XVimMotion*)motion; -- (void)xvim_makeUpperCase:(XVimMotion*)motion; +- (void)xvim_swapCharacters:(XVimMotion *)motion mode:(int)mode; - (BOOL)xvim_replaceCharacters:(unichar)c count:(NSUInteger)count; - (void)xvim_join:(NSUInteger)count addSpace:(BOOL)addSpace; - (void)xvim_filter:(XVimMotion*)motion; diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index 36d96d42..1c19ef6a 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -64,7 +64,6 @@ - (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion; - (NSRange)xvim_getOperationRangeFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type; - (void)xvim_indentCharacterRange:(NSRange)range; - (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward; -- (void)xvim_swapCaseForRange:(NSRange)range; - (void)xvim_registerInsertionPointForUndo; - (void)xvim_registerIndexForUndo:(NSUInteger)index; @end @@ -897,100 +896,59 @@ - (void)xvim_put:(NSString*)text withType:(TEXT_TYPE)type afterCursor:(bool)afte [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; } -- (void)xvim_swapCase:(XVimMotion*)motion{ - if( self.insertionPoint == 0 && [[self xvim_string] length] == 0 ){ - return ; +- (void)xvim_swapCharacters:(XVimMotion *)motion mode:(int)mode +{ + XVimBuffer *buffer = self.textStorage.xvim_buffer; + NSUInteger undoPos = self.insertionPoint; + NSUInteger endPos; + + if (buffer.length == 0) { + return; } - - if( self.selectionMode == XVIM_VISUAL_NONE ){ - if( motion.motion == MOTION_NONE ){ - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_FORWARD,CHARACTERWISE_EXCLUSIVE,LEFT_RIGHT_NOWRAP,motion.count); - XVimRange r = [self xvim_getMotionRange:self.insertionPoint Motion:m]; - if( r.end == NSNotFound){ + + if (self.selectionMode == XVIM_VISUAL_NONE) { + NSRange range; + + if (motion.motion == MOTION_NONE) { + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_FORWARD,CHARACTERWISE_EXCLUSIVE,LEFT_RIGHT_NOWRAP,motion.count); + XVimRange r = [self xvim_getMotionRange:undoPos Motion:m]; + + if (r.end == NSNotFound) { return; } - if( m.info->reachedEndOfLine ){ - [self xvim_swapCaseForRange:[self xvim_getOperationRangeFrom:r.begin To:r.end Type:CHARACTERWISE_INCLUSIVE]]; - }else{ - [self xvim_swapCaseForRange:[self xvim_getOperationRangeFrom:r.begin To:r.end Type:CHARACTERWISE_EXCLUSIVE]]; + if (m.info->reachedEndOfLine) { + range = [self xvim_getOperationRangeFrom:r.begin To:r.end Type:CHARACTERWISE_INCLUSIVE]; + } else { + range = [self xvim_getOperationRangeFrom:r.begin To:r.end Type:CHARACTERWISE_EXCLUSIVE]; } - [self xvim_moveCursor:r.end preserveColumn:NO]; - }else{ - NSRange r; - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( to.end == NSNotFound){ + endPos = r.end; + } else { + XVimRange to = [self xvim_getMotionRange:undoPos Motion:motion]; + if (to.end == NSNotFound) { return; } - r = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:motion.type]; - [self xvim_swapCaseForRange:r]; - [self xvim_moveCursor:r.location preserveColumn:NO]; - } - }else{ - NSArray* ranges = [self xvim_selectedRanges]; - for( NSValue* val in ranges){ - [self xvim_swapCaseForRange:[val rangeValue]]; - } - [self xvim_moveCursor:[[ranges objectAtIndex:0] rangeValue].location preserveColumn:NO]; - } - - [self xvim_syncState]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - -} -- (void)xvim_makeLowerCase:(XVimMotion*)motion{ - if( self.insertionPoint == 0 && [[self xvim_string] length] == 0 ){ - return ; - } - - NSString* s = [self xvim_string]; - if( self.selectionMode == XVIM_VISUAL_NONE ){ - NSRange r; - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( to.end == NSNotFound ){ - return; + range = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:motion.type]; + endPos = range.location; } - r = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:motion.type]; - [self insertText:[[s substringWithRange:r] lowercaseString] replacementRange:r]; - [self xvim_moveCursor:r.location preserveColumn:NO]; - }else{ - NSArray* ranges = [self xvim_selectedRanges]; - for( NSValue* val in ranges){ - [self insertText:[[s substringWithRange:val.rangeValue] lowercaseString] replacementRange:val.rangeValue]; - } - [self xvim_moveCursor:[[ranges objectAtIndex:0] rangeValue].location preserveColumn:NO]; - } - [self xvim_syncState]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} + [buffer beginEditingAtIndex:undoPos]; + [buffer swapCharactersInRange:range mode:mode]; + [buffer endEditingAtIndex:endPos]; + } else { + NSArray *ranges = [self xvim_selectedRanges]; -- (void)xvim_makeUpperCase:(XVimMotion*)motion{ - if( self.insertionPoint == 0 && [[self xvim_string] length] == 0 ){ - return ; - } - - NSString* s = [self xvim_string]; - if( self.selectionMode == XVIM_VISUAL_NONE ){ - NSRange r; - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( to.end == NSNotFound ){ - return; + endPos = undoPos = [[ranges objectAtIndex:0] rangeValue].location; + [buffer beginEditingAtIndex:undoPos]; + for (NSValue *v in ranges) { + [buffer swapCharactersInRange:v.rangeValue mode:mode]; } - r = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:motion.type]; // TODO: use to.begin instead of insertionPoint - [self insertText:[[s substringWithRange:r] uppercaseString] replacementRange:r]; - [self xvim_moveCursor:r.location preserveColumn:NO]; - }else{ - NSArray* ranges = [self xvim_selectedRanges]; - for( NSValue* val in ranges){ - [self insertText:[[s substringWithRange:val.rangeValue] uppercaseString] replacementRange:val.rangeValue]; - } - [self xvim_moveCursor:[[ranges objectAtIndex:0] rangeValue].location preserveColumn:NO]; + [buffer endEditingAtIndex:endPos]; } + [self xvim_moveCursor:endPos preserveColumn:NO]; [self xvim_syncState]; [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - } - (BOOL)xvim_replaceCharacters:(unichar)c count:(NSUInteger)count{ @@ -1956,29 +1914,6 @@ - (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_O return ret; } -- (void)xvim_swapCaseForRange:(NSRange)range { - [self xvim_registerInsertionPointForUndo]; - NSString* text = [self xvim_string]; - - - NSMutableString *substring = [[text substringWithRange:range] mutableCopy]; - for (NSUInteger i = 0; i < range.length; ++i) { - NSRange currentRange = NSMakeRange(i, 1); - NSString *currentCase = [substring substringWithRange:currentRange]; - NSString *upperCase = [currentCase uppercaseString]; - - NSRange replaceRange = NSMakeRange(i, 1); - if ([currentCase isEqualToString:upperCase]){ - [substring replaceCharactersInRange:replaceRange withString:[currentCase lowercaseString]]; - }else{ - [substring replaceCharactersInRange:replaceRange withString:upperCase]; - } - } - - [self insertText:substring replacementRange:range]; - [substring release]; -} - - (void)xvim_registerIndexForUndo:(NSUInteger)index { XVimUndoOperation *op = [[XVimUndoOperation alloc] initWithIndex:index]; diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 96161a71..245da396 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -295,8 +295,13 @@ - (void)beginEditingAtIndex:(NSUInteger)index; - (void)endEditingAtIndex:(NSUInteger)index; -- (void)replaceCharactersInRange:(NSRange)range - withString:(NSString *)string; +- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string; + +#define XVIM_BUFFER_SWAP_UPPER 0 +#define XVIM_BUFFER_SWAP_LOWER 1 +#define XVIM_BUFFER_SWAP_CASE 2 +#define XVIM_BUFFER_SWAP_ROT13 3 +- (void)swapCharactersInRange:(NSRange)range mode:(int)mode; // Never fails - (NSUInteger)shiftLines:(XVimRange)lines column:(NSUInteger)column diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index e35faca7..b81f4360 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -475,6 +475,88 @@ - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string [_textStorage replaceCharactersInRange:range withString:string]; } +NS_INLINE unichar rot13(unichar c) +{ + switch (c) { + case 'a' ... 'a' + 12: + case 'A' ... 'A' + 12: + return c + 13; + case 'z' - 12 ... 'z': + case 'Z' - 12 ... 'Z': + return c - 13; + } + return c; +} + +- (void)swapCharactersInRange:(NSRange)range mode:(int)mode +{ + NSMutableString *s, *L, *U; + unichar buf[XVIM_STRING_BUFFER_SIZE]; + unsigned pos = 0; + xvim_string_buffer_t sb; + + NSAssert(_curOp, @"you must call -beginEditingAtIndex: first"); + if (!range.length) { + return; + } + + CFLocaleRef locale = CFLocaleCopyCurrent(); + + if (mode == XVIM_BUFFER_SWAP_UPPER || mode == XVIM_BUFFER_SWAP_LOWER) { + s = [self.string newMutableSubstringWithRange:range]; + if (mode == XVIM_BUFFER_SWAP_LOWER) { + CFStringLowercase((CFMutableStringRef)s, locale); + } else { + CFStringUppercase((CFMutableStringRef)s, locale); + } + } else if (mode == XVIM_BUFFER_SWAP_ROT13) { + xvim_sb_init_range(&sb, self.string, range); + s = [[NSMutableString alloc] initWithCapacity:range.length]; + + do { + buf[pos++] = rot13(xvim_sb_peek(&sb)); + if (pos >= XVIM_STRING_BUFFER_SIZE) { + [s appendCharacters:buf length:pos]; + pos = 0; + } + } while (xvim_sb_next(&sb)); + if (pos) { + [s appendCharacters:buf length:pos]; + } + } else { + xvim_sb_init_range(&sb, self.string, range); + s = [[NSMutableString alloc] initWithCapacity:range.length]; + L = [[NSMutableString alloc] initWithCapacity:1]; + U = [[NSMutableString alloc] initWithCapacity:1]; + + do { + unichar ch = xvim_sb_peek(&sb); + + [L replaceCharactersInRange:NSMakeRange(0, L.length) withString:@""]; + [L appendCharacters:&ch length:1]; + [U replaceCharactersInRange:NSMakeRange(0, U.length) withString:@""]; + [U appendCharacters:&ch length:1]; + + CFStringUppercase((CFMutableStringRef)U, locale); + if ([L isEqualToString:U]) { + CFStringLowercase((CFMutableStringRef)L, locale); + [s appendString:L]; + } else { + [s appendString:U]; + } + } while (xvim_sb_next(&sb)); + + [L release]; + [U release]; + } + + [_textStorage beginEditing]; + [self replaceCharactersInRange:range withString:s]; + [_textStorage endEditing]; + [s release]; + CFRelease(locale); +} + - (void)_removeSpacesAtLine:(NSUInteger)line column:(NSUInteger)column count:(NSUInteger)count { NSUInteger tabWidth = self.tabWidth; diff --git a/XVim/XVimGActionEvaluator.m b/XVim/XVimGActionEvaluator.m index 33aeb593..ace7649f 100644 --- a/XVim/XVimGActionEvaluator.m +++ b/XVim/XVimGActionEvaluator.m @@ -8,9 +8,7 @@ #import "XVimJoinEvaluator.h" #import "XVimGActionEvaluator.h" -#import "XVimTildeEvaluator.h" -#import "XVimLowercaseEvaluator.h" -#import "XVimUppercaseEvaluator.h" +#import "XVimSwapCharsEvaluator.h" #import "XVimInsertEvaluator.h" #import "XVimKeyStroke.h" #import "XVimWindow.h" @@ -73,12 +71,12 @@ - (XVimEvaluator*)J{ - (XVimEvaluator*)u{ [self.argumentString appendString:@"u"]; - return [[[XVimLowercaseEvaluator alloc] initWithWindow:self.window] autorelease]; + return [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_LOWER] autorelease]; } - (XVimEvaluator*)U{ [self.argumentString appendString:@"U"]; - return [[[XVimUppercaseEvaluator alloc] initWithWindow:self.window] autorelease]; + return [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_UPPER] autorelease]; } - (XVimEvaluator*)v{ @@ -86,9 +84,14 @@ - (XVimEvaluator*)v{ return [[[XVimVisualEvaluator alloc] initWithLastVisualStateWithWindow:self.window] autorelease]; } +- (XVimEvaluator*)QUESTION{ + [self.argumentString appendString:@"?"]; + return [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_ROT13] autorelease]; +} + - (XVimEvaluator*)TILDE{ [self.argumentString appendString:@"~"]; - return [[[XVimTildeEvaluator alloc] initWithWindow:self.window] autorelease]; + return [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_CASE] autorelease]; } @end diff --git a/XVim/XVimGVisualEvaluator.m b/XVim/XVimGVisualEvaluator.m index d3c56f62..378af328 100644 --- a/XVim/XVimGVisualEvaluator.m +++ b/XVim/XVimGVisualEvaluator.m @@ -10,6 +10,7 @@ #import "NSTextView+VimOperation.h" #import "XVimWindow.h" #import "XVimJoinEvaluator.h" +#import "XVim.h" @implementation XVimGVisualEvaluator @@ -52,14 +53,18 @@ - (XVimEvaluator *)q{ } - (XVimEvaluator*)u{ - NSTextView *view = [self sourceView]; - [view xvim_makeLowerCase:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1)]; + [self.argumentString appendString:@"u"]; + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + [self.sourceView xvim_swapCharacters:m mode:XVIM_BUFFER_SWAP_LOWER]; + [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; } - (XVimEvaluator*)U{ - NSTextView *view = [self sourceView]; - [view xvim_makeUpperCase:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1)]; + [self.argumentString appendString:@"U"]; + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + [self.sourceView xvim_swapCharacters:m mode:XVIM_BUFFER_SWAP_UPPER]; + [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; } @@ -69,13 +74,18 @@ - (XVimEvaluator *)w{ } - (XVimEvaluator *)QUESTION{ - [self.window errorMessage:@"{visual}g? unimplemented" ringBell:NO]; - return [XVimEvaluator popEvaluator]; + [self.argumentString appendString:@"?"]; + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + [self.sourceView xvim_swapCharacters:m mode:XVIM_BUFFER_SWAP_ROT13]; + [[XVim instance] fixOperationCommands]; + return [XVimEvaluator invalidEvaluator]; } - (XVimEvaluator*)TILDE{ - NSTextView *view = [self sourceView]; - [view xvim_swapCase:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1)]; + [self.argumentString appendString:@"~"]; + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + [self.sourceView xvim_swapCharacters:m mode:XVIM_BUFFER_SWAP_CASE]; + [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; } diff --git a/XVim/XVimLowercaseEvaluator.m b/XVim/XVimLowercaseEvaluator.m deleted file mode 100644 index b800a12f..00000000 --- a/XVim/XVimLowercaseEvaluator.m +++ /dev/null @@ -1,30 +0,0 @@ -// -// XVimLowercaseEvaluator.m -// XVim -// -// Created by Tomas Lundell on 6/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimLowercaseEvaluator.h" -#import "XVimWindow.h" -#import "XVim.h" - -@implementation XVimLowercaseEvaluator - -- (XVimEvaluator*)u{ - if ([self numericArg] < 1) - return nil; - - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); - return [self _motionFixed:m]; -} - --(XVimEvaluator*)motionFixed:(XVimMotion*)motion{ - [[self sourceView] xvim_makeLowerCase:motion]; - return nil; -} - - -@end - diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index 95b27818..34b2641c 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -33,7 +33,6 @@ #import "XVimMark.h" #import "XVimMarks.h" #import "XVimMotion.h" -#import "XVimTildeEvaluator.h" #import "NSTextView+VimOperation.h" #import "XVimJoinEvaluator.h" @@ -491,9 +490,9 @@ - (XVimEvaluator*)DOT{ - (XVimEvaluator*)TILDE{ [self.argumentString appendString:@"~"]; - XVimTildeEvaluator* swap = [[[XVimTildeEvaluator alloc] initWithWindow:self.window] autorelease]; - // TODO: support tildeop option - return [swap fixWithNoMotion:self.numericArg]; + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg); + [self.sourceView xvim_swapCharacters:m mode:XVIM_BUFFER_SWAP_CASE]; + return nil; } - (XVimEvaluator*)ForwardDelete{ diff --git a/XVim/XVimStringBuffer.h b/XVim/XVimStringBuffer.h index 6dcdb30b..013d0b36 100644 --- a/XVim/XVimStringBuffer.h +++ b/XVim/XVimStringBuffer.h @@ -8,6 +8,8 @@ #import +#define XVIM_STRING_BUFFER_SIZE 64 + /** @brief structure used for fast search in an NSString * * This has complex invariants: @@ -26,26 +28,21 @@ typedef struct xvim_string_buffer_s { NSUInteger s_index; // index of buffer[0] in s NSUInteger b_index; // index in buffer being read NSUInteger b_len; // number of characters read in buffer - unichar buffer[64]; + unichar buffer[XVIM_STRING_BUFFER_SIZE]; } xvim_string_buffer_t; #define XVimInvalidChar ((unichar)-1) -NS_INLINE NSUInteger _xvim_sb_size(void) -{ - return sizeof(((xvim_string_buffer_t *)NULL)->buffer) / sizeof(unichar); -} - NS_INLINE void _xvim_sb_load(xvim_string_buffer_t *sb) { - NSUInteger len = MIN(sb->s_max - sb->s_index, _xvim_sb_size()); + NSUInteger len = MIN(sb->s_max - sb->s_index, XVIM_STRING_BUFFER_SIZE); sb->b_len = len; - NSCAssert(sb->b_len <= _xvim_sb_size(), @"b_len is bogus"); + NSCAssert(sb->b_len <= XVIM_STRING_BUFFER_SIZE, @"b_len is bogus"); if (len > 0) { [sb->s getCharacters:sb->buffer range:NSMakeRange(sb->s_index, len)]; } - if (len < _xvim_sb_size()) { + if (len < XVIM_STRING_BUFFER_SIZE) { sb->buffer[len] = XVimInvalidChar; } } @@ -60,13 +57,14 @@ NS_INLINE void xvim_sb_init(xvim_string_buffer_t *sb, NSString *s, NSCAssert(min <= max, @"Bad xvim_sb_init"); NSCAssert(index >= sb->s_min && index <= sb->s_max, @"bad xvim_sb_init"); + NSCAssert(max <= s.length, @"bad xvim_sb_init"); - if (max - min < _xvim_sb_size() || index - _xvim_sb_size() / 2 < sb->s_min) { + if (max - min < XVIM_STRING_BUFFER_SIZE || index - XVIM_STRING_BUFFER_SIZE / 2 < sb->s_min) { sb->s_index = sb->s_min; - } else if (index + _xvim_sb_size() >= sb->s_max) { - sb->s_index = sb->s_max - _xvim_sb_size() + 1; + } else if (index + XVIM_STRING_BUFFER_SIZE >= sb->s_max) { + sb->s_index = sb->s_max - XVIM_STRING_BUFFER_SIZE + 1; } else { - sb->s_index = index - _xvim_sb_size() / 2; + sb->s_index = index - XVIM_STRING_BUFFER_SIZE / 2; } sb->b_index = index - sb->s_index; _xvim_sb_load(sb); @@ -122,11 +120,11 @@ NS_INLINE BOOL xvim_sb_next(xvim_string_buffer_t *sb) if (sb->b_index < sb->b_len) { return YES; } - if (sb->b_len < _xvim_sb_size()) { + if (sb->b_len < XVIM_STRING_BUFFER_SIZE) { return NO; } - sb->s_index += _xvim_sb_size() / 2; - sb->b_index = _xvim_sb_size() / 2; + sb->s_index += XVIM_STRING_BUFFER_SIZE / 2; + sb->b_index = XVIM_STRING_BUFFER_SIZE / 2; _xvim_sb_load(sb); return sb->b_index < sb->b_len; } @@ -139,7 +137,7 @@ NS_INLINE BOOL xvim_sb_prev(xvim_string_buffer_t *sb) return YES; } - NSUInteger diff = MIN(sb->s_index - sb->s_min, _xvim_sb_size() / 2); + NSUInteger diff = MIN(sb->s_index - sb->s_min, XVIM_STRING_BUFFER_SIZE / 2); if (diff > 0) { sb->s_index -= diff; sb->b_index = diff; diff --git a/XVim/XVimLowercaseEvaluator.h b/XVim/XVimSwapCharsEvaluator.h similarity index 60% rename from XVim/XVimLowercaseEvaluator.h rename to XVim/XVimSwapCharsEvaluator.h index 9bf1dab7..aa9c386a 100644 --- a/XVim/XVimLowercaseEvaluator.h +++ b/XVim/XVimSwapCharsEvaluator.h @@ -8,5 +8,6 @@ #import "XVimOperatorEvaluator.h" -@interface XVimLowercaseEvaluator : XVimOperatorEvaluator +@interface XVimSwapCharsEvaluator : XVimOperatorEvaluator +- (instancetype)initWithWindow:(XVimWindow *)window mode:(int)mode; @end diff --git a/XVim/XVimSwapCharsEvaluator.m b/XVim/XVimSwapCharsEvaluator.m new file mode 100644 index 00000000..21953a70 --- /dev/null +++ b/XVim/XVimSwapCharsEvaluator.m @@ -0,0 +1,65 @@ +// +// XVimLowercaseEvaluator.m +// XVim +// +// Created by Tomas Lundell on 6/04/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "XVimSwapCharsEvaluator.h" +#import "XVimWindow.h" +#import "XVim.h" + +@implementation XVimSwapCharsEvaluator { + int _mode; +} + +- (instancetype)initWithWindow:(XVimWindow *)window mode:(int)mode +{ + if ((self = [self initWithWindow:window])) { + _mode = mode; + } + return self; +} + +- (XVimEvaluator *)_doitLineWiseIfModeIs:(int)mode +{ + if (_mode != mode || [self numericArg] < 1) + return nil; + + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + return [self _motionFixed:m]; +} + +- (XVimEvaluator *)u +{ + [self.argumentString appendString:@"u"]; + return [self _doitLineWiseIfModeIs:XVIM_BUFFER_SWAP_LOWER]; +} + +- (XVimEvaluator *)U +{ + [self.argumentString appendString:@"U"]; + return [self _doitLineWiseIfModeIs:XVIM_BUFFER_SWAP_UPPER]; +} + +- (XVimEvaluator *)QUESTION +{ + [self.argumentString appendString:@"?"]; + return [self _doitLineWiseIfModeIs:XVIM_BUFFER_SWAP_ROT13]; +} + +- (XVimEvaluator *)TILDE +{ + [self.argumentString appendString:@"~"]; + return [self _doitLineWiseIfModeIs:XVIM_BUFFER_SWAP_CASE]; +} + +- (XVimEvaluator *)motionFixed:(XVimMotion*)motion +{ + [self.sourceView xvim_swapCharacters:motion mode:_mode]; + return nil; +} + + +@end diff --git a/XVim/XVimTildeEvaluator.h b/XVim/XVimTildeEvaluator.h deleted file mode 100644 index 7faff2c0..00000000 --- a/XVim/XVimTildeEvaluator.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// XVimTildeEvaluator.h -// XVim -// -// Created by Tomas Lundell on 6/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimOperatorEvaluator.h" - -@interface XVimTildeEvaluator : XVimOperatorEvaluator -- (XVimEvaluator*)fixWithNoMotion:(NSUInteger)count; -@end diff --git a/XVim/XVimTildeEvaluator.m b/XVim/XVimTildeEvaluator.m deleted file mode 100644 index 28768ccd..00000000 --- a/XVim/XVimTildeEvaluator.m +++ /dev/null @@ -1,33 +0,0 @@ -// -// XVimTildeEvaluator.m -// XVim -// -// Created by Tomas Lundell on 6/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimTildeEvaluator.h" -#import "XVimWindow.h" -#import "NSTextView+VimOperation.h" -#import "XVim.h" - -@implementation XVimTildeEvaluator - -- (XVimEvaluator*)fixWithNoMotion:(NSUInteger)count{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, count)]; -} - -- (XVimEvaluator*)TILDE{ - if ([self numericArg] < 1) - return nil; - - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); - return [self _motionFixed:m]; -} - --(XVimEvaluator*)motionFixed:(XVimMotion*)motion{ - [[self sourceView] xvim_swapCase:motion]; - return nil; -} - -@end \ No newline at end of file diff --git a/XVim/XVimUppercaseEvaluator.h b/XVim/XVimUppercaseEvaluator.h deleted file mode 100644 index 3306446a..00000000 --- a/XVim/XVimUppercaseEvaluator.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// XVimUppercaseEvaluator.h -// XVim -// -// Created by Tomas Lundell on 6/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimOperatorEvaluator.h" - -@interface XVimUppercaseEvaluator : XVimOperatorEvaluator -@end - diff --git a/XVim/XVimUppercaseEvaluator.m b/XVim/XVimUppercaseEvaluator.m deleted file mode 100644 index ef6819d7..00000000 --- a/XVim/XVimUppercaseEvaluator.m +++ /dev/null @@ -1,29 +0,0 @@ -// -// XVimUppercaseEvaluator.m -// XVim -// -// Created by Tomas Lundell on 6/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimUppercaseEvaluator.h" -#import "XVimWindow.h" -#import "XVim.h" -#import "NSTextView+VimOperation.h" - -@implementation XVimUppercaseEvaluator - -- (XVimEvaluator*)U{ - if ([self numericArg] < 1) - return nil; - - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); - return [self _motionFixed:m]; -} - --(XVimEvaluator*)motionFixed:(XVimMotion*)motion{ - [[self sourceView] xvim_makeUpperCase:motion]; - return nil; -} - -@end diff --git a/XVim/XVimVisualEvaluator.m b/XVim/XVimVisualEvaluator.m index 605db793..ec0528c4 100644 --- a/XVim/XVimVisualEvaluator.m +++ b/XVim/XVimVisualEvaluator.m @@ -23,9 +23,7 @@ #import "XVim.h" #import "XVimYankEvaluator.h" #import "XVimShiftEvaluator.h" -#import "XVimLowercaseEvaluator.h" -#import "XVimUppercaseEvaluator.h" -#import "XVimTildeEvaluator.h" +#import "XVimSwapCharsEvaluator.h" #import "XVimJoinEvaluator.h" #import "NSTextView+VimOperation.h" @@ -331,12 +329,12 @@ - (XVimEvaluator *)S{ } - (XVimEvaluator*)u{ - XVimLowercaseEvaluator* eval = [[[XVimLowercaseEvaluator alloc] initWithWindow:self.window] autorelease]; + XVimSwapCharsEvaluator *eval = [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_LOWER] autorelease]; return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; } - (XVimEvaluator*)U{ - XVimUppercaseEvaluator* eval = [[[XVimUppercaseEvaluator alloc] initWithWindow:self.window] autorelease]; + XVimSwapCharsEvaluator *eval = [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_UPPER] autorelease]; return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; } @@ -567,7 +565,7 @@ - (XVimEvaluator*)SLASH{ } - (XVimEvaluator*)TILDE{ - XVimTildeEvaluator* eval = [[[XVimTildeEvaluator alloc] initWithWindow:self.window] autorelease]; + XVimSwapCharsEvaluator *eval = [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_CASE] autorelease]; return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; } From 598f882c98f756c9646cc74dadb771cd0a2820be Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Fri, 22 Nov 2013 17:34:48 +0100 Subject: [PATCH 26/44] Do not call xvim_replaceCharacters with a non printable one fixes 3r --- XVim/XVimInsertEvaluator.m | 4 +++- XVim/XVimKeyStroke.h | 1 + XVim/XVimKeyStroke.m | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index dca37b06..92541048 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -244,7 +244,9 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ if (nextEvaluator == self && nil == keySelector){ NSEvent *event = [keyStroke toEventwithWindowNumber:0 context:nil]; if (_oneCharMode) { - if( ![self.sourceView xvim_replaceCharacters:keyStroke.character count:[self numericArg]] ){ + if (!keyStroke.isPrintable) { + nextEvaluator = [XVimEvaluator invalidEvaluator]; + } else if (![self.sourceView xvim_replaceCharacters:keyStroke.character count:[self numericArg]]) { nextEvaluator = [XVimEvaluator invalidEvaluator]; }else{ nextEvaluator = nil; diff --git a/XVim/XVimKeyStroke.h b/XVim/XVimKeyStroke.h index e6e27a31..eb59042e 100644 --- a/XVim/XVimKeyStroke.h +++ b/XVim/XVimKeyStroke.h @@ -29,6 +29,7 @@ BOOL isPrintable(unichar c); @property unichar character; @property unsigned char modifier; @property (nonatomic, readonly) BOOL isNumeric; +@property (nonatomic, readonly) BOOL isPrintable; - (id)initWithCharacter:(unichar)c modifier:(unsigned char)mod; diff --git a/XVim/XVimKeyStroke.m b/XVim/XVimKeyStroke.m index 50a0ac0e..cc0fa019 100644 --- a/XVim/XVimKeyStroke.m +++ b/XVim/XVimKeyStroke.m @@ -592,6 +592,11 @@ - (NSString*)description{ return [str autorelease]; } +- (BOOL)isPrintable +{ + return !self.modifier && isPrintable(self.character); +} + - (NSString*)keyNotation{ NSMutableString *keyStr = [[NSMutableString alloc] init]; unichar charcode = self.character; From 50b42efa750585c39a946863323c311d1e8da22e Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Sun, 24 Nov 2013 19:38:35 +0100 Subject: [PATCH 27/44] Refactor NSTextView(VimOperation) into the XVimView This is a huge refactor that makes the code more independant, more efficient, and acting on XVimView's and XVimBuffer's as much as possible. In particular, this commit tries not to use any NSTextView methods to modify the content, which has several important side effects: - it's more efficient, since we don't go through all the XCode logic to handle edits (it's really a waste when we're doing block operationst typically), performance for several operations is visibly faster than it used to be; - it's better for the user as this goes below XCode AutoCompletion code, and doesn't pop up spurious dialogs when you're in command mode anymore; - this goes through our Undo logic which allows exact coalescing and cursor placement, instead of relying on the Appkit doing the right thing before. (and it doesn't for insertion, but we'll get to that at some point). This commit also does tons of cleanups with the selection and movement, notably fixing behavior when MOTION_END_OF_LINE ($) is used, as this should be preserved, and wasn't Note that for now XVimView is still XCode dependant, because I didn't write a category to swizzle XCode classes, and didn't write a protocol for the TextView yet. Such a protocol will include anything that is rendering related, like placeholder handling. Indenting should go to the NSTextStorage instead, as it's strictly content related. I wouldn't be surprised there are a couple of regression, crashers, but the test-suite passes, and I'm living on that patch now. --- XVim.xcodeproj/project.pbxproj | 10 - XVim/NSTextStorage+VimOperation.h | 7 - XVim/NSTextStorage+VimOperation.m | 17 +- XVim/NSTextView+VimOperation.h | 143 -- XVim/NSTextView+VimOperation.m | 1929 -------------------------- XVim/Test/XVimTestCase.m | 10 +- XVim/XVimBuffer.h | 3 + XVim/XVimBuffer.m | 60 +- XVim/XVimCommandLineEvaluator.m | 10 +- XVim/XVimDefs.h | 21 +- XVim/XVimDeleteEvaluator.m | 9 +- XVim/XVimEqualEvaluator.m | 4 +- XVim/XVimEvaluator.h | 3 +- XVim/XVimEvaluator.m | 22 +- XVim/XVimExCommand.m | 46 +- XVim/XVimGActionEvaluator.m | 25 +- XVim/XVimGMotionEvaluator.m | 7 +- XVim/XVimGVisualEvaluator.m | 10 +- XVim/XVimInsertEvaluator.m | 109 +- XVim/XVimJoinEvaluator.m | 4 +- XVim/XVimMarkSetEvaluator.m | 17 +- XVim/XVimMotion.h | 14 +- XVim/XVimMotionEvaluator.h | 2 - XVim/XVimMotionEvaluator.m | 51 +- XVim/XVimMotionOption.h | 20 - XVim/XVimNormalEvaluator.m | 63 +- XVim/XVimOperatorEvaluator.m | 15 +- XVim/XVimSearch.m | 5 +- XVim/XVimShiftEvaluator.m | 11 +- XVim/XVimSwapCharsEvaluator.m | 2 +- XVim/XVimTextObjectEvaluator.m | 1 - XVim/XVimUndo.h | 4 +- XVim/XVimUndo.m | 18 +- XVim/XVimView.h | 69 + XVim/XVimView.m | 2159 ++++++++++++++++++++++++++--- XVim/XVimVisualEvaluator.h | 2 +- XVim/XVimVisualEvaluator.m | 112 +- XVim/XVimWindow.m | 9 +- XVim/XVimYankEvaluator.m | 2 +- 39 files changed, 2386 insertions(+), 2639 deletions(-) delete mode 100644 XVim/NSTextView+VimOperation.h delete mode 100644 XVim/NSTextView+VimOperation.m delete mode 100644 XVim/XVimMotionOption.h diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 39be876d..d448a4a3 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -43,7 +43,6 @@ A2575409172F6698003D8A97 /* XVimTester+Mark.m in Sources */ = {isa = PBXBuildFile; fileRef = A2575408172F6698003D8A97 /* XVimTester+Mark.m */; }; A257540C1732A58F003D8A97 /* XVimTester+Visual.m in Sources */ = {isa = PBXBuildFile; fileRef = A257540B1732A58E003D8A97 /* XVimTester+Visual.m */; }; A257C28F156567250098CA09 /* DVTSourceTextView+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A257C28E156567250098CA09 /* DVTSourceTextView+XVim.m */; }; - A2640CC917ACE651003D197D /* NSTextView+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */; }; A26ACC4E154F2D6800B27D69 /* IDEEditor+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */; }; A26DC88615DF33C600779CB4 /* XVimTester.m in Sources */ = {isa = PBXBuildFile; fileRef = A26DC88515DF33C600779CB4 /* XVimTester.m */; }; A26E5F6417A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */; }; @@ -113,7 +112,6 @@ A28F426A17EEDBC200A3F7AE /* XVimTester+Recording.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AFD73617914ACE009B442B /* XVimTester+Recording.m */; }; A28F426B17EEDBC200A3F7AE /* XVimTester+Issues.m in Sources */ = {isa = PBXBuildFile; fileRef = A2771E6A179EF520003B621E /* XVimTester+Issues.m */; }; A28F426C17EEDBC200A3F7AE /* NSTextStorage+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */; }; - A28F426D17EEDBC200A3F7AE /* NSTextView+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */; }; A28F426E17EEDBC200A3F7AE /* XVimTester+ExCmd.m in Sources */ = {isa = PBXBuildFile; fileRef = A24BE47417AD0D30001F797B /* XVimTester+ExCmd.m */; }; A28F426F17EEDBC200A3F7AE /* XVimTester+Search.m in Sources */ = {isa = PBXBuildFile; fileRef = A28D895617BFF434002709D8 /* XVimTester+Search.m */; }; A28F427017EEDBC200A3F7AE /* XVimJoinEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A23D002317DA0C7D007EBB21 /* XVimJoinEvaluator.m */; }; @@ -255,8 +253,6 @@ A257540B1732A58E003D8A97 /* XVimTester+Visual.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XVimTester+Visual.m"; path = "XVim/Test/XVimTester+Visual.m"; sourceTree = SOURCE_ROOT; }; A257C28D156567250098CA09 /* DVTSourceTextView+XVim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "DVTSourceTextView+XVim.h"; path = "XVim/DVTSourceTextView+XVim.h"; sourceTree = SOURCE_ROOT; }; A257C28E156567250098CA09 /* DVTSourceTextView+XVim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DVTSourceTextView+XVim.m"; path = "XVim/DVTSourceTextView+XVim.m"; sourceTree = SOURCE_ROOT; }; - A2640CC717ACE651003D197D /* NSTextView+VimOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSTextView+VimOperation.h"; path = "XVim/NSTextView+VimOperation.h"; sourceTree = SOURCE_ROOT; }; - A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSTextView+VimOperation.m"; path = "XVim/NSTextView+VimOperation.m"; sourceTree = SOURCE_ROOT; }; A26ACC4C154F2D6600B27D69 /* IDEEditor+XVim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "IDEEditor+XVim.h"; path = "XVim/IDEEditor+XVim.h"; sourceTree = SOURCE_ROOT; }; A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "IDEEditor+XVim.m"; path = "XVim/IDEEditor+XVim.m"; sourceTree = SOURCE_ROOT; }; A26DC88415DF33C600779CB4 /* XVimTester.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTester.h; path = XVim/XVimTester.h; sourceTree = SOURCE_ROOT; }; @@ -330,7 +326,6 @@ C3552DA7153AC6F800D57577 /* XVimHistoryHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimHistoryHandler.m; path = XVim/XVimHistoryHandler.m; sourceTree = SOURCE_ROOT; }; C361DE661538F18400037B4B /* XVimGActionEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimGActionEvaluator.h; path = XVim/XVimGActionEvaluator.h; sourceTree = SOURCE_ROOT; }; C361DE671538F18400037B4B /* XVimGActionEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimGActionEvaluator.m; path = XVim/XVimGActionEvaluator.m; sourceTree = SOURCE_ROOT; }; - C36266CB153455C9000C79D8 /* XVimMotionOption.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = XVimMotionOption.h; path = XVim/XVimMotionOption.h; sourceTree = SOURCE_ROOT; }; C366C23915287EE30008C58E /* XVimKeymap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimKeymap.h; path = XVim/XVimKeymap.h; sourceTree = SOURCE_ROOT; }; C366C23A15287EE30008C58E /* XVimKeymap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimKeymap.m; path = XVim/XVimKeymap.m; sourceTree = SOURCE_ROOT; }; C36C1045153104EC00CE1D62 /* XVimMotionType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = XVimMotionType.h; path = XVim/XVimMotionType.h; sourceTree = SOURCE_ROOT; }; @@ -409,8 +404,6 @@ children = ( 6E2C392D183E4BA20056E30F /* XVimView.h */, 6E2C392E183E4BA20056E30F /* XVimView.m */, - A2640CC717ACE651003D197D /* NSTextView+VimOperation.h */, - A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */, ); name = View; sourceTree = ""; @@ -674,7 +667,6 @@ C3552DA7153AC6F800D57577 /* XVimHistoryHandler.m */, C36C1045153104EC00CE1D62 /* XVimMotionType.h */, C32CE8621532F0E5002BCE2B /* XVimKeymapProvider.h */, - C36266CB153455C9000C79D8 /* XVimMotionOption.h */, F17D0139150861DC00A8111B /* XVimRegister.h */, F17D013A150861DC00A8111B /* XVimRegister.m */, C38A5B2E1526C6B100E1448D /* XVimKeyStroke.h */, @@ -904,7 +896,6 @@ A28F426A17EEDBC200A3F7AE /* XVimTester+Recording.m in Sources */, A28F426B17EEDBC200A3F7AE /* XVimTester+Issues.m in Sources */, A28F426C17EEDBC200A3F7AE /* NSTextStorage+VimOperation.m in Sources */, - A28F426D17EEDBC200A3F7AE /* NSTextView+VimOperation.m in Sources */, A28F426E17EEDBC200A3F7AE /* XVimTester+ExCmd.m in Sources */, A28F426F17EEDBC200A3F7AE /* XVimTester+Search.m in Sources */, A28F427017EEDBC200A3F7AE /* XVimJoinEvaluator.m in Sources */, @@ -980,7 +971,6 @@ 6E6865D618390702008D5FBB /* XVimUndo.m in Sources */, A2771E6B179EF520003B621E /* XVimTester+Issues.m in Sources */, A26E5F6417A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m in Sources */, - A2640CC917ACE651003D197D /* NSTextView+VimOperation.m in Sources */, A24BE47517AD0D31001F797B /* XVimTester+ExCmd.m in Sources */, A28D895717BFF434002709D8 /* XVimTester+Search.m in Sources */, 6E2C392F183E4BA20056E30F /* XVimView.m in Sources */, diff --git a/XVim/NSTextStorage+VimOperation.h b/XVim/NSTextStorage+VimOperation.h index 52fe64e6..f8b8ab81 100644 --- a/XVim/NSTextStorage+VimOperation.h +++ b/XVim/NSTextStorage+VimOperation.h @@ -11,13 +11,6 @@ #import "XVimDefs.h" #import "XVimTextStoring.h" -typedef enum { - XVimSortOptionReversed = 1, - XVimSortOptionRemoveDuplicateLines = 1 << 1, - XVimSortOptionNumericSort = 1 << 2, - XVimSortOptionIgnoreCase = 1 << 3 -} XVimSortOptions; - /** VimOperation category on NSTextStorage Adds Vim-like functionality to NSTextStorage. diff --git a/XVim/NSTextStorage+VimOperation.m b/XVim/NSTextStorage+VimOperation.m index e88a2cc9..a3a88741 100644 --- a/XVim/NSTextStorage+VimOperation.m +++ b/XVim/NSTextStorage+VimOperation.m @@ -84,24 +84,9 @@ - (BOOL)isBlankline:(NSUInteger)index - (BOOL) isValidCursorPosition:(NSUInteger)index { ASSERT_VALID_RANGE_WITH_EOF(index); - if( [self isBlankline:index] ){ - return YES; - } - // "index" in not a blankline. - // Then the EOF is not a valid cursor position. - if( [self isEOF:index] ){ - return NO; - } - - // index is never the position of EOF. We can call isNewline with index. - if( ![self isNewline:index]){ - return YES; - } - - return NO; + return [self isBlankline:index] || ![self isEOL:index]; } - #pragma mark Searching Positions /** diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h deleted file mode 100644 index 53e4a6b4..00000000 --- a/XVim/NSTextView+VimOperation.h +++ /dev/null @@ -1,143 +0,0 @@ -// -// NSTextView+VimOperation.h -// XVim -// -// Created by Suzuki Shuichiro on 8/3/13. -// -// - -// Design Guideline -// -// NSTextView+VimOperation category should have additional medhots to achieve Vim related operations/status. -// This is a top level interface for it and you should not declare something used internally to calculate the position or motion. -// They should go into internal category "VimOperationPrivate" in NSTextView+VimOperation.m. -// The operations declared here must be complete its tasks including edit on characters, positioning a cursor or scrolling. -// If possible methods here should be called WITHOUT spcifing any position or line number as an argument -// because all the operation should be done on current state of the text view. -// But in Vim some operation (in Ex command especiall) takes line number or range as an argument so -// to complete such task you need to decleare some method which takes line number or range as an agument. -// Usually declare one method for one vim operation. -// -// -// Difference between NSTextStorage+VimOperation category -// -// NSTextStorage is a class represents a "Model" of the NSTextView when it is considered as MVC model. -// NSTextStorage holds the content of string and its attributes(color/font...) -// So with VimOperation category NSTextView handles all the view related things too including cursor or scroll. -// In both categories they share the similar name of methods but the role of these methods are different. -// For example, to delete a line both category may have "deleteLine:(NSUInteger)lineNumber" method. -// In NSTextView+VimOperation you must handle where the cursor should go after the operation or -// if it needs to scroll the view. On the other hand in NSTextStorage+VimOperation should provide -// ability to edit its string and do not need to consider any view related thing. -// deleteLine: method in NSTextView+VimOperation would use deleteLine: method in NSTextStorage+VimOperation -// and complete its task. -// -// Public or Private -// A method declared here is a public method to XVimEvaluators. -// As explained above these methods are interface to achieve Vim operation to a NSTextView. -// If you need some helper method used internally, declare it in VimOperationPrivate category in .m file -// -// -// Naming conventoin -// -// All the method in this caregory must have a prefix "xvim_" -// This is to avoid a conflict with method names in NSTextView. - -#import "XVimView.h" -#import "NSTextStorage+VimOperation.h" - -@interface NSTextView (VimOperation) -// TODO: Method names in category should have prefix like xvim_ -#pragma mark Properties -// Make sure that these property names are not conflicting to the properties in NSTextView -@property(readonly) NSUInteger insertionPoint; -@property(readonly) XVimPosition insertionPosition; -@property(readonly) NSUInteger insertionColumn; -@property(readonly) NSUInteger insertionLine; -@property(readonly) NSUInteger preservedColumn; -@property(readonly) NSUInteger selectionBegin; -@property(readonly) XVIM_VISUAL_MODE selectionMode; -@property(readonly) BOOL selectionToEOL; -@property(readonly) CURSOR_MODE cursorMode; -@property(strong) id xvimDelegate; -@property BOOL needsUpdateFoundRanges; -@property(readonly) NSArray* foundRanges; - -#pragma mark Changing state -- (void)xvim_changeSelectionMode:(XVIM_VISUAL_MODE)mode; -- (void)xvim_escapeFromInsert; -- (void)xvim_setWrapsLines:(BOOL)wraps; - -#pragma mark Operations (Has effect to internal state) -- (void)xvim_adjustCursorPosition; -- (void)xvim_moveToIndex:(NSUInteger)index; -- (void)xvim_moveToPosition:(XVimPosition)pos; -- (void)xvim_move:(XVimMotion*)motion; -- (void)xvim_selectSwapEndsOnSameLine:(BOOL)onSameLine; -- (void)xvim_delete:(XVimMotion*)motion andYank:(BOOL)yank; -- (void)xvim_change:(XVimMotion*)motion; -- (void)xvim_yank:(XVimMotion*)motion; -- (void)xvim_put:(NSString*)text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)count; -- (void)xvim_swapCharacters:(XVimMotion *)motion mode:(int)mode; -- (BOOL)xvim_replaceCharacters:(unichar)c count:(NSUInteger)count; -- (void)xvim_join:(NSUInteger)count addSpace:(BOOL)addSpace; -- (void)xvim_filter:(XVimMotion*)motion; -- (void)xvim_shiftRight:(XVimMotion*)motion; -- (void)xvim_shiftLeft:(XVimMotion*)motion; -- (void)xvim_insertText:(NSString*)str line:(NSUInteger)line column:(NSUInteger)column; -- (void)xvim_insertNewlineBelowLine:(NSUInteger)line; -- (void)xvim_insertNewlineBelowCurrentLine; -- (void)xvim_insertNewlineBelowCurrentLineWithIndent; -- (void)xvim_insertNewlineAboveLine:(NSUInteger)line; -- (void)xvim_insertNewlineAboveCurrentLine; -- (void)xvim_insertNewlineAboveCurrentLineWithIndent; -- (void)xvim_insertNewlineAboveAndInsertWithIndent; -- (void)xvim_insertNewlineBelowAndInsertWithIndent; -- (void)xvim_insert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column blockLines:(XVimRange *)lines; -- (void)xvim_overwriteCharacter:(unichar)c; -- (BOOL)xvim_incrementNumber:(int64_t)offset; -- (void)xvim_blockInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint)mode - count:(NSUInteger)count column:(NSUInteger)column lines:(XVimRange)lines; - -/** - * Sort specified lines. - * line1 - line beginning - * line2 - line end - * The lines must be greater than 0 (Line number starts from 1) - * line2 can be less than line1 - * If the range of lines exceeds the maximu line number of the text - * it sorts lines up to end of the text. - * If the range is out of range of current text it does nothing. - **/ -- (void)xvim_sortLinesFrom:(NSUInteger)line1 to:(NSUInteger)line2 withOptions:(XVimSortOptions)options; -- (void)xvim_selectNextPlaceholder; -- (void)xvim_selectPreviousPlaceholder; -- (void)xvim_hideCompletions; - -#pragma mark Search -/** - * Shows the candidate of the next search result. - **/ -- (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward; -- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt; -- (void)xvim_clearHighlightText; -- (NSRange)xvim_currentWord:(MOTION_OPTION)opt; - -#pragma mark Searching positions - -/** - * Return number of lines in current visible view. - * This means the capacity of the view to show lines and not actually showing now. - * For example the view is 100px height and 1 line is 10px then this returns 10 - * even there are only 2 lines in current view. - * - * TODO: This assumes that all the lines in a view has same text height. - * I thinks this is not bad assumption but there may be a situation the assumption does not work. - **/ -- (NSUInteger)xvim_numberOfLinesInVisibleRect; - -- (void)xvim_syncStateFromView; // update our instance variables with self's properties - -@end diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m deleted file mode 100644 index 1c19ef6a..00000000 --- a/XVim/NSTextView+VimOperation.m +++ /dev/null @@ -1,1929 +0,0 @@ -// -// NSTextView+VimOperation.m -// XVim -// -// Created by Suzuki Shuichiro on 8/3/13. -// -// - -#if (XVIM_XCODE_VERSION==5) -#define __XCODE5__ -#endif - -#define __USE_DVTKIT__ - -#ifdef __USE_DVTKIT__ -#import "DVTKit.h" -#import "IDEKit.h" -#endif - -#import "Utils.h" -#import "NSString+VimHelper.h" -#import "NSObject+ExtraData.h" -#import "NSTextView+VimOperation.h" -#import "NSTextStorage+VimOperation.h" -#import "Logger.h" -#import "XVimUndo.h" -#import "XVimBuffer.h" -#import "XVimStringBuffer.h" - -#define LOG_STATE() TRACE_LOG(@"mode:%d length:%d cursor:%d ip:%d begin:%d line:%d column:%d preservedColumn:%d", \ - self.selectionMode, \ - [self.textStorage string].length, \ - self.cursorMode, \ - self.insertionPoint, \ - self.selectionBegin, \ - self.insertionLine, \ - self.insertionColumn, \ - self.preservedColumn ) - -// These property declarations for for accessing them as readwrite from inside this category -@interface NSTextView () -@property NSUInteger insertionPoint; -@property XVimPosition insertionPosition; -//@property NSUInteger insertionColumn; // This is readonly also internally -//@property NSUInteger insertionLine; // This is readonly also internally -@property NSUInteger preservedColumn; -@property NSUInteger selectionBegin; -@property XVIM_VISUAL_MODE selectionMode; -@property BOOL selectionToEOL; -@property CURSOR_MODE cursorode; -@property(readonly) NSMutableArray* foundRanges; - -// Internal properties -@property(strong) NSString* lastYankedText; -@property TEXT_TYPE lastYankedType; -@end - -@interface NSTextView(VimOperationPrivate) -@property BOOL xvim_lockSyncStateFromView; -- (void)xvim_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve; -- (void)xvim_syncState; // update self's properties with our variables -- (NSArray*)xvim_selectedRanges; -- (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion; -- (NSRange)xvim_getOperationRangeFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type; -- (void)xvim_indentCharacterRange:(NSRange)range; -- (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward; -- (void)xvim_registerInsertionPointForUndo; -- (void)xvim_registerIndexForUndo:(NSUInteger)index; -@end - -@implementation NSTextView (VimOperation) - -#pragma mark internal helpers - -- (void)_xvim_insertSpaces:(NSUInteger)count replacementRange:(NSRange)replacementRange -{ - if (count || replacementRange.length) { - [self insertText:[NSString stringMadeOfSpaces:count] replacementRange:replacementRange]; - } -} - -- (XVimRange)_xvim_selectedLines{ - if (self.selectionMode == XVIM_VISUAL_NONE) { // its not in selecting mode - return (XVimRange){ NSNotFound, NSNotFound }; - } else { - XVimBuffer *buffer = self.textStorage.xvim_buffer; - NSUInteger l1 = [buffer lineNumberAtIndex:self.insertionPoint]; - NSUInteger l2 = [buffer lineNumberAtIndex:self.selectionBegin]; - - return (XVimRange){ MIN(l1, l2), MAX(l1, l2) }; - } -} - -- (NSRange)_xvim_selectedRange{ - if (self.selectionMode == XVIM_VISUAL_NONE) { - return NSMakeRange(self.insertionPoint, 0); - } - - if (self.selectionMode == XVIM_VISUAL_CHARACTER) { - XVimRange xvr = XVimMakeRange(self.selectionBegin, self.insertionPoint); - - if (xvr.begin > xvr.end) { - xvr = XVimRangeSwap(xvr); - } - if ([self.textStorage isEOF:xvr.end]) { - xvr.end--; - } - return XVimMakeNSRange(xvr); - } - - if (self.selectionMode == XVIM_VISUAL_LINE) { - XVimRange lines = [self _xvim_selectedLines]; - XVimBuffer *buffer = self.textStorage.xvim_buffer; - NSUInteger begin = [buffer indexOfLineNumber:lines.begin]; - NSUInteger end = [buffer indexOfLineNumber:lines.end]; - - end = [buffer endOfLine:end]; - if ([self.textStorage isEOF:end]) { - end--; - } - return NSMakeRange(begin, end - begin + 1); - } - - return NSMakeRange(NSNotFound, 0); -} - -- (XVimSelection)_xvim_selectedBlock{ - XVimSelection result = { }; - - if (self.selectionMode == XVIM_VISUAL_NONE) { - result.top = result.bottom = result.left = result.right = NSNotFound; - return result; - } - - NSTextStorage *ts = self.textStorage; - XVimBuffer *buffer = self.textStorage.xvim_buffer; - NSUInteger l1, c11, c12; - NSUInteger l2, c21, c22; - NSUInteger tabWidth = buffer.tabWidth; - NSUInteger pos; - - pos = self.selectionBegin; - l1 = [buffer lineNumberAtIndex:pos]; - c11 = [buffer columnOfIndex:pos]; - if (!tabWidth || [ts isEOF:pos] || [self.xvim_string characterAtIndex:pos] != '\t') { - c12 = c11; - } else { - c12 = c11 + tabWidth - (c11 % tabWidth) - 1; - } - - pos = self.insertionPoint; - l2 = [buffer lineNumberAtIndex:pos]; - c21 = [buffer columnOfIndex:pos]; - if (!tabWidth || [ts isEOF:pos] || [self.xvim_string characterAtIndex:pos] != '\t') { - c22 = c21; - } else { - c22 = c21 + tabWidth - (c21 % tabWidth) - 1; - } - - if (l1 <= l2) { - result.corner |= _XVIM_VISUAL_BOTTOM; - } - if (c11 <= c22) { - result.corner |= _XVIM_VISUAL_RIGHT; - } - result.top = MIN(l1, l2); - result.bottom = MAX(l1, l2); - result.left = MIN(c11, c21); - result.right = MAX(c12, c22); - if (self.selectionToEOL) { - result.right = XVimSelectionEOL; - } - return result; -} - -- (void)__xvim_startYankWithType:(MOTION_TYPE)type -{ - if (self.selectionMode == XVIM_VISUAL_NONE) { - if (type == CHARACTERWISE_EXCLUSIVE || type == CHARACTERWISE_INCLUSIVE) { - self.lastYankedType = TEXT_TYPE_CHARACTERS; - } else if (type == LINEWISE) { - self.lastYankedType = TEXT_TYPE_LINES; - } - } else if (self.selectionMode == XVIM_VISUAL_CHARACTER) { - self.lastYankedType = TEXT_TYPE_CHARACTERS; - } else if (self.selectionMode == XVIM_VISUAL_LINE) { - self.lastYankedType = TEXT_TYPE_LINES; - } else if (self.selectionMode == XVIM_VISUAL_BLOCK) { - self.lastYankedType = TEXT_TYPE_BLOCK; - } - TRACE_LOG(@"YANKED START WITH TYPE:%d", self.lastYankedType); -} - -- (void)_xvim_yankRange:(NSRange)range withType:(MOTION_TYPE)type -{ - NSString *s; - BOOL needsNL; - - [self __xvim_startYankWithType:type]; - - needsNL = self.lastYankedType == TEXT_TYPE_LINES; - if (range.length) { - s = [self.xvim_string substringWithRange:range]; - if (needsNL && !isNewline([s characterAtIndex:s.length - 1])) { - s = [s stringByAppendingString:@"\n"]; - } - } else if (needsNL) { - s = @"\n"; - } else { - s = @""; - } - - self.lastYankedText = s; - TRACE_LOG(@"YANKED STRING : %@", s); -} - -- (void)_xvim_yankSelection:(XVimSelection)sel -{ - NSTextStorage *ts = self.textStorage; - NSString *s = self.xvim_string; - XVimBuffer *buffer = ts.xvim_buffer; - NSUInteger tabWidth = buffer.tabWidth; - - NSMutableString *ybuf = [[NSMutableString alloc] init]; - self.lastYankedType = TEXT_TYPE_BLOCK; - - for (NSUInteger line = sel.top; line <= sel.bottom; line++) { - NSUInteger lpos = [buffer indexOfLineNumber:line column:sel.left]; - NSUInteger rpos = [buffer indexOfLineNumber:line column:sel.right]; - - /* if lpos points in the middle of a tab, split it and advance lpos */ - if (![ts isEOF:lpos] && [s characterAtIndex:lpos] == '\t') { - NSUInteger lcol = sel.left - (sel.left % tabWidth); - - if (lcol < sel.left) { - TRACE_LOG("lcol %ld left %ld tab %ld", (long)lcol, (long)sel.left, (long)tabWidth); - NSUInteger count = tabWidth - (sel.left - lcol); - - if (lpos == rpos) { - /* if rpos points to the same tab, truncate it to the right also */ - count = sel.right - sel.left + 1; - } - [ybuf appendString:[NSString stringMadeOfSpaces:count]]; - lpos++; - } - } - - if (lpos <= rpos) { - if (sel.right == XVimSelectionEOL) { - [ybuf appendString:[s substringWithRange:NSMakeRange(lpos, rpos - lpos)]]; - } else { - NSRange r = NSMakeRange(lpos, rpos - lpos + 1); - NSUInteger rcol; - BOOL mustPad = NO; - - if ([ts isEOF:rpos]) { - rcol = [buffer columnOfIndex:rpos]; - mustPad = YES; - r.length--; - } else { - unichar c = [s characterAtIndex:rpos]; - if (isNewline(c)) { - rcol = [buffer columnOfIndex:rpos]; - mustPad = YES; - r.length--; - } else if (c == '\t') { - rcol = [buffer columnOfIndex:rpos]; - if (sel.right - rcol + 1 < tabWidth) { - mustPad = YES; - r.length--; - } - } - } - - if (r.length) { - [ybuf appendString:[s substringWithRange:r]]; - } - - if (mustPad) { - [ybuf appendString:[NSString stringMadeOfSpaces:sel.right - rcol + 1]]; - } - } - } - [ybuf appendString:@"\n"]; - } - - self.lastYankedText = ybuf; - TRACE_LOG(@"YANKED STRING : %@", ybuf); - [ybuf release]; -} - -- (void)_xvim_killSelection:(XVimSelection)sel -{ - NSTextStorage *ts = self.textStorage; - NSString *s = self.xvim_string; - XVimBuffer *buffer = ts.xvim_buffer; - NSUInteger tabWidth = buffer.tabWidth; - - for (NSUInteger line = sel.bottom; line >= sel.top; line--) { - NSUInteger lpos = [buffer indexOfLineNumber:line column:sel.left]; - NSUInteger rpos = [buffer indexOfLineNumber:line column:sel.right]; - NSUInteger nspaces = 0; - - if ([ts isEOF:lpos]) { - continue; - } - - if ([s characterAtIndex:lpos] == '\t') { - NSUInteger lcol = [buffer columnOfIndex:lpos]; - - if (lcol < sel.left) { - nspaces = sel.left - lcol; - if (lpos == rpos) { - nspaces = tabWidth - (sel.right - sel.left + 1); - } - } - } - - if ([ts isEOL:rpos]) { - rpos--; - } else if (lpos < rpos) { - if ([s characterAtIndex:rpos] == '\t') { - nspaces += tabWidth - (sel.right - [buffer columnOfIndex:rpos] + 1); - } - } - - NSRange range = NSMakeRange(lpos, rpos - lpos + 1); - NSString *repl = @""; - - if (nspaces) { - repl = [NSString stringWithFormat:@"%*s", (int)nspaces, ""]; - } - [self insertText:repl replacementRange:range]; - } -} - - -#pragma mark Properties - -/** - * Properties in this category uses NSObject+ExtraData to - * store additional properties. - **/ - -- (NSUInteger)insertionPoint{ - id ret = [self dataForName:@"insertionPoint"]; - return nil == ret ? 0 : [ret unsignedIntegerValue]; -} - -- (void)setInsertionPoint:(NSUInteger)insertion{ - [self setUnsignedInteger:insertion forName:@"insertionPoint"]; -} - -- (XVimPosition)insertionPosition{ - return XVimMakePosition(self.insertionLine, self.insertionColumn); -} - -- (void)setInsertionPosition:(XVimPosition)pos{ - // Not implemented yet (Just update corresponding insertionPoint) -} - -- (NSUInteger)insertionColumn{ - return [self.textStorage.xvim_buffer columnOfIndex:self.insertionPoint]; -} - -- (NSUInteger)insertionLine{ - return [self.textStorage.xvim_buffer lineNumberAtIndex:self.insertionPoint]; -} - -- (NSUInteger)preservedColumn{ - id ret = [self dataForName:@"preservedColumn"]; - return nil == ret ? 0 : [ret unsignedIntegerValue]; -} - -- (void)setPreservedColumn:(NSUInteger)preservedColumn{ - TRACE_LOG(@"%d" , preservedColumn); - [self setUnsignedInteger:preservedColumn forName:@"preservedColumn"]; -} - -- (NSUInteger)selectionBegin{ - id ret = [self dataForName:@"selectionBegin"]; - return nil == ret ? 0 : [ret unsignedIntegerValue]; -} - -- (void)setSelectionBegin:(NSUInteger)selectionBegin{ - [self setUnsignedInteger:selectionBegin forName:@"selectionBegin"]; -} - -- (BOOL)selectionToEOL{ - return [[self dataForName:@"selectionToEOL"] boolValue]; -} - -- (void)setSelectionToEOL:(BOOL)selectionToEOL{ - [self setBool:selectionToEOL forName:@"selectionToEOL"]; -} - -- (XVIM_VISUAL_MODE) selectionMode{ - id ret = [self dataForName:@"selectionMode"]; - return nil == ret ? XVIM_VISUAL_NONE : (XVIM_VISUAL_MODE)[ret integerValue]; -} - -- (void)setSelectionMode:(XVIM_VISUAL_MODE)selectionMode{ - if (self.selectionMode != selectionMode) { - [self setInteger:selectionMode forName:@"selectionMode"]; - } -} - -- (CURSOR_MODE) cursorMode{ - id ret = [self dataForName:@"cursorMode"]; - return nil == ret ? CURSOR_MODE_COMMAND : (CURSOR_MODE)[ret integerValue]; -} - -- (void)setCursorMode:(CURSOR_MODE)cursorMode{ - [self setInteger:cursorMode forName:@"cursorMode"]; -} - -- (void)setXvimDelegate:(id)xvimDelegate{ - [self setData:xvimDelegate forName:@"xvimDelegate"]; -} - -- (id)xvimDelegate{ - return [self dataForName:@"xvimDelegate"]; -} - -- (BOOL)needsUpdateFoundRanges{ - id ret = [self dataForName:@"needsUpdateFoundRanges"]; - return nil == ret ? NO : [ret boolValue]; -} - -- (void)setNeedsUpdateFoundRanges:(BOOL)needsUpdateFoundRanges{ - [self setBool:needsUpdateFoundRanges forName:@"needsUpdateFoundRanges"]; -} - -- (NSMutableArray*)foundRanges{ - id ranges = [self dataForName:@"foundRanges"]; - if( nil == ranges ){ - ranges = [[[NSMutableArray alloc] init] autorelease]; - [self setData:ranges forName:@"foundRanges"]; - } - return ranges; -} - -#pragma mark Internal properties - -- (NSString*) lastYankedText{ - return [self dataForName:@"lastYankedText"]; -} - -- (void)setLastYankedText:(NSString*)text{ - [self setData:[NSString stringWithString:text] forName:@"lastYankedText"]; -} - -- (TEXT_TYPE) lastYankedType{ - return (TEXT_TYPE)[[self dataForName:@"lastYankedType"] integerValue]; -} - -- (void) setLastYankedType:(TEXT_TYPE)type{ - [self setInteger:type forName:@"lastYankedType"]; -} - -- (NSString *)xvim_string -{ - return self.textStorage.xvim_buffer.string; -} - -#pragma mark Status - -- (NSUInteger)xvim_numberOfLinesInVisibleRect{ - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:[self selectedRange] inTextContainer:container]; - NSAssert( glyphRect.size.height != 0 , @"Need to fix the code here if the height of current selected character can be 0 here" ); - return [scrollView contentView].bounds.size.height / glyphRect.size.height; -} - - - -#pragma mark Changing state - - -- (void)xvim_changeSelectionMode:(XVIM_VISUAL_MODE)mode{ - if( self.selectionMode == XVIM_VISUAL_NONE && mode != XVIM_VISUAL_NONE ){ - self.selectionBegin = self.insertionPoint; - }else if( self.selectionMode != XVIM_VISUAL_NONE && mode == XVIM_VISUAL_NONE){ - self.selectionBegin = NSNotFound; - } - self.selectionMode = mode; - [self xvim_syncState]; - return; -} - -- (void)xvim_escapeFromInsert{ - if( self.cursorMode == CURSOR_MODE_INSERT ){ - self.cursorMode = CURSOR_MODE_COMMAND; - if(![self.textStorage isBOL:self.insertionPoint]){ - [self xvim_moveCursor:self.insertionPoint-1 preserveColumn:NO]; - } - [self xvim_syncState]; - } -} - -- (void)xvim_setWrapsLines:(BOOL)wraps { -#ifdef __USE_DVTKIT__ - if( [self isKindOfClass:[DVTSourceTextView class]]){ - [(DVTSourceTextView*)self setWrapsLines:wraps]; - } -#endif -} - -#pragma mark Operations -/** - * Adjust cursor position if the position is not valid as normal mode cursor position - * This method may changes selected range of the view. - **/ -- (void)xvim_adjustCursorPosition{ - // If the current cursor position is not valid for normal mode move it. - if( ![self.textStorage isValidCursorPosition:[self selectedRange].location] ){ - NSRange currentRange = [self selectedRange]; - [self xvim_selectPreviousPlaceholder]; - NSRange prevPlaceHolder = [self selectedRange]; - if( currentRange.location != prevPlaceHolder.location && currentRange.location == (prevPlaceHolder.location + prevPlaceHolder.length) ){ - //The condition here means that just before current insertion point is a placeholder. - //So we select the the place holder and its already selected by "selectedPreviousPlaceholder" above - }else{ - [self setSelectedRange:NSMakeRange(currentRange.location-1, 0)]; - } - } - return; -} - -- (void)xvim_moveToIndex:(NSUInteger)index{ - [self xvim_moveCursor:index preserveColumn:NO]; - [self xvim_syncState]; -} - -- (void)xvim_moveToPosition:(XVimPosition)pos{ - NSUInteger index = [self.textStorage.xvim_buffer indexOfLineNumber:pos.line column:pos.column]; - [self xvim_moveToIndex:index]; -} - -- (void)xvim_move:(XVimMotion*)motion{ - XVimRange r = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( r.end == NSNotFound ){ - return; - } - - if( self.selectionMode != XVIM_VISUAL_NONE && [motion isTextObject]){ - if( self.selectionMode == XVIM_VISUAL_LINE){ - // Motion with text object in VISUAL LINE changes visual mode to VISUAL CHARACTER - [self setSelectionMode:XVIM_VISUAL_CHARACTER]; - } - - if( self.insertionPoint < self.selectionBegin ){ - // When insertionPoint < selectionBegin it only changes insertion point to begining of the text object - [self xvim_moveCursor:r.begin preserveColumn:NO]; - }else{ - // Text object expands one text object ( the text object under insertion point + 1 ) - if( ![self.textStorage isEOF:self.insertionPoint+1]){ - r = [self xvim_getMotionRange:self.insertionPoint+1 Motion:motion]; - } - if( self.selectionBegin > r.begin ){ - self.selectionBegin = r.begin; - } - [self xvim_moveCursor:r.end preserveColumn:NO]; - } - } else { - switch( motion.motion ){ - case MOTION_LINE_BACKWARD: - case MOTION_LINE_FORWARD: - case MOTION_LASTLINE: - case MOTION_LINENUMBER: - // TODO: Preserve column option can be included in motion object - if (self.selectionMode == XVIM_VISUAL_BLOCK && self.selectionToEOL) { - r.end = [self.textStorage.xvim_buffer endOfLine:r.end]; - } - [self xvim_moveCursor:r.end preserveColumn:YES]; - break; - case MOTION_END_OF_LINE: - self.selectionToEOL = YES; - [self xvim_moveCursor:r.end preserveColumn:NO]; - break; - - default: - self.selectionToEOL = NO; - [self xvim_moveCursor:r.end preserveColumn:NO]; - break; - } - } - [self setNeedsDisplay:YES]; - [self xvim_syncState]; -} - -- (void)xvim_selectSwapEndsOnSameLine:(BOOL)onSameLine{ - if (self.selectionMode == XVIM_VISUAL_BLOCK) { - XVimPosition start, end; - XVimSelection sel; - NSUInteger pos; - - self.selectionToEOL = NO; - sel = [self _xvim_selectedBlock]; - if (onSameLine) { - sel.corner ^= _XVIM_VISUAL_RIGHT; - } else { - sel.corner ^= _XVIM_VISUAL_RIGHT | _XVIM_VISUAL_BOTTOM; - } - - if (sel.corner & _XVIM_VISUAL_BOTTOM) { - start.line = sel.top; - end.line = sel.bottom; - } else { - end.line = sel.top; - start.line = sel.bottom; - } - - if (sel.corner & _XVIM_VISUAL_RIGHT) { - start.column = sel.left; - end.column = sel.right; - } else { - end.column = sel.left; - start.column = sel.right; - } - - pos = [self.textStorage.xvim_buffer indexOfLineNumber:start.line column:start.column]; - self.selectionBegin = pos; - pos = [self.textStorage.xvim_buffer indexOfLineNumber:end.line column:end.column]; - [self xvim_moveCursor:pos preserveColumn:NO]; - } else if (self.selectionMode != XVIM_VISUAL_NONE) { - NSUInteger begin = self.selectionBegin; - - self.selectionBegin = self.insertionPoint; - [self xvim_moveCursor:begin preserveColumn:NO]; - [self setNeedsDisplay:YES]; - } - [self xvim_syncState]; -} - -- (void)xvim_delete:(XVimMotion*)motion andYank:(BOOL)yank -{ - NSAssert( !(self.selectionMode == XVIM_VISUAL_NONE && motion == nil), - @"motion must be specified if current selection mode is not visual"); - if (self.insertionPoint == 0 && [[self xvim_string] length] == 0) { - return ; - } - NSUInteger newPos = NSNotFound; - - [self xvim_registerInsertionPointForUndo]; - - motion.info->deleteLastLine = NO; - if (self.selectionMode == XVIM_VISUAL_NONE) { - NSRange r; - XVimRange motionRange = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( motionRange.end == NSNotFound ){ - return; - } - // We have to treat some special cases - // When a cursor get end of line with "l" motion, make the motion type to inclusive. - // This make you to delete the last character. (if its exclusive last character never deleted with "dl") - if( motion.motion == MOTION_FORWARD && motion.info->reachedEndOfLine ){ - if( motion.type == CHARACTERWISE_EXCLUSIVE ){ - motion.type = CHARACTERWISE_INCLUSIVE; - }else if( motion.type == CHARACTERWISE_INCLUSIVE ){ - motion.type = CHARACTERWISE_EXCLUSIVE; - } - } - if( motion.motion == MOTION_WORD_FORWARD ){ - if ( (motion.info->isFirstWordInLine && motion.info->lastEndOfLine != NSNotFound )) { - // Special cases for word move over a line break. - motionRange.end = motion.info->lastEndOfLine; - motion.type = CHARACTERWISE_INCLUSIVE; - } - else if( motion.info->reachedEndOfLine ){ - if( motion.type == CHARACTERWISE_EXCLUSIVE ){ - motion.type = CHARACTERWISE_INCLUSIVE; - }else if( motion.type == CHARACTERWISE_INCLUSIVE ){ - motion.type = CHARACTERWISE_EXCLUSIVE; - } - } - } - r = [self xvim_getOperationRangeFrom:motionRange.begin To:motionRange.end Type:motion.type]; - if( motion.type == LINEWISE && [self.textStorage isLastLine:motionRange.end]){ - if( r.location != 0 ){ - motion.info->deleteLastLine = YES; - r.location--; - r.length++; - } - } - if (yank) { - [self _xvim_yankRange:r withType:motion.type]; - } - [self insertText:@"" replacementRange:r]; - if (motion.type == LINEWISE) { - newPos = [self.textStorage.xvim_buffer firstNonblankInLineAtIndex:self.insertionPoint allowEOL:YES]; - } - } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { - BOOL toFirstNonBlank = (self.selectionMode == XVIM_VISUAL_LINE); - NSRange range = [self _xvim_selectedRange]; - - // Currently not supportin deleting EOF with selection mode. - // This is because of the fact that NSTextView does not allow select EOF - - if (yank) { - [self _xvim_yankRange:range withType:DEFAULT_MOTION_TYPE]; - } - [self insertText:@"" replacementRange:range]; - if (toFirstNonBlank) { - newPos = [self.textStorage.xvim_buffer firstNonblankInLineAtIndex:range.location allowEOL:YES]; - } else { - newPos = range.location; - } - } else { - XVimSelection sel = [self _xvim_selectedBlock]; - if (yank) { - [self _xvim_yankSelection:sel]; - } - [self _xvim_killSelection:sel]; - - newPos = [self.textStorage.xvim_buffer indexOfLineNumber:sel.top column:sel.left]; - } - - [self.xvimDelegate textView:self didDelete:self.lastYankedText withType:self.lastYankedType]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - if (newPos != NSNotFound) { - [self xvim_moveCursor:newPos preserveColumn:NO]; - } -} - -- (void)xvim_change:(XVimMotion*)motion{ - // We do not need to call this since this method uses xvim_delete to operate on text - //[self xvim_registerInsertionPointForUndo]; - - BOOL insertNewline = NO; - if( motion.type == LINEWISE || self.selectionMode == XVIM_VISUAL_LINE){ - // 'cc' deletes the lines but need to keep the last newline. - // So insertNewline as 'O' does before entering insert mode - insertNewline = YES; - } - - // "cw" is like "ce" if the cursor is on a word ( in this case blank line is not treated as a word ) - if( motion.motion == MOTION_WORD_FORWARD && [self.textStorage isNonblank:self.insertionPoint] ){ - motion.motion = MOTION_END_OF_WORD_FORWARD; - motion.type = CHARACTERWISE_INCLUSIVE; - motion.option |= MOTION_OPTION_CHANGE_WORD; - } - self.cursorMode = CURSOR_MODE_INSERT; - [self xvim_delete:motion andYank:YES]; - if( motion.info->deleteLastLine){ - [self xvim_insertNewlineAboveLine:[self.textStorage.xvim_buffer lineNumberAtIndex:self.insertionPoint]]; - } - else if( insertNewline ){ - [self xvim_insertNewlineAboveLine:[self.textStorage.xvim_buffer lineNumberAtIndex:self.insertionPoint]]; - }else{ - - } - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - [self xvim_syncState]; -} - -- (void)xvim_yank:(XVimMotion*)motion{ - NSAssert( !(self.selectionMode == XVIM_VISUAL_NONE && motion == nil), @"motion must be specified if current selection mode is not visual"); - NSUInteger newPos = NSNotFound; - - if( self.selectionMode == XVIM_VISUAL_NONE ){ - NSRange r; - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( NSNotFound == to.end ){ - return; - } - // We have to treat some special cases (same as delete) - if( motion.motion == MOTION_FORWARD && motion.info->reachedEndOfLine){ - motion.type = CHARACTERWISE_INCLUSIVE; - } - if( motion.motion == MOTION_WORD_FORWARD ){ - if ( (motion.info->isFirstWordInLine && motion.info->lastEndOfLine != NSNotFound )) { - // Special cases for word move over a line break. - to.end = motion.info->lastEndOfLine; - motion.type = CHARACTERWISE_INCLUSIVE; - } - else if( motion.info->reachedEndOfLine ){ - if( motion.type == CHARACTERWISE_EXCLUSIVE ){ - motion.type = CHARACTERWISE_INCLUSIVE; - }else if( motion.type == CHARACTERWISE_INCLUSIVE ){ - motion.type = CHARACTERWISE_EXCLUSIVE; - } - } - } - r = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:motion.type]; - BOOL eof = [self.textStorage isEOF:to.end]; - BOOL blank = [self.textStorage isBlankline:to.end]; - if( motion.type == LINEWISE && blank && eof){ - if( r.location != 0 ){ - r.location--; - r.length++; - } - } - [self _xvim_yankRange:r withType:motion.type]; - } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { - NSRange range = [self _xvim_selectedRange]; - - newPos = range.location; - [self _xvim_yankRange:range withType:DEFAULT_MOTION_TYPE]; - } else { - XVimSelection sel = [self _xvim_selectedBlock]; - - newPos = [self.textStorage.xvim_buffer indexOfLineNumber:sel.top column:sel.left]; - [self _xvim_yankSelection:sel]; - } - - [self.xvimDelegate textView:self didYank:self.lastYankedText withType:self.lastYankedType]; - if (newPos != NSNotFound) { - [self xvim_moveCursor:newPos preserveColumn:NO]; - } - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)xvim_put:(NSString*)text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)count{ - [self xvim_registerInsertionPointForUndo]; - - TRACE_LOG(@"text:%@ type:%d afterCursor:%d count:%d", text, type, after, count); - if( self.selectionMode != XVIM_VISUAL_NONE ){ - // FIXME: Make them not to change text from register... - text = [NSString stringWithString:text]; // copy string because the text may be changed with folloing delete if it is from the same register... - [self xvim_delete:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 1) andYank:YES]; - after = NO; - } - - NSUInteger insertionPointAfterPut = self.insertionPoint; - NSUInteger targetPos = self.insertionPoint; - if( type == TEXT_TYPE_CHARACTERS ){ - //Forward insertion point +1 if after flag if on - if( 0 != text.length ){ - if (![self.textStorage isNewline:self.insertionPoint] && after) { - targetPos++; - } - insertionPointAfterPut = targetPos; - for(NSUInteger i = 0; i < count ; i++ ){ - [self insertText:text replacementRange:NSMakeRange(targetPos,0)]; - } - insertionPointAfterPut += text.length*count - 1; - } - }else if( type == TEXT_TYPE_LINES ){ - if( after ){ - [self xvim_insertNewlineBelowCurrentLine]; - targetPos = self.insertionPoint; - }else{ - targetPos= [self.textStorage.xvim_buffer startOfLine:self.insertionPoint]; - } - insertionPointAfterPut = targetPos; - for(NSUInteger i = 0; i < count ; i++ ){ - if( after && i == 0 ){ - // delete newline at the end. (TEXT_TYPE_LINES always have newline at the end of the text) - NSString* t = [text substringToIndex:text.length-1]; - [self insertText:t replacementRange:NSMakeRange(targetPos,0)]; - } else{ - [self insertText:text replacementRange:NSMakeRange(targetPos,0)]; - } - } - }else if( type == TEXT_TYPE_BLOCK ){ - //Forward insertion point +1 if after flag if on - if (![self.textStorage isNewline:self.insertionPoint] && ![self.textStorage isEOF:self.insertionPoint] && after) { - self.insertionPoint++; - } - insertionPointAfterPut = self.insertionPoint; - NSUInteger insertPos = self.insertionPoint; - NSUInteger column = [self.textStorage.xvim_buffer columnOfIndex:insertPos]; - NSUInteger startLine = [self.textStorage.xvim_buffer lineNumberAtIndex:insertPos]; - NSArray* lines = [text componentsSeparatedByString:@"\n"]; - for( NSUInteger i = 0 ; i < lines.count ; i++){ - NSString* line = [lines objectAtIndex:i]; - NSUInteger targetLine = startLine + i; - NSUInteger head = [self.textStorage.xvim_buffer indexOfLineNumber:targetLine]; - if( NSNotFound == head ){ - NSAssert( targetLine != 0, @"This should not be happen"); - [self xvim_insertNewlineBelowLine:targetLine-1]; - head = [self.textStorage.xvim_buffer indexOfLineNumber:targetLine]; - } - NSAssert( NSNotFound != head , @"Head of the target line must be found at this point"); - - // Find next insertion point - NSUInteger max = [self.textStorage.xvim_buffer numberOfColumnsInLineAtIndex:head]; - NSAssert( max != NSNotFound , @"Should not be NSNotFound"); - if( column > max ){ - // If the line does not have enough column pad it with spaces - NSUInteger end = [self.textStorage.xvim_buffer endOfLine:head]; - - [self _xvim_insertSpaces:column - max replacementRange:NSMakeRange(end, 0)]; - } - for(NSUInteger i = 0; i < count ; i++ ){ - [self xvim_insertText:line line:targetLine column:column]; - } - } - } - - - [self xvim_moveCursor:insertionPointAfterPut preserveColumn:NO]; - [self xvim_syncState]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)xvim_swapCharacters:(XVimMotion *)motion mode:(int)mode -{ - XVimBuffer *buffer = self.textStorage.xvim_buffer; - NSUInteger undoPos = self.insertionPoint; - NSUInteger endPos; - - if (buffer.length == 0) { - return; - } - - if (self.selectionMode == XVIM_VISUAL_NONE) { - NSRange range; - - if (motion.motion == MOTION_NONE) { - XVimMotion *m = XVIM_MAKE_MOTION(MOTION_FORWARD,CHARACTERWISE_EXCLUSIVE,LEFT_RIGHT_NOWRAP,motion.count); - XVimRange r = [self xvim_getMotionRange:undoPos Motion:m]; - - if (r.end == NSNotFound) { - return; - } - if (m.info->reachedEndOfLine) { - range = [self xvim_getOperationRangeFrom:r.begin To:r.end Type:CHARACTERWISE_INCLUSIVE]; - } else { - range = [self xvim_getOperationRangeFrom:r.begin To:r.end Type:CHARACTERWISE_EXCLUSIVE]; - } - endPos = r.end; - } else { - XVimRange to = [self xvim_getMotionRange:undoPos Motion:motion]; - if (to.end == NSNotFound) { - return; - } - - range = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:motion.type]; - endPos = range.location; - } - - [buffer beginEditingAtIndex:undoPos]; - [buffer swapCharactersInRange:range mode:mode]; - [buffer endEditingAtIndex:endPos]; - } else { - NSArray *ranges = [self xvim_selectedRanges]; - - endPos = undoPos = [[ranges objectAtIndex:0] rangeValue].location; - [buffer beginEditingAtIndex:undoPos]; - for (NSValue *v in ranges) { - [buffer swapCharactersInRange:v.rangeValue mode:mode]; - } - [buffer endEditingAtIndex:endPos]; - } - - [self xvim_moveCursor:endPos preserveColumn:NO]; - [self xvim_syncState]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (BOOL)xvim_replaceCharacters:(unichar)c count:(NSUInteger)count{ - NSUInteger end = [self.textStorage.xvim_buffer endOfLine:self.insertionPoint]; - // Note : endOfLine may return one less than self.insertionPoint if self.insertionPoint is on newline - if( NSNotFound == end ){ - return NO; - } - NSUInteger num = end - self.insertionPoint + 1; - if( num < count ){ - return NO; - } - - end = self.insertionPoint+count; - for( NSUInteger pos = self.insertionPoint; pos < end; pos++){ - [self insertText:[NSString stringWithFormat:@"%c",c] replacementRange:NSMakeRange(pos, 1)]; - } - return YES; -} - -- (void)xvim_joinAtLineNumber:(NSUInteger)line{ - BOOL needSpace = NO; - NSUInteger headOfLine = [self.textStorage.xvim_buffer indexOfLineNumber:line]; - if( headOfLine == NSNotFound){ - return; - } - - NSUInteger tail = [self.textStorage.xvim_buffer endOfLine:headOfLine]; - if( [self.textStorage isEOF:tail] ){ - // This is the last line and nothing to join - return; - } - - // Check if we need to insert space between lines. - NSUInteger lastOfLine = [self.textStorage.xvim_buffer lastOfLine:headOfLine]; - if( lastOfLine != NSNotFound ){ - // This is not blank line so we check if the last character is space or not . - if( ![self.textStorage isWhitespace:lastOfLine] ){ - needSpace = YES; - } - } - - // Search in next line for the position to join(skip white spaces in next line) - NSUInteger posToJoin = [self.textStorage nextLine:headOfLine column:0 count:1 option:MOTION_OPTION_NONE]; - - posToJoin = [self.textStorage.xvim_buffer nextNonblankInLineAtIndex:posToJoin allowEOL:YES]; - if (![self.textStorage isEOF:posToJoin] && [self.string characterAtIndex:posToJoin] == ')') { - needSpace = NO; - } - - // delete "tail" to "posToJoin" excluding the position of "posToJoin" and insert space if need. - if( needSpace ){ - [self insertText:@" " replacementRange:NSMakeRange(tail, posToJoin-tail)]; - }else{ - [self insertText:@"" replacementRange:NSMakeRange(tail, posToJoin-tail)]; - } - - // Move cursor - [self xvim_moveCursor:tail preserveColumn:NO]; -} - -- (void)xvim_join:(NSUInteger)count addSpace:(BOOL)addSpace{ - NSUInteger line; - - [self xvim_registerInsertionPointForUndo]; - - if (self.selectionMode == XVIM_VISUAL_NONE) { - line = self.insertionLine; - } else { - XVimRange lines = [self _xvim_selectedLines]; - - line = lines.begin; - count = MAX(1, lines.end - lines.begin); - } - - if (addSpace) { - for (NSUInteger i = 0; i < count; i++) { - [self xvim_joinAtLineNumber:line]; - } - } else { - XVimBuffer *buffer = self.textStorage.xvim_buffer; - NSUInteger pos = [buffer indexOfLineNumber:line]; - - for (NSUInteger i = 0; i < count; i++) { - NSUInteger tail = [buffer endOfLine:pos]; - - if (tail != NSNotFound && ![self.textStorage isEOF:tail]) { - [self insertText:@"" replacementRange:NSMakeRange(tail, 1)]; - [self xvim_moveCursor:tail preserveColumn:NO]; - } - } - } - - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)xvim_filter:(XVimMotion*)motion{ - if (self.insertionPoint == 0 && [[self xvim_string] length] == 0) { - return ; - } - - NSUInteger insertionAfterFilter = self.insertionPoint; - NSRange filterRange; - if (self.selectionMode == XVIM_VISUAL_NONE) { - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if (to.end == NSNotFound) { - return; - } - filterRange = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:LINEWISE]; - } else { - XVimRange lines = [self _xvim_selectedLines]; - NSUInteger from = [self.textStorage.xvim_buffer indexOfLineNumber:lines.begin]; - NSUInteger to = [self.textStorage.xvim_buffer indexOfLineNumber:lines.end]; - filterRange = [self xvim_getOperationRangeFrom:from To:to Type:LINEWISE]; - } - - [self xvim_indentCharacterRange:filterRange]; - [self xvim_moveCursor:insertionAfterFilter preserveColumn:NO]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right -{ - if (self.insertionPoint == 0 && [[self xvim_string] length] == 0) { - return ; - } - - XVimBuffer *buffer = self.textStorage.xvim_buffer; - NSUInteger shiftWidth = buffer.indentWidth; - NSUInteger column = 0, pos; - XVimRange lines; - BOOL blockMode = NO; - - if (self.selectionMode == XVIM_VISUAL_NONE) { - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if (to.end == NSNotFound) { - return; - } - lines = XVimMakeRange([buffer lineNumberAtIndex:to.begin], [buffer lineNumberAtIndex:to.end]); - } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { - lines = [self _xvim_selectedLines]; - shiftWidth *= motion.count; - } else { - XVimSelection sel = [self _xvim_selectedBlock]; - - column = sel.left; - lines = XVimMakeRange(sel.top, sel.bottom); - shiftWidth *= motion.count; - blockMode = YES; - } - - if (blockMode) { - pos = [buffer indexOfLineNumber:lines.begin column:column]; - } else { - pos = [buffer indexOfLineNumber:lines.begin]; - pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; - } - - [buffer beginEditingAtIndex:pos]; - pos = [buffer shiftLines:lines column:column - count:shiftWidth right:right block:blockMode]; - [buffer endEditingAtIndex:pos]; - - [self xvim_moveCursor:pos preserveColumn:NO]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)xvim_shiftRight:(XVimMotion*)motion{ - [self _xvim_shift:motion right:YES]; -} - -- (void)xvim_shiftLeft:(XVimMotion*)motion{ - [self _xvim_shift:motion right:NO]; -} - -- (void)xvim_insertText:(NSString*)str line:(NSUInteger)line column:(NSUInteger)column{ - NSUInteger pos = [self.textStorage.xvim_buffer indexOfLineNumber:line column:column]; - if( pos == NSNotFound ){ - return; - } - [self insertText:str replacementRange:NSMakeRange(pos,0)]; -} - -- (void)xvim_insertNewlineBelowLine:(NSUInteger)line{ - NSAssert( line != 0, @"line number starts from 1"); - NSUInteger pos = [self.textStorage.xvim_buffer indexOfLineNumber:line]; - if( NSNotFound == pos ){ - return; - } - pos = [self.textStorage.xvim_buffer endOfLine:pos]; - [self insertText:@"\n" replacementRange:NSMakeRange(pos ,0)]; - [self xvim_moveCursor:pos+1 preserveColumn:NO]; - [self xvim_syncState]; -} - -- (void)xvim_insertNewlineBelowCurrentLine{ - [self xvim_insertNewlineBelowLine:[self.textStorage.xvim_buffer lineNumberAtIndex:self.insertionPoint]]; -} - -- (void)xvim_insertNewlineBelowCurrentLineWithIndent{ - NSUInteger tail = [self.textStorage.xvim_buffer endOfLine:self.insertionPoint]; - [self setSelectedRange:NSMakeRange(tail,0)]; - [self insertNewline:self]; -} - -- (void)xvim_insertNewlineAboveLine:(NSUInteger)line{ - NSAssert( line != 0, @"line number starts from 1"); - NSUInteger pos = [self.textStorage.xvim_buffer indexOfLineNumber:line]; - if( NSNotFound == pos ){ - return; - } - if( 1 != line ){ - [self xvim_insertNewlineBelowLine:line-1]; - }else{ - [self insertText:@"\n" replacementRange:NSMakeRange(0,0)]; - [self setSelectedRange:NSMakeRange(0,0)]; - } -} - -- (void)xvim_insertNewlineAboveCurrentLine{ - [self xvim_insertNewlineAboveLine:[self.textStorage.xvim_buffer lineNumberAtIndex:self.insertionPoint]]; -} - -- (void)xvim_insertNewlineAboveCurrentLineWithIndent{ - NSUInteger head = [self.textStorage.xvim_buffer firstOfLine:self.insertionPoint]; - if( NSNotFound == head ){ - head = self.insertionPoint; - } - if( 0 != head ){ - [self setSelectedRange:NSMakeRange(head-1,0)]; - [self insertNewline:self]; - }else{ - [self setSelectedRange:NSMakeRange(head,0)]; - [self insertNewline:self]; - [self setSelectedRange:NSMakeRange(0,0)]; - } -} - -- (void)xvim_insertNewlineAboveAndInsertWithIndent{ - self.cursorMode = CURSOR_MODE_INSERT; - [self xvim_insertNewlineAboveCurrentLineWithIndent]; -} - -- (void)xvim_insertNewlineBelowAndInsertWithIndent{ - self.cursorMode = CURSOR_MODE_INSERT; - [self xvim_insertNewlineBelowCurrentLineWithIndent]; -} - -- (void)xvim_insert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column blockLines:(XVimRange *)lines{ - NSTextStorage *ts = self.textStorage; - XVimBuffer *buffer = ts.xvim_buffer; - - if (column) *column = NSNotFound; - if (lines) *lines = XVimMakeRange(NSNotFound, NSNotFound); - - if (self.selectionMode == XVIM_VISUAL_BLOCK) { - XVimSelection sel = [self _xvim_selectedBlock]; - - if (lines) *lines = XVimMakeRange(sel.top, sel.bottom); - switch (mode) { - case XVIM_INSERT_BLOCK_KILL_EOL: - sel.right = XVimSelectionEOL; - /* fallthrough */ - case XVIM_INSERT_BLOCK_KILL: - [self _xvim_yankSelection:sel]; - [self _xvim_killSelection:sel]; - /* falltrhough */ - case XVIM_INSERT_DEFAULT: - self.insertionPoint = [buffer indexOfLineNumber:sel.top column:sel.left]; - if (column) *column = sel.left; - break; - case XVIM_INSERT_APPEND: - if (sel.right != XVimSelectionEOL) { - sel.right++; - } - self.insertionPoint = [buffer indexOfLineNumber:sel.top column:sel.right]; - if (column) *column = sel.right; - break; - default: - NSAssert(false, @"unreachable"); - break; - } - } else if (mode != XVIM_INSERT_DEFAULT) { - NSUInteger pos = self.insertionPoint; - switch (mode) { - case XVIM_INSERT_APPEND_EOL: - self.insertionPoint = [buffer endOfLine:pos]; - break; - case XVIM_INSERT_APPEND: - NSAssert(self.cursorMode == CURSOR_MODE_COMMAND, @"self.cursorMode shoud be CURSOR_MODE_COMMAND"); - if (![ts isEOF:pos] && ![ts isNewline:pos]){ - self.insertionPoint = pos + 1; - } - break; - case XVIM_INSERT_BEFORE_FIRST_NONBLANK: - self.insertionPoint = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; - break; - default: - NSAssert(false, @"unreachable"); - } - } - self.cursorMode = CURSOR_MODE_INSERT; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - [self xvim_syncState]; -} - -- (void)xvim_overwriteCharacter:(unichar)c{ - if (self.insertionPoint >= self.textStorage.length) { - // Should not happen. - return; - } - [self insertText:[NSString stringWithFormat:@"%c",c] replacementRange:NSMakeRange(self.insertionPoint,1)]; - return; -} - -- (BOOL)xvim_incrementNumber:(int64_t)offset -{ - NSUInteger ip = self.insertionPoint; - - ip = [self.textStorage.xvim_buffer incrementNumberAtIndex:ip by:offset]; - if (ip == NSNotFound) { - return NO; - } - [self xvim_moveCursor:ip preserveColumn:NO]; - return YES; -} - -- (void)xvim_blockInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint)mode - count:(NSUInteger)count column:(NSUInteger)column lines:(XVimRange)lines -{ - NSMutableString *buf = nil; - NSTextStorage *ts; - NSUInteger tabWidth; - XVimBuffer *buffer; - - if (count == 0 || lines.begin > lines.end || text.length == 0) { - return; - } - if ([text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location != NSNotFound) { - return; - } - if (count > 1) { - buf = [[NSMutableString alloc] initWithCapacity:text.length * count]; - for (NSUInteger i = 0; i < count; i++) { - [buf appendString:text]; - } - text = buf; - } - - ts = self.textStorage; - buffer = ts.xvim_buffer; - tabWidth = buffer.tabWidth; - - for (NSUInteger line = lines.begin; line <= lines.end; line++) { - NSUInteger pos = [buffer indexOfLineNumber:line column:column]; - - if (column != XVimSelectionEOL && [ts isEOL:pos]) { - if ([buffer columnOfIndex:pos] < column) { - if (mode != XVIM_INSERT_APPEND) { - continue; - } - [self _xvim_insertSpaces:column - [buffer columnOfIndex:pos] replacementRange:NSMakeRange(pos, 0)]; - } - } - if (tabWidth && [self.xvim_string characterAtIndex:pos] == '\t') { - NSUInteger col = [buffer columnOfIndex:pos]; - - if (col < column) { - [self _xvim_insertSpaces:tabWidth - (col % tabWidth) replacementRange:NSMakeRange(pos, 1)]; - pos += column - col; - } - } - [self insertText:text replacementRange:NSMakeRange(pos, 0)]; - } - - [buf release]; -} - -- (void)xvim_sortLinesFrom:(NSUInteger)line1 to:(NSUInteger)line2 withOptions:(XVimSortOptions)options{ - NSAssert( line1 > 0, @"line1 must be greater than 0."); - NSAssert( line2 > 0, @"line2 must be greater than 0."); - - if( line2 < line1 ){ - //swap - NSUInteger tmp = line1; - line1 = line2; - line2 = tmp; - } - - NSRange characterRange = [self.textStorage.xvim_buffer indexRangeForLines:NSMakeRange(line1, line2 - line1 + 1)]; - NSString *str = [[self xvim_string] substringWithRange:characterRange]; - - NSMutableArray *lines = [[[str componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] mutableCopy] autorelease]; - if ([[lines lastObject] length] == 0) { - [lines removeLastObject]; - } - [lines sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) { - NSStringCompareOptions compareOptions = 0; - if (options & XVimSortOptionNumericSort) { - compareOptions |= NSNumericSearch; - } - if (options & XVimSortOptionIgnoreCase) { - compareOptions |= NSCaseInsensitiveSearch; - } - - if (options & XVimSortOptionReversed) { - return [str2 compare:str1 options:compareOptions]; - } else { - return [str1 compare:str2 options:compareOptions]; - } - }]; - - if (options & XVimSortOptionRemoveDuplicateLines) { - NSMutableIndexSet *removeIndices = [NSMutableIndexSet indexSet]; - // At this point the lines are already sorted - [lines enumerateObjectsUsingBlock:^(NSString *str, NSUInteger idx, BOOL *stop) { - if (idx < [lines count] - 1) { - NSString *nextStr = [lines objectAtIndex:idx + 1]; - if ([str isEqualToString:nextStr]) { - [removeIndices addIndex:idx + 1]; - } - } - }]; - [lines removeObjectsAtIndexes:removeIndices]; - } - - NSUInteger insertionAfterOperation = characterRange.location; - NSString *sortedLinesString = [[lines componentsJoinedByString:@"\n"] stringByAppendingString:@"\n"]; - if( [self shouldChangeTextInRange:characterRange replacementString:sortedLinesString] ){ - [self replaceCharactersInRange:characterRange withString:sortedLinesString]; - [self didChangeText]; - } - self.insertionPoint = insertionAfterOperation; - [self xvim_syncState]; -} - -- (void)xvim_selectNextPlaceholder { -#ifdef __USE_DVTKIT__ - if( [self isKindOfClass:[DVTSourceTextView class]] ){ - [(DVTSourceTextView*)self selectNextPlaceholder:self]; - } -#endif -} - -- (void)xvim_selectPreviousPlaceholder { -#ifdef __USE_DVTKIT__ - if( [self isKindOfClass:[DVTSourceTextView class]] ){ - [(DVTSourceTextView*)self selectPreviousPlaceholder:self]; - } -#endif -} - -- (void)xvim_hideCompletions { -#ifdef __USE_DVTKIT__ - if( [self isKindOfClass:[DVTSourceTextView class]] ){ - [((DVTSourceTextView*)self).completionController hideCompletions]; - } -#endif -} - -#pragma mark Search -// Thanks to http://lists.apple.com/archives/cocoa-dev/2005/Jun/msg01909.html -- (NSRange)xvim_visibleRange:(NSTextView *)tv{ - NSScrollView *sv = [tv enclosingScrollView]; - if(!sv) return NSMakeRange(0,0); - NSLayoutManager *lm = [tv layoutManager]; - NSRect visRect = [tv visibleRect]; - - NSPoint tco = [tv textContainerOrigin]; - visRect.origin.x -= tco.x; - visRect.origin.y -= tco.y; - - NSRange glyphRange = [lm glyphRangeForBoundingRect:visRect inTextContainer:[tv textContainer]]; - NSRange charRange = [lm characterRangeForGlyphRange:glyphRange actualGlyphRange:nil]; - return charRange; -} - -- (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward{ - NSRange range = NSMakeRange(NSNotFound,0); - if( forward ){ - range = [self.textStorage searchRegexForward:regex from:self.insertionPoint count:count option:opt]; - }else{ - range = [self.textStorage searchRegexBackward:regex from:self.insertionPoint count:count option:opt]; - } - if( range.location != NSNotFound ){ - [self.xvim_view scrollTo:range.location]; - [self showFindIndicatorForRange:range]; - } -} - -- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt{ - [self xvim_highlightNextSearchCandidate:regex count:count option:opt forward:YES]; -} - -- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt{ - [self xvim_highlightNextSearchCandidate:regex count:count option:opt forward:NO]; -} - -- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt{ - NSAssert( nil != pattern, @"pattern munst not be nil"); - if( !self.needsUpdateFoundRanges ){ - return; - } - - NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines; - if ( opt & SEARCH_CASEINSENSITIVE ){ - r_opts |= NSRegularExpressionCaseInsensitive; - } - - NSError *error = nil; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:r_opts error:&error]; - if( nil != error){ - [self.foundRanges removeAllObjects]; - return; - } - - // Find all the maches - NSString* string = self.string; - //NSTextStorage* storage = self.textStorage; - if( string == nil ){ - return; - } - NSArray* matches = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)]; - [self.foundRanges setArray:matches]; - - // Clear current highlight. - [self xvim_clearHighlightText]; - // Add yellow highlight - for( NSTextCheckingResult* result in self.foundRanges){ - [self.layoutManager addTemporaryAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] forCharacterRange:result.range]; - } - - [self setNeedsUpdateFoundRanges:NO]; -} - -- (void)xvim_clearHighlightText{ - if( !self.needsUpdateFoundRanges ){ - return; - } - NSString* string = self.string; - [self.layoutManager removeTemporaryAttribute:NSBackgroundColorAttributeName forCharacterRange:NSMakeRange(0,string.length)]; - // [self.layoutManager addTemporaryAttribute:NSBackgroundColorAttributeName value:[NSColor clearColor] forCharacterRange:NSMakeRange(0, string.length)]; - [self setNeedsUpdateFoundRanges:NO]; -} - -- (NSRange)xvim_currentWord:(MOTION_OPTION)opt{ - return [self.textStorage currentWord:self.insertionPoint count:1 option:opt|TEXTOBJECT_INNER]; -} - -#pragma mark Search Position - -/** - *Find and return an NSArray* with the placeholders in a current line. - * the placeholders are returned as NSValue* objects that encode NSRange structs. - * Returns an empty NSArray if there are no placeholders on the line. - */ --(NSArray*)xvim_placeholdersInLine:(NSUInteger)position{ - NSMutableArray* placeholders = [[NSMutableArray alloc] initWithCapacity:2]; - NSUInteger p = [self.textStorage.xvim_buffer firstOfLine:position]; - - for(NSUInteger curPos = p; curPos < [[self xvim_string] length]; curPos++){ - NSRange retval = [(DVTCompletingTextView*)self rangeOfPlaceholderFromCharacterIndex:curPos forward:YES wrap:NO limit:50]; - if(retval.location != NSNotFound){ - curPos = retval.location + retval.length; - [placeholders addObject:[NSValue valueWithRange:retval]]; - } - if ([self.textStorage isLOL:curPos] || [self.textStorage isEOF:curPos]) { - return [placeholders autorelease]; - } - } - - return [placeholders autorelease]; -} - - -#pragma mark helper methods - -- (void)xvim_syncStateFromView{ - // TODO: handle block selection (if selectedRanges have multiple ranges ) - if( self.xvim_lockSyncStateFromView ){ - return; - } - NSRange r = [self selectedRange]; - DEBUG_LOG(@"Selected Range(TotalLen:%d): Loc:%d Len:%d", self.string.length, r.location, r.length); - self.selectionMode = XVIM_VISUAL_NONE; - [self xvim_moveCursor:r.location preserveColumn:NO]; - self.selectionBegin = self.insertionPoint; -} - -@end - - -@implementation NSTextView(VimOperationPrivate) -#pragma mark Properties - -- (BOOL)xvim_lockSyncStateFromView{ - id ret = [self dataForName:@"lockSyncStateFromView"]; - return nil == ret ? NO : [ret boolValue]; -} - -- (void)setXvim_lockSyncStateFromView:(BOOL)lock{ - [self setBool:lock forName:@"lockSyncStateFromView"]; -} - -/** - * Returns start and end position of the specified motion. - * Note that this may return NSNotFound - **/ - -- (void)xvim_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve{ - // This method only update the internal state(like self.insertionPoint) - - if( pos > [self xvim_string].length){ - ERROR_LOG(@"Position specified exceeds the length of the text"); - pos = [self xvim_string].length; - } - - if( self.cursorMode == CURSOR_MODE_COMMAND && !(self.selectionMode == XVIM_VISUAL_BLOCK)){ - self.insertionPoint = [self.textStorage convertToValidCursorPositionForNormalMode:pos]; - }else{ - self.insertionPoint = pos; - } - - if( !preserve ){ - self.preservedColumn = [self.textStorage.xvim_buffer columnOfIndex:self.insertionPoint]; - } - - DEBUG_LOG(@"New Insertion Point:%d Preserved Column:%d", self.insertionPoint, self.preservedColumn); -} - -- (void)_adjustCursorPosition{ - if( ![self.textStorage isValidCursorPosition:self.insertionPoint] ){ - NSRange placeholder = [(DVTSourceTextView*)self rangeOfPlaceholderFromCharacterIndex:self.insertionPoint forward:NO wrap:NO limit:0]; - if( placeholder.location != NSNotFound && self.insertionPoint == (placeholder.location + placeholder.length)){ - //The condition here means that just before current insertion point is a placeholder. - //So we select the the place holder and its already selected by "selectedPreviousPlaceholder" above - [self xvim_moveCursor:placeholder.location preserveColumn:YES]; - }else{ - [self xvim_moveCursor:self.insertionPoint-1 preserveColumn:YES]; - } - } - -} - -/** - * Applies internal state to underlying view (self). - * This update self's property and applies the visual effect on it. - * All the state need to express Vim is held by this class and - * we use self to express it visually. - **/ -- (void)xvim_syncState{ - DEBUG_LOG(@"IP:%d", self.insertionPoint); - self.xvim_lockSyncStateFromView = YES; - // Reset current selection - if( self.cursorMode == CURSOR_MODE_COMMAND ){ - [self _adjustCursorPosition]; - } - [self dumpState]; - -#ifdef __XCODE5__ - [self setSelectedRanges:[self xvim_selectedRanges] affinity:NSSelectionAffinityDownstream stillSelecting:NO]; -#else - [(DVTFoldingTextStorage*)self.textStorage increaseUsingFoldedRanges]; - [self setSelectedRanges:[self xvim_selectedRanges] affinity:NSSelectionAffinityDownstream stillSelecting:NO]; - [(DVTFoldingTextStorage*)self.textStorage decreaseUsingFoldedRanges]; -#endif - [self.xvim_view scrollTo:self.insertionPoint]; - self.xvim_lockSyncStateFromView = NO; -} - -- (void)dumpState{ - LOG_STATE(); -} - -- (NSArray*)xvim_selectedRanges{ - if (self.selectionMode != XVIM_VISUAL_BLOCK) { - return [NSArray arrayWithObject:[NSValue valueWithRange:[self _xvim_selectedRange]]]; - } - - NSMutableArray *rangeArray = [[[NSMutableArray alloc] init] autorelease]; - NSTextStorage *ts = self.textStorage; - XVimBuffer *buffer = ts.xvim_buffer; - XVimSelection sel = [self _xvim_selectedBlock]; - - for (NSUInteger line = sel.top; line <= sel.bottom; line++) { - NSUInteger begin = [buffer indexOfLineNumber:line column:sel.left]; - NSUInteger end = [buffer indexOfLineNumber:line column:sel.right]; - - if ([ts isEOF:begin]) { - continue; - } - if ([ts isEOF:end]){ - end--; - } else if (sel.right != XVimSelectionEOL && [ts isEOL:end]) { - end--; - } - [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(begin, end - begin + 1)]]; - } - return rangeArray; -} - -- (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion{ - NSRange range = NSMakeRange( NSNotFound , 0 ); - NSUInteger begin = current; - NSUInteger end = NSNotFound; - NSUInteger tmpPos = NSNotFound; - NSUInteger start = NSNotFound; - NSTextStorage *ts = self.textStorage; - XVimBuffer *buffer = ts.xvim_buffer; - XVimView *xview = self.xvim_view; - - switch (motion.motion) { - case MOTION_NONE: - // Do nothing - break; - case MOTION_FORWARD: - end = [ts next:begin count:motion.count option:motion.option info:motion.info]; - break; - case MOTION_BACKWARD: - end = [ts prev:begin count:motion.count option:motion.option ]; - break; - case MOTION_WORD_FORWARD: - end = [ts wordsForward:begin count:motion.count option:motion.option info:motion.info]; - break; - case MOTION_WORD_BACKWARD: - end = [ts wordsBackward:begin count:motion.count option:motion.option]; - break; - case MOTION_END_OF_WORD_FORWARD: - end = [ts endOfWordsForward:begin count:motion.count option:motion.option]; - break; - case MOTION_END_OF_WORD_BACKWARD: - end = [ts endOfWordsBackward:begin count:motion.count option:motion.option]; - break; - case MOTION_LINE_FORWARD: - end = [ts nextLine:begin column:self.preservedColumn count:motion.count option:motion.option]; - break; - case MOTION_LINE_BACKWARD: - end = [ts prevLine:begin column:self.preservedColumn count:motion.count option:motion.option]; - break; - case MOTION_BEGINNING_OF_LINE: - end = [ts.xvim_buffer startOfLine:begin]; - if( end == NSNotFound){ - end = current; - } - break; - case MOTION_END_OF_LINE: - tmpPos = [ts nextLine:begin column:0 count:motion.count-1 option:MOTION_OPTION_NONE]; - end = [ts.xvim_buffer endOfLine:tmpPos]; - if( end == NSNotFound){ - end = tmpPos; - } - break; - case MOTION_SENTENCE_FORWARD: - end = [ts sentencesForward:begin count:motion.count option:motion.option]; - break; - case MOTION_SENTENCE_BACKWARD: - end = [ts sentencesBackward:begin count:motion.count option:motion.option]; - break; - case MOTION_PARAGRAPH_FORWARD: - end = [ts moveFromIndex:begin paragraphs:motion.scount option:motion.option]; - break; - case MOTION_PARAGRAPH_BACKWARD: - end = [ts moveFromIndex:begin paragraphs:-motion.scount option:motion.option]; - break; - case MOTION_NEXT_CHARACTER: - end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; - break; - case MOTION_PREV_CHARACTER: - end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; - break; - case MOTION_TILL_NEXT_CHARACTER: - end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; - if(end != NSNotFound){ - end--; - } - break; - case MOTION_TILL_PREV_CHARACTER: - end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; - if(end != NSNotFound){ - end++; - } - break; - case MOTION_NEXT_FIRST_NONBLANK: - end = [ts nextLine:begin column:0 count:motion.count option:motion.option]; - tmpPos = [ts.xvim_buffer nextNonblankInLineAtIndex:end allowEOL:NO]; - if( NSNotFound != tmpPos ){ - end = tmpPos; - } - break; - case MOTION_PREV_FIRST_NONBLANK: - end = [ts prevLine:begin column:0 count:motion.count option:motion.option]; - tmpPos = [ts.xvim_buffer nextNonblankInLineAtIndex:end allowEOL:NO]; - if( NSNotFound != tmpPos ){ - end = tmpPos; - } - break; - case MOTION_FIRST_NONBLANK: - end = [ts.xvim_buffer firstNonblankInLineAtIndex:begin allowEOL:NO]; - break; - case MOTION_LINENUMBER: - end = [ts.xvim_buffer indexOfLineNumber:motion.line column:self.preservedColumn]; - if( NSNotFound == end ){ - end = [buffer indexOfLineNumber:[buffer numberOfLines] column:self.preservedColumn]; - } - break; - case MOTION_PERCENT: - end = [buffer indexOfLineNumber:1 + ([buffer numberOfLines]-1) * motion.count/100]; - break; - case MOTION_NEXT_MATCHED_ITEM: - end = [ts positionOfMatchedPair:begin]; - break; - case MOTION_LASTLINE: - end = [buffer indexOfLineNumber:[buffer numberOfLines] column:self.preservedColumn]; - break; - case MOTION_HOME: - tmpPos = [xview lineNumberInScrollView:0.0 offset:motion.scount - 1]; - end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; - break; - case MOTION_MIDDLE: - tmpPos = [xview lineNumberInScrollView:0.5 offset:0]; - end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; - break; - case MOTION_BOTTOM: - tmpPos = [xview lineNumberInScrollView:1.0 offset:1 - motion.scount]; - end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; - break; - case MOTION_SEARCH_FORWARD: - end = [ts searchRegexForward:motion.regex from:self.insertionPoint count:motion.count option:motion.option].location; - break; - case MOTION_SEARCH_BACKWARD: - end = [ts searchRegexBackward:motion.regex from:self.insertionPoint count:motion.count option:motion.option].location; - break; - case TEXTOBJECT_WORD: - range = [ts currentWord:begin count:motion.count option:motion.option]; - break; - case TEXTOBJECT_BRACES: - range = xv_current_block([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '{', '}'); - break; - case TEXTOBJECT_PARAGRAPH: - // Not supported - start = [ts moveFromIndex:self.insertionPoint paragraphs:-1 option:MOPT_PARA_BOUND_BLANKLINE]; - end = [ts moveFromIndex:self.insertionPoint paragraphs:motion.scount option:MOPT_PARA_BOUND_BLANKLINE]; - range = NSMakeRange(start, end - start); - break; - case TEXTOBJECT_PARENTHESES: - range = xv_current_block([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '(', ')'); - break; - case TEXTOBJECT_SENTENCE: - // Not supported - break; - case TEXTOBJECT_ANGLEBRACKETS: - range = xv_current_block([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '<', '>'); - break; - case TEXTOBJECT_SQUOTE: - range = xv_current_quote([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '\''); - break; - case TEXTOBJECT_DQUOTE: - range = xv_current_quote([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '\"'); - break; - case TEXTOBJECT_TAG: - // Not supported - break; - case TEXTOBJECT_BACKQUOTE: - range = xv_current_quote([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '`'); - break; - case TEXTOBJECT_SQUAREBRACKETS: - range = xv_current_block([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '[', ']'); - break; - case MOTION_LINE_COLUMN: - end = [buffer indexOfLineNumber:motion.line column:motion.column]; - if( NSNotFound == end ){ - end = current; - } - break; - case MOTION_POSITION: - end = motion.position; - break; - } - - if( range.location != NSNotFound ){// This block is for TEXTOBJECT - begin = range.location; - if( range.length == 0 ){ - end = NSNotFound; - }else{ - end = range.location + range.length - 1; - } - } - XVimRange r = XVimMakeRange(begin, end); - TRACE_LOG(@"range location:%u length:%u", r.begin, r.end); - return r; -} - -- (NSRange)xvim_getOperationRangeFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type { - if( [[self xvim_string] length] == 0 ){ - NSMakeRange(0,0); // No range - } - - if( from > to ){ - NSUInteger tmp = from; - from = to; - to = tmp; - } - // EOF can not be included in operation range. - if( [self.textStorage isEOF:from] ){ - return NSMakeRange(from, 0); // from is EOF but the length is 0 means EOF will not be included in the returned range. - } - - // EOF should not be included. - // If type is exclusive we do not subtract 1 because we do it later below - if( [self.textStorage isEOF:to] && type != CHARACTERWISE_EXCLUSIVE){ - to--; // Note that we already know that "to" is not 0 so not chekcing if its 0. - } - - // At this point "from" and "to" is not EOF - if( type == CHARACTERWISE_EXCLUSIVE ){ - // to will not be included. - to--; - }else if( type == CHARACTERWISE_INCLUSIVE ){ - // Nothing special - }else if( type == LINEWISE ){ - to = [self.textStorage.xvim_buffer endOfLine:to]; - if( [self.textStorage isEOF:to] ){ - to--; - } - NSUInteger head = [self.textStorage.xvim_buffer firstOfLine:from]; - if( NSNotFound != head ){ - from = head; - } - } - - return NSMakeRange(from, to - from + 1); // Inclusive range -} - -- (void)xvim_indentCharacterRange:(NSRange)range{ -#ifdef __USE_DVTKIT__ -#ifdef __XCODE5__ - if ( [self.textStorage isKindOfClass:[DVTTextStorage class]] ){ - [(DVTTextStorage*)self.textStorage indentCharacterRange:range undoManager:self.undoManager]; - } - return; -#else - if ( [self.textStorage isKindOfClass:[DVTSourceTextStorage class]] ){ - [(DVTSourceTextStorage*)self.textStorage indentCharacterRange:range undoManager:self.undoManager]; - } - return; -#endif -#else -#error You must implement here -#endif - - NSAssert(NO, @"You must implement here if you dont use this caregory with DVTSourceTextView"); -} - -#pragma mark scrolling - -- (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward{ - NSRange ret = NSMakeRange(NSNotFound, 0); - if( forward ){ - ret = [self.textStorage searchRegexForward:regex from:self.insertionPoint count:count option:opt]; - }else{ - ret = [self.textStorage searchRegexBackward:regex from:self.insertionPoint count:count option:opt]; - } - return ret; -} - -- (void)xvim_registerIndexForUndo:(NSUInteger)index -{ - XVimUndoOperation *op = [[XVimUndoOperation alloc] initWithIndex:index]; - [op registerForBuffer:self.textStorage.xvim_buffer]; - [op release]; -} - -- (void)xvim_registerInsertionPointForUndo -{ - [self xvim_registerIndexForUndo:self.insertionPoint]; -} - -@end diff --git a/XVim/Test/XVimTestCase.m b/XVim/Test/XVimTestCase.m index 939ef093..6a5502d6 100644 --- a/XVim/Test/XVimTestCase.m +++ b/XVim/Test/XVimTestCase.m @@ -13,7 +13,7 @@ #import "DVTSourceTextView+XVim.h" #import "XVimWindow.h" #import "XVimKeyStroke.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" @implementation XVimTestCase + (XVimTestCase*)testCaseWithInitialText:(NSString*)it @@ -61,11 +61,13 @@ - (void)dealloc{ [super dealloc]; } -- (void)setUp{ +- (void)setUp +{ XVimWindow *window = XVimLastActiveSourceView().xvimWindow; - NSTextView *view = window.currentView.textView; + XVimView *xview = window.currentView; + NSTextView *view = xview.textView; - [view xvim_changeSelectionMode:XVIM_VISUAL_NONE]; + xview.selectionMode = XVIM_VISUAL_NONE; [view setString:self.initialText]; [view setSelectedRange:self.initialRange]; } diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 245da396..7027bfff 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -290,12 +290,15 @@ #pragma mark Support for modifications +@property (nonatomic, readonly) BOOL isEditing; + - (void)undoRedo:(XVimUndoOperation *)op; - (void)beginEditingAtIndex:(NSUInteger)index; - (void)endEditingAtIndex:(NSUInteger)index; - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string; +- (void)replaceCharactersInRange:(NSRange)range withSpaces:(NSUInteger)count; #define XVIM_BUFFER_SWAP_UPPER 0 #define XVIM_BUFFER_SWAP_LOWER 1 diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index b81f4360..44f23fcd 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -9,6 +9,7 @@ #import #import "XVimStringBuffer.h" #import "XVimBuffer.h" +#import "XVimView.h" #import "XVimUndo.h" #import "XVimTextStoring.h" #import "NSString+VimHelper.h" @@ -38,6 +39,7 @@ @implementation XVimBuffer { NSDocument *__unsafe_unretained _document; NSTextStorage *__unsafe_unretained _textStorage; XVimUndoOperation *_curOp; + NSUInteger _editCount; struct { unsigned has_xvim_string : 1; @@ -234,6 +236,7 @@ - (NSRange)indexRangeForLineAtIndex:(NSUInteger)index newLineLength:(NSUInteger - (NSUInteger)indexOfLineNumber:(NSUInteger)num { + NSAssert(num > 0, @"line number start at 1"); if (num == 1) { return 0; } @@ -433,13 +436,18 @@ - (NSUInteger)nextDigitInLine:(NSUInteger)index #pragma mark Support for modifications +- (BOOL)isEditing +{ + return _editCount > 0; +} + - (void)undoRedo:(XVimUndoOperation *)op { for (NSLayoutManager *mgr in _textStorage.layoutManagers) { NSTextView *view = mgr.firstTextView; if (view.textStorage == _textStorage) { - [op undoRedo:self view:view]; + [op undoRedo:self view:view.xvim_view]; return; } } @@ -449,23 +457,25 @@ - (void)undoRedo:(XVimUndoOperation *)op - (void)beginEditingAtIndex:(NSUInteger)index { - NSAssert(!_curOp, @"you can't call -beginEditingAtIndex: twice"); - [_curOp release]; - _curOp = [[XVimUndoOperation alloc] initWithIndex:index]; -} - -- (void)cancelEditing -{ - [_curOp release]; - _curOp = nil; + NSAssert(!_curOp || _editCount, @"invalid undo state"); + if (_curOp) { + _editCount++; + _curOp.startIndex = index; + } else { + _curOp = [[XVimUndoOperation alloc] initWithIndex:index]; + _editCount = 1; + } } - (void)endEditingAtIndex:(NSUInteger)index { _curOp.endIndex = index; - [_curOp registerForBuffer:self]; - [_curOp release]; - _curOp = nil; + + if (--_editCount == 0) { + [_curOp registerForBuffer:self]; + [_curOp release]; + _curOp = nil; + } } - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string @@ -475,6 +485,15 @@ - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string [_textStorage replaceCharactersInRange:range withString:string]; } +- (void)replaceCharactersInRange:(NSRange)range withSpaces:(NSUInteger)count +{ + if (count == 0) { + [self replaceCharactersInRange:range withString:@""]; + } else if (range.length) { + [self replaceCharactersInRange:range withString:[NSString stringMadeOfSpaces:count]]; + } +} + NS_INLINE unichar rot13(unichar c) { switch (c) { @@ -550,9 +569,7 @@ - (void)swapCharactersInRange:(NSRange)range mode:(int)mode [U release]; } - [_textStorage beginEditing]; [self replaceCharactersInRange:range withString:s]; - [_textStorage endEditing]; [s release]; CFRelease(locale); } @@ -602,8 +619,6 @@ - (NSUInteger)shiftLines:(XVimRange)lines column:(NSUInteger)column { NSString *string = self.string; - [_textStorage beginEditing]; - if (right) { NSString *s = [NSString stringMadeOfSpaces:count]; NSUInteger tabWidth = self.tabWidth; @@ -644,8 +659,6 @@ - (NSUInteger)shiftLines:(XVimRange)lines column:(NSUInteger)column pos = [self indexOfLineNumber:lines.begin]; pos = [self firstNonblankInLineAtIndex:pos allowEOL:YES]; } - - [_textStorage endEditing]; return pos; } @@ -720,11 +733,10 @@ - (NSRange)_numberAtIndex:(NSUInteger)index - (NSUInteger)incrementNumberAtIndex:(NSUInteger)index by:(int64_t)offset { NSRange range; - BOOL doOp = _curOp == nil; range = [self _numberAtIndex:index]; if (range.location == NSNotFound) { - NSUInteger pos = [self.textStorage.xvim_buffer nextDigitInLine:index]; + NSUInteger pos = [self nextDigitInLine:index]; if (pos == NSNotFound) { return NSNotFound; } @@ -750,12 +762,10 @@ - (NSUInteger)incrementNumberAtIndex:(NSUInteger)index by:(int64_t)offset repl = [NSString stringWithFormat:@"%lld", i + offset]; } - if (doOp) [self beginEditingAtIndex:index]; - [_textStorage beginEditing]; + [self beginEditingAtIndex:index]; [self replaceCharactersInRange:range withString:repl]; - [_textStorage endEditing]; index = range.location + repl.length - 1; - if (doOp) [self endEditingAtIndex:index]; + [self endEditingAtIndex:index]; return index; } diff --git a/XVim/XVimCommandLineEvaluator.m b/XVim/XVimCommandLineEvaluator.m index 019546d8..30c0df21 100644 --- a/XVim/XVimCommandLineEvaluator.m +++ b/XVim/XVimCommandLineEvaluator.m @@ -8,7 +8,7 @@ #import "Logger.h" #import "XVimCommandLineEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimKeymapProvider.h" #import "XVimWindow.h" #import "XVimCommandField.h" @@ -42,7 +42,7 @@ - (id)initWithWindow:(XVimWindow *)window _onKeyPress = [keyPressHandler copy]; _historyNo = 0; _evalutionResult = nil; - self.lastTextView = self.sourceView; + self.lastTextView = window.currentView.textView; XVimCommandField *commandField = self.window.commandLine.commandField; [commandField setString:_firstLetter]; [commandField moveToEndOfLine:self]; @@ -94,13 +94,13 @@ - (XVimEvaluator*)execute{ - (void)takeFocusFromWindow{ XVimCommandField *commandField = self.window.commandLine.commandField; [commandField setDelegate:self.window]; - [self.sourceView.window makeFirstResponder:commandField]; + [self.lastTextView.window makeFirstResponder:commandField]; } - (void)relinquishFocusToWindow{ XVimCommandField *commandField = self.window.commandLine.commandField; [commandField setDelegate:nil]; - [[self.lastTextView window] makeFirstResponder:self.lastTextView]; + [self.lastTextView.window makeFirstResponder:self.lastTextView]; [commandField setHidden:YES]; } @@ -167,7 +167,7 @@ - (XVimEvaluator*)CR{ } - (XVimEvaluator*)ESC{ - [self.currentView scrollTo:self.sourceView.insertionPoint]; + [self.currentView scrollTo:self.currentView.insertionPoint]; return nil; } diff --git a/XVim/XVimDefs.h b/XVim/XVimDefs.h index e2f2c290..6e7cd142 100644 --- a/XVim/XVimDefs.h +++ b/XVim/XVimDefs.h @@ -28,6 +28,13 @@ typedef NS_ENUM(NSUInteger, XVimInsertionPoint) { XVIM_INSERT_BLOCK_KILL_EOL, }; +typedef NS_ENUM(NSUInteger, XVimSortOptions) { + XVimSortOptionReversed = 1, + XVimSortOptionRemoveDuplicateLines = 1 << 1, + XVimSortOptionNumericSort = 1 << 2, + XVimSortOptionIgnoreCase = 1 << 3 +}; + typedef enum{ TEXT_TYPE_CHARACTERS, TEXT_TYPE_BLOCK, @@ -35,8 +42,8 @@ typedef enum{ } TEXT_TYPE; typedef enum { + CURSOR_MODE_COMMAND, // default must be command CURSOR_MODE_INSERT, - CURSOR_MODE_COMMAND } CURSOR_MODE; typedef enum { @@ -50,12 +57,12 @@ typedef enum { XVIM_MODE_COUNT, // This is the count of modes } XVIM_MODE; -typedef enum { +typedef NS_ENUM(uint8_t, XVimVisualMode) { XVIM_VISUAL_NONE, XVIM_VISUAL_CHARACTER, // for 'v' XVIM_VISUAL_LINE, // for 'V' XVIM_VISUAL_BLOCK, // for 'CTRL-V' -} XVIM_VISUAL_MODE; +}; typedef enum { _XVIM_VISUAL_RIGHT = 1, @@ -86,10 +93,10 @@ typedef struct _XVimSelection { } XVimSelection; typedef struct { - XVIM_VISUAL_MODE mode; - NSUInteger colwant; - XVimPosition start; - XVimPosition end; + XVimVisualMode mode; + NSUInteger colwant; + XVimPosition start; + XVimPosition end; } XVimVisualInfo; #define XVimSelectionEOL (NSIntegerMax - 1) diff --git a/XVim/XVimDeleteEvaluator.m b/XVim/XVimDeleteEvaluator.m index 65a16613..66ec1064 100644 --- a/XVim/XVimDeleteEvaluator.m +++ b/XVim/XVimDeleteEvaluator.m @@ -9,7 +9,7 @@ #import "XVimDeleteEvaluator.h" #import "XVimInsertEvaluator.h" #import "XVimWindow.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimTextObjectEvaluator.h" #import "Logger.h" #import "XVim.h" @@ -70,13 +70,14 @@ -(XVimEvaluator*)motionFixed:(XVimMotion*)motion{ if (_insertModeAtCompletion == TRUE) { // Do not repeat the insert, that is how vim works so for // example 'c3wWord' results in Word not WordWordWord - [[self sourceView] xvim_change:motion]; + [self.currentView doChange:motion]; [self resetNumericArg]; // Do not call [[XVim instance] fixRepeatCommand] here. // It will be called after XVimInsertEvaluator finish handling key input. return [[[XVimInsertEvaluator alloc] initWithWindow:self.window] autorelease]; - }else{ - [[self sourceView] xvim_delete:motion andYank:YES]; + } else { + [self.currentView doDelete:motion andYank:YES]; + [self.currentView adjustCursorPosition]; } return nil; } diff --git a/XVim/XVimEqualEvaluator.m b/XVim/XVimEqualEvaluator.m index d7507cce..ef788766 100644 --- a/XVim/XVimEqualEvaluator.m +++ b/XVim/XVimEqualEvaluator.m @@ -7,7 +7,7 @@ // #import "XVimEqualEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimMotionEvaluator.h" #import "XVimWindow.h" #import "Logger.h" @@ -23,7 +23,7 @@ - (XVimEvaluator*)EQUAL{ } - (XVimEvaluator *)motionFixed:(XVimMotion *)motion{ - [[self sourceView] xvim_filter:motion]; + [self.currentView doFilter:motion]; return nil; } diff --git a/XVim/XVimEvaluator.h b/XVim/XVimEvaluator.h index 83ec6244..f5026f10 100644 --- a/XVim/XVimEvaluator.h +++ b/XVim/XVimEvaluator.h @@ -33,7 +33,7 @@ XVimMotionEvaluator */ #import "XVimRegister.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" @class XVimCommandLineEvaluator; @class XVimMotionEvaluator; @@ -127,7 +127,6 @@ XVimMotionEvaluator - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider; -- (NSTextView *)sourceView; - (XVimView *)currentView; - (void)resetCompletionHandler; diff --git a/XVim/XVimEvaluator.m b/XVim/XVimEvaluator.m index 2ec58256..d8e0e736 100644 --- a/XVim/XVimEvaluator.m +++ b/XVim/XVimEvaluator.m @@ -17,7 +17,7 @@ #import "XVimNormalEvaluator.h" #import "XVimVisualEvaluator.h" #import "XVim.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimSearch.h" #import "XVimCommandLineEvaluator.h" @@ -81,11 +81,6 @@ - (XVimView *)currentView return self.window.currentView; } -- (NSTextView *)sourceView -{ - return self.window.currentView.textView; -} - - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ // This is default implementation of evaluator. // Only keyDown events are supposed to be passed here. @@ -109,15 +104,15 @@ - (XVimEvaluator*)onChildComplete:(XVimEvaluator*)childEvaluator{ } - (void)becameHandler{ - self.sourceView.xvimDelegate = self; + self.currentView.delegate = self; } - (void)cancelHandler{ - self.sourceView.xvimDelegate = nil; + self.currentView.delegate = nil; } - (void)didEndHandler{ - self.sourceView.xvimDelegate = nil; + self.currentView.delegate = nil; } - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { @@ -216,7 +211,10 @@ - (void)textView:(NSTextView*)view didDelete:(NSString*)deletedText withType:(TE return; } -- (XVimCommandLineEvaluator*)searchEvaluatorForward:(BOOL)forward{ +- (XVimCommandLineEvaluator*)searchEvaluatorForward:(BOOL)forward +{ + XVimView *xview = self.currentView; + return [[[XVimCommandLineEvaluator alloc] initWithWindow:self.window firstLetter:forward?@"/":@"?" history:[[XVim instance] searchHistory] @@ -250,9 +248,9 @@ - (XVimCommandLineEvaluator*)searchEvaluatorForward:(BOOL)forward{ BOOL forward = [command characterAtIndex:0] == '/'; XVimMotion* m = [XVim.instance.searcher motionForSearch:[command substringFromIndex:1] forward:forward]; if( [command characterAtIndex:0] == '/' ){ - [self.sourceView xvim_highlightNextSearchCandidateForward:m.regex count:self.numericArg option:m.option]; + [xview xvim_highlightNextSearchCandidateForward:m.regex count:self.numericArg option:m.option]; }else{ - [self.sourceView xvim_highlightNextSearchCandidateBackward:m.regex count:self.numericArg option:m.option]; + [xview xvim_highlightNextSearchCandidateBackward:m.regex count:self.numericArg option:m.option]; } }] autorelease]; } diff --git a/XVim/XVimExCommand.m b/XVim/XVimExCommand.m index d50548d1..d6d9daad 100644 --- a/XVim/XVimExCommand.m +++ b/XVim/XVimExCommand.m @@ -10,7 +10,7 @@ #import "XVimWindow.h" #import "XVim.h" #import "XVimSearch.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "NSString+VimHelper.h" #import "Logger.h" #import "XVimKeyStroke.h" @@ -585,17 +585,15 @@ - (void)dealloc{ // This method correnspons parsing part of get_address in ex_cmds.c - (NSUInteger)getAddress:(unichar*)parsing :(unichar**)cmdLeft inWindow:(XVimWindow*)window { - NSTextView* view = window.currentView.textView; - //DVTFoldingTextStorage* storage = [view textStorage]; - //TRACE_LOG(@"Storage Class:%@", NSStringFromClass([storage class])); - NSUInteger addr = NSNotFound; - NSUInteger begin = view.selectionBegin; - NSUInteger end = view.insertionPoint; - unichar* tmp; - NSUInteger count; - unichar mark; + XVimView *xview = window.currentView; XVimBuffer *buffer = window.currentBuffer; - + NSUInteger addr = NSNotFound; + NSUInteger begin = xview.selectionBegin; + NSUInteger end = xview.insertionPoint; + unichar *tmp; + NSUInteger count; + unichar mark; + // Parse base addr (line number) switch (*parsing) { @@ -732,8 +730,8 @@ - (XVimExArg*)parseCommand:(NSString*)cmd inWindow:(XVimWindow*)window // 3. parse range exarg.lineBegin = NSNotFound; exarg.lineEnd = NSNotFound; - - NSTextView* view = window.currentView.textView; + + XVimView *xview = window.currentView; XVimBuffer *buffer = window.currentBuffer; for(;;){ NSUInteger addr = [self getAddress:parsing :&parsing inWindow:window]; @@ -760,8 +758,8 @@ - (XVimExArg*)parseCommand:(NSString*)cmd inWindow:(XVimWindow*)window if( exarg.lineBegin == NSNotFound ){ // No range expression found. Use current line as range - exarg.lineBegin = [buffer lineNumberAtIndex:view.insertionPoint]; - exarg.lineEnd = exarg.lineBegin; + exarg.lineBegin = xview.insertionLine; + exarg.lineEnd = exarg.lineBegin; } // 4. parse command @@ -818,7 +816,6 @@ - (void)executeCommand:(NSString*)cmd inWindow:(XVimWindow*)window if( exarg.cmd == nil ) { XVimBuffer *buffer = window.currentBuffer; XVimView *xview = window.currentView; - NSTextView *srcView = xview.textView; // Jump to location NSUInteger pos = [buffer indexOfLineNumber:exarg.lineBegin column:0]; @@ -829,8 +826,8 @@ - (void)executeCommand:(NSString*)cmd inWindow:(XVimWindow*)window if( NSNotFound == pos_wo_space ){ pos_wo_space = pos; } - [srcView setSelectedRange:NSMakeRange(pos_wo_space,0)]; - [xview scrollTo:srcView.insertionPoint]; + [xview.textView setSelectedRange:NSMakeRange(pos_wo_space,0)]; + [xview scrollTo:xview.insertionPoint]; return; } @@ -1098,8 +1095,8 @@ - (void)run:(XVimExArg*)args inWindow:(XVimWindow*)window{ - (void)set:(XVimExArg*)args inWindow:(XVimWindow*)window{ NSString* setCommand = [args.arg stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - NSTextView* srcView = window.currentView.textView; XVimOptions* options = [[XVim instance] options]; + XVimView *xview = window.currentView; if( [setCommand rangeOfString:@"="].location != NSNotFound ){ // "set XXX=YYY" form @@ -1118,18 +1115,16 @@ - (void)set:(XVimExArg*)args inWindow:(XVimWindow*)window{ } if( [setCommand isEqualToString:@"wrap"] ){ - [srcView xvim_setWrapsLines:YES]; + [xview xvim_setWrapsLines:YES]; } else if( [setCommand isEqualToString:@"nowrap"] ){ - [srcView xvim_setWrapsLines:NO]; + [xview xvim_setWrapsLines:NO]; } else if( [setCommand isEqualToString:@"list!"] ){ [NSApp sendAction:@selector(toggleInvisibleCharactersShown:) to:nil from:self]; } } - (void)sort:(XVimExArg *)args inWindow:(XVimWindow *)window{ - NSTextView *view = window.currentView.textView; - NSString *cmdString = [[args cmd] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; NSString *argsString = [args arg]; XVimSortOptions options = 0; @@ -1150,8 +1145,9 @@ - (void)sort:(XVimExArg *)args inWindow:(XVimWindow *)window{ options |= XVimSortOptionRemoveDuplicateLines; } } - - [view xvim_sortLinesFrom:args.lineBegin to:args.lineEnd withOptions:options]; + + XVimRange range = XVimMakeRange(args.lineBegin, args.lineEnd); + [window.currentView doSortLines:range withOptions:options]; } - (void)sub:(XVimExArg*)args inWindow:(XVimWindow*)window{ diff --git a/XVim/XVimGActionEvaluator.m b/XVim/XVimGActionEvaluator.m index ace7649f..48f10ae4 100644 --- a/XVim/XVimGActionEvaluator.m +++ b/XVim/XVimGActionEvaluator.m @@ -17,7 +17,7 @@ #import "XVimMarks.h" #import "XVimVisualEvaluator.h" #import "NSTextStorage+VimOperation.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" @implementation XVimGActionEvaluator @@ -39,25 +39,28 @@ - (XVimEvaluator*)f{ } - (XVimEvaluator*)i{ - XVimMark* mark = [[XVim instance].marks markForName:@"^" forDocument:self.window.currentBuffer.document]; + XVimMark *mark = [[XVim instance].marks markForName:@"^" forDocument:self.window.currentBuffer.document]; XVimInsertionPoint mode = XVIM_INSERT_DEFAULT; XVimBuffer *buffer = self.window.currentBuffer; - if ( mark.line != NSNotFound) { + if (mark.line != NSNotFound) { NSUInteger newPos = [buffer indexOfLineNumber:mark.line column:mark.column]; - if( NSNotFound != newPos ){ - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 0); + if (NSNotFound != newPos) { + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 0); + m.position = newPos; // set the position before the jump - XVimMark* cur_mark = [[[XVimMark alloc] init] autorelease]; - cur_mark.line = [self.sourceView insertionLine]; - cur_mark.column = [self.sourceView insertionColumn]; - cur_mark.document = self.window.currentBuffer.document.fileURL.path; - if( nil != mark.document){ + XVimMark *cur_mark = [[[XVimMark alloc] init] autorelease]; + XVimView *xview = self.currentView; + XVimPosition pos = xview.insertionPosition; + cur_mark.line = pos.line; + cur_mark.column = pos.column; + cur_mark.document = buffer.document.fileURL.path; + if (nil != mark.document) { [[XVim instance].marks setMark:cur_mark forName:@"'"]; } - [self.sourceView xvim_move:m]; + [xview moveCursorWithMotion:m]; mode = XVIM_INSERT_APPEND; } } diff --git a/XVim/XVimGMotionEvaluator.m b/XVim/XVimGMotionEvaluator.m index b50337a8..3c918e67 100644 --- a/XVim/XVimGMotionEvaluator.m +++ b/XVim/XVimGMotionEvaluator.m @@ -10,7 +10,6 @@ #import "XVimGMotionEvaluator.h" #import "XVimMotionEvaluator.h" #import "XVimKeyStroke.h" -#import "XVimMotionOption.h" #import "XVimWindow.h" #import "XVim.h" #import "XVimSearch.h" @@ -31,7 +30,7 @@ - (XVimEvaluator*)g{ - (XVimEvaluator*)searchCurrentWord:(BOOL)forward { XVimCommandLineEvaluator* eval = [self searchEvaluatorForward:forward]; - NSRange r = [self.sourceView xvim_currentWord:MOTION_OPTION_NONE]; + NSRange r = [self.currentView xvim_currentWord:MOTION_OPTION_NONE]; if( r.location == NSNotFound ){ return nil; } @@ -40,9 +39,9 @@ - (XVimEvaluator*)searchCurrentWord:(BOOL)forward { // Vim also does this behavior( when matched string is not found ) XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); m.position = r.location; - [self.sourceView xvim_move:m]; + [self.currentView moveCursorWithMotion:m]; - NSString* word = [self.sourceView.string substringWithRange:r]; + NSString* word = [self.currentView.textView.string substringWithRange:r]; NSString* searchWord = [NSRegularExpression escapedPatternForString:word]; [eval appendString:searchWord]; [eval execute]; diff --git a/XVim/XVimGVisualEvaluator.m b/XVim/XVimGVisualEvaluator.m index 378af328..2340f203 100644 --- a/XVim/XVimGVisualEvaluator.m +++ b/XVim/XVimGVisualEvaluator.m @@ -7,7 +7,7 @@ // #import "XVimGVisualEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimWindow.h" #import "XVimJoinEvaluator.h" #import "XVim.h" @@ -55,7 +55,7 @@ - (XVimEvaluator *)q{ - (XVimEvaluator*)u{ [self.argumentString appendString:@"u"]; XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); - [self.sourceView xvim_swapCharacters:m mode:XVIM_BUFFER_SWAP_LOWER]; + [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_LOWER]; [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; } @@ -63,7 +63,7 @@ - (XVimEvaluator*)u{ - (XVimEvaluator*)U{ [self.argumentString appendString:@"U"]; XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); - [self.sourceView xvim_swapCharacters:m mode:XVIM_BUFFER_SWAP_UPPER]; + [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_UPPER]; [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; } @@ -76,7 +76,7 @@ - (XVimEvaluator *)w{ - (XVimEvaluator *)QUESTION{ [self.argumentString appendString:@"?"]; XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); - [self.sourceView xvim_swapCharacters:m mode:XVIM_BUFFER_SWAP_ROT13]; + [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_ROT13]; [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; } @@ -84,7 +84,7 @@ - (XVimEvaluator *)QUESTION{ - (XVimEvaluator*)TILDE{ [self.argumentString appendString:@"~"]; XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); - [self.sourceView xvim_swapCharacters:m mode:XVIM_BUFFER_SWAP_CASE]; + [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_CASE]; [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; } diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index 92541048..420dca2c 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -17,7 +17,8 @@ #import "XVimMark.h" #import "XVimMarks.h" #import "XVimNormalEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" +#import "NSTextStorage+VimOperation.h" @interface XVimInsertEvaluator() @property (nonatomic) NSRange startRange; @@ -92,8 +93,8 @@ - (XVIM_MODE)mode{ - (void)becameHandler{ [super becameHandler]; - [self.sourceView xvim_insert:_mode blockColumn:&_blockEditColumn blockLines:&_blockLines]; - self.startRange = [[self sourceView] selectedRange]; + [self.currentView doInsert:_mode blockColumn:&_blockEditColumn blockLines:&_blockLines]; + self.startRange = self.currentView.textView.selectedRange; } - (CGFloat)insertionPointHeightRatio{ @@ -122,31 +123,30 @@ - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider{ } - (NSString*)insertedText{ - NSTextView* view = [self sourceView]; + XVimView *xview = self.currentView; + XVimBuffer *buffer = self.window.currentBuffer; NSUInteger startLoc = self.startRange.location; - NSUInteger endLoc = [view selectedRange].location; + NSUInteger endLoc = xview.textView.selectedRange.location; NSRange textRange = NSMakeRange(NSNotFound, 0); - if( [[view string] length] == 0 ){ + if (buffer.length == 0 ){ return @""; } // If some text are deleted while editing startLoc could be out of range of the view's string. - if( ( startLoc >= [[view string] length] ) ){ - startLoc = [[view string] length] - 1; + if (startLoc >= buffer.length) { + startLoc = buffer.length - 1; } // Is this really what we want to do? // This means just moving cursor forward or backward and escape from insert mode generates the inserted test this method return. // -> The answer is 'OK'. see onMovementKeyPressed: method how it treats the inserted text. - if (endLoc > startLoc ){ + if (endLoc > startLoc) { textRange = NSMakeRange(startLoc, endLoc - startLoc); }else{ textRange = NSMakeRange(endLoc , startLoc - endLoc); } - NSString *text = [[view string] substringWithRange:textRange]; - return text; - + return [buffer.string substringWithRange:textRange]; } /* @@ -171,12 +171,15 @@ - (void)onMovementKeyPressed{ } // Store off the new start range - self.startRange = [[self sourceView] selectedRange]; + self.startRange = self.currentView.textView.selectedRange; } - (void)didEndHandler{ [super didEndHandler]; - NSTextView *sourceView = [self sourceView]; + + XVimView *xview = self.currentView; + NSTextView *sourceView = xview.textView; + XVimBuffer *buffer = self.window.currentBuffer; if( !_insertedEventsAbort && !_oneCharMode ){ NSString *text = [self insertedText]; @@ -186,40 +189,38 @@ - (void)didEndHandler{ if (_blockEditColumn != NSNotFound) { XVimRange range = XVimMakeRange(_blockLines.begin + 1, _blockLines.end); - [sourceView xvim_blockInsertFixupWithText:text mode:_mode count:self.numericArg - column:_blockEditColumn lines:range]; + [xview doInsertFixupWithText:text mode:_mode count:self.numericArg + column:_blockEditColumn lines:range]; } } // Store off any needed text XVim *xvim = [XVim instance]; [xvim fixOperationCommands]; - if( _oneCharMode ){ - }else if (!self.movementKeyPressed){ + if (_oneCharMode) { + } else if (!self.movementKeyPressed) { //[self recordTextIntoRegister:xvim.recordingRegister]; //[self recordTextIntoRegister:xvim.repeatRegister]; - }else if(self.lastInsertedText.length > 0){ + } else if (self.lastInsertedText.length > 0) { //[xvim.repeatRegister appendText:self.lastInsertedText]; } - [sourceView xvim_hideCompletions]; - + [xview xvim_hideCompletions]; + // Position for "^" is before escaped from insert mode - NSUInteger pos = self.sourceView.insertionPoint; - XVimBuffer *buffer = self.window.currentBuffer; - XVimMark *mark = XVimMakeMark([buffer lineNumberAtIndex:pos], [buffer columnOfIndex:pos], buffer.document); - if( nil != mark.document ){ + XVimPosition pos = xview.insertionPosition; + XVimMark *mark = XVimMakeMark(pos.line, pos.column, buffer.document); + if (nil != mark.document) { [[XVim instance].marks setMark:mark forName:@"^"]; } - - [[self sourceView] xvim_escapeFromInsert]; - + + [xview escapeFromInsert]; + // Position for "." is after escaped from insert mode - pos = self.sourceView.insertionPoint; - mark = XVimMakeMark([buffer lineNumberAtIndex:pos], [buffer columnOfIndex:pos], buffer.document); - if( nil != mark.document ){ + pos = xview.insertionPosition; + mark = XVimMakeMark(pos.line, pos.column, buffer.document); + if (nil != mark.document) { [[XVim instance].marks setMark:mark forName:@"."]; } - } - (BOOL)windowShouldReceive:(SEL)keySelector { @@ -238,7 +239,7 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ self.movementKeyPressed = NO; // Store off the new start range - self.startRange = [[self sourceView] selectedRange]; + self.startRange = self.currentView.textView.selectedRange; } if (nextEvaluator == self && nil == keySelector){ @@ -246,7 +247,7 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ if (_oneCharMode) { if (!keyStroke.isPrintable) { nextEvaluator = [XVimEvaluator invalidEvaluator]; - } else if (![self.sourceView xvim_replaceCharacters:keyStroke.character count:[self numericArg]]) { + } else if (![self.currentView doReplaceCharacters:keyStroke.character count:[self numericArg]]) { nextEvaluator = [XVimEvaluator invalidEvaluator]; }else{ nextEvaluator = nil; @@ -255,10 +256,10 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ // Here we pass the key input to original text view. // The input coming to this method is already handled by "Input Method" // and the input maight be non ascii like 'あ' - if( keyStroke.modifier == 0 && isPrintable(keyStroke.character)){ - [self.sourceView insertText:keyStroke.xvimString]; + if (keyStroke.isPrintable){ + [self.currentView.textView insertText:keyStroke.xvimString]; }else{ - [self.sourceView interpretKeyEvents:[NSArray arrayWithObject:event]]; + [self.currentView.textView interpretKeyEvents:[NSArray arrayWithObject:event]]; } } } @@ -287,23 +288,31 @@ - (XVimEvaluator*)C_c{ return [self ESC]; } -- (void)C_yC_eHelper:(BOOL)handlingC_y { +- (void)C_yC_eHelper:(BOOL)handlingC_y +{ XVimBuffer *buffer = self.window.currentBuffer; - NSUInteger currentCursorIndex = [self.sourceView selectedRange].location; - NSUInteger currentColumnIndex = [buffer columnOfIndex:currentCursorIndex]; - NSUInteger newCharIndex; + XVimView *xview = self.currentView; + + XVimPosition pos = xview.insertionPosition; + NSUInteger indexToCopy; + if (handlingC_y) { - newCharIndex = [self.sourceView.textStorage prevLine:currentCursorIndex column:currentColumnIndex count:[self numericArg] option:MOTION_OPTION_NONE]; + indexToCopy = [buffer indexOfLineNumber:pos.line - 1 column:pos.column]; } else { - newCharIndex = [self.sourceView.textStorage nextLine:currentCursorIndex column:currentColumnIndex count:[self numericArg] option:MOTION_OPTION_NONE]; + indexToCopy = [buffer indexOfLineNumber:pos.line + 1 column:pos.column]; } - NSUInteger newColumnIndex = [buffer columnOfIndex:newCharIndex]; - NSLog(@"Old column: %ld\tNew column: %ld", currentColumnIndex, newColumnIndex); - if (currentColumnIndex == newColumnIndex) { - unichar u = [[[self sourceView] string] characterAtIndex:newCharIndex]; - NSString *charToInsert = [NSString stringWithFormat:@"%c", u]; - [[self sourceView] insertText:charToInsert]; + if (indexToCopy == NSNotFound || [buffer.textStorage isEOL:indexToCopy]) { + return; } + + unichar c = [buffer.string characterAtIndex:indexToCopy]; + NSString *s = [[NSString alloc] initWithCharacters:&c length:1]; + + NSUInteger index = xview.insertionPoint; + [buffer beginEditingAtIndex:index]; + [buffer replaceCharactersInRange:NSMakeRange(index, 0) withString:s]; + [buffer endEditingAtIndex:index + 1]; + [xview moveCursorToIndex:index + 1]; } - (XVimEvaluator*)C_y{ @@ -318,7 +327,7 @@ - (XVimEvaluator*)C_e{ - (XVimEvaluator*)C_w{ XVimMotion* m = XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); - [[self sourceView] xvim_delete:m andYank:NO]; + [self.currentView doDelete:m andYank:NO]; return self; } diff --git a/XVim/XVimJoinEvaluator.m b/XVim/XVimJoinEvaluator.m index 0bbe4e7e..26397eca 100644 --- a/XVim/XVimJoinEvaluator.m +++ b/XVim/XVimJoinEvaluator.m @@ -7,7 +7,7 @@ // #import "XVimJoinEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimWindow.h" @implementation XVimJoinEvaluator { @@ -27,7 +27,7 @@ - (XVimEvaluator*)motionFixed:(XVimMotion*)motion{ // J and 2J is the same motion.count--; } - [self.sourceView xvim_join:motion.count addSpace:_addSpace]; + [self.currentView doJoin:motion.count addSpace:_addSpace]; return nil; } diff --git a/XVim/XVimMarkSetEvaluator.m b/XVim/XVimMarkSetEvaluator.m index b27ffa9f..665794e1 100644 --- a/XVim/XVimMarkSetEvaluator.m +++ b/XVim/XVimMarkSetEvaluator.m @@ -13,7 +13,7 @@ #import "XVimMark.h" #import "XVimMarks.h" #import "XVim.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" @implementation XVimMarkSetEvaluator @@ -22,18 +22,21 @@ - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ + XVimView *xview = self.currentView; XVimBuffer *buffer = self.window.currentBuffer; - NSString* keyStr = [keyStroke toSelectorString]; + + NSString *keyStr = [keyStroke toSelectorString]; if ([keyStr length] != 1) { return [XVimEvaluator invalidEvaluator]; } - XVimMark* mark = [[[XVimMark alloc] init] autorelease]; - NSRange r = [self.sourceView selectedRange]; - mark.line = [buffer lineNumberAtIndex:r.location]; - mark.column = [buffer columnOfIndex:r.location]; + XVimMark *mark = [[[XVimMark alloc] init] autorelease]; + XVimPosition pos = xview.insertionPosition; + + mark.line = pos.line; + mark.column = pos.column; mark.document = buffer.document.fileURL.path; - if( nil != mark.document ){ + if (nil != mark.document) { [[XVim instance].marks setMark:mark forName:keyStr]; } return nil; diff --git a/XVim/XVimMotion.h b/XVim/XVimMotion.h index 4e88240a..a3bd7c29 100644 --- a/XVim/XVimMotion.h +++ b/XVim/XVimMotion.h @@ -8,7 +8,6 @@ #import #import "XVimMotionType.h" -#import "XVimMotionOption.h" typedef struct { BOOL reachedEndOfLine; @@ -20,6 +19,19 @@ typedef struct { #define XVIM_MAKE_MOTION(MOTION,TYPE,OPTION,COUNT) [[[XVimMotion alloc] initWithMotion:MOTION type:TYPE option:OPTION count:COUNT] autorelease] +typedef enum { + MOTION_OPTION_NONE = 0x00, + LEFT_RIGHT_WRAP = 0x01, + LEFT_RIGHT_NOWRAP = 0x02, + BIGWORD = 0x04, // for 'WORD' motion + INCLUSIVE = 0x08, + MOPT_PARA_BOUND_BLANKLINE = 0x10, + TEXTOBJECT_INNER = 0x20, + SEARCH_WRAP= 0x40, + SEARCH_CASEINSENSITIVE = 0x80, + MOTION_OPTION_CHANGE_WORD = 0x100, // for 'cw','cW' +} MOTION_OPTION; + typedef enum _MOTION{ MOTION_NONE, MOTION_FORWARD, // l diff --git a/XVim/XVimMotionEvaluator.h b/XVim/XVimMotionEvaluator.h index d610b2d8..a28c23f6 100644 --- a/XVim/XVimMotionEvaluator.h +++ b/XVim/XVimMotionEvaluator.h @@ -17,8 +17,6 @@ @interface XVimMotionEvaluator : XVimNumericEvaluator -- (XVimEvaluator*)commonMotion:(SEL)motion Type:(MOTION_TYPE)type; - /** * The difference between motionFixed and _motionFixed: * _motionFixed is called internaly from its inherited classes. diff --git a/XVim/XVimMotionEvaluator.m b/XVim/XVimMotionEvaluator.m index cd2c93c5..d1d832b9 100644 --- a/XVim/XVimMotionEvaluator.m +++ b/XVim/XVimMotionEvaluator.m @@ -55,34 +55,6 @@ - (void)dealloc{ [super dealloc]; } -// This is helper method commonly used by many key event handlers. -// You do not need to use this if this is not proper to express the motion. -- (XVimEvaluator*)commonMotion:(SEL)motion Type:(MOTION_TYPE)type{ - NSTextView* view = [self sourceView]; - NSUInteger motionTo = (NSUInteger)[view performSelector:motion withObject:[NSNumber numberWithUnsignedInteger:[self numericArg]]]; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, type, MOTION_OPTION_NONE, [self numericArg]); - m.position = motionTo; - return [self _motionFixed:m]; -} - -/* -- (XVimEvaluator*)_motionFixedFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type{ - TRACE_LOG(@"from:%d to:%d type:%d", from, to, type); - if( _forcedMotionType != CHARACTERWISE_EXCLUSIVE){ - if ( type == LINEWISE) { - type = CHARACTERWISE_EXCLUSIVE; - } else if ( type == CHARACTERWISE_EXCLUSIVE ){ - type = CHARACTERWISE_INCLUSIVE; - } else if(type == CHARACTERWISE_INCLUSIVE) { - type = CHARACTERWISE_EXCLUSIVE; - } - } - - XVimEvaluator *ret = [self motionFixedFrom:from To:to Type:type]; - return ret; -} - */ - -(XVimEvaluator*)_motionFixed:(XVimMotion*)motion{ if( _forcedMotionType == CHARACTERWISE_EXCLUSIVE){ // CHARACTERWISE_EXCLUSIVE means 'v' is pressed and it means toggle inclusive/exclusive. @@ -315,7 +287,7 @@ - (XVimEvaluator*)NUM0{ - (XVimEvaluator*)searchCurrentWordForward:(BOOL)forward { XVimCommandLineEvaluator* eval = [self searchEvaluatorForward:forward]; - NSRange r = [self.sourceView xvim_currentWord:MOTION_OPTION_NONE]; + NSRange r = [self.currentView xvim_currentWord:MOTION_OPTION_NONE]; if( r.location == NSNotFound ){ return nil; } @@ -323,9 +295,9 @@ - (XVimEvaluator*)searchCurrentWordForward:(BOOL)forward { // Vim also does this behavior( when matched string is not found ) XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); m.position = r.location; - [self.sourceView xvim_move:m]; + [self.currentView moveCursorWithMotion:m]; - NSString* word = [self.sourceView.string substringWithRange:r]; + NSString* word = [self.currentView.buffer.string substringWithRange:r]; NSString* searchWord = [NSRegularExpression escapedPatternForString:word]; searchWord = [NSString stringWithFormat:@"%@%@%@", @"\\b", searchWord, @"\\b"]; [eval appendString:searchWord]; @@ -345,7 +317,7 @@ - (XVimEvaluator*)NUMBER{ // TODO: rename firstOfLine -> firstNonblankOfLine - (XVimEvaluator*)jumpToMark:(XVimMark*)mark firstOfLine:(BOOL)fol{ XVimBuffer *buffer = self.window.currentBuffer; - NSUInteger cur_pos = self.sourceView.insertionPoint; + NSUInteger cur_pos = self.currentView.insertionPoint; MOTION_TYPE motionType = fol?LINEWISE:CHARACTERWISE_EXCLUSIVE; if( mark.line == NSNotFound ){ @@ -430,18 +402,19 @@ - (XVimEvaluator*)DOLLAR{ // Underscore ( "_") moves the cursor to the start of the line (past leading whitespace) // Note: underscore without any numeric arguments behaves like caret but with a numeric argument greater than 1 // it will moves to start of the numeric argument - 1 lines down. -- (XVimEvaluator*)UNDERSCORE{ +- (XVimEvaluator*)UNDERSCORE +{ // TODO add this motion interface to NSTextView - NSTextView *view = self.sourceView; - XVimBuffer *buffer = self.window.currentBuffer; - NSRange r = [view selectedRange]; + XVimView *xview = self.currentView; + XVimBuffer *buffer = xview.buffer; + NSUInteger pos = xview.insertionPoint; NSUInteger repeat = self.numericArg; - NSUInteger linesUpCursorloc = [view.textStorage nextLine:r.location column:0 count:(repeat - 1) option:MOTION_OPTION_NONE]; + NSUInteger linesUpCursorloc = [buffer.textStorage nextLine:pos column:0 count:(repeat - 1) option:MOTION_OPTION_NONE]; NSUInteger head = [buffer firstNonblankInLineAtIndex:linesUpCursorloc allowEOL:NO]; - if( NSNotFound == head && linesUpCursorloc != NSNotFound){ + if (NSNotFound == head && linesUpCursorloc != NSNotFound) { head = linesUpCursorloc; }else if(NSNotFound == head){ - head = r.location; + head = pos; } XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 0); m.position = head; diff --git a/XVim/XVimMotionOption.h b/XVim/XVimMotionOption.h deleted file mode 100644 index fbe4e50c..00000000 --- a/XVim/XVimMotionOption.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// XVimMotionOption.h -// XVim -// -// Created by Tomas Lundell on 10/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -typedef enum{ - MOTION_OPTION_NONE = 0x00, - LEFT_RIGHT_WRAP = 0x01, - LEFT_RIGHT_NOWRAP = 0x02, - BIGWORD = 0x04, // for 'WORD' motion - INCLUSIVE = 0x08, - MOPT_PARA_BOUND_BLANKLINE = 0x10, - TEXTOBJECT_INNER = 0x20, - SEARCH_WRAP= 0x40, - SEARCH_CASEINSENSITIVE = 0x80, - MOTION_OPTION_CHANGE_WORD = 0x100, // for 'cw','cW' -} MOTION_OPTION; diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index 34b2641c..a06b177c 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -33,7 +33,7 @@ #import "XVimMark.h" #import "XVimMarks.h" #import "XVimMotion.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimJoinEvaluator.h" @interface XVimNormalEvaluator() { @@ -45,7 +45,7 @@ @implementation XVimNormalEvaluator - (void)becameHandler{ [super becameHandler]; - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_NONE]; + self.currentView.selectionMode = XVIM_VISUAL_NONE; } - (NSString*)modeString { @@ -75,8 +75,7 @@ - (XVimEvaluator*)A{ } - (XVimEvaluator*)C_a{ - NSTextView* view = [self sourceView]; - if ([view xvim_incrementNumber:(int64_t)self.numericArg]) { + if ([self.currentView doIncrementNumber:(int64_t)self.numericArg]) { [[XVim instance] fixOperationCommands]; } else { [[XVim instance] cancelOperationCommands]; @@ -137,19 +136,18 @@ - (XVimEvaluator*)C_f{ - (XVimEvaluator*)C_g { // process - XVimWindow *window = self.window; - XVimBuffer *buffer = window.currentBuffer; - NSRange range = self.sourceView.selectedRange; + XVimWindow *window = self.window; + XVimBuffer *buffer = window.currentBuffer; + XVimView *xview = self.currentView; + XVimPosition pos = xview.insertionPosition; NSUInteger numberOfLines = [buffer numberOfLines]; - NSUInteger lineNumber = [buffer lineNumberAtIndex:range.location]; - NSUInteger columnNumber = [buffer columnOfIndex:range.location]; NSURL *documentURL = buffer.document.fileURL; if ([documentURL isFileURL]) { NSString *text = [NSString stringWithFormat:@"%@ line %ld of %ld --%d%%-- col %ld", - documentURL.path, lineNumber, numberOfLines, - (int)((CGFloat)lineNumber*100.0/(CGFloat)numberOfLines), columnNumber+1 ]; + documentURL.path, pos.line, numberOfLines, + (int)((CGFloat)pos.line*100.0/(CGFloat)numberOfLines), pos.column + 1 ]; [window statusMessage:text]; } @@ -197,14 +195,12 @@ - (XVimEvaluator*)m{ } - (XVimEvaluator*)o{ - NSTextView* view = [self sourceView]; - [view xvim_insertNewlineBelowAndInsertWithIndent]; + [self.currentView insertNewlineBelowAndInsertWithIndent]; return [[[XVimInsertEvaluator alloc] initWithWindow:self.window] autorelease]; } - (XVimEvaluator*)O{ - NSTextView* view = [self sourceView]; - [view xvim_insertNewlineAboveAndInsertWithIndent]; + [self.currentView insertNewlineAboveAndInsertWithIndent]; return [[[XVimInsertEvaluator alloc] initWithWindow:self.window] autorelease]; } @@ -219,17 +215,15 @@ - (XVimEvaluator*)C_i{ } - (XVimEvaluator*)p{ - NSTextView* view = [self sourceView]; XVimRegister* reg = [[[XVim instance] registerManager] registerByName:self.yankRegister]; - [view xvim_put:reg.string withType:reg.type afterCursor:YES count:[self numericArg]]; + [self.currentView doPut:reg.string withType:reg.type afterCursor:YES count:[self numericArg]]; [[XVim instance] fixOperationCommands]; return nil; } - (XVimEvaluator*)P{ - NSTextView* view = [self sourceView]; XVimRegister* reg = [[[XVim instance] registerManager] registerByName:self.yankRegister]; - [view xvim_put:reg.string withType:reg.type afterCursor:NO count:[self numericArg]]; + [self.currentView doPut:reg.string withType:reg.type afterCursor:NO count:[self numericArg]]; [[XVim instance] fixOperationCommands]; return nil; } @@ -257,10 +251,12 @@ - (XVimEvaluator*)onComplete_Recording:childEvaluator{ return nil; } -- (XVimEvaluator*)C_r{ - NSTextView* view = [self sourceView]; - for( NSUInteger i = 0 ; i < [self numericArg] ; i++){ - [view.undoManager redo]; +- (XVimEvaluator*)C_r +{ + XVimBuffer *buffer = self.window.currentBuffer; + + for (NSUInteger i = 0; i < [self numericArg]; i++) { + [buffer.undoManager redo]; } return nil; } @@ -284,10 +280,12 @@ - (XVimEvaluator*)S{ return [d performSelector:@selector(c)]; } -- (XVimEvaluator*)u{ - NSTextView* view = [self sourceView]; - for( NSUInteger i = 0 ; i < [self numericArg] ; i++){ - [view.undoManager undo]; +- (XVimEvaluator*)u +{ + XVimBuffer *buffer = self.window.currentBuffer; + + for (NSUInteger i = 0; i < [self numericArg]; i++) { + [buffer.undoManager undo]; } return nil; } @@ -342,9 +340,7 @@ - (XVimEvaluator*)X{ } - (XVimEvaluator*)C_x{ - NSTextView* view = [self sourceView]; - - if ([view xvim_incrementNumber:-(int64_t)self.numericArg]) { + if ([self.currentView doIncrementNumber:-(int64_t)self.numericArg]) { [[XVim instance] fixOperationCommands]; } else { [[XVim instance] cancelOperationCommands]; @@ -437,7 +433,7 @@ - (XVimEvaluator*)C_RSQUAREBRACKET{ } - (XVimEvaluator*)HT{ - [[self sourceView] xvim_selectNextPlaceholder]; + [self.currentView selectNextPlaceholder]; return nil; } @@ -491,7 +487,8 @@ - (XVimEvaluator*)DOT{ - (XVimEvaluator*)TILDE{ [self.argumentString appendString:@"~"]; XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg); - [self.sourceView xvim_swapCharacters:m mode:XVIM_BUFFER_SWAP_CASE]; + [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_CASE]; + [XVim.instance fixOperationCommands]; return nil; } @@ -508,7 +505,7 @@ -(XVimEvaluator*)Pagedown{ } - (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ - [[self sourceView] xvim_move:motion]; + [self.currentView moveCursorWithMotion:motion]; return nil; } diff --git a/XVim/XVimOperatorEvaluator.m b/XVim/XVimOperatorEvaluator.m index ce80cb13..ea4f6f23 100644 --- a/XVim/XVimOperatorEvaluator.m +++ b/XVim/XVimOperatorEvaluator.m @@ -15,7 +15,7 @@ #import "XVimKeymapProvider.h" #import "XVimMark.h" #import "XVimMarks.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimYankEvaluator.h" #import "XVimShiftEvaluator.h" #import "XVimJoinEvaluator.h" @@ -69,22 +69,25 @@ - (XVimEvaluator*)_motionFixed:(XVimMotion *)motion{ // This happens for a command like "cw..." if( nil == evaluator ){ XVimBuffer *buffer = self.window.currentBuffer; + XVimView *xview = self.currentView; Class aClass = self.class; if (aClass != [XVimYankEvaluator class]) { [[XVim instance] fixOperationCommands]; - XVimMark* mark = nil; + XVimMark *mark = nil; + if (aClass == [XVimJoinEvaluator class]) { // This is specical case for join operation. // The mark is set at the head of next line of the insertion point after the operation - mark = XVimMakeMark([self.sourceView insertionLine]+1, 0, buffer.document); + mark = XVimMakeMark(xview.insertionLine + 1, 0, buffer.document); } else if (aClass == [XVimShiftEvaluator class]) { - mark = XVimMakeMark([self.sourceView insertionLine], 0, buffer.document); + mark = XVimMakeMark(xview.insertionLine, 0, buffer.document); } else { - mark = XVimMakeMark([self.sourceView insertionLine], [self.sourceView insertionColumn], buffer.document); + XVimPosition pos = xview.insertionPosition; + mark = XVimMakeMark(pos.line, pos.column, buffer.document); } - if( nil != mark.document){ + if (nil != mark.document) { [[XVim instance].marks setMark:mark forName:@"."]; } } diff --git a/XVim/XVimSearch.m b/XVim/XVimSearch.m index 0058a2fb..a70707dd 100644 --- a/XVim/XVimSearch.m +++ b/XVim/XVimSearch.m @@ -7,13 +7,14 @@ // #import "XVimSearch.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "NSString+VimHelper.h" #import "XVimWindow.h" #import "XVim.h" #import "XVimOptions.h" #import "Logger.h" #import "XVimUtil.h" +#import "NSTextStorage+VimOperation.h" @implementation XVimSearch @@ -314,7 +315,7 @@ - (NSRange)searchCurrentWordFrom:(NSUInteger)from forward:(BOOL)forward matchWho NSUInteger firstNonblank = NSNotFound; // TODO: must be moved to NSTextStorage+VimOperation - for (NSUInteger i = begin.location; ![view.textStorage isEOF:i]; ++i) + for (NSUInteger i = begin.location; i < string.length; ++i) { unichar curChar = [string characterAtIndex:i]; if (isNewline(curChar)){ diff --git a/XVim/XVimShiftEvaluator.m b/XVim/XVimShiftEvaluator.m index 86eb1f5b..90380e46 100644 --- a/XVim/XVimShiftEvaluator.m +++ b/XVim/XVimShiftEvaluator.m @@ -7,7 +7,7 @@ // #import "XVimShiftEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimWindow.h" #import "XVim.h" @@ -48,12 +48,9 @@ - (XVimEvaluator*)LESSTHAN{ return nil; } -- (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ - if( _unshift ){ - [[self sourceView] xvim_shiftLeft:motion]; - }else{ - [[self sourceView] xvim_shiftRight:motion]; - } +- (XVimEvaluator*)motionFixed:(XVimMotion *)motion +{ + [self.currentView doShift:motion right:!_unshift]; return nil; } @end diff --git a/XVim/XVimSwapCharsEvaluator.m b/XVim/XVimSwapCharsEvaluator.m index 21953a70..0ab3aad2 100644 --- a/XVim/XVimSwapCharsEvaluator.m +++ b/XVim/XVimSwapCharsEvaluator.m @@ -57,7 +57,7 @@ - (XVimEvaluator *)TILDE - (XVimEvaluator *)motionFixed:(XVimMotion*)motion { - [self.sourceView xvim_swapCharacters:motion mode:_mode]; + [self.currentView doSwapCharacters:motion mode:_mode]; return nil; } diff --git a/XVim/XVimTextObjectEvaluator.m b/XVim/XVimTextObjectEvaluator.m index 12661722..22ffde9e 100644 --- a/XVim/XVimTextObjectEvaluator.m +++ b/XVim/XVimTextObjectEvaluator.m @@ -8,7 +8,6 @@ #import "XVimWindow.h" #import "XVimKeyStroke.h" #import "XVimKeymapProvider.h" -#import "XVimMotionOption.h" @interface XVimTextObjectEvaluator() { BOOL _inner; diff --git a/XVim/XVimUndo.h b/XVim/XVimUndo.h index e3d553f9..8c409179 100644 --- a/XVim/XVimUndo.h +++ b/XVim/XVimUndo.h @@ -9,17 +9,19 @@ #import @class XVimBuffer; +@class XVimView; @interface XVimUndoOperation : NSObject - (instancetype)initWithIndex:(NSUInteger)index; -- (void)undoRedo:(XVimBuffer *)buffer view:(NSTextView *)view; +- (void)undoRedo:(XVimBuffer *)buffer view:(XVimView *)view; - (void)addUndoRange:(NSRange)range replacementRange:(NSRange)replacementRange buffer:(XVimBuffer *)buffer; +- (void)setStartIndex:(NSUInteger)index; - (void)setEndIndex:(NSUInteger)index; - (void)registerForBuffer:(XVimBuffer *)buffer; diff --git a/XVim/XVimUndo.m b/XVim/XVimUndo.m index 858678cd..112218bc 100644 --- a/XVim/XVimUndo.m +++ b/XVim/XVimUndo.m @@ -7,7 +7,7 @@ // #import "XVimUndo.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimBuffer.h" @interface NSTextView (NSPrivate) @@ -55,6 +55,13 @@ - (void)dealloc [super dealloc]; } +- (void)setStartIndex:(NSUInteger)index +{ + if (_startIndex == NSNotFound) { + _startIndex = index; + } +} + - (void)setEndIndex:(NSUInteger)index { _endIndex = index; @@ -82,10 +89,11 @@ - (void)_undoOp:(NSUInteger)i textStorage:(NSTextStorage *)ts range:(NSRange)ran [_values replaceObjectAtIndex:index withObject:newText]; } -- (void)undoRedo:(XVimBuffer *)buffer view:(NSTextView *)view +- (void)undoRedo:(XVimBuffer *)buffer view:(XVimView *)xview { - NSTextStorage *ts = buffer.textStorage; - NSUInteger count = _values.count / 3; + NSTextStorage *ts = buffer.textStorage; + NSTextView *view = xview.textView; + NSUInteger count = _values.count / 3; [view _setUndoRedoInProgress:YES]; if (!view || [view shouldChangeTextInRange:NSMakeRange(NSNotFound, 0) replacementString:@""]) { @@ -104,7 +112,7 @@ - (void)undoRedo:(XVimBuffer *)buffer view:(NSTextView *)view } NSUInteger index = [_undoManager isUndoing] ? _startIndex : _endIndex; if (index != NSNotFound) { - [view xvim_moveToIndex:index]; + [xview moveCursorToIndex:index]; } [view _setUndoRedoInProgress:NO]; } diff --git a/XVim/XVimView.h b/XVim/XVimView.h index bdfb1733..5d67ec34 100644 --- a/XVim/XVimView.h +++ b/XVim/XVimView.h @@ -8,6 +8,7 @@ #import #import "XVimDefs.h" +#import "XVimMotion.h" @class XVimView, XVimBuffer, XVimWindow; @@ -30,9 +31,65 @@ @interface XVimView : NSObject @property (readonly, nonatomic) XVimWindow *window; @property (readonly, nonatomic) NSTextView *textView; +@property (readonly, nonatomic) XVimBuffer *buffer; +@property (strong, nonatomic) id delegate; - (instancetype)initWithView:(NSTextView *)view window:(XVimWindow *)window; +#pragma mark *** Properties *** + +@property (readonly, nonatomic) NSUInteger insertionPoint; +@property (readonly, nonatomic) XVimPosition insertionPosition; +@property (readonly, nonatomic) NSUInteger insertionColumn; +@property (readonly, nonatomic) NSUInteger insertionLine; + +@property (readonly, nonatomic) NSUInteger selectionBegin; +@property (readonly, nonatomic) XVimPosition selectionPosition; +@property (readonly, nonatomic) NSUInteger selectionColumn; +@property (readonly, nonatomic) NSUInteger selectionLine; + +@property (nonatomic) XVimVisualMode selectionMode; +@property (readonly, nonatomic) BOOL inVisualMode; /* != VISUAL_NONE */ +@property (readonly, nonatomic) BOOL inBlockMode; /* == VISUAL_BLOCK */ + +@property (nonatomic, readonly) BOOL needsUpdateFoundRanges; +@property (nonatomic, readonly) NSArray *foundRanges; + +#pragma mark *** Visual Mode and Cursor Position *** + +- (void)escapeFromInsert; + +- (void)selectSwapCorners:(BOOL)onSameLine; + +- (void)saveVisualInfoForBuffer:(XVimBuffer *)buffer; + +- (void)selectNextPlaceholder; +- (void)selectPreviousPlaceholder; +- (void)adjustCursorPosition; + +- (void)moveCursorToIndex:(NSUInteger)index; +- (void)moveCursorToPosition:(XVimPosition)index; +- (void)moveCursorWithMotion:(XVimMotion *)motion; + +#pragma mark *** Operations *** + +- (void)doDelete:(XVimMotion *)motion andYank:(BOOL)yank; +- (void)doChange:(XVimMotion *)motion; +- (void)doYank:(XVimMotion*)motion; +- (void)doPut:(NSString *)text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)count; +- (void)doSwapCharacters:(XVimMotion *)motion mode:(int)mode; +- (BOOL)doReplaceCharacters:(unichar)c count:(NSUInteger)count; +- (void)doJoin:(NSUInteger)count addSpace:(BOOL)addSpace; +- (void)doFilter:(XVimMotion *)motion; +- (void)doShift:(XVimMotion *)motion right:(BOOL)right; +- (void)insertNewlineAboveAndInsertWithIndent; +- (void)insertNewlineBelowAndInsertWithIndent; +- (void)doInsert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column blockLines:(XVimRange *)lines; +- (BOOL)doIncrementNumber:(int64_t)offset; +- (void)doInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint)mode + count:(NSUInteger)count column:(NSUInteger)column lines:(XVimRange)lines; +- (void)doSortLines:(XVimRange)range withOptions:(XVimSortOptions)options; + #pragma mark *** Drawing *** - (NSUInteger)lineNumberInScrollView:(CGFloat)ratio offset:(NSInteger)offset; @@ -55,4 +112,16 @@ - (void)scrollLineForward:(NSUInteger)count; - (void)scrollLineBackward:(NSUInteger)count; +#pragma mark *** Crap to sort *** + +- (void)xvim_setWrapsLines:(BOOL)wraps; +- (void)xvim_hideCompletions; +- (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)count + option:(MOTION_OPTION)opt forward:(BOOL)forward; +- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt; +- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt; +- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt; +- (void)xvim_clearHighlightText; +- (NSRange)xvim_currentWord:(MOTION_OPTION)opt; + @end diff --git a/XVim/XVimView.m b/XVim/XVimView.m index 9f873f5a..84663f7b 100644 --- a/XVim/XVimView.m +++ b/XVim/XVimView.m @@ -6,37 +6,37 @@ // // +// FIXME: layering issue +#if XVIM_XCODE_VERSION == 5 +#define __XCODE5__ +#endif +#define __USE_DVTKIT__ +#import "DVTKit.h" +#import "XVimUndo.h" +#import "NSTextStorage+VimOperation.h" +// END FIXME + #import -#import "XVim.h" -#import "XVimView.h" -#import "XVimWindow.h" #import "Logger.h" #import "NSObject+XVimAdditions.h" -#import "NSTextView+VimOperation.h" +#import "NSString+VimHelper.h" +#import "Utils.h" +#import "XVim.h" +#import "XVimMotion.h" #import "XVimOptions.h" #import "XVimSearch.h" -#import "Utils.h" +#import "XVimView.h" +#import "XVimWindow.h" + @interface XVimView () +@property (nonatomic, readwrite) BOOL needsUpdateFoundRanges; - (NSRect)_glyphRectAtIndex:(NSUInteger)index length:(NSUInteger)length; +- (void)_syncStateFromView; @end -// disgusting copy, get rid of it eventually -@interface NSTextView(VimOperationPrivate) -@property BOOL xvim_lockSyncStateFromView; -- (void)xvim_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve; -- (void)xvim_syncState; // update self's properties with our variables -- (NSArray*)xvim_selectedRanges; -- (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion; -- (NSRange)xvim_getOperationRangeFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type; -- (void)xvim_indentCharacterRange:(NSRange)range; -- (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward; -- (void)xvim_swapCaseForRange:(NSRange)range; -- (void)xvim_registerInsertionPointForUndo; -- (void)xvim_registerIndexForUndo:(NSUInteger)index; -@end static char const * const XVIM_KEY_VIEW = "xvim_view"; @implementation NSTextView (XVimView) @@ -111,7 +111,7 @@ - (void)xvim_setSelectedRanges:(NSArray *)ranges { [self xvim_setSelectedRanges:ranges affinity:affinity stillSelecting:flag]; if (self.xvim_view && !XVim.instance.disabled) { - [self xvim_syncStateFromView]; + [self.xvim_view _syncStateFromView]; } } @@ -166,14 +166,15 @@ - (void)xvim_mouseDown:(NSEvent *)theEvent { XVimWindow *window = self.xvim_view.window; + [self xvim_mouseDown:theEvent]; + if (!window || XVim.instance.disabled) { - return [self xvim_mouseDown:theEvent]; + return; } @try { TRACE_LOG(@"Event:%@", theEvent.description); - // When mouse down, NSTextView ( base in this case) takes the control of event loop internally // and the method call above does not return immidiately and block until mouse up. mouseDragged: method is called from inside it but // it never calls mouseUp: event. After mouseUp event is handled internally it returns the control. @@ -184,7 +185,6 @@ - (void)xvim_mouseDown:(NSEvent *)theEvent // to handleKeyStroke as a special key stroke // and the key stroke should be handled by the current evaluator. - [self xvim_mouseDown:theEvent]; [window syncEvaluatorStack]; } @catch (NSException* exception) { @@ -195,7 +195,9 @@ - (void)xvim_mouseDown:(NSEvent *)theEvent - (void)xvim_drawRect:(NSRect)dirtyRect { - if (XVim.instance.disabled || !self.xvim_view) { + XVimView *xview = self.xvim_view; + + if (XVim.instance.disabled || !xview) { return [self xvim_drawRect:dirtyRect]; } @@ -204,19 +206,19 @@ - (void)xvim_drawRect:(NSRect)dirtyRect XVimMotion *lastSearch = [XVim.instance.searcher motionForRepeatSearch]; if (nil != lastSearch.regex) { - [self xvim_updateFoundRanges:lastSearch.regex withOption:lastSearch.option]; + [xview xvim_updateFoundRanges:lastSearch.regex withOption:lastSearch.option]; } } else { - [self xvim_clearHighlightText]; + [xview xvim_clearHighlightText]; } [self xvim_drawRect:dirtyRect]; - if (self.selectionMode != XVIM_VISUAL_NONE) { + if (xview.inVisualMode) { // NSTextView does not draw insertion point when selecting text. // We have to draw insertion point by ourselves. - NSRect glyphRect = [self.xvim_view _glyphRectAtIndex:self.insertionPoint length:1]; - [[[self insertionPointColor] colorWithAlphaComponent:0.5] set]; + NSRect glyphRect = [xview _glyphRectAtIndex:xview.insertionPoint length:1]; + [[self.insertionPointColor colorWithAlphaComponent:0.5] set]; NSRectFillUsingOperation(glyphRect, NSCompositeSourceOver); } } @@ -263,7 +265,7 @@ - (void)xvim_drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color turned - (void)xvim_didChangeText { if (self.xvim_view && !XVim.instance.disabled) { - [self setNeedsUpdateFoundRanges:YES]; + self.xvim_view.needsUpdateFoundRanges = YES; } [self xvim_didChangeText]; } @@ -289,7 +291,7 @@ - (void)xvim_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([@[ @"ignorecase", @"hlsearch", @"lastSearchString"] containsObject:keyPath]) { - [self setNeedsUpdateFoundRanges:YES]; + self.xvim_view.needsUpdateFoundRanges = YES; [self setNeedsDisplayInRect:self.visibleRect avoidAdditionalLayout:YES]; } } @@ -297,10 +299,27 @@ - (void)xvim_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object @end @implementation XVimView { - NSTextView *__unsafe_unretained _textView; + NSTextView *__unsafe_unretained _textView; + + NSUInteger _preservedColumn; + BOOL _syncStateLock; + CURSOR_MODE _cursorMode; + + NSString *_lastYankedText; + TEXT_TYPE _lastYankedType; + + NSMutableArray *_foundRanges; } @synthesize window = _window; @synthesize textView = _textView; +@synthesize delegate = _delegate; + +@synthesize insertionPoint = _insertionPoint; +@synthesize selectionBegin = _selectionBegin; +@synthesize selectionMode = _selectionMode; + +@synthesize needsUpdateFoundRanges = _needsUpdateFoundRanges; +@synthesize foundRanges = _foundRanges; + (void)initialize { @@ -330,6 +349,8 @@ - (void)dealloc { DEBUG_LOG("View %p deleted", self); [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_foundRanges release]; + [_lastYankedText release]; [_window release]; [super dealloc]; } @@ -337,256 +358,1903 @@ - (void)dealloc - (void)_xvim_statusChanged:(id)sender { if (!XVim.instance.disabled) { - [_textView xvim_syncStateFromView]; + [self _syncStateFromView]; } [_textView setNeedsDisplay:YES]; } -#pragma mark *** Drawing *** - -- (NSRect)_glyphRectAtIndex:(NSUInteger)index length:(NSUInteger)length +- (void)_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve { - if (length && index + length >= _textView.textStorage.length) { - // When the index is EOF the range to specify here can not be grater than 0. - // If it is greater than 0 it returns (0,0) as a glyph rect. - length = 0; + XVimBuffer *buffer = self.buffer; + NSUInteger length = buffer.length; + + // This method only update the internal state(like _insertionPoint) + if (pos > length) { + ERROR_LOG(@"Position specified exceeds the length of the text"); + pos = length; } - return [_textView.layoutManager boundingRectForGlyphRange:NSMakeRange(index, length) - inTextContainer:_textView.textContainer]; -} -- (NSUInteger)_glyphHeightAtIndex:(NSUInteger)index -{ - return NSHeight([self _glyphRectAtIndex:index length:0]); + if (_cursorMode == CURSOR_MODE_COMMAND && _selectionMode == XVIM_VISUAL_NONE) { + _insertionPoint = [_textView.textStorage convertToValidCursorPositionForNormalMode:pos]; + } else { + _insertionPoint = pos; + } + + if (!preserve) { + _preservedColumn = [buffer columnOfIndex:_insertionPoint]; + } + + DEBUG_LOG(@"New Insertion Point:%d Preserved Column:%d", _insertionPoint, _preservedColumn); } -- (NSUInteger)_lineNumberAtPoint:(NSPoint)point +- (void)_syncStateFromView { - NSUInteger index; + // TODO: handle block selection (if selectedRanges have multiple ranges ) + if (_syncStateLock || self.buffer.isEditing) { + return; + } - index = [_textView.enclosingScrollView.documentView characterIndexForInsertionAtPoint:point]; - return [_textView.textStorage.xvim_buffer lineNumberAtIndex:index]; + NSRange r = [_textView selectedRange]; + DEBUG_LOG(@"Selected Range(TotalLen:%d): Loc:%d Len:%d", self.buffer.length, r.location, r.length); + [self _moveCursor:r.location preserveColumn:NO]; + _selectionBegin = _insertionPoint; + self.selectionMode = XVIM_VISUAL_NONE; } -- (NSUInteger)lineNumberInScrollView:(CGFloat)ratio offset:(NSInteger)offset +/** + * Applies internal state to underlying view (self). + * This update self's property and applies the visual effect on it. + * All the state need to express Vim is held by this class and + * we use self to express it visually. + **/ +- (void)_syncState { - NSScrollView *scrollView = _textView.enclosingScrollView; - NSRect visibleRect = scrollView.contentView.bounds; - NSPoint point = visibleRect.origin; - CGFloat glyphHeight = [self _glyphHeightAtIndex:_textView.insertionPoint]; + DEBUG_LOG(@"IP:%d", _insertionPoint); + + _syncStateLock = YES; + + if (_cursorMode == CURSOR_MODE_COMMAND) { + NSTextStorage *ts = _textView.textStorage; + if (![ts isValidCursorPosition:_insertionPoint]) { + NSRange placeholder = [(DVTSourceTextView *)_textView rangeOfPlaceholderFromCharacterIndex:_insertionPoint forward:NO wrap:NO limit:0]; + if (placeholder.location != NSNotFound && _insertionPoint == (placeholder.location + placeholder.length)) { + // The condition here means that just before current insertion point is a placeholder. + // So we select the the place holder and its already selected by "selectedPreviousPlaceholder" above + [self _moveCursor:placeholder.location preserveColumn:YES]; + } else { + [self _moveCursor:_insertionPoint - 1 preserveColumn:YES]; + } + } + } - NSInteger minLine, line, maxLine; + TRACE_LOG(@"mode:%d length:%d cursor:%d ip:%d begin:%d line:%d column:%d preservedColumn:%d", \ + _selectionMode, self.buffer.length, _cursorMode, _insertionPoint, _selectionBegin, + self.insertionLine, self.insertionColumn, _preservedColumn); - minLine = (NSInteger)[self _lineNumberAtPoint:point]; - point.y += ratio * (NSHeight(visibleRect) - glyphHeight); - line = (NSInteger)[self _lineNumberAtPoint:point]; - point.y = NSMaxY(visibleRect) - glyphHeight; - maxLine = (NSInteger)[self _lineNumberAtPoint:point]; +#ifndef __XCODE5__ + [(DVTFoldingTextStorage*)_textView.textStorage increaseUsingFoldedRanges]; +#endif + [_textView xvim_setSelectedRanges:[self _selectedRanges] affinity:NSSelectionAffinityDownstream stillSelecting:NO]; +#ifndef __XCODE5__ + [(DVTFoldingTextStorage*)_textView.textStorage decreaseUsingFoldedRanges]; +#endif - return (NSUInteger)MIN(maxLine, MAX(minLine, line + offset)); + [self scrollTo:_insertionPoint]; + _syncStateLock = NO; } -- (NSUInteger)_glyphIndexForPoint:(NSPoint)point +#pragma mark *** Properties *** + +- (XVimBuffer *)buffer { - return [_textView.layoutManager glyphIndexForPoint:point inTextContainer:_textView.textContainer]; + return _textView.textStorage.xvim_buffer; } -- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color - heightRatio:(CGFloat)heightRatio - widthRatio:(CGFloat)widthRatio - alpha:(CGFloat)alpha +- (XVimPosition)insertionPosition { - NSUInteger glyphIndex = [_textView insertionPoint]; - NSRect glyphRect = [self _glyphRectAtIndex:glyphIndex length:1]; - - [[color colorWithAlphaComponent:alpha] set]; - rect.size.width = rect.size.height/2; - if (glyphRect.size.width > 0 && glyphRect.size.width < rect.size.width) { - rect.size.width = glyphRect.size.width; - } + return [self.buffer positionOfIndex:_insertionPoint]; +} - rect.origin.y += (1 - heightRatio) * rect.size.height; - rect.size.height *= heightRatio; - rect.size.width *= widthRatio; +- (NSUInteger)insertionColumn +{ + return [self.buffer columnOfIndex:_insertionPoint]; +} - NSRectFillUsingOperation(rect, NSCompositeSourceOver); +- (NSUInteger)insertionLine +{ + return [self.buffer lineNumberAtIndex:_insertionPoint]; } -#pragma mark *** Scrolling *** +- (XVimPosition)selectionPosition +{ + return [self.buffer positionOfIndex:_selectionBegin]; +} -- (void)_lineUp:(NSUInteger)index count:(NSUInteger)count +- (NSUInteger)selectionColumn { - [_textView scrollLineUp:_textView]; + return [self.buffer columnOfIndex:_selectionBegin]; +} - NSRect visibleRect = _textView.enclosingScrollView.contentView.bounds; - NSRect cursorRect = [self _glyphRectAtIndex:index length:0]; - if (NSMaxY(cursorRect) > NSMaxY(visibleRect)) { - [_textView moveUp:self]; - } +- (NSUInteger)selectionLine +{ + return [self.buffer lineNumberAtIndex:_selectionBegin]; } -- (void)scrollLineBackward:(NSUInteger)count +- (BOOL)inVisualMode { - [self _lineUp:_textView.insertionPoint count:count]; + return _selectionMode != XVIM_VISUAL_NONE; } -- (void)_lineDown:(NSUInteger)index count:(NSUInteger)count +- (BOOL)inBlockMode { - [_textView scrollLineDown:_textView]; + return _selectionMode == XVIM_VISUAL_BLOCK; +} - NSRect visibleRect = _textView.enclosingScrollView.contentView.bounds; - NSRect cursorRect = [self _glyphRectAtIndex:index length:0]; - if (NSMinY(cursorRect) < NSMinY(visibleRect)) { - [_textView moveDown:_textView]; +- (void)setSelectionMode:(XVimVisualMode)mode +{ + if (_selectionMode != mode) { + if (mode == XVIM_VISUAL_NONE) { + _selectionBegin = NSNotFound; + } else if (_selectionMode == XVIM_VISUAL_NONE) { + _selectionBegin = _insertionPoint; + } + _selectionMode = mode; + [self _syncState]; } } -- (void)scrollLineForward:(NSUInteger)count + +#pragma mark *** Visual Mode and Cursor Position *** + +- (XVimRange)_selectedLines { - [self _lineDown:_textView.insertionPoint count:count]; + if (_selectionMode == XVIM_VISUAL_NONE) { // its not in selecting mode + return (XVimRange){ NSNotFound, NSNotFound }; + } else { + NSUInteger l1 = self.insertionLine; + NSUInteger l2 = self.selectionLine; + + return (XVimRange){ MIN(l1, l2), MAX(l1, l2) }; + } } -- (void)_scroll:(CGFloat)ratio count:(NSUInteger)count +- (NSRange)_selectedRange { - NSScrollView *scrollView = _textView.enclosingScrollView; - NSClipView *clipView = scrollView.contentView; - XVimBuffer *buffer = _textView.textStorage.xvim_buffer; + XVimBuffer *buffer = self.buffer; - NSRect visibleRect = clipView.bounds; - CGFloat scrollSize = NSHeight(visibleRect) * ratio * count; - // This may be beyond the beginning or end of document (intentionally) - NSPoint scrollPoint = NSMakePoint(visibleRect.origin.x, visibleRect.origin.y + scrollSize); + if (_selectionMode == XVIM_VISUAL_NONE) { + return NSMakeRange(_insertionPoint, 0); + } - // Cursor position relative to left-top origin shold be kept after scroll - // (Exception is when it scrolls beyond the beginning or end of document) + if (_selectionMode == XVIM_VISUAL_CHARACTER) { + XVimRange xvr = XVimMakeRange(_selectionBegin, _insertionPoint); - NSRect currentInsertionRect = [self _glyphRectAtIndex:_textView.insertionPoint length:1]; - NSPoint relativeInsertionPoint = SubPoint(currentInsertionRect.origin, visibleRect.origin); + if (xvr.begin > xvr.end) { + xvr = XVimRangeSwap(xvr); + } + if (xvr.end >= buffer.length) { + xvr.end--; + } + return XVimMakeNSRange(xvr); + } - // Cursor Position after scroll - NSPoint cursorAfterScroll = AddPoint(scrollPoint, relativeInsertionPoint); + if (_selectionMode == XVIM_VISUAL_LINE) { + XVimRange lines = [self _selectedLines]; + NSUInteger begin = [buffer indexOfLineNumber:lines.begin]; + NSUInteger end = [buffer indexOfLineNumber:lines.end]; - // Nearest character index to the cursor position after scroll - // TODO: consider blank-EOF line. Xcode does not return blank-EOF index with following method... - NSUInteger cursorIndexAfterScroll = [self _glyphIndexForPoint:cursorAfterScroll]; + end = [buffer endOfLine:end]; + if (end >= buffer.length) { + end--; + } + return NSMakeRange(begin, end - begin + 1); + } - // We do not want to change the insert point relative position from top of visible rect - // We have to calc the distance between insertion point befor/after scrolling to keep the position. - NSRect insertionRectAfterScroll = [self _glyphRectAtIndex:cursorIndexAfterScroll length:1]; - NSPoint relativeInsertionPointAfterScroll = SubPoint(insertionRectAfterScroll.origin, scrollPoint); - CGFloat maxScrollY = NSHeight([scrollView.documentView frame]) - NSHeight(visibleRect); + return NSMakeRange(NSNotFound, 0); +} - scrollPoint.y += relativeInsertionPointAfterScroll.y - relativeInsertionPoint.y; - if (scrollPoint.y > maxScrollY) { - // Prohibit scroll beyond the bounds of document - scrollPoint.y = maxScrollY; - } else if (scrollPoint.y < 0.0) { - scrollPoint.y = 0.0; +- (XVimSelection)_selectedBlock +{ + XVimSelection result = { }; + + if (_selectionMode == XVIM_VISUAL_NONE) { + result.top = result.bottom = result.left = result.right = NSNotFound; + return result; } - [[scrollView contentView] scrollToPoint:scrollPoint]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; + XVimBuffer *buffer = self.buffer; + NSUInteger l1, c11, c12; + NSUInteger l2, c21, c22; + NSUInteger tabWidth = buffer.tabWidth; + NSUInteger pos; + + pos = _selectionBegin; + l1 = [buffer lineNumberAtIndex:pos]; + c11 = [buffer columnOfIndex:pos]; + if (!tabWidth || pos >= buffer.length || [buffer.string characterAtIndex:pos] != '\t') { + c12 = c11; + } else { + c12 = c11 + tabWidth - (c11 % tabWidth) - 1; + } - cursorIndexAfterScroll = [buffer firstNonblankInLineAtIndex:cursorIndexAfterScroll allowEOL:YES]; - [_textView xvim_moveCursor:cursorIndexAfterScroll preserveColumn:NO]; - [_textView xvim_syncState]; + pos = _insertionPoint; + l2 = [buffer lineNumberAtIndex:pos]; + c21 = [buffer columnOfIndex:pos]; + if (!tabWidth || pos >= buffer.length || [buffer.string characterAtIndex:pos] != '\t') { + c22 = c21; + } else { + c22 = c21 + tabWidth - (c21 % tabWidth) - 1; + } + + if (l1 <= l2) { + result.corner |= _XVIM_VISUAL_BOTTOM; + } + if (c11 <= c22) { + result.corner |= _XVIM_VISUAL_RIGHT; + } + result.top = MIN(l1, l2); + result.bottom = MAX(l1, l2); + result.left = MIN(c11, c21); + result.right = MAX(c12, c22); + if (_preservedColumn == XVimSelectionEOL) { + result.right = XVimSelectionEOL; + } + return result; } -- (void)scrollPageForward:(NSUInteger)count +- (NSArray *)_selectedRanges { - [self _scroll:1.0 count:count]; + XVimBuffer *buffer = self.buffer; + + if (_selectionMode != XVIM_VISUAL_BLOCK) { + return [NSArray arrayWithObject:[NSValue valueWithRange:[self _selectedRange]]]; + } + + NSMutableArray *rangeArray = [[[NSMutableArray alloc] init] autorelease]; + XVimSelection sel = [self _selectedBlock]; + NSUInteger length = buffer.length; + + for (NSUInteger line = sel.top; line <= sel.bottom; line++) { + NSUInteger begin = [buffer indexOfLineNumber:line column:sel.left]; + NSUInteger end = [buffer indexOfLineNumber:line column:sel.right]; + + if (begin >= length) { + continue; + } + if (end >= length) { + end--; + } else if (sel.right != XVimSelectionEOL && [_textView.textStorage isEOL:end]) { + end--; + } + [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(begin, end - begin + 1)]]; + } + return rangeArray; } -- (void)scrollPageBackward:(NSUInteger)count +- (void)selectSwapCorners:(BOOL)onSameLine { - [self _scroll:-1.0 count:count]; + if (_selectionMode == XVIM_VISUAL_BLOCK) { + XVimBuffer *buffer = self.buffer; + XVimPosition start, end; + XVimSelection sel; + NSUInteger pos; + + sel = [self _selectedBlock]; + if (onSameLine) { + sel.corner ^= _XVIM_VISUAL_RIGHT; + } else { + sel.corner ^= _XVIM_VISUAL_RIGHT | _XVIM_VISUAL_BOTTOM; + } + + if (sel.corner & _XVIM_VISUAL_BOTTOM) { + start.line = sel.top; + end.line = sel.bottom; + } else { + end.line = sel.top; + start.line = sel.bottom; + } + + if (sel.corner & _XVIM_VISUAL_RIGHT) { + start.column = sel.left; + end.column = sel.right; + } else { + end.column = sel.left; + start.column = sel.right; + } + + _selectionBegin = [buffer indexOfLineNumber:start.line column:start.column]; + pos = [buffer indexOfLineNumber:end.line column:end.column]; + [self _moveCursor:pos preserveColumn:NO]; + } else if (_selectionMode != XVIM_VISUAL_NONE) { + NSUInteger begin = _selectionBegin; + + _selectionBegin = _insertionPoint; + [self _moveCursor:begin preserveColumn:NO]; + } + [_textView setNeedsDisplay:YES]; + [self _syncState]; } -- (void)scrollHalfPageForward:(NSUInteger)count +- (void)escapeFromInsert { - [self _scroll:0.5 count:count]; + if (_cursorMode == CURSOR_MODE_INSERT) { + _cursorMode = CURSOR_MODE_COMMAND; + if (![_textView.textStorage isBOL:_insertionPoint]) { + [self _moveCursor:_insertionPoint - 1 preserveColumn:NO]; + } + [self _syncState]; + } } -- (void)scrollHalfPageBackward:(NSUInteger)count +- (void)saveVisualInfoForBuffer:(XVimBuffer *)buffer { - [self _scroll:-0.5 count:count]; + XVimVisualInfo *vi = &buffer->visualInfo; + + vi->mode = _selectionMode; + vi->end = self.insertionPosition; + vi->start = self.selectionPosition; + vi->colwant = _preservedColumn; } -- (void)_scrollCommon_moveCursorPos:(NSUInteger)lineNumber - ratio:(CGFloat)ratio - firstNonblank:(BOOL)fnb +- (void)selectNextPlaceholder { - XVimBuffer *buffer = _textView.textStorage.xvim_buffer; - NSUInteger pos = _textView.insertionPoint; - - if (lineNumber) { - if ((pos = [buffer indexOfLineNumber:lineNumber]) == NSNotFound) { - pos = buffer.length; - } - } - if (fnb) { - pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; +#ifdef __USE_DVTKIT__ + if ([_textView isKindOfClass:[DVTSourceTextView class]]) { + [(DVTSourceTextView *)_textView selectNextPlaceholder:self]; } - [_textView xvim_moveCursor:pos preserveColumn:NO]; - [_textView xvim_syncState]; +#endif +} - NSRect glyphRect = [self _glyphRectAtIndex:_textView.insertionPoint length:0]; +- (void)selectPreviousPlaceholder { +#ifdef __USE_DVTKIT__ + if ([_textView isKindOfClass:[DVTSourceTextView class]]) { + [(DVTSourceTextView *)_textView selectPreviousPlaceholder:self]; + } +#endif +} - NSScrollView *scrollView = _textView.enclosingScrollView; - NSClipView *clipView = scrollView.contentView; +/** + * Adjust cursor position if the position is not valid as normal mode cursor position + * This method may changes selected range of the view. + */ +- (void)adjustCursorPosition +{ + NSRange range = _textView.selectedRange; - NSPoint point = NSMakePoint(0., NSMaxY(glyphRect)); - CGFloat deltay = ratio * NSHeight(_textView.enclosingScrollView.contentView.bounds); - point.y = point.y > deltay ? point.y - deltay : 0.; + // If the current cursor position is not valid for normal mode move it. + if (![_textView.textStorage isValidCursorPosition:range.location]) { + [self selectPreviousPlaceholder]; + NSRange prevPlaceHolder = _textView.selectedRange; - [clipView scrollToPoint:point]; - [scrollView reflectScrolledClipView:clipView]; + if (range.location != prevPlaceHolder.location && range.location == NSMaxRange(prevPlaceHolder)) { + // The condition here means that just before current insertion point is a placeholder. + // So we select the the place holder and its already selected by "selectedPreviousPlaceholder" above + } else { + _textView.selectedRange = NSMakeRange(range.location - 1, 0); + } + } + return; } -- (void)scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zb / z- +- (void)moveCursorToIndex:(NSUInteger)index { - [self _scrollCommon_moveCursorPos:lineNumber ratio:1. firstNonblank:fnb]; + [self _moveCursor:index preserveColumn:NO]; + [self _syncState]; } -- (void)scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zz / z. +- (void)moveCursorToPosition:(XVimPosition)pos { - [self _scrollCommon_moveCursorPos:lineNumber ratio:.5 firstNonblank:fnb]; + NSUInteger index = [self.buffer indexOfLineNumber:pos.line column:pos.column]; + + [self _moveCursor:index preserveColumn:NO]; + if (pos.column == XVimSelectionEOL) { + _preservedColumn = XVimSelectionEOL; + } + [self _syncState]; } -- (void)scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zt / z +- (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion { - [self _scrollCommon_moveCursorPos:lineNumber ratio:0. firstNonblank:fnb]; + NSRange range = NSMakeRange(NSNotFound, 0); + NSUInteger begin = current; + NSUInteger end = NSNotFound; + NSUInteger tmpPos = NSNotFound; + NSUInteger start = NSNotFound; + XVimBuffer *buffer = self.buffer; + NSTextStorage *ts = buffer.textStorage; + + switch (motion.motion) { + case MOTION_NONE: + // Do nothing + break; + case MOTION_FORWARD: + end = [ts next:begin count:motion.count option:motion.option info:motion.info]; + break; + case MOTION_BACKWARD: + end = [ts prev:begin count:motion.count option:motion.option ]; + break; + case MOTION_WORD_FORWARD: + end = [ts wordsForward:begin count:motion.count option:motion.option info:motion.info]; + break; + case MOTION_WORD_BACKWARD: + end = [ts wordsBackward:begin count:motion.count option:motion.option]; + break; + case MOTION_END_OF_WORD_FORWARD: + end = [ts endOfWordsForward:begin count:motion.count option:motion.option]; + break; + case MOTION_END_OF_WORD_BACKWARD: + end = [ts endOfWordsBackward:begin count:motion.count option:motion.option]; + break; + case MOTION_LINE_FORWARD: + end = [ts nextLine:begin column:_preservedColumn count:motion.count option:motion.option]; + break; + case MOTION_LINE_BACKWARD: + end = [ts prevLine:begin column:_preservedColumn count:motion.count option:motion.option]; + break; + case MOTION_BEGINNING_OF_LINE: + end = [buffer startOfLine:begin]; + break; + case MOTION_END_OF_LINE: + end = [ts nextLine:begin column:0 count:motion.count - 1 option:MOTION_OPTION_NONE]; + end = [buffer endOfLine:end]; + break; + case MOTION_SENTENCE_FORWARD: + end = [ts sentencesForward:begin count:motion.count option:motion.option]; + break; + case MOTION_SENTENCE_BACKWARD: + end = [ts sentencesBackward:begin count:motion.count option:motion.option]; + break; + case MOTION_PARAGRAPH_FORWARD: + end = [ts moveFromIndex:begin paragraphs:motion.scount option:motion.option]; + break; + case MOTION_PARAGRAPH_BACKWARD: + end = [ts moveFromIndex:begin paragraphs:-motion.scount option:motion.option]; + break; + case MOTION_NEXT_CHARACTER: + end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + break; + case MOTION_PREV_CHARACTER: + end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + break; + case MOTION_TILL_NEXT_CHARACTER: + end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + if (end != NSNotFound) { + end--; + } + break; + case MOTION_TILL_PREV_CHARACTER: + end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + if (end != NSNotFound) { + end++; + } + break; + case MOTION_NEXT_FIRST_NONBLANK: + end = [ts nextLine:begin column:0 count:motion.count option:motion.option]; + tmpPos = [buffer nextNonblankInLineAtIndex:end allowEOL:NO]; + if (NSNotFound != tmpPos) { + end = tmpPos; + } + break; + case MOTION_PREV_FIRST_NONBLANK: + end = [ts prevLine:begin column:0 count:motion.count option:motion.option]; + tmpPos = [buffer nextNonblankInLineAtIndex:end allowEOL:NO]; + if (NSNotFound != tmpPos) { + end = tmpPos; + } + break; + case MOTION_FIRST_NONBLANK: + end = [buffer firstNonblankInLineAtIndex:begin allowEOL:NO]; + break; + case MOTION_LINENUMBER: + end = [buffer indexOfLineNumber:motion.line column:_preservedColumn]; + if (NSNotFound == end) { + end = [buffer indexOfLineNumber:[buffer numberOfLines] column:_preservedColumn]; + } + break; + case MOTION_PERCENT: + end = [buffer indexOfLineNumber:1 + ([buffer numberOfLines]-1) * motion.count/100]; + break; + case MOTION_NEXT_MATCHED_ITEM: + end = [ts positionOfMatchedPair:begin]; + break; + case MOTION_LASTLINE: + end = [buffer indexOfLineNumber:[buffer numberOfLines] column:_preservedColumn]; + break; + case MOTION_HOME: + tmpPos = [self lineNumberInScrollView:0.0 offset:motion.scount - 1]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; + break; + case MOTION_MIDDLE: + tmpPos = [self lineNumberInScrollView:0.5 offset:0]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; + break; + case MOTION_BOTTOM: + tmpPos = [self lineNumberInScrollView:1.0 offset:1 - motion.scount]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; + break; + case MOTION_SEARCH_FORWARD: + end = [ts searchRegexForward:motion.regex from:_insertionPoint count:motion.count option:motion.option].location; + break; + case MOTION_SEARCH_BACKWARD: + end = [ts searchRegexBackward:motion.regex from:_insertionPoint count:motion.count option:motion.option].location; + break; + case TEXTOBJECT_WORD: + range = [ts currentWord:begin count:motion.count option:motion.option]; + break; + case TEXTOBJECT_BRACES: + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '{', '}'); + break; + case TEXTOBJECT_PARAGRAPH: + // Not supported + start = [ts moveFromIndex:_insertionPoint paragraphs:-1 option:MOPT_PARA_BOUND_BLANKLINE]; + end = [ts moveFromIndex:_insertionPoint paragraphs:motion.scount option:MOPT_PARA_BOUND_BLANKLINE]; + range = NSMakeRange(start, end - start); + break; + case TEXTOBJECT_PARENTHESES: + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '(', ')'); + break; + case TEXTOBJECT_SENTENCE: + // Not supported + break; + case TEXTOBJECT_ANGLEBRACKETS: + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '<', '>'); + break; + case TEXTOBJECT_SQUOTE: + range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '\''); + break; + case TEXTOBJECT_DQUOTE: + range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '\"'); + break; + case TEXTOBJECT_TAG: + // Not supported + break; + case TEXTOBJECT_BACKQUOTE: + range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '`'); + break; + case TEXTOBJECT_SQUAREBRACKETS: + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '[', ']'); + break; + case MOTION_LINE_COLUMN: + end = [buffer indexOfLineNumber:motion.line column:motion.column]; + if (NSNotFound == end) { + end = current; + } + break; + case MOTION_POSITION: + end = motion.position; + break; + } + + if (range.location != NSNotFound) {// This block is for TEXTOBJECT + begin = range.location; + if (range.length == 0) { + end = NSNotFound; + }else{ + end = range.location + range.length - 1; + } + } + + TRACE_LOG(@"range location:%u length:%u", begin, end); + return XVimMakeRange(begin, end); } -- (void)scrollTo:(NSUInteger)location +- (void)moveCursorWithMotion:(XVimMotion*)motion { - // Update: I do not know if we really need Following block. - // It looks that they need it to call ensureLayoutForGlyphRange but do not know when it needed - // What I changed was the way calc "glyphRec". Not its using [self boundingRectForGlyphIndex] which coniders - // text folding when calc the rect. - /* - BOOL isBlankline = - (location == [[self string] length] || isNewline([[self string] characterAtIndex:location])) && - (location == 0 || isNewline([[self string] characterAtIndex:location-1])); + XVimRange r = [self _getMotionRange:_insertionPoint motion:motion]; - NSRange characterRange; - characterRange.location = location; - characterRange.length = isBlankline ? 0 : 1; + if (r.end == NSNotFound) { + return; + } - // Must call ensureLayoutForGlyphRange: to fix a bug where it will not scroll - // to the appropriate glyph due to non contiguous layout - NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:characterRange actualCharacterRange:NULL]; - [[self layoutManager] ensureLayoutForGlyphRange:NSMakeRange(0, glyphRange.location + glyphRange.length)]; - */ + if (_selectionMode != XVIM_VISUAL_NONE && [motion isTextObject]) { + if( _selectionMode == XVIM_VISUAL_LINE) { + // Motion with text object in VISUAL LINE changes visual mode to VISUAL CHARACTER + self.selectionMode = XVIM_VISUAL_CHARACTER; + } - NSScrollView *scrollView = _textView.enclosingScrollView; - NSClipView *clipView = scrollView.contentView; + if (_insertionPoint < _selectionBegin) { + // When insertionPoint < selectionBegin it only changes insertion point to begining of the text object + [self _moveCursor:r.begin preserveColumn:NO]; + } else { + // Text object expands one text object ( the text object under insertion point + 1 ) + if (_insertionPoint + 1 < self.buffer.length) { + r = [self _getMotionRange:_insertionPoint + 1 motion:motion]; + } + if (_selectionBegin > r.begin) { + _selectionBegin = r.begin; + } + [self _moveCursor:r.end preserveColumn:NO]; + } + } else { + switch (motion.motion) { + case MOTION_END_OF_LINE: + _preservedColumn = XVimSelectionEOL; + /* FALLTHROUGH */ + case MOTION_LINE_BACKWARD: + case MOTION_LINE_FORWARD: + case MOTION_LASTLINE: + case MOTION_LINENUMBER: + [self _moveCursor:r.end preserveColumn:YES]; + break; + default: + [self _moveCursor:r.end preserveColumn:NO]; + break; + } + } + [_textView setNeedsDisplay:YES]; + [self _syncState]; +} - NSRect glyphRect = [self _glyphRectAtIndex:location length:1]; - CGFloat glyphLeft = NSMinX(glyphRect); - CGFloat glyphRight = NSMaxX(glyphRect); - CGFloat glyphBottom = NSMaxY(glyphRect); - CGFloat glyphTop = NSMinY(glyphRect); +#pragma mark *** Operations *** + +- (NSRange)_getOperationRange:(XVimRange)xrange type:(MOTION_TYPE)type +{ + XVimBuffer *buffer = self.buffer; + NSUInteger length = buffer.length; + + if (buffer.length == 0) { + return NSMakeRange(0, 0); + } + + if (xrange.begin > xrange.end) { + xrange = XVimRangeSwap(xrange); + } + + // EOF can not be included in operation range. + if (xrange.begin >= length) { + return NSMakeRange(length, 0); + } + + // EOF should not be included. + // If type is exclusive we do not subtract 1 because we do it later below + if (xrange.end >= length && type != CHARACTERWISE_EXCLUSIVE) { + // Note that we already know that "to" is not 0 so not chekcing if its 0. + xrange.end--; + } + + // At this point "from" and "to" is not EOF + if (type == CHARACTERWISE_EXCLUSIVE) { + // to will not be included. + xrange.end--; + } else if (type == CHARACTERWISE_INCLUSIVE) { + // Nothing special + } else if (type == LINEWISE) { + xrange.end = [buffer endOfLine:xrange.end]; + if (xrange.end >= length) { + xrange.end--; + } + xrange.begin = [buffer startOfLine:xrange.begin]; + } + + return XVimMakeNSRange(xrange); +} + +- (void)_registerInsertionPointForUndo +{ + XVimUndoOperation *op = [[XVimUndoOperation alloc] initWithIndex:_insertionPoint]; + [op registerForBuffer:self.buffer]; + [op release]; +} + +- (void)__startYankWithType:(MOTION_TYPE)type +{ + if (_selectionMode == XVIM_VISUAL_NONE) { + if (type == CHARACTERWISE_EXCLUSIVE || type == CHARACTERWISE_INCLUSIVE) { + _lastYankedType = TEXT_TYPE_CHARACTERS; + } else if (type == LINEWISE) { + _lastYankedType = TEXT_TYPE_LINES; + } + } else if (_selectionMode == XVIM_VISUAL_CHARACTER) { + _lastYankedType = TEXT_TYPE_CHARACTERS; + } else if (_selectionMode == XVIM_VISUAL_LINE) { + _lastYankedType = TEXT_TYPE_LINES; + } else if (_selectionMode == XVIM_VISUAL_BLOCK) { + _lastYankedType = TEXT_TYPE_BLOCK; + } + TRACE_LOG(@"YANKED START WITH TYPE:%d", _lastYankedType); +} + +- (void)_yankRange:(NSRange)range withType:(MOTION_TYPE)type +{ + NSString *string = self.buffer.string; + NSString *s; + BOOL needsNL; + + [self __startYankWithType:type]; + + needsNL = _lastYankedType == TEXT_TYPE_LINES; + if (range.length) { + s = [string substringWithRange:range]; + if (needsNL && !isNewline([s characterAtIndex:s.length - 1])) { + s = [s stringByAppendingString:@"\n"]; + } + } else if (needsNL) { + s = @"\n"; + } else { + s = @""; + } + + [_lastYankedText release]; + _lastYankedText = [s retain]; + TRACE_LOG(@"YANKED STRING : %@", s); +} + +- (void)_yankSelection:(XVimSelection)sel +{ + XVimBuffer *buffer = self.buffer; + NSString *string = buffer.string; + NSUInteger tabWidth = buffer.tabWidth; + + NSMutableString *ybuf = [[NSMutableString alloc] init]; + + _lastYankedType = TEXT_TYPE_BLOCK; + + for (NSUInteger line = sel.top; line <= sel.bottom; line++) { + NSUInteger lpos = [buffer indexOfLineNumber:line column:sel.left]; + NSUInteger rpos = [buffer indexOfLineNumber:line column:sel.right]; + + /* if lpos points in the middle of a tab, split it and advance lpos */ + if (lpos < string.length && [string characterAtIndex:lpos] == '\t') { + NSUInteger lcol = sel.left - (sel.left % tabWidth); + + if (lcol < sel.left) { + NSUInteger count = tabWidth - (sel.left - lcol); + + if (lpos == rpos) { + /* if rpos points to the same tab, truncate it to the right also */ + count = sel.right - sel.left + 1; + } + CFStringPad((CFMutableStringRef)ybuf, CFSTR(" "), + (CFIndex)(ybuf.length + count), 0); + lpos++; + } + } + + if (lpos <= rpos) { + if (sel.right == XVimSelectionEOL) { + [ybuf appendString:[string substringWithRange:NSMakeRange(lpos, rpos - lpos)]]; + } else { + NSRange r = NSMakeRange(lpos, rpos - lpos + 1); + NSUInteger rcol; + BOOL mustPad = NO; + + if (rpos >= string.length) { + rcol = [buffer columnOfIndex:rpos]; + mustPad = YES; + r.length--; + } else { + unichar c = [string characterAtIndex:rpos]; + if (isNewline(c)) { + rcol = [buffer columnOfIndex:rpos]; + mustPad = YES; + r.length--; + } else if (c == '\t') { + rcol = [buffer columnOfIndex:rpos]; + if (sel.right - rcol + 1 < tabWidth) { + mustPad = YES; + r.length--; + } + } + } + + if (r.length) { + [ybuf appendString:[string substringWithRange:r]]; + } + + if (mustPad) { + [ybuf appendString:[NSString stringMadeOfSpaces:sel.right - rcol + 1]]; + } + } + } + [ybuf appendString:@"\n"]; + } + + [_lastYankedText release]; + _lastYankedText = ybuf; + TRACE_LOG(@"YANKED STRING : %@", ybuf); +} + +- (void)_killSelection:(XVimSelection)sel +{ + XVimBuffer *buffer = self.buffer; + NSString *string = buffer.string; + NSUInteger tabWidth = buffer.tabWidth; + + for (NSUInteger line = sel.bottom; line >= sel.top; line--) { + NSUInteger lpos = [buffer indexOfLineNumber:line column:sel.left]; + NSUInteger rpos = [buffer indexOfLineNumber:line column:sel.right]; + NSUInteger nspaces = 0; + + if (lpos >= string.length) { + continue; + } + + if ([string characterAtIndex:lpos] == '\t') { + NSUInteger lcol = [buffer columnOfIndex:lpos]; + + if (lcol < sel.left) { + nspaces = sel.left - lcol; + if (lpos == rpos) { + nspaces = tabWidth - (sel.right - sel.left + 1); + } + } + } + + if ([_textView.textStorage isEOL:rpos]) { + rpos--; + } else if (lpos < rpos) { + if ([string characterAtIndex:rpos] == '\t') { + nspaces += tabWidth - (sel.right - [buffer columnOfIndex:rpos] + 1); + } + } + + NSRange range = NSMakeRange(lpos, rpos - lpos + 1); + + [buffer replaceCharactersInRange:range withSpaces:nspaces]; + } +} + +- (void)doDelete:(XVimMotion *)motion andYank:(BOOL)yank +{ + XVimBuffer *buffer = self.buffer; + + NSAssert(!(_selectionMode == XVIM_VISUAL_NONE && motion == nil), + @"motion must be specified if current selection mode is not visual"); + if (_insertionPoint == 0 && buffer.length == 0) { + return ; + } + NSUInteger pos = _insertionPoint; + + motion.info->deleteLastLine = NO; + if (_selectionMode == XVIM_VISUAL_NONE) { + XVimRange motionRange = [self _getMotionRange:pos motion:motion]; + NSRange r; + + if (motionRange.end == NSNotFound) { + return; + } + + // We have to treat some special cases + // When a cursor get end of line with "l" motion, make the motion type to inclusive. + // This make you to delete the last character. (if its exclusive last character never deleted with "dl") + if (motion.motion == MOTION_FORWARD && motion.info->reachedEndOfLine) { + if (motion.type == CHARACTERWISE_EXCLUSIVE) { + motion.type = CHARACTERWISE_INCLUSIVE; + } else if (motion.type == CHARACTERWISE_INCLUSIVE) { + motion.type = CHARACTERWISE_EXCLUSIVE; + } + } + if (motion.motion == MOTION_WORD_FORWARD) { + if ((motion.info->isFirstWordInLine && motion.info->lastEndOfLine != NSNotFound)) { + // Special cases for word move over a line break. + motionRange.end = motion.info->lastEndOfLine; + motion.type = CHARACTERWISE_INCLUSIVE; + } + else if (motion.info->reachedEndOfLine) { + if (motion.type == CHARACTERWISE_EXCLUSIVE) { + motion.type = CHARACTERWISE_INCLUSIVE; + } else if (motion.type == CHARACTERWISE_INCLUSIVE) { + motion.type = CHARACTERWISE_EXCLUSIVE; + } + } + } + r = [self _getOperationRange:motionRange type:motion.type]; + + if (motion.type == LINEWISE && [_textView.textStorage isLastLine:motionRange.end]) { + if (r.location != 0) { + motion.info->deleteLastLine = YES; + r.location--; + r.length++; + } + } + if (yank) { + [self _yankRange:r withType:motion.type]; + } + + pos = r.location; + [self _moveCursor:pos preserveColumn:NO]; + [buffer beginEditingAtIndex:pos]; + [buffer replaceCharactersInRange:r withString:@""]; + if (motion.type == LINEWISE) { + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { + BOOL toFirstNonBlank = (self.selectionMode == XVIM_VISUAL_LINE); + NSRange range = [self _selectedRange]; + + // Currently not supportin deleting EOF with selection mode. + // This is because of the fact that NSTextView does not allow select EOF + + if (yank) { + [self _yankRange:range withType:DEFAULT_MOTION_TYPE]; + } + + pos = range.location; + [self _moveCursor:pos preserveColumn:NO]; + [buffer beginEditingAtIndex:pos]; + [buffer replaceCharactersInRange:range withString:@""]; + if (toFirstNonBlank) { + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + } else { + XVimSelection sel = [self _selectedBlock]; + if (yank) { + [self _yankSelection:sel]; + } + pos = [buffer indexOfLineNumber:sel.top column:sel.left]; + [buffer beginEditingAtIndex:pos]; + [self _killSelection:sel]; + } + [buffer endEditingAtIndex:pos]; + + [_delegate textView:_textView didDelete:_lastYankedText withType:_lastYankedType]; + + [self _moveCursor:pos preserveColumn:NO]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doChange:(XVimMotion *)motion +{ + XVimBuffer *buffer = self.buffer; + + BOOL insertNewline = NO; + if (motion.type == LINEWISE || _selectionMode == XVIM_VISUAL_LINE) { + // 'cc' deletes the lines but need to keep the last newline. + // So insertNewline as 'O' does before entering insert mode + insertNewline = YES; + } + + // "cw" is like "ce" if the cursor is on a word ( in this case blank line is not treated as a word ) + if (motion.motion == MOTION_WORD_FORWARD && [_textView.textStorage isNonblank:_insertionPoint]) { + motion.motion = MOTION_END_OF_WORD_FORWARD; + motion.type = CHARACTERWISE_INCLUSIVE; + motion.option |= MOTION_OPTION_CHANGE_WORD; + } + + [buffer beginEditingAtIndex:_insertionPoint]; + _cursorMode = CURSOR_MODE_INSERT; + [self doDelete:motion andYank:YES]; + + if (motion.info->deleteLastLine || insertNewline) { + [self _insertNewlineAboveLine:[buffer lineNumberAtIndex:_insertionPoint]]; + } + + [buffer endEditingAtIndex:_insertionPoint]; + + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doYank:(XVimMotion*)motion +{ + XVimBuffer *buffer = self.buffer; + + NSAssert( !(_selectionMode == XVIM_VISUAL_NONE && motion == nil), + @"motion must be specified if current selection mode is not visual"); + NSUInteger newPos = NSNotFound; + + if (_selectionMode == XVIM_VISUAL_NONE) { + XVimRange to = [self _getMotionRange:_insertionPoint motion:motion]; + NSRange r; + + if (NSNotFound == to.end) { + return; + } + + // We have to treat some special cases (same as delete) + if (motion.motion == MOTION_FORWARD && motion.info->reachedEndOfLine) { + motion.type = CHARACTERWISE_INCLUSIVE; + } + if (motion.motion == MOTION_WORD_FORWARD) { + if ((motion.info->isFirstWordInLine && motion.info->lastEndOfLine != NSNotFound)) { + // Special cases for word move over a line break. + to.end = motion.info->lastEndOfLine; + motion.type = CHARACTERWISE_INCLUSIVE; + } + else if (motion.info->reachedEndOfLine) { + if (motion.type == CHARACTERWISE_EXCLUSIVE) { + motion.type = CHARACTERWISE_INCLUSIVE; + } else if (motion.type == CHARACTERWISE_INCLUSIVE) { + motion.type = CHARACTERWISE_EXCLUSIVE; + } + } + } + r = [self _getOperationRange:to type:motion.type]; + if (motion.type == LINEWISE && to.end >= buffer.length && [_textView.textStorage isBOL:to.end]) { + if (r.location != 0) { + r.location--; + r.length++; + } + } + [self _yankRange:r withType:motion.type]; + } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { + NSRange range = [self _selectedRange]; + + newPos = range.location; + [self _yankRange:range withType:DEFAULT_MOTION_TYPE]; + } else { + XVimSelection sel = [self _selectedBlock]; + + newPos = [buffer indexOfLineNumber:sel.top column:sel.left]; + [self _yankSelection:sel]; + } + + [_delegate textView:_textView didYank:_lastYankedText withType:_lastYankedType]; + if (newPos != NSNotFound) { + [self _moveCursor:newPos preserveColumn:NO]; + } + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doPut:(NSString *)_text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)count +{ + XVimBuffer *buffer = self.buffer; + NSMutableString *text = [_text mutableCopy]; + + TRACE_LOG(@"text:%@ type:%d afterCursor:%d count:%d", text, type, after, count); + + [buffer beginEditingAtIndex:_insertionPoint]; + + if (self.selectionMode != XVIM_VISUAL_NONE) { + [self doDelete:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 1) andYank:YES]; + after = NO; + } + + NSUInteger insertionPointAfterPut = _insertionPoint; + NSUInteger targetPos = _insertionPoint; + + if (type == TEXT_TYPE_CHARACTERS) { + // Forward insertion point +1 if after flag if on + if (0 != text.length) { + if (![_textView.textStorage isNewline:_insertionPoint] && after) { + targetPos++; + } + insertionPointAfterPut = targetPos; + for (NSUInteger i = 0; i < count ; i++) { + [buffer replaceCharactersInRange:NSMakeRange(targetPos, 0) withString:text]; + } + insertionPointAfterPut += text.length * count - 1; + } + } else if (type == TEXT_TYPE_LINES) { + if (after) { + [self _insertNewlineBelowCurrentLine]; + targetPos = _insertionPoint; + } else { + targetPos= [buffer startOfLine:_insertionPoint]; + } + insertionPointAfterPut = targetPos; + if (after) { + // delete newline at the end. (TEXT_TYPE_LINES always have newline at the end of the text) + [text replaceCharactersInRange:NSMakeRange(text.length - 1, 1) withString:@""]; + } + for (NSUInteger i = 0; i < count ; i++) { + [buffer replaceCharactersInRange:NSMakeRange(targetPos, 0) withString:text]; + targetPos += text.length; + } + } else if (type == TEXT_TYPE_BLOCK) { + // Forward insertion point +1 if after flag if on + if (![_textView.textStorage isNewline:_insertionPoint] && _insertionPoint < buffer.length && after) { + _insertionPoint++; + } + insertionPointAfterPut = _insertionPoint; + + NSUInteger insertPos = _insertionPoint; + NSUInteger column = [buffer columnOfIndex:insertPos]; + NSUInteger startLine = [buffer lineNumberAtIndex:insertPos]; + NSArray *lines = [text componentsSeparatedByString:@"\n"]; + + for (NSUInteger i = 0 ; i < lines.count ; i++) { + NSString *line = [lines objectAtIndex:i]; + NSUInteger targetLine = startLine + i; + NSUInteger head = [buffer indexOfLineNumber:targetLine]; + + if (NSNotFound == head) { + NSAssert( targetLine != 0, @"This should not be happen"); + [buffer replaceCharactersInRange:NSMakeRange(buffer.length, 0) withString:@"\n"]; + head = buffer.length; + } + NSAssert(NSNotFound != head, @"Head of the target line must be found at this point"); + + // Find next insertion point + NSUInteger max = [buffer numberOfColumnsInLineAtIndex:head]; + + // FIXME: deal with tabs here + + // If the line does not have enough column pad it with spaces + if (column > max) { + NSUInteger end = [buffer endOfLine:head]; + + [buffer replaceCharactersInRange:NSMakeRange(end, 0) withSpaces:column - max]; + } + + NSUInteger pos = [buffer indexOfLineNumber:targetLine column:column]; + + for (NSUInteger i = 0; i < count ; i++) { + [buffer replaceCharactersInRange:NSMakeRange(pos, 0) withString:line]; + pos += line.length; + } + } + } + + [text release]; + [buffer endEditingAtIndex:insertionPointAfterPut]; + + [self _moveCursor:insertionPointAfterPut preserveColumn:NO]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doSwapCharacters:(XVimMotion *)motion mode:(int)mode +{ + XVimBuffer *buffer = self.buffer; + NSUInteger undoPos = _insertionPoint; + NSUInteger endPos; + + if (buffer.length == 0) { + return; + } + + if (self.selectionMode == XVIM_VISUAL_NONE) { + NSRange range; + + if (motion.motion == MOTION_NONE) { + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_FORWARD,CHARACTERWISE_EXCLUSIVE,LEFT_RIGHT_NOWRAP,motion.count); + XVimRange r = [self _getMotionRange:undoPos motion:m]; + + if (r.end == NSNotFound) { + return; + } + if (m.info->reachedEndOfLine) { + range = [self _getOperationRange:r type:CHARACTERWISE_INCLUSIVE]; + } else { + range = [self _getOperationRange:r type:CHARACTERWISE_EXCLUSIVE]; + } + endPos = r.end; + } else { + XVimRange to = [self _getMotionRange:undoPos motion:motion]; + if (to.end == NSNotFound) { + return; + } + + range = [self _getOperationRange:to type:motion.type]; + endPos = range.location; + } + + [buffer beginEditingAtIndex:undoPos]; + [buffer swapCharactersInRange:range mode:mode]; + [buffer endEditingAtIndex:endPos]; + } else { + NSArray *ranges = [self _selectedRanges]; + + endPos = undoPos = [[ranges objectAtIndex:0] rangeValue].location; + [buffer beginEditingAtIndex:undoPos]; + for (NSValue *v in ranges) { + [buffer swapCharactersInRange:v.rangeValue mode:mode]; + } + [buffer endEditingAtIndex:endPos]; + } + + [self _moveCursor:endPos preserveColumn:NO]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (BOOL)doReplaceCharacters:(unichar)c count:(NSUInteger)count +{ + XVimBuffer *buffer = self.buffer; + NSUInteger end = [buffer endOfLine:_insertionPoint]; + + // Note : endOfLine may return one less than _insertionPoint if _insertionPoint is on newline + if (NSNotFound == end) { + return NO; + } + + if (_insertionPoint + count >= end) { + return NO; + } + + NSMutableString *s = [[NSMutableString alloc] initWithCapacity:count]; + unichar buf[8] = { c, c, c, c, c, c, c, c }; + NSUInteger pos = _insertionPoint; + + [buffer beginEditingAtIndex:pos]; + for (NSUInteger i = 0; i < count; i += 8) { + [s appendCharacters:buf length:MIN(8, count - i)]; + } + [buffer replaceCharactersInRange:NSMakeRange(pos, count) withString:s]; + [buffer endEditingAtIndex:pos]; + + [s release]; + + [self _moveCursor:pos + count preserveColumn:NO]; + [self _syncState]; + return YES; +} + +- (void)_joinAtLineNumber:(NSUInteger)line +{ + XVimBuffer *buffer = self.buffer; + NSUInteger headOfLine = [buffer indexOfLineNumber:line]; + NSTextStorage *ts = _textView.textStorage; + BOOL needSpace = NO; + + if (headOfLine == NSNotFound) { + return; + } + + NSUInteger tail = [buffer endOfLine:headOfLine]; + if (tail >= buffer.length) { + // This is the last line and nothing to join + return; + } + + // Check if we need to insert space between lines. + NSUInteger lastOfLine = [buffer lastOfLine:headOfLine]; + if (lastOfLine != NSNotFound) { + // This is not blank line so we check if the last character is space or not . + if (![ts isWhitespace:lastOfLine]) { + needSpace = YES; + } + } + + // Search in next line for the position to join(skip white spaces in next line) + NSUInteger posToJoin = [ts nextLine:headOfLine column:0 count:1 option:MOTION_OPTION_NONE]; + + posToJoin = [buffer nextNonblankInLineAtIndex:posToJoin allowEOL:YES]; + if (posToJoin < buffer.length && [buffer.string characterAtIndex:posToJoin] == ')') { + needSpace = NO; + } + + // delete "tail" to "posToJoin" excluding the position of "posToJoin" and insert space if need. + if (needSpace) { + [buffer replaceCharactersInRange:NSMakeRange(tail, posToJoin - tail) withString:@" "]; + } else { + [buffer replaceCharactersInRange:NSMakeRange(tail, posToJoin - tail) withString:@""]; + } + + // Move cursor + [self _moveCursor:tail preserveColumn:NO]; +} + +- (void)doJoin:(NSUInteger)count addSpace:(BOOL)addSpace +{ + XVimBuffer *buffer = self.buffer; + NSUInteger line; + + [buffer beginEditingAtIndex:_insertionPoint]; + + if (_selectionMode == XVIM_VISUAL_NONE) { + line = self.insertionLine; + } else { + XVimRange lines = [self _selectedLines]; + + line = lines.begin; + count = MAX(1, lines.end - lines.begin); + } + + if (addSpace) { + for (NSUInteger i = 0; i < count; i++) { + [self _joinAtLineNumber:line]; + } + } else { + NSUInteger pos = [buffer indexOfLineNumber:line]; + + for (NSUInteger i = 0; i < count; i++) { + NSUInteger tail = [buffer endOfLine:pos]; + + if (tail < buffer.length) { + [buffer replaceCharactersInRange:NSMakeRange(tail, 1) withString:@""]; + [self _moveCursor:tail preserveColumn:NO]; + } + } + } + + [buffer endEditingAtIndex:_insertionPoint]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)_indentCharacterRange:(NSRange)range +{ + NSTextStorage *ts = _textView.textStorage; + XVimBuffer *buffer = self.buffer; + +#ifdef __USE_DVTKIT__ +#ifdef __XCODE5__ + if ([ts isKindOfClass:[DVTTextStorage class]]) { + [(DVTTextStorage *)ts indentCharacterRange:range undoManager:buffer.undoManager]; + } + return; +#else + if ([self.textStorage isKindOfClass:[DVTSourceTextStorage class]]) { + [(DVTSourceTextStorage *)ts indentCharacterRange:range undoManager:buffer.undoManager]; + } + return; +#endif +#else +#error You must implement here +#endif + + NSAssert(NO, @"You must implement here if you dont use this caregory with DVTSourceTextView"); +} + +- (void)doFilter:(XVimMotion*)motion +{ + XVimBuffer *buffer = self.buffer; + + if (_insertionPoint == 0 && buffer.length == 0) { + return ; + } + + NSUInteger insertionAfterFilter = _insertionPoint; + NSRange filterRange; + if (self.selectionMode == XVIM_VISUAL_NONE) { + XVimRange to = [self _getMotionRange:_insertionPoint motion:motion]; + if (to.end == NSNotFound) { + return; + } + filterRange = [self _getOperationRange:to type:LINEWISE]; + } else { + XVimRange lines = [self _selectedLines]; + NSUInteger from = [buffer indexOfLineNumber:lines.begin]; + NSUInteger to = [buffer indexOfLineNumber:lines.end]; + filterRange = [self _getOperationRange:XVimMakeRange(from, to) type:LINEWISE]; + } + + [self _indentCharacterRange:filterRange]; + [self _moveCursor:insertionAfterFilter preserveColumn:NO]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doShift:(XVimMotion *)motion right:(BOOL)right +{ + XVimBuffer *buffer = self.buffer; + + if (_insertionPoint == 0 && buffer.length == 0) { + return ; + } + + NSUInteger shiftWidth = buffer.indentWidth; + NSUInteger column = 0, pos; + XVimRange lines; + BOOL blockMode = NO; + + if (self.selectionMode == XVIM_VISUAL_NONE) { + XVimRange to = [self _getMotionRange:_insertionPoint motion:motion]; + if (to.end == NSNotFound) { + return; + } + lines = XVimMakeRange([buffer lineNumberAtIndex:to.begin], [buffer lineNumberAtIndex:to.end]); + } else if (_selectionMode != XVIM_VISUAL_BLOCK) { + lines = [self _selectedLines]; + shiftWidth *= motion.count; + } else { + XVimSelection sel = [self _selectedBlock]; + + column = sel.left; + lines = XVimMakeRange(sel.top, sel.bottom); + shiftWidth *= motion.count; + blockMode = YES; + } + + if (blockMode) { + pos = [buffer indexOfLineNumber:lines.begin column:column]; + } else { + pos = [buffer indexOfLineNumber:lines.begin]; + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + + [buffer beginEditingAtIndex:pos]; + pos = [buffer shiftLines:lines column:column + count:shiftWidth right:right block:blockMode]; + [buffer endEditingAtIndex:pos]; + + [self _moveCursor:pos preserveColumn:NO]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)_insertNewlineBelowCurrentLine +{ + XVimBuffer *buffer = self.buffer; + + NSUInteger pos = [buffer startOfLine:_insertionPoint]; + + _insertionPoint = pos; + [buffer beginEditingAtIndex:_insertionPoint]; + pos = [buffer endOfLine:pos]; + [buffer replaceCharactersInRange:NSMakeRange(pos, 0) withString:@"\n"]; + [buffer endEditingAtIndex:_insertionPoint]; + + [self _moveCursor:pos + 1 preserveColumn:NO]; + [self _syncState]; +} + +- (void)_insertNewlineAboveLine:(NSUInteger)line +{ + XVimBuffer *buffer = self.buffer; + NSUInteger pos = [self.buffer indexOfLineNumber:line]; + + if (NSNotFound == pos) { + return; + } + if (line == 1) { + [buffer beginEditingAtIndex:0]; + [buffer replaceCharactersInRange:NSMakeRange(0, 0) withString:@"\n"]; + [buffer endEditingAtIndex:0]; + } else { + _insertionPoint = pos; + [self _insertNewlineBelowCurrentLine]; + } +} + +- (void)insertNewlineAboveAndInsertWithIndent +{ + NSUInteger head = [self.buffer startOfLine:_insertionPoint]; + + _cursorMode = CURSOR_MODE_INSERT; + if (head) { + [_textView setSelectedRange:NSMakeRange(head - 1,0)]; + [_textView insertNewline:self]; + } else { + [_textView setSelectedRange:NSMakeRange(0, 0)]; + [_textView insertNewline:self]; + [_textView setSelectedRange:NSMakeRange(0, 0)]; + } +} + +- (void)insertNewlineBelowAndInsertWithIndent +{ + NSUInteger tail = [self.buffer endOfLine:_insertionPoint]; + + _cursorMode = CURSOR_MODE_INSERT; + [_textView setSelectedRange:NSMakeRange(tail, 0)]; + [_textView insertNewline:_textView]; +} + +- (void)doInsert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column blockLines:(XVimRange *)lines +{ + XVimBuffer *buffer = self.buffer; + + if (column) *column = NSNotFound; + if (lines) *lines = XVimMakeRange(NSNotFound, NSNotFound); + + if (self.selectionMode == XVIM_VISUAL_BLOCK) { + XVimSelection sel = [self _selectedBlock]; + NSUInteger tl = [buffer indexOfLineNumber:sel.top column:sel.left]; + + if (lines) *lines = XVimMakeRange(sel.top, sel.bottom); + switch (mode) { + case XVIM_INSERT_BLOCK_KILL_EOL: + sel.right = XVimSelectionEOL; + /* fallthrough */ + case XVIM_INSERT_BLOCK_KILL: + [self _yankSelection:sel]; + [buffer beginEditingAtIndex:tl]; + [self _killSelection:sel]; + [buffer endEditingAtIndex:tl]; + /* falltrhough */ + case XVIM_INSERT_DEFAULT: + _insertionPoint = tl; + if (column) *column = sel.left; + break; + case XVIM_INSERT_APPEND: + if (sel.right != XVimSelectionEOL) { + sel.right++; + } + _insertionPoint = [buffer indexOfLineNumber:sel.top column:sel.right]; + if (column) *column = sel.right; + break; + default: + NSAssert(false, @"unreachable"); + break; + } + } else if (mode != XVIM_INSERT_DEFAULT) { + NSUInteger pos = _insertionPoint; + switch (mode) { + case XVIM_INSERT_APPEND_EOL: + _insertionPoint = [buffer endOfLine:pos]; + break; + case XVIM_INSERT_APPEND: + NSAssert(_cursorMode == CURSOR_MODE_COMMAND, @"_cursorMode shoud be CURSOR_MODE_COMMAND"); + if (![_textView.textStorage isEOL:pos]) { + _insertionPoint = pos + 1; + } + break; + case XVIM_INSERT_BEFORE_FIRST_NONBLANK: + _insertionPoint = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + break; + default: + NSAssert(false, @"unreachable"); + } + } + + _cursorMode = CURSOR_MODE_INSERT; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (BOOL)doIncrementNumber:(int64_t)offset +{ + XVimBuffer *buffer = self.buffer; + NSUInteger pos; + + pos = [buffer incrementNumberAtIndex:_insertionPoint by:offset]; + if (pos == NSNotFound) { + return NO; + } + [self _moveCursor:pos preserveColumn:NO]; + [self _syncState]; + return YES; +} + +- (void)doInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint)mode + count:(NSUInteger)count column:(NSUInteger)column lines:(XVimRange)lines +{ + XVimBuffer *buffer = self.buffer; + NSMutableString *buf = nil; + NSUInteger tabWidth = buffer.tabWidth; + + if (count == 0 || lines.begin > lines.end || text.length == 0) { + return; + } + if ([text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location != NSNotFound) { + return; + } + if (count > 1) { + buf = [[NSMutableString alloc] initWithCapacity:text.length * count]; + for (NSUInteger i = 0; i < count; i++) { + [buf appendString:text]; + } + text = buf; + } + + [buffer beginEditingAtIndex:NSNotFound]; + for (NSUInteger line = lines.begin; line <= lines.end; line++) { + NSUInteger pos = [buffer indexOfLineNumber:line column:column]; + + if (column != XVimSelectionEOL && [_textView.textStorage isEOL:pos]) { + if ([buffer columnOfIndex:pos] < column) { + if (mode != XVIM_INSERT_APPEND) { + continue; + } + [buffer replaceCharactersInRange:NSMakeRange(pos, 0) + withSpaces:column - [buffer columnOfIndex:pos]]; + } + } + if (tabWidth && [buffer.string characterAtIndex:pos] == '\t') { + NSUInteger col = [buffer columnOfIndex:pos]; + + if (col < column) { + [buffer replaceCharactersInRange:NSMakeRange(pos, 1) + withSpaces:tabWidth - (col % tabWidth)]; + pos += column - col; + } + } + [buffer replaceCharactersInRange:NSMakeRange(pos, 0) withString:text]; + } + [buffer endEditingAtIndex:NSNotFound]; + + [buf release]; +} + +- (void)doSortLines:(XVimRange)range withOptions:(XVimSortOptions)options +{ + XVimBuffer *buffer = self.buffer; + NSUInteger pos; + + NSAssert(range.begin > 0 && range.end > 0, @"lines must be greater than 0."); + + if (range.begin > range.end) { + range = XVimRangeSwap(range); + } + + NSMutableArray *lines = [[NSMutableArray alloc] initWithCapacity:range.end - range.begin + 1]; + NSRange characterRange = [buffer indexRangeForLines:XVimMakeNSRange(range)]; + + pos = [buffer indexOfLineNumber:range.begin]; + for (NSUInteger line = range.begin; line <= range.end; line++) { + NSUInteger nlLen; + NSRange lineRange; + + lineRange = [buffer indexRangeForLineAtIndex:pos newLineLength:&nlLen]; + if (line == range.end && nlLen == 0 && lineRange.length == 0) { + break; + } + [lines addObject:[buffer.string substringWithRange:lineRange]]; + pos = NSMaxRange(lineRange) + nlLen; + } + + [lines sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) { + NSStringCompareOptions compareOptions = 0; + if (options & XVimSortOptionNumericSort) { + compareOptions |= NSNumericSearch; + } + if (options & XVimSortOptionIgnoreCase) { + compareOptions |= NSCaseInsensitiveSearch; + } + + if (options & XVimSortOptionReversed) { + return [str2 compare:str1 options:compareOptions]; + } else { + return [str1 compare:str2 options:compareOptions]; + } + }]; + + if (options & XVimSortOptionRemoveDuplicateLines) { + NSMutableIndexSet *removeIndices = [NSMutableIndexSet indexSet]; + // At this point the lines are already sorted + [lines enumerateObjectsUsingBlock:^(NSString *str, NSUInteger idx, BOOL *stop) { + if (idx < [lines count] - 1) { + NSString *nextStr = [lines objectAtIndex:idx + 1]; + if ([str isEqualToString:nextStr]) { + [removeIndices addIndex:idx + 1]; + } + } + }]; + [lines removeObjectsAtIndexes:removeIndices]; + } + + NSString *str = [[lines componentsJoinedByString:@"\n"] stringByAppendingString:@"\n"]; + + pos = characterRange.location; + [buffer beginEditingAtIndex:pos]; + [buffer replaceCharactersInRange:characterRange withString:str]; + [buffer endEditingAtIndex:pos]; + + [self _moveCursor:pos preserveColumn:NO]; + [self _syncState]; + + [lines release]; +} + +#pragma mark *** Drawing *** + +- (NSRect)_glyphRectAtIndex:(NSUInteger)index length:(NSUInteger)length +{ + if (length && index + length >= _textView.textStorage.length) { + // When the index is EOF the range to specify here can not be grater than 0. + // If it is greater than 0 it returns (0,0) as a glyph rect. + length = 0; + } + return [_textView.layoutManager boundingRectForGlyphRange:NSMakeRange(index, length) + inTextContainer:_textView.textContainer]; +} + +- (NSUInteger)_glyphHeightAtIndex:(NSUInteger)index +{ + return NSHeight([self _glyphRectAtIndex:index length:0]); +} + +- (NSUInteger)_lineNumberAtPoint:(NSPoint)point +{ + NSUInteger index; + + index = [_textView.enclosingScrollView.documentView characterIndexForInsertionAtPoint:point]; + return [self.buffer lineNumberAtIndex:index]; +} + +- (NSUInteger)lineNumberInScrollView:(CGFloat)ratio offset:(NSInteger)offset +{ + NSScrollView *scrollView = _textView.enclosingScrollView; + NSRect visibleRect = scrollView.contentView.bounds; + NSPoint point = visibleRect.origin; + CGFloat glyphHeight = [self _glyphHeightAtIndex:_insertionPoint]; + + NSInteger minLine, line, maxLine; + + minLine = (NSInteger)[self _lineNumberAtPoint:point]; + point.y += ratio * (NSHeight(visibleRect) - glyphHeight); + line = (NSInteger)[self _lineNumberAtPoint:point]; + point.y = NSMaxY(visibleRect) - glyphHeight; + maxLine = (NSInteger)[self _lineNumberAtPoint:point]; + + return (NSUInteger)MIN(maxLine, MAX(minLine, line + offset)); +} + +- (NSUInteger)_glyphIndexForPoint:(NSPoint)point +{ + return [_textView.layoutManager glyphIndexForPoint:point inTextContainer:_textView.textContainer]; +} + +- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color + heightRatio:(CGFloat)heightRatio + widthRatio:(CGFloat)widthRatio + alpha:(CGFloat)alpha +{ + NSRect glyphRect = [self _glyphRectAtIndex:_insertionPoint length:1]; + + [[color colorWithAlphaComponent:alpha] set]; + rect.size.width = rect.size.height/2; + if (glyphRect.size.width > 0 && glyphRect.size.width < rect.size.width) { + rect.size.width = glyphRect.size.width; + } + + rect.origin.y += (1 - heightRatio) * rect.size.height; + rect.size.height *= heightRatio; + rect.size.width *= widthRatio; + + NSRectFillUsingOperation(rect, NSCompositeSourceOver); +} + +#pragma mark *** Scrolling *** + +- (void)_lineUp:(NSUInteger)index count:(NSUInteger)count +{ + [_textView scrollLineUp:_textView]; + + NSRect visibleRect = _textView.enclosingScrollView.contentView.bounds; + NSRect cursorRect = [self _glyphRectAtIndex:index length:0]; + if (NSMaxY(cursorRect) > NSMaxY(visibleRect)) { + [_textView moveUp:self]; + } +} + +- (void)scrollLineBackward:(NSUInteger)count +{ + [self _lineUp:_insertionPoint count:count]; +} + +- (void)_lineDown:(NSUInteger)index count:(NSUInteger)count +{ + [_textView scrollLineDown:_textView]; + + NSRect visibleRect = _textView.enclosingScrollView.contentView.bounds; + NSRect cursorRect = [self _glyphRectAtIndex:index length:0]; + if (NSMinY(cursorRect) < NSMinY(visibleRect)) { + [_textView moveDown:_textView]; + } +} + +- (void)scrollLineForward:(NSUInteger)count +{ + [self _lineDown:_insertionPoint count:count]; +} + +- (void)_scroll:(CGFloat)ratio count:(NSUInteger)count +{ + NSScrollView *scrollView = _textView.enclosingScrollView; + NSClipView *clipView = scrollView.contentView; + XVimBuffer *buffer = self.buffer; + + NSRect visibleRect = clipView.bounds; + CGFloat scrollSize = NSHeight(visibleRect) * ratio * count; + // This may be beyond the beginning or end of document (intentionally) + NSPoint scrollPoint = NSMakePoint(visibleRect.origin.x, visibleRect.origin.y + scrollSize); + + // Cursor position relative to left-top origin shold be kept after scroll + // (Exception is when it scrolls beyond the beginning or end of document) + + NSRect currentInsertionRect = [self _glyphRectAtIndex:_insertionPoint length:1]; + NSPoint relativeInsertionPoint = SubPoint(currentInsertionRect.origin, visibleRect.origin); + + // Cursor Position after scroll + NSPoint cursorAfterScroll = AddPoint(scrollPoint, relativeInsertionPoint); + + // Nearest character index to the cursor position after scroll + // TODO: consider blank-EOF line. Xcode does not return blank-EOF index with following method... + NSUInteger cursorIndexAfterScroll = [self _glyphIndexForPoint:cursorAfterScroll]; + + // We do not want to change the insert point relative position from top of visible rect + // We have to calc the distance between insertion point befor/after scrolling to keep the position. + NSRect insertionRectAfterScroll = [self _glyphRectAtIndex:cursorIndexAfterScroll length:1]; + NSPoint relativeInsertionPointAfterScroll = SubPoint(insertionRectAfterScroll.origin, scrollPoint); + CGFloat maxScrollY = NSHeight([scrollView.documentView frame]) - NSHeight(visibleRect); + + scrollPoint.y += relativeInsertionPointAfterScroll.y - relativeInsertionPoint.y; + if (scrollPoint.y > maxScrollY) { + // Prohibit scroll beyond the bounds of document + scrollPoint.y = maxScrollY; + } else if (scrollPoint.y < 0.0) { + scrollPoint.y = 0.0; + } + + [[scrollView contentView] scrollToPoint:scrollPoint]; + [scrollView reflectScrolledClipView:[scrollView contentView]]; + + cursorIndexAfterScroll = [buffer firstNonblankInLineAtIndex:cursorIndexAfterScroll allowEOL:YES]; + [self _moveCursor:cursorIndexAfterScroll preserveColumn:NO]; + [self _syncState]; +} + +- (void)scrollPageForward:(NSUInteger)count +{ + [self _scroll:1.0 count:count]; +} + +- (void)scrollPageBackward:(NSUInteger)count +{ + [self _scroll:-1.0 count:count]; +} + +- (void)scrollHalfPageForward:(NSUInteger)count +{ + [self _scroll:0.5 count:count]; +} + +- (void)scrollHalfPageBackward:(NSUInteger)count +{ + [self _scroll:-0.5 count:count]; +} + +- (void)_scrollCommon_moveCursorPos:(NSUInteger)lineNumber + ratio:(CGFloat)ratio + firstNonblank:(BOOL)fnb +{ + XVimBuffer *buffer = self.buffer; + NSUInteger pos = _insertionPoint; + + if (lineNumber) { + if ((pos = [buffer indexOfLineNumber:lineNumber]) == NSNotFound) { + pos = buffer.length; + } + } + if (fnb) { + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + [self _moveCursor:pos preserveColumn:NO]; + [self _syncState]; + + NSRect glyphRect = [self _glyphRectAtIndex:_insertionPoint length:0]; + + NSScrollView *scrollView = _textView.enclosingScrollView; + NSClipView *clipView = scrollView.contentView; + + NSPoint point = NSMakePoint(0., NSMaxY(glyphRect)); + CGFloat deltay = ratio * NSHeight(_textView.enclosingScrollView.contentView.bounds); + point.y = point.y > deltay ? point.y - deltay : 0.; + + [clipView scrollToPoint:point]; + [scrollView reflectScrolledClipView:clipView]; +} + +- (void)scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zb / z- +{ + [self _scrollCommon_moveCursorPos:lineNumber ratio:1. firstNonblank:fnb]; +} + +- (void)scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zz / z. +{ + [self _scrollCommon_moveCursorPos:lineNumber ratio:.5 firstNonblank:fnb]; +} + +- (void)scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zt / z +{ + [self _scrollCommon_moveCursorPos:lineNumber ratio:0. firstNonblank:fnb]; +} + +- (void)scrollTo:(NSUInteger)location +{ + // Update: I do not know if we really need Following block. + // It looks that they need it to call ensureLayoutForGlyphRange but do not know when it needed + // What I changed was the way calc "glyphRec". Not its using [self boundingRectForGlyphIndex] which coniders + // text folding when calc the rect. + /* + BOOL isBlankline = + (location == [[self string] length] || isNewline([[self string] characterAtIndex:location])) && + (location == 0 || isNewline([[self string] characterAtIndex:location-1])); + + NSRange characterRange; + characterRange.location = location; + characterRange.length = isBlankline ? 0 : 1; + + // Must call ensureLayoutForGlyphRange: to fix a bug where it will not scroll + // to the appropriate glyph due to non contiguous layout + NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:characterRange actualCharacterRange:NULL]; + [[self layoutManager] ensureLayoutForGlyphRange:NSMakeRange(0, glyphRange.location + glyphRange.length)]; + */ + + NSScrollView *scrollView = _textView.enclosingScrollView; + NSClipView *clipView = scrollView.contentView; + + NSRect glyphRect = [self _glyphRectAtIndex:location length:1]; + CGFloat glyphLeft = NSMinX(glyphRect); + CGFloat glyphRight = NSMaxX(glyphRect); + CGFloat glyphBottom = NSMaxY(glyphRect); + CGFloat glyphTop = NSMinY(glyphRect); NSRect contentRect = clipView.bounds; CGFloat viewLeft = NSMinX(contentRect); @@ -621,4 +2289,111 @@ - (void)scrollTo:(NSUInteger)location [scrollView reflectScrolledClipView:clipView]; } +#pragma mark *** Crap to sort *** + +- (void)xvim_setWrapsLines:(BOOL)wraps +{ +#ifdef __USE_DVTKIT__ + if ([_textView isKindOfClass:[DVTSourceTextView class]]) { + [(DVTSourceTextView *)_textView setWrapsLines:wraps]; + } +#endif +} + +- (void)xvim_hideCompletions +{ +#ifdef __USE_DVTKIT__ + if ([_textView isKindOfClass:[DVTSourceTextView class]]) { + [((DVTSourceTextView *)_textView).completionController hideCompletions]; + } +#endif +} + +#pragma mark Search + +- (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)count + option:(MOTION_OPTION)opt forward:(BOOL)forward +{ + NSTextStorage *ts = _textView.textStorage; + NSRange range = NSMakeRange(NSNotFound,0); + + if (forward) { + range = [ts searchRegexForward:regex from:_insertionPoint count:count option:opt]; + }else{ + range = [ts searchRegexBackward:regex from:_insertionPoint count:count option:opt]; + } + if (range.location != NSNotFound) { + [self scrollTo:range.location]; + [_textView showFindIndicatorForRange:range]; + } +} + +- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt +{ + [self xvim_highlightNextSearchCandidate:regex count:count option:opt forward:YES]; +} + +- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt +{ + [self xvim_highlightNextSearchCandidate:regex count:count option:opt forward:NO]; +} + +- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt +{ + NSAssert( nil != pattern, @"pattern munst not be nil"); + + if (!_needsUpdateFoundRanges) { + return; + } + + NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines; + if (opt & SEARCH_CASEINSENSITIVE) { + r_opts |= NSRegularExpressionCaseInsensitive; + } + + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:r_opts error:&error]; + + if (nil != error) { + [_foundRanges removeAllObjects]; + return; + } + + // Find all the maches + NSString *string = self.buffer.string; + if (string == nil) { + return; + } + + NSArray *matches = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)]; + [_foundRanges setArray:matches]; + + // Clear current highlight. + [self xvim_clearHighlightText]; + // Add yellow highlight + for (NSTextCheckingResult *result in self.foundRanges) { + [_textView.layoutManager addTemporaryAttribute:NSBackgroundColorAttributeName + value:[NSColor yellowColor] forCharacterRange:result.range]; + } + + _needsUpdateFoundRanges = NO; +} + +- (void)xvim_clearHighlightText +{ + if (!_needsUpdateFoundRanges) { + return; + } + + [_textView.layoutManager removeTemporaryAttribute:NSBackgroundColorAttributeName + forCharacterRange:NSMakeRange(0, self.buffer.length)]; + // [self.layoutManager addTemporaryAttribute:NSBackgroundColorAttributeName value:[NSColor clearColor] forCharacterRange:NSMakeRange(0, string.length)]; + _needsUpdateFoundRanges = NO; +} + +- (NSRange)xvim_currentWord:(MOTION_OPTION)opt +{ + return [_textView.textStorage currentWord:_insertionPoint count:1 option:opt|TEXTOBJECT_INNER]; +} + @end diff --git a/XVim/XVimVisualEvaluator.h b/XVim/XVimVisualEvaluator.h index 2cb71cd4..173f9c7e 100644 --- a/XVim/XVimVisualEvaluator.h +++ b/XVim/XVimVisualEvaluator.h @@ -14,7 +14,7 @@ // They only rely on their interface to handle them. @interface XVimVisualEvaluator : XVimMotionEvaluator -- (id)initWithWindow:(XVimWindow*)window mode:(XVIM_VISUAL_MODE)mode; +- (id)initWithWindow:(XVimWindow*)window mode:(XVimVisualMode)mode; - (id)initWithLastVisualStateWithWindow:(XVimWindow *)window; @end diff --git a/XVim/XVimVisualEvaluator.m b/XVim/XVimVisualEvaluator.m index ec0528c4..170eff0f 100644 --- a/XVim/XVimVisualEvaluator.m +++ b/XVim/XVimVisualEvaluator.m @@ -25,7 +25,7 @@ #import "XVimShiftEvaluator.h" #import "XVimSwapCharsEvaluator.h" #import "XVimJoinEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" static NSString* MODE_STRINGS[] = {@"", @"-- VISUAL --", @"-- VISUAL LINE --", @"-- VISUAL BLOCK --"}; @@ -52,7 +52,7 @@ - (id)initWithLastVisualStateWithWindow:(XVimWindow *)window{ return self; } -- (id)initWithWindow:(XVimWindow *)window mode:(XVIM_VISUAL_MODE)mode { +- (id)initWithWindow:(XVimWindow *)window mode:(XVimVisualMode)mode { NSTextView *sourceView = window.currentView.textView; NSUInteger start = [[sourceView.selectedRanges objectAtIndex:0] rangeValue].location; NSUInteger end = NSMaxRange([sourceView.selectedRanges.lastObject rangeValue]); @@ -87,16 +87,20 @@ - (XVIM_MODE)mode{ return XVIM_MODE_VISUAL; } -- (void)becameHandler{ +- (void)becameHandler +{ + XVimView *xview = self.currentView; + [super becameHandler]; if (_initial.mode) { if (XVim.instance.isRepeating) { - [self.sourceView xvim_changeSelectionMode:_initial.mode]; - NSUInteger columns = XVimVisualInfoColumns(&_initial); NSUInteger lines = XVimVisualInfoLines(&_initial); - XVimPosition pos = self.sourceView.insertionPosition; + XVimPosition pos; + + xview.selectionMode = _initial.mode; + pos = xview.insertionPosition; // When repeating we have to set initial selected range if (_initial.mode == XVIM_VISUAL_CHARACTER) { @@ -115,11 +119,11 @@ - (void)becameHandler{ pos.line += lines - 1; } - [self.sourceView xvim_moveToPosition:pos]; + [xview moveCursorToPosition:pos]; } else { - [self.sourceView xvim_moveToPosition:_initial.start]; - [self.sourceView xvim_changeSelectionMode:_initial.mode]; - [self.sourceView xvim_moveToPosition:_initial.end]; + [xview moveCursorToPosition:_initial.start]; + xview.selectionMode = _initial.mode; + [xview moveCursorToPosition:_initial.end]; // TODO: self.sourceView.preservedColumn = _initial.colwant; } if (_initial.end.column == XVimSelectionEOL) { @@ -131,7 +135,7 @@ - (void)becameHandler{ - (void)didEndHandler{ if (!_waitForArgument) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_NONE]; + self.currentView.selectionMode = XVIM_VISUAL_NONE; // TODO: //[[[XVim instance] repeatRegister] setVisualMode:_mode withRange:_operationRange]; } @@ -144,16 +148,7 @@ - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ if (!XVim.instance.isRepeating) { - XVimBuffer *buffer = self.window.currentBuffer; - XVimVisualInfo *vi = &buffer->visualInfo; - - vi->mode = self.sourceView.selectionMode; - vi->end = self.sourceView.insertionPosition; - vi->start = [buffer positionOfIndex:self.sourceView.selectionBegin]; - vi->colwant = self.sourceView.preservedColumn; - if (self.sourceView.selectionToEOL) { - vi->end.column = XVimSelectionEOL; - } + [self.currentView saveVisualInfoForBuffer:self.window.currentBuffer]; } XVimEvaluator *nextEvaluator = [super eval:keyStroke]; @@ -195,7 +190,7 @@ - (XVimEvaluator*)onComplete_ai:(XVimTextObjectEvaluator*)childEvaluator{ } - (XVimEvaluator*)c{ - if (self.sourceView.selectionMode == XVIM_VISUAL_BLOCK) { + if (self.currentView.inBlockMode) { return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_BLOCK_KILL] autorelease]; } XVimDeleteEvaluator* eval = [[[XVimDeleteEvaluator alloc] initWithWindow:self.window insertModeAtCompletion:YES] autorelease]; @@ -203,8 +198,10 @@ - (XVimEvaluator*)c{ } - (XVimEvaluator *)C{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + XVimView *xview = self.currentView; + + if (!xview.inBlockMode) { + xview.selectionMode = XVIM_VISUAL_LINE; return [self c]; } return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_BLOCK_KILL_EOL] autorelease]; @@ -230,8 +227,10 @@ - (XVimEvaluator *)DEL{ } - (XVimEvaluator*)D{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + XVimView *xview = self.currentView; + + if (!xview.inBlockMode) { + xview.selectionMode = XVIM_VISUAL_LINE; return [self d]; } @@ -260,7 +259,7 @@ - (XVimEvaluator *)g_completed:(XVimEvaluator *)childEvaluator{ } - (XVimEvaluator*)I{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { + if (!self.currentView.inBlockMode) { return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_BEFORE_FIRST_NONBLANK] autorelease]; } return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_DEFAULT] autorelease]; @@ -285,19 +284,18 @@ - (XVimEvaluator*)m_completed:(XVimEvaluator*)childEvaluator{ } - (XVimEvaluator *)o{ - [self.sourceView xvim_selectSwapEndsOnSameLine:NO]; + [self.currentView selectSwapCorners:NO]; return self; } - (XVimEvaluator *)O{ - [self.sourceView xvim_selectSwapEndsOnSameLine:YES]; + [self.currentView selectSwapCorners:YES]; return self; } - (XVimEvaluator*)p{ - NSTextView* view = [self sourceView]; XVimRegister* reg = [[[XVim instance] registerManager] registerByName:self.yankRegister]; - [view xvim_put:reg.string withType:reg.type afterCursor:YES count:[self numericArg]]; + [self.currentView doPut:reg.string withType:reg.type afterCursor:YES count:[self numericArg]]; return nil; } @@ -321,8 +319,10 @@ - (XVimEvaluator*)s{ } - (XVimEvaluator *)S{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + XVimView *xview = self.currentView; + + if (!xview.inBlockMode) { + xview.selectionMode = XVIM_VISUAL_LINE; } [self.window errorMessage:@"{Visual}S not implemented yet" ringBell:NO]; return self; @@ -344,29 +344,32 @@ - (XVimEvaluator*)C_u{ } - (XVimEvaluator*)v{ - NSTextView *view = [self sourceView]; - if( view.selectionMode == XVIM_VISUAL_CHARACTER ){ - return [self ESC]; + XVimView *xview = self.currentView; + + if (xview.selectionMode == XVIM_VISUAL_CHARACTER) { + return [self ESC]; } - [view xvim_changeSelectionMode:XVIM_VISUAL_CHARACTER]; + xview.selectionMode = XVIM_VISUAL_CHARACTER; return self; } - (XVimEvaluator*)V{ - NSTextView *view = [self sourceView]; - if( view.selectionMode == XVIM_VISUAL_LINE){ + XVimView *xview = self.currentView; + + if (xview.selectionMode == XVIM_VISUAL_LINE) { return [self ESC]; } - [view xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + xview.selectionMode = XVIM_VISUAL_LINE; return self; } - (XVimEvaluator*)C_v{ - NSTextView *view = [self sourceView]; - if( view.selectionMode == XVIM_VISUAL_BLOCK){ + XVimView *xview = self.currentView; + + if (xview.selectionMode == XVIM_VISUAL_BLOCK) { return [self ESC]; } - [view xvim_changeSelectionMode:XVIM_VISUAL_BLOCK]; + xview.selectionMode = XVIM_VISUAL_BLOCK; return self; } @@ -375,20 +378,24 @@ - (XVimEvaluator*)x{ } - (XVimEvaluator*)X{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + XVimView *xview = self.currentView; + + if (!xview.inBlockMode) { + xview.selectionMode = XVIM_VISUAL_LINE; } return [self d]; } - (XVimEvaluator*)y{ - [[self sourceView] xvim_yank:nil]; + [self.currentView doYank:nil]; return nil; } - (XVimEvaluator*)Y{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + XVimView *xview = self.currentView; + + if (!xview.inBlockMode) { + xview.selectionMode = XVIM_VISUAL_LINE; } return [self y]; } @@ -442,7 +449,7 @@ - (XVimEvaluator*)EQUAL{ } - (XVimEvaluator*)ESC{ - [[self sourceView] xvim_changeSelectionMode:XVIM_VISUAL_NONE]; + self.currentView.selectionMode = XVIM_VISUAL_NONE; return nil; } @@ -467,9 +474,8 @@ - (XVimEvaluator*)COLON{ { XVimExCommand *excmd = [[XVim instance] excmd]; [excmd executeCommand:command inWindow:self.window]; - - //NSTextView *sourceView = [window sourceView]; - [[self sourceView] xvim_changeSelectionMode:XVIM_VISUAL_NONE]; + + self.currentView.selectionMode = XVIM_VISUAL_NONE; return nil; } onKeyPress:nil]; @@ -571,7 +577,7 @@ - (XVimEvaluator*)TILDE{ - (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ if(!XVim.instance.isRepeating){ - [[self sourceView] xvim_move:motion]; + [self.currentView moveCursorWithMotion:motion]; [self resetNumericArg]; } [self.argumentString setString:@""]; diff --git a/XVim/XVimWindow.m b/XVim/XVimWindow.m index 2ed41ba9..e1542323 100644 --- a/XVim/XVimWindow.m +++ b/XVim/XVimWindow.m @@ -8,6 +8,7 @@ #import #import "XVimWindow.h" #import "XVim.h" +#import "XVimView.h" #import "XVimUtil.h" #import "XVimNormalEvaluator.h" #import "XVimVisualEvaluator.h" @@ -20,7 +21,6 @@ #import "IDEEditor+XVim.h" #import "IDEEditorArea+XVim.h" #import "DVTSourceTextScrollView+XVim.h" -#import "NSTextView+VimOperation.h" #import "NSEvent+VimHelper.h" #import "XVimCommandLineEvaluator.h" #import "XVimInsertEvaluator.h" @@ -119,7 +119,7 @@ - (XVimView *)currentView - (XVimBuffer *)currentBuffer { - return self.currentView.textView.textStorage.xvim_buffer; + return self.currentView.buffer; } - (void)dealloc @@ -338,7 +338,8 @@ - (void)handleKeyStroke:(XVimKeyStroke *)keyStroke onStack:(NSMutableArray *)eva - (void)syncEvaluatorStack { - BOOL needsVisual = (self.sourceView.selectedRange.length != 0); + XVimView *xview = self.currentView; + BOOL needsVisual = (xview.textView.selectedRange.length != 0); if (!needsVisual && [self.currentEvaluator isKindOfClass:[XVimInsertEvaluator class]]) { return; @@ -352,7 +353,7 @@ - (void)syncEvaluatorStack // FIXME:JAS this doesn't work if v is remaped (yeah I know it's silly but...) [self handleOneXVimString:@"v"]; } else { - [self.sourceView xvim_adjustCursorPosition]; + [xview adjustCursorPosition]; } [_commandLine setModeString:[self.currentEvaluator.modeString stringByAppendingString:_staticString]]; } diff --git a/XVim/XVimYankEvaluator.m b/XVim/XVimYankEvaluator.m index 199a230e..051f66f5 100644 --- a/XVim/XVimYankEvaluator.m +++ b/XVim/XVimYankEvaluator.m @@ -29,7 +29,7 @@ - (XVimEvaluator*)UNDERSCORE{ } - (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ - [[self sourceView] xvim_yank:motion]; + [self.currentView doYank:motion]; return nil; } @end From 55acaf3613c08173b52f326ad54f0987a5d7fb71 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Sat, 23 Nov 2013 17:06:40 +0100 Subject: [PATCH 28/44] Force synthesizing properties I had a bug because it's not on by default in XVim and a property was automatically synthesized because I didn't implement the selector and had no warning. --- XVim.xcodeproj/project.pbxproj | 2 ++ XVim/Test/XVimTestCase.m | 10 ++++++++++ XVim/XVim.m | 16 ++++++++++++++++ XVim/XVimCommandLineEvaluator.m | 2 ++ XVim/XVimEvaluator.m | 7 +++++++ XVim/XVimGMotionEvaluator.m | 1 + XVim/XVimKeyStroke.m | 1 + XVim/XVimKeymap.m | 11 +++++++++++ XVim/XVimMotion.h | 4 ++-- XVim/XVimMotion.m | 12 +++++++++++- XVim/XVimOptions.m | 14 ++++++++++++++ XVim/XVimRecordingEvaluator.m | 2 ++ XVim/XVimRegister.m | 5 +++++ XVim/XVimRegisterEvaluator.m | 1 + XVim/XVimSearch.m | 8 ++++++++ XVim/XVimTester.m | 1 + 16 files changed, 94 insertions(+), 3 deletions(-) diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index d448a4a3..cdd9c3cf 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -1044,6 +1044,7 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1088,6 +1089,7 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; COPY_PHASE_STRIP = YES; diff --git a/XVim/Test/XVimTestCase.m b/XVim/Test/XVimTestCase.m index 6a5502d6..19778315 100644 --- a/XVim/Test/XVimTestCase.m +++ b/XVim/Test/XVimTestCase.m @@ -16,6 +16,15 @@ #import "XVimView.h" @implementation XVimTestCase +@synthesize initialText; +@synthesize initialRange; +@synthesize input; +@synthesize expectedText; +@synthesize expectedRange; +@synthesize description; +@synthesize message; +@synthesize success; + + (XVimTestCase*)testCaseWithInitialText:(NSString*)it initialRange:(NSRange)ir input:(NSString*)in @@ -79,6 +88,7 @@ - (BOOL)assert{ } NSRange resultRange = [XVimLastActiveSourceView() selectedRange]; + if( self.expectedRange.location != resultRange.location || self.expectedRange.length != resultRange.length ){ diff --git a/XVim/XVim.m b/XVim/XVim.m index d629d784..a08181ae 100644 --- a/XVim/XVim.m +++ b/XVim/XVim.m @@ -57,6 +57,22 @@ - (void)parseRcFile; @implementation XVim @synthesize disabled = _disabled; +@synthesize options = _options; +@synthesize searcher = _searcher; +@synthesize lastCharacterSearchMotion = _lastCharacterSearchMotion; +@synthesize excmd = _excmd; +@synthesize marks = _marks; +@synthesize testRunner = _testRunner; +@synthesize registerManager = _registerManager; +@synthesize exCommandHistory = _exCommandHistory; +@synthesize searchHistory = _searchHistory; +@synthesize lastOperationCommands = _lastOperationCommands; +@synthesize isRepeating = _isRepeating; +@synthesize tempRepeatRegister = _tempRepeatRegister; +@synthesize lastPlaybackRegister = _lastPlaybackRegister; +@synthesize document = _document; +@synthesize isExecuting = _isExecuting; + // For reverse engineering purpose. +(void)receiveNotification:(NSNotification*)notification{ diff --git a/XVim/XVimCommandLineEvaluator.m b/XVim/XVimCommandLineEvaluator.m index 30c0df21..95f2d49c 100644 --- a/XVim/XVimCommandLineEvaluator.m +++ b/XVim/XVimCommandLineEvaluator.m @@ -28,6 +28,8 @@ @interface XVimCommandLineEvaluator() { @end @implementation XVimCommandLineEvaluator +@synthesize evalutionResult = _evalutionResult; +@synthesize lastTextView = _lastTextView; - (id)initWithWindow:(XVimWindow *)window firstLetter:(NSString*)firstLetter diff --git a/XVim/XVimEvaluator.m b/XVim/XVimEvaluator.m index d8e0e736..c2162d39 100644 --- a/XVim/XVimEvaluator.m +++ b/XVim/XVimEvaluator.m @@ -26,6 +26,13 @@ static XVimEvaluator *_popEvaluator = nil; @implementation XVimEvaluator +@synthesize window = _window; +@synthesize parent = _parent; +@synthesize numericArg = _numericArg; +@synthesize numericMode = _numericMode; +@synthesize argumentString = _argumentString; +@synthesize yankRegister = _yankRegister; +@synthesize onChildCompleteHandler = _onChildCompleteHandler; + (void)initialize { diff --git a/XVim/XVimGMotionEvaluator.m b/XVim/XVimGMotionEvaluator.m index 3c918e67..beb25bef 100644 --- a/XVim/XVimGMotionEvaluator.m +++ b/XVim/XVimGMotionEvaluator.m @@ -16,6 +16,7 @@ #import "Logger.h" @implementation XVimGMotionEvaluator +@synthesize motion, key; - (XVimEvaluator*)eval:(XVimKeyStroke *)keyStroke{ self.key = keyStroke; diff --git a/XVim/XVimKeyStroke.m b/XVim/XVimKeyStroke.m index cc0fa019..1172cde3 100644 --- a/XVim/XVimKeyStroke.m +++ b/XVim/XVimKeyStroke.m @@ -517,6 +517,7 @@ - (XVimString*)toXVimString{ @implementation XVimKeyStroke +@synthesize character, modifier; - (id)initWithCharacter:(unichar)c modifier:(unsigned char)mod{ if( self = [super init] ){ diff --git a/XVim/XVimKeymap.m b/XVim/XVimKeymap.m index 4e686e2f..1e1b4e49 100644 --- a/XVim/XVimKeymap.m +++ b/XVim/XVimKeymap.m @@ -22,6 +22,10 @@ - (BOOL)hasChild; @end @implementation XVimKeymapNode +@synthesize dict; +@synthesize remap; +@synthesize target; + - (id)init{ if (self = [super init]){ self.dict = [[[NSMutableDictionary alloc] init] autorelease]; @@ -45,6 +49,11 @@ - (BOOL)hasChild{ @implementation XVimKeymapContext +@synthesize inputKeys; +@synthesize lastMappedKeys; +@synthesize lastMappedNode; +@synthesize node; + - (id)init { if (self = [super init]){ self.inputKeys = [[[NSMutableString alloc] init] autorelease]; @@ -84,6 +93,8 @@ @interface XVimKeymap() @end @implementation XVimKeymap +@synthesize root; + - (id)init{ self = [super init]; if (self) { diff --git a/XVim/XVimMotion.h b/XVim/XVimMotion.h index a3bd7c29..649d40fc 100644 --- a/XVim/XVimMotion.h +++ b/XVim/XVimMotion.h @@ -85,12 +85,12 @@ typedef enum _MOTION{ @property MOTION_TYPE type; @property MOTION_OPTION option; @property NSUInteger count; -@property NSInteger scount; +@property (readonly) NSInteger scount; @property NSUInteger line; @property NSUInteger column; @property NSUInteger position; @property unichar character; -@property(strong) NSString* regex; +@property (strong) NSString* regex; @property XVimMotionInfo* info; - (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(MOTION_OPTION)option count:(NSUInteger)count; diff --git a/XVim/XVimMotion.m b/XVim/XVimMotion.m index 489003af..79d01ead 100644 --- a/XVim/XVimMotion.m +++ b/XVim/XVimMotion.m @@ -9,10 +9,20 @@ #import "XVimMotion.h" @implementation XVimMotion +@synthesize motion = _motion; +@synthesize type = _type; +@synthesize option = _option; +@synthesize count = _count; +@synthesize line = _line; +@synthesize column = _column; +@synthesize position = _position; +@synthesize character = _character; +@synthesize regex = _regex; +@synthesize info = _info; - (NSInteger)scount { - return (NSInteger)self.count; + return (NSInteger)_count; } - (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(MOTION_OPTION)option count:(NSUInteger)count{ diff --git a/XVim/XVimOptions.m b/XVim/XVimOptions.m index d2e67034..5bb7fce8 100644 --- a/XVim/XVimOptions.m +++ b/XVim/XVimOptions.m @@ -16,6 +16,20 @@ @interface XVimOptions() { @end @implementation XVimOptions +@synthesize ignorecase = _ignorecase; +@synthesize wrapscan = _wrapscan; +@synthesize errorbells = _errorbells; +@synthesize incsearch = _incsearch; +@synthesize gdefault = _gdefault; +@synthesize smartcase = _smartcase; +@synthesize debug = _debug; +@synthesize hlsearch = _hlsearch; +@synthesize number = _number; +@synthesize clipboard = _clipboard; +@synthesize guioptions = _guioptions; +@synthesize timeoutlen = _timeoutlen; +@synthesize laststatus = _laststatus; +@synthesize vimregex = _vimregex; - (id)init{ if( self = [super init] ){ diff --git a/XVim/XVimRecordingEvaluator.m b/XVim/XVimRecordingEvaluator.m index 86bac170..ca338223 100644 --- a/XVim/XVimRecordingEvaluator.m +++ b/XVim/XVimRecordingEvaluator.m @@ -18,6 +18,8 @@ @interface XVimRecordingEvaluator() @end @implementation XVimRecordingEvaluator +@synthesize reg = _reg, evaluatorStack = _evaluatorStack; + - (id)initWithWindow:(XVimWindow *)window withRegister:(NSString*)reg{ if( self = [super initWithWindow:window] ){ self.evaluatorStack = [[[NSMutableArray alloc] init] autorelease]; diff --git a/XVim/XVimRegister.m b/XVim/XVimRegister.m index f42eb5eb..c1362318 100644 --- a/XVim/XVimRegister.m +++ b/XVim/XVimRegister.m @@ -19,6 +19,7 @@ @interface XVimRegister() @end @implementation XVimRegister +@synthesize type = _type, string = _string; - (id)init{ if(self = [super init]){ @@ -124,6 +125,10 @@ @interface XVimRegisterManager() @implementation XVimRegisterManager +@synthesize registers = _registers; +@synthesize recordingRegister = _recordingRegister; +@synthesize recordingRegisterName = _recordingRegisterName; +@synthesize lastExecutedRegister = _lastExecutedRegister; static const NSString* s_enum_registers = @"\"0123456789abcdefghijklmnopqrstuvwxyz-*.:%/+~"; diff --git a/XVim/XVimRegisterEvaluator.m b/XVim/XVimRegisterEvaluator.m index 8b44ddd5..2067e80c 100644 --- a/XVim/XVimRegisterEvaluator.m +++ b/XVim/XVimRegisterEvaluator.m @@ -19,6 +19,7 @@ @interface XVimRegisterEvaluator() { @end @implementation XVimRegisterEvaluator +@synthesize reg; - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { return [keymapProvider keymapForMode:XVIM_MODE_NONE]; diff --git a/XVim/XVimSearch.m b/XVim/XVimSearch.m index a70707dd..73f7fc0b 100644 --- a/XVim/XVimSearch.m +++ b/XVim/XVimSearch.m @@ -17,6 +17,14 @@ #import "NSTextStorage+VimOperation.h" @implementation XVimSearch +@synthesize lastSearchBackword = _lastSearchBackword; +@synthesize lastSearchCase = _lastSearchCase; +@synthesize lastSearchCmd = _lastSearchCmd; +@synthesize lastSearchString = _lastSearchString; +@synthesize lastSearchDisplayString = _lastSearchDisplayString; +@synthesize lastReplacementString = _lastReplacementString; +@synthesize matchStart = _matchStart; +@synthesize matchEnd = _matchEnd; - (id)init { if( self = [super init] ){ diff --git a/XVim/XVimTester.m b/XVim/XVimTester.m index 9706ad39..243746d9 100644 --- a/XVim/XVimTester.m +++ b/XVim/XVimTester.m @@ -79,6 +79,7 @@ @interface XVimTester(){ @implementation XVimTester +@synthesize testCases; - (id)init{ if( self = [super init] ){ From f49d75de100718b8369399c6e553808ebaefcb73 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Mon, 25 Nov 2013 07:31:48 +0100 Subject: [PATCH 29/44] Hunt of DVTKIT: Move -_indentCharacterRange into XVimTextStoring And implement all the forwarders all the way down. --- XVim/DVTTextStorage+XVimTextStoring.m | 5 ++++ XVim/XVimBuffer.h | 2 ++ XVim/XVimBuffer.m | 7 +++++ XVim/XVimTextStoring.h | 16 ++++++++--- XVim/XVimView.m | 40 ++++++++------------------- 5 files changed, 38 insertions(+), 32 deletions(-) diff --git a/XVim/DVTTextStorage+XVimTextStoring.m b/XVim/DVTTextStorage+XVimTextStoring.m index f3c13291..cf59f040 100644 --- a/XVim/DVTTextStorage+XVimTextStoring.m +++ b/XVim/DVTTextStorage+XVimTextStoring.m @@ -70,6 +70,11 @@ - (NSUInteger)xvim_lineNumberAtIndex:(NSUInteger)index return [self lineRangeForCharacterRange:NSMakeRange(index, 0)].location + 1; } +- (void)xvim_indentCharacterRange:(NSRange)range buffer:(XVimBuffer *)buffer +{ + [self indentCharacterRange:range undoManager:buffer.undoManager]; +} + @end #if XVIM_XCODE_VERSION != 5 diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 7027bfff..4e67f470 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -313,4 +313,6 @@ // May return NSNotFound - (NSUInteger)incrementNumberAtIndex:(NSUInteger)index by:(int64_t)offset; +- (void)indentCharacterRange:(NSRange)range; + @end diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index 44f23fcd..c6c16db5 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -769,4 +769,11 @@ - (NSUInteger)incrementNumberAtIndex:(NSUInteger)index by:(int64_t)offset return index; } +- (void)indentCharacterRange:(NSRange)range +{ + if ([_textStorage respondsToSelector:@selector(xvim_indentCharacterRange:buffer:)]) { + [(id)_textStorage xvim_indentCharacterRange:range buffer:self]; + } +} + @end diff --git a/XVim/XVimTextStoring.h b/XVim/XVimTextStoring.h index 6ee72119..6fd89740 100644 --- a/XVim/XVimTextStoring.h +++ b/XVim/XVimTextStoring.h @@ -8,6 +8,8 @@ #import +@class XVimBuffer; + /** @brief Protocol that can be implemented by your NSTextStorage. * * Implementing it will likely boost XVimBuffer performance significantly @@ -19,14 +21,12 @@ @optional +#pragma mark *** Content & Lines access *** + @property (nonatomic, readonly) NSString *xvim_string; @property (nonatomic, readonly) NSUInteger xvim_numberOfLines; -@property (nonatomic, readonly) NSUInteger xvim_tabWidth; - -@property (nonatomic, readonly) NSUInteger xvim_indentWidth; - /** @brief returns the index range for the given line number * * @param[in] num @@ -57,4 +57,12 @@ */ - (NSUInteger)xvim_lineNumberAtIndex:(NSUInteger)index; +#pragma mark *** Indent *** + +@property (nonatomic, readonly) NSUInteger xvim_tabWidth; + +@property (nonatomic, readonly) NSUInteger xvim_indentWidth; + +- (void)xvim_indentCharacterRange:(NSRange)range buffer:(XVimBuffer *)buffer; + @end diff --git a/XVim/XVimView.m b/XVim/XVimView.m index 84663f7b..f889684d 100644 --- a/XVim/XVimView.m +++ b/XVim/XVimView.m @@ -1656,31 +1656,7 @@ - (void)doJoin:(NSUInteger)count addSpace:(BOOL)addSpace [self _syncState]; } -- (void)_indentCharacterRange:(NSRange)range -{ - NSTextStorage *ts = _textView.textStorage; - XVimBuffer *buffer = self.buffer; - -#ifdef __USE_DVTKIT__ -#ifdef __XCODE5__ - if ([ts isKindOfClass:[DVTTextStorage class]]) { - [(DVTTextStorage *)ts indentCharacterRange:range undoManager:buffer.undoManager]; - } - return; -#else - if ([self.textStorage isKindOfClass:[DVTSourceTextStorage class]]) { - [(DVTSourceTextStorage *)ts indentCharacterRange:range undoManager:buffer.undoManager]; - } - return; -#endif -#else -#error You must implement here -#endif - - NSAssert(NO, @"You must implement here if you dont use this caregory with DVTSourceTextView"); -} - -- (void)doFilter:(XVimMotion*)motion +- (void)doFilter:(XVimMotion *)motion { XVimBuffer *buffer = self.buffer; @@ -1688,23 +1664,31 @@ - (void)doFilter:(XVimMotion*)motion return ; } - NSUInteger insertionAfterFilter = _insertionPoint; NSRange filterRange; + NSUInteger line, pos; + if (self.selectionMode == XVIM_VISUAL_NONE) { XVimRange to = [self _getMotionRange:_insertionPoint motion:motion]; if (to.end == NSNotFound) { return; } filterRange = [self _getOperationRange:to type:LINEWISE]; + line = [buffer lineNumberAtIndex:filterRange.location]; } else { XVimRange lines = [self _selectedLines]; NSUInteger from = [buffer indexOfLineNumber:lines.begin]; NSUInteger to = [buffer indexOfLineNumber:lines.end]; + filterRange = [self _getOperationRange:XVimMakeRange(from, to) type:LINEWISE]; + line = lines.begin; } - [self _indentCharacterRange:filterRange]; - [self _moveCursor:insertionAfterFilter preserveColumn:NO]; + [buffer indentCharacterRange:filterRange]; + + pos = [buffer indexOfLineNumber:line]; + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + + [self _moveCursor:pos preserveColumn:NO]; self.selectionMode = XVIM_VISUAL_NONE; [self _syncState]; } From 45b5e26db06dd1a1cc24add017caf39c3d0eb31f Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Mon, 25 Nov 2013 08:06:09 +0100 Subject: [PATCH 30/44] Begin to remove some things from the NSTextStorage category. Hide some -is* from the NSTextStorage category, move isLastLine as isIndexOnLastLine on XVimBuffer. --- XVim.xcodeproj/project.pbxproj | 8 +++ XVim/NSCharacterSet+XVimAdditions.h | 16 ++++++ XVim/NSCharacterSet+XVimAdditions.m | 41 ++++++++++++++ XVim/NSString+VimHelper.h | 9 --- XVim/NSString+VimHelper.m | 28 ---------- XVim/NSTextStorage+VimOperation.h | 37 ------------ XVim/NSTextStorage+VimOperation.m | 31 +++------- XVim/XVimBuffer.h | 23 ++++++-- XVim/XVimBuffer.m | 87 ++++++++++++++++++----------- XVim/XVimExCommand.m | 2 +- XVim/XVimStringBuffer.h | 2 +- XVim/XVimView.m | 18 +++--- 12 files changed, 158 insertions(+), 144 deletions(-) create mode 100644 XVim/NSCharacterSet+XVimAdditions.h create mode 100644 XVim/NSCharacterSet+XVimAdditions.m diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index cdd9c3cf..d69394cc 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -14,6 +14,8 @@ 6E2C3930183E4BA20056E30F /* XVimView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2C392E183E4BA20056E30F /* XVimView.m */; }; 6E6865D618390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; 6E6865D718390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; + 6EC4A31E1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EC4A31D1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m */; }; + 6EC4A31F1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EC4A31D1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m */; }; 6EE2579E183F81C3006E4E1A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A24782C414D6F6DC003B6433 /* InfoPlist.strings */; }; 6EF220D6183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */; }; 6EF220D7183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */; }; @@ -200,6 +202,8 @@ 6E6865D418390702008D5FBB /* XVimUndo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimUndo.h; path = XVim/XVimUndo.h; sourceTree = SOURCE_ROOT; }; 6E6865D518390702008D5FBB /* XVimUndo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimUndo.m; path = XVim/XVimUndo.m; sourceTree = SOURCE_ROOT; }; 6EAFF976183777A6003EADAE /* XVimStringBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimStringBuffer.h; path = XVim/XVimStringBuffer.h; sourceTree = SOURCE_ROOT; }; + 6EC4A31C1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCharacterSet+XVimAdditions.h"; path = "XVim/NSCharacterSet+XVimAdditions.h"; sourceTree = SOURCE_ROOT; }; + 6EC4A31D1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSCharacterSet+XVimAdditions.m"; path = "XVim/NSCharacterSet+XVimAdditions.m"; sourceTree = SOURCE_ROOT; }; 6EF220D4183E2A1400B814B8 /* NSObject+XVimAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+XVimAdditions.h"; path = "XVim/NSObject+XVimAdditions.h"; sourceTree = SOURCE_ROOT; }; 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+XVimAdditions.m"; path = "XVim/NSObject+XVimAdditions.m"; sourceTree = SOURCE_ROOT; }; A2126B7316FB30B0000BE21C /* XVimMark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimMark.h; path = XVim/XVimMark.h; sourceTree = SOURCE_ROOT; }; @@ -413,6 +417,8 @@ children = ( 6EF220D4183E2A1400B814B8 /* NSObject+XVimAdditions.h */, 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */, + 6EC4A31C1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.h */, + 6EC4A31D1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m */, ); name = Foundation; sourceTree = ""; @@ -845,6 +851,7 @@ A28F423617EEDBC200A3F7AE /* XVimZEvaluator.m in Sources */, A28F423717EEDBC200A3F7AE /* XVimEqualEvaluator.m in Sources */, A28F423817EEDBC200A3F7AE /* XVimRegister.m in Sources */, + 6EC4A31F1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m in Sources */, A28F423917EEDBC200A3F7AE /* XVimRegisterEvaluator.m in Sources */, A28F423A17EEDBC200A3F7AE /* XVimOperatorEvaluator.m in Sources */, A28F423B17EEDBC200A3F7AE /* XVimKeyStroke.m in Sources */, @@ -919,6 +926,7 @@ A28D42F514FE87AF004BC121 /* XVimGMotionEvaluator.m in Sources */, A28D42F814FE8E2A004BC121 /* XVimInsertEvaluator.m in Sources */, A2FF17D11502B292003FE648 /* XVimMotionEvaluator.m in Sources */, + 6EC4A31E1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m in Sources */, F1F2426D15041A9B00F706A4 /* XVimZEvaluator.m in Sources */, F1C1E515150475ED0005C1CB /* XVimEqualEvaluator.m in Sources */, F17D013B150861DC00A8111B /* XVimRegister.m in Sources */, diff --git a/XVim/NSCharacterSet+XVimAdditions.h b/XVim/NSCharacterSet+XVimAdditions.h new file mode 100644 index 00000000..d2ae7cd4 --- /dev/null +++ b/XVim/NSCharacterSet+XVimAdditions.h @@ -0,0 +1,16 @@ +// +// NSCharacterSet+XVimAdditions.h +// XVim +// +// Created by John AppleSeed on 25/11/13. +// +// + +#import + +@interface NSCharacterSet (XVimAdditions) + ++ (NSCharacterSet *)xvim_octDigitsCharacterSet; ++ (NSCharacterSet *)xvim_hexDigitsCharacterSet; + +@end diff --git a/XVim/NSCharacterSet+XVimAdditions.m b/XVim/NSCharacterSet+XVimAdditions.m new file mode 100644 index 00000000..c8288a74 --- /dev/null +++ b/XVim/NSCharacterSet+XVimAdditions.m @@ -0,0 +1,41 @@ +// +// NSCharacterSet+XVimAdditions.m +// XVim +// +// Created by John AppleSeed on 25/11/13. +// +// + +#import "NSCharacterSet+XVimAdditions.h" + +@implementation NSCharacterSet (XVimAdditions) + +static NSCharacterSet *_xvimHexDigits; +static NSCharacterSet *_xvimOctDigits; + +NS_INLINE void _xvim_init_charsets(void) +{ + static dispatch_once_t once; + dispatch_once(&once, ^{ +#define CS(s) [[NSCharacterSet characterSetWithCharactersInString:s] retain] + + _xvimHexDigits = CS(@"0123456789abcdefABCDEF"); + _xvimOctDigits = CS(@"01234567"); + +#undef CS + }); +} + ++ (NSCharacterSet *)xvim_octDigitsCharacterSet +{ + _xvim_init_charsets(); + return _xvimOctDigits; +} + ++ (NSCharacterSet *)xvim_hexDigitsCharacterSet +{ + _xvim_init_charsets(); + return _xvimHexDigits; +} + +@end diff --git a/XVim/NSString+VimHelper.h b/XVim/NSString+VimHelper.h index 8b5b55e8..0468e001 100644 --- a/XVim/NSString+VimHelper.h +++ b/XVim/NSString+VimHelper.h @@ -9,8 +9,6 @@ #import BOOL isDigit(unichar ch); -BOOL isOctDigit(unichar ch); -BOOL isHexDigit(unichar ch); BOOL isAlpha(unichar ch); BOOL isDelimeter(unichar ch); BOOL isWhitespace(unichar ch); @@ -19,13 +17,6 @@ BOOL isNonblank(unichar ch); BOOL isKeyword(unichar ch); @interface NSString (VimHelper) -- (BOOL) isDigit:(NSUInteger)index; -- (BOOL) isOctDigit:(NSUInteger)index; -- (BOOL) isHexDigit:(NSUInteger)index; -- (BOOL) isAlpha:(NSUInteger)index; -- (BOOL) isDelimeter:(NSUInteger)index; -- (BOOL) isNewline:(NSUInteger)index; -- (BOOL) isKeyword:(NSUInteger)index; /** * Convert Vim regex to ICU regex. diff --git a/XVim/NSString+VimHelper.m b/XVim/NSString+VimHelper.m index 97ebdb0b..065fa52c 100644 --- a/XVim/NSString+VimHelper.m +++ b/XVim/NSString+VimHelper.m @@ -11,8 +11,6 @@ // support functions // ///////////////////////// BOOL isDigit(unichar ch) { return ch >= '0' && ch <= '9'; } -BOOL isOctDigit(unichar ch) { return ch >= '0' && ch <= '7'; } -BOOL isHexDigit(unichar ch) { return isDigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); } BOOL isWhitespace(unichar ch) { return [[NSCharacterSet whitespaceCharacterSet] characterIsMember:ch]; } BOOL isNewline(unichar ch) { return [[NSCharacterSet newlineCharacterSet] characterIsMember:ch]; } BOOL isAlpha(unichar ch) { @@ -44,32 +42,6 @@ BOOL isKeyword(unichar ch){ // same as Vim's 'iskeyword' except that Vim's one i }; @implementation NSString (VimHelper) -- (BOOL) isDigit:(NSUInteger)index{ - return isDigit([self characterAtIndex:index]); -} - -- (BOOL) isOctDigit:(NSUInteger)index{ - return isOctDigit([self characterAtIndex:index]); -} - -- (BOOL) isHexDigit:(NSUInteger)index{ - return isHexDigit([self characterAtIndex:index]); -} - -- (BOOL) isAlpha:(NSUInteger)index{ - return isAlpha([self characterAtIndex:index]); -} - -- (BOOL) isDelimeter:(NSUInteger)index{ - return isDelimeter([self characterAtIndex:index]); -} - -- (BOOL) isNewline:(NSUInteger)index{ - return isNewline([self characterAtIndex:index]); } - -- (BOOL) isKeyword:(NSUInteger)index{ - return isKeyword([self characterAtIndex:index]); -} - (NSString*)convertToICURegex:(NSRegularExpressionOptions*)options{ // TODO: These conversion may replace '\\<' into '\\b' diff --git a/XVim/NSTextStorage+VimOperation.h b/XVim/NSTextStorage+VimOperation.h index f8b8ab81..8249b3a0 100644 --- a/XVim/NSTextStorage+VimOperation.h +++ b/XVim/NSTextStorage+VimOperation.h @@ -20,12 +20,6 @@ #pragma mark Definitions -// Determine if the position specified with "index" is EOF. -- (BOOL) isEOF:(NSUInteger)index; - -// Determine if the position specified with "index" is LOL. -- (BOOL) isLOL:(NSUInteger)index; - // Determine if the position specified with "index" is EOL. - (BOOL) isEOL:(NSUInteger)index; @@ -38,31 +32,10 @@ // Determine if the position specified with "index" is white space. - (BOOL) isWhitespace:(NSUInteger)index; -// Determine if the position is on the last line in the document -- (BOOL) isLastLine:(NSUInteger)index; - // Determine if the position is non blank character // EOF is a blank character - (BOOL) isNonblank:(NSUInteger)index; -/** - * Determine if the position specified with "index" is blankline. - * Blankline is one of followings - * - Newline after Newline. Ex. The second '\n' in "abc\n\nabc" is a blankline. First one is not. - * - Newline at begining of the document. - * - EOF after Newline. Ex. The index 4 of "abc\n" is blankline. Note that index 4 is exceed the string length. But the cursor can be there. - * - EOF of 0 sized document. - **/ -- (BOOL) isBlankline:(NSUInteger)index; - -/** - * Determine if the position specified with "index" is valid cursor position in normal mode. - * Valid position is followings - * - Non newline characters. - * - Blankline( including EOF after newline ) - **/ -- (BOOL) isValidCursorPosition:(NSUInteger)index; - #pragma mark Vim operation related methods - (NSUInteger)prev:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; @@ -137,14 +110,4 @@ NSRange xv_current_block(NSString *string, NSUInteger index, NSUInteger repeatCount, BOOL inclusive, char what, char other); NSRange xv_current_quote(NSString *string, NSUInteger index, NSUInteger repeatCount, BOOL inclusive, char what); - - -#pragma mark Conversions -/** - * Returns nearest valid cursor position for normal mode. - * This is usually convert cursor position on newline to previous character since - * a cursor can not be on a newline charaster if its not blankline - **/ -- (NSUInteger)convertToValidCursorPositionForNormalMode:(NSUInteger)index; - @end diff --git a/XVim/NSTextStorage+VimOperation.m b/XVim/NSTextStorage+VimOperation.m index a3a88741..c5dfbfcc 100644 --- a/XVim/NSTextStorage+VimOperation.m +++ b/XVim/NSTextStorage+VimOperation.m @@ -22,11 +22,6 @@ - (BOOL) isEOF:(NSUInteger)index{ return self.length == index; } -- (BOOL) isLOL:(NSUInteger)index{ - ASSERT_VALID_RANGE_WITH_EOF(index); - return [self isEOF:index] == NO && [self isNewline:index] == NO && [self isNewline:index+1]; -} - - (BOOL) isEOL:(NSUInteger)index{ ASSERT_VALID_RANGE_WITH_EOF(index); return [self isNewline:index] || [self isEOF:index]; @@ -61,12 +56,6 @@ - (BOOL) isWhitespace:(NSUInteger)index{ return isWhitespace([self.xvim_buffer.string characterAtIndex:index]); } -- (BOOL) isLastLine:(NSUInteger)index{ - ASSERT_VALID_RANGE_WITH_EOF(index); - XVimBuffer *buffer = self.xvim_buffer; - return [buffer lineNumberAtIndex:index] == [buffer numberOfLines]; -} - - (BOOL) isNonblank:(NSUInteger)index{ ASSERT_VALID_RANGE_WITH_EOF(index); if( [self isEOF:index]){ @@ -75,6 +64,15 @@ - (BOOL) isNonblank:(NSUInteger)index{ return isNonblank([self.xvim_buffer.string characterAtIndex:index]); } + +/** + * Determine if the position specified with "index" is blankline. + * Blankline is one of followings + * - Newline after Newline. Ex. The second '\n' in "abc\n\nabc" is a blankline. First one is not. + * - Newline at begining of the document. + * - EOF after Newline. Ex. The index 4 of "abc\n" is blankline. Note that index 4 is exceed the string length. But the cursor can be there. + * - EOF of 0 sized document. + **/ - (BOOL)isBlankline:(NSUInteger)index { XVimBuffer *buffer = self.xvim_buffer; @@ -1544,15 +1542,4 @@ NSRange xv_current_quote(NSString *string, NSUInteger index, NSUInteger repeatCo #pragma GCC diagnostic pop -#pragma mark Conversions - -- (NSUInteger)convertToValidCursorPositionForNormalMode:(NSUInteger)index{ - ASSERT_VALID_RANGE_WITH_EOF(index); - // If the current cursor position is not valid for normal mode move it. - if( ![self isValidCursorPosition:index] ){ - return index-1; - } - return index; -} - @end diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 4e67f470..7169e670 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -16,11 +16,8 @@ // The following macros asserts the range of index. // WITH_EOF permits the index at EOF position. // WITHOUT_EOF doesn't permit the index at EOF position. -#define ASSERT_VALID_RANGE_WITH_EOF(x) NSAssert( x <= [self length] || [self length] == 0, @"index can not exceed the length of string. TextLength:%lu SpecifiedIndex:%lu", (long)[self length], x) - -// Some methods assume that "index" is at valid cursor position in Normal mode. -// See isValidCursorPosition's description the condition of the valid cursor position. -#define ASSERT_VALID_CURSOR_POS(x) NSAssert( [self isValidCursorPosition:x], @"index can not be invalid cursor position" ) +#define ASSERT_VALID_RANGE_WITH_EOF(x) NSAssert( x <= [self length], \ + @"index can not exceed the length of string. TextLength:%lu SpecifiedIndex:%lu", (long)[self length], x) #else #define ASSERT_VALID_RANGE_WITH_EOF(x) #define ASSERT_VALID_CURSOR_POS(x) @@ -181,6 +178,14 @@ */ - (NSUInteger)lineNumberAtIndex:(NSUInteger)index; +/** @brief returns YES when \a index is on the last line + */ +- (BOOL)isIndexOnLastLine:(NSUInteger)index; + +/** @brief returns YES when \a index is a valid cursor position for normal mode + */ +- (BOOL)isNormalCursorPositionValidAtIndex:(NSUInteger)index; + #pragma mark Converting between Indexes and Line Numbers + Columns /** @brief returns the column number of \a index within the line. @@ -214,6 +219,14 @@ */ - (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column; +/** @brief returns the index for the given line containing \a index and column. + * + * @returns + * If \a column is larger than the number of columns in that line, + * it returns the index of the endOfLine for that line + */ +- (NSUInteger)indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column; + #pragma mark Searching particular positions on the current line /** @brief position of the first character of the line containing \a index. diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index c6c16db5..44e1e28a 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -14,6 +14,7 @@ #import "XVimTextStoring.h" #import "NSString+VimHelper.h" #import "Logger.h" +#import "NSCharacterSet+XVimAdditions.h" static char const * const XVIM_KEY_BUFFER = "xvim_buffer"; @@ -270,6 +271,19 @@ - (NSUInteger)lineNumberAtIndex:(NSUInteger)index return num; } +- (BOOL)isIndexOnLastLine:(NSUInteger)index +{ + return [self endOfLine:index] == self.length; +} + +- (BOOL)isNormalCursorPositionValidAtIndex:(NSUInteger)index +{ + NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; + + return range.length == 0 || index < NSMaxRange(range); +} + + #pragma mark Converting between Indexes and Line Numbers + Columns static NSUInteger xvim_sb_count_columns(xvim_string_buffer_t *sb, NSUInteger tabWidth) @@ -320,11 +334,9 @@ - (NSUInteger)numberOfColumnsInLineAtIndex:(NSUInteger)index return xvim_sb_count_columns(&sb, self.tabWidth); } -- (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column +- (NSUInteger)indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column { - NSUInteger index = [self indexOfLineNumber:num]; - - if (column == 0 || index == NSNotFound) { + if (column == 0) { return index; } @@ -349,6 +361,16 @@ - (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column return xvim_sb_index(&sb); } +- (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column +{ + NSUInteger index = [self indexOfLineNumber:num]; + + if (column == 0 || index == NSNotFound) { + return index; + } + + return [self indexOfLineAtIndex:index column:column]; +} #pragma mark Searching particular positions on the current line @@ -410,8 +432,8 @@ - (NSUInteger)nextNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEO xvim_sb_skip_forward(&sb, [NSCharacterSet whitespaceCharacterSet]); c = xvim_sb_peek(&sb); - if (c == XVimInvalidChar || isNewline(c)) { - return allowEOL ? xvim_sb_index(&sb) : NSNotFound; + if (!allowEOL && (c == XVimInvalidChar || isNewline(c))) { + return NSNotFound; } return xvim_sb_index(&sb); } @@ -664,44 +686,45 @@ - (NSUInteger)shiftLines:(XVimRange)lines column:(NSUInteger)column - (NSRange)_numberAtIndex:(NSUInteger)index { - NSUInteger n_start, n_end; - NSUInteger x_start, x_end; + NSUInteger o_start, n_start, x_start; + NSUInteger o_end, n_end, x_end; NSString *s = self.string; unichar c; BOOL isOctal = YES; + xvim_string_buffer_t sb; - n_start = index; - while (n_start > 0 && [s isDigit:n_start - 1]) { - if (![s isOctDigit:n_start]) { + xvim_sb_init(&sb, s, index, 0, s.length); + xvim_sb_skip_forward(&sb, [NSCharacterSet xvim_octDigitsCharacterSet]); + o_end = xvim_sb_index(&sb); + xvim_sb_skip_forward(&sb, [NSCharacterSet decimalDigitCharacterSet]); + n_end = xvim_sb_index(&sb); + if (o_end != n_end) isOctal = NO; + xvim_sb_skip_forward(&sb, [NSCharacterSet xvim_hexDigitsCharacterSet]); + x_end = xvim_sb_index(&sb); + + xvim_sb_init(&sb, s, index, 0, s.length); + if (isOctal) { + xvim_sb_skip_backward(&sb, [NSCharacterSet xvim_octDigitsCharacterSet]); + o_start = xvim_sb_index(&sb); + c = xvim_sb_peek_prev(&sb); + if (c == '8' || c == '9' || xvim_sb_peek(&sb) != '0') { isOctal = NO; } - n_start--; - } - n_end = index; - while (n_end < s.length && [s isDigit:n_end]) { - if (![s isOctDigit:n_end]) { - isOctal = NO; - } - n_end++; - } - - x_start = n_start; - while (x_start > 0 && [s isHexDigit:x_start - 1]) { - x_start--; - } - x_end = n_end; - while (x_end < s.length && [s isHexDigit:x_end]) { - x_end++; } + xvim_sb_skip_backward(&sb, [NSCharacterSet decimalDigitCharacterSet]); + n_start = xvim_sb_index(&sb); + xvim_sb_skip_backward(&sb, [NSCharacterSet xvim_hexDigitsCharacterSet]); + x_start = xvim_sb_index(&sb); // first deal with Hex: 0xNNNNN // case 1: check for insertion point on the '0' or 'x' if (x_end - x_start == 1) { NSUInteger end = x_end; if (end < s.length && [s characterAtIndex:end] == 'x') { - do { - end++; - } while (end < s.length && [s isHexDigit:end]); + xvim_sb_init(&sb, s, end + 1, 0, s.length); + xvim_sb_skip_forward(&sb, [NSCharacterSet xvim_hexDigitsCharacterSet]); + end = xvim_sb_index(&sb); + if (index < end && end - x_start > 2) { // YAY it's hex for real!!! return NSMakeRange(x_start, end - x_start); @@ -721,7 +744,7 @@ - (NSRange)_numberAtIndex:(NSUInteger)index } // okay it's not hex, if it's not octal, check for leading +/- - if (n_start > 0 && !(isOctal && [s characterAtIndex:n_start] == '0')) { + if (n_start > 0 && !isOctal) { c = [s characterAtIndex:n_start - 1]; if (c == '+' || c == '-') { n_start--; diff --git a/XVim/XVimExCommand.m b/XVim/XVimExCommand.m index d6d9daad..e186c185 100644 --- a/XVim/XVimExCommand.m +++ b/XVim/XVimExCommand.m @@ -818,7 +818,7 @@ - (void)executeCommand:(NSString*)cmd inWindow:(XVimWindow*)window XVimView *xview = window.currentView; // Jump to location - NSUInteger pos = [buffer indexOfLineNumber:exarg.lineBegin column:0]; + NSUInteger pos = [buffer indexOfLineNumber:exarg.lineBegin]; if (NSNotFound == pos) { pos = [buffer startOfLine:buffer.length]; } diff --git a/XVim/XVimStringBuffer.h b/XVim/XVimStringBuffer.h index 013d0b36..630f9024 100644 --- a/XVim/XVimStringBuffer.h +++ b/XVim/XVimStringBuffer.h @@ -92,7 +92,7 @@ NS_INLINE NSRange xvim_sb_range_to_end(xvim_string_buffer_t *sb) NS_INLINE unichar xvim_sb_peek_prev(xvim_string_buffer_t *sb) { - return sb->b_index > 0 ? XVimInvalidChar : sb->buffer[sb->b_index]; + return sb->b_index == 0 ? XVimInvalidChar : sb->buffer[sb->b_index - 1]; } NS_INLINE unichar xvim_sb_peek(xvim_string_buffer_t *sb) diff --git a/XVim/XVimView.m b/XVim/XVimView.m index f889684d..8da51cf1 100644 --- a/XVim/XVimView.m +++ b/XVim/XVimView.m @@ -374,10 +374,11 @@ - (void)_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve pos = length; } + _insertionPoint = pos; if (_cursorMode == CURSOR_MODE_COMMAND && _selectionMode == XVIM_VISUAL_NONE) { - _insertionPoint = [_textView.textStorage convertToValidCursorPositionForNormalMode:pos]; - } else { - _insertionPoint = pos; + if (![buffer isNormalCursorPositionValidAtIndex:pos]) { + _insertionPoint = pos - 1; + } } if (!preserve) { @@ -414,8 +415,7 @@ - (void)_syncState _syncStateLock = YES; if (_cursorMode == CURSOR_MODE_COMMAND) { - NSTextStorage *ts = _textView.textStorage; - if (![ts isValidCursorPosition:_insertionPoint]) { + if (![self.buffer isNormalCursorPositionValidAtIndex:_insertionPoint]) { NSRange placeholder = [(DVTSourceTextView *)_textView rangeOfPlaceholderFromCharacterIndex:_insertionPoint forward:NO wrap:NO limit:0]; if (placeholder.location != NSNotFound && _insertionPoint == (placeholder.location + placeholder.length)) { // The condition here means that just before current insertion point is a placeholder. @@ -722,7 +722,7 @@ - (void)adjustCursorPosition NSRange range = _textView.selectedRange; // If the current cursor position is not valid for normal mode move it. - if (![_textView.textStorage isValidCursorPosition:range.location]) { + if (![self.buffer isNormalCursorPositionValidAtIndex:range.location]) { [self selectPreviousPlaceholder]; NSRange prevPlaceHolder = _textView.selectedRange; @@ -848,7 +848,7 @@ - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion case MOTION_LINENUMBER: end = [buffer indexOfLineNumber:motion.line column:_preservedColumn]; if (NSNotFound == end) { - end = [buffer indexOfLineNumber:[buffer numberOfLines] column:_preservedColumn]; + end = [buffer indexOfLineAtIndex:buffer.length column:_preservedColumn]; } break; case MOTION_PERCENT: @@ -858,7 +858,7 @@ - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion end = [ts positionOfMatchedPair:begin]; break; case MOTION_LASTLINE: - end = [buffer indexOfLineNumber:[buffer numberOfLines] column:_preservedColumn]; + end = [buffer indexOfLineAtIndex:buffer.length column:_preservedColumn]; break; case MOTION_HOME: tmpPos = [self lineNumberInScrollView:0.0 offset:motion.scount - 1]; @@ -1240,7 +1240,7 @@ - (void)doDelete:(XVimMotion *)motion andYank:(BOOL)yank } r = [self _getOperationRange:motionRange type:motion.type]; - if (motion.type == LINEWISE && [_textView.textStorage isLastLine:motionRange.end]) { + if (motion.type == LINEWISE && [buffer isIndexOnLastLine:motionRange.end]) { if (r.location != 0) { motion.info->deleteLastLine = YES; r.location--; From 0f824b8dc98c2fe07fd05698142f64385b083b60 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Mon, 25 Nov 2013 08:26:51 +0100 Subject: [PATCH 31/44] Fix b_v_D btw it's not 100% correct because gv doesn't quite do the right thing yet. --- XVim/XVimDefs.h | 1 - XVim/XVimView.m | 3 --- XVim/XVimVisualEvaluator.m | 12 ++++++------ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/XVim/XVimDefs.h b/XVim/XVimDefs.h index 6e7cd142..a4ab8e56 100644 --- a/XVim/XVimDefs.h +++ b/XVim/XVimDefs.h @@ -25,7 +25,6 @@ typedef NS_ENUM(NSUInteger, XVimInsertionPoint) { XVIM_INSERT_BEFORE_FIRST_NONBLANK, XVIM_INSERT_APPEND_EOL, XVIM_INSERT_BLOCK_KILL, - XVIM_INSERT_BLOCK_KILL_EOL, }; typedef NS_ENUM(NSUInteger, XVimSortOptions) { diff --git a/XVim/XVimView.m b/XVim/XVimView.m index 8da51cf1..87eff087 100644 --- a/XVim/XVimView.m +++ b/XVim/XVimView.m @@ -1812,9 +1812,6 @@ - (void)doInsert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column block if (lines) *lines = XVimMakeRange(sel.top, sel.bottom); switch (mode) { - case XVIM_INSERT_BLOCK_KILL_EOL: - sel.right = XVimSelectionEOL; - /* fallthrough */ case XVIM_INSERT_BLOCK_KILL: [self _yankSelection:sel]; [buffer beginEditingAtIndex:tl]; diff --git a/XVim/XVimVisualEvaluator.m b/XVim/XVimVisualEvaluator.m index 170eff0f..4b86fadf 100644 --- a/XVim/XVimVisualEvaluator.m +++ b/XVim/XVimVisualEvaluator.m @@ -204,7 +204,8 @@ - (XVimEvaluator *)C{ xview.selectionMode = XVIM_VISUAL_LINE; return [self c]; } - return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_BLOCK_KILL_EOL] autorelease]; + [self performSelector:@selector(DOLLAR)]; + return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_BLOCK_KILL] autorelease]; } - (XVimEvaluator*)C_b{ @@ -229,14 +230,13 @@ - (XVimEvaluator *)DEL{ - (XVimEvaluator*)D{ XVimView *xview = self.currentView; - if (!xview.inBlockMode) { + if (xview.inBlockMode) { + [self performSelector:@selector(DOLLAR)]; + } else { xview.selectionMode = XVIM_VISUAL_LINE; - return [self d]; } - // FIXME: doesn't work ask expected in any visual mode - XVimDeleteEvaluator* eval = [[[XVimDeleteEvaluator alloc] initWithWindow:self.window] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, LINEWISE, MOTION_OPTION_NONE, 0)]; + return [self d]; } - (XVimEvaluator*)C_f{ From 890f5ad370a189cb4960ee99e72d67cdbff3c3a9 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Mon, 25 Nov 2013 19:29:38 +0100 Subject: [PATCH 32/44] Disable GC for XCode5, it's not a GC App anymore Should fix #533 --- XVim.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index d69394cc..70cda6f0 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -1005,7 +1005,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; COMBINE_HIDPI_IMAGES = YES; - GCC_ENABLE_OBJC_GC = supported; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "XVim/XVim-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -1027,7 +1026,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; COMBINE_HIDPI_IMAGES = YES; - GCC_ENABLE_OBJC_GC = supported; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "XVim/XVim-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = "XVIM_XCODE_VERSION=5"; From 7e2d1c8c2c587dd22788c12518e0d9482d6b6231 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Mon, 25 Nov 2013 19:31:24 +0100 Subject: [PATCH 33/44] get rid of XVimMotionType.h, merge it in XVimMotion.h --- XVim.xcodeproj/project.pbxproj | 6 ++---- XVim/XVimMotion.h | 9 ++++++++- XVim/XVimMotionType.h | 8 -------- 3 files changed, 10 insertions(+), 13 deletions(-) delete mode 100644 XVim/XVimMotionType.h diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 70cda6f0..1543cf33 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -332,7 +332,6 @@ C361DE671538F18400037B4B /* XVimGActionEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimGActionEvaluator.m; path = XVim/XVimGActionEvaluator.m; sourceTree = SOURCE_ROOT; }; C366C23915287EE30008C58E /* XVimKeymap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimKeymap.h; path = XVim/XVimKeymap.h; sourceTree = SOURCE_ROOT; }; C366C23A15287EE30008C58E /* XVimKeymap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimKeymap.m; path = XVim/XVimKeymap.m; sourceTree = SOURCE_ROOT; }; - C36C1045153104EC00CE1D62 /* XVimMotionType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = XVimMotionType.h; path = XVim/XVimMotionType.h; sourceTree = SOURCE_ROOT; }; C36C104C153131C500CE1D62 /* XVimTextObjectEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTextObjectEvaluator.h; path = XVim/XVimTextObjectEvaluator.h; sourceTree = SOURCE_ROOT; }; C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimTextObjectEvaluator.m; path = XVim/XVimTextObjectEvaluator.m; sourceTree = SOURCE_ROOT; }; C38A5B2E1526C6B100E1448D /* XVimKeyStroke.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimKeyStroke.h; path = XVim/XVimKeyStroke.h; sourceTree = SOURCE_ROOT; }; @@ -406,6 +405,8 @@ 6E2C392C183E4B880056E30F /* View */ = { isa = PBXGroup; children = ( + A23771571611CD4200FE5F8B /* XVimMotion.h */, + A23771581611CD4200FE5F8B /* XVimMotion.m */, 6E2C392D183E4BA20056E30F /* XVimView.h */, 6E2C392E183E4BA20056E30F /* XVimView.m */, ); @@ -671,7 +672,6 @@ 6E2C392C183E4B880056E30F /* View */, C3552DA6153AC6F800D57577 /* XVimHistoryHandler.h */, C3552DA7153AC6F800D57577 /* XVimHistoryHandler.m */, - C36C1045153104EC00CE1D62 /* XVimMotionType.h */, C32CE8621532F0E5002BCE2B /* XVimKeymapProvider.h */, F17D0139150861DC00A8111B /* XVimRegister.h */, F17D013A150861DC00A8111B /* XVimRegister.m */, @@ -687,8 +687,6 @@ A293A38716CE8A8000E1E827 /* XVimDebug.m */, A2A6BA2C152A544B00F0EB5F /* XVimSearch.h */, A2A6BA2D152A544B00F0EB5F /* XVimSearch.m */, - A23771571611CD4200FE5F8B /* XVimMotion.h */, - A23771581611CD4200FE5F8B /* XVimMotion.m */, A2126B7316FB30B0000BE21C /* XVimMark.h */, A2126B7416FB30B0000BE21C /* XVimMark.m */, A2126B7616FB4806000BE21C /* XVimMarks.h */, diff --git a/XVim/XVimMotion.h b/XVim/XVimMotion.h index 649d40fc..8a3e29c4 100644 --- a/XVim/XVimMotion.h +++ b/XVim/XVimMotion.h @@ -7,7 +7,6 @@ // #import -#import "XVimMotionType.h" typedef struct { BOOL reachedEndOfLine; @@ -17,6 +16,14 @@ typedef struct { NSUInteger lastEndOfWord; }XVimMotionInfo; +typedef enum _MOTION_TYPE{ + DEFAULT_MOTION_TYPE, + CHARACTERWISE_INCLUSIVE, + CHARACTERWISE_EXCLUSIVE, + LINEWISE, + BLOCKWISE +} MOTION_TYPE; + #define XVIM_MAKE_MOTION(MOTION,TYPE,OPTION,COUNT) [[[XVimMotion alloc] initWithMotion:MOTION type:TYPE option:OPTION count:COUNT] autorelease] typedef enum { diff --git a/XVim/XVimMotionType.h b/XVim/XVimMotionType.h deleted file mode 100644 index 941296db..00000000 --- a/XVim/XVimMotionType.h +++ /dev/null @@ -1,8 +0,0 @@ - -typedef enum _MOTION_TYPE{ - DEFAULT_MOTION_TYPE, - CHARACTERWISE_INCLUSIVE, - CHARACTERWISE_EXCLUSIVE, - LINEWISE, - BLOCKWISE -} MOTION_TYPE; From 0c2519effb555571bcf2f9301eb985a8e61b1207 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Mon, 25 Nov 2013 19:47:29 +0100 Subject: [PATCH 34/44] Move some more functions from the NSTextStorage category to XVimBuffer Those functions are related to position of the index within the line. --- XVim/NSTextStorage+VimOperation.h | 9 ------- XVim/NSTextStorage+VimOperation.m | 13 --------- XVim/XVimBuffer.h | 28 +++++++++++++------ XVim/XVimBuffer.m | 45 ++++++++++++++++++++++--------- XVim/XVimInsertEvaluator.m | 2 +- XVim/XVimView.m | 16 +++++------ 6 files changed, 62 insertions(+), 51 deletions(-) diff --git a/XVim/NSTextStorage+VimOperation.h b/XVim/NSTextStorage+VimOperation.h index 8249b3a0..8094bee8 100644 --- a/XVim/NSTextStorage+VimOperation.h +++ b/XVim/NSTextStorage+VimOperation.h @@ -20,15 +20,6 @@ #pragma mark Definitions -// Determine if the position specified with "index" is EOL. -- (BOOL) isEOL:(NSUInteger)index; - -// Determine if the position is a beginning of line -- (BOOL) isBOL:(NSUInteger)index; - -// Determine if the position specified with "index" is newline. -- (BOOL) isNewline:(NSUInteger)index; - // Determine if the position specified with "index" is white space. - (BOOL) isWhitespace:(NSUInteger)index; diff --git a/XVim/NSTextStorage+VimOperation.m b/XVim/NSTextStorage+VimOperation.m index c5dfbfcc..0c464755 100644 --- a/XVim/NSTextStorage+VimOperation.m +++ b/XVim/NSTextStorage+VimOperation.m @@ -27,19 +27,6 @@ - (BOOL) isEOL:(NSUInteger)index{ return [self isNewline:index] || [self isEOF:index]; } -- (BOOL) isBOL:(NSUInteger)index{ - ASSERT_VALID_RANGE_WITH_EOF(index); - if( 0 == index ){ - return YES; - } - - if( [self isNewline:index-1] ){ - return YES; - } - - return NO; -} - - (BOOL) isNewline:(NSUInteger)index{ ASSERT_VALID_RANGE_WITH_EOF(index); if( index == self.length ){ diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 7169e670..56de6691 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -128,6 +128,26 @@ @property (nonatomic, readonly) NSUInteger tabWidth; @property (nonatomic, readonly) NSUInteger indentWidth; +#pragma mark Specific positions of the index within the line + +/** @brief returns YES when \a index is on the last line + */ +- (BOOL)isIndexOnLastLine:(NSUInteger)index; + +/** @brief returns YES when \a index is at the beginning of its line + * It returns NO if \a index points to the \n of a \r\n + */ +- (BOOL)isIndexAtStartOfLine:(NSUInteger)index; + +/** @brief returns YES when \a index is at the end of its line + * It also returns YES if \a index points to the \n in a \r\n. + */ +- (BOOL)isIndexAtEndOfLine:(NSUInteger)index; + +/** @brief returns YES when \a index is a valid cursor position for normal mode + */ +- (BOOL)isNormalCursorPositionValidAtIndex:(NSUInteger)index; + #pragma mark Converting between Indexes and Line Numbers /** @brief returns the index range for the given line number @@ -178,14 +198,6 @@ */ - (NSUInteger)lineNumberAtIndex:(NSUInteger)index; -/** @brief returns YES when \a index is on the last line - */ -- (BOOL)isIndexOnLastLine:(NSUInteger)index; - -/** @brief returns YES when \a index is a valid cursor position for normal mode - */ -- (BOOL)isNormalCursorPositionValidAtIndex:(NSUInteger)index; - #pragma mark Converting between Indexes and Line Numbers + Columns /** @brief returns the column number of \a index within the line. diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index 44e1e28a..65b0eff8 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -140,6 +140,39 @@ - (NSUInteger)indentWidth return 8; } +#pragma mark Specific positions of the index within the line + +- (BOOL)isIndexOnLastLine:(NSUInteger)index +{ + return [self endOfLine:index] == self.length; +} + +- (BOOL)isIndexAtStartOfLine:(NSUInteger)index +{ + if (index == 0) { + return YES; + } + if (!isNewline([self.string characterAtIndex:index - 1])) { + return NO; + } + if (index >= self.length || !isNewline([self.string characterAtIndex:index])) { + return YES; + } + return [self startOfLine:index] == index; +} + +- (BOOL)isIndexAtEndOfLine:(NSUInteger)index +{ + return index >= self.length || isNewline([self.string characterAtIndex:index]); +} + +- (BOOL)isNormalCursorPositionValidAtIndex:(NSUInteger)index +{ + NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; + + return range.length == 0 || index < NSMaxRange(range); +} + #pragma mark Converting between Indexes and Line Numbers - (NSRange)indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUInteger *)newLineLength @@ -271,18 +304,6 @@ - (NSUInteger)lineNumberAtIndex:(NSUInteger)index return num; } -- (BOOL)isIndexOnLastLine:(NSUInteger)index -{ - return [self endOfLine:index] == self.length; -} - -- (BOOL)isNormalCursorPositionValidAtIndex:(NSUInteger)index -{ - NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; - - return range.length == 0 || index < NSMaxRange(range); -} - #pragma mark Converting between Indexes and Line Numbers + Columns diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index 420dca2c..bf41bd54 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -301,7 +301,7 @@ - (void)C_yC_eHelper:(BOOL)handlingC_y } else { indexToCopy = [buffer indexOfLineNumber:pos.line + 1 column:pos.column]; } - if (indexToCopy == NSNotFound || [buffer.textStorage isEOL:indexToCopy]) { + if (indexToCopy == NSNotFound || [buffer isIndexAtEndOfLine:indexToCopy]) { return; } diff --git a/XVim/XVimView.m b/XVim/XVimView.m index 87eff087..27f204e3 100644 --- a/XVim/XVimView.m +++ b/XVim/XVimView.m @@ -623,7 +623,7 @@ - (NSArray *)_selectedRanges } if (end >= length) { end--; - } else if (sel.right != XVimSelectionEOL && [_textView.textStorage isEOL:end]) { + } else if (sel.right != XVimSelectionEOL && [buffer isIndexAtEndOfLine:end]) { end--; } [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(begin, end - begin + 1)]]; @@ -679,7 +679,7 @@ - (void)escapeFromInsert { if (_cursorMode == CURSOR_MODE_INSERT) { _cursorMode = CURSOR_MODE_COMMAND; - if (![_textView.textStorage isBOL:_insertionPoint]) { + if (![self.buffer isIndexAtStartOfLine:_insertionPoint]) { [self _moveCursor:_insertionPoint - 1 preserveColumn:NO]; } [self _syncState]; @@ -1180,7 +1180,7 @@ - (void)_killSelection:(XVimSelection)sel } } - if ([_textView.textStorage isEOL:rpos]) { + if ([buffer isIndexAtEndOfLine:rpos]) { rpos--; } else if (lpos < rpos) { if ([string characterAtIndex:rpos] == '\t') { @@ -1361,7 +1361,7 @@ - (void)doYank:(XVimMotion*)motion } } r = [self _getOperationRange:to type:motion.type]; - if (motion.type == LINEWISE && to.end >= buffer.length && [_textView.textStorage isBOL:to.end]) { + if (motion.type == LINEWISE && to.end >= buffer.length && [buffer isIndexAtStartOfLine:to.end]) { if (r.location != 0) { r.location--; r.length++; @@ -1408,7 +1408,7 @@ - (void)doPut:(NSString *)_text withType:(TEXT_TYPE)type afterCursor:(bool)after if (type == TEXT_TYPE_CHARACTERS) { // Forward insertion point +1 if after flag if on if (0 != text.length) { - if (![_textView.textStorage isNewline:_insertionPoint] && after) { + if (![buffer isIndexAtEndOfLine:_insertionPoint] && after) { targetPos++; } insertionPointAfterPut = targetPos; @@ -1435,7 +1435,7 @@ - (void)doPut:(NSString *)_text withType:(TEXT_TYPE)type afterCursor:(bool)after } } else if (type == TEXT_TYPE_BLOCK) { // Forward insertion point +1 if after flag if on - if (![_textView.textStorage isNewline:_insertionPoint] && _insertionPoint < buffer.length && after) { + if (![buffer isIndexAtEndOfLine:_insertionPoint] && after) { _insertionPoint++; } insertionPointAfterPut = _insertionPoint; @@ -1841,7 +1841,7 @@ - (void)doInsert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column block break; case XVIM_INSERT_APPEND: NSAssert(_cursorMode == CURSOR_MODE_COMMAND, @"_cursorMode shoud be CURSOR_MODE_COMMAND"); - if (![_textView.textStorage isEOL:pos]) { + if (![buffer isIndexAtEndOfLine:pos]) { _insertionPoint = pos + 1; } break; @@ -1897,7 +1897,7 @@ - (void)doInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint)mode for (NSUInteger line = lines.begin; line <= lines.end; line++) { NSUInteger pos = [buffer indexOfLineNumber:line column:column]; - if (column != XVimSelectionEOL && [_textView.textStorage isEOL:pos]) { + if (column != XVimSelectionEOL && [buffer isIndexAtEndOfLine:pos]) { if ([buffer columnOfIndex:pos] < column) { if (mode != XVIM_INSERT_APPEND) { continue; From 4fa859ce6ab08284758e7d0b207ac0c1f741301e Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Mon, 25 Nov 2013 21:30:59 +0100 Subject: [PATCH 35/44] fix the release build --- XVim/XVimWindow.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/XVim/XVimWindow.m b/XVim/XVimWindow.m index e1542323..24fc6c23 100644 --- a/XVim/XVimWindow.m +++ b/XVim/XVimWindow.m @@ -138,11 +138,13 @@ - (void)dealloc - (void)dumpEvaluatorStack:(NSMutableArray*)stack { +#ifdef DEBUG for (NSUInteger i = 0; i < stack.count; i++) { XVimEvaluator *e = [stack objectAtIndex:i]; DEBUG_LOG(@"Evaluator%d:%@ argStr:%@ yankReg:%@", i, NSStringFromClass([e class]), e.argumentString, e.yankRegister); } +#endif } #pragma mark - Handling keystrokes and evaluation stack From a65942783b2dfa552040e8b0fd1fb2620b160c30 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Tue, 26 Nov 2013 00:44:31 +0100 Subject: [PATCH 36/44] Refactor -{prev,next}{,Line}:... in XVimBuffer This is now: -indexOfCharMotion:index:options: -indexOfLineMotion:index:column: Implement _/+/-/$/CR/C_m/... using this. Also rename and properly namespace MOTION_OPTION as XVimMotionOptions and the MOPT_ prefix. --- XVim/NSTextStorage+VimOperation.h | 54 +++------- XVim/NSTextStorage+VimOperation.m | 171 ++++++------------------------ XVim/XVimBuffer.h | 24 ++++- XVim/XVimBuffer.m | 77 +++++++++++++- XVim/XVimDefs.h | 15 ++- XVim/XVimDeleteEvaluator.m | 4 +- XVim/XVimEqualEvaluator.m | 2 +- XVim/XVimGActionEvaluator.m | 4 +- XVim/XVimGMotionEvaluator.m | 6 +- XVim/XVimGVisualEvaluator.m | 10 +- XVim/XVimInsertEvaluator.m | 2 +- XVim/XVimMotion.h | 18 +--- XVim/XVimMotion.m | 2 +- XVim/XVimMotionEvaluator.m | 89 +++++++--------- XVim/XVimNormalEvaluator.m | 4 +- XVim/XVimSearch.m | 12 +-- XVim/XVimShiftEvaluator.m | 4 +- XVim/XVimSwapCharsEvaluator.m | 2 +- XVim/XVimTextObjectEvaluator.m | 4 +- XVim/XVimView.h | 10 +- XVim/XVimView.m | 101 +++++++++++------- XVim/XVimVisualEvaluator.m | 18 ++-- XVim/XVimYankEvaluator.m | 2 +- 23 files changed, 299 insertions(+), 336 deletions(-) diff --git a/XVim/NSTextStorage+VimOperation.h b/XVim/NSTextStorage+VimOperation.h index 8094bee8..72aeeb86 100644 --- a/XVim/NSTextStorage+VimOperation.h +++ b/XVim/NSTextStorage+VimOperation.h @@ -29,57 +29,29 @@ #pragma mark Vim operation related methods -- (NSUInteger)prev:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)next:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimMotionInfo*)info; - -/** - * Returns the position when a cursor goes to upper line. - * @param index the position of the cursor - * @param column the desired position of the column in previous line - * @param count number of repeat - * @param opt currntly nothing is supported - * @return The position to move to. If the current index is on the first line it returns 0 - * - * "column" may be greater then number of characters in the current line. - * Assume that you have following text. - * abcd - * ef - * 12345678 - * When a cursor at character "4" goes up cursor will go at "f". - * When a cursor goes up agein it should got at d. (This is default Vim motion) - * To keep the column position you have to specifi the "column" argument. - * - **/ -- (NSUInteger)prevLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInteger)count option:(MOTION_OPTION)opt; - -/** - * See prevLine's description for meaning of arguments - **/ -- (NSUInteger)nextLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInteger)count option:(MOTION_OPTION)opt; - /** * Returns position of the head of count words forward and an info structure that handles the end of word boundaries. * @param index * @param count - * @param option MOTION_OPTION_NONE or BIGWORD + * @param option MOPT_NONE or MOPT_BIGWORD * @param info This is used with special cases explaind above such as 'cw' or 'w' crossing over the newline. **/ -- (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimMotionInfo*)info; +- (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt info:(XVimMotionInfo*)info; -- (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; //e,E -- (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; //ge,gE -- (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)moveFromIndex:(NSUInteger)index paragraphs:(NSInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(MOTION_OPTION)opt; -- (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(MOTION_OPTION)opt; +- (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; +- (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; //e,E +- (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; //ge,gE +- (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; +- (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; +- (NSUInteger)moveFromIndex:(NSUInteger)index paragraphs:(NSInteger)count option:(XVimMotionOptions)opt; +- (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(XVimMotionOptions)opt; +- (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(XVimMotionOptions)opt; // Search starts from 'index+1' to the end of the string -- (NSRange)searchRegexForward:(NSString*)regex from:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; +- (NSRange)searchRegexForward:(NSString*)regex from:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; // Search starts from 'index-1' to the beginning of the string -- (NSRange)searchRegexBackward:(NSString*)regex from:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; +- (NSRange)searchRegexBackward:(NSString*)regex from:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; /** @@ -91,7 +63,7 @@ #pragma mark Text Object // TODO: Following code should be rewritten -- (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; +- (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; // The following code is from xVim by WarWithinMe. // These will be integreted into NSTextView category. diff --git a/XVim/NSTextStorage+VimOperation.m b/XVim/NSTextStorage+VimOperation.m index 0c464755..1e68468f 100644 --- a/XVim/NSTextStorage+VimOperation.m +++ b/XVim/NSTextStorage+VimOperation.m @@ -129,115 +129,6 @@ - (NSUInteger)positionOfMatchedPair:(NSUInteger)pos } #pragma mark Vim operation related methods -#define charat(x) [[self string] characterAtIndex:(x)] - -- (NSUInteger)prev:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ - if( 0 == index){ - return 0; - } - - NSString* string = self.xvim_buffer.string; - NSUInteger pos = index; - for (NSUInteger i = 0; i < count && pos != 0 ; i++) - { - //Try move to prev position and check if its valid position. - NSUInteger prev = pos-1; //This is the position where we are trying to move to. - // If the position is new line and its not wrapable we stop moving - if( opt == LEFT_RIGHT_NOWRAP && isNewline([self.xvim_buffer.string characterAtIndex:prev]) ){ - break; // not update the position - } - - // If its wrapable, skip newline except its blankline - if (isNewline([string characterAtIndex:prev])) { - if(![self isBlankline:prev]) { - // skip the newline letter at the end of line - prev--; - } - } - - if(charat(prev) == '>' && prev){ - //possible autocomplete glyph that we should skip. - if(charat(prev - 1) == '#'){ - NSUInteger findstart = prev; - while (--findstart ) { - if(charat(findstart) == '#'){ - if(charat(findstart - 1) == '<'){ - prev = findstart - 1; - break; - } - } - } - } - } - - // Now the position can be move to the prev - pos = prev; - } - return pos; -} - -- (NSUInteger)next:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimMotionInfo*)info{ - info->reachedEndOfLine = NO; - - if( index == [self.xvim_buffer.string length] ) - return [self.xvim_buffer.string length]; - - NSString* string = self.xvim_buffer.string; - NSUInteger pos = index; - // If the currenct cursor position is on a newline (blank line) and not wrappable never move the cursor - if( opt == LEFT_RIGHT_NOWRAP && [self isBlankline:pos]){ - return pos; - } - - for (NSUInteger i = 0; i < count && pos < self.length; i++) { - NSUInteger next = pos + 1; - // If the next position is the end of docuement and current position is not a newline - // Never move a cursor to the end of document. - if( [self isEOF:next] && !isNewline([string characterAtIndex:pos]) ){ - info->reachedEndOfLine = YES; - break; - } - - if( opt == LEFT_RIGHT_NOWRAP && isNewline([string characterAtIndex:next]) ){ - info->reachedEndOfLine = YES; - break; - } - - // If the next position is newline and not a blankline skip it - if (isNewline([string characterAtIndex:next])) { - if(![self isBlankline:next]) { - // skip the newline letter at the end of line - next++; - } - } - pos = next; - } - return pos; -} - -- (NSUInteger)prevLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInteger)count option:(MOTION_OPTION)opt{ - ASSERT_VALID_RANGE_WITH_EOF(index); - XVimBuffer *buffer = self.xvim_buffer; - - NSUInteger lno = [buffer lineNumberAtIndex:index]; - - lno = lno <= count ? 1 : lno - count; - return [buffer indexOfLineNumber:lno column:column]; -} - -- (NSUInteger)nextLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInteger)count option:(MOTION_OPTION)opt{ - ASSERT_VALID_RANGE_WITH_EOF(index); - XVimBuffer *buffer = self.xvim_buffer; - - NSUInteger lno = [buffer lineNumberAtIndex:index] + count; - NSUInteger lines = buffer.numberOfLines; - - if (lno > lines) { - lno = lines; - } - return [buffer indexOfLineNumber:lno column:column]; -} - /** From Vim help: word and WORD *word* @@ -259,7 +150,7 @@ - (NSUInteger)nextLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInt next line. **/ -- (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimMotionInfo*)info{ +- (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt info:(XVimMotionInfo*)info{ ASSERT_VALID_RANGE_WITH_EOF(index); NSAssert(nil != info, @"Specify info"); @@ -317,7 +208,7 @@ - (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOT info->lastEndOfWord = i-1; wordInLineFound = NO; }else if(isNonblank(curChar)){ - if(isKeyword(lastChar) != isKeyword(curChar) && opt != BIGWORD){ + if(isKeyword(lastChar) != isKeyword(curChar) && opt != MOPT_BIGWORD){ --count; info->lastEndOfLine = i-1; info->lastEndOfWord = i-1; @@ -350,7 +241,7 @@ - (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOT } lastChar = curChar; - if( isNewline(curChar) && opt == LEFT_RIGHT_NOWRAP ){ + if( isNewline(curChar) && opt == MOPT_NOWRAP ){ pos = i-1; break; } @@ -363,7 +254,7 @@ - (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOT return pos; } -- (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); if( 1 >= index ) return 0; @@ -412,13 +303,13 @@ - (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MO } // new word starts between followings.( keyword is determined by 'iskeyword' in Vim ) // - Whitespace(including newline) and Non-Blank - // - keyword and non-keyword(without whitespace) (only when !BIGWORD) - // - non-keyword(without whitespace) and keyword (only when !BIGWORD) + // - keyword and non-keyword(without whitespace) (only when !MOPT_BIGWORD) + // - non-keyword(without whitespace) and keyword (only when !MOPT_BIGWORD) // - newline and newline(blankline) else if( ((isWhitespace(curChar) || isNewline(curChar)) && isNonblank(lastChar)) || - ( opt != BIGWORD && isKeyword(curChar) && !isKeyword(lastChar) && !isWhitespace(lastChar) && !isNewline(lastChar)) || - ( opt != BIGWORD && !isKeyword(curChar) && !isWhitespace(curChar) && !isNewline(lastChar) && isKeyword(lastChar) ) || + ( opt != MOPT_BIGWORD && isKeyword(curChar) && !isKeyword(lastChar) && !isWhitespace(lastChar) && !isNewline(lastChar)) || + ( opt != MOPT_BIGWORD && !isKeyword(curChar) && !isWhitespace(curChar) && !isNewline(lastChar) && isKeyword(lastChar) ) || ( isNewline(curChar) && [self isBlankline:i+1] ) ){ count--; @@ -429,7 +320,7 @@ - (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MO pos = 0; break; } - if( 0 == count || (isNewline(curChar) && opt == LEFT_RIGHT_NOWRAP) ){ + if( 0 == count || (isNewline(curChar) && opt == MOPT_NOWRAP) ){ pos = i+1; break; } @@ -444,7 +335,7 @@ - (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MO * This is because Vim causes an error(beeps) when type "e" at the end of document. * But currently this returns "index" as a next position to move because all evaluators does not expect NSNotFound **/ -- (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); NSAssert( 0 != count , @"count must be greater than 0"); @@ -455,7 +346,7 @@ - (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option } NSUInteger p; - if( opt & MOTION_OPTION_CHANGE_WORD ){ + if( opt & MOPT_CHANGE_WORD ){ p = index; } else { p = index+1; // We start searching end of word from next character @@ -468,7 +359,7 @@ - (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option // Vim defines "Blank Line as a word" but 'e' does not stop on blank line. // Thats why we are not seeing blank line as a border of a word here (commented out the condition currently) // We may add some option to specify if we count a blank line as a word here. - if( opt & BIGWORD ){ + if( opt & MOPT_BIGWORD ){ if( /*[self isBlankline:p] || */// blank line (isNonblank(curChar) && !isNonblank(nextChar)) // non blank to blank ){ @@ -496,7 +387,7 @@ - (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option /** * Returns position of the end of count words backward. **/ -- (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ // TODO: Implement! return index; } @@ -516,7 +407,7 @@ - (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count optio */ // TODO: Treat paragraph and section boundary as sections baundary as well -- (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( +- (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ //( NSUInteger pos = index+1; NSUInteger sentence_head = NSNotFound; NSString* s = self.xvim_buffer.string; @@ -568,7 +459,7 @@ - (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option: return sentence_head; } -- (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( +- (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ //( if( 0 == index ){ return NSNotFound; } @@ -641,7 +532,7 @@ Note that a blank line (only containing white space) is NOT a paragraph paragraph boundary |posix|. */ -- (NSUInteger)moveFromIndex:(NSUInteger)index paragraphs:(NSInteger)count option:(MOTION_OPTION)opt +- (NSUInteger)moveFromIndex:(NSUInteger)index paragraphs:(NSInteger)count option:(XVimMotionOptions)opt { XVimBuffer *buffer = self.xvim_buffer; NSUInteger length = buffer.length; @@ -686,15 +577,15 @@ - (NSUInteger)moveFromIndex:(NSUInteger)index paragraphs:(NSInteger)count option return forward ? length : 0; } -- (NSUInteger)sectionsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( +- (NSUInteger)sectionsForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ //( return 0; } -- (NSUInteger)sectionsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( +- (NSUInteger)sectionsBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ //( return 0; } -- (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(MOTION_OPTION)opt{ +- (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); if( [self isEOF:index] ){ return NSNotFound; @@ -716,7 +607,7 @@ - (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count char return NSNotFound; } -- (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(MOTION_OPTION)opt{ +- (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); if( 0 == index ){ return NSNotFound; @@ -738,14 +629,14 @@ - (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count char return NSNotFound; } -- (NSRange)searchRegexForward:(NSString*)pattern from:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSRange)searchRegexForward:(NSString*)pattern from:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); NSAssert(pattern != nil, @"pattern must not be nil"); NSRange ret = NSMakeRange(NSNotFound,0); NSRegularExpressionOptions options = NSRegularExpressionAnchorsMatchLines; - if( opt & SEARCH_CASEINSENSITIVE ){ + if( opt & MOPT_SEARCH_CASEINSENSITIVE ){ options |= NSRegularExpressionCaseInsensitive; } NSError* err = nil; @@ -766,8 +657,8 @@ - (NSRange)searchRegexForward:(NSString*)pattern from:(NSUInteger)index count:(N } } - // Then look for the position in range of [BOF,index] if SEARCH_WRAP - if( 0 != count && opt & SEARCH_WRAP ){ + // Then look for the position in range of [BOF,index] if MOPT_SEARCH_WRAP + if( 0 != count && opt & MOPT_SEARCH_WRAP ){ for( NSTextCheckingResult* result in matches ){ if( result.range.location <= index ){ count--; @@ -783,14 +674,14 @@ - (NSRange)searchRegexForward:(NSString*)pattern from:(NSUInteger)index count:(N return ret; } -- (NSRange)searchRegexBackward:(NSString*)pattern from:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSRange)searchRegexBackward:(NSString*)pattern from:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); NSAssert(pattern != nil, @"pattern must not be nil"); NSRange ret = NSMakeRange(NSNotFound,0); NSRegularExpressionOptions options = NSRegularExpressionAnchorsMatchLines; - if( opt & SEARCH_CASEINSENSITIVE ){ + if( opt & MOPT_SEARCH_CASEINSENSITIVE ){ options |= NSRegularExpressionCaseInsensitive; } NSError* err = nil; @@ -811,8 +702,8 @@ - (NSRange)searchRegexBackward:(NSString*)pattern from:(NSUInteger)index count:( } } - // Then look for the position in range of [index,EOF] if SEARCH_WRAP - if( 0 != count && opt & SEARCH_WRAP ){ + // Then look for the position in range of [index,EOF] if MOPT_SEARCH_WRAP + if( 0 != count && opt & MOPT_SEARCH_WRAP ){ for( NSTextCheckingResult* result in [matches reverseObjectEnumerator] ){ if( result.range.location >= index ){ count--; @@ -856,7 +747,7 @@ - (NSRange)searchRegexBackward:(NSString*)pattern from:(NSUInteger)index count:( static void initNSStringHelperBackward(NSStringHelper*, NSString* string, NSUInteger strLen); static unichar characterAtIndex(NSStringHelper*, NSInteger index); -- (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ NSString* string = self.xvim_buffer.string; NSInteger maxIndex = self.length - 1; if (index > maxIndex) { return NSMakeRange(NSNotFound, 0); } @@ -877,7 +768,7 @@ - (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION NSCharacterSet *wsSet = [NSCharacterSet whitespaceCharacterSet]; NSCharacterSet *wordSet = nil; - if ( opt & BIGWORD) { + if ( opt & MOPT_BIGWORD) { NSCharacterSet *charSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet]; wordSet = charSet; } @@ -901,7 +792,7 @@ - (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION end = seek_forwards(string, end, searchSet); // For inclusive mode, try to eat some more - if ( !(opt & TEXTOBJECT_INNER)) { + if ( !(opt & MOPT_TEXTOBJECT_INNER)) { NSInteger newEnd = end; if (end < maxIndex) { if (initialCharIsWs) { diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 56de6691..21cb6211 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -231,13 +231,29 @@ */ - (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column; -/** @brief returns the index for the given line containing \a index and column. +#pragma mark Motion support functions + +/** @brief returns the index for a 'h/l' movement * + * @param scount the motion count (negative means backwards) + * @param index the start index + * @param wrap wether the movement blocks at line start/end or wraps * @returns - * If \a column is larger than the number of columns in that line, - * it returns the index of the endOfLine for that line + * This never returns NSNotFound, but it can return the end of line, + * and it's up to the caller to detect that if it's a problem. + */ +- (NSUInteger)indexOfCharMotion:(NSInteger)scount index:(NSUInteger)index options:(XVimMotionOptions)options; + +/** @brief returns the index for a 'j/k' movement + * + * @param scount the number of lines to go forward (negative means backward). + * @param index the line number containing that index is the starting line for that movement + * @param column the column to try to reach + * @returns + * this never returns NSNotFound, if the movement reaches the start + * or end of the file, the resulting line number is clamped. */ -- (NSUInteger)indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column; +- (NSUInteger)indexOfLineMotion:(NSInteger)scount index:(NSUInteger)index column:(NSUInteger)column; #pragma mark Searching particular positions on the current line diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index 65b0eff8..34e11e5c 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -142,6 +142,20 @@ - (NSUInteger)indentWidth #pragma mark Specific positions of the index within the line +NS_INLINE NSUInteger _addClamp(NSUInteger a, NSInteger b, NSUInteger min, NSUInteger max) +{ +#if DEBUG + NSCAssert(min <= a && a <= max, @"invalid parameter to _addClamp"); +#endif + if (b < 0 && (NSInteger)a + b <= min) { + return min; + } + if (b > 0 && a + (NSUInteger)b > max) { + return max; + } + return (NSUInteger)((NSInteger)a + b); +} + - (BOOL)isIndexOnLastLine:(NSUInteger)index { return [self endOfLine:index] == self.length; @@ -355,7 +369,7 @@ - (NSUInteger)numberOfColumnsInLineAtIndex:(NSUInteger)index return xvim_sb_count_columns(&sb, self.tabWidth); } -- (NSUInteger)indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column +- (NSUInteger)_indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column { if (column == 0) { return index; @@ -390,7 +404,66 @@ - (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column return index; } - return [self indexOfLineAtIndex:index column:column]; + return [self _indexOfLineAtIndex:index column:column]; +} + +#pragma mark Motion support functions + +- (NSUInteger)indexOfCharMotion:(NSInteger)scount index:(NSUInteger)index options:(XVimMotionOptions)options +{ + NSUInteger nlLen, max; + NSRange range; + + NSAssert((options & ~MOPT_NOWRAP) == 0, @"unhandled options passed"); + + if (scount == 0) { + return index; + } + + if (options & MOPT_NOWRAP) { + range = [self indexRangeForLineAtIndex:index newLineLength:&nlLen]; + return _addClamp(index, scount, range.location, NSMaxRange(range)); + } + + while (scount > 0) { + range = [self indexRangeForLineAtIndex:index newLineLength:&nlLen]; + max = NSMaxRange(range); + if (index + (NSUInteger)scount > max) { + scount -= max - index + 1; + index = max + nlLen; + } else { + return index + (NSUInteger)scount; + } + } + + while (scount < 0) { + range = [self indexRangeForLineAtIndex:index - 1 newLineLength:&nlLen]; + max = NSMaxRange(range); + if (index > max) { + // if we're just past the EOL skip it back + scount++; + index = max; + } + if ((index - range.location) >= (NSUInteger)-scount) { + return (NSUInteger)((NSInteger)index + scount); + } + scount += index - range.location; + index = range.location; + } + + return index; +} + +- (NSUInteger)indexOfLineMotion:(NSInteger)scount index:(NSUInteger)index column:(NSUInteger)column; +{ + if (scount) { + NSUInteger line = [self lineNumberAtIndex:index]; + + line = _addClamp(line, scount, 1, self.numberOfLines); + index = [self indexOfLineNumber:line]; + } + + return [self _indexOfLineAtIndex:index column:column]; } #pragma mark Searching particular positions on the current line diff --git a/XVim/XVimDefs.h b/XVim/XVimDefs.h index a4ab8e56..999ae022 100644 --- a/XVim/XVimDefs.h +++ b/XVim/XVimDefs.h @@ -19,7 +19,7 @@ #endif #endif -typedef NS_ENUM(NSUInteger, XVimInsertionPoint) { +typedef NS_ENUM(uint8_t, XVimInsertionPoint) { XVIM_INSERT_DEFAULT, XVIM_INSERT_APPEND, XVIM_INSERT_BEFORE_FIRST_NONBLANK, @@ -27,13 +27,24 @@ typedef NS_ENUM(NSUInteger, XVimInsertionPoint) { XVIM_INSERT_BLOCK_KILL, }; -typedef NS_ENUM(NSUInteger, XVimSortOptions) { +typedef NS_ENUM(uint8_t, XVimSortOptions) { XVimSortOptionReversed = 1, XVimSortOptionRemoveDuplicateLines = 1 << 1, XVimSortOptionNumericSort = 1 << 2, XVimSortOptionIgnoreCase = 1 << 3 }; +typedef NS_OPTIONS(unsigned , XVimMotionOptions) { + MOPT_NONE = 0x00, + MOPT_NOWRAP = 0x01, // whether we stop or wrap at EOL + MOPT_BIGWORD = 0x02, // for 'WORD' motion + MOPT_PARA_BOUND_BLANKLINE = 0x04, + MOPT_TEXTOBJECT_INNER = 0x08, + MOPT_SEARCH_WRAP = 0x10, + MOPT_SEARCH_CASEINSENSITIVE = 0x20, + MOPT_CHANGE_WORD = 0x40, // for 'cw','cW' +}; + typedef enum{ TEXT_TYPE_CHARACTERS, TEXT_TYPE_BLOCK, diff --git a/XVim/XVimDeleteEvaluator.m b/XVim/XVimDeleteEvaluator.m index 66ec1064..7ff276cd 100644 --- a/XVim/XVimDeleteEvaluator.m +++ b/XVim/XVimDeleteEvaluator.m @@ -40,7 +40,7 @@ - (XVimEvaluator*)c{ if ([self numericArg] < 1) return nil; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } @@ -54,7 +54,7 @@ - (XVimEvaluator*)d{ if ([self numericArg] < 1) return nil; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } diff --git a/XVim/XVimEqualEvaluator.m b/XVim/XVimEqualEvaluator.m index ef788766..e75cef6d 100644 --- a/XVim/XVimEqualEvaluator.m +++ b/XVim/XVimEqualEvaluator.m @@ -18,7 +18,7 @@ @implementation XVimEqualEvaluator - (XVimEvaluator*)EQUAL{ if ([self numericArg] < 1) return nil; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } diff --git a/XVim/XVimGActionEvaluator.m b/XVim/XVimGActionEvaluator.m index 48f10ae4..51fb5708 100644 --- a/XVim/XVimGActionEvaluator.m +++ b/XVim/XVimGActionEvaluator.m @@ -46,7 +46,7 @@ - (XVimEvaluator*)i{ if (mark.line != NSNotFound) { NSUInteger newPos = [buffer indexOfLineNumber:mark.line column:mark.column]; if (NSNotFound != newPos) { - XVimMotion *m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 0); + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 0); m.position = newPos; @@ -69,7 +69,7 @@ - (XVimEvaluator*)i{ - (XVimEvaluator*)J{ XVimJoinEvaluator* eval = [[[XVimJoinEvaluator alloc] initWithWindow:self.window addSpace:NO] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)u{ diff --git a/XVim/XVimGMotionEvaluator.m b/XVim/XVimGMotionEvaluator.m index beb25bef..041fcf32 100644 --- a/XVim/XVimGMotionEvaluator.m +++ b/XVim/XVimGMotionEvaluator.m @@ -24,21 +24,21 @@ - (XVimEvaluator*)eval:(XVimKeyStroke *)keyStroke{ } - (XVimEvaluator*)g{ - self.motion = XVIM_MAKE_MOTION(MOTION_LINENUMBER, LINEWISE, MOTION_OPTION_NONE, 1); + self.motion = XVIM_MAKE_MOTION(MOTION_LINENUMBER, LINEWISE, MOPT_NONE, 1); self.motion.line = self.numericArg; return nil; } - (XVimEvaluator*)searchCurrentWord:(BOOL)forward { XVimCommandLineEvaluator* eval = [self searchEvaluatorForward:forward]; - NSRange r = [self.currentView xvim_currentWord:MOTION_OPTION_NONE]; + NSRange r = [self.currentView xvim_currentWord:MOPT_NONE]; if( r.location == NSNotFound ){ return nil; } // This is not for matching the searching word itself // Vim also does this behavior( when matched string is not found ) - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); m.position = r.location; [self.currentView moveCursorWithMotion:m]; diff --git a/XVim/XVimGVisualEvaluator.m b/XVim/XVimGVisualEvaluator.m index 2340f203..bf1f1a86 100644 --- a/XVim/XVimGVisualEvaluator.m +++ b/XVim/XVimGVisualEvaluator.m @@ -44,7 +44,7 @@ - (XVimEvaluator *)C_g{ - (XVimEvaluator*)J{ XVimJoinEvaluator* eval = [[[XVimJoinEvaluator alloc] initWithWindow:self.window addSpace:NO] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator *)q{ @@ -54,7 +54,7 @@ - (XVimEvaluator *)q{ - (XVimEvaluator*)u{ [self.argumentString appendString:@"u"]; - XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_LOWER]; [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; @@ -62,7 +62,7 @@ - (XVimEvaluator*)u{ - (XVimEvaluator*)U{ [self.argumentString appendString:@"U"]; - XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_UPPER]; [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; @@ -75,7 +75,7 @@ - (XVimEvaluator *)w{ - (XVimEvaluator *)QUESTION{ [self.argumentString appendString:@"?"]; - XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_ROT13]; [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; @@ -83,7 +83,7 @@ - (XVimEvaluator *)QUESTION{ - (XVimEvaluator*)TILDE{ [self.argumentString appendString:@"~"]; - XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_CASE]; [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index bf41bd54..c88bcc9e 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -326,7 +326,7 @@ - (XVimEvaluator*)C_e{ } - (XVimEvaluator*)C_w{ - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); [self.currentView doDelete:m andYank:NO]; return self; } diff --git a/XVim/XVimMotion.h b/XVim/XVimMotion.h index 8a3e29c4..fbb37c64 100644 --- a/XVim/XVimMotion.h +++ b/XVim/XVimMotion.h @@ -7,6 +7,7 @@ // #import +#import "XVimDefs.h" typedef struct { BOOL reachedEndOfLine; @@ -26,19 +27,6 @@ typedef enum _MOTION_TYPE{ #define XVIM_MAKE_MOTION(MOTION,TYPE,OPTION,COUNT) [[[XVimMotion alloc] initWithMotion:MOTION type:TYPE option:OPTION count:COUNT] autorelease] -typedef enum { - MOTION_OPTION_NONE = 0x00, - LEFT_RIGHT_WRAP = 0x01, - LEFT_RIGHT_NOWRAP = 0x02, - BIGWORD = 0x04, // for 'WORD' motion - INCLUSIVE = 0x08, - MOPT_PARA_BOUND_BLANKLINE = 0x10, - TEXTOBJECT_INNER = 0x20, - SEARCH_WRAP= 0x40, - SEARCH_CASEINSENSITIVE = 0x80, - MOTION_OPTION_CHANGE_WORD = 0x100, // for 'cw','cW' -} MOTION_OPTION; - typedef enum _MOTION{ MOTION_NONE, MOTION_FORWARD, // l @@ -90,7 +78,7 @@ typedef enum _MOTION{ @interface XVimMotion : NSObject @property MOTION motion; @property MOTION_TYPE type; -@property MOTION_OPTION option; +@property XVimMotionOptions option; @property NSUInteger count; @property (readonly) NSInteger scount; @property NSUInteger line; @@ -100,6 +88,6 @@ typedef enum _MOTION{ @property (strong) NSString* regex; @property XVimMotionInfo* info; -- (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(MOTION_OPTION)option count:(NSUInteger)count; +- (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(XVimMotionOptions)option count:(NSUInteger)count; - (BOOL) isTextObject; @end \ No newline at end of file diff --git a/XVim/XVimMotion.m b/XVim/XVimMotion.m index 79d01ead..3e4a5548 100644 --- a/XVim/XVimMotion.m +++ b/XVim/XVimMotion.m @@ -25,7 +25,7 @@ - (NSInteger)scount return (NSInteger)_count; } -- (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(MOTION_OPTION)option count:(NSUInteger)count{ +- (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(XVimMotionOptions)option count:(NSUInteger)count{ if( self = [super init]){ _motion = motion; _type = type; diff --git a/XVim/XVimMotionEvaluator.m b/XVim/XVimMotionEvaluator.m index d1d832b9..d1c4804e 100644 --- a/XVim/XVimMotionEvaluator.m +++ b/XVim/XVimMotionEvaluator.m @@ -94,11 +94,11 @@ - (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ /////////////////////////////////////////// - (XVimEvaluator*)b{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)B{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, BIGWORD, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_BIGWORD, [self numericArg])]; } /* @@ -107,12 +107,12 @@ - (XVimEvaluator*)B{ */ - (XVimEvaluator*)e{ - XVimMotion* motion = XVIM_MAKE_MOTION(MOTION_END_OF_WORD_FORWARD, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, [self numericArg]); + XVimMotion* motion = XVIM_MAKE_MOTION(MOTION_END_OF_WORD_FORWARD, CHARACTERWISE_INCLUSIVE, MOPT_NONE, [self numericArg]); return [self _motionFixed:motion]; } - (XVimEvaluator*)E{ - XVimMotion* motion = XVIM_MAKE_MOTION(MOTION_END_OF_WORD_FORWARD, CHARACTERWISE_INCLUSIVE, BIGWORD, [self numericArg]); + XVimMotion* motion = XVIM_MAKE_MOTION(MOTION_END_OF_WORD_FORWARD, CHARACTERWISE_INCLUSIVE, MOPT_BIGWORD, [self numericArg]); return [self _motionFixed:motion]; } @@ -135,14 +135,14 @@ - (XVimEvaluator*)onComplete_fFtT:(XVimArgumentEvaluator*)childEvaluator{ - (XVimEvaluator*)f{ [self.argumentString appendString:@"f"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - _motion = [XVIM_MAKE_MOTION(MOTION_NEXT_CHARACTER, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 1) retain]; + _motion = [XVIM_MAKE_MOTION(MOTION_NEXT_CHARACTER, CHARACTERWISE_INCLUSIVE, MOPT_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } - (XVimEvaluator*)F{ [self.argumentString appendString:@"F"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - _motion = [XVIM_MAKE_MOTION(MOTION_PREV_CHARACTER, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1) retain]; + _motion = [XVIM_MAKE_MOTION(MOTION_PREV_CHARACTER, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } @@ -173,40 +173,40 @@ - (XVimEvaluator*)onComplete_g:(XVimGMotionEvaluator*)childEvaluator{ - (XVimEvaluator*)G{ XVimMotion *m; if ([self numericMode]){ - m = XVIM_MAKE_MOTION(MOTION_LINENUMBER, LINEWISE, LEFT_RIGHT_NOWRAP, [self numericArg]); + m = XVIM_MAKE_MOTION(MOTION_LINENUMBER, LINEWISE, MOPT_NOWRAP, [self numericArg]); m.line = [self numericArg]; }else{ - m = XVIM_MAKE_MOTION(MOTION_LASTLINE, LINEWISE, LEFT_RIGHT_NOWRAP, [self numericArg]); + m = XVIM_MAKE_MOTION(MOTION_LASTLINE, LINEWISE, MOPT_NOWRAP, [self numericArg]); } return [self _motionFixed:m]; } - (XVimEvaluator*)h{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BACKWARD, CHARACTERWISE_EXCLUSIVE, LEFT_RIGHT_NOWRAP, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NOWRAP, [self numericArg])]; } - (XVimEvaluator*)H{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_HOME, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_HOME, LINEWISE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)j{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)k{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_LINE_BACKWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_LINE_BACKWARD, LINEWISE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)l{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_FORWARD, CHARACTERWISE_EXCLUSIVE, LEFT_RIGHT_NOWRAP, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NOWRAP, [self numericArg])]; } - (XVimEvaluator*)L{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BOTTOM, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BOTTOM, LINEWISE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)M{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_MIDDLE, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_MIDDLE, LINEWISE, MOPT_NONE, [self numericArg])]; } @@ -240,14 +240,14 @@ - (XVimEvaluator*)C_u{ - (XVimEvaluator*)t{ [self.argumentString appendString:@"t"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - _motion = [XVIM_MAKE_MOTION(MOTION_TILL_NEXT_CHARACTER, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 1) retain]; + _motion = [XVIM_MAKE_MOTION(MOTION_TILL_NEXT_CHARACTER, CHARACTERWISE_INCLUSIVE, MOPT_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } - (XVimEvaluator*)T{ [self.argumentString appendString:@"T"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - _motion = [XVIM_MAKE_MOTION(MOTION_TILL_PREV_CHARACTER, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1) retain]; + _motion = [XVIM_MAKE_MOTION(MOTION_TILL_PREV_CHARACTER, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } @@ -269,11 +269,11 @@ - (XVimEvaluator*)C_v{ } - (XVimEvaluator*)w{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_FORWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)W{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_FORWARD, CHARACTERWISE_EXCLUSIVE, BIGWORD, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_BIGWORD, [self numericArg])]; } - (XVimEvaluator*)z{ @@ -282,18 +282,18 @@ - (XVimEvaluator*)z{ } - (XVimEvaluator*)NUM0{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BEGINNING_OF_LINE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BEGINNING_OF_LINE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)searchCurrentWordForward:(BOOL)forward { XVimCommandLineEvaluator* eval = [self searchEvaluatorForward:forward]; - NSRange r = [self.currentView xvim_currentWord:MOTION_OPTION_NONE]; + NSRange r = [self.currentView xvim_currentWord:MOPT_NONE]; if( r.location == NSNotFound ){ return nil; } // This is not for matching the searching word itself // Vim also does this behavior( when matched string is not found ) - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); m.position = r.location; [self.currentView moveCursorWithMotion:m]; @@ -349,7 +349,7 @@ - (XVimEvaluator*)jumpToMark:(XVimMark*)mark firstOfLine:(BOOL)fol{ [[XVim instance].marks setMark:cur_mark forName:@"'"]; } - XVimMotion* m =XVIM_MAKE_MOTION(MOTION_POSITION, motionType, MOTION_OPTION_NONE, self.numericArg); + XVimMotion* m =XVIM_MAKE_MOTION(MOTION_POSITION, motionType, MOPT_NONE, self.numericArg); m.position = to; return [self _motionFixed:m]; } @@ -392,11 +392,11 @@ - (XVimEvaluator*)onComplete_BACKQUOTE:(XVimArgumentEvaluator*)childEvaluator{ // CARET ( "^") moves the cursor to the start of the currentline (past leading whitespace) // Note: CARET always moves to start of the current line ignoring any numericArg. - (XVimEvaluator*)CARET{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_FIRST_NONBLANK, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_FIRST_NONBLANK, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)DOLLAR{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_END_OF_LINE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_END_OF_LINE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } // Underscore ( "_") moves the cursor to the start of the line (past leading whitespace) @@ -404,28 +404,15 @@ - (XVimEvaluator*)DOLLAR{ // it will moves to start of the numeric argument - 1 lines down. - (XVimEvaluator*)UNDERSCORE { - // TODO add this motion interface to NSTextView - XVimView *xview = self.currentView; - XVimBuffer *buffer = xview.buffer; - NSUInteger pos = xview.insertionPoint; - NSUInteger repeat = self.numericArg; - NSUInteger linesUpCursorloc = [buffer.textStorage nextLine:pos column:0 count:(repeat - 1) option:MOTION_OPTION_NONE]; - NSUInteger head = [buffer firstNonblankInLineAtIndex:linesUpCursorloc allowEOL:NO]; - if (NSNotFound == head && linesUpCursorloc != NSNotFound) { - head = linesUpCursorloc; - }else if(NSNotFound == head){ - head = pos; - } - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 0); - m.position = head; - return [self _motionFixed:m]; + NSUInteger repeat = self.numericArg - 1; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NEXT_FIRST_NONBLANK, LINEWISE, MOPT_NONE, repeat)]; } - (XVimEvaluator*)PERCENT { if( self.numericMode ){ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PERCENT, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PERCENT, LINEWISE, MOPT_NONE, [self numericArg])]; }else{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NEXT_MATCHED_ITEM, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NEXT_MATCHED_ITEM, CHARACTERWISE_INCLUSIVE, MOPT_NONE, [self numericArg])]; } } @@ -444,17 +431,21 @@ - (XVimEvaluator*)BS{ } - (XVimEvaluator*)PLUS{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NEXT_FIRST_NONBLANK, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NEXT_FIRST_NONBLANK, LINEWISE, MOPT_NONE, [self numericArg])]; } + /* - * CR (return) acts like PLUS in vi + * CR / ^M (return) acts like PLUS in vi */ +- (XVimEvaluator *)C_m{ + return [self PLUS]; +} - (XVimEvaluator*)CR{ return [self PLUS]; } - (XVimEvaluator*)MINUS{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PREV_FIRST_NONBLANK, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PREV_FIRST_NONBLANK, LINEWISE, MOPT_NONE, [self numericArg])]; } @@ -469,20 +460,20 @@ - (XVimEvaluator*)RSQUAREBRACKET{ } - (XVimEvaluator*)LBRACE{ // { - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PARAGRAPH_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PARAGRAPH_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)RBRACE{ // } - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PARAGRAPH_FORWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PARAGRAPH_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)LPARENTHESIS{ // ( - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_SENTENCE_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_SENTENCE_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)RPARENTHESIS{ // ) - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_SENTENCE_FORWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_SENTENCE_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)COMMA{ diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index a06b177c..86c7d2a4 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -183,7 +183,7 @@ - (XVimEvaluator*)I{ - (XVimEvaluator*)J{ XVimJoinEvaluator* eval = [[[XVimJoinEvaluator alloc] initWithWindow:self.window addSpace:YES] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, self.numericArg)]; } // Should be moved to XVimMotionEvaluator @@ -486,7 +486,7 @@ - (XVimEvaluator*)DOT{ - (XVimEvaluator*)TILDE{ [self.argumentString appendString:@"~"]; - XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg); + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, self.numericArg); [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_CASE]; [XVim.instance fixOperationCommands]; return nil; diff --git a/XVim/XVimSearch.m b/XVim/XVimSearch.m index 73f7fc0b..8a504219 100644 --- a/XVim/XVimSearch.m +++ b/XVim/XVimSearch.m @@ -46,10 +46,10 @@ - (XVimMotion*)motionForSearch:(NSString *)string forward:(BOOL)forward{ XVimMotion* m = nil; if( forward ){ XVim.instance.searcher.lastSearchBackword = NO; - m = XVIM_MAKE_MOTION(MOTION_SEARCH_FORWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + m = XVIM_MAKE_MOTION(MOTION_SEARCH_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); }else{ XVim.instance.searcher.lastSearchBackword = YES; - m = XVIM_MAKE_MOTION(MOTION_SEARCH_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + m = XVIM_MAKE_MOTION(MOTION_SEARCH_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); } m.regex = string; @@ -68,11 +68,11 @@ - (XVimMotion*)motionForSearch:(NSString *)string forward:(BOOL)forward{ } // The last case sensitiveness is found at this point if( options & NSRegularExpressionCaseInsensitive ){ - m.option |= SEARCH_CASEINSENSITIVE; + m.option |= MOPT_SEARCH_CASEINSENSITIVE; } if( [XVim instance].options.wrapscan ){ - m.option |= SEARCH_WRAP; + m.option |= MOPT_SEARCH_WRAP; } return m; @@ -357,11 +357,11 @@ - (NSRange)searchCurrentWordFrom:(NSUInteger)from forward:(BOOL)forward matchWho unichar lastChar = [string characterAtIndex:wordStart-1]; if ((isKeyword(curChar) && isKeyword(lastChar)) || (!isKeyword(curChar) && isNonblank(curChar) && !isKeyword(lastChar) && isNonblank(lastChar))){ - wordStart = [view.textStorage wordsBackward:searchStart count:1 option:LEFT_RIGHT_NOWRAP]; + wordStart = [view.textStorage wordsBackward:searchStart count:1 option:MOPT_NOWRAP]; } } - NSUInteger wordEnd = [view.textStorage wordsForward:wordStart count:1 option:LEFT_RIGHT_NOWRAP info:&info]; + NSUInteger wordEnd = [view.textStorage wordsForward:wordStart count:1 option:MOPT_NOWRAP info:&info]; if (info.lastEndOfWord != NSNotFound){ wordEnd = info.lastEndOfWord; } diff --git a/XVim/XVimShiftEvaluator.m b/XVim/XVimShiftEvaluator.m index 90380e46..cd71d8d2 100644 --- a/XVim/XVimShiftEvaluator.m +++ b/XVim/XVimShiftEvaluator.m @@ -31,7 +31,7 @@ - (XVimEvaluator*)GREATERTHAN { return nil; } - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } return nil; @@ -42,7 +42,7 @@ - (XVimEvaluator*)LESSTHAN{ if ([self numericArg] < 1) return nil; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } return nil; diff --git a/XVim/XVimSwapCharsEvaluator.m b/XVim/XVimSwapCharsEvaluator.m index 0ab3aad2..19e1146e 100644 --- a/XVim/XVimSwapCharsEvaluator.m +++ b/XVim/XVimSwapCharsEvaluator.m @@ -27,7 +27,7 @@ - (XVimEvaluator *)_doitLineWiseIfModeIs:(int)mode if (_mode != mode || [self numericArg] < 1) return nil; - XVimMotion *m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } diff --git a/XVim/XVimTextObjectEvaluator.m b/XVim/XVimTextObjectEvaluator.m index 22ffde9e..7359f502 100644 --- a/XVim/XVimTextObjectEvaluator.m +++ b/XVim/XVimTextObjectEvaluator.m @@ -31,8 +31,8 @@ - (void)dealloc{ } - (XVimMotion *)motion { - MOTION_OPTION opt = _inner ? TEXTOBJECT_INNER : MOTION_OPTION_NONE; - opt |= _bigword ? BIGWORD : MOTION_OPTION_NONE; + XVimMotionOptions opt = _inner ? MOPT_TEXTOBJECT_INNER : MOPT_NONE; + opt |= _bigword ? MOPT_BIGWORD : MOPT_NONE; return XVIM_MAKE_MOTION(_textobject, CHARACTERWISE_INCLUSIVE, opt, [self numericArg]); } diff --git a/XVim/XVimView.h b/XVim/XVimView.h index 5d67ec34..f2c0083c 100644 --- a/XVim/XVimView.h +++ b/XVim/XVimView.h @@ -117,11 +117,11 @@ - (void)xvim_setWrapsLines:(BOOL)wraps; - (void)xvim_hideCompletions; - (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)count - option:(MOTION_OPTION)opt forward:(BOOL)forward; -- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt; + option:(XVimMotionOptions)opt forward:(BOOL)forward; +- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(XVimMotionOptions)opt; +- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(XVimMotionOptions)opt; +- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(XVimMotionOptions)opt; - (void)xvim_clearHighlightText; -- (NSRange)xvim_currentWord:(MOTION_OPTION)opt; +- (NSRange)xvim_currentWord:(XVimMotionOptions)opt; @end diff --git a/XVim/XVimView.m b/XVim/XVimView.m index 27f204e3..5ea501d1 100644 --- a/XVim/XVimView.m +++ b/XVim/XVimView.m @@ -359,6 +359,7 @@ - (void)_xvim_statusChanged:(id)sender { if (!XVim.instance.disabled) { [self _syncStateFromView]; + [self scrollTo:_insertionPoint]; } [_textView setNeedsDisplay:YES]; } @@ -753,6 +754,31 @@ - (void)moveCursorToPosition:(XVimPosition)pos [self _syncState]; } +- (NSUInteger)_fixupMotionEnd:(NSUInteger)end buffer:(XVimBuffer *)buffer motion:(XVimMotion *)motion +{ + NSUInteger search = end; + NSRange r; + + if (end < buffer.length && [buffer.string characterAtIndex:end] == '#') { + search++; + } + + r = [(DVTSourceTextView *)_textView rangeOfPlaceholderFromCharacterIndex:search forward:NO wrap:NO limit:0]; + if (r.location != NSNotFound && end < NSMaxRange(r)) { + if (motion.motion == MOTION_FORWARD) { + end = NSMaxRange(r); + } else { + end = r.location; + } + } + + if (![buffer isNormalCursorPositionValidAtIndex:end]) { + motion.info->reachedEndOfLine = YES; + end--; + } + return end; +} + - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion { NSRange range = NSMakeRange(NSNotFound, 0); @@ -768,10 +794,12 @@ - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion // Do nothing break; case MOTION_FORWARD: - end = [ts next:begin count:motion.count option:motion.option info:motion.info]; + end = [buffer indexOfCharMotion:motion.scount index:begin options:motion.option]; + end = [self _fixupMotionEnd:end buffer:buffer motion:motion]; break; case MOTION_BACKWARD: - end = [ts prev:begin count:motion.count option:motion.option ]; + end = [buffer indexOfCharMotion:-motion.scount index:begin options:motion.option]; + end = [self _fixupMotionEnd:end buffer:buffer motion:motion]; break; case MOTION_WORD_FORWARD: end = [ts wordsForward:begin count:motion.count option:motion.option info:motion.info]; @@ -786,17 +814,16 @@ - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion end = [ts endOfWordsBackward:begin count:motion.count option:motion.option]; break; case MOTION_LINE_FORWARD: - end = [ts nextLine:begin column:_preservedColumn count:motion.count option:motion.option]; + end = [buffer indexOfLineMotion:motion.scount index:begin column:_preservedColumn]; break; case MOTION_LINE_BACKWARD: - end = [ts prevLine:begin column:_preservedColumn count:motion.count option:motion.option]; + end = [buffer indexOfLineMotion:-motion.scount index:begin column:_preservedColumn ]; break; case MOTION_BEGINNING_OF_LINE: end = [buffer startOfLine:begin]; break; case MOTION_END_OF_LINE: - end = [ts nextLine:begin column:0 count:motion.count - 1 option:MOTION_OPTION_NONE]; - end = [buffer endOfLine:end]; + end = [buffer indexOfLineMotion:motion.scount - 1 index:begin column:XVimSelectionEOL]; break; case MOTION_SENTENCE_FORWARD: end = [ts sentencesForward:begin count:motion.count option:motion.option]; @@ -811,36 +838,30 @@ - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion end = [ts moveFromIndex:begin paragraphs:-motion.scount option:motion.option]; break; case MOTION_NEXT_CHARACTER: - end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOPT_NONE]; break; case MOTION_PREV_CHARACTER: - end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOPT_NONE]; break; case MOTION_TILL_NEXT_CHARACTER: - end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOPT_NONE]; if (end != NSNotFound) { end--; } break; case MOTION_TILL_PREV_CHARACTER: - end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; + end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOPT_NONE]; if (end != NSNotFound) { end++; } break; case MOTION_NEXT_FIRST_NONBLANK: - end = [ts nextLine:begin column:0 count:motion.count option:motion.option]; - tmpPos = [buffer nextNonblankInLineAtIndex:end allowEOL:NO]; - if (NSNotFound != tmpPos) { - end = tmpPos; - } + end = [buffer indexOfLineMotion:motion.scount index:begin column:0]; + end = [buffer firstNonblankInLineAtIndex:end allowEOL:YES]; break; case MOTION_PREV_FIRST_NONBLANK: - end = [ts prevLine:begin column:0 count:motion.count option:motion.option]; - tmpPos = [buffer nextNonblankInLineAtIndex:end allowEOL:NO]; - if (NSNotFound != tmpPos) { - end = tmpPos; - } + end = [buffer indexOfLineMotion:-motion.scount index:begin column:0 ]; + end = [buffer firstNonblankInLineAtIndex:end allowEOL:YES]; break; case MOTION_FIRST_NONBLANK: end = [buffer firstNonblankInLineAtIndex:begin allowEOL:NO]; @@ -848,7 +869,7 @@ - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion case MOTION_LINENUMBER: end = [buffer indexOfLineNumber:motion.line column:_preservedColumn]; if (NSNotFound == end) { - end = [buffer indexOfLineAtIndex:buffer.length column:_preservedColumn]; + end = [buffer indexOfLineMotion:0 index:buffer.length column:_preservedColumn]; } break; case MOTION_PERCENT: @@ -858,7 +879,7 @@ - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion end = [ts positionOfMatchedPair:begin]; break; case MOTION_LASTLINE: - end = [buffer indexOfLineAtIndex:buffer.length column:_preservedColumn]; + end = [buffer indexOfLineMotion:0 index:buffer.length column:_preservedColumn]; break; case MOTION_HOME: tmpPos = [self lineNumberInScrollView:0.0 offset:motion.scount - 1]; @@ -882,7 +903,7 @@ - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion range = [ts currentWord:begin count:motion.count option:motion.option]; break; case TEXTOBJECT_BRACES: - range = xv_current_block(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '{', '}'); + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '{', '}'); break; case TEXTOBJECT_PARAGRAPH: // Not supported @@ -891,28 +912,28 @@ - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion range = NSMakeRange(start, end - start); break; case TEXTOBJECT_PARENTHESES: - range = xv_current_block(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '(', ')'); + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '(', ')'); break; case TEXTOBJECT_SENTENCE: // Not supported break; case TEXTOBJECT_ANGLEBRACKETS: - range = xv_current_block(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '<', '>'); + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '<', '>'); break; case TEXTOBJECT_SQUOTE: - range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '\''); + range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '\''); break; case TEXTOBJECT_DQUOTE: - range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '\"'); + range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '\"'); break; case TEXTOBJECT_TAG: // Not supported break; case TEXTOBJECT_BACKQUOTE: - range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '`'); + range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '`'); break; case TEXTOBJECT_SQUAREBRACKETS: - range = xv_current_block(buffer.string, current, motion.count, !(motion.option & TEXTOBJECT_INNER), '[', ']'); + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '[', ']'); break; case MOTION_LINE_COLUMN: end = [buffer indexOfLineNumber:motion.line column:motion.column]; @@ -1309,7 +1330,7 @@ - (void)doChange:(XVimMotion *)motion if (motion.motion == MOTION_WORD_FORWARD && [_textView.textStorage isNonblank:_insertionPoint]) { motion.motion = MOTION_END_OF_WORD_FORWARD; motion.type = CHARACTERWISE_INCLUSIVE; - motion.option |= MOTION_OPTION_CHANGE_WORD; + motion.option |= MOPT_CHANGE_WORD; } [buffer beginEditingAtIndex:_insertionPoint]; @@ -1398,7 +1419,7 @@ - (void)doPut:(NSString *)_text withType:(TEXT_TYPE)type afterCursor:(bool)after [buffer beginEditingAtIndex:_insertionPoint]; if (self.selectionMode != XVIM_VISUAL_NONE) { - [self doDelete:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 1) andYank:YES]; + [self doDelete:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, 1) andYank:YES]; after = NO; } @@ -1500,7 +1521,7 @@ - (void)doSwapCharacters:(XVimMotion *)motion mode:(int)mode NSRange range; if (motion.motion == MOTION_NONE) { - XVimMotion *m = XVIM_MAKE_MOTION(MOTION_FORWARD,CHARACTERWISE_EXCLUSIVE,LEFT_RIGHT_NOWRAP,motion.count); + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_FORWARD,CHARACTERWISE_EXCLUSIVE,MOPT_NOWRAP,motion.count); XVimRange r = [self _getMotionRange:undoPos motion:m]; if (r.end == NSNotFound) { @@ -1600,7 +1621,7 @@ - (void)_joinAtLineNumber:(NSUInteger)line } // Search in next line for the position to join(skip white spaces in next line) - NSUInteger posToJoin = [ts nextLine:headOfLine column:0 count:1 option:MOTION_OPTION_NONE]; + NSUInteger posToJoin = [buffer indexOfLineMotion:1 index:headOfLine column:0]; posToJoin = [buffer nextNonblankInLineAtIndex:posToJoin allowEOL:YES]; if (posToJoin < buffer.length && [buffer.string characterAtIndex:posToJoin] == ')') { @@ -2293,7 +2314,7 @@ - (void)xvim_hideCompletions #pragma mark Search - (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)count - option:(MOTION_OPTION)opt forward:(BOOL)forward + option:(XVimMotionOptions)opt forward:(BOOL)forward { NSTextStorage *ts = _textView.textStorage; NSRange range = NSMakeRange(NSNotFound,0); @@ -2309,17 +2330,17 @@ - (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)co } } -- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt +- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(XVimMotionOptions)opt { [self xvim_highlightNextSearchCandidate:regex count:count option:opt forward:YES]; } -- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt +- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(XVimMotionOptions)opt { [self xvim_highlightNextSearchCandidate:regex count:count option:opt forward:NO]; } -- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt +- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(XVimMotionOptions)opt { NSAssert( nil != pattern, @"pattern munst not be nil"); @@ -2328,7 +2349,7 @@ - (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt } NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines; - if (opt & SEARCH_CASEINSENSITIVE) { + if (opt & MOPT_SEARCH_CASEINSENSITIVE) { r_opts |= NSRegularExpressionCaseInsensitive; } @@ -2372,9 +2393,9 @@ - (void)xvim_clearHighlightText _needsUpdateFoundRanges = NO; } -- (NSRange)xvim_currentWord:(MOTION_OPTION)opt +- (NSRange)xvim_currentWord:(XVimMotionOptions)opt { - return [_textView.textStorage currentWord:_insertionPoint count:1 option:opt|TEXTOBJECT_INNER]; + return [_textView.textStorage currentWord:_insertionPoint count:1 option:opt|MOPT_TEXTOBJECT_INNER]; } @end diff --git a/XVim/XVimVisualEvaluator.m b/XVim/XVimVisualEvaluator.m index 4b86fadf..31e5a929 100644 --- a/XVim/XVimVisualEvaluator.m +++ b/XVim/XVimVisualEvaluator.m @@ -194,7 +194,7 @@ - (XVimEvaluator*)c{ return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_BLOCK_KILL] autorelease]; } XVimDeleteEvaluator* eval = [[[XVimDeleteEvaluator alloc] initWithWindow:self.window insertModeAtCompletion:YES] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1)]; } - (XVimEvaluator *)C{ @@ -220,7 +220,7 @@ - (XVimEvaluator*)C_d{ - (XVimEvaluator*)d{ XVimDeleteEvaluator* eval = [[[XVimDeleteEvaluator alloc] initWithWindow:self.window] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 0)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, 0)]; } - (XVimEvaluator *)DEL{ @@ -267,7 +267,7 @@ - (XVimEvaluator*)I{ - (XVimEvaluator*)J{ XVimJoinEvaluator* eval = [[[XVimJoinEvaluator alloc] initWithWindow:self.window addSpace:YES] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)m{ @@ -330,12 +330,12 @@ - (XVimEvaluator *)S{ - (XVimEvaluator*)u{ XVimSwapCharsEvaluator *eval = [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_LOWER] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)U{ XVimSwapCharsEvaluator *eval = [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_UPPER] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)C_u{ @@ -445,7 +445,7 @@ - (XVimEvaluator*)DQUOTE:(XVimWindow*)window{ - (XVimEvaluator*)EQUAL{ XVimEqualEvaluator* eval = [[[XVimEqualEvaluator alloc] initWithWindow:self.window] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)ESC{ @@ -490,12 +490,12 @@ - (XVimEvaluator *)EXCLAMATION{ - (XVimEvaluator*)GREATERTHAN{ XVimShiftEvaluator* eval = [[[XVimShiftEvaluator alloc] initWithWindow:self.window unshift:NO] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)LESSTHAN{ XVimShiftEvaluator* eval = [[[XVimShiftEvaluator alloc] initWithWindow:self.window unshift:YES] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)executeSearch:(XVimWindow*)window firstLetter:(NSString*)firstLetter { @@ -572,7 +572,7 @@ - (XVimEvaluator*)SLASH{ - (XVimEvaluator*)TILDE{ XVimSwapCharsEvaluator *eval = [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_CASE] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ diff --git a/XVim/XVimYankEvaluator.m b/XVim/XVimYankEvaluator.m index 051f66f5..2fba88fc 100644 --- a/XVim/XVimYankEvaluator.m +++ b/XVim/XVimYankEvaluator.m @@ -19,7 +19,7 @@ - (XVimEvaluator*)y{ if ([self numericArg] < 1) return nil; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } From 4d73d3931abc2999b238a4fc108d9f77f39ea20d Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Tue, 26 Nov 2013 18:58:11 +0100 Subject: [PATCH 37/44] Fix a few mistakes, and support movement | When normal-mode replace failed, it used to fail to quit insert mode, which broke several motions. Make -escapeFromInsert take a boolean to know whether in addition to quitting INSERT mode, we should also go backwards. Replace also had an off-by-one that prevented to replace the Last char of the line (a >= comparison really should be >). Add a BAR selector to the XVimMotionEvaluator, and a new MOTION_COLUMN_OF_LINE enum value. --- XVim/XVimBuffer.h | 9 +++++++++ XVim/XVimBuffer.m | 5 +++++ XVim/XVimInsertEvaluator.m | 12 ++++++++---- XVim/XVimMotion.h | 1 + XVim/XVimMotionEvaluator.m | 4 ++++ XVim/XVimView.h | 2 +- XVim/XVimView.m | 26 +++++++++++++++++++------- 7 files changed, 47 insertions(+), 12 deletions(-) diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 21cb6211..4d172c7d 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -231,6 +231,15 @@ */ - (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column; +/** @brief returns the index for the given line containing \a index and column. + * + * @returns + * Never returns NSNotfound + * If \a column is larger than the number of columns in that line, + * it returns the index of the endOfLine for that line + */ +- (NSUInteger)indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column; + #pragma mark Motion support functions /** @brief returns the index for a 'h/l' movement diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index 34e11e5c..e2afb326 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -396,6 +396,11 @@ - (NSUInteger)_indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column return xvim_sb_index(&sb); } +- (NSUInteger)indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column +{ + return [self _indexOfLineAtIndex:[self startOfLine:index] column:column]; +} + - (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column { NSUInteger index = [self indexOfLineNumber:num]; diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index c88bcc9e..c3f94ab5 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -213,7 +213,7 @@ - (void)didEndHandler{ [[XVim instance].marks setMark:mark forName:@"^"]; } - [xview escapeFromInsert]; + [xview escapeFromInsertAndMoveBack:YES]; // Position for "." is after escaped from insert mode pos = xview.insertionPosition; @@ -230,7 +230,9 @@ - (BOOL)windowShouldReceive:(SEL)keySelector { } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ + XVimView *xview = self.currentView; XVimEvaluator *nextEvaluator = self; + SEL keySelector = [keyStroke selectorForInstance:self]; if (keySelector){ nextEvaluator = [self performSelector:keySelector]; @@ -246,8 +248,10 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ NSEvent *event = [keyStroke toEventwithWindowNumber:0 context:nil]; if (_oneCharMode) { if (!keyStroke.isPrintable) { + [xview escapeFromInsertAndMoveBack:NO]; nextEvaluator = [XVimEvaluator invalidEvaluator]; - } else if (![self.currentView doReplaceCharacters:keyStroke.character count:[self numericArg]]) { + } else if (![xview doReplaceCharacters:keyStroke.character count:[self numericArg]]) { + [xview escapeFromInsertAndMoveBack:NO]; nextEvaluator = [XVimEvaluator invalidEvaluator]; }else{ nextEvaluator = nil; @@ -257,9 +261,9 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ // The input coming to this method is already handled by "Input Method" // and the input maight be non ascii like 'あ' if (keyStroke.isPrintable){ - [self.currentView.textView insertText:keyStroke.xvimString]; + [xview.textView insertText:keyStroke.xvimString]; }else{ - [self.currentView.textView interpretKeyEvents:[NSArray arrayWithObject:event]]; + [xview.textView interpretKeyEvents:[NSArray arrayWithObject:event]]; } } } diff --git a/XVim/XVimMotion.h b/XVim/XVimMotion.h index fbb37c64..5263166d 100644 --- a/XVim/XVimMotion.h +++ b/XVim/XVimMotion.h @@ -38,6 +38,7 @@ typedef enum _MOTION{ MOTION_LINE_FORWARD, // k MOTION_LINE_BACKWARD, // j MOTION_END_OF_LINE, // $ + MOTION_COLUMN_OF_LINE, // | MOTION_BEGINNING_OF_LINE, // 0 MOTION_SENTENCE_FORWARD, MOTION_SENTENCE_BACKWARD, diff --git a/XVim/XVimMotionEvaluator.m b/XVim/XVimMotionEvaluator.m index d1c4804e..6b10c1b5 100644 --- a/XVim/XVimMotionEvaluator.m +++ b/XVim/XVimMotionEvaluator.m @@ -285,6 +285,10 @@ - (XVimEvaluator*)NUM0{ return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BEGINNING_OF_LINE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, [self numericArg])]; } +- (XVimEvaluator*)BAR{ + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_COLUMN_OF_LINE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; +} + - (XVimEvaluator*)searchCurrentWordForward:(BOOL)forward { XVimCommandLineEvaluator* eval = [self searchEvaluatorForward:forward]; NSRange r = [self.currentView xvim_currentWord:MOPT_NONE]; diff --git a/XVim/XVimView.h b/XVim/XVimView.h index f2c0083c..6fb8c307 100644 --- a/XVim/XVimView.h +++ b/XVim/XVimView.h @@ -57,7 +57,7 @@ #pragma mark *** Visual Mode and Cursor Position *** -- (void)escapeFromInsert; +- (void)escapeFromInsertAndMoveBack:(BOOL)moveBack; - (void)selectSwapCorners:(BOOL)onSameLine; diff --git a/XVim/XVimView.m b/XVim/XVimView.m index 5ea501d1..ee73fb3a 100644 --- a/XVim/XVimView.m +++ b/XVim/XVimView.m @@ -676,11 +676,11 @@ - (void)selectSwapCorners:(BOOL)onSameLine [self _syncState]; } -- (void)escapeFromInsert +- (void)escapeFromInsertAndMoveBack:(BOOL)moveBack { if (_cursorMode == CURSOR_MODE_INSERT) { _cursorMode = CURSOR_MODE_COMMAND; - if (![self.buffer isIndexAtStartOfLine:_insertionPoint]) { + if (moveBack && ![self.buffer isIndexAtStartOfLine:_insertionPoint]) { [self _moveCursor:_insertionPoint - 1 preserveColumn:NO]; } [self _syncState]; @@ -817,11 +817,18 @@ - (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion end = [buffer indexOfLineMotion:motion.scount index:begin column:_preservedColumn]; break; case MOTION_LINE_BACKWARD: - end = [buffer indexOfLineMotion:-motion.scount index:begin column:_preservedColumn ]; + end = [buffer indexOfLineMotion:-motion.scount index:begin column:_preservedColumn]; break; case MOTION_BEGINNING_OF_LINE: end = [buffer startOfLine:begin]; break; + case MOTION_COLUMN_OF_LINE: + end = [buffer indexOfLineAtIndex:begin column:motion.count - 1]; + if (![buffer isNormalCursorPositionValidAtIndex:end]) { + motion.info->reachedEndOfLine = YES; + end--; + } + break; case MOTION_END_OF_LINE: end = [buffer indexOfLineMotion:motion.scount - 1 index:begin column:XVimSelectionEOL]; break; @@ -987,20 +994,25 @@ - (void)moveCursorWithMotion:(XVimMotion*)motion [self _moveCursor:r.end preserveColumn:NO]; } } else { + BOOL preserveColumn = YES; + switch (motion.motion) { + case MOTION_COLUMN_OF_LINE: + _preservedColumn = motion.count - 1; + break; case MOTION_END_OF_LINE: _preservedColumn = XVimSelectionEOL; - /* FALLTHROUGH */ + break; case MOTION_LINE_BACKWARD: case MOTION_LINE_FORWARD: case MOTION_LASTLINE: case MOTION_LINENUMBER: - [self _moveCursor:r.end preserveColumn:YES]; break; default: - [self _moveCursor:r.end preserveColumn:NO]; + preserveColumn = NO; break; } + [self _moveCursor:r.end preserveColumn:preserveColumn]; } [_textView setNeedsDisplay:YES]; [self _syncState]; @@ -1572,7 +1584,7 @@ - (BOOL)doReplaceCharacters:(unichar)c count:(NSUInteger)count return NO; } - if (_insertionPoint + count >= end) { + if (_insertionPoint + count > end) { return NO; } From 794f94d0f2a55d49de56d000856cdd3bbfd8c091 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Tue, 26 Nov 2013 18:59:47 +0100 Subject: [PATCH 38/44] Add UUID for XCode 5.0 DP --- XVim/Info_Xcode5.plist | 1 + 1 file changed, 1 insertion(+) diff --git a/XVim/Info_Xcode5.plist b/XVim/Info_Xcode5.plist index c8f3d5f8..40eff009 100644 --- a/XVim/Info_Xcode5.plist +++ b/XVim/Info_Xcode5.plist @@ -22,6 +22,7 @@ 37B30044-3B14-46BA-ABAA-F01000C27B63 63FC1C47-140D-42B0-BB4D-A10B2D225574 + 640F884E-CE55-4B40-87C0-8869546CAB7A NSHumanReadableCopyright Copyright © 2012 JugglerShu.Net. All rights reserved. From ec162f0928373eb49926ddbc431dca9e20ef424a Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Tue, 26 Nov 2013 19:09:06 +0100 Subject: [PATCH 39/44] Fixup position after undo The reason for that is that when undo is generated by XCode it can leave us in really awkward places. Example, go to the end of a line, type: C i. u <-- at this point, your cursor is on the end-of-line where it shouldn't be since we're in normal mode. When the undo are generated by us, this of course never happens (or would be a bug). --- XVim/XVimNormalEvaluator.m | 1 + 1 file changed, 1 insertion(+) diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index 86c7d2a4..6fe70869 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -287,6 +287,7 @@ - (XVimEvaluator*)u for (NSUInteger i = 0; i < [self numericArg]; i++) { [buffer.undoManager undo]; } + [self.currentView moveCursorToIndex:self.currentView.insertionPoint]; return nil; } From 832005e1a687e728e9ebb938522ec17a40653297 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Wed, 27 Nov 2013 00:11:29 +0100 Subject: [PATCH 40/44] This is now unused code --- XVim.xcodeproj/project.pbxproj | 8 ----- XVim/NSObject+ExtraData.h | 19 ------------ XVim/NSObject+ExtraData.m | 57 ---------------------------------- 3 files changed, 84 deletions(-) delete mode 100644 XVim/NSObject+ExtraData.h delete mode 100644 XVim/NSObject+ExtraData.m diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 1543cf33..3a4718ff 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -48,7 +48,6 @@ A26ACC4E154F2D6800B27D69 /* IDEEditor+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */; }; A26DC88615DF33C600779CB4 /* XVimTester.m in Sources */ = {isa = PBXBuildFile; fileRef = A26DC88515DF33C600779CB4 /* XVimTester.m */; }; A26E5F6417A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */; }; - A2702B0B16FEA313005ADD76 /* NSObject+ExtraData.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */; }; A2771E6B179EF520003B621E /* XVimTester+Issues.m in Sources */ = {isa = PBXBuildFile; fileRef = A2771E6A179EF520003B621E /* XVimTester+Issues.m */; }; A28D42F514FE87AF004BC121 /* XVimGMotionEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A28D42F414FE87AF004BC121 /* XVimGMotionEvaluator.m */; }; A28D42F814FE8E2A004BC121 /* XVimInsertEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A28D42F714FE8E2A004BC121 /* XVimInsertEvaluator.m */; }; @@ -100,7 +99,6 @@ A28F425B17EEDBC200A3F7AE /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C1BB5216CEAC7F0066F420 /* Utils.m */; }; A28F425C17EEDBC200A3F7AE /* XVimMark.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7416FB30B0000BE21C /* XVimMark.m */; }; A28F425D17EEDBC200A3F7AE /* XVimMarks.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7716FB4806000BE21C /* XVimMarks.m */; }; - A28F425F17EEDBC200A3F7AE /* NSObject+ExtraData.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */; }; A28F426017EEDBC200A3F7AE /* XVimUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = A22A009F1708914D0003046C /* XVimUtil.m */; }; A28F426117EEDBC200A3F7AE /* XVimTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = A22A00A21709CBE50003046C /* XVimTestCase.m */; }; A28F426217EEDBC200A3F7AE /* XVimTester+TextObject.m in Sources */ = {isa = PBXBuildFile; fileRef = A25753FF172F5F0E003D8A97 /* XVimTester+TextObject.m */; }; @@ -263,8 +261,6 @@ A26DC88515DF33C600779CB4 /* XVimTester.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimTester.m; path = XVim/XVimTester.m; sourceTree = SOURCE_ROOT; }; A26E5F6217A6BCAF0087C9B6 /* NSTextStorage+VimOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSTextStorage+VimOperation.h"; path = "XVim/NSTextStorage+VimOperation.h"; sourceTree = SOURCE_ROOT; }; A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSTextStorage+VimOperation.m"; path = "XVim/NSTextStorage+VimOperation.m"; sourceTree = SOURCE_ROOT; }; - A2702B0916FEA313005ADD76 /* NSObject+ExtraData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+ExtraData.h"; path = "XVim/NSObject+ExtraData.h"; sourceTree = ""; }; - A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+ExtraData.m"; path = "XVim/NSObject+ExtraData.m"; sourceTree = ""; }; A2771E6A179EF520003B621E /* XVimTester+Issues.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XVimTester+Issues.m"; path = "XVim/Test/XVimTester+Issues.m"; sourceTree = SOURCE_ROOT; }; A28D42F314FE87AF004BC121 /* XVimGMotionEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimGMotionEvaluator.h; path = XVim/XVimGMotionEvaluator.h; sourceTree = SOURCE_ROOT; }; A28D42F414FE87AF004BC121 /* XVimGMotionEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimGMotionEvaluator.m; path = XVim/XVimGMotionEvaluator.m; sourceTree = SOURCE_ROOT; }; @@ -574,8 +570,6 @@ children = ( A2C1BB5116CEAC7F0066F420 /* Utils.h */, A2C1BB5216CEAC7F0066F420 /* Utils.m */, - A2702B0916FEA313005ADD76 /* NSObject+ExtraData.h */, - A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */, ); name = Support; sourceTree = ""; @@ -885,7 +879,6 @@ A28F425C17EEDBC200A3F7AE /* XVimMark.m in Sources */, A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m in Sources */, A28F425D17EEDBC200A3F7AE /* XVimMarks.m in Sources */, - A28F425F17EEDBC200A3F7AE /* NSObject+ExtraData.m in Sources */, A28F426017EEDBC200A3F7AE /* XVimUtil.m in Sources */, A28F426117EEDBC200A3F7AE /* XVimTestCase.m in Sources */, A28F426217EEDBC200A3F7AE /* XVimTester+TextObject.m in Sources */, @@ -961,7 +954,6 @@ A2126B7516FB30B0000BE21C /* XVimMark.m in Sources */, A2F2621B182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m in Sources */, A2126B7816FB4806000BE21C /* XVimMarks.m in Sources */, - A2702B0B16FEA313005ADD76 /* NSObject+ExtraData.m in Sources */, 6EF220D6183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */, A22A00A01708914E0003046C /* XVimUtil.m in Sources */, A22A00A31709CBE50003046C /* XVimTestCase.m in Sources */, diff --git a/XVim/NSObject+ExtraData.h b/XVim/NSObject+ExtraData.h deleted file mode 100644 index 38aeea34..00000000 --- a/XVim/NSObject+ExtraData.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// NSObject+ExtraData.h -// XVim -// -// Created by Suzuki Shuichiro on 3/24/13. -// -// - -#import - -@interface NSObject (ExtraData) -- (id)dataForName:(NSString*)name; -- (void)setData:(id)data forName:(NSString*)name; - -// Utilities -- (void)setBool:(BOOL)b forName:(NSString*)name; -- (void)setUnsignedInteger:(NSUInteger)b forName:(NSString*)name; -- (void)setInteger:(NSInteger)b forName:(NSString *)name; -@end diff --git a/XVim/NSObject+ExtraData.m b/XVim/NSObject+ExtraData.m deleted file mode 100644 index 4635b2c4..00000000 --- a/XVim/NSObject+ExtraData.m +++ /dev/null @@ -1,57 +0,0 @@ -// -// NSObject+ExtraData.m -// XVim -// -// Created by Suzuki Shuichiro on 3/24/13. -// -// - -#import "NSObject+ExtraData.h" -#import -#import "Logger.h" - -static const NSString* EXTRA_DATA_KEY = @"EXTRADATAKEY"; -@implementation NSObject (ExtraData) - -- (id)dataForName:(NSString*)name{ - NSMutableDictionary* dic = objc_getAssociatedObject(self , EXTRA_DATA_KEY); - if( nil == dic ){ - return nil; - } - - id ret = [dic objectForKey:name]; - if( [NSNull null] == ret ){ - return nil; - }else{ - return ret; - } -} - -- (void)setData:(id)data forName:(NSString*)name{ - NSMutableDictionary* dic = objc_getAssociatedObject(self , EXTRA_DATA_KEY); - if( nil == dic ){ - dic = [NSMutableDictionary dictionary]; - objc_setAssociatedObject(self, EXTRA_DATA_KEY, dic, OBJC_ASSOCIATION_RETAIN); - } - - if( nil == data){ - data = [NSNull null]; - } - [dic setObject:data forKey:name]; -} - -- (void)setBool:(BOOL)b forName:(NSString *)name{ - NSNumber* n = [NSNumber numberWithBool:b]; - [self setData:n forName:name]; -} - -- (void)setUnsignedInteger:(NSUInteger)b forName:(NSString *)name{ - NSNumber* n = [NSNumber numberWithUnsignedInteger:b]; - [self setData:n forName:name]; -} - -- (void)setInteger:(NSInteger)b forName:(NSString *)name{ - NSNumber* n = [NSNumber numberWithInteger:b]; - [self setData:n forName:name]; -} -@end From 6528a7cffd1400dfa7b6a72c01ec8614c2341a7f Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Wed, 27 Nov 2013 09:14:16 +0100 Subject: [PATCH 41/44] Do not use straight @"\n" in the code Use the line endings we guess from the first line of the file. This, really fixes dd for me on CRLF files now. It also means we support Classic Mac line endings too (CR-ended lines) Also compute the end-of-line to eat when deleting/yanking at the end properly. This should make things better for #530 --- XVim/XVimBuffer.h | 1 + XVim/XVimBuffer.m | 28 ++++++++++++++++++++++++++++ XVim/XVimView.m | 37 ++++++++++++++++++++++--------------- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 4d172c7d..a13545c3 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -123,6 +123,7 @@ #pragma mark Properties @property (nonatomic, readonly) NSString *string; +@property (nonatomic, readonly) NSString *lineEnding; @property (nonatomic, readonly) NSUInteger numberOfLines; @property (nonatomic, readonly) NSUInteger length; @property (nonatomic, readonly) NSUInteger tabWidth; diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index e2afb326..0f71b07c 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -41,6 +41,7 @@ @implementation XVimBuffer { NSTextStorage *__unsafe_unretained _textStorage; XVimUndoOperation *_curOp; NSUInteger _editCount; + NSString *_lineEnding; struct { unsigned has_xvim_string : 1; @@ -111,6 +112,33 @@ - (NSString *)string return _textStorage.string; } +- (NSString *)lineEnding +{ + if (!_lineEnding) { + NSUInteger nlLen; + NSRange r; + + r = [self indexRangeForLineAtIndex:0 newLineLength:&nlLen]; + if (nlLen == 2) { + _lineEnding = @"\r\n"; + } else if (nlLen == 1) { + unichar c = [self.string characterAtIndex:NSMaxRange(r)]; + if (c == '\n') { + _lineEnding = @"\n"; + } else if (c == '\r') { + _lineEnding = @"\r"; + } else { + // WTF?? + return @"\n"; + } + } else { + // FIXME: use the setting + return @"\n"; + } + } + return _lineEnding; +} + - (NSUInteger)numberOfLines { if (_flags.has_xvim_numberOfLines) { diff --git a/XVim/XVimView.m b/XVim/XVimView.m index ee73fb3a..4203b792 100644 --- a/XVim/XVimView.m +++ b/XVim/XVimView.m @@ -1089,7 +1089,8 @@ - (void)__startYankWithType:(MOTION_TYPE)type - (void)_yankRange:(NSRange)range withType:(MOTION_TYPE)type { - NSString *string = self.buffer.string; + XVimBuffer *buffer = self.buffer; + NSString *string = buffer.string; NSString *s; BOOL needsNL; @@ -1099,10 +1100,10 @@ - (void)_yankRange:(NSRange)range withType:(MOTION_TYPE)type if (range.length) { s = [string substringWithRange:range]; if (needsNL && !isNewline([s characterAtIndex:s.length - 1])) { - s = [s stringByAppendingString:@"\n"]; + s = [s stringByAppendingString:buffer.lineEnding]; } } else if (needsNL) { - s = @"\n"; + s = buffer.lineEnding; } else { s = @""; } @@ -1179,7 +1180,7 @@ - (void)_yankSelection:(XVimSelection)sel } } } - [ybuf appendString:@"\n"]; + [ybuf appendString:buffer.lineEnding]; } [_lastYankedText release]; @@ -1274,10 +1275,13 @@ - (void)doDelete:(XVimMotion *)motion andYank:(BOOL)yank r = [self _getOperationRange:motionRange type:motion.type]; if (motion.type == LINEWISE && [buffer isIndexOnLastLine:motionRange.end]) { - if (r.location != 0) { + // eat the previous end of line as well + if (r.location > 0) { + NSUInteger endOfPreviousLine = [buffer endOfLine:r.location - 1]; + + r.length += r.location - endOfPreviousLine; + r.location = endOfPreviousLine; motion.info->deleteLastLine = YES; - r.location--; - r.length++; } } if (yank) { @@ -1395,9 +1399,11 @@ - (void)doYank:(XVimMotion*)motion } r = [self _getOperationRange:to type:motion.type]; if (motion.type == LINEWISE && to.end >= buffer.length && [buffer isIndexAtStartOfLine:to.end]) { - if (r.location != 0) { - r.location--; - r.length++; + if (r.location > 0) { + NSUInteger endOfPreviousLine = [buffer endOfLine:r.location - 1]; + + r.length += r.location - endOfPreviousLine; + r.location = endOfPreviousLine; } } [self _yankRange:r withType:motion.type]; @@ -1476,7 +1482,7 @@ - (void)doPut:(NSString *)_text withType:(TEXT_TYPE)type afterCursor:(bool)after NSUInteger insertPos = _insertionPoint; NSUInteger column = [buffer columnOfIndex:insertPos]; NSUInteger startLine = [buffer lineNumberAtIndex:insertPos]; - NSArray *lines = [text componentsSeparatedByString:@"\n"]; + NSArray *lines = [text componentsSeparatedByString:buffer.lineEnding]; for (NSUInteger i = 0 ; i < lines.count ; i++) { NSString *line = [lines objectAtIndex:i]; @@ -1485,7 +1491,7 @@ - (void)doPut:(NSString *)_text withType:(TEXT_TYPE)type afterCursor:(bool)after if (NSNotFound == head) { NSAssert( targetLine != 0, @"This should not be happen"); - [buffer replaceCharactersInRange:NSMakeRange(buffer.length, 0) withString:@"\n"]; + [buffer replaceCharactersInRange:NSMakeRange(buffer.length, 0) withString:buffer.lineEnding]; head = buffer.length; } NSAssert(NSNotFound != head, @"Head of the target line must be found at this point"); @@ -1783,7 +1789,7 @@ - (void)_insertNewlineBelowCurrentLine _insertionPoint = pos; [buffer beginEditingAtIndex:_insertionPoint]; pos = [buffer endOfLine:pos]; - [buffer replaceCharactersInRange:NSMakeRange(pos, 0) withString:@"\n"]; + [buffer replaceCharactersInRange:NSMakeRange(pos, 0) withString:buffer.lineEnding]; [buffer endEditingAtIndex:_insertionPoint]; [self _moveCursor:pos + 1 preserveColumn:NO]; @@ -1800,7 +1806,7 @@ - (void)_insertNewlineAboveLine:(NSUInteger)line } if (line == 1) { [buffer beginEditingAtIndex:0]; - [buffer replaceCharactersInRange:NSMakeRange(0, 0) withString:@"\n"]; + [buffer replaceCharactersInRange:NSMakeRange(0, 0) withString:buffer.lineEnding]; [buffer endEditingAtIndex:0]; } else { _insertionPoint = pos; @@ -2012,7 +2018,8 @@ - (void)doSortLines:(XVimRange)range withOptions:(XVimSortOptions)options [lines removeObjectsAtIndexes:removeIndices]; } - NSString *str = [[lines componentsJoinedByString:@"\n"] stringByAppendingString:@"\n"]; + NSString *nl = buffer.lineEnding; + NSString *str = [[lines componentsJoinedByString:nl] stringByAppendingString:nl]; pos = characterRange.location; [buffer beginEditingAtIndex:pos]; From 934c8a298accb25f63758d11f03c43f9e6c3d812 Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Wed, 27 Nov 2013 22:35:16 +0100 Subject: [PATCH 42/44] Add some comments --- XVim/XVimView.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/XVim/XVimView.h b/XVim/XVimView.h index 6fb8c307..5a01f73b 100644 --- a/XVim/XVimView.h +++ b/XVim/XVimView.h @@ -28,6 +28,14 @@ - (void)textView:(NSTextView*)view didDelete:(NSString*)deletedText withType:(TEXT_TYPE)type; @end +/** @brief XVimView class, representing one XVim View + * + * An XVim View is more or less what a window is in vim, namely a view + * on a given buffer (TextStorage). You can have several views on the same buffer. + * + * This is actually grafted on top of an NSTextView that it hooks to give it + * vim-like capabilities. + */ @interface XVimView : NSObject @property (readonly, nonatomic) XVimWindow *window; @property (readonly, nonatomic) NSTextView *textView; From 38e71dba35fb0d7514601e4de71b60a59397db4f Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Thu, 28 Nov 2013 00:33:38 +0100 Subject: [PATCH 43/44] Improve performance and correctness of the event handling code Reduce the number of calls on each keystroke by avoiding the -selectorForInstance + instanceResponds + ... calls Avoid using NSStrings to compute the selector, it does malloc()s, a simple buffer + sel_getUid() is faster Use the NS*FunctionKey values from Foundation instead of hardcoded values. And now my XVim stops assert()ing when I use F13 by mistake. Avoid using self.{modifier,character} but access the ivars directly, it's faster. Use iswprint() instead of isPrintable, it's supposed to do what's right... --- XVim/XVimCommandLineEvaluator.m | 5 +- XVim/XVimEvaluator.m | 7 +- XVim/XVimInsertEvaluator.m | 21 +- XVim/XVimKeyStroke.h | 16 - XVim/XVimKeyStroke.m | 709 ++++++++++++++++---------------- XVim/XVimMarkSetEvaluator.m | 18 +- XVim/XVimMotionEvaluator.m | 2 +- XVim/XVimNormalEvaluator.m | 2 +- XVim/XVimNumericEvaluator.m | 36 +- XVim/XVimRegisterEvaluator.m | 4 +- 10 files changed, 402 insertions(+), 418 deletions(-) diff --git a/XVim/XVimCommandLineEvaluator.m b/XVim/XVimCommandLineEvaluator.m index 95f2d49c..a000c6d6 100644 --- a/XVim/XVimCommandLineEvaluator.m +++ b/XVim/XVimCommandLineEvaluator.m @@ -110,8 +110,9 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ XVimEvaluator* next = self; XVimCommandField *commandField = self.window.commandLine.commandField; - if ([keyStroke instanceResponds:self]) { - next = [self performSelector:[keyStroke selector]]; + SEL sel = keyStroke.selector; + if ([self respondsToSelector:sel]) { + next = [self performSelector:sel]; } else{ [commandField handleKeyStroke:keyStroke inWindow:self.window]; diff --git a/XVim/XVimEvaluator.m b/XVim/XVimEvaluator.m index c2162d39..4a301ba8 100644 --- a/XVim/XVimEvaluator.m +++ b/XVim/XVimEvaluator.m @@ -94,12 +94,11 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ // Invokes each key event handler // invokes "C_k:" selector - SEL handler = [keyStroke selectorForInstance:self]; - if (handler) { + SEL handler = keyStroke.selector; + if ([self respondsToSelector:handler]) { TRACE_LOG(@"Calling SELECTOR %@", NSStringFromSelector(handler)); return [self performSelector:handler]; - } - else{ + } else { TRACE_LOG(@"SELECTOR %@ not found", NSStringFromSelector(handler)); return [self defaultNextEvaluator]; } diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index c3f94ab5..854371fc 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -233,17 +233,20 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ XVimView *xview = self.currentView; XVimEvaluator *nextEvaluator = self; - SEL keySelector = [keyStroke selectorForInstance:self]; - if (keySelector){ + SEL keySelector = keyStroke.selector; + if ([self respondsToSelector:keySelector]) { nextEvaluator = [self performSelector:keySelector]; - }else if(self.movementKeyPressed){ - // Flag movement key as not pressed until the next movement key is pressed - self.movementKeyPressed = NO; - - // Store off the new start range - self.startRange = self.currentView.textView.selectedRange; + } else { + if(self.movementKeyPressed) { + // Flag movement key as not pressed until the next movement key is pressed + self.movementKeyPressed = NO; + + // Store off the new start range + self.startRange = self.currentView.textView.selectedRange; + } + keySelector = nil; } - + if (nextEvaluator == self && nil == keySelector){ NSEvent *event = [keyStroke toEventwithWindowNumber:0 context:nil]; if (_oneCharMode) { diff --git a/XVim/XVimKeyStroke.h b/XVim/XVimKeyStroke.h index eb59042e..33d1788e 100644 --- a/XVim/XVimKeyStroke.h +++ b/XVim/XVimKeyStroke.h @@ -18,7 +18,6 @@ XVimString* XVimStringFromKeyStrokes(NSArray* strokes); NSArray* XVimKeyStrokesFromXVimString(XVimString* string); NSArray* XVimKeyStrokesFromKeyNotation(NSString* notation); NSString* XVimKeyNotationFromXVimString(XVimString* string); -BOOL isPrintable(unichar c); @interface NSEvent(XVimKeyStroke) - (XVimKeyStroke*)toXVimKeyStroke; @@ -38,27 +37,12 @@ BOOL isPrintable(unichar c); // Generates an event from this key stroke - (NSEvent*)toEventwithWindowNumber:(NSInteger)num context:(NSGraphicsContext*)context; -// Creates the selector string from this key stroke -- (NSString*)toSelectorString; - // Creates a human-readable string - (NSString*)keyNotation; // Returns the selector for this object - (SEL)selector; -// Returns a selector for the target for this key stroke if one exists -- (SEL)selectorForInstance:(id)target; - -// Returns YES if the instance responds to this key stroke -- (BOOL)instanceResponds:(id)target; - -// Returns YES if the class' instances respond to this key stroke -- (BOOL)classResponds:(Class)class; - -// Returns YES if the class implements this method and does so different to its superclass -- (BOOL)classImplements:(Class)class; - // Following methods are for to be a key in NSDictionary - (NSUInteger)hash; - (BOOL)isEqual:(id)object; diff --git a/XVim/XVimKeyStroke.m b/XVim/XVimKeyStroke.m index 1172cde3..c037e110 100644 --- a/XVim/XVimKeyStroke.m +++ b/XVim/XVimKeyStroke.m @@ -4,21 +4,23 @@ // Copyright (c) 2012 __MyCompanyName__. All rights reserved. // +#import +#import #import "XVimKeyStroke.h" #import "NSEvent+VimHelper.h" #import "Logger.h" - +#import "XVimStringBuffer.h" /* XVimString and Key Notation - + (keymap.h file in Vim source helps understand this better) - + XVim uses internal string encoding as Vim does. - This encoding is not same as any usual character code because + This encoding is not same as any usual character code because the encoding include special flags with characters like modifiers. - + In Vim they treat all the input as a character. This is how recordings or keymapping does work. Vim uses internal code(character) to express an input(usually a key stroke). @@ -26,27 +28,27 @@ Vim uses internal code(character) to express an input(usually a key stroke). If the key is special like 'backspace' internal expression is 0x80, 'k', 'b' (3byte). Vim uses 0x80 as a escape character and followng bytes defines the key. Special keys like F1-F10 or S-F1 are all mapped to a 0x80 prefixed value. - + Vim always does following convertings. [Phisical Key Input] -> [Vim intarnal code] [Key Notation in key map] -> [Vim internal code] Key Notatation here is like or . - + And Vim interprets the [Vim intarnal code] as input and takes action. Printable codes are all stayed same. So if you record 'iabc' the internal code in Vim is 'iabc0x80kb'. You can see this when you see the recorded register with :registers - + If the key stroke has modifier Vim uses additional byte to represent the modifiers. - For example for Alt-F2 Vim internal code is like + For example for Alt-F2 Vim internal code is like 0x80,0xfc(252),0x08,0x80,k,2 where - 0x80,0xfc(252) means it has modifier flag and - 0x08 means the modifier is Alt - 0x80,k,2 is internal code for F2 - + As Vim does XVim have internal code. - XVim follows Vim way but the values we use is different. And also we use + XVim follows Vim way but the values we use is different. And also we use unichar(2bytes) instead of char in Vim This means... - Character betwee 0xF800 to 0xF8FF describes modifier flags with lower byte @@ -54,18 +56,18 @@ Vim uses internal code(character) to express an input(usually a key stroke). - For special keys like F1, arrow keys XVim does not use the same code with Vim. Cocoa defines unichar value for them so we use it instead. See or AppKit/NSEvent.h file - + So normal keys like 'a' is just 0x0061 but 'Alt+a' will be 0xF808,0x0061 (4bytes) where 0xF808 represents Alt. - + Note that all the key sequences are represented as array of unichar which is 2byte. You have to be careful about endian. So the value 'Alt+a' will be 0x08 0xF8 0x61 0x00 in byte sequence(each unichar endian is little endian) This makes easy to handle key sequence as NSString. - + Terminology: XVimString - The internal key code explained above. Notation - Key input represented by readable string like or ... - + XVimKeyStroke class: This class represents a key input. You always can convert XVimString <-> XVimKeyStroke(s). @@ -74,9 +76,9 @@ So when you handle XVimString you can convert it into XVimKeyStroke(s) and use i actuall character or modifier flag values. XVimString can represents "Sequence" of key input but XVimKeyStroke represents only one key stroke. So if XVimString has several key input it will be converted into array of XVimKeyStorke. - + In other words you can serialize/deserialize XVimKeyStroke(s) with XVimString - + Modifier Flags: Following bit mask for modifiers is from NSEvent.h We do not use this mask because modifier mask must be fits in 1 byte length. @@ -119,270 +121,284 @@ So when you handle XVimString you can convert it into XVimKeyStroke(s) and use i // If multiple key expressions are mapped to one char code // Put default key expression at the end of the same keys. // The last one will be used when converting charcode -> key expression. - {@"NUL", 0, @"NUL"}, - {@"SOH", 1, @"SOH"}, - {@"STX", 2, @"STX"}, - {@"ETX", 3, @"ETX" }, - {@"EOT", 4, @"EOT"}, - {@"ENQ", 5, @"ENQ"}, - {@"ACK", 6, @"ACK"}, - {@"BEL", 7, @"BEL"}, - {@"BS", 8, @"BS" }, - {@"HT", 9, @"TAB"}, - {@"TAB", 9, @"TAB"}, // Default notation - {@"NL", 10, @"NL"}, - {@"VT", 11, @"VT"}, - {@"NP", 12, @"NP"}, - {@"RETURN", 13, @"CR"}, - {@"ENTER", 13, @"CR"}, - {@"CR", 13, @"CR"}, // Default notation - {@"SO", 14, @"SO"}, - {@"SI", 15, @"SI"}, - {@"DLE", 16, @"DLE"}, - {@"DC1", 17, @"DC1"}, - {@"DC2", 18, @"DC2"}, - {@"DC3", 19, @"DC3"}, - {@"DC4", 20, @"DC4"}, - {@"NAK", 21, @"NAK"}, - {@"SYN", 22, @"SYN"}, - {@"ETB", 23, @"ETB"}, - {@"CAN", 24, @"CAN"}, - {@"EM", 25, @"EM"}, - {@"SUB", 26, @"SUB"}, - {@"ESC", 27, @"ESC"}, - {@"FS", 28, @"FS"}, - {@"GS", 29, @"GS"}, - {@"RS", 30, @"RS"}, - {@"US", 31, @"US"}, - {@"SPACE", 32, @"SPACE"}, - {@" ", 32, @"SPACE"}, // Default notation - {@"!", 33, @"EXCLAMATION"}, - {@"\"", 34, @"DQUOTE"}, - {@"#", 35, @"NUMBER"}, - {@"$", 36, @"DOLLAR"}, - {@"%", 37, @"PERCENT"}, - {@"&", 38, @"AMPASAND"}, - {@"'", 39, @"SQUOTE"}, - {@"(", 40, @"LPARENTHESIS"}, - {@")", 41, @"RPARENTHESIS"}, - {@"*", 42, @"ASTERISK"}, - {@"+", 43, @"PLUS"}, - {@",", 44, @"COMMA"}, - {@"-", 45, @"MINUS"}, - {@".", 46, @"DOT"}, - {@"/", 47, @"SLASH"}, - {@"0", 48, @"NUM0"}, - {@"1", 49, @"NUM1"}, - {@"2", 50, @"NUM2"}, - {@"3", 51, @"NUM3"}, - {@"4", 52, @"NUM4"}, - {@"5", 53, @"NUM5"}, - {@"6", 54, @"NUM6"}, - {@"7", 55, @"NUM7"}, - {@"8", 56, @"NUM8"}, - {@"9", 57, @"NUM9"}, - {@":", 58, @"COLON"}, - {@";", 59, @"SEMICOLON"}, - {@"LT",60, @"LESSTHAN"}, - {@"<", 60, @"LESSTHAN"}, // Default notation - {@"=", 61, @"EQUAL"}, - {@">", 62, @"GREATERTHAN"}, - {@"?", 63, @"QUESTION"}, - {@"@", 64, @"AT"}, - {@"A", 65, @"A" }, - {@"B", 66, @"B"}, - {@"C", 67, @"C"}, - {@"D", 68, @"D"}, - {@"E", 69, @"E"}, - {@"F", 70, @"F"}, - {@"G", 71, @"G"}, - {@"H", 72, @"H"}, - {@"I", 73, @"I"}, - {@"J", 74, @"J"}, - {@"K", 75, @"K"}, - {@"L", 76, @"L"}, - {@"M", 77, @"M"}, - {@"N", 78, @"N"}, - {@"O", 79, @"O"}, - {@"P", 80, @"P"}, - {@"Q", 81, @"Q"}, - {@"R", 82, @"R"}, - {@"S", 83, @"S"}, - {@"T", 84, @"T"}, - {@"U", 85, @"U"}, - {@"V", 86, @"V"}, - {@"W", 87, @"W"}, - {@"X", 88, @"X"}, - {@"Y", 89, @"Y"}, - {@"Z", 90, @"Z"}, - {@"[", 91, @"LSQUAREBRACKET"}, - {@"BSLASH", 92, @"BACKSLASH"}, - {@"\\",92, @"BACKSLASH"}, // Default noattion - {@"]",93, @"RSQUAREBRACKET"}, - {@"^",94, @"CARET"}, - {@"_",95, @"UNDERSCORE"}, - {@"`",96, @"BACKQUOTE"}, - {@"a",97, @"a"}, - {@"b",98, @"b"}, - {@"c",99, @"c"}, - {@"d",100, @"d"}, - {@"e",101, @"e"}, - {@"f",102, @"f"}, - {@"g",103, @"g"}, - {@"h",104, @"h"}, - {@"i",105, @"i"}, - {@"j",106, @"j"}, - {@"k",107, @"k"}, - {@"l",108, @"l"}, - {@"m",109, @"m"}, - {@"n",110, @"n"}, - {@"o",111, @"o"}, - {@"p",112, @"p"}, - {@"q",113, @"q"}, - {@"r",114, @"r"}, - {@"s",115, @"s"}, - {@"t",116, @"t"}, - {@"u",117, @"u"}, - {@"v",118, @"v"}, - {@"w",119, @"w"}, - {@"x",120, @"x"}, - {@"y",121, @"y"}, - {@"z",122, @"z"}, - {@"{",123, @"LBRACE"}, - {@"BAR",124, @"BAR"}, - {@"|",124, @"BAR"}, // Default notation - {@"}",125, @"RBRACE"}, - {@"~",126, @"TILDE"}, - {@"BS",127, @"BS"}, - {@"UP",63232, @"Up"}, - {@"DOWN", 63233, @"Down"}, - {@"LEFT", 63234, @"Left"}, - {@"RIGHT", 63235, @"Right"}, - {@"F1", 63236, @"F1"}, - {@"F2", 63237, @"F2"}, - {@"F3", 63238, @"F3"}, - {@"F4", 63239, @"F4"}, - {@"F5", 63240, @"F5"}, - {@"F6", 63241, @"F6"}, - {@"F7", 63242, @"F7"}, - {@"F8", 63243, @"F8"}, - {@"F9", 63244, @"F9"}, - {@"F10", 63245, @"F10"}, - {@"F11", 63246, @"F11"}, - {@"F12", 63247, @"F12"}, - {@"DEL", 63272, @"DEL"}, - {@"HOME", 63273, @"Home"}, - {@"END", 63275, @"End"}, - {@"PAGEUP", 63276, @"Pageup"}, - {@"PAGEDOWN", 63277, @"Pagedown"} + { @"NUL", 0, @"NUL"}, + { @"SOH", 1, @"SOH"}, + { @"STX", 2, @"STX"}, + { @"ETX", 3, @"ETX" }, + { @"EOT", 4, @"EOT"}, + { @"ENQ", 5, @"ENQ"}, + { @"ACK", 6, @"ACK"}, + { @"BEL", 7, @"BEL"}, + { @"BS", 8, @"BS" }, + { @"HT", 9, @"TAB"}, + { @"TAB", 9, @"TAB"}, // Default notation + { @"NL", 10, @"NL"}, + { @"VT", 11, @"VT"}, + { @"NP", 12, @"NP"}, + { @"RETURN", 13, @"CR"}, + { @"ENTER", 13, @"CR"}, + { @"CR", 13, @"CR"}, // Default notation + { @"SO", 14, @"SO"}, + { @"SI", 15, @"SI"}, + { @"DLE", 16, @"DLE"}, + { @"DC1", 17, @"DC1"}, + { @"DC2", 18, @"DC2"}, + { @"DC3", 19, @"DC3"}, + { @"DC4", 20, @"DC4"}, + { @"NAK", 21, @"NAK"}, + { @"SYN", 22, @"SYN"}, + { @"ETB", 23, @"ETB"}, + { @"CAN", 24, @"CAN"}, + { @"EM", 25, @"EM"}, + { @"SUB", 26, @"SUB"}, + { @"ESC", 27, @"ESC"}, + { @"FS", 28, @"FS"}, + { @"GS", 29, @"GS"}, + { @"RS", 30, @"RS"}, + { @"US", 31, @"US"}, + { @"SPACE", 32, @"SPACE"}, + { @" ", 32, @"SPACE"}, // Default notation + { @"!", 33, @"EXCLAMATION"}, + { @"\"", 34, @"DQUOTE"}, + { @"#", 35, @"NUMBER"}, + { @"$", 36, @"DOLLAR"}, + { @"%", 37, @"PERCENT"}, + { @"&", 38, @"AMPASAND"}, + { @"'", 39, @"SQUOTE"}, + { @"(", 40, @"LPARENTHESIS"}, + { @")", 41, @"RPARENTHESIS"}, + { @"*", 42, @"ASTERISK"}, + { @"+", 43, @"PLUS"}, + { @",", 44, @"COMMA"}, + { @"-", 45, @"MINUS"}, + { @".", 46, @"DOT"}, + { @"/", 47, @"SLASH"}, + { @"0", 48, @"NUM0"}, + { @"1", 49, @"NUM1"}, + { @"2", 50, @"NUM2"}, + { @"3", 51, @"NUM3"}, + { @"4", 52, @"NUM4"}, + { @"5", 53, @"NUM5"}, + { @"6", 54, @"NUM6"}, + { @"7", 55, @"NUM7"}, + { @"8", 56, @"NUM8"}, + { @"9", 57, @"NUM9"}, + { @":", 58, @"COLON"}, + { @";", 59, @"SEMICOLON"}, + { @"LT", 60, @"LESSTHAN"}, + { @"<", 60, @"LESSTHAN"}, // Default notation + { @"=", 61, @"EQUAL"}, + { @">", 62, @"GREATERTHAN"}, + { @"?", 63, @"QUESTION"}, + { @"@", 64, @"AT"}, + { @"[", 91, @"LSQUAREBRACKET"}, + { @"BSLASH", 92, @"BACKSLASH"}, + { @"\\", 92, @"BACKSLASH"}, // Default noattion + { @"]", 93, @"RSQUAREBRACKET"}, + { @"^", 94, @"CARET"}, + { @"_", 95, @"UNDERSCORE"}, + { @"`", 96, @"BACKQUOTE"}, + { @"{", 123, @"LBRACE"}, + { @"BAR", 124, @"BAR"}, + { @"|", 124, @"BAR"}, // Default notation + { @"}", 125, @"RBRACE"}, + { @"~", 126, @"TILDE"}, + { @"BS", 127, @"BS"}, + + { @"UP", NSUpArrowFunctionKey, @"Up" }, + { @"DOWN", NSDownArrowFunctionKey, @"Down" }, + { @"LEFT", NSLeftArrowFunctionKey, @"Left" }, + { @"RIGHT", NSRightArrowFunctionKey, @"Right" }, + { @"F1", NSF1FunctionKey, @"F1" }, + { @"F2", NSF2FunctionKey, @"F2" }, + { @"F3", NSF3FunctionKey, @"F3" }, + { @"F4", NSF4FunctionKey, @"F4" }, + { @"F5", NSF5FunctionKey, @"F5" }, + { @"F6", NSF6FunctionKey, @"F6" }, + { @"F7", NSF7FunctionKey, @"F7" }, + { @"F8", NSF8FunctionKey, @"F8" }, + { @"F9", NSF9FunctionKey, @"F9" }, + { @"F10", NSF10FunctionKey, @"F10" }, + { @"F11", NSF11FunctionKey, @"F11" }, + { @"F12", NSF12FunctionKey, @"F12" }, + { @"F13", NSF13FunctionKey, @"F13" }, + { @"F14", NSF14FunctionKey, @"F14" }, + { @"F15", NSF15FunctionKey, @"F15" }, + { @"F16", NSF16FunctionKey, @"F16" }, + { @"F17", NSF17FunctionKey, @"F17" }, + { @"F18", NSF18FunctionKey, @"F18" }, + { @"F19", NSF19FunctionKey, @"F19" }, + { @"F20", NSF20FunctionKey, @"F20" }, + { @"F21", NSF21FunctionKey, @"F21" }, + { @"F22", NSF22FunctionKey, @"F22" }, + { @"F23", NSF23FunctionKey, @"F23" }, + { @"F24", NSF24FunctionKey, @"F24" }, + { @"F25", NSF25FunctionKey, @"F25" }, + { @"F26", NSF26FunctionKey, @"F26" }, + { @"F27", NSF27FunctionKey, @"F27" }, + { @"F28", NSF28FunctionKey, @"F28" }, + { @"F29", NSF29FunctionKey, @"F29" }, + { @"F30", NSF30FunctionKey, @"F30" }, + { @"F31", NSF31FunctionKey, @"F31" }, + { @"F32", NSF32FunctionKey, @"F32" }, + { @"F33", NSF33FunctionKey, @"F33" }, + { @"F34", NSF34FunctionKey, @"F34" }, + { @"F35", NSF35FunctionKey, @"F35" }, + { @"INS", NSInsertFunctionKey, @"Insert" }, + + { @"DEL", NSDeleteFunctionKey, @"DEL" }, + { @"HOME", NSHomeFunctionKey, @"Home" }, + { @"BEGIN", NSBeginFunctionKey, @"Begin" }, + { @"END", NSEndFunctionKey, @"End" }, + { @"PGUP", NSPageUpFunctionKey, @"Pageup" }, + { @"PGDN", NSPageDownFunctionKey, @"Pagedown" }, + { @"PRINTSCREEN", NSPrintScreenFunctionKey, @"PrintScreen" }, + { @"SCREENLOCK", NSScrollLockFunctionKey, @"ScrLock" }, + { @"PAUSE", NSPauseFunctionKey, @"Pause" }, + { @"SYSREQ", NSSysReqFunctionKey, @"SysReq" }, + { @"BREAK", NSBreakFunctionKey, @"Break" }, + { @"RESET", NSResetFunctionKey, @"Reset" }, + { @"STOP", NSStopFunctionKey, @"Stop" }, + { @"MENU", NSMenuFunctionKey, @"Menu" }, + { @"USER", NSUserFunctionKey, @"User" }, + { @"SYSTEM", NSSystemFunctionKey, @"System" }, + { @"PRINT", NSPrintFunctionKey, @"Print" }, + { @"CLEARLINE", NSClearLineFunctionKey, @"ClearLine" }, + { @"CLEARDISPLAY", NSClearDisplayFunctionKey, @"ClearDisplay" }, + { @"INSLINE", NSInsertLineFunctionKey, @"InsLine" }, + { @"DELLINE", NSDeleteLineFunctionKey, @"DelLine" }, + { @"INSCHAR", NSInsertCharFunctionKey, @"InsChar" }, + { @"DELCHAR", NSDeleteCharFunctionKey, @"DelChar" }, + { @"PREV", NSPrevFunctionKey, @"Prev" }, + { @"NEXT", NSNextFunctionKey, @"Next" }, + { @"SELECT", NSSelectFunctionKey, @"Select" }, + { @"EXECUTE", NSExecuteFunctionKey, @"Execute" }, + { @"UNDO", NSUndoFunctionKey, @"Undo" }, + { @"REDO", NSRedoFunctionKey, @"Redo" }, + { @"FIND", NSFindFunctionKey, @"Find" }, + { @"HELP", NSHelpFunctionKey, @"Help" }, + { @"MODESWITCH", NSModeSwitchFunctionKey, @"ModeSwitch" }, + + { nil, 0, nil }, }; static NSMutableDictionary *s_unicharToSelector = nil; static NSMutableDictionary *s_keyToUnichar = nil; static NSMutableDictionary *s_unicharToKey= nil; +static locale_t s_locale; -static void initUnicharToSelector(){ - if( nil == s_unicharToSelector ){ +NS_INLINE void init_maps(void) +{ + static dispatch_once_t once; + dispatch_once(&once, ^{ s_unicharToSelector = [[NSMutableDictionary alloc] init]; // Never release - for( unsigned int i = 0; i < sizeof(key_maps)/sizeof(struct key_map); i++ ){ - [s_unicharToSelector setObject:key_maps[i].selector forKey:[NSNumber numberWithUnsignedInteger:key_maps[i].c]]; + s_keyToUnichar = [[NSMutableDictionary alloc] init]; // Never release + s_unicharToKey= [[NSMutableDictionary alloc] init]; // Never release + + for (NSUInteger i = 0; key_maps[i].key; i++) { + NSNumber *c = @(key_maps[i].c); + NSString *key = key_maps[i].key; + NSString *sel = key_maps[i].selector; + + [s_unicharToSelector setObject:sel forKey:c]; + [s_keyToUnichar setObject:c forKey:key]; + [s_unicharToKey setObject:key forKey:c]; + // any UTF-8 works because we ask for iswprint() or wcwidth() + s_locale = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", NULL); } - } + }); } -static void initKeyToUnichar(){ - if( nil == s_keyToUnichar){ - s_keyToUnichar = [[NSMutableDictionary alloc] init]; // Never release - for( unsigned int i = 0; i < sizeof(key_maps)/sizeof(struct key_map); i++ ){ - [s_keyToUnichar setObject:[NSNumber numberWithUnsignedInteger:key_maps[i].c] forKey:key_maps[i].key]; - } - } +NS_INLINE BOOL isNSFunctionKey(unichar c) +{ + // see NSEvent.h: OpenStep reserves the range 0xF700-0xF8FF for Function Keys + return 0xF700 <= c && c < 0xF900; } -static void initUnicharToKey(){ - if( nil == s_unicharToKey){ - s_unicharToKey= [[NSMutableDictionary alloc] init]; // Never release - for( unsigned int i = 0; i < sizeof(key_maps)/sizeof(struct key_map); i++ ){ - [s_unicharToKey setObject:key_maps[i].key forKey:[NSNumber numberWithUnsignedInteger:key_maps[i].c]]; - } - } +NS_INLINE BOOL isPrintable(unichar c) +{ + init_maps(); + + return !isNSFunctionKey(c) && iswprint_l(c, s_locale); } -static BOOL isValidKey(NSString* key){ - if( nil == s_keyToUnichar ){ - initKeyToUnichar(); +NS_INLINE BOOL isValidKey(NSString *key) +{ + init_maps(); + + if (key.length == 0) { + return NO; } - // Notations like , are all case insensitive - if( [key length] > 1 ){ - key = [key uppercaseString]; + if (key.length == 1) { + return isPrintable([key characterAtIndex:0]); } - return nil != [s_keyToUnichar objectForKey:key]; -} -static NSString* selectorFromUnichar(unichar c){ - if( nil == s_unicharToSelector ){ - initUnicharToSelector(); - } - return [s_unicharToSelector objectForKey:[NSNumber numberWithUnsignedInteger:c]]; + return [s_keyToUnichar objectForKey:key.uppercaseString] != 0; } -static unichar unicharFromKey(NSString* key){ - if( nil == s_keyToUnichar ){ - initKeyToUnichar(); - } - - if( !isValidKey(key) ){ +NS_INLINE unichar unicharFromKey(NSString *key) +{ + init_maps(); + + if (key.length == 0) { return (unichar)-1; - }else{ - if( [key length] > 1){ - key = [key uppercaseString]; - } - return [[s_keyToUnichar objectForKey:key] unsignedIntegerValue]; } -} + if (key.length == 1) { + unichar c = [key characterAtIndex:0]; -static NSString* keyFromUnichar(unichar c){ - if( nil == s_unicharToKey){ - initUnicharToKey(); + return isPrintable(c) ? c : (unichar)-1; } - return [s_unicharToKey objectForKey:[NSNumber numberWithUnsignedInteger:c]]; + + return [[s_keyToUnichar objectForKey:key.uppercaseString] unsignedIntegerValue]; } -BOOL isPrintable(unichar c){ - // FIXME: - // There may be better difinition of printable characters in unicode - if( c < 32 || c == 127 || ( 63232 <= c && c <= 63277 ) ){ - return NO; +NS_INLINE NSString *keyFromUnichar(unichar c) +{ + init_maps(); + + NSString *key = [s_unicharToKey objectForKey:@(c)]; + if (key) { + return key; + } + if (isPrintable(c)) { + return [NSString stringWithCharacters:&c length:1]; } - return YES; + return @"?"; } -static BOOL isModifier(unichar c){ - return ( XVIM_MODIFIER_MIN <= c && c <= XVIM_MODIFIER_MAX ); +NS_INLINE BOOL isModifier(unichar c) +{ + return (XVIM_MODIFIER_MIN <= c && c <= XVIM_MODIFIER_MAX); } -static XVimString* MakeXVimString( unichar character, unsigned short modifier){ - NSMutableString* str = [[[NSMutableString alloc] init] autorelease]; +static XVimString *MakeXVimString(unichar character, unsigned short modifier) +{ + NSMutableString *str = [[[NSMutableString alloc] init] autorelease]; + + init_maps(); + // If the character is pritable we do not consider Shift modifier // For example and ! is same - if( isPrintable(character) ){ + if (isPrintable(character)) { modifier = modifier & ~XVIM_MOD_SHIFT; } - if( modifier != 0 ){ + if (modifier != 0) { [str appendFormat:@"%C", XVIM_MAKE_MODIFIER(modifier)]; } [str appendFormat:@"%C", character]; return str; } -static XVimString* XVimStringFromKeyNotationImpl(NSString* string, NSUInteger* index){ - NSUInteger starti = *index; - - NSUInteger modifierFlags = 0; +static XVimString *XVimStringFromKeyNotationImpl(NSString *string, NSUInteger *index) +{ + NSUInteger starti = *index; + + NSUInteger modifierFlags = 0; NSUInteger p = starti; NSUInteger length = [string length]; - if ([string characterAtIndex:starti] == '<') { - // Find modifier flags, if any + + if ([string characterAtIndex:starti] == '<') { + // Find modifier flags, if any p += 1; // skip first '<' letter NSRange keyEnd = [string rangeOfString:@">" options:0 range:NSMakeRange(p, length-p)]; if( keyEnd.location != NSNotFound ){ @@ -410,28 +426,28 @@ static BOOL isModifier(unichar c){ } p+=2; } - - NSString* key = [string substringWithRange:NSMakeRange(p, keyEnd.location-p)]; - if( isValidKey(key) ){ - if( 0 == modifierFlags ){ + + NSString *key = [string substringWithRange:NSMakeRange(p, keyEnd.location-p)]; + if (isValidKey(key)) { + if (0 == modifierFlags) { //If it does not have modifier flag the key must be multiple letters - if( [key length] > 1 ){ + if ([key length] > 1) { *index = keyEnd.location+1; unichar c = unicharFromKey(key); - return MakeXVimString( c, modifierFlags); + return MakeXVimString(c, modifierFlags); } - }else{ + } else { //This is modifier flag + valid key *index = keyEnd.location+1; unichar c = unicharFromKey(key); - return MakeXVimString( c, modifierFlags); + return MakeXVimString(c, modifierFlags); } } } // if it not valid key like "" or "" take first letter "<" as a key // Just go through. } - + // Simple one letter key NSString* key = [string substringWithRange:NSMakeRange(starti, 1)]; unichar c = unicharFromKey(key); @@ -440,16 +456,16 @@ static BOOL isModifier(unichar c){ } XVimString* XVimStringFromKeyNotation(NSString* notation){ - NSUInteger index = 0; - NSUInteger len = notation.length; + NSUInteger index = 0; + NSUInteger len = notation.length; NSMutableString* str = [[[NSMutableString alloc] init] autorelease]; - while (index < len){ + while (index < len){ XVimString* oneKey = XVimStringFromKeyNotationImpl(notation, &index); - if( oneKey == nil ){ + if( oneKey == nil ){ break; } [str appendString:oneKey]; - } + } return str; } @@ -473,7 +489,7 @@ static BOOL isModifier(unichar c){ c2 = c1; c1 = 0; } - + XVimKeyStroke* stroke = [[[XVimKeyStroke alloc] initWithCharacter:c2 modifier:c1] autorelease]; [array addObject:stroke]; } @@ -500,11 +516,15 @@ - (XVimKeyStroke*)toXVimKeyStroke{ return nil; } unichar c = [[self charactersIgnoringModifiers] characterAtIndex:0]; - // We unset NSFunctionKeyMask bit for function keys (7F00 and above) NSUInteger mod = self.modifierFlags; - if( c >= 0x7F00 ){ + if (isNSFunctionKey(c)) { + // We unset NSFunctionKeyMask bit for function keys (7F00 and above) mod &= (NSUInteger)~NSFunctionKeyMask; } + if (c == 0x19 && (mod & NSDeviceIndependentModifierFlagsMask) == NSShiftKeyMask) { + // S-EM really is S-Tab + c = '\t'; + } mod = NSMOD2XVIMMOD(mod); return [[[XVimKeyStroke alloc] initWithCharacter:c modifier:(unsigned char)mod] autorelease]; } @@ -517,52 +537,53 @@ - (XVimString*)toXVimString{ @implementation XVimKeyStroke -@synthesize character, modifier; +@synthesize character = _character, modifier = _modifier; + ++ (void)initialize +{ + init_maps(); +} - (id)initWithCharacter:(unichar)c modifier:(unsigned char)mod{ if( self = [super init] ){ - self.character = c; - self.modifier = mod; + _character = c; + _modifier = mod; } return self; } - (XVimString*)xvimString{ - return MakeXVimString(self.character, self.modifier); + return MakeXVimString(_character, _modifier); } - (BOOL) isNumeric{ - if( self.modifier == 0 && ( '0' <= self.character && self.character <= '9' ) ){ - return YES; - }else{ - return NO; - } + return _modifier == 0 && ('0' <= _character && _character <= '9'); } - (NSUInteger)hash{ - return self.modifier + self.character; + return _modifier + _character; } - (BOOL)isEqual:(id)object{ - if (object == self) { - return YES; - } - if (!object || ![object isKindOfClass:[self class]]){ - return NO; - } - XVimKeyStroke* other = object; - return self.character == other.character && self.modifier== other.modifier; + if (object == self) { + return YES; + } + if (!object || ![object isKindOfClass:[self class]]){ + return NO; + } + XVimKeyStroke* other = object; + return _character == other.character && _modifier== other.modifier; } - (id)copyWithZone:(NSZone *)zone { - return [[XVimKeyStroke allocWithZone:zone] initWithCharacter:self.character modifier:self.modifier]; + return [[XVimKeyStroke allocWithZone:zone] initWithCharacter:_character modifier:_modifier]; } - (NSEvent*)toEventwithWindowNumber:(NSInteger)num context:(NSGraphicsContext*)context; { - unichar c = self.character; + unichar c = _character; NSString *characters = [NSString stringWithCharacters:&c length:1]; - NSUInteger mflags = XVIMMOD2NSMOD(self.modifier); - + NSUInteger mflags = XVIMMOD2NSMOD(_modifier); + return [NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0, 0) modifierFlags:mflags @@ -571,18 +592,18 @@ - (NSEvent*)toEventwithWindowNumber:(NSInteger)num context:(NSGraphicsContext*)c context:context characters:characters charactersIgnoringModifiers:characters - isARepeat:NO + isARepeat:NO keyCode:0]; } - (NSString*)description{ NSMutableString *str = [[NSMutableString alloc] init]; - if (0 != self.modifier) { - [str appendFormat:@"mod{0x%02x 0x%02x} ", KS_MODIFIER, self.modifier]; + if (0 != _modifier) { + [str appendFormat:@"mod{0x%02x 0x%02x} ", KS_MODIFIER, _modifier]; } - unichar c = self.character; + unichar c = _character; if (isPrintable(c)) { [str appendFormat:@"code{%C} ", c]; }else{ @@ -595,95 +616,75 @@ - (NSString*)description{ - (BOOL)isPrintable { - return !self.modifier && isPrintable(self.character); + return !_modifier && isPrintable(_character); } - (NSString*)keyNotation{ - NSMutableString *keyStr = [[NSMutableString alloc] init]; - unichar charcode = self.character; + NSMutableString *keyStr = [[NSMutableString alloc] init]; + unichar charcode = _character; - if (self.modifier || !isPrintable(charcode)) { + if (_modifier || !isPrintable(charcode)) { [keyStr appendString:@"<"]; } - - if (self.modifier & XVIM_MOD_SHIFT) { - [keyStr appendString:@"S-"]; - } - if (self.modifier & XVIM_MOD_CTRL) { - [keyStr appendString:@"C-"]; - } - if (self.modifier & XVIM_MOD_ALT) { - [keyStr appendString:@"M-"]; - } - if (self.modifier & XVIM_MOD_CMD) { - [keyStr appendString:@"D-"]; - } - if (self.modifier & XVIM_MOD_FUNC) { - [keyStr appendString:@"F-"]; - } + + if (_modifier & XVIM_MOD_SHIFT) { + [keyStr appendString:@"S-"]; + } + if (_modifier & XVIM_MOD_CTRL) { + [keyStr appendString:@"C-"]; + } + if (_modifier & XVIM_MOD_ALT) { + [keyStr appendString:@"M-"]; + } + if (_modifier & XVIM_MOD_CMD) { + [keyStr appendString:@"D-"]; + } + if (_modifier & XVIM_MOD_FUNC) { + [keyStr appendString:@"F-"]; + } [keyStr appendString:keyFromUnichar(charcode)]; - - if (self.modifier || !isPrintable(charcode)) { + + if (_modifier || !isPrintable(charcode)) { [keyStr appendString:@">"]; } return [keyStr autorelease]; } -- (NSString*) toSelectorString { - // S- Shift - // C- Control - // M- Option - // D- Command - // F_ Function (not F1,F2.. but 'Function' key) - NSMutableString* keyStr = [[[NSMutableString alloc] init] autorelease]; - if( self.modifier & XVIM_MOD_SHIFT){ - [keyStr appendString:@"S_"]; - } - if( self.modifier & XVIM_MOD_CTRL){ - [keyStr appendString:@"C_"]; - } - if( self.modifier & XVIM_MOD_ALT){ - [keyStr appendString:@"M_"]; - } - if( self.modifier & XVIM_MOD_CMD){ - [keyStr appendString:@"D_"]; - } - if( self.modifier & XVIM_MOD_FUNC){ - [keyStr appendString:@"F_"]; - } - - NSString *keyname = selectorFromUnichar(self.character); - if (keyname) { - [keyStr appendString:keyname]; - } - - return keyStr; -} - -- (SEL)selector { - return NSSelectorFromString([self toSelectorString]); -} +- (SEL)selector +{ + char buf[128]; + int pos = 0; -- (SEL)selectorForInstance:(id)target { - if( [target respondsToSelector:self.selector] ){ - return self.selector; - }else{ - return nil; + if (_modifier & XVIM_MOD_SHIFT) { + buf[pos++] = 'S'; buf[pos++] = '_'; + } + if (_modifier & XVIM_MOD_CTRL) { + buf[pos++] = 'C'; buf[pos++] = '_'; + } + if (_modifier & XVIM_MOD_ALT) { + buf[pos++] = 'M'; buf[pos++] = '_'; + } + if (_modifier & XVIM_MOD_CMD) { + buf[pos++] = 'D'; buf[pos++] = '_'; + } + if (_modifier & XVIM_MOD_FUNC) { + buf[pos++] = 'F'; buf[pos++] = '_'; } -} -- (BOOL)instanceResponds:(id)target { - return [self selectorForInstance:target] != nil; -} + if ((_character >= 'a' && _character <= 'z') || (_character >= 'A' && _character <= 'Z')) { + buf[pos++] = _character; + buf[pos++] = '\0'; + } else { + NSString *keyname = [s_unicharToSelector objectForKey:@(_character)]; -- (BOOL)classResponds:(Class)class{ - return [class instancesRespondToSelector:self.selector]; -} + if (!keyname) { + return @selector(__invalid_selector_name__); + } + strcpy(buf + pos, keyname.UTF8String); + } -- (BOOL)classImplements:(Class)class { - IMP imp = [class instanceMethodForSelector:self.selector]; - return imp && imp != [[class superclass] instanceMethodForSelector:self.selector]; + return sel_getUid(buf); } -@end \ No newline at end of file +@end diff --git a/XVim/XVimMarkSetEvaluator.m b/XVim/XVimMarkSetEvaluator.m index 665794e1..61af3420 100644 --- a/XVim/XVimMarkSetEvaluator.m +++ b/XVim/XVimMarkSetEvaluator.m @@ -25,11 +25,20 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ XVimView *xview = self.currentView; XVimBuffer *buffer = self.window.currentBuffer; - NSString *keyStr = [keyStroke toSelectorString]; - if ([keyStr length] != 1) { + if (keyStroke.modifier) { return [XVimEvaluator invalidEvaluator]; } - + switch (keyStroke.character) { + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': + case '\'': case '"': + case '[': case ']': + break; + default: + return [XVimEvaluator invalidEvaluator]; + } + XVimMark *mark = [[[XVimMark alloc] init] autorelease]; XVimPosition pos = xview.insertionPosition; @@ -37,7 +46,8 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ mark.column = pos.column; mark.document = buffer.document.fileURL.path; if (nil != mark.document) { - [[XVim instance].marks setMark:mark forName:keyStr]; + unichar c = keyStroke.character; + [[XVim instance].marks setMark:mark forName:[NSString stringWithCharacters:&c length:1]]; } return nil; } diff --git a/XVim/XVimMotionEvaluator.m b/XVim/XVimMotionEvaluator.m index 6b10c1b5..4b232b74 100644 --- a/XVim/XVimMotionEvaluator.m +++ b/XVim/XVimMotionEvaluator.m @@ -161,7 +161,7 @@ - (XVimEvaluator*)g{ } - (XVimEvaluator*)onComplete_g:(XVimGMotionEvaluator*)childEvaluator{ - if( [childEvaluator.key.toSelectorString isEqualToString:@"SEMICOLON"] ){ + if( childEvaluator.key.selector == @selector(SEMICOLON) ){ XVimMark* mark = [[XVim instance].marks markForName:@"." forDocument:self.window.currentBuffer.document]; return [self jumpToMark:mark firstOfLine:NO]; }else{ diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index 6fe70869..f361d84f 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -161,7 +161,7 @@ - (XVimEvaluator*)g{ } - (XVimEvaluator*)onComplete_g:(XVimGActionEvaluator*)childEvaluator{ - if( [childEvaluator.key.toSelectorString isEqualToString:@"SEMICOLON"] ){ + if (childEvaluator.key.selector == @selector(SEMICOLON)) { XVimMark* mark = [[XVim instance].marks markForName:@"." forDocument:self.window.currentBuffer.document]; return [self jumpToMark:mark firstOfLine:NO]; }else{ diff --git a/XVim/XVimNumericEvaluator.m b/XVim/XVimNumericEvaluator.m index 708cd221..622924bb 100644 --- a/XVim/XVimNumericEvaluator.m +++ b/XVim/XVimNumericEvaluator.m @@ -8,40 +8,26 @@ #import "XVimNumericEvaluator.h" #import "XVimKeyStroke.h" +#import "NSString+VimHelper.h" @implementation XVimNumericEvaluator - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ - NSString* keyStr = [keyStroke toSelectorString]; - if (keyStroke.isNumeric) { - if (self.numericMode) { - NSString* numStr = [keyStr substringFromIndex:3]; - NSUInteger n = (NSUInteger)[numStr integerValue]; - NSUInteger newHead = self.numericArg; - // prevent integer overflow - if(newHead <= floor((NSUIntegerMax - n) / 10)){ - newHead*=10; - newHead+=n; - self.numericArg = newHead; - [self.argumentString appendString:numStr]; + unichar buf[4] = { 'N', 'U', 'M', keyStroke.character }; + NSUInteger digit = buf[3] - '0'; + + if (self.numericMode || digit) { + NSUInteger n = self.numericMode ? self.numericArg : 0; + + self.numericMode = YES; + if (n <= NSUIntegerMax / 10) { + self.numericArg = 10 * n + digit; + [self.argumentString appendCharacters:buf length:4]; } return self; } - else{ - if( [keyStr isEqualToString:@"NUM0"] ){ - // Nothing to do - // Maybe handled by XVimNormalEvaluator - }else{ - NSString* numStr = [keyStr substringFromIndex:3]; - NSUInteger n = (NSUInteger)[numStr integerValue]; - self.numericArg = n; - [self.argumentString appendString:numStr]; - self.numericMode = YES; - return self; - } - } } return [super eval:keyStroke]; diff --git a/XVim/XVimRegisterEvaluator.m b/XVim/XVimRegisterEvaluator.m index 2067e80c..bfa49201 100644 --- a/XVim/XVimRegisterEvaluator.m +++ b/XVim/XVimRegisterEvaluator.m @@ -26,8 +26,8 @@ - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ - SEL handler = [keyStroke selectorForInstance:self]; - if (handler){ + SEL handler = keyStroke.selector; + if ([self respondsToSelector:handler]) { TRACE_LOG(@"Calling SELECTOR %@", NSStringFromSelector(handler)); return [self performSelector:handler]; } From e11416addbda6d246d6fee0428d8e0f085bb27df Mon Sep 17 00:00:00 2001 From: John AppleSeed Date: Sun, 1 Dec 2013 14:46:38 +0100 Subject: [PATCH 44/44] Do not swizzle KV Observing and -dealloc on NSTextView both sound like a very bad idea, move that stuff to the XVimView where it belongs. The less we swizzle, the better we are. --- XVim/XVimView.m | 67 +++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/XVim/XVimView.m b/XVim/XVimView.m index 4203b792..23054ac4 100644 --- a/XVim/XVimView.m +++ b/XVim/XVimView.m @@ -49,8 +49,6 @@ + (void)xvim_initialize #define swizzle(sel) \ [self xvim_swizzleInstanceMethod:@selector(sel) with:@selector(xvim_##sel)] - swizzle(dealloc); - swizzle(setSelectedRanges:affinity:stillSelecting:); swizzle(selectAll:); swizzle(paste:); @@ -62,7 +60,6 @@ + (void)xvim_initialize swizzle(drawInsertionPointInRect:color:turnedOn:); swizzle(didChangeText); swizzle(viewDidMoveToSuperview); - swizzle(observeValueForKeyPath:ofObject:change:context:); } #undef swizzle @@ -78,33 +75,6 @@ - (XVimView *)xvim_makeXVimViewInWindow:(XVimWindow *)window return [[[XVimView alloc] initWithView:self window:window] autorelease]; } -- (void)xvim_setupForXVimView:(XVimView *)view -{ - if (!self.xvim_view) { - [XVim.instance.options addObserver:self forKeyPath:@"hlsearch" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.options addObserver:self forKeyPath:@"ignorecase" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.searcher addObserver:self forKeyPath:@"lastSearchString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - } - objc_setAssociatedObject(self, XVIM_KEY_VIEW, view, OBJC_ASSOCIATION_RETAIN); -} - -- (void)xvim_dealloc -{ - if (self.xvim_view) { - @try { - [XVim.instance.options removeObserver:self forKeyPath:@"hlsearch"]; - [XVim.instance.options removeObserver:self forKeyPath:@"ignorecase"]; - [XVim.instance.searcher removeObserver:self forKeyPath:@"lastSearchString"]; - } - @catch (NSException* exception){ - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - } - [self xvim_dealloc]; -} - - - (void)xvim_setSelectedRanges:(NSArray *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)flag @@ -287,15 +257,6 @@ - (void)xvim_viewDidMoveToSuperview } } -- (void)xvim_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object - change:(NSDictionary *)change context:(void *)context -{ - if ([@[ @"ignorecase", @"hlsearch", @"lastSearchString"] containsObject:keyPath]) { - self.xvim_view.needsUpdateFoundRanges = YES; - [self setNeedsDisplayInRect:self.visibleRect avoidAdditionalLayout:YES]; - } -} - @end @implementation XVimView { @@ -335,12 +296,18 @@ - (instancetype)initWithView:(NSTextView *)view window:(XVimWindow *)window _textView = view; _window = [window retain]; - [view xvim_setupForXVimView:self]; + [self _xvim_statusChanged:nil]; + [XVim.instance.options addObserver:self forKeyPath:@"hlsearch" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + [XVim.instance.options addObserver:self forKeyPath:@"ignorecase" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + [XVim.instance.searcher addObserver:self forKeyPath:@"lastSearchString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_xvim_statusChanged:) name:XVimEnabledStatusChangedNotification object:nil]; + + objc_setAssociatedObject(view, XVIM_KEY_VIEW, self, OBJC_ASSOCIATION_RETAIN); } return self; } @@ -348,13 +315,31 @@ - (instancetype)initWithView:(NSTextView *)view window:(XVimWindow *)window - (void)dealloc { DEBUG_LOG("View %p deleted", self); - [[NSNotificationCenter defaultCenter] removeObserver:self]; + @try { + [XVim.instance.options removeObserver:self forKeyPath:@"hlsearch"]; + [XVim.instance.options removeObserver:self forKeyPath:@"ignorecase"]; + [XVim.instance.searcher removeObserver:self forKeyPath:@"lastSearchString"]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + } + @catch (NSException* exception){ + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } [_foundRanges release]; [_lastYankedText release]; [_window release]; [super dealloc]; } +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary *)change context:(void *)context +{ + if ([@[ @"ignorecase", @"hlsearch", @"lastSearchString"] containsObject:keyPath]) { + _needsUpdateFoundRanges = YES; + [_textView setNeedsDisplayInRect:_textView.visibleRect avoidAdditionalLayout:YES]; + } +} + - (void)_xvim_statusChanged:(id)sender { if (!XVim.instance.disabled) {