Archived
Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 (#8962)
* Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 * skip failing tests * update mac build image
This commit is contained in:
committed by
Karl Burtram
parent
0eaee18dc4
commit
fefe1454de
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" }
|
||||
|
||||
Binary file not shown.
@@ -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;
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -148,6 +148,7 @@ export namespace Event {
|
||||
|
||||
if (leading && !handle) {
|
||||
emitter.fire(output);
|
||||
output = undefined;
|
||||
}
|
||||
|
||||
clearTimeout(handle);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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); // ·
|
||||
sb.write1(spaceRenderWhitespaceCharacter); // · or word separator middle dot
|
||||
}
|
||||
|
||||
charOffsetInPart += charWidth;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user