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