mirror of
https://github.com/qvacua/vimr.git
synced 2024-12-25 06:43:24 +03:00
Merge remote-tracking branch 'origin/pr/1015' into stock-nvim
Conflicts: .gitignore NvimServer~HEAD NvimView/Sources/NvimView/NvimView.swift NvimView/Sources/NvimView/UiBridge.swift NvimView/Support/NvimViewSupport.xcodeproj/project.pbxproj VimR.xcworkspace/xcshareddata/swiftpm/Package.resolved
This commit is contained in:
commit
ffb3423b0f
4
.gitignore
vendored
4
.gitignore
vendored
@ -181,3 +181,7 @@ Temporary Items
|
||||
tags.*
|
||||
|
||||
.vscode/
|
||||
/RxPack/.build
|
||||
/Workspace/.swiftpm
|
||||
.swiftpm
|
||||
/NvimServer/.build
|
||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "NvimServer"]
|
||||
path = NvimServer
|
||||
url = git@github.com:qvacua/neovim.git
|
||||
[submodule "Neovim"]
|
||||
path = Neovim
|
||||
url = git@github.com:georgeharker/vimr-neovim.git
|
||||
|
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 52;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@ -202,7 +202,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
@ -257,7 +257,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
|
1
Neovim
Submodule
1
Neovim
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 3dcf6880ad65d15495dce18211e72a41e46f502c
|
@ -1 +0,0 @@
|
||||
Subproject commit 8ecd4b24d78ebd4f8fc1394f5fc89d65f025b04d
|
3
NvimServer/NvimServer/.gitignore
vendored
Normal file
3
NvimServer/NvimServer/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.deps
|
||||
third-party
|
||||
|
14
NvimServer/NvimServer/Resources/NvimServer.entitlements
Normal file
14
NvimServer/NvimServer/Resources/NvimServer.entitlements
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
7
NvimServer/NvimServer/Resources/buildInfo.json
Normal file
7
NvimServer/NvimServer/Resources/buildInfo.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"deploymentTarget": "10.15",
|
||||
"gettext": {
|
||||
"arm64BottleTag": "arm64_ventura",
|
||||
"x86_64BottleTag": "ventura"
|
||||
}
|
||||
}
|
1
NvimServer/NvimServer/Sources/NvimServerTypes.h
Normal file
1
NvimServer/NvimServer/Sources/NvimServerTypes.h
Normal file
@ -0,0 +1 @@
|
||||
../../NvimServerTypes/Sources/include/NvimServerTypes.h
|
1
NvimServer/NvimServer/Sources/main.c
Symbolic link
1
NvimServer/NvimServer/Sources/main.c
Symbolic link
@ -0,0 +1 @@
|
||||
../../../Neovim/src/nvim/main.c
|
1
NvimServer/NvimServer/bin/.gitignore
vendored
Normal file
1
NvimServer/NvimServer/bin/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.idea
|
86
NvimServer/NvimServer/bin/README.md
Normal file
86
NvimServer/NvimServer/bin/README.md
Normal file
@ -0,0 +1,86 @@
|
||||
## How to develop
|
||||
|
||||
First, clean everything
|
||||
|
||||
```
|
||||
$ ./bin/clean_all.sh
|
||||
```
|
||||
|
||||
Then, build `libnvim` once with dependencies
|
||||
|
||||
```
|
||||
$ target=x86_64 build_deps=true ./bin/build_libnvim.sh
|
||||
```
|
||||
|
||||
After editing the code of neovim or NvimServer, you build NvimServer in Xcode or by executing
|
||||
the following:
|
||||
|
||||
```
|
||||
$ target="${ARCH}" build_deps=false build_dir="${PROJECT_ROOT}/NvimServer/build" \
|
||||
./bin/build_nvimserver.sh
|
||||
```
|
||||
|
||||
where `${ARCH}` is either `arm64` or `x86_64`.
|
||||
We use `${PROJECT_ROOT}/NvimServer/build` as the NvimServer target assumes that location.
|
||||
|
||||
## How to release
|
||||
|
||||
```
|
||||
$ ./bin/build_release.sh
|
||||
```
|
||||
|
||||
The resulting package will be in `${PROJECT_ROOT}/NvimServer/build/NvimServer.tar.bz2`.
|
||||
|
||||
## Individual steps
|
||||
|
||||
In the following the `target` variable can be either `x86_64` or `arm64`.
|
||||
|
||||
### How to build `libintl`
|
||||
|
||||
```
|
||||
$ ./bin/build_deps.sh
|
||||
```
|
||||
|
||||
which will result in
|
||||
|
||||
```
|
||||
${PROJECT_ROOT}
|
||||
NvimServer
|
||||
third-party
|
||||
lib
|
||||
liba
|
||||
libb
|
||||
...
|
||||
include
|
||||
a.h
|
||||
b.h
|
||||
...
|
||||
x86_64
|
||||
lib
|
||||
liba
|
||||
libb
|
||||
include
|
||||
a.h
|
||||
b.h
|
||||
```
|
||||
|
||||
Files, e.g. `lib` and `include`, in `${PROJECT_ROOT}/NvimServer/third-party` are used to build
|
||||
`libnvim` and NvimServer.
|
||||
|
||||
### How to build `libnvim`
|
||||
|
||||
```
|
||||
$ build_deps=true ./bin/build_libnvim.sh
|
||||
```
|
||||
|
||||
When `build_deps` is `true`, then the `build_deps.sh` is executed. The resuling library will be
|
||||
located in `/build/lib/libnvim.a`.
|
||||
|
||||
### how to build NvimServer
|
||||
|
||||
```
|
||||
$ build_dir="${some_dir}" build_libnvim=true build_deps=true ./bin/build_nvimserver.sh
|
||||
```
|
||||
|
||||
The `build_libnvim.sh` script is executed automatically with the given parameters. The resulting
|
||||
binary will be located in `${some_dir}`.
|
56
NvimServer/NvimServer/bin/build_libnvim.sh
Executable file
56
NvimServer/NvimServer/bin/build_libnvim.sh
Executable file
@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
readonly clean=${clean:?"true or false"}
|
||||
|
||||
build_libnvim() {
|
||||
local -r deployment_target=$1
|
||||
|
||||
# Brew's gettext does not get sym-linked to PATH
|
||||
export PATH="/opt/homebrew/opt/gettext/bin:/usr/local/opt/gettext/bin:${PATH}"
|
||||
|
||||
macos_flags="-DCMAKE_OSX_DEPLOYMENT_TARGET=${deployment_target} -DCMAKE_OSX_ARCHITECTURES=arm64\;x86_64"
|
||||
#macos_flags="-DCMAKE_OSX_DEPLOYMENT_TARGET=${deployment_target} -DCMAKE_OSX_ARCHITECTURES=arm64"
|
||||
|
||||
pushd ../Neovim
|
||||
|
||||
# W/o setting MACOSX_DEPLOYMENT_TARGET, the dependencies have min. macOS set to the macOS you're on.
|
||||
make \
|
||||
CMAKE_BUILD_TYPE=Release \
|
||||
SDKROOT="$(xcrun --show-sdk-path)" \
|
||||
MACOSX_DEPLOYMENT_TARGET="${deployment_target}" \
|
||||
CMAKE_EXTRA_FLAGS="" \
|
||||
CMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
||||
DEPS_CMAKE_FLAGS="${macos_flags}" \
|
||||
libnvim nvim
|
||||
|
||||
popd
|
||||
}
|
||||
|
||||
main() {
|
||||
# This script is located in /NvimServer/bin and we have to go to /
|
||||
echo "$(dirname "${BASH_SOURCE[0]}")/../../"
|
||||
pushd "$(dirname "${BASH_SOURCE[0]}")/../../" >/dev/null
|
||||
|
||||
echo "### Building libnvim"
|
||||
local deployment_target
|
||||
deployment_target=$(jq -r .deploymentTarget ./NvimServer/Resources/buildInfo.json)
|
||||
readonly deployment_target
|
||||
|
||||
if [[ "${clean}" == true ]]; then
|
||||
pushd ../Neovim
|
||||
make distclean
|
||||
popd
|
||||
|
||||
./NvimServer/bin/prepare_libintl.sh
|
||||
|
||||
fi
|
||||
|
||||
build_libnvim "${deployment_target}"
|
||||
|
||||
popd >/dev/null
|
||||
echo "### Built libnvim"
|
||||
}
|
||||
|
||||
main
|
||||
|
27
NvimServer/NvimServer/bin/build_nvimserver.sh
Executable file
27
NvimServer/NvimServer/bin/build_nvimserver.sh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
declare -r -x clean=${clean:?"if true, will clean libnvim and nvimserver"}
|
||||
readonly build_libnvim=${build_libnvim:?"true or false"}
|
||||
readonly build_dir=${build_dir:-"./.build"}
|
||||
|
||||
main() {
|
||||
echo "### Building NvimServer"
|
||||
# This script is located in /NvimServer/bin and we have to go to /
|
||||
pushd "$(dirname "${BASH_SOURCE[0]}")/../.." >/dev/null
|
||||
if [[ "${clean}" == true ]]; then
|
||||
rm -rf "${build_dir}"
|
||||
fi
|
||||
|
||||
if [[ "${build_libnvim}" == true ]]; then
|
||||
./NvimServer/bin/build_libnvim.sh
|
||||
fi
|
||||
|
||||
swift build --arch arm64 --arch x86_64 -c release --product NvimServer
|
||||
#swift build --arch arm64 -c release --product NvimServer
|
||||
|
||||
popd >/dev/null
|
||||
echo "### Built NvimServer"
|
||||
}
|
||||
|
||||
main
|
42
NvimServer/NvimServer/bin/build_runtime.sh
Executable file
42
NvimServer/NvimServer/bin/build_runtime.sh
Executable file
@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
readonly nvim_install_path=${nvim_install_path:?"where to install temp nvim"}
|
||||
|
||||
build_runtime() {
|
||||
pushd ../Neovim
|
||||
|
||||
local -r deployment_target=$1
|
||||
|
||||
echo "#### runtime in ${nvim_install_path}"
|
||||
|
||||
echo "### Building nvim to get the complete runtime"
|
||||
make \
|
||||
SDKROOT="$(xcrun --show-sdk-path)" \
|
||||
MACOSX_DEPLOYMENT_TARGET="${deployment_target}" \
|
||||
CMAKE_EXTRA_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=${deployment_target} -DCUSTOM_UI=0 -DCMAKE_INSTALL_PREFIX=${nvim_install_path}" \
|
||||
DEPS_CMAKE_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=${deployment_target}" \
|
||||
CMAKE_BUILD_TYPE="Release" \
|
||||
install
|
||||
|
||||
echo "#### runtime is installed at ${nvim_install_path}/share/nvim/runtime"
|
||||
|
||||
popd
|
||||
}
|
||||
|
||||
main() {
|
||||
# This script is located in /NvimServer/bin and we have to go to /
|
||||
pushd "$(dirname "${BASH_SOURCE[0]}")/../.." >/dev/null
|
||||
|
||||
echo "### Building runtime"
|
||||
local deployment_target
|
||||
deployment_target=$(jq -r .deploymentTarget ./NvimServer/Resources/buildInfo.json)
|
||||
readonly deployment_target
|
||||
|
||||
build_runtime "${deployment_target}"
|
||||
|
||||
popd >/dev/null
|
||||
echo "### Built runtime"
|
||||
}
|
||||
|
||||
main
|
18
NvimServer/NvimServer/bin/clean_all.sh
Executable file
18
NvimServer/NvimServer/bin/clean_all.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
readonly clean_deps=${clean_deps:-true}
|
||||
|
||||
pushd "$(dirname "${BASH_SOURCE[0]}")/../.." >/dev/null
|
||||
pushd ../Neovim
|
||||
|
||||
rm -rf ./build
|
||||
rm -rf ./.deps
|
||||
make distclean
|
||||
|
||||
popd
|
||||
|
||||
if [[ "${clean_deps}" == true ]]; then
|
||||
rm -rf ./NvimServer/build
|
||||
fi
|
||||
popd >/dev/null
|
68
NvimServer/NvimServer/bin/prepare_libintl.sh
Executable file
68
NvimServer/NvimServer/bin/prepare_libintl.sh
Executable file
@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
shopt -s extglob
|
||||
|
||||
main() {
|
||||
# This script is located in /NvimServer/bin and we have to go to /
|
||||
pushd "$(dirname "${BASH_SOURCE[0]}")/../.." >/dev/null
|
||||
|
||||
rm -rf ./NvimServer/third-party
|
||||
mkdir -p NvimServer/third-party
|
||||
|
||||
local arm64_bottle
|
||||
arm64_bottle=$(jq -r .gettext.arm64BottleTag ./NvimServer/Resources/buildInfo.json)
|
||||
readonly arm64_bottle
|
||||
|
||||
local x86_64_bottle
|
||||
x86_64_bottle=$(jq -r .gettext.x86_64BottleTag ./NvimServer/Resources/buildInfo.json)
|
||||
readonly x86_64_bottle
|
||||
|
||||
pushd ./NvimServer/third-party >/dev/null
|
||||
brew fetch --bottle-tag="${arm64_bottle}" gettext
|
||||
brew fetch --bottle-tag="${x86_64_bottle}" gettext
|
||||
brew fetch --bottle-tag="${arm64_bottle}" lua
|
||||
brew fetch --bottle-tag="${x86_64_bottle}" lua
|
||||
|
||||
local version; version=$(brew info gettext --json | jq -r ".[0].versions.stable"); readonly version
|
||||
echo "### gettext version ${version}"
|
||||
|
||||
local lua_version; lua_version=$(brew info lua --json | jq -r ".[0].versions.stable"); readonly version
|
||||
echo "### gettext lua_version ${lua_version}"
|
||||
|
||||
local temp_dir; temp_dir="$(mktemp -d)"; readonly temp_dir
|
||||
echo "${temp_dir}"
|
||||
|
||||
pushd "${temp_dir}" >/dev/null
|
||||
mkdir "${arm64_bottle}"
|
||||
pushd "${arm64_bottle}" >/dev/null
|
||||
tar xf "$(brew --cache)"/**/*--gettext--+([0-9.])${arm64_bottle}*.tar.gz
|
||||
tar xf "$(brew --cache)"/**/*--lua--+([0-9.])${arm64_bottle}*.tar.gz
|
||||
popd >/dev/null
|
||||
|
||||
mkdir "${x86_64_bottle}"
|
||||
pushd "${x86_64_bottle}" >/dev/null
|
||||
tar xf "$(brew --cache)"/**/*--gettext--+([0-9.])${x86_64_bottle}*.tar.gz
|
||||
tar xf "$(brew --cache)"/**/*--lua--+([0-9.])${x86_64_bottle}*.tar.gz
|
||||
popd >/dev/null
|
||||
|
||||
mkdir universal
|
||||
cp -r "${arm64_bottle}/gettext/${version}/include" ./universal/
|
||||
mkdir universal/lib
|
||||
lipo "${arm64_bottle}/gettext/${version}/lib/libintl.a" "${x86_64_bottle}/gettext/${version}/lib/libintl.a" -create -output ./universal/lib/libintl.a
|
||||
|
||||
mkdir universal_lua
|
||||
cp -r "${arm64_bottle}/lua/${lua_version}/include" ./universal_lua/
|
||||
mkdir universal_lua/lib
|
||||
lipo "${arm64_bottle}/lua/${lua_version}/lib/liblua.a" "${x86_64_bottle}/lua/${lua_version}/lib/liblua.a" -create -output ./universal_lua/lib/liblua.a
|
||||
popd >/dev/null
|
||||
|
||||
mv "${temp_dir}/universal" gettext
|
||||
mv "${temp_dir}/universal_lua" lua
|
||||
rm -rf "${temp_dir}"
|
||||
|
||||
popd >/dev/null
|
||||
|
||||
popd >/dev/null
|
||||
}
|
||||
|
||||
main
|
1
NvimServer/NvimServer/neovim
Symbolic link
1
NvimServer/NvimServer/neovim
Symbolic link
@ -0,0 +1 @@
|
||||
../../Neovim
|
76
NvimServer/NvimServerTypes/include/NvimServerTypes.h
Normal file
76
NvimServer/NvimServerTypes/include/NvimServerTypes.h
Normal file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
#ifndef NVIMSERVER_SHARED_TYPES_H
|
||||
#define NVIMSERVER_SHARED_TYPES_H
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#define NSInteger long
|
||||
#define NSUInteger unsigned long
|
||||
|
||||
typedef CF_OPTIONS(NSUInteger, FontTrait) {
|
||||
FontTraitNone = 0,
|
||||
FontTraitItalic = (1 << 0),
|
||||
FontTraitBold = (1 << 1),
|
||||
FontTraitUnderline = (1 << 2),
|
||||
FontTraitUndercurl = (1 << 3)
|
||||
};
|
||||
|
||||
typedef CF_ENUM(NSInteger, RenderDataType) {
|
||||
RenderDataTypeRawLine,
|
||||
RenderDataTypeGoto,
|
||||
RenderDataTypeScroll,
|
||||
};
|
||||
|
||||
typedef CF_ENUM(NSInteger, NvimServerMsgId) {
|
||||
NvimServerMsgIdServerReady = 0,
|
||||
NvimServerMsgIdNvimReady,
|
||||
NvimServerMsgIdResize,
|
||||
NvimServerMsgIdClear,
|
||||
NvimServerMsgIdSetMenu,
|
||||
NvimServerMsgIdBusyStart,
|
||||
NvimServerMsgIdBusyStop,
|
||||
NvimServerMsgIdModeChange,
|
||||
NvimServerMsgIdModeInfoSet,
|
||||
NvimServerMsgIdBell,
|
||||
NvimServerMsgIdVisualBell,
|
||||
NvimServerMsgIdFlush,
|
||||
NvimServerMsgIdHighlightAttrs,
|
||||
NvimServerMsgIdSetTitle,
|
||||
NvimServerMsgIdStop,
|
||||
NvimServerMsgIdOptionSet,
|
||||
NvimServerMsgIdEvent,
|
||||
|
||||
NvimServerMsgIdDirtyStatusChanged,
|
||||
NvimServerMsgIdCwdChanged,
|
||||
NvimServerMsgIdColorSchemeChanged,
|
||||
NvimServerMsgIdDefaultColorsChanged,
|
||||
NvimServerMsgIdAutoCommandEvent,
|
||||
NvimServerMsgIdRpcEventSubscribed,
|
||||
|
||||
NvimServerMsgIdFatalError,
|
||||
|
||||
NvimServerMsgIdDebug1,
|
||||
};
|
||||
|
||||
typedef CF_ENUM(NSInteger, NvimServerFatalErrorCode) {
|
||||
NvimServerFatalErrorCodeLocalPort = 1,
|
||||
NvimServerFatalErrorCodeRemotePort,
|
||||
};
|
||||
|
||||
typedef CF_ENUM(NSInteger, NvimBridgeMsgId) {
|
||||
NvimBridgeMsgIdAgentReady = 0,
|
||||
NvimBridgeMsgIdReadyForRpcEvents,
|
||||
NvimBridgeMsgIdDeleteInput,
|
||||
NvimBridgeMsgIdResize,
|
||||
NvimBridgeMsgIdScroll,
|
||||
|
||||
NvimBridgeMsgIdFocusGained,
|
||||
|
||||
NvimBridgeMsgIdDebug1,
|
||||
};
|
||||
|
||||
#endif // NVIMSERVER_SHARED_TYPES_H
|
6
NvimServer/NvimServerTypes/stub.c
Normal file
6
NvimServer/NvimServerTypes/stub.c
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
// SwiftPM requires at least one c source file, which can be empty.
|
58
NvimServer/Package.swift
Normal file
58
NvimServer/Package.swift
Normal file
@ -0,0 +1,58 @@
|
||||
// swift-tools-version:5.7
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "NvimServer",
|
||||
platforms: [.macOS(.v13)],
|
||||
products: [
|
||||
.library(name: "NvimServerTypes", targets: ["NvimServerTypes"]),
|
||||
],
|
||||
dependencies: [],
|
||||
targets: [
|
||||
.target(name: "NvimServerTypes", dependencies: [], path: "NvimServerTypes"),
|
||||
.executableTarget(
|
||||
name: "NvimServer",
|
||||
dependencies: [],
|
||||
path: "NvimServer/Sources",
|
||||
cSettings: [
|
||||
// Otherwise we get typedef redefinition error due to double definition of Boolean
|
||||
.unsafeFlags(["-fno-modules"]),
|
||||
.define("INCLUDE_GENERATED_DECLARATIONS", to: "1"),
|
||||
// The target folder is the working directory.
|
||||
.headerSearchPath("../../NvimServer/neovim/src"),
|
||||
.headerSearchPath("../../NvimServer/neovim/build/include"),
|
||||
.headerSearchPath("../../NvimServer/neovim/.deps/usr/include"),
|
||||
.headerSearchPath("../../NvimServer/neovim/build/cmake.config"),
|
||||
.headerSearchPath("../../NvimServer/neovim/build/src/nvim/auto/"),
|
||||
.headerSearchPath("../../NvimServer/third-party/gettext/include"),
|
||||
.headerSearchPath("../../NvimServer/third-party/lua/include/lua"),
|
||||
],
|
||||
linkerSettings: [
|
||||
.linkedFramework("CoreServices"),
|
||||
.linkedFramework("CoreFoundation"),
|
||||
.linkedLibrary("util"),
|
||||
.linkedLibrary("m"),
|
||||
.linkedLibrary("dl"),
|
||||
.linkedLibrary("pthread"),
|
||||
.linkedLibrary("iconv"),
|
||||
.unsafeFlags([
|
||||
// These paths seem to depend on where swift build is executed. Xcode does it in the
|
||||
// folder where Package.swift is located.
|
||||
"../neovim/build/lib/libnvim.a",
|
||||
"../neovim/.deps/usr/lib/libmsgpack-c.a",
|
||||
"../neovim/.deps/usr/lib/libluv.a",
|
||||
"../neovim/.deps/usr/lib/liblpeg.a",
|
||||
"../neovim/.deps/usr/lib/libtermkey.a",
|
||||
"../neovim/.deps/usr/lib/libuv.a",
|
||||
"../neovim/.deps/usr/lib/libunibilium.a",
|
||||
"../neovim/.deps/usr/lib/libvterm.a",
|
||||
"../neovim/.deps/usr/lib/libluajit-5.1.a",
|
||||
"../neovim/.deps/usr/lib/libtree-sitter.a",
|
||||
"NvimServer/third-party/gettext/lib/libintl.a",
|
||||
]),
|
||||
]
|
||||
),
|
||||
],
|
||||
cLanguageStandard: .gnu99
|
||||
)
|
4
NvimServer/README.md
Normal file
4
NvimServer/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# NvimServerTypes
|
||||
|
||||
This is a package to provider some types of NvimServer that are used in NvimView.
|
||||
It consists of just one header file. Since SwiftPM requires at least a C file, we included an empty C file.
|
@ -1,130 +1,132 @@
|
||||
// Auto generated for nvim v0.8.2
|
||||
// Auto generated for nvim v0.9.0-dev
|
||||
// See bin/generate_autocmds.py
|
||||
|
||||
enum NvimAutoCommandEvent: Int {
|
||||
enum NvimAutoCommandEvent: String {
|
||||
|
||||
case bufadd = 0
|
||||
case bufdelete = 1
|
||||
case bufenter = 2
|
||||
case buffilepost = 3
|
||||
case buffilepre = 4
|
||||
case bufhidden = 5
|
||||
case bufleave = 6
|
||||
case bufmodifiedset = 7
|
||||
case bufnew = 8
|
||||
case bufnewfile = 9
|
||||
case bufreadcmd = 10
|
||||
case bufreadpost = 11
|
||||
case bufreadpre = 12
|
||||
case bufunload = 13
|
||||
case bufwinenter = 14
|
||||
case bufwinleave = 15
|
||||
case bufwipeout = 16
|
||||
case bufwritecmd = 17
|
||||
case bufwritepost = 18
|
||||
case bufwritepre = 19
|
||||
case chaninfo = 20
|
||||
case chanopen = 21
|
||||
case cmdundefined = 22
|
||||
case cmdwinenter = 23
|
||||
case cmdwinleave = 24
|
||||
case cmdlinechanged = 25
|
||||
case cmdlineenter = 26
|
||||
case cmdlineleave = 27
|
||||
case colorscheme = 28
|
||||
case colorschemepre = 29
|
||||
case completechanged = 30
|
||||
case completedone = 31
|
||||
case completedonepre = 32
|
||||
case cursorhold = 33
|
||||
case cursorholdi = 34
|
||||
case cursormoved = 35
|
||||
case cursormovedi = 36
|
||||
case diagnosticchanged = 37
|
||||
case diffupdated = 38
|
||||
case dirchanged = 39
|
||||
case dirchangedpre = 40
|
||||
case encodingchanged = 41
|
||||
case exitpre = 42
|
||||
case fileappendcmd = 43
|
||||
case fileappendpost = 44
|
||||
case fileappendpre = 45
|
||||
case filechangedro = 46
|
||||
case filechangedshell = 47
|
||||
case filechangedshellpost = 48
|
||||
case filereadcmd = 49
|
||||
case filereadpost = 50
|
||||
case filereadpre = 51
|
||||
case filetype = 52
|
||||
case filewritecmd = 53
|
||||
case filewritepost = 54
|
||||
case filewritepre = 55
|
||||
case filterreadpost = 56
|
||||
case filterreadpre = 57
|
||||
case filterwritepost = 58
|
||||
case filterwritepre = 59
|
||||
case focusgained = 60
|
||||
case focuslost = 61
|
||||
case funcundefined = 62
|
||||
case guienter = 63
|
||||
case guifailed = 64
|
||||
case insertchange = 65
|
||||
case insertcharpre = 66
|
||||
case insertenter = 67
|
||||
case insertleave = 68
|
||||
case insertleavepre = 69
|
||||
case lspattach = 70
|
||||
case lspdetach = 71
|
||||
case menupopup = 72
|
||||
case modechanged = 73
|
||||
case optionset = 74
|
||||
case quickfixcmdpost = 75
|
||||
case quickfixcmdpre = 76
|
||||
case quitpre = 77
|
||||
case recordingenter = 78
|
||||
case recordingleave = 79
|
||||
case remotereply = 80
|
||||
case searchwrapped = 81
|
||||
case sessionloadpost = 82
|
||||
case shellcmdpost = 83
|
||||
case shellfilterpost = 84
|
||||
case signal = 85
|
||||
case sourcecmd = 86
|
||||
case sourcepost = 87
|
||||
case sourcepre = 88
|
||||
case spellfilemissing = 89
|
||||
case stdinreadpost = 90
|
||||
case stdinreadpre = 91
|
||||
case swapexists = 92
|
||||
case syntax = 93
|
||||
case tabclosed = 94
|
||||
case tabenter = 95
|
||||
case tableave = 96
|
||||
case tabnew = 97
|
||||
case tabnewentered = 98
|
||||
case termchanged = 99
|
||||
case termclose = 100
|
||||
case termenter = 101
|
||||
case termleave = 102
|
||||
case termopen = 103
|
||||
case termresponse = 104
|
||||
case textchanged = 105
|
||||
case textchangedi = 106
|
||||
case textchangedp = 107
|
||||
case textyankpost = 108
|
||||
case uienter = 109
|
||||
case uileave = 110
|
||||
case user = 111
|
||||
case vimenter = 112
|
||||
case vimleave = 113
|
||||
case vimleavepre = 114
|
||||
case vimresized = 115
|
||||
case vimresume = 116
|
||||
case vimsuspend = 117
|
||||
case winclosed = 118
|
||||
case winenter = 119
|
||||
case winleave = 120
|
||||
case winnew = 121
|
||||
case winscrolled = 122
|
||||
case bufadd = "bufadd"
|
||||
case bufdelete = "bufdelete"
|
||||
case bufenter = "bufenter"
|
||||
case buffilepost = "buffilepost"
|
||||
case buffilepre = "buffilepre"
|
||||
case bufhidden = "bufhidden"
|
||||
case bufleave = "bufleave"
|
||||
case bufmodifiedset = "bufmodifiedset"
|
||||
case bufnew = "bufnew"
|
||||
case bufnewfile = "bufnewfile"
|
||||
case bufreadcmd = "bufreadcmd"
|
||||
case bufreadpost = "bufreadpost"
|
||||
case bufreadpre = "bufreadpre"
|
||||
case bufunload = "bufunload"
|
||||
case bufwinenter = "bufwinenter"
|
||||
case bufwinleave = "bufwinleave"
|
||||
case bufwipeout = "bufwipeout"
|
||||
case bufwritecmd = "bufwritecmd"
|
||||
case bufwritepost = "bufwritepost"
|
||||
case bufwritepre = "bufwritepre"
|
||||
case chaninfo = "chaninfo"
|
||||
case chanopen = "chanopen"
|
||||
case cmdundefined = "cmdundefined"
|
||||
case cmdwinenter = "cmdwinenter"
|
||||
case cmdwinleave = "cmdwinleave"
|
||||
case cmdlinechanged = "cmdlinechanged"
|
||||
case cmdlineenter = "cmdlineenter"
|
||||
case cmdlineleave = "cmdlineleave"
|
||||
case colorscheme = "colorscheme"
|
||||
case colorschemepre = "colorschemepre"
|
||||
case completechanged = "completechanged"
|
||||
case completedone = "completedone"
|
||||
case completedonepre = "completedonepre"
|
||||
case cursorhold = "cursorhold"
|
||||
case cursorholdi = "cursorholdi"
|
||||
case cursormoved = "cursormoved"
|
||||
case cursormovedi = "cursormovedi"
|
||||
case diagnosticchanged = "diagnosticchanged"
|
||||
case diffupdated = "diffupdated"
|
||||
case dirchanged = "dirchanged"
|
||||
case dirchangedpre = "dirchangedpre"
|
||||
case encodingchanged = "encodingchanged"
|
||||
case exitpre = "exitpre"
|
||||
case fileappendcmd = "fileappendcmd"
|
||||
case fileappendpost = "fileappendpost"
|
||||
case fileappendpre = "fileappendpre"
|
||||
case filechangedro = "filechangedro"
|
||||
case filechangedshell = "filechangedshell"
|
||||
case filechangedshellpost = "filechangedshellpost"
|
||||
case filereadcmd = "filereadcmd"
|
||||
case filereadpost = "filereadpost"
|
||||
case filereadpre = "filereadpre"
|
||||
case filetype = "filetype"
|
||||
case filewritecmd = "filewritecmd"
|
||||
case filewritepost = "filewritepost"
|
||||
case filewritepre = "filewritepre"
|
||||
case filterreadpost = "filterreadpost"
|
||||
case filterreadpre = "filterreadpre"
|
||||
case filterwritepost = "filterwritepost"
|
||||
case filterwritepre = "filterwritepre"
|
||||
case focusgained = "focusgained"
|
||||
case focuslost = "focuslost"
|
||||
case funcundefined = "funcundefined"
|
||||
case guienter = "guienter"
|
||||
case guifailed = "guifailed"
|
||||
case insertchange = "insertchange"
|
||||
case insertcharpre = "insertcharpre"
|
||||
case insertenter = "insertenter"
|
||||
case insertleave = "insertleave"
|
||||
case insertleavepre = "insertleavepre"
|
||||
case lspattach = "lspattach"
|
||||
case lspdetach = "lspdetach"
|
||||
case menupopup = "menupopup"
|
||||
case modechanged = "modechanged"
|
||||
case optionset = "optionset"
|
||||
case quickfixcmdpost = "quickfixcmdpost"
|
||||
case quickfixcmdpre = "quickfixcmdpre"
|
||||
case quitpre = "quitpre"
|
||||
case recordingenter = "recordingenter"
|
||||
case recordingleave = "recordingleave"
|
||||
case remotereply = "remotereply"
|
||||
case searchwrapped = "searchwrapped"
|
||||
case sessionloadpost = "sessionloadpost"
|
||||
case shellcmdpost = "shellcmdpost"
|
||||
case shellfilterpost = "shellfilterpost"
|
||||
case signal = "signal"
|
||||
case sourcecmd = "sourcecmd"
|
||||
case sourcepost = "sourcepost"
|
||||
case sourcepre = "sourcepre"
|
||||
case spellfilemissing = "spellfilemissing"
|
||||
case stdinreadpost = "stdinreadpost"
|
||||
case stdinreadpre = "stdinreadpre"
|
||||
case swapexists = "swapexists"
|
||||
case syntax = "syntax"
|
||||
case tabclosed = "tabclosed"
|
||||
case tabenter = "tabenter"
|
||||
case tableave = "tableave"
|
||||
case tabnew = "tabnew"
|
||||
case tabnewentered = "tabnewentered"
|
||||
case termchanged = "termchanged"
|
||||
case termclose = "termclose"
|
||||
case termenter = "termenter"
|
||||
case termleave = "termleave"
|
||||
case termopen = "termopen"
|
||||
case termresponse = "termresponse"
|
||||
case textchanged = "textchanged"
|
||||
case textchangedi = "textchangedi"
|
||||
case textchangedp = "textchangedp"
|
||||
case textchangedt = "textchangedt"
|
||||
case textyankpost = "textyankpost"
|
||||
case uienter = "uienter"
|
||||
case uileave = "uileave"
|
||||
case user = "user"
|
||||
case vimenter = "vimenter"
|
||||
case vimleave = "vimleave"
|
||||
case vimleavepre = "vimleavepre"
|
||||
case vimresized = "vimresized"
|
||||
case vimresume = "vimresume"
|
||||
case vimsuspend = "vimsuspend"
|
||||
case winclosed = "winclosed"
|
||||
case winenter = "winenter"
|
||||
case winleave = "winleave"
|
||||
case winnew = "winnew"
|
||||
case winresized = "winresized"
|
||||
case winscrolled = "winscrolled"
|
||||
}
|
||||
|
||||
|
@ -1,27 +1,26 @@
|
||||
// Auto generated for nvim v0.8.2
|
||||
// Auto generated for nvim v0.9.0-dev
|
||||
// See bin/generate_cursor_shape.py
|
||||
|
||||
public enum CursorModeShape: UInt {
|
||||
public enum CursorModeShape: String {
|
||||
|
||||
case normal = 0
|
||||
case visual = 1
|
||||
case insert = 2
|
||||
case replace = 3
|
||||
case cmdlineNormal = 4
|
||||
case cmdlineInsert = 5
|
||||
case cmdlineReplace = 6
|
||||
case operatorPending = 7
|
||||
case visualExclusive = 8
|
||||
case onCmdline = 9
|
||||
case onStatusLine = 10
|
||||
case draggingStatusLine = 11
|
||||
case onVerticalSepLine = 12
|
||||
case draggingVerticalSepLine = 13
|
||||
case more = 14
|
||||
case moreLastLine = 15
|
||||
case showingMatchingParen = 16
|
||||
case termFocus = 17
|
||||
case count = 18
|
||||
case normal = "normal"
|
||||
case visual = "visual"
|
||||
case insert = "insert"
|
||||
case replace = "replace"
|
||||
case cmdlineNormal = "cmdlineNormal"
|
||||
case cmdlineInsert = "cmdlineInsert"
|
||||
case cmdlineReplace = "cmdlineReplace"
|
||||
case operatorPending = "operatorPending"
|
||||
case visualExclusive = "visualExclusive"
|
||||
case onCmdline = "onCmdline"
|
||||
case onStatusLine = "onStatusLine"
|
||||
case draggingStatusLine = "draggingStatusLine"
|
||||
case onVerticalSepLine = "onVerticalSepLine"
|
||||
case draggingVerticalSepLine = "draggingVerticalSepLine"
|
||||
case more = "more"
|
||||
case moreLastLine = "moreLastLine"
|
||||
case showingMatchingParen = "showingMatchingParen"
|
||||
case termFocus = "termFocus"
|
||||
case count = "count"
|
||||
}
|
||||
|
||||
|
||||
|
@ -38,7 +38,18 @@ public extension NvimView {
|
||||
}
|
||||
|
||||
func hasDirtyBuffers() -> Single<Bool> {
|
||||
self.api.getDirtyStatus()
|
||||
self.api
|
||||
.execLua(code: """
|
||||
return vim.fn.getbufinfo({"bufmodified": v:true})
|
||||
""", args: [])
|
||||
.map { result -> Bool in
|
||||
guard let info_array = result.arrayValue
|
||||
else {
|
||||
throw RxNeovimApi.Error
|
||||
.exception(message: "Could not convert values into info array.")
|
||||
}
|
||||
return info_array.count > 0
|
||||
}
|
||||
}
|
||||
|
||||
func waitTillNvimExits() {
|
||||
@ -196,7 +207,17 @@ public extension NvimView {
|
||||
|
||||
func vimOutput(of command: String) -> Single<String> {
|
||||
self.api
|
||||
.exec(src: command, output: true)
|
||||
.exec2(src: command, opts:["output": true])
|
||||
.map({
|
||||
retval in
|
||||
guard let output_value = retval["output"] ?? retval["output"],
|
||||
let output = output_value.stringValue
|
||||
else {
|
||||
throw RxNeovimApi.Error
|
||||
.exception(message: "Could not convert values to output.")
|
||||
}
|
||||
return output
|
||||
})
|
||||
.subscribe(on: self.scheduler)
|
||||
}
|
||||
|
||||
@ -209,22 +230,52 @@ public extension NvimView {
|
||||
.subscribe(on: self.scheduler)
|
||||
}
|
||||
|
||||
func didBecomeMain() -> Completable { self.bridge.focusGained(true) }
|
||||
func didBecomeMain() -> Completable {
|
||||
self.focusGained(true)
|
||||
}
|
||||
|
||||
func didResignMain() -> Completable { self.bridge.focusGained(false) }
|
||||
func didResignMain() -> Completable {
|
||||
self.focusGained(false)
|
||||
}
|
||||
|
||||
internal func neoVimBuffer(
|
||||
for buf: RxNeovimApi.Buffer,
|
||||
currentBuffer: RxNeovimApi.Buffer?
|
||||
) -> Single<NvimView.Buffer> {
|
||||
self.api
|
||||
.bufGetInfo(buffer: buf)
|
||||
.map { info -> NvimView.Buffer in
|
||||
.execLua(code: """
|
||||
local function map(tbl, f)
|
||||
local t = {}
|
||||
for k,v in pairs(tbl) do
|
||||
t[k] = f(v)
|
||||
end
|
||||
return t
|
||||
end
|
||||
return map(vim.fn.getbufinfo(...), function(i)
|
||||
i.buftype = vim.api.nvim_get_option_value("buftype",
|
||||
{buf=i.bufnr})
|
||||
return i
|
||||
end)
|
||||
""", args: [MessagePackValue(buf.handle)])
|
||||
.map { result -> NvimView.Buffer in
|
||||
guard let info_array = result.arrayValue,
|
||||
info_array.count == 1,
|
||||
let raw_info = info_array[0].dictionaryValue
|
||||
else {
|
||||
throw RxNeovimApi.Error
|
||||
.exception(message: "Could not convert values into info array.")
|
||||
}
|
||||
let info : [String: MessagePackValue] = Dictionary<String, MessagePackValue>(
|
||||
uniqueKeysWithValues: raw_info.map({
|
||||
(key: MessagePackValue, value: MessagePackValue) in
|
||||
(key.stringValue!, value)
|
||||
}))
|
||||
|
||||
let current = buf == currentBuffer
|
||||
guard let path = info["filename"]?.stringValue,
|
||||
let dirty = info["modified"]?.boolValue,
|
||||
guard let path = info["name"]?.stringValue,
|
||||
let dirty = info["changed"]?.intValue,
|
||||
let buftype = info["buftype"]?.stringValue,
|
||||
let listed = info["buflisted"]?.boolValue
|
||||
let listed = info["listed"]?.intValue
|
||||
else {
|
||||
throw RxNeovimApi.Error
|
||||
.exception(message: "Could not convert values from the dictionary.")
|
||||
@ -236,9 +287,9 @@ public extension NvimView {
|
||||
apiBuffer: buf,
|
||||
url: url,
|
||||
type: buftype,
|
||||
isDirty: dirty,
|
||||
isDirty: dirty != 0,
|
||||
isCurrent: current,
|
||||
isListed: listed
|
||||
isListed: listed != 0
|
||||
)
|
||||
}
|
||||
.subscribe(on: self.scheduler)
|
||||
|
@ -97,11 +97,10 @@ extension NvimView {
|
||||
return
|
||||
}
|
||||
|
||||
guard self.modeInfoList.count > self.mode.rawValue else {
|
||||
guard let modeInfo = modeInfos[self.mode.rawValue] else {
|
||||
self.log.error("Could not get modeInfo for mode index \(self.mode.rawValue)")
|
||||
return
|
||||
}
|
||||
let modeInfo = modeInfoList[Int(mode.rawValue)]
|
||||
|
||||
guard let cursorAttrId = modeInfo.attrId,
|
||||
let cursorShapeAttrs = self.cellAttributesCollection.attributes(
|
||||
|
@ -58,9 +58,9 @@ public extension NvimView {
|
||||
default: return
|
||||
}
|
||||
|
||||
try? self.bridge
|
||||
.deleteCharacters(0, andInputEscapedString: self.vimPlainString(text))
|
||||
.wait()
|
||||
//try? self.api.feedkeys(keys: self.vimPlainString(text), mode:"m", escape_ks: false)
|
||||
// .wait()
|
||||
_ = self.api.input(keys: self.vimPlainString(text), errWhenBlocked: false).syncValue()
|
||||
|
||||
if self.hasMarkedText() { self._unmarkText() }
|
||||
self.keyDownDone = true
|
||||
@ -182,9 +182,9 @@ public extension NvimView {
|
||||
// after delete, cusor should be the location
|
||||
}
|
||||
if replacementRange.length > 0 {
|
||||
try? self.bridge
|
||||
.deleteCharacters(replacementRange.length, andInputEscapedString: "")
|
||||
.wait()
|
||||
let text = String(repeating:"<BS>", count: replacementRange.length)
|
||||
try? self.api.feedkeys(keys: text, mode:"i", escape_ks: false)
|
||||
.wait()
|
||||
}
|
||||
|
||||
// delay to wait async gui update handled.
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import RxNeovim
|
||||
|
||||
public extension NvimView {
|
||||
override func mouseDown(with event: NSEvent) {
|
||||
@ -86,9 +87,26 @@ public extension NvimView {
|
||||
min(Int(abs(deltaCellX)), maxScrollDeltaX),
|
||||
min(Int(abs(deltaCellY)), maxScrollDeltaY)
|
||||
)
|
||||
let (horizSign, vertSign) = (deltaCellX > 0 ? 1 : -1, deltaCellY > 0 ? 1 : -1)
|
||||
self.bridge
|
||||
.scroll(horizontal: horizSign * absDeltaX, vertical: vertSign * absDeltaY, at: cellPosition)
|
||||
var (horizSign, vertSign) = (deltaCellX > 0 ? 1 : -1, deltaCellY > 0 ? 1 : -1)
|
||||
if event.isDirectionInvertedFromDevice {
|
||||
vertSign = -vertSign
|
||||
}
|
||||
self.log.debug("# scroll: \(cellPosition.row + vertSign * absDeltaY) \(cellPosition.column + horizSign * absDeltaX)")
|
||||
|
||||
self.api.winGetCursor(window: RxNeovimApi.Window(0))
|
||||
.map( {
|
||||
guard $0.count == 2
|
||||
else {
|
||||
self.log.error("Error decoding \($0)")
|
||||
return
|
||||
}
|
||||
self.api.winSetCursor(window: RxNeovimApi.Window(0),
|
||||
pos: [$0[0] + vertSign * absDeltaY, $0[1] + horizSign * absDeltaX])
|
||||
.subscribe(onError: { [weak self] error in
|
||||
self?.log.error("Error in \(#function): \(error)")
|
||||
})
|
||||
.disposed(by: self.disposeBag)
|
||||
})
|
||||
.subscribe(onError: { [weak self] error in
|
||||
self?.log.error("Error in \(#function): \(error)")
|
||||
})
|
||||
|
@ -102,6 +102,8 @@ extension NvimView {
|
||||
|
||||
gui.async {
|
||||
self.font = newFont
|
||||
// Cell size likely changed, do a resize.
|
||||
self.resizeNeoVimUi(to: self.frame.size)
|
||||
self.markForRenderWholeView()
|
||||
self.eventsSubject.onNext(.guifontChanged(newFont))
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
import Cocoa
|
||||
import RxSwift
|
||||
import RxNeovim
|
||||
import MessagePack
|
||||
|
||||
extension NvimView {
|
||||
override public func setFrameSize(_ newSize: NSSize) {
|
||||
@ -13,6 +15,9 @@ extension NvimView {
|
||||
if self.isInitialResize {
|
||||
self.isInitialResize = false
|
||||
self.launchNeoVim(self.discreteSize(size: newSize))
|
||||
// FIXME: not clear why this is needed but otherwise
|
||||
// grid is too large
|
||||
self.resizeNeoVimUi(to: newSize)
|
||||
return
|
||||
}
|
||||
|
||||
@ -53,8 +58,7 @@ extension NvimView {
|
||||
self.offset.x = floor((size.width - self.cellSize.width * discreteSize.width.cgf) / 2)
|
||||
self.offset.y = floor((size.height - self.cellSize.height * discreteSize.height.cgf) / 2)
|
||||
|
||||
self.bridge
|
||||
.resize(width: discreteSize.width, height: discreteSize.height)
|
||||
self.api.uiTryResize(width: discreteSize.width, height: discreteSize.height)
|
||||
.subscribe(onError: { [weak self] error in
|
||||
self?.log.error("Error in \(#function): \(error)")
|
||||
})
|
||||
@ -63,28 +67,84 @@ extension NvimView {
|
||||
|
||||
private func launchNeoVim(_ size: Size) {
|
||||
self.log.info("=== Starting neovim...")
|
||||
let sockPath = URL(fileURLWithPath: NSTemporaryDirectory())
|
||||
.appendingPathComponent("vimr_\(self.uuid).sock").path
|
||||
let sockPath = self.bridge.listenAddress
|
||||
|
||||
self.log.info("NVIM_LISTEN_ADDRESS=\(sockPath)")
|
||||
|
||||
self.bridge.runLocalServerAndNvim(width: size.width, height: size.height)
|
||||
|
||||
// FIXME: need to wait for listen to occur
|
||||
Thread.sleep(forTimeInterval: 0.1)
|
||||
|
||||
// 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))
|
||||
try?
|
||||
self.api.run(at: sockPath)
|
||||
.andThen(
|
||||
self.sourceFileUrls.reduce(Completable.empty()) { prev, url in
|
||||
prev
|
||||
.andThen(
|
||||
self.api.exec(src: "source \(url.shellEscapedPath)", output: true)
|
||||
.asCompletable()
|
||||
)
|
||||
}
|
||||
)
|
||||
.andThen(self.api.subscribe(event: NvimView.rpcEventName))
|
||||
.wait()
|
||||
self.api.getApiInfo().map({
|
||||
value in
|
||||
guard let info = value.arrayValue,
|
||||
info.count == 2,
|
||||
let channel = info[0].int32Value
|
||||
else {
|
||||
throw RxNeovimApi.Error
|
||||
.exception(message: "Could not convert values to api info.")
|
||||
}
|
||||
return channel
|
||||
}).flatMapCompletable({
|
||||
// FIXME: make lua
|
||||
self.api.exec2(src: """
|
||||
":augroup vimr
|
||||
":augroup!
|
||||
:autocmd VimEnter * call rpcnotify(\($0), 'autocommand', 'vimenter')
|
||||
:autocmd BufWinEnter * call rpcnotify(\($0), 'autocommand', 'bufwinenter', str2nr(expand('<abuf>')))
|
||||
:autocmd BufWinEnter * call rpcnotify(\($0), 'autocommand', 'bufwinleave', str2nr(expand('<abuf>')))
|
||||
:autocmd TabEnter * call rpcnotify(\($0), 'autocommand', 'tabenter', str2nr(expand('<abuf>')))
|
||||
:autocmd BufWritePost * call rpcnotify(\($0), 'autocommand', 'bufwritepost', str2nr(expand('<abuf>')))
|
||||
:autocmd BufEnter * call rpcnotify(\($0), 'autocommand', 'bufenter', str2nr(expand('<abuf>')))
|
||||
:autocmd DirChanged * call rpcnotify(\($0), 'autocommand', 'dirchanged', expand('<afile>'))
|
||||
:autocmd ColorScheme * call rpcnotify(\($0), 'autocommand', 'colorscheme', \
|
||||
get(nvim_get_hl(0, {'id': hlID('Normal')}), 'fg', -1), \
|
||||
get(nvim_get_hl(0, {'id': hlID('Normal')}), 'bg', -1), \
|
||||
get(nvim_get_hl(0, {'id': hlID('Visual')}), 'fg', -1), \
|
||||
get(nvim_get_hl(0, {'id': hlID('Visual')}), 'bg', -1), \
|
||||
get(nvim_get_hl(0, {'id': hlID('Directory')}), 'fg', -1))
|
||||
:autocmd ExitPre * call rpcnotify(\($0), 'autocommand', 'exitpre')
|
||||
:autocmd BufModifiedSet * call rpcnotify(\($0), 'autocommand', 'bufmodifiedset', \
|
||||
str2nr(expand('<abuf>')), getbufinfo(str2nr(expand('<abuf>')))[0].changed)
|
||||
:let g:gui_vimr = 1
|
||||
":augroup END
|
||||
""", opts: [:]).asCompletable()
|
||||
|
||||
|
||||
.andThen(self.api.uiAttach(width: size.width, height: size.height, options: [
|
||||
"ext_linegrid": true,
|
||||
"ext_multigrid": false,
|
||||
"ext_tabline": MessagePackValue(self.usesCustomTabBar),
|
||||
"rgb": true
|
||||
]))
|
||||
.andThen(
|
||||
self.sourceFileUrls.reduce(Completable.empty()) { prev, url in
|
||||
prev
|
||||
.andThen(
|
||||
self.api.exec2(src: "source \(url.shellEscapedPath)", opts:["output": true])
|
||||
.map({
|
||||
retval in
|
||||
guard let output_value = retval["output"] ?? retval["output"],
|
||||
let output = output_value.stringValue
|
||||
else {
|
||||
throw RxNeovimApi.Error
|
||||
.exception(message: "Could not convert values to output.")
|
||||
}
|
||||
return output
|
||||
})
|
||||
.asCompletable()
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
).wait()
|
||||
}
|
||||
|
||||
private func randomEmoji() -> String {
|
||||
|
@ -24,6 +24,7 @@ public extension NvimView {
|
||||
var usesCustomTabBar: Bool
|
||||
var useInteractiveZsh: Bool
|
||||
var cwd: URL
|
||||
var nvimBinary: String
|
||||
var nvimArgs: [String]?
|
||||
var envDict: [String: String]?
|
||||
var sourceFiles: [URL]
|
||||
@ -32,6 +33,7 @@ public extension NvimView {
|
||||
usesCustomTabBar: Bool,
|
||||
useInteractiveZsh: Bool,
|
||||
cwd: URL,
|
||||
nvimBinary: String,
|
||||
nvimArgs: [String]?,
|
||||
envDict: [String: String]?,
|
||||
sourceFiles: [URL]
|
||||
@ -39,6 +41,7 @@ public extension NvimView {
|
||||
self.usesCustomTabBar = usesCustomTabBar
|
||||
self.useInteractiveZsh = useInteractiveZsh
|
||||
self.cwd = cwd
|
||||
self.nvimBinary = nvimBinary
|
||||
self.nvimArgs = nvimArgs
|
||||
self.envDict = envDict
|
||||
self.sourceFiles = sourceFiles
|
||||
|
@ -28,43 +28,74 @@ extension NvimView {
|
||||
}
|
||||
|
||||
final func resize(_ value: MessagePackValue) {
|
||||
guard let array = MessagePackUtils.array(
|
||||
from: value, ofSize: 2, conversion: { $0.intValue }
|
||||
) else {
|
||||
guard let array = value.arrayValue
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
guard array.count == 3 else {
|
||||
self.bridgeLogger.error("Could not convert; wrong count: \(array)")
|
||||
return
|
||||
}
|
||||
|
||||
self.bridgeLogger.debug(array)
|
||||
guard let grid = array[0].intValue,
|
||||
let width = array[1].intValue,
|
||||
let height = array[2].intValue
|
||||
else{
|
||||
self.bridgeLogger.error("Could not convert; wrong count: \(array)")
|
||||
return
|
||||
}
|
||||
// FIXME: this must happen immediately, or subsequent updates fail
|
||||
self.ugrid.resize(Size(width: width, height: height))
|
||||
gui.async {
|
||||
self.ugrid.resize(Size(width: array[0], height: array[1]))
|
||||
self.markForRenderWholeView()
|
||||
}
|
||||
}
|
||||
|
||||
final func optionSet(_ values: [MessagePackValue]) {
|
||||
var options : [MessagePackValue: MessagePackValue] = [:]
|
||||
for index in 1..<values.count {
|
||||
guard let option_pair = values[index].arrayValue,
|
||||
option_pair.count == 2
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(values)")
|
||||
continue
|
||||
}
|
||||
options[option_pair[0]] = option_pair[1]
|
||||
}
|
||||
|
||||
self.handleRemoteOptions(options)
|
||||
}
|
||||
|
||||
final func clear() {
|
||||
self.bridgeLogger.debug()
|
||||
|
||||
self.ugrid.clear()
|
||||
gui.async {
|
||||
self.ugrid.clear()
|
||||
self.markForRenderWholeView()
|
||||
}
|
||||
}
|
||||
|
||||
final func modeChange(_ value: MessagePackValue) {
|
||||
guard let mode = MessagePackUtils.value(
|
||||
from: value, conversion: { v -> CursorModeShape? in
|
||||
guard let rawValue = v.intValue else { return nil }
|
||||
return CursorModeShape(rawValue: UInt(rawValue))
|
||||
}
|
||||
) else {
|
||||
guard let mainTuple = value.arrayValue,
|
||||
mainTuple.count == 2,
|
||||
let modeName = mainTuple[0].stringValue,
|
||||
let modeIndex = mainTuple[1].uintValue
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
|
||||
guard let modeShape = CursorModeShape(rawValue: modeName),
|
||||
self.modeInfos[modeName] != nil
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
|
||||
gui.async {
|
||||
self.lastMode = self.mode
|
||||
self.mode = mode
|
||||
self.mode = modeShape
|
||||
self.bridgeLogger.debug("\(self.lastMode) -> \(self.mode)")
|
||||
self.handleInputMethodSource()
|
||||
|
||||
@ -78,67 +109,144 @@ extension NvimView {
|
||||
self.bridgeLogger.trace("modeInfoSet: \(value)")
|
||||
if let mainTuple = value.arrayValue,
|
||||
mainTuple.count == 2,
|
||||
let modeInfoList = mainTuple[1].arrayValue?.map(ModeInfo.init(withMsgPackDict:))
|
||||
let modeInfoArray = mainTuple[1].arrayValue?.map({
|
||||
let modeInfo = ModeInfo.init(withMsgPackDict:$0)
|
||||
return (modeInfo.name, modeInfo)
|
||||
})
|
||||
{
|
||||
self.modeInfoList = modeInfoList
|
||||
self.modeInfos = Dictionary(
|
||||
uniqueKeysWithValues: modeInfoArray)
|
||||
}
|
||||
}
|
||||
|
||||
final func flush(_ renderData: [MessagePackValue]) {
|
||||
|
||||
final func renderData(_ renderData: [MessagePackValue]) {
|
||||
self.bridgeLogger.trace("# of render data: \(renderData.count)")
|
||||
|
||||
gui.async { [self] in
|
||||
var (recompute, rowStart) = (false, Int.max)
|
||||
renderData.forEach { value in
|
||||
guard let renderEntry = value.arrayValue else { return }
|
||||
guard renderEntry.count == 2 else { return }
|
||||
guard renderEntry.count >= 2 else { return }
|
||||
|
||||
guard let rawType = renderEntry[0].intValue,
|
||||
let innerArray = renderEntry[1].arrayValue,
|
||||
let type = RenderDataType(rawValue: rawType)
|
||||
guard let rawType = renderEntry[0].stringValue,
|
||||
let innerArray = renderEntry[1].arrayValue
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
|
||||
switch type {
|
||||
case .rawLine:
|
||||
let possibleNewRowStart = self.doRawLine(data: innerArray)
|
||||
rowStart = min(rowStart, possibleNewRowStart)
|
||||
switch rawType {
|
||||
case "mode_change":
|
||||
self.modeChange(renderEntry[1])
|
||||
|
||||
case "grid_line":
|
||||
for index in 1..<renderEntry.count {
|
||||
guard let grid_line = renderEntry[index].arrayValue
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
let possibleNewRowStart = self.doRawLineNu(data: grid_line)
|
||||
rowStart = min(rowStart, possibleNewRowStart)
|
||||
}
|
||||
recompute = true
|
||||
|
||||
case .goto:
|
||||
guard let row = innerArray[0].uint64Value,
|
||||
let col = innerArray[1].uint64Value,
|
||||
let textPositionRow = innerArray[2].uint64Value,
|
||||
let textPositionCol = innerArray[3].uint64Value else { return }
|
||||
case "grid_resize":
|
||||
self.resize(renderEntry[1])
|
||||
recompute = true
|
||||
|
||||
case "hl_attr_define":
|
||||
for index in 1..<renderEntry.count {
|
||||
self.setAttr(with: renderEntry[index])
|
||||
}
|
||||
|
||||
case "default_colors_set":
|
||||
self.defaultColors(with: renderEntry[1])
|
||||
|
||||
case "grid_clear":
|
||||
self.clear()
|
||||
recompute = true
|
||||
|
||||
case "win_viewport":
|
||||
// FIXME: implement
|
||||
self.winViewportUpdate(innerArray)
|
||||
|
||||
case "mouse_on":
|
||||
self.mouseOn()
|
||||
|
||||
case "mouse_off":
|
||||
self.mouseOff()
|
||||
|
||||
case "busy_start":
|
||||
self.busyStart()
|
||||
|
||||
case "busy_stop":
|
||||
self.busyStop()
|
||||
|
||||
case "option_set":
|
||||
self.optionSet(renderEntry)
|
||||
|
||||
case "set_title":
|
||||
self.setTitle(with: innerArray[0])
|
||||
|
||||
case "update_menu":
|
||||
self.updateMenu()
|
||||
|
||||
case "bell":
|
||||
self.bell()
|
||||
|
||||
case "visual_bell":
|
||||
self.visualBell()
|
||||
|
||||
case "set_icon":
|
||||
// FIXME
|
||||
break
|
||||
|
||||
case "grid_cursor_goto":
|
||||
guard let grid = innerArray[0].uintValue,
|
||||
let row = innerArray[1].uintValue,
|
||||
let col = innerArray[2].uintValue
|
||||
else { return }
|
||||
|
||||
if let possibleNewRowStart = self.doGoto(
|
||||
position: Position(row: Int(row), column: Int(col)),
|
||||
textPosition: Position(row: Int(textPositionRow), column: Int(textPositionCol))
|
||||
textPosition: Position(row: Int(row), column: Int(col))
|
||||
) {
|
||||
rowStart = min(rowStart, possibleNewRowStart)
|
||||
recompute = true
|
||||
}
|
||||
|
||||
case .scroll:
|
||||
case "mode_info_set":
|
||||
self.modeInfoSet(renderEntry[1])
|
||||
|
||||
case "grid_scroll":
|
||||
let values = innerArray.compactMap(\.intValue)
|
||||
guard values.count == 6 else {
|
||||
guard values.count == 7 else {
|
||||
self.bridgeLogger.error("Could not convert \(values)")
|
||||
return
|
||||
}
|
||||
|
||||
let possibleNewRowStart = self.doScroll(values)
|
||||
let possibleNewRowStart = self.doScrollNu(values)
|
||||
rowStart = min(possibleNewRowStart, rowStart)
|
||||
recompute = true
|
||||
|
||||
@unknown default:
|
||||
self.log.error("Unknown flush data type")
|
||||
case "flush":
|
||||
// FIXME: buffer up all the prior data
|
||||
//self.markForRenderWholeView()
|
||||
break
|
||||
|
||||
case "tabline_update":
|
||||
self.tablineUpdate(innerArray)
|
||||
|
||||
default:
|
||||
self.log.error("Unknown flush data type \(rawType)")
|
||||
}
|
||||
}
|
||||
|
||||
guard recompute else { return }
|
||||
self.ugrid.recomputeFlatIndices(rowStart: rowStart)
|
||||
if (rowStart < Int.max) {
|
||||
self.ugrid.recomputeFlatIndices(rowStart: rowStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,7 +271,7 @@ extension NvimView {
|
||||
completable(.completed)
|
||||
return Disposables.create()
|
||||
})
|
||||
.andThen(self.bridge.quit())
|
||||
.andThen(self.quit())
|
||||
.subscribe(onCompleted: { [weak self] in
|
||||
self?.bridgeLogger.info("Successfully stopped the bridge.")
|
||||
self?.nvimExitedCondition.broadcast()
|
||||
@ -173,55 +281,89 @@ extension NvimView {
|
||||
.disposed(by: self.disposeBag)
|
||||
}
|
||||
|
||||
final func autoCommandEvent(_ value: MessagePackValue) {
|
||||
guard let array = MessagePackUtils.array(
|
||||
from: value, ofSize: 2, conversion: { $0.intValue }
|
||||
),
|
||||
let event = NvimAutoCommandEvent(rawValue: array[0])
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
final func autoCommandEvent(_ array: [MessagePackValue]) {
|
||||
guard array.count > 0,
|
||||
let aucmd = array[0].stringValue?.lowercased(),
|
||||
let event = NvimAutoCommandEvent(rawValue: aucmd)
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(array)")
|
||||
return
|
||||
}
|
||||
|
||||
self.bridgeLogger.debug("\(event): \(array)")
|
||||
|
||||
if event == .vimenter {
|
||||
Completable
|
||||
.empty()
|
||||
.observe(on: SerialDispatchQueueScheduler(qos: .userInitiated))
|
||||
.andThen(
|
||||
Completable.create { completable in
|
||||
self.rpcEventSubscriptionCondition.wait(for: 5)
|
||||
self.bridgeLogger.debug("RPC events subscription done.")
|
||||
|
||||
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 {
|
||||
self.bridgeLogger.debug("Source'ing ginit.vim")
|
||||
return self.api.command(command: "source \(ginitPath.shellEscapedPath)")
|
||||
} else {
|
||||
return .empty()
|
||||
}
|
||||
}()
|
||||
)
|
||||
//.andThen(self.bridge.notifyReadinessForRpcEvents())
|
||||
.subscribe(onCompleted: { [weak self] in
|
||||
self?.log.debug("Notified the NvimServer to fire GUIEnter")
|
||||
})
|
||||
.disposed(by: self.disposeBag)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if event == .exitpre {
|
||||
self.stop()
|
||||
return
|
||||
}
|
||||
|
||||
self.bridgeLogger.debug("\(event): \(array)")
|
||||
|
||||
let bufferHandle = array[1]
|
||||
|
||||
if event == .vimenter {
|
||||
Completable
|
||||
.empty()
|
||||
.observe(on: SerialDispatchQueueScheduler(qos: .userInitiated))
|
||||
.andThen(
|
||||
Completable.create { completable in
|
||||
self.rpcEventSubscriptionCondition.wait(for: 5)
|
||||
self.bridgeLogger.debug("RPC events subscription done.")
|
||||
|
||||
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 {
|
||||
self.bridgeLogger.debug("Source'ing ginit.vim")
|
||||
return self.api.command(command: "source \(ginitPath.shellEscapedPath)")
|
||||
} else {
|
||||
return .empty()
|
||||
}
|
||||
}()
|
||||
)
|
||||
.andThen(self.bridge.notifyReadinessForRpcEvents())
|
||||
.subscribe(onCompleted: { [weak self] in
|
||||
self?.log.debug("Notified the NvimServer to fire GUIEnter")
|
||||
})
|
||||
.disposed(by: self.disposeBag)
|
||||
|
||||
if event == .dirchanged {
|
||||
guard array.count > 1,
|
||||
array[1].stringValue != nil
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(array)")
|
||||
return
|
||||
}
|
||||
self.cwdChanged(array[1])
|
||||
return
|
||||
}
|
||||
|
||||
if event == .colorscheme {
|
||||
self.colorSchemeChanged(MessagePackValue(Array(array[1..<array.count])))
|
||||
return
|
||||
}
|
||||
|
||||
guard array.count > 1,
|
||||
let bufferHandle = array[1].intValue
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(array)")
|
||||
return
|
||||
}
|
||||
|
||||
if event == .bufmodifiedset {
|
||||
guard array.count > 2
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(array)")
|
||||
return
|
||||
}
|
||||
self.setDirty(with: array[2])
|
||||
}
|
||||
|
||||
if event == .bufwinenter || event == .bufwinleave {
|
||||
self.bufferListChanged()
|
||||
}
|
||||
@ -260,19 +402,37 @@ extension NvimView {
|
||||
})
|
||||
}
|
||||
|
||||
private func doRawLine(data: [MessagePackValue]) -> Int {
|
||||
guard data.count == 7 else {
|
||||
private func doRawLineNu(data: [MessagePackValue]) -> Int {
|
||||
guard data.count == 5 else {
|
||||
self.bridgeLogger.error("Could not convert; wrong count: \(data)")
|
||||
return Int.max
|
||||
}
|
||||
|
||||
guard let row = data[0].intValue,
|
||||
let startCol = data[1].intValue,
|
||||
let endCol = data[2].intValue, // past last index, but can be 0
|
||||
let clearCol = data[3].intValue, // past last index (can be 0?)
|
||||
let clearAttr = data[4].intValue,
|
||||
let chunk = data[5].arrayValue?.compactMap(\.stringValue),
|
||||
let attrIds = data[6].arrayValue?.compactMap(\.intValue)
|
||||
guard let grid = data[0].intValue,
|
||||
let row = data[1].intValue,
|
||||
let startCol = data[2].intValue,
|
||||
let chunk = data[3].arrayValue?.compactMap({
|
||||
arg -> UUpdate? in
|
||||
guard arg != nil,
|
||||
let argArray = arg.arrayValue
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
var string = ""
|
||||
var attrId: Int? = nil
|
||||
var repeats: Int? = nil
|
||||
if (argArray.count > 0 && arg[0] != nil && arg[0]?.stringValue != nil) {
|
||||
string = arg[0]!.stringValue!
|
||||
}
|
||||
if (argArray.count > 1 && arg[1] != nil && arg[1]?.intValue != nil) {
|
||||
attrId = arg[1]!.intValue!
|
||||
}
|
||||
if (argArray.count > 2 && arg[2] != nil && arg[2]?.intValue != nil) {
|
||||
repeats = arg[2]!.intValue!
|
||||
}
|
||||
return UUpdate(string: string, attrId: attrId, repeats: repeats)
|
||||
}),
|
||||
let wrap = data[4].boolValue
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(data)")
|
||||
return Int.max
|
||||
@ -281,24 +441,15 @@ extension NvimView {
|
||||
#if TRACE
|
||||
self.bridgeLogger.debug(
|
||||
"row: \(row), startCol: \(startCol), endCol: \(endCol), " +
|
||||
"clearCol: \(clearCol), clearAttr: \(clearAttr), " +
|
||||
"chunk: \(chunk), attrIds: \(attrIds)"
|
||||
"chunk: \(chunk)"
|
||||
)
|
||||
#endif
|
||||
|
||||
let count = endCol - startCol
|
||||
guard chunk.count == count, attrIds.count == count else {
|
||||
self.bridgeLogger.error("The count of chunks and attrIds do not match.")
|
||||
return Int.max
|
||||
}
|
||||
self.ugrid.update(
|
||||
let count = chunk.count
|
||||
let endCol = self.ugrid.updateNu(
|
||||
row: row,
|
||||
startCol: startCol,
|
||||
endCol: endCol,
|
||||
clearCol: clearCol,
|
||||
clearAttr: clearAttr,
|
||||
chunk: chunk,
|
||||
attrIds: attrIds
|
||||
chunk: chunk
|
||||
)
|
||||
|
||||
if count > 0 {
|
||||
@ -324,12 +475,6 @@ extension NvimView {
|
||||
}
|
||||
}
|
||||
|
||||
if clearCol > endCol {
|
||||
self.markForRender(region: Region(
|
||||
top: row, bottom: row, left: endCol, right: max(endCol, clearCol - 1)
|
||||
))
|
||||
}
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
@ -368,11 +513,11 @@ extension NvimView {
|
||||
return rowStart
|
||||
}
|
||||
|
||||
private func doScroll(_ array: [Int]) -> Int {
|
||||
self.bridgeLogger.trace("[top, bot, left, right, rows, cols] = \(array)")
|
||||
private func doScrollNu(_ array: [Int]) -> Int {
|
||||
self.bridgeLogger.trace("[grid, top, bot, left, right, rows, cols] = \(array)")
|
||||
|
||||
let (top, bottom, left, right, rows, cols)
|
||||
= (array[0], array[1] - 1, array[2], array[3] - 1, array[4], array[5])
|
||||
let (grid, top, bottom, left, right, rows, cols)
|
||||
= (array[0], array[1], array[2] - 1, array[3], array[4] - 1, array[5], array[6])
|
||||
|
||||
let scrollRegion = Region(
|
||||
top: top, bottom: bottom,
|
||||
@ -472,13 +617,13 @@ extension NvimView {
|
||||
}
|
||||
|
||||
final func setDirty(with value: MessagePackValue) {
|
||||
guard let dirty = value.boolValue else {
|
||||
guard let dirty = value.intValue else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
|
||||
self.bridgeLogger.debug(dirty)
|
||||
self.eventsSubject.onNext(.setDirtyStatus(dirty))
|
||||
self.eventsSubject.onNext(.setDirtyStatus(dirty == 1))
|
||||
}
|
||||
|
||||
final func rpcEventSubscribed() {
|
||||
@ -486,6 +631,7 @@ extension NvimView {
|
||||
self.eventsSubject.onNext(.rpcEventSubscribed)
|
||||
}
|
||||
|
||||
// FIXME: convert to subprocess
|
||||
final func bridgeHasFatalError(_ value: MessagePackValue?) {
|
||||
gui.async {
|
||||
let alert = NSAlert()
|
||||
@ -522,60 +668,83 @@ extension NvimView {
|
||||
}
|
||||
}
|
||||
|
||||
final func event(_ value: MessagePackValue) {
|
||||
guard let dict = value.dictionaryValue,
|
||||
let event = dict.keys.first,
|
||||
let args = dict[event]?.arrayValue
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
|
||||
switch event.stringValue {
|
||||
case "tabline_update": self.tablineUpdate(args)
|
||||
case "win_viewport": self.winViewportUpdate(args)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
final func setAttr(with value: MessagePackValue) {
|
||||
guard let array = value.arrayValue else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
guard array.count == 6 else {
|
||||
guard array.count == 4 else {
|
||||
self.bridgeLogger.error("Could not convert; wrong count \(value)")
|
||||
return
|
||||
}
|
||||
|
||||
guard let id = array[0].intValue,
|
||||
let rawTrait = array[1].uint64Value,
|
||||
let fg = array[2].intValue,
|
||||
let bg = array[3].intValue,
|
||||
let sp = array[4].intValue,
|
||||
let reverse = array[5].boolValue
|
||||
let rgb_dict = array[1].dictionaryValue,
|
||||
let cterm_dict = array[2].dictionaryValue,
|
||||
let info = array[3].arrayValue
|
||||
else {
|
||||
self.bridgeLogger.error(
|
||||
"Could not get highlight attributes from " +
|
||||
"\(value)"
|
||||
"\(value)"
|
||||
)
|
||||
return
|
||||
}
|
||||
let trait = FontTrait(rawValue: UInt(rawTrait))
|
||||
|
||||
let mapped_rgb_dict = rgb_dict.map({
|
||||
(key: MessagePackValue, value: MessagePackValue) in
|
||||
(key.stringValue!, value)
|
||||
})
|
||||
let rgb_attr = Dictionary<String, MessagePackValue>(
|
||||
uniqueKeysWithValues: mapped_rgb_dict)
|
||||
let attrs = CellAttributes(
|
||||
fontTrait: trait,
|
||||
foreground: fg,
|
||||
background: bg,
|
||||
special: sp,
|
||||
reverse: reverse
|
||||
withDict: rgb_attr,
|
||||
with: CellAttributes(fontTrait: FontTrait(), foreground: -1, background: -1, special:-1, reverse: false)
|
||||
//self.cellAttributesCollection.defaultAttributes
|
||||
)
|
||||
|
||||
self.bridgeLogger.debug("AttrId: \(id): \(attrs)")
|
||||
|
||||
gui.async {
|
||||
// FIXME: seems to not work well unless not async
|
||||
//gui.async {
|
||||
self.cellAttributesCollection.set(attributes: attrs, for: id)
|
||||
//}
|
||||
}
|
||||
|
||||
final func defaultColors(with value: MessagePackValue) {
|
||||
guard let array = value.arrayValue else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
guard array.count == 5 else {
|
||||
self.bridgeLogger.error("Could not convert; wrong count \(value)")
|
||||
return
|
||||
}
|
||||
|
||||
guard let rgb_fg = array[0].intValue,
|
||||
let rgb_bg = array[1].intValue,
|
||||
let rgb_sp = array[2].intValue,
|
||||
let cterm_fg = array[3].intValue,
|
||||
let cterm_bg = array[4].intValue
|
||||
else {
|
||||
self.bridgeLogger.error(
|
||||
"Could not get default colors from " +
|
||||
"\(value)"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
let attrs = CellAttributes(
|
||||
fontTrait: FontTrait(), foreground: rgb_fg, background: rgb_bg, special: rgb_sp, reverse: false)
|
||||
|
||||
//gui.async {
|
||||
self.cellAttributesCollection.set(
|
||||
attributes: attrs,
|
||||
for: CellAttributesCollection.defaultAttributesId
|
||||
)
|
||||
self.layer?.backgroundColor = ColorUtils.cgColorIgnoringAlpha(
|
||||
attrs.background
|
||||
)
|
||||
//}
|
||||
}
|
||||
|
||||
final func updateMenu() {
|
||||
@ -614,17 +783,17 @@ extension NvimView {
|
||||
}
|
||||
|
||||
final func markForRender(region: Region) {
|
||||
self.bridgeLogger.trace(region)
|
||||
self.bridgeLogger.debug(region)
|
||||
self.setNeedsDisplay(self.rect(for: region))
|
||||
}
|
||||
|
||||
final func markForRender(row: Int, column: Int) {
|
||||
self.bridgeLogger.trace("\(row):\(column)")
|
||||
self.bridgeLogger.debug("\(row):\(column)")
|
||||
self.setNeedsDisplay(self.rect(forRow: row, column: column))
|
||||
}
|
||||
|
||||
final func markForRender(position: Position) {
|
||||
self.bridgeLogger.trace(position)
|
||||
self.bridgeLogger.debug(position)
|
||||
self.setNeedsDisplay(
|
||||
self.rect(forRow: position.row, column: position.column)
|
||||
)
|
||||
@ -649,7 +818,31 @@ extension NvimView {
|
||||
gui.async { self.tabBar?.update(tabRepresentatives: self.tabEntries) }
|
||||
}
|
||||
|
||||
func winViewportUpdate(_: [MessagePackValue]) {}
|
||||
|
||||
func winViewportUpdate(_ value: [MessagePackValue]) {
|
||||
// FIXME
|
||||
/*
|
||||
guard let array = value.arrayValue,
|
||||
array.count == 8
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
guard let grid = array[0].intValue,
|
||||
let top = array[2].intValue,
|
||||
let bot = array[3].intValue,
|
||||
let curline = array[4].intValue,
|
||||
let curcol = array[5].intValue,
|
||||
let linecount = array[6].intValue,
|
||||
let scroll_delta = array[6].intValue
|
||||
else {
|
||||
self.bridgeLogger.error("Could not convert \(value)")
|
||||
return
|
||||
}
|
||||
// [top, bot, left, right, rows, cols]
|
||||
// FIXMEL self.doScroll([])
|
||||
*/
|
||||
}
|
||||
|
||||
private func bufferWritten(_ handle: Int) {
|
||||
self
|
||||
@ -691,6 +884,15 @@ extension NvimView {
|
||||
self.eventsSubject.onNext(.bufferListChanged)
|
||||
self.updateTouchBarCurrentBuffer()
|
||||
}
|
||||
|
||||
func focusGained(_ gained: Bool) -> Completable {
|
||||
return self.api.uiSetFocus(gained: gained)
|
||||
}
|
||||
|
||||
func quit() -> Completable {
|
||||
return self.api.command(command: ":q")
|
||||
.andThen(self.bridge.quit())
|
||||
}
|
||||
}
|
||||
|
||||
extension TISInputSource {
|
||||
|
@ -29,8 +29,6 @@ public protocol NvimViewDelegate: AnyObject {
|
||||
public final class NvimView: NSView, NSUserInterfaceValidations, NSTextInputClient {
|
||||
// MARK: - Public
|
||||
|
||||
public static let rpcEventName = "com.qvacua.NvimView"
|
||||
|
||||
public static let minFontSize = 4.0
|
||||
public static let maxFontSize = 128.0
|
||||
public static let defaultFont = NSFont.userFixedPitchFont(ofSize: 12)!
|
||||
@ -53,8 +51,8 @@ public final class NvimView: NSView, NSUserInterfaceValidations, NSTextInputClie
|
||||
public let uuid = UUID()
|
||||
public let api = RxNeovimApi()
|
||||
|
||||
public internal(set) var mode = CursorModeShape.normal
|
||||
public internal(set) var modeInfoList = [ModeInfo]()
|
||||
public internal(set) var mode: CursorModeShape = .normal
|
||||
public internal(set) var modeInfos = [String : ModeInfo]()
|
||||
|
||||
public internal(set) var theme = Theme.default
|
||||
|
||||
@ -172,9 +170,14 @@ public final class NvimView: NSView, NSUserInterfaceValidations, NSTextInputClie
|
||||
switch msg {
|
||||
case let .notification(method, params):
|
||||
self?.log.debug("NOTIFICATION: \(method): \(params)")
|
||||
|
||||
guard method == NvimView.rpcEventName else { return }
|
||||
self?.eventsSubject.onNext(.rpcEvent(params))
|
||||
|
||||
if (method == "redraw") {
|
||||
self?.renderData(params)
|
||||
} else if (method == "autocommand") {
|
||||
self?.autoCommandEvent(params)
|
||||
} else {
|
||||
self?.log.debug("MSG ERROR: \(msg)")
|
||||
}
|
||||
|
||||
case let .error(_, msg):
|
||||
self?.log.debug("MSG ERROR: \(msg)")
|
||||
@ -220,7 +223,6 @@ public final class NvimView: NSView, NSUserInterfaceValidations, NSTextInputClie
|
||||
.disposed(by: db)
|
||||
}
|
||||
|
||||
self.bridge.consumer = self
|
||||
self.registerForDraggedTypes([NSPasteboard.PasteboardType.fileURL])
|
||||
|
||||
self.wantsLayer = true
|
||||
@ -236,6 +238,7 @@ public final class NvimView: NSView, NSUserInterfaceValidations, NSTextInputClie
|
||||
usesCustomTabBar: true,
|
||||
useInteractiveZsh: false,
|
||||
cwd: URL(fileURLWithPath: NSHomeDirectory()),
|
||||
nvimBinary: "",
|
||||
nvimArgs: nil,
|
||||
envDict: nil,
|
||||
sourceFiles: []
|
||||
|
@ -6,6 +6,18 @@
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
struct UUpdate: Codable {
|
||||
var string: String
|
||||
var attrId: Int?
|
||||
var repeats: Int?
|
||||
|
||||
init(string: String, attrId: Int? = nil, repeats: Int? = nil) {
|
||||
self.string = string
|
||||
self.attrId = attrId
|
||||
self.repeats = repeats
|
||||
}
|
||||
}
|
||||
|
||||
struct UCell: Codable {
|
||||
var string: String
|
||||
var attrId: Int
|
||||
@ -259,6 +271,40 @@ final class UGrid: CustomStringConvertible, Codable {
|
||||
}
|
||||
}
|
||||
|
||||
/// This does not recompute the flat char indices. For performance it's done
|
||||
/// in NvimView.flush()
|
||||
func updateNu(
|
||||
row: Int,
|
||||
startCol: Int,
|
||||
chunk: [UUpdate]
|
||||
) -> Int {
|
||||
// remove marked patch and recover after modified from vim
|
||||
var oldMarkedInfo: MarkedInfo?
|
||||
if row == self.markedInfo?.position.row {
|
||||
oldMarkedInfo = self.popMarkedInfo()
|
||||
}
|
||||
defer {
|
||||
if let oldMarkedInfo = oldMarkedInfo {
|
||||
updateMarkedInfo(newValue: oldMarkedInfo)
|
||||
}
|
||||
}
|
||||
var lastAttrId : Int = 0
|
||||
var column = startCol
|
||||
for cindex in 0..<chunk.count {
|
||||
let reps = chunk[cindex].repeats ?? 1
|
||||
for _ in 0..<reps {
|
||||
self.cells[row][column].string = chunk[cindex].string
|
||||
let attrId = chunk[cindex].attrId
|
||||
if attrId != nil {
|
||||
lastAttrId = attrId!
|
||||
}
|
||||
self.cells[row][column].attrId = lastAttrId
|
||||
column += 1
|
||||
}
|
||||
}
|
||||
return column
|
||||
}
|
||||
|
||||
struct MarkedInfo {
|
||||
var position: Position
|
||||
var markedCell: [UCell]
|
||||
|
@ -6,19 +6,16 @@
|
||||
import Commons
|
||||
import Foundation
|
||||
import MessagePack
|
||||
import NvimServerTypes
|
||||
import os
|
||||
import RxPack
|
||||
import RxSwift
|
||||
|
||||
final class UiBridge {
|
||||
weak var consumer: NvimView?
|
||||
|
||||
init(uuid: UUID, config: NvimView.Config) {
|
||||
self.uuid = uuid
|
||||
|
||||
self.usesCustomTabBar = config.usesCustomTabBar
|
||||
self.usesInteractiveZsh = config.useInteractiveZsh
|
||||
self.nvimBinary = config.nvimBinary
|
||||
self.nvimArgs = config.nvimArgs ?? []
|
||||
self.cwd = config.cwd
|
||||
|
||||
@ -39,285 +36,83 @@ final class UiBridge {
|
||||
queue: self.queue,
|
||||
internalSerialQueueName: String(reflecting: UiBridge.self)
|
||||
)
|
||||
|
||||
self.server.stream
|
||||
.subscribe(onNext: { [weak self] message in
|
||||
self?.handleMessage(msgId: message.msgid, data: message.data)
|
||||
}, onError: { [weak self] error in
|
||||
self?.log.error("There was an error on the local message port server: \(error)")
|
||||
self?.consumer?.ipcBecameInvalid(error)
|
||||
})
|
||||
.disposed(by: self.disposeBag)
|
||||
}
|
||||
|
||||
func runLocalServerAndNvim(width: Int, height: Int) -> Completable {
|
||||
func runLocalServerAndNvim(width: Int, height: Int) {
|
||||
self.initialWidth = width
|
||||
self.initialHeight = height
|
||||
|
||||
return self.server
|
||||
.run(as: self.localServerName)
|
||||
.andThen(Completable.create { completable in
|
||||
self.runLocalServerAndNvimCompletable = completable
|
||||
self.launchNvimUsingLoginShellEnv()
|
||||
|
||||
// This will be completed in .nvimReady branch of handleMessage()
|
||||
return Disposables.create()
|
||||
})
|
||||
.timeout(.seconds(timeout), scheduler: self.scheduler)
|
||||
}
|
||||
|
||||
func deleteCharacters(_ count: Int, andInputEscapedString string: String) -> Completable {
|
||||
guard let strData = string.data(using: .utf8) else { return .empty() }
|
||||
|
||||
var data = Data(capacity: MemoryLayout<Int>.size + strData.count)
|
||||
|
||||
var c = count
|
||||
withUnsafeBytes(of: &c) { data.append(contentsOf: $0) }
|
||||
data.append(strData)
|
||||
|
||||
return self.sendMessage(msgId: .deleteInput, data: data)
|
||||
}
|
||||
|
||||
func resize(width: Int, height: Int) -> Completable {
|
||||
self.sendMessage(msgId: .resize, data: [width, height].data())
|
||||
}
|
||||
|
||||
func notifyReadinessForRpcEvents() -> Completable {
|
||||
self.sendMessage(msgId: .readyForRpcEvents, data: nil)
|
||||
}
|
||||
|
||||
func focusGained(_ gained: Bool) -> Completable {
|
||||
self.sendMessage(msgId: .focusGained, data: [gained].data())
|
||||
}
|
||||
|
||||
func scroll(horizontal: Int, vertical: Int, at position: Position) -> Completable {
|
||||
self.sendMessage(
|
||||
msgId: .scroll,
|
||||
data: [horizontal, vertical, position.row, position.column].data()
|
||||
)
|
||||
self.launchNvimUsingLoginShellEnv()
|
||||
}
|
||||
|
||||
func quit() -> Completable {
|
||||
self.quit {
|
||||
Completable.create { completable in
|
||||
self.nvimServerProc?.waitUntilExit()
|
||||
self.log.info("NvimServer \(self.uuid) exited successfully.")
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
func forceQuit() -> Completable {
|
||||
self.log.fault("Force-exiting NvimServer \(self.uuid).")
|
||||
|
||||
return self.quit {
|
||||
|
||||
return Completable.create { completable in
|
||||
self.forceExitNvimServer()
|
||||
self.log.fault("NvimServer \(self.uuid) was forcefully exited.")
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
func debug() -> Completable { self.sendMessage(msgId: .debug1, data: nil) }
|
||||
|
||||
private func handleMessage(msgId: Int32, data: Data?) {
|
||||
guard let msg = NvimServerMsgId(rawValue: Int(msgId)) else { return }
|
||||
|
||||
switch msg {
|
||||
case .serverReady:
|
||||
self
|
||||
.establishNvimConnection()
|
||||
.subscribe(onError: { [weak self] error in self?.consumer?.ipcBecameInvalid(error) })
|
||||
.disposed(by: self.disposeBag)
|
||||
|
||||
case .nvimReady:
|
||||
self.runLocalServerAndNvimCompletable?(.completed)
|
||||
self.runLocalServerAndNvimCompletable = nil
|
||||
|
||||
let isInitErrorPresent = MessagePackUtils
|
||||
.value(from: data, conversion: { $0.boolValue }) ?? false
|
||||
if isInitErrorPresent { self.consumer?.initVimError() }
|
||||
|
||||
case .resize:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.resize(v)
|
||||
|
||||
case .clear:
|
||||
self.consumer?.clear()
|
||||
|
||||
case .setMenu:
|
||||
self.consumer?.updateMenu()
|
||||
|
||||
case .busyStart:
|
||||
self.consumer?.busyStart()
|
||||
|
||||
case .busyStop:
|
||||
self.consumer?.busyStop()
|
||||
|
||||
case .modeChange:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.modeChange(v)
|
||||
|
||||
case .modeInfoSet:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.modeInfoSet(v)
|
||||
|
||||
case .bell:
|
||||
self.consumer?.bell()
|
||||
|
||||
case .visualBell:
|
||||
self.consumer?.visualBell()
|
||||
|
||||
case .flush:
|
||||
guard let d = data, let v = (try? unpackAll(d)) else { return }
|
||||
self.consumer?.flush(v)
|
||||
|
||||
case .setTitle:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.setTitle(with: v)
|
||||
|
||||
case .stop:
|
||||
self.consumer?.stop()
|
||||
|
||||
case .dirtyStatusChanged:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.setDirty(with: v)
|
||||
|
||||
case .cwdChanged:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.cwdChanged(v)
|
||||
|
||||
case .defaultColorsChanged:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.defaultColorsChanged(v)
|
||||
|
||||
case .colorSchemeChanged:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.colorSchemeChanged(v)
|
||||
|
||||
case .optionSet:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.optionSet(v)
|
||||
|
||||
case .autoCommandEvent:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.autoCommandEvent(v)
|
||||
|
||||
case .event:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.event(v)
|
||||
|
||||
case .debug1:
|
||||
break
|
||||
|
||||
case .highlightAttrs:
|
||||
guard let v = MessagePackUtils.value(from: data) else { return }
|
||||
self.consumer?.setAttr(with: v)
|
||||
|
||||
case .rpcEventSubscribed:
|
||||
self.consumer?.rpcEventSubscribed()
|
||||
|
||||
case .fatalError:
|
||||
self.consumer?.bridgeHasFatalError(MessagePackUtils.value(from: data))
|
||||
|
||||
@unknown default:
|
||||
self.log.error("Unkonwn msg type from NvimServer")
|
||||
}
|
||||
}
|
||||
|
||||
private func closePorts() -> Completable {
|
||||
self.client
|
||||
.stop()
|
||||
.andThen(self.server.stop())
|
||||
}
|
||||
|
||||
private func quit(using body: @escaping () -> Void) -> Completable {
|
||||
self
|
||||
.closePorts()
|
||||
.andThen(Completable.create { completable in
|
||||
body()
|
||||
|
||||
completable(.completed)
|
||||
return Disposables.create()
|
||||
})
|
||||
}
|
||||
|
||||
private func establishNvimConnection() -> Completable {
|
||||
self.client
|
||||
.connect(to: self.remoteServerName)
|
||||
.andThen(
|
||||
self
|
||||
.sendMessage(msgId: .agentReady, data: [self.initialWidth, self.initialHeight].data())
|
||||
)
|
||||
}
|
||||
|
||||
private func sendMessage(msgId: NvimBridgeMsgId, data: Data?) -> Completable {
|
||||
self.client
|
||||
.send(msgid: Int32(msgId.rawValue), data: data, expectsReply: false)
|
||||
.asCompletable()
|
||||
}
|
||||
|
||||
private func forceExitNvimServer() {
|
||||
self.nvimServerProc?.interrupt()
|
||||
self.nvimServerProc?.terminate()
|
||||
}
|
||||
|
||||
private func launchNvimUsingLoginShellEnv() {
|
||||
let listenAddress = URL(fileURLWithPath: NSTemporaryDirectory())
|
||||
.appendingPathComponent("vimr_\(self.uuid).sock")
|
||||
var env = self.envDict
|
||||
env["VIMRUNTIME"] = Bundle.module.url(forResource: "runtime", withExtension: nil)!.path
|
||||
env["NVIM_LISTEN_ADDRESS"] = listenAddress.path
|
||||
env["NVIM_LISTEN_ADDRESS"] = self.listenAddress
|
||||
|
||||
self.log.debug("Socket: \(listenAddress.path)")
|
||||
self.log.debug("Socket: \(self.listenAddress)")
|
||||
|
||||
let usesCustomTabBarArg = self.usesCustomTabBar ? "1" : "0"
|
||||
|
||||
let inPipe = Pipe()
|
||||
let outPipe = Pipe()
|
||||
let errorPipe = Pipe()
|
||||
let process = Process()
|
||||
process.environment = env
|
||||
process.standardInput = inPipe
|
||||
process.standardError = errorPipe
|
||||
process.standardOutput = outPipe
|
||||
process.currentDirectoryPath = self.cwd.path
|
||||
// We know that NvimServer is there.
|
||||
process.launchPath = Bundle.module.url(forResource: "NvimServer", withExtension: nil)!.path
|
||||
|
||||
if (self.nvimBinary != "" &&
|
||||
FileManager.default.fileExists(atPath: self.nvimBinary)) {
|
||||
process.launchPath = self.nvimBinary
|
||||
} else {
|
||||
// We know that NvimServer is there.
|
||||
env["VIMRUNTIME"] = Bundle.module.url(forResource: "runtime", withExtension: nil)!.path
|
||||
let launchPath = Bundle.module.url(forResource: "NvimServer", withExtension: nil)!.path
|
||||
process.launchPath = launchPath
|
||||
}
|
||||
process.environment = env
|
||||
|
||||
process
|
||||
.arguments = ["--gui", self.localServerName, self.remoteServerName, usesCustomTabBarArg] +
|
||||
["--headless"] + self.nvimArgs
|
||||
.arguments =
|
||||
["--embed",
|
||||
"--listen",
|
||||
self.listenAddress] + self.nvimArgs
|
||||
|
||||
self.log.debug(
|
||||
"Launching NvimServer with args: \(String(describing: process.arguments))"
|
||||
"Launching NvimServer \(String(describing: process.launchPath)) with args: \(String(describing: process.arguments))"
|
||||
)
|
||||
process.launch()
|
||||
do {
|
||||
try process.run()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
self.nvimServerProc = process
|
||||
}
|
||||
|
||||
// FIXME: GH-832
|
||||
private func launchNvimUsingLoginShell() {
|
||||
let nvimCmd = [
|
||||
// We know that NvimServer is there.
|
||||
Bundle.module.url(forResource: "NvimServer", withExtension: nil)!.path,
|
||||
"--gui",
|
||||
self.localServerName,
|
||||
self.remoteServerName,
|
||||
self.usesCustomTabBar ? "1" : "0",
|
||||
"--headless",
|
||||
] + self.nvimArgs
|
||||
|
||||
let listenAddress = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent("vimr_\(self.uuid).sock")
|
||||
let nvimEnv = [
|
||||
// We know that runtime is there.
|
||||
"VIMRUNTIME": Bundle.module.url(forResource: "runtime", withExtension: nil)!.path,
|
||||
"NVIM_LISTEN_ADDRESS": listenAddress.path,
|
||||
]
|
||||
|
||||
self.nvimServerProc = ProcessUtils.execProcessViaLoginShell(
|
||||
cmd: nvimCmd.map { "'\($0)'" }.joined(separator: " "),
|
||||
cwd: self.cwd,
|
||||
envs: nvimEnv,
|
||||
interactive: self.interactive(for: ProcessUtils.loginShell()),
|
||||
qos: .userInteractive
|
||||
)
|
||||
}
|
||||
|
||||
private func interactive(for shell: URL) -> Bool {
|
||||
if shell.lastPathComponent == "zsh" { return self.usesInteractiveZsh }
|
||||
return true
|
||||
@ -327,14 +122,11 @@ final class UiBridge {
|
||||
|
||||
private let uuid: UUID
|
||||
|
||||
private let usesCustomTabBar: Bool
|
||||
private let usesInteractiveZsh: Bool
|
||||
private let cwd: URL
|
||||
private let nvimArgs: [String]
|
||||
private let envDict: [String: String]
|
||||
|
||||
private let server = RxMessagePortServer(queueQos: .userInteractive)
|
||||
private let client = RxMessagePortClient(queueQos: .userInteractive)
|
||||
private let nvimBinary: String
|
||||
|
||||
private var nvimServerProc: Process?
|
||||
|
||||
@ -354,6 +146,11 @@ final class UiBridge {
|
||||
|
||||
private var localServerName: String { "com.qvacua.NvimView.\(self.uuid)" }
|
||||
private var remoteServerName: String { "com.qvacua.NvimView.NvimServer.\(self.uuid)" }
|
||||
|
||||
var listenAddress: String {
|
||||
return URL(fileURLWithPath: NSTemporaryDirectory())
|
||||
.appendingPathComponent("vimr_\(self.uuid).sock").path
|
||||
}
|
||||
}
|
||||
|
||||
private let timeout = 5
|
||||
|
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objectVersion = 60;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@ -339,6 +339,7 @@
|
||||
packageReferences = (
|
||||
4BD67C9524ECF4AB00147C51 /* XCRemoteSwiftPackageReference "MessagePack" */,
|
||||
4BD67CCF24ED08CB00147C51 /* XCRemoteSwiftPackageReference "PureLayout" */,
|
||||
1FF017012AF02F64003D62BB /* XCLocalSwiftPackageReference "../../NvimServer" */,
|
||||
);
|
||||
productRefGroup = 4B90F0051FD2AF59008A39E0 /* Products */;
|
||||
projectDirPath = "";
|
||||
@ -754,6 +755,13 @@
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCLocalSwiftPackageReference section */
|
||||
1FF017012AF02F64003D62BB /* XCLocalSwiftPackageReference "../../NvimServer" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = ../../NvimServer;
|
||||
};
|
||||
/* End XCLocalSwiftPackageReference section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
4BD67C9524ECF4AB00147C51 /* XCRemoteSwiftPackageReference "MessagePack" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
|
@ -5,9 +5,18 @@
|
||||
![Screenshot 1](https://raw.githubusercontent.com/qvacua/vimr/develop/resources/screenshot1.png)
|
||||
![Screenshot 2](https://raw.githubusercontent.com/qvacua/vimr/develop/resources/screenshot2.png)
|
||||
|
||||
## Fork differences
|
||||
|
||||
Originially and wonderfully by [qvacua](https://github.com/qvacua/) which appears to be unsupported any more.
|
||||
|
||||
This version can talk to an arbitrary nvim (once the upstream bits get merged)
|
||||
It therefore should be able to keep pace with nvim development better. Work was done to avoid
|
||||
having to modify neovim, and the uibridge mechanism has been replaced with api calls.
|
||||
|
||||
## About
|
||||
|
||||
Project VimR is a Neovim GUI for macOS.
|
||||
|
||||
The goal is to build an editor that uses Neovim inside with many of the convenience
|
||||
GUI features similar to those present in modern editors. We mainly use Swift,
|
||||
but also use C/Objective-C when where appropriate.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Auto generated for nvim version 0.8.2.
|
||||
// Auto generated for nvim version 0.9.2.
|
||||
// See bin/generate_api_methods.py
|
||||
|
||||
import Foundation
|
||||
@ -983,36 +983,6 @@ extension RxNeovimApi {
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func bufGetInfo(
|
||||
buffer: RxNeovimApi.Buffer,
|
||||
errWhenBlocked: Bool = true
|
||||
) -> Single<Dictionary<String, RxNeovimApi.Value>> {
|
||||
|
||||
let params: [RxNeovimApi.Value] = [
|
||||
.int(Int64(buffer.handle)),
|
||||
]
|
||||
|
||||
func transform(_ value: Value) throws -> Dictionary<String, RxNeovimApi.Value> {
|
||||
guard let result = (msgPackDictToSwift(value.dictionaryValue)) else {
|
||||
throw RxNeovimApi.Error.conversion(type: Dictionary<String, RxNeovimApi.Value>.self)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
if errWhenBlocked {
|
||||
return self
|
||||
.checkBlocked(
|
||||
self.rpc(method: "nvim_buf_get_info", params: params, expectsReturnValue: true)
|
||||
)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
return self
|
||||
.rpc(method: "nvim_buf_get_info", params: params, expectsReturnValue: true)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func parseCmd(
|
||||
str: String,
|
||||
opts: Dictionary<String, RxNeovimApi.Value>,
|
||||
@ -1239,6 +1209,36 @@ extension RxNeovimApi {
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func getOptionInfo(
|
||||
name: String,
|
||||
errWhenBlocked: Bool = true
|
||||
) -> Single<Dictionary<String, RxNeovimApi.Value>> {
|
||||
|
||||
let params: [RxNeovimApi.Value] = [
|
||||
.string(name),
|
||||
]
|
||||
|
||||
func transform(_ value: Value) throws -> Dictionary<String, RxNeovimApi.Value> {
|
||||
guard let result = (msgPackDictToSwift(value.dictionaryValue)) else {
|
||||
throw RxNeovimApi.Error.conversion(type: Dictionary<String, RxNeovimApi.Value>.self)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
if errWhenBlocked {
|
||||
return self
|
||||
.checkBlocked(
|
||||
self.rpc(method: "nvim_get_option_info", params: params, expectsReturnValue: true)
|
||||
)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
return self
|
||||
.rpc(method: "nvim_get_option_info", params: params, expectsReturnValue: true)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func createNamespace(
|
||||
name: String,
|
||||
errWhenBlocked: Bool = true
|
||||
@ -1623,13 +1623,15 @@ extension RxNeovimApi {
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func getOptionInfo(
|
||||
public func getOptionInfo2(
|
||||
name: String,
|
||||
opts: Dictionary<String, RxNeovimApi.Value>,
|
||||
errWhenBlocked: Bool = true
|
||||
) -> Single<Dictionary<String, RxNeovimApi.Value>> {
|
||||
|
||||
let params: [RxNeovimApi.Value] = [
|
||||
.string(name),
|
||||
.map(opts.mapToDict({ (Value.string($0), $1) })),
|
||||
]
|
||||
|
||||
func transform(_ value: Value) throws -> Dictionary<String, RxNeovimApi.Value> {
|
||||
@ -1643,13 +1645,13 @@ extension RxNeovimApi {
|
||||
if errWhenBlocked {
|
||||
return self
|
||||
.checkBlocked(
|
||||
self.rpc(method: "nvim_get_option_info", params: params, expectsReturnValue: true)
|
||||
self.rpc(method: "nvim_get_option_info2", params: params, expectsReturnValue: true)
|
||||
)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
return self
|
||||
.rpc(method: "nvim_get_option_info", params: params, expectsReturnValue: true)
|
||||
.rpc(method: "nvim_get_option_info2", params: params, expectsReturnValue: true)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
@ -2051,6 +2053,28 @@ extension RxNeovimApi {
|
||||
.asCompletable()
|
||||
}
|
||||
|
||||
public func uiSetFocus(
|
||||
gained: Bool,
|
||||
expectsReturnValue: Bool = false
|
||||
) -> Completable {
|
||||
|
||||
let params: [RxNeovimApi.Value] = [
|
||||
.bool(gained),
|
||||
]
|
||||
|
||||
if expectsReturnValue {
|
||||
return self
|
||||
.checkBlocked(
|
||||
self.rpc(method: "nvim_ui_set_focus", params: params, expectsReturnValue: expectsReturnValue)
|
||||
)
|
||||
.asCompletable()
|
||||
}
|
||||
|
||||
return self
|
||||
.rpc(method: "nvim_ui_set_focus", params: params, expectsReturnValue: expectsReturnValue)
|
||||
.asCompletable()
|
||||
}
|
||||
|
||||
public func uiDetach(
|
||||
expectsReturnValue: Bool = false
|
||||
) -> Completable {
|
||||
@ -2196,70 +2220,6 @@ extension RxNeovimApi {
|
||||
.asCompletable()
|
||||
}
|
||||
|
||||
public func getHlByName(
|
||||
name: String,
|
||||
rgb: Bool,
|
||||
errWhenBlocked: Bool = true
|
||||
) -> Single<Dictionary<String, RxNeovimApi.Value>> {
|
||||
|
||||
let params: [RxNeovimApi.Value] = [
|
||||
.string(name),
|
||||
.bool(rgb),
|
||||
]
|
||||
|
||||
func transform(_ value: Value) throws -> Dictionary<String, RxNeovimApi.Value> {
|
||||
guard let result = (msgPackDictToSwift(value.dictionaryValue)) else {
|
||||
throw RxNeovimApi.Error.conversion(type: Dictionary<String, RxNeovimApi.Value>.self)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
if errWhenBlocked {
|
||||
return self
|
||||
.checkBlocked(
|
||||
self.rpc(method: "nvim_get_hl_by_name", params: params, expectsReturnValue: true)
|
||||
)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
return self
|
||||
.rpc(method: "nvim_get_hl_by_name", params: params, expectsReturnValue: true)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func getHlById(
|
||||
hl_id: Int,
|
||||
rgb: Bool,
|
||||
errWhenBlocked: Bool = true
|
||||
) -> Single<Dictionary<String, RxNeovimApi.Value>> {
|
||||
|
||||
let params: [RxNeovimApi.Value] = [
|
||||
.int(Int64(hl_id)),
|
||||
.bool(rgb),
|
||||
]
|
||||
|
||||
func transform(_ value: Value) throws -> Dictionary<String, RxNeovimApi.Value> {
|
||||
guard let result = (msgPackDictToSwift(value.dictionaryValue)) else {
|
||||
throw RxNeovimApi.Error.conversion(type: Dictionary<String, RxNeovimApi.Value>.self)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
if errWhenBlocked {
|
||||
return self
|
||||
.checkBlocked(
|
||||
self.rpc(method: "nvim_get_hl_by_id", params: params, expectsReturnValue: true)
|
||||
)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
return self
|
||||
.rpc(method: "nvim_get_hl_by_id", params: params, expectsReturnValue: true)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func getHlIdByName(
|
||||
name: String,
|
||||
errWhenBlocked: Bool = true
|
||||
@ -2290,6 +2250,38 @@ extension RxNeovimApi {
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func getHl(
|
||||
ns_id: Int,
|
||||
opts: Dictionary<String, RxNeovimApi.Value>,
|
||||
errWhenBlocked: Bool = true
|
||||
) -> Single<Dictionary<String, RxNeovimApi.Value>> {
|
||||
|
||||
let params: [RxNeovimApi.Value] = [
|
||||
.int(Int64(ns_id)),
|
||||
.map(opts.mapToDict({ (Value.string($0), $1) })),
|
||||
]
|
||||
|
||||
func transform(_ value: Value) throws -> Dictionary<String, RxNeovimApi.Value> {
|
||||
guard let result = (msgPackDictToSwift(value.dictionaryValue)) else {
|
||||
throw RxNeovimApi.Error.conversion(type: Dictionary<String, RxNeovimApi.Value>.self)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
if errWhenBlocked {
|
||||
return self
|
||||
.checkBlocked(
|
||||
self.rpc(method: "nvim_get_hl", params: params, expectsReturnValue: true)
|
||||
)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
return self
|
||||
.rpc(method: "nvim_get_hl", params: params, expectsReturnValue: true)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func setHl(
|
||||
ns_id: Int,
|
||||
name: String,
|
||||
@ -3968,49 +3960,20 @@ extension RxNeovimApi {
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func getDirtyStatus(
|
||||
errWhenBlocked: Bool = true
|
||||
) -> Single<Bool> {
|
||||
|
||||
let params: [RxNeovimApi.Value] = [
|
||||
|
||||
]
|
||||
|
||||
func transform(_ value: Value) throws -> Bool {
|
||||
guard let result = (value.boolValue) else {
|
||||
throw RxNeovimApi.Error.conversion(type: Bool.self)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
if errWhenBlocked {
|
||||
return self
|
||||
.checkBlocked(
|
||||
self.rpc(method: "nvim_get_dirty_status", params: params, expectsReturnValue: true)
|
||||
)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
return self
|
||||
.rpc(method: "nvim_get_dirty_status", params: params, expectsReturnValue: true)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
public func exec(
|
||||
public func exec2(
|
||||
src: String,
|
||||
output: Bool,
|
||||
opts: Dictionary<String, RxNeovimApi.Value>,
|
||||
errWhenBlocked: Bool = true
|
||||
) -> Single<String> {
|
||||
) -> Single<Dictionary<String, RxNeovimApi.Value>> {
|
||||
|
||||
let params: [RxNeovimApi.Value] = [
|
||||
.string(src),
|
||||
.bool(output),
|
||||
.map(opts.mapToDict({ (Value.string($0), $1) })),
|
||||
]
|
||||
|
||||
func transform(_ value: Value) throws -> String {
|
||||
guard let result = (value.stringValue) else {
|
||||
throw RxNeovimApi.Error.conversion(type: String.self)
|
||||
func transform(_ value: Value) throws -> Dictionary<String, RxNeovimApi.Value> {
|
||||
guard let result = (msgPackDictToSwift(value.dictionaryValue)) else {
|
||||
throw RxNeovimApi.Error.conversion(type: Dictionary<String, RxNeovimApi.Value>.self)
|
||||
}
|
||||
|
||||
return result
|
||||
@ -4019,13 +3982,13 @@ extension RxNeovimApi {
|
||||
if errWhenBlocked {
|
||||
return self
|
||||
.checkBlocked(
|
||||
self.rpc(method: "nvim_exec", params: params, expectsReturnValue: true)
|
||||
self.rpc(method: "nvim_exec2", params: params, expectsReturnValue: true)
|
||||
)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
return self
|
||||
.rpc(method: "nvim_exec", params: params, expectsReturnValue: true)
|
||||
.rpc(method: "nvim_exec2", params: params, expectsReturnValue: true)
|
||||
.map(transform)
|
||||
}
|
||||
|
||||
|
@ -1,268 +0,0 @@
|
||||
/// Tae Won Ha - http://taewon.de - @hataewon
|
||||
/// See LICENSE
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
|
||||
public final class RxMessagePortClient {
|
||||
public enum ResponseCode {
|
||||
// Unfortunately, case success = kCFMessagePortSuccess is not possible.
|
||||
case success
|
||||
case sendTimeout
|
||||
case receiveTimeout
|
||||
case isInvalid
|
||||
case transportError
|
||||
case becameInvalidError
|
||||
case unknown
|
||||
|
||||
fileprivate init(rawResponseCode code: Int32) {
|
||||
switch code {
|
||||
case kCFMessagePortSuccess: self = .success
|
||||
case kCFMessagePortSendTimeout: self = .sendTimeout
|
||||
case kCFMessagePortReceiveTimeout: self = .receiveTimeout
|
||||
case kCFMessagePortIsInvalid: self = .isInvalid
|
||||
case kCFMessagePortTransportError: self = .transportError
|
||||
case kCFMessagePortBecameInvalidError: self = .becameInvalidError
|
||||
default: self = .unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum Error: Swift.Error {
|
||||
case serverInit
|
||||
case clientInit
|
||||
case portInvalid
|
||||
case send(msgid: Int32, response: ResponseCode)
|
||||
}
|
||||
|
||||
public static let defaultTimeout = CFTimeInterval(5)
|
||||
|
||||
public let uuid = UUID()
|
||||
public var timeout = RxMessagePortClient.defaultTimeout
|
||||
|
||||
public init(queueQos: DispatchQoS) {
|
||||
self.queue = DispatchQueue(
|
||||
label: "\(String(reflecting: RxMessagePortClient.self))-\(self.uuid.uuidString)",
|
||||
qos: queueQos,
|
||||
target: .global(qos: queueQos.qosClass)
|
||||
)
|
||||
}
|
||||
|
||||
public func send(msgid: Int32, data: Data?, expectsReply: Bool) -> Single<Data?> {
|
||||
Single.create { single in
|
||||
self.queue.async {
|
||||
guard CFMessagePortIsValid(self.port) else {
|
||||
single(.failure(Error.portInvalid))
|
||||
return
|
||||
}
|
||||
|
||||
let returnDataPtr = UnsafeMutablePointer<Unmanaged<CFData>?>.allocate(capacity: 1)
|
||||
defer { returnDataPtr.deallocate() }
|
||||
|
||||
let responseCode = CFMessagePortSendRequest(
|
||||
self.port,
|
||||
msgid,
|
||||
data?.cfdata,
|
||||
self.timeout,
|
||||
self.timeout,
|
||||
expectsReply ? CFRunLoopMode.defaultMode.rawValue : nil,
|
||||
expectsReply ? returnDataPtr : nil
|
||||
)
|
||||
|
||||
guard responseCode == kCFMessagePortSuccess else {
|
||||
single(.failure(
|
||||
Error.send(msgid: msgid, response: ResponseCode(rawResponseCode: responseCode))
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
guard expectsReply else {
|
||||
single(.success(nil))
|
||||
return
|
||||
}
|
||||
|
||||
// Upon return, [returnData] contains a CFData object
|
||||
// containing the reply data. Ownership follows the The Create Rule.
|
||||
// From: https://developer.apple.com/documentation/corefoundation/1543076-cfmessageportsendrequest
|
||||
// This means that we have to release the returned CFData.
|
||||
// Thus, we have to use Unmanaged.takeRetainedValue()
|
||||
// See also https://www.mikeash.com/pyblog/friday-qa-2017-08-11-swiftunmanaged.html
|
||||
let data: Data? = returnDataPtr.pointee?.takeRetainedValue().data
|
||||
single(.success(data))
|
||||
}
|
||||
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
public func connect(to name: String) -> Completable {
|
||||
Completable.create { completable in
|
||||
self.queue.async {
|
||||
self.port = CFMessagePortCreateRemote(kCFAllocatorDefault, name.cfstr)
|
||||
|
||||
if self.port == nil {
|
||||
completable(.error(Error.clientInit))
|
||||
return
|
||||
}
|
||||
|
||||
completable(.completed)
|
||||
}
|
||||
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
public func stop() -> Completable {
|
||||
Completable.create { completable in
|
||||
self.queue.async {
|
||||
if self.port != nil, CFMessagePortIsValid(self.port) {
|
||||
CFMessagePortInvalidate(self.port)
|
||||
}
|
||||
completable(.completed)
|
||||
}
|
||||
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
private var port: CFMessagePort?
|
||||
|
||||
private let queue: DispatchQueue
|
||||
}
|
||||
|
||||
public final class RxMessagePortServer {
|
||||
public typealias SyncReplyBody = (Int32, Data?) -> Data?
|
||||
|
||||
public struct Message {
|
||||
public var msgid: Int32
|
||||
public var data: Data?
|
||||
}
|
||||
|
||||
public let uuid = UUID()
|
||||
|
||||
public var syncReplyBody: SyncReplyBody? {
|
||||
get { self.messageHandler.syncReplyBody }
|
||||
set { self.messageHandler.syncReplyBody = newValue }
|
||||
}
|
||||
|
||||
public var stream: Observable<Message> { self.streamSubject.asObservable() }
|
||||
|
||||
public init(queueQos: DispatchQoS) {
|
||||
self.queue = DispatchQueue(
|
||||
label: "\(String(reflecting: RxMessagePortClient.self))-\(self.uuid.uuidString)",
|
||||
qos: queueQos,
|
||||
target: .global(qos: queueQos.qosClass)
|
||||
)
|
||||
self.messageHandler = MessageHandler(subject: self.streamSubject)
|
||||
|
||||
self.portQueue = DispatchQueue(
|
||||
label: "\(String(reflecting: RxMessagePortClient.self))-portQueue-\(self.uuid.uuidString)",
|
||||
qos: queueQos
|
||||
)
|
||||
}
|
||||
|
||||
public func run(as name: String) -> Completable {
|
||||
Completable.create { [unowned self] completable in
|
||||
self.queue.async {
|
||||
var localCtx = CFMessagePortContext(
|
||||
version: 0,
|
||||
info: Unmanaged.passUnretained(self.messageHandler).toOpaque(),
|
||||
retain: nil,
|
||||
release: nil,
|
||||
copyDescription: nil
|
||||
)
|
||||
|
||||
self.port = CFMessagePortCreateLocal(
|
||||
kCFAllocatorDefault,
|
||||
name.cfstr,
|
||||
{ _, msgid, data, info in
|
||||
guard let infoPtr = UnsafeRawPointer(info) else { return nil }
|
||||
|
||||
let handler = Unmanaged<MessageHandler>.fromOpaque(infoPtr).takeUnretainedValue()
|
||||
return handler.handleMessage(msgId: msgid, cfdata: data)
|
||||
},
|
||||
&localCtx,
|
||||
nil
|
||||
)
|
||||
|
||||
if self.port == nil {
|
||||
self.streamSubject.onError(RxMessagePortClient.Error.serverInit)
|
||||
completable(.error(RxMessagePortClient.Error.serverInit))
|
||||
}
|
||||
|
||||
self.portQueue.async { self.runServer() }
|
||||
completable(.completed)
|
||||
}
|
||||
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
public func stop() -> Completable {
|
||||
Completable.create { completable in
|
||||
self.queue.async {
|
||||
self.messageHandler.syncReplyBody = nil
|
||||
self.streamSubject.onCompleted()
|
||||
|
||||
if let portRunLoop = self.portRunLoop { CFRunLoopStop(portRunLoop) }
|
||||
|
||||
if self.port != nil, CFMessagePortIsValid(self.port) {
|
||||
CFMessagePortInvalidate(self.port)
|
||||
}
|
||||
|
||||
completable(.completed)
|
||||
}
|
||||
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
private var port: CFMessagePort?
|
||||
private let portQueue: DispatchQueue
|
||||
private var portRunLoop: CFRunLoop?
|
||||
|
||||
private let queue: DispatchQueue
|
||||
|
||||
private var messageHandler: MessageHandler
|
||||
private let streamSubject = PublishSubject<Message>()
|
||||
|
||||
private func runServer() {
|
||||
self.portRunLoop = CFRunLoopGetCurrent()
|
||||
let runLoopSrc = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, self.port, 0)
|
||||
CFRunLoopAddSource(self.portRunLoop, runLoopSrc, .defaultMode)
|
||||
CFRunLoopRun()
|
||||
}
|
||||
}
|
||||
|
||||
private class MessageHandler {
|
||||
fileprivate var syncReplyBody: RxMessagePortServer.SyncReplyBody?
|
||||
|
||||
fileprivate init(subject: PublishSubject<RxMessagePortServer.Message>) { self.subject = subject }
|
||||
|
||||
fileprivate func handleMessage(msgId: Int32, cfdata: CFData?) -> Unmanaged<CFData>? {
|
||||
let d = cfdata?.data
|
||||
|
||||
self.subject.onNext(RxMessagePortServer.Message(msgid: msgId, data: d))
|
||||
|
||||
guard let reply = self.syncReplyBody?(msgId, d) else { return nil }
|
||||
|
||||
// The system releases the returned CFData object.
|
||||
// From https://developer.apple.com/documentation/corefoundation/cfmessageportcallback
|
||||
// See also https://www.mikeash.com/pyblog/friday-qa-2017-08-11-swiftunmanaged.html
|
||||
return Unmanaged.passRetained(reply.cfdata)
|
||||
}
|
||||
|
||||
private let subject: PublishSubject<RxMessagePortServer.Message>
|
||||
}
|
||||
|
||||
private extension Data {
|
||||
var cfdata: CFData { self as NSData }
|
||||
}
|
||||
|
||||
private extension CFData {
|
||||
var data: Data { self as NSData as Data }
|
||||
}
|
||||
|
||||
private extension String {
|
||||
var cfstr: CFString { self as NSString }
|
||||
}
|
@ -9,7 +9,6 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
4B022661224AB1490052362B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B022660224AB1490052362B /* Assets.xcassets */; };
|
||||
4B022664224AB1490052362B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B022662224AB1490052362B /* MainMenu.xib */; };
|
||||
4B2AD136293BC0C4009DA797 /* RxMessagePort.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2AD135293BC0C4009DA797 /* RxMessagePort.swift */; };
|
||||
4B2AD13B293BCC4E009DA797 /* RxPack in Frameworks */ = {isa = PBXBuildFile; productRef = 4B2AD13A293BCC4E009DA797 /* RxPack */; };
|
||||
4B7FBFD024EC8632002D12A1 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 4B7FBFCF24EC8632002D12A1 /* RxSwift */; };
|
||||
4B7FBFD124EC8851002D12A1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B02265E224AB1490052362B /* AppDelegate.swift */; };
|
||||
@ -34,7 +33,6 @@
|
||||
4B022660224AB1490052362B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
4B022663224AB1490052362B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
4B022665224AB1490052362B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
4B2AD135293BC0C4009DA797 /* RxMessagePort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RxMessagePort.swift; path = ../Sources/RxPack/RxMessagePort.swift; sourceTree = "<group>"; };
|
||||
4BE73FA8285DC4DA00B63585 /* RxPack */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = RxPack; path = ..; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -54,7 +52,6 @@
|
||||
4B02263A224AB11A0052362B = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B2AD135293BC0C4009DA797 /* RxMessagePort.swift */,
|
||||
4BE73FA7285DC4DA00B63585 /* Packages */,
|
||||
4B02265D224AB1490052362B /* RxMessagePortDemo */,
|
||||
4B022644224AB11A0052362B /* Products */,
|
||||
@ -127,8 +124,9 @@
|
||||
4B02263B224AB11A0052362B /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastSwiftUpdateCheck = 1020;
|
||||
LastUpgradeCheck = 1340;
|
||||
LastUpgradeCheck = 1500;
|
||||
ORGANIZATIONNAME = "Tae Won Ha";
|
||||
TargetAttributes = {
|
||||
4B02265B224AB1490052362B = {
|
||||
@ -174,7 +172,6 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4B2AD136293BC0C4009DA797 /* RxMessagePort.swift in Sources */,
|
||||
4B7FBFD124EC8851002D12A1 /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -228,9 +225,11 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
@ -245,7 +244,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
@ -291,9 +290,11 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = fast;
|
||||
@ -303,7 +304,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
@ -318,11 +319,13 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
INFOPLIST_FILE = RxMessagePortDemo/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.qvacua.RxMessagePortDemo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -334,11 +337,13 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
INFOPLIST_FILE = RxMessagePortDemo/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.qvacua.RxMessagePortDemo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
3
VimR.xcworkspace/contents.xcworkspacedata
generated
3
VimR.xcworkspace/contents.xcworkspacedata
generated
@ -16,9 +16,6 @@
|
||||
<FileRef
|
||||
location = "group:Tabs">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:NvimServer">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:NvimView">
|
||||
</FileRef>
|
||||
|
@ -14,8 +14,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/mattgallagher/CwlCatchException.git",
|
||||
"state" : {
|
||||
"revision" : "35f9e770f54ce62dd8526470f14c6e137cef3eea",
|
||||
"version" : "2.1.1"
|
||||
"revision" : "3b123999de19bf04905bc1dfdb76f817b0f2cc00",
|
||||
"version" : "2.1.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -23,8 +23,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git",
|
||||
"state" : {
|
||||
"revision" : "c21f7bab5ca8eee0a9998bbd17ca1d0eb45d4688",
|
||||
"version" : "2.1.0"
|
||||
"revision" : "a23ded2c91df9156628a6996ab4f347526f17b6b",
|
||||
"version" : "2.1.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1224,6 +1224,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
OTHER_LDFLAGS = "-pthread";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(VIMR_BUNDLE_IDENTIFIER)";
|
||||
PRODUCT_MODULE_NAME = VimR;
|
||||
@ -1246,6 +1247,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
OTHER_LDFLAGS = "-pthread";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(VIMR_BUNDLE_IDENTIFIER)";
|
||||
PRODUCT_MODULE_NAME = VimR;
|
||||
|
@ -26,6 +26,9 @@ final class AdvancedPrefReducer: ReducerType {
|
||||
|
||||
case let .setUseSnapshotUpdate(value):
|
||||
state.useSnapshotUpdate = value
|
||||
|
||||
case let .setNvimBinary(value):
|
||||
state.mainWindowTemplate.nvimBinary = value
|
||||
}
|
||||
|
||||
return (state, pair.action, true)
|
||||
|
@ -15,6 +15,7 @@ final class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
case setUseSnapshotUpdate(Bool)
|
||||
case setUseLiveResize(Bool)
|
||||
case setDrawsParallel(Bool)
|
||||
case setNvimBinary(String)
|
||||
}
|
||||
|
||||
override var displayName: String {
|
||||
@ -32,6 +33,7 @@ final class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
self.useSnapshotUpdate = state.useSnapshotUpdate
|
||||
self.useLiveResize = state.mainWindowTemplate.useLiveResize
|
||||
self.drawsParallel = state.mainWindowTemplate.drawsParallel
|
||||
self.nvimBinary = state.mainWindowTemplate.nvimBinary
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
@ -42,11 +44,13 @@ final class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
.observe(on: MainScheduler.instance)
|
||||
.subscribe(onNext: { state in
|
||||
if self.useInteractiveZsh != state.mainWindowTemplate.useInteractiveZsh
|
||||
|| self.nvimBinary != state.mainWindowTemplate.nvimBinary
|
||||
|| self.useSnapshotUpdate != state.useSnapshotUpdate
|
||||
|| self.useLiveResize != state.mainWindowTemplate.useLiveResize
|
||||
|| self.drawsParallel != state.mainWindowTemplate.drawsParallel
|
||||
{
|
||||
self.useInteractiveZsh = state.mainWindowTemplate.useInteractiveZsh
|
||||
self.nvimBinary = state.mainWindowTemplate.nvimBinary
|
||||
self.useSnapshotUpdate = state.useSnapshotUpdate
|
||||
self.useLiveResize = state.mainWindowTemplate.useLiveResize
|
||||
self.drawsParallel = state.mainWindowTemplate.drawsParallel
|
||||
@ -64,22 +68,29 @@ final class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
private var useSnapshotUpdate: Bool
|
||||
private var useLiveResize: Bool
|
||||
private var drawsParallel: Bool
|
||||
private var nvimBinary: String = ""
|
||||
|
||||
private let useInteractiveZshCheckbox = NSButton(forAutoLayout: ())
|
||||
private let useSnapshotUpdateCheckbox = NSButton(forAutoLayout: ())
|
||||
private let useLiveResizeCheckbox = NSButton(forAutoLayout: ())
|
||||
private let drawsParallelCheckbox = NSButton(forAutoLayout: ())
|
||||
private let nvimBinaryField = NSTextView(forAutoLayout: ())
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func windowWillClose() {
|
||||
self.nvimBinaryFieldAction()
|
||||
}
|
||||
|
||||
private func updateViews() {
|
||||
self.useSnapshotUpdateCheckbox.boolState = self.useSnapshotUpdate
|
||||
self.useInteractiveZshCheckbox.boolState = self.useInteractiveZsh
|
||||
self.useLiveResizeCheckbox.boolState = self.useLiveResize
|
||||
self.drawsParallelCheckbox.boolState = self.drawsParallel
|
||||
self.nvimBinaryField.string = self.nvimBinary ?? ""
|
||||
}
|
||||
|
||||
private func addViews() {
|
||||
@ -136,6 +147,9 @@ final class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
when scrolling very fast.
|
||||
"""#)
|
||||
|
||||
let nvimBinaryTitle = self.titleTextField(title: "NeoVim Binary:")
|
||||
let nvimBinaryField = self.nvimBinaryField
|
||||
|
||||
self.addSubview(paneTitle)
|
||||
|
||||
self.addSubview(useSnapshotUpdate)
|
||||
@ -146,6 +160,8 @@ final class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
self.addSubview(useLiveResizeInfo)
|
||||
self.addSubview(drawsParallelBox)
|
||||
self.addSubview(drawsParallelInfo)
|
||||
self.addSubview(nvimBinaryTitle)
|
||||
self.addSubview(nvimBinaryField)
|
||||
|
||||
paneTitle.autoPinEdge(toSuperviewEdge: .top, withInset: 18)
|
||||
paneTitle.autoPinEdge(toSuperviewEdge: .left, withInset: 18)
|
||||
@ -174,6 +190,21 @@ final class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate {
|
||||
|
||||
useInteractiveZshInfo.autoPinEdge(.top, to: .bottom, of: useInteractiveZsh, withOffset: 5)
|
||||
useInteractiveZshInfo.autoPinEdge(.left, to: .left, of: useInteractiveZsh)
|
||||
|
||||
nvimBinaryTitle.autoPinEdge(.top, to: .bottom, of: useInteractiveZshInfo, withOffset: 18)
|
||||
nvimBinaryTitle.autoPinEdge(.left, to: .left, of: useLiveResize, withOffset: 5)
|
||||
//nvimBinaryTitle.autoAlignAxis(.baseline, toSameAxisOf: nvimBinaryField)
|
||||
|
||||
nvimBinaryField.autoPinEdge(.top, to: .bottom, of: useInteractiveZshInfo, withOffset: 18)
|
||||
nvimBinaryField.autoPinEdge(.left, to: .right, of: nvimBinaryTitle, withOffset: 5)
|
||||
nvimBinaryField.autoPinEdge(toSuperviewEdge: .right, withInset: 18)
|
||||
nvimBinaryField.autoSetDimension(.height, toSize: 20, relation: .greaterThanOrEqual)
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: NSControl.textDidEndEditingNotification,
|
||||
object: nvimBinaryField,
|
||||
queue: nil
|
||||
) { [weak self] _ in self?.nvimBinaryFieldAction() }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,4 +226,9 @@ extension AdvancedPref {
|
||||
@objc func useSnapshotUpdateChannelAction(_ sender: NSButton) {
|
||||
self.emit(.setUseSnapshotUpdate(sender.boolState))
|
||||
}
|
||||
|
||||
func nvimBinaryFieldAction() {
|
||||
let newNvimBinary = self.nvimBinaryField.string
|
||||
self.emit(.setNvimBinary(newNvimBinary))
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import MessagePack
|
||||
import NvimView
|
||||
import RxNeovim
|
||||
import RxPack
|
||||
@ -101,6 +102,8 @@ extension MainWindow {
|
||||
for: .setTheme(Theme(from: nvimTheme, additionalColorDict: colors))
|
||||
)
|
||||
)
|
||||
}, onError: {
|
||||
err in self.log.trace("oops couldn't set theme")
|
||||
})
|
||||
.disposed(by: self.disposeBag)
|
||||
}
|
||||
@ -153,7 +156,8 @@ extension MainWindow {
|
||||
(
|
||||
colorName: colorName,
|
||||
observable: self.neoVimView.api
|
||||
.getHlByName(name: colorName, rgb: true)
|
||||
.getHl(ns_id: 0,
|
||||
opts: ["name": MessagePackValue(colorName)])
|
||||
.asObservable()
|
||||
)
|
||||
})
|
||||
|
@ -86,6 +86,7 @@ final class MainWindow: NSObject,
|
||||
usesCustomTabBar: state.appearance.usesCustomTab,
|
||||
useInteractiveZsh: state.useInteractiveZsh,
|
||||
cwd: state.cwd,
|
||||
nvimBinary: state.nvimBinary,
|
||||
nvimArgs: state.nvimArgs,
|
||||
envDict: state.envDict,
|
||||
sourceFiles: sourceFileUrls
|
||||
@ -259,7 +260,7 @@ final class MainWindow: NSObject,
|
||||
private var usesTheme = true
|
||||
private var lastThemeMark = Token()
|
||||
|
||||
private let log = OSLog(
|
||||
internal let log = OSLog(
|
||||
subsystem: Defs.loggerSubsystem,
|
||||
category: Defs.LoggerCategory.ui
|
||||
)
|
||||
|
@ -20,6 +20,7 @@ final class PrefMiddleware: MiddlewareType {
|
||||
do {
|
||||
let dictionary: [String: Any] = try dictEncoder.encode(appState)
|
||||
defaults.set(dictionary, forKey: PrefMiddleware.compatibleVersion)
|
||||
defaults.synchronize()
|
||||
} catch {
|
||||
self.log.error("AppState could not converted to Dictionary: \(error)")
|
||||
}
|
||||
@ -59,6 +60,7 @@ final class PrefMiddleware: MiddlewareType {
|
||||
do {
|
||||
let dictionary: [String: Any] = try dictEncoder.encode(result.state)
|
||||
defaults.set(dictionary, forKey: PrefMiddleware.compatibleVersion)
|
||||
defaults.synchronize()
|
||||
} catch {
|
||||
self.log.error("AppState could not converted to Dictionary: \(error)")
|
||||
}
|
||||
@ -86,6 +88,7 @@ final class PrefMiddleware: MiddlewareType {
|
||||
do {
|
||||
let dictionary: [String: Any] = try dictEncoder.encode(result.state)
|
||||
defaults.set(dictionary, forKey: PrefMiddleware.compatibleVersion)
|
||||
defaults.synchronize()
|
||||
} catch {
|
||||
self.log.error("AppState could not converted to Dictionary: \(error)")
|
||||
}
|
||||
|
@ -263,7 +263,9 @@ struct AppearanceState: Codable {
|
||||
|
||||
extension MainWindow {
|
||||
struct State: Codable {
|
||||
static let `default` = State(isAllToolsVisible: true, isToolButtonsVisible: true)
|
||||
static let `default` = State(isAllToolsVisible: true,
|
||||
isToolButtonsVisible: true,
|
||||
nvimBinary: "")
|
||||
|
||||
static let defaultTools: [MainWindow.Tools: WorkspaceToolState] = [
|
||||
.fileBrowser: WorkspaceToolState(location: .left, dimension: 200, open: true),
|
||||
@ -317,6 +319,7 @@ extension MainWindow {
|
||||
|
||||
var appearance = AppearanceState.default
|
||||
var useInteractiveZsh = false
|
||||
var nvimBinary: String = ""
|
||||
var nvimArgs: [String]?
|
||||
var cliPipePath: String?
|
||||
var envDict: [String: String]?
|
||||
@ -326,15 +329,17 @@ extension MainWindow {
|
||||
var isLeftOptionMeta = false
|
||||
var isRightOptionMeta = false
|
||||
|
||||
|
||||
// to be cleaned
|
||||
var urlsToOpen = [URL: OpenMode]()
|
||||
var currentBufferToSet: NvimView.Buffer?
|
||||
var cwdToSet: URL?
|
||||
var viewToBeFocused: FocusableView? = FocusableView.neoVimView
|
||||
|
||||
init(isAllToolsVisible: Bool, isToolButtonsVisible: Bool) {
|
||||
init(isAllToolsVisible: Bool, isToolButtonsVisible: Bool, nvimBinary: String) {
|
||||
self.isAllToolsVisible = isAllToolsVisible
|
||||
self.isToolButtonsVisible = isToolButtonsVisible
|
||||
self.nvimBinary = nvimBinary
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
@ -348,6 +353,8 @@ extension MainWindow {
|
||||
case isRightOptionMeta = "is-right-option-meta"
|
||||
|
||||
case useInteractiveZsh = "use-interactive-zsh"
|
||||
case nvimBinary = "nvim-binary"
|
||||
|
||||
case useLiveResize = "use-live-resize"
|
||||
case drawsParallel = "draws-parallel"
|
||||
case isShowHidden = "is-show-hidden"
|
||||
@ -373,6 +380,7 @@ extension MainWindow {
|
||||
forKey: .useInteractiveZsh,
|
||||
default: State.default.useInteractiveZsh
|
||||
)
|
||||
self.nvimBinary = try container.decodeIfPresent(String.self, forKey: .nvimBinary) ?? State.default.nvimBinary
|
||||
self.useLiveResize = try container.decode(
|
||||
forKey: .useLiveResize,
|
||||
default: State.default.useLiveResize
|
||||
@ -459,6 +467,7 @@ extension MainWindow {
|
||||
try container.encode(self.isLeftOptionMeta, forKey: .isLeftOptionMeta)
|
||||
try container.encode(self.isRightOptionMeta, forKey: .isRightOptionMeta)
|
||||
try container.encode(self.useInteractiveZsh, forKey: .useInteractiveZsh)
|
||||
try container.encode(self.nvimBinary, forKey: .nvimBinary)
|
||||
try container.encode(self.fileBrowserShowHidden, forKey: .isShowHidden)
|
||||
|
||||
// See [1]
|
||||
|
@ -13,7 +13,7 @@ prepare_nvimserver() {
|
||||
|
||||
# Build NvimServer and copy
|
||||
build_libnvim=true ./NvimServer/NvimServer/bin/build_nvimserver.sh
|
||||
cp ./NvimServer/.build/apple/Products/Release/NvimServer "${resources_folder}"
|
||||
cp ./NvimServer/.build/arm64-apple-macosx/release/NvimServer "${resources_folder}"
|
||||
|
||||
# Create and copy runtime folder
|
||||
install_path="$(/usr/bin/mktemp -d -t 'nvim-runtime')"
|
||||
@ -38,10 +38,17 @@ build_vimr() {
|
||||
|
||||
echo "### Xcodebuilding"
|
||||
rm -rf "${build_path}"
|
||||
xcodebuild \
|
||||
-configuration Release -derivedDataPath "${build_path}" \
|
||||
-workspace VimR.xcworkspace -scheme VimR \
|
||||
clean build
|
||||
if [[ "${clean}" == true ]]; then
|
||||
xcodebuild \
|
||||
-configuration Release -derivedDataPath "${build_path}" \
|
||||
-workspace VimR.xcworkspace -scheme VimR \
|
||||
clean build
|
||||
else
|
||||
xcodebuild \
|
||||
-configuration Release -derivedDataPath "${build_path}" \
|
||||
-workspace VimR.xcworkspace -scheme VimR \
|
||||
build
|
||||
fi
|
||||
}
|
||||
|
||||
main () {
|
||||
|
@ -25,7 +25,7 @@ def swift_autocmds(version: str, template_string: str) -> str:
|
||||
|
||||
return template.substitute(
|
||||
event_cases="\n".join(
|
||||
[f" case {event[0].lower()} = {event[1]}" for event in autocmds]
|
||||
[f" case {event[0].lower()} = \"{event[0].lower()}\"" for event in autocmds]
|
||||
),
|
||||
version=version
|
||||
)
|
||||
|
@ -46,7 +46,7 @@ def are_shapes_same() -> bool:
|
||||
def swift_shapes() -> str:
|
||||
with io.open(SWIFT_TEMPLATE_FILE, "r") as template_file:
|
||||
template = Template(template_file.read())
|
||||
cases = "\n".join([f" case {v[1]} = {v[0]}" for (k, v) in SHAPE_NAMES.items()])
|
||||
cases = "\n".join([f" case {v[1]} = \"{v[1]}\"" for (k, v) in SHAPE_NAMES.items()])
|
||||
return template.substitute(
|
||||
cursor_shapes=cases,
|
||||
version=version
|
||||
|
@ -2,7 +2,7 @@
|
||||
set -Eeuo pipefail
|
||||
|
||||
readonly vimr_app_path=${vimr_app_path:?"Path to VimR.app"}
|
||||
readonly identity="Developer ID Application: Tae Won Ha (H96Q2NKTQH)"
|
||||
readonly identity="Developer ID Application: George Harker (B8V3694RNX)"
|
||||
|
||||
remove_sparkle_xpc () {
|
||||
# VimR is not sandboxed, so, remove the XPCs
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Auto generated for nvim ${version}
|
||||
// See bin/generate_autocmds.py
|
||||
|
||||
enum NvimAutoCommandEvent: Int {
|
||||
enum NvimAutoCommandEvent: String {
|
||||
|
||||
${event_cases}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
// Auto generated for nvim ${version}
|
||||
// See bin/generate_cursor_shape.py
|
||||
|
||||
public enum CursorModeShape: UInt {
|
||||
public enum CursorModeShape: String {
|
||||
|
||||
${cursor_shapes}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user