Merge branch 'master' into resolveInitialRenameValue

This commit is contained in:
Johannes Rieken
2018-01-26 17:08:21 +01:00
committed by GitHub
1428 changed files with 26292 additions and 21150 deletions

View File

@@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { localize } from 'vs/nls';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
namespace schema {
// --localizations contribution point
export interface ILocalizationDescriptor {
languageId: string;
languageName: string;
translations: string;
}
export function validateLocalizationDescriptors(localizationDescriptors: ILocalizationDescriptor[], collector: ExtensionMessageCollector): boolean {
if (!Array.isArray(localizationDescriptors)) {
collector.error(localize('requirearray', "localizations must be an array"));
return false;
}
for (let descriptor of localizationDescriptors) {
if (typeof descriptor.languageId !== 'string') {
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'languageId'));
return false;
}
if (typeof descriptor.languageName !== 'string') {
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'languageName'));
return false;
}
if (descriptor.translations && typeof descriptor.translations !== 'string') {
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'translations'));
return false;
}
}
return true;
}
export const localizationsContribution: IJSONSchema = {
description: localize('vscode.extension.contributes.localizations', "Contributes localizations to the editor"),
type: 'array',
items: {
type: 'object',
properties: {
id: {
description: localize('vscode.extension.contributes.localizations.languageId', 'Id of the language into which the display strings are translated.'),
type: 'string'
},
name: {
description: localize('vscode.extension.contributes.localizations.languageName', 'Name of the language into which the display strings are translated.'),
type: 'string'
},
translations: {
description: localize('vscode.extension.contributes.localizations.translations', 'A relative path to the folder containing all translation files for the contributed language.'),
type: 'string',
default: 'translations'
}
}
}
};
}
ExtensionsRegistry.registerExtensionPoint<schema.ILocalizationDescriptor[]>('localizations', [], schema.localizationsContribution)
.setHandler((extensions) => extensions.forEach(extension => schema.validateLocalizationDescriptors(extension.value, extension.collector)));

View File

@@ -46,6 +46,7 @@ import './mainThreadTask';
import './mainThreadTelemetry';
import './mainThreadTerminalService';
import './mainThreadTreeViews';
import './mainThreadLogService';
import './mainThreadWindow';
import './mainThreadWorkspace';

View File

