1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-12-25 14:52:19 +03:00

Merge remote-tracking branch 'origin/develop' into update-neovim

This commit is contained in:
Tae Won Ha 2019-03-03 21:19:39 +01:00
commit bc45c2be12
42 changed files with 668 additions and 143 deletions

View File

@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>SNAPSHOT-300</string>
<string>SNAPSHOT-301</string>
<key>CFBundleVersion</key>
<string>300</string>
<string>301</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>

View File

@ -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;

View File

@ -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);

View File

@ -16,6 +16,7 @@
#define FileInfo CarbonFileInfo
#define Boolean CarbonBoolean
#import <nvim/main.h>
#import <nvim/vim.h>
#import <nvim/api/vim.h>
#import <nvim/ui.h>
@ -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);

View File

@ -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 = "<group>"; };
4B0A1B38212B332800F1E02F /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = ../Carthage/Build/Mac/Nimble.framework; sourceTree = "<group>"; };
4B17E548209E3E4100265C1D /* RxNeovimApi.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxNeovimApi.framework; path = ../Carthage/Build/Mac/RxNeovimApi.framework; sourceTree = "<group>"; };
4B8662E41FDC3D4F007F490D /* vimr.vim */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = vimr.vim; sourceTree = "<group>"; };
4B8662E41FDC3D4F007F490D /* com.qvacua.NvimView.vim */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.vim; path = com.qvacua.NvimView.vim; sourceTree = "<group>"; };
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 = "<group>"; };
4B90F0101FD2AFAC008A39E0 /* NvimView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NvimView.swift; sourceTree = "<group>"; };
@ -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;

View File

@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>SNAPSHOT-300</string>
<string>SNAPSHOT-301</string>
<key>CFBundleVersion</key>
<string>300</string>
<string>301</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2017 Tae Won Ha. All rights reserved.</string>
<key>NSPrincipalClass</key>

View File

@ -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
}

View File

@ -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 {

View File

@ -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..<emojis.count)
guard let scalar = UnicodeScalar(emojis[idx]) else {
return "😎"
}

View File

@ -119,7 +119,6 @@ extension NvimView {
self.bridgeLogger.hr()
try? self.api
.stop()
.andThen(self.bridge.quit())
.andThen(Completable.create { completable in
self.eventsSubject.onNext(.neoVimStopped)
self.eventsSubject.onCompleted()
@ -127,6 +126,7 @@ extension NvimView {
completable(.completed)
return Disposables.create()
})
.andThen(self.bridge.quit())
.observeOn(MainScheduler.instance)
.wait()
}
@ -141,6 +141,44 @@ extension NvimView {
}
let bufferHandle = array[1]
if event == .vimenter {
Completable
.empty()
.observeOn(SerialDispatchQueueScheduler(qos: .userInitiated))
.andThen(
Completable.create { completable in
self.rpcEventSubscribedCondition.lock()
defer { self.rpcEventSubscribedCondition.unlock() }
while !self.rpcEventSubscribedFlag
&& self.rpcEventSubscribedCondition
.wait(until: Date(timeIntervalSinceNow: 5)) {}
completable(.completed)
return Disposables.create()
}
)
.andThen(
{
let ginitPath = URL(fileURLWithPath: NSHomeDirectory())
.appendingPathComponent(".config/nvim/ginit.vim").path
let loadGinit = FileManager.default.fileExists(atPath: ginitPath)
if loadGinit {
return self.api.command(command: "source \(ginitPath)")
} else {
return .empty()
}
}()
)
.andThen(self.bridge.notifyReadinessForRpcEvents())
.subscribe(onCompleted: {
logger.debug("GUIEnter aucmd fired")
})
.disposed(by: self.disposeBag)
return
}
#if TRACE
self.bridgeLogger.trace("\(event) -> \(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..<endCol]
.first(where: { $0.string.isEmpty }) != nil
.first(where: { $0.string.isEmpty }) != nil
let newRowContainsWideChar = chunk.first(where: { $0.isEmpty }) != nil
if !oldRowContainsWideChar && !newRowContainsWideChar {
@ -364,6 +402,15 @@ extension NvimView {
self.eventsSubject.onNext(.setDirtyStatus(dirty))
}
final func rpcEventSubscribed() {
self.rpcEventSubscribedCondition.lock()
defer { self.rpcEventSubscribedCondition.unlock() }
self.rpcEventSubscribedFlag = true
self.rpcEventSubscribedCondition.broadcast()
self.eventsSubject.onNext(.rpcEventSubscribed)
}
final func setAttr(with value: MessagePackValue) {
guard let array = value.arrayValue else { return }
guard array.count == 6 else { return }
@ -377,7 +424,7 @@ extension NvimView {
else {
self.bridgeLogger.error("Could not get highlight attributes from " +
"\(value)")
"\(value)")
return
}
let trait = FontTrait(rawValue: UInt(rawTrait))

View File

@ -6,10 +6,11 @@
import Cocoa
import RxNeovimApi
import RxSwift
import MessagePack
public class NvimView: NSView,
NSUserInterfaceValidations,
NSTextInputClient {
NSUserInterfaceValidations,
NSTextInputClient {
// MARK: - Public
public struct Config {
@ -18,16 +19,20 @@ public class NvimView: NSView,
var cwd: URL
var nvimArgs: [String]?
var envDict: [String: String]?
var sourceFiles: [URL]
public init(useInteractiveZsh: Bool,
cwd: URL,
nvimArgs: [String]?,
envDict: [String: String]?) {
public init(
useInteractiveZsh: Bool,
cwd: URL,
nvimArgs: [String]?,
envDict: [String: String]?,
sourceFiles: [URL]
) {
self.useInteractiveZsh = useInteractiveZsh
self.cwd = cwd
self.nvimArgs = nvimArgs
self.envDict = envDict
self.sourceFiles = sourceFiles
}
}
@ -50,6 +55,9 @@ public class NvimView: NSView,
case scroll
case cursor(Position)
case rpcEvent(String, [MessagePack.MessagePackValue])
case rpcEventSubscribed
case initVimError
// FIXME: maybe do onError()?
@ -95,12 +103,14 @@ public class NvimView: NSView,
public var description: String {
return "NVV.Theme<" +
"fg: \(self.foreground.hex), bg: \(self.background.hex), " +
"visual-fg: \(self.visualForeground.hex), visual-bg: \(self.visualBackground.hex)" +
">"
"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
}

View File

@ -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

View File

@ -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(<args>)
"
"function! s:VimRMaximizeWindow() abort
" call rpcnotify(0, 'com.qvacua.vimr.rpc-events.maximize-window')
"endfunction
"command! -nargs=0 VimRMaximizeWindow call s:VimRMaximizeWindow(<args>)
"
"" -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(<args>)
"
"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"

View File

@ -1,3 +0,0 @@
set termguicolors
set mouse=a
set title

View File

@ -15,8 +15,8 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>SNAPSHOT-300</string>
<string>SNAPSHOT-301</string>
<key>CFBundleVersion</key>
<string>300</string>
<string>301</string>
</dict>
</plist>

View File

@ -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,

@ -1 +1 @@
Subproject commit 9f731c05656fad98cc16bbbef7d1626b33695efc
Subproject commit b212d045162eb07faee944220e96d27618c296e7

View File

@ -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 = "<group>"; };
1929BBC84557C8351EC6183E /* FileItemIgnorePatternTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileItemIgnorePatternTest.swift; sourceTree = "<group>"; };
1929BBE0A534F2F6009D31BE /* AdvencedPref.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvencedPref.swift; sourceTree = "<group>"; };
1929BBF0944940845485A512 /* RpcEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RpcEvents.swift; sourceTree = "<group>"; };
1929BC2F05E9A5C0DB039739 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
1929BC6D45B7E14D4D75D4E6 /* com.qvacua.VimR.vim */ = {isa = PBXFileReference; lastKnownFileType = file.vim; path = com.qvacua.VimR.vim; sourceTree = "<group>"; };
1929BCE3E156C06EDF1F2806 /* FileOutlineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileOutlineView.swift; sourceTree = "<group>"; };
1929BD2CA8DD198A6BCDBCB7 /* ThemedTableSubviews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemedTableSubviews.swift; sourceTree = "<group>"; };
1929BD4149D5A25C82064DD8 /* UiRoot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UiRoot.swift; sourceTree = "<group>"; };
@ -817,6 +822,7 @@
4BEBA50F1CFF374B00673FDF /* Info.plist */,
4B37ADB81D6E471B00970D55 /* vimr */,
4B3AC8931DB031C600AC5823 /* sparkle_pub.pem */,
1929BC6D45B7E14D4D75D4E6 /* com.qvacua.VimR.vim */,
);
name = resources;
sourceTree = "<group>";
@ -878,6 +884,7 @@
4B97E2CF1D33F92200FC0660 /* resources */,
1929BA652D3B88FC071531EC /* UI */,
1929B66A5E2D00EA143AFD86 /* RxRedux.swift */,
1929BBF0944940845485A512 /* RpcEvents.swift */,
);
path = VimR;
sourceTree = "<group>";
@ -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;

View File

@ -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

View File

@ -220,6 +220,21 @@
<action selector="toggleFileBrowser:" target="-1" id="Ggq-4w-iN7"/>
</connections>
</menuItem>
<menuItem title="Toggle Buffer List" keyEquivalent="2" identifier="com.qvacua.vimr.menuitems.tools.toggle-buffer-list" id="OF4-kp-cNF">
<connections>
<action selector="toggleBufferList:" target="-1" id="bpT-lI-lYu"/>
</connections>
</menuItem>
<menuItem title="Toggle Markdown Preview" keyEquivalent="3" identifier="com.qvacua.vimr.menuitems.tools.toggle-markdown-preview" id="rUg-f6-aPa">
<connections>
<action selector="toggleMarkdownPreview:" target="-1" id="buC-mL-NpJ"/>
</connections>
</menuItem>
<menuItem title="Toggle HTML Preview" keyEquivalent="4" identifier="com.qvacua.vimr.menuitems.tools.toggle-html-preview" id="RB0-mE-Gdd">
<connections>
<action selector="toggleHtmlPreview:" target="-1" id="KTN-ib-Iv6"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="XHW-5e-Vad"/>
<menuItem title="Focus Neovim View" keyEquivalent="." identifier="com.qvacua.vimr.menuitems.tools.focus-neovim-view" id="TtL-Gg-pCj">
<connections>
@ -264,7 +279,8 @@
<action selector="debug1:" target="-1" id="OSW-j0-HVo"/>
</connections>
</menuItem>
<menuItem title="Debug 2" keyEquivalent="2" id="tWe-ll-a9P">
<menuItem title="Debug 2" id="tWe-ll-a9P">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="debug2:" target="-1" id="r0m-FW-POU"/>
</connections>

View File

@ -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<Action>) -> Void
private let disposeBag = DisposeBag()
private let uuid: String
private let uuid: UUID
private var usesTheme: Bool
private var showsFileIcon: Bool

View File

@ -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: "|",

View File

@ -67,7 +67,7 @@ class FileBrowser: NSView,
private let emit: (UuidAction<Action>) -> Void
private let disposeBag = DisposeBag()
private let uuid: String
private let uuid: UUID
private var currentBufferUrl: URL?

View File

@ -130,7 +130,7 @@ class FileOutlineView: NSOutlineView,
private let emit: (UuidAction<FileBrowser.Action>) -> Void
private let disposeBag = DisposeBag()
private let uuid: String
private let uuid: UUID
private var root: Node
private var cwd: URL {

View File

@ -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<Action>) -> Void
private let uuid: String
private let uuid: UUID
private var mark = Token()
private var scrollTop = 0

View File

@ -1224,7 +1224,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>SNAPSHOT-300</string>
<string>SNAPSHOT-301</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -1241,7 +1241,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>300</string>
<string>301</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>LSMinimumSystemVersion</key>

View File

@ -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
}

View File

@ -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)))
}
}

View File

@ -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<Action>) -> Void
let windowController: NSWindowController
@ -102,18 +113,33 @@ class MainWindow: NSObject,
var isClosing = false
let cliPipePath: String?
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
required init(
source: Observable<StateType>,
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)
}

View File

@ -78,6 +78,9 @@ class MainWindowReducer: ReducerType {
case let .setTheme(theme):
state.appearance.theme = Marked(theme)
case .makeSessionTemporary:
state.isTemporarySession = true
default:
return tuple

View File

@ -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 {

View File

@ -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<Action>) -> Void
private let uuid: String
private let uuid: UUID
private let webview: WKWebView
private let disposeBag = DisposeBag()

View File

@ -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<Position>,
@ -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")
}

17
VimR/VimR/RpcEvents.swift Normal file
View File

@ -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"
}

View File

@ -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

View File

@ -15,19 +15,19 @@ struct StateActionPair<S, A> {
protocol UuidTagged {
var uuid: String { get }
var uuid: UUID { get }
}
class UuidAction<A>: 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<A>: UuidTagged, CustomStringConvertible {
class UuidState<S>: 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
}
}

View File

@ -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<MainWindow.State>]()
private var mainWindows = [UUID: MainWindow]()
private var subjectForMainWindows = [UUID: CompletableSubject<MainWindow.State>]()
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)

View File

@ -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,

View File

@ -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
}

View File

@ -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(<args>)
function! s:VimRMaximizeWindow() abort
call rpcnotify(0, 'com.qvacua.NvimView', 'maximize-window')
endfunction
command! -nargs=0 VimRMaximizeWindow call s:VimRMaximizeWindow(<args>)
" -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(<args>)

View File

@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>SNAPSHOT-300</string>
<string>SNAPSHOT-301</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>300</string>
<string>301</string>
</dict>
</plist>

View File

@ -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).
* ...