github.com/phsym/gomarkdoc@v0.5.4/format/github.go (about) 1 package format 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "regexp" 7 "strings" 8 9 "github.com/phsym/gomarkdoc/format/formatcore" 10 "github.com/phsym/gomarkdoc/lang" 11 ) 12 13 // GitHubFlavoredMarkdown provides a Format which is compatible with GitHub 14 // Flavored Markdown's syntax and semantics. See GitHub's documentation for 15 // more details about their markdown format: 16 // https://guides.github.com/features/mastering-markdown/ 17 type GitHubFlavoredMarkdown struct{} 18 19 // Bold converts the provided text to bold 20 func (f *GitHubFlavoredMarkdown) Bold(text string) (string, error) { 21 return formatcore.Bold(text), nil 22 } 23 24 // CodeBlock wraps the provided code as a code block and tags it with the 25 // provided language (or no language if the empty string is provided). 26 func (f *GitHubFlavoredMarkdown) CodeBlock(language, code string) (string, error) { 27 return formatcore.GFMCodeBlock(language, code), nil 28 } 29 30 // Header converts the provided text into a header of the provided level. The 31 // level is expected to be at least 1. 32 func (f *GitHubFlavoredMarkdown) Header(level int, text string) (string, error) { 33 return formatcore.Header(level, formatcore.Escape(text)) 34 } 35 36 // RawHeader converts the provided text into a header of the provided level 37 // without escaping the header text. The level is expected to be at least 1. 38 func (f *GitHubFlavoredMarkdown) RawHeader(level int, text string) (string, error) { 39 return formatcore.Header(level, text) 40 } 41 42 var ( 43 gfmWhitespaceRegex = regexp.MustCompile(`\s`) 44 gfmRemoveRegex = regexp.MustCompile(`[^\pL-_\d]+`) 45 ) 46 47 // LocalHref generates an href for navigating to a header with the given 48 // headerText located within the same document as the href itself. 49 func (f *GitHubFlavoredMarkdown) LocalHref(headerText string) (string, error) { 50 result := formatcore.PlainText(headerText) 51 result = strings.ToLower(result) 52 result = strings.TrimSpace(result) 53 result = gfmWhitespaceRegex.ReplaceAllString(result, "-") 54 result = gfmRemoveRegex.ReplaceAllString(result, "") 55 56 return fmt.Sprintf("#%s", result), nil 57 } 58 59 // Link generates a link with the given text and href values. 60 func (f *GitHubFlavoredMarkdown) Link(text, href string) (string, error) { 61 return formatcore.Link(text, href), nil 62 } 63 64 // CodeHref generates an href to the provided code entry. 65 func (f *GitHubFlavoredMarkdown) CodeHref(loc lang.Location) (string, error) { 66 // If there's no repo, we can't compute an href 67 if loc.Repo == nil { 68 return "", nil 69 } 70 71 var ( 72 relative string 73 err error 74 ) 75 if filepath.IsAbs(loc.Filepath) { 76 relative, err = filepath.Rel(loc.WorkDir, loc.Filepath) 77 if err != nil { 78 return "", err 79 } 80 } else { 81 relative = loc.Filepath 82 } 83 84 full := filepath.Join(loc.Repo.PathFromRoot, relative) 85 p, err := filepath.Rel(string(filepath.Separator), full) 86 if err != nil { 87 return "", err 88 } 89 90 var locStr string 91 if loc.Start.Line == loc.End.Line { 92 locStr = fmt.Sprintf("L%d", loc.Start.Line) 93 } else { 94 locStr = fmt.Sprintf("L%d-L%d", loc.Start.Line, loc.End.Line) 95 } 96 97 return fmt.Sprintf( 98 "%s/blob/%s/%s#%s", 99 loc.Repo.Remote, 100 loc.Repo.DefaultBranch, 101 filepath.ToSlash(p), 102 locStr, 103 ), nil 104 } 105 106 // ListEntry generates an unordered list entry with the provided text at the 107 // provided zero-indexed depth. A depth of 0 is considered the topmost level of 108 // list. 109 func (f *GitHubFlavoredMarkdown) ListEntry(depth int, text string) (string, error) { 110 return formatcore.ListEntry(depth, text), nil 111 } 112 113 // Accordion generates a collapsible content. The accordion's visible title 114 // while collapsed is the provided title and the expanded content is the body. 115 func (f *GitHubFlavoredMarkdown) Accordion(title, body string) (string, error) { 116 return formatcore.GFMAccordion(title, body), nil 117 } 118 119 // AccordionHeader generates the header visible when an accordion is collapsed. 120 // 121 // The AccordionHeader is expected to be used in conjunction with 122 // AccordionTerminator() when the demands of the body's rendering requires it to 123 // be generated independently. The result looks conceptually like the following: 124 // 125 // accordion := format.AccordionHeader("Accordion Title") + "Accordion Body" + format.AccordionTerminator() 126 func (f *GitHubFlavoredMarkdown) AccordionHeader(title string) (string, error) { 127 return formatcore.GFMAccordionHeader(title), nil 128 } 129 130 // AccordionTerminator generates the code necessary to terminate an accordion 131 // after the body. It is expected to be used in conjunction with 132 // AccordionHeader(). See AccordionHeader for a full description. 133 func (f *GitHubFlavoredMarkdown) AccordionTerminator() (string, error) { 134 return formatcore.GFMAccordionTerminator(), nil 135 } 136 137 // Paragraph formats a paragraph with the provided text as the contents. 138 func (f *GitHubFlavoredMarkdown) Paragraph(text string) (string, error) { 139 return formatcore.Paragraph(text), nil 140 } 141 142 // Escape escapes special markdown characters from the provided text. 143 func (f *GitHubFlavoredMarkdown) Escape(text string) string { 144 return formatcore.Escape(text) 145 }