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
1 change: 1 addition & 0 deletions news/1 Enhancements/18710.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Move interpreter info status bar item to the right.
10 changes: 2 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -537,10 +537,7 @@
"items": {
"enum": [
"All",
"pythonDeprecatePythonPath",
"pythonSurveyNotification",
"pythonInterpreterInfoPinned",
"pythonInterpreterInfoUnpinned"
"pythonSurveyNotification"
]
},
"scope": "machine",
Expand All @@ -552,10 +549,7 @@
"items": {
"enum": [
"All",
"pythonDeprecatePythonPath",
"pythonSurveyNotification",
"pythonInterpreterInfoPinned",
"pythonInterpreterInfoUnpinned"
"pythonSurveyNotification"
]
},
"scope": "machine",
Expand Down
4 changes: 0 additions & 4 deletions src/client/common/experiments/groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,3 @@
export enum ShowExtensionSurveyPrompt {
experiment = 'pythonSurveyNotification',
}
export enum InterpreterStatusBarPosition {
Pinned = 'pythonInterpreterInfoPinned',
Unpinned = 'pythonInterpreterInfoUnpinned',
}
17 changes: 5 additions & 12 deletions src/client/interpreter/display/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import { Disposable, LanguageStatusItem, LanguageStatusSeverity, StatusBarAlignm
import { IExtensionSingleActivationService } from '../../activation/types';
import { IApplicationShell, IWorkspaceService } from '../../common/application/types';
import { Commands, PYTHON_LANGUAGE } from '../../common/constants';
import { InterpreterStatusBarPosition } from '../../common/experiments/groups';
import '../../common/extensions';
import { IDisposableRegistry, IExperimentService, IPathUtils, Resource } from '../../common/types';
import { IDisposableRegistry, IPathUtils, Resource } from '../../common/types';
import { InterpreterQuickPickList, Interpreters } from '../../common/utils/localize';
import { IServiceContainer } from '../../ioc/types';
import { traceLog } from '../../logging';
Expand All @@ -29,6 +28,7 @@ export class InterpreterDisplay implements IInterpreterDisplay, IExtensionSingle
virtualWorkspace: true,
};
private statusBar: StatusBarItem | undefined;
private useLanguageStatus = false;
private languageStatus: LanguageStatusItem | undefined;
private readonly helper: IInterpreterHelper;
private readonly workspaceService: IWorkspaceService;
Expand All @@ -40,7 +40,6 @@ export class InterpreterDisplay implements IInterpreterDisplay, IExtensionSingle
private statusBarCanBeDisplayed?: boolean;
private visibilityFilters: IInterpreterStatusbarVisibilityFilter[] = [];
private disposableRegistry: Disposable[];
private experiments: IExperimentService;

constructor(@inject(IServiceContainer) private readonly serviceContainer: IServiceContainer) {
this.helper = serviceContainer.get<IInterpreterHelper>(IInterpreterHelper);
Expand All @@ -55,12 +54,11 @@ export class InterpreterDisplay implements IInterpreterDisplay, IExtensionSingle
this,
this.disposableRegistry,
);
this.experiments = this.serviceContainer.get<IExperimentService>(IExperimentService);
}

