Skip to content

Commit d95f454

Browse files
Kartik Rajkarthiknadig
andauthored
Deprecate PythonPath - Part 1 (#11011)
* Modified `Select interpreter` command & add a reset interpreter command (#10373) * Modified `Select interpreter` command to support setting interpreter at workspace level * Added a command to reset value of Python interpreter * Code reviews * Update src/client/interpreter/configuration/interpreterSelector.ts Co-Authored-By: Karthik Nadig <[email protected]> Co-authored-by: Karthik Nadig <[email protected]> * Implemented new interpreter storage supporting multiroot workspaces (#10325) * Added python.defaultInterpreterPath setting * Created persistent storage infrastructure * Added vscode.workspace.workspaceFile API * Implemented inspect interpreter path * Implement config service updater for pythonPath * Correct build errors * Change extension to use new infrastructure * Store service container in a global variable * Update settings in the new way src/test/common * Correct cyclic dependency between IExperimentsManager and IConfigurationService * Detect change in new interpreter storage and act accordingly * Added reset python interpreters command * Cache the auto selected interpreter * Log python interpreter in the output channel * Fix autoselection and speed up the interpreter change process * Reset experiments * Reset python interpreters for workspace * Remove reset interpreter command from this PR * Added news entry * Update getWorkspaceFolderIdentifier * Code reviews * Dispose handler * More code reviews * Resolve merge conflicts * Fix issues * Some more fixes * More fixes * Do not assume ACTIVATED_SERVICE_CONTAINER is defined * Fix running tests * Fix cacheUtils.ts and test/common.ts * Correct cacheResourceSpecificInterpreterData decorator * Added tests for decorator-like implementation in enviroment provider * Code reviews * Fix interpreter service unit tests * Fix interpreter display unit tests * Fix mac interpreter unit tests * Fix interpreter selector unit tests * Fix configSettings pythonPath unit tests * Fix configSettings unit tests * Rebase with master * Fix installer.test.ts tests * Fix moduleInstaller.test.ts tests * Fix pythonPathUpdater.test.ts tests * Fix pythonProc.simple.multiroot.test.ts tests * Fix data science functional tests * Fix smoke tests * Use user friendly path in the output channel * Use symbols directly * Fix bug with configSettings.ts * Fix venv tests * Run all tests with the new interpreter storage" * Restore YAML pipeline * Fix absolute path resolver * Run all tests with old interpreter storage * Reduce run time of venv tests * Run only mac multiroot * Remove experiment from experiments.json * Added tests for Interpreter path service * saa * Ensure we use `CI_PYTHON_PATH` when running tests with the new interpreter storage * Reset global interpreter path as well * Added tests for mac interpreter diagnostic * Added tests for src\client\interpreter\autoSelection\rules\settings.ts * Added tests for src\client\interpreter\configuration\pythonPathUpdaterServiceFactory.ts * Added tests for src\client\interpreter\autoSelection\rules\workspaceEnv.ts * Added tests for src\client\interpreter\interpreterService.ts * Added tests for src\client\common\configuration\service.ts * Added tests for src\client\startupTelemetry.ts * Correct tests * Module installer tests * Code reviews * More code reviews * Fix lint error * Prompt when an "unsafe" workspace python environment is to be autoselected (#10430) * Implement prompt when an unsafe workspace python environment is to be autoselected * Code reviews * Refactor pythonPath stuff into a private method * Use variable instead of hardcoding 'python' string * Added tests for src\client\interpreter\autoSelection\interpreterSecurity\interpreterSecurityCommands.ts * Don't register command in the constructor * Refactored code * Updated tests * Code reviews * Moved bannerLabelYes/No to Common() namespace * Update content for news entry * Corrected activation manager tests * Fix single workspace CI tests * Fix multiroot tests + Code reviews * Rename Interpreter Security clear command * Fix tests * Added intellisense for pythonpath experiment * Fix multiroot tests * Rename `Reset interpreter` command to `Clear workspace interpreter setting * Fix bug with interpreter security storage * Correct prettier errors * Edit news entries appropriately * Git rebase * Code reviews Co-authored-by: Karthik Nadig <[email protected]>
1 parent 77bf701 commit d95f454

File tree

104 files changed

+5385
-1774
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+5385
-1774
lines changed

experiments.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,17 @@
160160
"salt": "EnableIPyWidgets",
161161
"min": 0,
162162
"max": 0
163+
},
164+
{
165+
"name": "DeprecatePythonPath - experiment",
166+
"salt": "DeprecatePythonPath",
167+
"min": 0,
168+
"max": 0
169+
},
170+
{
171+
"name": "DeprecatePythonPath - control",
172+
"salt": "DeprecatePythonPath",
173+
"min": 100,
174+
"max": 100
163175
}
164-
165176
]

