@@ -12,8 +12,7 @@ import com.intellij.openapi.vfs.VfsUtil
12
12
import com.intellij.openapi.vfs.VirtualFile
13
13
import com.intellij.psi.PsiFile
14
14
import com.intellij.psi.PsiManager
15
- import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererJava
16
- import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage
15
+ import software.aws.toolkits.core.utils.tryOrNull
17
16
18
17
/* *
19
18
* An interface define how do we parse and fetch files provided a psi file or project
@@ -40,51 +39,84 @@ interface FileCrawler {
40
39
* @param psiFile psi of the test file we are searching with, e.g. MainTest.java
41
40
* @return its source file e.g. Main.java, main.py or most relevant file if any
42
41
*/
43
- fun findFocalFileForTest (psiFile : PsiFile ): VirtualFile ?
42
+ fun listUtgCandidate (psiFile : PsiFile ): VirtualFile ?
44
43
45
44
/* *
46
45
* List files opened in the editors and sorted by file distance @see [CodeWhispererFileCrawler.getFileDistance]
46
+ * @return opened files and satisfy the following conditions
47
+ * (1) not the input file
48
+ * (2) with the same file extension as the input file has
49
+ * (3) non-test file which will be determined by [FileCrawler.isTestFile]
50
+ * (4) writable file
47
51
*/
48
- fun listRelevantFilesInEditors (psiFile : PsiFile ): List <VirtualFile >
52
+ fun listCrossFileCandidate (psiFile : PsiFile ): List <VirtualFile >
53
+
54
+ /* *
55
+ * Determine if the file given is test file or not based on its path and file name
56
+ */
57
+ fun isTestFile (virtualFile : VirtualFile , project : Project ): Boolean
49
58
}
50
59
51
60
class NoOpFileCrawler : FileCrawler {
52
61
override suspend fun listFilesImported (psiFile : PsiFile ): List <VirtualFile > = emptyList()
53
62
54
63
override fun listFilesUnderProjectRoot (project : Project ): List <VirtualFile > = emptyList()
55
- override fun findFocalFileForTest (psiFile : PsiFile ): VirtualFile ? = null
64
+ override fun listUtgCandidate (psiFile : PsiFile ): VirtualFile ? = null
56
65
57
66
override fun listFilesWithinSamePackage (psiFile : PsiFile ): List <VirtualFile > = emptyList()
58
67
59
- override fun listRelevantFilesInEditors (psiFile : PsiFile ): List <VirtualFile > = emptyList()
68
+ override fun listCrossFileCandidate (psiFile : PsiFile ): List <VirtualFile > = emptyList()
69
+
70
+ override fun isTestFile (virtualFile : VirtualFile , project : Project ): Boolean = false
60
71
}
61
72
62
73
abstract class CodeWhispererFileCrawler : FileCrawler {
63
74
abstract val fileExtension: String
64
- abstract val testFilenamePattern: Regex
65
75
abstract val dialects: Set <String >
76
+ abstract val testFileNamingPatterns: List <Regex >
77
+
78
+ override fun isTestFile (virtualFile : VirtualFile , project : Project ): Boolean {
79
+ val filePath = virtualFile.path
80
+
81
+ // if file path itself explicitly explains the file is under test sources
82
+ if (TestSourcesFilter .isTestSources(virtualFile, project) ||
83
+ filePath.contains(""" test/""" , ignoreCase = true ) ||
84
+ filePath.contains(""" tst/""" , ignoreCase = true ) ||
85
+ filePath.contains(""" tests/""" , ignoreCase = true )
86
+ ) {
87
+ return true
88
+ }
89
+
90
+ // no explicit clue from the file path, use regexes based on naming conventions
91
+ return testFileNamingPatterns.any { it.matches(virtualFile.name) }
92
+ }
66
93
67
94
override fun listFilesUnderProjectRoot (project : Project ): List <VirtualFile > = project.guessProjectDir()?.let { rootDir ->
68
95
VfsUtil .collectChildrenRecursively(rootDir).filter {
96
+ // TODO: need to handle cases js vs. jsx, ts vs. tsx when we enable js/ts utg since we likely have different file extensions
69
97
it.path.endsWith(fileExtension)
70
98
}
71
99
}.orEmpty()
72
100
73
- override fun listRelevantFilesInEditors (psiFile : PsiFile ): List <VirtualFile > {
74
- val targetFile = psiFile.virtualFile
75
- val language = psiFile.programmingLanguage()
101
+ override fun listFilesWithinSamePackage (psiFile : PsiFile ): List <VirtualFile > = runReadAction {
102
+ psiFile.containingDirectory?.files?.mapNotNull {
103
+ // exclude target file
104
+ if (it != psiFile) {
105
+ it.virtualFile
106
+ } else {
107
+ null
108
+ }
109
+ }.orEmpty()
110
+ }
76
111
77
- val isTestFilePredicate: (file: VirtualFile , project: Project ) -> Boolean = if (language is CodeWhispererJava ) {
78
- { file, project -> TestSourcesFilter .isTestSources(file, project) }
79
- } else {
80
- { file, _ -> testFilenamePattern.matches(file.name) }
81
- }
112
+ override fun listCrossFileCandidate (psiFile : PsiFile ): List <VirtualFile > {
113
+ val targetFile = psiFile.virtualFile
82
114
83
115
val openedFiles = runReadAction {
84
116
FileEditorManager .getInstance(psiFile.project).openFiles.toList().filter {
85
117
it.name != psiFile.virtualFile.name &&
86
118
isSameDialect(it.extension) &&
87
- ! isTestFilePredicate (it, psiFile.project)
119
+ ! isTestFile (it, psiFile.project)
88
120
}
89
121
}
90
122
@@ -97,7 +129,24 @@ abstract class CodeWhispererFileCrawler : FileCrawler {
97
129
return fileToFileDistanceList.sortedBy { it.second }.map { it.first }
98
130
}
99
131
100
- abstract fun guessSourceFileName (tstFileName : String ): String
132
+ override fun listUtgCandidate (psiFile : PsiFile ): VirtualFile ? = findSourceFileByName(psiFile) ? : findSourceFileByContent(psiFile)
133
+
134
+ abstract fun findSourceFileByName (psiFile : PsiFile ): VirtualFile ?
135
+
136
+ abstract fun findSourceFileByContent (psiFile : PsiFile ): VirtualFile ?
137
+
138
+ // TODO: may need to update when we enable JS/TS UTG, since we have to factor in .jsx/.tsx combinations
139
+ fun guessSourceFileName (tstFileName : String ): String? {
140
+ val srcFileName = tryOrNull {
141
+ testFileNamingPatterns.firstNotNullOf { regex ->
142
+ regex.find(tstFileName)?.groupValues?.let { groupValues ->
143
+ groupValues.get(1 ) + groupValues.get(2 )
144
+ }
145
+ }
146
+ }
147
+
148
+ return srcFileName
149
+ }
101
150
102
151
private fun isSameDialect (fileExt : String? ): Boolean = fileExt?.let {
103
152
dialects.contains(fileExt)
0 commit comments