Skip to content

Commit

Permalink
Error diagnostics handling for renaming, formating & running
Browse files Browse the repository at this point in the history
  • Loading branch information
nighca committed Jan 9, 2025
1 parent eeaafc5 commit 7fadd7e
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 57 deletions.
26 changes: 19 additions & 7 deletions spx-gui/src/components/asset/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type { Widget } from '@/models/widget'
import RenameModal from '../common/RenameModal.vue'
import SoundRecorderModal from '../editor/sound/SoundRecorderModal.vue'
import { useEditorCtx } from '../editor/EditorContextProvider.vue'
import { useCodeEditorCtx } from '../editor/code-editor/context'
import { useCodeEditorCtx, useRenameWarning } from '../editor/code-editor/context'
import { getResourceIdentifier } from '../editor/code-editor/common'
import AssetLibraryModal from './library/AssetLibraryModal.vue'
import AssetAddModal from './library/AssetAddModal.vue'
Expand Down Expand Up @@ -199,6 +199,7 @@ export function useRenameSprite() {
const editorCtx = useEditorCtx()
const codeEditorCtx = useCodeEditorCtx()
const invokeRenameModal = useModal(RenameModal)
const getRenameWarning = useRenameWarning()
return async function renameSprite(sprite: Sprite) {
return invokeRenameModal({
target: {
Expand All @@ -213,7 +214,8 @@ export function useRenameSprite() {
sprite.setName(newName)
})
},
inputTip: assetName.spriteNameTip
inputTip: assetName.spriteNameTip,
warning: await getRenameWarning()
}
})
}
Expand All @@ -223,6 +225,7 @@ export function useRenameSound() {
const editorCtx = useEditorCtx()
const codeEditorCtx = useCodeEditorCtx()
const invokeRenameModal = useModal(RenameModal)
const getRenameWarning = useRenameWarning()
return async function renameSound(sound: Sound) {
return invokeRenameModal({
target: {
Expand All @@ -237,7 +240,8 @@ export function useRenameSound() {
sound.setName(newName)
})
},
inputTip: assetName.soundNameTip
inputTip: assetName.soundNameTip,
warning: await getRenameWarning()
}
})
}
Expand All @@ -247,6 +251,7 @@ export function useRenameCostume() {
const editorCtx = useEditorCtx()
const codeEditorCtx = useCodeEditorCtx()
const invokeRenameModal = useModal(RenameModal)
const getRenameWarning = useRenameWarning()
return async function renameCostume(costume: Costume) {
return invokeRenameModal({
target: {
Expand All @@ -261,7 +266,8 @@ export function useRenameCostume() {
costume.setName(newName)
})
},
inputTip: assetName.costumeNameTip
inputTip: assetName.costumeNameTip,
warning: await getRenameWarning()
}
})
}
Expand All @@ -271,6 +277,7 @@ export function useRenameBackdrop() {
const editorCtx = useEditorCtx()
const codeEditorCtx = useCodeEditorCtx()
const invokeRenameModal = useModal(RenameModal)
const getRenameWarning = useRenameWarning()
return async function renameBackdrop(backdrop: Backdrop) {
return invokeRenameModal({
target: {
Expand All @@ -285,7 +292,8 @@ export function useRenameBackdrop() {
backdrop.setName(newName)
})
},
inputTip: assetName.backdropNameTip
inputTip: assetName.backdropNameTip,
warning: await getRenameWarning()
}
})
}
Expand All @@ -295,6 +303,7 @@ export function useRenameAnimation() {
const editorCtx = useEditorCtx()
const codeEditorCtx = useCodeEditorCtx()
const invokeRenameModal = useModal(RenameModal)
const getRenameWarning = useRenameWarning()
return async function renameAnimation(animation: Animation) {
return invokeRenameModal({
target: {
Expand All @@ -309,7 +318,8 @@ export function useRenameAnimation() {
animation.setName(newName)
})
},
inputTip: assetName.animationNameTip
inputTip: assetName.animationNameTip,
warning: await getRenameWarning()
}
})
}
Expand All @@ -319,6 +329,7 @@ export function useRenameWidget() {
const editorCtx = useEditorCtx()
const codeEditorCtx = useCodeEditorCtx()
const invokeRenameModal = useModal(RenameModal)
const getRenameWarning = useRenameWarning()
return async function renameWidget(widget: Widget) {
return invokeRenameModal({
target: {
Expand All @@ -333,7 +344,8 @@ export function useRenameWidget() {
widget.setName(newName)
})
},
inputTip: assetName.widgetNameTip
inputTip: assetName.widgetNameTip,
warning: await getRenameWarning()
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion spx-gui/src/components/asset/library/AssetAddModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const handleSubmit = useMessageHandle(
}),
content: t({
en: `The ${assetTypeName} you uploaded [${form.value.name}] is the same as the existing ${assetTypeName} [${assets[0].displayName}] in the asset library. Are you sure you want to add this ${assetTypeName} to the asset library?`,
zh: `您上传的${assetTypeName}「${form.value.name}」与已存在于素材库中的${assetTypeName}「${assets[0].displayName}」内容相同。是否确认需要将此${assetTypeName}添加到素材库中?`
zh: `你上传的${assetTypeName}「${form.value.name}」与已存在于素材库中的${assetTypeName}「${assets[0].displayName}」内容相同。是否确认需要将此${assetTypeName}添加到素材库中?`
})
})
}
Expand Down
21 changes: 20 additions & 1 deletion spx-gui/src/components/common/RenameModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ export interface IRenameTarget {
applyName(newName: string): Promise<void>
/** Tip for new name input */
inputTip: LocaleMessage
/** Extra warning message */
warning: LocaleMessage | null
}
</script>

