Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit c321002

Browse files
committed
Merge pull request #10401 from MarcelGerber/color-editor-fixes
Multiple ColorEditor fixes
2 parents 5dddaa1 + dfc1341 commit c321002

4 files changed

Lines changed: 74 additions & 64 deletions

File tree

src/extensions/default/InlineColorEditor/ColorEditor.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,15 @@ define(function (require, exports, module) {
391391
});
392392
};
393393

394+
/**
395+
* Checks whether colorVal is a valid color
396+
* @param {!string} colorVal
397+
* @return {boolean} Whether colorVal is valid
398+
*/
399+
ColorEditor.prototype.isValidColor = function (colorVal) {
400+
return tinycolor(colorVal).isValid();
401+
};
402+
394403
/**
395404
* Sets _hsv and _color based on an HSV input, and updates the UI. Attempts to preserve
396405
* the previous color format.
@@ -518,7 +527,7 @@ define(function (require, exports, module) {
518527

519528
/**
520529
* Handles undo gestures while color picker has focus. We don't want to let CodeMirror's
521-
* usual undo logic run since it will destroy our bookmarks.
530+
* usual undo logic run since it will destroy our marker.
522531
*/
523532
ColorEditor.prototype.undo = function () {
524533
if (this._originalColor.toString() !== this._color.toString()) {

src/extensions/default/InlineColorEditor/InlineColorEditor.js

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,11 @@ define(function (require, exports, module) {
4141
/**
4242
* Inline widget containing a ColorEditor control
4343
* @param {!string} color Initially selected color
44-
* @param {!CodeMirror.Bookmark} startBookmark
45-
* @param {!CodeMirror.Bookmark} endBookmark
44+
* @param {!CodeMirror.TextMarker} marker
4645
*/
47-
function InlineColorEditor(color, startBookmark, endBookmark) {
46+
function InlineColorEditor(color, marker) {
4847
this._color = color;
49-
this._startBookmark = startBookmark;
50-
this._endBookmark = endBookmark;
48+
this._marker = marker;
5149
this._isOwnChange = false;
5250
this._isHostChange = false;
5351
this._origin = "+InlineColorEditor_" + (lastOriginId++);
@@ -69,17 +67,10 @@ define(function (require, exports, module) {
6967
InlineColorEditor.prototype._color = null;
7068

7169
/**
72-
* Start of the range of code we're attached to; _startBookmark.find() may by null if sync is lost.
73-
* @type {!CodeMirror.Bookmark}
70+
* Range of code we're attached to; _marker.find() may by null if sync is lost.
71+
* @type {!CodeMirror.TextMarker}
7472
*/
75-
InlineColorEditor.prototype._startBookmark = null;
76-
77-
/**
78-
* End of the range of code we're attached to; _endBookmark.find() may by null if sync is lost or even
79-
* in some cases when it's not. Call getCurrentRange() for the definitive text range we're attached to.
80-
* @type {!CodeMirror.Bookmark}
81-
*/
82-
InlineColorEditor.prototype._endBookmark = null;
73+
InlineColorEditor.prototype._marker = null;
8374

8475
/** @type {boolean} True while we're syncing a color picker change into the code editor */
8576
InlineColorEditor.prototype._isOwnChange = null;
@@ -97,25 +88,24 @@ define(function (require, exports, module) {
9788
* @return {?{start:{line:number, ch:number}, end:{line:number, ch:number}}}
9889
*/
9990
InlineColorEditor.prototype.getCurrentRange = function () {
100-
var start, end;
91+
var pos, start, end;
10192

102-
start = this._startBookmark.find();
93+
pos = this._marker && this._marker.find();
94+
95+
start = pos && pos.from;
10396
if (!start) {
10497
return null;
10598
}
10699

107-
end = this._endBookmark.find();
100+
end = pos.to;
108101
if (!end) {
109-
end = { line: start.line };
102+
end = {line: start.line};
110103
}
111104

112-
// Even if we think we have a good end bookmark, we want to run the
113-
// regexp match to see if there's a valid match that extends past the bookmark.
105+
// Even if we think we have a good range end, we want to run the
106+
// regexp match to see if there's a valid match that extends past the marker.
114107
// This can happen if the user deletes the end of the existing color and then
115108
// types some more.
116-
// TODO: when we migrate to CodeMirror v3, we might be able to use markText()
117-
// instead of two bookmarks to track the range. (In our current old version of
118-
// CodeMirror v2, markText() isn't robust enough for this case.)
119109

120110
var line = this.hostEditor.document.getLine(start.line),
121111
matches = line.substr(start.ch).match(ColorUtils.COLOR_REGEX);
@@ -124,12 +114,12 @@ define(function (require, exports, module) {
124114
// the matched length here.
125115
if (matches && (end.ch === undefined || end.ch - start.ch < matches[0].length)) {
126116
end.ch = start.ch + matches[0].length;
127-
this._endBookmark.clear();
128-
this._endBookmark = this.hostEditor._codeMirror.setBookmark(end);
117+
this._marker.clear();
118+
this._marker = this.hostEditor._codeMirror.markText(start, end);
129119
}
130120

131121
if (end.ch === undefined) {
132-
// We were unable to resync the end bookmark.
122+
// We were unable to resync the marker.
133123
return null;
134124
} else {
135125
return {start: start, end: end};
@@ -150,14 +140,20 @@ define(function (require, exports, module) {
150140

151141
// Don't push the change back into the host editor if it came from the host editor.
152142
if (!this._isHostChange) {
143+
var endPos = {
144+
line: range.start.line,
145+
ch: range.start.ch + colorString.length
146+
};
153147
this._isOwnChange = true;
154148
this.hostEditor.document.batchOperation(function () {
155149
// Replace old color in code with the picker's color, and select it
150+
self.hostEditor.setSelection(range.start, range.end); // workaround for #2805
156151
self.hostEditor.document.replaceRange(colorString, range.start, range.end, self._origin);
157-
self.hostEditor.setSelection(range.start, {
158-
line: range.start.line,
159-
ch: range.start.ch + colorString.length
160-
});
152+
self.hostEditor.setSelection(range.start, endPos);
153+
if (self._marker) {
154+
self._marker.clear();
155+
self._marker = self.hostEditor._codeMirror.markText(range.start, endPos);
156+
}
161157
});
162158
this._isOwnChange = false;
163159
}
@@ -202,11 +198,8 @@ define(function (require, exports, module) {
202198
InlineColorEditor.prototype.onClosed = function () {
203199
InlineColorEditor.prototype.parentClass.onClosed.apply(this, arguments);
204200

205-
if (this._startBookmark) {
206-
this._startBookmark.clear();
207-
}
208-
if (this._endBookmark) {
209-
this._endBookmark.clear();
201+
if (this._marker) {
202+
this._marker.clear();
210203
}
211204

212205
var doc = this.hostEditor.document;
@@ -273,15 +266,17 @@ define(function (require, exports, module) {
273266
if (range) {
274267
var newColor = this.hostEditor.document.getRange(range.start, range.end);
275268
if (newColor !== this._color) {
276-
this._isHostChange = true;
277-
this.colorEditor.setColorFromString(newColor);
278-
this._isHostChange = false;
269+
if (this.colorEditor.isValidColor(newColor)) { // only update the editor if the color string is valid
270+
this._isHostChange = true;
271+
this.colorEditor.setColorFromString(newColor);
272+
this._isHostChange = false;
273+
}
279274
}
280275
} else {
281276
// The edit caused our range to become invalid. Close the editor.
282277
this.close();
283278
}
284279
};
285280

286-
module.exports.InlineColorEditor = InlineColorEditor;
281+
exports.InlineColorEditor = InlineColorEditor;
287282
});

src/extensions/default/InlineColorEditor/main.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ define(function (require, exports, module) {
3939
*
4040
* @param {Editor} hostEditor
4141
* @param {{line:Number, ch:Number}} pos
42-
* @return {?{color:String, start:TextMarker, end:TextMarker}}
42+
* @return {?{color:String, marker:TextMarker}}
4343
*/
4444
function prepareEditorForProvider(hostEditor, pos) {
45-
var colorRegEx, cursorLine, match, sel, start, end, startBookmark, endBookmark;
45+
var colorRegEx, cursorLine, match, sel, start, end, endPos, marker;
4646

4747
sel = hostEditor.getSelection();
4848
if (sel.start.line !== sel.end.line) {
@@ -68,16 +68,14 @@ define(function (require, exports, module) {
6868
// Adjust pos to the beginning of the match so that the inline editor won't get
6969
// dismissed while we're updating the color with the new values from user's inline editing.
7070
pos.ch = start;
71+
endPos = {line: pos.line, ch: end};
7172

72-
startBookmark = hostEditor._codeMirror.setBookmark(pos);
73-
endBookmark = hostEditor._codeMirror.setBookmark({ line: pos.line, ch: end });
74-
75-
hostEditor.setSelection(pos, { line: pos.line, ch: end });
73+
marker = hostEditor._codeMirror.markText(pos, endPos);
74+
hostEditor.setSelection(pos, endPos);
7675

7776
return {
7877
color: match[0],
79-
start: startBookmark,
80-
end: endBookmark
78+
marker: marker
8179
};
8280
}
8381

@@ -98,7 +96,7 @@ define(function (require, exports, module) {
9896
if (!context) {
9997
return null;
10098
} else {
101-
inlineColorEditor = new InlineColorEditor(context.color, context.start, context.end);
99+
inlineColorEditor = new InlineColorEditor(context.color, context.marker);
102100
inlineColorEditor.load(hostEditor);
103101

104102
result = new $.Deferred();

src/extensions/default/InlineColorEditor/unittests.js

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,16 @@ define(function (require, exports, module) {
213213
expect(inline.getCurrentRange()).toBeTruthy();
214214
});
215215
});
216+
217+
it("should update correct range of host document when the in-editor color string is invalid", function () {
218+
makeColorEditor({line: 1, ch: 18});
219+
runs(function () {
220+
testDocument.replaceRange("", {line: 1, ch: 22}, {line: 1, ch: 24});
221+
inline.colorEditor.setColorFromString("#c0c0c0");
222+
expect(inline.getCurrentRange()).toEqual({start: {line: 1, ch: 16}, end: {line: 1, ch: 23}});
223+
expect(testDocument.getRange({line: 1, ch: 16}, {line: 1, ch: 23})).toBe("#c0c0c0");
224+
});
225+
});
216226

217227
});
218228

@@ -232,12 +242,12 @@ define(function (require, exports, module) {
232242
});
233243
});
234244

235-
it("should close itself if edit is made that destroys end bookmark and leaves color invalid", function () {
245+
it("should close itself if edit is made that destroys end textmark and leaves color invalid", function () {
236246
makeColorEditor({line: 1, ch: 18});
237247
runs(function () {
238248
spyOn(inline, "close");
239249

240-
// Replace everything including the semicolon, so it crosses the bookmark boundary.
250+
// Replace everything including the semicolon, so it crosses the textmark boundary.
241251
testDocument.replaceRange("rgb(255, 25", {line: 1, ch: 16}, {line: 1, ch: 24});
242252
expect(inline.close).toHaveBeenCalled();
243253
});
@@ -256,25 +266,23 @@ define(function (require, exports, module) {
256266
});
257267
});
258268

259-
it("should not update the end bookmark to a shorter valid match if the bookmark still exists and the color becomes invalid", function () {
269+
it("should not update the end textmark and the color shown to a shorter valid match if the marker still exists and the color becomes invalid", function () {
260270
makeColorEditor({line: 1, ch: 18});
261271
runs(function () {
262272
testDocument.replaceRange("", {line: 1, ch: 22}, {line: 1, ch: 23});
263-
expect(inline._color).toBe("#abcde");
273+
expect(inline._color).toBe("#abcdef");
264274
expect(inline.getCurrentRange()).toEqual({start: {line: 1, ch: 16}, end: {line: 1, ch: 22}});
265275
});
266276
});
267277

268-
// TODO: (issue #2166) The following test fails because if the end bookmark is deleted, we match the shorter
269-
// #xxx string at the beginning of the color and assume that's valid, and then reset the bookmark
270-
// to the end of that location.
271-
// it("should not update the end bookmark to a shorter valid match if the bookmark no longer exists and the color becomes invalid", function () {
272-
// makeColorEditor({line: 1, ch: 18}).done(function (inline) {
273-
// testDocument.replaceRange("", {line: 1, ch: 22}, {line: 1, ch: 24});
274-
// expect(inline._color).toBe("#abcde");
275-
// expect(inline.getCurrentRange()).toEqual({start: {line: 1, ch: 16}, end: {line: 1, ch: 22}});
276-
// });
277-
// });
278+
it("should not update the end textmark and the color shown to a shorter valid match if the marker no longer exists and the color becomes invalid", function () {
279+
makeColorEditor({line: 1, ch: 18});
280+
runs(function () {
281+
testDocument.replaceRange("", {line: 1, ch: 22}, {line: 1, ch: 24});
282+
expect(inline._color).toBe("#abcdef");
283+
expect(inline.getCurrentRange()).toEqual({start: {line: 1, ch: 16}, end: {line: 1, ch: 22}});
284+
});
285+
});
278286

279287
});
280288

0 commit comments

Comments
 (0)