Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-list .monaco-list-row.focused.selected .outline-element .monaco-highlighted-label,
|
||||
.monaco-list .monaco-list-row.focused.selected .outline-element-decoration {
|
||||
/* make sure selection color wins when a label is being selected */
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.monaco-list .outline-element {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.monaco-list .outline-element .monaco-highlighted-label {
|
||||
color: var(--outline-element-color);
|
||||
}
|
||||
|
||||
.monaco-list .outline-element .monaco-icon-label-container .monaco-highlighted-label,
|
||||
.monaco-list .outline-element .monaco-icon-label-container .label-description {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.monaco-list .outline-element .outline-element-decoration {
|
||||
opacity: 0.75;
|
||||
font-size: 90%;
|
||||
font-weight: 600;
|
||||
padding: 0 12px 0 5px;
|
||||
margin-left: auto;
|
||||
text-align: center;
|
||||
color: var(--outline-element-color);
|
||||
}
|
||||
|
||||
.monaco-list .outline-element .outline-element-decoration.bubble {
|
||||
font-family: codicon;
|
||||
font-size: 14px;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.monaco-list .outline-element .outline-element-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-icon-label.deprecated {
|
||||
text-decoration: line-through;
|
||||
opacity: 0.66;
|
||||
}
|
||||
18
lib/vscode/src/vs/editor/contrib/documentSymbols/outline.ts
Normal file
18
lib/vscode/src/vs/editor/contrib/documentSymbols/outline.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
export const OutlineViewId = 'outline';
|
||||
|
||||
export const OutlineViewFiltered = new RawContextKey('outlineFiltered', false);
|
||||
export const OutlineViewFocused = new RawContextKey('outlineFocused', false);
|
||||
|
||||
export const enum OutlineConfigKeys {
|
||||
'icons' = 'outline.icons',
|
||||
'problemsEnabled' = 'outline.problems.enabled',
|
||||
'problemsColors' = 'outline.problems.colors',
|
||||
'problemsBadges' = 'outline.problems.badges'
|
||||
}
|
||||
448
lib/vscode/src/vs/editor/contrib/documentSymbols/outlineModel.ts
Normal file
448
lib/vscode/src/vs/editor/contrib/documentSymbols/outlineModel.ts
Normal file
@@ -0,0 +1,448 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { binarySearch, coalesceInPlace, equals } from 'vs/base/common/arrays';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { commonPrefixLength } from 'vs/base/common/strings';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { DocumentSymbol, DocumentSymbolProvider, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry';
|
||||
|
||||
export abstract class TreeElement {
|
||||
|
||||
abstract id: string;
|
||||
abstract children: Map<string, TreeElement>;
|
||||
abstract parent: TreeElement | undefined;
|
||||
|
||||
abstract adopt(newParent: TreeElement): TreeElement;
|
||||
|
||||
remove(): void {
|
||||
if (this.parent) {
|
||||
this.parent.children.delete(this.id);
|
||||
}
|
||||
}
|
||||
|
||||
static findId(candidate: DocumentSymbol | string, container: TreeElement): string {
|
||||
// complex id-computation which contains the origin/extension,
|
||||
// the parent path, and some dedupe logic when names collide
|
||||
let candidateId: string;
|
||||
if (typeof candidate === 'string') {
|
||||
candidateId = `${container.id}/${candidate}`;
|
||||
} else {
|
||||
candidateId = `${container.id}/${candidate.name}`;
|
||||
if (container.children.get(candidateId) !== undefined) {
|
||||
candidateId = `${container.id}/${candidate.name}_${candidate.range.startLineNumber}_${candidate.range.startColumn}`;
|
||||
}
|
||||
}
|
||||
|
||||
let id = candidateId;
|
||||
for (let i = 0; container.children.get(id) !== undefined; i++) {
|
||||
id = `${candidateId}_${i}`;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static getElementById(id: string, element: TreeElement): TreeElement | undefined {
|
||||
if (!id) {
|
||||
return undefined;
|
||||
}
|
||||
let len = commonPrefixLength(id, element.id);
|
||||
if (len === id.length) {
|
||||
return element;
|
||||
}
|
||||
if (len < element.id.length) {
|
||||
return undefined;
|
||||
}
|
||||
for (const [, child] of element.children) {
|
||||
let candidate = TreeElement.getElementById(id, child);
|
||||
if (candidate) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static size(element: TreeElement): number {
|
||||
let res = 1;
|
||||
for (const [, child] of element.children) {
|
||||
res += TreeElement.size(child);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static empty(element: TreeElement): boolean {
|
||||
return element.children.size === 0;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IOutlineMarker {
|
||||
startLineNumber: number;
|
||||
startColumn: number;
|
||||
endLineNumber: number;
|
||||
endColumn: number;
|
||||
severity: MarkerSeverity;
|
||||
}
|
||||
|
||||
export class OutlineElement extends TreeElement {
|
||||
|
||||
children = new Map<string, OutlineElement>();
|
||||
marker: { count: number, topSev: MarkerSeverity } | undefined;
|
||||
|
||||
constructor(
|
||||
readonly id: string,
|
||||
public parent: TreeElement | undefined,
|
||||
readonly symbol: DocumentSymbol
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
adopt(parent: TreeElement): OutlineElement {
|
||||
let res = new OutlineElement(this.id, parent, this.symbol);
|
||||
for (const [key, value] of this.children) {
|
||||
res.children.set(key, value.adopt(res));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineGroup extends TreeElement {
|
||||
|
||||
children = new Map<string, OutlineElement>();
|
||||
|
||||
constructor(
|
||||
readonly id: string,
|
||||
public parent: TreeElement | undefined,
|
||||
readonly label: string,
|
||||
readonly order: number,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
adopt(parent: TreeElement): OutlineGroup {
|
||||
let res = new OutlineGroup(this.id, parent, this.label, this.order);
|
||||
for (const [key, value] of this.children) {
|
||||
res.children.set(key, value.adopt(res));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
getItemEnclosingPosition(position: IPosition): OutlineElement | undefined {
|
||||
return position ? this._getItemEnclosingPosition(position, this.children) : undefined;
|
||||
}
|
||||
|
||||
private _getItemEnclosingPosition(position: IPosition, children: Map<string, OutlineElement>): OutlineElement | undefined {
|
||||
for (const [, item] of children) {
|
||||
if (!item.symbol.range || !Range.containsPosition(item.symbol.range, position)) {
|
||||
continue;
|
||||
}
|
||||
return this._getItemEnclosingPosition(position, item.children) || item;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
updateMarker(marker: IOutlineMarker[]): void {
|
||||
for (const [, child] of this.children) {
|
||||
this._updateMarker(marker, child);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateMarker(markers: IOutlineMarker[], item: OutlineElement): void {
|
||||
item.marker = undefined;
|
||||
|
||||
// find the proper start index to check for item/marker overlap.
|
||||
let idx = binarySearch<IRange>(markers, item.symbol.range, Range.compareRangesUsingStarts);
|
||||
let start: number;
|
||||
if (idx < 0) {
|
||||
start = ~idx;
|
||||
if (start > 0 && Range.areIntersecting(markers[start - 1], item.symbol.range)) {
|
||||
start -= 1;
|
||||
}
|
||||
} else {
|
||||
start = idx;
|
||||
}
|
||||
|
||||
let myMarkers: IOutlineMarker[] = [];
|
||||
let myTopSev: MarkerSeverity | undefined;
|
||||
|
||||
for (; start < markers.length && Range.areIntersecting(item.symbol.range, markers[start]); start++) {
|
||||
// remove markers intersecting with this outline element
|
||||
// and store them in a 'private' array.
|
||||
let marker = markers[start];
|
||||
myMarkers.push(marker);
|
||||
(markers as Array<IOutlineMarker | undefined>)[start] = undefined;
|
||||
if (!myTopSev || marker.severity > myTopSev) {
|
||||
myTopSev = marker.severity;
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse into children and let them match markers that have matched
|
||||
// this outline element. This might remove markers from this element and
|
||||
// therefore we remember that we have had markers. That allows us to render
|
||||
// the dot, saying 'this element has children with markers'
|
||||
for (const [, child] of item.children) {
|
||||
this._updateMarker(myMarkers, child);
|
||||
}
|
||||
|
||||
if (myTopSev) {
|
||||
item.marker = {
|
||||
count: myMarkers.length,
|
||||
topSev: myTopSev
|
||||
};
|
||||
}
|
||||
|
||||
coalesceInPlace(markers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class OutlineModel extends TreeElement {
|
||||
|
||||
private static readonly _requestDurations = new LanguageFeatureRequestDelays(DocumentSymbolProviderRegistry, 350);
|
||||
private static readonly _requests = new LRUCache<string, { promiseCnt: number, source: CancellationTokenSource, promise: Promise<any>, model: OutlineModel | undefined }>(9, 0.75);
|
||||
private static readonly _keys = new class {
|
||||
|
||||
private _counter = 1;
|
||||
private _data = new WeakMap<DocumentSymbolProvider, number>();
|
||||
|
||||
for(textModel: ITextModel, version: boolean): string {
|
||||
return `${textModel.id}/${version ? textModel.getVersionId() : ''}/${this._hash(DocumentSymbolProviderRegistry.all(textModel))}`;
|
||||
}
|
||||
|
||||
private _hash(providers: DocumentSymbolProvider[]): string {
|
||||
let result = '';
|
||||
for (const provider of providers) {
|
||||
let n = this._data.get(provider);
|
||||
if (typeof n === 'undefined') {
|
||||
n = this._counter++;
|
||||
this._data.set(provider, n);
|
||||
}
|
||||
result += n;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static create(textModel: ITextModel, token: CancellationToken): Promise<OutlineModel> {
|
||||
|
||||
let key = this._keys.for(textModel, true);
|
||||
let data = OutlineModel._requests.get(key);
|
||||
|
||||
if (!data) {
|
||||
let source = new CancellationTokenSource();
|
||||
data = {
|
||||
promiseCnt: 0,
|
||||
source,
|
||||
promise: OutlineModel._create(textModel, source.token),
|
||||
model: undefined,
|
||||
};
|
||||
OutlineModel._requests.set(key, data);
|
||||
|
||||
// keep moving average of request durations
|
||||
const now = Date.now();
|
||||
data.promise.then(() => {
|
||||
this._requestDurations.update(textModel, Date.now() - now);
|
||||
});
|
||||
}
|
||||
|
||||
if (data!.model) {
|
||||
// resolved -> return data
|
||||
return Promise.resolve(data.model!);
|
||||
}
|
||||
|
||||
// increase usage counter
|
||||
data!.promiseCnt += 1;
|
||||
|
||||
token.onCancellationRequested(() => {
|
||||
// last -> cancel provider request, remove cached promise
|
||||
if (--data!.promiseCnt === 0) {
|
||||
data!.source.cancel();
|
||||
OutlineModel._requests.delete(key);
|
||||
}
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
data!.promise.then(model => {
|
||||
data!.model = model;
|
||||
resolve(model);
|
||||
}, err => {
|
||||
OutlineModel._requests.delete(key);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static getRequestDelay(textModel: ITextModel | null): number {
|
||||
return textModel ? this._requestDurations.get(textModel) : this._requestDurations.min;
|
||||
}
|
||||
|
||||
private static _create(textModel: ITextModel, token: CancellationToken): Promise<OutlineModel> {
|
||||
|
||||
const cts = new CancellationTokenSource(token);
|
||||
const result = new OutlineModel(textModel.uri);
|
||||
const provider = DocumentSymbolProviderRegistry.ordered(textModel);
|
||||
const promises = provider.map((provider, index) => {
|
||||
|
||||
let id = TreeElement.findId(`provider_${index}`, result);
|
||||
let group = new OutlineGroup(id, result, provider.displayName ?? 'Unknown Outline Provider', index);
|
||||
|
||||
return Promise.resolve(provider.provideDocumentSymbols(textModel, cts.token)).then(result => {
|
||||
for (const info of result || []) {
|
||||
OutlineModel._makeOutlineElement(info, group);
|
||||
}
|
||||
return group;
|
||||
}, err => {
|
||||
onUnexpectedExternalError(err);
|
||||
return group;
|
||||
}).then(group => {
|
||||
if (!TreeElement.empty(group)) {
|
||||
result._groups.set(id, group);
|
||||
} else {
|
||||
group.remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const listener = DocumentSymbolProviderRegistry.onDidChange(() => {
|
||||
const newProvider = DocumentSymbolProviderRegistry.ordered(textModel);
|
||||
if (!equals(newProvider, provider)) {
|
||||
cts.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
if (cts.token.isCancellationRequested && !token.isCancellationRequested) {
|
||||
return OutlineModel._create(textModel, token);
|
||||
} else {
|
||||
return result._compact();
|
||||
}
|
||||
}).finally(() => {
|
||||
listener.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
private static _makeOutlineElement(info: DocumentSymbol, container: OutlineGroup | OutlineElement): void {
|
||||
let id = TreeElement.findId(info, container);
|
||||
let res = new OutlineElement(id, container, info);
|
||||
if (info.children) {
|
||||
for (const childInfo of info.children) {
|
||||
OutlineModel._makeOutlineElement(childInfo, res);
|
||||
}
|
||||
}
|
||||
container.children.set(res.id, res);
|
||||
}
|
||||
|
||||
static get(element: TreeElement | undefined): OutlineModel | undefined {
|
||||
while (element) {
|
||||
if (element instanceof OutlineModel) {
|
||||
return element;
|
||||
}
|
||||
element = element.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
readonly id = 'root';
|
||||
readonly parent = undefined;
|
||||
|
||||
protected _groups = new Map<string, OutlineGroup>();
|
||||
children = new Map<string, OutlineGroup | OutlineElement>();
|
||||
|
||||
protected constructor(readonly uri: URI) {
|
||||
super();
|
||||
|
||||
this.id = 'root';
|
||||
this.parent = undefined;
|
||||
}
|
||||
|
||||
adopt(): OutlineModel {
|
||||
let res = new OutlineModel(this.uri);
|
||||
for (const [key, value] of this._groups) {
|
||||
res._groups.set(key, value.adopt(res));
|
||||
}
|
||||
return res._compact();
|
||||
}
|
||||
|
||||
private _compact(): this {
|
||||
let count = 0;
|
||||
for (const [key, group] of this._groups) {
|
||||
if (group.children.size === 0) { // empty
|
||||
this._groups.delete(key);
|
||||
} else {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
if (count !== 1) {
|
||||
//
|
||||
this.children = this._groups;
|
||||
} else {
|
||||
// adopt all elements of the first group
|
||||
let group = Iterable.first(this._groups.values())!;
|
||||
for (let [, child] of group.children) {
|
||||
child.parent = this;
|
||||
this.children.set(child.id, child);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
merge(other: OutlineModel): boolean {
|
||||
if (this.uri.toString() !== other.uri.toString()) {
|
||||
return false;
|
||||
}
|
||||
if (this._groups.size !== other._groups.size) {
|
||||
return false;
|
||||
}
|
||||
this._groups = other._groups;
|
||||
this.children = other.children;
|
||||
return true;
|
||||
}
|
||||
|
||||
getItemEnclosingPosition(position: IPosition, context?: OutlineElement): OutlineElement | undefined {
|
||||
|
||||
let preferredGroup: OutlineGroup | undefined;
|
||||
if (context) {
|
||||
let candidate = context.parent;
|
||||
while (candidate && !preferredGroup) {
|
||||
if (candidate instanceof OutlineGroup) {
|
||||
preferredGroup = candidate;
|
||||
}
|
||||
candidate = candidate.parent;
|
||||
}
|
||||
}
|
||||
|
||||
let result: OutlineElement | undefined = undefined;
|
||||
for (const [, group] of this._groups) {
|
||||
result = group.getItemEnclosingPosition(position);
|
||||
if (result && (!preferredGroup || preferredGroup === group)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getItemById(id: string): TreeElement | undefined {
|
||||
return TreeElement.getElementById(id, this);
|
||||
}
|
||||
|
||||
updateMarker(marker: IOutlineMarker[]): void {
|
||||
// sort markers by start range so that we can use
|
||||
// outline element starts for quicker look up
|
||||
marker.sort(Range.compareRangesUsingStarts);
|
||||
|
||||
for (const [, group] of this._groups) {
|
||||
group.updateMarker(marker.slice(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
725
lib/vscode/src/vs/editor/contrib/documentSymbols/outlineTree.ts
Normal file
725
lib/vscode/src/vs/editor/contrib/documentSymbols/outlineTree.ts
Normal file
@@ -0,0 +1,725 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { IDataSource, ITreeNode, ITreeRenderer, ITreeSorter, ITreeFilter } from 'vs/base/browser/ui/tree/tree';
|
||||
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
|
||||
import 'vs/css!./media/outlineTree';
|
||||
import 'vs/css!./media/symbol-icons';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { SymbolKind, SymbolKinds, SymbolTag } from 'vs/editor/common/modes';
|
||||
import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline';
|
||||
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor, listErrorForeground, listWarningForeground, foreground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IdleValue } from 'vs/base/common/async';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
|
||||
export type OutlineItem = OutlineGroup | OutlineElement;
|
||||
|
||||
export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelProvider<OutlineItem> {
|
||||
|
||||
getKeyboardNavigationLabel(element: OutlineItem): { toString(): string; } {
|
||||
if (element instanceof OutlineGroup) {
|
||||
return element.label;
|
||||
} else {
|
||||
return element.symbol.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineAccessibilityProvider implements IListAccessibilityProvider<OutlineItem> {
|
||||
|
||||
constructor(private readonly ariaLabel: string) { }
|
||||
|
||||
getWidgetAriaLabel(): string {
|
||||
return this.ariaLabel;
|
||||
}
|
||||
|
||||
getAriaLabel(element: OutlineItem): string | null {
|
||||
if (element instanceof OutlineGroup) {
|
||||
return element.label;
|
||||
} else {
|
||||
return element.symbol.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineIdentityProvider implements IIdentityProvider<OutlineItem> {
|
||||
getId(element: OutlineItem): { toString(): string; } {
|
||||
return element.id;
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineGroupTemplate {
|
||||
static readonly id = 'OutlineGroupTemplate';
|
||||
constructor(
|
||||
readonly labelContainer: HTMLElement,
|
||||
readonly label: HighlightedLabel,
|
||||
) { }
|
||||
}
|
||||
|
||||
export class OutlineElementTemplate {
|
||||
static readonly id = 'OutlineElementTemplate';
|
||||
constructor(
|
||||
readonly container: HTMLElement,
|
||||
readonly iconLabel: IconLabel,
|
||||
readonly iconClass: HTMLElement,
|
||||
readonly decoration: HTMLElement,
|
||||
) { }
|
||||
}
|
||||
|
||||
export class OutlineVirtualDelegate implements IListVirtualDelegate<OutlineItem> {
|
||||
|
||||
getHeight(_element: OutlineItem): number {
|
||||
return 22;
|
||||
}
|
||||
|
||||
getTemplateId(element: OutlineItem): string {
|
||||
if (element instanceof OutlineGroup) {
|
||||
return OutlineGroupTemplate.id;
|
||||
} else {
|
||||
return OutlineElementTemplate.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineGroupRenderer implements ITreeRenderer<OutlineGroup, FuzzyScore, OutlineGroupTemplate> {
|
||||
|
||||
readonly templateId: string = OutlineGroupTemplate.id;
|
||||
|
||||
renderTemplate(container: HTMLElement): OutlineGroupTemplate {
|
||||
const labelContainer = dom.$('.outline-element-label');
|
||||
container.classList.add('outline-element');
|
||||
dom.append(container, labelContainer);
|
||||
return new OutlineGroupTemplate(labelContainer, new HighlightedLabel(labelContainer, true));
|
||||
}
|
||||
|
||||
renderElement(node: ITreeNode<OutlineGroup, FuzzyScore>, index: number, template: OutlineGroupTemplate): void {
|
||||
template.label.set(
|
||||
node.element.label,
|
||||
createMatches(node.filterData)
|
||||
);
|
||||
}
|
||||
|
||||
disposeTemplate(_template: OutlineGroupTemplate): void {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineElementRenderer implements ITreeRenderer<OutlineElement, FuzzyScore, OutlineElementTemplate> {
|
||||
|
||||
readonly templateId: string = OutlineElementTemplate.id;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): OutlineElementTemplate {
|
||||
container.classList.add('outline-element');
|
||||
const iconLabel = new IconLabel(container, { supportHighlights: true });
|
||||
const iconClass = dom.$('.outline-element-icon');
|
||||
const decoration = dom.$('.outline-element-decoration');
|
||||
container.prepend(iconClass);
|
||||
container.appendChild(decoration);
|
||||
return new OutlineElementTemplate(container, iconLabel, iconClass, decoration);
|
||||
}
|
||||
|
||||
renderElement(node: ITreeNode<OutlineElement, FuzzyScore>, index: number, template: OutlineElementTemplate): void {
|
||||
const { element } = node;
|
||||
const options = {
|
||||
matches: createMatches(node.filterData),
|
||||
labelEscapeNewLines: true,
|
||||
extraClasses: <string[]>[],
|
||||
title: localize('title.template', "{0} ({1})", element.symbol.name, OutlineElementRenderer._symbolKindNames[element.symbol.kind])
|
||||
};
|
||||
if (this._configurationService.getValue(OutlineConfigKeys.icons)) {
|
||||
// add styles for the icons
|
||||
template.iconClass.className = '';
|
||||
template.iconClass.classList.add(`outline-element-icon`, ...SymbolKinds.toCssClassName(element.symbol.kind, true).split(' '));
|
||||
}
|
||||
if (element.symbol.tags.indexOf(SymbolTag.Deprecated) >= 0) {
|
||||
options.extraClasses.push(`deprecated`);
|
||||
options.matches = [];
|
||||
}
|
||||
template.iconLabel.setLabel(element.symbol.name, element.symbol.detail, options);
|
||||
this._renderMarkerInfo(element, template);
|
||||
}
|
||||
|
||||
private _renderMarkerInfo(element: OutlineElement, template: OutlineElementTemplate): void {
|
||||
|
||||
if (!element.marker) {
|
||||
dom.hide(template.decoration);
|
||||
template.container.style.removeProperty('--outline-element-color');
|
||||
return;
|
||||
}
|
||||
|
||||
const { count, topSev } = element.marker;
|
||||
const color = this._themeService.getColorTheme().getColor(topSev === MarkerSeverity.Error ? listErrorForeground : listWarningForeground);
|
||||
const cssColor = color ? color.toString() : 'inherit';
|
||||
|
||||
// color of the label
|
||||
if (this._configurationService.getValue(OutlineConfigKeys.problemsColors)) {
|
||||
template.container.style.setProperty('--outline-element-color', cssColor);
|
||||
} else {
|
||||
template.container.style.removeProperty('--outline-element-color');
|
||||
}
|
||||
|
||||
// badge with color/rollup
|
||||
if (!this._configurationService.getValue(OutlineConfigKeys.problemsBadges)) {
|
||||
dom.hide(template.decoration);
|
||||
|
||||
} else if (count > 0) {
|
||||
dom.show(template.decoration);
|
||||
template.decoration.classList.remove('bubble');
|
||||
template.decoration.innerText = count < 10 ? count.toString() : '+9';
|
||||
template.decoration.title = count === 1 ? localize('1.problem', "1 problem in this element") : localize('N.problem', "{0} problems in this element", count);
|
||||
template.decoration.style.setProperty('--outline-element-color', cssColor);
|
||||
|
||||
} else {
|
||||
dom.show(template.decoration);
|
||||
template.decoration.classList.add('bubble');
|
||||
template.decoration.innerText = '\uea71';
|
||||
template.decoration.title = localize('deep.problem', "Contains elements with problems");
|
||||
template.decoration.style.setProperty('--outline-element-color', cssColor);
|
||||
}
|
||||
}
|
||||
|
||||
private static _symbolKindNames: { [symbol: number]: string } = {
|
||||
[SymbolKind.Array]: localize('Array', "array"),
|
||||
[SymbolKind.Boolean]: localize('Boolean', "boolean"),
|
||||
[SymbolKind.Class]: localize('Class', "class"),
|
||||
[SymbolKind.Constant]: localize('Constant', "constant"),
|
||||
[SymbolKind.Constructor]: localize('Constructor', "constructor"),
|
||||
[SymbolKind.Enum]: localize('Enum', "enumeration"),
|
||||
[SymbolKind.EnumMember]: localize('EnumMember', "enumeration member"),
|
||||
[SymbolKind.Event]: localize('Event', "event"),
|
||||
[SymbolKind.Field]: localize('Field', "field"),
|
||||
[SymbolKind.File]: localize('File', "file"),
|
||||
[SymbolKind.Function]: localize('Function', "function"),
|
||||
[SymbolKind.Interface]: localize('Interface', "interface"),
|
||||
[SymbolKind.Key]: localize('Key', "key"),
|
||||
[SymbolKind.Method]: localize('Method', "method"),
|
||||
[SymbolKind.Module]: localize('Module', "module"),
|
||||
[SymbolKind.Namespace]: localize('Namespace', "namespace"),
|
||||
[SymbolKind.Null]: localize('Null', "null"),
|
||||
[SymbolKind.Number]: localize('Number', "number"),
|
||||
[SymbolKind.Object]: localize('Object', "object"),
|
||||
[SymbolKind.Operator]: localize('Operator', "operator"),
|
||||
[SymbolKind.Package]: localize('Package', "package"),
|
||||
[SymbolKind.Property]: localize('Property', "property"),
|
||||
[SymbolKind.String]: localize('String', "string"),
|
||||
[SymbolKind.Struct]: localize('Struct', "struct"),
|
||||
[SymbolKind.TypeParameter]: localize('TypeParameter', "type parameter"),
|
||||
[SymbolKind.Variable]: localize('Variable', "variable"),
|
||||
};
|
||||
|
||||
disposeTemplate(_template: OutlineElementTemplate): void {
|
||||
_template.iconLabel.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export const enum OutlineSortOrder {
|
||||
ByPosition,
|
||||
ByName,
|
||||
ByKind
|
||||
}
|
||||
|
||||
export class OutlineFilter implements ITreeFilter<OutlineItem> {
|
||||
|
||||
static readonly configNameToKind = Object.freeze({
|
||||
['showFiles']: SymbolKind.File,
|
||||
['showModules']: SymbolKind.Module,
|
||||
['showNamespaces']: SymbolKind.Namespace,
|
||||
['showPackages']: SymbolKind.Package,
|
||||
['showClasses']: SymbolKind.Class,
|
||||
['showMethods']: SymbolKind.Method,
|
||||
['showProperties']: SymbolKind.Property,
|
||||
['showFields']: SymbolKind.Field,
|
||||
['showConstructors']: SymbolKind.Constructor,
|
||||
['showEnums']: SymbolKind.Enum,
|
||||
['showInterfaces']: SymbolKind.Interface,
|
||||
['showFunctions']: SymbolKind.Function,
|
||||
['showVariables']: SymbolKind.Variable,
|
||||
['showConstants']: SymbolKind.Constant,
|
||||
['showStrings']: SymbolKind.String,
|
||||
['showNumbers']: SymbolKind.Number,
|
||||
['showBooleans']: SymbolKind.Boolean,
|
||||
['showArrays']: SymbolKind.Array,
|
||||
['showObjects']: SymbolKind.Object,
|
||||
['showKeys']: SymbolKind.Key,
|
||||
['showNull']: SymbolKind.Null,
|
||||
['showEnumMembers']: SymbolKind.EnumMember,
|
||||
['showStructs']: SymbolKind.Struct,
|
||||
['showEvents']: SymbolKind.Event,
|
||||
['showOperators']: SymbolKind.Operator,
|
||||
['showTypeParameters']: SymbolKind.TypeParameter,
|
||||
});
|
||||
|
||||
static readonly kindToConfigName = Object.freeze({
|
||||
[SymbolKind.File]: 'showFiles',
|
||||
[SymbolKind.Module]: 'showModules',
|
||||
[SymbolKind.Namespace]: 'showNamespaces',
|
||||
[SymbolKind.Package]: 'showPackages',
|
||||
[SymbolKind.Class]: 'showClasses',
|
||||
[SymbolKind.Method]: 'showMethods',
|
||||
[SymbolKind.Property]: 'showProperties',
|
||||
[SymbolKind.Field]: 'showFields',
|
||||
[SymbolKind.Constructor]: 'showConstructors',
|
||||
[SymbolKind.Enum]: 'showEnums',
|
||||
[SymbolKind.Interface]: 'showInterfaces',
|
||||
[SymbolKind.Function]: 'showFunctions',
|
||||
[SymbolKind.Variable]: 'showVariables',
|
||||
[SymbolKind.Constant]: 'showConstants',
|
||||
[SymbolKind.String]: 'showStrings',
|
||||
[SymbolKind.Number]: 'showNumbers',
|
||||
[SymbolKind.Boolean]: 'showBooleans',
|
||||
[SymbolKind.Array]: 'showArrays',
|
||||
[SymbolKind.Object]: 'showObjects',
|
||||
[SymbolKind.Key]: 'showKeys',
|
||||
[SymbolKind.Null]: 'showNull',
|
||||
[SymbolKind.EnumMember]: 'showEnumMembers',
|
||||
[SymbolKind.Struct]: 'showStructs',
|
||||
[SymbolKind.Event]: 'showEvents',
|
||||
[SymbolKind.Operator]: 'showOperators',
|
||||
[SymbolKind.TypeParameter]: 'showTypeParameters',
|
||||
});
|
||||
|
||||
constructor(
|
||||
private readonly _prefix: string,
|
||||
@ITextResourceConfigurationService private readonly _textResourceConfigService: ITextResourceConfigurationService,
|
||||
) { }
|
||||
|
||||
filter(element: OutlineItem): boolean {
|
||||
const outline = OutlineModel.get(element);
|
||||
let uri: URI | undefined;
|
||||
|
||||
if (outline) {
|
||||
uri = outline.uri;
|
||||
}
|
||||
|
||||
if (!(element instanceof OutlineElement)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const configName = OutlineFilter.kindToConfigName[element.symbol.kind];
|
||||
const configKey = `${this._prefix}.${configName}`;
|
||||
return this._textResourceConfigService.getValue(uri, configKey);
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineItemComparator implements ITreeSorter<OutlineItem> {
|
||||
|
||||
private readonly _collator = new IdleValue<Intl.Collator>(() => new Intl.Collator(undefined, { numeric: true }));
|
||||
|
||||
constructor(
|
||||
public type: OutlineSortOrder = OutlineSortOrder.ByPosition
|
||||
) { }
|
||||
|
||||
compare(a: OutlineItem, b: OutlineItem): number {
|
||||
if (a instanceof OutlineGroup && b instanceof OutlineGroup) {
|
||||
return a.order - b.order;
|
||||
|
||||
} else if (a instanceof OutlineElement && b instanceof OutlineElement) {
|
||||
if (this.type === OutlineSortOrder.ByKind) {
|
||||
return a.symbol.kind - b.symbol.kind || this._collator.value.compare(a.symbol.name, b.symbol.name);
|
||||
} else if (this.type === OutlineSortOrder.ByName) {
|
||||
return this._collator.value.compare(a.symbol.name, b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range);
|
||||
} else if (this.type === OutlineSortOrder.ByPosition) {
|
||||
return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || this._collator.value.compare(a.symbol.name, b.symbol.name);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineDataSource implements IDataSource<OutlineModel, OutlineItem> {
|
||||
|
||||
getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement) {
|
||||
if (!element) {
|
||||
return Iterable.empty();
|
||||
}
|
||||
return element.children.values();
|
||||
}
|
||||
}
|
||||
|
||||
export const SYMBOL_ICON_ARRAY_FOREGROUND = registerColor('symbolIcon.arrayForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.arrayForeground', 'The foreground color for array symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_BOOLEAN_FOREGROUND = registerColor('symbolIcon.booleanForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.booleanForeground', 'The foreground color for boolean symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_CLASS_FOREGROUND = registerColor('symbolIcon.classForeground', {
|
||||
dark: '#EE9D28',
|
||||
light: '#D67E00',
|
||||
hc: '#EE9D28'
|
||||
}, localize('symbolIcon.classForeground', 'The foreground color for class symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_COLOR_FOREGROUND = registerColor('symbolIcon.colorForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.colorForeground', 'The foreground color for color symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_CONSTANT_FOREGROUND = registerColor('symbolIcon.constantForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.constantForeground', 'The foreground color for constant symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_CONSTRUCTOR_FOREGROUND = registerColor('symbolIcon.constructorForeground', {
|
||||
dark: '#B180D7',
|
||||
light: '#652D90',
|
||||
hc: '#B180D7'
|
||||
}, localize('symbolIcon.constructorForeground', 'The foreground color for constructor symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_ENUMERATOR_FOREGROUND = registerColor('symbolIcon.enumeratorForeground', {
|
||||
dark: '#EE9D28',
|
||||
light: '#D67E00',
|
||||
hc: '#EE9D28'
|
||||
}, localize('symbolIcon.enumeratorForeground', 'The foreground color for enumerator symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND = registerColor('symbolIcon.enumeratorMemberForeground', {
|
||||
dark: '#75BEFF',
|
||||
light: '#007ACC',
|
||||
hc: '#75BEFF'
|
||||
}, localize('symbolIcon.enumeratorMemberForeground', 'The foreground color for enumerator member symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_EVENT_FOREGROUND = registerColor('symbolIcon.eventForeground', {
|
||||
dark: '#EE9D28',
|
||||
light: '#D67E00',
|
||||
hc: '#EE9D28'
|
||||
}, localize('symbolIcon.eventForeground', 'The foreground color for event symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_FIELD_FOREGROUND = registerColor('symbolIcon.fieldForeground', {
|
||||
dark: '#75BEFF',
|
||||
light: '#007ACC',
|
||||
hc: '#75BEFF'
|
||||
}, localize('symbolIcon.fieldForeground', 'The foreground color for field symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_FILE_FOREGROUND = registerColor('symbolIcon.fileForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.fileForeground', 'The foreground color for file symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_FOLDER_FOREGROUND = registerColor('symbolIcon.folderForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.folderForeground', 'The foreground color for folder symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_FUNCTION_FOREGROUND = registerColor('symbolIcon.functionForeground', {
|
||||
dark: '#B180D7',
|
||||
light: '#652D90',
|
||||
hc: '#B180D7'
|
||||
}, localize('symbolIcon.functionForeground', 'The foreground color for function symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_INTERFACE_FOREGROUND = registerColor('symbolIcon.interfaceForeground', {
|
||||
dark: '#75BEFF',
|
||||
light: '#007ACC',
|
||||
hc: '#75BEFF'
|
||||
}, localize('symbolIcon.interfaceForeground', 'The foreground color for interface symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_KEY_FOREGROUND = registerColor('symbolIcon.keyForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.keyForeground', 'The foreground color for key symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_KEYWORD_FOREGROUND = registerColor('symbolIcon.keywordForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.keywordForeground', 'The foreground color for keyword symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_METHOD_FOREGROUND = registerColor('symbolIcon.methodForeground', {
|
||||
dark: '#B180D7',
|
||||
light: '#652D90',
|
||||
hc: '#B180D7'
|
||||
}, localize('symbolIcon.methodForeground', 'The foreground color for method symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_MODULE_FOREGROUND = registerColor('symbolIcon.moduleForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.moduleForeground', 'The foreground color for module symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_NAMESPACE_FOREGROUND = registerColor('symbolIcon.namespaceForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.namespaceForeground', 'The foreground color for namespace symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_NULL_FOREGROUND = registerColor('symbolIcon.nullForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.nullForeground', 'The foreground color for null symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_NUMBER_FOREGROUND = registerColor('symbolIcon.numberForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.numberForeground', 'The foreground color for number symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_OBJECT_FOREGROUND = registerColor('symbolIcon.objectForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.objectForeground', 'The foreground color for object symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_OPERATOR_FOREGROUND = registerColor('symbolIcon.operatorForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.operatorForeground', 'The foreground color for operator symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_PACKAGE_FOREGROUND = registerColor('symbolIcon.packageForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.packageForeground', 'The foreground color for package symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_PROPERTY_FOREGROUND = registerColor('symbolIcon.propertyForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.propertyForeground', 'The foreground color for property symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_REFERENCE_FOREGROUND = registerColor('symbolIcon.referenceForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.referenceForeground', 'The foreground color for reference symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_SNIPPET_FOREGROUND = registerColor('symbolIcon.snippetForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.snippetForeground', 'The foreground color for snippet symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_STRING_FOREGROUND = registerColor('symbolIcon.stringForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.stringForeground', 'The foreground color for string symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_STRUCT_FOREGROUND = registerColor('symbolIcon.structForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.structForeground', 'The foreground color for struct symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_TEXT_FOREGROUND = registerColor('symbolIcon.textForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.textForeground', 'The foreground color for text symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_TYPEPARAMETER_FOREGROUND = registerColor('symbolIcon.typeParameterForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.typeParameterForeground', 'The foreground color for type parameter symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_UNIT_FOREGROUND = registerColor('symbolIcon.unitForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.unitForeground', 'The foreground color for unit symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_VARIABLE_FOREGROUND = registerColor('symbolIcon.variableForeground', {
|
||||
dark: '#75BEFF',
|
||||
light: '#007ACC',
|
||||
hc: '#75BEFF'
|
||||
}, localize('symbolIcon.variableForeground', 'The foreground color for variable symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||
|
||||
const symbolIconArrayColor = theme.getColor(SYMBOL_ICON_ARRAY_FOREGROUND);
|
||||
if (symbolIconArrayColor) {
|
||||
collector.addRule(`${Codicon.symbolArray.cssSelector} { color: ${symbolIconArrayColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconBooleanColor = theme.getColor(SYMBOL_ICON_BOOLEAN_FOREGROUND);
|
||||
if (symbolIconBooleanColor) {
|
||||
collector.addRule(`${Codicon.symbolBoolean.cssSelector} { color: ${symbolIconBooleanColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconClassColor = theme.getColor(SYMBOL_ICON_CLASS_FOREGROUND);
|
||||
if (symbolIconClassColor) {
|
||||
collector.addRule(`${Codicon.symbolClass.cssSelector} { color: ${symbolIconClassColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconMethodColor = theme.getColor(SYMBOL_ICON_METHOD_FOREGROUND);
|
||||
if (symbolIconMethodColor) {
|
||||
collector.addRule(`${Codicon.symbolMethod.cssSelector} { color: ${symbolIconMethodColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconColorColor = theme.getColor(SYMBOL_ICON_COLOR_FOREGROUND);
|
||||
if (symbolIconColorColor) {
|
||||
collector.addRule(`${Codicon.symbolColor.cssSelector} { color: ${symbolIconColorColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconConstantColor = theme.getColor(SYMBOL_ICON_CONSTANT_FOREGROUND);
|
||||
if (symbolIconConstantColor) {
|
||||
collector.addRule(`${Codicon.symbolConstant.cssSelector} { color: ${symbolIconConstantColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconConstructorColor = theme.getColor(SYMBOL_ICON_CONSTRUCTOR_FOREGROUND);
|
||||
if (symbolIconConstructorColor) {
|
||||
collector.addRule(`${Codicon.symbolConstructor.cssSelector} { color: ${symbolIconConstructorColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconEnumeratorColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_FOREGROUND);
|
||||
if (symbolIconEnumeratorColor) {
|
||||
collector.addRule(`
|
||||
${Codicon.symbolValue.cssSelector},${Codicon.symbolEnum.cssSelector} { color: ${symbolIconEnumeratorColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconEnumeratorMemberColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND);
|
||||
if (symbolIconEnumeratorMemberColor) {
|
||||
collector.addRule(`${Codicon.symbolEnumMember.cssSelector} { color: ${symbolIconEnumeratorMemberColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconEventColor = theme.getColor(SYMBOL_ICON_EVENT_FOREGROUND);
|
||||
if (symbolIconEventColor) {
|
||||
collector.addRule(`${Codicon.symbolEvent.cssSelector} { color: ${symbolIconEventColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconFieldColor = theme.getColor(SYMBOL_ICON_FIELD_FOREGROUND);
|
||||
if (symbolIconFieldColor) {
|
||||
collector.addRule(`${Codicon.symbolField.cssSelector} { color: ${symbolIconFieldColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconFileColor = theme.getColor(SYMBOL_ICON_FILE_FOREGROUND);
|
||||
if (symbolIconFileColor) {
|
||||
collector.addRule(`${Codicon.symbolFile.cssSelector} { color: ${symbolIconFileColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconFolderColor = theme.getColor(SYMBOL_ICON_FOLDER_FOREGROUND);
|
||||
if (symbolIconFolderColor) {
|
||||
collector.addRule(`${Codicon.symbolFolder.cssSelector} { color: ${symbolIconFolderColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconFunctionColor = theme.getColor(SYMBOL_ICON_FUNCTION_FOREGROUND);
|
||||
if (symbolIconFunctionColor) {
|
||||
collector.addRule(`${Codicon.symbolFunction.cssSelector} { color: ${symbolIconFunctionColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconInterfaceColor = theme.getColor(SYMBOL_ICON_INTERFACE_FOREGROUND);
|
||||
if (symbolIconInterfaceColor) {
|
||||
collector.addRule(`${Codicon.symbolInterface.cssSelector} { color: ${symbolIconInterfaceColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconKeyColor = theme.getColor(SYMBOL_ICON_KEY_FOREGROUND);
|
||||
if (symbolIconKeyColor) {
|
||||
collector.addRule(`${Codicon.symbolKey.cssSelector} { color: ${symbolIconKeyColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconKeywordColor = theme.getColor(SYMBOL_ICON_KEYWORD_FOREGROUND);
|
||||
if (symbolIconKeywordColor) {
|
||||
collector.addRule(`${Codicon.symbolKeyword.cssSelector} { color: ${symbolIconKeywordColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconModuleColor = theme.getColor(SYMBOL_ICON_MODULE_FOREGROUND);
|
||||
if (symbolIconModuleColor) {
|
||||
collector.addRule(`${Codicon.symbolModule.cssSelector} { color: ${symbolIconModuleColor}; }`);
|
||||
}
|
||||
|
||||
const outlineNamespaceColor = theme.getColor(SYMBOL_ICON_NAMESPACE_FOREGROUND);
|
||||
if (outlineNamespaceColor) {
|
||||
collector.addRule(`${Codicon.symbolNamespace.cssSelector} { color: ${outlineNamespaceColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconNullColor = theme.getColor(SYMBOL_ICON_NULL_FOREGROUND);
|
||||
if (symbolIconNullColor) {
|
||||
collector.addRule(`${Codicon.symbolNull.cssSelector} { color: ${symbolIconNullColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconNumberColor = theme.getColor(SYMBOL_ICON_NUMBER_FOREGROUND);
|
||||
if (symbolIconNumberColor) {
|
||||
collector.addRule(`${Codicon.symbolNumber.cssSelector} { color: ${symbolIconNumberColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconObjectColor = theme.getColor(SYMBOL_ICON_OBJECT_FOREGROUND);
|
||||
if (symbolIconObjectColor) {
|
||||
collector.addRule(`${Codicon.symbolObject.cssSelector} { color: ${symbolIconObjectColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconOperatorColor = theme.getColor(SYMBOL_ICON_OPERATOR_FOREGROUND);
|
||||
if (symbolIconOperatorColor) {
|
||||
collector.addRule(`${Codicon.symbolOperator.cssSelector} { color: ${symbolIconOperatorColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconPackageColor = theme.getColor(SYMBOL_ICON_PACKAGE_FOREGROUND);
|
||||
if (symbolIconPackageColor) {
|
||||
collector.addRule(`${Codicon.symbolPackage.cssSelector} { color: ${symbolIconPackageColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconPropertyColor = theme.getColor(SYMBOL_ICON_PROPERTY_FOREGROUND);
|
||||
if (symbolIconPropertyColor) {
|
||||
collector.addRule(`${Codicon.symbolProperty.cssSelector} { color: ${symbolIconPropertyColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconReferenceColor = theme.getColor(SYMBOL_ICON_REFERENCE_FOREGROUND);
|
||||
if (symbolIconReferenceColor) {
|
||||
collector.addRule(`${Codicon.symbolReference.cssSelector} { color: ${symbolIconReferenceColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconSnippetColor = theme.getColor(SYMBOL_ICON_SNIPPET_FOREGROUND);
|
||||
if (symbolIconSnippetColor) {
|
||||
collector.addRule(`${Codicon.symbolSnippet.cssSelector} { color: ${symbolIconSnippetColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconStringColor = theme.getColor(SYMBOL_ICON_STRING_FOREGROUND);
|
||||
if (symbolIconStringColor) {
|
||||
collector.addRule(`${Codicon.symbolString.cssSelector} { color: ${symbolIconStringColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconStructColor = theme.getColor(SYMBOL_ICON_STRUCT_FOREGROUND);
|
||||
if (symbolIconStructColor) {
|
||||
collector.addRule(`${Codicon.symbolStruct.cssSelector} { color: ${symbolIconStructColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconTextColor = theme.getColor(SYMBOL_ICON_TEXT_FOREGROUND);
|
||||
if (symbolIconTextColor) {
|
||||
collector.addRule(`${Codicon.symbolText.cssSelector} { color: ${symbolIconTextColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconTypeParameterColor = theme.getColor(SYMBOL_ICON_TYPEPARAMETER_FOREGROUND);
|
||||
if (symbolIconTypeParameterColor) {
|
||||
collector.addRule(`${Codicon.symbolTypeParameter.cssSelector} { color: ${symbolIconTypeParameterColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconUnitColor = theme.getColor(SYMBOL_ICON_UNIT_FOREGROUND);
|
||||
if (symbolIconUnitColor) {
|
||||
collector.addRule(`${Codicon.symbolUnit.cssSelector} { color: ${symbolIconUnitColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconVariableColor = theme.getColor(SYMBOL_ICON_VARIABLE_FOREGROUND);
|
||||
if (symbolIconVariableColor) {
|
||||
collector.addRule(`${Codicon.symbolVariable.cssSelector} { color: ${symbolIconVariableColor}; }`);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,187 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { OutlineElement, OutlineGroup, OutlineModel } from '../outlineModel';
|
||||
import { SymbolKind, DocumentSymbol, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
|
||||
suite('OutlineModel', function () {
|
||||
|
||||
test('OutlineModel#create, cached', async function () {
|
||||
|
||||
let model = createTextModel('foo', undefined, undefined, URI.file('/fome/path.foo'));
|
||||
let count = 0;
|
||||
let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, {
|
||||
provideDocumentSymbols() {
|
||||
count += 1;
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
await OutlineModel.create(model, CancellationToken.None);
|
||||
assert.equal(count, 1);
|
||||
|
||||
// cached
|
||||
await OutlineModel.create(model, CancellationToken.None);
|
||||
assert.equal(count, 1);
|
||||
|
||||
// new version
|
||||
model.applyEdits([{ text: 'XXX', range: new Range(1, 1, 1, 1) }]);
|
||||
await OutlineModel.create(model, CancellationToken.None);
|
||||
assert.equal(count, 2);
|
||||
|
||||
reg.dispose();
|
||||
});
|
||||
|
||||
test('OutlineModel#create, cached/cancel', async function () {
|
||||
|
||||
let model = createTextModel('foo', undefined, undefined, URI.file('/fome/path.foo'));
|
||||
let isCancelled = false;
|
||||
|
||||
let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, {
|
||||
provideDocumentSymbols(d, token) {
|
||||
return new Promise(resolve => {
|
||||
token.onCancellationRequested(_ => {
|
||||
isCancelled = true;
|
||||
resolve(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(isCancelled, false);
|
||||
let s1 = new CancellationTokenSource();
|
||||
OutlineModel.create(model, s1.token);
|
||||
let s2 = new CancellationTokenSource();
|
||||
OutlineModel.create(model, s2.token);
|
||||
|
||||
s1.cancel();
|
||||
assert.equal(isCancelled, false);
|
||||
|
||||
s2.cancel();
|
||||
assert.equal(isCancelled, true);
|
||||
|
||||
reg.dispose();
|
||||
});
|
||||
|
||||
function fakeSymbolInformation(range: Range, name: string = 'foo'): DocumentSymbol {
|
||||
return {
|
||||
name,
|
||||
detail: 'fake',
|
||||
kind: SymbolKind.Boolean,
|
||||
tags: [],
|
||||
selectionRange: range,
|
||||
range: range
|
||||
};
|
||||
}
|
||||
|
||||
function fakeMarker(range: Range): IMarker {
|
||||
return { ...range, owner: 'ffff', message: 'test', severity: MarkerSeverity.Error, resource: null! };
|
||||
}
|
||||
|
||||
test('OutlineElement - updateMarker', function () {
|
||||
|
||||
let e0 = new OutlineElement('foo1', null!, fakeSymbolInformation(new Range(1, 1, 1, 10)));
|
||||
let e1 = new OutlineElement('foo2', null!, fakeSymbolInformation(new Range(2, 1, 5, 1)));
|
||||
let e2 = new OutlineElement('foo3', null!, fakeSymbolInformation(new Range(6, 1, 10, 10)));
|
||||
|
||||
let group = new OutlineGroup('group', null!, null!, 1);
|
||||
group.children.set(e0.id, e0);
|
||||
group.children.set(e1.id, e1);
|
||||
group.children.set(e2.id, e2);
|
||||
|
||||
const data = [fakeMarker(new Range(6, 1, 6, 7)), fakeMarker(new Range(1, 1, 1, 4)), fakeMarker(new Range(10, 2, 14, 1))];
|
||||
data.sort(Range.compareRangesUsingStarts); // model does this
|
||||
|
||||
group.updateMarker(data);
|
||||
assert.equal(data.length, 0); // all 'stolen'
|
||||
assert.equal(e0.marker!.count, 1);
|
||||
assert.equal(e1.marker, undefined);
|
||||
assert.equal(e2.marker!.count, 2);
|
||||
|
||||
group.updateMarker([]);
|
||||
assert.equal(e0.marker, undefined);
|
||||
assert.equal(e1.marker, undefined);
|
||||
assert.equal(e2.marker, undefined);
|
||||
});
|
||||
|
||||
test('OutlineElement - updateMarker, 2', function () {
|
||||
|
||||
let p = new OutlineElement('A', null!, fakeSymbolInformation(new Range(1, 1, 11, 1)));
|
||||
let c1 = new OutlineElement('A/B', null!, fakeSymbolInformation(new Range(2, 4, 5, 4)));
|
||||
let c2 = new OutlineElement('A/C', null!, fakeSymbolInformation(new Range(6, 4, 9, 4)));
|
||||
|
||||
let group = new OutlineGroup('group', null!, null!, 1);
|
||||
group.children.set(p.id, p);
|
||||
p.children.set(c1.id, c1);
|
||||
p.children.set(c2.id, c2);
|
||||
|
||||
let data = [
|
||||
fakeMarker(new Range(2, 4, 5, 4))
|
||||
];
|
||||
|
||||
group.updateMarker(data);
|
||||
assert.equal(p.marker!.count, 0);
|
||||
assert.equal(c1.marker!.count, 1);
|
||||
assert.equal(c2.marker, undefined);
|
||||
|
||||
data = [
|
||||
fakeMarker(new Range(2, 4, 5, 4)),
|
||||
fakeMarker(new Range(2, 6, 2, 8)),
|
||||
fakeMarker(new Range(7, 6, 7, 8)),
|
||||
];
|
||||
group.updateMarker(data);
|
||||
assert.equal(p.marker!.count, 0);
|
||||
assert.equal(c1.marker!.count, 2);
|
||||
assert.equal(c2.marker!.count, 1);
|
||||
|
||||
data = [
|
||||
fakeMarker(new Range(1, 4, 1, 11)),
|
||||
fakeMarker(new Range(7, 6, 7, 8)),
|
||||
];
|
||||
group.updateMarker(data);
|
||||
assert.equal(p.marker!.count, 1);
|
||||
assert.equal(c1.marker, undefined);
|
||||
assert.equal(c2.marker!.count, 1);
|
||||
});
|
||||
|
||||
test('OutlineElement - updateMarker/multiple groups', function () {
|
||||
|
||||
let model = new class extends OutlineModel {
|
||||
constructor() {
|
||||
super(null!);
|
||||
}
|
||||
readyForTesting() {
|
||||
this._groups = this.children as any;
|
||||
}
|
||||
};
|
||||
model.children.set('g1', new OutlineGroup('g1', model, null!, 1));
|
||||
model.children.get('g1')!.children.set('c1', new OutlineElement('c1', model.children.get('g1')!, fakeSymbolInformation(new Range(1, 1, 11, 1))));
|
||||
|
||||
model.children.set('g2', new OutlineGroup('g2', model, null!, 1));
|
||||
model.children.get('g2')!.children.set('c2', new OutlineElement('c2', model.children.get('g2')!, fakeSymbolInformation(new Range(1, 1, 7, 1))));
|
||||
model.children.get('g2')!.children.get('c2')!.children.set('c2.1', new OutlineElement('c2.1', model.children.get('g2')!.children.get('c2')!, fakeSymbolInformation(new Range(1, 3, 2, 19))));
|
||||
model.children.get('g2')!.children.get('c2')!.children.set('c2.2', new OutlineElement('c2.2', model.children.get('g2')!.children.get('c2')!, fakeSymbolInformation(new Range(4, 1, 6, 10))));
|
||||
|
||||
model.readyForTesting();
|
||||
|
||||
const data = [
|
||||
fakeMarker(new Range(1, 1, 2, 8)),
|
||||
fakeMarker(new Range(6, 1, 6, 98)),
|
||||
];
|
||||
|
||||
model.updateMarker(data);
|
||||
|
||||
assert.equal(model.children.get('g1')!.children.get('c1')!.marker!.count, 2);
|
||||
assert.equal(model.children.get('g2')!.children.get('c2')!.children.get('c2.1')!.marker!.count, 1);
|
||||
assert.equal(model.children.get('g2')!.children.get('c2')!.children.get('c2.2')!.marker!.count, 1);
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user