aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArne Georg Gleditsch <argggh@lxr.linpro.no>2008-02-12 22:53:01 +0100
committerArne Georg Gleditsch <argggh@lxr.linpro.no>2008-02-12 22:53:01 +0100
commitcd93fa83ba632588b357b190f47809205b9a5d91 (patch)
treea597c4c6fb8e07584fd81553b84a543eebb8a86f
parent31ad80f533166802520e018d0511c2f18b15db71 (diff)
Change line number layout and caching mechanism.
-rw-r--r--lib/LXRng/Markup/File.pm6
-rw-r--r--lib/LXRng/Web.pm140
-rw-r--r--webroot/.static/css/lxrng.css34
-rw-r--r--webroot/.static/js/lxrng-funcs.js79
4 files changed, 164 insertions, 95 deletions
diff --git a/lib/LXRng/Markup/File.pm b/lib/LXRng/Markup/File.pm
index 9bd811c..d94952c 100644
--- a/lib/LXRng/Markup/File.pm
+++ b/lib/LXRng/Markup/File.pm
@@ -49,9 +49,9 @@ sub make_format_newline {
$line++;
$nl = safe_html($nl);
- return qq{</span>$nl<li>}.
- qq{<a href="$name#L$line" class="line"><span></span></a>}.
- qq{<a id="L$line" name="L$line"></a><span class="line">};
+ return qq{$nl}.
+ qq{<a href="$name#L$line" class="line">$line</a>}.
+ qq{<a id="L$line" name="L$line"></a>};
}
}
diff --git a/lib/LXRng/Web.pm b/lib/LXRng/Web.pm
index 4334981..d7f9be4 100644
--- a/lib/LXRng/Web.pm
+++ b/lib/LXRng/Web.pm
@@ -34,13 +34,17 @@ use IO::Handle;
use Digest::SHA1 qw(sha1_hex);
use CGI::Ajax;
use File::Temp qw(tempdir tempfile);
+use File::Path qw(mkpath);
use POSIX qw(waitpid);
use constant PDF_LINELEN => 95;
use constant PDF_CHARPTS => 6.6;
+# Cache must be purged if this is changed.
+use constant FRAGMENT_SIZE => 250;
+
use vars qw($has_gzip_io);
-# eval { require PerlIO::gzip; $has_gzip_io = 1; };
+eval { require PerlIO::gzip; $has_gzip_io = 1; };
# Return 1 if gzip compression of html is desired.
@@ -77,39 +81,49 @@ sub print_markedup_file {
return;
}
else {
- # Grmble. We assume the identifiers to markup are identical
- # from one version to another, but if the same revision of a
- # file exists both in an indexed and un-indexed release, one
- # of them will have its identifiers highlighted and the other
- # not. So we can't share a cache slot across releases without
- # adding some extra logic here. Bummer.
- # TODO: Resolve by caching only accesses to releases that are
- # is_indexed.
+ my $line = 0;
+ my $focus = 1;
+ my $fline = $context->param('line');
+
+ $focus = $fline < 100 if defined($fline);
+
my $shaid = sha1_hex(join("\0", $node->name, $node->revision,
$context->release));
my $cfile;
+ $shaid =~ s,^(..)(..),$1/$2/,;
$cfile = $context->config->{'cache'}.'/'.$shaid
if exists $context->config->{'cache'};
- if ($cfile and -e $cfile) {
- open(my $cache, '<', $cfile);
-
- my $focus = $context->param('line') || 0;
- $focus = 0 if $context->param('full');
- my $class = $focus ? 'partial' : 'full';
- my $start = $focus > 5 ? " start=".($focus - 5) : "";
- print("<pre id=\"file_contents\" class=\"$class\"><ol$start><span>");
- while (<$cache>) {
- next if $focus and $. < $focus - 5;
- print($_);
- last if $focus and $. > $focus + 70;
+ if ($cfile and -d $cfile) {
+ print("<pre id=\"file_contents\">");
+ while (-r "$cfile/$line") {
+ print("<div class=\"".($focus ? "done" : "pending").
+ "\" id=\"$shaid/$line\">");
+ if ($focus) {
+ open(my $cache, '<', "$cfile/$line");
+ my $buf;
+ while (read($cache, $buf, 16384) > 0) {
+ print($buf);
+ }
+ close($cache);
+ }
+ else {
+ print("\n" x FRAGMENT_SIZE);
+ }
+ print("</div>");
+ $line += FRAGMENT_SIZE;
+
+ if (defined($fline)) {
+ $focus = ($line <= ($fline + 100)
+ and $line > ($fline - FRAGMENT_SIZE));
+ }
}
- print("</span></ol></pre>");
- close($cache);
+ print("</pre>\n");
}
else {
my $cache;
- open($cache, '>', $cfile) if $cfile;
+ mkpath($cfile, 0, 0777);
+ open($cache, '>', "$cfile/0") if $cfile;
my $handle = $node->handle();
LXRng::Lang->init($context);
my $lang = LXRng::Lang->new($node);
@@ -117,21 +131,39 @@ sub print_markedup_file {
@{$lang->parsespec});
my $markup = LXRng::Markup::File->new('context' => $context);
my $subst = $lang->markuphandlers($context, $node, $markup);
-
- # Possible optimization: store cached file also as .gz,
- # and pass that on if the client accepts gzip-encoded
- # data. Saves us from compressing the cached file each
- # time it's needed, but requires a bit of fiddling with
- # perlio and the streams to get right. Also messes up
- # partial transfers.
- print("<pre id=\"file_contents\" class=\"full\"><ol><span>");
+
+ print("<pre id=\"file_contents\">".
+ "<div class=\"".($focus ? "done" : "pending").
+ "\" id=\"$shaid/0\">");
while (1) {
- my @frags = $markup->markupfile($subst, $parse);
+ my @frags = map { split(/(?<=\n)/, $_) }
+ $markup->markupfile($subst, $parse);
last unless @frags;
- print(@frags);
- print($cache @frags) if $cache;
+ foreach my $f (@frags) {
+ print($f) if $focus;
+ print($cache $f) if $cache;
+ if ($f =~ /\n$/s) {
+ $line++;
+ if ($line % FRAGMENT_SIZE == 0) {
+ print("\n" x FRAGMENT_SIZE) unless $focus;
+ if (defined($fline)) {
+ $focus = ($line <= ($fline + 100)
+ and $line > ($fline - FRAGMENT_SIZE));
+ }
+ print("</div>".
+ "<div class=\"".
+ ($focus ? "done" : "pending").
+ "\" id=\"$shaid/$line\">");
+ if ($cache) {
+ close($cache);
+ open($cache, '>', "$cfile/$line");
+ }
+ }
+ }
+ }
}
- print("</span></ol></pre>\n");
+ print("</div></pre>\n");
+ close($cache) if $cache;
}
return $shaid;
}
@@ -161,6 +193,7 @@ sub source {
my $pjx = CGI::Ajax->new('pjx_search' => '',
'pjx_load_file' => '',
+ 'pjx_load_fragment' => '',
'pjx_releases' => '');
$pjx->js_encode_function('escape');
@@ -181,10 +214,17 @@ sub source {
my $base = $context->base_url(1);
$base =~ s,/*$,/ajax+*/,;
+ # This is a bit fragile, but only covers a relatively
+ # esoteric corner case. (CGI::Ajax splits results on
+ # __pjx__, and there doesn't seem to be any provisions
+ # for escaping any randomly occurring split markers.)
+ my $js = $pjx->show_javascript();
+ $js =~ s/var splitval.*var data[^;]+/var data = rsp/;
+
$template->process('main.tt2',
{'context' => $context,
'base_url' => $base,
- 'javascript' => $pjx->show_javascript(),
+ 'javascript' => $js,
'is_ajax' => 1})
or die $template->error();
}
@@ -443,11 +483,13 @@ sub handle_ajax_request {
my $gzip = do_compress_response($query);
# $query->no_cache(1); FIXME -- not available with CGI.pm.
- print($query->header(-type => 'text/html',
- -charset => 'utf-8',
- -cache-control => 'no-store, no-cache, must-revalidate',
- $gzip ? (-content_encoding => 'gzip') : ()));
+ my %headers = (-type => 'text/html',
+ -charset => 'utf-8');
+ $headers{'-cache-control'} = 'no-store, no-cache, must-revalidate'
+ unless $context->param('fname') eq 'pjx_load_fragment';
+ $headers{'-content_encoding'} = 'gzip' if $gzip;
+ print($query->header(%headers));
binmode(\*STDOUT, ":gzip") if $gzip;
if ($context->param('fname') eq 'pjx_load_file') {
@@ -456,6 +498,22 @@ sub handle_ajax_request {
print_markedup_file($context, $template, $node);
}
+ elsif ($context->param('fname') eq 'pjx_load_fragment') {
+ my $shaid = $context->param('frag');
+ return unless $shaid =~
+ m|^[0-9a-z]{2}/[0-9a-z]{2}/[0-9a-z]{36}/[0-9]+$|;
+ return unless exists $context->config->{'cache'};
+ my $cfile = $context->config->{'cache'}.'/'.$shaid;
+ return unless -e $cfile;
+ open(my $cache, '<', $cfile) or return;
+
+ print($shaid.'|');
+ my $buf;
+ while (read($cache, $buf, 16384) > 0) {
+ print($buf);
+ }
+ close($cache);
+ }
elsif ($context->param('fname') eq 'pjx_search') {
if ($context->param('ajax_lookup') =~
/^[+ ](code|ident|file|text|ambig)=(.*)/)
diff --git a/webroot/.static/css/lxrng.css b/webroot/.static/css/lxrng.css
index f5d84c0..3b6ff2b 100644
--- a/webroot/.static/css/lxrng.css
+++ b/webroot/.static/css/lxrng.css
@@ -150,6 +150,10 @@ span.close-button {
content: attr(id);
} */
+pre {
+ margin-left: 3.8em;
+}
+
a.line {
position: absolute;
top: auto;
@@ -157,39 +161,13 @@ a.line {
height: 2ex;
width: 3em;
text-align: right;
+ padding-right: 3px;
border: solid;
border-width: 1px;
border-color: #000000;
margin-left: 3px;
-}
-
-a.line span {
- position: absolute;
- top: auto;
- height: 2ex;
- left: 0px;
- background: #6c6c6c;
- filter: alpha(opacity=10);
- moz-opacity: .10;
- opacity: .10;
- width: 3em;
-}
-
-
-pre#file_contents li {
- color: blue;
-}
-
-span.line {
- position: absolute;
- left: 4em;
- color: black;
- white-space: pre;
-}
-
-pre {
- margin-left: 0.7em;
+ background: #F0F0F0;
}
a img {
diff --git a/webroot/.static/js/lxrng-funcs.js b/webroot/.static/js/lxrng-funcs.js
index 0aa4237..939eefd 100644
--- a/webroot/.static/js/lxrng-funcs.js
+++ b/webroot/.static/js/lxrng-funcs.js
@@ -190,6 +190,59 @@ function load_file(tree, file, ver, line) {
return false;
}
+
+function ajaxify_link_handlers(links) {
+ var i;
+ for (i = 0; i < links.length; i++) {
+ if (links[i].className == 'fref') {
+ links[i].onclick = ajax_nav;
+ }
+ else if (links[i].className == 'line') {
+ links[i].onclick = ajax_jumpto_line;
+ }
+ else if (links[i].className == 'sref' ||
+ links[i].className == 'falt')
+ {
+ links[i].onclick = ajax_lookup_anchor;
+ }
+
+ }
+}
+
+function load_next_pending_fragment() {
+ var pre = document.getElementById('file_contents');
+ if (!pre)
+ return;
+
+ for (var i = 0; i < pre.childNodes.length; i++) {
+ if ((pre.childNodes[i].nodeName == 'DIV') &&
+ (pre.childNodes[i].className == 'pending'))
+ {
+ pjx_load_fragment(['tree__' + pending_tree,
+ 'frag__' + pre.childNodes[i].id],
+ [load_fragment_finalize]);
+ return;
+ }
+ }
+}
+
+function load_fragment_finalize(content) {
+ var split = content.indexOf('|');
+ var div = document.getElementById(content.substr(0, split));
+ if (!div)
+ return;
+
+ div.innerHTML = content.substr(split+1);
+ div.className = 'done';
+
+ var links = div.getElementsByTagName('a');
+ ajaxify_link_handlers(links);
+ load_next_pending_fragment();
+
+// if (location.hash)
+// location.hash = location.hash;
+}
+
function load_file_finalize(content) {
var res = document.getElementById('content');
res.innerHTML = 'Done';
@@ -198,7 +251,7 @@ function load_file_finalize(content) {
head.innerHTML = '<a class=\"fref\" href=\".\">' + pending_tree + '</a>';
var path_walked = '';
var elems = pending_file.split(/\//);
- for (var i=0; i<elems.length; i++) {
+ for (var i = 0; i < elems.length; i++) {
if (elems[i] != '') {
head.innerHTML = head.innerHTML + '/' +
'<a class=\"fref\" href=\"' + path_walked + elems[i] +
@@ -214,13 +267,6 @@ function load_file_finalize(content) {
}
var full_path = full_tree + '/' + pending_file.replace(/^\/?/, '');
- var pre = document.getElementById('file_contents');
- if (pre && pre.className == 'partial') {
- pjx_load_file(['tree__' + pending_tree, 'file__' + pending_file,
- 'v__' + pending_ver, 'full__1', 'NO_CACHE'],
- [load_file_finalize]);
- }
-
var print = document.getElementById('lxr_print');
var dirlist = document.getElementById('content_dir');
if (dirlist) {
@@ -257,22 +303,9 @@ function load_file_finalize(content) {
loaded_ver = pending_ver;
hash_check = setTimeout('check_hash_navigation()', 50);
-// TODO: This really takes oodles of time. Consider coding into html.
- var i;
- for (i = 0; i < document.links.length; i++) {
- if (document.links[i].className == 'fref') {
- document.links[i].onclick = ajax_nav;
- }
- else if (document.links[i].className == 'line') {
- document.links[i].onclick = ajax_jumpto_line;
- }
- else if (document.links[i].className == 'sref' ||
- document.links[i].className == 'falt')
- {
- document.links[i].onclick = ajax_lookup_anchor;
- }
+ ajaxify_link_handlers(document.links);
- }
+ load_next_pending_fragment();
}
function load_content() {