Overlapping selections are merged

This commit is contained in:
Corey Johnson & Nathan Sobo 2012-03-26 12:41:36 -07:00
parent 22eb9c5860
commit 8422c7ad12
7 changed files with 110 additions and 3 deletions

View File

@ -757,7 +757,7 @@ describe "Editor", ->
expect(range.end).toEqual({row: 5, column: 27})
expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27)
fdescribe "multiple cursor placement", ->
fdescribe "multiple cursors", ->
it "places multiple cursor with meta-click", ->
editor.attachToDom()
editor.lines.trigger mousedownEvent(editor: editor, point: [3, 0])
@ -919,6 +919,22 @@ describe "Editor", ->
expect(selection1.getScreenRange()).toEqual [[4, 10], [5, 27]]
expect(selection2.getScreenRange()).toEqual [[6, 10], [8, 27]]
describe "when multiple selctions intersect", ->
it "merges a selection that is completely contained within another", ->
editor.attachToDom()
editor.lines.trigger mousedownEvent(editor: editor, point: [4, 10])
editor.lines.trigger mousemoveEvent(editor: editor, point: [5, 27])
editor.lines.trigger 'mouseup'
editor.lines.trigger mousedownEvent(editor: editor, point: [3, 10], metaKey: true)
editor.lines.trigger mousemoveEvent(editor: editor, point: [6, 27], metaKey: true)
editor.lines.trigger 'mouseup'
selections = editor.compositeSelection.getSelections()
expect(selections.length).toBe 1
[selection1] = selections
expect(selection1.getScreenRange()).toEqual [[3, 10], [6, 27]]
describe "cursor merging", ->
it "merges cursors when they overlap due to a buffer change", ->
editor.setCursorScreenPosition([0, 0])

View File

@ -15,3 +15,18 @@ describe "Range", ->
expect(new Range(new Point(1, 1), new Point(1, 1)).isEmpty()).toBeTruthy()
expect(new Range(new Point(1, 1), new Point(1, 2)).isEmpty()).toBeFalsy()
describe ".intersectsWith(otherRange)", ->
it "returns true if the ranges intersect", ->
expect(new Range([1, 1], [2, 10]).intersectsWith(new Range([2, 1], [3, 10]))).toBeTruthy()
expect(new Range([2, 1], [3, 10]).intersectsWith(new Range([1, 1], [2, 10]))).toBeTruthy()
expect(new Range([2, 1], [3, 10]).intersectsWith(new Range([2, 5], [3, 1]))).toBeTruthy()
expect(new Range([2, 5], [3, 1]).intersectsWith(new Range([2, 1], [3, 10]))).toBeTruthy()
expect(new Range([2, 5], [3, 1]).intersectsWith(new Range([3, 2], [3, 10]))).toBeFalsy()
expect(new Range([3, 2], [3, 10]).intersectsWith(new Range([2, 5], [3, 1]))).toBeFalsy()
describe ".union(otherRange)", ->
it "returns the union of the two ranges", ->
expect(new Range([1, 1], [2, 10]).union(new Range([2, 1], [3, 10]))).toEqual [[1, 1], [3, 10]]
expect(new Range([2, 1], [3, 10]).union(new Range([1, 1], [2, 10]))).toEqual [[1, 1], [3, 10]]
expect(new Range([2, 1], [3, 10]).union(new Range([2, 5], [3, 1]))).toEqual [[2, 1], [3, 10]]
expect(new Range([2, 5], [3, 1]).union(new Range([2, 1], [3, 10]))).toEqual [[2, 1], [3, 10]]

42
spec/atom/range-spec.js Normal file
View File

@ -0,0 +1,42 @@
(function() {
var Point, Range;
Range = require('range');
Point = require('point');
describe("Range", function() {
describe("constructor", function() {
return it("ensures that @start <= @end", function() {
var range1, range2;
range1 = new Range(new Point(0, 1), new Point(0, 4));
expect(range1.start).toEqual({
row: 0,
column: 1
});
range2 = new Range(new Point(1, 4), new Point(0, 1));
return expect(range2.start).toEqual({
row: 0,
column: 1
});
});
});
describe(".isEmpty()", function() {
return it("returns true if @start equals @end", function() {
expect(new Range(new Point(1, 1), new Point(1, 1)).isEmpty()).toBeTruthy();
return expect(new Range(new Point(1, 1), new Point(1, 2)).isEmpty()).toBeFalsy();
});
});
return describe(".intersectsWith(otherRange)", function() {
return fit("returns the intersection of the two ranges", function() {
var range1, range2;
range1 = new Range([1, 1], [2, 10]);
range2 = new Range([2, 1], [3, 10]);
expect(range1.intersectsWith(range2)).toBeTruth;
range2 = range1 = new Range([2, 1], [3, 10]);
return expect(range1.intersectsWith(range2)).toBeTruth;
});
});
});
}).call(this);

View File

@ -27,4 +27,14 @@ class CompositeSeleciton
selection.backspace()
selectToScreenPosition: (position) ->
_.last(@selections).selectToScreenPosition(position)
_.last(@selections).selectToScreenPosition(position)
mergeIntersectingSelections: ->
for selection in @getSelections()
otherSelections = @getSelections()
_.remove(otherSelections, selection)
for otherSelection in otherSelections
if selection.intersectsWith(otherSelection)
selection.merge(otherSelection)
@mergeIntersectingSelections()
return

View File

@ -166,7 +166,9 @@ class Editor extends View
selectOnMousemoveUntilMouseup: ->
moveHandler = (e) => @selectToScreenPosition(@screenPositionFromMouseEvent(e))
@on 'mousemove', moveHandler
$(document).one 'mouseup', => @off 'mousemove', moveHandler
$(document).one 'mouseup', =>
@off 'mousemove', moveHandler
@compositeSelection.mergeIntersectingSelections()
renderLines: ->
@lineCache = []

View File

@ -40,6 +40,17 @@ class Range
inspect: ->
"[#{@start.inspect()} - #{@end.inspect()}]"
intersectsWith: (otherRange) ->
if @start.isLessThanOrEqual(otherRange.start)
@end.isGreaterThan(otherRange.start)
else
otherRange.intersectsWith(this)
union: (otherRange) ->
start = if @start.isLessThan(otherRange.start) then @start else otherRange.start
end = if @end.isGreaterThan(otherRange.end) then @end else otherRange.end
new Range(start, end)
isEmpty: ->
@start.isEqual(@end)

View File

@ -92,6 +92,17 @@ class Selection extends View
isEmpty: ->
@getBufferRange().isEmpty()
intersectsWith: (otherSelection) ->
@getScreenRange().intersectsWith(otherSelection.getScreenRange())
merge: (otherSelection) ->
@setScreenRange(@getScreenRange().union(otherSelection.getScreenRange()))
otherSelection.remove()
remove: ->
@cursor?.remove()
super
modifySelection: (fn) ->
@placeAnchor()
@modifyingSelection = true