@@ -14,6 +14,7 @@ import org.jetbrains.yaml.psi.YAMLFile
1414import org.jetbrains.yaml.psi.YAMLKeyValue
1515import org.jetbrains.yaml.psi.YAMLMapping
1616import org.jetbrains.yaml.psi.YAMLScalar
17+ import org.jetbrains.yaml.psi.YAMLSequenceItem
1718
1819open class LetsReferenceContributor : PsiReferenceContributor () {
1920 override fun registerReferenceProviders (registrar : PsiReferenceRegistrar ) {
@@ -40,8 +41,54 @@ class LetsDependsReference(element: YAMLScalar) : PsiReferenceBase<YAMLScalar>(e
4041
4142 // Locate the command declaration in the same YAML file
4243 val yamlFile = myElement.containingFile as ? YAMLFile ? : return null
43- return PsiTreeUtil .findChildrenOfType(yamlFile, YAMLKeyValue ::class .java)
44+ val localCommand = PsiTreeUtil .findChildrenOfType(yamlFile, YAMLKeyValue ::class .java)
4445 .firstOrNull { it.keyText == commandName && it.parent is YAMLMapping }
46+
47+ if (localCommand != null ) {
48+ return localCommand
49+ }
50+
51+ // Search for the command in mixin files (with recursive support)
52+ return findCommandInMixins(yamlFile, commandName, mutableSetOf ())
53+ }
54+ /* *
55+ * Recursively searches for the given command name in mixin files.
56+ */
57+ private fun findCommandInMixins (yamlFile : YAMLFile , commandName : String , visitedFiles : MutableSet <YAMLFile >): PsiElement ? {
58+ if (! visitedFiles.add(yamlFile)) {
59+ return null // Prevent infinite recursion
60+ }
61+
62+ // If the current file is a mixin, retrieve the main lets.yaml file
63+ val mainConfigFile = findMainConfigFile(yamlFile) ? : return null
64+
65+ // Find the mixins key in the main lets.yaml file
66+ val mixinsKey = PsiTreeUtil .findChildrenOfType(mainConfigFile, YAMLKeyValue ::class .java)
67+ .firstOrNull { it.keyText == " mixins" } ? : return null
68+
69+ // Extract mixin file names from YAMLSequenceItems
70+ val mixinFiles = mixinsKey.value?.children
71+ ?.mapNotNull { it as ? YAMLSequenceItem }
72+ ?.mapNotNull { it.value as ? YAMLScalar }
73+ ?.mapNotNull { LetsMixinReference (it).resolve() as ? YAMLFile } ? : return null
74+
75+ // Search for the command in the resolved mixin files
76+ for (mixinFile in mixinFiles) {
77+ val command = PsiTreeUtil .findChildrenOfType(mixinFile, YAMLKeyValue ::class .java)
78+ .firstOrNull { it.keyText == commandName && it.parent is YAMLMapping }
79+
80+ if (command != null ) {
81+ return command
82+ }
83+
84+ // Recursively check mixins within this mixin
85+ val nestedCommand = findCommandInMixins(mixinFile, commandName, visitedFiles)
86+ if (nestedCommand != null ) {
87+ return nestedCommand
88+ }
89+ }
90+
91+ return null
4592 }
4693}
4794
@@ -81,12 +128,21 @@ class LetsMixinReference(element: YAMLScalar) : PsiReferenceBase<YAMLScalar>(ele
81128 private fun findMixinFile (project : Project , mixinPath : String ): VirtualFile ? {
82129 // Normalize paths (handle both "lets.mixin.yaml" and "lets/lets.mixin.yaml")
83130 val normalizedPath = mixinPath.trimStart(' /' )
84- // Normalize gitignored files (e.g. "-lets.mixin.yaml" -> "lets.mixin.yaml")
131+ // Normalize git-ignored files (e.g. "-lets.mixin.yaml" -> "lets.mixin.yaml")
85132 .removePrefix(" -" )
86133
87134 // Look for an exact match in the project
88135 return FilenameIndex .getVirtualFilesByName(
89136 PathUtil .getFileName(normalizedPath), GlobalSearchScope .allScope(project)
90137 ).firstOrNull { it.path.endsWith(normalizedPath) }
91138 }
139+ }
140+
141+ /* *
142+ * Finds the main lets.yaml configuration file, assuming it is located at the root.
143+ */
144+ private fun findMainConfigFile (currentFile : YAMLFile ): YAMLFile ? {
145+ val project = currentFile.project
146+ val mainFiles = FilenameIndex .getVirtualFilesByName(" lets.yaml" , GlobalSearchScope .projectScope(project))
147+ return mainFiles.mapNotNull { PsiManager .getInstance(project).findFile(it) as ? YAMLFile }.firstOrNull()
92148}
0 commit comments