mirror of
https://github.com/paradoxxxzero/butterfly.git
synced 2026-06-06 20:39:41 +00:00
Greatly improve selection
This commit is contained in:
@@ -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) ->
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user