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
40 changes: 21 additions & 19 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,26 @@
"LanguageServiceSurveyBanner.bannerMessage": "Can you please take 2 minutes to tell us how the Python Language Server is working for you?",
"LanguageServiceSurveyBanner.bannerLabelYes": "Yes, take survey now",
"LanguageServiceSurveyBanner.bannerLabelNo": "No, thanks",
"DataScience.unknownMimeType" : "Unknown mime type for data",
"DataScience.historyTitle" : "Python Interactive",
"DataScience.unknownMimeType": "Unknown mime type for data",
"DataScience.historyTitle": "Python Interactive",
"DataScience.badWebPanelFormatString": "<html><body><h1>{0} is not a valid file name</h1></body></html>",
"DataScience.sessionDisposed" : "Cannot execute code, session has been disposed.",
"DataScience.exportDialogTitle" : "Export to Jupyter Notebook",
"DataScience.exportDialogFilter" : "Jupyter Notebooks",
"DataScience.exportDialogComplete" : "Notebook written to {0}",
"DataScience.exportDialogFailed" : "Failed to export notebook. {0}",
"DataScience.exportOpenQuestion" : "Open in browser",
"DataScience.collapseInputTooltip" : "Collapse input block",
"DataScience.importDialogTitle" : "Import Jupyter Notebook",
"DataScience.importDialogFilter" : "Jupyter Notebooks",
"DataScience.notebookCheckForImportYes" : "Import",
"DataScience.notebookCheckForImportNo" : "Later",
"DataScience.notebookCheckForImportDontAskAgain" : "Don't Ask Again",
"DataScience.notebookCheckForImportTitle" : "Do you want to import the Jupyter Notebook into Python code?",
"DataScience.jupyterNotSupported" : "Jupyter is not installed",
"DataScience.jupyterNbConvertNotSupported" : "Jupyter nbconvert is not installed",
"DataScience.importingFormat" : "Importing {0}",
"DataScience.startingJupyter" : "Starting Jupyter backend"
"DataScience.sessionDisposed": "Cannot execute code, session has been disposed.",
"DataScience.exportDialogTitle": "Export to Jupyter Notebook",
"DataScience.exportDialogFilter": "Jupyter Notebooks",
"DataScience.exportDialogComplete": "Notebook written to {0}",
"DataScience.exportDialogFailed": "Failed to export notebook. {0}",
"DataScience.exportOpenQuestion": "Open in browser",
"DataScience.collapseInputTooltip": "Collapse input block",
"DataScience.importDialogTitle": "Import Jupyter Notebook",
"DataScience.importDialogFilter": "Jupyter Notebooks",
"DataScience.notebookCheckForImportYes": "Import",
"DataScience.notebookCheckForImportNo": "Later",
"DataScience.notebookCheckForImportDontAskAgain": "Don't Ask Again",
"DataScience.notebookCheckForImportTitle": "Do you want to import the Jupyter Notebook into Python code?",
"DataScience.jupyterNotSupported": "Jupyter is not installed",
"DataScience.jupyterNbConvertNotSupported": "Jupyter nbconvert is not installed",
"DataScience.importingFormat": "Importing {0}",
"DataScience.startingJupyter": "Starting Jupyter backend",
"Interpreters.RefreshingInterpreters": "Refreshing Python Interpreters",
"Interpreters.LoadingInterpreters": "Loading Python Interpreters"
}
2 changes: 1 addition & 1 deletion src/client/activation/interpreterDataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class InterpreterDataService {
}

const cacheKey = `InterpreterData-${interpreterPath}`;
let interpreterData = this.context.globalState.get(cacheKey) as InterpreterData;
let interpreterData = this.context.globalState.get<InterpreterData>(cacheKey);
let interpreterChanged = false;
if (interpreterData) {
// Check if interpreter executable changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ export class LanguageServerFolderService implements ILanguageServerFolderService
if (dirs.length === 0) {
return;
}
const sortedDirs = dirs.sort((a, b) => a.version.compare(b.version));
return sortedDirs[sortedDirs.length - 1];
dirs.sort((a, b) => a.version.compare(b.version));
return dirs[dirs.length - 1];
}
public async getExistingLanguageServerDirectories(): Promise<FolderVersionPair[]> {
const fs = this.serviceContainer.get<IFileSystem>(IFileSystem);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { DiagnosticCodes } from '../constants';
import { DiagnosticCommandPromptHandlerServiceId, MessageCommandPrompt } from '../promptHandler';
import { DiagnosticScope, IDiagnostic, IDiagnosticHandlerService, IInvalidPythonPathInDebuggerService } from '../types';

const InvalidPythonPathInDebuggerMessage = 'You need to select a Python interpreter before you start debugging.\n\nTip: click on "Select Python Environment" in the status bar.';
const InvalidPythonPathInDebuggerMessage = 'You need to select a Python interpreter before you start debugging.\n\nTip: click on "Select Python Interpreter" in the status bar.';

export class InvalidPythonPathInDebuggerDiagnostic extends BaseDiagnostic {
constructor() {
Expand Down
6 changes: 4 additions & 2 deletions src/client/common/application/applicationShell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
const opn = require('opn');

import { injectable } from 'inversify';
import { CancellationToken, Disposable, InputBoxOptions, MessageItem, MessageOptions, OpenDialogOptions, QuickPickItem, QuickPickOptions, SaveDialogOptions, StatusBarAlignment, StatusBarItem, Uri, window, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode';
import { CancellationToken, Disposable, InputBoxOptions, MessageItem, MessageOptions, OpenDialogOptions, Progress, ProgressOptions, QuickPickItem, QuickPickOptions, SaveDialogOptions, StatusBarAlignment, StatusBarItem, Uri, window, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode';
import { IApplicationShell } from './types';

@injectable()
Expand Down Expand Up @@ -67,5 +67,7 @@ export class ApplicationShell implements IApplicationShell {
public showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions): Thenable<WorkspaceFolder | undefined> {
return window.showWorkspaceFolderPick(options);
}

public withProgress<R>(options: ProgressOptions, task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => Thenable<R>): Thenable<R> {
return window.withProgress<R>(options, task);
}
}
27 changes: 24 additions & 3 deletions src/client/common/application/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import {
Breakpoint, BreakpointsChangeEvent, CancellationToken, ConfigurationChangeEvent, DebugConfiguration, DebugConfigurationProvider, DebugConsole, DebugSession, DebugSessionCustomEvent, Disposable,
Event, FileSystemWatcher, GlobPattern, InputBoxOptions, MessageItem,
MessageOptions, OpenDialogOptions, QuickPickItem, QuickPickOptions, SaveDialogOptions, StatusBarAlignment, StatusBarItem,
Terminal, TerminalOptions, TextDocument, TextDocumentShowOptions, TextEditor, TextEditorEdit, TextEditorOptionsChangeEvent, TextEditorSelectionChangeEvent,
TextEditorViewColumnChangeEvent, Uri, ViewColumn, WorkspaceConfiguration, WorkspaceEdit, WorkspaceFolder, WorkspaceFolderPickOptions, WorkspaceFoldersChangeEvent
MessageOptions, OpenDialogOptions, Progress, ProgressOptions, QuickPickItem, QuickPickOptions, SaveDialogOptions,
StatusBarAlignment, StatusBarItem, Terminal, TerminalOptions, TextDocument, TextDocumentShowOptions, TextEditor, TextEditorEdit,
TextEditorOptionsChangeEvent, TextEditorSelectionChangeEvent, TextEditorViewColumnChangeEvent, Uri, ViewColumn, WorkspaceConfiguration, WorkspaceEdit, WorkspaceFolder, WorkspaceFolderPickOptions, WorkspaceFoldersChangeEvent
} from 'vscode';

export const IApplicationShell = Symbol('IApplicationShell');
Expand Down Expand Up @@ -248,6 +248,27 @@ export interface IApplicationShell {
* @return A promise that resolves to the workspace folder or `undefined`.
*/
showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions): Thenable<WorkspaceFolder | undefined>;

/**
* Show progress in the editor. Progress is shown while running the given callback
* and while the promise it returned isn't resolved nor rejected. The location at which
* progress should show (and other details) is defined via the passed [`ProgressOptions`](#ProgressOptions).
*
* @param task A callback returning a promise. Progress state can be reported with
* the provided [progress](#Progress)-object.
*
* To report discrete progress, use `increment` to indicate how much work has been completed. Each call with
* a `increment` value will be summed up and reflected as overall progress until 100% is reached (a value of
* e.g. `10` accounts for `10%` of work done).
* Note that currently only `ProgressLocation.Notification` is capable of showing discrete progress.
*
* To monitor if the operation has been cancelled by the user, use the provided [`CancellationToken`](#CancellationToken).
* Note that currently only `ProgressLocation.Notification` is supporting to show a cancel button to cancel the
* long running operation.
*
* @return The thenable the task-callback returned.
*/
withProgress<R>(options: ProgressOptions, task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => Thenable<R>): Thenable<R>;
}

export const ICommandManager = Symbol('ICommandManager');
Expand Down
2 changes: 1 addition & 1 deletion src/client/common/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ function getTextEditsInternal(before: string, diffs: [number, string][], startLi
let line = startLine;
let character = 0;
if (line > 0) {
const beforeLines = <string[]>before.split(/\r?\n/g);
const beforeLines = before.split(/\r?\n/g);
beforeLines.filter((l, i) => i < line).forEach(l => character += l.length + NEW_LINE_LENGTH);
}
const edits: Edit[] = [];
Expand Down
8 changes: 5 additions & 3 deletions src/client/common/envFileParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function parseEnvironmentVariables(contents: string): EnvironmentVariables | und
return undefined;
}

const env = {} as EnvironmentVariables;
const env: EnvironmentVariables = {};
contents.split('\n').forEach(line => {
const match = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/);
if (match !== null) {
Expand Down Expand Up @@ -40,7 +40,9 @@ export function parseEnvFile(envFile: string, mergeWithProcessEnvVars: boolean =
export function mergeEnvVariables(targetEnvVars: EnvironmentVariables, sourceEnvVars: EnvironmentVariables = process.env): EnvironmentVariables {
const service = new EnvironmentVariablesService(new PathUtils(IS_WINDOWS));
service.mergeVariables(sourceEnvVars, targetEnvVars);
service.appendPythonPath(targetEnvVars, sourceEnvVars.PYTHONPATH);
if (sourceEnvVars.PYTHONPATH) {
service.appendPythonPath(targetEnvVars, sourceEnvVars.PYTHONPATH);
}
return targetEnvVars;
}

Expand All @@ -57,6 +59,6 @@ export function mergePythonPath(env: EnvironmentVariables, currentPythonPath: st
return env;
}
const service = new EnvironmentVariablesService(new PathUtils(IS_WINDOWS));
service.appendPythonPath(env, currentPythonPath!);
service.appendPythonPath(env, currentPythonPath);
return env;
}
4 changes: 2 additions & 2 deletions src/client/common/installer/channelManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ export class InstallationChannelManager implements IInstallationChannelManager {
}

public async getInstallationChannels(resource?: Uri): Promise<IModuleInstaller[]> {
let installers = this.serviceContainer.getAll<IModuleInstaller>(IModuleInstaller);
const installers = this.serviceContainer.getAll<IModuleInstaller>(IModuleInstaller);
const supportedInstallers: IModuleInstaller[] = [];
if (installers.length === 0) {
return [];
}
// group by priority and pick supported from the highest priority
installers = installers.sort((a, b) => b.priority - a.priority);
installers.sort((a, b) => b.priority - a.priority);
let currentPri = installers[0].priority;
for (const mi of installers) {
if (mi.priority !== currentPri) {
Expand Down
9 changes: 9 additions & 0 deletions src/client/common/utils/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,12 @@ class DeferredImpl<T> implements Deferred<T> {
export function createDeferred<T>(scope: any = null): Deferred<T> {
return new DeferredImpl<T>(scope);
}

export function createDeferredFrom<T>(...promises: Promise<T>[]): Deferred<T> {
const deferred = createDeferred<T>();
Promise.all(promises)
.then(deferred.resolve.bind(deferred))
.catch(deferred.reject.bind(deferred));

return deferred;
}
5 changes: 5 additions & 0 deletions src/client/common/utils/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export namespace LanguageServiceSurveyBanner {
export const bannerLabelNo = localize('LanguageServiceSurveyBanner.bannerLabelNo', 'No, thanks');
}

export namespace Interpreters {
export const loading = localize('Interpreters.LoadingInterpreters', 'Loading Python Interpreters');
export const refreshing = localize('Interpreters.RefreshingInterpreters', 'Refreshing Python Interpreters');
}

export namespace DataScience {
export const historyTitle = localize('DataScience.historyTitle', 'Python Interactive');
export const badWebPanelFormatString = localize('DataScience.badWebPanelFormatString', '<html><body><h1>{0} is not a valid file name</h1></body></html>');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export abstract class BaseConfigurationProvider implements DebugConfigurationPro
debugConfiguration.cwd = workspaceFolder.fsPath;
}
if (typeof debugConfiguration.envFile !== 'string' && workspaceFolder) {
const envFile = workspaceFolder ? path.join(workspaceFolder.fsPath, '.env') : '';
const envFile = path.join(workspaceFolder.fsPath, '.env');
debugConfiguration.envFile = envFile;
}
if (typeof debugConfiguration.stopOnEntry !== 'boolean') {
Expand Down
21 changes: 16 additions & 5 deletions src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { registerTypes as debugConfigurationRegisterTypes } from './debugger/ext
import { IDebugConfigurationProvider, IDebuggerBanner } from './debugger/extension/types';
import { registerTypes as formattersRegisterTypes } from './formatters/serviceRegistry';
import { IInterpreterSelector } from './interpreter/configuration/types';
import { ICondaService, IInterpreterService, PythonInterpreter } from './interpreter/contracts';
import { ICondaService, IInterpreterLocatorProgressService, IInterpreterService, InterpreterLocatorProgressHandler, PythonInterpreter } from './interpreter/contracts';
import { registerTypes as interpretersRegisterTypes } from './interpreter/serviceRegistry';
import { ServiceContainer } from './ioc/container';
import { ServiceManager } from './ioc/serviceManager';
Expand Down Expand Up @@ -102,7 +102,8 @@ export async function activate(context: ExtensionContext): Promise<IExtensionApi
serviceManager.get<ICodeExecutionManager>(ICodeExecutionManager).registerCommands();
sendStartupTelemetry(activationDeferred.promise, serviceContainer).ignoreErrors();

interpreterManager.refresh()
const workspaceService = serviceContainer.get<IWorkspaceService>(IWorkspaceService);
interpreterManager.refresh(workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined)
.catch(ex => console.error('Python Extension: interpreterManager.refresh', ex));

const jupyterExtension = extensions.getExtension('donjayamanne.jupyter');
Expand Down Expand Up @@ -214,8 +215,17 @@ function initializeServices(context: ExtensionContext, serviceManager: ServiceMa
const disposables = serviceManager.get<IDisposableRegistry>(IDisposableRegistry);
const dispatcher = new DebugSessionEventDispatcher(handlers, DebugService.instance, disposables);
dispatcher.registerEventHandlers();
}

// Display progress of interpreter refreshes only after extension has activated.
serviceContainer.get<InterpreterLocatorProgressHandler>(InterpreterLocatorProgressHandler).register();
serviceContainer.get<IInterpreterLocatorProgressService>(IInterpreterLocatorProgressService).register();

// Get latest interpreter list.
const workspaceService = serviceContainer.get<IWorkspaceService>(IWorkspaceService);
const mainWorkspaceUri = workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined;
const interpreterService = serviceContainer.get<IInterpreterService>(IInterpreterService);
interpreterService.getInterpreters(mainWorkspaceUri).ignoreErrors();
}
async function sendStartupTelemetry(activatedPromise: Promise<void>, serviceContainer: IServiceContainer) {
const logger = serviceContainer.get<ILogger>(ILogger);
try {
Expand All @@ -224,12 +234,13 @@ async function sendStartupTelemetry(activatedPromise: Promise<void>, serviceCont
const terminalShellType = terminalHelper.identifyTerminalShell(terminalHelper.getTerminalShellPath());
const condaLocator = serviceContainer.get<ICondaService>(ICondaService);
const interpreterService = serviceContainer.get<IInterpreterService>(IInterpreterService);
const workspaceService = serviceContainer.get<IWorkspaceService>(IWorkspaceService);
const mainWorkspaceUri = workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined;
const [condaVersion, interpreter, interpreters] = await Promise.all([
condaLocator.getCondaVersion().then(ver => ver ? ver.raw : '').catch<string>(() => ''),
interpreterService.getActiveInterpreter().catch<PythonInterpreter | undefined>(() => undefined),
interpreterService.getInterpreters().catch<PythonInterpreter[]>(() => [])
interpreterService.getInterpreters(mainWorkspaceUri).catch<PythonInterpreter[]>(() => [])
]);
const workspaceService = serviceContainer.get<IWorkspaceService>(IWorkspaceService);
const workspaceFolderCount = workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders!.length : 0;
const pythonVersion = interpreter ? interpreter.version_info.join('.') : undefined;
const interpreterType = interpreter ? interpreter.type : undefined;
Expand Down
15 changes: 14 additions & 1 deletion src/client/interpreter/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface IVirtualEnvironmentsSearchPathProvider {
export const IInterpreterLocatorService = Symbol('IInterpreterLocatorService');

export interface IInterpreterLocatorService extends Disposable {
readonly onLocating: Event<Promise<PythonInterpreter[]>>;
getInterpreters(resource?: Uri): Promise<PythonInterpreter[]>;
}

Expand Down Expand Up @@ -83,7 +84,7 @@ export interface IInterpreterService {
autoSetInterpreter(): Promise<void>;
getActiveInterpreter(resource?: Uri): Promise<PythonInterpreter | undefined>;
getInterpreterDetails(pythonPath: string, resoure?: Uri): Promise<undefined | PythonInterpreter>;
refresh(): Promise<void>;
refresh(resource: Uri | undefined): Promise<void>;
initialize(): void;
getDisplayName(interpreter: Partial<PythonInterpreter>): Promise<string>;
shouldAutoSetInterpreter(): Promise<boolean>;
Expand Down Expand Up @@ -126,3 +127,15 @@ export const IInterpreterWatcherBuilder = Symbol('IInterpreterWatcherBuilder');
export interface IInterpreterWatcherBuilder {
getWorkspaceVirtualEnvInterpreterWatcher(resource: Uri | undefined): Promise<IInterpreterWatcher>;
}

export const InterpreterLocatorProgressHandler = Symbol('InterpreterLocatorProgressHandler');
export interface InterpreterLocatorProgressHandler {
register(): void;
}

export const IInterpreterLocatorProgressService = Symbol('IInterpreterLocatorProgressService');
export interface IInterpreterLocatorProgressService {
readonly onRefreshing: Event<void>;
readonly onRefreshed: Event<void>;
register(): void;
}
2 changes: 1 addition & 1 deletion src/client/interpreter/display/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class InterpreterDisplay implements IInterpreterDisplay {
} else {
this.statusBar.tooltip = '';
this.statusBar.color = 'yellow';
this.statusBar.text = '$(alert) Select Python Environment';
this.statusBar.text = '$(alert) Select Python Interpreter';
}
this.statusBar.show();
}
Expand Down
Loading