Merge from vscode 7eaf220cafb9d9e901370ffce02229171cbf3ea6

This commit is contained in:
ADS Merger
2020-09-03 16:27:57 -07:00
committed by Anthony Dresser
parent 39d9eed585
commit a63578e6f7
519 changed files with 14338 additions and 6670 deletions
+1
View File
@@ -16,6 +16,7 @@ exports.base = [{
}];
exports.workerExtensionHost = [entrypoint('vs/workbench/services/extensions/worker/extensionHostWorker')];
exports.workerNotebook = [entrypoint('vs/workbench/contrib/notebook/common/services/notebookSimpleWorker')];
exports.workbenchDesktop = require('./vs/workbench/buildfile.desktop').collectModules();
exports.workbenchWeb = require('./vs/workbench/buildfile.web').collectModules();
@@ -92,7 +92,7 @@ export default class DiffEditorComponent extends ComponentBase implements ICompo
let editorinput1 = this._instantiationService.createInstance(ResourceEditorInput, uri1, 'source', undefined, undefined);
let editorinput2 = this._instantiationService.createInstance(ResourceEditorInput, uri2, 'target', undefined, undefined);
this._editorInput = new DiffEditorInput('DiffEditor', undefined, editorinput1, editorinput2, true);
this._editor.setInput(this._editorInput, undefined, cancellationTokenSource.token);
this._editor.setInput(this._editorInput, undefined, undefined, cancellationTokenSource.token);
this._editorInput.resolve().then(model => {
@@ -70,7 +70,7 @@ export default class EditorComponent extends ComponentBase implements IComponent
this._editor.setVisible(true);
let uri = this.createUri();
this._editorInput = this.editorService.createEditorInput({ forceUntitled: true, resource: uri, mode: 'plaintext' }) as UntitledTextEditorInput;
await this._editor.setInput(this._editorInput, undefined);
await this._editor.setInput(this._editorInput, undefined, undefined);
const model = await this._editorInput.resolve();
this._editorModel = model.textEditorModel;
this.fireEvent({
@@ -6,15 +6,15 @@ import 'vs/css!./media/modelViewEditor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions } from 'vs/workbench/common/editor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import * as DOM from 'vs/base/browser/dom';
import { ModelViewInput } from 'sql/workbench/browser/modelComponents/modelViewInput';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService } from 'vs/platform/storage/common/storage';
export class ModelViewEditor extends BaseEditor {
export class ModelViewEditor extends EditorPane {
public static ID: string = 'workbench.editor.modelViewEditor';
@@ -62,7 +62,7 @@ export class ModelViewEditor extends BaseEditor {
}
}
async setInput(input: ModelViewInput, options?: EditorOptions): Promise<void> {
async setInput(input: ModelViewInput, options?: EditorOptions, context?: IEditorOpenContext): Promise<void> {
if (this.input && this.input.matches(input)) {
return Promise.resolve(undefined);
}
@@ -73,7 +73,7 @@ export class ModelViewEditor extends BaseEditor {
input.container.style.visibility = 'visible';
this._content.setAttribute('aria-flowto', input.container.id);
await super.setInput(input, options, CancellationToken.None);
await super.setInput(input, options, context, CancellationToken.None);
this.doUpdateContainer();
}
@@ -15,7 +15,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { EditorOptions } from 'vs/workbench/common/editor';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -85,8 +85,8 @@ export class QueryTextEditor extends BaseTextEditor {
return options;
}
setInput(input: UntitledTextEditorInput, options: EditorOptions): Promise<void> {
return super.setInput(input, options, CancellationToken.None)
setInput(input: UntitledTextEditorInput, options: EditorOptions, context: IEditorOpenContext): Promise<void> {
return super.setInput(input, options, context, CancellationToken.None)
.then(() => this.input.resolve()
.then(editorModel => editorModel.load())
.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { EditorOptions } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -25,7 +25,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IQueryManagementService } from 'sql/workbench/services/query/common/queryManagement';
export class DashboardEditor extends BaseEditor {
export class DashboardEditor extends EditorPane {
public static ID: string = 'workbench.editor.connectiondashboard';
private _dashboardContainer: HTMLElement;
@@ -77,14 +77,14 @@ export class DashboardEditor extends BaseEditor {
this._dashboardService.layout(dimension);
}
public async setInput(input: DashboardInput, options: EditorOptions): Promise<void> {
public async setInput(input: DashboardInput, options: EditorOptions, context: IEditorOpenContext): Promise<void> {
if (this.input && this.input.matches(input)) {
return Promise.resolve(undefined);
}
const parentElement = this.getContainer();
super.setInput(input, options, CancellationToken.None);
super.setInput(input, options, context, CancellationToken.None);
DOM.clearNode(parentElement);
@@ -7,8 +7,8 @@ import * as strings from 'vs/base/common/strings';
import * as DOM from 'vs/base/browser/dom';
import * as nls from 'vs/nls';
import { EditorOptions, EditorInput, IEditorControl, IEditorPane } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions, EditorInput, IEditorControl, IEditorPane, IEditorOpenContext } from 'vs/workbench/common/editor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -41,7 +41,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
/**
* Editor that hosts an action bar and a resultSetInput for an edit data session
*/
export class EditDataEditor extends BaseEditor {
export class EditDataEditor extends EditorPane {
public static ID: string = 'workbench.editor.editDataEditor';
@@ -213,7 +213,7 @@ export class EditDataEditor extends BaseEditor {
/**
* Sets the input data for this editor.
*/
public setInput(newInput: EditDataInput, options?: EditorOptions): Promise<void> {
public setInput(newInput: EditDataInput, options?: EditorOptions, context?: IEditorOpenContext): Promise<void> {
let oldInput = <EditDataInput>this.input;
if (!newInput.setup) {
this._initialized = false;
@@ -224,7 +224,7 @@ export class EditDataEditor extends BaseEditor {
newInput.setupComplete();
}
return super.setInput(newInput, options, CancellationToken.None)
return super.setInput(newInput, options, context, CancellationToken.None)
.then(() => this._updateInput(oldInput, newInput, options));
}
@@ -251,7 +251,7 @@ export class EditDataEditor extends BaseEditor {
}
// PRIVATE METHODS ////////////////////////////////////////////////////////////
private _createEditor(editorInput: EditorInput, container: HTMLElement): Promise<BaseEditor> {
private _createEditor(editorInput: EditorInput, container: HTMLElement): Promise<EditorPane> {
const descriptor = this._editorDescriptorService.getEditor(editorInput);
if (!descriptor) {
return Promise.reject(new Error(strings.format('Can not find a registered editor for the input {0}', editorInput)));
@@ -493,7 +493,7 @@ export class EditDataEditor extends BaseEditor {
*/
private _onResultsEditorCreated(resultsEditor: EditDataResultsEditor, resultsInput: EditDataResultsInput, options: EditorOptions): Promise<void> {
this._resultsEditor = resultsEditor;
return this._resultsEditor.setInput(resultsInput, options);
return this._resultsEditor.setInput(resultsInput, options, undefined);
}
/**
@@ -501,7 +501,7 @@ export class EditDataEditor extends BaseEditor {
*/
private _onSqlEditorCreated(sqlEditor: TextResourceEditor, sqlInput: UntitledTextEditorInput, options: EditorOptions): Thenable<void> {
this._sqlEditor = sqlEditor;
return this._sqlEditor.setInput(sqlInput, options, CancellationToken.None);
return this._sqlEditor.setInput(sqlInput, options, undefined, CancellationToken.None);
}
private _resizeGridContents(): void {
@@ -5,13 +5,13 @@
import * as DOM from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { EditorOptions } from 'vs/workbench/common/editor';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { getZoomLevel } from 'vs/base/browser/browser';
import { Configuration } from 'vs/editor/browser/config/configuration';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import * as types from 'vs/base/common/types';
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
@@ -21,7 +21,7 @@ import { EditDataResultsInput } from 'sql/workbench/browser/editData/editDataRes
import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService } from 'vs/platform/storage/common/storage';
export class EditDataResultsEditor extends BaseEditor {
export class EditDataResultsEditor extends EditorPane {
public static ID: string = 'workbench.editor.editDataResultsEditor';
protected _input: EditDataResultsInput;
@@ -63,8 +63,8 @@ export class EditDataResultsEditor extends BaseEditor {
public layout(dimension: DOM.Dimension): void {
}
public setInput(input: EditDataResultsInput, options: EditorOptions): Promise<void> {
super.setInput(input, options, CancellationToken.None);
public setInput(input: EditDataResultsInput, options: EditorOptions, context: IEditorOpenContext): Promise<void> {
super.setInput(input, options, context, CancellationToken.None);
this._applySettings();
if (!input.hasBootstrapped) {
this.createGridPanel();
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations';
import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations';
import { IProductService } from 'vs/platform/product/common/productService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -19,6 +19,7 @@ import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { InstallRecommendedExtensionsByScenarioAction, ShowRecommendedExtensionsByScenarioAction } from 'sql/workbench/contrib/extensions/browser/extensionsActions';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
const choiceNever = localize('neverShowAgain', "Don't Show Again");
@@ -28,18 +29,20 @@ export class ScenarioRecommendations extends ExtensionRecommendations {
get recommendations(): ReadonlyArray<ExtensionRecommendation> { return this._recommendations; }
constructor(
isExtensionAllowedToBeRecommended: (extensionId: string) => boolean,
promptedExtensionRecommendations: PromptedExtensionRecommendations,
@IProductService private readonly productService: IProductService,
@IInstantiationService instantiationService: IInstantiationService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@INotificationService notificationService: INotificationService,
@INotificationService private readonly notificationService: INotificationService,
@ITelemetryService telemetryService: ITelemetryService,
@IStorageService storageService: IStorageService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IStorageService private readonly storageService: IStorageService,
@IExtensionManagementService protected readonly extensionManagementService: IExtensionManagementService,
@IAdsTelemetryService private readonly adsTelemetryService: IAdsTelemetryService,
@IExtensionsWorkbenchService protected readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService);
super(promptedExtensionRecommendations);
// this._recommendations = productService.recommendedExtensionsByScenario.map(r => ({ extensionId: r, reason: { reasonId: ExtensionRecommendationReason.Application, reasonText: localize('defaultRecommendations', "This extension is recommended by Azure Data Studio.") }, source: 'application' }));
}
@@ -123,12 +126,11 @@ export class ScenarioRecommendations extends ExtensionRecommendations {
});
}
getRecommendedExtensionsByScenario(scenarioType: string): Promise<IExtensionRecommendation[]> {
async getRecommendedExtensionsByScenario(scenarioType: string): Promise<IExtensionRecommendation[]> {
if (!scenarioType) {
return Promise.reject(new Error(localize('scenarioTypeUndefined', 'The scenario type for extension recommendations must be provided.')));
}
return Promise.resolve((this.productService.recommendedExtensionsByScenario[scenarioType] || [])
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId))
.map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: ['application'] })));
return this.promptedExtensionRecommendations.filterIgnoredOrNotAllowed(this.productService.recommendedExtensionsByScenario[scenarioType] || [])
.map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: ['application'] }));
}
}
@@ -3,16 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations';
import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations';
import { IProductService } from 'vs/platform/product/common/productService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { localize } from 'vs/nls';
import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export class StaticRecommendations extends ExtensionRecommendations {
@@ -20,16 +14,10 @@ export class StaticRecommendations extends ExtensionRecommendations {
get recommendations(): ReadonlyArray<ExtensionRecommendation> { return this._recommendations; }
constructor(
isExtensionAllowedToBeRecommended: (extensionId: string) => boolean,
@IProductService productService: IProductService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@INotificationService notificationService: INotificationService,
@ITelemetryService telemetryService: ITelemetryService,
@IStorageService storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
promptedExtensionRecommendations: PromptedExtensionRecommendations,
@IProductService productService: IProductService
) {
super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService);
super(promptedExtensionRecommendations);
this._recommendations = productService.recommendedExtensions.map(r => ({ extensionId: r, reason: { reasonId: ExtensionRecommendationReason.Application, reasonText: localize('defaultRecommendations', "This extension is recommended by Azure Data Studio.") }, source: 'application' }));
}
@@ -203,7 +203,7 @@ export class CodeComponent extends CellView implements OnInit, OnChanges {
cellModelSource = Array.isArray(this.cellModel.source) ? this.cellModel.source.join('') : this.cellModel.source;
const model = this._instantiationService.createInstance(UntitledTextEditorModel, uri, false, cellModelSource, this.cellModel.language, undefined);
this._editorInput = this._instantiationService.createInstance(UntitledTextEditorInput, model);
await this._editor.setInput(this._editorInput, undefined);
await this._editor.setInput(this._editorInput, undefined, undefined);
this.setFocusAndScroll();
let untitledEditorModel = await this._editorInput.resolve() as UntitledTextEditorModel;
@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions } from 'vs/workbench/common/editor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import * as DOM from 'vs/base/browser/dom';
import { bootstrapAngular } from 'sql/workbench/services/bootstrap/browser/bootstrapService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -35,7 +35,7 @@ import { TimeoutTimer } from 'vs/base/common/async';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { onUnexpectedError } from 'vs/base/common/errors';
export class NotebookEditor extends BaseEditor implements IFindNotebookController {
export class NotebookEditor extends EditorPane implements IFindNotebookController {
public static ID: string = 'workbench.editor.notebookEditor';
private _notebookContainer: HTMLElement;
@@ -187,14 +187,14 @@ export class NotebookEditor extends BaseEditor implements IFindNotebookControlle
}
}
public async setInput(input: NotebookInput, options: EditorOptions): Promise<void> {
public async setInput(input: NotebookInput, options: EditorOptions, context: IEditorOpenContext): Promise<void> {
if (this.input && this.input.matches(input)) {
return Promise.resolve(undefined);
}
const parentElement = this.getContainer();
await super.setInput(input, options, CancellationToken.None);
await super.setInput(input, options, context, CancellationToken.None);
DOM.clearNode(parentElement);
await this.setFindInput(parentElement);
if (!input.hasBootstrapped) {
@@ -126,7 +126,7 @@ suite.skip('Test class NotebookEditor:', () => {
const testNotebookEditor = new NotebookEditorStub({ cellGuid: cellTextEditorGuid, editor: queryTextEditor, model: notebookModel, notebookParams: <INotebookParams>{ notebookUri: untitledNotebookInput.notebookUri } });
notebookService.addNotebookEditor(testNotebookEditor);
notebookEditor.clearInput();
await notebookEditor.setInput(untitledNotebookInput, EditorOptions.create({ pinned: true }));
await notebookEditor.setInput(untitledNotebookInput, EditorOptions.create({ pinned: true }), undefined);
untitledNotebookInput.notebookFindModel.notebookModel = undefined; // clear preexisting notebookModel
const result = await notebookEditor.getNotebookModel();
assert.strictEqual(result, notebookModel, `getNotebookModel() should return the model set in the INotebookEditor object`);
@@ -214,7 +214,7 @@ suite.skip('Test class NotebookEditor:', () => {
untitledNotebookInput /* set to a known input */,
untitledNotebookInput /* tries to set the same input that was previously set */
]) {
await notebookEditor.setInput(input, editorOptions);
await notebookEditor.setInput(input, editorOptions, undefined);
assert.strictEqual(notebookEditor.input, input, `notebookEditor.input should be the one that we set`);
}
});
@@ -225,7 +225,7 @@ suite.skip('Test class NotebookEditor:', () => {
for (const isRevealed of [true, false]) {
notebookEditor['_findState']['_isRevealed'] = isRevealed;
notebookEditor.clearInput();
await notebookEditor.setInput(untitledNotebookInput, editorOptions);
await notebookEditor.setInput(untitledNotebookInput, editorOptions, undefined);
assert.strictEqual(notebookEditor.input, untitledNotebookInput, `notebookEditor.input should be the one that we set`);
}
});
@@ -744,7 +744,7 @@ async function setupNotebookEditor(notebookEditor: NotebookEditor, untitledNoteb
async function setInputDocument(notebookEditor: NotebookEditor, untitledNotebookInput: UntitledNotebookInput): Promise<void> {
const editorOptions = EditorOptions.create({ pinned: true });
await notebookEditor.setInput(untitledNotebookInput, editorOptions);
await notebookEditor.setInput(untitledNotebookInput, editorOptions, undefined);
assert.strictEqual(notebookEditor.options, editorOptions, 'NotebookEditor options must be the ones that we set');
}
@@ -38,7 +38,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IView, SplitView, Sizing } from 'vs/base/browser/ui/splitview/splitview';
import * as DOM from 'vs/base/browser/dom';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { EditorOptions } from 'vs/workbench/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkbenchThemeService, VS_DARK_THEME, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
@@ -117,7 +117,7 @@ export interface IDetailData {
value: string;
}
export class ProfilerEditor extends BaseEditor {
export class ProfilerEditor extends EditorPane {
public static readonly ID: string = 'workbench.editor.profiler';
private _untitledTextEditorModel: UntitledTextEditorModel;
@@ -440,7 +440,7 @@ export class ProfilerEditor extends BaseEditor {
this._editor.setVisible(true);
this._untitledTextEditorModel = this._instantiationService.createInstance(UntitledTextEditorModel, URI.from({ scheme: Schemas.untitled }), false, undefined, 'sql', undefined);
this._editorInput = this._instantiationService.createInstance(UntitledTextEditorInput, this._untitledTextEditorModel);
this._editor.setInput(this._editorInput, undefined);
this._editor.setInput(this._editorInput, undefined, undefined);
this._editorInput.resolve().then(model => this._editorModel = model.textEditorModel);
return editorContainer;
}
@@ -460,7 +460,7 @@ export class ProfilerEditor extends BaseEditor {
return Promise.resolve(null);
}
return super.setInput(input, options, CancellationToken.None).then(() => {
return super.setInput(input, options, undefined, CancellationToken.None).then(() => {
this._profilerTableEditor.setInput(input);
if (input.viewTemplate) {
@@ -15,7 +15,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { EditorOptions } from 'vs/workbench/common/editor';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -75,8 +75,8 @@ export class ProfilerResourceEditor extends BaseTextEditor {
return options;
}
setInput(input: UntitledTextEditorInput, options: EditorOptions): Promise<void> {
return super.setInput(input, options, CancellationToken.None)
setInput(input: UntitledTextEditorInput, options: EditorOptions, context: IEditorOpenContext): Promise<void> {
return super.setInput(input, options, context, CancellationToken.None)
.then(() => this.input.resolve()
.then(editorModel => editorModel.load())
.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
@@ -20,7 +20,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IEditorAction } from 'vs/editor/common/editorCommon';
import { IOverlayWidget } from 'vs/editor/browser/editorBrowser';
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Event, Emitter } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -39,7 +39,7 @@ export interface ProfilerTableViewState {
scrollLeft: number;
}
export class ProfilerTableEditor extends BaseEditor implements IProfilerController, ITableController {
export class ProfilerTableEditor extends EditorPane implements IProfilerController, ITableController {
public static ID: string = 'workbench.editor.profiler.table';
protected _input: ProfilerInput;
@@ -7,8 +7,8 @@ import 'vs/css!./media/queryEditor';
import * as DOM from 'vs/base/browser/dom';
import * as path from 'vs/base/common/path';
import { EditorOptions, IEditorControl, IEditorMemento } from 'vs/workbench/common/editor';
import { BaseEditor, EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions, IEditorControl, IEditorMemento, IEditorOpenContext } from 'vs/workbench/common/editor';
import { EditorPane, EditorMemento } from 'vs/workbench/browser/parts/editor/editorPane';
import { Orientation } from 'vs/base/browser/ui/sash/sash';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -50,7 +50,7 @@ interface IQueryEditorViewState {
* Editor that hosts 2 sub-editors: A TextResourceEditor for SQL file editing, and a QueryResultsEditor
* for viewing and editing query results. This editor is based off SideBySideEditor.
*/
export class QueryEditor extends BaseEditor {
export class QueryEditor extends EditorPane {
public static ID: string = 'workbench.editor.queryEditor';
@@ -320,7 +320,7 @@ export class QueryEditor extends BaseEditor {
this.taskbar.setContent(content);
}
public async setInput(newInput: QueryEditorInput, options: EditorOptions, token: CancellationToken): Promise<void> {
public async setInput(newInput: QueryEditorInput, options: EditorOptions, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
const oldInput = this.input;
if (newInput.matches(oldInput)) {
@@ -350,9 +350,9 @@ export class QueryEditor extends BaseEditor {
}
await Promise.all([
super.setInput(newInput, options, token),
this.currentTextEditor.setInput(newInput.text, options, token),
this.resultsEditor.setInput(newInput.results, options)
super.setInput(newInput, options, context, token),
this.currentTextEditor.setInput(newInput.text, options, context, token),
this.resultsEditor.setInput(newInput.results, options, context)
]);
this.inputDisposables.clear();
@@ -3,10 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { EditorOptions } from 'vs/workbench/common/editor';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { getZoomLevel } from 'vs/base/browser/browser';
@@ -71,7 +71,7 @@ export function getBareResultsGridInfoStyles(info: BareResultsGridInfo): string
/**
* Editor associated with viewing and editing the data of a query results grid.
*/
export class QueryResultsEditor extends BaseEditor {
export class QueryResultsEditor extends EditorPane {
public static ID: string = 'workbench.editor.queryResultsEditor';
protected _rawOptions: BareResultsGridInfo;
@@ -131,8 +131,8 @@ export class QueryResultsEditor extends BaseEditor {
this.resultsView.layout(dimension);
}
setInput(input: QueryResultsInput, options: EditorOptions): Promise<void> {
super.setInput(input, options, CancellationToken.None);
setInput(input: QueryResultsInput, options: EditorOptions, context: IEditorOpenContext): Promise<void> {
super.setInput(input, options, context, CancellationToken.None);
this.resultsView.input = input;
return Promise.resolve<void>(null);
}
@@ -16,7 +16,7 @@ import { EditorDescriptorService } from 'sql/workbench/services/queryEditor/brow
import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
@@ -76,7 +76,7 @@ suite('SQL QueryEditor Tests', () => {
getId: function (): string { return 'id'; },
getName: function (): string { return 'name'; },
describes: function (obj: any): boolean { return true; },
instantiate(instantiationService: IInstantiationService): BaseEditor { return undefined; }
instantiate(instantiationService: IInstantiationService): EditorPane { return undefined; }
};
editorDescriptorService = TypeMoq.Mock.ofType(EditorDescriptorService, TypeMoq.MockBehavior.Loose);
editorDescriptorService.setup(x => x.getEditor(TypeMoq.It.isAny())).returns(() => descriptor);
@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { EditorOptions } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { QueryPlanInput } from 'sql/workbench/contrib/queryPlan/common/queryPlanInput';
@@ -13,7 +13,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { QueryPlanView } from 'sql/workbench/contrib/queryPlan/browser/queryPlan';
export class QueryPlanEditor extends BaseEditor {
export class QueryPlanEditor extends EditorPane {
public static ID: string = 'workbench.editor.queryplan';
@@ -60,13 +60,13 @@ export class QueryPlanEditor extends BaseEditor {
this.view.layout(dimension);
}
public async setInput(input: QueryPlanInput, options: EditorOptions): Promise<void> {
public async setInput(input: QueryPlanInput, options: EditorOptions, context: IEditorOpenContext): Promise<void> {
if (this.input instanceof QueryPlanInput && this.input.matches(input)) {
return Promise.resolve(undefined);
}
await input.resolve();
await super.setInput(input, options, CancellationToken.None);
await super.setInput(input, options, context, CancellationToken.None);
this.view.showPlan(input.planXml!);
}
@@ -8,16 +8,16 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import * as DOM from 'vs/base/browser/dom';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions } from 'vs/workbench/common/editor';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IResourceViewerStateChangedEvent } from 'sql/workbench/common/editor/resourceViewer/resourceViewerState';
import { ResourceViewerInput } from 'sql/workbench/browser/editor/resourceViewer/resourceViewerInput';
import { ResourceViewerTable } from 'sql/workbench/contrib/resourceViewer/browser/resourceViewerTable';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
export class ResourceViewerEditor extends BaseEditor {
export class ResourceViewerEditor extends EditorPane {
public static readonly ID: string = 'workbench.editor.resource-viewer';
private _container!: HTMLElement;
@@ -72,8 +72,8 @@ export class ResourceViewerEditor extends BaseEditor {
return this._input as ResourceViewerInput;
}
public async setInput(input: ResourceViewerInput, options?: EditorOptions): Promise<void> {
await super.setInput(input, options, CancellationToken.None);
async setInput(input: ResourceViewerInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
await super.setInput(input, options, context, token);
this._inputDisposables.clear();
+1
View File
@@ -25,6 +25,7 @@
},
"lib": [
"ES2015",
"ES2016.Array.Include",
"ES2017.String",
"ES2018.Promise",
"DOM",
-1
View File
@@ -15,7 +15,6 @@
"include": [
"typings/require.d.ts",
"typings/thenable.d.ts",
"typings/lib.array-ext.d.ts",
"vs/css.d.ts",
"vs/monaco.d.ts",
"vs/nls.d.ts",
+27
View File
@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { renderCodiconsRegex } from 'vs/base/common/codicons';
export function renderCodiconsAsElement(text: string): Array<HTMLSpanElement | string> {
const elements = new Array<HTMLSpanElement | string>();
let match: RegExpMatchArray | null;
let textStart = 0, textStop = 0;
while ((match = renderCodiconsRegex.exec(text)) !== null) {
textStop = match.index || 0;
elements.push(text.substring(textStart, textStop));
textStart = (match.index || 0) + match[0].length;
const [, escaped, codicon, name, animation] = match;
elements.push(escaped ? `$(${codicon})` : dom.$(`span.codicon.codicon-${name}${animation ? `.codicon-animation-${animation}` : ''}`));
}
if (textStart < text.length) {
elements.push(text.substring(textStart));
}
return elements;
}
+17
View File
@@ -997,6 +997,11 @@ export function trackFocus(element: HTMLElement | Window): IFocusTracker {
return new FocusTracker(element);
}
export function after<T extends Node>(sibling: HTMLElement, child: T): T {
sibling.after(child);
return child;
}
export function append<T extends Node>(parent: HTMLElement, ...children: T[]): T {
children.forEach(child => parent.appendChild(child));
return children[children.length - 1];
@@ -1009,6 +1014,18 @@ export function prepend<T extends Node>(parent: HTMLElement, child: T): T {
const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((\.([\w\-]+))*)/;
export function reset<T extends Node>(parent: HTMLElement, ...children: Array<Node | string>) {
parent.innerText = '';
coalesce(children)
.forEach(child => {
if (child instanceof Node) {
parent.appendChild(child);
} else {
parent.appendChild(document.createTextNode(child as string));
}
});
}
export enum Namespace {
HTML = 'http://www.w3.org/1999/xhtml',
SVG = 'http://www.w3.org/2000/svg'
+34
View File
@@ -205,6 +205,40 @@ const altKeyMod = KeyMod.Alt;
const shiftKeyMod = KeyMod.Shift;
const metaKeyMod = (platform.isMacintosh ? KeyMod.CtrlCmd : KeyMod.WinCtrl);
export function printKeyboardEvent(e: KeyboardEvent): string {
let modifiers: string[] = [];
if (e.ctrlKey) {
modifiers.push(`ctrl`);
}
if (e.shiftKey) {
modifiers.push(`shift`);
}
if (e.altKey) {
modifiers.push(`alt`);
}
if (e.metaKey) {
modifiers.push(`meta`);
}
return `modifiers: [${modifiers.join(',')}], code: ${e.code}, keyCode: ${e.keyCode}, key: ${e.key}`;
}
export function printStandardKeyboardEvent(e: StandardKeyboardEvent): string {
let modifiers: string[] = [];
if (e.ctrlKey) {
modifiers.push(`ctrl`);
}
if (e.shiftKey) {
modifiers.push(`shift`);
}
if (e.altKey) {
modifiers.push(`alt`);
}
if (e.metaKey) {
modifiers.push(`meta`);
}
return `modifiers: [${modifiers.join(',')}], code: ${e.code}, keyCode: ${e.keyCode} ('${KeyCodeUtils.toString(e.keyCode)}')`;
}
export class StandardKeyboardEvent implements IKeyboardEvent {
readonly _standardKeyboardEventBrand = true;
+2 -3
View File
@@ -12,8 +12,7 @@ import { mixin } from 'vs/base/common/objects';
import { Event as BaseEvent, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { Gesture, EventType } from 'vs/base/browser/touch';
import { renderCodicons } from 'vs/base/common/codicons';
import { escape } from 'vs/base/common/strings';
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
export interface IButtonOptions extends IButtonStyles {
readonly title?: boolean | string;
@@ -181,7 +180,7 @@ export class Button extends Disposable {
DOM.addClass(this._element, 'monaco-text-button');
}
if (this.options.supportCodicons) {
this._element.innerHTML = renderCodicons(escape(value));
DOM.reset(this._element, ...renderCodiconsAsElement(value));
} else {
this._element.textContent = value;
}
@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { escape } from 'vs/base/common/strings';
import { renderCodicons } from 'vs/base/common/codicons';
import { reset } from 'vs/base/browser/dom';
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
export class CodiconLabel {
@@ -13,7 +13,7 @@ export class CodiconLabel {
) { }
set text(text: string) {
this._container.innerHTML = renderCodicons(escape(text ?? ''));
reset(this._container, ...renderCodiconsAsElement(text ?? ''));
}
set title(title: string) {
+2 -2
View File
@@ -121,7 +121,7 @@ export class ExternalElementsDragAndDropData<T> implements IDragAndDropData {
}
}
export class DesktopDragAndDropData implements IDragAndDropData {
export class NativeDragAndDropData implements IDragAndDropData {
readonly types: any[];
readonly files: any[];
@@ -976,7 +976,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
return false;
}
this.currentDragData = new DesktopDragAndDropData();
this.currentDragData = new NativeDragAndDropData();
}
}
@@ -24,6 +24,7 @@ export interface IPaneOptions {
expanded?: boolean;
orientation?: Orientation;
title: string;
titleDescription?: string;
}
export interface IPaneStyles {
-11
View File
@@ -590,17 +590,6 @@ export function asArray<T>(x: T | T[]): T[] {
return Array.isArray(x) ? x : [x];
}
/**
* @deprecated Use `Array.from` or `[...iter]`
*/
export function toArray<T>(iterable: IterableIterator<T>): T[] {
const result: T[] = [];
for (let element of iterable) {
result.push(element);
}
return result;
}
export function getRandomElement<T>(arr: T[]): T | undefined {
return arr[Math.floor(Math.random() * arr.length)];
}
+19
View File
@@ -170,6 +170,25 @@ export class Sequencer {
}
}
export class SequencerByKey<TKey> {
private promiseMap = new Map<TKey, Promise<any>>();
queue<T>(key: TKey, promiseTask: ITask<Promise<T>>): Promise<T> {
const runningPromise = this.promiseMap.get(key) ?? Promise.resolve();
const newPromise = runningPromise
.catch(() => { })
.then(promiseTask)
.finally(() => {
if (this.promiseMap.get(key) === newPromise) {
this.promiseMap.delete(key);
}
});
this.promiseMap.set(key, newPromise);
return newPromise;
}
}
/**
* A helper to delay execution of a task that is being requested often.
*
+5 -1
View File
@@ -499,7 +499,11 @@ export function markdownUnescapeCodicons(text: string): string {
return text.replace(markdownUnescapeCodiconsRegex, (match, escaped, codicon) => escaped ? match : `$(${codicon})`);
}
const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi;
export const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi;
/**
* @deprecated Use `renderCodiconsAsElement` instead
*/
export function renderCodicons(text: string): string {
return text.replace(renderCodiconsRegex, (_, escaped, codicon, name, animation) => {
// If the class for codicons is changed, it should also be updated in src\vs\base\browser\markdownRenderer.ts
+1 -1
View File
@@ -142,7 +142,7 @@ export function isUNC(path: string): boolean {
// Reference: https://en.wikipedia.org/wiki/Filename
const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g;
const UNIX_INVALID_FILE_CHARS = /[\\/]/g;
const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i;
const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])(\.(.*?))?$/i;
export function isValidBasename(name: string | null | undefined, isWindowsOS: boolean = isWindows): boolean {
const invalidFileChars = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS;
+40 -55
View File
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { compareAnything } from 'vs/base/common/comparers';
import { matchesPrefix, IMatch, isUpper, fuzzyScore, createMatches as createFuzzyMatches, matchesStrictPrefix } from 'vs/base/common/filters';
import { matchesPrefix, IMatch, isUpper, fuzzyScore, createMatches as createFuzzyMatches } from 'vs/base/common/filters';
import { sep } from 'vs/base/common/path';
import { isWindows, isLinux } from 'vs/base/common/platform';
import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings';
@@ -369,9 +369,8 @@ export interface IItemAccessor<T> {
}
const PATH_IDENTITY_SCORE = 1 << 18;
const LABEL_PREFIX_SCORE_MATCHCASE = 1 << 17;
const LABEL_PREFIX_SCORE_IGNORECASE = 1 << 16;
const LABEL_SCORE_THRESHOLD = 1 << 15;
const LABEL_PREFIX_SCORE_THRESHOLD = 1 << 17;
const LABEL_SCORE_THRESHOLD = 1 << 16;
export function scoreItemFuzzy<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: FuzzyScorerCache): IItemScore {
if (!item || !query.normalized) {
@@ -460,20 +459,33 @@ function doScoreItemFuzzyMultiple(label: string, description: string | undefined
function doScoreItemFuzzySingle(label: string, description: string | undefined, path: string | undefined, query: IPreparedQueryPiece, preferLabelMatches: boolean, fuzzy: boolean): IItemScore {
// Prefer label matches if told so
if (preferLabelMatches) {
// Treat prefix matches on the label highest
const prefixLabelMatchIgnoreCase = matchesPrefix(query.normalized, label);
if (prefixLabelMatchIgnoreCase) {
const prefixLabelMatchStrictCase = matchesStrictPrefix(query.normalized, label);
return { score: prefixLabelMatchStrictCase ? LABEL_PREFIX_SCORE_MATCHCASE : LABEL_PREFIX_SCORE_IGNORECASE, labelMatch: prefixLabelMatchStrictCase || prefixLabelMatchIgnoreCase };
}
// Second, score fuzzy
// Prefer label matches if told so or we have no description
if (preferLabelMatches || !description) {
const [labelScore, labelPositions] = scoreFuzzy(label, query.normalized, query.normalizedLowercase, fuzzy);
if (labelScore) {
return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) };
// If we have a prefix match on the label, we give a much
// higher baseScore to elevate these matches over others
// This ensures that typing a file name wins over results
// that are present somewhere in the label, but not the
// beginning.
const labelPrefixMatch = matchesPrefix(query.normalized, label);
let baseScore: number;
if (labelPrefixMatch) {
baseScore = LABEL_PREFIX_SCORE_THRESHOLD;
// We give another boost to labels that are short, e.g. given
// files "window.ts" and "windowActions.ts" and a query of
// "window", we want "window.ts" to receive a higher score.
// As such we compute the percentage the query has within the
// label and add that to the baseScore.
const prefixLengthBoost = Math.round((query.normalized.length / label.length) * 100);
baseScore += prefixLengthBoost;
} else {
baseScore = LABEL_SCORE_THRESHOLD;
}
return { score: baseScore + labelScore, labelMatch: labelPrefixMatch || createMatches(labelPositions) };
}
}
@@ -600,46 +612,19 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
}
}
// 2.) prefer label prefix matches (match case)
if (scoreA === LABEL_PREFIX_SCORE_MATCHCASE || scoreB === LABEL_PREFIX_SCORE_MATCHCASE) {
if (scoreA !== scoreB) {
return scoreA === LABEL_PREFIX_SCORE_MATCHCASE ? -1 : 1;
}
const labelA = accessor.getItemLabel(itemA) || '';
const labelB = accessor.getItemLabel(itemB) || '';
// prefer shorter names when both match on label prefix
if (labelA.length !== labelB.length) {
return labelA.length - labelB.length;
}
}
// 3.) prefer label prefix matches (ignore case)
if (scoreA === LABEL_PREFIX_SCORE_IGNORECASE || scoreB === LABEL_PREFIX_SCORE_IGNORECASE) {
if (scoreA !== scoreB) {
return scoreA === LABEL_PREFIX_SCORE_IGNORECASE ? -1 : 1;
}
const labelA = accessor.getItemLabel(itemA) || '';
const labelB = accessor.getItemLabel(itemB) || '';
// prefer shorter names when both match on label prefix
if (labelA.length !== labelB.length) {
return labelA.length - labelB.length;
}
}
// 4.) matches on label are considered higher compared to label+description matches
// 2.) matches on label are considered higher compared to label+description matches
if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) {
if (scoreA !== scoreB) {
return scoreA > scoreB ? -1 : 1;
}
// prefer more compact matches over longer in label
const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch);
if (comparedByMatchLength !== 0) {
return comparedByMatchLength;
// prefer more compact matches over longer in label (unless this is a prefix match where
// longer prefix matches are actually preferred)
if (scoreA < LABEL_PREFIX_SCORE_THRESHOLD && scoreB < LABEL_PREFIX_SCORE_THRESHOLD) {
const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch);
if (comparedByMatchLength !== 0) {
return comparedByMatchLength;
}
}
// prefer shorter labels over longer labels
@@ -650,12 +635,12 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
}
}
// 5.) compare by score in label+description
// 3.) compare by score in label+description
if (scoreA !== scoreB) {
return scoreA > scoreB ? -1 : 1;
}
// 6.) scores are identical: prefer matches in label over non-label matches
// 4.) scores are identical: prefer matches in label over non-label matches
const itemAHasLabelMatches = Array.isArray(itemScoreA.labelMatch) && itemScoreA.labelMatch.length > 0;
const itemBHasLabelMatches = Array.isArray(itemScoreB.labelMatch) && itemScoreB.labelMatch.length > 0;
if (itemAHasLabelMatches && !itemBHasLabelMatches) {
@@ -664,14 +649,14 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
return 1;
}
// 7.) scores are identical: prefer more compact matches (label and description)
// 5.) scores are identical: prefer more compact matches (label and description)
const itemAMatchDistance = computeLabelAndDescriptionMatchDistance(itemA, itemScoreA, accessor);
const itemBMatchDistance = computeLabelAndDescriptionMatchDistance(itemB, itemScoreB, accessor);
if (itemAMatchDistance && itemBMatchDistance && itemAMatchDistance !== itemBMatchDistance) {
return itemBMatchDistance > itemAMatchDistance ? -1 : 1;
}
// 8.) scores are identical: start to use the fallback compare
// 6.) scores are identical: start to use the fallback compare
return fallbackCompare(itemA, itemB, query, accessor);
}
+1 -1
View File
@@ -45,7 +45,7 @@ export class MarkdownString implements IMarkdownString {
// escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
this._value += (this._supportThemeIcons ? escapeCodicons(value) : value)
.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&')
.replace('\n', '\n\n');
.replace(/\n/g, '\n\n');
return this;
}
+13 -5
View File
@@ -5,7 +5,7 @@
import { VSBuffer } from 'vs/base/common/buffer';
import { regExpFlags } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
export function stringify(obj: any): string {
return JSON.stringify(obj, replacer);
@@ -33,7 +33,15 @@ function replacer(key: string, value: any): any {
return value;
}
export function revive(obj: any, depth = 0): any {
type Deserialize<T> = T extends UriComponents ? URI
: T extends object
? Revived<T>
: T;
export type Revived<T> = { [K in keyof T]: Deserialize<T[K]> };
export function revive<T = any>(obj: any, depth = 0): Revived<T> {
if (!obj || depth > 200) {
return obj;
}
@@ -41,15 +49,15 @@ export function revive(obj: any, depth = 0): any {
if (typeof obj === 'object') {
switch ((<MarshalledObject>obj).$mid) {
case 1: return URI.revive(obj);
case 2: return new RegExp(obj.source, obj.flags);
case 1: return <any>URI.revive(obj);
case 2: return <any>new RegExp(obj.source, obj.flags);
}
if (
obj instanceof VSBuffer
|| obj instanceof Uint8Array
) {
return obj;
return <any>obj;
}
if (Array.isArray(obj)) {
+3 -3
View File
@@ -33,7 +33,7 @@ export interface ReadableStreamEvents<T> {
/**
* A interface that emulates the API shape of a node.js readable
* stream for use in desktop and web environments.
* stream for use in native and web environments.
*/
export interface ReadableStream<T> extends ReadableStreamEvents<T> {
@@ -60,7 +60,7 @@ export interface ReadableStream<T> extends ReadableStreamEvents<T> {
/**
* A interface that emulates the API shape of a node.js readable
* for use in desktop and web environments.
* for use in native and web environments.
*/
export interface Readable<T> {
@@ -73,7 +73,7 @@ export interface Readable<T> {
/**
* A interface that emulates the API shape of a node.js writeable
* stream for use in desktop and web environments.
* stream for use in native and web environments.
*/
export interface WriteableStream<T> extends ReadableStream<T> {
+5 -7
View File
@@ -258,14 +258,12 @@ export type UriDto<T> = { [K in keyof T]: T[K] extends URI
/**
* Mapped-type that replaces all occurrences of URI with UriComponents and
* drops all functions.
* todo@joh use toJSON-results
*/
export type Dto<T> = { [K in keyof T]: T[K] extends URI
? UriComponents
: T[K] extends Function
? never
: UriDto<T[K]> };
export type Dto<T> = T extends { toJSON(): infer U }
? U
: T extends object
? { [k in keyof T]: Dto<T[k]>; }
: T;
export function NotImplementedProxy<T>(name: string): { new(): T } {
return <any>class {
@@ -93,6 +93,14 @@
process: {
platform: process.platform,
env: process.env,
_whenEnvResolved: undefined,
get whenEnvResolved() {
if (!this._whenEnvResolved) {
this._whenEnvResolved = resolveEnv();
}
return this._whenEnvResolved;
},
on:
/**
* @param {string} type
@@ -157,5 +165,33 @@
return true;
}
/**
* If VSCode is not run from a terminal, we should resolve additional
* shell specific environment from the OS shell to ensure we are seeing
* all development related environment variables. We do this from the
* main process because it may involve spawning a shell.
*/
function resolveEnv() {
return new Promise(function (resolve) {
const handle = setTimeout(function () {
console.warn('Preload: Unable to resolve shell environment in a reasonable time');
// It took too long to fetch the shell environment, return
resolve();
}, 3000);
ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) {
clearTimeout(handle);
// Assign all keys of the shell environment to our process environment
Object.assign(process.env, shellEnv);
resolve();
});
ipcRenderer.send('vscode:fetchShellEnv');
});
}
//#endregion
}());
@@ -84,6 +84,12 @@ export const process = (window as any).vscode.process as {
*/
env: { [key: string]: string | undefined };
/**
* Allows to await resolving the full process environment by checking for the shell environment
* of the OS in certain cases (e.g. when the app is started from the Dock on macOS).
*/
whenEnvResolved: Promise<void>;
/**
* A listener on the process. Only a small subset of listener types are allowed.
*/
+1 -1
View File
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Database, Statement } from 'vscode-sqlite3';
import type { Database, Statement } from 'vscode-sqlite3';
import { Event } from 'vs/base/common/event';
import { timeout } from 'vs/base/common/async';
import { mapToString, setToString } from 'vs/base/common/map';
+52
View File
@@ -0,0 +1,52 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
import * as assert from 'assert';
suite('renderCodicons', () => {
test('no codicons', () => {
const result = renderCodiconsAsElement(' hello World .');
assert.equal(elementsToString(result), ' hello World .');
});
test('codicon only', () => {
const result = renderCodiconsAsElement('$(alert)');
assert.equal(elementsToString(result), '<span class="codicon codicon-alert"></span>');
});
test('codicon and non-codicon strings', () => {
const result = renderCodiconsAsElement(` $(alert) Unresponsive`);
assert.equal(elementsToString(result), ' <span class="codicon codicon-alert"></span> Unresponsive');
});
test('multiple codicons', () => {
const result = renderCodiconsAsElement('$(check)$(error)');
assert.equal(elementsToString(result), '<span class="codicon codicon-check"></span><span class="codicon codicon-error"></span>');
});
test('escaped codicon', () => {
const result = renderCodiconsAsElement('\\$(escaped)');
assert.equal(elementsToString(result), '$(escaped)');
});
test('codicon with animation', () => {
const result = renderCodiconsAsElement('$(zip~anim)');
assert.equal(elementsToString(result), '<span class="codicon codicon-zip codicon-animation-anim"></span>');
});
const elementsToString = (elements: Array<HTMLElement | string>): string => {
return elements
.map(elem => elem instanceof HTMLElement ? elem.outerHTML : elem)
.reduce((a, b) => a + b, '');
};
});
+18
View File
@@ -688,4 +688,22 @@ suite('Async', () => {
assert.ok(Date.now() - now < 100);
assert.equal(timedout, false);
});
test('SequencerByKey', async () => {
const s = new async.SequencerByKey<string>();
const r1 = await s.queue('key1', () => Promise.resolve('hello'));
assert.equal(r1, 'hello');
await s.queue('key2', () => Promise.reject(new Error('failed'))).then(() => {
throw new Error('should not be resolved');
}, err => {
// Expected error
assert.equal(err.message, 'failed');
});
// Still works after a queued promise is rejected
const r3 = await s.queue('key2', () => Promise.resolve('hello'));
assert.equal(r3, 'hello');
});
});
+7
View File
@@ -57,6 +57,13 @@ suite('Paths', () => {
assert.ok(!extpath.isValidBasename('aux'));
assert.ok(!extpath.isValidBasename('Aux'));
assert.ok(!extpath.isValidBasename('LPT0'));
assert.ok(!extpath.isValidBasename('aux.txt'));
assert.ok(!extpath.isValidBasename('com0.abc'));
assert.ok(extpath.isValidBasename('LPT00'));
assert.ok(extpath.isValidBasename('aux1'));
assert.ok(extpath.isValidBasename('aux1.txt'));
assert.ok(extpath.isValidBasename('aux1.aux.txt'));
assert.ok(!extpath.isValidBasename('test.txt.'));
assert.ok(!extpath.isValidBasename('test.txt..'));
assert.ok(!extpath.isValidBasename('test.txt '));
@@ -1025,6 +1025,51 @@ suite('Fuzzy Scorer', () => {
assert.equal(res[1], resourceB);
});
test('compareFilesByScore - boost better prefix match if multiple queries are used', function () {
const resourceA = URI.file('src/vs/workbench/services/host/browser/browserHostService.ts');
const resourceB = URI.file('src/vs/workbench/browser/workbench.ts');
for (const query of ['workbench.ts browser', 'browser workbench.ts', 'browser workbench', 'workbench browser']) {
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
}
});
test('compareFilesByScore - boost shorter prefix match if multiple queries are used', function () {
const resourceA = URI.file('src/vs/workbench/browser/actions/windowActions.ts');
const resourceB = URI.file('src/vs/workbench/electron-browser/window.ts');
for (const query of ['window browser', 'window.ts browser']) {
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
}
});
test('compareFilesByScore - boost shorter prefix match if multiple queries are used (#99171)', function () {
const resourceA = URI.file('mesh_editor_lifetime_job.h');
const resourceB = URI.file('lifetime_job.h');
for (const query of ['m life, life m']) {
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
}
});
test('prepareQuery', () => {
assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa');
assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts');
@@ -17,6 +17,9 @@
<!-- Builtin Extensions (running out of sources) -->
<meta id="vscode-workbench-builtin-extensions" data-settings="{{WORKBENCH_BUILTIN_EXTENSIONS}}">
<!-- Workbench Credentials (running out of sources) -->
<meta id="vscode-workbench-credentials" data-settings="{{WORKBENCH_CREDENTIALS}}">
<!-- Workarounds/Hacks (remote user data uri) -->
<meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}">
+46 -27
View File
@@ -3,8 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, ICommand, IHomeIndicator, IProductQualityChangeHandler } from 'vs/workbench/workbench.web.api';
import product from 'vs/platform/product/common/product';
import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, IHomeIndicator, IProductQualityChangeHandler } from 'vs/workbench/workbench.web.api';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { generateUuid } from 'vs/base/common/uuid';
@@ -16,6 +15,7 @@ import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/wi
import { isEqual } from 'vs/base/common/resources';
import { isStandalone } from 'vs/base/browser/browser';
import { localize } from 'vs/nls';
import { Schemas } from 'vs/base/common/network';
interface ICredential {
service: string;
@@ -27,6 +27,13 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider {
static readonly CREDENTIALS_OPENED_KEY = 'credentials.provider';
constructor(credentials: ICredential[]) {
this._credentials = credentials;
for (const { service, account, password } of this._credentials) {
this.setPassword(service, account, password);
}
}
private _credentials: ICredential[] | undefined;
private get credentials(): ICredential[] {
if (!this._credentials) {
@@ -277,6 +284,20 @@ class WorkspaceProvider implements IWorkspaceProvider {
return false;
}
hasRemote(): boolean {
if (this.workspace) {
if (isFolderToOpen(this.workspace)) {
return this.workspace.folderUri.scheme === Schemas.vscodeRemote;
}
if (isWorkspaceToOpen(this.workspace)) {
return this.workspace.workspaceUri.scheme === Schemas.vscodeRemote;
}
}
return true;
}
}
class WindowIndicator implements IWindowIndicator {
@@ -287,8 +308,6 @@ class WindowIndicator implements IWindowIndicator {
readonly tooltip: string;
readonly command: string | undefined;
readonly commandImpl: ICommand | undefined = undefined;
constructor(workspace: IWorkspace) {
let repositoryOwner: string | undefined = undefined;
let repositoryName: string | undefined = undefined;
@@ -306,20 +325,16 @@ class WindowIndicator implements IWindowIndicator {
}
}
// Repo
if (repositoryName && repositoryOwner) {
this.label = localize('openInDesktopLabel', "$(remote) Open in Desktop");
this.tooltip = localize('openInDesktopTooltip', "Open in Desktop");
this.command = '_web.openInDesktop';
this.commandImpl = {
id: this.command,
handler: () => {
const protocol = product.quality === 'stable' ? 'vscode' : 'vscode-insiders';
window.open(`${protocol}://vscode.git/clone?url=${encodeURIComponent(`https://github.com/${repositoryOwner}/${repositoryName}.git`)}`);
}
};
} else {
this.label = localize('playgroundLabel', "Web Playground");
this.tooltip = this.label;
this.label = localize('playgroundLabelRepository', "$(remote) VS Code Web Playground: {0}/{1}", repositoryOwner, repositoryName);
this.tooltip = localize('playgroundRepositoryTooltip', "VS Code Web Playground: {0}/{1}", repositoryOwner, repositoryName);
}
// No Repo
else {
this.label = localize('playgroundLabel', "$(remote) VS Code Web Playground");
this.tooltip = localize('playgroundTooltip', "VS Code Web Playground");
}
}
}
@@ -391,6 +406,9 @@ class WindowIndicator implements IWindowIndicator {
}
}
// Workspace Provider
const workspaceProvider = new WorkspaceProvider(workspace, payload);
// Home Indicator
const homeIndicator: IHomeIndicator = {
href: 'https://github.com/Microsoft/vscode',
@@ -398,13 +416,10 @@ class WindowIndicator implements IWindowIndicator {
title: localize('home', "Home")
};
// Commands
const commands: ICommand[] = [];
// Window indicator
const windowIndicator = new WindowIndicator(workspace);
if (windowIndicator.commandImpl) {
commands.push(windowIndicator.commandImpl);
// Window indicator (unless connected to a remote)
let windowIndicator: WindowIndicator | undefined = undefined;
if (!workspaceProvider.hasRemote()) {
windowIndicator = new WindowIndicator(workspace);
}
// Product Quality Change Handler
@@ -422,15 +437,19 @@ class WindowIndicator implements IWindowIndicator {
window.location.href = `${window.location.origin}?${queryString}`;
};
// Find credentials from DOM
const credentialsElement = document.getElementById('vscode-workbench-credentials');
const credentialsElementAttribute = credentialsElement ? credentialsElement.getAttribute('data-settings') : undefined;
const credentialsProvider = new LocalStorageCredentialsProvider(credentialsElementAttribute ? JSON.parse(credentialsElementAttribute) : []);
// Finally create workbench
create(document.body, {
...config,
homeIndicator,
commands,
windowIndicator,
productQualityChangeHandler,
workspaceProvider: new WorkspaceProvider(workspace, payload),
workspaceProvider,
urlCallbackProvider: new PollingURLCallbackProvider(),
credentialsProvider: new LocalStorageCredentialsProvider()
credentialsProvider
});
})();
@@ -33,8 +33,8 @@ const bootstrapWindow = (() => {
return window.MonacoBootstrapWindow;
})();
// Setup shell environment
process['lazyEnv'] = getLazyEnv();
// Load environment in parallel to workbench loading to avoid waterfall
const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved;
// Load workbench main JS, CSS and NLS all in parallel. This is an
// optimization to prevent a waterfall of loading to happen, because
@@ -46,18 +46,19 @@ bootstrapWindow.load([
'vs/nls!vs/workbench/workbench.desktop.main',
'vs/css!vs/workbench/workbench.desktop.main'
],
function (workbench, configuration) {
async function (workbench, configuration) {
// Mark start of workbench
perf.mark('didLoadWorkbenchMain');
performance.mark('workbench-start');
return process['lazyEnv'].then(function () {
perf.mark('main/startup');
// Wait for process environment being fully resolved
await whenEnvResolved;
// @ts-ignore
return require('vs/workbench/electron-browser/desktop.main').main(configuration);
});
perf.mark('main/startup');
// @ts-ignore
return require('vs/workbench/electron-browser/desktop.main').main(configuration);
},
{
removeDeveloperKeybindingsAfterLoad: true,
@@ -77,7 +78,7 @@ bootstrapWindow.load([
* @param {{
* partsSplashPath?: string,
* highContrast?: boolean,
* defaultThemeType?: string,
* autoDetectHighContrast?: boolean,
* extensionDevelopmentPath?: string[],
* folderUri?: object,
* workspace?: object
@@ -96,7 +97,8 @@ function showPartsSplash(configuration) {
}
// high contrast mode has been turned on from the outside, e.g. OS -> ignore stored colors and layouts
if (data && configuration.highContrast && data.baseTheme !== 'hc-black') {
const isHighContrast = configuration.highContrast && configuration.autoDetectHighContrast;
if (data && isHighContrast && data.baseTheme !== 'hc-black') {
data = undefined;
}
@@ -111,14 +113,10 @@ function showPartsSplash(configuration) {
baseTheme = data.baseTheme;
shellBackground = data.colorInfo.editorBackground;
shellForeground = data.colorInfo.foreground;
} else if (configuration.highContrast || configuration.defaultThemeType === 'hc') {
} else if (isHighContrast) {
baseTheme = 'hc-black';
shellBackground = '#000000';
shellForeground = '#FFFFFF';
} else if (configuration.defaultThemeType === 'vs') {
baseTheme = 'vs';
shellBackground = '#FFFFFF';
shellForeground = '#000000';
} else {
baseTheme = 'vs-dark';
shellBackground = '#1E1E1E';
@@ -172,26 +170,3 @@ function showPartsSplash(configuration) {
perf.mark('didShowPartsSplash');
}
/**
* @returns {Promise<void>}
*/
function getLazyEnv() {
const ipcRenderer = bootstrapWindow.globals().ipcRenderer;
return new Promise(function (resolve) {
const handle = setTimeout(function () {
resolve();
console.warn('renderer did not receive lazyEnv in time');
}, 10000);
ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) {
clearTimeout(handle);
Object.assign(process.env, shellEnv);
// @ts-ignore
resolve(process.env);
});
ipcRenderer.send('vscode:fetchShellEnv');
});
}
+15 -105
View File
@@ -50,7 +50,7 @@ import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/err
import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener';
import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService';
import { RunOnceScheduler } from 'vs/base/common/async';
import { RunOnceScheduler, timeout } from 'vs/base/common/async';
import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu';
import { homedir } from 'os';
import { join, sep, posix } from 'vs/base/common/path';
@@ -68,11 +68,11 @@ import { statSync } from 'fs';
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc';
import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
import { ElectronExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/electron-main/extensionHostDebugIpc';
import { IElectronMainService, ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService';
import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService';
import { IDialogMainService, DialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
import { withNullAsUndefined } from 'vs/base/common/types';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { coalesce } from 'vs/base/common/arrays';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
@@ -80,8 +80,6 @@ import { INativeEnvironmentService } from 'vs/platform/environment/node/environm
import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels';
import { WebviewMainService } from 'vs/platform/webview/electron-main/webviewMainService';
import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService';
import { createServer, AddressInfo } from 'net';
import { IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug';
import { IFileService } from 'vs/platform/files/common/files';
import { stripComments } from 'vs/base/common/json';
import { generateUuid } from 'vs/base/common/uuid';
@@ -272,6 +270,12 @@ export class CodeApplication extends Disposable {
try {
const shellEnv = await getShellEnvironment(this.logService, this.environmentService);
// TODO@sandbox workaround for https://github.com/electron/electron/issues/25119
if (this.environmentService.sandbox) {
await timeout(100);
}
if (!webContents.isDestroyed()) {
webContents.send('vscode:acceptShellEnv', shellEnv);
}
@@ -297,7 +301,7 @@ export class CodeApplication extends Disposable {
const nativeKeymap = await import('native-keymap');
nativeKeymap.onDidChangeKeyboardLayout(() => {
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged');
}
});
})();
@@ -364,17 +368,17 @@ export class CodeApplication extends Disposable {
const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
const sharedProcessClient = sharedProcess.whenIpcReady().then(() => {
this.logService.trace('Shared process: IPC ready');
return connect(this.environmentService.sharedIPCHandle, 'main');
});
const sharedProcessReady = sharedProcess.whenReady().then(() => {
this.logService.trace('Shared process: init ready');
return sharedProcessClient;
});
this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
this._register(new RunOnceScheduler(async () => {
const userEnv = await getShellEnvironment(this.logService, this.environmentService);
sharedProcess.spawn(userEnv);
sharedProcess.spawn(await getShellEnvironment(this.logService, this.environmentService));
}, 3000)).schedule();
});
@@ -847,6 +851,9 @@ export class CodeApplication extends Disposable {
} catch (error) {
this.logService.error(error);
}
// Start to fetch shell environment after window has opened
getShellEnvironment(this.logService, this.environmentService);
}
private handleRemoteAuthorities(): void {
@@ -858,100 +865,3 @@ export class CodeApplication extends Disposable {
});
}
}
class ElectronExtensionHostDebugBroadcastChannel<TContext> extends ExtensionHostDebugBroadcastChannel<TContext> {
constructor(private windowsMainService: IWindowsMainService) {
super();
}
call(ctx: TContext, command: string, arg?: any): Promise<any> {
if (command === 'openExtensionDevelopmentHostWindow') {
return this.openExtensionDevelopmentHostWindow(arg[0], arg[1], arg[2]);
} else {
return super.call(ctx, command, arg);
}
}
private async openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment, debugRenderer: boolean): Promise<IOpenExtensionWindowResult> {
const pargs = parseArgs(args, OPTIONS);
const extDevPaths = pargs.extensionDevelopmentPath;
if (!extDevPaths) {
return {};
}
const [codeWindow] = this.windowsMainService.openExtensionDevelopmentHostWindow(extDevPaths, {
context: OpenContext.API,
cli: pargs,
userEnv: Object.keys(env).length > 0 ? env : undefined
});
if (!debugRenderer) {
return {};
}
const debug = codeWindow.win.webContents.debugger;
let listeners = debug.isAttached() ? Infinity : 0;
const server = createServer(listener => {
if (listeners++ === 0) {
debug.attach();
}
let closed = false;
const writeMessage = (message: object) => {
if (!closed) { // in case sendCommand promises settle after closed
listener.write(JSON.stringify(message) + '\0'); // null-delimited, CDP-compatible
}
};
const onMessage = (_event: Event, method: string, params: unknown, sessionId?: string) =>
writeMessage(({ method, params, sessionId }));
codeWindow.win.on('close', () => {
debug.removeListener('message', onMessage);
listener.end();
closed = true;
});
debug.addListener('message', onMessage);
let buf = Buffer.alloc(0);
listener.on('data', data => {
buf = Buffer.concat([buf, data]);
for (let delimiter = buf.indexOf(0); delimiter !== -1; delimiter = buf.indexOf(0)) {
let data: { id: number; sessionId: string; params: {} };
try {
const contents = buf.slice(0, delimiter).toString('utf8');
buf = buf.slice(delimiter + 1);
data = JSON.parse(contents);
} catch (e) {
console.error('error reading cdp line', e);
}
// depends on a new API for which electron.d.ts has not been updated:
// @ts-ignore
debug.sendCommand(data.method, data.params, data.sessionId)
.then((result: object) => writeMessage({ id: data.id, sessionId: data.sessionId, result }))
.catch((error: Error) => writeMessage({ id: data.id, sessionId: data.sessionId, error: { code: 0, message: error.message } }));
}
});
listener.on('error', err => {
console.error('error on cdp pipe:', err);
});
listener.on('close', () => {
closed = true;
if (--listeners === 0) {
debug.detach();
}
});
});
await new Promise(r => server.listen(0, r));
codeWindow.win.on('close', () => server.close());
return { rendererDebugPort: (server.address() as AddressInfo).port };
}
}
+50 -15
View File
@@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects';
import * as nls from 'vs/nls';
import { 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, Event } from 'electron';
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event, Details } from 'electron';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
import { ILogService } from 'vs/platform/log/common/log';
@@ -167,12 +167,23 @@ export class CodeWindow extends Disposable implements ICodeWindow {
title: product.nameLong,
webPreferences: {
preload: URI.parse(this.doGetPreloadUrl()).fsPath,
nodeIntegration: true,
enableWebSQL: false,
enableRemoteModule: false,
nativeWindowOpen: true,
webviewTag: true,
zoomFactor: zoomLevelToZoomFactor(windowConfig?.zoomLevel)
zoomFactor: zoomLevelToZoomFactor(windowConfig?.zoomLevel),
...this.environmentService.sandbox ?
// Sandbox
{
sandbox: true,
contextIsolation: true
} :
// No Sandbox
{
nodeIntegration: true
}
}
};
@@ -325,7 +336,18 @@ export class CodeWindow extends Disposable implements ICodeWindow {
return !!this.documentEdited;
}
focus(): void {
focus(options?: { force: boolean }): void {
// macOS: Electron >6.x changed its behaviour to not
// bring the application to the foreground when a window
// is focused programmatically. Only via `app.focus` and
// the option `steal: true` can you get the previous
// behaviour back. The only reason to use this option is
// when a window is getting focused while the application
// is not in the foreground.
if (isMacintosh && options?.force) {
app.focus({ steal: true });
}
if (!this._win) {
return;
}
@@ -392,7 +414,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
private registerListeners(): void {
// Crashes & Unrsponsive
this._win.webContents.on('crashed', () => this.onWindowError(WindowError.CRASHED));
this._win.webContents.on('render-process-gone', (event, details) => this.onWindowError(WindowError.CRASHED, details));
this._win.on('unresponsive', () => this.onWindowError(WindowError.UNRESPONSIVE));
// Window close
@@ -524,8 +546,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) })));
}
private onWindowError(error: WindowError): void {
this.logService.error(error === WindowError.CRASHED ? '[VS Code]: renderer process crashed!' : '[VS Code]: detected unresponsive');
private onWindowError(error: WindowError.UNRESPONSIVE): void;
private onWindowError(error: WindowError.CRASHED, details: Details): void;
private onWindowError(error: WindowError, details?: Details): void {
this.logService.error(error === WindowError.CRASHED ? `[VS Code]: renderer process crashed (detail: ${details?.reason})` : '[VS Code]: detected unresponsive');
// If we run extension tests from CLI, showing a dialog is not
// very helpful in this case. Rather, we bring down the test run
@@ -579,11 +603,18 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// Crashed
else {
let message: string;
if (details && details.reason !== 'crashed') {
message = nls.localize('appCrashedDetails', "The window has crashed (reason: '{0}')", details?.reason);
} else {
message = nls.localize('appCrashed', "The window has crashed", details?.reason);
}
this.dialogMainService.showMessageBox({
title: product.nameLong,
type: 'warning',
buttons: [mnemonicButtonLabel(nls.localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(nls.localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))],
message: nls.localize('appCrashed', "The window has crashed"),
message,
detail: nls.localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."),
noLink: true
}, this._win).then(result => {
@@ -699,7 +730,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this.showTimeoutHandle = setTimeout(() => {
if (this._win && !this._win.isVisible() && !this._win.isMinimized()) {
this._win.show();
this._win.focus();
this.focus({ force: true });
this._win.webContents.openDevTools();
}
}, 10000);
@@ -754,11 +785,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
windowConfiguration.fullscreen = this.isFullScreen;
// Set Accessibility Config
let autoDetectHighContrast = true;
if (windowConfig?.autoDetectHighContrast === false) {
autoDetectHighContrast = false;
}
windowConfiguration.highContrast = isWindows && autoDetectHighContrast && nativeTheme.shouldUseInvertedColorScheme;
windowConfiguration.highContrast = nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors;
windowConfiguration.autoDetectHighContrast = windowConfig?.autoDetectHighContrast ?? true;
windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled;
// Title style related
@@ -799,7 +827,14 @@ export class CodeWindow extends Disposable implements ICodeWindow {
}
private doGetUrl(config: object): string {
return `${require.toUrl('vs/code/electron-browser/workbench/workbench.html')}?config=${encodeURIComponent(JSON.stringify(config))}`;
let workbench: string;
if (this.environmentService.sandbox) {
workbench = 'vs/code/electron-sandbox/workbench/workbench.html';
} else {
workbench = 'vs/code/electron-browser/workbench/workbench.html';
}
return `${require.toUrl(workbench)}?config=${encodeURIComponent(JSON.stringify(config))}`;
}
private doGetPreloadUrl(): string {
@@ -8,7 +8,7 @@ import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is
import { ElectronService, IElectronService } from 'vs/platform/electron/electron-sandbox/electron';
import { ipcRenderer, process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { applyZoom, zoomIn, zoomOut } from 'vs/platform/windows/electron-sandbox/window';
import { $, windowOpenNoOpener, addClass } from 'vs/base/browser/dom';
import { $, reset, windowOpenNoOpener, addClass } from 'vs/base/browser/dom';
import { Button } from 'vs/base/browser/ui/button/button';
import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel';
import * as collections from 'vs/base/common/collections';
@@ -277,33 +277,25 @@ export class IssueReporter extends Disposable {
}
private updateSettingsSearchDetails(data: ISettingsSearchIssueReporterData): void {
const target = document.querySelector('.block-settingsSearchResults .block-info');
const target = document.querySelector<HTMLElement>('.block-settingsSearchResults .block-info');
if (target) {
const details = `
<div class='block-settingsSearchResults-details'>
<div>Query: "${data.query}"</div>
<div>Literal match count: ${data.filterResultCount}</div>
</div>
`;
const queryDiv = $<HTMLDivElement>('div', undefined, `Query: "${data.query}"` as string);
const countDiv = $<HTMLElement>('div', undefined, `Literal match count: ${data.filterResultCount}` as string);
const detailsDiv = $<HTMLDivElement>('.block-settingsSearchResults-details', undefined, queryDiv, countDiv);
let table = `
<tr>
<th>Setting</th>
<th>Extension</th>
<th>Score</th>
</tr>`;
data.actualSearchResults
.forEach(setting => {
table += `
<tr>
<td>${setting.key}</td>
<td>${setting.extensionId}</td>
<td>${String(setting.score).slice(0, 5)}</td>
</tr>`;
});
target.innerHTML = `${details}<table>${table}</table>`;
const table = $('table', undefined,
$('tr', undefined,
$('th', undefined, 'Setting'),
$('th', undefined, 'Extension'),
$('th', undefined, 'Score'),
),
...data.actualSearchResults.map(setting => $('tr', undefined,
$('td', undefined, setting.key),
$('td', undefined, setting.extensionId),
$('td', undefined, String(setting.score).slice(0, 5)),
))
);
reset(target, detailsDiv, table);
}
}
@@ -654,9 +646,9 @@ export class IssueReporter extends Disposable {
issueState.appendChild(issueIcon);
issueState.appendChild(issueStateLabel);
item = $('div.issue', {}, issueState, link);
item = $('div.issue', undefined, issueState, link);
} else {
item = $('div.issue', {}, link);
item = $('div.issue', undefined, link);
}
issues.appendChild(item);
@@ -672,19 +664,19 @@ export class IssueReporter extends Disposable {
}
private setUpTypes(): void {
const makeOption = (issueType: IssueType, description: string) => `<option value="${issueType.valueOf()}">${escape(description)}</option>`;
const makeOption = (issueType: IssueType, description: string) => $('option', { 'value': issueType.valueOf() }, escape(description));
const typeSelect = this.getElementById('issue-type')! as HTMLSelectElement;
const { issueType } = this.issueReporterModel.getData();
if (issueType === IssueType.SettingsSearchIssue) {
typeSelect.innerHTML = makeOption(IssueType.SettingsSearchIssue, localize('settingsSearchIssue', "Settings Search Issue"));
reset(typeSelect, makeOption(IssueType.SettingsSearchIssue, localize('settingsSearchIssue', "Settings Search Issue")));
typeSelect.disabled = true;
} else {
typeSelect.innerHTML = [
reset(typeSelect,
makeOption(IssueType.Bug, localize('bugReporter', "Bug Report")),
makeOption(IssueType.FeatureRequest, localize('featureRequest', "Feature Request")),
makeOption(IssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue"))
].join('\n');
);
}
typeSelect.value = issueType.toString();
@@ -774,9 +766,8 @@ export class IssueReporter extends Disposable {
} else {
show(extensionsBlock);
}
descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} <span class="required-input">*</span>`;
descriptionSubtitle.innerHTML = localize('bugDescription', "Share the steps needed to reliably reproduce the problem. Please include actual and expected results. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.");
reset(descriptionTitle, localize('stepsToReproduce', "Steps to Reproduce"), $('span.required-input', undefined, '*'));
reset(descriptionSubtitle, localize('bugDescription', "Share the steps needed to reliably reproduce the problem. Please include actual and expected results. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."));
} else if (issueType === IssueType.PerformanceIssue) {
show(blockContainer);
show(systemBlock);
@@ -790,11 +781,11 @@ export class IssueReporter extends Disposable {
show(extensionsBlock);
}
descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} <span class="required-input">*</span>`;
descriptionSubtitle.innerHTML = localize('performanceIssueDesciption', "When did this performance issue happen? Does it occur on startup or after a specific series of actions? We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.");
reset(descriptionTitle, localize('stepsToReproduce', "Steps to Reproduce"), $('span.required-input', undefined, '*'));
reset(descriptionSubtitle, localize('performanceIssueDesciption', "When did this performance issue happen? Does it occur on startup or after a specific series of actions? We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."));
} else if (issueType === IssueType.FeatureRequest) {
descriptionTitle.innerHTML = `${localize('description', "Description")} <span class="required-input">*</span>`;
descriptionSubtitle.innerHTML = localize('featureRequestDescription', "Please describe the feature you would like to see. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.");
reset(descriptionTitle, localize('description', "Description"), $('span.required-input', undefined, '*'));
reset(descriptionSubtitle, localize('featureRequestDescription', "Please describe the feature you would like to see. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."));
show(problemSource);
if (fileOnExtension) {
@@ -805,8 +796,8 @@ export class IssueReporter extends Disposable {
show(searchedExtensionsBlock);
show(settingsSearchResultsBlock);
descriptionTitle.innerHTML = `${localize('expectedResults', "Expected Results")} <span class="required-input">*</span>`;
descriptionSubtitle.innerHTML = localize('settingsSearchResultsDescription', "Please list the results that you were expecting to see when you searched with this query. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.");
reset(descriptionTitle, localize('expectedResults', "Expected Results"), $('span.required-input', undefined, '*'));
reset(descriptionSubtitle, localize('settingsSearchResultsDescription', "Please list the results that you were expecting to see when you searched with this query. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."));
}
}
@@ -928,42 +919,82 @@ export class IssueReporter extends Disposable {
}
private updateSystemInfo(state: IssueReporterModelData) {
const target = document.querySelector('.block-system .block-info');
const target = document.querySelector<HTMLElement>('.block-system .block-info');
if (target) {
const systemInfo = state.systemInfo!;
let renderedData = `
<table>
<tr><td>CPUs</td><td>${systemInfo.cpus}</td></tr>
<tr><td>GPU Status</td><td>${Object.keys(systemInfo.gpuStatus).map(key => `${key}: ${systemInfo.gpuStatus[key]}`).join('<br>')}</td></tr>
<tr><td>Load (avg)</td><td>${systemInfo.load}</td></tr>
<tr><td>Memory (System)</td><td>${systemInfo.memory}</td></tr>
<tr><td>Process Argv</td><td>${systemInfo.processArgs}</td></tr>
<tr><td>Screen Reader</td><td>${systemInfo.screenReader}</td></tr>
<tr><td>VM</td><td>${systemInfo.vmHint}</td></tr>
</table>`;
const renderedDataTable = $('table', undefined,
$('tr', undefined,
$('td', undefined, 'CPUs'),
$('td', undefined, systemInfo.cpus || ''),
),
$('tr', undefined,
$('td', undefined, 'GPU Status' as string),
$('td', undefined, Object.keys(systemInfo.gpuStatus).map(key => `${key}: ${systemInfo.gpuStatus[key]}`).join('\n')),
),
$('tr', undefined,
$('td', undefined, 'Load (avg)' as string),
$('td', undefined, systemInfo.load || ''),
),
$('tr', undefined,
$('td', undefined, 'Memory (System)' as string),
$('td', undefined, systemInfo.memory),
),
$('tr', undefined,
$('td', undefined, 'Process Argv' as string),
$('td', undefined, systemInfo.processArgs),
),
$('tr', undefined,
$('td', undefined, 'Screen Reader' as string),
$('td', undefined, systemInfo.screenReader),
),
$('tr', undefined,
$('td', undefined, 'VM'),
$('td', undefined, systemInfo.vmHint),
),
);
reset(target, renderedDataTable);
systemInfo.remoteData.forEach(remote => {
target.appendChild($<HTMLHRElement>('hr'));
if (isRemoteDiagnosticError(remote)) {
renderedData += `
<hr>
<table>
<tr><td>Remote</td><td>${remote.hostName}</td></tr>
<tr><td></td><td>${remote.errorMessage}</td></tr>
</table>`;
const remoteDataTable = $('table', undefined,
$('tr', undefined,
$('td', undefined, 'Remote'),
$('td', undefined, remote.hostName)
),
$('tr', undefined,
$('td', undefined, ''),
$('td', undefined, remote.errorMessage)
)
);
target.appendChild(remoteDataTable);
} else {
renderedData += `
<hr>
<table>
<tr><td>Remote</td><td>${remote.hostName}</td></tr>
<tr><td>OS</td><td>${remote.machineInfo.os}</td></tr>
<tr><td>CPUs</td><td>${remote.machineInfo.cpus}</td></tr>
<tr><td>Memory (System)</td><td>${remote.machineInfo.memory}</td></tr>
<tr><td>VM</td><td>${remote.machineInfo.vmHint}</td></tr>
</table>`;
const remoteDataTable = $('table', undefined,
$('tr', undefined,
$('td', undefined, 'Remote'),
$('td', undefined, remote.hostName)
),
$('tr', undefined,
$('td', undefined, 'OS'),
$('td', undefined, remote.machineInfo.os)
),
$('tr', undefined,
$('td', undefined, 'CPUs'),
$('td', undefined, remote.machineInfo.cpus || '')
),
$('tr', undefined,
$('td', undefined, 'Memory (System)' as string),
$('td', undefined, remote.machineInfo.memory)
),
$('tr', undefined,
$('td', undefined, 'VM'),
$('td', undefined, remote.machineInfo.vmHint)
),
);
target.appendChild(remoteDataTable);
}
});
target.innerHTML = renderedData;
}
}
@@ -995,15 +1026,18 @@ export class IssueReporter extends Disposable {
return 0;
});
const makeOption = (extension: IOption, selectedExtension?: IssueReporterExtensionData) => {
const makeOption = (extension: IOption, selectedExtension?: IssueReporterExtensionData): HTMLOptionElement => {
const selected = selectedExtension && extension.id === selectedExtension.id;
return `<option value="${extension.id}" ${selected ? 'selected' : ''}>${escape(extension.name)}</option>`;
return $<HTMLOptionElement>('option', {
'value': extension.id,
'selected': selected || ''
}, extension.name);
};
const extensionsSelector = this.getElementById('extension-selector');
if (extensionsSelector) {
const { selectedExtension } = this.issueReporterModel.getData();
extensionsSelector.innerHTML = '<option></option>' + extensionOptions.map(extension => makeOption(extension, selectedExtension)).join('\n');
reset(extensionsSelector, $<HTMLOptionElement>('option'), ...extensionOptions.map(extension => makeOption(extension, selectedExtension)));
this.addEventListener('extension-selector', 'change', (e: Event) => {
const selectedExtensionId = (<HTMLInputElement>e.target).value;
@@ -1071,9 +1105,9 @@ export class IssueReporter extends Disposable {
}
private updateProcessInfo(state: IssueReporterModelData) {
const target = document.querySelector('.block-process .block-info');
const target = document.querySelector('.block-process .block-info') as HTMLElement;
if (target) {
target.innerHTML = `<code>${state.processInfo}</code>`;
reset(target, $('code', undefined, state.processInfo));
}
}
@@ -1085,7 +1119,7 @@ export class IssueReporter extends Disposable {
const target = document.querySelector<HTMLElement>('.block-extensions .block-info');
if (target) {
if (this.configuration.disableExtensions) {
target.innerHTML = localize('disabledExtensions', "Extensions are disabled");
reset(target, localize('disabledExtensions', "Extensions are disabled"));
return;
}
@@ -1097,8 +1131,7 @@ export class IssueReporter extends Disposable {
return;
}
const table = this.getExtensionTableHtml(extensions);
target.innerHTML = `<table>${table}</table>${themeExclusionStr}`;
reset(target, this.getExtensionTableHtml(extensions), document.createTextNode(themeExclusionStr));
}
}
@@ -1111,28 +1144,24 @@ export class IssueReporter extends Disposable {
}
const table = this.getExtensionTableHtml(extensions);
target.innerHTML = `<table>${table}</table>`;
target.innerText = '';
target.appendChild(table);
}
}
private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): string {
let table = `
<tr>
<th>Extension</th>
<th>Author (truncated)</th>
<th>Version</th>
</tr>`;
table += extensions.map(extension => {
return `
<tr>
<td>${extension.name}</td>
<td>${extension.publisher.substr(0, 3)}</td>
<td>${extension.version}</td>
</tr>`;
}).join('');
return table;
private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): HTMLTableElement {
return $('table', undefined,
$('tr', undefined,
$('th', undefined, 'Extension'),
$('th', undefined, 'Author (truncated)' as string),
$('th', undefined, 'Version'),
),
...extensions.map(extension => $('tr', undefined,
$('td', undefined, extension.name),
$('td', undefined, extension.publisher.substr(0, 3)),
$('td', undefined, extension.version),
))
);
}
private openLink(event: MouseEvent): void {
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>

Before

Width:  |  Height:  |  Size: 139 B

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M11 10H5.344L11 4.414V10z"/></svg>

Before

Width:  |  Height:  |  Size: 118 B

@@ -58,6 +58,7 @@ table {
width: 100%;
table-layout: fixed;
}
th[scope='col'] {
vertical-align: bottom;
border-bottom: 1px solid #cccccc;
@@ -65,6 +66,7 @@ th[scope='col'] {
border-top: 1px solid #cccccc;
cursor: default;
}
td {
padding: .25rem;
vertical-align: top;
@@ -100,7 +102,6 @@ tbody > tr:hover {
display: none;
}
img {
width: 16px;
margin-right: 4px;
.header {
display: flex;
}
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/processExplorer';
import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded
import { ElectronService, IElectronService } from 'vs/platform/electron/electron-sandbox/electron';
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { localize } from 'vs/nls';
@@ -16,6 +17,7 @@ import { addDisposableListener, addClass } from 'vs/base/browser/dom';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel';
const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/;
const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/;
@@ -156,15 +158,15 @@ class ProcessExplorer {
return maxProcessId;
}
private updateSectionCollapsedState(shouldExpand: boolean, body: HTMLElement, twistie: HTMLImageElement, sectionName: string) {
private updateSectionCollapsedState(shouldExpand: boolean, body: HTMLElement, twistie: CodiconLabel, sectionName: string) {
if (shouldExpand) {
body.classList.remove('hidden');
this.collapsedStateCache.set(sectionName, false);
twistie.src = './media/expanded.svg';
twistie.text = '$(chevron-down)';
} else {
body.classList.add('hidden');
this.collapsedStateCache.set(sectionName, true);
twistie.src = './media/collapsed.svg';
twistie.text = '$(chevron-right)';
}
}
@@ -191,18 +193,27 @@ class ProcessExplorer {
private renderProcessGroupHeader(sectionName: string, body: HTMLElement, container: HTMLElement) {
const headerRow = document.createElement('tr');
const data = document.createElement('td');
data.textContent = sectionName;
data.colSpan = 4;
headerRow.appendChild(data);
const twistie = document.createElement('img');
this.updateSectionCollapsedState(!this.collapsedStateCache.get(sectionName), body, twistie, sectionName);
data.prepend(twistie);
const headerData = document.createElement('td');
headerData.colSpan = 4;
headerRow.appendChild(headerData);
this.listeners.add(addDisposableListener(data, 'click', (e) => {
const headerContainer = document.createElement('div');
headerContainer.className = 'header';
headerData.appendChild(headerContainer);
const twistieContainer = document.createElement('div');
const twistieCodicon = new CodiconLabel(twistieContainer);
this.updateSectionCollapsedState(!this.collapsedStateCache.get(sectionName), body, twistieCodicon, sectionName);
headerContainer.appendChild(twistieContainer);
const headerLabel = document.createElement('span');
headerLabel.textContent = sectionName;
headerContainer.appendChild(headerLabel);
this.listeners.add(addDisposableListener(headerData, 'click', (e) => {
const isHidden = body.classList.contains('hidden');
this.updateSectionCollapsedState(isHidden, body, twistie, sectionName);
this.updateSectionCollapsedState(isHidden, body, twistieCodicon, sectionName);
}));
container.appendChild(headerRow);
@@ -0,0 +1,18 @@
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: blob: vscode-remote-resource:; media-src 'none'; frame-src 'self' vscode-webview: https://*.vscode-webview-test.com; object-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https: vscode-remote-resource:;">
</head>
<body aria-label="">
</body>
<!-- Init Bootstrap Helpers -->
<script src="../../../../bootstrap.js"></script>
<script src="../../../../vs/loader.js"></script>
<script src="../../../../bootstrap-window.js"></script>
<!-- Startup via workbench.js -->
<script src="workbench.js"></script>
</html>
@@ -0,0 +1,74 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path="../../../../typings/require.d.ts" />
//@ts-check
'use strict';
const perf = (function () {
globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || [];
return {
/**
* @param {string} name
*/
mark(name) {
globalThis.MonacoPerformanceMarks.push(name, Date.now());
}
};
})();
perf.mark('renderer/started');
/**
* @type {{
* load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown,
* globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals')
* }}
*/
const bootstrapWindow = (() => {
// @ts-ignore (defined in bootstrap-window.js)
return window.MonacoBootstrapWindow;
})();
// Load environment in parallel to workbench loading to avoid waterfall
const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved;
// Load workbench main JS, CSS and NLS all in parallel. This is an
// optimization to prevent a waterfall of loading to happen, because
// we know for a fact that workbench.desktop.sandbox.main will depend on
// the related CSS and NLS counterparts.
bootstrapWindow.load([
'vs/workbench/workbench.desktop.sandbox.main',
'vs/nls!vs/workbench/workbench.desktop.main',
'vs/css!vs/workbench/workbench.desktop.main'
],
async function (workbench, configuration) {
// Mark start of workbench
perf.mark('didLoadWorkbenchMain');
performance.mark('workbench-start');
// Wait for process environment being fully resolved
await whenEnvResolved;
perf.mark('main/startup');
// @ts-ignore
return require('vs/workbench/electron-sandbox/desktop.main').main(configuration);
},
{
removeDeveloperKeybindingsAfterLoad: true,
canModifyDOM: function (windowConfig) {
// TODO@sandbox part-splash is non-sandboxed only
},
beforeLoaderConfig: function (windowConfig, loaderConfig) {
loaderConfig.recordStats = true;
},
beforeRequire: function () {
perf.mark('willLoadWorkbenchMain');
}
}
);
+1 -1
View File
@@ -14,7 +14,7 @@ import * as paths from 'vs/base/common/path';
import { whenDeleted, writeFileSync } from 'vs/base/node/pfs';
import { findFreePort, randomPort } from 'vs/base/node/ports';
import { isWindows, isLinux } from 'vs/base/common/platform';
import { ProfilingSession, Target } from 'v8-inspect-profiler';
import type { ProfilingSession, Target } from 'v8-inspect-profiler';
import { isString } from 'vs/base/common/types';
import { hasStdinWithoutTty, stdinDataListener, getStdinFilePath, readFromStdin } from 'vs/platform/environment/node/stdin';
+2 -1
View File
@@ -7,7 +7,6 @@ import { localize } from 'vs/nls';
import { raceTimeout } from 'vs/base/common/async';
import product from 'vs/platform/product/common/product';
import * as path from 'vs/base/common/path';
import * as semver from 'semver-umd';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -214,6 +213,8 @@ export class Main {
throw new Error('Invalid vsix');
}
const semver = await import('semver-umd');
const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) };
const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User);
const newer = installedExtensions.find(local => areSameExtensions(extensionIdentifier, local.identifier) && semver.gt(local.manifest.version, manifest.version));
+9 -10
View File
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import { spawn } from 'child_process';
import { generateUuid } from 'vs/base/common/uuid';
import { isWindows } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
@@ -30,7 +30,7 @@ function getUnixShellEnvironment(logService: ILogService): Promise<typeof proces
logService.trace('getUnixShellEnvironment#env', env);
logService.trace('getUnixShellEnvironment#spawn', command);
const child = cp.spawn(process.env.SHELL!, ['-ilc', command], {
const child = spawn(process.env.SHELL!, ['-ilc', command], {
detached: true,
stdio: ['ignore', 'pipe', process.stderr],
env
@@ -82,8 +82,7 @@ function getUnixShellEnvironment(logService: ILogService): Promise<typeof proces
return promise.catch(() => ({}));
}
let _shellEnv: Promise<typeof process.env>;
let shellEnvPromise: Promise<typeof process.env> | undefined = undefined;
/**
* We need to get the environment from a user's shell.
@@ -91,21 +90,21 @@ let _shellEnv: Promise<typeof process.env>;
* from within a shell.
*/
export function getShellEnvironment(logService: ILogService, environmentService: INativeEnvironmentService): Promise<typeof process.env> {
if (_shellEnv === undefined) {
if (!shellEnvPromise) {
if (environmentService.args['disable-user-env-probe']) {
logService.trace('getShellEnvironment: disable-user-env-probe set, skipping');
_shellEnv = Promise.resolve({});
shellEnvPromise = Promise.resolve({});
} else if (isWindows) {
logService.trace('getShellEnvironment: running on Windows, skipping');
_shellEnv = Promise.resolve({});
shellEnvPromise = Promise.resolve({});
} else if (process.env['VSCODE_CLI'] === '1' && process.env['VSCODE_FORCE_USER_ENV'] !== '1') {
logService.trace('getShellEnvironment: running on CLI, skipping');
_shellEnv = Promise.resolve({});
shellEnvPromise = Promise.resolve({});
} else {
logService.trace('getShellEnvironment: running on Unix');
_shellEnv = getUnixShellEnvironment(logService);
shellEnvPromise = getUnixShellEnvironment(logService);
}
}
return _shellEnv;
return shellEnvPromise;
}
@@ -28,9 +28,8 @@ suite('Windows Native Helpers', () => {
});
test('vscode-windows-ca-certs', async () => {
const windowsCerts = await new Promise<any>((resolve, reject) => {
require(['vscode-windows-ca-certs'], resolve, reject);
});
// @ts-ignore Windows only
const windowsCerts = await import('vscode-windows-ca-certs');
assert.ok(windowsCerts, 'Unable to load vscode-windows-ca-certs dependency.');
});
@@ -72,7 +72,7 @@ interface InMemoryClipboardMetadata {
* Every time we read from the cipboard, if the text matches our last written text,
* we can fetch the previous metadata.
*/
class InMemoryClipboardMetadataManager {
export class InMemoryClipboardMetadataManager {
public static readonly INSTANCE = new InMemoryClipboardMetadataManager();
private _lastState: InMemoryClipboardMetadata | null;
+8
View File
@@ -348,6 +348,14 @@ export interface IEditorConstructionOptions extends IEditorOptions {
overflowWidgetsDomNode?: HTMLElement;
}
export interface IDiffEditorConstructionOptions extends IDiffEditorOptions {
/**
* Place overflow widgets inside an external DOM node.
* Defaults to an internal DOM node.
*/
overflowWidgetsDomNode?: HTMLElement;
}
/**
* A rich code editor.
*/
@@ -4,13 +4,64 @@
*--------------------------------------------------------------------------------------------*/
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { WorkspaceEdit } from 'vs/editor/common/modes';
import { TextEdit, WorkspaceEdit, WorkspaceEditMetadata, WorkspaceFileEdit, WorkspaceFileEditOptions, WorkspaceTextEdit } from 'vs/editor/common/modes';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress';
import { IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { isObject } from 'vs/base/common/types';
export const IBulkEditService = createDecorator<IBulkEditService>('IWorkspaceEditService');
function isWorkspaceFileEdit(thing: any): thing is WorkspaceFileEdit {
return isObject(thing) && (Boolean((<WorkspaceFileEdit>thing).newUri) || Boolean((<WorkspaceFileEdit>thing).oldUri));
}
function isWorkspaceTextEdit(thing: any): thing is WorkspaceTextEdit {
return isObject(thing) && URI.isUri((<WorkspaceTextEdit>thing).resource) && isObject((<WorkspaceTextEdit>thing).edit);
}
export class ResourceEdit {
protected constructor(readonly metadata?: WorkspaceEditMetadata) { }
static convert(edit: WorkspaceEdit): ResourceEdit[] {
return edit.edits.map(edit => {
if (isWorkspaceTextEdit(edit)) {
return new ResourceTextEdit(edit.resource, edit.edit, edit.modelVersionId, edit.metadata);
}
if (isWorkspaceFileEdit(edit)) {
return new ResourceFileEdit(edit.oldUri, edit.newUri, edit.options, edit.metadata);
}
throw new Error('Unsupported edit');
});
}
}
export class ResourceTextEdit extends ResourceEdit {
constructor(
readonly resource: URI,
readonly textEdit: TextEdit,
readonly versionId?: number,
readonly metadata?: WorkspaceEditMetadata
) {
super(metadata);
}
}
export class ResourceFileEdit extends ResourceEdit {
constructor(
readonly oldResource: URI | undefined,
readonly newResource: URI | undefined,
readonly options?: WorkspaceFileEditOptions,
readonly metadata?: WorkspaceEditMetadata
) {
super(metadata);
}
}
export interface IBulkEditOptions {
editor?: ICodeEditor;
progress?: IProgress<IProgressStep>;
@@ -23,7 +74,7 @@ export interface IBulkEditResult {
ariaSummary: string;
}
export type IBulkEditPreviewHandler = (edit: WorkspaceEdit, options?: IBulkEditOptions) => Promise<WorkspaceEdit>;
export type IBulkEditPreviewHandler = (edits: ResourceEdit[], options?: IBulkEditOptions) => Promise<ResourceEdit[]>;
export interface IBulkEditService {
readonly _serviceBrand: undefined;
@@ -32,6 +83,5 @@ export interface IBulkEditService {
setPreviewHandler(handler: IBulkEditPreviewHandler): IDisposable;
apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise<IBulkEditResult>;
apply(edit: ResourceEdit[], options?: IBulkEditOptions): Promise<IBulkEditResult>;
}
@@ -72,7 +72,7 @@ export class DomReadingContext {
export class ViewLineOptions {
public readonly themeType: ThemeType;
public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'all';
public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all';
public readonly renderControlCharacters: boolean;
public readonly spaceWidth: number;
public readonly middotWidth: number;
@@ -217,7 +217,7 @@ export class SelectionsOverlay extends DynamicViewOverlay {
endStyle.top = CornerStyle.INTERN;
}
} else if (previousFrameTop) {
// Accept some hick-ups near the viewport edges to save on repaints
// Accept some hiccups near the viewport edges to save on repaints
startStyle.top = previousFrameTop.startStyle!.top;
endStyle.top = previousFrameTop.endStyle!.top;
}
@@ -239,7 +239,7 @@ export class SelectionsOverlay extends DynamicViewOverlay {
endStyle.bottom = CornerStyle.INTERN;
}
} else if (previousFrameBottom) {
// Accept some hick-ups near the viewport edges to save on repaints
// Accept some hiccups near the viewport edges to save on repaints
startStyle.bottom = previousFrameBottom.startStyle!.bottom;
endStyle.bottom = previousFrameBottom.endStyle!.bottom;
}
@@ -71,7 +71,7 @@ interface IEditorsZones {
modified: IMyViewZone[];
}
interface IDiffEditorWidgetStyle {
export interface IDiffEditorWidgetStyle {
// {{SQL CARBON EDIT}}
getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, reverse?: boolean): IEditorsDiffDecorationsWithZones;
setEnableSplitViewResizing(enableSplitViewResizing: boolean): void;
@@ -177,6 +177,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
private readonly _onDidUpdateDiff: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidUpdateDiff: Event<void> = this._onDidUpdateDiff.event;
private readonly _onDidContentSizeChange: Emitter<editorCommon.IContentSizeChangedEvent> = this._register(new Emitter<editorCommon.IContentSizeChangedEvent>());
public readonly onDidContentSizeChange: Event<editorCommon.IContentSizeChangedEvent> = this._onDidContentSizeChange.event;
private readonly id: number;
private _state: editorBrowser.DiffEditorState;
private _updatingDiffProgress: IProgressRunner | null;
@@ -230,7 +233,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
constructor(
domElement: HTMLElement,
options: IDiffEditorOptions,
options: editorBrowser.IDiffEditorConstructionOptions,
@IClipboardService clipboardService: IClipboardService,
@IEditorWorkerService editorWorkerService: IEditorWorkerService,
@IContextKeyService contextKeyService: IContextKeyService,
@@ -353,21 +356,19 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._diffComputationResult = null;
const leftContextKeyService = this._contextKeyService.createScoped();
leftContextKeyService.createKey('isInDiffLeftEditor', true);
const leftServices = new ServiceCollection();
leftServices.set(IContextKeyService, leftContextKeyService);
const leftScopedInstantiationService = instantiationService.createChild(leftServices);
const rightContextKeyService = this._contextKeyService.createScoped();
rightContextKeyService.createKey('isInDiffRightEditor', true);
const rightServices = new ServiceCollection();
rightServices.set(IContextKeyService, rightContextKeyService);
const rightScopedInstantiationService = instantiationService.createChild(rightServices);
this.originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService);
this.modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService);
this.originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService, leftContextKeyService);
this.modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService, rightContextKeyService);
this._originalOverviewRuler = null;
this._modifiedOverviewRuler = null;
@@ -377,8 +378,6 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._containerDomElement.appendChild(this._reviewPane.shadow.domNode);
this._containerDomElement.appendChild(this._reviewPane.actionBarContainer.domNode);
// enableSplitViewResizing
this._enableSplitViewResizing = true;
if (typeof options.enableSplitViewResizing !== 'undefined') {
@@ -426,6 +425,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
return this._renderIndicators;
}
public getContentHeight(): number {
return this.modifiedEditor.getContentHeight();
}
private _setState(newState: editorBrowser.DiffEditorState): void {
if (this._state === newState) {
return;
@@ -485,7 +488,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._layoutOverviewRulers();
}
private _createLeftHandSideEditor(options: IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget {
private _createLeftHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget {
const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable, this._originalCodeLens));
this._register(editor.onDidScrollChange((e) => {
@@ -515,10 +518,26 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
}));
const isInDiffLeftEditorKey = contextKeyService.createKey<boolean>('isInDiffLeftEditor', undefined);
this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true)));
this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false)));
this._register(editor.onDidContentSizeChange(e => {
const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH;
const height = Math.max(this.modifiedEditor.getContentHeight(), this.originalEditor.getContentHeight());
this._onDidContentSizeChange.fire({
contentHeight: height,
contentWidth: width,
contentHeightChanged: e.contentHeightChanged,
contentWidthChanged: e.contentWidthChanged
});
}));
return editor;
}
private _createRightHandSideEditor(options: IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget {
private _createRightHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget {
const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options, this._modifiedCodeLens));
this._register(editor.onDidScrollChange((e) => {
@@ -560,6 +579,22 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
}));
const isInDiffRightEditorKey = contextKeyService.createKey<boolean>('isInDiffRightEditor', undefined);
this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true)));
this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false)));
this._register(editor.onDidContentSizeChange(e => {
const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH;
const height = Math.max(this.modifiedEditor.getContentHeight(), this.originalEditor.getContentHeight());
this._onDidContentSizeChange.fire({
contentHeight: height,
contentWidth: width,
contentHeightChanged: e.contentHeightChanged,
contentWidthChanged: e.contentWidthChanged
});
}));
return editor;
}
@@ -1064,8 +1099,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
}
private _adjustOptionsForSubEditor(options: IDiffEditorOptions): IDiffEditorOptions {
let clonedOptions: IDiffEditorOptions = objects.deepClone(options || {});
private _adjustOptionsForSubEditor(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IDiffEditorConstructionOptions {
let clonedOptions: editorBrowser.IDiffEditorConstructionOptions = objects.deepClone(options || {});
clonedOptions.inDiffEditor = true;
clonedOptions.wordWrap = 'off';
clonedOptions.wordWrapMinified = false;
@@ -1075,6 +1110,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
clonedOptions.folding = false;
clonedOptions.codeLens = false;
clonedOptions.fixedOverflowWidgets = true;
clonedOptions.overflowWidgetsDomNode = options.overflowWidgetsDomNode;
// clonedOptions.lineDecorationsWidth = '2ch';
if (!clonedOptions.minimap) {
clonedOptions.minimap = {};
@@ -1083,7 +1119,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
return clonedOptions;
}
private _adjustOptionsForLeftHandSide(options: IDiffEditorOptions, isEditable: boolean, isCodeLensEnabled: boolean): IEditorOptions {
private _adjustOptionsForLeftHandSide(options: editorBrowser.IDiffEditorConstructionOptions, isEditable: boolean, isCodeLensEnabled: boolean): editorBrowser.IEditorConstructionOptions {
let result = this._adjustOptionsForSubEditor(options);
if (isCodeLensEnabled) {
result.codeLens = true;
@@ -1093,7 +1129,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
return result;
}
private _adjustOptionsForRightHandSide(options: IDiffEditorOptions, isCodeLensEnabled: boolean): IEditorOptions {
private _adjustOptionsForRightHandSide(options: editorBrowser.IDiffEditorConstructionOptions, isCodeLensEnabled: boolean): editorBrowser.IEditorConstructionOptions {
let result = this._adjustOptionsForSubEditor(options);
if (isCodeLensEnabled) {
result.codeLens = true;
@@ -1628,14 +1664,14 @@ abstract class ViewZonesComputer {
protected abstract _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null;
}
function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) {
export function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) {
return {
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
options: options
};
}
const DECORATIONS = {
export const DECORATIONS = {
charDelete: ModelDecorationOptions.register({
className: 'char-delete'
@@ -1683,7 +1719,7 @@ const DECORATIONS = {
};
class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider {
export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider {
static readonly MINIMUM_EDITOR_WIDTH = 100;
@@ -2211,11 +2247,11 @@ class InlineViewZonesComputer extends ViewZonesComputer {
}
}
function isChangeOrInsert(lineChange: editorCommon.IChange): boolean {
export function isChangeOrInsert(lineChange: editorCommon.IChange): boolean {
return lineChange.modifiedEndLineNumber > 0;
}
function isChangeOrDelete(lineChange: editorCommon.IChange): boolean {
export function isChangeOrDelete(lineChange: editorCommon.IChange): boolean {
return lineChange.originalEndLineNumber > 0;
}
+57 -22
View File
@@ -529,7 +529,7 @@ export interface IEditorOptions {
* Enable rendering of whitespace.
* Defaults to none.
*/
renderWhitespace?: 'none' | 'boundary' | 'selection' | 'all';
renderWhitespace?: 'none' | 'boundary' | 'selection' | 'trailing' | 'all';
/**
* Enable rendering of control characters.
* Defaults to false.
@@ -856,15 +856,13 @@ class EditorBooleanOption<K1 extends EditorOption> extends SimpleEditorOption<K1
class EditorIntOption<K1 extends EditorOption> extends SimpleEditorOption<K1, number> {
public static clampedInt(value: any, defaultValue: number, minimum: number, maximum: number): number {
let r: number;
public static clampedInt<T>(value: any, defaultValue: T, minimum: number, maximum: number): number | T {
if (typeof value === 'undefined') {
r = defaultValue;
} else {
r = parseInt(value, 10);
if (isNaN(r)) {
r = defaultValue;
}
return defaultValue;
}
let r = parseInt(value, 10);
if (isNaN(r)) {
return defaultValue;
}
r = Math.max(minimum, r);
r = Math.min(maximum, r);
@@ -1348,7 +1346,7 @@ class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions>
nls.localize('editor.find.autoFindInSelection.always', 'Always turn on Find in selection automatically'),
nls.localize('editor.find.autoFindInSelection.multiline', 'Turn on Find in selection automatically when multiple lines of content are selected.')
],
description: nls.localize('find.autoFindInSelection', "Controls whether the find operation is carried out on selected text or the entire file in the editor.")
description: nls.localize('find.autoFindInSelection', "Controls the condition for turning on find in selection automatically.")
},
'editor.find.globalFindClipboard': {
type: 'boolean',
@@ -1491,6 +1489,48 @@ class EditorFontSize extends SimpleEditorOption<EditorOption.fontSize, number> {
//#endregion
//#region fontWeight
class EditorFontWeight extends BaseEditorOption<EditorOption.fontWeight, string> {
private static SUGGESTION_VALUES = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'];
private static MINIMUM_VALUE = 1;
private static MAXIMUM_VALUE = 1000;
constructor() {
super(
EditorOption.fontWeight, 'fontWeight', EDITOR_FONT_DEFAULTS.fontWeight,
{
anyOf: [
{
type: 'number',
minimum: EditorFontWeight.MINIMUM_VALUE,
maximum: EditorFontWeight.MAXIMUM_VALUE,
errorMessage: nls.localize('fontWeightErrorMessage', "Only \"normal\" and \"bold\" keywords or numbers between 1 and 1000 are allowed.")
},
{
type: 'string',
pattern: '^(normal|bold|1000|[1-9][0-9]{0,2})$'
},
{
enum: EditorFontWeight.SUGGESTION_VALUES
}
],
default: EDITOR_FONT_DEFAULTS.fontWeight,
description: nls.localize('fontWeight', "Controls the font weight. Accepts \"normal\" and \"bold\" keywords or numbers between 1 and 1000.")
}
);
}
public validate(input: any): string {
if (input === 'normal' || input === 'bold') {
return input;
}
return String(EditorIntOption.clampedInt(input, EDITOR_FONT_DEFAULTS.fontWeight, EditorFontWeight.MINIMUM_VALUE, EditorFontWeight.MAXIMUM_VALUE));
}
}
//#endregion
//#region gotoLocation
export type GoToLocationValues = 'peek' | 'gotoAndPeek' | 'goto';
@@ -1824,7 +1864,7 @@ export interface EditorLayoutInfoComputerEnv {
readonly memory: ComputeOptionsMemory | null;
readonly outerWidth: number;
readonly outerHeight: number;
readonly isDominatedByLongLines: boolean
readonly isDominatedByLongLines: boolean;
readonly lineHeight: number;
readonly viewLineCount: number;
readonly lineNumbersDigitCount: number;
@@ -1839,7 +1879,7 @@ export interface EditorLayoutInfoComputerEnv {
export interface IEditorLayoutComputerInput {
readonly outerWidth: number;
readonly outerHeight: number;
readonly isDominatedByLongLines: boolean
readonly isDominatedByLongLines: boolean;
readonly lineHeight: number;
readonly lineNumbersDigitCount: number;
readonly typicalHalfwidthCharacterWidth: number;
@@ -3102,7 +3142,7 @@ export interface ISuggestOptions {
* Controls the visibility of the status bar at the bottom of the suggest widget.
*/
visible?: boolean;
}
};
}
export type InternalSuggestOptions = Readonly<Required<ISuggestOptions>>;
@@ -3869,13 +3909,7 @@ export const EditorOptions = {
fontInfo: register(new EditorFontInfo()),
fontLigatures2: register(new EditorFontLigatures()),
fontSize: register(new EditorFontSize()),
fontWeight: register(new EditorStringOption(
EditorOption.fontWeight, 'fontWeight', EDITOR_FONT_DEFAULTS.fontWeight,
{
enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
description: nls.localize('fontWeight', "Controls the font weight.")
}
)),
fontWeight: register(new EditorFontWeight()),
formatOnPaste: register(new EditorBooleanOption(
EditorOption.formatOnPaste, 'formatOnPaste', false,
{ description: nls.localize('formatOnPaste', "Controls whether the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document.") }
@@ -4054,13 +4088,14 @@ export const EditorOptions = {
)),
renderWhitespace: register(new EditorStringEnumOption(
EditorOption.renderWhitespace, 'renderWhitespace',
'selection' as 'selection' | 'none' | 'boundary' | 'all',
['none', 'boundary', 'selection', 'all'] as const,
'selection' as 'selection' | 'none' | 'boundary' | 'trailing' | 'all',
['none', 'boundary', 'selection', 'trailing', 'all'] as const,
{
enumDescriptions: [
'',
nls.localize('renderWhitespace.boundary', "Render whitespace characters except for single spaces between words."),
nls.localize('renderWhitespace.selection', "Render whitespace characters only on selected text."),
nls.localize('renderWhitespace.trailing', "Render only trailing whitespace characters"),
''
],
description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters.")
+1
View File
@@ -1312,6 +1312,7 @@ export interface IReadonlyTextBuffer {
getLinesContent(): string[];
getLineContent(lineNumber: number): string;
getLineCharCode(lineNumber: number, index: number): number;
getCharCode(offset: number): number;
getLineLength(lineNumber: number): number;
getLineFirstNonWhitespaceColumn(lineNumber: number): number;
getLineLastNonWhitespaceColumn(lineNumber: number): number;
@@ -626,8 +626,7 @@ export class PieceTreeBase {
return this._lastVisitedLine.value;
}
public getLineCharCode(lineNumber: number, index: number): number {
let nodePos = this.nodeAt2(lineNumber, index + 1);
private _getCharCode(nodePos: NodePosition): number {
if (nodePos.remainder === nodePos.node.piece.length) {
// the char we want to fetch is at the head of next node.
let matchingNode = nodePos.node.next();
@@ -647,6 +646,11 @@ export class PieceTreeBase {
}
}
public getLineCharCode(lineNumber: number, index: number): number {
let nodePos = this.nodeAt2(lineNumber, index + 1);
return this._getCharCode(nodePos);
}
public getLineLength(lineNumber: number): number {
if (lineNumber === this.getLineCount()) {
let startOffset = this.getOffsetAt(lineNumber, 1);
@@ -655,6 +659,11 @@ export class PieceTreeBase {
return this.getOffsetAt(lineNumber + 1, 1) - this.getOffsetAt(lineNumber, 1) - this._EOLLength;
}
public getCharCode(offset: number): number {
let nodePos = this.nodeAt(offset);
return this._getCharCode(nodePos);
}
public findMatchesInNode(node: TreeNode, searcher: Searcher, startLineNumber: number, startColumn: number, startCursor: BufferCursor, endCursor: BufferCursor, searchData: SearchData, captureMatches: boolean, limitResultCount: number, resultLen: number, result: FindMatch[]) {
let buffer = this._buffers[node.piece.bufferIndex];
let startOffsetInBuffer = this.offsetInBuffer(node.piece.bufferIndex, node.piece.start);
@@ -178,6 +178,10 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable {
return this._pieceTree.getLineCharCode(lineNumber, index);
}
public getCharCode(offset: number): number {
return this._pieceTree.getCharCode(offset);
}
public getLineLength(lineNumber: number): number {
return this._pieceTree.getLineLength(lineNumber);
}
-24
View File
@@ -8,7 +8,6 @@ import { Color } from 'vs/base/common/color';
import { Event } from 'vs/base/common/event';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable } from 'vs/base/common/lifecycle';
import { isObject } from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
@@ -1337,29 +1336,6 @@ export class FoldingRangeKind {
}
}
/**
* @internal
*/
export namespace WorkspaceFileEdit {
/**
* @internal
*/
export function is(thing: any): thing is WorkspaceFileEdit {
return isObject(thing) && (Boolean((<WorkspaceFileEdit>thing).newUri) || Boolean((<WorkspaceFileEdit>thing).oldUri));
}
}
/**
* @internal
*/
export namespace WorkspaceTextEdit {
/**
* @internal
*/
export function is(thing: any): thing is WorkspaceTextEdit {
return isObject(thing) && URI.isUri((<WorkspaceTextEdit>thing).resource) && isObject((<WorkspaceTextEdit>thing).edit);
}
}
export interface WorkspaceEditMetadata {
needsConfirmation: boolean;
@@ -399,10 +399,10 @@ export function generateTokensCSSForColorMap(colorMap: Color[]): string {
let rules: string[] = [];
for (let i = 1, len = colorMap.length; i < len; i++) {
let color = colorMap[i];
rules[i] = `.mtk${i} { color: ${color}; }`;
rules[i] = `.monaco-editor .mtk${i} { color: ${color}; }`;
}
rules.push('.mtki { font-style: italic; }');
rules.push('.mtkb { font-weight: bold; }');
rules.push('.mtku { text-decoration: underline; text-underline-position: under; }');
rules.push('.monaco-editor .mtki { font-style: italic; }');
rules.push('.monaco-editor .mtkb { font-weight: bold; }');
rules.push('.monaco-editor .mtku { text-decoration: underline; text-underline-position: under; }');
return rules.join('\n');
}
@@ -7,7 +7,6 @@ import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent } from 'vs/editor/common/modes';
import { toArray } from 'vs/base/common/arrays';
export class TokenizationRegistryImpl implements ITokenizationRegistry {
@@ -82,7 +81,7 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry {
public setColorMap(colorMap: Color[]): void {
this._colorMap = colorMap;
this._onDidChange.fire({
changedLanguages: toArray(this._map.keys()),
changedLanguages: Array.from(this._map.keys()),
changedColorMap: true
});
}
@@ -54,6 +54,11 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe
return classes;
}
export function getIconClassesForModeId(modeId: string): string[] {
return ['file-icon', `${cssEscape(modeId)}-lang-file-icon`];
}
export function detectModeId(modelService: IModelService, modeService: IModeService, resource: uri): string | null {
if (!resource) {
return null; // we need a resource at least
@@ -14,7 +14,8 @@ export const enum RenderWhitespace {
None = 0,
Boundary = 1,
Selection = 2,
All = 3
Trailing = 3,
All = 4
}
export const enum LinePartMetadata {
@@ -113,7 +114,7 @@ export class RenderLineInput {
middotWidth: number,
wsmiddotWidth: number,
stopRenderingLineAfter: number,
renderWhitespace: 'none' | 'boundary' | 'selection' | 'all',
renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all',
renderControlCharacters: boolean,
fontLigatures: boolean,
selectionsOnLine: LineRange[] | null
@@ -138,7 +139,9 @@ export class RenderLineInput {
? RenderWhitespace.Boundary
: renderWhitespace === 'selection'
? RenderWhitespace.Selection
: RenderWhitespace.None
: renderWhitespace === 'trailing'
? RenderWhitespace.Trailing
: RenderWhitespace.None
);
this.renderControlCharacters = renderControlCharacters;
this.fontLigatures = fontLigatures;
@@ -435,7 +438,11 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
}
let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len);
if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary || (input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine)) {
if (input.renderWhitespace === RenderWhitespace.All ||
input.renderWhitespace === RenderWhitespace.Boundary ||
(input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine) ||
input.renderWhitespace === RenderWhitespace.Trailing) {
tokens = _applyRenderWhitespace(input, lineContent, len, tokens);
}
let containsForeignElements = ForeignElementType.None;
@@ -592,6 +599,7 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len
const useMonospaceOptimizations = input.useMonospaceOptimizations;
const selections = input.selectionsOnLine;
const onlyBoundary = (input.renderWhitespace === RenderWhitespace.Boundary);
const onlyTrailing = (input.renderWhitespace === RenderWhitespace.Trailing);
const generateLinePartForEachWhitespace = (input.renderSpaceWidth !== input.spaceWidth);
let result: LinePart[] = [], resultLen = 0;
@@ -600,10 +608,11 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len
let tokenEndIndex = tokens[tokenIndex].endIndex;
const tokensLength = tokens.length;
let lineIsEmptyOrWhitespace = false;
let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
let lastNonWhitespaceIndex: number;
if (firstNonWhitespaceIndex === -1) {
// The entire line is whitespace
lineIsEmptyOrWhitespace = true;
firstNonWhitespaceIndex = len;
lastNonWhitespaceIndex = len;
} else {
@@ -651,6 +660,11 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len
isInWhitespace = !!currentSelection && currentSelection.startOffset <= charIndex && currentSelection.endOffset > charIndex;
}
// If rendering only trailing whitespace, check that the charIndex points to trailing whitespace.
if (isInWhitespace && onlyTrailing) {
isInWhitespace = lineIsEmptyOrWhitespace || charIndex > lastNonWhitespaceIndex;
}
if (wasInWhitespace) {
// was in whitespace token
if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) {
+58 -15
View File
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import * as browser from 'vs/base/browser/browser';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as platform from 'vs/base/common/platform';
import { CopyOptions } from 'vs/editor/browser/controller/textAreaInput';
import { CopyOptions, InMemoryClipboardMetadataManager } from 'vs/editor/browser/controller/textAreaInput';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, registerEditorAction, Command, MultiCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
@@ -16,6 +16,8 @@ import { MenuId } from 'vs/platform/actions/common/actions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { Handler } from 'vs/editor/common/editorCommon';
const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste';
@@ -23,10 +25,9 @@ const supportsCut = (platform.isNative || document.queryCommandSupported('cut'))
const supportsCopy = (platform.isNative || document.queryCommandSupported('copy'));
// IE and Edge have trouble with setting html content in clipboard
const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdge);
// Chrome incorrectly returns true for document.queryCommandSupported('paste')
// when the paste feature is available but the calling script has insufficient
// privileges to actually perform the action
const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste')));
// Firefox only supports navigator.clipboard.readText() in browser extensions.
// See https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText#Browser_compatibility
const supportsPaste = (browser.isFirefox ? document.queryCommandSupported('paste') : true);
function registerCommand<T extends Command>(command: T): T {
command.register();
@@ -160,7 +161,7 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction {
}
}
function registerExecCommandImpl(target: MultiCommand | undefined, browserCommand: 'cut' | 'copy' | 'paste'): void {
function registerExecCommandImpl(target: MultiCommand | undefined, browserCommand: 'cut' | 'copy'): void {
if (!target) {
return;
}
@@ -170,13 +171,11 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman
// Only if editor text focus (i.e. not if editor has widget focus).
const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
if (focusedEditor && focusedEditor.hasTextFocus()) {
if (browserCommand === 'cut' || browserCommand === 'copy') {
// Do not execute if there is no selection and empty selection clipboard is off
const emptySelectionClipboard = focusedEditor.getOption(EditorOption.emptySelectionClipboard);
const selection = focusedEditor.getSelection();
if (selection && selection.isEmpty() && !emptySelectionClipboard) {
return true;
}
// Do not execute if there is no selection and empty selection clipboard is off
const emptySelectionClipboard = focusedEditor.getOption(EditorOption.emptySelectionClipboard);
const selection = focusedEditor.getSelection();
if (selection && selection.isEmpty() && !emptySelectionClipboard) {
return true;
}
document.execCommand(browserCommand);
return true;
@@ -186,7 +185,6 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman
// 2. (default) handle case when focus is somewhere else.
target.addImplementation(0, (accessor: ServicesAccessor, args: any) => {
// Only if editor text focus (i.e. not if editor has widget focus).
document.execCommand(browserCommand);
return true;
});
@@ -194,7 +192,52 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman
registerExecCommandImpl(CutAction, 'cut');
registerExecCommandImpl(CopyAction, 'copy');
registerExecCommandImpl(PasteAction, 'paste');
if (PasteAction) {
// 1. Paste: handle case when focus is in editor.
PasteAction.addImplementation(10000, (accessor: ServicesAccessor, args: any) => {
const codeEditorService = accessor.get(ICodeEditorService);
const clipboardService = accessor.get(IClipboardService);
// Only if editor text focus (i.e. not if editor has widget focus).
const focusedEditor = codeEditorService.getFocusedCodeEditor();
if (focusedEditor && focusedEditor.hasTextFocus()) {
const result = document.execCommand('paste');
// Use the clipboard service if document.execCommand('paste') was not successful
if (!result && platform.isWeb) {
(async () => {
const clipboardText = await clipboardService.readText();
if (clipboardText !== '') {
const metadata = InMemoryClipboardMetadataManager.INSTANCE.get(clipboardText);
let pasteOnNewLine = false;
let multicursorText: string[] | null = null;
let mode: string | null = null;
if (metadata) {
pasteOnNewLine = (focusedEditor.getOption(EditorOption.emptySelectionClipboard) && !!metadata.isFromEmptySelection);
multicursorText = (typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null);
mode = metadata.mode;
}
focusedEditor.trigger('keyboard', Handler.Paste, {
text: clipboardText,
pasteOnNewLine,
multicursorText,
mode
});
}
})();
return true;
}
return true;
}
return false;
});
// 2. Paste: (default) handle case when focus is somewhere else.
PasteAction.addImplementation(0, (accessor: ServicesAccessor, args: any) => {
document.execCommand('paste');
return true;
});
}
if (supportsCopyWithSyntaxHighlighting) {
registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction);
@@ -11,7 +11,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService';
import { IPosition } from 'vs/editor/common/core/position';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
@@ -163,7 +163,7 @@ export async function applyCodeAction(
});
if (action.edit) {
await bulkEditService.apply(action.edit, { editor, label: action.title });
await bulkEditService.apply(ResourceEdit.convert(action.edit), { editor, label: action.title });
}
if (action.command) {
@@ -89,6 +89,7 @@ function createCodeActionKeybinding(keycode: KeyCode, command: string, commandAr
command,
commandArgs,
undefined,
false);
false,
null);
}
+1 -4
View File
@@ -20,7 +20,6 @@ import { MenuId } from 'vs/platform/actions/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
@@ -371,7 +370,6 @@ export class CommonFindController extends Disposable implements IEditorContribut
public async getGlobalBufferTerm(): Promise<string> {
if (this._editor.getOption(EditorOption.find).globalFindClipboard
&& this._clipboardService
&& this._editor.hasModel()
&& !this._editor.getModel().isTooLargeForSyncing()
) {
@@ -382,7 +380,6 @@ export class CommonFindController extends Disposable implements IEditorContribut
public setGlobalBufferTerm(text: string): void {
if (this._editor.getOption(EditorOption.find).globalFindClipboard
&& this._clipboardService
&& this._editor.hasModel()
&& !this._editor.getModel().isTooLargeForSyncing()
) {
@@ -406,7 +403,7 @@ export class FindController extends CommonFindController implements IFindControl
@INotificationService private readonly _notificationService: INotificationService,
@IStorageService _storageService: IStorageService,
@IStorageKeysSyncRegistryService private readonly _storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@optional(IClipboardService) clipboardService: IClipboardService,
@IClipboardService clipboardService: IClipboardService,
) {
super(editor, _contextKeyService, _storageService, clipboardService);
this._widget = null;
+1 -1
View File
@@ -6,7 +6,7 @@
/* Find widget */
.monaco-editor .find-widget {
position: absolute;
z-index: 20;
z-index: 50;
height: 33px;
overflow: hidden;
line-height: 19px;
@@ -267,14 +267,16 @@ class FormatSelectionAction extends EditorAction {
}
const instaService = accessor.get(IInstantiationService);
const model = editor.getModel();
let range: Range = editor.getSelection();
if (range.isEmpty()) {
range = new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber));
}
const ranges = editor.getSelections().map(range => {
return range.isEmpty()
? new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber))
: range;
});
const progressService = accessor.get(IEditorProgressService);
await progressService.showWhile(
instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, range, FormattingMode.Explicit, Progress.None, CancellationToken.None),
instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, ranges, FormattingMode.Explicit, Progress.None, CancellationToken.None),
250
);
}
@@ -971,7 +971,7 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
return;
}
const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model);
const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model) && this.editor.getOption(EditorOption.occurrencesHighlight);
let allMatches = model.findMatches(this.state.searchText, true, false, this.state.matchCase, this.state.wordSeparators, false).map(m => m.range);
allMatches.sort(Range.compareRangesUsingStarts);
+10 -3
View File
@@ -46,6 +46,8 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
return editor.getContribution<OnTypeRenameContribution>(OnTypeRenameContribution.ID);
}
private _debounceDuration = 200;
private readonly _editor: ICodeEditor;
private _enabled: boolean;
@@ -121,15 +123,15 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
this._languageWordPattern = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id);
}));
const rangeUpdateScheduler = new Delayer(200);
const rangeUpdateScheduler = new Delayer(this._debounceDuration);
const triggerRangeUpdate = () => {
this._rangeUpdateTriggerPromise = rangeUpdateScheduler.trigger(() => this.updateRanges());
this._rangeUpdateTriggerPromise = rangeUpdateScheduler.trigger(() => this.updateRanges(), this._debounceDuration);
};
const rangeSyncScheduler = new Delayer(0);
const triggerRangeSync = (decorations: string[]) => {
this._rangeSyncTriggerPromise = rangeSyncScheduler.trigger(() => this._syncRanges(decorations));
};
this._localToDispose.add(this._editor.onDidChangeCursorPosition((e) => {
this._localToDispose.add(this._editor.onDidChangeCursorPosition(() => {
triggerRangeUpdate();
}));
this._localToDispose.add(this._editor.onDidChangeModelContent((e) => {
@@ -332,6 +334,11 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
return request;
}
// for testing
public setDebounceDuration(timeInMS: number) {
this._debounceDuration = timeInMS;
}
// private printDecorators(model: ITextModel) {
// return this._currentDecorations.map(d => {
// const range = model.getDecorationRange(d);
+2 -2
View File
@@ -22,7 +22,7 @@ import { MessageController } from 'vs/editor/contrib/message/messageController';
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/browser/core/editorState';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService';
import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
@@ -226,7 +226,7 @@ class RenameController implements IEditorContribution {
return;
}
this._bulkEditService.apply(renameResult, {
this._bulkEditService.apply(ResourceEdit.convert(renameResult), {
editor: this.editor,
showPreview: inputFieldResult.wantsPreview,
label: nls.localize('label', "Renaming '{0}'", loc?.text),
@@ -79,6 +79,7 @@ suite('On type rename', () => {
OnTypeRenameContribution.ID,
OnTypeRenameContribution
);
ontypeRenameContribution.setDebounceDuration(0);
const testEditor: TestEditor = {
setPosition(pos: Position) {
@@ -18,6 +18,7 @@ import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ILogService } from 'vs/platform/log/common/log';
import { SnippetSession } from './snippetSession';
import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer';
export interface ISnippetInsertOptions {
overwriteBefore: number;
@@ -26,6 +27,7 @@ export interface ISnippetInsertOptions {
undoStopBefore: boolean;
undoStopAfter: boolean;
clipboardText: string | undefined;
overtypingCapturer: OvertypingCapturer | undefined;
}
const _defaultOptions: ISnippetInsertOptions = {
@@ -34,7 +36,8 @@ const _defaultOptions: ISnippetInsertOptions = {
undoStopBefore: true,
undoStopAfter: true,
adjustWhitespace: true,
clipboardText: undefined
clipboardText: undefined,
overtypingCapturer: undefined
};
export class SnippetController2 implements IEditorContribution {
@@ -23,6 +23,7 @@ import * as colors from 'vs/platform/theme/common/colorRegistry';
import { withNullAsUndefined } from 'vs/base/common/types';
import { ILabelService } from 'vs/platform/label/common/label';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer';
registerThemingParticipant((theme, collector) => {
@@ -319,13 +320,15 @@ export interface ISnippetSessionInsertOptions {
overwriteAfter: number;
adjustWhitespace: boolean;
clipboardText: string | undefined;
overtypingCapturer: OvertypingCapturer | undefined;
}
const _defaultOptions: ISnippetSessionInsertOptions = {
overwriteBefore: 0,
overwriteAfter: 0,
adjustWhitespace: true,
clipboardText: undefined
clipboardText: undefined,
overtypingCapturer: undefined
};
export class SnippetSession {
@@ -382,7 +385,7 @@ export class SnippetSession {
return selection;
}
static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean, clipboardText: string | undefined): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } {
static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean, clipboardText: string | undefined, overtypingCapturer: OvertypingCapturer | undefined): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } {
const edits: IIdentifiedSingleEditOperation[] = [];
const snippets: OneSnippet[] = [];
@@ -449,7 +452,7 @@ export class SnippetSession {
snippet.resolveVariables(new CompositeSnippetVariableResolver([
modelBasedVariableResolver,
new ClipboardBasedVariableResolver(readClipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'),
new SelectionBasedVariableResolver(model, selection),
new SelectionBasedVariableResolver(model, selection, idx, overtypingCapturer),
new CommentBasedVariableResolver(model, selection),
new TimeBasedVariableResolver,
new WorkspaceBasedVariableResolver(workspaceService),
@@ -496,7 +499,7 @@ export class SnippetSession {
}
// make insert edit and start with first selections
const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._options.overwriteBefore, this._options.overwriteAfter, false, this._options.adjustWhitespace, this._options.clipboardText);
const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._options.overwriteBefore, this._options.overwriteAfter, false, this._options.adjustWhitespace, this._options.clipboardText, this._options.overtypingCapturer);
this._snippets = snippets;
this._editor.executeEdits('snippet', edits, undoEdits => {
@@ -516,7 +519,7 @@ export class SnippetSession {
return;
}
this._templateMerges.push([this._snippets[0]._nestingLevel, this._snippets[0]._placeholderGroupsIdx, template]);
const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, options.overwriteBefore, options.overwriteAfter, true, options.adjustWhitespace, options.clipboardText);
const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, options.overwriteBefore, options.overwriteAfter, true, options.adjustWhitespace, options.clipboardText, options.overtypingCapturer);
this._editor.executeEdits('snippet', edits, undoEdits => {
for (const snippet of this._snippets) {
@@ -16,6 +16,7 @@ import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXT
import { ILabelService } from 'vs/platform/label/common/label';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { URI } from 'vs/base/common/uri';
import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer';
export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze({
'CURRENT_YEAR': true,
@@ -71,7 +72,9 @@ export class SelectionBasedVariableResolver implements VariableResolver {
constructor(
private readonly _model: ITextModel,
private readonly _selection: Selection
private readonly _selection: Selection,
private readonly _selectionIdx: number,
private readonly _overtypingCapturer: OvertypingCapturer | undefined
) {
//
}
@@ -82,7 +85,18 @@ export class SelectionBasedVariableResolver implements VariableResolver {
if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') {
let value = this._model.getValueInRange(this._selection) || undefined;
if (value && this._selection.startLineNumber !== this._selection.endLineNumber && variable.snippet) {
let isMultiline = this._selection.startLineNumber !== this._selection.endLineNumber;
// If there was no selected text, try to get last overtyped text
if (!value && this._overtypingCapturer) {
const info = this._overtypingCapturer.getLastOvertypedInfo(this._selectionIdx);
if (info) {
value = info.value;
isMultiline = info.multiline;
}
}
if (value && isMultiline && variable.snippet) {
// Selection is a multiline string which we indentation we now
// need to adjust. We compare the indentation of this variable
// with the indentation at the editor position and add potential
@@ -127,7 +127,7 @@ suite('SnippetSession', function () {
test('snippets, newline NO whitespace adjust', () => {
editor.setSelection(new Selection(2, 5, 2, 5));
const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined });
const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined, overtypingCapturer: undefined });
session.insert();
assert.equal(editor.getModel()!.getValue(), 'function foo() {\n abc\n foo\n bar\nconsole.log(a);\n}');
});
@@ -649,7 +649,7 @@ suite('SnippetSession', function () {
assert.ok(actual.equalsSelection(new Selection(1, 9, 1, 12)));
editor.setSelections([new Selection(1, 9, 1, 12)]);
new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined }).insert();
new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined, overtypingCapturer: undefined }).insert();
assert.equal(model.getValue(), 'console.far');
});
});
@@ -34,7 +34,7 @@ suite('Snippet Variables Resolver', function () {
resolver = new CompositeSnippetVariableResolver([
new ModelBasedVariableResolver(labelService, model),
new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1)),
new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1), 0, undefined),
]);
});
@@ -102,24 +102,24 @@ suite('Snippet Variables Resolver', function () {
test('editor variables, selection', function () {
resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3));
resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3), 0, undefined);
assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth');
assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line two');
assertVariableResolve(resolver, 'TM_LINE_INDEX', '1');
assertVariableResolve(resolver, 'TM_LINE_NUMBER', '2');
resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2));
resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2), 0, undefined);
assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth');
assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line one');
assertVariableResolve(resolver, 'TM_LINE_INDEX', '0');
assertVariableResolve(resolver, 'TM_LINE_NUMBER', '1');
resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2));
resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2), 0, undefined);
assertVariableResolve(resolver, 'TM_SELECTED_TEXT', undefined);
assertVariableResolve(resolver, 'TM_CURRENT_WORD', 'this');
resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1));
resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1), 0, undefined);
assertVariableResolve(resolver, 'TM_CURRENT_WORD', undefined);
});
@@ -34,6 +34,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerServ
import { IdleValue } from 'vs/base/common/async';
import { isObject, assertType } from 'vs/base/common/types';
import { CommitCharacterController } from './suggestCommitCharacters';
import { OvertypingCapturer } from './suggestOvertypingCapturer';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
@@ -112,6 +113,7 @@ export class SuggestController implements IEditorContribution {
private readonly _alternatives: IdleValue<SuggestAlternatives>;
private readonly _lineSuffix = new MutableDisposable<LineSuffix>();
private readonly _toDispose = new DisposableStore();
private readonly _overtypingCapturer: IdleValue<OvertypingCapturer>;
constructor(
editor: ICodeEditor,
@@ -203,6 +205,11 @@ export class SuggestController implements IEditorContribution {
return widget;
}));
// Wire up text overtyping capture
this._overtypingCapturer = this._toDispose.add(new IdleValue(() => {
return this._toDispose.add(new OvertypingCapturer(this.editor, this.model));
}));
this._alternatives = this._toDispose.add(new IdleValue(() => {
return this._toDispose.add(new SuggestAlternatives(this.editor, this._contextKeyService));
}));
@@ -361,7 +368,8 @@ export class SuggestController implements IEditorContribution {
undoStopBefore: false,
undoStopAfter: false,
adjustWhitespace: !(item.completion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace),
clipboardText: event.model.clipboardText
clipboardText: event.model.clipboardText,
overtypingCapturer: this._overtypingCapturer.value
});
if (!(flags & InsertFlags.NoAfterUndoStop)) {
@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { SuggestModel } from 'vs/editor/contrib/suggest/suggestModel';
export class OvertypingCapturer implements IDisposable {
private static readonly _maxSelectionLength = 51200;
private readonly _disposables = new DisposableStore();
private _lastOvertyped: { value: string; multiline: boolean }[] = [];
private _empty: boolean = true;
constructor(editor: ICodeEditor, suggestModel: SuggestModel) {
this._disposables.add(editor.onWillType(() => {
if (!this._empty) {
return;
}
if (!editor.hasModel()) {
return;
}
const selections = editor.getSelections();
const selectionsLength = selections.length;
// Check if it will overtype any selections
let willOvertype = false;
for (let i = 0; i < selectionsLength; i++) {
if (!selections[i].isEmpty()) {
willOvertype = true;
break;
}
}
if (!willOvertype) {
return;
}
this._lastOvertyped = [];
const model = editor.getModel();
for (let i = 0; i < selectionsLength; i++) {
const selection = selections[i];
// Check for overtyping capturer restrictions
if (model.getValueLengthInRange(selection) > OvertypingCapturer._maxSelectionLength) {
return;
}
this._lastOvertyped[i] = { value: model.getValueInRange(selection), multiline: selection.startLineNumber !== selection.endLineNumber };
}
this._empty = false;
}));
this._disposables.add(suggestModel.onDidCancel(e => {
if (!this._empty) {
this._empty = true;
}
}));
}
getLastOvertypedInfo(idx: number): { value: string; multiline: boolean } | undefined {
if (!this._empty && idx >= 0 && idx < this._lastOvertyped.length) {
return this._lastOvertyped[idx];
}
return undefined;
}
dispose() {
this._disposables.dispose();
}
}
@@ -101,13 +101,7 @@ export class CursorWordStartLeft extends WordLeftCommand {
inSelectionMode: false,
wordNavigationType: WordNavigationType.WordStart,
id: 'cursorWordStartLeft',
precondition: undefined,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.LeftArrow,
mac: { primary: KeyMod.Alt | KeyCode.LeftArrow },
weight: KeybindingWeight.EditorContrib
}
precondition: undefined
});
}
}
@@ -129,7 +123,13 @@ export class CursorWordLeft extends WordLeftCommand {
inSelectionMode: false,
wordNavigationType: WordNavigationType.WordStartFast,
id: 'cursorWordLeft',
precondition: undefined
precondition: undefined,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.LeftArrow,
mac: { primary: KeyMod.Alt | KeyCode.LeftArrow },
weight: KeybindingWeight.EditorContrib
}
});
}
}
@@ -140,13 +140,7 @@ export class CursorWordStartLeftSelect extends WordLeftCommand {
inSelectionMode: true,
wordNavigationType: WordNavigationType.WordStart,
id: 'cursorWordStartLeftSelect',
precondition: undefined,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow,
mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow },
weight: KeybindingWeight.EditorContrib
}
precondition: undefined
});
}
}
@@ -168,7 +162,13 @@ export class CursorWordLeftSelect extends WordLeftCommand {
inSelectionMode: true,
wordNavigationType: WordNavigationType.WordStartFast,
id: 'cursorWordLeftSelect',
precondition: undefined
precondition: undefined,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow,
mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow },
weight: KeybindingWeight.EditorContrib
}
});
}
}
@@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./inspectTokens';
import { $, append, reset } from 'vs/base/browser/dom';
import { CharCode } from 'vs/base/common/charCode';
import { Color } from 'vs/base/common/color';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { escape } from 'vs/base/common/strings';
import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { Position } from 'vs/editor/common/core/position';
@@ -115,23 +115,11 @@ function renderTokenText(tokenText: string): string {
let charCode = tokenText.charCodeAt(charIndex);
switch (charCode) {
case CharCode.Tab:
result += '&rarr;';
result += '\u2192'; // &rarr;
break;
case CharCode.Space:
result += '&middot;';
break;
case CharCode.LessThan:
result += '&lt;';
break;
case CharCode.GreaterThan:
result += '&gt;';
break;
case CharCode.Ampersand:
result += '&amp;';
result += '\u00B7'; // &middot;
break;
default:
@@ -211,8 +199,6 @@ class InspectTokensWidget extends Disposable implements IContentWidget {
}
}
let result = '';
let lineContent = this._model.getLineContent(position.lineNumber);
let tokenText = '';
if (token1Index < data.tokens1.length) {
@@ -220,26 +206,43 @@ class InspectTokensWidget extends Disposable implements IContentWidget {
let tokenEndIndex = token1Index + 1 < data.tokens1.length ? data.tokens1[token1Index + 1].offset : lineContent.length;
tokenText = lineContent.substring(tokenStartIndex, tokenEndIndex);
}
result += `<h2 class="tm-token">${renderTokenText(tokenText)}<span class="tm-token-length">(${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'})</span></h2>`;
reset(this._domNode,
$('h2.tm-token', undefined, renderTokenText(tokenText),
$('span.tm-token-length', undefined, `${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'}`)));
result += `<hr class="tokens-inspect-separator" style="clear:both"/>`;
append(this._domNode, $('hr.tokens-inspect-separator', { 'style': 'clear:both' }));
let metadata = (token2Index << 1) + 1 < data.tokens2.length ? this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]) : null;
result += `<table class="tm-metadata-table"><tbody>`;
result += `<tr><td class="tm-metadata-key">language</td><td class="tm-metadata-value">${metadata ? escape(metadata.languageIdentifier.language) : '-?-'}</td>`;
result += `<tr><td class="tm-metadata-key">token type</td><td class="tm-metadata-value">${metadata ? this._tokenTypeToString(metadata.tokenType) : '-?-'}</td>`;
result += `<tr><td class="tm-metadata-key">font style</td><td class="tm-metadata-value">${metadata ? this._fontStyleToString(metadata.fontStyle) : '-?-'}</td>`;
result += `<tr><td class="tm-metadata-key">foreground</td><td class="tm-metadata-value">${metadata ? Color.Format.CSS.formatHex(metadata.foreground) : '-?-'}</td>`;
result += `<tr><td class="tm-metadata-key">background</td><td class="tm-metadata-value">${metadata ? Color.Format.CSS.formatHex(metadata.background) : '-?-'}</td>`;
result += `</tbody></table>`;
result += `<hr class="tokens-inspect-separator"/>`;
const metadata = (token2Index << 1) + 1 < data.tokens2.length ? this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]) : null;
append(this._domNode, $('table.tm-metadata-table', undefined,
$('tbody', undefined,
$('tr', undefined,
$('td.tm-metadata-key', undefined, 'language'),
$('td.tm-metadata-value', undefined, `${metadata ? metadata.languageIdentifier.language : '-?-'}`)
),
$('tr', undefined,
$('td.tm-metadata-key', undefined, 'token type' as string),
$('td.tm-metadata-value', undefined, `${metadata ? this._tokenTypeToString(metadata.tokenType) : '-?-'}`)
),
$('tr', undefined,
$('td.tm-metadata-key', undefined, 'font style' as string),
$('td.tm-metadata-value', undefined, `${metadata ? this._fontStyleToString(metadata.fontStyle) : '-?-'}`)
),
$('tr', undefined,
$('td.tm-metadata-key', undefined, 'foreground'),
$('td.tm-metadata-value', undefined, `${metadata ? Color.Format.CSS.formatHex(metadata.foreground) : '-?-'}`)
),
$('tr', undefined,
$('td.tm-metadata-key', undefined, 'background'),
$('td.tm-metadata-value', undefined, `${metadata ? Color.Format.CSS.formatHex(metadata.background) : '-?-'}`)
)
)
));
append(this._domNode, $('hr.tokens-inspect-separator'));
if (token1Index < data.tokens1.length) {
result += `<span class="tm-token-type">${escape(data.tokens1[token1Index].type)}</span>`;
append(this._domNode, $('span.tm-token-type', undefined, data.tokens1[token1Index].type));
}
this._domNode.innerHTML = result;
this._editor.layoutContentWidget(this);
}
@@ -13,14 +13,13 @@ import { OS, isLinux, isMacintosh } from 'vs/base/common/platform';
import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor, IDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IBulkEditOptions, IBulkEditResult, IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IBulkEditOptions, IBulkEditResult, IBulkEditService, ResourceEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService';
import { isDiffEditorConfigurationKey, isEditorConfigurationKey } from 'vs/editor/common/config/commonEditorConfig';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { IPosition, Position as Pos } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IEditor } from 'vs/editor/common/editorCommon';
import { ITextModel, ITextSnapshot } from 'vs/editor/common/model';
import { TextEdit, WorkspaceEdit, WorkspaceTextEdit } from 'vs/editor/common/modes';
import { IIdentifiedSingleEditOperation, ITextModel, ITextSnapshot } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService';
import { ITextResourceConfigurationService, ITextResourcePropertiesService, ITextResourceConfigurationChangeEvent } from 'vs/editor/common/services/textResourceConfigurationService';
@@ -47,6 +46,7 @@ import { SimpleServicesNLS } from 'vs/editor/common/standaloneStrings';
import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings';
import { basename } from 'vs/base/common/resources';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { NullLogService } from 'vs/platform/log/common/log';
export class SimpleModel implements IResolvedTextEditorModel {
@@ -78,10 +78,17 @@ export class SimpleModel implements IResolvedTextEditorModel {
return false;
}
private disposed = false;
public dispose(): void {
this.disposed = true;
this._onDispose.fire();
}
public isDisposed(): boolean {
return this.disposed;
}
public isResolved(): boolean {
return true;
}
@@ -293,7 +300,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
notificationService: INotificationService,
domNode: HTMLElement
) {
super(contextKeyService, commandService, telemetryService, notificationService);
super(contextKeyService, commandService, telemetryService, notificationService, new NullLogService());
this._cachedResolver = null;
this._dynamicKeybindings = [];
@@ -319,7 +326,8 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
command: commandId,
when: when,
weight1: 1000,
weight2: 0
weight2: 0,
extensionId: null
});
toDispose.add(toDisposable(() => {
@@ -350,7 +358,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
if (!this._cachedResolver) {
const defaults = this._toNormalizedKeybindingItems(KeybindingsRegistry.getDefaultKeybindings(), true);
const overrides = this._toNormalizedKeybindingItems(this._dynamicKeybindings, false);
this._cachedResolver = new KeybindingResolver(defaults, overrides);
this._cachedResolver = new KeybindingResolver(defaults, overrides, (str) => this._log(str));
}
return this._cachedResolver;
}
@@ -367,11 +375,11 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
if (!keybinding) {
// This might be a removal keybinding item in user settings => accept it
result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault);
result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault, null);
} else {
const resolvedKeybindings = this.resolveKeybinding(keybinding);
for (const resolvedKeybinding of resolvedKeybindings) {
result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault);
result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault, null);
}
}
}
@@ -665,42 +673,43 @@ export class SimpleBulkEditService implements IBulkEditService {
return Disposable.None;
}
apply(workspaceEdit: WorkspaceEdit, options?: IBulkEditOptions): Promise<IBulkEditResult> {
async apply(edits: ResourceEdit[], _options?: IBulkEditOptions): Promise<IBulkEditResult> {
let edits = new Map<ITextModel, TextEdit[]>();
const textEdits = new Map<ITextModel, IIdentifiedSingleEditOperation[]>();
if (workspaceEdit.edits) {
for (let edit of workspaceEdit.edits) {
if (!WorkspaceTextEdit.is(edit)) {
return Promise.reject(new Error('bad edit - only text edits are supported'));
}
let model = this._modelService.getModel(edit.resource);
if (!model) {
return Promise.reject(new Error('bad edit - model not found'));
}
let array = edits.get(model);
if (!array) {
array = [];
edits.set(model, array);
}
array.push(edit.edit);
for (let edit of edits) {
if (!(edit instanceof ResourceTextEdit)) {
throw new Error('bad edit - only text edits are supported');
}
const model = this._modelService.getModel(edit.resource);
if (!model) {
throw new Error('bad edit - model not found');
}
if (typeof edit.versionId === 'number' && model.getVersionId() !== edit.versionId) {
throw new Error('bad state - model changed in the meantime');
}
let array = textEdits.get(model);
if (!array) {
array = [];
textEdits.set(model, array);
}
array.push(EditOperation.replaceMove(Range.lift(edit.textEdit.range), edit.textEdit.text));
}
let totalEdits = 0;
let totalFiles = 0;
edits.forEach((edits, model) => {
for (const [model, edits] of textEdits) {
model.pushStackElement();
model.pushEditOperations([], edits.map((e) => EditOperation.replaceMove(Range.lift(e.range), e.text)), () => []);
model.pushEditOperations([], edits, () => []);
model.pushStackElement();
totalFiles += 1;
totalEdits += edits.length;
});
}
return Promise.resolve({
selection: undefined,
return {
ariaSummary: strings.format(SimpleServicesNLS.bulkEditServiceSummary, totalEdits, totalFiles)
});
};
}
}
@@ -869,7 +869,7 @@ suite('viewLineRenderer.renderLine', () => {
suite('viewLineRenderer.renderLine 2', () => {
function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'all', selections: LineRange[] | null, expected: string): void {
function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all', selections: LineRange[] | null, expected: string): void {
let actual = renderViewLine(new RenderLineInput(
fontIsMonospace,
true,
@@ -1355,6 +1355,95 @@ suite('viewLineRenderer.renderLine 2', () => {
);
});
test('createLineParts render whitespace for trailing with leading, inner, and without trailing whitespace', () => {
testCreateLineParts(
false,
' Hello world!',
[
createPart(4, 0),
createPart(6, 1),
createPart(14, 2)
],
0,
'trailing',
null,
[
'<span>',
'<span class="mtk0">\u00a0Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!</span>',
'</span>',
].join('')
);
});
test('createLineParts render whitespace for trailing with leading, inner, and trailing whitespace', () => {
testCreateLineParts(
false,
' Hello world! \t',
[
createPart(4, 0),
createPart(6, 1),
createPart(15, 2)
],
0,
'trailing',
null,
[
'<span>',
'<span class="mtk0">\u00a0Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!</span>',
'<span class="mtkz" style="width:30px">\u00b7\u2192\u00a0</span>',
'</span>',
].join('')
);
});
test('createLineParts render whitespace for trailing with 8 leading and 8 trailing whitespaces', () => {
testCreateLineParts(
false,
' Hello world! ',
[
createPart(8, 1),
createPart(10, 2),
createPart(28, 3)
],
0,
'trailing',
null,
[
'<span>',
'<span class="mtk1">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'</span>',
].join('')
);
});
test('createLineParts render whitespace for trailing with line containing only whitespaces', () => {
testCreateLineParts(
false,
' \t ',
[
createPart(2, 0),
createPart(3, 1),
],
0,
'trailing',
null,
[
'<span>',
'<span class="mtkz" style="width:40px">\u00b7\u2192\u00a0\u00a0</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'</span>',
].join('')
);
});
test('createLineParts can handle unsorted inline decorations', () => {
let actual = renderViewLine(new RenderLineInput(
false,
+10 -2
View File
@@ -3068,7 +3068,7 @@ declare namespace monaco.editor {
* Enable rendering of whitespace.
* Defaults to none.
*/
renderWhitespace?: 'none' | 'boundary' | 'selection' | 'all';
renderWhitespace?: 'none' | 'boundary' | 'selection' | 'trailing' | 'all';
/**
* Enable rendering of control characters.
* Defaults to false.
@@ -4056,7 +4056,7 @@ declare namespace monaco.editor {
renderLineHighlight: IEditorOption<EditorOption.renderLineHighlight, 'all' | 'line' | 'none' | 'gutter'>;
renderLineHighlightOnlyWhenFocus: IEditorOption<EditorOption.renderLineHighlightOnlyWhenFocus, boolean>;
renderValidationDecorations: IEditorOption<EditorOption.renderValidationDecorations, 'on' | 'off' | 'editable'>;
renderWhitespace: IEditorOption<EditorOption.renderWhitespace, 'all' | 'none' | 'boundary' | 'selection'>;
renderWhitespace: IEditorOption<EditorOption.renderWhitespace, 'all' | 'none' | 'boundary' | 'selection' | 'trailing'>;
revealHorizontalRightPadding: IEditorOption<EditorOption.revealHorizontalRightPadding, number>;
roundedSelection: IEditorOption<EditorOption.roundedSelection, boolean>;
rulers: IEditorOption<EditorOption.rulers, {}>;
@@ -4418,6 +4418,14 @@ declare namespace monaco.editor {
overflowWidgetsDomNode?: HTMLElement;
}
export interface IDiffEditorConstructionOptions extends IDiffEditorOptions {
/**
* Place overflow widgets inside an external DOM node.
* Defaults to an internal DOM node.
*/
overflowWidgetsDomNode?: HTMLElement;
}
/**
* A rich code editor.
*/
@@ -111,11 +111,11 @@ export function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray<MenuI
}
if (isPrimaryGroup(group)) {
const to = Array.isArray<IAction>(target) ? target : target.primary;
const to = Array.isArray(target) ? target : target.primary;
to.unshift(...actions);
} else {
const to = Array.isArray<IAction>(target) ? target : target.secondary;
const to = Array.isArray(target) ? target : target.secondary;
if (to.length > 0) {
to.push(new Separator());

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