diff options
author | Arne Georg Gleditsch <argggh@lxr.linpro.no> | 2008-02-12 22:53:01 +0100 |
---|---|---|
committer | Arne Georg Gleditsch <argggh@lxr.linpro.no> | 2008-02-12 22:53:01 +0100 |
commit | cd93fa83ba632588b357b190f47809205b9a5d91 (patch) | |
tree | a597c4c6fb8e07584fd81553b84a543eebb8a86f | |
parent | 31ad80f533166802520e018d0511c2f18b15db71 (diff) |
Change line number layout and caching mechanism.
-rw-r--r-- | lib/LXRng/Markup/File.pm | 6 | ||||
-rw-r--r-- | lib/LXRng/Web.pm | 140 | ||||
-rw-r--r-- | webroot/.static/css/lxrng.css | 34 | ||||
-rw-r--r-- | webroot/.static/js/lxrng-funcs.js | 79 |
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() { |