Merge from vscode 718331d6f3ebd1b571530ab499edb266ddd493d5

This commit is contained in:
ADS Merger
2020-02-08 04:50:58 +00:00
parent 8c61538a27
commit 2af13c18d2
752 changed files with 16458 additions and 10063 deletions
@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { ChartView } from 'sql/workbench/contrib/charts/browser/chartView';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
import { TestLayoutService } from 'vs/workbench/test/workbenchTestServices';
import { TestLayoutService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -22,7 +22,7 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestEditorService, TestDialogService } from 'vs/workbench/test/workbenchTestServices';
import { TestEditorService, TestDialogService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
import { URI } from 'vs/base/common/uri';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { TestQueryModelService } from 'sql/workbench/services/query/test/common/testQueryModelService';
@@ -393,7 +393,7 @@ suite('commandLineService tests', () => {
querymodelService.setup(c => c.onRunQueryComplete).returns(() => Event.None);
const instantiationService = new TestInstantiationService();
let uri = URI.file(args._[0]);
const untitledEditorInput = new UntitledTextEditorInput(uri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined);
const untitledEditorInput = new UntitledTextEditorInput(uri, false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
const queryInput = new UntitledQueryEditorInput(undefined, untitledEditorInput, undefined, connectionManagementService.object, querymodelService.object, configurationService.object);
queryInput.state.connected = true;
const editorService: TypeMoq.Mock<IEditorService> = TypeMoq.Mock.ofType<IEditorService>(TestEditorService, TypeMoq.MockBehavior.Strict);
@@ -15,7 +15,6 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views';
import { ConnectionViewletPanel } from 'sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel';
import { Extensions as ViewContainerExtensions, IViewDescriptor, IViewsRegistry, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -146,11 +145,6 @@ export class DataExplorerViewPaneContainer extends ViewPaneContainer {
return actions;
}
protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] {
const addedViews = super.onDidAddViews(added);
return addedViews;
}
protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane {
let viewletPanel = this.instantiationService.createInstance(viewDescriptor.ctorDescriptor.ctor, options) as ViewPane;
this._register(viewletPanel);
@@ -28,7 +28,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { TestEnvironmentService, TestLifecycleService, TestStorageService, TestTextFileService, workbenchInstantiationService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices';
import { TestEnvironmentService, TestLifecycleService, TestStorageService, TestTextFileService, workbenchInstantiationService, TestTextResourcePropertiesService } from 'vs/workbench/test/browser/workbenchTestServices';
import { Range } from 'vs/editor/common/core/range';
import { nb } from 'azdata';
import { Emitter } from 'vs/base/common/event';
@@ -6,7 +6,7 @@
import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
import { nb } from 'azdata';
import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices';
import { workbenchInstantiationService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { UntitledNotebookInput } from 'sql/workbench/contrib/notebook/common/models/untitledNotebookInput';
@@ -48,7 +48,7 @@ suite('Notebook Input', function (): void {
let untitledNotebookInput: UntitledNotebookInput;
setup(() => {
untitledTextInput = new UntitledTextEditorInput(untitledUri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined);
untitledTextInput = new UntitledTextEditorInput(untitledUri, false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
untitledNotebookInput = new UntitledNotebookInput(
testTitle, untitledUri, untitledTextInput,
undefined, instantiationService, mockNotebookService.object, mockExtensionService.object);
@@ -169,7 +169,7 @@ suite('Notebook Input', function (): void {
assert.ok(untitledNotebookInput.matches(untitledNotebookInput), 'Input should match itself.');
let otherTestUri = URI.from({ scheme: Schemas.untitled, path: 'OtherTestPath' });
let otherTextInput = new UntitledTextEditorInput(otherTestUri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined);
let otherTextInput = new UntitledTextEditorInput(otherTestUri, false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
let otherInput = instantiationService.createInstance(UntitledNotebookInput, 'OtherTestInput', otherTestUri, otherTextInput);
assert.strictEqual(untitledNotebookInput.matches(otherInput), false, 'Input should not match different input.');
@@ -11,7 +11,7 @@ import * as tempWrite from 'temp-write';
import { LocalContentManager } from 'sql/workbench/services/notebook/common/localContentManager';
import { CellTypes } from 'sql/workbench/contrib/notebook/common/models/contracts';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { TestFileService } from 'vs/workbench/test/workbenchTestServices';
import { TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
import { IFileService, IReadFileOptions, IFileContent, IWriteFileOptions, IFileStatWithMetadata } from 'vs/platform/files/common/files';
import * as pfs from 'vs/base/node/pfs';
import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer';
@@ -25,7 +25,7 @@ import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/t
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ClientSession } from 'sql/workbench/contrib/notebook/browser/models/clientSession';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices';
import { NotebookRange } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
import { NotebookEditorContentManager } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
@@ -22,7 +22,7 @@ import { Memento } from 'vs/workbench/common/memento';
import { Emitter } from 'vs/base/common/event';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
@@ -22,7 +22,7 @@ import { TreeNode } from 'sql/workbench/contrib/objectExplorer/common/treeNode';
import { NodeType } from 'sql/workbench/contrib/objectExplorer/common/nodeType';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { ServerTreeDataSource } from 'sql/workbench/contrib/objectExplorer/browser/serverTreeDataSource';
import { Emitter } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
import { ObjectExplorerActionsContext } from 'sql/workbench/contrib/objectExplorer/browser/objectExplorerActions';
import { IConnectionResult, IConnectionParams } from 'sql/platform/connection/common/connectionManagement';
@@ -31,7 +31,7 @@ import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/t
import { UNSAVED_GROUP_ID, mssqlProviderName } from 'sql/platform/connection/common/constants';
import { $ } from 'vs/base/browser/dom';
import { OEManageConnectionAction } from 'sql/workbench/contrib/dashboard/browser/dashboardActions';
import { IViewsService, IView, ViewContainer, IViewDescriptorCollection } from 'vs/workbench/common/views';
import { IViewsService, IView } from 'vs/workbench/common/views';
import { ConsoleLogService } from 'vs/platform/log/common/log';
suite('SQL Connection Tree Action tests', () => {
@@ -109,18 +109,22 @@ suite('SQL Connection Tree Action tests', () => {
});
const viewsService = new class implements IViewsService {
getActiveViewWithId(id: string): IView {
getActiveViewWithId<T extends IView>(id: string): T | null {
throw new Error('Method not implemented.');
}
_serviceBrand: undefined;
openView(id: string, focus?: boolean): Promise<IView> {
return Promise.resolve({
openView<T extends IView>(id: string, focus?: boolean): Promise<T | null> {
return Promise.resolve(<T><any>{
id: '',
serversTree: undefined
});
}
getViewDescriptors(container: ViewContainer): IViewDescriptorCollection {
throw new Error('Method not implemented.');
onDidChangeViewVisibility: Event<{ id: string, visible: boolean }> = Event.None;
closeView(id: string): void {
return;
}
isViewVisible(id: string): boolean {
return true;
}
};
@@ -8,7 +8,7 @@ import { ServerTreeView } from 'sql/workbench/contrib/objectExplorer/browser/ser
import { ConnectionManagementService } from 'sql/workbench/services/connection/browser/connectionManagementService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices';
import * as TypeMoq from 'typemoq';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
@@ -11,8 +11,8 @@ import { IQueryModelService } from 'sql/workbench/services/query/common/queryMod
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { EncodingMode } from 'vs/workbench/common/editor';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
import { ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
type PublicPart<T> = { [K in keyof T]: T[K] };
@@ -31,7 +31,7 @@ export class FileQueryEditorInput extends QueryEditorInput implements PublicPart
super(description, text, results, connectionManagementService, queryModelService, configurationService);
}
public resolve(): Promise<TextFileEditorModel | BinaryEditorModel> {
public resolve(): Promise<ITextFileEditorModel | BinaryEditorModel> {
return this.text.resolve();
}
@@ -23,7 +23,7 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
import { TestStorageService, TestFileService } from 'vs/workbench/test/workbenchTestServices';
import { TestStorageService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
@@ -70,7 +70,7 @@ suite('SQL QueryAction Tests', () => {
connectionManagementService = TypeMoq.Mock.ofType<TestConnectionManagementService>(TestConnectionManagementService);
connectionManagementService.setup(q => q.onDisconnect).returns(() => Event.None);
const instantiationService = new TestInstantiationService();
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined);
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
// Setup a reusable mock QueryInput
testQueryInput = TypeMoq.Mock.ofType(UntitledQueryEditorInput, TypeMoq.MockBehavior.Strict, undefined, fileInput, undefined, connectionManagementService.object, queryModelService.object, configurationService.object);
testQueryInput.setup(x => x.uri).returns(() => testUri);
@@ -175,7 +175,7 @@ suite('SQL QueryAction Tests', () => {
queryModelService.setup(x => x.onRunQueryStart).returns(() => Event.None);
queryModelService.setup(x => x.onRunQueryComplete).returns(() => Event.None);
const instantiationService = new TestInstantiationService();
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined);
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
// ... Mock "isSelectionEmpty" in QueryEditor
let queryInput = TypeMoq.Mock.ofType(UntitledQueryEditorInput, TypeMoq.MockBehavior.Strict, undefined, fileInput, undefined, connectionManagementService.object, queryModelService.object, configurationService.object);
@@ -224,7 +224,7 @@ suite('SQL QueryAction Tests', () => {
// ... Mock "getSelection" in QueryEditor
const instantiationService = new TestInstantiationService();
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined);
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
let queryInput = TypeMoq.Mock.ofType(UntitledQueryEditorInput, TypeMoq.MockBehavior.Loose, undefined, fileInput, undefined, connectionManagementService.object, queryModelService.object, configurationService.object);
queryInput.setup(x => x.uri).returns(() => testUri);
@@ -18,7 +18,7 @@ import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { TestStorageService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
@@ -285,7 +285,7 @@ suite('SQL QueryEditor Tests', () => {
return new RunQueryAction(undefined, undefined, undefined);
});
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService.object, undefined, new LabelService(undefined, undefined), undefined, undefined);
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, undefined, undefined, undefined, instantiationService.object, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
queryModelService = TypeMoq.Mock.ofType(TestQueryModelService, TypeMoq.MockBehavior.Strict);
queryModelService.setup(x => x.disposeQuery(TypeMoq.It.isAny()));
queryModelService.setup(x => x.onRunQueryComplete).returns(() => Event.None);
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { TestEditorService } from 'vs/workbench/test/workbenchTestServices';
import { TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices';
import { URI } from 'vs/base/common/uri';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorInput } from 'vs/workbench/common/editor';
@@ -13,7 +13,7 @@ import { AccountAdditionResult, AccountProviderAddedEventParams, UpdateAccountLi
import { IAccountStore } from 'sql/platform/accounts/common/interfaces';
import { AccountProviderStub } from 'sql/platform/accounts/test/common/testAccountManagementService';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices';
import { EventVerifierSingle } from 'sql/base/test/common/event';
// SUITE CONSTANTS /////////////////////////////////////////////////////////
@@ -10,7 +10,7 @@ import { ConnectionType, IConnectableInput, IConnectionResult, INewConnectionPar
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
import * as TypeMoq from 'typemoq';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { NullLogService } from 'vs/platform/log/common/log';
@@ -28,7 +28,7 @@ import * as TypeMoq from 'typemoq';
import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
import { TestStorageService, TestEnvironmentService, TestEditorService } from 'vs/workbench/test/workbenchTestServices';
import { TestStorageService, TestEnvironmentService, TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { NullLogService } from 'vs/platform/log/common/log';
@@ -13,7 +13,7 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia
import * as azdata from 'azdata';
import { equal } from 'assert';
import { Mock, MockBehavior, It } from 'typemoq';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices';
import { Emitter } from 'vs/base/common/event';
import { InsightsDialogModel } from 'sql/workbench/services/insights/browser/insightsDialogModel';
import { IInsightsConfigDetails } from 'sql/platform/dashboard/browser/insightRegistry';
@@ -12,7 +12,7 @@ import * as path from 'vs/base/common/path';
import { Workspace, toWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
import { TestContextService, TestFileService } from 'vs/workbench/test/workbenchTestServices';
import { TestContextService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
import { IExtensionHostDebugParams, IDebugParams, ParsedArgs } from 'vs/platform/environment/common/environment';
import { URI } from 'vs/base/common/uri';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@@ -10,7 +10,7 @@ import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/br
import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { TestEditorService } from 'vs/workbench/test/workbenchTestServices';
import { TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices';
import { assign } from 'vs/base/common/objects';
suite('TaskUtilities', function () {
@@ -5,7 +5,7 @@
import * as assert from 'assert';
import { EditorReplacementContribution } from 'sql/workbench/common/editorReplacerContribution';
import { TestEditorService } from 'vs/workbench/test/workbenchTestServices';
import { TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices';
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
import { Event } from 'vs/base/common/event';
import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
@@ -8,13 +8,13 @@ import * as azdata from 'azdata';
import * as TypeMoq from 'typemoq';
import { AccountProviderStub, TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
import { ExtHostAccountManagement } from 'sql/workbench/api/common/extHostAccountManagement';
import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { SqlMainContext } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { MainThreadAccountManagement } from 'sql/workbench/api/browser/mainThreadAccountManagement';
import { IAccountManagementService, AzureResource } from 'sql/platform/accounts/common/interfaces';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol';
const IRPCProtocol = createDecorator<IRPCProtocol>('rpcProtocol');
@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { ExtHostCredentialManagement } from 'sql/workbench/api/common/extHostCredentialManagement';
import { SqlMainContext } from 'sql/workbench/api/common/sqlExtHost.protocol';
@@ -14,6 +13,7 @@ import { ICredentialsService } from 'sql/platform/credentials/common/credentials
import { Credential, CredentialProvider } from 'azdata';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { TestCredentialsService, TestCredentialsProvider } from 'sql/platform/credentials/test/common/testCredentialsService';
import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol';
const IRPCProtocol = createDecorator<IRPCProtocol>('rpcProtocol');
@@ -15,7 +15,7 @@ import { NotebookService } from 'sql/workbench/services/notebook/browser/noteboo
import { INotebookProvider } from 'sql/workbench/services/notebook/browser/notebookService';
import { INotebookManagerDetails, INotebookSessionDetails, INotebookKernelDetails, INotebookFutureDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
import { LocalContentManager } from 'sql/workbench/services/notebook/common/localContentManager';
import { TestLifecycleService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
import { TestLifecycleService, TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { ExtHostNotebookShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITestInstantiationService, workbenchInstantiationService as vsworkbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices';
import { ITestInstantiationService, workbenchInstantiationService as vsworkbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
import { TestQueryModelService } from 'sql/workbench/services/query/test/common/testQueryModelService';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
+1 -1
View File
@@ -120,6 +120,6 @@ export const isWebKit = (userAgent.indexOf('AppleWebKit') >= 0);
export const isChrome = (userAgent.indexOf('Chrome') >= 0);
export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0));
export const isWebkitWebView = (!isChrome && !isSafari && isWebKit);
export const isIPad = (userAgent.indexOf('iPad') >= 0);
export const isIPad = (userAgent.indexOf('iPad') >= 0 || (isSafari && navigator.maxTouchPoints > 0));
export const isEdgeWebView = isEdge && (userAgent.indexOf('WebView/') >= 0);
export const isStandalone = (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches);
+17 -2
View File
@@ -1313,7 +1313,22 @@ export function asCSSUrl(uri: URI): string {
return `url('${asDomUri(uri).toString(true).replace(/'/g, '%27')}')`;
}
export function triggerDownload(uri: URI, name: string): void {
export function triggerDownload(dataOrUri: Uint8Array | URI, name: string): void {
// If the data is provided as Buffer, we create a
// blog URL out of it to produce a valid link
let url: string;
if (URI.isUri(dataOrUri)) {
url = dataOrUri.toString(true);
} else {
const blob = new Blob([dataOrUri]);
url = URL.createObjectURL(blob);
// Ensure to free the data from DOM eventually
setTimeout(() => URL.revokeObjectURL(url));
}
// In order to download from the browser, the only way seems
// to be creating a <a> element with download attribute that
// points to the file to download.
@@ -1321,7 +1336,7 @@ export function triggerDownload(uri: URI, name: string): void {
const anchor = document.createElement('a');
document.body.appendChild(anchor);
anchor.download = name;
anchor.href = uri.toString(true);
anchor.href = url;
anchor.click();
// Ensure to remove the element from DOM eventually
+10
View File
@@ -28,6 +28,7 @@ export class FastDomNode<T extends HTMLElement> {
private _backgroundColor: string;
private _layerHint: boolean;
private _contain: 'none' | 'strict' | 'content' | 'size' | 'layout' | 'style' | 'paint';
private _boxShadow: string;
constructor(domNode: T) {
this.domNode = domNode;
@@ -51,6 +52,7 @@ export class FastDomNode<T extends HTMLElement> {
this._backgroundColor = '';
this._layerHint = false;
this._contain = 'none';
this._boxShadow = '';
}
public setMaxWidth(maxWidth: number): void {
@@ -218,6 +220,14 @@ export class FastDomNode<T extends HTMLElement> {
this.domNode.style.transform = this._layerHint ? 'translate3d(0px, 0px, 0px)' : '';
}
public setBoxShadow(boxShadow: string): void {
if (this._boxShadow === boxShadow) {
return;
}
this._boxShadow = boxShadow;
this.domNode.style.boxShadow = boxShadow;
}
public setContain(contain: 'none' | 'strict' | 'content' | 'size' | 'layout' | 'style' | 'paint'): void {
if (this._contain === contain) {
return;
+4 -5
View File
@@ -88,7 +88,7 @@ export class MenuBar extends Disposable {
private numMenusShown: number = 0;
private menuStyle: IMenuStyles | undefined;
private overflowLayoutScheduled: IDisposable | null = null;
private overflowLayoutScheduled: IDisposable | undefined = undefined;
constructor(private container: HTMLElement, private options: IMenuBarOptions = {}) {
super();
@@ -419,9 +419,8 @@ export class MenuBar extends Disposable {
DOM.removeNode(this.overflowMenu.titleElement);
DOM.removeNode(this.overflowMenu.buttonElement);
if (this.overflowLayoutScheduled) {
this.overflowLayoutScheduled = dispose(this.overflowLayoutScheduled);
}
dispose(this.overflowLayoutScheduled);
this.overflowLayoutScheduled = undefined;
}
blur(): void {
@@ -561,7 +560,7 @@ export class MenuBar extends Disposable {
if (!this.overflowLayoutScheduled) {
this.overflowLayoutScheduled = DOM.scheduleAtNextAnimationFrame(() => {
this.updateOverflowAction();
this.overflowLayoutScheduled = null;
this.overflowLayoutScheduled = undefined;
});
}
@@ -287,6 +287,7 @@ export abstract class AbstractScrollableElement extends Widget {
this._options.handleMouseWheel = massagedOptions.handleMouseWheel;
this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity;
this._options.fastScrollSensitivity = massagedOptions.fastScrollSensitivity;
this._options.scrollPredominantAxis = massagedOptions.scrollPredominantAxis;
this._setListeningToMouseWheel(this._options.handleMouseWheel);
if (!this._options.lazyRender) {
@@ -334,6 +335,14 @@ export abstract class AbstractScrollableElement extends Widget {
let deltaY = e.deltaY * this._options.mouseWheelScrollSensitivity;
let deltaX = e.deltaX * this._options.mouseWheelScrollSensitivity;
if (this._options.scrollPredominantAxis) {
if (Math.abs(deltaY) >= Math.abs(deltaX)) {
deltaX = 0;
} else {
deltaY = 0;
}
}
if (this._options.flipAxes) {
[deltaY, deltaX] = [deltaX, deltaY];
}
@@ -553,6 +562,7 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme
scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false),
mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1),
fastScrollSensitivity: (typeof opts.fastScrollSensitivity !== 'undefined' ? opts.fastScrollSensitivity : 5),
scrollPredominantAxis: (typeof opts.scrollPredominantAxis !== 'undefined' ? opts.scrollPredominantAxis : true),
mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true),
arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11),
@@ -55,6 +55,13 @@ export interface ScrollableElementCreationOptions {
* Defaults to 5.
*/
fastScrollSensitivity?: number;
/**
* Whether the scrollable will only scroll along the predominant axis when scrolling both
* vertically and horizontally at the same time.
* Prevents horizontal drift when scrolling vertically on a trackpad.
* Defaults to true.
*/
scrollPredominantAxis?: boolean;
/**
* Height for vertical arrows (top/bottom) and width for horizontal arrows (left/right).
* Defaults to 11.
@@ -113,6 +120,7 @@ export interface ScrollableElementChangeOptions {
handleMouseWheel?: boolean;
mouseWheelScrollSensitivity?: number;
fastScrollSensitivity: number;
scrollPredominantAxis: boolean;
}
export interface ScrollableElementResolvedOptions {
@@ -125,6 +133,7 @@ export interface ScrollableElementResolvedOptions {
alwaysConsumeMouseWheel: boolean;
mouseWheelScrollSensitivity: number;
fastScrollSensitivity: number;
scrollPredominantAxis: boolean;
mouseWheelSmoothScroll: boolean;
arrowSize: number;
listenOnDomNode: HTMLElement | null;
+1 -1
View File
@@ -1585,7 +1585,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
}
open(elements: TRef[], browserEvent?: UIEvent): void {
const indexes = elements.map(e => this.model.getListIndex(e));
const indexes = elements.map(e => this.model.getListIndex(e)).filter(i => i >= 0);
this.view.open(indexes, browserEvent);
}
@@ -562,6 +562,10 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
const node = this.getDataNode(element);
if (this.tree.hasElement(node) && !this.tree.isCollapsible(node)) {
return false;
}
if (node.refreshPromise) {
await this.root.refreshPromise;
await Event.toPromise(this._onDidRender.event);
@@ -205,6 +205,10 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
this.model.setChildren(node, children, _onDidCreateNode, _onDidDeleteNode);
}
has(element: T | null): boolean {
return this.nodes.has(element);
}
getListIndex(location: T | null): number {
const node = this.getCompressedNode(location);
return this.model.getListIndex(node);
@@ -421,6 +425,10 @@ export class CompressibleObjectTreeModel<T extends NonNullable<any>, TFilterData
this.model.setCompressionEnabled(enabled);
}
has(location: T | null): boolean {
return this.model.has(location);
}
getListIndex(location: T | null): number {
return this.model.getListIndex(location);
}
@@ -199,6 +199,10 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
}
}
has(location: number[]): boolean {
return this.hasTreeNode(location);
}
getListIndex(location: number[]): number {
const { listIndex, visible, revealed } = this.getTreeNodeWithListIndex(location);
return visible && revealed ? listIndex : -1;
@@ -291,6 +295,8 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
if (isCollapsibleStateUpdate(update)) {
result = node.collapsible !== update.collapsible;
node.collapsible = update.collapsible;
} else if (!node.collapsible) {
result = false;
} else {
result = node.collapsed !== update.collapsed;
node.collapsed = update.collapsed;
@@ -516,6 +522,21 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
}
}
// cheap
private hasTreeNode(location: number[], node: IIndexTreeNode<T, TFilterData> = this.root): boolean {
if (!location || location.length === 0) {
return true;
}
const [index, ...rest] = location;
if (index < 0 || index > node.children.length) {
return false;
}
return this.hasTreeNode(rest, node.children[index]);
}
// cheap
private getTreeNode(location: number[], node: IIndexTreeNode<T, TFilterData> = this.root): IIndexTreeNode<T, TFilterData> {
if (!location || location.length === 0) {
@@ -50,6 +50,10 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
this.model.resort(element, recursive);
}
hasElement(element: T): boolean {
return this.model.has(element);
}
protected createModel(user: string, view: ISpliceable<ITreeNode<T, TFilterData>>, options: IObjectTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
return new ObjectTreeModel(user, view, options);
}
@@ -195,6 +195,10 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
return this.model.getLastElementAncestor(location);
}
has(element: T | null): boolean {
return this.nodes.has(element);
}
getListIndex(element: T | null): number {
const location = this.getElementLocation(element);
return this.model.getListIndex(location);
+2
View File
@@ -108,6 +108,8 @@ export interface ITreeModel<T, TFilterData, TRef> {
readonly onDidChangeCollapseState: Event<ICollapseStateChangeEvent<T, TFilterData>>;
readonly onDidChangeRenderNodeCount: Event<ITreeNode<T, TFilterData>>;
has(location: TRef): boolean;
getListIndex(location: TRef): number;
getListRenderCount(location: TRef): number;
getNode(location?: TRef): ITreeNode<T, any>;
+4
View File
@@ -584,3 +584,7 @@ export function mapArrayOrNot<T, U>(items: T | T[], fn: (_: T) => U): U | U[] {
export function asArray<T>(x: T | T[]): T[] {
return Array.isArray(x) ? x : [x];
}
export function getRandomElement<T>(arr: T[]): T | undefined {
return arr[Math.floor(Math.random() * arr.length)];
}
-4
View File
@@ -175,7 +175,3 @@ export function applyEdits(text: string, edits: Edit[]): string {
}
return text;
}
export function isWS(text: string, offset: number) {
return '\r\n \t'.indexOf(text.charAt(offset)) !== -1;
}
+6 -1
View File
@@ -91,6 +91,9 @@ export function toDisposable(fn: () => void): IDisposable {
}
export class DisposableStore implements IDisposable {
static DISABLE_DISPOSED_WARNING = false;
private _toDispose = new Set<IDisposable>();
private _isDisposed = false;
@@ -127,7 +130,9 @@ export class DisposableStore implements IDisposable {
markTracked(t);
if (this._isDisposed) {
console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack);
if (!DisposableStore.DISABLE_DISPOSED_WARNING) {
console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack);
}
} else {
this._toDispose.add(t);
}
+3 -2
View File
@@ -182,7 +182,7 @@ export function hasTrailingPathSeparator(resource: URI, sep: string = paths.sep)
return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === sep;
} else {
const p = resource.path;
return p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; // ignore the slash at offset 0
return (p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash) && !(/^[a-zA-Z]:(\/$|\\$)/.test(resource.fsPath)); // ignore the slash at offset 0
}
}
@@ -191,6 +191,7 @@ export function hasTrailingPathSeparator(resource: URI, sep: string = paths.sep)
* Important: Doesn't remove the first slash, it would make the URI invalid
*/
export function removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
// Make sure that the path isn't a drive letter. A trailing separator there is not removable.
if (hasTrailingPathSeparator(resource, sep)) {
return resource.with({ path: resource.path.substr(0, resource.path.length - 1) });
}
@@ -226,7 +227,7 @@ export function relativePath(from: URI, to: URI, ignoreCase = hasToIgnoreCase(fr
return undefined;
}
if (from.scheme === Schemas.file) {
const relativePath = paths.relative(from.path, to.path);
const relativePath = paths.relative(originalFSPath(from), originalFSPath(to));
return isWindows ? extpath.toSlashes(relativePath) : relativePath;
}
let fromPath = from.path || '/', toPath = to.path || '/';
+2 -2
View File
@@ -126,8 +126,8 @@ export class ConfigWatcher<T> extends Disposable implements IConfigWatcher<T> {
}
private async handleSymbolicLink(): Promise<void> {
const { stat, isSymbolicLink } = await statLink(this._path);
if (isSymbolicLink && !stat.isDirectory()) {
const { stat, symbolicLink } = await statLink(this._path);
if (symbolicLink && !stat.isDirectory()) {
const realPath = await realpath(this._path);
this.watch(realPath, false);
+58 -132
View File
@@ -14,7 +14,18 @@ import { promisify } from 'util';
import { isRootOrDriveLetter } from 'vs/base/common/extpath';
import { generateUuid } from 'vs/base/common/uuid';
import { normalizeNFC } from 'vs/base/common/normalization';
import { encode, encodeStream } from 'vs/base/node/encoding';
import { encode } from 'vs/base/node/encoding';
// See https://github.com/Microsoft/vscode/issues/30180
const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB
const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB
// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149
const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB
const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB
export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE;
export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE;
export enum RimRafMode {
@@ -178,30 +189,52 @@ export function stat(path: string): Promise<fs.Stats> {
}
export interface IStatAndLink {
// The stats of the file. If the file is a symbolic
// link, the stats will be of that target file and
// not the link itself.
// If the file is a symbolic link pointing to a non
// existing file, the stat will be of the link and
// the `dangling` flag will indicate this.
stat: fs.Stats;
isSymbolicLink: boolean;
// Will be provided if the resource is a symbolic link
// on disk. Use the `dangling` flag to find out if it
// points to a resource that does not exist on disk.
symbolicLink?: { dangling: boolean };
}
export async function statLink(path: string): Promise<IStatAndLink> {
// First stat the link
let linkStat: fs.Stats | undefined;
let linkStatError: NodeJS.ErrnoException | undefined;
let lstats: fs.Stats | undefined;
try {
linkStat = await lstat(path);
lstats = await lstat(path);
// Return early if the stat is not a symbolic link at all
if (!lstats.isSymbolicLink()) {
return { stat: lstats };
}
} catch (error) {
linkStatError = error;
/* ignore - use stat() instead */
}
// Then stat the target and return that
const isLink = !!(linkStat && linkStat.isSymbolicLink());
if (linkStatError || isLink) {
const fileStat = await stat(path);
// If the stat is a symbolic link or failed to stat, use fs.stat()
// which for symbolic links will stat the target they point to
try {
const stats = await stat(path);
return { stat: fileStat, isSymbolicLink: isLink };
return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined };
} catch (error) {
// If the link points to a non-existing file we still want
// to return it as result while setting dangling: true flag
if (error.code === 'ENOENT' && lstats) {
return { stat: lstats, symbolicLink: { dangling: true } };
}
throw error;
}
return { stat: linkStat!, isSymbolicLink: false };
}
export function lstat(path: string): Promise<fs.Stats> {
@@ -213,9 +246,7 @@ export function rename(oldPath: string, newPath: string): Promise<void> {
}
export function renameIgnoreError(oldPath: string, newPath: string): Promise<void> {
return new Promise(resolve => {
fs.rename(oldPath, newPath, () => resolve());
});
return new Promise(resolve => fs.rename(oldPath, newPath, () => resolve()));
}
export function unlink(path: string): Promise<void> {
@@ -236,6 +267,10 @@ export function readFile(path: string, encoding?: string): Promise<Buffer | stri
return promisify(fs.readFile)(path, encoding);
}
export async function mkdirp(path: string, mode?: number): Promise<void> {
return promisify(fs.mkdir)(path, { mode, recursive: true });
}
// According to node.js docs (https://nodejs.org/docs/v6.5.0/api/fs.html#fs_fs_writefile_file_data_options_callback)
// it is not safe to call writeFile() on the same path multiple times without waiting for the callback to return.
// Therefor we use a Queue on the path that is given to us to sequentialize calls to the same path properly.
@@ -244,12 +279,15 @@ const writeFilePathQueues: Map<string, Queue<void>> = new Map();
export function writeFile(path: string, data: string, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: Buffer, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: Uint8Array, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: NodeJS.ReadableStream, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options?: IWriteFileOptions): Promise<void> {
export function writeFile(path: string, data: string | Buffer | Uint8Array, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: string | Buffer | Uint8Array, options?: IWriteFileOptions): Promise<void> {
const queueKey = toQueueKey(path);
return ensureWriteFileQueue(queueKey).queue(() => writeFileAndFlush(path, data, options));
return ensureWriteFileQueue(queueKey).queue(() => {
const ensuredOptions = ensureWriteOptions(options);
return new Promise((resolve, reject) => doWriteFileAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve()));
});
}
function toQueueKey(path: string): string {
@@ -294,103 +332,6 @@ interface IEnsuredWriteFileOptions extends IWriteFileOptions {
}
let canFlush = true;
function writeFileAndFlush(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options: IWriteFileOptions | undefined): Promise<void> {
const ensuredOptions = ensureWriteOptions(options);
return new Promise((resolve, reject) => {
if (typeof data === 'string' || Buffer.isBuffer(data) || data instanceof Uint8Array) {
doWriteFileAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve());
} else {
doWriteFileStreamAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve());
}
});
}
function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, options: IEnsuredWriteFileOptions, callback: (error?: Error) => void): void {
// finish only once
let finished = false;
const finish = (error?: Error) => {
if (!finished) {
finished = true;
// in error cases we need to manually close streams
// if the write stream was successfully opened
if (error) {
if (isOpen) {
writer.once('close', () => callback(error));
writer.destroy();
} else {
callback(error);
}
}
// otherwise just return without error
else {
callback();
}
}
};
// create writer to target. we set autoClose: false because we want to use the streams
// file descriptor to call fs.fdatasync to ensure the data is flushed to disk
const writer = fs.createWriteStream(path, { mode: options.mode, flags: options.flag, autoClose: false });
// Event: 'open'
// Purpose: save the fd for later use and start piping
// Notes: will not be called when there is an error opening the file descriptor!
let fd: number;
let isOpen: boolean;
writer.once('open', descriptor => {
fd = descriptor;
isOpen = true;
// if an encoding is provided, we need to pipe the stream through
// an encoder stream and forward the encoding related options
if (options.encoding) {
reader = reader.pipe(encodeStream(options.encoding.charset, { addBOM: options.encoding.addBOM }));
}
// start data piping only when we got a successful open. this ensures that we do
// not consume the stream when an error happens and helps to fix this issue:
// https://github.com/Microsoft/vscode/issues/42542
reader.pipe(writer);
});
// Event: 'error'
// Purpose: to return the error to the outside and to close the write stream (does not happen automatically)
reader.once('error', error => finish(error));
writer.once('error', error => finish(error));
// Event: 'finish'
// Purpose: use fs.fdatasync to flush the contents to disk
// Notes: event is called when the writer has finished writing to the underlying resource. we must call writer.close()
// because we have created the WriteStream with autoClose: false
writer.once('finish', () => {
// flush to disk
if (canFlush && isOpen) {
fs.fdatasync(fd, (syncError: Error) => {
// In some exotic setups it is well possible that node fails to sync
// In that case we disable flushing and warn to the console
if (syncError) {
console.warn('[node.js fs] fdatasync is now disabled for this session because it failed: ', syncError);
canFlush = false;
}
writer.destroy();
});
} else {
writer.destroy();
}
});
// Event: 'close'
// Purpose: signal we are done to the outside
// Notes: event is called when the writer's filedescriptor is closed
writer.once('close', () => finish());
}
// Calls fs.writeFile() followed by a fs.sync() call to flush the changes to disk
// We do this in cases where we want to make sure the data is really on disk and
@@ -631,18 +572,3 @@ async function doCopyFile(source: string, target: string, mode: number): Promise
reader.pipe(writer);
});
}
export async function mkdirp(path: string, mode?: number): Promise<void> {
return promisify(fs.mkdir)(path, { mode, recursive: true });
}
// See https://github.com/Microsoft/vscode/issues/30180
const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB
const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB
// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149
const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB
const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB
export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE;
export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE;
+102 -14
View File
@@ -3,12 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter, Relay } from 'vs/base/common/event';
import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter, Relay, EventMultiplexer } from 'vs/base/common/event';
import { IDisposable, toDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { VSBuffer } from 'vs/base/common/buffer';
import { getRandomElement } from 'vs/base/common/arrays';
import { isFunction } from 'vs/base/common/types';
/**
* An `IChannel` is an abstraction over a collection of commands.
@@ -95,7 +97,8 @@ export interface Client<TContext> {
export interface IConnectionHub<TContext> {
readonly connections: Connection<TContext>[];
readonly onDidChangeConnections: Event<Connection<TContext>>;
readonly onDidAddConnection: Event<Connection<TContext>>;
readonly onDidRemoveConnection: Event<Connection<TContext>>;
}
/**
@@ -116,7 +119,7 @@ export interface IClientRouter<TContext = string> {
* order to pick the right one.
*/
export interface IRoutingChannelClient<TContext = string> {
getChannel<T extends IChannel>(channelName: string, router: IClientRouter<TContext>): T;
getChannel<T extends IChannel>(channelName: string, router?: IClientRouter<TContext>): T;
}
interface IReader {
@@ -659,8 +662,11 @@ export class IPCServer<TContext = string> implements IChannelServer<TContext>, I
private channels = new Map<string, IServerChannel<TContext>>();
private _connections = new Set<Connection<TContext>>();
private readonly _onDidChangeConnections = new Emitter<Connection<TContext>>();
readonly onDidChangeConnections: Event<Connection<TContext>> = this._onDidChangeConnections.event;
private readonly _onDidAddConnection = new Emitter<Connection<TContext>>();
readonly onDidAddConnection: Event<Connection<TContext>> = this._onDidAddConnection.event;
private readonly _onDidRemoveConnection = new Emitter<Connection<TContext>>();
readonly onDidRemoveConnection: Event<Connection<TContext>> = this._onDidRemoveConnection.event;
get connections(): Connection<TContext>[] {
const result: Connection<TContext>[] = [];
@@ -683,30 +689,59 @@ export class IPCServer<TContext = string> implements IChannelServer<TContext>, I
const connection: Connection<TContext> = { channelServer, channelClient, ctx };
this._connections.add(connection);
this._onDidChangeConnections.fire(connection);
this._onDidAddConnection.fire(connection);
onDidClientDisconnect(() => {
channelServer.dispose();
channelClient.dispose();
this._connections.delete(connection);
this._onDidRemoveConnection.fire(connection);
});
});
});
}
getChannel<T extends IChannel>(channelName: string, router: IClientRouter<TContext>): T {
/**
* Get a channel from a remote client. When passed a router,
* one can specify which client it wants to call and listen to/from.
* Otherwise, when calling without a router, a random client will
* be selected and when listening without a router, every client
* will be listened to.
*/
getChannel<T extends IChannel>(channelName: string, router: IClientRouter<TContext>): T;
getChannel<T extends IChannel>(channelName: string, clientFilter: (client: Client<TContext>) => boolean): T;
getChannel<T extends IChannel>(channelName: string, routerOrClientFilter: IClientRouter<TContext> | ((client: Client<TContext>) => boolean)): T {
const that = this;
return {
call(command: string, arg?: any, cancellationToken?: CancellationToken) {
const channelPromise = router.routeCall(that, command, arg)
call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise<T> {
let connectionPromise: Promise<Client<TContext>>;
if (isFunction(routerOrClientFilter)) {
// when no router is provided, we go random client picking
let connection = getRandomElement(that.connections.filter(routerOrClientFilter));
connectionPromise = connection
// if we found a client, let's call on it
? Promise.resolve(connection)
// else, let's wait for a client to come along
: Event.toPromise(Event.filter(that.onDidAddConnection, routerOrClientFilter));
} else {
connectionPromise = routerOrClientFilter.routeCall(that, command, arg);
}
const channelPromise = connectionPromise
.then(connection => (connection as Connection<TContext>).channelClient.getChannel(channelName));
return getDelayedChannel(channelPromise)
.call(command, arg, cancellationToken);
},
listen(event: string, arg: any) {
const channelPromise = router.routeEvent(that, event, arg)
listen(event: string, arg: any): Event<T> {
if (isFunction(routerOrClientFilter)) {
return that.getMulticastEvent(channelName, routerOrClientFilter, event, arg);
}
const channelPromise = routerOrClientFilter.routeEvent(that, event, arg)
.then(connection => (connection as Connection<TContext>).channelClient.getChannel(channelName));
return getDelayedChannel(channelPromise)
@@ -715,6 +750,58 @@ export class IPCServer<TContext = string> implements IChannelServer<TContext>, I
} as T;
}
private getMulticastEvent<T extends IChannel>(channelName: string, clientFilter: (client: Client<TContext>) => boolean, eventName: string, arg: any): Event<T> {
const that = this;
let disposables = new DisposableStore();
// Create an emitter which hooks up to all clients
// as soon as first listener is added. It also
// disconnects from all clients as soon as the last listener
// is removed.
const emitter = new Emitter<T>({
onFirstListenerAdd: () => {
disposables = new DisposableStore();
// The event multiplexer is useful since the active
// client list is dynamic. We need to hook up and disconnection
// to/from clients as they come and go.
const eventMultiplexer = new EventMultiplexer<T>();
const map = new Map<Connection<TContext>, IDisposable>();
const onDidAddConnection = (connection: Connection<TContext>) => {
const channel = connection.channelClient.getChannel(channelName);
const event = channel.listen<T>(eventName, arg);
const disposable = eventMultiplexer.add(event);
map.set(connection, disposable);
};
const onDidRemoveConnection = (connection: Connection<TContext>) => {
const disposable = map.get(connection);
if (!disposable) {
return;
}
disposable.dispose();
map.delete(connection);
};
that.connections.filter(clientFilter).forEach(onDidAddConnection);
Event.filter(that.onDidAddConnection, clientFilter)(onDidAddConnection, undefined, disposables);
that.onDidRemoveConnection(onDidRemoveConnection, undefined, disposables);
eventMultiplexer.event(emitter.fire, emitter, disposables);
disposables.add(eventMultiplexer);
},
onLastListenerRemove: () => {
disposables.dispose();
}
});
return emitter.event;
}
registerChannel(channelName: string, channel: IServerChannel<TContext>): void {
this.channels.set(channelName, channel);
@@ -726,7 +813,8 @@ export class IPCServer<TContext = string> implements IChannelServer<TContext>, I
dispose(): void {
this.channels.clear();
this._connections.clear();
this._onDidChangeConnections.dispose();
this._onDidAddConnection.dispose();
this._onDidRemoveConnection.dispose();
}
}
@@ -827,7 +915,7 @@ export class StaticRouter<TContext = string> implements IClientRouter<TContext>
}
}
await Event.toPromise(hub.onDidChangeConnections);
await Event.toPromise(hub.onDidAddConnection);
return await this.route(hub);
}
}
@@ -415,4 +415,80 @@ suite('Base IPC', function () {
return assert.equal(r, 'Super Context');
});
});
suite('one to many', function () {
test('all clients get pinged', async function () {
const service = new TestService();
const channel = new TestChannel(service);
const server = new TestIPCServer();
server.registerChannel('channel', channel);
let client1GotPinged = false;
const client1 = server.createConnection('client1');
const ipcService1 = new TestChannelClient(client1.getChannel('channel'));
ipcService1.onPong(() => client1GotPinged = true);
let client2GotPinged = false;
const client2 = server.createConnection('client2');
const ipcService2 = new TestChannelClient(client2.getChannel('channel'));
ipcService2.onPong(() => client2GotPinged = true);
await timeout(1);
service.ping('hello');
await timeout(1);
assert(client1GotPinged, 'client 1 got pinged');
assert(client2GotPinged, 'client 2 got pinged');
client1.dispose();
client2.dispose();
server.dispose();
});
test('server gets pings from all clients (broadcast channel)', async function () {
const server = new TestIPCServer();
const client1 = server.createConnection('client1');
const clientService1 = new TestService();
const clientChannel1 = new TestChannel(clientService1);
client1.registerChannel('channel', clientChannel1);
const pings: string[] = [];
const channel = server.getChannel('channel', () => true);
const service = new TestChannelClient(channel);
service.onPong(msg => pings.push(msg));
await timeout(1);
clientService1.ping('hello 1');
await timeout(1);
assert.deepEqual(pings, ['hello 1']);
const client2 = server.createConnection('client2');
const clientService2 = new TestService();
const clientChannel2 = new TestChannel(clientService2);
client2.registerChannel('channel', clientChannel2);
await timeout(1);
clientService2.ping('hello 2');
await timeout(1);
assert.deepEqual(pings, ['hello 1', 'hello 2']);
client1.dispose();
clientService1.ping('hello 1');
await timeout(1);
assert.deepEqual(pings, ['hello 1', 'hello 2']);
await timeout(1);
clientService2.ping('hello again 2');
await timeout(1);
assert.deepEqual(pings, ['hello 1', 'hello 2', 'hello again 2']);
client2.dispose();
server.dispose();
});
});
});

Before

Width:  |  Height:  |  Size: 594 B

After

Width:  |  Height:  |  Size: 594 B

Before

Width:  |  Height:  |  Size: 594 B

After

Width:  |  Height:  |  Size: 594 B

File diff suppressed because it is too large Load Diff
@@ -5,9 +5,7 @@
import 'vs/css!./media/quickInput';
import * as dom from 'vs/base/browser/dom';
import { InputBox, IRange, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder } from 'vs/platform/theme/common/colorRegistry';
import { ITheme } from 'vs/platform/theme/common/themeService';
import { InputBox, IRange, MessageType, IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import Severity from 'vs/base/common/severity';
@@ -112,20 +110,7 @@ export class QuickInputBox extends Disposable {
this.inputBox.layout();
}
style(theme: ITheme) {
this.inputBox.style({
inputForeground: theme.getColor(inputForeground),
inputBackground: theme.getColor(inputBackground),
inputBorder: theme.getColor(inputBorder),
inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground),
inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground),
inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder),
inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground),
inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground),
inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder),
inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground),
inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground),
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder),
});
style(styles: IInputBoxStyles) {
this.inputBox.style(styles);
}
}
@@ -7,9 +7,7 @@ import 'vs/css!./media/quickInput';
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
import * as dom from 'vs/base/browser/dom';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { WorkbenchList, IWorkbenchListOptions } from 'vs/platform/list/browser/listService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput';
import { IMatch } from 'vs/base/common/filters';
import { matchesFuzzyCodiconAware, parseCodicons } from 'vs/base/common/codicon';
import { compareAnything } from 'vs/base/common/comparers';
@@ -22,13 +20,12 @@ import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlighte
import { memoize } from 'vs/base/common/decorators';
import { range } from 'vs/base/common/arrays';
import * as platform from 'vs/base/common/platform';
import { listFocusBackground, pickerGroupBorder, pickerGroupForeground, activeContrastBorder, listFocusForeground } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action } from 'vs/base/common/actions';
import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils';
import { getIconClass } from 'vs/base/parts/quickinput/browser/quickInputUtils';
import { withNullAsUndefined } from 'vs/base/common/types';
import { QUICK_INPUT_BACKGROUND } from 'vs/workbench/common/theme';
import { IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput';
import { IListOptions, List, IListStyles } from 'vs/base/browser/ui/list/listWidget';
const $ = dom.$;
@@ -215,7 +212,7 @@ export class QuickInputList {
readonly id: string;
private container: HTMLElement;
private list: WorkbenchList<ListElement>;
private list: List<ListElement>;
private inputElements: Array<IQuickPickItem | IQuickPickSeparator> = [];
private elements: ListElement[] = [];
private elementsToIndexes = new Map<IQuickPickItem, number>();
@@ -242,21 +239,18 @@ export class QuickInputList {
constructor(
private parent: HTMLElement,
id: string,
@IInstantiationService private readonly instantiationService: IInstantiationService
options: IQuickInputOptions,
) {
this.id = id;
this.container = dom.append(this.parent, $('.quick-input-list'));
const delegate = new ListElementDelegate();
this.list = this.instantiationService.createInstance(WorkbenchList, 'QuickInput', this.container, delegate, [new ListElementRenderer()], {
this.list = options.createList('QuickInput', this.container, delegate, [new ListElementRenderer()], {
identityProvider: { getId: element => element.saneLabel },
openController: { shouldOpen: () => false }, // Workaround #58124
setRowLineHeight: false,
multipleSelectionSupport: false,
horizontalScrolling: false,
overrideStyles: {
listBackground: QUICK_INPUT_BACKGROUND
}
} as IWorkbenchListOptions<ListElement>);
} as IListOptions<ListElement>);
this.list.getHTMLElement().id = id;
this.disposables.push(this.list);
this.disposables.push(this.list.onKeyDown(e => {
@@ -577,6 +571,10 @@ export class QuickInputList {
private fireButtonTriggered(event: IQuickPickItemButtonEvent<IQuickPickItem>) {
this._onButtonTriggered.fire(event);
}
style(styles: IListStyles) {
this.list.style(styles);
}
}
function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: string): number {
@@ -593,30 +591,3 @@ function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: s
return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor);
}
registerThemingParticipant((theme, collector) => {
// Override inactive focus foreground with active focus foreground for single-pick case.
const listInactiveFocusForeground = theme.getColor(listFocusForeground);
if (listInactiveFocusForeground) {
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { color: ${listInactiveFocusForeground}; }`);
}
// Override inactive focus background with active focus background for single-pick case.
const listInactiveFocusBackground = theme.getColor(listFocusBackground);
if (listInactiveFocusBackground) {
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { background-color: ${listInactiveFocusBackground}; }`);
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused:hover { background-color: ${listInactiveFocusBackground}; }`);
}
// dotted instead of solid (as in listWidget.ts) to match QuickOpen
const activeContrast = theme.getColor(activeContrastBorder);
if (activeContrast) {
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { outline: 1px dotted ${activeContrast}; outline-offset: -1px; }`);
}
const pickerGroupBorderColor = theme.getColor(pickerGroupBorder);
if (pickerGroupBorderColor) {
collector.addRule(`.quick-input-list .quick-input-list-entry { border-top-color: ${pickerGroupBorderColor}; }`);
}
const pickerGroupForegroundColor = theme.getColor(pickerGroupForeground);
if (pickerGroupForegroundColor) {
collector.addRule(`.quick-input-list .quick-input-list-separator { color: ${pickerGroupForegroundColor}; }`);
}
});
@@ -0,0 +1,256 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
export interface IQuickPickItem {
type?: 'item';
id?: string;
label: string;
description?: string;
detail?: string;
iconClasses?: string[];
buttons?: IQuickInputButton[];
picked?: boolean;
alwaysShow?: boolean;
}
export interface IQuickPickSeparator {
type: 'separator';
label?: string;
}
export interface IKeyMods {
readonly ctrlCmd: boolean;
readonly alt: boolean;
}
export interface IQuickNavigateConfiguration {
keybindings: ResolvedKeybinding[];
}
export interface IPickOptions<T extends IQuickPickItem> {
/**
* an optional string to show as placeholder in the input box to guide the user what she picks on
*/
placeHolder?: string;
/**
* an optional flag to include the description when filtering the picks
*/
matchOnDescription?: boolean;
/**
* an optional flag to include the detail when filtering the picks
*/
matchOnDetail?: boolean;
/**
* an optional flag to filter the picks based on label. Defaults to true.
*/
matchOnLabel?: boolean;
/**
* an option flag to control whether focus is always automatically brought to a list item. Defaults to true.
*/
autoFocusOnList?: boolean;
/**
* an optional flag to not close the picker on focus lost
*/
ignoreFocusLost?: boolean;
/**
* an optional flag to make this picker multi-select
*/
canPickMany?: boolean;
/**
* enables quick navigate in the picker to open an element without typing
*/
quickNavigate?: IQuickNavigateConfiguration;
/**
* a context key to set when this picker is active
*/
contextKey?: string;
/**
* an optional property for the item to focus initially.
*/
activeItem?: Promise<T> | T;
onKeyMods?: (keyMods: IKeyMods) => void;
onDidFocus?: (entry: T) => void;
onDidTriggerItemButton?: (context: IQuickPickItemButtonContext<T>) => void;
}
export interface IInputOptions {
/**
* the value to prefill in the input box
*/
value?: string;
/**
* the selection of value, default to the whole word
*/
valueSelection?: [number, number];
/**
* the text to display underneath the input box
*/
prompt?: string;
/**
* an optional string to show as placeholder in the input box to guide the user what to type
*/
placeHolder?: string;
/**
* set to true to show a password prompt that will not show the typed value
*/
password?: boolean;
ignoreFocusLost?: boolean;
/**
* an optional function that is used to validate user input.
*/
validateInput?: (input: string) => Promise<string | null | undefined>;
}
export interface IQuickInput {
title: string | undefined;
description: string | undefined;
step: number | undefined;
totalSteps: number | undefined;
enabled: boolean;
contextKey: string | undefined;
busy: boolean;
ignoreFocusOut: boolean;
show(): void;
hide(): void;
onDidHide: Event<void>;
dispose(): void;
}
export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
value: string;
placeholder: string | undefined;
readonly onDidChangeValue: Event<string>;
readonly onDidAccept: Event<void>;
ok: boolean;
readonly onDidCustom: Event<void>;
customButton: boolean;
customLabel: string | undefined;
customHover: string | undefined;
buttons: ReadonlyArray<IQuickInputButton>;
readonly onDidTriggerButton: Event<IQuickInputButton>;
readonly onDidTriggerItemButton: Event<IQuickPickItemButtonEvent<T>>;
items: ReadonlyArray<T | IQuickPickSeparator>;
canSelectMany: boolean;
matchOnDescription: boolean;
matchOnDetail: boolean;
matchOnLabel: boolean;
sortByLabel: boolean;
autoFocusOnList: boolean;
quickNavigate: IQuickNavigateConfiguration | undefined;
activeItems: ReadonlyArray<T>;
readonly onDidChangeActive: Event<T[]>;
selectedItems: ReadonlyArray<T>;
readonly onDidChangeSelection: Event<T[]>;
readonly keyMods: IKeyMods;
valueSelection: Readonly<[number, number]> | undefined;
validationMessage: string | undefined;
inputHasFocus(): boolean;
focusOnInput(): void;
}
export interface IInputBox extends IQuickInput {
value: string;
valueSelection: Readonly<[number, number]> | undefined;
placeholder: string | undefined;
password: boolean;
readonly onDidChangeValue: Event<string>;
readonly onDidAccept: Event<void>;
buttons: ReadonlyArray<IQuickInputButton>;
readonly onDidTriggerButton: Event<IQuickInputButton>;
prompt: string | undefined;
validationMessage: string | undefined;
}
export interface IQuickInputButton {
/** iconPath or iconClass required */
iconPath?: { dark: URI; light?: URI; };
/** iconPath or iconClass required */
iconClass?: string;
tooltip?: string;
}
export interface IQuickPickItemButtonEvent<T extends IQuickPickItem> {
button: IQuickInputButton;
item: T;
}
export interface IQuickPickItemButtonContext<T extends IQuickPickItem> extends IQuickPickItemButtonEvent<T> {
removeItem(): void;
}
export type QuickPickInput<T = IQuickPickItem> = T | IQuickPickSeparator;
@@ -754,6 +754,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider, IThem
else if (autoFocus.autoFocusLastEntry) {
if (entries.length > 1) {
this.tree.focusLast();
this.tree.reveal(this.tree.getFocus());
}
}
}
@@ -85,6 +85,10 @@
opacity: 1;
}
.monaco-quick-open-widget .quick-open-tree .quick-open-entry .monaco-highlighted-label .codicon {
vertical-align: sub; /* vertically align codicon */
}
.monaco-quick-open-widget .quick-open-tree .quick-open-entry-meta {
opacity: 0.7;
line-height: normal;
@@ -356,13 +356,15 @@ suite('IndexTreeModel', function () {
assert.deepEqual(list[1].collapsible, false);
assert.deepEqual(list[1].collapsed, false);
model.setCollapsed([0], true);
assert.deepEqual(list.length, 1);
assert.deepEqual(model.setCollapsed([0], true), false);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsible, false);
assert.deepEqual(list[0].collapsed, true);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[1].element, 10);
assert.deepEqual(list[1].collapsible, false);
assert.deepEqual(list[1].collapsed, false);
model.setCollapsed([0], false);
assert.deepEqual(model.setCollapsed([0], false), false);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsible, false);
assert.deepEqual(list[0].collapsed, false);
@@ -379,13 +381,13 @@ suite('IndexTreeModel', function () {
assert.deepEqual(list[1].collapsible, false);
assert.deepEqual(list[1].collapsed, false);
model.setCollapsed([0], true);
assert.deepEqual(model.setCollapsed([0], true), true);
assert.deepEqual(list.length, 1);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsible, true);
assert.deepEqual(list[0].collapsed, true);
model.setCollapsed([0], false);
assert.deepEqual(model.setCollapsed([0], false), true);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsible, true);
assert.deepEqual(list[0].collapsed, false);
+1 -1
View File
@@ -404,7 +404,7 @@ suite('URI', () => {
path = 'foo/bar';
assert.equal(URI.file(path).path, '/foo/bar');
path = './foo/bar';
assert.equal(URI.file(path).path, '/./foo/bar'); // todo@joh missing normalization
assert.equal(URI.file(path).path, '/./foo/bar'); // missing normalization
const fileUri1 = URI.parse(`file:foo/bar`);
assert.equal(fileUri1.path, '/foo/bar');
+8 -11
View File
@@ -26,35 +26,32 @@ export class DeferredPromise<T> {
public complete(value: T) {
return new Promise(resolve => {
process.nextTick(() => {
this.completeCallback(value);
resolve();
});
this.completeCallback(value);
resolve();
});
}
public error(err: any) {
return new Promise(resolve => {
process.nextTick(() => {
this.errorCallback(err);
resolve();
});
this.errorCallback(err);
resolve();
});
}
public cancel() {
process.nextTick(() => {
new Promise(resolve => {
this.errorCallback(canceled());
resolve();
});
}
}
export function toResource(this: any, path: string) {
if (isWindows) {
return URI.file(join('C:\\', Buffer.from(this.test.fullTitle()).toString('base64'), path));
return URI.file(join('C:\\', btoa(this.test.fullTitle()), path));
}
return URI.file(join('/', Buffer.from(this.test.fullTitle()).toString('base64'), path));
return URI.file(join('/', btoa(this.test.fullTitle()), path));
}
export function suiteRepeat(n: number, description: string, callback: (this: any) => void): void {
@@ -176,8 +176,8 @@ suite('Paths (Node Implementation)', () => {
});
test('dirname', () => {
assert.strictEqual(path.dirname(path.normalize(__filename)).substr(-11),
isWindows ? 'test\\common' : 'test/common');
assert.strictEqual(path.dirname(path.normalize(__filename)).substr(-9),
isWindows ? 'test\\node' : 'test/node');
assert.strictEqual(path.posix.dirname('/a/b/'), '/a');
assert.strictEqual(path.posix.dirname('/a/b'), '/a');
+30 -174
View File
@@ -7,47 +7,14 @@ import * as assert from 'assert';
import * as os from 'os';
import * as path from 'vs/base/common/path';
import * as fs from 'fs';
import { Readable } from 'stream';
import * as uuid from 'vs/base/common/uuid';
import * as pfs from 'vs/base/node/pfs';
import { timeout } from 'vs/base/common/async';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { isWindows, isLinux } from 'vs/base/common/platform';
import { isWindows } from 'vs/base/common/platform';
import { canNormalize } from 'vs/base/common/normalization';
import { VSBuffer } from 'vs/base/common/buffer';
const chunkSize = 64 * 1024;
const readError = 'Error while reading';
function toReadable(value: string, throwError?: boolean): Readable {
const totalChunks = Math.ceil(value.length / chunkSize);
const stringChunks: string[] = [];
for (let i = 0, j = 0; i < totalChunks; ++i, j += chunkSize) {
stringChunks[i] = value.substr(j, chunkSize);
}
let counter = 0;
return new Readable({
read: function () {
if (throwError) {
this.emit('error', new Error(readError));
}
let res!: string;
let canPush = true;
while (canPush && (res = stringChunks[counter++])) {
canPush = this.push(res);
}
// EOS
if (!res) {
this.push(null);
}
},
encoding: 'utf8'
});
}
suite('PFS', function () {
// Given issues such as https://github.com/microsoft/vscode/issues/84066
@@ -334,7 +301,7 @@ suite('PFS', function () {
test('stat link', async () => {
if (isWindows) {
return Promise.resolve(); // Symlinks are not the same on win, and we can not create them programitically without admin privileges
return; // Symlinks are not the same on win, and we can not create them programitically without admin privileges
}
const id1 = uuid.generateUuid();
@@ -349,14 +316,38 @@ suite('PFS', function () {
fs.symlinkSync(directory, symbolicLink);
let statAndIsLink = await pfs.statLink(directory);
assert.ok(!statAndIsLink!.isSymbolicLink);
assert.ok(!statAndIsLink?.symbolicLink);
statAndIsLink = await pfs.statLink(symbolicLink);
assert.ok(statAndIsLink!.isSymbolicLink);
assert.ok(statAndIsLink?.symbolicLink);
assert.ok(!statAndIsLink?.symbolicLink?.dangling);
pfs.rimrafSync(directory);
});
test('stat link (non existing target)', async () => {
if (isWindows) {
return; // Symlinks are not the same on win, and we can not create them programitically without admin privileges
}
const id1 = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id1);
const directory = path.join(parentDir, 'pfs', id1);
const id2 = uuid.generateUuid();
const symbolicLink = path.join(parentDir, 'pfs', id2);
await pfs.mkdirp(directory, 493);
fs.symlinkSync(directory, symbolicLink);
pfs.rimrafSync(directory);
const statAndIsLink = await pfs.statLink(symbolicLink);
assert.ok(statAndIsLink?.symbolicLink);
assert.ok(statAndIsLink?.symbolicLink?.dangling);
});
test('readdir', async () => {
if (canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) {
const id = uuid.generateUuid();
@@ -420,17 +411,10 @@ suite('PFS', function () {
return testWriteFileAndFlush(VSBuffer.fromString(smallData).buffer, smallData, VSBuffer.fromString(bigData).buffer, bigData);
});
test('writeFile (stream)', async () => {
const smallData = 'Hello World';
const bigData = (new Array(100 * 1024)).join('Large String\n');
return testWriteFileAndFlush(toReadable(smallData), smallData, toReadable(bigData), bigData);
});
async function testWriteFileAndFlush(
smallData: string | Buffer | NodeJS.ReadableStream | Uint8Array,
smallData: string | Buffer | Uint8Array,
smallDataValue: string,
bigData: string | Buffer | NodeJS.ReadableStream | Uint8Array,
bigData: string | Buffer | Uint8Array,
bigDataValue: string
): Promise<void> {
const id = uuid.generateUuid();
@@ -450,22 +434,6 @@ suite('PFS', function () {
await pfs.rimraf(parentDir);
}
test('writeFile (file stream)', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const sourceFile = getPathFromAmdModule(require, './fixtures/index.html');
const newDir = path.join(parentDir, 'pfs', id);
const testFile = path.join(newDir, 'flushed.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
await pfs.writeFile(testFile, fs.createReadStream(sourceFile));
assert.equal(fs.readFileSync(testFile).toString(), fs.readFileSync(sourceFile).toString());
await pfs.rimraf(parentDir);
});
test('writeFile (string, error handling)', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
@@ -490,118 +458,6 @@ suite('PFS', function () {
await pfs.rimraf(parentDir);
});
test('writeFile (stream, error handling EISDIR)', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
const testFile = path.join(newDir, 'flushed.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory!
const readable = toReadable('Hello World');
let expectedError: Error | undefined;
try {
await pfs.writeFile(testFile, readable);
} catch (error) {
expectedError = error;
}
if (!expectedError || (<any>expectedError).code !== 'EISDIR') {
throw new Error('Expected EISDIR error for writing to folder but got: ' + (expectedError ? (<any>expectedError).code : 'no error'));
}
// verify that the stream is still consumable (for https://github.com/Microsoft/vscode/issues/42542)
assert.equal(readable.read(), 'Hello World');
await pfs.rimraf(parentDir);
});
test('writeFile (stream, error handling READERROR)', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
const testFile = path.join(newDir, 'flushed.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
let expectedError: Error | undefined;
try {
await pfs.writeFile(testFile, toReadable('Hello World', true /* throw error */));
} catch (error) {
expectedError = error;
}
if (!expectedError || expectedError.message !== readError) {
throw new Error('Expected error for writing to folder');
}
await pfs.rimraf(parentDir);
});
test('writeFile (stream, error handling EACCES)', async () => {
if (isLinux) {
return Promise.resolve(); // somehow this test fails on Linux in our TFS builds
}
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
const testFile = path.join(newDir, 'flushed.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
fs.writeFileSync(testFile, '');
fs.chmodSync(testFile, 33060); // make readonly
let expectedError: Error | undefined;
try {
await pfs.writeFile(testFile, toReadable('Hello World'));
} catch (error) {
expectedError = error;
}
if (!expectedError || !((<any>expectedError).code !== 'EACCES' || (<any>expectedError).code !== 'EPERM')) {
throw new Error('Expected EACCES/EPERM error for writing to folder but got: ' + (expectedError ? (<any>expectedError).code : 'no error'));
}
await pfs.rimraf(parentDir);
});
test('writeFile (file stream, error handling)', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const sourceFile = getPathFromAmdModule(require, './fixtures/index.html');
const newDir = path.join(parentDir, 'pfs', id);
const testFile = path.join(newDir, 'flushed.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory!
let expectedError: Error | undefined;
try {
await pfs.writeFile(testFile, fs.createReadStream(sourceFile));
} catch (error) {
expectedError = error;
}
if (!expectedError) {
throw new Error('Expected error for writing to folder');
}
await pfs.rimraf(parentDir);
});
test('writeFileSync', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
@@ -31,6 +31,7 @@
'onigasm-umd': `${window.location.origin}/static/remote/web/node_modules/onigasm-umd/release/main`,
'xterm': `${window.location.origin}/static/remote/web/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-unicode11': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
'xterm-addon-web-links': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'xterm-addon-webgl': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
'semver-umd': `${window.location.origin}/static/remote/web/node_modules/semver-umd/lib/semver-umd.js`,
@@ -35,6 +35,7 @@
'onigasm-umd': `${window.location.origin}/static/node_modules/onigasm-umd/release/main`,
'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-unicode11': `${window.location.origin}/static/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'xterm-addon-webgl': `${window.location.origin}/static/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`,
@@ -35,7 +35,7 @@ export default (): string => `
<div id="extension-selection-validation-error" class="validation-error hidden" role="alert">${escape(localize('extensionWithNonstandardBugsUrl', "The issue reporter is unable to create issues for this extension. Please visit {0} to report an issue."))
.replace('{0}', `<span tabIndex=0 role="button" id="extensionBugsLink" class="workbenchCommand"><!-- To be dynamically filled --></span>`)}</div>
<div id="extension-selection-validation-error-no-url" class="validation-error hidden" role="alert">
${escape(localize('extensionWithNoBugsUrl', "The issue reporter is unable to create issues for this extension, as it does not specify a URL for reporting issues. Please check the marketplace page of this extension so see if other instructions are available."))}
${escape(localize('extensionWithNoBugsUrl', "The issue reporter is unable to create issues for this extension, as it does not specify a URL for reporting issues. Please check the marketplace page of this extension to see if other instructions are available."))}
</div>
</div>
</div>
@@ -26,7 +26,6 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper
import { TelemetryAppenderChannel } from 'vs/platform/telemetry/node/telemetryIpc';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
import { ActiveWindowManager } from 'vs/code/node/activeWindowTracker';
import { ipcRenderer } from 'electron';
import { ILogService, LogLevel, ILoggerService } from 'vs/platform/log/common/log';
import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
@@ -59,7 +58,7 @@ import { LoggerService } from 'vs/platform/log/node/loggerService';
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService';
import { UserDataAutoSync } from 'vs/platform/userDataSync/electron-browser/userDataAutoSync';
import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-browser/userDataAutoSyncService';
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
import { UserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataAuthTokenService';
@@ -131,9 +130,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const electronService = createChannelSender<IElectronService>(mainProcessService.getChannel('electron'), { context: configuration.windowId });
services.set(IElectronService, electronService);
const activeWindowManager = new ActiveWindowManager(electronService);
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
// Files
const fileService = new FileService(logService);
services.set(IFileService, fileService);
@@ -184,8 +180,8 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
services.set(ICredentialsService, new SyncDescriptor(KeytarCredentialsService));
services.set(IUserDataAuthTokenService, new SyncDescriptor(UserDataAuthTokenService));
services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService));
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', activeWindowRouter)));
services.set(IGlobalExtensionEnablementService, new GlobalExtensionEnablementServiceClient(server.getChannel('globalExtensionEnablement', activeWindowRouter)));
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main')));
services.set(IGlobalExtensionEnablementService, new GlobalExtensionEnablementServiceClient(server.getChannel('globalExtensionEnablement', client => client.ctx !== 'main')));
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser));
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
@@ -219,7 +215,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService);
server.registerChannel('userDataSync', userDataSyncChannel);
const userDataAutoSync = instantiationService2.createInstance(UserDataAutoSync);
const userDataAutoSync = instantiationService2.createInstance(UserDataAutoSyncService);
const userDataAutoSyncChannel = new UserDataAutoSyncChannel(userDataAutoSync);
server.registerChannel('userDataAutoSync', userDataAutoSyncChannel);
+18 -19
View File
@@ -171,7 +171,7 @@ export class CodeApplication extends Disposable {
app.on('web-contents-created', (_event: Event, contents) => {
contents.on('will-attach-webview', (event: Event, webPreferences, params) => {
const isValidWebviewSource = (source: string): boolean => {
const isValidWebviewSource = (source: string | undefined): boolean => {
if (!source) {
return false;
}
@@ -191,11 +191,12 @@ export class CodeApplication extends Disposable {
webPreferences.nodeIntegration = false;
// Verify URLs being loaded
if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) {
// https://github.com/electron/electron/issues/21553
if (isValidWebviewSource(params.src) && isValidWebviewSource((webPreferences as { preloadURL: string }).preloadURL)) {
return;
}
delete webPreferences.preloadUrl;
delete (webPreferences as { preloadURL: string }).preloadURL; // https://github.com/electron/electron/issues/21553
// Otherwise prevent loading
this.logService.error('webContents#web-contents-created: Prevented webview attach');
@@ -497,27 +498,27 @@ export class CodeApplication extends Disposable {
this.logService.info(`Tracing: waiting for windows to get ready...`);
let recordingStopped = false;
const stopRecording = (timeout: boolean) => {
const stopRecording = async (timeout: boolean) => {
if (recordingStopped) {
return;
}
recordingStopped = true; // only once
contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => {
if (!timeout) {
if (this.dialogMainService) {
this.dialogMainService.showMessageBox({
type: 'info',
message: localize('trace.message', "Successfully created trace."),
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
buttons: [localize('trace.ok', "Ok")]
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
}
} else {
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
const path = await contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`));
if (!timeout) {
if (this.dialogMainService) {
this.dialogMainService.showMessageBox({
type: 'info',
message: localize('trace.message', "Successfully created trace."),
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
buttons: [localize('trace.ok', "Ok")]
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
}
});
} else {
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
}
};
// Wait up to 30s before creating the trace anyways
@@ -594,8 +595,6 @@ export class CodeApplication extends Disposable {
// Catch file URLs
if (uri.authority === Schemas.file && !!uri.path) {
const cli = assign(Object.create(null), environmentService.args);
// hey Ben, we need to convert this `code://file` URI into a `file://` URI
const urisToOpen = [{ fileUri: URI.file(uri.fsPath) }];
windowsMainService.open({ context: OpenContext.API, cli, urisToOpen, gotoLineMode: true });
+9 -9
View File
@@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects';
import * as nls from 'vs/nls';
import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment } from 'electron';
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme } from 'electron';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -347,9 +347,9 @@ export class CodeWindow extends Disposable implements ICodeWindow {
});
this._win.webContents.session.webRequest.onHeadersReceived(null!, (details, callback) => {
const responseHeaders = details.responseHeaders as { [key: string]: string[] };
const responseHeaders = details.responseHeaders as Record<string, (string) | (string[])>;
const contentType: string[] = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
const contentType = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) {
return callback({ cancel: true });
}
@@ -441,7 +441,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// Inject headers when requests are incoming
const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) =>
this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined } })));
this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as Record<string, string> })));
}
private onWindowError(error: WindowError): void {
@@ -648,7 +648,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
if (windowConfig?.autoDetectHighContrast === false) {
autoDetectHighContrast = false;
}
windowConfiguration.highContrast = isWindows && autoDetectHighContrast && systemPreferences.isInvertedColorScheme();
windowConfiguration.highContrast = isWindows && autoDetectHighContrast && nativeTheme.shouldUseInvertedColorScheme;
windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled;
// Title style related
@@ -1007,22 +1007,22 @@ export class CodeWindow extends Disposable implements ICodeWindow {
switch (visibility) {
case ('default'):
this._win.setMenuBarVisibility(!isFullscreen);
this._win.setAutoHideMenuBar(isFullscreen);
this._win.autoHideMenuBar = isFullscreen;
break;
case ('visible'):
this._win.setMenuBarVisibility(true);
this._win.setAutoHideMenuBar(false);
this._win.autoHideMenuBar = false;
break;
case ('toggle'):
this._win.setMenuBarVisibility(false);
this._win.setAutoHideMenuBar(true);
this._win.autoHideMenuBar = true;
break;
case ('hidden'):
this._win.setMenuBarVisibility(false);
this._win.setAutoHideMenuBar(false);
this._win.autoHideMenuBar = false;
break;
}
}
@@ -3,24 +3,27 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IDimension } from 'vs/editor/common/editorCommon';
import * as dom from 'vs/base/browser/dom';
export class ElementSizeObserver extends Disposable {
private readonly referenceDomElement: HTMLElement | null;
private measureReferenceDomElementToken: any;
private readonly changeCallback: () => void;
private width: number;
private height: number;
private mutationObserver: MutationObserver | null;
private windowSizeListener: IDisposable | null;
constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void) {
super();
this.referenceDomElement = referenceDomElement;
this.changeCallback = changeCallback;
this.measureReferenceDomElementToken = -1;
this.width = -1;
this.height = -1;
this.mutationObserver = null;
this.windowSizeListener = null;
this.measureReferenceDomElement(false, dimension);
}
@@ -38,15 +41,25 @@ export class ElementSizeObserver extends Disposable {
}
public startObserving(): void {
if (this.measureReferenceDomElementToken === -1) {
this.measureReferenceDomElementToken = setInterval(() => this.measureReferenceDomElement(true), 100);
if (!this.mutationObserver && this.referenceDomElement) {
this.mutationObserver = new MutationObserver(() => this._onDidMutate());
this.mutationObserver.observe(this.referenceDomElement, {
attributes: true,
});
}
if (!this.windowSizeListener) {
this.windowSizeListener = dom.addDisposableListener(window, 'resize', () => this._onDidResizeWindow());
}
}
public stopObserving(): void {
if (this.measureReferenceDomElementToken !== -1) {
clearInterval(this.measureReferenceDomElementToken);
this.measureReferenceDomElementToken = -1;
if (this.mutationObserver) {
this.mutationObserver.disconnect();
this.mutationObserver = null;
}
if (this.windowSizeListener) {
this.windowSizeListener.dispose();
this.windowSizeListener = null;
}
}
@@ -54,6 +67,14 @@ export class ElementSizeObserver extends Disposable {
this.measureReferenceDomElement(true, dimension);
}
private _onDidMutate(): void {
this.measureReferenceDomElement(true);
}
private _onDidResizeWindow(): void {
this.measureReferenceDomElement(true);
}
private measureReferenceDomElement(callChangeCallback: boolean, dimension?: IDimension): void {
let observedWidth = 0;
let observedHeight = 0;
@@ -1753,12 +1753,17 @@ registerCommand(new EditorOrNativeTextInputCommand({
kbExpr: null,
primary: KeyMod.CtrlCmd | KeyCode.KEY_A
},
menuOpts: {
menuOpts: [{
menuId: MenuId.MenubarEditMenu, // {{SQL CARBON EDIT}} - Put this in the edit menu since we disabled the selection menu
group: '4_find_global', // {{SQL CARBON EDIT}} - Put this in the edit menu since we disabled the selection menu
title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"),
order: 1
}
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('selectAll', "Select All"),
order: 1
}]
}));
registerCommand(new EditorOrNativeTextInputCommand({
@@ -1771,12 +1776,17 @@ registerCommand(new EditorOrNativeTextInputCommand({
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_Z
},
menuOpts: {
menuOpts: [{
menuId: MenuId.MenubarEditMenu,
group: '1_do',
title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"),
order: 1
}
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('undo', "Undo"),
order: 1
}]
}));
registerCommand(new EditorHandlerCommand('default:' + Handler.Undo, Handler.Undo));
@@ -1792,12 +1802,17 @@ registerCommand(new EditorOrNativeTextInputCommand({
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z],
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }
},
menuOpts: {
menuOpts: [{
menuId: MenuId.MenubarEditMenu,
group: '1_do',
title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"),
order: 2
}
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('redo', "Redo"),
order: 1
}]
}));
registerCommand(new EditorHandlerCommand('default:' + Handler.Redo, Handler.Redo));
@@ -357,8 +357,9 @@ export class TextAreaHandler extends ViewPart {
this._accessibilitySupport = options.get(EditorOption.accessibilitySupport);
const accessibilityPageSize = options.get(EditorOption.accessibilityPageSize);
if (this._accessibilitySupport === AccessibilitySupport.Enabled && accessibilityPageSize === EditorOptions.accessibilityPageSize.defaultValue) {
// If a screen reader is attached and the default value is not set we shuold automatically increase the page size to 1000 for a better experience
this._accessibilityPageSize = 1000;
// If a screen reader is attached and the default value is not set we shuold automatically increase the page size to 160 for a better experience
// If we put more than 160 lines the nvda can not handle this https://github.com/microsoft/vscode/issues/89717
this._accessibilityPageSize = 160;
} else {
this._accessibilityPageSize = accessibilityPageSize;
}
@@ -331,7 +331,7 @@ export class TextAreaInput extends Disposable {
this._onType.fire(typeInput);
}
} else {
if (typeInput.text !== '') {
if (typeInput.text !== '' || typeInput.replaceCharCnt !== 0) {
this._firePaste(typeInput.text, null);
}
this._nextCommand = ReadFromTextArea.Type;
@@ -483,6 +483,9 @@ export class TextAreaInput extends Disposable {
// Setting this._hasFocus and writing the screen reader content
// will result in a focus() and setSelectionRange() in the textarea
this._setHasFocus(true);
// If the editor is off DOM, focus cannot be really set, so let's double check that we have managed to set the focus
this.refreshFocusState();
}
public isFocused(): boolean {
@@ -490,8 +493,11 @@ export class TextAreaInput extends Disposable {
}
public refreshFocusState(): void {
if (document.body.contains(this.textArea.domNode) && document.activeElement === this.textArea.domNode) {
this._setHasFocus(true);
const shadowRoot = dom.getShadowRoot(this.textArea.domNode);
if (shadowRoot) {
this._setHasFocus(shadowRoot.activeElement === this.textArea.domNode);
} else if (dom.isInDOM(this.textArea.domNode)) {
this._setHasFocus(document.activeElement === this.textArea.domNode);
} else {
this._setHasFocus(false);
}
@@ -696,7 +702,15 @@ class TextAreaWrapper extends Disposable implements ITextAreaWrapper {
public setSelectionRange(reason: string, selectionStart: number, selectionEnd: number): void {
const textArea = this._actual.domNode;
const currentIsFocused = (document.activeElement === textArea);
let activeElement: Element | null = null;
const shadowRoot = dom.getShadowRoot(textArea);
if (shadowRoot) {
activeElement = shadowRoot.activeElement;
} else {
activeElement = document.activeElement;
}
const currentIsFocused = (activeElement === textArea);
const currentSelectionStart = textArea.selectionStart;
const currentSelectionEnd = textArea.selectionEnd;
@@ -27,6 +27,8 @@ export type IBulkEditPreviewHandler = (edit: WorkspaceEdit, options?: IBulkEditO
export interface IBulkEditService {
_serviceBrand: undefined;
hasPreviewHandler(): boolean;
setPreviewHandler(handler: IBulkEditPreviewHandler): IDisposable;
apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise<IBulkEditResult>;
@@ -111,6 +111,7 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe
containerDomNode.style.position = 'absolute';
containerDomNode.style.top = '10000';
containerDomNode.style.wordWrap = 'break-word';
document.body.appendChild(containerDomNode);
let range = document.createRange();
@@ -316,8 +316,9 @@ class Widget {
}
private _layoutHorizontalSegmentInPage(windowSize: dom.Dimension, domNodePosition: dom.IDomNodePagePosition, left: number, width: number): [number, number] {
const MIN_LIMIT = (width <= domNodePosition.width - 20 ? domNodePosition.left : 0);
const MAX_LIMIT = (width <= domNodePosition.width - 20 ? domNodePosition.left + domNodePosition.width - 20 : windowSize.width - 20);
// Initially, the limits are defined as the dom node limits
const MIN_LIMIT = Math.max(0, domNodePosition.left - width);
const MAX_LIMIT = Math.min(domNodePosition.left + domNodePosition.width + width, windowSize.width);
let absoluteLeft = domNodePosition.left + left - dom.StandardWindow.scrollX;
@@ -34,6 +34,7 @@ export class EditorScrollbar extends ViewPart {
const scrollbar = options.get(EditorOption.scrollbar);
const mouseWheelScrollSensitivity = options.get(EditorOption.mouseWheelScrollSensitivity);
const fastScrollSensitivity = options.get(EditorOption.fastScrollSensitivity);
const scrollPredominantAxis = options.get(EditorOption.scrollPredominantAxis);
const scrollbarOptions: ScrollableElementCreationOptions = {
listenOnDomNode: viewDomNode.domNode,
@@ -54,6 +55,7 @@ export class EditorScrollbar extends ViewPart {
arrowSize: scrollbar.arrowSize,
mouseWheelScrollSensitivity: mouseWheelScrollSensitivity,
fastScrollSensitivity: fastScrollSensitivity,
scrollPredominantAxis: scrollPredominantAxis,
};
this.scrollbar = this._register(new SmoothScrollableElement(linesContent.domNode, scrollbarOptions, this._context.viewLayout.getScrollable()));
@@ -140,10 +142,12 @@ export class EditorScrollbar extends ViewPart {
const scrollbar = options.get(EditorOption.scrollbar);
const mouseWheelScrollSensitivity = options.get(EditorOption.mouseWheelScrollSensitivity);
const fastScrollSensitivity = options.get(EditorOption.fastScrollSensitivity);
const scrollPredominantAxis = options.get(EditorOption.scrollPredominantAxis);
const newOpts: ScrollableElementChangeOptions = {
handleMouseWheel: scrollbar.handleMouseWheel,
mouseWheelScrollSensitivity: mouseWheelScrollSensitivity,
fastScrollSensitivity: fastScrollSensitivity
fastScrollSensitivity: fastScrollSensitivity,
scrollPredominantAxis: scrollPredominantAxis
};
this.scrollbar.updateOptions(newOpts);
}
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
.monaco-editor .margin-view-overlays .line-numbers {
font-variant-numeric: tabular-nums;
position: absolute;
text-align: right;
display: inline-block;
@@ -37,6 +37,10 @@
width: 100%;
}
.monaco-editor .mtkz {
display: inline-block;
}
/* TODO@tokenization bootstrap fix */
/*.monaco-editor .view-line > span > span {
float: none;
@@ -589,6 +589,14 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost<ViewLine>,
if (boxEndY - boxStartY > viewportHeight) {
// the box is larger than the viewport ... scroll to its top
newScrollTop = boxStartY;
} else if (verticalType === viewEvents.VerticalRevealType.NearTop) {
// We want a gap that is 20% of the viewport, but with a minimum of 5 lines
const desiredGapAbove = Math.max(5 * this._lineHeight, viewportHeight * 0.2);
// Try to scroll just above the box with the desired gap
const desiredScrollTop = boxStartY - desiredGapAbove;
// But ensure that the box is not pushed out of viewport
const minScrollTop = boxEndY - viewportHeight;
newScrollTop = Math.max(minScrollTop, desiredScrollTop);
} else if (verticalType === viewEvents.VerticalRevealType.Center || verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport) {
if (verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport && viewportStartY <= boxStartY && boxEndY <= viewportEndY) {
// Box is already in the viewport... do nothing
@@ -25,8 +25,8 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v
import { ViewContext } from 'vs/editor/common/view/viewContext';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { ViewLineData } from 'vs/editor/common/viewModel/viewModel';
import { scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapSelection } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { minimapSelection, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapBackground } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService';
import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel';
import { Selection } from 'vs/editor/common/core/selection';
import { Color } from 'vs/base/common/color';
@@ -107,7 +107,9 @@ class MinimapOptions {
*/
public readonly canvasOuterHeight: number;
constructor(configuration: IConfiguration) {
public readonly backgroundColor: RGBA8;
constructor(configuration: IConfiguration, theme: ITheme, tokensColorTracker: MinimapTokensColorTracker) {
const options = configuration.options;
const pixelRatio = options.get(EditorOption.pixelRatio);
const layoutInfo = options.get(EditorOption.layoutInfo);
@@ -131,6 +133,16 @@ class MinimapOptions {
this.canvasOuterWidth = this.canvasInnerWidth / pixelRatio;
this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio;
this.backgroundColor = MinimapOptions._getMinimapBackground(theme, tokensColorTracker);
}
private static _getMinimapBackground(theme: ITheme, tokensColorTracker: MinimapTokensColorTracker): RGBA8 {
const themeColor = theme.getColor(minimapBackground);
if (themeColor) {
return new RGBA8(themeColor.rgba.r, themeColor.rgba.g, themeColor.rgba.b, themeColor.rgba.a);
}
return tokensColorTracker.getColor(ColorId.DefaultBackground);
}
public equals(other: MinimapOptions): boolean {
@@ -148,6 +160,7 @@ class MinimapOptions {
&& this.canvasInnerHeight === other.canvasInnerHeight
&& this.canvasOuterWidth === other.canvasOuterWidth
&& this.canvasOuterHeight === other.canvasOuterHeight
&& this.backgroundColor.equals(other.backgroundColor)
);
}
}
@@ -447,13 +460,13 @@ class MinimapBuffers {
export class Minimap extends ViewPart {
private readonly _tokensColorTracker: MinimapTokensColorTracker;
private readonly _domNode: FastDomNode<HTMLElement>;
private readonly _shadow: FastDomNode<HTMLElement>;
private readonly _canvas: FastDomNode<HTMLCanvasElement>;
private readonly _decorationsCanvas: FastDomNode<HTMLCanvasElement>;
private readonly _slider: FastDomNode<HTMLElement>;
private readonly _sliderHorizontal: FastDomNode<HTMLElement>;
private readonly _tokensColorTracker: MinimapTokensColorTracker;
private readonly _mouseDownListener: IDisposable;
private readonly _sliderMouseMoveMonitor: GlobalMouseMoveMonitor<IStandardMouseMoveEventData>;
private readonly _sliderMouseDownListener: IDisposable;
@@ -473,7 +486,8 @@ export class Minimap extends ViewPart {
constructor(context: ViewContext) {
super(context);
this._options = new MinimapOptions(this._context.configuration);
this._tokensColorTracker = MinimapTokensColorTracker.getInstance();
this._options = new MinimapOptions(this._context.configuration, this._context.theme, this._tokensColorTracker);
this._lastRenderData = null;
this._buffers = null;
this._selectionColor = this._context.theme.getColor(minimapSelection);
@@ -512,8 +526,6 @@ export class Minimap extends ViewPart {
this._sliderHorizontal.setClassName('minimap-slider-horizontal');
this._slider.appendChild(this._sliderHorizontal);
this._tokensColorTracker = MinimapTokensColorTracker.getInstance();
this._applyLayout();
this._mouseDownListener = dom.addStandardDisposableListener(this._domNode.domNode, 'mousedown', (e) => {
@@ -664,7 +676,7 @@ export class Minimap extends ViewPart {
this._canvas.domNode.getContext('2d')!,
this._options.canvasInnerWidth,
this._options.canvasInnerHeight,
this._tokensColorTracker.getColor(ColorId.DefaultBackground)
this._options.backgroundColor
);
}
}
@@ -672,7 +684,7 @@ export class Minimap extends ViewPart {
}
private _onOptionsMaybeChanged(): boolean {
const opts = new MinimapOptions(this._context.configuration);
const opts = new MinimapOptions(this._context.configuration, this._context.theme, this._tokensColorTracker);
if (this._options.equals(opts)) {
return false;
}
@@ -745,6 +757,7 @@ export class Minimap extends ViewPart {
this._context.model.invalidateMinimapColorCache();
this._selectionColor = this._context.theme.getColor(minimapSelection);
this._renderDecorations = true;
this._onOptionsMaybeChanged();
return true;
}
@@ -856,7 +869,7 @@ export class Minimap extends ViewPart {
const y = (lineNumber - layout.startLineNumber) * lineHeight;
// Skip rendering the line if it's vertically outside our viewport
if (y + height < 0 || y > this._options.canvasOuterHeight) {
if (y + height < 0 || y > this._options.canvasInnerHeight) {
return;
}
@@ -942,7 +955,7 @@ export class Minimap extends ViewPart {
// Fetch rendering info from view model for rest of lines that need rendering.
const lineInfo = this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed);
const tabSize = lineInfo.tabSize;
const background = this._tokensColorTracker.getColor(ColorId.DefaultBackground);
const background = this._options.backgroundColor;
const useLighterFont = this._tokensColorTracker.backgroundIsLight();
// Render the rest of lines
@@ -1141,6 +1154,10 @@ export class Minimap extends ViewPart {
}
registerThemingParticipant((theme, collector) => {
const minimapBackgroundValue = theme.getColor(minimapBackground);
if (minimapBackgroundValue) {
collector.addRule(`.monaco-editor .minimap > canvas { opacity: ${minimapBackgroundValue.rgba.a}; will-change: opacity; }`);
}
const sliderBackground = theme.getColor(scrollbarSliderBackground);
if (sliderBackground) {
const halfSliderBackground = sliderBackground.transparent(0.5);
@@ -5,6 +5,7 @@
import { RGBA8 } from 'vs/editor/common/core/rgba';
import { Constants, getCharIndex } from './minimapCharSheet';
import { toUint8 } from 'vs/base/common/uint';
export class MinimapCharRenderer {
_minimapCharRendererBrand: void;
@@ -20,7 +21,7 @@ export class MinimapCharRenderer {
private static soften(input: Uint8ClampedArray, ratio: number): Uint8ClampedArray {
let result = new Uint8ClampedArray(input.length);
for (let i = 0, len = input.length; i < len; i++) {
result[i] = input[i] * ratio;
result[i] = toUint8(input[i] * ratio);
}
return result;
}
@@ -7,6 +7,7 @@ import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimap
import { allCharCodes } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet';
import { prebakedMiniMaps } from 'vs/editor/browser/viewParts/minimap/minimapPreBaked';
import { Constants } from './minimapCharSheet';
import { toUint8 } from 'vs/base/common/uint';
/**
* Creates character renderers. It takes a 'scale' that determines how large
@@ -135,7 +136,7 @@ export class MinimapCharRendererFactory {
const final = value / samples;
brightest = Math.max(brightest, final);
dest[targetIndex++] = final;
dest[targetIndex++] = toUint8(final);
}
}
@@ -11,13 +11,13 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v
import { ViewContext } from 'vs/editor/common/view/viewContext';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { EditorOption, IRulerOption } from 'vs/editor/common/config/editorOptions';
export class Rulers extends ViewPart {
public domNode: FastDomNode<HTMLElement>;
private readonly _renderedRulers: FastDomNode<HTMLElement>[];
private _rulers: number[];
private _rulers: IRulerOption[];
private _typicalHalfwidthCharacterWidth: number;
constructor(context: ViewContext) {
@@ -92,9 +92,11 @@ export class Rulers extends ViewPart {
for (let i = 0, len = this._rulers.length; i < len; i++) {
const node = this._renderedRulers[i];
const ruler = this._rulers[i];
node.setBoxShadow(ruler.color ? `1px 0 0 0 ${ruler.color} inset` : ``);
node.setHeight(Math.min(ctx.scrollHeight, 1000000));
node.setLeft(this._rulers[i] * this._typicalHalfwidthCharacterWidth);
node.setLeft(ruler.column * this._typicalHalfwidthCharacterWidth);
}
}
}
@@ -557,6 +557,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
this._revealLine(lineNumber, VerticalRevealType.CenterIfOutsideViewport, scrollType);
}
public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this._revealLine(lineNumber, VerticalRevealType.NearTop, scrollType);
}
private _revealLine(lineNumber: number, revealType: VerticalRevealType, scrollType: editorCommon.ScrollType): void {
if (typeof lineNumber !== 'number') {
throw new Error('Invalid arguments');
@@ -597,6 +601,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
);
}
public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this._revealPosition(
position,
VerticalRevealType.NearTop,
true,
scrollType
);
}
private _revealPosition(position: IPosition, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
if (!Position.isIPosition(position)) {
throw new Error('Invalid arguments');
@@ -685,6 +698,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
);
}
public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this._revealLines(
startLineNumber,
endLineNumber,
VerticalRevealType.NearTop,
scrollType
);
}
private _revealLines(startLineNumber: number, endLineNumber: number, verticalType: VerticalRevealType, scrollType: editorCommon.ScrollType): void {
if (typeof startLineNumber !== 'number' || typeof endLineNumber !== 'number') {
throw new Error('Invalid arguments');
@@ -725,6 +747,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
);
}
public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this._revealRange(
range,
VerticalRevealType.NearTop,
true,
scrollType
);
}
public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this._revealRange(
range,
@@ -1613,6 +1644,7 @@ export class BooleanEventEmitter extends Disposable {
class EditorContextKeysManager extends Disposable {
private readonly _editor: CodeEditorWidget;
private readonly _editorSimpleInput: IContextKey<boolean>;
private readonly _editorFocus: IContextKey<boolean>;
private readonly _textInputFocus: IContextKey<boolean>;
private readonly _editorTextFocus: IContextKey<boolean>;
@@ -1632,6 +1664,8 @@ class EditorContextKeysManager extends Disposable {
this._editor = editor;
contextKeyService.createKey('editorId', editor.getId());
this._editorSimpleInput = EditorContextKeys.editorSimpleInput.bindTo(contextKeyService);
this._editorFocus = EditorContextKeys.focus.bindTo(contextKeyService);
this._textInputFocus = EditorContextKeys.textInputFocus.bindTo(contextKeyService);
this._editorTextFocus = EditorContextKeys.editorTextFocus.bindTo(contextKeyService);
@@ -1655,6 +1689,8 @@ class EditorContextKeysManager extends Disposable {
this._updateFromSelection();
this._updateFromFocus();
this._updateFromModel();
this._editorSimpleInput.set(this._editor.isSimpleWidget);
}
private _updateFromConfig(): void {
@@ -38,7 +38,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry';
import { ITheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { reverseLineChanges } from 'sql/editor/browser/diffEditorHelper'; // {{SQL CARBON EDIT}}
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
@@ -768,6 +768,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this.modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType);
}
public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this.modifiedEditor.revealLineNearTop(lineNumber, scrollType);
}
public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this.modifiedEditor.revealPosition(position, scrollType);
}
@@ -780,6 +784,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this.modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType);
}
public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this.modifiedEditor.revealPositionNearTop(position, scrollType);
}
public getSelection(): Selection | null {
return this.modifiedEditor.getSelection();
}
@@ -812,6 +820,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this.modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType);
}
public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this.modifiedEditor.revealLinesNearTop(startLineNumber, endLineNumber, scrollType);
}
public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void {
this.modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal);
}
@@ -824,6 +836,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType);
}
public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this.modifiedEditor.revealRangeNearTop(range, scrollType);
}
public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
this.modifiedEditor.revealRangeAtTop(range, scrollType);
}
@@ -2227,4 +2243,32 @@ registerThemingParticipant((theme, collector) => {
if (border) {
collector.addRule(`.monaco-diff-editor.side-by-side .editor.modified { border-left: 1px solid ${border}; }`);
}
const scrollbarSliderBackgroundColor = theme.getColor(scrollbarSliderBackground);
if (scrollbarSliderBackgroundColor) {
collector.addRule(`
.monaco-diff-editor .diffViewport {
background: ${scrollbarSliderBackgroundColor};
}
`);
}
const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground);
if (scrollbarSliderHoverBackgroundColor) {
collector.addRule(`
.monaco-diff-editor .diffViewport:hover {
background: ${scrollbarSliderHoverBackgroundColor};
}
`);
}
const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground);
if (scrollbarSliderActiveBackgroundColor) {
collector.addRule(`
.monaco-diff-editor .diffViewport:active {
background: ${scrollbarSliderActiveBackgroundColor};
}
`);
}
});
@@ -8,19 +8,14 @@
z-index: 9;
}
.monaco-diff-editor .diffOverview .diffViewport {
z-index: 10;
}
/* colors not externalized: using transparancy on background */
.monaco-diff-editor.vs .diffOverview { background: rgba(0, 0, 0, 0.03); }
.monaco-diff-editor.vs-dark .diffOverview { background: rgba(255, 255, 255, 0.01); }
.monaco-diff-editor .diffViewport {
box-shadow: inset 0px 0px 1px 0px #B9B9B9;
background: rgba(0, 0, 0, 0.10);
}
.monaco-diff-editor.vs-dark .diffViewport,
.monaco-diff-editor.hc-black .diffViewport {
background: rgba(255, 255, 255, 0.10);
}
.monaco-scrollable-element.modified-in-monaco-diff-editor.vs .scrollbar { background: rgba(0,0,0,0); }
.monaco-scrollable-element.modified-in-monaco-diff-editor.vs-dark .scrollbar { background: rgba(0,0,0,0); }
.monaco-scrollable-element.modified-in-monaco-diff-editor.hc-black .scrollbar { background: none; }
@@ -484,6 +484,11 @@ const editorConfiguration: IConfigurationNode = {
default: true,
description: nls.localize('wordBasedSuggestions', "Controls whether completions should be computed based on words in the document.")
},
'editor.semanticHighlighting.enabled': {
type: 'boolean',
default: true,
description: nls.localize('semanticHighlighting.enabled', "Controls whether the semanticHighlighting is shown for the languages that support it.")
},
'editor.stablePeek': {
type: 'boolean',
default: false,
+103 -82
View File
@@ -58,7 +58,7 @@ export interface IEditorOptions {
* Render vertical lines at the specified columns.
* Defaults to empty array.
*/
rulers?: number[];
rulers?: (number | IRulerOption)[];
/**
* A string containing the word separators used when doing word navigation.
* Defaults to `~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?
@@ -74,7 +74,7 @@ export interface IEditorOptions {
* If it is a function, it will be invoked when rendering a line number and the return value will be rendered.
* Otherwise, if it is a truey, line numbers will be rendered normally (equivalent of using an identity function).
* Otherwise, line numbers will not be rendered.
* Defaults to true.
* Defaults to `on`.
*/
lineNumbers?: LineNumbersType;
/**
@@ -267,10 +267,10 @@ export interface IEditorOptions {
*/
wrappingIndent?: 'none' | 'same' | 'indent' | 'deepIndent';
/**
* Controls the wrapping algorithm to use.
* Defaults to 'monospace'.
* Controls the wrapping strategy to use.
* Defaults to 'simple'.
*/
wrappingAlgorithm?: 'monospace' | 'dom';
wrappingStrategy?: 'simple' | 'advanced';
/**
* Configure word wrapping characters. A break will be introduced before these characters.
* Defaults to '([{‘“〈《「『【〔([{「£¥$£¥++'.
@@ -319,6 +319,11 @@ export interface IEditorOptions {
* Defaults to 5.
*/
fastScrollSensitivity?: number;
/**
* Enable that the editor scrolls only the predominant axis. Prevents horizontal drift when scrolling vertically on a trackpad.
* Defaults to true.
*/
scrollPredominantAxis?: boolean;
/**
* The modifier to be used to add multiple cursors with the mouse.
* Defaults to 'alt'
@@ -385,8 +390,8 @@ export interface IEditorOptions {
*/
autoSurround?: EditorAutoSurroundStrategy;
/**
* Enable auto indentation adjustment.
* Defaults to false.
* Controls whether the editor should automatically adjust the indentation when users type, paste, move or indent lines.
* Defaults to advanced.
*/
autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full';
/**
@@ -555,6 +560,11 @@ export interface IEditorOptions {
* Defaults to false.
*/
peekWidgetDefaultFocus?: 'tree' | 'editor';
/**
* Controls whether the definition link opens element in the peek widget.
* Defaults to false.
*/
definitionLinkOpensInPeek?: boolean;
}
export interface IEditorConstructionOptions extends IEditorOptions {
@@ -625,9 +635,6 @@ export class ConfigurationChangedEvent {
constructor(values: boolean[]) {
this._values = values;
}
/**
* @internal
*/
public hasChanged(id: EditorOption): boolean {
return this._values[id];
}
@@ -1592,55 +1599,6 @@ class EditorHover extends BaseEditorOption<EditorOption.hover, EditorHoverOption
//#endregion
//#region semantic highlighting
/**
* Configuration options for semantic highlighting
*/
export interface IEditorSemanticHighlightingOptions {
/**
* Enable semantic highlighting.
* Defaults to true.
*/
enabled?: boolean;
}
/**
* @internal
*/
export type EditorSemanticHighlightingOptions = Readonly<Required<IEditorSemanticHighlightingOptions>>;
class EditorSemanticHighlighting extends BaseEditorOption<EditorOption.semanticHighlighting, EditorSemanticHighlightingOptions> {
constructor() {
const defaults: EditorSemanticHighlightingOptions = {
enabled: true
};
super(
EditorOption.semanticHighlighting, 'semanticHighlighting', defaults,
{
'editor.semanticHighlighting.enabled': {
type: 'boolean',
default: defaults.enabled,
description: nls.localize('semanticHighlighting.enabled', "Controls whether the semanticHighlighting is shown for the languages that support it.")
}
}
);
}
public validate(_input: any): EditorSemanticHighlightingOptions {
if (typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorSemanticHighlightingOptions;
return {
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled)
};
}
}
//#endregion
//#region layoutInfo
/**
@@ -2362,16 +2320,37 @@ export function filterValidationDecorations(options: IComputedEditorOptions): bo
//#region rulers
class EditorRulers extends SimpleEditorOption<EditorOption.rulers, number[]> {
export interface IRulerOption {
readonly column: number;
readonly color: string | null;
}
class EditorRulers extends BaseEditorOption<EditorOption.rulers, IRulerOption[]> {
constructor() {
const defaults: number[] = [];
const defaults: IRulerOption[] = [];
const columnSchema: IJSONSchema = { type: 'number', description: nls.localize('rulers.size', "Number of monospace characters at which this editor ruler will render.") };
super(
EditorOption.rulers, 'rulers', defaults,
{
type: 'array',
items: {
type: 'number'
anyOf: [
columnSchema,
{
type: [
'object'
],
properties: {
column: columnSchema,
color: {
type: 'string',
description: nls.localize('rulers.color', "Color of this editor ruler."),
format: 'color-hex'
}
}
}
]
},
default: defaults,
description: nls.localize('rulers', "Render vertical rulers after a certain number of monospace characters. Use multiple values for multiple rulers. No rulers are drawn if array is empty.")
@@ -2379,13 +2358,24 @@ class EditorRulers extends SimpleEditorOption<EditorOption.rulers, number[]> {
);
}
public validate(input: any): number[] {
public validate(input: any): IRulerOption[] {
if (Array.isArray(input)) {
let rulers: number[] = [];
for (let value of input) {
rulers.push(EditorIntOption.clampedInt(value, 0, 0, 10000));
let rulers: IRulerOption[] = [];
for (let _element of input) {
if (typeof _element === 'number') {
rulers.push({
column: EditorIntOption.clampedInt(_element, 0, 0, 10000),
color: null
});
} else if (typeof _element === 'object') {
const element = _element as IRulerOption;
rulers.push({
column: EditorIntOption.clampedInt(element.column, 0, 0, 10000),
color: element.color
});
}
}
rulers.sort((a, b) => a - b);
rulers.sort((a, b) => a.column - b.column);
return rulers;
}
return this.defaultValue;
@@ -2674,6 +2664,10 @@ export interface ISuggestOptions {
* Show snippet-suggestions.
*/
showSnippets?: boolean;
/**
* Controls the visibility of the status bar at the bottom of the suggest widget.
*/
hideStatusBar?: boolean;
}
export type InternalSuggestOptions = Readonly<Required<ISuggestOptions>>;
@@ -2683,7 +2677,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
constructor() {
const defaults: InternalSuggestOptions = {
insertMode: 'insert',
insertHighlight: false,
insertHighlight: true,
filterGraceful: true,
snippetsPreventQuickSuggestions: true,
localityBonus: false,
@@ -2715,6 +2709,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
showFolders: true,
showTypeParameters: true,
showSnippets: true,
hideStatusBar: true
};
super(
EditorOption.suggest, 'suggest', defaults,
@@ -2752,7 +2747,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
'editor.suggest.snippetsPreventQuickSuggestions': {
type: 'boolean',
default: defaults.snippetsPreventQuickSuggestions,
description: nls.localize('suggest.snippetsPreventQuickSuggestions', "Control whether an active snippet prevents quick suggestions.")
description: nls.localize('suggest.snippetsPreventQuickSuggestions', "Controls whether an active snippet prevents quick suggestions.")
},
'editor.suggest.showIcons': {
type: 'boolean',
@@ -2899,6 +2894,11 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
type: 'boolean',
default: true,
markdownDescription: nls.localize('editor.suggest.showSnippets', "When enabled IntelliSense shows `snippet`-suggestions.")
},
'editor.suggest.hideStatusBar': {
type: 'boolean',
default: true,
markdownDescription: nls.localize('editor.suggest.hideStatusBar', "Controls the visibility of the status bar at the bottom of the suggest widget.")
}
}
);
@@ -2943,6 +2943,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
showFolders: EditorBooleanOption.boolean(input.showFolders, this.defaultValue.showFolders),
showTypeParameters: EditorBooleanOption.boolean(input.showTypeParameters, this.defaultValue.showTypeParameters),
showSnippets: EditorBooleanOption.boolean(input.showSnippets, this.defaultValue.showSnippets),
hideStatusBar: EditorBooleanOption.boolean(input.hideStatusBar, this.defaultValue.hideStatusBar),
};
}
}
@@ -3189,6 +3190,7 @@ export const enum EditorOption {
overviewRulerLanes,
parameterHints,
peekWidgetDefaultFocus,
definitionLinkOpensInPeek,
quickSuggestions,
quickSuggestionsDelay,
readOnly,
@@ -3204,10 +3206,10 @@ export const enum EditorOption {
scrollbar,
scrollBeyondLastColumn,
scrollBeyondLastLine,
scrollPredominantAxis,
selectionClipboard,
selectionHighlight,
selectOnLineNumbers,
semanticHighlighting,
showFoldingControls,
showUnused,
snippetSuggestions,
@@ -3227,7 +3229,7 @@ export const enum EditorOption {
wordWrapColumn,
wordWrapMinified,
wrappingIndent,
wrappingAlgorithm,
wrappingStrategy,
// Leave these at the end (because they have dependencies!)
editorClassName,
@@ -3437,7 +3439,13 @@ export const EditorOptions = {
EditorOption.foldingStrategy, 'foldingStrategy',
'auto' as 'auto' | 'indentation',
['auto', 'indentation'] as const,
{ markdownDescription: nls.localize('foldingStrategy', "Controls the strategy for computing folding ranges. `auto` uses a language specific folding strategy, if available. `indentation` uses the indentation based folding strategy.") }
{
enumDescriptions: [
nls.localize('foldingStrategy.auto', "Use a language-specific folding strategy if available, else the indentation-based one."),
nls.localize('foldingStrategy.indentation', "Use the indentation-based folding strategy."),
],
description: nls.localize('foldingStrategy', "Controls the strategy for computing folding ranges.")
}
)),
foldingHighlight: register(new EditorBooleanOption(
EditorOption.foldingHighlight, 'foldingHighlight', true,
@@ -3574,12 +3582,16 @@ export const EditorOptions = {
['tree', 'editor'] as const,
{
enumDescriptions: [
nls.localize('peekWidgetDefaultFocus.tree', "Focus the tree when openeing peek"),
nls.localize('peekWidgetDefaultFocus.tree', "Focus the tree when opening peek"),
nls.localize('peekWidgetDefaultFocus.editor', "Focus the editor when opening peek")
],
description: nls.localize('peekWidgetDefaultFocus', "Controls whether to focus the inline editor or the tree in the peek widget.")
}
)),
definitionLinkOpensInPeek: register(new EditorBooleanOption(
EditorOption.definitionLinkOpensInPeek, 'definitionLinkOpensInPeek', false,
{ description: nls.localize('definitionLinkOpensInPeek', "Controls whether the definition link opens element in the peek widget.") }
)),
quickSuggestions: register(new EditorQuickSuggestions()),
quickSuggestionsDelay: register(new EditorIntOption(
EditorOption.quickSuggestionsDelay, 'quickSuggestionsDelay',
@@ -3653,6 +3665,10 @@ export const EditorOptions = {
EditorOption.scrollBeyondLastLine, 'scrollBeyondLastLine', true,
{ description: nls.localize('scrollBeyondLastLine', "Controls whether the editor will scroll beyond the last line.") }
)),
scrollPredominantAxis: register(new EditorBooleanOption(
EditorOption.scrollPredominantAxis, 'scrollPredominantAxis', true,
{ description: nls.localize('scrollPredominantAxis', "Scroll only along the predominant axis when scrolling both vertically and horizontally at the same time. Prevents horizontal drift when scrolling vertically on a trackpad.") }
)),
selectionClipboard: register(new EditorBooleanOption(
EditorOption.selectionClipboard, 'selectionClipboard', true,
{
@@ -3667,12 +3683,17 @@ export const EditorOptions = {
selectOnLineNumbers: register(new EditorBooleanOption(
EditorOption.selectOnLineNumbers, 'selectOnLineNumbers', true,
)),
semanticHighlighting: register(new EditorSemanticHighlighting()),
showFoldingControls: register(new EditorStringEnumOption(
EditorOption.showFoldingControls, 'showFoldingControls',
'mouseover' as 'always' | 'mouseover',
['always', 'mouseover'] as const,
{ description: nls.localize('showFoldingControls', "Controls whether the fold controls on the gutter are automatically hidden.") }
{
enumDescriptions: [
nls.localize('showFoldingControls.always', "Always show the folding controls."),
nls.localize('showFoldingControls.mouseover', "Only show the folding controls when the mouse is over the gutter."),
],
description: nls.localize('showFoldingControls', "Controls when the folding controls on the gutter are shown.")
}
)),
showUnused: register(new EditorBooleanOption(
EditorOption.showUnused, 'showUnused', true,
@@ -3819,16 +3840,16 @@ export const EditorOptions = {
description: nls.localize('wrappingIndent', "Controls the indentation of wrapped lines."),
}
)),
wrappingAlgorithm: register(new EditorStringEnumOption(
EditorOption.wrappingAlgorithm, 'wrappingAlgorithm',
'monospace' as 'monospace' | 'dom',
['monospace', 'dom'] as const,
wrappingStrategy: register(new EditorStringEnumOption(
EditorOption.wrappingStrategy, 'wrappingStrategy',
'simple' as 'simple' | 'advanced',
['simple', 'advanced'] as const,
{
enumDescriptions: [
nls.localize('wrappingAlgorithm.monospace', "Assumes that all characters are of the same width. This is a fast algorithm."),
nls.localize('wrappingAlgorithm.dom', "Delegates wrapping points computation to the DOM. This is a slow algorithm, that might cause freezes for large files.")
nls.localize('wrappingStrategy.simple', "Assumes that all characters are of the same width. This is a fast algorithm that works correctly for monospace fonts and certain scripts (like Latin characters) where glyphs are of equal width."),
nls.localize('wrappingStrategy.advanced', "Delegates wrapping points computation to the browser. This is a slow algorithm, that might cause freezes for large files, but it works correctly in all cases.")
],
description: nls.localize('wrappingAlgorithm', "Controls the algorithm that computes wrapping points.")
description: nls.localize('wrappingStrategy', "Controls the algorithm that computes wrapping points.")
}
)),
@@ -7,6 +7,7 @@ import { CursorColumns, CursorConfiguration, ICursorSimpleModel, SingleCursorSta
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import * as strings from 'vs/base/common/strings';
import { Constants } from 'vs/base/common/uint';
export class CursorPosition {
_cursorPositionBrand: void;
@@ -214,7 +215,7 @@ export class MoveOperations {
public static moveToEndOfLine(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState {
let lineNumber = cursor.position.lineNumber;
let maxColumn = model.getLineMaxColumn(lineNumber);
return cursor.move(inSelectionMode, lineNumber, maxColumn, 0);
return cursor.move(inSelectionMode, lineNumber, maxColumn, Constants.MAX_SAFE_SMALL_INTEGER - maxColumn);
}
public static moveToBeginningOfBuffer(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState {
+9
View File
@@ -36,6 +36,15 @@ export class RGBA8 {
this.a = RGBA8._clamp(a);
}
public equals(other: RGBA8): boolean {
return (
this.r === other.r
&& this.g === other.g
&& this.b === other.b
&& this.a === other.a
);
}
private static _clamp(c: number): number {
if (c < 0) {
return 0;
+24
View File
@@ -355,6 +355,12 @@ export interface IEditor {
*/
revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType?: ScrollType): void;
/**
* Scroll vertically as necessary and reveal a line close to the top of the viewport,
* optimized for viewing a code definition.
*/
revealLineNearTop(lineNumber: number, scrollType?: ScrollType): void;
/**
* Scroll vertically or horizontally as necessary and reveal a position.
*/
@@ -370,6 +376,12 @@ export interface IEditor {
*/
revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType?: ScrollType): void;
/**
* Scroll vertically or horizontally as necessary and reveal a position close to the top of the viewport,
* optimized for viewing a code definition.
*/
revealPositionNearTop(position: IPosition, scrollType?: ScrollType): void;
/**
* Returns the primary selection of the editor.
*/
@@ -422,6 +434,12 @@ export interface IEditor {
*/
revealLinesInCenterIfOutsideViewport(lineNumber: number, endLineNumber: number, scrollType?: ScrollType): void;
/**
* Scroll vertically as necessary and reveal lines close to the top of the viewport,
* optimized for viewing a code definition.
*/
revealLinesNearTop(lineNumber: number, endLineNumber: number, scrollType?: ScrollType): void;
/**
* Scroll vertically or horizontally as necessary and reveal a range.
*/
@@ -442,6 +460,12 @@ export interface IEditor {
*/
revealRangeInCenterIfOutsideViewport(range: IRange, scrollType?: ScrollType): void;
/**
* Scroll vertically or horizontally as necessary and reveal a range close to the top of the viewport,
* optimized for viewing a code definition.
*/
revealRangeNearTop(range: IRange, scrollType?: ScrollType): void;
/**
* Directly trigger a handler or an editor action.
* @param source The source of the call.
@@ -6,6 +6,8 @@
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
export namespace EditorContextKeys {
export const editorSimpleInput = new RawContextKey<boolean>('editorSimpleInput', false);
/**
* A context key that is set when the editor's text has focus (cursor is blinking).
*/
+1
View File
@@ -290,6 +290,7 @@ export const enum EndOfLineSequence {
/**
* An identifier for a single edit operation.
* @internal
*/
export interface ISingleEditOperationIdentifier {
/**
@@ -670,7 +670,7 @@ export class PieceTreeBase {
if (searcher._wordSeparators) {
searchText = buffer.buffer.substring(start, end);
offsetInBuffer = (offset: number) => offset + start;
searcher.reset(-1);
searcher.reset(0);
} else {
searchText = buffer.buffer;
offsetInBuffer = (offset: number) => offset;
+49 -24
View File
@@ -2640,14 +2640,16 @@ export class TextModel extends Disposable implements model.ITextModel {
let goDown = true;
let indent = 0;
let initialIndent = 0;
for (let distance = 0; goUp || goDown; distance++) {
const upLineNumber = lineNumber - distance;
const downLineNumber = lineNumber + distance;
if (distance !== 0 && (upLineNumber < 1 || upLineNumber < minLineNumber)) {
if (distance > 1 && (upLineNumber < 1 || upLineNumber < minLineNumber)) {
goUp = false;
}
if (distance !== 0 && (downLineNumber > lineCount || downLineNumber > maxLineNumber)) {
if (distance > 1 && (downLineNumber > lineCount || downLineNumber > maxLineNumber)) {
goDown = false;
}
if (distance > 50000) {
@@ -2656,10 +2658,9 @@ export class TextModel extends Disposable implements model.ITextModel {
goDown = false;
}
let upLineIndentLevel: number = -1;
if (goUp) {
// compute indent level going up
let upLineIndentLevel: number;
const currentIndent = this._computeIndentLevel(upLineNumber - 1);
if (currentIndent >= 0) {
// This line has content (besides whitespace)
@@ -2671,30 +2672,11 @@ export class TextModel extends Disposable implements model.ITextModel {
up_resolveIndents(upLineNumber);
upLineIndentLevel = this._getIndentLevelForWhitespaceLine(offSide, up_aboveContentLineIndent, up_belowContentLineIndent);
}
if (distance === 0) {
// This is the initial line number
startLineNumber = upLineNumber;
endLineNumber = downLineNumber;
indent = upLineIndentLevel;
if (indent === 0) {
// No need to continue
return { startLineNumber, endLineNumber, indent };
}
continue;
}
if (upLineIndentLevel >= indent) {
startLineNumber = upLineNumber;
} else {
goUp = false;
}
}
let downLineIndentLevel = -1;
if (goDown) {
// compute indent level going down
let downLineIndentLevel: number;
const currentIndent = this._computeIndentLevel(downLineNumber - 1);
if (currentIndent >= 0) {
// This line has content (besides whitespace)
@@ -2706,7 +2688,50 @@ export class TextModel extends Disposable implements model.ITextModel {
down_resolveIndents(downLineNumber);
downLineIndentLevel = this._getIndentLevelForWhitespaceLine(offSide, down_aboveContentLineIndent, down_belowContentLineIndent);
}
}
if (distance === 0) {
initialIndent = upLineIndentLevel;
continue;
}
if (distance === 1) {
if (downLineNumber <= lineCount && downLineIndentLevel >= 0 && initialIndent + 1 === downLineIndentLevel) {
// This is the beginning of a scope, we have special handling here, since we want the
// child scope indent to be active, not the parent scope
goUp = false;
startLineNumber = downLineNumber;
endLineNumber = downLineNumber;
indent = downLineIndentLevel;
continue;
}
if (upLineNumber >= 1 && upLineIndentLevel >= 0 && upLineIndentLevel - 1 === initialIndent) {
// This is the end of a scope, just like above
goDown = false;
startLineNumber = upLineNumber;
endLineNumber = upLineNumber;
indent = upLineIndentLevel;
continue;
}
startLineNumber = lineNumber;
endLineNumber = lineNumber;
indent = initialIndent;
if (indent === 0) {
// No need to continue
return { startLineNumber, endLineNumber, indent };
}
}
if (goUp) {
if (upLineIndentLevel >= indent) {
startLineNumber = upLineNumber;
} else {
goUp = false;
}
}
if (goDown) {
if (downLineIndentLevel >= indent) {
endLineNumber = downLineNumber;
} else {
@@ -80,6 +80,7 @@ export interface IModelDecorationsChangedEvent {
/**
* An event describing that some ranges of lines have been tokenized (their tokens have changed).
* @internal
*/
export interface IModelTokensChangedEvent {
readonly tokenizationSupportChanged: boolean;
+23 -11
View File
@@ -786,6 +786,15 @@ export class TokensStore2 {
const bEndCharacter = bTokens.getEndCharacter(bIndex);
const bMetadata = bTokens.getMetadata(bIndex);
const bMask = (
((bMetadata & MetadataConsts.SEMANTIC_USE_ITALIC) ? MetadataConsts.ITALIC_MASK : 0)
| ((bMetadata & MetadataConsts.SEMANTIC_USE_BOLD) ? MetadataConsts.BOLD_MASK : 0)
| ((bMetadata & MetadataConsts.SEMANTIC_USE_UNDERLINE) ? MetadataConsts.UNDERLINE_MASK : 0)
| ((bMetadata & MetadataConsts.SEMANTIC_USE_FOREGROUND) ? MetadataConsts.FOREGROUND_MASK : 0)
| ((bMetadata & MetadataConsts.SEMANTIC_USE_BACKGROUND) ? MetadataConsts.BACKGROUND_MASK : 0)
) >>> 0;
const aMask = (~bMask) >>> 0;
// push any token from `a` that is before `b`
while (aIndex < aLen && aTokens.getEndOffset(aIndex) <= bStartCharacter) {
result[resultLen++] = aTokens.getEndOffset(aIndex);
@@ -800,21 +809,24 @@ export class TokensStore2 {
}
// skip any tokens from `a` that are contained inside `b`
while (aIndex < aLen && aTokens.getEndOffset(aIndex) <= bEndCharacter) {
while (aIndex < aLen && aTokens.getEndOffset(aIndex) < bEndCharacter) {
result[resultLen++] = aTokens.getEndOffset(aIndex);
result[resultLen++] = (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask);
aIndex++;
}
const aMetadata = aTokens.getMetadata(Math.min(Math.max(0, aIndex - 1), aLen - 1));
const languageId = TokenMetadata.getLanguageId(aMetadata);
const tokenType = TokenMetadata.getTokenType(aMetadata);
if (aIndex < aLen && aTokens.getEndOffset(aIndex) === bEndCharacter) {
// `a` ends exactly at the same spot as `b`!
result[resultLen++] = aTokens.getEndOffset(aIndex);
result[resultLen++] = (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask);
aIndex++;
} else {
const aMergeIndex = Math.min(Math.max(0, aIndex - 1), aLen - 1);
// push the token from `b`
result[resultLen++] = bEndCharacter;
result[resultLen++] = (
(bMetadata & MetadataConsts.LANG_TTYPE_CMPL)
| ((languageId << MetadataConsts.LANGUAGEID_OFFSET) >>> 0)
| ((tokenType << MetadataConsts.TOKEN_TYPE_OFFSET) >>> 0)
);
// push the token from `b`
result[resultLen++] = bEndCharacter;
result[resultLen++] = (aTokens.getMetadata(aMergeIndex) & aMask) | (bMetadata & bMask);
}
}
// push the remaining tokens from `a`
+12 -4
View File
@@ -125,7 +125,15 @@ export const enum MetadataConsts {
FOREGROUND_MASK = 0b00000000011111111100000000000000,
BACKGROUND_MASK = 0b11111111100000000000000000000000,
LANG_TTYPE_CMPL = 0b11111111111111111111100000000000,
ITALIC_MASK = 0b00000000000000000000100000000000,
BOLD_MASK = 0b00000000000000000001000000000000,
UNDERLINE_MASK = 0b00000000000000000010000000000000,
SEMANTIC_USE_ITALIC = 0b00000000000000000000000000000001,
SEMANTIC_USE_BOLD = 0b00000000000000000000000000000010,
SEMANTIC_USE_UNDERLINE = 0b00000000000000000000000000000100,
SEMANTIC_USE_FOREGROUND = 0b00000000000000000000000000001000,
SEMANTIC_USE_BACKGROUND = 0b00000000000000000000000000010000,
LANGUAGEID_OFFSET = 0,
TOKEN_TYPE_OFFSET = 8,
@@ -1330,10 +1338,10 @@ export interface RenameProvider {
/**
* @internal
*/
export interface Session {
export interface AuthenticationSession {
id: string;
accessToken: string;
displayName: string;
accessToken(): Promise<string>;
accountName: string;
}
export interface Command {
@@ -223,6 +223,7 @@ export class LinkComputer {
let state = State.Start;
let hasOpenParens = false;
let hasOpenSquareBracket = false;
let inSquareBrackets = false;
let hasOpenCurlyBracket = false;
while (j < len) {
@@ -241,10 +242,12 @@ export class LinkComputer {
chClass = (hasOpenParens ? CharacterClass.None : CharacterClass.ForceTermination);
break;
case CharCode.OpenSquareBracket:
inSquareBrackets = true;
hasOpenSquareBracket = true;
chClass = CharacterClass.None;
break;
case CharCode.CloseSquareBracket:
inSquareBrackets = false;
chClass = (hasOpenSquareBracket ? CharacterClass.None : CharacterClass.ForceTermination);
break;
case CharCode.OpenCurlyBrace:
@@ -272,6 +275,10 @@ export class LinkComputer {
// `|` terminates a link if the link began with `|`
chClass = (linkBeginChCode === CharCode.Pipe) ? CharacterClass.ForceTermination : CharacterClass.None;
break;
case CharCode.Space:
// ` ` allow space in between [ and ]
chClass = (inSquareBrackets ? CharacterClass.None : CharacterClass.ForceTermination);
break;
default:
chClass = classifier.get(chCode);
}
@@ -45,11 +45,13 @@ function canSyncModel(modelService: IModelService, resource: URI): boolean {
}
export class EditorWorkerServiceImpl extends Disposable implements IEditorWorkerService {
public _serviceBrand: undefined;
_serviceBrand: undefined;
private readonly _modelService: IModelService;
private readonly _workerManager: WorkerManager;
private readonly _logService: ILogService;
constructor(
@IModelService modelService: IModelService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@@ -60,7 +62,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
this._workerManager = this._register(new WorkerManager(this._modelService));
this._logService = logService;
// todo@joh make sure this happens only once
// register default link-provider and default completions-provider
this._register(modes.LinkProviderRegistry.register('*', {
provideLinks: (model, token) => {
if (!canSyncModel(this._modelService, model.uri)) {
@@ -8,13 +8,13 @@ import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecyc
import * as platform from 'vs/base/common/platform';
import * as errors from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { EDITOR_MODEL_DEFAULTS, IEditorSemanticHighlightingOptions } from 'vs/editor/common/config/editorOptions';
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model';
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { LanguageIdentifier, DocumentSemanticTokensProviderRegistry, DocumentSemanticTokensProvider, SemanticTokensLegend, SemanticTokens, SemanticTokensEdits, TokenMetadata } from 'vs/editor/common/modes';
import { LanguageIdentifier, DocumentSemanticTokensProviderRegistry, DocumentSemanticTokensProvider, SemanticTokensLegend, SemanticTokens, SemanticTokensEdits, TokenMetadata, FontStyle, MetadataConsts } from 'vs/editor/common/modes';
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
@@ -26,6 +26,10 @@ import { SparseEncodedTokens, MultilineTokens2 } from 'vs/editor/common/model/to
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
export interface IEditorSemanticHighlightingOptions {
enabled?: boolean;
}
function MODEL_ID(resource: URI): string {
return resource.toString();
}
@@ -629,7 +633,7 @@ class SemanticColoringProviderStyling {
public getMetadata(tokenTypeIndex: number, tokenModifierSet: number): number {
const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet);
let metadata: number | undefined;
let metadata: number;
if (entry) {
metadata = entry.metadata;
} else {
@@ -643,9 +647,31 @@ class SemanticColoringProviderStyling {
modifierSet = modifierSet >> 1;
}
metadata = this._themeService.getTheme().getTokenStyleMetadata(tokenType, tokenModifiers);
if (typeof metadata === 'undefined') {
const tokenStyle = this._themeService.getTheme().getTokenStyleMetadata(tokenType, tokenModifiers);
if (typeof tokenStyle === 'undefined') {
metadata = Constants.NO_STYLING;
} else {
metadata = 0;
if (typeof tokenStyle.italic !== 'undefined') {
const italicBit = (tokenStyle.italic ? FontStyle.Italic : 0) << MetadataConsts.FONT_STYLE_OFFSET;
metadata |= italicBit | MetadataConsts.SEMANTIC_USE_ITALIC;
}
if (typeof tokenStyle.bold !== 'undefined') {
const boldBit = (tokenStyle.bold ? FontStyle.Bold : 0) << MetadataConsts.FONT_STYLE_OFFSET;
metadata |= boldBit | MetadataConsts.SEMANTIC_USE_BOLD;
}
if (typeof tokenStyle.underline !== 'undefined') {
const underlineBit = (tokenStyle.underline ? FontStyle.Underline : 0) << MetadataConsts.FONT_STYLE_OFFSET;
metadata |= underlineBit | MetadataConsts.SEMANTIC_USE_UNDERLINE;
}
if (tokenStyle.foreground) {
const foregroundBits = (tokenStyle.foreground) << MetadataConsts.FOREGROUND_OFFSET;
metadata |= foregroundBits | MetadataConsts.SEMANTIC_USE_FOREGROUND;
}
if (metadata === 0) {
// Nothing!
metadata = Constants.NO_STYLING;
}
}
this._hashTable.add(tokenTypeIndex, tokenModifierSet, metadata);
}
@@ -763,10 +789,21 @@ class ModelSemanticColoring extends Disposable {
contentChangeListener.dispose();
this._setSemanticTokens(provider, res || null, styling, pendingChanges);
}, (err) => {
errors.onUnexpectedError(err);
if (!err || typeof err.message !== 'string' || err.message.indexOf('busy') === -1) {
errors.onUnexpectedError(err);
}
// Semantic tokens eats up all errors and considers errors to mean that the result is temporarily not available
// The API does not have a special error kind to express this...
this._currentRequestCancellationTokenSource = null;
contentChangeListener.dispose();
this._setSemanticTokens(provider, null, styling, pendingChanges);
if (pendingChanges.length > 0) {
// More changes occurred while the request was running
if (!this._fetchSemanticTokens.isScheduled()) {
this._fetchSemanticTokens.schedule();
}
}
});
}

Some files were not shown because too many files have changed in this diff Show More