news/1 Enhancements/10325.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Use new interpreter storage supporting multiroot workspaces when in Deprecate PythonPath experiment.

news/1 Enhancements/10372.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Modified `Select interpreter` command to support setting interpreter at workspace level.

news/1 Enhancements/10374.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added a command `Clear Workspace Interpreter Setting` to clear value of Python interpreter from workspace settings.

news/1 Enhancements/10879.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Prompt when an "untrusted" workspace Python environment is to be auto selected when in Deprecate PythonPath experiment.

news/1 Enhancements/10912.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added a command `Reset stored info for untrusted Interpreters` to reset "untrusted" interpreters storage when in Deprecate PythonPath experiment.

news/1 Enhancements/11021.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added a user setting `python.defaultInterpreterPath` to set up the default interpreter path when in Deprecate PythonPath experiment.

package.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474
"onCommand:python.switchOffInsidersChannel",
7575
"onCommand:python.switchToDailyChannel",
7676
"onCommand:python.switchToWeeklyChannel",
77+
"onCommand:python.clearWorkspaceInterpreter",
78+
"onCommand:python.resetInterpreterSecurityStorage",
7779
"onCommand:python.datascience.createnewnotebook",
7880
"onCommand:python.datascience.showhistorypane",
7981
"onCommand:python.datascience.importnotebook",
@@ -224,6 +226,16 @@
224226
"title": "%python.command.python.switchToWeeklyChannel.title%",
225227
"category": "Python"
226228
},
229+
{
230+
"command": "python.clearWorkspaceInterpreter",
231+
"title": "%python.command.python.clearWorkspaceInterpreter.title%",
232+
"category": "Python"
233+
},
234+
{
235+
"command": "python.resetInterpreterSecurityStorage",
236+
"title": "%python.command.python.resetInterpreterSecurityStorage.title%",
237+
"category": "Python"
238+
},
227239
{
228240
"command": "python.refactorExtractVariable",
229241
"title": "%python.command.python.refactorExtractVariable.title%",
@@ -753,6 +765,16 @@
753765
"category": "Python",
754766
"when": "config.python.insidersChannel != 'weekly'"
755767
},
768+
{
769+
"command": "python.clearWorkspaceInterpreter",
770+
"title": "%python.command.python.clearWorkspaceInterpreter.title%",
771+
"category": "Python"
772+
},
773+
{
774+
"command": "python.resetInterpreterSecurityStorage",
775+
"title": "%python.command.python.resetInterpreterSecurityStorage.title%",
776+
"category": "Python"
777+
},
756778
{
757779
"command": "python.viewOutput",
758780
"title": "%python.command.python.viewOutput.title%",
@@ -1554,6 +1576,12 @@
15541576
"description": "Enables/disables A/B tests.",
15551577
"scope": "machine"
15561578
},
1579+
"python.defaultInterpreterPath": {
1580+
"type": "string",
1581+
"default": "python",
1582+
"description": "Path to Python, you can use a custom version of Python by modifying this setting to include the full path.",
1583+
"scope": "machine"
1584+
},
15571585
"python.experiments.optInto": {
15581586
"type": "array",
15591587
"default": [],
@@ -1571,6 +1599,7 @@
15711599
"UseTerminalToGetActivatedEnvVars - experiment",
15721600
"CollectLSRequestTiming - experiment",
15731601
"CollectNodeLSRequestTiming - experiment",
1602+
"DeprecatePythonPath - experiment",
15741603
"All"
15751604
]
15761605
},
@@ -1594,6 +1623,7 @@
15941623
"UseTerminalToGetActivatedEnvVars - experiment",
15951624
"CollectLSRequestTiming - experiment",
15961625
"CollectNodeLSRequestTiming - experiment",
1626+
"DeprecatePythonPath - experiment",
15971627
"All"
15981628
]
15991629
},

package.nls.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"python.command.python.switchOffInsidersChannel.title": "Switch to Default Channel",
1111
"python.command.python.switchToDailyChannel.title": "Switch to Insiders Daily Channel",
1212
"python.command.python.switchToWeeklyChannel.title": "Switch to Insiders Weekly Channel",
13+
"python.command.python.clearWorkspaceInterpreter.title": "Clear Workspace Interpreter Setting",
14+
"python.command.python.resetInterpreterSecurityStorage.title": "Reset Stored Info for Untrusted Interpreters",
1315
"python.command.python.refactorExtractVariable.title": "Extract Variable",
1416
"python.command.python.refactorExtractMethod.title": "Extract Method",
1517
"python.command.python.viewOutput.title": "Show Output",
@@ -131,14 +133,20 @@
131133
"DataScience.connectingToJupyter": "Connecting to Jupyter server",
132134
"Experiments.inGroup": "User belongs to experiment group '{0}'",
133135
"Interpreters.RefreshingInterpreters": "Refreshing Python Interpreters",
136+
"Interpreters.entireWorkspace": "Entire workspace",
137+
"Interpreters.pythonInterpreterPath": "Python interpreter path: {0}",
134138
"Interpreters.LoadingInterpreters": "Loading Python Interpreters",
139+
"Interpreters.unsafeInterpreterMessage": "We found a Python environment in this workspace. Do you want to select it to start up the features in the Python extension? Only accept if you trust this environment.",
135140
"Interpreters.condaInheritEnvMessage": "We noticed you're using a conda environment. If you are experiencing issues with this environment in the integrated terminal, we recommend that you let the Python extension change \"terminal.integrated.inheritEnv\" to false in your user settings.",
136141
"Logging.CurrentWorkingDirectory": "cwd:",
142+
"Common.bannerLabelYes": "Yes",
143+
"Common.bannerLabelNo": "No",
137144
"Common.doNotShowAgain": "Do not show again",
138145
"Common.reload": "Reload",
139146
"Common.moreInfo": "More Info",
140147
"Common.and": "and",
141148
"Common.install": "Install",
149+
"Common.learnMore": "Learn more",
142150
"OutputChannelNames.languageServer": "Python Language Server",
143151
"OutputChannelNames.python": "Python",
144152
"OutputChannelNames.pythonTest": "Python Test Log",
@@ -166,8 +174,6 @@
166174
"DataScienceSurveyBanner.bannerLabelYes": "Yes, take survey now",
167175
"DataScienceSurveyBanner.bannerLabelNo": "No, thanks",
168176
"InteractiveShiftEnterBanner.bannerMessage": "Would you like to run code in the 'Python Interactive' window (an IPython console) for 'shift-enter'? Select 'No' to continue to run code in the Python Terminal. This can be changed later in settings.",
169-
"InteractiveShiftEnterBanner.bannerLabelYes": "Yes",
170-
"InteractiveShiftEnterBanner.bannerLabelNo": "No",
171177
"DataScience.restartingKernelStatus": "Restarting IPython Kernel",
172178
"DataScience.executingCode": "Executing Cell",
173179
"DataScience.collapseAll": "Collapse all cell inputs",

src/client/activation/activationManager.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@ import { inject, injectable, multiInject } from 'inversify';
77
import { TextDocument } from 'vscode';
88
import { IApplicationDiagnostics } from '../application/types';
99
import { IActiveResourceService, IDocumentManager, IWorkspaceService } from '../common/application/types';
10-
import { PYTHON_LANGUAGE } from '../common/constants';
10+
import { DEFAULT_INTERPRETER_SETTING, PYTHON_LANGUAGE } from '../common/constants';
11+
import { DeprecatePythonPath } from '../common/experimentGroups';
1112
import { traceDecorators } from '../common/logger';
1213
import { IFileSystem } from '../common/platform/types';
13-
import { IDisposable, Resource } from '../common/types';
14-
import { IInterpreterAutoSelectionService } from '../interpreter/autoSelection/types';
14+
import { IDisposable, IExperimentsManager, IInterpreterPathService, Resource } from '../common/types';
15+
import { createDeferred, Deferred } from '../common/utils/async';
16+
import { IInterpreterAutoSelectionService, IInterpreterSecurityService } from '../interpreter/autoSelection/types';
1517
import { IInterpreterService } from '../interpreter/contracts';
1618
import { sendActivationTelemetry } from '../telemetry/envFileTelemetry';
1719
import { IExtensionActivationManager, IExtensionActivationService, IExtensionSingleActivationService } from './types';
1820

