Skip to content

Commit

Permalink
PSES integrated shell is up
Browse files Browse the repository at this point in the history
  • Loading branch information
Yatao Li committed Jun 30, 2019
1 parent 6f24178 commit 6deda6a
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 55 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "PowerShell Language Support using PowerShell Editor Services",
"author": "Yatao Li, Tyler Leonhardt, Cory Knox",
"license": "MIT",
"version": "0.0.4",
"version": "0.0.5",
"publisher": "yatli, tylerl0706, corbob",
"repository": {
"type": "git",
Expand Down
72 changes: 19 additions & 53 deletions src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,67 +7,33 @@
import * as crypto from "crypto";
import * as fs from 'fs';
import * as path from 'path';
import * as net from 'net';
import { commands, workspace, ExtensionContext, events } from 'coc.nvim';
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'coc.nvim';
import { getDefaultPowerShellPath, getPlatformDetails } from './platform';
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, StreamInfo } from 'coc.nvim';
import { fileURLToPath } from './utils'
import * as settings from './settings';
import Shell from "node-powershell";

// Important paths.
const config = settings.load()
const cocPowerShellRoot = path.join(__dirname, "..", "..");
const bundledModulesPath = path.join(cocPowerShellRoot, "PowerShellEditorServices");
const logPath = path.join(cocPowerShellRoot, `/.pses/logs/${crypto.randomBytes(16).toString("hex")}-${process.pid}`);
const logger = workspace.createOutputChannel('powershell')
import { getDefaultPowerShellPath, getPlatformDetails } from './platform';
import settings = require("./settings");
import * as process from './process';

export async function activate(context: ExtensionContext) {

let config = settings.load()
let pwshPath = config.powerShellExePath
? config.powerShellExePath
? this.config.powerShellExePath
: getDefaultPowerShellPath(getPlatformDetails())

logger.appendLine("starting.")
logger.appendLine(`pwshPath = ${pwshPath}`)
logger.appendLine(`bundledModulesPath = ${bundledModulesPath}`)

// If PowerShellEditorServices is not downloaded yet, run the install script to do so.
if (!fs.existsSync(bundledModulesPath)) {
let notification = workspace.createStatusBarItem(0, { progress: true})
notification.text = "Downloading PowerShellEditorServices..."
notification.show()

const ps = new Shell({
executionPolicy: 'Bypass',
noProfile: true
});

ps.addCommand(path.join(cocPowerShellRoot, "src", "downloadPSES.ps1"));
await ps.invoke()
.catch(e => logger.appendLine("error downloading PSES: " + e))
.finally(() => {
notification.hide()
notification.dispose()
});
let proc = new process.PowerShellProcess(config, pwshPath, "PowerShell REPL")

}

let serverOptions: ServerOptions = {
command: pwshPath,
args: [
"-NoProfile",
"-NonInteractive",
path.join(bundledModulesPath, "/PowerShellEditorServices/Start-EditorServices.ps1"),
"-HostName", "coc.vim",
"-HostProfileId", "0",
"-HostVersion", "2.0.0",
"-LogPath", path.join(logPath, "log.txt"),
"-LogLevel", "Diagnostic",
"-BundledModulesPath", bundledModulesPath,
"-Stdio",
"-SessionDetailsPath", path.join(logPath, "session")],
transport: TransportKind.stdio
}
let sessionDetails = await proc.start()
let socket = net.connect(sessionDetails.languageServicePipeName);
let streamInfo = () => new Promise<StreamInfo>((resolve, reject) => {
socket.on(
"connect",
() => {
proc.log.appendLine("Language service connected.");
resolve({writer: socket, reader: socket});
});
});

workspace.addRootPatterns('ps1', ['*.ps1', '*.psd1', '*.psm1', '.vim', '.git', '.hg'])

Expand All @@ -88,7 +54,7 @@ export async function activate(context: ExtensionContext) {
}

// Create the language client and start the client.
let client = new LanguageClient('ps1', 'PowerShell Language Server', serverOptions, clientOptions);
let client = new LanguageClient('ps1', 'PowerShell Language Server', streamInfo, clientOptions);
let disposable = client.start();

// Status bar entry showing PS version
Expand Down
160 changes: 160 additions & 0 deletions src/client/process.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/

import cp = require("child_process");
import fs = require("fs");
import net = require("net");
import os = require("os");
import path = require("path");
import vscode = require("coc.nvim");
import Settings = require("./settings");
import utils = require("./utils");
import crypto = require("crypto");
import Shell from "node-powershell";

export class PowerShellProcess {
public static escapeSingleQuotes(pspath: string): string {
return pspath.replace(new RegExp("'", "g"), "''");
}

public onExited: vscode.Event<void>;
private onExitedEmitter = new vscode.Emitter<void>();

private consoleTerminal: vscode.Terminal = undefined;
private consoleCloseSubscription: vscode.Disposable;
private sessionFilePath: string
private sessionDetails: utils.IEditorServicesSessionDetails;

public log = vscode.workspace.createOutputChannel('powershell')
private cocPowerShellRoot = path.join(__dirname, "..", "..");
private bundledModulesPath = path.join(this.cocPowerShellRoot, "PowerShellEditorServices");

constructor(
private config: Settings.ISettings,
private pwshPath: string,
private title: string) {

this.onExited = this.onExitedEmitter.event;
}

public async start(): Promise<utils.IEditorServicesSessionDetails> {

// If PowerShellEditorServices is not downloaded yet, run the install script to do so.
if (!fs.existsSync(this.bundledModulesPath)) {
let notification = vscode.workspace.createStatusBarItem(0, { progress: true})
notification.text = "Downloading PowerShellEditorServices..."
notification.show()

const ps = new Shell({
executionPolicy: 'Bypass',
noProfile: true
});

ps.addCommand(path.join(this.cocPowerShellRoot, "src", "downloadPSES.ps1"));
await ps.invoke()
.catch(e => logger.appendLine("error downloading PSES: " + e))
.finally(() => {
notification.hide()
notification.dispose()
});

}

this.log.appendLine("starting.")
this.log.appendLine(`pwshPath = ${this.pwshPath}`)
this.log.appendLine(`bundledModulesPath = ${this.bundledModulesPath}`)

let logDir = path.join(this.cocPowerShellRoot, `/.pses/logs/${crypto.randomBytes(16).toString("hex")}-${process.pid}`)
utils.ensurePathExists(logDir)
this.sessionFilePath = path.join(logDir, "session")

// Make sure no old session file exists
utils.deleteSessionFile(this.sessionFilePath);

let powerShellArgs: string[] = []

// Only add ExecutionPolicy param on Windows
if (utils.isWindowsOS()) {
powerShellArgs.push("-ExecutionPolicy", "Bypass");
}

powerShellArgs.push(
"-NoProfile",
"-NonInteractive",
path.join(this.bundledModulesPath, "/PowerShellEditorServices/Start-EditorServices.ps1"),
"-HostName", "coc.vim",
"-HostProfileId", "0",
"-HostVersion", "2.0.0",
"-LogPath", path.join(logDir, "log.txt"),
"-LogLevel", "Diagnostic",
"-BundledModulesPath", this.bundledModulesPath,
"-EnableConsoleRepl",
"-SessionDetailsPath", this.sessionFilePath)


this.consoleTerminal = await vscode.workspace.createTerminal({
name: this.title,
shellPath: this.pwshPath,
shellArgs: powerShellArgs
})

if (!this.config.integratedConsole.showOnStartup) {
this.consoleTerminal.hide()
}

await new Promise((resolve, reject) => {
// Start the language client
utils.waitForSessionFile(
this.sessionFilePath,
(sessionDetails, error) => {
// Clean up the session file
utils.deleteSessionFile(this.sessionFilePath);

if (error) {
reject(error);
} else {
this.sessionDetails = sessionDetails;
resolve(this.sessionDetails);
}
});
})

this.consoleCloseSubscription =
vscode.workspace.onDidCloseTerminal(
(terminal) => {
if (terminal === this.consoleTerminal) {
this.log.appendLine("powershell.exe terminated or terminal UI was closed");
this.onExitedEmitter.fire();
}
}, this);

this.consoleTerminal.processId.then(
(pid) => { this.log.appendLine(`powershell.exe started, pid: ${pid}`); });

return this.sessionDetails
}

public showConsole(preserveFocus: boolean) {
if (this.consoleTerminal) {
this.consoleTerminal.show(preserveFocus);
}
}

public dispose() {

// Clean up the session file
utils.deleteSessionFile(this.sessionFilePath);

if (this.consoleCloseSubscription) {
this.consoleCloseSubscription.dispose();
this.consoleCloseSubscription = undefined;
}

if (this.consoleTerminal) {
this.log.appendLine("Terminating PowerShell process...");
this.consoleTerminal.dispose();
this.consoleTerminal = undefined;
}
}
}

0 comments on commit 6deda6a

Please sign in to comment.