Added type definitions and documentation to completion suggestions.

This commit is contained in:
Daan Vanden Bosch 2021-04-24 12:14:48 +02:00
parent 5210792a3e
commit 9494a70591
10 changed files with 130 additions and 102 deletions

View File

@ -54,7 +54,7 @@ class QstTests : LibTestSuite {
"/ep2/shop/gallon.qst",
"/princ/ep1/",
"/princ/ep4/",
"/solo/ep1/04.qst", // Skip because it contains every chuck twice.
"/solo/ep1/04.qst", // Skip because it contains every chunk twice.
"/fragmentofmemoryen.qst",
"/lost havoc vulcan.qst",
"/goodluck.qst",

View File

@ -316,88 +316,6 @@ class AssemblyWorker(private val sendMessage: (ServerMessage) -> Unit) {
}
}
private fun getSignature(opcode: Opcode): Signature {
val signature = StringBuilder(opcode.mnemonic).append(" ")
val params = mutableListOf<Parameter>()
var first = true
for (param in opcode.params) {
if (first) {
first = false
} else {
signature.append(", ")
}
val labelStart = signature.length
signature.appendParam(param)
params.add(
Parameter(
labelStart,
labelEnd = signature.length,
documentation = param.doc,
)
)
}
return Signature(
label = signature.toString(),
documentation = opcode.doc,
parameters = params,
)
}
private fun StringBuilder.appendParam(param: Param) {
if (param.read || param.write) {
if (param.read) append("in")
if (param.write) append("out")
append(" ")
}
when (val type = param.type) {
AnyType.Instance -> append("Any")
ByteType -> append("Byte")
ShortType -> append("Short")
IntType -> append("Int")
FloatType -> append("Float")
LabelType.Instance -> append("Label")
ILabelType -> append("ILabel")
DLabelType -> append("DLabel")
SLabelType -> append("SLabel")
ILabelVarType -> append("...ILabel")
StringType -> append("String")
is RegType -> {
append("Reg")
type.registers?.let { registers ->
append("<")
var first = true
for (register in registers) {
if (first) {
first = false
} else {
append(", ")
}
appendParam(register)
}
append(">")
}
}
RegVarType -> append("...Reg")
PointerType -> append("Pointer")
}
param.name?.let {
append(" ")
append(param.name)
}
}
private fun getHover(requestId: Int, lineNo: Int, col: Int) {
val hover = signatureHelp(lineNo, col)?.let { help ->
val sig = help.signature
@ -559,16 +477,22 @@ class AssemblyWorker(private val sendMessage: (ServerMessage) -> Unit) {
CompletionItem(
label = ".code",
type = CompletionItemType.Keyword,
detail = null,
documentation = "Start of a code segment",
insertText = "code",
),
CompletionItem(
label = ".data",
type = CompletionItemType.Keyword,
detail = null,
documentation = "Start of a data segment",
insertText = "data",
),
CompletionItem(
label = ".string",
type = CompletionItemType.Keyword,
detail = null,
documentation = "Start of a string data segment",
insertText = "string",
),
)
@ -578,14 +502,98 @@ class AssemblyWorker(private val sendMessage: (ServerMessage) -> Unit) {
(OPCODES.asSequence() + OPCODES_F8.asSequence() + OPCODES_F9.asSequence())
.filterNotNull()
.map { opcode ->
val sig = getSignature(opcode)
CompletionItem(
label = opcode.mnemonic,
// TODO: Add signature?
type = CompletionItemType.Opcode,
insertText = opcode.mnemonic,
detail = sig.label,
documentation = sig.documentation,
insertText = "${opcode.mnemonic} ",
)
}
.sortedBy { it.label }
.toList()
private fun getSignature(opcode: Opcode): Signature {
val signature = StringBuilder(opcode.mnemonic).append(" ")
val params = mutableListOf<Parameter>()
var first = true
for (param in opcode.params) {
if (first) {
first = false
} else {
signature.append(", ")
}
val labelStart = signature.length
signature.appendParam(param)
params.add(
Parameter(
labelStart,
labelEnd = signature.length,
documentation = param.doc,
)
)
}
return Signature(
label = signature.toString(),
documentation = opcode.doc,
parameters = params,
)
}
private fun StringBuilder.appendParam(param: Param) {
if (param.read || param.write) {
if (param.read) append("in")
if (param.write) append("out")
append(" ")
}
when (val type = param.type) {
AnyType.Instance -> append("Any")
ByteType -> append("Byte")
ShortType -> append("Short")
IntType -> append("Int")
FloatType -> append("Float")
LabelType.Instance -> append("Label")
ILabelType -> append("ILabel")
DLabelType -> append("DLabel")
SLabelType -> append("SLabel")
ILabelVarType -> append("...ILabel")
StringType -> append("String")
is RegType -> {
append("Reg")
type.registers?.let { registers ->
append("<")
var first = true
for (register in registers) {
if (first) {
first = false
} else {
append(", ")
}
appendParam(register)
}
append(">")
}
}
RegVarType -> append("...Reg")
PointerType -> append("Pointer")
}
param.name?.let {
append(" ")
append(param.name)
}
}
}
}

View File

@ -113,7 +113,13 @@ enum class CompletionItemType {
}
@Serializable
class CompletionItem(val label: String, val type: CompletionItemType, val insertText: String)
class CompletionItem(
val label: String,
val type: CompletionItemType,
val detail: String?,
val documentation: String?,
val insertText: String,
)
@Serializable
class SignatureHelp(val signature: Signature, val activeParameter: Int)

View File

@ -63,7 +63,7 @@ class QuestEditor(
val questInfoController = addDisposable(QuestInfoController(questEditorStore))
val npcCountsController = addDisposable(NpcCountsController(questEditorStore))
val entityInfoController = addDisposable(EntityInfoController(areaStore, questEditorStore))
val asmController = addDisposable(AsmController(asmStore))
val asmController = addDisposable(AsmEditorController(asmStore))
val npcListController = addDisposable(EntityListController(questEditorStore, npcs = true))
val objectListController =
addDisposable(EntityListController(questEditorStore, npcs = false))

View File

@ -7,6 +7,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeout
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import mu.KotlinLogging
import org.w3c.dom.Worker
import world.phantasmal.observable.ChangeEvent
import world.phantasmal.observable.Observable
@ -18,6 +19,8 @@ import world.phantasmal.web.shared.messages.*
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
private val logger = KotlinLogging.logger {}
class AsmAnalyser {
private var inlineStackArgs: Boolean = true
private var _mapDesignations = emitter<Map<Int, Int>>()
@ -95,8 +98,16 @@ class AsmAnalyser {
}
is Response<*> -> {
val continuation = inFlightRequests[message.id].unsafeCast<Continuation<Any?>?>()
continuation?.resume(message.result.unsafeCast<Any?>())
val continuation = inFlightRequests.remove(message.id)
if (continuation == null) {
logger.warn {
"No continuation for ${message::class.simpleName} ${message.id}, possibly due to timeout."
}
} else {
continuation.unsafeCast<Continuation<Any>>()
.resume(message.result.unsafeCast<Any>())
}
}
}
}

View File

@ -32,6 +32,8 @@ class AsmCompletionItemProvider(private val analyser: AsmAnalyser) : CompletionI
CompletionItemType.Opcode -> CompletionItemKind.Function
}
insertText = completion.insertText
completion.detail?.let { detail = it }
completion.documentation?.let { documentation = it }
}
}
incomplete = false

View File

@ -10,7 +10,7 @@ import world.phantasmal.web.externals.monacoEditor.createModel
import world.phantasmal.web.questEditor.stores.AsmStore
import world.phantasmal.webui.controllers.Controller
class AsmController(private val store: AsmStore) : Controller() {
class AsmEditorController(private val store: AsmStore) : Controller() {
val enabled: Val<Boolean> = store.editingEnabled
val readOnly: Val<Boolean> = !enabled or store.textModel.isNull()

View File

@ -4,12 +4,12 @@ import org.w3c.dom.Node
import world.phantasmal.core.disposable.disposable
import world.phantasmal.web.externals.monacoEditor.*
import world.phantasmal.web.questEditor.asm.monaco.EditorHistory
import world.phantasmal.web.questEditor.controllers.AsmController
import world.phantasmal.web.questEditor.controllers.AsmEditorController
import world.phantasmal.webui.dom.div
import world.phantasmal.webui.obj
import world.phantasmal.webui.widgets.Widget
class AsmEditorWidget(private val ctrl: AsmController) : Widget() {
class AsmEditorWidget(private val ctrl: AsmEditorController) : Widget() {
private lateinit var editor: IStandaloneCodeEditor
override fun Node.createElement() =

View File

@ -1,18 +1,19 @@
package world.phantasmal.web.questEditor.widgets
import org.w3c.dom.Node
import world.phantasmal.web.questEditor.controllers.AsmController
import world.phantasmal.web.questEditor.controllers.AsmEditorController
import world.phantasmal.webui.dom.div
import world.phantasmal.webui.widgets.Checkbox
import world.phantasmal.webui.widgets.Toolbar
import world.phantasmal.webui.widgets.Widget
class AsmToolbarWidget(private val ctrl: AsmController) : Widget() {
class AsmToolbarWidget(private val ctrl: AsmEditorController) : Widget() {
override fun Node.createElement() =
div {
className = "pw-quest-editor-asm-toolbar"
addChild(Toolbar(
addChild(
Toolbar(
enabled = ctrl.enabled,
children = listOf(
Checkbox(

View File

@ -1,11 +1,11 @@
package world.phantasmal.web.questEditor.widgets
import org.w3c.dom.Node
import world.phantasmal.web.questEditor.controllers.AsmController
import world.phantasmal.web.questEditor.controllers.AsmEditorController
import world.phantasmal.webui.dom.div
import world.phantasmal.webui.widgets.Widget
class AsmWidget(private val ctrl: AsmController) : Widget() {
class AsmWidget(private val ctrl: AsmEditorController) : Widget() {
private lateinit var editorWidget: AsmEditorWidget
override fun Node.createElement() =