Index: trunk/extensions/CodeReview/modules/ext.codereview.linecomment.js |
— | — | @@ -0,0 +1,99 @@ |
| 2 | +( function( $ ) { |
| 3 | +var $rev = 0; |
| 4 | + |
| 5 | +window.CodeReview = $.extend( window.CodeReview, { |
| 6 | + |
| 7 | + /* initialization from PHP */ |
| 8 | + lcInit: function( rev ) { |
| 9 | + $rev = rev; |
| 10 | + // Register click() event to each diff lines |
| 11 | + $( 'table.mw-codereview-diff tr.commentable').click( |
| 12 | + function () { CodeReview.lcShowForm( $(this) ); } |
| 13 | + ); |
| 14 | + }, |
| 15 | + |
| 16 | + /** |
| 17 | + * Show the line comment code below a line of code |
| 18 | + * @param lineCode jQuery object |
| 19 | + */ |
| 20 | + lcShowForm: function( lineCode ) { |
| 21 | + lineCode.unbind( 'click' ); |
| 22 | + lineCode.click( function () { |
| 23 | + $( '#lineComment' ).fadeOut( 200 ).remove(); |
| 24 | + |
| 25 | + lineCode.unbind( 'click' ); |
| 26 | + lineCode.click( function() { |
| 27 | + CodeReview.lcShowForm( lineCode ); |
| 28 | + }); |
| 29 | + }); |
| 30 | + |
| 31 | + var lineComment = |
| 32 | + $('<tr id="lineComment"><td colspan="3">' |
| 33 | + +'<textarea id="lineCommentContent" rows="5"></textarea><br/>' |
| 34 | + +'<input id="lineCommentSend" type="button" value="Send">' |
| 35 | + +'</td></tr>'); |
| 36 | + lineComment.insertAfter( lineCode ); |
| 37 | + $( '#lineCommentContent' ).focus(); |
| 38 | + |
| 39 | + $( '#lineCommentSend' ).click( function() { |
| 40 | + CodeReview.lcSubmitComment( |
| 41 | + lineCode, |
| 42 | + lineComment, |
| 43 | + $( '#lineCommentContent' ).val() |
| 44 | + ); |
| 45 | + }); |
| 46 | + }, |
| 47 | + |
| 48 | + lcSubmitComment: function( lineCode, lineComment, comment ) { |
| 49 | + |
| 50 | + // retrieve line number from the code line id attribute: |
| 51 | + var lineId = lineCode.attr('id'); |
| 52 | + lineId = lineId.substring( lineId.indexOf( '-' ) + 1); |
| 53 | + |
| 54 | + $.ajax({ |
| 55 | + url: mw.util.wikiScript( 'api' ), |
| 56 | + data: { |
| 57 | + 'action': 'coderevisionupdate', |
| 58 | + |
| 59 | + 'repo' : mw.config.get( 'wgCodeReviewRepository' ), |
| 60 | + 'rev' : $rev, |
| 61 | + |
| 62 | + 'comment': comment, |
| 63 | + 'patchline' : lineId, |
| 64 | + |
| 65 | + 'format': 'json', |
| 66 | + }, |
| 67 | + dataType: 'json', |
| 68 | + type: 'POST', |
| 69 | + success: function( data ) { |
| 70 | + // FIXME |
| 71 | + console.log( data.coderevisionupdate ); |
| 72 | + //data.coderevisionupdate.commentid; |
| 73 | + |
| 74 | + var text = data.coderevisionupdate.HTML |
| 75 | + lineComment.fadeOut( 200 ).remove(); |
| 76 | + |
| 77 | + // check we have a list to insert the comment in. |
| 78 | + var next = lineCode.next( '.inlineComment' ); |
| 79 | + if( next.length === 0 ) { |
| 80 | + $( '<tr class="inlineComment"><td colspan="3"><ul><li>'+text+'</li></ul></td></tr>' ).insertAfter( lineCode ) |
| 81 | + } else { |
| 82 | + lineCode.next('.inlineComment').find( 'ul' ).append( |
| 83 | + $( '<li>' + text + '</li>' ) |
| 84 | + ); |
| 85 | + } |
| 86 | + |
| 87 | + |
| 88 | + // rebind event handler |
| 89 | + lineCode.click( |
| 90 | + function () { CodeReview.lcShowForm( $(this) ); } |
| 91 | + ); |
| 92 | + }, |
| 93 | + error: function() { |
| 94 | + console.log( "AJAX ERROR" ); |
| 95 | + } |
| 96 | + }); |
| 97 | + }, |
| 98 | + |
| 99 | +}); // window.CodeReview |
| 100 | +})( jQuery ); |
Index: trunk/extensions/CodeReview/modules/ext.codereview.styles.css |
— | — | @@ -58,6 +58,15 @@ |
59 | 59 | padding: 16px 16px 16px 48px; |
60 | 60 | } |
61 | 61 | |
| 62 | +/** list containers to insert comments inside the diff table */ |
| 63 | +.mw-codereview-inlineComment ul { |
| 64 | + list-style-image: none; /* override Vector default */ |
| 65 | + list-style-type: none; |
| 66 | + padding:0; |
| 67 | + padding-left: 2em; |
| 68 | + margin-bottom: 8px; |
| 69 | +} |
| 70 | + |
62 | 71 | .mw-codereview-status-new, |
63 | 72 | .mw-codereview-status-new td { |
64 | 73 | background: #ffffc0 !important; |
Index: trunk/extensions/CodeReview/api/ApiRevisionUpdate.php |
— | — | @@ -59,10 +59,16 @@ |
60 | 60 | $params['patchline'] |
61 | 61 | ); |
62 | 62 | |
| 63 | + // Forge a response object |
63 | 64 | $r = array( 'result' => 'Success' ); |
64 | | - |
65 | 65 | if ( $commentID !== 0 ) { |
| 66 | + // id inserted |
66 | 67 | $r['commentid'] = intval($commentID); |
| 68 | + // HTML Formatted comment |
| 69 | + $view = new CodeRevisionView( $repo, $rev); |
| 70 | + $comment = CodeComment::newFromID( $commentID, $rev ); |
| 71 | + $r['HTML'] = $view->formatComment( $comment ); |
| 72 | + //$r['HTML'] = print_r( $comment, true ); |
67 | 73 | } |
68 | 74 | |
69 | 75 | $this->getResult()->addValue( null, $this->getModuleName(), $r ); |
Index: trunk/extensions/CodeReview/api/ApiCodeDiff.php |
— | — | @@ -47,6 +47,7 @@ |
48 | 48 | $html = 'Diff too large.'; |
49 | 49 | } else { |
50 | 50 | $hilite = new CodeDiffHighlighter(); |
| 51 | + # Fetch diff rendered without inline comments |
51 | 52 | $html = $hilite->render( $diff ); |
52 | 53 | } |
53 | 54 | |
Index: trunk/extensions/CodeReview/ui/CodeRevisionView.php |
— | — | @@ -193,6 +193,12 @@ |
194 | 194 | } |
195 | 195 | $html .= xml::closeElement( 'form' ); |
196 | 196 | |
| 197 | + $wgOut->addModules( 'ext.codereview.linecomment' ); |
| 198 | + $encRev = Xml::encodeJsVar( $this->mRev->getId() ); |
| 199 | + $wgOut->addInLineScript( |
| 200 | + "CodeReview.lcInit( $encRev );" |
| 201 | + ); |
| 202 | + |
197 | 203 | $wgOut->addHTML( $html ); |
198 | 204 | } |
199 | 205 | |
— | — | @@ -441,7 +447,8 @@ |
442 | 448 | return htmlspecialchars( wfMsg( 'code-rev-diff-too-large' ) ); |
443 | 449 | } else { |
444 | 450 | $hilite = new CodeDiffHighlighter(); |
445 | | - return $hilite->render( $diff ); |
| 451 | + # Diff rendered with inline comments |
| 452 | + return $hilite->render( $diff, $this->mRepo, $this->mRev ); |
446 | 453 | } |
447 | 454 | } |
448 | 455 | |
Index: trunk/extensions/CodeReview/CodeReview.php |
— | — | @@ -168,6 +168,10 @@ |
169 | 169 | 'scripts' => 'ext.codereview.loaddiff.js' |
170 | 170 | ) + $commonModuleInfo; |
171 | 171 | |
| 172 | +$wgResourceModules['ext.codereview.linecomment'] = array( |
| 173 | + 'scripts' => 'ext.codereview.linecomment.js' |
| 174 | +) + $commonModuleInfo; |
| 175 | + |
172 | 176 | // Revision tooltips CodeRevisionView: |
173 | 177 | $wgResourceModules['ext.codereview.tooltips'] = array( |
174 | 178 | 'scripts' => 'ext.codereview.tooltips.js', |
Index: trunk/extensions/CodeReview/backend/DiffHighlighter.php |
— | — | @@ -13,19 +13,63 @@ |
14 | 14 | /* line number inside patch */ |
15 | 15 | private $lineNumber = 0; |
16 | 16 | |
| 17 | + /* CodeRepository: The repository for this revision */ |
| 18 | + private $repo = null; |
| 19 | + /* CodeRevision: revision the diff comes from */ |
| 20 | + private $rev = null; |
| 21 | + |
17 | 22 | /** |
| 23 | + * Comments inside the diff. |
| 24 | + * initialized with fetchInlineComments() |
| 25 | + */ |
| 26 | + private $inlineComments = null; |
| 27 | + |
| 28 | + /** |
18 | 29 | * Main entry point. Given a diff text, highlight it |
19 | 30 | * and wrap it in a div |
| 31 | + * Pass both $repos and $rev to have the diff rendered with inline comments |
| 32 | + * |
20 | 33 | * @param $text string Text to highlight |
| 34 | + * @param $repo CodeRepository (default: null) |
| 35 | + * @param $repo CodeRevision (default: null) |
21 | 36 | * @return string |
22 | 37 | */ |
23 | | - function render( $text ) { |
| 38 | + function render( $text, CodeRepository $repo = null, CodeRevision $rev = null ) { |
| 39 | + if( $repo xor $rev ) { |
| 40 | + throw new MWException( __METHOD__ . " must have both repository and revision or none of them\n" ); |
| 41 | + } elseif( $repo && $rev ) { |
| 42 | + $this->repo = $repo; |
| 43 | + $this->rev = $rev; |
| 44 | + $this->fetchInlineComments(); |
| 45 | + } |
| 46 | + |
24 | 47 | return '<table class="mw-codereview-diff">' . |
25 | 48 | $this->splitLines( $text ) . |
26 | 49 | "</table>\n"; |
27 | 50 | } |
28 | 51 | |
29 | 52 | /** |
| 53 | + * Fetch comments attached to a patch line. |
| 54 | + * Comments can be accessed through the array inlineComments. Its format is: |
| 55 | + * array( |
| 56 | + * line# => array( |
| 57 | + * CodeComment, CodeComment ... |
| 58 | + * ), |
| 59 | + * line# => array( |
| 60 | + * CodeComment, CodeComment ... |
| 61 | + * ), |
| 62 | + * ... |
| 63 | + * ); |
| 64 | + */ |
| 65 | + private function fetchInlineComments() { |
| 66 | + $inline = $this->rev->getComments( 'which are attached' ); |
| 67 | + foreach( $inline as $comment ) { |
| 68 | + $line = $comment->patchLine; # absolute line number in the diff |
| 69 | + $this->inlineComments[$line][] = $comment; |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + /** |
30 | 74 | * Given a bunch of text, split it into individual |
31 | 75 | * lines, color them, then put it back into one big |
32 | 76 | * string |
— | — | @@ -79,10 +123,34 @@ |
80 | 124 | $r = $this->handleLineFile( $line ); |
81 | 125 | } |
82 | 126 | |
| 127 | + # Finally add up any lineComments that might apply to this line |
| 128 | + $r .= $this->addLineComments(); |
| 129 | + |
83 | 130 | # Return HTML generated by one of the handler |
84 | 131 | return $r; |
85 | 132 | } |
86 | 133 | |
| 134 | + function addLineComments() { |
| 135 | + $return = ''; |
| 136 | + if( !isset( $this->inlineComments ) ) { |
| 137 | + # No inline comments for this revision. |
| 138 | + return $return; |
| 139 | + } |
| 140 | + if( !array_key_exists( $this->lineNumber, $this->inlineComments ) ) { |
| 141 | + # Line does not have any comment applying to |
| 142 | + return $return; |
| 143 | + } |
| 144 | + |
| 145 | + $comments = $this->inlineComments[$this->lineNumber]; |
| 146 | + # FIXME: comment formatting can only be handled by a view |
| 147 | + # Would need abstraction, for example as a class of views helpers. |
| 148 | + $view = new CodeRevisionView( $this->repo, $this->rev ); |
| 149 | + foreach( $comments as $comment ) { |
| 150 | + $return .= "<li>{$view->formatComment( $comment )}</li>\n" ; |
| 151 | + } |
| 152 | + return "<tr class=\"mw-codereview-inlineComment\"><td colspan=\"3\"><ul>{$return}</ul></td></tr>\n"; |
| 153 | + } |
| 154 | + |
87 | 155 | function formatLine( $content, $class = null ) { |
88 | 156 | $formatLN = |
89 | 157 | "<td class=\"linenumbers\">%s</td>" |
— | — | @@ -91,7 +159,7 @@ |
92 | 160 | |
93 | 161 | $idAttr = $this->getLineIdAttr(); |
94 | 162 | if( is_null($class) ) { |
95 | | - return sprintf( "<tr {$idAttr}>{$formatLN}<td>%s</td></tr>\n", |
| 163 | + return sprintf( "<tr class=\"commentable\" {$idAttr}>{$formatLN}<td>%s</td></tr>\n", |
96 | 164 | $this->left, |
97 | 165 | $this->right, |
98 | 166 | $content |
— | — | @@ -121,7 +189,7 @@ |
122 | 190 | } |
123 | 191 | |
124 | 192 | $classAttr = is_null($class) ? '' : " class=\"$class\""; |
125 | | - return sprintf( "<tr {$idAttr}>{$formatLN}<td%s>%s</td></tr>\n", |
| 193 | + return sprintf( "<tr class=\"commentable\" {$idAttr}>{$formatLN}<td%s>%s</td></tr>\n", |
126 | 194 | $left, $right, |
127 | 195 | $classAttr, $content |
128 | 196 | ); |