diff options
author | Rémy Coutable <remy@rymai.me> | 2016-06-28 08:42:42 +0000 |
---|---|---|
committer | Rémy Coutable <remy@rymai.me> | 2016-06-28 08:42:42 +0000 |
commit | 0d0f8a3b7dd566778b94f6e0862bae5cba9b5ef7 (patch) | |
tree | a85cd176775372ddca710edc0a8072237869cd7d | |
parent | c9a46263336dd38aef90b71995e2790be72d441d (diff) | |
parent | ffd8162191a5fab2a182da718fc5d83d6cd965fc (diff) | |
download | gitlab-ce-0d0f8a3b7dd566778b94f6e0862bae5cba9b5ef7.tar.gz |
Merge branch 'feature/custom-highlighting' into 'master'
Add custom highlighting via .gitattributes
## What does this MR do?
Allows user control of language selection via a `gitlab-language` entry in `.gitattributes`
## Are there points in the code the reviewer needs to double check?
(paired with @stanhu)
## Why was this MR needed?
Guessing languages by filename is fraught and often wrong. In one project, `foo.pl` may be perl, and in another it may be prolog. Users might have a Thingfile that needs ruby highlighting, or depend on things that can't work in general, like `*.C` (capitalized) mapping to C++ instead of C.
This allows the user to override language choice so they never have to look at a mis-highlighted file.
## What are the relevant issue numbers?
https://github.com/jneen/rouge/issues/494
https://gitlab.com/gitlab-org/gitlab-ce/issues/13818 (*.tpl can't in general map to Smarty)
https://gitlab.com/gitlab-org/gitlab-ce/issues/13615 (in cases we don't have a language and mis-identify it, users could map to 'text' to turn off highlighting)
## Screenshots (if relevant)
## Does this MR meet the acceptance criteria?
- [x] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added
- [x] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md)
- [x] API support added (N/A)
- [x] Tests
- [x] Added for this feature/bug
- [x] All builds are passing
- [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [x] Branch has no merge conflicts with `master` (if you do - rebase it please)
- [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
See merge request !4606
-rw-r--r-- | app/helpers/blob_helper.rb | 8 | ||||
-rw-r--r-- | app/models/repository.rb | 4 | ||||
-rw-r--r-- | app/models/snippet.rb | 6 | ||||
-rw-r--r-- | app/views/projects/blob/_text.html.haml | 2 | ||||
-rw-r--r-- | app/views/shared/_file_highlight.html.haml | 4 | ||||
-rw-r--r-- | doc/user/project/highlighting.md | 31 | ||||
-rw-r--r-- | lib/gitlab/blame.rb | 3 | ||||
-rw-r--r-- | lib/gitlab/highlight.rb | 27 | ||||
-rw-r--r-- | spec/lib/gitlab/highlight_spec.rb | 27 | ||||
-rw-r--r-- | spec/support/test_env.rb | 1 |
10 files changed, 101 insertions, 12 deletions
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 4b4bc3d4276..428a42266d0 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -1,10 +1,10 @@ module BlobHelper - def highlighter(blob_name, blob_content, nowrap: false) - Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap) + def highlighter(blob_name, blob_content, repository: nil, nowrap: false) + Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository) end - def highlight(blob_name, blob_content, nowrap: false, plain: false) - Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain) + def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false) + Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository) end def no_highlight_files diff --git a/app/models/repository.rb b/app/models/repository.rb index acc720ccfa3..2a6a3b086c2 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -978,6 +978,10 @@ class Repository raw_repository.ls_files(actual_ref) end + def gitattribute(path, name) + raw_repository.attributes(path)[name] + end + def copy_gitattributes(ref) actual_ref = ref || root_ref begin diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 51f6ae7b25c..5ec933601ac 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -20,6 +20,7 @@ class Snippet < ActiveRecord::Base length: { within: 0..255 }, format: { with: Gitlab::Regex.file_name_regex, message: Gitlab::Regex.file_name_regex_message } + validates :content, presence: true validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } @@ -81,6 +82,11 @@ class Snippet < ActiveRecord::Base 0 end + # alias for compatibility with blobs and highlighting + def path + file_name + end + def name file_name end diff --git a/app/views/projects/blob/_text.html.haml b/app/views/projects/blob/_text.html.haml index b1769759dce..58524418a67 100644 --- a/app/views/projects/blob/_text.html.haml +++ b/app/views/projects/blob/_text.html.haml @@ -16,4 +16,4 @@ .file-content.code .nothing-here-block Empty file - else - = render 'shared/file_highlight', blob: blob + = render 'shared/file_highlight', blob: blob, repository: @repository diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index ad944a19ca1..e26693bf5b9 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -1,3 +1,5 @@ +- repository = nil unless local_assigns.key?(:repository) + .file-content.code.js-syntax-highlight .line-numbers - if blob.data.present? @@ -11,4 +13,4 @@ = link_icon = i .blob-content{data: {blob_id: blob.id}} - = highlight(blob.name, blob.data, plain: blob.no_highlighting?) + = highlight(blob.path, blob.data, repository: repository, plain: blob.no_highlighting?) diff --git a/doc/user/project/highlighting.md b/doc/user/project/highlighting.md new file mode 100644 index 00000000000..73a2d176b54 --- /dev/null +++ b/doc/user/project/highlighting.md @@ -0,0 +1,31 @@ +[Rouge]: https://rubygems.org/gems/rouge + +# Syntax Highlighting + +GitLab provides syntax highlighting on all files and snippets through the [Rouge][] rubygem. It will try to guess what language to use based on the file extension, which most of the time is sufficient. + +If GitLab is guessing wrong, you can override its choice of language using the `gitlab-language` attribute in `.gitattributes`. For example, if you are working in a Prolog project and using the `.pl` file extension (which would normally be highlighted as Perl), you can add the following to your `.gitattributes` file: + +``` conf +*.pl gitlab-language=prolog +``` + +When you check in and push that change, all `*.pl` files in your project will be highlighted as Prolog. + +The paths here are simply git's builtin [`.gitattributes` interface](https://git-scm.com/docs/gitattributes). So, if you were to invent a file format called a `Nicefile` at the root of your project that used ruby syntax, all you need is: + +``` conf +/Nicefile gitlab-language=ruby +``` + +To disable highlighting entirely, use `gitlab-language=text`. Lots more fun shenanigans are available through CGI options, such as: + +``` conf +# json with erb in it +/my-cool-file gitlab-language=erb?parent=json + +# an entire file of highlighting errors! +/other-file gitlab-language=text?token=Error +``` + +Please note that these configurations will only take effect when the `.gitattributes` file is in your default branch (usually `master`). diff --git a/lib/gitlab/blame.rb b/lib/gitlab/blame.rb index 997a22779a0..d62bc50ce78 100644 --- a/lib/gitlab/blame.rb +++ b/lib/gitlab/blame.rb @@ -41,7 +41,8 @@ module Gitlab def highlighted_lines @blob.load_all_data!(repository) - @highlighted_lines ||= Gitlab::Highlight.highlight(@blob.name, @blob.data).lines + @highlighted_lines ||= + Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: repository).lines end def project diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb index 280120b0f9e..41296415e35 100644 --- a/lib/gitlab/highlight.rb +++ b/lib/gitlab/highlight.rb @@ -1,7 +1,7 @@ module Gitlab class Highlight - def self.highlight(blob_name, blob_content, nowrap: true, plain: false) - new(blob_name, blob_content, nowrap: nowrap). + def self.highlight(blob_name, blob_content, repository: nil, nowrap: true, plain: false) + new(blob_name, blob_content, nowrap: nowrap, repository: repository). highlight(blob_content, continue: false, plain: plain) end @@ -10,12 +10,21 @@ module Gitlab return [] unless blob blob.load_all_data!(repository) - highlight(file_name, blob.data).lines.map!(&:html_safe) + highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe) end - def initialize(blob_name, blob_content, nowrap: true) + attr_reader :lexer + def initialize(blob_name, blob_content, repository: nil, nowrap: true) + @blob_name = blob_name + @blob_content = blob_content + @repository = repository @formatter = rouge_formatter(nowrap: nowrap) - @lexer = Rouge::Lexer.guess(filename: blob_name, source: blob_content).new rescue Rouge::Lexers::PlainText + + @lexer = custom_language || begin + Rouge::Lexer.guess(filename: blob_name, source: blob_content).new + rescue Rouge::Lexer::AmbiguousGuess => e + e.alternatives.sort_by(&:tag).first + end end def highlight(text, continue: true, plain: false) @@ -30,6 +39,14 @@ module Gitlab private + def custom_language + language_name = @repository && @repository.gitattribute(@blob_name, 'gitlab-language') + + return nil unless language_name + + Rouge::Lexer.find_fancy(language_name) + end + def rouge_formatter(options = {}) options = options.reverse_merge( nowrap: true, diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb index 1620eb6c60a..364532e94e3 100644 --- a/spec/lib/gitlab/highlight_spec.rb +++ b/spec/lib/gitlab/highlight_spec.rb @@ -4,6 +4,7 @@ describe Gitlab::Highlight, lib: true do include RepoHelpers let(:project) { create(:project) } + let(:repository) { project.repository } let(:commit) { project.commit(sample_commit.id) } describe '.highlight_lines' do @@ -18,4 +19,30 @@ describe Gitlab::Highlight, lib: true do end end + describe 'custom highlighting from .gitattributes' do + let(:branch) { 'gitattributes' } + let(:blob) { repository.blob_at_branch(branch, path) } + + let(:highlighter) do + Gitlab::Highlight.new(blob.path, blob.data, repository: repository) + end + + before { project.change_head('gitattributes') } + + 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 + end + + describe 'cgi options' do + let(:path) { 'custom-highlighting/test.gitlab-cgi' } + + it 'highlights as json with erb' do + expect(highlighter.lexer.tag).to eq 'erb' + expect(highlighter.lexer.parent.tag).to eq 'json' + end + end + end end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 498bd4bf800..426bf53f198 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -17,6 +17,7 @@ module TestEnv "'test'" => 'e56497b', 'orphaned-branch' => '45127a9', 'binary-encoding' => '7b1cf43', + 'gitattributes' => '5a62481', } # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily |