diff --git a/NvimView/DrawerDev/Info.plist b/NvimView/DrawerDev/Info.plist index cbb9b409..bb0e22c7 100644 --- a/NvimView/DrawerDev/Info.plist +++ b/NvimView/DrawerDev/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - SNAPSHOT-300 + SNAPSHOT-301 CFBundleVersion - 300 + 301 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright diff --git a/NvimView/NvimServer/NvimServer.m b/NvimView/NvimServer/NvimServer.m index 87d28449..314a254d 100644 --- a/NvimView/NvimServer/NvimServer.m +++ b/NvimView/NvimServer/NvimServer.m @@ -68,6 +68,9 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD case NvimBridgeMsgIdFocusGained: return data_async(data, neovim_focus_gained); + case NvimBridgeMsgIdReadyForRpcEvents: + return data_async(data, neovim_ready_for_rpcevents); + default: CFRelease(data); return NULL; diff --git a/NvimView/NvimServer/server_ui.h b/NvimView/NvimServer/server_ui.h index 04739e89..fb3f7bcc 100644 --- a/NvimView/NvimServer/server_ui.h +++ b/NvimView/NvimServer/server_ui.h @@ -19,5 +19,6 @@ extern void neovim_vim_input(void **argv); extern void neovim_delete_and_input(void **argv); extern void neovim_focus_gained(void **argv); +extern void neovim_ready_for_rpcevents(void **argv); extern void neovim_debug1(void **argv); diff --git a/NvimView/NvimServer/server_ui.m b/NvimView/NvimServer/server_ui.m index e2c811ae..f9d40fe6 100644 --- a/NvimView/NvimServer/server_ui.m +++ b/NvimView/NvimServer/server_ui.m @@ -16,6 +16,7 @@ #define FileInfo CarbonFileInfo #define Boolean CarbonBoolean +#import #import #import #import @@ -73,6 +74,8 @@ static NSInteger _initialHeight = 15; static msgpack_sbuffer flush_sbuffer; static msgpack_packer *flush_packer; +static dispatch_queue_t rpc_queue; + #pragma mark Helper functions static inline String vim_string_from(NSString *str) { @@ -245,6 +248,8 @@ static void server_ui_main(UIBridgeData *bridge, UI *ui) { msgpack_sbuffer_init(&flush_sbuffer); flush_packer = msgpack_packer_new(&flush_sbuffer, msgpack_sbuffer_write); + rpc_queue = dispatch_queue_create("rpc_queu", NULL); + Loop loop; loop_init(&loop, NULL); @@ -553,6 +558,13 @@ void custom_ui_start(void) { ui_bridge_attach(ui, server_ui_main, server_ui_scheduler); } +void custom_ui_rpcevent_subscribed() { + dispatch_async(rpc_queue, ^{ + [_neovim_server sendMessageWithId:NvimServerMsgIdRpcEventSubscribed + data:NULL]; + }); +} + void custom_ui_autocmds_groups( event_T event, char_u *fname __unused, @@ -573,6 +585,8 @@ void custom_ui_autocmds_groups( case EVENT_TABENTER: case EVENT_TEXTCHANGED: case EVENT_TEXTCHANGEDI: + case EVENT_VIMENTER: + case EVENT_GUIENTER: break; default: @@ -739,6 +753,25 @@ void neovim_delete_and_input(void **argv) { }); } +static void do_autocmd_guienter() { + static bool recursive = false; + + if (recursive) { + return; // disallow recursion + } + recursive = true; + apply_autocmds(EVENT_GUIENTER, NULL, NULL, false, curbuf); + recursive = false; +} + +static void guienter_event(void **argv __unused) { + do_autocmd_guienter(); +} + +static void aucmd_schedule_guienter() { + loop_schedule_deferred(&main_loop, event_create(guienter_event, 0)); +} + void neovim_focus_gained(void **argv) { work_async(argv, ^(NSData *data) { const bool *values = data.bytes; @@ -747,6 +780,12 @@ void neovim_focus_gained(void **argv) { }); } +void neovim_ready_for_rpcevents(void **argv) { + work_async(argv, ^(NSData *data) { + aucmd_schedule_guienter(); + }); +} + void neovim_debug1(void **argv) { work_async(argv, ^(NSData *data) { NSLog(@"normal fg: %#08X", normal_fg); diff --git a/NvimView/NvimView.xcodeproj/project.pbxproj b/NvimView/NvimView.xcodeproj/project.pbxproj index 50464733..6e27b234 100644 --- a/NvimView/NvimView.xcodeproj/project.pbxproj +++ b/NvimView/NvimView.xcodeproj/project.pbxproj @@ -45,7 +45,7 @@ 4B177886201220F300E32FF0 /* SharedTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 1929B4F32708E99C40A57020 /* SharedTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B17E549209E3E4100265C1D /* RxNeovimApi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B17E548209E3E4100265C1D /* RxNeovimApi.framework */; }; 4B21ED53213D4AEC009FD017 /* CocoaCommons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B0C89838D8402BB80BFC /* CocoaCommons.swift */; }; - 4B8662E81FDC3F9F007F490D /* vimr.vim in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4B8662E41FDC3D4F007F490D /* vimr.vim */; }; + 4B8662E81FDC3F9F007F490D /* com.qvacua.NvimView.vim in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4B8662E41FDC3D4F007F490D /* com.qvacua.NvimView.vim */; }; 4B90F02E1FD2AFAE008A39E0 /* NvimView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B90F0101FD2AFAC008A39E0 /* NvimView.swift */; }; 4B90F02F1FD2AFAE008A39E0 /* NvimView+Resize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B90F0111FD2AFAC008A39E0 /* NvimView+Resize.swift */; }; 4B90F0301FD2AFAE008A39E0 /* KeyUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B90F0121FD2AFAC008A39E0 /* KeyUtils.swift */; }; @@ -140,7 +140,7 @@ dstPath = runtime/plugin; dstSubfolderSpec = 7; files = ( - 4B8662E81FDC3F9F007F490D /* vimr.vim in CopyFiles */, + 4B8662E81FDC3F9F007F490D /* com.qvacua.NvimView.vim in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -199,7 +199,7 @@ 4B0A1B152129F49500F1E02F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4B0A1B38212B332800F1E02F /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = ../Carthage/Build/Mac/Nimble.framework; sourceTree = ""; }; 4B17E548209E3E4100265C1D /* RxNeovimApi.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxNeovimApi.framework; path = ../Carthage/Build/Mac/RxNeovimApi.framework; sourceTree = ""; }; - 4B8662E41FDC3D4F007F490D /* vimr.vim */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = vimr.vim; sourceTree = ""; }; + 4B8662E41FDC3D4F007F490D /* com.qvacua.NvimView.vim */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.vim; path = com.qvacua.NvimView.vim; sourceTree = ""; }; 4B90F0041FD2AF59008A39E0 /* NvimView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NvimView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4B90F0081FD2AF59008A39E0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4B90F0101FD2AFAC008A39E0 /* NvimView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NvimView.swift; sourceTree = ""; }; @@ -359,7 +359,7 @@ isa = PBXGroup; children = ( 4B90F0081FD2AF59008A39E0 /* Info.plist */, - 4B8662E41FDC3D4F007F490D /* vimr.vim */, + 4B8662E41FDC3D4F007F490D /* com.qvacua.NvimView.vim */, 4BF18C5C1FD2EEE400DF95D1 /* NvimView.h */, 4B90F0121FD2AFAC008A39E0 /* KeyUtils.swift */, 4B90F0271FD2AFAD008A39E0 /* Logger.swift */, @@ -783,7 +783,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 301; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -845,7 +845,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 301; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -874,7 +874,7 @@ COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 300; + DYLIB_CURRENT_VERSION = 301; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac"; FRAMEWORK_VERSION = A; @@ -896,7 +896,7 @@ COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 300; + DYLIB_CURRENT_VERSION = 301; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac"; FRAMEWORK_VERSION = A; diff --git a/NvimView/NvimView/Info.plist b/NvimView/NvimView/Info.plist index d1a2ab21..4cb41d50 100644 --- a/NvimView/NvimView/Info.plist +++ b/NvimView/NvimView/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType FMWK CFBundleShortVersionString - SNAPSHOT-300 + SNAPSHOT-301 CFBundleVersion - 300 + 301 NSHumanReadableCopyright Copyright © 2017 Tae Won Ha. All rights reserved. NSPrincipalClass diff --git a/NvimView/NvimView/Logger.swift b/NvimView/NvimView/Logger.swift index 78008a74..6451f83b 100644 --- a/NvimView/NvimView/Logger.swift +++ b/NvimView/NvimView/Logger.swift @@ -86,7 +86,7 @@ class Logger { default: self.name = String(describing: name) } - self.logDateFormatter.dateFormat = "dd HH:mm:SSS" + self.logDateFormatter.dateFormat = "dd HH:mm:ss.SSS" self.appender = appender } diff --git a/NvimView/NvimView/NvimView+Draw.swift b/NvimView/NvimView/NvimView+Draw.swift index 31ad1037..eecffa81 100644 --- a/NvimView/NvimView/NvimView+Draw.swift +++ b/NvimView/NvimView/NvimView+Draw.swift @@ -88,6 +88,13 @@ extension NvimView { } let cursorRegion = self.cursorRegion(for: self.ugrid.cursorPosition) + if cursorRegion.top < 0 + || cursorRegion.bottom > self.ugrid.size.height - 1 + || cursorRegion.left < 0 + || cursorRegion.right > self.ugrid.size.width - 1 { + logger.error("\(cursorRegion) vs. \(self.ugrid.size)") + return + } guard let cursorAttrs = self.cellAttributesCollection.attributes( of: self.ugrid.cells[cursorPosition.row][cursorPosition.column].attrId )?.reversed else { diff --git a/NvimView/NvimView/NvimView+Resize.swift b/NvimView/NvimView/NvimView+Resize.swift index f9ae9e01..4a365dde 100644 --- a/NvimView/NvimView/NvimView+Resize.swift +++ b/NvimView/NvimView/NvimView+Resize.swift @@ -52,8 +52,12 @@ extension NvimView { return } - self.offset.x = floor((size.width - self.cellSize.width * CGFloat(discreteSize.width)) / 2) - self.offset.y = floor((size.height - self.cellSize.height * CGFloat(discreteSize.height)) / 2) + self.offset.x = floor( + (size.width - self.cellSize.width * CGFloat(discreteSize.width)) / 2 + ) + self.offset.y = floor( + (size.height - self.cellSize.height * CGFloat(discreteSize.height)) / 2 + ) self.bridge .resize(width: discreteSize.width, height: discreteSize.height) @@ -62,32 +66,50 @@ extension NvimView { private func launchNeoVim(_ size: Size) { logger.info("=== Starting neovim...") - let sockPath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("vimr_\(self.uuid).sock").path + let sockPath = URL( + fileURLWithPath: NSTemporaryDirectory() + ).appendingPathComponent("vimr_\(self.uuid).sock").path self.api.msgpackRawStream - .subscribe(onNext: { msg in - switch msg { - case let .notification(method, params): - print("NOTIFICATION: \(method) with \(params.count) elements") - case let .error(_, msg): - print("MSG ERROR: \(msg)") - default: - print("???") - break - } - }, onError: { print("ERROR: \($0)" )}) - .disposed(by: self.disposeBag) + .subscribe(onNext: { msg in + switch msg { - // We wait here, since the user of NvimView cannot subscribe on the Completable. We could demand that the user - // call launchNeoVim() by themselves, but... + case let .notification(method, params): + logger.debug("NOTIFICATION: \(method): \(params)") + + guard method == NvimView.rpcEventName else { return } + self.eventsSubject.onNext(.rpcEvent(method, params)) + + case let .error(_, msg): + logger.debug("MSG ERROR: \(msg)") + + default: + logger.debug("???: This should not happen") + break + + } + }, onError: { print("ERROR: \($0)") }) + .disposed(by: self.disposeBag) + + // We wait here, since the user of NvimView cannot subscribe + // on the Completable. We could demand that the user call launchNeoVim() + // by themselves, but... try? self.bridge .runLocalServerAndNvim(width: size.width, height: size.height) .andThen(self.api.run(at: sockPath)) + .andThen( + self.sourceFileUrls.reduce(Completable.empty()) { prev, url in + prev.andThen(self.api + .commandOutput(str: "source \(url.path)") + .asCompletable()) + } + ) + .andThen(self.api.subscribe(event: NvimView.rpcEventName)) .wait() } private func randomEmoji() -> String { - let idx = Int(arc4random_uniform(UInt32(emojis.count))) + let idx = Int.random(in: 0.. \(bufferHandle)") #endif @@ -200,8 +238,8 @@ extension NvimView { #if TRACE self.bridgeLogger.trace( "row: \(row), startCol: \(startCol), endCol: \(endCol), " + - "clearCol: \(clearCol), clearAttr: \(clearAttr), " + - "chunk: \(chunk), attrIds: \(attrIds)" + "clearCol: \(clearCol), clearAttr: \(clearAttr), " + + "chunk: \(chunk), attrIds: \(attrIds)" ) #endif @@ -242,13 +280,13 @@ extension NvimView { } if row == self.markedPosition.row - && startCol <= self.markedPosition.column - && self.markedPosition.column <= endCol { + && startCol <= self.markedPosition.column + && self.markedPosition.column <= endCol { self.ugrid.markCell(at: self.markedPosition) } let oldRowContainsWideChar = self.ugrid.cells[row][startCol.." + "fg: \(self.foreground.hex), bg: \(self.background.hex), " + + "visual-fg: \(self.visualForeground.hex), visual-bg: \(self.visualBackground.hex)" + + ">" } } + public static let rpcEventName = "com.qvacua.NvimView" + public static let minFontSize = CGFloat(4) public static let maxFontSize = CGFloat(128) public static let defaultFont = NSFont.userFixedPitchFont(ofSize: 12)! @@ -112,7 +122,7 @@ public class NvimView: NSView, public var isLeftOptionMeta = false public var isRightOptionMeta = false - public let uuid = UUID().uuidString + public let uuid = UUID() public internal(set) var mode = CursorModeShape.normal @@ -202,6 +212,8 @@ public class NvimView: NSView, self.scheduler = SerialDispatchQueueScheduler(queue: self.queue, internalSerialQueueName: "com.qvacua.NvimView.NvimView") + self.sourceFileUrls = config.sourceFiles + super.init(frame: .zero) self.registerForDraggedTypes([NSPasteboard.PasteboardType(String(kUTTypeFileURL))]) @@ -285,6 +297,10 @@ public class NvimView: NSView, case let .highlightAttrs(value): self.setAttr(with: value) + case .rpcEventSubscribed: + self.rpcEventSubscribed() + + case .debug1: self.debug1(self) @@ -296,10 +312,16 @@ public class NvimView: NSView, } convenience override public init(frame rect: NSRect) { - self.init(frame: rect, config: Config(useInteractiveZsh: false, - cwd: URL(fileURLWithPath: NSHomeDirectory()), - nvimArgs: nil, - envDict: nil)) + self.init( + frame: rect, + config: Config( + useInteractiveZsh: false, + cwd: URL(fileURLWithPath: NSHomeDirectory()), + nvimArgs: nil, + envDict: nil, + sourceFiles: [] + ) + ) } required public init?(coder: NSCoder) { @@ -364,6 +386,11 @@ public class NvimView: NSView, shouldLogDebug: nil ) + let sourceFileUrls: [URL] + + let rpcEventSubscribedCondition = NSCondition() + var rpcEventSubscribedFlag = false + // MARK: - Private private var _linespacing = NvimView.defaultLinespacing } diff --git a/NvimView/NvimView/UiBridge.swift b/NvimView/NvimView/UiBridge.swift index 40737276..8c43bacc 100644 --- a/NvimView/NvimView/UiBridge.swift +++ b/NvimView/NvimView/UiBridge.swift @@ -35,6 +35,7 @@ class UiBridge { case defaultColorsChanged(MessagePackValue) case autoCommandEvent(MessagePackValue) case highlightAttrs(MessagePackValue) + case rpcEventSubscribed case debug1 case unknown } @@ -51,7 +52,7 @@ class UiBridge { return self.streamSubject.asObservable() } - init(uuid: String, queue: DispatchQueue, config: NvimView.Config) { + init(uuid: UUID, queue: DispatchQueue, config: NvimView.Config) { self.uuid = uuid self.useInteractiveZsh = config.useInteractiveZsh @@ -125,6 +126,10 @@ class UiBridge { return self.sendMessage(msgId: .resize, data: [width, height].data()) } + func notifyReadinessForRpcEvents() -> Completable { + return self.sendMessage(msgId: .readyForRpcEvents, data: nil) + } + func focusGained(_ gained: Bool) -> Completable { return self.sendMessage(msgId: .focusGained, data: [gained].data()) } @@ -249,6 +254,9 @@ class UiBridge { guard let v = MessagePackUtils.value(from: data) else { return } self.streamSubject.onNext(.highlightAttrs(v)) + case .rpcEventSubscribed: + self.streamSubject.onNext(.rpcEventSubscribed) + } } @@ -319,7 +327,7 @@ class UiBridge { private let logger = LogContext.fileLogger(as: UiBridge.self, with: URL(fileURLWithPath: "/tmp/nvv-bridge.log")) - private let uuid: String + private let uuid: UUID private let useInteractiveZsh: Bool private let cwd: URL diff --git a/NvimView/NvimView/com.qvacua.NvimView.vim b/NvimView/NvimView/com.qvacua.NvimView.vim new file mode 100644 index 00000000..1f1b847e --- /dev/null +++ b/NvimView/NvimView/com.qvacua.NvimView.vim @@ -0,0 +1,45 @@ +set termguicolors +set mouse=a +set title + +"function! s:VimRMakeSessionTemporary() abort +" call rpcnotify(0, 'com.qvacua.vimr.rpc-events.make-session-temporary') +"endfunction +"command! -nargs=0 VimRMakeSessionTemporary call s:VimRMakeSessionTemporary() +" +"function! s:VimRMaximizeWindow() abort +" call rpcnotify(0, 'com.qvacua.vimr.rpc-events.maximize-window') +"endfunction +"command! -nargs=0 VimRMaximizeWindow call s:VimRMaximizeWindow() +" +"" -1: hide, 0: toggle, 1: show +"function! s:VimRToggleTools(value) abort +" call rpcnotify(0, 'com.qvacua.vimr.rpc-events.toggle-tools', a:value) +"endfunction +"command! -nargs=0 VimRHideTools call s:VimRToggleTools(-1) +"command! -nargs=0 VimRToggleTools call s:VimRToggleTools(0) +"command! -nargs=0 VimRShowTools call s:VimRToggleTools(1) +" +"" -1: hide, 0: toggle, 1: show +"function! s:VimRToggleToolButtons(value) abort +" call rpcnotify(0, 'com.qvacua.vimr.rpc-events.toggle-tool-buttons', a:value) +"endfunction +"command! -nargs=0 VimRHideToolButtons call s:VimRToggleToolButtons(-1) +"command! -nargs=0 VimRToggleToolButtons call s:VimRToggleToolButtons(0) +"command! -nargs=0 VimRShowToolButtons call s:VimRToggleToolButtons(1) +" +"function! s:VimRToggleFullscreen() abort +" call rpcnotify(0, 'com.qvacua.vimr.rpc-events.toggle-fullscreen') +"endfunction +"command! -nargs=0 VimRToggleFullscreen call s:VimRToggleFullscreen() +" +"function! s:VimRTest() abort +" VimRMakeSessionTemporary +" VimRHideTools +" VimRMaximizeWindow +" normal o +"endfunction +"command! -nargs=0 VimRTest call s:VimRTest() +" +""au VimEnter * echo "hello" +""au GuiEnter * echo "world" diff --git a/NvimView/NvimView/vimr.vim b/NvimView/NvimView/vimr.vim deleted file mode 100644 index 69002294..00000000 --- a/NvimView/NvimView/vimr.vim +++ /dev/null @@ -1,3 +0,0 @@ -set termguicolors -set mouse=a -set title diff --git a/NvimView/NvimViewTests/Info.plist b/NvimView/NvimViewTests/Info.plist index 32fdfdea..9252666e 100644 --- a/NvimView/NvimViewTests/Info.plist +++ b/NvimView/NvimViewTests/Info.plist @@ -15,8 +15,8 @@ CFBundlePackageType BNDL CFBundleShortVersionString - SNAPSHOT-300 + SNAPSHOT-301 CFBundleVersion - 300 + 301 diff --git a/NvimView/SharedTypes.h b/NvimView/SharedTypes.h index be314a2c..3beae944 100644 --- a/NvimView/SharedTypes.h +++ b/NvimView/SharedTypes.h @@ -41,12 +41,14 @@ typedef NS_ENUM(NSInteger, NvimServerMsgId) { NvimServerMsgIdColorSchemeChanged, NvimServerMsgIdDefaultColorsChanged, NvimServerMsgIdAutoCommandEvent, - + NvimServerMsgIdRpcEventSubscribed, + NvimServerMsgIdDebug1, }; typedef NS_ENUM(NSInteger, NvimBridgeMsgId) { NvimBridgeMsgIdAgentReady = 0, + NvimBridgeMsgIdReadyForRpcEvents, NvimBridgeMsgIdInput, NvimBridgeMsgIdDeleteInput, NvimBridgeMsgIdResize, diff --git a/NvimView/neovim b/NvimView/neovim index 9f731c05..b212d045 160000 --- a/NvimView/neovim +++ b/NvimView/neovim @@ -1 +1 @@ -Subproject commit 9f731c05656fad98cc16bbbef7d1626b33695efc +Subproject commit b212d045162eb07faee944220e96d27618c296e7 diff --git a/VimR/VimR.xcodeproj/project.pbxproj b/VimR/VimR.xcodeproj/project.pbxproj index c2df9b57..97f8d051 100644 --- a/VimR/VimR.xcodeproj/project.pbxproj +++ b/VimR/VimR.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 1929B0F599D1F62C7BE53D2C /* HttpServerMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B1DC584C89C477E83FA2 /* HttpServerMiddleware.swift */; }; 1929B1837C750CADB3A5BCB9 /* OpenQuicklyFileViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B1558455B3A74D93EF2A /* OpenQuicklyFileViewRow.swift */; }; 1929B20CE35B43BB1CE023BA /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BC2F05E9A5C0DB039739 /* Theme.swift */; }; + 1929B250DB3FB395A700FE8C /* RpcEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BBF0944940845485A512 /* RpcEvents.swift */; }; 1929B29B95AD176D57942E08 /* UiRootReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B457B9D0FA4D21F3751E /* UiRootReducer.swift */; }; 1929B2D56C4652E251C23AD4 /* DefaultShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B93256AF7F9137223E36 /* DefaultShortcuts.swift */; }; 1929B3217A7A3D79E28C80DB /* PrefWindowReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B49E6924847AD085C8C9 /* PrefWindowReducer.swift */; }; @@ -26,6 +27,7 @@ 1929B3AC66EFE35D68C020E3 /* PreviewToolReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BFB0F294F3714D5E095F /* PreviewToolReducer.swift */; }; 1929B3F5743967125F357C9F /* Matcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BEEB33113B0E33C3830F /* Matcher.swift */; }; 1929B462CD4935AFF6D69457 /* FileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B7CB4863F80230C32D3C /* FileItem.swift */; }; + 1929B489A51FD5B13888A00C /* RpcEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BBF0944940845485A512 /* RpcEvents.swift */; }; 1929B4B00D7BB191A9A6532D /* HtmlPreviewToolReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BE5AEA3D0980860EED50 /* HtmlPreviewToolReducer.swift */; }; 1929B4B70926DE113E6BF990 /* PreviewReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BE37AA2843779CAFA76F /* PreviewReducer.swift */; }; 1929B4E54E2F13A7F5F2B682 /* BufferListReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B67A10E6BB2986B2416E /* BufferListReducer.swift */; }; @@ -36,6 +38,7 @@ 1929B53876E6952D378C2B30 /* ScoredFileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BDF9EBAF1D9D44399045 /* ScoredFileItem.swift */; }; 1929B542A071BD03C846F6EF /* PrefUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B8241CDE58F7AAF89AE4 /* PrefUtils.swift */; }; 1929B5543B1E31A26096E656 /* FileMonitorReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B04EC69F616EEFAF5F96 /* FileMonitorReducer.swift */; }; + 1929B560C6CE264FD1E1F5A3 /* com.qvacua.VimR.vim in Resources */ = {isa = PBXBuildFile; fileRef = 1929BC6D45B7E14D4D75D4E6 /* com.qvacua.VimR.vim */; }; 1929B59FA5C286E010F70BEE /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BFC0A5A9C6DB09BE1368 /* Types.swift */; }; 1929B5A2EE366F79ED32744C /* KeysPrefReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B88B5FA08E897A3C2168 /* KeysPrefReducer.swift */; }; 1929B5C1BABBC0D09D97C3EF /* PreviewMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B617C229B19DB3E987B8 /* PreviewMiddleware.swift */; }; @@ -352,7 +355,9 @@ 1929BB6608B4F0E037CA0F4C /* States.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = States.swift; sourceTree = ""; }; 1929BBC84557C8351EC6183E /* FileItemIgnorePatternTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileItemIgnorePatternTest.swift; sourceTree = ""; }; 1929BBE0A534F2F6009D31BE /* AdvencedPref.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvencedPref.swift; sourceTree = ""; }; + 1929BBF0944940845485A512 /* RpcEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RpcEvents.swift; sourceTree = ""; }; 1929BC2F05E9A5C0DB039739 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; + 1929BC6D45B7E14D4D75D4E6 /* com.qvacua.VimR.vim */ = {isa = PBXFileReference; lastKnownFileType = file.vim; path = com.qvacua.VimR.vim; sourceTree = ""; }; 1929BCE3E156C06EDF1F2806 /* FileOutlineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileOutlineView.swift; sourceTree = ""; }; 1929BD2CA8DD198A6BCDBCB7 /* ThemedTableSubviews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemedTableSubviews.swift; sourceTree = ""; }; 1929BD4149D5A25C82064DD8 /* UiRoot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UiRoot.swift; sourceTree = ""; }; @@ -817,6 +822,7 @@ 4BEBA50F1CFF374B00673FDF /* Info.plist */, 4B37ADB81D6E471B00970D55 /* vimr */, 4B3AC8931DB031C600AC5823 /* sparkle_pub.pem */, + 1929BC6D45B7E14D4D75D4E6 /* com.qvacua.VimR.vim */, ); name = resources; sourceTree = ""; @@ -878,6 +884,7 @@ 4B97E2CF1D33F92200FC0660 /* resources */, 1929BA652D3B88FC071531EC /* UI */, 1929B66A5E2D00EA143AFD86 /* RxRedux.swift */, + 1929BBF0944940845485A512 /* RpcEvents.swift */, ); path = VimR; sourceTree = ""; @@ -1067,6 +1074,7 @@ 4B9433E620B95EC6005807BA /* MacVim-log.icns in Resources */, 4B94340D20B95EC7005807BA /* MacVim-dtd.icns in Resources */, 4B9433DF20B95EC6005807BA /* MacVim-lisp.icns in Resources */, + 1929B560C6CE264FD1E1F5A3 /* com.qvacua.VimR.vim in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1182,6 +1190,7 @@ 1929BA269EBD68251410A08E /* ShortcutsTableSubviews.swift in Sources */, 1929B2D56C4652E251C23AD4 /* DefaultShortcuts.swift in Sources */, 1929B0C7150100A84FBDB8BF /* ShortcutItem.swift in Sources */, + 1929B250DB3FB395A700FE8C /* RpcEvents.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1216,6 +1225,7 @@ 1929BC682EA78BF50D1E0890 /* ShortcutsTableSubviews.swift in Sources */, 1929B0244BD7111E168726CF /* DefaultShortcuts.swift in Sources */, 1929BDC69A5F9D1661423488 /* ShortcutItem.swift in Sources */, + 1929B489A51FD5B13888A00C /* RpcEvents.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1310,7 +1320,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 301; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -1368,7 +1378,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 301; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; diff --git a/VimR/VimR/AppDelegateReducer.swift b/VimR/VimR/AppDelegateReducer.swift index b1d734b1..339bb638 100644 --- a/VimR/VimR/AppDelegateReducer.swift +++ b/VimR/VimR/AppDelegateReducer.swift @@ -50,7 +50,7 @@ class AppDelegateReducer: ReducerType { var mainWindow = state.mainWindowTemplate - mainWindow.uuid = UUID().uuidString + mainWindow.uuid = UUID() mainWindow.cwd = config.cwd mainWindow.isDirty = false diff --git a/VimR/VimR/Base.lproj/MainMenu.xib b/VimR/VimR/Base.lproj/MainMenu.xib index a38c959b..1c69ad33 100644 --- a/VimR/VimR/Base.lproj/MainMenu.xib +++ b/VimR/VimR/Base.lproj/MainMenu.xib @@ -220,6 +220,21 @@ + + + + + + + + + + + + + + + @@ -264,7 +279,8 @@ - + + diff --git a/VimR/VimR/BufferList.swift b/VimR/VimR/BufferList.swift index 174ad412..499e4e60 100644 --- a/VimR/VimR/BufferList.swift +++ b/VimR/VimR/BufferList.swift @@ -48,6 +48,11 @@ class BuffersList: NSView, source .observeOn(MainScheduler.instance) .subscribe(onNext: { state in + if state.viewToBeFocused != nil, + case .bufferList = state.viewToBeFocused! { + self.beFirstResponder() + } + let themeChanged = changeTheme( themePrefChanged: state.appearance.usesTheme != self.usesTheme, themeChanged: state.appearance.theme.mark != self.lastThemeMark, @@ -73,7 +78,7 @@ class BuffersList: NSView, private let emit: (UuidAction) -> Void private let disposeBag = DisposeBag() - private let uuid: String + private let uuid: UUID private var usesTheme: Bool private var showsFileIcon: Bool diff --git a/VimR/VimR/DefaultShortcuts.swift b/VimR/VimR/DefaultShortcuts.swift index d151a640..0c2645d3 100644 --- a/VimR/VimR/DefaultShortcuts.swift +++ b/VimR/VimR/DefaultShortcuts.swift @@ -111,6 +111,24 @@ let defaultShortcuts: [String: [String: Any]] = [ SRShortcutKeyCode: 18, SRShortcutModifierFlagsKey: 1048840, ], + "com.qvacua.vimr.menuitems.tools.toggle-buffer-list": [ + SRShortcutCharacters: "1", + SRShortcutCharactersIgnoringModifiers: "2", + SRShortcutKeyCode: 19, + SRShortcutModifierFlagsKey: 1048840, + ], + "com.qvacua.vimr.menuitems.tools.toggle-markdown-preview": [ + SRShortcutCharacters: "1", + SRShortcutCharactersIgnoringModifiers: "3", + SRShortcutKeyCode: 20, + SRShortcutModifierFlagsKey: 1048840, + ], + "com.qvacua.vimr.menuitems.tools.toggle-html-preview": [ + SRShortcutCharacters: "1", + SRShortcutCharactersIgnoringModifiers: "4", + SRShortcutKeyCode: 21, + SRShortcutModifierFlagsKey: 1048840, + ], "com.qvacua.vimr.menuitems.tools.toggle-tool-buttons": [ SRShortcutCharacters: "\\", SRShortcutCharactersIgnoringModifiers: "|", diff --git a/VimR/VimR/FileBrowser.swift b/VimR/VimR/FileBrowser.swift index 06a474f3..671df4b5 100644 --- a/VimR/VimR/FileBrowser.swift +++ b/VimR/VimR/FileBrowser.swift @@ -67,7 +67,7 @@ class FileBrowser: NSView, private let emit: (UuidAction) -> Void private let disposeBag = DisposeBag() - private let uuid: String + private let uuid: UUID private var currentBufferUrl: URL? diff --git a/VimR/VimR/FileOutlineView.swift b/VimR/VimR/FileOutlineView.swift index 7ce4a34b..c4be8e1a 100644 --- a/VimR/VimR/FileOutlineView.swift +++ b/VimR/VimR/FileOutlineView.swift @@ -130,7 +130,7 @@ class FileOutlineView: NSOutlineView, private let emit: (UuidAction) -> Void private let disposeBag = DisposeBag() - private let uuid: String + private let uuid: UUID private var root: Node private var cwd: URL { diff --git a/VimR/VimR/HtmlPreviewTool.swift b/VimR/VimR/HtmlPreviewTool.swift index dab02215..847e8d1c 100644 --- a/VimR/VimR/HtmlPreviewTool.swift +++ b/VimR/VimR/HtmlPreviewTool.swift @@ -45,6 +45,11 @@ class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate { source .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] state in + if state.viewToBeFocused != nil, + case .htmlPreview = state.viewToBeFocused! { + self.beFirstResponder() + } + guard let serverUrl = state.htmlPreview.server, let htmlFileUrl = state.htmlPreview.htmlFile else { self.monitor = nil return @@ -87,7 +92,7 @@ class HtmlPreviewTool: NSView, UiComponent, WKNavigationDelegate { } private let emit: (UuidAction) -> Void - private let uuid: String + private let uuid: UUID private var mark = Token() private var scrollTop = 0 diff --git a/VimR/VimR/Info.plist b/VimR/VimR/Info.plist index 14affb17..86db3cf6 100644 --- a/VimR/VimR/Info.plist +++ b/VimR/VimR/Info.plist @@ -1224,7 +1224,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - SNAPSHOT-300 + SNAPSHOT-301 CFBundleSignature ???? CFBundleURLTypes @@ -1241,7 +1241,7 @@ CFBundleVersion - 300 + 301 LSApplicationCategoryType public.app-category.productivity LSMinimumSystemVersion diff --git a/VimR/VimR/Logger.swift b/VimR/VimR/Logger.swift index 09ce61c0..1bc65ffe 100644 --- a/VimR/VimR/Logger.swift +++ b/VimR/VimR/Logger.swift @@ -80,7 +80,7 @@ class Logger { default: self.name = String(describing: name) } - self.logDateFormatter.dateFormat = "dd HH:mm:SSS" + self.logDateFormatter.dateFormat = "dd HH:mm:ss.SSS" self.appender = appender } diff --git a/VimR/VimR/MainWindow+Actions.swift b/VimR/VimR/MainWindow+Actions.swift index 6e2909a7..8a7c68b8 100644 --- a/VimR/VimR/MainWindow+Actions.swift +++ b/VimR/VimR/MainWindow+Actions.swift @@ -5,6 +5,95 @@ import Cocoa import RxSwift +import MessagePack + +// MARK: - RpcEvent Actions +extension MainWindow { + + func rpcEventAction(params rawParams: [MessagePackValue]) { + guard rawParams.count > 0 else { return } + + guard let strEvent = rawParams[0].stringValue, + let event = RpcEvent(rawValue: "\(RpcEvent.prefix).\(strEvent)") + else { + return + } + let params = Array(rawParams.suffix(from: 1)) + + stdoutLog.debug("\(event): \(params)") + + switch event { + case .makeSessionTemporary: + self.emit(self.uuidAction(for: .makeSessionTemporary)) + + case .maximizeWindow: + guard let screen = self.window.screen else { return } + self.window.setFrame(screen.frame, display: true) + + case .toggleTools: + if params.count == 0 { return } + + let param = params[0].integerValue + + if params.isEmpty || param == 0 { + self.toggleAllTools(self) + } else if param == -1 { + self.hideAllTools() + } else if param == 1 { + self.showAllTools() + } + + case .toggleToolButtons: + if params.count == 0 { return } + + let param = params[0].integerValue + + if params.isEmpty || param == 0 { + self.toggleToolButtons(self) + } else if param == -1 { + self.hideToolButtons() + } else if param == 1 { + self.showToolButtons() + } + + case .toggleFullScreen: + self.window.toggleFullScreen(self) + + } + } + + private func hideToolButtons() { + self.workspace.hideToolButtons() + self.focusNvimView(self) + self.emit(self.uuidAction( + for: .toggleToolButtons(self.workspace.isToolButtonsVisible) + )) + } + + private func showToolButtons() { + self.workspace.showToolButtons() + self.focusNvimView(self) + self.emit(self.uuidAction( + for: .toggleToolButtons(self.workspace.isToolButtonsVisible) + )) + } + + private func hideAllTools() { + self.workspace.hideAllTools() + self.focusNvimView(self) + self.emit(self.uuidAction( + for: .toggleAllTools(self.workspace.isAllToolsVisible) + )) + } + + private func showAllTools() { + self.workspace.showAllTools() + self.focusNvimView(self) + self.emit(self.uuidAction( + for: .toggleAllTools(self.workspace.isAllToolsVisible) + )) + } +} // MARK: - File Menu Item Actions extension MainWindow { @@ -123,26 +212,43 @@ extension MainWindow { } @IBAction func toggleFileBrowser(_ sender: Any?) { - let fileBrowser = self.fileBrowserContainer + guard let fileBrowser = self.fileBrowserContainer else { return } + self.toggle(tool: fileBrowser, toolType: .fileBrowser) + } - if fileBrowser?.isSelected == true { - if fileBrowser?.view.isFirstResponder == true { - fileBrowser?.toggle() + @IBAction func toggleBufferList(_ sender: Any?) { + guard let bufferList = self.buffersListContainer else { return } + self.toggle(tool: bufferList, toolType: .bufferList) + } + + @IBAction func toggleMarkdownPreview(_ sender: Any?) { + guard let markdownPreview = self.previewContainer else { return } + self.toggle(tool: markdownPreview, toolType: .markdownPreview) + } + + @IBAction func toggleHtmlPreview(_ sender: Any?) { + guard let htmlPreview = self.htmlPreviewContainer else { return } + self.toggle(tool: htmlPreview, toolType: .htmlPreview) + } + + @IBAction func focusNvimView(_: Any?) { + self.emit(self.uuidAction(for: .focus(.neoVimView))) + } + + private func toggle(tool: WorkspaceTool, toolType: FocusableView) { + if tool.isSelected == true { + if tool.view.isFirstResponder == true { + tool.toggle() self.focusNvimView(self) } else { - self.emit(self.uuidAction(for: .focus(.fileBrowser))) + self.emit(self.uuidAction(for: .focus(toolType))) } return } - fileBrowser?.toggle() - self.emit(self.uuidAction(for: .focus(.fileBrowser))) - } - - @IBAction func focusNvimView(_: Any?) { -// self.window.makeFirstResponder(self.neoVimView) - self.emit(self.uuidAction(for: .focus(.neoVimView))) + tool.toggle() + self.emit(self.uuidAction(for: .focus(toolType))) } } diff --git a/VimR/VimR/MainWindow.swift b/VimR/VimR/MainWindow.swift index 365c4e39..9aff18b7 100644 --- a/VimR/VimR/MainWindow.swift +++ b/VimR/VimR/MainWindow.swift @@ -40,6 +40,8 @@ class MainWindow: NSObject, case setState(for: Tools, with: WorkspaceTool) case setToolsState([(Tools, WorkspaceTool)]) + case makeSessionTemporary + case setTheme(Theme) case close @@ -49,12 +51,21 @@ class MainWindow: NSObject, case neoVimView case fileBrowser - case preview + case bufferList + case markdownPreview + case htmlPreview } enum Tools: String, Codable { - static let all = Set([Tools.fileBrowser, Tools.buffersList, Tools.preview, Tools.htmlPreview]) + static let all = Set( + [ + Tools.fileBrowser, + Tools.buffersList, + Tools.preview, + Tools.htmlPreview + ] + ) case fileBrowser = "com.qvacua.vimr.tools.file-browser" case buffersList = "com.qvacua.vimr.tools.opened-files-list" @@ -71,7 +82,7 @@ class MainWindow: NSObject, case verticalSplit } - let uuid: String + let uuid: UUID let emit: (UuidAction) -> Void let windowController: NSWindowController @@ -102,18 +113,33 @@ class MainWindow: NSObject, var isClosing = false let cliPipePath: String? - required init(source: Observable, emitter: ActionEmitter, state: StateType) { + required init( + source: Observable, + emitter: ActionEmitter, + state: StateType + ) { self.emit = emitter.typedEmit() self.uuid = state.uuid self.cliPipePath = state.cliPipePath - self.windowController = NSWindowController(windowNibName: NSNib.Name("MainWindow")) + self.windowController = NSWindowController( + windowNibName: NSNib.Name("MainWindow") + ) - let neoVimViewConfig = NvimView.Config(useInteractiveZsh: state.useInteractiveZsh, - cwd: state.cwd, - nvimArgs: state.nvimArgs, - envDict: state.envDict) + var sourceFileUrls = [URL]() + if let sourceFileUrl = Bundle(for: MainWindow.self) + .url(forResource: "com.qvacua.VimR", withExtension: "vim") { + sourceFileUrls.append(sourceFileUrl) + } + + let neoVimViewConfig = NvimView.Config( + useInteractiveZsh: state.useInteractiveZsh, + cwd: state.cwd, + nvimArgs: state.nvimArgs, + envDict: state.envDict, + sourceFiles: sourceFileUrls + ) self.neoVimView = NvimView(frame: .zero, config: neoVimViewConfig) self.neoVimView.configureForAutoLayout() @@ -122,9 +148,11 @@ class MainWindow: NSObject, var tools: [Tools: WorkspaceTool] = [:] if state.activeTools[.preview] == true { self.preview = PreviewTool(source: source, emitter: emitter, state: state) - let previewConfig = WorkspaceTool.Config(title: "Markdown", - view: self.preview!, - customMenuItems: self.preview!.menuItems) + let previewConfig = WorkspaceTool.Config( + title: "Markdown", + view: self.preview!, + customMenuItems: self.preview!.menuItems + ) self.previewContainer = WorkspaceTool(previewConfig) self.previewContainer!.dimension = state.tools[.preview]?.dimension ?? 250 tools[.preview] = self.previewContainer @@ -132,30 +160,43 @@ class MainWindow: NSObject, if state.activeTools[.htmlPreview] == true { self.htmlPreview = HtmlPreviewTool(source: source, emitter: emitter, state: state) - let htmlPreviewConfig = WorkspaceTool.Config(title: "HTML", - view: self.htmlPreview!, - customToolbar: self.htmlPreview!.innerCustomToolbar) + let htmlPreviewConfig = WorkspaceTool.Config( + title: "HTML", + view: self.htmlPreview!, + customToolbar: self.htmlPreview!.innerCustomToolbar + ) self.htmlPreviewContainer = WorkspaceTool(htmlPreviewConfig) self.htmlPreviewContainer!.dimension = state.tools[.htmlPreview]?.dimension ?? 250 tools[.htmlPreview] = self.htmlPreviewContainer } if state.activeTools[.fileBrowser] == true { - self.fileBrowser = FileBrowser(source: source, emitter: emitter, state: state) - let fileBrowserConfig = WorkspaceTool.Config(title: "Files", - view: self.fileBrowser!, - customToolbar: self.fileBrowser!.innerCustomToolbar, - customMenuItems: self.fileBrowser!.menuItems) + self.fileBrowser = FileBrowser( + source: source, emitter: emitter, state: state + ) + let fileBrowserConfig = WorkspaceTool.Config( + title: "Files", + view: self.fileBrowser!, + customToolbar: self.fileBrowser!.innerCustomToolbar, + customMenuItems: self.fileBrowser!.menuItems + ) self.fileBrowserContainer = WorkspaceTool(fileBrowserConfig) - self.fileBrowserContainer!.dimension = state.tools[.fileBrowser]?.dimension ?? 200 + self.fileBrowserContainer!.dimension = state + .tools[.fileBrowser]? + .dimension ?? 200 tools[.fileBrowser] = self.fileBrowserContainer } if state.activeTools[.buffersList] == true { - self.buffersList = BuffersList(source: source, emitter: emitter, state: state) - let buffersListConfig = WorkspaceTool.Config(title: "Buffers", view: self.buffersList!) + self.buffersList = BuffersList( + source: source, emitter: emitter, state: state + ) + let buffersListConfig = WorkspaceTool.Config(title: "Buffers", + view: self.buffersList!) self.buffersListContainer = WorkspaceTool(buffersListConfig) - self.buffersListContainer!.dimension = state.tools[.buffersList]?.dimension ?? 200 + self.buffersListContainer!.dimension = state + .tools[.buffersList]? + .dimension ?? 200 tools[.buffersList] = self.buffersListContainer } @@ -181,7 +222,8 @@ class MainWindow: NSObject, return } - self.workspace.append(tool: tool, location: state.tools[toolId]?.location ?? .left) + self.workspace.append(tool: tool, + location: state.tools[toolId]?.location ?? .left) } self.tools.forEach { (toolId, toolContainer) in @@ -203,7 +245,9 @@ class MainWindow: NSObject, self.addViews() - self.neoVimView.trackpadScrollResistance = CGFloat(state.trackpadScrollResistance) + self.neoVimView.trackpadScrollResistance = CGFloat( + state.trackpadScrollResistance + ) self.neoVimView.usesLiveResize = state.useLiveResize self.updateNeoVimAppearance() @@ -229,14 +273,24 @@ class MainWindow: NSObject, case .newCurrentBuffer(let curBuf): self.newCurrentBuffer(curBuf) case .bufferWritten(let buf): self.bufferWritten(buf) case .colorschemeChanged(let theme): self.colorschemeChanged(to: theme) - case .ipcBecameInvalid(let reason): self.ipcBecameInvalid(reason: reason) + + case .ipcBecameInvalid(let reason): + self.ipcBecameInvalid(reason: reason) + case .scroll: self.scroll() case .cursor(let position): self.cursor(to: position) case .initVimError: self.showInitError() + case .apiError(let error, let msg): fileLog.error("Got api error with msg '\(msg)' and error: \(error)") break + case .rpcEvent(let method, let params): + self.rpcEventAction(params: params) + + case .rpcEventSubscribed: + break + } }, onError: { error in // FIXME call onError @@ -251,7 +305,8 @@ class MainWindow: NSObject, return } - if state.viewToBeFocused != nil, case .neoVimView = state.viewToBeFocused! { + if state.viewToBeFocused != nil, + case .neoVimView = state.viewToBeFocused! { self.window.makeFirstResponder(self.neoVimView) } @@ -269,7 +324,9 @@ class MainWindow: NSObject, && state.preview.previewPosition.hasDifferentMark(as: self.previewPosition) { self.previewPosition = state.preview.previewPosition - return self.neoVimView.cursorGo(to: state.preview.previewPosition.payload) + return self.neoVimView.cursorGo( + to: state.preview.previewPosition.payload + ) } return .empty() @@ -303,10 +360,13 @@ class MainWindow: NSObject, } _ = changeTheme( - themePrefChanged: themePrefChanged, themeChanged: themeChanged, usesTheme: usesTheme, + themePrefChanged: themePrefChanged, + themeChanged: themeChanged, + usesTheme: usesTheme, forTheme: { self.themeTitlebar(grow: !self.titlebarThemed) - self.window.backgroundColor = state.appearance.theme.payload.background.brightening(by: 0.9) + self.window.backgroundColor = state.appearance + .theme.payload.background.brightening(by: 0.9) self.set(workspaceThemeWith: state.appearance.theme.payload) self.lastThemeMark = state.appearance.theme.mark @@ -445,8 +505,10 @@ class MainWindow: NSObject, case .default: return self.neoVimView.open(urls: [url]) case .currentTab: return self.neoVimView.openInCurrentTab(url: url) case .newTab: return self.neoVimView.openInNewTab(urls: [url]) - case .horizontalSplit: return self.neoVimView.openInHorizontalSplit(urls: [url]) - case .verticalSplit: return self.neoVimView.openInVerticalSplit(urls: [url]) + case .horizontalSplit: + return self.neoVimView.openInHorizontalSplit(urls: [url]) + case .verticalSplit: + return self.neoVimView.openInVerticalSplit(urls: [url]) } } ) @@ -460,10 +522,11 @@ class MainWindow: NSObject, private func showInitError() { let notification = NSUserNotification() notification.title = "Error during initialization" - notification.informativeText = """ - There was an error during the initialization of NeoVim. - Use :messages to view the error messages. - """ + notification + .informativeText = """ + There was an error during the initialization of NeoVim. + Use :messages to view the error messages. + """ NSUserNotificationCenter.default.deliver(notification) } diff --git a/VimR/VimR/MainWindowReducer.swift b/VimR/VimR/MainWindowReducer.swift index f3c58635..9216638e 100644 --- a/VimR/VimR/MainWindowReducer.swift +++ b/VimR/VimR/MainWindowReducer.swift @@ -78,6 +78,9 @@ class MainWindowReducer: ReducerType { case let .setTheme(theme): state.appearance.theme = Marked(theme) + case .makeSessionTemporary: + state.isTemporarySession = true + default: return tuple diff --git a/VimR/VimR/PreviewMiddleware.swift b/VimR/VimR/PreviewMiddleware.swift index 8eeec5e9..abf4cdad 100644 --- a/VimR/VimR/PreviewMiddleware.swift +++ b/VimR/VimR/PreviewMiddleware.swift @@ -34,7 +34,7 @@ class PreviewMiddleware { self.template = template } - func apply(_ state: MainWindow.State, uuid: String) { + func apply(_ state: MainWindow.State, uuid: UUID) { let preview = state.preview guard let buffer = preview.buffer, let html = preview.html else { guard let previewUrl = self.previewFiles[uuid] else { @@ -80,7 +80,7 @@ class PreviewMiddleware { } private let template: String - private var previewFiles = [String: URL]() + private var previewFiles = [UUID: URL]() } class PreviewToolMiddleware: MiddlewareType { diff --git a/VimR/VimR/PreviewTool.swift b/VimR/VimR/PreviewTool.swift index 0f7c8cbe..291a25b5 100644 --- a/VimR/VimR/PreviewTool.swift +++ b/VimR/VimR/PreviewTool.swift @@ -80,7 +80,8 @@ class PreviewTool: NSView, UiComponent, WKNavigationDelegate { source .observeOn(MainScheduler.instance) .subscribe(onNext: { state in - if state.viewToBeFocused != nil, case .preview = state.viewToBeFocused! { + if state.viewToBeFocused != nil, + case .markdownPreview = state.viewToBeFocused! { self.beFirstResponder() } @@ -143,7 +144,7 @@ class PreviewTool: NSView, UiComponent, WKNavigationDelegate { } private let emit: (UuidAction) -> Void - private let uuid: String + private let uuid: UUID private let webview: WKWebView private let disposeBag = DisposeBag() diff --git a/VimR/VimR/PreviewUtils.swift b/VimR/VimR/PreviewUtils.swift index 7fdb54e5..d742d8bb 100644 --- a/VimR/VimR/PreviewUtils.swift +++ b/VimR/VimR/PreviewUtils.swift @@ -40,7 +40,7 @@ class PreviewUtils { } } - static func state(for uuid: String, + static func state(for uuid: UUID, baseUrl: URL, buffer: NvimView.Buffer?, editorPosition: Marked, @@ -72,11 +72,11 @@ class PreviewUtils { previewPosition: previewPosition) } - private static func serverUrl(for uuid: String, baseUrl: URL, lastComponent: String) -> URL { + private static func serverUrl(for uuid: UUID, baseUrl: URL, lastComponent: String) -> URL { return baseUrl.appendingPathComponent("\(uuid)/\(markdownPath)/\(lastComponent)") } - private static func htmlUrl(with uuid: String) -> URL { + private static func htmlUrl(with uuid: UUID) -> URL { return self.tempDir.appendingPathComponent("\(uuid)-markdown-index.html") } diff --git a/VimR/VimR/RpcEvents.swift b/VimR/VimR/RpcEvents.swift new file mode 100644 index 00000000..020415fd --- /dev/null +++ b/VimR/VimR/RpcEvents.swift @@ -0,0 +1,17 @@ +/** + * Tae Won Ha - http://taewon.de - @hataewon + * See LICENSE + */ + +import Foundation + +enum RpcEvent: String, CaseIterable { + + static let prefix = "com.qvacua.vimr.rpc-events" + + case makeSessionTemporary = "com.qvacua.vimr.rpc-events.make-session-temporary" + case maximizeWindow = "com.qvacua.vimr.rpc-events.maximize-window" + case toggleTools = "com.qvacua.vimr.rpc-events.toggle-tools" + case toggleToolButtons = "com.qvacua.vimr.rpc-events.toggle-tool-buttons" + case toggleFullScreen = "com.qvacua.vimr.rpc-events.toggle-fullscreen" +} diff --git a/VimR/VimR/States.swift b/VimR/VimR/States.swift index 3c5103e7..d82cf053 100644 --- a/VimR/VimR/States.swift +++ b/VimR/VimR/States.swift @@ -38,9 +38,9 @@ struct AppState: Codable { var preferencesOpen = Marked(false) var mainWindowTemplate = MainWindow.State.default - var currentMainWindowUuid: String? + var currentMainWindowUuid: UUID? - var mainWindows: [String: MainWindow.State] = [:] + var mainWindows: [UUID: MainWindow.State] = [:] var openQuickly = OpenQuicklyWindow.State.default @@ -264,8 +264,10 @@ extension MainWindow { var trackpadScrollResistance = 5.0 var useLiveResize = false + var isTemporarySession = false + // neovim - var uuid = UUID().uuidString + var uuid = UUID() var currentBuffer: NvimView.Buffer? var buffers = [NvimView.Buffer]() var cwd = FileUtils.userHomeUrl diff --git a/VimR/VimR/Types.swift b/VimR/VimR/Types.swift index 290e072b..1203a390 100644 --- a/VimR/VimR/Types.swift +++ b/VimR/VimR/Types.swift @@ -15,19 +15,19 @@ struct StateActionPair { protocol UuidTagged { - var uuid: String { get } + var uuid: UUID { get } } class UuidAction: UuidTagged, CustomStringConvertible { - let uuid: String + let uuid: UUID let payload: A var description: String { return "UuidAction(uuid: \(uuid), payload: \(String(reflecting: payload)))" } - init(uuid: String, action: A) { + init(uuid: UUID, action: A) { self.uuid = uuid self.payload = action } @@ -35,14 +35,14 @@ class UuidAction: UuidTagged, CustomStringConvertible { class UuidState: UuidTagged, CustomStringConvertible { - let uuid: String + let uuid: UUID let payload: S var description: String { return "UuidState(uuid: \(uuid), payload: \(String(reflecting: payload)))" } - init(uuid: String, state: S) { + init(uuid: UUID, state: S) { self.uuid = uuid self.payload = state } @@ -127,4 +127,4 @@ class UiComponentTemplate: UiComponent { private let disposeBag = DisposeBag() private let someField: String -} \ No newline at end of file +} diff --git a/VimR/VimR/UiRoot.swift b/VimR/VimR/UiRoot.swift index c80942ab..2f663ff1 100644 --- a/VimR/VimR/UiRoot.swift +++ b/VimR/VimR/UiRoot.swift @@ -79,8 +79,8 @@ class UiRoot: UiComponent { private let openQuicklyWindow: OpenQuicklyWindow private let prefWindow: PrefWindow - private var mainWindows = [String: MainWindow]() - private var subjectForMainWindows = [String: CompletableSubject]() + private var mainWindows = [UUID: MainWindow]() + private var subjectForMainWindows = [UUID: CompletableSubject]() private func newMainWindow(with state: MainWindow.State) -> MainWindow { let subject = self.source.mapOmittingNil { $0.mainWindows[state.uuid] }.completableSubject() @@ -89,7 +89,7 @@ class UiRoot: UiComponent { return MainWindow(source: subject.asObservable(), emitter: self.emitter, state: state) } - private func removeMainWindow(with uuid: String) { + private func removeMainWindow(with uuid: UUID) { self.subjectForMainWindows[uuid]?.onCompleted() self.subjectForMainWindows.removeValue(forKey: uuid) diff --git a/VimR/VimR/UiRootReducer.swift b/VimR/VimR/UiRootReducer.swift index ef353815..e22b6e9d 100644 --- a/VimR/VimR/UiRootReducer.swift +++ b/VimR/VimR/UiRootReducer.swift @@ -38,6 +38,11 @@ class UiRootReducer: ReducerType { case let .becomeKey(isFullScreen): appState.currentMainWindowUuid = uuid + + if appState.mainWindows[uuid]?.isTemporarySession == true { + break + } + appState.mainWindowTemplate = self.mainWindowTemplate( from: appState.mainWindowTemplate, new: appState.mainWindows[uuid] ?? appState.mainWindowTemplate, @@ -45,20 +50,40 @@ class UiRootReducer: ReducerType { ) case let .frameChanged(to:frame): + if appState.mainWindows[uuid]?.isTemporarySession == true { + break + } + if uuid == appState.currentMainWindowUuid { appState.mainWindowTemplate.frame = frame } case let .setToolsState(tools): + if appState.mainWindows[uuid]?.isTemporarySession == true { + break + } + appState.mainWindowTemplate.orderedTools = tools.map { $0.0 } case let .toggleAllTools(value): + if appState.mainWindows[uuid]?.isTemporarySession == true { + break + } + appState.mainWindowTemplate.isAllToolsVisible = value case let .toggleToolButtons(value): + if appState.mainWindows[uuid]?.isTemporarySession == true { + break + } + appState.mainWindowTemplate.isToolButtonsVisible = value case .close: + if appState.mainWindows[uuid]?.isTemporarySession == true { + break + } + if appState.currentMainWindowUuid == uuid, let mainWindowToClose = appState.mainWindows[uuid] { appState.mainWindowTemplate = self.mainWindowTemplate(from: appState.mainWindowTemplate, new: mainWindowToClose, diff --git a/VimR/VimR/Workspace/Workspace.swift b/VimR/VimR/Workspace/Workspace.swift index 6342e6f5..5125be6f 100644 --- a/VimR/VimR/Workspace/Workspace.swift +++ b/VimR/VimR/Workspace/Workspace.swift @@ -127,10 +127,34 @@ class Workspace: NSView, WorkspaceBarDelegate { self.delegate?.moved(tool: tool) } + func hideAllTools() { + if self.isAllToolsVisible { + self.isAllToolsVisible = false + } + } + + func showAllTools() { + if !self.isAllToolsVisible { + self.isAllToolsVisible = true + } + } + func toggleAllTools() { self.isAllToolsVisible = !self.isAllToolsVisible } + func hideToolButtons() { + if self.isToolButtonsVisible { + self.isToolButtonsVisible = false + } + } + + func showToolButtons() { + if !self.isToolButtonsVisible { + self.isToolButtonsVisible = true + } + } + func toggleToolButtons() { self.isToolButtonsVisible = !self.isToolButtonsVisible } diff --git a/VimR/VimR/com.qvacua.VimR.vim b/VimR/VimR/com.qvacua.VimR.vim new file mode 100644 index 00000000..eed681df --- /dev/null +++ b/VimR/VimR/com.qvacua.VimR.vim @@ -0,0 +1,30 @@ +function! s:VimRMakeSessionTemporary() abort + call rpcnotify(0, 'com.qvacua.NvimView', 'make-session-temporary') +endfunction +command! -nargs=0 VimRMakeSessionTemporary call s:VimRMakeSessionTemporary() + +function! s:VimRMaximizeWindow() abort + call rpcnotify(0, 'com.qvacua.NvimView', 'maximize-window') +endfunction +command! -nargs=0 VimRMaximizeWindow call s:VimRMaximizeWindow() + +" -1: hide, 0: toggle, 1: show +function! s:VimRToggleTools(value) abort + call rpcnotify(0, 'com.qvacua.NvimView', 'toggle-tools', a:value) +endfunction +command! -nargs=0 VimRHideTools call s:VimRToggleTools(-1) +command! -nargs=0 VimRToggleTools call s:VimRToggleTools(0) +command! -nargs=0 VimRShowTools call s:VimRToggleTools(1) + +" -1: hide, 0: toggle, 1: show +function! s:VimRToggleToolButtons(value) abort + call rpcnotify(0, 'com.qvacua.NvimView', 'toggle-tool-buttons', a:value) +endfunction +command! -nargs=0 VimRHideToolButtons call s:VimRToggleToolButtons(-1) +command! -nargs=0 VimRToggleToolButtons call s:VimRToggleToolButtons(0) +command! -nargs=0 VimRShowToolButtons call s:VimRToggleToolButtons(1) + +function! s:VimRToggleFullscreen() abort + call rpcnotify(0, 'com.qvacua.NvimView', 'toggle-fullscreen') +endfunction +command! -nargs=0 VimRToggleFullscreen call s:VimRToggleFullscreen() diff --git a/VimR/VimRTests/Info.plist b/VimR/VimRTests/Info.plist index cc27bf47..1764ac9b 100644 --- a/VimR/VimRTests/Info.plist +++ b/VimR/VimRTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - SNAPSHOT-300 + SNAPSHOT-301 CFBundleSignature ???? CFBundleVersion - 300 + 301 diff --git a/resources/release-notes.md b/resources/release-notes.md index 077bf736..448068b0 100644 --- a/resources/release-notes.md +++ b/resources/release-notes.md @@ -1,6 +1,8 @@ # 0.26.0-??? * GH-314: You can customize the key shortcut for all menu items in the *Shortcut* preferences pane. +* GH-501: Add key shortcuts to toggle the Buffer List, Markdown Preview, and HTML Preview tools. +* GH-649: Add commands that can control some of GUI elements. * Draw the disclosure triangle in appropriate color of the current color scheme (and improve handling of changes of `cwd` in the file browser). * ...