Exemple #1
0
 def textwidth(text):
     if isinstance(text, unicode):
         length = wcwidth.wcswidth(text)
         return len(text) if length == -1 else length
     else:
         text = text.decode("utf-8", "replace")
         length = wcwidth.wcswidth(text)
         return len(text) if length == -1 else length
Exemple #2
0
def _visible_width(s):
    """Visible width of a printed string. ANSI color codes are removed.

    >>> _visible_width('\x1b[31mhello\x1b[0m'), _visible_width("world")
    (5, 5)

    """
    if isinstance(s, _text_type) or isinstance(s, _binary_type):
        return wcswidth(_strip_invisible(s))
    else:
        return wcswidth(_text_type(s))
Exemple #3
0
 def force_text(self, text, prompt=False):
     if isinstance(text, str):
         text = text.decode('utf-8')
     if prompt:
         text_length = wcswidth(text + self.page_index)
         ret = text + (self.width - text_length) * u' ' + self.page_index
     else:
         text_length = wcswidth(text)
         ret = text + (self.width - text_length) * u' '
     # XXX stdout = unicode -> ansii NG(maybe ansii encode fail)
     # XXX stdout = unicode -> str OK
     return ret.encode('utf-8')
Exemple #4
0
def _get_line_with_reprcrash_message(config, rep, termwidth):
    """Get summary line for a report, trying to add reprcrash message."""
    from wcwidth import wcswidth

    verbose_word = rep._get_verbose_word(config)
    pos = _get_pos(config, rep)

    line = "%s %s" % (verbose_word, pos)
    len_line = wcswidth(line)
    ellipsis, len_ellipsis = "...", 3
    if len_line > termwidth - len_ellipsis:
        # No space for an additional message.
        return line

    try:
        msg = rep.longrepr.reprcrash.message
    except AttributeError:
        pass
    else:
        # Only use the first line.
        i = msg.find("\n")
        if i != -1:
            msg = msg[:i]
        len_msg = wcswidth(msg)

        sep, len_sep = " - ", 3
        max_len_msg = termwidth - len_line - len_sep
        if max_len_msg >= len_ellipsis:
            if len_msg > max_len_msg:
                max_len_msg -= len_ellipsis
                msg = msg[:max_len_msg]
                while wcswidth(msg) > max_len_msg:
                    msg = msg[:-1]
                if six.PY2:
                    # on python 2 systems with narrow unicode compilation, trying to
                    # get a single character out of a multi-byte unicode character such as
                    # u'😄' will result in a High Surrogate (U+D83D) character, which is
                    # rendered as u'�'; in this case we just strip that character out as it
                    # serves no purpose being rendered
                    try:
                        surrogate = six.unichr(0xD83D)
                        msg = msg.rstrip(surrogate)
                    except ValueError:  # pragma: no cover
                        # Jython cannot represent this lone surrogate at all (#5256):
                        # ValueError: unichr() arg is a lone surrogate in range
                        #     (0xD800, 0xDFFF) (Jython UTF-16 encoding)
                        # ignore this case as it shouldn't appear in the string anyway
                        pass
                msg += ellipsis
            line += sep + msg
    return line
Exemple #5
0
    def format_field(self, value, format_spec):
        if not isinstance(value, str):
            # If `value` is not a string use format built-in
            return format(value, format_spec)
        if format_spec == '':
            # If `format_spec` is empty we just return the `value` string
            return value

        print_length = wcwidth.wcswidth(value)
        if len(value) == print_length:
            return format(value, format_spec)

        fill, align, width, format_spec = UnicodeFormatter.parse_align(format_spec)
        if width == 0:
            return value
        formatted_value = format(value, format_spec)
        pad_len = width - print_length
        if pad_len <= 0:
            return formatted_value
        left_pad = ''
        right_pad = ''
        if align in '<=':
            right_pad = fill * pad_len
        elif align == '>':
            left_pad = fill * pad_len
        elif align == '^':
            left_pad = fill * math.floor(pad_len/2)
            right_pad = fill * math.ceil(pad_len/2)
        return ''.join((left_pad, formatted_value, right_pad))
    def __str__(self):
        answer = ""
        skip_next = False
        for i, line in enumerate(self.field):
            for j, c in enumerate(line):
                fg_ansi = ""
                bg_ansi = ""
                stop = ""

                if self.field[i][j].foreground:
                    fg_ansi = '\033[38;2;%s;%s;%sm' % rgb_from_str(self.field[i][j].foreground)
                    stop = colored.attr("reset")

                if self.field[i][j].background:
                    bg_ansi = '\033[48;2;%s;%s;%sm' % rgb_from_str(self.field[i][j].background)
                    stop = colored.attr("reset")

                char = c.char or " "
                if not skip_next:
                    answer += fg_ansi + bg_ansi + char.encode('utf-8') + stop
                skip_next = wcswidth(char) == 2

            # answer += "...\n"
            answer += "\n"
        return answer
Exemple #7
0
    def erase(self, string, keypress=chr(127)):
        """ .. method:: erase(string, keypress=chr(127)) -> string

            Returns sequence for erasing ``string`` preceeding cursor given
            the erase character ``keypressed`` (one of chr(127) or 8) has
            been used to perform deletion, assisting predicted cursor
            movement of sessions using remote line editing with echo off.
        """
        assert keypress in (chr(127), chr(8)), chr
        string_disp = "".join(
            (
                (_char if self.stream.can_write(_char) and _char.isprintable() else name_unicode(_char))
                for _char in string
            )
        )
        vtlen = wcwidth.wcswidth(string_disp)
        assert vtlen >= 0, string

        # non-BSD clients will echo
        if self.stream.will_echo:
            return ("\b" * vtlen) + "\x1b[K"

        # BSD has strange behavior for line editing with local echo:
        if keypress == chr(127):
            # (1) '^?' actually advances the cursor right one cell,
            return "\b" + ("\b" * vtlen) + "\x1b[K"
        else:
            # (2) whereas '^h' moves the cursor left one (displayable) cell !
            return "\b" * (vtlen - 1) + "\x1b[K"
def monospaced_width(text):
    r"""
    Return the number of character cells that this string is likely to occupy
    when displayed in a monospaced, modern, Unicode-aware terminal emulator.
    We refer to this as the "display width" of the string.

    This can be useful for formatting text that may contain non-spacing
    characters, or CJK characters that take up two character cells.

    Returns -1 if the string contains a non-printable or control character.

    >>> monospaced_width('ちゃぶ台返し')
    12
    >>> len('ちゃぶ台返し')
    6
    >>> monospaced_width('owl\N{SOFT HYPHEN}flavored')
    12
    >>> monospaced_width('example\x80')
    -1

    A more complex example: The Korean word 'ibnida' can be written with 3
    pre-composed characters or 7 jamo. Either way, it *looks* the same and
    takes up 6 character cells.

    >>> monospaced_width('입니다')
    6
    >>> monospaced_width('\u110b\u1175\u11b8\u1102\u1175\u1103\u1161')
    6
    """
    # NFC-normalize the text first, so that we don't need special cases for
    # Hangul jamo.
    return wcswidth(normalize('NFC', text))
Exemple #9
0
def _padleft(width, s, has_invisible=True):
    """Flush right.

    >>> _padleft(6, '\u044f\u0439\u0446\u0430') == '  \u044f\u0439\u0446\u0430'
    True

    """
    lwidth = width - wcswidth(_strip_invisible(s) if has_invisible else s)
    return ' ' * lwidth + s
Exemple #10
0
def _padright(width, s, has_invisible=True):
    """Flush left.

    >>> _padright(6, '\u044f\u0439\u0446\u0430') == '\u044f\u0439\u0446\u0430  '
    True

    """
    rwidth = width - wcswidth(_strip_invisible(s) if has_invisible else s)
    return s + ' ' * rwidth
def format_value_text(val, encoding, colormap, quote=False, **_):
    escapedval = val.replace(u'\\', u'\\\\')
    if quote:
        escapedval = escapedval.replace("'", "''")
    escapedval = unicode_controlchars_re.sub(_show_control_chars, escapedval)
    bval = escapedval.encode(encoding, 'backslashreplace')
    if quote:
        bval = "'%s'" % bval

    return bval if colormap is NO_COLOR_MAP else color_text(bval, colormap, wcwidth.wcswidth(bval.decode(encoding)))
Exemple #12
0
def _padboth(width, s, has_invisible=True):
    """Center string.

    >>> _padboth(6, '\u044f\u0439\u0446\u0430') == ' \u044f\u0439\u0446\u0430 '
    True

    """
    xwidth = width - wcswidth(_strip_invisible(s) if has_invisible else s)
    lwidth = xwidth // 2
    rwidth =  0 if xwidth <= 0 else lwidth + xwidth % 2
    return ' ' * lwidth + s + ' ' * rwidth
Exemple #13
0
def width_aware_slice(s, start, end, replacement_char=u' '):
    divides = [wcwidth.wcswidth(s, i) for i in range(len(s)+1)]

    new_chunk_chars = []
    for char, char_start, char_end in zip(s, divides[:-1], divides[1:]):
        if char_start >= start and char_end <= end:
            new_chunk_chars.append(char)
        else:
            new_chunk_chars.extend(replacement_char * interval_overlap(char_start, char_end, start, end))

    return ''.join(new_chunk_chars)
Exemple #14
0
def test_combining_cafe():
    u"""Phrase cafe + COMBINING ACUTE ACCENT is café of length 4."""
    phrase = u"cafe\u0301"
    expect_length_each = (1, 1, 1, 1, 0)
    expect_length_phrase = 4

    # exercise,
    length_each = tuple(map(wcwidth.wcwidth, phrase))
    length_phrase = wcwidth.wcswidth(phrase, len(phrase))

    # verify,
    assert length_each == expect_length_each
    assert length_phrase == expect_length_phrase
Exemple #15
0
def test_combining_spacing():
    u"""Balinese kapal (ship) is ᬓᬨᬮ᭄ of length 4."""
    phrase = u"\u1B13\u1B28\u1B2E\u1B44"
    expect_length_each = (1, 1, 1, 1)
    expect_length_phrase = 4

    # exercise,
    length_each = tuple(map(wcwidth.wcwidth, phrase))
    length_phrase = wcwidth.wcswidth(phrase, len(phrase))

    # verify,
    assert length_each == expect_length_each
    assert length_phrase == expect_length_phrase
Exemple #16
0
def test_combining_enclosing():
    u"""CYRILLIC CAPITAL LETTER A + COMBINING CYRILLIC HUNDRED THOUSANDS SIGN is А҈ of length 1."""
    phrase = u"\u0410\u0488"
    expect_length_each = (1, 0)
    expect_length_phrase = 1

    # exercise,
    length_each = tuple(map(wcwidth.wcwidth, phrase))
    length_phrase = wcwidth.wcswidth(phrase, len(phrase))

    # verify,
    assert length_each == expect_length_each
    assert length_phrase == expect_length_phrase
Exemple #17
0
    def show_top_status(self):
        """Show top status row."""
        self.header_win.erase()
        size = self.get_size()
        display = self.app.config["display"]
        head_parts = []
        if display["show_app_name"]:
            name_str = "Suplemon Editor v{0} -".format(self.app.version)
            if self.app.config["app"]["use_unicode_symbols"]:
                logo = "\U0001f34b"  # Fancy lemon
                name_str = " {0} {1}".format(logo, name_str)
            head_parts.append(name_str)

        # Add module statuses to the status bar in descending order
        module_keys = sorted(self.app.modules.modules.keys())
        for name in module_keys:
            module = self.app.modules.modules[name]
            if module.options["status"] == "top":
                status = module.get_status()
                if status:
                    head_parts.append(status)

        if display["show_file_list"]:
            head_parts.append(self.file_list_str())

        head = " ".join(head_parts)
        head = head + (" " * (size[0]-wcswidth(head)-1))
        head_width = wcswidth(head)
        if head_width > size[0]:
            head = head[:size[0]-head_width]
        try:
            if self.app.config["display"]["invert_status_bars"]:
                self.header_win.addstr(0, 0, head, curses.color_pair(0) | curses.A_REVERSE)
            else:
                self.header_win.addstr(0, 0, head, curses.color_pair(0))
        except curses.error:
            pass
        self.header_win.refresh()
Exemple #18
0
def print_status(*args, fmt=fmt_status):
    if isatty() and not opts.verbose:
        msg = " ".join(args)
        msg = msg.replace("\n", " ")
        out = ""
        out += "\033[1G" # cursor to column 1
        out += "\033[0J" # erase below
        out += fmt_status(msg)
        lines = math.ceil(wcswidth(msg) / ttywidth())
        if lines > 1:
            out += "\033[%dA" % (lines-1) # cursor up 1
        sys.stderr.write(out)
        if not args:
            sys.stderr.flush()
Exemple #19
0
def test_combining_width_negative_1():
    """Simple test combining reports total width of 4."""
    # given,
    phrase = u'--\u05bf--'
    expect_length_each = (1, 1, 0, 1, 1)
    expect_length_phrase = 4

    # exercise,
    length_each = tuple(map(wcwidth.wcwidth, phrase))
    length_phrase = wcwidth.wcswidth(phrase, len(phrase))

    # verify,
    assert length_each == expect_length_each
    assert length_phrase == expect_length_phrase
Exemple #20
0
def test_control_c0_width_negative_1():
    """CSI (Control sequence initiate) reports width -1."""
    # given,
    phrase = u'\x1b[0m'
    expect_length_each = (-1, 1, 1, 1)
    expect_length_phrase = -1

    # exercise,
    length_each = tuple(map(wcwidth.wcwidth, phrase))
    length_phrase = wcwidth.wcswidth(phrase, len(phrase))

    # verify,
    assert length_each == expect_length_each
    assert length_phrase == expect_length_phrase
Exemple #21
0
def test_null_width_0():
    """NULL (0) reports width 0."""
    # given,
    phrase = u'abc\x00def'
    expect_length_each = (1, 1, 1, 0, 1, 1, 1)
    expect_length_phrase = sum(expect_length_each)

    # exercise,
    length_each = tuple(map(wcwidth.wcwidth, phrase))
    length_phrase = wcwidth.wcswidth(phrase, len(phrase))

    # verify,
    assert length_each == expect_length_each
    assert length_phrase == expect_length_phrase
def pad_fields(row, widths):
    """Pads fields of the given row, so each field lines up nicely with the
    others.

    """
    wgaps = [wcwidth.wcswidth(c) - len(c) for c in row]
    widths = [w-wgaps[i] for i, w in enumerate(widths)]
    widths = [(' %-' + str(w) + 's ') for w in widths]

    # Pad all fields using the calculated widths
    new_row = []
    for i in range(len(row)):
        col = row[i]
        col = widths[i] % col.strip()
        new_row.append(col)
    return new_row
Exemple #23
0
    def __init__(self, text=None, align='left'):
        """Create an instance of Entry

        Exceptions: TypeError, ValueError
        """
        if text is None:
            text = u''
        else:
            if not isinstance(text, six.text_type):
                raise TypeError('text must be a unicode string')

        if align not in ['left', 'right', 'center']:
            raise ValueError('align should be left, right or center')

        self.text = text
        self.align = align
        self.width = wcswidth(text)
Exemple #24
0
def test_wcswidth_substr():
    """
    Test wcswidth() optional 2nd parameter, ``n``.

    ``n`` determines at which position of the string
    to stop counting length.
    """
    # given,
    phrase = u'コンニチハ, セカイ!'
    end = 7
    expect_length_each = (2, 2, 2, 2, 2, 1, 1,)
    expect_length_phrase = sum(expect_length_each)

    # exercise,
    length_phrase = wcwidth.wcswidth(phrase, end)

    # verify,
    assert length_phrase == expect_length_phrase
Exemple #25
0
def _drawtext(options, args):

    if select.select([sys.stdin, ], [], [], 0.0):
        text = sys.stdin.read()
    elif len(args) == 0 or args[0] == '-':
        text = sys.stdin.read()

    import re
    text = re.sub('[\x00-\x1f\x7f]', '', text)

    text = unicode(text, "utf-8", "ignore")
    from PIL import Image
    from PIL import ImageDraw
    from PIL import ImageFont

    if options.font:
        fontfile = options.font
    else:
        import inspect
        name = "unifont-5.1.20080907.ttf"
        filename = inspect.getfile(inspect.currentframe())
        dirpath = os.path.abspath(os.path.dirname(filename))
        fontfile = os.path.join(dirpath, name)

    font = ImageFont.truetype(filename=fontfile,
                              size=50)
    w, h = font.getsize(text)
    image = Image.new('RGB', (w, h + 2), (255, 255, 255))
    draw = ImageDraw.Draw(image)
    draw.text((0, 0), text, font=font, fill=(0, 0, 0))
    import wcwidth
    columns = wcwidth.wcswidth(text)
    rows = 1

    writer = DrcsWriter(f8bit=options.f8bit)
    writer.draw(image,
                columns,
                rows,
                options.negate,
                options.use_unicode,
                ncolor=options.ncolor,
                defonly=options.defonly,
                startoffset=options.startoffset)
Exemple #26
0
def test_hello_jp():
    u"""
    Width of Japanese phrase: コンニチハ, セカイ!

    Given a phrase of 5 and 3 Katakana ideographs, joined with
    3 English-ASCII punctuation characters, totaling 11, this
    phrase consumes 19 cells of a terminal emulator.
    """
    # given,
    phrase = u'コンニチハ, セカイ!'
    expect_length_each = (2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 1)
    expect_length_phrase = sum(expect_length_each)

    # exercise,
    length_each = tuple(map(wcwidth.wcwidth, phrase))
    length_phrase = wcwidth.wcswidth(phrase)

    # verify,
    assert length_each == expect_length_each
    assert length_phrase == expect_length_phrase
Exemple #27
0
def test_hello_jp():
    u"""
    Width of Japanese phrase: コンニチハ, セカイ!

    Given a phrase of 5 and 3 Katakana ideographs, joined with
    3 English-ASCII punctuation characters, totaling 11, this
    phrase consumes 19 cells of a terminal emulator.
    """
    # given,
    phrase = u'コンニチハ, セカイ!'
    expect_length_each = (2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 1)
    expect_length_phrase = sum(expect_length_each)

    # exercise,
    length_each = tuple(map(wcwidth.wcwidth, phrase))
    length_phrase = wcwidth.wcswidth(phrase)

    # verify,
    assert length_each == expect_length_each
    assert length_phrase == expect_length_phrase
Exemple #28
0
 def width_aware_slice(self, index):
     """Slice based on the number of columns it would take to display the substring."""
     if wcswidth(self.s) == -1:
         raise ValueError('bad values for width aware slicing')
     index = normalize_slice(self.width, index)
     counter = 0
     parts = []
     for chunk in self.chunks:
         if index.start < counter + chunk.width and index.stop > counter:
             start = max(0, index.start - counter)
             end = min(index.stop - counter, chunk.width)
             if end - start == chunk.width:
                 parts.append(chunk)
             else:
                 s_part = width_aware_slice(chunk.s, max(0, index.start - counter), index.stop - counter)
                 parts.append(Chunk(s_part, chunk.atts))
         counter += chunk.width
         if index.stop < counter:
             break
     return FmtStr(*parts) if parts else fmtstr('')
Exemple #29
0
 def width_aware_slice(self, index):
     """Slice based on the number of columns it would take to display the substring"""
     if wcwidth.wcswidth(self.s) == -1:
         raise ValueError('bad values for width aware slicing')
     index = normalize_slice(self.width, index)
     counter = 0
     parts = []
     for chunk in self.basefmtstrs:
         if index.start < counter + chunk.width and index.stop > counter:
             start = max(0, index.start - counter)
             end = min(index.stop - counter, chunk.width)
             if end - start == chunk.width:
                 parts.append(chunk)
             else:
                 s_part = width_aware_slice(chunk.s, max(0, index.start - counter), index.stop - counter)
                 parts.append(Chunk(s_part, chunk.atts))
         counter += chunk.width
         if index.stop < counter:
             break
     return FmtStr(*parts) if parts else fmtstr('')
Exemple #30
0
    def request(self, max_width):
        # type: (int) -> Optional[Tuple[int, Chunk]]
        """Requests a sub-chunk of max_width or shorter. Returns None if no chunks left."""
        if max_width < 1:
            raise ValueError('requires positive integer max_width')

        s = self.chunk.s
        length = len(s)

        if self.internal_offset == len(s):
            return None

        width = 0
        start_offset = i = self.internal_offset
        replacement_char = u' '

        while True:
            w = wcswidth(s[i])

            # If adding a character puts us over the requested width, return what we've got so far
            if width + w > max_width:
                self.internal_offset = i  # does not include ith character
                self.internal_width += width

                # if not adding it us makes us short, this must have been a double-width character
                if width < max_width:
                    assert width + 1 == max_width, 'unicode character width of more than 2!?!'
                    assert w == 2, 'unicode character of width other than 2?'
                    return (width + 1, Chunk(s[start_offset:self.internal_offset] + replacement_char,
                                             atts=self.chunk.atts))
                return (width, Chunk(s[start_offset:self.internal_offset], atts=self.chunk.atts))
            # otherwise add this width
            width += w

            # If one more char would put us over, return whatever we've got
            if i + 1 == length:
                self.internal_offset = i + 1  # beware the fencepost, i is an index not an offset
                self.internal_width += width
                return (width, Chunk(s[start_offset:self.internal_offset], atts=self.chunk.atts))
            # otherwise attempt to add the next character
            i += 1
Exemple #31
0
def width_aware_slice(s, start, end, replacement_char=u' '):
    # type: (Text, int, int, Text) -> Text
    """
    >>> width_aware_slice(u'a\uff25iou', 0, 2)[1] == u' '
    True
    """
    divides = [0]
    for c in s:
        divides.append(divides[-1] + wcswidth(c))

    new_chunk_chars = []
    for char, char_start, char_end in zip(s, divides[:-1], divides[1:]):
        if char_start == start and char_end == start:
            continue  # don't use zero-width characters at the beginning of a slice
                      # (combining characters combine with the chars before themselves)
        elif char_start >= start and char_end <= end:
            new_chunk_chars.append(char)
        else:
            new_chunk_chars.extend(replacement_char * interval_overlap(char_start, char_end, start, end))

    return ''.join(new_chunk_chars)
Exemple #32
0
def get_text_index(text: str) -> int:
    """
    计算文本最终显示的真实长度
    Keyword arguments:
    text -- 要进行长度计算的文本
    """
    text_index = 0
    style_width = 0
    style_name_list = list(game_config.config_font_data.keys())
    for i in range(0, len(style_name_list)):
        style_text_head = "<" + style_name_list[i] + ">"
        style_text_tail = "</" + style_name_list[i] + ">"
        if style_text_head in text:
            text = text.replace(style_text_head, "")
            text = text.replace(style_text_tail, "")
    for i in range(len(text)):
        text_index += wcswidth(text[i])
    now_width = text_index + style_width
    if now_width < 0:
        now_width = 0
    return now_width
  def AddColumn(self, column_name, align='l', **kwds):
    """Add a column to this table.

    Args:
      column_name: Name for the new column.
      align: (optional, default: 'l') Alignment for the new column entries.

    Raises:
      FormatterException: If the table already has any rows, or if the
        provided alignment is invalid.
    """
    if self:
      raise FormatterException(
          'Cannot add a new column to an initialized table')
    if align not in ('l', 'c', 'r'):
      raise FormatterException('Invalid column alignment: %s' % (align,))
    lines = six.text_type(column_name).split('\n')
    self.column_widths.append(max(wcwidth.wcswidth(line) for line in lines))
    self.column_alignments.append(align)
    self.column_names.append(column_name)
    self.header_height = max(len(lines), self.header_height)
Exemple #34
0
def print_status(*args, fmt=fmt_status, wrap=True):
    if not stderr_tty():
        return
    out = ""
    out += "\033[1G"  # cursor to column 1
    #out += "\033[K" # erase to right (XXX: this was used in _truncated)
    out += "\033[0J"  # erase below
    if args:
        msg = " ".join(args)
        msg = msg.replace("\n", " ")
        if wrap:
            out += fmt_status(msg)
            lines = math.ceil(wcswidth(msg) / stderr_width())
            if lines > 1:
                out += "\033[%dA" % (lines - 1)  # cursor up 1
        else:
            msg = wctruncate(msg, stderr_width())
            out += fmt_status(msg)
    sys.stderr.write(out)
    if not args:
        sys.stderr.flush()
Exemple #35
0
def monospaced_width(text: str) -> int:
    r"""
    Return the number of character cells that this string is likely to occupy
    when displayed in a monospaced, modern, Unicode-aware terminal emulator.
    We refer to this as the "display width" of the string.

    This can be useful for formatting text that may contain non-spacing
    characters, or CJK characters that take up two character cells.

    Returns -1 if the string contains a non-printable or control character.

    >>> monospaced_width('ちゃぶ台返し')
    12
    >>> len('ちゃぶ台返し')
    6
    >>> monospaced_width('owl\N{SOFT HYPHEN}flavored')
    12
    >>> monospaced_width('example\x80')
    -1

    A more complex example: The Korean word 'ibnida' can be written with 3
    pre-composed characters or 7 jamo. Either way, it *looks* the same and
    takes up 6 character cells.

    >>> monospaced_width('입니다')
    6
    >>> monospaced_width('\u110b\u1175\u11b8\u1102\u1175\u1103\u1161')
    6

    The word "blue" with terminal escapes to make it blue still takes up only
    4 characters, when shown as intended.
    >>> monospaced_width('\x1b[34mblue\x1b[m')
    4
    """
    # NFC-normalize the text first, so that we don't need special cases for
    # Hangul jamo.
    #
    # Remove terminal escapes before calculating width, because if they are
    # displayed as intended, they will have zero width.
    return wcswidth(remove_terminal_escapes(normalize("NFC", text)))
Exemple #36
0
 def visual_justify(self, text: str, width: int, alignment: str) -> str:
     """
     The default python string methods, ljust, center, and rjust check
     the string length using len(), which adds too many spaces when the 
     string includes characters with a visual length of 2. We need to
     implement our own justification methods to handle this.
     """
     text_width = wcswidth(text)
     diff = width - text_width
     if alignment == 'l':
         right_padding = " " * diff
         return text + right_padding
     elif alignment == 'c':
         left_length = (diff // 2)
         left_padding = " " * left_length
         right_padding = " " * (diff - left_length)
         return ''.join([left_padding, text, right_padding])
     elif alignment == 'r':
         left_padding = " " * diff
         return left_padding + text
     else:
         raise ValueError(f"Got invalid justification value: {alignment}")
Exemple #37
0
def fit_to_actual_width(text: str, length_lim: int) -> str:
    """
    Cuts given text with varying character width to fit inside given width.
    Expects that lines is short enough, will read entire lines on memory multiple times.

    :param text: Source text
    :param length_lim: length limit in 1-width characters

    :return: cut string
    """

    ellipsis_ = "..."

    # returns immediately if no action is needed
    if wcswidth(text) != len(text):

        _, padded = pad_actual_length(text)
        source = "".join(padded)
        limited = source[:length_lim]

        # if last character was 2-width, padding unicode wore off, so last 2-width character can't fit.
        # instead, pad with space for consistent ellipsis position.
        if wcwidth(limited[-1]) == 2:
            limited = limited[:-1] + " "
    else:
        source = text
        limited = text[:length_lim]

    # Add ellipsis if original text was longer than given width
    if len(source) > length_lim:
        limited = limited[:length_lim - len(ellipsis_)]

        # check if last character was 2-width, if so, strip last char and add space
        if wcwidth(limited[-1]):
            limited = limited[:-1] + ' '

        limited += ellipsis_

    return limited
Exemple #38
0
def print_table(column_headings, row_entries, alignment, file=sys.stdout):
    """
    Given the column headings and the row_entries, print a table.
    Align according to alignment specification and always pad with 2 spaces.

    :param column_headings: the column headings
    :type column_headings: list of str
    :param row_entries: a list of the row entries
    :type row_entries: list of list of str
    :param alignment: the alignment indicator for each key, '<', '>', '^', '='
    :type alignment: list of str
    :param file: file to print too
    :type file: writeable stream

    Precondition: len(column_headings) == len(alignment) == len of each entry
    in row_entries.

    Precondition: all(wcswidth(h) != -1 for h in column_headings)
                  all(wcswidth(i) != -1 for row in rows for item in row)
                  (in other words, no items to be printed contain
                   unprintable characters)
    """
    column_lengths = [0] * len(column_headings)
    cell_widths = []

    # Column header isn't different than any other row, insert into rows.
    row_entries.insert(0, column_headings)

    for row_index, row in enumerate(row_entries):
        cell_widths.append([])
        for column_index, cell in enumerate(row):
            cell_width = wcswidth(cell)
            cell_widths[row_index].append(cell_width)
            column_lengths[column_index] = max(column_lengths[column_index],
                                               cell_width)

    for row, row_widths in zip(row_entries, cell_widths):
        _print_row(file, row, row_widths, column_lengths, alignment)
        print(file=file)
Exemple #39
0
    def dump(
        self,
        atk_eval: Callable[['Card'], int] = atk_at_level,
        rcv_eval: Callable[['Card'], int] = rcv_at_level,
        print_active_skill: bool = True,
        print_leader_skill: bool = False,
        evo_skill_index: Optional[int] = None,
    ) -> None:
        print(self.color_code(),
              self.name,
              common.Orb.NO_ORB.color_code(),
              ' ' * (50 - wcwidth.wcswidth(self.name)),
              f'{self.hp_at_level():8}',
              f'{atk_eval(self):8}',
              f'{rcv_eval(self):8}',
              sep='',
              end='  ')

        if print_active_skill:
            if evo_skill_index:
                desc = self.evo_skills[evo_skill_index].clean_description
            else:
                desc = self.skill.clean_description
            turn_max = self.skill.turn_max
            turn_min = self.skill.turn_min

            extra_turn = 0
            if evo_skill_index is not None:
                for i in range(1, evo_skill_index + 1):
                    extra_turn += self.evo_skills[i].turn_min_checked()

            print(f'{turn_max:2}/{turn_min:2}',
                  f'+{extra_turn:<2} ' if extra_turn else '    ',
                  desc,
                  sep='',
                  end='')
        if print_leader_skill:
            print(self.leader_skill.clean_description, end='')
        print()
Exemple #40
0
def check_line_length(entry, last_entry):

    lineno0 = entry['lineno']
    filename = entry['filename']

    lineno = lineno0
    last_line = None
    for line in entry['dialog']:
        lineno += 1

        len_this = len(line)
        if not _is_ascii(line):
            len_wc = 0
            try:
                from wcwidth import wcswidth
                len_wc = wcswidth(line)
            except:
                pass
            if (len_wc > 60) or (len_this > 35):
                print(
                    "%s:%s:HINT: [LONGLINEU] Line too long: %d chars (non-ascii)%s"
                    % (filename, lineno, len_this,
                       " wcwidth=%d" % len_wc if len_wc > 0 else ""))

        else:
            if len_this > 60:
                print("%s:%s:HINT: [LONGLINE]  Line too long: %d chars " %
                      (filename, lineno, len_this))

            if last_line:
                len_last = len(last_line)
                len_total = len_last + len_this
                if len_total < 40 and not line.startswith(
                        '-'):  # and (len_last<40 or len_this<40)
                    print(
                        "%s:%s:HINT: [SHORTLINE] Line too short: merge with last line? %d"
                        % (filename, lineno, len_this))
        last_line = line
Exemple #41
0
    def display_header(self):
        x = self._x
        y = self._y
        self.display_info('喜马拉雅', x, y, 1)

        if self.user:
            userstr = '用户: %s' % self.user.username
            tmp_x = x + self.weight - wcswidth(userstr)
            self.display_info(userstr, tmp_x, y, 1)

        self.display_info('\u2500' * self.weight, x, y + 1)
        nar_str = ''
        for obj in self.menu_stack.get_raw():
            if obj.get('type') == 0:
                nar_str += 'Home'
            elif obj.get('type') == 1:
                nar_str += ' / Album'
            elif obj.get('type') == 2:
                nar_str += ' / Chapter'
            elif obj.get('type') == 3:
                nar_str += ' / Exit'
        self.display_info(nar_str, x, y + 2)
        self.display_info('\u2500' * self.weight, x, y + 3)
Exemple #42
0
def show_detailed_sections_info(conf):

    try:
        logger.info("Requesting section info from Plex...")
        resp = requests.get(
            "%s/library/sections/all?X-Plex-Token=%s" % (conf.configs["PLEX_LOCAL_URL"], conf.configs["PLEX_TOKEN"]),
            timeout=30,
        )
        if resp.status_code == 200:
            logger.info("Requesting of section info was successful.")
            logger.debug("Request response: %s", resp.text)
            root = ElementTree.fromstring(resp.text)
            print("")
            print("Plex Sections:")
            print("==============")
            for document in root.findall("Directory"):
                print("")
                key_title = f"{document.get('key')}) {document.get('title')}"
                print(key_title)
                print("-" * wcswidth(key_title))
                print("\n".join([os.path.join(k.get("path"), "") for k in document.findall("Location")]))
    except Exception:
        logger.exception("Issue encountered when attempting to list detailed sections info.")
Exemple #43
0
def fmt(x, w, align='r'):
    """
    formatting and spacing on both english and korean

    :param x: given string
    :param w:  width size
    :param align: c|r|l
    :return:
    """
    x = str(x)
    l = wcswidth(x)
    s = w - l

    if s <= 0:
        return x
    if align == 'l':
        return x + ' ' * s
    if align == 'c':
        sl = s // 2
        sr = s - sl
        return ' ' * sl + x + ' ' * sr

    return ' ' * s + x
Exemple #44
0
def main(screen, config_file):
    # Find configuration file.
    missing_config = not os.path.isfile(config_file)

    if missing_config:
        # Write default configuration to config file location.
        sample_config = os.path.realpath(
            os.path.join(os.path.dirname(__file__), 'default_config.yml')
        )
        missing_sample = not os.path.isfile(sample_config)

        if not missing_sample:
            shutil.copyfile(sample_config, config_file)

    # Load configuration.
    config_load_error = None
    try:
        with open(config_file) as f:
            config = yaml.safe_load(f)
    except Exception as e:
        config_load_error = f'Error loading configuration: {e}'
        config = {'feeds': []}

    # Ensure config['keys'] exists and make all keys uppercase.
    config['keys'] = configure_keys(config.get('keys', dict()))

    # Set up database and requests sessions.
    db_session, www_session = configure_sessions(config)

    # This is the only time the whole screen is ever refreshed. But if you
    # don't refresh it, screen.getkey will clear it, because curses is awful.
    screen.refresh()

    # Turn off visible cursor.
    curses.curs_set(False)

    # Create screen objects.
    content, logo, sidebar, menu, messages = init_windows(screen, config)

    # Write logo to screen.
    draw_logo(logo)

    # Add key listing to menu.
    menu.write(menu_text(config['keys'], menu.width))
    menu.refresh()

    # Add loading message.
    def log(message):
        messages.write(
            f'{datetime.now():%Y-%m-%d %H:%M}: {message}', autoscroll=True
        )
        messages.refresh()

    if missing_config and missing_sample:
        log(
            f'No configuration file found at {config_file}. Please consult the'
            ' readme document.'
        )
    elif missing_config:
        log(
            f'No configuration file found at {config_file}. A default '
            'configuration file has been provided.'
        )

    if config_load_error:
        log(config_load_error)

    # Load feeds from the DB.
    feeds = []
    for feed in config['feeds']:
        row = db_session.query(Feed).filter(Feed.url == feed['url']).scalar()

        if row:
            row.name = feed.get('name', row.name)
        else:
            row = Feed(feed.get('name', feed['url']), feed['url'])
            db_session.add(row)
            db_session.commit()

        feeds.append(row)

    # Initial selections.
    if (len(feeds) > 0) and (
        (feeds[0].last_refresh is None) or (
            datetime.utcnow() - feeds[0].last_refresh >
            timedelta(minutes=config.get('refresh', 10))
        )
    ):
        feeds[0].refresh(db_session, www_session, config.get('timeout'), log)

    item_open = False
    autoscroll_to_item = False
    redraw_sidebar = True
    redraw_content = True
    selected_feed = 0
    selected_item = 0

    # TODO: Add ability to do 10j or 10<DOWN_ARROW>, like in vim. Clear it
    # whenever a non-numeric key is hit.

    # TODO: Add g/gg (or something) for top/bottom of feed items

    # TODO: Feed selection, item selection, etc. should probably be abstracted
    # into an object as well. These loops are really awkward.

    while True:
        current_feed = feeds[selected_feed] if feeds else None
        current_item = (
            current_feed.items[selected_item] if current_feed else None
        )

        if redraw_sidebar:
            redraw_sidebar = False
            for i, feed in enumerate(feeds):
                # Add optional unread count to feed name display.
                display_name = feed.name
                if config.get('unread_count'):
                    display_name += ' ({}{})'.format(
                        feed.unread,
                        ', *{}'.format(feed.starred) if feed.starred else ''
                    )

                sidebar.write(
                    '{:{}}'.format(display_name, sidebar.width), row_offset=i,
                    attr=curses.A_BOLD | curses.A_REVERSE * (i==selected_feed)
                )

            # Refresh sidebar.
            sidebar.refresh()

        if redraw_content:
            redraw_content = False
            if current_feed:
                for i, item in enumerate(current_feed.items):
                    attributes = (
                        curses.A_REVERSE * (i == selected_item) |
                        curses.A_BOLD * (not item.read)
                    )

                    # Use manual padding calcuations because Python's built-in
                    # string formatting doesn't play nicely with double-width
                    # Unicode characters
                    disp_title = ("* " if item.starred else "") + item.title
                    width = wcswidth(disp_title)

                    # Is there space to display the pubdate?
                    show_date = width + 16 < content.width
                    padding = content.width - width - 16 * show_date
                    content.write(
                        f'{disp_title}{padding * " "}' + (
                            f'{to_local(item.date):%Y-%m-%d %H:%M}'
                            if show_date else ''
                        ),
                        row_offset=0 if i == 0 else None,
                        attr=attributes
                    )

                    if i == selected_item:
                        # Autoscroll if newly-selected content is offscreen.
                        if autoscroll_to_item:
                            content.constrain_scroll(
                                first_line=max(
                                    content.next_row - content.height, 0
                                ),
                                last_line=content.height + content.next_row - 1
                            )
                            # Not perfect, because sometimes when items are
                            # open only the title will be visible. Oh well.
                            autoscroll_to_item = False

                        if item_open:
                            # Parse the HTML content.
                            parsed_string = parse_content(
                                item.content, config, content.width, log
                            )

                            # Print it to the screen.
                            content.write(f'\n{parsed_string}')

            else:
                log(
                    'No feeds to display. Instructions for adding feeds are '
                    'available in the readme document.'
                )

            content.refresh()

        # Block, waiting for input.
        try:
            key = screen.getkey().upper()
        except:
            # On resize, getkey screws up once (maybe more).
            pass

        if key == 'KEY_RESIZE':
            resize(content, logo, sidebar, menu, messages)
            draw_logo(logo)
            menu.write(menu_text(config['keys'], menu.width), row_offset=0)
            menu.refresh()
            redraw_content = True
            redraw_sidebar = True

        elif key == config['keys']['open'] and current_item:
            content.clear()     # Should be more selective.
            redraw_content = True
            item_open = not item_open

            if item_open:
                current_item.read = True
                db_session.commit()
            else:
                # When closed, title might be off the screen now.
                autoscroll_to_item = True

        elif key == config['keys']['next_item'] and current_feed:
            if len(current_feed.items) > 0:
                content.clear()     # Should be more selective.
                redraw_content = True
                selected_item = (selected_item + 1) % len(current_feed.items)
                autoscroll_to_item = True

                if item_open:
                    current_feed.items[selected_item].read = True
                    db_session.commit()
                    redraw_sidebar = True

        elif key == config['keys']['prev_item'] and current_feed:
            if len(current_feed.items) > 0:
                content.clear()     # Should be more selective.
                redraw_content = True
                selected_item = (selected_item - 1) % len(current_feed.items)
                autoscroll_to_item = True

                if item_open:
                    current_feed.items[selected_item].read = True
                    db_session.commit()
                    redraw_sidebar = True

        elif key == config['keys']['next_feed'] and current_feed:
            content.clear()     # Should be more selective.
            sidebar.clear()     # Should be more selective.
            redraw_content = True
            redraw_sidebar = True

            selected_feed = (selected_feed + 1) % len(feeds)
            selected_item = 0
            item_open = False
            autoscroll_to_item = True

            if (feeds[selected_feed].last_refresh is None) or (
                datetime.utcnow() - feeds[selected_feed].last_refresh >
                    timedelta(minutes=config.get('refresh', 10))
            ):
                feeds[selected_feed].refresh(
                    db_session, www_session, config.get('timeout'), log
                )

        elif key == config['keys']['prev_feed'] and current_feed:
            content.clear()     # Should be more selective.
            sidebar.clear()     # Should be more selective.
            redraw_content = True
            redraw_sidebar = True

            selected_feed = (selected_feed - 1) % len(feeds)
            selected_item = 0
            item_open = False
            autoscroll_to_item = True

            if (feeds[selected_feed].last_refresh is None) or (
                datetime.utcnow() - feeds[selected_feed].last_refresh >
                    timedelta(minutes=config.get('refresh', 10))
            ):
                feeds[selected_feed].refresh(
                    db_session, www_session, config.get('timeout'), log
                )

        elif key == config['keys']['scroll_down']:
            content.scroll_down(config.get('scroll_lines', 5))

        elif key == config['keys']['scroll_up']:
            content.scroll_up(config.get('scroll_lines', 5))

        elif key == config['keys']['update_feed'] and current_feed:
            content.clear()     # Should be more selective.
            sidebar.clear()     # Should be more selective.
            redraw_content = True
            redraw_sidebar = True

            selected_item = 0
            item_open = False
            autoscroll_to_item = True

            feeds[selected_feed].refresh(
                db_session, www_session, config.get('timeout'), log
            )

        elif key == config['keys']['open_in_browser'] and current_item:
            if current_item.url:
                webbrowser.open(current_item.url)
            else:
                log(f'No URL associated with {current_item.title}')

        elif key == config['keys']['toggle_read'] and current_item:
            # Should be more selective.
            redraw_content = True
            redraw_sidebar = True
            current_item.read = not current_item.read
            db_session.commit()

        elif key == config['keys']['toggle_star'] and current_item:
            # Should be more selective.
            redraw_content = True
            redraw_sidebar = True
            current_item.starred = not current_item.starred
            db_session.commit()

        elif key == config['keys']['quit']:
            break
Exemple #45
0
 def width(self):
     width = wcwidth.wcswidth(self._s)
     if len(self._s) > 0 and width < 1:
         raise ValueError("Can't calculate width of string %r" % self._s)
     return width
Exemple #46
0
    def pformat(self, indent: str= '    ', remove_comments=False) -> str:
        """Return a pretty-print of self.string as string.

        Try to organize templates and parser functions by indenting, aligning
        at the equal signs, and adding space where appropriate.

        Note that this function will not mutate self.
        """
        ws = WS
        # Do not try to do inplace pformat. It will overwrite on some spans.
        string = self.string
        parsed = WikiText([string], self._pp_type_to_spans())
        span = [0, len(string)]
        parsed._span = span
        parsed._type_to_spans['WikiText'] = [span]
        if remove_comments:
            for c in parsed.comments:
                del c[:]
        else:
            # Only remove comments that contain whitespace.
            for c in parsed.comments:
                if not c.contents.strip(ws):
                    del c[:]
        # First remove all current spacings.
        for template in parsed.templates:
            s_tl_name = template.name.strip(ws)
            template.name = (
                ' ' + s_tl_name + ' '
                if s_tl_name[0] == '{' else s_tl_name
            )
            args = template.arguments
            if not args:
                continue
            if ':' in s_tl_name:
                # Don't use False because we don't know for sure.
                not_a_parser_function = None
            else:
                not_a_parser_function = True
            # Required for alignment
            arg_stripped_names = [a.name.strip(ws) for a in args]
            arg_positionalities = [a.positional for a in args]
            arg_name_lengths = [
                wcswidth(n.replace('لا', '?'))
                if not p else 0
                for n, p in zip(arg_stripped_names, arg_positionalities)
            ]
            max_name_len = max(arg_name_lengths)
            # Format template.name.
            level = template.nesting_level
            newline_indent = '\n' + indent * level
            template.name += newline_indent
            if level == 1:
                last_comment_indent = '<!--\n-->'
            else:
                last_comment_indent = '<!--\n' + indent * (level - 2) + ' -->'
            # Special formatting for the last argument.
            last_arg = args.pop()
            last_is_positional = arg_positionalities.pop()
            last_value = last_arg.value
            last_stripped_value = last_value.strip(ws)
            if last_is_positional and last_value != last_stripped_value:
                stop_conversion = True
                if not last_value.endswith('\n' + indent * (level - 1)):
                    last_arg.value = last_value + last_comment_indent
            elif not_a_parser_function:
                stop_conversion = False
                last_arg.name = (
                    ' ' + arg_stripped_names.pop() + ' ' +
                    ' ' * (max_name_len - arg_name_lengths.pop())
                )
                last_arg.value = (
                    ' ' + last_stripped_value + '\n' + indent * (level - 1)
                )
            elif last_is_positional:
                # (last_value == last_stripped_value
                # and not_a_parser_function is not True)
                stop_conversion = True
                # Can't strip or adjust the position of the value
                # because this could be a positional argument in a template.
                last_arg.value = last_value + last_comment_indent
            else:
                stop_conversion = True
                # This is either a parser function or a keyword
                # argument in a template. In both cases the name
                # can be lstripped and the value can be rstripped.
                last_arg.name = ' ' + last_arg.name.lstrip(ws)
                if not last_value.endswith('\n' + indent * (level - 1)):
                    last_arg.value = (
                        last_value.rstrip(ws) + ' ' + last_comment_indent
                    )
            if not args:
                continue
            comment_indent = '<!--\n' + indent * (level - 1) + ' -->'
            for arg, stripped_name, positional, arg_name_len in zip(
                reversed(args),
                reversed(arg_stripped_names),
                reversed(arg_positionalities),
                reversed(arg_name_lengths),
            ):
                value = arg.value
                stripped_value = value.strip(ws)
                # Positional arguments of templates are sensitive to
                # whitespace. See:
                # https://meta.wikimedia.org/wiki/Help:Newlines_and_spaces
                if stop_conversion:
                    if not value.endswith(newline_indent):
                        arg.value += comment_indent
                elif positional and value != stripped_value:
                        stop_conversion = True
                        if not value.endswith(newline_indent):
                            arg.value += comment_indent
                elif not_a_parser_function:
                    arg.name = (
                        ' ' + stripped_name + ' ' +
                        ' ' * (max_name_len - arg_name_len)
                    )
                    arg.value = ' ' + stripped_value + newline_indent
        i = 0
        functions = parsed.parser_functions
        while i < len(functions):
            func = functions[i]
            i += 1
            name = func.name
            ls_name = name.lstrip(ws)
            lws = len(name) - len(ls_name)
            if lws:
                del func[2:lws + 2]
            if ls_name.lower() in ('#tag', '#invoke', ''):
                # The 2nd argument of `tag` parser function is an exception
                # and cannot be stripped.
                # So in `{{#tag:tagname|arg1|...}}`, no whitespace should be
                # added/removed to/from arg1.
                # See: [[mw:Help:Extension:ParserFunctions#Miscellaneous]]
                # All args of #invoke are also whitespace-sensitive.
                # Todo: Instead use comments to indent.
                continue
            args = func.arguments
            # Whitespace, including newlines, tabs, and spaces is stripped
            # from the beginning and end of all the parameters of
            # parser functions. See:
            # www.mediawiki.org/wiki/Help:Extension:ParserFunctions#
            #    Stripping_whitespace
            level = func.nesting_level
            short_indent = '\n' + indent * (level - 1)
            newline_indent = short_indent + indent
            if len(args) == 1:
                arg = args[0]
                # the first arg is both the first and last argument
                if arg.positional:
                    arg.value = (
                        newline_indent + arg.value.strip(ws) + short_indent
                    )
                else:
                    # Note that we don't add spaces before and after the
                    # '=' in parser functions because it could be part of
                    # an ordinary string.
                    arg.name = newline_indent + arg.name.lstrip(ws)
                    arg.value = arg.value.rstrip(ws) + short_indent
                functions = parsed.parser_functions
                continue
            # Special formatting for the first argument
            arg = args[0]
            if arg.positional:
                arg.value = \
                    newline_indent + arg.value.strip(ws) + newline_indent
            else:
                arg.name = newline_indent + arg.name.lstrip(ws)
                arg.value = arg.value.rstrip(ws) + newline_indent
            # Formatting the middle arguments
            for arg in args[1:-1]:
                if arg.positional:
                    arg.value = ' ' + arg.value.strip(ws) + newline_indent
                else:
                    arg.name = ' ' + arg.name.lstrip(ws)
                    arg.value = arg.value.rstrip(ws) + newline_indent
            # Special formatting for the last argument
            arg = args[-1]
            if arg.positional:
                arg.value = ' ' + arg.value.strip(ws) + short_indent
            else:
                arg.name = ' ' + arg.name.lstrip(ws)
                arg.value = arg.value.rstrip(ws) + short_indent
            functions = parsed.parser_functions
        return parsed.string
Exemple #47
0
 def width_at_offset(self, n):
     """Returns the horizontal position of character n of the string"""
     #TODO make more efficient?
     width = wcwidth.wcswidth(self.s[:n])
     assert width != -1
     return width
 def _pad_and_truncate(self, text):
     s = (text + " " * (self.column_width))[:self.column_width]
     diff = wcwidth.wcswidth(s) - len(s)
     if diff > 0:
         s = s[:-diff]
     return s
Exemple #49
0
 def width(self):
     return wcswidth(self.s)
Exemple #50
0
 def width(self):
     width = wcwidth.wcswidth(self._s)
     if len(self._s) > 0 and width < 1:
         raise ValueError("Can't calculate width of string %r" % self._s)
     return width
Exemple #51
0
 def width_at_offset(self, n):
     """Returns the horizontal position of character n of the string"""
     #TODO make more efficient?
     width = wcwidth.wcswidth(self.s[:n])
     assert width != -1
     return width
Exemple #52
0
 def rjust(string: str, length: int):
     return ' ' * (length - wcswidth(string)) + string
Exemple #53
0
 def _lpad(self, text, length, padding=' '):
     ''' left pad for printing, not used '''
     return padding * max(0, (length - wcswidth(text))) + text
Exemple #54
0
 def str_block_width(self, val):
     return wcwidth.wcswidth(self._re.sub("", val))
Exemple #55
0
def showCmdQRCode(filename):
    global Image
    import wcwidth
    # 165x165 -> 33x33
    size = 33
    padding = 1
    rgb = Image.open(filename).resize((size, size)).convert('RGB')

    qrtext = '0' * (size + padding * 2) + '\n'
    for rr in range(size):
        qrtext += '0' * padding
        for cc in range(size):
            r, g, b = rgb.getpixel((cc, rr))
            if (r > 127 or g > 127 or b > 127):
                qrtext += '0'
            else:
                qrtext += '1'
        qrtext += '0' * padding
        qrtext += '\n'
    qrtext = qrtext + '0' * (size + padding * 2) + '\n'

    try:
        b = u'\u2588'
        sys.stdout.write(b + '\r')
        sys.stdout.flush()
    except UnicodeEncodeError:
        white = 'MM'
    else:
        white = b

    black = '  '

    # currently for Windows, '\u2588' is not correct. So use 'MM' for windows.
    osName = platform.system()
    if osName == 'Windows':
        white = '@@'

    blockCount = int(2 / wcwidth.wcswidth(white))
    white *= abs(blockCount)

    sys.stdout.write(' ' * 50 + '\r')
    sys.stdout.flush()
    qr = qrtext.replace('0', white).replace('1', black)
    qr = '\033[37m\033[40m\n' + qr + '\033[0m\n'  # force to use white/black.
    sys.stdout.write(qr)
    sys.stdout.flush()

    # A space-saving text QRCode
    if osName != 'Windows':
        charlist = [
            u' ', u'\u2598', u'\u259D', u'\u2580', u'\u2596', u'\u258C',
            u'\u259E', u'\u259B', u'\u2597', u'\u259A', u'\u2590', u'\u259C',
            u'\u2584', u'\u2599', u'\u259F', u'\u2588'
        ]
        qrarray = map(lambda x: map(lambda y: y, x), qrtext.split('\n'))
        qrtext = ''
        for rr in range(0, size + padding * 2, 2):
            for cc in range(0, size + padding * 2, 2):
                index = int(
                    ''.join([
                        x for row in qrarray[rr:rr + 2]
                        for x in (row + ['0'])[cc:cc + 2]
                    ][::-1]), 2)
                qrtext += hex(15 - index)[-1]
            qrtext += '\n'
        qr = ''.join(
            map(lambda x: charlist[int(x, 16)] if x != '\n' else x, qrtext))
        qr = '\033[37m\033[40m\n' + qr + '\033[0m\n'  # force to use white/black.
        sys.stdout.write(qr)
        sys.stdout.flush()
def scFormatter(string):
    string = regex.sub(lambda i: i.group().lower(), string)
    # Build the lexer
    lexer = lex.lex()

    if string.strip() == '':
        return
    # Give the lexer some input
    lexer.input(f'{string}')

    # Tokenize
    result = []
    newline = ''
    step = ' ' * 8
    align = ' ' * 4
    indent = ''

    for tok in lexer:
        if not tok: break  # No more input
        # print(tok.type, tok.value, tok.lexpos, sep = '\t'*2)

        if tok.type in ("LPARENTHESIS", "LBRACKET"):
            ######测试代码{
            #如果一行代码以(or|and|not) (结尾且不仅仅只包括or|and|not,那么就在or|and|not前换行
            match = re.match('^(?P<head>.*?)\s+(?P<tail>(?:or|and|not))\s*$',
                             newline)
            if match:
                if match.group('head'):
                    result.append(match.group('head'))
                    newline = indent[:-4] + match.group('tail').ljust(4, ' ')
                    # if match.group('tail') == 'or':
                    #or 在行首,额外增加一个空格,以使其能够与and对齐
                    # newline += ' '
                    # result.append(newline)


######测试代码}
            elif newline.endswith(' = '):
                pass
            elif re.search(t_CONNECT + '\s*$', newline):
                pass
            elif newline.strip() != '':
                newline = newline.rstrip()

            newline += tok.value
            result.append(newline)
            indent += step
            newline = indent

        elif tok.type in ("RPARENTHESIS", "RBRACKET"):
            indent = indent[:-8]

            if 0 < len(newline.strip()) < 40 and result[-1].endswith('('):
                newline = result.pop().rstrip() + newline.strip()
            else:
                result.append(newline.rstrip())
                newline = ''
                newline += indent

            newline += tok.value
            result.append(newline)
            newline = indent

        # elif tok.type == "LBRACKET":
        # newline += tok.value + ''
        # elif tok.type == "RBRACKET":
        # newline = newline.rstrip() + tok.value
        # result.append(newline)
        # newline += indent

        elif tok.type == "KEYWORD":
            match = re.match('(?P<head>\S+?)\s+(?P<tail>or|and|not)\s*$',
                             newline)
            if match:
                # if match.group('head'):
                result.append(match.group('head'))
                newline = indent[:-4] + match.group('tail').ljust(4, ' ')

            newline += tok.value + ' '

        elif tok.type == "EQUAL":
            newline += tok.value + ' '
        elif tok.type == "VALUE":
            if newline.strip() == '' and result[-1].endswith(')'):
                #主要用来处理公司名称中出现的括号
                #当公司名称中有括号时,不要换行
                newline = result.pop()
            newline += tok.value + ' '

        elif tok.type == "CONNECT":
            tok.value = re.sub('\(\s*([1-9]?)\s*([wn])\s*\)', '(\g<1>\g<2>)',
                               tok.value.lower())
            if newline.strip() == '' and result[-1].endswith(')'):
                if result[-1].strip() != ')':
                    #连字符紧跟在右括号后边时,不换行
                    newline = result.pop() + ' '
                    newline += tok.value + ' '
                else:
                    result.append(newline.rstrip())
                    newline = indent
                    newline = newline[:-4]
                    newline += tok.value.ljust(4, ' ')
            else:
                newline += tok.value + ' '
        elif tok.type == "TO":
            newline += tok.value.lower() + ' '
        elif tok.type == "AND":
            if newline.strip() == '':
                if len(newline) < 8:
                    return scFormatter(f'({string})')
                newline = newline[:-4]
            newline += tok.value.lower() + ' '
        elif tok.type == "OR":
            if wcswidth(newline) > 100:
                result.append(newline.rstrip())
                newline = '' + indent

            if newline.strip() == '':
                if len(newline) < 8:
                    return scFormatter(f'({string})')
                newline = newline[:-4]
                newline += tok.value.lower() + '  '
            else:
                newline += tok.value.lower() + ' '
        elif tok.type == "NOT":
            if newline.strip() == '':
                if len(newline) < 8:
                    return scFormatter(f'({string})')
                newline = newline[:-4]
            newline += tok.value.lower() + ' '
    else:
        if newline.strip():
            result.append(newline.rstrip())
        while indent:
            indent = indent[:-8]
            result.append(indent + ')')

        for i in range(1, len(result) - 1):
            if result[i] == '':
                continue
            else:
                l = i - 1
                while (l and result[l] == ''):
                    l -= 1
                n = i + 1
            # if result[l].endswith('(') and len(result[i].strip()) < 50 and result[n].strip() == ')':
            # result[l] += result[i].strip() + result[n].strip()
            # result[i] = ''
            # result[n] = ''
            # elif result[i].lstrip().startswith('(w)') or result[i].lstrip().startswith('(n)'):
            # result[l] += ' ' + result[i].strip()
            # result[i] = ''
        else:
            result = '\n'.join(filter(None, result))

        result = re.sub('\([\n\s]*\)', '', result)  #删除空括号对(自动补全括号时,有可能会产生空括号对。
        result = re.sub('^\s*(and|or|not)*\s*$', '', result,
                        flags=re.M)  #删除只有and or not的行以及空行。
        result = re.sub(r'[A-Z]([A-Za-z-]*?[A-Za-z])?(?=\s*=\s*)',
                        lambda matched: matched.group(0).lower(), result)
        result = re.sub(
            r'(?P<quote>")?(?P<ipc>\b(?P<section>[A-H])(?(section)(?P<class>\d{2})?)(?(class)(?P<subclass>[A-Z])?)(?(subclass)(?P<group>\d{1,})?)(?(group)(?:/(?P<subgroup>\d{1,}))?)\b)(?(quote)")',
            lambda matched: matched.group('ipc').upper(),
            result,
            flags=re.IGNORECASE)
        #将所有ipc分类号转为大写
        result = re.sub(
            r'\b(?P<country>am|ap|ar|at|au|ba|be|bg|br|by|ca|ch|cl|cn|co|cr|cs|cu|cy|cz|dd|de|dk|do|dz|ea|ec|ee|eg|ep|es|fi|fr|gb|gc|ge|gr|gt|hk|hn|hr|hu|id|ie|il|in|is|it|jo|jp|ke|kr|kz|lt|lu|lv|ma|mc|md|me|mn|mo|mt|mw|mx|my|ni|nl|no|nz|oa|pa|pe|ph|pl|pt|ro|rs|ru|se|sg|si|sk|sm|su|sv|th|tj|tr|tt|tw|ua|us|uy|uz|vn|wo|yu|za|zm|zw|py|bo|ve|eu|sa|kg|tn|ae|bh|bn|lb)\b(?!\s*=)',
            lambda matched: matched.group('country').upper(),
            result,
            flags=re.IGNORECASE)
        #将所有国别代码转为大写
        result = re.sub('(\n\))+\n*$', '\n)\n', result)
        result = re.sub(
            '\[[\n\s]*(?P<start>\d{1,8})\s*to\s*(?P<end>\d{1,8})[\n\s]*\]',
            '[\g<start> to \g<end>]', result)  #避免方括号内的日期占用三行空间
        return result.strip()
Exemple #57
0
def get_field_width(field_text):
    return max([wcwidth.wcswidth(s) for s in field_text.split('\n')])
Exemple #58
0
    def draw(self, data):
        """
        Terminus alters the logic to better support double width chars and linefeed marker
        """
        data = data.translate(
            self.g1_charset if self.charset else self.g0_charset)

        for char in data:
            char_width = wcwidth(char)
            if (self.cursor.x == self.columns and char_width >= 1)  \
                    or (self.cursor.x == self.columns - 1 and char_width >= 2):
                if mo.DECAWM in self.mode:
                    last = self.buffer[self.cursor.y][self.columns - 1]
                    self.buffer[self.cursor.y][self.columns - 1] = \
                        last._replace(linefeed=True)
                    self.dirty.add(self.cursor.y)
                    self.carriage_return()
                    self.linefeed()
                elif char_width > 0:
                    self.cursor.x -= char_width

            if mo.IRM in self.mode and char_width > 0:
                self.insert_characters(char_width)

            line = self.buffer[self.cursor.y]
            if char_width == 1:
                if is_windows and self.cursor.x == self.columns - 1:
                    # always put a linefeed marker when cursor is at the last column
                    line[self.cursor.x] = self.cursor.attrs._replace(
                        data=char, linefeed=True)
                else:
                    line[self.cursor.x] = self.cursor.attrs._replace(data=char)

            elif char_width == 2:
                line[self.cursor.x] = self.cursor.attrs._replace(data=char)
                if is_windows and self.cursor.x == self.columns - 2:
                    # always put a linefeed marker when the next char is at the last column
                    line[self.cursor.x + 1] = self.cursor.attrs._replace(
                        data="", linefeed=True)
                elif self.cursor.x + 1 < self.columns:
                    line[self.cursor.x +
                         1] = self.cursor.attrs._replace(data="")

            elif char_width == 0 and unicodedata.combining(char):
                # unfornately, sublime text doesn't render decomposed double char correctly
                pos = None
                for (row, col) in [(self.cursor.y, self.cursor.x),
                                   (self.cursor.y - 1, self.columns)]:
                    if row < 0:
                        continue
                    if col >= 2:
                        last = line[col - 2]
                        if wcswidth(last.data) >= 2:
                            pos = (row, col - 2)
                            break
                    if col >= 1:
                        last = line[col - 1]
                        pos = (row, col - 1)
                        break

                if pos:
                    normalized = unicodedata.normalize("NFC", last.data + char)
                    self.buffer[pos[0]][pos[1]] = last._replace(
                        data=normalized)
                    self.dirty.add(pos[0])
            else:
                break

            if char_width > 0:
                self.cursor.x = min(self.cursor.x + char_width, self.columns)

        self.dirty.add(self.cursor.y)
Exemple #59
0
def list_to_googletrans(  # pylint: disable=too-many-locals, too-many-arguments, too-many-branches, too-many-statements
        text_list,
        translator,
        src_language=constants.DEFAULT_SRC_LANGUAGE,
        dst_language=constants.DEFAULT_DST_LANGUAGE,
        size_per_trans=constants.DEFAULT_SIZE_PER_TRANS,
        sleep_seconds=constants.DEFAULT_SLEEP_SECONDS,
        drop_override_codes=False,
        delete_chars=None):
    """
    Give a text list, generate translated text list from GoogleTranslatorV2 api.
    """

    if not text_list:
        return None

    size = 0
    i = 0
    partial_index = []
    valid_index = []
    is_last = ""
    text_list_length = len(text_list)
    if size_per_trans <= 0:
        size_per_trans = float("inf")
    while i < text_list_length:
        if text_list[i]:
            if not is_last:
                is_last = text_list[i]
                valid_index.append(i)
                # valid_index for valid text position start
            wcswidth_text = wcwidth.wcswidth(text_list[i])
            if wcswidth_text * 10 / len(text_list[i]) >= 10:
                # If text contains full-wide char,
                # count its length about 4 times than the ordinary text.
                # Avoid weird problem when text has full-wide char.
                # In this case Google will count a full-wide char
                # at least 2 times larger than a half-wide char.
                # It will certainly exceed the limit of the size_per_trans.
                # Causing a googletrans internal jsondecode error.
                size = size + wcswidth_text * 2
            else:
                size = size + len(text_list[i])
            if size > size_per_trans:
                # use size_per_trans to split the list
                partial_index.append(i)
                size = 0
                continue
                # stay at this line of text
                # in case it's the last one
        else:
            if is_last:
                is_last = text_list[i]
                valid_index.append(i)
                # valid_index for valid text position end
        i = i + 1

    if size:
        partial_index.append(i)
        # python sequence
        # every group's end index
    else:
        return None

    len_valid_index = len(valid_index)

    if len_valid_index % 2:
        valid_index.append(i)
        # valid_index for valid text position end

    if translator != ManualTranslator and src_language == "auto":
        content_to_trans = '\n'.join(text_list[i:partial_index[0]])
        result_src = translator.detect(content_to_trans).lang
    else:
        result_src = src_language

    print(_("\nTranslating text from \"{0}\" to \"{1}\".").format(
        result_src,
        dst_language))

    widgets = [_("Translation: "),
               progressbar.Percentage(), ' ',
               progressbar.Bar(), ' ',
               progressbar.ETA()]
    pbar = progressbar.ProgressBar(widgets=widgets, maxval=i).start()

    try:
        translated_text = []
        i = 0
        # total position
        j = 0
        # valid_index position

        for index in partial_index:
            content_to_trans = '\n'.join(text_list[i:index])
            if drop_override_codes:
                content_to_trans = "".join(re.compile(r'{.*?}').split(content_to_trans))
            translation = translator.translate(text=content_to_trans,
                                               dest=dst_language,
                                               src=src_language)
            result_text = translation.text.translate(str.maketrans('’', '\''))
            result_src = translation.src
            result_list = result_text.split('\n')
            k = 0
            len_result_list = len(result_list)
            while i < index and j < len_valid_index and k < len_result_list:
                if not result_list[k]:
                    # if the result is invalid,
                    # continue
                    k = k + 1
                    continue
                if i < valid_index[j]:
                    # if text is invalid,
                    # append the empty string
                    # and then continue
                    translated_text.append("")
                    i = i + 1
                    if i % 20 == 5:
                        pbar.update(i)
                    continue
                if i < valid_index[j + 1]:
                    # if text is valid, append it
                    if delete_chars:
                        result_list[k] = result_list[k].translate(
                            str.maketrans(delete_chars, " " * len(delete_chars)))
                        result_list[k] = result_list[k].rstrip(" ")
                    translated_text.append(result_list[k])
                    k = k + 1
                    i = i + 1
                else:
                    j = j + 2
                if i % 20 == 5:
                    pbar.update(i)
            if len(partial_index) > 1:
                time.sleep(sleep_seconds)

        i = valid_index[-1]
        while i < partial_index[-1]:
            # if valid_index[-1] is less than partial_index[-1]
            # add empty strings
            translated_text.append("")
            i = i + 1

        pbar.finish()

    except KeyboardInterrupt:
        pbar.finish()
        print(_("Cancelling translation."))
        return 1

    return translated_text, result_src
Exemple #60
0
    def FormatCell(entry, cell_width, cell_height=1, align='c', valign='t'):
        """Format an entry into a list of strings for a fixed cell size.

    Given a (possibly multi-line) entry and a cell height and width,
    we split the entry into a list of lines and format each one into
    the given width and alignment. We then pad the list with
    additional blank lines of the appropriate width so that the
    resulting list has exactly cell_height entries. Each entry
    is also padded with one space on either side.

    We abbreviate strings for width, but we require that the
    number of lines in entry is at most cell_height.

    Args:
      entry: String to format, which may have newlines.
      cell_width: Maximum width for lines in the cell.
      cell_height: Number of lines in the cell.
      align: Alignment to use for lines of text.
      valign: Vertical alignment in the cell. One of 't',
        'c', or 'b' (top, center, and bottom, respectively).

    Returns:
      An iterator yielding exactly cell_height lines, each of
      exact width cell_width + 2, corresponding to this cell.

    Raises:
      FormatterException: If there are too many lines in entry.
      ValueError: If the valign is invalid.
    """
        entry_lines = [
            PrettyFormatter.Abbreviate(line, cell_width)
            for line in entry.split('\n')
        ]
        if len(entry_lines) > cell_height:
            raise FormatterException(
                'Too many lines (%s) for a cell of size %s' %
                (len(entry_lines), cell_height))
        if valign == 't':
            top_lines = []
            bottom_lines = itertools.repeat(' ' * (cell_width + 2),
                                            cell_height - len(entry_lines))
        elif valign == 'c':
            top_padding, bottom_padding = PrettyFormatter.CenteredPadding(
                cell_height, len(entry_lines))
            top_lines = itertools.repeat(' ' * (cell_width + 2), top_padding)
            bottom_lines = itertools.repeat(' ' * (cell_width + 2),
                                            bottom_padding)
        elif valign == 'b':
            bottom_lines = []
            top_lines = itertools.repeat(' ' * (cell_width + 2),
                                         cell_height - len(entry_lines))
        else:
            raise ValueError('Unknown value for valign: %s' % (valign, ))
        content_lines = []
        for line in entry_lines:
            if align == 'c':
                left_padding, right_padding = PrettyFormatter.CenteredPadding(
                    cell_width, wcwidth.wcswidth(line))
                content_lines.append(
                    ' %s%s%s ' %
                    (' ' * left_padding, line, ' ' * right_padding))
            elif align in ('l', 'r'):
                padding = ' ' * (cell_width - wcwidth.wcswidth(line))
                fmt = ' %s%s '
                if align == 'l':
                    output = fmt % (line, padding)
                else:
                    output = fmt % (padding, line)
                content_lines.append(output)
            else:
                raise FormatterException('Unknown alignment: %s' % (align, ))
        return itertools.chain(top_lines, content_lines, bottom_lines)