From b0963b1163475d35d7e49d5e88bfcdc6c996fc72 Mon Sep 17 00:00:00 2001 From: Misode Date: Tue, 19 Nov 2024 03:26:00 +0100 Subject: [PATCH] Implement renaming files --- src/app/components/generator/FileRenaming.tsx | 23 ++++++++++--------- src/app/components/generator/ProjectPanel.tsx | 22 ++++++++++++++++-- src/app/services/Spyglass.ts | 17 +++++++++++++- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/app/components/generator/FileRenaming.tsx b/src/app/components/generator/FileRenaming.tsx index 96a29233..6cf62538 100644 --- a/src/app/components/generator/FileRenaming.tsx +++ b/src/app/components/generator/FileRenaming.tsx @@ -1,4 +1,4 @@ -import { useState } from 'preact/hooks' +import { useCallback, useState } from 'preact/hooks' import { Analytics } from '../../Analytics.js' import { useLocale } from '../../contexts/index.js' import { useModal } from '../../contexts/Modal.jsx' @@ -7,33 +7,34 @@ import { TextInput } from '../forms/index.js' import { Modal } from '../Modal.js' interface Props { - uri: string, + oldId: string, + onRename: (newId: string) => void, } -export function FileRenaming({ uri }: Props) { +export function FileRenaming({ oldId, onRename }: Props) { const { locale } = useLocale() const { hideModal } = useModal() - const [fileId, setFileId] = useState(uri) // TODO: get original file id + const [fileId, setFileId] = useState(oldId) const [error, setError] = useState() - const changeFileId = (str: string) => { + const changeFileId = useCallback((str: string) => { setError(undefined) setFileId(str) - } + }, []) - const doSave = () => { + const doRename = useCallback(() => { if (!fileId.match(/^([a-z0-9_.-]+:)?[a-z0-9/_.-]+$/)) { setError('Invalid resource location') return } Analytics.renameProjectFile('menu') - // TODO: rename file + onRename(fileId) hideModal() - } + }, [fileId, hideModal]) return

{locale('project.rename_file')}

- + {error !== undefined && {error}} - +
} diff --git a/src/app/components/generator/ProjectPanel.tsx b/src/app/components/generator/ProjectPanel.tsx index 1b9b4993..219e1559 100644 --- a/src/app/components/generator/ProjectPanel.tsx +++ b/src/app/components/generator/ProjectPanel.tsx @@ -1,3 +1,4 @@ +import { Identifier } from 'deepslate' import { route } from 'preact-router' import { useCallback, useMemo, useRef } from 'preact/hooks' import config from '../../Config.js' @@ -59,7 +60,24 @@ export function ProjectPanel() { icon: 'pencil', label: locale('project.rename_file'), onAction: (uri: string) => { - showModal(() => ) + const res = service?.dissectUri(uri) + if (res?.ok) { + // This is pretty hacky, improve this in the future when spyglass has a "constructUri" function + const oldSuffix = `/${res.namespace}/${res.path}/${res.identifier}${res.ext}` + if (!uri.endsWith(oldSuffix)) { + console.warn(`Expected ${uri} to end with ${oldSuffix}`) + return + } + const onRename = (newId: string) => { + const prefix = uri.substring(0, uri.length - oldSuffix.length) + const { namespace, path } = Identifier.parse(newId) + const newUri = prefix + `/${namespace}/${res.path}/${path}${res.ext}` + service?.renameFile(uri, newUri).then(() => { + setProjectUri(newUri) + }) + } + showModal(() => ) + } }, }, { @@ -71,7 +89,7 @@ export function ProjectPanel() { }) }, }, - ], [client, projectRoot, showModal]) + ], [client, service, projectRoot, showModal]) const FolderEntry: TreeViewGroupRenderer = useCallback(({ name, open, onClick }) => { return
diff --git a/src/app/services/Spyglass.ts b/src/app/services/Spyglass.ts index f5dab4a3..95940cdd 100644 --- a/src/app/services/Spyglass.ts +++ b/src/app/services/Spyglass.ts @@ -132,7 +132,7 @@ export class SpyglassService { public async writeFile(uri: string, content: string) { const document = this.client.documents.get(uri) - if (document !== undefined) { + if (document) { document.undoStack.push(document.doc.getText()) document.redoStack = [] TextDocument.update(document.doc, [{ text: content }], document.doc.version + 1) @@ -143,6 +143,21 @@ export class SpyglassService { } } + public async renameFile(oldUri: string, newUri: string) { + const content = await this.readFile(oldUri) + if (!content) { + throw new Error(`Cannot rename nonexistent file ${oldUri}`) + } + await this.service.project.externals.fs.writeFile(newUri, content) + await this.service.project.externals.fs.unlink(oldUri) + const d = this.client.documents.get(oldUri) + if (d) { + const doc = TextDocument.create(newUri, d.doc.languageId, d.doc.version, d.doc.getText()) + this.client.documents.set(newUri, { ...d, doc }) + this.client.documents.delete(oldUri) + } + } + public async applyEdit(uri: string, edit: (node: core.FileNode) => void) { const document = this.client.documents.get(uri) if (document !== undefined) {