github.com/anthonyme00/gomarkdoc@v1.0.0/format/github.go (about) 1 package format 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "regexp" 7 "strings" 8 9 "github.com/anthonyme00/gomarkdoc/format/formatcore" 10 "github.com/anthonyme00/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 // Anchor produces an anchor for the provided link. 31 func (f *GitHubFlavoredMarkdown) Anchor(anchor string) string { 32 return formatcore.Anchor(anchor) 33 } 34 35 // AnchorHeader converts the provided text and custom anchor link into a header 36 // of the provided level. The level is expected to be at least 1. 37 func (f *GitHubFlavoredMarkdown) AnchorHeader(level int, text, anchor string) (string, error) { 38 return formatcore.AnchorHeader(level, formatcore.Escape(text), anchor) 39 } 40 41 // Header converts the provided text into a header of the provided level. The 42 // level is expected to be at least 1. 43 func (f *GitHubFlavoredMarkdown) Header(level int, text string) (string, error) { 44 return formatcore.Header(level, formatcore.Escape(text)) 45 } 46 47 // RawAnchorHeader converts the provided text and custom anchor link into a 48 // header of the provided level without escaping the header text. The level is 49 // expected to be at least 1. 50 func (f *GitHubFlavoredMarkdown) RawAnchorHeader(level int, text, anchor string) (string, error) { 51 return formatcore.AnchorHeader(level, text, anchor) 52 } 53 54 // RawHeader converts the provided text into a header of the provided level 55 // without escaping the header text. The level is expected to be at least 1. 56 func (f *GitHubFlavoredMarkdown) RawHeader(level int, text string) (string, error) { 57 return formatcore.Header(level, text) 58 } 59 60 var ( 61 gfmWhitespaceRegex = regexp.MustCompile(`\s`) 62 gfmRemoveRegex = regexp.MustCompile(`[^\pL-_\d]+`) 63 ) 64 65 // LocalHref generates an href for navigating to a header with the given 66 // headerText located within the same document as the href itself. 67 func (f *GitHubFlavoredMarkdown) LocalHref(headerText string) (string, error) { 68 result := formatcore.PlainText(headerText) 69 result = strings.ToLower(result) 70 result = strings.TrimSpace(result) 71 result = gfmWhitespaceRegex.ReplaceAllString(result, "-") 72 result = gfmRemoveRegex.ReplaceAllString(result, "") 73 74 return fmt.Sprintf("#%s", result), nil 75 } 76 77 // RawLocalHref generates an href within the same document but with a direct 78 // link provided instead of text to slugify. 79 func (f *GitHubFlavoredMarkdown) RawLocalHref(anchor string) string { 80 return fmt.Sprintf("#%s", anchor) 81 } 82 83 // Link generates a link with the given text and href values. 84 func (f *GitHubFlavoredMarkdown) Link(text, href string) (string, error) { 85 return formatcore.Link(text, href), nil 86 } 87 88 // CodeHref generates an href to the provided code entry. 89 func (f *GitHubFlavoredMarkdown) CodeHref(loc lang.Location) (string, error) { 90 // If there's no repo, we can't compute an href 91 if loc.Repo == nil { 92 return "", nil 93 } 94 95 var ( 96 relative string 97 err error 98 ) 99 if filepath.IsAbs(loc.Filepath) { 100 relative, err = filepath.Rel(loc.WorkDir, loc.Filepath) 101 if err != nil { 102 return "", err 103 } 104 } else { 105 relative = loc.Filepath 106 } 107 108 full := filepath.Join(loc.Repo.PathFromRoot, relative) 109 p, err := filepath.Rel(string(filepath.Separator), full) 110 if err != nil { 111 return "", err 112 } 113 114 var locStr string 115 if loc.Start.Line == loc.End.Line { 116 locStr = fmt.Sprintf("L%d", loc.Start.Line) 117 } else { 118 locStr = fmt.Sprintf("L%d-L%d", loc.Start.Line, loc.End.Line) 119 } 120 121 return fmt.Sprintf( 122 "%s/blob/%s/%s#%s", 123 loc.Repo.Remote, 124 loc.Repo.DefaultBranch, 125 filepath.ToSlash(p), 126 locStr, 127 ), nil 128 } 129 130 // ListEntry generates an unordered list entry with the provided text at the 131 // provided zero-indexed depth. A depth of 0 is considered the topmost level of 132 // list. 133 func (f *GitHubFlavoredMarkdown) ListEntry(depth int, text string) (string, error) { 134 return formatcore.ListEntry(depth, text), nil 135 } 136 137 // Accordion generates a collapsible content. The accordion's visible title 138 // while collapsed is the provided title and the expanded content is the body. 139 func (f *GitHubFlavoredMarkdown) Accordion(title, body string) (string, error) { 140 return formatcore.GFMAccordion(title, body), nil 141 } 142 143 // AccordionHeader generates the header visible when an accordion is collapsed. 144 // 145 // The AccordionHeader is expected to be used in conjunction with 146 // AccordionTerminator() when the demands of the body's rendering requires it to 147 // be generated independently. The result looks conceptually like the following: 148 // 149 // accordion := format.AccordionHeader("Accordion Title") + "Accordion Body" + format.AccordionTerminator() 150 func (f *GitHubFlavoredMarkdown) AccordionHeader(title string) (string, error) { 151 return formatcore.GFMAccordionHeader(title), nil 152 } 153 154 // AccordionTerminator generates the code necessary to terminate an accordion 155 // after the body. It is expected to be used in conjunction with 156 // AccordionHeader(). See AccordionHeader for a full description. 157 func (f *GitHubFlavoredMarkdown) AccordionTerminator() (string, error) { 158 return formatcore.GFMAccordionTerminator(), nil 159 } 160 161 // Escape escapes special markdown characters from the provided text. 162 func (f *GitHubFlavoredMarkdown) Escape(text string) string { 163 return formatcore.Escape(text) 164 }