mirror of
https://github.com/qvacua/vimr.git
synced 2024-11-28 11:35:35 +03:00
GH-666 Optimize rendering
Too slow: When we parallelize the CTLine computation, it's quite fast, but then, the CPU usage is very very high. Still, parallelizing.
This commit is contained in:
parent
9b8e0b6d42
commit
d531835e4c
@ -40,9 +40,36 @@ extension NvimView {
|
|||||||
).set()
|
).set()
|
||||||
dirtyRects.fill()
|
dirtyRects.fill()
|
||||||
|
|
||||||
self.runs(intersecting: dirtyRects).forEach { row in
|
let attrsRuns = self.runs(intersecting: dirtyRects)
|
||||||
self.draw(row, in: context)
|
let runs = attrsRuns.parallelMap {
|
||||||
|
// let runs = attrsRuns.map {
|
||||||
|
run -> (attrsRun: AttributesRun, fontGlyphRuns: [FontGlyphRun]) in
|
||||||
|
|
||||||
|
let font = FontUtils.font(adding: run.attrs.fontTrait, to: self.font)
|
||||||
|
|
||||||
|
let fontGlyphRuns = self.typesetter.fontGlyphRunsWithLigatures(
|
||||||
|
nvimUtf16Cells: run.cells.map { Array($0.string.utf16) },
|
||||||
|
startColumn: run.cells.startIndex,
|
||||||
|
offset: CGPoint(
|
||||||
|
x: self.xOffset, y: run.location.y + self.baselineOffset
|
||||||
|
),
|
||||||
|
font: font,
|
||||||
|
cellWidth: self.cellSize.width
|
||||||
|
)
|
||||||
|
|
||||||
|
return (attrsRun: run, fontGlyphRuns: fontGlyphRuns)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let defaultAttrs = self.cellAttributesCollection.defaultAttributes
|
||||||
|
runs.forEach { (attrsRun, fontGlyphRuns) in
|
||||||
|
self.runDrawer.draw(
|
||||||
|
attrsRun,
|
||||||
|
fontGlyphRuns: fontGlyphRuns,
|
||||||
|
defaultAttributes: defaultAttrs,
|
||||||
|
in: context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// self.drawCursor(context: context)
|
// self.drawCursor(context: context)
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@ -191,8 +218,8 @@ extension NvimView {
|
|||||||
else {
|
else {
|
||||||
// GH-666: FIXME: correct error handling
|
// GH-666: FIXME: correct error handling
|
||||||
logger.error("row: \(row), range: \(range): " +
|
logger.error("row: \(row), range: \(range): " +
|
||||||
"Could not get CellAttributes with ID " +
|
"Could not get CellAttributes with ID " +
|
||||||
"\(cells.first?.attrId)")
|
"\(cells.first?.attrId)")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,6 +305,7 @@ extension NvimView {
|
|||||||
of: newFont, linespacing: self.linespacing
|
of: newFont, linespacing: self.linespacing
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.baselineOffset = self.cellSize.height - CTFontGetAscent(newFont)
|
||||||
self.resizeNeoVimUi(to: self.bounds.size)
|
self.resizeNeoVimUi(to: self.bounds.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import MessagePack
|
|||||||
|
|
||||||
extension NvimView {
|
extension NvimView {
|
||||||
|
|
||||||
func resize(_ value: MessagePackValue) {
|
final func resize(_ value: MessagePackValue) {
|
||||||
guard let array = MessagePackUtils.array(from: value, ofSize: 2, conversion: { $0.intValue }) else {
|
guard let array = MessagePackUtils.array(from: value, ofSize: 2, conversion: { $0.intValue }) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -22,16 +22,16 @@ extension NvimView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func clear() {
|
final func clear() {
|
||||||
bridgeLogger.mark()
|
bridgeLogger.mark()
|
||||||
|
|
||||||
gui.async {
|
gui.async {
|
||||||
self.grid.clear()
|
// self.ugrid.clear()
|
||||||
self.markForRenderWholeView()
|
self.markForRenderWholeView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func modeChange(_ value: MessagePackValue) {
|
final func modeChange(_ value: MessagePackValue) {
|
||||||
guard let mode = MessagePackUtils.value(from: value, conversion: { v -> CursorModeShape? in
|
guard let mode = MessagePackUtils.value(from: value, conversion: { v -> CursorModeShape? in
|
||||||
guard let rawValue = v.intValue else { return nil }
|
guard let rawValue = v.intValue else { return nil }
|
||||||
return CursorModeShape(rawValue: UInt(rawValue))
|
return CursorModeShape(rawValue: UInt(rawValue))
|
||||||
@ -43,7 +43,7 @@ extension NvimView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func scroll(_ value: MessagePackValue) {
|
final func scroll(_ value: MessagePackValue) {
|
||||||
guard let array = MessagePackUtils.array(
|
guard let array = MessagePackUtils.array(
|
||||||
from: value, ofSize: 6, conversion: { $0.intValue }
|
from: value, ofSize: 6, conversion: { $0.intValue }
|
||||||
) else {
|
) else {
|
||||||
@ -64,7 +64,7 @@ extension NvimView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmark(_ value: MessagePackValue) {
|
final func unmark(_ value: MessagePackValue) {
|
||||||
// bridgeLogger.debug("\(row):\(column)")
|
// bridgeLogger.debug("\(row):\(column)")
|
||||||
//
|
//
|
||||||
// gui.async {
|
// gui.async {
|
||||||
@ -75,8 +75,9 @@ extension NvimView {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func flush(_ renderData: [MessagePackValue]) {
|
final func flush(_ renderData: [MessagePackValue]) {
|
||||||
bridgeLogger.hr()
|
// bridgeLogger.hr()
|
||||||
|
bridgeLogger.debug(renderData.count)
|
||||||
|
|
||||||
gui.async {
|
gui.async {
|
||||||
renderData.forEach { value in
|
renderData.forEach { value in
|
||||||
@ -109,14 +110,14 @@ extension NvimView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTitle(with value: MessagePackValue) {
|
final func setTitle(with value: MessagePackValue) {
|
||||||
guard let title = value.stringValue else { return }
|
guard let title = value.stringValue else { return }
|
||||||
|
|
||||||
bridgeLogger.debug(title)
|
bridgeLogger.debug(title)
|
||||||
self.eventsSubject.onNext(.setTitle(title))
|
self.eventsSubject.onNext(.setTitle(title))
|
||||||
}
|
}
|
||||||
|
|
||||||
func stop() {
|
final func stop() {
|
||||||
bridgeLogger.hr()
|
bridgeLogger.hr()
|
||||||
try? self.api
|
try? self.api
|
||||||
.stop()
|
.stop()
|
||||||
@ -132,7 +133,7 @@ extension NvimView {
|
|||||||
.wait()
|
.wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func autoCommandEvent(_ value: MessagePackValue) {
|
final func autoCommandEvent(_ value: MessagePackValue) {
|
||||||
guard let array = MessagePackUtils.array(from: value, ofSize: 2, conversion: { $0.intValue }),
|
guard let array = MessagePackUtils.array(from: value, ofSize: 2, conversion: { $0.intValue }),
|
||||||
let event = NvimAutoCommandEvent(rawValue: array[0]) else { return }
|
let event = NvimAutoCommandEvent(rawValue: array[0]) else { return }
|
||||||
let bufferHandle = array[1]
|
let bufferHandle = array[1]
|
||||||
@ -156,7 +157,7 @@ extension NvimView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipcBecameInvalid(_ reason: String) {
|
final func ipcBecameInvalid(_ reason: String) {
|
||||||
bridgeLogger.debug(reason)
|
bridgeLogger.debug(reason)
|
||||||
|
|
||||||
self.eventsSubject.onNext(.ipcBecameInvalid(reason))
|
self.eventsSubject.onNext(.ipcBecameInvalid(reason))
|
||||||
@ -191,11 +192,11 @@ extension NvimView {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bridgeLogger.trace(
|
// bridgeLogger.trace(
|
||||||
"row: \(row), startCol: \(startCol), endCol: \(endCol), " +
|
// "row: \(row), startCol: \(startCol), endCol: \(endCol), " +
|
||||||
"clearCol: \(clearCol), clearAttr: \(clearAttr), " +
|
// "clearCol: \(clearCol), clearAttr: \(clearAttr), " +
|
||||||
"chunk: \(chunk), attrIds: \(attrIds)"
|
// "chunk: \(chunk), attrIds: \(attrIds)"
|
||||||
)
|
// )
|
||||||
|
|
||||||
let count = endCol - startCol
|
let count = endCol - startCol
|
||||||
guard chunk.count == count && attrIds.count == count else { return }
|
guard chunk.count == count && attrIds.count == count else { return }
|
||||||
@ -241,13 +242,13 @@ extension NvimView {
|
|||||||
// MARK: - Simple
|
// MARK: - Simple
|
||||||
extension NvimView {
|
extension NvimView {
|
||||||
|
|
||||||
func bell() {
|
final func bell() {
|
||||||
bridgeLogger.mark()
|
bridgeLogger.mark()
|
||||||
|
|
||||||
NSSound.beep()
|
NSSound.beep()
|
||||||
}
|
}
|
||||||
|
|
||||||
func cwdChanged(_ value: MessagePackValue) {
|
final func cwdChanged(_ value: MessagePackValue) {
|
||||||
guard let cwd = value.stringValue else { return }
|
guard let cwd = value.stringValue else { return }
|
||||||
|
|
||||||
bridgeLogger.debug(cwd)
|
bridgeLogger.debug(cwd)
|
||||||
@ -255,7 +256,7 @@ extension NvimView {
|
|||||||
self.eventsSubject.onNext(.cwdChanged)
|
self.eventsSubject.onNext(.cwdChanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
func colorSchemeChanged(_ value: MessagePackValue) {
|
final func colorSchemeChanged(_ value: MessagePackValue) {
|
||||||
guard let values = MessagePackUtils.array(from: value, ofSize: 5, conversion: { $0.intValue }) else { return }
|
guard let values = MessagePackUtils.array(from: value, ofSize: 5, conversion: { $0.intValue }) else { return }
|
||||||
|
|
||||||
let theme = Theme(values)
|
let theme = Theme(values)
|
||||||
@ -267,7 +268,7 @@ extension NvimView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultColorsChanged(_ value: MessagePackValue) {
|
final func defaultColorsChanged(_ value: MessagePackValue) {
|
||||||
guard let values = MessagePackUtils.array(
|
guard let values = MessagePackUtils.array(
|
||||||
from: value, ofSize: 3, conversion: { $0.intValue }
|
from: value, ofSize: 3, conversion: { $0.intValue }
|
||||||
) else {
|
) else {
|
||||||
@ -294,14 +295,14 @@ extension NvimView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setDirty(with value: MessagePackValue) {
|
final func setDirty(with value: MessagePackValue) {
|
||||||
guard let dirty = value.boolValue else { return }
|
guard let dirty = value.boolValue else { return }
|
||||||
|
|
||||||
bridgeLogger.debug(dirty)
|
bridgeLogger.debug(dirty)
|
||||||
self.eventsSubject.onNext(.setDirtyStatus(dirty))
|
self.eventsSubject.onNext(.setDirtyStatus(dirty))
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAttr(with value: MessagePackValue) {
|
final func setAttr(with value: MessagePackValue) {
|
||||||
guard let array = value.arrayValue else { return }
|
guard let array = value.arrayValue else { return }
|
||||||
guard array.count == 6 else { return }
|
guard array.count == 6 else { return }
|
||||||
|
|
||||||
@ -334,38 +335,38 @@ extension NvimView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMenu() {
|
final func updateMenu() {
|
||||||
bridgeLogger.mark()
|
bridgeLogger.mark()
|
||||||
}
|
}
|
||||||
|
|
||||||
func busyStart() {
|
final func busyStart() {
|
||||||
bridgeLogger.mark()
|
bridgeLogger.mark()
|
||||||
}
|
}
|
||||||
|
|
||||||
func busyStop() {
|
final func busyStop() {
|
||||||
bridgeLogger.mark()
|
bridgeLogger.mark()
|
||||||
}
|
}
|
||||||
|
|
||||||
func mouseOn() {
|
final func mouseOn() {
|
||||||
bridgeLogger.mark()
|
bridgeLogger.mark()
|
||||||
}
|
}
|
||||||
|
|
||||||
func mouseOff() {
|
final func mouseOff() {
|
||||||
bridgeLogger.mark()
|
bridgeLogger.mark()
|
||||||
}
|
}
|
||||||
|
|
||||||
func visualBell() {
|
final func visualBell() {
|
||||||
bridgeLogger.mark()
|
bridgeLogger.mark()
|
||||||
}
|
}
|
||||||
|
|
||||||
func suspend() {
|
final func suspend() {
|
||||||
bridgeLogger.mark()
|
bridgeLogger.mark()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NvimView {
|
extension NvimView {
|
||||||
|
|
||||||
func markForRender(cellPosition position: Position) {
|
final func markForRender(cellPosition position: Position) {
|
||||||
self.markForRender(position: position)
|
self.markForRender(position: position)
|
||||||
|
|
||||||
if self.grid.isCellEmpty(position) {
|
if self.grid.isCellEmpty(position) {
|
||||||
@ -377,26 +378,26 @@ extension NvimView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func markForRender(position: Position) {
|
final func markForRender(position: Position) {
|
||||||
self.markForRender(row: position.row, column: position.column)
|
self.markForRender(row: position.row, column: position.column)
|
||||||
}
|
}
|
||||||
|
|
||||||
func markForRender(screenCursor position: Position) {
|
final func markForRender(screenCursor position: Position) {
|
||||||
self.markForRender(position: position)
|
self.markForRender(position: position)
|
||||||
if self.grid.isNextCellEmpty(position) {
|
if self.grid.isNextCellEmpty(position) {
|
||||||
self.markForRender(position: self.grid.nextCellPosition(position))
|
self.markForRender(position: self.grid.nextCellPosition(position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func markForRenderWholeView() {
|
final func markForRenderWholeView() {
|
||||||
self.needsDisplay = true
|
self.needsDisplay = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func markForRender(region: Region) {
|
final func markForRender(region: Region) {
|
||||||
self.setNeedsDisplay(self.rect(for: region))
|
self.setNeedsDisplay(self.rect(for: region))
|
||||||
}
|
}
|
||||||
|
|
||||||
func markForRender(row: Int, column: Int) {
|
final func markForRender(row: Int, column: Int) {
|
||||||
self.setNeedsDisplay(self.rect(forRow: row, column: column))
|
self.setNeedsDisplay(self.rect(forRow: row, column: column))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,4 +380,6 @@ public class NvimView: NSView,
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
private var _linespacing = NvimView.defaultLinespacing
|
private var _linespacing = NvimView.defaultLinespacing
|
||||||
|
let typesetter = Typesetter()
|
||||||
|
var baselineOffset = CGFloat(0)
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,7 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class AttributesRunDrawer {
|
final class AttributesRunDrawer {
|
||||||
|
|
||||||
private let logger = LogContext.stdoutLogger(
|
|
||||||
as: String(reflecting: AttributesRunDrawer.self)
|
|
||||||
)
|
|
||||||
|
|
||||||
var baseFont: NSFont {
|
var baseFont: NSFont {
|
||||||
didSet {
|
didSet {
|
||||||
@ -33,6 +29,38 @@ class AttributesRunDrawer {
|
|||||||
self.updateFontMetrics()
|
self.updateFontMetrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func draw(
|
||||||
|
_ run: AttributesRun,
|
||||||
|
fontGlyphRuns: [FontGlyphRun],
|
||||||
|
defaultAttributes: CellAttributes,
|
||||||
|
`in` context: CGContext
|
||||||
|
) {
|
||||||
|
context.saveGState()
|
||||||
|
defer { context.restoreGState() }
|
||||||
|
|
||||||
|
self.draw(
|
||||||
|
backgroundFor: run,
|
||||||
|
with: defaultAttributes,
|
||||||
|
in: context
|
||||||
|
)
|
||||||
|
|
||||||
|
context.setFillColor(
|
||||||
|
ColorUtils.cgColorIgnoringAlpha(run.attrs.effectiveForeground)
|
||||||
|
)
|
||||||
|
|
||||||
|
fontGlyphRuns.forEach { run in
|
||||||
|
CTFontDrawGlyphs(
|
||||||
|
run.font,
|
||||||
|
run.glyphs,
|
||||||
|
run.positions,
|
||||||
|
run.glyphs.count,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: GH-666: Draw underline/curl
|
||||||
|
}
|
||||||
|
|
||||||
func draw(
|
func draw(
|
||||||
_ run: AttributesRun,
|
_ run: AttributesRun,
|
||||||
with defaultAttributes: CellAttributes,
|
with defaultAttributes: CellAttributes,
|
||||||
@ -58,7 +86,6 @@ class AttributesRunDrawer {
|
|||||||
nvimUtf16Cells: run.cells.map { Array($0.string.utf16) },
|
nvimUtf16Cells: run.cells.map { Array($0.string.utf16) },
|
||||||
startColumn: run.cells.startIndex,
|
startColumn: run.cells.startIndex,
|
||||||
offset: CGPoint(x: xOffset, y: run.location.y + self.baselineOffset),
|
offset: CGPoint(x: xOffset, y: run.location.y + self.baselineOffset),
|
||||||
foreground: run.attrs.effectiveForeground,
|
|
||||||
font: font,
|
font: font,
|
||||||
cellWidth: self.cellSize.width
|
cellWidth: self.cellSize.width
|
||||||
)
|
)
|
||||||
@ -68,7 +95,6 @@ class AttributesRunDrawer {
|
|||||||
nvimCells: run.cells.map { $0.string },
|
nvimCells: run.cells.map { $0.string },
|
||||||
startColumn: run.cells.startIndex,
|
startColumn: run.cells.startIndex,
|
||||||
offset: CGPoint(x: xOffset, y: run.location.y + self.baselineOffset),
|
offset: CGPoint(x: xOffset, y: run.location.y + self.baselineOffset),
|
||||||
foreground: run.attrs.effectiveForeground,
|
|
||||||
font: font,
|
font: font,
|
||||||
cellWidth: self.cellSize.width
|
cellWidth: self.cellSize.width
|
||||||
)
|
)
|
||||||
|
@ -13,6 +13,25 @@ extension Array where Element: Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Array {
|
||||||
|
|
||||||
|
/// Does not retain the order of elements.
|
||||||
|
func parallelMap<T>(_ transform: @escaping (Element) -> T) -> [T] {
|
||||||
|
var result = Array<T>()
|
||||||
|
result.reserveCapacity(self.count)
|
||||||
|
|
||||||
|
var lock = OS_SPINLOCK_INIT
|
||||||
|
DispatchQueue.concurrentPerform(iterations: self.count) { i in
|
||||||
|
let mapped = transform(self[i])
|
||||||
|
OSSpinLockLock(&lock)
|
||||||
|
result.append(mapped)
|
||||||
|
OSSpinLockUnlock(&lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension ArraySlice {
|
extension ArraySlice {
|
||||||
|
|
||||||
func groupedRanges<T: Equatable>(with marker: (Int, Element, ArraySlice<Element>) -> T) -> [CountableClosedRange<Int>] {
|
func groupedRanges<T: Equatable>(with marker: (Int, Element, ArraySlice<Element>) -> T) -> [CountableClosedRange<Int>] {
|
||||||
|
@ -5,13 +5,12 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class Typesetter {
|
final class Typesetter {
|
||||||
|
|
||||||
final func fontGlyphRunsWithLigatures(
|
func fontGlyphRunsWithLigatures(
|
||||||
nvimUtf16Cells: [[Unicode.UTF16.CodeUnit]],
|
nvimUtf16Cells: [[Unicode.UTF16.CodeUnit]],
|
||||||
startColumn: Int,
|
startColumn: Int,
|
||||||
offset: CGPoint,
|
offset: CGPoint,
|
||||||
foreground: Int,
|
|
||||||
font: NSFont,
|
font: NSFont,
|
||||||
cellWidth: CGFloat
|
cellWidth: CGFloat
|
||||||
) -> [FontGlyphRun] {
|
) -> [FontGlyphRun] {
|
||||||
@ -21,13 +20,9 @@ class Typesetter {
|
|||||||
from: nvimUtf16Cells,
|
from: nvimUtf16Cells,
|
||||||
utf16CharsCount: utf16Chars.count
|
utf16CharsCount: utf16Chars.count
|
||||||
)
|
)
|
||||||
let ctRuns = self.ctRuns(
|
let ctRuns = self.ctRuns(from: utf16Chars, font: font)
|
||||||
from: utf16Chars, font: font, foreground: foreground
|
|
||||||
)
|
|
||||||
|
|
||||||
var result = Array<FontGlyphRun>()
|
let result = ctRuns.map { run -> FontGlyphRun in
|
||||||
result.reserveCapacity(ctRuns.count)
|
|
||||||
for run in ctRuns {
|
|
||||||
let glyphCount = CTRunGetGlyphCount(run)
|
let glyphCount = CTRunGetGlyphCount(run)
|
||||||
|
|
||||||
var glyphs = Array(repeating: CGGlyph(), count: glyphCount)
|
var glyphs = Array(repeating: CGGlyph(), count: glyphCount)
|
||||||
@ -53,19 +48,16 @@ class Typesetter {
|
|||||||
positions[i].y += offset.y
|
positions[i].y += offset.y
|
||||||
}
|
}
|
||||||
|
|
||||||
guard
|
let attrs = CTRunGetAttributes(run)
|
||||||
let attrs = CTRunGetAttributes(run) as? [NSAttributedStringKey: Any],
|
let font = Unmanaged<NSFont>.fromOpaque(
|
||||||
let font = attrs[NSAttributedStringKey.font] as? NSFont
|
CFDictionaryGetValue(
|
||||||
else {
|
attrs, Unmanaged.passUnretained(kCTFontAttributeName).toOpaque()
|
||||||
// FIXME: GH-666: Return the default font
|
)
|
||||||
preconditionFailure("Could not get font from CTRun!")
|
).takeUnretainedValue()
|
||||||
}
|
|
||||||
|
|
||||||
let fontGlyphRun = FontGlyphRun(
|
return FontGlyphRun(
|
||||||
font: font, glyphs: glyphs, positions: positions
|
font: font, glyphs: glyphs, positions: positions
|
||||||
)
|
)
|
||||||
|
|
||||||
result.append(fontGlyphRun)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -80,36 +72,45 @@ class Typesetter {
|
|||||||
var i = 0
|
var i = 0
|
||||||
repeat {
|
repeat {
|
||||||
if nvimUtf16Cells[cellIndex].isEmpty {
|
if nvimUtf16Cells[cellIndex].isEmpty {
|
||||||
cellIndex += 1
|
cellIndex = cellIndex &+ 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _ in (0..<nvimUtf16Cells[cellIndex].count) {
|
for _ in (0..<nvimUtf16Cells[cellIndex].count) {
|
||||||
cellIndices[i] = cellIndex
|
cellIndices[i] = cellIndex
|
||||||
i += 1
|
i = i &+ 1
|
||||||
}
|
}
|
||||||
cellIndex += 1
|
cellIndex = cellIndex &+ 1
|
||||||
} while cellIndex < nvimUtf16Cells.count
|
} while cellIndex < nvimUtf16Cells.count
|
||||||
|
|
||||||
return cellIndices
|
return cellIndices
|
||||||
}
|
}
|
||||||
|
|
||||||
private func utf16Chars(from nvimUtf16Cells: [[Unicode.UTF16.CodeUnit]]) -> Array<UInt16> {
|
private func utf16Chars(
|
||||||
var utf16Chars = Array<Unicode.UTF16.CodeUnit>()
|
from nvimUtf16Cells: [[Unicode.UTF16.CodeUnit]]
|
||||||
utf16Chars.reserveCapacity(Int(Double(nvimUtf16Cells.count) * 1.5))
|
) -> Array<UInt16> {
|
||||||
|
// Using reduce seems to be slower than the following:
|
||||||
|
var count = 0
|
||||||
for i in 0..<nvimUtf16Cells.count {
|
for i in 0..<nvimUtf16Cells.count {
|
||||||
utf16Chars.append(contentsOf: nvimUtf16Cells[i])
|
count = count &+ nvimUtf16Cells[i].count
|
||||||
}
|
}
|
||||||
|
|
||||||
return utf16Chars
|
// Using append(contentsOf:) seems to be slower than the following:
|
||||||
|
var result = Array(repeating: Unicode.UTF16.CodeUnit(), count: count)
|
||||||
|
for i in 0..<nvimUtf16Cells.count {
|
||||||
|
let cell = nvimUtf16Cells[i]
|
||||||
|
for j in 0..<cell.count {
|
||||||
|
result[i + j] = cell[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
final func fontGlyphRunsWithoutLigatures(
|
func fontGlyphRunsWithoutLigatures(
|
||||||
nvimCells: [String],
|
nvimCells: [String],
|
||||||
startColumn: Int,
|
startColumn: Int,
|
||||||
offset: CGPoint,
|
offset: CGPoint,
|
||||||
foreground: Int,
|
|
||||||
font: NSFont,
|
font: NSFont,
|
||||||
cellWidth: CGFloat
|
cellWidth: CGFloat
|
||||||
) -> [FontGlyphRun] {
|
) -> [FontGlyphRun] {
|
||||||
@ -124,7 +125,6 @@ class Typesetter {
|
|||||||
nvimUtf16Cells: run.nvimUtf16Cells,
|
nvimUtf16Cells: run.nvimUtf16Cells,
|
||||||
startColumn: startColumn + run.startColumn,
|
startColumn: startColumn + run.startColumn,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: foreground,
|
|
||||||
font: font,
|
font: font,
|
||||||
cellWidth: cellWidth
|
cellWidth: cellWidth
|
||||||
)
|
)
|
||||||
@ -133,9 +133,11 @@ class Typesetter {
|
|||||||
let unichars = self.utf16Chars(from: run.nvimUtf16Cells)
|
let unichars = self.utf16Chars(from: run.nvimUtf16Cells)
|
||||||
var glyphs = Array<CGGlyph>(repeating: CGGlyph(), count: unichars.count)
|
var glyphs = Array<CGGlyph>(repeating: CGGlyph(), count: unichars.count)
|
||||||
|
|
||||||
let gotAllGlyphs = CTFontGetGlyphsForCharacters(
|
let gotAllGlyphs = unichars.withUnsafeBufferPointer { pointer in
|
||||||
font, unichars, &glyphs, unichars.count
|
CTFontGetGlyphsForCharacters(
|
||||||
)
|
font, pointer.baseAddress!, &glyphs, unichars.count
|
||||||
|
)
|
||||||
|
}
|
||||||
if gotAllGlyphs {
|
if gotAllGlyphs {
|
||||||
let startColumnForPositions = startColumn + run.startColumn
|
let startColumnForPositions = startColumn + run.startColumn
|
||||||
let endColumn = startColumnForPositions + glyphs.count
|
let endColumn = startColumnForPositions + glyphs.count
|
||||||
@ -160,7 +162,6 @@ class Typesetter {
|
|||||||
nvimUtf16Cells: nvimUtf16Cells,
|
nvimUtf16Cells: nvimUtf16Cells,
|
||||||
startColumn: startColumn + range.lowerBound,
|
startColumn: startColumn + range.lowerBound,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: foreground,
|
|
||||||
font: font,
|
font: font,
|
||||||
cellWidth: cellWidth
|
cellWidth: cellWidth
|
||||||
)
|
)
|
||||||
@ -187,21 +188,22 @@ class Typesetter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func ctRuns(
|
private func ctRuns(
|
||||||
from utf16Chars: [Unicode.UTF16.CodeUnit],
|
from utf16Chars: Array<Unicode.UTF16.CodeUnit>,
|
||||||
font: NSFont,
|
font: NSFont
|
||||||
foreground: Int
|
|
||||||
) -> [CTRun] {
|
) -> [CTRun] {
|
||||||
let attrStr = NSAttributedString(
|
let attrStr = NSAttributedString(
|
||||||
string: String(utf16CodeUnits: utf16Chars, count: utf16Chars.count),
|
string: String(utf16CodeUnits: utf16Chars, count: utf16Chars.count),
|
||||||
attributes: [
|
attributes: [
|
||||||
.font: font,
|
.font: font,
|
||||||
.foregroundColor: ColorUtils.cgColorIgnoringAlpha(foreground),
|
.ligature: 1
|
||||||
.ligature: 1,
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
let ctLine = CTLineCreateWithAttributedString(attrStr)
|
|
||||||
|
|
||||||
guard let ctRuns = CTLineGetGlyphRuns(ctLine) as? [CTRun] else { return [] }
|
let ctLine = CTLineCreateWithAttributedString(attrStr)
|
||||||
|
guard let ctRuns = CTLineGetGlyphRuns(ctLine) as? [CTRun] else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
return ctRuns
|
return ctRuns
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +222,7 @@ class Typesetter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let nvimUtf16Cells = nvimCells.map { Array($0.utf16) }
|
let nvimUtf16Cells = nvimCells.map { Array($0.utf16) }
|
||||||
let utf16Chars = nvimUtf16Cells.flatMap { $0 }
|
let utf16Chars = self.utf16Chars(from: nvimUtf16Cells)
|
||||||
|
|
||||||
let hasMoreThanTwoCells = nvimUtf16Cells.count >= 2
|
let hasMoreThanTwoCells = nvimUtf16Cells.count >= 2
|
||||||
let firstCharHasSingleUnichar = nvimUtf16Cells[0].count == 1
|
let firstCharHasSingleUnichar = nvimUtf16Cells[0].count == 1
|
||||||
|
@ -11,7 +11,7 @@ struct UCell {
|
|||||||
var attrId: Int
|
var attrId: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
class UGrid {
|
final class UGrid {
|
||||||
|
|
||||||
private(set) var size = Size.zero
|
private(set) var size = Size.zero
|
||||||
private(set) var posision = Position.zero
|
private(set) var posision = Position.zero
|
||||||
@ -102,6 +102,10 @@ class UGrid {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func clear() {
|
||||||
|
// self.clear(region: self.region)
|
||||||
|
}
|
||||||
|
|
||||||
func clear(region: Region) {
|
func clear(region: Region) {
|
||||||
// FIXME: sometimes clearRegion gets called without first resizing the Grid.
|
// FIXME: sometimes clearRegion gets called without first resizing the Grid.
|
||||||
// Should we handle this?
|
// Should we handle this?
|
||||||
|
@ -16,7 +16,6 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
|||||||
nvimCells: emojiMarked(["a", "b", "c"]),
|
nvimCells: emojiMarked(["a", "b", "c"]),
|
||||||
startColumn: 10,
|
startColumn: 10,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -40,7 +39,6 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
|||||||
nvimCells: emojiMarked(["ü", "î", "ñ"]),
|
nvimCells: emojiMarked(["ü", "î", "ñ"]),
|
||||||
startColumn: 20,
|
startColumn: 20,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -66,7 +64,6 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
|||||||
),
|
),
|
||||||
startColumn: 10,
|
startColumn: 10,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -127,7 +124,6 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
|||||||
nvimCells: asciiMarked(["a", "b", "\u{1F600}", "", "\u{1F377}", ""]),
|
nvimCells: asciiMarked(["a", "b", "\u{1F600}", "", "\u{1F377}", ""]),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -161,7 +157,6 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
|||||||
nvimCells: asciiMarked(["a", "\u{1F476}", "", "\u{1F3FD}", ""]),
|
nvimCells: asciiMarked(["a", "\u{1F476}", "", "\u{1F3FD}", ""]),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -193,7 +188,6 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
|||||||
nvimCells: asciiMarked(["a", "b", "하", "", "태", "", "원", ""]),
|
nvimCells: asciiMarked(["a", "b", "하", "", "태", "", "원", ""]),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -227,7 +221,6 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
|||||||
nvimCells: asciiMarked(["a", "b", "河", "", "泰", "", "元", ""]),
|
nvimCells: asciiMarked(["a", "b", "河", "", "泰", "", "元", ""]),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -261,7 +254,6 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
|||||||
nvimCells: emojiMarked(["a", "\u{10437}", "\u{1F14}"]),
|
nvimCells: emojiMarked(["a", "\u{10437}", "\u{1F14}"]),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -301,7 +293,6 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
|||||||
nvimCells: emojiMarked(["a", "-", "-", ">", "a"]),
|
nvimCells: emojiMarked(["a", "-", "-", ">", "a"]),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: fira,
|
font: fira,
|
||||||
cellWidth: firaWidth
|
cellWidth: firaWidth
|
||||||
)
|
)
|
||||||
@ -340,7 +331,6 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
|||||||
)),
|
)),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -364,7 +354,6 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
|||||||
nvimUtf16Cells: utf16Chars(emojiMarked(["ü", "î", "ñ"])),
|
nvimUtf16Cells: utf16Chars(emojiMarked(["ü", "î", "ñ"])),
|
||||||
startColumn: 10,
|
startColumn: 10,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -392,7 +381,6 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
|||||||
),
|
),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -437,7 +425,6 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
|||||||
),
|
),
|
||||||
startColumn: 0,
|
startColumn: 0,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -465,7 +452,6 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
|||||||
),
|
),
|
||||||
startColumn: 0,
|
startColumn: 0,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -493,7 +479,6 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
|||||||
)),
|
)),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -518,7 +503,6 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
|||||||
),
|
),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -544,7 +528,6 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
|||||||
),
|
),
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -570,7 +553,6 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
|||||||
),
|
),
|
||||||
startColumn: 0,
|
startColumn: 0,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: defaultFont,
|
font: defaultFont,
|
||||||
cellWidth: defaultWidth
|
cellWidth: defaultWidth
|
||||||
)
|
)
|
||||||
@ -602,7 +584,6 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
|||||||
),
|
),
|
||||||
startColumn: 0,
|
startColumn: 0,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
foreground: 0,
|
|
||||||
font: fira,
|
font: fira,
|
||||||
cellWidth: firaWidth
|
cellWidth: firaWidth
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user