Fix copy-paste from KTS files
#KT-37517 Fixed
This commit is contained in:
@@ -130,10 +130,7 @@ import org.jetbrains.kotlin.idea.repl.AbstractIdeReplCompletionTest
|
||||
import org.jetbrains.kotlin.idea.resolve.*
|
||||
import org.jetbrains.kotlin.idea.scratch.AbstractScratchLineMarkersTest
|
||||
import org.jetbrains.kotlin.idea.scratch.AbstractScratchRunActionTest
|
||||
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationCompletionTest
|
||||
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationHighlightingTest
|
||||
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationNavigationTest
|
||||
import org.jetbrains.kotlin.idea.script.AbstractScriptDefinitionsOrderTest
|
||||
import org.jetbrains.kotlin.idea.script.*
|
||||
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerLeafGroupingTest
|
||||
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerMultiplatformTest
|
||||
import org.jetbrains.kotlin.idea.slicer.AbstractSlicerNullnessGroupingTest
|
||||
@@ -891,6 +888,23 @@ fun main(args: Array<String>) {
|
||||
model("script/definition/completion", extension = null, recursive = false)
|
||||
}
|
||||
|
||||
testClass<AbstractScriptConfigurationInsertImportOnPasteTest> {
|
||||
model(
|
||||
"script/definition/imports",
|
||||
testMethod = "doTestCopy",
|
||||
testClassName = "Copy",
|
||||
extension = null,
|
||||
recursive = false
|
||||
)
|
||||
model(
|
||||
"script/definition/imports",
|
||||
testMethod = "doTestCut",
|
||||
testClassName = "Cut",
|
||||
extension = null,
|
||||
recursive = false
|
||||
)
|
||||
}
|
||||
|
||||
testClass<AbstractScriptDefinitionsOrderTest> {
|
||||
model("script/definition/order", extension = null, recursive = false)
|
||||
}
|
||||
|
||||
+23
-3
@@ -27,11 +27,13 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.allowResolveInDispatchThread
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.codeInsight.ReviewAddedImports.reviewAddedImports
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
|
||||
import org.jetbrains.kotlin.idea.core.util.end
|
||||
import org.jetbrains.kotlin.idea.core.util.range
|
||||
import org.jetbrains.kotlin.idea.core.util.start
|
||||
@@ -48,6 +50,7 @@ import org.jetbrains.kotlin.idea.util.getSourceRoot
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.kdoc.psi.api.KDocElement
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition.Companion.STD_SCRIPT_EXT
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
@@ -428,15 +431,32 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
|
||||
|
||||
""".trimIndent()
|
||||
|
||||
val sourceFileUrl = transferableData.sourceFileUrl
|
||||
val script = !sourceFileUrl.endsWith(KotlinFileType.EXTENSION)
|
||||
val extension = run {
|
||||
if (!script) return@run KotlinFileType.EXTENSION
|
||||
ScriptDefinitionsManager.getInstance(project).getKnownFilenameExtensions().filter {
|
||||
sourceFileUrl.endsWith(it)
|
||||
}.sortedByDescending { it.length }.firstOrNull() ?: KotlinFileType.EXTENSION
|
||||
}
|
||||
|
||||
val dummyOriginalFile = runReadAction {
|
||||
KtPsiFactory(project)
|
||||
.createAnalyzableFile(
|
||||
"dummy-original.kt",
|
||||
"dummy-original.$extension",
|
||||
"$dummyOrigFileProlog${transferableData.sourceText}",
|
||||
ctxFile
|
||||
)
|
||||
}
|
||||
|
||||
if (script) {
|
||||
val originalFile = runReadAction {
|
||||
val virtualFile = VirtualFileManager.getInstance().findFileByUrl(sourceFileUrl) ?: return@runReadAction null
|
||||
PsiManager.getInstance(project).findFile(virtualFile)
|
||||
} ?: return emptyList()
|
||||
dummyOriginalFile.originalFile = originalFile
|
||||
}
|
||||
|
||||
val offsetDelta = dummyOrigFileProlog.length - transferableData.sourceTextOffset
|
||||
|
||||
val dummyOriginalFileTextRanges =
|
||||
@@ -476,7 +496,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
|
||||
// - those source package imports those are not present in a fake package
|
||||
// - all rest imports
|
||||
|
||||
val sourceImportPrefix = "import $sourcePkgName"
|
||||
val sourceImportPrefix = "import ${if (sourcePkgName.isEmpty()) fakePkgName else sourcePkgName}"
|
||||
val fakeImportPrefix = "import $fakePkgName"
|
||||
|
||||
val affectedSourcePkgImports = imports.filter { it.startsWith(sourceImportPrefix) }
|
||||
@@ -511,7 +531,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<BasicKotlinRefe
|
||||
|
||||
return """
|
||||
${joinLines(dummyFileImports)}
|
||||
import ${sourcePkgName}.*
|
||||
${if (sourcePkgName.isNotEmpty()) "import ${sourcePkgName}.*" else ""}
|
||||
${joinLines(filteredImports)}
|
||||
"""
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package to
|
||||
|
||||
val relocatedJarContents = configurations.creating {
|
||||
attributes {
|
||||
attribute(USAGE_ATTRIBUTE, JAVA_RUNTIME)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import custom.scriptDefinition.KotlinPlatformType.Companion.attribute
|
||||
|
||||
<selection>
|
||||
val relocatedJarContents = configurations.creating {
|
||||
attributes {
|
||||
attribute(USAGE_ATTRIBUTE, JAVA_RUNTIME)
|
||||
}
|
||||
}</selection>
|
||||
@@ -0,0 +1,2 @@
|
||||
package to
|
||||
<caret>
|
||||
@@ -0,0 +1,58 @@
|
||||
package custom.scriptDefinition
|
||||
|
||||
import kotlin.script.experimental.annotations.KotlinScript
|
||||
import kotlin.script.experimental.jvm.dependenciesFromClassContext
|
||||
import kotlin.script.experimental.jvm.jvm
|
||||
import kotlin.script.experimental.api.*
|
||||
|
||||
@KotlinScript(
|
||||
displayName = "Definition for tests",
|
||||
fileExtension = "kts",
|
||||
compilationConfiguration = TemplateDefinition::class
|
||||
)
|
||||
open class Template(val args: Array<String>)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
object TemplateDefinition : ScriptCompilationConfiguration(
|
||||
{
|
||||
baseClass(Base::class)
|
||||
jvm {
|
||||
dependenciesFromClassContext(TemplateDefinition::class)
|
||||
}
|
||||
ide {
|
||||
acceptedLocations(ScriptAcceptedLocation.Everywhere)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
data class Attr(val name: String, val clazz: Class<*>)
|
||||
|
||||
class KotlinPlatformType {
|
||||
companion object {
|
||||
val attribute = Attr("org.gradle.usage", Attr::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
class AttributesBuilder {
|
||||
fun attribute(attribute: Attr, name: String) {}
|
||||
}
|
||||
|
||||
class ConfigurationsBuilder {
|
||||
fun attributes(configuration: AttributesBuilder.() -> Unit) {
|
||||
AttributesBuilder().apply(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
class Configurations {
|
||||
fun creating(initializer: ConfigurationsBuilder.() -> Unit) {
|
||||
ConfigurationsBuilder().apply(initializer)
|
||||
}
|
||||
}
|
||||
|
||||
open class Base {
|
||||
val configurations = Configurations()
|
||||
|
||||
val USAGE_ATTRIBUTE = Attr("org.gradle.usage", Attr::class.java)
|
||||
|
||||
val JAVA_RUNTIME = "java-runtime"
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.script
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.IdeActions
|
||||
import com.intellij.openapi.actionSystem.Presentation
|
||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.ReviewAddedImports
|
||||
import org.jetbrains.kotlin.idea.test.dumpTextWithErrors
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractScriptConfigurationInsertImportOnPasteTest : AbstractScriptConfigurationTest() {
|
||||
private val NO_ERRORS_DUMP_DIRECTIVE = "// NO_ERRORS_DUMP"
|
||||
private val DELETE_DEPENDENCIES_BEFORE_PASTE_DIRECTIVE = "// DELETE_DEPENDENCIES_BEFORE_PASTE"
|
||||
|
||||
protected fun doTestCut(path: String) {
|
||||
doTestAction(IdeActions.ACTION_CUT, path)
|
||||
}
|
||||
|
||||
protected fun doTestCopy(path: String) {
|
||||
doTestAction(IdeActions.ACTION_COPY, path)
|
||||
}
|
||||
|
||||
private fun doTestAction(cutOrCopy: String, unused: String) {
|
||||
val testDataFile = testDataFile()
|
||||
val testFile = File(testDataFile, "script.kts")
|
||||
val testFileText = FileUtil.loadFile(testFile, true)
|
||||
val noErrorsDump = InTextDirectivesUtils.isDirectiveDefined(testFileText, NO_ERRORS_DUMP_DIRECTIVE)
|
||||
|
||||
val scriptFile = configureScriptFile(testDataFile.path)
|
||||
if (!noErrorsDump) {
|
||||
val sourceFile = scriptFile.toKtFile()
|
||||
val dumpTextWithErrors = sourceFile.dumpTextWithErrors()
|
||||
assertEquals(sourceFile.text, dumpTextWithErrors)
|
||||
}
|
||||
|
||||
performEditorAction(cutOrCopy)
|
||||
|
||||
KotlinCopyPasteReferenceProcessor.declarationsToImportSuggested = emptyList()
|
||||
ReviewAddedImports.importsToBeReviewed = emptyList()
|
||||
|
||||
val importsToBeDeletedFile = File(testDataFile, "imports_to_delete")
|
||||
ReviewAddedImports.importsToBeDeleted = if (importsToBeDeletedFile.exists()) {
|
||||
importsToBeDeletedFile.readLines()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
val targetScript = createFileAndSyncDependencies(File(testDataFile, "target.kts"))
|
||||
performEditorAction(IdeActions.ACTION_PASTE)
|
||||
UIUtil.dispatchAllInvocationEvents()
|
||||
|
||||
val namesToImportDump = KotlinCopyPasteReferenceProcessor.declarationsToImportSuggested.joinToString("\n")
|
||||
KotlinTestUtils.assertEqualsToFile(File(testDataFile, "expected.names"), namesToImportDump)
|
||||
assertEquals(namesToImportDump, ReviewAddedImports.importsToBeReviewed.joinToString("\n"))
|
||||
|
||||
val sourceFile = targetScript.toKtFile()
|
||||
val sourceText = if (noErrorsDump)
|
||||
sourceFile.text
|
||||
else
|
||||
sourceFile.dumpTextWithErrors()
|
||||
KotlinTestUtils.assertEqualsToFile(File(testDataFile, "expected.kts"), sourceText)
|
||||
}
|
||||
|
||||
private fun VirtualFile.toKtFile() = PsiManager.getInstance(project).findFile(this) as KtFile
|
||||
|
||||
private fun performEditorAction(actionId: String): Boolean {
|
||||
val dataContext = getEditorDataContext()
|
||||
val managerEx = ActionManagerEx.getInstanceEx()
|
||||
val action = managerEx.getAction(actionId)
|
||||
val event = AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, Presentation(), managerEx, 0)
|
||||
action.beforeActionPerformedUpdate(event)
|
||||
if (!event.presentation.isEnabled) {
|
||||
return false
|
||||
}
|
||||
ActionUtil.performActionDumbAwareWithCallbacks(action, event, dataContext)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getEditorDataContext() = (editor as EditorEx).dataContext
|
||||
|
||||
}
|
||||
@@ -27,14 +27,15 @@ import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
|
||||
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil
|
||||
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationTest.Companion.useDefaultTemplate
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.util.getProjectJdkTableSafe
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.MockLibraryUtil
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.test.TestMetadata
|
||||
import org.jetbrains.kotlin.test.util.addDependency
|
||||
import org.jetbrains.kotlin.test.util.projectLibrary
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
@@ -43,13 +44,10 @@ import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_COMMON_JAR
|
||||
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_JVM_JAR
|
||||
import java.io.File
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.script.dependencies.Environment
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
|
||||
private val validKeys = setOf("javaHome", "sources", "classpath", "imports", "template-classes-names")
|
||||
private const val useDefaultTemplate = "// DEPENDENCIES:"
|
||||
private const val templatesSettings = "// TEMPLATES: "
|
||||
|
||||
// some bugs can only be reproduced when some module and script have intersecting library dependencies
|
||||
private const val configureConflictingModule = "// CONFLICTING_MODULE"
|
||||
|
||||
@@ -62,6 +60,24 @@ internal val switches = listOf(
|
||||
abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
|
||||
companion object {
|
||||
private const val SCRIPT_NAME = "script.kts"
|
||||
|
||||
val validKeys = setOf("javaHome", "sources", "classpath", "imports", "template-classes-names")
|
||||
const val useDefaultTemplate = "// DEPENDENCIES:"
|
||||
const val templatesSettings = "// TEMPLATES: "
|
||||
}
|
||||
|
||||
protected fun testDataFile(fileName: String): File = File(testDataPath, fileName)
|
||||
|
||||
protected fun testDataFile(): File = testDataFile(fileName())
|
||||
|
||||
protected fun testPath(fileName: String = fileName()): String = testDataFile(fileName).toString()
|
||||
|
||||
protected fun testPath(): String = testPath(fileName())
|
||||
|
||||
protected open fun fileName(): String = KotlinTestUtils.getTestDataFileName(this::class.java, this.name) ?: (getTestName(false) + ".kt")
|
||||
|
||||
override fun getTestDataPath(): String {
|
||||
return this::class.findAnnotation<TestMetadata>()?.value ?: super.getTestDataPath()
|
||||
}
|
||||
|
||||
override fun setUpModule() {
|
||||
@@ -84,12 +100,12 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
protected fun configureScriptFile(path: String) {
|
||||
protected fun configureScriptFile(path: String): VirtualFile {
|
||||
val mainScriptFile = findMainScript(path)
|
||||
configureScriptFile(path, mainScriptFile)
|
||||
return configureScriptFile(path, mainScriptFile)
|
||||
}
|
||||
|
||||
protected fun configureScriptFile(path: String, mainScriptFile: File) {
|
||||
protected fun configureScriptFile(path: String, mainScriptFile: File): VirtualFile {
|
||||
val environment = createScriptEnvironment(mainScriptFile)
|
||||
registerScriptTemplateProvider(environment)
|
||||
|
||||
@@ -136,7 +152,7 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
createFileAndSyncDependencies(mainScriptFile)
|
||||
return createFileAndSyncDependencies(mainScriptFile)
|
||||
}
|
||||
|
||||
private val oldScripClasspath: String? = System.getProperty("kotlin.script.classpath")
|
||||
@@ -268,7 +284,7 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
protected fun createFileAndSyncDependencies(scriptFile: File) {
|
||||
protected fun createFileAndSyncDependencies(scriptFile: File): VirtualFile {
|
||||
var script: VirtualFile? = null
|
||||
if (module != null) {
|
||||
script = module.moduleFile?.parent?.findChild(scriptFile.name)
|
||||
@@ -284,6 +300,7 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
|
||||
|
||||
configureByExistingFile(script)
|
||||
loadScriptConfigurationSynchronously(script)
|
||||
return script!!
|
||||
}
|
||||
|
||||
protected open fun loadScriptConfigurationSynchronously(script: VirtualFile) {
|
||||
|
||||
idea/tests/org/jetbrains/kotlin/idea/script/ScriptConfigurationInsertImportOnPasteTestGenerated.java
Generated
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.script;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class ScriptConfigurationInsertImportOnPasteTestGenerated extends AbstractScriptConfigurationInsertImportOnPasteTest {
|
||||
@TestMetadata("idea/testData/script/definition/imports")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Copy extends AbstractScriptConfigurationInsertImportOnPasteTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTestCopy, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInCopy() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/script/definition/imports"), Pattern.compile("^([^\\.]+)$"), null, false);
|
||||
}
|
||||
|
||||
@TestMetadata("implicitImport")
|
||||
public void testImplicitImport() throws Exception {
|
||||
runTest("idea/testData/script/definition/imports/implicitImport/");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/script/definition/imports")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Cut extends AbstractScriptConfigurationInsertImportOnPasteTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTestCut, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInCut() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/script/definition/imports"), Pattern.compile("^([^\\.]+)$"), null, false);
|
||||
}
|
||||
|
||||
@TestMetadata("implicitImport")
|
||||
public void testImplicitImport() throws Exception {
|
||||
runTest("idea/testData/script/definition/imports/implicitImport/");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user