Created
June 17, 2023 16:53
-
-
Save migueldeicaza/b3451fb7880def299112e5e362c94323 to your computer and use it in GitHub Desktop.
Patch for Chinese and emoji double-cell characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode 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