Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions spx-gui/src/components/editor/code-editor/code-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { Runtime } from '@/models/runtime'
import type { Project } from '@/models/project'
import { Copilot } from './copilot'
import { DocumentBase } from './document-base'
import { SpxLSPClient } from './lsp'
import { SpxLSPClient, semanticTokenLegend } from './lsp'
import {
type ICodeEditorUI,
type DiagnosticsContext,
Expand Down Expand Up @@ -55,7 +55,8 @@ import {
type CommandArgs,
getTextDocumentId,
containsPosition,
makeBasicMarkdownString
makeBasicMarkdownString,
fromMonacoUri
} from './common'
import { TextDocument, createTextDocument } from './text-document'
import { type Monaco } from './monaco'
Expand Down Expand Up @@ -542,6 +543,22 @@ export class CodeEditor extends Disposable {

init() {
this.lspClient.init()

this.addDisposable(
this.monaco.languages.registerDocumentSemanticTokensProvider('spx', {
getLegend: () => semanticTokenLegend,
provideDocumentSemanticTokens: async (model) => {
const tokens = await this.lspClient.textDocumentSemanticTokens({
textDocument: fromMonacoUri(model.uri)
})
if (tokens == null) return { data: new Uint32Array(0) }
return { data: new Uint32Array(tokens.data) } // TODO: pass data with array buffer instead of number array
},
releaseDocumentSemanticTokens() {
// do nothing
}
})
)
}

dispose(): void {
Expand Down
9 changes: 9 additions & 0 deletions spx-gui/src/components/editor/code-editor/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Costume } from '@/models/costume'
import { Animation } from '@/models/animation'
import { isWidget } from '@/models/widget'
import { stageCodeFilePaths } from '@/models/stage'
import type { Monaco, monaco } from './monaco'

export type Position = {
line: number
Expand Down Expand Up @@ -562,3 +563,11 @@ export function textDocumentId2CodeFileName(id: TextDocumentIdentifier) {
return { en: spriteName, zh: spriteName }
}
}

export function fromMonacoUri(uri: monaco.Uri): TextDocumentIdentifier {
return { uri: uri.toString() }
}

export function toMonacoUri(id: TextDocumentIdentifier, monaco: Monaco): monaco.Uri {
return monaco.Uri.parse(id.uri)
}
32 changes: 32 additions & 0 deletions spx-gui/src/components/editor/code-editor/lsp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ export class SpxLSPClient extends Disposable {
return spxlc.request<lsp.TextEdit[] | null>(lsp.DocumentFormattingRequest.method, params)
}

async textDocumentSemanticTokens(params: lsp.SemanticTokensParams): Promise<lsp.SemanticTokens | null> {
const spxlc = await this.prepareRequest()
return spxlc.request<lsp.SemanticTokens | null>(lsp.SemanticTokensRequest.method, params)
}

// Higher-level APIs

async getResourceReferences(textDocument: TextDocumentIdentifier): Promise<ResourceReference[]> {
Expand Down Expand Up @@ -200,3 +205,30 @@ export class SpxLSPClient extends Disposable {
return completionResult as CompletionItem[]
}
}

// Keep in sync with `tools/spxls/internal/server/semantic_token.go`
export const semanticTokenLegend = {
tokenTypes: [
lsp.SemanticTokenTypes.namespace,
lsp.SemanticTokenTypes.type,
lsp.SemanticTokenTypes.interface,
lsp.SemanticTokenTypes.struct,
lsp.SemanticTokenTypes.parameter,
lsp.SemanticTokenTypes.variable,
lsp.SemanticTokenTypes.property,
lsp.SemanticTokenTypes.function,
lsp.SemanticTokenTypes.method,
lsp.SemanticTokenTypes.keyword,
lsp.SemanticTokenTypes.comment,
lsp.SemanticTokenTypes.string,
lsp.SemanticTokenTypes.number,
lsp.SemanticTokenTypes.operator,
'label' // since LSP 3.18.0, Not supported by `vscode-languageserver-protocol` yet
],
tokenModifiers: [
lsp.SemanticTokenModifiers.declaration,
lsp.SemanticTokenModifiers.readonly,
lsp.SemanticTokenModifiers.static,
lsp.SemanticTokenModifiers.defaultLibrary
]
}
9 changes: 7 additions & 2 deletions spx-gui/src/components/editor/code-editor/text-document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
type TextDocumentIdentifier,
type WordAtPosition,
type TextEdit,
getTextDocumentId
getTextDocumentId,
toMonacoUri
} from './common'
import { toMonacoPosition, toMonacoRange, fromMonacoPosition } from './ui/common'
import type { Monaco, monaco } from './monaco'
Expand Down Expand Up @@ -114,7 +115,11 @@ export class TextDocument
) {
super()

this.monacoTextModel = monaco.editor.createModel(codeOwner.getCode() ?? '', 'spx')
this.monacoTextModel = monaco.editor.createModel(
codeOwner.getCode() ?? '',
'spx',
toMonacoUri(this.codeOwner.getTextDocumentId(), monaco)
)

this.addDisposer(
watch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ const monacoEditorOptions = computed<monaco.editor.IStandaloneEditorConstruction
tabSize,
insertSpaces,
fontSize: fontSize.value,
contextmenu: false
contextmenu: false,
// TODO: preserve `semanticHighlighting` of theme (in `@shikijs/monaco`), then remove this line
'semanticHighlighting.enabled': true
}))

const monacEditorInitDataRef = shallowRef<MonacoEditorInitData | null>(null)
Expand Down
12 changes: 1 addition & 11 deletions spx-gui/src/components/editor/code-editor/ui/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import type { ResourceModel } from '@/models/common/resource-model'
import { Sprite } from '@/models/sprite'
import { Sound } from '@/models/sound'
import { isWidget } from '@/models/widget'
import { type Range, type Position, type TextDocumentIdentifier, type Selection } from '../common'
import type { Monaco } from '../monaco'
import { type Range, type Position, type Selection } from '../common'

export function token2Signal(token: monaco.CancellationToken): AbortSignal {
const ctrl = new AbortController()
Expand Down Expand Up @@ -60,15 +59,6 @@ export function toMonacoSelection(selection: Selection): monaco.ISelection {
}
}

export function fromMonacoUri(uri: monaco.Uri): TextDocumentIdentifier {
// TODO: check if this is correct
return { uri: uri.toString() }
}

export function toMonacoUri(id: TextDocumentIdentifier, monaco: Monaco): monaco.Uri {
return monaco.Uri.parse(id.uri)
}

export function supportGoTo(resourceModel: ResourceModel): boolean {
// Currently, we do not support "go to detail" for other types of resources due to two reasons:
// 1. The "selected" state of certain resource types, such as animations, is still managed within the Component, making it difficult to control from here.
Expand Down
3 changes: 0 additions & 3 deletions spx-gui/src/utils/spx/gop-tm-language.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
"name": "GoPlus",
"scopeName": "source.gop",
"patterns": [
{
"include": "#statements"
}
],
"repository": {
"statements": {
Expand Down
Loading