Code improvements

This commit is contained in:
Valentin Kipyatkov
2015-04-02 19:29:34 +03:00
parent 4b32db5c5e
commit 751f952536
2 changed files with 57 additions and 79 deletions
@@ -379,9 +379,9 @@ public fun ASTNode.siblings(forward: Boolean = true, withItself: Boolean = true)
return if (withItself) stream else stream.drop(1) return if (withItself) stream else stream.drop(1)
} }
public fun PsiElement.parents(withItself: Boolean = true): Stream<PsiElement> { public fun PsiElement.parents(withItself: Boolean = true): Sequence<PsiElement> {
val stream = stream(this) { if (it is PsiFile) null else it.getParent() } val sequence = sequence(this) { if (it is PsiFile) null else it.getParent() }
return if (withItself) stream else stream.drop(1) return if (withItself) sequence else sequence.drop(1)
} }
public fun ASTNode.parents(withItself: Boolean = true): Stream<ASTNode> { public fun ASTNode.parents(withItself: Boolean = true): Stream<ASTNode> {
@@ -36,8 +36,10 @@ import com.intellij.lang.java.JavaLanguage
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.DumbService
import org.jetbrains.kotlin.psi.psiUtil.parents
public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBlockTransferableData>() { public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBlockTransferableData>() {
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.conversion.copy.ConvertJavaCopyPastePostProcessor")
override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> { override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> {
try { try {
@@ -60,118 +62,98 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
public override fun processTransferableData(project: Project, editor: Editor, bounds: RangeMarker, caretOffset: Int, indented: Ref<Boolean>, values: List<TextBlockTransferableData>) { public override fun processTransferableData(project: Project, editor: Editor, bounds: RangeMarker, caretOffset: Int, indented: Ref<Boolean>, values: List<TextBlockTransferableData>) {
if (DumbService.getInstance(project).isDumb()) return if (DumbService.getInstance(project).isDumb()) return
assert(values.size() == 1) val data = values.single()
val value = values.first() if (data !is CopiedCode) return
if (value !is CopiedCode) return
val sourceFile = PsiFileFactory.getInstance(project). val sourceFile = PsiFileFactory.getInstance(project).
createFileFromText(value.fileName, JavaLanguage.INSTANCE, value.fileText) as? PsiJavaFile ?: return createFileFromText(data.fileName, JavaLanguage.INSTANCE, data.fileText) as? PsiJavaFile ?: return
val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) as? JetFile ?: return
if (targetFile !is JetFile) return
val jetEditorOptions = JetEditorOptions.getInstance()!! val jetEditorOptions = JetEditorOptions.getInstance()!!
val needConvert = jetEditorOptions.isEnableJavaToKotlinConversion() && (jetEditorOptions.isDonTShowConversionDialog() || okFromDialog(project)) val needConvert = jetEditorOptions.isEnableJavaToKotlinConversion() && (jetEditorOptions.isDonTShowConversionDialog() || okFromDialog(project))
if (needConvert) { if (needConvert) {
val text = convertCopiedCodeToKotlin(value, sourceFile, targetFile) val text = convertCopiedCodeToKotlin(data, sourceFile, data.fileText, targetFile)
if (text.isNotEmpty()) { if (!text.isEmpty()) {
ApplicationManager.getApplication()!!.runWriteAction { ApplicationManager.getApplication()!!.runWriteAction {
val startOffset = bounds.getStartOffset() val startOffset = bounds.getStartOffset()
editor.getDocument().replaceString(bounds.getStartOffset(), bounds.getEndOffset(), text) editor.getDocument().replaceString(startOffset, bounds.getEndOffset(), text)
val endOffsetAfterCopy = startOffset + text.length() val endOffsetAfterCopy = startOffset + text.length()
editor.getCaretModel().moveToOffset(endOffsetAfterCopy) editor.getCaretModel().moveToOffset(endOffsetAfterCopy)
CodeStyleManager.getInstance(project)!!.reformatText(targetFile, startOffset, endOffsetAfterCopy) CodeStyleManager.getInstance(project)!!.reformatText(targetFile, startOffset, endOffsetAfterCopy)
PsiDocumentManager.getInstance(targetFile.getProject()).commitDocument(editor.getDocument())
} }
} }
} }
} }
private fun convertCopiedCodeToKotlin(code: CopiedCode, fileCopiedFrom: PsiJavaFile, fileCopiedTo: JetFile): String { private fun convertCopiedCodeToKotlin(code: CopiedCode, sourceFile: PsiJavaFile, sourceFileText: String, targetFile: JetFile): String {
val converter = JavaToKotlinConverter( val converter = JavaToKotlinConverter(
fileCopiedFrom.getProject(), sourceFile.getProject(),
ConverterSettings.defaultSettings, ConverterSettings.defaultSettings,
FilesConversionScope(listOf(fileCopiedFrom)), FilesConversionScope(listOf(sourceFile)),
IdeaReferenceSearcher, IdeaReferenceSearcher,
IdeaResolverForConverter IdeaResolverForConverter
) )
val startOffsets = code.startOffsets assert(code.startOffsets.size() == code.endOffsets.size(), "Must have the same size")
val endOffsets = code.endOffsets val builder = StringBuilder()
assert(startOffsets.size() == endOffsets.size()) { "Must have the same size" } for (i in code.startOffsets.indices) {
val result = StringBuilder() val textRange = TextRange(code.startOffsets[i], code.endOffsets[i])
for (i in startOffsets.indices) { builder.append(convertRangeToKotlin(sourceFile, sourceFileText, targetFile, textRange, converter))
val startOffset = startOffsets[i]
val endOffset = endOffsets[i]
result.append(convertRangeToKotlin(fileCopiedFrom, fileCopiedTo, TextRange(startOffset, endOffset), converter))
} }
return StringUtil.convertLineSeparators(result.toString()) return StringUtil.convertLineSeparators(builder.toString())
} }
private fun convertRangeToKotlin(file: PsiJavaFile, private fun convertRangeToKotlin(file: PsiJavaFile,
fileCopiedTo: JetFile, fileText: String,
targetFile: JetFile,
range: TextRange, range: TextRange,
converter: JavaToKotlinConverter): String { converter: JavaToKotlinConverter): String {
val result = StringBuilder() val builder = StringBuilder()
var currentRange = range var currentRange = range
//TODO: probably better to use document to get text by range
val fileText = file.getText()!!
while (!currentRange.isEmpty()) { while (!currentRange.isEmpty()) {
val leafElement = findFirstLeafElementWhollyInRange(file, currentRange) val leaf = findFirstLeafWhollyInRange(file, currentRange)
if (leafElement == null) { if (leaf == null) {
val unconvertedSuffix = fileText.substring(currentRange.start, currentRange.end) val unconvertedSuffix = fileText.substring(currentRange.start, currentRange.end)
result.append(unconvertedSuffix) builder.append(unconvertedSuffix)
break break
} }
val elementToConvert = findTopMostParentWhollyInRange(currentRange, leafElement)
val unconvertedPrefix = fileText.substring(currentRange.start, elementToConvert.range.start) val elementToConvert = leaf
result.append(unconvertedPrefix) .parents(withItself = true)
val converted = converter.elementsToKotlin(listOf(elementToConvert to J2kPostProcessor(fileCopiedTo, formatCode = false)))[0] .first {
if (converted.isNotEmpty()) { val parent = it.getParent()
result.append(converted) parent == null || parent.range !in currentRange
}
val elementToConvertRange = elementToConvert.range
val unconvertedPrefix = fileText.substring(currentRange.start, elementToConvertRange.start)
builder.append(unconvertedPrefix)
val converted = converter.elementsToKotlin(listOf(elementToConvert to J2kPostProcessor(targetFile, formatCode = false))).single()
if (!converted.isEmpty()) {
builder.append(converted)
} }
else { else {
result.append(elementToConvert.getText()) builder.append(fileText.substring(elementToConvertRange.start, elementToConvertRange.end))
} }
val endOfConverted = elementToConvert.range.end
currentRange = TextRange(endOfConverted, currentRange.end) currentRange = TextRange(elementToConvertRange.end, currentRange.end)
} }
return result.toString() return builder.toString()
} }
private fun findFirstLeafElementWhollyInRange(file: PsiJavaFile, range: TextRange): PsiElement? { private fun findFirstLeafWhollyInRange(file: PsiJavaFile, range: TextRange): PsiElement? {
var i = range.start var element = file.findElementAt(range.start) ?: return null
while (i < range.end) { var elementRange = element.range
val element = file.findElementAt(i) if (elementRange.start < range.start) {
if (element == null) { element = file.findElementAt(elementRange.end) ?: return null
++i elementRange = element.range
continue
}
val elemRange = element.range
if (elemRange !in range) {
i = elemRange.end
continue
}
return element
} }
return null assert(elementRange.start >= range.start)
} return if (elementRange.end <= range.end) element else null
private fun findTopMostParentWhollyInRange(range: TextRange,
base: PsiElement): PsiElement {
assert(base.range in range) {
"Base element out of range. Range: $range, element: ${base.getText()}, element's range: ${base.range}."
}
var elem = base
while (true) {
val parent = elem.getParent()
if (parent == null || parent is PsiJavaFile || parent.range !in range) {
break
}
elem = parent
}
return elem
} }
private fun okFromDialog(project: Project): Boolean { private fun okFromDialog(project: Project): Boolean {
@@ -179,8 +161,4 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
dialog.show() dialog.show()
return dialog.isOK() return dialog.isOK()
} }
companion object {
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.conversion.copy.ConvertJavaCopyPastePostProcessor")
}
} }