changeset 1776:22333ddd1a40 beta

implements #307, configurable diffs - global context increase and whitespace - perfile context and whitespace - fixed anchor links support
author Marcin Kuzminski <marcin@python-works.com>
date Sun, 11 Dec 2011 00:51:53 +0200
parents fb423ee576e8
children 910e3a0d27c0
files rhodecode/controllers/changeset.py rhodecode/lib/helpers.py rhodecode/public/css/style.css rhodecode/templates/changeset/changeset.html
diffstat 4 files changed, 161 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/controllers/changeset.py	Sat Dec 10 14:49:12 2011 +0200
+++ b/rhodecode/controllers/changeset.py	Sun Dec 11 00:51:53 2011 +0200
@@ -25,12 +25,18 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import logging
 import traceback
+from collections import defaultdict
+from webob.exc import HTTPForbidden
 
 from pylons import tmpl_context as c, url, request, response
 from pylons.i18n.translation import _
 from pylons.controllers.util import redirect
 from pylons.decorators import jsonify
 
+from vcs.exceptions import RepositoryError, ChangesetError, \
+    ChangesetDoesNotExistError
+from vcs.nodes import FileNode
+
 import rhodecode.lib.helpers as h
 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 from rhodecode.lib.base import BaseRepoController, render
@@ -39,16 +45,109 @@
 from rhodecode.lib import diffs
 from rhodecode.model.db import ChangesetComment
 from rhodecode.model.comment import ChangesetCommentsModel
-
-from vcs.exceptions import RepositoryError, ChangesetError, \
-    ChangesetDoesNotExistError
-from vcs.nodes import FileNode
-from webob.exc import HTTPForbidden
 from rhodecode.model.meta import Session
 
 log = logging.getLogger(__name__)
 
 
+def anchor_url(revision,path):
+    fid = h.FID(revision, path)
+    return h.url.current(anchor=fid,**request.GET)
+
+def get_ignore_ws(fid, GET):
+    ig_ws_global = request.GET.get('ignorews')
+    ig_ws = filter(lambda k:k.startswith('WS'),GET.getall(fid))
+    if ig_ws:
+        try:
+            return int(ig_ws[0].split(':')[-1])
+        except:
+            pass
+    return ig_ws_global
+
+def _ignorews_url(fileid=None):
+
+    params = defaultdict(list)
+    lbl = _('show white space')
+    ig_ws = get_ignore_ws(fileid, request.GET)
+    ln_ctx = get_line_ctx(fileid, request.GET)
+    # global option
+    if fileid is None:
+        if ig_ws is None:
+            params['ignorews'] += [1]
+            lbl = _('ignore white space')
+        ctx_key = 'context'
+        ctx_val = ln_ctx
+    # per file options
+    else:
+        if ig_ws is None:
+            params[fileid] += ['WS:1']
+            lbl = _('ignore white space')
+
+        ctx_key = fileid
+        ctx_val = 'C:%s' % ln_ctx
+    # if we have passed in ln_ctx pass it along to our params
+    if ln_ctx:
+        params[ctx_key] += [ctx_val]
+    
+    params['anchor'] = fileid
+    return h.link_to(lbl, h.url.current(**params))
+
+def get_line_ctx(fid, GET):
+    ln_ctx_global = request.GET.get('context')
+    ln_ctx = filter(lambda k:k.startswith('C'),GET.getall(fid))
+    
+    if ln_ctx:
+        retval = ln_ctx[0].split(':')[-1]
+    else:
+        retval = ln_ctx_global
+
+    try:
+        return int(retval)
+    except:
+        return
+
+def _context_url(fileid=None):
+    """
+    Generates url for context lines
+    
+    :param fileid:
+    """
+    ig_ws = get_ignore_ws(fileid, request.GET)
+    ln_ctx = (get_line_ctx(fileid, request.GET) or 3) * 2
+
+    params = defaultdict(list)
+
+    # global option
+    if fileid is None:
+        if ln_ctx > 0:
+            params['context'] += [ln_ctx]
+
+        if ig_ws:
+            ig_ws_key = 'ignorews'
+            ig_ws_val = 1
+
+    # per file option
+    else:
+        params[fileid] += ['C:%s' % ln_ctx]
+        ig_ws_key = fileid
+        ig_ws_val = 'WS:%s' % 1
+        
+    if ig_ws:
+        params[ig_ws_key] += [ig_ws_val]
+
+    lbl = _('%s line context') % ln_ctx
+
+    params['anchor'] = fileid
+    return h.link_to(lbl, h.url.current(**params))
+
+def wrap_to_table(str_):
+    return '''<table class="code-difftable">
+                <tr class="line">
+                <td class="lineno new"></td>
+                <td class="code"><pre>%s</pre></td>
+                </tr>
+              </table>''' % str_
+
 class ChangesetController(BaseRepoController):
 
     @LoginRequired()
