Initial commit: Push working extension changes

This commit is contained in:
Sandeep Somavarapu 2017-03-24 11:07:48 +01:00
Родитель 20a6556672
Коммит e4bec35280
18 изменённых файлов: 2556 добавлений и 0 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -191,6 +191,7 @@ ClientBin/
*.pfx
*.publishsettings
node_modules/
out/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components

33
.vscode/launch.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,33 @@
// A launch configuration that compiles the extension and then opens it inside a new window
{
"version": "0.1.0",
"configurations": [
{
"name": "Launch Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}"
],
"stopOnEntry": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/out/src",
"preLaunchTask": "npm"
},
{
"name": "Launch Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}",
"--extensionTestsPath=${workspaceRoot}/out/test"
],
"stopOnEntry": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/out/test",
"preLaunchTask": "npm"
}
]
}

11
.vscode/settings.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
"typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version,
"editor.insertSpaces": false
}

28
.vscode/tasks.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,28 @@
// Available variables which can be used inside of strings.
// ${workspaceRoot}: the root folder of the team
// ${file}: the current opened file
// ${fileBasename}: the current opened file's basename
// ${fileDirname}: the current opened file's dirname
// ${fileExtname}: the current opened file's extension
// ${cwd}: the current working directory of the spawned process
// A task runner that calls a custom npm script that compiles the extension.
{
"version": "0.1.0",
// we want to run npm
"command": "npm",
// the command is a shell script
"isShellCommand": true,
// show the output window only if unrecognized errors occur.
"showOutput": "silent",
// we run the custom script "compile" as defined in package.json
"args": [
"run",
"compile",
"--loglevel",
"silent"
],
// The tsc compiler is started in watching mode
"isWatching": true,
// use the standard tsc in watch mode problem matcher to find compile problems in the output.
"problemMatcher": "$tsc-watch"
}

127
package.json Normal file
Просмотреть файл

@ -0,0 +1,127 @@
{
"name": "vscode-mongodb",
"displayName": "vscode-mongodb",
"description": "VS Code extension for Mongo db",
"version": "0.0.1",
"publisher": "sandy081",
"engines": {
"vscode": "^1.10.0"
},
"enableProposedApi": true,
"categories": [
"Other"
],
"homepage": "https://github.com/sandy081/vscode-mongodb/blob/master/README.md",
"bugs": {
"url": "https://github.com/sandy081/vscode-mongodb/issues"
},
"license": "SEE LICENSE IN LICENSE.md",
"repository": {
"type": "git",
"url": "https://github.com/sandy081/vscode-mongodb"
},
"activationEvents": [
"*"
],
"main": "./out/src/extension",
"contributes": {
"explorer": {
"treeLabel": "Mongo",
"treeExplorerNodeProviderId": "mongoExplorer",
"icon": "resources/icons/database.png"
},
"languages": [
{
"id": "mongo",
"extensions": [
".mongo"
],
"firstLine": "^#!/.*\\bmongo*\\b"
}
],
"commands": [
{
"category": "Mongo",
"command": "mongo.openShellEditor",
"title": "Open Mongo Shell Editor"
},
{
"category": "Mongo",
"command": "mongo.execute",
"title": "Execute Script"
},
{
"category": "Mongo",
"command": "mongo.executeLine",
"title": "Execute Script at cursor"
},
{
"category": "Mongo",
"command": "mongo.addServer",
"title": "Add Server",
"icon": {
"light": "resources/icons/light/add.svg",
"dark": "resources/icons/dark/add.svg"
}
}
],
"menus": {
"editor/context": [
{
"command": "mongo.execute",
"when": "resourceLangId==mongo"
},
{
"command": "mongo.executeLine",
"when": "resourceLangId==mongo"
}
],
"viewlet/title": [
{
"command": "mongo.addServer",
"when": "treeExplorerProvider == mongoExplorer",
"group": "navigation"
}
]
},
"keybindings": [
{
"command": "mongo.execute",
"key": "shift+f8"
},
{
"command": "mongo.executeLine",
"key": "f8"
}
],
"configuration": {
"title": "Mongo",
"properties": {
"mongo.shell.path": {
"type": [
"string",
"null"
],
"description": "Path to the mongo shell executable",
"default": null,
"isExecutable": true
}
}
}
},
"scripts": {
"vscode:prepublish": "tsc -p ./",
"compile": "tsc -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install"
},
"devDependencies": {
"typescript": "^2.0.3",
"vscode": "^1.0.0",
"mocha": "^2.3.3",
"@types/node": "^6.0.40",
"@types/mocha": "^2.2.32"
},
"dependencies": {
"mongodb": "^2.2.25"
}
}

