diff options
Diffstat (limited to 'helpers/path.go')
-rw-r--r-- | helpers/path.go | 228 |
1 files changed, 114 insertions, 114 deletions
diff --git a/helpers/path.go b/helpers/path.go index 36bd3269b..12ddfeb56 100644 --- a/helpers/path.go +++ b/helpers/path.go @@ -26,6 +26,8 @@ import ( "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/common/hugio" _errors "github.com/pkg/errors" "github.com/spf13/afero" @@ -172,32 +174,6 @@ func ReplaceExtension(path string, newExt string) string { return f + "." + newExt } -// GetFirstThemeDir gets the root directory of the first theme, if there is one. -// If there is no theme, returns the empty string. -func (p *PathSpec) GetFirstThemeDir() string { - if p.ThemeSet() { - return p.AbsPathify(filepath.Join(p.ThemesDir, p.Themes()[0])) - } - return "" -} - -// GetThemesDir gets the absolute root theme dir path. -func (p *PathSpec) GetThemesDir() string { - if p.ThemeSet() { - return p.AbsPathify(p.ThemesDir) - } - return "" -} - -// GetRelativeThemeDir gets the relative root directory of the current theme, if there is one. -// If there is no theme, returns the empty string. -func (p *PathSpec) GetRelativeThemeDir() string { - if p.ThemeSet() { - return strings.TrimPrefix(filepath.Join(p.ThemesDir, p.Themes()[0]), FilePathSeparator) - } - return "" -} - func makePathRelative(inPath string, possibleDirectories ...string) (string, error) { for _, currentPath := range possibleDirectories { @@ -379,6 +355,107 @@ func prettifyPath(in string, b filepathPathBridge) string { return b.Join(b.Dir(in), name, "index"+ext) } +type NamedSlice struct { + Name string + Slice []string +} + +func (n NamedSlice) String() string { + if len(n.Slice) == 0 { + return n.Name + } + return fmt.Sprintf("%s%s{%s}", n.Name, FilePathSeparator, strings.Join(n.Slice, ",")) +} + +func ExtractAndGroupRootPaths(paths []string) []NamedSlice { + if len(paths) == 0 { + return nil + } + + pathsCopy := make([]string, len(paths)) + hadSlashPrefix := strings.HasPrefix(paths[0], FilePathSeparator) + + for i, p := range paths { + pathsCopy[i] = strings.Trim(filepath.ToSlash(p), "/") + } + + sort.Strings(pathsCopy) + + pathsParts := make([][]string, len(pathsCopy)) + + for i, p := range pathsCopy { + pathsParts[i] = strings.Split(p, "/") + } + + var groups [][]string + + for i, p1 := range pathsParts { + c1 := -1 + + for j, p2 := range pathsParts { + if i == j { + continue + } + + c2 := -1 + + for i, v := range p1 { + if i >= len(p2) { + break + } + if v != p2[i] { + break + } + + c2 = i + } + + if c1 == -1 || (c2 != -1 && c2 < c1) { + c1 = c2 + } + } + + if c1 != -1 { + groups = append(groups, p1[:c1+1]) + } else { + groups = append(groups, p1) + } + } + + groupsStr := make([]string, len(groups)) + for i, g := range groups { + groupsStr[i] = strings.Join(g, "/") + } + + groupsStr = UniqueStringsSorted(groupsStr) + + var result []NamedSlice + + for _, g := range groupsStr { + name := filepath.FromSlash(g) + if hadSlashPrefix { + name = FilePathSeparator + name + } + ns := NamedSlice{Name: name} + for _, p := range pathsCopy { + if !strings.HasPrefix(p, g) { + continue + } + + p = strings.TrimPrefix(p, g) + if p != "" { + ns.Slice = append(ns.Slice, p) + } + } + + ns.Slice = UniqueStrings(ExtractRootPaths(ns.Slice)) + + result = append(result, ns) + } + + return result +} + // ExtractRootPaths extracts the root paths from the supplied list of paths. // The resulting root path will not contain any file separators, but there // may be duplicates. @@ -425,98 +502,21 @@ func FindCWD() (string, error) { return path, nil } -// SymbolicWalk is like filepath.Walk, but it supports the root being a -// symbolic link. It will still not follow symbolic links deeper down in -// the file structure. -func SymbolicWalk(fs afero.Fs, root string, walker filepath.WalkFunc) error { - - // Sanity check - if root != "" && len(root) < 4 { - return errors.New("path is too short") - } - - // Handle the root first - fileInfo, realPath, err := getRealFileInfo(fs, root) - - if err != nil { - return walker(root, nil, err) - } - - if !fileInfo.IsDir() { - return fmt.Errorf("cannot walk regular file %s", root) - } - - if err := walker(realPath, fileInfo, err); err != nil && err != filepath.SkipDir { - return err - } - - // Some of Hugo's filesystems represents an ordered root folder, i.e. project first, then theme folders. - // Make sure that order is preserved. afero.Walk will sort the directories down in the file tree, - // but we don't care about that. - rootContent, err := readDir(fs, root, false) - - if err != nil { - return walker(root, nil, err) - } - - for _, fi := range rootContent { - if err := afero.Walk(fs, filepath.Join(root, fi.Name()), walker); err != nil { - return err - } +// SymbolicWalk is like filepath.Walk, but it follows symbolic links. +func SymbolicWalk(fs afero.Fs, root string, walker hugofs.WalkFunc) error { + if _, isOs := fs.(*afero.OsFs); isOs { + // Mainly to track symlinks. + fs = hugofs.NewBaseFileDecorator(fs) } - return nil + w := hugofs.NewWalkway(hugofs.WalkwayConfig{ + Fs: fs, + Root: root, + WalkFn: walker, + }) -} - -func readDir(fs afero.Fs, dirname string, doSort bool) ([]os.FileInfo, error) { - f, err := fs.Open(dirname) - if err != nil { - return nil, err - } - list, err := f.Readdir(-1) - f.Close() - if err != nil { - return nil, err - } - if doSort { - sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() }) - } - return list, nil -} - -func getRealFileInfo(fs afero.Fs, path string) (os.FileInfo, string, error) { - fileInfo, err := LstatIfPossible(fs, path) - realPath := path - - if err != nil { - return nil, "", err - } - - if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink { - link, err := filepath.EvalSymlinks(path) - if err != nil { - return nil, "", _errors.Wrapf(err, "Cannot read symbolic link %q", path) - } - fileInfo, err = LstatIfPossible(fs, link) - if err != nil { - return nil, "", _errors.Wrapf(err, "Cannot stat %q", link) - } - realPath = link - } - return fileInfo, realPath, nil -} - -// GetRealPath returns the real file path for the given path, whether it is a -// symlink or not. -func GetRealPath(fs afero.Fs, path string) (string, error) { - _, realPath, err := getRealFileInfo(fs, path) - - if err != nil { - return "", err - } + return w.Walk() - return realPath, nil } // LstatIfPossible can be used to call Lstat if possible, else Stat. |