Skip to content

Commit 9ef15e7

Browse files
committed
get SupportedFiletype globs from initializeResult
1 parent b179b37 commit 9ef15e7

File tree

3 files changed

+90
-17
lines changed

3 files changed

+90
-17
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ private class AmazonQServerInstance(private val project: Project, private val cs
309309

310310
DefaultAuthCredentialsService(project, encryptionManager, this)
311311
TextDocumentServiceHandler(project, this)
312-
WorkspaceServiceHandler(project, this)
312+
WorkspaceServiceHandler(project, initializeResult, this)
313313
}
314314

315315
override fun dispose() {

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

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent
1414
import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent
1515
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
1616
import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent
17+
import kotlinx.coroutines.Deferred
1718
import org.eclipse.lsp4j.CreateFilesParams
1819
import org.eclipse.lsp4j.DeleteFilesParams
1920
import org.eclipse.lsp4j.DidChangeWatchedFilesParams
@@ -22,28 +23,34 @@ import org.eclipse.lsp4j.FileChangeType
2223
import org.eclipse.lsp4j.FileCreate
2324
import org.eclipse.lsp4j.FileDelete
2425
import org.eclipse.lsp4j.FileEvent
26+
import org.eclipse.lsp4j.FileOperationFilter
2527
import org.eclipse.lsp4j.FileRename
28+
import org.eclipse.lsp4j.InitializeResult
2629
import org.eclipse.lsp4j.RenameFilesParams
2730
import org.eclipse.lsp4j.WorkspaceFolder
2831
import org.eclipse.lsp4j.WorkspaceFoldersChangeEvent
2932
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
3033
import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.WorkspaceFolderUtil.createWorkspaceFolders
3134
import software.aws.toolkits.jetbrains.utils.pluginAwareExecuteOnPooledThread
3235
import java.nio.file.FileSystems
36+
import java.nio.file.PathMatcher
3337
import java.nio.file.Paths
3438

3539
class WorkspaceServiceHandler(
3640
private val project: Project,
41+
private val initializeResult: Deferred<InitializeResult>,
3742
serverInstance: Disposable,
3843
) : BulkFileListener,
3944
ModuleRootListener {
4045

4146
private var lastSnapshot: List<WorkspaceFolder> = emptyList()
42-
private val supportedFilePatterns = FileSystems.getDefault().getPathMatcher(
43-
"glob:**/*.{ts,js,py,java}"
44-
)
47+
private val operationMatchers: MutableMap<FileOperationType, List<Pair<PathMatcher, String>>> = mutableMapOf()
4548

4649
init {
50+
initializeResult.invokeOnCompletion {
51+
operationMatchers.putAll(initializePatterns(initializeResult.getCompleted()))
52+
}
53+
4754
project.messageBus.connect(serverInstance).subscribe(
4855
VirtualFileManager.VFS_CHANGES,
4956
this
@@ -55,10 +62,44 @@ class WorkspaceServiceHandler(
5562
)
5663
}
5764

65+
enum class FileOperationType {
66+
CREATE,
67+
DELETE,
68+
RENAME,
69+
}
70+
71+
private fun initializePatterns(initializeResult: InitializeResult): Map<FileOperationType, List<Pair<PathMatcher, String>>> {
72+
val patterns = mutableMapOf<FileOperationType, List<Pair<PathMatcher, String>>>()
73+
74+
initializeResult.capabilities?.workspace?.fileOperations?.let { fileOps ->
75+
patterns[FileOperationType.CREATE] = createMatchers(fileOps.didCreate?.filters)
76+
patterns[FileOperationType.DELETE] = createMatchers(fileOps.didDelete?.filters)
77+
patterns[FileOperationType.RENAME] = createMatchers(fileOps.didRename?.filters)
78+
}
79+
80+
return patterns
81+
}
82+
83+
private fun createMatchers(filters: List<FileOperationFilter>?): List<Pair<PathMatcher, String>> =
84+
filters?.map { filter ->
85+
FileSystems.getDefault().getPathMatcher("glob:${filter.pattern.glob}") to filter.pattern.matches
86+
} ?: emptyList()
87+
88+
private fun shouldHandleFile(file: VirtualFile, operation: FileOperationType): Boolean {
89+
val matchers = operationMatchers[operation] ?: return false
90+
return matchers.any { (matcher, type) ->
91+
when (type) {
92+
"file" -> !file.isDirectory && matcher.matches(Paths.get(file.path))
93+
"folder" -> file.isDirectory && matcher.matches(Paths.get(file.path))
94+
else -> matcher.matches(Paths.get(file.path))
95+
}
96+
}
97+
}
98+
5899
private fun didCreateFiles(events: List<VFileEvent>) {
59100
AmazonQLspService.executeIfRunning(project) { languageServer ->
60101
val validFiles = events.mapNotNull { event ->
61-
val file = event.file?.takeIf { shouldHandleFile(it) } ?: return@mapNotNull null
102+
val file = event.file?.takeIf { shouldHandleFile(it, FileOperationType.CREATE) } ?: return@mapNotNull null
62103
file.toNioPath().toUri().toString().takeIf { it.isNotEmpty() }?.let { uri ->
63104
FileCreate().apply {
64105
this.uri = uri
@@ -79,7 +120,7 @@ class WorkspaceServiceHandler(
79120
private fun didDeleteFiles(events: List<VFileEvent>) {
80121
AmazonQLspService.executeIfRunning(project) { languageServer ->
81122
val validFiles = events.mapNotNull { event ->
82-
val file = event.file?.takeIf { shouldHandleFile(it) } ?: return@mapNotNull null
123+
val file = event.file?.takeIf { shouldHandleFile(it, FileOperationType.DELETE) } ?: return@mapNotNull null
83124
file.toNioPath().toUri().toString().takeIf { it.isNotEmpty() }?.let { uri ->
84125
FileDelete().apply {
85126
this.uri = uri
@@ -102,7 +143,7 @@ class WorkspaceServiceHandler(
102143
val validRenames = events
103144
.filter { it.propertyName == VirtualFile.PROP_NAME }
104145
.mapNotNull { event ->
105-
val file = event.file.takeIf { shouldHandleFile(it) } ?: return@mapNotNull null
146+
val file = event.file.takeIf { shouldHandleFile(it, FileOperationType.RENAME) } ?: return@mapNotNull null
106147
val oldName = event.oldValue as? String ?: return@mapNotNull null
107148
if (event.newValue !is String) return@mapNotNull null
108149

@@ -186,13 +227,4 @@ class WorkspaceServiceHandler(
186227
lastSnapshot = currentSnapshot
187228
}
188229
}
189-
190-
private fun shouldHandleFile(file: VirtualFile): Boolean {
191-
if (file.isDirectory) {
192-
return true // Matches "**/*" with matches: "folder"
193-
}
194-
val path = Paths.get(file.path)
195-
val result = supportedFilePatterns.matches(path)
196-
return result
197-
}
198230
}

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

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,22 @@ import io.mockk.mockkStatic
2323
import io.mockk.runs
2424
import io.mockk.slot
2525
import io.mockk.verify
26+
import kotlinx.coroutines.CompletableDeferred
2627
import kotlinx.coroutines.test.runTest
2728
import org.eclipse.lsp4j.CreateFilesParams
2829
import org.eclipse.lsp4j.DeleteFilesParams
2930
import org.eclipse.lsp4j.DidChangeWatchedFilesParams
3031
import org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams
3132
import org.eclipse.lsp4j.FileChangeType
33+
import org.eclipse.lsp4j.FileOperationFilter
34+
import org.eclipse.lsp4j.FileOperationOptions
35+
import org.eclipse.lsp4j.FileOperationPattern
36+
import org.eclipse.lsp4j.FileOperationsServerCapabilities
37+
import org.eclipse.lsp4j.InitializeResult
3238
import org.eclipse.lsp4j.RenameFilesParams
39+
import org.eclipse.lsp4j.ServerCapabilities
3340
import org.eclipse.lsp4j.WorkspaceFolder
41+
import org.eclipse.lsp4j.WorkspaceServerCapabilities
3442
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
3543
import org.eclipse.lsp4j.services.WorkspaceService
3644
import org.junit.jupiter.api.Assertions.assertEquals
@@ -94,7 +102,40 @@ class WorkspaceServiceHandlerTest {
94102
every { messageBus.connect(any<Disposable>()) } returns mockConnection
95103
every { mockConnection.subscribe(any(), any()) } just runs
96104

97-
sut = WorkspaceServiceHandler(project, mockk())
105+
// Mock InitializeResult with file operation patterns
106+
val mockInitializeResult = mockk<InitializeResult>()
107+
val mockCapabilities = mockk<ServerCapabilities>()
108+
val mockWorkspaceCapabilities = mockk<WorkspaceServerCapabilities>()
109+
val mockFileOperations = mockk<FileOperationsServerCapabilities>()
110+
111+
val fileFilter = FileOperationFilter().apply {
112+
pattern = FileOperationPattern().apply {
113+
glob = "**/*.{ts,js,py,java}"
114+
matches = "file"
115+
}
116+
}
117+
val folderFilter = FileOperationFilter().apply {
118+
pattern = FileOperationPattern().apply {
119+
glob = "**/*"
120+
matches = "folder"
121+
}
122+
}
123+
124+
val fileOperationOptions = FileOperationOptions().apply {
125+
filters = listOf(fileFilter, folderFilter)
126+
}
127+
128+
every { mockFileOperations.didCreate } returns fileOperationOptions
129+
every { mockFileOperations.didDelete } returns fileOperationOptions
130+
every { mockFileOperations.didRename } returns fileOperationOptions
131+
every { mockWorkspaceCapabilities.fileOperations } returns mockFileOperations
132+
every { mockCapabilities.workspace } returns mockWorkspaceCapabilities
133+
every { mockInitializeResult.capabilities } returns mockCapabilities
134+
135+
val mockDeferred = CompletableDeferred(mockInitializeResult)
136+
137+
// Create WorkspaceServiceHandler with mocked InitializeResult
138+
sut = WorkspaceServiceHandler(project, mockDeferred, mockk())
98139
}
99140

100141
@Test

0 commit comments

Comments
 (0)