Deduplicate findReadmeFile() (#22177)
This code was copy-pasted at some point. Revisit it to reunify it. ~~Doing that then encouraged simplifying the types of a couple of related functions.~~ ~~As a follow-up, move two helper functions, `isReadmeFile()` and `isReadmeFileExtension()`, intimately tied to `findReadmeFile()`, in as package-private.~~ Signed-off-by: Nick Guenther <nick.guenther@polymtl.ca>
This commit is contained in:
parent
b0f18726a3
commit
e1aca7cbdd
2 changed files with 78 additions and 98 deletions
|
@ -101,6 +101,15 @@ func (te *TreeEntry) FollowLinks() (*TreeEntry, error) {
|
||||||
return entry, nil
|
return entry, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns the subtree, or nil if this is not a tree
|
||||||
|
func (te *TreeEntry) Tree() *Tree {
|
||||||
|
t, err := te.ptree.repo.getTree(te.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
// GetSubJumpablePathName return the full path of subdirectory jumpable ( contains only one directory )
|
// GetSubJumpablePathName return the full path of subdirectory jumpable ( contains only one directory )
|
||||||
func (te *TreeEntry) GetSubJumpablePathName() string {
|
func (te *TreeEntry) GetSubJumpablePathName() string {
|
||||||
if te.IsSubModule() || !te.IsDir() {
|
if te.IsSubModule() || !te.IsDir() {
|
||||||
|
|
|
@ -56,18 +56,15 @@ type namedBlob struct {
|
||||||
blob *git.Blob
|
blob *git.Blob
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// locate a README for a tree in one of the supported paths.
|
||||||
|
//
|
||||||
|
// entries is passed to reduce calls to ListEntries(), so
|
||||||
|
// this has precondition:
|
||||||
|
//
|
||||||
|
// entries == ctx.Repo.Commit.SubTree(ctx.Repo.TreePath).ListEntries()
|
||||||
|
//
|
||||||
// FIXME: There has to be a more efficient way of doing this
|
// FIXME: There has to be a more efficient way of doing this
|
||||||
func getReadmeFileFromPath(ctx *context.Context, commit *git.Commit, treePath string) (*namedBlob, error) {
|
func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry) (*namedBlob, error) {
|
||||||
tree, err := commit.SubTree(treePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
entries, err := tree.ListEntries()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a list of extensions in priority order
|
// Create a list of extensions in priority order
|
||||||
// 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
|
// 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
|
||||||
// 2. Txt files - e.g. README.txt
|
// 2. Txt files - e.g. README.txt
|
||||||
|
@ -75,16 +72,38 @@ func getReadmeFileFromPath(ctx *context.Context, commit *git.Commit, treePath st
|
||||||
exts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority
|
exts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority
|
||||||
extCount := len(exts)
|
extCount := len(exts)
|
||||||
readmeFiles := make([]*namedBlob, extCount+1)
|
readmeFiles := make([]*namedBlob, extCount+1)
|
||||||
|
|
||||||
|
docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/)
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
|
// as a special case for the top-level repo introduction README,
|
||||||
|
// fall back to subfolders, looking for e.g. docs/README.md, .gitea/README.zh-CN.txt, .github/README.txt, ...
|
||||||
|
// (note that docsEntries is ignored unless we are at the root)
|
||||||
|
lowerName := strings.ToLower(entry.Name())
|
||||||
|
switch lowerName {
|
||||||
|
case "docs":
|
||||||
|
if entry.Name() == "docs" || docsEntries[0] == nil {
|
||||||
|
docsEntries[0] = entry
|
||||||
|
}
|
||||||
|
case ".gitea":
|
||||||
|
if entry.Name() == ".gitea" || docsEntries[1] == nil {
|
||||||
|
docsEntries[1] = entry
|
||||||
|
}
|
||||||
|
case ".github":
|
||||||
|
if entry.Name() == ".github" || docsEntries[2] == nil {
|
||||||
|
docsEntries[2] = entry
|
||||||
|
}
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok {
|
if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok {
|
||||||
|
log.Debug("Potential readme file: %s", entry.Name())
|
||||||
if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].name, entry.Blob().Name()) {
|
if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].name, entry.Blob().Name()) {
|
||||||
name := entry.Name()
|
name := entry.Name()
|
||||||
isSymlink := entry.IsLink()
|
isSymlink := entry.IsLink()
|
||||||
target := entry
|
target := entry
|
||||||
if isSymlink {
|
if isSymlink {
|
||||||
|
var err error
|
||||||
target, err = entry.FollowLinks()
|
target, err = entry.FollowLinks()
|
||||||
if err != nil && !git.IsErrBadLink(err) {
|
if err != nil && !git.IsErrBadLink(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -107,6 +126,33 @@ func getReadmeFileFromPath(ctx *context.Context, commit *git.Commit, treePath st
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.Repo.TreePath == "" && readmeFile == nil {
|
||||||
|
for _, subTreeEntry := range docsEntries {
|
||||||
|
if subTreeEntry == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subTree := subTreeEntry.Tree()
|
||||||
|
if subTree == nil {
|
||||||
|
// this should be impossible; if subTreeEntry exists so should this.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
childEntries, err := subTree.ListEntries()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
readmeFile, err = findReadmeFileInEntries(ctx, childEntries)
|
||||||
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if readmeFile != nil {
|
||||||
|
readmeFile.name = subTreeEntry.Name() + "/" + readmeFile.name
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return readmeFile, nil
|
return readmeFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,12 +173,20 @@ func renderDirectory(ctx *context.Context, treeLink string) {
|
||||||
ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled && !ctx.Repo.Repository.IsArchived
|
ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled && !ctx.Repo.Repository.IsArchived
|
||||||
}
|
}
|
||||||
|
|
||||||
readmeFile, readmeTreelink := findReadmeFile(ctx, entries, treeLink)
|
if ctx.Written() {
|
||||||
if ctx.Written() || readmeFile == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
renderReadmeFile(ctx, readmeFile, readmeTreelink)
|
readmeFile, err := findReadmeFileInEntries(ctx, entries)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("findReadmeFileInEntries", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if readmeFile == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderReadmeFile(ctx, readmeFile, treeLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
// localizedExtensions prepends the provided language code with and without a
|
// localizedExtensions prepends the provided language code with and without a
|
||||||
|
@ -157,89 +211,6 @@ func localizedExtensions(ext, languageCode string) (localizedExts []string) {
|
||||||
return []string{lowerLangCode + ext, ext}
|
return []string{lowerLangCode + ext, ext}
|
||||||
}
|
}
|
||||||
|
|
||||||
func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string) (*namedBlob, string) {
|
|
||||||
// Create a list of extensions in priority order
|
|
||||||
// 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
|
|
||||||
// 2. Txt files - e.g. README.txt
|
|
||||||
// 3. No extension - e.g. README
|
|
||||||
exts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority
|
|
||||||
extCount := len(exts)
|
|
||||||
readmeFiles := make([]*namedBlob, extCount+1)
|
|
||||||
|
|
||||||
docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/)
|
|
||||||
for _, entry := range entries {
|
|
||||||
if entry.IsDir() {
|
|
||||||
lowerName := strings.ToLower(entry.Name())
|
|
||||||
switch lowerName {
|
|
||||||
case "docs":
|
|
||||||
if entry.Name() == "docs" || docsEntries[0] == nil {
|
|
||||||
docsEntries[0] = entry
|
|
||||||
}
|
|
||||||
case ".gitea":
|
|
||||||
if entry.Name() == ".gitea" || docsEntries[1] == nil {
|
|
||||||
docsEntries[1] = entry
|
|
||||||
}
|
|
||||||
case ".github":
|
|
||||||
if entry.Name() == ".github" || docsEntries[2] == nil {
|
|
||||||
docsEntries[2] = entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok {
|
|
||||||
log.Debug("Potential readme file: %s", entry.Name())
|
|
||||||
name := entry.Name()
|
|
||||||
isSymlink := entry.IsLink()
|
|
||||||
target := entry
|
|
||||||
if isSymlink {
|
|
||||||
var err error
|
|
||||||
target, err = entry.FollowLinks()
|
|
||||||
if err != nil && !git.IsErrBadLink(err) {
|
|
||||||
ctx.ServerError("FollowLinks", err)
|
|
||||||
return nil, ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if target != nil && (target.IsExecutable() || target.IsRegular()) {
|
|
||||||
readmeFiles[i] = &namedBlob{
|
|
||||||
name,
|
|
||||||
isSymlink,
|
|
||||||
target.Blob(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var readmeFile *namedBlob
|
|
||||||
readmeTreelink := treeLink
|
|
||||||
for _, f := range readmeFiles {
|
|
||||||
if f != nil {
|
|
||||||
readmeFile = f
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Repo.TreePath == "" && readmeFile == nil {
|
|
||||||
for _, entry := range docsEntries {
|
|
||||||
if entry == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
readmeFile, err = getReadmeFileFromPath(ctx, ctx.Repo.Commit, entry.GetSubJumpablePathName())
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("getReadmeFileFromPath", err)
|
|
||||||
return nil, ""
|
|
||||||
}
|
|
||||||
if readmeFile != nil {
|
|
||||||
readmeFile.name = entry.Name() + "/" + readmeFile.name
|
|
||||||
readmeTreelink = treeLink + "/" + util.PathEscapeSegments(entry.GetSubJumpablePathName())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return readmeFile, readmeTreelink
|
|
||||||
}
|
|
||||||
|
|
||||||
type fileInfo struct {
|
type fileInfo struct {
|
||||||
isTextFile bool
|
isTextFile bool
|
||||||
isLFSFile bool
|
isLFSFile bool
|
||||||
|
@ -342,7 +313,7 @@ func renderReadmeFile(ctx *context.Context, readmeFile *namedBlob, readmeTreelin
|
||||||
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
|
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
RelativePath: path.Join(ctx.Repo.TreePath, readmeFile.name), // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path).
|
RelativePath: path.Join(ctx.Repo.TreePath, readmeFile.name), // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path).
|
||||||
URLPrefix: readmeTreelink,
|
URLPrefix: path.Dir(readmeTreelink),
|
||||||
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
}, rd)
|
}, rd)
|
||||||
|
|
Loading…
Reference in a new issue