Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 (#8962)

* Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229

* skip failing tests

* update mac build image
This commit is contained in:
Anthony Dresser
2020-01-27 15:28:17 -08:00
committed by Karl Burtram
parent 0eaee18dc4
commit fefe1454de
481 changed files with 12764 additions and 7836 deletions
@@ -95,7 +95,7 @@ class MainThreadNotebookEditor extends Disposable {
}
public save(): Thenable<boolean> {
return this.textFileService.save(this.uri);
return this.textFileService.save(this.uri).then(uri => !!uri);
}
public matches(input: NotebookInput): boolean {
@@ -351,7 +351,7 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements
let uriString = URI.revive(uri).toString();
let editor = this._notebookEditors.get(uriString);
if (editor) {
return editor.save();
return editor.save().then(uri => !!uri);
} else {
return Promise.resolve(false);
}
@@ -34,6 +34,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IViewDescriptorService } from 'vs/workbench/common/views';
export class CategoryView extends ViewPane {
@@ -45,9 +46,10 @@ export class CategoryView extends ViewPane {
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService
@IInstantiationService instantiationService: IInstantiationService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService
) {
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService);
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
}
// we want a fixed size, so when we render to will measure our content and set that to be our
@@ -6,7 +6,7 @@
import * as azdata from 'azdata';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { EditorInput, EditorModel } from 'vs/workbench/common/editor';
import { EditorInput, EditorModel, IEditorInput } from 'vs/workbench/common/editor';
import * as DOM from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -139,8 +139,8 @@ export class ModelViewInput extends EditorInput {
/**
* Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
*/
save(): Promise<boolean> {
return this._model.save();
save(): Promise<IEditorInput | undefined> {
return this._model.save().then(saved => saved ? this : undefined);
}
public dispose(): void {
@@ -196,7 +196,7 @@ export class QueryTextEditor extends BaseTextEditor {
this.refreshEditorConfiguration();
}
private refreshEditorConfiguration(configuration = this.configurationService.getValue<IEditorConfiguration>(this.getResource())): void {
private refreshEditorConfiguration(configuration = this.textResourceConfigurationService.getValue<IEditorConfiguration>(this.input.getResource())): void {
if (!this.getControl()) {
return;
}
@@ -12,7 +12,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
import { ContextAwareMenuEntryActionViewItem, createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions } from 'vs/workbench/common/views';
import { TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions, IViewDescriptorService } from 'vs/workbench/common/views';
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -27,7 +27,7 @@ import { URI } from 'vs/base/common/uri';
import { dirname, basename } from 'vs/base/common/resources';
import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { FileKind } from 'vs/platform/files/common/files';
import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService';
import { WorkbenchAsyncDataTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService';
import { localize } from 'vs/nls';
import { timeout } from 'vs/base/common/async';
import { editorFindMatchHighlight, editorFindMatchHighlightBorder, textLinkForeground, textCodeBlockBackground, focusBorder } from 'vs/platform/theme/common/colorRegistry';
@@ -62,9 +62,10 @@ export class CustomTreeViewPanel extends ViewPane {
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService
@IInstantiationService instantiationService: IInstantiationService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService
) {
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService);
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
const { treeView } = (<ITreeViewDescriptor>Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).getView(options.id));
this.treeView = treeView as ITreeView;
this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this));
@@ -443,7 +444,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
}));
this.tree.setInput(this.root).then(() => this.updateContentAreas());
const customTreeNavigator = new TreeResourceNavigator2(this.tree);
const customTreeNavigator = new TreeResourceNavigator(this.tree);
this._register(customTreeNavigator);
this._register(customTreeNavigator.onDidOpenResource(e => {
if (!e.browserEvent) {
@@ -36,6 +36,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IViewDescriptorService } from 'vs/workbench/common/views';
class AccountPanel extends ViewPane {
public index: number;
@@ -48,9 +49,10 @@ class AccountPanel extends ViewPane {
@IConfigurationService configurationService: IConfigurationService,
@IThemeService private themeService: IThemeService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService
@IInstantiationService instantiationService: IInstantiationService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService
) {
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService);
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
}
protected renderBody(container: HTMLElement): void {
@@ -126,6 +128,7 @@ export class AccountDialog extends Modal {
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IClipboardService clipboardService: IClipboardService,
@ILogService logService: ILogService,
@IViewDescriptorService private viewDescriptorService: IViewDescriptorService,
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
) {
super(
@@ -296,7 +299,8 @@ export class AccountDialog extends Modal {
this._configurationService,
this._themeService,
this.contextKeyService,
this._instantiationService
this._instantiationService,
this.viewDescriptorService
);
attachPanelStyler(providerView, this._themeService);
@@ -86,7 +86,7 @@ function createInstantiationService(addAccountFailureEmitter?: Emitter<string>):
.returns(() => undefined);
// Create a mock account dialog
let accountDialog = new AccountDialog(undefined!, undefined!, instantiationService.object, undefined!, undefined!, undefined!, undefined!, new MockContextKeyService(), undefined!, undefined!, undefined!);
let accountDialog = new AccountDialog(undefined!, undefined!, instantiationService.object, undefined!, undefined!, undefined!, undefined!, new MockContextKeyService(), undefined!, undefined!, undefined!, undefined!);
let mockAccountDialog = TypeMoq.Mock.ofInstance(accountDialog);
mockAccountDialog.setup(x => x.onAddAccountErrorEvent)
.returns(() => { return addAccountFailureEmitter ? addAccountFailureEmitter.event : mockEvent.event; });
@@ -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, undefined);
const untitledEditorInput = new UntitledTextEditorInput(uri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined);
const queryInput = new UntitledQueryEditorInput(undefined, untitledEditorInput, undefined, connectionManagementService.object, querymodelService.object, configurationService.object, undefined);
queryInput.state.connected = true;
const editorService: TypeMoq.Mock<IEditorService> = TypeMoq.Mock.ofType<IEditorService>(TestEditorService, TypeMoq.MockBehavior.Strict);
@@ -20,6 +20,7 @@ import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/br
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IViewDescriptorService } from 'vs/workbench/common/views';
export class ConnectionViewletPanel extends ViewPane {
@@ -38,9 +39,10 @@ export class ConnectionViewletPanel extends ViewPane {
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@IObjectExplorerService private readonly objectExplorerService: IObjectExplorerService,
@IContextKeyService contextKeyService: IContextKeyService
@IContextKeyService contextKeyService: IContextKeyService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
) {
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService);
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
this._addServerAction = this.instantiationService.createInstance(AddServerAction,
AddServerAction.ID,
AddServerAction.LABEL);
@@ -17,7 +17,7 @@ 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 } from 'vs/workbench/common/views';
import { Extensions as ViewContainerExtensions, IViewDescriptor, IViewsRegistry, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -103,9 +103,10 @@ export class DataExplorerViewPaneContainer extends ViewPaneContainer {
@IExtensionService extensionService: IExtensionService,
@IConfigurationService configurationService: IConfigurationService,
@IMenuService private menuService: IMenuService,
@IContextKeyService private contextKeyService: IContextKeyService
@IContextKeyService private contextKeyService: IContextKeyService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService
) {
super(VIEWLET_ID, `${VIEWLET_ID}.state`, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService);
super(VIEWLET_ID, `${VIEWLET_ID}.state`, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService);
}
create(parent: HTMLElement): void {
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { EditorInput, EditorModel, EncodingMode } from 'vs/workbench/common/editor';
import { EditorInput, EditorModel, EncodingMode, IEditorInput } from 'vs/workbench/common/editor';
import { IConnectionManagementService, IConnectableInput, INewConnectionParams } from 'sql/platform/connection/common/connectionManagement';
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { Event, Emitter } from 'vs/base/common/event';
@@ -107,7 +107,7 @@ export class EditDataInput extends EditorInput implements IConnectableInput {
public get objectType(): string { return this._objectType; }
public showResultsEditor(): void { this._showResultsEditor.fire(undefined); }
public isDirty(): boolean { return false; }
public save(): Promise<boolean> { return Promise.resolve(false); }
public save(): Promise<IEditorInput | undefined> { return Promise.resolve(undefined); }
public getTypeId(): string { return EditDataInput.ID; }
public setBootstrappedTrue(): void { this._hasBootstrapped = true; }
public getResource(): URI { return this._uri; }
@@ -220,7 +220,6 @@ export class EditDataInput extends EditorInput implements IConnectableInput {
public get onDidModelChangeEncoding(): Event<void> { return this._sql.onDidModelChangeEncoding; }
public resolve(refresh?: boolean): Promise<EditorModel> { return this._sql.resolve(); }
public getEncoding(): string { return this._sql.getEncoding(); }
public suggestFileName(): string { return this._sql.suggestFileName(); }
public getName(): string { return this._sql.getName(); }
public get hasAssociatedFilePath(): boolean { return this._sql.hasAssociatedFilePath; }
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { EditorInput, EditorModel } from 'vs/workbench/common/editor';
import { EditorInput, EditorModel, IRevertOptions, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor';
import { Emitter, Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
@@ -230,8 +230,8 @@ export abstract class NotebookInput extends EditorInput {
return this._textInput;
}
public revert(): Promise<boolean> {
return this._textInput.revert();
public revert(group: GroupIdentifier, options?: IRevertOptions): Promise<boolean> {
return this._textInput.revert(group, options);
}
public get notebookUri(): URI {
@@ -283,11 +283,11 @@ export abstract class NotebookInput extends EditorInput {
return this._standardKernels;
}
save(groupId: number, options?: ITextFileSaveOptions): Promise<boolean> {
save(groupId: number, options?: ITextFileSaveOptions): Promise<IEditorInput | undefined> {
return this.textInput.save(groupId, options);
}
saveAs(group: number, options?: ITextFileSaveOptions): Promise<boolean> {
saveAs(group: number, options?: ITextFileSaveOptions): Promise<IEditorInput | undefined> {
return this.textInput.saveAs(group, options);
}
@@ -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, undefined);
untitledTextInput = new UntitledTextEditorInput(untitledUri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, 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, undefined);
let otherTextInput = new UntitledTextEditorInput(otherTestUri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined);
let otherInput = instantiationService.createInstance(UntitledNotebookInput, 'OtherTestInput', otherTestUri, otherTextInput);
assert.strictEqual(untitledNotebookInput.matches(otherInput), false, 'Input should not match different input.');
@@ -109,6 +109,9 @@ suite('SQL Connection Tree Action tests', () => {
});
const viewsService = new class implements IViewsService {
getActiveViewWithId(id: string): IView {
throw new Error('Method not implemented.');
}
_serviceBrand: undefined;
openView(id: string, focus?: boolean): Promise<IView> {
return Promise.resolve({
@@ -7,7 +7,7 @@ import { localize } from 'vs/nls';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { EditorInput } from 'vs/workbench/common/editor';
import { EditorInput, GroupIdentifier, IRevertOptions, ISaveOptions, IEditorInput } from 'vs/workbench/common/editor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IFileService } from 'vs/platform/files/common/files';
@@ -17,7 +17,6 @@ import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { ISelectionData, ExecutionPlanOptions } from 'azdata';
import { startsWith } from 'vs/base/common/strings';
import { ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles';
const MAX_SIZE = 13;
@@ -175,7 +174,9 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab
// Description is shown beside the tab name in the combobox of open editors
public getDescription(): string { return this._description; }
public supportsSplitEditor(): boolean { return false; }
public revert(): Promise<boolean> { return this._text.revert(); }
public revert(group: GroupIdentifier, options?: IRevertOptions): Promise<boolean> {
return this._text.revert(group, options);
}
public isReadonly(): boolean {
return false;
@@ -224,11 +225,11 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab
}
}
save(groupId: number, options?: ITextFileSaveOptions): Promise<boolean> {
return this.text.save(groupId, options);
save(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
return this.text.save(group, options);
}
saveAs(group: number, options?: ITextFileSaveOptions): Promise<boolean> {
saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
return this.text.saveAs(group, options);
}
@@ -47,10 +47,6 @@ export class UntitledQueryEditorInput extends QueryEditorInput implements IEncod
return this.text.hasAssociatedFilePath;
}
public suggestFileName(): string {
return this.text.suggestFileName();
}
public setMode(mode: string): void {
this.text.setMode(mode);
}
@@ -75,12 +71,4 @@ export class UntitledQueryEditorInput extends QueryEditorInput implements IEncod
// Subclasses need to explicitly opt-in to being untitled.
return true;
}
hasBackup(): boolean {
if (this.text) {
return this.text.hasBackup();
}
return false;
}
}
@@ -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, undefined);
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, 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, undefined);
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, 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, undefined);
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, 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);
@@ -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, undefined);
let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService.object, undefined, new LabelService(undefined, undefined), undefined, 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);
@@ -43,6 +43,7 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/textRe
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import { onUnexpectedError } from 'vs/base/common/errors';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IViewDescriptorService } from 'vs/workbench/common/views';
const labelDisplay = nls.localize("insights.item", "Item");
const valueDisplay = nls.localize("insights.value", "Value");
@@ -63,9 +64,10 @@ class InsightTableView<T> extends ViewPane {
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService
@IInstantiationService instantiationService: IInstantiationService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService
) {
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService);
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
}
protected renderBody(container: HTMLElement): void {
@@ -25,6 +25,7 @@ import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api';
class TestEnvironmentService implements IWorkbenchEnvironmentService {
userDataSyncHome: URI;
keybindingsSyncPreviewResource: URI;
argvResource: URI;
userDataSyncLogResource: URI;
+34 -7
View File
@@ -287,7 +287,7 @@ export function addDisposableGenericMouseUpListner(node: EventTarget, handler: (
export function addDisposableNonBubblingMouseOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable {
return addDisposableListener(node, 'mouseout', (e: MouseEvent) => {
// Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements
let toElement: Node | null = <Node>(e.relatedTarget || e.target);
let toElement: Node | null = <Node>(e.relatedTarget);
while (toElement && toElement !== node) {
toElement = toElement.parentNode;
}
@@ -302,7 +302,7 @@ export function addDisposableNonBubblingMouseOutListener(node: Element, handler:
export function addDisposableNonBubblingPointerOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable {
return addDisposableListener(node, 'pointerout', (e: MouseEvent) => {
// Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements
let toElement: Node | null = <Node>(e.relatedTarget || e.target);
let toElement: Node | null = <Node>(e.relatedTarget);
while (toElement && toElement !== node) {
toElement = toElement.parentNode;
}
@@ -628,11 +628,17 @@ export function getTopLeftOffset(element: HTMLElement): { left: number; top: num
// Adapted from WinJS.Utilities.getPosition
// and added borders to the mix
let offsetParent = element.offsetParent, top = element.offsetTop, left = element.offsetLeft;
let offsetParent = element.offsetParent;
let top = element.offsetTop;
let left = element.offsetLeft;
while ((element = <HTMLElement>element.parentNode) !== null && element !== document.body && element !== document.documentElement) {
while (
(element = <HTMLElement>element.parentNode) !== null
&& element !== document.body
&& element !== document.documentElement
) {
top -= element.scrollTop;
let c = getComputedStyle(element);
const c = isShadowRoot(element) ? null : getComputedStyle(element);
if (c) {
left -= c.direction !== 'rtl' ? element.scrollLeft : -element.scrollLeft;
}
@@ -793,7 +799,7 @@ export function isAncestor(testChild: Node | null, testAncestor: Node | null): b
}
export function findParentWithClass(node: HTMLElement, clazz: string, stopAtClazzOrNode?: string | HTMLElement): HTMLElement | null {
while (node) {
while (node && node.nodeType === node.ELEMENT_NODE) {
if (hasClass(node, clazz)) {
return node;
}
@@ -820,6 +826,27 @@ export function hasParentWithClass(node: HTMLElement, clazz: string, stopAtClazz
return !!findParentWithClass(node, clazz, stopAtClazzOrNode);
}
export function isShadowRoot(node: Node): node is ShadowRoot {
return (
node && !!(<ShadowRoot>node).host && !!(<ShadowRoot>node).mode
);
}
export function isInShadowDOM(domNode: Node): boolean {
return !!getShadowRoot(domNode);
}
export function getShadowRoot(domNode: Node): ShadowRoot | null {
while (domNode.parentNode) {
if (domNode === document.body) {
// reached the body
return null;
}
domNode = domNode.parentNode;
}
return isShadowRoot(domNode) ? domNode : null;
}
export function createStyleSheet(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLStyleElement {
let style = document.createElement('style');
style.type = 'text/css';
@@ -1167,7 +1194,7 @@ export function hide(...elements: HTMLElement[]): void {
}
function findParentWithAttribute(node: Node | null, attribute: string): HTMLElement | null {
while (node) {
while (node && node.nodeType === node.ELEMENT_NODE) {
if (node instanceof HTMLElement && node.hasAttribute(attribute)) {
return node;
}
+10
View File
@@ -25,6 +25,7 @@ export class FastDomNode<T extends HTMLElement> {
private _display: string;
private _position: string;
private _visibility: string;
private _backgroundColor: string;
private _layerHint: boolean;
private _contain: 'none' | 'strict' | 'content' | 'size' | 'layout' | 'style' | 'paint';
@@ -47,6 +48,7 @@ export class FastDomNode<T extends HTMLElement> {
this._display = '';
this._position = '';
this._visibility = '';
this._backgroundColor = '';
this._layerHint = false;
this._contain = 'none';
}
@@ -200,6 +202,14 @@ export class FastDomNode<T extends HTMLElement> {
this.domNode.style.visibility = this._visibility;
}
public setBackgroundColor(backgroundColor: string): void {
if (this._backgroundColor === backgroundColor) {
return;
}
this._backgroundColor = backgroundColor;
this.domNode.style.backgroundColor = this._backgroundColor;
}
public setLayerHinting(layerHint: boolean): void {
if (this._layerHint === layerHint) {
return;
+12 -4
View File
@@ -75,6 +75,7 @@ export class GlobalMouseMoveMonitor<R extends { buttons: number; }> implements I
}
public startMonitoring(
initialElement: HTMLElement,
initialButtons: number,
mouseMoveEventMerger: IEventMerger<R>,
mouseMoveCallback: IMouseMoveCallback<R>,
@@ -88,11 +89,18 @@ export class GlobalMouseMoveMonitor<R extends { buttons: number; }> implements I
this._mouseMoveCallback = mouseMoveCallback;
this._onStopCallback = onStopCallback;
let windowChain = IframeUtils.getSameOriginWindowChain();
const windowChain = IframeUtils.getSameOriginWindowChain();
const mouseMove = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointermove' : 'mousemove';
const mouseUp = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointerup' : 'mouseup';
for (const element of windowChain) {
this._hooks.add(dom.addDisposableThrottledListener(element.window.document, mouseMove,
const listenTo: (Document | ShadowRoot)[] = windowChain.map(element => element.window.document);
const shadowRoot = dom.getShadowRoot(initialElement);
if (shadowRoot) {
listenTo.unshift(shadowRoot);
}
for (const element of listenTo) {
this._hooks.add(dom.addDisposableThrottledListener(element, mouseMove,
(data: R) => {
if (data.buttons !== initialButtons) {
// Buttons state has changed in the meantime
@@ -103,7 +111,7 @@ export class GlobalMouseMoveMonitor<R extends { buttons: number; }> implements I
},
(lastEvent: R | null, currentEvent) => this._mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent)
));
this._hooks.add(dom.addDisposableListener(element.window.document, mouseUp, (e: MouseEvent) => this.stopMonitoring(true)));
this._hooks.add(dom.addDisposableListener(element, mouseUp, (e: MouseEvent) => this.stopMonitoring(true)));
}
if (IframeUtils.hasDifferentOriginAncestor()) {
@@ -5,7 +5,7 @@
@font-face {
font-family: "codicon";
src: url("./codicon.ttf?ed926e87ee4e27771159d875e877f74a") format("truetype");
src: url("./codicon.ttf?be537a78617db0869caa4b4cc683a24a") format("truetype");
}
.codicon[class*='codicon-'] {
@@ -119,6 +119,7 @@
.codicon-github:before { content: "\ea84" }
.codicon-terminal:before { content: "\ea85" }
.codicon-console:before { content: "\ea85" }
.codicon-repl:before { content: "\ea85" }
.codicon-zap:before { content: "\ea86" }
.codicon-symbol-event:before { content: "\ea86" }
.codicon-error:before { content: "\ea87" }
@@ -410,4 +411,6 @@
.codicon-menu:before { content: "\eb94" }
.codicon-expand-all:before { content: "\eb95" }
.codicon-feedback:before { content: "\eb96" }
.codicon-group-by-ref-type:before { content: "\eb97" }
.codicon-ungroup-by-ref-type:before { content: "\eb98" }
.codicon-debug-alt:before { content: "\f101" }
@@ -92,8 +92,10 @@ class FastLabelNode {
export class IconLabel extends Disposable {
private domNode: FastLabelNode;
private descriptionContainer: FastLabelNode;
private nameNode: Label | LabelWithHighlights;
private descriptionContainer: FastLabelNode;
private descriptionNode: FastLabelNode | HighlightedLabel | undefined;
private descriptionNodeFactory: () => FastLabelNode | HighlightedLabel;
+1 -1
View File
@@ -844,7 +844,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
}
// sanitize feedback list
feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort();
feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort((a, b) => a - b);
feedback = feedback[0] === -1 ? [-1] : feedback;
if (equalsDragFeedback(this.currentDragFeedback, feedback)) {
@@ -61,6 +61,7 @@ export abstract class AbstractScrollbar extends Widget {
this._scrollable = opts.scrollable;
this._scrollbarState = opts.scrollbarState;
this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName));
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
this._mouseMoveMonitor = this._register(new GlobalMouseMoveMonitor<IStandardMouseMoveEventData>());
this._shouldRender = true;
this.domNode = createFastDomNode(document.createElement('div'));
@@ -216,13 +217,14 @@ export abstract class AbstractScrollbar extends Widget {
}
}
private _sliderMouseDown(e: ISimplifiedMouseEvent, onDragFinished: () => void): void {
private _sliderMouseDown(e: IMouseEvent, onDragFinished: () => void): void {
const initialMousePosition = this._sliderMousePosition(e);
const initialMouseOrthogonalPosition = this._sliderOrthogonalMousePosition(e);
const initialScrollbarState = this._scrollbarState.clone();
this.slider.toggleClassName('active', true);
this._mouseMoveMonitor.startMonitoring(
e.target,
e.buttons,
standardMouseMoveMerger,
(mouseMoveData: IStandardMouseMoveEventData) => {
@@ -13,13 +13,18 @@ import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from
export class HorizontalScrollbar extends AbstractScrollbar {
constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) {
const scrollDimensions = scrollable.getScrollDimensions();
const scrollPosition = scrollable.getCurrentScrollPosition();
super({
lazyRender: options.lazyRender,
host: host,
scrollbarState: new ScrollbarState(
(options.horizontalHasArrows ? options.arrowSize : 0),
(options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize),
(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize)
(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize),
scrollDimensions.width,
scrollDimensions.scrollWidth,
scrollPosition.scrollLeft
),
visibility: options.horizontal,
extraScrollbarClassName: 'horizontal',
@@ -93,6 +93,7 @@ export class ScrollbarArrow extends Widget {
this._mousedownScheduleRepeatTimer.cancelAndSet(scheduleRepeater, 200);
this._mouseMoveMonitor.startMonitoring(
e.target,
e.buttons,
standardMouseMoveMerger,
(mouseMoveData: IStandardMouseMoveEventData) => {
@@ -62,14 +62,14 @@ export class ScrollbarState {
private _computedSliderRatio: number;
private _computedSliderPosition: number;
constructor(arrowSize: number, scrollbarSize: number, oppositeScrollbarSize: number) {
constructor(arrowSize: number, scrollbarSize: number, oppositeScrollbarSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) {
this._scrollbarSize = Math.round(scrollbarSize);
this._oppositeScrollbarSize = Math.round(oppositeScrollbarSize);
this._arrowSize = Math.round(arrowSize);
this._visibleSize = 0;
this._scrollSize = 0;
this._scrollPosition = 0;
this._visibleSize = visibleSize;
this._scrollSize = scrollSize;
this._scrollPosition = scrollPosition;
this._computedAvailableSize = 0;
this._computedIsNeeded = false;
@@ -81,11 +81,7 @@ export class ScrollbarState {
}
public clone(): ScrollbarState {
let r = new ScrollbarState(this._arrowSize, this._scrollbarSize, this._oppositeScrollbarSize);
r.setVisibleSize(this._visibleSize);
r.setScrollSize(this._scrollSize);
r.setScrollPosition(this._scrollPosition);
return r;
return new ScrollbarState(this._arrowSize, this._scrollbarSize, this._oppositeScrollbarSize, this._visibleSize, this._scrollSize, this._scrollPosition);
}
public setVisibleSize(visibleSize: number): boolean {
@@ -13,6 +13,8 @@ import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from
export class VerticalScrollbar extends AbstractScrollbar {
constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) {
const scrollDimensions = scrollable.getScrollDimensions();
const scrollPosition = scrollable.getCurrentScrollPosition();
super({
lazyRender: options.lazyRender,
host: host,
@@ -20,7 +22,10 @@ export class VerticalScrollbar extends AbstractScrollbar {
(options.verticalHasArrows ? options.arrowSize : 0),
(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize),
// give priority to vertical scroll bar over horizontal and let it scroll all the way to the bottom
0
0,
scrollDimensions.height,
scrollDimensions.scrollHeight,
scrollPosition.scrollTop
),
visibility: options.vertical,
extraScrollbarClassName: 'vertical',
+5 -1
View File
@@ -6,7 +6,11 @@
import { URI } from 'vs/base/common/uri';
export function getPathFromAmdModule(requirefn: typeof require, relativePath: string): string {
return URI.parse(requirefn.toUrl(relativePath)).fsPath;
return getUriFromAmdModule(requirefn, relativePath).fsPath;
}
export function getUriFromAmdModule(requirefn: typeof require, relativePath: string): URI {
return URI.parse(requirefn.toUrl(relativePath));
}
/**
+8 -2
View File
@@ -481,8 +481,9 @@ export class Queue<T> extends Limiter<T> {
* A helper to organize queues per resource. The ResourceQueue makes sure to manage queues per resource
* by disposing them once the queue is empty.
*/
export class ResourceQueue {
private queues: Map<string, Queue<void>> = new Map();
export class ResourceQueue implements IDisposable {
private readonly queues = new Map<string, Queue<void>>();
queueFor(resource: URI): Queue<void> {
const key = resource.toString();
@@ -498,6 +499,11 @@ export class ResourceQueue {
return this.queues.get(key)!;
}
dispose(): void {
this.queues.forEach(queue => queue.dispose());
this.queues.clear();
}
}
export class TimeoutTimer implements IDisposable {
+1
View File
@@ -148,6 +148,7 @@ export namespace Event {
if (leading && !handle) {
emitter.fire(output);
output = undefined;
}
clearTimeout(handle);
+1 -1
View File
@@ -120,7 +120,7 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo
}
}
function withFormatting(text: string, edit: Edit, formattingOptions: FormattingOptions): Edit[] {
export function withFormatting(text: string, edit: Edit, formattingOptions: FormattingOptions): Edit[] {
// apply the edit
let newText = applyEdit(text, edit);
+2 -2
View File
@@ -228,7 +228,7 @@ function computeIndentLevel(content: string, options: FormattingOptions): number
return Math.floor(nChars / tabSize);
}
function getEOL(options: FormattingOptions, text: string): string {
export function getEOL(options: FormattingOptions, text: string): string {
for (let i = 0; i < text.length; i++) {
const ch = text.charAt(i);
if (ch === '\r') {
@@ -245,4 +245,4 @@ function getEOL(options: FormattingOptions, text: string): string {
export function isEOL(text: string, offset: number) {
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
}
}
+43
View File
@@ -72,6 +72,49 @@ function doFindFreePort(startPort: number, giveUpAfter: number, clb: (port: numb
client.connect(startPort, '127.0.0.1');
}
/**
* Uses listen instead of connect. Is faster, but if there is another listener on 0.0.0.0 then this will take 127.0.0.1 from that listener.
*/
export function findFreePortFaster(startPort: number, giveUpAfter: number, timeout: number): Promise<number> {
let resolved: boolean = false;
let timeoutHandle: NodeJS.Timeout | undefined = undefined;
let countTried: number = 1;
const server = net.createServer({ pauseOnConnect: true });
function doResolve(port: number, resolve: (port: number) => void) {
if (!resolved) {
resolved = true;
server.removeAllListeners();
server.close();
if (timeoutHandle) {
clearTimeout(timeoutHandle);
}
resolve(port);
}
}
return new Promise<number>(resolve => {
timeoutHandle = setTimeout(() => {
doResolve(0, resolve);
}, timeout);
server.on('listening', () => {
doResolve(startPort, resolve);
});
server.on('error', err => {
if (err && ((<any>err).code === 'EADDRINUSE' || (<any>err).code === 'EACCES') && (countTried < giveUpAfter)) {
startPort++;
countTried++;
server.listen(startPort, '127.0.0.1');
} else {
doResolve(0, resolve);
}
});
server.on('close', () => {
doResolve(0, resolve);
});
server.listen(startPort, '127.0.0.1');
});
}
function dispose(socket: net.Socket): void {
try {
socket.removeAllListeners('connect');
File diff suppressed because one or more lines are too long
@@ -8,10 +8,7 @@ import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState';
suite('ScrollbarState', () => {
test('inflates slider size', () => {
let actual = new ScrollbarState(0, 14, 0);
actual.setVisibleSize(339);
actual.setScrollSize(42423);
actual.setScrollPosition(32787);
let actual = new ScrollbarState(0, 14, 0, 339, 42423, 32787);
assert.equal(actual.getArrowSize(), 0);
assert.equal(actual.getScrollPosition(), 32787);
@@ -34,10 +31,7 @@ suite('ScrollbarState', () => {
});
test('inflates slider size with arrows', () => {
let actual = new ScrollbarState(12, 14, 0);
actual.setVisibleSize(339);
actual.setScrollSize(42423);
actual.setScrollPosition(32787);
let actual = new ScrollbarState(12, 14, 0, 339, 42423, 32787);
assert.equal(actual.getArrowSize(), 12);
assert.equal(actual.getScrollPosition(), 32787);
+14
View File
@@ -237,6 +237,20 @@ suite('Event', function () {
assert.equal(calls, 2);
});
test('Debounce Event - leading reset', async function () {
const emitter = new Emitter<number>();
let debounced = Event.debounce(emitter.event, (l, e) => l ? l + 1 : 1, 0, /*leading=*/true);
let calls: number[] = [];
debounced((e) => calls.push(e));
emitter.fire(1);
emitter.fire(1);
await timeout(1);
assert.deepEqual(calls, [1, 1]);
});
test('Emitter - In Order Delivery', function () {
const a = new Emitter<string>();
const listener2Events: string[] = [];
@@ -12,8 +12,8 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionManagementChannel, GlobalExtensionEnablementServiceClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -113,6 +113,9 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
disposables.add(logService);
logService.info('main', JSON.stringify(configuration));
const mainProcessService = new MainProcessService(server, mainRouter);
services.set(IMainProcessService, mainProcessService);
const configurationService = new ConfigurationService(environmentService.settingsResource);
disposables.add(configurationService);
await configurationService.initialize();
@@ -124,8 +127,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(ILoggerService, new SyncDescriptor(LoggerService));
const mainProcessService = new MainProcessService(server, mainRouter);
services.set(IMainProcessService, mainProcessService);
const electronService = createChannelSender<IElectronService>(mainProcessService.getChannel('electron'), { context: configuration.windowId });
services.set(IElectronService, electronService);
@@ -184,6 +185,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
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(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser));
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
+17 -18
View File
@@ -171,7 +171,7 @@ export class CodeApplication extends Disposable {
app.on('web-contents-created', (_event: Event, contents) => {
contents.on('will-attach-webview', (event: Event, webPreferences, params) => {
const isValidWebviewSource = (source: string | undefined): boolean => {
const isValidWebviewSource = (source: string): boolean => {
if (!source) {
return false;
}
@@ -191,12 +191,11 @@ export class CodeApplication extends Disposable {
webPreferences.nodeIntegration = false;
// Verify URLs being loaded
// https://github.com/electron/electron/issues/21553
if (isValidWebviewSource(params.src) && isValidWebviewSource((webPreferences as { preloadURL: string }).preloadURL)) {
if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) {
return;
}
delete (webPreferences as { preloadURL: string }).preloadURL; // https://github.com/electron/electron/issues/21553
delete webPreferences.preloadUrl;
// Otherwise prevent loading
this.logService.error('webContents#web-contents-created: Prevented webview attach');
@@ -498,27 +497,27 @@ export class CodeApplication extends Disposable {
this.logService.info(`Tracing: waiting for windows to get ready...`);
let recordingStopped = false;
const stopRecording = async (timeout: boolean) => {
const stopRecording = (timeout: boolean) => {
if (recordingStopped) {
return;
}
recordingStopped = true; // only once
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()));
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}`);
}
} else {
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
}
});
};
// Wait up to 30s before creating the trace anyways
+9 -9
View File
@@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects';
import * as nls from 'vs/nls';
import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme } from 'electron';
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment } 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 Record<string, (string) | (string[])>;
const responseHeaders = details.responseHeaders as { [key: string]: string[] };
const contentType = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
const contentType: string[] = (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 Record<string, string> })));
this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined } })));
}
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 && nativeTheme.shouldUseInvertedColorScheme;
windowConfiguration.highContrast = isWindows && autoDetectHighContrast && systemPreferences.isInvertedColorScheme();
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.autoHideMenuBar = isFullscreen;
this._win.setAutoHideMenuBar(isFullscreen);
break;
case ('visible'):
this._win.setMenuBarVisibility(true);
this._win.autoHideMenuBar = false;
this._win.setAutoHideMenuBar(false);
break;
case ('toggle'):
this._win.setMenuBarVisibility(false);
this._win.autoHideMenuBar = true;
this._win.setAutoHideMenuBar(true);
break;
case ('hidden'):
this._win.setMenuBarVisibility(false);
this._win.autoHideMenuBar = false;
this._win.setAutoHideMenuBar(false);
break;
}
}
@@ -14,7 +14,7 @@ import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/c
import { EditorOption, IEditorConstructionOptions, EditorFontLigatures } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo';
import { IDimension } from 'vs/editor/common/editorCommon';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
class CSSBasedConfigurationCache {
@@ -87,6 +87,7 @@ export interface ISerializedFontInfo {
readonly typicalFullwidthCharacterWidth: number;
readonly canUseHalfwidthRightwardsArrow: boolean;
readonly spaceWidth: number;
middotWidth: number;
readonly maxDigitWidth: number;
}
@@ -159,6 +160,7 @@ class CSSBasedConfiguration extends Disposable {
const savedFontInfo = savedFontInfos[i];
// compatibility with older versions of VS Code which did not store this...
savedFontInfo.fontFeatureSettings = savedFontInfo.fontFeatureSettings || EditorFontLigatures.OFF;
savedFontInfo.middotWidth = savedFontInfo.middotWidth || savedFontInfo.spaceWidth;
const fontInfo = new FontInfo(savedFontInfo, false);
this._writeToCache(fontInfo, fontInfo);
}
@@ -183,6 +185,7 @@ class CSSBasedConfiguration extends Disposable {
typicalFullwidthCharacterWidth: Math.max(readConfig.typicalFullwidthCharacterWidth, 5),
canUseHalfwidthRightwardsArrow: readConfig.canUseHalfwidthRightwardsArrow,
spaceWidth: Math.max(readConfig.spaceWidth, 5),
middotWidth: Math.max(readConfig.middotWidth, 5),
maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5),
}, false);
}
@@ -223,7 +226,8 @@ class CSSBasedConfiguration extends Disposable {
const rightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, monospace);
const halfwidthRightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, null);
this.createRequest('·', CharWidthRequestType.Regular, all, monospace);
// middle dot character
const middot = this.createRequest('·', CharWidthRequestType.Regular, all, monospace);
// monospace test: some characters
this.createRequest('|', CharWidthRequestType.Regular, all, monospace);
@@ -289,6 +293,7 @@ class CSSBasedConfiguration extends Disposable {
typicalFullwidthCharacterWidth: typicalFullwidthCharacter.width,
canUseHalfwidthRightwardsArrow: canUseHalfwidthRightwardsArrow,
spaceWidth: space.width,
middotWidth: middot.width,
maxDigitWidth: maxDigitWidth
}, canTrustBrowserZoomLevel);
}
@@ -333,7 +338,7 @@ export class Configuration extends CommonEditorConfiguration {
}
this._register(browser.onDidChangeZoomLevel(_ => this._recomputeOptions()));
this._register(this.accessibilityService.onDidChangeAccessibilitySupport(() => this._recomputeOptions()));
this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => this._recomputeOptions()));
this._recomputeOptions();
}
@@ -374,7 +379,7 @@ export class Configuration extends CommonEditorConfiguration {
emptySelectionClipboard: browser.isWebKit || browser.isFirefox,
pixelRatio: browser.getPixelRatio(),
zoomLevel: browser.getZoomLevel(),
accessibilitySupport: this.accessibilityService.getAccessibilitySupport()
accessibilitySupport: this.accessibilityService.isScreenReaderOptimized() ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled
};
}
@@ -363,6 +363,7 @@ class MouseDownOperation extends Disposable {
this._isActive = true;
this._mouseMoveMonitor.startMonitoring(
e.target,
e.buttons,
createMouseMoveEventMerger(null),
(e) => this._onMouseDownThenMove(e),
@@ -387,6 +388,7 @@ class MouseDownOperation extends Disposable {
if (!this._isActive) {
this._isActive = true;
this._mouseMoveMonitor.startMonitoring(
e.target,
e.buttons,
createMouseMoveEventMerger(null),
(e) => this._onMouseDownThenMove(e),
+103 -2
View File
@@ -17,6 +17,7 @@ import { HorizontalPosition } from 'vs/editor/common/view/renderingContext';
import { ViewContext } from 'vs/editor/common/view/viewContext';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
import * as dom from 'vs/base/browser/dom';
export interface IViewZoneData {
viewZoneId: string;
@@ -835,8 +836,17 @@ export class MouseTargetFactory {
}
private static _actualDoHitTestWithCaretRangeFromPoint(ctx: HitTestContext, coords: ClientCoordinates): IHitTestResult {
const range: Range = document.caretRangeFromPoint(coords.clientX, coords.clientY);
const shadowRoot = dom.getShadowRoot(ctx.viewDomNode);
let range: Range;
if (shadowRoot) {
if (typeof shadowRoot.caretRangeFromPoint === 'undefined') {
range = shadowCaretRangeFromPoint(shadowRoot, coords.clientX, coords.clientY);
} else {
range = shadowRoot.caretRangeFromPoint(coords.clientX, coords.clientY);
}
} else {
range = document.caretRangeFromPoint(coords.clientX, coords.clientY);
}
if (!range || !range.startContainer) {
return {
@@ -1009,3 +1019,94 @@ export class MouseTargetFactory {
};
}
}
export function shadowCaretRangeFromPoint(shadowRoot: ShadowRoot, x: number, y: number): Range {
const range = document.createRange();
// Get the element under the point
let el: Element | null = shadowRoot.elementFromPoint(x, y);
if (el !== null) {
// Get the last child of the element until its firstChild is a text node
// This assumes that the pointer is on the right of the line, out of the tokens
// and that we want to get the offset of the last token of the line
while (el && el.firstChild && el.firstChild.nodeType !== el.firstChild.TEXT_NODE) {
el = <Element>el.lastChild;
}
// Grab its rect
const rect = el.getBoundingClientRect();
// And its font
const font = window.getComputedStyle(el, null).getPropertyValue('font');
// And also its txt content
const text = (el as any).innerText;
// Position the pixel cursor at the left of the element
let pixelCursor = rect.left;
let offset = 0;
let step: number;
// If the point is on the right of the box put the cursor after the last character
if (x > rect.left + rect.width) {
offset = text.length;
} else {
const charWidthReader = CharWidthReader.getInstance();
// Goes through all the characters of the innerText, and checks if the x of the point
// belongs to the character.
for (let i = 0; i < text.length + 1; i++) {
// The step is half the width of the character
step = charWidthReader.getCharWidth(text.charAt(i), font) / 2;
// Move to the center of the character
pixelCursor += step;
// If the x of the point is smaller that the position of the cursor, the point is over that character
if (x < pixelCursor) {
offset = i;
break;
}
// Move between the current character and the next
pixelCursor += step;
}
}
// Creates a range with the text node of the element and set the offset found
range.setStart(el.firstChild!, offset);
range.setEnd(el.firstChild!, offset);
}
return range;
}
class CharWidthReader {
private static _INSTANCE: CharWidthReader | null = null;
public static getInstance(): CharWidthReader {
if (!CharWidthReader._INSTANCE) {
CharWidthReader._INSTANCE = new CharWidthReader();
}
return CharWidthReader._INSTANCE;
}
private readonly _cache: { [cacheKey: string]: number; };
private readonly _canvas: HTMLCanvasElement;
private constructor() {
this._cache = {};
this._canvas = document.createElement('canvas');
}
public getCharWidth(char: string, font: string): number {
const cacheKey = char + font;
if (this._cache[cacheKey]) {
return this._cache[cacheKey];
}
const context = this._canvas.getContext('2d')!;
context.font = font;
const metrics = context.measureText(char);
const width = metrics.width;
this._cache[cacheKey] = width;
return width;
}
}
@@ -17,7 +17,7 @@ import { ViewController } from 'vs/editor/browser/view/viewController';
import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart';
import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers';
import { Margin } from 'vs/editor/browser/viewParts/margin/margin';
import { RenderLineNumbersType, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions';
import { RenderLineNumbersType, EditorOption, IComputedEditorOptions, EditorOptions } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier';
import { Position } from 'vs/editor/common/core/position';
@@ -62,8 +62,8 @@ export class TextAreaHandler extends ViewPart {
private _scrollLeft: number;
private _scrollTop: number;
private _accessibilitySupport: AccessibilitySupport;
private _accessibilityPageSize: number;
private _accessibilitySupport!: AccessibilitySupport;
private _accessibilityPageSize!: number;
private _contentLeft: number;
private _contentWidth: number;
private _contentHeight: number;
@@ -77,6 +77,7 @@ export class TextAreaHandler extends ViewPart {
*/
private _visibleTextArea: VisibleTextAreaData | null;
private _selections: Selection[];
private _modelSelections: Selection[];
/**
* The position at which the textarea was rendered.
@@ -99,8 +100,7 @@ export class TextAreaHandler extends ViewPart {
const options = this._context.configuration.options;
const layoutInfo = options.get(EditorOption.layoutInfo);
this._accessibilitySupport = options.get(EditorOption.accessibilitySupport);
this._accessibilityPageSize = options.get(EditorOption.accessibilityPageSize);
this._setAccessibilityOptions(options);
this._contentLeft = layoutInfo.contentLeft;
this._contentWidth = layoutInfo.contentWidth;
this._contentHeight = layoutInfo.height;
@@ -111,6 +111,7 @@ export class TextAreaHandler extends ViewPart {
this._visibleTextArea = null;
this._selections = [new Selection(1, 1, 1, 1)];
this._modelSelections = [new Selection(1, 1, 1, 1)];
this._lastRenderPosition = null;
// Text Area (The focus will always be in the textarea when the cursor is blinking)
@@ -149,24 +150,30 @@ export class TextAreaHandler extends ViewPart {
const textAreaInputHost: ITextAreaInputHost = {
getDataToCopy: (generateHTML: boolean): ClipboardDataToCopy => {
const rawTextToCopy = this._context.model.getPlainTextToCopy(this._selections, this._emptySelectionClipboard, platform.isWindows);
const rawTextToCopy = this._context.model.getPlainTextToCopy(this._modelSelections, this._emptySelectionClipboard, platform.isWindows);
const newLineCharacter = this._context.model.getEOL();
const isFromEmptySelection = (this._emptySelectionClipboard && this._selections.length === 1 && this._selections[0].isEmpty());
const isFromEmptySelection = (this._emptySelectionClipboard && this._modelSelections.length === 1 && this._modelSelections[0].isEmpty());
const multicursorText = (Array.isArray(rawTextToCopy) ? rawTextToCopy : null);
const text = (Array.isArray(rawTextToCopy) ? rawTextToCopy.join(newLineCharacter) : rawTextToCopy);
let html: string | null | undefined = undefined;
let mode: string | null = null;
if (generateHTML) {
if (CopyOptions.forceCopyWithSyntaxHighlighting || (this._copyWithSyntaxHighlighting && text.length < 65536)) {
html = this._context.model.getHTMLToCopy(this._selections, this._emptySelectionClipboard);
const richText = this._context.model.getRichTextToCopy(this._modelSelections, this._emptySelectionClipboard);
if (richText) {
html = richText.html;
mode = richText.mode;
}
}
}
return {
isFromEmptySelection,
multicursorText,
text,
html
html,
mode
};
},
@@ -220,11 +227,13 @@ export class TextAreaHandler extends ViewPart {
this._register(this._textAreaInput.onPaste((e: IPasteData) => {
let pasteOnNewLine = false;
let multicursorText: string[] | null = null;
let mode: string | null = null;
if (e.metadata) {
pasteOnNewLine = (this._emptySelectionClipboard && !!e.metadata.isFromEmptySelection);
multicursorText = (typeof e.metadata.multicursorText !== 'undefined' ? e.metadata.multicursorText : null);
mode = e.metadata.mode;
}
this._viewController.paste('keyboard', e.text, pasteOnNewLine, multicursorText);
this._viewController.paste('keyboard', e.text, pasteOnNewLine, multicursorText, mode);
}));
this._register(this._textAreaInput.onCut(() => {
@@ -344,14 +353,24 @@ export class TextAreaHandler extends ViewPart {
return options.get(EditorOption.ariaLabel);
}
private _setAccessibilityOptions(options: IComputedEditorOptions): void {
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;
} else {
this._accessibilityPageSize = accessibilityPageSize;
}
}
// --- begin event handlers
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
const options = this._context.configuration.options;
const layoutInfo = options.get(EditorOption.layoutInfo);
this._accessibilitySupport = options.get(EditorOption.accessibilitySupport);
this._accessibilityPageSize = options.get(EditorOption.accessibilityPageSize);
this._setAccessibilityOptions(options);
this._contentLeft = layoutInfo.contentLeft;
this._contentWidth = layoutInfo.contentWidth;
this._contentHeight = layoutInfo.height;
@@ -377,6 +396,7 @@ export class TextAreaHandler extends ViewPart {
}
public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean {
this._selections = e.selections.slice(0);
this._modelSelections = e.modelSelections.slice(0);
this._textAreaInput.writeScreenReaderContent('selection changed');
return true;
}
@@ -41,12 +41,14 @@ export interface ClipboardDataToCopy {
multicursorText: string[] | null | undefined;
text: string;
html: string | null | undefined;
mode: string | null;
}
export interface ClipboardStoredMetadata {
version: 1;
isFromEmptySelection: boolean | undefined;
multicursorText: string[] | null | undefined;
mode: string | null;
}
export interface ITextAreaInputHost {
@@ -550,7 +552,8 @@ export class TextAreaInput extends Disposable {
const storedMetadata: ClipboardStoredMetadata = {
version: 1,
isFromEmptySelection: dataToCopy.isFromEmptySelection,
multicursorText: dataToCopy.multicursorText
multicursorText: dataToCopy.multicursorText,
mode: dataToCopy.mode
};
InMemoryClipboardMetadataManager.INSTANCE.set(
// When writing "LINE\r\n" to the clipboard and then pasting,
+16 -3
View File
@@ -308,6 +308,14 @@ export interface IPartialEditorMouseEvent {
readonly target: IMouseTarget | null;
}
/**
* A paste event originating from the editor.
*/
export interface IPasteEvent {
readonly range: Range;
readonly mode: string | null;
}
/**
* An overview ruler
* @internal
@@ -416,11 +424,11 @@ export interface ICodeEditor extends editorCommon.IEditor {
/**
* An event emitted after composition has started.
*/
onCompositionStart(listener: () => void): IDisposable;
onDidCompositionStart(listener: () => void): IDisposable;
/**
* An event emitted after composition has ended.
*/
onCompositionEnd(listener: () => void): IDisposable;
onDidCompositionEnd(listener: () => void): IDisposable;
/**
* An event emitted when editing failed because the editor is read-only.
* @event
@@ -431,7 +439,7 @@ export interface ICodeEditor extends editorCommon.IEditor {
* An event emitted when users paste text in the editor.
* @event
*/
onDidPaste(listener: (range: Range) => void): IDisposable;
onDidPaste(listener: (e: IPasteEvent) => void): IDisposable;
/**
* An event emitted on a "mouseup".
* @event
@@ -723,6 +731,11 @@ export interface ICodeEditor extends editorCommon.IEditor {
*/
getTelemetryData(): { [key: string]: any } | undefined;
/**
* Returns the editor's container dom node
*/
getContainerDomNode(): HTMLElement;
/**
* Returns the editor's dom node
*/
+8 -2
View File
@@ -182,7 +182,13 @@ export class GlobalEditorMouseMoveMonitor extends Disposable {
this._keydownListener = null;
}
public startMonitoring(initialButtons: number, merger: EditorMouseEventMerger, mouseMoveCallback: (e: EditorMouseEvent) => void, onStopCallback: () => void): void {
public startMonitoring(
initialElement: HTMLElement,
initialButtons: number,
merger: EditorMouseEventMerger,
mouseMoveCallback: (e: EditorMouseEvent) => void,
onStopCallback: () => void
): void {
// Add a <<capture>> keydown event listener that will cancel the monitoring
// if something other than a modifier key is pressed
@@ -199,7 +205,7 @@ export class GlobalEditorMouseMoveMonitor extends Disposable {
return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode));
};
this._globalMouseMoveMonitor.startMonitoring(initialButtons, myMerger, mouseMoveCallback, () => {
this._globalMouseMoveMonitor.startMonitoring(initialElement, initialButtons, myMerger, mouseMoveCallback, () => {
this._keydownListener!.dispose();
onStopCallback();
});
@@ -89,7 +89,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC
return editorWithWidgetFocus;
}
abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void;
abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void;
abstract removeDecorationType(key: string): void;
abstract resolveDecorationOptions(decorationTypeKey: string | undefined, writable: boolean): IModelDecorationOptions;
@@ -120,6 +120,16 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC
return this._transientWatchers[uri].get(key);
}
public getTransientModelProperties(model: ITextModel): [string, any][] | undefined {
const uri = model.uri.toString();
if (!this._transientWatchers.hasOwnProperty(uri)) {
return undefined;
}
return this._transientWatchers[uri].keys().map(key => [key, this._transientWatchers[uri].get(key)]);
}
_removeWatcher(w: ModelTransientSettingWatcher): void {
delete this._transientWatchers[w.uri];
}
@@ -145,4 +155,8 @@ export class ModelTransientSettingWatcher {
public get(key: string): any {
return this._values[key];
}
public keys(): string[] {
return Object.keys(this._values);
}
}
@@ -37,12 +37,13 @@ export interface ICodeEditorService {
*/
getFocusedCodeEditor(): ICodeEditor | null;
registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void;
registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void;
removeDecorationType(key: string): void;
resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions;
setTransientModelProperty(model: ITextModel, key: string, value: any): void;
getTransientModelProperty(model: ITextModel, key: string): any;
getTransientModelProperties(model: ITextModel): [string, any][] | undefined;
getActiveCodeEditor(): ICodeEditor | null;
openCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null>;
@@ -14,31 +14,101 @@ import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, Overview
import { IResourceInput } from 'vs/platform/editor/common/editor';
import { ITheme, IThemeService, ThemeColor } from 'vs/platform/theme/common/themeService';
class RefCountedStyleSheet {
private readonly _parent: CodeEditorServiceImpl;
private readonly _editorId: string;
public readonly styleSheet: HTMLStyleElement;
private _refCount: number;
constructor(parent: CodeEditorServiceImpl, editorId: string, styleSheet: HTMLStyleElement) {
this._parent = parent;
this._editorId = editorId;
this.styleSheet = styleSheet;
this._refCount = 0;
}
public ref(): void {
this._refCount++;
}
public unref(): void {
this._refCount--;
if (this._refCount === 0) {
this.styleSheet.parentNode?.removeChild(this.styleSheet);
this._parent._removeEditorStyleSheets(this._editorId);
}
}
}
class GlobalStyleSheet {
public readonly styleSheet: HTMLStyleElement;
constructor(styleSheet: HTMLStyleElement) {
this.styleSheet = styleSheet;
}
public ref(): void {
}
public unref(): void {
}
}
export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService {
private readonly _styleSheet: HTMLStyleElement;
private _globalStyleSheet: GlobalStyleSheet | null;
private readonly _decorationOptionProviders = new Map<string, IModelDecorationOptionsProvider>();
private readonly _editorStyleSheets = new Map<string, RefCountedStyleSheet>();
private readonly _themeService: IThemeService;
constructor(@IThemeService themeService: IThemeService, styleSheet = dom.createStyleSheet()) {
constructor(@IThemeService themeService: IThemeService, styleSheet: HTMLStyleElement | null = null) {
super();
this._styleSheet = styleSheet;
this._globalStyleSheet = styleSheet ? new GlobalStyleSheet(styleSheet) : null;
this._themeService = themeService;
}
public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void {
private _getOrCreateGlobalStyleSheet(): GlobalStyleSheet {
if (!this._globalStyleSheet) {
this._globalStyleSheet = new GlobalStyleSheet(dom.createStyleSheet());
}
return this._globalStyleSheet;
}
private _getOrCreateStyleSheet(editor: ICodeEditor | undefined): GlobalStyleSheet | RefCountedStyleSheet {
if (!editor) {
return this._getOrCreateGlobalStyleSheet();
}
const domNode = editor.getContainerDomNode();
if (!dom.isInShadowDOM(domNode)) {
return this._getOrCreateGlobalStyleSheet();
}
const editorId = editor.getId();
if (!this._editorStyleSheets.has(editorId)) {
const refCountedStyleSheet = new RefCountedStyleSheet(this, editorId, dom.createStyleSheet(domNode));
this._editorStyleSheets.set(editorId, refCountedStyleSheet);
}
return this._editorStyleSheets.get(editorId)!;
}
_removeEditorStyleSheets(editorId: string): void {
this._editorStyleSheets.delete(editorId);
}
public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void {
let provider = this._decorationOptionProviders.get(key);
if (!provider) {
const styleSheet = this._getOrCreateStyleSheet(editor);
const providerArgs: ProviderArguments = {
styleSheet: this._styleSheet,
styleSheet: styleSheet.styleSheet,
key: key,
parentTypeKey: parentTypeKey,
options: options || Object.create(null)
};
if (!parentTypeKey) {
provider = new DecorationTypeOptionsProvider(this._themeService, providerArgs);
provider = new DecorationTypeOptionsProvider(this._themeService, styleSheet, providerArgs);
} else {
provider = new DecorationSubTypeOptionsProvider(this._themeService, providerArgs);
provider = new DecorationSubTypeOptionsProvider(this._themeService, styleSheet, providerArgs);
}
this._decorationOptionProviders.set(key, provider);
}
@@ -76,13 +146,16 @@ interface IModelDecorationOptionsProvider extends IDisposable {
class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvider {
private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet;
public refCount: number;
private readonly _parentTypeKey: string | undefined;
private _beforeContentRules: DecorationCSSRules | null;
private _afterContentRules: DecorationCSSRules | null;
constructor(themeService: IThemeService, providerArgs: ProviderArguments) {
constructor(themeService: IThemeService, styleSheet: GlobalStyleSheet | RefCountedStyleSheet, providerArgs: ProviderArguments) {
this._styleSheet = styleSheet;
this._styleSheet.ref();
this._parentTypeKey = providerArgs.parentTypeKey;
this.refCount = 0;
@@ -110,6 +183,7 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide
this._afterContentRules.dispose();
this._afterContentRules = null;
}
this._styleSheet.unref();
}
}
@@ -124,6 +198,7 @@ interface ProviderArguments {
class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider {
private readonly _disposables = new DisposableStore();
private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet;
public refCount: number;
public className: string | undefined;
@@ -136,7 +211,9 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider {
public overviewRuler: IModelDecorationOverviewRulerOptions | undefined;
public stickiness: TrackedRangeStickiness | undefined;
constructor(themeService: IThemeService, providerArgs: ProviderArguments) {
constructor(themeService: IThemeService, styleSheet: GlobalStyleSheet | RefCountedStyleSheet, providerArgs: ProviderArguments) {
this._styleSheet = styleSheet;
this._styleSheet.ref();
this.refCount = 0;
const createCSSRules = (type: ModelDecorationCSSRuleType) => {
@@ -202,6 +279,7 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider {
public dispose(): void {
this._disposables.dispose();
this._styleSheet.unref();
}
}
+3 -3
View File
@@ -37,7 +37,7 @@ export interface IMouseDispatchData {
export interface ICommandDelegate {
executeEditorCommand(editorCommand: CoreEditorCommand, args: any): void;
paste(source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null): void;
paste(source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void;
type(source: string, text: string): void;
replacePreviousChar(source: string, text: string, replaceCharCnt: number): void;
compositionStart(source: string): void;
@@ -69,8 +69,8 @@ export class ViewController {
this.commandDelegate.executeEditorCommand(editorCommand, args);
}
public paste(source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null): void {
this.commandDelegate.paste(source, text, pasteOnNewLine, multicursorText);
public paste(source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void {
this.commandDelegate.paste(source, text, pasteOnNewLine, multicursorText, mode);
}
public type(source: string, text: string): void {
+9 -3
View File
@@ -38,6 +38,7 @@ import { ViewCursors } from 'vs/editor/browser/viewParts/viewCursors/viewCursors
import { ViewZones } from 'vs/editor/browser/viewParts/viewZones/viewZones';
import { Cursor } from 'vs/editor/common/controller/cursor';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IConfiguration } from 'vs/editor/common/editorCommon';
import { RenderingContext } from 'vs/editor/common/view/renderingContext';
import { ViewContext } from 'vs/editor/common/view/viewContext';
@@ -525,10 +526,15 @@ export class View extends ViewEventHandler {
}
public layoutContentWidget(widgetData: IContentWidgetData): void {
const newPosition = widgetData.position ? widgetData.position.position : null;
const newRange = widgetData.position ? widgetData.position.range || null : null;
let newRange = widgetData.position ? widgetData.position.range || null : null;
if (newRange === null) {
const newPosition = widgetData.position ? widgetData.position.position : null;
if (newPosition !== null) {
newRange = new Range(newPosition.lineNumber, newPosition.column, newPosition.lineNumber, newPosition.column);
}
}
const newPreference = widgetData.position ? widgetData.position.preference : null;
this.contentWidgets.setWidgetPosition(widgetData.widget, newPosition, newRange, newPreference);
this.contentWidgets.setWidgetPosition(widgetData.widget, newRange, newPreference);
this._scheduleRender();
}
@@ -7,7 +7,6 @@ import * as dom from 'vs/base/browser/dom';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { ContentWidgetPositionPreference, IContentWidget } from 'vs/editor/browser/editorBrowser';
import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { Constants } from 'vs/base/common/uint';
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
@@ -112,9 +111,9 @@ export class ViewContentWidgets extends ViewPart {
this.setShouldRender();
}
public setWidgetPosition(widget: IContentWidget, position: IPosition | null, range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void {
public setWidgetPosition(widget: IContentWidget, range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void {
const myWidget = this._widgets[widget.getId()];
myWidget.setPosition(position, range, preference);
myWidget.setPosition(range, preference);
this.setShouldRender();
}
@@ -187,8 +186,6 @@ class Widget {
private _contentLeft: number;
private _lineHeight: number;
private _position: IPosition | null;
private _viewPosition: Position | null;
private _range: IRange | null;
private _viewRange: Range | null;
private _preference: ContentWidgetPositionPreference[] | null;
@@ -217,9 +214,7 @@ class Widget {
this._contentLeft = layoutInfo.contentLeft;
this._lineHeight = options.get(EditorOption.lineHeight);
this._position = null;
this._range = null;
this._viewPosition = null;
this._viewRange = null;
this._preference = [];
this._cachedDomNodeClientWidth = -1;
@@ -246,26 +241,19 @@ class Widget {
}
public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): void {
this._setPosition(this._position, this._range);
this._setPosition(this._range);
}
private _setPosition(position: IPosition | null, range: IRange | null): void {
this._position = position;
private _setPosition(range: IRange | null): void {
this._range = range;
this._viewPosition = null;
this._viewRange = null;
if (this._position) {
// Do not trust that widgets give a valid position
const validModelPosition = this._context.model.validateModelPosition(this._position);
if (this._context.model.coordinatesConverter.modelPositionIsVisible(validModelPosition)) {
this._viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(validModelPosition);
}
}
if (this._range) {
// Do not trust that widgets give a valid position
const validModelRange = this._context.model.validateModelRange(this._range);
this._viewRange = this._context.model.coordinatesConverter.convertModelRangeToViewRange(validModelRange);
if (this._context.model.coordinatesConverter.modelPositionIsVisible(validModelRange.getStartPosition()) || this._context.model.coordinatesConverter.modelPositionIsVisible(validModelRange.getEndPosition())) {
this._viewRange = this._context.model.coordinatesConverter.convertModelRangeToViewRange(validModelRange);
}
}
}
@@ -277,8 +265,8 @@ class Widget {
);
}
public setPosition(position: IPosition | null, range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void {
this._setPosition(position, range);
public setPosition(range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void {
this._setPosition(range);
this._preference = preference;
this._cachedDomNodeClientWidth = -1;
this._cachedDomNodeClientHeight = -1;
@@ -327,60 +315,65 @@ class Widget {
};
}
private _layoutBoxInPage(topLeft: Coordinate, bottomLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult | null {
const aboveLeft0 = topLeft.left - ctx.scrollLeft;
const belowLeft0 = bottomLeft.left - ctx.scrollLeft;
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);
let aboveTop = topLeft.top - height;
let belowTop = bottomLeft.top + this._lineHeight;
let aboveLeft = aboveLeft0 + this._contentLeft;
let belowLeft = belowLeft0 + this._contentLeft;
let absoluteLeft = domNodePosition.left + left - dom.StandardWindow.scrollX;
if (absoluteLeft + width > MAX_LIMIT) {
const delta = absoluteLeft - (MAX_LIMIT - width);
absoluteLeft -= delta;
left -= delta;
}
if (absoluteLeft < MIN_LIMIT) {
const delta = absoluteLeft - MIN_LIMIT;
absoluteLeft -= delta;
left -= delta;
}
return [left, absoluteLeft];
}
private _layoutBoxInPage(topLeft: Coordinate, bottomLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult | null {
const aboveTop = topLeft.top - height;
const belowTop = bottomLeft.top + this._lineHeight;
const domNodePosition = dom.getDomNodePagePosition(this._viewDomNode.domNode);
const absoluteAboveTop = domNodePosition.top + aboveTop - dom.StandardWindow.scrollY;
const absoluteBelowTop = domNodePosition.top + belowTop - dom.StandardWindow.scrollY;
let absoluteAboveLeft = domNodePosition.left + aboveLeft - dom.StandardWindow.scrollX;
let absoluteBelowLeft = domNodePosition.left + belowLeft - dom.StandardWindow.scrollX;
const INNER_WIDTH = window.innerWidth || document.documentElement!.clientWidth || document.body.clientWidth;
const INNER_HEIGHT = window.innerHeight || document.documentElement!.clientHeight || document.body.clientHeight;
const windowSize = dom.getClientArea(document.body);
const [aboveLeft, absoluteAboveLeft] = this._layoutHorizontalSegmentInPage(windowSize, domNodePosition, topLeft.left - ctx.scrollLeft + this._contentLeft, width);
const [belowLeft, absoluteBelowLeft] = this._layoutHorizontalSegmentInPage(windowSize, domNodePosition, bottomLeft.left - ctx.scrollLeft + this._contentLeft, width);
// Leave some clearance to the bottom
// Leave some clearance to the top/bottom
const TOP_PADDING = 22;
const BOTTOM_PADDING = 22;
const fitsAbove = (absoluteAboveTop >= TOP_PADDING),
fitsBelow = (absoluteBelowTop + height <= INNER_HEIGHT - BOTTOM_PADDING);
if (absoluteAboveLeft + width + 20 > INNER_WIDTH) {
const delta = absoluteAboveLeft - (INNER_WIDTH - width - 20);
absoluteAboveLeft -= delta;
aboveLeft -= delta;
}
if (absoluteBelowLeft + width + 20 > INNER_WIDTH) {
const delta = absoluteBelowLeft - (INNER_WIDTH - width - 20);
absoluteBelowLeft -= delta;
belowLeft -= delta;
}
if (absoluteAboveLeft < 0) {
const delta = absoluteAboveLeft;
absoluteAboveLeft -= delta;
aboveLeft -= delta;
}
if (absoluteBelowLeft < 0) {
const delta = absoluteBelowLeft;
absoluteBelowLeft -= delta;
belowLeft -= delta;
}
const fitsAbove = (absoluteAboveTop >= TOP_PADDING);
const fitsBelow = (absoluteBelowTop + height <= windowSize.height - BOTTOM_PADDING);
if (this._fixedOverflowWidgets) {
aboveTop = absoluteAboveTop;
belowTop = absoluteBelowTop;
aboveLeft = absoluteAboveLeft;
belowLeft = absoluteBelowLeft;
return {
fitsAbove,
aboveTop: Math.max(absoluteAboveTop, TOP_PADDING),
aboveLeft: absoluteAboveLeft,
fitsBelow,
belowTop: absoluteBelowTop,
belowLeft: absoluteBelowLeft
};
}
return { fitsAbove, aboveTop: Math.max(aboveTop, TOP_PADDING), aboveLeft, fitsBelow, belowTop, belowLeft };
return {
fitsAbove,
aboveTop: Math.max(aboveTop, TOP_PADDING),
aboveLeft,
fitsBelow,
belowTop,
belowLeft
};
}
private _prepareRenderWidgetAtExactPositionOverflowing(topLeft: Coordinate): Coordinate {
@@ -391,45 +384,45 @@ class Widget {
* Compute `this._topLeft`
*/
private _getTopAndBottomLeft(ctx: RenderingContext): [Coordinate, Coordinate] | [null, null] {
if (!this._viewPosition) {
if (!this._viewRange) {
return [null, null];
}
const visibleRangeForPosition = ctx.visibleRangeForPosition(this._viewPosition);
if (!visibleRangeForPosition) {
const visibleRangesForRange = ctx.linesVisibleRangesForRange(this._viewRange, false);
if (!visibleRangesForRange || visibleRangesForRange.length === 0) {
return [null, null];
}
const topForPosition = ctx.getVerticalOffsetForLineNumber(this._viewPosition.lineNumber) - ctx.scrollTop;
const topLeft = new Coordinate(topForPosition, visibleRangeForPosition.left);
let largestLineNumber = this._viewPosition.lineNumber;
let smallestLeft = visibleRangeForPosition.left;
if (this._viewRange) {
const visibleRangesForRange = ctx.linesVisibleRangesForRange(this._viewRange, false);
if (visibleRangesForRange && visibleRangesForRange.length > 0) {
for (let i = visibleRangesForRange.length - 1; i >= 0; i--) {
const visibleRangesForLine = visibleRangesForRange[i];
if (visibleRangesForLine.lineNumber >= largestLineNumber) {
if (visibleRangesForLine.lineNumber > largestLineNumber) {
largestLineNumber = visibleRangesForLine.lineNumber;
smallestLeft = Constants.MAX_SAFE_SMALL_INTEGER;
}
for (let j = 0, lenJ = visibleRangesForLine.ranges.length; j < lenJ; j++) {
const visibleRange = visibleRangesForLine.ranges[j];
if (visibleRange.left < smallestLeft) {
smallestLeft = visibleRange.left;
}
}
}
}
let firstLine = visibleRangesForRange[0];
let lastLine = visibleRangesForRange[0];
for (const visibleRangesForLine of visibleRangesForRange) {
if (visibleRangesForLine.lineNumber < firstLine.lineNumber) {
firstLine = visibleRangesForLine;
}
if (visibleRangesForLine.lineNumber > lastLine.lineNumber) {
lastLine = visibleRangesForLine;
}
}
const topForBottomLine = ctx.getVerticalOffsetForLineNumber(largestLineNumber) - ctx.scrollTop;
const bottomLeft = new Coordinate(topForBottomLine, smallestLeft);
let firstLineMinLeft = Constants.MAX_SAFE_SMALL_INTEGER;//firstLine.Constants.MAX_SAFE_SMALL_INTEGER;
for (const visibleRange of firstLine.ranges) {
if (visibleRange.left < firstLineMinLeft) {
firstLineMinLeft = visibleRange.left;
}
}
let lastLineMinLeft = Constants.MAX_SAFE_SMALL_INTEGER;//lastLine.Constants.MAX_SAFE_SMALL_INTEGER;
for (const visibleRange of lastLine.ranges) {
if (visibleRange.left < lastLineMinLeft) {
lastLineMinLeft = visibleRange.left;
}
}
const topForPosition = ctx.getVerticalOffsetForLineNumber(firstLine.lineNumber) - ctx.scrollTop;
const topLeft = new Coordinate(topForPosition, firstLineMinLeft);
const topForBottomLine = ctx.getVerticalOffsetForLineNumber(lastLine.lineNumber) - ctx.scrollTop;
const bottomLeft = new Coordinate(topForBottomLine, lastLineMinLeft);
return [topLeft, bottomLeft];
}
@@ -491,11 +484,11 @@ class Widget {
* On this first pass, we ensure that the content widget (if it is in the viewport) has the max width set correctly.
*/
public onBeforeRender(viewportData: ViewportData): void {
if (!this._viewPosition || !this._preference) {
if (!this._viewRange || !this._preference) {
return;
}
if (this._viewPosition.lineNumber < viewportData.startLineNumber || this._viewPosition.lineNumber > viewportData.endLineNumber) {
if (this._viewRange.endLineNumber < viewportData.startLineNumber || this._viewRange.startLineNumber > viewportData.endLineNumber) {
// Outside of viewport
return;
}
@@ -73,6 +73,7 @@ export class ViewLineOptions {
public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'all';
public readonly renderControlCharacters: boolean;
public readonly spaceWidth: number;
public readonly middotWidth: number;
public readonly useMonospaceOptimizations: boolean;
public readonly canUseHalfwidthRightwardsArrow: boolean;
public readonly lineHeight: number;
@@ -86,6 +87,7 @@ export class ViewLineOptions {
this.renderWhitespace = options.get(EditorOption.renderWhitespace);
this.renderControlCharacters = options.get(EditorOption.renderControlCharacters);
this.spaceWidth = fontInfo.spaceWidth;
this.middotWidth = fontInfo.middotWidth;
this.useMonospaceOptimizations = (
fontInfo.isMonospace
&& !options.get(EditorOption.disableMonospaceOptimizations)
@@ -102,6 +104,7 @@ export class ViewLineOptions {
&& this.renderWhitespace === other.renderWhitespace
&& this.renderControlCharacters === other.renderControlCharacters
&& this.spaceWidth === other.spaceWidth
&& this.middotWidth === other.middotWidth
&& this.useMonospaceOptimizations === other.useMonospaceOptimizations
&& this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow
&& this.lineHeight === other.lineHeight
@@ -215,6 +218,7 @@ export class ViewLine implements IVisibleLine {
lineData.tabSize,
lineData.startVisibleColumn,
options.spaceWidth,
options.middotWidth,
options.stopRenderingLineAfter,
options.renderWhitespace,
options.renderControlCharacters,
@@ -126,8 +126,8 @@ class MinimapOptions {
this.minimapWidth = layoutInfo.minimapWidth;
this.minimapHeight = layoutInfo.height;
this.canvasInnerWidth = Math.max(1, Math.floor(pixelRatio * this.minimapWidth));
this.canvasInnerHeight = Math.max(1, Math.floor(pixelRatio * this.minimapHeight));
this.canvasInnerWidth = Math.floor(pixelRatio * this.minimapWidth);
this.canvasInnerHeight = Math.floor(pixelRatio * this.minimapHeight);
this.canvasOuterWidth = this.canvasInnerWidth / pixelRatio;
this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio;
@@ -555,6 +555,7 @@ export class Minimap extends ViewPart {
this._slider.toggleClassName('active', true);
this._sliderMouseMoveMonitor.startMonitoring(
e.target,
e.buttons,
standardMouseMoveMerger,
(mouseMoveData: IStandardMouseMoveEventData) => {
@@ -656,16 +657,18 @@ export class Minimap extends ViewPart {
this._slider.setWidth(this._options.minimapWidth);
}
private _getBuffer(): ImageData {
private _getBuffer(): ImageData | null {
if (!this._buffers) {
this._buffers = new MinimapBuffers(
this._canvas.domNode.getContext('2d')!,
this._options.canvasInnerWidth,
this._options.canvasInnerHeight,
this._tokensColorTracker.getColor(ColorId.DefaultBackground)
);
if (this._options.canvasInnerWidth > 0 && this._options.canvasInnerHeight > 0) {
this._buffers = new MinimapBuffers(
this._canvas.domNode.getContext('2d')!,
this._options.canvasInnerWidth,
this._options.canvasInnerHeight,
this._tokensColorTracker.getColor(ColorId.DefaultBackground)
);
}
}
return this._buffers!.getBuffer();
return this._buffers ? this._buffers.getBuffer() : null;
}
private _onOptionsMaybeChanged(): boolean {
@@ -905,7 +908,7 @@ export class Minimap extends ViewPart {
canvasContext.fillRect(x, y, width, height);
}
private renderLines(layout: MinimapLayout): RenderData {
private renderLines(layout: MinimapLayout): RenderData | null {
const renderMinimap = this._options.renderMinimap;
const charRenderer = this._options.charRenderer();
const startLineNumber = layout.startLineNumber;
@@ -922,6 +925,10 @@ export class Minimap extends ViewPart {
// Oh well!! We need to repaint some lines...
const imageData = this._getBuffer();
if (!imageData) {
// 0 width or 0 height canvas, nothing to do
return null;
}
// Render untouched lines by using last rendered data.
let [_dirtyY1, _dirtyY2, needed] = Minimap._renderUntouchedLines(
@@ -74,8 +74,14 @@ class Settings {
this.right = position.right;
this.domWidth = position.width;
this.domHeight = position.height;
this.canvasWidth = (this.domWidth * this.pixelRatio) | 0;
this.canvasHeight = (this.domHeight * this.pixelRatio) | 0;
if (this.overviewRulerLanes === 0) {
// overview ruler is off
this.canvasWidth = 0;
this.canvasHeight = 0;
} else {
this.canvasWidth = (this.domWidth * this.pixelRatio) | 0;
this.canvasHeight = (this.domHeight * this.pixelRatio) | 0;
}
const [x, w] = this._initLanes(1, this.canvasWidth, this.overviewRulerLanes);
this.x = x;
@@ -303,6 +309,11 @@ export class DecorationsOverviewRuler extends ViewPart {
}
private _render(): void {
if (this._settings.overviewRulerLanes === 0) {
// overview ruler is off
this._domNode.setBackgroundColor(this._settings.backgroundColor ? this._settings.backgroundColor : '');
return;
}
const canvasWidth = this._settings.canvasWidth;
const canvasHeight = this._settings.canvasHeight;
const lineHeight = this._settings.lineHeight;
@@ -22,7 +22,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
import { ICommandDelegate } from 'vs/editor/browser/view/viewController';
import { IContentWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view/viewImpl';
import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, IEditorConstructionOptions } from 'vs/editor/common/config/editorOptions';
import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, IEditorConstructionOptions, filterValidationDecorations } from 'vs/editor/common/config/editorOptions';
import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor';
import { CursorColumns, ICursors } from 'vs/editor/common/controller/cursorCommon';
import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
@@ -156,13 +156,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
private readonly _onDidType: Emitter<string> = this._register(new Emitter<string>());
public readonly onDidType = this._onDidType.event;
private readonly _onCompositionStart: Emitter<void> = this._register(new Emitter<void>());
public readonly onCompositionStart = this._onCompositionStart.event;
private readonly _onDidCompositionStart: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidCompositionStart = this._onDidCompositionStart.event;
private readonly _onCompositionEnd: Emitter<void> = this._register(new Emitter<void>());
public readonly onCompositionEnd = this._onCompositionEnd.event;
private readonly _onDidCompositionEnd: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidCompositionEnd = this._onDidCompositionEnd.event;
private readonly _onDidPaste: Emitter<Range> = this._register(new Emitter<Range>());
private readonly _onDidPaste: Emitter<editorBrowser.IPasteEvent> = this._register(new Emitter<editorBrowser.IPasteEvent>());
public readonly onDidPaste = this._onDidPaste.event;
private readonly _onMouseUp: Emitter<editorBrowser.IEditorMouseEvent> = this._register(new Emitter<editorBrowser.IEditorMouseEvent>());
@@ -950,19 +950,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
const endPosition = this._modelData.cursor.getSelection().getStartPosition();
if (source === 'keyboard') {
this._onDidPaste.fire(
new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column)
{
range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column),
mode: payload.mode
}
);
}
return;
}
if (handlerId === editorCommon.Handler.CompositionStart) {
this._onCompositionStart.fire();
}
if (handlerId === editorCommon.Handler.CompositionEnd) {
this._onCompositionEnd.fire();
}
const action = this.getAction(handlerId);
if (action) {
Promise.resolve(action.run()).then(undefined, onUnexpectedError);
@@ -978,6 +974,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
}
this._modelData.cursor.trigger(source, handlerId, payload);
if (handlerId === editorCommon.Handler.CompositionStart) {
this._onDidCompositionStart.fire();
}
if (handlerId === editorCommon.Handler.CompositionEnd) {
this._onDidCompositionEnd.fire();
}
}
private _triggerEditorCommand(source: string, handlerId: string, payload: any): boolean {
@@ -1061,7 +1064,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (!this._modelData) {
return null;
}
return this._modelData.model.getLineDecorations(lineNumber, this._id, this._configuration.options.get(EditorOption.readOnly));
return this._modelData.model.getLineDecorations(lineNumber, this._id, filterValidationDecorations(this._configuration.options));
}
public deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[] {
@@ -1165,6 +1168,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
return this._modelData.view.createOverviewRuler(cssClassName);
}
public getContainerDomNode(): HTMLElement {
return this._domElement;
}
public getDomNode(): HTMLElement | null {
if (!this._modelData || !this._modelData.hasRealView) {
return null;
@@ -1441,8 +1448,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => {
editorCommand.runCoreEditorCommand(cursor, args);
},
paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null) => {
this.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText });
paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => {
this.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText, mode });
},
type: (source: string, text: string) => {
this.trigger(source, editorCommon.Handler.Type, { text });
@@ -1465,11 +1472,12 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => {
editorCommand.runCoreEditorCommand(cursor, args);
},
paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null) => {
paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => {
this._commandService.executeCommand(editorCommon.Handler.Paste, {
text: text,
pasteOnNewLine: pasteOnNewLine,
multicursorText: multicursorText
multicursorText: multicursorText,
mode
});
},
type: (source: string, text: string) => {
@@ -1548,7 +1556,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
}
private _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void {
this._codeEditorService.registerDecorationType(key, options, parentTypeKey);
this._codeEditorService.registerDecorationType(key, options, parentTypeKey, this);
}
private _removeDecorationType(key: string): void {
@@ -2164,6 +2164,7 @@ class InlineViewZonesComputer extends ViewZonesComputer {
tabSize,
0,
fontInfo.spaceWidth,
fontInfo.middotWidth,
options.get(EditorOption.stopRenderingLineAfter),
options.get(EditorOption.renderWhitespace),
options.get(EditorOption.renderControlCharacters),
@@ -782,6 +782,7 @@ export class DiffReview extends Disposable {
tabSize,
0,
fontInfo.spaceWidth,
fontInfo.middotWidth,
options.get(EditorOption.stopRenderingLineAfter),
options.get(EditorOption.renderWhitespace),
options.get(EditorOption.renderControlCharacters),
@@ -432,7 +432,7 @@ export const editorConfigurationBaseNode = Object.freeze<IConfigurationNode>({
order: 5,
type: 'object',
title: nls.localize('editorConfigurationTitle', "Editor"),
scope: ConfigurationScope.RESOURCE_LANGUAGE,
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
});
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
+92 -5
View File
@@ -135,6 +135,11 @@ export interface IEditorOptions {
* Defaults to false.
*/
readOnly?: boolean;
/**
* Should the editor render validation decorations.
* Defaults to editable.
*/
renderValidationDecorations?: 'editable' | 'on' | 'off';
/**
* Control the behavior and rendering of the scrollbars.
*/
@@ -295,6 +300,10 @@ export interface IEditorOptions {
* Enable inline color decorators and color picker rendering.
*/
colorDecorators?: boolean;
/**
* Control the behaviour of comments in the editor.
*/
comments?: IEditorCommentsOptions;
/**
* Enable custom contextmenu.
* Defaults to true.
@@ -545,7 +554,7 @@ export interface IEditorOptions {
* Controls whether to focus the inline editor in the peek widget by default.
* Defaults to false.
*/
peekWidgetFocusInlineEditor?: boolean;
peekWidgetDefaultFocus?: 'tree' | 'editor';
}
export interface IEditorConstructionOptions extends IEditorOptions {
@@ -998,6 +1007,52 @@ class EditorAccessibilitySupport extends BaseEditorOption<EditorOption.accessibi
//#endregion
//#region comments
/**
* Configuration options for editor comments
*/
export interface IEditorCommentsOptions {
/**
* Insert a space after the line comment token and inside the block comments tokens.
* Defaults to true.
*/
insertSpace?: boolean;
}
export type EditorCommentsOptions = Readonly<Required<IEditorCommentsOptions>>;
class EditorComments extends BaseEditorOption<EditorOption.comments, EditorCommentsOptions> {
constructor() {
const defaults: EditorCommentsOptions = {
insertSpace: true,
};
super(
EditorOption.comments, 'comments', defaults,
{
'editor.comments.insertSpace': {
type: 'boolean',
default: defaults.insertSpace,
description: nls.localize('comments.insertSpace', "Controls whether a space character is inserted when commenting.")
},
}
);
}
public validate(_input: any): EditorCommentsOptions {
if (typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorCommentsOptions;
return {
insertSpace: EditorBooleanOption.boolean(input.insertSpace, this.defaultValue.insertSpace),
};
}
}
//#endregion
//#region cursorBlinking
/**
@@ -2290,6 +2345,21 @@ class EditorRenderLineNumbersOption extends BaseEditorOption<EditorOption.lineNu
//#endregion
//#region renderValidationDecorations
/**
* @internal
*/
export function filterValidationDecorations(options: IComputedEditorOptions): boolean {
const renderValidationDecorations = options.get(EditorOption.renderValidationDecorations);
if (renderValidationDecorations === 'editable') {
return options.get(EditorOption.readOnly);
}
return renderValidationDecorations === 'on' ? false : true;
}
//#endregion
//#region rulers
class EditorRulers extends SimpleEditorOption<EditorOption.rulers, number[]> {
@@ -3066,6 +3136,7 @@ export const enum EditorOption {
autoSurround,
codeLens,
colorDecorators,
comments,
contextmenu,
copyWithSyntaxHighlighting,
cursorBlinking,
@@ -3117,7 +3188,7 @@ export const enum EditorOption {
overviewRulerBorder,
overviewRulerLanes,
parameterHints,
peekWidgetFocusInlineEditor,
peekWidgetDefaultFocus,
quickSuggestions,
quickSuggestionsDelay,
readOnly,
@@ -3125,6 +3196,7 @@ export const enum EditorOption {
renderIndentGuides,
renderFinalNewline,
renderLineHighlight,
renderValidationDecorations,
renderWhitespace,
revealHorizontalRightPadding,
roundedSelection,
@@ -3169,6 +3241,7 @@ export const enum EditorOption {
* WORKAROUND: TS emits "any" for complex editor options values (anything except string, bool, enum, etc. ends up being "any")
* @monacodtsreplace
* /accessibilitySupport, any/accessibilitySupport, AccessibilitySupport/
* /comments, any/comments, EditorCommentsOptions/
* /find, any/find, EditorFindOptions/
* /fontInfo, any/fontInfo, FontInfo/
* /gotoLocation, any/gotoLocation, GoToLocationOptions/
@@ -3285,6 +3358,7 @@ export const EditorOptions = {
EditorOption.colorDecorators, 'colorDecorators', true,
{ description: nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") }
)),
comments: register(new EditorComments()),
contextmenu: register(new EditorBooleanOption(
EditorOption.contextmenu, 'contextmenu', true,
)),
@@ -3494,9 +3568,17 @@ export const EditorOptions = {
3, 0, 3
)),
parameterHints: register(new EditorParameterHints()),
peekWidgetFocusInlineEditor: register(new EditorBooleanOption(
EditorOption.peekWidgetFocusInlineEditor, 'peekWidgetFocusInlineEditor', false,
{ description: nls.localize('peekWidgetFocusInlineEditor', "Controls whether to focus the inline editor in the peek widget by default.") }
peekWidgetDefaultFocus: register(new EditorStringEnumOption(
EditorOption.peekWidgetDefaultFocus, 'peekWidgetDefaultFocus',
'tree' as 'tree' | 'editor',
['tree', 'editor'] as const,
{
enumDescriptions: [
nls.localize('peekWidgetDefaultFocus.tree', "Focus the tree when openeing 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.")
}
)),
quickSuggestions: register(new EditorQuickSuggestions()),
quickSuggestionsDelay: register(new EditorIntOption(
@@ -3533,6 +3615,11 @@ export const EditorOptions = {
description: nls.localize('renderLineHighlight', "Controls how the editor should render the current line highlight.")
}
)),
renderValidationDecorations: register(new EditorStringEnumOption(
EditorOption.renderValidationDecorations, 'renderValidationDecorations',
'editable' as 'editable' | 'on' | 'off',
['editable', 'on', 'off'] as const
)),
renderWhitespace: register(new EditorStringEnumOption(
EditorOption.renderWhitespace, 'renderWhitespace',
'none' as 'none' | 'boundary' | 'selection' | 'all',
+4
View File
@@ -134,6 +134,7 @@ export class FontInfo extends BareFontInfo {
readonly typicalFullwidthCharacterWidth: number;
readonly canUseHalfwidthRightwardsArrow: boolean;
readonly spaceWidth: number;
readonly middotWidth: number;
readonly maxDigitWidth: number;
/**
@@ -152,6 +153,7 @@ export class FontInfo extends BareFontInfo {
typicalFullwidthCharacterWidth: number;
canUseHalfwidthRightwardsArrow: boolean;
spaceWidth: number;
middotWidth: number;
maxDigitWidth: number;
}, isTrusted: boolean) {
super(opts);
@@ -161,6 +163,7 @@ export class FontInfo extends BareFontInfo {
this.typicalFullwidthCharacterWidth = opts.typicalFullwidthCharacterWidth;
this.canUseHalfwidthRightwardsArrow = opts.canUseHalfwidthRightwardsArrow;
this.spaceWidth = opts.spaceWidth;
this.middotWidth = opts.middotWidth;
this.maxDigitWidth = opts.maxDigitWidth;
}
@@ -179,6 +182,7 @@ export class FontInfo extends BareFontInfo {
&& this.typicalFullwidthCharacterWidth === other.typicalFullwidthCharacterWidth
&& this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow
&& this.spaceWidth === other.spaceWidth
&& this.middotWidth === other.middotWidth
&& this.maxDigitWidth === other.maxDigitWidth
);
}
+1 -1
View File
@@ -545,7 +545,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// Let the view get the event first.
try {
const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewCursorStateChangedEvent(viewSelections));
eventsCollector.emit(new viewEvents.ViewCursorStateChangedEvent(viewSelections, selections));
} finally {
this._endEmit();
}
@@ -492,15 +492,20 @@ export class TypeOperations {
});
}
private static _autoClosingPairIsSymmetric(autoClosingPair: StandardAutoClosingPairConditional): boolean {
const { open, close } = autoClosingPair;
return (open.indexOf(close) >= 0 || close.indexOf(open) >= 0);
}
private static _isBeforeClosingBrace(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional, characterAfter: string) {
const otherAutoClosingPairs = config.autoClosingPairsClose2.get(characterAfter);
if (!otherAutoClosingPairs) {
return false;
}
const thisBraceIsSymmetric = (autoClosingPair.open === autoClosingPair.close);
const thisBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(autoClosingPair);
for (const otherAutoClosingPair of otherAutoClosingPairs) {
const otherBraceIsSymmetric = (otherAutoClosingPair.open === otherAutoClosingPair.close);
const otherBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(otherAutoClosingPair);
if (!thisBraceIsSymmetric && otherBraceIsSymmetric) {
continue;
}
@@ -72,11 +72,12 @@ function spacesDiff(a: string, aLength: number, b: string, bLength: number, resu
if (spacesDiff > 0 && 0 <= bSpacesCnt - 1 && bSpacesCnt - 1 < a.length && bSpacesCnt < b.length) {
if (b.charCodeAt(bSpacesCnt) !== CharCode.Space && a.charCodeAt(bSpacesCnt - 1) === CharCode.Space) {
// This looks like an alignment desire: e.g.
// const a = b + c,
// d = b - c;
result.looksLikeAlignment = true;
if (a.charCodeAt(a.length - 1) === CharCode.Comma) {
// This looks like an alignment desire: e.g.
// const a = b + c,
// d = b - c;
result.looksLikeAlignment = true;
}
}
}
return;
+20 -1
View File
@@ -1947,6 +1947,7 @@ export class TextModel extends Disposable implements model.ITextModel {
private _matchBracket(position: Position): [Range, Range] | null {
const lineNumber = position.lineNumber;
const lineTokens = this._getLineTokens(lineNumber);
const tokenCount = lineTokens.getCount();
const lineText = this._buffer.getLineContent(lineNumber);
const tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
@@ -1959,6 +1960,15 @@ export class TextModel extends Disposable implements model.ITextModel {
if (currentModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex))) {
// limit search to not go before `maxBracketLength`
let searchStartOffset = Math.max(0, position.column - 1 - currentModeBrackets.maxBracketLength);
for (let i = tokenIndex - 1; i >= 0; i--) {
const tokenEndOffset = lineTokens.getEndOffset(i);
if (tokenEndOffset <= searchStartOffset) {
break;
}
if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i))) {
searchStartOffset = tokenEndOffset;
}
}
// limit search to not go after `maxBracketLength`
const searchEndOffset = Math.min(lineText.length, position.column - 1 + currentModeBrackets.maxBracketLength);
@@ -1998,7 +2008,16 @@ export class TextModel extends Disposable implements model.ITextModel {
if (prevModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(prevTokenIndex))) {
// limit search in case previous token is very large, there's no need to go beyond `maxBracketLength`
const searchStartOffset = Math.max(0, position.column - 1 - prevModeBrackets.maxBracketLength);
const searchEndOffset = Math.min(lineText.length, position.column - 1 + prevModeBrackets.maxBracketLength);
let searchEndOffset = Math.min(lineText.length, position.column - 1 + prevModeBrackets.maxBracketLength);
for (let i = prevTokenIndex + 1; i < tokenCount; i++) {
const tokenStartOffset = lineTokens.getStartOffset(i);
if (tokenStartOffset >= searchEndOffset) {
break;
}
if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i))) {
searchEndOffset = tokenStartOffset;
}
}
const foundBracket = BracketsUtils.findPrevBracketInRange(prevModeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
// check that we didn't hit a bracket too far away from position
+1 -1
View File
@@ -804,7 +804,7 @@ export class TokensStore2 {
aIndex++;
}
const aMetadata = aTokens.getMetadata(aIndex - 1 > 0 ? aIndex - 1 : aIndex);
const aMetadata = aTokens.getMetadata(Math.min(Math.max(0, aIndex - 1), aLen - 1));
const languageId = TokenMetadata.getLanguageId(aMetadata);
const tokenType = TokenMetadata.getTokenType(aMetadata);
+42 -5
View File
@@ -369,6 +369,28 @@ export let completionKindFromString: {
};
})();
export interface CompletionItemLabel {
/**
* The function or variable. Rendered leftmost.
*/
name: string;
/**
* The signature without the return type. Render after `name`.
*/
signature?: string;
/**
* The fully qualified name, like package name or file path. Rendered after `signature`.
*/
qualifier?: string;
/**
* The return-type of a function or type of a property/variable. Rendered rightmost.
*/
type?: string;
}
export const enum CompletionItemTag {
Deprecated = 1
}
@@ -396,7 +418,7 @@ export interface CompletionItem {
* this is also the text that is inserted when selecting
* this completion.
*/
label: string;
label: string | CompletionItemLabel;
/**
* The kind of this completion item. Based on the kind
* an icon is chosen by the editor.
@@ -481,7 +503,6 @@ export interface CompletionItem {
export interface CompletionList {
suggestions: CompletionItem[];
incomplete?: boolean;
isDetailsResolved?: boolean;
dispose?(): void;
}
@@ -1257,20 +1278,36 @@ export namespace WorkspaceTextEdit {
* @internal
*/
export function is(thing: any): thing is WorkspaceTextEdit {
return isObject(thing) && (<WorkspaceTextEdit>thing).resource && Array.isArray((<WorkspaceTextEdit>thing).edits);
return isObject(thing) && URI.isUri((<WorkspaceTextEdit>thing).resource) && isObject((<WorkspaceTextEdit>thing).edit);
}
}
export interface WorkspaceEditMetadata {
needsConfirmation: boolean;
label: string;
description?: string;
iconPath?: { id: string } | { light: URI, dark: URI };
}
export interface WorkspaceFileEditOptions {
overwrite?: boolean;
ignoreIfNotExists?: boolean;
ignoreIfExists?: boolean;
recursive?: boolean;
}
export interface WorkspaceFileEdit {
oldUri?: URI;
newUri?: URI;
options?: { overwrite?: boolean, ignoreIfNotExists?: boolean, ignoreIfExists?: boolean, recursive?: boolean };
options?: WorkspaceFileEditOptions;
metadata?: WorkspaceEditMetadata;
}
export interface WorkspaceTextEdit {
resource: URI;
edit: TextEdit;
modelVersionId?: number;
edits: TextEdit[];
metadata?: WorkspaceEditMetadata;
}
export interface WorkspaceEdit {
@@ -268,6 +268,10 @@ export class LinkComputer {
// `*` terminates a link if the link began with `*`
chClass = (linkBeginChCode === CharCode.Asterisk) ? CharacterClass.ForceTermination : CharacterClass.None;
break;
case CharCode.Pipe:
// `|` terminates a link if the link began with `|`
chClass = (linkBeginChCode === CharCode.Pipe) ? CharacterClass.ForceTermination : CharacterClass.None;
break;
default:
chClass = classifier.get(chCode);
}
+5
View File
@@ -62,6 +62,11 @@ export class ScopedLineTokens {
return actualLineContent.substring(this.firstCharOffset, this._lastCharOffset);
}
public getActualLineContentBefore(offset: number): string {
const actualLineContent = this._actual.getLineContent();
return actualLineContent.substring(0, this.firstCharOffset + offset);
}
public getTokenCount(): number {
return this._lastTokenIndex - this._firstTokenIndex;
}
@@ -49,28 +49,27 @@ export class BracketElectricCharacterSupport {
return null;
}
let tokenIndex = context.findTokenIndexAtOffset(column - 1);
const tokenIndex = context.findTokenIndexAtOffset(column - 1);
if (ignoreBracketsInToken(context.getStandardTokenType(tokenIndex))) {
return null;
}
let reversedBracketRegex = this._richEditBrackets.reversedRegex;
let text = context.getLineContent().substring(0, column - 1) + character;
const reversedBracketRegex = this._richEditBrackets.reversedRegex;
const text = context.getLineContent().substring(0, column - 1) + character;
let r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, 1, text, 0, text.length);
const r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, 1, text, 0, text.length);
if (!r) {
return null;
}
let bracketText = text.substring(r.startColumn - 1, r.endColumn - 1);
bracketText = bracketText.toLowerCase();
const bracketText = text.substring(r.startColumn - 1, r.endColumn - 1).toLowerCase();
let isOpen = this._richEditBrackets.textIsOpenBracket[bracketText];
const isOpen = this._richEditBrackets.textIsOpenBracket[bracketText];
if (isOpen) {
return null;
}
let textBeforeBracket = text.substring(0, r.startColumn - 1);
const textBeforeBracket = context.getActualLineContentBefore(r.startColumn - 1);
if (!/^\s*$/.test(textBeforeBracket)) {
// There is other text on the line before the bracket
return null;
@@ -147,12 +147,10 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor
let ret = Range.lift(rawMarker);
if (rawMarker.severity === MarkerSeverity.Hint) {
if (!rawMarker.tags || rawMarker.tags.indexOf(MarkerTag.Unnecessary) === -1) {
// * never render hints on multiple lines
// * make enough space for three dots
ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2);
}
if (rawMarker.severity === MarkerSeverity.Hint && !this._hasMarkerTag(rawMarker, MarkerTag.Unnecessary) && !this._hasMarkerTag(rawMarker, MarkerTag.Deprecated)) {
// * never render hints on multiple lines
// * make enough space for three dots
ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2);
}
ret = model.validateRange(ret);
@@ -188,7 +186,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor
private _createDecorationOption(marker: IMarker): IModelDecorationOptions {
let className: string;
let className: string | undefined;
let color: ThemeColor | undefined = undefined;
let zIndex: number;
let inlineClassName: string | undefined = undefined;
@@ -196,7 +194,9 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor
switch (marker.severity) {
case MarkerSeverity.Hint:
if (marker.tags && marker.tags.indexOf(MarkerTag.Unnecessary) >= 0) {
if (this._hasMarkerTag(marker, MarkerTag.Deprecated)) {
className = undefined;
} else if (this._hasMarkerTag(marker, MarkerTag.Unnecessary)) {
className = ClassName.EditorUnnecessaryDecoration;
} else {
className = ClassName.EditorHintDecoration;
@@ -251,4 +251,11 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor
inlineClassName,
};
}
private _hasMarkerTag(marker: IMarker, tag: MarkerTag): boolean {
if (marker.tags) {
return marker.tags.indexOf(tag) >= 0;
}
return false;
}
}
@@ -178,101 +178,103 @@ export enum EditorOption {
autoSurround = 10,
codeLens = 11,
colorDecorators = 12,
contextmenu = 13,
copyWithSyntaxHighlighting = 14,
cursorBlinking = 15,
cursorSmoothCaretAnimation = 16,
cursorStyle = 17,
cursorSurroundingLines = 18,
cursorSurroundingLinesStyle = 19,
cursorWidth = 20,
disableLayerHinting = 21,
disableMonospaceOptimizations = 22,
dragAndDrop = 23,
emptySelectionClipboard = 24,
extraEditorClassName = 25,
fastScrollSensitivity = 26,
find = 27,
fixedOverflowWidgets = 28,
folding = 29,
foldingStrategy = 30,
foldingHighlight = 31,
fontFamily = 32,
fontInfo = 33,
fontLigatures = 34,
fontSize = 35,
fontWeight = 36,
formatOnPaste = 37,
formatOnType = 38,
glyphMargin = 39,
gotoLocation = 40,
hideCursorInOverviewRuler = 41,
highlightActiveIndentGuide = 42,
hover = 43,
inDiffEditor = 44,
letterSpacing = 45,
lightbulb = 46,
lineDecorationsWidth = 47,
lineHeight = 48,
lineNumbers = 49,
lineNumbersMinChars = 50,
links = 51,
matchBrackets = 52,
minimap = 53,
mouseStyle = 54,
mouseWheelScrollSensitivity = 55,
mouseWheelZoom = 56,
multiCursorMergeOverlapping = 57,
multiCursorModifier = 58,
multiCursorPaste = 59,
occurrencesHighlight = 60,
overviewRulerBorder = 61,
overviewRulerLanes = 62,
parameterHints = 63,
peekWidgetFocusInlineEditor = 64,
quickSuggestions = 65,
quickSuggestionsDelay = 66,
readOnly = 67,
renderControlCharacters = 68,
renderIndentGuides = 69,
renderFinalNewline = 70,
renderLineHighlight = 71,
renderWhitespace = 72,
revealHorizontalRightPadding = 73,
roundedSelection = 74,
rulers = 75,
scrollbar = 76,
scrollBeyondLastColumn = 77,
scrollBeyondLastLine = 78,
selectionClipboard = 79,
selectionHighlight = 80,
selectOnLineNumbers = 81,
semanticHighlighting = 82,
showFoldingControls = 83,
showUnused = 84,
snippetSuggestions = 85,
smoothScrolling = 86,
stopRenderingLineAfter = 87,
suggest = 88,
suggestFontSize = 89,
suggestLineHeight = 90,
suggestOnTriggerCharacters = 91,
suggestSelection = 92,
tabCompletion = 93,
useTabStops = 94,
wordSeparators = 95,
wordWrap = 96,
wordWrapBreakAfterCharacters = 97,
wordWrapBreakBeforeCharacters = 98,
wordWrapColumn = 99,
wordWrapMinified = 100,
wrappingIndent = 101,
wrappingAlgorithm = 102,
editorClassName = 103,
pixelRatio = 104,
tabFocusMode = 105,
layoutInfo = 106,
wrappingInfo = 107
comments = 13,
contextmenu = 14,
copyWithSyntaxHighlighting = 15,
cursorBlinking = 16,
cursorSmoothCaretAnimation = 17,
cursorStyle = 18,
cursorSurroundingLines = 19,
cursorSurroundingLinesStyle = 20,
cursorWidth = 21,
disableLayerHinting = 22,
disableMonospaceOptimizations = 23,
dragAndDrop = 24,
emptySelectionClipboard = 25,
extraEditorClassName = 26,
fastScrollSensitivity = 27,
find = 28,
fixedOverflowWidgets = 29,
folding = 30,
foldingStrategy = 31,
foldingHighlight = 32,
fontFamily = 33,
fontInfo = 34,
fontLigatures = 35,
fontSize = 36,
fontWeight = 37,
formatOnPaste = 38,
formatOnType = 39,
glyphMargin = 40,
gotoLocation = 41,
hideCursorInOverviewRuler = 42,
highlightActiveIndentGuide = 43,
hover = 44,
inDiffEditor = 45,
letterSpacing = 46,
lightbulb = 47,
lineDecorationsWidth = 48,
lineHeight = 49,
lineNumbers = 50,
lineNumbersMinChars = 51,
links = 52,
matchBrackets = 53,
minimap = 54,
mouseStyle = 55,
mouseWheelScrollSensitivity = 56,
mouseWheelZoom = 57,
multiCursorMergeOverlapping = 58,
multiCursorModifier = 59,
multiCursorPaste = 60,
occurrencesHighlight = 61,
overviewRulerBorder = 62,
overviewRulerLanes = 63,
parameterHints = 64,
peekWidgetDefaultFocus = 65,
quickSuggestions = 66,
quickSuggestionsDelay = 67,
readOnly = 68,
renderControlCharacters = 69,
renderIndentGuides = 70,
renderFinalNewline = 71,
renderLineHighlight = 72,
renderValidationDecorations = 73,
renderWhitespace = 74,
revealHorizontalRightPadding = 75,
roundedSelection = 76,
rulers = 77,
scrollbar = 78,
scrollBeyondLastColumn = 79,
scrollBeyondLastLine = 80,
selectionClipboard = 81,
selectionHighlight = 82,
selectOnLineNumbers = 83,
semanticHighlighting = 84,
showFoldingControls = 85,
showUnused = 86,
snippetSuggestions = 87,
smoothScrolling = 88,
stopRenderingLineAfter = 89,
suggest = 90,
suggestFontSize = 91,
suggestLineHeight = 92,
suggestOnTriggerCharacters = 93,
suggestSelection = 94,
tabCompletion = 95,
useTabStops = 96,
wordSeparators = 97,
wordWrap = 98,
wordWrapBreakAfterCharacters = 99,
wordWrapBreakBeforeCharacters = 100,
wordWrapColumn = 101,
wordWrapMinified = 102,
wrappingIndent = 103,
wrappingAlgorithm = 104,
editorClassName = 105,
pixelRatio = 106,
tabFocusMode = 107,
layoutInfo = 108,
wrappingInfo = 109
}
/**
+3 -4
View File
@@ -69,13 +69,12 @@ export class ViewCursorStateChangedEvent {
public readonly type = ViewEventType.ViewCursorStateChanged;
/**
* The primary selection is always at index 0.
*/
public readonly selections: Selection[];
public readonly modelSelections: Selection[];
constructor(selections: Selection[]) {
constructor(selections: Selection[], modelSelections: Selection[]) {
this.selections = selections;
this.modelSelections = modelSelections;
}
}
@@ -68,6 +68,7 @@ export class RenderLineInput {
public readonly tabSize: number;
public readonly startVisibleColumn: number;
public readonly spaceWidth: number;
public readonly middotWidth: number;
public readonly stopRenderingLineAfter: number;
public readonly renderWhitespace: RenderWhitespace;
public readonly renderControlCharacters: boolean;
@@ -92,6 +93,7 @@ export class RenderLineInput {
tabSize: number,
startVisibleColumn: number,
spaceWidth: number,
middotWidth: number,
stopRenderingLineAfter: number,
renderWhitespace: 'none' | 'boundary' | 'selection' | 'all',
renderControlCharacters: boolean,
@@ -110,6 +112,7 @@ export class RenderLineInput {
this.tabSize = tabSize;
this.startVisibleColumn = startVisibleColumn;
this.spaceWidth = spaceWidth;
this.middotWidth = middotWidth;
this.stopRenderingLineAfter = stopRenderingLineAfter;
this.renderWhitespace = (
renderWhitespace === 'all'
@@ -380,6 +383,7 @@ class ResolvedRenderLineInput {
public readonly startVisibleColumn: number,
public readonly containsRTL: boolean,
public readonly spaceWidth: number,
public readonly middotWidth: number,
public readonly renderWhitespace: RenderWhitespace,
public readonly renderControlCharacters: boolean,
) {
@@ -439,6 +443,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
input.startVisibleColumn,
input.containsRTL,
input.spaceWidth,
input.middotWidth,
input.renderWhitespace,
input.renderControlCharacters
);
@@ -734,9 +739,13 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
const startVisibleColumn = input.startVisibleColumn;
const containsRTL = input.containsRTL;
const spaceWidth = input.spaceWidth;
const middotWidth = input.middotWidth;
const renderWhitespace = input.renderWhitespace;
const renderControlCharacters = input.renderControlCharacters;
// use U+2E31 - WORD SEPARATOR MIDDLE DOT or U+00B7 - MIDDLE DOT
const spaceRenderWhitespaceCharacter = (middotWidth > spaceWidth ? 0x2E31 : 0xB7);
const characterMapping = new CharacterMapping(len + 1, parts.length);
let charIndex = 0;
@@ -808,7 +817,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
} else { // must be CharCode.Space
charWidth = 1;
sb.write1(0xB7); // &middot;
sb.write1(spaceRenderWhitespaceCharacter); // &middot; or word separator middle dot
}
charOffsetInPart += charWidth;
+2 -2
View File
@@ -138,8 +138,8 @@ export interface IViewModel {
deduceModelPositionRelativeToViewPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position;
getEOL(): string;
getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[];
getHTMLToCopy(ranges: Range[], emptySelectionClipboard: boolean): string | null;
getPlainTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[];
getRichTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): { html: string, mode: string } | null;
}
export class MinimapLinesRenderingData {
@@ -10,7 +10,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { IModelDecoration, ITextModel } from 'vs/editor/common/model';
import { IViewModelLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection';
import { ICoordinatesConverter, InlineDecoration, InlineDecorationType, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { filterValidationDecorations } from 'vs/editor/common/config/editorOptions';
export interface IDecorationsViewportData {
/**
@@ -104,7 +104,7 @@ export class ViewModelDecorations implements IDisposable {
}
private _getDecorationsViewportData(viewportRange: Range): IDecorationsViewportData {
const modelDecorations = this._linesCollection.getDecorationsInRange(viewportRange, this.editorId, this.configuration.options.get(EditorOption.readOnly));
const modelDecorations = this._linesCollection.getDecorationsInRange(viewportRange, this.editorId, filterValidationDecorations(this.configuration.options));
const startLineNumber = viewportRange.startLineNumber;
const endLineNumber = viewportRange.endLineNumber;
+36 -35
View File
@@ -6,7 +6,7 @@
import { Color } from 'vs/base/common/color';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as strings from 'vs/base/common/strings';
import { ConfigurationChangedEvent, EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions';
import { ConfigurationChangedEvent, EDITOR_FONT_DEFAULTS, EditorOption, filterValidationDecorations } from 'vs/editor/common/config/editorOptions';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { IConfiguration, IViewState } from 'vs/editor/common/editorCommon';
@@ -596,7 +596,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
}
public getAllOverviewRulerDecorations(theme: ITheme): IOverviewRulerDecorations {
return this.lines.getAllOverviewRulerDecorations(this.editorId, this.configuration.options.get(EditorOption.readOnly), theme);
return this.lines.getAllOverviewRulerDecorations(this.editorId, filterValidationDecorations(this.configuration.options), theme);
}
public invalidateOverviewRulerColorCache(): void {
@@ -656,15 +656,15 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
return this.model.getEOL();
}
public getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[] {
public getPlainTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[] {
const newLineCharacter = forceCRLF ? '\r\n' : this.model.getEOL();
ranges = ranges.slice(0);
ranges.sort(Range.compareRangesUsingStarts);
modelRanges = modelRanges.slice(0);
modelRanges.sort(Range.compareRangesUsingStarts);
let hasEmptyRange = false;
let hasNonEmptyRange = false;
for (const range of ranges) {
for (const range of modelRanges) {
if (range.isEmpty()) {
hasEmptyRange = true;
} else {
@@ -678,10 +678,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
return '';
}
const modelLineNumbers = ranges.map((r) => {
const viewLineStart = new Position(r.startLineNumber, 1);
return this.coordinatesConverter.convertViewPositionToModelPosition(viewLineStart).lineNumber;
});
const modelLineNumbers = modelRanges.map((r) => r.startLineNumber);
let result = '';
for (let i = 0; i < modelLineNumbers.length; i++) {
@@ -697,14 +694,14 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
// mixed empty selections and non-empty selections
let result: string[] = [];
let prevModelLineNumber = 0;
for (const range of ranges) {
const modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
if (range.isEmpty()) {
for (const modelRange of modelRanges) {
const modelLineNumber = modelRange.startLineNumber;
if (modelRange.isEmpty()) {
if (modelLineNumber !== prevModelLineNumber) {
result.push(this.model.getLineContent(modelLineNumber));
}
} else {
result.push(this.getValueInRange(range, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
result.push(this.model.getValueInRange(modelRange, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
}
prevModelLineNumber = modelLineNumber;
}
@@ -712,31 +709,32 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
}
let result: string[] = [];
for (const range of ranges) {
if (!range.isEmpty()) {
result.push(this.getValueInRange(range, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
for (const modelRange of modelRanges) {
if (!modelRange.isEmpty()) {
result.push(this.model.getValueInRange(modelRange, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
}
}
return result.length === 1 ? result[0] : result;
}
public getHTMLToCopy(viewRanges: Range[], emptySelectionClipboard: boolean): string | null {
if (this.model.getLanguageIdentifier().id === LanguageId.PlainText) {
public getRichTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): { html: string, mode: string } | null {
const languageId = this.model.getLanguageIdentifier();
if (languageId.id === LanguageId.PlainText) {
return null;
}
if (viewRanges.length !== 1) {
if (modelRanges.length !== 1) {
// no multiple selection support at this time
return null;
}
let range = this.coordinatesConverter.convertViewRangeToModelRange(viewRanges[0]);
let range = modelRanges[0];
if (range.isEmpty()) {
if (!emptySelectionClipboard) {
// nothing to copy
return null;
}
let lineNumber = range.startLineNumber;
const lineNumber = range.startLineNumber;
range = new Range(lineNumber, this.model.getLineMinColumn(lineNumber), lineNumber, this.model.getLineMaxColumn(lineNumber));
}
@@ -744,19 +742,22 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
const colorMap = this._getColorMap();
const fontFamily = fontInfo.fontFamily === EDITOR_FONT_DEFAULTS.fontFamily ? fontInfo.fontFamily : `'${fontInfo.fontFamily}', ${EDITOR_FONT_DEFAULTS.fontFamily}`;
return (
`<div style="`
+ `color: ${colorMap[ColorId.DefaultForeground]};`
+ `background-color: ${colorMap[ColorId.DefaultBackground]};`
+ `font-family: ${fontFamily};`
+ `font-weight: ${fontInfo.fontWeight};`
+ `font-size: ${fontInfo.fontSize}px;`
+ `line-height: ${fontInfo.lineHeight}px;`
+ `white-space: pre;`
+ `">`
+ this._getHTMLToCopy(range, colorMap)
+ '</div>'
);
return {
mode: languageId.language,
html: (
`<div style="`
+ `color: ${colorMap[ColorId.DefaultForeground]};`
+ `background-color: ${colorMap[ColorId.DefaultBackground]};`
+ `font-family: ${fontFamily};`
+ `font-weight: ${fontInfo.fontWeight};`
+ `font-size: ${fontInfo.fontSize}px;`
+ `line-height: ${fontInfo.lineHeight}px;`
+ `white-space: pre;`
+ `">`
+ this._getHTMLToCopy(range, colorMap)
+ '</div>'
)
};
}
private _getHTMLToCopy(modelRange: Range, colorMap: string[]): string {
@@ -110,7 +110,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
// showings until mouse is released
this.hide();
const monitor = new GlobalMouseMoveMonitor<IStandardMouseMoveEventData>();
monitor.startMonitoring(e.buttons, standardMouseMoveMerger, () => { }, () => {
monitor.startMonitoring(<HTMLElement>e.target, e.buttons, standardMouseMoveMerger, () => { }, () => {
monitor.dispose();
});
}));
@@ -220,6 +220,43 @@ suite('CodeAction', () => {
}
});
test('getCodeActions no invoke a provider that has been excluded #84602', async function () {
const baseType = CodeActionKind.Refactor;
const subType = CodeActionKind.Refactor.append('sub');
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', staticCodeActionProvider(
{ title: 'a', kind: baseType.value }
)));
let didInvoke = false;
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', new class implements modes.CodeActionProvider {
providedCodeActionKinds = [subType.value];
provideCodeActions(): modes.ProviderResult<modes.CodeActionList> {
didInvoke = true;
return {
actions: [
{ title: 'x', kind: subType.value }
],
dispose: () => { }
};
}
}));
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), {
type: modes.CodeActionTriggerType.Auto, filter: {
include: baseType,
excludes: [subType],
}
}, CancellationToken.None);
assert.strictEqual(didInvoke, false);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
}
});
test('getCodeActions should not invoke code action providers filtered out by providedCodeActionKinds', async function () {
let wasInvoked = false;
const provider = new class implements modes.CodeActionProvider {
+18 -4
View File
@@ -58,6 +58,12 @@ export function mayIncludeActionsOfKind(filter: CodeActionFilter, providedKind:
return false;
}
if (filter.excludes) {
if (filter.excludes.some(exclude => excludesAction(providedKind, exclude, filter.include))) {
return false;
}
}
// Don't return source actions unless they are explicitly requested
if (!filter.includeSourceActions && CodeActionKind.Source.contains(providedKind)) {
return false;
@@ -77,10 +83,7 @@ export function filtersAction(filter: CodeActionFilter, action: CodeAction): boo
}
if (filter.excludes) {
if (actionKind && filter.excludes.some(exclude => {
// Excludes are overwritten by includes
return exclude.contains(actionKind) && (!filter.include || !filter.include.contains(actionKind));
})) {
if (actionKind && filter.excludes.some(exclude => excludesAction(actionKind, exclude, filter.include))) {
return false;
}
}
@@ -101,6 +104,17 @@ export function filtersAction(filter: CodeActionFilter, action: CodeAction): boo
return true;
}
function excludesAction(providedKind: CodeActionKind, exclude: CodeActionKind, include: CodeActionKind | undefined): boolean {
if (!exclude.contains(providedKind)) {
return false;
}
if (include && exclude.contains(include)) {
// The include is more specific, don't filter out
return false;
}
return true;
}
export interface CodeActionTrigger {
readonly type: CodeActionTriggerType;
readonly filter?: CodeActionFilter;
@@ -18,7 +18,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { createStyleSheet } from 'vs/base/browser/dom';
import * as dom from 'vs/base/browser/dom';
import { hash } from 'vs/base/common/hash';
export class CodeLensContribution implements IEditorContribution {
@@ -65,7 +65,11 @@ export class CodeLensContribution implements IEditorContribution {
this._onModelChange();
this._styleClassName = hash(this._editor.getId()).toString(16);
this._styleElement = createStyleSheet();
this._styleElement = dom.createStyleSheet(
dom.isInShadowDOM(this._editor.getContainerDomNode())
? this._editor.getContainerDomNode()
: undefined
);
this._updateLensStyle();
}
@@ -81,7 +85,13 @@ export class CodeLensContribution implements IEditorContribution {
const fontInfo = options.get(EditorOption.fontInfo);
const lineHeight = options.get(EditorOption.lineHeight);
const newStyle = `.monaco-editor .codelens-decoration.${this._styleClassName} { height: ${Math.round(lineHeight * 1.1)}px; line-height: ${lineHeight}px; font-size: ${Math.round(fontInfo.fontSize * 0.9)}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;}`;
const height = Math.round(lineHeight * 1.1);
const fontSize = Math.round(fontInfo.fontSize * 0.9);
const newStyle = `
.monaco-editor .codelens-decoration.${this._styleClassName} { height: ${height}px; line-height: ${lineHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;}
.monaco-editor .codelens-decoration.${this._styleClassName} > a > .codicon { line-height: ${lineHeight}px; font-size: ${fontSize}px; }
`;
this._styleElement.innerHTML = newStyle;
}
@@ -27,9 +27,8 @@
}
.monaco-editor .codelens-decoration .codicon {
line-height: inherit;
font-size: 110%;
vertical-align: inherit;
vertical-align: middle;
color: currentColor !important;
}
.monaco-editor .codelens-decoration > a:hover .codicon::before {
@@ -192,7 +192,7 @@ export class ColorDetector extends Disposable implements IEditorContribution {
border: 'solid 0.1em #eee'
}
}
});
}, undefined, this._editor);
}
newDecorationsTypes[key] = true;
@@ -163,7 +163,7 @@ class SaturationBox extends Disposable {
this.onDidChangePosition(e.offsetX, e.offsetY);
}
this.monitor.startMonitoring(e.buttons, standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null);
this.monitor.startMonitoring(<HTMLElement>e.target, e.buttons, standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null);
const mouseUpListener = dom.addDisposableGenericMouseUpListner(document, () => {
this._onColorFlushed.fire();
@@ -270,7 +270,7 @@ abstract class Strip extends Disposable {
this.onDidChangeTop(e.offsetY);
}
monitor.startMonitoring(e.buttons, standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null);
monitor.startMonitoring(<HTMLElement>e.target, e.buttons, standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null);
const mouseUpListener = dom.addDisposableGenericMouseUpListner(document, () => {
this._onColorFlushed.fire();
@@ -15,10 +15,12 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo
export class BlockCommentCommand implements ICommand {
private readonly _selection: Selection;
private readonly _insertSpace: boolean;
private _usedEndToken: string | null;
constructor(selection: Selection) {
constructor(selection: Selection, insertSpace: boolean) {
this._selection = selection;
this._insertSpace = insertSpace;
this._usedEndToken = null;
}
@@ -53,7 +55,7 @@ export class BlockCommentCommand implements ICommand {
return true;
}
private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, model: ITextModel, builder: IEditOperationBuilder): void {
private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, insertSpace: boolean, model: ITextModel, builder: IEditOperationBuilder): void {
const startLineNumber = selection.startLineNumber;
const startColumn = selection.startColumn;
const endLineNumber = selection.endLineNumber;
@@ -91,25 +93,21 @@ export class BlockCommentCommand implements ICommand {
if (startTokenIndex !== -1 && endTokenIndex !== -1) {
// Consider spaces as part of the comment tokens
if (startTokenIndex + startToken.length < startLineText.length) {
if (startLineText.charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) {
// Pretend the start token contains a trailing space
startToken = startToken + ' ';
}
if (insertSpace && startTokenIndex + startToken.length < startLineText.length && startLineText.charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) {
// Pretend the start token contains a trailing space
startToken = startToken + ' ';
}
if (endTokenIndex > 0) {
if (endLineText.charCodeAt(endTokenIndex - 1) === CharCode.Space) {
// Pretend the end token contains a leading space
endToken = ' ' + endToken;
endTokenIndex -= 1;
}
if (insertSpace && endTokenIndex > 0 && endLineText.charCodeAt(endTokenIndex - 1) === CharCode.Space) {
// Pretend the end token contains a leading space
endToken = ' ' + endToken;
endTokenIndex -= 1;
}
ops = BlockCommentCommand._createRemoveBlockCommentOperations(
new Range(startLineNumber, startTokenIndex + startToken.length + 1, endLineNumber, endTokenIndex + 1), startToken, endToken
);
} else {
ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken);
ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken, this._insertSpace);
this._usedEndToken = ops.length === 1 ? endToken : null;
}
@@ -144,15 +142,15 @@ export class BlockCommentCommand implements ICommand {
return res;
}
public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string): IIdentifiedSingleEditOperation[] {
public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string, insertSpace: boolean): IIdentifiedSingleEditOperation[] {
let res: IIdentifiedSingleEditOperation[] = [];
if (!Range.isEmpty(r)) {
// Insert block comment start
res.push(EditOperation.insert(new Position(r.startLineNumber, r.startColumn), startToken + ' '));
res.push(EditOperation.insert(new Position(r.startLineNumber, r.startColumn), startToken + (insertSpace ? ' ' : '')));
// Insert block comment end
res.push(EditOperation.insert(new Position(r.endLineNumber, r.endColumn), ' ' + endToken));
res.push(EditOperation.insert(new Position(r.endLineNumber, r.endColumn), (insertSpace ? ' ' : '') + endToken));
} else {
// Insert both continuously
res.push(EditOperation.replace(new Range(
@@ -176,7 +174,7 @@ export class BlockCommentCommand implements ICommand {
return;
}
this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, model, builder);
this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, this._insertSpace, model, builder);
}
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
+11 -8
View File
@@ -12,6 +12,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand';
import { LineCommentCommand, Type } from 'vs/editor/contrib/comment/lineCommentCommand';
// import { MenuId } from 'vs/platform/actions/common/actions';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
abstract class CommentLineAction extends EditorAction {
@@ -28,13 +29,14 @@ abstract class CommentLineAction extends EditorAction {
return;
}
let model = editor.getModel();
let commands: ICommand[] = [];
let selections = editor.getSelections();
let opts = model.getOptions();
const model = editor.getModel();
const commands: ICommand[] = [];
const selections = editor.getSelections();
const modelOptions = model.getOptions();
const commentsOptions = editor.getOption(EditorOption.comments);
for (const selection of selections) {
commands.push(new LineCommentCommand(selection, opts.tabSize, this._type));
commands.push(new LineCommentCommand(selection, modelOptions.tabSize, this._type, commentsOptions.insertSpace));
}
editor.pushUndoStop();
@@ -126,10 +128,11 @@ class BlockCommentAction extends EditorAction {
return;
}
let commands: ICommand[] = [];
let selections = editor.getSelections();
const commentsOptions = editor.getOption(EditorOption.comments);
const commands: ICommand[] = [];
const selections = editor.getSelections();
for (const selection of selections) {
commands.push(new BlockCommentCommand(selection));
commands.push(new BlockCommentCommand(selection, commentsOptions.insertSpace));
}
editor.pushUndoStop();
@@ -50,17 +50,19 @@ export const enum Type {
export class LineCommentCommand implements ICommand {
private readonly _selection: Selection;
private readonly _tabSize: number;
private readonly _type: Type;
private readonly _insertSpace: boolean;
private _selectionId: string | null;
private _deltaColumn: number;
private _moveEndPositionDown: boolean;
private readonly _tabSize: number;
private readonly _type: Type;
constructor(selection: Selection, tabSize: number, type: Type) {
constructor(selection: Selection, tabSize: number, type: Type, insertSpace: boolean) {
this._selection = selection;
this._selectionId = null;
this._tabSize = tabSize;
this._type = type;
this._insertSpace = insertSpace;
this._selectionId = null;
this._deltaColumn = 0;
this._moveEndPositionDown = false;
}
@@ -98,7 +100,7 @@ export class LineCommentCommand implements ICommand {
* Analyze lines and decide which lines are relevant and what the toggle should do.
* Also, build up several offsets and lengths useful in the generation of editor operations.
*/
public static _analyzeLines(type: Type, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number): IPreflightData {
public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number): IPreflightData {
let onlyWhitespaceLines = true;
let shouldRemoveComments: boolean;
@@ -145,7 +147,8 @@ export class LineCommentCommand implements ICommand {
}
}
if (shouldRemoveComments) {
if (shouldRemoveComments && insertSpace) {
// Remove a following space if present
const commentStrEndOffset = lineContentStartOffset + lineData.commentStrLength;
if (commentStrEndOffset < lineContent.length && lineContent.charCodeAt(commentStrEndOffset) === CharCode.Space) {
lineData.commentStrLength += 1;
@@ -173,7 +176,7 @@ export class LineCommentCommand implements ICommand {
/**
* Analyze all lines and decide exactly what to do => not supported | insert line comments | remove line comments
*/
public static _gatherPreflightData(type: Type, model: ITextModel, startLineNumber: number, endLineNumber: number): IPreflightData {
public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number): IPreflightData {
const lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber);
if (lines === null) {
return {
@@ -181,7 +184,7 @@ export class LineCommentCommand implements ICommand {
};
}
return LineCommentCommand._analyzeLines(type, model, lines, startLineNumber);
return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber);
}
/**
@@ -195,7 +198,7 @@ export class LineCommentCommand implements ICommand {
ops = LineCommentCommand._createRemoveLineCommentsOperations(data.lines, s.startLineNumber);
} else {
LineCommentCommand._normalizeInsertionPoint(model, data.lines, s.startLineNumber, this._tabSize);
ops = LineCommentCommand._createAddLineCommentsOperations(data.lines, s.startLineNumber);
ops = this._createAddLineCommentsOperations(data.lines, s.startLineNumber);
}
const cursorPosition = new Position(s.positionLineNumber, s.positionColumn);
@@ -288,11 +291,17 @@ export class LineCommentCommand implements ICommand {
firstNonWhitespaceIndex = lineContent.length;
}
ops = BlockCommentCommand._createAddBlockCommentOperations(
new Range(s.startLineNumber, firstNonWhitespaceIndex + 1, s.startLineNumber, lineContent.length + 1), startToken, endToken
new Range(s.startLineNumber, firstNonWhitespaceIndex + 1, s.startLineNumber, lineContent.length + 1),
startToken,
endToken,
this._insertSpace
);
} else {
ops = BlockCommentCommand._createAddBlockCommentOperations(
new Range(s.startLineNumber, model.getLineFirstNonWhitespaceColumn(s.startLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), startToken, endToken
new Range(s.startLineNumber, model.getLineFirstNonWhitespaceColumn(s.startLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)),
startToken,
endToken,
this._insertSpace
);
}
@@ -317,7 +326,7 @@ export class LineCommentCommand implements ICommand {
s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1));
}
const data = LineCommentCommand._gatherPreflightData(this._type, model, s.startLineNumber, s.endLineNumber);
const data = LineCommentCommand._gatherPreflightData(this._type, this._insertSpace, model, s.startLineNumber, s.endLineNumber);
if (data.supported) {
return this._executeLineComments(model, builder, data, s);
}
@@ -365,8 +374,10 @@ export class LineCommentCommand implements ICommand {
/**
* Generate edit operations in the add line comment case
*/
public static _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] {
private _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] {
let res: IIdentifiedSingleEditOperation[] = [];
const afterCommentStr = this._insertSpace ? ' ' : '';
for (let i = 0, len = lines.length; i < len; i++) {
const lineData = lines[i];
@@ -375,7 +386,7 @@ export class LineCommentCommand implements ICommand {
continue;
}
res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + ' '));
res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + afterCommentStr));
}
return res;
@@ -9,7 +9,7 @@ import { CommentMode } from 'vs/editor/test/common/commentMode';
function testBlockCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, true), expectedLines, expectedSelection);
mode.dispose();
}
@@ -468,4 +468,45 @@ suite('Editor Contrib - Block Comment Command', () => {
new Selection(1, 1, 1, 1)
);
});
test('', () => {
});
test('insertSpace false', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, false), expectedLines, expectedSelection);
mode.dispose();
}
testLineCommentCommand(
[
'some text'
],
new Selection(1, 1, 1, 5),
[
'<0some0> text'
],
new Selection(1, 3, 1, 7)
);
});
test('insertSpace false does not remove space', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, false), expectedLines, expectedSelection);
mode.dispose();
}
testLineCommentCommand(
[
'<0 some 0> text'
],
new Selection(1, 4, 1, 8),
[
' some text'
],
new Selection(1, 1, 1, 7)
);
});
});
@@ -2,6 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Selection } from 'vs/editor/common/core/selection';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
@@ -18,13 +19,13 @@ suite('Editor Contrib - Line Comment Command', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<!@#', '#@!>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection);
mode.dispose();
}
function testAddLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<!@#', '#@!>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd, true), expectedLines, expectedSelection);
mode.dispose();
}
@@ -46,7 +47,7 @@ suite('Editor Contrib - Line Comment Command', () => {
test('case insensitive', function () {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: 'rem' });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection);
mode.dispose();
}
@@ -85,7 +86,7 @@ suite('Editor Contrib - Line Comment Command', () => {
test('_analyzeLines', () => {
let r: IPreflightData;
r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([
r = LineCommentCommand._analyzeLines(Type.Toggle, true, createSimpleModel([
'\t\t',
' ',
' c',
@@ -116,7 +117,7 @@ suite('Editor Contrib - Line Comment Command', () => {
assert.equal(r.lines[3].commentStrOffset, 2);
r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([
r = LineCommentCommand._analyzeLines(Type.Toggle, true, createSimpleModel([
'\t\t',
' rem ',
' !@# c',
@@ -626,13 +627,51 @@ suite('Editor Contrib - Line Comment Command', () => {
new Selection(2, 11, 1, 1)
);
});
test('insertSpace false', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#' });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false), expectedLines, expectedSelection);
mode.dispose();
}
testLineCommentCommand(
[
'some text'
],
new Selection(1, 1, 1, 1),
[
'!@#some text'
],
new Selection(1, 4, 1, 4)
);
});
test('insertSpace false does not remove space', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#' });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false), expectedLines, expectedSelection);
mode.dispose();
}
testLineCommentCommand(
[
'!@# some text'
],
new Selection(1, 1, 1, 1),
[
' some text'
],
new Selection(1, 1, 1, 1)
);
});
});
suite('Editor Contrib - Line Comment As Block Comment', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '', blockComment: ['(', ')'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection);
mode.dispose();
}
@@ -743,7 +782,7 @@ suite('Editor Contrib - Line Comment As Block Comment', () => {
suite('Editor Contrib - Line Comment As Block Comment 2', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: null, blockComment: ['<!@#', '#@!>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection);
mode.dispose();
}
@@ -984,7 +1023,7 @@ suite('Editor Contrib - Line Comment in mixed modes', () => {
lines,
outerMode.getLanguageIdentifier(),
selection,
(sel) => new LineCommentCommand(sel, 4, Type.Toggle),
(sel) => new LineCommentCommand(sel, 4, Type.Toggle, true),
expectedLines,
expectedSelection,
true
+21 -10
View File
@@ -9,7 +9,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { Selection } from 'vs/editor/common/core/selection';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
@@ -35,6 +35,14 @@ class CursorState {
}
}
class StackElement {
constructor(
public readonly cursorState: CursorState,
public readonly scrollTop: number,
public readonly scrollLeft: number
) { }
}
export class CursorUndoRedoController extends Disposable implements IEditorContribution {
public static readonly ID = 'editor.contrib.cursorUndoRedoController';
@@ -46,8 +54,8 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr
private readonly _editor: ICodeEditor;
private _isCursorUndoRedo: boolean;
private _undoStack: CursorState[];
private _redoStack: CursorState[];
private _undoStack: StackElement[];
private _redoStack: StackElement[];
constructor(editor: ICodeEditor) {
super();
@@ -76,9 +84,9 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr
return;
}
const prevState = new CursorState(e.oldSelections);
const isEqualToLastUndoStack = (this._undoStack.length > 0 && this._undoStack[this._undoStack.length - 1].equals(prevState));
const isEqualToLastUndoStack = (this._undoStack.length > 0 && this._undoStack[this._undoStack.length - 1].cursorState.equals(prevState));
if (!isEqualToLastUndoStack) {
this._undoStack.push(prevState);
this._undoStack.push(new StackElement(prevState, editor.getScrollTop(), editor.getScrollLeft()));
this._redoStack = [];
if (this._undoStack.length > 50) {
// keep the cursor undo stack bounded
@@ -93,7 +101,7 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr
return;
}
this._redoStack.push(new CursorState(this._editor.getSelections()));
this._redoStack.push(new StackElement(new CursorState(this._editor.getSelections()), this._editor.getScrollTop(), this._editor.getScrollLeft()));
this._applyState(this._undoStack.pop()!);
}
@@ -102,14 +110,17 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr
return;
}
this._undoStack.push(new CursorState(this._editor.getSelections()));
this._undoStack.push(new StackElement(new CursorState(this._editor.getSelections()), this._editor.getScrollTop(), this._editor.getScrollLeft()));
this._applyState(this._redoStack.pop()!);
}
private _applyState(state: CursorState): void {
private _applyState(stackElement: StackElement): void {
this._isCursorUndoRedo = true;
this._editor.setSelections(state.selections);
this._editor.revealRangeInCenterIfOutsideViewport(state.selections[0], ScrollType.Smooth);
this._editor.setSelections(stackElement.cursorState.selections);
this._editor.setScrollPosition({
scrollTop: stackElement.scrollTop,
scrollLeft: stackElement.scrollLeft
});
this._isCursorUndoRedo = false;
}
}
@@ -191,7 +191,7 @@ class FormatOnPaste implements IEditorContribution {
return;
}
this._callOnModel.add(this.editor.onDidPaste(range => this._trigger(range)));
this._callOnModel.add(this.editor.onDidPaste(({ range }) => this._trigger(range)));
}
private _trigger(range: Range): void {
+4 -2
View File
@@ -27,6 +27,7 @@ import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { Action } from 'vs/base/common/actions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { isEqual } from 'vs/base/common/resources';
import { IOpenerService } from 'vs/platform/opener/common/opener';
class MarkerModel {
@@ -209,7 +210,8 @@ export class MarkerController implements IEditorContribution {
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IThemeService private readonly _themeService: IThemeService,
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@IKeybindingService private readonly _keybindingService: IKeybindingService
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IOpenerService private readonly _openerService: IOpenerService
) {
this._editor = editor;
this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService);
@@ -243,7 +245,7 @@ export class MarkerController implements IEditorContribution {
new Action(NextMarkerAction.ID, NextMarkerAction.LABEL + (nextMarkerKeybinding ? ` (${nextMarkerKeybinding.getLabel()})` : ''), 'show-next-problem codicon-chevron-down', this._model.canNavigate(), async () => { if (this._model) { this._model.move(true, true); } }),
new Action(PrevMarkerAction.ID, PrevMarkerAction.LABEL + (prevMarkerKeybinding ? ` (${prevMarkerKeybinding.getLabel()})` : ''), 'show-previous-problem codicon-chevron-up', this._model.canNavigate(), async () => { if (this._model) { this._model.move(false, true); } })
];
this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService);
this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService, this._openerService);
this._widgetVisible.set(true);
this._widget.onDidClose(() => this.closeMarkersNavigation(), this, this._disposeOnClose);
@@ -26,6 +26,7 @@ import { IAction } from 'vs/base/common/actions';
import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
class MessageWidget {
@@ -39,7 +40,14 @@ class MessageWidget {
private readonly _relatedDiagnostics = new WeakMap<HTMLElement, IRelatedInformation>();
private readonly _disposables: DisposableStore = new DisposableStore();
constructor(parent: HTMLElement, editor: ICodeEditor, onRelatedInformation: (related: IRelatedInformation) => void) {
private _codeLink?: HTMLElement;
constructor(
parent: HTMLElement,
editor: ICodeEditor,
onRelatedInformation: (related: IRelatedInformation) => void,
private readonly _openerService: IOpenerService,
) {
this._editor = editor;
const domNode = document.createElement('div');
@@ -81,12 +89,20 @@ class MessageWidget {
}
update({ source, message, relatedInformation, code }: IMarker): void {
let sourceAndCodeLength = (source?.length || 0) + '()'.length;
if (code) {
if (typeof code === 'string') {
sourceAndCodeLength += code.length;
} else {
sourceAndCodeLength += code.value.length;
}
}
const lines = message.split(/\r\n|\r|\n/g);
this._lines = lines.length;
this._longestLineLength = 0;
for (const line of lines) {
this._longestLineLength = Math.max(line.length, this._longestLineLength);
this._longestLineLength = Math.max(line.length + sourceAndCodeLength, this._longestLineLength);
}
dom.clearNode(this._messageBlock);
@@ -111,10 +127,25 @@ class MessageWidget {
detailsElement.appendChild(sourceElement);
}
if (code) {
const codeElement = document.createElement('span');
codeElement.innerText = `(${code})`;
dom.addClass(codeElement, 'code');
detailsElement.appendChild(codeElement);
if (typeof code === 'string') {
const codeElement = document.createElement('span');
codeElement.innerText = `(${code})`;
dom.addClass(codeElement, 'code');
detailsElement.appendChild(codeElement);
} else {
this._codeLink = dom.$('a.code-link');
this._codeLink.setAttribute('href', `${code.link.toString()}`);
this._codeLink.onclick = (e) => {
this._openerService.open(code.link);
e.preventDefault();
e.stopPropagation();
};
const codeElement = dom.append(this._codeLink, dom.$('span'));
codeElement.innerText = code.value;
detailsElement.appendChild(this._codeLink);
}
}
}
@@ -180,7 +211,8 @@ export class MarkerNavigationWidget extends PeekViewWidget {
constructor(
editor: ICodeEditor,
private readonly actions: ReadonlyArray<IAction>,
private readonly _themeService: IThemeService
private readonly _themeService: IThemeService,
private readonly _openerService: IOpenerService
) {
super(editor, { showArrow: true, showFrame: true, isAccessible: true });
this._severity = MarkerSeverity.Warning;
@@ -250,7 +282,7 @@ export class MarkerNavigationWidget extends PeekViewWidget {
this._container = document.createElement('div');
container.appendChild(this._container);
this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related));
this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related), this._openerService);
this._disposables.add(this._message);
}
@@ -329,8 +361,9 @@ export const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationI
export const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: '#2D2D30', light: Color.white, hc: '#0C141F' }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.'));
registerThemingParticipant((theme, collector) => {
const link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(`.monaco-editor .marker-widget a { color: ${link}; }`);
const linkFg = theme.getColor(textLinkForeground);
if (linkFg) {
collector.addRule(`.monaco-editor .marker-widget a { color: ${linkFg}; }`);
collector.addRule(`.monaco-editor .marker-widget a.code-link span:hover { color: ${linkFg}; }`);
}
});
@@ -45,10 +45,27 @@
}
.monaco-editor .marker-widget .descriptioncontainer .message .source,
.monaco-editor .marker-widget .descriptioncontainer .message .code {
.monaco-editor .marker-widget .descriptioncontainer .message span.code {
opacity: 0.6;
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link {
opacity: 0.6;
color: inherit;
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link:before {
content: '(';
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link:after {
content: ')';
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link > span {
text-decoration: underline;
/** Hack to force underline to show **/
border-bottom: 1px solid transparent;
text-underline-position: under;
}
.monaco-editor .marker-widget .descriptioncontainer .filename {
cursor: pointer;
}
@@ -165,7 +165,7 @@ export abstract class ReferencesController implements IEditorContribution {
let selection = this._model.nearestReference(uri, pos);
if (selection) {
return this._widget.setSelection(selection).then(() => {
if (this._widget && this._editor.getOption(EditorOption.peekWidgetFocusInlineEditor)) {
if (this._widget && this._editor.getOption(EditorOption.peekWidgetDefaultFocus) === 'editor') {
this._widget.focusOnPreviewEditor();
}
});

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