Skip to content

Google auto complete

Dmitry Kandalov edited this page Oct 27, 2024 · 16 revisions

This is a mini-plugin for IntelliJ IDEs that adds Google search results to the auto-complete menu. It's not particularly useful (and such a hipster thing to do) but it can be a lot of fun. I guess this could be called "AI" these days 🤷

The main point to confirm that auto-completion contributors can be added and modified at runtime. Unfortunately, the completion is not always working as expected so please let me know if you can fix it.

import com.google.gson.Gson
import com.google.gson.JsonArray
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.completion.CompletionType.BASIC
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.lang.Language
import com.intellij.lang.LanguageExtension
import com.intellij.openapi.Disposable
import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
import liveplugin.PluginUtil.show
import liveplugin.getStaticField
import java.net.URL
import java.net.URLEncoder

if (!isIdeStartup) {
    val language = Language.findLanguageByID("kotlin")!!
    registerCompletionContributor(language, BASIC, psiElement(), pluginDisposable) { _, _, result ->
        result.addAllElements(
            findSuggestionsFor(result.prefixMatcher.prefix)
                .map { LookupElementBuilder.create(it.toCamelCase()) }
        )
    }
    show("Loaded auto-completion contributor")
}

fun registerCompletionContributor(
    language: Language,
    completionType: CompletionType,
    elementPattern: ElementPattern<PsiElement>,
    disposable: Disposable,
    callback: (CompletionParameters, ProcessingContext, CompletionResultSet) -> Unit
) {
    val languageExtension = CompletionContributor::class.java.getStaticField<LanguageExtension<CompletionContributor>>("INSTANCE")
//    languageExtension.clearCache()

    val contributor = object : CompletionContributor() {}
    contributor.extend(completionType, elementPattern, object : CompletionProvider<CompletionParameters>() {
        override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
            callback(parameters, context, result)
        }
    })
    languageExtension.addExplicitExtension(language, contributor, disposable)
}

val gson = Gson()

fun findSuggestionsFor(text: String): List<String> {
    val encodedText = URLEncoder.encode(text, "UTF-8")
    val json = URL("https://suggestqueries.google.com/complete/search?client=firefox&q=$encodedText").readText()
    return (gson.fromJson(json, JsonArray::class.java).get(1) as JsonArray).map { it.asString }
        .filter { !it.equals(text, ignoreCase = true) }
}

fun String.toCamelCase() =
    split(" ").joinToString("") { word -> word.replaceFirstChar { it.titlecase() } }
Clone this wiki locally