Skip to content

Commit fd2305d

Browse files
committed
feat: add import optimizer
1 parent e27a9bf commit fd2305d

File tree

4 files changed

+77
-5
lines changed

4 files changed

+77
-5
lines changed

resources/META-INF/plugin.xml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
implementationClass="com.koxudaxi.ruff.RuffInspection"/>
1818
<externalAnnotator language="Python" implementationClass="com.koxudaxi.ruff.RuffExternalAnnotator"/>
1919
<formattingService implementation="com.koxudaxi.ruff.RuffAsyncFormatter" order="first"/>
20+
<lang.importOptimizer language="Python" implementationClass="com.koxudaxi.ruff.RuffImportOptimizer" order="first"/>
2021
<platform.backend.documentation.targetProvider
2122
implementation="com.koxudaxi.ruff.RuffNoqaDocumentationTargetProvider"/>
2223
<actionOnSave id="RuffFormatterActionOnSave" implementation="com.koxudaxi.ruff.RuffActionOnSave" order="last"/>

src/com/koxudaxi/ruff/Ruff.kt

+14-3
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,17 @@ val NO_FIX_OUTPUT_FORMAT_ARGS = ARGS_BASE + listOf("--no-fix", "--output-format"
9292
val FORMAT_ARGS = listOf("format", "--force-exclude", "--quiet")
9393
val FORMAT_CHECK_ARGS = FORMAT_ARGS + listOf("--check")
9494
val FORMAT_RANGE_ARGS = listOf("--range")
95+
val SELECT_ARGS = listOf("--select")
96+
val IMPORT_SORT_RULES = listOf("I")
97+
val REMOVE_UNUSED_IMPORTS_RULES = listOf("F401")
98+
val OPTIMIZE_IMPORTS_RULES = SELECT_ARGS + listOf((IMPORT_SORT_RULES + REMOVE_UNUSED_IMPORTS_RULES).joinToString(","))
9599
val LSP_ARGS_BASE = listOf("server")
96100
val PREVIEW_ARGS = listOf("--preview")
101+
val Project.OPTIMIZE_IMPORTS_ARGS: List<String>
102+
get() = when {
103+
RuffCacheService.hasCheck(this) == true -> CHECK + FIX_ARGS_BASE + OPTIMIZE_IMPORTS_RULES
104+
else -> FIX_ARGS_BASE + OPTIMIZE_IMPORTS_RULES
105+
}
97106
val Project.LSP_ARGS: List<String>
98107
get() = when {
99108
RuffCacheService.hasStableServer(this) == true -> LSP_ARGS_BASE
@@ -445,20 +454,22 @@ data class SourceFile(
445454
) {
446455
val text: String? by lazy {
447456
val text = when (reloadText) {
448-
true -> documentationManager.getDocument(psiFile)?.text
457+
true -> currentText
449458
else -> psiFile.text
450459
} ?: return@lazy null
451460
if (textRange == null) return@lazy text
452461
if (textRange.endOffset <= text.length) textRange.substring(text)
453462
text.substring(textRange.startOffset, text.length)
454463
}
464+
val currentText: String? get() = documentationManager.getDocument(psiFile)?.text
455465
private val documentationManager: PsiDocumentManager get() = PsiDocumentManager.getInstance(project)
456466
val project: Project get() = psiFile.project
457467
val virtualFile: VirtualFile? get() = psiFile.virtualFile
458468

459469
val name: String get() = psiFile.name
460-
val asStdin: ByteArray? get() = text?.toCharArray()?.toByteArrayAndClear()
461-
470+
val asStdin: ByteArray? get() = text?.let {convertToBytes(it) }
471+
val asCurrentTextStdin: ByteArray? get() = currentText?.let {convertToBytes(it) }
472+
private fun convertToBytes(text: String): ByteArray = text.toCharArray().toByteArrayAndClear()
462473
fun hasSameContentAsDocument(document: Document): Boolean = document.charsSequence.contentEquals(text)
463474
}
464475

src/com/koxudaxi/ruff/RuffAsyncFormatter.kt

+15-2
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import com.intellij.formatting.FormattingContext
44
import com.intellij.formatting.service.AsyncDocumentFormattingService
55
import com.intellij.formatting.service.AsyncFormattingRequest
66
import com.intellij.formatting.service.FormattingService
7+
import com.intellij.lang.ImportOptimizer
78
import com.intellij.openapi.progress.ProcessCanceledException
89
import com.intellij.psi.PsiFile
910
import com.jetbrains.python.packaging.PyExecutionException
1011
import java.io.FileNotFoundException
1112

1213

1314
class RuffAsyncFormatter : AsyncDocumentFormattingService() {
14-
private val FEATURES: Set<FormattingService.Feature> = setOf(FormattingService.Feature.FORMAT_FRAGMENTS)
15+
private val FEATURES: Set<FormattingService.Feature> =
16+
setOf(FormattingService.Feature.FORMAT_FRAGMENTS, FormattingService.Feature.OPTIMIZE_IMPORTS)
1517

1618
override fun getFeatures(): Set<FormattingService.Feature> {
1719
return FEATURES
@@ -60,7 +62,8 @@ class RuffAsyncFormatter : AsyncDocumentFormattingService() {
6062
false
6163
)
6264
?: return@runCatching null
63-
val formatCommandStdout = runRuff(formatCommandArgs, currentText.toByteArray()) ?: return@runCatching null
65+
val formatCommandStdout =
66+
runRuff(formatCommandArgs, currentText.toByteArray()) ?: return@runCatching null
6467
return@runCatching assertResult(currentText, formatCommandStdout)
6568
}
6669

@@ -110,4 +113,14 @@ class RuffAsyncFormatter : AsyncDocumentFormattingService() {
110113
override fun getName(): String {
111114
return "Ruff Formatter"
112115
}
116+
117+
override fun getImportOptimizers(file: PsiFile): Set<ImportOptimizer?> {
118+
val importOptimizer = RuffImportOptimizer()
119+
return if (importOptimizer.supports(file)) {
120+
setOf(importOptimizer)
121+
} else {
122+
emptySet()
123+
}
124+
}
125+
113126
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.koxudaxi.ruff
2+
3+
import com.intellij.lang.ImportOptimizer
4+
import com.intellij.openapi.application.ApplicationManager
5+
import com.intellij.openapi.command.CommandProcessor
6+
import com.intellij.psi.PsiFile
7+
import com.jetbrains.python.psi.PyUtil
8+
9+
class RuffImportOptimizer : ImportOptimizer {
10+
override fun supports(psiFile: PsiFile): Boolean = psiFile.isApplicableTo
11+
12+
override fun processFile(psiFile: PsiFile): Runnable {
13+
val project = psiFile.project
14+
val sourceFile = psiFile.sourceFile
15+
val optimizeImportsArgs = project.OPTIMIZE_IMPORTS_ARGS
16+
17+
return Runnable {
18+
val stdin = sourceFile.asCurrentTextStdin ?: return@Runnable
19+
runRuffInBackground(project, stdin, project.OPTIMIZE_IMPORTS_ARGS, "Optimize Imports with Ruff") {
20+
val result = executeOnPooledThread(project, null as String?) {
21+
runRuff(sourceFile, optimizeImportsArgs)
22+
}
23+
24+
if (result != null && result.isNotBlank()) {
25+
ApplicationManager.getApplication().invokeLater {
26+
CommandProcessor.getInstance().executeCommand(
27+
project,
28+
{
29+
ApplicationManager.getApplication().runWriteAction {
30+
PyUtil.updateDocumentUnblockedAndCommitted(
31+
psiFile
32+
) { document ->
33+
if (document.text != result) {
34+
document.setText(result)
35+
}
36+
}
37+
}
38+
},
39+
"Ruff: Optimize Imports",
40+
null
41+
)
42+
}
43+
}
44+
}
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)