From 47193dbcd97555d0c72d3219a372d9d5d01755d3 Mon Sep 17 00:00:00 2001
From: qwerty287 <80460567+qwerty287@users.noreply.github.com>
Date: Sat, 2 Oct 2021 09:46:43 +0200
Subject: [PATCH] Add option to copy line permalink (#17145)

* Add option to copy line permalink

* Fix lint

* Apply review suggestions

* Update code and fix lint

* Use features/clipboard.js framework
---
 options/locale/locale_en-US.ini |  1 +
 templates/repo/view_file.tmpl   | 19 ++++++------
 web_src/js/index.js             | 51 ++++++++++++++++-----------------
 3 files changed, 37 insertions(+), 34 deletions(-)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index bd95cc8d892..bc03f866194 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -970,6 +970,7 @@ file_view_rendered = View Rendered
 file_view_raw = View Raw
 file_permalink = Permalink
 file_too_large = The file is too large to be shown.
+file_copy_permalink = Copy Permalink
 video_not_supported_in_browser = Your browser does not support the HTML5 'video' tag.
 audio_not_supported_in_browser = Your browser does not support the HTML5 'audio' tag.
 stored_lfs = Stored with Git LFS
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index b6198bd0e20..8e75bcb4ca4 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -112,17 +112,20 @@
 						{{end}}
 					</tbody>
 				</table>
-					{{if $.Permission.CanRead $.UnitTypeIssues}}
-						<div class="code-line-menu ui fluid popup transition hidden">
-							<div class="ui column relaxed equal height">
-								<div class="column">
-									<div class="ui link list">
-										<a class="item ref-in-new-issue" href="{{.RepoLink}}/issues/new?body={{URLJoin AppUrl .RepoLink}}/src/commit/{{.CommitID}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.issues.context.reference_issue"}}</a>
-									</div>
+				<div class="code-line-menu ui fluid popup transition hidden">
+					<div class="ui column relaxed equal height">
+						<div class="column">
+							{{if $.Permission.CanRead $.UnitTypeIssues}}
+								<div class="ui link list">
+									<a class="item ref-in-new-issue" href="{{.RepoLink}}/issues/new?body={{URLJoin AppUrl .RepoLink}}/src/commit/{{.CommitID}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.issues.context.reference_issue"}}</a>
 								</div>
+							{{end}}
+							<div class="ui link list">
+								<a data-clipboard-text="{{URLJoin AppUrl .RepoLink}}/src/commit/{{.CommitID}}/{{EscapePound .TreePath}}" class="item copy-line-permalink">{{.i18n.Tr "repo.file_copy_permalink"}}</a>
 							</div>
 						</div>
-					{{end}}
+					</div>
+				</div>
 				{{end}}
 			{{end}}
 		</div>
diff --git a/web_src/js/index.js b/web_src/js/index.js
index 5c1510fb89f..12539c4ec91 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -2917,6 +2917,27 @@ function deSelect() {
 
 function selectRange($list, $select, $from) {
   $list.removeClass('active');
+
+  // add hashchange to permalink
+  const $issue = $('a.ref-in-new-issue');
+  const $copyPermalink = $('a.copy-line-permalink');
+
+  if ($issue.length === 0 || $copyPermalink.length === 0) {
+    return;
+  }
+
+  const updateIssueHref = function(anchor) {
+    let href = $issue.attr('href');
+    href = `${href.replace(/%23L\d+$|%23L\d+-L\d+$/, '')}%23${anchor}`;
+    $issue.attr('href', href);
+  };
+
+  const updateCopyPermalinkHref = function(anchor) {
+    let link = $copyPermalink.attr('data-clipboard-text');
+    link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`;
+    $copyPermalink.attr('data-clipboard-text', link);
+  };
+
   if ($from) {
     let a = parseInt($select.attr('rel').substr(1));
     let b = parseInt($from.attr('rel').substr(1));
@@ -2934,38 +2955,16 @@ function selectRange($list, $select, $from) {
       $list.filter(classes.join(',')).addClass('active');
       changeHash(`#L${a}-L${b}`);
 
-      // add hashchange to permalink
-      const $issue = $('a.ref-in-new-issue');
-
-      if ($issue.length === 0) {
-        return;
-      }
-
-      const matched = $issue.attr('href').match(/%23L\d+$|%23L\d+-L\d+$/);
-      if (matched) {
-        $issue.attr('href', $issue.attr('href').replace($issue.attr('href').substr(matched.index), `%23L${a}-L${b}`));
-      } else {
-        $issue.attr('href', `${$issue.attr('href')}%23L${a}-L${b}`);
-      }
+      updateIssueHref(`L${a}-L${b}`);
+      updateCopyPermalinkHref(`L${a}-L${b}`);
       return;
     }
   }
   $select.addClass('active');
   changeHash(`#${$select.attr('rel')}`);
 
-  // add hashchange to permalink
-  const $issue = $('a.ref-in-new-issue');
-
-  if ($issue.length === 0) {
-    return;
-  }
-
-  const matched = $issue.attr('href').match(/%23L\d+$|%23L\d+-L\d+$/);
-  if (matched) {
-    $issue.attr('href', $issue.attr('href').replace($issue.attr('href').substr(matched.index), `%23${$select.attr('rel')}`));
-  } else {
-    $issue.attr('href', `${$issue.attr('href')}%23${$select.attr('rel')}`);
-  }
+  updateIssueHref($select.attr('rel'));
+  updateCopyPermalinkHref($select.attr('rel'));
 }
 
 $(() => {