Skip to content
Merged
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
6 changes: 3 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
"editor.formatOnSave": true,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"prettier.tabWidth": 4
}
}
40 changes: 40 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,18 @@
"title": "Run as Task",
"category": "Python Envs",
"icon": "$(play)"
},
{
"command": "python-envs.terminal.activate",
"title": "Activate Environment in Current Terminal",
"category": "Python Envs",
"icon": "$(zap)"
},
{
"command": "python-envs.terminal.deactivate",
"title": "Deactivate Environment in Current Terminal",
"category": "Python Envs",
"icon": "$(circle-slash)"
}
],
"menus": {
Expand Down Expand Up @@ -234,6 +246,14 @@
{
"command": "python-envs.runAsTask",
"when": "true"
},
{
"command": "python-envs.terminal.activate",
"when": "false"
},
{
"command": "python-envs.terminal.deactivate",
"when": "false"
}
],
"view/item/context": [
Expand Down Expand Up @@ -315,6 +335,16 @@
"command": "python-envs.refreshAllManagers",
"group": "navigation",
"when": "view == env-managers"
},
{
"command": "python-envs.terminal.activate",
"group": "navigation",
"when": "view == terminal && pythonTerminalActivation && !pythonTerminalActivated"
},
{
"command": "python-envs.terminal.deactivate",
"group": "navigation",
"when": "view == terminal && pythonTerminalActivation && pythonTerminalActivated"
}
],
"explorer/context": [
Expand All @@ -335,6 +365,16 @@
"group": "Python",
"when": "editorLangId == python"
}
],
"terminal/title/context": [
{
"command": "python-envs.terminal.activate",
"when": "pythonTerminalActivation && !pythonTerminalActivated"
},
{
"command": "python-envs.terminal.deactivate",
"when": "pythonTerminalActivation && pythonTerminalActivated"
}
]
},
"viewsContainers": {
Expand Down
70 changes: 69 additions & 1 deletion src/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Uri, Disposable, MarkdownString, Event, LogOutputChannel, ThemeIcon } from 'vscode';
import { Uri, Disposable, MarkdownString, Event, LogOutputChannel, ThemeIcon, Terminal } from 'vscode';

/**
* The path to an icon, or a theme-specific configuration of icons.
Expand Down Expand Up @@ -767,6 +767,40 @@ export interface Installable {
readonly uri?: Uri;
}

export interface PythonTaskResult {}

export interface PythonProcess {
/**
* The process ID of the Python process.
*/
readonly pid: number;

/**
* The standard input of the Python process.
*/
readonly stdin: NodeJS.WritableStream;

/**
* The standard output of the Python process.
*/
readonly stdout: NodeJS.ReadableStream;

/**
* The standard error of the Python process.
*/
readonly stderr: NodeJS.ReadableStream;

/**
* Kills the Python process.
*/
kill(): void;

/**
* Event that is fired when the Python process exits.
*/
onExit: Event<number>;
}

export interface PythonEnvironmentManagerApi {
/**
* Register an environment manager implementation.
Expand Down Expand Up @@ -963,6 +997,40 @@ export interface PythonProjectApi {
registerPythonProjectCreator(creator: PythonProjectCreator): Disposable;
}

export interface PythonExecutionApi {
createTerminal(
cwd: string | Uri | PythonProject,
environment?: PythonEnvironment,
envVars?: { [key: string]: string },
): Promise<Terminal>;
runInTerminal(
environment: PythonEnvironment,
cwd: string | Uri | PythonProject,
command: string,
args?: string[],
): Promise<Terminal>;
runInDedicatedTerminal(
terminalKey: string | Uri,
environment: PythonEnvironment,
cwd: string | Uri | PythonProject,
command: string,
args?: string[],
): Promise<Terminal>;
runAsTask(
environment: PythonEnvironment,
cwd: string | Uri | PythonProject,
command: string,
args?: string[],
envVars?: { [key: string]: string },
): Promise<PythonTaskResult>;
runInBackground(
environment: PythonEnvironment,
cwd: string | Uri | PythonProject,
command: string,
args?: string[],
envVars?: { [key: string]: string },
): Promise<PythonProcess>;
}
/**
* The API for interacting with Python environments, package managers, and projects.
*/
Expand Down
5 changes: 5 additions & 0 deletions src/common/command.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { commands } from 'vscode';

export function executeCommand<T = unknown>(command: string, ...rest: any[]): Thenable<T> {
return commands.executeCommand(command, ...rest);
}
29 changes: 29 additions & 0 deletions src/common/window.apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Terminal,
TerminalOptions,
TerminalShellExecutionEndEvent,
TerminalShellExecutionStartEvent,
TerminalShellIntegrationChangeEvent,
TextEditor,
Uri,
Expand Down Expand Up @@ -53,6 +54,34 @@ export function activeTerminal(): Terminal | undefined {
return window.activeTerminal;
}

export function activeTextEditor(): TextEditor | undefined {
return window.activeTextEditor;
}

export function onDidChangeActiveTerminal(
listener: (e: Terminal | undefined) => any,
thisArgs?: any,
disposables?: Disposable[],
): Disposable {
return window.onDidChangeActiveTerminal(listener, thisArgs, disposables);
}

export function onDidChangeActiveTextEditor(
listener: (e: TextEditor | undefined) => any,
thisArgs?: any,
disposables?: Disposable[],
): Disposable {
return window.onDidChangeActiveTextEditor(listener, thisArgs, disposables);
}

export function onDidStartTerminalShellExecution(
listener: (e: TerminalShellExecutionStartEvent) => any,
thisArgs?: any,
disposables?: Disposable[],
): Disposable {
return window.onDidStartTerminalShellExecution(listener, thisArgs, disposables);
}

export function onDidEndTerminalShellExecution(
listener: (e: TerminalShellExecutionEndEvent) => any,
thisArgs?: any,
Expand Down
56 changes: 49 additions & 7 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
resetEnvironmentCommand,
refreshPackagesCommand,
createAnyEnvironmentCommand,
runInDedicatedTerminalCommand,
} from './features/envCommands';
import { registerCondaFeatures } from './managers/conda/main';
import { registerSystemPythonFeatures } from './managers/sysPython/main';
Expand All @@ -37,6 +38,13 @@ import {
} from './features/projectCreators';
import { WorkspaceView } from './features/views/projectView';
import { registerCompletionProvider } from './features/settings/settingCompletions';
import { TerminalManager, TerminalManagerImpl } from './features/terminal/terminalManager';
import { activeTerminal, onDidChangeActiveTerminal, onDidChangeActiveTextEditor } from './common/window.apis';
import {
getEnvironmentForTerminal,
setActivateMenuButtonContext,
updateActivateMenuButtonContext,
} from './features/terminal/activateMenuButton';

export async function activate(context: ExtensionContext): Promise<PythonEnvironmentApi> {
// Logging should be set up before anything else.
Expand All @@ -46,6 +54,9 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
// Setup the persistent state for the extension.
setPersistentState(context);

const terminalManager: TerminalManager = new TerminalManagerImpl();
context.subscriptions.push(terminalManager);

const projectManager: PythonProjectManager = new PythonProjectManagerImpl();
context.subscriptions.push(projectManager);

Expand Down Expand Up @@ -104,23 +115,25 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
if (result) {
const projects: PythonProject[] = [];
result.forEach((r) => {
if (r.projects) {
projects.push(r.projects);
if (r.project) {
projects.push(r.project);
}
});
workspaceView.updateProject(projects);
await updateActivateMenuButtonContext(terminalManager, projectManager, envManagers);
}
}),
commands.registerCommand('python-envs.setEnv', async (item) => {
const result = await setEnvironmentCommand(item, envManagers, projectManager);
if (result) {
const projects: PythonProject[] = [];
result.forEach((r) => {
if (r.projects) {
projects.push(r.projects);
if (r.project) {
projects.push(r.project);
}
});
workspaceView.updateProject(projects);
await updateActivateMenuButtonContext(terminalManager, projectManager, envManagers);
}
}),
commands.registerCommand('python-envs.reset', async (item) => {
Expand All @@ -143,15 +156,44 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
await envManagers.clearCache(undefined);
}),
commands.registerCommand('python-envs.runInTerminal', (item) => {
return runInTerminalCommand(item, api);
return runInTerminalCommand(item, api, terminalManager);
}),
commands.registerCommand('python-envs.runInDedicatedTerminal', (item) => {
return runInDedicatedTerminalCommand(item, api, terminalManager);
}),
commands.registerCommand('python-envs.runAsTask', (item) => {
return runAsTaskCommand(item, api);
}),
commands.registerCommand('python-envs.createTerminal', (item) => {
return createTerminalCommand(item, api);
return createTerminalCommand(item, api, terminalManager);
}),
commands.registerCommand('python-envs.terminal.activate', async () => {
const terminal = activeTerminal();
if (terminal) {
const env = await getEnvironmentForTerminal(terminalManager, projectManager, envManagers, terminal);
if (env) {
await terminalManager.activate(terminal, env);
await setActivateMenuButtonContext(terminalManager, terminal, env);
}
}
}),
commands.registerCommand('python-envs.terminal.deactivate', async () => {
const terminal = activeTerminal();
if (terminal) {
await terminalManager.deactivate(terminal);
const env = await getEnvironmentForTerminal(terminalManager, projectManager, envManagers, terminal);
if (env) {
await setActivateMenuButtonContext(terminalManager, terminal, env);
}
}
}),
envManagers.onDidChangeEnvironmentManager(async () => {
await updateActivateMenuButtonContext(terminalManager, projectManager, envManagers);
}),
onDidChangeActiveTerminal(async (t) => {
await updateActivateMenuButtonContext(terminalManager, projectManager, envManagers, t);
}),
window.onDidChangeActiveTextEditor(async (e: TextEditor | undefined) => {
onDidChangeActiveTextEditor(async (e: TextEditor | undefined) => {
if (e && !e.document.isUntitled && e.document.uri.scheme === 'file') {
if (
e.document.languageId === 'python' ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,24 @@ export function getActivationCommand(

return activation;
}

export function getDeactivationCommand(
terminal: Terminal,
environment: PythonEnvironment,
): PythonCommandRunConfiguration[] | undefined {
const shell = identifyTerminalShell(terminal);

let deactivation: PythonCommandRunConfiguration[] | undefined;
if (environment.execInfo?.shellDeactivation) {
deactivation = environment.execInfo.shellDeactivation.get(shell);
if (!deactivation) {
deactivation = environment.execInfo.shellDeactivation.get(TerminalShellType.unknown);
}
}

if (!deactivation) {
deactivation = environment.execInfo?.deactivation;
}

return deactivation;
}
Loading