PY-88099 Normalize project names and improve new project wizard UX

- Add PyPackageName.normalizeProjectName() per PEP 503 name normalization spec
- Normalize project names in uv, poetry, and hatch project generators
- Fix hatch createNewProject to use NioFiles.copyRecursively instead of broken EEL move
- Add background progress indicators for module structure creation and SDK setup
- Refresh VFS with markDirtyAndRefresh after module structure creation
- Add TraceContext to project generation and packaging tool window coroutines


(cherry picked from commit b64dfb4ae541e4a220698d804aec3fe95103bdf2)

IJ-MR-194426

GitOrigin-RevId: 0a1212d70d8136e1ba8821b736d09e00d78f310e
This commit is contained in:
Vitaly Legchilkin
2026-03-04 18:44:08 +00:00
committed by intellij-monorepo-bot
parent 1a6543038b
commit 67235b83d2
11 changed files with 71 additions and 24 deletions
@@ -40,8 +40,8 @@ class EnvironmentCreationHatchError(details: @NlsSafe String) : HatchError(
PyHatchBundle.message("python.hatch.error.environment.creation", details)
)
class FileSystemOperationHatchError(eelFsError: EelFsError) : HatchError(
PyHatchBundle.message("python.hatch.error.filesystem.operation", eelFsError)
class FileSystemOperationHatchError(details: @NlsSafe Any) : HatchError(
PyHatchBundle.message("python.hatch.error.filesystem.operation", details)
)
@@ -1,10 +1,9 @@
package com.intellij.python.hatch.service
import com.intellij.openapi.util.io.NioFiles
import com.intellij.platform.eel.fs.EelFileSystemApi
import com.intellij.platform.eel.fs.EelFileSystemApi.ReplaceExistingDuringMove.DO_NOT_REPLACE_DIRECTORIES
import com.intellij.platform.eel.fs.move
import com.intellij.platform.eel.fs.EelFileUtils
import com.intellij.platform.eel.getOr
import com.intellij.platform.eel.provider.asEelPath
import com.intellij.platform.eel.provider.asNioPath
import com.intellij.platform.eel.provider.getEelDescriptor
import com.intellij.platform.eel.provider.toEelApi
@@ -31,6 +30,7 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import java.io.IOException
import java.nio.file.Path
import kotlin.io.path.exists
import kotlin.io.path.isDirectory
@@ -114,14 +114,20 @@ internal class CliBasedHatchService private constructor(
}
hatchRuntime.hatchCli().new(projectName, tempDir.asNioPath()).getOr { return it }
val target = workingDirectoryPath.asEelPath()
eelApi.fs.move(tempDir, target).replaceExisting(DO_NOT_REPLACE_DIRECTORIES).eelIt().getOr { failure ->
return Result.failure(FileSystemOperationHatchError(failure.error))
try {
withContext(Dispatchers.IO) {
val source = tempDir.asNioPath()
NioFiles.copyRecursively(source, workingDirectoryPath)
EelFileUtils.deleteRecursively(source)
}
}
catch (e: IOException) {
return Result.failure(FileSystemOperationHatchError(e.localizedMessage ?: e.toString()))
}
return Result.success(ProjectStructure(
sourceRoot = target.asNioPath().resolve("src").takeIf { it.isDirectory() },
testRoot = target.asNioPath().resolve("tests").takeIf { it.isDirectory() },
sourceRoot = workingDirectoryPath.resolve("src").takeIf { it.isDirectory() },
testRoot = workingDirectoryPath.resolve("tests").takeIf { it.isDirectory() },
))
}