@@ -450,6 +450,41 @@ function! projectionist#query_file(key, ...) abort
450
450
return s: uniq (files )
451
451
endfunction
452
452
453
+ let s: projectionist_max_file_recursion = 3
454
+
455
+ function ! s: query_file_recursive (key , ... ) abort
456
+ let keys = type (a: key ) == type ([]) ? a: key : [a: key ]
457
+ let start_file = get (a: 0 ? a: 1 : {}, ' file' , get (b: , ' projectionist_file' , expand (' %:p' )))
458
+ let files = []
459
+ let visited_files = {start_file: 1 }
460
+ let current_files = [start_file]
461
+ let depth = 0
462
+ while ! empty (current_files) && depth < s: projectionist_max_file_recursion
463
+ let next_files = []
464
+ for file in current_files
465
+ let query_opts = extend (a: 0 ? copy (a: 1 ) : {}, {' file' : file })
466
+ for key in keys
467
+ let [root, match ] = get (projectionist#query (key , query_opts), 0 , [' ' , []])
468
+ let subfiles = type (match ) == type ([]) ? copy (match ) : [match ]
469
+ call map (filter (subfiles, ' len(v:val)' ), ' s:absolute(v:val, root)' )
470
+ if ! empty (subfiles)
471
+ break
472
+ endif
473
+ endfor
474
+ for subfile in subfiles
475
+ if ! has_key (visited_files, subfile)
476
+ let visited_files[subfile] = 1
477
+ call add (files , subfile)
478
+ call add (next_files, subfile)
479
+ endif
480
+ endfor
481
+ endfor
482
+ let current_files = next_files
483
+ let depth += 1
484
+ endwhile
485
+ return files
486
+ endfunction
487
+
453
488
function ! s: shelljoin (val) abort
454
489
return substitute (s: join (a: val ), ' ["'' ]\([{}]\)["'' ]' , ' \1' , ' g' )
455
490
endfunction
@@ -674,6 +709,38 @@ function! projectionist#navigation_commands() abort
674
709
return commands
675
710
endfunction
676
711
712
+ function ! s: find_related_file (patterns) abort
713
+ let alternates = s: query_file_recursive ([' related' , ' alternate' ], {' lnum' : 0 })
714
+ for alternate in alternates
715
+ for pattern in a: patterns
716
+ if ! empty (s: match (alternate, pattern))
717
+ return alternate
718
+ endif
719
+ endfor
720
+ endfor
721
+ let current_file = get (b: , ' projectionist_file' , expand (' %:p' ))
722
+ for pattern in a: patterns
723
+ if pattern !~# ' \*'
724
+ continue
725
+ endif
726
+ for candidate in projectionist#glob (pattern)
727
+ let candidate_alternates = s: query_file_recursive (
728
+ \ [' related' , ' alternate' ],
729
+ \ {' lnum' : 0 , ' file' : candidate})
730
+ for candidate_alternate in candidate_alternates
731
+ if candidate_alternate == # current_file
732
+ return candidate
733
+ endif
734
+ for alternate in alternates
735
+ if alternate == # candidate_alternate
736
+ return candidate
737
+ endif
738
+ endfor
739
+ endfor
740
+ endfor
741
+ endfor
742
+ endfunction
743
+
677
744
function ! s: open_projection (mods, edit , variants, ... ) abort
678
745
let formats = []
679
746
for variant in a: variants
@@ -692,7 +759,12 @@ function! s:open_projection(mods, edit, variants, ...) abort
692
759
let base = matchstr (name, ' [^\/]*$' )
693
760
call map (formats, ' substitute(substitute(v:val, "\\*\\*\\([\\/]\\=\\)", empty(dir) ? "" : dir . "\\1", ""), "\\*", base, "")' )
694
761
else
695
- call filter (formats, ' v:val !~# "\\*"' )
762
+ let related_file = s: find_related_file (formats)
763
+ if ! empty (related_file)
764
+ let formats = [related_file]
765
+ else
766
+ call filter (formats, ' v:val !~# "\\*"' )
767
+ endif
696
768
endif
697
769
if empty (formats)
698
770
return ' echoerr "Invalid number of arguments"'
0 commit comments