user/dev discussion of public-inbox itself
 help / color / mirror / code / Atom feed
* [PATCH 01/11] feed: remove threading from index
@ 2015-08-20  2:57 Eric Wong
  2015-08-20  2:57 ` [PATCH 02/11] feed: move timestamp parsing to view Eric Wong
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

We'll be making the index smarter for people with search
support enabled.  Otherwise, it'll be chronological and
a bit dumb.  At least it'll use less memory.
---
 lib/PublicInbox/Feed.pm | 72 ++++++++++++++++---------------------------------
 1 file changed, 23 insertions(+), 49 deletions(-)

diff --git a/lib/PublicInbox/Feed.pm b/lib/PublicInbox/Feed.pm
index 95bde4f..253eed2 100644
--- a/lib/PublicInbox/Feed.pm
+++ b/lib/PublicInbox/Feed.pm
@@ -40,7 +40,7 @@ sub generate {
 
 	my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
 	each_recent_blob($ctx, sub {
-		my ($add) = @_;
+		my ($add, undef) = @_;
 		add_to_feed($feed_opts, $feed, $add, $git);
 	});
 	$git = undef; # destroy pipes
@@ -50,7 +50,6 @@ sub generate {
 
 sub generate_html_index {
 	my ($class, $ctx) = @_;
-	require PublicInbox::Thread;
 
 	my $max = $ctx->{max} || MAX_PER_PAGE;
 	my $feed_opts = get_feedopts($ctx);
@@ -58,30 +57,27 @@ sub generate_html_index {
 	my $title = $feed_opts->{description} || '';
 	$title = PublicInbox::Hval->new_oneline($title)->as_html;
 
-	my @messages;
-	my $git_dir = $ctx->{git_dir};
-	my $git = PublicInbox::GitCatFile->new($git_dir);
-	my ($first, $last) = each_recent_blob($ctx, sub {
-		mime_load_for_sort($git, $_[0], \@messages);
-	});
-	$git = undef; # destroy pipes.
-
-	my $th = PublicInbox::Thread->new(@messages);
-	$th->thread;
 	my $html = "<html><head><title>$title</title>" .
 		'<link rel="alternate" title="Atom feed"' . "\nhref=\"" .
 		$feed_opts->{atomurl} . "\"\ntype=\"application/atom+xml\"/>" .
 		'</head><body>' . PRE_WRAP;
 
-	# sort child messages in chronological order
-	$th->order(*PublicInbox::Thread::sort_ts);
-
-	# except we sort top-level messages reverse chronologically
-	my $state = [ $ctx->{srch}, {}, $first, 0 ];
-	for (PublicInbox::Thread::rsort_ts($th->rootset)) {
-		dump_msg($_, 0, \$html, $state)
-	}
+	my $state;
+	my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+	my (undef, $last) = each_recent_blob($ctx, sub {
+		my ($path, $commit) = @_;
+		unless (defined $state) {
+			$state = [ $ctx->{srch}, {}, $commit, 0 ];
+		}
+		my $mime = do_cat_mail($git, $_[0]) or return 0;
+		my $t = eval { str2time($mime->header('Date')) };
+		defined($t) or $t = 0;
+		$mime->header_set('X-PI-TS', $t);
+		$html .= PublicInbox::View->index_entry($mime, 0, $state);
+		1;
+	});
 	Email::Address->purge_cache;
+	$git = undef; # destroy pipes.
 
 	my $footer = nav_footer($ctx->{cgi}, $last, $feed_opts, $state);
 	if ($footer) {
@@ -144,12 +140,12 @@ sub each_recent_blob {
 	my %deleted; # only an optimization at this point
 	my $last;
 	my $nr = 0;
-	my @commits = ();
+	my ($cur_commit, $first_commit, $last_commit);
 	while (my $line = <$log>) {
 		if ($line =~ /$addmsg/o) {
 			my $add = $1;
-			next if $deleted{$add};
-			$nr += $cb->($add);
+			next if $deleted{$add}; # optimization-only
+			$nr += $cb->($add, $cur_commit);
 			if ($nr >= $max) {
 				$last = 1;
 				last;
@@ -157,24 +153,23 @@ sub each_recent_blob {
 		} elsif ($line =~ /$delmsg/o) {
 			$deleted{$1} = 1;
 		} elsif ($line =~ /^commit (${hex}{7,40})/o) {
-			push @commits, $1;
+			$cur_commit = $1;
+			$first_commit = $1 unless defined $first_commit;
 		}
 	}
 
 	if ($last) {
 		while (my $line = <$log>) {
 			if ($line =~ /^commit (${hex}{7,40})/o) {
-				push @commits, $1;
+				$last_commit = $1;
 				last;
 			}
 		}
-	} else {
-		push @commits, undef;
 	}
 
 	close $log; # we may EPIPE here
 	# for pagination
-	($commits[0], $commits[-1]);
+	($first_commit, $last_commit);
 }
 
 # private functions below
@@ -279,16 +274,6 @@ sub add_to_feed {
 	1;
 }
 
-sub dump_msg {
-	my ($self, $level, $html, $state) = @_;
-	my $mime = $self->message;
-	if ($mime) {
-		$$html .= PublicInbox::View->index_entry($mime, $level, $state);
-	}
-	dump_msg($self->child, $level+1, $html, $state) if $self->child;
-	dump_msg($self->next, $level, $html, $state) if $self->next;
-}
-
 sub do_cat_mail {
 	my ($git, $path) = @_;
 	my $mime = eval {
@@ -298,15 +283,4 @@ sub do_cat_mail {
 	$@ ? undef : $mime;
 }
 
-sub mime_load_for_sort {
-	my ($git, $path, $messages) = @_;
-	my $mime = do_cat_mail($git, $path) or return 0;
-
-	my $t = eval { str2time($mime->header('Date')) };
-	defined($t) or $t = 0;
-	$mime->header_set('X-PI-TS', $t);
-	push @$messages, $mime;
-	1;
-}
-
 1;
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 02/11] feed: move timestamp parsing to view
  2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
@ 2015-08-20  2:57 ` Eric Wong
  2015-08-20  2:57 ` [PATCH 03/11] use tables for rendering comment nesting Eric Wong
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

We don't need share duplicate logic across both files.
---
 lib/PublicInbox/Feed.pm |  5 +----
 lib/PublicInbox/View.pm | 17 ++++++++++++-----
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/lib/PublicInbox/Feed.pm b/lib/PublicInbox/Feed.pm
index 253eed2..0b7ef7f 100644
--- a/lib/PublicInbox/Feed.pm
+++ b/lib/PublicInbox/Feed.pm
@@ -5,7 +5,7 @@ use strict;
 use warnings;
 use Email::Address;
 use Email::MIME;
-use Date::Parse qw(strptime str2time);
+use Date::Parse qw(strptime);
 use PublicInbox::Hval;
 use PublicInbox::GitCatFile;
 use PublicInbox::View;
@@ -70,9 +70,6 @@ sub generate_html_index {
 			$state = [ $ctx->{srch}, {}, $commit, 0 ];
 		}
 		my $mime = do_cat_mail($git, $_[0]) or return 0;
-		my $t = eval { str2time($mime->header('Date')) };
-		defined($t) or $t = 0;
-		$mime->header_set('X-PI-TS', $t);
 		$html .= PublicInbox::View->index_entry($mime, 0, $state);
 		1;
 	});
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index b3545a4..4a8e54a 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -97,11 +97,14 @@ sub index_entry {
 	}
 
 	my $ts = $mime->header('X-PI-TS');
-	my $fmt = '%Y-%m-%d %H:%M UTC';
+	unless (defined $ts) {
+		$ts = msg_timestamp($mime);
+	}
+	my $fmt = '%Y-%m-%d %H:%M';
 	$ts = POSIX::strftime($fmt, gmtime($ts));
 
 	$rv .= "$pfx<b\nid=\"$id\">$subj</b>\n$pfx";
-	$rv .= "- by $from @ $ts - ";
+	$rv .= "- by $from @ $ts UTC - ";
 	$rv .= "<a\nid=\"s$midx\"\nhref=\"#s$next\">next</a>";
 	if ($prev >= 0) {
 		$rv .= "/<a\nhref=\"#s$prev\">prev</a>";
@@ -583,13 +586,17 @@ sub load_results {
 			Email::MIME->new($str);
 		};
 		unless ($@) {
-			my $t = eval { str2time($mime->header('Date')) };
-			defined($t) or $t = 0;
-			$mime->header_set('X-PI-TS', $t);
+			$mime->header_set('X-PI-TS', msg_timestamp($mime));
 			push @msgs, $mime;
 		}
 	}
 	\@msgs;
 }
 
+sub msg_timestamp {
+	my ($mime) = @_;
+	my $ts = eval { str2time($mime->header('Date')) };
+	defined($ts) ? $ts : 0;
+}
+
 1;
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 03/11] use tables for rendering comment nesting
  2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
  2015-08-20  2:57 ` [PATCH 02/11] feed: move timestamp parsing to view Eric Wong
