mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
IJ-CR-123098 [java-console] IDEA-331307 Provide navigation for logs for jvm languages
- resolve only on links - reuse `codePointAt` GitOrigin-RevId: 207034ef37fe94d4cbb6bbe5e3f68c7ce8c0cbdd
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d636f37d4e
commit
c6e9c60193
@@ -183,6 +183,8 @@ jvm.inspection.logging.condition.disagrees.with.log.statement.problem.descriptor
|
|||||||
jvm.inspection.logging.condition.disagrees.with.log.statement.fix.name=Change level of the {0, choice, 0#condition|1#call}
|
jvm.inspection.logging.condition.disagrees.with.log.statement.fix.name=Change level of the {0, choice, 0#condition|1#call}
|
||||||
jvm.inspection.logging.condition.disagrees.with.log.statement.fix.family.name=Change log level
|
jvm.inspection.logging.condition.disagrees.with.log.statement.fix.family.name=Change log level
|
||||||
|
|
||||||
|
action.find.similar.stack.call.methods.not.found=Similar classes are not found
|
||||||
|
|
||||||
jvm.inspection.test.failed.line.display.name=Failed line in test
|
jvm.inspection.test.failed.line.display.name=Failed line in test
|
||||||
|
|
||||||
jvm.inspections.thread.run.display.name=Call to 'Thread.run()'
|
jvm.inspections.thread.run.display.name=Call to 'Thread.run()'
|
||||||
|
|||||||
@@ -3,12 +3,9 @@ package com.intellij.analysis.customization.console
|
|||||||
|
|
||||||
import com.intellij.execution.filters.Filter
|
import com.intellij.execution.filters.Filter
|
||||||
import com.intellij.execution.filters.HyperlinkInfo
|
import com.intellij.execution.filters.HyperlinkInfo
|
||||||
import com.intellij.execution.filters.HyperlinkInfoFactory
|
|
||||||
import com.intellij.openapi.editor.markup.EffectType
|
import com.intellij.openapi.editor.markup.EffectType
|
||||||
import com.intellij.openapi.editor.markup.TextAttributes
|
import com.intellij.openapi.editor.markup.TextAttributes
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.util.ui.NamedColorUtil
|
import com.intellij.util.ui.NamedColorUtil
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
@@ -16,21 +13,21 @@ import org.jetbrains.annotations.NonNls
|
|||||||
internal data class ProbableClassName(val from: Int,
|
internal data class ProbableClassName(val from: Int,
|
||||||
val to: Int,
|
val to: Int,
|
||||||
val fullLine: String,
|
val fullLine: String,
|
||||||
val shortClassName: String,
|
val fullClassName: String)
|
||||||
val packageName: String,
|
|
||||||
val virtualFiles: List<VirtualFile>)
|
|
||||||
|
|
||||||
private val EXCEPTION_IN_THREAD: @NonNls String = "Exception in thread \""
|
private const val EXCEPTION_IN_THREAD: @NonNls String = "Exception in thread \""
|
||||||
private val CAUSED_BY: @NonNls String = "Caused by: "
|
private const val CAUSED_BY: @NonNls String = "Caused by: "
|
||||||
private val AT: @NonNls String = "\tat "
|
private const val AT: @NonNls String = "\tat "
|
||||||
|
|
||||||
|
private const val POINT_CODE = '.'.code
|
||||||
|
|
||||||
internal class ClassFinderFilter(private val myProject: Project, myScope: GlobalSearchScope) : Filter {
|
internal class ClassFinderFilter(private val myProject: Project, myScope: GlobalSearchScope) : Filter {
|
||||||
private val cache = ClassInfoCache(myProject, myScope)
|
private val cache = ClassInfoResolver(myProject, myScope)
|
||||||
|
|
||||||
override fun applyFilter(line: String, entireLength: Int): Filter.Result? {
|
override fun applyFilter(line: String, entireLength: Int): Filter.Result? {
|
||||||
val textStartOffset = entireLength - line.length
|
val textStartOffset = entireLength - line.length
|
||||||
|
|
||||||
val expectedClasses = findProbableClasses(line, cache)
|
val expectedClasses = findProbableClasses(line)
|
||||||
val results: MutableList<Filter.Result> = ArrayList()
|
val results: MutableList<Filter.Result> = ArrayList()
|
||||||
for (probableClass in expectedClasses) {
|
for (probableClass in expectedClasses) {
|
||||||
val attributes = hyperLinkAttributes()
|
val attributes = hyperLinkAttributes()
|
||||||
@@ -57,40 +54,53 @@ internal class ClassFinderFilter(private val myProject: Project, myScope: Global
|
|||||||
|
|
||||||
|
|
||||||
private fun getHyperLink(probableClassName: ProbableClassName): HyperlinkInfo {
|
private fun getHyperLink(probableClassName: ProbableClassName): HyperlinkInfo {
|
||||||
return HyperlinkInfoFactory.getInstance()
|
return OnFlyMultipleFilesHyperlinkInfo(cache, probableClassName,0, myProject, LogFinderHyperlinkHandler(probableClassName))
|
||||||
.createMultipleFilesHyperlinkInfo(probableClassName.virtualFiles, 0, myProject, LogFinderHyperlinkHandler(probableClassName))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun findProbableClasses(line: String, cache: ClassInfoCache): List<ProbableClassName> {
|
private fun findProbableClasses(line: String): List<ProbableClassName> {
|
||||||
if (line.startsWith(EXCEPTION_IN_THREAD) || line.startsWith(CAUSED_BY) || line.startsWith(AT)) {
|
if (line.isBlank() || line.startsWith(EXCEPTION_IN_THREAD) || line.startsWith(CAUSED_BY) || line.startsWith(AT)) {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
val result = mutableListOf<ProbableClassName>()
|
val result = mutableListOf<ProbableClassName>()
|
||||||
var start = -1
|
var start = -1
|
||||||
var pointCount = 0
|
var pointCount = 0
|
||||||
for (i in line.indices) {
|
var i = 0
|
||||||
val c = line.codePointAt(i)
|
var first = true
|
||||||
if (start == -1 && isJavaIdentifierStart(c)) {
|
while (true) {
|
||||||
|
if (!first) {
|
||||||
|
val previousPoint = line.codePointAt(i)
|
||||||
|
i += Character.charCount(previousPoint)
|
||||||
|
if (i >= line.length) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
first = false
|
||||||
|
}
|
||||||
|
|
||||||
|
val point = line.codePointAt(i)
|
||||||
|
if (start == -1 && isJavaIdentifierStart(point)) {
|
||||||
start = i
|
start = i
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (start != -1 && c == '.'.code) {
|
if (start != -1 && point == POINT_CODE) {
|
||||||
pointCount++
|
pointCount++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (start != -1 &&
|
if (start != -1 &&
|
||||||
((line[i - 1] == '.' && isJavaIdentifierStart(c)) ||
|
((line.codePointAt(i - 1) == POINT_CODE && isJavaIdentifierStart(point)) ||
|
||||||
(line[i - 1] != '.' && isJavaIdentifierPart(c)))) {
|
(line.codePointAt(i - 1) != POINT_CODE && isJavaIdentifierPart(point)))) {
|
||||||
if (i == line.lastIndex && pointCount >= 2) {
|
val charCount = Character.charCount(point)
|
||||||
addProbableClass(line, start, i + 1, cache, result)
|
if (i + charCount >= line.length && pointCount >= 2) {
|
||||||
|
addProbableClass(line, start, line.length, result)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pointCount >= 2) {
|
if (pointCount >= 2) {
|
||||||
addProbableClass(line, start, i, cache, result)
|
addProbableClass(line, start, i, result)
|
||||||
}
|
}
|
||||||
pointCount = 0
|
pointCount = 0
|
||||||
start = -1
|
start = -1
|
||||||
@@ -111,7 +121,6 @@ internal class ClassFinderFilter(private val myProject: Project, myScope: Global
|
|||||||
private fun addProbableClass(line: String,
|
private fun addProbableClass(line: String,
|
||||||
startInclusive: Int,
|
startInclusive: Int,
|
||||||
endExclusive: Int,
|
endExclusive: Int,
|
||||||
cache: ClassInfoCache,
|
|
||||||
result: MutableList<ProbableClassName>) {
|
result: MutableList<ProbableClassName>) {
|
||||||
var actualEndExclusive = endExclusive
|
var actualEndExclusive = endExclusive
|
||||||
if (actualEndExclusive > 0 && line[actualEndExclusive - 1] == '.') {
|
if (actualEndExclusive > 0 && line[actualEndExclusive - 1] == '.') {
|
||||||
@@ -119,15 +128,9 @@ internal class ClassFinderFilter(private val myProject: Project, myScope: Global
|
|||||||
}
|
}
|
||||||
val fullClassName = line.substring(startInclusive, actualEndExclusive)
|
val fullClassName = line.substring(startInclusive, actualEndExclusive)
|
||||||
if (canBeShortenedFullyQualifiedClassName(fullClassName) && isJavaStyle(fullClassName)) {
|
if (canBeShortenedFullyQualifiedClassName(fullClassName) && isJavaStyle(fullClassName)) {
|
||||||
val packageName = StringUtil.getPackageName(fullClassName)
|
val probableClassName = ProbableClassName(startInclusive + fullClassName.lastIndexOf(".") + 1,
|
||||||
val className = fullClassName.substring(packageName.length + 1)
|
startInclusive + fullClassName.length, line, fullClassName)
|
||||||
val resolvedClasses = cache.resolveClasses(className, packageName)
|
result.add(probableClassName)
|
||||||
if (resolvedClasses.classes.isNotEmpty()) {
|
|
||||||
val probableClassName = ProbableClassName(startInclusive + fullClassName.lastIndexOf(".") + 1,
|
|
||||||
startInclusive + fullClassName.length,
|
|
||||||
line, className, packageName, resolvedClasses.classes.values.toList())
|
|
||||||
result.add(probableClassName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,8 @@ import com.intellij.psi.PsiClass
|
|||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.search.PsiShortNamesCache
|
import com.intellij.psi.search.PsiShortNamesCache
|
||||||
import com.intellij.util.containers.ContainerUtil
|
|
||||||
|
|
||||||
class ClassInfoCache(val project: Project, private val mySearchScope: GlobalSearchScope) {
|
class ClassInfoResolver(val project: Project, private val mySearchScope: GlobalSearchScope) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun findClasses(project: Project,
|
private fun findClasses(project: Project,
|
||||||
@@ -56,15 +55,7 @@ class ClassInfoCache(val project: Project, private val mySearchScope: GlobalSear
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val myCache = ContainerUtil.createConcurrentSoftValueMap<String, ClassResolveInfo>()
|
|
||||||
|
|
||||||
fun resolveClasses(className: String, packageName: String): ClassResolveInfo {
|
fun resolveClasses(className: String, packageName: String): ClassResolveInfo {
|
||||||
val key = "$packageName.$className"
|
|
||||||
val cached = myCache[key]
|
|
||||||
if (cached != null && cached.isValid) {
|
|
||||||
return cached
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDumb(project)) {
|
if (isDumb(project)) {
|
||||||
return ClassResolveInfo.EMPTY
|
return ClassResolveInfo.EMPTY
|
||||||
}
|
}
|
||||||
@@ -77,7 +68,6 @@ class ClassInfoCache(val project: Project, private val mySearchScope: GlobalSear
|
|||||||
.toMap()
|
.toMap()
|
||||||
|
|
||||||
val result = if (mapWithClasses.isEmpty()) ClassResolveInfo.EMPTY else ClassResolveInfo(mapWithClasses)
|
val result = if (mapWithClasses.isEmpty()) ClassResolveInfo.EMPTY else ClassResolveInfo(mapWithClasses)
|
||||||
myCache[key] = result
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ import org.jetbrains.uast.*
|
|||||||
|
|
||||||
internal class LogFinderHyperlinkHandler(private val probableClassName: ProbableClassName) : HyperlinkInfoFactory.HyperlinkHandler {
|
internal class LogFinderHyperlinkHandler(private val probableClassName: ProbableClassName) : HyperlinkInfoFactory.HyperlinkHandler {
|
||||||
override fun onLinkFollowed(project: Project, file: VirtualFile, targetEditor: Editor, originalEditor: Editor?) {
|
override fun onLinkFollowed(project: Project, file: VirtualFile, targetEditor: Editor, originalEditor: Editor?) {
|
||||||
LogConsoleLogHandlerCollectors.logHandleClass(project, probableClassName.virtualFiles.size)
|
LogConsoleLogHandlerCollectors.logHandleClass(project, 1)
|
||||||
|
|
||||||
val psiFile = PsiManager.getInstance(project).findFile(file)
|
val psiFile = PsiManager.getInstance(project).findFile(file)
|
||||||
if (psiFile == null) return
|
if (psiFile == null) return
|
||||||
@@ -102,13 +102,14 @@ internal class LogFinderHyperlinkHandler(private val probableClassName: Probable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LogVisitor(private val probableClassName: ProbableClassName) : PsiRecursiveElementVisitor() {
|
internal class LogVisitor(private val probableClassName: ProbableClassName) : PsiRecursiveElementVisitor() {
|
||||||
val similarClasses = mutableSetOf<UClass>()
|
val similarClasses = mutableSetOf<UClass>()
|
||||||
val similarCalls = mutableSetOf<UCallExpression>()
|
val similarCalls = mutableSetOf<UCallExpression>()
|
||||||
|
val shortClassName = probableClassName.fullClassName.substringAfterLast('.')
|
||||||
|
|
||||||
override fun visitElement(element: PsiElement) {
|
override fun visitElement(element: PsiElement) {
|
||||||
val uClass = element.toUElementOfType<UClass>()
|
val uClass = element.toUElementOfType<UClass>()
|
||||||
if (uClass != null &&
|
if (uClass != null && shortClassName == uClass.javaPsi.name) {
|
||||||
probableClassName.shortClassName == uClass.javaPsi.name) {
|
|
||||||
similarClasses.add(uClass)
|
similarClasses.add(uClass)
|
||||||
}
|
}
|
||||||
val uCall = element.toUElementOfType<UCallExpression>()
|
val uCall = element.toUElementOfType<UCallExpression>()
|
||||||
@@ -143,10 +144,9 @@ private class LogVisitor(private val probableClassName: ProbableClassName) : Psi
|
|||||||
if (logStringArgument == null) return false
|
if (logStringArgument == null) return false
|
||||||
val calculateValue = LoggingStringPartEvaluator.calculateValue(logStringArgument) ?: return false
|
val calculateValue = LoggingStringPartEvaluator.calculateValue(logStringArgument) ?: return false
|
||||||
val fullLine = probableClassName.fullLine
|
val fullLine = probableClassName.fullLine
|
||||||
val classFullName = probableClassName.packageName + "." + probableClassName.shortClassName
|
var startPoint = probableClassName.fullLine.indexOf(probableClassName.fullClassName)
|
||||||
var startPoint = probableClassName.fullLine.indexOf(classFullName)
|
|
||||||
if (startPoint == -1) return false
|
if (startPoint == -1) return false
|
||||||
startPoint += classFullName.length
|
startPoint += probableClassName.fullClassName.length
|
||||||
if (calculateValue.none { it.isConstant && it.text != null }) return false
|
if (calculateValue.none { it.isConstant && it.text != null }) return false
|
||||||
for (value in calculateValue) {
|
for (value in calculateValue) {
|
||||||
if (value.isConstant && value.text != null) {
|
if (value.isConstant && value.text != null) {
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
|
package com.intellij.analysis.customization.console
|
||||||
|
|
||||||
|
import com.intellij.analysis.JvmAnalysisBundle
|
||||||
|
import com.intellij.codeInsight.hint.HintUtil
|
||||||
|
import com.intellij.execution.filters.HyperlinkInfoFactory.HyperlinkHandler
|
||||||
|
import com.intellij.execution.filters.impl.MultipleFilesHyperlinkInfoBase
|
||||||
|
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.ui.popup.Balloon
|
||||||
|
import com.intellij.openapi.ui.popup.JBPopupFactory
|
||||||
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.ui.awt.RelativePoint
|
||||||
|
import org.jetbrains.annotations.ApiStatus
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
class OnFlyMultipleFilesHyperlinkInfo internal constructor(private val myInfoCache: ClassInfoResolver,
|
||||||
|
private val probableClassName: ProbableClassName,
|
||||||
|
lineNumber: Int,
|
||||||
|
project: Project,
|
||||||
|
action: HyperlinkHandler?) :
|
||||||
|
MultipleFilesHyperlinkInfoBase(lineNumber, project, action) {
|
||||||
|
override fun getFiles(project: Project): List<PsiFile> {
|
||||||
|
val packageName = StringUtil.getPackageName(probableClassName.fullClassName)
|
||||||
|
if (packageName.length == probableClassName.fullClassName.length) return emptyList()
|
||||||
|
val className = probableClassName.fullClassName.substring(packageName.length + 1)
|
||||||
|
val resolvedClasses = myInfoCache.resolveClasses(className, packageName)
|
||||||
|
val currentFiles: MutableList<PsiFile> = ArrayList()
|
||||||
|
for (file in resolvedClasses.classes.values) {
|
||||||
|
if (!file.isValid) continue
|
||||||
|
val psiFile = PsiManager.getInstance(project).findFile(file)
|
||||||
|
if (psiFile != null) {
|
||||||
|
val navigationElement = psiFile.navigationElement // Sources may be downloaded.
|
||||||
|
if (navigationElement is PsiFile) {
|
||||||
|
currentFiles.add(navigationElement)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currentFiles.add(psiFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showNotFound(project: Project, hyperlinkLocationPoint: RelativePoint?) {
|
||||||
|
if (hyperlinkLocationPoint == null) return
|
||||||
|
val message = JvmAnalysisBundle.message("action.find.similar.stack.call.methods.not.found")
|
||||||
|
val label = HintUtil.createWarningLabel(message)
|
||||||
|
JBPopupFactory.getInstance().createBalloonBuilder(label)
|
||||||
|
.setFadeoutTime(4000)
|
||||||
|
.setFillColor(HintUtil.getWarningColor())
|
||||||
|
.createBalloon()
|
||||||
|
.show(RelativePoint(hyperlinkLocationPoint.screenPoint), Balloon.Position.above)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDescriptor(): OpenFileDescriptor? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.intellij.jvm.analysis.internal.testFramework.internal
|
package com.intellij.jvm.analysis.internal.testFramework.internal
|
||||||
|
|
||||||
import com.intellij.analysis.customization.console.ClassLoggingConsoleFilterProvider
|
import com.intellij.analysis.customization.console.ClassLoggingConsoleFilterProvider
|
||||||
|
import com.intellij.analysis.customization.console.OnFlyMultipleFilesHyperlinkInfo
|
||||||
import com.intellij.jvm.analysis.testFramework.LightJvmCodeInsightFixtureTestCase
|
import com.intellij.jvm.analysis.testFramework.LightJvmCodeInsightFixtureTestCase
|
||||||
import com.intellij.openapi.editor.LogicalPosition
|
import com.intellij.openapi.editor.LogicalPosition
|
||||||
import com.intellij.openapi.options.advanced.AdvancedSettings
|
import com.intellij.openapi.options.advanced.AdvancedSettings
|
||||||
@@ -41,7 +42,14 @@ abstract class LogFinderHyperlinkTestBase : LightJvmCodeInsightFixtureTestCase()
|
|||||||
len += logLine.length
|
len += logLine.length
|
||||||
val result = filter.applyFilter(logLine, len)
|
val result = filter.applyFilter(logLine, len)
|
||||||
if (logItem.position == null) {
|
if (logItem.position == null) {
|
||||||
Assert.assertNull(logItem.toString(), result)
|
if (result == null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val hyperlinkInfo = result.firstHyperlinkInfo
|
||||||
|
if (hyperlinkInfo is OnFlyMultipleFilesHyperlinkInfo) {
|
||||||
|
val files = hyperlinkInfo.getFiles(project)
|
||||||
|
Assert.assertTrue(logItem.toString(), files.isEmpty())
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
Assert.assertNotNull(logItem.toString(), result)
|
Assert.assertNotNull(logItem.toString(), result)
|
||||||
|
|||||||
@@ -211,6 +211,47 @@ public final class Slf4JFluent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testUnicode() {
|
||||||
|
LoggingTestUtils.addSlf4J(myFixture)
|
||||||
|
checkColumnFinderJava(
|
||||||
|
fileName = "Slf4JFluent",
|
||||||
|
classText = """
|
||||||
|
package com.example.úsh.čas;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public final class UčenjaУченья {
|
||||||
|
private final static Logger log = LoggerFactory.getLogger(UčenjaУченья.class);
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(2);
|
||||||
|
log1(1);
|
||||||
|
log2(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void log1(int i) {
|
||||||
|
String msg = "test" + i;
|
||||||
|
log.atInfo()
|
||||||
|
.setMessage(msg)
|
||||||
|
.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void log2(int i) {
|
||||||
|
String msg = "log2" + i;
|
||||||
|
log.atInfo()
|
||||||
|
.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
logItems = listOf(
|
||||||
|
LogItem("java.exe", null),
|
||||||
|
LogItem("1", null),
|
||||||
|
LogItem("20:13:25.878 [main] INFO com.example.úsh.čas.UčenjaУченья - test1", LogicalPosition(16, 8)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun testSkipException() {
|
fun testSkipException() {
|
||||||
LoggingTestUtils.addSlf4J(myFixture)
|
LoggingTestUtils.addSlf4J(myFixture)
|
||||||
checkColumnFinderJava(
|
checkColumnFinderJava(
|
||||||
|
|||||||
@@ -15,43 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package com.intellij.execution.filters.impl;
|
package com.intellij.execution.filters.impl;
|
||||||
|
|
||||||
import com.intellij.codeInsight.navigation.PsiTargetNavigator;
|
|
||||||
import com.intellij.execution.ExecutionBundle;
|
|
||||||
import com.intellij.execution.filters.FileHyperlinkInfo;
|
|
||||||
import com.intellij.execution.filters.HyperlinkInfoBase;
|
|
||||||
import com.intellij.execution.filters.HyperlinkInfoFactory;
|
import com.intellij.execution.filters.HyperlinkInfoFactory;
|
||||||
import com.intellij.ide.DataManager;
|
|
||||||
import com.intellij.ide.util.gotoByName.GotoFileCellRenderer;
|
|
||||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext;
|
|
||||||
import com.intellij.openapi.editor.Document;
|
|
||||||
import com.intellij.openapi.editor.Editor;
|
|
||||||
import com.intellij.openapi.editor.ex.EditorEx;
|
|
||||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
|
||||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.openapi.wm.WindowManager;
|
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import com.intellij.psi.PsiManager;
|
import com.intellij.psi.PsiManager;
|
||||||
import com.intellij.ui.awt.RelativePoint;
|
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public final class MultipleFilesHyperlinkInfo extends HyperlinkInfoBase implements FileHyperlinkInfo {
|
public final class MultipleFilesHyperlinkInfo extends MultipleFilesHyperlinkInfoBase {
|
||||||
private final List<? extends VirtualFile> myVirtualFiles;
|
private final List<? extends VirtualFile> myVirtualFiles;
|
||||||
private final int myLineNumber;
|
|
||||||
private final Project myProject;
|
|
||||||
private final HyperlinkInfoFactory.@Nullable HyperlinkHandler myAction;
|
|
||||||
|
|
||||||
MultipleFilesHyperlinkInfo(@NotNull List<? extends VirtualFile> virtualFiles, int lineNumber, @NotNull Project project) {
|
MultipleFilesHyperlinkInfo(@NotNull List<? extends VirtualFile> virtualFiles, int lineNumber, @NotNull Project project) {
|
||||||
this(virtualFiles, lineNumber, project, null);
|
this(virtualFiles, lineNumber, project, null);
|
||||||
@@ -61,38 +42,13 @@ public final class MultipleFilesHyperlinkInfo extends HyperlinkInfoBase implemen
|
|||||||
int lineNumber,
|
int lineNumber,
|
||||||
@NotNull Project project,
|
@NotNull Project project,
|
||||||
@Nullable HyperlinkInfoFactory.HyperlinkHandler action) {
|
@Nullable HyperlinkInfoFactory.HyperlinkHandler action) {
|
||||||
|
super(lineNumber, project, action);
|
||||||
myVirtualFiles = virtualFiles;
|
myVirtualFiles = virtualFiles;
|
||||||
myLineNumber = lineNumber;
|
|
||||||
myProject = project;
|
|
||||||
myAction = action;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void navigate(@NotNull final Project project, @Nullable RelativePoint hyperlinkLocationPoint) {
|
|
||||||
Editor originalEditor;
|
|
||||||
if (hyperlinkLocationPoint != null) {
|
|
||||||
DataManager dataManager = DataManager.getInstance();
|
|
||||||
DataContext dataContext = dataManager.getDataContext(hyperlinkLocationPoint.getOriginalComponent());
|
|
||||||
originalEditor = CommonDataKeys.EDITOR.getData(dataContext);
|
|
||||||
} else {
|
|
||||||
originalEditor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
JFrame frame = WindowManager.getInstance().getFrame(project);
|
|
||||||
int width = frame != null ? frame.getSize().width : 200;
|
|
||||||
GotoFileCellRenderer renderer = new GotoFileCellRenderer(width);
|
|
||||||
|
|
||||||
new PsiTargetNavigator<>(() -> getFiles(project))
|
|
||||||
.title(ExecutionBundle.message("popup.title.choose.target.file"))
|
|
||||||
.presentationProvider(element -> renderer.computePresentation(element))
|
|
||||||
.navigate(hyperlinkLocationPoint, ExecutionBundle.message("popup.title.choose.target.file"), project, file -> {
|
|
||||||
open(file.getVirtualFile(), originalEditor);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private List<PsiFile> getFiles(@NotNull Project project) {
|
public List<PsiFile> getFiles(@NotNull Project project) {
|
||||||
List<PsiFile> currentFiles = new ArrayList<>();
|
List<PsiFile> currentFiles = new ArrayList<>();
|
||||||
for (VirtualFile file : myVirtualFiles) {
|
for (VirtualFile file : myVirtualFiles) {
|
||||||
if (!file.isValid()) continue;
|
if (!file.isValid()) continue;
|
||||||
@@ -110,30 +66,6 @@ public final class MultipleFilesHyperlinkInfo extends HyperlinkInfoBase implemen
|
|||||||
return currentFiles;
|
return currentFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void open(@NotNull VirtualFile file, Editor originalEditor) {
|
|
||||||
Document document = FileDocumentManager.getInstance().getDocument(file, myProject);
|
|
||||||
int offset = 0;
|
|
||||||
if (document != null && myLineNumber >= 0 && myLineNumber < document.getLineCount()) {
|
|
||||||
offset = document.getLineStartOffset(myLineNumber);
|
|
||||||
}
|
|
||||||
OpenFileDescriptor descriptor = new OpenFileDescriptor(myProject, file, offset);
|
|
||||||
Editor editor = FileEditorManager.getInstance(myProject).openTextEditor(descriptor, true);
|
|
||||||
if (myAction != null && editor != null) {
|
|
||||||
if (editor instanceof EditorEx) {
|
|
||||||
((EditorEx)editor).setCaretEnabled(false);
|
|
||||||
try {
|
|
||||||
myAction.onLinkFollowed(myProject, file, editor, originalEditor);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
((EditorEx)editor).setCaretEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
myAction.onLinkFollowed(myProject, file, editor, originalEditor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public OpenFileDescriptor getDescriptor() {
|
public OpenFileDescriptor getDescriptor() {
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
|
package com.intellij.execution.filters.impl;
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.navigation.PsiTargetNavigator;
|
||||||
|
import com.intellij.execution.ExecutionBundle;
|
||||||
|
import com.intellij.execution.filters.FileHyperlinkInfo;
|
||||||
|
import com.intellij.execution.filters.HyperlinkInfoBase;
|
||||||
|
import com.intellij.execution.filters.HyperlinkInfoFactory;
|
||||||
|
import com.intellij.ide.DataManager;
|
||||||
|
import com.intellij.ide.util.gotoByName.GotoFileCellRenderer;
|
||||||
|
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext;
|
||||||
|
import com.intellij.openapi.editor.Document;
|
||||||
|
import com.intellij.openapi.editor.Editor;
|
||||||
|
import com.intellij.openapi.editor.ex.EditorEx;
|
||||||
|
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||||
|
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
import com.intellij.openapi.wm.WindowManager;
|
||||||
|
import com.intellij.psi.PsiFile;
|
||||||
|
import com.intellij.ui.awt.RelativePoint;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public abstract class MultipleFilesHyperlinkInfoBase extends HyperlinkInfoBase implements FileHyperlinkInfo {
|
||||||
|
protected final int myLineNumber;
|
||||||
|
protected final Project myProject;
|
||||||
|
private final HyperlinkInfoFactory.@Nullable HyperlinkHandler myAction;
|
||||||
|
|
||||||
|
public MultipleFilesHyperlinkInfoBase(int lineNumber,
|
||||||
|
@NotNull Project project,
|
||||||
|
@Nullable HyperlinkInfoFactory.HyperlinkHandler action) {
|
||||||
|
myLineNumber = lineNumber;
|
||||||
|
myProject = project;
|
||||||
|
myAction = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void navigate(@NotNull final Project project, @Nullable RelativePoint hyperlinkLocationPoint) {
|
||||||
|
Editor originalEditor;
|
||||||
|
if (hyperlinkLocationPoint != null) {
|
||||||
|
DataManager dataManager = DataManager.getInstance();
|
||||||
|
DataContext dataContext = dataManager.getDataContext(hyperlinkLocationPoint.getOriginalComponent());
|
||||||
|
originalEditor = CommonDataKeys.EDITOR.getData(dataContext);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
originalEditor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
JFrame frame = WindowManager.getInstance().getFrame(project);
|
||||||
|
int width = frame != null ? frame.getSize().width : 200;
|
||||||
|
GotoFileCellRenderer renderer = new GotoFileCellRenderer(width);
|
||||||
|
|
||||||
|
boolean navigated = new PsiTargetNavigator<>(() -> getFiles(project))
|
||||||
|
.title(ExecutionBundle.message("popup.title.choose.target.file"))
|
||||||
|
.presentationProvider(element -> renderer.computePresentation(element))
|
||||||
|
.navigate(hyperlinkLocationPoint, ExecutionBundle.message("popup.title.choose.target.file"), project, file -> {
|
||||||
|
open(file.getVirtualFile(), originalEditor);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (!navigated) {
|
||||||
|
showNotFound(project, hyperlinkLocationPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showNotFound(@NotNull final Project project, @Nullable RelativePoint hyperlinkLocationPoint) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public abstract List<PsiFile> getFiles(@NotNull Project project);
|
||||||
|
|
||||||
|
private void open(@NotNull VirtualFile file, Editor originalEditor) {
|
||||||
|
Document document = FileDocumentManager.getInstance().getDocument(file, myProject);
|
||||||
|
int offset = 0;
|
||||||
|
if (document != null && myLineNumber >= 0 && myLineNumber < document.getLineCount()) {
|
||||||
|
offset = document.getLineStartOffset(myLineNumber);
|
||||||
|
}
|
||||||
|
OpenFileDescriptor descriptor = new OpenFileDescriptor(myProject, file, offset);
|
||||||
|
Editor editor = FileEditorManager.getInstance(myProject).openTextEditor(descriptor, true);
|
||||||
|
if (myAction != null && editor != null) {
|
||||||
|
if (editor instanceof EditorEx) {
|
||||||
|
((EditorEx)editor).setCaretEnabled(false);
|
||||||
|
try {
|
||||||
|
myAction.onLinkFollowed(myProject, file, editor, originalEditor);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
((EditorEx)editor).setCaretEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
myAction.onLinkFollowed(myProject, file, editor, originalEditor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user