Fix copy/paste in diff view

The addition of syntax highlighting silently broke copy/paste
functionality due to the addition of another layer of div nesting.

Related to this bug are some issues with correct text selection in
unified diff view, so this patch addresses them as well.

Bug: Issue 4317
Change-Id: Iac7379de4131ab4e44905a54218d42fcfe67ce62
This commit is contained in:
Kasper Nilsson
2016-08-24 14:44:21 -07:00
parent 623e917b6f
commit 613b49c64e
5 changed files with 46 additions and 31 deletions

View File

@@ -340,6 +340,9 @@
side, side,
this._comments.meta.projectConfig); this._comments.meta.projectConfig);
threadEl.comments = comments; threadEl.comments = comments;
if (opt_side) {
threadEl.setAttribute('data-side', opt_side);
}
return threadEl; return threadEl;
}; };
@@ -363,11 +366,14 @@
GrDiffBuilder.prototype._createTextEl = function(line, opt_side) { GrDiffBuilder.prototype._createTextEl = function(line, opt_side) {
var td = this._createElement('td'); var td = this._createElement('td');
var text = line.text;
if (line.type !== GrDiffLine.Type.BLANK) { if (line.type !== GrDiffLine.Type.BLANK) {
td.classList.add('content'); td.classList.add('content');
if (!text) {
text = '\xa0';
}
} }
td.classList.add(line.type); td.classList.add(line.type);
var text = line.text;
var html = util.escapeHTML(text); var html = util.escapeHTML(text);
html = this._addTabWrappers(html, this._prefs.tab_size); html = this._addTabWrappers(html, this._prefs.tab_size);

View File

@@ -18,17 +18,19 @@ limitations under the License.
<dom-module id="gr-diff-selection"> <dom-module id="gr-diff-selection">
<template> <template>
<style> <style>
.contentWrapper ::content .content { .contentWrapper ::content .content,
.contentWrapper ::content .contextControl {
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
} }
:host.selected-right .contentWrapper ::content .right + .content, :host.selected-right .contentWrapper ::content .side-by-side .right + .content,
:host.selected-left .contentWrapper ::content .left + .content, :host.selected-left .contentWrapper ::content .side-by-side .left + .content,
:host.selected-right .contentWrapper ::content .unified .right ~ .content, :host.selected-right .contentWrapper ::content .unified .right.lineNum ~ .content,
:host.selected-left .contentWrapper ::content .unified .left ~ .content { :host.selected-left .contentWrapper ::content .unified .left.lineNum ~ .content:not(.both),
:host .contentWrapper ::content .unified .gr-diff-comment .message {
-webkit-user-select: text; -webkit-user-select: text;
-moz-user-select: text; -moz-user-select: text;
-ms-user-select: text; -ms-user-select: text;

View File

@@ -56,7 +56,8 @@
}, },
_handleCopy: function(e) { _handleCopy: function(e) {
if (!e.target.classList.contains('content')) { if (!e.target.classList.contains('contentText') &&
!e.target.classList.contains('gr-syntax')) {
return; return;
} }
var lineEl = this.diffBuilder.getLineElByChild(e.target); var lineEl = this.diffBuilder.getLineElByChild(e.target);
@@ -69,17 +70,17 @@
e.preventDefault(); e.preventDefault();
}, },
_getSelectedText: function(opt_side) { _getSelectedText: function(side) {
var sel = window.getSelection(); var sel = window.getSelection();
if (sel.rangeCount != 1) { if (sel.rangeCount != 1) {
return; // No multi-select support yet. return; // No multi-select support yet.
} }
var range = sel.getRangeAt(0); var range = sel.getRangeAt(0);
var fragment = range.cloneContents(); var fragment = range.cloneContents();
var selector = '.content,td.content:nth-of-type(1)'; var selector = '.contentText';
if (opt_side) { selector += '[data-side="' + side + '"]';
selector = '.' + opt_side + ' + ' + selector; selector += ':not(:empty)';
}
var contentEls = Polymer.dom(fragment).querySelectorAll(selector); var contentEls = Polymer.dom(fragment).querySelectorAll(selector);
if (contentEls.length === 0) { if (contentEls.length === 0) {
return fragment.textContent; return fragment.textContent;

View File

@@ -30,21 +30,33 @@ limitations under the License.
<table> <table>
<tr> <tr>
<td class="lineNum left">1</td> <td class="lineNum left">1</td>
<td class="content">ba ba</td> <td class="content">
<div class="contentText" data-side="left">ba ba</div>
</td>
<td class="lineNum right">1</td> <td class="lineNum right">1</td>
<td class="content">some other text</td> <td class="content">
<div class="contentText" data-side="right">some other text</div>
</td>
</tr> </tr>
<tr> <tr>
<td class="lineNum left">2</td> <td class="lineNum left">2</td>
<td class="content">zin</td> <td class="content">
<div class="contentText" data-side="left">zin</div>
</td>
<td class="lineNum right">2</td> <td class="lineNum right">2</td>
<td class="content">more more more</td> <td class="content">
<div class="contentText" data-side="right">more more more</div>
</td>
</tr> </tr>
<tr> <tr>
<td class="lineNum left">2</td> <td class="lineNum left">2</td>
<td class="content">ga ga</td> <td class="content">
<div class="contentText" data-side="left">ga ga</div>
</td>
<td class="lineNum right">3</td> <td class="lineNum right">3</td>
<td class="other">some other text</td> <td class="other">
<div class="contentText" data-side="right">some other text</div>
</td>
</tr> </tr>
</table> </table>
</gr-diff-selection> </gr-diff-selection>
@@ -105,36 +117,37 @@ limitations under the License.
test('asks for text for right side Elements', function() { test('asks for text for right side Elements', function() {
element._cachedDiffBuilder.getSideByLineEl.returns('left'); element._cachedDiffBuilder.getSideByLineEl.returns('left');
sinon.stub(element, '_getSelectedText'); sinon.stub(element, '_getSelectedText');
emulateCopyOn(element.querySelector('td.content')); emulateCopyOn(element.querySelector('div.contentText'));
assert.deepEqual(['left'], element._getSelectedText.lastCall.args); assert.deepEqual(['left'], element._getSelectedText.lastCall.args);
}); });
test('reacts to copy for content Elements', function() { test('reacts to copy for content Elements', function() {
sinon.stub(element, '_getSelectedText'); sinon.stub(element, '_getSelectedText');
emulateCopyOn(element.querySelector('td.content')); emulateCopyOn(element.querySelector('div.contentText'));
assert.isTrue(element._getSelectedText.called); assert.isTrue(element._getSelectedText.called);
}); });
test('copy event is prevented for content Elements', function() { test('copy event is prevented for content Elements', function() {
sinon.stub(element, '_getSelectedText'); sinon.stub(element, '_getSelectedText');
var event = emulateCopyOn(element.querySelector('td.content')); var event = emulateCopyOn(element.querySelector('div.contentText'));
assert.isTrue(event.preventDefault.called); assert.isTrue(event.preventDefault.called);
}); });
test('inserts text into clipboard on copy', function() { test('inserts text into clipboard on copy', function() {
sinon.stub(element, '_getSelectedText').returns('the text'); sinon.stub(element, '_getSelectedText').returns('the text');
var event = emulateCopyOn(element.querySelector('td.content')); var event = emulateCopyOn(element.querySelector('div.contentText'));
assert.deepEqual( assert.deepEqual(
['Text', 'the text'], event.clipboardData.setData.lastCall.args); ['Text', 'the text'], event.clipboardData.setData.lastCall.args);
}); });
test('copies content correctly', function() { test('copies content correctly', function() {
element.classList.add('selected-left'); element.classList.add('selected-left');
element.classList.remove('selected-right');
var selection = window.getSelection(); var selection = window.getSelection();
var range = document.createRange(); var range = document.createRange();
range.setStart(element.querySelector('td.content').firstChild, 3); range.setStart(element.querySelector('div.contentText').firstChild, 3);
range.setEnd( range.setEnd(
element.querySelectorAll('td.content')[4].firstChild, 2); element.querySelectorAll('div.contentText')[4].firstChild, 2);
selection.addRange(range); selection.addRange(range);
assert.equal('ba\nzin\nga\n', element._getSelectedText('left')); assert.equal('ba\nzin\nga\n', element._getSelectedText('left'));
}); });

View File

@@ -91,13 +91,6 @@ limitations under the License.
vertical-align: top; vertical-align: top;
white-space: pre; white-space: pre;
} }
.contentText:empty:before {
/**
* Insert glyph to prevent empty diff content from collapsing.
* "\200B" is a 'ZERO WIDTH SPACE' (U+200B)
*/
content: "\200B";
}
.contextLineNum:before, .contextLineNum:before,
.lineNum:before { .lineNum:before {
display: inline-block; display: inline-block;