Bug 734432 - Add Orion ProjectionTextModel to our package; r=rcampbell

This commit is contained in:
Mihai Sucan 2012-03-11 13:25:19 +02:00
parent 16b0690be6
commit a497d27e07
6 changed files with 598 additions and 7 deletions

View File

@ -24,6 +24,7 @@
- Rob Campbell <robcee@mozilla.com> (original author)
- Mihai Sucan <mihai.sucan@gmail.com>
- Erik Vold <erikvvold@gmail.com>
- Mark Capella <markcapella@twcny.rr.com>
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or

View File

@ -53,6 +53,7 @@ copy({
ORION_EDITOR + "/orion/textview/rulers.js",
ORION_EDITOR + "/orion/textview/undoStack.js",
ORION_EDITOR + "/orion/textview/textModel.js",
ORION_EDITOR + "/orion/textview/projectionTextModel.js",
ORION_EDITOR + "/orion/textview/tooltip.js",
ORION_EDITOR + "/orion/textview/textView.js",
ORION_EDITOR + "/orion/textview/textDND.js",

View File

@ -22,6 +22,10 @@ Orion version: git clone from 2012-01-26
http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=27177e9a3dc70c20b4877e3eab3adfff1d56e342
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=370606
+ patch for Mozilla Bug 730532 - remove CSS2Properties aliases for MozOpacity
and MozOutline*
see https://bugzilla.mozilla.org/show_bug.cgi?id=730532#c3
# License
The following files are licensed according to the contents in the LICENSE

View File

@ -2563,6 +2563,590 @@ define("orion/textview/textModel", ['orion/textview/eventTarget'], function(mEve
return {TextModel: TextModel};
});/*******************************************************************************
* @license
* Copyright (c) 2010, 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
* License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
*
* Contributors:
* Felipe Heidrich (IBM Corporation) - initial API and implementation
* Silenio Quarti (IBM Corporation) - initial API and implementation
******************************************************************************/
/*global define */
define("orion/textview/projectionTextModel", ['orion/textview/textModel', 'orion/textview/eventTarget'], function(mTextModel, mEventTarget) {
/**
* @class This object represents a projection range. A projection specifies a
* range of text and the replacement text. The range of text is relative to the
* base text model associated to a projection model.
* <p>
* <b>See:</b><br/>
* {@link orion.textview.ProjectionTextModel}<br/>
* {@link orion.textview.ProjectionTextModel#addProjection}<br/>
* </p>
* @name orion.textview.Projection
*
* @property {Number} start The start offset of the projection range.
* @property {Number} end The end offset of the projection range. This offset is exclusive.
* @property {String|orion.textview.TextModel} [text=""] The projection text to be inserted
*/
/**
* Constructs a new <code>ProjectionTextModel</code> based on the specified <code>TextModel</code>.
*
* @param {orion.textview.TextModel} baseModel The base text model.
*
* @name orion.textview.ProjectionTextModel
* @class The <code>ProjectionTextModel</code> represents a projection of its base text
* model. Projection ranges can be added to the projection text model to hide and/or insert
* ranges to the base text model.
* <p>
* The contents of the projection text model is modified when changes occur in the base model,
* projection model or by calls to {@link #addProjection} and {@link #removeProjection}.
* </p>
* <p>
* <b>See:</b><br/>
* {@link orion.textview.TextView}<br/>
* {@link orion.textview.TextModel}
* {@link orion.textview.TextView#setModel}
* </p>
* @borrows orion.textview.EventTarget#addEventListener as #addEventListener
* @borrows orion.textview.EventTarget#removeEventListener as #removeEventListener
* @borrows orion.textview.EventTarget#dispatchEvent as #dispatchEvent
*/
function ProjectionTextModel(baseModel) {
this._model = baseModel; /* Base Model */
this._projections = [];
}
ProjectionTextModel.prototype = /** @lends orion.textview.ProjectionTextModel.prototype */ {
/**
* Adds a projection range to the model.
* <p>
* The model must notify the listeners before and after the the text is
* changed by calling {@link #onChanging} and {@link #onChanged} respectively.
* </p>
* @param {orion.textview.Projection} projection The projection range to be added.
*
* @see #removeProjection
*/
addProjection: function(projection) {
if (!projection) {return;}
//start and end can't overlap any exist projection
var model = this._model, projections = this._projections;
projection._lineIndex = model.getLineAtOffset(projection.start);
projection._lineCount = model.getLineAtOffset(projection.end) - projection._lineIndex;
var text = projection.text;
if (!text) { text = ""; }
if (typeof text === "string") {
projection._model = new mTextModel.TextModel(text, model.getLineDelimiter());
} else {
projection._model = text;
}
var eventStart = this.mapOffset(projection.start, true);
var removedCharCount = projection.end - projection.start;
var removedLineCount = projection._lineCount;
var addedCharCount = projection._model.getCharCount();
var addedLineCount = projection._model.getLineCount() - 1;
var modelChangingEvent = {
type: "Changing",
text: projection._model.getText(),
start: eventStart,
removedCharCount: removedCharCount,
addedCharCount: addedCharCount,
removedLineCount: removedLineCount,
addedLineCount: addedLineCount
};
this.onChanging(modelChangingEvent);
var index = this._binarySearch(projections, projection.start);
projections.splice(index, 0, projection);
var modelChangedEvent = {
type: "Changed",
start: eventStart,
removedCharCount: removedCharCount,
addedCharCount: addedCharCount,
removedLineCount: removedLineCount,
addedLineCount: addedLineCount
};
this.onChanged(modelChangedEvent);
},
/**
* Returns all projection ranges of this model.
*
* @return {orion.textview.Projection[]} The projection ranges.
*
* @see #addProjection
*/
getProjections: function() {
return this._projections.slice(0);
},
/**
* Gets the base text model.
*
* @return {orion.textview.TextModel} The base text model.
*/
getBaseModel: function() {
return this._model;
},
/**
* Maps offsets between the projection model and its base model.
*
* @param {Number} offset The offset to be mapped.
* @param {Boolean} [baseOffset=false] <code>true</code> if <code>offset</code> is in base model and
* should be mapped to the projection model.
* @return {Number} The mapped offset
*/
mapOffset: function(offset, baseOffset) {
var projections = this._projections, delta = 0, i, projection;
if (baseOffset) {
for (i = 0; i < projections.length; i++) {
projection = projections[i];
if (projection.start > offset) { break; }
if (projection.end > offset) { return -1; }
delta += projection._model.getCharCount() - (projection.end - projection.start);
}
return offset + delta;
}
for (i = 0; i < projections.length; i++) {
projection = projections[i];
if (projection.start > offset - delta) { break; }
var charCount = projection._model.getCharCount();
if (projection.start + charCount > offset - delta) {
return -1;
}
delta += charCount - (projection.end - projection.start);
}
return offset - delta;
},
/**
* Removes a projection range from the model.
* <p>
* The model must notify the listeners before and after the the text is
* changed by calling {@link #onChanging} and {@link #onChanged} respectively.
* </p>
*
* @param {orion.textview.Projection} projection The projection range to be removed.
*
* @see #addProjection
*/
removeProjection: function(projection) {
//TODO remove listeners from model
var i, delta = 0;
for (i = 0; i < this._projections.length; i++) {
var p = this._projections[i];
if (p === projection) {
projection = p;
break;
}
delta += p._model.getCharCount() - (p.end - p.start);
}
if (i < this._projections.length) {
var model = this._model;
var eventStart = projection.start + delta;
var addedCharCount = projection.end - projection.start;
var addedLineCount = projection._lineCount;
var removedCharCount = projection._model.getCharCount();
var removedLineCount = projection._model.getLineCount() - 1;
var modelChangingEvent = {
type: "Changing",
text: model.getText(projection.start, projection.end),
start: eventStart,
removedCharCount: removedCharCount,
addedCharCount: addedCharCount,
removedLineCount: removedLineCount,
addedLineCount: addedLineCount
};
this.onChanging(modelChangingEvent);
this._projections.splice(i, 1);
var modelChangedEvent = {
type: "Changed",
start: eventStart,
removedCharCount: removedCharCount,
addedCharCount: addedCharCount,
removedLineCount: removedLineCount,
addedLineCount: addedLineCount
};
this.onChanged(modelChangedEvent);
}
},
/** @ignore */
_binarySearch: function (array, offset) {
var high = array.length, low = -1, index;
while (high - low > 1) {
index = Math.floor((high + low) / 2);
if (offset <= array[index].start) {
high = index;
} else {
low = index;
}
}
return high;
},
/**
* @see orion.textview.TextModel#getCharCount
*/
getCharCount: function() {
var count = this._model.getCharCount(), projections = this._projections;
for (var i = 0; i < projections.length; i++) {
var projection = projections[i];
count += projection._model.getCharCount() - (projection.end - projection.start);
}
return count;
},
/**
* @see orion.textview.TextModel#getLine
*/
getLine: function(lineIndex, includeDelimiter) {
if (lineIndex < 0) { return null; }
var model = this._model, projections = this._projections;
var delta = 0, result = [], offset = 0, i, lineCount, projection;
for (i = 0; i < projections.length; i++) {
projection = projections[i];
if (projection._lineIndex >= lineIndex - delta) { break; }
lineCount = projection._model.getLineCount() - 1;
if (projection._lineIndex + lineCount >= lineIndex - delta) {
var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
if (projectionLineIndex < lineCount) {
return projection._model.getLine(projectionLineIndex, includeDelimiter);
} else {
result.push(projection._model.getLine(lineCount));
}
}
offset = projection.end;
delta += lineCount - projection._lineCount;
}
offset = Math.max(offset, model.getLineStart(lineIndex - delta));
for (; i < projections.length; i++) {
projection = projections[i];
if (projection._lineIndex > lineIndex - delta) { break; }
result.push(model.getText(offset, projection.start));
lineCount = projection._model.getLineCount() - 1;
if (projection._lineIndex + lineCount > lineIndex - delta) {
result.push(projection._model.getLine(0, includeDelimiter));
return result.join("");
}
result.push(projection._model.getText());
offset = projection.end;
delta += lineCount - projection._lineCount;
}
var end = model.getLineEnd(lineIndex - delta, includeDelimiter);
if (offset < end) {
result.push(model.getText(offset, end));
}
return result.join("");
},
/**
* @see orion.textview.TextModel#getLineAtOffset
*/
getLineAtOffset: function(offset) {
var model = this._model, projections = this._projections;
var delta = 0, lineDelta = 0;
for (var i = 0; i < projections.length; i++) {
var projection = projections[i];
if (projection.start > offset - delta) { break; }
var charCount = projection._model.getCharCount();
if (projection.start + charCount > offset - delta) {
var projectionOffset = offset - (projection.start + delta);
lineDelta += projection._model.getLineAtOffset(projectionOffset);
delta += projectionOffset;
break;
}
lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
delta += charCount - (projection.end - projection.start);
}
return model.getLineAtOffset(offset - delta) + lineDelta;
},
/**
* @see orion.textview.TextModel#getLineCount
*/
getLineCount: function() {
var model = this._model, projections = this._projections;
var count = model.getLineCount();
for (var i = 0; i < projections.length; i++) {
var projection = projections[i];
count += projection._model.getLineCount() - 1 - projection._lineCount;
}
return count;
},
/**
* @see orion.textview.TextModel#getLineDelimiter
*/
getLineDelimiter: function() {
return this._model.getLineDelimiter();
},
/**
* @see orion.textview.TextModel#getLineEnd
*/
getLineEnd: function(lineIndex, includeDelimiter) {
if (lineIndex < 0) { return -1; }
var model = this._model, projections = this._projections;
var delta = 0, offsetDelta = 0;
for (var i = 0; i < projections.length; i++) {
var projection = projections[i];
if (projection._lineIndex > lineIndex - delta) { break; }
var lineCount = projection._model.getLineCount() - 1;
if (projection._lineIndex + lineCount > lineIndex - delta) {
var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
return projection._model.getLineEnd (projectionLineIndex, includeDelimiter) + projection.start + offsetDelta;
}
offsetDelta += projection._model.getCharCount() - (projection.end - projection.start);
delta += lineCount - projection._lineCount;
}
return model.getLineEnd(lineIndex - delta, includeDelimiter) + offsetDelta;
},
/**
* @see orion.textview.TextModel#getLineStart
*/
getLineStart: function(lineIndex) {
if (lineIndex < 0) { return -1; }
var model = this._model, projections = this._projections;
var delta = 0, offsetDelta = 0;
for (var i = 0; i < projections.length; i++) {
var projection = projections[i];
if (projection._lineIndex >= lineIndex - delta) { break; }
var lineCount = projection._model.getLineCount() - 1;
if (projection._lineIndex + lineCount >= lineIndex - delta) {
var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
return projection._model.getLineStart (projectionLineIndex) + projection.start + offsetDelta;
}
offsetDelta += projection._model.getCharCount() - (projection.end - projection.start);
delta += lineCount - projection._lineCount;
}
return model.getLineStart(lineIndex - delta) + offsetDelta;
},
/**
* @see orion.textview.TextModel#getText
*/
getText: function(start, end) {
if (start === undefined) { start = 0; }
var model = this._model, projections = this._projections;
var delta = 0, result = [], i, projection, charCount;
for (i = 0; i < projections.length; i++) {
projection = projections[i];
if (projection.start > start - delta) { break; }
charCount = projection._model.getCharCount();
if (projection.start + charCount > start - delta) {
if (end !== undefined && projection.start + charCount > end - delta) {
return projection._model.getText(start - (projection.start + delta), end - (projection.start + delta));
} else {
result.push(projection._model.getText(start - (projection.start + delta)));
start = projection.end + delta + charCount - (projection.end - projection.start);
}
}
delta += charCount - (projection.end - projection.start);
}
var offset = start - delta;
if (end !== undefined) {
for (; i < projections.length; i++) {
projection = projections[i];
if (projection.start > end - delta) { break; }
result.push(model.getText(offset, projection.start));
charCount = projection._model.getCharCount();
if (projection.start + charCount > end - delta) {
result.push(projection._model.getText(0, end - (projection.start + delta)));
return result.join("");
}
result.push(projection._model.getText());
offset = projection.end;
delta += charCount - (projection.end - projection.start);
}
result.push(model.getText(offset, end - delta));
} else {
for (; i < projections.length; i++) {
projection = projections[i];
result.push(model.getText(offset, projection.start));
result.push(projection._model.getText());
offset = projection.end;
}
result.push(model.getText(offset));
}
return result.join("");
},
/** @ignore */
_onChanging: function(text, start, removedCharCount, addedCharCount, removedLineCount, addedLineCount) {
var model = this._model, projections = this._projections, i, projection, delta = 0, lineDelta;
var end = start + removedCharCount;
for (; i < projections.length; i++) {
projection = projections[i];
if (projection.start > start) { break; }
delta += projection._model.getCharCount() - (projection.end - projection.start);
}
/*TODO add stuff saved by setText*/
var mapStart = start + delta, rangeStart = i;
for (; i < projections.length; i++) {
projection = projections[i];
if (projection.start > end) { break; }
delta += projection._model.getCharCount() - (projection.end - projection.start);
lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
}
/*TODO add stuff saved by setText*/
var mapEnd = end + delta, rangeEnd = i;
this.onChanging(mapStart, mapEnd - mapStart, addedCharCount/*TODO add stuff saved by setText*/, removedLineCount + lineDelta/*TODO add stuff saved by setText*/, addedLineCount/*TODO add stuff saved by setText*/);
projections.splice(projections, rangeEnd - rangeStart);
var count = text.length - (mapEnd - mapStart);
for (; i < projections.length; i++) {
projection = projections[i];
projection.start += count;
projection.end += count;
projection._lineIndex = model.getLineAtOffset(projection.start);
}
},
/**
* @see orion.textview.TextModel#onChanging
*/
onChanging: function(modelChangingEvent) {
return this.dispatchEvent(modelChangingEvent);
},
/**
* @see orion.textview.TextModel#onChanged
*/
onChanged: function(modelChangedEvent) {
return this.dispatchEvent(modelChangedEvent);
},
/**
* @see orion.textview.TextModel#setLineDelimiter
*/
setLineDelimiter: function(lineDelimiter) {
this._model.setLineDelimiter(lineDelimiter);
},
/**
* @see orion.textview.TextModel#setText
*/
setText: function(text, start, end) {
if (text === undefined) { text = ""; }
if (start === undefined) { start = 0; }
var eventStart = start, eventEnd = end;
var model = this._model, projections = this._projections;
var delta = 0, lineDelta = 0, i, projection, charCount, startProjection, endProjection, startLineDelta = 0;
for (i = 0; i < projections.length; i++) {
projection = projections[i];
if (projection.start > start - delta) { break; }
charCount = projection._model.getCharCount();
if (projection.start + charCount > start - delta) {
if (end !== undefined && projection.start + charCount > end - delta) {
projection._model.setText(text, start - (projection.start + delta), end - (projection.start + delta));
//TODO events - special case
return;
} else {
startLineDelta = projection._model.getLineCount() - 1 - projection._model.getLineAtOffset(start - (projection.start + delta));
startProjection = {
projection: projection,
start: start - (projection.start + delta)
};
start = projection.end + delta + charCount - (projection.end - projection.start);
}
}
lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
delta += charCount - (projection.end - projection.start);
}
var mapStart = start - delta, rangeStart = i, startLine = model.getLineAtOffset(mapStart) + lineDelta - startLineDelta;
if (end !== undefined) {
for (; i < projections.length; i++) {
projection = projections[i];
if (projection.start > end - delta) { break; }
charCount = projection._model.getCharCount();
if (projection.start + charCount > end - delta) {
lineDelta += projection._model.getLineAtOffset(end - (projection.start + delta));
charCount = end - (projection.start + delta);
end = projection.end + delta;
endProjection = {
projection: projection,
end: charCount
};
break;
}
lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
delta += charCount - (projection.end - projection.start);
}
} else {
for (; i < projections.length; i++) {
projection = projections[i];
lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
delta += projection._model.getCharCount() - (projection.end - projection.start);
}
end = eventEnd = model.getCharCount() + delta;
}
var mapEnd = end - delta, rangeEnd = i, endLine = model.getLineAtOffset(mapEnd) + lineDelta;
//events
var removedCharCount = eventEnd - eventStart;
var removedLineCount = endLine - startLine;
var addedCharCount = text.length;
var addedLineCount = 0;
var cr = 0, lf = 0, index = 0;
while (true) {
if (cr !== -1 && cr <= index) { cr = text.indexOf("\r", index); }
if (lf !== -1 && lf <= index) { lf = text.indexOf("\n", index); }
if (lf === -1 && cr === -1) { break; }
if (cr !== -1 && lf !== -1) {
if (cr + 1 === lf) {
index = lf + 1;
} else {
index = (cr < lf ? cr : lf) + 1;
}
} else if (cr !== -1) {
index = cr + 1;
} else {
index = lf + 1;
}
addedLineCount++;
}
var modelChangingEvent = {
type: "Changing",
text: text,
start: eventStart,
removedCharCount: removedCharCount,
addedCharCount: addedCharCount,
removedLineCount: removedLineCount,
addedLineCount: addedLineCount
};
this.onChanging(modelChangingEvent);
// var changeLineCount = model.getLineAtOffset(mapEnd) - model.getLineAtOffset(mapStart) + addedLineCount;
model.setText(text, mapStart, mapEnd);
if (startProjection) {
projection = startProjection.projection;
projection._model.setText("", startProjection.start);
}
if (endProjection) {
projection = endProjection.projection;
projection._model.setText("", 0, endProjection.end);
projection.start = projection.end;
projection._lineCount = 0;
}
projections.splice(rangeStart, rangeEnd - rangeStart);
var changeCount = text.length - (mapEnd - mapStart);
for (i = rangeEnd; i < projections.length; i++) {
projection = projections[i];
projection.start += changeCount;
projection.end += changeCount;
// if (projection._lineIndex + changeLineCount !== model.getLineAtOffset(projection.start)) {
// log("here");
// }
projection._lineIndex = model.getLineAtOffset(projection.start);
// projection._lineIndex += changeLineCount;
}
var modelChangedEvent = {
type: "Changed",
start: eventStart,
removedCharCount: removedCharCount,
addedCharCount: addedCharCount,
removedLineCount: removedLineCount,
addedLineCount: addedLineCount
};
this.onChanged(modelChangedEvent);
}
};
mEventTarget.EventTarget.addMixin(ProjectionTextModel.prototype);
return {ProjectionTextModel: ProjectionTextModel};
});
/*******************************************************************************
* @license
* Copyright (c) 2010, 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials are made

View File

@ -908,7 +908,8 @@ SourceEditor.prototype = {
*
* @private
* @param string aType
* The annotation type to filter annotations for.
* The annotation type to filter annotations for. Use one of the keys
* in ORION_ANNOTATION_TYPES.
* @param number aStart
* Offset from where to start finding the annotations.
* @param number aEnd

View File

@ -268,24 +268,24 @@ SourceEditor.EVENTS = {
BLUR: "Blur",
/**
* The MouseMove event is sent when the user moves the mouse over a line
* annotation. The event object properties:
* The MouseMove event is sent when the user moves the mouse over a line.
* The event object properties:
* - event - the DOM mousemove event object.
* - x and y - the mouse coordinates relative to the document being edited.
*/
MOUSE_MOVE: "MouseMove",
/**
* The MouseOver event is sent when the mouse pointer enters a line
* annotation. The event object properties:
* The MouseOver event is sent when the mouse pointer enters a line.
* The event object properties:
* - event - the DOM mouseover event object.
* - x and y - the mouse coordinates relative to the document being edited.
*/
MOUSE_OVER: "MouseOver",
/**
* This MouseOut event is sent when the mouse pointer exits a line
* annotation. The event object properties:
* This MouseOut event is sent when the mouse pointer exits a line.
* The event object properties:
* - event - the DOM mouseout event object.
* - x and y - the mouse coordinates relative to the document being edited.
*/