diff --git a/app/assets/javascripts/syntax_highlight.coffee b/app/assets/javascripts/syntax_highlight.coffee new file mode 100644 index 00000000000..510f15d1b49 --- /dev/null +++ b/app/assets/javascripts/syntax_highlight.coffee @@ -0,0 +1,9 @@ +# Applies a syntax highlighting color scheme CSS class to any element with the +# `js-syntax-highlight` class +# +# ### Example Markup +# +#
+# +$(document).on 'ready page:load', -> + $('.js-syntax-highlight').addClass(gon.user_color_scheme) diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 2674fde41ae..e5902597c4d 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -132,10 +132,6 @@ p.time { text-shadow: none; } -.highlight_word { - background: #fafe3d; -} - .thin_area{ height: 150px; } diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss index c8cb18ec35f..8323a8598ec 100644 --- a/app/assets/stylesheets/highlight/dark.scss +++ b/app/assets/stylesheets/highlight/dark.scss @@ -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 */ diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index 001e8b31020..e8381674336 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -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 */ diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss index f5b827e7c02..bd41480aefb 100644 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ b/app/assets/stylesheets/highlight/solarized_dark.scss @@ -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 diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss index 6b44c00c305..4cc62863870 100644 --- a/app/assets/stylesheets/highlight/solarized_light.scss +++ b/app/assets/stylesheets/highlight/solarized_light.scss @@ -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 diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index a52ffc971d1..e0edfb80b42 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -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; } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ef1170e16da..cb1cf13d34d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -192,11 +192,12 @@ class ApplicationController < ActionController::Base end 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.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.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 diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index eb3f72a307d..114730eb948 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -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) diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index ea774e28ecf..7f1b6a69926 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -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? diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 1134317ee06..aa0361a0a1b 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -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 diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml index 58f58eff54d..0fe8a3b490a 100644 --- a/app/views/search/results/_blob.html.haml +++ b/app/views/search/results/_blob.html.haml @@ -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 diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml index 95099853918..9a4f9fb9485 100644 --- a/app/views/search/results/_snippet_blob.html.haml +++ b/app/views/search/results/_snippet_blob.html.haml @@ -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? diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml index c03438eb952..f5859481d46 100644 --- a/app/views/search/results/_wiki_blob.html.haml +++ b/app/views/search/results/_wiki_blob.html.haml @@ -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 diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index d6a2e177da1..57c3aff3e18 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -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| diff --git a/lib/gitlab/color_schemes.rb b/lib/gitlab/color_schemes.rb new file mode 100644 index 00000000000..9c4664df903 --- /dev/null +++ b/lib/gitlab/color_schemes.rb @@ -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 diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb index 5209df92795..83f91de810c 100644 --- a/lib/gitlab/themes.rb +++ b/lib/gitlab/themes.rb @@ -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 diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb index f57b56cbdf0..9cb8e91d6e3 100644 --- a/lib/redcarpet/render/gitlab_html.rb +++ b/lib/redcarpet/render/gitlab_html.rb @@ -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 diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index 3da4dfc2b23..4fe019f8342 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -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 diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index da58ab98462..e68a5ec29ab 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -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 = '
' \
-               'Code block...
' + expected = %r{Code block\.\.\.} 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 = '
' \
+    expected = '
' \
       "def test\n" \
       "  \'hello world\'\n" \
       "end" \
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index d814b562113..06f69262b71 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -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)
-          end
-        end
+    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
diff --git a/spec/lib/gitlab/color_schemes_spec.rb b/spec/lib/gitlab/color_schemes_spec.rb
new file mode 100644
index 00000000000..c7be45dbcd3
--- /dev/null
+++ b/spec/lib/gitlab/color_schemes_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb
index 9c6c3fd8104..e554458e41c 100644
--- a/spec/lib/gitlab/themes_spec.rb
+++ b/spec/lib/gitlab/themes_spec.rb
@@ -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