@@ -6,10 +6,13 @@
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import uri from 'vs/base/common/uri';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint } from 'vs/workbench/parts/debug/common/debug';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IRawBreakpoint } from 'vs/workbench/parts/debug/common/debug';
import { TPromise } from 'vs/base/common/winjs.base';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, IExtHostContext, IBreakpointsDelta, ISourceBreakpointData, IFunctionBreakpointData } from '../node/extHost.protocol';
import {
ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext,
IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto
} from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import severity from 'vs/base/common/severity';
@@ -59,15 +62,15 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
// set up a handler to send more
this._toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(e => {
if (e) {
const delta: IBreakpointsDelta = {};
const delta: IBreakpointsDeltaDto = {};
if (e.added) {
delta.added = this.toWire(e.added);
delta.added = this.convertToDto(e.added);
}
if (e.removed) {
delta.removed = e.removed.map(x => x.getId());
}
if (e.changed) {
delta.changed = this.toWire(e.changed);
delta.changed = this.convertToDto(e.changed);
}
if (delta.added || delta.removed || delta.changed) {
@@ -81,7 +84,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
const fbps = this.debugService.getModel().getFunctionBreakpoints();
if (bps.length > 0 || fbps.length > 0) {
this._proxy.$acceptBreakpointsDelta({
added: this.toWire(bps).concat(this.toWire(fbps))
added: this.convertToDto(bps).concat(this.convertToDto(fbps))
});
}
}
@@ -89,30 +92,57 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
return TPromise.wrap<void>(undefined);
}
private toWire(bps: (IBreakpoint | IFunctionBreakpoint)[]): (ISourceBreakpointData | IFunctionBreakpointData)[] {
public $registerBreakpoints(DTOs: (ISourceMultiBreakpointDto | IFunctionBreakpointDto)[]): TPromise<void> {
for (let dto of DTOs) {
if (dto.type === 'sourceMulti') {
const rawbps = dto.lines.map(l =>
<IRawBreakpoint>{
id: l.id,
enabled: l.enabled,
lineNumber: l.line + 1,
column: l.character > 0 ? l.character + 1 : 0,
condition: l.condition,
hitCondition: l.hitCondition
}
);
this.debugService.addBreakpoints(uri.revive(dto.uri), rawbps);
} else if (dto.type === 'function') {
this.debugService.addFunctionBreakpoint(dto.functionName, dto.id);
}
}
return void 0;
}
public $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): TPromise<void> {
breakpointIds.forEach(id => this.debugService.removeBreakpoints(id));
functionBreakpointIds.forEach(id => this.debugService.removeFunctionBreakpoints(id));
return void 0;
}
private convertToDto(bps: (IBreakpoint | IFunctionBreakpoint)[]): (ISourceBreakpointDto | IFunctionBreakpointDto)[] {
return bps.map(bp => {
if ('name' in bp) {
const fbp = <IFunctionBreakpoint>bp;
return <IFunctionBreakpointData>{
return <IFunctionBreakpointDto>{
type: 'function',
id: bp.getId(),
enabled: bp.enabled,
id: fbp.getId(),
enabled: fbp.enabled,
functionName: fbp.name,
hitCondition: bp.hitCondition,
/* condition: bp.condition */
hitCondition: fbp.hitCondition,
/* condition: fbp.condition */
};
} else {
const sbp = <IBreakpoint>bp;
return <ISourceBreakpointData>{
return <ISourceBreakpointDto>{
type: 'source',
id: bp.getId(),
enabled: bp.enabled,
id: sbp.getId(),
enabled: sbp.enabled,
condition: sbp.condition,
hitCondition: bp.hitCondition,
hitCondition: sbp.hitCondition,
uri: sbp.uri,
line: sbp.lineNumber > 0 ? sbp.lineNumber - 1 : 0,
character: (typeof sbp.column === 'number' && sbp.column > 0) ? sbp.column - 1 : 0
character: (typeof sbp.column === 'number' && sbp.column > 0) ? sbp.column - 1 : 0,
};
}
});

View File

@@ -15,17 +15,18 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { MainThreadTextEditor } from './mainThreadEditor';
import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions } from 'vs/workbench/api/node/extHost.protocol';
import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions, WorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors';
import { equals as objectEquals } from 'vs/base/common/objects';
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext, IWorkspaceResourceEdit } from '../node/extHost.protocol';
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext } from '../node/extHost.protocol';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IFileService } from 'vs/platform/files/common/files';
import { bulkEdit, IResourceEdit } from 'vs/editor/browser/services/bulkEdit';
import { BulkEdit } from 'vs/editor/browser/services/bulkEdit';
import { IModelService } from 'vs/editor/common/services/modelService';
import { isCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { isResourceFileEdit } from 'vs/editor/common/modes';
export class MainThreadEditors implements MainThreadEditorsShape {
@@ -210,40 +211,22 @@ export class MainThreadEditors implements MainThreadEditorsShape {
return TPromise.as(this._documentsAndEditors.getEditor(id).applyEdits(modelVersionId, edits, opts));
}
$tryApplyWorkspaceEdit(workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean> {
$tryApplyWorkspaceEdit(dto: WorkspaceEditDto): TPromise<boolean> {
const { edits } = reviveWorkspaceEditDto(dto);
// First check if loaded models were not changed in the meantime
for (let i = 0, len = workspaceResourceEdits.length; i < len; i++) {
const workspaceResourceEdit = workspaceResourceEdits[i];
if (workspaceResourceEdit.modelVersionId) {
const uri = URI.revive(workspaceResourceEdit.resource);
let model = this._modelService.getModel(uri);
if (model && model.getVersionId() !== workspaceResourceEdit.modelVersionId) {
for (let i = 0, len = edits.length; i < len; i++) {
const edit = edits[i];
if (!isResourceFileEdit(edit) && edit.modelVersionId) {
let model = this._modelService.getModel(edit.resource);
if (model && model.getVersionId() !== edit.modelVersionId) {
// model changed in the meantime
return TPromise.as(false);
}
}
}
// Convert to shape expected by bulkEdit below
let resourceEdits: IResourceEdit[] = [];
for (let i = 0, len = workspaceResourceEdits.length; i < len; i++) {
const workspaceResourceEdit = workspaceResourceEdits[i];
const uri = URI.revive(workspaceResourceEdit.resource);
const edits = workspaceResourceEdit.edits;
for (let j = 0, lenJ = edits.length; j < lenJ; j++) {
const edit = edits[j];
resourceEdits.push({
resource: uri,
newText: edit.newText,
newEol: edit.newEol,
range: edit.range
});
}
}
let codeEditor: ICodeEditor;
let editor = this._workbenchEditorService.getActiveEditor();
if (editor) {
@@ -253,8 +236,7 @@ export class MainThreadEditors implements MainThreadEditorsShape {
}
}
return bulkEdit(this._textModelResolverService, codeEditor, resourceEdits, this._fileService)
.then(() => true);
return BulkEdit.perform(edits, this._textModelResolverService, this._fileService, codeEditor).then(() => true);
}
$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): TPromise<boolean> {

View File

@@ -16,6 +16,7 @@ import { ISearchResultProvider, ISearchQuery, ISearchComplete, ISearchProgressIt
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { onUnexpectedError } from 'vs/base/common/errors';
import { values } from 'vs/base/common/map';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
@extHostNamedCustomer(MainContext.MainThreadFileSystem)
export class MainThreadFileSystem implements MainThreadFileSystemShape {
@@ -103,12 +104,12 @@ class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProv
constructor(
fileService: IFileService,
searchService: ISearchService,
scheme: string,
private readonly _scheme: string,
private readonly _handle: number,
private readonly _proxy: ExtHostFileSystemShape
) {
this._registrations = [
fileService.registerProvider(scheme, this),
fileService.registerProvider(_scheme, this),
searchService.registerSearchResultProvider(this),
];
}
@@ -170,6 +171,20 @@ class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProv
search(query: ISearchQuery): PPromise<ISearchComplete, ISearchProgressItem> {
if (isFalsyOrEmpty(query.folderQueries)) {
return PPromise.as(undefined);
}
let includes = { ...query.includePattern };
let excludes = { ...query.excludePattern };
for (const folderQuery of query.folderQueries) {
if (folderQuery.folder.scheme === this._scheme) {
includes = { ...includes, ...folderQuery.includePattern };
excludes = { ...excludes, ...folderQuery.excludePattern };
}
}
return new PPromise((resolve, reject, report) => {
const search = new SearchOperation(report);
@@ -177,7 +192,7 @@ class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProv
const promise = query.type === QueryType.File
? this._proxy.$findFiles(this._handle, search.id, query.filePattern)
: this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, undefined, undefined);
: this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, { excludes: Object.keys(excludes), includes: Object.keys(includes) });
promise.then(() => {
this._searches.delete(search.id);

View File

@@ -15,7 +15,7 @@ import { wireCancellationToken } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, SymbolInformationDto, WorkspaceEditDto, ResourceEditDto, CodeActionDto } from '../node/extHost.protocol';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, SymbolInformationDto, CodeActionDto, reviveWorkspaceEditDto } from '../node/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IHeapService } from './mainThreadHeapService';
@@ -86,21 +86,9 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
private static _reviveResourceEditDto(data: ResourceEditDto): modes.IResourceEdit {
data.resource = URI.revive(data.resource);
return <modes.IResourceEdit>data;
}
private static _reviveWorkspaceEditDto(data: WorkspaceEditDto): modes.WorkspaceEdit {
if (data && data.edits) {
data.edits.forEach(MainThreadLanguageFeatures._reviveResourceEditDto);
}
return <modes.WorkspaceEdit>data;
}
private static _reviveCodeActionDto(data: CodeActionDto[]): modes.CodeAction[] {
if (data) {
data.forEach(code => MainThreadLanguageFeatures._reviveWorkspaceEditDto(code.edit));
data.forEach(code => reviveWorkspaceEditDto(code.edit));
}
return <modes.CodeAction[]>data;
}
@@ -206,8 +194,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerQuickFixSupport(handle: number, selector: vscode.DocumentSelector): void {
this._registrations[handle] = modes.CodeActionProviderRegistry.register(toLanguageSelector(selector), <modes.CodeActionProvider>{
provideCodeActions: (model: ITextModel, range: EditorRange, token: CancellationToken): Thenable<modes.CodeAction[]> => {
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideCodeActions(handle, model.uri, range))).then(MainThreadLanguageFeatures._reviveCodeActionDto);
provideCodeActions: (model: ITextModel, range: EditorRange, context: modes.CodeActionContext, token: CancellationToken): Thenable<modes.CodeAction[]> => {
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideCodeActions(handle, model.uri, range, context))).then(MainThreadLanguageFeatures._reviveCodeActionDto);
}
});
}
@@ -266,11 +254,12 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector, supportsResolveInitialValues: boolean): void {
this._registrations[handle] = modes.RenameProviderRegistry.register(toLanguageSelector(selector), <modes.RenameProvider>{
provideRenameEdits: (model: ITextModel, position: EditorPosition, newName: string, token: CancellationToken): Thenable<modes.WorkspaceEdit> => {
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName)).then(MainThreadLanguageFeatures._reviveWorkspaceEditDto);
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName)).then(reviveWorkspaceEditDto);
},
resolveInitialRenameValue: supportsResolveInitialValues
? (model: ITextModel, position: EditorPosition, token: CancellationToken): Thenable<modes.RenameInitialValue> => wireCancellationToken(token, this._proxy.$resolveInitialRenameValue(handle, model.uri, position))
: undefined
: undefined
}
});
}

View File

@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { IExtHostContext, ExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
@extHostCustomer
export class MainThreadLogService extends Disposable {
constructor(
extHostContext: IExtHostContext,
@ILogService logService: ILogService,
) {
super();
this._register(logService.onDidChangeLogLevel(level => extHostContext.getProxy(ExtHostContext.ExtHostLogService).$setLevel(level)));
}
}

View File

@@ -26,6 +26,8 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerServ
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress';
import { localize } from 'vs/nls';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
export interface ISaveParticipantParticipant extends ISaveParticipant {
// progressMessage: string;
@@ -144,7 +146,7 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant
const lineCount = model.getLineCount();
// Do not insert new line if file does not end with new line
if (!lineCount) {
if (lineCount === 1) {
return;
}
@@ -208,7 +210,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
});
}).then(edits => {
if (edits && versionNow === model.getVersionId()) {
if (!isFalsyOrEmpty(edits) && versionNow === model.getVersionId()) {
const editor = findEditor(model, this._editorService);
if (editor) {
this._editsWithEditor(editor, edits);
@@ -278,8 +280,9 @@ export class SaveParticipant implements ISaveParticipant {
constructor(
extHostContext: IExtHostContext,
@IInstantiationService instantiationService: IInstantiationService,
@IProgressService2 private _progressService: IProgressService2,
@IInstantiationService instantiationService: IInstantiationService
@ILogService private _logService: ILogService
) {
this._saveParticipants = [
instantiationService.createInstance(TrimWhitespaceParticipant),
@@ -302,7 +305,7 @@ export class SaveParticipant implements ISaveParticipant {
const promiseFactory = this._saveParticipants.map(p => () => {
return Promise.resolve(p.participate(model, env));
});
return sequence(promiseFactory).then(() => { });
return sequence(promiseFactory).then(() => { }, err => this._logService.error(err));
});
}
}

View File

@@ -13,6 +13,7 @@ import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import URI from 'vs/base/common/uri';
@extHostNamedCustomer(MainContext.MainThreadTask)
export class MainThreadTask implements MainThreadTaskShape {
@@ -45,7 +46,7 @@ export class MainThreadTask implements MainThreadTaskShape {
let uri = (task._source as any as ExtensionTaskSourceTransfer).__workspaceFolder;
if (uri) {
delete (task._source as any as ExtensionTaskSourceTransfer).__workspaceFolder;
(task._source as any).workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(uri);
(task._source as any).workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(uri));
}
}
}
@@ -57,7 +58,7 @@ export class MainThreadTask implements MainThreadTaskShape {
return TPromise.wrap<void>(undefined);
}
public $unregisterTaskProvider(handle: number): TPromise<any> {
public $unregisterTaskProvider(handle: number): TPromise<void> {
this._taskService.unregisterTaskProvider(handle);
delete this._activeHandles[handle];
return TPromise.wrap<void>(undefined);

View File

@@ -67,7 +67,7 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
return this.postGetElements(elements);
}, err => {
this.messageService.show(Severity.Error, err);
return null;
return [];
});
}
@@ -80,7 +80,7 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
return this.postGetElements(children);
}, err => {
this.messageService.show(Severity.Error, err);
return null;
return [];
});
}

View File

@@ -5,7 +5,7 @@
'use strict';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import URI from 'vs/base/common/uri';
import URI, { UriComponents } from 'vs/base/common/uri';
import { ISearchService, QueryType, ISearchQuery, IFolderQuery, ISearchConfiguration } from 'vs/platform/search/common/search';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
@@ -14,6 +14,9 @@ import { MainThreadWorkspaceShape, ExtHostWorkspaceShape, ExtHostContext, MainCo
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { localize } from 'vs/nls';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@@ -27,7 +30,9 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@ISearchService private readonly _searchService: ISearchService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ITextFileService private readonly _textFileService: ITextFileService,
@IConfigurationService private _configurationService: IConfigurationService
@IConfigurationService private _configurationService: IConfigurationService,
@IWorkspaceEditingService private _workspaceEditingService: IWorkspaceEditingService,
@IStatusbarService private _statusbarService: IStatusbarService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace);
this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose);
@@ -45,6 +50,47 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
// --- workspace ---
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, foldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void> {
const workspaceFoldersToAdd = foldersToAdd.map(f => ({ uri: URI.revive(f.uri), name: f.name }));
// Indicate in status message
this._statusbarService.setStatusMessage(this.getStatusMessage(extensionName, workspaceFoldersToAdd.length, deleteCount), 10 * 1000 /* 10s */);
return this._workspaceEditingService.updateFolders(index, deleteCount, workspaceFoldersToAdd, true);
}
private getStatusMessage(extensionName, addCount: number, removeCount: number): string {
let message: string;
const wantsToAdd = addCount > 0;
const wantsToDelete = removeCount > 0;
// Add Folders
if (wantsToAdd && !wantsToDelete) {
if (addCount === 1) {
message = localize('folderStatusMessageAddSingleFolder', "Extension '{0}' added 1 folder to the workspace", extensionName);
} else {
message = localize('folderStatusMessageAddMultipleFolders', "Extension '{0}' added {1} folders to the workspace", extensionName, addCount);
}
}
// Delete Folders
else if (wantsToDelete && !wantsToAdd) {
if (removeCount === 1) {
message = localize('folderStatusMessageRemoveSingleFolder', "Extension '{0}' removed 1 folder from the workspace", extensionName);
} else {
message = localize('folderStatusMessageRemoveMultipleFolders', "Extension '{0}' removed {1} folders from the workspace", extensionName, removeCount);
}
}
// Change Folders
else {
message = localize('folderStatusChangeFolder', "Extension '{0}' changed folders of the workspace", extensionName);
}
return message;
}
private _onDidChangeWorkspace(): void {
this._proxy.$acceptWorkspaceData(this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : this._contextService.getWorkspace());
}
@@ -122,4 +168,4 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
return result.results.every(each => each.success === true);
});
}
}
}

View File

@@ -55,8 +55,8 @@ import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations';
import { toGlobPattern, toLanguageSelector } from 'vs/workbench/api/node/extHostTypeConverters';
import { ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
import { OverviewRulerLane } from 'vs/editor/common/model';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
@@ -89,18 +89,19 @@ export function createApiFactory(
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extensionService: ExtHostExtensionService,
logService: ILogService
extHostLogService: ExtHostLogService
): IExtensionApiFactory {
// Addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService);
const extHostHeapService = rpcProtocol.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService());
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, new ExtHostDecorations(rpcProtocol));
const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(rpcProtocol));
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(logService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadEditors)));
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadEditors)));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, logService));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, extHostLogService));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands));
rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace);
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace));
@@ -111,7 +112,7 @@ export function createApiFactory(
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService());
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, logService));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace));
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
@@ -418,6 +419,9 @@ export function createApiFactory(
set name(value) {
throw errors.readonly();
},
updateWorkspaceFolders: proposedApiFunction(extension, (index, deleteCount, ...workspaceFoldersToAdd) => {
return extHostWorkspace.updateWorkspaceFolders(extension, index, deleteCount, ...workspaceFoldersToAdd);
}),
onDidChangeWorkspaceFolders: function (listener, thisArgs?, disposables?) {
return extHostWorkspace.onDidChangeWorkspace(listener, thisArgs, disposables);
},
@@ -537,7 +541,13 @@ export function createApiFactory(
},
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider) {
return extHostDebugService.registerDebugConfigurationProvider(debugType, provider);
}
},
addBreakpoints: proposedApiFunction(extension, (breakpoints: vscode.Breakpoint[]) => {
return extHostDebugService.addBreakpoints(breakpoints);
}),
removeBreakpoints: proposedApiFunction(extension, (breakpoints: vscode.Breakpoint[]) => {
return extHostDebugService.removeBreakpoints(breakpoints);
})
};
@@ -556,6 +566,7 @@ export function createApiFactory(
Breakpoint: extHostTypes.Breakpoint,
CancellationTokenSource: CancellationTokenSource,
CodeAction: extHostTypes.CodeAction,
CodeActionKind: extHostTypes.CodeActionKind,
CodeLens: extHostTypes.CodeLens,
Color: extHostTypes.Color,
ColorPresentation: extHostTypes.ColorPresentation,

View File

@@ -4,17 +4,11 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {
createMainContextProxyIdentifier as createMainId,
createExtHostContextProxyIdentifier as createExtId,
ProxyIdentifier,
IRPCProtocol,
ProxyType
} from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { createMainContextProxyIdentifier as createMainId, createExtHostContextProxyIdentifier as createExtId, ProxyIdentifier, IRPCProtocol } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import * as vscode from 'vscode';
import { UriComponents } from 'vs/base/common/uri';
import URI, { UriComponents } from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -52,8 +46,9 @@ import { IStat, FileChangeType } from 'vs/platform/files/common/files';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { ILineMatch, IPatternInfo } from 'vs/platform/search/common/search';
import { LogLevel } from 'vs/platform/log/common/log';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
@@ -71,6 +66,7 @@ export interface IWorkspaceData {
id: string;
name: string;
folders: { uri: UriComponents, name: string, index: number }[];
configuration?: UriComponents;
}
export interface IInitData {
@@ -83,6 +79,7 @@ export interface IInitData {
windowId: number;
args: ParsedArgs;
execPath: string;
logLevel: LogLevel;
}
export interface IConfigurationInitData extends IConfigurationData {
@@ -192,8 +189,6 @@ export interface IApplyEditsOptions extends IUndoStopOptions {
setEndOfLine: EndOfLine;
}
export interface ITextDocumentShowOptions {
position?: EditorPosition;
preserveFocus?: boolean;
@@ -201,16 +196,6 @@ export interface ITextDocumentShowOptions {
selection?: IRange;
}
export interface IWorkspaceResourceEdit {
resource: UriComponents;
modelVersionId?: number;
edits: {
range?: IRange;
newText: string;
newEol?: EndOfLineSequence;
}[];
}
export interface MainThreadEditorsShape extends IDisposable {
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): TPromise<string>;
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
@@ -223,7 +208,7 @@ export interface MainThreadEditorsShape extends IDisposable {
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<void>;
$trySetSelections(id: string, selections: ISelection[]): TPromise<void>;
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): TPromise<boolean>;
$tryApplyWorkspaceEdit(workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean>;
$tryApplyWorkspaceEdit(workspaceEditDto: WorkspaceEditDto): TPromise<boolean>;
$tryInsertSnippet(id: string, template: string, selections: IRange[], opts: IUndoStopOptions): TPromise<boolean>;
$getDiffInformation(id: string): TPromise<editorCommon.ILineChange[]>;
}
@@ -364,6 +349,7 @@ export interface MainThreadWorkspaceShape extends IDisposable {
$startSearch(includePattern: string, includeFolder: string, excludePattern: string, maxResults: number, requestId: number): Thenable<UriComponents[]>;
$cancelSearch(requestId: number): Thenable<boolean>;
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void>;
}
export interface IFileChangeDto {
@@ -383,8 +369,8 @@ export interface MainThreadFileSystemShape extends IDisposable {
}
export interface MainThreadTaskShape extends IDisposable {
$registerTaskProvider(handle: number): TPromise<any>;
$unregisterTaskProvider(handle: number): TPromise<any>;
$registerTaskProvider(handle: number): TPromise<void>;
$unregisterTaskProvider(handle: number): TPromise<void>;
}
export interface MainThreadExtensionServiceShape extends IDisposable {
@@ -457,6 +443,8 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): TPromise<any>;
$appendDebugConsole(value: string): TPromise<any>;
$startBreakpointEvents(): TPromise<any>;
$registerBreakpoints(breakpoints: (ISourceMultiBreakpointDto | IFunctionBreakpointDto)[]): TPromise<void>;
$unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): TPromise<void>;
}
export interface MainThreadWindowShape extends IDisposable {
@@ -549,7 +537,7 @@ export interface ExtHostFileSystemShape {
$readdir(handle: number, resource: UriComponents): TPromise<[UriComponents, IStat][]>;
$rmdir(handle: number, resource: UriComponents): TPromise<void>;
$findFiles(handle: number, session: number, query: string): TPromise<void>;
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, include: string, exclude: string): TPromise<void>;
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, options: { includes: string[], excludes: string[] }): TPromise<void>;
}
export interface ExtHostExtensionServiceShape {
@@ -623,22 +611,44 @@ export interface WorkspaceSymbolsDto extends IdObject {
symbols: SymbolInformationDto[];
}
export interface ResourceEditDto {
export interface ResourceFileEditDto {
oldUri: UriComponents;
newUri: UriComponents;
}
export interface ResourceTextEditDto {
resource: UriComponents;
range: IRange;
newText: string;
modelVersionId?: number;
edits: modes.TextEdit[];
}
export interface WorkspaceEditDto {
edits: ResourceEditDto[];
edits: (ResourceFileEditDto | ResourceTextEditDto)[];
// todo@joh reject should go into rename
rejectReason?: string;
}
export function reviveWorkspaceEditDto(data: WorkspaceEditDto): modes.WorkspaceEdit {
if (data && data.edits) {
for (const edit of data.edits) {
if (typeof (<ResourceTextEditDto>edit).resource === 'object') {
(<ResourceTextEditDto>edit).resource = URI.revive((<ResourceTextEditDto>edit).resource);
} else {
(<ResourceFileEditDto>edit).newUri = URI.revive((<ResourceFileEditDto>edit).newUri);
(<ResourceFileEditDto>edit).oldUri = URI.revive((<ResourceFileEditDto>edit).oldUri);
}
}
}
return <modes.WorkspaceEdit>data;
}
export interface CodeActionDto {
title: string;
edit?: WorkspaceEditDto;
diagnostics?: IMarkerData[];
command?: modes.Command;
kind?: string;
}
export interface ExtHostLanguageFeaturesShape {
@@ -651,7 +661,7 @@ export interface ExtHostLanguageFeaturesShape {
$provideHover(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.Hover>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.DocumentHighlight[]>;
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext): TPromise<LocationDto[]>;
$provideCodeActions(handle: number, resource: UriComponents, range: IRange): TPromise<CodeActionDto[]>;
$provideCodeActions(handle: number, resource: UriComponents, range: IRange, context: modes.CodeActionContext): TPromise<CodeActionDto[]>;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]>;
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]>;
@@ -690,30 +700,43 @@ export interface ExtHostTaskShape {
$provideTasks(handle: number): TPromise<TaskSet>;
}
export interface IBreakpointData {
type: 'source' | 'function';
id: string;
export interface IFunctionBreakpointDto {
type: 'function';
id?: string;
enabled: boolean;
condition?: string;
hitCondition?: string;
functionName: string;
}
export interface ISourceBreakpointData extends IBreakpointData {
export interface ISourceBreakpointDto {
type: 'source';
id?: string;
enabled: boolean;
condition?: string;
hitCondition?: string;
uri: UriComponents;
line: number;
character: number;
}
export interface IFunctionBreakpointData extends IBreakpointData {
type: 'function';
functionName: string;
export interface IBreakpointsDeltaDto {
added?: (ISourceBreakpointDto | IFunctionBreakpointDto)[];
removed?: string[];
changed?: (ISourceBreakpointDto | IFunctionBreakpointDto)[];
}
export interface IBreakpointsDelta {
added?: (ISourceBreakpointData | IFunctionBreakpointData)[];
removed?: string[];
changed?: (ISourceBreakpointData | IFunctionBreakpointData)[];
export interface ISourceMultiBreakpointDto {
type: 'sourceMulti';
uri: UriComponents;
lines: {
id: string;
enabled: boolean;
condition?: string;
hitCondition?: string;
line: number;
character: number;
}[];
}
export interface ExtHostDebugServiceShape {
@@ -723,7 +746,7 @@ export interface ExtHostDebugServiceShape {
$acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void;
$acceptDebugSessionActiveChanged(id: DebugSessionUUID | undefined, type?: string, name?: string): void;
$acceptDebugSessionCustomEvent(id: DebugSessionUUID, type: string, name: string, event: any): void;
$acceptBreakpointsDelta(delat: IBreakpointsDelta): void;
$acceptBreakpointsDelta(delat: IBreakpointsDeltaDto): void;
}
@@ -744,6 +767,10 @@ export interface ExtHostWindowShape {
$onDidChangeWindowFocus(value: boolean): void;
}
export interface ExtHostLogServiceShape {
$setLevel(level: LogLevel);
}
// --- proxy identifiers
export const MainContext = {
@@ -772,7 +799,7 @@ export const MainContext = {
MainThreadFileSystem: createMainId<MainThreadFileSystemShape>('MainThreadFileSystem'),
MainThreadExtensionService: createMainId<MainThreadExtensionServiceShape>('MainThreadExtensionService'),
MainThreadSCM: createMainId<MainThreadSCMShape>('MainThreadSCM'),
MainThreadTask: createMainId<MainThreadTaskShape>('MainThreadTask', ProxyType.CustomMarshaller),
MainThreadTask: createMainId<MainThreadTaskShape>('MainThreadTask'),
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
};
@@ -794,10 +821,10 @@ export const ExtHostContext = {
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),
ExtHostQuickOpen: createExtId<ExtHostQuickOpenShape>('ExtHostQuickOpen'),
ExtHostExtensionService: createExtId<ExtHostExtensionServiceShape>('ExtHostExtensionService'),
// ExtHostLogService: createExtId<ExtHostLogServiceShape>('ExtHostLogService'),
ExtHostLogService: createExtId<ExtHostLogServiceShape>('ExtHostLogService'),
ExtHostTerminalService: createExtId<ExtHostTerminalServiceShape>('ExtHostTerminalService'),
ExtHostSCM: createExtId<ExtHostSCMShape>('ExtHostSCM'),
ExtHostTask: createExtId<ExtHostTaskShape>('ExtHostTask', ProxyType.CustomMarshaller),
ExtHostTask: createExtId<ExtHostTaskShape>('ExtHostTask'),
ExtHostWorkspace: createExtId<ExtHostWorkspaceShape>('ExtHostWorkspace'),
ExtHostWindow: createExtId<ExtHostWindowShape>('ExtHostWindow'),
};

View File

@@ -353,11 +353,7 @@ export class ExtHostApiCommands {
if (value.rejectReason) {
return TPromise.wrapError<types.WorkspaceEdit>(new Error(value.rejectReason));
}
let workspaceEdit = new types.WorkspaceEdit();
for (let edit of value.edits) {
workspaceEdit.replace(edit.resource, typeConverters.toRange(edit.range), edit.newText);
}
return workspaceEdit;
return typeConverters.WorkspaceEdit.to(value);
});
}
@@ -417,8 +413,11 @@ export class ExtHostApiCommands {
} else {
const ret = new types.CodeAction(
codeAction.title,
typeConverters.WorkspaceEdit.to(codeAction.edit)
codeAction.kind ? new types.CodeActionKind(codeAction.kind) : undefined
);
if (codeAction.edit) {
ret.edit = typeConverters.WorkspaceEdit.to(codeAction.edit);
}
return ret;
}
});

View File

@@ -7,12 +7,16 @@
import { TPromise } from 'vs/base/common/winjs.base';
import Event, { Emitter } from 'vs/base/common/event';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, IMainContext, IBreakpointsDelta, ISourceBreakpointData, IFunctionBreakpointData } from 'vs/workbench/api/node/extHost.protocol';
import {
MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID,
IMainContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto
} from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import * as vscode from 'vscode';
import URI, { UriComponents } from 'vs/base/common/uri';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint } from 'vs/workbench/api/node/extHostTypes';
import { generateUuid } from 'vs/base/common/uuid';
export class ExtHostDebugService implements ExtHostDebugServiceShape {
@@ -95,51 +99,161 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return result;
}
public $acceptBreakpointsDelta(delta: IBreakpointsDelta): void {
public $acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void {
let a: vscode.Breakpoint[] = [];
let r: vscode.Breakpoint[] = [];
let c: vscode.Breakpoint[] = [];
if (delta.added) {
a = delta.added.map(bpd => {
const bp = this.fromWire(bpd);
this._breakpoints.set(bpd.id, bp);
return bp;
});
for (const bpd of delta.added) {
if (!this._breakpoints.has(bpd.id)) {
let bp: vscode.Breakpoint;
if (bpd.type === 'function') {
bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition);
} else {
const uri = URI.revive(bpd.uri);
bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition);
}
bp['_id'] = bpd.id;
this._breakpoints.set(bpd.id, bp);
a.push(bp);
}
}
}
if (delta.removed) {
r = delta.removed.map(id => {
for (const id of delta.removed) {
const bp = this._breakpoints.get(id);
if (bp) {
this._breakpoints.delete(id);
r.push(bp);
}
return bp;
});
}
}
if (delta.changed) {
c = delta.changed.map(bpd => {
const bp = this.fromWire(bpd);
this._breakpoints.set(bpd.id, bp);
return bp;
});
for (const bpd of delta.changed) {
let bp = this._breakpoints.get(bpd.id);
if (bp) {
if (bpd.type === 'function') {
const fbp = <any>bp;
fbp.enabled = bpd.enabled;
fbp.condition = bpd.condition;
fbp.hitCondition = bpd.hitCondition;
fbp.functionName = bpd.functionName;
} else {
const sbp = <any>bp;
sbp.enabled = bpd.enabled;
sbp.condition = bpd.condition;
sbp.hitCondition = bpd.hitCondition;
}
c.push(bp);
}
}
}
this._onDidChangeBreakpoints.fire(Object.freeze({
added: Object.freeze<vscode.Breakpoint[]>(a || []),
removed: Object.freeze<vscode.Breakpoint[]>(r || []),
changed: Object.freeze<vscode.Breakpoint[]>(c || [])
}));
this.fireBreakpointChanges(a, r, c);
}
private fromWire(bp: ISourceBreakpointData | IFunctionBreakpointData): vscode.Breakpoint {
if (bp.type === 'function') {
return new FunctionBreakpoint(bp.enabled, bp.condition, bp.hitCondition, bp.functionName);
public addBreakpoints(breakpoints0: vscode.Breakpoint[]): TPromise<void> {
this.startBreakpoints();
// assign uuids for brand new breakpoints
const breakpoints: vscode.Breakpoint[] = [];
for (const bp of breakpoints0) {
let id = bp['_id'];
if (id) { // has already id
if (this._breakpoints.has(id)) {
// already there
} else {
breakpoints.push(bp);
}
} else {
id = generateUuid();
bp['_id'] = id;
this._breakpoints.set(id, bp);
breakpoints.push(bp);
}
}
// send notification for added breakpoints
this.fireBreakpointChanges(breakpoints, [], []);
// convert added breakpoints to DTOs
const dtos: (ISourceMultiBreakpointDto | IFunctionBreakpointDto)[] = [];
const map = new Map<string, ISourceMultiBreakpointDto>();
for (const bp of breakpoints) {
if (bp instanceof SourceBreakpoint) {
let dto = map.get(bp.location.uri.toString());
if (!dto) {
dto = <ISourceMultiBreakpointDto>{
type: 'sourceMulti',
uri: bp.location.uri,
lines: []
};
map.set(bp.location.uri.toString(), dto);
dtos.push(dto);
}
dto.lines.push({
id: bp['_id'],
enabled: bp.enabled,
condition: bp.condition,
hitCondition: bp.hitCondition,
line: bp.location.range.start.line,
character: bp.location.range.start.character
});
} else if (bp instanceof FunctionBreakpoint) {
dtos.push({
type: 'function',
id: bp['_id'],
enabled: bp.enabled,
functionName: bp.functionName,
hitCondition: bp.hitCondition,
condition: bp.condition
});
}
}
// send DTOs to VS Code
return this._debugServiceProxy.$registerBreakpoints(dtos);
}
public removeBreakpoints(breakpoints0: vscode.Breakpoint[]): TPromise<void> {
this.startBreakpoints();
// remove from array
const breakpoints: vscode.Breakpoint[] = [];
for (const b of breakpoints0) {
let id = b['_id'];
if (id && this._breakpoints.delete(id)) {
breakpoints.push(b);
}
}
// send notification
this.fireBreakpointChanges([], breakpoints, []);
// unregister with VS Code
const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp['_id']);
const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp['_id']);
return this._debugServiceProxy.$unregisterBreakpoints(ids, fids);
}
private fireBreakpointChanges(added: vscode.Breakpoint[], removed: vscode.Breakpoint[], changed: vscode.Breakpoint[]) {
if (added.length > 0 || removed.length > 0 || changed.length > 0) {
this._onDidChangeBreakpoints.fire(Object.freeze({
added: Object.freeze<vscode.Breakpoint[]>(added),
removed: Object.freeze<vscode.Breakpoint[]>(removed),
changed: Object.freeze<vscode.Breakpoint[]>(changed)
}));
}
const uri = URI.revive(bp.uri);
return new SourceBreakpoint(bp.enabled, bp.condition, bp.hitCondition, new Location(uri, new Position(bp.line, bp.character)));
}
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {

View File

@@ -8,7 +8,7 @@ import Event from 'vs/base/common/event';
import URI, { UriComponents } from 'vs/base/common/uri';
import { sequence, always } from 'vs/base/common/async';
import { illegalState } from 'vs/base/common/errors';
import { ExtHostDocumentSaveParticipantShape, MainThreadEditorsShape, IWorkspaceResourceEdit } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentSaveParticipantShape, MainThreadEditorsShape, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { TextEdit } from 'vs/workbench/api/node/extHostTypes';
import { fromRange, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
@@ -142,7 +142,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
}).then(values => {
let workspaceResourceEdit: IWorkspaceResourceEdit = {
const resourceEdit: ResourceTextEditDto = {
resource: document.uri,
edits: []
};
@@ -150,10 +150,10 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
for (const value of values) {
if (Array.isArray(value) && (<vscode.TextEdit[]>value).every(e => e instanceof TextEdit)) {
for (const { newText, newEol, range } of value) {
workspaceResourceEdit.edits.push({
resourceEdit.edits.push({
range: range && fromRange(range),
newText,
newEol: EndOfLine.from(newEol)
text: newText,
eol: EndOfLine.from(newEol)
});
}
}
@@ -161,12 +161,12 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
// apply edits if any and if document
// didn't change somehow in the meantime
if (workspaceResourceEdit.edits.length === 0) {
if (resourceEdit.edits.length === 0) {
return undefined;
}
if (version === document.version) {
return this._mainThreadEditors.$tryApplyWorkspaceEdit([workspaceResourceEdit]);
return this._mainThreadEditors.$tryApplyWorkspaceEdit({ edits: [resourceEdit] });
}
// TODO@joh bubble this to listener?

View File

@@ -22,6 +22,7 @@ import { Barrier } from 'vs/base/common/async';
import { ILogService } from 'vs/platform/log/common/log';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import URI from 'vs/base/common/uri';
class ExtensionMemento implements IExtensionMemento {
@@ -106,7 +107,11 @@ class ExtensionStoragePath {
await mkdirp(storagePath);
await writeFile(
join(storagePath, 'meta.json'),
JSON.stringify({ id: this._workspace.id })
JSON.stringify({
id: this._workspace.id,
configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(),
name: this._workspace.name
}, undefined, 2)
);
return storagePath;
@@ -125,7 +130,6 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private readonly _storage: ExtHostStorage;
private readonly _storagePath: ExtensionStoragePath;
private readonly _proxy: MainThreadExtensionServiceShape;
private readonly _logService: ILogService;
private readonly _extHostLogService: ExtHostLogService;
private _activator: ExtensionsActivator;
private _extensionPathIndex: TPromise<TernarySearchTree<IExtensionDescription>>;
@@ -136,21 +140,20 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
extHostContext: IExtHostContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
logService: ILogService,
extHostLogService: ExtHostLogService,
environmentService: IEnvironmentService
) {
this._barrier = new Barrier();
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
this._logService = logService;
this._extHostLogService = extHostLogService;
this._mainThreadTelemetry = extHostContext.getProxy(MainContext.MainThreadTelemetry);
this._storage = new ExtHostStorage(extHostContext);
this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
this._proxy = extHostContext.getProxy(MainContext.MainThreadExtensionService);
this._activator = null;
this._extHostLogService = new ExtHostLogService(environmentService);
// initialize API first (i.e. do not release barrier until the API is initialized)
const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, logService);
const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, this._extHostLogService);
initializeExtensionApi(this, apiFactory).then(() => {
@@ -311,14 +314,14 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return TPromise.as(new EmptyExtension(ExtensionActivationTimes.NONE));
}
this._logService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`);
this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`);
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return TPromise.join<any>([
loadCommonJSModule(this._logService, extensionDescription.main, activationTimesBuilder),
loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return ExtHostExtensionService._callActivate(this._logService, extensionDescription.id, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
}, (errors: any[]) => {
// Avoid failing with an array of errors, fail with a single error
if (errors[0]) {
@@ -336,7 +339,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage);
let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage);
this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`);
this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`);
return TPromise.join([
globalState.whenReady,
workspaceState.whenReady,

View File

@@ -137,7 +137,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
};
return asWinJsPromise(token => provider.findFiles(query, progress, token));
}
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, include: string, exclude: string): TPromise<void> {
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, options: { includes: string[], excludes: string[] }): TPromise<void> {
const provider = this._provider.get(handle);
if (!provider.provideTextSearchResults) {
return TPromise.as(undefined);
@@ -151,6 +151,6 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
}]);
}
};
return asWinJsPromise(token => provider.provideTextSearchResults(pattern, include, exclude, progress, token));
return asWinJsPromise(token => provider.provideTextSearchResults(pattern, options, progress, token));
}
}

View File

@@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { mixin } from 'vs/base/common/objects';
import * as vscode from 'vscode';
import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { Range, Disposable, CompletionList, SnippetString, Color } from 'vs/workbench/api/node/extHostTypes';
import { Range, Disposable, CompletionList, SnippetString, Color, CodeActionKind } from 'vs/workbench/api/node/extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
@@ -17,7 +17,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostDiagnostics, DiagnosticCollection } from 'vs/workbench/api/node/extHostDiagnostics';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto } from './extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto } from './extHost.protocol';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
@@ -255,7 +255,7 @@ class ReferenceAdapter {
}
}
export interface CustomCodeAction extends modes.CodeAction {
export interface CustomCodeAction extends CodeActionDto {
_isSynthetic?: boolean;
}
@@ -273,7 +273,8 @@ class CodeActionAdapter {
this._provider = provider;
}
provideCodeActions(resource: URI, range: IRange): TPromise<modes.CodeAction[]> {
provideCodeActions(resource: URI, range: IRange, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
const doc = this._documents.getDocumentData(resource).document;
const ran = <vscode.Range>TypeConverters.toRange(range);
@@ -289,8 +290,12 @@ class CodeActionAdapter {
}
});
const codeActionContext: vscode.CodeActionContext = {
diagnostics: allDiagnostics,
only: context.only ? new CodeActionKind(context.only) : undefined
};
return asWinJsPromise(token =>
this._provider.provideCodeActions(doc, ran, { diagnostics: allDiagnostics }, token)
this._provider.provideCodeActions(doc, ran, codeActionContext, token)
).then(commandsOrActions => {
if (isFalsyOrEmpty(commandsOrActions)) {
return undefined;
@@ -314,6 +319,7 @@ class CodeActionAdapter {
command: candidate.command && this._commands.toInternal(candidate.command),
diagnostics: candidate.diagnostics && candidate.diagnostics.map(DiagnosticCollection.toMarkerData),
edit: candidate.edit && TypeConverters.WorkspaceEdit.from(candidate.edit),
kind: candidate.kind && candidate.kind.value
});
}
}
@@ -963,8 +969,9 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideCodeActions(handle: number, resource: UriComponents, range: IRange): TPromise<modes.CodeAction[]> {
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), range));
$provideCodeActions(handle: number, resource: UriComponents, range: IRange, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), range, context));
}
// --- formatting

