mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	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:
		@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user