improved completion
This commit is contained in:
@@ -6,6 +6,7 @@ import com.intellij.codeInsight.completion.CompletionProvider
|
||||
import com.intellij.codeInsight.completion.CompletionResultSet
|
||||
import com.intellij.codeInsight.completion.CompletionType
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.patterns.PlatformPatterns
|
||||
@@ -17,8 +18,11 @@ import dev.hgh.jai.lexer.JaiTokenTypes
|
||||
import dev.hgh.jai.module.JaiModuleResolver
|
||||
import dev.hgh.jai.module.JaiPathMode
|
||||
import dev.hgh.jai.psi.JaiDirectiveExpr
|
||||
import dev.hgh.jai.reference.JaiSymbolResolver
|
||||
|
||||
/** Directives and their adjacent comma flags before an import/load string. */
|
||||
private const val COMPLETION_DEBUG_PROPERTY = "jai.completion.debug"
|
||||
private val LOG = Logger.getInstance("dev.hgh.jai.completion")
|
||||
private val DIRECTIVE_WITH_FLAGS =
|
||||
Regex("""#(import|load)((?:\s*,\s*[A-Za-z_][A-Za-z0-9_]*)*)\s*$""")
|
||||
|
||||
@@ -43,6 +47,12 @@ private class JaiCompletionProvider : CompletionProvider<CompletionParameters>()
|
||||
) {
|
||||
val completionContext = JaiCompletionContext(parameters)
|
||||
val moduleDirective = completionContext.moduleDirective
|
||||
debug(
|
||||
"request file=${parameters.originalFile.virtualFile?.path ?: parameters.originalFile.name} " +
|
||||
"offset=${parameters.offset} token=${completionContext.debugToken} " +
|
||||
"prefix='${completionContext.prefix}' module=$moduleDirective " +
|
||||
"member=${completionContext.memberContext}",
|
||||
)
|
||||
when {
|
||||
moduleDirective != null -> {
|
||||
addModuleNames(parameters, moduleDirective, completionContext.prefix, resultSet)
|
||||
@@ -52,12 +62,18 @@ private class JaiCompletionProvider : CompletionProvider<CompletionParameters>()
|
||||
return
|
||||
}
|
||||
|
||||
completionContext.memberContext != null -> {
|
||||
val member = completionContext.memberContext!!
|
||||
addMemberSymbols(parameters, member, resultSet)
|
||||
}
|
||||
|
||||
completionContext.isDirectivePrefix -> {
|
||||
addDirectives(completionContext.prefix, resultSet)
|
||||
}
|
||||
|
||||
else -> {
|
||||
addKeywords(completionContext.prefix, resultSet)
|
||||
addSymbols(parameters, completionContext.prefix, resultSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,6 +116,53 @@ private class JaiCompletionProvider : CompletionProvider<CompletionParameters>()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addSymbols(
|
||||
parameters: CompletionParameters,
|
||||
prefix: String,
|
||||
resultSet: CompletionResultSet,
|
||||
) {
|
||||
val candidates = JaiSymbolResolver.completionCandidates(parameters.originalFile, parameters.position)
|
||||
debug("unqualified candidates=${candidates.size} names=${candidates.take(40).joinToString { it.name }}")
|
||||
candidates
|
||||
.filter { it.name.startsWith(prefix) }
|
||||
.forEach { candidate ->
|
||||
resultSet.addElement(
|
||||
LookupElementBuilder
|
||||
.create(candidate.name)
|
||||
.withPsiElement(candidate.element)
|
||||
.withTypeText(candidate.kind),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addMemberSymbols(
|
||||
parameters: CompletionParameters,
|
||||
member: JaiCompletionContext.MemberContext,
|
||||
resultSet: CompletionResultSet,
|
||||
) {
|
||||
val candidates =
|
||||
JaiSymbolResolver.memberCompletionCandidates(
|
||||
parameters.originalFile,
|
||||
parameters.position,
|
||||
member.qualifier,
|
||||
)
|
||||
debug("member qualifier=${member.qualifier} candidates=${candidates.size} names=${candidates.take(40).joinToString { it.name }}")
|
||||
candidates
|
||||
.filter { it.name.startsWith(member.prefix) }
|
||||
.forEach { candidate ->
|
||||
resultSet.addElement(
|
||||
LookupElementBuilder
|
||||
.create(candidate.name)
|
||||
.withPsiElement(candidate.element)
|
||||
.withTypeText(candidate.kind),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun debug(message: String) {
|
||||
if (java.lang.Boolean.getBoolean(COMPLETION_DEBUG_PROPERTY)) LOG.info(message)
|
||||
}
|
||||
|
||||
private fun addModuleNames(
|
||||
parameters: CompletionParameters,
|
||||
directive: String,
|
||||
@@ -149,6 +212,23 @@ private class JaiCompletionContext(
|
||||
val isDirectivePrefix: Boolean
|
||||
get() = token?.type === JaiTokenTypes.DIRECTIVE || token?.type === JaiTokenTypes.HASH
|
||||
|
||||
val debugToken: String
|
||||
get() = token?.let { "${it.type}@${it.start}..${it.end}" } ?: "none"
|
||||
|
||||
data class MemberContext(
|
||||
val qualifier: String,
|
||||
val prefix: String,
|
||||
)
|
||||
|
||||
val memberContext: MemberContext?
|
||||
get() =
|
||||
MEMBER_PREFIX.find(text.substring(0, offset))?.let { match ->
|
||||
MemberContext(
|
||||
qualifier = match.groupValues[1],
|
||||
prefix = match.groupValues.getOrNull(2).orEmpty(),
|
||||
)
|
||||
}
|
||||
|
||||
val prefix: String
|
||||
get() {
|
||||
val current = token ?: return ""
|
||||
@@ -210,6 +290,11 @@ private class JaiCompletionContext(
|
||||
val end: Int,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
val MEMBER_PREFIX =
|
||||
Regex("""(?:^|[^A-Za-z0-9_`])(`?[A-Za-z_][A-Za-z0-9_]*`?)\s*\.\s*(`?[A-Za-z_][A-Za-z0-9_]*`?)?$""")
|
||||
}
|
||||
|
||||
private fun tokenAtCaret(
|
||||
text: String,
|
||||
offset: Int,
|
||||
|
||||
Reference in New Issue
Block a user