diff --git a/modules/context/context.go b/modules/context/context.go
index 5876e23cc40a2..1eff1459a14af 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -628,7 +628,9 @@ func (ctx *Context) Value(key interface{}) interface{} {
if key == git.RepositoryContextKey && ctx.Repo != nil {
return ctx.Repo.GitRepo
}
-
+ if key == translation.ContextKey && ctx.Locale != nil {
+ return ctx.Locale
+ }
return ctx.Req.Context().Value(key)
}
diff --git a/modules/markup/html.go b/modules/markup/html.go
index 76fc54cf465eb..11888b8536353 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/regexplru"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates/vars"
+ "code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
"golang.org/x/net/html"
@@ -97,14 +98,30 @@ var issueFullPattern *regexp.Regexp
// Once for to prevent races
var issueFullPatternOnce sync.Once
+// regexp for full links to hash comment in pull request files changed tab
+var filesChangedFullPattern *regexp.Regexp
+
+// Once for to prevent races
+var filesChangedFullPatternOnce sync.Once
+
func getIssueFullPattern() *regexp.Regexp {
issueFullPatternOnce.Do(func() {
+ // example: https://domain/org/repo/pulls/27#hash
issueFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) +
`[\w_.-]+/[\w_.-]+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#](\S+)?)?\b`)
})
return issueFullPattern
}
+func getFilesChangedFullPattern() *regexp.Regexp {
+ filesChangedFullPatternOnce.Do(func() {
+ // example: https://domain/org/repo/pulls/27/files#hash
+ filesChangedFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) +
+ `[\w_.-]+/[\w_.-]+/pulls/((?:\w{1,10}-)?[1-9][0-9]*)/files([\?|#](\S+)?)?\b`)
+ })
+ return filesChangedFullPattern
+}
+
// CustomLinkURLSchemes allows for additional schemes to be detected when parsing links within text
func CustomLinkURLSchemes(schemes []string) {
schemes = append(schemes, "http", "https")
@@ -793,15 +810,30 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
}
-
next := node.NextSibling
for node != nil && node != next {
m := getIssueFullPattern().FindStringSubmatchIndex(node.Data)
if m == nil {
return
}
+
+ mDiffView := getFilesChangedFullPattern().FindStringSubmatchIndex(node.Data)
+ // leave it as it is if the link is from "Files Changed" tab in PR Diff View https://domain/org/repo/pulls/27/files
+ if mDiffView != nil {
+ return
+ }
+
link := node.Data[m[0]:m[1]]
- id := "#" + node.Data[m[2]:m[3]]
+ text := "#" + node.Data[m[2]:m[3]]
+ // if m[4] and m[5] is not -1, then link is to a comment
+ // indicate that in the text by appending (comment)
+ if m[4] != -1 && m[5] != -1 {
+ if locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale); ok {
+ text += " " + locale.Tr("repo.from_comment")
+ } else {
+ text += " (comment)"
+ }
+ }
// extract repo and org name from matched link like
// http://localhost:3000/gituser/myrepo/issues/1
@@ -810,12 +842,10 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
matchRepo := linkParts[len(linkParts)-3]
if matchOrg == ctx.Metas["user"] && matchRepo == ctx.Metas["repo"] {
- // TODO if m[4]:m[5] is not nil, then link is to a comment,
- // and we should indicate that in the text somehow
- replaceContent(node, m[0], m[1], createLink(link, id, "ref-issue"))
+ replaceContent(node, m[0], m[1], createLink(link, text, "ref-issue"))
} else {
- orgRepoID := matchOrg + "/" + matchRepo + id
- replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "ref-issue"))
+ text = matchOrg + "/" + matchRepo + text
+ replaceContent(node, m[0], m[1], createLink(link, text, "ref-issue"))
}
node = node.NextSibling.NextSibling
}
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index a048f1f527889..7e04b03531d87 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -330,13 +330,17 @@ func TestRender_FullIssueURLs(t *testing.T) {
test("Look here http://localhost:3000/person/repo/issues/4",
`Look here person/repo#4`)
test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
- `person/repo#4`)
+ `person/repo#4 (comment)`)
test("http://localhost:3000/gogits/gogs/issues/4",
`#4`)
test("http://localhost:3000/gogits/gogs/issues/4 test",
`#4 test`)
test("http://localhost:3000/gogits/gogs/issues/4?a=1&b=2#comment-123 test",
- `#4 test`)
+ `#4 (comment) test`)
+ test("http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24",
+ "http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24")
+ test("http://localhost:3000/testOrg/testOrgRepo/pulls/2/files",
+ "http://localhost:3000/testOrg/testOrgRepo/pulls/2/files")
}
func TestRegExp_sha1CurrentPattern(t *testing.T) {
diff --git a/modules/translation/translation.go b/modules/translation/translation.go
index 5a1009bfa3d80..3165390c3238c 100644
--- a/modules/translation/translation.go
+++ b/modules/translation/translation.go
@@ -18,6 +18,10 @@ import (
"golang.org/x/text/language"
)
+type contextKey struct{}
+
+var ContextKey interface{} = &contextKey{}
+
// Locale represents an interface to translation
type Locale interface {
Language() string
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index b7180d7967119..5834703556c29 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1104,6 +1104,7 @@ download_file = Download file
normal_view = Normal View
line = line
lines = lines
+from_comment = (comment)
editor.add_file = Add File
editor.new_file = New File
diff --git a/web_src/js/features/comp/MarkupContentPreview.js b/web_src/js/features/comp/MarkupContentPreview.js
index 61fd699b29714..a32bf301843c8 100644
--- a/web_src/js/features/comp/MarkupContentPreview.js
+++ b/web_src/js/features/comp/MarkupContentPreview.js
@@ -1,5 +1,6 @@
import $ from 'jquery';
import {initMarkupContent} from '../../markup/content.js';
+import {attachRefIssueContextPopup} from '../contextpopup.js';
const {csrfToken} = window.config;
@@ -16,6 +17,8 @@ export function initCompMarkupContentPreviewTab($form) {
}, (data) => {
const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`);
$previewPanel.html(data);
+ const refIssues = $previewPanel.find('p .ref-issue');
+ attachRefIssueContextPopup(refIssues);
initMarkupContent();
});
});
diff --git a/web_src/js/features/contextpopup.js b/web_src/js/features/contextpopup.js
index c685d93db04a3..7b37035547a13 100644
--- a/web_src/js/features/contextpopup.js
+++ b/web_src/js/features/contextpopup.js
@@ -6,8 +6,11 @@ import {createTippy} from '../modules/tippy.js';
export function initContextPopups() {
const refIssues = $('.ref-issue');
- if (!refIssues.length) return;
+ attachRefIssueContextPopup(refIssues);
+}
+export function attachRefIssueContextPopup(refIssues) {
+ if (!refIssues.length) return;
refIssues.each(function () {
if ($(this).hasClass('ref-external-issue')) {
return;
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index f7fc1aa4eb92d..3689c342721c7 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -26,6 +26,7 @@ import {initCompReactionSelector} from './comp/ReactionSelector.js';
import {initRepoSettingBranches} from './repo-settings.js';
import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.js';
import {hideElem, showElem} from '../utils/dom.js';
+import {attachRefIssueContextPopup} from './contextpopup.js';
const {csrfToken} = window.config;
@@ -439,6 +440,8 @@ async function onEditContent(event) {
} else {
$renderContent.html(data.content);
$rawContent.text($textarea.val());
+ const refIssues = $renderContent.find('p .ref-issue');
+ attachRefIssueContextPopup(refIssues);
}
const $content = $segment;
if (!$content.find('.dropzone-attachments').length) {