diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 7404b2ae849..aa743022a9e 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -828,6 +828,8 @@ + + diff --git a/idea/src/org/jetbrains/kotlin/idea/run/KotlinConsoleFilterProvider.kt b/idea/src/org/jetbrains/kotlin/idea/run/KotlinConsoleFilterProvider.kt new file mode 100644 index 00000000000..9fd68ed4dc0 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/run/KotlinConsoleFilterProvider.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2017 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.run + +import com.intellij.execution.filters.ConsoleFilterProviderEx +import com.intellij.execution.filters.Filter +import com.intellij.execution.filters.HyperlinkInfo +import com.intellij.openapi.editor.colors.CodeInsightColors +import com.intellij.openapi.editor.colors.EditorColorsManager +import com.intellij.openapi.fileEditor.OpenFileDescriptor +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.psi.search.GlobalSearchScope + +class KotlinConsoleFilterProvider : ConsoleFilterProviderEx { + override fun getDefaultFilters(project: Project): Array { + return getDefaultFilters(project, GlobalSearchScope.allScope(project)) + } + + override fun getDefaultFilters(project: Project, scope: GlobalSearchScope): Array { + return arrayOf(KotlinConsoleFilter(project, scope)) + } +} + +class KotlinConsoleFilter(val project: Project, val scope: GlobalSearchScope) : Filter { + override fun applyFilter(line: String, entireLength: Int): Filter.Result? { + val messageSuffix = pattern.find(line) ?: return null + val pathStart = line.indexOf(":\\").takeIf { it >= 0 }?.minus(1) + ?: line.indexOf("/").takeIf { it >= 0 } + ?: return null + val path = line.substring(pathStart, messageSuffix.groups[1]!!.range.last + 1) + val lineNumber = Integer.parseInt(messageSuffix.groupValues[2]) - 1 + val column = Integer.parseInt(messageSuffix.groupValues[3]) - 1 + val attrs = EditorColorsManager.getInstance().globalScheme.getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES) + + val offset = entireLength - line.length + return Filter.Result(offset + pathStart, offset + messageSuffix.range.last, LocalFileHyperlinkInfo(path, lineNumber, column), attrs) + } + + companion object { + private val pattern = Regex("""(\.kts?): \((\d+), (\d+)\):""") + } +} + +class LocalFileHyperlinkInfo(val path: String, val line: Int, val column: Int) : HyperlinkInfo { + override fun navigate(project: Project) { + val f = LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(path)) + if (f != null) { + OpenFileDescriptor(project, f, line, column).navigate(true) + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/run/KotlinConsoleFilterTest.kt b/idea/tests/org/jetbrains/kotlin/idea/run/KotlinConsoleFilterTest.kt new file mode 100644 index 00000000000..694947884a3 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/run/KotlinConsoleFilterTest.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2017 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.run + +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor + +class KotlinConsoleFilterTest : KotlinLightCodeInsightFixtureTestCase() { + fun testWindowsPath() { + val filter = KotlinConsoleFilter(project, GlobalSearchScope.allScope(project)) + val line = """w: E:\Work\src\settings\jmx\JMX.kt: (54, 56): This cast can never succeed""" + val result = filter.applyFilter(line, line.length)!! + val resultItem = result.resultItems.single() + assertEquals(3, resultItem.getHighlightStartOffset()) + assertEquals(44, resultItem.getHighlightEndOffset()) + val hyperlinkInfo = resultItem.getHyperlinkInfo() as LocalFileHyperlinkInfo + assertEquals("E:\\Work\\src\\settings\\jmx\\JMX.kt", hyperlinkInfo.path) + } + + fun testLinuxPath() { + val filter = KotlinConsoleFilter(project, GlobalSearchScope.allScope(project)) + val line = """e: /Users/Work/app-web/src/main/kotlin/services/teamDirectory/NewOrEditTeamPageDescriptor.kt: (58, 52): The boolean literal does not conform to the expected type TeamMemberBuilder.() -> Unit""" + val result = filter.applyFilter(line, line.length)!! + val resultItem = result.resultItems.single() + assertEquals(3, resultItem.getHighlightStartOffset()) + assertEquals(102, resultItem.getHighlightEndOffset()) + } + + override fun getProjectDescriptor() = KotlinLightProjectDescriptor.INSTANCE!! +} \ No newline at end of file