Skip to content

Commit 780395e

Browse files
committed
glob pattern matching
1 parent 401826d commit 780395e

File tree

2 files changed

+150
-19
lines changed

2 files changed

+150
-19
lines changed

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/workspace/WorkspaceServiceHandler.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.intellij.openapi.Disposable
77
import com.intellij.openapi.project.Project
88
import com.intellij.openapi.roots.ModuleRootEvent
99
import com.intellij.openapi.roots.ModuleRootListener
10+
import com.intellij.openapi.vfs.VirtualFile
1011
import com.intellij.openapi.vfs.VirtualFileManager
1112
import com.intellij.openapi.vfs.newvfs.BulkFileListener
1213
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent
@@ -25,6 +26,8 @@ import org.eclipse.lsp4j.WorkspaceFoldersChangeEvent
2526
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
2627
import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.WorkspaceFolderUtil.createWorkspaceFolders
2728
import software.aws.toolkits.jetbrains.utils.pluginAwareExecuteOnPooledThread
29+
import java.nio.file.FileSystems
30+
import java.nio.file.Paths
2831

2932
class WorkspaceServiceHandler(
3033
private val project: Project,
@@ -33,6 +36,9 @@ class WorkspaceServiceHandler(
3336
ModuleRootListener {
3437

3538
private var lastSnapshot: List<WorkspaceFolder> = emptyList()
39+
private val supportedFilePatterns = FileSystems.getDefault().getPathMatcher(
40+
"glob:**/*.{ts,js,py,java}"
41+
)
3642

3743
init {
3844
project.messageBus.connect(serverInstance).subscribe(
@@ -49,7 +55,8 @@ class WorkspaceServiceHandler(
4955
private fun didCreateFiles(events: List<VFileEvent>) {
5056
AmazonQLspService.executeIfRunning(project) { languageServer ->
5157
val validFiles = events.mapNotNull { event ->
52-
event.file?.toNioPath()?.toUri()?.toString()?.takeIf { it.isNotEmpty() }?.let { uri ->
58+
val file = event.file?.takeIf { shouldHandleFile(it) } ?: return@mapNotNull null
59+
file.toNioPath().toUri().toString().takeIf { it.isNotEmpty() }?.let { uri ->
5360
FileCreate().apply {
5461
this.uri = uri
5562
}
@@ -69,7 +76,8 @@ class WorkspaceServiceHandler(
6976
private fun didDeleteFiles(events: List<VFileEvent>) {
7077
AmazonQLspService.executeIfRunning(project) { languageServer ->
7178
val validFiles = events.mapNotNull { event ->
72-
event.file?.toNioPath()?.toUri()?.toString()?.takeIf { it.isNotEmpty() }?.let { uri ->
79+
val file = event.file?.takeIf { shouldHandleFile(it) } ?: return@mapNotNull null
80+
file.toNioPath().toUri().toString().takeIf { it.isNotEmpty() }?.let { uri ->
7381
FileDelete().apply {
7482
this.uri = uri
7583
}
@@ -144,4 +152,13 @@ class WorkspaceServiceHandler(
144152
lastSnapshot = currentSnapshot
145153
}
146154
}
155+
156+
private fun shouldHandleFile(file: VirtualFile): Boolean {
157+
if (file.isDirectory) {
158+
return true // Matches "**/*" with matches: "folder"
159+
}
160+
val path = Paths.get(file.path)
161+
val result = supportedFilePatterns.matches(path)
162+
return result
163+
}
147164
}

plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/workspace/WorkspaceServiceHandlerTest.kt

Lines changed: 131 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent
1414
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
1515
import com.intellij.util.messages.MessageBus
1616
import com.intellij.util.messages.MessageBusConnection
17-
import io.mockk.coEvery
1817
import io.mockk.every
1918
import io.mockk.just
2019
import io.mockk.mockk
@@ -96,30 +95,143 @@ class WorkspaceServiceHandlerTest {
9695
}
9796

9897
@Test
99-
fun `test didCreateFiles with valid events`() = runTest {
100-
val uri = URI("file:///test/path")
101-
val event = createMockVFileEvent(uri, FileChangeType.Created)
98+
fun `test didCreateFiles with Python file`() = runTest {
99+
val pyUri = URI("file:///test/path")
100+
val pyEvent = createMockVFileEvent(pyUri, FileChangeType.Created, false, "py")
102101

103-
sut.after(listOf(event))
102+
sut.after(listOf(pyEvent))
104103

105104
val paramsSlot = slot<CreateFilesParams>()
106105
verify { mockWorkspaceService.didCreateFiles(capture(paramsSlot)) }
107-
assertEquals(uri.toString(), paramsSlot.captured.files[0].uri)
106+
assertEquals(pyUri.toString(), paramsSlot.captured.files[0].uri)
108107
}
109108

110109
@Test
111-
fun `test didDeleteFiles with valid event`() = runTest {
112-
val uri = URI("file:///test/path")
113-
val deleteEvent = createMockVFileEvent(uri, FileChangeType.Deleted)
110+
fun `test didCreateFiles with TypeScript file`() = runTest {
111+
val tsUri = URI("file:///test/path")
112+
val tsEvent = createMockVFileEvent(tsUri, FileChangeType.Created, false, "ts")
114113

115-
// Act
116-
sut.after(listOf(deleteEvent))
114+
sut.after(listOf(tsEvent))
115+
116+
val paramsSlot = slot<CreateFilesParams>()
117+
verify { mockWorkspaceService.didCreateFiles(capture(paramsSlot)) }
118+
assertEquals(tsUri.toString(), paramsSlot.captured.files[0].uri)
119+
}
120+
121+
@Test
122+
fun `test didCreateFiles with JavaScript file`() = runTest {
123+
val jsUri = URI("file:///test/path")
124+
val jsEvent = createMockVFileEvent(jsUri, FileChangeType.Created, false, "js")
125+
126+
sut.after(listOf(jsEvent))
127+
128+
val paramsSlot = slot<CreateFilesParams>()
129+
verify { mockWorkspaceService.didCreateFiles(capture(paramsSlot)) }
130+
assertEquals(jsUri.toString(), paramsSlot.captured.files[0].uri)
131+
}
132+
133+
@Test
134+
fun `test didCreateFiles with Java file`() = runTest {
135+
val javaUri = URI("file:///test/path")
136+
val javaEvent = createMockVFileEvent(javaUri, FileChangeType.Created, false, "java")
137+
138+
sut.after(listOf(javaEvent))
139+
140+
val paramsSlot = slot<CreateFilesParams>()
141+
verify { mockWorkspaceService.didCreateFiles(capture(paramsSlot)) }
142+
assertEquals(javaUri.toString(), paramsSlot.captured.files[0].uri)
143+
}
144+
145+
@Test
146+
fun `test didCreateFiles called for directory`() = runTest {
147+
val dirUri = URI("file:///test/directory/path")
148+
val dirEvent = createMockVFileEvent(dirUri, FileChangeType.Created, true, "")
149+
150+
sut.after(listOf(dirEvent))
151+
152+
val paramsSlot = slot<CreateFilesParams>()
153+
verify { mockWorkspaceService.didCreateFiles(capture(paramsSlot)) }
154+
assertEquals(dirUri.toString(), paramsSlot.captured.files[0].uri)
155+
}
156+
157+
@Test
158+
fun `test didCreateFiles not called for unsupported file extension`() = runTest {
159+
val txtUri = URI("file:///test/path")
160+
val txtEvent = createMockVFileEvent(txtUri, FileChangeType.Created, false, "txt")
161+
162+
sut.after(listOf(txtEvent))
163+
164+
verify(exactly = 0) { mockWorkspaceService.didCreateFiles(any()) }
165+
}
166+
167+
@Test
168+
fun `test didDeleteFiles with Python file`() = runTest {
169+
val pyUri = URI("file:///test/path")
170+
val pyEvent = createMockVFileEvent(pyUri, FileChangeType.Deleted, false, "py")
171+
172+
sut.after(listOf(pyEvent))
117173

118174
val paramsSlot = slot<DeleteFilesParams>()
119175
verify { mockWorkspaceService.didDeleteFiles(capture(paramsSlot)) }
120-
assertEquals(uri.toString(), paramsSlot.captured.files[0].uri)
176+
assertEquals(pyUri.toString(), paramsSlot.captured.files[0].uri)
177+
}
121178

122-
// Assert
179+
@Test
180+
fun `test didDeleteFiles with TypeScript file`() = runTest {
181+
val tsUri = URI("file:///test/path")
182+
val tsEvent = createMockVFileEvent(tsUri, FileChangeType.Deleted, false, "ts")
183+
184+
sut.after(listOf(tsEvent))
185+
186+
val paramsSlot = slot<DeleteFilesParams>()
187+
verify { mockWorkspaceService.didDeleteFiles(capture(paramsSlot)) }
188+
assertEquals(tsUri.toString(), paramsSlot.captured.files[0].uri)
189+
}
190+
191+
@Test
192+
fun `test didDeleteFiles with JavaScript file`() = runTest {
193+
val jsUri = URI("file:///test/path")
194+
val jsEvent = createMockVFileEvent(jsUri, FileChangeType.Deleted, false, "js")
195+
196+
sut.after(listOf(jsEvent))
197+
198+
val paramsSlot = slot<DeleteFilesParams>()
199+
verify { mockWorkspaceService.didDeleteFiles(capture(paramsSlot)) }
200+
assertEquals(jsUri.toString(), paramsSlot.captured.files[0].uri)
201+
}
202+
203+
@Test
204+
fun `test didDeleteFiles with Java file`() = runTest {
205+
val javaUri = URI("file:///test/path")
206+
val javaEvent = createMockVFileEvent(javaUri, FileChangeType.Deleted, false, "java")
207+
208+
sut.after(listOf(javaEvent))
209+
210+
val paramsSlot = slot<DeleteFilesParams>()
211+
verify { mockWorkspaceService.didDeleteFiles(capture(paramsSlot)) }
212+
assertEquals(javaUri.toString(), paramsSlot.captured.files[0].uri)
213+
}
214+
215+
@Test
216+
fun `test didDeleteFiles not called for unsupported file extension`() = runTest {
217+
val txtUri = URI("file:///test/path")
218+
val txtEvent = createMockVFileEvent(txtUri, FileChangeType.Deleted, false, "txt")
219+
220+
sut.after(listOf(txtEvent))
221+
222+
verify(exactly = 0) { mockWorkspaceService.didDeleteFiles(any()) }
223+
}
224+
225+
@Test
226+
fun `test didDeleteFiles called for directory`() = runTest {
227+
val dirUri = URI("file:///test/directory/path")
228+
val dirEvent = createMockVFileEvent(dirUri, FileChangeType.Deleted, true, "")
229+
230+
sut.after(listOf(dirEvent))
231+
232+
val paramsSlot = slot<DeleteFilesParams>()
233+
verify { mockWorkspaceService.didDeleteFiles(capture(paramsSlot)) }
234+
assertEquals(dirUri.toString(), paramsSlot.captured.files[0].uri)
123235
}
124236

125237
@Test
@@ -129,9 +241,9 @@ class WorkspaceServiceHandlerTest {
129241
val deleteURI = URI("file:///test/pathOfDeletion")
130242
val changeURI = URI("file:///test/pathOfChange")
131243

132-
val virtualFileCreate = createMockVFileEvent(createURI, FileChangeType.Created)
133-
val virtualFileDelete = createMockVFileEvent(deleteURI, FileChangeType.Deleted)
134-
val virtualFileChange = createMockVFileEvent(changeURI)
244+
val virtualFileCreate = createMockVFileEvent(createURI, FileChangeType.Created, false)
245+
val virtualFileDelete = createMockVFileEvent(deleteURI, FileChangeType.Deleted, false)
246+
val virtualFileChange = createMockVFileEvent(changeURI, FileChangeType.Changed, false)
135247

136248
// Act
137249
sut.after(listOf(virtualFileCreate, virtualFileDelete, virtualFileChange))
@@ -314,12 +426,14 @@ class WorkspaceServiceHandlerTest {
314426
assertEquals("folder2", paramsSlot.captured.event.removed[0].name)
315427
}
316428

317-
private fun createMockVFileEvent(uri: URI, type: FileChangeType = FileChangeType.Changed): VFileEvent {
429+
private fun createMockVFileEvent(uri: URI, type: FileChangeType = FileChangeType.Changed, isDirectory: Boolean, extension: String = "py"): VFileEvent {
318430
val virtualFile = mockk<VirtualFile>()
319431
val nioPath = mockk<Path>()
320432

433+
every { virtualFile.isDirectory } returns isDirectory
321434
every { virtualFile.toNioPath() } returns nioPath
322435
every { nioPath.toUri() } returns uri
436+
every { virtualFile.path } returns "${uri.path}.$extension"
323437

324438
return when (type) {
325439
FileChangeType.Deleted -> mockk<VFileDeleteEvent>()

0 commit comments

Comments
 (0)