Initial commit: Push working extension changes
This commit is contained in:
Родитель
20a6556672
Коммит
e4bec35280
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 })));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/// <reference path="modules/mongodb/index.d.ts" />
|
||||
/// <reference path='./vscode.proposed.d.ts'/>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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[]>;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
});
|
||||
});
|
|
@ -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;
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "out",
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "."
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode-test"
|
||||
]
|
||||
}
|
Загрузка…
Ссылка в новой задаче