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 12 "{gfm-extraction-#{md5}}"
13 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 20 # Insert pre block extractions
64 21 text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
... ...
lib/gitlab/markdown.rb 0 → 100644
... ... @@ -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
... ...