Commit 67a6a0b29b1ca9563244f914bd0eaf0165389efd

Authored by Robert Speicher
1 parent 90f587f4

Change Gitlab::Markdown to a module; add emoji parsing

Showing 1 changed file with 69 additions and 13 deletions   Show diff stats
lib/gitlab/markdown.rb
1 1 module Gitlab
2 2 # Custom parser for Gitlab-flavored Markdown
3 3 #
4   - # It replaces references in the text with links to the appropriate items in Gitlab.
  4 + # It replaces references in the text with links to the appropriate items in
  5 + # Gitlab.
5 6 #
6 7 # Supported reference formats are:
7 8 # * @foo for team members
... ... @@ -10,19 +11,20 @@ module Gitlab
10 11 # * $123 for snippets
11 12 # * 123456 for commits
12 13 #
13   - # Examples
  14 + # It also parses Emoji codes to insert images. See
  15 + # http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
14 16 #
15   - # >> m = Markdown.new(...)
  17 + # Examples
16 18 #
17   - # >> m.parse("Hey @david, can you fix this?")
  19 + # >> gfm("Hey @david, can you fix this?")
18 20 # => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?"
19 21 #
20   - # >> m.parse("Commit 35d5f7c closes #1234")
  22 + # >> gfm("Commit 35d5f7c closes #1234")
21 23 # => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
22   - class Markdown
23   - include Rails.application.routes.url_helpers
24   - include ActionView::Helpers
25   -
  24 + #
  25 + # >> gfm(":trollface:")
  26 + # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
  27 + module Markdown
26 28 REFERENCE_PATTERN = %r{
27 29 ([^\w&;])? # Prefix (1)
28 30 ( # Reference (2)
... ... @@ -33,15 +35,52 @@ module Gitlab
33 35 ([^\w&;])? # Suffix (6)
34 36 }x.freeze
35 37  
  38 + EMOJI_PATTERN = %r{(:([\w\-\+]+):)}.freeze
  39 +
36 40 attr_reader :html_options
37 41  
38   - def initialize(project, html_options = {})
39   - @project = project
  42 + # Public: Parse the provided text with GitLab-Flavored Markdown
  43 + #
  44 + # text - the source text
  45 + # html_options - extra options for the reference links as given to link_to
  46 + #
  47 + # Note: reference links will only be generated if @project is set
  48 + def gfm(text, html_options = {})
  49 + return text if text.nil?
  50 + return text if @project.nil?
  51 +
40 52 @html_options = html_options
  53 +
  54 + # Extract pre blocks so they are not altered
  55 + # from http://github.github.com/github-flavored-markdown/
  56 + extractions = {}
  57 + text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
  58 + md5 = Digest::MD5.hexdigest(match)
  59 + extractions[md5] = match
  60 + "{gfm-extraction-#{md5}}"
  61 + end
  62 +
  63 + # TODO: add popups with additional information
  64 +
  65 + text = parse(text)
  66 +
  67 + # Insert pre block extractions
  68 + text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
  69 + extractions[$1]
  70 + end
  71 +
  72 + sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class)
41 73 end
42 74  
  75 + private
  76 +
  77 + # Private: Parses text for references and emoji
  78 + #
  79 + # text - Text to parse
  80 + #
  81 + # Returns parsed text
43 82 def parse(text)
44   - text.gsub(REFERENCE_PATTERN) do |match|
  83 + text = text.gsub(REFERENCE_PATTERN) do |match|
45 84 prefix = $1 || ''
46 85 reference = $2
47 86 identifier = $3 || $4 || $5
... ... @@ -53,9 +92,26 @@ module Gitlab
53 92 match
54 93 end
55 94 end
  95 +
  96 + text = text.gsub(EMOJI_PATTERN) do |match|
  97 + if valid_emoji?($2)
  98 + helper.image_tag("#{$2}.png", class: 'emoji', title: $1, alt: $1)
  99 + else
  100 + match
  101 + end
  102 + end
  103 +
  104 + text
56 105 end
57 106  
58   - private
  107 + # Private: Checks if an emoji icon exists in the image asset directory
  108 + #
  109 + # emoji - Identifier of the emoji as a string (e.g., "+1", "heart")
  110 + #
  111 + # Returns boolean
  112 + def valid_emoji?(emoji)
  113 + File.exists?(Rails.root.join('app', 'assets', 'images', 'emoji', "#{emoji}.png"))
  114 + end
59 115  
60 116 # Private: Dispatches to a dedicated processing method based on reference
61 117 #
... ...