Skip to content

Commit

Permalink
feat(completion): add support for code fence language detection and c…
Browse files Browse the repository at this point in the history
…ompletion #101

This commit introduces a new feature that enhances the completion experience for code fences. It includes support for detecting the language of a code fence based on its info-string and provides completion suggestions accordingly. Additionally, the commit refactors the existing completion logic to ensure smoother integration with the new code fence language support.
  • Loading branch information
phodal committed Mar 13, 2024
1 parent 2f0b598 commit 7fd13e7
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 15 deletions.
@@ -0,0 +1,58 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package cc.unitmesh.devti

import com.intellij.openapi.util.text.StringUtil

/**
* Service to work with Markdown code fence's info-string.
*
* [CodeFenceLanguageAliases] is able to find possible IntelliJ Language ID
* for info string (including resolution of standard aliases) and
* (backwards) suggest correct info string for IntelliJ Language ID
*/
object CodeFenceLanguageAliases {
private data class Entry(
val id: String,
val main: String,
val aliases: Set<String>
)

private val aliases = setOf(
Entry("go", "go", setOf("golang")),
Entry("HCL", "hcl", setOf("hcl")),
Entry("ApacheConfig", "apacheconf", setOf("aconf", "apache", "apacheconfig")),
Entry("Batch", "batch", setOf("bat", "batchfile")),
Entry("CoffeeScript", "coffeescript", setOf("coffee", "coffee-script")),
Entry("JavaScript", "javascript", setOf("js", "node")),
Entry("Markdown", "markdown", setOf("md")),
Entry("PowerShell", "powershell", setOf("posh", "pwsh")),
Entry("Python", "python", setOf("python2", "python3", "py")),
Entry("R", "r", setOf("rlang", "rscript")),
Entry("RegExp", "regexp", setOf("regex")),
Entry("Ruby", "ruby", setOf("ruby", "rb")),
Entry("Yaml", "yaml", setOf("yml")),
Entry("Kotlin", "kotlin", setOf("kt", "kts")),
Entry("HCL-Terraform", "terraform", setOf("hcl-terraform", "tf")),
Entry("C#", "csharp", setOf("cs", "c#")),
Entry("F#", "fsharp", setOf("fs", "f#")),
Entry("Shell Script", "shell", setOf("shell script", "bash", "zsh", "sh"))
)

fun findRegisteredEntry(value: String): String? {
val lower = value.lowercase()
val entry = aliases.singleOrNull { lower == it.main || lower in it.aliases }
return entry?.id
}

/**
* Get recommended alias for [id]
* @return recommended alias if any or just [id]
*/
fun findMainAlias(id: String): String {
return findMainAliasIfRegistered(id) ?: StringUtil.toLowerCase(id)
}

fun findMainAliasIfRegistered(id: String): String? {
return aliases.singleOrNull { id == it.id }?.main
}
}
Expand Up @@ -19,19 +19,17 @@ class DevInTypedHandler : TypedHandlerDelegate() {

return when (charTyped) {
'`' -> {
PsiDocumentManager.getInstance(project).commitDocument(editor.document)
for (caret in editor.caretModel.allCarets) {
val offset = caret.offset
if (offset == 0) {
return Result.CONTINUE
}

val element = file.findElementAt(offset - 1)
if (element?.elementType == DevInTypes.CODE_CONTENT || element?.elementType == DevInTypes.CODE_BLOCK_END) {
return Result.CONTINUE
}
val offset = editor.caretModel.primaryCaret.offset
if (offset == 0) {
return Result.CONTINUE
}

val element = file.findElementAt(offset - 1)
if (element?.elementType == DevInTypes.CODE_CONTENT || element?.elementType == DevInTypes.CODE_BLOCK_END) {
return Result.CONTINUE
}

PsiDocumentManager.getInstance(project).commitDocument(editor.document)
AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, null)
return Result.STOP
}
Expand Down
@@ -1,5 +1,6 @@
package cc.unitmesh.language.completion

import cc.unitmesh.devti.CodeFenceLanguageAliases
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
Expand All @@ -16,8 +17,8 @@ class CodeLanguageProvider : CompletionProvider<CompletionParameters>() {
result: CompletionResultSet,
) {
for (language in LanguageUtil.getInjectableLanguages()) {
val id = language.id
val handler = LookupElementBuilder.create(id)
val alias = CodeFenceLanguageAliases.findMainAlias(language.id)
val handler = LookupElementBuilder.create(alias)
.withIcon(createLanguageIcon(language))
.withTypeText(language.displayName, true)
.withInsertHandler(MyInsertHandler())
Expand All @@ -26,7 +27,7 @@ class CodeLanguageProvider : CompletionProvider<CompletionParameters>() {
}
}

fun createLanguageIcon(language: Language): Icon {
private fun createLanguageIcon(language: Language): Icon {
return DeferredIconImpl(null, language, true) { curLanguage: Language -> curLanguage.associatedFileType?.icon }
}

Expand Down
Expand Up @@ -11,8 +11,8 @@ class DevInCompletionContributor : CompletionContributor() {
private val INPUT_DUMMY_IDENTIFIER = "AutoDevDummy"

init {
extend(CompletionType.BASIC, psiElement(DevInTypes.VARIABLE_ID), CustomVariableProvider())
extend(CompletionType.BASIC, psiElement(DevInTypes.LANGUAGE_ID), CodeLanguageProvider())
extend(CompletionType.BASIC, psiElement(DevInTypes.VARIABLE_ID), CustomVariableProvider())
}

override fun beforeCompletion(context: CompletionInitializationContext) {
Expand Down

0 comments on commit 7fd13e7

Please sign in to comment.