PT #156854801: main chunk of concourse extension

This commit is contained in:
BoykoAlex
2018-05-17 13:56:24 -04:00
parent 9678e08913
commit 6cbc055cb5
20 changed files with 529 additions and 33 deletions

View File

@@ -1,33 +0,0 @@
import { injectable, inject } from "inversify";
import { CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry, MessageService } from "@theia/core/lib/common";
import { CommonMenus } from "@theia/core/lib/browser";
export const CfManifestYamlCommand = {
id: 'CfManifestYaml.command',
label: "Shows a message"
};
@injectable()
export class CfManifestYamlCommandContribution implements CommandContribution {
constructor(
@inject(MessageService) private readonly messageService: MessageService,
) { }
registerCommands(registry: CommandRegistry): void {
registry.registerCommand(CfManifestYamlCommand, {
execute: () => this.messageService.info('Hello World!')
});
}
}
@injectable()
export class CfManifestYamlMenuContribution implements MenuContribution {
registerMenus(menus: MenuModelRegistry): void {
menus.registerMenuAction(CommonMenus.EDIT_FIND, {
commandId: CfManifestYamlCommand.id,
label: 'Say Hello'
});
}
}

View File

@@ -33,6 +33,7 @@ export class CfManifestYamlContribution extends BaseLanguageServerContribution {
'-Dsts.lsp.client=theia',
'-Dlsp.completions.indentation.enable=true',
'-Dlsp.yaml.completions.errors.disable=true',
'-Dorg.slf4j.simpleLogger.logFile=cf-manifest-yaml.log'
];
}

View File

@@ -0,0 +1,8 @@
node_modules
.browser_modules
lib
*.log
*-app/*
!*-app/package.json
/**/server

View File

@@ -0,0 +1,60 @@
{
// Use IntelliSense to learn about possible Node.js debug attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Start Browser Backend",
"program": "${workspaceRoot}/browser-app/src-gen/backend/main.js",
"args": [
"--loglevel=debug",
"--port=3000",
"--no-cluster"
],
"env": {
"NODE_ENV": "development"
},
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/node_modules/@theia/*/lib/**/*.js",
"${workspaceRoot}/browser-app/lib/**/*.js",
"${workspaceRoot}/browser-app/src-gen/**/*.js"
],
"smartStep": true,
"internalConsoleOptions": "openOnSessionStart",
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Start Electron Backend",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
},
"program": "${workspaceRoot}/electron-app/src-gen/frontend/electron-main.js",
"protocol": "inspector",
"args": [
"--loglevel=debug",
"--hostname=localhost",
"--no-cluster"
],
"env": {
"NODE_ENV": "development"
},
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/electron-app/src-gen/frontend/electron-main.js",
"${workspaceRoot}/electron-app/src-gen/backend/main.js",
"${workspaceRoot}/electron-app/lib/**/*.js",
"${workspaceRoot}/node_modules/@theia/*/lib/**/*.js"
],
"smartStep": true,
"internalConsoleOptions": "openOnSessionStart",
"outputCapture": "std"
}
]
}

View File

@@ -0,0 +1,81 @@
# Concourse Extension for Theia IDE
The example of how to build the Theia-based applications with the concourse-extension.
## Getting started
Install [nvm](https://github.com/creationix/nvm#install-script).
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.5/install.sh | bash
Install npm and node.
nvm install 8
nvm use 8
Install yarn.
npm install -g yarn
## Install dependencies
Install dependencies and compile the code.
yarn
Note: it is also running `yarn rebuild:browser` and `yarn rebuild:electron`, thus one could navigate to the proper folder and launch it
## Running the browser example
yarn rebuild:browser
cd browser-app
yarn start
Open http://localhost:3000 in the browser.
## Running the Electron example
yarn rebuild:electron
cd electron-app
yarn start
## Developing with the browser example
Start watching of the hello world extension.
cd concourse-extension
yarn watch
Start watching of the browser example.
yarn rebuild:browser
cd browser-app
yarn watch
Launch `Start Browser Backend` configuration from VS code.
Open http://localhost:3000 in the browser.
## Developing with the Electron example
Start watching of the hello world extension.
cd concourse-extension
yarn watch
Start watching of the electron example.
yarn rebuild:electron
cd electron-app
yarn watch
Launch `Start Electron Backend` configuration from VS code.
## Publishing concourse-extension
Create a npm user and login to the npm registry, [more on npm publishing](https://docs.npmjs.com/getting-started/publishing-npm-packages).
npm login
Publish packages with lerna to update versions properly across local packages, [more on publishing with lerna](https://github.com/lerna/lerna#publish).
npx lerna publish

View File

@@ -0,0 +1,32 @@
{
"private": true,
"name": "browser-app",
"version": "0.0.0",
"dependencies": {
"@theia/core": "latest",
"@theia/filesystem": "latest",
"@theia/workspace": "latest",
"@theia/preferences": "latest",
"@theia/navigator": "latest",
"@theia/process": "latest",
"@theia/terminal": "latest",
"@theia/editor": "latest",
"@theia/languages": "latest",
"@theia/markers": "latest",
"@theia/monaco": "latest",
"@theia/typescript": "latest",
"@theia/messages": "latest",
"@theia/concourse": "0.0.0"
},
"devDependencies": {
"@theia/cli": "latest"
},
"scripts": {
"prepare": "theia build",
"start": "theia start",
"watch": "theia build --watch"
},
"theia": {
"target": "browser"
}
}

View File

@@ -0,0 +1,36 @@
{
"name": "@theia/concourse",
"keywords": [
"theia-extension"
],
"version": "0.0.0",
"files": [
"lib",
"src"
],
"dependencies": {
"@theia/core": "latest",
"@theia/languages": "latest",
"@pivotal-tools/jvm-launch-utils": "0.0.11",
"@types/glob": "^5.0.30",
"glob": "^7.1.2"
},
"devDependencies": {
"rimraf": "latest",
"typescript": "latest",
"download": "^6.2.5"
},
"scripts": {
"prepare": "yarn run clean && yarn run download && yarn run build",
"download": "node script.js",
"clean": "rimraf lib",
"build": "tsc",
"watch": "tsc -w"
},
"theiaExtensions": [
{
"frontend": "lib/browser/concourse-frontend-module",
"backend": "lib/node/concourse-backend-module"
}
]
}

View File

@@ -0,0 +1,3 @@
{
"jarUrl": "https://s3.amazonaws.com/dist.springsource.com/release/STS4/fatjars/concourse-language-server-0.2.1-201805111942.jar"
}

View File

@@ -0,0 +1,34 @@
const fs = require('fs');
const path = require('path');
const url = require('url');
const glob = require('glob');
const download = require('download');
const PROPERTIES = require('./properties.json');
let fileExists = function(path) {
return new Promise((resolve, reject) => {
fs.access(path, fs.R_OK, error => {
resolve(!error || error.code !== 'ENOENT');
})
});
};
const serverHome = path.join(__dirname, 'server');
const filePaths = glob.sync('concourse-language-server*.jar', { cwd: serverHome });
if (filePaths.length === 0) {
const serverDownloadUrl = PROPERTIES.jarUrl;
const fileName = path.basename(url.parse(serverDownloadUrl).pathname);
const localFileName = path.join(serverHome, fileName.startsWith('concourse-language-server') ? fileName : 'concourse-language-server.jar');
console.log(`Downloading ${serverDownloadUrl} to ${localFileName}`);
fileExists(serverHome)
.then(doesExist => { if (!doesExist) fs.mkdir(serverHome) })
.then(() => download(serverDownloadUrl))
.then(data => fs.writeFileSync(localFileName, data))
.then(() => fileExists(localFileName))
.then(doesExist => { if (!doesExist) throw Error(`Failed to install the ${this.getServerName()} language server`); })
.then(() => console.log(`Successfully downloaded ${serverDownloadUrl}`));
}

View File

@@ -0,0 +1,16 @@
/**
* Generated using theia-extension-generator
*/
import { ContainerModule } from "inversify";
import { ConcourseClientContribution } from './language-client-contribution';
import { LanguageClientContribution } from '@theia/languages/lib/browser';
// Contribute monaco-editor languages for pipeline and task yaml
import './pipeline-yaml-monaco-contribution';
import './task-yaml-monaco-contribution';
export default new ContainerModule(bind => {
// add your contribution bindings here
bind(LanguageClientContribution).to(ConcourseClientContribution).inSingletonScope();
});

View File

@@ -0,0 +1,33 @@
import { injectable, inject } from 'inversify';
import { BaseLanguageClientContribution, Workspace, Languages, LanguageClientFactory } from '@theia/languages/lib/browser';
import {
CONCOURSE_PIPELINE_YAML_LANGUAGE_ID, CONCOURSE_SERVER_ID, CONCOURSE_SERVER_NAME,
CONCOURSE_TASK_YAML_LANGUAGE_ID
} from '../common';
@injectable()
export class ConcourseClientContribution extends BaseLanguageClientContribution {
readonly id = CONCOURSE_SERVER_ID;
readonly name = CONCOURSE_SERVER_NAME;
constructor(
@inject(Workspace) protected readonly workspace: Workspace,
@inject(Languages) protected readonly languages: Languages,
@inject(LanguageClientFactory) protected readonly languageClientFactory: LanguageClientFactory,
) {
super(workspace, languages, languageClientFactory);
}
protected get documentSelector() {
return [CONCOURSE_PIPELINE_YAML_LANGUAGE_ID, CONCOURSE_TASK_YAML_LANGUAGE_ID];
}
protected get globPatterns() {
return [
'*pipeline*.yml',
'*task.yml',
'**/tasks/*.yml'
];
}
}

View File

@@ -0,0 +1,26 @@
/// <reference types='monaco-editor-core/monaco'/>
import {
CONCOURSE_PIPELINE_YAML_LANGUAGE_ID,
CONCOURSE_PIPELINE_YAML_LANGUAGE_NAME
} from '../common';
// Pipeline .yml file language registration
let YAML_LANG_MODULE_PROMISE: monaco.Promise<any>;
monaco.languages.register({
id: CONCOURSE_PIPELINE_YAML_LANGUAGE_ID,
filenamePatterns: ['*pipeline*.yml'],
aliases: [CONCOURSE_PIPELINE_YAML_LANGUAGE_NAME, CONCOURSE_PIPELINE_YAML_LANGUAGE_ID],
firstLine: '^#(\\s)*pipeline(\\s)*',
});
monaco.languages.onLanguage(CONCOURSE_PIPELINE_YAML_LANGUAGE_ID, () => {
if (!YAML_LANG_MODULE_PROMISE) {
YAML_LANG_MODULE_PROMISE = (<any>monaco.languages.getLanguages().find(ext => ext.id === 'yaml')).loader();
}
return YAML_LANG_MODULE_PROMISE.then(mod => {
monaco.languages.setLanguageConfiguration(CONCOURSE_PIPELINE_YAML_LANGUAGE_ID, mod.conf);
monaco.languages.setMonarchTokensProvider(CONCOURSE_PIPELINE_YAML_LANGUAGE_ID, mod.language);
})
});

View File

@@ -0,0 +1,26 @@
/// <reference types='monaco-editor-core/monaco'/>
import {
CONCOURSE_TASK_YAML_LANGUAGE_ID,
CONCOURSE_TASK_YAML_LANGUAGE_NAME
} from '../common';
// Task .yml file language registration
let YAML_LANG_MODULE_PROMISE: monaco.Promise<any>;
monaco.languages.register({
id: CONCOURSE_TASK_YAML_LANGUAGE_ID,
filenamePatterns: ['*task.yml', '**/tasks/*.yml'],
aliases: [CONCOURSE_TASK_YAML_LANGUAGE_NAME, CONCOURSE_TASK_YAML_LANGUAGE_ID],
firstLine: '^#(\\\\s)*task(\\\\s)*',
});
monaco.languages.onLanguage(CONCOURSE_TASK_YAML_LANGUAGE_ID, () => {
if (!YAML_LANG_MODULE_PROMISE) {
YAML_LANG_MODULE_PROMISE = (<any>monaco.languages.getLanguages().find(ext => ext.id === 'yaml')).loader();
}
return YAML_LANG_MODULE_PROMISE.then(mod => {
monaco.languages.setLanguageConfiguration(CONCOURSE_TASK_YAML_LANGUAGE_ID, mod.conf);
monaco.languages.setMonarchTokensProvider(CONCOURSE_TASK_YAML_LANGUAGE_ID, mod.language);
})
});

View File

@@ -0,0 +1,8 @@
export const CONCOURSE_SERVER_ID = 'concourse-yaml';
export const CONCOURSE_SERVER_NAME = 'Concourse YAML';
export const CONCOURSE_PIPELINE_YAML_LANGUAGE_ID = 'concourse-pipeline-yaml';
export const CONCOURSE_PIPELINE_YAML_LANGUAGE_NAME = 'Concourse Pipeline';
export const CONCOURSE_TASK_YAML_LANGUAGE_ID = 'concourse-task-yaml';
export const CONCOURSE_TASK_YAML_LANGUAGE_NAME = 'Concourse Task';

View File

@@ -0,0 +1,7 @@
import { ContainerModule } from "inversify";
import { LanguageServerContribution } from "@theia/languages/lib/node";
import { ConcourseLanguageContribution } from './concourse-language-contribution';
export default new ContainerModule(bind => {
bind(LanguageServerContribution).to(ConcourseLanguageContribution).inSingletonScope();
});

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2017 TypeFox 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 * as path from 'path';
import * as glob from 'glob';
import { injectable } from 'inversify';
// import { DEBUG_MODE } from '@theia/core/lib/node';
import { IConnection, BaseLanguageServerContribution } from '@theia/languages/lib/node';
import { CONCOURSE_SERVER_ID, CONCOURSE_SERVER_NAME } from '../common';
import { findJvm } from '@pivotal-tools/jvm-launch-utils';
@injectable()
export class ConcourseLanguageContribution extends BaseLanguageServerContribution {
readonly id = CONCOURSE_SERVER_ID;
readonly name = CONCOURSE_SERVER_NAME;
start(clientConnection: IConnection): void {
const serverPath = path.resolve(__dirname, '../../server');
const jarPaths = glob.sync('concourse-language-server*.jar', { cwd: serverPath });
if (jarPaths.length === 0) {
throw new Error('The Concourse YAML server launcher is not found.');
}
const jarPath = path.resolve(serverPath, jarPaths[0]);
findJvm()
.catch(error => {
throw new Error('Error trying to find JVM');
})
.then(jvm => {
if (!jvm) {
throw new Error("Couldn't locate java in $JAVA_HOME or $PATH");
}
this.startSocketServer().then(server => {
const socket = this.accept(server);
// this.logInfo('logs at ' + path.resolve(workspacePath, '.metadata', '.log'));
const env = Object.create(process.env);
const addressInfo = server.address();
if (typeof addressInfo === 'string') {
throw new Error(`Address info was string ${addressInfo}`);
}
env.CLIENT_HOST = addressInfo.address;
env.CLIENT_PORT = addressInfo.port;
const command = jvm.getJavaExecutable();
const args = [
'-Dsts.lsp.client=theia',
'-Dlsp.completions.indentation.enable=true',
'-Dlsp.yaml.completions.errors.disable=true',
'-Dorg.slf4j.simpleLogger.logFile=concourse-yaml.log',
`-Dserver.port=${env.CLIENT_PORT}`
];
// if (DEBUG_MODE) {
args.push(
'-Xdebug',
'-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=7999',
'-Dlog.level=ALL'
);
// }
args.push(
'-jar', jarPath,
);
this.createProcessSocketConnection(socket, socket, command, args, { env })
.then(serverConnection => this.forward(clientConnection, serverConnection));
});
});
}
}

View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"strict": true,
"experimentalDecorators": true,
"noUnusedLocals": true,
"emitDecoratorMetadata": true,
"downlevelIteration": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es5",
"lib": [
"es6",
"dom"
],
"sourceMap": true,
"rootDir": "src",
"outDir": "lib"
},
"include": [
"src"
]
}

View File

@@ -0,0 +1,32 @@
{
"private": true,
"name": "electron-app",
"version": "0.0.0",
"dependencies": {
"@theia/core": "latest",
"@theia/filesystem": "latest",
"@theia/workspace": "latest",
"@theia/preferences": "latest",
"@theia/navigator": "latest",
"@theia/process": "latest",
"@theia/terminal": "latest",
"@theia/editor": "latest",
"@theia/languages": "latest",
"@theia/markers": "latest",
"@theia/monaco": "latest",
"@theia/typescript": "latest",
"@theia/messages": "latest",
"@theia/concourse": "0.0.0"
},
"devDependencies": {
"@theia/cli": "latest"
},
"scripts": {
"prepare": "theia build",
"start": "theia start",
"watch": "theia build --watch"
},
"theia": {
"target": "electron"
}
}

View File

@@ -0,0 +1,11 @@
{
"lerna": "2.4.0",
"version": "0.0.0",
"useWorkspaces": true,
"npmClient": "yarn",
"command": {
"run": {
"stream": true
}
}
}

View File

@@ -0,0 +1,14 @@
{
"private": true,
"scripts": {
"prepare": "lerna run prepare",
"rebuild:browser": "theia rebuild:browser",
"rebuild:electron": "theia rebuild:electron"
},
"devDependencies": {
"lerna": "2.4.0"
},
"workspaces": [
"concourse", "browser-app", "electron-app"
]
}