github.com/xyproto/orbiton/v2@v2.65.12-0.20240516144430-e10a419274ec/filesearch.go (about) 1 package main 2 3 import ( 4 "errors" 5 "os" 6 "path/filepath" 7 "strings" 8 "time" 9 10 "github.com/xyproto/files" 11 ) 12 13 // Don't search for a corresponding header/source file for longer than ~0.5 seconds 14 var fileSearchMaxTime = 500 * time.Millisecond 15 16 // ExtFileSearch will search for a corresponding file, given a slice of extensions. 17 // This is useful for ie. finding a corresponding .h file for a .c file. 18 // The search starts in the current directory, then searches every parent directory in depth. 19 // TODO: Search sibling and parent directories named "include" first, then search the rest. 20 func ExtFileSearch(absCppFilename string, headerExtensions []string, maxTime time.Duration) (string, error) { 21 cppBasename := filepath.Base(absCppFilename) 22 searchPath := filepath.Dir(absCppFilename) 23 ext := filepath.Ext(cppBasename) 24 if ext == "" { 25 return "", errors.New("filename has no extension: " + cppBasename) 26 } 27 firstName := cppBasename[:len(cppBasename)-len(ext)] 28 29 // First search the same path as the given filename, without using Walk 30 withoutExt := strings.TrimSuffix(absCppFilename, ext) 31 for _, hext := range headerExtensions { 32 if files.Exists(withoutExt + hext) { 33 return withoutExt + hext, nil 34 } 35 } 36 37 var headerNames []string 38 for _, ext := range headerExtensions { 39 headerNames = append(headerNames, firstName+ext) 40 } 41 foundHeaderAbsPath := "" 42 startTime := time.Now() 43 for { 44 err := filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error { 45 basename := filepath.Base(info.Name()) 46 if err == nil { 47 // logf("Walking %s\n", path) 48 for _, headerName := range headerNames { 49 if time.Since(startTime) > maxTime { 50 return errors.New("file search timeout") 51 } 52 if basename == headerName { 53 // Found the corresponding header! 54 absFilename, err := filepath.Abs(path) 55 if err != nil { 56 continue 57 } 58 foundHeaderAbsPath = absFilename 59 // logf("Found %s!\n", absFilename) 60 return nil 61 } 62 } 63 } 64 // No result 65 return nil 66 }) 67 if err != nil { 68 return "", errors.New("error when searching for a corresponding header for " + cppBasename + ":" + err.Error()) 69 } 70 if len(foundHeaderAbsPath) == 0 { 71 // Try the parent directory 72 searchPath = filepath.Dir(searchPath) 73 if len(searchPath) > 2 { 74 continue 75 } 76 } 77 break 78 } 79 if len(foundHeaderAbsPath) == 0 { 80 return "", errors.New("found no corresponding header for " + cppBasename) 81 } 82 83 // Return the result 84 return foundHeaderAbsPath, nil 85 }