Merge branch 'rs-remove-user-color-scheme-class' into 'master'
Apply syntax highlighting to fenced code blocks client-side Instead of applying the syntax highlighting scheme class to these blocks server-side, we use Javascript and Gon to apply the user's color scheme (or the default) client-side. This will make it easier to cache these blocks in the future because they're no longer state-dependent. See merge request !1203
This commit is contained in:
commit
30555c1d24
23 changed files with 245 additions and 102 deletions
9
app/assets/javascripts/syntax_highlight.coffee
Normal file
9
app/assets/javascripts/syntax_highlight.coffee
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Applies a syntax highlighting color scheme CSS class to any element with the
|
||||
# `js-syntax-highlight` class
|
||||
#
|
||||
# ### Example Markup
|
||||
#
|
||||
# <div class="js-syntax-highlight"></div>
|
||||
#
|
||||
$(document).on 'ready page:load', ->
|
||||
$('.js-syntax-highlight').addClass(gon.user_color_scheme)
|
|
@ -132,10 +132,6 @@ p.time {
|
|||
text-shadow: none;
|
||||
}
|
||||
|
||||
.highlight_word {
|
||||
background: #fafe3d;
|
||||
}
|
||||
|
||||
.thin_area{
|
||||
height: 150px;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,12 @@ pre.code.highlight.dark,
|
|||
background-color: #557 !important;
|
||||
}
|
||||
|
||||
// Search result highlight
|
||||
span.highlight_word {
|
||||
background: #ffe792;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.hll { background-color: #373b41 }
|
||||
.c { color: #969896 } /* Comment */
|
||||
.err { color: #cc6666 } /* Error */
|
||||
|
|
|
@ -21,6 +21,12 @@ pre.code.monokai,
|
|||
background-color: #49483e !important;
|
||||
}
|
||||
|
||||
// Search result highlight
|
||||
span.highlight_word {
|
||||
background: #ffe792;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.hll { background-color: #49483e }
|
||||
.c { color: #75715e } /* Comment */
|
||||
.err { color: #960050; background-color: #1e0010 } /* Error */
|
||||
|
|
|
@ -21,6 +21,11 @@ pre.code.highlight.solarized-dark,
|
|||
background-color: #174652 !important;
|
||||
}
|
||||
|
||||
// Search result highlight
|
||||
span.highlight_word {
|
||||
background: #094554;
|
||||
}
|
||||
|
||||
/* Solarized Dark
|
||||
|
||||
For use with Jekyll and Pygments
|
||||
|
|
|
@ -21,6 +21,11 @@ pre.code.highlight.solarized-light,
|
|||
background-color: #ddd8c5 !important;
|
||||
}
|
||||
|
||||
// Search result highlight
|
||||
span.highlight_word {
|
||||
background: #eee8d5;
|
||||
}
|
||||
|
||||
/* Solarized Light
|
||||
|
||||
For use with Jekyll and Pygments
|
||||
|
|
|
@ -21,6 +21,11 @@ pre.code.highlight.white,
|
|||
background-color: #f8eec7 !important;
|
||||
}
|
||||
|
||||
// Search result highlight
|
||||
span.highlight_word {
|
||||
background: #fafe3d;
|
||||
}
|
||||
|
||||
.hll { background-color: #f8f8f8 }
|
||||
.c { color: #999988; font-style: italic; }
|
||||
.err { color: #a61717; background-color: #e3d2d2; }
|
||||
|
|
|
@ -192,11 +192,12 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def add_gon_variables
|
||||
gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
|
||||
gon.api_version = API::API.version
|
||||
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
|
||||
gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
|
||||
gon.max_file_size = current_application_settings.max_attachment_size;
|
||||
gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
|
||||
gon.max_file_size = current_application_settings.max_attachment_size
|
||||
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
|
||||
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
|
||||
|
||||
if current_user
|
||||
gon.current_user_id = current_user.id
|
||||
|
|
|
@ -58,7 +58,7 @@ module GitlabMarkdownHelper
|
|||
@options = options
|
||||
|
||||
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch
|
||||
rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, options)
|
||||
rend = Redcarpet::Render::GitlabHTML.new(self, options)
|
||||
|
||||
# see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
|
||||
@markdown = Redcarpet::Markdown.new(rend, MARKDOWN_OPTIONS)
|
||||
|
|
|
@ -1,25 +1,5 @@
|
|||
# Helper methods for per-User preferences
|
||||
module PreferencesHelper
|
||||
COLOR_SCHEMES = {
|
||||
1 => 'white',
|
||||
2 => 'dark',
|
||||
3 => 'solarized-light',
|
||||
4 => 'solarized-dark',
|
||||
5 => 'monokai',
|
||||
}
|
||||
COLOR_SCHEMES.default = 'white'
|
||||
|
||||
# Helper method to access the COLOR_SCHEMES
|
||||
#
|
||||
# The keys are the `color_scheme_ids`
|
||||
# The values are the `name` of the scheme.
|
||||
#
|
||||
# The preview images are `name-scheme-preview.png`
|
||||
# The stylesheets should use the css class `.name`
|
||||
def color_schemes
|
||||
COLOR_SCHEMES.freeze
|
||||
end
|
||||
|
||||
# Maps `dashboard` values to more user-friendly option text
|
||||
DASHBOARD_CHOICES = {
|
||||
projects: 'Your Projects (default)',
|
||||
|
@ -50,12 +30,11 @@ module PreferencesHelper
|
|||
end
|
||||
|
||||
def user_application_theme
|
||||
theme = Gitlab::Themes.by_id(current_user.try(:theme_id))
|
||||
theme.css_class
|
||||
Gitlab::Themes.for_user(current_user).css_class
|
||||
end
|
||||
|
||||
def user_color_scheme_class
|
||||
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
|
||||
def user_color_scheme
|
||||
Gitlab::ColorSchemes.for_user(current_user).css_class
|
||||
end
|
||||
|
||||
def prefer_readme?
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
.panel-heading
|
||||
Syntax highlighting theme
|
||||
.panel-body
|
||||
- color_schemes.each do |color_scheme_id, color_scheme|
|
||||
- Gitlab::ColorSchemes.each do |scheme|
|
||||
= label_tag do
|
||||
.preview= image_tag "#{color_scheme}-scheme-preview.png"
|
||||
= f.radio_button :color_scheme_id, color_scheme_id
|
||||
= color_scheme.tr('-_', ' ').titleize
|
||||
.preview= image_tag "#{scheme.css_class}-scheme-preview.png"
|
||||
= f.radio_button :color_scheme_id, scheme.id
|
||||
= scheme.name
|
||||
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
|
|
|
@ -7,4 +7,4 @@
|
|||
%strong
|
||||
= blob.filename
|
||||
.file-content.code.term
|
||||
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, user_color_scheme_class: 'white'
|
||||
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
.nothing-here-block Empty file
|
||||
- else
|
||||
.file-content.code
|
||||
%div.highlighted-data{class: user_color_scheme_class}
|
||||
%div.highlighted-data{ class: user_color_scheme }
|
||||
.line-numbers
|
||||
- snippet_blob[:snippet_chunks].each do |snippet|
|
||||
- unless snippet[:data].empty?
|
||||
|
|
|
@ -7,4 +7,4 @@
|
|||
%strong
|
||||
= wiki_blob.filename
|
||||
.file-content.code.term
|
||||
= render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline, user_color_scheme_class: 'white'
|
||||
= render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.file-content.code{class: user_color_scheme_class}
|
||||
.file-content.code.js-syntax-highlight{ class: user_color_scheme }
|
||||
.line-numbers
|
||||
- if blob.data.present?
|
||||
- blob.data.lines.each_index do |index|
|
||||
|
|
67
lib/gitlab/color_schemes.rb
Normal file
67
lib/gitlab/color_schemes.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
module Gitlab
|
||||
# Module containing GitLab's syntax color scheme definitions and helper
|
||||
# methods for accessing them.
|
||||
module ColorSchemes
|
||||
# Struct class representing a single Scheme
|
||||
Scheme = Struct.new(:id, :name, :css_class)
|
||||
|
||||
SCHEMES = [
|
||||
Scheme.new(1, 'White', 'white'),
|
||||
Scheme.new(2, 'Dark', 'dark'),
|
||||
Scheme.new(3, 'Solarized Light', 'solarized-light'),
|
||||
Scheme.new(4, 'Solarized Dark', 'solarized-dark'),
|
||||
Scheme.new(5, 'Monokai', 'monokai')
|
||||
].freeze
|
||||
|
||||
# Convenience method to get a space-separated String of all the color scheme
|
||||
# classes that might be applied to a code block.
|
||||
#
|
||||
# Returns a String
|
||||
def self.body_classes
|
||||
SCHEMES.collect(&:css_class).uniq.join(' ')
|
||||
end
|
||||
|
||||
# Get a Scheme by its ID
|
||||
#
|
||||
# If the ID is invalid, returns the default Scheme.
|
||||
#
|
||||
# id - Integer ID
|
||||
#
|
||||
# Returns a Scheme
|
||||
def self.by_id(id)
|
||||
SCHEMES.detect { |s| s.id == id } || default
|
||||
end
|
||||
|
||||
# Returns the number of defined Schemes
|
||||
def self.count
|
||||
SCHEMES.size
|
||||
end
|
||||
|
||||
# Get the default Scheme
|
||||
#
|
||||
# Returns a Scheme
|
||||
def self.default
|
||||
by_id(1)
|
||||
end
|
||||
|
||||
# Iterate through each Scheme
|
||||
#
|
||||
# Yields the Scheme object
|
||||
def self.each(&block)
|
||||
SCHEMES.each(&block)
|
||||
end
|
||||
|
||||
# Get the Scheme for the specified user, or the default
|
||||
#
|
||||
# user - User record
|
||||
#
|
||||
# Returns a Scheme
|
||||
def self.for_user(user)
|
||||
if user
|
||||
by_id(user.color_scheme_id)
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -37,6 +37,11 @@ module Gitlab
|
|||
THEMES.detect { |t| t.id == id } || default
|
||||
end
|
||||
|
||||
# Returns the number of defined Themes
|
||||
def self.count
|
||||
THEMES.size
|
||||
end
|
||||
|
||||
# Get the default Theme
|
||||
#
|
||||
# Returns a Theme
|
||||
|
@ -51,6 +56,19 @@ module Gitlab
|
|||
THEMES.each(&block)
|
||||
end
|
||||
|
||||
# Get the Theme for the specified user, or the default
|
||||
#
|
||||
# user - User record
|
||||
#
|
||||
# Returns a Theme
|
||||
def self.for_user(user)
|
||||
if user
|
||||
by_id(user.theme_id)
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.default_id
|
||||
|
|
|
@ -4,9 +4,8 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
|
|||
attr_reader :template
|
||||
alias_method :h, :template
|
||||
|
||||
def initialize(template, color_scheme, options = {})
|
||||
def initialize(template, options = {})
|
||||
@template = template
|
||||
@color_scheme = color_scheme
|
||||
@options = options.dup
|
||||
|
||||
@options.reverse_merge!(
|
||||
|
@ -35,7 +34,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
|
|||
end
|
||||
|
||||
formatter = Rouge::Formatters::HTMLGitlab.new(
|
||||
cssclass: "code highlight #{@color_scheme} #{lexer.tag}"
|
||||
cssclass: "code highlight js-syntax-highlight #{lexer.tag}"
|
||||
)
|
||||
formatter.format(lexer.lex(code))
|
||||
end
|
||||
|
|
|
@ -64,8 +64,8 @@ describe 'GitLab Markdown', feature: true do
|
|||
|
||||
it 'parses fenced code blocks' do
|
||||
aggregate_failures do
|
||||
expect(doc).to have_selector('pre.code.highlight.white.c')
|
||||
expect(doc).to have_selector('pre.code.highlight.white.python')
|
||||
expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.c')
|
||||
expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.python')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -224,8 +224,4 @@ describe 'GitLab Markdown', feature: true do
|
|||
def current_user
|
||||
@feat.user
|
||||
end
|
||||
|
||||
def user_color_scheme_class
|
||||
:white
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,8 +28,7 @@ describe EventsHelper do
|
|||
|
||||
it 'should display the first line of a code block' do
|
||||
input = "```\nCode block\nwith two lines\n```"
|
||||
expected = '<pre class="code highlight white plaintext"><code>' \
|
||||
'Code block...</code></pre>'
|
||||
expected = %r{<pre.+><code>Code block\.\.\.</code></pre>}
|
||||
|
||||
expect(event_note(input)).to match(expected)
|
||||
end
|
||||
|
@ -55,7 +54,7 @@ describe EventsHelper do
|
|||
|
||||
it 'should preserve code color scheme' do
|
||||
input = "```ruby\ndef test\n 'hello world'\nend\n```"
|
||||
expected = '<pre class="code highlight white ruby">' \
|
||||
expected = '<pre class="code highlight js-syntax-highlight ruby">' \
|
||||
"<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
|
||||
" <span class=\"s1\">\'hello world\'</span>\n" \
|
||||
"<span class=\"k\">end</span>" \
|
||||
|
|
|
@ -1,72 +1,82 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe PreferencesHelper do
|
||||
describe 'user_application_theme' do
|
||||
context 'with a user' do
|
||||
it "returns user's theme's css_class" do
|
||||
user = double('user', theme_id: 3)
|
||||
allow(self).to receive(:current_user).and_return(user)
|
||||
expect(user_application_theme).to eq 'ui_green'
|
||||
end
|
||||
|
||||
it 'returns the default when id is invalid' do
|
||||
user = double('user', theme_id: Gitlab::Themes::THEMES.size + 5)
|
||||
|
||||
allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(2)
|
||||
allow(self).to receive(:current_user).and_return(user)
|
||||
|
||||
expect(user_application_theme).to eq 'ui_charcoal'
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a user' do
|
||||
before do
|
||||
allow(self).to receive(:current_user).and_return(nil)
|
||||
end
|
||||
|
||||
it 'returns the default theme' do
|
||||
expect(user_application_theme).to eq Gitlab::Themes.default.css_class
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'dashboard_choices' do
|
||||
it 'raises an exception when defined choices may be missing' do
|
||||
expect(User).to receive(:dashboards).and_return(foo: 'foo')
|
||||
expect { dashboard_choices }.to raise_error(RuntimeError)
|
||||
expect { helper.dashboard_choices }.to raise_error(RuntimeError)
|
||||
end
|
||||
|
||||
it 'raises an exception when defined choices may be using the wrong key' do
|
||||
expect(User).to receive(:dashboards).and_return(foo: 'foo', bar: 'bar')
|
||||
expect { dashboard_choices }.to raise_error(KeyError)
|
||||
expect { helper.dashboard_choices }.to raise_error(KeyError)
|
||||
end
|
||||
|
||||
it 'provides better option descriptions' do
|
||||
expect(dashboard_choices).to match_array [
|
||||
expect(helper.dashboard_choices).to match_array [
|
||||
['Your Projects (default)', 'projects'],
|
||||
['Starred Projects', 'stars']
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
describe 'user_color_scheme_class' do
|
||||
context 'with current_user is nil' do
|
||||
it 'should return a string' do
|
||||
allow(self).to receive(:current_user).and_return(nil)
|
||||
expect(user_color_scheme_class).to be_kind_of(String)
|
||||
describe 'user_application_theme' do
|
||||
context 'with a user' do
|
||||
it "returns user's theme's css_class" do
|
||||
stub_user(theme_id: 3)
|
||||
|
||||
expect(helper.user_application_theme).to eq 'ui_green'
|
||||
end
|
||||
|
||||
it 'returns the default when id is invalid' do
|
||||
stub_user(theme_id: Gitlab::Themes.count + 5)
|
||||
|
||||
allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(2)
|
||||
|
||||
expect(helper.user_application_theme).to eq 'ui_charcoal'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a current_user' do
|
||||
(1..5).each do |color_scheme_id|
|
||||
context "with color_scheme_id == #{color_scheme_id}" do
|
||||
it 'should return a string' do
|
||||
current_user = double(color_scheme_id: color_scheme_id)
|
||||
allow(self).to receive(:current_user).and_return(current_user)
|
||||
expect(user_color_scheme_class).to be_kind_of(String)
|
||||
context 'without a user' do
|
||||
it 'returns the default theme' do
|
||||
stub_user
|
||||
|
||||
expect(helper.user_application_theme).to eq Gitlab::Themes.default.css_class
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'user_color_scheme' do
|
||||
context 'with a user' do
|
||||
it "returns user's scheme's css_class" do
|
||||
allow(helper).to receive(:current_user).
|
||||
and_return(double(color_scheme_id: 3))
|
||||
|
||||
expect(helper.user_color_scheme).to eq 'solarized-light'
|
||||
end
|
||||
|
||||
it 'returns the default when id is invalid' do
|
||||
allow(helper).to receive(:current_user).
|
||||
and_return(double(color_scheme_id: Gitlab::ColorSchemes.count + 5))
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a user' do
|
||||
it 'returns the default theme' do
|
||||
stub_user
|
||||
|
||||
expect(helper.user_color_scheme).
|
||||
to eq Gitlab::ColorSchemes.default.css_class
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def stub_user(messages = {})
|
||||
if messages.empty?
|
||||
allow(helper).to receive(:current_user).and_return(nil)
|
||||
else
|
||||
allow(helper).to receive(:current_user).
|
||||
and_return(double('user', messages))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
45
spec/lib/gitlab/color_schemes_spec.rb
Normal file
45
spec/lib/gitlab/color_schemes_spec.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::ColorSchemes do
|
||||
describe '.body_classes' do
|
||||
it 'returns a space-separated list of class names' do
|
||||
css = described_class.body_classes
|
||||
|
||||
expect(css).to include('white')
|
||||
expect(css).to include(' solarized-light ')
|
||||
expect(css).to include(' monokai')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.by_id' do
|
||||
it 'returns a scheme by its ID' do
|
||||
expect(described_class.by_id(1).name).to eq 'White'
|
||||
expect(described_class.by_id(4).name).to eq 'Solarized Dark'
|
||||
end
|
||||
end
|
||||
|
||||
describe '.default' do
|
||||
it 'returns the default scheme' do
|
||||
expect(described_class.default.id).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
describe '.each' do
|
||||
it 'passes the block to the SCHEMES Array' do
|
||||
ids = []
|
||||
described_class.each { |scheme| ids << scheme.id }
|
||||
expect(ids).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '.for_user' do
|
||||
it 'returns default when user is nil' do
|
||||
expect(described_class.for_user(nil).id).to eq 1
|
||||
end
|
||||
|
||||
it "returns user's preferred color scheme" do
|
||||
user = double(color_scheme_id: 5)
|
||||
expect(described_class.for_user(user).id).to eq 5
|
||||
end
|
||||
end
|
||||
end
|
|
@ -43,9 +43,6 @@ describe Gitlab::Themes do
|
|||
ids = []
|
||||
described_class.each { |theme| ids << theme.id }
|
||||
expect(ids).not_to be_empty
|
||||
|
||||
# TODO (rspeicher): RSpec 3.x
|
||||
# expect(described_class.each).to yield_with_arg(described_class::Theme)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue