diff options
author | bptato <nincsnevem662@gmail.com> | 2024-09-08 15:18:45 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-09-08 16:06:02 +0200 |
commit | 4124c041ed2e3b497ede72fdae229aa2c6aca249 (patch) | |
tree | e8488449de6f0be54b9c79547352829b998833d3 /src/local | |
parent | 5a64e3193924c7e503dddb10a99989148b26e922 (diff) | |
download | chawan-4124c041ed2e3b497ede72fdae229aa2c6aca249.tar.gz |
utils: add twtuni
std/unicode has the following issues: * Rune is an int32, which implies overflow checking. Also, it is distinct, so you have to convert it manually to do arithmetic. * QJS libunicode and Chagashi work with uint32, interfacing with these required pointless type conversions. * fastRuneAt is a template, meaning it's pasted into every call site. Also, it decodes to UCS-4, so it generates two branches that aren't even used. Overall this lead to quite some code bloat. * fastRuneAt and lastRune have frustratingly different interfaces. Writing code to handle both cases is error prone. * On older Nim versions which we still support, std/unicode takes strings, not openArray[char]'s. Replace it with "twtuni", which includes some improved versions of the few procedures from std/unicode that we actually use.
Diffstat (limited to 'src/local')
-rw-r--r-- | src/local/container.nim | 205 | ||||
-rw-r--r-- | src/local/lineedit.nim | 125 | ||||
-rw-r--r-- | src/local/pager.nim | 21 | ||||
-rw-r--r-- | src/local/term.nim | 1 |
4 files changed, 175 insertions, 177 deletions
diff --git a/src/local/container.nim b/src/local/container.nim index 2c12c4ae..cb7738b8 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -3,7 +3,6 @@ import std/options import std/os import std/posix import std/tables -import std/unicode import chagashi/charset import config/config @@ -32,6 +31,7 @@ import utils/luwrap import utils/mimeguess import utils/strwidth import utils/twtstr +import utils/twtuni import utils/wordbreak type @@ -369,12 +369,12 @@ proc popCursorPos(select: Select; nojump = false) = if not nojump: select.queueDraw() -const HorizontalBar = $Rune(0x2500) -const VerticalBar = $Rune(0x2502) -const CornerTopLeft = $Rune(0x250C) -const CornerTopRight = $Rune(0x2510) -const CornerBottomLeft = $Rune(0x2514) -const CornerBottomRight = $Rune(0x2518) +const HorizontalBar = "\u2500" +const VerticalBar = "\u2502" +const CornerTopLeft = "\u250C" +const CornerTopRight = "\u2510" +const CornerBottomLeft = "\u2514" +const CornerBottomRight = "\u2518" proc drawBorders(display: var FixedGrid; sx, ex, sy, ey: int; upmore, downmore: bool) = @@ -446,7 +446,6 @@ proc drawSelect*(select: Select; display: var FixedGrid) = # move inside border inc sy inc sx - var r: Rune var k = 0 var format = Format() while k < select.selected.len and select.selected[k] < si: @@ -462,13 +461,13 @@ proc drawSelect*(select: Select; display: var FixedGrid) = else: format.flags.excl(ffReverse) while j < select.options[i].len: - fastRuneAt(select.options[i], j, r) - let rw = r.twidth(x) + let pj = j + let u = select.options[i].nextUTF8(j) let ox = x - x += rw + x += u.twidth(x) if x > ex: break - display[dls + ox].str = $r + display[dls + ox].str = select.options[i].substr(pj, j - 1) display[dls + ox].format = format while x < ex: display[dls + x].str = " " @@ -578,9 +577,8 @@ func findColBytes(s: string; endx: int; startx = 0; starti = 0): int = var w = startx var i = starti while i < s.len and w < endx: - var r: Rune - fastRuneAt(s, i, r) - w += r.twidth(w) + let u = s.nextUTF8(i) + w += u.twidth(w) return i func cursorBytes(container: Container; y: int; cc = container.cursorx): int = @@ -596,11 +594,10 @@ func cursorFirstX(container: Container): int = let line = container.currentLine var w = 0 var i = 0 - var r: Rune let cc = container.cursorx while i < line.len: - fastRuneAt(line, i, r) - let tw = r.twidth(w) + let u = line.nextUTF8(i) + let tw = u.twidth(w) if w + tw > cc: return w w += tw @@ -613,11 +610,10 @@ func cursorLastX(container: Container): int = let line = container.currentLine var w = 0 var i = 0 - var r: Rune let cc = container.cursorx while i < line.len and w <= cc: - fastRuneAt(line, i, r) - w += r.twidth(w) + let u = line.nextUTF8(i) + w += u.twidth(w) return max(w - 1, 0) # Last cell for tab, first cell for everything else (e.g. double width.) @@ -630,16 +626,15 @@ func cursorDispX(container: Container): int = var w = 0 var pw = 0 var i = 0 - var r: Rune + var u = 0u32 let cc = container.cursorx while i < line.len and w <= cc: - fastRuneAt(line, i, r) + u = line.nextUTF8(i) pw = w - w += r.twidth(w) - if r == Rune('\t'): + w += u.twidth(w) + if u == uint32('\t'): return max(w - 1, 0) - else: - return pw + return pw func acursorx*(container: Container): int = max(0, container.cursorDispX() - container.fromx) @@ -911,10 +906,10 @@ proc setCursorXY*(container: Container; x, y: int; refresh = true) {.jsfunc.} = proc cursorLineTextStart(container: Container) {.jsfunc.} = if container.numLines == 0: return var x = 0 - for r in container.currentLine.runes: - if not container.luctx.isWhiteSpaceLU(r): + for u in container.currentLine.points: + if not container.luctx.isWhiteSpaceLU(u): break - x += r.twidth(x) + x += u.twidth(x) if x == 0: dec x container.setCursorX(x) @@ -1020,45 +1015,62 @@ proc cursorLineBegin(container: Container) {.jsfunc.} = proc cursorLineEnd(container: Container) {.jsfunc.} = container.setCursorX(container.currentLineWidth() - 1) -type BreakFunc = proc(ctx: LUContext; r: Rune): BreakCategory {.nimcall.} +type BreakFunc = proc(ctx: LUContext; r: uint32): BreakCategory {.nimcall.} -proc skipSpace(container: Container; b, x: var int; breakFunc: BreakFunc) = +# move to first char that is not in this category +proc skipCat(container: Container; b, x: var int; breakFunc: BreakFunc; + cat: BreakCategory) = while b < container.currentLine.len: - var r: Rune let pb = b - fastRuneAt(container.currentLine, b, r) - if container.luctx.breakFunc(r) != bcSpace: + let u = container.currentLine.nextUTF8(b) + if container.luctx.breakFunc(u) != cat: b = pb break - x += r.twidth(x) + x += u.twidth(x) -proc skipSpaceRev(container: Container; b, x: var int; breakFunc: BreakFunc) = - while b >= 0: - let (r, o) = lastRune(container.currentLine, b) - if container.luctx.breakFunc(r) != bcSpace: +proc skipSpace(container: Container; b, x: var int; breakFunc: BreakFunc) = + container.skipCat(b, x, breakFunc, bcSpace) + +# move to last char in category, backwards +proc lastCatRev(container: Container; b, x: var int; breakFunc: BreakFunc; + cat: BreakCategory) = + while b > 0: + let pb = b + let u = container.currentLine.prevUTF8(b) + if container.luctx.breakFunc(u) != cat: + b = pb break - b -= o - x -= r.twidth(x) + x -= u.width() + +# move to first char that is not in this category, backwards +proc skipCatRev(container: Container; b, x: var int; breakFunc: BreakFunc; + cat: BreakCategory): BreakCategory = + while b > 0: + let u = container.currentLine.prevUTF8(b) + x -= u.width() + let it = container.luctx.breakFunc(u) + if it != cat: + return it + b = -1 + return cat + +proc skipSpaceRev(container: Container; b, x: var int; breakFunc: BreakFunc): + BreakCategory = + return container.skipCatRev(b, x, breakFunc, bcSpace) proc cursorNextWord(container: Container; breakFunc: BreakFunc) = if container.numLines == 0: return - var r: Rune var b = container.currentCursorBytes() var x = container.cursorx # meow let currentCat = if b < container.currentLine.len: - container.luctx.breakFunc(container.currentLine.runeAt(b)) + var tmp = b + container.luctx.breakFunc(container.currentLine.nextUTF8(tmp)) else: bcSpace if currentCat != bcSpace: # not in space, skip chars that have the same category - while b < container.currentLine.len: - let pb = b - fastRuneAt(container.currentLine, b, r) - if container.luctx.breakFunc(r) != currentCat: - b = pb - break - x += r.twidth(x) + container.skipCat(b, x, breakFunc, currentCat) container.skipSpace(b, x, breakFunc) if b < container.currentLine.len: container.setCursorX(x) @@ -1084,19 +1096,16 @@ proc cursorPrevWord(container: Container; breakFunc: BreakFunc) = var x = container.cursorx if container.currentLine.len > 0: b = min(b, container.currentLine.len - 1) - let currentCat = if b >= 0: - container.luctx.breakFunc(container.currentLine.runeAt(b)) + var currentCat = if b >= 0: + var tmp = b + container.luctx.breakFunc(container.currentLine.nextUTF8(tmp)) else: bcSpace if currentCat != bcSpace: # not in space, skip chars that have the same category - while b >= 0: - let (r, o) = lastRune(container.currentLine, b) - if container.luctx.breakFunc(r) != currentCat: - break - b -= o - x -= r.twidth(x) - container.skipSpaceRev(b, x, breakFunc) + currentCat = container.skipCatRev(b, x, breakFunc, currentCat) + if currentCat == bcSpace: + discard container.skipSpaceRev(b, x, breakFunc) else: b = -1 if b >= 0: @@ -1119,32 +1128,33 @@ proc cursorPrevBigWord(container: Container) {.jsfunc.} = proc cursorWordEnd(container: Container; breakFunc: BreakFunc) = if container.numLines == 0: return - var r: Rune var b = container.currentCursorBytes() var x = container.cursorx var px = x # if not in space, move to the right by one if b < container.currentLine.len: let pb = b - fastRuneAt(container.currentLine, b, r) - if container.luctx.breakFunc(r) == bcSpace: + let u = container.currentLine.nextUTF8(b) + if container.luctx.breakFunc(u) == bcSpace: b = pb else: px = x - x += r.twidth(x) + x += u.twidth(x) container.skipSpace(b, x, breakFunc) # move to the last char in the current category let ob = b if b < container.currentLine.len: - let currentCat = container.luctx.breakFunc(container.currentLine.runeAt(b)) + var tmp = b + let u = container.currentLine.nextUTF8(tmp) + let currentCat = container.luctx.breakFunc(u) while b < container.currentLine.len: let pb = b - fastRuneAt(container.currentLine, b, r) - if container.luctx.breakFunc(r) != currentCat: + let u = container.currentLine.nextUTF8(b) + if container.luctx.breakFunc(u) != currentCat: b = pb break px = x - x += r.twidth(x) + x += u.twidth(x) x = px if b < container.currentLine.len or ob != b: container.setCursorX(x) @@ -1168,35 +1178,27 @@ proc cursorWordBegin(container: Container; breakFunc: BreakFunc) = if container.numLines == 0: return var b = container.currentCursorBytes() var x = container.cursorx - var px = x - var ob = b if container.currentLine.len > 0: b = min(b, container.currentLine.len - 1) if b >= 0: - let (r, o) = lastRune(container.currentLine, b) + var tmp = b + var u = container.currentLine.nextUTF8(tmp) + var currentCat = container.luctx.breakFunc(u) # if not in space, move to the left by one - if container.luctx.breakFunc(r) != bcSpace: - b -= o - px = x - x -= r.twidth(x) - container.skipSpaceRev(b, x, breakFunc) - # move to the first char in the current category - ob = b - if b >= 0: - let (r, _) = lastRune(container.currentLine, b) - let currentCat = container.luctx.breakFunc(r) - while b >= 0: - let (r, o) = lastRune(container.currentLine, b) - if container.luctx.breakFunc(r) != currentCat: - break - b -= o - px = x - x -= r.twidth(x) - x = px + if currentCat != bcSpace: + if b > 0: + u = container.currentLine.prevUTF8(b) + x -= u.width() + currentCat = container.luctx.breakFunc(u) + else: + b = -1 + if container.luctx.breakFunc(u) == bcSpace: + currentCat = container.skipSpaceRev(b, x, breakFunc) + # move to the first char in the current category + container.lastCatRev(b, x, breakFunc, currentCat) else: b = -1 - ob = -1 - if b >= 0 or ob != b: + if b >= 0: container.setCursorX(x) else: if container.cursory > 0: @@ -1994,7 +1996,6 @@ proc drawLines*(container: Container; display: var FixedGrid; hlcolor: CellColor cell.format = cf.format if bgcolor != defaultColor and cell.format.bgcolor == defaultColor: cell.format.bgcolor = bgcolor - var r: Rune var by = 0 let endy = min(container.fromy + display.height, container.numLines) for line in container.ilines(container.fromy ..< endy): @@ -2002,8 +2003,8 @@ proc drawLines*(container: Container; display: var FixedGrid; hlcolor: CellColor var i = 0 # byte in line.str # Skip cells till fromx. while w < container.fromx and i < line.str.len: - fastRuneAt(line.str, i, r) - w += r.twidth(w) + let u = line.str.nextUTF8(i) + w += u.twidth(w) let dls = by * display.width # starting position of row in display # Fill in the gap in case we skipped more cells than fromx mandates (i.e. # we encountered a double-width character.) @@ -2018,25 +2019,27 @@ proc drawLines*(container: Container; display: var FixedGrid; hlcolor: CellColor # Now fill in the visible part of the row. while i < line.str.len: let pw = w - fastRuneAt(line.str, i, r) - let rw = r.twidth(w) - w += rw + let pi = i + let u = line.str.nextUTF8(i) + let uw = u.twidth(w) + w += uw if w > container.fromx + display.width: break # die on exceeding the width limit if nf.pos != -1 and nf.pos <= pw: cf = nf nf = line.findNextFormat(pw) - if r == Rune('\t'): + if u == uint32('\t'): # Needs to be replaced with spaces, otherwise bgcolor isn't displayed. - let tk = k + rw + let tk = k + uw while k < tk: display[dls + k].str &= ' ' set_fmt display[dls + k], cf inc k else: - display[dls + k].str &= r + for j in pi ..< i: + display[dls + k].str &= line.str[j] set_fmt display[dls + k], cf - k += rw + k += uw if bgcolor != defaultColor: # Fill the screen if bgcolor is not default. while k < display.width: diff --git a/src/local/lineedit.nim b/src/local/lineedit.nim index ecfc3db8..edb6ee09 100644 --- a/src/local/lineedit.nim +++ b/src/local/lineedit.nim @@ -1,5 +1,4 @@ import std/strutils -import std/unicode import chagashi/charset import chagashi/decoder @@ -11,6 +10,7 @@ import types/winattrs import utils/luwrap import utils/strwidth import utils/twtstr +import utils/twtuni import utils/wordbreak type @@ -37,6 +37,7 @@ type hist: LineHistory histindex: int histtmp: string + luctx: LUContext redraw*: bool jsDestructor(LineEdit) @@ -48,10 +49,8 @@ func newLineHistory*(): LineHistory = func getDisplayWidth(edit: LineEdit): int = var dispw = 0 var i = edit.shifti - var r: Rune while i < edit.news.len and dispw < edit.maxwidth: - fastRuneAt(edit.news, i, r) - dispw += r.width() + dispw += edit.news.nextUTF8(i).width() return dispw proc shiftView(edit: LineEdit) = @@ -69,17 +68,14 @@ proc shiftView(edit: LineEdit) = edit.shifti = 0 else: while edit.shiftx > targetx: - let (r, len) = edit.news.lastRune(edit.shifti - 1) - edit.shiftx -= r.width() - edit.shifti -= len + let u = edit.news.prevUTF8(edit.shifti) + edit.shiftx -= u.width() edit.padding = 0 # Shift view so it contains the cursor. (act 2) if edit.shiftx < edit.cursorx - edit.maxwidth: while edit.shiftx < edit.cursorx - edit.maxwidth and edit.shifti < edit.news.len: - var r: Rune - fastRuneAt(edit.news, edit.shifti, r) - edit.shiftx += r.width() + edit.shiftx += edit.news.nextUTF8(edit.shifti).width() if edit.shiftx > edit.cursorx - edit.maxwidth: # skipped over a cell because of a double-width char edit.padding = 1 @@ -89,9 +85,9 @@ proc generateOutput*(edit: LineEdit): FixedGrid = # Make the output grid +1 cell wide, so it covers the whole input area. result = newFixedGrid(edit.promptw + edit.maxwidth + 1) var x = 0 - for r in edit.prompt.runes: - result[x].str &= $r - x += r.width() + for u in edit.prompt.points: + result[x].str.addUTF8(u) + x += u.width() if x >= result.width: break for i in 0 ..< edit.padding: if x < result.width: @@ -99,18 +95,19 @@ proc generateOutput*(edit: LineEdit): FixedGrid = inc x var i = edit.shifti while i < edit.news.len: - var r: Rune - fastRuneAt(edit.news, i, r) + let pi = i + let u = edit.news.nextUTF8(i) if not edit.hide: - let w = r.width() + let w = u.width() if x + w > result.width: break - if r.isControlChar(): + if u.isControlChar(): result[x].str &= '^' inc x - result[x].str &= char(r).getControlLetter() + result[x].str &= char(u).getControlLetter() inc x else: - result[x].str &= $r + for j in pi ..< i: + result[x].str &= edit.news[j] x += w else: if x + 1 > result.width: break @@ -143,10 +140,10 @@ proc submit(edit: LineEdit) {.jsfunc.} = proc backspace(edit: LineEdit) {.jsfunc.} = if edit.cursori > 0: - let (r, len) = edit.news.lastRune(edit.cursori - 1) - edit.news.delete(edit.cursori - len .. edit.cursori - 1) - edit.cursori -= len - edit.cursorx -= r.width() + let pi = edit.cursori + let u = edit.news.prevUTF8(edit.cursori) + edit.news.delete(edit.cursori ..< pi) + edit.cursorx -= u.width() edit.redraw = true proc write*(edit: LineEdit; s: string; cs: Charset): bool = @@ -171,7 +168,7 @@ proc write(edit: LineEdit; s: string): bool {.jsfunc.} = proc delete(edit: LineEdit) {.jsfunc.} = if edit.cursori < edit.news.len: - let len = edit.news.runeLenAt(edit.cursori) + let len = edit.news.pointLenAt(edit.cursori) edit.news.delete(edit.cursori ..< edit.cursori + len) edit.redraw = true @@ -192,55 +189,53 @@ proc kill(edit: LineEdit) {.jsfunc.} = proc backward(edit: LineEdit) {.jsfunc.} = if edit.cursori > 0: - let (r, len) = edit.news.lastRune(edit.cursori - 1) - edit.cursori -= len - edit.cursorx -= r.width() + let u = edit.news.prevUTF8(edit.cursori) + edit.cursorx -= u.width() if edit.cursorx < edit.shiftx: edit.redraw = true proc forward(edit: LineEdit) {.jsfunc.} = if edit.cursori < edit.news.len: - var r: Rune - fastRuneAt(edit.news, edit.cursori, r) - edit.cursorx += r.width() + let u = edit.news.nextUTF8(edit.cursori) + edit.cursorx += u.width() if edit.cursorx >= edit.shiftx + edit.maxwidth: edit.redraw = true proc prevWord(edit: LineEdit) {.jsfunc.} = if edit.cursori == 0: return - let ctx = LUContext() - let (r, len) = edit.news.lastRune(edit.cursori - 1) - if ctx.breaksWord(r): - edit.cursori -= len - edit.cursorx -= r.width() + let pi = edit.cursori + let u = edit.news.prevUTF8(edit.cursori) + if edit.luctx.breaksWord(u): + edit.cursorx -= u.width() + else: + edit.cursori = pi while edit.cursori > 0: - let (r, len) = edit.news.lastRune(edit.cursori - 1) - if ctx.breaksWord(r): + let pi = edit.cursori + let u = edit.news.prevUTF8(edit.cursori) + if edit.luctx.breaksWord(u): + edit.cursori = pi break - edit.cursori -= len - edit.cursorx -= r.width() + edit.cursorx -= u.width() if edit.cursorx < edit.shiftx: edit.redraw = true proc nextWord(edit: LineEdit) {.jsfunc.} = if edit.cursori >= edit.news.len: return - let ctx = LUContext() - let oc = edit.cursori - var r: Rune - fastRuneAt(edit.news, edit.cursori, r) - if ctx.breaksWord(r): - edit.cursorx += r.width() + let pi = edit.cursori + let u = edit.news.nextUTF8(edit.cursori) + if edit.luctx.breaksWord(u): + edit.cursorx += u.width() else: - edit.cursori = oc + edit.cursori = pi while edit.cursori < edit.news.len: - let pc = edit.cursori - fastRuneAt(edit.news, edit.cursori, r) - if ctx.breaksWord(r): - edit.cursori = pc + let pi = edit.cursori + let u = edit.news.nextUTF8(edit.cursori) + if edit.luctx.breaksWord(u): + edit.cursori = pi break - edit.cursorx += r.width() + edit.cursorx += u.width() if edit.cursorx >= edit.shiftx + edit.maxwidth: edit.redraw = true @@ -254,18 +249,17 @@ proc clearWord(edit: LineEdit) {.jsfunc.} = proc killWord(edit: LineEdit) {.jsfunc.} = if edit.cursori >= edit.news.len: return - let oc = edit.cursori - let ox = edit.cursorx - edit.nextWord() - if edit.cursori != oc: - if edit.cursori < edit.news.len: - let len = edit.news.runeLenAt(edit.cursori) - edit.news.delete(oc ..< edit.cursori + len) - else: - edit.news.delete(oc ..< edit.cursori) - edit.cursori = oc - edit.cursorx = ox - edit.redraw = true + var i = edit.cursori + var u = edit.news.nextUTF8(i) + if not edit.luctx.breaksWord(u): + while i < edit.news.len: + let pi = i + let u = edit.news.nextUTF8(i) + if edit.luctx.breaksWord(u): + i = pi + break + edit.news.delete(edit.cursori ..< i) + edit.redraw = true proc begin(edit: LineEdit) {.jsfunc.} = edit.cursori = 0 @@ -310,7 +304,7 @@ proc windowChange*(edit: LineEdit; attrs: WindowAttributes) = edit.maxwidth = attrs.width - edit.promptw - 1 proc readLine*(prompt, current: string; termwidth: int; disallowed: set[char]; - hide: bool; hist: LineHistory): LineEdit = + hide: bool; hist: LineHistory; luctx: LUContext): LineEdit = let promptw = prompt.width() return LineEdit( prompt: prompt, @@ -324,7 +318,8 @@ proc readLine*(prompt, current: string; termwidth: int; disallowed: set[char]; # - 1, so that the cursor always has place maxwidth: termwidth - promptw - 1, hist: hist, - histindex: hist.lines.len + histindex: hist.lines.len, + luctx: luctx ) proc addLineEditModule*(ctx: JSContext) = diff --git a/src/local/pager.nim b/src/local/pager.nim index 035ec2d7..3822eb77 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -7,7 +7,6 @@ import std/posix import std/selectors import std/sets import std/tables -import std/unicode import chagashi/charset import config/chapath @@ -52,6 +51,7 @@ import utils/mimeguess import utils/regexutils import utils/strwidth import utils/twtstr +import utils/twtuni type LineMode* = enum @@ -284,7 +284,7 @@ proc setLineEdit(pager: Pager; mode: LineMode; current = ""; hide = false; if pager.term.isatty() and pager.config.input.use_mouse: pager.term.disableMouse() pager.lineedit = readLine($mode & extraPrompt, current, pager.attrs.width, - {}, hide, hist) + {}, hide, hist, pager.luctx) pager.linemode = mode proc clearLineEdit(pager: Pager) = @@ -387,19 +387,19 @@ proc writeStatusMessage(pager: Pager; str: string; format = Format(); if i >= e: return i pager.status.redraw = true - for r in str.runes: - let w = r.width() + for u in str.points: + let w = u.width() if i + w >= e: pager.status.grid[i].format = format pager.status.grid[i].str = $clip inc i # Note: we assume `clip' is 1 cell wide break - if r.isControlChar(): + if u.isControlChar(): pager.status.grid[i].str = "^" - pager.status.grid[i + 1].str = $getControlLetter(char(r)) + pager.status.grid[i + 1].str = $getControlLetter(char(u)) pager.status.grid[i + 1].format = format else: - pager.status.grid[i].str = $r + pager.status.grid[i].str = u.toUTF8() pager.status.grid[i].format = format i += w result = i @@ -461,9 +461,8 @@ proc drawBuffer*(pager: Pager; container: Container; ofile: File) = for f in line.formats: let si = i while x < f.pos: - var r: Rune - fastRuneAt(line.str, i, r) - x += r.width() + let u = line.str.nextUTF8(i) + x += u.width() let outstr = line.str.substr(si, i - 1) s &= pager.term.processOutputString(outstr, w) s &= pager.term.processFormat(format, f.format) @@ -576,6 +575,8 @@ proc initImages(pager: Pager; container: Container) = dispw = min(width + xpx, maxwpx) - xpx let ypx = (image.y - container.fromy) * pager.attrs.ppl erry = -min(ypx, 0) mod 6 + if dispw <= offx: + continue let cached = container.findCachedImage(image, offx, erry, dispw) let imageId = image.bmp.imageId if cached == nil: diff --git a/src/local/term.nim b/src/local/term.nim index 263e6363..2f31104a 100644 --- a/src/local/term.nim +++ b/src/local/term.nim @@ -4,7 +4,6 @@ import std/posix import std/strutils import std/tables import std/termios -import std/unicode import bindings/termcap import chagashi/charset |