mirror of
https://github.com/qvacua/vimr.git
synced 2024-11-28 02:54:31 +03:00
GH-666 Add scrolling
Re-implement runs with ligatures to speed up things
This commit is contained in:
parent
4ceff1ea6c
commit
a739e6bb39
@ -23,6 +23,34 @@ class MyView: NSView {
|
||||
|
||||
let cellSize = FontUtils.cellSize(of: fira, linespacing: 1)
|
||||
|
||||
/*
|
||||
let string = "a\u{034B}"
|
||||
let attrStr = NSAttributedString(string: string, attributes: [.font: fira])
|
||||
let ctLine = CTLineCreateWithAttributedString(attrStr)
|
||||
let ctRun = (CTLineGetGlyphRuns(ctLine) as! Array<CTRun>)[0]
|
||||
let glyphCount = CTRunGetGlyphCount(ctRun)
|
||||
var glyphs = Array(repeating: CGGlyph(), count: glyphCount)
|
||||
var positions = Array(repeating: CGPoint(), count: glyphCount)
|
||||
var advances = Array(repeating: CGSize(), count: glyphCount)
|
||||
CTRunGetGlyphs(ctRun, .zero, &glyphs)
|
||||
CTRunGetPositions(ctRun, .zero, &positions)
|
||||
CTRunGetAdvances(ctRun, .zero, &advances)
|
||||
|
||||
let attrs = CTRunGetAttributes(ctRun) as! [NSAttributedStringKey: Any]
|
||||
let font = attrs[NSAttributedStringKey.font] as! NSFont
|
||||
|
||||
for i in (0..<positions.count) {
|
||||
positions[i].x += 20
|
||||
positions[i].y += 10
|
||||
}
|
||||
|
||||
print(glyphs)
|
||||
print(positions)
|
||||
print(advances)
|
||||
|
||||
CTFontDrawGlyphs(font, glyphs, positions, glyphCount, context)
|
||||
*/
|
||||
|
||||
/*
|
||||
// let glyphs: [CGGlyph] = [1614, 1494, 1104, 133]
|
||||
let glyphs: [CGGlyph] = [1614, 1614, 1063]
|
||||
@ -38,7 +66,7 @@ class MyView: NSView {
|
||||
)
|
||||
*/
|
||||
|
||||
let runs = (0..<3).map { row in
|
||||
let runs = (0..<4).map { row in
|
||||
AttributesRun(
|
||||
location: CGPoint(x: 0, y: CGFloat(row) * cellSize.height),
|
||||
cells: self.ugrid.cells[row][0..<10],
|
||||
@ -59,7 +87,8 @@ class MyView: NSView {
|
||||
reverse: false
|
||||
)
|
||||
runs.forEach { run in
|
||||
self.runDrawer.draw(run, with: defaultAttrs, xOffset: 0, in: context) }
|
||||
self.runDrawer.draw(run, with: defaultAttrs, xOffset: 0, in: context)
|
||||
}
|
||||
|
||||
self.draw(cellGridIn: context, cellSize: cellSize)
|
||||
}
|
||||
@ -119,7 +148,7 @@ class MyView: NSView {
|
||||
attrIds: Array(repeating: 0, count: 10)
|
||||
)
|
||||
self.ugrid.update(
|
||||
row: 1,
|
||||
row: 2,
|
||||
startCol: 0,
|
||||
endCol: 10,
|
||||
clearCol: 10,
|
||||
@ -137,7 +166,7 @@ class MyView: NSView {
|
||||
attrIds: Array(repeating: 0, count: 10)
|
||||
)
|
||||
self.ugrid.update(
|
||||
row: 2,
|
||||
row: 3,
|
||||
startCol: 0,
|
||||
endCol: 10,
|
||||
clearCol: 10,
|
||||
|
@ -876,7 +876,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.qvacua.NvimView;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG NO_LOG_TRACE";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -44,15 +44,24 @@ extension NvimView {
|
||||
}
|
||||
|
||||
func scroll(_ value: MessagePackValue) {
|
||||
// bridgeLogger.debug(count)
|
||||
//
|
||||
// gui.async {
|
||||
// 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.eventsSubject.onNext(.scroll)
|
||||
// }
|
||||
guard let array = MessagePackUtils.array(
|
||||
from: value, ofSize: 6, conversion: { $0.intValue }
|
||||
) else {
|
||||
bridgeLogger.error("The data did not have required 6 Int's!")
|
||||
return
|
||||
}
|
||||
|
||||
bridgeLogger.debug("[top, bot, left, right, rows, cols] = \(array)")
|
||||
|
||||
gui.async {
|
||||
let scrollRegion = Region(
|
||||
top: array[0], bottom: array[1] - 1,
|
||||
left: array[2], right: array[3] - 1
|
||||
)
|
||||
self.ugrid.scroll(region: scrollRegion, rows: array[4], cols: array[5])
|
||||
self.markForRender(region: scrollRegion)
|
||||
self.eventsSubject.onNext(.scroll)
|
||||
}
|
||||
}
|
||||
|
||||
func unmark(_ value: MessagePackValue) {
|
||||
@ -305,7 +314,7 @@ extension NvimView {
|
||||
else {
|
||||
|
||||
bridgeLogger.error("Could not get highlight attributes from " +
|
||||
"\(value)")
|
||||
"\(value)")
|
||||
return
|
||||
}
|
||||
let trait = FontTrait(rawValue: UInt(rawTrait))
|
||||
|
@ -35,7 +35,108 @@ struct CellGlyphUnion {
|
||||
|
||||
class Typesetter {
|
||||
|
||||
func fontGlyphRunsWithLigatures(
|
||||
final func fontGlyphRunsWithLigatures(
|
||||
nvimUtf16Cells: [[Unicode.UTF16.CodeUnit]],
|
||||
startColumn: Int,
|
||||
offset: CGPoint,
|
||||
foreground: Int,
|
||||
font: NSFont,
|
||||
cellWidth: CGFloat
|
||||
) -> [FontGlyphRun] {
|
||||
|
||||
let utf16Chars = self.utf16Chars(from: nvimUtf16Cells)
|
||||
let cellIndices = self.cellIndices(
|
||||
from: nvimUtf16Cells,
|
||||
utf16CharsCount: utf16Chars.count
|
||||
)
|
||||
let ctRuns = self.ctRuns(
|
||||
from: utf16Chars, font: font, foreground: foreground
|
||||
)
|
||||
|
||||
var result = Array<FontGlyphRun>()
|
||||
result.reserveCapacity(ctRuns.count)
|
||||
for run in ctRuns {
|
||||
let glyphCount = CTRunGetGlyphCount(run)
|
||||
|
||||
var glyphs = Array(repeating: CGGlyph(), count: glyphCount)
|
||||
CTRunGetGlyphs(run, .zero, &glyphs)
|
||||
|
||||
var positions = Array(repeating: CGPoint.zero, count: glyphCount)
|
||||
CTRunGetPositions(run, .zero, &positions)
|
||||
|
||||
var indices = Array(repeating: CFIndex(), count: glyphCount)
|
||||
CTRunGetStringIndices(run, .zero, &indices)
|
||||
|
||||
var advances = Array(repeating: CGSize.zero, count: glyphCount)
|
||||
CTRunGetAdvances(run, .zero, &advances)
|
||||
|
||||
var column = -1
|
||||
var columnPosition = CGFloat(0)
|
||||
var deltaX = CGFloat(0)
|
||||
for i in 0..<positions.count {
|
||||
let newColumn = cellIndices[indices[i]] + startColumn
|
||||
if newColumn != column {
|
||||
columnPosition = offset.x + CGFloat(newColumn) * cellWidth
|
||||
deltaX = columnPosition - positions[i].x
|
||||
column = newColumn
|
||||
}
|
||||
positions[i].x += deltaX
|
||||
positions[i].y += offset.y
|
||||
}
|
||||
|
||||
guard
|
||||
let attrs = CTRunGetAttributes(run) as? [NSAttributedStringKey: Any],
|
||||
let font = attrs[NSAttributedStringKey.font] as? NSFont
|
||||
else {
|
||||
// FIXME: GH-666: Return the default font
|
||||
preconditionFailure("Could not get font from CTRun!")
|
||||
}
|
||||
|
||||
let fontGlyphRun = FontGlyphRun(
|
||||
font: font, glyphs: glyphs, positions: positions
|
||||
)
|
||||
|
||||
result.append(fontGlyphRun)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private func cellIndices(
|
||||
from nvimUtf16Cells: [[Unicode.UTF16.CodeUnit]],
|
||||
utf16CharsCount: Int
|
||||
) -> Array<Int> {
|
||||
var cellIndices = Array(repeating: 0, count: utf16CharsCount)
|
||||
var cellIndex = 0
|
||||
var i = 0
|
||||
repeat {
|
||||
defer { cellIndex += 1}
|
||||
|
||||
if nvimUtf16Cells[cellIndex].isEmpty {
|
||||
continue
|
||||
}
|
||||
|
||||
for _ in (0..<nvimUtf16Cells[cellIndex].count) {
|
||||
cellIndices[i] = cellIndex
|
||||
i += 1
|
||||
}
|
||||
} while cellIndex < nvimUtf16Cells.count
|
||||
|
||||
return cellIndices
|
||||
}
|
||||
|
||||
private func utf16Chars(from nvimUtf16Cells: [[Unicode.UTF16.CodeUnit]]) -> Array<UInt16> {
|
||||
var utf16Chars = Array<Unicode.UTF16.CodeUnit>()
|
||||
utf16Chars.reserveCapacity(Int(Double(nvimUtf16Cells.count) * 1.5))
|
||||
|
||||
for i in 0..<nvimUtf16Cells.count {
|
||||
utf16Chars.append(contentsOf: nvimUtf16Cells[i])
|
||||
}
|
||||
|
||||
return utf16Chars
|
||||
}
|
||||
|
||||
final func fontGlyphRunsWithLigatures_old(
|
||||
nvimUtf16Cells: [[Unicode.UTF16.CodeUnit]],
|
||||
startColumn: Int,
|
||||
offset: CGPoint,
|
||||
@ -112,7 +213,7 @@ class Typesetter {
|
||||
return fontGlyphRuns
|
||||
}
|
||||
|
||||
func fontGlyphRunsWithoutLigatures(
|
||||
final func fontGlyphRunsWithoutLigatures(
|
||||
nvimCells: [String],
|
||||
startColumn: Int,
|
||||
offset: CGPoint,
|
||||
@ -137,7 +238,7 @@ class Typesetter {
|
||||
)
|
||||
}
|
||||
|
||||
let unichars = run.nvimUtf16Cells.flatMap { $0 }
|
||||
let unichars = self.utf16Chars(from: run.nvimUtf16Cells)
|
||||
var glyphs = Array<CGGlyph>(repeating: CGGlyph(), count: unichars.count)
|
||||
|
||||
let gotAllGlyphs = CTFontGetGlyphsForCharacters(
|
||||
|
@ -60,6 +60,69 @@ class UGrid {
|
||||
return self.size.width - 1
|
||||
}
|
||||
|
||||
func scroll(
|
||||
region: Region,
|
||||
rows: Int,
|
||||
cols: Int
|
||||
) {
|
||||
var start, stop, step: Int
|
||||
if rows > 0 {
|
||||
start = region.top;
|
||||
stop = region.bottom - rows + 1;
|
||||
step = 1;
|
||||
} else {
|
||||
start = region.bottom;
|
||||
stop = region.top - rows - 1;
|
||||
step = -1;
|
||||
}
|
||||
|
||||
// copy cell data
|
||||
let rangeWithinRow = region.left...region.right
|
||||
for i in stride(from: start, to: stop, by: step) {
|
||||
self.cells[i].replaceSubrange(
|
||||
rangeWithinRow, with: self.cells[i + rows][rangeWithinRow]
|
||||
)
|
||||
}
|
||||
|
||||
// clear cells in the emptied region,
|
||||
var clearTop, clearBottom: Int
|
||||
if rows > 0 {
|
||||
clearTop = stop
|
||||
clearBottom = stop + rows - 1
|
||||
} else {
|
||||
clearBottom = stop
|
||||
clearTop = stop + rows + 1
|
||||
}
|
||||
|
||||
self.clear(region: Region(
|
||||
top: clearTop,
|
||||
bottom: clearBottom,
|
||||
left: region.left,
|
||||
right: region.right
|
||||
))
|
||||
}
|
||||
|
||||
func clear(region: Region) {
|
||||
// FIXME: sometimes clearRegion gets called without first resizing the Grid.
|
||||
// Should we handle this?
|
||||
guard self.hasData else {
|
||||
return
|
||||
}
|
||||
|
||||
let clearedCell = UCell(
|
||||
string: " ",
|
||||
attrId: CellAttributesCollection.defaultAttributesId
|
||||
)
|
||||
let clearedRow = Array(
|
||||
repeating: clearedCell, count: region.right - region.left + 1
|
||||
)
|
||||
for i in region.top...region.bottom {
|
||||
self.cells[i].replaceSubrange(
|
||||
region.left...region.right, with: clearedRow
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func resize(_ size: Size) {
|
||||
logger.debug(size)
|
||||
|
||||
|
@ -302,7 +302,7 @@ class UiBridge {
|
||||
process.currentDirectoryPath = self.cwd.path
|
||||
process.launchPath = self.nvimServerExecutablePath()
|
||||
// GH-666: FIXME
|
||||
process.arguments = [self.localServerName, self.remoteServerName] + ["--headless", "/Users/hat/unicode.txt"] + self.nvimArgs
|
||||
process.arguments = [self.localServerName, self.remoteServerName] + ["--headless", "/Users/hat/php.php"] + self.nvimArgs
|
||||
process.launch()
|
||||
|
||||
self.nvimServerProc = process
|
||||
|
@ -88,7 +88,7 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
||||
.to(equal(CGPoint(x: offset.x + 11 * defaultWidth, y: offset.y)))
|
||||
expect(run.positions[1].x)
|
||||
.to(beCloseTo(offset.x + 11 * defaultWidth + 0.003, within: 0.001))
|
||||
expect(run.positions[1].y).to(equal(offset.y))
|
||||
expect(run.positions[1].y).to(beCloseTo(offset.y + 0.305, within: 0.001))
|
||||
|
||||
run = runs[2]
|
||||
expect(run.font).to(equal(defaultFont))
|
||||
@ -97,7 +97,7 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
||||
.to(equal(CGPoint(x: offset.x + 12 * defaultWidth, y: offset.y)))
|
||||
expect(run.positions[1].x)
|
||||
.to(beCloseTo(offset.x + 12 * defaultWidth, within: 0.001))
|
||||
expect(run.positions[1].y).to(equal(offset.y))
|
||||
expect(run.positions[1].y).to(beCloseTo(offset.y - 0.279, within: 0.001))
|
||||
|
||||
run = runs[3]
|
||||
expect(run.font).to(equal(monaco))
|
||||
@ -106,7 +106,7 @@ class TypesetterWithoutLigaturesTest: XCTestCase {
|
||||
.to(equal(CGPoint(x: offset.x + 13 * defaultWidth, y: offset.y)))
|
||||
expect(run.positions[1].x)
|
||||
.to(beCloseTo(offset.x + 13 * defaultWidth + 7.804, within: 0.001))
|
||||
expect(run.positions[1].y).to(equal(offset.y))
|
||||
expect(run.positions[1].y).to(beCloseTo(offset.y + 2.446, within: 0.001))
|
||||
|
||||
run = runs[4]
|
||||
expect(run.font).to(equal(defaultFont))
|
||||
@ -700,7 +700,7 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
||||
.to(equal(CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y)))
|
||||
expect(run.positions[1].x)
|
||||
.to(beCloseTo(offset.x + 1 * defaultWidth + 0.003, within: 0.001))
|
||||
expect(run.positions[1].y).to(equal(offset.y))
|
||||
expect(run.positions[1].y).to(beCloseTo(offset.y + 0.305, within: 0.001))
|
||||
|
||||
run = runs[1]
|
||||
expect(run.font).to(equal(defaultFont))
|
||||
@ -709,7 +709,7 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
||||
.to(equal(CGPoint(x: offset.x + 2 * defaultWidth, y: offset.y)))
|
||||
expect(run.positions[1].x)
|
||||
.to(beCloseTo(offset.x + 2 * defaultWidth, within: 0.001))
|
||||
expect(run.positions[1].y).to(equal(offset.y))
|
||||
expect(run.positions[1].y).to(beCloseTo(offset.y - 0.279, within: 0.001))
|
||||
|
||||
run = runs[2]
|
||||
expect(run.font).to(equal(monaco))
|
||||
@ -718,7 +718,7 @@ class TypesetterWithLigaturesTest: XCTestCase {
|
||||
.to(equal(CGPoint(x: offset.x + 3 * defaultWidth, y: offset.y)))
|
||||
expect(run.positions[1].x)
|
||||
.to(beCloseTo(offset.x + 3 * defaultWidth + 7.804, within: 0.001))
|
||||
expect(run.positions[1].y).to(equal(offset.y))
|
||||
expect(run.positions[1].y).to(beCloseTo(offset.y + 2.446, within: 0.001))
|
||||
|
||||
self.assertEmojiMarker(run: runs[3], xPosition: offset.x + 4 * defaultWidth)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user