Skip to content

Commit

Permalink
Introduce the Variable Resolver Extension
Browse files Browse the repository at this point in the history
Signed-off-by: Artem Zatsarynnyi <azatsary@redhat.com>
  • Loading branch information
azatsarynnyy committed Mar 28, 2018
1 parent f4de4f5 commit ea79ea1
Show file tree
Hide file tree
Showing 15 changed files with 700 additions and 2 deletions.
3 changes: 2 additions & 1 deletion examples/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@theia/terminal": "^0.3.7",
"@theia/typescript": "^0.3.7",
"@theia/userstorage": "^0.3.7",
"@theia/variable-resolver": "^0.3.7",
"@theia/workspace": "^0.3.7"
},
"scripts": {
Expand All @@ -50,4 +51,4 @@
"devDependencies": {
"@theia/cli": "^0.3.7"
}
}
}
3 changes: 2 additions & 1 deletion examples/electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@theia/terminal": "^0.3.7",
"@theia/typescript": "^0.3.7",
"@theia/userstorage": "^0.3.7",
"@theia/variable-resolver": "^0.3.7",
"@theia/workspace": "^0.3.7"
},
"scripts": {
Expand All @@ -49,4 +50,4 @@
"devDependencies": {
"@theia/cli": "^0.3.7"
}
}
}
81 changes: 81 additions & 0 deletions packages/variable-resolver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Theia - Variable Resolver Extension

The extension provides variable substitution mechanism inside of strings using `${variableName}` syntax.

## Variable Contribution Point
Extension provides a hook that allows any extensions to contribute its own variables.
Here's the example of contributing two variables:
- `${file}` - returns the name of the file opened in the current editor
- `${lineNumber}` - returns the current line number in the current file

```typescript
@injectable()
export class EditorVariableContribution implements VariableContribution {

constructor(
@inject(EditorManager) protected readonly editorManager: EditorManager
) { }

registerVariables(variables: VariableRegistry): void {
variables.registerVariable({
name: 'file',
description: 'The name of the file opened in the current editor',
resolve: () => {
const currentEditor = this.getCurrentEditor();
if (currentEditor) {
return currentEditor.uri.displayName;
}
return undefined;
}
});
variables.registerVariable({
name: 'lineNumber',
description: 'The current line number in the current file',
resolve: () => {
const currentEditor = this.getCurrentEditor();
if (currentEditor) {
return `${currentEditor.cursor.line + 1}`;
}
return undefined;
}
});
}

protected getCurrentEditor(): TextEditor | undefined {
const currentEditor = this.editorManager.currentEditor;
if (currentEditor) {
return currentEditor.editor;
}
return undefined;
}
}
```

Note that a Variable is resolved to `MaybePromise<string | undefined>` which means that it can be resolved synchronously or within a Promise.

## Using the Variable Resolver Service

There's the example of how one can use Variable Resolver Service in its own plugin:
```typescript
@injectable()
export class MyService {

constructor(
@inject(VariableResolverService) protected readonly variableResolver: VariableResolverService
) { }

async resolve(): Promise<void> {
const text = 'cursor is in file ${file} on line ${lineNumber}';
const resolved = await this.variableResolver.resolve(text);
console.log(resolved);
}
}
```

If `package.json` file is currently opened and cursor is on line 5 then the following output will be logged to the console:
```
cursor is in file package.json on line 5
```

## License
[Apache-2.0](https://github.com/theia-ide/theia/blob/master/LICENSE)
10 changes: 10 additions & 0 deletions packages/variable-resolver/compile.tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../configs/base.tsconfig",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib"
},
"include": [
"src"
]
}
52 changes: 52 additions & 0 deletions packages/variable-resolver/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "@theia/variable-resolver",
"version": "0.3.7",
"description": "Theia - Variable Resolver Extension",
"dependencies": {
"@theia/core": "^0.3.7"
},
"publishConfig": {
"access": "public"
},
"theiaExtensions": [
{
"frontend": "lib/browser/variable-resolver-frontend-module"
}
],
"keywords": [
"theia-extension"
],
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/theia-ide/theia.git"
},
"bugs": {
"url": "https://github.com/theia-ide/theia/issues"
},
"homepage": "https://github.com/theia-ide/theia",
"contributors": [
{
"name": "Artem Zatsarynnyi",
"email": "azatsary@redhat.com"
}
],
"files": [
"lib",
"src"
],
"scripts": {
"prepare": "yarn run clean && yarn run build",
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
"test": "theiaext test",
"docs": "theiaext docs"
},
"devDependencies": {
"@theia/ext-scripts": "^0.2.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
}
}
10 changes: 10 additions & 0 deletions packages/variable-resolver/src/browser/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright (C) 2018 Red Hat, Inc. and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

