@@ -26,7 +26,9 @@ import org.eclipse.lsp4j.FileChangeType
2626import org.eclipse.lsp4j.FileCreate
2727import org.eclipse.lsp4j.FileDelete
2828import org.eclipse.lsp4j.FileEvent
29+ import org.eclipse.lsp4j.FileOperationFilter
2930import org.eclipse.lsp4j.FileRename
31+ import org.eclipse.lsp4j.InitializeResult
3032import org.eclipse.lsp4j.RenameFilesParams
3133import org.eclipse.lsp4j.TextDocumentIdentifier
3234import org.eclipse.lsp4j.TextDocumentItem
@@ -37,20 +39,22 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.FileUriUtil.toU
3739import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.WorkspaceFolderUtil.createWorkspaceFolders
3840import software.aws.toolkits.jetbrains.utils.pluginAwareExecuteOnPooledThread
3941import java.nio.file.FileSystems
42+ import java.nio.file.PathMatcher
4043import java.nio.file.Paths
4144
4245class WorkspaceServiceHandler (
4346 private val project : Project ,
47+ initializeResult : InitializeResult ,
4448 serverInstance : Disposable ,
4549) : BulkFileListener,
4650 ModuleRootListener {
4751
4852 private var lastSnapshot: List <WorkspaceFolder > = emptyList()
49- private val supportedFilePatterns = FileSystems .getDefault().getPathMatcher(
50- " glob:**/*.{ts,js,py,java}"
51- )
53+ private val operationMatchers: MutableMap <FileOperationType , List <Pair <PathMatcher , String >>> = mutableMapOf ()
5254
5355 init {
56+ operationMatchers.putAll(initializePatterns(initializeResult))
57+
5458 project.messageBus.connect(serverInstance).subscribe(
5559 VirtualFileManager .VFS_CHANGES ,
5660 this
@@ -62,12 +66,46 @@ class WorkspaceServiceHandler(
6266 )
6367 }
6468
69+ enum class FileOperationType {
70+ CREATE ,
71+ DELETE ,
72+ RENAME ,
73+ }
74+
75+ private fun initializePatterns (initializeResult : InitializeResult ): Map <FileOperationType , List <Pair <PathMatcher , String >>> {
76+ val patterns = mutableMapOf<FileOperationType , List <Pair <PathMatcher , String >>>()
77+
78+ initializeResult.capabilities?.workspace?.fileOperations?.let { fileOps ->
79+ patterns[FileOperationType .CREATE ] = createMatchers(fileOps.didCreate?.filters)
80+ patterns[FileOperationType .DELETE ] = createMatchers(fileOps.didDelete?.filters)
81+ patterns[FileOperationType .RENAME ] = createMatchers(fileOps.didRename?.filters)
82+ }
83+
84+ return patterns
85+ }
86+
87+ private fun createMatchers (filters : List <FileOperationFilter >? ): List <Pair <PathMatcher , String >> =
88+ filters?.map { filter ->
89+ FileSystems .getDefault().getPathMatcher(" glob:${filter.pattern.glob} " ) to filter.pattern.matches
90+ }.orEmpty()
91+
92+ private fun shouldHandleFile (file : VirtualFile , operation : FileOperationType ): Boolean {
93+ val matchers = operationMatchers[operation] ? : return false
94+ return matchers.any { (matcher, type) ->
95+ when (type) {
96+ " file" -> ! file.isDirectory && matcher.matches(Paths .get(file.path))
97+ " folder" -> file.isDirectory && matcher.matches(Paths .get(file.path))
98+ else -> matcher.matches(Paths .get(file.path))
99+ }
100+ }
101+ }
102+
65103 private fun didCreateFiles (events : List <VFileEvent >) {
66104 AmazonQLspService .executeIfRunning(project) { languageServer ->
67105 val validFiles = events.mapNotNull { event ->
68106 when (event) {
69107 is VFileCopyEvent -> {
70- val newFile = event.newParent.findChild(event.newChildName)?.takeIf { shouldHandleFile(it) }
108+ val newFile = event.newParent.findChild(event.newChildName)?.takeIf { shouldHandleFile(it, FileOperationType . CREATE ) }
71109 ? : return @mapNotNull null
72110 toUriString(newFile)?.let { uri ->
73111 FileCreate ().apply {
@@ -76,7 +114,7 @@ class WorkspaceServiceHandler(
76114 }
77115 }
78116 else -> {
79- val file = event.file?.takeIf { shouldHandleFile(it) }
117+ val file = event.file?.takeIf { shouldHandleFile(it, FileOperationType . CREATE ) }
80118 ? : return @mapNotNull null
81119 toUriString(file)?.let { uri ->
82120 FileCreate ().apply {
@@ -102,11 +140,11 @@ class WorkspaceServiceHandler(
102140 val validFiles = events.mapNotNull { event ->
103141 when (event) {
104142 is VFileDeleteEvent -> {
105- val file = event.file.takeIf { shouldHandleFile(it) } ? : return @mapNotNull null
143+ val file = event.file.takeIf { shouldHandleFile(it, FileOperationType . DELETE ) } ? : return @mapNotNull null
106144 toUriString(file)
107145 }
108146 is VFileMoveEvent -> {
109- val oldFile = event.oldParent?.takeIf { shouldHandleFile(it) } ? : return @mapNotNull null
147+ val oldFile = event.oldParent?.takeIf { shouldHandleFile(it, FileOperationType . DELETE ) } ? : return @mapNotNull null
110148 toUriString(oldFile)
111149 }
112150 else -> null
@@ -132,7 +170,7 @@ class WorkspaceServiceHandler(
132170 val validRenames = events
133171 .filter { it.propertyName == VirtualFile .PROP_NAME }
134172 .mapNotNull { event ->
135- val renamedFile = event.file.takeIf { shouldHandleFile(it) } ? : return @mapNotNull null
173+ val renamedFile = event.file.takeIf { shouldHandleFile(it, FileOperationType . RENAME ) } ? : return @mapNotNull null
136174 val oldFileName = event.oldValue as ? String ? : return @mapNotNull null
137175 val parentFile = renamedFile.parent ? : return @mapNotNull null
138176
@@ -275,13 +313,4 @@ class WorkspaceServiceHandler(
275313 lastSnapshot = currentSnapshot
276314 }
277315 }
278-
279- private fun shouldHandleFile (file : VirtualFile ): Boolean {
280- if (file.isDirectory) {
281- return true // Matches "**/*" with matches: "folder"
282- }
283- val path = Paths .get(file.path)
284- val result = supportedFilePatterns.matches(path)
285- return result
286- }
287316}
0 commit comments