1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-11-24 11:37:32 +03:00

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

Conflicts:
	NeoVimServer/server_ui.m
	SwiftNeoVim/NeoVimView.swift
        and many others...
This commit is contained in:
Tae Won Ha 2017-05-27 17:22:47 +02:00
commit a7bc657514
18 changed files with 1966 additions and 1510 deletions

1
.gitignore vendored
View File

@ -179,5 +179,6 @@ Temporary Items
# -------------------------------------------
*.generated.h
*.generated.m
.deps

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -12,11 +12,11 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="MacNeovim" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" animationBehavior="default" id="QvC-M9-y7g">
<window title="MacNeovim" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" frameAutosaveName="neovim-window" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<rect key="screenRect" x="0.0" y="0.0" width="2304" height="1273"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
<autoresizingMask key="autoresizingMask"/>

View File

@ -8,7 +8,6 @@
#import "Logging.h"
#import "server_globals.h"
#import "NeoVimServer.h"
#import "NeoVimUiBridgeProtocol.h"
#import "NeoVimBuffer.h"
#import "NeoVimWindow.h"
#import "NeoVimTab.h"
@ -32,6 +31,28 @@
#define pun_type(t, x) (*((t *) (&(x))))
// From NeoVimUiBridgeProtocol.h
typedef NS_ENUM(NSUInteger, FontTrait) {
FontTraitNone = 0,
FontTraitItalic = (1 << 0),
FontTraitBold = (1 << 1),
FontTraitUnderline = (1 << 2),
FontTraitUndercurl = (1 << 3)
};
typedef struct {
FontTrait fontTrait;
unsigned int foreground;
unsigned int background;
unsigned int special;
} CellAttributes;
static NSInteger _default_foreground = 0xFF000000;
static NSInteger _default_background = 0xFFFFFFFF;
static NSInteger _default_special = 0xFFFF0000;
// ---
typedef struct {
UIBridgeData *bridge;
Loop *loop;
@ -42,9 +63,6 @@ typedef struct {
// We declare nvim_main because it's not declared in any header files of neovim
extern int nvim_main(int argc, char **argv);
static NSInteger _default_foreground = qDefaultForeground;
static NSInteger _default_background = qDefaultBackground;
static NSInteger _default_special = qDefaultSpecial;
// The thread in which neovim's main runs
static uv_thread_t _nvim_thread;

View File

@ -20,7 +20,8 @@ extension CellAttributes: CustomStringConvertible, Equatable {
}
public var description: String {
return "CellAttributes<fg: \(String(format: "%x", self.foreground)), bg: \(String(format: "%x", self.background)))"
return "CellAttributes<fg: \(String(self.foreground, radix: 16)), " +
"bg: \(String(self.background, radix: 16)))>"
}
public var inverted: CellAttributes {

163
SwiftNeoVim/Logger.swift Normal file
View File

@ -0,0 +1,163 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Foundation
class FileLogger {
enum Level: String {
case `default` = "DEFAULT"
case info = "INFO"
case debug = "DEBUG"
case error = "ERROR"
case fault = "FAULT"
}
let uuid = UUID().uuidString
let name: String
let shouldLogDebug: Bool
init<T>(as name: T, with fileUrl: URL) {
#if DEBUG
self.shouldLogDebug = true
#else
self.shouldLogDebug = false
#endif
switch name {
case let str as String: self.name = str
default: self.name = String(describing: name)
}
guard fileUrl.isFileURL else {
preconditionFailure("\(fileUrl) must be a file URL!")
}
self.queue = DispatchQueue(label: self.uuid, qos: .background)
self.fileUrl = fileUrl
self.setupFileHandle(at: fileUrl)
self.logDateFormatter.dateFormat = "dd HH:mm:SSS"
self.fileDateFormatter.dateFormat = "yyyy-MM-dd_HH-mm-SSS"
}
fileprivate func setupFileHandle(at fileUrl: URL) {
if !fileManager.fileExists(atPath: fileUrl.path) {
fileManager.createFile(atPath: fileUrl.path, contents: nil)
}
if let fileHandle = try? FileHandle(forWritingTo: fileUrl) {
self.fileHandle = fileHandle
self.fileHandle.seekToEndOfFile()
} else {
NSLog("[ERROR] Could not get handle for \(fileUrl), defaulting to STDOUT")
self.fileHandle = FileHandle.standardOutput
}
}
deinit {
self.fileHandle.closeFile()
}
func mark(file: String = #file, line: Int = #line, function: String = #function) {
guard self.shouldLogDebug else {
return
}
self.log("", level: .debug, file: file, line: line, function: function)
}
func `default`<T>(_ message: @escaping @autoclosure () -> T,
file: String = #file, line: Int = #line, function: String = #function) {
self.log(message, level: .default, file: file, line: line, function: function)
}
func info<T>(_ message: @escaping @autoclosure () -> T,
file: String = #file, line: Int = #line, function: String = #function) {
self.log(message, level: .info, file: file, line: line, function: function)
}
func debug<T>(_ message: @escaping @autoclosure () -> T,
file: String = #file, line: Int = #line, function: String = #function) {
guard self.shouldLogDebug else {
return
}
self.log(message, level: .debug, file: file, line: line, function: function)
}
func error<T>(_ message: @escaping @autoclosure () -> T,
file: String = #file, line: Int = #line, function: String = #function) {
self.log(message, level: .error, file: file, line: line, function: function)
}
func fault<T>(_ message: @escaping @autoclosure () -> T,
file: String = #file, line: Int = #line, function: String = #function) {
self.log(message, level: .fault, file: file, line: line, function: function)
}
func log<T>(_ message: @escaping @autoclosure () -> T, level: Level = .default,
file: String = #file, line: Int = #line, function: String = #function) {
self.queue.async {
let timestamp = self.logDateFormatter.string(from: Date())
let strMsg = self.string(from: message())
let logMsg = "\(timestamp) \(self.name) \(function) \(strMsg)"
let data = "[\(level.rawValue)] \(logMsg)\n".data(using: .utf8) ?? conversionError
self.fileHandle.write(data)
if self.fileHandle.offsetInFile >= maxFileSize {
self.archiveLogFile()
}
}
}
fileprivate func archiveLogFile() {
self.fileHandle.closeFile()
do {
let fileTimestamp = self.fileDateFormatter.string(from: Date())
let fileName = self.fileUrl.deletingPathExtension().lastPathComponent
let archiveFileName = "\(fileName)-\(fileTimestamp).\(self.fileUrl.pathExtension)"
let archiveFileUrl = self.fileUrl
.deletingLastPathComponent().appendingPathComponent(archiveFileName)
try fileManager.moveItem(at: self.fileUrl, to: archiveFileUrl)
} catch let error as NSError {
NSLog("[ERROR] Could not archive log file: \(error)")
}
self.setupFileHandle(at: self.fileUrl)
}
fileprivate func string<T>(from obj: T) -> String {
switch obj {
case let str as String: return str
case let convertible as CustomStringConvertible: return convertible.description
case let convertible as CustomDebugStringConvertible: return convertible.debugDescription
default: return String(describing: obj)
}
}
fileprivate let fileUrl: URL
fileprivate var fileHandle = FileHandle.standardOutput
fileprivate let logDateFormatter = DateFormatter()
fileprivate let fileDateFormatter = DateFormatter()
fileprivate let queue: DispatchQueue
}
fileprivate let conversionError = "[ERROR] Could not convert log msg to Data!".data(using: .utf8)!
fileprivate let fileManager = FileManager.default
fileprivate let maxFileSize: UInt64 = 1 * 1024 * 1024

View File

@ -31,6 +31,8 @@ typedef NS_ENUM(NSUInteger, CursorModeShape) {
CursorModeShapeCount = 18,
};
extern NSString * __nonnull cursorModeShapeName(CursorModeShape mode);
typedef NS_ENUM(NSUInteger, FontTrait) {
FontTraitNone = 0,
FontTraitItalic = (1 << 0),

View File

@ -0,0 +1,27 @@
@import Foundation;
#import "NeoVimUiBridgeProtocol.h"
extern NSString * __nonnull cursorModeShapeName(CursorModeShape mode) {
switch (mode) {
case CursorModeShapeNormal: return @"Normal";
case CursorModeShapeVisual: return @"Visual";
case CursorModeShapeInsert: return @"Insert";
case CursorModeShapeReplace: return @"Replace";
case CursorModeShapeCmdline: return @"Cmdline";
case CursorModeShapeCmdlineInsert: return @"CmdlineInsert";
case CursorModeShapeCmdlineReplace: return @"CmdlineReplace";
case CursorModeShapeOperatorPending: return @"OperatorPending";
case CursorModeShapeVisualExclusive: return @"VisualExclusive";
case CursorModeShapeOnCmdline: return @"OnCmdline";
case CursorModeShapeOnStatusLine: return @"OnStatusLine";
case CursorModeShapeDraggingStatusLine: return @"DraggingStatusLine";
case CursorModeShapeOnVerticalSepLine: return @"OnVerticalSepLine";
case CursorModeShapeDraggingVerticalSepLine: return @"DraggingVerticalSepLine";
case CursorModeShapeMore: return @"More";
case CursorModeShapeMoreLastLine: return @"MoreLastLine";
case CursorModeShapeShowingMatchingParen: return @"ShowingMatchingParen";
case CursorModeShapeTermFocus: return @"TermFocus";
case CursorModeShapeCount: return @"Count";
default: return @"NON_EXISTING_MODE";
}
}

View File

@ -0,0 +1,161 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Cocoa
extension NeoVimView {
public func syncNeoVimWithBounds() {
self.resizeNeoVimUi(to: self.bounds.size)
}
public func enterResizeMode() {
self.currentlyResizing = true
self.needsDisplay = true
}
public func exitResizeMode() {
self.currentlyResizing = false
self.needsDisplay = true
self.resizeNeoVimUi(to: self.bounds.size)
}
/**
- returns: nil when for exampls a quickfix panel is open.
*/
public func currentBuffer() -> NeoVimBuffer? {
return self.agent.buffers().first { $0.isCurrent }
}
public func allBuffers() -> [NeoVimBuffer] {
return self.agent.tabs().map { $0.allBuffers() }.flatMap { $0 }
}
public func hasDirtyDocs() -> Bool {
return self.agent.hasDirtyDocs()
}
public func isCurrentBufferDirty() -> Bool {
let curBuf = self.currentBuffer()
return curBuf?.isDirty ?? true
}
public func newTab() {
self.exec(command: "tabe")
}
public func `open`(urls: [URL]) {
let tabs = self.agent.tabs()
let buffers = self.allBuffers()
let currentBufferIsTransient = buffers.first { $0.isCurrent }?.isTransient ?? false
urls.enumerated().forEach { (idx, url) in
if buffers.filter({ $0.url == url }).first != nil {
for window in tabs.map({ $0.windows }).flatMap({ $0 }) {
if window.buffer.url == url {
self.agent.select(window)
return
}
}
}
if currentBufferIsTransient {
self.open(url, cmd: "e")
} else {
self.open(url, cmd: "tabe")
}
}
}
public func openInNewTab(urls: [URL]) {
urls.forEach { self.open($0, cmd: "tabe") }
}
public func openInCurrentTab(url: URL) {
self.open(url, cmd: "e")
}
public func openInHorizontalSplit(urls: [URL]) {
urls.forEach { self.open($0, cmd: "sp") }
}
public func openInVerticalSplit(urls: [URL]) {
urls.forEach { self.open($0, cmd: "vsp") }
}
public func select(buffer: NeoVimBuffer) {
for window in self.agent.tabs().map({ $0.windows }).flatMap({ $0 }) {
if window.buffer.handle == buffer.handle {
self.agent.select(window)
return
}
}
}
public func closeCurrentTab() {
self.exec(command: "q")
}
public func saveCurrentTab() {
self.exec(command: "w")
}
public func saveCurrentTab(url: URL) {
let path = url.path
guard let escapedFileName = self.agent.escapedFileName(path) else {
self.logger.fault("Escaped file name returned nil.")
return
}
self.exec(command: "w \(escapedFileName)")
}
public func closeCurrentTabWithoutSaving() {
self.exec(command: "q!")
}
public func closeAllWindows() {
self.exec(command: "qa")
}
public func closeAllWindowsWithoutSaving() {
self.exec(command: "qa!")
}
public func vimOutput(of command: String) -> String {
return self.agent.vimCommandOutput(command) ?? ""
}
public func cursorGo(to position: Position) {
self.agent.cursorGo(toRow: Int32(position.row), column: Int32(position.column))
}
/**
Does the following
- normal mode: `:command<CR>`
- else: `:<Esc>:command<CR>`
We don't use NeoVimAgent.vimCommand because if we do for example "e /some/file"
and its swap file already exists, then NeoVimServer spins and become unresponsive.
*/
fileprivate func exec(command cmd: String) {
switch self.mode {
case .normal:
self.agent.vimInput(":\(cmd)<CR>")
default:
self.agent.vimInput("<Esc>:\(cmd)<CR>")
}
}
fileprivate func `open`(_ url: URL, cmd: String) {
let path = url.path
guard let escapedFileName = self.agent.escapedFileName(path) else {
self.logger.fault("Escaped file name returned nil.")
return
}
self.exec(command: "\(cmd) \(escapedFileName)")
}
}

View File

@ -0,0 +1,299 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Cocoa
extension NeoVimView {
override public func viewDidMoveToWindow() {
self.window?.colorSpace = self.colorSpace
}
override public func draw(_ dirtyUnionRect: NSRect) {
guard self.grid.hasData else {
return
}
if self.inLiveResize || self.currentlyResizing {
NSColor.windowBackgroundColor.set()
dirtyUnionRect.fill()
let boundsSize = self.bounds.size
let emojiSize = self.currentEmoji.size(withAttributes: self.emojiAttrs)
let emojiX = (boundsSize.width - emojiSize.width) / 2
let emojiY = (boundsSize.height - emojiSize.height) / 2
let discreteSize = self.discreteSize(size: boundsSize)
let displayStr = "\(discreteSize.width) × \(discreteSize.height)"
let size = displayStr.size(withAttributes: self.resizeTextAttrs)
let x = (boundsSize.width - size.width) / 2
let y = emojiY - size.height
self.currentEmoji.draw(at: CGPoint(x: emojiX, y: emojiY), withAttributes: self.emojiAttrs)
displayStr.draw(at: CGPoint(x: x, y: y), withAttributes: self.resizeTextAttrs)
return
}
// self.logger.debug("\(#function): \(dirtyUnionRect)")
let context = NSGraphicsContext.current()!.cgContext
if self.isCurrentlyPinching {
let interpolationQuality = context.interpolationQuality
context.interpolationQuality = .none
let boundsSize = self.bounds.size
let targetSize = CGSize(width: boundsSize.width * self.pinchTargetScale,
height: boundsSize.height * self.pinchTargetScale)
self.pinchBitmap?.draw(in: CGRect(origin: self.bounds.origin, size: targetSize),
from: CGRect.zero,
operation: .sourceOver,
fraction: 1,
respectFlipped: true,
hints: nil)
context.interpolationQuality = interpolationQuality
return
}
// When both anti-aliasing and font smoothing is turned on, then the "Use LCD font smoothing
// when available" setting is used to render texts,
// cf. chapter 11 from "Programming with Quartz".
context.setShouldSmoothFonts(true);
context.textMatrix = CGAffineTransform.identity;
context.setTextDrawingMode(.fill);
let dirtyRects = self.rectsBeingDrawn()
// self.logger.debug("\(dirtyRects)")
self.rowRunIntersecting(rects: dirtyRects).forEach { self.draw(rowRun: $0, context: context) }
self.drawCursor(context: context)
}
func randomEmoji() -> String {
let idx = Int(arc4random_uniform(UInt32(NeoVimView.emojis.count)))
guard let scalar = UnicodeScalar(NeoVimView.emojis[idx]) else {
return "😎"
}
return String(scalar)
}
fileprivate func draw(rowRun rowFrag: RowRun, context: CGContext) {
// For background drawing we don't filter out the put(0, 0)s:
// in some cases only the put(0, 0)-cells should be redrawn.
// => FIXME: probably we have to consider this also when drawing further down,
// ie when the range starts with '0'...
self.drawBackground(
positions: rowFrag.range.map { self.pointInViewFor(row: rowFrag.row, column: $0) },
background: rowFrag.attrs.background
)
let positions = rowFrag.range
// filter out the put(0, 0)s (after a wide character)
.filter { self.grid.cells[rowFrag.row][$0].string.characters.count > 0 }
.map { self.pointInViewFor(row: rowFrag.row, column: $0) }
if positions.isEmpty {
return
}
let string = self.grid.cells[rowFrag.row][rowFrag.range].reduce("") { $0 + $1.string }
let offset = self.drawer.baselineOffset
let glyphPositions = positions.map { CGPoint(x: $0.x, y: $0.y + offset) }
self.drawer.draw(
string,
positions: UnsafeMutablePointer(mutating: glyphPositions), positionsCount: positions.count,
highlightAttrs: rowFrag.attrs,
context: context
)
}
fileprivate func cursorRegion() -> Region {
let cursorPosition: Position
if self.mode == .cmdline
|| self.mode == .cmdlineInsert
|| self.mode == .cmdlineReplace {
cursorPosition = self.grid.putPosition
} else {
cursorPosition = self.grid.screenCursor
}
let saneRow = max(0, min(cursorPosition.row, self.grid.size.height - 1))
let saneColumn = max(0, min(cursorPosition.column, self.grid.size.width - 1))
var cursorRegion = Region(top: saneRow, bottom: saneRow, left: saneColumn, right: saneColumn)
if self.grid.isNextCellEmpty(cursorPosition) {
cursorRegion = Region(top: cursorPosition.row,
bottom: cursorPosition.row,
left: cursorPosition.column,
right: min(self.grid.size.width - 1, cursorPosition.column + 1))
}
return cursorRegion
}
fileprivate func drawCursor(context: CGContext) {
let cursorRegion = self.cursorRegion()
let cursorRow = cursorRegion.top
let cursorColumnStart = cursorRegion.left
if self.mode == .insert {
ColorUtils.colorIgnoringAlpha(self.grid.foreground).withAlphaComponent(0.75).set()
var cursorRect = self.cellRectFor(row: cursorRow, column: cursorColumnStart)
cursorRect.size.width = 2
cursorRect.fill()
return
}
// FIXME: for now do some rudimentary cursor drawing
let attrsAtCursor = self.grid.cells[cursorRow][cursorColumnStart].attrs
let attrs = CellAttributes(fontTrait: attrsAtCursor.fontTrait,
foreground: self.grid.background,
background: self.grid.foreground,
special: self.grid.special)
// FIXME: take ligatures into account (is it a good idea to do this?)
let rowRun = RowRun(row: cursorRegion.top, range: cursorRegion.columnRange, attrs: attrs)
self.draw(rowRun: rowRun, context: context)
}
fileprivate func drawBackground(positions: [CGPoint], background: Int) {
ColorUtils.colorIgnoringAlpha(background).set()
// To use random color use the following
// NSColor(calibratedRed: CGFloat(drand48()),
// green: CGFloat(drand48()),
// blue: CGFloat(drand48()),
// alpha: 1.0).set()
let backgroundRect = CGRect(
x: positions[0].x, y: positions[0].y,
width: CGFloat(positions.count) * self.cellSize.width, height: self.cellSize.height
)
backgroundRect.fill()
}
fileprivate func rowRunIntersecting(rects: [CGRect]) -> [RowRun] {
return rects
.map { rect -> (CountableClosedRange<Int>, CountableClosedRange<Int>) in
// Get all Regions that intersects with the given rects.
// There can be overlaps between the Regions, but for the time being we ignore them;
// probably not necessary to optimize them away.
let region = self.regionFor(rect: rect)
return (region.rowRange, region.columnRange)
}
// All RowRuns for all Regions grouped by their row range.
.map { self.rowRunsFor(rowRange: $0, columnRange: $1) }
// Flattened RowRuns for all Regions.
.flatMap { $0 }
}
fileprivate func rowRunsFor(rowRange: CountableClosedRange<Int>,
columnRange: CountableClosedRange<Int>) -> [RowRun] {
return rowRange
.map { (row) -> [RowRun] in
let rowCells = self.grid.cells[row]
let startIdx = columnRange.lowerBound
var result = [RowRun(row: row, range: startIdx...startIdx, attrs: rowCells[startIdx].attrs)]
columnRange.forEach { idx in
if rowCells[idx].attrs == result.last!.attrs {
let last = result.popLast()!
result.append(RowRun(row: row, range: last.range.lowerBound...idx, attrs: last.attrs))
} else {
result.append(RowRun(row: row, range: idx...idx, attrs: rowCells[idx].attrs))
}
}
return result // All RowRuns for a row in a Region.
} // All RowRuns for all rows in a Region grouped by row.
.flatMap { $0 } // Flattened RowRuns for a Region.
}
fileprivate func regionFor(rect: CGRect) -> Region {
let cellWidth = self.cellSize.width
let cellHeight = self.cellSize.height
let rowStart = max(
Int(floor(
(self.bounds.height - self.yOffset - (rect.origin.y + rect.size.height)) / cellHeight)
), 0
)
let rowEnd = min(
Int(ceil((self.bounds.height - self.yOffset - rect.origin.y) / cellHeight)) - 1,
self.grid.size.height - 1
)
let columnStart = max(
Int(floor((rect.origin.x - self.xOffset) / cellWidth)), 0
)
let columnEnd = min(
Int(ceil((rect.origin.x - self.xOffset + rect.size.width) / cellWidth)) - 1,
self.grid.size.width - 1
)
return Region(top: rowStart, bottom: rowEnd, left: columnStart, right: columnEnd)
}
fileprivate func pointInViewFor(position: Position) -> CGPoint {
return self.pointInViewFor(row: position.row, column: position.column)
}
fileprivate func pointInViewFor(row: Int, column: Int) -> CGPoint {
return CGPoint(
x: self.xOffset + CGFloat(column) * self.cellSize.width,
y: self.bounds.size.height - self.yOffset - CGFloat(row) * self.cellSize.height
- self.cellSize.height
)
}
func cellRectFor(row: Int, column: Int) -> CGRect {
return CGRect(origin: self.pointInViewFor(row: row, column: column), size: self.cellSize)
}
func regionRectFor(region: Region) -> CGRect {
let top = CGFloat(region.top)
let bottom = CGFloat(region.bottom)
let left = CGFloat(region.left)
let right = CGFloat(region.right)
let width = right - left + 1
let height = bottom - top + 1
let cellWidth = self.cellSize.width
let cellHeight = self.cellSize.height
return CGRect(
x: self.xOffset + left * cellWidth,
y: self.bounds.size.height - self.yOffset - top * cellHeight - height * cellHeight,
width: width * cellWidth,
height: height * cellHeight
)
}
func wrapNamedKeys(_ string: String) -> String {
return "<\(string)>"
}
func vimPlainString(_ string: String) -> String {
return string.replacingOccurrences(of: "<", with: self.wrapNamedKeys("lt"))
}
func updateFontMetaData(_ newFont: NSFont) {
self.drawer.font = newFont
self.cellSize = self.drawer.cellSize
self.descent = self.drawer.descent
self.leading = self.drawer.leading
self.resizeNeoVimUi(to: self.bounds.size)
}
}

View File

@ -0,0 +1,232 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Cocoa
extension NeoVimView {
override public func keyDown(with event: NSEvent) {
self.keyDownDone = false
let context = NSTextInputContext.current()!
let cocoaHandledEvent = context.handleEvent(event)
if self.keyDownDone && cocoaHandledEvent {
return
}
// self.logger.debug("\(#function): \(event)")
let modifierFlags = event.modifierFlags
let capslock = modifierFlags.contains(.capsLock)
let shift = modifierFlags.contains(.shift)
let chars = event.characters!
let charsIgnoringModifiers = shift || capslock
? event.charactersIgnoringModifiers!.lowercased()
: event.charactersIgnoringModifiers!
if KeyUtils.isSpecial(key: charsIgnoringModifiers) {
if let vimModifiers = self.vimModifierFlags(modifierFlags) {
self.agent.vimInput(
self.wrapNamedKeys(vimModifiers + KeyUtils.namedKeyFrom(key: charsIgnoringModifiers))
)
} else {
self.agent.vimInput(self.wrapNamedKeys(KeyUtils.namedKeyFrom(key: charsIgnoringModifiers)))
}
} else {
if let vimModifiers = self.vimModifierFlags(modifierFlags) {
self.agent.vimInput(self.wrapNamedKeys(vimModifiers + charsIgnoringModifiers))
} else {
self.agent.vimInput(self.vimPlainString(chars))
}
}
self.keyDownDone = true
}
public func insertText(_ aString: Any, replacementRange: NSRange) {
// self.logger.debug("\(#function): \(replacementRange): '\(aString)'")
switch aString {
case let string as String:
self.agent.vimInput(self.vimPlainString(string))
case let attributedString as NSAttributedString:
self.agent.vimInput(self.vimPlainString(attributedString.string))
default:
break;
}
// unmarkText()
self.lastMarkedText = self.markedText
self.markedText = nil
self.markedPosition = Position.null
self.keyDownDone = true
}
public override func doCommand(by aSelector: Selector) {
// self.logger.debug("\(#function): \(aSelector)");
// FIXME: handle when -> delete
if self.responds(to: aSelector) {
Swift.print("\(#function): calling \(aSelector)")
self.perform(aSelector, with: self)
self.keyDownDone = true
return
}
// self.logger.debug("\(#function): "\(aSelector) not implemented, forwarding input to vim")
self.keyDownDone = false
}
public func setMarkedText(_ aString: Any, selectedRange: NSRange, replacementRange: NSRange) {
if self.markedText == nil {
self.markedPosition = self.grid.putPosition
}
// eg -> hanja popup, cf comment for self.lastMarkedText
if replacementRange.length > 0 {
self.agent.deleteCharacters(replacementRange.length)
}
switch aString {
case let string as String:
self.markedText = string
case let attributedString as NSAttributedString:
self.markedText = attributedString.string
default:
self.markedText = String(describing: aString) // should not occur
}
// self.logger.debug("\(#function): \(self.markedText), \(selectedRange), \(replacementRange)")
self.agent.vimInputMarkedText(self.markedText!)
self.keyDownDone = true
}
public func unmarkText() {
// self.logger.debug("\(#function): ")
self.markedText = nil
self.markedPosition = Position.null
self.keyDownDone = true
// TODO: necessary?
self.markForRender(row: self.grid.putPosition.row, column: self.grid.putPosition.column)
}
/// Return the current selection (or the position of the cursor with empty-length range).
/// For example when you enter "Cmd-Ctrl-Return" you'll get the Emoji-popup at the rect
/// by firstRectForCharacterRange(actualRange:) where the first range is the result of this method
public func selectedRange() -> NSRange {
// When the app starts and the Hangul input method is selected,
// this method gets called very early...
guard self.grid.hasData else {
// self.logger.debug("\(#function): not found")
return NSRange(location: NSNotFound, length: 0)
}
let result = NSRange(location: self.grid.singleIndexFrom(self.grid.putPosition), length: 0)
// self.logger.debug("\(#function): \(result)")
return result
}
public func markedRange() -> NSRange {
// FIXME: do we have to handle positions at the column borders?
if let markedText = self.markedText {
let result = NSRange(location: self.grid.singleIndexFrom(self.markedPosition),
length: markedText.characters.count)
// self.logger.debug("\(#function): \(result)")
return result
}
self.logger.debug("\(#function): returning empty range")
return NSRange(location: NSNotFound, length: 0)
}
public func hasMarkedText() -> Bool {
// self.logger.debug("\(#function)")
return self.markedText != nil
}
// FIXME: take into account the "return nil"-case
// FIXME: just fix me, PLEASE...
public func attributedSubstring(forProposedRange aRange: NSRange,
actualRange: NSRangePointer?) -> NSAttributedString? {
// self.logger.debug("\(#function): \(aRange), \(actualRange[0])")
if aRange.location == NSNotFound {
// self.logger.debug("\(#function): range not found: returning nil")
return nil
}
guard let lastMarkedText = self.lastMarkedText else {
// self.logger.debug("\(#function): no last marked text: returning nil")
return nil
}
// we only support last marked text, thus fill dummy characters when Cocoa asks for more
// characters than marked...
let fillCount = aRange.length - lastMarkedText.characters.count
guard fillCount >= 0 else {
return nil
}
let fillChars = Array(0..<fillCount).reduce("") { (result, _) in return result + " " }
// self.logger.debug("\(#function): \(aRange), \(actualRange[0]): \(fillChars + lastMarkedText)")
return NSAttributedString(string: fillChars + lastMarkedText)
}
public func validAttributesForMarkedText() -> [String] {
return []
}
public func firstRect(forCharacterRange aRange: NSRange, actualRange: NSRangePointer?) -> NSRect {
let position = self.grid.positionFromSingleIndex(aRange.location)
// self.logger.debug("\(#function): \(aRange),\(actualRange[0]) -> " +
// "\(position.row):\(position.column)")
let resultInSelf = self.cellRectFor(row: position.row, column: position.column)
let result = self.window?.convertToScreen(self.convert(resultInSelf, to: nil))
return result!
}
public func characterIndex(for aPoint: NSPoint) -> Int {
// self.logger.debug("\(#function): \(aPoint)")
return 1
}
func vimModifierFlags(_ modifierFlags: NSEventModifierFlags) -> String? {
var result = ""
let control = modifierFlags.contains(.control)
let option = modifierFlags.contains(.option)
let command = modifierFlags.contains(.command)
let shift = modifierFlags.contains(.shift)
if control {
result += "C-"
}
if option {
result += "M-"
}
if command {
result += "D-"
}
if shift {
result += "S-"
}
if result.characters.count > 0 {
return result
}
return nil
}
}

View File

@ -0,0 +1,161 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Cocoa
// MARK: - NSUserInterfaceValidationsProtocol
extension NeoVimView {
public func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
let canUndoOrRedo = self.mode == .insert || self.mode == .replace
|| self.mode == .normal || self.mode == .visual
let canCopyOrCut = self.mode == .normal || self.mode == .visual
let canPaste = self.pasteboard.string(forType: NSPasteboardTypeString) != nil
let canDelete = self.mode == .visual || self.mode == .normal
let canSelectAll = self.mode == .insert || self.mode == .replace
|| self.mode == .normal || self.mode == .visual
guard let action = item.action else {
return true
}
switch action {
case #selector(undo(_:)), #selector(redo(_:)):
return canUndoOrRedo
case #selector(copy(_:)), #selector(cut(_:)):
return canCopyOrCut
case #selector(paste(_:)):
return canPaste
case #selector(delete(_:)):
return canDelete
case #selector(selectAll(_:)):
return canSelectAll
default:
return true
}
}
}
// MARK: - Edit Menu Items
extension NeoVimView {
@IBAction func undo(_ sender: AnyObject?) {
switch self.mode {
case .insert, .replace:
self.agent.vimInput("<Esc>ui")
case .normal, .visual:
self.agent.vimInput("u")
default:
return
}
}
@IBAction func redo(_ sender: AnyObject?) {
switch self.mode {
case .insert, .replace:
self.agent.vimInput("<Esc><C-r>i")
case .normal, .visual:
self.agent.vimInput("<C-r>")
default:
return
}
}
@IBAction func cut(_ sender: AnyObject?) {
switch self.mode {
case .visual, .normal:
self.agent.vimInput("\"+d")
default:
return
}
}
@IBAction func copy(_ sender: AnyObject?) {
switch self.mode {
case .visual, .normal:
self.agent.vimInput("\"+y")
default:
return
}
}
@IBAction func paste(_ sender: AnyObject?) {
guard let content = self.pasteboard.string(forType: NSPasteboardTypeString) else {
return
}
if self.mode == .cmdline || self.mode == .cmdlineInsert || self.mode == .cmdlineReplace
|| self.mode == .replace
|| self.mode == .termFocus {
self.agent.vimInput(self.vimPlainString(content))
return
}
guard let curPasteMode = self.agent.boolOption("paste") else {
self.ipcBecameInvalid("Reason: 'set paste' failed")
return
}
let pasteModeSet: Bool
if curPasteMode == false {
self.agent.setBoolOption("paste", to: true)
pasteModeSet = true
} else {
pasteModeSet = false
}
let resetPasteModeCmd = pasteModeSet ? ":set nopaste<CR>" : ""
switch self.mode {
case .insert:
self.agent.vimInput("<ESC>\"+p\(resetPasteModeCmd)a")
case .normal, .visual:
self.agent.vimInput("\"+p\(resetPasteModeCmd)")
default:
return
}
}
@IBAction func delete(_ sender: AnyObject?) {
switch self.mode {
case .normal, .visual:
self.agent.vimInput("x")
default:
return
}
}
@IBAction public override func selectAll(_ sender: Any?) {
switch self.mode {
case .insert, .visual:
self.agent.vimInput("<Esc>ggVG")
default:
self.agent.vimInput("ggVG")
}
}
}
// MARK: - Font Menu Items
extension NeoVimView {
@IBAction func resetFontSize(_ sender: Any?) {
self.font = self._font
}
@IBAction func makeFontBigger(_ sender: Any?) {
let curFont = self.drawer.font
let font = self.fontManager.convert(curFont,
toSize: min(curFont.pointSize + 1, NeoVimView.maxFontSize))
self.updateFontMetaData(font)
}
@IBAction func makeFontSmaller(_ sender: Any?) {
let curFont = self.drawer.font
let font = self.fontManager.convert(curFont,
toSize: max(curFont.pointSize - 1, NeoVimView.minFontSize))
self.updateFontMetaData(font)
}
}

View File

@ -0,0 +1,222 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Cocoa
extension NeoVimView {
override public func mouseDown(with event: NSEvent) {
self.mouse(event: event, vimName: "LeftMouse")
}
override public func mouseUp(with event: NSEvent) {
self.mouse(event: event, vimName: "LeftRelease")
}
override public func mouseDragged(with event: NSEvent) {
self.mouse(event: event, vimName: "LeftDrag")
}
override public func scrollWheel(with event: NSEvent) {
let (deltaX, deltaY) = (event.scrollingDeltaX, event.scrollingDeltaY)
if deltaX == 0 && deltaY == 0 {
return
}
let isTrackpad = event.hasPreciseScrollingDeltas
let cellPosition = self.cellPositionFor(event: event)
let (vimInputX, vimInputY) = self.vimScrollInputFor(deltaX: deltaX, deltaY: deltaY,
modifierFlags: event.modifierFlags,
cellPosition: cellPosition)
// We patched neovim such that it scrolls only 1 line for each scroll input.
// The default is 3 and for mouse scrolling we restore the original behavior.
if isTrackpad == false {
(0..<3).forEach { _ in
self.agent.vimInput(vimInputX)
self.agent.vimInput(vimInputY)
}
return
}
let (absDeltaX, absDeltaY) = (abs(deltaX), abs(deltaY))
// The absolute delta values can get very very big when you use two finger scrolling
// on the trackpad: Cap them using heuristic values...
let numX = deltaX != 0 ?
max(1, min(Int(absDeltaX / self.scrollLimiterX), self.maxScrollDeltaX)) : 0
let numY = deltaY != 0 ?
max(1, min(Int(absDeltaY / self.scrollLimiterY), self.maxScrollDeltaY)) : 0
for i in 0..<max(numX, numY) {
if i < numX {
self.throttleScrollX(absDelta: absDeltaX, vimInput: vimInputX)
}
if i < numY {
self.throttleScrollY(absDelta: absDeltaY, vimInput: vimInputY)
}
}
}
override public func magnify(with event: NSEvent) {
let factor = 1 + event.magnification
let pinchTargetScale = self.pinchTargetScale * factor
let resultingFontSize = round(pinchTargetScale * self.font.pointSize)
if resultingFontSize >= NeoVimView.minFontSize && resultingFontSize <= NeoVimView.maxFontSize {
self.pinchTargetScale = pinchTargetScale
}
switch event.phase {
case NSEventPhase.began:
let pinchImageRep = self.bitmapImageRepForCachingDisplay(in: self.bounds)!
self.cacheDisplay(in: self.bounds, to: pinchImageRep)
self.pinchBitmap = pinchImageRep
self.isCurrentlyPinching = true
self.needsDisplay = true
case NSEventPhase.ended, NSEventPhase.cancelled:
self.isCurrentlyPinching = false
self.updateFontMetaData(self.fontManager.convert(self.font, toSize: resultingFontSize))
self.pinchTargetScale = 1
default:
self.needsDisplay = true
}
}
fileprivate func cellPositionFor(event: NSEvent) -> Position {
let location = self.convert(event.locationInWindow, from: nil)
let row = Int((location.x - self.xOffset) / self.cellSize.width)
let column = Int((self.bounds.size.height - location.y - self.yOffset) / self.cellSize.height)
let cellPosition = Position(row: min(max(0, row), self.grid.size.width - 1),
column: min(max(0, column), self.grid.size.height - 1))
return cellPosition
}
fileprivate func mouse(event: NSEvent, vimName: String) {
let cellPosition = self.cellPositionFor(event: event)
guard self.shouldFireVimInputFor(event: event, newCellPosition: cellPosition) else {
return
}
let vimMouseLocation = self.wrapNamedKeys("\(cellPosition.row),\(cellPosition.column)")
let vimClickCount = self.vimClickCountFrom(event: event)
let result: String
if let vimModifiers = self.vimModifierFlags(event.modifierFlags) {
result = self.wrapNamedKeys("\(vimModifiers)\(vimClickCount)\(vimName)") + vimMouseLocation
} else {
result = self.wrapNamedKeys("\(vimClickCount)\(vimName)") + vimMouseLocation
}
// self.logger.debug("\(#function): \(result)")
self.agent.vimInput(result)
}
fileprivate func shouldFireVimInputFor(event: NSEvent, newCellPosition: Position) -> Bool {
let type = event.type
guard type == .leftMouseDragged || type == .rightMouseDragged
|| type == .otherMouseDragged else {
self.lastClickedCellPosition = newCellPosition
return true
}
if self.lastClickedCellPosition == newCellPosition {
return false
}
self.lastClickedCellPosition = newCellPosition
return true
}
fileprivate func vimClickCountFrom(event: NSEvent) -> String {
let clickCount = event.clickCount
guard 2 <= clickCount && clickCount <= 4 else {
return ""
}
switch event.type {
case .leftMouseDown, .leftMouseUp, .rightMouseDown, .rightMouseUp:
return "\(clickCount)-"
default:
return ""
}
}
fileprivate func vimScrollEventNamesFor(deltaX: CGFloat, deltaY: CGFloat) -> (String, String) {
let typeY: String
if deltaY > 0 {
typeY = "ScrollWheelUp"
} else {
typeY = "ScrollWheelDown"
}
let typeX: String
if deltaX < 0 {
typeX = "ScrollWheelRight"
} else {
typeX = "ScrollWheelLeft"
}
return (typeX, typeY)
}
fileprivate func vimScrollInputFor(deltaX: CGFloat, deltaY: CGFloat,
modifierFlags: NSEventModifierFlags,
cellPosition: Position) -> (String, String) {
let vimMouseLocation = self.wrapNamedKeys("\(cellPosition.row),\(cellPosition.column)")
let (typeX, typeY) = self.vimScrollEventNamesFor(deltaX: deltaX, deltaY: deltaY)
let resultX: String
let resultY: String
if let vimModifiers = self.vimModifierFlags(modifierFlags) {
resultX = self.wrapNamedKeys("\(vimModifiers)\(typeX)") + vimMouseLocation
resultY = self.wrapNamedKeys("\(vimModifiers)\(typeY)") + vimMouseLocation
} else {
resultX = self.wrapNamedKeys("\(typeX)") + vimMouseLocation
resultY = self.wrapNamedKeys("\(typeY)") + vimMouseLocation
}
return (resultX, resultY)
}
fileprivate func throttleScrollX(absDelta absDeltaX: CGFloat, vimInput: String) {
if absDeltaX == 0 {
self.scrollGuardCounterX = self.scrollGuardYield - 1
} else if absDeltaX <= 2 {
// Poor man's throttle for scroll value = 1 or 2
if self.scrollGuardCounterX % self.scrollGuardYield == 0 {
self.agent.vimInput(vimInput)
self.scrollGuardCounterX = 1
} else {
self.scrollGuardCounterX += 1
}
} else {
self.agent.vimInput(vimInput)
}
}
fileprivate func throttleScrollY(absDelta absDeltaY: CGFloat, vimInput: String) {
if absDeltaY == 0 {
self.scrollGuardCounterY = self.scrollGuardYield - 1
} else if absDeltaY <= 2 {
// Poor man's throttle for scroll value = 1 or 2
if self.scrollGuardCounterY % self.scrollGuardYield == 0 {
self.agent.vimInput(vimInput)
self.scrollGuardCounterY = 1
} else {
self.scrollGuardCounterY += 1
}
} else {
self.agent.vimInput(vimInput)
}
}
}

View File

@ -0,0 +1,53 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Cocoa
extension NeoVimView {
override public func setFrameSize(_ newSize: NSSize) {
super.setFrameSize(newSize)
// initial resizing is done when grid has data
guard self.grid.hasData else {
return
}
if self.inLiveResize || self.currentlyResizing {
// TODO: Turn off live resizing for now.
// self.resizeNeoVimUi(to: newSize)
return
}
// There can be cases where the frame is resized not by live resizing,
// eg when the window is resized by window management tools.
// Thus, we make sure that the resize call is made when this happens.
self.resizeNeoVimUi(to: newSize)
}
override public func viewDidEndLiveResize() {
super.viewDidEndLiveResize()
self.resizeNeoVimUi(to: self.bounds.size)
}
func discreteSize(size: CGSize) -> Size {
return Size(width: Int(floor(size.width / self.cellSize.width)),
height: Int(floor(size.height / self.cellSize.height)))
}
func resizeNeoVimUi(to size: CGSize) {
self.currentEmoji = self.randomEmoji()
let discreteSize = self.discreteSize(size: size)
if discreteSize == self.grid.size {
return
}
self.xOffset = floor((size.width - self.cellSize.width * CGFloat(discreteSize.width)) / 2)
self.yOffset = floor((size.height - self.cellSize.height * CGFloat(discreteSize.height)) / 2)
self.agent.resize(toWidth: Int32(discreteSize.width), height: Int32(discreteSize.height))
}
}

View File

@ -0,0 +1,406 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Cocoa
extension NeoVimView {
public func resize(toWidth width: Int, height: Int) {
gui.async {
self.logger.debug("\(width) x \(height)")
self.grid.resize(Size(width: width, height: height))
self.markForRenderWholeView()
}
}
public func clear() {
gui.async {
self.logger.mark()
self.grid.clear()
self.markForRenderWholeView()
}
}
public func eolClear() {
gui.async {
self.logger.mark()
self.grid.eolClear()
let putPosition = self.grid.putPosition
let region = Region(top: putPosition.row,
bottom: putPosition.row,
left: putPosition.column,
right: self.grid.region.right)
self.markForRender(region: region)
}
}
public func gotoPosition(_ position: Position,
screenCursor: Position,
currentPosition: Position) {
gui.async {
self.logger.debug("pos: \(position), screen: \(screenCursor), " +
"current-pos: \(currentPosition)")
self.currentPosition = currentPosition
let curScreenCursor = self.grid.screenCursor
// Because neovim fills blank space with "Space" and when we enter "Space"
// we don't get the puts, thus we have to redraw the put position.
if self.usesLigatures {
self.markForRender(region: self.grid.regionOfWord(at: self.grid.putPosition))
self.markForRender(region: self.grid.regionOfWord(at: curScreenCursor))
self.markForRender(region: self.grid.regionOfWord(at: position))
self.markForRender(region: self.grid.regionOfWord(at: screenCursor))
} else {
self.markForRender(cellPosition: self.grid.putPosition)
// Redraw where the cursor has been till now, ie remove the current cursor.
self.markForRender(cellPosition: curScreenCursor)
if self.grid.isPreviousCellEmpty(curScreenCursor) {
self.markForRender(cellPosition: self.grid.previousCellPosition(curScreenCursor))
}
if self.grid.isNextCellEmpty(curScreenCursor) {
self.markForRender(cellPosition: self.grid.nextCellPosition(curScreenCursor))
}
self.markForRender(cellPosition: position)
self.markForRender(cellPosition: screenCursor)
}
self.grid.goto(position)
self.grid.moveCursor(screenCursor)
}
gui.async {
self.delegate?.cursor(to: currentPosition)
}
}
public func modeChange(_ mode: CursorModeShape) {
gui.async {
self.logger.debug(cursorModeShapeName(mode))
self.mode = mode
}
}
public func setScrollRegionToTop(_ top: Int, bottom: Int, left: Int, right: Int) {
gui.async {
self.logger.debug("\(top):\(bottom):\(left):\(right)")
let region = Region(top: top, bottom: bottom, left: left, right: right)
self.grid.setScrollRegion(region)
}
}
public func scroll(_ count: Int) {
gui.async {
self.logger.debug(count)
self.grid.scroll(count)
self.markForRender(region: self.grid.region)
// Do not send msgs to agent -> neovim in the delegate method. It causes spinning
// when you're opening a file with existing swap file.
self.delegate?.scroll()
}
}
public func highlightSet(_ attrs: CellAttributes) {
gui.async {
self.logger.debug(attrs)
self.grid.attrs = attrs
}
}
public func put(_ string: String, screenCursor: Position) {
gui.async {
self.logger.debug("'\(string)' <- screen: \(screenCursor)")
let curPos = self.grid.putPosition
// self.logger.debug("\(#function): \(curPos) -> \(string)")
self.grid.put(string)
if self.usesLigatures {
if string == " " {
self.markForRender(cellPosition: curPos)
} else {
self.markForRender(region: self.grid.regionOfWord(at: curPos))
}
} else {
self.markForRender(cellPosition: curPos)
}
self.updateCursorWhenPutting(currentPosition: curPos, screenCursor: screenCursor)
}
}
public func putMarkedText(_ markedText: String, screenCursor: Position) {
gui.async {
self.logger.debug("'\(markedText)' <- screen: \(screenCursor)")
let curPos = self.grid.putPosition
self.grid.putMarkedText(markedText)
self.markForRender(position: curPos)
// When the cursor is in the command line, then we need this...
self.markForRender(cellPosition: self.grid.nextCellPosition(curPos))
if markedText.characters.count == 0 {
self.markForRender(position: self.grid.previousCellPosition(curPos))
}
self.updateCursorWhenPutting(currentPosition: curPos, screenCursor: screenCursor)
}
}
public func unmarkRow(_ row: Int, column: Int) {
gui.async {
self.logger.debug("\(row):\(column)")
let position = Position(row: row, column: column)
self.grid.unmarkCell(position)
self.markForRender(position: position)
self.markForRender(screenCursor: self.grid.screenCursor)
}
}
public func flush() {
gui.async {
self.logger.debug("-----------------------------")
}
}
public func updateForeground(_ fg: Int) {
gui.async {
self.logger.debug(ColorUtils.colorIgnoringAlpha(fg))
self.grid.foreground = fg
}
}
public func updateBackground(_ bg: Int) {
gui.async {
self.logger.debug(ColorUtils.colorIgnoringAlpha(bg))
self.grid.background = bg
self.layer?.backgroundColor = ColorUtils.colorIgnoringAlpha(self.grid.background).cgColor
}
}
public func updateSpecial(_ sp: Int) {
gui.async {
self.logger.debug(ColorUtils.colorIgnoringAlpha(sp))
self.grid.special = sp
}
}
public func setTitle(_ title: String) {
gui.async {
self.logger.debug(title)
self.delegate?.set(title: title)
}
}
public func stop() {
gui.async {
self.logger.mark()
self.delegate?.neoVimStopped()
}
self.agent.quit()
}
public func autoCommandEvent(_ event: NeoVimAutoCommandEvent, bufferHandle: Int) {
gui.async {
self.logger.debug("\(neoVimAutoCommandEventName(event)) -> \(bufferHandle)")
if event == .BUFWINENTER || event == .BUFWINLEAVE {
self.bufferListChanged()
}
if event == .TABENTER {
self.tabChanged()
}
if event == .DIRCHANGED {
self.cwdChanged()
}
if event == .BUFREADPOST || event == .BUFWRITEPOST {
self.currentBufferChanged(bufferHandle)
}
}
}
public func ipcBecameInvalid(_ reason: String) {
gui.async {
self.logger.debug(reason)
if self.agent.neoVimIsQuitting {
return
}
self.delegate?.ipcBecameInvalid(reason: reason)
self.logger.fault("force-quitting")
self.agent.quit()
}
}
}
// MARK: - Simple
extension NeoVimView {
public func bell() {
gui.async {
self.logger.mark()
NSBeep()
}
}
public func setDirtyStatus(_ dirty: Bool) {
gui.async {
self.logger.debug(dirty)
self.delegate?.set(dirtyStatus: dirty)
}
}
public func updateMenu() {
gui.async {
self.logger.mark()
}
}
public func busyStart() {
gui.async {
self.logger.mark()
}
}
public func busyStop() {
gui.async {
self.logger.mark()
}
}
public func mouseOn() {
gui.async {
self.logger.mark()
}
}
public func mouseOff() {
gui.async {
self.logger.mark()
}
}
public func visualBell() {
gui.async {
self.logger.mark()
}
}
public func suspend() {
gui.async {
self.logger.mark()
}
}
public func setIcon(_ icon: String) {
gui.async {
self.logger.debug(icon)
}
}
}
extension NeoVimView {
func markForRender(cellPosition position: Position) {
self.markForRender(position: position)
if self.grid.isCellEmpty(position) {
self.markForRender(position: self.grid.previousCellPosition(position))
}
if self.grid.isNextCellEmpty(position) {
self.markForRender(position: self.grid.nextCellPosition(position))
}
}
func markForRender(position: Position) {
self.markForRender(row: position.row, column: position.column)
}
func markForRender(screenCursor position: Position) {
self.markForRender(position: position)
if self.grid.isNextCellEmpty(position) {
self.markForRender(position: self.grid.nextCellPosition(position))
}
}
func markForRenderWholeView() {
self.needsDisplay = true
}
func markForRender(region: Region) {
self.setNeedsDisplay(self.regionRectFor(region: region))
}
func markForRender(row: Int, column: Int) {
self.setNeedsDisplay(self.cellRectFor(row: row, column: column))
}
}
extension NeoVimView {
fileprivate func currentBufferChanged(_ handle: Int) {
guard let currentBuffer = self.currentBuffer() else {
return
}
guard currentBuffer.handle == handle else {
return
}
self.delegate?.currentBufferChanged(currentBuffer)
}
fileprivate func tabChanged() {
self.delegate?.tabChanged()
}
fileprivate func cwdChanged() {
self.delegate?.cwdChanged()
}
fileprivate func bufferListChanged() {
self.delegate?.bufferListChanged()
}
fileprivate func updateCursorWhenPutting(currentPosition curPos: Position,
screenCursor: Position) {
if self.mode == .cmdline {
// When the cursor is in the command line, then we need this...
self.markForRender(cellPosition: self.grid.previousCellPosition(curPos))
self.markForRender(cellPosition: self.grid.nextCellPosition(curPos))
self.markForRender(screenCursor: self.grid.screenCursor)
}
self.markForRender(screenCursor: screenCursor)
self.markForRender(cellPosition: self.grid.screenCursor)
self.grid.moveCursor(screenCursor)
}
}
fileprivate let gui = DispatchQueue.main

File diff suppressed because it is too large Load Diff

View File

@ -48,6 +48,7 @@
1929B71381946860626E5224 /* FileBrowserReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BDC8F5D48578A90236E9 /* FileBrowserReducer.swift */; };
1929B71610FF1DC6E459BA49 /* PreviewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B8EF9A9F5ACC175452BD /* PreviewUtils.swift */; };
1929B728262BAA14FC93F6AC /* NeoVimView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BF00B466B40629C2AABE /* NeoVimView.swift */; };
1929B7993C8DB7F59447DF5F /* NeoVimView+Draw.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B19207FBC2EBDF1B88F3 /* NeoVimView+Draw.swift */; };
1929B8E90A1378E494D481E7 /* PrefUtilsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B7BB3E4B3DC96284B686 /* PrefUtilsTest.swift */; };
1929B8EE07CA6D47855E7332 /* ImageAndTextTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B015804B46789F719C01 /* ImageAndTextTableCell.swift */; };
1929B8FB248D71BF88A35761 /* PreviewTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B6C6C7792B05164B0216 /* PreviewTool.swift */; };
@ -59,14 +60,20 @@
1929BAA19A6BEE73963BD4A8 /* NeoVimWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B868F11F398A159EF554 /* NeoVimWindow.swift */; };
1929BAAD7336FDFF1F78E749 /* ScorerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BF69B01107F358CF7EAD /* ScorerTest.swift */; };
1929BAE4900D72A7877741B1 /* PrefWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BE168F31344B69E61B62 /* PrefWindow.swift */; };
1929BAE6B93067FD52DBE892 /* NeoVimView+Mouse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B4A088958A7B4FF152B7 /* NeoVimView+Mouse.swift */; };
1929BAFF1E011321D3186EE6 /* UiRoot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BD4149D5A25C82064DD8 /* UiRoot.swift */; };
1929BB0A840D93D1A13C25FD /* NeoVimView+MenuItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B4F65149D3C3E326DA65 /* NeoVimView+MenuItems.swift */; };
1929BB38525ECCEE8E39288A /* NeoVimView+Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BDF3167E15E7F3349798 /* NeoVimView+Key.swift */; };
1929BB4A9B2FA42A64CCCC76 /* MainWindowReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BD83A13BF133741766CC /* MainWindowReducer.swift */; };
1929BC9EF45463113EBF3BCB /* NeoVimView+UiBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B2AD3F9375F57F21AD58 /* NeoVimView+UiBridge.swift */; };
1929BCC7908DD899999B70BE /* AppearancePrefReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BED01F5D94BFCA4CF80F /* AppearancePrefReducer.swift */; };
1929BCC9D3604933DFF07E2E /* FileBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BA5C7099CDEB04B76BA4 /* FileBrowser.swift */; };
1929BCF7F7B9CC5499A3F506 /* AdvancedPrefReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B7039C5689CE45F53888 /* AdvancedPrefReducer.swift */; };
1929BD294A33873BAE2804C0 /* NeoVimView+Resize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BD05CA498FC3C229455E /* NeoVimView+Resize.swift */; };
1929BD2F41D93ADFF43C1C98 /* NetUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 1929B02440BC99C42F9EBD45 /* NetUtils.m */; };
1929BD3878A3A47B8D685CD2 /* AppDelegateReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B7A68B7109CEFAF105E8 /* AppDelegateReducer.swift */; };
1929BD3F9E609BFADB27584B /* Scorer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B9D510177918080BE39B /* Scorer.swift */; };
1929BDD4BFA4175D0A1B0BC3 /* NeoVimView+Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BD8F08E21E3ECE9F84AB /* NeoVimView+Api.swift */; };
1929BDFDBDA7180D02ACB37E /* RxSwiftCommonsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B6C215ACCBE12672A8D7 /* RxSwiftCommonsTest.swift */; };
1929BE0DAEE9664C5BCFA211 /* States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB6608B4F0E037CA0F4C /* States.swift */; };
1929BEAE0592096BC1191B67 /* PrefPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B07A4A9209C88380E015 /* PrefPane.swift */; };
@ -147,6 +154,9 @@
4B8AC0441DBCB3A2007CCC9B /* NeoVimObjectsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8AC0431DBCB3A1007CCC9B /* NeoVimObjectsExtensions.swift */; };
4B91FFF41DEB772200447068 /* CocoaFontAwesome.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4B337FBA1DEB76F20020ADD2 /* CocoaFontAwesome.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
4B91FFF61DEB772B00447068 /* CocoaFontAwesome.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4B337FBA1DEB76F20020ADD2 /* CocoaFontAwesome.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
4B96384C1ED9793B001C556F /* NeoVimAutoCommandEvent.generated.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B96384B1ED9793B001C556F /* NeoVimAutoCommandEvent.generated.m */; };
4B96384E1ED9797F001C556F /* NeoVimUiBridgeProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B96384D1ED9797F001C556F /* NeoVimUiBridgeProtocol.m */; };
4B9638501ED979BD001C556F /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B96384F1ED979BD001C556F /* Logger.swift */; };
4B96FB3B1EBBC56F00E4E164 /* FileItemIgnorePattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B69499B2569793350CEC /* FileItemIgnorePattern.swift */; };
4B96FB3C1EBBC56F00E4E164 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BA8AC40B901B20F20B71 /* FileUtils.swift */; };
4B96FB3D1EBBC56F00E4E164 /* Matcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BEEB33113B0E33C3830F /* Matcher.swift */; };
@ -362,13 +372,17 @@
1929B12CE56A9B36980288A4 /* OpenQuicklyReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenQuicklyReducer.swift; sourceTree = "<group>"; };
1929B1558455B3A74D93EF2A /* OpenQuicklyFileViewRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenQuicklyFileViewRow.swift; sourceTree = "<group>"; };
1929B15B7EDC9B0F40E5E95C /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
1929B19207FBC2EBDF1B88F3 /* NeoVimView+Draw.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NeoVimView+Draw.swift"; sourceTree = "<group>"; };
1929B1A51F076E088EF4CCA4 /* server_globals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = server_globals.h; sourceTree = "<group>"; };
1929B1DC584C89C477E83FA2 /* HttpServerService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpServerService.swift; sourceTree = "<group>"; };
1929B2AD3F9375F57F21AD58 /* NeoVimView+UiBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NeoVimView+UiBridge.swift"; sourceTree = "<group>"; };
1929B34FC23D805A8B29E8F7 /* Context.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Context.swift; sourceTree = "<group>"; };
1929B364460D86F17E80943C /* PrefService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefService.swift; sourceTree = "<group>"; };
1929B365A6434354B568B04F /* FileMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileMonitor.swift; sourceTree = "<group>"; };
1929B457B9D0FA4D21F3751E /* UiRootReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UiRootReducer.swift; sourceTree = "<group>"; };
1929B49E6924847AD085C8C9 /* PrefWindowReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefWindowReducer.swift; sourceTree = "<group>"; };
1929B4A088958A7B4FF152B7 /* NeoVimView+Mouse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NeoVimView+Mouse.swift"; sourceTree = "<group>"; };
1929B4F65149D3C3E326DA65 /* NeoVimView+MenuItems.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NeoVimView+MenuItems.swift"; sourceTree = "<group>"; };
1929B5046239709E33516F5C /* Pref128ToCurrentConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pref128ToCurrentConverter.swift; sourceTree = "<group>"; };
1929B56C8ED31834BA9D8543 /* FileItemUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileItemUtils.swift; sourceTree = "<group>"; };
1929B5C3F2F1CA4113DABFFD /* CocoaCategories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CocoaCategories.m; sourceTree = "<group>"; };
@ -409,10 +423,13 @@
1929BBC84557C8351EC6183E /* FileItemIgnorePatternTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileItemIgnorePatternTest.swift; sourceTree = "<group>"; };
1929BBE0A534F2F6009D31BE /* AdvencedPref.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvencedPref.swift; sourceTree = "<group>"; };
1929BCE3E156C06EDF1F2806 /* FileOutlineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileOutlineView.swift; sourceTree = "<group>"; };
1929BD05CA498FC3C229455E /* NeoVimView+Resize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NeoVimView+Resize.swift"; sourceTree = "<group>"; };
1929BD4149D5A25C82064DD8 /* UiRoot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UiRoot.swift; sourceTree = "<group>"; };
1929BD83A13BF133741766CC /* MainWindowReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowReducer.swift; sourceTree = "<group>"; };
1929BD8CBADC191CF8C85309 /* MainWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; };
1929BD8F08E21E3ECE9F84AB /* NeoVimView+Api.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NeoVimView+Api.swift"; sourceTree = "<group>"; };
1929BDC8F5D48578A90236E9 /* FileBrowserReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileBrowserReducer.swift; sourceTree = "<group>"; };
1929BDF3167E15E7F3349798 /* NeoVimView+Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NeoVimView+Key.swift"; sourceTree = "<group>"; };
1929BDF9EBAF1D9D44399045 /* ScoredFileItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScoredFileItem.swift; sourceTree = "<group>"; };
1929BE168F31344B69E61B62 /* PrefWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefWindow.swift; sourceTree = "<group>"; };
1929BE37AA2843779CAFA76F /* PreviewReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewReducer.swift; sourceTree = "<group>"; };
@ -480,6 +497,9 @@
4B854A1A1D31447C00E08DE1 /* NeoVimServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = NeoVimServer; sourceTree = BUILT_PRODUCTS_DIR; };
4B854A1C1D31447C00E08DE1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
4B8AC0431DBCB3A1007CCC9B /* NeoVimObjectsExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeoVimObjectsExtensions.swift; sourceTree = "<group>"; };
4B96384B1ED9793B001C556F /* NeoVimAutoCommandEvent.generated.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NeoVimAutoCommandEvent.generated.m; sourceTree = "<group>"; };
4B96384D1ED9797F001C556F /* NeoVimUiBridgeProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NeoVimUiBridgeProtocol.m; sourceTree = "<group>"; };
4B96384F1ED979BD001C556F /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
4B97E2CD1D33F53D00FC0660 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainWindow.xib; sourceTree = "<group>"; };
4BB1BEA81D48773200463C29 /* RxSwiftCommons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxSwiftCommons.swift; sourceTree = "<group>"; };
4BB409E61DD68CCC005F39A2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/FileBrowserMenu.xib; sourceTree = "<group>"; };
@ -775,6 +795,7 @@
4BDF641B1D0887C100D47E1D /* TextDrawer.m */,
4BDF64221D08CAB000D47E1D /* MMCoreTextView.h */,
4BDF64231D08CAB000D47E1D /* MMCoreTextView.m */,
4B96384F1ED979BD001C556F /* Logger.swift */,
4B4192171D0C52D700A0BEB2 /* Grid.swift */,
4BCADE071D11ED12004DAD0F /* CocoaExtensions.swift */,
4B1BB3521D16C5E500CA4FEF /* InputTestView.swift */,
@ -1012,7 +1033,16 @@
4B570DC01D303CAF006EDC21 /* NeoVimAgent.h */,
4B570DC11D303CAF006EDC21 /* NeoVimAgent.m */,
4B183E1A1E08748B0079E8A8 /* NeoVimAutoCommandEvent.generated.h */,
4B96384B1ED9793B001C556F /* NeoVimAutoCommandEvent.generated.m */,
4B2A2C061D0352CB0074CE9A /* NeoVimUiBridgeProtocol.h */,
4B96384D1ED9797F001C556F /* NeoVimUiBridgeProtocol.m */,
1929BD8F08E21E3ECE9F84AB /* NeoVimView+Api.swift */,
1929B2AD3F9375F57F21AD58 /* NeoVimView+UiBridge.swift */,
1929B4A088958A7B4FF152B7 /* NeoVimView+Mouse.swift */,
1929B19207FBC2EBDF1B88F3 /* NeoVimView+Draw.swift */,
1929BD05CA498FC3C229455E /* NeoVimView+Resize.swift */,
1929BDF3167E15E7F3349798 /* NeoVimView+Key.swift */,
1929B4F65149D3C3E326DA65 /* NeoVimView+MenuItems.swift */,
);
name = NeoVimView;
sourceTree = "<group>";
@ -1378,7 +1408,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "pushd neovim\nmake CFLAGS='-mmacosx-version-min=10.10' MACOSX_DEPLOYMENT_TARGET=10.10 CMAKE_EXTRA_FLAGS=\"-DGETTEXT_SOURCE=CUSTOM\" libnvim\npopd";
shellScript = "pushd neovim\n../bin/build_libnvim.sh\npopd";
};
4BDFAC751E59BAC30056122C /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
@ -1391,7 +1421,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [ \"${CONFIGURATION}\" = \"Debug\" ]; then\nif [ -f \"SwiftNeoVim/NeoVimAutoCommandEvent.generated.h\" ]; then\nexit 0\nfi\nfi\n\nsed 's/^typedef enum auto_event/typedef NS_ENUM(NSUInteger, NeoVimAutoCommandEvent)/' <./neovim/build/include/auevents_enum.generated.h | sed 's/ event_T//' | sed 's/EVENT_/NeoVimAutoCommandEvent/' | sed 's/NUM_EVENTS/NumberOfAutoCommandEvents/' | sed -e '1s/^/@import Foundation;\\'$'\\n/' > SwiftNeoVim/NeoVimAutoCommandEvent.generated.h\n";
shellScript = ./bin/generate_source.py;
};
/* End PBXShellScriptBuildPhase section */
@ -1400,6 +1430,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4B96384C1ED9793B001C556F /* NeoVimAutoCommandEvent.generated.m in Sources */,
4BDD05891DBBC50000D1B405 /* NeoVimTab.m in Sources */,
4BEE79171D16D3800012EDAA /* CellAttributes.swift in Sources */,
4BDD05861DBBC50000D1B405 /* NeoVimBuffer.m in Sources */,
@ -1407,13 +1438,22 @@
4BCADE081D11ED12004DAD0F /* CocoaExtensions.swift in Sources */,
4B401B1A1D046E0600D99EDC /* NeoVimViewDelegate.swift in Sources */,
1929B728262BAA14FC93F6AC /* NeoVimView.swift in Sources */,
4B9638501ED979BD001C556F /* Logger.swift in Sources */,
4B570DC31D303CAF006EDC21 /* NeoVimAgent.m in Sources */,
4BDF641D1D0887C100D47E1D /* TextDrawer.m in Sources */,
4BDF64251D08CAB000D47E1D /* MMCoreTextView.m in Sources */,
4BDD058C1DBBC50000D1B405 /* NeoVimWindow.m in Sources */,
1929BEB90DCDAF7A2B68C886 /* ColorUtils.swift in Sources */,
4B8AC0441DBCB3A2007CCC9B /* NeoVimObjectsExtensions.swift in Sources */,
4B96384E1ED9797F001C556F /* NeoVimUiBridgeProtocol.m in Sources */,
4B4192181D0C52D700A0BEB2 /* Grid.swift in Sources */,
1929BDD4BFA4175D0A1B0BC3 /* NeoVimView+Api.swift in Sources */,
1929BC9EF45463113EBF3BCB /* NeoVimView+UiBridge.swift in Sources */,
1929BAE6B93067FD52DBE892 /* NeoVimView+Mouse.swift in Sources */,
1929B7993C8DB7F59447DF5F /* NeoVimView+Draw.swift in Sources */,
1929BD294A33873BAE2804C0 /* NeoVimView+Resize.swift in Sources */,
1929BB38525ECCEE8E39288A /* NeoVimView+Key.swift in Sources */,
1929BB0A840D93D1A13C25FD /* NeoVimView+MenuItems.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -174,18 +174,6 @@ class FileOutlineView: NSOutlineView,
self.insertItems(at: IndexSet(indicesToInsert), inParent: parent)
}
fileprivate func handleChildren(for fileBrowserItem: FileBrowserItem, new newChildren: [FileBrowserItem]) {
let curChildren = fileBrowserItem.children
let curPreparedChildren = self.prepare(curChildren)
let newPreparedChildren = self.prepare(newChildren)
let keptChildren = curPreparedChildren.filter { newPreparedChildren.contains($0) }
let childrenToRecurse = keptChildren.filter { self.isItemExpanded($0) }
childrenToRecurse.forEach(self.update)
}
fileprivate func sortedChildren(of url: URL) -> [FileBrowserItem] {
return FileUtils.directDescendants(of: url).map(FileBrowserItem.init).sorted()
}
@ -198,7 +186,7 @@ class FileOutlineView: NSOutlineView,
self.handleRemovals(for: fileBrowserItem, new: newChildren)
self.handleAdditions(for: fileBrowserItem, new: newChildren)
self.handleChildren(for: fileBrowserItem, new: newChildren)
fileBrowserItem.children.filter { self.isItemExpanded($0) }.forEach(self.update)
}
fileprivate func fileBrowserItem(with url: URL) -> FileBrowserItem? {
@ -234,7 +222,7 @@ extension FileOutlineView {
func outlineView(_: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
if item == nil {
if self.root.isChildrenScanned == false {
self.root.children = sortedChildren(of: self.cwd)
self.root.children = self.sortedChildren(of: self.cwd)
self.root.isChildrenScanned = true
}

62
bin/generate_source.py Executable file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env python
import os
import io
import re
from string import Template
if 'CONFIGURATION' in os.environ and os.environ['CONFIGURATION'] == 'Debug':
if os.path.isfile('SwiftNeoVim/NeoVimAutoCommandEvent.generated.h') and os.path.isfile('SwiftNeoVim/NeoVimAutoCommandEvent.generated.m'):
print("Files already there, exiting...")
exit(0)
with io.open('./neovim/build/include/auevents_enum.generated.h', 'r') as auto_cmds_file:
raw_auto_cmds = [line.strip() for line in auto_cmds_file.readlines() if re.match(r'^EVENT_', line.strip())]
def convert(line):
result = re.match(r'^EVENT_(.*) = (.*)', line.replace(',', ''))
return result.group(1), result.group(2)
auto_cmds = [convert(line) for line in raw_auto_cmds]
auto_cmds_impl_template = Template(
'''
@import Foundation;
#import "NeoVimAutoCommandEvent.generated.h"
NSString *neoVimAutoCommandEventName(NeoVimAutoCommandEvent event) {
switch (event) {
${event_cases}
default: return @"NON_EXISTING_EVENT";
}
}
''')
impl = auto_cmds_impl_template.substitute(
event_cases='\n'.join(
[' case NeoVimAutoCommandEvent{}: return @"{}";'.format(event[0], event[0]) for event in auto_cmds])
)
with io.open('SwiftNeoVim/NeoVimAutoCommandEvent.generated.m', 'w') as auto_cmds_impl_file:
auto_cmds_impl_file.write(unicode(impl))
auto_cmds_header_template = Template(
'''
@import Foundation;
typedef NS_ENUM(NSUInteger, NeoVimAutoCommandEvent) {
${event_cases}
};
#define NumberOfAutoCommandEvents ${count}
extern NSString * __nonnull neoVimAutoCommandEventName(NeoVimAutoCommandEvent event);
''')
header = auto_cmds_header_template.substitute(
event_cases='\n'.join(
[' NeoVimAutoCommandEvent{} = {},'.format(event[0], event[1]) for event in auto_cmds]
),
count=str(len(auto_cmds))
)
with io.open('SwiftNeoVim/NeoVimAutoCommandEvent.generated.h', 'w') as auto_cmds_header_file:
auto_cmds_header_file.write(unicode(header))