# HG changeset patch # User Alexander Plavin # Date 1372933124 -14400 # Node ID f2e4fdb3dd2738d927137d9164d7d0e1646dafa9 # Parent 0e25a9e259314b8b5aa4d73766bd88247fe22bd9 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.

This implementation is chosen because other variants have important issues:

Strategy             FF              Chrome

current             D,LT,E,T,L        D,L
pre                 S,NW              S,NW
pre/div/nbsp        LT,E,T,TS,NW      TS,NW
pre/div/br          LT,E,T,NW         NW
ol/li/nbsp          LT,E,T,TS,AJ      TS,AJ
ol/li/br            LT,E,T,AJ         AJ
pre/span            LV                LV

Legend

Strategies:
- current: implemented in hgweb before this patch, i.e. divs for each line,
and line numbers links in the div too
- pre: the whole code in one pre tag with newlines, all line numbers
in another one with 'float: left'
- pre/div/{nbsp,br}: same as just 'pre', but separate divs for each line and
  or 
instead of empty lines (otherwise they are not copied at all) - ol/li/{nbsp,br}: a single ol with li's and divs for each line,   or
same as in previous strategy - pre/span: this patch Problems: D = (very minor) display problems, like wrong width of leading tabs LT = loses leading/trailing whitespace E = loses embedded whitespace B = loses blank lines T = loses tabs L = selects line numbers LV = (only) visually selects line numbers LVE = (only) visually selects line numbers at empty lines S = no stripes (and no ability to easily highlight lines-which-are-linked-at in the future) TS = space copied instead of empty line AJ = get anchor links only with JS (they work even without) NW = no linewrap easily possible (in future) As for browser versions compatibility, the CSS tricks used are supported in (according to caniuse.com): a) line numbers generation with 'content:' property and CSS counters: IE 8+, all other popular browsers (in pre-WebKit Opera numbers are being copied) b) stripes ('nth-child' selector): IE 8+, FF 3.5+, Safari 3.2+, Opera 9.5+, all other popular browsers c) line numbers are not visually selected ('user-select:' property): IE 10+, Opera 15.0+, all other popular browsers This patch is based on a demo implementation by Martin Geisler . diff -r 0e25a9e25931 -r f2e4fdb3dd27 mercurial/templates/paper/filerevision.tmpl --- a/mercurial/templates/paper/filerevision.tmpl Sat Jul 06 21:48:07 2013 +0200 +++ 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 0e25a9e25931 -r f2e4fdb3dd27 mercurial/templates/paper/map --- a/mercurial/templates/paper/map Sat Jul 06 21:48:07 2013 +0200 +++ 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 0e25a9e25931 -r f2e4fdb3dd27 mercurial/templates/static/style-paper.css --- a/mercurial/templates/static/style-paper.css Sat Jul 06 21:48:07 2013 +0200 +++ 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 0e25a9e25931 -r f2e4fdb3dd27 tests/test-hgweb-commands.t --- a/tests/test-hgweb-commands.t Sat Jul 06 21:48:07 2013 +0200 +++ 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 0e25a9e25931 -r f2e4fdb3dd27 tests/test-highlight.t --- a/tests/test-highlight.t Sat Jul 06 21:48:07 2013 +0200 +++ 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 ..