Skip to content

Commit

Permalink
Optimize APIReference loading
Browse files Browse the repository at this point in the history
  • Loading branch information
nighca committed Feb 7, 2025
1 parent 4e5e091 commit e86b5fb
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 41 deletions.
108 changes: 70 additions & 38 deletions spx-gui/src/components/editor/code-editor/code-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ import {
type CompletionContext,
type CompletionItem,
InsertTextFormat,
CompletionItemKind
CompletionItemKind,
type IAPIReferenceProvider,
type APIReferenceContext
} from './ui/code-editor-ui'
import {
type Action,
Expand All @@ -57,11 +59,66 @@ import {
makeBasicMarkdownString,
type WorkspaceDiagnostics,
type TextDocumentDiagnostics,
fromLSPDiagnostic
fromLSPDiagnostic,
isTextDocumentStageCode
} from './common'
import { TextDocument, createTextDocument } from './text-document'
import { type Monaco } from './monaco'

class APIReferenceProvider implements IAPIReferenceProvider {
constructor(
private documentBase: DocumentBase,
private lspClient: SpxLSPClient
) {}

private async getFallbackItems(ctx: APIReferenceContext) {
const isStage = isTextDocumentStageCode(ctx.textDocument.id)
const allItems = await this.documentBase.getAllDocumentations()
const overviewSet = new Set<string>()
const fallbackItems: DefinitionDocumentationItem[] = []
for (const item of allItems) {
if (item.hiddenFromList) continue
if (item.definition.package === packageSpx) {
const namespace = (item.definition.name ?? '').split('.')[0] // `Sprite` / `Game`
if (isStage && namespace === 'Sprite') continue
}
if (overviewSet.has(item.overview)) continue // Skip duplicated items, e.g., `Sprite.onStart` & `Game.onStart`
overviewSet.add(item.overview)
fallbackItems.push(item)
}
return fallbackItems
}

async provideAPIReference(ctx: APIReferenceContext, position: Position | null) {
if (position == null) return this.getFallbackItems(ctx)

const definitions = await this.lspClient
.workspaceExecuteCommandSpxGetDefinitions({
textDocument: ctx.textDocument.id,
position: toLSPPosition(position)
})
.catch((e) => {
console.warn('Failed to get definitions', e)
return null
})
ctx.signal.throwIfAborted()
let apiReferenceItems: DefinitionDocumentationItem[]
if (definitions != null && definitions.length > 0) {
const maybeDocumentationItems = await Promise.all(
definitions.map(async (def) => {
const doc = await this.documentBase.getDocumentation(def)
if (doc == null || doc.hiddenFromList) return null
return doc
})
)
apiReferenceItems = maybeDocumentationItems.filter((d) => d != null) as DefinitionDocumentationItem[]
} else {
apiReferenceItems = await this.getFallbackItems(ctx)
}
return apiReferenceItems
}
}

class ResourceReferencesProvider implements IResourceReferencesProvider {
constructor(private lspClient: SpxLSPClient) {}
async provideResourceReferences(ctx: ResourceReferencesContext): Promise<ResourceReference[]> {
Expand Down Expand Up @@ -388,6 +445,9 @@ export class CodeEditor extends Disposable {
private copilot: Copilot
private documentBase: DocumentBase
private lspClient: SpxLSPClient
private apiReferenceProvider: APIReferenceProvider
private completionProvider: CompletionProvider
private contextMenuProvider: ContextMenuProvider
private resourceReferencesProvider: ResourceReferencesProvider
private diagnosticsProvider: DiagnosticsProvider
private hoverProvider: HoverProvider
Expand All @@ -402,6 +462,9 @@ export class CodeEditor extends Disposable {
this.copilot = new Copilot(i18n, project)
this.documentBase = new DocumentBase()
this.lspClient = new SpxLSPClient(project)
this.apiReferenceProvider = new APIReferenceProvider(this.documentBase, this.lspClient)
this.completionProvider = new CompletionProvider(this.lspClient, this.documentBase)
this.contextMenuProvider = new ContextMenuProvider(this.lspClient, this.documentBase)
this.resourceReferencesProvider = new ResourceReferencesProvider(this.lspClient)
this.diagnosticsProvider = new DiagnosticsProvider(this.runtime, this.lspClient)
this.hoverProvider = new HoverProvider(this.lspClient, this.documentBase)
Expand Down Expand Up @@ -497,46 +560,15 @@ export class CodeEditor extends Disposable {
const idx = this.uis.indexOf(ui)
if (idx >= 0) this.uis.splice(idx, 1)
this.uis.push(ui)
;(window as any).ui = ui // for debugging only
const { copilot, documentBase, lspClient } = this

ui.registerAPIReferenceProvider({
async provideAPIReference(ctx, position) {
const definitions = await lspClient
.workspaceExecuteCommandSpxGetDefinitions({
textDocument: ctx.textDocument.id,
position: toLSPPosition(position)
})
.catch((e) => {
console.warn('Failed to get definitions', e)
return null
})
ctx.signal.throwIfAborted()
let apiReferenceItems: DefinitionDocumentationItem[]
if (definitions != null && definitions.length > 0) {
const maybeDocumentationItems = await Promise.all(
definitions.map(async (def) => {
const doc = await documentBase.getDocumentation(def)
if (doc == null || doc.hiddenFromList) return null
return doc
})
)
apiReferenceItems = maybeDocumentationItems.filter((d) => d != null) as DefinitionDocumentationItem[]
} else {
// When compiling errors encountered, we fallback to "all items".
apiReferenceItems = await documentBase.getAllDocumentations()
}
return apiReferenceItems
}
})

ui.registerCompletionProvider(new CompletionProvider(this.lspClient, documentBase))
ui.registerContextMenuProvider(new ContextMenuProvider(lspClient, documentBase))
ui.registerCopilot(copilot)
ui.registerAPIReferenceProvider(this.apiReferenceProvider)
ui.registerCompletionProvider(this.completionProvider)
ui.registerContextMenuProvider(this.contextMenuProvider)
ui.registerCopilot(this.copilot)
ui.registerDiagnosticsProvider(this.diagnosticsProvider)
ui.registerHoverProvider(this.hoverProvider)
ui.registerResourceReferencesProvider(this.resourceReferencesProvider)
ui.registerDocumentBase(documentBase)
ui.registerDocumentBase(this.documentBase)
}

detachUI(ui: ICodeEditorUI) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type APIReferenceItem = DefinitionDocumentationItem
export type APIReferenceContext = BaseContext

export interface IAPIReferenceProvider {
provideAPIReference(ctx: APIReferenceContext, position: Position): Promise<APIReferenceItem[]>
provideAPIReference(ctx: APIReferenceContext, position: Position | null): Promise<APIReferenceItem[]>
}

export class APIReferenceController extends Disposable {
Expand All @@ -29,8 +29,7 @@ export class APIReferenceController extends Disposable {
if (provider == null) throw new Error('No provider registered')
const { activeTextDocument: textDocument, cursorPosition } = this.ui
if (textDocument == null) throw new Error('No active text document')
const position = cursorPosition ?? { line: 1, column: 1 }
return provider.provideAPIReference({ textDocument, signal }, position)
return provider.provideAPIReference({ textDocument, signal }, cursorPosition)
})

get items() {
Expand Down

0 comments on commit e86b5fb

Please sign in to comment.