Greatly improve selection

This commit is contained in:
Florian Mounier
2014-02-24 11:06:29 +01:00
parent 4aa5f75e4f
commit 459b256ddc
2 changed files with 217 additions and 112 deletions

View File

@@ -24,9 +24,30 @@ selection = null
# range.setStart first_node, 0
# range.setEnd last_node, last_node.length
# range
previous_leaf = (node) ->
previous = node.previousSibling
if not previous
previous = node.parentNode.previousSibling
if not previous
previous = node.parentNode.parentNode.previousSibling.firstChild
while previous.lastChild
previous = previous.lastChild
previous
next_leaf = (node) ->
next = node.nextSibling
if not next
next = node.parentNode.nextSibling
if not next
next = node.parentNode.parentNode.nextSibling
while next.firstChild
next = next.firstChild
next
class Selection
constructor: ->
@reset()
@selection = getSelection()
reset: ->
@selection = getSelection()
@@ -35,14 +56,22 @@ class Selection
fake_range.setEnd(@selection.focusNode, @selection.focusOffset)
@start =
node: @selection.anchorNode
off: @selection.anchorOffset
offset: @selection.anchorOffset
@end =
node: @selection.focusNode
off: @selection.focusOffset
offset: @selection.focusOffset
if fake_range.collapsed
[@start, @end] = [@end, @start]
@start_line = @start.node
while not @start_line.classList or 'line' not in @start_line.classList
@start_line = @start_line.parentNode
@end_line = @end.node
while not @end_line.classList or 'line' not in @end_line.classList
@end_line = @end_line.parentNode
clear: ->
@selection.removeAllRanges()
@@ -56,50 +85,60 @@ class Selection
@go +1
go: (n) ->
index = term.children.indexOf @get_selected_line()
index = term.children.indexOf(@start_line) + n
if 0 <= index < term.children.length
@select_line index
if 0 <= index + n < term.children.length
@clear()
@selection.addRange get_line_range(index + n)
apply: ->
@clear()
range = document.createRange()
range.setStart @start.node, @start.offset
range.setEnd @end.node, @end.offset
@selection.addRange range
get_selected_line: ->
node = @start.node
while not node.classList or 'line' not in node.classList
node = node.parentNode
node
select_line: (y) ->
line = term.children[y]
line_start =
node: line.firstChild
offset: 0
line_end =
node: line.lastChild
offset: line.lastChild.textContent.length
nextLeaf = (node) ->
next = node.nextSibling
if not next
next = node.parentNode.nextSibling
if not next
next = node.parentNode.parentNode.nextSibling.firstChild
next
@start = @walk line_start, /\S/
@end = @walk line_end, /\S/, true
find_node_offset = (line, backward=false) ->
step = if backward then -1 else 1
shrink_right: ->
node = @walk @end, /\s/, true
@end = @walk node, /\S/, true
for node in line.childNodes by step
if node.nodeType != node.TEXT_NODE
node = node.firstChild
for c, offset in node.textContent by step
if not c.match /\s/
return [node, offset + if backward then 1 else 0]
return [line.firstChild, 0]
shrink_left: ->
node = @walk @start, /\s/
@start = @walk node, /\S/
get_line_range = (y) ->
line = term.children[y]
range = document.createRange()
range.setStart.apply range, find_node_offset(line)
range.setEnd.apply range, find_node_offset(line, true)
range
walk: (needle, til, backward=false) ->
node = if needle.node.firstChild then needle.node.firstChild else needle.node
text = node.textContent
i = needle.offset
if backward
while node
while i > 0
if text[--i].match til
return node: node, offset: i + 1
node = previous_leaf node
text = node.textContent
i = text.length
else
while node
while i < text.length
if text[i++].match til
return node: node, offset: i - 1
node = next_leaf node
text = node.textContent
i = 0
sel_to_line = (y) ->
selection = getSelection()
selection.removeAllRanges()
selection.addRange get_line_range(y)
return needle
document.addEventListener 'keydown', (e) ->
@@ -110,11 +149,12 @@ document.addEventListener 'keydown', (e) ->
if e.shiftKey and e.ctrlKey
if e.keyCode == 38
selection.up()
return cancel e
else if e.keyCode == 40
selection.down()
return cancel e
else if e.keyCode == 39
selection.shrink_left()
else if e.keyCode == 37
selection.shrink_right()
else if e.keyCode == 13
term.handler selection.text()
selection.clear()
@@ -123,12 +163,15 @@ document.addEventListener 'keydown', (e) ->
selection.clear()
selection = null
return true
selection?.apply()
return cancel e
# Start selection mode with shift up
if not selection and e.ctrlKey and e.shiftKey and e.keyCode == 38
sel_to_line term.y - 1
selection = new Selection()
selection.select_line term.y - 1
selection.apply()
return cancel e
document.addEventListener 'dblclick', (e) ->

