diff options
context:
space:
mode:
Diffstat (limited to 'source/ui-blame.c')
-rw-r--r--source/ui-blame.c62
1 file changed, 52 insertions, 10 deletions
diff --git a/source/ui-blame.c b/source/ui-blame.c
index 418e3ae..d6779c0 100644
--- a/source/ui-blame.c
+++ b/source/ui-blame.c
@@ -16,6 +16,38 @@
#include "blame.h"
+/* Blame coalesces neighbouring lines from one commit, but a commit that
+ * touched several separate parts of the file still comes back once per part.
+ * Each of those repeats a full commit parse, so the rendered detail is kept
+ * and looked up by object id. */
+static struct string_list suspect_details = STRING_LIST_INIT_DUP;
+
+static void free_suspect_details(void)
+{
+ struct string_list_item *item;
+
+ for_each_string_list_item(item, &suspect_details)
+ free(item->util);
+ string_list_clear(&suspect_details, 0);
+}
+
+/* Write out a run of one repeated character. A single blame entry can cover
+ * the whole file, so the run is handed on in batches rather than built whole. */
+static void emit_chars(char ch, unsigned long count)
+{
+ struct strbuf run = STRBUF_INIT;
+
+ while (count) {
+ unsigned long n = count < HTML_BATCH ? count : HTML_BATCH;
+
+ strbuf_addchars(&run, ch, n);
+ html_raw(run.buf, run.len);
+ strbuf_reset(&run);
+ count -= n;
+ }
+ strbuf_release(&run);
+}
+
static char *emit_suspect_detail(struct blame_origin *suspect)
{
struct commitinfo *info;
@@ -47,7 +79,6 @@ static void emit_blame_entry_hash(struct blame_entry *ent)
{
struct blame_origin *suspect = ent->suspect;
struct object_id *oid = &suspect->commit->object.oid;
- unsigned long line = 0;
char *detail = emit_suspect_detail(suspect);
html("<span class='oid'>");
@@ -65,28 +96,36 @@ static void emit_blame_entry_hash(struct blame_entry *ent)
suspect->path);
}
- while (line++ < ent->num_lines)
- html("\n");
+ // Batched rather than one write per line of the entry.
+ emit_chars('\n', ent->num_lines);
}
static void emit_blame_entry_linenumber(struct blame_entry *ent)
{
const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n";
+ struct strbuf numbers = STRBUF_INIT;
+ int lineno = ent->lno;
- unsigned long lineno = ent->lno;
- while (lineno < ent->lno + ent->num_lines)
- htmlf(numberfmt, ++lineno);
+ // Batched rather than a formatted write per line of the entry.
+ while (lineno < ent->lno + ent->num_lines) {
+ strbuf_addf(&numbers, numberfmt, ++lineno);
+ if (numbers.len >= HTML_BATCH) {
+ html_raw(numbers.buf, numbers.len);
+ strbuf_reset(&numbers);
+ }
+ }
+ html_raw(numbers.buf, numbers.len);
+ strbuf_release(&numbers);
}
static void emit_blame_entry_line_background(struct blame_scoreboard *sb,
struct blame_entry *ent)
{
- unsigned long line;
+ int line;
size_t len, maxlen = 2;
const char* pos, *endpos;
for (line = ent->lno; line < ent->lno + ent->num_lines; line++) {
- html("\n");
pos = blame_nth_line(sb, line);
endpos = blame_nth_line(sb, line + 1);
len = 0;
@@ -99,8 +138,11 @@ static void emit_blame_entry_line_background(struct blame_scoreboard *sb,
maxlen = len;
}
- for (len = 0; len < maxlen - 1; len++)
- html(" ");
+ // The widest line decides the padding, so the entry has to be measured
+ // before any of it is written. The newlines do not depend on that
+ // measurement, so both runs go out batched once it is known.
+ emit_chars('\n', ent->num_lines);
+ emit_chars(' ', maxlen - 1);
}
struct walk_tree_context {