@@ -59,16 +158,10 @@
         c.affected_files_cut_off = 60
 
     def index(self, revision):
-        ignore_whitespace = request.GET.get('ignorews') == '1'
-        line_context = request.GET.get('context', 3)
-        def wrap_to_table(str):
 
-            return '''<table class="code-difftable">
-                        <tr class="line">
-                        <td class="lineno new"></td>
-                        <td class="code"><pre>%s</pre></td>
-                        </tr>
-                      </table>''' % str
+        c.anchor_url = anchor_url
+        c.ignorews_url = _ignorews_url
+        c.context_url = _context_url
 
         #get ranges of revisions if preset
         rev_range = revision.split('...')[:2]
@@ -130,10 +223,13 @@
                     # added nodes and their size defines how many changes were
                     # made
                     c.sum_added += node.size
+                    fid = h.FID(revision, node.path)
+                    line_context_lcl = get_line_ctx(fid, request.GET)
+                    ignore_whitespace_lcl = get_ignore_ws(fid, request.GET)
                     if c.sum_added < self.cut_off_limit:
                         f_gitdiff = diffs.get_gitdiff(filenode_old, node,
-                                           ignore_whitespace=ignore_whitespace,
-                                           context=line_context)
+                                        ignore_whitespace=ignore_whitespace_lcl,
+                                        context=line_context_lcl)
                         d = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
 
                         st = d.stat()
@@ -171,9 +267,12 @@
                     else:
 
                         if c.sum_removed < self.cut_off_limit:
+                            fid = h.FID(revision, node.path)
+                            line_context_lcl = get_line_ctx(fid, request.GET)
+                            ignore_whitespace_lcl = get_ignore_ws(fid, request.GET,)
                             f_gitdiff = diffs.get_gitdiff(filenode_old, node,
-                                           ignore_whitespace=ignore_whitespace,
-                                           context=line_context)
+                                    ignore_whitespace=ignore_whitespace_lcl,
+                                    context=line_context_lcl)
                             d = diffs.DiffProcessor(f_gitdiff,
                                                      format='gitdiff')
                             st = d.stat()
--- a/rhodecode/lib/helpers.py	Sat Dec 10 14:49:12 2011 +0200
+++ b/rhodecode/lib/helpers.py	Sun Dec 11 00:51:53 2011 +0200
@@ -52,6 +52,17 @@
 reset = _reset
 safeid = _make_safe_id_component
 
