Archived
Merge from vscode 718331d6f3ebd1b571530ab499edb266ddd493d5
This commit is contained in:
@@ -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';
|
||||
|
||||
+11
-7
@@ -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';
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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)];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 || '/';
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
+3
-18
@@ -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);
|
||||
}
|
||||
}
|
||||
+12
-41
@@ -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);
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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).
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user