Make j2k depends on idea-core instead of idea-full

This commit is contained in:
Natalia Ukhorskaya
2015-07-27 18:32:12 +03:00
parent 22ee063269
commit c86b25b1dc
18 changed files with 450 additions and 291 deletions
@@ -32,11 +32,12 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileVisitor
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.j2k.*
import org.jetbrains.kotlin.j2k.ConverterSettings
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
import java.io.File
import java.io.IOException
import java.util.ArrayList
@@ -50,7 +51,7 @@ public class JavaToKotlinAction : AnAction() {
var converterResult: JavaToKotlinConverter.FilesResult? = null
fun convert() {
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, IdeaResolverForConverter, IdeaDocCommentConverter)
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices)
converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().getProgressIndicator())
}
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor
import org.jetbrains.kotlin.idea.codeInsight.KotlinReferenceData
import org.jetbrains.kotlin.idea.editor.JetEditorOptions
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
@@ -159,9 +160,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
val converter = JavaToKotlinConverter(
project,
ConverterSettings.defaultSettings,
IdeaReferenceSearcher,
IdeaResolverForConverter,
IdeaDocCommentConverter
IdeaJavaToKotlinServices
)
val inputElements = elementsAndTexts.filterIsInstance<PsiElement>()
@@ -0,0 +1,217 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.j2k
import com.intellij.ide.highlighter.HtmlFileType
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.XmlRecursiveElementVisitor
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.javadoc.PsiDocTag
import com.intellij.psi.javadoc.PsiDocToken
import com.intellij.psi.javadoc.PsiInlineDocTag
import com.intellij.psi.xml.XmlFile
import com.intellij.psi.xml.XmlTag
import com.intellij.psi.xml.XmlText
import com.intellij.psi.xml.XmlTokenType
import org.jetbrains.kotlin.j2k.DocCommentConverter
import org.jetbrains.kotlin.j2k.content
object IdeaDocCommentConverter : DocCommentConverter {
override fun convertDocComment(docComment: PsiDocComment): String {
val html = StringBuilder {
appendJavadocElements(docComment.getDescriptionElements())
tagsLoop@
for (tag in docComment.getTags()) {
when (tag.getName()) {
"deprecated" -> continue@tagsLoop
"see" -> append("@see ${convertJavadocLink(tag.content())}\n")
else -> appendJavadocElements(tag.getChildren()).append("\n")
}
}
}.toString()
if (html.trim().isEmpty() && docComment.findTagByName("deprecated") != null) {
// @deprecated was the only content of the doc comment; we can drop the comment
return ""
}
val htmlFile = PsiFileFactory.getInstance(docComment.getProject()).createFileFromText(
"javadoc.html", HtmlFileType.INSTANCE, html)
val htmlToMarkdownConverter = HtmlToMarkdownConverter()
htmlFile.accept(htmlToMarkdownConverter)
return htmlToMarkdownConverter.result
}
private fun StringBuilder.appendJavadocElements(elements: Array<PsiElement>): StringBuilder {
elements.forEach {
if (it is PsiInlineDocTag) {
append(convertInlineDocTag(it))
}
else {
append(it.getText())
}
}
return this
}
private fun convertInlineDocTag(tag: PsiInlineDocTag) = when(tag.getName()) {
"code", "literal" -> {
val text = tag.getDataElements().map { it.getText() }.join("")
val escaped = StringUtil.escapeXml(text.trimStart())
if (tag.getName() == "code") "<code>$escaped</code>" else escaped
}
"link", "linkplain" -> {
val valueElement = tag.linkElement()
val labelText = tag.getDataElements().firstOrNull { it is PsiDocToken }?.getText() ?: ""
val kdocLink = convertJavadocLink(valueElement?.getText())
val linkText = if (labelText.isEmpty()) kdocLink else StringUtil.escapeXml(labelText)
"<a docref=\"$kdocLink\">$linkText</a>"
}
else -> tag.getText()
}
private fun convertJavadocLink(link: String?): String =
if (link != null) link.substringBefore('(').replace('#', '.') else ""
private fun PsiDocTag.linkElement(): PsiElement? =
getValueElement() ?: getDataElements().firstOrNull { it !is PsiWhiteSpace }
private class HtmlToMarkdownConverter() : XmlRecursiveElementVisitor() {
private enum class ListType { Ordered, Unordered; }
data class MarkdownSpan(val prefix: String, val suffix: String) {
companion object {
val Empty = MarkdownSpan("", "")
fun wrap(text: String) = MarkdownSpan(text, text)
fun prefix(text: String) = MarkdownSpan(text, "")
}
}
val result: String
get() = markdownBuilder.toString()
private val markdownBuilder = StringBuilder("/**")
private var afterLineBreak = false
private var whitespaceIsPartOfText = true
private var currentListType = ListType.Unordered
override fun visitWhiteSpace(space: PsiWhiteSpace) {
super.visitWhiteSpace(space)
if (whitespaceIsPartOfText) {
appendPendingText()
markdownBuilder.append(space.getText())
if (space.textContains('\n')) {
afterLineBreak = true
}
}
}
override fun visitElement(element: PsiElement) {
super.visitElement(element)
val tokenType = element.getNode().getElementType()
if (tokenType == XmlTokenType.XML_DATA_CHARACTERS || tokenType == XmlTokenType.XML_CHAR_ENTITY_REF) {
appendPendingText()
markdownBuilder.append(element.getText())
}
}
override fun visitXmlTag(tag: XmlTag) {
withWhitespaceAsPartOfText(false) {
val oldListType = currentListType
val atLineStart = afterLineBreak
appendPendingText()
val (openingMarkdown, closingMarkdown) = getMarkdownForTag(tag, atLineStart)
markdownBuilder.append(openingMarkdown)
super.visitXmlTag(tag)
markdownBuilder.append(closingMarkdown)
currentListType = oldListType
}
}
override fun visitXmlText(text: XmlText) {
withWhitespaceAsPartOfText(true) {
super.visitXmlText(text)
}
}
private inline fun withWhitespaceAsPartOfText(newValue: Boolean, block: () -> Unit) {
val oldValue = whitespaceIsPartOfText
whitespaceIsPartOfText = newValue
try {
block()
}
finally {
whitespaceIsPartOfText = oldValue
}
}
private fun getMarkdownForTag(tag: XmlTag, atLineStart: Boolean): MarkdownSpan = when(tag.getName()) {
"b", "strong" -> MarkdownSpan.wrap("**")
"p" -> if (atLineStart) MarkdownSpan.prefix("\n * ") else MarkdownSpan.prefix("\n *\n *")
"i", "em" -> MarkdownSpan.wrap("*")
"s", "del" -> MarkdownSpan.wrap("~~")
"code" -> MarkdownSpan.wrap("`")
"a" -> {
if (tag.getAttributeValue("docref") != null) {
val docRef = tag.getAttributeValue("docref")
val innerText = tag.getValue().getText()
if (docRef == innerText) MarkdownSpan("[", "]") else MarkdownSpan("[", "][$docRef]")
}
else {
MarkdownSpan("[", "](${tag.getAttributeValue("href")})")
}
}
"ul" -> { currentListType = ListType.Unordered; MarkdownSpan.Empty }
"ol" -> { currentListType = ListType.Ordered; MarkdownSpan.Empty }
"li" -> if (currentListType == ListType.Unordered) MarkdownSpan.prefix(" * ") else MarkdownSpan.prefix(" 1. ")
else -> MarkdownSpan.Empty
}
private fun appendPendingText() {
if (afterLineBreak ) {
markdownBuilder.append(" * ")
afterLineBreak = false
}
}
override fun visitXmlFile(file: XmlFile) {
super.visitXmlFile(file)
markdownBuilder.append(" */")
}
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.j2k
import org.jetbrains.kotlin.j2k.*
object IdeaJavaToKotlinServices: JavaToKotlinConverterServices {
override val referenceSearcher: ReferenceSearcher
get() = IdeaReferenceSearcher
override val resolverForConverter: ResolverForConverter
get() = IdeaResolverForConverter
override val docCommentConverter: DocCommentConverter
get() = IdeaDocCommentConverter
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.j2k
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.fileTypes.FileType
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.idea.JetLanguage
import org.jetbrains.kotlin.j2k.ReferenceSearcher
import java.util.*
public object IdeaReferenceSearcher: ReferenceSearcher {
override fun findLocalUsages(element: PsiElement, scope: PsiElement) = ReferencesSearch.search(element, LocalSearchScope(scope)).findAll()
override fun hasInheritors(`class`: PsiClass) = ClassInheritorsSearch.search(`class`, false).any()
override fun hasOverrides(method: PsiMethod) = OverridingMethodsSearch.search(method, false).any()
override fun findUsagesForExternalCodeProcessing(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference> {
val fileTypes = ArrayList<FileType>()
if (searchJava) {
fileTypes.add(JavaLanguage.INSTANCE.getAssociatedFileType()!!)
}
if (searchKotlin) {
fileTypes.add(JetLanguage.INSTANCE.getAssociatedFileType()!!)
}
val searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.projectScope(element.getProject()), *fileTypes.toTypedArray())
return ReferencesSearch.search(element, searchScope).findAll()
}
}
@@ -16,8 +16,6 @@
package org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiExpression
import com.intellij.psi.PsiMethod
@@ -25,10 +23,7 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.refactoring.introduceParameter.IntroduceParameterData
import com.intellij.refactoring.introduceParameter.IntroduceParameterMethodUsagesProcessor
import com.intellij.usageView.UsageInfo
import com.intellij.util.ArrayUtil
import com.intellij.util.containers.MultiMap
import gnu.trove.TIntArrayList
import org.jetbrains.kotlin.asJava.KotlinLightMethod
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -36,24 +31,20 @@ import org.jetbrains.kotlin.idea.JetFileType
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.refactoring.j2k
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeSignatureData
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo
import org.jetbrains.kotlin.idea.refactoring.changeSignature.originalBaseFunctionDescriptor
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.*
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JetCallableDefinitionUsage
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JetConstructorDelegationCallUsage
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JetFunctionCallUsage
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JetUsageInfo
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.j2k.ConverterSettings
import org.jetbrains.kotlin.j2k.IdeaReferenceSearcher
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.types.JetType
import java.util.Arrays
import java.util.Collections
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
public class KotlinIntroduceParameterMethodUsageProcessor : IntroduceParameterMethodUsagesProcessor {
override fun isMethodUsage(usage: UsageInfo): Boolean = (usage.getElement() as? JetElement)?.let {
@@ -63,12 +63,10 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMemberDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.string.collapseSpaces
import org.jetbrains.kotlin.j2k.ConverterSettings
import org.jetbrains.kotlin.j2k.IdeaDocCommentConverter
import org.jetbrains.kotlin.j2k.IdeaReferenceSearcher
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
@@ -77,10 +75,10 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import org.jetbrains.kotlin.resolve.BindingContext
import java.io.File
import java.lang.annotation.Retention
import java.util.ArrayList
import java.util.Collections
import javax.swing.Icon
import java.lang.annotation.Retention
fun <T: Any> PsiElement.getAndRemoveCopyableUserData(key: Key<T>): T? {
val data = getCopyableUserData(key)
@@ -572,9 +570,7 @@ fun PsiExpression.j2k(): JetExpression? {
val project = getProject()
val j2kConverter = JavaToKotlinConverter(project,
ConverterSettings.defaultSettings,
IdeaReferenceSearcher,
IdeaResolverForConverter,
IdeaDocCommentConverter)
IdeaJavaToKotlinServices)
val text = j2kConverter.elementsToKotlin(listOf(this)).results.single()?.text ?: return null //TODO: insert imports
return JetPsiFactory(getProject()).createExpression(text)
}
+3 -1
View File
@@ -8,12 +8,14 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="idea-full" level="project" />
<orderEntry type="library" name="intellij-core" level="project" />
<orderEntry type="module" module-name="frontend" />
<orderEntry type="module" module-name="frontend.java" />
<orderEntry type="module" module-name="light-classes" />
<orderEntry type="module" module-name="idea" scope="TEST" />
<orderEntry type="module" module-name="compiler-tests" scope="TEST" />
<orderEntry type="module" module-name="util" />
<orderEntry type="library" scope="TEST" name="idea-full" level="project" />
<orderEntry type="module" module-name="idea-test-framework" scope="TEST" />
</component>
</module>
+15 -17
View File
@@ -39,9 +39,7 @@ class Converter private constructor(
private val elementToConvert: PsiElement,
val settings: ConverterSettings,
val inConversionScope: (PsiElement) -> Boolean,
val referenceSearcher: ReferenceSearcher,
val resolverForConverter: ResolverForConverter,
private val docCommentConverter: DocCommentConverter,
val services: JavaToKotlinConverterServices,
private val commonState: Converter.CommonState,
private val personalState: Converter.PersonalState
) {
@@ -61,20 +59,20 @@ class Converter private constructor(
public val specialContext: PsiElement? = personalState.specialContext
public val referenceSearcher: CachingReferenceSearcher = CachingReferenceSearcher(services.referenceSearcher)
companion object {
public fun create(elementToConvert: PsiElement, settings: ConverterSettings, inConversionScope: (PsiElement) -> Boolean,
referenceSearcher: ReferenceSearcher, resolverForConverter: ResolverForConverter,
docCommentConverter: DocCommentConverter, usageProcessingsCollector: (UsageProcessing) -> Unit): Converter {
services: JavaToKotlinConverterServices, usageProcessingsCollector: (UsageProcessing) -> Unit): Converter {
return Converter(elementToConvert, settings, inConversionScope,
CachingReferenceSearcher(referenceSearcher),
resolverForConverter, docCommentConverter, CommonState(usageProcessingsCollector), PersonalState(null))
services, CommonState(usageProcessingsCollector), PersonalState(null))
}
}
public fun withSpecialContext(context: PsiElement): Converter = withState(PersonalState(context))
private fun withState(state: PersonalState): Converter
= Converter(elementToConvert, settings, inConversionScope, referenceSearcher, resolverForConverter, docCommentConverter, commonState, state)
= Converter(elementToConvert, settings, inConversionScope, services, commonState, state)
private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null)
@@ -98,7 +96,7 @@ class Converter private constructor(
{ usageProcessings ->
unfoldDeferredElements(usageProcessings)
val builder = CodeBuilder(elementToConvert, docCommentConverter)
val builder = CodeBuilder(elementToConvert, services.docCommentConverter)
builder.append(element)
Result(builder.resultText, builder.importsToAdd)
},
@@ -322,14 +320,14 @@ class Converter private constructor(
addUsageProcessing(FieldToPropertyProcessing(field, correction?.name ?: field.getName()!!, propertyType.isNullable))
return Property(name,
annotations,
modifiers,
propertyType,
deferredElement { codeConverter -> codeConverter.convertExpression(field.getInitializer(), field.getType()) },
isVal,
typeToDeclare != null,
shouldGenerateDefaultInitializer(referenceSearcher, field),
if (correction != null) correction.setterAccess else modifiers.accessModifier()
annotations,
modifiers,
propertyType,
deferredElement { codeConverter -> codeConverter.convertExpression(field.getInitializer(), field.getType()) },
isVal,
typeToDeclare != null,
shouldGenerateDefaultInitializer(referenceSearcher, field),
if (correction != null) correction.setterAccess else modifiers.accessModifier()
).assignPrototype(field)
}
}
@@ -16,18 +16,10 @@
package org.jetbrains.kotlin.j2k
import com.intellij.ide.highlighter.HtmlFileType
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
import com.intellij.psi.JavaDocTokenType
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.javadoc.PsiDocTag
import com.intellij.psi.javadoc.PsiDocToken
import com.intellij.psi.javadoc.PsiInlineDocTag
import com.intellij.psi.xml.XmlFile
import com.intellij.psi.xml.XmlTag
import com.intellij.psi.xml.XmlText
import com.intellij.psi.xml.XmlTokenType
import java.util.Stack
interface DocCommentConverter {
fun convertDocComment(docComment: PsiDocComment): String
@@ -37,193 +29,10 @@ object EmptyDocCommentConverter: DocCommentConverter {
override fun convertDocComment(docComment: PsiDocComment) = docComment.text
}
object IdeaDocCommentConverter : DocCommentConverter {
override fun convertDocComment(docComment: PsiDocComment): String {
val html = StringBuilder {
appendJavadocElements(docComment.getDescriptionElements())
tagsLoop@
for (tag in docComment.getTags()) {
when (tag.getName()) {
"deprecated" -> continue@tagsLoop
"see" -> append("@see ${convertJavadocLink(tag.content())}\n")
else -> appendJavadocElements(tag.getChildren()).append("\n")
}
}
}.toString()
if (html.trim().isEmpty() && docComment.findTagByName("deprecated") != null) {
// @deprecated was the only content of the doc comment; we can drop the comment
return ""
}
val htmlFile = PsiFileFactory.getInstance(docComment.getProject()).createFileFromText(
"javadoc.html", HtmlFileType.INSTANCE, html)
val htmlToMarkdownConverter = HtmlToMarkdownConverter()
htmlFile.accept(htmlToMarkdownConverter)
return htmlToMarkdownConverter.result
}
private fun StringBuilder.appendJavadocElements(elements: Array<PsiElement>): StringBuilder {
elements.forEach {
if (it is PsiInlineDocTag) {
append(convertInlineDocTag(it))
}
else {
append(it.getText())
}
}
return this
}
private fun convertInlineDocTag(tag: PsiInlineDocTag) = when(tag.getName()) {
"code", "literal" -> {
val text = tag.getDataElements().map { it.getText() }.join("")
val escaped = StringUtil.escapeXml(text.trimStart())
if (tag.getName() == "code") "<code>$escaped</code>" else escaped
}
"link", "linkplain" -> {
val valueElement = tag.linkElement()
val labelText = tag.getDataElements().firstOrNull { it is PsiDocToken }?.getText() ?: ""
val kdocLink = convertJavadocLink(valueElement?.getText())
val linkText = if (labelText.isEmpty()) kdocLink else StringUtil.escapeXml(labelText)
"<a docref=\"$kdocLink\">$linkText</a>"
}
else -> tag.getText()
}
private fun convertJavadocLink(link: String?): String =
if (link != null) link.substringBefore('(').replace('#', '.') else ""
private fun PsiDocTag.linkElement(): PsiElement? =
getValueElement() ?: getDataElements().firstOrNull { it !is PsiWhiteSpace }
private class HtmlToMarkdownConverter() : XmlRecursiveElementVisitor() {
private enum class ListType { Ordered, Unordered; }
data class MarkdownSpan(val prefix: String, val suffix: String) {
companion object {
val Empty = MarkdownSpan("", "")
fun wrap(text: String) = MarkdownSpan(text, text)
fun prefix(text: String) = MarkdownSpan(text, "")
}
}
val result: String
get() = markdownBuilder.toString()
private val markdownBuilder = StringBuilder("/**")
private var afterLineBreak = false
private var whitespaceIsPartOfText = true
private var currentListType = ListType.Unordered
override fun visitWhiteSpace(space: PsiWhiteSpace) {
super.visitWhiteSpace(space)
if (whitespaceIsPartOfText) {
appendPendingText()
markdownBuilder.append(space.getText())
if (space.textContains('\n')) {
afterLineBreak = true
}
}
}
override fun visitElement(element: PsiElement) {
super.visitElement(element)
val tokenType = element.getNode().getElementType()
if (tokenType == XmlTokenType.XML_DATA_CHARACTERS || tokenType == XmlTokenType.XML_CHAR_ENTITY_REF) {
appendPendingText()
markdownBuilder.append(element.getText())
}
}
override fun visitXmlTag(tag: XmlTag) {
withWhitespaceAsPartOfText(false) {
val oldListType = currentListType
val atLineStart = afterLineBreak
appendPendingText()
val (openingMarkdown, closingMarkdown) = getMarkdownForTag(tag, atLineStart)
markdownBuilder.append(openingMarkdown)
super.visitXmlTag(tag)
markdownBuilder.append(closingMarkdown)
currentListType = oldListType
}
}
override fun visitXmlText(text: XmlText) {
withWhitespaceAsPartOfText(true) {
super.visitXmlText(text)
}
}
private inline fun withWhitespaceAsPartOfText(newValue: Boolean, block: () -> Unit) {
val oldValue = whitespaceIsPartOfText
whitespaceIsPartOfText = newValue
try {
block()
}
finally {
whitespaceIsPartOfText = oldValue
}
}
private fun getMarkdownForTag(tag: XmlTag, atLineStart: Boolean): MarkdownSpan = when(tag.getName()) {
"b", "strong" -> MarkdownSpan.wrap("**")
"p" -> if (atLineStart) MarkdownSpan.prefix("\n * ") else MarkdownSpan.prefix("\n *\n *")
"i", "em" -> MarkdownSpan.wrap("*")
"s", "del" -> MarkdownSpan.wrap("~~")
"code" -> MarkdownSpan.wrap("`")
"a" -> {
if (tag.getAttributeValue("docref") != null) {
val docRef = tag.getAttributeValue("docref")
val innerText = tag.getValue().getText()
if (docRef == innerText) MarkdownSpan("[", "]") else MarkdownSpan("[", "][$docRef]")
}
else {
MarkdownSpan("[", "](${tag.getAttributeValue("href")})")
}
}
"ul" -> { currentListType = ListType.Unordered; MarkdownSpan.Empty }
"ol" -> { currentListType = ListType.Ordered; MarkdownSpan.Empty }
"li" -> if (currentListType == ListType.Unordered) MarkdownSpan.prefix(" * ") else MarkdownSpan.prefix(" 1. ")
else -> MarkdownSpan.Empty
}
private fun appendPendingText() {
if (afterLineBreak ) {
markdownBuilder.append(" * ")
afterLineBreak = false
}
}
override fun visitXmlFile(file: XmlFile) {
super.visitXmlFile(file)
markdownBuilder.append(" */")
}
}
}
fun PsiDocTag.content(): String =
getChildren()
.dropWhile { it.getNode().getElementType() == JavaDocTokenType.DOC_TAG_NAME }
.dropWhile { it is PsiWhiteSpace }
.filterNot { it.getNode().getElementType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS }
.map { it.getText() }
.join("")
getChildren()
.dropWhile { it.getNode().getElementType() == JavaDocTokenType.DOC_TAG_NAME }
.dropWhile { it is PsiWhiteSpace }
.filterNot { it.getNode().getElementType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS }
.map { it.getText() }
.join("")
@@ -16,10 +16,12 @@
package org.jetbrains.kotlin.j2k
import com.intellij.codeInsight.generation.GenerateEqualsHelper
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.tree.IElementType
import com.intellij.psi.util.MethodSignature
import com.intellij.psi.util.MethodSignatureUtil
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.KotlinLightField
@@ -139,7 +141,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
if (!psiClass.hasModifierProperty(PsiModifier.FINAL)) return false
if (psiClass.isEnum()) return true
val equalsSignature = GenerateEqualsHelper.getEqualsSignature(converter.project, GlobalSearchScope.allScope(converter.project))
val equalsSignature = getEqualsSignature(converter.project, GlobalSearchScope.allScope(converter.project))
val equalsMethod = MethodSignatureUtil.findMethodBySignature(psiClass, equalsSignature, true)
if (equalsMethod != null && equalsMethod.getContainingClass()?.getQualifiedName() != CommonClassNames.JAVA_LANG_OBJECT) return false
@@ -151,6 +153,11 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
private fun getEqualsSignature(project: Project, scope: GlobalSearchScope): MethodSignature {
val javaLangObject = PsiType.getJavaLangObject(PsiManager.getInstance(project), scope)
return MethodSignatureUtil.createMethodSignature("equals", arrayOf<PsiType>(javaLangObject), PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY)
}
private val NON_NULL_OPERAND_OPS = setOf(
JavaTokenType.ANDAND,
JavaTokenType.OROR,
@@ -16,14 +16,11 @@
package org.jetbrains.kotlin.j2k
import com.intellij.ide.util.DelegatingProgressIndicator
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.*
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
@@ -83,9 +80,7 @@ public enum class ParseContext {
public class JavaToKotlinConverter(
private val project: Project,
private val settings: ConverterSettings,
private val referenceSearcher: ReferenceSearcher,
private val resolverForConverter: ResolverForConverter,
private val docCommentConverter: DocCommentConverter
private val services: JavaToKotlinConverterServices
) {
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.j2k.JavaToKotlinConverter")
@@ -143,7 +138,7 @@ public class JavaToKotlinConverter(
val intermediateResults = processor.processItems(0.25, inputElements) { inputElement ->
Converter.create(inputElement, settings, ::inConversionScope, referenceSearcher, resolverForConverter, docCommentConverter, usageProcessingCollector).convert()
Converter.create(inputElement, settings, ::inConversionScope, services, usageProcessingCollector).convert()
}.toArrayList()
val results = processor.processItems(0.25, intermediateResults.withIndex()) { pair ->
@@ -209,7 +204,7 @@ public class JavaToKotlinConverter(
{
val searchJava = processings.any { it.javaCodeProcessor != null }
val searchKotlin = processings.any { it.kotlinCodeProcessor != null }
referenceSearcher.findUsagesForExternalCodeProcessing(psiElement, searchJava, searchKotlin)
services.referenceSearcher.findUsagesForExternalCodeProcessing(psiElement, searchJava, searchKotlin)
.filterNot { inConversionScope(it.getElement()) }
.mapTo(refs) { ReferenceInfo(it, psiElement, it.getElement().getContainingFile(), processings) }
},
@@ -335,4 +330,63 @@ public class JavaToKotlinConverter(
override fun setText2(text: String?) {
}
}
// Copied from com.intellij.ide.util.DelegatingProgressIndicator
private open class DelegatingProgressIndicator : WrappedProgressIndicator, StandardProgressIndicator {
protected val delegate: ProgressIndicator
public constructor(indicator: ProgressIndicator) {
delegate = indicator
}
public constructor() {
val indicator = ProgressManager.getInstance().progressIndicator
delegate = indicator ?: EmptyProgressIndicator()
}
override fun start() = delegate.start()
override fun stop() = delegate.stop()
override fun isRunning() = delegate.isRunning
override fun cancel() = delegate.cancel()
override fun isCanceled() = delegate.isCanceled
override fun setText(text: String?) {
delegate.text = text
}
override fun getText() = delegate.text
override fun setText2(text: String?) {
delegate.text2 = text
}
override fun getText2() = delegate.text2
override fun getFraction() = delegate.fraction
override fun setFraction(fraction: Double) {
delegate.fraction = fraction
}
override fun pushState() = delegate.pushState()
override fun popState() = delegate.popState()
override fun startNonCancelableSection() = delegate.startNonCancelableSection()
override fun finishNonCancelableSection() = delegate.finishNonCancelableSection()
override fun isModal() = delegate.isModal
override fun getModalityState() = delegate.modalityState
override fun setModalityProgress(modalityProgress: ProgressIndicator) {
delegate.setModalityProgress(modalityProgress)
}
override fun isIndeterminate() = delegate.isIndeterminate
override fun setIndeterminate(indeterminate: Boolean) {
delegate.isIndeterminate = indeterminate
}
override fun checkCanceled() = delegate.checkCanceled()
override fun getOriginalProgressIndicator() = delegate
override fun isPopupWasShown() = delegate.isPopupWasShown
override fun isShowing() = delegate.isShowing
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.j2k
interface JavaToKotlinConverterServices {
val referenceSearcher: ReferenceSearcher
val resolverForConverter: ResolverForConverter
val docCommentConverter: DocCommentConverter
}
object EmptyJavaToKotlinServices: JavaToKotlinConverterServices {
override val referenceSearcher: ReferenceSearcher
get() = EmptyReferenceSearcher
override val resolverForConverter: ResolverForConverter
get() = EmptyResolverForConverter
override val docCommentConverter: DocCommentConverter
get() = EmptyDocCommentConverter
}
@@ -45,7 +45,7 @@ public object JavaToKotlinTranslator {
public fun generateKotlinCode(javaCode: String, project: Project): String {
val file = createFile(javaCode, project)
if (file is PsiJavaFile) {
val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyReferenceSearcher, EmptyResolverForConverter, EmptyDocCommentConverter)
val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyJavaToKotlinServices)
return prettify(converter.elementsToKotlin(listOf(file)).results.single()!!.text) //TODO: imports
}
return ""
@@ -16,16 +16,8 @@
package org.jetbrains.kotlin.j2k
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.fileTypes.FileType
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.idea.JetLanguage
import java.util.ArrayList
import com.intellij.psi.util.PsiUtil
public interface ReferenceSearcher {
fun findLocalUsages(element: PsiElement, scope: PsiElement): Collection<PsiReference>
@@ -56,24 +48,4 @@ public object EmptyReferenceSearcher: ReferenceSearcher {
override fun hasOverrides(method: PsiMethod) = false
override fun findUsagesForExternalCodeProcessing(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference>
= throw UnsupportedOperationException()
}
public object IdeaReferenceSearcher: ReferenceSearcher {
override fun findLocalUsages(element: PsiElement, scope: PsiElement) = ReferencesSearch.search(element, LocalSearchScope(scope)).findAll()
override fun hasInheritors(`class`: PsiClass) = ClassInheritorsSearch.search(`class`, false).any()
override fun hasOverrides(method: PsiMethod) = OverridingMethodsSearch.search(method, false).any()
override fun findUsagesForExternalCodeProcessing(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference> {
val fileTypes = ArrayList<FileType>()
if (searchJava) {
fileTypes.add(JavaLanguage.INSTANCE.getAssociatedFileType()!!)
}
if (searchKotlin) {
fileTypes.add(JetLanguage.INSTANCE.getAssociatedFileType()!!)
}
val searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.projectScope(element.getProject()), *fileTypes.toTypedArray())
return ReferencesSearch.search(element, searchScope).findAll()
}
}
}
@@ -375,7 +375,7 @@ class TypeConverter(val converter: Converter) {
override fun fromAnnotations(owner: PsiModifierListOwner): Mutability {
if (owner is KotlinLightElement<*, *>) {
val jetDeclaration = owner.getOrigin() as? JetCallableDeclaration ?: return Mutability.Default
val descriptor = converter.resolverForConverter.resolveToDescriptor(jetDeclaration) as? CallableDescriptor ?: return Mutability.Default
val descriptor = converter.services.resolverForConverter.resolveToDescriptor(jetDeclaration) as? CallableDescriptor ?: return Mutability.Default
val type = descriptor.getReturnType() ?: return Mutability.Default
val classDescriptor = TypeUtils.getClassDescriptor(type) ?: return Mutability.Default
return if (DescriptorUtils.getFqName(classDescriptor).asString() in mutableKotlinClasses)
@@ -21,6 +21,7 @@ import com.intellij.psi.PsiFile
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.PsiManager
import com.intellij.testFramework.LightPlatformTestCase
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
@@ -54,7 +55,7 @@ public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaT
assert(psiFile is PsiJavaFile || psiFile is JetFile)
}
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, IdeaResolverForConverter, EmptyDocCommentConverter)
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices)
val (results, externalCodeProcessor) = converter.filesToKotlin(psiFilesToConvert, J2kPostProcessor(formatCode = true))
val process = externalCodeProcessor?.prepareWriteOperation(EmptyProgressIndicator())
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.test.util.trimIndent
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor
import com.intellij.psi.PsiJavaFile
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
import org.jetbrains.kotlin.idea.test.dumpTextWithErrors
@@ -107,8 +108,7 @@ public abstract class AbstractJavaToKotlinConverterSingleFileTest : AbstractJava
private fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String {
val file = createJavaFile(text)
val converter = JavaToKotlinConverter(project, settings,
IdeaReferenceSearcher, IdeaResolverForConverter, IdeaDocCommentConverter)
val converter = JavaToKotlinConverter(project, settings, IdeaJavaToKotlinServices)
return converter.filesToKotlin(listOf(file), J2kPostProcessor(formatCode = true)).results.single()
}