fix moving lines with folds

This commit is contained in:
nightwing 2013-02-24 01:17:56 +04:00
commit 43854d7a35
2 changed files with 91 additions and 76 deletions

2
build

@ -1 +1 @@
Subproject commit 75ba742f4f00cefadfef1ae451cfb2a4280ad367 Subproject commit 1e3407af692eaf661800083897c73d46d0a4bc46

View file

@ -3,7 +3,7 @@
* *
* Copyright (c) 2010, Ajax.org B.V. * Copyright (c) 2010, Ajax.org B.V.
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
@ -14,7 +14,7 @@
* * Neither the name of Ajax.org B.V. nor the * * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@ -44,8 +44,8 @@ var SearchHighlight = require("./search_highlight").SearchHighlight;
var config = require("./config"); var config = require("./config");
/** /**
* Stores all the data about [[Editor `Editor`]] state providing easy way to change editors state. * Stores all the data about [[Editor `Editor`]] state providing easy way to change editors state.
* *
* `EditSession` can be attached to only one [[Document `Document`]]. Same `Document` can be attached to several `EditSession`s. * `EditSession` can be attached to only one [[Document `Document`]]. Same `Document` can be attached to several `EditSession`s.
* @class EditSession * @class EditSession
**/ **/
@ -94,19 +94,19 @@ var config = require("./config");
* @param {Object} e An object containing one property, `"data"`, that contains information about the changing rows * @param {Object} e An object containing one property, `"data"`, that contains information about the changing rows
* *
**/ **/
/** /**
* Emitted when the current mode changes. * Emitted when the current mode changes.
* *
* @event changeMode * @event changeMode
* *
**/ **/
/** /**
* Emitted when the wrap mode changes. * Emitted when the wrap mode changes.
* *
* @event changeWrapMode * @event changeWrapMode
* *
**/ **/
/** /**
* Emitted when the wrapping limit changes. * Emitted when the wrapping limit changes.
* *
* @event changeWrapLimit * @event changeWrapLimit
@ -148,14 +148,14 @@ var EditSession = function(text, mode) {
this.$backMarkers = {}; this.$backMarkers = {};
this.$markerId = 1; this.$markerId = 1;
this.$undoSelect = true; this.$undoSelect = true;
this.$foldData = []; this.$foldData = [];
this.$foldData.toString = function() { this.$foldData.toString = function() {
return this.join("\n"); return this.join("\n");
} }
this.on("changeFold", this.onChangeFold.bind(this)); this.on("changeFold", this.onChangeFold.bind(this));
this.$onChange = this.onChange.bind(this); this.$onChange = this.onChange.bind(this);
if (typeof text != "object" || !text.getLine) if (typeof text != "object" || !text.getLine)
text = new Document(text); text = new Document(text);
@ -194,13 +194,13 @@ var EditSession = function(text, mode) {
/** /**
* Returns the `Document` associated with this session. * Returns the `Document` associated with this session.
* @return {Document} * @return {Document}
**/ **/
this.getDocument = function() { this.getDocument = function() {
return this.doc; return this.doc;
}; };
/** /**
* @param {Number} row The row to work with * @param {Number} row The row to work with
* *
**/ **/
@ -293,19 +293,19 @@ var EditSession = function(text, mode) {
this.getUndoManager().reset(); this.getUndoManager().reset();
}; };
/** /**
* Returns the current [[Document `Document`]] as a string. * Returns the current [[Document `Document`]] as a string.
* @method toString * @method toString
* @returns {String} * @returns {String}
* @alias EditSession.getValue * @alias EditSession.getValue
* *
**/ **/
/** /**
* Returns the current [[Document `Document`]] as a string. * Returns the current [[Document `Document`]] as a string.
* @method getValue * @method getValue
* @returns {String} * @returns {String}
* @alias EditSession.toString * @alias EditSession.toString
**/ **/
this.getValue = this.getValue =
this.toString = function() { this.toString = function() {
@ -319,7 +319,7 @@ var EditSession = function(text, mode) {
return this.selection; return this.selection;
}; };
/** /**
* {:BackgroundTokenizer.getState} * {:BackgroundTokenizer.getState}
* @param {Number} row The row to start at * @param {Number} row The row to start at
* *
@ -329,7 +329,7 @@ var EditSession = function(text, mode) {
return this.bgTokenizer.getState(row); return this.bgTokenizer.getState(row);
}; };
/** /**
* Starts tokenizing at the row indicated. Returns a list of objects of the tokenized rows. * Starts tokenizing at the row indicated. Returns a list of objects of the tokenized rows.
* @param {Number} row The row to start at * @param {Number} row The row to start at
* *
@ -483,7 +483,7 @@ var EditSession = function(text, mode) {
this.$overwrite = false; this.$overwrite = false;
/** /**
* Pass in `true` to enable overwrites in your session, or `false` to disable. * Pass in `true` to enable overwrites in your session, or `false` to disable.
* *
* If overwrites is enabled, any text you enter will type over any text after it. If the value of `overwrite` changes, this function also emites the `changeOverwrite` event. * If overwrites is enabled, any text you enter will type over any text after it. If the value of `overwrite` changes, this function also emites the `changeOverwrite` event.
* *
@ -537,7 +537,7 @@ var EditSession = function(text, mode) {
this.$decorations[row] = (this.$decorations[row] || "").replace(" " + className, ""); this.$decorations[row] = (this.$decorations[row] || "").replace(" " + className, "");
this._emit("changeBreakpoint", {}); this._emit("changeBreakpoint", {});
}; };
/** /**
* Returns an array of numbers, indicating which rows have breakpoints. * Returns an array of numbers, indicating which rows have breakpoints.
* @returns {[Number]} * @returns {[Number]}
@ -635,7 +635,7 @@ var EditSession = function(text, mode) {
* @param {Object} marker object with update method * @param {Object} marker object with update method
* @param {Boolean} inFront Set to `true` to establish a front marker * @param {Boolean} inFront Set to `true` to establish a front marker
* *
* *
* @return {Object} The added marker * @return {Object} The added marker
**/ **/
this.addDynamicMarker = function(marker, inFront) { this.addDynamicMarker = function(marker, inFront) {
@ -692,7 +692,7 @@ var EditSession = function(text, mode) {
} }
this.$searchHighlight.setRegexp(re); this.$searchHighlight.setRegexp(re);
} }
// experimental // experimental
this.highlightLines = function(startRow, endRow, clazz, inFront) { this.highlightLines = function(startRow, endRow, clazz, inFront) {
if (typeof endRow != "number") { if (typeof endRow != "number") {
@ -701,12 +701,12 @@ var EditSession = function(text, mode) {
} }
if (!clazz) if (!clazz)
clazz = "ace_step"; clazz = "ace_step";
var range = new Range(startRow, 0, endRow, Infinity); var range = new Range(startRow, 0, endRow, Infinity);
range.id = this.addMarker(range, clazz, "fullLine", inFront); range.id = this.addMarker(range, clazz, "fullLine", inFront);
return range; return range;
}; };
/* /*
* Error: * Error:
* { * {
@ -741,7 +741,7 @@ var EditSession = function(text, mode) {
this.setAnnotations([]); this.setAnnotations([]);
}; };
/** /**
* If `text` contains either the newline (`\n`) or carriage-return ('\r') characters, `$autoNewLine` stores that value. * If `text` contains either the newline (`\n`) or carriage-return ('\r') characters, `$autoNewLine` stores that value.
* @param {String} text A block of text * @param {String} text A block of text
* *
@ -772,7 +772,7 @@ var EditSession = function(text, mode) {
if (!inToken) if (!inToken)
inToken = !!line.charAt(column).match(this.tokenRe); inToken = !!line.charAt(column).match(this.tokenRe);
if (inToken) if (inToken)
var re = this.tokenRe; var re = this.tokenRe;
else if (/^\s+$/.test(line.slice(column-1, column+1))) else if (/^\s+$/.test(line.slice(column-1, column+1)))
@ -814,7 +814,7 @@ var EditSession = function(text, mode) {
return wordRange; return wordRange;
}; };
/** /**
* {:Document.setNewLineMode.desc} * {:Document.setNewLineMode.desc}
* @param {String} newLineMode {:Document.setNewLineMode.param} * @param {String} newLineMode {:Document.setNewLineMode.param}
* *
@ -825,7 +825,7 @@ var EditSession = function(text, mode) {
this.doc.setNewLineMode(newLineMode); this.doc.setNewLineMode(newLineMode);
}; };
/** /**
* *
* Returns the current new line mode. * Returns the current new line mode.
* @returns {String} * @returns {String}
@ -1061,7 +1061,7 @@ var EditSession = function(text, mode) {
} }
}; };
/** /**
* Returns a verbatim copy of the given line as it is in the document * Returns a verbatim copy of the given line as it is in the document
* @param {Number} row The row to retrieve from * @param {Number} row The row to retrieve from
* *
@ -1073,7 +1073,7 @@ var EditSession = function(text, mode) {
return this.doc.getLine(row); return this.doc.getLine(row);
}; };
/** /**
* Returns an array of strings of the rows between `firstRow` and `lastRow`. This function is inclusive of `lastRow`. * Returns an array of strings of the rows between `firstRow` and `lastRow`. This function is inclusive of `lastRow`.
* @param {Number} firstRow The first row index to retrieve * @param {Number} firstRow The first row index to retrieve
* @param {Number} lastRow The final row index to retrieve * @param {Number} lastRow The final row index to retrieve
@ -1085,7 +1085,7 @@ var EditSession = function(text, mode) {
return this.doc.getLines(firstRow, lastRow); return this.doc.getLines(firstRow, lastRow);
}; };
/** /**
* Returns the number of rows in the document. * Returns the number of rows in the document.
* @returns {Number} * @returns {Number}
**/ **/
@ -1093,7 +1093,7 @@ var EditSession = function(text, mode) {
return this.doc.getLength(); return this.doc.getLength();
}; };
/** /**
* {:Document.getTextRange.desc} * {:Document.getTextRange.desc}
* @param {Range} range The range to work with * @param {Range} range The range to work with
* *
@ -1103,7 +1103,7 @@ var EditSession = function(text, mode) {
return this.doc.getTextRange(range || this.selection.getRange()); return this.doc.getTextRange(range || this.selection.getRange());
}; };
/** /**
* Inserts a block of `text` and the indicated `position`. * Inserts a block of `text` and the indicated `position`.
* @param {Object} position The position {row, column} to start inserting at * @param {Object} position The position {row, column} to start inserting at
* @param {String} text A chunk of text to insert * @param {String} text A chunk of text to insert
@ -1115,7 +1115,7 @@ var EditSession = function(text, mode) {
return this.doc.insert(position, text); return this.doc.insert(position, text);
}; };
/** /**
* Removes the `range` from the document. * Removes the `range` from the document.
* @param {Range} range A specified Range to remove * @param {Range} range A specified Range to remove
* @returns {Object} The new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`. * @returns {Object} The new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`.
@ -1132,7 +1132,7 @@ var EditSession = function(text, mode) {
* @param {Array} deltas An array of previous changes * @param {Array} deltas An array of previous changes
* @param {Boolean} dontSelect [If `true`, doesn't select the range of where the change occured]{: #dontSelect} * @param {Boolean} dontSelect [If `true`, doesn't select the range of where the change occured]{: #dontSelect}
* *
* *
* @returns {Range} * @returns {Range}
**/ **/
this.undoChanges = function(deltas, dontSelect) { this.undoChanges = function(deltas, dontSelect) {
@ -1254,16 +1254,16 @@ var EditSession = function(text, mode) {
return range; return range;
}; };
/** /**
* Replaces a range in the document with the new `text`. * Replaces a range in the document with the new `text`.
* *
* @param {Range} range A specified Range to replace * @param {Range} range A specified Range to replace
* @param {String} text The new text to use as a replacement * @param {String} text The new text to use as a replacement
* @returns {Object} An object containing the final row and column, like this: * @returns {Object} An object containing the final row and column, like this:
* ``` * ```
* {row: endRow, column: 0} * {row: endRow, column: 0}
* ``` * ```
* If the text and range are empty, this function returns an object containing the current `range.start` value. * If the text and range are empty, this function returns an object containing the current `range.start` value.
* If the text is the exact same as what currently exists, this function returns an object containing the current `range.end` value. * If the text is the exact same as what currently exists, this function returns an object containing the current `range.end` value.
* *
* *
@ -1364,7 +1364,39 @@ var EditSession = function(text, mode) {
} }
}; };
/** this.$moveLines = function(firstRow, lastRow, dir) {
var firstRow = this.getRowFoldStart(firstRow);
var lastRow = this.getRowFoldEnd(lastRow);
if (dir < 0) {
var row = this.getRowFoldStart(firstRow + dir);
if (row < 0) return 0;
var diff = row-firstRow;
} else if (dir > 0) {
var row = this.getRowFoldEnd(lastRow + dir);
if (row > this.doc.getLength()-1) return 0;
var diff = row-lastRow;
} else {
firstRow = this.$clipRowToDocument(firstRow);
lastRow = this.$clipRowToDocument(lastRow);
var diff = lastRow - firstRow + 1;
}
var range = new Range(firstRow, 0, lastRow, Number.MAX_VALUE);
var folds = this.getFoldsInRange(range).map(function(x){
x = x.clone();
x.start.row += diff;
x.end.row += diff;
return x;
});
var lines = dir == 0
? this.doc.getLines(firstRow, lastRow)
: this.doc.removeLines(firstRow, lastRow);
this.doc.insertLines(firstRow+diff, lines);
folds.length && this.addFolds(folds);
return diff;
};
/**
* Shifts all the lines in the document up one, starting from `firstRow` and ending at `lastRow`. * Shifts all the lines in the document up one, starting from `firstRow` and ending at `lastRow`.
* @param {Number} firstRow The starting row to move up * @param {Number} firstRow The starting row to move up
* @param {Number} lastRow The final row to move up * @param {Number} lastRow The final row to move up
@ -1374,29 +1406,19 @@ var EditSession = function(text, mode) {
* *
**/ **/
this.moveLinesUp = function(firstRow, lastRow) { this.moveLinesUp = function(firstRow, lastRow) {
if (firstRow <= 0) return 0; return this.$moveLines(firstRow, lastRow, -1);
var removed = this.doc.removeLines(firstRow, lastRow);
this.doc.insertLines(firstRow - 1, removed);
return -1;
}; };
/** /**
* Shifts all the lines in the document down one, starting from `firstRow` and ending at `lastRow`. * Shifts all the lines in the document down one, starting from `firstRow` and ending at `lastRow`.
* @param {Number} firstRow The starting row to move down * @param {Number} firstRow The starting row to move down
* @param {Number} lastRow The final row to move down * @param {Number} lastRow The final row to move down
* @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1. * @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1.
* *
*
*
* @related Document.insertLines * @related Document.insertLines
**/ **/
this.moveLinesDown = function(firstRow, lastRow) { this.moveLinesDown = function(firstRow, lastRow) {
if (lastRow >= this.doc.getLength()-1) return 0; return this.$moveLines(firstRow, lastRow, 1);
var removed = this.doc.removeLines(firstRow, lastRow);
this.doc.insertLines(firstRow+1, removed);
return 1;
}; };
/** /**
@ -1408,14 +1430,7 @@ var EditSession = function(text, mode) {
* *
**/ **/
this.duplicateLines = function(firstRow, lastRow) { this.duplicateLines = function(firstRow, lastRow) {
var firstRow = this.$clipRowToDocument(firstRow); return this.$moveLines(firstRow, lastRow, 0);
var lastRow = this.$clipRowToDocument(lastRow);
var lines = this.getLines(firstRow, lastRow);
this.doc.insertLines(firstRow, lines);
var addedRows = lastRow - firstRow + 1;
return addedRows;
}; };
@ -1539,12 +1554,12 @@ var EditSession = function(text, mode) {
} }
}; };
/** /**
* This should generally only be called by the renderer when a resize is detected. * This should generally only be called by the renderer when a resize is detected.
* @param {Number} desiredLimit The new wrap limit * @param {Number} desiredLimit The new wrap limit
* @returns {Boolean} * @returns {Boolean}
* *
* *
* @private * @private
**/ **/
this.adjustWrapLimit = function(desiredLimit) { this.adjustWrapLimit = function(desiredLimit) {
@ -1993,7 +2008,7 @@ var EditSession = function(text, mode) {
} }
}; };
/** /**
* Returns the position (on screen) for the last character in the provided screen row. * Returns the position (on screen) for the last character in the provided screen row.
* @param {Number} screenRow The screen row to check * @param {Number} screenRow The screen row to check
* @returns {Number} * @returns {Number}
@ -2005,21 +2020,21 @@ var EditSession = function(text, mode) {
return this.documentToScreenColumn(pos.row, pos.column); return this.documentToScreenColumn(pos.row, pos.column);
}; };
/** /**
* For the given document row and column, this returns the column position of the last screen row. * For the given document row and column, this returns the column position of the last screen row.
* @param {Number} docRow * @param {Number} docRow
* *
* @param {Number} docColumn * @param {Number} docColumn
**/ **/
this.getDocumentLastRowColumn = function(docRow, docColumn) { this.getDocumentLastRowColumn = function(docRow, docColumn) {
var screenRow = this.documentToScreenRow(docRow, docColumn); var screenRow = this.documentToScreenRow(docRow, docColumn);
return this.getScreenLastRowColumn(screenRow); return this.getScreenLastRowColumn(screenRow);
}; };
/** /**
* For the given document row and column, this returns the document position of the last row. * For the given document row and column, this returns the document position of the last row.
* @param {Number} docRow * @param {Number} docRow
* @param {Number} docColumn * @param {Number} docColumn
* *
* *
**/ **/
@ -2028,7 +2043,7 @@ var EditSession = function(text, mode) {
return this.screenToDocumentPosition(screenRow, Number.MAX_VALUE / 10); return this.screenToDocumentPosition(screenRow, Number.MAX_VALUE / 10);
}; };
/** /**
* For the given row, this returns the split data. * For the given row, this returns the split data.
* @returns {String} * @returns {String}
**/ **/
@ -2044,7 +2059,7 @@ var EditSession = function(text, mode) {
* The distance to the next tab stop at the specified screen column. * The distance to the next tab stop at the specified screen column.
* @param {Number} screenColumn The screen column to check * @param {Number} screenColumn The screen column to check
* *
* *
* @returns {Number} * @returns {Number}
**/ **/
this.getScreenTabSize = function(screenColumn) { this.getScreenTabSize = function(screenColumn) {
@ -2061,7 +2076,7 @@ var EditSession = function(text, mode) {
return this.screenToDocumentPosition(screenRow, screenColumn).column; return this.screenToDocumentPosition(screenRow, screenColumn).column;
}; };
/** /**
* Converts characters coordinates on the screen to characters coordinates within the document. [This takes into account code folding, word wrap, tab size, and any other visual modifications.]{: #conversionConsiderations} * Converts characters coordinates on the screen to characters coordinates within the document. [This takes into account code folding, word wrap, tab size, and any other visual modifications.]{: #conversionConsiderations}
* @param {Number} screenRow The screen row to check * @param {Number} screenRow The screen row to check
* @param {Number} screenColumn The screen column to check * @param {Number} screenColumn The screen column to check
@ -2110,7 +2125,7 @@ var EditSession = function(text, mode) {
foldStart = foldLine ? foldLine.start.row : Infinity; foldStart = foldLine ? foldLine.start.row : Infinity;
} }
} }
if (doCache) { if (doCache) {
this.$docRowCache.push(docRow); this.$docRowCache.push(docRow);
this.$screenRowCache.push(row); this.$screenRowCache.push(row);
@ -2155,7 +2170,7 @@ var EditSession = function(text, mode) {
return {row: docRow, column: docColumn}; return {row: docRow, column: docColumn};
}; };
/** /**
* Converts document coordinates to screen coordinates. {:conversionConsiderations} * Converts document coordinates to screen coordinates. {:conversionConsiderations}
* @param {Number} docRow The document row to check * @param {Number} docRow The document row to check
* @param {Number} docColumn The document column to check * @param {Number} docColumn The document column to check