mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-23 07:10:41 +00:00
Keep track of opened documents and prepare for undo/redo
This commit is contained in:
3
package-lock.json
generated
3
package-lock.json
generated
@@ -31,7 +31,8 @@
|
||||
"lz-string": "^1.4.4",
|
||||
"marked": "^4.0.10",
|
||||
"rfdc": "^1.3.0",
|
||||
"sourcemapped-stacktrace": "^1.1.11"
|
||||
"sourcemapped-stacktrace": "^1.1.11",
|
||||
"vscode-languageserver-textdocument": "^1.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preact/preset-vite": "^2.4.0",
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
"lz-string": "^1.4.4",
|
||||
"marked": "^4.0.10",
|
||||
"rfdc": "^1.3.0",
|
||||
"sourcemapped-stacktrace": "^1.1.11"
|
||||
"sourcemapped-stacktrace": "^1.1.11",
|
||||
"vscode-languageserver-textdocument": "^1.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preact/preset-vite": "^2.4.0",
|
||||
|
||||
@@ -63,7 +63,6 @@ function Head({ simpleType, optional, node }: HeadProps) {
|
||||
</select>
|
||||
}
|
||||
if (type.kind === 'struct' && optional) {
|
||||
console.log(type, node)
|
||||
if (node && JsonObjectNode.is(node)) {
|
||||
return <button class="node-collapse open tooltipped tip-se" aria-label={locale('remove')}>
|
||||
{Octicon.trashcan}
|
||||
@@ -83,7 +82,7 @@ function Head({ simpleType, optional, node }: HeadProps) {
|
||||
{Octicon.plus_circle}
|
||||
</button>
|
||||
}
|
||||
console.warn('Unhandled head', type)
|
||||
// console.warn('Unhandled head', type)
|
||||
return <></>
|
||||
}
|
||||
|
||||
@@ -108,7 +107,7 @@ function Body({ simpleType, node }: BodyProps) {
|
||||
if (type.kind === 'byte' || type.kind === 'short' || type.kind === 'int' || type.kind === 'boolean') {
|
||||
return <></>
|
||||
}
|
||||
console.warn('Unhandled body', type, node)
|
||||
// console.warn('Unhandled body', type, node)
|
||||
return <></>
|
||||
}
|
||||
|
||||
@@ -124,8 +123,8 @@ function StructBody({ type, node }: StructBodyProps) {
|
||||
const dynamicFields = type.fields.filter(field =>
|
||||
field.key.kind === 'string')
|
||||
if (type.fields.length !== staticFields.length + dynamicFields.length) {
|
||||
console.warn('Missed struct fields', type.fields.filter(field =>
|
||||
!staticFields.includes(field) && !dynamicFields.includes(field)))
|
||||
// console.warn('Missed struct fields', type.fields.filter(field =>
|
||||
// !staticFields.includes(field) && !dynamicFields.includes(field)))
|
||||
}
|
||||
return <>
|
||||
{staticFields.map(field => {
|
||||
|
||||
@@ -82,8 +82,11 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||
ignoreChange.current = true
|
||||
data = file.data
|
||||
}
|
||||
if (data) {
|
||||
await spyglass.writeFile(version, uri, JSON.stringify(data))
|
||||
}
|
||||
// TODO: if data is undefined, set to generator's default
|
||||
const docAndNode = await spyglass.setFileContents(version, uri, data ? JSON.stringify(data) : undefined)
|
||||
const docAndNode = await spyglass.getFile(version, uri, () => '{}')
|
||||
Analytics.setGenerator(gen.id)
|
||||
return docAndNode
|
||||
}, [gen.id, version, sharedSnippetId, currentPreset, project.name, file?.id, spyglass])
|
||||
|
||||
@@ -78,7 +78,7 @@ export function SourcePanel({ spyglass, docAndNode, doCopy, doDownload, doImport
|
||||
if (!spyglass || !docAndNode) return
|
||||
try {
|
||||
const data = await parseSource(value, format)
|
||||
await spyglass.setFileContents(version, docAndNode.doc.uri, JSON.stringify(data))
|
||||
await spyglass.writeFile(version, docAndNode.doc.uri, JSON.stringify(data))
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
e.message = `Error importing: ${e.message}`
|
||||
|
||||
@@ -9,36 +9,76 @@ import { localize } from '@spyglassmc/locales'
|
||||
import * as mcdoc from '@spyglassmc/mcdoc'
|
||||
import * as nbt from '@spyglassmc/nbt'
|
||||
import * as zip from '@zip.js/zip.js'
|
||||
import type { TextEdit } from 'vscode-languageserver-textdocument'
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument'
|
||||
import type { ConfigGenerator, ConfigVersion } from '../Config.js'
|
||||
import siteConfig from '../Config.js'
|
||||
import { computeIfAbsent, genPath } from '../Utils.js'
|
||||
import { fetchBlockStates, fetchRegistries, fetchVanillaMcdoc, getVersionChecksum } from './DataFetcher.js'
|
||||
import type { VersionId } from './Versions.js'
|
||||
|
||||
interface DocumentData {
|
||||
doc: TextDocument
|
||||
undoStack: { edits: TextEdit[] }[]
|
||||
redoStack: { edits: TextEdit[] }[]
|
||||
}
|
||||
|
||||
export class Spyglass {
|
||||
private static readonly LOGGER: core.Logger = console
|
||||
private static readonly EXTERNALS: core.Externals = {
|
||||
...BrowserExternals,
|
||||
archive: {
|
||||
...BrowserExternals.archive,
|
||||
decompressBall,
|
||||
},
|
||||
}
|
||||
|
||||
private readonly instances = new Map<VersionId, Promise<core.Service>>()
|
||||
private readonly documents = new Map<string, DocumentData>()
|
||||
private readonly watchers = new Map<string, ((docAndNode: core.DocAndNode) => void)[]>()
|
||||
|
||||
public async setFileContents(versionId: VersionId, uri: string, contents?: string) {
|
||||
const service = await this.getService(versionId)
|
||||
if (contents !== undefined) {
|
||||
await service.project.externals.fs.writeFile(uri, contents)
|
||||
} else {
|
||||
public async getFile(version: VersionId, uri: string, emptyContent?: () => string) {
|
||||
const service = await this.getService(version)
|
||||
const document = this.documents.get(uri)
|
||||
let docAndNode: core.DocAndNode | undefined
|
||||
if (document === undefined) {
|
||||
let doc: TextDocument
|
||||
try {
|
||||
const buffer = await service.project.externals.fs.readFile(uri)
|
||||
contents = new TextDecoder().decode(buffer)
|
||||
const buffer = await Spyglass.EXTERNALS.fs.readFile(uri)
|
||||
const content = new TextDecoder().decode(buffer)
|
||||
doc = TextDocument.create(uri, 'json', 1, content)
|
||||
Spyglass.LOGGER.info(`[Spyglass#openFile] Opening file with content from fs: ${uri}`)
|
||||
} catch (e) {
|
||||
contents = '{}'
|
||||
doc = TextDocument.create(uri, 'json', 1, emptyContent ? emptyContent() : '')
|
||||
Spyglass.LOGGER.info(`[Spyglass#openFile] Opening empty file: ${uri}`)
|
||||
}
|
||||
this.documents.set(uri, { doc, undoStack: [], redoStack: [] })
|
||||
await service.project.onDidOpen(doc.uri, doc.languageId, doc.version, doc.getText())
|
||||
docAndNode = await service.project.ensureClientManagedChecked(uri)
|
||||
} else {
|
||||
docAndNode = service.project.getClientManaged(uri)
|
||||
Spyglass.LOGGER.info(`[Spyglass#openFile] Opening already open file: ${uri}`)
|
||||
}
|
||||
await service.project.onDidOpen(uri, 'json', 1, contents)
|
||||
const docAndNode = await service.project.ensureClientManagedChecked(uri)
|
||||
if (!docAndNode) {
|
||||
throw new Error('[Spyglass setFileContents] Cannot get doc and node')
|
||||
throw new Error(`[Spyglass#openFile] Cannot get doc and node: ${uri}`)
|
||||
}
|
||||
return docAndNode
|
||||
}
|
||||
|
||||
public async writeFile(versionId: VersionId, uri: string, content: string) {
|
||||
await Spyglass.EXTERNALS.fs.writeFile(uri, content)
|
||||
Spyglass.LOGGER.info(`[Spyglass#writeFile] Writing file: ${uri} ${content.substring(0, 50)}`)
|
||||
const doc = this.documents.get(uri)?.doc
|
||||
if (doc !== undefined) {
|
||||
const service = await this.getService(versionId)
|
||||
await service.project.onDidChange(doc.uri, [{ text: content }], doc.version + 1)
|
||||
const docAndNode = service.project.getClientManaged(doc.uri)
|
||||
if (docAndNode) {
|
||||
service.project.emit('documentUpdated', docAndNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getFileContents(_uri: string): string | undefined {
|
||||
return undefined // TODO
|
||||
}
|
||||
@@ -69,21 +109,15 @@ export class Spyglass {
|
||||
const promise = (async () => {
|
||||
const version = siteConfig.versions.find(v => v.id === versionId)!
|
||||
const service = new core.Service({
|
||||
logger: console,
|
||||
profilers: new core.ProfilerFactory(console, [
|
||||
logger: Spyglass.LOGGER,
|
||||
profilers: new core.ProfilerFactory(Spyglass.LOGGER, [
|
||||
'project#init',
|
||||
'project#ready',
|
||||
]),
|
||||
project: {
|
||||
cacheRoot: 'file:///cache/',
|
||||
projectRoots: ['file:///project/'],
|
||||
externals: {
|
||||
...BrowserExternals,
|
||||
archive: {
|
||||
...BrowserExternals.archive,
|
||||
decompressBall,
|
||||
},
|
||||
},
|
||||
externals: Spyglass.EXTERNALS,
|
||||
defaultConfig: core.ConfigService.merge(core.VanillaConfig, {
|
||||
env: {
|
||||
gameVersion: version.ref ?? version.id,
|
||||
@@ -108,7 +142,7 @@ export class Spyglass {
|
||||
}
|
||||
}
|
||||
|
||||
const decompressBall: core.Externals['archive']['decompressBall'] = async (buffer, options) => {
|
||||
async function decompressBall(buffer: Uint8Array, options?: { stripLevel?: number }): Promise<core.DecompressedFile[]> {
|
||||
const reader = new zip.ZipReader(new zip.BlobReader(new Blob([buffer])))
|
||||
const entries = await reader.getEntries()
|
||||
return await Promise.all(entries.map(async e => {
|
||||
|
||||
Reference in New Issue
Block a user