Skip to content

Commit 41ae0c7

Browse files
committed
move rendering marker hover to hover widget
1 parent ff157f9 commit 41ae0c7

9 files changed

Lines changed: 309 additions & 210 deletions

File tree

src/vs/base/browser/htmlContentRenderer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as DOM from 'vs/base/browser/dom';
77
import { defaultGenerator } from 'vs/base/common/idGenerator';
88
import { escape } from 'vs/base/common/strings';
9-
import { removeMarkdownEscapes, IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
9+
import { removeMarkdownEscapes, IMarkdownString } from 'vs/base/common/htmlContent';
1010
import * as marked from 'vs/base/common/marked/marked';
1111
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
1212
import { IDisposable } from 'vs/base/common/lifecycle';
@@ -211,7 +211,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions
211211
}
212212

213213
const markedOptions: marked.MarkedOptions = {
214-
sanitize: markdown instanceof MarkdownString ? markdown.sanitize : true,
214+
sanitize: true,
215215
renderer
216216
};
217217

src/vs/base/common/htmlContent.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export class MarkdownString implements IMarkdownString {
1616

1717
value: string;
1818
isTrusted?: boolean;
19-
sanitize: boolean = true;
2019

2120
constructor(value: string = '') {
2221
this.value = value;
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { IMarkerService, IMarker, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers';
7+
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
8+
import { URI } from 'vs/base/common/uri';
9+
import { IModelDeltaDecoration, ITextModel, IModelDecorationOptions, TrackedRangeStickiness, OverviewRulerLane, IModelDecoration } from 'vs/editor/common/model';
10+
import { ClassName } from 'vs/editor/common/model/intervalTree';
11+
import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService';
12+
import { overviewRulerWarning, overviewRulerInfo, overviewRulerError } from 'vs/editor/common/view/editorColorRegistry';
13+
import { IModelService } from 'vs/editor/common/services/modelService';
14+
import { Range } from 'vs/editor/common/core/range';
15+
import { keys } from 'vs/base/common/map';
16+
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
17+
18+
function MODEL_ID(resource: URI): string {
19+
return resource.toString();
20+
}
21+
22+
class MarkerDecorations extends Disposable {
23+
24+
private readonly _markersData: Map<string, IMarker> = new Map<string, IMarker>();
25+
26+
constructor(
27+
readonly model: ITextModel
28+
) {
29+
super();
30+
this._register(toDisposable(() => {
31+
this.model.deltaDecorations(keys(this._markersData), []);
32+
this._markersData.clear();
33+
}));
34+
}
35+
36+
public update(markers: IMarker[], newDecorations: IModelDeltaDecoration[]): void {
37+
const ids = this.model.deltaDecorations(keys(this._markersData), newDecorations);
38+
for (let index = 0; index < ids.length; index++) {
39+
this._markersData.set(ids[index], markers[index]);
40+
}
41+
}
42+
43+
getMarker(decoration: IModelDecoration): IMarker | null {
44+
return this._markersData.get(decoration.id);
45+
}
46+
}
47+
48+
export class MarkerDecorationsService extends Disposable implements IMarkerDecorationsService {
49+
50+
_serviceBrand: any;
51+
52+
private readonly _markerDecorations: Map<string, MarkerDecorations> = new Map<string, MarkerDecorations>();
53+
54+
constructor(
55+
@IModelService modelService: IModelService,
56+
@IMarkerService private readonly _markerService: IMarkerService
57+
) {
58+
super();
59+
this._register(modelService.onModelAdded(this._onModelAdded, this));
60+
this._register(modelService.onModelRemoved(this._onModelRemoved, this));
61+
this._register(this._markerService.onMarkerChanged(this._handleMarkerChange, this));
62+
}
63+
64+
getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null {
65+
const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri));
66+
return markerDecorations ? markerDecorations.getMarker(decoration) : null;
67+
}
68+
69+
private _handleMarkerChange(changedResources: URI[]): void {
70+
changedResources.forEach((resource) => {
71+
const markerDecorations = this._markerDecorations.get(MODEL_ID(resource));
72+
if (markerDecorations) {
73+
this.updateDecorations(markerDecorations);
74+
}
75+
});
76+
}
77+
78+
private _onModelAdded(model: ITextModel): void {
79+
const markerDecorations = new MarkerDecorations(model);
80+
this._markerDecorations.set(MODEL_ID(model.uri), markerDecorations);
81+
this.updateDecorations(markerDecorations);
82+
}
83+
84+
private _onModelRemoved(model: ITextModel): void {
85+
const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri));
86+
if (markerDecorations) {
87+
markerDecorations.dispose();
88+
this._markerDecorations.delete(MODEL_ID(model.uri));
89+
}
90+
}
91+
92+
private updateDecorations(markerDecorations: MarkerDecorations): void {
93+
// Limit to the first 500 errors/warnings
94+
const markers = this._markerService.read({ resource: markerDecorations.model.uri, take: 500 });
95+
let newModelDecorations: IModelDeltaDecoration[] = markers.map((marker) => {
96+
return {
97+
range: this._createDecorationRange(markerDecorations.model, marker),
98+
options: this._createDecorationOption(marker)
99+
};
100+
});
101+
markerDecorations.update(markers, newModelDecorations);
102+
}
103+
104+
private _createDecorationRange(model: ITextModel, rawMarker: IMarker): Range {
105+
106+
let ret = Range.lift(rawMarker);
107+
108+
if (rawMarker.severity === MarkerSeverity.Hint) {
109+
if (!rawMarker.tags || rawMarker.tags.indexOf(MarkerTag.Unnecessary) === -1) {
110+
// * never render hints on multiple lines
111+
// * make enough space for three dots
112+
ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2);
113+
}
114+
}
115+
116+
ret = model.validateRange(ret);
117+
118+
if (ret.isEmpty()) {
119+
let word = model.getWordAtPosition(ret.getStartPosition());
120+
if (word) {
121+
ret = new Range(ret.startLineNumber, word.startColumn, ret.endLineNumber, word.endColumn);
122+
} else {
123+
let maxColumn = model.getLineLastNonWhitespaceColumn(ret.startLineNumber) ||
124+
model.getLineMaxColumn(ret.startLineNumber);
125+
126+
if (maxColumn === 1) {
127+
// empty line
128+
// console.warn('marker on empty line:', marker);
129+
} else if (ret.endColumn >= maxColumn) {
130+
// behind eol
131+
ret = new Range(ret.startLineNumber, maxColumn - 1, ret.endLineNumber, maxColumn);
132+
} else {
133+
// extend marker to width = 1
134+
ret = new Range(ret.startLineNumber, ret.startColumn, ret.endLineNumber, ret.endColumn + 1);
135+
}
136+
}
137+
} else if (rawMarker.endColumn === Number.MAX_VALUE && rawMarker.startColumn === 1 && ret.startLineNumber === ret.endLineNumber) {
138+
let minColumn = model.getLineFirstNonWhitespaceColumn(rawMarker.startLineNumber);
139+
if (minColumn < ret.endColumn) {
140+
ret = new Range(ret.startLineNumber, minColumn, ret.endLineNumber, ret.endColumn);
141+
rawMarker.startColumn = minColumn;
142+
}
143+
}
144+
return ret;
145+
}
146+
147+
private _createDecorationOption(marker: IMarker): IModelDecorationOptions {
148+
149+
let className: string;
150+
let color: ThemeColor | undefined = undefined;
151+
let zIndex: number;
152+
let inlineClassName: string | undefined = undefined;
153+
154+
switch (marker.severity) {
155+
case MarkerSeverity.Hint:
156+
if (marker.tags && marker.tags.indexOf(MarkerTag.Unnecessary) >= 0) {
157+
className = ClassName.EditorUnnecessaryDecoration;
158+
} else {
159+
className = ClassName.EditorHintDecoration;
160+
}
161+
zIndex = 0;
162+
break;
163+
case MarkerSeverity.Warning:
164+
className = ClassName.EditorWarningDecoration;
165+
color = themeColorFromId(overviewRulerWarning);
166+
zIndex = 20;
167+
break;
168+
case MarkerSeverity.Info:
169+
className = ClassName.EditorInfoDecoration;
170+
color = themeColorFromId(overviewRulerInfo);
171+
zIndex = 10;
172+
break;
173+
case MarkerSeverity.Error:
174+
default:
175+
className = ClassName.EditorErrorDecoration;
176+
color = themeColorFromId(overviewRulerError);
177+
zIndex = 30;
178+
break;
179+
}
180+
181+
if (marker.tags) {
182+
if (marker.tags.indexOf(MarkerTag.Unnecessary) !== -1) {
183+
inlineClassName = ClassName.EditorUnnecessaryInlineDecoration;
184+
}
185+
}
186+
187+
return {
188+
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
189+
className,
190+
showIfCollapsed: true,
191+
overviewRuler: {
192+
color,
193+
position: OverviewRulerLane.Right
194+
},
195+
zIndex,
196+
inlineClassName,
197+
};
198+
}
199+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { ITextModel, IModelDecoration } from 'vs/editor/common/model';
7+
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
8+
import { IMarker } from 'vs/platform/markers/common/markers';
9+
10+
export const IMarkerDecorationsService = createDecorator<IMarkerDecorationsService>('markerDecorationsService');
11+
12+
export interface IMarkerDecorationsService {
13+
_serviceBrand: any;
14+
15+
getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null;
16+
}

0 commit comments

Comments
 (0)