diff options
-rw-r--r-- | ranger/ext/utfwidth.py | 121 | ||||
-rw-r--r-- | ranger/gui/bar.py | 15 | ||||
-rw-r--r-- | ranger/gui/curses_shortcuts.py | 30 | ||||
-rw-r--r-- | ranger/gui/widgets/browsercolumn.py | 8 | ||||
-rw-r--r-- | test/tc_utfwidth.py | 7 |
5 files changed, 77 insertions, 104 deletions
diff --git a/ranger/ext/utfwidth.py b/ranger/ext/utfwidth.py index 5c850607..a506c676 100644 --- a/ranger/ext/utfwidth.py +++ b/ranger/ext/utfwidth.py @@ -18,80 +18,49 @@ # ---- # This file contains portions of code from cmus (uchar.c). -""" -This module provides functions that operate with the width of characters -and strings rather than characters or bytes. -""" - -import sys - NARROW = 1 WIDE = 2 -if sys.version > '3': - def uwid(string, count=-1): - """Return the width of a string""" - width = 0 - for c in string: - width += utf_char_width(c) - count -= 1 - if not count: - break - return width - - def uchars(string): - """Return a list with one string for each character""" - return list(string) +def uwid(string): + """Return the width of a string""" + end = len(string) + i = 0 + width = 0 + while i < end: + bytelen = utf_byte_length(string[i:]) + width += utf_char_width(string[i:i+bytelen]) + i += bytelen + return width - utf_ord = ord -else: - def uwid(string, count=-1): - """Return the width of a string""" - end = len(string) - i = 0 - width = 0 - while i < end and count: - bytelen = _utf_byte_length(string[i:]) - width += utf_char_width(string[i:i+bytelen]) - i += bytelen - count -= 1 - return width +def uchars(string): + """Return a list with one string for each character""" + end = len(string) + i = 0 + result = [] + while i < end: + bytelen = utf_byte_length(string[i:]) + result.append(string[i:i+bytelen]) + i += bytelen + return result - def uchars(string): - """Return a list with one string for each character""" - end = len(string) - i = 0 - result = [] - while i < end: - bytelen = _utf_byte_length(string[i:]) - result.append(string[i:i+bytelen]) - i += bytelen - return result - - def _utf_byte_length(string): - """Return the byte length of one utf character""" - firstord = ord(string[0]) - if firstord < 0b01111111: - return 1 - if firstord < 0b10111111: - return 1 # invalid - if firstord < 0b11011111: - return 2 - if firstord < 0b11101111: - return 3 - if firstord < 0b11110100: - return 4 +def utf_byte_length(string): + """Return the byte length of one utf character""" + firstord = ord(string[0]) + if firstord < 0b01111111: + return 1 + if firstord < 0b10111111: return 1 # invalid - - def utf_ord(char): - value = 0 - for byte in char: - value = (value << 6) | (ord(byte) & 0b00111111) - return value + if firstord < 0b11011111: + return 2 + if firstord < 0b11101111: + return 3 + if firstord < 0b11110100: + return 4 + return 1 # invalid def utf_char_width(string): """Return the width of a single character""" - u = utf_ord(string) + u = _utf_char_to_int(string) if u < 0x1100: return NARROW # Hangul Jamo init. constonants @@ -132,19 +101,9 @@ def utf_char_width(string): return WIDE return NARROW # invalid (?) -def uslice(string, start=0, end=1000000000): - """ - Returns a sliced string. - - Works like string[start:end] except that one step represents - one narrow character in a monospaced character grid. - """ - chars = [] - for c in uchars(string): - c_wid = utf_char_width(c) - if c_wid == NARROW: - chars.append(c) - elif c_wid == WIDE: - chars.append("") - chars.append(c) - return "".join(chars[start:end]) +def _utf_char_to_int(string): + # Squash the last 6 bits of each byte together to an integer + u = 0 + for c in string: + u = (u << 6) | (ord(c) & 0b00111111) + return u diff --git a/ranger/gui/bar.py b/ranger/gui/bar.py index 42e1f1c4..41cc8133 100644 --- a/ranger/gui/bar.py +++ b/ranger/gui/bar.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from ranger.ext.utfwidth import uwid, uslice, utf_char_width +from ranger.ext.utfwidth import uwid class Bar(object): left = None @@ -75,11 +75,10 @@ class Bar(object): # Shrink items to a minimum size of 1 until there is enough room. for item in self.left: if not item.fixed: - itemlen = uwid(item.string) - minimal_width = uwid(item.string, count=1) - if oversize > itemlen - minimal_width: - item.cut_off_to(minimal_width) - oversize -= (itemlen - minimal_width) + itemlen = len(item) + if oversize > itemlen - 1: + item.cut_off_to(1) + oversize -= (itemlen - 1) else: item.cut_off(oversize) break @@ -133,10 +132,10 @@ class ColoredString(object): def cut_off(self, n): if n >= 1: - self.string = uslice(self.string, 0, -n) + self.string = self.string[:-n] def cut_off_to(self, n): - self.string = uslice(self.string, 0, n) + self.string = self.string[:n] def __len__(self): return uwid(self.string) diff --git a/ranger/gui/curses_shortcuts.py b/ranger/gui/curses_shortcuts.py index e5683b66..3df45700 100644 --- a/ranger/gui/curses_shortcuts.py +++ b/ranger/gui/curses_shortcuts.py @@ -44,26 +44,38 @@ class CursesShortcuts(SettingsAware): addstr(*args) -- failsafe version of self.win.addstr(*args) """ - def _addxyz_wrapper(self, function, args): + def addstr(self, *args): try: - function(*args) + self.win.addstr(*args) except (_curses.error, TypeError): pass except UnicodeEncodeError: try: - function(*(obj.encode('utf8') if hasattr(obj, 'encode') \ - else obj for obj in args)) + self.win.addstr(*(ascii_only(obj) for obj in args)) except (_curses.error, TypeError): pass - def addstr(self, *args): - self._addxyz_wrapper(self.win.addstr, args) - def addnstr(self, *args): - self._addxyz_wrapper(self.win.addnstr, args) + try: + self.win.addnstr(*args) + except (_curses.error, TypeError): + pass + except UnicodeEncodeError: + try: + self.win.addnstr(*(ascii_only(obj) for obj in args)) + except (_curses.error, TypeError): + pass def addch(self, *args): - self._addxyz_wrapper(self.win.addch, args) + try: + self.win.addch(*args) + except (_curses.error, TypeError): + pass + except UnicodeEncodeError: + try: + self.win.addch(*(ascii_only(obj) for obj in args)) + except (_curses.error, TypeError): + pass def color(self, *keys): """Change the colors from now on.""" diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py index 6021d622..d617e64e 100644 --- a/ranger/gui/widgets/browsercolumn.py +++ b/ranger/gui/widgets/browsercolumn.py @@ -20,7 +20,6 @@ from time import time from . import Widget from .pager import Pager from ranger.fsobject import BAD_INFO -from ranger.ext.utfwidth import uslice class BrowserColumn(Pager): main_column = False @@ -249,13 +248,14 @@ class BrowserColumn(Pager): this_color.append('link') this_color.append(drawn.exists and 'good' or 'bad') + string = drawn.basename if self.main_column: if tagged: - self.addstr(line, 0, uslice(text, 0, self.wid - 2)) + self.addnstr(line, 0, text, self.wid - 2) elif self.wid > 1: - self.addstr(line, 1, uslice(text, 0, self.wid - 2)) + self.addnstr(line, 1, text, self.wid - 2) else: - self.addstr(line, 0, uslice(text, 0, self.wid)) + self.addnstr(line, 0, text, self.wid) if self.display_infostring and drawn.infostring \ and self.settings.display_size_in_main_column: diff --git a/test/tc_utfwidth.py b/test/tc_utfwidth.py index fba9f783..0288c17b 100644 --- a/test/tc_utfwidth.py +++ b/test/tc_utfwidth.py @@ -29,6 +29,11 @@ a_katakana = "ア" # width = 2, bytes = 3 # need one with width = 1 & bytes = 3 class Test(TestCase): + def test_utf_byte_length(self): + self.assertEqual(1, utf_byte_length(a_ascii)) + self.assertEqual(2, utf_byte_length(a_umlaut)) + self.assertEqual(3, utf_byte_length(a_katakana)) + def test_uwid(self): self.assertEqual(1, uwid(a_ascii)) self.assertEqual(1, uwid(a_umlaut)) @@ -37,7 +42,5 @@ class Test(TestCase): self.assertEqual(4, uwid("asdf")) self.assertEqual(5, uwid("löööl")) self.assertEqual(6, uwid("バババ")) - self.assertEqual(1, uwid("äsdf", count=1)) - self.assertEqual(2, uwid("バババ", count=1)) if __name__ == '__main__': main() |