@ 2015-08-20  2:57 ` Eric Wong
  2015-08-20  2:57 ` [PATCH 04/11] view: avoid nesting <a> tags from auto-linkification Eric Wong
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

This is more space efficient since we don't need to place padding
bytes in front of every line.  While this unfortunately does not
render well on lynx; w3m, links, elinks can all render tables
sanely.

Tables are also superior for long lines which require wrapping
inside <pre> containers.
---
 lib/PublicInbox/Feed.pm |  7 +++----
 lib/PublicInbox/View.pm | 43 ++++++++++++++++++-------------------------
 2 files changed, 21 insertions(+), 29 deletions(-)

diff --git a/lib/PublicInbox/Feed.pm b/lib/PublicInbox/Feed.pm
index 0b7ef7f..8bfd19e 100644
--- a/lib/PublicInbox/Feed.pm
+++ b/lib/PublicInbox/Feed.pm
@@ -12,7 +12,6 @@ use PublicInbox::View;
 use constant {
 	DATEFMT => '%Y-%m-%dT%H:%M:%SZ', # atom standard
 	MAX_PER_PAGE => 25, # this needs to be tunable
-	PRE_WRAP => "<pre\nstyle=\"white-space:pre-wrap\">",
 };
 
 # main function
@@ -60,7 +59,7 @@ sub generate_html_index {
 	my $html = "<html><head><title>$title</title>" .
 		'<link rel="alternate" title="Atom feed"' . "\nhref=\"" .
 		$feed_opts->{atomurl} . "\"\ntype=\"application/atom+xml\"/>" .
-		'</head><body>' . PRE_WRAP;
+		'</head><body>';
 
 	my $state;
 	my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
@@ -80,9 +79,9 @@ sub generate_html_index {
 	if ($footer) {
 		my $list_footer = $ctx->{footer};
 		$footer .= "\n" . $list_footer if $list_footer;
-		$footer = "<hr />" . PRE_WRAP . "$footer</pre>";
+		$footer = "<hr /><pre>$footer</pre>";
 	}
-	$html . "</pre>$footer</body></html>";
+	$html .= "$footer</body></html>";
 }
 
 # private subs
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 4a8e54a..800c1a2 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -33,8 +33,9 @@ sub msg_html {
 	}
 	headers_to_html_header($mime, $full_pfx, $srch) .
 		multipart_text_as_html($mime, $full_pfx) .
-		'</pre><hr />' . PRE_WRAP .
-		html_footer($mime, 1, $full_pfx, $srch) . $footer .
+		'</pre><hr /><pre>' .
+		html_footer($mime, 1, $full_pfx, $srch) .
+		$footer .
 		'</pre></body></html>';
 }
 
