Реализация IntelliJ Completion Contributor для верхнего уровня файла - PullRequest
2 голосов
/ 12 июля 2020

Я разрабатываю плагин настраиваемый язык для IntelliJ и хочу добавить поддержку завершения кода , используя CompletionContributor . Язык, который я разрабатываю для поддержки IntelliJ, использует OOP и предоставляет возможность использовать типичные классы (class) и пространства имен (namespace).

На данный момент все предельно ясно, кроме для одной вещи. Я не могу понять, как вызвать специфику c поставщика завершения только на самом высоком уровне области видимости файла. Ниже я привожу пример, чтобы наглядно показать место, где в данный момент требуется автозаполнение (псевдокод):

1. namespace Foo;
2.
3. class Test {
4. 
5. }
6.
7. function foo() {
8. 
9. }

В приведенном выше примере следует использовать поставщика завершения только в строках 1 и 2 (область действия класса), частично в строке 3 (до фигурной скобки), а также в строке 6. Короче говоря, поставщик завершения не должен вызываться для строк 4 и 8.

Обратите внимание, что файл может быть пустым:

1.
2.

В этом случае завершение кода тоже должно работать .

Сильфон - это шаблонный код для достичь этого (Kotlin).

Автор:

// com.some.lang.core.completion.MyCompletionContributor

package com.some.lang.core.completion

import com.intellij.codeInsight.completion.CompletionContributor
import com.some.lang.core.completion.providers.FileScopeCompletionProvider

class MyCompletionContributor : CompletionContributor() {
    private val providers = listOf(
        FileScopeCompletionProvider
    )

    init {
        providers.forEach { extend(it) }
    }

    private fun extend(provider: MyCompletionProvider) {
        extend(provider.type, provider.context, provider)
    }
}

Провайдер аннотации:

// package com.some.lang.core.completion.MyCompletionProvider

package com.some.lang.core.completion

import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionProvider
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.patterns.ElementPattern
import com.intellij.psi.PsiElement

abstract class MyCompletionProvider : CompletionProvider<CompletionParameters>() {
    abstract val context: ElementPattern<out PsiElement>
    open val type: CompletionType = CompletionType.BASIC
}

Провайдер области файла:

// package com.some.lang.core.completion.providers.FileScopeCompletionProvider

package com.some.lang.core.completion.providers

import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
import com.some.lang.core.Language
import com.some.lang.core.completion.MyCompletionProvider

object FileScopeCompletionProvider : MyCompletionProvider() {
    override val context: ElementPattern<PsiElement>
        get() = PlatformPatterns.psiElement().withLanguage(Language)

    override fun addCompletions(
        parameters: CompletionParameters,
        processingContext: ProcessingContext,
        result: CompletionResultSet
    ) {
        result.addElement(LookupElementBuilder.create("Hello"))
    }
}

Конечно, этот код не делает того, что нужно. Тем не менее, он показывает общий дизайн, который я использую. Я уверен, что мне нужно исправить следующие строки:

    override val context: ElementPattern<PsiElement>
        get() = PlatformPatterns.psiElement().withLanguage(Language)

И главный вопрос в том, что я не понимаю, как это сделать.

Обновление:

Соответствующая часть BNF:

{
    psiClassPrefix='My'

    // ...
}

File ::= TopStatement*

private TopStatement ::= NamespaceStatement (ClassDefinition | InterfaceDefinition)

NamespaceStatement ::= 'namespace' ComplexId ';' {pin=2}

ClassDefinition ::= ClassModifier? 'class' Id SuperClass? ImplementsList? ClassBody {pin=3}

// ...

1 Ответ

2 голосов
/ 12 июля 2020

Вы можете использовать with, чтобы добавить свой собственный PatternCondition к вашим шаблонам элементов.

Предполагая, что у вас есть функция isTopLevel, определенная примерно так:

fun isTopLevel(elem: PsiElement): Boolean = elem.parent is MyLanguageFile

You Вы можете использовать это ElementPattern, чтобы сделать ваше завершение доступным только для элементов верхнего уровня.

val context = PlatformPatterns
  .psiElement()
  .with(object : PatternCondition<PsiElement>("toplevel") {
    override fun accepts(elem: PsiElement, context: ProcessingContext?) = isTopLevel(elem)
  })

Изменить: вы также можете использовать withElementType для управления типами элементов, к которым будет применяться завершение. Например:

context = psiElement()
  .andOr(
    psiElement().withElementType(NAMESPACE_NAME),
    psiElement().withElementType(CLASS_NAME),
    //other top level stuff
  )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...