Merge branch 'main' into fix-issue-card

This commit is contained in:
wxiaoguang 2024-05-02 18:52:47 +08:00 committed by GitHub
commit 0594d86377
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 169 additions and 138 deletions

View File

@ -17,11 +17,14 @@ import (
"time"
charsetModule "code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
"github.com/klauspost/compress/gzhttp"
)
type ServeHeaderOptions struct {
@ -38,6 +41,11 @@ type ServeHeaderOptions struct {
func ServeSetHeaders(w http.ResponseWriter, opts *ServeHeaderOptions) {
header := w.Header()
skipCompressionExts := container.SetOf(".gz", ".bz2", ".zip", ".xz", ".zst", ".deb", ".apk", ".jar", ".png", ".jpg", ".webp")
if skipCompressionExts.Contains(strings.ToLower(path.Ext(opts.Filename))) {
w.Header().Add(gzhttp.HeaderNoCompression, "1")
}
contentType := typesniffer.ApplicationOctetStream
if opts.ContentType != "" {
if opts.ContentTypeCharset != "" {

View File

@ -39,8 +39,6 @@ import (
const (
unicodeNormalizeName = "unicodeNormalize"
maxBatchSize = 16
// fuzzyDenominator determines the levenshtein distance per each character of a keyword
fuzzyDenominator = 4
)
func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
@ -245,7 +243,7 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
phraseQuery.Analyzer = repoIndexerAnalyzer
keywordQuery = phraseQuery
if opts.IsKeywordFuzzy {
phraseQuery.Fuzziness = len(opts.Keyword) / fuzzyDenominator
phraseQuery.Fuzziness = inner_bleve.GuessFuzzinessByKeyword(opts.Keyword)
}
if len(opts.RepoIDs) > 0 {

View File

@ -47,3 +47,15 @@ func openIndexer(path string, latestVersion int) (bleve.Index, int, error) {
return index, 0, nil
}
func GuessFuzzinessByKeyword(s string) int {
// according to https://github.com/blevesearch/bleve/issues/1563, the supported max fuzziness is 2
// magic number 4 was chosen to determine the levenshtein distance per each character of a keyword
// BUT, when using CJK (eg: `갃갃갃` `啊啊啊`), it mismatches a lot.
for _, r := range s {
if r >= 128 {
return 0
}
}
return min(2, len(s)/4)
}

View File

@ -35,11 +35,7 @@ func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
})
}
const (
maxBatchSize = 16
// fuzzyDenominator determines the levenshtein distance per each character of a keyword
fuzzyDenominator = 4
)
const maxBatchSize = 16
// IndexerData an update to the issue indexer
type IndexerData internal.IndexerData
@ -162,7 +158,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
if options.Keyword != "" {
fuzziness := 0
if options.IsFuzzyKeyword {
fuzziness = len(options.Keyword) / fuzzyDenominator
fuzziness = inner_bleve.GuessFuzzinessByKeyword(options.Keyword)
}
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{

View File

@ -591,17 +591,16 @@ func replaceContentList(node *html.Node, i, j int, newNodes []*html.Node) {
func mentionProcessor(ctx *RenderContext, node *html.Node) {
start := 0
next := node.NextSibling
for node != nil && node != next && start < len(node.Data) {
// We replace only the first mention; other mentions will be addressed later
found, loc := references.FindFirstMentionBytes([]byte(node.Data[start:]))
for node != nil {
found, loc := references.FindFirstMentionBytes(util.UnsafeStringToBytes(node.Data[start:]))
if !found {
return
node = node.NextSibling
start = 0
continue
}
loc.Start += start
loc.End += start
mention := node.Data[loc.Start:loc.End]
var teams string
teams, ok := ctx.Metas["teams"]
// FIXME: util.URLJoin may not be necessary here:
// - setting.AppURL is defined to have a terminal '/' so unless mention[1:]
@ -623,10 +622,10 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention"))
node = node.NextSibling.NextSibling
start = 0
} else {
node = node.NextSibling
start = loc.End
}
start = 0
}
}

View File

@ -29,7 +29,7 @@ var (
// TODO: fix invalid linking issue
// mentionPattern matches all mentions in the form of "@user" or "@org/team"
mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[0-9a-zA-Z-_]+|@[0-9a-zA-Z-_]+\/?[0-9a-zA-Z-_]+|@[0-9a-zA-Z-_][0-9a-zA-Z-_.]+\/?[0-9a-zA-Z-_.]+[0-9a-zA-Z-_])(?:\s|[:,;.?!]\s|[:,;.?!]?$|\)|\])`)
mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[-\w][-.\w]*?|@[-\w][-.\w]*?/[-\w][-.\w]*?)(?:\s|$|[:,;.?!](\s|$)|'|\)|\])`)
// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\'|\")([#!][0-9]+)(?:\s|$|\)|\]|\'|\"|[:;,.?!]\s|[:;,.?!]$)`)
// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234

View File

@ -392,6 +392,7 @@ func TestRegExp_mentionPattern(t *testing.T) {
{"@gitea,", "@gitea"},
{"@gitea;", "@gitea"},
{"@gitea/team1;", "@gitea/team1"},
{"@user's idea", "@user"},
}
falseTestCases := []string{
"@ 0",
@ -412,7 +413,6 @@ func TestRegExp_mentionPattern(t *testing.T) {
for _, testCase := range trueTestCases {
found := mentionPattern.FindStringSubmatch(testCase.pat)
assert.Len(t, found, 2)
assert.Equal(t, testCase.exp, found[1])
}
for _, testCase := range falseTestCases {

View File

@ -207,3 +207,8 @@ func TestRenderLabels(t *testing.T) {
expected = `/owner/repo/pulls?labels=123`
assert.Contains(t, RenderLabels(ctx, locale, []*issues.Label{label}, "/owner/repo", issue), expected)
}
func TestUserMention(t *testing.T) {
rendered := RenderMarkdownToHtml(context.Background(), "@no-such-user @mention-user @mention-user")
assert.EqualValues(t, `<p>@no-such-user <a href="/mention-user" rel="nofollow">@mention-user</a> <a href="/mention-user" rel="nofollow">@mention-user</a></p>`, strings.TrimSpace(string(rendered)))
}

View File

@ -28,6 +28,7 @@ func Search(ctx *context.Context) {
ctx.Data["Language"] = language
ctx.Data["IsFuzzy"] = isFuzzy
ctx.Data["PageIsViewCode"] = true
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
if keyword == "" {
ctx.HTML(http.StatusOK, tplSearch)
@ -86,7 +87,6 @@ func Search(ctx *context.Context) {
}
}
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["Repo"] = ctx.Repo.Repository
ctx.Data["SearchResults"] = searchResults
ctx.Data["SearchResultLanguages"] = searchResultLanguages

View File

@ -54,7 +54,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
const GzipMinSize = 1400 // min size to compress for the body size of response
var GzipMinSize = 1400 // min size to compress for the body size of response
// optionsCorsHandler return a http handler which sets CORS options if enabled by config, it blocks non-CORS OPTIONS requests.
func optionsCorsHandler() func(next http.Handler) http.Handler {

View File

@ -69,9 +69,9 @@
<div class="js-branch-tag-selector {{if .ContainerClasses}}{{.ContainerClasses}}{{end}}">
{{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}}
<div class="ui dropdown custom">
<button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex tw-m-0">
<span class="text tw-flex tw-items-center tw-mr-1 gt-ellipsis">
<div class="ui dropdown custom branch-selector-dropdown">
<div class="ui button branch-dropdown-button">
<span class="flex-text-block gt-ellipsis">
{{if .release}}
{{ctx.Locale.Tr "repo.release.compare"}}
{{else}}
@ -84,6 +84,6 @@
{{end}}
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</button>
</div>
</div>
</div>

View File

@ -4,10 +4,12 @@
<form method="post" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/ref" id="update_issueref_form">
{{$.CsrfTokenHtml}}
</form>
{{/* TODO: share this branch selector dropdown with the same in repo page */}}
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating filter select-branch dropdown tw-max-w-full" data-no-results="{{ctx.Locale.Tr "no_results_found"}}">
<div class="ui basic small button">
<span class="text branch-name gt-ellipsis">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span>
<div class="ui dropdown select-branch branch-selector-dropdown {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}}"
data-no-results="{{ctx.Locale.Tr "no_results_found"}}"
{{if not .Issue}}data-for-new-issue="true"{{end}}
>
<div class="ui button branch-dropdown-button">
<span class="text-branch-name gt-ellipsis">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span>
{{if .HasIssuesOrPullsWritePermission}}{{svg "octicon-triangle-down" 14 "dropdown icon"}}{{end}}
</div>
<div class="menu">
@ -15,26 +17,18 @@
<i class="icon">{{svg "octicon-filter" 16}}</i>
<input name="search" placeholder="{{ctx.Locale.Tr "repo.filter_branch_and_tag"}}...">
</div>
<div class="header">
<div class="ui grid">
<div class="two column row">
<a class="reference column muted" href="#" data-target="#branch-list">
<span class="text black">
{{svg "octicon-git-branch" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.branches"}}
</span>
</a>
<a class="reference column muted" href="#" data-target="#tag-list">
<span class="text">
{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.tags"}}
</span>
</a>
</div>
</div>
<div class="branch-tag-tab">
<a class="branch-tag-item reference column muted active" href="#" data-target="#branch-list">
{{svg "octicon-git-branch" 16 "tw-mr-1"}} {{ctx.Locale.Tr "repo.branches"}}
</a>
<a class="branch-tag-item reference column muted" href="#" data-target="#tag-list">
{{svg "octicon-tag" 16 "tw-mr-1"}} {{ctx.Locale.Tr "repo.tags"}}
</a>
</div>
<div class="branch-tag-divider"></div>
<div id="branch-list" class="scrolling menu reference-list-menu {{if not .Issue}}new-issue{{end}}">
{{if .Reference}}
<div class="item text small" data-id="" data-id-selector="#ref_selector"><strong><a href="#">{{ctx.Locale.Tr "repo.clear_ref"}}</a></strong></div>
<div id="branch-list" class="scrolling menu reference-list-menu">
{{if or .Reference (not .Issue)}}
<div class="item text small" data-id="" data-name="{{ctx.Locale.Tr "repo.issues.no_ref"}}" data-id-selector="#ref_selector"><strong><a href="#">{{ctx.Locale.Tr "repo.clear_ref"}}</a></strong></div>
{{end}}
{{range .Branches}}
<div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector" title="{{.}}">{{.}}</div>
@ -42,9 +36,9 @@
<div class="item">{{ctx.Locale.Tr "no_results_found"}}</div>
{{end}}
</div>
<div id="tag-list" class="scrolling menu reference-list-menu {{if not .Issue}}new-issue{{end}} tw-hidden">
{{if .Reference}}
<div class="item text small" data-id="" data-id-selector="#ref_selector"><strong><a href="#">{{ctx.Locale.Tr "repo.clear_ref"}}</a></strong></div>
<div id="tag-list" class="scrolling menu reference-list-menu tw-hidden">
{{if or .Reference (not .Issue)}}
<div class="item text small" data-id="" data-name="{{ctx.Locale.Tr "repo.issues.no_ref"}}" data-id-selector="#ref_selector"><strong><a href="#">{{ctx.Locale.Tr "repo.clear_ref"}}</a></strong></div>
{{end}}
{{range .Tags}}
<div class="item" data-id="refs/tags/{{.}}" data-name="tags/{{.}}" data-id-selector="#ref_selector">{{.}}</div>

View File

@ -1,6 +1,6 @@
<div class="ui labels list">
<span class="no-select item {{if .root.HasSelectedLabel}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_label"}}</span>
<span class="labels-list">
<span class="no-select {{if .root.HasSelectedLabel}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_label"}}</span>
{{range .root.Labels}}
{{template "repo/issue/labels/label" dict "root" $.root "label" .}}
{{end}}

View File

@ -0,0 +1,33 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"io"
"net/http"
"testing"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/web"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
)
func TestRepoDownloadArchive(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer test.MockVariableValue(&setting.EnableGzip, true)()
defer test.MockVariableValue(&web.GzipMinSize, 10)()
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
req := NewRequest(t, "GET", "/user2/repo1/archive/master.zip")
req.Header.Set("Accept-Encoding", "gzip")
resp := MakeRequest(t, req, http.StatusOK)
bs, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
assert.Empty(t, resp.Header().Get("Content-Encoding"))
assert.Equal(t, 320, len(bs))
}

View File

@ -871,6 +871,7 @@ input:-webkit-autofill:active,
.ui.dropdown .scrolling.menu {
border-color: var(--color-secondary);
border-radius: 0 0 var(--border-radius) var(--border-radius) !important;
}
.color-preview {

View File

@ -152,6 +152,7 @@
}
.ui.attached.segment:has(+ .ui[class*="top attached"].header),
.ui.attached.segment:has(+ .page.buttons),
.ui.attached.segment:last-child,
.ui.segment:has(+ .ui.segment:not(.attached)),
.ui.attached.segment:has(+ .ui.modal) {

View File

@ -2742,23 +2742,6 @@ tbody.commit-list {
}
}
.branch-dropdown-button {
max-width: 340px;
vertical-align: bottom !important;
}
@media (min-width: 768px) and (max-width: 991.98px) {
.branch-dropdown-button {
max-width: 185px;
}
}
@media (max-width: 767.98px) {
.branch-dropdown-button {
max-width: 165px;
}
}
.commit-status-header {
/* reset the default ".ui.attached.header" styles, to use the outer border */
border: none !important;
@ -2835,32 +2818,70 @@ tbody.commit-list {
max-height: 200px;
}
/* Branch tag selector - TODO: Merge this into the same selector on repo page */
.repository .issue-content .issue-content-right .ui.grid .column.row {
padding: 10px;
padding-bottom: 0;
.branch-selector-dropdown {
max-width: 100%;
}
.repository .issue-content .issue-content-right .ui.grid .column.muted {
padding: 0;
.ui.dropdown.branch-selector-dropdown > .menu {
margin-top: 4px;
}
.repository .issue-content .issue-content-right .ui.grid .column.muted .text {
.branch-selector-dropdown .branch-dropdown-button {
margin: 0;
max-width: 340px;
line-height: var(--line-height-default);
}
/* FIXME: These media selectors are not ideal (just keep them from old code).
There are many different pages, some need the max-width while some others don't,
they should be tested and improved in the future. */
@media (min-width: 768px) and (max-width: 991.98px) {
.branch-selector-dropdown .branch-dropdown-button {
max-width: 185px;
}
}
@media (max-width: 767.98px) {
.branch-selector-dropdown .branch-dropdown-button {
max-width: 165px;
}
}
.branch-selector-dropdown .branch-tag-tab {
padding: 0 10px;
}
.branch-selector-dropdown .branch-tag-item {
display: inline-block;
padding: 10px;
width: 100%;
text-align: center;
border: 1px solid transparent;
border-bottom: none;
}
.repository .issue-content .issue-content-right .ui.grid .column.muted .text.black {
.branch-selector-dropdown .branch-tag-item.active {
border-color: var(--color-secondary);
background: var(--color-menu);
border-top-left-radius: var(--border-radius);
border-top-right-radius: var(--border-radius);
}
.repository .issue-content .issue-content-right .ui.dropdown .scrolling.menu {
border-top: none;
}
.repository .issue-content .issue-content-right .branch-tag-divider {
margin-top: -1px;
.branch-selector-dropdown .branch-tag-divider {
margin-top: -1px !important;
border-top: 1px solid var(--color-secondary);
}
.branch-selector-dropdown .scrolling.menu {
border-top: none !important;
}
.branch-selector-dropdown .menu .item .rss-icon {
visibility: hidden; /* only show RSS icon on hover */
}
.branch-selector-dropdown .menu .item:hover .rss-icon {
visibility: visible;
}
.branch-selector-dropdown .scrolling.menu .loading-indicator {
height: 4em;
}

View File

@ -246,9 +246,9 @@ export function initRepoBranchTagSelector(selector) {
export default sfc; // activate IDE's Vue plugin
</script>
<template>
<div class="ui dropdown custom">
<button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex tw-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
<span class="text tw-flex tw-items-center tw-mr-1 gt-ellipsis">
<div class="ui dropdown custom branch-selector-dropdown">
<div class="ui button branch-dropdown-button" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
<span class="flex-text-block gt-ellipsis">
<template v-if="release">{{ textReleaseCompare }}</template>
<template v-else>
<svg-icon v-if="isViewTag" name="octicon-tag"/>
@ -257,7 +257,7 @@ export default sfc; // activate IDE's Vue plugin
</template>
</span>
<svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/>
</button>
</div>
<div class="menu transition" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak>
<div class="ui icon search input">
<i class="icon"><svg-icon name="octicon-filter" :size="16"/></i>
@ -317,43 +317,3 @@ export default sfc; // activate IDE's Vue plugin
</div>
</div>
</template>
<style scoped>
.branch-tag-tab {
padding: 0 10px;
}
.branch-tag-item {
display: inline-block;
padding: 10px;
border: 1px solid transparent;
border-bottom: none;
}
.branch-tag-item.active {
border-color: var(--color-secondary);
background: var(--color-menu);
border-top-left-radius: var(--border-radius);
border-top-right-radius: var(--border-radius);
}
.branch-tag-divider {
margin-top: -1px !important;
border-top: 1px solid var(--color-secondary);
}
.scrolling.menu {
border-top: none !important;
}
.menu .item .rss-icon {
display: none; /* only show RSS icon on hover */
}
.menu .item:hover .rss-icon {
display: inline-block;
}
.scrolling.menu .loading-indicator {
height: 4em;
}
</style>

View File

@ -19,7 +19,7 @@ import {initCompReactionSelector} from './comp/ReactionSelector.js';
import {initRepoSettingBranches} from './repo-settings.js';
import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.js';
import {initRepoPullRequestCommitStatus} from './repo-issue-pr-status.js';
import {hideElem, showElem} from '../utils/dom.js';
import {hideElem, queryElemChildren, showElem} from '../utils/dom.js';
import {POST} from '../modules/fetch.js';
import {initRepoIssueCommentEdit} from './repo-issue-edit.js';
@ -56,16 +56,19 @@ export function initRepoCommentForm() {
}
function initBranchSelector() {
const $selectBranch = $('.ui.select-branch');
const elSelectBranch = document.querySelector('.ui.dropdown.select-branch');
const isForNewIssue = elSelectBranch.getAttribute('data-for-new-issue') === 'true';
const $selectBranch = $(elSelectBranch);
const $branchMenu = $selectBranch.find('.reference-list-menu');
const $isNewIssue = $branchMenu.hasClass('new-issue');
$branchMenu.find('.item:not(.no-select)').on('click', async function () {
const selectedValue = $(this).data('id');
$branchMenu.find('.item:not(.no-select)').on('click', async function (e) {
e.preventDefault();
const selectedValue = $(this).data('id'); // eg: "refs/heads/my-branch"
const editMode = $('#editing_mode').val();
$($(this).data('id-selector')).val(selectedValue);
if ($isNewIssue) {
$selectBranch.find('.ui .branch-name').text($(this).data('name'));
return;
if (isForNewIssue) {
elSelectBranch.querySelector('.text-branch-name').textContent = this.getAttribute('data-name');
return; // only update UI&form, do not send request/reload
}
if (editMode === 'true') {
@ -84,9 +87,9 @@ export function initRepoCommentForm() {
});
$selectBranch.find('.reference.column').on('click', function () {
hideElem($selectBranch.find('.scrolling.reference-list-menu'));
$selectBranch.find('.reference .text').removeClass('black');
showElem($($(this).data('target')));
$(this).find('.text').addClass('black');
showElem(this.getAttribute('data-target'));
queryElemChildren(this.parentNode, '.branch-tag-item', (el) => el.classList.remove('active'));
this.classList.add('active');
return false;
});
}