···11package markup2233-import "strings"33+import (44+ "regexp"55+)4657type Format string68···1210)13111412var FileTypes map[Format][]string = map[Format][]string{1515- FormatMarkdown: []string{".md", ".markdown", ".mdown", ".mkdn", ".mkd"},1313+ FormatMarkdown: {".md", ".markdown", ".mdown", ".mkdn", ".mkd"},1614}17151818-// ReadmeFilenames contains the list of common README filenames to search for,1919-// in order of preference. Only includes well-supported formats.2020-var ReadmeFilenames = []string{2121- "README.md", "readme.md",2222- "README",2323- "readme",2424- "README.markdown",2525- "readme.markdown",2626- "README.txt",2727- "readme.txt",1616+var FileTypePatterns = map[Format]*regexp.Regexp{1717+ FormatMarkdown: regexp.MustCompile(`(?i)\.(md|markdown|mdown|mkdn|mkd)$`),1818+}1919+2020+var ReadmePattern = regexp.MustCompile(`(?i)^readme(\.(md|markdown|txt))?$`)2121+2222+func IsReadmeFile(filename string) bool {2323+ return ReadmePattern.MatchString(filename)2824}29253026func GetFormat(filename string) Format {3131- for format, extensions := range FileTypes {3232- for _, extension := range extensions {3333- if strings.HasSuffix(filename, extension) {3434- return format3535- }2727+ for format, pattern := range FileTypePatterns {2828+ if pattern.MatchString(filename) {2929+ return format3630 }3731 }3832 // default format
···484484 if xrpcResp.Dotdot != nil {485485 result.DotDot = *xrpcResp.Dotdot486486 }487487+ if xrpcResp.Readme != nil {488488+ result.ReadmeFileName = xrpcResp.Readme.Filename489489+ result.Readme = xrpcResp.Readme.Contents490490+ }487491488492 // redirects tree paths trying to access a blob; in this case the result.Files is unpopulated,489493 // so we can safely redirect to the "parent" (which is the same file).
+24
knotserver/xrpc/repo_tree.go
···44 "net/http"55 "path/filepath"66 "time"77+ "unicode/utf8"7889 "tangled.org/core/api/tangled"1010+ "tangled.org/core/appview/pages/markup"911 "tangled.org/core/knotserver/git"1012 xrpcerr "tangled.org/core/xrpc/errors"1113)···4341 xrpcerr.WithMessage("failed to read repository tree"),4442 ), http.StatusNotFound)4543 return4444+ }4545+4646+ // if any of these files are a readme candidate, pass along its blob contents too4747+ var readmeFileName string4848+ var readmeContents string4949+ for _, file := range files {5050+ if markup.IsReadmeFile(file.Name) {5151+ contents, err := gr.RawContent(filepath.Join(path, file.Name))5252+ if err != nil {5353+ x.Logger.Error("failed to read contents of file", "path", path, "file", file.Name)5454+ }5555+5656+ if utf8.Valid(contents) {5757+ readmeFileName = file.Name5858+ readmeContents = string(contents)5959+ break6060+ }6161+ }4662 }47634864 // convert NiceTree -> tangled.RepoTree_TreeEntry···10383 Parent: parentPtr,10484 Dotdot: dotdotPtr,10585 Files: treeEntries,8686+ Readme: &tangled.RepoTree_Readme{8787+ Filename: readmeFileName,8888+ Contents: readmeContents,8989+ },10690 }1079110892 writeJson(w, response)