Skip to content

Commit 9b6bb7e

Browse files
author
Kartik Raj
authored
Added setting to control when interpreter information is displayed in the status bar (#19513)
* Added setting to control when interpreter information is displayed in the status bar * Oops * Team reviews * Add feature * Debounce change notification * Add test
1 parent 7c3498c commit 9b6bb7e

File tree

7 files changed

+121
-20
lines changed

7 files changed

+121
-20
lines changed

package.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,22 @@
587587
"scope": "resource",
588588
"type": "string"
589589
},
590+
"python.interpreter.infoVisibility": {
591+
"default": "onPythonRelated",
592+
"description": "%python.interpreter.infoVisibility.description%",
593+
"enum": [
594+
"never",
595+
"onPythonRelated",
596+
"always"
597+
],
598+
"enumDescriptions": [
599+
"%python.interpreter.infoVisibility.never.description%",
600+
"%python.interpreter.infoVisibility.onPythonRelated.description%",
601+
"%python.interpreter.infoVisibility.always.description%"
602+
],
603+
"scope": "machine",
604+
"type": "string"
605+
},
590606
"python.linting.flake8CategorySeverity.W": {
591607
"default": "Warning",
592608
"description": "%python.linting.flake8CategorySeverity.W.description%",

package.nls.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@
5757
"python.linting.flake8Enabled.description": "Whether to lint Python files using flake8.",
5858
"python.linting.flake8Path.description": "Path to flake8, you can use a custom version of flake8 by modifying this setting to include the full path.",
5959
"python.linting.ignorePatterns.description": "Patterns used to exclude files or folders from being linted.",
60+
"python.interpreter.infoVisibility.description": "Controls when to display information of selected interpreter in the status bar.",
61+
"python.interpreter.infoVisibility.never.description": "Never display information of selected interpreter in the status bar.",
62+
"python.interpreter.infoVisibility.onPythonRelated.description": "Only display information of selected interpreter in the status bar if Python related files are opened.",
63+
"python.interpreter.infoVisibility.always.description": "Always display information of selected interpreter in the status bar.",
6064
"python.linting.lintOnSave.description": "Whether to lint Python files when saved.",
6165
"python.linting.maxNumberOfProblems.description": "Controls the maximum number of problems produced by the server.",
6266
"python.linting.mypyArgs.description": "Arguments passed in. Each argument is a separate item in the array.",

src/client/common/configSettings.ts

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
IExperiments,
3131
IFormattingSettings,
3232
IInterpreterPathService,
33+
IInterpreterSettings,
3334
ILintingSettings,
3435
IPythonSettings,
3536
ISortImportSettings,
@@ -44,10 +45,15 @@ import { getOSType, OSType } from './utils/platform';
4445
const untildify = require('untildify');
4546

4647
export class PythonSettings implements IPythonSettings {
47-
public get onDidChange(): Event<void> {
48+
private get onDidChange(): Event<ConfigurationChangeEvent | undefined> {
4849
return this.changed.event;
4950
}
5051

52+
// eslint-disable-next-line class-methods-use-this
53+
public static onConfigChange(): Event<ConfigurationChangeEvent | undefined> {
54+
return PythonSettings.configChanged.event;
55+
}
56+
5157
public get pythonPath(): string {
5258
return this._pythonPath;
5359
}
@@ -88,6 +94,8 @@ export class PythonSettings implements IPythonSettings {
8894

8995
public venvPath = '';
9096

97+
public interpreter!: IInterpreterSettings;
98+
9199
public venvFolders: string[] = [];
92100

93101
public condaPath = '';
@@ -122,7 +130,9 @@ export class PythonSettings implements IPythonSettings {
122130

123131
public languageServerIsDefault = true;
124132

125-
protected readonly changed = new EventEmitter<void>();
133+
protected readonly changed = new EventEmitter<ConfigurationChangeEvent | undefined>();
134+
135+
private static readonly configChanged = new EventEmitter<ConfigurationChangeEvent | undefined>();
126136

127137
private workspaceRoot: Resource;
128138

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

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

191+
@debounceSync(1)
192+
// eslint-disable-next-line class-methods-use-this
193+
protected static debounceConfigChangeNotification(event?: ConfigurationChangeEvent): void {
194+
PythonSettings.configChanged.fire(event);
195+
}
196+
180197
public static getSettingsUriAndTarget(
181198
resource: Uri | undefined,
182199
workspace?: IWorkspaceService,
@@ -246,6 +263,9 @@ export class PythonSettings implements IPythonSettings {
246263
const poetryPath = systemVariables.resolveAny(pythonSettings.get<string>('poetryPath'))!;
247264
this.poetryPath = poetryPath && poetryPath.length > 0 ? getAbsolutePath(poetryPath, workspaceRoot) : poetryPath;
248265

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

515-
public initialize(): void {
516-
const onDidChange = () => {
517-
const currentConfig = this.workspace.getConfiguration('python', this.workspaceRoot);
518-
this.update(currentConfig);
535+
private onDidChanged(event?: ConfigurationChangeEvent) {
536+
const currentConfig = this.workspace.getConfiguration('python', this.workspaceRoot);
537+
this.update(currentConfig);
519538

520-
// If workspace config changes, then we could have a cascading effect of on change events.
521-
// Let's defer the change notification.
522-
this.debounceChangeNotification();
523-
};
539+
// If workspace config changes, then we could have a cascading effect of on change events.
540+
// Let's defer the change notification.
541+
this.debounceChangeNotification(event);
542+
}
543+
544+
public initialize(): void {
524545
this.disposables.push(this.workspace.onDidChangeWorkspaceFolders(this.onWorkspaceFoldersChanged, this));
525546
this.disposables.push(
526-
this.interpreterAutoSelectionService.onDidChangeAutoSelectedInterpreter(onDidChange.bind(this)),
547+
this.interpreterAutoSelectionService.onDidChangeAutoSelectedInterpreter(() => {
548+
this.onDidChanged();
549+
}),
527550
);
528551
this.disposables.push(
529552
this.workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => {
530553
if (event.affectsConfiguration('python')) {
531-
onDidChange();
554+
this.onDidChanged(event);
532555
}
533556
}),
534557
);
535558
if (this.interpreterPathService) {
536-
this.disposables.push(this.interpreterPathService.onDidChange(onDidChange.bind(this)));
559+
this.disposables.push(
560+
this.interpreterPathService.onDidChange(() => {
561+
this.onDidChanged();
562+
}),
563+
);
537564
}
538565

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

545572
@debounceSync(1)
546-
protected debounceChangeNotification(): void {
547-
this.changed.fire();
573+
protected debounceChangeNotification(event?: ConfigurationChangeEvent): void {
574+
this.changed.fire(event);
548575
}
549576

550577
private getPythonPath(systemVariables: SystemVariables, workspaceRoot: string | undefined) {

src/client/common/configuration/service.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT License.
33

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

21+
// eslint-disable-next-line class-methods-use-this
22+
public get onDidChange(): Event<ConfigurationChangeEvent | undefined> {
23+
return PythonSettings.onConfigChange();
24+
}
25+
2126
public getSettings(resource?: Uri): IPythonSettings {
2227
const InterpreterAutoSelectionService = this.serviceContainer.get<IInterpreterAutoSelectionService>(
2328
IInterpreterAutoSelectionService,

src/client/common/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Socket } from 'net';
77
import { Request as RequestResult } from 'request';
88
import {
99
CancellationToken,
10+
ConfigurationChangeEvent,
1011
ConfigurationTarget,
1112
DiagnosticSeverity,
1213
Disposable,
@@ -179,6 +180,7 @@ export interface ICurrentProcess {
179180
}
180181

181182
export interface IPythonSettings {
183+
readonly interpreter: IInterpreterSettings;
182184
readonly pythonPath: string;
183185
readonly venvPath: string;
184186
readonly venvFolders: string[];
@@ -195,7 +197,6 @@ export interface IPythonSettings {
195197
readonly envFile: string;
196198
readonly globalModuleInstallation: boolean;
197199
readonly pylanceLspNotebooksEnabled: boolean;
198-
readonly onDidChange: Event<void>;
199200
readonly experiments: IExperiments;
200201
readonly languageServer: LanguageServerType;
201202
readonly languageServerIsDefault: boolean;
@@ -233,6 +234,9 @@ export interface IMypyCategorySeverity {
233234
readonly error: DiagnosticSeverity;
234235
readonly note: DiagnosticSeverity;
235236
}
237+
export interface IInterpreterSettings {
238+
infoVisibility: 'never' | 'onPythonRelated' | 'always';
239+
}
236240

237241
export interface ILintingSettings {
238242
readonly enabled: boolean;
@@ -308,6 +312,7 @@ export interface IAutoCompleteSettings {
308312

309313
export const IConfigurationService = Symbol('IConfigurationService');
310314
export interface IConfigurationService {
315+
readonly onDidChange: Event<ConfigurationChangeEvent | undefined>;
311316
getSettings(resource?: Uri): IPythonSettings;
312317
isTestExecution(): boolean;
313318
updateSetting(setting: string, value?: unknown, resource?: Uri, configTarget?: ConfigurationTarget): Promise<void>;

src/client/interpreter/interpreterService.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
// eslint-disable-next-line max-classes-per-file
22
import { inject, injectable } from 'inversify';
33
import * as pathUtils from 'path';
4-
import { Disposable, Event, EventEmitter, ProgressLocation, ProgressOptions, Uri } from 'vscode';
4+
import {
5+
ConfigurationChangeEvent,
6+
Disposable,
7+
Event,
8+
EventEmitter,
9+
ProgressLocation,
10+
ProgressOptions,
11+
Uri,
12+
} from 'vscode';
513
import '../common/extensions';
614
import { IApplicationShell, IDocumentManager } from '../common/application/types';
715
import {
@@ -95,20 +103,39 @@ export class InterpreterService implements Disposable, IInterpreterService {
95103
const documentManager = this.serviceContainer.get<IDocumentManager>(IDocumentManager);
96104
const interpreterDisplay = this.serviceContainer.get<IInterpreterDisplay>(IInterpreterDisplay);
97105
const filter = new (class implements IInterpreterStatusbarVisibilityFilter {
98-
constructor(private readonly docManager: IDocumentManager) {}
106+
constructor(
107+
private readonly docManager: IDocumentManager,
108+
private readonly configService: IConfigurationService,
109+
private readonly disposablesReg: IDisposableRegistry,
110+
) {
111+
this.disposablesReg.push(
112+
this.configService.onDidChange(async (event: ConfigurationChangeEvent | undefined) => {
113+
if (event?.affectsConfiguration('python.interpreter.infoVisibility')) {
114+
this.interpreterVisibilityEmitter.fire();
115+
}
116+
}),
117+
);
118+
}
99119

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

102122
public readonly changed = this.interpreterVisibilityEmitter.event;
103123

104124
get hidden() {
125+
const visibility = this.configService.getSettings().interpreter.infoVisibility;
126+
if (visibility === 'never') {
127+
return true;
128+
}
129+
if (visibility === 'always') {
130+
return false;
131+
}
105132
const document = this.docManager.activeTextEditor?.document;
106133
if (document?.fileName.endsWith('settings.json')) {
107134
return false;
108135
}
109136
return document?.languageId !== PYTHON_LANGUAGE;
110137
}
111-
})(documentManager);
138+
})(documentManager, this.configService, disposables);
112139
interpreterDisplay.registerVisibilityFilter(filter);
113140
disposables.push(
114141
this.onDidChangeInterpreters((e): void => {

src/test/common/configSettings/configSettings.unit.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
IAutoCompleteSettings,
2020
IExperiments,
2121
IFormattingSettings,
22+
IInterpreterSettings,
2223
ILintingSettings,
2324
ISortImportSettings,
2425
ITerminalSettings,
@@ -109,6 +110,7 @@ suite('Python Settings', async () => {
109110
config.setup((c) => c.get<any[]>('devOptions')).returns(() => sourceSettings.devOptions);
110111

111112
// complex settings
113+
config.setup((c) => c.get<IInterpreterSettings>('interpreter')).returns(() => sourceSettings.interpreter);
112114
config.setup((c) => c.get<ILintingSettings>('linting')).returns(() => sourceSettings.linting);
113115
config.setup((c) => c.get<ISortImportSettings>('sortImports')).returns(() => sourceSettings.sortImports);
114116
config.setup((c) => c.get<IFormattingSettings>('formatting')).returns(() => sourceSettings.formatting);
@@ -145,6 +147,21 @@ suite('Python Settings', async () => {
145147
});
146148
});
147149

150+
test('Interpreter settings object', () => {
151+
initializeConfig(expected);
152+
config
153+
.setup((c) => c.get<string>('condaPath'))
154+
.returns(() => expected.condaPath)
155+
.verifiable(TypeMoq.Times.once());
156+
157+
settings.update(config.object);
158+
159+
expect(settings.interpreter).to.deep.equal({
160+
infoVisibility: 'onPythonRelated',
161+
});
162+
config.verifyAll();
163+
});
164+
148165
test('condaPath updated', () => {
149166
expected.pythonPath = 'python3';
150167
expected.condaPath = 'spam';

0 commit comments

Comments
 (0)