Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start adding python support #622

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion vscode-wpilib/src/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ interface ITaskRunnerQuickPick {
taskRunner: ITaskRunner;
}

export class ExecuteAPI implements IExecuteAPI {
export interface IExecuteAPIEx {
executeProcessCommand(command: string, name: string, args: string[], rootDir: string, workspace: vscode.WorkspaceFolder): Promise<number>;
executePythonCommand(args: string[], rootDir: string, workspace: vscode.WorkspaceFolder, name: string): Promise<number>;
}

export class ExecuteAPI implements IExecuteAPI, IExecuteAPIEx {
private runners: ITaskRunner[] = [];

constructor() {
Expand All @@ -39,6 +44,34 @@ export class ExecuteAPI implements IExecuteAPI {
});
}

public async executeProcessCommand(command: string, name: string, args: string[], rootDir: string,
workspace: vscode.WorkspaceFolder): Promise<number> {
const process = new vscode.ProcessExecution(command, args, {
cwd: rootDir
});
logger.log('executing process in workspace', process, workspace.uri.fsPath);

const task = new vscode.Task({ type: 'wpilibprocexec'}, workspace, name, 'wpilib', process);
task.presentationOptions.echo = true;
task.presentationOptions.clear = true;
const execution = await vscode.tasks.executeTask(task);
const runner: ITaskRunner = {
cancelled: false,
condition: new PromiseCondition(-1),
execution,

};
this.runners.push(runner);
return runner.condition.wait();
}

public async executePythonCommand(args: string[], rootDir: string, workspace: vscode.WorkspaceFolder, name: string): Promise<number> {
const configuration = vscode.workspace.getConfiguration('python', workspace.uri);
const interpreter: string = configuration.get('pythonPath', 'python');

return this.executeProcessCommand(interpreter, name, args, rootDir, workspace);
}

public async executeCommand(command: string, name: string, rootDir: string, workspace: vscode.WorkspaceFolder,
env?: { [key: string]: string }): Promise<number> {
const shell = new vscode.ShellExecution(command, {
Expand Down
14 changes: 12 additions & 2 deletions vscode-wpilib/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { CommandAPI } from './commandapi';
import { activateCpp } from './cpp/cpp';
import { ApiProvider } from './cppprovider/apiprovider';
import { DeployDebugAPI } from './deploydebugapi';
import { ExecuteAPI } from './executor';
import { ExecuteAPI, IExecuteAPIEx } from './executor';
import { activateJava } from './java/java';
import { findJdkPath } from './jdkdetector';
import { localize as i18n } from './locale';
Expand All @@ -36,9 +36,14 @@ import { Gradle2020Import } from './webviews/gradle2020import';
import { Help } from './webviews/help';
import { ProjectCreator } from './webviews/projectcreator';
import { WPILibUpdates } from './wpilibupdates';
import { activatePython } from './python/python';

export interface IExternalAPIEx extends IExternalAPI {
getExecuteAPIEx(): IExecuteAPIEx;
}

// External API class to implement the IExternalAPI interface
class ExternalAPI implements IExternalAPI {
class ExternalAPI implements IExternalAPI, IExternalAPIEx {
// Create method is used because constructors cannot be async.
public static async Create(resourceFolder: string): Promise<ExternalAPI> {
const preferencesApi = await PreferencesAPI.Create();
Expand Down Expand Up @@ -92,6 +97,9 @@ class ExternalAPI implements IExternalAPI {
public getUtilitiesAPI(): UtilitiesAPI {
return this.utilitiesApi;
}
public getExecuteAPIEx(): IExecuteAPIEx {
return this.executeApi;
}
}

let updatePromptCount = 0;
Expand All @@ -117,6 +125,8 @@ async function handleAfterTrusted(externalApi: ExternalAPI, context: vscode.Exte
await activateCpp(context, externalApi);
// Active the java parts of the extension
await activateJava(context, externalApi);
// Activate the python parts of the extension
await activatePython(context, externalApi);

try {
// Add built in tools
Expand Down
100 changes: 100 additions & 0 deletions vscode-wpilib/src/python/deploydebug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use strict';

import * as vscode from 'vscode';
import { ICodeDeployer, IPreferencesAPI } from 'vscode-wpilibapi';
import { IExternalAPIEx } from '../extension';
import { PyPreferencesAPI } from './pypreferencesapi';
import { IExecuteAPIEx } from '../executor';

function getCurrentFileIfPython(): string | undefined {
const currentEditor = vscode.window.activeTextEditor;
if (currentEditor === undefined) {
return undefined;
}
if (currentEditor.document.fileName.endsWith('.py')) {
return currentEditor.document.fileName;
}
return undefined;
}

class DeployCodeDeployer implements ICodeDeployer {
private preferences: IPreferencesAPI;
private pyPreferences : PyPreferencesAPI;
private executeApi: IExecuteAPIEx;

constructor(externalApi: IExternalAPIEx, pyPreferences: PyPreferencesAPI) {
this.preferences = externalApi.getPreferencesAPI();
this.executeApi = externalApi.getExecuteAPIEx();
this.pyPreferences = pyPreferences;
}

public async getIsCurrentlyValid(workspace: vscode.WorkspaceFolder): Promise<boolean> {
const prefs = this.preferences.getPreferences(workspace);
const currentLanguage = prefs.getCurrentLanguage();
return currentLanguage === 'none' || currentLanguage === 'python';
}

public async runDeployer(_teamNumber: number, workspace: vscode.WorkspaceFolder,
source: vscode.Uri | undefined, ..._args: string[]): Promise<boolean> {
let file: string = '';
if (source === undefined) {
const cFile = getCurrentFileIfPython();
if (cFile !== undefined) {
file = cFile;
} else {
const mFile = await this.pyPreferences.getPreferences(workspace).getMainFile();
if (mFile === undefined) {
return false;
}
file = mFile;
}
} else {
file = source.fsPath;
}

const prefs = this.preferences.getPreferences(workspace);

const deploy = [file, 'deploy', `--team=${await prefs.getTeamNumber()}`];

if (prefs.getSkipTests()) {
deploy.push('--skip-tests');
}

const result = await this.executeApi.executePythonCommand(deploy, workspace.uri.fsPath, workspace, 'Python Deploy');

return result === 0;
return false;
}

public getDisplayName(): string {
return 'python';
}

public getDescription(): string {
return 'Python Deploy';
}
}

export class DeployDebug {
private deployDeployer: DeployCodeDeployer;

constructor(externalApi: IExternalAPIEx, pyPreferences: PyPreferencesAPI) {
const deployDebugApi = externalApi.getDeployDebugAPI();
deployDebugApi.addLanguageChoice('python');

// this.deployDebuger = new DebugCodeDeployer(externalApi);
this.deployDeployer = new DeployCodeDeployer(externalApi, pyPreferences);
// this.simulator = new SimulateCodeDeployer(externalApi);

deployDebugApi.registerCodeDeploy(this.deployDeployer);

// if (allowDebug) {
// deployDebugApi.registerCodeDebug(this.deployDebuger);
// deployDebugApi.registerCodeSimulate(this.simulator);
// }
}

// tslint:disable-next-line:no-empty
public dispose() {
}
}
117 changes: 117 additions & 0 deletions vscode-wpilib/src/python/pypreferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
'use strict';
import * as fs from 'fs';
import * as jsonc from 'jsonc-parser';
import * as path from 'path';
import * as vscode from 'vscode';
import { mkdirAsync, writeFileAsync } from '../utilities';

interface IPreferencesJson {
mainFile?: string;
}

const defaultPreferences: IPreferencesJson = {
};

export class PyPreferences {
public workspace: vscode.WorkspaceFolder;
private preferencesFile?: vscode.Uri;
private readonly configFolder: string;
private readonly preferenceFileName: string = 'python_preferences.json';
private preferencesJson: IPreferencesJson;
private configFileWatcher: vscode.FileSystemWatcher;
private readonly preferencesGlob: string = '**/' + this.preferenceFileName;
private disposables: vscode.Disposable[] = [];

constructor(workspace: vscode.WorkspaceFolder) {
this.workspace = workspace;
this.configFolder = path.join(workspace.uri.fsPath, '.wpilib');

const configFilePath = path.join(this.configFolder, this.preferenceFileName);

if (fs.existsSync(configFilePath)) {
this.preferencesFile = vscode.Uri.file(configFilePath);
this.preferencesJson = defaultPreferences;
this.updatePreferences();
} else {
// Set up defaults, and create
this.preferencesJson = defaultPreferences;
}

const rp = new vscode.RelativePattern(workspace, this.preferencesGlob);

this.configFileWatcher = vscode.workspace.createFileSystemWatcher(rp);
this.disposables.push(this.configFileWatcher);

this.configFileWatcher.onDidCreate((uri) => {
this.preferencesFile = uri;
this.updatePreferences();
});

this.configFileWatcher.onDidDelete(() => {
this.preferencesFile = undefined;
this.updatePreferences();
});

this.configFileWatcher.onDidChange(() => {
this.updatePreferences();
});
}

public async getMainFile(): Promise<string | undefined> {
if (this.preferencesJson.mainFile === undefined) {
const selection = await this.requestMainFile();
if (selection !== undefined) {
await this.setMainFile(selection);
return selection;
}
}
return this.preferencesJson.mainFile;
}

public async setMainFile(file: string): Promise<void> {
this.preferencesJson.mainFile = file;
await this.writePreferences();
}

public dispose() {
for (const d of this.disposables) {
d.dispose();
}
}

private async requestMainFile(): Promise<string | undefined> {
const glob = await vscode.workspace.findFiles(new vscode.RelativePattern(this.workspace, '*.py'));
if (glob.length === 0) {
return undefined;
}

const map = glob.map((v) => {
return path.basename(v.fsPath);
});

const selection = await vscode.window.showQuickPick(map, {
placeHolder: 'Pick a file to be your main file',
});

return selection;
}

private updatePreferences() {
if (this.preferencesFile === undefined) {
this.preferencesJson = defaultPreferences;
return;
}

const results = fs.readFileSync(this.preferencesFile.fsPath, 'utf8');
this.preferencesJson = jsonc.parse(results) as IPreferencesJson;
}

private async writePreferences(): Promise<void> {
if (this.preferencesFile === undefined) {
const configFilePath = path.join(this.configFolder, this.preferenceFileName);
this.preferencesFile = vscode.Uri.file(configFilePath);
await mkdirAsync(path.dirname(this.preferencesFile.fsPath));
}
await writeFileAsync(this.preferencesFile.fsPath, JSON.stringify(this.preferencesJson, null, 4));
}
}
Loading
Loading