From e685ef3581e1405d67bc85eb965751f40b4fa481 Mon Sep 17 00:00:00 2001 From: Tae Won Ha Date: Fri, 1 Jul 2016 21:24:42 +0200 Subject: [PATCH] Try to correctly render marked text --- NeoVimXpc/NeoVimXpc.h | 2 +- NeoVimXpc/NeoVimXpcImpl.h | 2 - NeoVimXpc/NeoVimXpcImpl.m | 281 +++++++++++++-------------- SwiftNeoVim/Grid.swift | 39 ++-- SwiftNeoVim/NeoVimUiBridgeProtocol.h | 7 +- SwiftNeoVim/NeoVimView.swift | 24 ++- SwiftNeoVim/NeoVimViewEvents.swift | 227 +++++++++++----------- SwiftNeoVim/NeoVimViewUiBridge.swift | 78 +++++--- nvox/AppDelegate.swift | 2 +- nvox/Base.lproj/MainMenu.xib | 12 +- 10 files changed, 349 insertions(+), 325 deletions(-) diff --git a/NeoVimXpc/NeoVimXpc.h b/NeoVimXpc/NeoVimXpc.h index 822995ab..e9908d4e 100644 --- a/NeoVimXpc/NeoVimXpc.h +++ b/NeoVimXpc/NeoVimXpc.h @@ -17,6 +17,6 @@ - (void)vimInput:(NSString * _Nonnull)input; - (void)vimInputMarkedText:(NSString *_Nonnull)markedText; -- (void)debugScreenLines; +- (void)debug1; @end diff --git a/NeoVimXpc/NeoVimXpcImpl.h b/NeoVimXpc/NeoVimXpcImpl.h index cef8dd80..515da5e7 100644 --- a/NeoVimXpc/NeoVimXpcImpl.h +++ b/NeoVimXpc/NeoVimXpcImpl.h @@ -18,6 +18,4 @@ - (void)vimInput:(NSString *_Nonnull)input; - (void)vimInputMarkedText:(NSString *_Nonnull)markedText; -- (void)debugScreenLines; - @end diff --git a/NeoVimXpc/NeoVimXpcImpl.m b/NeoVimXpc/NeoVimXpcImpl.m index dc5d3f45..a1aed334 100644 --- a/NeoVimXpc/NeoVimXpcImpl.m +++ b/NeoVimXpc/NeoVimXpcImpl.m @@ -18,6 +18,7 @@ #import #import + #define pun_type(t, x) (*((t *)(&x))) typedef struct { @@ -48,9 +49,12 @@ static uv_cond_t _condition; static id _neo_vim_osx_ui; -static int _markedRow = 0; -static int _markedColumn = 0; -static NSString *_markedText = nil; +static int _marked_row = 0; +static int _marked_column = 0; +static int _put_row = -1; +static int _put_column = -1; +static NSString *_marked_text = nil; +static NSString *_backspace = nil; static dispatch_queue_t _queue; @@ -129,234 +133,219 @@ static void suspend_event(void **argv) { static void xpc_ui_resize(UI *ui __unused, int width, int height) { xpc_async(^{ - //printf("resize: %d:%d\n", width, height); - [_neo_vim_osx_ui resizeToWidth:width height:height]; + [_neo_vim_osx_ui resizeToWidth:width height:height]; }); } static void xpc_ui_clear(UI *ui __unused) { xpc_async(^{ - //printf("clear\n"); - [_neo_vim_osx_ui clear]; + [_neo_vim_osx_ui clear]; }); } static void xpc_ui_eol_clear(UI *ui __unused) { xpc_async(^{ - //printf("eol clear\n"); - [_neo_vim_osx_ui eolClear]; + [_neo_vim_osx_ui eolClear]; }); } static void xpc_ui_cursor_goto(UI *ui __unused, int row, int col) { xpc_async(^{ - //printf("cursor goto %d:%d\n", row, col); - [_neo_vim_osx_ui cursorGotoRow:row column:col]; + _put_row = row; + _put_column = col; + + [_neo_vim_osx_ui gotoPosition:(Position) { .row = row, .column = col } + screenCursor:(Position) { .row = cursorRow(), .column = cursorColumn() } + bufferCursor:(Position) { .row = curwin->w_cursor.lnum - 1, .column = curwin->w_cursor.col }]; }); } static void xpc_ui_update_menu(UI *ui __unused) { xpc_async(^{ - //printf("update menu\n"); - [_neo_vim_osx_ui updateMenu]; + [_neo_vim_osx_ui updateMenu]; }); } static void xpc_ui_busy_start(UI *ui __unused) { xpc_async(^{ - //printf("busy start\n"); - [_neo_vim_osx_ui busyStart]; + [_neo_vim_osx_ui busyStart]; }); } static void xpc_ui_busy_stop(UI *ui __unused) { xpc_async(^{ - //printf("busy stop\n"); - [_neo_vim_osx_ui busyStop]; + [_neo_vim_osx_ui busyStop]; }); } static void xpc_ui_mouse_on(UI *ui __unused) { xpc_async(^{ - //printf("mouse on\n"); - [_neo_vim_osx_ui mouseOn]; + [_neo_vim_osx_ui mouseOn]; }); } static void xpc_ui_mouse_off(UI *ui __unused) { xpc_async(^{ - //printf("mouse off\n"); - [_neo_vim_osx_ui mouseOff]; + [_neo_vim_osx_ui mouseOff]; }); } static void xpc_ui_mode_change(UI *ui __unused, int mode) { xpc_async(^{ - //printf("mode change %04x\n", mode); - [_neo_vim_osx_ui modeChange:mode]; + [_neo_vim_osx_ui modeChange:mode]; }); } static void xpc_ui_set_scroll_region(UI *ui __unused, int top, int bot, int left, int right) { xpc_async(^{ - //printf("set scroll region: %d, %d, %d, %d\n", top, bot, left, right); - [_neo_vim_osx_ui setScrollRegionToTop:top bottom:bot left:left right:right]; + [_neo_vim_osx_ui setScrollRegionToTop:top bottom:bot left:left right:right]; }); } static void xpc_ui_scroll(UI *ui __unused, int count) { xpc_async(^{ - //printf("scroll %d\n", count); - [_neo_vim_osx_ui scroll:count]; + [_neo_vim_osx_ui scroll:count]; }); } static void xpc_ui_highlight_set(UI *ui __unused, HlAttrs attrs) { xpc_async(^{ - //printf("highlight set\n"); - FontTrait trait = FontTraitNone; - if (attrs.italic) { - trait |= FontTraitItalic; - } - if (attrs.bold) { - trait |= FontTraitBold; - } - if (attrs.underline) { - trait |= FontTraitUnderline; - } - if (attrs.undercurl) { - trait |= FontTraitUndercurl; - } - CellAttributes cellAttrs; - cellAttrs.fontTrait = trait; + FontTrait trait = FontTraitNone; + if (attrs.italic) { + trait |= FontTraitItalic; + } + if (attrs.bold) { + trait |= FontTraitBold; + } + if (attrs.underline) { + trait |= FontTraitUnderline; + } + if (attrs.undercurl) { + trait |= FontTraitUndercurl; + } + CellAttributes cellAttrs; + cellAttrs.fontTrait = trait; - unsigned int fg = attrs.foreground == -1 ? _default_foreground : pun_type(unsigned int, attrs.foreground); - unsigned int bg = attrs.background == -1 ? _default_background : pun_type(unsigned int, attrs.background); + unsigned int fg = attrs.foreground == -1 ? _default_foreground : pun_type(unsigned int, attrs.foreground); + unsigned int bg = attrs.background == -1 ? _default_background : pun_type(unsigned int, attrs.background); - cellAttrs.foreground = attrs.reverse ? bg : fg; - cellAttrs.background = attrs.reverse ? fg : bg; - cellAttrs.special = attrs.special == -1 ? _default_special : pun_type(unsigned int, attrs.special); + cellAttrs.foreground = attrs.reverse ? bg : fg; + cellAttrs.background = attrs.reverse ? fg : bg; + cellAttrs.special = attrs.special == -1 ? _default_special : pun_type(unsigned int, attrs.special); - [_neo_vim_osx_ui highlightSet:cellAttrs]; + [_neo_vim_osx_ui highlightSet:cellAttrs]; }); } static void xpc_ui_put(UI *ui __unused, uint8_t *str, size_t len) { xpc_async(^{ - NSString *string = [[NSString alloc] initWithBytes:str length:len encoding:NSUTF8StringEncoding]; -// printf("put: %lu:'%s'\n", len, [string cStringUsingEncoding:NSUTF8StringEncoding]); + NSString *string = [[NSString alloc] initWithBytes:str length:len encoding:NSUTF8StringEncoding]; - if (_markedText != nil && _markedColumn == cursorColumn() && _markedRow == cursorRow()) { - [_neo_vim_osx_ui putMarkedText:string]; - } else if (_markedText != nil && len == 0 && _markedColumn == cursorColumn() - 1) { - [_neo_vim_osx_ui putMarkedText:string]; - } else { - [_neo_vim_osx_ui put:string]; - } + if (_marked_text != nil && _marked_row == _put_row && _marked_column == _put_column) { +// NSLog(@"!!! putting marked text: '%@'", string); + [_neo_vim_osx_ui putMarkedText:string]; + } else if (_marked_text != nil && len == 0 && _marked_row == _put_row && _marked_column == _put_column - 1) { +// NSLog(@"!!! putting marked text cuz zero"); + [_neo_vim_osx_ui putMarkedText:string]; + } else { +// NSLog(@"putting non-marked text: '%@'", string); + [_neo_vim_osx_ui put:string]; + } - [string release]; + _put_column += 1; + [string release]; }); } static void xpc_ui_bell(UI *ui __unused) { xpc_async(^{ - //printf("bell\n"); - [_neo_vim_osx_ui bell]; + [_neo_vim_osx_ui bell]; }); } static void xpc_ui_visual_bell(UI *ui __unused) { xpc_async(^{ - //printf("visual bell\n"); - [_neo_vim_osx_ui visualBell]; + [_neo_vim_osx_ui visualBell]; }); } static void xpc_ui_flush(UI *ui __unused) { xpc_async(^{ - //printf("flush\n"); - [_neo_vim_osx_ui flush]; + [_neo_vim_osx_ui flush]; }); } static void xpc_ui_update_fg(UI *ui __unused, int fg) { xpc_async(^{ -// printf("update fg: %x\n", fg); - if (fg == -1) { - [_neo_vim_osx_ui updateForeground:_default_foreground]; - return; - } + if (fg == -1) { + [_neo_vim_osx_ui updateForeground:_default_foreground]; + return; + } - _default_foreground = pun_type(unsigned int, fg); - [_neo_vim_osx_ui updateForeground:fg]; + _default_foreground = pun_type(unsigned int, fg); + [_neo_vim_osx_ui updateForeground:fg]; }); } static void xpc_ui_update_bg(UI *ui __unused, int bg) { xpc_async(^{ -// printf("update bg: %x\n", bg); - if (bg == -1) { - [_neo_vim_osx_ui updateBackground:_default_background]; - return; - } + if (bg == -1) { + [_neo_vim_osx_ui updateBackground:_default_background]; + return; + } - _default_background = pun_type(unsigned int, bg); - [_neo_vim_osx_ui updateBackground:bg]; + _default_background = pun_type(unsigned int, bg); + [_neo_vim_osx_ui updateBackground:bg]; }); } static void xpc_ui_update_sp(UI *ui __unused, int sp) { xpc_async(^{ -// printf("update sp: %x\n", sp); - if (sp == -1) { - [_neo_vim_osx_ui updateSpecial:_default_special]; - return; - } + if (sp == -1) { + [_neo_vim_osx_ui updateSpecial:_default_special]; + return; + } - _default_special = pun_type(unsigned int, sp); - [_neo_vim_osx_ui updateSpecial:sp]; + _default_special = pun_type(unsigned int, sp); + [_neo_vim_osx_ui updateSpecial:sp]; }); } static void xpc_ui_suspend(UI *ui __unused) { xpc_async(^{ - //printf("suspend\n"); - [_neo_vim_osx_ui suspend]; + [_neo_vim_osx_ui suspend]; - XpcUiData *data = ui->data; - // FIXME: dunno whether we need this: copied from tui.c - // kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT - // before continuing. This is done in another callback to avoid - // loop_poll_events recursion - queue_put_event(data->loop->fast_events, event_create(1, suspend_event, 1, ui)); + XpcUiData *data = ui->data; + // FIXME: dunno whether we need this: copied from tui.c + // kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT + // before continuing. This is done in another callback to avoid + // loop_poll_events recursion + queue_put_event(data->loop->fast_events, event_create(1, suspend_event, 1, ui)); }); } static void xpc_ui_set_title(UI *ui __unused, char *title) { xpc_async(^{ - //printf("set title: %s\n", title); - NSString *string = [[NSString alloc] initWithCString:title encoding:NSUTF8StringEncoding]; - [_neo_vim_osx_ui setTitle:string]; - [string release]; + NSString *string = [[NSString alloc] initWithCString:title encoding:NSUTF8StringEncoding]; + [_neo_vim_osx_ui setTitle:string]; + [string release]; }); } static void xpc_ui_set_icon(UI *ui __unused, char *icon) { xpc_async(^{ - //printf("set title: %s\n", icon); - NSString *string = [[NSString alloc] initWithCString:icon encoding:NSUTF8StringEncoding]; - [_neo_vim_osx_ui setIcon:string]; - [string release]; + NSString *string = [[NSString alloc] initWithCString:icon encoding:NSUTF8StringEncoding]; + [_neo_vim_osx_ui setIcon:string]; + [string release]; }); } static void xpc_ui_stop(UI *ui __unused) { xpc_async(^{ - //printf("stop\n"); - [_neo_vim_osx_ui stop]; + [_neo_vim_osx_ui stop]; - XpcUiData *data = (XpcUiData *) ui->data; - data->stop = true; + XpcUiData *data = (XpcUiData *) ui->data; + data->stop = true; }); } @@ -442,14 +431,18 @@ static void neovim_input(void **argv) { uv_cond_destroy(&_condition); uv_mutex_destroy(&_mutex); - // casting because [ui retain] returns an NSObject - _neo_vim_osx_ui = (id ) [ui retain]; + [ui retain]; + _neo_vim_osx_ui = ui; + + _backspace = [[NSString alloc] initWithString:@""]; return self; } - (void)dealloc { [_neo_vim_osx_ui release]; + [_backspace release]; + // FIXME: uv_thread_join(&thread) here after terminating neovim [super dealloc]; @@ -460,64 +453,64 @@ static void neovim_input(void **argv) { } - (void)vimInput:(NSString *_Nonnull)input { - xpc_async(^{ - if (_markedText == nil) { - loop_schedule(&main_loop, event_create(1, neovim_input, 1, [input retain])); // release in neovim_input - return; - } - - // Handle cases like ㅎ -> arrow key: The previously marked text is the same as the finalized text which should - // inserted. Neovim's drawing code is optimized such that it does not call put in this case again, thus, we - // have to manually unmark the cells in the main app. - if ([_markedText isEqualToString:input]) { - [_neo_vim_osx_ui unmarkRow:cursorRow() column:MAX(cursorColumn() - 1, 0)]; - if (ScreenLines[cursorRow() * screen_Columns + MAX(cursorColumn() - 1, 0)] == 0x00) { - [_neo_vim_osx_ui unmarkRow:cursorRow() column:MAX(cursorColumn() - 2, 0)]; - } - } - - [self deleteMarkedText]; + if (_marked_text == nil) { loop_schedule(&main_loop, event_create(1, neovim_input, 1, [input retain])); // release in neovim_input + return; + } + + // Handle cases like ㅎ -> arrow key: The previously marked text is the same as the finalized text which should + // inserted. Neovim's drawing code is optimized such that it does not call put in this case again, thus, we have + // to manually unmark the cells in the main app. + if ([_marked_text isEqualToString:input]) { +// NSLog(@"unmarking text: '%@'\t now at %d:%d", input, _put_row, _put_column); + const char *str = [_marked_text cStringUsingEncoding:NSUTF8StringEncoding]; + size_t cellCount = mb_string2cells((const char_u *) str); + for (int i = 1; i <= cellCount; i++) { +// NSLog(@"unmarking at %d:%d", _put_row, _put_column - i); + [_neo_vim_osx_ui unmarkRow:_put_row column:MAX(_put_column - i, 0)]; + } + } + + [self deleteMarkedText]; + loop_schedule(&main_loop, event_create(1, neovim_input, 1, [input retain])); // release in neovim_input }); } - (void)vimInputMarkedText:(NSString *_Nonnull)markedText { xpc_async(^{ - if (_markedText != nil) { - [self deleteMarkedText]; - } + if (_marked_text == nil) { + _marked_row = _put_row; + _marked_column = _put_column; + } else { + [self deleteMarkedText]; + } - [self insertMarkedText:markedText]; +// NSLog(@"inserting marked text '%@' at %d:%d", markedText, _put_row, _put_column); + [self insertMarkedText:markedText]; }); } - (void)insertMarkedText:(NSString *_Nonnull)markedText { - _markedRow = cursorRow(); - _markedColumn = cursorColumn(); - _markedText = [markedText retain]; // release when the final text is input in -vimInput + _marked_text = [markedText retain]; // release when the final text is input in -vimInput - loop_schedule(&main_loop, event_create(1, neovim_input, 1, [_markedText retain])); // release in neovim_input + loop_schedule(&main_loop, event_create(1, neovim_input, 1, [_marked_text retain])); // release in neovim_input } - (void)deleteMarkedText { - NSUInteger length = [_markedText lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4; + NSUInteger length = [_marked_text lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4; - [_markedText release]; - _markedText = nil; + [_marked_text release]; + _marked_text = nil; - NSString *backspace = [[NSString alloc] initWithString:@""]; for (int i = 0; i < length; i++) { - loop_schedule(&main_loop, event_create(1, neovim_input, 1, [backspace retain])); // release in neovim_input + loop_schedule(&main_loop, event_create(1, neovim_input, 1, [_backspace retain])); // release in neovim_input } - [backspace release]; } -- (void)debugScreenLines { - NSLog(@"--- ScreenLines ---"); - for (int i = 0; i < 20; i++) { - printf("%c,\n", ScreenLines[i]); - } +- (void)debug1 { + NSLog(@"_marked position: %d:%d", _marked_row, _marked_column); + NSLog(@"current cursor position: %d:%d", ui_current_row(), ui_current_col()); } @end diff --git a/SwiftNeoVim/Grid.swift b/SwiftNeoVim/Grid.swift index c8a95f3f..049f0d6c 100644 --- a/SwiftNeoVim/Grid.swift +++ b/SwiftNeoVim/Grid.swift @@ -26,15 +26,12 @@ struct Cell: CustomStringConvertible { } } -struct Position: CustomStringConvertible { +extension Position: CustomStringConvertible { static let zero = Position(row: 0, column: 0) static let null = Position(row: -1, column: -1) - var row: Int - var column: Int - - var description: String { + public var description: String { return "Position<\(self.row):\(self.column)>" } } @@ -78,7 +75,8 @@ class Grid: CustomStringConvertible { private(set) var region = Region.zero private(set) var size = Size.zero - private(set) var position = Position.zero + private(set) var putPosition = Position.zero + private(set) var screenCursor = Position.zero var foreground = qDefaultForeground var background = qDefaultBackground @@ -102,7 +100,7 @@ class Grid: CustomStringConvertible { func resize(size: Size) { self.region = Region(top: 0, bottom: size.height - 1, left: 0, right: size.width - 1) self.size = size - self.position = Position.zero + self.putPosition = Position.zero let emptyCellAttrs = CellAttributes(fontTrait: .None, foreground: self.foreground, background: self.background, special: self.special) @@ -117,7 +115,7 @@ class Grid: CustomStringConvertible { func eolClear() { self.clearRegion( - Region(top: self.position.row, bottom: self.position.row, left: self.position.column, right: self.region.right) + Region(top: self.putPosition.row, bottom: self.putPosition.row, left: self.putPosition.column, right: self.region.right) ) } @@ -156,7 +154,11 @@ class Grid: CustomStringConvertible { } func goto(position: Position) { - self.position = position + self.putPosition = position + } + + func moveCursor(position: Position) { + self.screenCursor = position } func put(string: String) { @@ -165,14 +167,17 @@ class Grid: CustomStringConvertible { // => // |abcde>| <- ">" at the end of the line is wrong -> the XPC could tell the main app whether the string occupies // |ㅎ | two cells using vim_strwidth() - self.cells[self.position.row][self.position.column] = Cell(string: string, attrs: self.attrs) - self.position.column += 1 + self.cells[self.putPosition.row][self.putPosition.column] = Cell(string: string, attrs: self.attrs) + + // Increment the column of the put position because neovim calls sets the position only once when drawing + // consecutive cells in the same line + self.putPosition.column += 1 } func putMarkedText(string: String) { // NOTE: Maybe there's a better way to indicate marked text than inverting... - self.cells[self.position.row][self.position.column] = Cell(string: string, attrs: self.attrs, marked: true) - self.position.column += 1 + self.cells[self.putPosition.row][self.putPosition.column] = Cell(string: string, attrs: self.attrs, marked: true) + self.putPosition.column += 1 } func unmarkCell(position: Position) { @@ -200,13 +205,17 @@ class Grid: CustomStringConvertible { } func isPreviousCellEmpty(position: Position) -> Bool { - return self.isCellEmpty(Position(row: position.row, column: max(position.column - 1, 0))) + return self.isCellEmpty(self.previousCellPosition(position)) } func isNextCellEmpty(position: Position) -> Bool { - return self.isCellEmpty(Position(row: position.row, column: min(position.column + 1, self.size.width - 1))) + return self.isCellEmpty(self.nextCellPosition(position)) } + func previousCellPosition(position: Position) -> Position { + return Position(row: position.row, column: max(position.column - 1, 0)) + } + func nextCellPosition(position: Position) -> Position { return Position(row: position.row, column: min(position.column + 1, self.size.width - 1)) } diff --git a/SwiftNeoVim/NeoVimUiBridgeProtocol.h b/SwiftNeoVim/NeoVimUiBridgeProtocol.h index c5ae9040..138584c3 100644 --- a/SwiftNeoVim/NeoVimUiBridgeProtocol.h +++ b/SwiftNeoVim/NeoVimUiBridgeProtocol.h @@ -21,6 +21,11 @@ typedef struct { unsigned int special; } CellAttributes; +typedef struct { + NSInteger row; + NSInteger column; +} Position; + #define qDefaultForeground 0xFF000000 #define qDefaultBackground 0xFFFFFFFF #define qDefaultSpecial 0xFFFF0000 @@ -49,7 +54,7 @@ typedef struct { * 2. NeoVim wants to put the cursor at (row, column). * In case of 1. NeoVim will put in subsequent call. In case of 2. NeoVim seems to flush twice in a row. */ -- (void)cursorGotoRow:(int)row column:(int)column; +- (void)gotoPosition:(Position)position screenCursor:(Position)screenCursor bufferCursor:(Position)bufferCursor; - (void)updateMenu; - (void)busyStart; diff --git a/SwiftNeoVim/NeoVimView.swift b/SwiftNeoVim/NeoVimView.swift index 6e89ad45..d6f729ff 100644 --- a/SwiftNeoVim/NeoVimView.swift +++ b/SwiftNeoVim/NeoVimView.swift @@ -118,14 +118,15 @@ public class NeoVimView: NSView { self.rowRunIntersecting(rects: dirtyRects).forEach { rowFrag in // For background drawing we don't filter out the put(0, 0)s: in some cases only the put(0, 0)-cells should be - // redrawn. - self.drawBackground(positions: rowFrag.range.map { self.positionOnView(rowFrag.row, column: $0) }, + // redrawn. => FIXME: probably we have to consider this also when drawing further down, ie when the range starts + // with '0'... + self.drawBackground(positions: rowFrag.range.map { self.pointInView(rowFrag.row, column: $0) }, background: rowFrag.attrs.background) let positions = rowFrag.range // filter out the put(0, 0)s (after a wide character) .filter { self.grid.cells[rowFrag.row][$0].string.characters.count > 0 } - .map { self.positionOnView(rowFrag.row, column: $0) } + .map { self.pointInView(rowFrag.row, column: $0) } if positions.isEmpty { return @@ -144,7 +145,7 @@ public class NeoVimView: NSView { private func drawCursor(background: UInt32) { // FIXME: for now do some rudimentary cursor drawing - let cursorPosition = self.grid.position + let cursorPosition = self.grid.screenCursor // Swift.print("\(#function): \(cursorPosition)") var cursorRect = self.cellRect(row: cursorPosition.row, column: cursorPosition.column) @@ -153,7 +154,8 @@ public class NeoVimView: NSView { cursorRect = cursorRect.union(self.cellRect(row: nextPosition.row, column:nextPosition.column)) } - ColorUtils.colorFromCodeIgnoringAlpha(background).set() + // set cursor to an abhorrent color for debugging + ColorUtils.colorFromCodeIgnoringAlpha(0xFF990000).set() NSRectFillUsingOperation(cursorRect, .CompositeDifference) } @@ -203,16 +205,24 @@ public class NeoVimView: NSView { } // All RowRuns for all Regions grouped by Region. .flatMap { $0 } // Flattened RowRuns for all Regions. } + + func pointInView(position: Position) -> CGPoint { + return self.pointInView(position.row, column: position.column) + } - func positionOnView(row: Int, column: Int) -> CGPoint { + func pointInView(row: Int, column: Int) -> CGPoint { return CGPoint( x: CGFloat(column) * self.cellSize.width, y: self.frame.size.height - CGFloat(row) * self.cellSize.height - self.cellSize.height ) } + func cellRect(position: Position) -> CGRect { + return self.cellRect(row: position.row, column: position.column) + } + func cellRect(row row: Int, column: Int) -> CGRect { - return CGRect(origin: self.positionOnView(row, column: column), size: self.cellSize) + return CGRect(origin: self.pointInView(row, column: column), size: self.cellSize) } func regionRect(region: Region) -> CGRect { diff --git a/SwiftNeoVim/NeoVimViewEvents.swift b/SwiftNeoVim/NeoVimViewEvents.swift index c123750a..ded52d5f 100644 --- a/SwiftNeoVim/NeoVimViewEvents.swift +++ b/SwiftNeoVim/NeoVimViewEvents.swift @@ -34,7 +34,7 @@ extension NeoVimView: NSTextInputClient { } public func insertText(aString: AnyObject, replacementRange: NSRange) { - NSLog("\(#function): \(replacementRange): \(aString)") + NSLog("\(#function): \(replacementRange): '\(aString)'") switch aString { case let string as String: @@ -49,14 +49,14 @@ extension NeoVimView: NSTextInputClient { self.markedText = nil self.markedPosition = Position.null // TODO: necessary? - self.setNeedsDisplayInRect(self.cellRect(row: self.grid.position.row, column: self.grid.position.column)) + self.setNeedsDisplayInRect(self.cellRect(row: self.grid.putPosition.row, column: self.grid.putPosition.column)) self.keyDownDone = true } public override func doCommandBySelector(aSelector: Selector) { // NSLog("\(#function): "\(aSelector)") - // TODO: handle when ㅎ -> delete + // FIXME: handle when ㅎ -> delete if self.respondsToSelector(aSelector) { Swift.print("\(#function): calling \(aSelector)") @@ -72,7 +72,7 @@ extension NeoVimView: NSTextInputClient { public func setMarkedText(aString: AnyObject, selectedRange: NSRange, replacementRange: NSRange) { if self.markedText == nil { - self.markedPosition = self.grid.position + self.markedPosition = self.grid.putPosition } switch aString { @@ -95,7 +95,7 @@ extension NeoVimView: NSTextInputClient { self.markedText = nil self.markedPosition = Position.null // TODO: necessary? - self.setNeedsDisplayInRect(self.cellRect(row: self.grid.position.row, column: self.grid.position.column)) + self.setNeedsDisplayInRect(self.cellRect(row: self.grid.putPosition.row, column: self.grid.putPosition.column)) self.keyDownDone = true } @@ -110,19 +110,19 @@ extension NeoVimView: NSTextInputClient { // } // FIXME: do we have to handle positions at the column borders? - if self.grid.isPreviousCellEmpty(self.grid.position) { + if self.grid.isPreviousCellEmpty(self.grid.putPosition) { let result = NSRange( location: self.grid.singleIndexFrom( - Position(row: self.grid.position.row, column: self.grid.position.column - 1) + Position(row: self.grid.putPosition.row, column: self.grid.putPosition.column - 1) ), length: 0 ) - NSLog("\(#function): \(result)") +// NSLog("\(#function): \(result)") return result } - let result = NSRange(location: self.grid.singleIndexFrom(self.grid.position), length: 0) - NSLog("\(#function): \(result)") + let result = NSRange(location: self.grid.singleIndexFrom(self.grid.putPosition), length: 0) +// NSLog("\(#function): \(result)") return result } @@ -131,11 +131,11 @@ extension NeoVimView: NSTextInputClient { if let markedText = self.markedText { let result = NSRange(location: self.grid.singleIndexFrom(self.markedPosition), length: markedText.characters.count) - NSLog("\(#function): \(result)") +// NSLog("\(#function): \(result)") return result } - NSLog("\(#function): returning empty range") +// NSLog("\(#function): returning empty range") return NSRange(location: NSNotFound, length: 0) } @@ -168,36 +168,29 @@ extension NeoVimView: NSTextInputClient { $0 + $1.string } actualRange[0].length = string.characters.count - NSLog("\(#function): \(aRange), \(actualRange[0]): \(string)") +// NSLog("\(#function): \(aRange), \(actualRange[0]): \(string)") return NSAttributedString(string: string) } - // TODO: maybe make Grid a Indexable or similar + // FIXME: maybe make Grid a Indexable or similar var string = "" for i in location...(location + length) { string += self.grid.cellForSingleIndex(i).string } - NSLog("\(#function): \(aRange), \(actualRange[0]): \(string)") +// NSLog("\(#function): \(aRange), \(actualRange[0]): \(string)") return NSAttributedString(string: string) } public func validAttributesForMarkedText() -> [String] { - // Swift.print("\(#function): ") return [] } public func firstRectForCharacterRange(aRange: NSRange, actualRange: NSRangePointer) -> NSRect { - NSLog("\(#function): \(aRange), \(actualRange[0])") - if actualRange != nil { - Swift.print("\(#function): \(aRange), \(actualRange[0])") - } else { - Swift.print("\(#function): \(aRange), nil") - } + let position = self.grid.positionFromSingleIndex(aRange.location) + + NSLog("\(#function): \(aRange),\(actualRange[0]) -> \(position.row):\(position.column)") - let row = Int(floor(Double(aRange.location / self.grid.size.width))) - let column = aRange.location - row * self.grid.size.width - - let resultInSelf = self.cellRect(row: row, column: column) + let resultInSelf = self.cellRect(row: position.row, column: position.column) let result = self.window?.convertRectToScreen(self.convertRect(resultInSelf, toView: nil)) return result! @@ -229,95 +222,95 @@ extension NeoVimView: NSTextInputClient { return result } - - /* - public func moveWordForward(sender: AnyObject?) - public func moveWordBackward(sender: AnyObject?) - public func moveToBeginningOfLine(sender: AnyObject?) - public func moveToEndOfLine(sender: AnyObject?) - public func moveToBeginningOfParagraph(sender: AnyObject?) - public func moveToEndOfParagraph(sender: AnyObject?) - public func moveToEndOfDocument(sender: AnyObject?) - public func moveToBeginningOfDocument(sender: AnyObject?) - public func pageDown(sender: AnyObject?) - public func pageUp(sender: AnyObject?) - public func centerSelectionInVisibleArea(sender: AnyObject?) - - public func moveBackwardAndModifySelection(sender: AnyObject?) - public func moveForwardAndModifySelection(sender: AnyObject?) - public func moveWordForwardAndModifySelection(sender: AnyObject?) - public func moveWordBackwardAndModifySelection(sender: AnyObject?) - public func moveUpAndModifySelection(sender: AnyObject?) - public func moveDownAndModifySelection(sender: AnyObject?) - - public func moveToBeginningOfLineAndModifySelection(sender: AnyObject?) - public func moveToEndOfLineAndModifySelection(sender: AnyObject?) - public func moveToBeginningOfParagraphAndModifySelection(sender: AnyObject?) - public func moveToEndOfParagraphAndModifySelection(sender: AnyObject?) - public func moveToEndOfDocumentAndModifySelection(sender: AnyObject?) - public func moveToBeginningOfDocumentAndModifySelection(sender: AnyObject?) - public func pageDownAndModifySelection(sender: AnyObject?) - public func pageUpAndModifySelection(sender: AnyObject?) - public func moveParagraphForwardAndModifySelection(sender: AnyObject?) - public func moveParagraphBackwardAndModifySelection(sender: AnyObject?) - - public func moveWordRight(sender: AnyObject?) - public func moveWordLeft(sender: AnyObject?) - public func moveRightAndModifySelection(sender: AnyObject?) - public func moveLeftAndModifySelection(sender: AnyObject?) - public func moveWordRightAndModifySelection(sender: AnyObject?) - public func moveWordLeftAndModifySelection(sender: AnyObject?) - - public func moveToLeftEndOfLine(sender: AnyObject?) - public func moveToRightEndOfLine(sender: AnyObject?) - public func moveToLeftEndOfLineAndModifySelection(sender: AnyObject?) - public func moveToRightEndOfLineAndModifySelection(sender: AnyObject?) - - public func scrollLineUp(sender: AnyObject?) - public func scrollLineDown(sender: AnyObject?) - - public func transpose(sender: AnyObject?) - public func transposeWords(sender: AnyObject?) - - public func selectAll(sender: AnyObject?) - public func selectParagraph(sender: AnyObject?) - public func selectLine(sender: AnyObject?) - public func selectWord(sender: AnyObject?) - - public func indent(sender: AnyObject?) - public func insertTab(sender: AnyObject?) - public func insertBacktab(sender: AnyObject?) - public func insertNewline(sender: AnyObject?) - public func insertParagraphSeparator(sender: AnyObject?) - public func insertNewlineIgnoringFieldEditor(sender: AnyObject?) - public func insertTabIgnoringFieldEditor(sender: AnyObject?) - public func insertLineBreak(sender: AnyObject?) - public func insertContainerBreak(sender: AnyObject?) - public func insertSingleQuoteIgnoringSubstitution(sender: AnyObject?) - public func insertDoubleQuoteIgnoringSubstitution(sender: AnyObject?) - - public func changeCaseOfLetter(sender: AnyObject?) - public func uppercaseWord(sender: AnyObject?) - public func lowercaseWord(sender: AnyObject?) - public func capitalizeWord(sender: AnyObject?) - - public func deleteBackwardByDecomposingPreviousCharacter(sender: AnyObject?) - public func deleteWordForward(sender: AnyObject?) - public func deleteWordBackward(sender: AnyObject?) - public func deleteToBeginningOfLine(sender: AnyObject?) - public func deleteToEndOfLine(sender: AnyObject?) - public func deleteToBeginningOfParagraph(sender: AnyObject?) - public func deleteToEndOfParagraph(sender: AnyObject?) - - public func yank(sender: AnyObject?) - - public func complete(sender: AnyObject?) - - public func setMark(sender: AnyObject?) - public func deleteToMark(sender: AnyObject?) - public func selectToMark(sender: AnyObject?) - public func swapWithMark(sender: AnyObject?) - - public func cancelOperation(sender: AnyObject?) - */ } + +/* + public func moveWordForward(sender: AnyObject?) + public func moveWordBackward(sender: AnyObject?) + public func moveToBeginningOfLine(sender: AnyObject?) + public func moveToEndOfLine(sender: AnyObject?) + public func moveToBeginningOfParagraph(sender: AnyObject?) + public func moveToEndOfParagraph(sender: AnyObject?) + public func moveToEndOfDocument(sender: AnyObject?) + public func moveToBeginningOfDocument(sender: AnyObject?) + public func pageDown(sender: AnyObject?) + public func pageUp(sender: AnyObject?) + public func centerSelectionInVisibleArea(sender: AnyObject?) + + public func moveBackwardAndModifySelection(sender: AnyObject?) + public func moveForwardAndModifySelection(sender: AnyObject?) + public func moveWordForwardAndModifySelection(sender: AnyObject?) + public func moveWordBackwardAndModifySelection(sender: AnyObject?) + public func moveUpAndModifySelection(sender: AnyObject?) + public func moveDownAndModifySelection(sender: AnyObject?) + + public func moveToBeginningOfLineAndModifySelection(sender: AnyObject?) + public func moveToEndOfLineAndModifySelection(sender: AnyObject?) + public func moveToBeginningOfParagraphAndModifySelection(sender: AnyObject?) + public func moveToEndOfParagraphAndModifySelection(sender: AnyObject?) + public func moveToEndOfDocumentAndModifySelection(sender: AnyObject?) + public func moveToBeginningOfDocumentAndModifySelection(sender: AnyObject?) + public func pageDownAndModifySelection(sender: AnyObject?) + public func pageUpAndModifySelection(sender: AnyObject?) + public func moveParagraphForwardAndModifySelection(sender: AnyObject?) + public func moveParagraphBackwardAndModifySelection(sender: AnyObject?) + + public func moveWordRight(sender: AnyObject?) + public func moveWordLeft(sender: AnyObject?) + public func moveRightAndModifySelection(sender: AnyObject?) + public func moveLeftAndModifySelection(sender: AnyObject?) + public func moveWordRightAndModifySelection(sender: AnyObject?) + public func moveWordLeftAndModifySelection(sender: AnyObject?) + + public func moveToLeftEndOfLine(sender: AnyObject?) + public func moveToRightEndOfLine(sender: AnyObject?) + public func moveToLeftEndOfLineAndModifySelection(sender: AnyObject?) + public func moveToRightEndOfLineAndModifySelection(sender: AnyObject?) + + public func scrollLineUp(sender: AnyObject?) + public func scrollLineDown(sender: AnyObject?) + + public func transpose(sender: AnyObject?) + public func transposeWords(sender: AnyObject?) + + public func selectAll(sender: AnyObject?) + public func selectParagraph(sender: AnyObject?) + public func selectLine(sender: AnyObject?) + public func selectWord(sender: AnyObject?) + + public func indent(sender: AnyObject?) + public func insertTab(sender: AnyObject?) + public func insertBacktab(sender: AnyObject?) + public func insertNewline(sender: AnyObject?) + public func insertParagraphSeparator(sender: AnyObject?) + public func insertNewlineIgnoringFieldEditor(sender: AnyObject?) + public func insertTabIgnoringFieldEditor(sender: AnyObject?) + public func insertLineBreak(sender: AnyObject?) + public func insertContainerBreak(sender: AnyObject?) + public func insertSingleQuoteIgnoringSubstitution(sender: AnyObject?) + public func insertDoubleQuoteIgnoringSubstitution(sender: AnyObject?) + + public func changeCaseOfLetter(sender: AnyObject?) + public func uppercaseWord(sender: AnyObject?) + public func lowercaseWord(sender: AnyObject?) + public func capitalizeWord(sender: AnyObject?) + + public func deleteBackwardByDecomposingPreviousCharacter(sender: AnyObject?) + public func deleteWordForward(sender: AnyObject?) + public func deleteWordBackward(sender: AnyObject?) + public func deleteToBeginningOfLine(sender: AnyObject?) + public func deleteToEndOfLine(sender: AnyObject?) + public func deleteToBeginningOfParagraph(sender: AnyObject?) + public func deleteToEndOfParagraph(sender: AnyObject?) + + public func yank(sender: AnyObject?) + + public func complete(sender: AnyObject?) + + public func setMark(sender: AnyObject?) + public func deleteToMark(sender: AnyObject?) + public func selectToMark(sender: AnyObject?) + public func swapWithMark(sender: AnyObject?) + + public func cancelOperation(sender: AnyObject?) + */ diff --git a/SwiftNeoVim/NeoVimViewUiBridge.swift b/SwiftNeoVim/NeoVimViewUiBridge.swift index f4d45d79..3bac75d5 100644 --- a/SwiftNeoVim/NeoVimViewUiBridge.swift +++ b/SwiftNeoVim/NeoVimViewUiBridge.swift @@ -17,6 +17,7 @@ extension NeoVimView: NeoVimUiBridgeProtocol { // Swift.print("### resize to \(width):\(height)") self.grid.resize(Size(width: Int(width), height: Int(height))) self.delegate?.resizeToSize(rectSize) + // TODO: set needs display? } } @@ -33,9 +34,9 @@ extension NeoVimView: NeoVimUiBridgeProtocol { // Swift.print("### eol clear") self.grid.eolClear() - let origin = self.positionOnView(self.grid.position.row, column: self.grid.position.column) + let origin = self.pointInView(self.grid.putPosition) let size = CGSize( - width: CGFloat(self.grid.region.right - self.grid.position.column + 1) * self.cellSize.width, + width: CGFloat(self.grid.region.right - self.grid.putPosition.column + 1) * self.cellSize.width, height: self.cellSize.height ) let rect = CGRect(origin: origin, size: size) @@ -43,12 +44,15 @@ extension NeoVimView: NeoVimUiBridgeProtocol { } } - public func cursorGotoRow(row: Int32, column: Int32) { + public func gotoPosition(position: Position, screenCursor: Position, bufferCursor: Position) { DispatchUtils.gui { - NSLog("\(#function): \(row):\(column)") - self.setCursorNeedsDisplay(self.grid.position) - self.grid.goto(Position(row: Int(row), column: Int(column))) - self.setCursorNeedsDisplay(self.grid.position) +// NSLog("\(#function): \(position), \(screenCursor), \(bufferCursor)") + + self.setNeedsDisplay(cellPosition: self.grid.screenCursor) // redraw where the cursor was till now + self.setNeedsDisplay(screenCursor: screenCursor) // draw the new cursor + + self.grid.goto(position) + self.grid.moveCursor(screenCursor) } } @@ -81,7 +85,7 @@ extension NeoVimView: NeoVimUiBridgeProtocol { DispatchUtils.gui { let region = Region(top: Int(top), bottom: Int(bottom), left: Int(left), right: Int(right)) self.grid.setScrollRegion(region) - self.setNeedsDisplayInRect(self.regionRect(region)) + self.setNeedsDisplay(region: region) } } @@ -90,7 +94,7 @@ extension NeoVimView: NeoVimUiBridgeProtocol { DispatchUtils.gui { self.grid.scroll(Int(count)) - self.setNeedsDisplayInRect(self.regionRect(self.grid.region)) + self.setNeedsDisplay(region: self.grid.region) } } @@ -103,30 +107,26 @@ extension NeoVimView: NeoVimUiBridgeProtocol { public func put(string: String) { DispatchUtils.gui { -// Swift.print("\(#function): \(string)") - let curPos = Position(row: self.grid.position.row, column: self.grid.position.column) -// Swift.print("\(#function): \(curPos) -> \(string)") + let curPos = self.grid.putPosition +// NSLog("\(#function): \(curPos) -> \(string)") self.grid.put(string) - - self.setNeedsDisplayAt(position: curPos) - if string.characters.count == 0 { - self.setNeedsDisplayAt(row: curPos.row, column: max(curPos.column - 1, 0)) - } - self.setCursorNeedsDisplay(self.grid.position) + self.setNeedsDisplay(cellPosition: curPos) + + self.setNeedsDisplay(screenCursor: self.grid.screenCursor) } } public func putMarkedText(markedText: String) { DispatchUtils.gui { // Swift.print("\(#function): \(markedText)") - let curPos = Position(row: self.grid.position.row, column: self.grid.position.column) + let curPos = self.grid.putPosition self.grid.putMarkedText(markedText) - self.setNeedsDisplayAt(position: curPos) + self.setNeedsDisplay(position: curPos) if markedText.characters.count == 0 { - self.setNeedsDisplayAt(row: curPos.row, column: max(curPos.column - 1, 0)) + self.setNeedsDisplay(position: self.grid.previousCellPosition(curPos)) } - self.setCursorNeedsDisplay(self.grid.position) + self.setNeedsDisplay(screenCursor: self.grid.screenCursor) } } @@ -134,8 +134,8 @@ extension NeoVimView: NeoVimUiBridgeProtocol { DispatchUtils.gui { // Swift.print("\(#function): \(row):\(column)") self.grid.unmarkCell(Position(row: Int(row), column: Int(column))) - self.setNeedsDisplayAt(row: Int(row), column: Int(column)) - self.setCursorNeedsDisplay(self.grid.position) + self.setNeedsDisplay(row: Int(row), column: Int(column)) + self.setNeedsDisplay(screenCursor: self.grid.screenCursor) } } @@ -191,20 +191,36 @@ extension NeoVimView: NeoVimUiBridgeProtocol { public func stop() { // Swift.print("### stop") } - - private func setNeedsDisplayAt(position position: Position) { - self.setNeedsDisplayAt(row: position.row, column: position.column) + + private func setNeedsDisplay(region region: Region) { + self.setNeedsDisplayInRect(self.regionRect(region)) + } + + private func setNeedsDisplay(cellPosition position: Position) { + self.setNeedsDisplay(position: position) + + if self.grid.isCellEmpty(position) { + self.setNeedsDisplay(position: self.grid.previousCellPosition(position)) + } + + if self.grid.isNextCellEmpty(position) { + self.setNeedsDisplay(position: self.grid.nextCellPosition(position)) + } } - private func setNeedsDisplayAt(row row: Int, column: Int) { + private func setNeedsDisplay(position position: Position) { + self.setNeedsDisplay(row: position.row, column: position.column) + } + + private func setNeedsDisplay(row row: Int, column: Int) { // Swift.print("\(#function): \(row):\(column)") self.setNeedsDisplayInRect(self.cellRect(row: row, column: column)) } - private func setCursorNeedsDisplay(position: Position) { - self.setNeedsDisplayAt(position: position) + private func setNeedsDisplay(screenCursor position: Position) { + self.setNeedsDisplay(position: position) if self.grid.isNextCellEmpty(position) { - self.setNeedsDisplayAt(position: self.grid.nextCellPosition(position)) + self.setNeedsDisplay(position: self.grid.nextCellPosition(position)) } } } diff --git a/nvox/AppDelegate.swift b/nvox/AppDelegate.swift index 4beead42..b07c0e39 100644 --- a/nvox/AppDelegate.swift +++ b/nvox/AppDelegate.swift @@ -13,7 +13,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NeoVimViewDelegate { var neoVim: NeoVim! @IBAction func debugSomething(sender: AnyObject!) { - self.neoVim.view.debugInfo() + self.neoVim.xpc.debug1() } func applicationDidFinishLaunching(aNotification: NSNotification) { diff --git a/nvox/Base.lproj/MainMenu.xib b/nvox/Base.lproj/MainMenu.xib index a923c5e0..dcd115ff 100644 --- a/nvox/Base.lproj/MainMenu.xib +++ b/nvox/Base.lproj/MainMenu.xib @@ -670,15 +670,15 @@ - - + + - + - +