1921
@injectable()
2022
export class ExtensionActivationManager implements IExtensionActivationManager {
2123
public readonly activatedWorkspaces = new Set<string>();
24+
protected readonly isInterpreterSetForWorkspacePromises = new Map<string, Deferred<void>>();
2225
private readonly disposables: IDisposable[] = [];
2326
private docOpenedHandler?: IDisposable;
2427
constructor(
@@ -31,7 +34,10 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
3134
@inject(IApplicationDiagnostics) private readonly appDiagnostics: IApplicationDiagnostics,
3235
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService,
3336
@inject(IFileSystem) private readonly fileSystem: IFileSystem,
34-
@inject(IActiveResourceService) private readonly activeResourceService: IActiveResourceService
37+
@inject(IActiveResourceService) private readonly activeResourceService: IActiveResourceService,
38+
@inject(IExperimentsManager) private readonly experiments: IExperimentsManager,
39+
@inject(IInterpreterPathService) private readonly interpreterPathService: IInterpreterPathService,
40+
@inject(IInterpreterSecurityService) private readonly interpreterSecurityService: IInterpreterSecurityService
3541
) {}
3642

3743
public dispose() {
@@ -66,6 +72,7 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
6672
await sendActivationTelemetry(this.fileSystem, this.workspaceService, resource);
6773

6874
await this.autoSelection.autoSelectInterpreter(resource);
75+
await this.evaluateAutoSelectedInterpreterSafety(resource);
6976
await Promise.all(this.activationServices.map((item) => item.activate(resource)));
7077
await this.appDiagnostics.performPreStartupHealthCheck(resource);
7178
}
@@ -88,8 +95,38 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
8895
const folder = this.workspaceService.getWorkspaceFolder(doc.uri);
8996
this.activateWorkspace(folder ? folder.uri : undefined).ignoreErrors();
9097
}
98+
99+
public async evaluateAutoSelectedInterpreterSafety(resource: Resource) {
100+
if (this.experiments.inExperiment(DeprecatePythonPath.experiment)) {
101+
const workspaceKey = this.getWorkspaceKey(resource);
102+
const interpreterSettingValue = this.interpreterPathService.get(resource);
103+
if (interpreterSettingValue.length === 0 || interpreterSettingValue === DEFAULT_INTERPRETER_SETTING) {
104+
// Setting is not set, extension will use the autoselected value. Make sure it's safe.
105+
const interpreter = this.autoSelection.getAutoSelectedInterpreter(resource);
106+
if (interpreter) {
107+
const isInterpreterSetForWorkspace = createDeferred<void>();
108+
this.isInterpreterSetForWorkspacePromises.set(workspaceKey, isInterpreterSetForWorkspace);
109+
await Promise.race([
110+
isInterpreterSetForWorkspace.promise,
111+
this.interpreterSecurityService.evaluateAndRecordInterpreterSafety(interpreter, resource)
112+
]);
113+
}
114+
} else {
115+
// Resolve any concurrent calls waiting on the promise
116+
if (this.isInterpreterSetForWorkspacePromises.has(workspaceKey)) {
117+
this.isInterpreterSetForWorkspacePromises.get(workspaceKey)!.resolve();
118+
this.isInterpreterSetForWorkspacePromises.delete(workspaceKey);
119+
}
120+
}
121+
}
122+
this.experiments.sendTelemetryIfInExperiment(DeprecatePythonPath.control);
123+
}
124+
91125
protected addHandlers() {
92126
this.disposables.push(this.workspaceService.onDidChangeWorkspaceFolders(this.onWorkspaceFoldersChanged, this));
127+
this.disposables.push(
128+
this.interpreterPathService.onDidChange((i) => this.evaluateAutoSelectedInterpreterSafety(i.uri))
129+
);
93130
}
94131
protected addRemoveDocOpenedHandlers() {
95132
if (this.hasMultipleWorkspaces()) {

src/client/activation/serviceRegistry.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
3-
import { ActiveResourceService } from '../common/application/activeResource';
4-
import { IActiveResourceService } from '../common/application/types';
3+
54
import { registerTypes as registerDotNetTypes } from '../common/dotnet/serviceRegistry';
65
import { INugetRepository } from '../common/nuget/types';
76
import {
@@ -211,6 +210,5 @@ export function registerTypes(serviceManager: IServiceManager, languageServerTyp
211210
IExtensionSingleActivationService,
212211
ExtensionSurveyPrompt
213212
);
214-
serviceManager.addSingleton<IActiveResourceService>(IActiveResourceService, ActiveResourceService);
215213
serviceManager.addSingleton<IExtensionSingleActivationService>(IExtensionSingleActivationService, AATesting);
216214
}

src/client/application/diagnostics/checks/macPythonInterpreter.ts

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,17 @@
66
import { inject, injectable } from 'inversify';
77
import { ConfigurationChangeEvent, DiagnosticSeverity, Uri } from 'vscode';
88
import { IWorkspaceService } from '../../../common/application/types';
9+
import { DeprecatePythonPath } from '../../../common/experimentGroups';
910
import '../../../common/extensions';
1011
import { IPlatformService } from '../../../common/platform/types';
11-
import { IConfigurationService, IDisposableRegistry, Resource } from '../../../common/types';
12+
import {
13+
IConfigurationService,
14+
IDisposableRegistry,
15+
IExperimentsManager,
16+
IInterpreterPathService,
17+
InterpreterConfigurationScope,
18+
Resource
19+
} from '../../../common/types';
1220
import { IInterpreterHelper, IInterpreterService, InterpreterType } from '../../../interpreter/contracts';
1321
import { IServiceContainer } from '../../../ioc/types';
1422
import { BaseDiagnostic, BaseDiagnosticsService } from '../base';
@@ -133,18 +141,37 @@ export class InvalidMacPythonInterpreterService extends BaseDiagnosticsService {
133141
protected addPythonPathChangedHandler() {
134142
const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
135143
const disposables = this.serviceContainer.get<IDisposableRegistry>(IDisposableRegistry);
144+
const interpreterPathService = this.serviceContainer.get<IInterpreterPathService>(IInterpreterPathService);
145+
const experiments = this.serviceContainer.get<IExperimentsManager>(IExperimentsManager);
146+
if (experiments.inExperiment(DeprecatePythonPath.experiment)) {
147+
disposables.push(interpreterPathService.onDidChange((i) => this.onDidChangeConfiguration(undefined, i)));
148+
}
149+
experiments.sendTelemetryIfInExperiment(DeprecatePythonPath.control);
136150
disposables.push(workspaceService.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this)));
137151
}
138-
protected async onDidChangeConfiguration(event: ConfigurationChangeEvent) {
139-
const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
140-
const workspacesUris: (Uri | undefined)[] = workspaceService.hasWorkspaceFolders
141-
? workspaceService.workspaceFolders!.map((workspace) => workspace.uri)
142-
: [undefined];
143-
const workspaceUriIndex = workspacesUris.findIndex((uri) =>
144-
event.affectsConfiguration('python.pythonPath', uri)
145-
);
146-
if (workspaceUriIndex === -1) {
147-
return;
152+
protected async onDidChangeConfiguration(
153+
event?: ConfigurationChangeEvent,
154+
interpreterConfigurationScope?: InterpreterConfigurationScope
155+
) {
156+
let workspaceUri: Resource;
157+
if (event) {
158+
const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
159+
const workspacesUris: (Uri | undefined)[] = workspaceService.hasWorkspaceFolders
160+
? workspaceService.workspaceFolders!.map((workspace) => workspace.uri)
161+
: [undefined];
162+
const workspaceUriIndex = workspacesUris.findIndex((uri) =>
163+
event.affectsConfiguration('python.pythonPath', uri)
164+
);
165+
if (workspaceUriIndex === -1) {
166+
return;
167+
}
168+
workspaceUri = workspacesUris[workspaceUriIndex];
169+
} else if (interpreterConfigurationScope) {
170+
workspaceUri = interpreterConfigurationScope.uri;
171+
} else {
172+
throw new Error(
173+
'One of `interpreterConfigurationScope` or `event` should be defined when calling `onDidChangeConfiguration`.'
174+
);
148175
}
149176
// Lets wait, for more changes, dirty simple throttling.
150177
if (this.timeOut) {
@@ -154,7 +181,7 @@ export class InvalidMacPythonInterpreterService extends BaseDiagnosticsService {
154181
}
155182
this.timeOut = setTimeout(() => {
156183
this.timeOut = undefined;
157-
this.diagnose(workspacesUris[workspaceUriIndex])
184+
this.diagnose(workspaceUri)
158185
.then((diagnostics) => this.handle(diagnostics))
159186
.ignoreErrors();
160187
}, this.changeThrottleTimeout);

src/client/common/application/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export type CommandsWithoutArgs = keyof ICommandNameWithoutArgumentTypeMapping;
2222
interface ICommandNameWithoutArgumentTypeMapping {
2323
[Commands.SwitchToInsidersDaily]: [];
2424
[Commands.SwitchToInsidersWeekly]: [];
25+
[Commands.ClearWorkspaceInterpreter]: [];
26+
[Commands.ResetInterpreterSecurityStorage]: [];
2527
[Commands.SwitchOffInsidersChannel]: [];
2628
[Commands.Set_Interpreter]: [];
2729
[Commands.Set_ShebangInterpreter]: [];

src/client/common/application/types.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,37 @@ export interface IWorkspaceService {
667667
*/
668668
readonly workspaceFolders: readonly WorkspaceFolder[] | undefined;
669669

670+
/**
671+
* The location of the workspace file, for example:
672+
*
673+
* `file:///Users/name/Development/myProject.code-workspace`
674+
*
675+
* or
676+
*
677+
* `untitled:1555503116870`
678+
*
679+
* for a workspace that is untitled and not yet saved.
680+
*
681+
* Depending on the workspace that is opened, the value will be:
682+
* * `undefined` when no workspace or a single folder is opened
683+
* * the path of the workspace file as `Uri` otherwise. if the workspace
684+
* is untitled, the returned URI will use the `untitled:` scheme
685+
*
686+
* The location can e.g. be used with the `vscode.openFolder` command to
687+
* open the workspace again after it has been closed.
688+
*
689+
* **Example:**
690+
* ```typescript
691+
* vscode.commands.executeCommand('vscode.openFolder', uriOfWorkspace);
692+
* ```
693+
*
694+
* **Note:** it is not advised to use `workspace.workspaceFile` to write
695+
* configuration data into the file. You can use `workspace.getConfiguration().update()`
696+
* for that purpose which will work both when a single folder is opened as
697+
* well as an untitled or saved workspace.
698+
*/
699+
readonly workspaceFile: Resource;
700+
670701
/**
671702
* An event that is emitted when a workspace folder is added or removed.
672703
*/

0 commit comments

Comments
 (0)