@@ -102,20 +102,32 @@ function getSourcedPathInfoFromNode({
102102 }
103103 }
104104
105- if ( argumentNode . type === 'word' ) {
105+ const strValue = TreeSitterUtil . resolveStaticString ( argumentNode )
106+ if ( strValue !== null ) {
106107 return {
107- sourcedPath : argumentNode . text ,
108+ sourcedPath : strValue ,
108109 }
109110 }
110111
111- if ( argumentNode . type === 'string' || argumentNode . type === 'raw_string' ) {
112- const children = argumentNode . namedChildren
113- if (
114- children . length === 0 ||
115- ( children . length === 1 && children [ 0 ] . type === 'string_content' )
116- ) {
112+ // Strip one leading dynamic section.
113+ if ( argumentNode . type === 'string' && argumentNode . namedChildren . length === 1 ) {
114+ const [ variableNode ] = argumentNode . namedChildren
115+ if ( TreeSitterUtil . isExpansion ( variableNode ) ) {
116+ const stringContents = argumentNode . text . slice ( 1 , - 1 )
117+ if ( stringContents . startsWith ( `${ variableNode . text } /` ) ) {
118+ return {
119+ sourcedPath : `.${ stringContents . slice ( variableNode . text . length ) } ` ,
120+ }
121+ }
122+ }
123+ }
124+
125+ if ( argumentNode . type === 'concatenation' ) {
126+ // Strip one leading dynamic section from a concatenation node.
127+ const sourcedPath = resolveSourceFromConcatenation ( argumentNode )
128+ if ( sourcedPath ) {
117129 return {
118- sourcedPath : argumentNode . text . slice ( 1 , - 1 ) ,
130+ sourcedPath,
119131 }
120132 }
121133 }
@@ -171,3 +183,49 @@ function resolveSourcedUri({
171183
172184 return null
173185}
186+
187+ /*
188+ * Resolves the source path from a concatenation node, stripping a leading dynamic directory segment.
189+ * Returns null if the source path can't be statically determined after stripping a segment.
190+ * Note: If a non-concatenation node is passed, null will be returned. This is likely a programmer error.
191+ */
192+ function resolveSourceFromConcatenation ( node : Parser . SyntaxNode ) : string | null {
193+ if ( node . type !== 'concatenation' ) return null
194+ const stringValue = TreeSitterUtil . resolveStaticString ( node )
195+ if ( stringValue !== null ) return stringValue // This string is fully static.
196+
197+ const values : string [ ] = [ ]
198+ // Since the string must begin with the variable, the variable must be in the first child.
199+ const [ firstNode , ...rest ] = node . namedChildren
200+ // The first child is static, this means one of the other children is not!
201+ if ( TreeSitterUtil . resolveStaticString ( firstNode ) !== null ) return null
202+
203+ // if the string is unquoted, the first child is the variable, so there's no more text in it.
204+ if ( ! TreeSitterUtil . isExpansion ( firstNode ) ) {
205+ if ( firstNode . namedChildCount > 1 ) return null // Only one variable is allowed.
206+ // Since the string must begin with the variable, the variable must be first child.
207+ const variableNode = firstNode . namedChildren [ 0 ] // Get the variable (quoted case)
208+ // This is command substitution!
209+ if ( ! TreeSitterUtil . isExpansion ( variableNode ) ) return null
210+ const stringContents = firstNode . text . slice ( 1 , - 1 )
211+ // The string doesn't start with the variable!
212+ if ( ! stringContents . startsWith ( variableNode . text ) ) return null
213+ // Get the remaining static portion the string
214+ values . push ( stringContents . slice ( variableNode . text . length ) )
215+ }
216+
217+ for ( const child of rest ) {
218+ const value = TreeSitterUtil . resolveStaticString ( child )
219+ // The other values weren't statically determinable!
220+ if ( value === null ) return null
221+ values . push ( value )
222+ }
223+
224+ // Join all our found static values together.
225+ const staticResult = values . join ( '' )
226+ // The path starts with slash, so trim the leading variable and replace with a dot
227+ if ( staticResult . startsWith ( '/' ) ) return `.${ staticResult } `
228+ // The path doesn't start with a slash, so it's invalid
229+ // PERF: can we fail earlier than this?
230+ return null
231+ }
0 commit comments