@@ -51,7 +52,6 @@ sub index_entry {
 	my ($srch, $seen, $first_commit) = @$state;
 	my $midx = $state->[3]++;
 	my ($prev, $next) = ($midx - 1, $midx + 1);
-	my $rv = '';
 	my $part_nr = 0;
 	my $enc_msg = enc_for($mime->header("Content-Type"));
 	my $subj = $mime->header('Subject');
@@ -69,15 +69,9 @@ sub index_entry {
 
 	$from = PublicInbox::Hval->new_oneline($from)->as_html;
 	$subj = PublicInbox::Hval->new_oneline($subj)->as_html;
-	my $pfx = ('  ' x $level);
 	my $root_anchor = $seen->{root_anchor};
-	my $path;
 	my $more = 'permalink';
-	if ($root_anchor) {
-		$path = '../';
-	} else {
-		$path = '';
-	}
+	my $path = $root_anchor ? '../' : '';
 	my $href = $mid->as_href;
 	my $irt = $header_obj->header_raw('In-Reply-To');
 	my ($anchor_idx, $anchor, $t_anchor);
@@ -88,7 +82,6 @@ sub index_entry {
 	} else {
 		$t_anchor = '';
 	}
-
 	if (defined $srch) {
 		$subj = "<a\nhref=\"${path}t/$href.html#u\">$subj</a>";
 	}
@@ -103,7 +96,12 @@ sub index_entry {
 	my $fmt = '%Y-%m-%d %H:%M';
 	$ts = POSIX::strftime($fmt, gmtime($ts));
 
-	$rv .= "$pfx<b\nid=\"$id\">$subj</b>\n$pfx";
+	my $rv = "<table\nsummary=l$level><tr>";
+	if ($level) {
+		$rv .= '<td><pre>' . ('  ' x $level) . '</pre></td>';
+	}
+	$rv .= '<td>' . PRE_WRAP;
+	$rv .= "<b\nid=\"$id\">$subj</b>\n";
 	$rv .= "- by $from @ $ts UTC - ";
 	$rv .= "<a\nid=\"s$midx\"\nhref=\"#s$next\">next</a>";
 	if ($prev >= 0) {
@@ -115,13 +113,12 @@ sub index_entry {
 	my $fhref = "${path}f/$href.html";
 	# scan through all parts, looking for displayable text
 	$mime->walk_parts(sub {
-		$rv .= index_walk($_[0], $pfx, $enc_msg, $part_nr, $fhref,
-				  \$more);
+		$rv .= index_walk($_[0], $enc_msg, $part_nr, $fhref, \$more);
 		$part_nr++;
 	});
 	$mime->body_set('');
 
-	$rv .= "\n$pfx<a\nhref=\"$mhref\">$more</a> ";
+	$rv .= "\n<a\nhref=\"$mhref\">$more</a> ";
 	my $txt = "${path}m/$href.txt";
 	$rv .= "<a\nhref=\"$txt\">raw</a> ";
 	$rv .= html_footer($mime, 0);
@@ -141,7 +138,7 @@ sub index_entry {
 		       "threadlink</a>";
 	}
 
-	$rv . "\n\n";
+	$rv .= '</pre></td></tr></table>';
 }
 
 sub thread_html {
@@ -168,7 +165,7 @@ sub thread_html {
 	}
 	$next .= "</a>, back to <a\nhref=\"../\">index</a>\n";
 
-	$rv .= "</pre><hr />" . PRE_WRAP . $next . $foot . "</pre>";
+	$rv .= "<hr />" . PRE_WRAP . $next . $foot . "</pre>";
 }
 
 sub subject_path_html {
@@ -188,13 +185,13 @@ sub subject_path_html {
 	my $final_anchor = $state->[3];
 	my $next = "<a\nid=\"s$final_anchor\">end of thread</a>\n";
 
-	$rv .= "</pre><hr />" . PRE_WRAP . $next . $foot . "</pre>";
+	$rv .= "<hr />" . PRE_WRAP . $next . $foot . "</pre>";
 }
 
 # only private functions below.
 
 sub index_walk {
-	my ($part, $pfx, $enc_msg, $part_nr, $fhref, $more) = @_;
+	my ($part, $enc_msg, $part_nr, $fhref, $more) = @_;
 	my $rv = '';
 	return $rv if $part->subparts; # walk_parts already recurses
 	my $ct = $part->content_type;
@@ -207,7 +204,7 @@ sub index_walk {
 	if ($part_nr > 0) {
 		my $fn = $part->filename;
 		defined($fn) or $fn = "part #" . ($part_nr + 1);
-		$rv .= $pfx . add_filename_line($enc->decode($fn));
+		$rv .= add_filename_line($enc->decode($fn));
 	}
 
 	my $s = add_text_body_short($enc, $part, $part_nr, $fhref);
@@ -224,9 +221,6 @@ sub index_walk {
 	$s =~ s/\s+\z//s;
 
 	if (length $s) {
-		# add prefix:
-		$s =~ s/^/$pfx/sgm;
-
 		# kill per-line trailing whitespace
 		$s =~ s/[ \t]+$//sgm;
 
@@ -549,8 +543,7 @@ sub thread_html_head {
 	my ($mime) = @_;
 	my $s = PublicInbox::Hval->new_oneline($mime->header('Subject'));
 	$s = $s->as_html;
-	"<html><head><title>$s</title></head><body>" . PRE_WRAP
-
+	"<html><head><title>$s</title></head><body>";
 }
 
 sub thread_entry {
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 04/11] view: avoid nesting <a> tags from auto-linkification
  2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
  2015-08-20  2:57 ` [PATCH 02/11] feed: move timestamp parsing to view Eric Wong
  2015-08-20  2:57 ` [PATCH 03/11] use tables for rendering comment nesting Eric Wong
@ 2015-08-20  2:57 ` Eric Wong
  2015-08-20  2:57 ` [PATCH 05/11] index: simplify main landing page if search-enabled Eric Wong
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

It is wrong HTML to have <a> tags nested due to auto-linkification.
---
 lib/PublicInbox/View.pm | 110 +++++++++++++++++++++++++++---------------------
 t/view.t                |   2 +-
 2 files changed, 62 insertions(+), 50 deletions(-)

diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 800c1a2..2ba5118 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -207,7 +207,7 @@ sub index_walk {
 		$rv .= add_filename_line($enc->decode($fn));
 	}
 
-	my $s = add_text_body_short($enc, $part, $part_nr, $fhref);
+	my $s = add_text_body($enc, $part, $part_nr, $fhref);
 
 	# drop the remainder of git patches, they're usually better
 	# to review when the full message is viewed
@@ -266,12 +266,7 @@ sub multipart_text_as_html {
 			$rv .= add_filename_line($enc->decode($fn));
 		}
 
-		if (defined $full_pfx) {
-			$rv .= add_text_body_short($enc, $part, $part_nr,
-						$full_pfx);
-		} else {
-			$rv .= add_text_body_full($enc, $part, $part_nr);
-		}
+		$rv .= add_text_body($enc, $part, $part_nr, $full_pfx);
 		$rv .= "\n" unless $rv =~ /\n\z/s;
 		++$part_nr;
 	});
@@ -294,57 +289,74 @@ my $LINK_RE = qr!\b((?:ftp|https?|nntp)://[@\w\+\&\?\.\%\;/#=-]+)!;
 sub linkify {
 	# no newlines added here since it'd break the splitting we do
 	# to fold quotes
-	$_[0] =~ s!$LINK_RE!<a href="$1">$1</a>!g;
+	$_[0] =~ s!$LINK_RE!<a\nhref="$1">$1</a>!g;
 }
 
-sub add_text_body_short {
-	my ($enc, $part, $part_nr, $full_pfx) = @_;
-	my $n = 0;
-	my $s = ascii_html($enc->decode($part->body));
-	linkify($s);
-	$s =~ s!^((?:(?:&gt;[^\n]*)\n)+)!
-		my $cur = $1;
-		my @lines = split(/\n/, $cur);
-		if (@lines > MAX_INLINE_QUOTED) {
-			# show a short snippet of quoted text
-			$cur = join(' ', @lines);
-			$cur =~ s/^&gt;\s*//;
-
-			my @sum = split(/\s+/, $cur);
-			$cur = '';
-			do {
-				my $tmp = shift(@sum);
-				my $len = length($tmp) + length($cur);
-				if ($len > MAX_TRUNC_LEN) {
-					@sum = ();
-				} else {
-					$cur .= $tmp . ' ';
-				}
-			} while (@sum && length($cur) < MAX_TRUNC_LEN);
-			$cur =~ s/ \z/ .../;
-			"&gt; &lt;<a\nhref=\"${full_pfx}#q${part_nr}_" . $n++ .
-				"\">$cur<\/a>&gt;\n";
-		} else {
-			$cur;
+sub flush_quote {
+	my ($quot, $n, $part_nr, $full_pfx, $final) = @_;
+
+	if ($full_pfx) {
+		if (!$final && scalar(@$quot) <= MAX_INLINE_QUOTED) {
+			# show quote inline
+			my $rv = join("\n", map { linkify($_); $_ } @$quot);
+			@$quot = ();
+			return $rv . "\n";
 		}
-	!emg;
-	$s;
+
+		# show a short snippet of quoted text and link to full version:
+		@$quot = map { s/^(?:&gt;\s*)+//gm; $_ } @$quot;
+		my $cur = join(' ', @$quot);
+		@$quot = split(/\s+/, $cur);
+		$cur = '';
+		do {
+			my $tmp = shift(@$quot);
+			my $len = length($tmp) + length($cur);
+			if ($len > MAX_TRUNC_LEN) {
+				@$quot = ();
+			} else {
+				$cur .= $tmp . ' ';
+			}
+		} while (@$quot && length($cur) < MAX_TRUNC_LEN);
+		@$quot = ();
+		$cur =~ s/ \z/ .../s;
+		my $nr = ++$$n;
+		"&gt; [<a\nhref=\"$full_pfx#q${part_nr}_$nr\">$cur</a>]\n";
+	} else {
+		# show everything in the full version with anchor from
+		# short version (see above)
+		my $nr = ++$$n;
+		my $rv = "<a\nid=q${part_nr}_$nr></a>";
+		$rv .= join("\n", map { linkify($_); $_ } @$quot) . "\n";
+		@$quot = ();
+		$rv;
+	}
 }
 
-sub add_text_body_full {
-	my ($enc, $part, $part_nr) = @_;
+sub add_text_body {
+	my ($enc, $part, $part_nr, $full_pfx) = @_;
 	my $n = 0;
 	my $s = ascii_html($enc->decode($part->body));
-	linkify($s);
-	$s =~ s!^((?:(?:&gt;[^\n]*)\n)+)!
-		my $cur = $1;
-		my @lines = split(/\n/, $cur);
-		if (@lines > MAX_INLINE_QUOTED) {
-			"<a\nid=q${part_nr}_" . $n++ . ">$cur</a>";
+	my @lines = split(/\n/, $s);
+	$s = '';
+	my $nr = 0;
+	my @quot;
+	while (defined(my $cur = shift @lines)) {
+		if ($cur !~ /^&gt;/) {
+			# show the previously buffered quote inline
+			if (scalar @quot) {
+				$s .= flush_quote(\@quot, \$n, $part_nr,
+						  $full_pfx, 0);
+			}
+
+			# regular line, OK
+			linkify($cur);
+			$s .= $cur;
+			$s .= "\n";
 		} else {
-			$cur;
+			push @quot, $cur;
 		}
-	!emg;
+	}
+	$s .= flush_quote(\@quot, \$n, $part_nr, $full_pfx, 1) if scalar @quot;
 	$s;
 }
 
diff --git a/t/view.t b/t/view.t
index 463fc07..ca456aa 100644
--- a/t/view.t
+++ b/t/view.t
@@ -47,7 +47,7 @@ EOF
 	like($html, qr!<a\nhref="\.\./m/hello%40!s, "MID link present");
 	like($html, qr/hello world\b/, "body present");
 	like($html, qr/&gt; keep this inline/, "short quoted text is inline");
-	like($html, qr/<a\nid=[^>]+>&gt; Long and wordy/,
+	like($html, qr/<a\nid=[^>]+><\/a>&gt; Long and wordy/,
 		"long quoted text is anchored");
 
 	# short page
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 05/11] index: simplify main landing page if search-enabled
  2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
                   ` (2 preceding siblings ...)
  2015-08-20  2:57 ` [PATCH 04/11] view: avoid nesting <a> tags from auto-linkification Eric Wong
@ 2015-08-20  2:57 ` Eric Wong
  2015-08-20  2:57 ` [PATCH 06/11] search: avoid needless decode Eric Wong
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

We can display /t/$MESSAGE_ID.html easily with a Xapian search
index, so rely on it instead of trying to display messages inline.
---
 lib/PublicInbox/Feed.pm   | 103 +++++++++++++++++++++++++++++++++++++++-------
 lib/PublicInbox/Search.pm |  15 +++++--
 2 files changed, 98 insertions(+), 20 deletions(-)

diff --git a/lib/PublicInbox/Feed.pm b/lib/PublicInbox/Feed.pm
index 8bfd19e..40dfb45 100644
--- a/lib/PublicInbox/Feed.pm
+++ b/lib/PublicInbox/Feed.pm
@@ -9,11 +9,15 @@ use Date::Parse qw(strptime);
 use PublicInbox::Hval;
 use PublicInbox::GitCatFile;
 use PublicInbox::View;
+use PublicInbox::MID qw/mid_clean mid_compressed/;
 use constant {
 	DATEFMT => '%Y-%m-%dT%H:%M:%SZ', # atom standard
 	MAX_PER_PAGE => 25, # this needs to be tunable
 };
 
+use Encode qw/find_encoding/;
+my $enc_utf8 = find_encoding('UTF-8');
+
 # main function
 sub generate {
 	my ($class, $ctx) = @_;
@@ -55,22 +59,30 @@ sub generate_html_index {
 
 	my $title = $feed_opts->{description} || '';
 	$title = PublicInbox::Hval->new_oneline($title)->as_html;
+	my $atom_url = $feed_opts->{atomurl};
 
 	my $html = "<html><head><title>$title</title>" .
-		'<link rel="alternate" title="Atom feed"' . "\nhref=\"" .
-		$feed_opts->{atomurl} . "\"\ntype=\"application/atom+xml\"/>" .
-		'</head><body>';
+		"<link\nrel=alternate\ntitle=\"Atom feed\"\n".
+		"href=\"$atom_url\"\"\ntype=\"application/atom+xml\"/>" .
+		'</head><body>' . PublicInbox::View::PRE_WRAP;
 
 	my $state;
 	my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+	my $topics;
+	my $srch = $ctx->{srch};
+	$srch and $topics = [ [], {} ];
 	my (undef, $last) = each_recent_blob($ctx, sub {
-		my ($path, $commit) = @_;
-		unless (defined $state) {
-			$state = [ $ctx->{srch}, {}, $commit, 0 ];
+		my ($path, $commit, $ts, $u, $subj) = @_;
+		$state ||= [ undef, {}, $commit, 0 ];
+
+		if ($srch) {
+			add_topic($git, $srch, $topics, $path, $ts, $u, $subj);
+		} else {
+			my $mime = do_cat_mail($git, $path) or return 0;
+			$html .=
+			     PublicInbox::View->index_entry($mime, 0, $state);
+			1;
 		}
-		my $mime = do_cat_mail($git, $_[0]) or return 0;
-		$html .= PublicInbox::View->index_entry($mime, 0, $state);
-		1;
 	});
 	Email::Address->purge_cache;
 	$git = undef; # destroy pipes.
@@ -81,6 +93,7 @@ sub generate_html_index {
 		$footer .= "\n" . $list_footer if $list_footer;
 		$footer = "<hr /><pre>$footer</pre>";
 	}
+	dump_topics(\$html, $topics) if $topics;
 	$html .= "$footer</body></html>";
 }
 
@@ -92,6 +105,7 @@ sub nav_footer {
 	my $old_r = $cgi->param('r');
 	my $head = '    ';
 	my $next = '    ';
+	# $state = [ undef, {}, $first_commit, $last_anchor ];
 	my $first = $state->[2];
 	my $anchor = $state->[3];
 
@@ -128,7 +142,8 @@ sub each_recent_blob {
 	# leave us with filenames with spaces in them..
 	my @cmd = ('git', "--git-dir=$ctx->{git_dir}",
 			qw/log --no-notes --no-color --raw -r
-			   --abbrev=16 --abbrev-commit/);
+			   --abbrev=16 --abbrev-commit/,
+			"--format=%h%x00%ct%x00%an%x00%s%x00");
 	push @cmd, $range;
 
 	my $pid = open(my $log, '-|', @cmd) or
@@ -137,26 +152,29 @@ sub each_recent_blob {
 	my $last;
 	my $nr = 0;
 	my ($cur_commit, $first_commit, $last_commit);
-	while (my $line = <$log>) {
+	my ($ts, $subj, $u);
+	while (defined(my $line = <$log>)) {
 		if ($line =~ /$addmsg/o) {
 			my $add = $1;
 			next if $deleted{$add}; # optimization-only
-			$nr += $cb->($add, $cur_commit);
+			$nr += $cb->($add, $cur_commit, $ts, $u, $subj);
 			if ($nr >= $max) {
 				$last = 1;
 				last;
 			}
 		} elsif ($line =~ /$delmsg/o) {
 			$deleted{$1} = 1;
-		} elsif ($line =~ /^commit (${hex}{7,40})/o) {
-			$cur_commit = $1;
-			$first_commit = $1 unless defined $first_commit;
+		} elsif ($line =~ /^${hex}{7,40}/o) {
+			($cur_commit, $ts, $u, $subj) = split("\0", $line);
+			unless (defined $first_commit) {
+				$first_commit = $cur_commit;
+			}
 		}
 	}
 
 	if ($last) {
 		while (my $line = <$log>) {
-			if ($line =~ /^commit (${hex}{7,40})/o) {
+			if ($line =~ /^(${hex}{7,40})/o) {
 				$last_commit = $1;
 				last;
 			}
@@ -279,4 +297,57 @@ sub do_cat_mail {
 	$@ ? undef : $mime;
 }
 
+# accumulate recent topics if search is supported
+sub add_topic {
+	my ($git, $srch, $topics, $path, $ts, $u, $subj) = @_;
+	my ($order, $subjs) = @$topics;
+	my $header_obj;
+
+	# legacy ssoma did not set commit titles based on Subject
+	$subj = $enc_utf8->decode($subj);
+	if ($subj eq 'mda') {
+		my $mime = do_cat_mail($git, $path) or return 0;
+		$header_obj = $mime->header_obj;
+		$subj = mime_header($header_obj, 'Subject');
+	}
+
+	$subj = $srch->subject_normalized($subj);
+	if (++$subjs->{$subj} == 1) {
+		unless ($header_obj) {
+			my $mime = do_cat_mail($git, $path) or return 0;
+			$header_obj = $mime->header_obj;
+		}
+		my $mid = $header_obj->header_raw('Message-ID');
+		$mid = mid_compressed(mid_clean($mid));
+		$u = $enc_utf8->decode($u);
+		push @$order, [ $mid, $ts, $u, $subj ];
+		return 1;
+	}
+	0; # old topic, continue going
+}
+
+sub dump_topics {
+	my ($dst, $topics) = @_;
+	my ($order, $subjs) = @$topics;
+	$$dst .= '[No recent topics]' unless (scalar @$order);
+	while (defined(my $info = shift @$order)) {
+		my ($mid, $ts, $u, $subj) = @$info;
+		my $n = delete $subjs->{$subj};
+		$mid = PublicInbox::Hval->new($mid)->as_href;
+		$subj = PublicInbox::Hval->new($subj)->as_html;
+		$u = PublicInbox::Hval->new($u)->as_html;
+		$$dst .= "<a\nhref=\"t/$mid.html#u\"><b>$subj</b></a>\n- ";
+		$ts = POSIX::strftime('%Y-%m-%d %H:%M', gmtime($ts));
+		if ($n == 1) {
+			$$dst .= "created by $u @ $ts UTC\n"
+		} else {
+			# $n isn't the total number of posts on the topic,
+			# just the number of posts in the current "git log"
+			# window, so leave it unlabeled
+			$$dst .= "updated by $u @ $ts UTC ($n)\n"
+		}
+	}
+	$$dst .= '</pre>'
+}
+
 1;
diff --git a/lib/PublicInbox/Search.pm b/lib/PublicInbox/Search.pm
index b9f283f..c28401b 100644
--- a/lib/PublicInbox/Search.pm
+++ b/lib/PublicInbox/Search.pm
@@ -492,14 +492,21 @@ sub merge_threads {
 # normalize subjects so they are suitable as pathnames for URLs
 sub subject_path {
 	my $subj = pop;
-
-	$subj =~ s/\A\s+//;
-	$subj =~ s/\s+\z//;
-	$subj =~ s/$REPLY_RE//igo; # remove reply prefix
+	$subj = subject_normalized($subj);
 	$subj =~ s![^a-zA-Z0-9_\.~/\-]+!_!g;
 	lc($subj);
 }
 
+sub subject_normalized {
+	my $subj = pop;
+	$subj =~ s/\A\s+//s; # no leading space
+	$subj =~ s/\s+\z//s; # no trailing space
+	$subj =~ s/\s+/ /gs; # no redundant spaces
+	$subj =~ s/\.+\z//; # no trailing '.'
+	$subj =~ s/$REPLY_RE//igo; # remove reply prefix
+	$subj;
+}
+
 sub do_cat_mail {
 	my ($git, $blob) = @_;
 	my $mime = eval {
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 06/11] search: avoid needless decode
  2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
                   ` (3 preceding siblings ...)
  2015-08-20  2:57 ` [PATCH 05/11] index: simplify main landing page if search-enabled Eric Wong
@ 2015-08-20  2:57 ` Eric Wong
  2015-08-20  2:57 ` [PATCH 07/11] search: reject ghosts in all cases Eric Wong
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

Email::MIME should handle everything for us and make things
work nicely with Xapian (assuming I understand how encoding
works in Perl).

While we're at it, reduce temporary strings and arrays by
using destructive operations and clobbering parts as we
iterate through them.
---
 lib/PublicInbox/Search.pm | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/lib/PublicInbox/Search.pm b/lib/PublicInbox/Search.pm
index c28401b..aa29ae5 100644
--- a/lib/PublicInbox/Search.pm
+++ b/lib/PublicInbox/Search.pm
@@ -6,7 +6,6 @@ use strict;
 use warnings;
 use PublicInbox::SearchMsg;
 use Search::Xapian qw/:standard/;
-require PublicInbox::View;
 use Email::MIME;
 use PublicInbox::MID qw/mid_clean mid_compressed/;
 
@@ -88,7 +87,6 @@ sub add_message {
 	my $mid = mid_compressed($mid_orig);
 	my $was_ghost = 0;
 	my $ct_msg = $mime->header('Content-Type') || 'text/plain';
-	my $enc_msg = PublicInbox::View::enc_for($ct_msg);
 
 	eval {
 		my $smsg = $self->lookup_message($mid);
@@ -151,9 +149,11 @@ sub add_message {
 			# account for filter bugs...
 			$ct =~ m!\btext/plain\b!i or return;
 
-			my $enc = PublicInbox::View::enc_for($ct, $enc_msg);
 			my (@orig, @quot);
-			foreach my $l (split(/\n/, $enc->decode($part->body))) {
+			my $body = $part->body;
+			$part->body_set('');
+			my @lines = split(/\n/, $body);
+			while (defined(my $l = shift @lines)) {
 				if ($l =~ /^\s*>/) {
 					push @quot, $l;
 				} else {
@@ -162,10 +162,12 @@ sub add_message {
 			}
 			if (@quot) {
 				$tg->index_text(join("\n", @quot), 0);
+				@quot = ();
 				$tg->increase_termpos;
 			}
 			if (@orig) {
 				$tg->index_text(join("\n", @orig));
+				@orig = ();
 				$tg->increase_termpos;
 			}
 		});
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 07/11] search: reject ghosts in all cases
  2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
                   ` (4 preceding siblings ...)
  2015-08-20  2:57 ` [PATCH 06/11] search: avoid needless decode Eric Wong
@ 2015-08-20  2:57 ` Eric Wong
  2015-08-20  2:57 ` [PATCH 08/11] view: reduce memory usage when displaying large threads Eric Wong
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

We do not need ghost messages in any of our thread views
---
 lib/PublicInbox/Search.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/PublicInbox/Search.pm b/lib/PublicInbox/Search.pm
index aa29ae5..0e63ee3 100644
--- a/lib/PublicInbox/Search.pm
+++ b/lib/PublicInbox/Search.pm
@@ -214,7 +214,6 @@ sub query {
 	my ($self, $query_string, $opts) = @_;
 	my $query = $self->qp->parse_query($query_string, QP_FLAGS);
 
-	$query = Search::Xapian::Query->new(OP_AND, $mail_query, $query);
 	$self->do_enquire($query, $opts);
 }
 
@@ -255,6 +254,7 @@ sub do_enquire {
 	my ($self, $query, $opts) = @_;
 	my $enquire = $self->enquire;
 
+	$query = Search::Xapian::Query->new(OP_AND, $query, $mail_query);
 	$enquire->set_query($query);
 	$enquire->set_sort_by_relevance_then_value(TS, 0);
 	$opts ||= {};
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 08/11] view: reduce memory usage when displaying large threads
  2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
                   ` (5 preceding siblings ...)
  2015-08-20  2:57 ` [PATCH 07/11] search: reject ghosts in all cases Eric Wong
@ 2015-08-20  2:57 ` Eric Wong
  2015-08-20  2:57 ` [PATCH 09/11] search: bump schema version to 5 for subject_path Eric Wong
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

We want to minimize the time any large objects or strings
are referenced.  We can do threading entirely from the
mini_mime-generated messages and lazilly load full messages
when rendering the display.
---
 lib/PublicInbox/View.pm | 80 +++++++++++++++++++++++++++----------------------
 1 file changed, 44 insertions(+), 36 deletions(-)

diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 2ba5118..391e3ad 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -146,7 +146,7 @@ sub thread_html {
 	my $mid = mid_compressed($ctx->{mid});
 	my $res = $srch->get_thread($mid);
 	my $rv = '';
-	my $msgs = load_results($ctx, $res);
+	my $msgs = load_results($res);
 	my $nr = scalar @$msgs;
 	return $rv if $nr == 0;
 	require PublicInbox::Thread;
@@ -154,7 +154,11 @@ sub thread_html {
 	$th->thread;
 	$th->order(*PublicInbox::Thread::sort_ts);
 	my $state = [ $srch, { root_anchor => anchor_for($mid) }, undef, 0 ];
-	thread_entry(\$rv, $state, $_, 0) for $th->rootset;
+	{
+		require PublicInbox::GitCatFile;
+		my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+		thread_entry(\$rv, $git, $state, $_, 0) for $th->rootset;
+	}
 	my $final_anchor = $state->[3];
 	my $next = "<a\nid=\"s$final_anchor\">";
 
@@ -173,7 +177,7 @@ sub subject_path_html {
 	my $path = $ctx->{subject_path};
 	my $res = $srch->get_subject_path($path);
 	my $rv = '';
-	my $msgs = load_results($ctx, $res);
+	my $msgs = load_results($res);
 	my $nr = scalar @$msgs;
 	return $rv if $nr == 0;
 	require PublicInbox::Thread;
@@ -181,7 +185,11 @@ sub subject_path_html {
 	$th->thread;
 	$th->order(*PublicInbox::Thread::sort_ts);
 	my $state = [ $srch, { root_anchor => 'dummy' }, undef, 0 ];
-	thread_entry(\$rv, $state, $_, 0) for $th->rootset;
+	{
+		require PublicInbox::GitCatFile;
+		my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+		thread_entry(\$rv, $git, $state, $_, 0) for $th->rootset;
+	}
 	my $final_anchor = $state->[3];
 	my $next = "<a\nid=\"s$final_anchor\">end of thread</a>\n";
 
@@ -197,7 +205,10 @@ sub index_walk {
 	my $ct = $part->content_type;
 
 	# account for filter bugs...
-	return '' if defined $ct && $ct =~ m!\btext/[xh]+tml\b!i;
+	if (defined $ct && $ct =~ m!\btext/[xh]+tml\b!i) {
+		$part->body_set('');
+		return '';
+	}
 
 	my $enc = enc_for($ct, $enc_msg);
 
@@ -224,7 +235,9 @@ sub index_walk {
 		# kill per-line trailing whitespace
 		$s =~ s/[ \t]+$//sgm;
 
-		$rv .= $s . "\n";
+		$rv .= $s;
+		$s = undef;
+		$rv .= "\n";
 	}
 	$rv;
 }
@@ -335,10 +348,13 @@ sub flush_quote {
 sub add_text_body {
 	my ($enc, $part, $part_nr, $full_pfx) = @_;
 	my $n = 0;
-	my $s = ascii_html($enc->decode($part->body));
+	my $nr = 0;
+	my $s = $part->body;
+	$part->body_set('');
+	$s = $enc->decode($s);
+	$s = ascii_html($s);
 	my @lines = split(/\n/, $s);
 	$s = '';
-	my $nr = 0;
 	my @quot;
 	while (defined(my $cur = shift @lines)) {
 		if ($cur !~ /^&gt;/) {
@@ -538,10 +554,10 @@ sub simple_dump {
 
 sub thread_followups {
 	my ($dst, $root, $res) = @_;
-	my @msgs = map { $_->mini_mime } @{$res->{msgs}};
+	my $msgs = load_results($res);
 	require PublicInbox::Thread;
 	$root->header_set('X-PI-TS', '0');
-	my $th = PublicInbox::Thread->new($root, @msgs);
+	my $th = PublicInbox::Thread->new($root, @$msgs);
 	$th->thread;
 	$th->order(*PublicInbox::Thread::sort_ts);
 	my $srch = $res->{srch};
@@ -559,43 +575,35 @@ sub thread_html_head {
 }
 
 sub thread_entry {
-	my ($dst, $state, $node, $level) = @_;
+	my ($dst, $git, $state, $node, $level) = @_;
 	# $state = [ $search_res, $seen, undef, 0 (msg_nr) ];
 	# $seen is overloaded with 3 types of fields:
 	#	1) "root_anchor" => anchor_for(Message-ID),
 	#	2) seen subject hashes: sha1(subject) => 1
 	#	3) anchors hashes: "#$sha1_hex" (same as $seen in index_entry)
 	if (my $mime = $node->message) {
-		if (length($$dst) == 0) {
-			$$dst .= thread_html_head($mime);
+
+		# lazy load the full message from mini_mime:
+		my $path = mid2path(mid_clean($mime->header('Message-ID')));
+		$mime = eval { Email::MIME->new($git->cat_file("HEAD:$path")) };
+		if ($mime) {
+			if (length($$dst) == 0) {
+				$$dst .= thread_html_head($mime);
+			}
+			$$dst .= index_entry(undef, $mime, $level, $state);
 		}
-		$$dst .= index_entry(undef, $mime, $level, $state);
 	}
-	thread_entry($dst, $state, $node->child, $level + 1) if $node->child;
-	thread_entry($dst, $state, $node->next, $level) if $node->next;
+	my $cur;
+	$cur = $node->child and
+		thread_entry($dst, $git, $state, $cur, $level + 1);
+	$cur = $node->next and
+		thread_entry($dst, $git, $state, $cur, $level);
 }
 
 sub load_results {
-	my ($ctx, $res) = @_;
-
-	require PublicInbox::GitCatFile;
-	my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
-	my @msgs;
-	while (my $smsg = shift @{$res->{msgs}}) {
-		my $m = $smsg->mid;
-		my $path = mid2path($m);
-
-		# FIXME: duplicated code from Feed.pm
-		my $mime = eval {
-			my $str = $git->cat_file("HEAD:$path");
-			Email::MIME->new($str);
-		};
-		unless ($@) {
-			$mime->header_set('X-PI-TS', msg_timestamp($mime));
-			push @msgs, $mime;
-		}
-	}
-	\@msgs;
+	my ($res) = @_;
+
+	[ map { $_->mini_mime } @{delete $res->{msgs}} ];
 }
 
 sub msg_timestamp {
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 09/11] search: bump schema version to 5 for subject_path
  2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
                   ` (6 preceding siblings ...)
  2015-08-20  2:57 ` [PATCH 08/11] view: reduce memory usage when displaying large threads Eric Wong
@ 2015-08-20  2:57 ` Eric Wong
  2015-08-20  2:57 ` [PATCH 10/11] index: layout fix + title and Atom feed links at top Eric Wong
  2015-08-20  2:57 ` [PATCH 11/11] view: do not fold top-level messages in thread Eric Wong
  9 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

In "index: simplify main landing page if search-enabled",
subject normalization went a little farther to drop trailing
'.' characters, so we will need to re-index.
---
 lib/PublicInbox/Search.pm | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/PublicInbox/Search.pm b/lib/PublicInbox/Search.pm
index 0e63ee3..abd9db4 100644
--- a/lib/PublicInbox/Search.pm
+++ b/lib/PublicInbox/Search.pm
@@ -22,7 +22,8 @@ use constant {
 	# 2 - subject_path is mid_compressed in the index, only
 	# 3 - message-ID is compressed if it includes '%' (hack!)
 	# 4 - change "Re: " normalization, avoid circular Reference ghosts
-	SCHEMA_VERSION => 4,
+	# 5 - subject_path drops trailing '.'
+	SCHEMA_VERSION => 5,
 	QP_FLAGS => FLAG_PHRASE|FLAG_BOOLEAN|FLAG_LOVEHATE|FLAG_WILDCARD,
 };
 
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 10/11] index: layout fix + title and Atom feed links at top
  2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
                   ` (7 preceding siblings ...)
  2015-08-20  2:57 ` [PATCH 09/11] search: bump schema version to 5 for subject_path Eric Wong
@ 2015-08-20  2:57 ` Eric Wong
  2015-08-20  2:57 ` [PATCH 11/11] view: do not fold top-level messages in thread Eric Wong
  9 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

Add some spacing between topics to improve readability when
scanning or in case a subject gets too long.

The title and Atom feed may not be highly-visible otherwise.
While we're at it, use the proper "Atom feed" terminology since
some folks may not understand just what "atom" means.
---
 lib/PublicInbox/Feed.pm | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/lib/PublicInbox/Feed.pm b/lib/PublicInbox/Feed.pm
index 40dfb45..2e352cb 100644
--- a/lib/PublicInbox/Feed.pm
+++ b/lib/PublicInbox/Feed.pm
@@ -64,7 +64,8 @@ sub generate_html_index {
 	my $html = "<html><head><title>$title</title>" .
 		"<link\nrel=alternate\ntitle=\"Atom feed\"\n".
 		"href=\"$atom_url\"\"\ntype=\"application/atom+xml\"/>" .
-		'</head><body>' . PublicInbox::View::PRE_WRAP;
+		'</head><body>' . PublicInbox::View::PRE_WRAP .
+		"<b>$title</b> (<a\nhref=\"$atom_url\">Atom feed</a>)\n";
 
 	my $state;
 	my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
@@ -329,14 +330,14 @@ sub add_topic {
 sub dump_topics {
 	my ($dst, $topics) = @_;
 	my ($order, $subjs) = @$topics;
-	$$dst .= '[No recent topics]' unless (scalar @$order);
+	$$dst .= "\n[No recent topics]" unless (scalar @$order);
 	while (defined(my $info = shift @$order)) {
 		my ($mid, $ts, $u, $subj) = @$info;
 		my $n = delete $subjs->{$subj};
 		$mid = PublicInbox::Hval->new($mid)->as_href;
 		$subj = PublicInbox::Hval->new($subj)->as_html;
 		$u = PublicInbox::Hval->new($u)->as_html;
-		$$dst .= "<a\nhref=\"t/$mid.html#u\"><b>$subj</b></a>\n- ";
+		$$dst .= "\n<a\nhref=\"t/$mid.html#u\"><b>$subj</b></a>\n- ";
 		$ts = POSIX::strftime('%Y-%m-%d %H:%M', gmtime($ts));
 		if ($n == 1) {
 			$$dst .= "created by $u @ $ts UTC\n"
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 11/11] view: do not fold top-level messages in thread
  2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
                   ` (8 preceding siblings ...)
  2015-08-20  2:57 ` [PATCH 10/11] index: layout fix + title and Atom feed links at top Eric Wong
@ 2015-08-20  2:57 ` Eric Wong
  9 siblings, 0 replies; 11+ messages in thread
From: Eric Wong @ 2015-08-20  2:57 UTC (permalink / raw)
  To: meta

This hopefully reduces clicking.  We may drop folding entirely
since we can use Xapian to make searching easier.
---
 lib/PublicInbox/View.pm | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 391e3ad..17af21f 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -109,11 +109,15 @@ sub index_entry {
 	}
 	$rv .= "\n\n";
 
+	my ($fhref, $more_ref);
 	my $mhref = "${path}m/$href.html";
-	my $fhref = "${path}f/$href.html";
+	if ($level > 0) {
+		$fhref = "${path}f/$href.html";
+		$more_ref = \$more;
+	}
 	# scan through all parts, looking for displayable text
 	$mime->walk_parts(sub {
-		$rv .= index_walk($_[0], $enc_msg, $part_nr, $fhref, \$more);
+		$rv .= index_walk($_[0], $enc_msg, $part_nr, $fhref, $more_ref);
 		$part_nr++;
 	});
 	$mime->body_set('');
@@ -220,12 +224,14 @@ sub index_walk {
 
 	my $s = add_text_body($enc, $part, $part_nr, $fhref);
 
-	# drop the remainder of git patches, they're usually better
-	# to review when the full message is viewed
-	$s =~ s!^---+\n.*\z!!ms and $$more = 'more...';
+	if ($more) {
+		# drop the remainder of git patches, they're usually better
+		# to review when the full message is viewed
+		$s =~ s!^---+\n.*\z!!ms and $$more = 'more...';
 
-	# Drop signatures
-	$s =~ s/^-- \n.*\z//ms and $$more = 'more...';
+		# Drop signatures
+		$s =~ s/^-- \n.*\z//ms and $$more = 'more...';
+	}
 
 	# kill any leading or trailing whitespace lines
 	$s =~ s/^\s*$//sgm;
-- 
EW


^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2015-08-20  2:57 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-20  2:57 [PATCH 01/11] feed: remove threading from index Eric Wong
2015-08-20  2:57 ` [PATCH 02/11] feed: move timestamp parsing to view Eric Wong
2015-08-20  2:57 ` [PATCH 03/11] use tables for rendering comment nesting Eric Wong
2015-08-20  2:57 ` [PATCH 04/11] view: avoid nesting <a> tags from auto-linkification Eric Wong
2015-08-20  2:57 ` [PATCH 05/11] index: simplify main landing page if search-enabled Eric Wong
2015-08-20  2:57 ` [PATCH 06/11] search: avoid needless decode Eric Wong
2015-08-20  2:57 ` [PATCH 07/11] search: reject ghosts in all cases Eric Wong
2015-08-20  2:57 ` [PATCH 08/11] view: reduce memory usage when displaying large threads Eric Wong
2015-08-20  2:57 ` [PATCH 09/11] search: bump schema version to 5 for subject_path Eric Wong
2015-08-20  2:57 ` [PATCH 10/11] index: layout fix + title and Atom feed links at top Eric Wong
2015-08-20  2:57 ` [PATCH 11/11] view: do not fold top-level messages in thread Eric Wong

Code repositories for project(s) associated with this public inbox

	https://80x24.org/public-inbox.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).