Merge remote-tracking branch 'upstream/master' into file_as_folder_in_custom_tree

This commit is contained in:
Julien Roncaglia
2018-02-23 22:01:38 +01:00
285 changed files with 4083 additions and 1632 deletions

View File

@@ -39,7 +39,7 @@ import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import URI from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
@@ -292,6 +292,9 @@ export function createApiFactory(
registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
return extHostLanguageFeatures.registerColorProvider(selector, provider);
},
registerFoldingProvider: proposedApiFunction(extension, (selector: vscode.DocumentSelector, provider: vscode.FoldingProvider): vscode.Disposable => {
return extHostLanguageFeatures.registerFoldingProvider(selector, provider);
}),
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => {
return extHostLanguageFeatures.setLanguageConfiguration(language, configuration);
}
@@ -389,8 +392,8 @@ export function createApiFactory(
}
return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs);
},
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider);
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.TreeView<any> {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, (fn) => proposedApiFunction(extension, fn));
},
// proposed API
sampleFunction: proposedApiFunction(extension, () => {
@@ -635,7 +638,10 @@ export function createApiFactory(
RelativePattern: extHostTypes.RelativePattern,
FileChangeType: extHostTypes.FileChangeType,
FileType: extHostTypes.FileType
FileType: extHostTypes.FileType,
FoldingRangeList: extHostTypes.FoldingRangeList,
FoldingRange: extHostTypes.FoldingRange,
FoldingRangeType: extHostTypes.FoldingRangeType
};
};
}

View File

@@ -14,7 +14,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
@@ -216,6 +216,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
export interface MainThreadTreeViewsShape extends IDisposable {
$registerTreeViewDataProvider(treeViewId: string): void;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): void;
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options?: { donotSelect?: boolean }): TPromise<void>;
}
export interface MainThreadErrorsShape extends IDisposable {
@@ -282,6 +283,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): void;
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): void;
$registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): void;
$registerFoldingProvider(handle: number, selector: vscode.DocumentSelector): void;
$setLanguageConfiguration(handle: number, languageId: string, configuration: ISerializedLanguageConfiguration): void;
}
@@ -692,6 +694,7 @@ export interface ExtHostLanguageFeaturesShape {
$resolveDocumentLink(handle: number, link: modes.ILink): TPromise<modes.ILink>;
$provideDocumentColors(handle: number, resource: UriComponents): TPromise<IRawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo): TPromise<modes.IColorPresentation[]>;
$provideFoldingRanges(handle: number, resource: UriComponents): TPromise<modes.IFoldingRangeList>;
}
export interface ExtHostQuickOpenShape {

View File

@@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
import { LinkedList } from 'vs/base/common/linkedList';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
type Listener = [Function, any, IExtensionDescription];

View File

@@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtHostLogger } from 'vs/workbench/api/node/extHostLogService';
const hasOwnProperty = Object.hasOwnProperty;

View File

@@ -10,7 +10,7 @@ import { mkdirp, dirExists, realpath, writeFile } from 'vs/base/node/pfs';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
import { createApiFactory, initializeExtensionApi, checkProposedApiEnabled } from 'vs/workbench/api/node/extHost.api.impl';
import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IExtHostContext } from './extHost.protocol';

View File

