@@ -14,6 +14,7 @@ import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent
1414import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent
1515import com.intellij.openapi.vfs.newvfs.events.VFileEvent
1616import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent
17+ import kotlinx.coroutines.Deferred
1718import org.eclipse.lsp4j.CreateFilesParams
1819import org.eclipse.lsp4j.DeleteFilesParams
1920import org.eclipse.lsp4j.DidChangeWatchedFilesParams
@@ -22,28 +23,34 @@ import org.eclipse.lsp4j.FileChangeType
2223import org.eclipse.lsp4j.FileCreate
2324import org.eclipse.lsp4j.FileDelete
2425import org.eclipse.lsp4j.FileEvent
26+ import org.eclipse.lsp4j.FileOperationFilter
2527import org.eclipse.lsp4j.FileRename
28+ import org.eclipse.lsp4j.InitializeResult
2629import org.eclipse.lsp4j.RenameFilesParams
2730import org.eclipse.lsp4j.WorkspaceFolder
2831import org.eclipse.lsp4j.WorkspaceFoldersChangeEvent
2932import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
3033import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.WorkspaceFolderUtil.createWorkspaceFolders
3134import software.aws.toolkits.jetbrains.utils.pluginAwareExecuteOnPooledThread
3235import java.nio.file.FileSystems
36+ import java.nio.file.PathMatcher
3337import java.nio.file.Paths
3438
3539class 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}
0 commit comments