69
src/extension.ts Normal file
Просмотреть файл

@ -0,0 +1,69 @@
'use strict';
import * as vscode from 'vscode';
import { MongoExplorer } from './mongo/explorer';
import { MongoCommands, ResultDocument } from './mongo/commands';
import { Model } from './mongo/mongo';
import * as fs from 'fs';
export function activate(context: vscode.ExtensionContext) {
// Create the storage folder
if (context.storagePath) {
createStorageFolder(context).then(() => {
const outputChannel = vscode.window.createOutputChannel('Mongo');
const model = new Model({ extensionContext: context, outputChannel });
const resultDocument = new ResultDocument(context);
// Mongo explorer
context.subscriptions.push(vscode.window.registerTreeExplorerNodeProvider('mongoExplorer', new MongoExplorer(model)));
// Commands
context.subscriptions.push(vscode.commands.registerCommand('mongo.addServer', () => { MongoCommands.addServer(model, context) }));
context.subscriptions.push(vscode.commands.registerCommand('mongo.openShellEditor', node => { MongoCommands.openShell(node.element) }));
context.subscriptions.push(vscode.commands.registerCommand('mongo.execute', () => MongoCommands.executeScript(model, resultDocument, outputChannel, true)));
context.subscriptions.push(vscode.commands.registerCommand('mongo.executeLine', () => MongoCommands.executeScript(model, resultDocument, outputChannel, false)));
});
} else {
context.subscriptions.push(vscode.window.registerTreeExplorerNodeProvider('mongoExplorer', {
getLabel(): string {
return '';
},
getHasChildren(): boolean {
return false;
},
getClickCommand(): string {
return '';
},
provideRootNode(): any {
vscode.window.showInformationMessage('Open a workspace first.')
return {};
},
resolveChildren(): Thenable<any[]> {
return null;
}
}));
}
}
async function createStorageFolder(context: vscode.ExtensionContext): Promise<void> {
return new Promise<void>((c, e) => {
fs.exists(context.storagePath, exists => {
if (exists) {
c(null);
} else {
fs.mkdir(context.storagePath, error => {
c(null);
})
}
});
})
}
// this method is called when your extension is deactivated
export function deactivate() {
}

89
src/mongo/commands.ts Normal file
Просмотреть файл

@ -0,0 +1,89 @@
import * as vscode from 'vscode';
import { Model, Server, Database } from './mongo';
export class ResultDocument implements vscode.TextDocumentContentProvider {
private _result: Map<string, string> = new Map<string, any>();
private _onDidChange: vscode.EventEmitter<vscode.Uri> = new vscode.EventEmitter();
public readonly onDidChange: vscode.Event<vscode.Uri> = this._onDidChange.event;
constructor(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('mongo', this));
}
provideTextDocumentContent(uri: vscode.Uri, token: vscode.CancellationToken): vscode.ProviderResult<string> {
const result = this._result.get(uri.toString())
return result ? result : '';
}
setResult(uri: vscode.Uri, result: string) {
this._result.set(uri.toString(), result);
this._onDidChange.fire(uri);
}
}
export class MongoCommands {
public static addServer(model: Model, context: vscode.ExtensionContext): void {
vscode.window.showInputBox({
placeHolder: 'Server connection string'
}).then(value => {
if (value) {
model.add(value);
}
})
}
public static openShell(database: Database): void {
const uri = database.getMongoShellUri();
vscode.workspace.openTextDocument(uri)
.then(textDocument => {
return vscode.window.showTextDocument(textDocument)
.then(editor => {
if (uri.scheme === 'untitled' && !textDocument.getText()) {
editor.edit(builder => {
builder.insert(new vscode.Position(0, 0), '#!/usr/bin/env mongo\n');
});
}
})
});
}
public static executeScript(model: Model, resultDocument: ResultDocument, outputChannel: vscode.OutputChannel, selection: boolean): void {
const editor = vscode.window.activeTextEditor;
if (editor.document.languageId === 'mongo' || editor.document.uri.fsPath.endsWith('.mongo')) {
const text = selection ? MongoCommands.getSelectedText(editor) : editor.document.lineAt(editor.selection.start.line).text;
const database = MongoCommands.getDatabaseWithUri(editor.document.uri, model);
database.executeScript(text)
.then(result => {
const uri = vscode.Uri.parse('mongo://test/result.json');
resultDocument.setResult(uri, result);
vscode.workspace.openTextDocument(uri)
.then(textDocument => {
vscode.window.showTextDocument(textDocument, vscode.ViewColumn.Two, true);
});
outputChannel.append(result);
});
}
}
private static getSelectedText(editor: vscode.TextEditor): string {
const selection = editor.selection;
if (selection.start.isEqual(selection.end)) {
editor.document.getText();
}
return editor.document.getText(selection);
}
private static getDatabaseWithUri(uri: vscode.Uri, model: Model) {
for (const server of model.servers) {
for (const database of server.databases) {
if (database.getMongoShellUri().toString() === uri.toString()) {
return database;
}
}
}
return null;
}
}

37
src/mongo/explorer.ts Normal file
Просмотреть файл

@ -0,0 +1,37 @@
import { TreeExplorerNodeProvider } from 'vscode';
import { Model, Server, Database, IMongoResource } from './mongo';
interface INode {
element: IMongoResource;
}
export class MongoExplorer implements TreeExplorerNodeProvider<INode> {
constructor(private model: Model) { }
getLabel(node: INode): string {
return node.element.id;
}
getHasChildren(node: INode): boolean {
return node.element.hasChildren();
}
getClickCommand(node: INode): string {
if (node.element instanceof Database) {
return 'mongo.openShellEditor'
}
return '';
}
provideRootNode(): INode {
if (this.model) {
// TODO: dispose
}
return { element: this.model };
}
resolveChildren(node: INode): Thenable<INode[]> {
return node.element.loadChildren().then(children => children.map(element => ({ element })));
}
}

187
src/mongo/mongo.ts Normal file
Просмотреть файл

@ -0,0 +1,187 @@
import * as vscode from 'vscode';
import * as cp from 'child_process';
import * as fs from 'fs';
import { MongoClient, Db, ReadPreference, Code } from 'mongodb';
import { Shell } from './shell';
export interface IMongoResource {
id: string
hasChildren(): boolean;
loadChildren(): Promise<IMongoResource[]>;
}
export interface IMongoContext {
extensionContext: vscode.ExtensionContext;
outputChannel: vscode.OutputChannel;
}
class ServersJson {
private _filePath: string;
constructor(context: vscode.ExtensionContext) {
this._filePath = context.storagePath + '/servers.json';
}
async load(): Promise<string[]> {
return new Promise<string[]>((c, e) => {
fs.exists(this._filePath, exists => {
if (exists) {
fs.readFile(this._filePath, (error, data) => {
c(<string[]>JSON.parse(data.toString()));
});
} else {
fs.writeFile(this._filePath, JSON.stringify([]), () => c([]));
}
})
});
}
async write(servers: string[]) {
fs.writeFile(this._filePath, JSON.stringify(servers));
}
}
export class Model implements IMongoResource {
public readonly id: string = '';
private _serversJson: ServersJson;
private _servers: Server[];
constructor(private context: IMongoContext) {
this._serversJson = new ServersJson(context.extensionContext);
}
hasChildren(): boolean { return true; }
loadChildren(): Promise<IMongoResource[]> {
return this._serversJson.load().then(servers => {
this._servers = servers.map(server => new Server(server, this.context));
return this._servers;
});
}
get servers(): Server[] {
return this._servers;
}
add(connectionString: string) {
this._servers.push(new Server(connectionString, this.context));
this._serversJson.write(this._servers.map(server => server.id));
}
}
export class Server implements IMongoResource {
private _databases: Database[] = [];
constructor(public readonly id: string, private context: IMongoContext) {
}
hasChildren(): boolean { return true; }
loadChildren(): Promise<IMongoResource[]> {
return <Promise<IMongoResource[]>>MongoClient.connect(this.id)
.then(db => db.admin().listDatabases()
.then((value: { databases: { name }[] }) => {
this._databases = value.databases.map(database => new Database(database.name, this, this.context));
db.close();
return this._databases;
}), error => {
});
}
get databases(): Database[] {
return this._databases;
}
}
export class Database implements IMongoResource {
private shell: Shell;
private shellUri: vscode.Uri;
constructor(readonly id: string, private server: Server, private context: IMongoContext) {
this.shellUri = vscode.Uri.file(this.context.extensionContext.storagePath + '/' + this.server.id + '_' + this.id + '.mongo')
if (!fs.existsSync(this.shellUri.fsPath)) {
this.shellUri = this.shellUri.with({ scheme: 'untitled' });
}
this.context.extensionContext.subscriptions.push(vscode.window.onDidChangeActiveTextEditor((editor => {
if (editor.document.uri.toString() === this.shellUri.toString()) {
vscode.window.setStatusBarMessage('Connected to ' + id);
}
})));
}
hasChildren(): boolean { return false; }
loadChildren(): Promise<IMongoResource[]> {
return Promise.resolve([]);
}
getDb(): Promise<Db> {
const uri = vscode.Uri.parse(this.server.id);
const connectionString = `${uri.scheme}://${uri.authority}/${this.id}?${uri.query}`
return <Promise<Db>>MongoClient.connect(connectionString)
.then(db => {
return db.db(this.id)
});
}
getMongoShellUri(): vscode.Uri {
return this.shellUri;
}
executeScript(script: string): Promise<string> {
return this.getShell()
.then(() => this.shell.exec(script));
}
private getShell(): Promise<void> {
if (this.shell) {
return Promise.resolve();
}
const shellPath = <string>vscode.workspace.getConfiguration().get('mongo.shell.path')
if (!shellPath) {
return <Promise<null>>vscode.window.showInputBox({
placeHolder: "Configure the path to mongo shell executable",
ignoreFocusOut: true
}).then(value => vscode.workspace.getConfiguration().update('mongo.shell.path', value, true)
.then(() => this.createShell(value)));
} else {
return this.createShell(shellPath);
}
}
private createShell(shellPath: string): Promise<void> {
return <Promise<null>>Shell.create(shellPath, this.server.id)
.then(shell => {
this.shell = shell;
return this.shell.useDatabase(this.id).then(() => null);
}, error => vscode.window.showErrorMessage(error));
}
_executeScript(script: string): Promise<string> {
return this.getDb().then(db => {
return db.eval(new Code(`function() {
var result = ${script};
if (result.hasNext) {
let results = [];
for (let counter = 0; counter < 20 && result.hasNext(); counter++) {
results.push(result.next());
}
return results;
} else {
return result;
}
}`), [], { readPreference: ReadPreference.PRIMARY }).then(result => {
db.close();
return JSON.stringify(result, null, '\t')
}, error => {
console.log(error);
});
});
}
}

104
src/mongo/shell.ts Normal file
Просмотреть файл

@ -0,0 +1,104 @@
import * as cp from 'child_process';
import * as vscode from 'vscode';
import * as os from 'os';
import { IDisposable, toDisposable, dispose } from './../util';
import { EventEmitter, window } from 'vscode';
export class Shell {
private executionId: number = 0;
private disposables: IDisposable[] = [];
private onResult: EventEmitter<{ exitCode, result, stderr }> = new EventEmitter<{ exitCode, result, stderr }>();
public static create(execPath: string, connectionString: string): Promise<Shell> {
return new Promise((c, e) => {
try {
cp.exec(`which ${execPath}`, err => {
if (err) {
e(`Error while creating mongo shell with path ${execPath}: ${err}`);
}
const shellProcess = cp.spawn(execPath, ['--quiet', connectionString]);
return c(new Shell(shellProcess));
});
} catch (error) {
e(`Error while creating mongo shell with path ${execPath}: ${error}`);
}
});
}
constructor(private mongoShell: cp.ChildProcess) {
this.initialize();
}
private initialize() {
const once = (ee: NodeJS.EventEmitter, name: string, fn: Function) => {
ee.once(name, fn);
this.disposables.push(toDisposable(() => ee.removeListener(name, fn)));
};
const on = (ee: NodeJS.EventEmitter, name: string, fn: Function) => {
ee.on(name, fn);
this.disposables.push(toDisposable(() => ee.removeListener(name, fn)));
};
once(this.mongoShell, 'error', result => this.onResult.fire(result));
once(this.mongoShell, 'exit', result => this.onResult.fire(result));
let buffers: string[] = [];
on(this.mongoShell.stdout, 'data', b => {
if ((<string>b.toString()).endsWith(`${this.executionId}\n`)) {
const result = buffers.join('');
buffers = [];
this.onResult.fire({
exitCode: void 0,
result,
stderr: void 0
});
} else {
buffers.push(b);
}
});
on(this.mongoShell.stderr, 'data', result => this.onResult.fire(result));
once(this.mongoShell.stderr, 'close', result => this.onResult.fire(result));
}
async useDatabase(databse: string): Promise<string> {
return this.exec(`use ${databse}`);
}
async exec(script: string): Promise<string> {
const executionId = this._generateExecutionSequenceId();
try {
this.mongoShell.stdin.write(script, 'utf8');
this.mongoShell.stdin.write(os.EOL);
this.mongoShell.stdin.write(executionId, 'utf8');
this.mongoShell.stdin.write(os.EOL);
} catch (error) {
window.showErrorMessage(error.toString());
}
const disposables: IDisposable[] = [];
const once = (ee: NodeJS.EventEmitter, name: string, fn: Function) => {
ee.once(name, fn);
disposables.push(toDisposable(() => ee.removeListener(name, fn)));
};
return await new Promise<string>((c, e) => {
const disposable = this.onResult.event(result => {
disposable.dispose();
let lines = (<string>result.result).split('\n').filter(line => !!line && line !== 'Type "it" for more');
lines = lines[lines.length - 1] === 'Type "it" for more' ? lines.splice(lines.length - 1, 1) : lines;
let value = lines.join('\n');
c(lines.join('\n'));
})
});
}
private _generateExecutionSequenceId(): string {
return `${++this.executionId}`;
}
}

2
src/typings/index.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,2 @@
/// <reference path="modules/mongodb/index.d.ts" />
/// <reference path='./vscode.proposed.d.ts'/>

1468
src/typings/modules/mongodb/index.d.ts поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,21 @@
{
"resolution": "main",
"tree": {
"src": "https://raw.githubusercontent.com/Think7/typings-mongodb/83d989a208affd72c130e1c06862363e702f87a3/typings.json",
"raw": "registry:npm/mongodb#2.1.0+20170211100415",
"main": "mongodb.d.ts",
"global": false,
"dependencies": {
"es6-promise": {
"src": "https://raw.githubusercontent.com/types/npm-es6-promise/fb04188767acfec1defd054fc8024fafa5cd4de7/typings.json",
"raw": "registry:common/es6-promise#4.0.0+20161003185919",
"main": "dist/es6-promise.d.ts",
"global": false,
"name": "es6-promise",
"type": "typings"
}
},
"name": "mongodb",
"type": "typings"
}
}

169
src/typings/vscode.proposed.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,169 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// This is the place for API experiments and proposal.
import { CancellationToken, Uri, Disposable, ProviderResult, Event, TextDocument } from 'vscode';
declare module 'vscode' {
/**
* Defines a generalized way of reporing progress updates.
*/
export interface Progress<T> {
/**
* Report a progress update.
* @param value A progress item, like a message or an updated percentage value
*/
report(value: T): void
}
export namespace window {
/**
* Show window-wide progress, e.g. in the status bar, for the provided task. The task is
* considering running as long as the promise it returned isn't resolved or rejected.
*
* @param task A function callback that represents a long running operation.
*/
export function withWindowProgress<R>(title: string, task: (progress: Progress<string>, token: CancellationToken) => Thenable<R>): Thenable<R>;
export function withScmProgress<R>(task: (progress: Progress<number>) => Thenable<R>): Thenable<R>;
export function sampleFunction(): Thenable<any>;
}
export namespace window {
/**
* Register a [TreeExplorerNodeProvider](#TreeExplorerNodeProvider).
*
* @param providerId A unique id that identifies the provider.
* @param provider A [TreeExplorerNodeProvider](#TreeExplorerNodeProvider).
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
*/
export function registerTreeExplorerNodeProvider(providerId: string, provider: TreeExplorerNodeProvider<any>): Disposable;
}
/**
* A node provider for a tree explorer contribution.
*
* Providers are registered through (#window.registerTreeExplorerNodeProvider) with a
* `providerId` that corresponds to the `treeExplorerNodeProviderId` in the extension's
* `contributes.explorer` section.
*
* The contributed tree explorer will ask the corresponding provider to provide the root
* node and resolve children for each node. In addition, the provider could **optionally**
* provide the following information for each node:
* - label: A human-readable label used for rendering the node.
* - hasChildren: Whether the node has children and is expandable.
* - clickCommand: A command to execute when the node is clicked.
*/
export interface TreeExplorerNodeProvider<T> {
/**
* Provide the root node. This function will be called when the tree explorer is activated
* for the first time. The root node is hidden and its direct children will be displayed on the first level of
* the tree explorer.
*
* @return The root node.
*/
provideRootNode(): T | Thenable<T>;
/**
* Resolve the children of `node`.
*
* @param node The node from which the provider resolves children.
* @return Children of `node`.
*/
resolveChildren(node: T): T[] | Thenable<T[]>;
/**
* Provide a human-readable string that will be used for rendering the node. Default to use
* `node.toString()` if not provided.
*
* @param node The node from which the provider computes label.
* @return A human-readable label.
*/
getLabel?(node: T): string;
/**
* Determine if `node` has children and is expandable. Default to `true` if not provided.
*
* @param node The node to determine if it has children and is expandable.
* @return A boolean that determines if `node` has children and is expandable.
*/
getHasChildren?(node: T): boolean;
/**
* Get the command to execute when `node` is clicked.
*
* Commands can be registered through [registerCommand](#commands.registerCommand). `node` will be provided
* as the first argument to the command's callback function.
*
* @param node The node that the command is associated with.
* @return The command to execute when `node` is clicked.
*/
getClickCommand?(node: T): string;
}
export interface SCMResourceThemableDecorations {
readonly iconPath?: string | Uri;
}
export interface SCMResourceDecorations extends SCMResourceThemableDecorations {
readonly strikeThrough?: boolean;
readonly light?: SCMResourceThemableDecorations;
readonly dark?: SCMResourceThemableDecorations;
}
export interface SCMResource {
readonly uri: Uri;
readonly decorations?: SCMResourceDecorations;
}
export interface SCMResourceGroup {
readonly id: string;
readonly label: string;
readonly resources: SCMResource[];
}
export interface SCMProvider {
readonly label: string;
readonly resources: SCMResourceGroup[];
readonly onDidChange: Event<SCMResourceGroup[]>;
readonly count?: number | undefined;
readonly state?: string;
getOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult<Uri>;
open?(resource: SCMResource, token: CancellationToken): ProviderResult<void>;
drag?(resource: SCMResource, resourceGroup: SCMResourceGroup, token: CancellationToken): ProviderResult<void>;
acceptChanges?(token: CancellationToken): ProviderResult<void>;
}
export interface SCMInputBox {
value: string;
readonly onDidChange: Event<string>;
}
export namespace scm {
export const onDidChangeActiveProvider: Event<SCMProvider>;
export let activeProvider: SCMProvider | undefined;
export const inputBox: SCMInputBox;
export function getResourceFromURI(uri: Uri): SCMResource | SCMResourceGroup | undefined;
export function registerSCMProvider(id: string, provider: SCMProvider): Disposable;
}
export interface LineChange {
readonly originalStartLineNumber: number;
readonly originalEndLineNumber: number;
readonly modifiedStartLineNumber: number;
readonly modifiedEndLineNumber: number;
}
export function computeDiff(oneDocument: TextDocument, otherDocument: TextDocument): Thenable<LineChange[]>;
}

150
src/util.ts Normal file
Просмотреть файл

@ -0,0 +1,150 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Event } from 'vscode';
import { dirname } from 'path';
import * as fs from 'fs';
export function log(...args: any[]): void {
console.log.apply(console, ['vscode-mongo:', ...args]);
}
export interface IDisposable {
dispose(): void;
}
export function dispose<T extends IDisposable>(disposables: T[]): T[] {
disposables.forEach(d => d.dispose());
return [];
}
export function toDisposable(dispose: () => void): IDisposable {
return { dispose };
}
export function combinedDisposable(disposables: IDisposable[]): IDisposable {
return toDisposable(() => dispose(disposables));
}
export const EmptyDisposable = toDisposable(() => null);
export function mapEvent<I, O>(event: Event<I>, map: (i: I) => O): Event<O> {
return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables);
}
export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Event<T> {
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
}
export function anyEvent<T>(...events: Event<T>[]): Event<T> {
return (listener, thisArgs = null, disposables?) => {
const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i))));
if (disposables) {
disposables.push(result);
}
return result;
};
}
export function done<T>(promise: Promise<T>): Promise<void> {
return promise.then<void>(() => void 0, () => void 0);
}
export function once<T>(event: Event<T>): Event<T> {
return (listener, thisArgs = null, disposables?) => {
const result = event(e => {
result.dispose();
return listener.call(thisArgs, e);
}, null, disposables);
return result;
};
}
export function eventToPromise<T>(event: Event<T>): Promise<T> {
return new Promise(c => once(event)(c));
}
// TODO@Joao: replace with Object.assign
export function assign<T>(destination: T, ...sources: any[]): T {
for (const source of sources) {
Object.keys(source).forEach(key => destination[key] = source[key]);
}
return destination;
}
export function uniqBy<T>(arr: T[], fn: (el: T) => string): T[] {
const seen = Object.create(null);
return arr.filter(el => {
const key = fn(el);
if (seen[key]) {
return false;
}
seen[key] = true;
return true;
});
}
export function groupBy<T>(arr: T[], fn: (el: T) => string): { [key: string]: T[] } {
return arr.reduce((result, el) => {
const key = fn(el);
result[key] = [...(result[key] || []), el];
return result;
}, Object.create(null));
}
export function denodeify<R>(fn: Function): (...args) => Promise<R> {
return (...args) => new Promise((c, e) => fn(...args, (err, r) => err ? e(err) : c(r)));
}
export function nfcall<R>(fn: Function, ...args): Promise<R> {
return new Promise((c, e) => fn(...args, (err, r) => err ? e(err) : c(r)));
}
export async function mkdirp(path: string, mode?: number): Promise<boolean> {
const mkdir = async () => {
try {
await nfcall(fs.mkdir, path, mode);
} catch (err) {
if (err.code === 'EEXIST') {
const stat = await nfcall<fs.Stats>(fs.stat, path);
if (stat.isDirectory) {
return;
}
throw new Error(`'${path}' exists and is not a directory.`);
}
throw err;
}
};
// is root?
if (path === dirname(path)) {
return true;
}
try {
await mkdir();
} catch (err) {
if (err.code !== 'ENOENT') {
throw err;
}
await mkdirp(dirname(path), mode);
await mkdir();
}
return true;
}

22
test/extension.test.ts Normal file
Просмотреть файл

@ -0,0 +1,22 @@
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//
// The module 'assert' provides assertion methods from node
import * as assert from 'assert';
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
import * as myExtension from '../src/extension';
// Defines a Mocha test suite to group tests of similar kind together
suite("Extension Tests", () => {
// Defines a Mocha unit test
test("Something 1", () => {
assert.equal(-1, [1, 2, 3].indexOf(5));
assert.equal(-1, [1, 2, 3].indexOf(0));
});
});

22
test/index.ts Normal file
Просмотреть файл

@ -0,0 +1,22 @@
//
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
//
// This file is providing the test runner to use when running extension tests.
// By default the test runner in use is Mocha based.
//
// You can provide your own test runner if you want to override it by exporting
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
// host can call to run the tests. The test runner is expected to use console.log
// to report the results back to the caller. When the tests are finished, return
// a possible error to the callback or null if none.
var testRunner = require('vscode/lib/testrunner');
// You can directly control Mocha options by uncommenting the following lines
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
testRunner.configure({
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
useColors: true // colored output from test results
});
module.exports = testRunner;

16
tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6"
],
"sourceMap": true,
"rootDir": "."
},
"exclude": [
"node_modules",
".vscode-test"
]
}