@@ -804,10 +804,29 @@ class ColorProviderAdapter {
}
}
class FoldingProviderAdapter {
constructor(
private _documents: ExtHostDocuments,
private _provider: vscode.FoldingProvider
) { }
provideFoldingRanges(resource: URI): TPromise<modes.IFoldingRangeList> {
const doc = this._documents.getDocumentData(resource).document;
return asWinJsPromise(token => this._provider.provideFoldingRanges(doc, token)).then(list => {
if (!Array.isArray(list.ranges)) {
return void 0;
}
return TypeConverters.FoldingRangeList.from(list);
});
}
}
type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter;
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter
| ColorProviderAdapter | FoldingProviderAdapter;
export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
@@ -1108,6 +1127,16 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo));
}
registerFoldingProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingProvider): vscode.Disposable {
const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider));
this._proxy.$registerFoldingProvider(handle, selector);
return this._createDisposable(handle);
}
$provideFoldingRanges(handle: number, resource: UriComponents): TPromise<modes.IFoldingRangeList> {
return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource)));
}
// --- configuration
private static _serializeRegExp(regExp: RegExp): ISerializedRegExp {

View File

@@ -7,7 +7,7 @@
import Severity from 'vs/base/common/severity';
import vscode = require('vscode');
import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
function isMessageItem(item: any): item is vscode.MessageItem {
return item && item.title;

View File

@@ -7,7 +7,7 @@
import { Progress, ProgressOptions, CancellationToken } from 'vscode';
import { MainThreadProgressShape } from './extHost.protocol';
import { ProgressLocation } from './extHostTypeConverters';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IProgressStep } from 'vs/platform/progress/common/progress';
export class ExtHostProgress {

View File

@@ -10,7 +10,7 @@ import Event, { Emitter, once } from 'vs/base/common/event';
import { debounce } from 'vs/base/common/decorators';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { asWinJsPromise } from 'vs/base/common/async';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape } from './extHost.protocol';
import { sortedDiff } from 'vs/base/common/arrays';

View File

@@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import * as Objects from 'vs/base/common/objects';
import { asWinJsPromise } from 'vs/base/common/async';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as TaskSystem from 'vs/workbench/parts/tasks/common/tasks';
import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';

View File

@@ -15,7 +15,6 @@ import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.proto
import { ITreeItem, TreeViewItemHandleArg, IThemeIcon } from 'vs/workbench/common/views';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { asWinJsPromise } from 'vs/base/common/async';
import { coalesce } from 'vs/base/common/arrays';
import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/node/extHostTypes';
import { isUndefinedOrNull } from 'vs/base/common/types';
@@ -39,10 +38,12 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
});
}
registerTreeDataProvider<T>(id: string, treeDataProvider: vscode.TreeDataProvider<T>): vscode.Disposable {
const treeView = new ExtHostTreeView<T>(id, treeDataProvider, this._proxy, this.commands.converter);
this.treeViews.set(id, treeView);
registerTreeDataProvider<T>(id: string, dataProvider: vscode.TreeDataProvider<T>, proposedApiFunction: <U>(fn: U) => U): vscode.TreeView<T> {
const treeView = this.createExtHostTreeViewer(id, dataProvider);
return {
reveal: proposedApiFunction((element: T, options?: { donotSelect?: boolean }): Thenable<void> => {
return treeView.reveal(element, options);
}),
dispose: () => {
this.treeViews.delete(id);
treeView.dispose();
@@ -58,6 +59,12 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
return treeView.getChildren(treeItemHandle);
}
private createExtHostTreeViewer<T>(id: string, dataProvider: vscode.TreeDataProvider<T>): ExtHostTreeView<T> {
const treeView = new ExtHostTreeView<T>(id, dataProvider, this._proxy, this.commands.converter);
this.treeViews.set(id, treeView);
return treeView;
}
private convertArgument(arg: TreeViewItemHandleArg): any {
const treeView = this.treeViews.get(arg.$treeViewId);
return treeView ? treeView.getExtensionElement(arg.$treeItemHandle) : null;
@@ -65,9 +72,9 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
}
interface TreeNode {
handle: TreeItemHandle;
parentHandle: TreeItemHandle;
childrenHandles: TreeItemHandle[];
item: ITreeItem;
parent: TreeNode;
children: TreeNode[];
}
class ExtHostTreeView<T> extends Disposable {
@@ -75,15 +82,15 @@ class ExtHostTreeView<T> extends Disposable {
private static LABEL_HANDLE_PREFIX = '0';
private static ID_HANDLE_PREFIX = '1';
private rootHandles: TreeItemHandle[] = [];
private roots: TreeNode[] = null;
private elements: Map<TreeItemHandle, T> = new Map<TreeItemHandle, T>();
private nodes: Map<T, TreeNode> = new Map<T, TreeNode>();
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) {
super();
this.proxy.$registerTreeViewDataProvider(viewId);
if (dataProvider.onDidChangeTreeData) {
this._register(debounceEvent<T, T[]>(dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this.refresh(elements)));
if (this.dataProvider.onDidChangeTreeData) {
this._register(debounceEvent<T, T[]>(this.dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this.refresh(elements)));
}
}
@@ -94,30 +101,95 @@ class ExtHostTreeView<T> extends Disposable {
return TPromise.as([]);
}
this.clearChildren(parentElement);
return asWinJsPromise(() => this.dataProvider.getChildren(parentElement))
.then(elements => TPromise.join(
coalesce(elements || []).map(element =>
asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => {
if (extTreeItem) {
if (extTreeItem.id && this.elements.has(this.createHandle(element, extTreeItem))) {
throw new Error(localize('treeView.duplicateElement', 'Element with id {0} is already registered', extTreeItem.id));
}
return { element, extTreeItem };
}
return null;
})
))).then(extTreeItems => coalesce(extTreeItems).map((({ element, extTreeItem }) => this.createTreeItem(element, extTreeItem, parentHandle))));
const childrenNodes = this.getChildrenNodes(parentHandle); // Get it from cache
return (childrenNodes ? TPromise.as(childrenNodes) : this.fetchChildrenNodes(parentElement))
.then(nodes => nodes.map(n => n.item));
}
getExtensionElement(treeItemHandle: TreeItemHandle): T {
return this.elements.get(treeItemHandle);
}
reveal(element: T, options?: { donotSelect?: boolean }): TPromise<void> {
if (typeof this.dataProvider.getParent !== 'function') {
return TPromise.wrapError(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' mehtod`));
}
return this.resolveUnknownParentChain(element)
.then(parentChain => this.resolveTreeItem(element, parentChain[parentChain.length - 1])
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), options)));
}
private resolveUnknownParentChain(element: T): TPromise<TreeNode[]> {
return this.resolveParent(element)
.then((parent) => {
if (!parent) {
return TPromise.as([]);
}
return this.resolveUnknownParentChain(parent)
.then(result => this.resolveTreeItem(parent, result[result.length - 1])
.then(parentNode => {
result.push(parentNode);
return result;
}));
});
}
private resolveParent(element: T): TPromise<T> {
const node = this.nodes.get(element);
if (node) {
return TPromise.as(node.parent ? this.elements.get(node.parent.item.handle) : null);
}
return asWinJsPromise(() => this.dataProvider.getParent(element));
}
private resolveTreeItem(element: T, parent?: TreeNode): TPromise<TreeNode> {
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => this.createHandle(element, extTreeItem, parent))
.then(handle => this.getChildren(parent ? parent.item.handle : null)
.then(() => {
const cachedElement = this.getExtensionElement(handle);
if (cachedElement) {
const node = this.nodes.get(cachedElement);
if (node) {
return TPromise.as(node);
}
}
throw new Error(`Cannot resolve tree item for element ${handle}`);
}));
}
private getChildrenNodes(parentNodeOrHandle?: TreeNode | TreeItemHandle): TreeNode[] {
if (parentNodeOrHandle) {
let parentNode: TreeNode;
if (typeof parentNodeOrHandle === 'string') {
const parentElement = this.getExtensionElement(parentNodeOrHandle);
parentNode = parentElement ? this.nodes.get(parentElement) : null;
} else {
parentNode = parentNodeOrHandle;
}
return parentNode ? parentNode.children : null;
}
return this.roots;
}
private fetchChildrenNodes(parentElement?: T): TPromise<TreeNode[]> {
// clear children cache
this.clearChildren(parentElement);
const parentNode = parentElement ? this.nodes.get(parentElement) : void 0;
return asWinJsPromise(() => this.dataProvider.getChildren(parentElement))
.then(elements => TPromise.join(
(elements || [])
.filter(element => !!element)
.map(element => asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => extTreeItem ? this.createAndRegisterTreeNode(element, extTreeItem, parentNode) : null))))
.then(nodes => nodes.filter(n => !!n));
}
private refresh(elements: T[]): void {
const hasRoot = elements.some(element => !element);
if (hasRoot) {
this.clearAll(); // clear cache
this.proxy.$refresh(this.viewId);
} else {
const handlesToRefresh = this.getHandlesToRefresh(elements);
@@ -131,15 +203,15 @@ class ExtHostTreeView<T> extends Disposable {
const elementsToUpdate = new Set<TreeItemHandle>();
for (const element of elements) {
let elementNode = this.nodes.get(element);
if (elementNode && !elementsToUpdate.has(elementNode.handle)) {
if (elementNode && !elementsToUpdate.has(elementNode.item.handle)) {
// check if an ancestor of extElement is already in the elements to update list
let currentNode = elementNode;
while (currentNode && currentNode.parentHandle && !elementsToUpdate.has(currentNode.parentHandle)) {
const parentElement = this.elements.get(currentNode.parentHandle);
while (currentNode && currentNode.parent && !elementsToUpdate.has(currentNode.parent.item.handle)) {
const parentElement = this.elements.get(currentNode.parent.item.handle);
currentNode = this.nodes.get(parentElement);
}
if (!currentNode.parentHandle) {
elementsToUpdate.add(elementNode.handle);
if (!currentNode.parent) {
elementsToUpdate.add(elementNode.item.handle);
}
}
}
@@ -149,7 +221,7 @@ class ExtHostTreeView<T> extends Disposable {
elementsToUpdate.forEach((handle) => {
const element = this.elements.get(handle);
let node = this.nodes.get(element);
if (node && !elementsToUpdate.has(node.parentHandle)) {
if (node && (!node.parent || !elementsToUpdate.has(node.parent.item.handle))) {
handlesToUpdate.push(handle);
}
});
@@ -158,31 +230,57 @@ class ExtHostTreeView<T> extends Disposable {
}
private refreshHandles(itemHandles: TreeItemHandle[]): TPromise<void> {
const itemsToRefresh: { [handle: string]: ITreeItem } = {};
const promises: TPromise<void>[] = [];
itemHandles.forEach(treeItemHandle => {
const extElement = this.getExtensionElement(treeItemHandle);
const node = this.nodes.get(extElement);
promises.push(asWinJsPromise(() => this.dataProvider.getTreeItem(extElement))
.then(extTreeItem => {
if (extTreeItem) {
itemsToRefresh[treeItemHandle] = this.createTreeItem(extElement, extTreeItem, node.parentHandle);
const itemsToRefresh: { [treeItemHandle: string]: ITreeItem } = {};
return TPromise.join(itemHandles.map(treeItemHandle =>
this.refreshNode(treeItemHandle)
.then(node => {
if (node) {
itemsToRefresh[treeItemHandle] = node.item;
}
}));
});
return TPromise.join(promises)
.then(treeItems => this.proxy.$refresh(this.viewId, itemsToRefresh));
})))
.then(() => Object.keys(itemsToRefresh).length ? this.proxy.$refresh(this.viewId, itemsToRefresh) : null);
}
private createTreeItem(element: T, extensionTreeItem: vscode.TreeItem, parentHandle: TreeItemHandle): ITreeItem {
private refreshNode(treeItemHandle: TreeItemHandle): TPromise<TreeNode> {
const extElement = this.getExtensionElement(treeItemHandle);
const existing = this.nodes.get(extElement);
this.clearChildren(extElement); // clear children cache
return asWinJsPromise(() => this.dataProvider.getTreeItem(extElement))
.then(extTreeItem => {
if (extTreeItem) {
const newNode = this.createTreeNode(extElement, extTreeItem, existing.parent);
this.updateNodeCache(extElement, newNode, existing, existing.parent);
return newNode;
}
return null;
});
}
const handle = this.createHandle(element, extensionTreeItem, parentHandle);
const icon = this.getLightIconPath(extensionTreeItem);
this.update(element, handle, parentHandle);
private createAndRegisterTreeNode(element: T, extTreeItem: vscode.TreeItem, parentNode: TreeNode): TreeNode {
const node = this.createTreeNode(element, extTreeItem, parentNode);
if (extTreeItem.id && this.elements.has(node.item.handle)) {
throw new Error(localize('treeView.duplicateElement', 'Element with id {0} is already registered', extTreeItem.id));
}
this.addNodeToCache(element, node);
this.addNodeToParentCache(node, parentNode);
return node;
}
private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode): TreeNode {
return {
item: this.createTreeItem(element, extensionTreeItem, parent),
parent,
children: void 0
};
}
private createTreeItem(element: T, extensionTreeItem: vscode.TreeItem, parent?: TreeNode): ITreeItem {
const handle = this.createHandle(element, extensionTreeItem, parent);
const icon = this.getLightIconPath(extensionTreeItem);
const item = {
handle,
parentHandle,
parentHandle: parent ? parent.item.handle : void 0,
label: extensionTreeItem.label,
resourceUri: extensionTreeItem.resourceUri,
tooltip: typeof extensionTreeItem.tooltip === 'string' ? extensionTreeItem.tooltip : void 0,
@@ -192,19 +290,22 @@ class ExtHostTreeView<T> extends Disposable {
iconDark: this.getDarkIconPath(extensionTreeItem) || icon,
collapsibleState: isUndefinedOrNull(extensionTreeItem.collapsibleState) ? TreeItemCollapsibleState.None : extensionTreeItem.collapsibleState
};
return item;
}
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parentHandle?: TreeItemHandle): TreeItemHandle {
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent?: TreeNode): TreeItemHandle {
if (id) {
return `${ExtHostTreeView.ID_HANDLE_PREFIX}/${id}`;
}
const prefix = parentHandle ? parentHandle : ExtHostTreeView.LABEL_HANDLE_PREFIX;
const prefix: string = parent ? parent.item.handle : ExtHostTreeView.LABEL_HANDLE_PREFIX;
let elementId = label ? label : resourceUri ? basename(resourceUri.path) : '';
elementId = elementId.indexOf('/') !== -1 ? elementId.replace('/', '//') : elementId;
const existingHandle = this.nodes.has(element) ? this.nodes.get(element).handle : void 0;
const existingHandle = this.nodes.has(element) ? this.nodes.get(element).item.handle : void 0;
const childrenNodes = (this.getChildrenNodes(parent) || []);
for (let counter = 0; counter <= this.getChildrenHandles(parentHandle).length; counter++) {
for (let counter = 0; counter <= childrenNodes.length; counter++) {
const handle = `${prefix}/${counter}:${elementId}`;
if (!this.elements.has(handle) || existingHandle === handle) {
return handle;
@@ -243,48 +344,56 @@ class ExtHostTreeView<T> extends Disposable {
return URI.file(iconPath).toString();
}
private getChildrenHandles(parentHandle?: TreeItemHandle): TreeItemHandle[] {
return parentHandle ? this.nodes.get(this.getExtensionElement(parentHandle)).childrenHandles : this.rootHandles;
private addNodeToCache(element: T, node: TreeNode): void {
this.elements.set(node.item.handle, element);
this.nodes.set(element, node);
}
private update(element: T, handle: TreeItemHandle, parentHandle: TreeItemHandle): void {
const node = this.nodes.get(element);
const childrenHandles = this.getChildrenHandles(parentHandle);
// Update parent node
if (node) {
if (node.handle !== handle) {
// Remove the old handle from the system
this.elements.delete(node.handle);
childrenHandles[childrenHandles.indexOf(node.handle)] = handle;
this.clearChildren(element);
}
} else {
childrenHandles.push(handle);
private updateNodeCache(element: T, newNode: TreeNode, existing: TreeNode, parentNode: TreeNode): void {
// Remove from the cache
this.elements.delete(newNode.item.handle);
this.nodes.delete(element);
if (newNode.item.handle !== existing.item.handle) {
this.elements.delete(existing.item.handle);
}
// Update element maps
this.elements.set(handle, element);
this.nodes.set(element, {
handle,
parentHandle,
childrenHandles: node ? node.childrenHandles : []
});
// Add the new node to the cache
this.addNodeToCache(element, newNode);
// Replace the node in parent's children nodes
const childrenNodes = (this.getChildrenNodes(parentNode) || []);
const childNode = childrenNodes.filter(c => c.item.handle === existing.item.handle)[0];
if (childNode) {
childrenNodes.splice(childrenNodes.indexOf(childNode), 1, newNode);
}
}
private addNodeToParentCache(node: TreeNode, parentNode: TreeNode): void {
if (parentNode) {
if (!parentNode.children) {
parentNode.children = [];
}
parentNode.children.push(node);
} else {
if (!this.roots) {
this.roots = [];
}
this.roots.push(node);
}
}
private clearChildren(parentElement?: T): void {
if (parentElement) {
let node = this.nodes.get(parentElement);
if (node.childrenHandles) {
for (const childHandle of node.childrenHandles) {
const childEleement = this.elements.get(childHandle);
if (node.children) {
for (const child of node.children) {
const childEleement = this.elements.get(child.item.handle);
if (childEleement) {
this.clear(childEleement);
}
}
}
node.childrenHandles = [];
node.children = [];
} else {
this.clearAll();
}
@@ -292,20 +401,20 @@ class ExtHostTreeView<T> extends Disposable {
private clear(element: T): void {
let node = this.nodes.get(element);
if (node.childrenHandles) {
for (const childHandle of node.childrenHandles) {
const childEleement = this.elements.get(childHandle);
if (node.children) {
for (const child of node.children) {
const childEleement = this.elements.get(child.item.handle);
if (childEleement) {
this.clear(childEleement);
}
}
}
this.nodes.delete(element);
this.elements.delete(node.handle);
this.elements.delete(node.item.handle);
}
private clearAll(): void {
this.rootHandles = [];
this.roots = null;
this.elements.clear();
this.nodes.clear();
}

View File

@@ -586,6 +586,14 @@ export namespace ProgressLocation {
}
}
export namespace FoldingRangeList {
export function from(rangeList: vscode.FoldingRangeList): modes.IFoldingRangeList {
return {
ranges: rangeList.ranges.map(r => ({ startLineNumber: r.startLine + 1, endLineNumber: r.endLine + 1, type: r.type }))
};
}
}
export function toTextEditorOptions(options?: vscode.TextDocumentShowOptions): ITextEditorOptions {
if (options) {
return {

View File

@@ -1671,3 +1671,46 @@ export enum FileType {
}
//#endregion
//#region folding api
export class FoldingRangeList {
ranges: FoldingRange[];
constructor(ranges: FoldingRange[]) {
this.ranges = ranges;
}
}
export class FoldingRange {
startLine: number;
endLine: number;
type?: FoldingRangeType | string;
constructor(startLine: number, endLine: number, type?: FoldingRangeType | string) {
this.startLine = startLine;
this.endLine = endLine;
this.type = type;
}
}
export enum FoldingRangeType {
/**
* Folding range for a comment
*/
Comment = 'comment',
/**
* Folding range for a imports or includes
*/
Imports = 'imports',
/**
* Folding range for a region (e.g. `#region`)
*/
Region = 'region'
}
//#endregion

View File

@@ -16,7 +16,7 @@ import { compare } from 'vs/base/common/strings';
import { TernarySearchTree } from 'vs/base/common/map';
import { basenameOrAuthority, isEqual } from 'vs/base/common/resources';
import { isLinux } from 'vs/base/common/platform';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { localize } from 'vs/nls';
import { Severity } from 'vs/platform/notification/common/notification';