+        
+def FID(raw_id,path):
+    """
+    Creates a uniqe ID for filenode based on it's path and revision
+    
+    :param raw_id:
+    :param path:
+    """
+    return 'C-%s-%s' % (short_id(raw_id), safeid(safe_unicode(path)))
+
+
 def get_token():
     """Return the current authentication token, creating one if one doesn't
     already exist.
--- a/rhodecode/public/css/style.css	Sat Dec 10 14:49:12 2011 +0200
+++ b/rhodecode/public/css/style.css	Sun Dec 11 00:51:53 2011 +0200
@@ -1890,7 +1890,9 @@
 	color: #556CB5;
 	white-space: pre-wrap;
 }
-
+#changeset_content .container .left .message a:hover {
+	text-decoration: none;
+}
 .cs_files .cur_cs {
 	margin: 10px 2px;
 	font-weight: bold;
@@ -2254,7 +2256,7 @@
 	float: left;
 }
 
-.diffblock .changeset_header .diff-menu{
+.diffblock .diff-menu{
     position: absolute;
     background: none repeat scroll 0 0 #FFFFFF;
     border-color: #003367 #666666 #666666;
@@ -2267,14 +2269,14 @@
     
 }
 
-.diffblock .changeset_header .diff-menu ul li {
+.diffblock  .diff-menu ul li {
 	padding: 0px 0px 0px 0px !important;
 }
-.diffblock .changeset_header .diff-menu ul li a{
+.diffblock  .diff-menu ul li a{
 	display: block;
 	padding: 3px 8px 3px 8px !important;
 }
-.diffblock .changeset_header .diff-menu ul li a:hover{
+.diffblock  .diff-menu ul li a:hover{
     text-decoration: none;
     background-color: #EEEEEE;
 }
@@ -3689,6 +3691,11 @@
     border-bottom: 1px solid #CCCCCC;
     background: #EEEEEE;
     padding:10px 0 10px 0;
+    height: 14px;
+}
+div.diffblock .code-header .date{
+    float:left;
+    text-transform: uppercase;
 }
 div.diffblock .code-header div{
     margin-left:4px;
--- a/rhodecode/templates/changeset/changeset.html	Sat Dec 10 14:49:12 2011 +0200
+++ b/rhodecode/templates/changeset/changeset.html	Sun Dec 11 00:51:53 2011 +0200
@@ -18,12 +18,6 @@
     ${self.menu('changelog')}     
 </%def>
 
-<%def name="fid(raw_id,path)" filter="strip">
-  <% 
-    return 'C-%s-%s' % (h.short_id(raw_id),h.safeid(h.safe_unicode(path))) 
-  %>
-</%def>
-
 <%def name="main()">
 <div class="box">
     <!-- box / title -->
@@ -33,19 +27,24 @@
     <div class="table">
 		<div class="diffblock">
 			<div class="code-header">
-				<div>
-                <span>${h.link_to(_('raw diff'),
-				h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</span>
-				 &raquo; <span>${h.link_to(_('download diff'),
-				h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</span>
-				<div class="comments-number" style="float:right;padding-right:5px">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
-                </div>
+                <div class="date">${_('commit')} ${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} ${c.changeset.date}</div>
+                <div class="diff-menu-wrapper">
+                    <img class="diff-menu-activate" style="cursor: pointer" alt="diff-menu" src="${h.url('/images/icons/script_gear.png')}" />
+                    <div class="diff-menu" style="display:none">
+                        <ul>
+                          <li>${h.link_to(_('raw diff'),h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</li>
+                          <li>${h.link_to(_('download diff'),h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</li>
+                          <li>${c.ignorews_url()}</li>
+                          <li>${c.context_url()}</li>
+                        </ul>
+                    </div>                        
+                </div>				
+                <div class="comments-number" style="float:right;padding-right:5px">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
 			</div>
 		</div>
 	    <div id="changeset_content">
 			<div class="container">
 	             <div class="left">
-	                 <div class="date">${_('commit')} ${c.changeset.revision}: ${h.short_id(c.changeset.raw_id)}@${c.changeset.date}</div>
 	                 <div class="author">
 	                     <div class="gravatar">
 	                         <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
@@ -100,9 +99,9 @@
 	                    <div class="cs_${change}">
                             <div class="node">
                             %if change != 'removed':
-                                ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=self.fid(filenode.changeset.raw_id,filenode.path)))}
+                                ${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path))}
                             %else:
-                                ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=self.fid('',filenode.path)))}
+                                ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID('',filenode.path)))}
                             %endif
                             </div>
 		                    <div class="changes">${h.fancy_file_stats(stat)}</div>
@@ -118,8 +117,8 @@
     	
 	%for change,filenode,diff,cs1,cs2,stat in c.changes:
 		%if change !='removed':
-		<div style="clear:both;height:10px"></div>
-		<div class="diffblock  margined comm" id="${self.fid(filenode.changeset.raw_id,filenode.path)}">
+		<div id="${h.FID(filenode.changeset.raw_id,filenode.path)}" style="clear:both;height:90px;margin-top:-60px"></div>
+		<div class="diffblock  margined comm">
 			<div class="code-header">
 				<div class="changeset_header">
 					<div class="changeset_file">
@@ -133,13 +132,15 @@
                               <li>${h.link_to(_('diff'),h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff'))}</li>
                               <li>${h.link_to(_('raw diff'),h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='raw'))}</li>
                               <li>${h.link_to(_('download diff'),h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='download'))}</li>
+                              <li>${c.ignorews_url(h.FID(filenode.changeset.raw_id,filenode.path))}</li>
+                              <li>${c.context_url(h.FID(filenode.changeset.raw_id,filenode.path))}</li>
                             </ul>
                         </div>                        
                     </div>
                     <span style="float:right;margin-top:-3px">
                       <label>
                       ${_('show inline comments')}
-                      ${h.checkbox('',checked="checked",class_="show-inline-comments",id_for=self.fid(filenode.changeset.raw_id,filenode.path))}
+                      ${h.checkbox('',checked="checked",class_="show-inline-comments",id_for=h.FID(filenode.changeset.raw_id,filenode.path))}
                       </label>
                     </span>
 				</div>
@@ -164,7 +165,7 @@
         <div class="comments-number">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
         
         %for path, lines in c.inline_comments:
-            <div style="display:none" class="inline-comment-placeholder" path="${path}" target_id="${self.fid(c.changeset.raw_id,path)}">
+            <div style="display:none" class="inline-comment-placeholder" path="${path}" target_id="${h.FID(c.changeset.raw_id,path)}">
             % for line,comments in lines.iteritems():
                 <div class="inline-comment-placeholder-line" line="${line}" target_id="${h.safeid(h.safe_unicode(path))}"> 
                 %for co in comments: