diff --git a/src/vs/workbench/browser/positronComponents/positronModalDialog/components/fileInputValidators.ts b/src/vs/workbench/browser/positronComponents/positronModalDialog/components/fileInputValidators.ts index 6be6cc69ba5..1b33ec03e32 100644 --- a/src/vs/workbench/browser/positronComponents/positronModalDialog/components/fileInputValidators.ts +++ b/src/vs/workbench/browser/positronComponents/positronModalDialog/components/fileInputValidators.ts @@ -93,6 +93,25 @@ export async function checkIfPathExists(path: string | number, fileService: IFil return undefined; } +/** + * Check if the current URI exists. For use with Positron web. + * + * @see `checkIfPathValid` `useDebouncedValidator` `LabeledTextInput` `LabeledFolderInput` + * @returns Promise with error message if path doesn't exist or undefined if it does. + */ +export async function checkIfURIExists(path: URI, fileService: IFileService): Promise { + try { + const pathExists = await fileService.exists(path); + + if (!pathExists) { + return localize('pathDoesNotExistError', "The path {0} does not exist.", sanitizePathForDisplay(path.path)); + } + } catch (e) { + return localize('errorCheckingIfPathExists', "An error occurred while checking if the path {0} exists.", sanitizePathForDisplay(path.path)); + } + + return undefined; +} /** * Helper function to print paths in a more readable format. diff --git a/src/vs/workbench/browser/positronComponents/positronModalDialog/components/labeledTextInput.tsx b/src/vs/workbench/browser/positronComponents/positronModalDialog/components/labeledTextInput.tsx index 79f61a1c96d..4c32a80b4ee 100644 --- a/src/vs/workbench/browser/positronComponents/positronModalDialog/components/labeledTextInput.tsx +++ b/src/vs/workbench/browser/positronComponents/positronModalDialog/components/labeledTextInput.tsx @@ -29,7 +29,7 @@ export interface LabeledTextInputProps { * Custom error message. Will override the validator error message if present. */ errorMsg?: string; - validator?: ValidatorFn; + validator?: ValidatorFn; onChange: ChangeEventHandler; /** * Maximum allowed number of characters in the input field. diff --git a/src/vs/workbench/browser/positronComponents/positronModalDialog/components/useDebouncedValidator.tsx b/src/vs/workbench/browser/positronComponents/positronModalDialog/components/useDebouncedValidator.tsx index 8e4a5c35a58..1d88fe61711 100644 --- a/src/vs/workbench/browser/positronComponents/positronModalDialog/components/useDebouncedValidator.tsx +++ b/src/vs/workbench/browser/positronComponents/positronModalDialog/components/useDebouncedValidator.tsx @@ -5,13 +5,13 @@ import * as React from 'react'; -export type ValidatorFn = (value: string | number) => (string | undefined) | Promise; +export type ValidatorFn = (value: T) => (string | undefined) | Promise; /** * A hook to debounce the validation of input values. * @param validator The function to validate the input value. Can be synchronous or asynchronous. */ -export function useDebouncedValidator({ validator, value, debounceDelayMs = 100 }: { validator?: ValidatorFn; value: string | number; debounceDelayMs?: number }) { +export function useDebouncedValidator({ validator, value, debounceDelayMs = 100 }: { validator?: ValidatorFn; value: T; debounceDelayMs?: number }) { const [errorMsg, setErrorMsg] = React.useState(undefined); const callbackTimeoutRef = React.useRef(); diff --git a/src/vs/workbench/browser/positronModalDialogs/newFolderModalDialog.tsx b/src/vs/workbench/browser/positronModalDialogs/newFolderModalDialog.tsx index 67f573ba6db..0b4e9cefda2 100644 --- a/src/vs/workbench/browser/positronModalDialogs/newFolderModalDialog.tsx +++ b/src/vs/workbench/browser/positronModalDialogs/newFolderModalDialog.tsx @@ -153,7 +153,7 @@ const NewFolderModalDialog = (props: NewFolderModalDialogProps) => { autoFocus value={result.folder} onChange={e => setResult({ ...result, folder: e.target.value })} - validator={x => checkIfPathValid(x, { parentPath: result.parentFolder })} + validator={(x: string | number) => checkIfPathValid(x, { parentPath: result.parentFolder })} /> localize( diff --git a/src/vs/workbench/browser/positronNewProjectWizard/components/steps/projectNameLocationStep.tsx b/src/vs/workbench/browser/positronNewProjectWizard/components/steps/projectNameLocationStep.tsx index 233b9d4afa9..e1ecab825e9 100644 --- a/src/vs/workbench/browser/positronNewProjectWizard/components/steps/projectNameLocationStep.tsx +++ b/src/vs/workbench/browser/positronNewProjectWizard/components/steps/projectNameLocationStep.tsx @@ -22,7 +22,7 @@ import { WizardFormattedText, WizardFormattedTextType } from 'vs/workbench/brows import { checkProjectName } from 'vs/workbench/browser/positronNewProjectWizard/utilities/projectNameUtils'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { NewProjectType } from 'vs/workbench/services/positronNewProject/common/positronNewProject'; -import { checkIfPathExists, checkIfPathValid } from 'vs/workbench/browser/positronComponents/positronModalDialog/components/fileInputValidators'; +import { checkIfPathValid, checkIfURIExists } from 'vs/workbench/browser/positronComponents/positronModalDialog/components/fileInputValidators'; import { PathDisplay } from 'vs/workbench/browser/positronNewProjectWizard/components/pathDisplay'; import { useDebouncedValidator } from 'vs/workbench/browser/positronComponents/positronModalDialog/components/useDebouncedValidator'; @@ -46,12 +46,12 @@ export const ProjectNameLocationStep = (props: PropsWithChildren checkIfPathValid(x, { parentPath: parentFolder }) + validator: x => checkIfPathValid(x, { parentPath: parentFolder.fsPath }) }); const isInvalidName = nameValidationErrorMsg !== undefined; const parentPathErrorMsg = useDebouncedValidator({ value: parentFolder, - validator: (path: string | number) => checkIfPathExists(path, fileService) + validator: (path: URI) => checkIfURIExists(path, fileService) }); const isInvalidParentPath = parentPathErrorMsg !== undefined; @@ -75,7 +75,7 @@ export const ProjectNameLocationStep = (props: PropsWithChildren { // Show the open dialog. const uri = await fileDialogService.showOpenDialog({ - defaultUri: URI.file(parentFolder), + defaultUri: parentFolder, canSelectFiles: false, canSelectFolders: true, canSelectMany: false, @@ -83,7 +83,7 @@ export const ProjectNameLocationStep = (props: PropsWithChildren { + const onChangeParentFolder = async (folder: URI) => { context.parentFolder = folder; context.projectNameFeedback = await checkProjectName( projectName, @@ -181,7 +181,7 @@ export const ProjectNameLocationStep = (props: PropsWithChildren onChangeProjectName(e.target.value)} type='text' // Don't let the user create a project with a location that is too long. - maxLength={255 - parentFolder.length} + maxLength={255 - parentFolder.fsPath.length} error={ (projectNameFeedback && projectNameFeedback.type === WizardFormattedTextType.Error) || @@ -207,7 +207,7 @@ export const ProjectNameLocationStep = (props: PropsWithChildren @@ -219,11 +219,11 @@ export const ProjectNameLocationStep = (props: PropsWithChildren onChangeParentFolder(e.target.value)} + onChange={(e) => onChangeParentFolder(parentFolder.with({ path: e.target.value }))} /> { // Create the new project folder if it doesn't already exist. - const folder = URI.file((await pathService.path).join(result.parentFolder, result.projectName)); + const folder = result.parentFolder.with({ path: (await pathService.path).join(result.parentFolder.fsPath, result.projectName) }); const existingFolder = await fileService.exists(folder); if (!existingFolder) { await fileService.createFolder(folder); diff --git a/src/vs/workbench/browser/positronNewProjectWizard/newProjectWizardState.ts b/src/vs/workbench/browser/positronNewProjectWizard/newProjectWizardState.ts index eccd73c929f..3cb0fecb2ea 100644 --- a/src/vs/workbench/browser/positronNewProjectWizard/newProjectWizardState.ts +++ b/src/vs/workbench/browser/positronNewProjectWizard/newProjectWizardState.ts @@ -23,6 +23,7 @@ import { WizardFormattedTextItem } from 'vs/workbench/browser/positronNewProject import { LanguageIds, NewProjectType } from 'vs/workbench/services/positronNewProject/common/positronNewProject'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CondaPythonVersionInfo, EMPTY_CONDA_PYTHON_VERSION_INFO } from 'vs/workbench/browser/positronNewProjectWizard/utilities/condaUtils'; +import { URI } from 'vs/base/common/uri'; /** * NewProjectWizardServices interface. @@ -49,7 +50,7 @@ interface NewProjectWizardServices { */ export interface NewProjectWizardStateConfig { readonly services: NewProjectWizardServices; - readonly parentFolder: string; + readonly parentFolder: URI; readonly initialStep: NewProjectWizardStep; readonly steps?: NewProjectWizardStep[]; } @@ -62,7 +63,7 @@ export interface NewProjectWizardState { selectedRuntime: ILanguageRuntimeMetadata | undefined; projectType: NewProjectType | undefined; projectName: string; - parentFolder: string; + parentFolder: URI; initGitRepo: boolean; openInNewWindow: boolean; pythonEnvSetupType: EnvironmentSetupType | undefined; @@ -106,7 +107,7 @@ export class NewProjectWizardStateManager private _projectType: NewProjectType | undefined; private _projectName: string; private _projectNameFeedback: WizardFormattedTextItem | undefined; - private _parentFolder: string; + private _parentFolder: URI; private _initGitRepo: boolean; private _openInNewWindow: boolean; // Python-specific state. @@ -282,7 +283,7 @@ export class NewProjectWizardStateManager * Gets the parent folder. * @returns The parent folder. */ - get parentFolder(): string { + get parentFolder(): URI { return this._parentFolder; } @@ -290,7 +291,7 @@ export class NewProjectWizardStateManager * Sets the parent folder. * @param value The parent folder. */ - set parentFolder(value: string) { + set parentFolder(value: URI) { this._parentFolder = value; this._onUpdateProjectDirectoryEmitter.fire(); } diff --git a/src/vs/workbench/browser/positronNewProjectWizard/utilities/projectNameUtils.ts b/src/vs/workbench/browser/positronNewProjectWizard/utilities/projectNameUtils.ts index 69fc7c664ab..703c6326b21 100644 --- a/src/vs/workbench/browser/positronNewProjectWizard/utilities/projectNameUtils.ts +++ b/src/vs/workbench/browser/positronNewProjectWizard/utilities/projectNameUtils.ts @@ -19,7 +19,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService'; */ export const checkProjectName = async ( projectName: string, - parentFolder: string, + parentFolder: URI, pathService: IPathService, fileService: IFileService ) => { @@ -37,9 +37,7 @@ export const checkProjectName = async ( // TODO: Additional project name validation (i.e. unsupported characters, length, etc.) // The project directory can't already exist. - const folderPath = URI.file( - (await pathService.path).join(parentFolder, projectName) - ); + const folderPath = parentFolder.with({ path: (await pathService.path).join(parentFolder.fsPath, projectName) }); if (await fileService.exists(folderPath)) { return { type: WizardFormattedTextType.Error,