diff options
author | Douwe Maan <douwe@gitlab.com> | 2018-10-30 20:44:03 +0000 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2018-10-30 20:44:03 +0000 |
commit | 571e651b21c7a618b8686a4b3f8a8c09c87a37f5 (patch) | |
tree | 168c177b68440043ffa08ce282b894cab5728148 | |
parent | 7bcd0dc19bfd31e79d52ae148d3edf15b054cb5a (diff) | |
parent | 0fa5260f1d1e99bcd0429cba09140c039a3d9d5a (diff) | |
download | gitlab-ce-571e651b21c7a618b8686a4b3f8a8c09c87a37f5.tar.gz |
Merge branch 'add-language-param-to-highlight' into 'master'
Add language param to highlight
See merge request gitlab-org/gitlab-ce!21584
28 files changed, 289 insertions, 125 deletions
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 8040a14ef56..8f683ca06ad 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -61,7 +61,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController format.html do usage_data_json = JSON.pretty_generate(Gitlab::UsageData.data) - render html: Gitlab::Highlight.highlight('payload.json', usage_data_json) + render html: Gitlab::Highlight.highlight('payload.json', usage_data_json, language: 'json') end format.json { render json: Gitlab::UsageData.to_json } end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 56a884b8a2a..c02ec407262 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -92,7 +92,7 @@ class Projects::BlobController < Projects::ApplicationController apply_diff_view_cookie! @blob.load_all_data! - @lines = Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: @repository).lines + @lines = @blob.present.highlight.lines @form = UnfoldForm.new(params) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index ff7f1e3a9aa..638744a1426 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -1,9 +1,8 @@ # frozen_string_literal: true module BlobHelper - def highlight(blob_name, blob_content, repository: nil, plain: false) - plain ||= blob_content.length > Blob::MAXIMUM_TEXT_HIGHLIGHT_SIZE - highlighted = Gitlab::Highlight.highlight(blob_name, blob_content, plain: plain, repository: repository) + def highlight(file_name, file_content, language: nil, plain: false) + highlighted = Gitlab::Highlight.highlight(file_name, file_content, plain: plain, language: language) raw %(<pre class="code highlight"><code>#{highlighted}</code></pre>) end diff --git a/app/models/blob.rb b/app/models/blob.rb index 31a839274b5..4f310e70f4f 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -1,12 +1,13 @@ # frozen_string_literal: true -# Blob is a Rails-specific wrapper around Gitlab::Git::Blob objects +# Blob is a Rails-specific wrapper around Gitlab::Git::Blob, SnippetBlob and Ci::ArtifactBlob class Blob < SimpleDelegator + include Presentable + include BlobLanguageFromGitAttributes + CACHE_TIME = 60 # Cache raw blobs referred to by a (mutable) ref for 1 minute CACHE_TIME_IMMUTABLE = 3600 # Cache blobs referred to by an immutable reference for 1 hour - MAXIMUM_TEXT_HIGHLIGHT_SIZE = 1.megabyte - # Finding a viewer for a blob happens based only on extension and whether the # blob is binary or text, which means 1 blob should only be matched by 1 viewer, # and the order of these viewers doesn't really matter. @@ -121,10 +122,6 @@ class Blob < SimpleDelegator end end - def no_highlighting? - raw_size && raw_size > MAXIMUM_TEXT_HIGHLIGHT_SIZE - end - def empty? raw_size == 0 end diff --git a/app/models/concerns/blob_language_from_git_attributes.rb b/app/models/concerns/blob_language_from_git_attributes.rb new file mode 100644 index 00000000000..70213d22147 --- /dev/null +++ b/app/models/concerns/blob_language_from_git_attributes.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Applicable for blob classes with project attribute +module BlobLanguageFromGitAttributes + extend ActiveSupport::Concern + + def language_from_gitattributes + return nil unless project + + repository = project.repository + repository.gitattribute(path, 'gitlab-language') + end +end diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb new file mode 100644 index 00000000000..6323c1b3389 --- /dev/null +++ b/app/presenters/blob_presenter.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class BlobPresenter < Gitlab::View::Presenter::Simple + presents :blob + + def highlight(plain: nil) + blob.load_all_data! if blob.respond_to?(:load_all_data!) + + Gitlab::Highlight.highlight( + blob.path, + blob.data, + language: blob.language_from_gitattributes, + plain: plain + ) + end +end diff --git a/app/views/projects/blob/viewers/_highlight_embed.html.haml b/app/views/projects/blob/viewers/_highlight_embed.html.haml index 9bd4ef6ad0b..6a631fef1a9 100644 --- a/app/views/projects/blob/viewers/_highlight_embed.html.haml +++ b/app/views/projects/blob/viewers/_highlight_embed.html.haml @@ -4,4 +4,6 @@ - blob.data.each_line.each_with_index do |_, index| %span.diff-line-num= index + 1 .blob-content{ data: { blob_id: blob.id } } - = highlight(blob.path, blob.data, repository: nil, plain: blob.no_highlighting?) + %pre.code.highlight + %code + = blob.present.highlight diff --git a/app/views/projects/blob/viewers/_text.html.haml b/app/views/projects/blob/viewers/_text.html.haml index a91df321ca0..26ad23da436 100644 --- a/app/views/projects/blob/viewers/_text.html.haml +++ b/app/views/projects/blob/viewers/_text.html.haml @@ -1 +1 @@ -= render 'shared/file_highlight', blob: viewer.blob, repository: @repository += render 'shared/file_highlight', blob: viewer.blob diff --git a/app/views/projects/settings/ci_cd/_badge.html.haml b/app/views/projects/settings/ci_cd/_badge.html.haml index d14360913a4..82c8ec088e5 100644 --- a/app/views/projects/settings/ci_cd/_badge.html.haml +++ b/app/views/projects/settings/ci_cd/_badge.html.haml @@ -15,14 +15,14 @@ .col-md-2.text-center Markdown .col-md-10.code.js-syntax-highlight - = highlight('.md', badge.to_markdown) + = highlight('.md', badge.to_markdown, language: 'markdown') .row %hr .row .col-md-2.text-center HTML .col-md-10.code.js-syntax-highlight - = highlight('.html', badge.to_html) + = highlight('.html', badge.to_html, language: 'html') .row %hr .row diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml index 8b95bdf9747..b4ecd7bbae9 100644 --- a/app/views/search/results/_snippet_blob.html.haml +++ b/app/views/search/results/_snippet_blob.html.haml @@ -39,7 +39,7 @@ .blob-content - snippet_chunks.each do |chunk| - unless chunk[:data].empty? - = highlight(snippet.file_name, chunk[:data], repository: nil, plain: snippet.blob.no_highlighting?) + = highlight(snippet.file_name, chunk[:data]) - else .file-content.code .nothing-here-block Empty file diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index 8d64cb5d698..5073e6ad48f 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -1,5 +1,3 @@ -- repository = nil unless local_assigns.key?(:repository) - .file-content.code.js-syntax-highlight .line-numbers - if blob.data.present? @@ -13,4 +11,6 @@ = link_icon = i .blob-content{ data: { blob_id: blob.id } } - = highlight(blob.path, blob.data, repository: repository, plain: blob.no_highlighting?) + %pre.code.highlight + %code + = blob.present.highlight diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml index ddc089b0bd7..52c7bc47ca7 100644 --- a/app/views/sherlock/queries/_general.html.haml +++ b/app/views/sherlock/queries/_general.html.haml @@ -36,7 +36,7 @@ %li .code.js-syntax-highlight.sherlock-code :preserve - #{highlight("#{@query.id}.sql", @query.formatted_query)} + #{highlight("#{@query.id}.sql", @query.formatted_query, language: 'sql')} .card .card-header diff --git a/app/views/sherlock/transactions/_queries.html.haml b/app/views/sherlock/transactions/_queries.html.haml index c1ec4b91bb6..5e224f3aa0e 100644 --- a/app/views/sherlock/transactions/_queries.html.haml +++ b/app/views/sherlock/transactions/_queries.html.haml @@ -17,7 +17,7 @@ = t('sherlock.milliseconds') %td .code.js-syntax-highlight.sherlock-code - = highlight("#{query.id}.sql", query.formatted_query) + = highlight("#{query.id}.sql", query.formatted_query, language: 'sql') %td = link_to(t('sherlock.view'), sherlock_transaction_query_path(@transaction, query), diff --git a/lib/gitlab/blame.rb b/lib/gitlab/blame.rb index 0d79594363e..f1a653a9d95 100644 --- a/lib/gitlab/blame.rb +++ b/lib/gitlab/blame.rb @@ -43,8 +43,7 @@ module Gitlab def highlighted_lines @blob.load_all_data! - @highlighted_lines ||= - Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: repository).lines + @highlighted_lines ||= @blob.present.highlight.lines end def project diff --git a/lib/gitlab/conflict/file.rb b/lib/gitlab/conflict/file.rb index 30911b49b18..501c2111530 100644 --- a/lib/gitlab/conflict/file.rb +++ b/lib/gitlab/conflict/file.rb @@ -3,6 +3,7 @@ module Gitlab class File include Gitlab::Routing include IconsHelper + include Gitlab::Utils::StrongMemoize CONTEXT_LINES = 3 @@ -30,11 +31,8 @@ module Gitlab end def highlight_lines! - their_file = lines.reject { |line| line.type == 'new' }.map(&:text).join("\n") - our_file = lines.reject { |line| line.type == 'old' }.map(&:text).join("\n") - - their_highlight = Gitlab::Highlight.highlight(their_path, their_file, repository: repository).lines - our_highlight = Gitlab::Highlight.highlight(our_path, our_file, repository: repository).lines + their_highlight = Gitlab::Highlight.highlight(their_path, their_lines, language: their_language).lines + our_highlight = Gitlab::Highlight.highlight(our_path, our_lines, language: our_language).lines lines.each do |line| line.rich_text = @@ -182,6 +180,34 @@ module Gitlab raw_line[:line_new], parent_file: self) end end + + def their_language + strong_memoize(:their_language) do + repository.gitattribute(their_path, 'gitlab-language') + end + end + + def our_language + strong_memoize(:our_language) do + if our_path == their_path + their_language + else + repository.gitattribute(our_path, 'gitlab-language') + end + end + end + + def their_lines + strong_memoize(:their_lines) do + lines.reject { |line| line.type == 'new' }.map(&:text).join("\n") + end + end + + def our_lines + strong_memoize(:our_lines) do + lines.reject { |line| line.type == 'old' }.map(&:text).join("\n") + end + end end end end diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb index a605ddb5c33..1d833183ec3 100644 --- a/lib/gitlab/diff/highlight.rb +++ b/lib/gitlab/diff/highlight.rb @@ -79,7 +79,7 @@ module Gitlab return [] unless blob blob.load_all_data! - Gitlab::Highlight.highlight(blob.path, blob.data, repository: repository).lines + blob.present.highlight.lines end end end diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb index 83095acc528..a4e60bbd828 100644 --- a/lib/gitlab/highlight.rb +++ b/lib/gitlab/highlight.rb @@ -4,22 +4,25 @@ module Gitlab class Highlight TIMEOUT_BACKGROUND = 30.seconds TIMEOUT_FOREGROUND = 3.seconds + MAXIMUM_TEXT_HIGHLIGHT_SIZE = 1.megabyte - def self.highlight(blob_name, blob_content, repository: nil, plain: false) - new(blob_name, blob_content, repository: repository) + def self.highlight(blob_name, blob_content, language: nil, plain: false) + new(blob_name, blob_content, language: language) .highlight(blob_content, continue: false, plain: plain) end attr_reader :blob_name - def initialize(blob_name, blob_content, repository: nil) + def initialize(blob_name, blob_content, language: nil) @formatter = Rouge::Formatters::HTMLGitlab - @repository = repository + @language = language @blob_name = blob_name @blob_content = blob_content end def highlight(text, continue: true, plain: false) + plain ||= text.length > MAXIMUM_TEXT_HIGHLIGHT_SIZE + highlighted_text = highlight_text(text, continue: continue, plain: plain) highlighted_text = link_dependencies(text, highlighted_text) if blob_name highlighted_text @@ -36,11 +39,9 @@ module Gitlab private def custom_language - language_name = @repository && @repository.gitattribute(@blob_name, 'gitlab-language') - - return nil unless language_name + return nil unless @language - Rouge::Lexer.find_fancy(language_name) + Rouge::Lexer.find_fancy(@language) end def highlight_text(text, continue: true, plain: false) diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 3a202d915e3..04df881bf03 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -82,7 +82,7 @@ module Gitlab ref: ref, startline: startline, data: data.join, - project_id: project ? project.id : nil + project: project ) end diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 5ce3eda2ccb..458737f31eb 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -4,8 +4,10 @@ module Gitlab class SearchResults class FoundBlob include EncodingHelper + include Presentable + include BlobLanguageFromGitAttributes - attr_reader :id, :filename, :basename, :ref, :startline, :data, :project_id + attr_reader :id, :filename, :basename, :ref, :startline, :data, :project def initialize(opts = {}) @id = opts.fetch(:id, nil) @@ -15,6 +17,11 @@ module Gitlab @startline = opts.fetch(:startline, nil) @data = encode_utf8(opts.fetch(:data, nil)) @per_page = opts.fetch(:per_page, 20) + @project = opts.fetch(:project, nil) + # Some caller does not have project object (e.g. elastic search), + # yet they can trigger many calls in one go, + # causing duplicated queries. + # Allow those to just pass project_id instead. @project_id = opts.fetch(:project_id, nil) end @@ -22,8 +29,12 @@ module Gitlab filename end - def no_highlighting? - false + def project_id + @project_id || @project&.id + end + + def present + super(presenter_class: BlobPresenter) end end diff --git a/spec/features/search/user_searches_for_wiki_pages_spec.rb b/spec/features/search/user_searches_for_wiki_pages_spec.rb index 3ee753b7d23..7225ca65492 100644 --- a/spec/features/search/user_searches_for_wiki_pages_spec.rb +++ b/spec/features/search/user_searches_for_wiki_pages_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe 'User searches for wiki pages', :js do let(:user) { create(:user) } - let(:project) { create(:project, :wiki_repo, namespace: user.namespace) } + let(:project) { create(:project, :repository, :wiki_repo, namespace: user.namespace) } let!(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: { title: 'test_wiki', content: 'Some Wiki content' }) } before do diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 1c216b3fe97..f709f152c92 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -3,63 +3,13 @@ require 'spec_helper' describe BlobHelper do include TreeHelper - let(:blob_name) { 'test.lisp' } - let(:no_context_content) { ":type \"assem\"))" } - let(:blob_content) { "(make-pathname :defaults name\n#{no_context_content}" } - let(:split_content) { blob_content.split("\n") } - let(:multiline_content) do - %q( - def test(input): - """This is line 1 of a multi-line comment. - This is line 2. - """ - ) - end - describe '#highlight' do - it 'returns plaintext for unknown lexer context' do - result = helper.highlight(blob_name, no_context_content) - expect(result).to eq(%[<pre class="code highlight"><code><span id="LC1" class="line" lang="">:type "assem"))</span></code></pre>]) + it 'wraps highlighted content' do + expect(helper.highlight('test.rb', '52')).to eq(%q[<pre class="code highlight"><code><span id="LC1" class="line" lang="ruby"><span class="mi">52</span></span></code></pre>]) end - it 'returns plaintext for long blobs' do - stub_const('Blob::MAXIMUM_TEXT_HIGHLIGHT_SIZE', 1) - result = helper.highlight(blob_name, blob_content) - - expect(result).to eq(%[<pre class="code highlight"><code><span id="LC1" class="line" lang="">(make-pathname :defaults name</span>\n<span id="LC2" class="line" lang="">:type "assem"))</span></code></pre>]) - end - - it 'highlights single block' do - expected = %Q[<pre class="code highlight"><code><span id="LC1" class="line" lang="common_lisp"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span> -<span id="LC2" class="line" lang="common_lisp"><span class="ss">:type</span> <span class="s">"assem"</span><span class="p">))</span></span></code></pre>] - - expect(helper.highlight(blob_name, blob_content)).to eq(expected) - end - - it 'highlights multi-line comments' do - result = helper.highlight(blob_name, multiline_content) - html = Nokogiri::HTML(result) - lines = html.search('.s') - expect(lines.count).to eq(3) - expect(lines[0].text).to eq('"""This is line 1 of a multi-line comment.') - expect(lines[1].text).to eq(' This is line 2.') - expect(lines[2].text).to eq(' """') - end - - context 'diff highlighting' do - let(:blob_name) { 'test.diff' } - let(:blob_content) { "+aaa\n+bbb\n- ccc\n ddd\n"} - let(:expected) do - %q(<pre class="code highlight"><code><span id="LC1" class="line" lang="diff"><span class="gi">+aaa</span></span> -<span id="LC2" class="line" lang="diff"><span class="gi">+bbb</span></span> -<span id="LC3" class="line" lang="diff"><span class="gd">- ccc</span></span> -<span id="LC4" class="line" lang="diff"> ddd</span></code></pre>) - end - - it 'highlights each line properly' do - result = helper.highlight(blob_name, blob_content) - expect(result).to eq(expected) - end + it 'handles plain version' do + expect(helper.highlight('test.rb', '52', plain: true)).to eq(%q[<pre class="code highlight"><code><span id="LC1" class="line" lang="">52</span></code></pre>]) end end diff --git a/spec/lib/gitlab/git/attributes_parser_spec.rb b/spec/lib/gitlab/git/attributes_parser_spec.rb index 18ebfef38f0..f431d4e2a53 100644 --- a/spec/lib/gitlab/git/attributes_parser_spec.rb +++ b/spec/lib/gitlab/git/attributes_parser_spec.rb @@ -94,7 +94,7 @@ describe Gitlab::Git::AttributesParser, :seed_helper do # It's a bit hard to test for something _not_ being processed. As such we'll # just test the number of entries. it 'ignores any comments and empty lines' do - expect(subject.patterns.length).to eq(10) + expect(subject.patterns.length).to eq(12) end end @@ -126,7 +126,7 @@ describe Gitlab::Git::AttributesParser, :seed_helper do describe '#each_line' do it 'iterates over every line in the attributes file' do - args = [String] * 14 # the number of lines in the file + args = [String] * 16 # the number of lines in the file expect { |b| subject.each_line(&b) }.to yield_successive_args(*args) end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 51eb997a325..9a443fa7f20 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1194,6 +1194,34 @@ describe Gitlab::Git::Repository, :seed_helper do end end + describe '#gitattribute' do + let(:repository) { Gitlab::Git::Repository.new('default', TEST_GITATTRIBUTES_REPO_PATH, '') } + + after do + ensure_seeds + end + + it 'returns matching language attribute' do + expect(repository.gitattribute("custom-highlighting/test.gitlab-custom", 'gitlab-language')).to eq('ruby') + end + + it 'returns matching language attribute with additional options' do + expect(repository.gitattribute("custom-highlighting/test.gitlab-cgi", 'gitlab-language')).to eq('erb?parent=json') + end + + it 'returns nil if nothing matches' do + expect(repository.gitattribute("report.xslt", 'gitlab-language')).to eq(nil) + end + + context 'without gitattributes file' do + let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } + + it 'returns nil' do + expect(repository.gitattribute("README.md", 'gitlab-language')).to eq(nil) + end + end + end + describe '#ref_exists?' do it 'returns true for an existing tag' do expect(repository.ref_exists?('refs/heads/master')).to eq(true) diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb index 88f7099ff3c..fe0e9702f8a 100644 --- a/spec/lib/gitlab/highlight_spec.rb +++ b/spec/lib/gitlab/highlight_spec.rb @@ -5,44 +5,85 @@ describe Gitlab::Highlight do let(:project) { create(:project, :repository) } let(:repository) { project.repository } - let(:commit) { project.commit(sample_commit.id) } - - describe 'custom highlighting from .gitattributes' do - let(:branch) { 'gitattributes' } - let(:blob) { repository.blob_at_branch(branch, path) } + describe 'language provided' do let(:highlighter) do - described_class.new(blob.path, blob.data, repository: repository) + described_class.new('foo.erb', 'bar', language: 'erb?parent=json') end - before do - project.change_head('gitattributes') + it 'sets correct lexer' do + expect(highlighter.lexer.tag).to eq 'erb' + expect(highlighter.lexer.parent.tag).to eq 'json' end + end - describe 'basic language selection' do - let(:path) { 'custom-highlighting/test.gitlab-custom' } - it 'highlights as ruby' do - expect(highlighter.lexer.tag).to eq 'ruby' - end + describe '#highlight' do + let(:file_name) { 'test.lisp' } + let(:no_context_content) { ":type \"assem\"))" } + let(:content) { "(make-pathname :defaults name\n#{no_context_content}" } + let(:multiline_content) do + %q( + def test(input): + """This is line 1 of a multi-line comment. + This is line 2. + """ + ) + end + + it 'highlights' do + expected = %Q[<span id="LC1" class="line" lang="common_lisp"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span> +<span id="LC2" class="line" lang="common_lisp"><span class="ss">:type</span> <span class="s">"assem"</span><span class="p">))</span></span>] + + expect(described_class.highlight(file_name, content)).to eq(expected) + end + + it 'returns plain version for unknown lexer context' do + result = described_class.highlight(file_name, no_context_content) + + expect(result).to eq(%[<span id="LC1" class="line" lang="">:type "assem"))</span>]) end - describe 'cgi options' do - let(:path) { 'custom-highlighting/test.gitlab-cgi' } + it 'returns plain version for long content' do + stub_const('Gitlab::Highlight::MAXIMUM_TEXT_HIGHLIGHT_SIZE', 1) + result = described_class.highlight(file_name, content) - it 'highlights as json with erb' do - expect(highlighter.lexer.tag).to eq 'erb' - expect(highlighter.lexer.parent.tag).to eq 'json' + expect(result).to eq(%[<span id="LC1" class="line" lang="">(make-pathname :defaults name</span>\n<span id="LC2" class="line" lang="">:type "assem"))</span>]) + end + + it 'highlights multi-line comments' do + result = described_class.highlight(file_name, multiline_content) + html = Nokogiri::HTML(result) + lines = html.search('.s') + + expect(lines.count).to eq(3) + expect(lines[0].text).to eq('"""This is line 1 of a multi-line comment.') + expect(lines[1].text).to eq(' This is line 2.') + expect(lines[2].text).to eq(' """') + end + + context 'diff highlighting' do + let(:file_name) { 'test.diff' } + let(:content) { "+aaa\n+bbb\n- ccc\n ddd\n"} + let(:expected) do + %q(<span id="LC1" class="line" lang="diff"><span class="gi">+aaa</span></span> +<span id="LC2" class="line" lang="diff"><span class="gi">+bbb</span></span> +<span id="LC3" class="line" lang="diff"><span class="gd">- ccc</span></span> +<span id="LC4" class="line" lang="diff"> ddd</span>) + end + + it 'highlights each line properly' do + result = described_class.highlight(file_name, content) + + expect(result).to eq(expected) end end - end - describe '#highlight' do describe 'with CRLF' do let(:branch) { 'crlf-diff' } let(:path) { 'files/whitespace' } let(:blob) { repository.blob_at_branch(branch, path) } let(:lines) do - described_class.highlight(blob.path, blob.data, repository: repository).lines + described_class.highlight(blob.path, blob.data).lines end it 'strips extra LFs' do diff --git a/spec/models/concerns/blob_language_from_git_attributes_spec.rb b/spec/models/concerns/blob_language_from_git_attributes_spec.rb new file mode 100644 index 00000000000..7f05073b08e --- /dev/null +++ b/spec/models/concerns/blob_language_from_git_attributes_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe BlobLanguageFromGitAttributes do + include FakeBlobHelpers + + let(:project) { build(:project, :repository) } + + describe '#language_from_gitattributes' do + subject(:blob) { fake_blob(path: 'file.md') } + + it 'returns return value from gitattribute' do + expect(blob.project.repository).to receive(:gitattribute).with(blob.path, 'gitlab-language').and_return('erb?parent=json') + + expect(blob.language_from_gitattributes).to eq('erb?parent=json') + end + + it 'returns nil if project is absent' do + allow(blob).to receive(:project).and_return(nil) + + expect(blob.language_from_gitattributes).to eq(nil) + end + end +end diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb new file mode 100644 index 00000000000..e85e7a41017 --- /dev/null +++ b/spec/presenters/blob_presenter_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe BlobPresenter, :seed_helper do + let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } + + let(:git_blob) do + Gitlab::Git::Blob.find( + repository, + 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6', + 'files/ruby/regex.rb' + ) + end + let(:blob) { Blob.new(git_blob) } + + describe '#highlight' do + subject { described_class.new(blob) } + + it 'returns highlighted content' do + expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: nil) + + subject.highlight + end + + it 'returns plain content when :plain is true' do + expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: true, language: nil) + + subject.highlight(plain: true) + end + + context 'gitlab-language contains a match' do + before do + allow(blob).to receive(:language_from_gitattributes).and_return('ruby') + end + + it 'passes language to inner call' do + expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: 'ruby') + + subject.highlight + end + end + end +end diff --git a/spec/support/helpers/seed_helper.rb b/spec/support/helpers/seed_helper.rb index 25781f5e679..90d7f60fdeb 100644 --- a/spec/support/helpers/seed_helper.rb +++ b/spec/support/helpers/seed_helper.rb @@ -1,15 +1,18 @@ +# frozen_string_literal: true + require_relative 'test_env' # This file is specific to specs in spec/lib/gitlab/git/ SEED_STORAGE_PATH = TestEnv.repos_path -TEST_REPO_PATH = 'gitlab-git-test.git'.freeze -TEST_NORMAL_REPO_PATH = 'not-bare-repo.git'.freeze -TEST_MUTABLE_REPO_PATH = 'mutable-repo.git'.freeze -TEST_BROKEN_REPO_PATH = 'broken-repo.git'.freeze +TEST_REPO_PATH = 'gitlab-git-test.git' +TEST_NORMAL_REPO_PATH = 'not-bare-repo.git' +TEST_MUTABLE_REPO_PATH = 'mutable-repo.git' +TEST_BROKEN_REPO_PATH = 'broken-repo.git' +TEST_GITATTRIBUTES_REPO_PATH = 'with-git-attributes.git' module SeedHelper - GITLAB_GIT_TEST_REPO_URL = File.expand_path('../gitlab-git-test.git', __dir__).freeze + GITLAB_GIT_TEST_REPO_URL = File.expand_path('../gitlab-git-test.git', __dir__) def ensure_seeds if File.exist?(SEED_STORAGE_PATH) @@ -66,6 +69,11 @@ module SeedHelper end def create_git_attributes + system(git_env, *%W(#{Gitlab.config.git.bin_path} clone --bare #{TEST_REPO_PATH} #{TEST_GITATTRIBUTES_REPO_PATH}), + chdir: SEED_STORAGE_PATH, + out: '/dev/null', + err: '/dev/null') + dir = File.join(SEED_STORAGE_PATH, 'with-git-attributes.git', 'info') FileUtils.mkdir_p(dir) @@ -82,6 +90,8 @@ foo/bar.* foo *.cgi key=value?p1=v1&p2=v2 /*.png gitlab-language=png *.binary binary +/custom-highlighting/*.gitlab-custom gitlab-language=ruby +/custom-highlighting/*.gitlab-cgi gitlab-language=erb?parent=json # This uses a tab instead of spaces to ensure the parser also supports this. *.md\tgitlab-language=markdown diff --git a/spec/views/projects/blob/_viewer.html.haml_spec.rb b/spec/views/projects/blob/_viewer.html.haml_spec.rb index aedbaa66d34..95f7f87d37b 100644 --- a/spec/views/projects/blob/_viewer.html.haml_spec.rb +++ b/spec/views/projects/blob/_viewer.html.haml_spec.rb @@ -29,6 +29,8 @@ describe 'projects/blob/_viewer.html.haml' do controller.params[:namespace_id] = project.namespace.to_param controller.params[:project_id] = project.to_param controller.params[:id] = File.join('master', blob.path) + + allow(project.repository).to receive(:gitattribute).and_return(nil) end def render_view |