mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-12 20:12:23 +01:00
Merge remote-tracking branch 'origin/master' into alex/asar
This commit is contained in:
@@ -93,7 +93,7 @@ const BUNDLED_FILE_HEADER = [
|
||||
' *--------------------------------------------------------*/'
|
||||
].join('\n');
|
||||
|
||||
const languages = i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages: []);
|
||||
const languages = i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []);
|
||||
|
||||
gulp.task('clean-optimized-vscode', util.rimraf('out-vscode'));
|
||||
gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compile-extensions-build'], common.optimizeTask({
|
||||
@@ -411,7 +411,7 @@ gulp.task('vscode-translations-push', function () {
|
||||
gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
|
||||
gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
|
||||
).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
|
||||
).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken));
|
||||
).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken));
|
||||
});
|
||||
|
||||
gulp.task('vscode-translations-push-test', function () {
|
||||
@@ -424,7 +424,7 @@ gulp.task('vscode-translations-push-test', function () {
|
||||
gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
|
||||
gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
|
||||
).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
|
||||
).pipe(vfs.dest('../vscode-transifex-input'));
|
||||
).pipe(vfs.dest('../vscode-transifex-input'));
|
||||
});
|
||||
|
||||
gulp.task('vscode-translations-pull', function () {
|
||||
@@ -599,17 +599,3 @@ gulp.task('generate-vscode-configuration', () => {
|
||||
console.error(e.toString());
|
||||
});
|
||||
});
|
||||
|
||||
//#region Built-In Extensions
|
||||
gulp.task('clean-builtin-extensions', util.rimraf('.build/builtInExtensions'));
|
||||
gulp.task('download-builtin-extensions', ['clean-builtin-extensions'], function () {
|
||||
const marketplaceExtensions = es.merge(...builtInExtensions.map(extension => {
|
||||
return ext.fromMarketplace(extension.name, extension.version)
|
||||
.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`));
|
||||
}));
|
||||
|
||||
return marketplaceExtensions
|
||||
.pipe(util.setExecutableBit(['**/*.sh']))
|
||||
.pipe(vfs.dest('.build/builtInExtensions'));
|
||||
});
|
||||
//#endregion
|
||||
|
||||
@@ -7,26 +7,116 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const mkdirp = require('mkdirp');
|
||||
const rimraf = require('rimraf');
|
||||
const es = require('event-stream');
|
||||
const rename = require('gulp-rename');
|
||||
const vfs = require('vinyl-fs');
|
||||
const ext = require('./extensions');
|
||||
const util = require('gulp-util');
|
||||
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
const builtInExtensions = require('../builtInExtensions');
|
||||
const controlFilePath = path.join(process.env['HOME'], '.vscode-oss-dev', 'extensions', 'control.json');
|
||||
|
||||
function getExtensionPath(extension) {
|
||||
return path.join(root, '.build', 'builtInExtensions', extension.name);
|
||||
}
|
||||
|
||||
function isUpToDate(extension) {
|
||||
const packagePath = path.join(root, '.build', 'builtInExtensions', extension.name, 'package.json');
|
||||
const packagePath = path.join(getExtensionPath(extension), 'package.json');
|
||||
|
||||
if (!fs.existsSync(packagePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const packageContents = fs.readFileSync(packagePath);
|
||||
|
||||
try {
|
||||
const diskVersion = JSON.parse(packageContents).version;
|
||||
return (diskVersion === extension.version);
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const builtInExtensions = require('../builtInExtensions');
|
||||
builtInExtensions.forEach((extension) => {
|
||||
if (!isUpToDate(extension)) {
|
||||
process.exit(1);
|
||||
function syncMarketplaceExtension(extension) {
|
||||
if (isUpToDate(extension)) {
|
||||
util.log(util.colors.blue('[marketplace]'), `${extension.name}@${extension.version}`, util.colors.green('✔︎'));
|
||||
return es.readArray([]);
|
||||
}
|
||||
});
|
||||
process.exit(0);
|
||||
|
||||
rimraf.sync(getExtensionPath(extension));
|
||||
|
||||
return ext.fromMarketplace(extension.name, extension.version)
|
||||
.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`))
|
||||
.pipe(vfs.dest('.build/builtInExtensions'))
|
||||
.on('end', () => util.log(util.colors.blue('[marketplace]'), extension.name, util.colors.green('✔︎')));
|
||||
}
|
||||
|
||||
function syncExtension(extension, controlState) {
|
||||
switch (controlState) {
|
||||
case 'disabled':
|
||||
util.log(util.colors.blue('[disabled]'), util.colors.gray(extension.name));
|
||||
rimraf.sync(getExtensionPath(extension));
|
||||
return es.readArray([]);
|
||||
|
||||
case 'marketplace':
|
||||
return syncMarketplaceExtension(extension);
|
||||
|
||||
default:
|
||||
if (!fs.existsSync(controlState)) {
|
||||
util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`));
|
||||
return es.readArray([]);
|
||||
|
||||
} else if (!fs.existsSync(path.join(controlState, 'package.json'))) {
|
||||
util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`));
|
||||
return es.readArray([]);
|
||||
}
|
||||
|
||||
util.log(util.colors.blue('[local]'), `${extension.name}: ${controlState}`, util.colors.green('✔︎'));
|
||||
return es.readArray([]);
|
||||
}
|
||||
}
|
||||
|
||||
function readControlFile() {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(controlFilePath, 'utf8'));
|
||||
} catch (err) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function writeControlFile(control) {
|
||||
mkdirp.sync(path.dirname(controlFilePath));
|
||||
fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2));
|
||||
}
|
||||
|
||||
function main() {
|
||||
util.log('Syncronizing built-in extensions...');
|
||||
util.log('Control file:', controlFilePath);
|
||||
|
||||
const control = readControlFile();
|
||||
const streams = [];
|
||||
|
||||
for (const extension of builtInExtensions) {
|
||||
let controlState = control[extension.name] || 'marketplace';
|
||||
control[extension.name] = controlState;
|
||||
|
||||
streams.push(syncExtension(extension, controlState));
|
||||
}
|
||||
|
||||
writeControlFile(control);
|
||||
|
||||
es.merge(streams)
|
||||
.on('error', err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
})
|
||||
.on('end', () => {
|
||||
util.log(`${streams.length} built-in extensions processed.`);
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
+1
-2
@@ -17,9 +17,8 @@ set CODE=".build\electron\%NAMESHORT%"
|
||||
node build\lib\electron.js
|
||||
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron
|
||||
|
||||
:: Get built-in extensions
|
||||
:: Sync built-in extensions
|
||||
node build\lib\builtInExtensions.js
|
||||
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js download-builtin-extensions
|
||||
|
||||
:: Build
|
||||
if not exist out node .\node_modules\gulp\bin\gulp.js compile
|
||||
|
||||
+2
-2
@@ -24,8 +24,8 @@ function code() {
|
||||
# Get electron
|
||||
node build/lib/electron.js || ./node_modules/.bin/gulp electron
|
||||
|
||||
# Get built-in extensions
|
||||
node build/lib/builtInExtensions.js || ./node_modules/.bin/gulp download-builtin-extensions
|
||||
# Sync built-in extensions
|
||||
node build/lib/builtInExtensions.js
|
||||
|
||||
# Build
|
||||
test -d out || ./node_modules/.bin/gulp compile
|
||||
|
||||
@@ -17,6 +17,12 @@ export interface IRenderer<TElement, TTemplateData> {
|
||||
disposeTemplate(templateData: TTemplateData): void;
|
||||
}
|
||||
|
||||
export interface IListOpenEvent<T> {
|
||||
elements: T[];
|
||||
indexes: number[];
|
||||
browserEvent?: UIEvent;
|
||||
}
|
||||
|
||||
export interface IListEvent<T> {
|
||||
elements: T[];
|
||||
indexes: number[];
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import 'vs/css!./list';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
import { IDelegate, IRenderer, IListEvent } from './list';
|
||||
import { IDelegate, IRenderer, IListEvent, IListOpenEvent } from './list';
|
||||
import { List, IListStyles, IListOptions } from './listWidget';
|
||||
import { IPagedModel } from 'vs/base/common/paging';
|
||||
import Event, { mapEvent } from 'vs/base/common/event';
|
||||
@@ -97,6 +97,10 @@ export class PagedList<T> {
|
||||
return mapEvent(this.list.onFocusChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
|
||||
}
|
||||
|
||||
get onOpen(): Event<IListOpenEvent<T>> {
|
||||
return mapEvent(this.list.onOpen, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent }));
|
||||
}
|
||||
|
||||
get onSelectionChange(): Event<IListEvent<T>> {
|
||||
return mapEvent(this.list.onSelectionChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
|
||||
}
|
||||
@@ -126,8 +130,8 @@ export class PagedList<T> {
|
||||
this.list.scrollTop = scrollTop;
|
||||
}
|
||||
|
||||
open(indexes: number[]): void {
|
||||
this.list.open(indexes);
|
||||
open(indexes: number[], browserEvent?: UIEvent): void {
|
||||
this.list.open(indexes, browserEvent);
|
||||
}
|
||||
|
||||
setFocus(indexes: number[]): void {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import Event, { Emitter, EventBufferer, chain, mapEvent, anyEvent } from 'vs/base/common/event';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { IDelegate, IRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list';
|
||||
import { IDelegate, IRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListOpenEvent } from './list';
|
||||
import { ListView, IListViewOptions } from './listView';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
@@ -261,6 +261,7 @@ function isInputElement(e: HTMLElement): boolean {
|
||||
class KeyboardController<T> implements IDisposable {
|
||||
|
||||
private disposables: IDisposable[];
|
||||
private openController: IOpenController;
|
||||
|
||||
constructor(
|
||||
private list: List<T>,
|
||||
@@ -270,6 +271,8 @@ class KeyboardController<T> implements IDisposable {
|
||||
const multipleSelectionSupport = !(options.multipleSelectionSupport === false);
|
||||
this.disposables = [];
|
||||
|
||||
this.openController = options.openController || DefaultOpenController;
|
||||
|
||||
const onKeyDown = chain(domEvent(view.domNode, 'keydown'))
|
||||
.filter(e => !isInputElement(e.target as HTMLElement))
|
||||
.map(e => new StandardKeyboardEvent(e));
|
||||
@@ -290,7 +293,10 @@ class KeyboardController<T> implements IDisposable {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.list.setSelection(this.list.getFocus());
|
||||
this.list.open(this.list.getFocus());
|
||||
|
||||
if (this.openController.shouldOpen(e.browserEvent)) {
|
||||
this.list.open(this.list.getFocus(), e.browserEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private onUpArrow(e: StandardKeyboardEvent): void {
|
||||
@@ -357,10 +363,15 @@ const DefaultMultipleSelectionContoller = {
|
||||
isSelectionRangeChangeEvent
|
||||
};
|
||||
|
||||
const DefaultOpenController = {
|
||||
shouldOpen: (event: UIEvent) => true
|
||||
};
|
||||
|
||||
class MouseController<T> implements IDisposable {
|
||||
|
||||
private multipleSelectionSupport: boolean;
|
||||
private multipleSelectionController: IMultipleSelectionController<T> | undefined;
|
||||
private multipleSelectionController: IMultipleSelectionController<T>;
|
||||
private openController: IOpenController;
|
||||
private didJustPressContextMenuKey: boolean = false;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
@@ -406,6 +417,8 @@ class MouseController<T> implements IDisposable {
|
||||
this.multipleSelectionController = options.multipleSelectionController || DefaultMultipleSelectionContoller;
|
||||
}
|
||||
|
||||
this.openController = options.openController || DefaultOpenController;
|
||||
|
||||
view.onMouseDown(this.onMouseDown, this, this.disposables);
|
||||
view.onMouseClick(this.onPointer, this, this.disposables);
|
||||
view.onMouseDblClick(this.onDoubleClick, this, this.disposables);
|
||||
@@ -458,7 +471,10 @@ class MouseController<T> implements IDisposable {
|
||||
|
||||
if (this.options.selectOnMouseDown) {
|
||||
this.list.setSelection([focus]);
|
||||
this.list.open([focus]);
|
||||
|
||||
if (this.openController.shouldOpen(e.browserEvent)) {
|
||||
this.list.open([focus], e.browserEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +486,10 @@ class MouseController<T> implements IDisposable {
|
||||
if (!this.options.selectOnMouseDown) {
|
||||
const focus = this.list.getFocus();
|
||||
this.list.setSelection(focus);
|
||||
this.list.open(focus);
|
||||
|
||||
if (this.openController.shouldOpen(e.browserEvent)) {
|
||||
this.list.open(focus, e.browserEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,6 +542,10 @@ export interface IMultipleSelectionController<T> {
|
||||
isSelectionRangeChangeEvent(event: IListMouseEvent<T> | IListTouchEvent<T>): boolean;
|
||||
}
|
||||
|
||||
export interface IOpenController {
|
||||
shouldOpen(event: UIEvent): boolean;
|
||||
}
|
||||
|
||||
export interface IListOptions<T> extends IListViewOptions, IListStyles {
|
||||
identityProvider?: IIdentityProvider<T>;
|
||||
ariaLabel?: string;
|
||||
@@ -533,6 +556,7 @@ export interface IListOptions<T> extends IListViewOptions, IListStyles {
|
||||
verticalScrollMode?: ScrollbarVisibility;
|
||||
multipleSelectionSupport?: boolean;
|
||||
multipleSelectionController?: IMultipleSelectionController<T>;
|
||||
openController?: IOpenController;
|
||||
}
|
||||
|
||||
export interface IListStyles {
|
||||
@@ -708,9 +732,9 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
readonly onContextMenu: Event<IListContextMenuEvent<T>> = Event.None;
|
||||
|
||||
private _onOpen = new Emitter<number[]>();
|
||||
@memoize get onOpen(): Event<IListEvent<T>> {
|
||||
return mapEvent(this._onOpen.event, indexes => this.toListEvent({ indexes }));
|
||||
private _onOpen = new Emitter<IListOpenEvent<T>>();
|
||||
@memoize get onOpen(): Event<IListOpenEvent<T>> {
|
||||
return this._onOpen.event;
|
||||
}
|
||||
|
||||
private _onPin = new Emitter<number[]>();
|
||||
@@ -973,8 +997,8 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
return this.view.domNode;
|
||||
}
|
||||
|
||||
open(indexes: number[]): void {
|
||||
this._onOpen.fire(indexes);
|
||||
open(indexes: number[], browserEvent?: UIEvent): void {
|
||||
this._onOpen.fire({ indexes, elements: indexes.map(i => this.view.element(i)), browserEvent });
|
||||
}
|
||||
|
||||
pin(indexes: number[]): void {
|
||||
|
||||
@@ -38,8 +38,14 @@ export enum ClickBehavior {
|
||||
ON_MOUSE_UP
|
||||
}
|
||||
|
||||
export enum OpenMode {
|
||||
SINGLE_CLICK,
|
||||
DOUBLE_CLICK
|
||||
}
|
||||
|
||||
export interface IControllerOptions {
|
||||
clickBehavior?: ClickBehavior;
|
||||
openMode?: OpenMode;
|
||||
keyboardSupport?: boolean;
|
||||
}
|
||||
|
||||
@@ -82,7 +88,7 @@ export class DefaultController implements _.IController {
|
||||
|
||||
private options: IControllerOptions;
|
||||
|
||||
constructor(options: IControllerOptions = { clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: true }) {
|
||||
constructor(options: IControllerOptions = { clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: true, openMode: OpenMode.SINGLE_CLICK }) {
|
||||
this.options = options;
|
||||
|
||||
this.downKeyBindingDispatcher = new KeybindingDispatcher();
|
||||
@@ -153,6 +159,7 @@ export class DefaultController implements _.IController {
|
||||
|
||||
protected onLeftClick(tree: _.ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
|
||||
const payload = { origin: origin, originalEvent: eventish };
|
||||
const isDoubleClick = (origin === 'mouse' && (<mouse.IMouseEvent>eventish).detail === 2);
|
||||
|
||||
if (tree.getInput() === element) {
|
||||
tree.clearFocus(payload);
|
||||
@@ -168,16 +175,26 @@ export class DefaultController implements _.IController {
|
||||
tree.setSelection([element], payload);
|
||||
tree.setFocus(element, payload);
|
||||
|
||||
if (tree.isExpanded(element)) {
|
||||
tree.collapse(element).done(null, errors.onUnexpectedError);
|
||||
} else {
|
||||
tree.expand(element).done(null, errors.onUnexpectedError);
|
||||
if (this.openOnSingleClick || isDoubleClick) {
|
||||
if (tree.isExpanded(element)) {
|
||||
tree.collapse(element).done(null, errors.onUnexpectedError);
|
||||
} else {
|
||||
tree.expand(element).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected setOpenMode(openMode: OpenMode) {
|
||||
this.options.openMode = openMode;
|
||||
}
|
||||
|
||||
protected get openOnSingleClick(): boolean {
|
||||
return this.options.openMode === OpenMode.SINGLE_CLICK;
|
||||
}
|
||||
|
||||
public onContextMenu(tree: _.ITree, element: any, event: _.ContextMenuEvent): boolean {
|
||||
if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') {
|
||||
return false; // allow context menu on input fields
|
||||
|
||||
@@ -23,7 +23,6 @@ import { GestureEvent } from 'vs/base/browser/touch';
|
||||
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
|
||||
import { FileLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import * as tree from 'vs/base/parts/tree/browser/tree';
|
||||
import { DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
@@ -41,7 +40,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TrackedRangeStickiness, IModelDeltaDecoration } from 'vs/editor/common/model';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Location } from 'vs/editor/common/modes';
|
||||
|
||||
@@ -218,7 +217,7 @@ class DataSource implements tree.IDataSource {
|
||||
}
|
||||
}
|
||||
|
||||
class Controller extends DefaultController {
|
||||
class Controller extends WorkbenchTreeController {
|
||||
|
||||
private _onDidFocus = new Emitter<any>();
|
||||
readonly onDidFocus: Event<any> = this._onDidFocus.event;
|
||||
@@ -243,19 +242,22 @@ class Controller extends DefaultController {
|
||||
}
|
||||
|
||||
public onMouseDown(tree: tree.ITree, element: any, event: IMouseEvent): boolean {
|
||||
var isDoubleClick = event.detail === 2;
|
||||
if (event.leftButton) {
|
||||
if (element instanceof FileReferences) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return this._expandCollapse(tree, element);
|
||||
if (this.openOnSingleClick || isDoubleClick) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return this._expandCollapse(tree, element);
|
||||
}
|
||||
}
|
||||
|
||||
var result = super.onClick(tree, element, event);
|
||||
if (event.ctrlKey || event.metaKey || event.altKey) {
|
||||
this._onDidOpenToSide.fire(element);
|
||||
} else if (event.detail === 2) {
|
||||
} else if (isDoubleClick) {
|
||||
this._onDidSelect.fire(element);
|
||||
} else {
|
||||
} else if (this.openOnSingleClick) {
|
||||
this._onDidFocus.fire(element);
|
||||
}
|
||||
return result;
|
||||
@@ -631,7 +633,9 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
|
||||
// tree
|
||||
container.div({ 'class': 'ref-tree inline' }, (div: Builder) => {
|
||||
const controller = new Controller();
|
||||
var controller = this._instantiationService.createInstance(Controller, {});
|
||||
this._callOnDispose.push(controller);
|
||||
|
||||
var config = <tree.ITreeConfiguration>{
|
||||
dataSource: this._instantiationService.createInstance(DataSource),
|
||||
renderer: this._instantiationService.createInstance(Renderer),
|
||||
@@ -641,8 +645,7 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
|
||||
var options: tree.ITreeOptions = {
|
||||
twistiePixels: 20,
|
||||
ariaLabel: nls.localize('treeAriaLabel', "References"),
|
||||
keyboardSupport: false
|
||||
ariaLabel: nls.localize('treeAriaLabel', "References")
|
||||
};
|
||||
|
||||
this._tree = this._instantiationService.createInstance(WorkbenchTree, div.getHTMLElement(), config, options);
|
||||
|
||||
@@ -444,8 +444,7 @@ export class SnippetSession {
|
||||
return false;
|
||||
}
|
||||
|
||||
let ranges: Range[] = [];
|
||||
let placeholderIndex: number = -1;
|
||||
let allPossibleSelections: Map<number, Range[]>;
|
||||
for (const snippet of this._snippets) {
|
||||
|
||||
const possibleSelections = snippet.computePossibleSelections();
|
||||
@@ -453,49 +452,58 @@ export class SnippetSession {
|
||||
// for the first snippet find the placeholder (and its ranges)
|
||||
// that contain at least one selection. for all remaining snippets
|
||||
// the same placeholder (and their ranges) must be used.
|
||||
if (placeholderIndex < 0) {
|
||||
if (!allPossibleSelections) {
|
||||
allPossibleSelections = new Map<number, Range[]>();
|
||||
possibleSelections.forEach((ranges, index) => {
|
||||
if (placeholderIndex >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ranges.sort(Range.compareRangesUsingStarts);
|
||||
for (const selection of selections) {
|
||||
if (ranges[0].containsRange(selection)) {
|
||||
placeholderIndex = index;
|
||||
allPossibleSelections.set(index, []);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (placeholderIndex < 0) {
|
||||
if (allPossibleSelections.size === 0) {
|
||||
// return false if we couldn't associate a selection to
|
||||
// this (the first) snippet
|
||||
return false;
|
||||
}
|
||||
|
||||
ranges.push(...possibleSelections.get(placeholderIndex));
|
||||
// add selections from 'this' snippet so that we know all
|
||||
// selections for this placeholder
|
||||
allPossibleSelections.forEach((array, index) => {
|
||||
array.push(...possibleSelections.get(index));
|
||||
});
|
||||
}
|
||||
|
||||
if (selections.length !== ranges.length) {
|
||||
// this means we started at a placeholder with N
|
||||
// ranges and new have M (N > M) selections.
|
||||
// So (at least) one placeholder is without selection -> cancel
|
||||
return false;
|
||||
}
|
||||
|
||||
// also sort (placeholder)-ranges. then walk both arrays and
|
||||
// make sure the placeholder-ranges contain the corresponding
|
||||
// sort selections (and later placeholder-ranges). then walk both
|
||||
// arrays and make sure the placeholder-ranges contain the corresponding
|
||||
// selection
|
||||
selections.sort(Range.compareRangesUsingStarts);
|
||||
ranges.sort(Range.compareRangesUsingStarts);
|
||||
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
if (!ranges[i].containsRange(selections[i])) {
|
||||
return false;
|
||||
allPossibleSelections.forEach((ranges, index) => {
|
||||
|
||||
if (ranges.length !== selections.length) {
|
||||
allPossibleSelections.delete(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
ranges.sort(Range.compareRangesUsingStarts);
|
||||
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
if (!ranges[i].containsRange(selections[i])) {
|
||||
allPossibleSelections.delete(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// from all possible selections we have deleted those
|
||||
// that don't match with the current selection. if we don't
|
||||
// have any left, we don't have a selection anymore
|
||||
return allPossibleSelections.size > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,4 +303,18 @@ suite('SnippetController2', function () {
|
||||
assertSelections(editor, new Selection(2, 9, 2, 9), new Selection(1, 7, 1, 7));
|
||||
assertContextKeys(contextKeys, true, false, true);
|
||||
});
|
||||
|
||||
test('“Nested” snippets terminating abruptly in VSCode 1.19.2. #42012', function () {
|
||||
|
||||
const ctrl = new SnippetController2(editor, logService, contextKeys);
|
||||
model.setValue('');
|
||||
editor.setSelection(new Selection(1, 1, 1, 1));
|
||||
ctrl.insert('var ${2:${1:name}} = ${1:name} + 1;${0}');
|
||||
|
||||
assertSelections(editor, new Selection(1, 5, 1, 9), new Selection(1, 12, 1, 16));
|
||||
assertContextKeys(contextKeys, true, false, true);
|
||||
|
||||
ctrl.next();
|
||||
assertContextKeys(contextKeys, true, true, true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -273,7 +273,7 @@ export class ExtensionManagementService implements IExtensionManagementService {
|
||||
installingExtension = this.getExtensionsReport()
|
||||
.then(report => {
|
||||
if (getMaliciousExtensionsSet(report).has(extension.identifier.id)) {
|
||||
throw new Error(nls.localize('malicious extension', "Can't install extension since it was reported to be malicious."));
|
||||
throw new Error(nls.localize('malicious extension', "Can't install extension since it was reported to be problematic."));
|
||||
} else {
|
||||
return extension;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
'use strict';
|
||||
|
||||
import { ITree, ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
|
||||
import { List, IListOptions, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, IMultipleSelectionController } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { List, IListOptions, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, IMultipleSelectionController, IOpenController } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, toDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, toDisposable, combinedDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextKeyService, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { PagedList, IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
|
||||
import { IDelegate, IRenderer, IListMouseEvent, IListTouchEvent } from 'vs/base/browser/ui/list/list';
|
||||
@@ -20,7 +20,10 @@ import { mixin } from 'vs/base/common/objects';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
import { DefaultController, IControllerOptions, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
export type ListWidget = List<any> | PagedList<any> | ITree;
|
||||
|
||||
export const IListService = createDecorator<IListService>('listService');
|
||||
@@ -94,12 +97,17 @@ function createScopedContextKeyService(contextKeyService: IContextKeyService, wi
|
||||
return result;
|
||||
}
|
||||
|
||||
export const multiSelectModifierSettingKey = 'workbench.multiSelectModifier';
|
||||
export const multiSelectModifierSettingKey = 'workbench.list.multiSelectModifier';
|
||||
export const openModeSettingKey = 'workbench.list.openMode';
|
||||
|
||||
export function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean {
|
||||
function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean {
|
||||
return configurationService.getValue(multiSelectModifierSettingKey) === 'alt';
|
||||
}
|
||||
|
||||
function useSingleClickToOpen(configurationService: IConfigurationService): boolean {
|
||||
return configurationService.getValue(openModeSettingKey) !== 'doubleClick';
|
||||
}
|
||||
|
||||
class MultipleSelectionController<T> implements IMultipleSelectionController<T> {
|
||||
|
||||
constructor(private configurationService: IConfigurationService) { }
|
||||
@@ -117,11 +125,39 @@ class MultipleSelectionController<T> implements IMultipleSelectionController<T>
|
||||
}
|
||||
}
|
||||
|
||||
class OpenController implements IOpenController {
|
||||
|
||||
constructor(private configurationService: IConfigurationService) { }
|
||||
|
||||
shouldOpen(event: UIEvent): boolean {
|
||||
if (event instanceof MouseEvent) {
|
||||
const isDoubleClick = event.detail === 2;
|
||||
|
||||
return useSingleClickToOpen(this.configurationService) || isDoubleClick;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleListControllers<T>(options: IListOptions<T>, configurationService: IConfigurationService): IListOptions<T> {
|
||||
if (options.multipleSelectionSupport === true && !options.multipleSelectionController) {
|
||||
options.multipleSelectionController = new MultipleSelectionController(configurationService);
|
||||
}
|
||||
|
||||
options.openController = new OpenController(configurationService);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
export class WorkbenchList<T> extends List<T> {
|
||||
|
||||
readonly contextKeyService: IContextKeyService;
|
||||
|
||||
private listDoubleSelection: IContextKey<boolean>;
|
||||
|
||||
private _useAltAsMultipleSelectionModifier: boolean;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
delegate: IDelegate<T>,
|
||||
@@ -130,31 +166,45 @@ export class WorkbenchList<T> extends List<T> {
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
const multipleSelectionSupport = !(options.multipleSelectionSupport === false);
|
||||
super(container, delegate, renderers, mixin(handleListControllers(options, configurationService), { keyboardSupport: false } as IListOptions<any>, false));
|
||||
|
||||
if (multipleSelectionSupport && !options.multipleSelectionController) {
|
||||
options.multipleSelectionController = new MultipleSelectionController(configurationService);
|
||||
}
|
||||
|
||||
super(container, delegate, renderers, mixin(options, useAltAsMultipleSelectionModifier(configurationService)));
|
||||
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
|
||||
this.listDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService);
|
||||
|
||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
|
||||
|
||||
this.disposables.push(combinedDisposable([
|
||||
this.contextKeyService,
|
||||
(listService as ListService).register(this),
|
||||
attachListStyler(this, themeService),
|
||||
this.onSelectionChange(() => this.listDoubleSelection.set(this.getSelection().length === 2))
|
||||
]));
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
public get useAltAsMultipleSelectionModifier(): boolean {
|
||||
return this._useAltAsMultipleSelectionModifier;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.disposables.push(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
|
||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkbenchPagedList<T> extends PagedList<T> {
|
||||
|
||||
readonly contextKeyService: IContextKeyService;
|
||||
private disposable: IDisposable;
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
private _useAltAsMultipleSelectionModifier: boolean;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
@@ -164,57 +214,100 @@ export class WorkbenchPagedList<T> extends PagedList<T> {
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
const multipleSelectionSupport = !(options.multipleSelectionSupport === false);
|
||||
super(container, delegate, renderers, mixin(handleListControllers(options, configurationService), { keyboardSupport: false } as IListOptions<any>, false));
|
||||
|
||||
if (multipleSelectionSupport && !options.multipleSelectionController) {
|
||||
options.multipleSelectionController = new MultipleSelectionController(configurationService);
|
||||
}
|
||||
|
||||
super(container, delegate, renderers, mixin(options, useAltAsMultipleSelectionModifier(configurationService)));
|
||||
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
|
||||
|
||||
this.disposable = combinedDisposable([
|
||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
|
||||
|
||||
this.disposables.push(combinedDisposable([
|
||||
this.contextKeyService,
|
||||
(listService as ListService).register(this),
|
||||
attachListStyler(this, themeService)
|
||||
]);
|
||||
]));
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
public get useAltAsMultipleSelectionModifier(): boolean {
|
||||
return this._useAltAsMultipleSelectionModifier;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.disposables.push(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
|
||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposable.dispose();
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkbenchTree extends Tree {
|
||||
|
||||
readonly contextKeyService: IContextKeyService;
|
||||
|
||||
protected disposables: IDisposable[] = [];
|
||||
|
||||
private listDoubleSelection: IContextKey<boolean>;
|
||||
|
||||
private _openOnSingleClick: boolean;
|
||||
private _useAltAsMultipleSelectionModifier: boolean;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
configuration: ITreeConfiguration,
|
||||
options: ITreeOptions,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
super(container, configuration, options);
|
||||
super(container, configuration, mixin(options, { keyboardSupport: false } as ITreeOptions, false));
|
||||
|
||||
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
|
||||
this.listDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService);
|
||||
|
||||
this._openOnSingleClick = useSingleClickToOpen(configurationService);
|
||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
|
||||
|
||||
this.disposables.push(
|
||||
this.contextKeyService,
|
||||
(listService as ListService).register(this),
|
||||
attachListStyler(this, themeService)
|
||||
);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
public get openOnSingleClick(): boolean {
|
||||
return this._openOnSingleClick;
|
||||
}
|
||||
|
||||
public get useAltAsMultipleSelectionModifier(): boolean {
|
||||
return this._useAltAsMultipleSelectionModifier;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.disposables.push(this.onDidChangeSelection(() => {
|
||||
const selection = this.getSelection();
|
||||
this.listDoubleSelection.set(selection && selection.length === 2);
|
||||
}));
|
||||
|
||||
this.disposables.push(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(openModeSettingKey)) {
|
||||
this._openOnSingleClick = useSingleClickToOpen(this.configurationService);
|
||||
}
|
||||
|
||||
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
|
||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -222,6 +315,123 @@ export class WorkbenchTree extends Tree {
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkbenchTreeController extends DefaultController {
|
||||
|
||||
protected disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
options: IControllerOptions,
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
super(options);
|
||||
|
||||
// if the open mode is not set, we configure it based on settings
|
||||
if (isUndefinedOrNull(options.openMode)) {
|
||||
this.setOpenMode(this.getOpenModeSetting());
|
||||
this.registerListeners();
|
||||
}
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.disposables.push(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(openModeSettingKey)) {
|
||||
this.setOpenMode(this.getOpenModeSetting());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private getOpenModeSetting(): OpenMode {
|
||||
return useSingleClickToOpen(this.configurationService) ? OpenMode.SINGLE_CLICK : OpenMode.DOUBLE_CLICK;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IOpenResourceOptions {
|
||||
editorOptions: IEditorOptions;
|
||||
sideBySide: boolean;
|
||||
element: any;
|
||||
payload: any;
|
||||
}
|
||||
|
||||
export interface IResourceResultsNavigationOptions {
|
||||
openOnFocus: boolean;
|
||||
}
|
||||
|
||||
export default class ResourceResultsNavigation extends Disposable {
|
||||
|
||||
private _openResource: Emitter<IOpenResourceOptions> = new Emitter<IOpenResourceOptions>();
|
||||
public readonly openResource: Event<IOpenResourceOptions> = this._openResource.event;
|
||||
|
||||
constructor(private tree: WorkbenchTree, private options?: IResourceResultsNavigationOptions) {
|
||||
super();
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
if (this.options && this.options.openOnFocus) {
|
||||
this._register(this.tree.onDidChangeFocus(e => this.onFocus(e)));
|
||||
}
|
||||
|
||||
this._register(this.tree.onDidChangeSelection(e => this.onSelection(e)));
|
||||
}
|
||||
|
||||
private onFocus({ payload }: any): void {
|
||||
const element = this.tree.getFocus();
|
||||
this.tree.setSelection([element], { fromFocus: true });
|
||||
|
||||
const originalEvent: KeyboardEvent | MouseEvent = payload && payload.originalEvent;
|
||||
const isMouseEvent = payload && payload.origin === 'mouse';
|
||||
const isDoubleClick = isMouseEvent && originalEvent && originalEvent.detail === 2;
|
||||
|
||||
if (!isMouseEvent || this.tree.openOnSingleClick || isDoubleClick) {
|
||||
this._openResource.fire({
|
||||
editorOptions: {
|
||||
preserveFocus: true,
|
||||
pinned: false,
|
||||
revealIfVisible: true
|
||||
},
|
||||
sideBySide: false,
|
||||
element,
|
||||
payload
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onSelection({ payload }: any): void {
|
||||
if (payload && payload.fromFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalEvent: KeyboardEvent | MouseEvent = payload && payload.originalEvent;
|
||||
const isMouseEvent = payload && payload.origin === 'mouse';
|
||||
const isDoubleClick = isMouseEvent && originalEvent && originalEvent.detail === 2;
|
||||
|
||||
if (!isMouseEvent || this.tree.openOnSingleClick || isDoubleClick) {
|
||||
if (isDoubleClick && originalEvent) {
|
||||
originalEvent.preventDefault(); // focus moves to editor, we need to prevent default
|
||||
}
|
||||
|
||||
const isFromKeyboard = payload && payload.origin === 'keyboard';
|
||||
const sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey || originalEvent.altKey));
|
||||
const preserveFocus = !((isFromKeyboard && (!payload || !payload.preserveFocus)) || isDoubleClick || (payload && payload.focusEditor));
|
||||
this._openResource.fire({
|
||||
editorOptions: {
|
||||
preserveFocus,
|
||||
pinned: isDoubleClick,
|
||||
revealIfVisible: true
|
||||
},
|
||||
sideBySide,
|
||||
element: this.tree.getSelection()[0],
|
||||
payload
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
|
||||
configurationRegistry.registerConfiguration({
|
||||
@@ -230,7 +440,7 @@ configurationRegistry.registerConfiguration({
|
||||
'title': localize('workbenchConfigurationTitle', "Workbench"),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'workbench.multiSelectModifier': {
|
||||
'workbench.list.multiSelectModifier': {
|
||||
'type': 'string',
|
||||
'enum': ['ctrlCmd', 'alt'],
|
||||
'enumDescriptions': [
|
||||
@@ -244,7 +454,20 @@ configurationRegistry.registerConfiguration({
|
||||
'- `ctrlCmd` refers to a value the setting can take and should not be localized.',
|
||||
'- `Control` and `Command` refer to the modifier keys Ctrl or Cmd on the keyboard and can be localized.'
|
||||
]
|
||||
}, "The modifier to be used to add an item to a multi-selection with the mouse (for example in trees and lists, if supported). `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The 'Open to Side' mouse gestures - if supported - will adapt such that they do not conflict with the multiselect modifier.")
|
||||
}, "The modifier to be used to add an item in trees and lists to a multi-selection with the mouse (if supported). `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The 'Open to Side' mouse gestures - if supported - will adapt such that they do not conflict with the multiselect modifier.")
|
||||
},
|
||||
'workbench.list.openMode': {
|
||||
'type': 'string',
|
||||
'enum': ['singleClick', 'doubleClick'],
|
||||
'enumDescriptions': [
|
||||
localize('openMode.singleClick', "Opens items on mouse single click."),
|
||||
localize('openMode.doubleClick', "Open items on mouse double click.")
|
||||
],
|
||||
'default': 'singleClick',
|
||||
'description': localize({
|
||||
key: 'openModeModifier',
|
||||
comment: ['`singleClick` and `doubleClick` refers to a value the setting can take and should not be localized.']
|
||||
}, "Controls how to open items in trees and lists using the mouse (if supported). Set to `singleClick` to open items with a single mouse click and `doubleClick` to only open via mouse double click. For parents with children in trees, this setting will control if a single click expands the parent or a double click. Note that some trees and lists might chose to ignore this setting if it is not applicable. ")
|
||||
}
|
||||
}
|
||||
});
|
||||
Vendored
+3
-2
@@ -5495,13 +5495,14 @@ declare module 'vscode' {
|
||||
* will be matched against the file paths of resulting matches relative to their workspace. Use a [relative pattern](#RelativePattern)
|
||||
* to restrict the search results to a [workspace folder](#WorkspaceFolder).
|
||||
* @param exclude A [glob pattern](#GlobPattern) that defines files and folders to exclude. The glob pattern
|
||||
* will be matched against the file paths of resulting matches relative to their workspace.
|
||||
* will be matched against the file paths of resulting matches relative to their workspace. When `undefined` only default excludes will
|
||||
* apply, when `null` no excludes will apply.
|
||||
* @param maxResults An upper-bound for the result.
|
||||
* @param token A token that can be used to signal cancellation to the underlying search engine.
|
||||
* @return A thenable that resolves to an array of resource identifiers. Will return no results if no
|
||||
* [workspace folders](#workspace.workspaceFolders) are opened.
|
||||
*/
|
||||
export function findFiles(include: GlobPattern, exclude?: GlobPattern, maxResults?: number, token?: CancellationToken): Thenable<Uri[]>;
|
||||
export function findFiles(include: GlobPattern, exclude?: GlobPattern | null, maxResults?: number, token?: CancellationToken): Thenable<Uri[]>;
|
||||
|
||||
/**
|
||||
* Save all dirty files.
|
||||
|
||||
Vendored
+3
-3
@@ -159,7 +159,7 @@ declare module 'vscode' {
|
||||
export function registerFileSystemProvider(scheme: string, provider: FileSystemProvider): Disposable;
|
||||
|
||||
/**
|
||||
* Updates the workspace folders of the currently opened workspace. This method allows to add, remove
|
||||
* Updates the [workspace folders](#workspace.workspaceFolders) of the currently opened workspace. This method allows to add, remove
|
||||
* and change workspace folders a the same time. Use the [onDidChangeWorkspaceFolders()](#onDidChangeWorkspaceFolders)
|
||||
* event to get notified when the workspace folders have been updated.
|
||||
*
|
||||
@@ -181,11 +181,11 @@ declare module 'vscode' {
|
||||
* It is valid to remove an existing workspace folder and add it again with a different name
|
||||
* to rename that folder.
|
||||
*
|
||||
* Note: if the first workspace folder is added, removed or changed, all extensions will be restarted
|
||||
* **Note:** if the first workspace folder is added, removed or changed, all extensions will be restarted
|
||||
* so that the (deprecated) `rootPath` property is updated to point to the first workspace
|
||||
* folder.
|
||||
*
|
||||
* Note: it is not valid to call [updateWorkspaceFolders()](#updateWorkspaceFolders) multiple times
|
||||
* **Note:** it is not valid to call [updateWorkspaceFolders()](#updateWorkspaceFolders) multiple times
|
||||
* without waiting for the [onDidChangeWorkspaceFolders()](#onDidChangeWorkspaceFolders) to fire.
|
||||
*
|
||||
* @param start the zero-based location in the list of currently opened [workspace folders](#WorkspaceFolder)
|
||||
|
||||
@@ -97,7 +97,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
|
||||
// --- search ---
|
||||
|
||||
$startSearch(includePattern: string, includeFolder: string, excludePattern: string, maxResults: number, requestId: number): Thenable<URI[]> {
|
||||
$startSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<URI[]> {
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
if (!workspace.folders.length) {
|
||||
return undefined;
|
||||
@@ -129,7 +129,8 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
type: QueryType.File,
|
||||
maxResults,
|
||||
includePattern: { [typeof includePattern === 'string' ? includePattern : undefined]: true },
|
||||
excludePattern: { [typeof excludePattern === 'string' ? excludePattern : undefined]: true },
|
||||
excludePattern: { [typeof excludePatternOrDisregardExcludes === 'string' ? excludePatternOrDisregardExcludes : undefined]: true },
|
||||
disregardExcludeSettings: excludePatternOrDisregardExcludes === false,
|
||||
useRipgrep,
|
||||
ignoreSymlinks
|
||||
};
|
||||
@@ -168,4 +169,4 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
return result.results.every(each => each.success === true);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ export interface MainThreadTelemetryShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadWorkspaceShape extends IDisposable {
|
||||
$startSearch(includePattern: string, includeFolder: string, excludePattern: string, maxResults: number, requestId: number): Thenable<UriComponents[]>;
|
||||
$startSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<UriComponents[]>;
|
||||
$cancelSearch(requestId: number): Thenable<boolean>;
|
||||
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
|
||||
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void>;
|
||||
|
||||
@@ -603,11 +603,11 @@ export function toGlobPattern(pattern: vscode.GlobPattern): string | IRelativePa
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if (!isRelativePattern(pattern)) {
|
||||
return undefined;
|
||||
if (isRelativePattern(pattern)) {
|
||||
return new types.RelativePattern(pattern.base, pattern.pattern);
|
||||
}
|
||||
|
||||
return new types.RelativePattern(pattern.base, pattern.pattern);
|
||||
return pattern; // preserve `undefined` and `null`
|
||||
}
|
||||
|
||||
function isRelativePattern(obj: any): obj is vscode.RelativePattern {
|
||||
|
||||
@@ -222,7 +222,9 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
|
||||
}
|
||||
|
||||
// Try to accept directly
|
||||
return this.trySetWorkspaceFolders(newWorkspaceFolders);
|
||||
this.trySetWorkspaceFolders(newWorkspaceFolders);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getWorkspaceFolder(uri: vscode.Uri, resolveParent?: boolean): vscode.WorkspaceFolder {
|
||||
@@ -281,7 +283,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
|
||||
return normalize(result, true);
|
||||
}
|
||||
|
||||
private trySetWorkspaceFolders(folders: vscode.WorkspaceFolder[]): boolean {
|
||||
private trySetWorkspaceFolders(folders: vscode.WorkspaceFolder[]): void {
|
||||
|
||||
// Update directly here. The workspace is unconfirmed as long as we did not get an
|
||||
// acknowledgement from the main side (via $acceptWorkspaceData)
|
||||
@@ -292,11 +294,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
|
||||
configuration: this._actualWorkspace.configuration,
|
||||
folders
|
||||
} as IWorkspaceData, this._actualWorkspace).workspace;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$acceptWorkspaceData(data: IWorkspaceData): void {
|
||||
@@ -331,16 +329,18 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
|
||||
}
|
||||
}
|
||||
|
||||
let excludePattern: string;
|
||||
if (exclude) {
|
||||
let excludePatternOrDisregardExcludes: string | false;
|
||||
if (exclude === null) {
|
||||
excludePatternOrDisregardExcludes = false;
|
||||
} else if (exclude) {
|
||||
if (typeof exclude === 'string') {
|
||||
excludePattern = exclude;
|
||||
excludePatternOrDisregardExcludes = exclude;
|
||||
} else {
|
||||
excludePattern = exclude.pattern;
|
||||
excludePatternOrDisregardExcludes = exclude.pattern;
|
||||
}
|
||||
}
|
||||
|
||||
const result = this._proxy.$startSearch(includePattern, includeFolder, excludePattern, maxResults, requestId);
|
||||
const result = this._proxy.$startSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, requestId);
|
||||
if (token) {
|
||||
token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
|
||||
}
|
||||
|
||||
@@ -270,17 +270,17 @@ function registerEditorCommands() {
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
|
||||
handler: (accessor, resource: URI, context: IEditorIdentifier | IEditorCommandsContext) => {
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const model = editorGroupService.getStacksModel();
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const contexts = getMultiSelectedEditorContexts(toEditorIdentifier(context, editorGroupService), accessor.get(IListService));
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
|
||||
let positionOne: { unmodifiedOnly: boolean } = void 0;
|
||||
let positionTwo: { unmodifiedOnly: boolean } = void 0;
|
||||
let positionThree: { unmodifiedOnly: boolean } = void 0;
|
||||
contexts.forEach(c => {
|
||||
switch (model.positionOfGroup(c.group)) {
|
||||
switch (model.positionOfGroup(model.getGroup(c.groupId))) {
|
||||
case Position.ONE: positionOne = { unmodifiedOnly: true }; break;
|
||||
case Position.TWO: positionTwo = { unmodifiedOnly: true }; break;
|
||||
case Position.THREE: positionThree = { unmodifiedOnly: true }; break;
|
||||
@@ -296,14 +296,15 @@ function registerEditorCommands() {
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
|
||||
handler: (accessor, resource: URI, context: IEditorIdentifier | IEditorCommandsContext) => {
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const contexts = getMultiSelectedEditorContexts(toEditorIdentifier(context, editorGroupService), accessor.get(IListService));
|
||||
const distinctGroups = distinct(contexts.map(c => c.group));
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
const distinctGroupIds = distinct(contexts.map(c => c.groupId));
|
||||
const model = editorGroupService.getStacksModel();
|
||||
|
||||
if (distinctGroups.length) {
|
||||
return editorService.closeEditors(distinctGroups.map(g => editorGroupService.getStacksModel().positionOfGroup(g)));
|
||||
if (distinctGroupIds.length) {
|
||||
return editorService.closeEditors(distinctGroupIds.map(gid => model.positionOfGroup(model.getGroup(gid))));
|
||||
}
|
||||
const activeEditor = editorService.getActiveEditor();
|
||||
if (activeEditor) {
|
||||
@@ -320,21 +321,23 @@ function registerEditorCommands() {
|
||||
when: void 0,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
|
||||
win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
|
||||
handler: (accessor, resource: URI, context: IEditorIdentifier | IEditorCommandsContext) => {
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
|
||||
const contexts = getMultiSelectedEditorContexts(toEditorIdentifier(context, editorGroupService), accessor.get(IListService));
|
||||
const groups = distinct(contexts.map(context => context.group));
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
const groupIds = distinct(contexts.map(context => context.groupId));
|
||||
const model = editorGroupService.getStacksModel();
|
||||
|
||||
const editorsToClose = new Map<Position, IEditorInput[]>();
|
||||
|
||||
groups.forEach(group => {
|
||||
const position = editorGroupService.getStacksModel().positionOfGroup(group);
|
||||
groupIds.forEach(groupId => {
|
||||
const group = model.getGroup(groupId);
|
||||
const position = model.positionOfGroup(group);
|
||||
if (position >= 0) {
|
||||
editorsToClose.set(position, contexts.map(c => {
|
||||
if (group === c.group) {
|
||||
let input = c ? c.editor : void 0;
|
||||
if (c && groupId === c.groupId) {
|
||||
let input = group.getEditor(c.editorIndex);
|
||||
if (!input) {
|
||||
|
||||
// Get Top Editor at Position
|
||||
@@ -373,24 +376,26 @@ function registerEditorCommands() {
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
|
||||
handler: (accessor, resource: URI, context: IEditorIdentifier | IEditorCommandsContext) => {
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const contexts = getMultiSelectedEditorContexts(toEditorIdentifier(context, editorGroupService), accessor.get(IListService));
|
||||
const groups = distinct(contexts.map(context => context.group));
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
const groupIds = distinct(contexts.map(context => context.groupId));
|
||||
const editorsToClose = new Map<Position, IEditorInput[]>();
|
||||
const model = editorGroupService.getStacksModel();
|
||||
|
||||
groups.forEach(group => {
|
||||
groupIds.forEach(groupId => {
|
||||
const group = model.getGroup(groupId);
|
||||
const inputsToSkip = contexts.map(c => {
|
||||
if (!!c.editor && c.group === group) {
|
||||
return c.editor;
|
||||
if (c.groupId === groupId) {
|
||||
return group.getEditor(c.editorIndex);
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}).filter(input => !!input);
|
||||
|
||||
const toClose = group.getEditors().filter(input => inputsToSkip.indexOf(input) === -1);
|
||||
editorsToClose.set(editorGroupService.getStacksModel().positionOfGroup(group), toClose);
|
||||
editorsToClose.set(model.positionOfGroup(group), toClose);
|
||||
});
|
||||
|
||||
return editorService.closeEditors({
|
||||
@@ -406,7 +411,7 @@ function registerEditorCommands() {
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
handler: (accessor, resource: URI, context: IEditorIdentifier | IEditorCommandsContext) => {
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
|
||||
@@ -425,7 +430,7 @@ function registerEditorCommands() {
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: void 0,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter),
|
||||
handler: (accessor, resource: URI, context: IEditorIdentifier | IEditorCommandsContext) => {
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
|
||||
@@ -489,12 +494,13 @@ function registerEditorCommands() {
|
||||
});
|
||||
}
|
||||
|
||||
function positionAndInput(editorGroupService: IEditorGroupService, editorService: IWorkbenchEditorService, context?: IEditorIdentifier | IEditorCommandsContext): { position: Position, input: IEditorInput } {
|
||||
function positionAndInput(editorGroupService: IEditorGroupService, editorService: IWorkbenchEditorService, context?: IEditorCommandsContext): { position: Position, input: IEditorInput } {
|
||||
|
||||
// Resolve from context
|
||||
const editorContext = toEditorIdentifier(context, editorGroupService);
|
||||
let position = editorContext ? editorGroupService.getStacksModel().positionOfGroup(editorContext.group) : null;
|
||||
let input = editorContext ? editorContext.editor : null;
|
||||
const model = editorGroupService.getStacksModel();
|
||||
const group = context ? model.getGroup(context.groupId) : undefined;
|
||||
let position = group ? model.positionOfGroup(group) : undefined;
|
||||
let input = group ? group.getEditor(context.editorIndex) : undefined;
|
||||
|
||||
// If position or input are not passed in take the position and input of the active editor.
|
||||
const active = editorService.getActiveEditor();
|
||||
@@ -506,54 +512,28 @@ function positionAndInput(editorGroupService: IEditorGroupService, editorService
|
||||
return { position, input };
|
||||
}
|
||||
|
||||
export function getMultiSelectedEditorContexts(editorContext: IEditorIdentifier, listService: IListService): IEditorIdentifier[] {
|
||||
const elementToContext = (element: IEditorIdentifier | EditorGroup) => element instanceof EditorGroup ? { group: element, editor: void 0 } : element;
|
||||
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext, listService: IListService): IEditorCommandsContext[] {
|
||||
const elementToContext = (element: IEditorIdentifier | EditorGroup) =>
|
||||
element instanceof EditorGroup ? { groupId: element.id, editorIndex: undefined } : { groupId: element.group.id, editorIndex: element.group.indexOf(element.editor) };
|
||||
|
||||
// First check for a focused list to return the selected items from
|
||||
const list = listService.lastFocusedList;
|
||||
if (list instanceof List && list.isDOMFocused()) {
|
||||
const selection = list.getSelectedElements();
|
||||
const focus = list.getFocusedElements();
|
||||
const focusedElements: (IEditorIdentifier | EditorGroup)[] = list.getFocusedElements();
|
||||
// need to take into account when editor context is { group: group }
|
||||
const focus = editorContext ? editorContext : focusedElements.length ? focusedElements.map(elementToContext)[0] : undefined;
|
||||
|
||||
// Only respect selection if it contains focused element
|
||||
if (focus.length && selection && selection.indexOf(focus[0]) >= 0) {
|
||||
return list.getSelectedElements().filter(e => e instanceof EditorGroup || isEditorIdentifier(e)).map(elementToContext);
|
||||
}
|
||||
if (focus) {
|
||||
const selection: (IEditorIdentifier | EditorGroup)[] = list.getSelectedElements();
|
||||
// Only respect selection if it contains focused element
|
||||
if (selection && selection.some(s => s instanceof EditorGroup ? s.id === focus.groupId : s.group.id === focus.groupId && s.group.indexOf(s.editor) === focus.editorIndex)) {
|
||||
return selection.map(elementToContext);
|
||||
}
|
||||
|
||||
if (focus.length) {
|
||||
return focus.filter(e => e instanceof EditorGroup || isEditorIdentifier(e)).map(elementToContext);
|
||||
return [focus];
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise go with passed in context
|
||||
return !!editorContext ? [editorContext] : [];
|
||||
}
|
||||
|
||||
function isEditorIdentifier(object: any): object is IEditorIdentifier {
|
||||
const identifier = object as IEditorIdentifier;
|
||||
|
||||
return identifier && !!identifier.group && !!identifier.editor;
|
||||
}
|
||||
|
||||
function isEditorGroupContext(object: any): object is IEditorCommandsContext {
|
||||
const context = object as IEditorCommandsContext;
|
||||
|
||||
return context && typeof context.groupId === 'number';
|
||||
}
|
||||
|
||||
function toEditorIdentifier(object: IEditorIdentifier | IEditorCommandsContext, editorGroupService: IEditorGroupService): IEditorIdentifier {
|
||||
if (isEditorIdentifier(object)) {
|
||||
return object as IEditorIdentifier;
|
||||
}
|
||||
|
||||
if (isEditorGroupContext(object)) {
|
||||
const stacks = editorGroupService.getStacksModel();
|
||||
const group = stacks.getGroup(object.groupId);
|
||||
return {
|
||||
group,
|
||||
editor: typeof object.editorIndex === 'number' ? group.getEditor(object.editorIndex) : void 0
|
||||
};
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
@@ -635,7 +635,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
return void 0; // only for left mouse click
|
||||
}
|
||||
|
||||
const { editor, position } = this.toTabContext(index);
|
||||
const { editor, position } = this.getGroupPositionAndEditor(index);
|
||||
if (!this.isTabActionBar(((e as GestureEvent).initialTarget || e.target || e.srcElement) as HTMLElement)) {
|
||||
setTimeout(() => this.editorService.openEditor(editor, null, position).done(null, errors.onUnexpectedError)); // timeout to keep focus in editor after mouse up
|
||||
}
|
||||
@@ -646,7 +646,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
const showContextMenu = (e: Event) => {
|
||||
DOM.EventHelper.stop(e);
|
||||
|
||||
const { group, editor } = this.toTabContext(index);
|
||||
const { group, editor } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
this.onContextMenu({ group, editor }, e, tab);
|
||||
};
|
||||
@@ -668,7 +668,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
tab.blur();
|
||||
|
||||
if (e.button === 1 /* Middle Button*/ && !this.isTabActionBar((e.target || e.srcElement) as HTMLElement)) {
|
||||
this.closeEditorAction.run(this.toTabContext(index)).done(null, errors.onUnexpectedError);
|
||||
this.closeEditorAction.run(this.getGroupPositionAndEditor(index)).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -690,7 +690,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
let handled = false;
|
||||
|
||||
const { group, position, editor } = this.toTabContext(index);
|
||||
const { group, position, editor } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
// Run action on Enter/Space
|
||||
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
@@ -733,7 +733,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.DBLCLICK, (e: MouseEvent) => {
|
||||
DOM.EventHelper.stop(e);
|
||||
|
||||
const { group, editor } = this.toTabContext(index);
|
||||
const { group, editor } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
this.editorGroupService.pinEditor(group, editor);
|
||||
}));
|
||||
@@ -741,14 +741,14 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Context menu
|
||||
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.CONTEXT_MENU, (e: Event) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
const { group, editor } = this.toTabContext(index);
|
||||
const { group, editor } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
this.onContextMenu({ group, editor }, e, tab);
|
||||
}, true /* use capture to fix https://github.com/Microsoft/vscode/issues/19145 */));
|
||||
|
||||
// Drag start
|
||||
disposables.push(DOM.addDisposableListener(tab, DOM.EventType.DRAG_START, (e: DragEvent) => {
|
||||
const { group, editor } = this.toTabContext(index);
|
||||
const { group, editor } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
this.onEditorDragStart({ editor, group });
|
||||
e.dataTransfer.effectAllowed = 'copyMove';
|
||||
@@ -795,7 +795,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
let draggedEditorIsTab = false;
|
||||
const draggedEditor = TabsTitleControl.getDraggedEditor();
|
||||
if (draggedEditor) {
|
||||
const { group, editor } = this.toTabContext(index);
|
||||
const { group, editor } = this.getGroupPositionAndEditor(index);
|
||||
if (draggedEditor.editor === editor && draggedEditor.group === group) {
|
||||
draggedEditorIsTab = true;
|
||||
}
|
||||
@@ -832,7 +832,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
DOM.removeClass(tab, 'dragged-over');
|
||||
this.updateDropFeedback(tab, false, index);
|
||||
|
||||
const { group, position } = this.toTabContext(index);
|
||||
const { group, position } = this.getGroupPositionAndEditor(index);
|
||||
|
||||
this.onDrop(e, group, position, index);
|
||||
}));
|
||||
@@ -844,7 +844,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
return !!DOM.findParentWithClass(element, 'monaco-action-bar', 'tab');
|
||||
}
|
||||
|
||||
private toTabContext(index: number): { group: IEditorGroup, position: Position, editor: IEditorInput } {
|
||||
private getGroupPositionAndEditor(index: number): { group: IEditorGroup, position: Position, editor: IEditorInput } {
|
||||
const group = this.context;
|
||||
const position = this.stacks.positionOfGroup(group);
|
||||
const editor = group.getEditor(index);
|
||||
@@ -907,7 +907,7 @@ class TabActionRunner extends ActionRunner {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
|
||||
return super.run(action, { group, editor: group.getEditor(this.index) });
|
||||
return super.run(action, { groupId: group.id, editorIndex: this.index });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { IAction, IActionItem, ActionRunner } from 'vs/base/common/actions';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ClickBehavior, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IThemeService, LIGHT } from 'vs/platform/theme/common/themeService';
|
||||
import { createActionItem, fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
@@ -26,12 +26,13 @@ import { ViewsRegistry, TreeItemCollapsibleState, ITreeItem, ITreeViewDataProvid
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IViewletViewOptions, IViewOptions, TreeViewsViewletPanel, FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
import { ResourceLabel } from 'vs/workbench/browser/labels';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { basename } from 'vs/base/common/paths';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export class TreeView extends TreeViewsViewletPanel {
|
||||
|
||||
@@ -92,11 +93,11 @@ export class TreeView extends TreeViewsViewletPanel {
|
||||
const tree = this.instantiationService.createInstance(FileIconThemableWorkbenchTree,
|
||||
container.getHTMLElement(),
|
||||
{ dataSource, renderer, controller },
|
||||
{ keyboardSupport: false }
|
||||
{}
|
||||
);
|
||||
|
||||
tree.contextKeyService.createKey<boolean>(this.id, true);
|
||||
this.disposables.push(tree.onDidChangeSelection(() => this.onSelection()));
|
||||
this.disposables.push(tree.onDidChangeSelection(e => this.onSelection(e)));
|
||||
|
||||
return tree;
|
||||
}
|
||||
@@ -159,11 +160,17 @@ export class TreeView extends TreeViewsViewletPanel {
|
||||
return DOM.getLargestChildWidth(parentNode, childNodes);
|
||||
}
|
||||
|
||||
private onSelection(): void {
|
||||
private onSelection({ payload }: any): void {
|
||||
const selection: ITreeItem = this.tree.getSelection()[0];
|
||||
if (selection) {
|
||||
if (selection.command) {
|
||||
this.commandService.executeCommand(selection.command.id, ...(selection.command.arguments || []));
|
||||
const originalEvent: KeyboardEvent | MouseEvent = payload && payload.originalEvent;
|
||||
const isMouseEvent = payload && payload.origin === 'mouse';
|
||||
const isDoubleClick = isMouseEvent && originalEvent && originalEvent.detail === 2;
|
||||
|
||||
if (!isMouseEvent || this.tree.openOnSingleClick || isDoubleClick) {
|
||||
this.commandService.executeCommand(selection.command.id, ...(selection.command.arguments || []));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,15 +354,16 @@ class TreeRenderer implements IRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
class TreeController extends DefaultController {
|
||||
class TreeController extends WorkbenchTreeController {
|
||||
|
||||
constructor(
|
||||
private treeViewId: string,
|
||||
private menus: Menus,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IKeybindingService private _keybindingService: IKeybindingService
|
||||
@IKeybindingService private _keybindingService: IKeybindingService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, keyboardSupport: false });
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, keyboardSupport: false }, configurationService);
|
||||
}
|
||||
|
||||
public onContextMenu(tree: ITree, node: ITreeItem, event: ContextMenuEvent): boolean {
|
||||
|
||||
@@ -31,6 +31,7 @@ import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listServic
|
||||
import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export interface IViewOptions extends IPanelOptions {
|
||||
id: string;
|
||||
@@ -771,9 +772,10 @@ export class FileIconThemableWorkbenchTree extends WorkbenchTree {
|
||||
options: ITreeOptions,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IWorkbenchThemeService
|
||||
@IThemeService themeService: IWorkbenchThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super(container, configuration, { ...options, ...{ showTwistie: false, twistiePixels: 12 } }, contextKeyService, listService, themeService);
|
||||
super(container, configuration, { ...options, ...{ showTwistie: false, twistiePixels: 12 } }, contextKeyService, listService, themeService, configurationService);
|
||||
|
||||
DOM.addClass(container, 'file-icon-themable-tree');
|
||||
DOM.addClass(container, 'show-file-icons');
|
||||
|
||||
@@ -380,9 +380,9 @@ export class StackFrame implements IStackFrame {
|
||||
return `${this.name} (${this.source.inMemory ? this.source.name : this.source.uri.fsPath}:${this.range.startLineNumber})`;
|
||||
}
|
||||
|
||||
public openInEditor(editorService: IWorkbenchEditorService, preserveFocus?: boolean, sideBySide?: boolean): TPromise<any> {
|
||||
public openInEditor(editorService: IWorkbenchEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise<any> {
|
||||
return !this.source.available ? TPromise.as(null) :
|
||||
this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide);
|
||||
this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide, pinned);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ export class Source {
|
||||
return this.uri.scheme === DEBUG_SCHEME;
|
||||
}
|
||||
|
||||
public openInEditor(editorService: IWorkbenchEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean): TPromise<any> {
|
||||
public openInEditor(editorService: IWorkbenchEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise<any> {
|
||||
return !this.available ? TPromise.as(null) : editorService.openEditor({
|
||||
resource: this.uri,
|
||||
description: this.origin,
|
||||
@@ -65,7 +65,7 @@ export class Source {
|
||||
selection,
|
||||
revealIfVisible: true,
|
||||
revealInCenterIfOutsideViewport: true,
|
||||
pinned: !preserveFocus && !this.inMemory
|
||||
pinned: pinned || (!preserveFocus && !this.inMemory)
|
||||
}
|
||||
}, sideBySide);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,13 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
|
||||
import { ClickBehavior, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { ClickBehavior, IControllerOptions } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
|
||||
export const twistiePixels = 20;
|
||||
@@ -190,19 +192,23 @@ export function renderRenameBox(debugService: IDebugService, contextViewService:
|
||||
}));
|
||||
}
|
||||
|
||||
export class BaseDebugController extends DefaultController {
|
||||
export const DefaultDebugControllerOptions: IControllerOptions = { clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false };
|
||||
|
||||
export class BaseDebugController extends WorkbenchTreeController {
|
||||
|
||||
private contributedContextMenu: IMenu;
|
||||
|
||||
constructor(
|
||||
private actionProvider: IActionProvider,
|
||||
menuId: MenuId,
|
||||
options: IControllerOptions,
|
||||
@IDebugService protected debugService: IDebugService,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IMenuService menuService: IMenuService
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false });
|
||||
super(options, configurationService);
|
||||
|
||||
this.contributedContextMenu = menuService.createMenu(menuId, contextKeyService);
|
||||
}
|
||||
|
||||
@@ -78,29 +78,39 @@ export class BreakpointsView extends ViewsViewletPanel {
|
||||
|
||||
this.list.onContextMenu(this.onListContextMenu, this, this.disposables);
|
||||
|
||||
const handleBreakpointFocus = (preserveFocuse: boolean, sideBySide: boolean, selectFunctionBreakpoint: boolean) => {
|
||||
const handleBreakpointFocus = (preserveFocus: boolean, sideBySide: boolean, selectFunctionBreakpoint: boolean) => {
|
||||
const focused = this.list.getFocusedElements();
|
||||
const element = focused.length ? focused[0] : undefined;
|
||||
if (element instanceof Breakpoint) {
|
||||
openBreakpointSource(element, sideBySide, preserveFocuse, this.debugService, this.editorService).done(undefined, onUnexpectedError);
|
||||
openBreakpointSource(element, sideBySide, preserveFocus, this.debugService, this.editorService).done(undefined, onUnexpectedError);
|
||||
}
|
||||
if (selectFunctionBreakpoint && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) {
|
||||
this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
|
||||
this.onBreakpointsChange();
|
||||
}
|
||||
};
|
||||
this.disposables.push(this.list.onOpen(e => {
|
||||
let isSingleClick = false;
|
||||
let isDoubleClick = false;
|
||||
let openToSide = false;
|
||||
|
||||
const browserEvent = e.browserEvent;
|
||||
if (browserEvent instanceof MouseEvent) {
|
||||
isSingleClick = browserEvent.detail === 1;
|
||||
isDoubleClick = browserEvent.detail === 2;
|
||||
openToSide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey);
|
||||
}
|
||||
|
||||
handleBreakpointFocus(isSingleClick, openToSide, isDoubleClick);
|
||||
}));
|
||||
|
||||
// TODO@Isidor this should be a command (breakpoints.openToSide)
|
||||
this.disposables.push(this.list.onKeyUp(e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
if (event.equals(KeyCode.Enter)) {
|
||||
handleBreakpointFocus(false, event && (event.ctrlKey || event.metaKey || event.altKey), false);
|
||||
if (event.equals(KeyCode.Enter) && (event.ctrlKey || event.metaKey || event.altKey)) {
|
||||
handleBreakpointFocus(false, true, false);
|
||||
}
|
||||
}));
|
||||
this.disposables.push(this.list.onMouseDblClick(e => {
|
||||
handleBreakpointFocus(false, e.browserEvent.altKey, true);
|
||||
}));
|
||||
this.disposables.push(this.list.onMouseClick(e => {
|
||||
handleBreakpointFocus(true, e.browserEvent.altKey, false);
|
||||
}));
|
||||
|
||||
this.list.splice(0, this.list.length, this.elements);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { BaseDebugController, twistiePixels, renderViewTree, DefaultDebugControllerOptions } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { ITree, IActionProvider, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||
import { RestartAction, StopAction, ContinueAction, StepOverAction, StepIntoAction, StepOutAction, PauseAction, RestartFrameAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
@@ -24,9 +24,8 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
|
||||
import { basenameOrAuthority } from 'vs/base/common/resources';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import ResourceResultsNavigation, { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import FileResultsNavigation from 'vs/workbench/parts/files/browser/fileResultsNavigation';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -93,7 +92,7 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
dom.addClass(container, 'debug-call-stack');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
const actionProvider = new CallStackActionProvider(this.debugService, this.keybindingService);
|
||||
const controller = this.instantiationService.createInstance(CallStackController, actionProvider, MenuId.DebugCallStackContext);
|
||||
const controller = this.instantiationService.createInstance(CallStackController, actionProvider, MenuId.DebugCallStackContext, DefaultDebugControllerOptions);
|
||||
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new CallStackDataSource(),
|
||||
@@ -102,13 +101,12 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
controller
|
||||
}, {
|
||||
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'callStackAriaLabel' }, "Debug Call Stack"),
|
||||
twistiePixels,
|
||||
keyboardSupport: false
|
||||
twistiePixels
|
||||
});
|
||||
|
||||
const fileResultsNavigation = new FileResultsNavigation(this.tree);
|
||||
const fileResultsNavigation = new ResourceResultsNavigation(this.tree);
|
||||
this.disposables.push(fileResultsNavigation);
|
||||
this.disposables.push(fileResultsNavigation.openFile(e => {
|
||||
this.disposables.push(fileResultsNavigation.openResource(e => {
|
||||
if (this.ignoreSelectionChangedEvent) {
|
||||
return;
|
||||
}
|
||||
@@ -116,7 +114,7 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
const element = e.element;
|
||||
if (element instanceof StackFrame) {
|
||||
this.debugService.focusStackFrame(element, element.thread, element.thread.process, true);
|
||||
element.openInEditor(this.editorService, e.editorOptions.preserveFocus, e.sideBySide).done(undefined, errors.onUnexpectedError);
|
||||
element.openInEditor(this.editorService, e.editorOptions.preserveFocus, e.sideBySide, e.editorOptions.pinned).done(undefined, errors.onUnexpectedError);
|
||||
}
|
||||
if (element instanceof Thread) {
|
||||
this.debugService.focusStackFrame(undefined, element, element.process, true);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { DefaultController, ICancelableEvent, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { DefaultController, ICancelableEvent, ClickBehavior, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -75,8 +75,7 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
}, {
|
||||
indentPixels: 6,
|
||||
twistiePixels: 15,
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Debug Hover"),
|
||||
keyboardSupport: false
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Debug Hover")
|
||||
});
|
||||
|
||||
this.valueContainer = $('.value');
|
||||
@@ -337,7 +336,7 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
class DebugHoverController extends DefaultController {
|
||||
|
||||
constructor(private editor: ICodeEditor) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false });
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false, openMode: OpenMode.SINGLE_CLICK });
|
||||
}
|
||||
|
||||
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin = 'mouse'): boolean {
|
||||
|
||||
@@ -44,13 +44,13 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { ClickBehavior, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
const replTreeOptions: ITreeOptions = {
|
||||
twistiePixels: 20,
|
||||
ariaLabel: nls.localize('replAriaLabel', "Read Eval Print Loop Panel"),
|
||||
keyboardSupport: false
|
||||
ariaLabel: nls.localize('replAriaLabel', "Read Eval Print Loop Panel")
|
||||
};
|
||||
|
||||
const HISTORY_STORAGE_KEY = 'debug.repl.history';
|
||||
@@ -135,7 +135,7 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
this.createReplInput(this.container);
|
||||
|
||||
this.renderer = this.instantiationService.createInstance(ReplExpressionsRenderer);
|
||||
const controller = this.instantiationService.createInstance(ReplExpressionsController, new ReplExpressionsActionProvider(this.instantiationService), MenuId.DebugConsoleContext);
|
||||
const controller = this.instantiationService.createInstance(ReplExpressionsController, new ReplExpressionsActionProvider(this.instantiationService), MenuId.DebugConsoleContext, { clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false, openMode: OpenMode.SINGLE_CLICK });
|
||||
controller.toFocusOnClick = this.replInput;
|
||||
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
|
||||
@@ -28,6 +28,7 @@ import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { ClickBehavior, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -87,11 +88,10 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
dataSource: new VariablesDataSource(),
|
||||
renderer: this.instantiationService.createInstance(VariablesRenderer),
|
||||
accessibilityProvider: new VariablesAccessibilityProvider(),
|
||||
controller: this.instantiationService.createInstance(VariablesController, new VariablesActionProvider(this.debugService, this.keybindingService), MenuId.DebugVariablesContext)
|
||||
controller: this.instantiationService.createInstance(VariablesController, new VariablesActionProvider(this.debugService, this.keybindingService), MenuId.DebugVariablesContext, { clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false, openMode: OpenMode.SINGLE_CLICK })
|
||||
}, {
|
||||
ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"),
|
||||
twistiePixels,
|
||||
keyboardSupport: false
|
||||
twistiePixels
|
||||
});
|
||||
|
||||
CONTEXT_VARIABLES_FOCUSED.bindTo(this.tree.contextKeyService);
|
||||
|
||||
@@ -26,7 +26,7 @@ import { CopyValueAction } from 'vs/workbench/parts/debug/electron-browser/elect
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { IMouseEvent, DragMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { DefaultDragAndDrop, ClickBehavior, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IVariableTemplateData, renderVariable, renderRenameBox, renderExpressionValue, BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
|
||||
@@ -65,12 +65,11 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
dataSource: new WatchExpressionsDataSource(this.debugService),
|
||||
renderer: this.instantiationService.createInstance(WatchExpressionsRenderer),
|
||||
accessibilityProvider: new WatchExpressionsAccessibilityProvider(),
|
||||
controller: this.instantiationService.createInstance(WatchExpressionsController, actionProvider, MenuId.DebugWatchContext),
|
||||
controller: this.instantiationService.createInstance(WatchExpressionsController, actionProvider, MenuId.DebugWatchContext, { clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false, openMode: OpenMode.SINGLE_CLICK }),
|
||||
dnd: new WatchExpressionsDragAndDrop(this.debugService)
|
||||
}, {
|
||||
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"),
|
||||
twistiePixels,
|
||||
keyboardSupport: false
|
||||
twistiePixels
|
||||
});
|
||||
|
||||
CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.tree.contextKeyService);
|
||||
|
||||
@@ -9,13 +9,15 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise, Promise } from 'vs/base/common/winjs.base';
|
||||
import { IDataSource, ITree, IRenderer } from 'vs/base/parts/tree/browser/tree';
|
||||
import { DefaultController, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IExtensionDependencies, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export interface IExtensionTemplateData {
|
||||
icon: HTMLImageElement;
|
||||
@@ -156,21 +158,24 @@ export class Renderer implements IRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
export class Controller extends DefaultController {
|
||||
export class Controller extends WorkbenchTreeController {
|
||||
|
||||
constructor( @IExtensionsWorkbenchService private extensionsWorkdbenchService: IExtensionsWorkbenchService) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false });
|
||||
constructor(
|
||||
@IExtensionsWorkbenchService private extensionsWorkdbenchService: IExtensionsWorkbenchService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false }, configurationService);
|
||||
|
||||
// TODO@Sandeep this should be a command
|
||||
this.downKeyBindingDispatcher.set(KeyMod.CtrlCmd | KeyCode.Enter, (tree: ITree, event: any) => this.openExtension(tree, true));
|
||||
}
|
||||
|
||||
protected onLeftClick(tree: ITree, element: IExtensionDependencies, event: IMouseEvent): boolean {
|
||||
let currentFoucssed = tree.getFocus();
|
||||
let currentFocused = tree.getFocus();
|
||||
if (super.onLeftClick(tree, element, event)) {
|
||||
if (element.dependent === null) {
|
||||
if (currentFoucssed) {
|
||||
tree.setFocus(currentFoucssed);
|
||||
if (currentFocused) {
|
||||
tree.setFocus(currentFocused);
|
||||
} else {
|
||||
tree.focusFirst();
|
||||
}
|
||||
|
||||
@@ -526,8 +526,7 @@ export class ExtensionEditor extends BaseEditor {
|
||||
controller
|
||||
}, {
|
||||
indentPixels: 40,
|
||||
twistiePixels: 20,
|
||||
keyboardSupport: false
|
||||
twistiePixels: 20
|
||||
});
|
||||
|
||||
tree.setInput(extensionDependencies);
|
||||
|
||||
@@ -1564,10 +1564,10 @@ export class MaliciousStatusLabelAction extends Action {
|
||||
set extension(extension: IExtension) { this._extension = extension; this.update(); }
|
||||
|
||||
constructor(long: boolean) {
|
||||
const tooltip = localize('malicious tooltip', "This extension was reported to be malicious.");
|
||||
const tooltip = localize('malicious tooltip', "This extension was reported to be problematic.");
|
||||
const label = long ? tooltip : localize('malicious', "Malicious");
|
||||
super('extensions.install', label, '', false);
|
||||
this.tooltip = localize('malicious tooltip', "This extension was reported to be malicious.");
|
||||
this.tooltip = localize('malicious tooltip', "This extension was reported to be problematic.");
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
|
||||
@@ -486,7 +486,7 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution {
|
||||
if (maliciousExtensions.length) {
|
||||
return TPromise.join(maliciousExtensions.map(e => this.extensionsManagementService.uninstall(e, true).then(() => {
|
||||
this.messageService.show(Severity.Warning, {
|
||||
message: localize('malicious warning', "We have uninstalled '{0}' which was reported to be malicious.", getGalleryExtensionIdFromLocal(e)),
|
||||
message: localize('malicious warning', "We have uninstalled '{0}' which was reported to be problematic.", getGalleryExtensionIdFromLocal(e)),
|
||||
actions: [this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, localize('reloadNow', "Reload Now"))]
|
||||
});
|
||||
})));
|
||||
|
||||
@@ -78,11 +78,10 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
const delegate = new Delegate();
|
||||
const renderer = this.instantiationService.createInstance(Renderer);
|
||||
this.list = this.instantiationService.createInstance(WorkbenchPagedList, this.extensionsList, delegate, [renderer], {
|
||||
ariaLabel: localize('extensions', "Extensions"),
|
||||
keyboardSupport: false
|
||||
ariaLabel: localize('extensions', "Extensions")
|
||||
});
|
||||
|
||||
chain(this.list.onSelectionChange)
|
||||
chain(this.list.onOpen)
|
||||
.map(e => e.elements[0])
|
||||
.filter(e => !!e)
|
||||
.on(this.openExtension, this, this.disposables);
|
||||
@@ -443,6 +442,7 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
const activeEditorInput = this.editorService.getActiveEditorInput();
|
||||
|
||||
this.editorInputService.pinEditor(activeEditor.position, activeEditorInput);
|
||||
activeEditor.focus();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -569,7 +569,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
}
|
||||
|
||||
if (extension.isMalicious) {
|
||||
return TPromise.wrapError<void>(new Error(nls.localize('malicious', "This extension is reported to be malicious.")));
|
||||
return TPromise.wrapError<void>(new Error(nls.localize('malicious', "This extension is reported to be problematic.")));
|
||||
}
|
||||
|
||||
const ext = extension as Extension;
|
||||
@@ -581,7 +581,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
|
||||
return this.progressService.withProgress({
|
||||
location: ProgressLocation.Extensions,
|
||||
title: nls.localize('installingMarketPlaceExtension', 'Installing extension from Market place....'),
|
||||
title: nls.localize('installingMarketPlaceExtension', 'Installing extension from Marketplace....'),
|
||||
tooltip: `${extension.id}`
|
||||
}, () => this.extensionService.installFromGallery(gallery));
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
|
||||
export interface IOpenFileOptions {
|
||||
editorOptions: IEditorOptions;
|
||||
sideBySide: boolean;
|
||||
element: any;
|
||||
payload: any;
|
||||
}
|
||||
|
||||
export interface IFileResultsNavigationOptions {
|
||||
openOnFocus: boolean;
|
||||
}
|
||||
|
||||
export default class FileResultsNavigation extends Disposable {
|
||||
|
||||
private _openFile: Emitter<IOpenFileOptions> = new Emitter<IOpenFileOptions>();
|
||||
public readonly openFile: Event<IOpenFileOptions> = this._openFile.event;
|
||||
|
||||
constructor(private tree: ITree, options?: IFileResultsNavigationOptions) {
|
||||
super();
|
||||
if (options && options.openOnFocus) {
|
||||
this._register(this.tree.onDidChangeFocus(e => this.onFocus(e)));
|
||||
}
|
||||
this._register(this.tree.onDidChangeSelection(e => this.onSelection(e)));
|
||||
}
|
||||
|
||||
private onFocus(event: any): void {
|
||||
const element = this.tree.getFocus();
|
||||
this.tree.setSelection([element], { fromFocus: true });
|
||||
this._openFile.fire({
|
||||
editorOptions: {
|
||||
preserveFocus: true,
|
||||
pinned: false,
|
||||
revealIfVisible: true
|
||||
},
|
||||
sideBySide: false,
|
||||
element,
|
||||
payload: event.payload
|
||||
});
|
||||
}
|
||||
|
||||
private onSelection({ payload }: any): void {
|
||||
if (payload && payload.fromFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
const keyboard = payload && payload.origin === 'keyboard';
|
||||
const originalEvent: KeyboardEvent | MouseEvent = payload && payload.originalEvent;
|
||||
|
||||
const pinned = (payload && payload.origin === 'mouse' && originalEvent && originalEvent.detail === 2);
|
||||
if (pinned && originalEvent) {
|
||||
originalEvent.preventDefault(); // focus moves to editor, we need to prevent default
|
||||
}
|
||||
|
||||
const sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey || originalEvent.altKey));
|
||||
const preserveFocus = !((keyboard && (!payload || !payload.preserveFocus)) || pinned || (payload && payload.focusEditor));
|
||||
this._openFile.fire({
|
||||
editorOptions: {
|
||||
preserveFocus,
|
||||
pinned,
|
||||
revealIfVisible: true
|
||||
},
|
||||
sideBySide,
|
||||
element: this.tree.getSelection()[0],
|
||||
payload
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { FileStat, OpenEditor } from 'vs/workbench/parts/files/common/explorerMo
|
||||
import { toResource } from 'vs/workbench/common/editor';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IFileStat } from 'vs/platform/files/common/files';
|
||||
|
||||
// Commands can get exeucted from a command pallete, from a context menu or from some list using a keybinding
|
||||
// To cover all these cases we need to properly compute the resource on which the command is being executed
|
||||
@@ -38,18 +39,26 @@ export function getMultiSelectedResources(resource: URI, listService: IListServi
|
||||
if (list && list.isDOMFocused()) {
|
||||
// Explorer
|
||||
if (list instanceof Tree) {
|
||||
const focus = list.getFocus();
|
||||
const selection = list.getSelection();
|
||||
if (selection && selection.indexOf(focus) >= 0) {
|
||||
return selection.map(fs => fs.resource);
|
||||
const focus: IFileStat = list.getFocus();
|
||||
// If the resource is passed it has to be a part of the returned context.
|
||||
if (focus && (!URI.isUri(resource) || focus.resource.toString() === resource.toString())) {
|
||||
const selection = list.getSelection();
|
||||
// We only respect the selection if it contains the focused element.
|
||||
if (selection && selection.indexOf(focus) >= 0) {
|
||||
return selection.map(fs => fs.resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Open editors view
|
||||
if (list instanceof List) {
|
||||
const focus = list.getFocusedElements();
|
||||
const selection = list.getSelectedElements();
|
||||
if (selection && focus.length && selection.indexOf(focus[0]) >= 0) {
|
||||
return selection.filter(s => s instanceof OpenEditor).map((oe: OpenEditor) => oe.getResource());
|
||||
// If the resource is passed it has to be a part of the returned context.
|
||||
if (focus.length && (!URI.isUri(resource) || (focus[0] instanceof OpenEditor && focus[0].getResource().toString() === resource.toString()))) {
|
||||
const selection = list.getSelectedElements();
|
||||
// We only respect the selection if it contains the focused element.
|
||||
if (selection && selection.indexOf(focus[0]) >= 0) {
|
||||
return selection.filter(s => s instanceof OpenEditor).map((oe: OpenEditor) => oe.getResource());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,6 +380,10 @@ export class OpenEditor implements IEditorIdentifier {
|
||||
return this._editor;
|
||||
}
|
||||
|
||||
public get editorIndex() {
|
||||
return this._group.indexOf(this.editor);
|
||||
}
|
||||
|
||||
public get group() {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as labels from 'vs/base/common/labels';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { toResource, IEditorIdentifier } from 'vs/workbench/common/editor';
|
||||
import { toResource, IEditorCommandsContext } from 'vs/workbench/common/editor';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
@@ -492,8 +492,9 @@ CommandsRegistry.registerCommand({
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: SAVE_ALL_IN_GROUP_COMMAND_ID,
|
||||
handler: (accessor, resource: URI, editorContext: IEditorIdentifier) => {
|
||||
handler: (accessor, resource: URI, editorContext: IEditorCommandsContext) => {
|
||||
const contexts = getMultiSelectedEditorContexts(editorContext, accessor.get(IListService));
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
let saveAllArg: any;
|
||||
if (!contexts.length) {
|
||||
saveAllArg = true;
|
||||
@@ -501,7 +502,7 @@ CommandsRegistry.registerCommand({
|
||||
const fileService = accessor.get(IFileService);
|
||||
saveAllArg = [];
|
||||
contexts.forEach(context => {
|
||||
const editorGroup = context.group;
|
||||
const editorGroup = editorGroupService.getStacksModel().getGroup(context.groupId);
|
||||
editorGroup.getEditors().forEach(editor => {
|
||||
const resource = toResource(editor, { supportSideBySide: true });
|
||||
if (resource && (resource.scheme === 'untitled' || fileService.canHandleResource(resource))) {
|
||||
|
||||
@@ -403,8 +403,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView
|
||||
accessibilityProvider
|
||||
}, {
|
||||
autoExpandSingleChildren: true,
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Files Explorer"),
|
||||
keyboardSupport: false
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Files Explorer")
|
||||
});
|
||||
|
||||
// Bind context keys
|
||||
|
||||
@@ -29,7 +29,7 @@ import { ResourceMap } from 'vs/base/common/map';
|
||||
import { DuplicateFileAction, ImportFileAction, IEditableData, IFileViewletState } from 'vs/workbench/parts/files/electron-browser/fileActions';
|
||||
import { IDataSource, ITree, IAccessibilityProvider, IRenderer, ContextMenuEvent, ISorter, IFilter, IDragAndDropData, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY, DRAG_OVER_ACCEPT_BUBBLE_UP, DRAG_OVER_ACCEPT_BUBBLE_UP_COPY, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
|
||||
import { DesktopDragAndDropData, ExternalElementsDragAndDropData, SimpleFileResourceDragAndDrop } from 'vs/base/parts/tree/browser/treeDnd';
|
||||
import { ClickBehavior, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { FileStat, NewStatPlaceholder, Model } from 'vs/workbench/parts/files/common/explorerModel';
|
||||
import { DragMouseEvent, IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -56,7 +56,7 @@ import { extractResources } from 'vs/workbench/browser/editor';
|
||||
import { relative } from 'path';
|
||||
import { DataTransfers } from 'vs/base/browser/dnd';
|
||||
import { distinctParents } from 'vs/base/common/resources';
|
||||
import { WorkbenchTree, multiSelectModifierSettingKey } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
|
||||
export class FileDataSource implements IDataSource {
|
||||
constructor(
|
||||
@@ -325,12 +325,11 @@ export class FileAccessibilityProvider implements IAccessibilityProvider {
|
||||
}
|
||||
|
||||
// Explorer Controller
|
||||
export class FileController extends DefaultController implements IDisposable {
|
||||
export class FileController extends WorkbenchTreeController implements IDisposable {
|
||||
|
||||
private contributedContextMenu: IMenu;
|
||||
private toDispose: IDisposable[];
|
||||
private previousSelectionRangeStop: FileStat;
|
||||
private useAltAsMultiSelectModifier: boolean;
|
||||
|
||||
constructor(
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@@ -338,25 +337,14 @@ export class FileController extends DefaultController implements IDisposable {
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IMenuService private menuService: IMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, keyboardSupport: false /* handled via IListService */ });
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, keyboardSupport: false /* handled via IListService */ }, configurationService);
|
||||
|
||||
this.useAltAsMultiSelectModifier = configurationService.getValue(multiSelectModifierSettingKey) === 'alt';
|
||||
this.toDispose = [];
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
|
||||
this.useAltAsMultiSelectModifier = this.configurationService.getValue(multiSelectModifierSettingKey) === 'alt';
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public onLeftClick(tree: ITree, stat: FileStat | Model, event: IMouseEvent, origin: string = 'mouse'): boolean {
|
||||
public onLeftClick(tree: WorkbenchTree, stat: FileStat | Model, event: IMouseEvent, origin: string = 'mouse'): boolean {
|
||||
const payload = { origin: origin };
|
||||
const isDoubleClick = (origin === 'mouse' && event.detail === 2);
|
||||
|
||||
@@ -394,7 +382,7 @@ export class FileController extends DefaultController implements IDisposable {
|
||||
}
|
||||
|
||||
// Allow to multiselect
|
||||
if ((this.useAltAsMultiSelectModifier && event.altKey) || !this.useAltAsMultiSelectModifier && (event.ctrlKey || event.metaKey)) {
|
||||
if ((tree.useAltAsMultipleSelectionModifier && event.altKey) || !tree.useAltAsMultipleSelectionModifier && (event.ctrlKey || event.metaKey)) {
|
||||
const selection = tree.getSelection();
|
||||
this.previousSelectionRangeStop = undefined;
|
||||
if (selection.indexOf(stat) >= 0) {
|
||||
@@ -419,9 +407,12 @@ export class FileController extends DefaultController implements IDisposable {
|
||||
|
||||
// Select, Focus and open files
|
||||
else {
|
||||
|
||||
// Expand / Collapse
|
||||
tree.toggleExpansion(stat, event.altKey);
|
||||
this.previousSelectionRangeStop = undefined;
|
||||
if (isDoubleClick || this.openOnSingleClick) {
|
||||
tree.toggleExpansion(stat, event.altKey);
|
||||
this.previousSelectionRangeStop = undefined;
|
||||
}
|
||||
|
||||
const preserveFocus = !isDoubleClick;
|
||||
tree.setFocus(stat, payload);
|
||||
@@ -432,10 +423,10 @@ export class FileController extends DefaultController implements IDisposable {
|
||||
|
||||
tree.setSelection([stat], payload);
|
||||
|
||||
if (!stat.isDirectory) {
|
||||
if (!stat.isDirectory && (isDoubleClick || this.openOnSingleClick)) {
|
||||
let sideBySide = false;
|
||||
if (event) {
|
||||
sideBySide = this.useAltAsMultiSelectModifier ? (event.ctrlKey || event.metaKey) : event.altKey;
|
||||
sideBySide = tree.useAltAsMultipleSelectionModifier ? (event.ctrlKey || event.metaKey) : event.altKey;
|
||||
}
|
||||
|
||||
this.openEditor(stat, { preserveFocus, sideBySide, pinned: isDoubleClick });
|
||||
|
||||
@@ -28,15 +28,13 @@ import { EditorGroup } from 'vs/workbench/common/editor/editorStacksModel';
|
||||
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { WorkbenchList, useAltAsMultipleSelectionModifier } from 'vs/platform/list/browser/listService';
|
||||
import { IDelegate, IRenderer, IListContextMenuEvent, IListMouseEvent } from 'vs/base/browser/ui/list/list';
|
||||
import { WorkbenchList } from 'vs/platform/list/browser/listService';
|
||||
import { IDelegate, IRenderer, IListContextMenuEvent } from 'vs/base/browser/ui/list/list';
|
||||
import { EditorLabel } from 'vs/workbench/browser/labels';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
|
||||
@@ -153,7 +151,6 @@ export class OpenEditorsView extends ViewsViewletPanel {
|
||||
new EditorGroupRenderer(this.keybindingService, this.instantiationService, this.editorGroupService),
|
||||
new OpenEditorRenderer(getSelectedElements, this.instantiationService, this.keybindingService, this.configurationService, this.editorGroupService)
|
||||
], {
|
||||
keyboardSupport: false,
|
||||
identityProvider: element => element instanceof OpenEditor ? element.getId() : element.id.toString()
|
||||
});
|
||||
|
||||
@@ -185,15 +182,28 @@ export class OpenEditorsView extends ViewsViewletPanel {
|
||||
});
|
||||
|
||||
// Open when selecting via keyboard
|
||||
this.disposables.push(this.list.onMouseClick(e => this.onMouseClick(e, false)));
|
||||
this.disposables.push(this.list.onMouseDblClick(e => this.onMouseClick(e, true)));
|
||||
this.disposables.push(this.list.onKeyDown(e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
if (event.keyCode === KeyCode.Enter) {
|
||||
const focused = this.list.getFocusedElements();
|
||||
const element = focused.length ? focused[0] : undefined;
|
||||
if (element instanceof OpenEditor) {
|
||||
this.openEditor(element, { pinned: false, sideBySide: !!(event.altKey || event.ctrlKey || event.metaKey), preserveFocus: false });
|
||||
this.disposables.push(this.list.onOpen(e => {
|
||||
const browserEvent = e.browserEvent;
|
||||
|
||||
let openToSide = false;
|
||||
let isSingleClick = false;
|
||||
let isDoubleClick = false;
|
||||
let isMiddleClick = false;
|
||||
if (browserEvent instanceof MouseEvent) {
|
||||
isSingleClick = browserEvent.detail === 1;
|
||||
isDoubleClick = browserEvent.detail === 2;
|
||||
isMiddleClick = browserEvent.button === 1 /* middle button */;
|
||||
openToSide = this.list.useAltAsMultipleSelectionModifier ? (browserEvent.ctrlKey || browserEvent.metaKey) : browserEvent.altKey;
|
||||
}
|
||||
|
||||
const focused = this.list.getFocusedElements();
|
||||
const element = focused.length ? focused[0] : undefined;
|
||||
if (element instanceof OpenEditor) {
|
||||
if (isMiddleClick) {
|
||||
const position = this.model.positionOfGroup(element.group);
|
||||
this.editorService.closeEditor(position, element.editor).done(null, errors.onUnexpectedError);
|
||||
} else {
|
||||
this.openEditor(element, { preserveFocus: isSingleClick, pinned: isDoubleClick, sideBySide: openToSide });
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -268,21 +278,6 @@ export class OpenEditorsView extends ViewsViewletPanel {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private onMouseClick(event: IListMouseEvent<OpenEditor | IEditorGroup>, isDoubleClick: boolean): void {
|
||||
const element = event.element;
|
||||
if (!(element instanceof OpenEditor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.browserEvent && event.browserEvent.button === 1 /* Middle Button */) {
|
||||
const position = this.model.positionOfGroup(element.group);
|
||||
this.editorService.closeEditor(position, element.editor).done(null, errors.onUnexpectedError);
|
||||
} else {
|
||||
const sideBySide = useAltAsMultipleSelectionModifier(this.configurationService) ? event.browserEvent.altKey : (event.browserEvent.ctrlKey || event.browserEvent.metaKey);
|
||||
this.openEditor(element, { preserveFocus: !isDoubleClick, pinned: isDoubleClick, sideBySide });
|
||||
}
|
||||
}
|
||||
|
||||
private openEditor(element: OpenEditor, options: { preserveFocus: boolean; pinned: boolean; sideBySide: boolean; }): void {
|
||||
if (element) {
|
||||
/* __GDPR__
|
||||
@@ -311,7 +306,7 @@ export class OpenEditorsView extends ViewsViewletPanel {
|
||||
fillInActions(this.contributedContextMenu, { shouldForwardArgs: true, arg: element instanceof OpenEditor ? element.editor.getResource() : {} }, actions, this.contextMenuService);
|
||||
return TPromise.as(actions);
|
||||
},
|
||||
getActionsContext: () => element instanceof OpenEditor ? { group: element.group, editor: element.editor } : { group: element }
|
||||
getActionsContext: () => element instanceof OpenEditor ? { groupId: element.group.id, editorIndex: element.editorIndex } : { groupId: element.id }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -493,7 +488,7 @@ class EditorGroupRenderer implements IRenderer<IEditorGroup, IEditorGroupTemplat
|
||||
renderElement(editorGroup: IEditorGroup, index: number, templateData: IEditorGroupTemplateData): void {
|
||||
templateData.editorGroup = editorGroup;
|
||||
templateData.name.textContent = editorGroup.label;
|
||||
templateData.actionBar.context = { group: editorGroup };
|
||||
templateData.actionBar.context = { groupId: editorGroup.id };
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IEditorGroupTemplateData): void {
|
||||
@@ -597,7 +592,7 @@ class OpenEditorRenderer implements IRenderer<OpenEditor, IOpenEditorTemplateDat
|
||||
extraClasses: ['open-editor'],
|
||||
fileDecorations: this.configurationService.getValue<IFilesConfiguration>().explorer.decorations
|
||||
});
|
||||
templateData.actionBar.context = { group: editor.group, editor: editor.editor };
|
||||
templateData.actionBar.context = { groupId: editor.group.id, editorIndex: editor.editorIndex };
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IOpenEditorTemplateData): void {
|
||||
|
||||
@@ -27,11 +27,10 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import Messages from 'vs/workbench/parts/markers/common/messages';
|
||||
import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import FileResultsNavigation from 'vs/workbench/parts/files/browser/fileResultsNavigation';
|
||||
import { debounceEvent } from 'vs/base/common/event';
|
||||
import { SimpleFileResourceDragAndDrop } from 'vs/base/parts/tree/browser/treeDnd';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import ResourceResultsNavigation, { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { IMarkersWorkbenchService } from 'vs/workbench/parts/markers/common/markers';
|
||||
|
||||
export class MarkersPanel extends Panel {
|
||||
@@ -206,14 +205,13 @@ export class MarkersPanel extends Panel {
|
||||
}, {
|
||||
indentPixels: 0,
|
||||
twistiePixels: 20,
|
||||
ariaLabel: Messages.MARKERS_PANEL_ARIA_LABEL_PROBLEMS_TREE,
|
||||
keyboardSupport: false
|
||||
ariaLabel: Messages.MARKERS_PANEL_ARIA_LABEL_PROBLEMS_TREE
|
||||
});
|
||||
|
||||
Constants.MarkerFocusContextKey.bindTo(this.tree.contextKeyService);
|
||||
|
||||
const fileResultsNavigation = this._register(new FileResultsNavigation(this.tree, { openOnFocus: true }));
|
||||
this._register(debounceEvent(fileResultsNavigation.openFile, (last, event) => event, 75, true)(options => {
|
||||
const fileResultsNavigation = this._register(new ResourceResultsNavigation(this.tree, { openOnFocus: true }));
|
||||
this._register(debounceEvent(fileResultsNavigation.openResource, (last, event) => event, 75, true)(options => {
|
||||
this.openFileAtElement(options.element, options.editorOptions.preserveFocus, options.sideBySide, options.editorOptions.pinned);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -14,16 +14,18 @@ import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export class Controller extends treedefaults.DefaultController {
|
||||
export class Controller extends WorkbenchTreeController {
|
||||
|
||||
constructor(
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IMenuService private menuService: IMenuService,
|
||||
@IKeybindingService private _keybindingService: IKeybindingService
|
||||
@IKeybindingService private _keybindingService: IKeybindingService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN, keyboardSupport: false });
|
||||
super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN, keyboardSupport: false }, configurationService);
|
||||
}
|
||||
|
||||
protected onLeftClick(tree: tree.ITree, element: any, event: mouse.IMouseEvent): boolean {
|
||||
|
||||
@@ -333,7 +333,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
this.keybindingsListContainer = DOM.append(parent, $('.keybindings-list-container'));
|
||||
|
||||
this.keybindingsList = this._register(this.instantiationService.createInstance(WorkbenchList, this.keybindingsListContainer, new Delegate(), [new KeybindingHeaderRenderer(), new KeybindingItemRenderer(this, this.keybindingsService)],
|
||||
{ identityProvider: e => e.id, keyboardSupport: false, mouseSupport: true, ariaLabel: localize('keybindingsLabel', "Keybindings") }));
|
||||
{ identityProvider: e => e.id, mouseSupport: true, ariaLabel: localize('keybindingsLabel', "Keybindings") }));
|
||||
this._register(this.keybindingsList.onContextMenu(e => this.onContextMenu(e)));
|
||||
this._register(this.keybindingsList.onFocusChange(e => this.onFocusChange(e)));
|
||||
this._register(this.keybindingsList.onDidFocus(() => {
|
||||
|
||||
@@ -854,8 +854,7 @@ export class RepositoryPanel extends ViewletPanel {
|
||||
];
|
||||
|
||||
this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, {
|
||||
identityProvider: scmResourceIdentityProvider,
|
||||
keyboardSupport: false
|
||||
identityProvider: scmResourceIdentityProvider
|
||||
});
|
||||
|
||||
chain(this.list.onOpen)
|
||||
@@ -941,6 +940,7 @@ export class RepositoryPanel extends ViewletPanel {
|
||||
}
|
||||
|
||||
this.editorGroupService.pinEditor(activeEditor.position, activeEditorInput);
|
||||
activeEditor.focus();
|
||||
}
|
||||
|
||||
private onListContextMenu(e: IListContextMenuEvent<ISCMResourceGroup | ISCMResource>): void {
|
||||
|
||||
@@ -53,12 +53,11 @@ import { OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/act
|
||||
import * as Constants from 'vs/workbench/parts/search/common/constants';
|
||||
import { IThemeService, ITheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { editorFindMatchHighlight, diffInserted, diffRemoved, diffInsertedOutline, diffRemovedOutline, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import FileResultsNavigation from 'vs/workbench/parts/files/browser/fileResultsNavigation';
|
||||
import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/common/search';
|
||||
import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor';
|
||||
import { SimpleFileResourceDragAndDrop } from 'vs/base/parts/tree/browser/treeDnd';
|
||||
import { isDiffEditor, isCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import ResourceResultsNavigation, { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
export class SearchViewlet extends Viewlet {
|
||||
@@ -84,7 +83,7 @@ export class SearchViewlet extends Viewlet {
|
||||
private searching: boolean;
|
||||
|
||||
private actions: SearchAction[] = [];
|
||||
private tree: ITree;
|
||||
private tree: WorkbenchTree;
|
||||
private viewletSettings: any;
|
||||
private messages: Builder;
|
||||
private searchWidgetsContainer: Builder;
|
||||
@@ -505,15 +504,14 @@ export class SearchViewlet extends Viewlet {
|
||||
accessibilityProvider: this.instantiationService.createInstance(SearchAccessibilityProvider),
|
||||
dnd
|
||||
}, {
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Search Results"),
|
||||
keyboardSupport: false
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Search Results")
|
||||
});
|
||||
|
||||
this.tree.setInput(this.viewModel.searchResult);
|
||||
this.toUnbind.push(renderer);
|
||||
|
||||
const fileResultsNavigation = this._register(new FileResultsNavigation(this.tree, { openOnFocus: true }));
|
||||
this._register(debounceEvent(fileResultsNavigation.openFile, (last, event) => event, 75, true)(options => {
|
||||
const fileResultsNavigation = this._register(new ResourceResultsNavigation(this.tree, { openOnFocus: true }));
|
||||
this._register(debounceEvent(fileResultsNavigation.openResource, (last, event) => event, 75, true)(options => {
|
||||
if (options.element instanceof Match) {
|
||||
let selectedMatch: Match = options.element;
|
||||
if (this.currentSelectedFileMatch) {
|
||||
|
||||
@@ -289,6 +289,25 @@ export class ExtensionScannerInput {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IExtensionReference {
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface IExtensionResolver {
|
||||
resolveExtensions(): TPromise<IExtensionReference[]>;
|
||||
}
|
||||
|
||||
class DefaultExtensionResolver implements IExtensionResolver {
|
||||
|
||||
constructor(private root: string) { }
|
||||
|
||||
resolveExtensions(): TPromise<IExtensionReference[]> {
|
||||
return pfs.readDirsInDir(this.root)
|
||||
.then(folders => folders.map(name => ({ name, path: join(this.root, name) })));
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtensionScanner {
|
||||
|
||||
/**
|
||||
@@ -318,10 +337,14 @@ export class ExtensionScanner {
|
||||
/**
|
||||
* Scan a list of extensions defined in `absoluteFolderPath`
|
||||
*/
|
||||
public static async scanExtensions(input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
|
||||
public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolver?: IExtensionResolver): TPromise<IExtensionDescription[]> {
|
||||
const absoluteFolderPath = input.absoluteFolderPath;
|
||||
const isBuiltin = input.isBuiltin;
|
||||
|
||||
if (!resolver) {
|
||||
resolver = new DefaultExtensionResolver(absoluteFolderPath);
|
||||
}
|
||||
|
||||
try {
|
||||
let obsolete: { [folderName: string]: boolean; } = {};
|
||||
if (!isBuiltin) {
|
||||
@@ -333,38 +356,35 @@ export class ExtensionScanner {
|
||||
}
|
||||
}
|
||||
|
||||
const rawFolders = await pfs.readDirsInDir(absoluteFolderPath);
|
||||
let refs = await resolver.resolveExtensions();
|
||||
|
||||
// Ensure the same extension order
|
||||
rawFolders.sort();
|
||||
refs.sort((a, b) => a.name < b.name ? -1 : 1);
|
||||
|
||||
let folders: string[] = null;
|
||||
if (isBuiltin) {
|
||||
folders = rawFolders;
|
||||
} else {
|
||||
if (!isBuiltin) {
|
||||
// TODO: align with extensionsService
|
||||
const nonGallery: string[] = [];
|
||||
const gallery: string[] = [];
|
||||
const nonGallery: IExtensionReference[] = [];
|
||||
const gallery: IExtensionReference[] = [];
|
||||
|
||||
rawFolders.forEach(folder => {
|
||||
if (obsolete[folder]) {
|
||||
refs.forEach(ref => {
|
||||
if (obsolete[ref.name]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { id, version } = getIdAndVersionFromLocalExtensionId(folder);
|
||||
const { id, version } = getIdAndVersionFromLocalExtensionId(ref.name);
|
||||
|
||||
if (!id || !version) {
|
||||
nonGallery.push(folder);
|
||||
nonGallery.push(ref);
|
||||
} else {
|
||||
gallery.push(folder);
|
||||
gallery.push(ref);
|
||||
}
|
||||
});
|
||||
|
||||
folders = [...nonGallery, ...gallery];
|
||||
refs = [...nonGallery, ...gallery];
|
||||
}
|
||||
|
||||
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
|
||||
let extensionDescriptions = await TPromise.join(folders.map(f => this.scanExtension(input.ourVersion, log, join(absoluteFolderPath, f), isBuiltin, nlsConfig)));
|
||||
let extensionDescriptions = await TPromise.join(refs.map(r => this.scanExtension(input.ourVersion, log, r.path, isBuiltin, nlsConfig)));
|
||||
extensionDescriptions = extensionDescriptions.filter(item => item !== null);
|
||||
|
||||
if (!isBuiltin) {
|
||||
|
||||
@@ -19,7 +19,7 @@ import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService,
|
||||
import { IExtensionEnablementService, IExtensionIdentifier, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import { ExtensionScanner, ILog, ExtensionScannerInput } from 'vs/workbench/services/extensions/electron-browser/extensionPoints';
|
||||
import { ExtensionScanner, ILog, ExtensionScannerInput, IExtensionResolver, IExtensionReference } from 'vs/workbench/services/extensions/electron-browser/extensionPoints';
|
||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
|
||||
import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
@@ -46,6 +46,42 @@ import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol';
|
||||
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
|
||||
const ExtraDevSystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', '.build', 'builtInExtensions'));
|
||||
|
||||
interface IBuiltInExtension {
|
||||
name: string;
|
||||
version: string;
|
||||
repo: string;
|
||||
}
|
||||
|
||||
interface IBuiltInExtensionControl {
|
||||
[name: string]: 'marketplace' | 'disabled' | string;
|
||||
}
|
||||
|
||||
class ExtraBuiltInExtensionResolver implements IExtensionResolver {
|
||||
|
||||
constructor(private builtInExtensions: IBuiltInExtension[], private control: IBuiltInExtensionControl) { }
|
||||
|
||||
resolveExtensions(): TPromise<IExtensionReference[]> {
|
||||
const result: IExtensionReference[] = [];
|
||||
|
||||
for (const ext of this.builtInExtensions) {
|
||||
const controlState = this.control[ext.name] || 'marketplace';
|
||||
|
||||
switch (controlState) {
|
||||
case 'disabled':
|
||||
break;
|
||||
case 'marketplace':
|
||||
result.push({ name: ext.name, path: path.join(ExtraDevSystemExtensionsRoot, ext.name) });
|
||||
break;
|
||||
default:
|
||||
result.push({ name: ext.name, path: controlState });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TPromise.as(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Enable to see detailed message communication between window and extension host
|
||||
const logExtensionHostCommunication = false;
|
||||
|
||||
@@ -632,7 +668,19 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
let finalBuiltinExtensions: TPromise<IExtensionDescription[]> = builtinExtensions;
|
||||
|
||||
if (devMode) {
|
||||
const extraBuiltinExtensions = ExtensionScanner.scanExtensions(new ExtensionScannerInput(version, commit, locale, devMode, ExtraDevSystemExtensionsRoot, true), log);
|
||||
const builtInExtensionsFilePath = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'build', 'builtInExtensions.json'));
|
||||
const builtInExtensions = pfs.readFile(builtInExtensionsFilePath, 'utf8')
|
||||
.then<IBuiltInExtension[]>(raw => JSON.parse(raw));
|
||||
|
||||
const controlFilePath = path.join(process.env['HOME'], '.vscode-oss-dev', 'extensions', 'control.json');
|
||||
const controlFile = pfs.readFile(controlFilePath, 'utf8')
|
||||
.then<IBuiltInExtensionControl>(raw => JSON.parse(raw), () => ({} as any));
|
||||
|
||||
const input = new ExtensionScannerInput(version, commit, locale, devMode, ExtraDevSystemExtensionsRoot, true);
|
||||
const extraBuiltinExtensions = TPromise.join([builtInExtensions, controlFile])
|
||||
.then(([builtInExtensions, control]) => new ExtraBuiltInExtensionResolver(builtInExtensions, control))
|
||||
.then(resolver => ExtensionScanner.scanExtensions(input, log, resolver));
|
||||
|
||||
finalBuiltinExtensions = TPromise.join([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => {
|
||||
let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null);
|
||||
for (let i = 0, len = builtinExtensions.length; i < len; i++) {
|
||||
|
||||
Reference in New Issue
Block a user