Skip to content

Commit c91ac60

Browse files
committed
DRAFT: renameProject
1 parent f82dda4 commit c91ac60

File tree

5 files changed

+100
-5
lines changed

5 files changed

+100
-5
lines changed

app/gui/project-manager-shim-middleware/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,28 @@ export class ProjectManagerShimMiddleware {
320320
})
321321
break
322322
}
323+
case 'POST /api/project-service/project/rename': {
324+
interface Body {
325+
readonly projectId: UUID
326+
readonly name: string
327+
readonly projectsDirectory: Path
328+
}
329+
bodyJson<Body>(request)
330+
.then(async (body) => {
331+
const projectService = await this.getProjectService()
332+
return projectService.renameProject(body.projectId, body.name, body.projectsDirectory)
333+
})
334+
.then(() => {
335+
response.writeHead(HTTP_STATUS_OK, COMMON_HEADERS).end(toJSONRPCResult(null))
336+
})
337+
.catch((err) => {
338+
console.error(err)
339+
response
340+
.writeHead(HTTP_STATUS_OK, COMMON_HEADERS)
341+
.end(toJSONRPCError('project/rename failed', err))
342+
})
343+
break
344+
}
323345
default: {
324346
console.error('Unknown middleware request:', requestPath)
325347
break

app/gui/src/dashboard/services/ProjectManager/ProjectManager.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,13 @@ export class ProjectManager {
245245
/** Rename a project. */
246246
async renameProject(params: WithProjectPath<RenameProjectParams>): Promise<void> {
247247
const fullParams: RenameProjectParams = this.paramsWithPathToWithId(params)
248-
await this.sendRequest('project/rename', fullParams)
248+
// eslint-disable-next-line react-hooks/rules-of-hooks
249+
const enableProjectService = useFeatureFlag('enableProjectService')
250+
if (enableProjectService.value) {
251+
await this.runProjectServiceCommandJson('project/rename', fullParams)
252+
} else {
253+
await this.sendRequest('project/rename', fullParams)
254+
}
249255
const state = this.projects.get(fullParams.projectId)
250256
if (state?.state === backend.ProjectState.opened) {
251257
this.projects.set(fullParams.projectId, {

app/project-manager-shim/src/projectService/ensoRunner.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ import { extract } from 'tar'
99

1010
export interface Runner {
1111
createProject(path: Path, name: string, projectTemplate?: string): Promise<void>
12-
1312
openProject(
1413
projectPath: Path,
1514
projectId: string,
1615
extraEnv?: Array<[string, string]>,
1716
): Promise<LanguageServerSockets>
1817
closeProject(projectId: string): Promise<void>
18+
isProjectRunning(projectId: string): Promise<boolean>
19+
renameProject(
20+
projectId: string,
21+
namespace: string,
22+
oldPackage: string,
23+
newPackage: string,
24+
): Promise<void>
1925
}
2026

2127
export interface LanguageServerSockets {
@@ -257,6 +263,24 @@ export class EnsoRunner implements Runner {
257263
})
258264
}
259265

266+
/** Checks if a project's language server is currently running. */
267+
async isProjectRunning(projectId: string): Promise<boolean> {
268+
return this.runningProcesses.has(projectId)
269+
}
270+
271+
/** Renames a project in the language server. */
272+
async renameProject(
273+
projectId: string,
274+
namespace: string,
275+
oldPackage: string,
276+
newPackage: string,
277+
): Promise<void> {
278+
// TODO: Implement when JSON-RPC client is available
279+
console.warn(
280+
`Project rename refactoring not yet implemented for project ${projectId}: ${namespace}.${oldPackage} -> ${namespace}.${newPackage}`,
281+
)
282+
}
283+
260284
/** Finds an available port starting from the given port number. */
261285
private async findAvailablePort(startPort: number): Promise<number> {
262286
const net = await import('node:net')

app/project-manager-shim/src/projectService/index.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,44 @@ export class ProjectService {
254254
}
255255

256256
/** Renames a project. */
257-
async renameProject(_projectId: UUID, _newName: string, _projectsDirectory: Path): Promise<void> {
258-
// TODO: Implement renameProject
259-
throw new Error('renameProject not implemented yet')
257+
async renameProject(projectId: UUID, newName: string, projectsDirectory: Path): Promise<void> {
258+
this.logger.debug('Renaming project', projectId, 'to', newName)
259+
// Validate the new project name
260+
await this.validateProjectName(newName)
261+
// Get the repository
262+
const repo = this.getProjectRepository(projectsDirectory)
263+
// Check if project exists
264+
const project = await repo.findById(projectId)
265+
if (!project) {
266+
throw new Error(`Project not found: ${projectId}`)
267+
}
268+
// Check if new name already exists
269+
await this.checkIfNameExists(newName, repo)
270+
// Get the old package name (normalized)
271+
const oldNormalizedName = nameValidation.normalizedName(project.name)
272+
// Get the namespace
273+
const namespace = project.namespace
274+
// Create new package name (normalized)
275+
const newNormalizedName = nameValidation.normalizedName(newName)
276+
// Rename in the repository (updates metadata)
277+
await repo.rename(projectId, newName)
278+
// Check if language server is running for this project
279+
const isRunning = await this.runner.isProjectRunning(projectId)
280+
if (isRunning) {
281+
// If server is running, we would need to register a shutdown hook
282+
// to rename the directory after the server stops.
283+
// For now, we'll just log a warning since the runner doesn't expose this functionality
284+
this.logger.warn(
285+
'Project directory rename deferred - language server is running for project',
286+
projectId,
287+
)
288+
// In the Scala version, this would register a shutdown hook
289+
// For now, we can try to send a rename command to the running server
290+
await this.runner.renameProject(projectId, namespace, oldNormalizedName, newNormalizedName)
291+
} else {
292+
// If server is not running, rename the directory immediately
293+
await repo.renameProjectDirectory(project.path, newNormalizedName)
294+
}
260295
}
261296

262297
// ========================

app/project-manager-shim/src/projectService/projectRepository.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface ProjectRepository {
3636
delete(path: Path): Promise<void>
3737
moveToTrash(path: Path): Promise<void>
3838
rename(projectId: UUID, name: string): Promise<void>
39+
renameProjectDirectory(oldPath: Path, newNormalizedName: string): Promise<Path>
3940
findById(projectId: UUID): Promise<Project | null>
4041
find(predicate: (project: Project) => boolean): Promise<readonly Project[]>
4142
getAll(): Promise<readonly Project[]>
@@ -113,6 +114,13 @@ export class ProjectFileRepository implements ProjectRepository {
113114
await this.renamePackage(project.path, name)
114115
}
115116

117+
/** Renames the project directory on disk. */
118+
async renameProjectDirectory(oldPath: Path, newNormalizedName: string): Promise<Path> {
119+
const newPath = await this.findTargetPath(newNormalizedName)
120+
await fs.rename(oldPath, newPath)
121+
return newPath
122+
}
123+
116124
/** Finds a project by ID. */
117125
async findById(projectId: UUID): Promise<Project | null> {
118126
const projects = await this.getAll()

0 commit comments

Comments
 (0)