Skip to content

Commit

Permalink
Fixes microsoft#41492: Add collapseOnReplaceEdit decoration option
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdima committed Sep 20, 2018
1 parent 141f055 commit eadda7c
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/vs/editor/common/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ export interface IModelDecorationOptions {
* @internal
*/
showIfCollapsed?: boolean;
/**
* Collapse the decoration if its entire range is being replaced via an edit.
* @internal
*/
collapseOnReplaceEdit?: boolean;
/**
* Specifies the stack order of a decoration.
* A decoration with greater stack order is always in front of a decoration with a lower stack order.
Expand Down
23 changes: 23 additions & 0 deletions src/vs/editor/common/model/intervalTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ const enum Constants {
StickinessMaskInverse = 0b11001111,
StickinessOffset = 4,

CollapseOnReplaceEditMask = 0b01000000,
CollapseOnReplaceEditMaskInverse = 0b10111111,
CollapseOnReplaceEditOffset = 6,

/**
* Due to how deletion works (in order to avoid always walking the right subtree of the deleted node),
* the deltas for nodes can grow and shrink dramatically. It has been observed, in practice, that unless
Expand Down Expand Up @@ -121,6 +125,14 @@ function _setNodeStickiness(node: IntervalNode, stickiness: TrackedRangeStickine
(node.metadata & Constants.StickinessMaskInverse) | (stickiness << Constants.StickinessOffset)
);
}
function getCollapseOnReplaceEdit(node: IntervalNode): boolean {
return ((node.metadata & Constants.CollapseOnReplaceEditMask) >>> Constants.CollapseOnReplaceEditOffset) === 1;
}
function setCollapseOnReplaceEdit(node: IntervalNode, value: boolean): void {
node.metadata = (
(node.metadata & Constants.CollapseOnReplaceEditMaskInverse) | ((value ? 1 : 0) << Constants.CollapseOnReplaceEditOffset)
);
}
export function setNodeStickiness(node: IntervalNode, stickiness: ActualTrackedRangeStickiness): void {
_setNodeStickiness(node, <number>stickiness);
}
Expand Down Expand Up @@ -170,6 +182,7 @@ export class IntervalNode implements IModelDecoration {
setNodeIsForValidation(this, false);
_setNodeStickiness(this, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
setNodeIsInOverviewRuler(this, false);
setCollapseOnReplaceEdit(this, false);

this.cachedVersionId = 0;
this.cachedAbsoluteStart = start;
Expand Down Expand Up @@ -199,6 +212,7 @@ export class IntervalNode implements IModelDecoration {
));
_setNodeStickiness(this, <number>this.options.stickiness);
setNodeIsInOverviewRuler(this, (this.options.overviewRuler && this.options.overviewRuler.color) ? true : false);
setCollapseOnReplaceEdit(this, this.options.collapseOnReplaceEdit);
}

public setCachedOffsets(absoluteStart: number, absoluteEnd: number, cachedVersionId: number): void {
Expand Down Expand Up @@ -417,6 +431,15 @@ export function nodeAcceptEdit(node: IntervalNode, start: number, end: number, t
const nodeEnd = node.end;
let endDone = false;

if (start <= nodeStart && nodeEnd <= end && getCollapseOnReplaceEdit(node)) {
// This edit encompasses the entire decoration range
// and the decoration has asked to become collapsed
node.start = start;
startDone = true;
node.end = start;
endDone = true;
}

{
const moveSemantics = forceMoveMarkers ? MarkerMoveSemantics.ForceMove : (deletingCnt > 0 ? MarkerMoveSemantics.ForceStay : MarkerMoveSemantics.MarkerDefined);
if (!startDone && adjustMarkerBeforeColumn(nodeStart, startStickToPreviousCharacter, start, moveSemantics)) {
Expand Down
2 changes: 2 additions & 0 deletions src/vs/editor/common/model/textModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2839,6 +2839,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
readonly glyphMarginHoverMessage: IMarkdownString | IMarkdownString[];
readonly isWholeLine: boolean;
readonly showIfCollapsed: boolean;
readonly collapseOnReplaceEdit: boolean;
readonly overviewRuler: ModelDecorationOverviewRulerOptions;
readonly glyphMarginClassName: string;
readonly linesDecorationsClassName: string;
Expand All @@ -2856,6 +2857,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || null;
this.isWholeLine = options.isWholeLine || false;
this.showIfCollapsed = options.showIfCollapsed || false;
this.collapseOnReplaceEdit = options.collapseOnReplaceEdit || false;
this.overviewRuler = options.overviewRuler ? new ModelDecorationOverviewRulerOptions(options.overviewRuler) : null;
this.glyphMarginClassName = options.glyphMarginClassName ? cleanClassName(options.glyphMarginClassName) : null;
this.linesDecorationsClassName = options.linesDecorationsClassName ? cleanClassName(options.linesDecorationsClassName) : null;
Expand Down
8 changes: 8 additions & 0 deletions src/vs/editor/contrib/links/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,41 +54,49 @@ const HOVER_MESSAGE_COMMAND_ALT = new MarkdownString().appendText(
const decoration = {
meta: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link',
hoverMessage: HOVER_MESSAGE_GENERAL_META
}),
metaActive: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link-active',
hoverMessage: HOVER_MESSAGE_GENERAL_META
}),
alt: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link',
hoverMessage: HOVER_MESSAGE_GENERAL_ALT
}),
altActive: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link-active',
hoverMessage: HOVER_MESSAGE_GENERAL_ALT
}),
altCommand: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link',
hoverMessage: HOVER_MESSAGE_COMMAND_ALT
}),
altCommandActive: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link-active',
hoverMessage: HOVER_MESSAGE_COMMAND_ALT
}),
metaCommand: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link',
hoverMessage: HOVER_MESSAGE_COMMAND_META
}),
metaCommandActive: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link-active',
hoverMessage: HOVER_MESSAGE_COMMAND_META
}),
Expand Down
18 changes: 18 additions & 0 deletions src/vs/editor/test/common/model/modelDecorations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1370,4 +1370,22 @@ suite('deltaDecorations', () => {

model.dispose();
});

test('issue #41492: URL highlighting persists after pasting over url', () => {

let model = TextModel.createFromString([
'My First Line'
].join('\n'));

const id = model.deltaDecorations([], [{ range: new Range(1, 2, 1, 14), options: { stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, collapseOnReplaceEdit: true } }])[0];
model.applyEdits([{
range: new Range(1, 1, 1, 14),
text: 'Some new text that is longer than the previous one',
forceMoveMarkers: false
}]);
const actual = model.getDecorationRange(id);
assert.deepEqual(actual, new Range(1, 1, 1, 1));

model.dispose();
});
});

0 comments on commit eadda7c

Please sign in to comment.