<script setup lang="ts">
import { useI18n, type LocaleMessage } from '@/utils/i18n'
import { UIFormModal, UIButton, UITextInput, UIForm, UIFormItem, useForm } from '@/components/ui'
import { UIFormModal, UIButton, UITextInput, UIForm, UIFormItem, useForm, UIIcon } from '@/components/ui'
import { useMessageHandle } from '@/utils/exception'
const props = defineProps<{
Expand Down Expand Up @@ -64,6 +66,10 @@ function validateName(name: string) {
<UITextInput v-model:value="form.value.name" />
<template #tip>{{ $t(target.inputTip) }}</template>
</UIFormItem>
<p v-if="target.warning != null" class="warning">
<UIIcon class="icon" type="warning" />
{{ $t(target.warning) }}
</p>
<footer class="footer">
<UIButton type="boring" @click="emit('cancelled')">
{{ $t({ en: 'Cancel', zh: '取消' }) }}
Expand All @@ -77,6 +83,19 @@ function validateName(name: string) {
</template>

<style lang="scss" scoped>
.warning {
display: flex;
gap: 8px;
margin-top: 12px;
color: var(--ui-color-yellow-500);
align-items: flex-start;
.icon {
flex: 0 0 auto;
height: 22px;
}
}
.footer {
margin-top: 40px;
display: flex;
Expand Down
4 changes: 2 additions & 2 deletions spx-gui/src/components/editor/code-editor/FormatButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ const handleFormat = useMessageHandle(
codeEditorCtx.formatTextDocument(getTextDocumentId(props.codeFilePath))
),
{
en: 'Failed to format',
zh: '格式化失败'
en: 'Failed to format, please check the code',
zh: '格式化失败,请检查代码'
}
)
</script>
30 changes: 20 additions & 10 deletions spx-gui/src/components/editor/code-editor/code-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import {
selection2Range,
toLSPPosition,
fromLSPRange,
fromLSPSeverity,
positionEq,
type ITextDocument,
type Range,
Expand All @@ -55,7 +54,10 @@ import {
type CommandArgs,
getTextDocumentId,
containsPosition,
makeBasicMarkdownString
makeBasicMarkdownString,
type WorkspaceDiagnostics,
type TextDocumentDiagnostics,
fromLSPDiagnostic
} from './common'
import { TextDocument, createTextDocument } from './text-document'
import { type Monaco } from './monaco'
Expand Down Expand Up @@ -123,14 +125,9 @@ class DiagnosticsProvider
if (report.kind !== lsp.DocumentDiagnosticReportKind.Full)
throw new Error(`Report kind ${report.kind} not supported`)
for (const item of report.items) {
const severity = item.severity == null ? null : fromLSPSeverity(item.severity)
if (severity === null) continue
const range = this.adaptDiagnosticRange(fromLSPRange(item.range), ctx.textDocument)
diagnostics.push({
range,
severity,
message: item.message
})
const diagnostic = fromLSPDiagnostic(item)
const range = this.adaptDiagnosticRange(diagnostic.range, ctx.textDocument)
diagnostics.push({ ...diagnostic, range })
}
return diagnostics
}
Expand Down Expand Up @@ -470,6 +467,19 @@ export class CodeEditor extends Disposable {
}
}

async diagnosticWorkspace(): Promise<WorkspaceDiagnostics> {
const diagnosticReport = await this.lspClient.workspaceDiagnostic({ previousResultIds: [] })
const items: TextDocumentDiagnostics[] = []
for (const report of diagnosticReport.items) {
if (report.kind === 'unchanged') continue // For now, we support 'full' reports only
items.push({
textDocument: { uri: report.uri },
diagnostics: report.items.map(fromLSPDiagnostic)
})
}
return { items }
}

/** Update code for renaming */
async rename(id: TextDocumentIdentifier, position: Position, newName: string) {
const edit = await this.lspClient.textDocumentRename({
Expand Down
21 changes: 19 additions & 2 deletions spx-gui/src/components/editor/code-editor/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,15 @@ export type Diagnostic = {
message: string
}

export type TextDocumentDiagnostics = {
textDocument: TextDocumentIdentifier
diagnostics: Diagnostic[]
}

export type WorkspaceDiagnostics = {
items: TextDocumentDiagnostics[]
}

export interface WordAtPosition {
word: string
startColumn: number
Expand Down Expand Up @@ -422,14 +431,22 @@ export function fromLSPRange(range: lsp.Range): Range {
}
}

export function fromLSPSeverity(severity: lsp.DiagnosticSeverity): DiagnosticSeverity | null {
export function fromLSPSeverity(severity: lsp.DiagnosticSeverity | undefined): DiagnosticSeverity {
switch (severity) {
case lsp.DiagnosticSeverity.Error:
return DiagnosticSeverity.Error
case lsp.DiagnosticSeverity.Warning:
return DiagnosticSeverity.Warning
default:
return null
return DiagnosticSeverity.Error
}
}

export function fromLSPDiagnostic(diagnostic: lsp.Diagnostic): Diagnostic {
return {
range: fromLSPRange(diagnostic.range),
severity: fromLSPSeverity(diagnostic.severity),
message: diagnostic.message
}
}

Expand Down
32 changes: 29 additions & 3 deletions spx-gui/src/components/editor/code-editor/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ import { getHighlighter } from '@/utils/spx/highlighter'
import { composeQuery, useQuery, type QueryRet } from '@/utils/query'
import type { Project } from '@/models/project'
import type { Runtime } from '@/models/runtime'
import { type Position, type ResourceIdentifier, type TextDocumentIdentifier } from './common'
import {
DiagnosticSeverity,
type Position,
type ResourceIdentifier,
type TextDocumentIdentifier,
type WorkspaceDiagnostics
} from './common'
import { type ICodeEditorUI } from './ui/code-editor-ui'
import { TextDocument } from './text-document'
import { getMonaco, type monaco, type Monaco } from './monaco'
Expand All @@ -19,9 +25,10 @@ export type CodeEditorCtx = {
getTextDocument: (id: TextDocumentIdentifier) => TextDocument | null
formatTextDocument(id: TextDocumentIdentifier): Promise<void>
formatWorkspace(): Promise<void>
/** Update code for renaming */
diagnosticWorkspace(): Promise<WorkspaceDiagnostics>
/** Update code for (symbol) renaming */
rename(id: TextDocumentIdentifier, position: Position, newName: string): Promise<void>
/** Update code for resource renaming, should be called before model name update */
/** Update code for resource renaming, should be called before model name update. */
renameResource(resource: ResourceIdentifier, newName: string): Promise<void>
}

Expand All @@ -33,6 +40,21 @@ export function useCodeEditorCtx() {
return ctx
}

// There may be errors in the project code when renaming resource/symbol, which may cause some references to be updated incorrectly.
// This function is used to provide warning for errors in the project code when renaming: we notify the user to check the code and update the references manually.
export function useRenameWarning() {
const codeEditorCtx = useCodeEditorCtx()
return async function getRenameWarning() {
const r = await codeEditorCtx.diagnosticWorkspace()
const hasError = r.items.some((i) => i.diagnostics.some((d) => d.severity === DiagnosticSeverity.Error))
if (!hasError) return null
return {
en: 'There are errors in the project code. Some references may not be updated automatically. You can check the code and update them manually after renaming.',
zh: '当前项目代码中存在错误,部分引用可能不会自动更新,你可以在重命名后检查代码并手动修改。'
}
}
}

// copied from https://github.com/goplus/vscode-gop/blob/dc065c1701ec54a719747ff41d2054e9ed200eb8/languages/gop.language-configuration.json
const spxLanguageConfiguration: monaco.languages.LanguageConfiguration = {
comments: {
Expand Down Expand Up @@ -182,6 +204,10 @@ export function useProvideCodeEditorCtx(
if (editorRef.value == null) throw new Error('Code editor not initialized')
return editorRef.value.formatWorkspace()
},
diagnosticWorkspace() {
if (editorRef.value == null) throw new Error('Code editor not initialized')
return editorRef.value.diagnosticWorkspace()
},
rename(id: TextDocumentIdentifier, position: Position, newName: string) {
if (editorRef.value == null) throw new Error('Code editor not initialized')
return editorRef.value.rename(id, position, newName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ export const setXYpos: DefinitionDocumentationItem = {
insertText: 'setXYpos ${1:x}, ${2:y}',
overview: 'setXYpos x, y',
detail: makeBasicMarkdownString({
en: 'Set the sprite\'s position, e.g., `setXYpos 100, 100`',
en: "Set the sprite's position, e.g., `setXYpos 100, 100`",
zh: '设置精灵位置,如:`setXYpos 100, 100`'
})
}
Expand All @@ -631,7 +631,7 @@ export const changeXYpos: DefinitionDocumentationItem = {
insertText: 'changeXYpos ${1:dX}, ${2:dY}',
overview: 'changeXYpos dX, dY',
detail: makeBasicMarkdownString({
en: 'Change the sprite\'s position, e.g., `changeXYpos 10, 20` changing X position by 10 and Y position by 20',
en: "Change the sprite's position, e.g., `changeXYpos 10, 20` changing X position by 10 and Y position by 20",
zh: '改变精灵位置,如:`changeXYpos 10, 10` 使水平位置增加 10,垂直位置增加 10'
})
}
Expand Down Expand Up @@ -661,7 +661,7 @@ export const setXpos: DefinitionDocumentationItem = {
insertText: 'setXpos ${1:x}',
overview: 'setXpos x',
detail: makeBasicMarkdownString({
en: 'Set the sprite\'s X position, e.g., `setXpos 100`',
en: "Set the sprite's X position, e.g., `setXpos 100`",
zh: '设置精灵的水平位置,如:`setXpos 100`'
})
}
Expand All @@ -676,7 +676,7 @@ export const changeXpos: DefinitionDocumentationItem = {
insertText: 'changeXpos ${1:dX}',
overview: 'changeXpos dX',
detail: makeBasicMarkdownString({
en: 'Change the sprite\'s X position, e.g., `changeXpos 10` changing X position by 10',
en: "Change the sprite's X position, e.g., `changeXpos 10` changing X position by 10",
zh: '改变精灵的水平位置,如:`changeXpos 10` 使水平位置增加 10'
})
}
Expand Down Expand Up @@ -706,7 +706,7 @@ export const setYpos: DefinitionDocumentationItem = {
insertText: 'setYpos ${1:y}',
overview: 'setYpos y',
detail: makeBasicMarkdownString({
en: 'Set the sprite\'s Y position, e.g., `setYpos 100`',
en: "Set the sprite's Y position, e.g., `setYpos 100`",
zh: '设置精灵的垂直位置,如:`setYpos 100`'
})
}
Expand All @@ -721,7 +721,7 @@ export const changeYpos: DefinitionDocumentationItem = {
insertText: 'changeYpos ${1:dY}',
overview: 'changeYpos dY',
detail: makeBasicMarkdownString({
en: 'Change the sprite\'s Y position, e.g., `changeYpos 10` changing Y position by 10',
en: "Change the sprite's Y position, e.g., `changeYpos 10` changing Y position by 10",
zh: '改变精灵的垂直位置,如:`changeYpos 10` 使垂直位置增加 10'
})
}
Expand Down
Loading

0 comments on commit 7fadd7e

Please sign in to comment.