;
}
@@ -120,14 +122,16 @@ export class CallHierarchyTreeWidget extends TreeWidget {
const container = (containerName) ? containerName + ' — ' + location : location;
return
-
- {symbol}
-
-
- {(referenceCount > 1) ? `[${referenceCount}]` : ''}
-
-
- {container}
+
+
+ {symbol}
+
+
+ {(referenceCount > 1) ? `[${referenceCount}]` : ''}
+
+
+ {container}
+
;
}
diff --git a/packages/callhierarchy/src/browser/style/index.css b/packages/callhierarchy/src/browser/style/index.css
index a556ba015c15f..d82492f8926db 100644
--- a/packages/callhierarchy/src/browser/style/index.css
+++ b/packages/callhierarchy/src/browser/style/index.css
@@ -24,11 +24,12 @@
}
.theia-CallHierarchyTree .theia-ExpansionToggle {
- min-width: 16px;
+ min-width: 9px;
+ padding-right: 4px;
}
.theia-CallHierarchyTree .noCallers {
- margin: 5px;
+ margin: 5px 19px;
}
.theia-CallHierarchyTree .definitionNode {
@@ -44,23 +45,23 @@
}
.theia-CallHierarchyTree .definitionNode .symbol {
- white-space: nowrap;
- overflow: hidden;
+ padding-right: 4px;
}
.theia-CallHierarchyTree .definitionNode .referenceCount {
- white-space: nowrap;
- overflow: hidden;
color: var(--theia-ui-font-color3);
}
.theia-CallHierarchyTree .definitionNode .container {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
color: var(--theia-ui-font-color2);
}
.call-hierarchy-tab-icon::before {
content: "\f0ab"
}
+
+.theia-CallHierarchyTree .definitionNode-content {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
diff --git a/packages/console/package.json b/packages/console/package.json
index b62e4eb70fdc3..f85193c014c5e 100644
--- a/packages/console/package.json
+++ b/packages/console/package.json
@@ -1,10 +1,10 @@
{
"name": "@theia/console",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Console Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/monaco": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/monaco": "^0.5.0",
"anser": "^1.4.7"
},
"publishConfig": {
@@ -36,11 +36,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/core/package.json b/packages/core/package.json
index f0a3815d8c3d3..1b0f953c112e4 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,12 +1,12 @@
{
"name": "@theia/core",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.",
"main": "lib/common/index.js",
"typings": "lib/common/index.d.ts",
"dependencies": {
"@phosphor/widgets": "^1.5.0",
- "@theia/application-package": "^0.4.0",
+ "@theia/application-package": "^0.5.0",
"@types/body-parser": "^1.16.4",
"@types/bunyan": "^1.8.0",
"@types/express": "^4.16.0",
@@ -20,7 +20,7 @@
"@types/yargs": "^11.1.0",
"ajv": "^6.5.3",
"body-parser": "^1.17.2",
- "electron": "^2.0.14",
+ "electron": "^3.1.7",
"electron-store": "^2.0.0",
"es6-promise": "^4.2.4",
"express": "^4.16.3",
@@ -31,6 +31,7 @@
"inversify": "^4.14.0",
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.1.1",
+ "nsfw": "^1.2.2",
"perfect-scrollbar": "^1.3.0",
"react": "^16.4.1",
"react-dom": "^16.4.1",
@@ -39,7 +40,6 @@
"reflect-metadata": "^0.1.10",
"route-parser": "^0.0.5",
"vscode-languageserver-types": "^3.10.0",
- "vscode-nsfw": "^1.0.17",
"vscode-uri": "^1.0.1",
"vscode-ws-jsonrpc": "^0.0.2-1",
"ws": "^5.2.2",
@@ -79,11 +79,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts
index 8ff0b10ceec1f..9c3510ce9bb87 100644
--- a/packages/core/src/browser/common-frontend-contribution.ts
+++ b/packages/core/src/browser/common-frontend-contribution.ts
@@ -30,7 +30,7 @@ import { AboutDialog } from './about-dialog';
import * as browser from './browser';
import URI from '../common/uri';
import { ContextKeyService } from './context-key-service';
-import { OS } from '../common/os';
+import { OS, isOSX } from '../common/os';
import { ResourceContextKey } from './resource-context-key';
import { UriSelection } from '../common/selection';
@@ -216,6 +216,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
this.contextKeyService.createKey
('isWindows', OS.type() === OS.Type.Windows);
this.initResourceContextKeys();
+ this.registerCtrlWHandling();
}
protected initResourceContextKeys(): void {
@@ -580,12 +581,36 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
this.aboutDialog.open();
}
+ protected shouldPreventClose = false;
+
+ /**
+ * registers event listener which make sure that
+ * window doesn't get closed if CMD/CTRL W is pressed.
+ * Too many users have that in their muscle memory.
+ * Chrome doesn't let us rebind or prevent default the keybinding, so this
+ * at least doesn't close the window immediately.
+ */
+ protected registerCtrlWHandling() {
+ function isCtrlCmd(event: KeyboardEvent) {
+ return (isOSX && event.metaKey) || (!isOSX && event.ctrlKey);
+ }
+
+ window.document.addEventListener('keydown', event => {
+ this.shouldPreventClose = isCtrlCmd(event) || event.code === 'KeyW';
+ });
+
+ window.document.addEventListener('keyup', () => {
+ this.shouldPreventClose = false;
+ });
+ }
+
onWillStop() {
- if (this.shell.canSaveAll()) {
- setTimeout(() => {
- this.messageService.info('Some documents should be saved, data will be lost otherwise.');
- });
- return true;
+ try {
+ if (this.shouldPreventClose || this.shell.canSaveAll()) {
+ return true;
+ }
+ } finally {
+ this.shouldPreventClose = false;
}
}
}
diff --git a/packages/core/src/browser/dialogs.ts b/packages/core/src/browser/dialogs.ts
index a8ad83b270e59..8f25d23995f26 100644
--- a/packages/core/src/browser/dialogs.ts
+++ b/packages/core/src/browser/dialogs.ts
@@ -15,7 +15,7 @@
********************************************************************************/
import { injectable, inject } from 'inversify';
-import { Disposable } from '../common';
+import { Disposable, MaybePromise, CancellationTokenSource } from '../common';
import { Key } from './keys';
import { Widget, BaseWidget, Message } from './widgets';
@@ -168,7 +168,7 @@ export abstract class AbstractDialog extends BaseWidget {
open(): Promise {
if (this.resolve) {
- return Promise.reject('The dialog is already opened.');
+ return Promise.reject(new Error('The dialog is already opened.'));
}
this.activeElement = window.document.activeElement as HTMLElement;
return new Promise((resolve, reject) => {
@@ -194,31 +194,45 @@ export abstract class AbstractDialog extends BaseWidget {
this.activeElement = undefined;
super.close();
}
-
protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
this.validate();
}
- protected validate(): void {
+ protected validateCancellationSource = new CancellationTokenSource();
+ protected async validate(): Promise {
if (!this.resolve) {
return;
}
+ this.validateCancellationSource.cancel();
+ this.validateCancellationSource = new CancellationTokenSource();
+ const token = this.validateCancellationSource.token;
const value = this.value;
- const error = this.isValid(value, 'preview');
+ const error = await this.isValid(value, 'preview');
+ if (token.isCancellationRequested) {
+ return;
+ }
this.setErrorMessage(error);
}
- protected accept(): void {
- if (this.resolve) {
- const value = this.value;
- const error = this.isValid(value, 'open');
- if (!DialogError.getResult(error)) {
- this.setErrorMessage(error);
- } else {
- this.resolve(value);
- Widget.detach(this);
- }
+ protected acceptCancellationSource = new CancellationTokenSource();
+ protected async accept(): Promise {
+ if (!this.resolve) {
+ return;
+ }
+ this.acceptCancellationSource.cancel();
+ this.acceptCancellationSource = new CancellationTokenSource();
+ const token = this.acceptCancellationSource.token;
+ const value = this.value;
+ const error = await this.isValid(value, 'open');
+ if (token.isCancellationRequested) {
+ return;
+ }
+ if (!DialogError.getResult(error)) {
+ this.setErrorMessage(error);
+ } else {
+ this.resolve(value);
+ Widget.detach(this);
}
}
@@ -227,7 +241,7 @@ export abstract class AbstractDialog extends BaseWidget {
/**
* Return a string of zero-length or true if valid.
*/
- protected isValid(value: T, mode: DialogMode): DialogError {
+ protected isValid(value: T, mode: DialogMode): MaybePromise {
return '';
}
@@ -299,7 +313,7 @@ export class SingleTextInputDialogProps extends DialogProps {
end: number
direction?: 'forward' | 'backward' | 'none'
};
- readonly validate?: (input: string, mode: DialogMode) => DialogError;
+ readonly validate?: (input: string, mode: DialogMode) => MaybePromise;
}
export class SingleTextInputDialog extends AbstractDialog {
@@ -333,7 +347,7 @@ export class SingleTextInputDialog extends AbstractDialog {
return this.inputField.value;
}
- protected isValid(value: string, mode: DialogMode): DialogError {
+ protected isValid(value: string, mode: DialogMode): MaybePromise {
if (this.props.validate) {
return this.props.validate(value, mode);
}
diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts
index 052e5f74e7e91..eeff9e95a0a4e 100644
--- a/packages/core/src/browser/frontend-application-module.ts
+++ b/packages/core/src/browser/frontend-application-module.ts
@@ -44,7 +44,9 @@ import { LocalStorageService, StorageService } from './storage-service';
import { WidgetFactory, WidgetManager } from './widget-manager';
import {
ApplicationShell, ApplicationShellOptions, DockPanelRenderer, TabBarRenderer,
- TabBarRendererFactory, ShellLayoutRestorer, SidePanelHandler, SidePanelHandlerFactory, SplitPositionHandler, DockPanelRendererFactory
+ TabBarRendererFactory, ShellLayoutRestorer,
+ SidePanelHandler, SidePanelHandlerFactory,
+ SplitPositionHandler, DockPanelRendererFactory
} from './shell';
import { StatusBar, StatusBarImpl } from './status-bar/status-bar';
import { LabelParser } from './label-parser';
diff --git a/packages/core/src/browser/frontend-application.ts b/packages/core/src/browser/frontend-application.ts
index f33184cabce3f..4ee111a39809d 100644
--- a/packages/core/src/browser/frontend-application.ts
+++ b/packages/core/src/browser/frontend-application.ts
@@ -171,7 +171,7 @@ export class FrontendApplication {
document.addEventListener('keydown', event => this.keybindings.run(event), true);
// Prevent forward/back navigation by scrolling in OS X
if (isOSX) {
- document.body.addEventListener('wheel', preventNavigation);
+ document.body.addEventListener('wheel', preventNavigation, { passive: false });
}
}
diff --git a/packages/core/src/browser/icons/open-change-bright.svg b/packages/core/src/browser/icons/open-change-bright.svg
new file mode 100644
index 0000000000000..8c2b0f890b0f7
--- /dev/null
+++ b/packages/core/src/browser/icons/open-change-bright.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/core/src/browser/icons/open-change-dark.svg b/packages/core/src/browser/icons/open-change-dark.svg
new file mode 100644
index 0000000000000..4e655f075e133
--- /dev/null
+++ b/packages/core/src/browser/icons/open-change-dark.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/packages/core/src/browser/icons/preview-bright.svg b/packages/core/src/browser/icons/preview-bright.svg
new file mode 100644
index 0000000000000..d4282dc517c65
--- /dev/null
+++ b/packages/core/src/browser/icons/preview-bright.svg
@@ -0,0 +1,4 @@
+
+
+
+PreviewInRightPanel_16x
\ No newline at end of file
diff --git a/packages/core/src/browser/icons/preview-dark.svg b/packages/core/src/browser/icons/preview-dark.svg
new file mode 100644
index 0000000000000..f65df35e141d8
--- /dev/null
+++ b/packages/core/src/browser/icons/preview-dark.svg
@@ -0,0 +1,4 @@
+
+
+
+PreviewInRightPanel_16x
\ No newline at end of file
diff --git a/packages/core/src/browser/messaging/ws-connection-provider.ts b/packages/core/src/browser/messaging/ws-connection-provider.ts
index fec8cb55464ed..e6e4f2ab4e522 100644
--- a/packages/core/src/browser/messaging/ws-connection-provider.ts
+++ b/packages/core/src/browser/messaging/ws-connection-provider.ts
@@ -14,13 +14,16 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
-import { injectable, interfaces } from 'inversify';
+import { injectable, interfaces, decorate, unmanaged } from 'inversify';
import { createWebSocketConnection, Logger, ConsoleLogger } from 'vscode-ws-jsonrpc/lib';
import { ConnectionHandler, JsonRpcProxyFactory, JsonRpcProxy, Emitter, Event } from '../../common';
import { WebSocketChannel } from '../../common/messaging/web-socket-channel';
import { Endpoint } from '../endpoint';
const ReconnectingWebSocket = require('reconnecting-websocket');
+decorate(injectable(), JsonRpcProxyFactory);
+decorate(unmanaged(), JsonRpcProxyFactory, 0);
+
export interface WebSocketOptions {
/**
* True by default.
@@ -31,8 +34,21 @@ export interface WebSocketOptions {
@injectable()
export class WebSocketConnectionProvider {
- static createProxy(container: interfaces.Container, path: string, target?: object): JsonRpcProxy {
- return container.get(WebSocketConnectionProvider).createProxy(path, target);
+ /**
+ * Create a proxy object to remote interface of T type
+ * over a web socket connection for the given path and proxy factory.
+ */
+ static createProxy(container: interfaces.Container, path: string, factory: JsonRpcProxyFactory): JsonRpcProxy;
+ /**
+ * Create a proxy object to remote interface of T type
+ * over a web socket connection for the given path.
+ *
+ * An optional target can be provided to handle
+ * notifications and requests from a remote side.
+ */
+ static createProxy(container: interfaces.Container, path: string, target?: object): JsonRpcProxy;
+ static createProxy(container: interfaces.Container, path: string, arg?: object): JsonRpcProxy {
+ return container.get(WebSocketConnectionProvider).createProxy(path, arg);
}
protected channelIdSeq = 0;
@@ -64,6 +80,11 @@ export class WebSocketConnectionProvider {
this.socket = socket;
}
+ /**
+ * Create a proxy object to remote interface of T type
+ * over a web socket connection for the given path and proxy factory.
+ */
+ createProxy(path: string, factory: JsonRpcProxyFactory): JsonRpcProxy;
/**
* Create a proxy object to remote interface of T type
* over a web socket connection for the given path.
@@ -71,8 +92,9 @@ export class WebSocketConnectionProvider {
* An optional target can be provided to handle
* notifications and requests from a remote side.
*/
- createProxy(path: string, target?: object): JsonRpcProxy {
- const factory = new JsonRpcProxyFactory(target);
+ createProxy(path: string, target?: object): JsonRpcProxy;
+ createProxy(path: string, arg?: object): JsonRpcProxy {
+ const factory = arg instanceof JsonRpcProxyFactory ? arg : new JsonRpcProxyFactory(arg);
this.listen({
path,
onConnection: c => factory.listen(c)
diff --git a/packages/core/src/browser/opener-service.ts b/packages/core/src/browser/opener-service.ts
index d3ef9325f365f..5739b83870093 100644
--- a/packages/core/src/browser/opener-service.ts
+++ b/packages/core/src/browser/opener-service.ts
@@ -95,7 +95,7 @@ export class DefaultOpenerService implements OpenerService {
if (handlers.length >= 1) {
return handlers[0];
}
- return Promise.reject(`There is no opener for ${uri}.`);
+ return Promise.reject(new Error(`There is no opener for ${uri}.`));
}
async getOpeners(uri?: URI, options?: OpenerOptions): Promise {
diff --git a/packages/core/src/browser/preferences/preference-contribution.ts b/packages/core/src/browser/preferences/preference-contribution.ts
index 8bfa705519c00..2f6471f208d7e 100644
--- a/packages/core/src/browser/preferences/preference-contribution.ts
+++ b/packages/core/src/browser/preferences/preference-contribution.ts
@@ -20,6 +20,13 @@ import { ContributionProvider, bindContributionProvider, escapeRegExpCharacters,
import { PreferenceScope } from './preference-scope';
import { PreferenceProvider, PreferenceProviderPriority, PreferenceProviderDataChange } from './preference-provider';
+import {
+ PreferenceSchema, PreferenceSchemaProperties, PreferenceDataSchema, PreferenceItem, PreferenceSchemaProperty, PreferenceDataProperty, JsonType
+} from '../../common/preferences/preference-schema';
+import { FrontendApplicationConfigProvider } from '../frontend-application-config-provider';
+import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
+export { PreferenceSchema, PreferenceSchemaProperties, PreferenceDataSchema, PreferenceItem, PreferenceSchemaProperty, PreferenceDataProperty, JsonType };
+
// tslint:disable:no-any
// tslint:disable:forin
@@ -28,73 +35,6 @@ export interface PreferenceContribution {
readonly schema: PreferenceSchema;
}
-export interface PreferenceSchema {
- [name: string]: any,
- scope?: 'application' | 'window' | 'resource' | PreferenceScope,
- overridable?: boolean;
- properties: PreferenceSchemaProperties
-}
-export namespace PreferenceSchema {
- export function getDefaultScope(schema: PreferenceSchema): PreferenceScope {
- let defaultScope: PreferenceScope = PreferenceScope.Workspace;
- if (!PreferenceScope.is(schema.scope)) {
- defaultScope = PreferenceScope.fromString(schema.scope) || PreferenceScope.Workspace;
- } else {
- defaultScope = schema.scope;
- }
- return defaultScope;
- }
-}
-
-export interface PreferenceSchemaProperties {
- [name: string]: PreferenceSchemaProperty
-}
-
-export interface PreferenceDataSchema {
- [name: string]: any,
- scope?: PreferenceScope,
- properties: {
- [name: string]: PreferenceDataProperty
- }
- patternProperties: {
- [name: string]: PreferenceDataProperty
- };
-}
-
-export interface PreferenceItem {
- type?: JsonType | JsonType[];
- minimum?: number;
- default?: any;
- enum?: string[];
- items?: PreferenceItem;
- properties?: { [name: string]: PreferenceItem };
- additionalProperties?: object;
- [name: string]: any;
- overridable?: boolean;
-}
-
-export interface PreferenceSchemaProperty extends PreferenceItem {
- description?: string;
- scope?: 'application' | 'window' | 'resource' | PreferenceScope;
-}
-
-export interface PreferenceDataProperty extends PreferenceItem {
- description?: string;
- scope?: PreferenceScope;
-}
-export namespace PreferenceDataProperty {
- export function fromPreferenceSchemaProperty(schemaProps: PreferenceSchemaProperty, defaultScope: PreferenceScope = PreferenceScope.Workspace): PreferenceDataProperty {
- if (!schemaProps.scope) {
- schemaProps.scope = defaultScope;
- } else if (typeof schemaProps.scope === 'string') {
- return Object.assign(schemaProps, { scope: PreferenceScope.fromString(schemaProps.scope) || defaultScope });
- }
- return schemaProps;
- }
-}
-
-export type JsonType = 'string' | 'array' | 'number' | 'integer' | 'object' | 'boolean' | 'null';
-
export function bindPreferenceSchemaProvider(bind: interfaces.Bind): void {
bind(PreferenceSchemaProvider).toSelf().inSingletonScope();
bindContributionProvider(bind, PreferenceContribution);
@@ -110,6 +50,17 @@ export const OVERRIDE_PROPERTY_PATTERN = new RegExp(OVERRIDE_PROPERTY);
const OVERRIDE_PATTERN_WITH_SUBSTITUTION = '\\[(${0})\\]$';
+export interface FrontendApplicationPreferenceConfig extends FrontendApplicationConfig {
+ preferences: {
+ [preferenceName: string]: any
+ }
+}
+export namespace FrontendApplicationPreferenceConfig {
+ export function is(config: FrontendApplicationConfig): config is FrontendApplicationPreferenceConfig {
+ return 'preferences' in config && typeof config['preferences'] === 'object';
+ }
+}
+
@injectable()
export class PreferenceSchemaProvider extends PreferenceProvider {
@@ -196,16 +147,39 @@ export class PreferenceSchemaProvider extends PreferenceProvider {
if (schemaProps.overridable) {
this.overridePatternProperties.properties[preferenceName] = schemaProps;
}
- const newValue = schemaProps.default = this.getDefaultValue(schemaProps);
this.combinedSchema.properties[preferenceName] = schemaProps;
- this.preferences[preferenceName] = newValue;
- changes.push({ preferenceName, newValue, scope, domain });
+
+ const value = schemaProps.default = this.getDefaultValue(schemaProps, preferenceName);
+ if (this.testOverrideValue(preferenceName, value)) {
+ for (const overridenPreferenceName in value) {
+ const overrideValue = value[overridenPreferenceName];
+ const overridePreferenceName = `${preferenceName}.${overridenPreferenceName}`;
+ changes.push(this.doSetPreferenceValue(overridePreferenceName, overrideValue, { scope, domain }));
+ }
+ } else {
+ changes.push(this.doSetPreferenceValue(preferenceName, value, { scope, domain }));
+ }
}
}
return changes;
}
+ protected doSetPreferenceValue(preferenceName: string, newValue: any, { scope, domain }: {
+ scope: PreferenceScope,
+ domain: string[]
+ }): PreferenceProviderDataChange {
+ const oldValue = this.preferences[preferenceName];
+ this.preferences[preferenceName] = newValue;
+ return { preferenceName, oldValue, newValue, scope, domain };
+ }
- protected getDefaultValue(property: PreferenceItem): any {
+ /** @deprecated since 0.6.0 pass preferenceName as the second arg */
+ protected getDefaultValue(property: PreferenceItem): any;
+ protected getDefaultValue(property: PreferenceItem, preferenceName: string): any;
+ protected getDefaultValue(property: PreferenceItem, preferenceName?: string): any {
+ const config = FrontendApplicationConfigProvider.get();
+ if (preferenceName && FrontendApplicationPreferenceConfig.is(config) && preferenceName in config.preferences) {
+ return config.preferences[preferenceName];
+ }
if (property.default) {
return property.default;
}
@@ -305,7 +279,7 @@ export class PreferenceSchemaProvider extends PreferenceProvider {
return { preferenceName, overrideIdentifier };
}
- testOverrideValue(name: string, value: any): boolean {
- return typeof value === 'object' && OVERRIDE_PROPERTY_PATTERN.test(name);
+ testOverrideValue(name: string, value: any): value is PreferenceSchemaProperties {
+ return PreferenceSchemaProperties.is(value) && OVERRIDE_PROPERTY_PATTERN.test(name);
}
}
diff --git a/packages/core/src/browser/preferences/preference-scope.ts b/packages/core/src/browser/preferences/preference-scope.ts
index e05e001b69ca9..4625439147eb5 100644
--- a/packages/core/src/browser/preferences/preference-scope.ts
+++ b/packages/core/src/browser/preferences/preference-scope.ts
@@ -14,52 +14,5 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
-// tslint:disable:no-any
-
-export enum PreferenceScope {
- Default,
- User,
- Workspace,
- Folder
-}
-
-export namespace PreferenceScope {
- export function is(scope: any): scope is PreferenceScope {
- return typeof scope === 'number' && getScopes().findIndex(s => s === scope) >= 0;
- }
-
- export function getScopes(): PreferenceScope[] {
- return Object.keys(PreferenceScope)
- .filter(k => typeof PreferenceScope[k as any] === 'string')
- .map(v => Number(v));
- }
-
- export function getReversedScopes(): PreferenceScope[] {
- return getScopes().reverse();
- }
-
- export function getScopeNames(scope?: PreferenceScope): string[] {
- const names: string[] = [];
- const allNames = Object.keys(PreferenceScope)
- .filter(k => typeof PreferenceScope[k as any] === 'number');
- if (scope) {
- for (const name of allNames) {
- if ((PreferenceScope)[name] <= scope) {
- names.push(name);
- }
- }
- }
- return names;
- }
-
- export function fromString(strScope: string): PreferenceScope | undefined {
- switch (strScope) {
- case 'application':
- return PreferenceScope.User;
- case 'window':
- return PreferenceScope.Workspace;
- case 'resource':
- return PreferenceScope.Folder;
- }
- }
-}
+import { PreferenceScope } from '../../common/preferences/preference-scope';
+export { PreferenceScope };
diff --git a/packages/core/src/browser/quick-open/index.ts b/packages/core/src/browser/quick-open/index.ts
index 8652830a95c3e..cf5bd3fa0e1b7 100644
--- a/packages/core/src/browser/quick-open/index.ts
+++ b/packages/core/src/browser/quick-open/index.ts
@@ -15,6 +15,7 @@
********************************************************************************/
export * from './quick-open-model';
+export * from './quick-open-action-provider';
export * from './quick-open-service';
export * from './quick-pick-service';
export * from './quick-input-service';
diff --git a/packages/core/src/browser/quick-open/quick-open-action-provider.ts b/packages/core/src/browser/quick-open/quick-open-action-provider.ts
new file mode 100644
index 0000000000000..d5deda316dccf
--- /dev/null
+++ b/packages/core/src/browser/quick-open/quick-open-action-provider.ts
@@ -0,0 +1,100 @@
+/********************************************************************************
+ * Copyright (C) 2019 Red Hat, Inc. and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { Disposable } from '../../common/disposable';
+import { injectable } from 'inversify';
+import { QuickOpenItem } from './quick-open-model';
+
+export interface QuickOpenActionProvider {
+ hasActions(item: QuickOpenItem): boolean;
+ getActions(item: QuickOpenItem): Promise;
+}
+
+export interface QuickOpenActionOptions {
+ id: string;
+ label?: string;
+ tooltip?: string;
+ class?: string | undefined;
+ enabled?: boolean;
+ checked?: boolean;
+ radio?: boolean;
+}
+
+export interface QuickOpenAction extends QuickOpenActionOptions, Disposable {
+ run(item?: QuickOpenItem): PromiseLike;
+}
+
+@injectable()
+export abstract class QuickOpenBaseAction implements QuickOpenAction {
+ constructor(protected options: QuickOpenActionOptions) {
+ }
+
+ get id(): string {
+ return this.options.id;
+ }
+
+ get label(): string {
+ return this.options.label || '';
+ }
+
+ set label(value: string) {
+ this.options.label = value;
+ }
+
+ get tooltip(): string {
+ return this.options.tooltip || '';
+ }
+
+ set tooltip(value: string) {
+ this.options.tooltip = value;
+ }
+
+ get class(): string | undefined {
+ return this.options.class || '';
+ }
+
+ set class(value: string | undefined) {
+ this.options.class = value;
+ }
+
+ get enabled(): boolean {
+ return this.options.enabled || true;
+ }
+
+ set enabled(value: boolean) {
+ this.options.enabled = value;
+ }
+
+ get checked(): boolean {
+ return this.options.checked || false;
+ }
+
+ set checked(value: boolean) {
+ this.options.checked = value;
+ }
+
+ get radio(): boolean {
+ return this.options.radio || false;
+ }
+
+ set radio(value: boolean) {
+ this.options.radio = value;
+ }
+
+ abstract run(item?: QuickOpenItem): PromiseLike;
+
+ dispose(): void { }
+}
diff --git a/packages/core/src/browser/quick-open/quick-open-model.ts b/packages/core/src/browser/quick-open/quick-open-model.ts
index 970fb80b981b5..7b66e1046d456 100644
--- a/packages/core/src/browser/quick-open/quick-open-model.ts
+++ b/packages/core/src/browser/quick-open/quick-open-model.ts
@@ -16,6 +16,7 @@
import URI from '../../common/uri';
import { Keybinding } from '../keybinding';
+import { QuickOpenActionProvider } from './quick-open-action-provider';
export interface Highlight {
start: number
@@ -105,5 +106,5 @@ export class QuickOpenGroupItem void): void;
+ onType(lookFor: string, acceptor: (items: QuickOpenItem[], actionProvider?: QuickOpenActionProvider) => void): void;
}
diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts
index a372eb34661ee..8a946539214b6 100644
--- a/packages/core/src/browser/shell/application-shell.ts
+++ b/packages/core/src/browser/shell/application-shell.ts
@@ -1095,7 +1095,7 @@ export class ApplicationShell extends Widget {
alignment: StatusBarAlignment.RIGHT,
tooltip: 'Toggle Bottom Panel',
command: 'core.toggle.bottom.panel',
- priority: 0
+ priority: -1000
};
this.statusBar.setElement(BOTTOM_PANEL_TOGGLE_ID, element);
}
@@ -1444,19 +1444,19 @@ export namespace ApplicationShell {
bottomPanel: Object.freeze({
emptySize: 140,
expandThreshold: 160,
- expandDuration: 150,
+ expandDuration: 0,
initialSizeRatio: 0.382
}),
leftPanel: Object.freeze({
emptySize: 140,
expandThreshold: 140,
- expandDuration: 150,
+ expandDuration: 0,
initialSizeRatio: 0.191
}),
rightPanel: Object.freeze({
emptySize: 140,
expandThreshold: 140,
- expandDuration: 150,
+ expandDuration: 0,
initialSizeRatio: 0.191
})
});
diff --git a/packages/core/src/browser/shell/side-panel-handler.ts b/packages/core/src/browser/shell/side-panel-handler.ts
index 29bd26ad3530a..255edf49f755b 100644
--- a/packages/core/src/browser/shell/side-panel-handler.ts
+++ b/packages/core/src/browser/shell/side-panel-handler.ts
@@ -24,6 +24,8 @@ import { TabBarRendererFactory, TabBarRenderer, SHELL_TABBAR_CONTEXT_MENU, SideT
import { SplitPositionHandler, SplitPositionOptions } from './split-panels';
import { FrontendApplicationStateService } from '../frontend-application-state';
import { TheiaDockPanel } from './theia-dock-panel';
+import { SidePanelToolbar } from './side-panel-toolbar';
+import { TabBarToolbarRegistry, TabBarToolbarFactory, TabBarToolbar } from './tab-bar-toolbar';
/** The class name added to the left and right area panels. */
export const LEFT_RIGHT_AREA_CLASS = 'theia-app-sides';
@@ -56,6 +58,10 @@ export class SidePanelHandler {
* tab bar itself remains visible as long as there is at least one widget.
*/
tabBar: SideTabBar;
+ /**
+ * A tool bar, which displays a title and widget specific command buttons.
+ */
+ toolBar: SidePanelToolbar;
/**
* The widget container is a dock panel in `single-document` mode, which means that the panel
* cannot be split.
@@ -85,6 +91,8 @@ export class SidePanelHandler {
*/
protected options: SidePanel.Options;
+ @inject(TabBarToolbarRegistry) protected tabBarToolBarRegistry: TabBarToolbarRegistry;
+ @inject(TabBarToolbarFactory) protected tabBarToolBarFactory: () => TabBarToolbar;
@inject(TabBarRendererFactory) protected tabBarRendererFactory: () => TabBarRenderer;
@inject(SplitPositionHandler) protected splitPositionHandler: SplitPositionHandler;
@inject(FrontendApplicationStateService) protected readonly applicationStateService: FrontendApplicationStateService;
@@ -96,6 +104,7 @@ export class SidePanelHandler {
this.side = side;
this.options = options;
this.tabBar = this.createSideBar();
+ this.toolBar = this.createToolbar();
this.dockPanel = this.createSidePanel();
this.container = this.createContainer();
@@ -143,6 +152,7 @@ export class SidePanelHandler {
mode: 'single-document'
});
sidePanel.id = 'theia-' + this.side + '-side-panel';
+ sidePanel.addClass('theia-side-panel');
sidePanel.widgetActivated.connect((sender, widget) => {
this.tabBar.currentTitle = widget.title;
@@ -152,7 +162,19 @@ export class SidePanelHandler {
return sidePanel;
}
+ protected createToolbar(): SidePanelToolbar {
+ const toolbar = new SidePanelToolbar(this.tabBarToolBarRegistry, this.tabBarToolBarFactory, this.side);
+ return toolbar;
+ }
+
protected createContainer(): Panel {
+ const contentBox = new BoxLayout({ direction: 'top-to-bottom', spacing: 0 });
+ BoxPanel.setStretch(this.toolBar, 0);
+ contentBox.addWidget(this.toolBar);
+ BoxPanel.setStretch(this.dockPanel, 1);
+ contentBox.addWidget(this.dockPanel);
+ const contentPanel = new BoxPanel({layout: contentBox});
+
const side = this.side;
let direction: BoxLayout.Direction;
switch (side) {
@@ -165,12 +187,12 @@ export class SidePanelHandler {
default:
throw new Error('Illegal argument: ' + side);
}
- const boxLayout = new BoxLayout({ direction, spacing: 0 });
+ const containerLayout = new BoxLayout({ direction, spacing: 0 });
BoxPanel.setStretch(this.tabBar, 0);
- boxLayout.addWidget(this.tabBar);
- BoxPanel.setStretch(this.dockPanel, 1);
- boxLayout.addWidget(this.dockPanel);
- const boxPanel = new BoxPanel({ layout: boxLayout });
+ containerLayout.addWidget(this.tabBar);
+ BoxPanel.setStretch(contentPanel, 1);
+ containerLayout.addWidget(contentPanel);
+ const boxPanel = new BoxPanel({ layout: containerLayout });
boxPanel.id = 'theia-' + side + '-content-panel';
return boxPanel;
}
@@ -327,6 +349,8 @@ export class SidePanelHandler {
const hideDockPanel = currentTitle === null;
let relativeSizes: number[] | undefined;
+ this.toolBar.toolbarTitle = currentTitle || undefined;
+
if (hideDockPanel) {
container.addClass(COLLAPSED_CLASS);
if (this.state.expansion === SidePanel.ExpansionState.expanded && !this.state.empty) {
diff --git a/packages/core/src/browser/shell/side-panel-toolbar.ts b/packages/core/src/browser/shell/side-panel-toolbar.ts
new file mode 100644
index 0000000000000..4c0ddcd1974aa
--- /dev/null
+++ b/packages/core/src/browser/shell/side-panel-toolbar.ts
@@ -0,0 +1,89 @@
+/********************************************************************************
+ * Copyright (C) 2019 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { Widget, Title } from '@phosphor/widgets';
+import { TabBarToolbar, TabBarToolbarRegistry } from './tab-bar-toolbar';
+import { Message } from '@phosphor/messaging';
+
+export class SidePanelToolbar extends Widget {
+
+ protected titleContainer: HTMLElement | undefined;
+ private _toolbarTitle: Title | undefined;
+ protected toolbar: TabBarToolbar | undefined;
+
+ constructor(
+ protected readonly tabBarToolbarRegistry: TabBarToolbarRegistry,
+ protected readonly tabBarToolbarFactory: () => TabBarToolbar,
+ protected readonly side: 'left' | 'right') {
+ super();
+ this.init();
+ this.tabBarToolbarRegistry.onDidChange(() => this.update());
+ }
+
+ protected onAfterAttach(msg: Message): void {
+ if (this.toolbar) {
+ if (this.toolbar.isAttached) {
+ Widget.detach(this.toolbar);
+ }
+ Widget.attach(this.toolbar, this.node);
+ }
+ super.onAfterAttach(msg);
+ }
+
+ protected onBeforeDetach(msg: Message): void {
+ if (this.titleContainer) {
+ this.node.removeChild(this.titleContainer);
+ }
+ if (this.toolbar && this.toolbar.isAttached) {
+ Widget.detach(this.toolbar);
+ }
+ super.onBeforeDetach(msg);
+ }
+
+ protected onUpdateRequest(msg: Message): void {
+ super.onUpdateRequest(msg);
+ this.updateToolbar();
+ }
+
+ protected updateToolbar(): void {
+ if (!this.toolbar) {
+ return;
+ }
+ const current = this._toolbarTitle;
+ const widget = current && current.owner || undefined;
+ const items = widget ? this.tabBarToolbarRegistry.visibleItems(widget) : [];
+ this.toolbar.updateItems(items, widget);
+ }
+
+ protected init(): void {
+ this.titleContainer = document.createElement('div');
+ this.titleContainer.classList.add('theia-sidepanel-title');
+ this.titleContainer.classList.add('noWrapInfo');
+ this.node.appendChild(this.titleContainer);
+ this.node.classList.add('theia-sidepanel-toolbar');
+ this.node.classList.add(`theia-${this.side}-side-panel`);
+ this.toolbar = this.tabBarToolbarFactory();
+ this.update();
+ }
+
+ set toolbarTitle(title: Title | undefined) {
+ if (this.titleContainer && title) {
+ this._toolbarTitle = title;
+ this.titleContainer.innerHTML = this._toolbarTitle.label;
+ this.update();
+ }
+ }
+}
diff --git a/packages/core/src/browser/shell/split-panels.ts b/packages/core/src/browser/shell/split-panels.ts
index 55ad655d6e73d..bce4c6b3c55f9 100644
--- a/packages/core/src/browser/shell/split-panels.ts
+++ b/packages/core/src/browser/shell/split-panels.ts
@@ -52,11 +52,11 @@ export class SplitPositionHandler {
*/
setSidePanelSize(sidePanel: Widget, targetSize: number, options: SplitPositionOptions): Promise {
if (targetSize < 0) {
- return Promise.reject('Cannot resize to negative value.');
+ return Promise.reject(new Error('Cannot resize to negative value.'));
}
const parent = sidePanel.parent;
if (!(parent instanceof SplitPanel)) {
- return Promise.reject('Widget must be contained in a SplitPanel.');
+ return Promise.reject(new Error('Widget must be contained in a SplitPanel.'));
}
let index = parent.widgets.indexOf(sidePanel);
if (index > 0 && (options.side === 'right' || options.side === 'bottom')) {
diff --git a/packages/core/src/browser/shell/tab-bar-toolbar.tsx b/packages/core/src/browser/shell/tab-bar-toolbar.tsx
index 2c411ec389621..46f3136cf3b53 100644
--- a/packages/core/src/browser/shell/tab-bar-toolbar.tsx
+++ b/packages/core/src/browser/shell/tab-bar-toolbar.tsx
@@ -23,6 +23,9 @@ import { FrontendApplicationContribution } from '../frontend-application';
import { CommandRegistry, CommandService } from '../../common/command';
import { Disposable } from '../../common/disposable';
import { ContextKeyService } from '../context-key-service';
+import { Event, Emitter } from '../../common/event';
+
+import debounce = require('lodash.debounce');
/**
* Factory for instantiating tab-bar toolbars.
@@ -80,15 +83,21 @@ export class TabBarToolbar extends ReactWidget {
}
}
const command = this.commands.getCommand(item.command);
- const iconClass = command && command.iconClass;
- if (iconClass) {
- classNames.push(iconClass);
+ if (command) {
+ const iconClass = command.iconClass;
+ if (iconClass) {
+ classNames.push(iconClass);
+ }
}
- return
+ return
;
}
+ protected commandIsEnabled(command: string): boolean {
+ return this.commands.isEnabled(command, this.current);
+ }
+
protected executeCommand = (e: React.MouseEvent
) => {
const item = this.items.get(e.currentTarget.id);
if (item) {
@@ -174,6 +183,8 @@ export interface TabBarToolbarItem {
*/
readonly when?: string;
+ readonly onDidChange?: Event;
+
}
export namespace TabBarToolbarItem {
@@ -227,6 +238,11 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution {
@named(TabBarToolbarContribution)
protected readonly contributionProvider: ContributionProvider;
+ protected readonly onDidChangeEmitter = new Emitter();
+ readonly onDidChange: Event = this.onDidChangeEmitter.event;
+ // debounce in order to avoid to fire more than once in the same tick
+ protected fireOnDidChange = debounce(() => this.onDidChangeEmitter.fire(undefined), 0);
+
onStart(): void {
const contributions = this.contributionProvider.getContributions();
for (const contribution of contributions) {
@@ -245,6 +261,10 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution {
throw new Error(`A toolbar item is already registered with the '${id}' ID.`);
}
this.items.set(id, item);
+ this.fireOnDidChange();
+ if (item.onDidChange) {
+ item.onDidChange(() => this.fireOnDidChange());
+ }
}
/**
diff --git a/packages/core/src/browser/shell/tab-bars.ts b/packages/core/src/browser/shell/tab-bars.ts
index 63bb8eed1cedb..93b99351bd8ee 100644
--- a/packages/core/src/browser/shell/tab-bars.ts
+++ b/packages/core/src/browser/shell/tab-bars.ts
@@ -305,6 +305,7 @@ export class ToolbarAwareTabBar extends ScrollableTabBar {
super(options);
this.rewireDOM();
+ this.tabBarToolbarRegistry.onDidChange(() => this.update());
}
/**
@@ -336,9 +337,6 @@ export class ToolbarAwareTabBar extends ScrollableTabBar {
}
protected onBeforeDetach(msg: Message): void {
- if (this.contentContainer) {
- this.node.removeChild(this.contentContainer);
- }
if (this.toolbar && this.toolbar.isAttached) {
Widget.detach(this.toolbar);
}
@@ -363,7 +361,7 @@ export class ToolbarAwareTabBar extends ScrollableTabBar {
/**
* Restructures the DOM defined in PhosphorJS.
*
- * By default the tabs (`li`) are contained in the `this.contentNode` (`lu`) which is wrapped in a `div` (`this.node`).
+ * By default the tabs (`li`) are contained in the `this.contentNode` (`ul`) which is wrapped in a `div` (`this.node`).
* Instead of this structure, we add a container for the `this.contentNode` and for the toolbar.
* The scrollbar will only work for the `ul` part but it does not affect the toolbar, so it can be on the right hand-side.
*/
diff --git a/packages/core/src/browser/style/alert-messages.css b/packages/core/src/browser/style/alert-messages.css
index ba096e45689f8..2cd54b50dbbcb 100644
--- a/packages/core/src/browser/style/alert-messages.css
+++ b/packages/core/src/browser/style/alert-messages.css
@@ -20,6 +20,10 @@
padding: 10px;
}
+.theia-side-panel .theia-alert-message-container {
+ padding-left: 20px;
+}
+
.theia-alert-message-container i {
padding-right: 3px;
}
diff --git a/packages/core/src/browser/style/dockpanel.css b/packages/core/src/browser/style/dockpanel.css
index c3969c661845c..8f6a8636d87f3 100644
--- a/packages/core/src/browser/style/dockpanel.css
+++ b/packages/core/src/browser/style/dockpanel.css
@@ -23,7 +23,6 @@
}
.p-DockPanel-widget {
- background: var(--theia-layout-color0);
min-width: 100px;
min-height: 100px;
}
diff --git a/packages/core/src/browser/style/icons.css b/packages/core/src/browser/style/icons.css
new file mode 100644
index 0000000000000..0da9414f01116
--- /dev/null
+++ b/packages/core/src/browser/style/icons.css
@@ -0,0 +1,33 @@
+/********************************************************************************
+ * Copyright (C) 2019 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+.theia-open-change-icon {
+ width: 16px;
+ height: 16px;
+ background: var(--theia-icon-open-change) no-repeat;
+}
+
+.theia-open-file-icon {
+ width: 16px;
+ height: 16px;
+ background: var(--theia-icon-open-file) no-repeat;
+}
+
+.theia-open-preview-icon {
+ width: 16px;
+ height: 16px;
+ background: var(--theia-icon-preview) no-repeat;
+}
diff --git a/packages/core/src/browser/style/index.css b/packages/core/src/browser/style/index.css
index b6c0e9c932bef..9993e7c08bf84 100644
--- a/packages/core/src/browser/style/index.css
+++ b/packages/core/src/browser/style/index.css
@@ -59,7 +59,7 @@ body {
}
.theia-icon {
- width: 18px;
+ width: 32px;
height: 18px;
margin: 5px;
margin-left: 8px;
@@ -184,3 +184,4 @@ textarea {
@import './view-container.css';
@import './notification.css';
@import './alert-messages.css';
+@import './icons.css';
\ No newline at end of file
diff --git a/packages/core/src/browser/style/menus.css b/packages/core/src/browser/style/menus.css
index 176037c5578c5..9b2edcd9230ba 100644
--- a/packages/core/src/browser/style/menus.css
+++ b/packages/core/src/browser/style/menus.css
@@ -20,7 +20,7 @@
:root {
- --theia-private-menubar-height: 28px;
+ --theia-private-menubar-height: 32px;
--theia-private-menu-item-height: 24px;
}
diff --git a/packages/core/src/browser/style/notification.css b/packages/core/src/browser/style/notification.css
index cd77cf95a5204..3c89a0f3aced4 100644
--- a/packages/core/src/browser/style/notification.css
+++ b/packages/core/src/browser/style/notification.css
@@ -14,6 +14,10 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
+:root {
+ --theia-notification-count-height: 15.5px;
+}
+
.notification-count-container {
align-self: center;
background-color: var(--theia-ui-font-color3);
@@ -22,7 +26,7 @@
display: flex;
font-size: calc(var(--theia-ui-font-size0) * 0.8);
font-weight: 500;
- height: calc(var(--theia-private-horizontal-tab-height) * 0.7);
+ height: var(--theia-notification-count-height);
justify-content: center;
min-width: 6px;
padding: 0 5px;
diff --git a/packages/core/src/browser/style/scrollbars.css b/packages/core/src/browser/style/scrollbars.css
index c256eb4ce264c..b518b17896f4f 100644
--- a/packages/core/src/browser/style/scrollbars.css
+++ b/packages/core/src/browser/style/scrollbars.css
@@ -68,7 +68,7 @@
#theia-app-shell .ps__rail-x:focus > .ps__thumb-x,
#theia-app-shell .ps__rail-x.ps--clicking .ps__thumb-x,
#theia-dialog-shell .ps__rail-x:hover > .ps__thumb-x,
-#ttheia-dialog-shell .ps__rail-x:focus > .ps__thumb-x,
+#theia-dialog-shell .ps__rail-x:focus > .ps__thumb-x,
#theia-dialog-shell .ps__rail-x.ps--clicking .ps__thumb-x {
height: var(--theia-scrollbar-width);
}
diff --git a/packages/core/src/browser/style/sidepanel.css b/packages/core/src/browser/style/sidepanel.css
index 32ee05c42dd82..0407aa37f1f35 100644
--- a/packages/core/src/browser/style/sidepanel.css
+++ b/packages/core/src/browser/style/sidepanel.css
@@ -19,9 +19,11 @@
|----------------------------------------------------------------------------*/
:root {
- --theia-private-sidebar-tab-width: 32px;
+ --theia-private-sidebar-tab-width: 50px;
+ --theia-private-sidebar-tab-height: 32px;
--theia-private-sidebar-scrollbar-rail-width: 7px;
--theia-private-sidebar-scrollbar-width: 5px;
+ --theia-private-sidebar-icon-size: 28px;
}
@@ -31,10 +33,10 @@
.p-TabBar.theia-app-sides {
display: block;
- color: var(--theia-ui-font-color1);
+ color: var(--theia-tab-font-color);
background: var(--theia-layout-color2);
font-size: var(--theia-ui-font-size1);
- min-width: var(--theia-private-sidebar-tab-width);
+ min-width: var(--theia-private-sidebar-tab-width);
max-width: var(--theia-private-sidebar-tab-width);
}
@@ -44,22 +46,32 @@
.p-TabBar.theia-app-sides .p-TabBar-tab {
position: relative;
- padding: 12px 8px;
+ padding: 11px 10px;
background: var(--theia-layout-color2);
flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ min-height: var(--theia-private-sidebar-tab-height);
+ cursor: pointer;
}
.p-TabBar.theia-app-left .p-TabBar-tab {
border-left: var(--theia-panel-border-width) solid var(--theia-layout-color2);
+ margin-right: var(--theia-panel-border-width);
}
.p-TabBar.theia-app-right .p-TabBar-tab {
border-right: var(--theia-panel-border-width) solid var(--theia-layout-color2);
+ margin-left: var(--theia-panel-border-width);
}
.p-TabBar.theia-app-sides .p-TabBar-tab.p-mod-current {
- color: var(--theia-ui-font-color0);
- background: var(--theia-layout-color0);
+ color: var(--theia-tab-font-color);
+ opacity: 1.0;
+ background: none;
+ min-height: var(--theia-private-sidebar-tab-height);
+ height: var(--theia-private-sidebar-tab-height);
+ border-top: none;
}
.p-TabBar.theia-app-left .p-TabBar-tab.p-mod-current.theia-mod-active {
@@ -72,25 +84,45 @@
border-top-color: transparent;
}
-.p-TabBar.theia-app-sides .p-TabBar-tab:hover {
- background: var(--theia-accent-color3);
-}
-
-.p-TabBar.theia-app-sides .p-TabBar-tabIcon,
+.p-TabBar.theia-app-sides .p-TabBar-tabLabel,
.p-TabBar.theia-app-sides .p-TabBar-tabCloseIcon {
display: none;
}
-.p-TabBar.theia-app-sides .p-TabBar-tabLabel {
- position: absolute;
- min-height: var(--theia-private-sidebar-tab-width);
- max-height: var(--theia-private-sidebar-tab-width);
- align-items: flex-start;
+.p-TabBar.theia-app-sides .p-TabBar-tabIcon {
+ width: var(--theia-private-sidebar-icon-size);
+ height: var(--theia-private-sidebar-icon-size);
+ background-color: var(--theia-tab-icon-color);
+ opacity: 0.6;
+ mask-repeat: no-repeat;
+ -webkit-mask-repeat: no-repeat;
+ mask-size: var(--theia-private-sidebar-icon-size);
+ -webkit-mask-size: var(--theia-private-sidebar-icon-size);
}
-.p-TabBar.theia-app-sides .p-TabBar-tabIcon {
- transform: scale(1.5);
- max-height: 15px;
+.p-TabBar.theia-app-sides .file-icon.p-TabBar-tabIcon,
+.p-TabBar.theia-app-sides .file-icon.p-TabBar-tabIcon:hover,
+.p-TabBar.theia-app-sides .p-mod-current .file-icon.p-TabBar-tabIcon,
+.p-TabBar.theia-app-sides .fa.p-TabBar-tabIcon:hover,
+.p-TabBar.theia-app-sides .p-mod-current .fa.p-TabBar-tabIcon {
+ opacity: 1.0;
+ background: none;
+}
+
+.p-TabBar.theia-app-sides .p-TabBar-tabIcon:hover,
+.p-TabBar.theia-app-sides .p-mod-current .p-TabBar-tabIcon {
+ opacity: 1.0;
+}
+
+.p-TabBar.theia-app-sides .fa.p-TabBar-tabIcon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 23px;
+ opacity: 0.6;
+ text-align: center;
+ background: none;
+ color: var(--theia-tab-icon-color);
}
.p-TabBar.theia-app-left .p-TabBar-tabLabel {
@@ -108,12 +140,8 @@
max-width: var(--theia-private-sidebar-tab-width);
}
-#theia-left-side-panel {
- border-right: var(--theia-panel-border-width) solid var(--theia-border-color1);
-}
-
-#theia-right-side-panel {
- border-left: var(--theia-panel-border-width) solid var(--theia-border-color1);
+.theia-side-panel {
+ background-color: var(--theia-layout-color1);
}
@@ -173,3 +201,33 @@
.p-TabBar.theia-app-right > .theia-TabBar-hidden-content .p-TabBar-tabLabel {
transform: none;
}
+
+/*-----------------------------------------------------------------------------
+| Sidepanel Toolbar
+|----------------------------------------------------------------------------*/
+
+.theia-sidepanel-toolbar {
+ min-height: calc(var(--theia-private-horizontal-tab-height) + var(--theia-private-horizontal-tab-scrollbar-rail-height) / 2);
+ display: flex;
+ padding-left: 5px;
+ align-items: center;
+ background-color: var(--theia-layout-color1);
+}
+
+.theia-sidepanel-toolbar .theia-sidepanel-title {
+ color: var(--theia-ui-font-color1);
+ flex: 1;
+ margin-left: 14px;
+ text-transform: uppercase;
+ font-size: var(--theia-ui-font-size0);
+}
+
+.theia-sidepanel-toolbar .p-TabBar-toolbar .item {
+ color: var(--theia-ui-font-color1);
+}
+
+.theia-sidepanel-toolbar .p-TabBar-toolbar .item > div{
+ height: 18px;
+ width: 18px;
+ background-repeat: no-repeat;
+}
diff --git a/packages/core/src/browser/style/tabs.css b/packages/core/src/browser/style/tabs.css
index 54d1953de9a0c..b7c4785f88549 100644
--- a/packages/core/src/browser/style/tabs.css
+++ b/packages/core/src/browser/style/tabs.css
@@ -4,8 +4,7 @@
:root {
/* These need to be root because tabs get attached to the body during dragging. */
- --theia-private-horizontal-tab-height: 22px;
- --theia-private-horizontal-tab-active-top-border: 2px;
+ --theia-private-horizontal-tab-height: 28.5px;
--theia-private-horizontal-tab-scrollbar-rail-height: 7px;
--theia-private-horizontal-tab-scrollbar-height: 5px;
}
@@ -18,12 +17,30 @@
color: var(--theia-ui-font-color1);
background: var(--theia-layout-color1);
font-size: var(--theia-ui-font-size1);
+ cursor: pointer;
+}
+
+.p-TabBar[data-orientation='horizontal'].theia-app-bottom {
+ background: var(--theia-layout-color0);
+}
+
+.p-TabBar[data-orientation='horizontal'].theia-app-bottom .p-TabBar-tab {
+ background: var(--theia-layout-color0);
+}
+
+.p-TabBar[data-orientation='horizontal'].theia-app-bottom .p-TabBar-tab.p-mod-current {
+ background: var(--theia-layout-color0);
+ border-top: var(--theia-border-width) solid var(--theia-ui-font-color2);
+}
+
+.p-TabBar[data-orientation='horizontal'].theia-app-bottom .p-TabBar-tab.p-mod-current.theia-mod-active {
+ border-top-color: var(--theia-accent-color2);
}
.p-TabBar[data-orientation='horizontal'] {
overflow-x: hidden;
overflow-y: hidden;
- min-height: calc(var(--theia-private-horizontal-tab-height) + var(--theia-border-width) + var(--theia-private-horizontal-tab-scrollbar-rail-height) / 2);
+ min-height: calc(var(--theia-private-horizontal-tab-height) + var(--theia-private-horizontal-tab-scrollbar-rail-height) / 2);
}
.p-TabBar[data-orientation='horizontal'] .p-TabBar-content {
@@ -38,7 +55,8 @@
min-width: 35px;
line-height: var(--theia-private-horizontal-tab-height);
padding: 0px 8px;
- background: var(--theia-layout-color3);
+ background: var(--theia-layout-color2);
+ align-items: center;
}
.p-TabBar[data-orientation='horizontal'] .p-TabBar-tab:last-child {
@@ -90,13 +108,41 @@
}
.p-TabBar .p-TabBar-tabIcon {
- width: 15px;
- line-height: 1.7;
+ width: 15px;
+ line-height: 1.7;
+ font-size: 12px;
+ text-align: center;
+ background-repeat: no-repeat;
+}
+
+.p-TabBar.theia-app-centers .p-TabBar-tabIcon {
+ height: 15px;
+ background-size: 13px;
+ background-position-y: 3px;
+ background-color: var(--theia-tab-icon-color);
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-size: 13px;
+ -webkit-mask-position-y: 1px;
+ mask-repeat: no-repeat;
+ mask-size: 13px;
+ mask-position: 0 1px;
+ padding-right: 2px;
+}
+
+.p-TabBar[data-orientation='horizontal'] .file-icon.p-TabBar-tabIcon {
+ background: none;
+ padding-bottom: 0px;
+ padding-right: 0;
+ min-height: 20px;
}
+.p-TabBar[data-orientation='horizontal'] .fa.p-TabBar-tabIcon {
+ background: none;
+ padding-bottom: 6px;
+ min-height: 0;
+}
.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable > .p-TabBar-tabCloseIcon {
- padding-top: 6px;
padding-left: 10px;
height: 16px;
width: 16px;
@@ -139,18 +185,18 @@
}
.p-TabBar[data-orientation='horizontal'] > .p-TabBar-content-container > .ps__rail-x > .ps__thumb-x {
- height: var(--theia-private-horizontal-tab-scrollbar-height);
+ height: var(--theia-private-horizontal-tab-scrollbar-height) !important;
bottom: calc((var(--theia-private-horizontal-tab-scrollbar-rail-height) - var(--theia-private-horizontal-tab-scrollbar-height)) / 2);
}
.p-TabBar[data-orientation='horizontal'] > .p-TabBar-content-container > .ps__rail-x:hover,
.p-TabBar[data-orientation='horizontal'] > .p-TabBar-content-container > .ps__rail-x:focus {
- height: var(--theia-private-horizontal-tab-scrollbar-rail-height);
+ height: var(--theia-private-horizontal-tab-scrollbar-rail-height) !important;
}
.p-TabBar[data-orientation='horizontal'] > .p-TabBar-content-container > .ps__rail-x:hover > .ps__thumb-x,
.p-TabBar[data-orientation='horizontal'] > .p-TabBar-content-container > .ps__rail-x:focus > .ps__thumb-x {
- height: var(--theia-private-horizontal-tab-scrollbar-height);
+ height: calc(var(--theia-private-horizontal-tab-scrollbar-height) / 2) !important;
bottom: calc((var(--theia-private-horizontal-tab-scrollbar-rail-height) - var(--theia-private-horizontal-tab-scrollbar-height)) / 2);
}
@@ -207,8 +253,17 @@
display: flex;
align-items: center;
margin-left: 8px; /* `padding` + `margin-right` from the container toolbar */
+ opacity: 0.25;
+ cursor: default;
}
-.p-TabBar-toolbar .item:hover {
- cursor: pointer;
+.p-TabBar-toolbar .item.enabled {
+ opacity: 1.0;
+ cursor: pointer;
+}
+
+.p-TabBar-toolbar .item > div{
+ height: 18px;
+ width: 18px;
+ background-repeat: no-repeat;
}
diff --git a/packages/core/src/browser/style/tree.css b/packages/core/src/browser/style/tree.css
index e095bb0fb9489..e11e57f1a50a1 100644
--- a/packages/core/src/browser/style/tree.css
+++ b/packages/core/src/browser/style/tree.css
@@ -17,7 +17,6 @@
.theia-Tree {
overflow: hidden;
color: var(--theia-ui-font-color1);
- background: var(--theia-layout-color0);
font-size: var(--theia-ui-font-size1);
max-height: calc(100% - var(--theia-border-width));
position: relative;
@@ -35,7 +34,7 @@
}
.theia-TreeNode {
- line-height: var(--theia-private-horizontal-tab-height);
+ line-height: 22px;
display: flex;
}
@@ -52,7 +51,9 @@
.theia-ExpansionToggle {
padding-right: var(--theia-ui-padding);
- min-width: 8px;
+ min-width: 10px;
+ display: flex;
+ justify-content: center;
}
.theia-ExpansionToggle:hover {
@@ -68,7 +69,8 @@
.theia-ExpansionToggle:not(.theia-mod-collapsed)::before {
font-family: FontAwesome;
font-size: calc(var(--theia-content-font-size) * 0.8);
- content: "\f0d7";
+ content: "\f0da";
+ transform: rotate(45deg);
}
.theia-Tree:focus .theia-TreeNode.theia-mod-selected,
diff --git a/packages/core/src/browser/style/variables-bright.useable.css b/packages/core/src/browser/style/variables-bright.useable.css
index 8e32bdccaed9d..2f1fbd1dfef26 100644
--- a/packages/core/src/browser/style/variables-bright.useable.css
+++ b/packages/core/src/browser/style/variables-bright.useable.css
@@ -78,14 +78,18 @@ is not optimized for dense, information rich UIs.
--theia-terminal-font-family: monospace;
--theia-ui-padding: 6px;
+ /* Tab Icon Colors */
+ --theia-tab-icon-color: var(--theia-ui-font-color1);
+ --theia-tab-font-color: #000;
+
/* Main layout colors (bright to dark)
------------------------------------ */
--theia-layout-color0: #ffffff;
- --theia-layout-color1: var(--md-grey-100);
- --theia-layout-color2: var(--md-grey-200);
- --theia-layout-color3: var(--md-grey-300);
- --theia-layout-color4: var(--md-grey-400);
+ --theia-layout-color1: #f3f3f3;
+ --theia-layout-color2: #ececec;
+ --theia-layout-color3: #dcdcdc;
+ --theia-layout-color4: #dcdcdc;
/* Brand colors */
@@ -181,9 +185,11 @@ is not optimized for dense, information rich UIs.
--theia-icon-replace: url(../icons/replace.svg);
--theia-icon-replace-all: url(../icons/replace-all.svg);
--theia-icon-open-file: url(../icons/open-file-bright.svg);
+ --theia-icon-open-change: url(../icons/open-change-bright.svg);
+ --theia-icon-preview: url(../icons/preview-bright.svg);
/* Scrollbars */
- --theia-scrollbar-width: 6px;
+ --theia-scrollbar-width: 10px;
--theia-scrollbar-rail-width: 10px;
--theia-scrollbar-thumb-color: hsla(0,0%,61%,.4);
--theia-scrollbar-rail-color: transparent;
@@ -191,7 +197,7 @@ is not optimized for dense, information rich UIs.
--theia-scrollbar-active-rail-color: transparent;
/* Menu */
- --theia-menu-color0: var(--theia-layout-color2);
+ --theia-menu-color0: var(--theia-layout-color3);
--theia-menu-color1: var(--theia-layout-color0);
--theia-menu-color2: var(--theia-layout-color3);
diff --git a/packages/core/src/browser/style/variables-dark.useable.css b/packages/core/src/browser/style/variables-dark.useable.css
index 63d9ffb8c46ec..7aa29eeae26bd 100644
--- a/packages/core/src/browser/style/variables-dark.useable.css
+++ b/packages/core/src/browser/style/variables-dark.useable.css
@@ -78,14 +78,18 @@ is not optimized for dense, information rich UIs.
--theia-terminal-font-family: monospace;
--theia-ui-padding: 6px;
+ /* Tab Colors */
+ --theia-tab-icon-color: rgb(255, 255, 255);
+ --theia-tab-font-color: #FFF;
+
/* Main layout colors (dark to bright)
------------------------------------ */
- --theia-layout-color0: #1e1e1e;
- --theia-layout-color1: #262626;
- --theia-layout-color2: #2e2e2e;
- --theia-layout-color3: #303030;
- --theia-layout-color4: #333333;
+ --theia-layout-color0: #1d1d1d;
+ --theia-layout-color1: #252526;
+ --theia-layout-color2: #333333;
+ --theia-layout-color3: #383838;
+ --theia-layout-color4: #383838;
/* Brand colors */
@@ -181,9 +185,11 @@ is not optimized for dense, information rich UIs.
--theia-icon-replace: url(../icons/replace-inverse.svg);
--theia-icon-replace-all: url(../icons/replace-all-inverse.svg);
--theia-icon-open-file: url(../icons/open-file-dark.svg);
+ --theia-icon-open-change: url(../icons/open-change-dark.svg);
+ --theia-icon-preview: url(../icons/preview-dark.svg);
/* Scrollbars */
- --theia-scrollbar-width: 6px;
+ --theia-scrollbar-width: 10px;
--theia-scrollbar-rail-width: 10px;
--theia-scrollbar-thumb-color: hsla(0,0%,39%,.4);
--theia-scrollbar-rail-color: transparent;
@@ -191,7 +197,7 @@ is not optimized for dense, information rich UIs.
--theia-scrollbar-active-rail-color: transparent;
/* Menu */
- --theia-menu-color0: var(--theia-layout-color2);
+ --theia-menu-color0: var(--theia-layout-color4);
--theia-menu-color1: var(--theia-layout-color4);
--theia-menu-color2: var(--theia-layout-color1);
diff --git a/packages/core/src/browser/tree/tree-decorator.ts b/packages/core/src/browser/tree/tree-decorator.ts
index 41f780a96c381..c1adc16a8bcc7 100644
--- a/packages/core/src/browser/tree/tree-decorator.ts
+++ b/packages/core/src/browser/tree/tree-decorator.ts
@@ -233,16 +233,53 @@ export namespace TreeDecoration {
}
+ export interface BaseTailDecoration {
+
+ /**
+ * Optional tooltip for the tail decoration.
+ */
+ readonly tooltip?: string;
+ }
+
/**
* Unlike caption suffixes, tail decorations appears right-aligned after the caption and the caption suffixes (is any).
*/
- export interface TailDecoration extends CaptionAffix {
+ export interface TailDecoration extends BaseTailDecoration {
+ /**
+ * The text content of the tail decoration.
+ */
+ readonly data: string;
/**
- * Optional tooltip for the tail decoration.
+ * Font data for customizing the content.
*/
- readonly tooltip?: string;
+ readonly fontData?: FontData;
+ }
+
+ export interface TailDecorationIcon extends BaseTailDecoration {
+ /**
+ * This should be the name of the Font Awesome icon with out the `fa fa-` prefix, just the name, for instance `paw`.
+ * For the existing icons, see here: https://fontawesome.com/v4.7.0/icons/.
+ */
+ readonly icon: string;
+
+ /**
+ * The color of the icon.
+ */
+ readonly color?: Color;
+ }
+ export interface TailDecorationIconClass extends BaseTailDecoration {
+ /**
+ * This should be the entire Font Awesome class array, for instance ['fa', 'fa-paw']
+ * For the existing icons, see here: https://fontawesome.com/v4.7.0/icons/.
+ */
+ readonly iconClass: string[];
+
+ /**
+ * The color of the icon.
+ */
+ readonly color?: Color;
}
/**
@@ -307,19 +344,13 @@ export namespace TreeDecoration {
/**
* Has not effect if the tree node being decorated has no associated icon.
*/
- export interface IconOverlay {
+ export interface BaseOverlay {
/**
* The position where the decoration will be placed on the top of the original icon.
*/
readonly position: IconOverlayPosition;
- /**
- * This should be the name of the Font Awesome icon with out the `fa fa-` prefix, just the name, for instance `paw`.
- * For the existing icons, see here: https://fontawesome.com/v4.7.0/icons/.
- */
- readonly icon: string;
-
/**
* The color of the overlaying icon. If not specified, then the default icon color will be used.
*/
@@ -332,6 +363,22 @@ export namespace TreeDecoration {
}
+ export interface IconOverlay extends BaseOverlay {
+ /**
+ * This should be the name of the Font Awesome icon with out the `fa fa-` prefix, just the name, for instance `paw`.
+ * For the existing icons, see here: https://fontawesome.com/v4.7.0/icons/.
+ */
+ readonly icon: string;
+ }
+
+ export interface IconClassOverlay extends BaseOverlay {
+ /**
+ * This should be the entire Font Awesome class array, for instance ['fa', 'fa-paw']
+ * For the existing icons, see here: https://fontawesome.com/v4.7.0/icons/.
+ */
+ readonly iconClass: string[];
+ }
+
/**
* The caption highlighting with the highlighted ranges and an optional background color.
*/
@@ -468,7 +515,7 @@ export namespace TreeDecoration {
/**
* Optional right-aligned decorations that appear after the node caption and after the caption suffixes (is any).
*/
- readonly tailDecorations?: TailDecoration[];
+ readonly tailDecorations?: Array;
/**
* Custom tooltip for the decorated item. Tooltip will be appended to the original tooltip, if any.
@@ -483,7 +530,7 @@ export namespace TreeDecoration {
/**
* Has not effect if given, but the tree node does not have an associated image.
*/
- readonly iconOverlay?: IconOverlay;
+ readonly iconOverlay?: IconOverlay | IconClassOverlay;
/**
* An array of ranges to highlight the caption.
diff --git a/packages/core/src/browser/tree/tree-expansion.spec.ts b/packages/core/src/browser/tree/tree-expansion.spec.ts
new file mode 100644
index 0000000000000..103e1c6939a33
--- /dev/null
+++ b/packages/core/src/browser/tree/tree-expansion.spec.ts
@@ -0,0 +1,158 @@
+/********************************************************************************
+ * Copyright (C) 2019 Thomas Drosdzoll.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { expect } from 'chai';
+import { MockTreeModel } from './test/mock-tree-model';
+import { TreeModelImpl, TreeModel } from './tree-model';
+import { TreeImpl, Tree, TreeNode, CompositeTreeNode } from './tree';
+import { Container } from 'inversify';
+import { TreeSelectionServiceImpl } from './tree-selection-impl';
+import { TreeSelectionService } from './tree-selection';
+import { TreeExpansionServiceImpl, TreeExpansionService, ExpandableTreeNode } from './tree-expansion';
+import { TreeNavigationService } from './tree-navigation';
+import { TreeSearch } from './tree-search';
+import { FuzzySearch } from './fuzzy-search';
+import { MockLogger } from '../../common/test/mock-logger';
+import { ILogger } from '../../common';
+
+// tslint:disable:no-unused-expression
+describe('TreeExpansionService', () => {
+ let model: TreeModel;
+ beforeEach(() => {
+ model = createTreeModel();
+ model.root = MockTreeModel.HIERARCHICAL_MOCK_ROOT();
+ });
+ describe('expandNode', () => {
+ it('won\'t expand an already expanded node', done => {
+ const node: ExpandableTreeNode = retrieveNode('1');
+ model.expandNode(node).then(result => {
+ expect(result).to.be.false;
+ done();
+ });
+ });
+
+ it('will expand a collapsed node', done => {
+ const node: ExpandableTreeNode = retrieveNode('1');
+ model.collapseNode(node).then(() => {
+ model.expandNode(node).then(result => {
+ expect(result).to.be.true;
+ done();
+ });
+ });
+ });
+
+ it('won\'t expand an undefined node', done => {
+ model.expandNode(undefined).then(result => {
+ expect(result).to.be.false;
+ done();
+ });
+ });
+ });
+
+ describe('collapseNode', () => {
+ it('will collapse an expanded node', done => {
+ const node: ExpandableTreeNode = retrieveNode('1');
+ model.collapseNode(node).then(result => {
+ expect(result).to.be.true;
+ done();
+ });
+ });
+
+ it('won\'t collapse an already collapsed node', done => {
+ const node: ExpandableTreeNode = retrieveNode('1');
+ model.collapseNode(node).then(() => {
+ model.collapseNode(node).then(result => {
+ expect(result).to.be.false;
+ done();
+ });
+ });
+ });
+
+ it('cannot collapse a leaf node', done => {
+ const node: ExpandableTreeNode = retrieveNode('1.1.2');
+ model.collapseNode(node).then(result => {
+ expect(result).to.be.false;
+ done();
+ });
+ });
+ });
+
+ describe('collapseAll', () => {
+ it('will collapse all nodes recursively', done => {
+ model.collapseAll(retrieveNode('1')).then(result => {
+ expect(result).to.be.true;
+ done();
+ });
+ });
+
+ it('won\'t collapse nodes recursively if the root node is collapsed already', done => {
+ model.collapseNode(retrieveNode('1')).then(() => {
+ model.collapseAll(retrieveNode('1')).then(result => {
+ expect(result).to.be.true;
+ done();
+ });
+ });
+ });
+ });
+
+ describe('toggleNodeExpansion', () => {
+ it('changes the expansion state from expanded to collapsed', done => {
+ const node = retrieveNode('1');
+ model.onExpansionChanged((e: Readonly) => {
+ expect(e).to.be.equal(node);
+ expect(e.expanded).to.be.false;
+ });
+ model.toggleNodeExpansion(node).then(() => {
+ done();
+ });
+ });
+
+ it('changes the expansion state from collapsed to expanded', done => {
+ const node = retrieveNode('1');
+ model.collapseNode(node).then(() => {
+ });
+ model.onExpansionChanged((e: Readonly) => {
+ expect(e).to.be.equal(node);
+ expect(e.expanded).to.be.true;
+ });
+ model.toggleNodeExpansion(node).then(() => {
+ done();
+ });
+ });
+ });
+
+ function createTreeModel(): TreeModel {
+ const container = new Container({ defaultScope: 'Singleton' });
+ container.bind(TreeImpl).toSelf();
+ container.bind(Tree).toService(TreeImpl);
+ container.bind(TreeSelectionServiceImpl).toSelf();
+ container.bind(TreeSelectionService).toService(TreeSelectionServiceImpl);
+ container.bind(TreeExpansionServiceImpl).toSelf();
+ container.bind(TreeExpansionService).toService(TreeExpansionServiceImpl);
+ container.bind(TreeNavigationService).toSelf();
+ container.bind(TreeModelImpl).toSelf();
+ container.bind(TreeModel).toService(TreeModelImpl);
+ container.bind(TreeSearch).toSelf();
+ container.bind(FuzzySearch).toSelf();
+ container.bind(MockLogger).toSelf();
+ container.bind(ILogger).to(MockLogger).inSingletonScope();
+ return container.get(TreeModel);
+ }
+ function retrieveNode(id: string): Readonly {
+ const readonlyNode: Readonly = model.getNode(id) as T;
+ return readonlyNode;
+ }
+});
diff --git a/packages/core/src/browser/tree/tree-expansion.ts b/packages/core/src/browser/tree/tree-expansion.ts
index a275bbfe7ca08..01c8dcfb737be 100644
--- a/packages/core/src/browser/tree/tree-expansion.ts
+++ b/packages/core/src/browser/tree/tree-expansion.ts
@@ -65,15 +65,15 @@ export interface ExpandableTreeNode extends CompositeTreeNode {
}
export namespace ExpandableTreeNode {
- export function is(node: TreeNode | undefined): node is ExpandableTreeNode {
+ export function is(node: Object | undefined): node is ExpandableTreeNode {
return !!node && CompositeTreeNode.is(node) && 'expanded' in node;
}
- export function isExpanded(node: TreeNode | undefined): node is ExpandableTreeNode {
+ export function isExpanded(node: Object | undefined): node is ExpandableTreeNode {
return ExpandableTreeNode.is(node) && node.expanded;
}
- export function isCollapsed(node: TreeNode | undefined): node is ExpandableTreeNode {
+ export function isCollapsed(node: Object | undefined): node is ExpandableTreeNode {
return ExpandableTreeNode.is(node) && !node.expanded;
}
}
diff --git a/packages/core/src/browser/tree/tree-selection-state.ts b/packages/core/src/browser/tree/tree-selection-state.ts
index 8f56c1158f697..045cfe7f1337c 100644
--- a/packages/core/src/browser/tree/tree-selection-state.ts
+++ b/packages/core/src/browser/tree/tree-selection-state.ts
@@ -14,7 +14,7 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
-import { Tree } from './tree';
+import { Tree, TreeNode } from './tree';
import { DepthFirstTreeIterator } from './tree-iterator';
import { TreeSelection, SelectableTreeNode } from './tree-selection';
@@ -74,26 +74,29 @@ export class TreeSelectionState {
selection(): ReadonlyArray {
const copy = this.checkNoDefaultSelection(this.selectionStack);
- const nodes = new Set();
+ const nodeIds = new Set();
for (let i = 0; i < copy.length; i++) {
const { node, type } = copy[i];
if (TreeSelection.isRange(type)) {
const selection = copy[i];
- this.selectionRange(selection).forEach(n => nodes.add(n));
+ for (const id of this.selectionRange(selection).map(n => n.id)) {
+ nodeIds.add(id);
+ }
} else if (TreeSelection.isToggle(type)) {
- if (nodes.has(node)) {
- nodes.delete(node);
+ if (nodeIds.has(node.id)) {
+ nodeIds.delete(node.id);
} else {
- nodes.add(node);
+ nodeIds.add(node.id);
}
}
}
- return Array.from(nodes.keys()).reverse();
+ return Array.from(nodeIds.keys()).map(id => this.tree.getNode(id)).filter(SelectableTreeNode.is).reverse();
}
get focus(): SelectableTreeNode | undefined {
const copy = this.checkNoDefaultSelection(this.selectionStack);
- return copy[copy.length - 1].focus;
+ const candidate = copy[copy.length - 1].focus;
+ return this.toSelectableTreeNode(candidate);
}
protected handleDefault(state: TreeSelectionState, node: Readonly): TreeSelectionState {
@@ -139,7 +142,7 @@ export class TreeSelectionState {
// Drop the previous range when we are trying to modify that.
if (TreeSelection.isRange(copy[copy.length - 1])) {
const range = this.selectionRange(copy.pop()!);
- // And we drop all preceeding individual nodes that were contained in the range we are dropping.
+ // And we drop all preceding individual nodes that were contained in the range we are dropping.
// That means, anytime we cover individual nodes with a range, they will belong to the range so we need to drop them now.
for (let i = copy.length - 1; i >= 0; i--) {
if (range.indexOf(copy[i].node) !== -1) {
@@ -208,6 +211,22 @@ export class TreeSelectionState {
return range.filter(SelectableTreeNode.is);
}
+ protected toSelectableTreeNode(node: TreeNode | undefined): SelectableTreeNode | undefined {
+ if (!!node) {
+ const candidate = this.tree.getNode(node.id);
+ if (!!candidate) {
+ if (SelectableTreeNode.is(candidate)) {
+ return candidate;
+ } else {
+ console.warn(`Could not map to a selectable tree node. Node with ID: ${node.id} is not a selectable node.`);
+ }
+ } else {
+ console.warn(`Could not map to a selectable tree node. Node does not exist with ID: ${node.id}.`);
+ }
+ }
+ return undefined;
+ }
+
/**
* Checks whether the argument contains any `DEFAULT` tree selection type. If yes, throws an error, otherwise returns with a reference the argument.
*/
diff --git a/packages/core/src/browser/tree/tree-widget-selection.ts b/packages/core/src/browser/tree/tree-widget-selection.ts
new file mode 100644
index 0000000000000..0868c0f541495
--- /dev/null
+++ b/packages/core/src/browser/tree/tree-widget-selection.ts
@@ -0,0 +1,37 @@
+/********************************************************************************
+ * Copyright (C) 2019 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { TreeWidget } from './tree-widget';
+import { SelectableTreeNode } from './tree-selection';
+
+export type TreeWidgetSelection = ReadonlyArray> & {
+ source: TreeWidget
+};
+export namespace TreeWidgetSelection {
+ export function isSource(selection: Object | undefined, source: TreeWidget) {
+ return getSource(selection) === source;
+ }
+ export function getSource(selection: Object | undefined): TreeWidget | undefined {
+ return is(selection) ? selection.source : undefined;
+ }
+ export function is(selection: Object | undefined): selection is TreeWidgetSelection {
+ // tslint:disable-next-line:no-any
+ return Array.isArray(selection) && ('source' in selection) && selection['source'] instanceof TreeWidget;
+ }
+ export function create(source: TreeWidget): TreeWidgetSelection {
+ return Object.assign(source.model.selectedNodes, { source });
+ }
+}
diff --git a/packages/core/src/browser/tree/tree-widget.tsx b/packages/core/src/browser/tree/tree-widget.tsx
index 736d9f09a572e..600d1eb450551 100644
--- a/packages/core/src/browser/tree/tree-widget.tsx
+++ b/packages/core/src/browser/tree/tree-widget.tsx
@@ -16,7 +16,7 @@
import { injectable, inject, postConstruct } from 'inversify';
import { Message } from '@phosphor/messaging';
-import { Disposable, MenuPath } from '../../common';
+import { Disposable, MenuPath, SelectionService } from '../../common';
import { Key, KeyCode, KeyModifier } from '../keys';
import { ContextMenuRenderer } from '../context-menu-renderer';
import { StatefulWidget } from '../shell';
@@ -35,6 +35,7 @@ import { TopDownTreeIterator } from './tree-iterator';
import { SearchBox, SearchBoxFactory, SearchBoxProps } from './search-box';
import { TreeSearch } from './tree-search';
import { ElementExt } from '@phosphor/domutils';
+import { TreeWidgetSelection } from './tree-widget-selection';
const debounce = require('lodash.debounce');
@@ -83,6 +84,11 @@ export interface TreeProps {
* 'true' if the selected node should be auto scrolled only if the widget is active. Otherwise, `false`. Defaults to `false`.
*/
readonly scrollIfActive?: boolean
+
+ /**
+ * `true` if a tree widget contributes to the global selection. Defaults to `false`.
+ */
+ readonly globalSelection?: boolean;
}
export interface NodeProps {
@@ -126,6 +132,9 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
protected decorations: Map = new Map();
+ @inject(SelectionService)
+ protected readonly selectionService: SelectionService;
+
constructor(
@inject(TreeProps) readonly props: TreeProps,
@inject(TreeModel) readonly model: TreeModel,
@@ -133,7 +142,8 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
) {
super();
this.scrollOptions = {
- suppressScrollX: true
+ suppressScrollX: true,
+ minScrollbarLength: 35
};
this.addClass(TREE_CLASS);
this.node.tabIndex = 0;
@@ -178,6 +188,25 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
this.updateRows();
this.updateDecorations();
});
+ if (this.props.globalSelection) {
+ this.toDispose.pushAll([
+ this.model.onSelectionChanged(() => {
+ if (this.node.contains(document.activeElement)) {
+ this.updateGlobalSelection();
+ }
+ }),
+ Disposable.create(() => {
+ const selection = this.selectionService.selection;
+ if (TreeWidgetSelection.isSource(selection, this)) {
+ this.selectionService.selection = undefined;
+ }
+ })
+ ]);
+ }
+ }
+
+ protected updateGlobalSelection(): void {
+ this.selectionService.selection = TreeWidgetSelection.create(this);
}
protected rows = new Map();
@@ -255,6 +284,10 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
}
}
}
+ // it has to be called after nodes are selected
+ if (this.props.globalSelection) {
+ this.updateGlobalSelection();
+ }
this.forceUpdate();
}
@@ -513,15 +546,16 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
const overlayIcons: React.ReactNode[] = [];
new Map(this.getDecorationData(node, 'iconOverlay').reverse().filter(notEmpty)
- .map(overlay => [overlay.position, overlay] as [TreeDecoration.IconOverlayPosition, TreeDecoration.IconOverlay]))
+ .map(overlay => [overlay.position, overlay] as [TreeDecoration.IconOverlayPosition, TreeDecoration.IconOverlay | TreeDecoration.IconClassOverlay]))
.forEach((overlay, position) => {
- const overlayClass = (iconName: string) =>
- ['a', 'fa', `fa-${iconName}`, TreeDecoration.Styles.DECORATOR_SIZE_CLASS, TreeDecoration.IconOverlayPosition.getStyle(position)].join(' ');
+ const iconClasses = [TreeDecoration.Styles.DECORATOR_SIZE_CLASS, TreeDecoration.IconOverlayPosition.getStyle(position)];
const style = (color?: string) => color === undefined ? {} : { color };
if (overlay.background) {
- overlayIcons.push( );
+ overlayIcons.push(
+ );
}
- overlayIcons.push( );
+ const overlayIcon = (overlay as TreeDecoration.IconOverlay).icon || (overlay as TreeDecoration.IconClassOverlay).iconClass;
+ overlayIcons.push( );
});
if (overlayIcons.length > 0) {
@@ -532,18 +566,29 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
}
protected renderTailDecorations(node: TreeNode, props: NodeProps): React.ReactNode {
- const style = (fontData: TreeDecoration.FontData | undefined) => this.applyFontStyles({}, fontData);
return
{this.getDecorationData(node, 'tailDecorations').filter(notEmpty).reduce((acc, current) => acc.concat(current), []).map((decoration, index) => {
- const { fontData, data, tooltip } = decoration;
+ const { tooltip } = decoration;
+ const { data, fontData } = decoration as TreeDecoration.TailDecoration;
+ const color = (decoration as TreeDecoration.TailDecorationIcon).color;
+ const icon = (decoration as TreeDecoration.TailDecorationIcon).icon || (decoration as TreeDecoration.TailDecorationIconClass).iconClass;
const className = [TREE_NODE_SEGMENT_CLASS, TREE_NODE_TAIL_CLASS].join(' ');
- return
- {data}
+ const style = fontData ? this.applyFontStyles({}, fontData) : color ? { color } : undefined;
+ const content = data ? data : icon ?
: '';
+ return
+ {content}
;
})}
;
}
+ // Determine the classes to use for an icon
+ // Assumes a Font Awesome name when passed a single string, otherwise uses the passed string array
+ private getIconClass(iconName: string | string[], additionalClasses: string[] = []): string {
+ const iconClass = (typeof iconName === 'string') ? ['a', 'fa', `fa-${iconName}`] : ['a'].concat(iconName);
+ return iconClass.concat(additionalClasses).join(' ');
+ }
+
protected renderNode(node: TreeNode, props: NodeProps): React.ReactNode {
if (!TreeNode.isVisible(node)) {
return undefined;
diff --git a/packages/core/src/browser/tree/tree.spec.ts b/packages/core/src/browser/tree/tree.spec.ts
index 6254dd83ddb05..d8893092fe274 100644
--- a/packages/core/src/browser/tree/tree.spec.ts
+++ b/packages/core/src/browser/tree/tree.spec.ts
@@ -15,8 +15,21 @@
********************************************************************************/
import * as assert from 'assert';
-import { TreeNode, CompositeTreeNode } from './tree';
+import { TreeNode, CompositeTreeNode, TreeImpl, Tree } from './tree';
+import { TreeModel, TreeModelImpl } from './tree-model';
+import { MockTreeModel } from './test/mock-tree-model';
+import { expect } from 'chai';
+import { Container } from 'inversify';
+import { TreeSelectionServiceImpl } from './tree-selection-impl';
+import { TreeSelectionService } from './tree-selection';
+import { TreeExpansionServiceImpl, TreeExpansionService } from './tree-expansion';
+import { TreeNavigationService } from './tree-navigation';
+import { TreeSearch } from './tree-search';
+import { FuzzySearch } from './fuzzy-search';
+import { MockLogger } from '../../common/test/mock-logger';
+import { ILogger } from '../../common';
+// tslint:disable:no-unused-expression
describe('Tree', () => {
it('addChildren', () => {
@@ -93,7 +106,7 @@ describe('Tree', () => {
}`, node);
});
- it('removeChild - thrid', () => {
+ it('removeChild - third', () => {
const node = getNode();
CompositeTreeNode.removeChild(node, node.children[2]);
assertTreeNode(`{
@@ -116,6 +129,83 @@ describe('Tree', () => {
}`, node);
});
+ let model: TreeModel;
+ beforeEach(() => {
+ model = createTreeModel();
+ model.root = MockTreeModel.HIERARCHICAL_MOCK_ROOT();
+ });
+ describe('getNode', () => {
+ it('returns undefined for undefined nodes', done => {
+ expect(model.getNode(undefined)).to.be.undefined;
+ done();
+ });
+
+ it('returns undefined for a non-existing id', done => {
+ expect(model.getNode('10')).to.be.undefined;
+ done();
+ });
+
+ it('returns a valid node for existing an id', done => {
+ expect(model.getNode('1.1')).not.to.be.undefined;
+ done();
+ });
+ });
+
+ describe('validateNode', () => {
+ it('returns undefined for undefined nodes', done => {
+ expect(model.validateNode(undefined)).to.be.undefined;
+ done();
+ });
+
+ it('returns undefined for non-existing nodes', done => {
+ expect(model.validateNode(MockTreeModel.Node.toTreeNode({ 'id': '10' }))).to.be.undefined;
+ done();
+ });
+
+ it('returns a valid node for an existing node', done => {
+ expect(model.validateNode(retrieveNode
('1.1'))).not.to.be.undefined;
+ done();
+ });
+ });
+
+ describe('refresh', () => {
+ it('refreshes all composite nodes starting with the root', done => {
+ let result: Boolean = true;
+ const expectedRefreshedNodes = new Set([
+ retrieveNode('1'),
+ retrieveNode('1.1'),
+ retrieveNode('1.2')]);
+ model.onNodeRefreshed((e: Readonly) => {
+ result = result && expectedRefreshedNodes.has(e);
+ expectedRefreshedNodes.delete(e);
+ });
+ model.refresh().then(() => {
+ expect(result).to.be.true;
+ expect(expectedRefreshedNodes.size).to.be.equal(0);
+ done();
+ });
+ });
+ });
+
+ describe('refresh(parent: Readonly)', () => {
+ it('refreshes all composite nodes starting with the provided node', done => {
+ let result: Boolean = true;
+ const expectedRefreshedNodes = new Set([
+ retrieveNode('1.2'),
+ retrieveNode('1.2.1')
+ ]);
+ model.onNodeRefreshed((e: Readonly) => {
+ result = result && expectedRefreshedNodes.has(e);
+ expectedRefreshedNodes.delete(e);
+ });
+ model.refresh(retrieveNode('1.2')).then(() => {
+ expect(result).to.be.true;
+ expect(expectedRefreshedNodes.size).to.be.equal(0);
+ done();
+ });
+ });
+ });
+
function getNode(): CompositeTreeNode {
return CompositeTreeNode.addChildren({
id: 'parent',
@@ -147,4 +237,26 @@ describe('Tree', () => {
}, 2));
}
+ function createTreeModel(): TreeModel {
+ const container = new Container({ defaultScope: 'Singleton' });
+ container.bind(TreeImpl).toSelf();
+ container.bind(Tree).toService(TreeImpl);
+ container.bind(TreeSelectionServiceImpl).toSelf();
+ container.bind(TreeSelectionService).toService(TreeSelectionServiceImpl);
+ container.bind(TreeExpansionServiceImpl).toSelf();
+ container.bind(TreeExpansionService).toService(TreeExpansionServiceImpl);
+ container.bind(TreeNavigationService).toSelf();
+ container.bind(TreeModelImpl).toSelf();
+ container.bind(TreeModel).toService(TreeModelImpl);
+ container.bind(TreeSearch).toSelf();
+ container.bind(FuzzySearch).toSelf();
+ container.bind(MockLogger).toSelf();
+ container.bind(ILogger).to(MockLogger).inSingletonScope();
+ return container.get(TreeModel);
+ }
+ function retrieveNode(id: string): Readonly {
+ const readonlyNode: Readonly = model.getNode(id) as T;
+ return readonlyNode;
+ }
+
});
diff --git a/packages/core/src/browser/tree/tree.ts b/packages/core/src/browser/tree/tree.ts
index 66d265e55ef32..41d923832ef17 100644
--- a/packages/core/src/browser/tree/tree.ts
+++ b/packages/core/src/browser/tree/tree.ts
@@ -116,7 +116,7 @@ export interface CompositeTreeNode extends TreeNode {
}
export namespace CompositeTreeNode {
- export function is(node: TreeNode | undefined): node is CompositeTreeNode {
+ export function is(node: Object | undefined): node is CompositeTreeNode {
return !!node && 'children' in node;
}
diff --git a/packages/core/src/browser/widgets/react-widget.tsx b/packages/core/src/browser/widgets/react-widget.tsx
index 9d0268cc725a9..9c0dde62f0549 100644
--- a/packages/core/src/browser/widgets/react-widget.tsx
+++ b/packages/core/src/browser/widgets/react-widget.tsx
@@ -28,7 +28,8 @@ export abstract class ReactWidget extends BaseWidget {
constructor() {
super();
this.scrollOptions = {
- suppressScrollX: true
+ suppressScrollX: true,
+ minScrollbarLength: 35,
};
this.toDispose.push(Disposable.create(() => {
ReactDOM.unmountComponentAtNode(this.node);
diff --git a/packages/core/src/common/menu.ts b/packages/core/src/common/menu.ts
index b33907ab2fb7a..752cd0f058ea5 100644
--- a/packages/core/src/common/menu.ts
+++ b/packages/core/src/common/menu.ts
@@ -142,7 +142,7 @@ export class MenuModelRegistry {
return sub;
}
if (sub) {
- throw Error(`'${menuId}' is not a menu group.`);
+ throw new Error(`'${menuId}' is not a menu group.`);
}
const newSub = new CompositeMenuNode(menuId);
current.addNode(newSub);
diff --git a/packages/core/src/common/messaging/proxy-factory.spec.ts b/packages/core/src/common/messaging/proxy-factory.spec.ts
index 2b7f8cd05cd26..924b797da2bb9 100644
--- a/packages/core/src/common/messaging/proxy-factory.spec.ts
+++ b/packages/core/src/common/messaging/proxy-factory.spec.ts
@@ -42,7 +42,7 @@ class TestServer {
}
fails2(arg: string, otherArg: string): Promise {
- return Promise.reject('fails2 failed');
+ return Promise.reject(new Error('fails2 failed'));
}
}
diff --git a/packages/core/src/common/preferences/preference-schema.ts b/packages/core/src/common/preferences/preference-schema.ts
new file mode 100644
index 0000000000000..c3ad1e90a0edf
--- /dev/null
+++ b/packages/core/src/common/preferences/preference-schema.ts
@@ -0,0 +1,94 @@
+/********************************************************************************
+ * Copyright (C) 2019 Ericsson and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+// tslint:disable:no-any
+
+import { PreferenceScope } from './preference-scope';
+
+export interface PreferenceSchema {
+ [name: string]: any,
+ scope?: 'application' | 'window' | 'resource' | PreferenceScope,
+ overridable?: boolean;
+ properties: PreferenceSchemaProperties
+}
+export namespace PreferenceSchema {
+ export function is(obj: Object | undefined): obj is PreferenceSchema {
+ return !!obj && ('properties' in obj) && PreferenceSchemaProperties.is((obj)['properties']);
+ }
+ export function getDefaultScope(schema: PreferenceSchema): PreferenceScope {
+ let defaultScope: PreferenceScope = PreferenceScope.Workspace;
+ if (!PreferenceScope.is(schema.scope)) {
+ defaultScope = PreferenceScope.fromString(schema.scope) || PreferenceScope.Workspace;
+ } else {
+ defaultScope = schema.scope;
+ }
+ return defaultScope;
+ }
+}
+
+export interface PreferenceSchemaProperties {
+ [name: string]: PreferenceSchemaProperty
+}
+export namespace PreferenceSchemaProperties {
+ export function is(obj: Object | undefined): obj is PreferenceSchemaProperties {
+ return !!obj && typeof obj === 'object';
+ }
+}
+
+export interface PreferenceDataSchema {
+ [name: string]: any,
+ scope?: PreferenceScope,
+ properties: {
+ [name: string]: PreferenceDataProperty
+ }
+ patternProperties: {
+ [name: string]: PreferenceDataProperty
+ };
+}
+
+export interface PreferenceItem {
+ type?: JsonType | JsonType[];
+ minimum?: number;
+ default?: any;
+ enum?: string[];
+ items?: PreferenceItem;
+ properties?: { [name: string]: PreferenceItem };
+ additionalProperties?: object;
+ [name: string]: any;
+ overridable?: boolean;
+}
+
+export interface PreferenceSchemaProperty extends PreferenceItem {
+ description?: string;
+ scope?: 'application' | 'window' | 'resource' | PreferenceScope;
+}
+
+export interface PreferenceDataProperty extends PreferenceItem {
+ description?: string;
+ scope?: PreferenceScope;
+}
+export namespace PreferenceDataProperty {
+ export function fromPreferenceSchemaProperty(schemaProps: PreferenceSchemaProperty, defaultScope: PreferenceScope = PreferenceScope.Workspace): PreferenceDataProperty {
+ if (!schemaProps.scope) {
+ schemaProps.scope = defaultScope;
+ } else if (typeof schemaProps.scope === 'string') {
+ return Object.assign(schemaProps, { scope: PreferenceScope.fromString(schemaProps.scope) || defaultScope });
+ }
+ return schemaProps;
+ }
+}
+
+export type JsonType = 'string' | 'array' | 'number' | 'integer' | 'object' | 'boolean' | 'null';
diff --git a/packages/core/src/common/preferences/preference-scope.ts b/packages/core/src/common/preferences/preference-scope.ts
new file mode 100644
index 0000000000000..e05e001b69ca9
--- /dev/null
+++ b/packages/core/src/common/preferences/preference-scope.ts
@@ -0,0 +1,65 @@
+/********************************************************************************
+ * Copyright (C) 2019 Ericsson and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+// tslint:disable:no-any
+
+export enum PreferenceScope {
+ Default,
+ User,
+ Workspace,
+ Folder
+}
+
+export namespace PreferenceScope {
+ export function is(scope: any): scope is PreferenceScope {
+ return typeof scope === 'number' && getScopes().findIndex(s => s === scope) >= 0;
+ }
+
+ export function getScopes(): PreferenceScope[] {
+ return Object.keys(PreferenceScope)
+ .filter(k => typeof PreferenceScope[k as any] === 'string')
+ .map(v => Number(v));
+ }
+
+ export function getReversedScopes(): PreferenceScope[] {
+ return getScopes().reverse();
+ }
+
+ export function getScopeNames(scope?: PreferenceScope): string[] {
+ const names: string[] = [];
+ const allNames = Object.keys(PreferenceScope)
+ .filter(k => typeof PreferenceScope[k as any] === 'number');
+ if (scope) {
+ for (const name of allNames) {
+ if ((PreferenceScope)[name] <= scope) {
+ names.push(name);
+ }
+ }
+ }
+ return names;
+ }
+
+ export function fromString(strScope: string): PreferenceScope | undefined {
+ switch (strScope) {
+ case 'application':
+ return PreferenceScope.User;
+ case 'window':
+ return PreferenceScope.Workspace;
+ case 'resource':
+ return PreferenceScope.Folder;
+ }
+ }
+}
diff --git a/packages/core/src/common/resource.ts b/packages/core/src/common/resource.ts
index e2506cb27b431..e095886221923 100644
--- a/packages/core/src/common/resource.ts
+++ b/packages/core/src/common/resource.ts
@@ -112,7 +112,7 @@ export class DefaultResourceProvider {
// no-op
}
}
- return Promise.reject(`A resource provider for '${uri.toString()}' is not registered.`);
+ return Promise.reject(new Error(`A resource provider for '${uri.toString()}' is not registered.`));
}
}
diff --git a/packages/core/src/common/selection-command-handler.ts b/packages/core/src/common/selection-command-handler.ts
new file mode 100644
index 0000000000000..d8be975b16c0f
--- /dev/null
+++ b/packages/core/src/common/selection-command-handler.ts
@@ -0,0 +1,101 @@
+/********************************************************************************
+ * Copyright (C) 2019 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+// tslint:disable:no-any
+import { CommandHandler } from './command';
+import { SelectionService } from '../common/selection-service';
+
+export class SelectionCommandHandler implements CommandHandler {
+
+ constructor(
+ protected readonly selectionService: SelectionService,
+ protected readonly toSelection: (arg: any) => S | undefined,
+ protected readonly options: SelectionCommandHandler.Options
+ ) { }
+
+ execute(...args: any[]): Object | undefined {
+ const selection = this.getSelection(...args);
+ return selection ? (this.options.execute as any)(selection, ...args) : undefined;
+ }
+
+ isVisible(...args: any[]): boolean {
+ const selection = this.getSelection(...args);
+ return !!selection && (!this.options.isVisible || (this.options.isVisible as any)(selection as any, ...args));
+ }
+
+ isEnabled(...args: any[]): boolean {
+ const selection = this.getSelection(...args);
+ return !!selection && (!this.options.isEnabled || (this.options.isEnabled as any)(selection as any, ...args));
+ }
+
+ protected isMulti(): boolean {
+ return this.options && !!this.options.multi;
+ }
+
+ protected getSelection(...args: any[]): S | S[] | undefined {
+ const givenSelection = args.length && this.toSelection(args[0]);
+ if (givenSelection) {
+ return this.isMulti() ? [givenSelection] : givenSelection;
+ }
+ const globalSelection = this.getSingleSelection(this.selectionService.selection);
+ if (this.isMulti()) {
+ return this.getMulitSelection(globalSelection);
+ }
+ return this.getSingleSelection(globalSelection);
+ }
+
+ protected getSingleSelection(arg: Object | undefined): S | undefined {
+ let selection = this.toSelection(arg);
+ if (selection) {
+ return selection;
+ }
+ if (Array.isArray(arg)) {
+ for (const element of arg) {
+ selection = this.toSelection(element);
+ if (selection) {
+ return selection;
+ }
+ }
+ }
+ return undefined;
+ }
+
+ protected getMulitSelection(arg: Object | undefined): S[] | undefined {
+ let selection = this.toSelection(arg);
+ if (selection) {
+ return [selection];
+ }
+ const result = [];
+ if (Array.isArray(arg)) {
+ for (const element of arg) {
+ selection = this.toSelection(element);
+ if (selection) {
+ result.push(selection);
+ }
+ }
+ }
+ return result.length ? result : undefined;
+ }
+}
+export namespace SelectionCommandHandler {
+ export type Options = SelectionOptions | SelectionOptions;
+ export interface SelectionOptions {
+ multi: Multi;
+ execute(selection: T, ...args: any[]): any;
+ isEnabled?(selection: T, ...args: any[]): boolean;
+ isVisible?(selection: T, ...args: any[]): boolean;
+ }
+}
diff --git a/packages/core/src/node/backend-application.ts b/packages/core/src/node/backend-application.ts
index d24f46e32f9ce..ed7cdc8ff9dd3 100644
--- a/packages/core/src/node/backend-application.ts
+++ b/packages/core/src/node/backend-application.ts
@@ -24,6 +24,7 @@ import { ILogger, ContributionProvider, MaybePromise } from '../common';
import { CliContribution } from './cli';
import { Deferred } from '../common/promise-util';
import { environment } from '../common/index';
+import { AddressInfo } from 'net';
export const BackendApplicationContribution = Symbol('BackendApplicationContribution');
export interface BackendApplicationContribution {
@@ -185,7 +186,7 @@ export class BackendApplication {
server.listen(port, hostname, () => {
const scheme = this.cliParams.ssl ? 'https' : 'http';
- this.logger.info(`Theia app listening on ${scheme}://${hostname || 'localhost'}:${server.address().port}.`);
+ this.logger.info(`Theia app listening on ${scheme}://${hostname || 'localhost'}:${(server.address() as AddressInfo).port}.`);
deferred.resolve(server);
});
diff --git a/packages/core/src/node/logger-cli-contribution.ts b/packages/core/src/node/logger-cli-contribution.ts
index f891c5c414725..13369caddca90 100644
--- a/packages/core/src/node/logger-cli-contribution.ts
+++ b/packages/core/src/node/logger-cli-contribution.ts
@@ -19,7 +19,7 @@ import { injectable } from 'inversify';
import { LogLevel } from '../common/logger';
import { CliContribution } from './cli';
import * as fs from 'fs-extra';
-import * as nsfw from 'vscode-nsfw';
+import * as nsfw from 'nsfw';
import { Event, Emitter } from '../common/event';
import * as path from 'path';
diff --git a/packages/core/src/node/messaging/test/test-web-socket-channel.ts b/packages/core/src/node/messaging/test/test-web-socket-channel.ts
index 355d963d1abbd..823e7b551cddb 100644
--- a/packages/core/src/node/messaging/test/test-web-socket-channel.ts
+++ b/packages/core/src/node/messaging/test/test-web-socket-channel.ts
@@ -19,6 +19,7 @@ import * as http from 'http';
import * as https from 'https';
import { WebSocketChannel } from '../../../common/messaging/web-socket-channel';
import { Disposable } from '../../../common/disposable';
+import { AddressInfo } from 'net';
export class TestWebSocketChannel extends WebSocketChannel {
@@ -27,7 +28,7 @@ export class TestWebSocketChannel extends WebSocketChannel {
path: string
}) {
super(0, content => socket.send(content));
- const socket = new ws(`ws://localhost:${server.address().port}${WebSocketChannel.wsPath}`);
+ const socket = new ws(`ws://localhost:${(server.address() as AddressInfo).port}${WebSocketChannel.wsPath}`);
socket.on('error', error =>
this.fireError(error)
);
diff --git a/packages/core/src/typings/nsfw/index.d.ts b/packages/core/src/typings/nsfw/index.d.ts
index 62309125cab7b..de2f5475c0e23 100644
--- a/packages/core/src/typings/nsfw/index.d.ts
+++ b/packages/core/src/typings/nsfw/index.d.ts
@@ -14,7 +14,7 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
-declare module 'vscode-nsfw' {
+declare module 'nsfw' {
function nsfw(dir: string, eventHandler: (events: nsfw.ChangeEvent[]) => void, options?: nsfw.Options): Promise;
namespace nsfw {
diff --git a/packages/cpp/README.md b/packages/cpp/README.md
index bb55ff4cf54ef..bb9a30bbddeb1 100644
--- a/packages/cpp/README.md
+++ b/packages/cpp/README.md
@@ -79,6 +79,30 @@ To get this working, you need to enable clangd's global index using the
"cpp.clangdArgs": "--background-index"
}
+## Using the clang-tidy linter
+
+Note: This functionality is available when using clangd 9 and later.
+
+You can set the preference 'cpp.clangTidy' to enable the clang-tidy linter included in clangd. When the preference is set, there are two ways to chose which of its built-in checks clang-tidy will use:
+
+- using the preferences: 'cpp.clangTidyChecks'
+- using the file '.clang-tidy' . The file is located in the same folder of the files or a parent folder.
+
+Note: using the preference checks will supersede the value found in the .clang-tidy file.
+
+The syntax used to fill the checks can be found at http://clang.llvm.org/extra/clang-tidy/
+
+clang-tidy has its own checks and can also run Clang static analyzer checks. Each check has a name ([see link above for full list](http://clang.llvm.org/extra/clang-tidy/)). Clang-tidy takes as input the checks that should run, in the form of a comma-separated list of positive and negative (prefixed with -) globs. Positive globs add subsets of checks, negative globs remove them.
+
+There are two ways to configure clang-tidy's checks: through a Theia preference or using a .clang-tidy config file. Here are examples for both:"
+
+ - for the preferences: "cpp.clangTidyChecks": "*,-readability-*"
+ - Meaning: enables all list-checks and disable all readability-* checks
+
+ - for the .clang-tidy file: Checks: "-*,readability-*"
+ - Meaning: disable all list-checks and enable all readability-* checks
+
## License
+
- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/)
- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp)
diff --git a/packages/cpp/package.json b/packages/cpp/package.json
index 8ebeb650fd315..6ce6c0d6613bb 100644
--- a/packages/cpp/package.json
+++ b/packages/cpp/package.json
@@ -1,16 +1,16 @@
{
"name": "@theia/cpp",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Cpp Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/languages": "^0.4.0",
- "@theia/monaco": "^0.4.0",
- "@theia/preferences": "^0.4.0",
- "@theia/process": "^0.4.0",
- "@theia/task": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/languages": "^0.5.0",
+ "@theia/monaco": "^0.5.0",
+ "@theia/preferences": "^0.5.0",
+ "@theia/process": "^0.5.0",
+ "@theia/task": "^0.5.0",
"string-argv": "^0.1.1"
},
"publishConfig": {
@@ -44,11 +44,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/cpp/src/browser/cpp-language-client-contribution.ts b/packages/cpp/src/browser/cpp-language-client-contribution.ts
index 039f94b3c6331..5200adf995d33 100644
--- a/packages/cpp/src/browser/cpp-language-client-contribution.ts
+++ b/packages/cpp/src/browser/cpp-language-client-contribution.ts
@@ -69,6 +69,11 @@ export class CppLanguageClientContribution extends BaseLanguageClientContributio
@postConstruct()
protected init() {
this.cppBuildConfigurations.onActiveConfigChange(config => this.onActiveBuildConfigChanged(config));
+ this.cppPreferences.onPreferenceChanged(e => {
+ if (this.running) {
+ this.restart();
+ }
+ });
}
protected onReady(languageClient: ILanguageClient): void {
@@ -140,7 +145,9 @@ export class CppLanguageClientContribution extends BaseLanguageClientContributio
protected getStartParameters(): CppStartParameters {
return {
clangdExecutable: this.cppPreferences['cpp.clangdExecutable'],
- clangdArgs: this.cppPreferences['cpp.clangdArgs']
+ clangdArgs: this.cppPreferences['cpp.clangdArgs'],
+ clangTidy: this.cppPreferences['cpp.clangTidy'],
+ clangTidyChecks: this.cppPreferences['cpp.clangTidyChecks']
};
}
}
diff --git a/packages/cpp/src/browser/cpp-preferences.ts b/packages/cpp/src/browser/cpp-preferences.ts
index f91e5119e5579..731c6eb652a20 100644
--- a/packages/cpp/src/browser/cpp-preferences.ts
+++ b/packages/cpp/src/browser/cpp-preferences.ts
@@ -61,6 +61,16 @@ export const cppPreferencesSchema: PreferenceSchema = {
description: 'Specify the arguments to pass to clangd when starting the language server.',
default: '',
type: 'string'
+ },
+ 'cpp.clangTidy': {
+ description: 'Enable/disable C/C++ linting.',
+ default: false,
+ type: 'boolean'
+ },
+ 'cpp.clangTidyChecks': {
+ description: 'Specify comma separated arguments to pass to clang-tidy. Activated only if cpp.clang-tidy is enabled',
+ default: '',
+ type: 'string'
}
}
};
@@ -70,6 +80,8 @@ export class CppConfiguration {
'cpp.experimentalCommands': boolean;
'cpp.clangdExecutable': string;
'cpp.clangdArgs': string;
+ 'cpp.clangTidy': boolean;
+ 'cpp.clangTidyChecks': string;
}
export const CppPreferences = Symbol('CppPreferences');
diff --git a/packages/cpp/src/common/index.ts b/packages/cpp/src/common/index.ts
index 08d489e56a1bf..3c4d79c626a34 100644
--- a/packages/cpp/src/common/index.ts
+++ b/packages/cpp/src/common/index.ts
@@ -27,4 +27,6 @@ export const CLANGD_EXECUTABLE_DEFAULT = 'clangd';
export interface CppStartParameters {
clangdExecutable: string;
clangdArgs: string;
+ clangTidy?: boolean;
+ clangTidyChecks?: string;
}
diff --git a/packages/cpp/src/node/cpp-contribution.ts b/packages/cpp/src/node/cpp-contribution.ts
index 5bec335b74d09..aedef76bf05bf 100644
--- a/packages/cpp/src/node/cpp-contribution.ts
+++ b/packages/cpp/src/node/cpp-contribution.ts
@@ -1,5 +1,5 @@
/********************************************************************************
- * Copyright (C) 2017 Ericsson and others.
+ * Copyright (C) 2017-2019 Ericsson and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -42,7 +42,34 @@ export class CppContribution extends BaseLanguageServerContribution {
|| undefined
);
+ const clangTidy = parameters && parameters.clangTidy;
+ const clangTidyChecks = parameters && parameters.clangTidyChecks;
+
+ if (clangTidy) {
+ const supportsClangTidy = await this.testSupportsClangTidy(command);
+ if (supportsClangTidy) {
+ args.push('-clang-tidy');
+ if (typeof clangTidyChecks === 'string' && clangTidyChecks.length > 0) {
+ args.push(`-clang-tidy-checks=${clangTidyChecks}`);
+ }
+ }
+ }
const serverConnection = await this.createProcessStreamConnectionAsync(command, args);
this.forward(clientConnection, serverConnection);
}
+
+ protected async testSupportsClangTidy(command: string): Promise {
+ // clangd should fail if -clang-tidy flag is not supported
+ // but if it is supported, it will run forever until killed/stopped.
+ // to avoid that, we pass -version flag which makes it exit early.
+ const process = await super.spawnProcessAsync(command, ['-clang-tidy', '-version']);
+ return new Promise(resolve => {
+ process.errorOutput.on('data', (data: string) => {
+ if (data.includes('-clang-tidy')) {
+ resolve(false);
+ }
+ });
+ process.errorOutput.once('close', () => resolve(true));
+ });
+ }
}
diff --git a/packages/debug-nodejs/package.json b/packages/debug-nodejs/package.json
index 62eb50b1d3a6c..6c09dc04db38b 100644
--- a/packages/debug-nodejs/package.json
+++ b/packages/debug-nodejs/package.json
@@ -1,9 +1,9 @@
{
"name": "@theia/debug-nodejs",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - NodeJS Debug Extension",
"dependencies": {
- "@theia/debug": "^0.4.0",
+ "@theia/debug": "^0.5.0",
"ps-list": "5.0.1",
"vscode-debugprotocol": "^1.32.0"
},
@@ -40,11 +40,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/debug/package.json b/packages/debug/package.json
index ab85676001863..b7eee7d6085d5 100644
--- a/packages/debug/package.json
+++ b/packages/debug/package.json
@@ -1,20 +1,20 @@
{
"name": "@theia/debug",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Debug Extension",
"dependencies": {
- "@theia/console": "^0.4.0",
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/json": "^0.4.0",
- "@theia/markers": "^0.4.0",
- "@theia/monaco": "^0.4.0",
- "@theia/output": "^0.4.0",
- "@theia/process": "^0.4.0",
- "@theia/terminal": "^0.4.0",
- "@theia/variable-resolver": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/console": "^0.5.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/json": "^0.5.0",
+ "@theia/markers": "^0.5.0",
+ "@theia/monaco": "^0.5.0",
+ "@theia/output": "^0.5.0",
+ "@theia/process": "^0.5.0",
+ "@theia/terminal": "^0.5.0",
+ "@theia/variable-resolver": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"@types/p-debounce": "^1.0.0",
"jsonc-parser": "^2.0.2",
"mkdirp": "^0.5.0",
@@ -60,11 +60,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/debug/src/browser/console/debug-console-contribution.ts b/packages/debug/src/browser/console/debug-console-contribution.ts
index 32689a83913fb..458c53d0ff368 100644
--- a/packages/debug/src/browser/console/debug-console-contribution.ts
+++ b/packages/debug/src/browser/console/debug-console-contribution.ts
@@ -41,7 +41,8 @@ export class DebugConsoleContribution extends AbstractViewContribution {
this.manager.onDidCreateDebugSession(session => this.openSession(session, { reveal: false }));
this.manager.onDidStartDebugSession(session => {
- const { noDebug, openDebug, internalConsoleOptions } = session.configuration;
+ const { noDebug } = session.configuration;
+ const openDebug = session.configuration.openDebug || this.preference['debug.openDebug'];
+ const internalConsoleOptions = session.configuration.internalConsoleOptions || this.preference['debug.internalConsoleOptions'];
if (internalConsoleOptions === 'openOnSessionStart' ||
(internalConsoleOptions === 'openOnFirstSessionStart' && this.firstSessionStart)) {
this.console.openView({
@@ -878,7 +884,7 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
}
): Promise {
const { debugViewLocation, reveal } = {
- debugViewLocation: session.configuration.debugViewLocation,
+ debugViewLocation: session.configuration.debugViewLocation || this.preference['debug.debugViewLocation'],
reveal: true,
...options
};
diff --git a/packages/debug/src/browser/debug-preferences.ts b/packages/debug/src/browser/debug-preferences.ts
index 951774d6011fa..94e40814c5125 100644
--- a/packages/debug/src/browser/debug-preferences.ts
+++ b/packages/debug/src/browser/debug-preferences.ts
@@ -24,12 +24,30 @@ export const debugPreferencesSchema: PreferenceSchema = {
type: 'boolean',
default: false,
description: 'Enable/disable tracing communications with debug adapters'
+ },
+ 'debug.debugViewLocation': {
+ enum: ['default', 'left', 'right', 'bottom'],
+ default: 'default',
+ description: 'Controls the location of the debug view.'
+ },
+ 'debug.openDebug': {
+ enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart', 'openOnDebugBreak'],
+ default: 'openOnSessionStart',
+ description: 'Controls when the debug view should open.'
+ },
+ 'debug.internalConsoleOptions': {
+ enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'],
+ default: 'openOnFirstSessionStart',
+ description: 'Controls when the internal debug console should open.'
}
}
};
export class DebugConfiguration {
'debug.trace': boolean;
+ 'debug.debugViewLocation': 'default' | 'left' | 'right' | 'bottom';
+ 'debug.openDebug': 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart' | 'openOnDebugBreak';
+ 'debug.internalConsoleOptions': 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
}
export const DebugPreferences = Symbol('DebugPreferences');
diff --git a/packages/debug/src/browser/debug-schema-updater.ts b/packages/debug/src/browser/debug-schema-updater.ts
index 7b18d64721af1..03e4417718173 100644
--- a/packages/debug/src/browser/debug-schema-updater.ts
+++ b/packages/debug/src/browser/debug-schema-updater.ts
@@ -20,6 +20,7 @@ import { InMemoryResources, deepClone } from '@theia/core/lib/common';
import { IJSONSchema } from '@theia/core/lib/common/json-schema';
import URI from '@theia/core/lib/common/uri';
import { DebugService } from '../common/debug-service';
+import { debugPreferencesSchema } from './debug-preferences';
@injectable()
export class DebugSchemaUpdater {
@@ -37,24 +38,11 @@ export class DebugSchemaUpdater {
const attributePromises = types.map(type => this.debug.getSchemaAttributes(type));
for (const attributes of await Promise.all(attributePromises)) {
for (const attribute of attributes) {
- attribute.properties = {
- 'debugViewLocation': {
- enum: ['default', 'left', 'right', 'bottom'],
- default: 'default',
- description: 'Controls the location of the debug view.'
- },
- 'openDebug': {
- enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart', 'openOnDebugBreak'],
- default: 'openOnSessionStart',
- description: 'Controls when the debug view should open.'
- },
- 'internalConsoleOptions': {
- enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'],
- default: 'openOnFirstSessionStart',
- description: 'Controls when the internal debug console should open.'
- },
- ...attribute.properties
- };
+ const properties: typeof attribute['properties'] = {};
+ for (const key of ['debugViewLocation', 'openDebug', 'internalConsoleOptions']) {
+ properties[key] = debugPreferencesSchema.properties[`debug.${key}`];
+ }
+ attribute.properties = Object.assign(properties, attribute.properties);
items.oneOf!.push(attribute);
}
}
diff --git a/packages/debug/src/browser/debug-session.tsx b/packages/debug/src/browser/debug-session.tsx
index d0bd64c151758..c6c3a4336bbfe 100644
--- a/packages/debug/src/browser/debug-session.tsx
+++ b/packages/debug/src/browser/debug-session.tsx
@@ -36,6 +36,7 @@ import { DebugSessionOptions, InternalDebugSessionOptions } from './debug-sessio
import { DebugConfiguration } from '../common/debug-common';
import { SourceBreakpoint } from './breakpoint/breakpoint-marker';
import { FileSystem } from '@theia/filesystem/lib/common';
+import { TerminalWidgetOptions } from '@theia/terminal/lib/browser/base/terminal-widget';
export enum DebugState {
Inactive,
@@ -257,7 +258,7 @@ export class DebugSession implements CompositeTreeElement {
supportsVariablePaging: false,
supportsRunInTerminalRequest: true
});
- this._capabilities = response.body || {};
+ this.updateCapabilities(response.body || {});
}
protected async launchOrAttach(): Promise {
try {
@@ -367,7 +368,11 @@ export class DebugSession implements CompositeTreeElement {
}
protected async runInTerminal({ arguments: { title, cwd, args, env } }: DebugProtocol.RunInTerminalRequest): Promise {
- const terminal = await this.terminalServer.newTerminal({ title, cwd, shellPath: args[0], shellArgs: args.slice(1), env });
+ return this.doRunInTerminal({ title, cwd, shellPath: args[0], shellArgs: args.slice(1), env });
+ }
+
+ protected async doRunInTerminal(options: TerminalWidgetOptions): Promise {
+ const terminal = await this.terminalServer.newTerminal(options);
this.terminalServer.activateTerminal(terminal);
const processId = await terminal.start();
return { processId };
diff --git a/packages/debug/src/browser/style/debug-dark.svg b/packages/debug/src/browser/style/debug-dark.svg
new file mode 100644
index 0000000000000..5c4141704fbfb
--- /dev/null
+++ b/packages/debug/src/browser/style/debug-dark.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/packages/debug/src/browser/style/index.css b/packages/debug/src/browser/style/index.css
index d4303c3d41ec7..ad1376f29fe95 100644
--- a/packages/debug/src/browser/style/index.css
+++ b/packages/debug/src/browser/style/index.css
@@ -23,6 +23,11 @@
color: var(--theia-ui-font-color1);
}
+.theia-side-panel .theia-debug-container .theia-ExpansionToggle {
+ padding-right: 4px;
+ min-width: 10px;
+}
+
#theia-bottom-content-panel .theia-session-container .theia-view-container {
flex-direction: row;
}
@@ -118,6 +123,10 @@
margin-left: var(--theia-ui-padding);
}
+.theia-side-panel .debug-toolbar {
+ padding-left: 3px;
+}
+
.theia-session-container > .debug-toolbar {
padding-top: var(--theia-ui-padding);
padding-bottom: var(--theia-ui-padding);
@@ -152,8 +161,14 @@
opacity: 1;
}
-.debug-tab-icon::before {
- content: "\f188"
+.debug-tab-icon {
+ -webkit-mask: url('debug-dark.svg') 50%;
+ mask: url('debug-dark.svg') 50%;
+}
+
+.theia-debug-console-icon {
+ -webkit-mask: url('repl.svg');
+ mask: url('repl.svg');
}
/** Console */
diff --git a/packages/debug/src/browser/view/debug-widget.ts b/packages/debug/src/browser/view/debug-widget.ts
index 1a527c6452cb2..6e7b07fb5b728 100644
--- a/packages/debug/src/browser/view/debug-widget.ts
+++ b/packages/debug/src/browser/view/debug-widget.ts
@@ -55,7 +55,7 @@ export class DebugWidget extends BaseWidget implements ApplicationShell.Trackabl
this.title.label = DebugWidget.LABEL;
this.title.caption = DebugWidget.LABEL;
this.title.closable = true;
- this.title.iconClass = 'fa debug-tab-icon';
+ this.title.iconClass = 'debug-tab-icon';
this.addClass('theia-debug-container');
this.toDispose.pushAll([
this.toolbar,
diff --git a/packages/editor-preview/package.json b/packages/editor-preview/package.json
index 5f34d43682295..69be430d0d338 100644
--- a/packages/editor-preview/package.json
+++ b/packages/editor-preview/package.json
@@ -1,11 +1,11 @@
{
"name": "@theia/editor-preview",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Editor Preview Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/navigator": "^0.4.0"
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/navigator": "^0.5.0"
},
"publishConfig": {
"access": "public"
@@ -36,11 +36,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/editor-preview/src/browser/editor-preview-manager.ts b/packages/editor-preview/src/browser/editor-preview-manager.ts
index 09665ffab324f..3f7a2fd0f8b4d 100644
--- a/packages/editor-preview/src/browser/editor-preview-manager.ts
+++ b/packages/editor-preview/src/browser/editor-preview-manager.ts
@@ -17,7 +17,7 @@
import { injectable, inject, postConstruct } from 'inversify';
import URI from '@theia/core/lib/common/uri';
import { ApplicationShell, DockPanel } from '@theia/core/lib/browser';
-import { EditorManager, EditorOpenerOptions, EditorWidget } from '@theia/editor/lib/browser';
+import { EditorManager, EditorOpenerOptions, EditorWidget } from '@theia/editor/lib/browser';
import { EditorPreviewWidget } from './editor-preview-widget';
import { EditorPreviewWidgetFactory, EditorPreviewWidgetOptions } from './editor-preview-factory';
import { EditorPreviewPreferences } from './editor-preview-preferences';
@@ -35,7 +35,7 @@ export interface PreviewEditorOpenerOptions extends EditorOpenerOptions {
* Class for managing an editor preview widget.
*/
@injectable()
-export class EditorPreviewManager extends WidgetOpenHandler {
+export class EditorPreviewManager extends WidgetOpenHandler {
readonly id = EditorPreviewWidgetFactory.ID;
@@ -60,6 +60,14 @@ export class EditorPreviewManager extends WidgetOpenHandler {
+ this.currentEditorPreview.then(editorPreview => {
+ if (!change.newValue && editorPreview) {
+ editorPreview.pinEditorWidget();
+ }
+ });
+ });
}
protected async handlePreviewWidgetCreated(widget: EditorPreviewWidget): Promise {
@@ -72,13 +80,13 @@ export class EditorPreviewManager extends WidgetOpenHandler this.currentEditorPreview = Promise.resolve(undefined));
- widget.onPinned(({preview, editorWidget}) => {
+ widget.onPinned(({ preview, editorWidget }) => {
// TODO(caseyflynn): I don't believe there is ever a case where
// this will not hold true.
if (preview.parent && preview.parent instanceof DockPanel) {
- preview.parent.addWidget(editorWidget, {ref: preview});
+ preview.parent.addWidget(editorWidget, { ref: preview });
} else {
- this.shell.addWidget(editorWidget, {area: 'main'});
+ this.shell.addWidget(editorWidget, { area: 'main' });
}
preview.dispose();
this.shell.activateWidget(editorWidget.id);
@@ -100,7 +108,7 @@ export class EditorPreviewManager extends WidgetOpenHandler {
- options = {...options, mode: 'open'};
+ options = { ...options, mode: 'open' };
const deferred = new Deferred();
const previousPreview = await this.currentEditorPreview;
diff --git a/packages/editor/package.json b/packages/editor/package.json
index 49ccf28a64de5..2aa989b8dda1f 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -1,11 +1,11 @@
{
"name": "@theia/editor",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Editor Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/languages": "^0.4.0",
- "@theia/variable-resolver": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/languages": "^0.5.0",
+ "@theia/variable-resolver": "^0.5.0",
"@types/base64-arraybuffer": "0.1.0",
"base64-arraybuffer": "^0.1.5"
},
@@ -38,11 +38,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/editor/src/browser/editor-manager.ts b/packages/editor/src/browser/editor-manager.ts
index 2d29c98e1b8bd..29b549d8f8dba 100644
--- a/packages/editor/src/browser/editor-manager.ts
+++ b/packages/editor/src/browser/editor-manager.ts
@@ -25,6 +25,7 @@ import { TextEditor } from './editor';
export interface EditorOpenerOptions extends WidgetOpenerOptions {
selection?: RecursivePartial;
+ preview?: boolean;
}
@injectable()
diff --git a/packages/editor/src/browser/editor-preferences.ts b/packages/editor/src/browser/editor-preferences.ts
index 55b1fd69b5a9d..8cf52bd11cd52 100644
--- a/packages/editor/src/browser/editor-preferences.ts
+++ b/packages/editor/src/browser/editor-preferences.ts
@@ -96,8 +96,8 @@ export const editorPreferenceSchema: PreferenceSchema = {
'on',
'off'
],
- 'default': 'on',
- 'description': 'Configure whether the editor should be auto saved.',
+ 'default': 'off',
+ 'description': 'Controls auto save of dirty files.',
overridable: false
},
'editor.autoSaveDelay': {
diff --git a/packages/editorconfig/package.json b/packages/editorconfig/package.json
index 5ba2f390d6730..ab44620d22e58 100644
--- a/packages/editorconfig/package.json
+++ b/packages/editorconfig/package.json
@@ -1,12 +1,12 @@
{
"name": "@theia/editorconfig",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Editorconfig Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/languages": "^0.4.0",
- "@theia/monaco": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/languages": "^0.5.0",
+ "@theia/monaco": "^0.5.0",
"editorconfig": "^0.15.0"
},
"publishConfig": {
@@ -39,11 +39,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/editorconfig/src/browser/editorconfig-document-manager.ts b/packages/editorconfig/src/browser/editorconfig-document-manager.ts
index 803e707744ae8..53d790a40ee4c 100644
--- a/packages/editorconfig/src/browser/editorconfig-document-manager.ts
+++ b/packages/editorconfig/src/browser/editorconfig-document-manager.ts
@@ -98,6 +98,7 @@ export class EditorconfigDocumentManager {
const uri = editor.uri.toString();
this.editorconfigServer.getConfig(uri).then(properties => {
this.properties[uri] = properties;
+ this.applyProperties(properties, editor);
});
}
}
diff --git a/packages/extension-manager/package.json b/packages/extension-manager/package.json
index af2701394401c..5c6e3aaf25442 100644
--- a/packages/extension-manager/package.json
+++ b/packages/extension-manager/package.json
@@ -1,12 +1,12 @@
{
"name": "@theia/extension-manager",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Extension Manager",
"dependencies": {
- "@theia/application-manager": "^0.4.0",
- "@theia/application-package": "^0.4.0",
- "@theia/core": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
+ "@theia/application-manager": "^0.5.0",
+ "@theia/application-package": "^0.5.0",
+ "@theia/core": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
"@types/fs-extra": "^4.0.2",
"@types/sanitize-html": "^1.13.31",
"@types/showdown": "^1.7.1",
@@ -44,11 +44,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/extension-manager/src/browser/extension-contribution.ts b/packages/extension-manager/src/browser/extension-contribution.ts
index 462ffcb4e5868..e5f70f62a0184 100644
--- a/packages/extension-manager/src/browser/extension-contribution.ts
+++ b/packages/extension-manager/src/browser/extension-contribution.ts
@@ -35,7 +35,7 @@ export class ExtensionContribution extends AbstractViewContribution
+
+
+
diff --git a/packages/file-search/package.json b/packages/file-search/package.json
index b236492fc060e..12388d6cc4df1 100644
--- a/packages/file-search/package.json
+++ b/packages/file-search/package.json
@@ -1,13 +1,13 @@
{
"name": "@theia/file-search",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - File Search Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/process": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/process": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"fuzzy": "^0.1.3",
"vscode-ripgrep": "^1.2.4"
},
@@ -41,11 +41,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/file-search/src/browser/quick-file-open.ts b/packages/file-search/src/browser/quick-file-open.ts
index f04120e7f1abd..9e99e61c2bab7 100644
--- a/packages/file-search/src/browser/quick-file-open.ts
+++ b/packages/file-search/src/browser/quick-file-open.ts
@@ -205,6 +205,7 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler {
fuzzyMatch: true,
limit: 200,
useGitIgnore: this.hideIgnoredFiles,
+ excludePatterns: ['*.git*']
}, token).then(handler);
} else {
acceptor(recentlyUsedItems);
diff --git a/packages/file-search/src/common/file-search-service.ts b/packages/file-search/src/common/file-search-service.ts
index 3b1dbc0141c32..71ac6054e0238 100644
--- a/packages/file-search/src/common/file-search-service.ts
+++ b/packages/file-search/src/common/file-search-service.ts
@@ -33,13 +33,24 @@ export interface FileSearchService {
export const FileSearchService = Symbol('FileSearchService');
export namespace FileSearchService {
- export interface Options {
- rootUris: string[]
+ export interface BaseOptions {
+ useGitIgnore?: boolean
+ includePatterns?: string[]
+ excludePatterns?: string[]
+ }
+ export interface RootOptions {
+ [rootUri: string]: BaseOptions
+ }
+ export interface Options extends BaseOptions {
+ rootUris?: string[]
+ rootOptions?: RootOptions
fuzzyMatch?: boolean
limit?: number
- useGitIgnore?: boolean
- /** when `undefined`, no excludes will apply, when empty array, default excludes will apply */
+ /**
+ * when `undefined`, no excludes will apply, when empty array, default excludes will apply
+ *
+ * @deprecated since 0.5.0 use `excludePatterns` instead
+ */
defaultIgnorePatterns?: string[]
- includePatterns?: string[]
}
}
diff --git a/packages/file-search/src/node/file-search-service-impl.spec.ts b/packages/file-search/src/node/file-search-service-impl.spec.ts
index 074413ef88715..70cfb4bc86cda 100644
--- a/packages/file-search/src/node/file-search-service-impl.spec.ts
+++ b/packages/file-search/src/node/file-search-service-impl.spec.ts
@@ -24,6 +24,7 @@ import { CancellationTokenSource } from '@theia/core';
import { bindLogger } from '@theia/core/lib/node/logger-backend-module';
import processBackendModule from '@theia/process/lib/node/process-backend-module';
import URI from '@theia/core/lib/common/uri';
+import { FileSearchService } from '../common/file-search-service';
// tslint:disable:no-unused-expression
@@ -89,35 +90,71 @@ describe('search-service', function () {
expect(matches.length).to.eq(1);
});
- it('should support file searches with globs without the prefixed or trailing star (*)', async () => {
+ it('should NOT support file searches with globs without the prefixed or trailing star (*)', async () => {
const rootUri = FileUri.create(path.resolve(__dirname, '../../test-resources/subdir1/sub2')).toString();
const trailingMatches = await service.find('', { rootUris: [rootUri], includePatterns: ['*oo'] });
expect(trailingMatches).to.be.not.undefined;
- expect(trailingMatches.length).to.eq(1);
+ expect(trailingMatches.length).to.eq(0);
const prefixedMatches = await service.find('', { rootUris: [rootUri], includePatterns: ['oo*'] });
expect(prefixedMatches).to.be.not.undefined;
- expect(prefixedMatches.length).to.eq(1);
+ expect(prefixedMatches.length).to.eq(0);
});
});
describe('search with ignored patterns', () => {
- it('should ignore strings passed through the search options', async () => {
+ it('should NOT ignore strings passed through the search options', async () => {
const rootUri = FileUri.create(path.resolve(__dirname, '../../test-resources/subdir1/sub2')).toString();
- const matches = await service.find('', { rootUris: [rootUri], includePatterns: ['**/*oo.*'], defaultIgnorePatterns: ['foo'] });
+ const matches = await service.find('', { rootUris: [rootUri], includePatterns: ['**/*oo.*'], excludePatterns: ['foo'] });
expect(matches).to.be.not.undefined;
- expect(matches.length).to.eq(0);
+ expect(matches.length).to.eq(1);
});
- it('should ignore globs passed through the search options', async () => {
- const rootUri = FileUri.create(path.resolve(__dirname, '../../test-resources/subdir1/sub2')).toString();
-
- const matches = await service.find('', { rootUris: [rootUri], includePatterns: ['**/*oo.*'], defaultIgnorePatterns: ['*fo*'] });
+ const ignoreGlobsUri = FileUri.create(path.resolve(__dirname, '../../test-resources/subdir1/sub2')).toString();
+ it('should ignore globs passed through the search options #1', () => assertIgnoreGlobs({
+ rootUris: [ignoreGlobsUri],
+ includePatterns: ['**/*oo.*'],
+ excludePatterns: ['*fo*']
+ }));
+ it('should ignore globs passed through the search options #2', () => assertIgnoreGlobs({
+ rootOptions: {
+ [ignoreGlobsUri]: {
+ includePatterns: ['**/*oo.*'],
+ excludePatterns: ['*fo*']
+ }
+ }
+ }));
+ it('should ignore globs passed through the search options #3', () => assertIgnoreGlobs({
+ rootOptions: {
+ [ignoreGlobsUri]: {
+ includePatterns: ['**/*oo.*']
+ }
+ },
+ excludePatterns: ['*fo*']
+ }));
+ it('should ignore globs passed through the search options #4', () => assertIgnoreGlobs({
+ rootOptions: {
+ [ignoreGlobsUri]: {
+ excludePatterns: ['*fo*']
+ }
+ },
+ includePatterns: ['**/*oo.*']
+ }));
+ it('should ignore globs passed through the search options #5', () => assertIgnoreGlobs({
+ rootOptions: {
+ [ignoreGlobsUri]: {}
+ },
+ excludePatterns: ['*fo*'],
+ includePatterns: ['**/*oo.*']
+ }));
+
+ async function assertIgnoreGlobs(options: FileSearchService.Options): Promise {
+ const matches = await service.find('', options);
expect(matches).to.be.not.undefined;
expect(matches.length).to.eq(0);
- });
+ }
});
describe('irrelevant absolute results', () => {
diff --git a/packages/file-search/src/node/file-search-service-impl.ts b/packages/file-search/src/node/file-search-service-impl.ts
index 72da3d32625b0..bafc5f03c62ed 100644
--- a/packages/file-search/src/node/file-search-service-impl.ts
+++ b/packages/file-search/src/node/file-search-service-impl.ts
@@ -37,23 +37,45 @@ export class FileSearchServiceImpl implements FileSearchService {
clientToken.onCancellationRequested(() => cancellationSource.cancel());
}
const token = cancellationSource.token;
-
- if (options.defaultIgnorePatterns && options.defaultIgnorePatterns.length === 0) { // default excludes
- options.defaultIgnorePatterns.push('.git');
- }
const opts = {
fuzzyMatch: true,
limit: Number.MAX_SAFE_INTEGER,
useGitIgnore: true,
...options
};
+
+ const roots: FileSearchService.RootOptions = options.rootOptions || {};
+ if (options.rootUris) {
+ for (const rootUri of options.rootUris) {
+ if (!roots[rootUri]) {
+ roots[rootUri] = {};
+ }
+ }
+ }
+ // tslint:disable-next-line:forin
+ for (const rootUri in roots) {
+ const rootOptions = roots[rootUri];
+ if (opts.includePatterns) {
+ const includePatterns = rootOptions.includePatterns || [];
+ rootOptions.includePatterns = [...includePatterns, ...opts.includePatterns];
+ }
+ if (opts.excludePatterns) {
+ const excludePatterns = rootOptions.excludePatterns || [];
+ rootOptions.excludePatterns = [...excludePatterns, ...opts.excludePatterns];
+ }
+ if (rootOptions.useGitIgnore === undefined) {
+ rootOptions.useGitIgnore = opts.useGitIgnore;
+ }
+ }
+
const exactMatches = new Set();
const fuzzyMatches = new Set();
const stringPattern = searchPattern.toLocaleLowerCase();
- await Promise.all(options.rootUris.map(async root => {
+ await Promise.all(Object.keys(roots).map(async root => {
try {
const rootUri = new URI(root);
- await this.doFind(rootUri, searchPattern, opts, candidate => {
+ const rootOptions = roots[root];
+ await this.doFind(rootUri, rootOptions, candidate => {
const fileUri = rootUri.resolve(candidate).toString();
if (exactMatches.has(fileUri) || fuzzyMatches.has(fileUri)) {
return;
@@ -77,7 +99,7 @@ export class FileSearchServiceImpl implements FileSearchService {
return [...exactMatches, ...fuzzyMatches];
}
- private doFind(rootUri: URI, searchPattern: string, options: FileSearchService.Options,
+ private doFind(rootUri: URI, options: FileSearchService.BaseOptions,
accept: (fileUri: string) => void, token: CancellationToken): Promise {
return new Promise((resolve, reject) => {
try {
@@ -106,38 +128,25 @@ export class FileSearchServiceImpl implements FileSearchService {
});
}
- private getSearchArgs(options: FileSearchService.Options): string[] {
+ private getSearchArgs(options: FileSearchService.BaseOptions): string[] {
const args = ['--files', '--case-sensitive'];
if (options.includePatterns) {
for (const includePattern of options.includePatterns) {
- let includeGlob = includePattern;
- if (!includeGlob.endsWith('*')) {
- includeGlob = `${includeGlob}*`;
+ if (includePattern) {
+ args.push('--glob', includePattern);
}
- if (!includeGlob.startsWith('*')) {
- includeGlob = `*${includeGlob}`;
+ }
+ }
+ if (options.excludePatterns) {
+ for (const excludePattern of options.excludePatterns) {
+ if (excludePattern) {
+ args.push('--glob', `!${excludePattern}`);
}
- args.push('--glob', includeGlob);
}
}
if (!options.useGitIgnore) {
args.push('-uu');
}
- if (options && options.defaultIgnorePatterns) {
- options.defaultIgnorePatterns.filter(p => p !== '')
- .forEach(ignore => {
- if (!ignore.endsWith('*')) {
- ignore = `${ignore}*`;
- }
- if (!ignore.startsWith('*')) {
- ignore = `!*${ignore}`;
- } else {
- ignore = `!${ignore}`;
- }
- args.push('--glob');
- args.push(ignore);
- });
- }
return args;
}
diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json
index 32339e1aca79e..b71f7cfe05131 100644
--- a/packages/filesystem/package.json
+++ b/packages/filesystem/package.json
@@ -1,11 +1,12 @@
{
"name": "@theia/filesystem",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - FileSystem Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
+ "@theia/core": "^0.5.0",
"@types/base64-js": "^1.2.5",
"@types/body-parser": "^1.17.0",
+ "@types/formidable": "^1.0.31",
"@types/fs-extra": "^4.0.2",
"@types/mime-types": "^2.1.0",
"@types/rimraf": "^2.0.2",
@@ -15,6 +16,7 @@
"base64-js": "^1.2.1",
"body-parser": "^1.18.3",
"drivelist": "^6.4.3",
+ "formidable": "^1.2.1",
"fs-extra": "^4.0.2",
"http-status-codes": "^1.3.0",
"mime-types": "^2.1.18",
@@ -66,11 +68,10 @@
"build": "theiaext build",
"watch": "theiaext watch",
"test": "theiaext test",
- "test:watch": "theiaext test:watch",
- "docs": "theiaext docs"
+ "test:watch": "theiaext test:watch"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/filesystem/src/browser/download/file-download-command-contribution.ts b/packages/filesystem/src/browser/download/file-download-command-contribution.ts
index 079ff055c1359..4e19defa86b9f 100644
--- a/packages/filesystem/src/browser/download/file-download-command-contribution.ts
+++ b/packages/filesystem/src/browser/download/file-download-command-contribution.ts
@@ -16,12 +16,14 @@
import { inject, injectable } from 'inversify';
import URI from '@theia/core/lib/common/uri';
-import { notEmpty } from '@theia/core/lib/common/objects';
-import { UriSelection } from '@theia/core/lib/common/selection';
import { SelectionService } from '@theia/core/lib/common/selection-service';
import { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/common/command';
import { UriAwareCommandHandler, UriCommandHandler } from '@theia/core/lib/common/uri-command-handler';
+import { ExpandableTreeNode } from '@theia/core/lib/browser/tree';
import { FileDownloadService } from './file-download-service';
+import { FileSelection } from '../file-selection';
+import { TreeWidgetSelection } from '@theia/core/lib/browser/tree/tree-widget-selection';
+import { isCancelled } from '@theia/core/lib/common/cancellation';
@injectable()
export class FileDownloadCommandContribution implements CommandContribution {
@@ -35,6 +37,30 @@ export class FileDownloadCommandContribution implements CommandContribution {
registerCommands(registry: CommandRegistry): void {
const handler = new UriAwareCommandHandler(this.selectionService, this.downloadHandler(), { multi: true });
registry.registerCommand(FileDownloadCommands.DOWNLOAD, handler);
+ registry.registerCommand(FileDownloadCommands.UPLOAD, new FileSelection.CommandHandler(this.selectionService, {
+ multi: false,
+ isEnabled: selection => this.canUpload(selection),
+ isVisible: selection => this.canUpload(selection),
+ execute: selection => this.upload(selection)
+ }));
+ }
+
+ protected canUpload({ fileStat }: FileSelection): boolean {
+ return fileStat.isDirectory;
+ }
+
+ protected async upload(selection: FileSelection): Promise {
+ try {
+ const source = TreeWidgetSelection.getSource(this.selectionService.selection);
+ await this.downloadService.upload(selection.fileStat.uri);
+ if (ExpandableTreeNode.is(selection) && source) {
+ await source.model.expandNode(selection);
+ }
+ } catch (e) {
+ if (!isCancelled(e)) {
+ console.error(e);
+ }
+ }
}
protected downloadHandler(): UriCommandHandler {
@@ -57,29 +83,20 @@ export class FileDownloadCommandContribution implements CommandContribution {
return this.isDownloadEnabled(uris);
}
- protected getUris(uri: Object | undefined): URI[] {
- if (uri === undefined) {
- return [];
- }
- return (Array.isArray(uri) ? uri : [uri]).map(u => this.getUri(u)).filter(notEmpty);
- }
-
- protected getUri(uri: Object | undefined): URI | undefined {
- if (uri instanceof URI) {
- return uri;
- }
- if (UriSelection.is(uri)) {
- return uri.uri;
- }
- return undefined;
- }
-
}
export namespace FileDownloadCommands {
export const DOWNLOAD: Command = {
- id: 'file.download'
+ id: 'file.download',
+ category: 'File',
+ label: 'Download'
+ };
+
+ export const UPLOAD: Command = {
+ id: 'file.upload',
+ category: 'File',
+ label: 'Upload Files...'
};
}
diff --git a/packages/filesystem/src/browser/download/file-download-service.ts b/packages/filesystem/src/browser/download/file-download-service.ts
index 25d795ab9ec47..b870a97ca5824 100644
--- a/packages/filesystem/src/browser/download/file-download-service.ts
+++ b/packages/filesystem/src/browser/download/file-download-service.ts
@@ -14,13 +14,16 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
-import { inject, injectable } from 'inversify';
+import { inject, injectable, postConstruct } from 'inversify';
import URI from '@theia/core/lib/common/uri';
+import { cancelled } from '@theia/core/lib/common/cancellation';
import { ILogger } from '@theia/core/lib/common/logger';
import { Endpoint } from '@theia/core/lib/browser/endpoint';
import { StatusBar, StatusBarAlignment } from '@theia/core/lib/browser/status-bar';
import { FileSystem } from '../../common/filesystem';
import { FileDownloadData } from '../../common/download/file-download-data';
+import { Deferred } from '@theia/core/lib/common/promise-util';
+import { MessageService } from '@theia/core/lib/common/message-service';
@injectable()
export class FileDownloadService {
@@ -40,6 +43,110 @@ export class FileDownloadService {
@inject(StatusBar)
protected readonly statusBar: StatusBar;
+ @inject(MessageService)
+ protected readonly messageService: MessageService;
+
+ protected uploadForm: {
+ target: HTMLInputElement
+ file: HTMLInputElement
+ };
+
+ @postConstruct()
+ protected init(): void {
+ this.uploadForm = this.createUploadForm();
+ }
+
+ protected createUploadForm(): {
+ target: HTMLInputElement
+ file: HTMLInputElement
+ } {
+ const target = document.createElement('input');
+ target.type = 'text';
+ target.name = 'target';
+
+ const file = document.createElement('input');
+ file.type = 'file';
+ file.name = 'upload';
+ file.multiple = true;
+
+ const form = document.createElement('form');
+ form.style.display = 'none';
+ form.enctype = 'multipart/form-data';
+ form.append(target);
+ form.append(file);
+
+ document.body.appendChild(form);
+
+ file.addEventListener('change', async () => {
+ if (file.value) {
+ const body = new FormData(form);
+ // clean up to allow upload to the same folder twice
+ file.value = '';
+ const filesUrl = this.filesUrl();
+ const deferredUpload = this.deferredUpload;
+ try {
+ const request = new XMLHttpRequest();
+
+ const cb = () => {
+ if (request.status === 200) {
+ deferredUpload.resolve();
+ } else {
+ let statusText = request.statusText;
+ if (!statusText) {
+ if (request.status === 413) {
+ statusText = 'Payload Too Large';
+ } else if (request.status) {
+ statusText = String(request.status);
+ } else {
+ statusText = 'Network Failure';
+ }
+ }
+ const message = 'Upload Failed: ' + statusText;
+ deferredUpload.reject(new Error(message));
+ this.messageService.error(message);
+ }
+ };
+ request.addEventListener('load', cb);
+ request.addEventListener('error', cb);
+ request.addEventListener('abort', () => deferredUpload.reject(cancelled()));
+
+ const progress = await this.messageService.showProgress({
+ text: 'Uploading Files...', options: { cancelable: true }
+ }, () => {
+ request.upload.removeEventListener('progress', progressListener);
+ request.abort();
+ });
+ deferredUpload.promise.then(() => progress.cancel(), () => progress.cancel());
+ const progressListener = (event: ProgressEvent) => {
+ if (event.lengthComputable) {
+ progress.report({
+ work: {
+ done: event.loaded,
+ total: event.total
+ }
+ });
+ }
+ };
+ request.upload.addEventListener('progress', progressListener);
+
+ request.open('POST', filesUrl);
+ request.send(body);
+ } catch (e) {
+ deferredUpload.reject(e);
+ }
+ }
+ });
+ return { target, file };
+ }
+
+ protected deferredUpload = new Deferred();
+ upload(targetUri: string | URI): Promise {
+ this.deferredUpload = new Deferred();
+ this.uploadForm.target.value = String(targetUri);
+ this.uploadForm.file.click();
+ return this.deferredUpload.promise;
+ }
+
async download(uris: URI[]): Promise {
if (uris.length === 0) {
return;
@@ -155,8 +262,12 @@ export class FileDownloadService {
}
protected endpoint(): string {
- const url = new Endpoint({ path: 'files' }).getRestUrl().toString();
+ const url = this.filesUrl();
return url.endsWith('/') ? url.slice(0, -1) : url;
}
+ protected filesUrl(): string {
+ return new Endpoint({ path: 'files' }).getRestUrl().toString();
+ }
+
}
diff --git a/packages/filesystem/src/browser/file-dialog/file-dialog.ts b/packages/filesystem/src/browser/file-dialog/file-dialog.ts
index bf1e012e849ba..fb035641049f0 100644
--- a/packages/filesystem/src/browser/file-dialog/file-dialog.ts
+++ b/packages/filesystem/src/browser/file-dialog/file-dialog.ts
@@ -261,9 +261,12 @@ export class OpenFileDialog extends FileDialog> {
}
}
- protected accept(): void {
- if (this.props.canSelectFolders === false && !Array.isArray(this.value)) {
- this.widget.model.openNode(this.value);
+ protected async accept(): Promise {
+ const selection = this.value;
+ if (!this.props.canSelectFolders
+ && !Array.isArray(selection)
+ && selection.fileStat.isDirectory) {
+ this.widget.model.openNode(selection);
return;
}
super.accept();
diff --git a/packages/filesystem/src/browser/file-resource.ts b/packages/filesystem/src/browser/file-resource.ts
index 10d4203a76f39..79c365142d161 100644
--- a/packages/filesystem/src/browser/file-resource.ts
+++ b/packages/filesystem/src/browser/file-resource.ts
@@ -116,7 +116,7 @@ export class FileResource implements Resource {
}
protected async getFileStat(): Promise {
- if (!this.fileSystem.exists(this.uriString)) {
+ if (!await this.fileSystem.exists(this.uriString)) {
return undefined;
}
try {
diff --git a/packages/filesystem/src/browser/file-selection.ts b/packages/filesystem/src/browser/file-selection.ts
new file mode 100644
index 0000000000000..d26fff13eedbd
--- /dev/null
+++ b/packages/filesystem/src/browser/file-selection.ts
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * Copyright (C) 2019 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { SelectionService } from '@theia/core/lib/common/selection-service';
+import { SelectionCommandHandler } from '@theia/core/lib/common/selection-command-handler';
+import { FileStat } from '../common/filesystem';
+
+export interface FileSelection {
+ fileStat: FileStat
+}
+export namespace FileSelection {
+ export function is(arg: Object | undefined): arg is FileSelection {
+ return typeof arg === 'object' && ('fileStat' in arg) && FileStat.is(arg['fileStat']);
+ }
+ export class CommandHandler extends SelectionCommandHandler {
+
+ constructor(
+ protected readonly selectionService: SelectionService,
+ protected readonly options: SelectionCommandHandler.Options
+ ) {
+ super(
+ selectionService,
+ arg => FileSelection.is(arg) ? arg : undefined,
+ options
+ );
+ }
+
+ }
+
+}
diff --git a/packages/filesystem/src/browser/file-tree/file-tree.ts b/packages/filesystem/src/browser/file-tree/file-tree.ts
index c796d8325641c..3466ea959d8eb 100644
--- a/packages/filesystem/src/browser/file-tree/file-tree.ts
+++ b/packages/filesystem/src/browser/file-tree/file-tree.ts
@@ -20,6 +20,7 @@ import { TreeNode, CompositeTreeNode, SelectableTreeNode, ExpandableTreeNode, Tr
import { FileSystem, FileStat } from '../../common';
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
import { UriSelection } from '@theia/core/lib/common/selection';
+import { FileSelection } from '../file-selection';
@injectable()
export class FileTree extends TreeImpl {
@@ -91,8 +92,7 @@ export class FileTree extends TreeImpl {
}
}
-export interface FileStatNode extends SelectableTreeNode, UriSelection {
- fileStat: FileStat;
+export interface FileStatNode extends SelectableTreeNode, UriSelection, FileSelection {
}
export namespace FileStatNode {
export function is(node: object | undefined): node is FileStatNode {
diff --git a/packages/filesystem/src/browser/filesystem-frontend-module.ts b/packages/filesystem/src/browser/filesystem-frontend-module.ts
index 5f3583842d87e..d8a7c749bb01e 100644
--- a/packages/filesystem/src/browser/filesystem-frontend-module.ts
+++ b/packages/filesystem/src/browser/filesystem-frontend-module.ts
@@ -14,6 +14,8 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
+import '../../src/browser/style/index.css';
+
import { ContainerModule } from 'inversify';
import { ResourceResolver } from '@theia/core/lib/common';
import { WebSocketConnectionProvider, FrontendApplicationContribution, ConfirmDialog } from '@theia/core/lib/browser';
@@ -26,8 +28,7 @@ import { FileResourceResolver } from './file-resource';
import { bindFileSystemPreferences } from './filesystem-preferences';
import { FileSystemWatcher } from './filesystem-watcher';
import { FileSystemFrontendContribution } from './filesystem-frontend-contribution';
-
-import '../../src/browser/style/index.css';
+import { FileSystemProxyFactory } from './filesystem-proxy-factory';
export default new ContainerModule(bind => {
bindFileSystemPreferences(bind);
@@ -47,9 +48,11 @@ export default new ContainerModule(bind => {
return !!await dialog.open();
});
- bind(FileSystem).toDynamicValue(ctx =>
- WebSocketConnectionProvider.createProxy(ctx.container, fileSystemPath)
- ).inSingletonScope();
+ bind(FileSystemProxyFactory).toSelf();
+ bind(FileSystem).toDynamicValue(ctx => {
+ const proxyFactory = ctx.container.get(FileSystemProxyFactory);
+ return WebSocketConnectionProvider.createProxy(ctx.container, fileSystemPath, proxyFactory);
+ }).inSingletonScope();
bind(FileResourceResolver).toSelf().inSingletonScope();
bind(ResourceResolver).toService(FileResourceResolver);
diff --git a/packages/filesystem/src/browser/filesystem-preferences.ts b/packages/filesystem/src/browser/filesystem-preferences.ts
index 4adacc6dd02ef..aee86e22f7fd9 100644
--- a/packages/filesystem/src/browser/filesystem-preferences.ts
+++ b/packages/filesystem/src/browser/filesystem-preferences.ts
@@ -43,6 +43,11 @@ export const filesystemPreferenceSchema: PreferenceSchema = {
'default': { '**/.git': true, '**/.svn': true, '**/.hg': true, '**/CVS': true, '**/.DS_Store': true },
'description': 'Configure glob patterns for excluding files and folders.',
'scope': 'resource'
+ },
+ 'files.enableTrash': {
+ 'type': 'boolean',
+ 'default': true,
+ 'description': 'Moves files/folders to the OS trash (recycle bin on Windows) when deleting. Disabling this will delete files/folders permanently.'
}
}
};
@@ -50,6 +55,7 @@ export const filesystemPreferenceSchema: PreferenceSchema = {
export interface FileSystemConfiguration {
'files.watcherExclude': { [globPattern: string]: boolean };
'files.exclude': { [key: string]: boolean };
+ 'files.enableTrash': boolean;
}
export const FileSystemPreferences = Symbol('FileSystemPreferences');
diff --git a/packages/filesystem/src/browser/filesystem-proxy-factory.ts b/packages/filesystem/src/browser/filesystem-proxy-factory.ts
new file mode 100644
index 0000000000000..f93a54eadabfb
--- /dev/null
+++ b/packages/filesystem/src/browser/filesystem-proxy-factory.ts
@@ -0,0 +1,45 @@
+/********************************************************************************
+ * Copyright (C) 2019 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+// tslint:disable:no-any
+
+import { injectable, inject } from 'inversify';
+import { JsonRpcProxyFactory } from '@theia/core/lib/common/messaging/proxy-factory';
+import { FileSystem, FileDeleteOptions } from '../common/filesystem';
+import { FileSystemPreferences } from './filesystem-preferences';
+
+@injectable()
+export class FileSystemProxyFactory extends JsonRpcProxyFactory {
+
+ @inject(FileSystemPreferences)
+ protected readonly preferences: FileSystemPreferences;
+
+ get(target: FileSystem, propertyKey: PropertyKey, receiver: any): any {
+ const property = super.get(target, propertyKey, receiver);
+ if (propertyKey !== 'delete') {
+ return property;
+ }
+ const deleteFn: FileSystem['delete'] = (uri, options) => {
+ const opt: FileDeleteOptions = { ...options };
+ if (opt.moveToTrash === undefined) {
+ opt.moveToTrash = this.preferences['files.enableTrash'];
+ }
+ return property(uri, opt);
+ };
+ return deleteFn;
+ }
+
+}
diff --git a/packages/filesystem/src/browser/style/file-dialog.css b/packages/filesystem/src/browser/style/file-dialog.css
index 1b6e3c3c2b80e..fa4aed9435bdf 100644
--- a/packages/filesystem/src/browser/style/file-dialog.css
+++ b/packages/filesystem/src/browser/style/file-dialog.css
@@ -25,6 +25,7 @@
.dialogContent .theia-FileDialog {
height: 500px;
+ background-color: var(--theia-layout-color0);
}
.dialogContent .theia-SaveFileDialog {
diff --git a/packages/filesystem/src/browser/style/file-icons.css b/packages/filesystem/src/browser/style/file-icons.css
index 165ff782e10ea..0627fbc315480 100644
--- a/packages/filesystem/src/browser/style/file-icons.css
+++ b/packages/filesystem/src/browser/style/file-icons.css
@@ -14,8 +14,7 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
-.file-icon {
- max-height: calc(var(--theia-content-font-size) * 0.8);
+ .file-icon {
/*
Here, the `line-height` ensures that FontAwesome and `file-icons-js` container has the same height.
Ideally, it would be 1 em, but since we set the max height above (and other places too) with 0.8
@@ -28,4 +27,14 @@
font-size: calc(var(--theia-content-font-size) * 0.8);
text-align: center;
margin-right: 4px;
-}
\ No newline at end of file
+}
+
+.theia-app-sides .file-icon {
+ max-height: none;
+ line-height: inherit;
+}
+
+.theia-app-sides .file-icon:before {
+ font-size: var(--theia-private-sidebar-icon-size);
+ margin-right: 0px;
+}
diff --git a/packages/filesystem/src/common/filesystem.ts b/packages/filesystem/src/common/filesystem.ts
index 8971948c48785..cfddd74da11af 100644
--- a/packages/filesystem/src/common/filesystem.ts
+++ b/packages/filesystem/src/common/filesystem.ts
@@ -258,10 +258,8 @@ export interface FileStat {
}
export namespace FileStat {
- export function is(candidate: object): candidate is FileStat {
- return candidate.hasOwnProperty('uri')
- && candidate.hasOwnProperty('lastModification')
- && candidate.hasOwnProperty('isDirectory');
+ export function is(candidate: Object | undefined): candidate is FileStat {
+ return typeof candidate === 'object' && ('uri' in candidate) && ('lastModification' in candidate) && ('isDirectory' in candidate);
}
export function equals(one: object | undefined, other: object | undefined): boolean {
diff --git a/packages/filesystem/src/node/download/file-download-endpoint.ts b/packages/filesystem/src/node/download/file-download-endpoint.ts
index 86212d79662f8..be81f6306df09 100644
--- a/packages/filesystem/src/node/download/file-download-endpoint.ts
+++ b/packages/filesystem/src/node/download/file-download-endpoint.ts
@@ -16,10 +16,20 @@
import { injectable, inject, named } from 'inversify';
import { json } from 'body-parser';
-import { Application, Router } from 'express';
+// tslint:disable-next-line:no-implicit-dependencies
+import { Application, Router, Request, Response, NextFunction } from 'express';
+import * as formidable from 'formidable';
+import URI from '@theia/core/lib/common/uri';
+import { FileUri } from '@theia/core/lib/node/file-uri';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import { FileDownloadHandler } from './file-download-handler';
+// upload max file size in MB, default 2048
+let uploadMaxFileSize = Number(process.env.THEIA_UPLOAD_MAX_FILE_SIZE);
+if (typeof uploadMaxFileSize !== 'number' || Number.isNaN(uploadMaxFileSize) || !Number.isFinite(uploadMaxFileSize)) {
+ uploadMaxFileSize = 2048;
+}
+
@injectable()
export class FileDownloadEndpoint implements BackendApplicationContribution {
@@ -34,12 +44,53 @@ export class FileDownloadEndpoint implements BackendApplicationContribution {
protected readonly multiFileDownloadHandler: FileDownloadHandler;
configure(app: Application): void {
+ const upload = this.upload.bind(this);
const router = Router();
router.get('/', (request, response) => this.singleFileDownloadHandler.handle(request, response));
router.put('/', (request, response) => this.multiFileDownloadHandler.handle(request, response));
+ router.post('/', upload);
// Content-Type: application/json
app.use(json());
app.use(FileDownloadEndpoint.PATH, router);
}
+ protected upload(req: Request, res: Response, next: NextFunction): void {
+ const form = new formidable.IncomingForm();
+ form.multiples = true;
+ form.maxFileSize = uploadMaxFileSize * 1024 * 1024;
+
+ let targetUri: URI | undefined;
+ const clientErrors: string[] = [];
+ form.on('field', (name: string, value: string) => {
+ if (name === 'target') {
+ targetUri = new URI(value);
+ }
+ });
+ form.on('fileBegin', (_: string, file: formidable.File) => {
+ if (targetUri) {
+ file.path = FileUri.fsPath(targetUri.resolve(file.name));
+ } else {
+ clientErrors.push(`cannot upload "${file.name}", target is not provided`);
+ }
+ });
+ form.on('error', (error: Error) => {
+ if (String(error).indexOf('maxFileSize') !== -1) {
+ res.writeHead(413, 'Payload Exceeded ' + uploadMaxFileSize + 'MB');
+ } else {
+ console.error(error);
+ res.writeHead(500, String(error));
+ }
+ res.end();
+ });
+ form.on('end', () => {
+ if (clientErrors.length) {
+ res.writeHead(400, clientErrors.join('\n'));
+ } else {
+ res.writeHead(200);
+ }
+ res.end();
+ });
+ form.parse(req);
+ }
+
}
diff --git a/packages/filesystem/src/node/download/file-download-handler.ts b/packages/filesystem/src/node/download/file-download-handler.ts
index e53cb5a8e51df..52888e49fe182 100644
--- a/packages/filesystem/src/node/download/file-download-handler.ts
+++ b/packages/filesystem/src/node/download/file-download-handler.ts
@@ -89,7 +89,7 @@ export abstract class FileDownloadHandler {
protected async handleError(response: Response, reason: string | Error, status: number = INTERNAL_SERVER_ERROR): Promise {
this.logger.error(reason);
- response.status(status).send(reason).end();
+ response.status(status).send('Unable to download file.').end();
}
}
diff --git a/packages/filesystem/src/node/node-filesystem.spec.ts b/packages/filesystem/src/node/node-filesystem.spec.ts
index 66ac753caeecb..3edfacbe17fb7 100644
--- a/packages/filesystem/src/node/node-filesystem.spec.ts
+++ b/packages/filesystem/src/node/node-filesystem.spec.ts
@@ -446,6 +446,14 @@ describe('NodeFileSystem', function () {
await expectThrowsAsync(fileSystem.copy(sourceUri.toString(), targetUri.toString()), Error);
});
+ it('Copy a file to existing location with the same file name. Should be rejected with an error.', async () => {
+ const sourceUri = root.resolve('foo');
+ fs.mkdirSync(FileUri.fsPath(sourceUri));
+ expect(fs.statSync(FileUri.fsPath(sourceUri)).isDirectory()).to.be.true;
+
+ await expectThrowsAsync(fileSystem.copy(sourceUri.toString(), sourceUri.toString()), Error);
+ });
+
it('Copy an empty directory to a non-existing location. Should return with the file stat representing the new file at the target location.', async () => {
const sourceUri = root.resolve('foo');
const targetUri = root.resolve('bar');
diff --git a/packages/filesystem/src/node/node-filesystem.ts b/packages/filesystem/src/node/node-filesystem.ts
index 229330e91cfc9..64a580367b208 100644
--- a/packages/filesystem/src/node/node-filesystem.ts
+++ b/packages/filesystem/src/node/node-filesystem.ts
@@ -231,6 +231,9 @@ export class FileSystemNode implements FileSystem {
if (targetStat && !overwrite) {
throw FileSystemError.FileExists(targetUri, "Did you set the 'overwrite' flag to true?");
}
+ if (targetStat && targetStat.uri === sourceStat.uri) {
+ throw FileSystemError.FileExists(targetUri, 'Cannot perform copy, source and destination are the same.');
+ }
await fs.copy(FileUri.fsPath(_sourceUri), FileUri.fsPath(_targetUri), { overwrite, recursive });
const newStat = await this.doGetStat(_targetUri, 1);
if (newStat) {
diff --git a/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-watcher.ts b/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-watcher.ts
index 51eb1056d9f08..7138e7275f7e1 100644
--- a/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-watcher.ts
+++ b/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-watcher.ts
@@ -15,7 +15,7 @@
********************************************************************************/
import * as fs from 'fs';
-import * as nsfw from 'vscode-nsfw';
+import * as nsfw from 'nsfw';
import * as paths from 'path';
import { IMinimatch, Minimatch } from 'minimatch';
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
@@ -111,17 +111,17 @@ export class NsfwFileSystemWatcherServer implements FileSystemWatcherServer {
let watcher: nsfw.NSFW | undefined = await nsfw(fs.realpathSync(basePath), (events: nsfw.ChangeEvent[]) => {
for (const event of events) {
if (event.action === nsfw.actions.CREATED) {
- this.pushAdded(watcherId, paths.join(event.directory, event.file!));
+ this.pushAdded(watcherId, this.resolvePath(event.directory, event.file!));
}
if (event.action === nsfw.actions.DELETED) {
- this.pushDeleted(watcherId, paths.join(event.directory, event.file!));
+ this.pushDeleted(watcherId, this.resolvePath(event.directory, event.file!));
}
if (event.action === nsfw.actions.MODIFIED) {
- this.pushUpdated(watcherId, paths.join(event.directory, event.file!));
+ this.pushUpdated(watcherId, this.resolvePath(event.directory, event.file!));
}
if (event.action === nsfw.actions.RENAMED) {
- this.pushDeleted(watcherId, paths.join(event.directory, event.oldFile!));
- this.pushAdded(watcherId, paths.join(event.directory, event.newFile!));
+ this.pushDeleted(watcherId, this.resolvePath(event.directory, event.oldFile!));
+ this.pushAdded(watcherId, this.resolvePath(event.directory, event.newFile!));
}
}
});
@@ -192,6 +192,21 @@ export class NsfwFileSystemWatcherServer implements FileSystemWatcherServer {
this.fireDidFilesChanged();
}
+ protected resolvePath(directory: string, file: string): string {
+ const path = paths.join(directory, file);
+ try {
+ return fs.realpathSync(path);
+ } catch {
+ try {
+ // file does not exist try to resolve directory
+ return paths.join(fs.realpathSync(directory), file);
+ } catch {
+ // directory does not exist fall back to symlink
+ return path;
+ }
+ }
+ }
+
/**
* Fires file changes to clients.
* It is debounced in the case if the filesystem is spamming to avoid overwhelming clients with events.
diff --git a/packages/getting-started/package.json b/packages/getting-started/package.json
index 783ea464afc66..f6ddfb3de97d9 100644
--- a/packages/getting-started/package.json
+++ b/packages/getting-started/package.json
@@ -1,12 +1,12 @@
{
"name": "@theia/getting-started",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - GettingStarted Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/keymaps": "^0.4.0",
- "@theia/workspace": "^0.4.0"
+ "@theia/core": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/keymaps": "^0.5.0",
+ "@theia/workspace": "^0.5.0"
},
"publishConfig": {
"access": "public"
@@ -38,11 +38,10 @@
"build": "theiaext build",
"watch": "theiaext watch",
"test": "theiaext test",
- "test:watch": "theiaext test:watch",
- "docs": "theiaext docs"
+ "test:watch": "theiaext test:watch"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/git/package.json b/packages/git/package.json
index e4cd60fc49dc1..d4fbd7c6d19d6 100644
--- a/packages/git/package.json
+++ b/packages/git/package.json
@@ -1,14 +1,14 @@
{
"name": "@theia/git",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Git Integration",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/languages": "^0.4.0",
- "@theia/navigator": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/languages": "^0.5.0",
+ "@theia/navigator": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"@types/diff": "^3.2.2",
"@types/fs-extra": "^4.0.2",
"@types/p-queue": "^2.3.1",
@@ -61,11 +61,10 @@
"build": "theiaext build",
"watch": "theiaext watch",
"test": "theiaext test",
- "test:watch": "theiaext test:watch",
- "docs": "theiaext docs"
+ "test:watch": "theiaext test:watch"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0",
+ "@theia/ext-scripts": "^0.5.0",
"upath": "^1.0.2"
},
"nyc": {
diff --git a/packages/git/src/browser/diff/git-diff-contribution.ts b/packages/git/src/browser/diff/git-diff-contribution.ts
index f069b8df27d6b..ba7187acb68c2 100644
--- a/packages/git/src/browser/diff/git-diff-contribution.ts
+++ b/packages/git/src/browser/diff/git-diff-contribution.ts
@@ -56,7 +56,7 @@ export class GitDiffContribution extends AbstractViewContribution
widgetName: 'Git diff',
defaultWidgetOptions: {
area: 'left',
- rank: 400
+ rank: 500
}
});
}
diff --git a/packages/git/src/browser/diff/git-diff-widget.tsx b/packages/git/src/browser/diff/git-diff-widget.tsx
index 505786062773c..e084ff1cf3f98 100644
--- a/packages/git/src/browser/diff/git-diff-widget.tsx
+++ b/packages/git/src/browser/diff/git-diff-widget.tsx
@@ -32,6 +32,8 @@ export const GIT_DIFF = 'git-diff';
@injectable()
export class GitDiffWidget extends GitNavigableListWidget implements StatefulWidget {
+ protected readonly GIT_DIFF_TITLE = 'Diff';
+
protected fileChangeNodes: GitFileChangeNode[] = [];
protected options: Git.Options.Diff;
@@ -48,7 +50,10 @@ export class GitDiffWidget extends GitNavigableListWidget imp
super();
this.id = GIT_DIFF;
this.scrollContainer = 'git-diff-list-container';
- this.title.label = 'Diff';
+ this.title.label = this.GIT_DIFF_TITLE;
+ this.title.caption = this.GIT_DIFF_TITLE;
+ this.title.closable = true;
+ this.title.iconClass = 'theia-git-diff-icon';
this.addClass('theia-git');
}
@@ -401,7 +406,7 @@ export class GitDiffListContainer extends React.Component this.listContainer = ref || undefined} className='listContainer' id={id} tabIndex={0}>{...files} ;
+ return this.listContainer = ref || undefined} className='listContainer filesChanged' id={id} tabIndex={0}>{...files}
;
}
componentDidMount() {
diff --git a/packages/git/src/browser/git-view-contribution.ts b/packages/git/src/browser/git-view-contribution.ts
index 5939b6e0d575e..5788509685c6c 100644
--- a/packages/git/src/browser/git-view-contribution.ts
+++ b/packages/git/src/browser/git-view-contribution.ts
@@ -80,12 +80,14 @@ export namespace GIT_COMMANDS {
export const OPEN_FILE: Command = {
id: 'git.open.file',
category: 'Git',
- label: 'Open File'
+ label: 'Open File',
+ iconClass: 'theia-open-file-icon'
};
export const OPEN_CHANGES: Command = {
id: 'git.open.changes',
category: 'Git',
- label: 'Open Changes'
+ label: 'Open Changes',
+ iconClass: 'theia-open-change-icon'
};
export const SYNC = {
id: 'git.sync',
@@ -160,7 +162,7 @@ export class GitViewContribution extends AbstractViewContribution
widgetName: 'Git',
defaultWidgetOptions: {
area: 'left',
- rank: 200
+ rank: 300
},
toggleCommandId: 'gitView:toggle',
toggleKeybinding: 'ctrlcmd+shift+g'
@@ -411,13 +413,11 @@ export class GitViewContribution extends AbstractViewContribution
registry.registerItem({
id: GIT_COMMANDS.OPEN_FILE.id,
command: GIT_COMMANDS.OPEN_FILE.id,
- text: '$(file-o)',
tooltip: GIT_COMMANDS.OPEN_FILE.label
});
registry.registerItem({
id: GIT_COMMANDS.OPEN_CHANGES.id,
command: GIT_COMMANDS.OPEN_CHANGES.id,
- text: '$(files-o)',
tooltip: GIT_COMMANDS.OPEN_CHANGES.label
});
}
diff --git a/packages/git/src/browser/git-widget.tsx b/packages/git/src/browser/git-widget.tsx
index 2d38acca26380..f0c13a2d0d168 100644
--- a/packages/git/src/browser/git-widget.tsx
+++ b/packages/git/src/browser/git-widget.tsx
@@ -80,7 +80,8 @@ export class GitWidget extends GitDiffWidget implements StatefulWidget {
this.id = 'theia-gitContainer';
this.title.label = 'Git';
this.title.caption = 'Git';
- this.title.iconClass = 'fa git-tab-icon';
+ this.title.iconClass = 'git-tab-icon';
+ this.title.closable = true;
this.scrollContainer = GitWidget.Styles.CHANGES_CONTAINER;
this.addClass('theia-git');
this.node.tabIndex = 0;
diff --git a/packages/git/src/browser/history/git-history-contribution.ts b/packages/git/src/browser/history/git-history-contribution.ts
index b503c065c299e..0cbbba6b9c697 100644
--- a/packages/git/src/browser/history/git-history-contribution.ts
+++ b/packages/git/src/browser/history/git-history-contribution.ts
@@ -61,7 +61,7 @@ export class GitHistoryContribution extends AbstractViewContribution
this.title.label = GIT_HISTORY_LABEL;
this.title.caption = GIT_HISTORY_LABEL;
this.title.iconClass = 'fa git-history-tab-icon';
+ this.title.closable = true;
this.addClass('theia-git');
this.resetState();
this.cancelIndicator = new CancellationTokenSource();
@@ -186,7 +187,7 @@ export class GitHistoryWidget extends GitNavigableListWidget
if (commit.expanded) {
this.removeFileChangeNodes(commit, id);
} else {
- this.addFileChangeNodes(commit, id);
+ await this.addFileChangeNodes(commit, id);
}
commit.expanded = !commit.expanded;
this.update();
@@ -340,7 +341,6 @@ export class GitHistoryWidget extends GitNavigableListWidget
e => {
if (commit.selected && !this.singleFileMode) {
this.addOrRemoveFileChangeNodes(commit);
- this.update();
} else {
this.selectNode(commit);
}
diff --git a/packages/git/src/browser/style/diff.css b/packages/git/src/browser/style/diff.css
index 59bd6078840fd..ce8e54a95d9de 100644
--- a/packages/git/src/browser/style/diff.css
+++ b/packages/git/src/browser/style/diff.css
@@ -18,6 +18,11 @@
color: var(--theia-ui-font-color1);
}
+.theia-git-diff-icon {
+ -webkit-mask: url('git-diff.svg');
+ mask: url('git-diff.svg');
+}
+
.theia-git .git-diff-container {
display: flex;
flex-direction: column;
@@ -36,7 +41,7 @@
.theia-git .header-row {
display: flex;
- flex-direction: row;
+ flex-direction: row;
}
.theia-git .header-value {
@@ -64,3 +69,8 @@
margin-left: 10px;
cursor: pointer;
}
+
+.theia-git .listContainer.filesChanged {
+ flex: 1;
+ overflow: auto;
+}
diff --git a/packages/git/src/browser/style/git-diff.svg b/packages/git/src/browser/style/git-diff.svg
new file mode 100644
index 0000000000000..262a9b3f00af5
--- /dev/null
+++ b/packages/git/src/browser/style/git-diff.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/packages/git/src/browser/style/git-icons.css b/packages/git/src/browser/style/git-icons.css
index cf2c0224acdbe..c3d6274a1a24c 100644
--- a/packages/git/src/browser/style/git-icons.css
+++ b/packages/git/src/browser/style/git-icons.css
@@ -13,7 +13,7 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
-
+
.icon-git-commit {
mask-repeat: no-repeat;
mask-position: center;
diff --git a/packages/git/src/browser/style/git.svg b/packages/git/src/browser/style/git.svg
new file mode 100644
index 0000000000000..9280ae1dbf89d
--- /dev/null
+++ b/packages/git/src/browser/style/git.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/packages/git/src/browser/style/history.css b/packages/git/src/browser/style/history.css
index f593e6ee8dead..43ace1ef80266 100644
--- a/packages/git/src/browser/style/history.css
+++ b/packages/git/src/browser/style/history.css
@@ -18,11 +18,7 @@
margin: 3px 0;
}
-.theia-git .commitListElement {
- border-top: 1px solid var(--theia-layout-color4);
-}
-
-.theia-git .commitListElement.first {
+.theia-git .commitListElement.first .containerHead{
border: none;
}
@@ -31,6 +27,7 @@
height: 50px;
display: flex;
align-items: center;
+ border-top: 1px solid var(--theia-layout-color4);
}
.theia-git .commitListElement .containerHead:hover {
diff --git a/packages/git/src/browser/style/index.css b/packages/git/src/browser/style/index.css
index 18dfd485bd2f6..287c7a280335b 100644
--- a/packages/git/src/browser/style/index.css
+++ b/packages/git/src/browser/style/index.css
@@ -18,7 +18,10 @@
color: var(--theia-ui-font-color1);
padding: 5px;
box-sizing: border-box;
- background: var(--theia-layout-color0);
+}
+
+.theia-side-panel .theia-git {
+ padding-left: 19px;
}
.theia-git:focus, .theia-git :focus {
@@ -326,8 +329,9 @@
outline: none;
}
-.git-tab-icon::before {
- content: "\f126"
+.git-tab-icon {
+ -webkit-mask: url('git.svg');
+ mask: url('git.svg');
}
.git-change-count {
diff --git a/packages/git/src/electron-node/askpass/askpass.ts b/packages/git/src/electron-node/askpass/askpass.ts
index 7b7715ed896fc..c61d9b6a0e76a 100644
--- a/packages/git/src/electron-node/askpass/askpass.ts
+++ b/packages/git/src/electron-node/askpass/askpass.ts
@@ -14,6 +14,7 @@ import { MaybePromise } from '@theia/core/lib/common/types';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { GitPrompt } from '../../common/git-prompt';
import { DugiteGitPromptServer } from '../../node/dugite-git-prompt';
+import { AddressInfo } from 'net';
/**
* Environment for the Git askpass helper.
@@ -89,7 +90,7 @@ export class Askpass implements Disposable {
return new Promise(resolve => {
this.server.on('error', err => this.logger.error(err));
this.server.listen(0, this.hostname(), () => {
- resolve(this.server.address());
+ resolve(this.server.address() as AddressInfo);
});
});
} catch (err) {
diff --git a/packages/java-debug/package.json b/packages/java-debug/package.json
index ae37fe750f27a..4d06fee68407b 100644
--- a/packages/java-debug/package.json
+++ b/packages/java-debug/package.json
@@ -1,10 +1,10 @@
{
"name": "@theia/java-debug",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Java Debug Extension",
"dependencies": {
- "@theia/debug": "^0.4.0",
- "@theia/java": "^0.4.0",
+ "@theia/debug": "^0.5.0",
+ "@theia/java": "^0.5.0",
"lodash": "^4.17.10"
},
"publishConfig": {
@@ -41,11 +41,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/java/package.json b/packages/java/package.json
index 47c12277faee6..3f4c9d5f01be7 100644
--- a/packages/java/package.json
+++ b/packages/java/package.json
@@ -1,12 +1,12 @@
{
"name": "@theia/java",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Java Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/languages": "^0.4.0",
- "@theia/monaco": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/languages": "^0.5.0",
+ "@theia/monaco": "^0.5.0",
"@types/glob": "^5.0.30",
"@types/tar": "4.0.0",
"glob": "^7.1.2",
@@ -15,7 +15,7 @@
"tar": "^4.0.0"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"scripts": {
"prepare": "yarn run clean && yarn run build",
@@ -24,7 +24,6 @@
"build": "theiaext build",
"watch": "theiaext watch",
"test": "theiaext test",
- "docs": "theiaext docs",
"dev-server": "node ./scripts/get-dev-server.js"
},
"publishConfig": {
diff --git a/packages/java/src/browser/java-protocol.ts b/packages/java/src/browser/java-protocol.ts
index 8e1ec36436cd7..9e332b10d8333 100644
--- a/packages/java/src/browser/java-protocol.ts
+++ b/packages/java/src/browser/java-protocol.ts
@@ -15,7 +15,7 @@
********************************************************************************/
import { RequestType, NotificationType } from 'vscode-jsonrpc';
-import { VersionedTextDocumentIdentifier, TextDocumentIdentifier, Command, MessageType, ExecuteCommandParams } from '@theia/languages/lib/browser';
+import { TextDocumentIdentifier, Command, MessageType, ExecuteCommandParams } from '@theia/languages/lib/browser';
export interface StatusReport {
message: string;
@@ -34,16 +34,6 @@ export interface ActionableMessage {
commands?: Command[];
}
-export interface SemanticHighlightingParams {
- readonly textDocument: VersionedTextDocumentIdentifier;
- readonly lines: SemanticHighlightingInformation[];
-}
-
-export interface SemanticHighlightingInformation {
- readonly line: number;
- readonly tokens: string | undefined;
-}
-
export namespace ClassFileContentsRequest {
export const type = new RequestType('java/classFileContents');
}
@@ -52,10 +42,6 @@ export namespace ActionableNotification {
export const type = new NotificationType('language/actionableNotification');
}
-export namespace SemanticHighlight {
- export const type = new NotificationType('textDocument/semanticHighlighting');
-}
-
export enum CompileWorkspaceStatus {
FAILED = 0,
SUCCEED = 1,
diff --git a/packages/java/src/node/java-contribution.ts b/packages/java/src/node/java-contribution.ts
index 6b28e14c2563f..f78ea079389dc 100644
--- a/packages/java/src/node/java-contribution.ts
+++ b/packages/java/src/node/java-contribution.ts
@@ -28,6 +28,7 @@ import { JAVA_LANGUAGE_ID, JAVA_LANGUAGE_NAME, JavaStartParams } from '../common
import { JavaCliContribution } from './java-cli-contribution';
import { ContributionProvider } from '@theia/core';
import { JavaExtensionContribution } from './java-extension-model';
+import { AddressInfo } from 'net';
const sha1 = require('sha1');
export type ConfigurationType = 'config_win' | 'config_mac' | 'config_linux';
@@ -137,8 +138,8 @@ export class JavaContribution extends BaseLanguageServerContribution {
this.logInfo('logs at ' + path.resolve(workspacePath, '.metadata', '.log'));
const env = Object.create(process.env);
const address = server.address();
- env.CLIENT_HOST = address.address;
- env.CLIENT_PORT = address.port;
+ env.CLIENT_HOST = (address as AddressInfo).address;
+ env.CLIENT_PORT = (address as AddressInfo).port;
const serverConnection = await this.createProcessSocketConnection(socket, socket, command, args, { env });
this.forward(clientConnection, serverConnection);
}
diff --git a/packages/json/package.json b/packages/json/package.json
index 4a73440b3853a..d137e2dd4a310 100644
--- a/packages/json/package.json
+++ b/packages/json/package.json
@@ -1,23 +1,22 @@
{
"name": "@theia/json",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - JSON Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/languages": "^0.4.0",
- "@theia/monaco": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/languages": "^0.5.0",
+ "@theia/monaco": "^0.5.0",
"vscode-json-languageserver": "^1.0.1"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"scripts": {
"prepare": "yarn run clean && yarn run build",
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"publishConfig": {
"access": "public"
diff --git a/packages/json/src/browser/json-client-contribution.ts b/packages/json/src/browser/json-client-contribution.ts
index 7e580d9ad4d15..782afef264aa3 100644
--- a/packages/json/src/browser/json-client-contribution.ts
+++ b/packages/json/src/browser/json-client-contribution.ts
@@ -67,7 +67,7 @@ export class JsonClientContribution extends BaseLanguageClientContribution {
for (const s of allConfigs) {
if (s.fileMatch) {
for (let fileMatch of s.fileMatch) {
- if (fileMatch.charAt(0) !== '/' && !fileMatch.match(/\w+:\/\//)) {
+ if (fileMatch.charAt(0) !== '/' && !fileMatch.match(/\w+:/)) {
fileMatch = '/' + fileMatch;
}
registry[fileMatch] = [s.url];
diff --git a/packages/keymaps/package.json b/packages/keymaps/package.json
index f5b7221369cc3..ed0e7938276be 100644
--- a/packages/keymaps/package.json
+++ b/packages/keymaps/package.json
@@ -1,12 +1,12 @@
{
"name": "@theia/keymaps",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Custom Keymaps Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/monaco": "^0.4.0",
- "@theia/userstorage": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/monaco": "^0.5.0",
+ "@theia/userstorage": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"@types/lodash.debounce": "4.0.3",
"ajv": "^6.5.3",
"fuzzy": "^0.1.3",
@@ -14,7 +14,7 @@
"lodash.debounce": "^4.0.8"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0",
+ "@theia/ext-scripts": "^0.5.0",
"@types/temp": "^0.8.29",
"temp": "^0.8.3"
},
@@ -47,8 +47,7 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/keymaps/src/browser/keybindings-widget.tsx b/packages/keymaps/src/browser/keybindings-widget.tsx
index 52f94d3a33c12..920a0e4820e5a 100644
--- a/packages/keymaps/src/browser/keybindings-widget.tsx
+++ b/packages/keymaps/src/browser/keybindings-widget.tsx
@@ -61,7 +61,7 @@ export class KeybindingWidget extends ReactWidget {
protected query: string = '';
protected readonly regexp = /(.*?)<\/match>/g;
- protected readonly keybindingSeperator = /\+<\/match>/g;
+ protected readonly keybindingSeparator = /\+<\/match>/g;
protected readonly fuzzyOptions = {
pre: '',
@@ -131,7 +131,7 @@ export class KeybindingWidget extends ReactWidget {
}
protected render(): React.ReactNode {
- return
+ return
{this.renderSearch()}
{(this.items.length > 0) ? this.renderTable() : this.renderMessage()}
;
@@ -158,7 +158,7 @@ export class KeybindingWidget extends ReactWidget {
}
protected renderTable(): React.ReactNode {
- return
+ return
@@ -214,7 +214,7 @@ export class KeybindingWidget extends ReactWidget {
}
protected renderKeybinding(keybinding: string): React.ReactNode {
- const regex = new RegExp(this.keybindingSeperator);
+ const regex = new RegExp(this.keybindingSeparator);
keybinding = keybinding.replace(regex, '+');
const keys = keybinding.split('+');
@@ -227,7 +227,7 @@ export class KeybindingWidget extends ReactWidget {
;
} else {
return
- +
+ +
{this.renderKeybinding(key)}
;
}
diff --git a/packages/keymaps/src/browser/style/index.css b/packages/keymaps/src/browser/style/index.css
index 5add1fd81ad76..49bb35764b3e8 100644
--- a/packages/keymaps/src/browser/style/index.css
+++ b/packages/keymaps/src/browser/style/index.css
@@ -14,6 +14,17 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
+#kb-main-container {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+#kb-table-container {
+ flex: 1;
+ overflow: auto;
+}
+
.fuzzy-match {
font-weight: 600;
color: var(--theia-brand-color1);
@@ -104,6 +115,9 @@
padding-top: 5px;
text-align: left;
vertical-align: middle;
+ position: sticky;
+ top: 0;
+ background-color: var(--theia-layout-color2);
}
.kb table .th-action {
diff --git a/packages/languages/package.json b/packages/languages/package.json
index b076296c7d31c..4b4617824a35b 100644
--- a/packages/languages/package.json
+++ b/packages/languages/package.json
@@ -1,12 +1,12 @@
{
"name": "@theia/languages",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Languages Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/output": "^0.4.0",
- "@theia/process": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/output": "^0.5.0",
+ "@theia/process": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"@typefox/monaco-editor-core": "^0.14.6",
"@types/uuid": "^3.4.3",
"monaco-languageclient": "^0.9.0",
@@ -42,11 +42,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/markers/package.json b/packages/markers/package.json
index 48c2b702aedbf..67208021e2e8b 100644
--- a/packages/markers/package.json
+++ b/packages/markers/package.json
@@ -1,12 +1,12 @@
{
"name": "@theia/markers",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Markers Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/navigator": "^0.4.0",
- "@theia/workspace": "^0.4.0"
+ "@theia/core": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/navigator": "^0.5.0",
+ "@theia/workspace": "^0.5.0"
},
"publishConfig": {
"access": "public"
@@ -37,11 +37,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/markers/src/browser/style/index.css b/packages/markers/src/browser/style/index.css
index 3a2a5d243f9ac..0f4755d127df4 100644
--- a/packages/markers/src/browser/style/index.css
+++ b/packages/markers/src/browser/style/index.css
@@ -23,6 +23,10 @@
padding: 5px;
}
+.theia-side-panel .theia-marker-container .noMarkers {
+ padding-left: 19px;
+}
+
.theia-marker-container .markerNode,
.theia-marker-container .markerFileNode {
display: flex;
diff --git a/packages/merge-conflicts/package.json b/packages/merge-conflicts/package.json
index 1f20dde7ce58c..8c7e590025fe8 100644
--- a/packages/merge-conflicts/package.json
+++ b/packages/merge-conflicts/package.json
@@ -1,11 +1,11 @@
{
"name": "@theia/merge-conflicts",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Merge Conflicts Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/languages": "^0.4.0"
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/languages": "^0.5.0"
},
"publishConfig": {
"access": "public"
@@ -36,11 +36,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/messages/package.json b/packages/messages/package.json
index 745b4ebe4bac8..2c21aff47ad75 100644
--- a/packages/messages/package.json
+++ b/packages/messages/package.json
@@ -1,9 +1,9 @@
{
"name": "@theia/messages",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Messages Extension",
"dependencies": {
- "@theia/core": "^0.4.0"
+ "@theia/core": "^0.5.0"
},
"publishConfig": {
"access": "public"
@@ -34,11 +34,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/metrics/package.json b/packages/metrics/package.json
index 0b0f5c347cfb8..6152090ce3085 100644
--- a/packages/metrics/package.json
+++ b/packages/metrics/package.json
@@ -1,10 +1,10 @@
{
"name": "@theia/metrics",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Metrics Extension",
"dependencies": {
- "@theia/application-package": "^0.4.0",
- "@theia/core": "^0.4.0",
+ "@theia/application-package": "^0.5.0",
+ "@theia/core": "^0.5.0",
"prom-client": "^10.2.0"
},
"publishConfig": {
@@ -36,11 +36,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/mini-browser/package.json b/packages/mini-browser/package.json
index 005f35e6d4805..8e7e6f86d73e9 100644
--- a/packages/mini-browser/package.json
+++ b/packages/mini-browser/package.json
@@ -1,10 +1,10 @@
{
"name": "@theia/mini-browser",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Mini-Browser Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
"@types/fs-extra": "^4.0.2",
"@types/mime-types": "^2.1.0",
"fs-extra": "^4.0.2",
@@ -41,11 +41,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/mini-browser/src/browser/mini-browser-frontend-module.ts b/packages/mini-browser/src/browser/mini-browser-frontend-module.ts
index 06ad9e86d7cc2..5defe33ef6d1d 100644
--- a/packages/mini-browser/src/browser/mini-browser-frontend-module.ts
+++ b/packages/mini-browser/src/browser/mini-browser-frontend-module.ts
@@ -14,6 +14,8 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
+import '../../src/browser/style/index.css';
+
import { ContainerModule } from 'inversify';
import URI from '@theia/core/lib/common/uri';
import { OpenHandler } from '@theia/core/lib/browser/opener-service';
@@ -38,8 +40,6 @@ import {
LocationWithoutSchemeMapper,
} from './location-mapper-service';
-import '../../src/browser/style/index.css';
-
export default new ContainerModule(bind => {
bind(MiniBrowserMouseClickTracker).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(MiniBrowserMouseClickTracker);
diff --git a/packages/mini-browser/src/browser/mini-browser-open-handler.ts b/packages/mini-browser/src/browser/mini-browser-open-handler.ts
index 5b8d01233f63f..9eea089d06bc2 100644
--- a/packages/mini-browser/src/browser/mini-browser-open-handler.ts
+++ b/packages/mini-browser/src/browser/mini-browser-open-handler.ts
@@ -35,10 +35,12 @@ import { LocationMapperService } from './location-mapper-service';
export namespace MiniBrowserCommands {
export const PREVIEW: Command = {
id: 'mini-browser.preview',
- label: 'Open Preview'
+ label: 'Open Preview',
+ iconClass: 'theia-open-preview-icon'
};
export const OPEN_SOURCE: Command = {
- id: 'mini-browser.open.source'
+ id: 'mini-browser.open.source',
+ iconClass: 'theia-open-file-icon'
};
export const OPEN_URL: Command = {
id: 'mini-browser.openUrl',
@@ -191,13 +193,11 @@ export class MiniBrowserOpenHandler extends NavigatableWidgetOpenHandler
+
+
+
+
+ icon
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/monaco/package.json b/packages/monaco/package.json
index ede5e5733e2a0..668406f126656 100644
--- a/packages/monaco/package.json
+++ b/packages/monaco/package.json
@@ -1,15 +1,15 @@
{
"name": "@theia/monaco",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Monaco Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/languages": "^0.4.0",
- "@theia/markers": "^0.4.0",
- "@theia/outline-view": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/languages": "^0.5.0",
+ "@theia/markers": "^0.5.0",
+ "@theia/outline-view": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"deepmerge": "2.0.1",
"jsonc-parser": "^2.0.2",
"monaco-css": "^2.0.1",
@@ -48,11 +48,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/monaco/src/browser/monaco-editor-model.ts b/packages/monaco/src/browser/monaco-editor-model.ts
index d4bdfbd7247a9..e1fb8c789a058 100644
--- a/packages/monaco/src/browser/monaco-editor-model.ts
+++ b/packages/monaco/src/browser/monaco-editor-model.ts
@@ -19,6 +19,7 @@ import { MonacoToProtocolConverter, ProtocolToMonacoConverter } from 'monaco-lan
import { TextEditorDocument } from '@theia/editor/lib/browser';
import { DisposableCollection, Disposable, Emitter, Event, Resource, CancellationTokenSource, CancellationToken, ResourceError } from '@theia/core';
import ITextEditorModel = monaco.editor.ITextEditorModel;
+import { Range } from 'vscode-languageserver-types';
export {
TextDocumentSaveReason
@@ -124,8 +125,15 @@ export class MonacoEditorModel implements ITextEditorModel, TextEditorDocument {
return this.model.getVersionId();
}
- getText(): string {
- return this.model.getValue();
+ /**
+ * Return selected text by Range or all text by default
+ */
+ getText(range?: Range): string {
+ if (!range) {
+ return this.model.getValue();
+ } else {
+ return this.model.getValueInRange(this.p2m.asRange(range));
+ }
}
positionAt(offset: number): Position {
@@ -141,6 +149,9 @@ export class MonacoEditorModel implements ITextEditorModel, TextEditorDocument {
return this.model.getLineCount();
}
+ /**
+ * Retrieves a line in a text document expressed as a one-based position.
+ */
getLineContent(lineNumber: number): string {
return this.model.getLineContent(lineNumber);
}
diff --git a/packages/monaco/src/browser/monaco-editor-service.ts b/packages/monaco/src/browser/monaco-editor-service.ts
index 86b0f2eee40d1..000f0c10b9f22 100644
--- a/packages/monaco/src/browser/monaco-editor-service.ts
+++ b/packages/monaco/src/browser/monaco-editor-service.ts
@@ -17,7 +17,7 @@
import { injectable, inject, decorate } from 'inversify';
import { MonacoToProtocolConverter } from 'monaco-languageclient';
import URI from '@theia/core/lib/common/uri';
-import { OpenerService, open, WidgetOpenMode, ApplicationShell } from '@theia/core/lib/browser';
+import { OpenerService, open, WidgetOpenMode, ApplicationShell, PreferenceService } from '@theia/core/lib/browser';
import { EditorWidget, EditorOpenerOptions, EditorManager } from '@theia/editor/lib/browser';
import { MonacoEditor } from './monaco-editor';
@@ -30,6 +30,8 @@ decorate(injectable(), monaco.services.CodeEditorServiceImpl);
@injectable()
export class MonacoEditorService extends monaco.services.CodeEditorServiceImpl {
+ public static readonly ENABLE_PREVIEW_PREFERENCE: string = 'editor.enablePreview';
+
@inject(OpenerService)
protected readonly openerService: OpenerService;
@@ -42,6 +44,9 @@ export class MonacoEditorService extends monaco.services.CodeEditorServiceImpl {
@inject(EditorManager)
protected readonly editors: EditorManager;
+ @inject(PreferenceService)
+ protected readonly preferencesService: PreferenceService;
+
constructor() {
super(monaco.services.StaticServices.standaloneThemeService.get());
}
@@ -66,7 +71,8 @@ export class MonacoEditorService extends monaco.services.CodeEditorServiceImpl {
const mode = this.getEditorOpenMode(input);
const selection = input.options && this.m2p.asRange(input.options.selection);
const widgetOptions = this.getWidgetOptions(source, sideBySide);
- return { mode, selection, widgetOptions };
+ const preview = !!this.preferencesService.get(MonacoEditorService.ENABLE_PREVIEW_PREFERENCE, false);
+ return { mode, selection, widgetOptions, preview };
}
protected getEditorOpenMode(input: IResourceInput): WidgetOpenMode {
const options = {
diff --git a/packages/monaco/src/browser/monaco-quick-open-service.ts b/packages/monaco/src/browser/monaco-quick-open-service.ts
index 6fc9d0e0ad530..934f4c79a84e9 100644
--- a/packages/monaco/src/browser/monaco-quick-open-service.ts
+++ b/packages/monaco/src/browser/monaco-quick-open-service.ts
@@ -17,8 +17,8 @@
import { injectable, inject, postConstruct } from 'inversify';
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
import {
- QuickOpenService, QuickOpenModel, QuickOpenOptions,
- QuickOpenItem, QuickOpenGroupItem, QuickOpenMode, KeySequence
+ QuickOpenService, QuickOpenModel, QuickOpenOptions, QuickOpenItem,
+ QuickOpenGroupItem, QuickOpenMode, KeySequence, QuickOpenActionProvider, QuickOpenAction
} from '@theia/core/lib/browser';
import { KEY_CODE_MAP } from './monaco-keycode-map';
import { ContextKey } from '@theia/core/lib/browser/context-key-service';
@@ -215,7 +215,7 @@ export class MonacoQuickOpenControllerOptsImpl implements MonacoQuickOpenControl
this.options.onClose(cancelled);
}
- private toOpenModel(lookFor: string, items: QuickOpenItem[]): monaco.quickOpen.QuickOpenModel {
+ private toOpenModel(lookFor: string, items: QuickOpenItem[], actionProvider?: QuickOpenActionProvider): monaco.quickOpen.QuickOpenModel {
const entries: monaco.quickOpen.QuickOpenEntry[] = [];
for (const item of items) {
const entry = this.createEntry(item, lookFor);
@@ -226,7 +226,7 @@ export class MonacoQuickOpenControllerOptsImpl implements MonacoQuickOpenControl
if (this.options.fuzzySort) {
entries.sort((a, b) => monaco.quickOpen.compareEntries(a, b, lookFor));
}
- return new monaco.quickOpen.QuickOpenModel(entries);
+ return new monaco.quickOpen.QuickOpenModel(entries, actionProvider ? new MonacoQuickOpenActionProvider(actionProvider) : undefined);
}
getModel(lookFor: string): monaco.quickOpen.QuickOpenModel {
@@ -234,8 +234,8 @@ export class MonacoQuickOpenControllerOptsImpl implements MonacoQuickOpenControl
}
onType(lookFor: string, acceptor: (model: monaco.quickOpen.QuickOpenModel) => void): void {
- this.model.onType(lookFor, items => {
- const result = this.toOpenModel(lookFor, items);
+ this.model.onType(lookFor, (items, actionProvider) => {
+ const result = this.toOpenModel(lookFor, items, actionProvider);
acceptor(result);
});
}
@@ -408,3 +408,72 @@ export class QuickOpenEntryGroup extends monaco.quickOpen.QuickOpenEntryGroup {
}
}
+
+export class MonacoQuickOpenAction implements monaco.quickOpen.IAction {
+ constructor(public readonly action: QuickOpenAction) { }
+
+ get id(): string {
+ return this.action.id;
+ }
+
+ get label(): string {
+ return this.action.label || '';
+ }
+
+ get tooltip(): string {
+ return this.action.tooltip || '';
+ }
+
+ get class(): string | undefined {
+ return this.action.class;
+ }
+
+ get enabled(): boolean {
+ return this.action.enabled || true;
+ }
+
+ get checked(): boolean {
+ return this.action.checked || false;
+ }
+
+ get radio(): boolean {
+ return this.action.radio || false;
+ }
+
+ // tslint:disable-next-line:no-any
+ run(entry: QuickOpenEntry | QuickOpenEntryGroup): PromiseLike {
+ return this.action.run(entry.item);
+ }
+
+ dispose(): void {
+ this.action.dispose();
+ }
+}
+
+export class MonacoQuickOpenActionProvider implements monaco.quickOpen.IActionProvider {
+ constructor(public readonly provider: QuickOpenActionProvider) { }
+
+ // tslint:disable-next-line:no-any
+ hasActions(element: any, entry: QuickOpenEntry | QuickOpenEntryGroup): boolean {
+ return this.provider.hasActions(entry.item);
+ }
+
+ // tslint:disable-next-line:no-any
+ async getActions(element: any, entry: QuickOpenEntry | QuickOpenEntryGroup): monaco.Promise {
+ const actions = await this.provider.getActions(entry.item);
+ const monacoActions = actions.map(action => new MonacoQuickOpenAction(action));
+ return monaco.Promise.wrap(monacoActions);
+ }
+
+ hasSecondaryActions(): boolean {
+ return false;
+ }
+
+ getSecondaryActions(): monaco.Promise {
+ return monaco.Promise.wrap([]);
+ }
+
+ getActionItem() {
+ return undefined;
+ }
+}
diff --git a/packages/monaco/src/typings/monaco/index.d.ts b/packages/monaco/src/typings/monaco/index.d.ts
index bb24439df9800..46985f2bf6565 100644
--- a/packages/monaco/src/typings/monaco/index.d.ts
+++ b/packages/monaco/src/typings/monaco/index.d.ts
@@ -743,8 +743,28 @@ declare module monaco.quickOpen {
setShowBorder(showBorder: boolean): void;
getEntry(): QuickOpenEntry | undefined;
}
+
+ export interface IAction extends IDisposable {
+ id: string;
+ label: string;
+ tooltip: string;
+ class: string | undefined;
+ enabled: boolean;
+ checked: boolean;
+ radio: boolean;
+ run(event?: any): PromiseLike;
+ }
+
+ export interface IActionProvider {
+ hasActions(element: any, item: any): boolean;
+ getActions(element: any, item: any): monaco.Promise;
+ hasSecondaryActions(element: any, item: any): boolean;
+ getSecondaryActions(element: any, item: any): monaco.Promise;
+ getActionItem(element: any, item: any, action: IAction): any;
+ }
+
export class QuickOpenModel implements IModel, IDataSource, IFilter, IRunner {
- constructor(entries?: QuickOpenEntry[] /*, actionProvider?: IActionProvider */);
+ constructor(entries?: QuickOpenEntry[], actionProvider?: IActionProvider);
addEntries(entries: QuickOpenEntry[]): void;
entries: QuickOpenEntry[];
dataSource: IDataSource;
diff --git a/packages/navigator/package.json b/packages/navigator/package.json
index 7b02c6a56c036..6be2af06b07af 100644
--- a/packages/navigator/package.json
+++ b/packages/navigator/package.json
@@ -1,11 +1,11 @@
{
"name": "@theia/navigator",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Navigator Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"fuzzy": "^0.1.3",
"minimatch": "^3.0.4"
},
@@ -38,11 +38,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/navigator/src/browser/navigator-container.ts b/packages/navigator/src/browser/navigator-container.ts
index 84e42b62aa471..022cb691f01c9 100644
--- a/packages/navigator/src/browser/navigator-container.ts
+++ b/packages/navigator/src/browser/navigator-container.ts
@@ -28,7 +28,8 @@ export const FILE_NAVIGATOR_PROPS = {
...defaultTreeProps,
contextMenuPath: NAVIGATOR_CONTEXT_MENU,
multiSelect: true,
- search: true
+ search: true,
+ globalSelection: true
};
export function createFileNavigatorContainer(parent: interfaces.Container): Container {
diff --git a/packages/navigator/src/browser/navigator-contribution.ts b/packages/navigator/src/browser/navigator-contribution.ts
index 5d3a5a6c70378..6dbbd41ca1b22 100644
--- a/packages/navigator/src/browser/navigator-contribution.ts
+++ b/packages/navigator/src/browser/navigator-contribution.ts
@@ -30,6 +30,7 @@ import { NavigatorKeybindingContexts } from './navigator-keybinding-context';
import { FileNavigatorFilter } from './navigator-filter';
import { WorkspaceNode } from './navigator-tree';
import { NavigatorContextKeyService } from './navigator-context-key-service';
+import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
export namespace FileNavigatorCommands {
export const REVEAL_IN_NAVIGATOR: Command = {
@@ -41,7 +42,8 @@ export namespace FileNavigatorCommands {
label: 'Toggle Hidden Files'
};
export const COLLAPSE_ALL: Command = {
- id: 'navigator.collapse.all'
+ id: 'navigator.collapse.all',
+ iconClass: 'collapse-all'
};
}
@@ -77,7 +79,7 @@ export namespace NavigatorContextMenu {
}
@injectable()
-export class FileNavigatorContribution extends AbstractViewContribution implements FrontendApplicationContribution {
+export class FileNavigatorContribution extends AbstractViewContribution implements FrontendApplicationContribution, TabBarToolbarContribution {
@inject(NavigatorContextKeyService)
protected readonly contextKeyService: NavigatorContextKeyService;
@@ -91,7 +93,7 @@ export class FileNavigatorContribution extends AbstractViewContribution {
@@ -135,12 +136,19 @@ export class FileNavigatorContribution extends AbstractViewContribution true
});
registry.registerCommand(FileNavigatorCommands.COLLAPSE_ALL, {
- execute: () => this.collapseFileNavigatorTree(),
- isEnabled: () => this.workspaceService.opened,
- isVisible: () => this.workspaceService.opened
+ execute: widget => this.withWidget(widget, () => this.collapseFileNavigatorTree()),
+ isEnabled: widget => this.withWidget(widget, () => this.workspaceService.opened),
+ isVisible: wodget => this.withWidget(wodget, () => this.workspaceService.opened)
});
}
+ protected withWidget(widget: Widget | undefined = this.tryGetWidget(), cb: (navigator: FileNavigatorWidget) => T): T | false {
+ if (widget instanceof FileNavigatorWidget && widget.id === FILE_NAVIGATOR_ID) {
+ return cb(widget);
+ }
+ return false;
+ }
+
registerMenus(registry: MenuModelRegistry): void {
super.registerMenus(registry);
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_MENU, {
@@ -184,10 +192,15 @@ export class FileNavigatorContribution extends AbstractViewContribution {
+ toolbarRegistry.registerItem({
+ id: FileNavigatorCommands.COLLAPSE_ALL.id,
+ command: FileNavigatorCommands.COLLAPSE_ALL.id,
+ tooltip: 'Collapse All',
+ priority: 0,
+ });
+ }
+
/**
* Reveals and selects node in the file navigator to which given widget is related.
* Does nothing if given widget undefined or doesn't have related resource.
diff --git a/packages/navigator/src/browser/navigator-filter.ts b/packages/navigator/src/browser/navigator-filter.ts
index e3f4b0853357a..f9030af166a46 100644
--- a/packages/navigator/src/browser/navigator-filter.ts
+++ b/packages/navigator/src/browser/navigator-filter.ts
@@ -22,8 +22,6 @@ import { PreferenceChangeEvent } from '@theia/core/lib/browser/preferences';
import { FileSystemPreferences, FileSystemConfiguration } from '@theia/filesystem/lib/browser/filesystem-preferences';
import { FileNavigatorPreferences, FileNavigatorConfiguration } from './navigator-preferences';
-const FILES_EXCLUDE_PREFERENCE: keyof FileSystemConfiguration = 'files.exclude';
-
/**
* Filter for omitting elements from the navigator. For more details on the exclusion patterns,
* one should check either the manual with `man 5 gitignore` or just [here](https://git-scm.com/docs/gitignore).
@@ -44,7 +42,7 @@ export class FileNavigatorFilter {
@postConstruct()
protected async init(): Promise {
- this.filterPredicate = this.createFilterPredicate(this.filesPreferences[FILES_EXCLUDE_PREFERENCE]);
+ this.filterPredicate = this.createFilterPredicate(this.filesPreferences['files.exclude']);
this.filesPreferences.onPreferenceChanged(event => this.onFilesPreferenceChanged(event));
this.preferences.onPreferenceChanged(event => this.onPreferenceChanged(event));
}
@@ -67,7 +65,7 @@ export class FileNavigatorFilter {
protected onFilesPreferenceChanged(event: PreferenceChangeEvent): void {
const { preferenceName, newValue } = event;
- if (preferenceName === FILES_EXCLUDE_PREFERENCE) {
+ if (preferenceName === 'files.exclude') {
this.filterPredicate = this.createFilterPredicate(newValue as FileNavigatorFilter.Exclusions | undefined || {});
this.fireFilterChanged();
}
@@ -82,7 +80,7 @@ export class FileNavigatorFilter {
toggleHiddenFiles(): void {
this.showHiddenFiles = !this.showHiddenFiles;
- const filesExcludes = this.filesPreferences[FILES_EXCLUDE_PREFERENCE];
+ const filesExcludes = this.filesPreferences['files.exclude'];
this.filterPredicate = this.createFilterPredicate(filesExcludes || {});
this.fireFilterChanged();
diff --git a/packages/navigator/src/browser/navigator-frontend-module.ts b/packages/navigator/src/browser/navigator-frontend-module.ts
index c045effc11609..b85fdceb89957 100644
--- a/packages/navigator/src/browser/navigator-frontend-module.ts
+++ b/packages/navigator/src/browser/navigator-frontend-module.ts
@@ -26,6 +26,7 @@ import { WidgetFactory } from '@theia/core/lib/browser/widget-manager';
import { bindFileNavigatorPreferences } from './navigator-preferences';
import { FileNavigatorFilter } from './navigator-filter';
import { NavigatorContextKeyService } from './navigator-context-key-service';
+import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
export default new ContainerModule(bind => {
bindFileNavigatorPreferences(bind);
@@ -35,6 +36,7 @@ export default new ContainerModule(bind => {
bindViewContribution(bind, FileNavigatorContribution);
bind(FrontendApplicationContribution).toService(FileNavigatorContribution);
+ bind(TabBarToolbarContribution).toService(FileNavigatorContribution);
bind(KeybindingContext).to(NavigatorActiveContext).inSingletonScope();
diff --git a/packages/navigator/src/browser/navigator-model.spec.ts b/packages/navigator/src/browser/navigator-model.spec.ts
index 582fa9dc1b873..741fa6532344d 100644
--- a/packages/navigator/src/browser/navigator-model.spec.ts
+++ b/packages/navigator/src/browser/navigator-model.spec.ts
@@ -129,6 +129,7 @@ describe('FileNavigatorModel', () => {
let mockPreferences: CorePreferences;
const mockWorkspaceServiceEmitter: Emitter = new Emitter();
+ const mockWorkspaceOnLocationChangeEmitter: Emitter = new Emitter();
const mockFileChangeEmitter: Emitter = new Emitter();
const mockFileMoveEmitter: Emitter = new Emitter();
const mockTreeChangeEmitter: Emitter = new Emitter();
@@ -175,6 +176,7 @@ describe('FileNavigatorModel', () => {
testContainer.bind(CorePreferences).toConstantValue(mockPreferences);
sinon.stub(mockWorkspaceService, 'onWorkspaceChanged').value(mockWorkspaceServiceEmitter.event);
+ sinon.stub(mockWorkspaceService, 'onWorkspaceLocationChanged').value(mockWorkspaceOnLocationChangeEmitter.event);
sinon.stub(mockFileSystemWatcher, 'onFilesChanged').value(mockFileChangeEmitter.event);
sinon.stub(mockFileSystemWatcher, 'onDidMove').value(mockFileMoveEmitter.event);
sinon.stub(mockFileNavigatorTree, 'onChanged').value(mockTreeChangeEmitter.event);
diff --git a/packages/navigator/src/browser/navigator-model.ts b/packages/navigator/src/browser/navigator-model.ts
index 1d1252aa71869..4f9b6c00b5bd4 100644
--- a/packages/navigator/src/browser/navigator-model.ts
+++ b/packages/navigator/src/browser/navigator-model.ts
@@ -35,6 +35,11 @@ export class FileNavigatorModel extends FileTreeModel {
this.updateRoot();
})
);
+ this.toDispose.push(
+ this.workspaceService.onWorkspaceLocationChanged(() => {
+ this.updateRoot();
+ })
+ );
super.init();
}
@@ -71,7 +76,11 @@ export class FileNavigatorModel extends FileTreeModel {
protected async createRoot(): Promise {
if (this.workspaceService.opened) {
- const workspaceNode = WorkspaceNode.createRoot();
+ const stat = this.workspaceService.workspace;
+ const isMulti = (stat) ? !stat.isDirectory : false;
+ const workspaceNode = isMulti
+ ? this.createMultipleRootNode()
+ : WorkspaceNode.createRoot();
const roots = await this.workspaceService.roots;
for (const root of roots) {
workspaceNode.children.push(
@@ -82,6 +91,20 @@ export class FileNavigatorModel extends FileTreeModel {
}
}
+ /**
+ * Create multiple root node used to display
+ * the multiple root workspace name.
+ *
+ * @returns `WorkspaceNode`
+ */
+ protected createMultipleRootNode(): WorkspaceNode {
+ const workspace = this.workspaceService.workspace;
+ const name = (workspace)
+ ? new URI(workspace.uri).path.name
+ : 'untitled';
+ return WorkspaceNode.createRoot(name);
+ }
+
/**
* Move the given source file or directory to the given target directory.
*/
diff --git a/packages/navigator/src/browser/navigator-tree.ts b/packages/navigator/src/browser/navigator-tree.ts
index 28fc0ecb46764..5d8968d9f513e 100644
--- a/packages/navigator/src/browser/navigator-tree.ts
+++ b/packages/navigator/src/browser/navigator-tree.ts
@@ -63,16 +63,16 @@ export namespace WorkspaceNode {
export const name = 'WorkspaceNode';
export function is(node: TreeNode | undefined): node is WorkspaceNode {
- return CompositeTreeNode.is(node) && node.name === WorkspaceNode.name;
+ return CompositeTreeNode.is(node) && node.id === WorkspaceNode.id;
}
- export function createRoot(): WorkspaceNode {
+ export function createRoot(multiRootName?: string): WorkspaceNode {
return {
id: WorkspaceNode.id,
- name: WorkspaceNode.name,
+ name: multiRootName || WorkspaceNode.name,
parent: undefined,
children: [],
- visible: false,
+ visible: !!multiRootName,
selected: false
};
}
diff --git a/packages/navigator/src/browser/navigator-widget.tsx b/packages/navigator/src/browser/navigator-widget.tsx
index 4f3f8f824c230..e1d341686e717 100644
--- a/packages/navigator/src/browser/navigator-widget.tsx
+++ b/packages/navigator/src/browser/navigator-widget.tsx
@@ -17,7 +17,7 @@
import { injectable, inject, postConstruct } from 'inversify';
import { Message } from '@phosphor/messaging';
import URI from '@theia/core/lib/common/uri';
-import { CommandService, SelectionService, Disposable } from '@theia/core/lib/common';
+import { CommandService, SelectionService } from '@theia/core/lib/common';
import { CommonCommands, CorePreferences } from '@theia/core/lib/browser';
import {
ContextMenuRenderer, ExpandableTreeNode,
@@ -35,7 +35,7 @@ import * as React from 'react';
import { NavigatorContextKeyService } from './navigator-context-key-service';
export const FILE_NAVIGATOR_ID = 'files';
-export const LABEL = 'Files';
+export const LABEL = 'Explorer';
export const CLASS = 'theia-Files';
@injectable()
@@ -60,7 +60,8 @@ export class FileNavigatorWidget extends FileTreeWidget {
this.id = FILE_NAVIGATOR_ID;
this.title.label = LABEL;
this.title.caption = LABEL;
- this.title.iconClass = 'fa navigator-tab-icon';
+ this.title.closable = true;
+ this.title.iconClass = 'navigator-tab-icon';
this.addClass(CLASS);
this.initialize();
}
@@ -70,12 +71,9 @@ export class FileNavigatorWidget extends FileTreeWidget {
super.init();
this.updateSelectionContextKeys();
this.toDispose.pushAll([
- this.model.onSelectionChanged(selection => {
- if (this.shell.activeWidget === this) {
- this.selectionService.selection = selection;
- }
- this.updateSelectionContextKeys();
- }),
+ this.model.onSelectionChanged(() =>
+ this.updateSelectionContextKeys()
+ ),
this.model.onExpansionChanged(node => {
if (node.expanded && node.children.length === 1) {
const child = node.children[0];
@@ -83,18 +81,13 @@ export class FileNavigatorWidget extends FileTreeWidget {
this.model.expandNode(child);
}
}
- }),
- Disposable.create(() => {
- if (this.selectionService.selection === this) {
- this.selectionService.selection = undefined;
- }
+
})
]);
}
- protected onActivateRequest(msg: Message): void {
- super.onActivateRequest(msg);
- this.selectionService.selection = this.model.selectedNodes;
+ protected async initialize(): Promise {
+ await this.model.updateRoot();
const root = this.model.root;
if (CompositeTreeNode.is(root) && root.children.length === 1) {
const child = root.children[0];
@@ -105,10 +98,6 @@ export class FileNavigatorWidget extends FileTreeWidget {
}
}
- protected async initialize(): Promise {
- await this.model.updateRoot();
- }
-
protected enableDndOnMainPanel(): void {
const mainPanelNode = this.shell.mainPanel.node;
this.addEventListener(mainPanelNode, 'drop', async ({ dataTransfer }) => {
diff --git a/packages/navigator/src/browser/style/files.svg b/packages/navigator/src/browser/style/files.svg
new file mode 100644
index 0000000000000..9ee9fdb925f13
--- /dev/null
+++ b/packages/navigator/src/browser/style/files.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/packages/navigator/src/browser/style/index.css b/packages/navigator/src/browser/style/index.css
index f8c6dca6616f1..abc3a6ab43842 100644
--- a/packages/navigator/src/browser/style/index.css
+++ b/packages/navigator/src/browser/style/index.css
@@ -24,6 +24,9 @@
.theia-navigator-container .open-workspace-button-container {
margin: auto;
margin-top: 5px;
+ display: flex;
+ justify-content: center;
+ align-self: center;
}
.theia-navigator-container .center {
@@ -45,6 +48,14 @@
width: calc(100% - var(--theia-ui-padding)*4);
}
-.navigator-tab-icon::before {
- content: "\f0c5"
+.navigator-tab-icon {
+ -webkit-mask: url('files.svg');
+ mask: url('files.svg');
+}
+
+#files > div > div > div > div:first-child {
+ background-color: var(--theia-layout-color4);
+ text-transform: uppercase;
+ font-size: 80%;
+ font-weight: 500;
}
diff --git a/packages/outline-view/package.json b/packages/outline-view/package.json
index d3cc888cb2a07..82f9d4638e2b2 100644
--- a/packages/outline-view/package.json
+++ b/packages/outline-view/package.json
@@ -1,9 +1,9 @@
{
"name": "@theia/outline-view",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Outline View Extension",
"dependencies": {
- "@theia/core": "^0.4.0"
+ "@theia/core": "^0.5.0"
},
"publishConfig": {
"access": "public"
@@ -34,11 +34,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/outline-view/src/browser/outline-view-widget.tsx b/packages/outline-view/src/browser/outline-view-widget.tsx
index 888770869c23e..dc1086919efb3 100644
--- a/packages/outline-view/src/browser/outline-view-widget.tsx
+++ b/packages/outline-view/src/browser/outline-view-widget.tsx
@@ -58,6 +58,7 @@ export class OutlineViewWidget extends TreeWidget {
this.id = 'outline-view';
this.title.label = 'Outline';
this.title.caption = 'Outline';
+ this.title.closable = true;
this.title.iconClass = 'fa outline-view-tab-icon';
this.addClass('theia-outline-view');
}
diff --git a/packages/outline-view/src/browser/styles/index.css b/packages/outline-view/src/browser/styles/index.css
index 3a680e34b2733..241cb2111bc72 100644
--- a/packages/outline-view/src/browser/styles/index.css
+++ b/packages/outline-view/src/browser/styles/index.css
@@ -23,3 +23,7 @@
padding: 10px;
text-align: left;
}
+
+.theia-side-panel .no-outline {
+ margin-left: 9px;
+}
diff --git a/packages/output/package.json b/packages/output/package.json
index ed2b409361bb7..cf561faccecd2 100644
--- a/packages/output/package.json
+++ b/packages/output/package.json
@@ -1,9 +1,9 @@
{
"name": "@theia/output",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Output Extension",
"dependencies": {
- "@theia/core": "^0.4.0"
+ "@theia/core": "^0.5.0"
},
"publishConfig": {
"access": "public"
@@ -34,11 +34,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/output/src/browser/style/output.css b/packages/output/src/browser/style/output.css
index e28472800b297..99a160622d4c3 100644
--- a/packages/output/src/browser/style/output.css
+++ b/packages/output/src/browser/style/output.css
@@ -26,6 +26,10 @@
box-sizing: border-box;
}
+.theia-side-panel #outputView #outputContents {
+ margin-left: 14px;
+}
+
#outputView #outputOverlay {
position: absolute;
right: 24px;
diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json
index c0c21524f20f2..bb28d96f068ae 100644
--- a/packages/plugin-ext-vscode/package.json
+++ b/packages/plugin-ext-vscode/package.json
@@ -1,11 +1,11 @@
{
"name": "@theia/plugin-ext-vscode",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Plugin Extension for VsCode",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/plugin": "^0.4.0",
- "@theia/plugin-ext": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/plugin": "^0.5.0",
+ "@theia/plugin-ext": "^0.5.0",
"vscode-uri": "^1.0.1"
},
"publishConfig": {
@@ -38,11 +38,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/plugin-ext-vscode/src/node/plugin-vscode-backend-module.ts b/packages/plugin-ext-vscode/src/node/plugin-vscode-backend-module.ts
index 222ad178961bb..7bc4cb01f6e2d 100644
--- a/packages/plugin-ext-vscode/src/node/plugin-vscode-backend-module.ts
+++ b/packages/plugin-ext-vscode/src/node/plugin-vscode-backend-module.ts
@@ -20,11 +20,18 @@ import { PluginVsCodeFileHandler } from './plugin-vscode-file-handler';
import { PluginVsCodeDirectoryHandler } from './plugin-vscode-directory-handler';
import { VsCodePluginScanner } from './scanner-vscode';
import { VsCodePluginDeployerResolver } from './plugin-vscode-resolver';
+import { PluginVsCodeCliContribution } from './plugin-vscode-cli-contribution';
+import { CliContribution } from '@theia/core/lib/node';
+import { PluginHostEnvironmentVariable } from '@theia/plugin-ext/lib/common';
export default new ContainerModule(bind => {
bind(PluginDeployerFileHandler).to(PluginVsCodeFileHandler).inSingletonScope();
bind(PluginDeployerDirectoryHandler).to(PluginVsCodeDirectoryHandler).inSingletonScope();
bind(PluginScanner).to(VsCodePluginScanner).inSingletonScope();
bind(PluginDeployerResolver).to(VsCodePluginDeployerResolver).inSingletonScope();
+
+ bind(PluginVsCodeCliContribution).toSelf().inSingletonScope();
+ bind(CliContribution).toService(PluginVsCodeCliContribution);
+ bind(PluginHostEnvironmentVariable).toService(PluginVsCodeCliContribution);
}
);
diff --git a/packages/plugin-ext-vscode/src/node/plugin-vscode-cli-contribution.ts b/packages/plugin-ext-vscode/src/node/plugin-vscode-cli-contribution.ts
new file mode 100644
index 0000000000000..1cc721621c0b5
--- /dev/null
+++ b/packages/plugin-ext-vscode/src/node/plugin-vscode-cli-contribution.ts
@@ -0,0 +1,54 @@
+/********************************************************************************
+ * Copyright (C) 2019 Red Hat, Inc. and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { injectable } from 'inversify';
+import { Argv, Arguments } from 'yargs';
+import { CliContribution } from '@theia/core/lib/node/cli';
+import { PluginHostEnvironmentVariable } from '@theia/plugin-ext/lib/common';
+import { VSCODE_DEFAULT_API_VERSION } from './plugin-vscode-init';
+/**
+ * CLI Contribution allowing to override the VS Code API version which is returned by `vscode.version` API call.
+ */
+@injectable()
+export class PluginVsCodeCliContribution implements CliContribution, PluginHostEnvironmentVariable {
+
+ static VSCODE_API_VERSION = 'vscode-api-version';
+
+ protected vsCodeApiVersion: string | undefined;
+
+ configure(conf: Argv): void {
+ conf.option(PluginVsCodeCliContribution.VSCODE_API_VERSION, {
+ // tslint:disable-next-line:max-line-length
+ description: `Overrides the version returned by VSCode API 'vscode.version'. Example: --${PluginVsCodeCliContribution.VSCODE_API_VERSION}=. Default [${VSCODE_DEFAULT_API_VERSION}]`,
+ type: 'string',
+ nargs: 1
+ });
+ }
+
+ setArguments(args: Arguments): void {
+ const arg = args[PluginVsCodeCliContribution.VSCODE_API_VERSION];
+ if (arg) {
+ this.vsCodeApiVersion = arg;
+ }
+ }
+
+ process(env: NodeJS.ProcessEnv): void {
+ if (this.vsCodeApiVersion) {
+ env['VSCODE_API_VERSION'] = this.vsCodeApiVersion;
+ }
+ }
+
+}
diff --git a/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts b/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts
index 1e017b9fd7416..86c305834fea7 100644
--- a/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts
+++ b/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts
@@ -1,5 +1,5 @@
/********************************************************************************
- * Copyright (C) 2018 Red Hat, Inc.
+ * Copyright (C) 2018-2019 Red Hat, Inc.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -19,6 +19,8 @@
import * as theia from '@theia/plugin';
import { BackendInitializationFn, PluginAPIFactory, Plugin, emptyPlugin } from '@theia/plugin-ext';
+export const VSCODE_DEFAULT_API_VERSION = '1.32.3';
+
/** Set up en as a default locale for VS Code extensions using vscode-nls */
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify({ locale: 'en', availableLanguages: {} });
process.env['VSCODE_PID'] = process.env['THEIA_PARENT_PID'];
@@ -72,7 +74,7 @@ export const doInitialization: BackendInitializationFn = (apiFactory: PluginAPIF
};
// override the version for vscode to be a VSCode version
- (vscode).version = '1.27.2';
+ (vscode).version = process.env['VSCODE_API_VERSION'] || VSCODE_DEFAULT_API_VERSION;
pluginsApiImpl.set(plugin.model.id, vscode);
plugins.push(plugin);
diff --git a/packages/plugin-ext-vscode/src/node/plugin-vscode-resolver.ts b/packages/plugin-ext-vscode/src/node/plugin-vscode-resolver.ts
index 08bd9caf904e7..40a2a0a70bea2 100644
--- a/packages/plugin-ext-vscode/src/node/plugin-vscode-resolver.ts
+++ b/packages/plugin-ext-vscode/src/node/plugin-vscode-resolver.ts
@@ -57,7 +57,7 @@ export class VsCodePluginDeployerResolver implements PluginDeployerResolver {
const extracted = /^vscode:extension\/(.*)/gm.exec(pluginResolverContext.getOriginId());
if (!extracted || extracted === null) {
- reject('Invalid extension' + pluginResolverContext.getOriginId());
+ reject(new Error('Invalid extension' + pluginResolverContext.getOriginId()));
return;
}
const extensionName = extracted[1];
@@ -84,7 +84,7 @@ export class VsCodePluginDeployerResolver implements PluginDeployerResolver {
} else if (response.statusCode === 200) {
const extension = body.results[0].extensions[0];
if (!extension) {
- reject('No extension');
+ reject(new Error('No extension'));
}
let asset;
if (wantedExtensionVersion !== undefined) {
diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json
index 9df5e35de5290..8db527c5fee4a 100644
--- a/packages/plugin-ext/package.json
+++ b/packages/plugin-ext/package.json
@@ -1,24 +1,24 @@
{
"name": "@theia/plugin-ext",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Plugin Extension",
"main": "lib/common/index.js",
"typings": "lib/common/index.d.ts",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/debug": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/file-search": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/markers": "^0.4.0",
- "@theia/messages": "^0.4.0",
- "@theia/monaco": "^0.4.0",
- "@theia/navigator": "^0.4.0",
- "@theia/plugin": "^0.4.0",
- "@theia/preferences": "^0.4.0",
- "@theia/search-in-workspace": "^0.4.0",
- "@theia/task": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/debug": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/file-search": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/markers": "^0.5.0",
+ "@theia/messages": "^0.5.0",
+ "@theia/monaco": "^0.5.0",
+ "@theia/navigator": "^0.5.0",
+ "@theia/plugin": "^0.5.0",
+ "@theia/preferences": "^0.5.0",
+ "@theia/search-in-workspace": "^0.5.0",
+ "@theia/task": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"decompress": "^4.2.0",
"jsonc-parser": "^2.0.2",
"lodash.clonedeep": "^4.5.0",
@@ -58,11 +58,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0",
+ "@theia/ext-scripts": "^0.5.0",
"@types/decompress": "^4.2.2",
"@types/lodash.clonedeep": "^4.5.3"
},
diff --git a/packages/plugin-ext/src/api/async-util.ts b/packages/plugin-ext/src/api/async-util.ts
index a048e915afe63..48e80583ad96d 100644
--- a/packages/plugin-ext/src/api/async-util.ts
+++ b/packages/plugin-ext/src/api/async-util.ts
@@ -18,7 +18,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
export function hookCancellationToken(token: CancellationToken, promise: Promise): PromiseLike {
return new Promise((resolve, reject) => {
- const sub = token.onCancellationRequested(() => reject('This promise is cancelled'));
+ const sub = token.onCancellationRequested(() => reject(new Error('This promise is cancelled')));
promise.then(value => {
sub.dispose();
resolve(value);
diff --git a/packages/plugin-ext/src/api/plugin-api.ts b/packages/plugin-ext/src/api/plugin-api.ts
index f407b48b9bd78..c18921aa51085 100644
--- a/packages/plugin-ext/src/api/plugin-api.ts
+++ b/packages/plugin-ext/src/api/plugin-api.ts
@@ -384,6 +384,7 @@ export interface WorkspaceMain {
$onTextDocumentContentChange(uri: string, content: string): void;
$registerFileSystemWatcher(options: FileWatcherSubscriberOptions): Promise;
$unregisterFileSystemWatcher(watcherId: string): Promise;
+ $updateWorkspaceFolders(start: number, deleteCount?: number, ...rootsToAdd: string[]): Promise;
}
export interface WorkspaceExt {
@@ -423,6 +424,8 @@ export class TreeViewItem {
collapsibleState?: TreeViewItemCollapsibleState;
+ metadata?: any;
+
}
/**
@@ -740,6 +743,13 @@ export interface PreferenceChangeExt {
preferenceName: string,
newValue: any
}
+
+export interface TerminalOptionsExt {
+ attributes?: {
+ [key: string]: string;
+ }
+}
+
export interface PreferenceRegistryExt {
$acceptConfigurationChanged(data: { [key: string]: any }, eventData: PreferenceChangeExt): void;
}
@@ -903,7 +913,7 @@ export interface LanguagesMain {
$registerImplementationProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerTypeDefinitionProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerDefinitionProvider(handle: number, selector: SerializedDocumentFilter[]): void;
- $registeReferenceProvider(handle: number, selector: SerializedDocumentFilter[]): void;
+ $registerReferenceProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerSignatureHelpProvider(handle: number, selector: SerializedDocumentFilter[], triggerCharacters: string[]): void;
$registerHoverProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerDocumentHighlightProvider(handle: number, selector: SerializedDocumentFilter[]): void;
@@ -983,6 +993,7 @@ export interface DebugExt {
$getConfigurationSnippets(debugType: string): Promise;
$createDebugSession(debugConfiguration: theia.DebugConfiguration): Promise;
$terminateDebugSession(sessionId: string): Promise;
+ $getTerminalCreationOptions(debugType: string): Promise;
}
export interface DebugMain {
diff --git a/packages/plugin-ext/src/common/plugin-protocol.ts b/packages/plugin-ext/src/common/plugin-protocol.ts
index 1e9d8fc2c8910..0529608372f4a 100644
--- a/packages/plugin-ext/src/common/plugin-protocol.ts
+++ b/packages/plugin-ext/src/common/plugin-protocol.ts
@@ -18,10 +18,10 @@ import { RPCProtocol } from '../api/rpc-protocol';
import { Disposable } from '@theia/core/lib/common/disposable';
import { LogPart, KeysToAnyValues, KeysToKeysToAnyValue } from './types';
import { CharacterPair, CommentRule, PluginAPIFactory, Plugin } from '../api/plugin-api';
-// FIXME get rid of browser code in backend
-import { PreferenceSchema, PreferenceSchemaProperties } from '@theia/core/lib/browser/preferences';
import { ExtPluginApi } from './plugin-ext-api-contribution';
import { IJSONSchema, IJSONSchemaSnippet } from '@theia/core/lib/common/json-schema';
+import { RecursivePartial } from '@theia/core/lib/common/types';
+import { PreferenceSchema, PreferenceSchemaProperties } from '@theia/core/lib/common/preferences/preference-schema';
export const hostedServicePath = '/services/hostedPlugin';
@@ -55,8 +55,8 @@ export interface PluginPackage {
* This interface describes a package.json contribution section object.
*/
export interface PluginPackageContribution {
- configuration?: PreferenceSchema;
- configurationDefaults?: PreferenceSchemaProperties;
+ configuration?: RecursivePartial;
+ configurationDefaults?: RecursivePartial;
languages?: PluginPackageLanguageContribution[];
grammars?: PluginPackageGrammarsContribution[];
viewsContainers?: { [location: string]: PluginPackageViewContainer[] };
@@ -453,7 +453,7 @@ export interface FoldingRules {
export interface ViewContainer {
id: string;
title: string;
- icon: string;
+ iconUrl: string;
}
/**
@@ -628,3 +628,8 @@ export interface ServerPluginRunner {
*/
getExtraPluginMetadata(): Promise;
}
+
+export const PluginHostEnvironmentVariable = Symbol('PluginHostEnvironmentVariable');
+export interface PluginHostEnvironmentVariable {
+ process(env: NodeJS.ProcessEnv): void;
+}
diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin-manager-client.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin-manager-client.ts
index 91c264f1d222e..7453fa705df7c 100644
--- a/packages/plugin-ext/src/hosted/browser/hosted-plugin-manager-client.ts
+++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin-manager-client.ts
@@ -247,7 +247,10 @@ export class HostedPluginManagerClient {
const dialog = this.openFileDialogFactory({
title: HostedPluginCommands.SELECT_PATH.label!,
- canSelectFiles: false
+ openLabel: 'Select',
+ canSelectFiles: false,
+ canSelectFolders: true,
+ canSelectMany: false
});
dialog.model.navigateTo(rootNode);
const result = await dialog.open();
diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
index 231c3ca99201a..a8c5665abe017 100644
--- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
+++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
@@ -65,6 +65,12 @@ export class HostedPluginSupport {
private frontendExtManagerProxy: PluginManagerExt;
private backendExtManagerProxy: PluginManagerExt;
+ // loaded plugins per #id
+ private loadedPlugins: Set = new Set();
+
+ // per #hostKey
+ private rpc: Map = new Map();
+
constructor(
@inject(PreferenceServiceImpl) private readonly preferenceServiceImpl: PreferenceServiceImpl,
@inject(PluginPathsService) private readonly pluginPathsService: PluginPathsService,
@@ -72,7 +78,6 @@ export class HostedPluginSupport {
@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService,
) {
this.theiaReadyPromise = Promise.all([this.preferenceServiceImpl.ready, this.workspaceService.roots]);
-
this.storagePathService.onStoragePathChanged(path => {
this.updateStoragePath(path);
});
@@ -110,6 +115,10 @@ export class HostedPluginSupport {
if (initData.hostedPlugin) {
initData.plugins.push(initData.hostedPlugin);
}
+
+ // don't load plugins twice
+ initData.plugins = initData.plugins.filter(value => !this.loadedPlugins.has(value.model.id));
+
const confStorage: ConfigStorage = {
hostLogPath: initData.logPath,
hostStoragePath: initData.storagePath || ''
@@ -151,8 +160,14 @@ export class HostedPluginSupport {
if (plugins.length >= 1) {
pluginID = getPluginId(plugins[0].model);
}
- const rpc = this.createServerRpc(pluginID, hostKey);
- setUpPluginApi(rpc, container);
+
+ let rpc = this.rpc.get(hostKey);
+ if (!rpc) {
+ rpc = this.createServerRpc(pluginID, hostKey);
+ setUpPluginApi(rpc, container);
+ this.rpc.set(hostKey, rpc);
+ }
+
const hostedExtManager = rpc.getProxy(MAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT);
hostedExtManager.$init({
plugins: plugins,
@@ -162,10 +177,13 @@ export class HostedPluginSupport {
env: { queryParams: getQueryParameters() },
extApi: initData.pluginAPIs
}, confStorage);
- this.mainPluginApiProviders.getContributions().forEach(p => p.initialize(rpc, container));
+ this.mainPluginApiProviders.getContributions().forEach(p => p.initialize(rpc!, container));
this.backendExtManagerProxy = hostedExtManager;
});
}
+
+ // update list with loaded plugins
+ initData.plugins.forEach(value => this.loadedPlugins.add(value.model.id));
});
}
diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts
index f6b793e971cb1..bed91911901f9 100644
--- a/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts
+++ b/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts
@@ -27,6 +27,7 @@ import { ExtPluginApi } from '../../../common/plugin-ext-api-contribution';
import { createDebugExtStub } from './debug-stub';
import { EditorsAndDocumentsExtImpl } from '../../../plugin/editors-and-documents';
import { WorkspaceExtImpl } from '../../../plugin/workspace';
+import { MessageRegistryExt } from '../../../plugin/message-registry';
// tslint:disable-next-line:no-any
const ctx = self as any;
@@ -50,7 +51,8 @@ function initialize(contextPath: string, pluginMetadata: PluginMetadata): void {
}
const envExt = new EnvExtImpl(rpc);
const editorsAndDocuments = new EditorsAndDocumentsExtImpl(rpc);
-const workspaceExt = new WorkspaceExtImpl(rpc, editorsAndDocuments);
+const messageRegistryExt = new MessageRegistryExt(rpc);
+const workspaceExt = new WorkspaceExtImpl(rpc, editorsAndDocuments, messageRegistryExt);
const preferenceRegistryExt = new PreferenceRegistryExtImpl(rpc, workspaceExt);
const debugExt = createDebugExtStub(rpc);
@@ -130,7 +132,8 @@ const apiFactory = createAPIFactory(
debugExt,
preferenceRegistryExt,
editorsAndDocuments,
- workspaceExt
+ workspaceExt,
+ messageRegistryExt
);
let defaultApi: typeof theia;
diff --git a/packages/plugin-ext/src/hosted/node/hosted-instance-manager.ts b/packages/plugin-ext/src/hosted/node/hosted-instance-manager.ts
index 9de9f9f3148f4..c30e5e39322c1 100644
--- a/packages/plugin-ext/src/hosted/node/hosted-instance-manager.ts
+++ b/packages/plugin-ext/src/hosted/node/hosted-instance-manager.ts
@@ -313,7 +313,7 @@ export abstract class AbstractHostedInstanceManager implements HostedInstanceMan
if (!started) {
this.terminate();
this.isPluginRunnig = false;
- reject('Timeout.');
+ reject(new Error('Timeout.'));
}
}, HOSTED_INSTANCE_START_TIMEOUT_MS);
});
diff --git a/packages/plugin-ext/src/hosted/node/hosted-plugin-deployer-handler.ts b/packages/plugin-ext/src/hosted/node/hosted-plugin-deployer-handler.ts
index 52fb1bc5e6e2a..9a0b2b1c96a6a 100644
--- a/packages/plugin-ext/src/hosted/node/hosted-plugin-deployer-handler.ts
+++ b/packages/plugin-ext/src/hosted/node/hosted-plugin-deployer-handler.ts
@@ -50,6 +50,10 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
for (const plugin of frontendPlugins) {
const metadata = await this.reader.getPluginMetadata(plugin.path());
if (metadata) {
+ if (this.getDeployedFrontendMetadata().some(value => value.model.id === metadata.model.id)) {
+ continue;
+ }
+
this.currentFrontendPluginsMetadata.push(metadata);
this.logger.info(`Deploying frontend plugin "${metadata.model.name}@${metadata.model.version}" from "${metadata.model.entryPoint.frontend || plugin.path()}"`);
}
@@ -60,6 +64,10 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
for (const plugin of backendPlugins) {
const metadata = await this.reader.getPluginMetadata(plugin.path());
if (metadata) {
+ if (this.getDeployedBackendMetadata().some(value => value.model.id === metadata.model.id)) {
+ continue;
+ }
+
this.currentBackendPluginsMetadata.push(metadata);
this.logger.info(`Deploying backend plugin "${metadata.model.name}@${metadata.model.version}" from "${metadata.model.entryPoint.backend || plugin.path()}"`);
}
diff --git a/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts b/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts
index 5a9647ba48bba..696edae185c32 100644
--- a/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts
+++ b/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts
@@ -16,11 +16,11 @@
import * as path from 'path';
import * as cp from 'child_process';
-import { injectable, inject } from 'inversify';
-import { ILogger, ConnectionErrorHandler } from '@theia/core/lib/common';
+import { injectable, inject, named } from 'inversify';
+import { ILogger, ConnectionErrorHandler, ContributionProvider } from '@theia/core/lib/common';
import { Emitter } from '@theia/core/lib/common/event';
import { createIpcEnv } from '@theia/core/lib/node/messaging/ipc-protocol';
-import { HostedPluginClient, ServerPluginRunner, PluginMetadata } from '../../common/plugin-protocol';
+import { HostedPluginClient, ServerPluginRunner, PluginMetadata, PluginHostEnvironmentVariable } from '../../common/plugin-protocol';
import { RPCProtocolImpl } from '../../api/rpc-protocol';
import { MAIN_RPC_CONTEXT } from '../../api/plugin-api';
import { HostedPluginCliContribution } from './hosted-plugin-cli-contribution';
@@ -41,6 +41,10 @@ export class HostedPluginProcess implements ServerPluginRunner {
@inject(HostedPluginCliContribution)
protected readonly cli: HostedPluginCliContribution;
+ @inject(ContributionProvider)
+ @named(PluginHostEnvironmentVariable)
+ protected readonly pluginHostEnvironmentVariables: ContributionProvider;
+
private childProcess: cp.ChildProcess | undefined;
private client: HostedPluginClient;
@@ -128,6 +132,8 @@ export class HostedPluginProcess implements ServerPluginRunner {
delete env[key];
}
}
+ // apply external env variables
+ this.pluginHostEnvironmentVariables.getContributions().forEach(envVar => envVar.process(env));
if (this.cli.extensionTestsPath) {
env.extensionTestsPath = this.cli.extensionTestsPath;
}
diff --git a/packages/plugin-ext/src/hosted/node/plugin-ext-hosted-backend-module.ts b/packages/plugin-ext/src/hosted/node/plugin-ext-hosted-backend-module.ts
index 7ed9fcf9ac907..673b519b71e84 100644
--- a/packages/plugin-ext/src/hosted/node/plugin-ext-hosted-backend-module.ts
+++ b/packages/plugin-ext/src/hosted/node/plugin-ext-hosted-backend-module.ts
@@ -27,7 +27,7 @@ import { HostedPluginReader } from './plugin-reader';
import { HostedPluginSupport } from './hosted-plugin';
import { TheiaPluginScanner } from './scanners/scanner-theia';
import { HostedPluginsManager, HostedPluginsManagerImpl } from './hosted-plugins-manager';
-import { HostedPluginServer, PluginScanner, HostedPluginClient, hostedServicePath, PluginDeployerHandler } from '../../common/plugin-protocol';
+import { HostedPluginServer, PluginScanner, HostedPluginClient, hostedServicePath, PluginDeployerHandler, PluginHostEnvironmentVariable } from '../../common/plugin-protocol';
import { GrammarsReader } from './scanners/grammars-reader';
import { HostedPluginProcess } from './hosted-plugin-process';
import { ExtPluginApiProvider } from '../../common/plugin-ext-api-contribution';
@@ -42,6 +42,8 @@ const commonHostedConnectionModule = ConnectionContainerModule.create(({ bind, b
bind(HostedPluginsManager).toService(HostedPluginsManagerImpl);
bindContributionProvider(bind, Symbol.for(ExtPluginApiProvider));
+ bindContributionProvider(bind, PluginHostEnvironmentVariable);
+
bind(HostedPluginServerImpl).toSelf().inSingletonScope();
bind(HostedPluginServer).toService(HostedPluginServerImpl);
bindBackendService(hostedServicePath, HostedPluginServer, (server, client) => {
diff --git a/packages/plugin-ext/src/hosted/node/plugin-host-rpc.ts b/packages/plugin-ext/src/hosted/node/plugin-host-rpc.ts
index e6ccc3a546446..c1532a0e787a0 100644
--- a/packages/plugin-ext/src/hosted/node/plugin-host-rpc.ts
+++ b/packages/plugin-ext/src/hosted/node/plugin-host-rpc.ts
@@ -24,6 +24,7 @@ import { ExtPluginApi } from '../../common/plugin-ext-api-contribution';
import { DebugExtImpl } from '../../plugin/node/debug/debug';
import { EditorsAndDocumentsExtImpl } from '../../plugin/editors-and-documents';
import { WorkspaceExtImpl } from '../../plugin/workspace';
+import { MessageRegistryExt } from '../../plugin/message-registry';
/**
* Handle the RPC calls.
@@ -42,7 +43,8 @@ export class PluginHostRPC {
const envExt = new EnvExtImpl(this.rpc);
const debugExt = new DebugExtImpl(this.rpc);
const editorsAndDocumentsExt = new EditorsAndDocumentsExtImpl(this.rpc);
- const workspaceExt = new WorkspaceExtImpl(this.rpc, editorsAndDocumentsExt);
+ const messageRegistryExt = new MessageRegistryExt(this.rpc);
+ const workspaceExt = new WorkspaceExtImpl(this.rpc, editorsAndDocumentsExt, messageRegistryExt);
const preferenceRegistryExt = new PreferenceRegistryExtImpl(this.rpc, workspaceExt);
this.pluginManager = this.createPluginManager(envExt, preferenceRegistryExt, this.rpc);
this.rpc.set(MAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT, this.pluginManager);
@@ -57,7 +59,8 @@ export class PluginHostRPC {
debugExt,
preferenceRegistryExt,
editorsAndDocumentsExt,
- workspaceExt
+ workspaceExt,
+ messageRegistryExt
);
}
diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts
index 6331b304a1030..948ea701877d2 100644
--- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts
+++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts
@@ -54,6 +54,8 @@ import * as jsoncparser from 'jsonc-parser';
import { IJSONSchema } from '@theia/core/lib/common/json-schema';
import { deepClone } from '@theia/core/lib/common/objects';
import { FileUri } from '@theia/core/lib/node/file-uri';
+import { PreferenceSchema, PreferenceSchemaProperties } from '@theia/core/lib/common/preferences/preference-schema';
+import { RecursivePartial } from '@theia/core/lib/common/types';
namespace nls {
export function localize(key: string, _default: string) {
@@ -115,11 +117,12 @@ export class TheiaPluginScanner implements PluginScanner {
}
const contributions: PluginContribution = {};
- if (rawPlugin.contributes!.configuration) {
- const config = this.readConfiguration(rawPlugin.contributes.configuration!, rawPlugin.packagePath);
+ if (rawPlugin.contributes.configuration) {
+ const config = this.readConfiguration(rawPlugin.contributes.configuration, rawPlugin.packagePath);
contributions.configuration = config;
}
- contributions.configurationDefaults = rawPlugin.contributes.configurationDefaults;
+ const configurationDefaults = rawPlugin.contributes.configurationDefaults;
+ contributions.configurationDefaults = PreferenceSchemaProperties.is(configurationDefaults) ? configurationDefaults : undefined;
if (rawPlugin.contributes!.languages) {
const languages = this.readLanguages(rawPlugin.contributes.languages!, rawPlugin.packagePath);
@@ -135,7 +138,7 @@ export class TheiaPluginScanner implements PluginScanner {
contributions.viewsContainers = {};
Object.keys(rawPlugin.contributes.viewsContainers!).forEach(location => {
- const containers = this.readViewsContainers(rawPlugin.contributes!.viewsContainers![location], rawPlugin.packagePath);
+ const containers = this.readViewsContainers(rawPlugin.contributes!.viewsContainers![location], rawPlugin);
if (location === 'activitybar') {
location = 'left';
}
@@ -235,12 +238,8 @@ export class TheiaPluginScanner implements PluginScanner {
}
// tslint:disable-next-line:no-any
- private readConfiguration(rawConfiguration: any, pluginPath: string): any {
- return {
- type: rawConfiguration.type,
- title: rawConfiguration.title,
- properties: rawConfiguration.properties
- };
+ private readConfiguration(rawConfiguration: RecursivePartial, pluginPath: string): PreferenceSchema | undefined {
+ return PreferenceSchema.is(rawConfiguration) ? rawConfiguration : undefined;
}
private readKeybinding(rawKeybinding: PluginPackageKeybinding): Keybinding {
@@ -254,18 +253,16 @@ export class TheiaPluginScanner implements PluginScanner {
};
}
- private readViewsContainers(rawViewsContainers: PluginPackageViewContainer[], pluginPath: string): ViewContainer[] {
- return rawViewsContainers.map(rawViewContainer => this.readViewContainer(rawViewContainer, pluginPath));
+ private readViewsContainers(rawViewsContainers: PluginPackageViewContainer[], pck: PluginPackage): ViewContainer[] {
+ return rawViewsContainers.map(rawViewContainer => this.readViewContainer(rawViewContainer, pck));
}
- private readViewContainer(rawViewContainer: PluginPackageViewContainer, pluginPath: string): ViewContainer {
- const result: ViewContainer = {
+ private readViewContainer(rawViewContainer: PluginPackageViewContainer, pck: PluginPackage): ViewContainer {
+ return {
id: rawViewContainer.id,
title: rawViewContainer.title,
- icon: rawViewContainer.icon
+ iconUrl: this.toPluginUrl(pck, rawViewContainer.icon)
};
-
- return result;
}
private readViews(rawViews: PluginPackageView[]): View[] {
diff --git a/packages/plugin-ext/src/main/browser/debug/debug-main.ts b/packages/plugin-ext/src/main/browser/debug/debug-main.ts
index 5346030da84a9..83df89de42b7a 100644
--- a/packages/plugin-ext/src/main/browser/debug/debug-main.ts
+++ b/packages/plugin-ext/src/main/browser/debug/debug-main.ts
@@ -115,6 +115,7 @@ export class DebugMainImpl implements DebugMain {
async $registerDebuggerContribution(description: DebuggerDescription): Promise {
const disposable = new DisposableCollection();
this.toDispose.set(description.type, disposable);
+ const terminalOptionsExt = await this.debugExt.$getTerminalCreationOptions(description.type);
const debugSessionFactory = new PluginDebugSessionFactory(
this.terminalService,
@@ -128,7 +129,8 @@ export class DebugMainImpl implements DebugMain {
const connection = await this.connectionMain.ensureConnection(sessionId);
return new PluginWebSocketChannel(connection);
},
- this.fileSystem
+ this.fileSystem,
+ terminalOptionsExt
);
disposable.pushAll([
diff --git a/packages/plugin-ext/src/main/browser/debug/plugin-debug-session-factory.ts b/packages/plugin-ext/src/main/browser/debug/plugin-debug-session-factory.ts
index 7dbfb48ebb82e..3ea8661d5b2d5 100644
--- a/packages/plugin-ext/src/main/browser/debug/plugin-debug-session-factory.ts
+++ b/packages/plugin-ext/src/main/browser/debug/plugin-debug-session-factory.ts
@@ -27,6 +27,30 @@ import { DebugSession } from '@theia/debug/lib/browser/debug-session';
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
import { IWebSocket } from 'vscode-ws-jsonrpc/lib/socket/socket';
import { FileSystem } from '@theia/filesystem/lib/common';
+import { DebugProtocol } from 'vscode-debugprotocol';
+import { TerminalWidgetOptions } from '@theia/terminal/lib/browser/base/terminal-widget';
+import { TerminalOptionsExt } from '../../../api/plugin-api';
+
+export class PluginDebugSession extends DebugSession {
+ constructor(
+ readonly id: string,
+ readonly options: DebugSessionOptions,
+ protected readonly connection: DebugSessionConnection,
+ protected readonly terminalServer: TerminalService,
+ protected readonly editorManager: EditorManager,
+ protected readonly breakpoints: BreakpointManager,
+ protected readonly labelProvider: LabelProvider,
+ protected readonly messages: MessageClient,
+ protected readonly fileSystem: FileSystem,
+ protected readonly terminalOptionsExt: TerminalOptionsExt | undefined) {
+ super(id, options, connection, terminalServer, editorManager, breakpoints, labelProvider, messages, fileSystem);
+ }
+
+ protected async doRunInTerminal(terminalWidgetOptions: TerminalWidgetOptions): Promise {
+ terminalWidgetOptions = Object.assign({}, terminalWidgetOptions, this.terminalOptionsExt);
+ return super.doRunInTerminal(terminalWidgetOptions);
+ }
+}
/**
* Session factory for a client debug session that communicates with debug adapter contributed as plugin.
@@ -42,7 +66,8 @@ export class PluginDebugSessionFactory extends DefaultDebugSessionFactory {
protected readonly outputChannelManager: OutputChannelManager,
protected readonly debugPreferences: DebugPreferences,
protected readonly connectionFactory: (sessionId: string) => Promise,
- protected readonly fileSystem: FileSystem
+ protected readonly fileSystem: FileSystem,
+ protected readonly terminalOptionsExt: TerminalOptionsExt | undefined
) {
super();
}
@@ -53,7 +78,7 @@ export class PluginDebugSessionFactory extends DefaultDebugSessionFactory {
this.connectionFactory,
this.getTraceOutputChannel());
- return new DebugSession(
+ return new PluginDebugSession(
sessionId,
options,
connection,
@@ -62,6 +87,7 @@ export class PluginDebugSessionFactory extends DefaultDebugSessionFactory {
this.breakpoints,
this.labelProvider,
this.messages,
- this.fileSystem);
+ this.fileSystem,
+ this.terminalOptionsExt);
}
}
diff --git a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts
index ec7b87cc54f89..b089362763497 100644
--- a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts
+++ b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts
@@ -30,6 +30,7 @@ import { TextEditorsMainImpl } from './text-editors-main';
import { EditorManager } from '@theia/editor/lib/browser';
import { OpenerService } from '@theia/core/lib/browser/opener-service';
import { MonacoBulkEditService } from '@theia/monaco/lib/browser/monaco-bulk-edit-service';
+import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service';
export class EditorsAndDocumentsMain {
private toDispose = new DisposableCollection();
@@ -57,11 +58,12 @@ export class EditorsAndDocumentsMain {
const editorManager = container.get(EditorManager);
const openerService = container.get(OpenerService);
const bulkEditService = container.get(MonacoBulkEditService);
+ const monacoEditorService = container.get(MonacoEditorService);
const documentsMain = new DocumentsMainImpl(this, this.modelService, rpc, editorManager, openerService);
rpc.set(PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN, documentsMain);
- const editorsMain = new TextEditorsMainImpl(this, rpc, bulkEditService);
+ const editorsMain = new TextEditorsMainImpl(this, rpc, bulkEditService, monacoEditorService);
rpc.set(PLUGIN_RPC_CONTEXT.TEXT_EDITORS_MAIN, editorsMain);
this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), editorService, this.modelService);
diff --git a/packages/plugin-ext/src/main/browser/languages-main.ts b/packages/plugin-ext/src/main/browser/languages-main.ts
index 75127997662c9..f150dce0996b8 100644
--- a/packages/plugin-ext/src/main/browser/languages-main.ts
+++ b/packages/plugin-ext/src/main/browser/languages-main.ts
@@ -112,7 +112,7 @@ export class LanguagesMainImpl implements LanguagesMain {
this.disposables.set(handle, disposable);
}
- $registeReferenceProvider(handle: number, selector: SerializedDocumentFilter[]): void {
+ $registerReferenceProvider(handle: number, selector: SerializedDocumentFilter[]): void {
const languageSelector = fromLanguageSelector(selector);
const referenceProvider = this.createReferenceProvider(handle, languageSelector);
const disposable = new DisposableCollection();
diff --git a/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts b/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts
index 9a91a2a328622..c296a3cdde16b 100644
--- a/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts
+++ b/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts
@@ -27,6 +27,7 @@ import { VIEW_ITEM_CONTEXT_MENU } from '../view/tree-views-main';
import { PluginContribution, Menu } from '../../../common';
import { DebugStackFramesWidget } from '@theia/debug/lib/browser/view/debug-stack-frames-widget';
import { DebugThreadsWidget } from '@theia/debug/lib/browser/view/debug-threads-widget';
+import { MetadataSelection } from '../metadata-selection';
@injectable()
export class MenusContributionPointHandler {
@@ -115,6 +116,12 @@ export class MenusContributionPointHandler {
const command: Command = { id: commandId };
const selectedResource = () => {
const selection = this.selectionService.selection;
+
+ const metadata = MetadataSelection.getMetadata(selection);
+ if (metadata) {
+ return metadata;
+ }
+
const uri = UriSelection.getUri(selection);
return uri ? uri['codeUri'] : (typeof selection !== 'object' && typeof selection !== 'function') ? selection : undefined;
};
diff --git a/packages/plugin-ext/src/main/browser/metadata-selection.ts b/packages/plugin-ext/src/main/browser/metadata-selection.ts
new file mode 100644
index 0000000000000..45ae12a7e89bb
--- /dev/null
+++ b/packages/plugin-ext/src/main/browser/metadata-selection.ts
@@ -0,0 +1,40 @@
+/********************************************************************************
+ * Copyright (C) 2019 Red Hat, Inc. and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+export interface MetadataSelection {
+ // tslint:disable-next-line:no-any
+ readonly metadata: any
+}
+
+export namespace MetadataSelection {
+
+ export function is(arg: Object | undefined): arg is MetadataSelection {
+ // tslint:disable-next-line:no-any
+ return typeof arg === 'object' && ('metadata' in arg);
+ }
+
+ // tslint:disable-next-line:no-any
+ export function getMetadata(selection: Object | undefined): any | undefined {
+ if (is(selection)) {
+ return selection.metadata;
+ }
+ if (Array.isArray(selection) && is(selection[0])) {
+ return selection[0].metadata;
+ }
+ return undefined;
+ }
+
+}
diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-deploy-command.ts b/packages/plugin-ext/src/main/browser/plugin-ext-deploy-command.ts
index b8a2a272e1fe6..22f1eaa396261 100644
--- a/packages/plugin-ext/src/main/browser/plugin-ext-deploy-command.ts
+++ b/packages/plugin-ext/src/main/browser/plugin-ext-deploy-command.ts
@@ -87,7 +87,7 @@ export class DeployQuickOpenItem extends QuickOpenItem {
protected readonly pluginServer: PluginServer,
protected readonly hostedPluginSupport: HostedPluginSupport,
protected readonly pluginWidget: PluginWidget,
- protected readonly description?: string,
+ protected readonly description?: string
) {
super();
}
diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-widget.tsx b/packages/plugin-ext/src/main/browser/plugin-ext-widget.tsx
index 980f2de9fd4ed..76945cc5ebb80 100644
--- a/packages/plugin-ext/src/main/browser/plugin-ext-widget.tsx
+++ b/packages/plugin-ext/src/main/browser/plugin-ext-widget.tsx
@@ -40,6 +40,7 @@ export class PluginWidget extends ReactWidget {
this.title.label = 'Plugins';
this.title.caption = 'Plugins';
this.title.iconClass = 'fa plugins-tab-icon';
+ this.title.closable = true;
this.addClass('theia-plugins');
this.update();
diff --git a/packages/plugin-ext/src/main/browser/plugin-frontend-view-contribution.ts b/packages/plugin-ext/src/main/browser/plugin-frontend-view-contribution.ts
index 21ea9429f3bbc..1153427bebc5b 100644
--- a/packages/plugin-ext/src/main/browser/plugin-frontend-view-contribution.ts
+++ b/packages/plugin-ext/src/main/browser/plugin-frontend-view-contribution.ts
@@ -29,7 +29,7 @@ export class PluginFrontendViewContribution extends AbstractViewContribution();
constructor(private readonly editorsAndDocuments: EditorsAndDocumentsMain,
- rpc: RPCProtocol,
- private readonly bulkEditService: MonacoBulkEditService) {
+ rpc: RPCProtocol,
+ private readonly bulkEditService: MonacoBulkEditService,
+ private readonly monacoEditorService: MonacoEditorService) {
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TEXT_EDITORS_EXT);
this.toDispose.push(editorsAndDocuments.onTextEditorAdd(editors => editors.forEach(this.onTextEditorAdd, this)));
this.toDispose.push(editorsAndDocuments.onTextEditorRemove(editors => editors.forEach(this.onTextEditorRemove, this)));
@@ -108,9 +110,9 @@ export class TextEditorsMainImpl implements TextEditorsMain {
}
$tryApplyWorkspaceEdit(dto: WorkspaceEditDto): Promise {
- const edits = reviveWorkspaceEditDto(dto);
+ const edits = reviveWorkspaceEditDto(dto);
return new Promise(resolve => {
- this.bulkEditService.apply( edits).then(() => resolve(true), err => resolve(false));
+ this.bulkEditService.apply(edits).then(() => resolve(true), err => resolve(false));
});
}
@@ -122,11 +124,11 @@ export class TextEditorsMainImpl implements TextEditorsMain {
}
$registerTextEditorDecorationType(key: string, options: DecorationRenderOptions): void {
- monaco.services.StaticServices.codeEditorService.get().registerDecorationType(key, options);
+ this.monacoEditorService.registerDecorationType(key, options);
}
$removeTextEditorDecorationType(key: string): void {
- monaco.services.StaticServices.codeEditorService.get().removeDecorationType(key);
+ this.monacoEditorService.removeDecorationType(key);
}
$trySetDecorations(id: string, key: string, ranges: DecorationOptions[]): Promise {
diff --git a/packages/plugin-ext/src/main/browser/view/tree-views-main.tsx b/packages/plugin-ext/src/main/browser/view/tree-views-main.tsx
index 8b6d4b43ca894..a977801f2cc83 100644
--- a/packages/plugin-ext/src/main/browser/view/tree-views-main.tsx
+++ b/packages/plugin-ext/src/main/browser/view/tree-views-main.tsx
@@ -40,6 +40,7 @@ import { MenuPath } from '@theia/core/lib/common/menu';
import * as ReactDOM from 'react-dom';
import * as React from 'react';
import { ContextKeyService, ContextKey } from '@theia/core/lib/browser/context-key-service';
+import { SelectionService } from '@theia/core/lib/common';
export const TREE_NODE_HYPERLINK = 'theia-TreeNodeHyperlink';
export const VIEW_ITEM_CONTEXT_MENU: MenuPath = ['view-item-context-menu'];
@@ -104,7 +105,8 @@ export class TreeViewsMainImpl implements TreeViewsMain {
createTreeViewContainer(dataProvider: TreeViewDataProviderMain): Container {
const child = createTreeContainer(this.container, {
- contextMenuPath: VIEW_ITEM_CONTEXT_MENU
+ contextMenuPath: VIEW_ITEM_CONTEXT_MENU,
+ globalSelection: true
});
child.bind(TreeViewDataProviderMain).toConstantValue(dataProvider);
@@ -140,10 +142,15 @@ export class TreeViewsMainImpl implements TreeViewsMain {
}
-export interface TreeViewFolderNode extends SelectableTreeNode, ExpandableTreeNode, CompositeTreeNode {
+export interface DescriptiveMetadata {
+ // tslint:disable-next-line:no-any
+ readonly metadata?: any
+}
+
+export interface TreeViewFolderNode extends SelectableTreeNode, ExpandableTreeNode, CompositeTreeNode, DescriptiveMetadata {
}
-export interface TreeViewFileNode extends SelectableTreeNode {
+export interface TreeViewFileNode extends SelectableTreeNode, DescriptiveMetadata {
}
export class TreeViewDataProviderMain {
@@ -166,7 +173,8 @@ export class TreeViewDataProviderMain {
visible: true,
selected: false,
expanded,
- children: []
+ children: [],
+ metadata: item.metadata
};
}
@@ -179,6 +187,7 @@ export class TreeViewDataProviderMain {
parent: undefined,
visible: true,
selected: false,
+ metadata: item.metadata
};
}
@@ -218,8 +227,8 @@ export class TreeViewWidget extends TreeWidget {
@inject(TreeProps) readonly treeProps: TreeProps,
@inject(TreeModel) readonly model: TreeModel,
@inject(ContextMenuRenderer) readonly contextMenuRenderer: ContextMenuRenderer,
- @inject(TreeViewDataProviderMain) readonly dataProvider: TreeViewDataProviderMain) {
-
+ @inject(TreeViewDataProviderMain) readonly dataProvider: TreeViewDataProviderMain,
+ @inject(SelectionService) readonly selectionService: SelectionService) {
super(treeProps, model, contextMenuRenderer);
}
diff --git a/packages/plugin-ext/src/main/browser/view/view-registry.ts b/packages/plugin-ext/src/main/browser/view/view-registry.ts
index 8821a3750faab..357b00d5f1dc1 100644
--- a/packages/plugin-ext/src/main/browser/view/view-registry.ts
+++ b/packages/plugin-ext/src/main/browser/view/view-registry.ts
@@ -23,6 +23,7 @@ import {
} from '@theia/core/lib/browser/frontend-application-state';
import { ViewsContainerWidget } from './views-container-widget';
import { TreeViewWidget } from './tree-views-main';
+import { PluginSharedStyle } from '../plugin-shared-style';
const READY: FrontendApplicationState = 'ready';
const DEFAULT_LOCATION: ApplicationShell.Area = 'left';
@@ -36,6 +37,9 @@ export class ViewRegistry {
@inject(FrontendApplicationStateService)
protected applicationStateService: FrontendApplicationStateService;
+ @inject(PluginSharedStyle)
+ protected readonly style: PluginSharedStyle;
+
private treeViewWidgets: Map = new Map();
private containerWidgets: Map = new Map();
private updateContainerOnApplicationReady: Promise;
@@ -49,7 +53,14 @@ export class ViewRegistry {
if (this.containerWidgets.has(viewsContainer.id)) {
return;
}
+ const iconClass = 'plugin-view-container-icon-' + viewsContainer.id;
+ this.style.insertRule('.' + iconClass, () => `
+ mask: : url('${viewsContainer.iconUrl}') no-repeat 50% 50%;
+ -webkit-mask: url('${viewsContainer.iconUrl}') no-repeat 50% 50%;
+ `);
+
const containerWidget = new ViewsContainerWidget(viewsContainer, containerViews);
+ containerWidget.title.iconClass = iconClass;
this.containerWidgets.set(viewsContainer.id, containerWidget);
// add to the promise chain
diff --git a/packages/plugin-ext/src/main/browser/view/views-container-widget.ts b/packages/plugin-ext/src/main/browser/view/views-container-widget.ts
index 153a33a978757..73f634a2390a8 100644
--- a/packages/plugin-ext/src/main/browser/view/views-container-widget.ts
+++ b/packages/plugin-ext/src/main/browser/view/views-container-widget.ts
@@ -31,8 +31,6 @@ export class ViewsContainerWidget extends Widget {
private sections: Map = new Map();
private childrenId: string[] = [];
- sectionTitle: HTMLElement;
-
constructor(protected viewContainer: ViewContainer, protected views: View[]) {
super();
@@ -42,11 +40,6 @@ export class ViewsContainerWidget extends Widget {
this.addClass('theia-views-container');
- // create container title
- this.sectionTitle = createElement('theia-views-container-title');
- this.sectionTitle.innerText = viewContainer.title;
- this.node.appendChild(this.sectionTitle);
-
views.forEach((view: View) => {
if (this.hasView(view.id)) {
return;
@@ -80,7 +73,6 @@ export class ViewsContainerWidget extends Widget {
public updateDimensions() {
let visibleSections = 0;
let availableHeight = this.node.offsetHeight;
- availableHeight -= this.sectionTitle.offsetHeight;
// Determine available space for sections and how much sections are opened
this.sections.forEach((section: ViewContainerSection) => {
availableHeight -= section.header.offsetHeight;
diff --git a/packages/plugin-ext/src/main/browser/webview/webview.ts b/packages/plugin-ext/src/main/browser/webview/webview.ts
index 96f653dbd50fa..c40334db4121c 100644
--- a/packages/plugin-ext/src/main/browser/webview/webview.ts
+++ b/packages/plugin-ext/src/main/browser/webview/webview.ts
@@ -13,7 +13,7 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
-import { BaseWidget } from '@theia/core/lib/browser/widgets/widget';
+import { BaseWidget, Message } from '@theia/core/lib/browser/widgets/widget';
import { IdGenerator } from '../../../common/id-generator';
import { Disposable, DisposableCollection } from '@theia/core';
@@ -38,6 +38,7 @@ export class WebviewWidget extends BaseWidget {
constructor(title: string, private options: WebviewWidgetOptions, private eventDelegate: WebviewEvents) {
super();
+ this.node.tabIndex = 0;
this.id = WebviewWidget.ID.nextId();
this.title.closable = true;
this.title.label = title;
@@ -132,15 +133,16 @@ export class WebviewWidget extends BaseWidget {
this.loadTimeout = undefined;
onLoad(e.target, newFrame.contentWindow);
}
- });
+ }, { once: true });
newFrame.contentDocument!.write(newDocument!.documentElement!.innerHTML);
newFrame.contentDocument!.close();
this.updateSandboxAttribute(newFrame);
}
- focus() {
- this.iframe.contentWindow!.focus();
+ protected onActivateRequest(msg: Message): void {
+ super.onActivateRequest(msg);
+ this.node.focus();
}
private reloadFrame() {
diff --git a/packages/plugin-ext/src/main/browser/webviews-main.ts b/packages/plugin-ext/src/main/browser/webviews-main.ts
index 0140e77ac3bf6..2db2002c573aa 100644
--- a/packages/plugin-ext/src/main/browser/webviews-main.ts
+++ b/packages/plugin-ext/src/main/browser/webviews-main.ts
@@ -85,10 +85,19 @@ export class WebviewsMainImpl implements WebviewsMain {
this.onCloseView(viewId);
});
this.views.set(viewId, view);
- this.shell.addWidget(view, { area: showOptions.area ? showOptions.area : 'main' });
- this.shell.activateWidget(view.id);
+ const widgetOptions: ApplicationShell.WidgetOptions = { area: showOptions.area ? showOptions.area : 'main' };
+ // FIXME translate all view columns properly
+ if (showOptions.viewColumn === -2) {
+ const ref = this.shell.currentWidget;
+ if (ref && this.shell.getAreaFor(ref) === widgetOptions.area) {
+ Object.assign(widgetOptions, { ref, mode: 'open-to-right' });
+ }
+ }
+ this.shell.addWidget(view, widgetOptions);
if (showOptions.preserveFocus) {
- view.focus();
+ this.shell.revealWidget(view.id);
+ } else {
+ this.shell.activateWidget(view.id);
}
}
$disposeWebview(handle: string): void {
@@ -98,7 +107,16 @@ export class WebviewsMainImpl implements WebviewsMain {
}
}
$reveal(handle: string, showOptions: WebviewPanelShowOptions): void {
- throw new Error('Method not implemented.');
+ const webview = this.getWebview(handle);
+ if (webview.isDisposed) {
+ return;
+ }
+ // FIXME handle view column here too!
+ if (showOptions.preserveFocus) {
+ this.shell.revealWidget(webview.id);
+ } else {
+ this.shell.activateWidget(webview.id);
+ }
}
$setTitle(handle: string, value: string): void {
const webview = this.getWebview(handle);
diff --git a/packages/plugin-ext/src/main/browser/workspace-main.ts b/packages/plugin-ext/src/main/browser/workspace-main.ts
index ed18eac945715..54a045dfa6111 100644
--- a/packages/plugin-ext/src/main/browser/workspace-main.ts
+++ b/packages/plugin-ext/src/main/browser/workspace-main.ts
@@ -32,6 +32,7 @@ import { FileWatcherSubscriberOptions } from '../../api/model';
import { InPluginFileSystemWatcherManager } from './in-plugin-filesystem-watcher-manager';
import { StoragePathService } from './storage-path-service';
import { PluginServer } from '../../common/plugin-protocol';
+import { FileSystemPreferences } from '@theia/filesystem/lib/browser';
export class WorkspaceMainImpl implements WorkspaceMain {
@@ -55,6 +56,8 @@ export class WorkspaceMainImpl implements WorkspaceMain {
private storagePathService: StoragePathService;
+ private fsPreferences: FileSystemPreferences;
+
constructor(rpc: RPCProtocol, container: interfaces.Container) {
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.WORKSPACE_EXT);
this.storageProxy = rpc.getProxy(MAIN_RPC_CONTEXT.STORAGE_EXT);
@@ -64,6 +67,7 @@ export class WorkspaceMainImpl implements WorkspaceMain {
this.pluginServer = container.get(PluginServer);
this.workspaceService = container.get(WorkspaceService);
this.storagePathService = container.get(StoragePathService);
+ this.fsPreferences = container.get(FileSystemPreferences);
this.inPluginFileSystemWatcherManager = new InPluginFileSystemWatcherManager(this.proxy, container);
@@ -154,16 +158,31 @@ export class WorkspaceMainImpl implements WorkspaceMain {
async $startFileSearch(includePattern: string, includeFolderUri: string | undefined, excludePatternOrDisregardExcludes?: string | false,
maxResults?: number): Promise {
+ const roots: FileSearchService.RootOptions = {};
const rootUris = includeFolderUri ? [includeFolderUri] : this.roots.map(r => r.uri);
- const opts: FileSearchService.Options = { rootUris };
+ for (const rootUri of rootUris) {
+ roots[rootUri] = {};
+ }
+ const opts: FileSearchService.Options = { rootOptions: roots };
if (includePattern) {
opts.includePatterns = [includePattern];
}
if (typeof excludePatternOrDisregardExcludes === 'string') {
- if (excludePatternOrDisregardExcludes === '') { // default excludes
- opts.defaultIgnorePatterns = [];
- } else {
- opts.defaultIgnorePatterns = [excludePatternOrDisregardExcludes];
+ opts.excludePatterns = [excludePatternOrDisregardExcludes];
+ }
+ if (excludePatternOrDisregardExcludes !== false) {
+ for (const rootUri of rootUris) {
+ const filesExclude = this.fsPreferences.get('files.exclude', undefined, rootUri);
+ if (filesExclude) {
+ for (const excludePattern in filesExclude) {
+ if (filesExclude[excludePattern]) {
+ const rootOptions = roots[rootUri];
+ const rootExcludePatterns = rootOptions.excludePatterns || [];
+ rootExcludePatterns.push(excludePattern);
+ rootOptions.excludePatterns = rootExcludePatterns;
+ }
+ }
+ }
}
}
if (typeof maxResults === 'number') {
@@ -194,6 +213,10 @@ export class WorkspaceMainImpl implements WorkspaceMain {
this.resourceResolver.onContentChange(uri, content);
}
+ async $updateWorkspaceFolders(start: number, deleteCount?: number, ...rootsToAdd: string[]): Promise {
+ await this.workspaceService.spliceRoots(start, deleteCount, ...rootsToAdd.map(root => new URI(root)));
+ }
+
}
/**
@@ -289,7 +312,7 @@ export class TextContentResource implements Resource {
}
}
- return Promise.reject(`Unable to get content for '${this.uri.toString()}'`);
+ return Promise.reject(new Error(`Unable to get content for '${this.uri.toString()}'`));
}
dispose() {
diff --git a/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts b/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts
index 250dd33bff65e..04db06161a7a5 100644
--- a/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts
+++ b/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts
@@ -37,7 +37,7 @@ export class PluginDeployerImpl implements PluginDeployer {
protected readonly logger: ILogger;
@inject(PluginDeployerHandler)
- protected readonly hostedPluginServer: PluginDeployerHandler;
+ protected readonly pluginDeployerHandler: PluginDeployerHandler;
@inject(PluginCliContribution)
protected readonly cliContribution: PluginCliContribution;
@@ -112,9 +112,7 @@ export class PluginDeployerImpl implements PluginDeployer {
}
public async deploy(pluginEntry: string): Promise {
- const entries: string[] = [];
- entries.push(pluginEntry);
- await this.deployMultipleEntries(entries);
+ await this.deployMultipleEntries([pluginEntry]);
return Promise.resolve();
}
@@ -156,8 +154,8 @@ export class PluginDeployerImpl implements PluginDeployer {
await Promise.all([
// start the backend plugins
- this.hostedPluginServer.deployBackendPlugins(acceptedBackendPlugins),
- this.hostedPluginServer.deployFrontendPlugins(acceptedFrontendPlugins)
+ this.pluginDeployerHandler.deployBackendPlugins(acceptedBackendPlugins),
+ this.pluginDeployerHandler.deployFrontendPlugins(acceptedFrontendPlugins)
]);
}
diff --git a/packages/plugin-ext/src/main/node/plugin-github-resolver.ts b/packages/plugin-ext/src/main/node/plugin-github-resolver.ts
index 86308998220f2..4a4a7e47f8360 100644
--- a/packages/plugin-ext/src/main/node/plugin-github-resolver.ts
+++ b/packages/plugin-ext/src/main/node/plugin-github-resolver.ts
@@ -53,7 +53,7 @@ export class GithubPluginDeployerResolver implements PluginDeployerResolver {
const extracted = /^github:(.*)\/(.*)\/(.*)$/gm.exec(pluginResolverContext.getOriginId());
if (!extracted || extracted === null || extracted.length !== 4) {
- reject('Invalid extension' + pluginResolverContext.getOriginId());
+ reject(new Error('Invalid extension' + pluginResolverContext.getOriginId()));
return;
}
@@ -87,14 +87,14 @@ export class GithubPluginDeployerResolver implements PluginDeployerResolver {
if (response.statusCode === 302) {
const redirectLocation = response.headers.location;
if (!redirectLocation) {
- reject('Invalid github link with latest not being found');
+ reject(new Error('Invalid github link with latest not being found'));
return;
}
// parse redirect link
const taggedValueArray = /^https:\/\/.*tag\/(.*)/gm.exec(redirectLocation);
if (!taggedValueArray || taggedValueArray.length !== 2) {
- reject('The redirect link for latest is invalid ' + redirectLocation);
+ reject(new Error('The redirect link for latest is invalid ' + redirectLocation));
return;
}
diff --git a/packages/plugin-ext/src/main/node/plugin-http-resolver.ts b/packages/plugin-ext/src/main/node/plugin-http-resolver.ts
index 227ebfbaa1d8f..4fe923ef3560e 100644
--- a/packages/plugin-ext/src/main/node/plugin-http-resolver.ts
+++ b/packages/plugin-ext/src/main/node/plugin-http-resolver.ts
@@ -52,7 +52,7 @@ export class HttpPluginDeployerResolver implements PluginDeployerResolver {
const urlPath = pluginResolverContext.getOriginId();
const link = url.parse(urlPath);
if (!link.pathname) {
- reject('invalid link URI' + urlPath);
+ reject(new Error('invalid link URI' + urlPath));
return;
}
diff --git a/packages/plugin-ext/src/plugin/command-registry.ts b/packages/plugin-ext/src/plugin/command-registry.ts
index 2ef2b2428687f..8467f4883e2bb 100644
--- a/packages/plugin-ext/src/plugin/command-registry.ts
+++ b/packages/plugin-ext/src/plugin/command-registry.ts
@@ -75,7 +75,7 @@ export class CommandRegistryImpl implements CommandRegistryExt {
if (this.handlers.has(id)) {
return this.executeLocalCommand(id, ...args);
} else {
- return Promise.reject(`Command: ${id} does not exist.`);
+ return Promise.reject(new Error(`Command: ${id} does not exist.`));
}
}
diff --git a/packages/plugin-ext/src/plugin/dialogs.ts b/packages/plugin-ext/src/plugin/dialogs.ts
index 066858b06bcd7..68ca3a406886d 100644
--- a/packages/plugin-ext/src/plugin/dialogs.ts
+++ b/packages/plugin-ext/src/plugin/dialogs.ts
@@ -29,8 +29,8 @@ export class DialogsExtImpl {
const optionsMain = {
openLabel: options.openLabel,
defaultUri: options.defaultUri ? options.defaultUri.path : undefined,
- canSelectFiles: options.canSelectFiles,
- canSelectFolders: options.canSelectFolders,
+ canSelectFiles: options.canSelectFiles ? options.canSelectFiles : true,
+ canSelectFolders: options.canSelectFolders ? options.canSelectFolders : false,
canSelectMany: options.canSelectMany,
filters: options.filters
} as OpenDialogOptionsMain;
diff --git a/packages/plugin-ext/src/plugin/languages.ts b/packages/plugin-ext/src/plugin/languages.ts
index 79dc178d99112..6e650d8c81bb6 100644
--- a/packages/plugin-ext/src/plugin/languages.ts
+++ b/packages/plugin-ext/src/plugin/languages.ts
@@ -429,7 +429,7 @@ export class LanguagesExtImpl implements LanguagesExt {
registerReferenceProvider(selector: theia.DocumentSelector, provider: theia.ReferenceProvider): theia.Disposable {
const callId = this.addNewAdapter(new ReferenceAdapter(provider, this.documents));
- this.proxy.$registeReferenceProvider(callId, this.transformDocumentSelector(selector));
+ this.proxy.$registerReferenceProvider(callId, this.transformDocumentSelector(selector));
return this.createDisposable(callId);
}
// ### Code Reference Provider end
diff --git a/packages/plugin-ext/src/plugin/node/debug/debug.ts b/packages/plugin-ext/src/plugin/node/debug/debug.ts
index 40fcfd07543df..3cf349b63d2db 100644
--- a/packages/plugin-ext/src/plugin/node/debug/debug.ts
+++ b/packages/plugin-ext/src/plugin/node/debug/debug.ts
@@ -20,7 +20,8 @@ import { RPCProtocol } from '../../../api/rpc-protocol';
import {
PLUGIN_RPC_CONTEXT as Ext,
DebugMain,
- DebugExt
+ DebugExt,
+ TerminalOptionsExt
} from '../../../api/plugin-api';
import * as theia from '@theia/plugin';
import uuid = require('uuid');
@@ -227,6 +228,14 @@ export class DebugExtImpl implements DebugExt {
return contribution && contribution.configurationSnippets || [];
}
+ async $getTerminalCreationOptions(debugType: string): Promise {
+ return this.doGetTerminalCreationOptions(debugType);
+ }
+
+ async doGetTerminalCreationOptions(debugType: string): Promise {
+ return undefined;
+ }
+
async $provideDebugConfigurations(debugType: string, workspaceFolderUri: string | undefined): Promise {
let result: theia.DebugConfiguration[] = [];
diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts
index 3097fc37110fd..bed7853400fd9 100644
--- a/packages/plugin-ext/src/plugin/plugin-context.ts
+++ b/packages/plugin-ext/src/plugin/plugin-context.ts
@@ -131,13 +131,13 @@ export function createAPIFactory(
debugExt: DebugExtImpl,
preferenceRegistryExt: PreferenceRegistryExtImpl,
editorsAndDocumentsExt: EditorsAndDocumentsExtImpl,
- workspaceExt: WorkspaceExtImpl
+ workspaceExt: WorkspaceExtImpl,
+ messageRegistryExt: MessageRegistryExt
): PluginAPIFactory {
const commandRegistry = rpc.set(MAIN_RPC_CONTEXT.COMMAND_REGISTRY_EXT, new CommandRegistryImpl(rpc));
const quickOpenExt = rpc.set(MAIN_RPC_CONTEXT.QUICK_OPEN_EXT, new QuickOpenExtImpl(rpc));
const dialogsExt = new DialogsExtImpl(rpc);
- const messageRegistryExt = new MessageRegistryExt(rpc);
const windowStateExt = rpc.set(MAIN_RPC_CONTEXT.WINDOW_STATE_EXT, new WindowStateExtImpl());
const notificationExt = rpc.set(MAIN_RPC_CONTEXT.NOTIFICATION_EXT, new NotificationExtImpl(rpc));
const statusBarExt = new StatusBarExtImpl(rpc);
@@ -396,7 +396,7 @@ export function createAPIFactory(
uri = await documents.createDocumentData(options);
} else {
- return Promise.reject('illegal argument - uriOrFileNameOrOptions');
+ return Promise.reject(new Error('illegal argument - uriOrFileNameOrOptions'));
}
const data = await documents.openDocument(uri);
@@ -429,6 +429,9 @@ export function createAPIFactory(
asRelativePath(pathOrUri: theia.Uri | string, includeWorkspace?: boolean): string | undefined {
return workspaceExt.getRelativePath(pathOrUri, includeWorkspace);
},
+ updateWorkspaceFolders: (index, deleteCount, ...workspaceFoldersToAdd) =>
+ workspaceExt.updateWorkspaceFolders(index, deleteCount || 0, ...workspaceFoldersToAdd)
+ ,
registerTaskProvider(type: string, provider: theia.TaskProvider): theia.Disposable {
return tasks.registerTaskProvider(type, provider);
},
diff --git a/packages/plugin-ext/src/plugin/preference-registry.ts b/packages/plugin-ext/src/plugin/preference-registry.ts
index fa74a71b15418..0e4385f51ef7f 100644
--- a/packages/plugin-ext/src/plugin/preference-registry.ts
+++ b/packages/plugin-ext/src/plugin/preference-registry.ts
@@ -80,6 +80,7 @@ export class PreferenceRegistryExtImpl implements PreferenceRegistryExt {
}
init(data: PreferenceData): void {
+ data[PreferenceScope.Default]['files.associations'] = {};
this._preferences = this.parse(data);
}
diff --git a/packages/plugin-ext/src/plugin/tasks/task-provider.ts b/packages/plugin-ext/src/plugin/tasks/task-provider.ts
index 5e56fd8705fb0..3d5d81cb473e2 100644
--- a/packages/plugin-ext/src/plugin/tasks/task-provider.ts
+++ b/packages/plugin-ext/src/plugin/tasks/task-provider.ts
@@ -52,7 +52,8 @@ export class TaskProviderAdapter {
return Promise.resolve(undefined);
}
const id = ObjectIdentifier.of(task);
- const item = this.cache.get(id);
+ const cached = this.cache.get(id);
+ const item = cached ? cached : Converter.toTask(task);
if (!item) {
return Promise.resolve(undefined);
}
diff --git a/packages/plugin-ext/src/plugin/tree/tree-views.ts b/packages/plugin-ext/src/plugin/tree/tree-views.ts
index fb539aa0314f8..bbe8681086626 100644
--- a/packages/plugin-ext/src/plugin/tree/tree-views.ts
+++ b/packages/plugin-ext/src/plugin/tree/tree-views.ts
@@ -215,7 +215,8 @@ class TreeViewExtImpl extends Disposable {
label: label,
icon,
tooltip: treeItem.tooltip,
- collapsibleState: treeItem.collapsibleState
+ collapsibleState: treeItem.collapsibleState,
+ metadata: value
} as TreeViewItem;
treeItems.push(treeViewItem);
diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts
index 3f34d573258a4..2191a1d3fecfd 100644
--- a/packages/plugin-ext/src/plugin/types-impl.ts
+++ b/packages/plugin-ext/src/plugin/types-impl.ts
@@ -950,6 +950,10 @@ export class CodeActionKind {
public contains(other: CodeActionKind): boolean {
return this.value === other.value || startsWithIgnoreCase(other.value, this.value + CodeActionKind.sep);
}
+
+ public intersects(other: CodeActionKind): boolean {
+ return this.contains(other) || other.contains(this);
+ }
}
export enum TextDocumentSaveReason {
@@ -1679,17 +1683,16 @@ export class Task {
}
private updateDefinitionBasedOnExecution(): void {
- this.taskDefinition = undefined;
if (this.taskExecution instanceof ProcessExecution) {
- this.taskDefinition = {
+ Object.assign(this.taskDefinition, {
type: 'process',
id: this.taskExecution.computeId()
- };
+ });
} else if (this.taskExecution instanceof ShellExecution) {
- this.taskDefinition = {
+ Object.assign(this.taskDefinition, {
type: 'shell',
id: this.taskExecution.computeId()
- };
+ });
}
}
}
diff --git a/packages/plugin-ext/src/plugin/webviews.ts b/packages/plugin-ext/src/plugin/webviews.ts
index 2b4da44fb2f0f..3d5ff196daf71 100644
--- a/packages/plugin-ext/src/plugin/webviews.ts
+++ b/packages/plugin-ext/src/plugin/webviews.ts
@@ -298,12 +298,28 @@ export class WebviewPanelImpl implements theia.WebviewPanel {
this._visible = value;
}
- reveal(area?: WebviewPanelTargetArea, viewColumn?: theia.ViewColumn, preserveFocus?: boolean): void {
+ reveal(arg0?: theia.ViewColumn | WebviewPanelTargetArea, arg1?: theia.ViewColumn | boolean, arg2?: boolean): void {
+ let area: WebviewPanelTargetArea | undefined = undefined;
+ let viewColumn: theia.ViewColumn | undefined = undefined;
+ let preserveFocus: boolean | undefined = undefined;
+ if (typeof arg0 === 'number') {
+ viewColumn = arg0;
+ } else {
+ area = arg0;
+ }
+ if (typeof arg1 === 'number') {
+ viewColumn = arg1;
+ } else {
+ preserveFocus = arg1;
+ }
+ if (typeof arg2 === 'boolean') {
+ preserveFocus = arg2;
+ }
this.checkIsDisposed();
this.proxy.$reveal(this.viewId, {
- area: area,
+ area,
viewColumn: viewColumn ? fromViewColumn(viewColumn) : undefined,
- preserveFocus: !!preserveFocus
+ preserveFocus
});
}
diff --git a/packages/plugin-ext/src/plugin/workspace.ts b/packages/plugin-ext/src/plugin/workspace.ts
index 3d929b60a8e0a..6a8ee119ce613 100644
--- a/packages/plugin-ext/src/plugin/workspace.ts
+++ b/packages/plugin-ext/src/plugin/workspace.ts
@@ -13,6 +13,11 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+// copied and modified from https://github.com/Microsoft/vscode/blob/master/src/vs/workbench/services/workspace/node/workspaceEditingService.ts
import * as paths from 'path';
import * as theia from '@theia/plugin';
@@ -22,7 +27,8 @@ import {
WorkspaceExt,
WorkspaceFolderPickOptionsMain,
WorkspaceMain,
- PLUGIN_RPC_CONTEXT as Ext
+ PLUGIN_RPC_CONTEXT as Ext,
+ MainMessageType
} from '../api/plugin-api';
import { Path } from '@theia/core/lib/common/path';
import { RPCProtocol } from '../api/rpc-protocol';
@@ -35,6 +41,7 @@ import { normalize } from '../common/paths';
import { relative } from '../common/paths-util';
import { Schemes } from '../common/uri-components';
import { toWorkspaceFolder } from './type-converters';
+import { MessageRegistryExt } from './message-registry';
export class WorkspaceExtImpl implements WorkspaceExt {
@@ -47,7 +54,9 @@ export class WorkspaceExtImpl implements WorkspaceExt {
private folders: theia.WorkspaceFolder[] | undefined;
private documentContentProviders = new Map();
- constructor(rpc: RPCProtocol, private editorsAndDocuments: EditorsAndDocumentsExtImpl) {
+ constructor(rpc: RPCProtocol,
+ private editorsAndDocuments: EditorsAndDocumentsExtImpl,
+ private messageService: MessageRegistryExt) {
this.proxy = rpc.getProxy(Ext.WORKSPACE_MAIN);
this.fileSystemWatcherManager = new InPluginFileSystemWatcherProxy(this.proxy);
}
@@ -72,15 +81,20 @@ export class WorkspaceExtImpl implements WorkspaceExt {
$onWorkspaceFoldersChanged(event: WorkspaceRootsChangeEvent): void {
const newRoots = event.roots || [];
const newFolders = newRoots.map((root, index) => this.toWorkspaceFolder(root, index));
- const added = this.foldersDiff(newFolders, this.folders);
- const removed = this.foldersDiff(this.folders, newFolders);
+ const delta = this.deltaFolders(this.folders, newFolders);
this.folders = newFolders;
- this.workspaceFoldersChangedEmitter.fire({
- added: added,
- removed: removed
- });
+ this.workspaceFoldersChangedEmitter.fire(delta);
+ }
+
+ private deltaFolders(currentFolders: theia.WorkspaceFolder[] = [], newFolders: theia.WorkspaceFolder[] = []): {
+ added: theia.WorkspaceFolder[]
+ removed: theia.WorkspaceFolder[]
+ } {
+ const added = this.foldersDiff(newFolders, currentFolders);
+ const removed = this.foldersDiff(currentFolders, newFolders);
+ return { added, removed };
}
private foldersDiff(folder1: theia.WorkspaceFolder[] = [], folder2: theia.WorkspaceFolder[] = []): theia.WorkspaceFolder[] {
@@ -282,6 +296,54 @@ export class WorkspaceExtImpl implements WorkspaceExt {
return normalize(result, true);
}
+ updateWorkspaceFolders(start: number, deleteCount: number, ...workspaceFoldersToAdd: { uri: theia.Uri, name?: string }[]): boolean {
+ const rootsToAdd = new Set();
+ if (Array.isArray(workspaceFoldersToAdd)) {
+ workspaceFoldersToAdd.forEach(folderToAdd => {
+ const uri = URI.isUri(folderToAdd.uri) && folderToAdd.uri.toString();
+ if (uri && !rootsToAdd.has(uri)) {
+ rootsToAdd.add(uri);
+ }
+ });
+ }
+
+ if ([start, deleteCount].some(i => typeof i !== 'number' || i < 0)) {
+ return false; // validate numbers
+ }
+
+ if (deleteCount === 0 && rootsToAdd.size === 0) {
+ return false; // nothing to delete or add
+ }
+
+ const currentWorkspaceFolders = this.workspaceFolders || [];
+ if (start + deleteCount > currentWorkspaceFolders.length) {
+ return false; // cannot delete more than we have
+ }
+
+ // Simulate the updateWorkspaceFolders method on our data to do more validation
+ const newWorkspaceFolders = currentWorkspaceFolders.slice(0);
+ newWorkspaceFolders.splice(start, deleteCount, ...[...rootsToAdd].map(uri => ({ uri: URI.parse(uri), name: undefined!, index: undefined! })));
+
+ for (let i = 0; i < newWorkspaceFolders.length; i++) {
+ const folder = newWorkspaceFolders[i];
+ if (newWorkspaceFolders.some((otherFolder, index) => index !== i && folder.uri.toString() === otherFolder.uri.toString())) {
+ return false; // cannot add the same folder multiple times
+ }
+ }
+
+ const { added, removed } = this.deltaFolders(currentWorkspaceFolders, newWorkspaceFolders);
+ if (added.length === 0 && removed.length === 0) {
+ return false; // nothing actually changed
+ }
+
+ // Trigger on main side
+ this.proxy.$updateWorkspaceFolders(start, deleteCount, ...rootsToAdd).then(undefined, error =>
+ this.messageService.showMessage(MainMessageType.Error, `Failed to update workspace folders: ${error}`)
+ );
+
+ return true;
+ }
+
// Experimental API https://github.com/theia-ide/theia/issues/4167
private workspaceWillRenameFileEmitter = new Emitter();
private workspaceDidRenameFileEmitter = new Emitter();
diff --git a/packages/plugin/package.json b/packages/plugin/package.json
index b0666ae2c0082..0437820523f6a 100644
--- a/packages/plugin/package.json
+++ b/packages/plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@theia/plugin",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Plugin API",
"types": "./src/theia.d.ts",
"publishConfig": {
@@ -23,11 +23,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "rimraf docs && typedoc --module commonjs --target es6 --mode file --hideGenerator --name 'Theia Plug-in API' --excludeExternals --includeDeclarations --out ./docs/api src/theia.d.ts"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts
index 8ea86351aa1b6..7820b12c13c42 100644
--- a/packages/plugin/src/theia.d.ts
+++ b/packages/plugin/src/theia.d.ts
@@ -2663,6 +2663,17 @@ declare module '@theia/plugin' {
*/
readonly onDidDispose: Event;
+ /**
+ * Show the webview panel in a given column.
+ *
+ * A webview panel may only show in a single column at a time. If it is already showing, this
+ * method moves it to a new column.
+ *
+ * @param viewColumn View column to show the panel in. Shows in the current `viewColumn` if undefined.
+ * @param preserveFocus When `true`, the webview will not take focus.
+ */
+ reveal(viewColumn?: ViewColumn, preserveFocus?: boolean): void;
+
/**
* Show the webview panel according to a given options.
*
@@ -3670,48 +3681,48 @@ declare module '@theia/plugin' {
export class FileSystemError extends Error {
/**
- * Create an error to signal that a file or folder wasn't found.
- * @param messageOrUri Message or uri.
- */
+ * Create an error to signal that a file or folder wasn't found.
+ * @param messageOrUri Message or uri.
+ */
static FileNotFound(messageOrUri?: string | Uri): FileSystemError;
/**
- * Create an error to signal that a file or folder already exists, e.g. when
- * creating but not overwriting a file.
- * @param messageOrUri Message or uri.
- */
+ * Create an error to signal that a file or folder already exists, e.g. when
+ * creating but not overwriting a file.
+ * @param messageOrUri Message or uri.
+ */
static FileExists(messageOrUri?: string | Uri): FileSystemError;
/**
- * Create an error to signal that a file is not a folder.
- * @param messageOrUri Message or uri.
- */
+ * Create an error to signal that a file is not a folder.
+ * @param messageOrUri Message or uri.
+ */
static FileNotADirectory(messageOrUri?: string | Uri): FileSystemError;
/**
- * Create an error to signal that a file is a folder.
- * @param messageOrUri Message or uri.
- */
+ * Create an error to signal that a file is a folder.
+ * @param messageOrUri Message or uri.
+ */
static FileIsADirectory(messageOrUri?: string | Uri): FileSystemError;
/**
- * Create an error to signal that an operation lacks required permissions.
- * @param messageOrUri Message or uri.
- */
+ * Create an error to signal that an operation lacks required permissions.
+ * @param messageOrUri Message or uri.
+ */
static NoPermissions(messageOrUri?: string | Uri): FileSystemError;
/**
- * Create an error to signal that the file system is unavailable or too busy to
- * complete a request.
- * @param messageOrUri Message or uri.
- */
+ * Create an error to signal that the file system is unavailable or too busy to
+ * complete a request.
+ * @param messageOrUri Message or uri.
+ */
static Unavailable(messageOrUri?: string | Uri): FileSystemError;
/**
- * Creates a new filesystem error.
- *
- * @param messageOrUri Message or uri.
- */
+ * Creates a new filesystem error.
+ *
+ * @param messageOrUri Message or uri.
+ */
constructor(messageOrUri?: string | Uri);
}
@@ -4147,6 +4158,49 @@ declare module '@theia/plugin' {
*/
export function asRelativePath(pathOrUri: string | Uri, includeWorkspaceFolder?: boolean): string | undefined;
+ /**
+ * This method replaces `deleteCount` [workspace folders](#workspace.workspaceFolders) starting at index `start`
+ * by an optional set of `workspaceFoldersToAdd` on the `theia.workspace.workspaceFolders` array. This "splice"
+ * behavior can be used to add, remove and change workspace folders in a single operation.
+ *
+ * If the first workspace folder is added, removed or changed, the currently executing extensions (including the
+ * one that called this method) will be terminated and restarted so that the (deprecated) `rootPath` property is
+ * updated to point to the first workspace folder.
+ *
+ * Use the [`onDidChangeWorkspaceFolders()`](#onDidChangeWorkspaceFolders) event to get notified when the
+ * workspace folders have been updated.
+ *
+ * **Example:** adding a new workspace folder at the end of workspace folders
+ * ```typescript
+ * workspace.updateWorkspaceFolders(workspace.workspaceFolders ? workspace.workspaceFolders.length : 0, null, { uri: ...});
+ * ```
+ *
+ * **Example:** removing the first workspace folder
+ * ```typescript
+ * workspace.updateWorkspaceFolders(0, 1);
+ * ```
+ *
+ * **Example:** replacing an existing workspace folder with a new one
+ * ```typescript
+ * workspace.updateWorkspaceFolders(0, 1, { uri: ...});
+ * ```
+ *
+ * It is valid to remove an existing workspace folder and add it again with a different name
+ * to rename that folder.
+ *
+ * **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)
+ * from which to start deleting workspace folders.
+ * @param deleteCount the optional number of workspace folders to remove.
+ * @param workspaceFoldersToAdd the optional variable set of workspace folders to add in place of the deleted ones.
+ * Each workspace is identified with a mandatory URI and an optional name.
+ * @return true if the operation was successfully started and false otherwise if arguments were used that would result
+ * in invalid workspace folder state (e.g. 2 folders with the same URI).
+ */
+ export function updateWorkspaceFolders(start: number, deleteCount: number | undefined | null, ...workspaceFoldersToAdd: { uri: Uri, name?: string }[]): boolean;
+
/**
* ~~Register a task provider.~~
*
@@ -5716,6 +5770,15 @@ declare module '@theia/plugin' {
* @param other Kind to check.
*/
contains(other: CodeActionKind): boolean;
+
+ /**
+ * Check if this code action kind intersects `other`.
+ * The kind "refactor.extract" for example intersects refactor, "refactor.extract" and
+ * `"refactor.extract.function", but not "unicorn.refactor.extract", or "refactor.extractAll".
+ *
+ * @param other Kind to check.
+ */
+ intersects(other: CodeActionKind): boolean;
}
/**
diff --git a/packages/preferences/package.json b/packages/preferences/package.json
index 3505105cea40c..fb8ee99e5e451 100644
--- a/packages/preferences/package.json
+++ b/packages/preferences/package.json
@@ -1,15 +1,15 @@
{
"name": "@theia/preferences",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Preferences Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/json": "^0.4.0",
- "@theia/monaco": "^0.4.0",
- "@theia/userstorage": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/json": "^0.5.0",
+ "@theia/monaco": "^0.5.0",
+ "@theia/userstorage": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"@types/fs-extra": "^4.0.2",
"fs-extra": "^4.0.2",
"jsonc-parser": "^2.0.2"
@@ -43,11 +43,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/preferences/src/browser/abstract-resource-preference-provider.ts b/packages/preferences/src/browser/abstract-resource-preference-provider.ts
index 05de310a9835b..aff488ced08bb 100644
--- a/packages/preferences/src/browser/abstract-resource-preference-provider.ts
+++ b/packages/preferences/src/browser/abstract-resource-preference-provider.ts
@@ -118,8 +118,7 @@ export abstract class AbstractResourcePreferenceProvider extends PreferenceProvi
// tslint:disable-next-line:no-any
protected async getParsedContent(content: string): Promise<{ [key: string]: any }> {
- const strippedContent = jsoncparser.stripComments(content);
- const jsonData = jsoncparser.parse(strippedContent);
+ const jsonData = this.parse(content);
// tslint:disable-next-line:no-any
const preferences: { [key: string]: any } = {};
if (typeof jsonData !== 'object') {
@@ -146,6 +145,12 @@ export abstract class AbstractResourcePreferenceProvider extends PreferenceProvi
return preferences;
}
+ // tslint:disable-next-line:no-any
+ protected parse(content: string): any {
+ const strippedContent = jsoncparser.stripComments(content);
+ return jsoncparser.parse(strippedContent);
+ }
+
// tslint:disable-next-line:no-any
protected async handlePreferenceChanges(newPrefs: { [key: string]: any }): Promise {
const oldPrefs = Object.assign({}, this.preferences);
diff --git a/packages/preferences/src/browser/preference-service.spec.ts b/packages/preferences/src/browser/preference-service.spec.ts
index eb6dde85c0a11..c85cfbfc49983 100644
--- a/packages/preferences/src/browser/preference-service.spec.ts
+++ b/packages/preferences/src/browser/preference-service.spec.ts
@@ -29,7 +29,7 @@ import * as temp from 'temp';
import { Emitter } from '@theia/core/lib/common';
import {
PreferenceService, PreferenceScope, PreferenceProviderDataChanges,
- PreferenceSchemaProvider, PreferenceProviderProvider, PreferenceServiceImpl, bindPreferenceSchemaProvider, PreferenceChange
+ PreferenceSchemaProvider, PreferenceProviderProvider, PreferenceServiceImpl, bindPreferenceSchemaProvider, PreferenceChange, PreferenceSchema
} from '@theia/core/lib/browser/preferences';
import { FileSystem, FileShouldOverwrite, FileStat } from '@theia/filesystem/lib/common/';
import { FileSystemWatcher } from '@theia/filesystem/lib/browser/filesystem-watcher';
@@ -623,14 +623,59 @@ describe('Preference Service', () => {
})));
});
- function prepareServices() {
+ it('defaultOverrides [go].editor.formatOnSave', () => {
+ const { preferences, schema } = prepareServices({
+ schema: {
+ properties: {
+ 'editor.insertSpaces': {
+ type: 'boolean',
+ default: true,
+ overridable: true
+ },
+ 'editor.formatOnSave': {
+ type: 'boolean',
+ default: false,
+ overridable: true
+ }
+ }
+ }
+ });
+
+ assert.equal(true, preferences.get('editor.insertSpaces'));
+ assert.equal(undefined, preferences.get('[go].editor.insertSpaces'));
+ assert.equal(false, preferences.get('editor.formatOnSave'));
+ assert.equal(undefined, preferences.get('[go].editor.formatOnSave'));
+
+ schema.registerOverrideIdentifier('go');
+ schema.setSchema({
+ id: 'defaultOverrides',
+ title: 'Default Configuration Overrides',
+ properties: {
+ '[go]': {
+ type: 'object',
+ default: {
+ 'editor.insertSpaces': false,
+ 'editor.formatOnSave': true
+ },
+ description: 'Configure editor settings to be overridden for go language.'
+ }
+ }
+ });
+
+ assert.equal(true, preferences.get('editor.insertSpaces'));
+ assert.equal(false, preferences.get('[go].editor.insertSpaces'));
+ assert.equal(false, preferences.get('editor.formatOnSave'));
+ assert.equal(true, preferences.get('[go].editor.formatOnSave'));
+ });
+
+ function prepareServices(options?: { schema: PreferenceSchema }) {
const container = new Container();
bindPreferenceSchemaProvider(container.bind.bind(container));
container.bind(PreferenceProviderProvider).toFactory(() => () => new MockPreferenceProvider());
container.bind(PreferenceServiceImpl).toSelf().inSingletonScope();
const schema = container.get(PreferenceSchemaProvider);
- schema.setSchema({
+ schema.setSchema(options && options.schema || {
properties: {
'editor.tabSize': {
type: 'number',
diff --git a/packages/preferences/src/browser/workspace-preference-provider.ts b/packages/preferences/src/browser/workspace-preference-provider.ts
index c45c49a206074..be78e5e1d9561 100644
--- a/packages/preferences/src/browser/workspace-preference-provider.ts
+++ b/packages/preferences/src/browser/workspace-preference-provider.ts
@@ -62,16 +62,14 @@ export class WorkspacePreferenceProvider extends AbstractResourcePreferenceProvi
}
// tslint:disable-next-line:no-any
- protected async getParsedContent(content: string): Promise<{ [key: string]: any }> {
- const data = await super.getParsedContent(content);
+ protected parse(content: string): any {
+ const data = super.parse(content);
if (this.workspaceService.saved) {
if (WorkspaceData.is(data)) {
return data.settings || {};
}
- } else {
- return data || {};
}
- return {};
+ return data;
}
protected getPath(preferenceName: string): string[] {
diff --git a/packages/preview/package.json b/packages/preview/package.json
index 6183f78b786ae..b313f40815722 100644
--- a/packages/preview/package.json
+++ b/packages/preview/package.json
@@ -1,12 +1,12 @@
{
"name": "@theia/preview",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Preview Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/languages": "^0.4.0",
- "@theia/mini-browser": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/languages": "^0.5.0",
+ "@theia/mini-browser": "^0.5.0",
"@types/highlight.js": "^9.12.2",
"@types/markdown-it": "^0.0.4",
"@types/markdown-it-anchor": "^4.0.1",
@@ -43,11 +43,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/preview/src/browser/markdown/markdown-preview-handler.ts b/packages/preview/src/browser/markdown/markdown-preview-handler.ts
index 832df374fa8cd..62357d0588209 100644
--- a/packages/preview/src/browser/markdown/markdown-preview-handler.ts
+++ b/packages/preview/src/browser/markdown/markdown-preview-handler.ts
@@ -106,7 +106,7 @@ export class MarkdownPreviewHandler implements PreviewHandler {
if (!elementToReveal) {
return;
}
- elementToReveal.scrollIntoView({ behavior: 'instant' });
+ elementToReveal.scrollIntoView();
}
findElementForFragment(content: HTMLElement, link: string): HTMLElement | undefined {
diff --git a/packages/preview/src/browser/preview-contribution.ts b/packages/preview/src/browser/preview-contribution.ts
index 2ae65dfa03773..cf1cc36efed64 100644
--- a/packages/preview/src/browser/preview-contribution.ts
+++ b/packages/preview/src/browser/preview-contribution.ts
@@ -36,10 +36,12 @@ export namespace PreviewCommands {
*/
export const OPEN: Command = {
id: 'preview:open',
- label: 'Open Preview'
+ label: 'Open Preview',
+ iconClass: 'theia-open-preview-icon'
};
export const OPEN_SOURCE: Command = {
- id: 'preview.open.source'
+ id: 'preview.open.source',
+ iconClass: 'theia-open-file-icon'
};
}
@@ -196,13 +198,11 @@ export class PreviewContribution extends NavigatableWidgetOpenHandler {
this.preventScrollNotification = false;
}, 50);
@@ -225,7 +225,7 @@ export class PreviewWidget extends BaseWidget implements Navigatable {
const elementToReveal = this.previewHandler.findElementForSourceLine(this.node, sourceLine);
if (elementToReveal) {
this.preventScrollNotification = true;
- elementToReveal.scrollIntoView({ behavior: 'instant' });
+ elementToReveal.scrollIntoView();
window.setTimeout(() => {
this.preventScrollNotification = false;
}, 50);
diff --git a/packages/process/package.json b/packages/process/package.json
index 44e0dae296c00..f61e9d14ca327 100644
--- a/packages/process/package.json
+++ b/packages/process/package.json
@@ -1,9 +1,9 @@
{
"name": "@theia/process",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia process support.",
"dependencies": {
- "@theia/core": "^0.4.0",
+ "@theia/core": "^0.5.0",
"@theia/node-pty": "0.7.8-theia004",
"string-argv": "^0.1.1"
},
@@ -36,11 +36,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/process/src/node/raw-process.ts b/packages/process/src/node/raw-process.ts
index 4c24c17e79f71..e32f3793da3b0 100644
--- a/packages/process/src/node/raw-process.ts
+++ b/packages/process/src/node/raw-process.ts
@@ -119,9 +119,9 @@ export class RawProcess extends Process {
);
});
- this.output = this.process.stdout;
- this.input = this.process.stdin;
- this.errorOutput = this.process.stderr;
+ this.output = this.process.stdout || new DevNullStream();
+ this.input = this.process.stdin || new DevNullStream();
+ this.errorOutput = this.process.stderr || new DevNullStream();
if (this.process.pid !== undefined) {
process.nextTick(() => {
diff --git a/packages/python/package.json b/packages/python/package.json
index 24edea2008c4d..818a5cdc4ab96 100644
--- a/packages/python/package.json
+++ b/packages/python/package.json
@@ -1,12 +1,12 @@
{
"name": "@theia/python",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Python Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/languages": "^0.4.0",
- "@theia/monaco": "^0.4.0",
- "@theia/process": "^0.4.0"
+ "@theia/core": "^0.5.0",
+ "@theia/languages": "^0.5.0",
+ "@theia/monaco": "^0.5.0",
+ "@theia/process": "^0.5.0"
},
"publishConfig": {
"access": "public"
@@ -39,11 +39,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/search-in-workspace/package.json b/packages/search-in-workspace/package.json
index e7e5b449d9ff6..f39dfd6c98691 100644
--- a/packages/search-in-workspace/package.json
+++ b/packages/search-in-workspace/package.json
@@ -1,14 +1,14 @@
{
"name": "@theia/search-in-workspace",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Search in workspace",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/navigator": "^0.4.0",
- "@theia/process": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/navigator": "^0.5.0",
+ "@theia/process": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"vscode-ripgrep": "^1.2.4"
},
"publishConfig": {
@@ -41,10 +41,9 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
}
}
diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts b/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts
index ab420767eb567..e8ae6c4f95fc1 100644
--- a/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts
+++ b/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts
@@ -18,12 +18,14 @@ import { AbstractViewContribution, KeybindingRegistry, LabelProvider, CommonMenu
import { SearchInWorkspaceWidget } from './search-in-workspace-widget';
import { injectable, inject, postConstruct } from 'inversify';
import { CommandRegistry, MenuModelRegistry, SelectionService, Command } from '@theia/core';
+import { Widget } from '@theia/core/lib/browser/widgets';
import { NavigatorContextMenu } from '@theia/navigator/lib/browser/navigator-contribution';
import { UriCommandHandler, UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler';
import URI from '@theia/core/lib/common/uri';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { FileSystem } from '@theia/filesystem/lib/common';
import { SearchInWorkspaceContextKeyService } from './search-in-workspace-context-key-service';
+import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
export namespace SearchInWorkspaceCommands {
const SEARCH_CATEGORY = 'Search';
@@ -41,10 +43,28 @@ export namespace SearchInWorkspaceCommands {
category: SEARCH_CATEGORY,
label: 'Find in Folder'
};
+ export const REFRESH_RESULTS: Command = {
+ id: 'search-in-workspace.refresh',
+ category: SEARCH_CATEGORY,
+ label: 'Refresh',
+ iconClass: 'refresh'
+ };
+ export const COLLAPSE_ALL: Command = {
+ id: 'search-in-workspace.collapse-all',
+ category: SEARCH_CATEGORY,
+ label: 'Collapse All',
+ iconClass: 'collapse-all'
+ };
+ export const CLEAR_ALL: Command = {
+ id: 'search-in-workspace.clear-all',
+ category: SEARCH_CATEGORY,
+ label: 'Clear All',
+ iconClass: 'clear-all'
+ };
}
@injectable()
-export class SearchInWorkspaceFrontendContribution extends AbstractViewContribution implements FrontendApplicationContribution {
+export class SearchInWorkspaceFrontendContribution extends AbstractViewContribution implements FrontendApplicationContribution, TabBarToolbarContribution {
@inject(SelectionService) protected readonly selectionService: SelectionService;
@inject(LabelProvider) protected readonly labelProvider: LabelProvider;
@@ -60,7 +80,7 @@ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribut
widgetName: SearchInWorkspaceWidget.LABEL,
defaultWidgetOptions: {
area: 'left',
- rank: 300
+ rank: 200
},
toggleCommandId: SearchInWorkspaceCommands.TOGGLE_SIW_WIDGET.id
});
@@ -78,7 +98,7 @@ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribut
await this.openView({ activate: false });
}
- registerCommands(commands: CommandRegistry): void {
+ async registerCommands(commands: CommandRegistry): Promise {
super.registerCommands(commands);
commands.registerCommand(SearchInWorkspaceCommands.OPEN_SIW_WIDGET, {
isEnabled: () => this.workspaceService.tryGetRoots().length > 0,
@@ -104,10 +124,33 @@ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribut
}
}
});
- const widget: SearchInWorkspaceWidget = await this.openView({ activate: true });
+ const widget = await this.openView({ activate: true });
widget.findInFolder(resources);
}
}));
+
+ commands.registerCommand(SearchInWorkspaceCommands.REFRESH_RESULTS, {
+ execute: w => this.withWidget(w, widget => widget.refresh()),
+ isEnabled: w => this.withWidget(w, widget => (widget.hasResultList() || widget.hasSearchTerm()) && this.workspaceService.tryGetRoots().length > 0),
+ isVisible: w => this.withWidget(w, () => true)
+ });
+ commands.registerCommand(SearchInWorkspaceCommands.COLLAPSE_ALL, {
+ execute: w => this.withWidget(w, widget => widget.collapseAll()),
+ isEnabled: w => this.withWidget(w, widget => widget.hasResultList()),
+ isVisible: w => this.withWidget(w, () => true)
+ });
+ commands.registerCommand(SearchInWorkspaceCommands.CLEAR_ALL, {
+ execute: w => this.withWidget(w, widget => widget.clear()),
+ isEnabled: w => this.withWidget(w, widget => widget.hasResultList()),
+ isVisible: w => this.withWidget(w, () => true)
+ });
+ }
+
+ protected withWidget(widget: Widget | undefined = this.tryGetWidget(), fn: (widget: SearchInWorkspaceWidget) => T): T | false {
+ if (widget instanceof SearchInWorkspaceWidget && widget.id === SearchInWorkspaceWidget.ID) {
+ return fn(widget);
+ }
+ return false;
}
registerKeybindings(keybindings: KeybindingRegistry): void {
@@ -128,6 +171,32 @@ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribut
});
}
+ async registerToolbarItems(toolbarRegistry: TabBarToolbarRegistry): Promise {
+ const widget = await this.widget;
+ const onDidChange = widget.onDidUpdate;
+ toolbarRegistry.registerItem({
+ id: SearchInWorkspaceCommands.REFRESH_RESULTS.id,
+ command: SearchInWorkspaceCommands.REFRESH_RESULTS.id,
+ tooltip: SearchInWorkspaceCommands.REFRESH_RESULTS.label,
+ priority: 0,
+ onDidChange
+ });
+ toolbarRegistry.registerItem({
+ id: SearchInWorkspaceCommands.CLEAR_ALL.id,
+ command: SearchInWorkspaceCommands.CLEAR_ALL.id,
+ tooltip: SearchInWorkspaceCommands.CLEAR_ALL.label,
+ priority: 1,
+ onDidChange
+ });
+ toolbarRegistry.registerItem({
+ id: SearchInWorkspaceCommands.COLLAPSE_ALL.id,
+ command: SearchInWorkspaceCommands.COLLAPSE_ALL.id,
+ tooltip: SearchInWorkspaceCommands.COLLAPSE_ALL.label,
+ priority: 2,
+ onDidChange
+ });
+ }
+
protected newUriAwareCommandHandler(handler: UriCommandHandler): UriAwareCommandHandler {
return new UriAwareCommandHandler(this.selectionService, handler);
}
diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-frontend-module.ts b/packages/search-in-workspace/src/browser/search-in-workspace-frontend-module.ts
index 33fa6f6a28ccd..8faa063529446 100644
--- a/packages/search-in-workspace/src/browser/search-in-workspace-frontend-module.ts
+++ b/packages/search-in-workspace/src/browser/search-in-workspace-frontend-module.ts
@@ -26,6 +26,8 @@ import { SearchInWorkspaceResultTreeWidget } from './search-in-workspace-result-
import { SearchInWorkspaceFrontendContribution } from './search-in-workspace-frontend-contribution';
import { InMemoryTextResourceResolver } from './in-memory-text-resource';
import { SearchInWorkspaceContextKeyService } from './search-in-workspace-context-key-service';
+import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
+import { bindSearchInWorkspacePreferences } from './search-in-workspace-preferences';
export default new ContainerModule(bind => {
bind(SearchInWorkspaceContextKeyService).toSelf().inSingletonScope();
@@ -39,6 +41,7 @@ export default new ContainerModule(bind => {
bindViewContribution(bind, SearchInWorkspaceFrontendContribution);
bind(FrontendApplicationContribution).toService(SearchInWorkspaceFrontendContribution);
+ bind(TabBarToolbarContribution).toService(SearchInWorkspaceFrontendContribution);
// The object that gets notified of search results.
bind(SearchInWorkspaceClientImpl).toSelf().inSingletonScope();
@@ -53,6 +56,8 @@ export default new ContainerModule(bind => {
bind(InMemoryTextResourceResolver).toSelf().inSingletonScope();
bind(ResourceResolver).toService(InMemoryTextResourceResolver);
+
+ bindSearchInWorkspacePreferences(bind);
});
export function createSearchTreeWidget(parent: interfaces.Container): SearchInWorkspaceResultTreeWidget {
diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-preferences.ts b/packages/search-in-workspace/src/browser/search-in-workspace-preferences.ts
new file mode 100644
index 0000000000000..8d423676ccf8c
--- /dev/null
+++ b/packages/search-in-workspace/src/browser/search-in-workspace-preferences.ts
@@ -0,0 +1,48 @@
+/********************************************************************************
+ * Copyright (C) 2019 Ericsson and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { PreferenceSchema, PreferenceProxy, PreferenceService, createPreferenceProxy, PreferenceContribution } from '@theia/core/lib/browser/preferences';
+import { interfaces } from 'inversify';
+
+export const searchInWorkspacePreferencesSchema: PreferenceSchema = {
+ type: 'object',
+ properties: {
+ 'search.lineNumbers': {
+ description: 'Controls whether to show line numbers for search results.',
+ default: false,
+ type: 'boolean',
+ }
+ }
+};
+
+export class SearchInWorkspaceConfiguration {
+ 'search.lineNumbers': boolean;
+}
+
+export const SearchInWorkspacePreferences = Symbol('SearchInWorkspacePreferences');
+export type SearchInWorkspacePreferences = PreferenceProxy;
+
+export function createSearchInWorkspacePreferences(preferences: PreferenceService): SearchInWorkspacePreferences {
+ return createPreferenceProxy(preferences, searchInWorkspacePreferencesSchema);
+}
+
+export function bindSearchInWorkspacePreferences(bind: interfaces.Bind): void {
+ bind(SearchInWorkspacePreferences).toDynamicValue(ctx => {
+ const preferences = ctx.container.get(PreferenceService);
+ return createSearchInWorkspacePreferences(preferences);
+ }).inSingletonScope();
+ bind(PreferenceContribution).toConstantValue({ schema: searchInWorkspacePreferencesSchema });
+}
diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx b/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx
index e3314c464ff28..6d35165f8ede9 100644
--- a/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx
+++ b/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx
@@ -42,6 +42,7 @@ import { SearchInWorkspaceService } from './search-in-workspace-service';
import { MEMORY_TEXT } from './in-memory-text-resource';
import URI from '@theia/core/lib/common/uri';
import * as React from 'react';
+import { SearchInWorkspacePreferences } from './search-in-workspace-preferences';
const ROOT_ID = 'ResultTree';
@@ -115,6 +116,7 @@ export class SearchInWorkspaceResultTreeWidget extends TreeWidget {
@inject(LabelProvider) protected readonly labelProvider: LabelProvider;
@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService;
@inject(TreeExpansionService) protected readonly expansionService: TreeExpansionService;
+ @inject(SearchInWorkspacePreferences) protected readonly searchInWorkspacePreferences: SearchInWorkspacePreferences;
constructor(
@inject(TreeProps) readonly props: TreeProps,
@@ -153,6 +155,10 @@ export class SearchInWorkspaceResultTreeWidget extends TreeWidget {
this.toDispose.push(this.editorManager.onActiveEditorChanged(() => {
this.updateCurrentEditorDecorations();
}));
+
+ this.toDispose.push(this.searchInWorkspacePreferences.onPreferenceChanged(() => {
+ this.update();
+ }));
}
get fileNumber(): number {
@@ -588,6 +594,7 @@ export class SearchInWorkspaceResultTreeWidget extends TreeWidget {
protected renderResultLineNode(node: SearchInWorkspaceResultLineNode): React.ReactNode {
const prefix = node.character > 26 ? '... ' : '';
return
+ {this.searchInWorkspacePreferences['search.lineNumbers'] &&
{node.line} }
{prefix + node.lineText.substr(0, node.character - 1).substr(-25)}
diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx b/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx
index 2fe771015766e..ba3aee37167d8 100644
--- a/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx
+++ b/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx
@@ -20,7 +20,7 @@ import { SearchInWorkspaceResultTreeWidget } from './search-in-workspace-result-
import { SearchInWorkspaceOptions } from '../common/search-in-workspace-interface';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
-import { Disposable } from '@theia/core/lib/common';
+import { Event, Emitter, Disposable } from '@theia/core/lib/common';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { SearchInWorkspaceContextKeyService } from './search-in-workspace-context-key-service';
@@ -72,6 +72,9 @@ export class SearchInWorkspaceWidget extends BaseWidget implements StatefulWidge
protected searchFormContainer: HTMLElement;
protected resultContainer: HTMLElement;
+ protected readonly onDidUpdateEmitter = new Emitter
();
+ readonly onDidUpdate: Event = this.onDidUpdateEmitter.event;
+
@inject(SearchInWorkspaceResultTreeWidget) protected readonly resultTreeWidget: SearchInWorkspaceResultTreeWidget;
@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService;
@@ -83,8 +86,8 @@ export class SearchInWorkspaceWidget extends BaseWidget implements StatefulWidge
this.id = SearchInWorkspaceWidget.ID;
this.title.label = SearchInWorkspaceWidget.LABEL;
this.title.caption = SearchInWorkspaceWidget.LABEL;
- this.title.iconClass = 'fa search-in-workspace-tab-icon';
-
+ this.title.iconClass = 'search-in-workspace-tab-icon';
+ this.title.closable = true;
this.contentNode = document.createElement('div');
this.contentNode.classList.add('t-siw-search-container');
this.searchFormContainer = document.createElement('div');
@@ -180,6 +183,47 @@ export class SearchInWorkspaceWidget extends BaseWidget implements StatefulWidge
this.update();
}
+ hasResultList(): boolean {
+ return this.hasResults;
+ }
+
+ hasSearchTerm(): boolean {
+ return this.searchTerm !== '';
+ }
+
+ refresh(): void {
+ this.resultTreeWidget.search(this.searchTerm, this.searchInWorkspaceOptions);
+ this.update();
+ }
+
+ collapseAll(): void {
+ this.resultTreeWidget.collapseAll();
+ this.update();
+ }
+
+ clear(): void {
+ this.searchTerm = '';
+ this.replaceTerm = '';
+ this.searchInWorkspaceOptions.include = [];
+ this.searchInWorkspaceOptions.exclude = [];
+ this.includeIgnoredState.enabled = false;
+ this.matchCaseState.enabled = false;
+ this.wholeWordState.enabled = false;
+ this.regExpState.enabled = false;
+ const search = document.getElementById('search-input-field');
+ const replace = document.getElementById('replace-input-field');
+ const include = document.getElementById('include-glob-field');
+ const exclude = document.getElementById('exclude-glob-field');
+ if (search && replace && include && exclude) {
+ (search as HTMLInputElement).value = '';
+ (replace as HTMLInputElement).value = '';
+ (include as HTMLInputElement).value = '';
+ (exclude as HTMLInputElement).value = '';
+ }
+ this.resultTreeWidget.search(this.searchTerm, this.searchInWorkspaceOptions);
+ this.update();
+ }
+
protected onAfterAttach(msg: Message): void {
super.onAfterAttach(msg);
ReactDOM.render({this.renderSearchHeader()}{this.renderSearchInfo()} , this.searchFormContainer);
@@ -192,6 +236,7 @@ export class SearchInWorkspaceWidget extends BaseWidget implements StatefulWidge
protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
ReactDOM.render({this.renderSearchHeader()}{this.renderSearchInfo()} , this.searchFormContainer);
+ this.onDidUpdateEmitter.fire(undefined);
}
protected onResize(msg: Widget.ResizeMessage): void {
@@ -224,55 +269,9 @@ export class SearchInWorkspaceWidget extends BaseWidget implements StatefulWidge
}
protected renderSearchHeader(): React.ReactNode {
- const controlButtons = this.renderControlButtons();
const searchAndReplaceContainer = this.renderSearchAndReplace();
const searchDetails = this.renderSearchDetails();
- return {controlButtons}{searchAndReplaceContainer}{searchDetails}
;
- }
-
- protected refresh = () => {
- this.resultTreeWidget.search(this.searchTerm, this.searchInWorkspaceOptions);
- this.update();
- }
-
- protected collapseAll = () => {
- this.resultTreeWidget.collapseAll();
- this.update();
- }
-
- protected clear = () => {
- this.searchTerm = '';
- this.replaceTerm = '';
- this.searchInWorkspaceOptions.include = [];
- this.searchInWorkspaceOptions.exclude = [];
- this.includeIgnoredState.enabled = false;
- this.matchCaseState.enabled = false;
- this.wholeWordState.enabled = false;
- this.regExpState.enabled = false;
- const search = document.getElementById('search-input-field');
- const replace = document.getElementById('replace-input-field');
- const include = document.getElementById('include-glob-field');
- const exclude = document.getElementById('exclude-glob-field');
- if (search && replace && include && exclude) {
- (search as HTMLInputElement).value = '';
- (replace as HTMLInputElement).value = '';
- (include as HTMLInputElement).value = '';
- (exclude as HTMLInputElement).value = '';
- }
- this.resultTreeWidget.search(this.searchTerm, this.searchInWorkspaceOptions);
- this.update();
- }
-
- protected renderControlButtons(): React.ReactNode {
- const refreshButton = this.renderControlButton(`refresh${(this.hasResults || this.searchTerm !== '') && this.workspaceService.tryGetRoots().length > 0
- ? ' enabled' : ''}`, 'Refresh', this.refresh);
- const collapseAllButton = this.renderControlButton(`collapse-all${this.hasResults ? ' enabled' : ''}`, 'Collapse All', this.collapseAll);
- const clearButton = this.renderControlButton(`clear-all${this.hasResults ? ' enabled' : ''}`, 'Clear', this.clear);
- return {refreshButton}{collapseAllButton}{clearButton}
;
- }
-
- protected renderControlButton(btnClass: string, title: string, clickHandler: () => void): React.ReactNode {
- return ;
+ return {searchAndReplaceContainer}{searchDetails}
;
}
protected renderSearchAndReplace(): React.ReactNode {
diff --git a/packages/search-in-workspace/src/browser/styles/index.css b/packages/search-in-workspace/src/browser/styles/index.css
index 855d7c193c5c9..0162a42b5feac 100644
--- a/packages/search-in-workspace/src/browser/styles/index.css
+++ b/packages/search-in-workspace/src/browser/styles/index.css
@@ -21,13 +21,18 @@
.t-siw-search-container {
color: var(--theia-ui-font-color1);
- padding: 5px;
+ padding: 1px 5px;
display: flex;
flex-direction: column;
height: 100%;
box-sizing: border-box;
}
+.t-siw-search-container .theia-ExpansionToggle {
+ padding-right: 4px;
+ min-width: 6px;
+}
+
.t-siw-search-container input[type="text"] {
flex: 1;
line-height: var(--theia-content-line-height);
@@ -59,16 +64,16 @@
margin-bottom: 5px;
}
-.t-siw-search-container .searchHeader .controls .refresh {
- background: var(--theia-icon-refresh);
+.p-TabBar-toolbar .item .refresh {
+ background: var(--theia-icon-refresh) no-repeat;
}
-.t-siw-search-container .searchHeader .controls .collapse-all {
- background: var(--theia-icon-collapse-all);
+.p-TabBar-toolbar .item .collapse-all {
+ background: var(--theia-icon-collapse-all) no-repeat;
}
-.t-siw-search-container .searchHeader .controls .clear-all {
- background: var(--theia-icon-clear);
+.p-TabBar-toolbar .item .clear-all {
+ background: var(--theia-icon-clear) no-repeat;
}
.t-siw-search-container .searchHeader .search-field-container {
@@ -181,26 +186,10 @@
justify-content: center;
}
-.t-siw-search-container .searchHeader .controls .btn{
- margin-left: 3px;
- opacity: 0.25;
- width: 18px
-}
-
-.t-siw-search-container .searchHeader .controls .btn.enabled{
- opacity: 0.7;
- cursor: pointer;
-}
-
-.t-siw-search-container .searchHeader .controls .btn.enabled:hover{
- opacity: 1;
-}
-
.t-siw-search-container .searchHeader .search-details .button-container {
height: 5px;
}
-
.t-siw-search-container .searchHeader .search-details .button-container .btn{
cursor: pointer;
}
@@ -224,6 +213,7 @@
.t-siw-search-container .resultContainer {
height: 100%;
+ margin-left: 13px;
}
.t-siw-search-container .result {
@@ -344,6 +334,11 @@
box-sizing: border-box;
}
+.theia-side-panel .replace-toggle {
+ width: 13px;
+ min-width: 13px;
+}
+
.replace-toggle:hover {
background: var(--theia-layout-color2);
}
@@ -388,8 +383,9 @@
opacity: 0.5;
}
-.search-in-workspace-tab-icon::before {
- content: "\f002"
+.search-in-workspace-tab-icon {
+ -webkit-mask: url('search.svg');
+ mask: url('search.svg');
}
.highlighted-count-container {
@@ -401,3 +397,8 @@
color: var(--theia-ui-font-color2);
margin-left: 17px;
}
+
+.theia-siw-lineNumber {
+ color: var(--theia-ui-font-color2);
+ padding-right: 4px;
+}
diff --git a/packages/search-in-workspace/src/browser/styles/search.svg b/packages/search-in-workspace/src/browser/styles/search.svg
new file mode 100644
index 0000000000000..5b8c2af051798
--- /dev/null
+++ b/packages/search-in-workspace/src/browser/styles/search.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/packages/task/package.json b/packages/task/package.json
index 68fd3afdc98fb..77254e4e987d5 100644
--- a/packages/task/package.json
+++ b/packages/task/package.json
@@ -1,15 +1,15 @@
{
"name": "@theia/task",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Task extension. This extension adds support for executing raw or terminal processes in the backend.",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/markers": "^0.4.0",
- "@theia/process": "^0.4.0",
- "@theia/terminal": "^0.4.0",
- "@theia/variable-resolver": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/markers": "^0.5.0",
+ "@theia/process": "^0.5.0",
+ "@theia/terminal": "^0.5.0",
+ "@theia/variable-resolver": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"jsonc-parser": "^2.0.2"
},
"publishConfig": {
@@ -42,11 +42,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/task/src/browser/provided-task-configurations.spec.ts b/packages/task/src/browser/provided-task-configurations.spec.ts
new file mode 100644
index 0000000000000..6849cc8257c5b
--- /dev/null
+++ b/packages/task/src/browser/provided-task-configurations.spec.ts
@@ -0,0 +1,44 @@
+/********************************************************************************
+ * Copyright (C) 2019 Red Hat, Inc. and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { assert } from 'chai';
+import { Container } from 'inversify';
+import { ProvidedTaskConfigurations } from './provided-task-configurations';
+import { TaskProviderRegistry } from './task-contribution';
+import { TaskConfiguration } from '../common';
+
+describe('provided-task-configurations', () => {
+ let container: Container;
+ beforeEach(() => {
+ container = new Container();
+ container.bind(ProvidedTaskConfigurations).toSelf().inSingletonScope();
+ container.bind(TaskProviderRegistry).toSelf().inSingletonScope();
+ });
+
+ it('provided-task-search', async () => {
+ const providerRegistry = container.get(TaskProviderRegistry);
+ providerRegistry.register('test', {
+ provideTasks(): Promise {
+ return Promise.resolve([{ type: 'test', label: 'task from test', _source: 'test', _scope: 'test' } as TaskConfiguration]);
+ }
+ });
+
+ const task = await container.get(ProvidedTaskConfigurations).getTask('test', 'task from test');
+ assert.isOk(task);
+ assert.equal(task!.type, 'test');
+ assert.equal(task!.label, 'task from test');
+ });
+});
diff --git a/packages/task/src/browser/provided-task-configurations.ts b/packages/task/src/browser/provided-task-configurations.ts
index 336dd5da0d3c9..d904e595f0175 100644
--- a/packages/task/src/browser/provided-task-configurations.ts
+++ b/packages/task/src/browser/provided-task-configurations.ts
@@ -42,7 +42,21 @@ export class ProvidedTaskConfigurations {
}
/** returns the task configuration for a given source and label or undefined if none */
- getTask(source: string, taskLabel: string): TaskConfiguration | undefined {
+ async getTask(source: string, taskLabel: string): Promise {
+ const task = this.getCachedTask(source, taskLabel);
+ if (task) {
+ return task;
+ } else {
+ const provider = this.taskProviderRegistry.getProvider(source);
+ if (provider) {
+ const tasks = await provider.provideTasks();
+ this.cacheTasks(tasks);
+ return this.getCachedTask(source, taskLabel);
+ }
+ }
+ }
+
+ protected getCachedTask(source: string, taskLabel: string): TaskConfiguration | undefined {
const labelConfigMap = this.tasksMap.get(source);
if (labelConfigMap) {
return labelConfigMap.get(taskLabel);
diff --git a/packages/task/src/browser/quick-open-task.ts b/packages/task/src/browser/quick-open-task.ts
index 60d0ed4f8b490..ba034f9c1e21a 100644
--- a/packages/task/src/browser/quick-open-task.ts
+++ b/packages/task/src/browser/quick-open-task.ts
@@ -15,16 +15,22 @@
********************************************************************************/
import { inject, injectable } from 'inversify';
-import { QuickOpenService, QuickOpenModel, QuickOpenItem, QuickOpenGroupItem, QuickOpenMode, QuickOpenHandler, QuickOpenOptions } from '@theia/core/lib/browser/quick-open/';
+import {
+ QuickOpenService, QuickOpenModel, QuickOpenItem,
+ QuickOpenGroupItem, QuickOpenMode, QuickOpenHandler, QuickOpenOptions, QuickOpenActionProvider
+} from '@theia/core/lib/browser/quick-open/';
import { TaskService } from './task-service';
import { TaskInfo, TaskConfiguration } from '../common/task-protocol';
import { TaskConfigurations } from './task-configurations';
import URI from '@theia/core/lib/common/uri';
+import { TaskActionProvider } from './task-action-provider';
+import { LabelProvider } from '@theia/core/lib/browser';
@injectable()
export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler {
protected items: QuickOpenItem[];
+ protected actionProvider: QuickOpenActionProvider | undefined;
readonly prefix: string = 'task ';
@@ -36,6 +42,12 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler {
@inject(QuickOpenService)
protected readonly quickOpenService: QuickOpenService;
+ @inject(TaskActionProvider)
+ protected readonly taskActionProvider: TaskActionProvider;
+
+ @inject(LabelProvider)
+ protected readonly labelProvider: LabelProvider;
+
/**
* @deprecated To be removed in 0.5.0
*/
@@ -47,11 +59,21 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler {
const configuredTasks = this.taskService.getConfiguredTasks();
const providedTasks = await this.taskService.getProvidedTasks();
+ const filteredProvidedTasks: TaskConfiguration[] = [];
+ providedTasks.forEach(provided => {
+ if (!configuredTasks.some(configured => configured.label === provided.label)) {
+ filteredProvidedTasks.push(provided);
+ }
+ });
+
this.items = [];
this.items.push(
- ...configuredTasks.map((t, ind) => new TaskRunQuickOpenItem(t, this.taskService, true, ind === 0 ? 'configured' : undefined)),
- ...providedTasks.map((t, ind) => new TaskRunQuickOpenItem(t, this.taskService, false, ind === 0 ? 'provided' : undefined))
+ ...configuredTasks.map((t, ind) => new TaskRunQuickOpenItem(t, this.taskService, true, ind === 0 ? 'configured tasks' : undefined)),
+ ...filteredProvidedTasks.map((t, ind) => new TaskRunQuickOpenItem(t, this.taskService, false, ind === 0 ? 'detected tasks' : undefined))
);
+
+ this.actionProvider = this.items.length ? this.taskActionProvider : undefined;
+
if (!this.items.length) {
this.items.push(new QuickOpenItem({
label: 'No tasks found',
@@ -63,7 +85,7 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler {
async open(): Promise {
await this.init();
this.quickOpenService.open(this, {
- placeholder: 'Type the name of a task you want to execute',
+ placeholder: 'Select the task to run',
fuzzyMatchLabel: true,
fuzzySort: false
});
@@ -82,6 +104,7 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler {
attach(): void {
this.items = [];
+ this.actionProvider = undefined;
this.taskService.getRunningTasks().then(tasks => {
if (!tasks.length) {
@@ -110,8 +133,31 @@ export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler {
});
}
- onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void {
- acceptor(this.items);
+ async configure(): Promise {
+ this.items = [];
+ this.actionProvider = undefined;
+
+ const providedTasks = await this.taskService.getProvidedTasks();
+ if (!providedTasks.length) {
+ this.items.push(new QuickOpenItem({
+ label: 'No tasks found',
+ run: (_mode: QuickOpenMode): boolean => false
+ }));
+ }
+
+ providedTasks.forEach(task => {
+ this.items.push(new TaskConfigureQuickOpenItem(task, this.taskService, this.labelProvider));
+ });
+
+ this.quickOpenService.open(this, {
+ placeholder: 'Select a task to configure',
+ fuzzyMatchLabel: true,
+ fuzzySort: true
+ });
+ }
+
+ onType(lookFor: string, acceptor: (items: QuickOpenItem[], actionProvider?: QuickOpenActionProvider) => void): void {
+ acceptor(this.items, this.actionProvider);
}
protected getRunningTaskLabel(task: TaskInfo): string {
@@ -130,6 +176,10 @@ export class TaskRunQuickOpenItem extends QuickOpenGroupItem {
super();
}
+ getTask(): TaskConfiguration {
+ return this.task;
+ }
+
getLabel(): string {
if (this.isConfigured) {
return `${this.task.type}: ${this.task.label}`;
@@ -155,7 +205,12 @@ export class TaskRunQuickOpenItem extends QuickOpenGroupItem {
if (mode !== QuickOpenMode.OPEN) {
return false;
}
- this.taskService.run(this.task._source, this.task.label);
+
+ if (this.isConfigured) {
+ this.taskService.runConfiguredTask(this.task._source, this.task.label);
+ } else {
+ this.taskService.run(this.task._source, this.task.label);
+ }
return true;
}
@@ -185,3 +240,33 @@ export class TaskAttachQuickOpenItem extends QuickOpenItem {
return true;
}
}
+export class TaskConfigureQuickOpenItem extends QuickOpenGroupItem {
+
+ constructor(
+ protected readonly task: TaskConfiguration,
+ protected readonly taskService: TaskService,
+ protected readonly labelProvider: LabelProvider
+ ) {
+ super();
+ }
+
+ getLabel(): string {
+ return `${this.task._source}: ${this.task.label}`;
+ }
+
+ getDescription(): string {
+ if (this.task._scope) {
+ return this.labelProvider.getLongName(new URI(this.task._scope));
+ }
+ return this.task._source;
+ }
+
+ run(mode: QuickOpenMode): boolean {
+ if (mode !== QuickOpenMode.OPEN) {
+ return false;
+ }
+ this.taskService.configure(this.task);
+
+ return true;
+ }
+}
diff --git a/packages/task/src/browser/style/configure-inverse.svg b/packages/task/src/browser/style/configure-inverse.svg
new file mode 100644
index 0000000000000..692b34b2c1647
--- /dev/null
+++ b/packages/task/src/browser/style/configure-inverse.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/packages/task/src/browser/style/configure.svg b/packages/task/src/browser/style/configure.svg
new file mode 100644
index 0000000000000..cdd6588df5c12
--- /dev/null
+++ b/packages/task/src/browser/style/configure.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/packages/task/src/browser/style/index.css b/packages/task/src/browser/style/index.css
new file mode 100644
index 0000000000000..84168e26ab32e
--- /dev/null
+++ b/packages/task/src/browser/style/index.css
@@ -0,0 +1,25 @@
+/********************************************************************************
+ * Copyright (C) 2019 Red Hat, Inc. and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+ .quick-open-task-configure-dark {
+ background-image: url('configure-inverse.svg');
+ background-position-y: -2px;
+}
+
+.quick-open-task-configure-bright {
+ background-image: url('configure.svg');
+ background-position-y: -2px;
+}
diff --git a/packages/task/src/browser/task-action-provider.ts b/packages/task/src/browser/task-action-provider.ts
new file mode 100644
index 0000000000000..736330452f3fe
--- /dev/null
+++ b/packages/task/src/browser/task-action-provider.ts
@@ -0,0 +1,66 @@
+/********************************************************************************
+ * Copyright (C) 2019 Red Hat, Inc. and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { injectable, inject } from 'inversify';
+import { TaskService } from './task-service';
+import { TaskRunQuickOpenItem } from './quick-open-task';
+import { QuickOpenBaseAction, QuickOpenItem, QuickOpenActionProvider, QuickOpenAction } from '@theia/core/lib/browser/quick-open';
+import { ThemeService } from '@theia/core/lib/browser/theming';
+
+@injectable()
+export class ConfigureTaskAction extends QuickOpenBaseAction {
+
+ @inject(TaskService)
+ protected readonly taskService: TaskService;
+
+ constructor() {
+ super({ id: 'configure:task' });
+
+ this.updateTheme();
+
+ ThemeService.get().onThemeChange(() => this.updateTheme());
+ }
+
+ async run(item?: QuickOpenItem): Promise {
+ if (item instanceof TaskRunQuickOpenItem) {
+ this.taskService.configure(item.getTask());
+ }
+ }
+
+ protected updateTheme(): void {
+ const theme = ThemeService.get().getCurrentTheme().id;
+ if (theme === 'dark') {
+ this.class = 'quick-open-task-configure-dark';
+ } else if (theme === 'light') {
+ this.class = 'quick-open-task-configure-bright';
+ }
+ }
+}
+
+@injectable()
+export class TaskActionProvider implements QuickOpenActionProvider {
+
+ @inject(ConfigureTaskAction)
+ protected configureTaskAction: ConfigureTaskAction;
+
+ hasActions(): boolean {
+ return true;
+ }
+
+ async getActions(): Promise {
+ return [this.configureTaskAction];
+ }
+}
diff --git a/packages/task/src/browser/task-configurations.ts b/packages/task/src/browser/task-configurations.ts
index 825f6eebd7c80..c84bbbd0c5dae 100644
--- a/packages/task/src/browser/task-configurations.ts
+++ b/packages/task/src/browser/task-configurations.ts
@@ -16,13 +16,16 @@
import { inject, injectable } from 'inversify';
import { TaskConfiguration } from '../common/task-protocol';
-import { Disposable, DisposableCollection } from '@theia/core/lib/common';
+import { Disposable, DisposableCollection, ResourceProvider } from '@theia/core/lib/common';
import URI from '@theia/core/lib/common/uri';
import { FileSystemWatcher, FileChangeEvent } from '@theia/filesystem/lib/browser/filesystem-watcher';
import { FileChange, FileChangeType } from '@theia/filesystem/lib/common/filesystem-watcher-protocol';
import { FileSystem } from '@theia/filesystem/lib/common';
import * as jsoncparser from 'jsonc-parser';
import { ParseError } from 'jsonc-parser';
+import { WorkspaceService } from '@theia/workspace/lib/browser';
+import { open, OpenerService } from '@theia/core/lib/browser';
+import { Resource } from '@theia/core';
export interface TaskConfigurationClient {
/**
@@ -54,6 +57,15 @@ export class TaskConfigurations implements Disposable {
protected client: TaskConfigurationClient | undefined = undefined;
+ @inject(WorkspaceService)
+ protected readonly workspaceService: WorkspaceService;
+
+ @inject(ResourceProvider)
+ protected readonly resourceProvider: ResourceProvider;
+
+ @inject(OpenerService)
+ protected readonly openerService: OpenerService;
+
constructor(
@inject(FileSystemWatcher) protected readonly watcherServer: FileSystemWatcher,
@inject(FileSystem) protected readonly fileSystem: FileSystem
@@ -230,6 +242,49 @@ export class TaskConfigurations implements Disposable {
}
}
+ /** Adds given task to a config file and opens the file to provide ability to edit task configuration. */
+ async configure(task: TaskConfiguration): Promise {
+ const workspace = this.workspaceService.workspace;
+ if (!workspace) {
+ return;
+ }
+
+ const configFileUri = this.getConfigFileUri(workspace.uri);
+ if (!this.getTasks().some(t => t.label === task.label)) {
+ await this.saveTask(configFileUri, task);
+ }
+
+ try {
+ await open(this.openerService, new URI(configFileUri));
+ } catch (e) {
+ console.error(`Error occurred while opening: ${this.TASKFILE}.`, e);
+ }
+ }
+
+ /** Writes the task to a config file. Creates a config file if this one does not exist */
+ async saveTask(configFileUri: string, task: TaskConfiguration): Promise {
+ if (configFileUri && !await this.fileSystem.exists(configFileUri)) {
+ await this.fileSystem.createFile(configFileUri);
+ }
+
+ const { _source, $ident, ...preparedTask } = task;
+ try {
+ const response = await this.fileSystem.resolveContent(configFileUri);
+ const content = response.content;
+
+ const formattingOptions = { tabSize: 4, insertSpaces: true, eol: '' };
+ const edits = jsoncparser.modify(content, ['tasks', -1], preparedTask, { formattingOptions });
+ const result = jsoncparser.applyEdits(content, edits);
+
+ const resource = await this.resourceProvider(new URI(configFileUri));
+ Resource.save(resource, { content: result });
+ } catch (e) {
+ const message = `Failed to save task configuration for ${task.label} task.`;
+ console.error(`${message} ${e.toString()}`);
+ return;
+ }
+ }
+
protected filterDuplicates(tasks: TaskConfiguration[]): TaskConfiguration[] {
const filteredTasks: TaskConfiguration[] = [];
for (const task of tasks) {
diff --git a/packages/task/src/browser/task-frontend-contribution.ts b/packages/task/src/browser/task-frontend-contribution.ts
index ea81f8af09fb5..2cbd3adb70f36 100644
--- a/packages/task/src/browser/task-frontend-contribution.ts
+++ b/packages/task/src/browser/task-frontend-contribution.ts
@@ -47,6 +47,18 @@ export namespace TaskCommands {
category: TASK_CATEGORY,
label: 'Attach Task...'
};
+
+ export const TASK_RUN_TEXT: Command = {
+ id: 'task:run:text',
+ category: TASK_CATEGORY,
+ label: 'Run Selected Text'
+ };
+
+ export const TASK_CONFIGURE: Command = {
+ id: 'task:configure',
+ category: TASK_CATEGORY,
+ label: 'Configure Tasks...'
+ };
}
@injectable()
@@ -119,6 +131,20 @@ export class TaskFrontendContribution implements CommandContribution, MenuContri
execute: () => this.taskService.runLastTask()
}
);
+ registry.registerCommand(
+ TaskCommands.TASK_RUN_TEXT,
+ {
+ isEnabled: () => true,
+ execute: () => this.taskService.runSelectedText()
+ }
+ );
+
+ registry.registerCommand(
+ TaskCommands.TASK_CONFIGURE,
+ {
+ execute: () => this.quickOpenTask.configure()
+ }
+ );
}
registerMenus(menus: MenuModelRegistry): void {
@@ -136,6 +162,16 @@ export class TaskFrontendContribution implements CommandContribution, MenuContri
commandId: TaskCommands.TASK_ATTACH.id,
order: '2'
});
+
+ menus.registerMenuAction(TerminalMenus.TERMINAL_TASKS, {
+ commandId: TaskCommands.TASK_RUN_TEXT.id,
+ order: '3'
+ });
+
+ menus.registerMenuAction(TerminalMenus.TERMINAL_TASKS, {
+ commandId: TaskCommands.TASK_CONFIGURE.id,
+ order: '4'
+ });
}
registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void {
diff --git a/packages/task/src/browser/task-frontend-module.ts b/packages/task/src/browser/task-frontend-module.ts
index 00d84180ada1d..8e95eba0efddc 100644
--- a/packages/task/src/browser/task-frontend-module.ts
+++ b/packages/task/src/browser/task-frontend-module.ts
@@ -29,10 +29,14 @@ import { TaskServer, taskPath } from '../common/task-protocol';
import { TaskWatcher } from '../common/task-watcher';
import { bindProcessTaskModule } from './process/process-task-frontend-module';
import { TaskSchemaUpdater } from './task-schema-updater';
+import { TaskActionProvider, ConfigureTaskAction } from './task-action-provider';
+import '../../src/browser/style/index.css';
export default new ContainerModule(bind => {
bind(TaskFrontendContribution).toSelf().inSingletonScope();
bind(TaskService).toSelf().inSingletonScope();
+ bind(TaskActionProvider).toSelf().inSingletonScope();
+ bind(ConfigureTaskAction).toSelf().inSingletonScope();
for (const identifier of [FrontendApplicationContribution, CommandContribution, KeybindingContribution, MenuContribution, QuickOpenContribution]) {
bind(identifier).toService(TaskFrontendContribution);
diff --git a/packages/task/src/browser/task-service.ts b/packages/task/src/browser/task-service.ts
index 41e1ba774d3dc..a0e7d2108e544 100644
--- a/packages/task/src/browser/task-service.ts
+++ b/packages/task/src/browser/task-service.ts
@@ -15,10 +15,12 @@
********************************************************************************/
import { inject, injectable, named, postConstruct } from 'inversify';
+import { EditorManager } from '@theia/editor/lib/browser';
import { ILogger } from '@theia/core/lib/common';
import { FrontendApplication, ApplicationShell } from '@theia/core/lib/browser';
import { TaskResolverRegistry, TaskProviderRegistry } from './task-contribution';
import { TERMINAL_WIDGET_FACTORY_ID, TerminalWidgetFactoryOptions } from '@theia/terminal/lib/browser/terminal-widget-impl';
+import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
import { WidgetManager } from '@theia/core/lib/browser/widget-manager';
import { MessageService } from '@theia/core/lib/common/message-service';
@@ -28,6 +30,7 @@ import { VariableResolverService } from '@theia/variable-resolver/lib/browser';
import { TaskWatcher } from '../common/task-watcher';
import { TaskConfigurationClient, TaskConfigurations } from './task-configurations';
import { ProvidedTaskConfigurations } from './provided-task-configurations';
+import { Range } from 'vscode-languageserver-types';
import URI from '@theia/core/lib/common/uri';
@injectable()
@@ -79,6 +82,12 @@ export class TaskService implements TaskConfigurationClient {
@inject(TaskResolverRegistry)
protected readonly taskResolverRegistry: TaskResolverRegistry;
+ @inject(TerminalService)
+ protected readonly terminalService: TerminalService;
+
+ @inject(EditorManager)
+ protected readonly editorManager: EditorManager;
+
/**
* @deprecated To be removed in 0.5.0
*/
@@ -148,7 +157,7 @@ export class TaskService implements TaskConfigurationClient {
* Returns a task configuration provided by an extension by task source and label.
* If there are no task configuration, returns undefined.
*/
- getProvidedTask(source: string, label: string): TaskConfiguration | undefined {
+ async getProvidedTask(source: string, label: string): Promise {
return this.providedTaskConfigurations.getTask(source, label);
}
@@ -181,7 +190,8 @@ export class TaskService implements TaskConfigurationClient {
this.logger.error(`Can't get task launch configuration for label: ${taskLabel}`);
return;
}
- this.run(task._source, task.label);
+
+ this.runTask(task);
}
/**
@@ -200,7 +210,7 @@ export class TaskService implements TaskConfigurationClient {
* It looks for configured and provided tasks.
*/
async run(source: string, taskLabel: string): Promise {
- let task = this.getProvidedTask(source, taskLabel);
+ let task = await this.getProvidedTask(source, taskLabel);
if (!task) {
task = this.taskConfigurations.getTask(source, taskLabel);
if (!task) {
@@ -209,6 +219,13 @@ export class TaskService implements TaskConfigurationClient {
}
}
+ this.runTask(task);
+ }
+
+ async runTask(task: TaskConfiguration): Promise {
+ const source = task._source;
+ const taskLabel = task.label;
+
const resolver = this.taskResolverRegistry.getResolver(task.type);
let resolvedTask: TaskConfiguration;
try {
@@ -238,6 +255,30 @@ export class TaskService implements TaskConfigurationClient {
}
}
+ /**
+ * Run selected text in the last active terminal.
+ */
+ async runSelectedText(): Promise {
+ if (!this.editorManager.currentEditor) { return; }
+ const startLine = this.editorManager.currentEditor.editor.selection.start.line;
+ const startCharacter = this.editorManager.currentEditor.editor.selection.start.character;
+ const endLine = this.editorManager.currentEditor.editor.selection.end.line;
+ const endCharacter = this.editorManager.currentEditor.editor.selection.end.character;
+ let selectedRange: Range = Range.create(startLine, startCharacter, endLine, endCharacter);
+ // if no text is selected, default to selecting entire line
+ if (startLine === endLine && startCharacter === endCharacter) {
+ selectedRange = Range.create(startLine, 0, endLine + 1, 0);
+ }
+ const selectedText: string = this.editorManager.currentEditor.editor.document.getText(selectedRange).trimRight() + '\n';
+ let terminal = this.terminalService.currentTerminal;
+ if (!terminal) {
+ terminal = await this.terminalService.newTerminal({ created: new Date().toString() });
+ await terminal.start();
+ this.terminalService.activateTerminal(terminal);
+ }
+ terminal.sendText(selectedText);
+ }
+
async attach(terminalId: number, taskId: number): Promise {
// create terminal widget to display an execution output of a Task that was launched as a command inside a shell
const widget = await this.widgetManager.getOrCreateWidget(
@@ -255,6 +296,10 @@ export class TaskService implements TaskConfigurationClient {
widget.start(terminalId);
}
+ async configure(task: TaskConfiguration): Promise {
+ await this.taskConfigurations.configure(task);
+ }
+
protected isEventForThisClient(context: string | undefined): boolean {
if (context === this.getContext()) {
return true;
diff --git a/packages/task/src/node/task-server.slow-spec.ts b/packages/task/src/node/task-server.slow-spec.ts
index b2dd0caf5dc27..054ce333bfbf4 100644
--- a/packages/task/src/node/task-server.slow-spec.ts
+++ b/packages/task/src/node/task-server.slow-spec.ts
@@ -87,14 +87,14 @@ describe('Task server / back-end', function () {
await new Promise((resolve, reject) => {
const channel = new TestWebSocketChannel({ server, path: `${terminalsPath}/${terminalId}` });
channel.onError(reject);
- channel.onClose((code, reason) => reject(`channel is closed with '${code}' code and '${reason}' reason`));
+ channel.onClose((code, reason) => reject(new Error(`channel is closed with '${code}' code and '${reason}' reason`)));
channel.onMessage(msg => {
// check output of task on terminal is what we expect
const expected = `tasking... ${someString}`;
if (msg.toString().indexOf(expected) !== -1) {
resolve();
} else {
- reject(`expected sub-string not found in terminal output. Expected: "${expected}" vs Actual: "${msg.toString()}"`);
+ reject(new Error(`expected sub-string not found in terminal output. Expected: "${expected}" vs Actual: "${msg.toString()}"`));
}
channel.close();
});
@@ -114,7 +114,7 @@ describe('Task server / back-end', function () {
if (taskInfo.terminalId === undefined) {
resolve();
} else {
- reject(`terminal id was expected to be undefined, actual: ${taskInfo.terminalId}`);
+ reject(new Error(`terminal id was expected to be undefined, actual: ${taskInfo.terminalId}`));
}
toDispose.dispose();
}
@@ -272,14 +272,14 @@ describe('Task server / back-end', function () {
if (runningTasksAll.length === 6) {
resolve();
} else {
- reject(`Error: unexpected total number of running tasks for all contexts: expected: 6, actual: ${runningTasksCtx1.length}`);
+ reject(new Error(`Error: unexpected total number of running tasks for all contexts: expected: 6, actual: ${runningTasksCtx1.length}`));
}
} else {
- reject(`Error: unexpected number of running tasks for context 2: expected: 2, actual: ${runningTasksCtx1.length}`);
+ reject(new Error(`Error: unexpected number of running tasks for context 2: expected: 2, actual: ${runningTasksCtx1.length}`));
}
} else {
- reject(`Error: unexpected number of running tasks for context 1: expected: 4, actual: ${runningTasksCtx1.length}`);
+ reject(new Error(`Error: unexpected number of running tasks for context 1: expected: 4, actual: ${runningTasksCtx1.length}`));
}
});
@@ -320,11 +320,11 @@ describe('Task server / back-end', function () {
if (numRunningTasksAfterKilled.length === 0) {
resolve();
} else {
- reject(`Error: remaining running tasks, after all killed: expected: 0, actual: ${numRunningTasksAfterKilled.length}`);
+ reject(new Error(`Error: remaining running tasks, after all killed: expected: 0, actual: ${numRunningTasksAfterKilled.length}`));
}
} else {
- reject(`Error: unexpected number of running tasks: expected: ${numTasks}, actual: ${numRunningTasksAfterCreated.length}`);
+ reject(new Error(`Error: unexpected number of running tasks: expected: ${numTasks}, actual: ${numRunningTasksAfterCreated.length}`));
}
});
diff --git a/packages/task/src/node/task-server.ts b/packages/task/src/node/task-server.ts
index 13abfe82ae29f..6f615b21c8302 100644
--- a/packages/task/src/node/task-server.ts
+++ b/packages/task/src/node/task-server.ts
@@ -95,7 +95,7 @@ export class TaskServerImpl implements TaskServer {
return taskToKill.kill();
} else {
this.logger.info(`Could not find task to kill, task id ${id}. Already terminated?`);
- return Promise.reject(`Could not find task to kill, task id ${id}. Already terminated?`);
+ return Promise.reject(new Error(`Could not find task to kill, task id ${id}. Already terminated?`));
}
}
diff --git a/packages/terminal/package.json b/packages/terminal/package.json
index 80545ba73ea4f..acfb46c4060cf 100644
--- a/packages/terminal/package.json
+++ b/packages/terminal/package.json
@@ -1,13 +1,13 @@
{
"name": "@theia/terminal",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Terminal Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/filesystem": "^0.4.0",
- "@theia/process": "^0.4.0",
- "@theia/workspace": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/filesystem": "^0.5.0",
+ "@theia/process": "^0.5.0",
+ "@theia/workspace": "^0.5.0",
"xterm": "3.9.2"
},
"publishConfig": {
@@ -40,11 +40,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/terminal/src/browser/terminal-preferences.ts b/packages/terminal/src/browser/terminal-preferences.ts
index 4155588369852..d829d307e9f4e 100644
--- a/packages/terminal/src/browser/terminal-preferences.ts
+++ b/packages/terminal/src/browser/terminal-preferences.ts
@@ -39,6 +39,7 @@ export const TerminalConfigSchema: PreferenceSchema = {
'terminal.integrated.fontSize': {
type: 'number',
description: 'Controls the font size in pixels of the terminal.',
+ minimum: 6,
default: EDITOR_FONT_DEFAULTS.fontSize
},
'terminal.integrated.fontWeight': {
@@ -61,6 +62,7 @@ export const TerminalConfigSchema: PreferenceSchema = {
'terminal.integrated.lineHeight': {
description: 'Controls the line height of the terminal, this number is multiplied by the terminal font size to get the actual line-height in pixels.',
type: 'number',
+ minimum: 1,
default: 1
},
}
diff --git a/packages/terminal/src/node/terminal-backend-contribution.slow-spec.ts b/packages/terminal/src/node/terminal-backend-contribution.slow-spec.ts
index 82e230dfd00fc..064f1b828db37 100644
--- a/packages/terminal/src/node/terminal-backend-contribution.slow-spec.ts
+++ b/packages/terminal/src/node/terminal-backend-contribution.slow-spec.ts
@@ -47,7 +47,7 @@ describe('Terminal Backend Contribution', function () {
await new Promise((resolve, reject) => {
const channel = new TestWebSocketChannel({ server, path: `${terminalsPath}/${terminalId}` });
channel.onError(reject);
- channel.onClose((code, reason) => reject(`channel is closed with '${code}' code and '${reason}' reason`));
+ channel.onClose((code, reason) => reject(new Error(`channel is closed with '${code}' code and '${reason}' reason`)));
channel.onOpen(() => {
resolve();
channel.close();
diff --git a/packages/textmate-grammars/package.json b/packages/textmate-grammars/package.json
index 9148160e2be2c..ef21f6d689635 100644
--- a/packages/textmate-grammars/package.json
+++ b/packages/textmate-grammars/package.json
@@ -1,9 +1,9 @@
{
"name": "@theia/textmate-grammars",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Textmate Grammars",
"dependencies": {
- "@theia/monaco": "^0.4.0"
+ "@theia/monaco": "^0.5.0"
},
"publishConfig": {
"access": "public"
@@ -35,11 +35,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/textmate-grammars/src/browser/perl.ts b/packages/textmate-grammars/src/browser/perl.ts
index 741a97ab56013..536d9b92de6e7 100644
--- a/packages/textmate-grammars/src/browser/perl.ts
+++ b/packages/textmate-grammars/src/browser/perl.ts
@@ -22,6 +22,32 @@ export class PerlContribution implements LanguageGrammarDefinitionContribution {
readonly id = 'perl';
readonly scopeName = 'source.perl';
+ readonly config: monaco.languages.LanguageConfiguration = {
+ comments: {
+ lineComment: '#'
+ },
+ brackets: [
+ ['{', '}'],
+ ['[', ']'],
+ ['(', ')']
+ ],
+ autoClosingPairs: [
+ { open: '{', close: '}' },
+ { open: '[', close: ']' },
+ { open: '(', close: ')' },
+ { open: '"', close: '"' },
+ { open: '\'', close: '\'' },
+ { open: '`', close: '`' }
+ ],
+ surroundingPairs: [
+ { open: '{', close: '}' },
+ { open: '[', close: ']' },
+ { open: '(', close: ')' },
+ { open: '"', close: '"' },
+ { open: '\'', close: '\'' },
+ { open: '`', close: '`' }
+ ]
+ };
registerTextmateLanguage(registry: TextmateRegistry) {
monaco.languages.register({
@@ -30,6 +56,9 @@ export class PerlContribution implements LanguageGrammarDefinitionContribution {
extensions: ['.pl', '.pm', '.pod', '.t', '.PL', '.psgi'],
firstLine: '^#!.*\\bperl\\b'
});
+
+ monaco.languages.setLanguageConfiguration(this.id, this.config);
+
const grammar = require('../../data/perl.tmLanguage.json');
registry.registerTextmateGrammarScope(this.scopeName, {
async getGrammarDefinition() {
diff --git a/packages/tslint/package.json b/packages/tslint/package.json
index 27acc78a698f6..7ec8d91373718 100644
--- a/packages/tslint/package.json
+++ b/packages/tslint/package.json
@@ -1,6 +1,6 @@
{
"name": "@theia/tslint",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - TSLint Extension",
"publishConfig": {
"access": "public"
@@ -30,11 +30,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json
index d15a74a2cae23..db66651728868 100644
--- a/packages/typehierarchy/package.json
+++ b/packages/typehierarchy/package.json
@@ -1,11 +1,11 @@
{
"name": "@theia/typehierarchy",
- "version": "0.4.0",
+ "version": "0.5.0",
"description": "Theia - Type Hierarchy Extension",
"dependencies": {
- "@theia/core": "^0.4.0",
- "@theia/editor": "^0.4.0",
- "@theia/languages": "^0.4.0",
+ "@theia/core": "^0.5.0",
+ "@theia/editor": "^0.5.0",
+ "@theia/languages": "^0.5.0",
"@types/uuid": "^3.4.3",
"uuid": "^3.2.1"
},
@@ -38,11 +38,10 @@
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
- "test": "theiaext test",
- "docs": "theiaext docs"
+ "test": "theiaext test"
},
"devDependencies": {
- "@theia/ext-scripts": "^0.4.0"
+ "@theia/ext-scripts": "^0.5.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
diff --git a/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts b/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts
index 6a511a984c50c..11c2ac019ea63 100644
--- a/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts
+++ b/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts
@@ -131,6 +131,10 @@ export namespace TypeHierarchyTree {
export function create(item: TypeHierarchyItem, direction: TypeHierarchyDirection, resolved: boolean = true): Node {
const items = TypeHierarchyDirection.Children === direction ? item.children : item.parents;
+ if (items && items.length > 0) {
+ // If the server sent more levels than requested, use them.
+ resolved = true;
+ }
const node = {
id: v4(),
name: item.name,
diff --git a/packages/typehierarchy/src/browser/typehierarchy-contribution.ts b/packages/typehierarchy/src/browser/typehierarchy-contribution.ts
index 8038606b19715..74ee7ae797648 100644
--- a/packages/typehierarchy/src/browser/typehierarchy-contribution.ts
+++ b/packages/typehierarchy/src/browser/typehierarchy-contribution.ts
@@ -44,7 +44,7 @@ export class TypeHierarchyContribution extends AbstractViewContribution boolean = require('valid-filename');
@@ -136,6 +137,15 @@ export class FileMenuContribution implements MenuContribution {
registry.registerMenuAction(CommonMenus.FILE_NEW, {
commandId: WorkspaceCommands.NEW_FOLDER.id
});
+ const downloadUploadMenu = [...CommonMenus.FILE, '4_downloadupload'];
+ registry.registerMenuAction(downloadUploadMenu, {
+ commandId: FileDownloadCommands.UPLOAD.id,
+ order: 'a'
+ });
+ registry.registerMenuAction(downloadUploadMenu, {
+ commandId: FileDownloadCommands.DOWNLOAD.id,
+ order: 'b'
+ });
}
}
@@ -172,11 +182,13 @@ export class WorkspaceCommandContribution implements CommandContribution {
const parentUri = new URI(parent.uri);
const { fileName, fileExtension } = this.getDefaultFileConfig();
const vacantChildUri = FileSystemUtils.generateUniqueResourceURI(parentUri, parent, fileName, fileExtension);
+
const dialog = new SingleTextInputDialog({
title: 'New File',
initialValue: vacantChildUri.path.base,
- validate: name => this.validateFileName(name, parent)
+ validate: name => this.validateFileName(name, parent, true)
});
+
dialog.open().then(name => {
if (name) {
const fileUri = parentUri.resolve(name);
@@ -196,7 +208,7 @@ export class WorkspaceCommandContribution implements CommandContribution {
const dialog = new SingleTextInputDialog({
title: 'New Folder',
initialValue: vacantChildUri.path.base,
- validate: name => this.validateFileName(name, parent)
+ validate: name => this.validateFileName(name, parent, true)
});
dialog.open().then(name => {
if (name) {
@@ -230,12 +242,12 @@ export class WorkspaceCommandContribution implements CommandContribution {
if (initialValue === name && mode === 'preview') {
return false;
}
- return this.validateFileName(name, parent);
+ return this.validateFileName(name, parent, false);
}
});
dialog.open().then(name => {
if (name) {
- this.fileSystem.move(uri.toString(), uri.parent.resolve(name).toString());
+ this.fileSystem.move(uri.toString(), uri.parent.resolve(name).toString(), { overwrite: true });
}
});
}
@@ -298,21 +310,40 @@ export class WorkspaceCommandContribution implements CommandContribution {
*
* @param name the simple file name of the file to validate.
* @param parent the parent directory's file stat.
+ * @param recursive allow file or folder creation using recursive path
*/
- protected validateFileName(name: string, parent: FileStat): string {
- if (!validFilename(name)) {
- return 'Invalid name, try other';
+ protected async validateFileName(name: string, parent: FileStat, recursive: boolean = false): Promise {
+ if (!name) {
+ return '';
}
- if (parent.children) {
- for (const child of parent.children) {
- if (new URI(child.uri).path.base === name) {
- return 'A file with this name already exists.';
- }
- }
+ // do not allow recursive rename
+ if (!recursive && !validFilename(name)) {
+ return 'Invalid file or folder name';
+ }
+ if (name.startsWith('/')) {
+ return 'Absolute paths or names that starts with / are not allowed';
+ } else if (name.startsWith(' ') || name.endsWith(' ')) {
+ return 'Names with leading or trailing whitespaces are not allowed';
+ }
+ // check and validate each sub-paths
+ if (name.split(/[\\/]/).some(file => !file || !validFilename(file) || /^\s+$/.test(file))) {
+ return `The name ${this.trimFileName(name)} is not a valid file or folder name.`;
+ }
+ const childUri = new URI(parent.uri).resolve(name).toString();
+ const exists = await this.fileSystem.exists(childUri);
+ if (exists) {
+ return `A file or folder ${this.trimFileName(name)} already exists at this location.`;
}
return '';
}
+ protected trimFileName(name: string): string {
+ if (name && name.length > 30) {
+ return `${name.substr(0, 30)}...`;
+ }
+ return name;
+ }
+
protected async getDirectory(candidate: URI): Promise {
const stat = await this.fileSystem.getFileStat(candidate.toString());
if (stat && stat.isDirectory) {
@@ -390,7 +421,6 @@ export class WorkspaceCommandContribution implements CommandContribution {
}
return false;
}
-
}
export class WorkspaceRootUriAwareCommandHandler extends UriAwareCommandHandler {
diff --git a/packages/workspace/src/browser/workspace-service.spec.ts b/packages/workspace/src/browser/workspace-service.spec.ts
index 140752f856278..5d7af9aa37509 100644
--- a/packages/workspace/src/browser/workspace-service.spec.ts
+++ b/packages/workspace/src/browser/workspace-service.spec.ts
@@ -33,6 +33,7 @@ import { createMockPreferenceProxy } from '@theia/core/lib/browser/preferences/t
import * as jsoncparser from 'jsonc-parser';
import * as sinon from 'sinon';
import * as chai from 'chai';
+import * as assert from 'assert';
import URI from '@theia/core/lib/common/uri';
const expect = chai.expect;
@@ -483,43 +484,6 @@ describe('WorkspaceService', () => {
await wsService.addRoot(new URI(folderB.uri));
expect(spyWriteFile.calledWith(workspaceFileStat, { folders: [{ path: folderA.uri }, { path: folderB.uri }] })).to.be.true;
});
-
- [true, false].forEach(existTemporaryWorkspaceFile => {
- it('should write workspace data into a temporary file when theia currently uses a folder as the workspace ' +
- `and the temporary file ${existTemporaryWorkspaceFile ? 'exists' : 'does not exist'}`, async () => {
- const stubSave = sinon.stub(wsService, 'save').callsFake(() => { });
- const stubWriteWorkspaceFile = sinon.stub(wsService, 'writeWorkspaceFile').callsFake(() => { });
- toRestore.push(...[stubSave, stubWriteWorkspaceFile]);
- wsService['_workspace'] = folderA;
- wsService['_roots'] = [folderA];
- const homeStat = {
- uri: 'file:///home/user',
- lastModification: 0,
- isDirectory: true
- };
- const untitledStat = {
- uri: 'file:///home/user/.theia/Untitled.theia-workspace',
- lastModification: 0,
- isDirectory: true
- };
- (mockFilesystem.getCurrentUserHome).resolves(homeStat);
- const stubGetFileStat = mockFilesystem.getFileStat;
- stubGetFileStat.onCall(0).resolves(folderB);
- (mockFilesystem.exists).resolves(existTemporaryWorkspaceFile);
- const stubCreateFile = mockFilesystem.createFile;
- stubCreateFile.resolves(untitledStat);
- if (existTemporaryWorkspaceFile) {
- stubGetFileStat.onCall(1).resolves(untitledStat);
- }
- wsService['_workspace'] = folderA;
- wsService['_roots'] = [folderA];
-
- await wsService.addRoot(new URI(folderB.uri));
- expect(stubCreateFile.calledWith(untitledStat.uri)).to.eq(!existTemporaryWorkspaceFile);
- expect(stubSave.calledWith(untitledStat)).to.be.true;
- expect(stubWriteWorkspaceFile.called).to.be.true;
- });
- });
});
describe('save() function', () => {
@@ -725,6 +689,59 @@ describe('WorkspaceService', () => {
});
});
+ describe('spliceRoots', () => {
+ const workspace = { uri: 'file:///workspace.theia-workspace', isDirectory: false };
+ const fooDir = { uri: 'file:///foo', isDirectory: true };
+ const workspaceService: WorkspaceService = new WorkspaceService();
+ workspaceService['getUntitledWorkspace'] = async () => new URI('file:///untitled.theia-workspace');
+ workspaceService['save'] = async () => { };
+ workspaceService['getWorkspaceDataFromFile'] = async () => ({ folders: [] });
+ workspaceService['writeWorkspaceFile'] = async (_, data) => {
+ workspaceService['_roots'] = data.folders.map(({ path }) => { uri: path });
+ return undefined;
+ };
+ const assertRemoved = (removed: URI[], ...roots: string[]) =>
+ assert.deepEqual(removed.map(uri => uri.toString()), roots);
+ const assertRoots = (...roots: string[]) =>
+ assert.deepEqual(workspaceService['_roots'].map(root => root.uri), roots);
+
+ beforeEach(() => {
+ workspaceService['_workspace'] = workspace;
+ workspaceService['_roots'] = [fooDir];
+ });
+
+ it('skip', async () => {
+ assertRemoved(await workspaceService.spliceRoots(0, 0));
+ assertRoots('file:///foo');
+ });
+
+ it('add', async () => {
+ assertRemoved(await workspaceService.spliceRoots(1, 0, new URI('file:///bar')));
+ assertRoots('file:///foo', 'file:///bar');
+ });
+
+ it('add dups', async () => {
+ assertRemoved(await workspaceService.spliceRoots(1, 0, new URI('file:///bar'), new URI('file:///baz'), new URI('file:///bar')));
+ assertRoots('file:///foo', 'file:///bar', 'file:///baz');
+ });
+
+ it('remove', async () => {
+ assertRemoved(await workspaceService.spliceRoots(0, 1), 'file:///foo');
+ assertRoots();
+ });
+
+ it('update', async () => {
+ assertRemoved(await workspaceService.spliceRoots(0, 1, new URI('file:///bar')), 'file:///foo');
+ assertRoots('file:///bar');
+ });
+
+ it('add untitled', async () => {
+ workspaceService['_workspace'] = fooDir;
+ assertRemoved(await workspaceService.spliceRoots(1, 0, new URI('file:///bar')));
+ assertRoots('file:///foo', 'file:///bar');
+ });
+ });
+
describe('getWorkspaceRootUri() function', () => {
it('should return undefined if no uri is passed into the function', () => {
expect(wsService.getWorkspaceRootUri(undefined)).to.be.undefined;
diff --git a/packages/workspace/src/browser/workspace-service.ts b/packages/workspace/src/browser/workspace-service.ts
index 697bca3a11eec..1eb008c301cb9 100644
--- a/packages/workspace/src/browser/workspace-service.ts
+++ b/packages/workspace/src/browser/workspace-service.ts
@@ -301,27 +301,7 @@ export class WorkspaceService implements FrontendApplicationContribution {
* @param uri URI of the root folder being added
*/
async addRoot(uri: URI): Promise {
- await this.roots;
-
- if (!this.opened) {
- throw new Error('Folder cannot be added as there is no active workspace or opened folder.');
- }
- const valid = await this.toValidRoot(uri);
- if (!valid) {
- throw new Error(`Invalid workspace root URI. Expected an existing directory location. URI: ${uri.toString()}.`);
- }
-
- if (this._workspace && !this._roots.find(r => r.uri === valid.uri)) {
- if (this._workspace.isDirectory) { // save the workspace data in a temporary file
- const tempFile = await this.getTemporaryWorkspaceFile();
- if (tempFile) {
- await this.save(tempFile);
- }
- }
- const workspaceData = await this.getWorkspaceDataFromFile();
- this._workspace = await this.writeWorkspaceFile(this._workspace,
- WorkspaceData.buildWorkspaceData([...this._roots, valid], workspaceData ? workspaceData.settings : undefined));
- }
+ await this.spliceRoots(this._roots.length, 0, uri);
}
/**
@@ -342,6 +322,41 @@ export class WorkspaceService implements FrontendApplicationContribution {
}
}
+ async spliceRoots(start: number, deleteCount?: number, ...rootsToAdd: URI[]): Promise {
+ if (!this._workspace) {
+ throw new Error('There is not active workspace');
+ }
+ const dedup = new Set();
+ const roots = this._roots.map(root => (dedup.add(root.uri), root.uri));
+ const toAdd: string[] = [];
+ for (const root of rootsToAdd) {
+ const uri = root.toString();
+ if (!dedup.has(uri)) {
+ dedup.add(uri);
+ toAdd.push(uri);
+ }
+ }
+ const toRemove = roots.splice(start, deleteCount || 0, ...toAdd);
+ if (!toRemove.length && !toAdd.length) {
+ return [];
+ }
+ if (this._workspace.isDirectory) {
+ const utitledWorkspace = await this.getUntitledWorkspace();
+ if (utitledWorkspace) {
+ await this.save(utitledWorkspace);
+ }
+ }
+ const currentData = await this.getWorkspaceDataFromFile();
+ const newData = WorkspaceData.buildWorkspaceData(roots, currentData && currentData.settings);
+ await this.writeWorkspaceFile(this._workspace, newData);
+ return toRemove.map(root => new URI(root));
+ }
+
+ protected async getUntitledWorkspace(): Promise {
+ const home = await this.fileSystem.getCurrentUserHome();
+ return home && getTemporaryWorkspaceFileUri(new URI(home.uri));
+ }
+
private async writeWorkspaceFile(workspaceFile: FileStat | undefined, workspaceData: WorkspaceData): Promise {
if (workspaceFile) {
const data = JSON.stringify(WorkspaceData.transformToRelative(workspaceData, workspaceFile));
@@ -352,17 +367,6 @@ export class WorkspaceService implements FrontendApplicationContribution {
}
}
- private async getTemporaryWorkspaceFile(): Promise {
- const home = await this.fileSystem.getCurrentUserHome();
- if (home) {
- const tempWorkspaceUri = getTemporaryWorkspaceFileUri(new URI(home.uri));
- if (!await this.fileSystem.exists(tempWorkspaceUri.toString())) {
- return this.fileSystem.createFile(tempWorkspaceUri.toString());
- }
- return this.toFileStat(tempWorkspaceUri);
- }
- }
-
/**
* Clears current workspace root.
*/
diff --git a/tsconfig.json b/tsconfig.json
index 9fd662f6241d0..3e2c0edd6da6a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -111,6 +111,15 @@
],
"@theia/getting-started/lib/*": [
"packages/getting-started/src/*"
+ ],
+ "@theia/file-search/lib/*": [
+ "packages/file-search/src/*"
+ ],
+ "@theia/plugin-ext/lib/*": [
+ "packages/plugin-ext/src/*"
+ ],
+ "@theia/plugin-ext-vscode/lib/*": [
+ "packages/plugin-ext-vscode/src/*"
]
},
"plugins": [
diff --git a/yarn.lock b/yarn.lock
index 46a4b252fd7cc..73c0843a3c671 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -121,6 +121,7 @@
"@theia/node-pty@0.7.8-theia004":
version "0.7.8-theia004"
resolved "https://registry.yarnpkg.com/@theia/node-pty/-/node-pty-0.7.8-theia004.tgz#0fe31b958df9315352d5fbeea7075047cf69c935"
+ integrity sha512-GetaD2p1qVPq/xbNCHCwKYjIr9IWjksf9V2iiv/hV6f885cJ+ie0Osr4+C159PrwzGRYW2jQVUtXghBJoyOCLg==
dependencies:
nan "2.10.0"
@@ -212,15 +213,24 @@
dependencies:
"@types/node" "*"
+"@types/formidable@^1.0.31":
+ version "1.0.31"
+ resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-1.0.31.tgz#274f9dc2d0a1a9ce1feef48c24ca0859e7ec947b"
+ integrity sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==
+ dependencies:
+ "@types/events" "*"
+ "@types/node" "*"
+
"@types/fs-extra@^4.0.2":
version "4.0.8"
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.8.tgz#6957ddaf9173195199cb96da3db44c74700463d2"
dependencies:
"@types/node" "*"
-"@types/fs-extra@^5.0.3":
- version "5.0.4"
- resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.4.tgz#b971134d162cc0497d221adde3dbb67502225599"
+"@types/fs-extra@^5.0.5":
+ version "5.0.5"
+ resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.5.tgz#080d90a792f3fa2c5559eb44bd8ef840aae9104b"
+ integrity sha512-w7iqhDH9mN8eLClQOYTkhdYUOSpp25eXxfc6VbFOGtzxW34JcvctH2bKjj4jD4++z4R5iO5D+pg48W2e03I65A==
dependencies:
"@types/node" "*"
@@ -232,10 +242,6 @@
"@types/minimatch" "*"
"@types/node" "*"
-"@types/handlebars@^4.0.38":
- version "4.0.39"
- resolved "https://registry.yarnpkg.com/@types/handlebars/-/handlebars-4.0.39.tgz#961fb54db68030890942e6aeffe9f93a957807bd"
-
"@types/highlight.js@^9.12.2", "@types/highlight.js@^9.12.3":
version "9.12.3"
resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca"
@@ -271,9 +277,10 @@
version "4.14.116"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.116.tgz#5ccf215653e3e8c786a58390751033a9adca0eb9"
-"@types/lodash@^4.14.110":
- version "4.14.118"
- resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.118.tgz#247bab39bfcc6d910d4927c6e06cbc70ec376f27"
+"@types/lodash@^4.14.123":
+ version "4.14.123"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.123.tgz#39be5d211478c8dd3bdae98ee75bb7efe4abfe4d"
+ integrity sha512-pQvPkc4Nltyx7G1Ww45OjVqUsJP4UsZm+GWJpigXgkikZqJgRm4c48g027o6tdgubWHwFRF15iFd+Y4Pmqv6+Q==
"@types/markdown-it-anchor@^4.0.1":
version "4.0.1"
@@ -289,9 +296,10 @@
version "0.0.4"
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.4.tgz#c5f67365916044b342dae8d702724788ba0b5b74"
-"@types/marked@^0.4.0":
- version "0.4.2"
- resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.4.2.tgz#64a89e53ea37f61cc0f3ee1732c555c2dbf6452f"
+"@types/marked@^0.6.0":
+ version "0.6.4"
+ resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.6.4.tgz#7c1238662976dde4c74f89c0a94c573474edae02"
+ integrity sha512-rJe6ToeWAUmR6I7eY87gxb2CTwXKqfwfrkDAr9ododuha9D/d57DWWK0xdtKZ2C6yTdP60pAg5fJwC5PHguzyA==
"@types/mime-types@^2.1.0":
version "2.1.0"
@@ -313,14 +321,15 @@
version "10.7.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.7.1.tgz#b704d7c259aa40ee052eec678758a68d07132a2e"
-"@types/node@8.10.20":
- version "8.10.20"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.20.tgz#fe674ea52e13950ab10954433a7824438aabbcac"
-
"@types/node@^8.0.24", "@types/node@^8.0.26":
version "8.10.26"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.26.tgz#950e3d4e6b316ba6e1ae4e84d9155aba67f88c2f"
+"@types/node@~10.3.6":
+ version "10.3.6"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.3.6.tgz#ea8aab9439b59f40d19ec5f13b44642344872b11"
+ integrity sha512-h7VDRFL8IhdPw1JjiNVvhr+WynfKW09q2BOflIOA0yeuXNeXBP1bIRuBrysSryH4keaJ5bYUNp63aIyQL9YpDQ==
+
"@types/p-debounce@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/p-debounce/-/p-debounce-1.0.0.tgz#c7fab3d61f9bc6454337c4aef0dec069456d00ee"
@@ -395,9 +404,10 @@
"@types/express-serve-static-core" "*"
"@types/mime" "*"
-"@types/shelljs@^0.8.0":
- version "0.8.0"
- resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.0.tgz#0caa56b68baae4f68f44e0dd666ab30b098e3632"
+"@types/shelljs@^0.8.3":
+ version "0.8.4"
+ resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.4.tgz#b903e41ad5e5195b7d5e34d3cd33c94bdbd07895"
+ integrity sha512-UNACC6scKFVljWEvO3rHBkbbKXu3QkKPBOMCisxI7au9cnFK7tjOGPsKh5OjedAPLmtsKSarmk+YeehKTQSKtg==
dependencies:
"@types/glob" "*"
"@types/node" "*"
@@ -929,12 +939,19 @@ async@1.x, async@^1.4.0, async@^1.5.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
-async@^2.0.0, async@^2.1.4, async@^2.5.0, async@^2.6.0:
+async@^2.0.0, async@^2.5.0, async@^2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
dependencies:
lodash "^4.17.10"
+async@^2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381"
+ integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==
+ dependencies:
+ lodash "^4.17.11"
+
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -1502,7 +1519,7 @@ babel-register@^6.26.0, babel-register@^6.9.0:
mkdirp "^0.5.1"
source-map-support "^0.4.15"
-babel-runtime@6.26.0, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0, babel-runtime@~6.26.0:
+babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0, babel-runtime@~6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
@@ -1554,6 +1571,13 @@ back@~0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/back/-/back-0.1.5.tgz#342b96b804657b03ec9a31f248a11f200608dcc2"
+backbone@^1.1.2:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.4.0.tgz#54db4de9df7c3811c3f032f34749a4cd27f3bd12"
+ integrity sha512-RLmDrRXkVdouTg38jcgHhyQ/2zjg7a8E6sz2zxfz21Hh17xDJYUHBZimVIt5fUyS8vbfpeSmTL3gUjTEvUV3qQ==
+ dependencies:
+ underscore ">=1.8.3"
+
balanced-match@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
@@ -1630,6 +1654,14 @@ bl@^1.0.0:
readable-stream "^2.3.5"
safe-buffer "^5.1.1"
+bl@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.0.tgz#e1a574cdf528e4053019bb800b041c0ac88da493"
+ integrity sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==
+ dependencies:
+ readable-stream "^2.3.5"
+ safe-buffer "^5.1.1"
+
block-stream@*:
version "0.0.9"
resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
@@ -1719,6 +1751,7 @@ browser-stdout@1.3.0:
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
+ integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.2.0"
@@ -2066,7 +2099,6 @@ check-error@^1.0.1:
checksum@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/checksum/-/checksum-0.1.1.tgz#dc6527d4c90be8560dbd1ed4cecf3297d528e9e9"
- integrity sha1-3GUn1MkL6FYNvR7Uzs8yl9Uo6ek=
dependencies:
optimist "~0.3.5"
@@ -2093,11 +2125,6 @@ chownr@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
-chownr@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494"
- integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==
-
chrome-trace-event@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48"
@@ -2363,13 +2390,14 @@ command-join@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/command-join/-/command-join-2.0.0.tgz#52e8b984f4872d952ff1bdc8b98397d27c7144cf"
-commander@*, commander@^2.11.0, commander@^2.12.1, commander@^2.8.1, commander@^2.9.0:
+commander@*, commander@^2.11.0, commander@^2.12.1, commander@^2.8.1:
version "2.17.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
commander@2.15.1:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
+ integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
commander@2.6.0:
version "2.6.0"
@@ -2381,10 +2409,20 @@ commander@2.9.0:
dependencies:
graceful-readlink ">= 1.0.0"
+commander@^2.19.0:
+ version "2.19.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
+ integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
+
commander@~2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
+commander@~2.19.0:
+ version "2.19.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
+ integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
+
commander@~2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
@@ -2445,6 +2483,7 @@ concurrently@^3.5.0:
conf@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/conf/-/conf-2.2.0.tgz#ee282efafc1450b61e205372041ad7d866802d9a"
+ integrity sha512-93Kz74FOMo6aWRVpAZsonOdl2I57jKtHrNmxhumehFQw4X8Sk37SohNY11PG7Q8Okta+UnrVaI006WLeyp8/XA==
dependencies:
dot-prop "^4.1.0"
env-paths "^1.0.0"
@@ -2822,6 +2861,7 @@ css-loader@~0.26.1:
css-parse@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4"
+ integrity sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=
dependencies:
css "^2.0.0"
@@ -2993,15 +3033,16 @@ debug@2.6.9, debug@^2.1.2, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.5.
dependencies:
ms "2.0.0"
-debug@3.1.0, debug@^3.1.0:
+debug@3.1.0, debug@^3.0.0, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
-debug@^4.0.0:
+debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
+ integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
dependencies:
ms "^2.1.1"
@@ -3272,6 +3313,7 @@ dot-prop@^3.0.0:
dot-prop@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
+ integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
dependencies:
is-obj "^1.0.0"
@@ -3304,6 +3346,7 @@ dugite-extra@0.1.11:
dugite-no-gpl@1.69.0:
version "1.69.0"
resolved "https://registry.yarnpkg.com/dugite-no-gpl/-/dugite-no-gpl-1.69.0.tgz#bc9007cf5a595180f563ccc0e4f2cc80ebbaa52e"
+ integrity sha512-9NzPMyWW1uWEm+rEGivfQ0+zZ9soXrtk/zb6FIVpPa5CLoUdhMkLY4jHc0DDyayarxivJgrI/rHDdTUej4Zhrw==
dependencies:
checksum "^0.1.1"
mkdirp "^0.5.1"
@@ -3369,19 +3412,20 @@ ejs@~2.5.6:
version "2.5.9"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5"
-electron-download@^3.0.1:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-3.3.0.tgz#2cfd54d6966c019c4d49ad65fbe65cc9cdef68c8"
+electron-download@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.1.tgz#02e69556705cc456e520f9e035556ed5a015ebe8"
+ integrity sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==
dependencies:
- debug "^2.2.0"
- fs-extra "^0.30.0"
- home-path "^1.0.1"
+ debug "^3.0.0"
+ env-paths "^1.0.0"
+ fs-extra "^4.0.1"
minimist "^1.2.0"
- nugget "^2.0.0"
- path-exists "^2.1.0"
- rc "^1.1.2"
- semver "^5.3.0"
- sumchecker "^1.2.0"
+ nugget "^2.0.1"
+ path-exists "^3.0.0"
+ rc "^1.2.1"
+ semver "^5.4.1"
+ sumchecker "^2.0.2"
electron-mocha@~3.5.0:
version "3.5.0"
@@ -3411,6 +3455,7 @@ electron-rebuild@^1.5.11:
electron-store@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-2.0.0.tgz#1035cca2a95409d1f54c7466606345852450d64a"
+ integrity sha512-1WCFYHsYvZBqDsoaS0Relnz0rd81ZkBAI0Fgx7Nq2UWU77rSNs1qxm4S6uH7TCZ0bV3LQpJFk7id/is/ZgoOPA==
dependencies:
conf "^2.0.0"
@@ -3424,12 +3469,13 @@ electron-window@^0.8.0:
dependencies:
is-electron-renderer "^2.0.0"
-electron@^2.0.14:
- version "2.0.14"
- resolved "https://registry.yarnpkg.com/electron/-/electron-2.0.14.tgz#fad6766645e7c0cd10b4ae822d3167959735a870"
+electron@^3.1.7:
+ version "3.1.7"
+ resolved "https://registry.yarnpkg.com/electron/-/electron-3.1.7.tgz#2041031db272e88f167b2e5fe2de9eecabcf4632"
+ integrity sha512-rvmucnAsB4hQVdD0fOd1ad7+5u/BX1ak6emcSyPsLUk6rTqvVfOMk5ryC19h7Yd/5X8NWvCGkgYzSyQbgAJngA==
dependencies:
"@types/node" "^8.0.24"
- electron-download "^3.0.1"
+ electron-download "^4.1.0"
extract-zip "^1.0.3"
elegant-spinner@^1.0.1:
@@ -3468,9 +3514,10 @@ encoding@^0.1.11:
dependencies:
iconv-lite "~0.4.13"
-end-of-stream@^1.0.0, end-of-stream@^1.1.0:
+end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
+ integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
dependencies:
once "^1.4.0"
@@ -3489,6 +3536,7 @@ entities@^1.1.1, entities@~1.1.1:
env-paths@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0"
+ integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=
env-variable@0.0.x:
version "0.0.4"
@@ -3513,7 +3561,7 @@ error@^7.0.2:
string-template "~0.2.1"
xtend "~4.0.0"
-es6-promise@^4.0.3, es6-promise@^4.0.5, es6-promise@^4.2.4:
+es6-promise@^4.0.3, es6-promise@^4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
@@ -3888,9 +3936,12 @@ fecha@^2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd"
-fibers@~2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/fibers/-/fibers-2.0.2.tgz#36db63ea61c543174e2264675fea8c2783371366"
+fibers@^3.0.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/fibers/-/fibers-3.1.1.tgz#0238902ca938347bd779523692fbeefdf4f688ab"
+ integrity sha512-dl3Ukt08rHVQfY8xGD0ODwyjwrRALtaghuqGH2jByYX1wpY+nAnRQjJ6Dbqq0DnVgNVQ9yibObzbF4IlPyiwPw==
+ dependencies:
+ detect-libc "^1.0.3"
figures@^1.7.0:
version "1.7.0"
@@ -3992,6 +4043,7 @@ find-git-exec@0.0.1-alpha.2, find-git-exec@^0.0.1-alpha.2:
find-git-repositories@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/find-git-repositories/-/find-git-repositories-0.1.0.tgz#1ac886f0f54a11f5f073bca3bcdfddc03486305a"
+ integrity sha1-GsiG8PVKEfXwc7yjvN/dwDSGMFo=
dependencies:
nan "^2.0.0"
@@ -4082,6 +4134,11 @@ formatio@1.2.0:
dependencies:
samsam "1.x"
+formidable@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659"
+ integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==
+
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@@ -4111,16 +4168,6 @@ fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
-fs-extra@^0.26.5:
- version "0.26.7"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.26.7.tgz#9ae1fdd94897798edab76d0918cf42d0c3184fa9"
- dependencies:
- graceful-fs "^4.1.2"
- jsonfile "^2.1.0"
- klaw "^1.0.0"
- path-is-absolute "^1.0.0"
- rimraf "^2.2.8"
-
fs-extra@^0.30.0:
version "0.30.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0"
@@ -4155,6 +4202,15 @@ fs-extra@^7.0.0:
jsonfile "^4.0.0"
universalify "^0.1.0"
+fs-extra@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
+ integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
+ dependencies:
+ graceful-fs "^4.1.2"
+ jsonfile "^4.0.0"
+ universalify "^0.1.0"
+
fs-minipass@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
@@ -4531,6 +4587,7 @@ graceful-fs@^4.1.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2
grapheme-splitter@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
+ integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
grouped-queue@^0.3.3:
version "0.3.3"
@@ -4541,6 +4598,7 @@ grouped-queue@^0.3.3:
growl@1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
+ integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
growl@1.9.2:
version "1.9.2"
@@ -4575,7 +4633,7 @@ gulplog@^1.0.0:
dependencies:
glogg "^1.0.0"
-handlebars@^4.0.1, handlebars@^4.0.11, handlebars@^4.0.2, handlebars@^4.0.6:
+handlebars@^4.0.1, handlebars@^4.0.11, handlebars@^4.0.2:
version "4.0.11"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
dependencies:
@@ -4585,6 +4643,17 @@ handlebars@^4.0.1, handlebars@^4.0.11, handlebars@^4.0.2, handlebars@^4.0.6:
optionalDependencies:
uglify-js "^2.6"
+handlebars@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.1.tgz#6e4e41c18ebe7719ae4d38e5aca3d32fa3dd23d3"
+ integrity sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA==
+ dependencies:
+ neo-async "^2.6.0"
+ optimist "^0.6.1"
+ source-map "^0.6.1"
+ optionalDependencies:
+ uglify-js "^3.1.4"
+
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
@@ -4693,10 +4762,15 @@ he@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
-highlight.js@^9.0.0, highlight.js@^9.12.0:
+highlight.js@^9.12.0:
version "9.12.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
+highlight.js@^9.13.1:
+ version "9.15.6"
+ resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.6.tgz#72d4d8d779ec066af9a17cb14360c3def0aa57c4"
+ integrity sha512-zozTAWM1D6sozHo8kqhfYgsac+B+q0PmsjXeyDrYIHHcBN0zTVT66+s2GW1GZv7DbyaROdLXKdabwS/WqPyIdQ==
+
hmac-drbg@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@@ -4712,10 +4786,6 @@ home-or-tmp@^2.0.0:
os-homedir "^1.0.0"
os-tmpdir "^1.0.1"
-home-path@^1.0.1:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/home-path/-/home-path-1.0.6.tgz#d549dc2465388a7f8667242c5b31588d29af29fc"
-
homedir-polyfill@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc"
@@ -4806,6 +4876,7 @@ https-proxy-agent@^2.2.1:
humanize-duration@~3.15.0:
version "3.15.3"
resolved "https://registry.yarnpkg.com/humanize-duration/-/humanize-duration-3.15.3.tgz#600a939bd9d9a16b696e907b3fc08d1a4f15e8c9"
+ integrity sha512-BMz6w8p3NVa6QP9wDtqUkXfwgBqDaZ5z/np0EYdoWrLqL849Onp6JWMXMhbHtuvO9jUThLN5H1ThRQ8dUWnYkA==
iconv-lite@0.4.19:
version "0.4.19"
@@ -5349,6 +5420,11 @@ isurl@^1.0.0-alpha5:
has-to-string-tag-x "^1.2.0"
is-object "^1.0.1"
+jquery@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.4.tgz#2c89d6889b5eac522a7eea32c14521559c6cbf02"
+ integrity sha1-LInWiJterFIqfuoywUUhVZxsvwI=
+
js-base64@^2.1.9:
version "2.4.8"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.8.tgz#57a9b130888f956834aa40c5b165ba59c758f033"
@@ -5505,6 +5581,7 @@ jsonc-parser@^2.0.0-next.1, jsonc-parser@^2.0.1:
jsonc-parser@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.2.tgz#42fcf56d70852a043fadafde51ddb4a85649978d"
+ integrity sha512-TSU435K5tEKh3g7bam1AFf+uZrISheoDsLlpmAo6wWZYqjsnd09lHYK1Qo+moK4Ikifev1Gdpa69g4NELKnCrQ==
jsonfile@^2.1.0:
version "2.4.0"
@@ -5667,6 +5744,7 @@ less-loader@~2.2.3:
less@^3.0.3:
version "3.9.0"
resolved "https://registry.yarnpkg.com/less/-/less-3.9.0.tgz#b7511c43f37cf57dc87dffd9883ec121289b1474"
+ integrity sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==
dependencies:
clone "^2.1.2"
optionalDependencies:
@@ -5975,6 +6053,11 @@ lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5,
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
+lodash@^4.17.11:
+ version "4.17.11"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
+ integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
+
log-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
@@ -6040,6 +6123,11 @@ lru-cache@^4.0.0, lru-cache@^4.0.1, lru-cache@^4.1.1:
pseudomap "^1.0.2"
yallist "^2.1.2"
+lunr@^2.3.6:
+ version "2.3.6"
+ resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.6.tgz#f278beee7ffd56ad86e6e478ce02ab2b98c78dd5"
+ integrity sha512-swStvEyDqQ85MGpABCMBclZcLI/pBIlu8FFDtmX197+oEgKloJ67QnB+Tidh0340HmLMs39c4GrkPY3cmkXp6Q==
+
make-dir@^1.0.0, make-dir@^1.1.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
@@ -6088,9 +6176,10 @@ markdown-it@^8.4.0:
mdurl "^1.0.1"
uc.micro "^1.0.5"
-marked@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/marked/-/marked-0.4.0.tgz#9ad2c2a7a1791f10a852e0112f77b571dce10c66"
+marked@^0.6.0:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/marked/-/marked-0.6.1.tgz#a63addde477bca9613028de4b2bc3629e53a0562"
+ integrity sha512-+H0L3ibcWhAZE02SKMqmvYsErLo4EAVJxu5h3bHBBDvvjeWXtl92rGUSBYHL2++5Y+RSNgl8dYOAXcYe7lp1fA==
math-expression-evaluator@^1.2.14:
version "1.2.17"
@@ -6269,6 +6358,7 @@ mime@1.4.1:
mime@^1.4.1:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+ integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
mime@^2.0.3:
version "2.3.1"
@@ -6326,27 +6416,12 @@ minipass@^2.2.1, minipass@^2.3.3:
safe-buffer "^5.1.2"
yallist "^3.0.0"
-minipass@^2.3.4:
- version "2.3.5"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
- integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==
- dependencies:
- safe-buffer "^5.1.2"
- yallist "^3.0.0"
-
minizlib@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb"
dependencies:
minipass "^2.2.1"
-minizlib@^1.1.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614"
- integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==
- dependencies:
- minipass "^2.2.1"
-
mississippi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f"
@@ -6392,9 +6467,10 @@ mocha@^3.4.2:
mkdirp "0.5.1"
supports-color "3.1.2"
-mocha@^5.0.0:
+mocha@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
+ integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
dependencies:
browser-stdout "1.3.1"
commander "2.15.1"
@@ -6558,6 +6634,11 @@ neo-async@^2.5.0:
version "2.5.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.2.tgz#489105ce7bc54e709d736b195f82135048c50fcc"
+neo-async@^2.6.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835"
+ integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==
+
nice-try@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
@@ -6572,15 +6653,10 @@ nise@^1.0.1:
path-to-regexp "^1.7.0"
text-encoding "^0.6.4"
-node-abi@^2.0.0:
- version "2.4.3"
- resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.4.3.tgz#43666b7b17e57863e572409edbb82115ac7af28b"
- dependencies:
- semver "^5.4.1"
-
-node-abi@^2.2.0:
- version "2.4.4"
- resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.4.4.tgz#410d8968809fe616dc078a181c44a370912f12fd"
+node-abi@^2.0.0, node-abi@^2.2.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.5.0.tgz#942e1a78bce764bc0c1672d5821e492b9d032052"
+ integrity sha512-9g2twBGSP6wIR5PW7tXvAWnEWKJDH/VskdXp168xsw9VVxpEGov8K4jsP4/VeoC7b2ZAyzckvMCuQuQlw44lXg==
dependencies:
semver "^5.4.1"
@@ -6655,12 +6731,6 @@ node-pre-gyp@^0.10.0:
semver "^5.3.0"
tar "^4"
-nodegit-promise@~4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/nodegit-promise/-/nodegit-promise-4.0.0.tgz#5722b184f2df7327161064a791d2e842c9167b34"
- dependencies:
- asap "~2.0.3"
-
nomnom@^1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7"
@@ -6763,9 +6833,20 @@ npm-run-path@^2.0.0:
gauge "~2.7.3"
set-blocking "~2.0.0"
-nugget@^2.0.0:
+nsfw@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/nsfw/-/nsfw-1.2.2.tgz#95b79b6b0e311268aaa20c5c085b9f3b341b0769"
+ integrity sha512-YwoS39dkrp6loO0gvh61UbQPiOYwmbAiKqWSYuMeoSkpxxy8rbe/RVgxIJ1L+ua5usLGr0FPSo7NEQnDQOGyIw==
+ dependencies:
+ fs-extra "^7.0.0"
+ lodash.isinteger "^4.0.4"
+ lodash.isundefined "^3.0.1"
+ nan "^2.0.0"
+
+nugget@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/nugget/-/nugget-2.0.1.tgz#201095a487e1ad36081b3432fa3cada4f8d071b0"
+ integrity sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=
dependencies:
debug "^2.1.3"
minimist "^1.1.0"
@@ -6929,7 +7010,6 @@ optimist@^0.6.1, optimist@~0.6.1:
optimist@~0.3.5:
version "0.3.7"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.3.7.tgz#c90941ad59e4273328923074d2cf2e7cbc6ec0d9"
- integrity sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=
dependencies:
wordwrap "~0.0.2"
@@ -7146,7 +7226,7 @@ path-dirname@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
-path-exists@^2.0.0, path-exists@^2.1.0:
+path-exists@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
dependencies:
@@ -7271,6 +7351,7 @@ pkg-dir@^2.0.0:
pkg-up@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
+ integrity sha1-yBmscoBZpGHKscOImivjxJoATX8=
dependencies:
find-up "^2.1.0"
@@ -7607,14 +7688,20 @@ progress-stream@^1.1.0:
speedometer "~0.1.2"
through2 "~0.2.3"
-progress@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.1.tgz#c9242169342b1c29d275889c95734621b1952e31"
+progress@2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+ integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
progress@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
+progress@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+ integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+
prom-client@^10.2.0:
version "10.2.3"
resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-10.2.3.tgz#a51bf21c239c954a6c5be4b1361fdd380218bb41"
@@ -7635,12 +7722,6 @@ promised-io@*:
version "0.3.5"
resolved "https://registry.yarnpkg.com/promised-io/-/promised-io-0.3.5.tgz#4ad217bb3658bcaae9946b17a8668ecd851e1356"
-promisify-node@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/promisify-node/-/promisify-node-0.3.0.tgz#b4b55acf90faa7d2b8b90ca396899086c03060cf"
- dependencies:
- nodegit-promise "~4.0.0"
-
prop-types@^15.6.0:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
@@ -7806,7 +7887,7 @@ raw-body@2.3.3:
iconv-lite "0.4.23"
unpipe "1.0.0"
-rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.2.7:
+rc@^1.0.1, rc@^1.1.6, rc@^1.2.1, rc@^1.2.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
dependencies:
@@ -7927,6 +8008,15 @@ readable-stream@1.0.x:
isarray "0.0.1"
string_decoder "~0.10.x"
+readable-stream@^3.1.1:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d"
+ integrity sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==
+ dependencies:
+ inherits "^2.0.3"
+ string_decoder "^1.1.1"
+ util-deprecate "^1.0.1"
+
readable-stream@~1.1.9:
version "1.1.14"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
@@ -8257,6 +8347,7 @@ ret@~0.1.10:
rgb2hex@^0.1.9:
version "0.1.9"
resolved "https://registry.yarnpkg.com/rgb2hex/-/rgb2hex-0.1.9.tgz#5d3e0e14b0177b568e6f0d5b43e34fbfdb670346"
+ integrity sha512-32iuQzhOjyT+cv9aAFRBJ19JgHwzQwbjUhH3Fj2sWW2EEGAW8fpFrDFP5ndoKDxJaLO06x1hE3kyuIFrUQtybQ==
right-align@^0.1.1:
version "0.1.3"
@@ -8410,22 +8501,23 @@ seek-bzip@^1.0.5:
commander "~2.8.1"
selenium-standalone@^6.15.4:
- version "6.15.4"
- resolved "https://registry.yarnpkg.com/selenium-standalone/-/selenium-standalone-6.15.4.tgz#9f9056f625bd7d2558483562b3e8be80947e9faf"
+ version "6.16.0"
+ resolved "https://registry.yarnpkg.com/selenium-standalone/-/selenium-standalone-6.16.0.tgz#ffcf02665c58ff7a7472427ae819ba79c15967ac"
+ integrity sha512-tl7HFH2FOxJD1is7Pzzsl0pY4vuePSdSWiJdPn+6ETBkpeJDiuzou8hBjvWYWpD+eIVcOrmy3L0R3GzkdHLzDw==
dependencies:
- async "^2.1.4"
- commander "^2.9.0"
- cross-spawn "^6.0.0"
- debug "^4.0.0"
- lodash "^4.17.4"
+ async "^2.6.2"
+ commander "^2.19.0"
+ cross-spawn "^6.0.5"
+ debug "^4.1.1"
+ lodash "^4.17.11"
minimist "^1.2.0"
mkdirp "^0.5.1"
- progress "2.0.1"
+ progress "2.0.3"
request "2.88.0"
- tar-stream "1.6.2"
- urijs "^1.18.4"
- which "^1.2.12"
- yauzl "^2.5.0"
+ tar-stream "2.0.0"
+ urijs "^1.19.1"
+ which "^1.3.1"
+ yauzl "^2.10.0"
"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0:
version "5.5.1"
@@ -8542,7 +8634,7 @@ shell-path@^2.0.0:
dependencies:
shell-env "^0.3.0"
-shelljs@^0.8.0, shelljs@^0.8.2:
+shelljs@^0.8.0:
version "0.8.2"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.2.tgz#345b7df7763f4c2340d584abb532c5f752ca9e35"
dependencies:
@@ -8550,6 +8642,15 @@ shelljs@^0.8.0, shelljs@^0.8.2:
interpret "^1.0.0"
rechoir "^0.6.2"
+shelljs@^0.8.3:
+ version "0.8.3"
+ resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097"
+ integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==
+ dependencies:
+ glob "^7.0.0"
+ interpret "^1.0.0"
+ rechoir "^0.6.2"
+
showdown@^1.7.4:
version "1.8.6"
resolved "https://registry.yarnpkg.com/showdown/-/showdown-1.8.6.tgz#91ea4ee3b7a5448aaca6820a4e27e690c6ad771c"
@@ -8921,6 +9022,13 @@ string_decoder@^1.0.0, string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
+string_decoder@^1.1.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d"
+ integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==
+ dependencies:
+ safe-buffer "~5.1.0"
+
string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
@@ -9005,12 +9113,12 @@ style-loader@~0.13.1:
dependencies:
loader-utils "^1.0.2"
-sumchecker@^1.2.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-1.3.1.tgz#79bb3b4456dd04f18ebdbc0d703a1d1daec5105d"
+sumchecker@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-2.0.2.tgz#0f42c10e5d05da5d42eea3e56c3399a37d6c5b3e"
+ integrity sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=
dependencies:
debug "^2.2.0"
- es6-promise "^4.0.5"
supports-color@3.1.2:
version "3.1.2"
@@ -9021,6 +9129,7 @@ supports-color@3.1.2:
supports-color@5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
+ integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==
dependencies:
has-flag "^3.0.0"
@@ -9087,17 +9196,16 @@ tar-fs@^1.13.0, tar-fs@^1.16.2:
pump "^1.0.0"
tar-stream "^1.1.2"
-tar-stream@1.6.2:
- version "1.6.2"
- resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
+tar-stream@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.0.0.tgz#8829bbf83067bc0288a9089db49c56be395b6aea"
+ integrity sha512-n2vtsWshZOVr/SY4KtslPoUlyNh06I2SGgAOCZmquCEjlbV/LjY2CY80rDtdQRHFOYXNlgBDo6Fr3ww2CWPOtA==
dependencies:
- bl "^1.0.0"
- buffer-alloc "^1.2.0"
- end-of-stream "^1.0.0"
+ bl "^2.2.0"
+ end-of-stream "^1.4.1"
fs-constants "^1.0.0"
- readable-stream "^2.3.0"
- to-buffer "^1.1.1"
- xtend "^4.0.0"
+ inherits "^2.0.3"
+ readable-stream "^3.1.1"
tar-stream@^1.1.2, tar-stream@^1.5.0, tar-stream@^1.5.2:
version "1.6.1"
@@ -9119,7 +9227,7 @@ tar@^2.0.0:
fstream "^1.0.2"
inherits "2"
-tar@^4, tar@^4.0.0:
+tar@^4, tar@^4.0.0, tar@^4.0.2:
version "4.4.6"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b"
dependencies:
@@ -9131,19 +9239,6 @@ tar@^4, tar@^4.0.0:
safe-buffer "^5.1.2"
yallist "^3.0.2"
-tar@^4.0.2:
- version "4.4.8"
- resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
- integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==
- dependencies:
- chownr "^1.1.1"
- fs-minipass "^1.2.5"
- minipass "^2.3.4"
- minizlib "^1.1.1"
- mkdirp "^0.5.0"
- safe-buffer "^5.1.2"
- yallist "^3.0.2"
-
tasklist@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/tasklist/-/tasklist-3.1.1.tgz#84cb49f8359b9ed0451dd1d9b6111da18107dbd5"
@@ -9292,7 +9387,7 @@ to-arraybuffer@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
-to-buffer@^1.1.0, to-buffer@^1.1.1:
+to-buffer@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
@@ -9455,31 +9550,42 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
-typedoc-default-themes@^0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.5.0.tgz#6dc2433e78ed8bea8e887a3acde2f31785bd6227"
+typedoc-default-themes@^0.6.0-0:
+ version "0.6.0-0"
+ resolved "https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.6.0-0.tgz#a4867eaf91fb7888efd01680f1328b72e8a33640"
+ integrity sha512-O7hBMS1yBCozvVUntIIdlBk04WiqM+f6NOEc9p+LimJSFKJMF66cgzejeiybuTk6mgbMJW+olg42BNYC8E9x9Q==
+ dependencies:
+ backbone "^1.1.2"
+ jquery "^2.2.4"
+ lunr "^2.3.6"
+ underscore "^1.9.1"
-typedoc@^0.13.0:
- version "0.13.0"
- resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.13.0.tgz#9efdf352bd54873955cd161bd4b75f24a8c59dde"
+typedoc-plugin-external-module-map@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/typedoc-plugin-external-module-map/-/typedoc-plugin-external-module-map-1.0.0.tgz#7021d0e2bc9a98b7266f4ea2eab593b7c63802ce"
+ integrity sha512-OtlTOmanX0yqRYUVLBuGSBjrffLLAjWNn8mqh6k6FkvfXAIIe3Yfg0kCeKZDN/65v4dt3MJ9AuGXTGLPue3Kqg==
+
+typedoc@^0.15.0-0:
+ version "0.15.0-0"
+ resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.15.0-0.tgz#4d0acd8697c22824fb51fff68766fd435b99163d"
+ integrity sha512-N43CSq6T22MVrP1kb0OYusgwnUniwuh9vGVmtgTCjvTSkuJjdMyMeJPMfnugmfRIWxuP9pO6wvNhdDRG32+EQA==
dependencies:
- "@types/fs-extra" "^5.0.3"
- "@types/handlebars" "^4.0.38"
+ "@types/fs-extra" "^5.0.5"
"@types/highlight.js" "^9.12.3"
- "@types/lodash" "^4.14.110"
- "@types/marked" "^0.4.0"
+ "@types/lodash" "^4.14.123"
+ "@types/marked" "^0.6.0"
"@types/minimatch" "3.0.3"
- "@types/shelljs" "^0.8.0"
- fs-extra "^7.0.0"
- handlebars "^4.0.6"
- highlight.js "^9.0.0"
- lodash "^4.17.10"
- marked "^0.4.0"
+ "@types/shelljs" "^0.8.3"
+ fs-extra "^7.0.1"
+ handlebars "^4.1.0"
+ highlight.js "^9.13.1"
+ lodash "^4.17.11"
+ marked "^0.6.0"
minimatch "^3.0.0"
- progress "^2.0.0"
- shelljs "^0.8.2"
- typedoc-default-themes "^0.5.0"
- typescript "3.1.x"
+ progress "^2.0.3"
+ shelljs "^0.8.3"
+ typedoc-default-themes "^0.6.0-0"
+ typescript "3.3.x"
typescript-language-server@^0.3.7:
version "0.3.7"
@@ -9493,9 +9599,10 @@ typescript-language-server@^0.3.7:
vscode-languageserver "^4.4.0"
vscode-uri "^1.0.5"
-typescript@3.1.x:
- version "3.1.6"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68"
+typescript@3.3.x:
+ version "3.3.4000"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.4000.tgz#76b0f89cfdbf97827e1112d64f283f1151d6adf0"
+ integrity sha512-jjOcCZvpkl2+z7JFn0yBOoLQyLoIkNZAs/fYJkUG6VKy6zLPHJGfQJYFHzibB6GJaF/8QrcECtlQ5cpvRHSMEA==
typescript@^3.1.3:
version "3.1.3"
@@ -9529,6 +9636,14 @@ uglify-js@^2.6:
optionalDependencies:
uglify-to-browserify "~1.0.0"
+uglify-js@^3.1.4:
+ version "3.5.3"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.5.3.tgz#d490bb5347f23025f0c1bc0dee901d98e4d6b063"
+ integrity sha512-rIQPT2UMDnk4jRX+w4WO84/pebU2jiLsjgIyrCktYgSvx28enOE3iYQMr+BD1rHiitWnDmpu0cY/LfIEpKcjcw==
+ dependencies:
+ commander "~2.19.0"
+ source-map "~0.6.1"
+
uglify-to-browserify@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
@@ -9561,6 +9676,11 @@ unbzip2-stream@^1.0.9:
buffer "^3.0.1"
through "^2.3.6"
+underscore@>=1.8.3, underscore@^1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
+ integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==
+
underscore@~1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
@@ -9640,9 +9760,10 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
-urijs@^1.18.4:
+urijs@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.1.tgz#5b0ff530c0cbde8386f6342235ba5ca6e995d25a"
+ integrity sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg==
urix@^0.1.0:
version "0.1.0"
@@ -9689,7 +9810,7 @@ user-home@^2.0.0:
dependencies:
os-homedir "^1.0.0"
-util-deprecate@~1.0.1:
+util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -9850,16 +9971,6 @@ vscode-nls@^3.2.2, vscode-nls@^3.2.4:
version "3.2.4"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
-vscode-nsfw@^1.0.17:
- version "1.0.17"
- resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-1.0.17.tgz#da3820f26aea3a7e95cadc54bd9e5dae3d47e474"
- dependencies:
- fs-extra "^0.26.5"
- lodash.isinteger "^4.0.4"
- lodash.isundefined "^3.0.1"
- nan "^2.0.0"
- promisify-node "^0.3.0"
-
vscode-ripgrep@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.2.4.tgz#b3cfbe08ed13f6cf6b134147ea4d982970ab4f70"
@@ -9910,17 +10021,19 @@ wdio-dot-reporter@~0.0.8:
version "0.0.10"
resolved "https://registry.yarnpkg.com/wdio-dot-reporter/-/wdio-dot-reporter-0.0.10.tgz#facfb7c9c5984149951f59cbc3cd0752101cf0e0"
-wdio-mocha-framework@0.5.13:
- version "0.5.13"
- resolved "https://registry.yarnpkg.com/wdio-mocha-framework/-/wdio-mocha-framework-0.5.13.tgz#f4da119456cb673b8c058fb60936132ec752a9d4"
+wdio-mocha-framework@0.6.4:
+ version "0.6.4"
+ resolved "https://registry.yarnpkg.com/wdio-mocha-framework/-/wdio-mocha-framework-0.6.4.tgz#291b05b5f8735716023e1228e461f66ff2e7e1c9"
+ integrity sha512-GZsXwoW60/fkkfqZJR/ZAdiALaM+hW+BbnTT9x214qPR4Pe5XeyYxhJNEdyf0dNI9625cMdkyZYaWoFHN5zDcA==
dependencies:
babel-runtime "^6.23.0"
- mocha "^5.0.0"
- wdio-sync "0.7.1"
+ mocha "^5.2.0"
+ wdio-sync "0.7.3"
wdio-selenium-standalone-service@0.0.12:
version "0.0.12"
resolved "https://registry.yarnpkg.com/wdio-selenium-standalone-service/-/wdio-selenium-standalone-service-0.0.12.tgz#f472d00d3a7800b2dbedb781bff0f5e726a21e9d"
+ integrity sha512-R8iUL30SkFfZictAG5wRofeCsHQ4bIucDtaArCQWZkUqS+DlGTStIk3TaIOCaX7dS7UW1YN/lJt9Vsn4Ekmoxg==
dependencies:
fs-extra "^0.30.0"
selenium-standalone "^6.15.4"
@@ -9928,22 +10041,25 @@ wdio-selenium-standalone-service@0.0.12:
wdio-spec-reporter@0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/wdio-spec-reporter/-/wdio-spec-reporter-0.1.5.tgz#6d6f865deac6b36f96988c1204cc81099b75fc7e"
+ integrity sha512-MqvgTow8hFwhFT47q67JwyJyeynKodGRQCxF7ijKPGfsaG1NLssbXYc0JhiL7SiAyxnQxII0UxzTCd3I6sEdkg==
dependencies:
babel-runtime "~6.26.0"
chalk "^2.3.0"
humanize-duration "~3.15.0"
-wdio-sync@0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/wdio-sync/-/wdio-sync-0.7.1.tgz#00847fbbce16826c3225618f4259d28b60a42483"
+wdio-sync@0.7.3:
+ version "0.7.3"
+ resolved "https://registry.yarnpkg.com/wdio-sync/-/wdio-sync-0.7.3.tgz#858c7439c18c0dbdcd2e25e29db8a0ea2f34bc04"
+ integrity sha512-ukASSHOQmOxaz5HTILR0jykqlHBtAPsBpMtwhpiG0aW9uc7SO7PF+E5LhVvTG4ypAh+UGmY3rTjohOsqDr39jw==
dependencies:
- babel-runtime "6.26.0"
- fibers "~2.0.0"
+ babel-runtime "^6.26.0"
+ fibers "^3.0.0"
object.assign "^4.0.3"
webdriverio@4.14.1:
version "4.14.1"
resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-4.14.1.tgz#50fdb010d37233c77c48e5f0497a63ab875cdfc1"
+ integrity sha512-Gjb5ft6JtO7WdoZifedeM6U941UZi03IlG0t3Xq9M9SxSm6FuyqMEmNZ4HI3UcBRkSbWxdOWGAvpFShYxVr7iA==
dependencies:
archiver "~2.1.0"
babel-runtime "^6.26.0"
@@ -10106,7 +10222,7 @@ which-pm-runs@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
-which@1, which@^1.1.1, which@^1.2.12, which@^1.2.14, which@^1.2.8, which@^1.2.9, which@^1.3.0:
+which@1, which@^1.1.1, which@^1.2.14, which@^1.2.8, which@^1.2.9, which@^1.3.0, which@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
dependencies:
@@ -10256,6 +10372,7 @@ xtend@~2.1.1:
xterm@3.9.2:
version "3.9.2"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.9.2.tgz#e94bfbb84217b19bc1c16ed43d303b8245c9313d"
+ integrity sha512-fpQJQFTosY97EK4eB7UOrlFAwwqv1rSqlXgttEVD0S1v4MlevsUkRwrM/ew5X73jQXc+vdglRtccIhcXg5wtGg==
y18n@^3.2.1:
version "3.2.1"
@@ -10388,9 +10505,10 @@ yauzl@2.4.1:
dependencies:
fd-slicer "~1.0.1"
-yauzl@^2.4.2, yauzl@^2.5.0:
+yauzl@^2.10.0, yauzl@^2.4.2:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
+ integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"