From 4fdacc7296d5160c0ba2867cefb58edccc8247ed Mon Sep 17 00:00:00 2001 From: Greg Omelaenko Date: Wed, 20 Sep 2017 01:56:41 +1000 Subject: [PATCH 1/5] Call `currentBufferChanged` when a different buffer is selected - in addition to being called when a file is read or written, as before --- SwiftNeoVim/NeoVimView+UiBridge.swift | 2 +- SwiftNeoVim/NeoVimViewDelegate.swift | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/SwiftNeoVim/NeoVimView+UiBridge.swift b/SwiftNeoVim/NeoVimView+UiBridge.swift index a1ebd77a9..96a92c355 100644 --- a/SwiftNeoVim/NeoVimView+UiBridge.swift +++ b/SwiftNeoVim/NeoVimView+UiBridge.swift @@ -200,7 +200,7 @@ extension NeoVimView { self.tabChanged() } - if event == .BUFREADPOST || event == .BUFWRITEPOST { + if event == .BUFREADPOST || event == .BUFWRITEPOST || event == .BUFENTER { self.currentBufferChanged(bufferHandle) } } diff --git a/SwiftNeoVim/NeoVimViewDelegate.swift b/SwiftNeoVim/NeoVimViewDelegate.swift index c55805c45..f6414f4fe 100644 --- a/SwiftNeoVim/NeoVimViewDelegate.swift +++ b/SwiftNeoVim/NeoVimViewDelegate.swift @@ -14,6 +14,7 @@ public protocol NeoVimViewDelegate: class { func cwdChanged() func bufferListChanged() func tabChanged() + /// Called when the current buffer changes, including when a new one is selected. func currentBufferChanged(_ currentBuffer: NeoVimBuffer) func colorschemeChanged(to: NeoVimView.Theme) From 479a64e9c3a2c06f2c5b13fd8cdfe8752b707a5f Mon Sep 17 00:00:00 2001 From: Greg Omelaenko Date: Wed, 20 Sep 2017 02:11:07 +1000 Subject: [PATCH 2/5] Set 2-space indentation in Xcode project config to match source files --- VimR.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/VimR.xcodeproj/project.pbxproj b/VimR.xcodeproj/project.pbxproj index c3494b774..abd38d073 100644 --- a/VimR.xcodeproj/project.pbxproj +++ b/VimR.xcodeproj/project.pbxproj @@ -1096,7 +1096,9 @@ 4B5012001EBA791000F76C46 /* Frameworks */, 4BEBA5061CFF374B00673FDF /* Products */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; 4BEBA5061CFF374B00673FDF /* Products */ = { isa = PBXGroup; From f22675fc92642a26bbf9ad779bc0b1337c44ff1b Mon Sep 17 00:00:00 2001 From: Greg Omelaenko Date: Wed, 20 Sep 2017 03:09:59 +1000 Subject: [PATCH 3/5] Add -isCurrent to NeoVimTab, -isCurrentInTab to NeoVimWindow for state tracking Also adds -currentWindow to NeoVimTab for convenience --- NeoVimServer/NeoVimTab.h | 6 +++++- NeoVimServer/NeoVimTab.m | 11 ++++++++++- NeoVimServer/NeoVimWindow.h | 3 ++- NeoVimServer/NeoVimWindow.m | 6 +++++- NeoVimServer/server_ui.m | 10 ++++++++-- SwiftNeoVim/NeoVimView+Api.swift | 1 + SwiftNeoVim/NeoVimView.swift | 3 +++ 7 files changed, 34 insertions(+), 6 deletions(-) diff --git a/NeoVimServer/NeoVimTab.h b/NeoVimServer/NeoVimTab.h index 9b4f45632..294dc777d 100644 --- a/NeoVimServer/NeoVimTab.h +++ b/NeoVimServer/NeoVimTab.h @@ -15,8 +15,12 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSInteger handle; @property (nonatomic, readonly) NSArray *windows; +@property (nonatomic, readonly) bool isCurrent; -- (instancetype)initWithHandle:(NSInteger)handle windows:(NSArray *)windows; +- (instancetype)initWithHandle:(NSInteger)handle windows:(NSArray *)windows current:(bool)current; + +/// @returns The most recently selected window in *this* tab. +- (NeoVimWindow * _Nullable )currentWindow; - (NSString *)description; diff --git a/NeoVimServer/NeoVimTab.m b/NeoVimServer/NeoVimTab.m index 6c0adbfc1..76218b89a 100644 --- a/NeoVimServer/NeoVimTab.m +++ b/NeoVimServer/NeoVimTab.m @@ -9,7 +9,7 @@ @implementation NeoVimTab -- (instancetype)initWithHandle:(NSInteger)handle windows:(NSArray *)windows { +- (instancetype)initWithHandle:(NSInteger)handle windows:(NSArray *)windows current:(bool)current { self = [super init]; if (self == nil) { return nil; @@ -17,14 +17,21 @@ - (instancetype)initWithHandle:(NSInteger)handle windows:(NSArray "]; return description; } @@ -32,6 +39,7 @@ - (NSString *)description { - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:@(self.handle) forKey:@"handle"]; [coder encodeObject:self.windows forKey:@"windows"]; + [coder encodeBool:self.isCurrent forKey:@"current"]; } - (instancetype)initWithCoder:(NSCoder *)coder { @@ -40,6 +48,7 @@ - (instancetype)initWithCoder:(NSCoder *)coder { NSNumber *objHandle = [coder decodeObjectForKey:@"handle"]; _handle = objHandle.integerValue; _windows = [coder decodeObjectForKey:@"windows"]; + _isCurrent = [coder decodeBoolForKey:@"current"]; } return self; diff --git a/NeoVimServer/NeoVimWindow.h b/NeoVimServer/NeoVimWindow.h index a34ce8046..394e72f42 100644 --- a/NeoVimServer/NeoVimWindow.h +++ b/NeoVimServer/NeoVimWindow.h @@ -15,8 +15,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSInteger handle; @property (nonatomic, readonly) NeoVimBuffer *buffer; +@property (nonatomic, readonly) bool isCurrentInTab; -- (instancetype)initWithHandle:(NSInteger)handle buffer:(NeoVimBuffer *)buffer; +- (instancetype)initWithHandle:(NSInteger)handle buffer:(NeoVimBuffer *)buffer currentInTab:(bool)current; - (instancetype)initWithCoder:(NSCoder *)coder; - (void)encodeWithCoder:(NSCoder *)coder; diff --git a/NeoVimServer/NeoVimWindow.m b/NeoVimServer/NeoVimWindow.m index d8e874de9..561c4ae44 100644 --- a/NeoVimServer/NeoVimWindow.m +++ b/NeoVimServer/NeoVimWindow.m @@ -9,7 +9,7 @@ @implementation NeoVimWindow -- (instancetype)initWithHandle:(NSInteger)handle buffer:(NeoVimBuffer *)buffer { +- (instancetype)initWithHandle:(NSInteger)handle buffer:(NeoVimBuffer *)buffer currentInTab:(bool)current { self = [super init]; if (self == nil) { return nil; @@ -17,6 +17,7 @@ - (instancetype)initWithHandle:(NSInteger)handle buffer:(NeoVimBuffer *)buffer { _handle = handle; _buffer = buffer; + _isCurrentInTab = current; return self; } @@ -25,6 +26,7 @@ - (NSString *)description { NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: ", NSStringFromClass([self class])]; [description appendFormat:@"self.handle=%li", self.handle]; [description appendFormat:@", self.buffer=%@", self.buffer]; + [description appendFormat:@", self.currentInTab=%d", self.isCurrentInTab]; [description appendString:@">"]; return description; } @@ -32,6 +34,7 @@ - (NSString *)description { - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:@(self.handle) forKey:@"handle"]; [coder encodeObject:self.buffer forKey:@"buffer"]; + [coder encodeBool:self.isCurrentInTab forKey:@"currentInTab"]; } - (instancetype)initWithCoder:(NSCoder *)coder { @@ -40,6 +43,7 @@ - (instancetype)initWithCoder:(NSCoder *)coder { NSNumber *objHandle = [coder decodeObjectForKey:@"handle"]; _handle = objHandle.integerValue; _buffer = [coder decodeObjectForKey:@"buffer"]; + _isCurrentInTab = [coder decodeBoolForKey:@"currentInTab"]; } return self; diff --git a/NeoVimServer/server_ui.m b/NeoVimServer/server_ui.m index ddb4c6ff1..65ed8acac 100644 --- a/NeoVimServer/server_ui.m +++ b/NeoVimServer/server_ui.m @@ -779,18 +779,24 @@ void neovim_tabs(void **argv) { FOR_ALL_TABS(t) { NSMutableArray *windows = [NSMutableArray new]; + bool currentTab = curtab ? t->handle == curtab->handle : false; + FOR_ALL_WINDOWS_IN_TAB(win, t) { NeoVimBuffer *buffer = buffer_for(win->w_buffer); if (buffer == nil) { continue; } - NeoVimWindow *window = [[NeoVimWindow alloc] initWithHandle:win->handle buffer:buffer]; + bool current = false; + // tp_curwin is only valid for tabs that aren't the current one + if (currentTab) current = curwin ? win->handle == curwin->handle : false; + else if (t->tp_curwin) current = win->handle == t->tp_curwin->handle; + NeoVimWindow *window = [[NeoVimWindow alloc] initWithHandle:win->handle buffer:buffer currentInTab:current]; [windows addObject:window]; [window release]; } - NeoVimTab *tab = [[NeoVimTab alloc] initWithHandle:t->handle windows:windows]; + NeoVimTab *tab = [[NeoVimTab alloc] initWithHandle:t->handle windows:windows current:currentTab]; [windows release]; [tabs addObject:tab]; diff --git a/SwiftNeoVim/NeoVimView+Api.swift b/SwiftNeoVim/NeoVimView+Api.swift index 770e44ce4..1645a648a 100644 --- a/SwiftNeoVim/NeoVimView+Api.swift +++ b/SwiftNeoVim/NeoVimView+Api.swift @@ -90,6 +90,7 @@ extension NeoVimView { } } + /// Closes the current window. public func closeCurrentTab() { // We don't have to wait here even when neovim quits since we wait in gui.async() block in neoVimStopped(). self.exec(command: "q") diff --git a/SwiftNeoVim/NeoVimView.swift b/SwiftNeoVim/NeoVimView.swift index 68477cdcc..841a55ee2 100644 --- a/SwiftNeoVim/NeoVimView.swift +++ b/SwiftNeoVim/NeoVimView.swift @@ -235,6 +235,9 @@ public class NeoVimView: NSView, var _cwd = URL(fileURLWithPath: NSHomeDirectory()) var shouldDrawCursor = false var isInitialResize = true + + // cache the tabs for Touch Bar use + var tabsCache = [NeoVimTab]() // MARK: - Private fileprivate var _linespacing = NeoVimView.defaultLinespacing From d13e4b71358b600e71dab96a50675df86f9f9c25 Mon Sep 17 00:00:00 2001 From: Greg Omelaenko Date: Wed, 20 Sep 2017 04:47:00 +1000 Subject: [PATCH 4/5] Add Touch Bar support for switching between tabs --- SwiftNeoVim/NeoVimView+TouchBar.swift | 91 +++++++++++++++++++++++++++ SwiftNeoVim/NeoVimView+UiBridge.swift | 6 ++ VimR.xcodeproj/project.pbxproj | 4 ++ 3 files changed, 101 insertions(+) create mode 100644 SwiftNeoVim/NeoVimView+TouchBar.swift diff --git a/SwiftNeoVim/NeoVimView+TouchBar.swift b/SwiftNeoVim/NeoVimView+TouchBar.swift new file mode 100644 index 000000000..7b3ccf14d --- /dev/null +++ b/SwiftNeoVim/NeoVimView+TouchBar.swift @@ -0,0 +1,91 @@ +/** + * Greg Omelaenko - http://omelaen.co + * See LICENSE + */ + +import Cocoa + +@available(OSX 10.12.2, *) +extension NeoVimView : NSTouchBarDelegate, NSScrubberDataSource, NSScrubberDelegate { + + private static let touchBarIdentifier = NSTouchBarCustomizationIdentifier("com.qvacua.VimR.SwiftNeoVim.touchBar") + private static let touchBarTabSwitcherIdentifier = NSTouchBarItemIdentifier("com.qvacua.VimR.SwiftNeoVim.touchBar.tabSwitcher") + private static let touchBarTabSwitcherItem = "com.qvacua.VimR.SwiftNeoVim.touchBar.tabSwitcher.item" + + override public func makeTouchBar() -> NSTouchBar? { + let bar = NSTouchBar() + bar.delegate = self + bar.customizationIdentifier = NeoVimView.touchBarIdentifier + bar.defaultItemIdentifiers = [NeoVimView.touchBarTabSwitcherIdentifier] + bar.customizationRequiredItemIdentifiers = [NeoVimView.touchBarTabSwitcherIdentifier] + return bar + } + + public func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? { + switch identifier { + case NeoVimView.touchBarTabSwitcherIdentifier: + let item = NSCustomTouchBarItem(identifier: identifier) + item.customizationLabel = "Tab Switcher" + let tabsControl = NSScrubber() + tabsControl.register(NSScrubberTextItemView.self, forItemIdentifier: NeoVimView.touchBarTabSwitcherItem) + tabsControl.mode = .fixed + tabsControl.dataSource = self + tabsControl.delegate = self + tabsControl.selectionOverlayStyle = .outlineOverlay + tabsControl.selectedIndex = selectedTabIndex() + let layout = NSScrubberProportionalLayout() + layout.numberOfVisibleItems = 1 + tabsControl.scrubberLayout = layout + item.view = tabsControl + return item + default: + return nil + } + } + + private func selectedTabIndex() -> Int { + return tabsCache.index(where: { $0.isCurrent }) ?? -1 + } + + private func getTabsControl() -> NSScrubber? { + return (self.touchBar?.item(forIdentifier: NeoVimView.touchBarTabSwitcherIdentifier) as? NSCustomTouchBarItem)?.view as? NSScrubber + } + + func updateTouchBarCurrentBuffer() { + guard let tabsControl = getTabsControl() else { return } + tabsCache = self.agent.tabs() + tabsControl.reloadData() + (tabsControl.scrubberLayout as! NSScrubberProportionalLayout).numberOfVisibleItems = tabsControl.numberOfItems > 0 ? tabsControl.numberOfItems : 1 + tabsControl.selectedIndex = selectedTabIndex() + } + + func updateTouchBarTab() { + guard let tabsControl = getTabsControl() else { return } + tabsCache = self.agent.tabs() + if tabsControl.numberOfItems != tabsCache.count { + tabsControl.reloadData() + } + tabsControl.selectedIndex = selectedTabIndex() + tabsControl.reloadItems(at: [tabsControl.selectedIndex]) + } + + public func numberOfItems(for scrubber: NSScrubber) -> Int { + return tabsCache.count + } + + public func scrubber(_ scrubber: NSScrubber, viewForItemAt index: Int) -> NSScrubberItemView { + let itemView = scrubber.makeItem(withIdentifier: type(of: self).touchBarTabSwitcherItem, owner: nil) as! NSScrubberTextItemView + guard tabsCache.count > index else { return itemView } + let tab = tabsCache[index] + itemView.textField.stringValue = tab.currentWindow()?.buffer.name ?? "[No Name]" + + return itemView + } + + public func scrubber(_ scrubber: NSScrubber, didSelectItemAt selectedIndex: Int) { + let tab = tabsCache[selectedIndex] + guard tab.windows.count > 0 else { return } + self.agent.select(tab.currentWindow() ?? tab.windows[0]) + } + +} diff --git a/SwiftNeoVim/NeoVimView+UiBridge.swift b/SwiftNeoVim/NeoVimView+UiBridge.swift index 96a92c355..a740b57e0 100644 --- a/SwiftNeoVim/NeoVimView+UiBridge.swift +++ b/SwiftNeoVim/NeoVimView+UiBridge.swift @@ -358,6 +358,9 @@ extension NeoVimView { } self.delegate?.currentBufferChanged(currentBuffer) + if #available(OSX 10.12.2, *) { + self.updateTouchBarTab() + } } fileprivate func tabChanged() { @@ -370,6 +373,9 @@ extension NeoVimView { fileprivate func bufferListChanged() { self.delegate?.bufferListChanged() + if #available(OSX 10.12.2, *) { + self.updateTouchBarCurrentBuffer() + } } } diff --git a/VimR.xcodeproj/project.pbxproj b/VimR.xcodeproj/project.pbxproj index abd38d073..ec5511de7 100644 --- a/VimR.xcodeproj/project.pbxproj +++ b/VimR.xcodeproj/project.pbxproj @@ -96,6 +96,7 @@ 1929BFC70581084B5CE04A5B /* MatcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BFE179BCA3C75A13D71B /* MatcherTests.swift */; }; 1929BFDE22D155F7C4B19E96 /* HtmlPreviewTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B85023B042C485409CE1 /* HtmlPreviewTool.swift */; }; 1F1000F81F0ABC0000CA3195 /* NeoVimView+Dragging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F1000F71F0ABC0000CA3195 /* NeoVimView+Dragging.swift */; }; + 373416BD1F71879300A87A92 /* NeoVimView+TouchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373416BC1F71879300A87A92 /* NeoVimView+TouchBar.swift */; }; 4B029F1A1D45E349004EE0D3 /* PrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B029F1C1D45E349004EE0D3 /* PrefWindow.xib */; }; 4B0BCC941D70320C00D3CE65 /* Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B0BCC931D70320C00D3CE65 /* Logger.h */; settings = {ATTRIBUTES = (Private, ); }; }; 4B12CD891F5A985600167D59 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B12CD881F5A985600167D59 /* AppDelegate.swift */; }; @@ -493,6 +494,7 @@ 1929BFC0A5A9C6DB09BE1368 /* Types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; 1929BFE179BCA3C75A13D71B /* MatcherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatcherTests.swift; sourceTree = ""; }; 1F1000F71F0ABC0000CA3195 /* NeoVimView+Dragging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NeoVimView+Dragging.swift"; sourceTree = ""; }; + 373416BC1F71879300A87A92 /* NeoVimView+TouchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NeoVimView+TouchBar.swift"; sourceTree = ""; }; 4B029F1B1D45E349004EE0D3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PrefWindow.xib; sourceTree = ""; }; 4B0BCC931D70320C00D3CE65 /* Logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logger.h; path = VimR/Logger.h; sourceTree = SOURCE_ROOT; }; 4B12CD861F5A985600167D59 /* ThemedWindow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ThemedWindow.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1165,6 +1167,7 @@ 1929BDF3167E15E7F3349798 /* NeoVimView+Key.swift */, 1929B4F65149D3C3E326DA65 /* NeoVimView+MenuItems.swift */, 1F1000F71F0ABC0000CA3195 /* NeoVimView+Dragging.swift */, + 373416BC1F71879300A87A92 /* NeoVimView+TouchBar.swift */, ); name = NeoVimView; sourceTree = ""; @@ -1596,6 +1599,7 @@ 4BDD05891DBBC50000D1B405 /* NeoVimTab.m in Sources */, 4BEE79171D16D3800012EDAA /* CellAttributes.swift in Sources */, 4BDD05861DBBC50000D1B405 /* NeoVimBuffer.m in Sources */, + 373416BD1F71879300A87A92 /* NeoVimView+TouchBar.swift in Sources */, 4BF6E29C1D34153C0053FA76 /* KeyUtils.swift in Sources */, 4BCADE081D11ED12004DAD0F /* CocoaExtensions.swift in Sources */, 4B401B1A1D046E0600D99EDC /* NeoVimViewDelegate.swift in Sources */, From b0862601ef8a7b149143f435322b04d7e0a03658 Mon Sep 17 00:00:00 2001 From: Greg Omelaenko Date: Sat, 7 Oct 2017 14:38:11 +1100 Subject: [PATCH 5/5] Change the way Touch Bar control titles are set --- SwiftNeoVim/NeoVimView+TouchBar.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SwiftNeoVim/NeoVimView+TouchBar.swift b/SwiftNeoVim/NeoVimView+TouchBar.swift index 7b3ccf14d..fa905f821 100644 --- a/SwiftNeoVim/NeoVimView+TouchBar.swift +++ b/SwiftNeoVim/NeoVimView+TouchBar.swift @@ -77,7 +77,7 @@ extension NeoVimView : NSTouchBarDelegate, NSScrubberDataSource, NSScrubberDeleg let itemView = scrubber.makeItem(withIdentifier: type(of: self).touchBarTabSwitcherItem, owner: nil) as! NSScrubberTextItemView guard tabsCache.count > index else { return itemView } let tab = tabsCache[index] - itemView.textField.stringValue = tab.currentWindow()?.buffer.name ?? "[No Name]" + itemView.title = tab.currentWindow()?.buffer.name ?? "[No Name]" return itemView }