View File

@@ -1,5 +1,5 @@
// Generated by CoffeeScript 1.6.3
var $, Selection, State, Terminal, alt, bench, cancel, cbench, cols, ctl, ctrl, find_node_offset, first, get_line_range, nextLeaf, quit, rows, s, sel_to_line, selection, send, term, virtual_input, ws, ws_url,
var $, Selection, State, Terminal, alt, bench, cancel, cbench, cols, ctl, ctrl, first, next_leaf, previous_leaf, quit, rows, s, selection, send, term, virtual_input, ws, ws_url,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__slice = [].slice;
@@ -2414,28 +2414,68 @@ Terminal = (function() {
selection = null;
previous_leaf = function(node) {
var previous;
previous = node.previousSibling;
if (!previous) {
previous = node.parentNode.previousSibling;
}
if (!previous) {
previous = node.parentNode.parentNode.previousSibling.firstChild;
}
while (previous.lastChild) {
previous = previous.lastChild;
}
return previous;
};
next_leaf = function(node) {
var next;
next = node.nextSibling;
if (!next) {
next = node.parentNode.nextSibling;
}
if (!next) {
next = node.parentNode.parentNode.nextSibling;
}
while (next.firstChild) {
next = next.firstChild;
}
return next;
};
Selection = (function() {
function Selection() {
this.reset();
this.selection = getSelection();
}
Selection.prototype.reset = function() {
var fake_range, _ref;
var fake_range, _ref, _results;
this.selection = getSelection();
fake_range = document.createRange();
fake_range.setStart(this.selection.anchorNode, this.selection.anchorOffset);
fake_range.setEnd(this.selection.focusNode, this.selection.focusOffset);
this.start = {
node: this.selection.anchorNode,
off: this.selection.anchorOffset
offset: this.selection.anchorOffset
};
this.end = {
node: this.selection.focusNode,
off: this.selection.focusOffset
offset: this.selection.focusOffset
};
if (fake_range.collapsed) {
return _ref = [this.end, this.start], this.start = _ref[0], this.end = _ref[1], _ref;
_ref = [this.end, this.start], this.start = _ref[0], this.end = _ref[1];
}
this.start_line = this.start.node;
while (!this.start_line.classList || __indexOf.call(this.start_line.classList, 'line') < 0) {
this.start_line = this.start_line.parentNode;
}
this.end_line = this.end.node;
_results = [];
while (!this.end_line.classList || __indexOf.call(this.end_line.classList, 'line') < 0) {
_results.push(this.end_line = this.end_line.parentNode);
}
return _results;
};
Selection.prototype.clear = function() {
@@ -2455,77 +2495,93 @@ Selection = (function() {
};
Selection.prototype.go = function(n) {
var index, _ref;
index = term.children.indexOf(this.get_selected_line());
if ((0 <= (_ref = index + n) && _ref < term.children.length)) {
this.clear();
return this.selection.addRange(get_line_range(index + n));
var index;
index = term.children.indexOf(this.start_line) + n;
if ((0 <= index && index < term.children.length)) {
return this.select_line(index);
}
};
Selection.prototype.get_selected_line = function() {
Selection.prototype.apply = function() {
var range;
this.clear();
range = document.createRange();
range.setStart(this.start.node, this.start.offset);
range.setEnd(this.end.node, this.end.offset);
return this.selection.addRange(range);
};
Selection.prototype.select_line = function(y) {
var line, line_end, line_start;
line = term.children[y];
line_start = {
node: line.firstChild,
offset: 0
};
line_end = {
node: line.lastChild,
offset: line.lastChild.textContent.length
};
this.start = this.walk(line_start, /\S/);
return this.end = this.walk(line_end, /\S/, true);
};
Selection.prototype.shrink_right = function() {
var node;
node = this.start.node;
while (!node.classList || __indexOf.call(node.classList, 'line') < 0) {
node = node.parentNode;
node = this.walk(this.end, /\s/, true);
return this.end = this.walk(node, /\S/, true);
};
Selection.prototype.shrink_left = function() {
var node;
node = this.walk(this.start, /\s/);
return this.start = this.walk(node, /\S/);
};
Selection.prototype.walk = function(needle, til, backward) {
var i, node, text;
if (backward == null) {
backward = false;
}
return node;
node = needle.node.firstChild ? needle.node.firstChild : needle.node;
text = node.textContent;
i = needle.offset;
if (backward) {
while (node) {
while (i > 0) {
if (text[--i].match(til)) {
return {
node: node,
offset: i + 1
};
}
}
node = previous_leaf(node);
text = node.textContent;
i = text.length;
}
} else {
while (node) {
while (i < text.length) {
if (text[i++].match(til)) {
return {
node: node,
offset: i - 1
};
}
}
node = next_leaf(node);
text = node.textContent;
i = 0;
}
}
return needle;
};
return Selection;
})();
nextLeaf = function(node) {
var next;
next = node.nextSibling;
if (!next) {
next = node.parentNode.nextSibling;
}
if (!next) {
next = node.parentNode.parentNode.nextSibling.firstChild;
}
return next;
};
find_node_offset = function(line, backward) {
var c, node, offset, step, _i, _j, _len, _len1, _ref, _ref1;
if (backward == null) {
backward = false;
}
step = backward ? -1 : 1;
_ref = line.childNodes;
for ((step > 0 ? (_i = 0, _len = _ref.length) : _i = _ref.length - 1); step > 0 ? _i < _len : _i >= 0; _i += step) {
node = _ref[_i];
if (node.nodeType !== node.TEXT_NODE) {
node = node.firstChild;
}
_ref1 = node.textContent;
for ((step > 0 ? (offset = _j = 0, _len1 = _ref1.length) : offset = _j = _ref1.length - 1); step > 0 ? _j < _len1 : _j >= 0; offset = _j += step) {
c = _ref1[offset];
if (!c.match(/\s/)) {
return [node, offset + (backward ? 1 : 0)];
}
}
}
return [line.firstChild, 0];
};
get_line_range = function(y) {
var line, range;
line = term.children[y];
range = document.createRange();
range.setStart.apply(range, find_node_offset(line));
range.setEnd.apply(range, find_node_offset(line, true));
return range;
};
sel_to_line = function(y) {
selection = getSelection();
selection.removeAllRanges();
return selection.addRange(get_line_range(y));
};
document.addEventListener('keydown', function(e) {
var _ref;
if (selection) {
@@ -2536,11 +2592,13 @@ document.addEventListener('keydown', function(e) {
if (e.shiftKey && e.ctrlKey) {
if (e.keyCode === 38) {
selection.up();
return cancel(e);
} else if (e.keyCode === 40) {
selection.down();
return cancel(e);
}
} else if (e.keyCode === 39) {
selection.shrink_left();
} else if (e.keyCode === 37) {
selection.shrink_right();
} else if (e.keyCode === 13) {
term.handler(selection.text());
selection.clear();
@@ -2550,11 +2608,15 @@ document.addEventListener('keydown', function(e) {
selection = null;
return true;
}
if (selection != null) {
selection.apply();
}
return cancel(e);
}
if (!selection && e.ctrlKey && e.shiftKey && e.keyCode === 38) {
sel_to_line(term.y - 1);
selection = new Selection();
selection.select_line(term.y - 1);
selection.apply();
return cancel(e);
}
});