From 9db221780f28e161c02c4106f63d0b6f185933d4 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 11 Nov 2022 11:22:36 +0100
Subject: [PATCH] Ignore line anchor links with leading zeroes (#21728)

Fixes: https://github.com/go-gitea/gitea/issues/21722

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
---
 web_src/js/features/repo-code.js      | 36 ++++++++++++++++-----------
 web_src/js/features/repo-code.test.js | 18 ++++++++++++++
 2 files changed, 39 insertions(+), 15 deletions(-)
 create mode 100644 web_src/js/features/repo-code.test.js

diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js
index ad27036372..ef6b61196b 100644
--- a/web_src/js/features/repo-code.js
+++ b/web_src/js/features/repo-code.js
@@ -5,6 +5,8 @@ import {createTippy, showTemporaryTooltip} from '../modules/tippy.js';
 import {copyToClipboard} from './clipboard.js';
 
 const {i18n} = window.config;
+export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/;
+export const rangeAnchorRegex = /^#(L[1-9][0-9]*)-(L[1-9][0-9]*)$/;
 
 function changeHash(hash) {
   if (window.history.pushState) {
@@ -149,7 +151,7 @@ export function initRepoCodeView() {
     });
 
     $(window).on('hashchange', () => {
-      let m = window.location.hash.match(/^#(L\d+)-(L\d+)$/);
+      let m = window.location.hash.match(rangeAnchorRegex);
       let $list;
       if ($('div.blame').length) {
         $list = $('.code-view td.lines-code.blame-code');
@@ -159,27 +161,31 @@ export function initRepoCodeView() {
       let $first;
       if (m) {
         $first = $list.filter(`[rel=${m[1]}]`);
-        selectRange($list, $first, $list.filter(`[rel=${m[2]}]`));
+        if ($first.length) {
+          selectRange($list, $first, $list.filter(`[rel=${m[2]}]`));
 
-        // show code view menu marker (don't show in blame page)
-        if ($('div.blame').length === 0) {
-          showLineButton();
+          // show code view menu marker (don't show in blame page)
+          if ($('div.blame').length === 0) {
+            showLineButton();
+          }
+
+          $('html, body').scrollTop($first.offset().top - 200);
+          return;
         }
-
-        $('html, body').scrollTop($first.offset().top - 200);
-        return;
       }
-      m = window.location.hash.match(/^#(L|n)(\d+)$/);
+      m = window.location.hash.match(singleAnchorRegex);
       if (m) {
         $first = $list.filter(`[rel=L${m[2]}]`);
-        selectRange($list, $first);
+        if ($first.length) {
+          selectRange($list, $first);
 
-        // show code view menu marker (don't show in blame page)
-        if ($('div.blame').length === 0) {
-          showLineButton();
+          // show code view menu marker (don't show in blame page)
+          if ($('div.blame').length === 0) {
+            showLineButton();
+          }
+
+          $('html, body').scrollTop($first.offset().top - 200);
         }
-
-        $('html, body').scrollTop($first.offset().top - 200);
       }
     }).trigger('hashchange');
   }
diff --git a/web_src/js/features/repo-code.test.js b/web_src/js/features/repo-code.test.js
new file mode 100644
index 0000000000..3bd1973c1f
--- /dev/null
+++ b/web_src/js/features/repo-code.test.js
@@ -0,0 +1,18 @@
+import {test, expect} from 'vitest';
+import {singleAnchorRegex, rangeAnchorRegex} from './repo-code.js';
+
+test('singleAnchorRegex', () => {
+  expect(singleAnchorRegex.test('#L0')).toEqual(false);
+  expect(singleAnchorRegex.test('#L1')).toEqual(true);
+  expect(singleAnchorRegex.test('#L01')).toEqual(false);
+  expect(singleAnchorRegex.test('#n0')).toEqual(false);
+  expect(singleAnchorRegex.test('#n1')).toEqual(true);
+  expect(singleAnchorRegex.test('#n01')).toEqual(false);
+});
+
+test('rangeAnchorRegex', () => {
+  expect(rangeAnchorRegex.test('#L0-L10')).toEqual(false);
+  expect(rangeAnchorRegex.test('#L1-L10')).toEqual(true);
+  expect(rangeAnchorRegex.test('#L01-L10')).toEqual(false);
+  expect(rangeAnchorRegex.test('#L1-L01')).toEqual(false);
+});