# # # patch "database.cc" # from [4f43f637ccc0ddc5f685fd85db8e7b45fe85c44d] # to [60b972204926d91705bf155776984c2dd0f8a2af] # # patch "transforms.cc" # from [dafb8e0440ee40b6bbc594e6dbaf07731c76f11f] # to [b65ba4a193f66e341ff1cde29848fb2f343d6dd6] # # patch "ui.cc" # from [9544a3fa1a5de8eb280b0bee314f53cde270964f] # to [c9520fd1a64974b99cec62bf4f1a870bb418ec88] # # patch "ui.hh" # from [76ebb302ccb046a38367933123f7569af2d011f3] # to [e0e20f167b6f3abad9608477d61bc1a29f8cefff] # ============================================================ --- database.cc 4f43f637ccc0ddc5f685fd85db8e7b45fe85c44d +++ database.cc 60b972204926d91705bf155776984c2dd0f8a2af @@ -935,7 +935,7 @@ extend_path_if_not_cycle(string table_name, shared_ptr p, hexenc const & ext, - set< hexenc > seen_nodes, + set< hexenc > & seen_nodes, vector< shared_ptr > & next_paths) { for (version_path::const_iterator i = p->begin(); i != p->end(); ++i) @@ -1060,6 +1060,7 @@ } } + I(selected_path || !next_paths.empty()); live_paths = next_paths; } ============================================================ --- transforms.cc dafb8e0440ee40b6bbc594e6dbaf07731c76f11f +++ transforms.cc b65ba4a193f66e341ff1cde29848fb2f343d6dd6 @@ -492,25 +492,29 @@ size_t display_width(utf8 const & utf) { - // this function is called many thousands of times by the tickers, so we - // try and avoid performing heap allocations by starting with a reasonable - // size buffer, and only ever growing the buffer if needed. - static size_t widebuf_sz = 128; - static boost::scoped_array widebuf(new wchar_t[widebuf_sz]); - - size_t len = mbstowcs(0, utf().c_str(), 0) + 1; - - if (len == static_cast(-1)) - return utf().length(); // conversion failed; punt and return original length - - if (len > widebuf_sz) { - widebuf.reset(new wchar_t[len]); - widebuf_sz = len; - } - - mbstowcs(widebuf.get(), utf().c_str(), widebuf_sz); - - return wcswidth(widebuf.get(), widebuf_sz); + std::string const & u = utf(); + size_t sz = 0; + std::string::const_iterator i = u.begin(); + while (i != u.end()) + { + if (UNLIKELY(static_cast(*i) & static_cast(0x80))) + { + // A UTF-8 escape: consume the full escape. + ++i; + ++sz; + while (i != u.end() + && (static_cast(*i) & static_cast(0x80)) + && (!(static_cast(*i) & static_cast(0x40)))) + ++i; + } + else + { + // An ASCII-like character in the range 0..0x7F. + ++i; + ++sz; + } + } + return sz; } // Lots of gunk to avoid charset conversion as much as possible. Running ============================================================ --- ui.cc 9544a3fa1a5de8eb280b0bee314f53cde270964f +++ ui.cc c9520fd1a64974b99cec62bf4f1a870bb418ec88 @@ -84,76 +84,103 @@ void tick_write_count::write_ticks() { - string tickline1, tickline2; - bool first_tick = true; + vector tick_widths; + vector tick_title_strings; + vector tick_count_strings; - tickline1 = "monotone: "; - tickline2 = "\rmonotone:"; - - unsigned int width; - unsigned int minwidth = 7; for (map::const_iterator i = ui.tickers.begin(); i != ui.tickers.end(); ++i) { - width = 1 + display_width(utf8(i->second->name)); - if (!first_tick) - { - tickline1 += " | "; - tickline2 += " |"; - } - first_tick = false; - if (display_width(utf8(i->second->name)) < minwidth) - { - tickline1.append(minwidth - display_width(utf8(i->second->name)),' '); - width += minwidth - display_width(utf8(i->second->name)); - } - tickline1 += i->second->name; - string count; - if (i->second->kilocount && i->second->ticks >= 10000) - { // automatic unit conversion is enabled - float div; + if (i->second->kilocount) + { + // automatic unit conversion is enabled + float div = 1.0; const char *message; - if (i->second->ticks >= 1048576) { - // ticks >=1MB, use Mb - div = 1048576; - // xgettext: mebibytes (2^20 bytes) - message = N_("%.1f M"); - } else { - // ticks <1MB, use kb - div = 1024; - // xgettext: kibibytes (2^10 bytes) - message = N_("%.1f k"); - } - // we reset the mod to the divider, to avoid spurious screen updates + + if (i->second->ticks >= 1073741824/10) + { + div = 1073741824; + // xgettext: gibibytes (2^30 bytes) + message = N_("%.1f G"); + } + else if (i->second->ticks >= 1048576/10) + { + div = 1048576; + // xgettext: mebibytes (2^20 bytes) + message = N_("%.1f M"); + } + else + { + div = 1024; + // xgettext: kibibytes (2^10 bytes) + message = N_("%.1f k"); + } + // We reset the mod to the divider, to avoid spurious screen updates. i->second->mod = static_cast(div / 10.0); count = (F(message) % (i->second->ticks / div)).str(); } else if (i->second->use_total) { + // We know that we're going to eventually have 'total' displayed + // twice on screen, plus a slash. So we should pad out this field + // to that eventual size to avoid spurious re-issuing of the + // tick titles as we expand to the goal. + string complete = (F("%d/%d") % i->second->total % i->second->total).str(); // xgettext: bytes - count = (F("%d/%d") % i->second->ticks % i->second->total).str(); + string current = (F("%d/%d") % i->second->ticks % i->second->total).str(); + count.append(complete.size() - current.size(),' '); + count.append(current); } else { // xgettext: bytes count = (F("%d") % i->second->ticks).str(); } - - if (display_width(utf8(count)) < width) + + size_t title_width = display_width(utf8(i->second->name)); + size_t count_width = display_width(utf8(count)); + size_t max_width = title_width > count_width ? title_width : count_width; + + string name; + name.append(max_width - i->second->name.size(), ' '); + name.append(i->second->name); + + string count2; + count2.append(max_width - count.size(), ' '); + count2.append(count); + + tick_title_strings.push_back(name); + tick_count_strings.push_back(count2); + tick_widths.push_back(max_width); + } + + string tickline1; + bool write_tickline1 = !(ui.last_write_was_a_tick + && (tick_widths == last_tick_widths)); + if (write_tickline1) + { + // Reissue the titles if the widths have changed. + tickline1 = "monotone: "; + for (size_t i = 0; i < tick_widths.size(); ++i) { - tickline2.append(width - display_width(utf8(count)),' '); + if (i != 0) + tickline1.append(" | "); + tickline1.append(idx(tick_title_strings, i)); } - else if (display_width(utf8(count)) > width) - { - // FIXME: not quite right, because substr acts on bytes rather than - // characters; but there are always more bytes than characters, so - // at worst this will just chop off a little too much. - count = count.substr(display_width(utf8(count)) - width); - } - tickline2 += count; + last_tick_widths = tick_widths; + write_tickline1 = true; } + // Always reissue the counts, with a \r to clear. + string tickline2 = "\rmonotone: "; + for (size_t i = 0; i < tick_widths.size(); ++i) + { + if (i != 0) + tickline2.append(" | "); + tickline2.append(idx(tick_count_strings, i)); + } + if (!ui.tick_trailer.empty()) { tickline2 += " "; @@ -166,8 +193,11 @@ last_tick_len = curr_sz; unsigned int tw = terminal_width(); - if(!ui.last_write_was_a_tick) + if(write_tickline1) { + if (ui.last_write_was_a_tick) + clog << "\n"; + if (tw && display_width(utf8(tickline1)) > tw) { // FIXME: may chop off more than necessary (because we chop by ============================================================ --- ui.hh 76ebb302ccb046a38367933123f7569af2d011f3 +++ ui.hh e0e20f167b6f3abad9608477d61bc1a29f8cefff @@ -51,6 +51,7 @@ void write_ticks(); void clear_line(); private: + std::vector last_tick_widths; size_t last_tick_len; };