Skip to content

Instantly share code, notes, and snippets.

@migueldeicaza
Created June 17, 2023 16:53
Show Gist options
  • Save migueldeicaza/b3451fb7880def299112e5e362c94323 to your computer and use it in GitHub Desktop.
Save migueldeicaza/b3451fb7880def299112e5e362c94323 to your computer and use it in GitHub Desktop.
Patch for Chinese and emoji double-cell characters
diff --git a/Sources/SwiftTerm/Apple/AppleTerminalView.swift b/Sources/SwiftTerm/Apple/AppleTerminalView.swift
index 046a089..0aadfa9 100644
--- a/Sources/SwiftTerm/Apple/AppleTerminalView.swift
+++ b/Sources/SwiftTerm/Apple/AppleTerminalView.swift
@@ -368,6 +368,8 @@ extension TerminalView {
var hasUrl = false
var str = prefix
+ var lastIsPlain = true
+
for col in 0..<cols {
let ch: CharData = line[col]
if col == 0 {
@@ -388,7 +390,20 @@ extension TerminalView {
hasUrl = chhas
}
}
- str.append(ch.code == 0 ? " " : ch.getCharacter ())
+ str.append(ch.code == 0 && lastIsPlain ? " " : ch.getCharacter ())
+
+ // Catches asian languages and emoji, and we trigger the slow path
+ // extract this from the unicode table, or vim that already contains
+ // the double-width and known-to-be-double-width emoji in src/mbyte.c
+ if ch.code > 0x1100 {
+ if let p = ch.getCharacter().unicodeScalars.first?.properties {
+ lastIsPlain = !p.isEmoji
+ } else {
+ lastIsPlain = true
+ }
+ } else {
+ lastIsPlain = true
+ }
}
res.append (NSAttributedString(string: str, attributes: getAttributes(attr, withUrl: hasUrl)))
updateSelectionAttributesIfNeeded(attributedLine: res, row: row, cols: cols)
@@ -527,6 +542,11 @@ extension TerminalView {
currentContext.restoreGState()
}
+ func getLineData (forRow: Int) -> (ViewLineInfo, CTLine) {
+ let line = terminal.buffer.lines [forRow]
+ let lineInfo = buildAttributedString(row: forRow, line: line, cols: terminal.cols)
+ return (lineInfo, CTLineCreateWithAttributedString(lineInfo.attrStr))
+ }
// TODO: this should not render any lines outside the dirtyRect
func drawTerminalContents (dirtyRect: TTRect, context: CGContext, bufferOffset: Int)
@@ -613,11 +633,21 @@ extension TerminalView {
continue
}
#endif
- let line = terminal.buffer.lines [row]
- let lineInfo = buildAttributedString(row: row, line: line, cols: terminal.cols)
- let ctline = CTLineCreateWithAttributedString(lineInfo.attrStr)
+ let (lineInfo, ctline) = getLineData(forRow: row)
var col = 0
+ var debug = false
+ if terminal.buffer.y == 1 {
+ debug = true
+ print ("CTLine is: \(ctline)")
+ let x = CTLineGetGlyphRuns(ctline) as? [CTRun] ?? []
+ print ("GlyphRunCount: \(x.count)")
+ for y in 0..<x.count {
+ print ("Run \(y): \(x [y])")
+ }
+ } else {
+ debug = false
+ }
for run in CTLineGetGlyphRuns(ctline) as? [CTRun] ?? [] {
let runGlyphsCount = CTRunGetGlyphCount(run)
let runAttributes = CTRunGetAttributes(run) as? [NSAttributedString.Key: Any] ?? [:]
@@ -627,11 +657,19 @@ extension TerminalView {
CTRunGetGlyphs(run, CFRange(), bufferPointer.baseAddress!)
count = runGlyphsCount
}
-
+ let runPositions = [CGPoint](unsafeUninitializedCapacity: runGlyphsCount) { (bufferPointer, count) in
+ CTRunGetPositions(run, CFRange(), bufferPointer.baseAddress!)
+ count = runGlyphsCount
+ }
var positions = runGlyphs.enumerated().map { (i: Int, glyph: CGGlyph) -> CGPoint in
- CGPoint(x: lineOrigin.x + (cellDimension.width * CGFloat(col + i)), y: lineOrigin.y + yOffset)
+
+ CGPoint(x: lineOrigin.x + runPositions [i].x, y: lineOrigin.y + yOffset)
+ }
+ var advances = [CGSize](unsafeUninitializedCapacity: runGlyphsCount) { (bufferPointer, count) in
+ CTRunGetAdvances(run, CFRange(), bufferPointer.baseAddress!)
+ count = runGlyphsCount
+
}
-
var backgroundColor: TTColor?
if runAttributes.keys.contains(.selectionBackgroundColor) {
backgroundColor = runAttributes[.selectionBackgroundColor] as? TTColor
@@ -844,6 +882,30 @@ extension TerminalView {
}
}
+ /// Given a column and a CTLine that we have produced for a line, compute the
+ /// x position for the column to position the cursor. This takes into account the
+ /// composed text from CoreText, instead of assuming that every character uses one cell
+ ///
+ func computeX (forColumn: Int, on ctline: CTLine) -> CGFloat {
+ var col = 0
+
+ for run in CTLineGetGlyphRuns(ctline) as? [CTRun] ?? [] {
+ let runGlyphsCount = CTRunGetGlyphCount(run)
+ let endCol = col + runGlyphsCount
+ defer { col = endCol }
+
+ if forColumn > endCol {
+ continue
+ }
+ let runPositions = [CGPoint](unsafeUninitializedCapacity: runGlyphsCount) { (bufferPointer, count) in
+ CTRunGetPositions(run, CFRange(), bufferPointer.baseAddress!)
+ count = runGlyphsCount
+ }
+ return runPositions [forColumn-col].x
+ }
+ return 0
+ }
+
func updateCursorPosition()
{
//let lineOrigin = CGPoint(x: 0, y: frame.height - (cellDimension.height * (CGFloat(terminal.buffer.y - terminal.buffer.yDisp + 1))))
@@ -865,7 +927,12 @@ extension TerminalView {
let offset = (cellDimension.height * (CGFloat(buffer.y-(buffer.yDisp-buffer.yBase)+1)))
let lineOrigin = CGPoint(x: 0, y: frame.height - offset)
#endif
- caretView.frame.origin = CGPoint(x: lineOrigin.x + (cellDimension.width * doublePosition * CGFloat(buffer.x)), y: lineOrigin.y)
+
+ print ("Target column is: \(buffer.x)")
+ let (_, ctline) = getLineData(forRow: vy)
+ caretView.frame.origin = CGPoint(
+ x: lineOrigin.x + (cellDimension.width * doublePosition * CGFloat(buffer.x)),
+ y: lineOrigin.y)
caretView.setText (ch: buffer.lines [vy][buffer.x])
}
diff --git a/Sources/SwiftTerm/Mac/MacCaretView.swift b/Sources/SwiftTerm/Mac/MacCaretView.swift
index f2442ec..6bdfe2a 100644
--- a/Sources/SwiftTerm/Mac/MacCaretView.swift
+++ b/Sources/SwiftTerm/Mac/MacCaretView.swift
@@ -37,6 +37,7 @@ class CaretView: NSView, CALayerDelegate {
}
func setText (ch: CharData) {
+ print ("Setting Character to \(ch.getCharacter())")
let res = NSAttributedString (
string: String (ch.getCharacter()),
attributes: terminal?.getAttributedValue(ch.attribute, usingFg: caretColor, andBg: caretTextColor ?? terminal?.nativeForegroundColor ?? NSColor.black))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment