提交 41ae0c78 编写于 作者: S Sandeep Somavarapu

move rendering marker hover to hover widget

上级 ff157f90
......@@ -6,7 +6,7 @@
import * as DOM from 'vs/base/browser/dom';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import { escape } from 'vs/base/common/strings';
import { removeMarkdownEscapes, IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
import { removeMarkdownEscapes, IMarkdownString } from 'vs/base/common/htmlContent';
import * as marked from 'vs/base/common/marked/marked';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IDisposable } from 'vs/base/common/lifecycle';
......@@ -211,7 +211,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions
}
const markedOptions: marked.MarkedOptions = {
sanitize: markdown instanceof MarkdownString ? markdown.sanitize : true,
sanitize: true,
renderer
};
......
......@@ -16,7 +16,6 @@ export class MarkdownString implements IMarkdownString {
value: string;
isTrusted?: boolean;
sanitize: boolean = true;
constructor(value: string = '') {
this.value = value;
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IMarkerService, IMarker, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IModelDeltaDecoration, ITextModel, IModelDecorationOptions, TrackedRangeStickiness, OverviewRulerLane, IModelDecoration } from 'vs/editor/common/model';
import { ClassName } from 'vs/editor/common/model/intervalTree';
import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService';
import { overviewRulerWarning, overviewRulerInfo, overviewRulerError } from 'vs/editor/common/view/editorColorRegistry';
import { IModelService } from 'vs/editor/common/services/modelService';
import { Range } from 'vs/editor/common/core/range';
import { keys } from 'vs/base/common/map';
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
function MODEL_ID(resource: URI): string {
return resource.toString();
}
class MarkerDecorations extends Disposable {
private readonly _markersData: Map<string, IMarker> = new Map<string, IMarker>();
constructor(
readonly model: ITextModel
) {
super();
this._register(toDisposable(() => {
this.model.deltaDecorations(keys(this._markersData), []);
this._markersData.clear();
}));
}
public update(markers: IMarker[], newDecorations: IModelDeltaDecoration[]): void {
const ids = this.model.deltaDecorations(keys(this._markersData), newDecorations);
for (let index = 0; index < ids.length; index++) {
this._markersData.set(ids[index], markers[index]);
}
}
getMarker(decoration: IModelDecoration): IMarker | null {
return this._markersData.get(decoration.id);
}
}
export class MarkerDecorationsService extends Disposable implements IMarkerDecorationsService {
_serviceBrand: any;
private readonly _markerDecorations: Map<string, MarkerDecorations> = new Map<string, MarkerDecorations>();
constructor(
@IModelService modelService: IModelService,
@IMarkerService private readonly _markerService: IMarkerService
) {
super();
this._register(modelService.onModelAdded(this._onModelAdded, this));
this._register(modelService.onModelRemoved(this._onModelRemoved, this));
this._register(this._markerService.onMarkerChanged(this._handleMarkerChange, this));
}
getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null {
const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri));
return markerDecorations ? markerDecorations.getMarker(decoration) : null;
}
private _handleMarkerChange(changedResources: URI[]): void {
changedResources.forEach((resource) => {
const markerDecorations = this._markerDecorations.get(MODEL_ID(resource));
if (markerDecorations) {
this.updateDecorations(markerDecorations);
}
});
}
private _onModelAdded(model: ITextModel): void {
const markerDecorations = new MarkerDecorations(model);
this._markerDecorations.set(MODEL_ID(model.uri), markerDecorations);
this.updateDecorations(markerDecorations);
}
private _onModelRemoved(model: ITextModel): void {
const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri));
if (markerDecorations) {
markerDecorations.dispose();
this._markerDecorations.delete(MODEL_ID(model.uri));
}
}
private updateDecorations(markerDecorations: MarkerDecorations): void {
// Limit to the first 500 errors/warnings
const markers = this._markerService.read({ resource: markerDecorations.model.uri, take: 500 });
let newModelDecorations: IModelDeltaDecoration[] = markers.map((marker) => {
return {
range: this._createDecorationRange(markerDecorations.model, marker),
options: this._createDecorationOption(marker)
};
});
markerDecorations.update(markers, newModelDecorations);
}
private _createDecorationRange(model: ITextModel, rawMarker: IMarker): Range {
let ret = Range.lift(rawMarker);
if (rawMarker.severity === MarkerSeverity.Hint) {
if (!rawMarker.tags || rawMarker.tags.indexOf(MarkerTag.Unnecessary) === -1) {
// * never render hints on multiple lines
// * make enough space for three dots
ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2);
}
}
ret = model.validateRange(ret);
if (ret.isEmpty()) {
let word = model.getWordAtPosition(ret.getStartPosition());
if (word) {
ret = new Range(ret.startLineNumber, word.startColumn, ret.endLineNumber, word.endColumn);
} else {
let maxColumn = model.getLineLastNonWhitespaceColumn(ret.startLineNumber) ||
model.getLineMaxColumn(ret.startLineNumber);
if (maxColumn === 1) {
// empty line
// console.warn('marker on empty line:', marker);
} else if (ret.endColumn >= maxColumn) {
// behind eol
ret = new Range(ret.startLineNumber, maxColumn - 1, ret.endLineNumber, maxColumn);
} else {
// extend marker to width = 1
ret = new Range(ret.startLineNumber, ret.startColumn, ret.endLineNumber, ret.endColumn + 1);
}
}
} else if (rawMarker.endColumn === Number.MAX_VALUE && rawMarker.startColumn === 1 && ret.startLineNumber === ret.endLineNumber) {
let minColumn = model.getLineFirstNonWhitespaceColumn(rawMarker.startLineNumber);
if (minColumn < ret.endColumn) {
ret = new Range(ret.startLineNumber, minColumn, ret.endLineNumber, ret.endColumn);
rawMarker.startColumn = minColumn;
}
}
return ret;
}
private _createDecorationOption(marker: IMarker): IModelDecorationOptions {
let className: string;
let color: ThemeColor | undefined = undefined;
let zIndex: number;
let inlineClassName: string | undefined = undefined;
switch (marker.severity) {
case MarkerSeverity.Hint:
if (marker.tags && marker.tags.indexOf(MarkerTag.Unnecessary) >= 0) {
className = ClassName.EditorUnnecessaryDecoration;
} else {
className = ClassName.EditorHintDecoration;
}
zIndex = 0;
break;
case MarkerSeverity.Warning:
className = ClassName.EditorWarningDecoration;
color = themeColorFromId(overviewRulerWarning);
zIndex = 20;
break;
case MarkerSeverity.Info:
className = ClassName.EditorInfoDecoration;
color = themeColorFromId(overviewRulerInfo);
zIndex = 10;
break;
case MarkerSeverity.Error:
default:
className = ClassName.EditorErrorDecoration;
color = themeColorFromId(overviewRulerError);
zIndex = 30;
break;
}
if (marker.tags) {
if (marker.tags.indexOf(MarkerTag.Unnecessary) !== -1) {
inlineClassName = ClassName.EditorUnnecessaryInlineDecoration;
}
}
return {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className,
showIfCollapsed: true,
overviewRuler: {
color,
position: OverviewRulerLane.Right
},
zIndex,
inlineClassName,
};
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITextModel, IModelDecoration } from 'vs/editor/common/model';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IMarker } from 'vs/platform/markers/common/markers';
export const IMarkerDecorationsService = createDecorator<IMarkerDecorationsService>('markerDecorationsService');
export interface IMarkerDecorationsService {
_serviceBrand: any;
getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null;
}
\ No newline at end of file
......@@ -3,20 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { Emitter, Event } from 'vs/base/common/event';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { escape } from 'vs/base/common/strings';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as network from 'vs/base/common/network';
import { basename } from 'vs/base/common/paths';
import * as platform from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, IModelDecorationOptions, IModelDeltaDecoration, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
import { ClassName } from 'vs/editor/common/model/intervalTree';
import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model';
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
import { IModelLanguageChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { LanguageIdentifier } from 'vs/editor/common/modes';
......@@ -24,10 +19,8 @@ import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegis
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
import { overviewRulerError, overviewRulerInfo, overviewRulerWarning } from 'vs/editor/common/view/editorColorRegistry';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IMarker, IMarkerService, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers';
import { ThemeColor, themeColorFromId } from 'vs/platform/theme/common/themeService';
import { IMarkerService } from 'vs/platform/markers/common/markers';
function MODEL_ID(resource: URI): string {
return resource.toString();
......@@ -39,7 +32,6 @@ class ModelData implements IDisposable {
private _languageSelection: ILanguageSelection | null;
private _languageSelectionListener: IDisposable | null;
private _markerDecorations: string[];
private _modelEventListeners: IDisposable[];
constructor(
......@@ -52,8 +44,6 @@ class ModelData implements IDisposable {
this._languageSelection = null;
this._languageSelectionListener = null;
this._markerDecorations = [];
this._modelEventListeners = [];
this._modelEventListeners.push(model.onWillDispose(() => onWillDispose(model)));
this._modelEventListeners.push(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e)));
......@@ -71,15 +61,10 @@ class ModelData implements IDisposable {
}
public dispose(): void {
this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, []);
this._modelEventListeners = dispose(this._modelEventListeners);
this._disposeLanguageSelection();
}
public acceptMarkerDecorations(newDecorations: IModelDeltaDecoration[]): void {
this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, newDecorations);
}
public setLanguage(languageSelection: ILanguageSelection): void {
this._disposeLanguageSelection();
this._languageSelection = languageSelection;
......@@ -88,155 +73,6 @@ class ModelData implements IDisposable {
}
}
class ModelMarkerHandler {
public static setMarkers(modelData: ModelData, markerService: IMarkerService): void {
// Limit to the first 500 errors/warnings
const markers = markerService.read({ resource: modelData.model.uri, take: 500 });
let newModelDecorations: IModelDeltaDecoration[] = markers.map((marker) => {
return {
range: ModelMarkerHandler._createDecorationRange(modelData.model, marker),
options: ModelMarkerHandler._createDecorationOption(marker)
};
});
modelData.acceptMarkerDecorations(newModelDecorations);
}
private static _createDecorationRange(model: ITextModel, rawMarker: IMarker): Range {
let ret = Range.lift(rawMarker);
if (rawMarker.severity === MarkerSeverity.Hint) {
if (!rawMarker.tags || rawMarker.tags.indexOf(MarkerTag.Unnecessary) === -1) {
// * never render hints on multiple lines
// * make enough space for three dots
ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2);
}
}
ret = model.validateRange(ret);
if (ret.isEmpty()) {
let word = model.getWordAtPosition(ret.getStartPosition());
if (word) {
ret = new Range(ret.startLineNumber, word.startColumn, ret.endLineNumber, word.endColumn);
} else {
let maxColumn = model.getLineLastNonWhitespaceColumn(ret.startLineNumber) ||
model.getLineMaxColumn(ret.startLineNumber);
if (maxColumn === 1) {
// empty line
// console.warn('marker on empty line:', marker);
} else if (ret.endColumn >= maxColumn) {
// behind eol
ret = new Range(ret.startLineNumber, maxColumn - 1, ret.endLineNumber, maxColumn);
} else {
// extend marker to width = 1
ret = new Range(ret.startLineNumber, ret.startColumn, ret.endLineNumber, ret.endColumn + 1);
}
}
} else if (rawMarker.endColumn === Number.MAX_VALUE && rawMarker.startColumn === 1 && ret.startLineNumber === ret.endLineNumber) {
let minColumn = model.getLineFirstNonWhitespaceColumn(rawMarker.startLineNumber);
if (minColumn < ret.endColumn) {
ret = new Range(ret.startLineNumber, minColumn, ret.endLineNumber, ret.endColumn);
rawMarker.startColumn = minColumn;
}
}
return ret;
}
private static _createDecorationOption(marker: IMarker): IModelDecorationOptions {
let className: string;
let color: ThemeColor | undefined = undefined;
let zIndex: number;
let inlineClassName: string | undefined = undefined;
switch (marker.severity) {
case MarkerSeverity.Hint:
if (marker.tags && marker.tags.indexOf(MarkerTag.Unnecessary) >= 0) {
className = ClassName.EditorUnnecessaryDecoration;
} else {
className = ClassName.EditorHintDecoration;
}
zIndex = 0;
break;
case MarkerSeverity.Warning:
className = ClassName.EditorWarningDecoration;
color = themeColorFromId(overviewRulerWarning);
zIndex = 20;
break;
case MarkerSeverity.Info:
className = ClassName.EditorInfoDecoration;
color = themeColorFromId(overviewRulerInfo);
zIndex = 10;
break;
case MarkerSeverity.Error:
default:
className = ClassName.EditorErrorDecoration;
color = themeColorFromId(overviewRulerError);
zIndex = 30;
break;
}
if (marker.tags) {
if (marker.tags.indexOf(MarkerTag.Unnecessary) !== -1) {
inlineClassName = ClassName.EditorUnnecessaryInlineDecoration;
}
}
let hoverMessage: MarkdownString | null = null;
let { message, source, relatedInformation, code } = marker;
if (typeof message === 'string') {
hoverMessage = new MarkdownString();
// Disable markdown renderer sanitize to allow html
// Hence, escape all input strings
hoverMessage.sanitize = false;
hoverMessage.appendMarkdown(`<div>`);
hoverMessage.appendMarkdown(`<span style='font-family: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback"; white-space: pre-wrap;'>${escape(message.trim())}</span>`);
if (source) {
hoverMessage.appendMarkdown(`<span style='opacity: 0.6; padding-left:6px;'>${escape(source)}</span>`);
if (code) {
hoverMessage.appendMarkdown(`<span style='opacity: 0.6; padding-left:2px;'>(${escape(code)})</span>`);
}
} else if (code) {
hoverMessage.appendMarkdown(`<span style='opacity: 0.6; padding-left:6px;'>(${escape(code)})</span>`);
}
hoverMessage.appendMarkdown(`</div>`);
if (isNonEmptyArray(relatedInformation)) {
hoverMessage.appendMarkdown(`<ul>`);
for (const { message, resource, startLineNumber, startColumn } of relatedInformation) {
hoverMessage.appendMarkdown(`<li>`);
hoverMessage.appendMarkdown(`<a href='#' data-href='${resource.toString(false)}#${startLineNumber},${startColumn}'>${escape(basename(resource.path))}(${startLineNumber}, ${startColumn})</a>`);
hoverMessage.appendMarkdown(`<span>: ${escape(message)}</span>`);
hoverMessage.appendMarkdown(`</li>`);
}
hoverMessage.appendMarkdown(`</ul>`);
}
}
return {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className,
hoverMessage,
showIfCollapsed: true,
overviewRuler: {
color,
position: OverviewRulerLane.Right
},
zIndex,
inlineClassName,
};
}
}
interface IRawEditorConfig {
tabSize?: any;
insertSpaces?: any;
......@@ -283,7 +119,7 @@ export class ModelServiceImpl extends Disposable implements IModelService {
constructor(
@IMarkerService markerService: IMarkerService | null,
@IConfigurationService configurationService: IConfigurationService,
@ITextResourcePropertiesService resourcePropertiesService: ITextResourcePropertiesService,
@ITextResourcePropertiesService resourcePropertiesService: ITextResourcePropertiesService
) {
super();
this._markerService = markerService;
......@@ -292,11 +128,6 @@ export class ModelServiceImpl extends Disposable implements IModelService {
this._models = {};
this._modelCreationOptionsByLanguageAndResource = Object.create(null);
if (this._markerService) {
this._markerServiceSubscription = this._markerService.onMarkerChanged(this._handleMarkerChange, this);
this._handleMarkerChange(this._markerService.read().map(m => m.resource));
}
this._configurationServiceSubscription = this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions());
this._updateModelOptions();
}
......@@ -413,17 +244,6 @@ export class ModelServiceImpl extends Disposable implements IModelService {
super.dispose();
}
private _handleMarkerChange(changedResources: URI[]): void {
changedResources.forEach((resource) => {
let modelId = MODEL_ID(resource);
let modelData = this._models[modelId];
if (!modelData) {
return;
}
ModelMarkerHandler.setMarkers(modelData, this._markerService!);
});
}
private _cleanUp(model: ITextModel): void {
// clean up markers for internal, transient models
if (model.uri.scheme === network.Schemas.inMemory
......@@ -541,11 +361,6 @@ export class ModelServiceImpl extends Disposable implements IModelService {
modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource, isForSimpleWidget);
}
// handle markers (marker service => model)
if (this._markerService) {
ModelMarkerHandler.setMarkers(modelData, this._markerService);
}
this._onModelAdded.fire(modelData.model);
return modelData.model;
......
......@@ -24,6 +24,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCodeBlockBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
export class ModesHoverController implements IEditorContribution {
......@@ -61,6 +62,7 @@ export class ModesHoverController implements IEditorContribution {
constructor(private readonly _editor: ICodeEditor,
@IOpenerService private readonly _openerService: IOpenerService,
@IModeService private readonly _modeService: IModeService,
@IMarkerDecorationsService private readonly _markerDecorationsService: IMarkerDecorationsService,
@IThemeService private readonly _themeService: IThemeService
) {
this._toUnhook = [];
......@@ -204,7 +206,7 @@ export class ModesHoverController implements IEditorContribution {
private _createHoverWidget() {
const renderer = new MarkdownRenderer(this._editor, this._modeService, this._openerService);
this._contentWidget = new ModesContentHoverWidget(this._editor, renderer, this._themeService);
this._contentWidget = new ModesContentHoverWidget(this._editor, renderer, this._markerDecorationsService, this._themeService);
this._glyphWidget = new ModesGlyphHoverWidget(this._editor, renderer);
}
......
......@@ -13,7 +13,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { DocumentColorProvider, Hover, HoverProviderRegistry, IColor } from 'vs/editor/common/modes';
import { DocumentColorProvider, Hover as MarkdownHover, HoverProviderRegistry, IColor } from 'vs/editor/common/modes';
import { getColorPresentations } from 'vs/editor/contrib/colorPicker/color';
import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector';
import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel';
......@@ -23,7 +23,10 @@ import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contri
import { ContentHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { coalesce } from 'vs/base/common/arrays';
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
import { IMarker, IMarkerData } from 'vs/platform/markers/common/markers';
import { basename } from 'vs/base/common/paths';
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
const $ = dom.$;
......@@ -36,7 +39,15 @@ class ColorHover {
) { }
}
type HoverPart = Hover | ColorHover;
class MarkerHover {
constructor(
public readonly range: IRange,
public readonly marker: IMarker,
) { }
}
type HoverPart = MarkdownHover | ColorHover | MarkerHover;
class ModesContentComputer implements IHoverComputer<HoverPart[]> {
......@@ -44,7 +55,10 @@ class ModesContentComputer implements IHoverComputer<HoverPart[]> {
private _result: HoverPart[];
private _range: Range | null;
constructor(editor: ICodeEditor) {
constructor(
editor: ICodeEditor,
private _markerDecorationsService: IMarkerDecorationsService
) {
this._editor = editor;
this._range = null;
}
......@@ -80,6 +94,7 @@ class ModesContentComputer implements IHoverComputer<HoverPart[]> {
return [];
}
const model = this._editor.getModel();
const lineNumber = this._range.startLineNumber;
if (lineNumber > this._editor.getModel().getLineCount()) {
......@@ -88,7 +103,7 @@ class ModesContentComputer implements IHoverComputer<HoverPart[]> {
}
const colorDetector = ColorDetector.get(this._editor);
const maxColumn = this._editor.getModel().getLineMaxColumn(lineNumber);
const maxColumn = model.getLineMaxColumn(lineNumber);
const lineDecorations = this._editor.getLineDecorations(lineNumber);
let didFindColor = false;
......@@ -102,6 +117,11 @@ class ModesContentComputer implements IHoverComputer<HoverPart[]> {
}
const range = new Range(hoverRange.startLineNumber, startColumn, hoverRange.startLineNumber, endColumn);
const marker = this._markerDecorationsService.getMarker(model, d);
if (marker) {
return new MarkerHover(range, marker);
}
const colorData = colorDetector.getColorData(d.range.getStartPosition());
if (!didFindColor && colorData) {
......@@ -182,13 +202,14 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
constructor(
editor: ICodeEditor,
markdownRenderer: MarkdownRenderer,
markerDecorationsService: IMarkerDecorationsService,
private readonly _themeService: IThemeService
) {
super(ModesContentHoverWidget.ID, editor);
this._messages = [];
this._lastRange = null;
this._computer = new ModesContentComputer(this._editor);
this._computer = new ModesContentComputer(this._editor, markerDecorationsService);
this._highlightDecorations = [];
this._isChangingDecorations = false;
......@@ -328,16 +349,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
renderColumn = Math.min(renderColumn, msg.range.startColumn);
highlightRange = highlightRange ? Range.plusRange(highlightRange, msg.range) : Range.lift(msg.range);
if (!(msg instanceof ColorHover)) {
msg.contents
.filter(contents => !isEmptyMarkdownString(contents))
.forEach(contents => {
const renderedContents = this._markdownRenderer.render(contents);
markdownDisposeable = renderedContents;
fragment.appendChild($('div.hover-row', undefined, renderedContents.element));
isEmptyHoverContent = false;
});
} else {
if (msg instanceof ColorHover) {
containColorPicker = true;
const { red, green, blue, alpha } = msg.color;
......@@ -420,6 +432,20 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this.renderDisposable = combinedDisposable([colorListener, colorChangeListener, widget, markdownDisposeable]);
});
} else {
if (msg instanceof MarkerHover) {
isEmptyHoverContent = false;
fragment.appendChild($('div.hover-row', undefined, this.renderMarkerHover(msg)));
} else {
msg.contents
.filter(contents => !isEmptyMarkdownString(contents))
.forEach(contents => {
const renderedContents = this._markdownRenderer.render(contents);
markdownDisposeable = renderedContents;
fragment.appendChild($('div.hover-row', undefined, renderedContents.element));
isEmptyHoverContent = false;
});
}
}
});
......@@ -438,6 +464,36 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._isChangingDecorations = false;
}
private renderMarkerHover(markerHover: MarkerHover): HTMLElement {
const hoverElement = $('div');
const { source, message, code, relatedInformation } = markerHover.marker;
const messageElement = dom.append(hoverElement, $('span'));
messageElement.style.whiteSpace = 'pre-wrap';
messageElement.innerText = message.trim();
this._editor.applyFontInfo(messageElement);
if (source || code) {
const detailsElement = dom.append(hoverElement, $('span'));
detailsElement.style.opacity = '0.6';
detailsElement.style.paddingLeft = '6px';
detailsElement.innerText = source && code ? `${source}(${code})` : `(${code})`;
}
if (isNonEmptyArray(relatedInformation)) {
const listElement = dom.append(hoverElement, $('ul'));
for (const { message, resource, startLineNumber, startColumn } of relatedInformation) {
const item = dom.append(listElement, $('li'));
const a = dom.append(item, $('a'));
a.setAttribute('data-href', `${resource.toString(false)}#${startLineNumber},${startColumn}`);
a.innerText = `${basename(resource.path)}(${startLineNumber}, ${startColumn})`;
const messageElement = dom.append<HTMLAnchorElement>(item, $('span'));
messageElement.innerText = `: ${message}`;
}
}
return hoverElement;
}
private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({
className: 'hoverHighlight'
});
......@@ -450,10 +506,13 @@ function hoverContentsEquals(first: HoverPart[], second: HoverPart[]): boolean {
for (let i = 0; i < first.length; i++) {
const firstElement = first[i];
const secondElement = second[i];
if (firstElement instanceof ColorHover) {
if (firstElement instanceof MarkerHover && secondElement instanceof MarkerHover) {
return IMarkerData.makeKey(firstElement.marker) === IMarkerData.makeKey(secondElement.marker);
}
if (firstElement instanceof ColorHover || secondElement instanceof ColorHover) {
return false;
}
if (secondElement instanceof ColorHover) {
if (firstElement instanceof MarkerHover || secondElement instanceof MarkerHover) {
return false;
}
if (!markedStringsEquals(firstElement.contents, secondElement.contents)) {
......
......@@ -42,6 +42,8 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { MenuService } from 'vs/platform/actions/common/menuService';
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
import { MarkerDecorationsService } from 'vs/editor/common/services/markerDecorationsServiceImpl';
export interface IEditorOverrideServices {
[index: string]: any;
......@@ -135,6 +137,8 @@ export module StaticServices {
export const modelService = define(IModelService, (o) => new ModelServiceImpl(markerService.get(o), configurationService.get(o), resourcePropertiesService.get(o)));
export const markerDecorationsService = define(IMarkerDecorationsService, (o) => new MarkerDecorationsService(modelService.get(o), markerService.get(o)));
export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), resourceConfigurationService.get(o)));
export const standaloneThemeService = define(IStandaloneThemeService, () => new StandaloneThemeServiceImpl());
......
......@@ -104,6 +104,8 @@ import { TextResourcePropertiesService } from 'vs/workbench/services/textfile/el
import { MultiExtensionManagementService } from 'vs/platform/extensionManagement/node/multiExtensionManagement';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService';
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
import { MarkerDecorationsService } from 'vs/editor/common/services/markerDecorationsServiceImpl';
/**
* Services that we require for the Shell
......@@ -488,6 +490,7 @@ export class WorkbenchShell extends Disposable {
serviceCollection.set(IMarkerService, new SyncDescriptor(MarkerService, undefined, true));
serviceCollection.set(IModeService, new SyncDescriptor(WorkbenchModeServiceImpl));
serviceCollection.set(ITextResourceConfigurationService, new SyncDescriptor(TextResourceConfigurationService));
......@@ -496,6 +499,8 @@ export class WorkbenchShell extends Disposable {
serviceCollection.set(IModelService, new SyncDescriptor(ModelServiceImpl, undefined, true));
serviceCollection.set(IMarkerDecorationsService, new SyncDescriptor(MarkerDecorationsService));
serviceCollection.set(IEditorWorkerService, new SyncDescriptor(EditorWorkerServiceImpl));
serviceCollection.set(IUntitledEditorService, new SyncDescriptor(UntitledEditorService, undefined, true));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册