Skip to content

Commit

Permalink
Merge #7536
Browse files Browse the repository at this point in the history
7536: TYPE: show autocompletion popup when typing `|` r=Kobzol a=vlad20012

An improvement to #7487 - automatically show completion popup when typing `|`.

Co-authored-by: vlad20012 <beskvlad@gmail.com>
  • Loading branch information
bors[bot] and vlad20012 committed Jul 20, 2021
2 parents d5404e7 + 399ca6d commit 911d9ae
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 61 deletions.
42 changes: 0 additions & 42 deletions src/main/kotlin/org/rust/ide/typing/RsDotTypedHandler.kt

This file was deleted.

81 changes: 81 additions & 0 deletions src/main/kotlin/org/rust/ide/typing/RsTypedHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Use of this source code is governed by the MIT license that can be
* found in the LICENSE file.
*/

package org.rust.ide.typing

import com.intellij.codeInsight.AutoPopupController
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.codeInsight.editorActions.TypedHandlerDelegate
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.elementType
import com.intellij.util.text.CharArrayUtil
import org.rust.lang.core.psi.RsDotExpr
import org.rust.lang.core.psi.RsElementTypes.COLONCOLON
import org.rust.lang.core.psi.RsFile
import org.rust.lang.core.psi.RsPat


class RsTypedHandler : TypedHandlerDelegate() {
override fun charTyped(c: Char, project: Project, editor: Editor, file: PsiFile): Result {
if (file !is RsFile) return Result.CONTINUE
if (c != '.') return Result.CONTINUE

val offset = editor.caretModel.offset
PsiDocumentManager.getInstance(project).commitDocument(editor.document)
if (indentDotIfNeeded(project, file, offset)) return Result.STOP

return Result.CONTINUE
}

private fun indentDotIfNeeded(project: Project, file: RsFile, offset: Int): Boolean {
val currElement = file.findElementAt(offset - 1) ?: return false
val prevLeaf = PsiTreeUtil.prevLeaf(currElement)
if (!(prevLeaf is PsiWhiteSpace && prevLeaf.text.contains("\n"))) return false
if (currElement.parent !is RsDotExpr) return false
val curElementLength = currElement.text.length
if (offset < curElementLength) return false
CodeStyleManager.getInstance(project).adjustLineIndent(file, offset - curElementLength)
return true
}

override fun checkAutoPopup(charTyped: Char, project: Project, editor: Editor, file: PsiFile): Result {
if (file !is RsFile) return Result.CONTINUE

val offset = editor.caretModel.offset

// `:` is typed right after `:`
if (charTyped == ':' && StringUtil.endsWith(editor.document.immutableCharSequence, 0, offset, ":")) {
AutoPopupController.getInstance(project).scheduleAutoPopup(editor, CompletionType.BASIC) { f ->
val leaf = f.findElementAt(offset - 1)
leaf.elementType == COLONCOLON
}
return Result.STOP
}

// `|` is typed after `(` or `,` or `=` (ignoring whitespace) - perform lambda expr completion
if (charTyped == '|') {
val i = CharArrayUtil.shiftBackward(editor.document.immutableCharSequence, 0, offset - 1, " \n") + 1
val shouldShowPopup = StringUtil.endsWith(editor.document.immutableCharSequence, 0, i, "(")
|| StringUtil.endsWith(editor.document.immutableCharSequence, 0, i, ",")
|| StringUtil.endsWith(editor.document.immutableCharSequence, 0, i, "=")
if (shouldShowPopup) {
AutoPopupController.getInstance(project).scheduleAutoPopup(editor, CompletionType.BASIC) { f ->
val leaf = f.findElementAt(offset)
leaf?.parent !is RsPat
}
return Result.STOP
}
}

return Result.CONTINUE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ package org.rust.lang.core.completion

import com.intellij.codeInsight.completion.CompletionContributor
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.psi.PsiElement
import org.rust.lang.core.RsPsiPattern
import org.rust.lang.core.RsPsiPattern.declarationPattern
import org.rust.lang.core.RsPsiPattern.inherentImplDeclarationPattern
import org.rust.lang.core.completion.lint.RsClippyLintCompletionProvider
import org.rust.lang.core.completion.lint.RsRustcLintCompletionProvider
import org.rust.lang.core.or
import org.rust.lang.core.psi.RsElementTypes.COLON
import org.rust.lang.core.psi.ext.elementType

class RsCompletionContributor : CompletionContributor() {

Expand Down Expand Up @@ -45,7 +42,4 @@ class RsCompletionContributor : CompletionContributor() {
fun extend(type: CompletionType?, provider: RsCompletionProvider) {
extend(type, provider.elementPattern, provider)
}

override fun invokeAutoPopup(position: PsiElement, typeChar: Char): Boolean =
typeChar == ':' && position.elementType == COLON
}
4 changes: 2 additions & 2 deletions src/main/resources/META-INF/rust-core.xml
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,8 @@
id="RsRawLiteralHashesInserter"/>
<typedHandler implementation="org.rust.ide.typing.RsAngleBraceTypedHandler"
id="RsAngleBraceTypedHandler"/>
<typedHandler implementation="org.rust.ide.typing.RsDotTypedHandler"
id="RsDotTypedHandler"/>
<typedHandler implementation="org.rust.ide.typing.RsTypedHandler"
id="RsTypedHandler"/>

<backspaceHandlerDelegate implementation="org.rust.ide.typing.RsRawLiteralHashesDeleter"
id="RsRawLiteralHashesDeleter"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

package org.rust.ide.typing

class RsDotTypedHandlerTest : RsTypingTestBase() {
/** @see org.rust.lang.core.completion.RsCompletionAutoPopupTest */
class RsTypedHandlerTest : RsTypingTestBase() {
fun `test autoindent dot in chained call`() = doTestByText("""
fn main() {
frobnicate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,65 @@ package org.rust.lang.core.completion

import com.intellij.testFramework.fixtures.CompletionAutoPopupTester
import com.intellij.util.ThrowableRunnable
import org.intellij.lang.annotations.Language

class RsCompletionAutoPopupTest : RsCompletionTestBase() {
private lateinit var tester: CompletionAutoPopupTester

fun `test path`() = checkPopupIsShownAfterTyping("""
enum Foo { Bar, Baz}
fn main() {
let _ = Foo/*caret*/
}
""", "::")

fun `test lambda argument 1`() = checkPopupIsShownAfterTyping("""
fn foo(a: fn()) {}
fn main() {
foo(/*caret*/);
}
""", "|")

fun `test lambda argument 2`() = checkPopupIsShownAfterTyping("""
fn foo(a: i32, b: fn()) {}
fn main() {
foo(0, /*caret*/);
}
""", "|")

fun `test lambda argument 3`() = checkPopupIsShownAfterTyping("""
fn foo(a: i32, b: fn()) {}
fn main() {
foo(
0,
/*caret*/
);
}
""", "|")

fun `test lambda assignment`() = checkPopupIsShownAfterTyping("""
fn main() {
let a: fn() = /*caret*/
}
""", "|")

fun `test popup is not shown after typing bit OR operator`() = checkPopupIsNotShownAfterTyping("""
fn main() {
let a = 1;
let b = a/*caret*/
}
""", "|")

fun `test popup is not shown after typing OR pattern`() = checkPopupIsNotShownAfterTyping("""
const C: i32 = 0;
fn foo(a: Option<E>) {
match a {
Some(/*caret*/) => {},
_ => {}
}
}
""", "|")

override fun setUp() {
super.setUp()
tester = CompletionAutoPopupTester(myFixture)
Expand All @@ -22,16 +77,18 @@ class RsCompletionAutoPopupTest : RsCompletionTestBase() {

override fun runInDispatchThread(): Boolean = false

fun `test path auto popup`() {
myFixture.configureByText("main.rs", """
enum Foo { Bar, Baz}
fn main() {
let _ = Foo<caret>
}
""")
tester.typeWithPauses("::")
private fun checkPopupIsShownAfterTyping(@Language("Rust") code: String, toType: String) {
configureAndType(code, toType)
assertNotNull(tester.lookup)
}

private fun checkPopupIsNotShownAfterTyping(@Language("Rust") code: String, toType: String) {
configureAndType(code, toType)
assertNull(tester.lookup)
}

// TODO: find out why this test fails
// assertNotNull(tester.lookup)
private fun configureAndType(code: String, toType: String) {
InlineFile(code).withCaret()
tester.typeWithPauses(toType)
}
}

0 comments on commit 911d9ae

Please sign in to comment.