Commit 0456dd72e26aaba6455e851260426d0156ba159a

Authored by Dmitriy Zaporozhets
2 parents 6ebd360c b039a169

Merge pull request #1232 from tsigo/refactor_gfm

GFM Refactoring
app/helpers/gitlab_markdown_helper.rb
@@ -12,53 +12,10 @@ module GitlabMarkdownHelper @@ -12,53 +12,10 @@ module GitlabMarkdownHelper
12 "{gfm-extraction-#{md5}}" 12 "{gfm-extraction-#{md5}}"
13 end 13 end
14 14
15 - # match 1 2 3 4 5 6  
16 - text.gsub!(/(\W)?(@([\w\._]+)|[#!$](\d+)|([\h]{6,40}))(\W)?/) do |match|  
17 - prefix = $1  
18 - reference = $2  
19 - user_name = $3  
20 - issue_id = $4  
21 - merge_request_id = $4  
22 - snippet_id = $4  
23 - commit_id = $5  
24 - suffix = $6 15 + # TODO: add popups with additional information
25 16
26 - # TODO: add popups with additional information  
27 - ref_link = case reference  
28 -  
29 - # team member: @foo  
30 - when /^@/  
31 - user = @project.users.where(name: user_name).first  
32 - member = @project.users_projects.where(user_id: user).first if user  
33 - link_to("@#{user_name}", project_team_member_path(@project, member), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member  
34 -  
35 - # issue: #123  
36 - when /^#/  
37 - # avoid HTML entities  
38 - unless prefix.try(:end_with?, "&") && suffix.try(:start_with?, ";")  
39 - issue = @project.issues.where(id: issue_id).first  
40 - link_to("##{issue_id}", project_issue_path(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}")) if issue  
41 - end  
42 -  
43 - # merge request: !123  
44 - when /^!/  
45 - merge_request = @project.merge_requests.where(id: merge_request_id).first  
46 - link_to("!#{merge_request_id}", project_merge_request_path(@project, merge_request), html_options.merge(title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}")) if merge_request  
47 -  
48 - # snippet: $123  
49 - when /^\$/  
50 - snippet = @project.snippets.where(id: snippet_id).first  
51 - link_to("$#{snippet_id}", project_snippet_path(@project, snippet), html_options.merge(title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}")) if snippet  
52 -  
53 - # commit: 123456...  
54 - when /^\h/  
55 - commit = @project.commit(commit_id)  
56 - link_to(commit_id, project_commit_path(@project, id: commit.id), html_options.merge(title: "Commit: #{commit.author_name} - #{CommitDecorator.new(commit).title}", class: "gfm gfm-commit #{html_options[:class]}")) if commit  
57 -  
58 - end # case  
59 -  
60 - ref_link.nil? ? match : "#{prefix}#{ref_link}#{suffix}"  
61 - end # gsub 17 + parser = Gitlab::Markdown.new(@project, html_options)
  18 + text = parser.parse(text)
62 19
63 # Insert pre block extractions 20 # Insert pre block extractions
64 text.gsub!(/\{gfm-extraction-(\h{32})\}/) do 21 text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
lib/gitlab/markdown.rb 0 → 100644
@@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
  1 +module Gitlab
  2 + # Custom parsing for Gitlab-flavored Markdown
  3 + #
  4 + # Examples
  5 + #
  6 + # >> m = Markdown.new(...)
  7 + #
  8 + # >> m.parse("Hey @david, can you fix this?")
  9 + # => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?"
  10 + #
  11 + # >> m.parse("Commit 35d5f7c closes #1234")
  12 + # => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
  13 + class Markdown
  14 + include Rails.application.routes.url_helpers
  15 + include ActionView::Helpers
  16 +
  17 + REFERENCE_PATTERN = %r{
  18 + ([^\w&;])? # Prefix (1)
  19 + ( # Reference (2)
  20 + @([\w\._]+) # User name (3)
  21 + |[#!$](\d+) # Issue/MR/Snippet ID (4)
  22 + |([\h]{6,40}) # Commit ID (5)
  23 + )
  24 + ([^\w&;])? # Suffix (6)
  25 + }x.freeze
  26 +
  27 + attr_reader :html_options
  28 +
  29 + def initialize(project, html_options = {})
  30 + @project = project
  31 + @html_options = html_options
  32 + end
  33 +
  34 + def parse(text)
  35 + text.gsub(REFERENCE_PATTERN) do |match|
  36 + prefix = $1 || ''
  37 + reference = $2
  38 + identifier = $3 || $4 || $5
  39 + suffix = $6 || ''
  40 +
  41 + if ref_link = reference_link(reference, identifier)
  42 + prefix + ref_link + suffix
  43 + else
  44 + match
  45 + end
  46 + end
  47 + end
  48 +
  49 + private
  50 +
  51 + # Private: Dispatches to a dedicated processing method based on reference
  52 + #
  53 + # reference - Object reference ("@1234", "!567", etc.)
  54 + # identifier - Object identifier (Issue ID, SHA hash, etc.)
  55 + #
  56 + # Returns string rendered by the processing method
  57 + def reference_link(reference, identifier)
  58 + case reference
  59 + when /^@/ then reference_user(identifier)
  60 + when /^#/ then reference_issue(identifier)
  61 + when /^!/ then reference_merge_request(identifier)
  62 + when /^\$/ then reference_snippet(identifier)
  63 + when /^\h/ then reference_commit(identifier)
  64 + end
  65 + end
  66 +
  67 + def reference_user(identifier)
  68 + if user = @project.users.where(name: identifier).first
  69 + member = @project.users_projects.where(user_id: user).first
  70 + link_to("@#{user.name}", project_team_member_path(@project, member), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member
  71 + end
  72 + end
  73 +
  74 + def reference_issue(identifier)
  75 + if issue = @project.issues.where(id: identifier).first
  76 + link_to("##{issue.id}", project_issue_path(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}"))
  77 + end
  78 + end
  79 +
  80 + def reference_merge_request(identifier)
  81 + if merge_request = @project.merge_requests.where(id: identifier).first
  82 + link_to("!#{merge_request.id}", project_merge_request_path(@project, merge_request), html_options.merge(title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}"))
  83 + end
  84 + end
  85 +
  86 + def reference_snippet(identifier)
  87 + if snippet = @project.snippets.where(id: identifier).first
  88 + link_to("$#{snippet.id}", project_snippet_path(@project, snippet), html_options.merge(title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}"))
  89 + end
  90 + end
  91 +
  92 + def reference_commit(identifier)
  93 + if commit = @project.commit(identifier)
  94 + link_to(identifier, project_commit_path(@project, id: commit.id), html_options.merge(title: "Commit: #{commit.author_name} - #{CommitDecorator.new(commit).title}", class: "gfm gfm-commit #{html_options[:class]}"))
  95 + end
  96 + end
  97 + end
  98 +end