This instance was upgraded to Heptapod 0.27.2 on 2021-12-08 at 22:00 UTC+1

Commit b88da58c authored by Robert Speicher's avatar Robert Speicher
Browse files

Add `reference_pattern` to Referable models

parent c1463cdd900e
...@@ -62,6 +62,19 @@ def ==(other) ...@@ -62,6 +62,19 @@ def ==(other)
(self.class === other) && (raw == other.raw) (self.class === other) && (raw == other.raw)
end end
def self.reference_prefix
'@'
end
# Pattern used to extract commit references from text
#
# The SHA can be between 6 and 40 hex characters.
#
# This pattern supports cross-project references.
def self.reference_pattern
%r{(?:#{Project.reference_pattern}#{reference_prefix})?(?<commit>\h{6,40})}
end
def to_reference(from_project = nil) def to_reference(from_project = nil)
if cross_project_reference?(from_project) if cross_project_reference?(from_project)
"#{project.to_reference}@#{id}" "#{project.to_reference}@#{id}"
......
...@@ -29,10 +29,24 @@ class CommitRange ...@@ -29,10 +29,24 @@ class CommitRange
# See `exclude_start?` # See `exclude_start?`
attr_reader :exclude_start attr_reader :exclude_start
# The beginning and ending SHA sums can be between 6 and 40 hex characters, # The beginning and ending SHAs can be between 6 and 40 hex characters, and
# and the range selection can be double- or triple-dot. # the range notation can be double- or triple-dot.
PATTERN = /\h{6,40}\.{2,3}\h{6,40}/ PATTERN = /\h{6,40}\.{2,3}\h{6,40}/
def self.reference_prefix
'@'
end
# Pattern used to extract commit range references from text
#
# This pattern supports cross-project references.
def self.reference_pattern
%r{
(?:#{Project.reference_pattern}#{reference_prefix})?
(?<commit_range>#{PATTERN})
}x
end
# Initialize a CommitRange # Initialize a CommitRange
# #
# range_string - The String commit range. # range_string - The String commit range.
......
...@@ -35,6 +35,16 @@ module ClassMethods ...@@ -35,6 +35,16 @@ module ClassMethods
def reference_prefix def reference_prefix
'' ''
end end
# Regexp pattern used to match references to this object
#
# This must be overridden by the including class.
#
# Returns Regexp
def reference_pattern
raise NotImplementedError,
%Q{#{self} does not implement "reference_pattern"}
end
end end
private private
......
...@@ -9,10 +9,6 @@ def to_s ...@@ -9,10 +9,6 @@ def to_s
@issue_identifier.to_s @issue_identifier.to_s
end end
def to_reference(_from_project = nil)
id
end
def id def id
@issue_identifier.to_s @issue_identifier.to_s
end end
...@@ -32,4 +28,13 @@ def ==(other) ...@@ -32,4 +28,13 @@ def ==(other)
def project def project
@project @project
end end
# Pattern used to extract `JIRA-123` issue references from text
def self.reference_pattern
%r{(?<issue>([A-Z\-]+-)\d+)}
end
def to_reference(_from_project = nil)
id
end
end end
...@@ -40,7 +40,11 @@ def sort(method) ...@@ -40,7 +40,11 @@ def sort(method)
end end
def reference_prefix def reference_prefix
'@' User.reference_prefix
end
def reference_pattern
User.reference_pattern
end end
end end
......
...@@ -50,12 +50,22 @@ class Issue < ActiveRecord::Base ...@@ -50,12 +50,22 @@ class Issue < ActiveRecord::Base
state :closed state :closed
end end
def hook_attrs
attributes
end
def self.reference_prefix def self.reference_prefix
'#' '#'
end end
def hook_attrs # Pattern used to extract `#123` issue references from text
attributes #
# This pattern supports cross-project references.
def self.reference_pattern
%r{
#{Project.reference_pattern}?
#{Regexp.escape(reference_prefix)}(?<issue>\d+)
}x
end end
def to_reference(from_project = nil) def to_reference(from_project = nil)
......
...@@ -40,6 +40,22 @@ def self.reference_prefix ...@@ -40,6 +40,22 @@ def self.reference_prefix
'~' '~'
end end
# Pattern used to extract label references from text
#
# TODO (rspeicher): Limit to double quotes (meh) or disallow single quotes in label names (bad).
def self.reference_pattern
%r{
#{reference_prefix}
(?:
(?<label_id>\d+) | # Integer-based label ID, or
(?<label_name>
[A-Za-z0-9_-]+ | # String-based single-word label title
['"][^&\?,]+['"] # String-based multi-word label surrounded in quotes
)
)
}x
end
# Returns the String necessary to reference this Label in Markdown # Returns the String necessary to reference this Label in Markdown
# #
# format - Symbol format to use (default: :id, optional: :name) # format - Symbol format to use (default: :id, optional: :name)
......
...@@ -140,6 +140,16 @@ def self.reference_prefix ...@@ -140,6 +140,16 @@ def self.reference_prefix
'!' '!'
end end
# Pattern used to extract `!123` merge request references from text
#
# This pattern supports cross-project references.
def self.reference_pattern
%r{
#{Project.reference_pattern}?
#{Regexp.escape(reference_prefix)}(?<merge_request>\d+)
}x
end
def to_reference(from_project = nil) def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{iid}" reference = "#{self.class.reference_prefix}#{iid}"
......
...@@ -248,6 +248,11 @@ def sort(method) ...@@ -248,6 +248,11 @@ def sort(method)
order_by(method) order_by(method)
end end
end end
def reference_pattern
name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR
%r{(?<project>#{name_pattern}/#{name_pattern})}
end
end end
def team def team
......
...@@ -56,6 +56,16 @@ def self.reference_prefix ...@@ -56,6 +56,16 @@ def self.reference_prefix
'$' '$'
end end
# Pattern used to extract `$123` snippet references from text
#
# This pattern supports cross-project references.
def self.reference_pattern
%r{
#{Project.reference_pattern}?
#{Regexp.escape(reference_prefix)}(?<snippet>\d+)
}x
end
def to_reference(from_project = nil) def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{id}" reference = "#{self.class.reference_prefix}#{id}"
......
...@@ -253,6 +253,14 @@ def build_user(attrs = {}) ...@@ -253,6 +253,14 @@ def build_user(attrs = {})
def reference_prefix def reference_prefix
'@' '@'
end end
# Pattern used to extract `@user` user references from text
def reference_pattern
%r{
#{Regexp.escape(reference_prefix)}
(?<user>#{Gitlab::Regex::NAMESPACE_REGEX_STR})
}x
end
end end
# #
......
...@@ -19,7 +19,7 @@ class CommitRangeReferenceFilter < ReferenceFilter ...@@ -19,7 +19,7 @@ class CommitRangeReferenceFilter < ReferenceFilter
# #
# Returns a String replaced with the return of the block. # Returns a String replaced with the return of the block.
def self.references_in(text) def self.references_in(text)
text.gsub(COMMIT_RANGE_PATTERN) do |match| text.gsub(CommitRange.reference_pattern) do |match|
yield match, $~[:commit_range], $~[:project] yield match, $~[:commit_range], $~[:project]
end end
end end
...@@ -30,13 +30,8 @@ def initialize(*args) ...@@ -30,13 +30,8 @@ def initialize(*args)
@commit_map = {} @commit_map = {}
end end
# Pattern used to extract commit range references from text
#
# This pattern supports cross-project references.
COMMIT_RANGE_PATTERN = /(#{PROJECT_PATTERN}@)?(?<commit_range>#{CommitRange::PATTERN})/
def call def call
replace_text_nodes_matching(COMMIT_RANGE_PATTERN) do |content| replace_text_nodes_matching(CommitRange.reference_pattern) do |content|
commit_range_link_filter(content) commit_range_link_filter(content)
end end
end end
......
...@@ -19,20 +19,13 @@ class CommitReferenceFilter < ReferenceFilter ...@@ -19,20 +19,13 @@ class CommitReferenceFilter < ReferenceFilter
# #
# Returns a String replaced with the return of the block. # Returns a String replaced with the return of the block.
def self.references_in(text) def self.references_in(text)
text.gsub(COMMIT_PATTERN) do |match| text.gsub(Commit.reference_pattern) do |match|
yield match, $~[:commit], $~[:project] yield match, $~[:commit], $~[:project]
end end
end end
# Pattern used to extract commit references from text
#
# The SHA1 sum can be between 6 and 40 hex characters.
#
# This pattern supports cross-project references.
COMMIT_PATTERN = /(#{PROJECT_PATTERN}@)?(?<commit>\h{6,40})/
def call def call
replace_text_nodes_matching(COMMIT_PATTERN) do |content| replace_text_nodes_matching(Commit.reference_pattern) do |content|
commit_link_filter(content) commit_link_filter(content)
end end
end end
......
...@@ -3,9 +3,6 @@ module Markdown ...@@ -3,9 +3,6 @@ module Markdown
# Common methods for ReferenceFilters that support an optional cross-project # Common methods for ReferenceFilters that support an optional cross-project
# reference. # reference.
module CrossProjectReference module CrossProjectReference
NAMING_PATTERN = Gitlab::Regex::NAMESPACE_REGEX_STR
PROJECT_PATTERN = "(?<project>#{NAMING_PATTERN}/#{NAMING_PATTERN})"
# Given a cross-project reference string, get the Project record # Given a cross-project reference string, get the Project record
# #
# Defaults to value of `context[:project]` if: # Defaults to value of `context[:project]` if:
......
...@@ -16,19 +16,16 @@ class ExternalIssueReferenceFilter < ReferenceFilter ...@@ -16,19 +16,16 @@ class ExternalIssueReferenceFilter < ReferenceFilter
# #
# Returns a String replaced with the return of the block. # Returns a String replaced with the return of the block.
def self.references_in(text) def self.references_in(text)
text.gsub(ISSUE_PATTERN) do |match| text.gsub(ExternalIssue.reference_pattern) do |match|
yield match, $~[:issue] yield match, $~[:issue]
end end
end end
# Pattern used to extract `JIRA-123` issue references from text
ISSUE_PATTERN = /(?<issue>([A-Z\-]+-)\d+)/
def call def call
# Early return if the project isn't using an external tracker # Early return if the project isn't using an external tracker
return doc if project.nil? || project.default_issues_tracker? return doc if project.nil? || project.default_issues_tracker?
replace_text_nodes_matching(ISSUE_PATTERN) do |content| replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content|
issue_link_filter(content) issue_link_filter(content)
end end
end end
......
...@@ -20,18 +20,13 @@ class IssueReferenceFilter < ReferenceFilter ...@@ -20,18 +20,13 @@ class IssueReferenceFilter < ReferenceFilter
# #
# Returns a String replaced with the return of the block. # Returns a String replaced with the return of the block.
def self.references_in(text) def self.references_in(text)
text.gsub(ISSUE_PATTERN) do |match| text.gsub(Issue.reference_pattern) do |match|
yield match, $~[:issue].to_i, $~[:project] yield match, $~[:issue].to_i, $~[:project]
end end
end end
# Pattern used to extract `#123` issue references from text
#
# This pattern supports cross-project references.
ISSUE_PATTERN = /#{PROJECT_PATTERN}?\#(?<issue>([a-zA-Z\-]+-)?\d+)/
def call def call
replace_text_nodes_matching(ISSUE_PATTERN) do |content| replace_text_nodes_matching(Issue.reference_pattern) do |content|
issue_link_filter(content) issue_link_filter(content)
end end
end end
......
...@@ -15,26 +15,13 @@ class LabelReferenceFilter < ReferenceFilter ...@@ -15,26 +15,13 @@ class LabelReferenceFilter < ReferenceFilter
# #
# Returns a String replaced with the return of the block. # Returns a String replaced with the return of the block.
def self.references_in(text) def self.references_in(text)
text.gsub(LABEL_PATTERN) do |match| text.gsub(Label.reference_pattern) do |match|
yield match, $~[:label_id].to_i, $~[:label_name] yield match, $~[:label_id].to_i, $~[:label_name]
end end
end end
# Pattern used to extract label references from text
#
# TODO (rspeicher): Limit to double quotes (meh) or disallow single quotes in label names (bad).
LABEL_PATTERN = %r{
~(
(?<label_id>\d+) | # Integer-based label ID, or
(?<label_name>
[A-Za-z0-9_-]+ | # String-based single-word label title
['"][^&\?,]+['"] # String-based multi-word label surrounded in quotes
)
)
}x
def call def call
replace_text_nodes_matching(LABEL_PATTERN) do |content| replace_text_nodes_matching(Label.reference_pattern) do |content|
label_link_filter(content) label_link_filter(content)
end end
end end
......
...@@ -20,18 +20,13 @@ class MergeRequestReferenceFilter < ReferenceFilter ...@@ -20,18 +20,13 @@ class MergeRequestReferenceFilter < ReferenceFilter
# #
# Returns a String replaced with the return of the block. # Returns a String replaced with the return of the block.
def self.references_in(text) def self.references_in(text)
text.gsub(MERGE_REQUEST_PATTERN) do |match| text.gsub(MergeRequest.reference_pattern) do |match|
yield match, $~[:merge_request].to_i, $~[:project] yield match, $~[:merge_request].to_i, $~[:project]
end end
end end
# Pattern used to extract `!123` merge request references from text
#
# This pattern supports cross-project references.
MERGE_REQUEST_PATTERN = /#{PROJECT_PATTERN}?!(?<merge_request>\d+)/
def call def call
replace_text_nodes_matching(MERGE_REQUEST_PATTERN) do |content| replace_text_nodes_matching(MergeRequest.reference_pattern) do |content|
merge_request_link_filter(content) merge_request_link_filter(content)
end end
end end
......
...@@ -20,18 +20,13 @@ class SnippetReferenceFilter < ReferenceFilter ...@@ -20,18 +20,13 @@ class SnippetReferenceFilter < ReferenceFilter
# #
# Returns a String replaced with the return of the block. # Returns a String replaced with the return of the block.
def self.references_in(text) def self.references_in(text)
text.gsub(SNIPPET_PATTERN) do |match| text.gsub(Snippet.reference_pattern) do |match|
yield match, $~[:snippet].to_i, $~[:project] yield match, $~[:snippet].to_i, $~[:project]
end end
end end
# Pattern used to extract `$123` snippet references from text
#
# This pattern supports cross-project references.
SNIPPET_PATTERN = /#{PROJECT_PATTERN}?\$(?<snippet>\d+)/
def call def call
replace_text_nodes_matching(SNIPPET_PATTERN) do |content| replace_text_nodes_matching(Snippet.reference_pattern) do |content|
snippet_link_filter(content) snippet_link_filter(content)
end end
end end
......
...@@ -16,16 +16,13 @@ class UserReferenceFilter < ReferenceFilter ...@@ -16,16 +16,13 @@ class UserReferenceFilter < ReferenceFilter
# #
# Returns a String replaced with the return of the block. # Returns a String replaced with the return of the block.
def self.references_in(text) def self.references_in(text)
text.gsub(USER_PATTERN) do |match| text.gsub(User.reference_pattern) do |match|
yield match, $~[:user] yield match, $~[:user]
end end
end end
# Pattern used to extract `@user` user references from text
USER_PATTERN = /@(?<user>#{Gitlab::Regex::NAMESPACE_REGEX_STR})/
def call def call
replace_text_nodes_matching(USER_PATTERN) do |content| replace_text_nodes_matching(User.reference_pattern) do |content|
user_link_filter(content) user_link_filter(content)
end end
end end
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment