Skip to content

Commit 52f9b5d

Browse files
committed
Find an import insertion position automatically using the AST
1 parent a6fb6b7 commit 52f9b5d

File tree

1 file changed

+17
-6
lines changed

1 file changed

+17
-6
lines changed

server/src/main/kotlin/org/javacs/kt/completion/Completions.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import org.javacs.kt.util.noResult
1919
import org.javacs.kt.util.stringDistance
2020
import org.javacs.kt.util.toPath
2121
import org.javacs.kt.util.onEachIndexed
22+
import org.javacs.kt.position.location
2223
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
2324
import org.jetbrains.kotlin.container.get
2425
import org.jetbrains.kotlin.descriptors.*
@@ -62,10 +63,11 @@ fun completions(file: CompiledFile, cursor: Int, index: SymbolIndex, config: Com
6263
val partial = findPartialIdentifier(file, cursor)
6364
LOG.debug("Looking for completions that match '{}'", partial)
6465

65-
// TODO: Filter non-imported (i.e. the elementCompletions already found) and auto-import these when selected by the user
66+
// TODO: Filter out already imported completions (i.e. those for which elementCompletions were already found)
67+
// from the index completion items.
6668
val (elementItems, isExhaustive) = elementCompletionItems(file, cursor, config, partial)
6769
val elementItemList = elementItems.take(MAX_COMPLETION_ITEMS).toList()
68-
val items = (elementItemList.asSequence() + if (isExhaustive) emptySequence() else indexCompletionItems(index, partial))
70+
val items = (elementItemList.asSequence() + if (isExhaustive) emptySequence() else indexCompletionItems(file.parse, index, partial))
6971
.ifEmpty { keywordCompletionItems(partial) }
7072
val itemList = items
7173
.take(MAX_COMPLETION_ITEMS)
@@ -76,8 +78,8 @@ fun completions(file: CompiledFile, cursor: Int, index: SymbolIndex, config: Com
7678
return CompletionList(isIncomplete, itemList)
7779
}
7880

79-
/** Finds completions in the symbol index. */
80-
private fun indexCompletionItems(index: SymbolIndex, partial: String): Sequence<CompletionItem> = index
81+
/** Finds completions in the global symbol index, for potentially unimported symbols. */
82+
private fun indexCompletionItems(parsedFile: KtFile, index: SymbolIndex, partial: String): Sequence<CompletionItem> = index
8183
.query(partial, limit = MAX_COMPLETION_ITEMS)
8284
.map { CompletionItem().apply {
8385
label = it.fqName.shortName().toString()
@@ -93,11 +95,20 @@ private fun indexCompletionItems(index: SymbolIndex, partial: String): Sequence<
9395
Symbol.Kind.FIELD -> CompletionItemKind.Field
9496
}
9597
detail = "(import from ${it.fqName.parent()})"
96-
// TODO: Use actual range
97-
additionalTextEdits = listOf(TextEdit(Range(Position(0, 0), Position(0, 0)), "import ${it.fqName}\n"))
98+
val pos = findImportInsertionPosition(parsedFile, it.fqName)
99+
additionalTextEdits = listOf(TextEdit(Range(pos, pos), "\nimport ${it.fqName}")) // TODO: CRLF?
98100
} }
99101
.asSequence()
100102

103+
/** Finds a good insertion position for a new import of the given fully-qualified name. */
104+
private fun findImportInsertionPosition(parsedFile: KtFile, fqName: FqName): Position =
105+
// TODO: Insert lexicographically instead of at the end
106+
(parsedFile.importDirectives.lastOrNull() as? KtElement ?: parsedFile.packageDirective as? KtElement)
107+
?.let(::location)
108+
?.range
109+
?.end
110+
?: Position(0, 0)
111+
101112
/** Finds keyword completions starting with the given partial identifier. */
102113
private fun keywordCompletionItems(partial: String): Sequence<CompletionItem> =
103114
(KtTokens.SOFT_KEYWORDS.getTypes() + KtTokens.KEYWORDS.getTypes()).asSequence()

0 commit comments

Comments
 (0)