Repository navigation
fix: hide Python activity bar icon in non-Python workspaces #1663
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
Changes from all commits
88f9c13
96e6814
293f0ec
96e0494
f922e06
d72d5df
df4e496
5a0a982
d1503b4
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,79 @@ | ||
| import { Disposable, Uri } from 'vscode'; | ||
| import { executeCommand } from '../../common/command.api'; | ||
| import { traceError } from '../../common/logging'; | ||
| import { | ||
| createFileSystemWatcher, | ||
| findFiles, | ||
| getOpenTextDocuments, | ||
| onDidChangeWorkspaceFolders, | ||
| onDidOpenTextDocument, | ||
| } from '../../common/workspace.apis'; | ||
|
|
||
| export const PYTHON_WORKSPACE_KEY = 'python-envs.workspaceHasPython'; | ||
|
|
||
| /** | ||
| * Files that identify a workspace as a Python one. | ||
| * | ||
| * `findFiles` only returns files, never directories, so virtual environments are detected through | ||
| * their `pyvenv.cfg` rather than a `.venv`/`.conda` folder. `*.py` already covers `setup.py`, | ||
| * `manage.py`, `app.py`, etc. | ||
| * | ||
| * Keep the `workspaceContains:` entries in `activationEvents` (package.json) in sync with this list, | ||
| * otherwise the extension is not activated and the context key is never set. | ||
| */ | ||
| export const MARKER_GLOB = | ||
| '**/{*.py,*.pyi,*.ipynb,pyproject.toml,requirements.txt,Pipfile,pyvenv.cfg,mspythonconfig.json}'; | ||
| const EXCLUDE = '**/{node_modules,.git,site-packages}/**'; | ||
|
edvilme marked this conversation as resolved.
|
||
| // `createFileSystemWatcher` has no exclude option, so the same folders are filtered in the handlers. | ||
| const EXCLUDE_RE = /[\\/](node_modules|\.git|site-packages)[\\/]/; | ||
|
|
||
| async function hasPythonContent(): Promise<boolean> { | ||
| const markers = await findFiles(MARKER_GLOB, EXCLUDE, 1); | ||
| if (markers.length > 0) { | ||
| return true; | ||
| } | ||
| // `findFiles` only searches workspace folders, so also cover a standalone Python file opened without a folder. | ||
| return getOpenTextDocuments().some((doc) => doc.languageId === 'python'); | ||
| } | ||
|
|
||
| /** | ||
| * Keeps the `python-envs.workspaceHasPython` context key up to date. It controls whether the Python | ||
| * activity bar icon and its views are shown. | ||
| */ | ||
| export function registerWorkspacePythonContext(disposables: Disposable[]): void { | ||
| // Every refresh takes a ticket and only the newest one may write the context key, so a slow older | ||
| // scan can never overwrite the result of a newer one. | ||
| let latestTicket = 0; | ||
|
|
||
| const refresh = async (): Promise<void> => { | ||
| const ticket = ++latestTicket; | ||
| try { | ||
| const hasPython = await hasPythonContent(); | ||
| if (ticket === latestTicket) { | ||
| await executeCommand('setContext', PYTHON_WORKSPACE_KEY, hasPython); | ||
| } | ||
| } catch (ex) { | ||
| traceError('Failed to update the Python workspace context', ex); | ||
| } | ||
| }; | ||
|
|
||
| const onMarkerChanged = (uri: Uri): void => { | ||
| if (!EXCLUDE_RE.test(uri.fsPath)) { | ||
| void refresh(); | ||
| } | ||
| }; | ||
|
|
||
| const watcher = createFileSystemWatcher(MARKER_GLOB, false, true, false); | ||
| disposables.push( | ||
|
Contributor
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. Is
Contributor
Author
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. Correct-- const EXCLUDE_RE = /[\\/](node_modules|\.git|site-packages)[\\/]/;
watcher.onDidCreate((uri) => { if (!EXCLUDE_RE.test(uri.fsPath)) void refresh(); }),
watcher.onDidDelete((uri) => { if (!EXCLUDE_RE.test(uri.fsPath)) void refresh(); }),This avoids unnecessary |
||
| watcher, | ||
| watcher.onDidCreate(onMarkerChanged), | ||
| watcher.onDidDelete(onMarkerChanged), | ||
| onDidChangeWorkspaceFolders(() => void refresh()), | ||
| onDidOpenTextDocument((doc) => { | ||
| if (doc.languageId === 'python') { | ||
| void refresh(); | ||
| } | ||
| }), | ||
| ); | ||
| void refresh(); | ||
|
edvilme marked this conversation as resolved.
edvilme marked this conversation as resolved.
edvilme marked this conversation as resolved.
edvilme marked this conversation as resolved.
|
||
| } | ||
|
edvilme marked this conversation as resolved.
|
||
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.
The PR hides the activity-bar icon unless this context key is true:
python-envs.workspaceHasPythonThat key is not defined initially. It only gets set after the extension’s
activate()function runs:registerWorkspacePythonContext(context.subscriptions);However,
package.jsoncurrently activates the extension only when VS Code opens a Python-language document:"activationEvents": [ "onLanguage:python" ]This creates a circular dependency:
onLanguage:pythonnever activates the extension.Example
A user opens a repository containing:
my-project/
├── pyproject.toml
├── requirements.txt
└── README.md
The repository is clearly a Python project, but the user has not opened a .py file yet.
Expected: The Python activity-bar icon appears because pyproject.toml identifies the workspace as Python.
Actual: The extension does not activate, so it never searches for pyproject.toml . The context key remains unset, and the icon stays hidden.
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.
Good catch. Fix is to add
workspaceContainsactivation events topackage.jsonso the extension activates even before a.pyfile is opened:"activationEvents": [
"onLanguage:python",
"workspaceContains:**/*.py",
"workspaceContains:pyproject.toml",
"workspaceContains:requirements.txt",
"workspaceContains:Pipfile",
"workspaceContains:setup.py",
"workspaceContains:mspythonconfig.json",
"workspaceContains:.venv",
"workspaceContains:.conda"
]
This breaks the circular dependency - extension activates when any marker file is present in the workspace, sets the context key, and the icon appears without needing a .py file open first.