public async activate(): Promise<void> {
const application = this.serviceContainer.get<IApplicationShell>(IApplicationShell);
if (this.experiments.inExperimentSync(InterpreterStatusBarPosition.Unpinned)) {
if (this.useLanguageStatus) {
this.languageStatus = application.createLanguageStatusItem('python.selectedInterpreter', {
language: PYTHON_LANGUAGE,
});
Expand All @@ -71,10 +69,7 @@ export class InterpreterDisplay implements IInterpreterDisplay, IExtensionSingle
};
this.disposableRegistry.push(this.languageStatus);
} else {
let [alignment, priority] = [StatusBarAlignment.Left, 100];
if (this.experiments.inExperimentSync(InterpreterStatusBarPosition.Pinned)) {
[alignment, priority] = [StatusBarAlignment.Right, STATUS_BAR_ITEM_PRIORITY];
}
const [alignment, priority] = [StatusBarAlignment.Right, STATUS_BAR_ITEM_PRIORITY];
this.statusBar = application.createStatusBarItem(alignment, priority);
this.statusBar.command = Commands.Set_Interpreter;
this.disposableRegistry.push(this.statusBar);
Expand Down Expand Up @@ -120,9 +115,7 @@ export class InterpreterDisplay implements IInterpreterDisplay, IExtensionSingle
this.interpreterPath = interpreter.path;
}
let text = interpreter.detailedDisplayName;
if (this.experiments.inExperimentSync(InterpreterStatusBarPosition.Pinned)) {
text = text?.startsWith('Python') ? text?.substring('Python'.length)?.trim() : text;
}
text = text?.startsWith('Python') ? text?.substring('Python'.length)?.trim() : text;
this.statusBar.text = text!;
this.currentlySelectedInterpreterPath = interpreter.path;
} else {
Expand Down
19 changes: 7 additions & 12 deletions src/client/interpreter/interpreterService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ import * as pathUtils from 'path';
import { Disposable, Event, EventEmitter, Uri } from 'vscode';
import '../common/extensions';
import { IDocumentManager } from '../common/application/types';
import {
IConfigurationService,
IDisposableRegistry,
IExperimentService,
IInterpreterPathService,
} from '../common/types';
import { IConfigurationService, IDisposableRegistry, IInterpreterPathService } from '../common/types';
import { IServiceContainer } from '../ioc/types';
import { PythonEnvironment } from '../pythonEnvironments/info';
import {
Expand All @@ -22,7 +17,6 @@ import {
import { PythonLocatorQuery } from '../pythonEnvironments/base/locator';
import { traceError } from '../logging';
import { PYTHON_LANGUAGE } from '../common/constants';
import { InterpreterStatusBarPosition } from '../common/experiments/groups';
import { reportActiveInterpreterChanged } from '../proposedApi';
import { IPythonExecutionFactory } from '../common/process/types';

Expand Down Expand Up @@ -100,13 +94,14 @@ export class InterpreterService implements Disposable, IInterpreterService {
public readonly changed = this.interpreterVisibilityEmitter.event;

get hidden() {
return this.docManager.activeTextEditor?.document.languageId !== PYTHON_LANGUAGE;
const document = this.docManager.activeTextEditor?.document;
if (document?.fileName.endsWith('settings.json')) {
return false;
}
return document?.languageId !== PYTHON_LANGUAGE;
}
})(documentManager);
const experiments = this.serviceContainer.get<IExperimentService>(IExperimentService);
if (experiments.inExperimentSync(InterpreterStatusBarPosition.Pinned)) {
interpreterDisplay.registerVisibilityFilter(filter);
}
interpreterDisplay.registerVisibilityFilter(filter);
disposables.push(
this.onDidChangeInterpreters((e): void => {
const interpreter = e.old ?? e.new;
Expand Down
43 changes: 14 additions & 29 deletions src/test/interpreters/display.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ import {
import { IExtensionSingleActivationService } from '../../client/activation/types';
import { IApplicationShell, IWorkspaceService } from '../../client/common/application/types';
import { Commands, PYTHON_LANGUAGE } from '../../client/common/constants';
import { InterpreterStatusBarPosition } from '../../client/common/experiments/groups';
import { IFileSystem } from '../../client/common/platform/types';
import { IDisposableRegistry, IExperimentService, IPathUtils, ReadWrite } from '../../client/common/types';
import { IDisposableRegistry, IPathUtils, ReadWrite } from '../../client/common/types';
import { InterpreterQuickPickList, Interpreters } from '../../client/common/utils/localize';
import { Architecture } from '../../client/common/utils/platform';
import {
Expand Down Expand Up @@ -55,7 +54,6 @@ suite('Interpreters Display', () => {
let statusBar: TypeMoq.IMock<StatusBarItem>;
let interpreterDisplay: IInterpreterDisplay & IExtensionSingleActivationService;
let interpreterHelper: TypeMoq.IMock<IInterpreterHelper>;
let experiments: TypeMoq.IMock<IExperimentService>;
let pathUtils: TypeMoq.IMock<IPathUtils>;
let languageStatusItem: TypeMoq.IMock<LanguageStatusItem>;
let traceLogStub: sinon.SinonStub;
Expand All @@ -65,17 +63,11 @@ suite('Interpreters Display', () => {
filters.forEach((f) => interpreterDisplay.registerVisibilityFilter(f));
}

async function setupMocks(inExperiment: InterpreterStatusBarPosition | undefined) {
async function setupMocks(useLanguageStatus: boolean) {
serviceContainer = TypeMoq.Mock.ofType<IServiceContainer>();
workspaceService = TypeMoq.Mock.ofType<IWorkspaceService>();
applicationShell = TypeMoq.Mock.ofType<IApplicationShell>();
interpreterService = TypeMoq.Mock.ofType<IInterpreterService>();
experiments = TypeMoq.Mock.ofType<IExperimentService>();
if (inExperiment) {
experiments.setup((e) => e.inExperimentSync(inExperiment)).returns(() => true);
} else {
experiments.setup((e) => e.inExperimentSync(TypeMoq.It.isAny())).returns(() => false);
}
fileSystem = TypeMoq.Mock.ofType<IFileSystem>();
interpreterHelper = TypeMoq.Mock.ofType<IInterpreterHelper>();
disposableRegistry = [];
Expand All @@ -94,29 +86,22 @@ suite('Interpreters Display', () => {
serviceContainer
.setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService)))
.returns(() => interpreterService.object);
serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IExperimentService))).returns(() => experiments.object);
serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object);
serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => disposableRegistry);
serviceContainer
.setup((c) => c.get(TypeMoq.It.isValue(IInterpreterHelper)))
.returns(() => interpreterHelper.object);
serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IPathUtils))).returns(() => pathUtils.object);
if (inExperiment === InterpreterStatusBarPosition.Pinned) {
if (!useLanguageStatus) {
applicationShell
.setup((a) => a.createStatusBarItem(TypeMoq.It.isValue(StatusBarAlignment.Right), TypeMoq.It.isAny()))
.returns(() => statusBar.object);
} else if (inExperiment === InterpreterStatusBarPosition.Unpinned) {
} else {
applicationShell
.setup((a) =>
a.createLanguageStatusItem(TypeMoq.It.isAny(), TypeMoq.It.isValue({ language: PYTHON_LANGUAGE })),
)
.returns(() => languageStatusItem.object);
} else {
applicationShell
.setup((a) =>
a.createStatusBarItem(TypeMoq.It.isValue(StatusBarAlignment.Left), TypeMoq.It.isValue(100)),
)
.returns(() => statusBar.object);
}
pathUtils.setup((p) => p.getDisplayName(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((p) => p);
await createInterpreterDisplay();
Expand All @@ -133,17 +118,17 @@ suite('Interpreters Display', () => {
workspaceService.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource))).returns(() => undefined);
}
}
[InterpreterStatusBarPosition.Unpinned, InterpreterStatusBarPosition.Pinned, undefined].forEach((inExperiment) => {
suite(`When ${inExperiment ? `in experiment ${inExperiment}` : 'not in experiment'}`, () => {
[false].forEach((useLanguageStatus) => {
suite(`When ${useLanguageStatus ? `using language status` : 'using status bar'}`, () => {
setup(async () => {
setupMocks(inExperiment);
setupMocks(useLanguageStatus);
});

teardown(() => {
sinon.restore();
});
test('Statusbar must be created and have command name initialized', () => {
if (inExperiment === InterpreterStatusBarPosition.Unpinned) {
if (useLanguageStatus) {
languageStatusItem.verify(
(s) => (s.severity = TypeMoq.It.isValue(LanguageStatusSeverity.Information)),
TypeMoq.Times.once(),
Expand Down Expand Up @@ -185,7 +170,7 @@ suite('Interpreters Display', () => {

await interpreterDisplay.refresh(resource);

if (inExperiment === InterpreterStatusBarPosition.Unpinned) {
if (useLanguageStatus) {
languageStatusItem.verify(
(s) => (s.text = TypeMoq.It.isValue(activeInterpreter.detailedDisplayName)!),
TypeMoq.Times.once(),
Expand Down Expand Up @@ -233,7 +218,7 @@ suite('Interpreters Display', () => {
const pythonPath = path.join('user', 'development', 'env', 'bin', 'python');
const workspaceFolder = Uri.file('workspace');
const displayName = 'Python 3.10.1';
const expectedDisplayName = inExperiment ? '3.10.1' : 'Python 3.10.1';
const expectedDisplayName = '3.10.1';

setupWorkspaceFolder(resource, workspaceFolder);
const pythonInterpreter: PythonEnvironment = ({
Expand All @@ -245,7 +230,7 @@ suite('Interpreters Display', () => {
.returns(() => Promise.resolve(pythonInterpreter));

await interpreterDisplay.refresh(resource);
if (inExperiment === InterpreterStatusBarPosition.Unpinned) {
if (useLanguageStatus) {
languageStatusItem.verify(
(s) => (s.detail = TypeMoq.It.isValue(pythonPath)),
TypeMoq.Times.atLeastOnce(),
Expand Down Expand Up @@ -280,7 +265,7 @@ suite('Interpreters Display', () => {

await interpreterDisplay.refresh(resource);

if (inExperiment === InterpreterStatusBarPosition.Unpinned) {
if (useLanguageStatus) {
languageStatusItem.verify(
(s) => (s.text = TypeMoq.It.isValue('$(alert) No Interpreter Selected')),
TypeMoq.Times.once(),
Expand Down Expand Up @@ -320,7 +305,7 @@ suite('Interpreters Display', () => {

interpreterHelper.verifyAll();
interpreterService.verifyAll();
if (inExperiment === InterpreterStatusBarPosition.Unpinned) {
if (useLanguageStatus) {
languageStatusItem.verify(
(s) => (s.text = TypeMoq.It.isValue(activeInterpreter.detailedDisplayName)!),
TypeMoq.Times.once(),
Expand All @@ -340,7 +325,7 @@ suite('Interpreters Display', () => {
suite('Visibility', () => {
const resource = Uri.file('x');
suiteSetup(function () {
if (inExperiment === InterpreterStatusBarPosition.Unpinned) {
if (useLanguageStatus) {
return this.skip();
}
});
Expand Down
2 changes: 0 additions & 2 deletions src/test/interpreters/interpreterService.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import * as TypeMoq from 'typemoq';
import * as sinon from 'sinon';
import { ConfigurationTarget, Disposable, TextDocument, TextEditor, Uri, WorkspaceConfiguration } from 'vscode';
import { IDocumentManager, IWorkspaceService } from '../../client/common/application/types';
import { InterpreterStatusBarPosition } from '../../client/common/experiments/groups';
import { IFileSystem } from '../../client/common/platform/types';
import { IPythonExecutionFactory, IPythonExecutionService } from '../../client/common/process/types';
import {
Expand Down Expand Up @@ -78,7 +77,6 @@ suite('Interpreters service', () => {
pythonExecutionService = TypeMoq.Mock.ofType<IPythonExecutionService>();
configService = TypeMoq.Mock.ofType<IConfigurationService>();
experiments = TypeMoq.Mock.ofType<IExperimentService>();
experiments.setup((e) => e.inExperimentSync(InterpreterStatusBarPosition.Pinned)).returns(() => false);

pythonSettings = TypeMoq.Mock.ofType<IPythonSettings>();
pythonSettings.setup((s) => s.pythonPath).returns(() => PYTHON_PATH);
Expand Down