From patchwork Fri Jul 5 18:20:19 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [1, of, 3, V6] hgweb: code selection without line numbers in file source view From: Alexander Plavin X-Patchwork-Id: 1797 Message-Id: To: mercurial-devel@selenic.com Cc: raf@durin42.comangel.ezquerra, ""@gmail.com Date: Fri, 05 Jul 2013 22:20:19 +0400 # HG changeset patch # User Alexander Plavin # Date 1372933124 -14400 # Thu Jul 04 14:18:44 2013 +0400 # Node ID dd7a098f0093dc28d025064bba4407acc53a90e5 # Parent d7b4aa1049d3fbbc2b8d24114afad82586c49db3 hgweb: code selection without line numbers in file source view All the source lines are put in a
 tag, which gives correct display and
copy&paste in both Chromium (WebKit) and FireFox: line numbers are not copied,
all the tabs and spaces are kept. This doesn't change the visual appearance
of the view compared to current hgweb version and doesn't use any JS code.
Also, stripes in this view are now generated clientside with CSS.

Issues:
a) in pre-Webkit versions of Opera line numbers are copied
b) line numbers are (only visually) selected at empty lines and in older
versions of most browsers, which don't support 'user-select' propery

This implementation is chosen because other variants have more important issues:
a) current hgweb implementation: line numbers are selected, tabs and spaces are
lost when copy in FireFox
b) the whole code in one 
 tag, and all line numbers in another one
with 'float: left': not possible to add stripes, highlighting and line wrapping
c) same as previous, but separate divs for each line and nbsp's or br's
in place of empty lines (to keep display correct): tabs and spaces are lost
when copy in FireFox, not possible to add line wrapping
d) ordered html list for the lines, with nbsp's or br's in place of empty
lines (to keep display correct): tabs and spaces are lost when copy in FireFox,
getting anchor links to lines is possible only with JavaScript, and display is
altered with extra dots after line numbers.

As for browser compatibility, the CSS tricks used are supported in
(according to caniuse.com):
a) line numbers generation with 'content:' property and CSS counters
is supported in IE 8 and later and in all other popular browsers
b) stripes, implemented with 'nth-child' selector supported in
IE 8, FF 3.5, Safari 3.2, Opera 9.5 and later, and all other popular browsers

This patch is based on a demo implementation by
Martin Geisler .

diff -r d7b4aa1049d3 -r dd7a098f0093 mercurial/templates/paper/filerevision.tmpl
--- a/mercurial/templates/paper/filerevision.tmpl	Sat Jun 29 14:36:51 2013 +0400
+++ b/mercurial/templates/paper/filerevision.tmpl	Thu Jul 04 14:18:44 2013 +0400
@@ -68,7 +68,7 @@
 
 
line source
-{text%fileline} +
{text%fileline}
diff -r d7b4aa1049d3 -r dd7a098f0093 mercurial/templates/paper/map --- a/mercurial/templates/paper/map Sat Jun 29 14:36:51 2013 +0400 +++ b/mercurial/templates/paper/map Thu Jul 04 14:18:44 2013 +0400 @@ -72,7 +72,7 @@ filecomparison = filecomparison.tmpl filelog = filelog.tmpl fileline = ' -
{linenumber} {line|escape}
' + {strip(line|escape, '\r\n')}' filelogentry = filelogentry.tmpl annotateline = ' diff -r d7b4aa1049d3 -r dd7a098f0093 mercurial/templates/static/style-paper.css --- a/mercurial/templates/static/style-paper.css Sat Jun 29 14:36:51 2013 +0400 +++ b/mercurial/templates/static/style-paper.css Thu Jul 04 14:18:44 2013 +0400 @@ -209,6 +209,44 @@ .source a { color: #999; font-size: smaller; font-family: monospace;} .bottomline { border-bottom: 1px solid #999; } +.sourcelines { + font-size: 90%; + position: relative; +} + +.sourcelines > span { + display: inline-block; + width: 100%; + padding: 1px 0px; + counter-increment: lineno; +} + +.sourcelines > span:before { + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + display: inline-block; + width: 4em; + margin-right: 1em; + font-size: smaller; + color: #999; + text-align: right; + content: counter(lineno); +} + +.sourcelines > span:nth-child(4n+1) { background-color: #f0f0f0; } +.sourcelines > span:nth-child(4n+3) { background-color: white; } + +.sourcelines > a { + display: inline-block; + position: absolute; + left: 0px; + width: 4em; + height: 1em; +} + .fileline { font-family: monospace; } .fileline img { border: 0; } diff -r d7b4aa1049d3 -r dd7a098f0093 tests/test-hgweb-commands.t --- a/tests/test-hgweb-commands.t Sat Jun 29 14:36:51 2013 +0400 +++ b/tests/test-hgweb-commands.t Thu Jul 04 14:18:44 2013 +0400 @@ -668,9 +668,8 @@
line source
- -
1 foo -
+
+  foo
diff -r d7b4aa1049d3 -r dd7a098f0093 tests/test-highlight.t --- a/tests/test-highlight.t Sat Jun 29 14:36:51 2013 +0400 +++ b/tests/test-highlight.t Thu Jul 04 14:18:44 2013 +0400 @@ -137,39 +137,39 @@
line source
- -
1 #!/usr/bin/env python
- -
3 """Fun with generators. Corresponding Haskell implementation:
- -
5 primes = 2 : sieve [3, 5..]
-
6 where sieve (p:ns) = p : sieve [n | n <- ns, mod n p /= 0]
-
7 """
- -
9 from itertools import dropwhile, ifilter, islice, count, chain
- -
11 def primes():
-
12 """Generate all primes."""
-
13 def sieve(ns):
-
14 p = ns.next()
-
15 # It is important to yield *here* in order to stop the
-
16 # infinite recursion.
-
17 yield p
-
18 ns = ifilter(lambda n: n % p != 0, ns)
-
19 for n in sieve(ns):
-
20 yield n
- -
22 odds = ifilter(lambda i: i % 2 == 1, count())
-
23 return chain([2], sieve(dropwhile(lambda n: n < 3, odds)))
- -
25 if __name__ == "__main__":
-
26 import sys
-
27 try:
-
28 n = int(sys.argv[1])
-
29 except (ValueError, IndexError):
-
30 n = 10
-
31 p = primes()
-
32 print "The first %d primes: %s" % (n, list(islice(p, n)))
+
+  #!/usr/bin/env python
+  
+  """Fun with generators. Corresponding Haskell implementation:
+  
+  primes = 2 : sieve [3, 5..]
+      where sieve (p:ns) = p : sieve [n | n <- ns, mod n p /= 0]
+  """
+  
+  from itertools import dropwhile, ifilter, islice, count, chain
+  
+  def primes():
+      """Generate all primes."""
+      def sieve(ns):
+          p = ns.next()
+          # It is important to yield *here* in order to stop the
+          # infinite recursion.
+          yield p
+          ns = ifilter(lambda n: n % p != 0, ns)
+          for n in sieve(ns):
+              yield n
+  
+      odds = ifilter(lambda i: i % 2 == 1, count())
+      return chain([2], sieve(dropwhile(lambda n: n < 3, odds)))
+  
+  if __name__ == "__main__":
+      import sys
+      try:
+          n = int(sys.argv[1])
+      except (ValueError, IndexError):
+          n = 10
+      p = primes()
+      print "The first %d primes: %s" % (n, list(islice(p, n)))
@@ -593,17 +593,14 @@ $ hgserveget euc-jp eucjp.txt % HGENCODING=euc-jp hg serve % hgweb filerevision, html -
1 \xb5\xfe
(esc) % errors encountered $ hgserveget utf-8 eucjp.txt % HGENCODING=utf-8 hg serve % hgweb filerevision, html -
1 \xef\xbf\xbd\xef\xbf\xbd
(esc) % errors encountered $ hgserveget us-ascii eucjp.txt % HGENCODING=us-ascii hg serve % hgweb filerevision, html -
1 ??
% errors encountered $ cd ..