(s string, dirOnly bool, escape, unescape func(string) string)
| 240 | } |
| 241 | |
| 242 | func matchFile(s string, dirOnly bool, escape, unescape func(string) string) (matches []compMatch, longest string) { |
| 243 | dir, file := filepath.Split(unescape(replaceTilde(s))) |
| 244 | |
| 245 | d := dir |
| 246 | if dir == "" { |
| 247 | d = "." |
| 248 | } |
| 249 | files, err := os.ReadDir(d) |
| 250 | if err != nil { |
| 251 | log.Printf("reading directory: %s", err) |
| 252 | longest = s |
| 253 | return |
| 254 | } |
| 255 | |
| 256 | var longestName string |
| 257 | |
| 258 | for _, f := range files { |
| 259 | isDir := false |
| 260 | if f.IsDir() { |
| 261 | isDir = true |
| 262 | } else if f.Type()&os.ModeSymlink != 0 { |
| 263 | if stat, err := os.Stat(filepath.Join(d, f.Name())); err == nil && stat.IsDir() { |
| 264 | isDir = true |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | if !isDir && dirOnly { |
| 269 | continue |
| 270 | } |
| 271 | |
| 272 | if !strings.HasPrefix(strings.ToLower(f.Name()), strings.ToLower(file)) { |
| 273 | continue |
| 274 | } |
| 275 | |
| 276 | name := f.Name() |
| 277 | if isDir { |
| 278 | name += string(filepath.Separator) |
| 279 | } |
| 280 | matches = append(matches, compMatch{name, escape(dir + name)}) |
| 281 | |
| 282 | if len(matches) == 1 { |
| 283 | longestName = name |
| 284 | } else { |
| 285 | // Match case-insensitively without changing the prefix's case. |
| 286 | p := getLongest(strings.ToLower(longestName), strings.ToLower(name)) |
| 287 | longestName = string([]rune(longestName)[:len([]rune(p))]) |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | switch len(matches) { |
| 292 | case 0: |
| 293 | longest = s |
| 294 | case 1: |
| 295 | longest = escape(dir + longestName) |
| 296 | if !strings.HasSuffix(longestName, string(filepath.Separator)) { |
| 297 | longest += " " |
| 298 | } |
| 299 | default: |
no test coverage detected