mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Bug #1189
- Fixed bug #1189, which caused wide Asian UTF8 characters to be measured as narrow characters (thanks to Roy Zuo).
This commit is contained in:
parent
914447c885
commit
6aa0277749
10 changed files with 45 additions and 36 deletions
1
AUTHORS
1
AUTHORS
|
@ -168,4 +168,5 @@ suggestions:
|
||||||
trHD
|
trHD
|
||||||
Benjamin Weber
|
Benjamin Weber
|
||||||
alparo
|
alparo
|
||||||
|
Roy Zuo
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,8 @@ Bugs
|
||||||
package (thanks to Jakub Wilk).
|
package (thanks to Jakub Wilk).
|
||||||
+ Fixed bug #1183, correcting error message typos (thanks to Jakub Wilk).
|
+ Fixed bug #1183, correcting error message typos (thanks to Jakub Wilk).
|
||||||
+ Fixed bug #1184, correcting man page formatting (thanks to Jakub Wilk).
|
+ Fixed bug #1184, correcting man page formatting (thanks to Jakub Wilk).
|
||||||
|
+ Fixed bug #1189, which caused wide Asian UTF8 characters to be measured as
|
||||||
|
narrow characters (thanks to Roy Zuo).
|
||||||
+ Improved hyphenation by splitting on commas (even if no whitespace after).
|
+ Improved hyphenation by splitting on commas (even if no whitespace after).
|
||||||
Leads to better output of, for example, 'task show', where comma-separated
|
Leads to better output of, for example, 'task show', where comma-separated
|
||||||
lists are common.
|
lists are common.
|
||||||
|
|
|
@ -146,7 +146,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
|
||||||
|
|
||||||
if (print_empty_columns || global_min != 0)
|
if (print_empty_columns || global_min != 0)
|
||||||
{
|
{
|
||||||
unsigned int label_length = utf8_length ((*i)->label ());
|
unsigned int label_length = utf8_width ((*i)->label ());
|
||||||
if (label_length > global_min) global_min = label_length;
|
if (label_length > global_min) global_min = label_length;
|
||||||
if (label_length > global_ideal) global_ideal = label_length;
|
if (label_length > global_ideal) global_ideal = label_length;
|
||||||
minimal.push_back (global_min);
|
minimal.push_back (global_min);
|
||||||
|
|
|
@ -122,7 +122,7 @@ std::string ViewText::render ()
|
||||||
for (unsigned int col = 0; col < _columns.size (); ++col)
|
for (unsigned int col = 0; col < _columns.size (); ++col)
|
||||||
{
|
{
|
||||||
// Headers factor in to width calculations.
|
// Headers factor in to width calculations.
|
||||||
unsigned int global_min = utf8_length (_columns[col]->label ());
|
unsigned int global_min = utf8_width (_columns[col]->label ());
|
||||||
unsigned int global_ideal = global_min;
|
unsigned int global_ideal = global_min;
|
||||||
|
|
||||||
for (unsigned int row = 0; row < _data.size (); ++row)
|
for (unsigned int row = 0; row < _data.size (); ++row)
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <Date.h>
|
#include <Date.h>
|
||||||
#include <ColDescription.h>
|
#include <ColDescription.h>
|
||||||
#include <text.h>
|
#include <text.h>
|
||||||
|
#include <utf8.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
#include <i18n.h>
|
#include <i18n.h>
|
||||||
|
|
||||||
|
@ -106,14 +107,14 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int
|
||||||
int min_desc = longestWord (description);
|
int min_desc = longestWord (description);
|
||||||
int min_anno = indent + Date::length (format);
|
int min_anno = indent + Date::length (format);
|
||||||
minimum = std::max (min_desc, min_anno);
|
minimum = std::max (min_desc, min_anno);
|
||||||
maximum = description.length ();
|
maximum = utf8_width (description);
|
||||||
|
|
||||||
std::map <std::string, std::string> annos;
|
std::map <std::string, std::string> annos;
|
||||||
task.getAnnotations (annos);
|
task.getAnnotations (annos);
|
||||||
std::map <std::string, std::string>::iterator i;
|
std::map <std::string, std::string>::iterator i;
|
||||||
for (i = annos.begin (); i != annos.end (); i++)
|
for (i = annos.begin (); i != annos.end (); i++)
|
||||||
{
|
{
|
||||||
unsigned int len = min_anno + 1 + i->second.length ();
|
unsigned int len = min_anno + 1 + utf8_width (i->second);
|
||||||
if (len > maximum)
|
if (len > maximum)
|
||||||
maximum = len;
|
maximum = len;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +123,7 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int
|
||||||
// Just the text
|
// Just the text
|
||||||
else if (_style == "desc")
|
else if (_style == "desc")
|
||||||
{
|
{
|
||||||
maximum = description.length ();
|
maximum = utf8_width (description);
|
||||||
minimum = longestWord (description);
|
minimum = longestWord (description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,20 +137,20 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int
|
||||||
int min_desc = longestWord (description);
|
int min_desc = longestWord (description);
|
||||||
int min_anno = Date::length (format);
|
int min_anno = Date::length (format);
|
||||||
minimum = std::max (min_desc, min_anno);
|
minimum = std::max (min_desc, min_anno);
|
||||||
maximum = description.length ();
|
maximum = utf8_width (description);
|
||||||
|
|
||||||
std::map <std::string, std::string> annos;
|
std::map <std::string, std::string> annos;
|
||||||
task.getAnnotations (annos);
|
task.getAnnotations (annos);
|
||||||
std::map <std::string, std::string>::iterator i;
|
std::map <std::string, std::string>::iterator i;
|
||||||
for (i = annos.begin (); i != annos.end (); i++)
|
for (i = annos.begin (); i != annos.end (); i++)
|
||||||
maximum += i->second.length () + minimum + 1;
|
maximum += utf8_width (i->second) + minimum + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The te...
|
// The te...
|
||||||
else if (_style == "truncated")
|
else if (_style == "truncated")
|
||||||
{
|
{
|
||||||
minimum = 4;
|
minimum = 4;
|
||||||
maximum = description.length ();
|
maximum = utf8_width (description);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The text [2]
|
// The text [2]
|
||||||
|
@ -159,7 +160,7 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int
|
||||||
task.getAnnotations (annos);
|
task.getAnnotations (annos);
|
||||||
|
|
||||||
// <description> + ' ' + '[' + <count> + ']'
|
// <description> + ' ' + '[' + <count> + ']'
|
||||||
maximum = description.length () + 3 + format ((int)annos.size ()).length ();
|
maximum = utf8_width (description) + 3 + utf8_width (format ((int)annos.size ()));
|
||||||
minimum = longestWord (description);
|
minimum = longestWord (description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +250,7 @@ void ColumnDescription::render (
|
||||||
// This is a des...
|
// This is a des...
|
||||||
else if (_style == "truncated")
|
else if (_style == "truncated")
|
||||||
{
|
{
|
||||||
int len = description.length ();
|
int len = utf8_width (description);
|
||||||
if (len > width)
|
if (len > width)
|
||||||
lines.push_back (color.colorize (description.substr (0, width - 3) + "..."));
|
lines.push_back (color.colorize (description.substr (0, width - 3) + "..."));
|
||||||
else
|
else
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <Context.h>
|
#include <Context.h>
|
||||||
#include <ColProject.h>
|
#include <ColProject.h>
|
||||||
#include <text.h>
|
#include <text.h>
|
||||||
|
#include <utf8.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
#include <i18n.h>
|
#include <i18n.h>
|
||||||
|
|
||||||
|
@ -87,7 +88,7 @@ void ColumnProject::measure (Task& task, unsigned int& minimum, unsigned int& ma
|
||||||
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
|
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
|
||||||
|
|
||||||
minimum = longestWord (project);
|
minimum = longestWord (project);
|
||||||
maximum = project.length ();
|
maximum = utf8_width (project);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <ColTags.h>
|
#include <ColTags.h>
|
||||||
#include <text.h>
|
#include <text.h>
|
||||||
#include <i18n.h>
|
#include <i18n.h>
|
||||||
|
#include <utf8.h>
|
||||||
|
|
||||||
extern Context context;
|
extern Context context;
|
||||||
|
|
||||||
|
@ -85,14 +86,14 @@ void ColumnTags::setStyle (const std::string& value)
|
||||||
// Set the minimum and maximum widths for the value.
|
// Set the minimum and maximum widths for the value.
|
||||||
void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
|
void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
|
||||||
{
|
{
|
||||||
if (_style == "indicator") minimum = maximum = context.config.get ("tag.indicator").length ();
|
if (_style == "indicator") minimum = maximum = utf8_width (context.config.get ("tag.indicator"));
|
||||||
else if (_style == "count") minimum = maximum = 3;
|
else if (_style == "count") minimum = maximum = 3;
|
||||||
else if (_style == "default" ||
|
else if (_style == "default" ||
|
||||||
_style == "list")
|
_style == "list")
|
||||||
{
|
{
|
||||||
std::string tags = task.get (_name);
|
std::string tags = task.get (_name);
|
||||||
minimum = 0;
|
minimum = 0;
|
||||||
maximum = tags.length ();
|
maximum = utf8_width (tags);
|
||||||
|
|
||||||
if (maximum)
|
if (maximum)
|
||||||
{
|
{
|
||||||
|
@ -101,9 +102,9 @@ void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maxim
|
||||||
std::vector <std::string>::iterator i;
|
std::vector <std::string>::iterator i;
|
||||||
for (i = all.begin (); i != all.end (); ++i)
|
for (i = all.begin (); i != all.end (); ++i)
|
||||||
{
|
{
|
||||||
unsigned int length_ = i->length ();
|
unsigned int length = utf8_width (*i);
|
||||||
if (length_ > minimum)
|
if (length > minimum)
|
||||||
minimum = i->length () + 1;
|
minimum = length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,7 +122,18 @@ void ColumnTags::render (
|
||||||
std::string tags = task.get (_name);
|
std::string tags = task.get (_name);
|
||||||
if (tags != "")
|
if (tags != "")
|
||||||
{
|
{
|
||||||
if (_style == "indicator")
|
if (_style == "default" ||
|
||||||
|
_style == "list")
|
||||||
|
{
|
||||||
|
std::replace (tags.begin (), tags.end (), ',', ' ');
|
||||||
|
std::vector <std::string> all;
|
||||||
|
wrapText (all, tags, width, _hyphenate);
|
||||||
|
|
||||||
|
std::vector <std::string>::iterator i;
|
||||||
|
for (i = all.begin (); i != all.end (); ++i)
|
||||||
|
lines.push_back (color.colorize (leftJustify (*i, width)));
|
||||||
|
}
|
||||||
|
else if (_style == "indicator")
|
||||||
{
|
{
|
||||||
lines.push_back (
|
lines.push_back (
|
||||||
color.colorize (
|
color.colorize (
|
||||||
|
@ -135,17 +147,6 @@ void ColumnTags::render (
|
||||||
color.colorize (
|
color.colorize (
|
||||||
rightJustify ("[" + format ((int)all.size ()) + "]", width)));
|
rightJustify ("[" + format ((int)all.size ()) + "]", width)));
|
||||||
}
|
}
|
||||||
else if (_style == "default" ||
|
|
||||||
_style == "list")
|
|
||||||
{
|
|
||||||
std::replace (tags.begin (), tags.end (), ',', ' ');
|
|
||||||
std::vector <std::string> all;
|
|
||||||
wrapText (all, tags, width, _hyphenate);
|
|
||||||
|
|
||||||
std::vector <std::string>::iterator i;
|
|
||||||
for (i = all.begin (); i != all.end (); ++i)
|
|
||||||
lines.push_back (color.colorize (leftJustify (*i, width)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <Date.h>
|
#include <Date.h>
|
||||||
#include <ColUDA.h>
|
#include <ColUDA.h>
|
||||||
#include <text.h>
|
#include <text.h>
|
||||||
|
#include <utf8.h>
|
||||||
#include <i18n.h>
|
#include <i18n.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
@ -99,11 +100,11 @@ void ColumnUDA::measure (Task& task, unsigned int& minimum, unsigned int& maximu
|
||||||
if (format == "")
|
if (format == "")
|
||||||
format = context.config.get ("dateformat");
|
format = context.config.get ("dateformat");
|
||||||
|
|
||||||
minimum = maximum = date.toString (format).length ();
|
minimum = maximum = utf8_width (date.toString (format));
|
||||||
}
|
}
|
||||||
else if (_type == "duration")
|
else if (_type == "duration")
|
||||||
{
|
{
|
||||||
minimum = maximum = Duration (value).formatCompact ().length ();
|
minimum = maximum = utf8_width (Duration (value).formatCompact ());
|
||||||
}
|
}
|
||||||
else if (_type == "string")
|
else if (_type == "string")
|
||||||
{
|
{
|
||||||
|
@ -113,7 +114,7 @@ void ColumnUDA::measure (Task& task, unsigned int& minimum, unsigned int& maximu
|
||||||
}
|
}
|
||||||
else if (_type == "numeric")
|
else if (_type == "numeric")
|
||||||
{
|
{
|
||||||
minimum = maximum = value.length ();
|
minimum = maximum = utf8_width (value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1049,7 +1049,7 @@ std::string leftJustify (const int input, const int width)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
std::string leftJustify (const std::string& input, const int width)
|
std::string leftJustify (const std::string& input, const int width)
|
||||||
{
|
{
|
||||||
return input + std::string (width - utf8_text_length (input), ' ');
|
return input + std::string (width - utf8_text_width (input), ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1071,7 +1071,7 @@ std::string rightJustify (const int input, const int width)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
std::string rightJustify (const std::string& input, const int width)
|
std::string rightJustify (const std::string& input, const int width)
|
||||||
{
|
{
|
||||||
return std::string (width - utf8_text_length (input), ' ') + input;
|
return std::string (width - utf8_text_width (input), ' ') + input;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -40,15 +40,17 @@ if (open my $fh, '>', '455.rc')
|
||||||
ok (-r '455.rc', 'Created 455.rc');
|
ok (-r '455.rc', 'Created 455.rc');
|
||||||
}
|
}
|
||||||
|
|
||||||
# Bug #455 - Text alignment in reports is broken when text contains utf8 characters
|
# Bug #455 - Text alignment in reports is broken when text contains wide utf8
|
||||||
|
# characters
|
||||||
|
|
||||||
qx{../src/task rc:455.rc add abc pro:Bar\x{263A} 2>&1};
|
qx{../src/task rc:455.rc add abc pro:Bar\x{263A} 2>&1};
|
||||||
qx{../src/task rc:455.rc add def pro:Foo! 2>&1};
|
qx{../src/task rc:455.rc add def pro:Foo! 2>&1};
|
||||||
|
|
||||||
my $output = qx{../src/task rc:455.rc ls 2>&1};
|
my $output = qx{../src/task rc:455.rc ls 2>&1};
|
||||||
|
|
||||||
like ($output, qr/\s{7}abc/ms, 'bug 455 - correct spacing in utf8 task');
|
# ' ' + 'Pri' + ' ' == 5
|
||||||
like ($output, qr/\s{7}def/ms, 'bug 455 - correct spacing in non utf8 task');
|
like ($output, qr/\S\s{5}abc/ms, 'bug 455 - correct spacing in utf8 task');
|
||||||
|
like ($output, qr/\S\s{5}def/ms, 'bug 455 - correct spacing in non utf8 task');
|
||||||
|
|
||||||
# Cleanup.
|
# Cleanup.
|
||||||
unlink qw(pending.data completed.data undo.data backlog.data synch.key 455.rc);
|
unlink qw(pending.data completed.data undo.data backlog.data synch.key 455.rc);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue