Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
YiiGuxing committed Jan 8, 2020
2 parents 5aa40b6 + 2cb30bd commit eb3e6b1
Show file tree
Hide file tree
Showing 26 changed files with 545 additions and 71 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log

## [v2.7.0](https://github.com/YiiGuxing/TranslationPlugin/tree/v2.7.0) (2020-01-08)

- 新增对Go, Dart, Python, C, C++, Objective-C/C++语言的文档注释翻译支持
- 支持列选择模式的翻译

## [v2.6.2](https://github.com/YiiGuxing/TranslationPlugin/tree/v2.6.2) (2019-12-16)

- 修复了导致插件无法被初始化的致命错误
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,11 @@ FAQ

更新日志
--------
## [v2.6.2](https://github.com/YiiGuxing/TranslationPlugin/tree/v2.6.2) (2019-12-16)

- 修复了导致插件无法被初始化的致命错误
## [v2.7.0](https://github.com/YiiGuxing/TranslationPlugin/tree/v2.7.0) (2020-01-08)

- 新增对Go, Dart, Python, C, C++, Objective-C/C++语言的文档注释翻译支持
- 支持列选择模式的翻译

[完整的更新历史记录](./CHANGELOG.md)

Expand Down
15 changes: 8 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ buildscript {

plugins {
// https://github.com/JetBrains/gradle-intellij-plugin
id "org.jetbrains.intellij" version "0.4.14"
id "org.jetbrains.intellij" version "0.4.15"
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
}

Expand All @@ -26,7 +26,7 @@ intellij {
pluginName = 'TranslationPlugin'
downloadSources = Boolean.valueOf(sources)
sandboxDirectory 'sandbox'
plugins "org.jetbrains.kotlin:1.2.40-release-IJ2017.1-1"
plugins "org.jetbrains.kotlin:1.2.40-release-IJ2017.1-1", "Pythonid:2017.1.171.3780.116", "Dart:171.4006", "org.jetbrains.plugins.go:171.4694.35"
}

patchPluginXml {
Expand All @@ -35,12 +35,13 @@ patchPluginXml {
}

dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
testImplementation group: 'junit', name: 'junit', version: '4.12'
compileOnly fileTree(dir: 'libs', include: ['*.jar'])

compile 'org.jsoup:jsoup:1.12.1'
compile 'org.apache.commons:commons-dbcp2:2.6.0'
compile 'commons-dbutils:commons-dbutils:1.7'
compile('com.googlecode.soundlibs:mp3spi:1.9.5.4') {
implementation 'org.jsoup:jsoup:1.12.1'
implementation 'org.apache.commons:commons-dbcp2:2.6.0'
implementation 'commons-dbutils:commons-dbutils:1.7'
implementation('com.googlecode.soundlibs:mp3spi:1.9.5.4') {
exclude module: 'junit'
}
}
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# suppress inspection "UnusedProperty" for whole file
version=2.6.2
version=2.7.0
buildNumber=
ideaVersion=2017.1
ideaVersion=IU-2017.1
javaVersion=1.8
javaTargetVersion=1.8
kotlinLanguageVersion=1.3
Expand Down
Binary file added libs/clion.jar
Binary file not shown.
Binary file added libs/rider.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,46 @@ open class TranslateAction(checkSelection: Boolean = false) : AutoSelectAction(c

override fun onActionPerformed(event: AnActionEvent, editor: Editor, selectionRange: TextRange) {
val project = editor.project ?: return
editor.document.getText(selectionRange).processBeforeTranslate()?.let { text ->
val highlightManager = HighlightManager.getInstance(project)
val highlighters = ArrayList<RangeHighlighter>()
val startLine = editor.offsetToVisualPosition(selectionRange.startOffset).line
val endLine = editor.offsetToVisualPosition(selectionRange.endOffset).line
val highlightAttributes = if (startLine == endLine) HIGHLIGHT_ATTRIBUTES else MULTILINE_HIGHLIGHT_ATTRIBUTES
val selectionModel = editor.selectionModel
val isColumnSelectionMode = editor.caretModel.caretCount > 1

highlightManager.addRangeHighlight(
editor, selectionRange.startOffset, selectionRange.endOffset, highlightAttributes, true, highlighters
)
val text: String
val starts: IntArray
val ends: IntArray
if (selectionModel.hasSelection(true) && isColumnSelectionMode) {
starts = selectionModel.blockSelectionStarts
ends = selectionModel.blockSelectionEnds
text = selectionModel.getSelectedText(true)?.processBeforeTranslate() ?: return
} else {
starts = intArrayOf(selectionRange.startOffset)
ends = intArrayOf(selectionRange.endOffset)
text = editor.document.getText(selectionRange).processBeforeTranslate() ?: return
}

val startLine by lazy { editor.offsetToVisualPosition(selectionRange.startOffset).line }
val endLine by lazy { editor.offsetToVisualPosition(selectionRange.endOffset).line }
val highlightAttributes = if (starts.size > 1 || startLine == endLine) {
HIGHLIGHT_ATTRIBUTES
} else {
MULTILINE_HIGHLIGHT_ATTRIBUTES
}

val highlightManager = HighlightManager.getInstance(project)
val highlighters = ArrayList<RangeHighlighter>()
for (i in starts.indices) {
highlightManager.addRangeHighlight(editor, starts[i], ends[i], highlightAttributes, true, highlighters)
}

val caretRangeMarker = editor.createCaretRangeMarker(selectionRange)
val tracker = BalloonPositionTracker(editor, caretRangeMarker)
val balloon = TranslationUIManager.showBalloon(editor, text, tracker, Balloon.Position.below)
val caretRangeMarker = editor.createCaretRangeMarker(selectionRange)
val tracker = BalloonPositionTracker(editor, caretRangeMarker)
val balloon = TranslationUIManager.showBalloon(editor, text, tracker, Balloon.Position.below)

if (highlighters.isNotEmpty()) {
Disposer.register(balloon, Disposable {
for (highlighter in highlighters) {
highlightManager.removeSegmentHighlighter(editor, highlighter)
}
})
}
if (highlighters.isNotEmpty()) {
Disposer.register(balloon, Disposable {
for (highlighter in highlighters) {
highlightManager.removeSegmentHighlighter(editor, highlighter)
}
})
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.DimensionService
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiDocCommentBase
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.presentation.java.SymbolPresentationUtil
Expand Down Expand Up @@ -51,7 +50,10 @@ class TranslateDocumentationAction : PsiElementTranslateAction() {
override fun doTranslate(editor: Editor, element: PsiElement, dataContext: DataContext) {
val editorRef = WeakReference(editor)
val project = editor.project
val docCommentOwner = (if (element is PsiDocCommentBase) element.owner else element.parent) ?: return
val docCommentOwner = DocumentationElementProvider
.forLanguage(element.language)
.getDocumentationOwner(element)
?: return
val provider = docCommentOwner.documentationProvider ?: return

executeOnPooledThread {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package cn.yiiguxing.plugin.translate.provider

import cn.yiiguxing.plugin.translate.util.IdeVersion
import cn.yiiguxing.plugin.translate.util.SKIP_WHITE_SPACE
import cn.yiiguxing.plugin.translate.util.elementType
import cn.yiiguxing.plugin.translate.util.getNextSiblingSkippingCondition
import com.intellij.lang.documentation.DocumentationProvider
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.jetbrains.rider.ideaInterop.fileTypes.csharp.lexer.CSharpTokenType
import com.jetbrains.rider.ideaInterop.fileTypes.csharp.psi.CSharpDummyNode
import com.jetbrains.rider.ideaInterop.fileTypes.csharp.psi.impl.CSharpDummyDeclaration

/**
* 由于`C#`的[DocumentationProvider]的机制与常规的不同,并隐藏了文档生成的细节,通过
* `C#`的[DocumentationProvider]并不能正确地获取到生成的文档,因此该类并不能实现最终
* 的目的。
* 另见:`com.jetbrains.rdclient.quickDoc.FrontendDocumentationProvider`
*/
class CSharpDocumentationElementProvider : DocumentationElementProvider {

override fun findDocumentationElementAt(psiFile: PsiFile, offset: Int): PsiElement? {
// 2019版本之前没有C#的PSI
if (!IdeVersion.isIde2019OrNewer) {
return null
}

val element = psiFile.findElementAt(offset)
return (element as? PsiComment)?.takeIf { it.owner != null }
}

override fun getDocumentationOwner(documentationElement: PsiElement): PsiElement? {
return (documentationElement as? PsiComment)?.owner
}

private companion object {
val SKIP_WHITE_SPACE_AND_COMMENT: (PsiElement) -> Boolean = { it is PsiWhiteSpace || it is PsiComment }

val PsiComment.isDocComment: Boolean
get() = when (elementType) {
CSharpTokenType.END_OF_LINE_COMMENT -> text.startsWith("///")
CSharpTokenType.C_STYLE_COMMENT -> text.startsWith("/**")
else -> false
}

val PsiComment.owner: PsiElement?
get() {
if (!isDocComment) {
return null
}

val element = getNextSiblingSkippingCondition(SKIP_WHITE_SPACE_AND_COMMENT) as? CSharpDummyNode
?: return null
val nextNode = element.getNextSiblingSkippingCondition(SKIP_WHITE_SPACE)
if (nextNode is CSharpDummyDeclaration) {
return nextNode.declaredElement
}

return element.identifier
}

val CSharpDummyNode.identifier: PsiElement?
get() {
var brackets = 0
var element = firstChild
while (element != null) {
when (element.elementType) {
CSharpTokenType.LBRACKET, CSharpTokenType.LPARENTH -> brackets++
CSharpTokenType.RBRACKET, CSharpTokenType.RPARENTH -> brackets--
CSharpTokenType.IDENTIFIER -> {
if (brackets == 0) {
return element
}
}
}

element = element.nextSibling
}

return null
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cn.yiiguxing.plugin.translate.provider

import cn.yiiguxing.plugin.translate.util.elementType
import cn.yiiguxing.plugin.translate.util.findChildOfType
import cn.yiiguxing.plugin.translate.util.getNextSiblingSkippingCondition
import cn.yiiguxing.plugin.translate.util.getPrevSiblingSkippingCondition
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.jetbrains.lang.dart.DartTokenTypesSets
import com.jetbrains.lang.dart.psi.DartComponent
import com.jetbrains.lang.dart.psi.DartComponentName
import com.jetbrains.lang.dart.psi.DartDocComment
import com.jetbrains.lang.dart.psi.DartVarDeclarationList

class DartDocumentationElementProvider : DocumentationElementProvider {

override fun findDocumentationElementAt(psiFile: PsiFile, offset: Int): PsiElement? {
val element = psiFile.findElementAt(offset) ?: return null
val type = element.elementType
val docElement = when {
element is PsiComment && type == DartTokenTypesSets.SINGLE_LINE_DOC_COMMENT -> element
element.parent is DartDocComment -> element.parent
else -> return null
} as PsiComment

return docElement.takeIf { it.owner != null }
}

override fun getDocumentationOwner(documentationElement: PsiElement): PsiElement? {
return (documentationElement as? PsiComment)?.owner
}

companion object {
private val SKIPPING_CONDITION: (PsiElement) -> Boolean = {
it is PsiWhiteSpace || (it is PsiComment && it !is DartDocComment)
}

private val DART_COMPONENT_NAME_CONDITION: (PsiElement) -> Boolean = { it is DartComponentName }

/**
* 向上检查是否存在多行文档注释
*/
private fun PsiComment.checkPreviousComments(): Boolean {
return getPrevSiblingSkippingCondition(SKIPPING_CONDITION) !is DartDocComment
}

/**
* 找到注释目标
*/
private val PsiComment.owner: PsiElement?
get() {
// 文档注释类型中,多行注释有最高的优先级。
// 如果当前注释不是多行注释(DartDocComment),则向上寻找,如果上方有多行注释,则说明当前的注释是无效的。
// 且,最下方的多行文档注释有最高的优先级,向下寻找时如遇文档注释,则当前的注释也是无效的。

if (this !is DartDocComment && !checkPreviousComments()) {
return null
}

return when (val sibling = getNextSiblingSkippingCondition(SKIPPING_CONDITION)) {
is DartComponent -> sibling.componentName
is DartVarDeclarationList -> sibling.findChildOfType(DartComponent::class.java)?.componentName
else -> null
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ interface DocumentationElementProvider {
*/
fun findDocumentationElementAt(psiFile: PsiFile, offset: Int): PsiElement?

/**
* Returns the owner of the specified [documentationElement].
*/
fun getDocumentationOwner(documentationElement: PsiElement): PsiElement? {
return if (documentationElement is PsiDocCommentBase) {
documentationElement.owner
} else {
documentationElement.parent
}
}

private object DefaultDocumentationElementProvider : DocumentationElementProvider {

override fun findDocumentationElementAt(psiFile: PsiFile, offset: Int): PsiElement? {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package cn.yiiguxing.plugin.translate.provider

import cn.yiiguxing.plugin.translate.util.findChildOfType
import cn.yiiguxing.plugin.translate.util.getNextSiblingSkippingCondition
import com.goide.psi.*
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace

class GoDocumentationElementProvider : DocumentationElementProvider {

override fun findDocumentationElementAt(psiFile: PsiFile, offset: Int): PsiElement? {
val element = psiFile.findElementAt(offset)
return (element as? PsiComment)?.takeIf { it.owner != null }
}

override fun getDocumentationOwner(documentationElement: PsiElement): PsiElement? {
return (documentationElement as? PsiComment)?.owner
}

private companion object {
val SKIP_WHITE_SPACE_AND_COMMENT: (PsiElement) -> Boolean = {
(it is PsiWhiteSpace && it.text.count { char -> char == '\n' } <= 1) || it is PsiComment
}

val GoTypeDeclaration.innerOwner: PsiElement?
get() = findChildOfType(GoTypeSpec::class.java)

val GoVarDeclaration.innerOwner: PsiElement?
get() = findChildOfType(GoVarDefinition::class.java, true)

val PsiComment.owner: PsiElement?
get() {
val element = getNextSiblingSkippingCondition(SKIP_WHITE_SPACE_AND_COMMENT)

println(element)
println(element is GoMethodSpec)
println(element?.javaClass?.name)

return when (element) {
is GoPackageClause -> element.takeIf { parent is GoFile }
is GoTypeDeclaration -> element.innerOwner
is GoMethodDeclaration -> element
is GoMethodSpec -> element
is GoVarDeclaration -> element.innerOwner
else -> null
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.kdoc.psi.api.KDoc

class KotlinDocumentationElementProvider : DocumentationElementProvider {

override fun findDocumentationElementAt(psiFile: PsiFile, offset: Int): PsiElement? {
return psiFile.findElementOfTypeAt(offset, KDoc::class.java)
}

}
Loading

0 comments on commit eb3e6b1

Please sign in to comment.