mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-01 05:36:19 +01:00
Allow different HardBreaks settings for documents and comments (#11515)
GH has different HardBreaks behaviour for markdown comments and documents. Comments have hard breaks and documents have soft breaks - therefore Gitea's rendering will always be different from GH's if we only provide one setting. Here we split the setting in to two - one for documents and one for comments and other things. Signed-off-by: Andrew Thornton art27@cantab.net Changes to index.js as per @silverwind Co-authored-by: silverwind <me@silverwind.io> Changes to docs as per @guillep2k Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
This commit is contained in:
parent
3761bdb640
commit
814ca9ffea
12 changed files with 77 additions and 29 deletions
|
@ -216,7 +216,10 @@ EVENT_SOURCE_UPDATE_TIME = 10s
|
|||
; Render soft line breaks as hard line breaks, which means a single newline character between
|
||||
; paragraphs will cause a line break and adding trailing whitespace to paragraphs is not
|
||||
; necessary to force a line break.
|
||||
ENABLE_HARD_LINE_BREAK = true
|
||||
; Render soft line breaks as hard line breaks for comments
|
||||
ENABLE_HARD_LINE_BREAK_IN_COMMENTS = true
|
||||
; Render soft line breaks as hard line breaks for markdown documents
|
||||
ENABLE_HARD_LINE_BREAK_IN_DOCUMENTS = false
|
||||
; Comma separated list of custom URL-Schemes that are allowed as links when rendering Markdown
|
||||
; for example git,magnet,ftp (more at https://en.wikipedia.org/wiki/List_of_URI_schemes)
|
||||
; URLs starting with http and https are always displayed, whatever is put in this entry.
|
||||
|
|
|
@ -152,7 +152,10 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
|||
|
||||
## Markdown (`markdown`)
|
||||
|
||||
- `ENABLE_HARD_LINE_BREAK`: **true**: Render soft line breaks as hard line breaks, which
|
||||
- `ENABLE_HARD_LINE_BREAK_IN_COMMENTS`: **true**: Render soft line breaks as hard line breaks in comments, which
|
||||
means a single newline character between paragraphs will cause a line break and adding
|
||||
trailing whitespace to paragraphs is not necessary to force a line break.
|
||||
- `ENABLE_HARD_LINE_BREAK_IN_DOCUMENTS`: **false**: Render soft line breaks as hard line breaks in documents, which
|
||||
means a single newline character between paragraphs will cause a line break and adding
|
||||
trailing whitespace to paragraphs is not necessary to force a line break.
|
||||
- `CUSTOM_URL_SCHEMES`: Use a comma separated list (ftp,git,svn) to indicate additional
|
||||
|
|
|
@ -174,9 +174,10 @@ type Repository struct {
|
|||
*Mirror `xorm:"-"`
|
||||
Status RepositoryStatus `xorm:"NOT NULL DEFAULT 0"`
|
||||
|
||||
RenderingMetas map[string]string `xorm:"-"`
|
||||
Units []*RepoUnit `xorm:"-"`
|
||||
PrimaryLanguage *LanguageStat `xorm:"-"`
|
||||
RenderingMetas map[string]string `xorm:"-"`
|
||||
DocumentRenderingMetas map[string]string `xorm:"-"`
|
||||
Units []*RepoUnit `xorm:"-"`
|
||||
PrimaryLanguage *LanguageStat `xorm:"-"`
|
||||
|
||||
IsFork bool `xorm:"INDEX NOT NULL DEFAULT false"`
|
||||
ForkID int64 `xorm:"INDEX"`
|
||||
|
@ -545,11 +546,12 @@ func (repo *Repository) mustOwner(e Engine) *User {
|
|||
|
||||
// ComposeMetas composes a map of metas for properly rendering issue links and external issue trackers.
|
||||
func (repo *Repository) ComposeMetas() map[string]string {
|
||||
if repo.RenderingMetas == nil {
|
||||
if len(repo.RenderingMetas) == 0 {
|
||||
metas := map[string]string{
|
||||
"user": repo.OwnerName,
|
||||
"repo": repo.Name,
|
||||
"repoPath": repo.RepoPath(),
|
||||
"mode": "comment",
|
||||
}
|
||||
|
||||
unit, err := repo.GetUnit(UnitTypeExternalTracker)
|
||||
|
@ -581,6 +583,19 @@ func (repo *Repository) ComposeMetas() map[string]string {
|
|||
return repo.RenderingMetas
|
||||
}
|
||||
|
||||
// ComposeDocumentMetas composes a map of metas for properly rendering documents
|
||||
func (repo *Repository) ComposeDocumentMetas() map[string]string {
|
||||
if len(repo.DocumentRenderingMetas) == 0 {
|
||||
metas := map[string]string{}
|
||||
for k, v := range repo.ComposeMetas() {
|
||||
metas[k] = v
|
||||
}
|
||||
metas["mode"] = "document"
|
||||
repo.DocumentRenderingMetas = metas
|
||||
}
|
||||
return repo.DocumentRenderingMetas
|
||||
}
|
||||
|
||||
// DeleteWiki removes the actual and local copy of repository wiki.
|
||||
func (repo *Repository) DeleteWiki() error {
|
||||
return repo.deleteWiki(x)
|
||||
|
|
|
@ -151,6 +151,16 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
|||
v.AppendChild(v, newChild)
|
||||
}
|
||||
}
|
||||
case *ast.Text:
|
||||
if v.SoftLineBreak() && !v.HardLineBreak() {
|
||||
renderMetas := pc.Get(renderMetasKey).(map[string]string)
|
||||
mode := renderMetas["mode"]
|
||||
if mode != "document" {
|
||||
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInComments)
|
||||
} else {
|
||||
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
})
|
||||
|
|
|
@ -29,17 +29,19 @@ var once = sync.Once{}
|
|||
|
||||
var urlPrefixKey = parser.NewContextKey()
|
||||
var isWikiKey = parser.NewContextKey()
|
||||
var renderMetasKey = parser.NewContextKey()
|
||||
|
||||
// NewGiteaParseContext creates a parser.Context with the gitea context set
|
||||
func NewGiteaParseContext(urlPrefix string, isWiki bool) parser.Context {
|
||||
func NewGiteaParseContext(urlPrefix string, metas map[string]string, isWiki bool) parser.Context {
|
||||
pc := parser.NewContext(parser.WithIDs(newPrefixedIDs()))
|
||||
pc.Set(urlPrefixKey, urlPrefix)
|
||||
pc.Set(isWikiKey, isWiki)
|
||||
pc.Set(renderMetasKey, metas)
|
||||
return pc
|
||||
}
|
||||
|
||||
// RenderRaw renders Markdown to HTML without handling special links.
|
||||
func RenderRaw(body []byte, urlPrefix string, wikiMarkdown bool) []byte {
|
||||
// render renders Markdown to HTML without handling special links.
|
||||
func render(body []byte, urlPrefix string, metas map[string]string, wikiMarkdown bool) []byte {
|
||||
once.Do(func() {
|
||||
converter = goldmark.New(
|
||||
goldmark.WithExtensions(extension.Table,
|
||||
|
@ -75,12 +77,9 @@ func RenderRaw(body []byte, urlPrefix string, wikiMarkdown bool) []byte {
|
|||
),
|
||||
)
|
||||
|
||||
if setting.Markdown.EnableHardLineBreak {
|
||||
converter.Renderer().AddOptions(html.WithHardWraps())
|
||||
}
|
||||
})
|
||||
|
||||
pc := NewGiteaParseContext(urlPrefix, wikiMarkdown)
|
||||
pc := NewGiteaParseContext(urlPrefix, metas, wikiMarkdown)
|
||||
var buf bytes.Buffer
|
||||
if err := converter.Convert(giteautil.NormalizeEOL(body), &buf, parser.WithContext(pc)); err != nil {
|
||||
log.Error("Unable to render: %v", err)
|
||||
|
@ -112,7 +111,7 @@ func (Parser) Extensions() []string {
|
|||
|
||||
// Render implements markup.Parser
|
||||
func (Parser) Render(rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte {
|
||||
return RenderRaw(rawBytes, urlPrefix, isWiki)
|
||||
return render(rawBytes, urlPrefix, metas, isWiki)
|
||||
}
|
||||
|
||||
// Render renders Markdown to HTML with all specific handling stuff.
|
||||
|
@ -120,6 +119,11 @@ func Render(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
|
|||
return markup.Render("a.md", rawBytes, urlPrefix, metas)
|
||||
}
|
||||
|
||||
// RenderRaw renders Markdown to HTML without handling special links.
|
||||
func RenderRaw(body []byte, urlPrefix string, wikiMarkdown bool) []byte {
|
||||
return render(body, urlPrefix, map[string]string{}, wikiMarkdown)
|
||||
}
|
||||
|
||||
// RenderString renders Markdown to HTML with special links and returns string type.
|
||||
func RenderString(raw, urlPrefix string, metas map[string]string) string {
|
||||
return markup.RenderString("a.md", raw, urlPrefix, metas)
|
||||
|
|
|
@ -256,12 +256,14 @@ var (
|
|||
|
||||
// Markdown settings
|
||||
Markdown = struct {
|
||||
EnableHardLineBreak bool
|
||||
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
|
||||
FileExtensions []string
|
||||
EnableHardLineBreakInComments bool
|
||||
EnableHardLineBreakInDocuments bool
|
||||
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
|
||||
FileExtensions []string
|
||||
}{
|
||||
EnableHardLineBreak: true,
|
||||
FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
|
||||
EnableHardLineBreakInComments: true,
|
||||
EnableHardLineBreakInDocuments: false,
|
||||
FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
|
||||
}
|
||||
|
||||
// Admin settings
|
||||
|
|
|
@ -48,10 +48,12 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
|
|||
}
|
||||
|
||||
switch form.Mode {
|
||||
case "comment":
|
||||
fallthrough
|
||||
case "gfm":
|
||||
md := []byte(form.Text)
|
||||
urlPrefix := form.Context
|
||||
var meta map[string]string
|
||||
meta := map[string]string{}
|
||||
if !strings.HasPrefix(setting.AppSubURL+"/", urlPrefix) {
|
||||
// check if urlPrefix is already set to a URL
|
||||
linkRegex, _ := xurls.StrictMatchingScheme("https?://")
|
||||
|
@ -61,7 +63,15 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
|
|||
}
|
||||
}
|
||||
if ctx.Repo != nil && ctx.Repo.Repository != nil {
|
||||
meta = ctx.Repo.Repository.ComposeMetas()
|
||||
// "gfm" = Github Flavored Markdown - set this to render as a document
|
||||
if form.Mode == "gfm" {
|
||||
meta = ctx.Repo.Repository.ComposeDocumentMetas()
|
||||
} else {
|
||||
meta = ctx.Repo.Repository.ComposeMetas()
|
||||
}
|
||||
}
|
||||
if form.Mode == "gfm" {
|
||||
meta["mode"] = "document"
|
||||
}
|
||||
if form.Wiki {
|
||||
_, err := ctx.Write([]byte(markdown.RenderWiki(md, urlPrefix, meta)))
|
||||
|
|
|
@ -94,7 +94,7 @@ Here are some links to the most important topics. You can find the full list of
|
|||
<p><strong>Wine Staging</strong> on website <a href="http://wine-staging.com" rel="nofollow">wine-staging.com</a>.</p>
|
||||
<h2 id="user-content-quick-links">Quick Links</h2>
|
||||
<p>Here are some links to the most important topics. You can find the full list of pages at the sidebar.</p>
|
||||
<p><a href="` + AppSubURL + `wiki/Configuration" rel="nofollow">Configuration</a><br/>
|
||||
<p><a href="` + AppSubURL + `wiki/Configuration" rel="nofollow">Configuration</a>
|
||||
<a href="` + AppSubURL + `wiki/raw/images/icon-bug.png" rel="nofollow"><img src="` + AppSubURL + `wiki/raw/images/icon-bug.png" title="icon-bug.png" alt="images/icon-bug.png"/></a></p>
|
||||
`,
|
||||
// Guard wiki sidebar: special syntax
|
||||
|
|
|
@ -319,7 +319,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
|
|||
if markupType := markup.Type(readmeFile.name); markupType != "" {
|
||||
ctx.Data["IsMarkup"] = true
|
||||
ctx.Data["MarkupType"] = string(markupType)
|
||||
ctx.Data["FileContent"] = string(markup.Render(readmeFile.name, buf, readmeTreelink, ctx.Repo.Repository.ComposeMetas()))
|
||||
ctx.Data["FileContent"] = string(markup.Render(readmeFile.name, buf, readmeTreelink, ctx.Repo.Repository.ComposeDocumentMetas()))
|
||||
} else {
|
||||
ctx.Data["IsRenderedHTML"] = true
|
||||
ctx.Data["FileContent"] = strings.Replace(
|
||||
|
@ -459,7 +459,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
|||
if markupType := markup.Type(blob.Name()); markupType != "" {
|
||||
ctx.Data["IsMarkup"] = true
|
||||
ctx.Data["MarkupType"] = markupType
|
||||
ctx.Data["FileContent"] = string(markup.Render(blob.Name(), buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
|
||||
ctx.Data["FileContent"] = string(markup.Render(blob.Name(), buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeDocumentMetas()))
|
||||
} else if readmeExist {
|
||||
ctx.Data["IsRenderedHTML"] = true
|
||||
ctx.Data["FileContent"] = strings.Replace(
|
||||
|
@ -538,7 +538,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
|||
buf = append(buf, d...)
|
||||
ctx.Data["IsMarkup"] = true
|
||||
ctx.Data["MarkupType"] = markupType
|
||||
ctx.Data["FileContent"] = string(markup.Render(blob.Name(), buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
|
||||
ctx.Data["FileContent"] = string(markup.Render(blob.Name(), buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeDocumentMetas()))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -209,7 +209,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
metas := ctx.Repo.Repository.ComposeMetas()
|
||||
metas := ctx.Repo.Repository.ComposeDocumentMetas()
|
||||
ctx.Data["content"] = markdown.RenderWiki(data, ctx.Repo.RepoLink, metas)
|
||||
ctx.Data["sidebarPresent"] = sidebarContent != nil
|
||||
ctx.Data["sidebarContent"] = markdown.RenderWiki(sidebarContent, ctx.Repo.RepoLink, metas)
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff">
|
||||
<a class="active item" data-tab="write">{{svg "octicon-code" 16}} {{if .IsNewFile}}{{.i18n.Tr "repo.editor.new_file"}}{{else}}{{.i18n.Tr "repo.editor.edit_file"}}{{end}}</a>
|
||||
{{if not .IsNewFile}}
|
||||
<a class="item" data-tab="preview" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL | EscapePound}}" data-preview-file-modes="{{.PreviewableFileModes}}">{{svg "octicon-eye" 16}} {{.i18n.Tr "preview"}}</a>
|
||||
<a class="item" data-tab="preview" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL | EscapePound}}" data-preview-file-modes="{{.PreviewableFileModes}}" data-markdown-mode="gfm">{{svg "octicon-eye" 16}} {{.i18n.Tr "preview"}}</a>
|
||||
<a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName | EscapePound}}/{{.TreePath | EscapePound}}" data-context="{{.BranchLink}}">{{svg "octicon-diff" 16}} {{.i18n.Tr "repo.editor.preview_changes"}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
|
|
@ -41,7 +41,7 @@ function initCommentPreviewTab($form) {
|
|||
const $this = $(this);
|
||||
$.post($this.data('url'), {
|
||||
_csrf: csrf,
|
||||
mode: 'gfm',
|
||||
mode: 'comment',
|
||||
context: $this.data('context'),
|
||||
text: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val()
|
||||
}, (data) => {
|
||||
|
@ -65,6 +65,7 @@ function initEditPreviewTab($form) {
|
|||
$previewTab.on('click', function () {
|
||||
const $this = $(this);
|
||||
let context = `${$this.data('context')}/`;
|
||||
const mode = $this.data('markdown-mode') || 'comment';
|
||||
const treePathEl = $form.find('input#tree_path');
|
||||
if (treePathEl.length > 0) {
|
||||
context += treePathEl.val();
|
||||
|
@ -72,7 +73,7 @@ function initEditPreviewTab($form) {
|
|||
context = context.substring(0, context.lastIndexOf('/'));
|
||||
$.post($this.data('url'), {
|
||||
_csrf: csrf,
|
||||
mode: 'gfm',
|
||||
mode,
|
||||
context,
|
||||
text: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val()
|
||||
}, (data) => {
|
||||
|
|
Loading…
Reference in a new issue