diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -1697,7 +1697,9 @@ def help_(ui, name=None, with_version=Fa commands = cmds[f].replace("|",", ") ui.write(" %s:\n %s\n"%(commands, h[f])) else: - ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4))) + ui.write('%s\n' % (util.wrap(h[f], + initindent=' %-*s ' % (m, f), + hangindent=' ' * (m + 4)))) if not ui.quiet: addglobalopts(True) @@ -1824,8 +1826,11 @@ def help_(ui, name=None, with_version=Fa opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0]) for first, second in opt_output: if second: - second = util.wrap(second, opts_len + 3) - ui.write(" %-*s %s\n" % (opts_len, first, second)) + initindent = ' %-*s ' % (opts_len, first) + hangindent = ' ' * (opts_len + 3) + ui.write('%s\n' % (util.wrap(second, + initindent=initindent, + hangindent=hangindent))) else: ui.write("%s\n" % first) diff --git a/mercurial/encoding.py b/mercurial/encoding.py --- a/mercurial/encoding.py +++ b/mercurial/encoding.py @@ -72,6 +72,6 @@ def colwidth(s): d = s.decode(encoding, 'replace') if hasattr(unicodedata, 'east_asian_width'): w = unicodedata.east_asian_width - return sum([w(c) in 'WF' and 2 or 1 for c in d]) + return sum([w(c) in 'WFA' and 2 or 1 for c in d]) return len(d) diff --git a/mercurial/minirst.py b/mercurial/minirst.py --- a/mercurial/minirst.py +++ b/mercurial/minirst.py @@ -35,8 +35,8 @@ It only supports a small subset of reStr - inline literals (no other inline markup is not recognized) """ -import re, sys, textwrap - +import re, sys +import util def findblocks(text): """Find continuous blocks of lines in text. @@ -304,9 +304,9 @@ def formatblock(block, width): hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip()) defindent = indent + hang * ' ' text = ' '.join(map(str.strip, block['lines'][1:])) - return "%s\n%s" % (term, textwrap.fill(text, width=width, - initial_indent=defindent, - subsequent_indent=defindent)) + return '%s\n%s' % (term, util.wrap(text, width=width, + initindent=defindent, + hangindent=defindent)) subindent = indent if block['type'] == 'bullet': if block['lines'][0].startswith('| '): @@ -338,9 +338,9 @@ def formatblock(block, width): subindent = indent + (len(option) + len(arg)) * ' ' text = ' '.join(map(str.strip, block['lines'])) - return textwrap.fill(text, width=width, - initial_indent=indent, - subsequent_indent=subindent) + return util.wrap(text, width=width, + initindent=indent, + hangindent=subindent) def format(text, width, indent=0, keep=None): diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py --- a/mercurial/templatefilters.py +++ b/mercurial/templatefilters.py @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import cgi, re, os, time, urllib, textwrap +import cgi, re, os, time, urllib import util, encoding def stringify(thing): @@ -61,15 +61,17 @@ def fill(text, width): while True: m = para_re.search(text, start) if not m: - w = len(text) - while w > start and text[w - 1].isspace(): + uctext = unicode(text[start:], encoding.encoding) + w = len(uctext) + while 0 < w and uctext[w - 1].isspace(): w -= 1 - yield text[start:w], text[w:] + yield (uctext[:w].encode(encoding.encoding), + uctext[w:].encode(encoding.encoding)) break yield text[start:m.start(0)], m.group(1) start = m.end(1) - return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest + return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest for para, rest in findparas()]) def firstline(text): diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -16,7 +16,7 @@ hide platform-specific details from the from i18n import _ import error, osutil, encoding import cStringIO, errno, re, shutil, sys, tempfile, traceback -import os, stat, time, calendar, textwrap, signal +import os, stat, time, calendar, textwrap, unicodedata, signal import imp # Python compatibility @@ -1257,21 +1257,49 @@ def uirepr(s): # Avoid double backslash in Windows path repr() return repr(s).replace('\\\\', '\\') -def wrap(line, hangindent, width=None): +#### naming convention of below implementation follows 'textwrap' module + +class MBTextWrapper(textwrap.TextWrapper): + def __init__(self, **kwargs): + textwrap.TextWrapper.__init__(self, **kwargs) + + def _cutdown(self, str, space_left): + l = 0 + ucstr = unicode(str, encoding.encoding) + w = unicodedata.east_asian_width + for i in xrange(len(ucstr)): + l += w(ucstr[i]) in 'WFA' and 2 or 1 + if space_left < l: + return (ucstr[:i].encode(encoding.encoding), + ucstr[i:].encode(encoding.encoding)) + return str, '' + + # ---------------------------------------- + # overriding of base class + + def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): + space_left = max(width - cur_len, 1) + + if self.break_long_words: + cut, res = self._cutdown(reversed_chunks[-1], space_left) + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + +#### naming convention of above implementation follows 'textwrap' module + +def wrap(line, width=None, initindent='', hangindent=''): if width is None: width = termwidth() - 2 - if width <= hangindent: + maxindent = max(len(hangindent), len(initindent)) + if width <= maxindent: # adjust for weird terminal size - width = max(78, hangindent + 1) - padding = '\n' + ' ' * hangindent - # To avoid corrupting multi-byte characters in line, we must wrap - # a Unicode string instead of a bytestring. - try: - u = line.decode(encoding.encoding) - w = padding.join(textwrap.wrap(u, width=width - hangindent)) - return w.encode(encoding.encoding) - except UnicodeDecodeError: - return padding.join(textwrap.wrap(line, width=width - hangindent)) + width = max(78, maxindent + 1) + wrapper = MBTextWrapper(width=width, + initial_indent=initindent, + subsequent_indent=hangindent) + return wrapper.fill(line) def iterlines(iterator): for chunk in iterator: diff --git a/tests/test-encoding.out b/tests/test-encoding.out --- a/tests/test-encoding.out +++ b/tests/test-encoding.out @@ -121,18 +121,18 @@ tip 5:db5 ? 3:770b9b11621d % hg tags (latin-1) tip 5:db5520b4645f -é 3:770b9b11621d +é 3:770b9b11621d % hg tags (utf-8) tip 5:db5520b4645f -é 3:770b9b11621d +é 3:770b9b11621d % hg branches (ascii) ? 5:db5520b4645f default 4:9cff3c980b58 (inactive) % hg branches (latin-1) -é 5:db5520b4645f +é 5:db5520b4645f default 4:9cff3c980b58 (inactive) % hg branches (utf-8) -é 5:db5520b4645f +é 5:db5520b4645f default 4:9cff3c980b58 (inactive) % hg log (utf-8) changeset: 5:db5520b4645f