1
1
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:
Tae Won Ha 2018-08-30 17:48:49 +02:00
parent 4ceff1ea6c
commit a739e6bb39
7 changed files with 227 additions and 25 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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