View File

@@ -4,51 +4,65 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as path from 'path';
import * as vscode from 'vscode';
import { TPromise } from 'vs/base/common/winjs.base';
import { join } from 'vs/base/common/paths';
import { mkdirp, dirExists } from 'vs/base/node/pfs';
import Event, { Emitter } from 'vs/base/common/event';
import Event from 'vs/base/common/event';
import { LogLevel } from 'vs/workbench/api/node/extHostTypes';
import { ILogService } from 'vs/platform/log/common/log';
import { ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { memoize } from 'vs/base/common/decorators';
import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol';
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
export class ExtHostLogService {
private _loggers: Map<string, ExtHostLogger> = new Map();
constructor(private _environmentService: IEnvironmentService) {
constructor(
windowId: number,
logLevel: LogLevel,
private _environmentService: IEnvironmentService
) {
super(createSpdLogService(`exthost${windowId}`, logLevel, _environmentService.logsPath));
}
$setLevel(level: LogLevel): void {
this.setLevel(level);
}
getExtLogger(extensionID: string): ExtHostLogger {
if (!this._loggers.has(extensionID)) {
const logService = createSpdLogService(extensionID, this._environmentService, extensionID);
const logsDirPath = path.join(this._environmentService.logsPath, extensionID);
this._loggers.set(extensionID, new ExtHostLogger(logService, logsDirPath));
let logger = this._loggers.get(extensionID);
if (!logger) {
logger = this.createLogger(extensionID);
this._loggers.set(extensionID, logger);
}
return logger;
}
return this._loggers.get(extensionID);
private createLogger(extensionID: string): ExtHostLogger {
const logsDirPath = join(this._environmentService.logsPath, extensionID);
const logService = createSpdLogService(extensionID, this.getLevel(), logsDirPath);
this._register(this.onDidChangeLogLevel(level => logService.setLevel(level)));
return new ExtHostLogger(logService, logsDirPath);
}
}
export class ExtHostLogger implements vscode.Logger {
private _currentLevel: LogLevel;
private _onDidChangeLogLevel: Emitter<LogLevel>;
constructor(
private readonly _logService: ILogService,
private readonly _logDirectory: string
) {
this._currentLevel = this._logService.getLevel();
this._onDidChangeLogLevel = new Emitter<LogLevel>();
this.onDidChangeLogLevel = this._onDidChangeLogLevel.event;
}
// TODO
readonly onDidChangeLogLevel: Event<LogLevel>;
get onDidChangeLogLevel(): Event<LogLevel> {
return this._logService.onDidChangeLogLevel;
}
get currentLevel(): LogLevel { return this._currentLevel; }
get currentLevel(): LogLevel { return this._logService.getLevel(); }
@memoize
get logDirectory(): TPromise<string> {

View File

@@ -294,11 +294,11 @@ namespace ShellConfiguration {
namespace Tasks {
export function from(tasks: vscode.Task[], rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): TaskSystem.Task[] {
export function from(tasks: vscode.Task[], rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): TaskSystem.ContributedTask[] {
if (tasks === void 0 || tasks === null) {
return [];
}
let result: TaskSystem.Task[] = [];
let result: TaskSystem.ContributedTask[] = [];
for (let task of tasks) {
let converted = fromSingle(task, rootFolder, extension);
if (converted) {
@@ -351,7 +351,7 @@ namespace Tasks {
// We can't transfer a workspace folder object from the extension host to main since they differ
// in shape and we don't have backwards converting function. So transfer the URI and resolve the
// workspace folder on the main side.
(source as any).__workspaceFolder = workspaceFolder ? workspaceFolder.uri as URI : undefined;
(source as any as TaskSystem.ExtensionTaskSourceTransfer).__workspaceFolder = workspaceFolder ? workspaceFolder.uri as URI : undefined;
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
let key = (task as types.Task).definitionKey;
let kind = (task as types.Task).definition;

View File

@@ -12,7 +12,7 @@ import * as TypeConverters from './extHostTypeConverters';
import { TextEditorDecorationType, ExtHostTextEditor } from './extHostTextEditor';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { MainContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IResolvedTextEditorConfiguration, ISelectionChangeEvent, IMainContext, IWorkspaceResourceEdit } from './extHost.protocol';
import { MainContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IResolvedTextEditorConfiguration, ISelectionChangeEvent, IMainContext, WorkspaceEditDto } from './extHost.protocol';
import * as vscode from 'vscode';
export class ExtHostEditors implements ExtHostEditorsShape {
@@ -92,36 +92,23 @@ export class ExtHostEditors implements ExtHostEditorsShape {
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
let workspaceResourceEdits: IWorkspaceResourceEdit[] = [];
const dto: WorkspaceEditDto = { edits: [] };
let entries = edit.entries();
for (let entry of entries) {
let [uri, edits] = entry;
let doc = this._extHostDocumentsAndEditors.getDocument(uri.toString());
let docVersion: number = undefined;
if (doc) {
docVersion = doc.version;
}
let workspaceResourceEdit: IWorkspaceResourceEdit = {
resource: uri,
modelVersionId: docVersion,
edits: []
};
for (let edit of edits) {
workspaceResourceEdit.edits.push({
newText: edit.newText,
newEol: TypeConverters.EndOfLine.from(edit.newEol),
range: edit.range && TypeConverters.fromRange(edit.range)
for (let entry of edit.allEntries()) {
let [uri, uriOrEdits] = entry;
if (Array.isArray(uriOrEdits)) {
let doc = this._extHostDocumentsAndEditors.getDocument(uri.toString());
dto.edits.push({
resource: uri,
modelVersionId: doc && doc.version,
edits: uriOrEdits.map(TypeConverters.TextEdit.from)
});
} else {
dto.edits.push({ oldUri: uri, newUri: uriOrEdits });
}
workspaceResourceEdits.push(workspaceResourceEdit);
}
return this._proxy.$tryApplyWorkspaceEdit(workspaceResourceEdits);
return this._proxy.$tryApplyWorkspaceEdit(dto);
}
// --- called from main thread

View File

@@ -114,7 +114,7 @@ class ExtHostTreeView<T> extends Disposable {
}
return null;
})
))).then(extTreeItems => extTreeItems.map((({ element, extTreeItem }) => this.createTreeItem(element, extTreeItem, parentHandle))));
))).then(extTreeItems => coalesce(extTreeItems).map((({ element, extTreeItem }) => this.createTreeItem(element, extTreeItem, parentHandle))));
}
getExtensionElement(treeItemHandle: TreeItemHandle): T {

View File

@@ -20,6 +20,7 @@ import { ISelection } from 'vs/editor/common/core/selection';
import * as htmlContent from 'vs/base/common/htmlContent';
import { IRelativePattern } from 'vs/base/common/glob';
import { LanguageSelector, LanguageFilter } from 'vs/editor/common/modes/languageSelector';
import { WorkspaceEditDto, ResourceTextEditDto, ResourceFileEditDto } from 'vs/workbench/api/node/extHost.protocol';
export interface PositionLike {
line: number;
@@ -228,24 +229,36 @@ export const TextEdit = {
export namespace WorkspaceEdit {
export function from(value: vscode.WorkspaceEdit): modes.WorkspaceEdit {
const result: modes.WorkspaceEdit = { edits: [] };
for (let entry of value.entries()) {
let [uri, textEdits] = entry;
for (let textEdit of textEdits) {
result.edits.push({
resource: uri,
newText: textEdit.newText,
range: fromRange(textEdit.range)
});
const result: modes.WorkspaceEdit = {
edits: []
};
for (const entry of value.allEntries()) {
const [uri, uriOrEdits] = entry;
if (Array.isArray(uriOrEdits)) {
// text edits
result.edits.push({ resource: uri, edits: uriOrEdits.map(TextEdit.from) });
} else {
// resource edits
result.edits.push({ oldUri: uri, newUri: uriOrEdits });
}
}
return result;
}
export function to(value: modes.WorkspaceEdit) {
export function to(value: WorkspaceEditDto) {
const result = new types.WorkspaceEdit();
for (const edit of value.edits) {
result.replace(edit.resource, toRange(edit.range), edit.newText);
if (Array.isArray((<ResourceTextEditDto>edit).edits)) {
result.set(
URI.revive((<ResourceTextEditDto>edit).resource),
<types.TextEdit[]>(<ResourceTextEditDto>edit).edits.map(TextEdit.to)
);
} else {
result.renameResource(
URI.revive((<ResourceFileEditDto>edit).oldUri),
URI.revive((<ResourceFileEditDto>edit).newUri)
);
}
}
return result;
}

View File

@@ -12,6 +12,7 @@ import * as vscode from 'vscode';
import { isMarkdownString } from 'vs/base/common/htmlContent';
import { IRelativePattern } from 'vs/base/common/glob';
import { relative } from 'path';
import { startsWith } from 'vs/base/common/strings';
export class Disposable {
@@ -491,10 +492,28 @@ export class TextEdit {
}
}
export class WorkspaceEdit {
export class WorkspaceEdit implements vscode.WorkspaceEdit {
private _values: [URI, TextEdit[]][] = [];
private _index = new Map<string, number>();
private _seqPool: number = 0;
private _resourceEdits: { seq: number, from: URI, to: URI }[] = [];
private _textEdits = new Map<string, { seq: number, uri: URI, edits: TextEdit[] }>();
createResource(uri: vscode.Uri): void {
this.renameResource(undefined, uri);
}
deleteResource(uri: vscode.Uri): void {
this.renameResource(uri, undefined);
}
renameResource(from: vscode.Uri, to: vscode.Uri): void {
this._resourceEdits.push({ seq: this._seqPool++, from, to });
}
resourceEdits(): [vscode.Uri, vscode.Uri][] {
return this._resourceEdits.map(({ from, to }) => (<[vscode.Uri, vscode.Uri]>[from, to]));
}
replace(uri: URI, range: Range, newText: string): void {
let edit = new TextEdit(range, newText);
@@ -502,8 +521,9 @@ export class WorkspaceEdit {
if (array) {
array.push(edit);
} else {
this.set(uri, [edit]);
array = [edit];
}
this.set(uri, array);
}
insert(resource: URI, position: Position, newText: string): void {
@@ -515,34 +535,58 @@ export class WorkspaceEdit {
}
has(uri: URI): boolean {
return this._index.has(uri.toString());
return this._textEdits.has(uri.toString());
}
set(uri: URI, edits: TextEdit[]): void {
const idx = this._index.get(uri.toString());
if (typeof idx === 'undefined') {
let newLen = this._values.push([uri, edits]);
this._index.set(uri.toString(), newLen - 1);
let data = this._textEdits.get(uri.toString());
if (!data) {
data = { seq: this._seqPool++, uri, edits: [] };
this._textEdits.set(uri.toString(), data);
}
if (!edits) {
data.edits = undefined;
} else {
this._values[idx][1] = edits;
data.edits = edits.slice(0);
}
}
get(uri: URI): TextEdit[] {
let idx = this._index.get(uri.toString());
return typeof idx !== 'undefined' && this._values[idx][1];
if (!this._textEdits.has(uri.toString())) {
return undefined;
}
const { edits } = this._textEdits.get(uri.toString());
return edits ? edits.slice() : undefined;
}
entries(): [URI, TextEdit[]][] {
return this._values;
const res: [URI, TextEdit[]][] = [];
this._textEdits.forEach(value => res.push([value.uri, value.edits]));
return res.slice();
}
allEntries(): ([URI, TextEdit[]] | [URI, URI])[] {
// use the 'seq' the we have assigned when inserting
// the operation and use that order in the resulting
// array
const res: ([URI, TextEdit[]] | [URI, URI])[] = [];
this._textEdits.forEach(value => {
const { seq, uri, edits } = value;
res[seq] = [uri, edits];
});
this._resourceEdits.forEach(value => {
const { seq, from, to } = value;
res[seq] = [from, to];
});
return res;
}
get size(): number {
return this._values.length;
return this._textEdits.size + this._resourceEdits.length;
}
toJSON(): any {
return this._values;
return this.entries();
}
}
@@ -818,12 +862,39 @@ export class CodeAction {
dianostics?: Diagnostic[];
constructor(title: string, edit?: WorkspaceEdit) {
kind?: CodeActionKind;
constructor(title: string, kind?: CodeActionKind) {
this.title = title;
this.edit = edit;
this.kind = kind;
}
}
export class CodeActionKind {
private static readonly sep = '.';
public static readonly Empty = new CodeActionKind('');
public static readonly QuickFix = CodeActionKind.Empty.append('quickfix');
public static readonly Refactor = CodeActionKind.Empty.append('refactor');
public static readonly RefactorExtract = CodeActionKind.Refactor.append('extract');
public static readonly RefactorInline = CodeActionKind.Refactor.append('inline');
public static readonly RefactorRewrite = CodeActionKind.Refactor.append('rewrite');
constructor(
public readonly value: string
) { }
public append(parts: string): CodeActionKind {
return new CodeActionKind(this.value ? this.value + CodeActionKind.sep + parts : parts);
}
public contains(other: CodeActionKind): boolean {
return this.value === other.value || startsWith(other.value, this.value + CodeActionKind.sep);
}
}
export class CodeLens {
range: Range;
@@ -1510,19 +1581,21 @@ export class Breakpoint {
readonly condition?: string;
readonly hitCondition?: string;
protected constructor(enabled: boolean, condition: string, hitCondition: string) {
this.enabled = enabled;
this.condition = condition;
this.hitCondition = hitCondition;
this.condition = condition;
this.hitCondition = hitCondition;
protected constructor(enabled?: boolean, condition?: string, hitCondition?: string) {
this.enabled = typeof enabled === 'boolean' ? enabled : true;
if (typeof condition === 'string') {
this.condition = condition;
}
if (typeof hitCondition === 'string') {
this.hitCondition = hitCondition;
}
}
}
export class SourceBreakpoint extends Breakpoint {
readonly location: Location;
constructor(enabled: boolean, condition: string, hitCondition: string, location: Location) {
constructor(location: Location, enabled?: boolean, condition?: string, hitCondition?: string) {
super(enabled, condition, hitCondition);
this.location = location;
}
@@ -1531,7 +1604,7 @@ export class SourceBreakpoint extends Breakpoint {
export class FunctionBreakpoint extends Breakpoint {
readonly functionName: string;
constructor(enabled: boolean, condition: string, hitCondition: string, functionName: string) {
constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string) {
super(enabled, condition, hitCondition);
this.functionName = functionName;
}

View File

@@ -7,40 +7,109 @@
import URI from 'vs/base/common/uri';
import Event, { Emitter } from 'vs/base/common/event';
import { normalize } from 'vs/base/common/paths';
import { delta } from 'vs/base/common/arrays';
import { delta as arrayDelta } from 'vs/base/common/arrays';
import { relative, dirname } from 'path';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext } from './extHost.protocol';
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext, MainThreadMessageServiceShape } from './extHost.protocol';
import * as vscode from 'vscode';
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 { Severity } from 'vs/platform/message/common/message';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { localize } from 'vs/nls';
class Workspace2 extends Workspace {
function isFolderEqual(folderA: URI, folderB: URI): boolean {
return isEqual(folderA, folderB, !isLinux);
}
static fromData(data: IWorkspaceData) {
function compareWorkspaceFolderByUri(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
return isFolderEqual(a.uri, b.uri) ? 0 : compare(a.uri.toString(), b.uri.toString());
}
function compareWorkspaceFolderByUriAndNameAndIndex(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
if (a.index !== b.index) {
return a.index < b.index ? -1 : 1;
}
return isFolderEqual(a.uri, b.uri) ? compare(a.name, b.name) : compare(a.uri.toString(), b.uri.toString());
}
function delta(oldFolders: vscode.WorkspaceFolder[], newFolders: vscode.WorkspaceFolder[], compare: (a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder) => number): { removed: vscode.WorkspaceFolder[], added: vscode.WorkspaceFolder[] } {
const oldSortedFolders = oldFolders.slice(0).sort(compare);
const newSortedFolders = newFolders.slice(0).sort(compare);
return arrayDelta(oldSortedFolders, newSortedFolders, compare);
}
interface MutableWorkspaceFolder extends vscode.WorkspaceFolder {
name: string;
index: number;
}
class ExtHostWorkspaceImpl extends Workspace {
static toExtHostWorkspace(data: IWorkspaceData, previousConfirmedWorkspace?: ExtHostWorkspaceImpl, previousUnconfirmedWorkspace?: ExtHostWorkspaceImpl): { workspace: ExtHostWorkspaceImpl, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } {
if (!data) {
return null;
} else {
const { id, name, folders } = data;
return new Workspace2(
id,
name,
folders.map(({ uri, name, index }) => new WorkspaceFolder({ name, index, uri: URI.revive(uri) }))
);
return { workspace: null, added: [], removed: [] };
}
const { id, name, folders } = data;
const newWorkspaceFolders: vscode.WorkspaceFolder[] = [];
// If we have an existing workspace, we try to find the folders that match our
// data and update their properties. It could be that an extension stored them
// for later use and we want to keep them "live" if they are still present.
const oldWorkspace = previousConfirmedWorkspace;
if (oldWorkspace) {
folders.forEach((folderData, index) => {
const folderUri = URI.revive(folderData.uri);
const existingFolder = ExtHostWorkspaceImpl._findFolder(previousUnconfirmedWorkspace || previousConfirmedWorkspace, folderUri);
if (existingFolder) {
existingFolder.name = folderData.name;
existingFolder.index = folderData.index;
newWorkspaceFolders.push(existingFolder);
} else {
newWorkspaceFolders.push({ uri: folderUri, name: folderData.name, index });
}
});
} else {
newWorkspaceFolders.push(...folders.map(({ uri, name, index }) => ({ uri: URI.revive(uri), name, index })));
}
// make sure to restore sort order based on index
newWorkspaceFolders.sort((f1, f2) => f1.index < f2.index ? -1 : 1);
const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders);
const { added, removed } = delta(oldWorkspace ? oldWorkspace.workspaceFolders : [], workspace.workspaceFolders, compareWorkspaceFolderByUri);
return { workspace, added, removed };
}
private static _findFolder(workspace: ExtHostWorkspaceImpl, folderUriToFind: URI): MutableWorkspaceFolder {
for (let i = 0; i < workspace.folders.length; i++) {
const folder = workspace.workspaceFolders[i];
if (isFolderEqual(folder.uri, folderUriToFind)) {
return folder;
}
}
return undefined;
}
private readonly _workspaceFolders: vscode.WorkspaceFolder[] = [];
private readonly _structure = TernarySearchTree.forPaths<vscode.WorkspaceFolder>();
private constructor(id: string, name: string, folders: WorkspaceFolder[]) {
super(id, name, folders);
private constructor(id: string, name: string, folders: vscode.WorkspaceFolder[]) {
super(id, name, folders.map(f => new WorkspaceFolder(f)));
// setup the workspace folder data structure
this.folders.forEach(({ name, uri, index }) => {
const workspaceFolder = { name, uri, index };
this._workspaceFolders.push(workspaceFolder);
this._structure.set(workspaceFolder.uri.toString(), workspaceFolder);
folders.forEach(folder => {
this._workspaceFolders.push(folder);
this._structure.set(folder.uri.toString(), folder);
});
}
@@ -63,44 +132,116 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
private readonly _proxy: MainThreadWorkspaceShape;
private _workspace: Workspace2;
private _confirmedWorkspace: ExtHostWorkspaceImpl;
private _unconfirmedWorkspace: ExtHostWorkspaceImpl;
private _messageService: MainThreadMessageServiceShape;
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
constructor(mainContext: IMainContext, data: IWorkspaceData) {
this._proxy = mainContext.getProxy(MainContext.MainThreadWorkspace);
this._workspace = Workspace2.fromData(data);
this._messageService = mainContext.getProxy(MainContext.MainThreadMessageService);
this._confirmedWorkspace = ExtHostWorkspaceImpl.toExtHostWorkspace(data).workspace;
}
// --- workspace ---
get workspace(): Workspace {
return this._workspace;
return this._actualWorkspace;
}
private get _actualWorkspace(): ExtHostWorkspaceImpl {
return this._unconfirmedWorkspace || this._confirmedWorkspace;
}
getWorkspaceFolders(): vscode.WorkspaceFolder[] {
if (!this._workspace) {
if (!this._actualWorkspace) {
return undefined;
} else {
return this._workspace.workspaceFolders.slice(0);
}
return this._actualWorkspace.workspaceFolders.slice(0);
}
updateWorkspaceFolders(extension: IExtensionDescription, index: number, deleteCount: number, ...workspaceFoldersToAdd: { uri: vscode.Uri, name?: string }[]): boolean {
const validatedDistinctWorkspaceFoldersToAdd: { uri: vscode.Uri, name?: string }[] = [];
if (Array.isArray(workspaceFoldersToAdd)) {
workspaceFoldersToAdd.forEach(folderToAdd => {
if (URI.isUri(folderToAdd.uri) && !validatedDistinctWorkspaceFoldersToAdd.some(f => isFolderEqual(f.uri, folderToAdd.uri))) {
validatedDistinctWorkspaceFoldersToAdd.push({ uri: folderToAdd.uri, name: folderToAdd.name || basenameOrAuthority(folderToAdd.uri) });
}
});
}
if (!!this._unconfirmedWorkspace) {
return false; // prevent accumulated calls without a confirmed workspace
}
if ([index, deleteCount].some(i => typeof i !== 'number' || i < 0)) {
return false; // validate numbers
}
if (deleteCount === 0 && validatedDistinctWorkspaceFoldersToAdd.length === 0) {
return false; // nothing to delete or add
}
const currentWorkspaceFolders: MutableWorkspaceFolder[] = this._actualWorkspace ? this._actualWorkspace.workspaceFolders : [];
if (index + deleteCount > currentWorkspaceFolders.length) {
return false; // cannot delete more than we have
}
// Simulate the updateWorkspaceFolders method on our data to do more validation
const newWorkspaceFolders = currentWorkspaceFolders.slice(0);
newWorkspaceFolders.splice(index, deleteCount, ...validatedDistinctWorkspaceFoldersToAdd.map(f => ({ uri: f.uri, name: f.name || basenameOrAuthority(f.uri) })));
for (let i = 0; i < newWorkspaceFolders.length; i++) {
const folder = newWorkspaceFolders[i];
if (newWorkspaceFolders.some((otherFolder, index) => index !== i && isFolderEqual(folder.uri, otherFolder.uri))) {
return false; // cannot add the same folder multiple times
}
}
newWorkspaceFolders.forEach((f, index) => f.index = index); // fix index
const { added, removed } = delta(currentWorkspaceFolders, newWorkspaceFolders, compareWorkspaceFolderByUriAndNameAndIndex);
if (added.length === 0 && removed.length === 0) {
return false; // nothing actually changed
}
// Trigger on main side
if (this._proxy) {
const extName = extension.displayName || extension.name;
this._proxy.$updateWorkspaceFolders(extName, index, deleteCount, validatedDistinctWorkspaceFoldersToAdd).then(null, error => {
// in case of an error, make sure to clear out the unconfirmed workspace
// because we cannot expect the acknowledgement from the main side for this
this._unconfirmedWorkspace = undefined;
// show error to user
this._messageService.$showMessage(Severity.Error, localize('updateerror', "Extension '{0}' failed to update workspace folders: {1}", extName, error.toString()), { extension }, []);
});
}
// Try to accept directly
return this.trySetWorkspaceFolders(newWorkspaceFolders);
}
getWorkspaceFolder(uri: vscode.Uri, resolveParent?: boolean): vscode.WorkspaceFolder {
if (!this._workspace) {
if (!this._actualWorkspace) {
return undefined;
}
return this._workspace.getWorkspaceFolder(uri, resolveParent);
return this._actualWorkspace.getWorkspaceFolder(uri, resolveParent);
}
getPath(): string {
// this is legacy from the days before having
// multi-root and we keep it only alive if there
// is just one workspace folder.
if (!this._workspace) {
if (!this._actualWorkspace) {
return undefined;
}
const { folders } = this._workspace;
const { folders } = this._actualWorkspace;
if (folders.length === 0) {
return undefined;
}
@@ -130,7 +271,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
}
if (typeof includeWorkspace === 'undefined') {
includeWorkspace = this.workspace.folders.length > 1;
includeWorkspace = this._actualWorkspace.folders.length > 1;
}
let result = relative(folder.uri.fsPath, path);
@@ -140,27 +281,40 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
return normalize(result, true);
}
private trySetWorkspaceFolders(folders: vscode.WorkspaceFolder[]): boolean {
// Update directly here. The workspace is unconfirmed as long as we did not get an
// acknowledgement from the main side (via $acceptWorkspaceData)
if (this._actualWorkspace) {
this._unconfirmedWorkspace = ExtHostWorkspaceImpl.toExtHostWorkspace({
id: this._actualWorkspace.id,
name: this._actualWorkspace.name,
configuration: this._actualWorkspace.configuration,
folders
} as IWorkspaceData, this._actualWorkspace).workspace;
return true;
}
return false;
}
$acceptWorkspaceData(data: IWorkspaceData): void {
// keep old workspace folder, build new workspace, and
// capture new workspace folders. Compute delta between
// them send that as event
const oldRoots = this._workspace ? this._workspace.workspaceFolders.sort(ExtHostWorkspace._compareWorkspaceFolder) : [];
const { workspace, added, removed } = ExtHostWorkspaceImpl.toExtHostWorkspace(data, this._confirmedWorkspace, this._unconfirmedWorkspace);
this._workspace = Workspace2.fromData(data);
const newRoots = this._workspace ? this._workspace.workspaceFolders.sort(ExtHostWorkspace._compareWorkspaceFolder) : [];
// Update our workspace object. We have a confirmed workspace, so we drop our
// unconfirmed workspace.
this._confirmedWorkspace = workspace;
this._unconfirmedWorkspace = undefined;
const { added, removed } = delta(oldRoots, newRoots, ExtHostWorkspace._compareWorkspaceFolder);
// Events
this._onDidChangeWorkspace.fire(Object.freeze({
added: Object.freeze<vscode.WorkspaceFolder[]>(added),
removed: Object.freeze<vscode.WorkspaceFolder[]>(removed)
}));
}
private static _compareWorkspaceFolder(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
return compare(a.uri.toString(), b.uri.toString());
}
// --- search ---
findFiles(include: vscode.GlobPattern, exclude: vscode.GlobPattern, maxResults?: number, token?: vscode.CancellationToken): Thenable<vscode.Uri[]> {