|
| 1 | +package org.javacs.kt.codeaction.quickfix |
| 2 | + |
| 3 | +import org.eclipse.lsp4j.* |
| 4 | +import org.eclipse.lsp4j.jsonrpc.messages.Either |
| 5 | +import org.jetbrains.kotlin.psi.KtFile |
| 6 | +import org.javacs.kt.CompiledFile |
| 7 | +import org.javacs.kt.LOG |
| 8 | +import org.javacs.kt.index.SymbolIndex |
| 9 | +import org.javacs.kt.index.Symbol |
| 10 | +import org.javacs.kt.position.offset |
| 11 | +import org.javacs.kt.util.toPath |
| 12 | +import org.javacs.kt.codeaction.quickfix.diagnosticMatch |
| 13 | +import org.javacs.kt.imports.getImportTextEditEntry |
| 14 | + |
| 15 | +class AddMissingImportsQuickFix: QuickFix { |
| 16 | + override fun compute(file: CompiledFile, index: SymbolIndex, range: Range, diagnostics: List<Diagnostic>): List<Either<Command, CodeAction>> { |
| 17 | + val uri = file.parse.toPath().toUri().toString() |
| 18 | + val unresolvedReferences = getUnresolvedReferencesFromDiagnostics(diagnostics) |
| 19 | + |
| 20 | + return unresolvedReferences.flatMap { diagnostic -> |
| 21 | + val diagnosticRange = diagnostic.range |
| 22 | + val startCursor = offset(file.content, diagnosticRange.start) |
| 23 | + val endCursor = offset(file.content, diagnosticRange.end) |
| 24 | + val symbolName = file.content.substring(startCursor, endCursor) |
| 25 | + |
| 26 | + getImportAlternatives(symbolName, file.parse, index).map { (importStr, edit) -> |
| 27 | + val codeAction = CodeAction() |
| 28 | + codeAction.title = "import ${importStr}" |
| 29 | + codeAction.kind = CodeActionKind.QuickFix |
| 30 | + codeAction.diagnostics = listOf(diagnostic) |
| 31 | + codeAction.edit = WorkspaceEdit(mapOf(uri to listOf(edit))) |
| 32 | + |
| 33 | + Either.forRight(codeAction) |
| 34 | + } |
| 35 | + } |
| 36 | + } |
| 37 | + |
| 38 | + private fun getUnresolvedReferencesFromDiagnostics(diagnostics: List<Diagnostic>): List<Diagnostic> = |
| 39 | + diagnostics.filter { |
| 40 | + "UNRESOLVED_REFERENCE" == it.code.left.trim() |
| 41 | + } |
| 42 | + |
| 43 | + private fun getImportAlternatives(symbolName: String, file: KtFile, index: SymbolIndex): List<Pair<String, TextEdit>> { |
| 44 | + // wildcard matcher to empty string, because we only want to match exactly the symbol itself, not anything extra |
| 45 | + val queryResult = index.query(symbolName, wildcardMatcher = "") |
| 46 | + |
| 47 | + return queryResult |
| 48 | + .filter { it.kind != Symbol.Kind.MODULE } |
| 49 | + .filter { |
| 50 | + // TODO: Visibility checker should be less liberal |
| 51 | + it.visibility == Symbol.Visibility.PUBLIC |
| 52 | + || it.visibility == Symbol.Visibility.PROTECTED |
| 53 | + || it.visibility == Symbol.Visibility.INTERNAL |
| 54 | + } |
| 55 | + .map { |
| 56 | + Pair(it.fqName.toString(), getImportTextEditEntry(file, it.fqName)) |
| 57 | + } |
| 58 | + } |
| 59 | +} |
0 commit comments