You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

syntax_highlighting.rb 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006-2022 Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. module Redmine
  19. module SyntaxHighlighting
  20. class << self
  21. attr_reader :highlighter
  22. def highlighter=(name)
  23. if name.is_a?(Module)
  24. @highlighter = name
  25. else
  26. @highlighter = const_get(name)
  27. end
  28. end
  29. def highlight_by_filename(text, filename)
  30. highlighter.highlight_by_filename(text, filename)
  31. rescue
  32. ERB::Util.h(text)
  33. end
  34. def highlight_by_language(text, language)
  35. highlighter.highlight_by_language(text, language)
  36. rescue
  37. ERB::Util.h(text)
  38. end
  39. def language_supported?(language)
  40. if highlighter.respond_to? :language_supported?
  41. highlighter.language_supported? language
  42. else
  43. true
  44. end
  45. rescue
  46. false
  47. end
  48. def filename_supported?(filename)
  49. if highlighter.respond_to? :filename_supported?
  50. highlighter.filename_supported? filename
  51. else
  52. false
  53. end
  54. end
  55. end
  56. module Rouge
  57. require 'rouge'
  58. # Customized formatter based on Rouge::Formatters::HTMLLinewise
  59. # Syntax highlighting is completed within each line.
  60. class CustomHTMLLinewise < ::Rouge::Formatter
  61. def initialize(formatter)
  62. @formatter = formatter
  63. end
  64. def stream(tokens, &b)
  65. token_lines(tokens) do |line|
  66. line.each do |tok, val|
  67. yield @formatter.span(tok, val)
  68. end
  69. yield "\n"
  70. end
  71. end
  72. end
  73. class << self
  74. # Highlights +text+ as the content of +filename+
  75. # Should not return line numbers nor outer pre tag
  76. def highlight_by_filename(text, filename)
  77. # TODO: Delete the following workaround for #30434 and
  78. # test_syntax_highlight_should_normalize_line_endings in
  79. # application_helper_test.rb when Rouge is improved to
  80. # handle CRLF properly.
  81. # See also: https://github.com/jneen/rouge/pull/1078
  82. text = text.gsub(/\r\n?/, "\n")
  83. lexer =::Rouge::Lexer.guess(:source => text, :filename => filename)
  84. formatter = ::Rouge::Formatters::HTML.new
  85. ::Rouge.highlight(text, lexer, CustomHTMLLinewise.new(formatter))
  86. end
  87. # Highlights +text+ using +language+ syntax
  88. # Should not return outer pre tag
  89. def highlight_by_language(text, language)
  90. lexer =
  91. find_lexer(language.to_s.downcase) || ::Rouge::Lexers::PlainText
  92. ::Rouge.highlight(text, lexer, ::Rouge::Formatters::HTML)
  93. end
  94. def language_supported?(language)
  95. find_lexer(language.to_s.downcase) ? true : false
  96. end
  97. def filename_supported?(filename)
  98. !::Rouge::Lexer.guesses(:filename => filename).empty?
  99. end
  100. private
  101. # Alias names used by CodeRay and not supported by Rouge
  102. LANG_ALIASES = {
  103. 'delphi' => 'pascal',
  104. 'cplusplus' => 'cpp',
  105. 'ecmascript' => 'javascript',
  106. 'ecma_script' => 'javascript',
  107. 'java_script' => 'javascript',
  108. 'xhtml' => 'html'
  109. }
  110. def find_lexer(language)
  111. ::Rouge::Lexer.find(language) ||
  112. ::Rouge::Lexer.find(LANG_ALIASES[language])
  113. end
  114. end
  115. end
  116. end
  117. SyntaxHighlighting.highlighter = 'Rouge'
  118. end