mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-25 06:25:31 +00:00
Merge pull request '[gitea] week 2024-28 cherry pick (gitea/main -> forgejo)' (#4391) from earl-warren/wcp/2024-28 into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4391 Reviewed-by: twenty-panda <twenty-panda@noreply.codeberg.org>
This commit is contained in:
commit
4f6c823ae7
2
Makefile
2
Makefile
|
@ -981,7 +981,7 @@ generate-gomock:
|
||||||
|
|
||||||
.PHONY: generate-images
|
.PHONY: generate-images
|
||||||
generate-images: | node_modules
|
generate-images: | node_modules
|
||||||
npm install --no-save fabric@6.0.0-beta20 imagemin-zopfli@7
|
npm install --no-save fabric@6 imagemin-zopfli@7
|
||||||
node tools/generate-images.js $(TAGS)
|
node tools/generate-images.js $(TAGS)
|
||||||
|
|
||||||
.PHONY: generate-manpage
|
.PHONY: generate-manpage
|
||||||
|
|
|
@ -104,11 +104,8 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
|
||||||
buffer := new(bytes.Buffer)
|
buffer := new(bytes.Buffer)
|
||||||
for _, file := range filenames {
|
for _, file := range filenames {
|
||||||
if file != "" {
|
if file != "" {
|
||||||
buffer.WriteString("0 ")
|
// using format: mode SP type SP sha1 TAB path
|
||||||
buffer.WriteString(objectFormat.EmptyObjectID().String())
|
buffer.WriteString("0 blob " + objectFormat.EmptyObjectID().String() + "\t" + file + "\000")
|
||||||
buffer.WriteByte('\t')
|
|
||||||
buffer.WriteString(file)
|
|
||||||
buffer.WriteByte('\000')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cmd.Run(&RunOpts{
|
return cmd.Run(&RunOpts{
|
||||||
|
@ -119,11 +116,33 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IndexObjectInfo struct {
|
||||||
|
Mode string
|
||||||
|
Object ObjectID
|
||||||
|
Filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddObjectsToIndex adds the provided object hashes to the index at the provided filenames
|
||||||
|
func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error {
|
||||||
|
cmd := NewCommand(repo.Ctx, "update-index", "--add", "--replace", "-z", "--index-info")
|
||||||
|
stdout := new(bytes.Buffer)
|
||||||
|
stderr := new(bytes.Buffer)
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
for _, object := range objects {
|
||||||
|
// using format: mode SP type SP sha1 TAB path
|
||||||
|
buffer.WriteString(object.Mode + " blob " + object.Object.String() + "\t" + object.Filename + "\000")
|
||||||
|
}
|
||||||
|
return cmd.Run(&RunOpts{
|
||||||
|
Dir: repo.Path,
|
||||||
|
Stdin: bytes.NewReader(buffer.Bytes()),
|
||||||
|
Stdout: stdout,
|
||||||
|
Stderr: stderr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// AddObjectToIndex adds the provided object hash to the index at the provided filename
|
// AddObjectToIndex adds the provided object hash to the index at the provided filename
|
||||||
func (repo *Repository) AddObjectToIndex(mode string, object ObjectID, filename string) error {
|
func (repo *Repository) AddObjectToIndex(mode string, object ObjectID, filename string) error {
|
||||||
cmd := NewCommand(repo.Ctx, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, object.String(), filename)
|
return repo.AddObjectsToIndex(IndexObjectInfo{Mode: mode, Object: object, Filename: filename})
|
||||||
_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteTree writes the current index as a tree to the object db and returns its hash
|
// WriteTree writes the current index as a tree to the object db and returns its hash
|
||||||
|
|
|
@ -552,6 +552,14 @@ func TestMathBlock(t *testing.T) {
|
||||||
"$a$ ($b$) [$c$] {$d$}",
|
"$a$ ($b$) [$c$] {$d$}",
|
||||||
`<p><code class="language-math is-loading">a</code> (<code class="language-math is-loading">b</code>) [$c$] {$d$}</p>` + nl,
|
`<p><code class="language-math is-loading">a</code> (<code class="language-math is-loading">b</code>) [$c$] {$d$}</p>` + nl,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$$a$$ test",
|
||||||
|
`<p><code class="language-math display is-loading">a</code> test</p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"test $$a$$",
|
||||||
|
`<p>test <code class="language-math display is-loading">a</code></p>` + nl,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testcases {
|
for _, test := range testcases {
|
||||||
|
|
|
@ -47,6 +47,12 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex
|
||||||
}
|
}
|
||||||
idx := bytes.Index(line[pos+2:], endBytes)
|
idx := bytes.Index(line[pos+2:], endBytes)
|
||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
|
// for case $$ ... $$ any other text
|
||||||
|
for i := pos + idx + 4; i < len(line); i++ {
|
||||||
|
if line[i] != ' ' && line[i] != '\n' {
|
||||||
|
return nil, parser.NoChildren
|
||||||
|
}
|
||||||
|
}
|
||||||
segment.Stop = segment.Start + idx + 2
|
segment.Stop = segment.Start + idx + 2
|
||||||
reader.Advance(segment.Len() - 1)
|
reader.Advance(segment.Len() - 1)
|
||||||
segment.Start += 2
|
segment.Start += 2
|
||||||
|
|
31
modules/markup/markdown/math/inline_block_node.go
Normal file
31
modules/markup/markdown/math/inline_block_node.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package math
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/yuin/goldmark/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InlineBlock represents inline math e.g. $$...$$
|
||||||
|
type InlineBlock struct {
|
||||||
|
Inline
|
||||||
|
}
|
||||||
|
|
||||||
|
// InlineBlock implements InlineBlock.
|
||||||
|
func (n *InlineBlock) InlineBlock() {}
|
||||||
|
|
||||||
|
// KindInlineBlock is the kind for math inline block
|
||||||
|
var KindInlineBlock = ast.NewNodeKind("MathInlineBlock")
|
||||||
|
|
||||||
|
// Kind returns KindInlineBlock
|
||||||
|
func (n *InlineBlock) Kind() ast.NodeKind {
|
||||||
|
return KindInlineBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInlineBlock creates a new ast math inline block node
|
||||||
|
func NewInlineBlock() *InlineBlock {
|
||||||
|
return &InlineBlock{
|
||||||
|
Inline{},
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,11 +21,20 @@ var defaultInlineDollarParser = &inlineParser{
|
||||||
end: []byte{'$'},
|
end: []byte{'$'},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultDualDollarParser = &inlineParser{
|
||||||
|
start: []byte{'$', '$'},
|
||||||
|
end: []byte{'$', '$'},
|
||||||
|
}
|
||||||
|
|
||||||
// NewInlineDollarParser returns a new inline parser
|
// NewInlineDollarParser returns a new inline parser
|
||||||
func NewInlineDollarParser() parser.InlineParser {
|
func NewInlineDollarParser() parser.InlineParser {
|
||||||
return defaultInlineDollarParser
|
return defaultInlineDollarParser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewInlineDualDollarParser() parser.InlineParser {
|
||||||
|
return defaultDualDollarParser
|
||||||
|
}
|
||||||
|
|
||||||
var defaultInlineBracketParser = &inlineParser{
|
var defaultInlineBracketParser = &inlineParser{
|
||||||
start: []byte{'\\', '('},
|
start: []byte{'\\', '('},
|
||||||
end: []byte{'\\', ')'},
|
end: []byte{'\\', ')'},
|
||||||
|
@ -38,7 +47,7 @@ func NewInlineBracketParser() parser.InlineParser {
|
||||||
|
|
||||||
// Trigger triggers this parser on $ or \
|
// Trigger triggers this parser on $ or \
|
||||||
func (parser *inlineParser) Trigger() []byte {
|
func (parser *inlineParser) Trigger() []byte {
|
||||||
return parser.start[0:1]
|
return parser.start
|
||||||
}
|
}
|
||||||
|
|
||||||
func isPunctuation(b byte) bool {
|
func isPunctuation(b byte) bool {
|
||||||
|
@ -88,7 +97,11 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
suceedingCharacter := line[pos]
|
suceedingCharacter := line[pos]
|
||||||
if !isPunctuation(suceedingCharacter) && !(suceedingCharacter == ' ') && !isBracket(suceedingCharacter) {
|
// check valid ending character
|
||||||
|
if !isPunctuation(suceedingCharacter) &&
|
||||||
|
!(suceedingCharacter == ' ') &&
|
||||||
|
!(suceedingCharacter == '\n') &&
|
||||||
|
!isBracket(suceedingCharacter) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if line[ender-1] != '\\' {
|
if line[ender-1] != '\\' {
|
||||||
|
@ -101,12 +114,21 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
|
||||||
|
|
||||||
block.Advance(opener)
|
block.Advance(opener)
|
||||||
_, pos := block.Position()
|
_, pos := block.Position()
|
||||||
node := NewInline()
|
var node ast.Node
|
||||||
|
if parser == defaultDualDollarParser {
|
||||||
|
node = NewInlineBlock()
|
||||||
|
} else {
|
||||||
|
node = NewInline()
|
||||||
|
}
|
||||||
segment := pos.WithStop(pos.Start + ender - opener)
|
segment := pos.WithStop(pos.Start + ender - opener)
|
||||||
node.AppendChild(node, ast.NewRawTextSegment(segment))
|
node.AppendChild(node, ast.NewRawTextSegment(segment))
|
||||||
block.Advance(ender - opener + len(parser.end))
|
block.Advance(ender - opener + len(parser.end))
|
||||||
|
|
||||||
trimBlock(node, block)
|
if parser == defaultDualDollarParser {
|
||||||
|
trimBlock(&(node.(*InlineBlock)).Inline, block)
|
||||||
|
} else {
|
||||||
|
trimBlock(node.(*Inline), block)
|
||||||
|
}
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,11 @@ func NewInlineRenderer() renderer.NodeRenderer {
|
||||||
|
|
||||||
func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
|
func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||||
if entering {
|
if entering {
|
||||||
_, _ = w.WriteString(`<code class="language-math is-loading">`)
|
extraClass := ""
|
||||||
|
if _, ok := n.(*InlineBlock); ok {
|
||||||
|
extraClass = "display "
|
||||||
|
}
|
||||||
|
_, _ = w.WriteString(`<code class="language-math ` + extraClass + `is-loading">`)
|
||||||
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
|
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
|
||||||
segment := c.(*ast.Text).Segment
|
segment := c.(*ast.Text).Segment
|
||||||
value := util.EscapeHTML(segment.Value(source))
|
value := util.EscapeHTML(segment.Value(source))
|
||||||
|
@ -43,4 +47,5 @@ func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Nod
|
||||||
// RegisterFuncs registers the renderer for inline math nodes
|
// RegisterFuncs registers the renderer for inline math nodes
|
||||||
func (r *InlineRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
func (r *InlineRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||||
reg.Register(KindInline, r.renderInline)
|
reg.Register(KindInline, r.renderInline)
|
||||||
|
reg.Register(KindInlineBlock, r.renderInline)
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,8 @@ func (e *Extension) Extend(m goldmark.Markdown) {
|
||||||
util.Prioritized(NewInlineBracketParser(), 501),
|
util.Prioritized(NewInlineBracketParser(), 501),
|
||||||
}
|
}
|
||||||
if e.parseDollarInline {
|
if e.parseDollarInline {
|
||||||
inlines = append(inlines, util.Prioritized(NewInlineDollarParser(), 501))
|
inlines = append(inlines, util.Prioritized(NewInlineDollarParser(), 503),
|
||||||
|
util.Prioritized(NewInlineDualDollarParser(), 502))
|
||||||
}
|
}
|
||||||
m.Parser().AddOptions(parser.WithInlineParsers(inlines...))
|
m.Parser().AddOptions(parser.WithInlineParsers(inlines...))
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ func (e *errMergeConflict) Error() string {
|
||||||
return fmt.Sprintf("conflict detected at: %s", e.filename)
|
return fmt.Sprintf("conflict detected at: %s", e.filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, gitRepo *git.Repository) error {
|
func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, filesToRemove *[]string, filesToAdd *[]git.IndexObjectInfo) error {
|
||||||
log.Trace("Attempt to merge:\n%v", file)
|
log.Trace("Attempt to merge:\n%v", file)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -142,14 +142,13 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not a genuine conflict and we can simply remove the file from the index
|
// Not a genuine conflict and we can simply remove the file from the index
|
||||||
return gitRepo.RemoveFilesFromIndex(file.stage1.path)
|
*filesToRemove = append(*filesToRemove, file.stage1.path)
|
||||||
|
return nil
|
||||||
case file.stage1 == nil && file.stage2 != nil && (file.stage3 == nil || file.stage2.SameAs(file.stage3)):
|
case file.stage1 == nil && file.stage2 != nil && (file.stage3 == nil || file.stage2.SameAs(file.stage3)):
|
||||||
// 2. Added in ours but not in theirs or identical in both
|
// 2. Added in ours but not in theirs or identical in both
|
||||||
//
|
//
|
||||||
// Not a genuine conflict just add to the index
|
// Not a genuine conflict just add to the index
|
||||||
if err := gitRepo.AddObjectToIndex(file.stage2.mode, git.MustIDFromString(file.stage2.sha), file.stage2.path); err != nil {
|
*filesToAdd = append(*filesToAdd, git.IndexObjectInfo{Mode: file.stage2.mode, Object: git.MustIDFromString(file.stage2.sha), Filename: file.stage2.path})
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
case file.stage1 == nil && file.stage2 != nil && file.stage3 != nil && file.stage2.sha == file.stage3.sha && file.stage2.mode != file.stage3.mode:
|
case file.stage1 == nil && file.stage2 != nil && file.stage3 != nil && file.stage2.sha == file.stage3.sha && file.stage2.mode != file.stage3.mode:
|
||||||
// 3. Added in both with the same sha but the modes are different
|
// 3. Added in both with the same sha but the modes are different
|
||||||
|
@ -160,7 +159,8 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g
|
||||||
// 4. Added in theirs but not ours:
|
// 4. Added in theirs but not ours:
|
||||||
//
|
//
|
||||||
// Not a genuine conflict just add to the index
|
// Not a genuine conflict just add to the index
|
||||||
return gitRepo.AddObjectToIndex(file.stage3.mode, git.MustIDFromString(file.stage3.sha), file.stage3.path)
|
*filesToAdd = append(*filesToAdd, git.IndexObjectInfo{Mode: file.stage3.mode, Object: git.MustIDFromString(file.stage3.sha), Filename: file.stage3.path})
|
||||||
|
return nil
|
||||||
case file.stage1 == nil:
|
case file.stage1 == nil:
|
||||||
// 5. Created by new in both
|
// 5. Created by new in both
|
||||||
//
|
//
|
||||||
|
@ -221,7 +221,8 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hash = strings.TrimSpace(hash)
|
hash = strings.TrimSpace(hash)
|
||||||
return gitRepo.AddObjectToIndex(file.stage2.mode, git.MustIDFromString(hash), file.stage2.path)
|
*filesToAdd = append(*filesToAdd, git.IndexObjectInfo{Mode: file.stage2.mode, Object: git.MustIDFromString(hash), Filename: file.stage2.path})
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
if file.stage1 != nil {
|
if file.stage1 != nil {
|
||||||
return &errMergeConflict{file.stage1.path}
|
return &errMergeConflict{file.stage1.path}
|
||||||
|
@ -245,6 +246,9 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
|
||||||
return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %w", err)
|
return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var filesToRemove []string
|
||||||
|
var filesToAdd []git.IndexObjectInfo
|
||||||
|
|
||||||
// Then we use git ls-files -u to list the unmerged files and collate the triples in unmergedfiles
|
// Then we use git ls-files -u to list the unmerged files and collate the triples in unmergedfiles
|
||||||
unmerged := make(chan *unmergedFile)
|
unmerged := make(chan *unmergedFile)
|
||||||
go unmergedFiles(ctx, gitPath, unmerged)
|
go unmergedFiles(ctx, gitPath, unmerged)
|
||||||
|
@ -270,7 +274,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
|
||||||
}
|
}
|
||||||
|
|
||||||
// OK now we have the unmerged file triplet attempt to merge it
|
// OK now we have the unmerged file triplet attempt to merge it
|
||||||
if err := attemptMerge(ctx, file, gitPath, gitRepo); err != nil {
|
if err := attemptMerge(ctx, file, gitPath, &filesToRemove, &filesToAdd); err != nil {
|
||||||
if conflictErr, ok := err.(*errMergeConflict); ok {
|
if conflictErr, ok := err.(*errMergeConflict); ok {
|
||||||
log.Trace("Conflict: %s in %s", conflictErr.filename, description)
|
log.Trace("Conflict: %s in %s", conflictErr.filename, description)
|
||||||
conflict = true
|
conflict = true
|
||||||
|
@ -283,6 +287,15 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
|
||||||
return false, nil, err
|
return false, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add and remove files in one command, as this is slow with many files otherwise
|
||||||
|
if err := gitRepo.RemoveFilesFromIndex(filesToRemove...); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
if err := gitRepo.AddObjectsToIndex(filesToAdd...); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return conflict, conflictedFiles, nil
|
return conflict, conflictedFiles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue