X-Git-Url: https://git.josue.xyz/?a=blobdiff_plain;f=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%40v0.1.1-0.20210319172145-bda8f5cee399%2Fgodoc%2Fvfs%2Fnamespace.go;fp=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%40v0.1.1-0.20210319172145-bda8f5cee399%2Fgodoc%2Fvfs%2Fnamespace.go;h=32c82599ddcc64d07b11304b80c56c2d7803dad2;hb=3c06164f15bd10aed7d66b6314764a2961a14762;hp=0000000000000000000000000000000000000000;hpb=0e9c3ceb40901f4d44981c1376cb9e23a248e006;p=dotfiles%2F.git diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/godoc/vfs/namespace.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/godoc/vfs/namespace.go new file mode 100644 index 00000000..32c82599 --- /dev/null +++ b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/godoc/vfs/namespace.go @@ -0,0 +1,389 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vfs + +import ( + "fmt" + "io" + "os" + pathpkg "path" + "sort" + "strings" + "time" +) + +// Setting debugNS = true will enable debugging prints about +// name space translations. +const debugNS = false + +// A NameSpace is a file system made up of other file systems +// mounted at specific locations in the name space. +// +// The representation is a map from mount point locations +// to the list of file systems mounted at that location. A traditional +// Unix mount table would use a single file system per mount point, +// but we want to be able to mount multiple file systems on a single +// mount point and have the system behave as if the union of those +// file systems were present at the mount point. +// For example, if the OS file system has a Go installation in +// c:\Go and additional Go path trees in d:\Work1 and d:\Work2, then +// this name space creates the view we want for the godoc server: +// +// NameSpace{ +// "/": { +// {old: "/", fs: OS(`c:\Go`), new: "/"}, +// }, +// "/src/pkg": { +// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"}, +// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"}, +// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"}, +// }, +// } +// +// This is created by executing: +// +// ns := NameSpace{} +// ns.Bind("/", OS(`c:\Go`), "/", BindReplace) +// ns.Bind("/src/pkg", OS(`d:\Work1`), "/src", BindAfter) +// ns.Bind("/src/pkg", OS(`d:\Work2`), "/src", BindAfter) +// +// A particular mount point entry is a triple (old, fs, new), meaning that to +// operate on a path beginning with old, replace that prefix (old) with new +// and then pass that path to the FileSystem implementation fs. +// +// If you do not explicitly mount a FileSystem at the root mountpoint "/" of the +// NameSpace like above, Stat("/") will return a "not found" error which could +// break typical directory traversal routines. In such cases, use NewNameSpace() +// to get a NameSpace pre-initialized with an emulated empty directory at root. +// +// Given this name space, a ReadDir of /src/pkg/code will check each prefix +// of the path for a mount point (first /src/pkg/code, then /src/pkg, then /src, +// then /), stopping when it finds one. For the above example, /src/pkg/code +// will find the mount point at /src/pkg: +// +// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"}, +// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"}, +// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"}, +// +// ReadDir will when execute these three calls and merge the results: +// +// OS(`c:\Go`).ReadDir("/src/pkg/code") +// OS(`d:\Work1').ReadDir("/src/code") +// OS(`d:\Work2').ReadDir("/src/code") +// +// Note that the "/src/pkg" in "/src/pkg/code" has been replaced by +// just "/src" in the final two calls. +// +// OS is itself an implementation of a file system: it implements +// OS(`c:\Go`).ReadDir("/src/pkg/code") as ioutil.ReadDir(`c:\Go\src\pkg\code`). +// +// Because the new path is evaluated by fs (here OS(root)), another way +// to read the mount table is to mentally combine fs+new, so that this table: +// +// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"}, +// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"}, +// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"}, +// +// reads as: +// +// "/src/pkg" -> c:\Go\src\pkg +// "/src/pkg" -> d:\Work1\src +// "/src/pkg" -> d:\Work2\src +// +// An invariant (a redundancy) of the name space representation is that +// ns[mtpt][i].old is always equal to mtpt (in the example, ns["/src/pkg"]'s +// mount table entries always have old == "/src/pkg"). The 'old' field is +// useful to callers, because they receive just a []mountedFS and not any +// other indication of which mount point was found. +// +type NameSpace map[string][]mountedFS + +// A mountedFS handles requests for path by replacing +// a prefix 'old' with 'new' and then calling the fs methods. +type mountedFS struct { + old string + fs FileSystem + new string +} + +// hasPathPrefix reports whether x == y or x == y + "/" + more. +func hasPathPrefix(x, y string) bool { + return x == y || strings.HasPrefix(x, y) && (strings.HasSuffix(y, "/") || strings.HasPrefix(x[len(y):], "/")) +} + +// translate translates path for use in m, replacing old with new. +// +// mountedFS{"/src/pkg", fs, "/src"}.translate("/src/pkg/code") == "/src/code". +func (m mountedFS) translate(path string) string { + path = pathpkg.Clean("/" + path) + if !hasPathPrefix(path, m.old) { + panic("translate " + path + " but old=" + m.old) + } + return pathpkg.Join(m.new, path[len(m.old):]) +} + +func (NameSpace) String() string { + return "ns" +} + +// Fprint writes a text representation of the name space to w. +func (ns NameSpace) Fprint(w io.Writer) { + fmt.Fprint(w, "name space {\n") + var all []string + for mtpt := range ns { + all = append(all, mtpt) + } + sort.Strings(all) + for _, mtpt := range all { + fmt.Fprintf(w, "\t%s:\n", mtpt) + for _, m := range ns[mtpt] { + fmt.Fprintf(w, "\t\t%s %s\n", m.fs, m.new) + } + } + fmt.Fprint(w, "}\n") +} + +// clean returns a cleaned, rooted path for evaluation. +// It canonicalizes the path so that we can use string operations +// to analyze it. +func (NameSpace) clean(path string) string { + return pathpkg.Clean("/" + path) +} + +type BindMode int + +const ( + BindReplace BindMode = iota + BindBefore + BindAfter +) + +// Bind causes references to old to redirect to the path new in newfs. +// If mode is BindReplace, old redirections are discarded. +// If mode is BindBefore, this redirection takes priority over existing ones, +// but earlier ones are still consulted for paths that do not exist in newfs. +// If mode is BindAfter, this redirection happens only after existing ones +// have been tried and failed. +func (ns NameSpace) Bind(old string, newfs FileSystem, new string, mode BindMode) { + old = ns.clean(old) + new = ns.clean(new) + m := mountedFS{old, newfs, new} + var mtpt []mountedFS + switch mode { + case BindReplace: + mtpt = append(mtpt, m) + case BindAfter: + mtpt = append(mtpt, ns.resolve(old)...) + mtpt = append(mtpt, m) + case BindBefore: + mtpt = append(mtpt, m) + mtpt = append(mtpt, ns.resolve(old)...) + } + + // Extend m.old, m.new in inherited mount point entries. + for i := range mtpt { + m := &mtpt[i] + if m.old != old { + if !hasPathPrefix(old, m.old) { + // This should not happen. If it does, panic so + // that we can see the call trace that led to it. + panic(fmt.Sprintf("invalid Bind: old=%q m={%q, %s, %q}", old, m.old, m.fs.String(), m.new)) + } + suffix := old[len(m.old):] + m.old = pathpkg.Join(m.old, suffix) + m.new = pathpkg.Join(m.new, suffix) + } + } + + ns[old] = mtpt +} + +// resolve resolves a path to the list of mountedFS to use for path. +func (ns NameSpace) resolve(path string) []mountedFS { + path = ns.clean(path) + for { + if m := ns[path]; m != nil { + if debugNS { + fmt.Printf("resolve %s: %v\n", path, m) + } + return m + } + if path == "/" { + break + } + path = pathpkg.Dir(path) + } + return nil +} + +// Open implements the FileSystem Open method. +func (ns NameSpace) Open(path string) (ReadSeekCloser, error) { + var err error + for _, m := range ns.resolve(path) { + if debugNS { + fmt.Printf("tx %s: %v\n", path, m.translate(path)) + } + tp := m.translate(path) + r, err1 := m.fs.Open(tp) + if err1 == nil { + return r, nil + } + // IsNotExist errors in overlay FSes can mask real errors in + // the underlying FS, so ignore them if there is another error. + if err == nil || os.IsNotExist(err) { + err = err1 + } + } + if err == nil { + err = &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist} + } + return nil, err +} + +// stat implements the FileSystem Stat and Lstat methods. +func (ns NameSpace) stat(path string, f func(FileSystem, string) (os.FileInfo, error)) (os.FileInfo, error) { + var err error + for _, m := range ns.resolve(path) { + fi, err1 := f(m.fs, m.translate(path)) + if err1 == nil { + return fi, nil + } + if err == nil { + err = err1 + } + } + if err == nil { + err = &os.PathError{Op: "stat", Path: path, Err: os.ErrNotExist} + } + return nil, err +} + +func (ns NameSpace) Stat(path string) (os.FileInfo, error) { + return ns.stat(path, FileSystem.Stat) +} + +func (ns NameSpace) Lstat(path string) (os.FileInfo, error) { + return ns.stat(path, FileSystem.Lstat) +} + +// dirInfo is a trivial implementation of os.FileInfo for a directory. +type dirInfo string + +func (d dirInfo) Name() string { return string(d) } +func (d dirInfo) Size() int64 { return 0 } +func (d dirInfo) Mode() os.FileMode { return os.ModeDir | 0555 } +func (d dirInfo) ModTime() time.Time { return startTime } +func (d dirInfo) IsDir() bool { return true } +func (d dirInfo) Sys() interface{} { return nil } + +var startTime = time.Now() + +// ReadDir implements the FileSystem ReadDir method. It's where most of the magic is. +// (The rest is in resolve.) +// +// Logically, ReadDir must return the union of all the directories that are named +// by path. In order to avoid misinterpreting Go packages, of all the directories +// that contain Go source code, we only include the files from the first, +// but we include subdirectories from all. +// +// ReadDir must also return directory entries needed to reach mount points. +// If the name space looks like the example in the type NameSpace comment, +// but c:\Go does not have a src/pkg subdirectory, we still want to be able +// to find that subdirectory, because we've mounted d:\Work1 and d:\Work2 +// there. So if we don't see "src" in the directory listing for c:\Go, we add an +// entry for it before returning. +// +func (ns NameSpace) ReadDir(path string) ([]os.FileInfo, error) { + path = ns.clean(path) + + // List matching directories and determine whether any of them contain + // Go files. + var ( + dirs [][]os.FileInfo + goDirIndex = -1 + readDirErr error + ) + + for _, m := range ns.resolve(path) { + dir, err := m.fs.ReadDir(m.translate(path)) + if err != nil { + if readDirErr == nil { + readDirErr = err + } + continue + } + + dirs = append(dirs, dir) + + if goDirIndex < 0 { + for _, f := range dir { + if !f.IsDir() && strings.HasSuffix(f.Name(), ".go") { + goDirIndex = len(dirs) - 1 + break + } + } + } + } + + // Build a list of files and subdirectories. If a directory contains Go files, + // only include files from that directory. Otherwise, include files from + // all directories. Include subdirectories from all directories regardless + // of whether Go files are present. + haveName := make(map[string]bool) + var all []os.FileInfo + for i, dir := range dirs { + for _, f := range dir { + name := f.Name() + if !haveName[name] && (f.IsDir() || goDirIndex < 0 || goDirIndex == i) { + all = append(all, f) + haveName[name] = true + } + } + } + + // Add any missing directories needed to reach mount points. + for old := range ns { + if hasPathPrefix(old, path) && old != path { + // Find next element after path in old. + elem := old[len(path):] + elem = strings.TrimPrefix(elem, "/") + if i := strings.Index(elem, "/"); i >= 0 { + elem = elem[:i] + } + if !haveName[elem] { + haveName[elem] = true + all = append(all, dirInfo(elem)) + } + } + } + + if len(all) == 0 { + return nil, readDirErr + } + + sort.Sort(byName(all)) + return all, nil +} + +// RootType returns the RootType for the given path in the namespace. +func (ns NameSpace) RootType(path string) RootType { + // We resolve the given path to a list of mountedFS and then return + // the root type for the filesystem which contains the path. + for _, m := range ns.resolve(path) { + _, err := m.fs.ReadDir(m.translate(path)) + // Found a match, return the filesystem's root type + if err == nil { + return m.fs.RootType(path) + } + } + return "" +} + +// byName implements sort.Interface. +type byName []os.FileInfo + +func (f byName) Len() int { return len(f) } +func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() } +func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }