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
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))
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')
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
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
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))
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
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)))
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
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)
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
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
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
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()
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()
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
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
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
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)
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
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)
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
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('')
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('')
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
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)
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)
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()
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)))
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}")
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
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)
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()
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
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)
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.")
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
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
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
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
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
def width(self): return wcswidth(self.s)
def rjust(string: str, length: int): return ' ' * (length - wcswidth(string)) + string
def _lpad(self, text, length, padding=' '): ''' left pad for printing, not used ''' return padding * max(0, (length - wcswidth(text))) + text
def str_block_width(self, val): return wcwidth.wcswidth(self._re.sub("", val))
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()
def get_field_width(field_text): return max([wcwidth.wcswidth(s) for s in field_text.split('\n')])
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)
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
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)