mirror of
https://github.com/facebook/sapling.git
synced 2024-10-07 15:27:13 +03:00
50b99d1a5a
The current implementation of colwidth was treating 'A'mbiguous characters as wide, which was incorrect in a non-East Asian context. As per http://unicode.org/reports/tr11/#Recommendations, we should instead default to 'narrow' if we don't know better. As character width is dependent on the particular font used and we have no idea what fonts are in use, this recommendation applies. This introduces HGENCODINGAMBIGUOUS to get the old behavior back.
104 lines
3.6 KiB
Python
104 lines
3.6 KiB
Python
# encoding.py - character transcoding support for Mercurial
|
|
#
|
|
# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
import error
|
|
import unicodedata, locale, os
|
|
|
|
def _getpreferredencoding():
|
|
'''
|
|
On darwin, getpreferredencoding ignores the locale environment and
|
|
always returns mac-roman. http://bugs.python.org/issue6202 fixes this
|
|
for Python 2.7 and up. This is the same corrected code for earlier
|
|
Python versions.
|
|
|
|
However, we can't use a version check for this method, as some distributions
|
|
patch Python to fix this. Instead, we use it as a 'fixer' for the mac-roman
|
|
encoding, as it is unlikely that this encoding is the actually expected.
|
|
'''
|
|
try:
|
|
locale.CODESET
|
|
except AttributeError:
|
|
# Fall back to parsing environment variables :-(
|
|
return locale.getdefaultlocale()[1]
|
|
|
|
oldloc = locale.setlocale(locale.LC_CTYPE)
|
|
locale.setlocale(locale.LC_CTYPE, "")
|
|
result = locale.nl_langinfo(locale.CODESET)
|
|
locale.setlocale(locale.LC_CTYPE, oldloc)
|
|
|
|
return result
|
|
|
|
_encodingfixers = {
|
|
'646': lambda: 'ascii',
|
|
'ANSI_X3.4-1968': lambda: 'ascii',
|
|
'mac-roman': _getpreferredencoding
|
|
}
|
|
|
|
try:
|
|
encoding = os.environ.get("HGENCODING")
|
|
if not encoding:
|
|
encoding = locale.getpreferredencoding() or 'ascii'
|
|
encoding = _encodingfixers.get(encoding, lambda: encoding)()
|
|
except locale.Error:
|
|
encoding = 'ascii'
|
|
encodingmode = os.environ.get("HGENCODINGMODE", "strict")
|
|
fallbackencoding = 'ISO-8859-1'
|
|
|
|
def tolocal(s):
|
|
"""
|
|
Convert a string from internal UTF-8 to local encoding
|
|
|
|
All internal strings should be UTF-8 but some repos before the
|
|
implementation of locale support may contain latin1 or possibly
|
|
other character sets. We attempt to decode everything strictly
|
|
using UTF-8, then Latin-1, and failing that, we use UTF-8 and
|
|
replace unknown characters.
|
|
"""
|
|
for e in ('UTF-8', fallbackencoding):
|
|
try:
|
|
u = s.decode(e) # attempt strict decoding
|
|
return u.encode(encoding, "replace")
|
|
except LookupError, k:
|
|
raise error.Abort("%s, please check your locale settings" % k)
|
|
except UnicodeDecodeError:
|
|
pass
|
|
u = s.decode("utf-8", "replace") # last ditch
|
|
return u.encode(encoding, "replace")
|
|
|
|
def fromlocal(s):
|
|
"""
|
|
Convert a string from the local character encoding to UTF-8
|
|
|
|
We attempt to decode strings using the encoding mode set by
|
|
HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
|
|
characters will cause an error message. Other modes include
|
|
'replace', which replaces unknown characters with a special
|
|
Unicode character, and 'ignore', which drops the character.
|
|
"""
|
|
try:
|
|
return s.decode(encoding, encodingmode).encode("utf-8")
|
|
except UnicodeDecodeError, inst:
|
|
sub = s[max(0, inst.start - 10):inst.start + 10]
|
|
raise error.Abort("decoding near '%s': %s!" % (sub, inst))
|
|
except LookupError, k:
|
|
raise error.Abort("%s, please check your locale settings" % k)
|
|
|
|
# How to treat ambiguous-width characters. Set to 'wide' to treat as wide.
|
|
ambiguous = os.environ.get("HGENCODINGAMBIGUOUS", "narrow")
|
|
|
|
def colwidth(s):
|
|
"Find the column width of a UTF-8 string for display"
|
|
d = s.decode(encoding, 'replace')
|
|
if hasattr(unicodedata, 'east_asian_width'):
|
|
wide = "WF"
|
|
if ambiguous == "wide":
|
|
wide = "WFA"
|
|
w = unicodedata.east_asian_width
|
|
return sum([w(c) in wide and 2 or 1 for c in d])
|
|
return len(d)
|
|
|