export * from './variable';
export * from './variable-quick-open-service';
export * from './variable-resolver-service';
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2018 Red Hat, Inc. and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import { inject, injectable } from 'inversify';
import { QuickOpenService, QuickOpenModel, QuickOpenItem, QuickOpenMode } from '@theia/core/lib/browser/quick-open/';
import { VariableRegistry } from './variable';

@injectable()
export class VariableQuickOpenService implements QuickOpenModel {

protected items: QuickOpenItem[];

constructor(
@inject(VariableRegistry) protected readonly variableRegistry: VariableRegistry,
@inject(QuickOpenService) protected readonly quickOpenService: QuickOpenService
) { }

open(): void {
this.items = this.variableRegistry.getVariables().map(
v => new VariableQuickOpenItem(v.name, v.description)
);

this.quickOpenService.open(this, {
placeholder: 'Registered variables',
fuzzyMatchLabel: true,
fuzzyMatchDescription: true,
fuzzySort: true
});
}

onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void {
acceptor(this.items);
}
}

export class VariableQuickOpenItem extends QuickOpenItem {

constructor(
protected readonly name: string,
protected readonly description?: string
) {
super();
}

getLabel(): string {
return '${' + this.name + '}';
}

getDetail(): string {
return this.description || '';
}

run(mode: QuickOpenMode): boolean {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2018 Red Hat, Inc. and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';

let disableJSDOM = enableJSDOM();

import * as chai from 'chai';
import { Container, ContainerModule } from 'inversify';
import { QuickOpenService } from '@theia/core/lib/browser';
import { ILogger, bindContributionProvider } from '@theia/core/lib/common';
import { MockLogger } from '@theia/core/lib/common/test/mock-logger';
import { VariableContribution, VariableRegistry } from './variable';
import { VariableQuickOpenService } from './variable-quick-open-service';
import { VariableResolverFrontendContribution } from './variable-resolver-frontend-contribution';

disableJSDOM();

const expect = chai.expect;

before(() => {
chai.config.showDiff = true;
chai.config.includeStack = true;
});

describe('variable-resolver-frontend-contribution', () => {

let testContainer: Container;
let variableRegistry: VariableRegistry;

before(() => {
disableJSDOM = enableJSDOM();

testContainer = new Container();
const module = new ContainerModule((bind, unbind, isBound, rebind) => {
bindContributionProvider(bind, VariableContribution);
bind(VariableContribution).toConstantValue(new TestVariableContribution());

bind(ILogger).to(MockLogger);
bind(VariableRegistry).toSelf().inSingletonScope();

bind(QuickOpenService).toSelf();
bind(VariableQuickOpenService).toSelf();

bind(VariableResolverFrontendContribution).toSelf();
});
testContainer.load(module);
});

after(() => {
disableJSDOM();
});

beforeEach(() => {
variableRegistry = testContainer.get<VariableRegistry>(VariableRegistry);

const variableRegistrar = testContainer.get(VariableResolverFrontendContribution);
variableRegistrar.onStart();
});

it('should register all variables from the contribution points', () => {
const variables = variableRegistry.getVariables();
expect(variables.length).to.be.equal(2);
expect(variables[0].name).to.be.equal('file');
expect(variables[1].name).to.be.equal('lineNumber');
});
});

export class TestVariableContribution implements VariableContribution {

registerVariables(variables: VariableRegistry): void {
variables.registerVariable({
name: 'file',
description: 'Resolves to file name opened in the current editor',
resolve: () => Promise.resolve('package.json')
});
variables.registerVariable({
name: 'lineNumber',
description: 'Resolves to current line number',
resolve: () => Promise.resolve('5')
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 Red Hat, Inc. and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import { injectable, inject, named } from 'inversify';
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
import { Command, CommandContribution, CommandRegistry, ContributionProvider } from '@theia/core/lib/common';
import { VariableContribution, VariableRegistry } from './variable';
import { VariableQuickOpenService } from './variable-quick-open-service';

export const LIST_VARIABLES: Command = {
id: 'variable.list',
label: 'Variable: List All'
};

@injectable()
export class VariableResolverFrontendContribution implements FrontendApplicationContribution, CommandContribution {

constructor(
@inject(ContributionProvider) @named(VariableContribution)
protected readonly contributionProvider: ContributionProvider<VariableContribution>,
@inject(VariableRegistry) protected readonly variableRegistry: VariableRegistry,
@inject(VariableQuickOpenService) protected readonly variableQuickOpenService: VariableQuickOpenService
) { }

onStart(): void {
this.contributionProvider.getContributions().forEach(contrib =>
contrib.registerVariables(this.variableRegistry)
);
}

registerCommands(commands: CommandRegistry): void {
commands.registerCommand(LIST_VARIABLES, {
isEnabled: () => true,
execute: () => this.variableQuickOpenService.open()
});
}
}
Loading

0 comments on commit ea79ea1

Please sign in to comment.