-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Group interpreters in interpreter quick picker using separators #18171
Changes from all commits
48af4b4
47a4e0e
872715c
bafea21
d849bfb
5f3e210
a0ac6ad
ad49821
f3fe240
cb93744
815ea70
676fbc8
42aba8b
6d9a5cd
732466f
4ea9c3f
1e9078e
af473cd
e58c0f0
0b56fb6
d73bdd0
9d2fb27
aeefac1
38212b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Group interpreters in interpreter quick picker using separators. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,22 +3,22 @@ | |
|
||
import { injectable, inject } from 'inversify'; | ||
import { getArchitectureDisplayName } from '../../common/platform/registry'; | ||
import { Resource } from '../../common/types'; | ||
import { isParentPath } from '../../pythonEnvironments/common/externalDependencies'; | ||
import { EnvironmentType, PythonEnvironment } from '../../pythonEnvironments/info'; | ||
import { EnvironmentType, PythonEnvironment, virtualEnvTypes } from '../../pythonEnvironments/info'; | ||
import { PythonVersion } from '../../pythonEnvironments/info/pythonVersion'; | ||
import { IInterpreterHelper } from '../contracts'; | ||
import { IInterpreterComparer } from './types'; | ||
|
||
/* | ||
* Enum description: | ||
* - Local environments (.venv); | ||
* - Global environments (pipenv, conda); | ||
* - Globally-installed interpreters (/usr/bin/python3, Windows Store). | ||
*/ | ||
export enum EnvTypeHeuristic { | ||
export enum EnvLocationHeuristic { | ||
/** | ||
* Environments inside the workspace. | ||
*/ | ||
Local = 1, | ||
/** | ||
* Environments outside the workspace. | ||
*/ | ||
Global = 2, | ||
GlobalInterpreters = 3, | ||
} | ||
|
||
@injectable() | ||
|
@@ -41,8 +41,14 @@ export class EnvironmentTypeComparer implements IInterpreterComparer { | |
* Always sort with newest version of Python first within each subgroup. | ||
*/ | ||
public compare(a: PythonEnvironment, b: PythonEnvironment): number { | ||
// Check environment location. | ||
const envLocationComparison = compareEnvironmentLocation(a, b, this.workspaceFolderPath); | ||
if (envLocationComparison !== 0) { | ||
return envLocationComparison; | ||
} | ||
|
||
// Check environment type. | ||
const envTypeComparison = compareEnvironmentType(a, b, this.workspaceFolderPath); | ||
const envTypeComparison = compareEnvironmentType(a, b); | ||
if (envTypeComparison !== 0) { | ||
return envTypeComparison; | ||
} | ||
|
@@ -53,20 +59,15 @@ export class EnvironmentTypeComparer implements IInterpreterComparer { | |
return versionComparison; | ||
} | ||
|
||
// Prioritize non-Conda environments. | ||
if (isCondaEnvironment(a) && !isCondaEnvironment(b)) { | ||
// If we have the "base" Conda env, put it last in its Python version subgroup. | ||
if (isBaseCondaEnvironment(a)) { | ||
return 1; | ||
} | ||
|
||
if (!isCondaEnvironment(a) && isCondaEnvironment(b)) { | ||
if (isBaseCondaEnvironment(b)) { | ||
return -1; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is handled by |
||
|
||
// If we have the "base" Conda env, put it last in its Python version subgroup. | ||
if (isBaseCondaEnvironment(a)) { | ||
return 1; | ||
} | ||
|
||
// Check alphabetical order. | ||
const nameA = getSortName(a, this.interpreterHelper); | ||
const nameB = getSortName(b, this.interpreterHelper); | ||
|
@@ -76,6 +77,25 @@ export class EnvironmentTypeComparer implements IInterpreterComparer { | |
|
||
return nameA > nameB ? 1 : -1; | ||
} | ||
|
||
public getRecommended(interpreters: PythonEnvironment[], resource: Resource): PythonEnvironment | undefined { | ||
// When recommending an intepreter for a workspace, we either want to return a local one | ||
// or fallback on a globally-installed interpreter, and we don't want want to suggest a global environment | ||
// because we would have to add a way to match environments to a workspace. | ||
const workspaceUri = this.interpreterHelper.getActiveWorkspaceUri(resource); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be a good item for debt week, we can remove the dependency on the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you suggesting we directly query Interpreter helper also handles the case when |
||
const filteredInterpreters = interpreters.filter((i) => { | ||
if (getEnvLocationHeuristic(i, workspaceUri?.folderUri.fsPath || '') === EnvLocationHeuristic.Local) { | ||
return true; | ||
} | ||
if (virtualEnvTypes.includes(i.envType)) { | ||
// We're not sure if these envs were created for the workspace, so do not recommend them. | ||
return false; | ||
} | ||
return true; | ||
}); | ||
filteredInterpreters.sort(this.compare.bind(this)); | ||
return filteredInterpreters.length ? filteredInterpreters[0] : undefined; | ||
} | ||
} | ||
|
||
function getSortName(info: PythonEnvironment, interpreterHelper: IInterpreterHelper): string { | ||
|
@@ -113,12 +133,11 @@ function getSortName(info: PythonEnvironment, interpreterHelper: IInterpreterHel | |
return `${sortNameParts.join(' ')} ${envSuffix}`.trim(); | ||
} | ||
|
||
function isCondaEnvironment(environment: PythonEnvironment): boolean { | ||
return environment.envType === EnvironmentType.Conda; | ||
} | ||
|
||
function isBaseCondaEnvironment(environment: PythonEnvironment): boolean { | ||
return isCondaEnvironment(environment) && (environment.envName === 'base' || environment.envName === 'miniconda'); | ||
return ( | ||
environment.envType === EnvironmentType.Conda && | ||
(environment.envName === 'base' || environment.envName === 'miniconda') | ||
); | ||
} | ||
|
||
/** | ||
|
@@ -151,40 +170,50 @@ function comparePythonVersionDescending(a: PythonVersion | undefined, b: PythonV | |
} | ||
|
||
/** | ||
* Compare 2 environment types: return 0 if they are the same, -1 if a comes before b, 1 otherwise. | ||
* Compare 2 environment locations: return 0 if they are the same, -1 if a comes before b, 1 otherwise. | ||
*/ | ||
function compareEnvironmentType(a: PythonEnvironment, b: PythonEnvironment, workspacePath: string): number { | ||
const aHeuristic = getEnvTypeHeuristic(a, workspacePath); | ||
const bHeuristic = getEnvTypeHeuristic(b, workspacePath); | ||
function compareEnvironmentLocation(a: PythonEnvironment, b: PythonEnvironment, workspacePath: string): number { | ||
const aHeuristic = getEnvLocationHeuristic(a, workspacePath); | ||
const bHeuristic = getEnvLocationHeuristic(b, workspacePath); | ||
|
||
return Math.sign(aHeuristic - bHeuristic); | ||
} | ||
|
||
/** | ||
* Return a heuristic value depending on the environment type. | ||
*/ | ||
export function getEnvTypeHeuristic(environment: PythonEnvironment, workspacePath: string): EnvTypeHeuristic { | ||
const { envType } = environment; | ||
|
||
export function getEnvLocationHeuristic(environment: PythonEnvironment, workspacePath: string): EnvLocationHeuristic { | ||
if ( | ||
workspacePath.length > 0 && | ||
((environment.envPath && isParentPath(environment.envPath, workspacePath)) || | ||
(environment.path && isParentPath(environment.path, workspacePath))) | ||
) { | ||
return EnvTypeHeuristic.Local; | ||
return EnvLocationHeuristic.Local; | ||
} | ||
return EnvLocationHeuristic.Global; | ||
} | ||
|
||
switch (envType) { | ||
case EnvironmentType.Venv: | ||
case EnvironmentType.Conda: | ||
case EnvironmentType.VirtualEnv: | ||
case EnvironmentType.VirtualEnvWrapper: | ||
case EnvironmentType.Pipenv: | ||
case EnvironmentType.Poetry: | ||
return EnvTypeHeuristic.Global; | ||
// The default case covers global environments. | ||
// For now this includes: pyenv, Windows Store, Global, System and Unknown environment types. | ||
default: | ||
return EnvTypeHeuristic.GlobalInterpreters; | ||
} | ||
Comment on lines
-177
to
-189
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We want to make sure environments of the same type appear together, so they can be grouped. But in this all global environment types were assigned the same priority, so order was not guaranteed. |
||
/** | ||
* Compare 2 environment types: return 0 if they are the same, -1 if a comes before b, 1 otherwise. | ||
*/ | ||
function compareEnvironmentType(a: PythonEnvironment, b: PythonEnvironment): number { | ||
const envTypeByPriority = getPrioritizedEnvironmentType(); | ||
return Math.sign(envTypeByPriority.indexOf(a.envType) - envTypeByPriority.indexOf(b.envType)); | ||
} | ||
|
||
function getPrioritizedEnvironmentType(): EnvironmentType[] { | ||
return [ | ||
// Prioritize non-Conda environments. | ||
EnvironmentType.Poetry, | ||
EnvironmentType.Pipenv, | ||
EnvironmentType.VirtualEnvWrapper, | ||
EnvironmentType.Venv, | ||
EnvironmentType.VirtualEnv, | ||
EnvironmentType.Conda, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead we assign each type a unique priority, conda being the last in the priority queue. |
||
EnvironmentType.Pyenv, | ||
EnvironmentType.WindowsStore, | ||
EnvironmentType.Global, | ||
EnvironmentType.System, | ||
EnvironmentType.Unknown, | ||
]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored it out as we also need this info while showing the recommended interpreter in the list.