Commit 30555c1d authored by Douwe Maan's avatar Douwe Maan

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
parents abb5b9f6 ce0a0fef
# 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 { ...@@ -132,10 +132,6 @@ p.time {
text-shadow: none; text-shadow: none;
} }
.highlight_word {
background: #fafe3d;
}
.thin_area{ .thin_area{
height: 150px; height: 150px;
} }
......
...@@ -21,6 +21,12 @@ pre.code.highlight.dark, ...@@ -21,6 +21,12 @@ pre.code.highlight.dark,
background-color: #557 !important; background-color: #557 !important;
} }
// Search result highlight
span.highlight_word {
background: #ffe792;
color: #000000;
}
.hll { background-color: #373b41 } .hll { background-color: #373b41 }
.c { color: #969896 } /* Comment */ .c { color: #969896 } /* Comment */
.err { color: #cc6666 } /* Error */ .err { color: #cc6666 } /* Error */
......
...@@ -21,6 +21,12 @@ pre.code.monokai, ...@@ -21,6 +21,12 @@ pre.code.monokai,
background-color: #49483e !important; background-color: #49483e !important;
} }
// Search result highlight
span.highlight_word {
background: #ffe792;
color: #000000;
}
.hll { background-color: #49483e } .hll { background-color: #49483e }
.c { color: #75715e } /* Comment */ .c { color: #75715e } /* Comment */
.err { color: #960050; background-color: #1e0010 } /* Error */ .err { color: #960050; background-color: #1e0010 } /* Error */
......
...@@ -21,6 +21,11 @@ pre.code.highlight.solarized-dark, ...@@ -21,6 +21,11 @@ pre.code.highlight.solarized-dark,
background-color: #174652 !important; background-color: #174652 !important;
} }
// Search result highlight
span.highlight_word {
background: #094554;
}
/* Solarized Dark /* Solarized Dark
For use with Jekyll and Pygments For use with Jekyll and Pygments
......
...@@ -21,6 +21,11 @@ pre.code.highlight.solarized-light, ...@@ -21,6 +21,11 @@ pre.code.highlight.solarized-light,
background-color: #ddd8c5 !important; background-color: #ddd8c5 !important;
} }
// Search result highlight
span.highlight_word {
background: #eee8d5;
}
/* Solarized Light /* Solarized Light
For use with Jekyll and Pygments For use with Jekyll and Pygments
......
...@@ -21,6 +21,11 @@ pre.code.highlight.white, ...@@ -21,6 +21,11 @@ pre.code.highlight.white,
background-color: #f8eec7 !important; background-color: #f8eec7 !important;
} }
// Search result highlight
span.highlight_word {
background: #fafe3d;
}
.hll { background-color: #f8f8f8 } .hll { background-color: #f8f8f8 }
.c { color: #999988; font-style: italic; } .c { color: #999988; font-style: italic; }
.err { color: #a61717; background-color: #e3d2d2; } .err { color: #a61717; background-color: #e3d2d2; }
......
...@@ -192,11 +192,12 @@ class ApplicationController < ActionController::Base ...@@ -192,11 +192,12 @@ class ApplicationController < ActionController::Base
end end
def add_gon_variables def add_gon_variables
gon.api_version = API::API.version
gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
gon.default_issues_tracker = Project.new.default_issue_tracker.to_param gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
gon.api_version = API::API.version gon.max_file_size = current_application_settings.max_attachment_size
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root 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.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
gon.max_file_size = current_application_settings.max_attachment_size;
if current_user if current_user
gon.current_user_id = current_user.id gon.current_user_id = current_user.id
......
...@@ -58,7 +58,7 @@ module GitlabMarkdownHelper ...@@ -58,7 +58,7 @@ module GitlabMarkdownHelper
@options = options @options = options
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch # 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 # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
@markdown = Redcarpet::Markdown.new(rend, MARKDOWN_OPTIONS) @markdown = Redcarpet::Markdown.new(rend, MARKDOWN_OPTIONS)
......
# Helper methods for per-User preferences # Helper methods for per-User preferences
module PreferencesHelper 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 # Maps `dashboard` values to more user-friendly option text
DASHBOARD_CHOICES = { DASHBOARD_CHOICES = {
projects: 'Your Projects (default)', projects: 'Your Projects (default)',
...@@ -50,12 +30,11 @@ module PreferencesHelper ...@@ -50,12 +30,11 @@ module PreferencesHelper
end end
def user_application_theme def user_application_theme
theme = Gitlab::Themes.by_id(current_user.try(:theme_id)) Gitlab::Themes.for_user(current_user).css_class
theme.css_class
end end
def user_color_scheme_class def user_color_scheme
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user) Gitlab::ColorSchemes.for_user(current_user).css_class
end end
def prefer_readme? def prefer_readme?
......
...@@ -22,11 +22,11 @@ ...@@ -22,11 +22,11 @@
.panel-heading .panel-heading
Syntax highlighting theme Syntax highlighting theme
.panel-body .panel-body
- color_schemes.each do |color_scheme_id, color_scheme| - Gitlab::ColorSchemes.each do |scheme|
= label_tag do = label_tag do
.preview= image_tag "#{color_scheme}-scheme-preview.png" .preview= image_tag "#{scheme.css_class}-scheme-preview.png"
= f.radio_button :color_scheme_id, color_scheme_id = f.radio_button :color_scheme_id, scheme.id
= color_scheme.tr('-_', ' ').titleize = scheme.name
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
......
...@@ -7,4 +7,4 @@ ...@@ -7,4 +7,4 @@
%strong %strong
= blob.filename = blob.filename
.file-content.code.term .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 @@ ...@@ -23,7 +23,7 @@
.nothing-here-block Empty file .nothing-here-block Empty file
- else - else
.file-content.code .file-content.code
%div.highlighted-data{class: user_color_scheme_class} %div.highlighted-data{ class: user_color_scheme }
.line-numbers .line-numbers
- snippet_blob[:snippet_chunks].each do |snippet| - snippet_blob[:snippet_chunks].each do |snippet|
- unless snippet[:data].empty? - unless snippet[:data].empty?
......
...@@ -7,4 +7,4 @@ ...@@ -7,4 +7,4 @@
%strong %strong
= wiki_blob.filename = wiki_blob.filename
.file-content.code.term .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
.file-content.code{class: user_color_scheme_class} .file-content.code.js-syntax-highlight{ class: user_color_scheme }
.line-numbers .line-numbers
- if blob.data.present? - if blob.data.present?
- blob.data.lines.each_index do |index| - blob.data.lines.each_index do |index|
......
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 ...@@ -37,6 +37,11 @@ module Gitlab
THEMES.detect { |t| t.id == id } || default THEMES.detect { |t| t.id == id } || default
end end
# Returns the number of defined Themes
def self.count
THEMES.size
end
# Get the default Theme # Get the default Theme
# #
# Returns a Theme # Returns a Theme
...@@ -51,6 +56,19 @@ module Gitlab ...@@ -51,6 +56,19 @@ module Gitlab
THEMES.each(&block) THEMES.each(&block)
end 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 private
def self.default_id def self.default_id
......
...@@ -4,9 +4,8 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -4,9 +4,8 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
attr_reader :template attr_reader :template
alias_method :h, :template alias_method :h, :template
def initialize(template, color_scheme, options = {}) def initialize(template, options = {})
@template = template @template = template
@color_scheme = color_scheme
@options = options.dup @options = options.dup
@options.reverse_merge!( @options.reverse_merge!(
...@@ -35,7 +34,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -35,7 +34,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
end end
formatter = Rouge::Formatters::HTMLGitlab.new( 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)) formatter.format(lexer.lex(code))
end end
......
...@@ -64,8 +64,8 @@ describe 'GitLab Markdown', feature: true do ...@@ -64,8 +64,8 @@ describe 'GitLab Markdown', feature: true do
it 'parses fenced code blocks' do it 'parses fenced code blocks' do
aggregate_failures do aggregate_failures do
expect(doc).to have_selector('pre.code.highlight.white.c') expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.c')
expect(doc).to have_selector('pre.code.highlight.white.python') expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.python')
end end
end end
...@@ -224,8 +224,4 @@ describe 'GitLab Markdown', feature: true do ...@@ -224,8 +224,4 @@ describe 'GitLab Markdown', feature: true do
def current_user def current_user
@feat.user @feat.user
end end
def user_color_scheme_class
:white
end
end end
...@@ -28,8 +28,7 @@ describe EventsHelper do ...@@ -28,8 +28,7 @@ describe EventsHelper do
it 'should display the first line of a code block' do it 'should display the first line of a code block' do
input = "```\nCode block\nwith two lines\n```" input = "```\nCode block\nwith two lines\n```"
expected = '<pre class="code highlight white plaintext"><code>' \ expected = %r{<pre.+><code>Code block\.\.\.</code></pre>}
'Code block...</code></pre>'
expect(event_note(input)).to match(expected) expect(event_note(input)).to match(expected)
end end
...@@ -55,7 +54,7 @@ describe EventsHelper do ...@@ -55,7 +54,7 @@ describe EventsHelper do
it 'should preserve code color scheme' do it 'should preserve code color scheme' do
input = "```ruby\ndef test\n 'hello world'\nend\n```" 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" \ "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
" <span class=\"s1\">\'hello world\'</span>\n" \ " <span class=\"s1\">\'hello world\'</span>\n" \
"<span class=\"k\">end</span>" \ "<span class=\"k\">end</span>" \
......
require 'spec_helper' require 'spec_helper'
describe PreferencesHelper do describe PreferencesHelper do
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 { 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 { helper.dashboard_choices }.to raise_error(KeyError)
end
it 'provides better option descriptions' do
expect(helper.dashboard_choices).to match_array [
['Your Projects (default)', 'projects'],
['Starred Projects', 'stars']
]
end
end
describe 'user_application_theme' do describe 'user_application_theme' do
context 'with a user' do context 'with a user' do
it "returns user's theme's css_class" do it "returns user's theme's css_class" do
user = double('user', theme_id: 3) stub_user(theme_id: 3)
allow(self).to receive(:current_user).and_return(user)
expect(user_application_theme).to eq 'ui_green' expect(helper.user_application_theme).to eq 'ui_green'
end end
it 'returns the default when id is invalid' do it 'returns the default when id is invalid' do
user = double('user', theme_id: Gitlab::Themes::THEMES.size + 5) stub_user(theme_id: Gitlab::Themes.count + 5)
allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(2) 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' expect(helper.user_application_theme).to eq 'ui_charcoal'
end end
end end
context 'without a user' do context 'without a user' do
before do
allow(self).to receive(:current_user).and_return(nil)
end
it 'returns the default theme' do it 'returns the default theme' do
expect(user_application_theme).to eq Gitlab::Themes.default.css_class stub_user
expect(helper.user_application_theme).to eq Gitlab::Themes.default.css_class
end end
end end
end end
describe 'dashboard_choices' do describe 'user_color_scheme' do
it 'raises an exception when defined choices may be missing' do context 'with a user' do
expect(User).to receive(:dashboards).and_return(foo: 'foo') it "returns user's scheme's css_class" do
expect { dashboard_choices }.to raise_error(RuntimeError) allow(helper).to receive(:current_user).
end and_return(double(color_scheme_id: 3))
it 'raises an exception when defined choices may be using the wrong key' do expect(helper.user_color_scheme).to eq 'solarized-light'
expect(User).to receive(:dashboards).and_return(foo: 'foo', bar: 'bar') end
expect { dashboard_choices }.to raise_error(KeyError)
end
it 'provides better option descriptions' do it 'returns the default when id is invalid' do
expect(dashboard_choices).to match_array [ allow(helper).to receive(:current_user).
['Your Projects (default)', 'projects'], and_return(double(color_scheme_id: Gitlab::ColorSchemes.count + 5))
['Starred Projects', 'stars'] end
]
end end
end
describe 'user_color_scheme_class' do context 'without a user' do
context 'with current_user is nil' do it 'returns the default theme' do
it 'should return a string' do stub_user
allow(self).to receive(:current_user).and_return(nil)
expect(user_color_scheme_class).to be_kind_of(String) expect(helper.user_color_scheme).
to eq Gitlab::ColorSchemes.default.css_class
end end
end end
end
context 'with a current_user' do def stub_user(messages = {})
(1..5).each do |color_scheme_id| if messages.empty?
context "with color_scheme_id == #{color_scheme_id}" do allow(helper).to receive(:current_user).and_return(nil)
it 'should return a string' do else
current_user = double(color_scheme_id: color_scheme_id) allow(helper).to receive(:current_user).
allow(self).to receive(:current_user).and_return(current_user) and_return(double('user', messages))
expect(user_color_scheme_class).to be_kind_of(String)
end
end
end
end end
end end
end end
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 ...@@ -43,9 +43,6 @@ describe Gitlab::Themes do
ids = [] ids = []
described_class.each { |theme| ids << theme.id } described_class.each { |theme| ids << theme.id }
expect(ids).not_to be_empty 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 end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment