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

Added setting to control when interpreter information is displayed in the status bar #19513

Merged
merged 6 commits into from
Jul 22, 2022
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
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,22 @@
"scope": "resource",
"type": "string"
},
"python.interpreter.infoVisibility": {
"default": "onPythonRelated",
"description": "%python.interpreter.infoVisibility.description%",
"enum": [
"never",
"onPythonRelated",
"always"
],
"enumDescriptions": [
"%python.interpreter.infoVisibility.never.description%",
"%python.interpreter.infoVisibility.onPythonRelated.description%",
"%python.interpreter.infoVisibility.always.description%"
],
"scope": "machine",
"type": "string"
},
"python.linting.flake8CategorySeverity.W": {
"default": "Warning",
"description": "%python.linting.flake8CategorySeverity.W.description%",
Expand Down
4 changes: 4 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
"python.linting.flake8Enabled.description": "Whether to lint Python files using flake8.",
"python.linting.flake8Path.description": "Path to flake8, you can use a custom version of flake8 by modifying this setting to include the full path.",
"python.linting.ignorePatterns.description": "Patterns used to exclude files or folders from being linted.",
"python.interpreter.infoVisibility.description": "Controls when to display information of selected interpreter in the status bar.",
"python.interpreter.infoVisibility.never.description": "Never display information of selected interpreter in the status bar.",
"python.interpreter.infoVisibility.onPythonRelated.description": "Only display information of selected interpreter in the status bar if Python related files are opened.",
"python.interpreter.infoVisibility.always.description": "Always display information of selected interpreter in the status bar.",
"python.linting.lintOnSave.description": "Whether to lint Python files when saved.",
"python.linting.maxNumberOfProblems.description": "Controls the maximum number of problems produced by the server.",
"python.linting.mypyArgs.description": "Arguments passed in. Each argument is a separate item in the array.",
Expand Down
57 changes: 42 additions & 15 deletions src/client/common/configSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
IExperiments,
IFormattingSettings,
IInterpreterPathService,
IInterpreterSettings,
ILintingSettings,
IPythonSettings,
ISortImportSettings,
Expand All @@ -44,10 +45,15 @@ import { getOSType, OSType } from './utils/platform';
const untildify = require('untildify');

export class PythonSettings implements IPythonSettings {
public get onDidChange(): Event<void> {
private get onDidChange(): Event<ConfigurationChangeEvent | undefined> {
return this.changed.event;
}

// eslint-disable-next-line class-methods-use-this
public static onConfigChange(): Event<ConfigurationChangeEvent | undefined> {
return PythonSettings.configChanged.event;
}

public get pythonPath(): string {
return this._pythonPath;
}
Expand Down Expand Up @@ -88,6 +94,8 @@ export class PythonSettings implements IPythonSettings {

public venvPath = '';

public interpreter!: IInterpreterSettings;

public venvFolders: string[] = [];

public condaPath = '';
Expand Down Expand Up @@ -122,7 +130,9 @@ export class PythonSettings implements IPythonSettings {

public languageServerIsDefault = true;

protected readonly changed = new EventEmitter<void>();
protected readonly changed = new EventEmitter<ConfigurationChangeEvent | undefined>();

private static readonly configChanged = new EventEmitter<ConfigurationChangeEvent | undefined>();

private workspaceRoot: Resource;

Expand Down Expand Up @@ -166,6 +176,7 @@ export class PythonSettings implements IPythonSettings {
defaultLS,
);
PythonSettings.pythonSettings.set(workspaceFolderKey, settings);
settings.onDidChange((event) => PythonSettings.debounceConfigChangeNotification(event));
// Pass null to avoid VSC from complaining about not passing in a value.

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -177,6 +188,12 @@ export class PythonSettings implements IPythonSettings {
return PythonSettings.pythonSettings.get(workspaceFolderKey)!;
}

@debounceSync(1)
// eslint-disable-next-line class-methods-use-this
protected static debounceConfigChangeNotification(event?: ConfigurationChangeEvent): void {
PythonSettings.configChanged.fire(event);
}

public static getSettingsUriAndTarget(
resource: Uri | undefined,
workspace?: IWorkspaceService,
Expand Down Expand Up @@ -246,6 +263,9 @@ export class PythonSettings implements IPythonSettings {
const poetryPath = systemVariables.resolveAny(pythonSettings.get<string>('poetryPath'))!;
this.poetryPath = poetryPath && poetryPath.length > 0 ? getAbsolutePath(poetryPath, workspaceRoot) : poetryPath;

this.interpreter = pythonSettings.get<IInterpreterSettings>('interpreter') ?? {
infoVisibility: 'onPythonRelated',
};
// Get as a string and verify; don't just accept.
let userLS = pythonSettings.get<string>('languageServer');
userLS = systemVariables.resolveAny(userLS);
Expand Down Expand Up @@ -512,28 +532,35 @@ export class PythonSettings implements IPythonSettings {
this.initialize();
}

public initialize(): void {
const onDidChange = () => {
const currentConfig = this.workspace.getConfiguration('python', this.workspaceRoot);
this.update(currentConfig);
private onDidChanged(event?: ConfigurationChangeEvent) {
const currentConfig = this.workspace.getConfiguration('python', this.workspaceRoot);
this.update(currentConfig);

// If workspace config changes, then we could have a cascading effect of on change events.
// Let's defer the change notification.
this.debounceChangeNotification();
};
// If workspace config changes, then we could have a cascading effect of on change events.
// Let's defer the change notification.
this.debounceChangeNotification(event);
}

public initialize(): void {
this.disposables.push(this.workspace.onDidChangeWorkspaceFolders(this.onWorkspaceFoldersChanged, this));
this.disposables.push(
this.interpreterAutoSelectionService.onDidChangeAutoSelectedInterpreter(onDidChange.bind(this)),
this.interpreterAutoSelectionService.onDidChangeAutoSelectedInterpreter(() => {
this.onDidChanged();
}),
);
this.disposables.push(
this.workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => {
if (event.affectsConfiguration('python')) {
onDidChange();
this.onDidChanged(event);
}
}),
);
if (this.interpreterPathService) {
this.disposables.push(this.interpreterPathService.onDidChange(onDidChange.bind(this)));
this.disposables.push(
this.interpreterPathService.onDidChange(() => {
this.onDidChanged();
}),
);
}

const initialConfig = this.workspace.getConfiguration('python', this.workspaceRoot);
Expand All @@ -543,8 +570,8 @@ export class PythonSettings implements IPythonSettings {
}

@debounceSync(1)
protected debounceChangeNotification(): void {
this.changed.fire();
protected debounceChangeNotification(event?: ConfigurationChangeEvent): void {
this.changed.fire(event);
}

private getPythonPath(systemVariables: SystemVariables, workspaceRoot: string | undefined) {
Expand Down
7 changes: 6 additions & 1 deletion src/client/common/configuration/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.

import { inject, injectable } from 'inversify';
import { ConfigurationTarget, Uri, WorkspaceConfiguration } from 'vscode';
import { ConfigurationTarget, Event, Uri, WorkspaceConfiguration, ConfigurationChangeEvent } from 'vscode';
import { IInterpreterAutoSelectionService } from '../../interpreter/autoSelection/types';
import { IServiceContainer } from '../../ioc/types';
import { IWorkspaceService } from '../application/types';
Expand All @@ -18,6 +18,11 @@ export class ConfigurationService implements IConfigurationService {
this.workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
}

// eslint-disable-next-line class-methods-use-this
public get onDidChange(): Event<ConfigurationChangeEvent | undefined> {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Config service also updates itself on workspace config change, but often workspace config change event (obtained via WorkspaceService) fires before config service updates. So add a separate reliable change event for config service.

return PythonSettings.onConfigChange();
}

public getSettings(resource?: Uri): IPythonSettings {
const InterpreterAutoSelectionService = this.serviceContainer.get<IInterpreterAutoSelectionService>(
IInterpreterAutoSelectionService,
Expand Down
7 changes: 6 additions & 1 deletion src/client/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Socket } from 'net';
import { Request as RequestResult } from 'request';
import {
CancellationToken,
ConfigurationChangeEvent,
ConfigurationTarget,
DiagnosticSeverity,
Disposable,
Expand Down Expand Up @@ -179,6 +180,7 @@ export interface ICurrentProcess {
}

export interface IPythonSettings {
readonly interpreter: IInterpreterSettings;
readonly pythonPath: string;
readonly venvPath: string;
readonly venvFolders: string[];
Expand All @@ -195,7 +197,6 @@ export interface IPythonSettings {
readonly envFile: string;
readonly globalModuleInstallation: boolean;
readonly pylanceLspNotebooksEnabled: boolean;
readonly onDidChange: Event<void>;
readonly experiments: IExperiments;
readonly languageServer: LanguageServerType;
readonly languageServerIsDefault: boolean;
Expand Down Expand Up @@ -233,6 +234,9 @@ export interface IMypyCategorySeverity {
readonly error: DiagnosticSeverity;
readonly note: DiagnosticSeverity;
}
export interface IInterpreterSettings {
infoVisibility: 'never' | 'onPythonRelated' | 'always';
}

export interface ILintingSettings {
readonly enabled: boolean;
Expand Down Expand Up @@ -308,6 +312,7 @@ export interface IAutoCompleteSettings {

export const IConfigurationService = Symbol('IConfigurationService');
export interface IConfigurationService {
readonly onDidChange: Event<ConfigurationChangeEvent | undefined>;
getSettings(resource?: Uri): IPythonSettings;
isTestExecution(): boolean;
updateSetting(setting: string, value?: unknown, resource?: Uri, configTarget?: ConfigurationTarget): Promise<void>;
Expand Down
33 changes: 30 additions & 3 deletions src/client/interpreter/interpreterService.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
// eslint-disable-next-line max-classes-per-file
import { inject, injectable } from 'inversify';
import * as pathUtils from 'path';
import { Disposable, Event, EventEmitter, ProgressLocation, ProgressOptions, Uri } from 'vscode';
import {
ConfigurationChangeEvent,
Disposable,
Event,
EventEmitter,
ProgressLocation,
ProgressOptions,
Uri,
} from 'vscode';
import '../common/extensions';
import { IApplicationShell, IDocumentManager } from '../common/application/types';
import {
Expand Down Expand Up @@ -95,20 +103,39 @@ export class InterpreterService implements Disposable, IInterpreterService {
const documentManager = this.serviceContainer.get<IDocumentManager>(IDocumentManager);
const interpreterDisplay = this.serviceContainer.get<IInterpreterDisplay>(IInterpreterDisplay);
const filter = new (class implements IInterpreterStatusbarVisibilityFilter {
constructor(private readonly docManager: IDocumentManager) {}
constructor(
private readonly docManager: IDocumentManager,
private readonly configService: IConfigurationService,
private readonly disposablesReg: IDisposableRegistry,
) {
this.disposablesReg.push(
this.configService.onDidChange(async (event: ConfigurationChangeEvent | undefined) => {
if (event?.affectsConfiguration('python.interpreter.infoVisibility')) {
this.interpreterVisibilityEmitter.fire();
}
}),
);
}

public readonly interpreterVisibilityEmitter = new EventEmitter<void>();

public readonly changed = this.interpreterVisibilityEmitter.event;

get hidden() {
const visibility = this.configService.getSettings().interpreter.infoVisibility;
if (visibility === 'never') {
return true;
}
if (visibility === 'always') {
return false;
}
const document = this.docManager.activeTextEditor?.document;
if (document?.fileName.endsWith('settings.json')) {
return false;
}
return document?.languageId !== PYTHON_LANGUAGE;
}
})(documentManager);
})(documentManager, this.configService, disposables);
interpreterDisplay.registerVisibilityFilter(filter);
disposables.push(
this.onDidChangeInterpreters((e): void => {
Expand Down
17 changes: 17 additions & 0 deletions src/test/common/configSettings/configSettings.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
IAutoCompleteSettings,
IExperiments,
IFormattingSettings,
IInterpreterSettings,
ILintingSettings,
ISortImportSettings,
ITerminalSettings,
Expand Down Expand Up @@ -109,6 +110,7 @@ suite('Python Settings', async () => {
config.setup((c) => c.get<any[]>('devOptions')).returns(() => sourceSettings.devOptions);

// complex settings
config.setup((c) => c.get<IInterpreterSettings>('interpreter')).returns(() => sourceSettings.interpreter);
config.setup((c) => c.get<ILintingSettings>('linting')).returns(() => sourceSettings.linting);
config.setup((c) => c.get<ISortImportSettings>('sortImports')).returns(() => sourceSettings.sortImports);
config.setup((c) => c.get<IFormattingSettings>('formatting')).returns(() => sourceSettings.formatting);
Expand Down Expand Up @@ -145,6 +147,21 @@ suite('Python Settings', async () => {
});
});

test('Interpreter settings object', () => {
initializeConfig(expected);
config
.setup((c) => c.get<string>('condaPath'))
.returns(() => expected.condaPath)
.verifiable(TypeMoq.Times.once());

settings.update(config.object);

expect(settings.interpreter).to.deep.equal({
infoVisibility: 'onPythonRelated',
});
config.verifyAll();
});

test('condaPath updated', () => {
expected.pythonPath = 'python3';
expected.condaPath = 'spam';
Expand Down