From 568a5e5e8b31b9be4a88cca888e3d1cbc84669e9 Mon Sep 17 00:00:00 2001
From: Adam Waldenberg <adam.waldenberg@ejwa.se>
Date: Fri, 19 Dec 2014 03:02:08 +0100
Subject: [PATCH] Fixed the terminal column alignment for languages with
 multi-column chars.

This is for example needed for the Chinese and Korean translations to work
correctly when using terminal output.
---
 gitinspector/blame.py    |  6 +++---
 gitinspector/changes.py  |  7 ++++---
 gitinspector/terminal.py | 17 +++++++++++++++++
 gitinspector/timeline.py | 17 +++++++++++------
 4 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/gitinspector/blame.py b/gitinspector/blame.py
index eb1bb58..a9c0629 100644
--- a/gitinspector/blame.py
+++ b/gitinspector/blame.py
@@ -276,11 +276,11 @@ class BlameOutput(Outputable):
 			terminal.clear_row()
 
 		print(textwrap.fill(_(BLAME_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
-		terminal.printb(_("Author").ljust(21) + _("Rows").rjust(10) + _("Stability").rjust(15) + _("Age").rjust(13) +
-		                _("% in comments").rjust(20))
+		terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Rows"), 10) + terminal.rjust(_("Stability"), 15) +
+		                terminal.rjust(_("Age"), 13) + terminal.rjust(_("% in comments"), 20))
 
 		for i in sorted(__blame__.get_summed_blames().items()):
-			print(i[0].ljust(20)[0:20], end=" ")
+			print(terminal.ljust(i[0], 20)[0:20 - terminal.get_excess_column_count(i[0])], end=" ")
 			print(str(i[1].rows).rjust(10), end=" ")
 			print("{0:.1f}".format(Blame.get_stability(i[0], i[1].rows, self.changes)).rjust(14), end=" ")
 			print("{0:.1f}".format(float(i[1].skew) / i[1].rows).rjust(12), end=" ")
diff --git a/gitinspector/changes.py b/gitinspector/changes.py
index 2fc03cd..472e788 100644
--- a/gitinspector/changes.py
+++ b/gitinspector/changes.py
@@ -278,14 +278,15 @@ class ChangesOutput(Outputable):
 
 		if authorinfo_list:
 			print(textwrap.fill(_(HISTORICAL_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
-			terminal.printb(_("Author").ljust(21) + _("Commits").rjust(13) + _("Insertions").rjust(14) +
-			                _("Deletions").rjust(15) + _("% of changes").rjust(16))
+			terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Commits"), 13) +
+			                terminal.rjust(_("Insertions"), 14) + terminal.rjust(_("Deletions"), 15) +
+					terminal.rjust(_("% of changes"), 16))
 
 			for i in sorted(authorinfo_list):
 				authorinfo = authorinfo_list.get(i)
 				percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
 
-				print(i.ljust(20)[0:20], end=" ")
+				print(terminal.ljust(i, 20)[0:20 - terminal.get_excess_column_count(i)], end=" ")
 				print(str(authorinfo.commits).rjust(13), end=" ")
 				print(str(authorinfo.insertions).rjust(13), end=" ")
 				print(str(authorinfo.deletions).rjust(14), end=" ")
diff --git a/gitinspector/terminal.py b/gitinspector/terminal.py
index f912d19..55b8b99 100644
--- a/gitinspector/terminal.py
+++ b/gitinspector/terminal.py
@@ -22,6 +22,7 @@ import codecs
 import os
 import platform
 import sys
+import unicodedata
 
 __bold__ =  "\033[1m"
 __normal__ =  "\033[0;0m"
@@ -128,3 +129,19 @@ def check_terminal_encoding():
 	if sys.stdout.isatty() and (sys.stdout.encoding == None or sys.stdin.encoding == None):
 		print(_("WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. "
 		        "The encoding can be configured with the environment variable 'PYTHONIOENCODING'."), file=sys.stderr)
+
+def get_excess_column_count(string):
+	width_mapping = {'F': 2, 'H': 1, 'W': 2, 'Na': 1, 'N': 1, 'A': 1}
+	result = 0
+
+	for c in string:
+		w = unicodedata.east_asian_width(c)
+		result += width_mapping[w]
+
+	return result - len(string)
+
+def ljust(string, pad):
+	return string.ljust(pad - get_excess_column_count(string))
+
+def rjust(string, pad):
+	return string.rjust(pad - get_excess_column_count(string))
diff --git a/gitinspector/timeline.py b/gitinspector/timeline.py
index 0b49bc4..30b74bd 100644
--- a/gitinspector/timeline.py
+++ b/gitinspector/timeline.py
@@ -111,16 +111,17 @@ TIMELINE_INFO_TEXT = N_("The following history timeline has been gathered from t
 MODIFIED_ROWS_TEXT = N_("Modified Rows:")
 
 def __output_row__text__(timeline_data, periods, names):
-	print("\n" + terminal.__bold__ + _("Author").ljust(20), end=" ")
+	print("\n" + terminal.__bold__ + terminal.ljust(_("Author"), 20), end=" ")
 	
 	for period in periods:
-		print(period.rjust(10), end=" ")
+		print(terminal.rjust(period, 10), end=" ")
 
 	print(terminal.__normal__)
 
 	for name in names:
 		if timeline_data.is_author_in_periods(periods, name[0]):
-			print(name[0].ljust(20)[0:20], end=" ")
+			print(terminal.ljust(name[0], 20)[0:20 - terminal.get_excess_column_count(name[0])], end=" ")
+
 			for period in periods:
 				multiplier = timeline_data.get_multiplier(period, 9)
 				signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
@@ -129,11 +130,15 @@ def __output_row__text__(timeline_data, periods, names):
 				               len(signs_str) == 0 else signs_str).rjust(10), end=" ")
 			print("")
 
-	print(terminal.__bold__  + _(MODIFIED_ROWS_TEXT).ljust(20) + terminal.__normal__, end=" ")
+	print(terminal.__bold__  + terminal.ljust(_(MODIFIED_ROWS_TEXT), 20) + terminal.__normal__, end=" ")
 
 	for period in periods:
-		total_changes = timeline_data.get_total_changes_in_period(period)
-		print("" + str(total_changes[2]).rjust(10), end=" ")
+		total_changes = str(timeline_data.get_total_changes_in_period(period)[2])
+
+		if hasattr(total_changes, 'decode'):
+			total_changes = total_changes.decode("utf-8", "replace")
+
+		print(terminal.rjust(total_changes, 10), end=" ")
 
 	print("")