From 3f6084874bc72108cdf49d70b6280b509ec73ed8 Mon Sep 17 00:00:00 2001 From: Robin Daugherty Date: Sun, 13 Dec 2020 11:36:25 -0500 Subject: [PATCH 1/4] Fix code line highlight not stretching full width --- lib/better_errors/code_formatter/html.rb | 5 ++++- lib/better_errors/style/main.scss | 14 ++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/better_errors/code_formatter/html.rb b/lib/better_errors/code_formatter/html.rb index c20b3aa8..6fc50a92 100644 --- a/lib/better_errors/code_formatter/html.rb +++ b/lib/better_errors/code_formatter/html.rb @@ -20,7 +20,10 @@ def formatted_nums end def formatted_code - %{
#{formatted_nums.join}
#{super}
} + %{ +
#{formatted_nums.join}
+
#{super}
+ } end end end diff --git a/lib/better_errors/style/main.scss b/lib/better_errors/style/main.scss index 41eb9fb4..a26fd79f 100644 --- a/lib/better_errors/style/main.scss +++ b/lib/better_errors/style/main.scss @@ -510,11 +510,17 @@ p.no-javascript-notice { border-top-left-radius:2px; padding: 10px 0; overflow: auto; -} -.code pre{ - padding-left:12px; - min-height:16px; + .code-wrapper { + // This fixes the highlight or other background of the pre not stretching the full scrollable width. + display: inline-block; + min-width: 100%; + } + + pre { + padding-left:12px; + min-height:16px; + } } /* Source unavailable */ From edf17f19289f52ee47739f87bfd3c12478873da9 Mon Sep 17 00:00:00 2001 From: Robin Daugherty Date: Mon, 14 Dec 2020 15:53:54 -0500 Subject: [PATCH 2/4] Replace CodeRay with Rouge --- better_errors.gemspec | 2 +- lib/better_errors.rb | 1 - lib/better_errors/code_formatter.rb | 43 +-- lib/better_errors/code_formatter/html.rb | 13 +- lib/better_errors/style/main.scss | 4 +- .../style/syntax_highlighting.scss | 287 +++++++++--------- .../better_errors/code_formatter/html_spec.rb | 52 ++++ .../better_errors/code_formatter/text_spec.rb | 69 +++++ spec/better_errors/code_formatter_spec.rb | 92 ------ 9 files changed, 302 insertions(+), 261 deletions(-) create mode 100644 spec/better_errors/code_formatter/html_spec.rb create mode 100644 spec/better_errors/code_formatter/text_spec.rb delete mode 100644 spec/better_errors/code_formatter_spec.rb diff --git a/better_errors.gemspec b/better_errors.gemspec index 60a14810..a6cae6d3 100644 --- a/better_errors.gemspec +++ b/better_errors.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |s| # simplecov and coveralls must not be included here. See the Gemfiles instead. s.add_dependency "erubi", ">= 1.0.0" - s.add_dependency "coderay", ">= 1.0.0" + s.add_dependency "rouge", ">= 1.0.0" s.add_dependency "rack", ">= 0.9.0" # optional dependencies: diff --git a/lib/better_errors.rb b/lib/better_errors.rb index 346a1b1d..aff646e1 100644 --- a/lib/better_errors.rb +++ b/lib/better_errors.rb @@ -1,6 +1,5 @@ require "pp" require "erubi" -require "coderay" require "uri" require "better_errors/version" diff --git a/lib/better_errors/code_formatter.rb b/lib/better_errors/code_formatter.rb index a0e0245c..29092af9 100644 --- a/lib/better_errors/code_formatter.rb +++ b/lib/better_errors/code_formatter.rb @@ -4,14 +4,6 @@ class CodeFormatter require "better_errors/code_formatter/html" require "better_errors/code_formatter/text" - FILE_TYPES = { - ".rb" => :ruby, - "" => :ruby, - ".html" => :html, - ".erb" => :erb, - ".haml" => :haml - } - attr_reader :filename, :line, :context def initialize(filename, line, context = 5) @@ -26,13 +18,21 @@ def output source_unavailable end - def formatted_code - formatted_lines.join + def line_range + min = [line - context, 1].max + max = [line + context, source_lines.count].min + min..max end - def coderay_scanner - ext = File.extname(filename) - FILE_TYPES[ext] || :text + def context_lines + range = line_range + source_lines[(range.begin - 1)..(range.end - 1)] or raise Errno::EINVAL + end + + private + + def formatted_code + formatted_lines.join end def each_line_of(lines, &blk) @@ -41,23 +41,12 @@ def each_line_of(lines, &blk) } end - def highlighted_lines - CodeRay.scan(context_lines.join, coderay_scanner).html(css: :class).lines - end - - def context_lines - range = line_range - source_lines[(range.begin - 1)..(range.end - 1)] or raise Errno::EINVAL + def source + @source ||= File.read(filename) end def source_lines - @source_lines ||= File.readlines(filename) - end - - def line_range - min = [line - context, 1].max - max = [line + context, source_lines.count].min - min..max + @source_lines ||= source.lines end end end diff --git a/lib/better_errors/code_formatter/html.rb b/lib/better_errors/code_formatter/html.rb index 6fc50a92..3e94cd4b 100644 --- a/lib/better_errors/code_formatter/html.rb +++ b/lib/better_errors/code_formatter/html.rb @@ -1,3 +1,5 @@ +require "rouge" + module BetterErrors # @private class CodeFormatter::HTML < CodeFormatter @@ -8,7 +10,7 @@ def source_unavailable def formatted_lines each_line_of(highlighted_lines) { |highlight, current_line, str| class_name = highlight ? "highlight" : "" - sprintf '
%s
', class_name, str + sprintf '
%s
', class_name, str } end @@ -25,5 +27,14 @@ def formatted_code
#{super}
} end + + def rouge_lexer + Rouge::Lexer.guess(filename: filename, source: source) { Rouge::Lexers::Ruby } + end + + def highlighted_lines + Rouge::Formatters::HTML.new.format(rouge_lexer.lex(context_lines.join)).lines + end + end end diff --git a/lib/better_errors/style/main.scss b/lib/better_errors/style/main.scss index a26fd79f..61b73d2e 100644 --- a/lib/better_errors/style/main.scss +++ b/lib/better_errors/style/main.scss @@ -726,4 +726,6 @@ nav.sidebar:hover::-webkit-scrollbar-thumb { background: #888; } -@import "syntax_highlighting"; +.code pre { + @import "syntax_highlighting"; +} diff --git a/lib/better_errors/style/syntax_highlighting.scss b/lib/better_errors/style/syntax_highlighting.scss index e24e9be8..5002cd49 100644 --- a/lib/better_errors/style/syntax_highlighting.scss +++ b/lib/better_errors/style/syntax_highlighting.scss @@ -1,139 +1,150 @@ -.CodeRay { -// background-color: #FFF; -// border: 1px solid #CCC; -// font-family: Monaco, "Courier New", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", monospace; - color: #000; -// padding: 1em 0px 1em 1em; - - &span { white-space: pre; border: 0px; padding: 2px } - - &table { - border-collapse: collapse; - width: 100%; - padding: 2px - td { - padding: 1em 0.5em; - vertical-align: top; - } - } - - pre { - margin: 0px; - } - - .line-numbers, .no { - background-color: #ECECEC; - color: #AAA; - text-align: right; - } - - .line-numbers a { - color: #AAA; - } - - .line-numbers tt { font-weight: bold } - .line-numbers .highlighted { color: red } - .line { display: block; float: left; width: 100%; } - span.line-numbers { padding: 0px 4px } - .code { width: 100% } - - &ol { - font-size: 10pt; - li { - white-space: pre - } - } - - .code pre { overflow: auto } - .debug { color:white ! important; background:blue ! important; } - - .annotation { color:#007 } - .attribute-name { color:#f08 } - .attribute-value { color:#700 } - .binary { color:#509; font-weight:bold } - .comment { color:#998; font-style: italic;} - .char { color:#04D } - .char .content { color:#04D } - .char .delimiter { color:#039 } - .class { color:#458; font-weight:bold } - .complex { color:#A08; font-weight:bold } - .constant { color:teal; } - .color { color:#0A0 } - .class-variable { color:#369 } - .decorator { color:#B0B; } - .definition { color:#099; font-weight:bold } - .directive { color:#088; font-weight:bold } - .delimiter { color:black } - .doc { color:#970 } - .doctype { color:#34b } - .doc-string { color:#D42; font-weight:bold } - .escape { color:#666; font-weight:bold } - .entity { color:#800; font-weight:bold } - .error { color:#F00; background-color:#FAA } - .exception { color:#C00; font-weight:bold } - .filename { color:#099; } - .function { color:#900; font-weight:bold } - .global-variable { color:teal; font-weight:bold } - .hex { color:#058; font-weight:bold } - .integer { color:#099; } - .include { color:#B44; font-weight:bold } - .inline { color: black } - .inline .inline { background: #ccc } - .inline .inline .inline { background: #bbb } - .inline .inline-delimiter { color: #D14; } - .inline-delimiter { color: #D14; } - .important { color:#f00; } - .interpreted { color:#B2B; font-weight:bold } - .instance-variable { color:teal } - .label { color:#970; font-weight:bold } - .local-variable { color:#963 } - .octal { color:#40E; font-weight:bold } - .operator { } - .predefined-constant { font-weight:bold } - .predefined { color:#369; font-weight:bold } - .preprocessor { color:#579; } - .pseudo-class { color:#00C; font-weight:bold } - .predefined-type { color:#074; font-weight:bold } - .reserved, .keyword { color:#000; font-weight:bold } - - .key { color: #808; } - .key .delimiter { color: #606; } - .key .char { color: #80f; } - .value { color: #088; } - - .regexp { background-color:#fff0ff } - .regexp .content { color:#808 } - .regexp .delimiter { color:#404 } - .regexp .modifier { color:#C2C } - .regexp .function { color:#404; font-weight: bold } - - .string { color: #D20; } - .string .string { } - .string .string .string { background-color:#ffd0d0 } - .string .content { color: #D14; } - .string .char { color: #D14; } - .string .delimiter { color: #D14; } - - .shell { color:#D14 } - .shell .content { } - .shell .delimiter { color:#D14 } - - .symbol { color:#990073 } - .symbol .content { color:#A60 } - .symbol .delimiter { color:#630 } - - .tag { color:#070 } - .tag-special { color:#D70; font-weight:bold } - .type { color:#339; font-weight:bold } - .variable { color:#036 } - - .insert { background: #afa; } - .delete { background: #faa; } - .change { color: #aaf; background: #007; } - .head { color: #f8f; background: #505 } - - .insert .insert { color: #080; font-weight:bold } - .delete .delete { color: #800; font-weight:bold } - .change .change { color: #66f; } - .head .head { color: #f4f; } +// rougify style pastie --scope '' +// (And remove the first couple of selectors.) + .c, .ch, .cd, .cm, .cpf, .c1 { + color: #888888; +} + .cp { + color: #cc0000; + font-weight: bold; +} + .cs { + color: #cc0000; + background-color: #fff0f0; + font-weight: bold; +} + .err { + color: #a61717; + background-color: #e3d2d2; +} + .gr { + color: #aa0000; +} + .gh { + color: #333333; +} + .gu { + color: #666666; +} + .gd { + color: #000000; + background-color: #ffdddd; +} + .gi { + color: #000000; + background-color: #ddffdd; +} + .ge { + font-style: italic; +} + .gs { + font-weight: bold; +} + .gl { + color: #888888; +} + .go { + color: #888888; +} + .gp { + color: #555555; +} + .gt { + color: #aa0000; +} + .k, .kc, .kd, .kn, .kr, .kv { + color: #008800; + font-weight: bold; +} + .kp { + color: #008800; +} + .kt { + color: #888888; + font-weight: bold; +} + .m, .mb, .mf, .mh, .mi, .il, .mo, .mx { + color: #0000dd; + font-weight: bold; +} + .s, .sb, .sc, .dl, .sd, .s2, .sh, .s1 { + color: #dd2200; + background-color: #fff0f0; +} + .sa { + color: #008800; + font-weight: bold; +} + .se { + color: #0044dd; + background-color: #fff0f0; +} + .si { + color: #3333bb; + background-color: #fff0f0; +} + .sx { + color: #22bb22; + background-color: #f0fff0; +} + .sr { + color: #008800; +} + .ss { + color: #aa6600; + background-color: #fff0f0; +} + .na { + color: #336699; +} + .nb, .bp { + color: #003388; +} + .nc { + color: #bb0066; + font-weight: bold; +} + .no { + color: #003366; + font-weight: bold; +} + .nd { + color: #555555; +} + .ne { + color: #bb0066; + font-weight: bold; +} + .nf, .fm { + color: #0066bb; + font-weight: bold; +} + .nl { + color: #336699; +} + .nn { + color: #bb0066; + font-weight: bold; +} + .py { + color: #336699; + font-weight: bold; +} + .nt { + color: #bb0066; + font-weight: bold; +} + .nv, .vc, .vm { + color: #336699; +} + .vg { + color: #dd7700; +} + .vi { + color: #3333bb; +} + .ow { + color: #008800; +} + .w { + color: #bbbbbb; } diff --git a/spec/better_errors/code_formatter/html_spec.rb b/spec/better_errors/code_formatter/html_spec.rb new file mode 100644 index 00000000..406a9394 --- /dev/null +++ b/spec/better_errors/code_formatter/html_spec.rb @@ -0,0 +1,52 @@ +require "spec_helper" + +RSpec.describe BetterErrors::CodeFormatter::HTML do + let(:filename) { File.expand_path("../../support/my_source.rb", __FILE__) } + let(:line) { 8 } + let(:formatter) { described_class.new(filename, line) } + + it "shows 5 lines of context above and below the line" do + expect(formatter.line_range).to eq(3..13) + + expect(formatter.context_lines).to eq([ + "three\n", + "four\n", + "five\n", + "six\n", + "seven\n", + "eight\n", + "nine\n", + "ten\n", + "eleven\n", + "twelve\n", + "thirteen\n" + ]) + end + + context 'when the line is right at the end of the file' do + let(:line) { 20 } + it "ends on the line" do + expect(formatter.line_range).to eq(15..20) + end + end + + it "highlights the erroring line" do + formatter = described_class.new(filename, 8) + expect(formatter.output).to match(/highlight.*eight/) + end + + it "works when the line is right on the edge" do + formatter = described_class.new(filename, 20) + expect(formatter.output).not_to eq(formatter.source_unavailable) + end + + it "doesn't barf when the lines don't make any sense" do + formatter = described_class.new(filename, 999) + expect(formatter.output).to eq(formatter.source_unavailable) + end + + it "doesn't barf when the file doesn't exist" do + formatter = described_class.new("fkdguhskd7e l", 1) + expect(formatter.output).to eq(formatter.source_unavailable) + end +end diff --git a/spec/better_errors/code_formatter/text_spec.rb b/spec/better_errors/code_formatter/text_spec.rb new file mode 100644 index 00000000..a691301a --- /dev/null +++ b/spec/better_errors/code_formatter/text_spec.rb @@ -0,0 +1,69 @@ +require "spec_helper" + +RSpec.describe BetterErrors::CodeFormatter::Text do + let(:filename) { File.expand_path("../../support/my_source.rb", __FILE__) } + let(:line) { 8 } + let(:formatter) { described_class.new(filename, line) } + + it "shows 5 lines of context" do + expect(formatter.line_range).to eq(3..13) + + expect(formatter.context_lines).to eq([ + "three\n", + "four\n", + "five\n", + "six\n", + "seven\n", + "eight\n", + "nine\n", + "ten\n", + "eleven\n", + "twelve\n", + "thirteen\n" + ]) + end + + context 'when the line is right at the end of the file' do + let(:line) { 20 } + + it "ends on the line" do + expect(formatter.line_range).to eq(15..20) + end + end + + describe '#output' do + subject(:output) { formatter.output } + + it "highlights the erroring line" do + expect(output).to eq <<-TEXT.gsub(/^ /, "") + 3 three + 4 four + 5 five + 6 six + 7 seven + > 8 eight + 9 nine + 10 ten + 11 eleven + 12 twelve + 13 thirteen + TEXT + end + + context 'when the line is outside the file' do + let(:line) { 999 } + + it "returns the 'source unavailable' message" do + expect(output).to eq(formatter.source_unavailable) + end + end + + context 'when the the file path is not valid' do + let(:filename) { "fkdguhskd7e l" } + + it "returns the 'source unavailable' message" do + expect(output).to eq(formatter.source_unavailable) + end + end + end +end diff --git a/spec/better_errors/code_formatter_spec.rb b/spec/better_errors/code_formatter_spec.rb deleted file mode 100644 index 5b612de6..00000000 --- a/spec/better_errors/code_formatter_spec.rb +++ /dev/null @@ -1,92 +0,0 @@ -require "spec_helper" - -module BetterErrors - describe CodeFormatter do - let(:filename) { File.expand_path("../support/my_source.rb", __FILE__) } - - let(:formatter) { CodeFormatter.new(filename, 8) } - - it "picks an appropriate scanner" do - expect(formatter.coderay_scanner).to eq(:ruby) - end - - it "shows 5 lines of context" do - expect(formatter.line_range).to eq(3..13) - - expect(formatter.context_lines).to eq([ - "three\n", - "four\n", - "five\n", - "six\n", - "seven\n", - "eight\n", - "nine\n", - "ten\n", - "eleven\n", - "twelve\n", - "thirteen\n" - ]) - end - - it "works when the line is right on the edge" do - formatter = CodeFormatter.new(filename, 20) - expect(formatter.line_range).to eq(15..20) - end - - describe CodeFormatter::HTML do - it "highlights the erroring line" do - formatter = CodeFormatter::HTML.new(filename, 8) - expect(formatter.output).to match(/highlight.*eight/) - end - - it "works when the line is right on the edge" do - formatter = CodeFormatter::HTML.new(filename, 20) - expect(formatter.output).not_to eq(formatter.source_unavailable) - end - - it "doesn't barf when the lines don't make any sense" do - formatter = CodeFormatter::HTML.new(filename, 999) - expect(formatter.output).to eq(formatter.source_unavailable) - end - - it "doesn't barf when the file doesn't exist" do - formatter = CodeFormatter::HTML.new("fkdguhskd7e l", 1) - expect(formatter.output).to eq(formatter.source_unavailable) - end - end - - describe CodeFormatter::Text do - it "highlights the erroring line" do - formatter = CodeFormatter::Text.new(filename, 8) - expect(formatter.output).to eq <<-TEXT.gsub(/^ /, "") - 3 three - 4 four - 5 five - 6 six - 7 seven - > 8 eight - 9 nine - 10 ten - 11 eleven - 12 twelve - 13 thirteen - TEXT - end - - it "works when the line is right on the edge" do - formatter = CodeFormatter::Text.new(filename, 20) - expect(formatter.output).not_to eq(formatter.source_unavailable) - end - - it "doesn't barf when the lines don't make any sense" do - formatter = CodeFormatter::Text.new(filename, 999) - expect(formatter.output).to eq(formatter.source_unavailable) - end - - it "doesn't barf when the file doesn't exist" do - formatter = CodeFormatter::Text.new("fkdguhskd7e l", 1) - expect(formatter.output).to eq(formatter.source_unavailable) - end - end - end -end From 24e9eca488e6d24e783123127a4cdd319d6c6c45 Mon Sep 17 00:00:00 2001 From: Robin Daugherty Date: Sun, 13 Dec 2020 14:19:24 -0500 Subject: [PATCH 3/4] Solarized light and dark themes and fix dark theme being affected by light theme --- lib/better_errors/style/main.scss | 34 ++- .../style/syntax_highlighting.scss | 231 ++++++------------ .../style/syntax_highlighting_dark.scss | 84 +++++++ 3 files changed, 186 insertions(+), 163 deletions(-) create mode 100644 lib/better_errors/style/syntax_highlighting_dark.scss diff --git a/lib/better_errors/style/main.scss b/lib/better_errors/style/main.scss index 61b73d2e..af2e0f8a 100644 --- a/lib/better_errors/style/main.scss +++ b/lib/better_errors/style/main.scss @@ -487,12 +487,14 @@ p.no-javascript-notice { } .code, .be-console, .unavailable { - background: #fff; padding: 5px; - box-shadow: inset 3px 3px 3px rgba(0, 0, 0, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1); } +.code, .unavailable { + text-shadow: none; +} + .code_linenums{ background:#f1f1f1; padding-top:10px; @@ -506,6 +508,12 @@ p.no-javascript-notice { } .code { + @import "syntax_highlighting"; + + @media (prefers-color-scheme: dark) { + @import "syntax_highlighting_dark"; + } + margin-bottom: -1px; border-top-left-radius:2px; padding: 10px 0; @@ -543,21 +551,24 @@ p.unavailable:before { margin-bottom: -10px; } +$code-highlight-background: rgba(51, 136, 170, 0.15); +$code-highlight-background-flash: adjust-color($code-highlight-background, $alpha: 0.3); + @-webkit-keyframes highlight { - 0% { background: rgba(220, 30, 30, 0.3); } - 100% { background: rgba(220, 30, 30, 0.1); } + 0% { background: $code-highlight-background-flash; } + 100% { background: $code-highlight-background; } } @-moz-keyframes highlight { - 0% { background: rgba(220, 30, 30, 0.3); } - 100% { background: rgba(220, 30, 30, 0.1); } + 0% { background: $code-highlight-background-flash; } + 100% { background: $code-highlight-background; } } @keyframes highlight { - 0% { background: rgba(220, 30, 30, 0.3); } - 100% { background: rgba(220, 30, 30, 0.1); } + 0% { background: $code-highlight-background-flash; } + 100% { background: $code-highlight-background; } } .code .highlight, .code_linenums .highlight { - background: rgba(220, 30, 30, 0.1); + background: $code-highlight-background; -webkit-animation: highlight 400ms linear 1; -moz-animation: highlight 400ms linear 1; animation: highlight 400ms linear 1; @@ -565,6 +576,7 @@ p.unavailable:before { /* REPL shell */ .be-console { + background: #fff; padding: 0 1px 10px 1px; border-bottom-left-radius: 2px; border-bottom-right-radius: 2px; @@ -725,7 +737,3 @@ nav.sidebar:hover::-webkit-scrollbar-thumb { .code:hover::-webkit-scrollbar-thumb { background: #888; } - -.code pre { - @import "syntax_highlighting"; -} diff --git a/lib/better_errors/style/syntax_highlighting.scss b/lib/better_errors/style/syntax_highlighting.scss index 5002cd49..894a91e9 100644 --- a/lib/better_errors/style/syntax_highlighting.scss +++ b/lib/better_errors/style/syntax_highlighting.scss @@ -1,150 +1,81 @@ -// rougify style pastie --scope '' -// (And remove the first couple of selectors.) - .c, .ch, .cd, .cm, .cpf, .c1 { - color: #888888; -} - .cp { - color: #cc0000; - font-weight: bold; -} - .cs { - color: #cc0000; - background-color: #fff0f0; - font-weight: bold; -} - .err { - color: #a61717; - background-color: #e3d2d2; -} - .gr { - color: #aa0000; -} - .gh { - color: #333333; -} - .gu { - color: #666666; -} - .gd { - color: #000000; - background-color: #ffdddd; -} - .gi { - color: #000000; - background-color: #ddffdd; -} - .ge { - font-style: italic; -} - .gs { - font-weight: bold; -} - .gl { - color: #888888; -} - .go { - color: #888888; -} - .gp { - color: #555555; -} - .gt { - color: #aa0000; -} - .k, .kc, .kd, .kn, .kr, .kv { - color: #008800; - font-weight: bold; -} - .kp { - color: #008800; -} - .kt { - color: #888888; - font-weight: bold; -} - .m, .mb, .mf, .mh, .mi, .il, .mo, .mx { - color: #0000dd; - font-weight: bold; -} - .s, .sb, .sc, .dl, .sd, .s2, .sh, .s1 { - color: #dd2200; - background-color: #fff0f0; -} - .sa { - color: #008800; - font-weight: bold; -} - .se { - color: #0044dd; - background-color: #fff0f0; -} - .si { - color: #3333bb; - background-color: #fff0f0; -} - .sx { - color: #22bb22; - background-color: #f0fff0; -} - .sr { - color: #008800; -} - .ss { - color: #aa6600; - background-color: #fff0f0; -} - .na { - color: #336699; -} - .nb, .bp { - color: #003388; -} - .nc { - color: #bb0066; - font-weight: bold; -} - .no { - color: #003366; - font-weight: bold; -} - .nd { - color: #555555; -} - .ne { - color: #bb0066; - font-weight: bold; -} - .nf, .fm { - color: #0066bb; - font-weight: bold; -} - .nl { - color: #336699; -} - .nn { - color: #bb0066; - font-weight: bold; -} - .py { - color: #336699; - font-weight: bold; -} - .nt { - color: #bb0066; - font-weight: bold; -} - .nv, .vc, .vm { - color: #336699; -} - .vg { - color: #dd7700; -} - .vi { - color: #3333bb; -} - .ow { - color: #008800; -} - .w { - color: #bbbbbb; -} +// Thanks to https://github.com/jwarby/jekyll-pygments-themes/blob/master/pastie.css + +$base01: #586e75; +$base1: #93a1a1; +$base3: #fdf6e3; +$orange: #cb4b16; +$red: #dc322f; +$blue: #268bd2; +$cyan: #2aa198; +$green: #859900; +$yellow: #B58900; + +& { background-color: $base3; color: $base01 } +.c { color: $base1 } /* Comment */ +.err { color: $base01 } /* Error */ +.g { color: $base01 } /* Generic */ +.k { color: $green } /* Keyword */ +.l { color: $base01 } /* Literal */ +.n { color: $base01 } /* Name */ +.o { color: $green } /* Operator */ +.x { color: $orange } /* Other */ +.p { color: $base01 } /* Punctuation */ +.cm { color: $base1 } /* Comment.Multiline */ +.cp { color: $green } /* Comment.Preproc */ +.c1 { color: $base1 } /* Comment.Single */ +.cs { color: $green } /* Comment.Special */ +.gd { color: $cyan } /* Generic.Deleted */ +.ge { color: $base01; font-style: italic } /* Generic.Emph */ +.gr { color: $red } /* Generic.Error */ +.gh { color: $orange } /* Generic.Heading */ +.gi { color: $green } /* Generic.Inserted */ +.go { color: $base01 } /* Generic.Output */ +.gp { color: $base01 } /* Generic.Prompt */ +.gs { color: $base01; font-weight: bold } /* Generic.Strong */ +.gu { color: $orange } /* Generic.Subheading */ +.gt { color: $base01 } /* Generic.Traceback */ +.kc { color: $orange } /* Keyword.Constant */ +.kd { color: $blue } /* Keyword.Declaration */ +.kn { color: $green } /* Keyword.Namespace */ +.kp { color: $green } /* Keyword.Pseudo */ +.kr { color: $blue } /* Keyword.Reserved */ +.kt { color: $red } /* Keyword.Type */ +.ld { color: $base01 } /* Literal.Date */ +.m { color: $cyan } /* Literal.Number */ +.s { color: $cyan } /* Literal.String */ +.na { color: $base01 } /* Name.Attribute */ +.nb { color: $yellow } /* Name.Builtin */ +.nc { color: $blue } /* Name.Class */ +.no { color: $orange } /* Name.Constant */ +.nd { color: $blue } /* Name.Decorator */ +.ni { color: $orange } /* Name.Entity */ +.ne { color: $orange } /* Name.Exception */ +.nf { color: $blue } /* Name.Function */ +.nl { color: $base01 } /* Name.Label */ +.nn { color: $base01 } /* Name.Namespace */ +.nx { color: $base01 } /* Name.Other */ +.py { color: $base01 } /* Name.Property */ +.nt { color: $blue } /* Name.Tag */ +.nv { color: $blue } /* Name.Variable */ +.ow { color: $green } /* Operator.Word */ +.w { color: $base01 } /* Text.Whitespace */ +.mf { color: $cyan } /* Literal.Number.Float */ +.mh { color: $cyan } /* Literal.Number.Hex */ +.mi { color: $cyan } /* Literal.Number.Integer */ +.mo { color: $cyan } /* Literal.Number.Oct */ +.sb { color: $base1 } /* Literal.String.Backtick */ +.sc { color: $cyan } /* Literal.String.Char */ +.sd { color: $base01 } /* Literal.String.Doc */ +.s2 { color: $cyan } /* Literal.String.Double */ +.se { color: $orange } /* Literal.String.Escape */ +.sh { color: $base01 } /* Literal.String.Heredoc */ +.si { color: $cyan } /* Literal.String.Interpol */ +.sx { color: $cyan } /* Literal.String.Other */ +.sr { color: $red } /* Literal.String.Regex */ +.s1 { color: $cyan } /* Literal.String.Single */ +.ss { color: $cyan } /* Literal.String.Symbol */ +.bp { color: $blue } /* Name.Builtin.Pseudo */ +.vc { color: $blue } /* Name.Variable.Class */ +.vg { color: $blue } /* Name.Variable.Global */ +.vi { color: $blue } /* Name.Variable.Instance */ +.il { color: $cyan } /* Literal.Number.Integer.Long */ diff --git a/lib/better_errors/style/syntax_highlighting_dark.scss b/lib/better_errors/style/syntax_highlighting_dark.scss new file mode 100644 index 00000000..664d30db --- /dev/null +++ b/lib/better_errors/style/syntax_highlighting_dark.scss @@ -0,0 +1,84 @@ +// Thanks to https://gist.github.com/nicolashery/5765395 + +// Since the light theme is applied before the dark theme, the dark theme is additive. +// So we must set the background color and font weight on everything that the light theme might have changed. + +$base03: #002b36; +$base01: #586e75; +$base1: #93a1a1; +$orange: #cb4b16; +$red: #dc322f; +$blue: #268bd2; +$cyan: #2aa198; +$green: #859900; +$yellow: #B58900; + +& { background-color: $base03; color: $base1; } +.c { color: $base01; background-color: transparent; font-style: inherit; } /* Comment */ +.err { color: $base1; background-color: transparent; font-style: inherit; } /* Error */ +.g { color: $base1; background-color: transparent; font-style: inherit; } /* Generic */ +.k { color: $green; background-color: transparent; font-style: inherit; } /* Keyword */ +.l { color: $base1; background-color: transparent; font-style: inherit; } /* Literal */ +.n { color: $base1; background-color: transparent; font-style: inherit; } /* Name */ +.o { color: $green; background-color: transparent; font-style: inherit; } /* Operator */ +.x { color: $orange; background-color: transparent; font-style: inherit; } /* Other */ +.p { color: $base1; background-color: transparent; font-style: inherit; } /* Punctuation */ +.cm { color: $base01; background-color: transparent; font-style: inherit; } /* Comment.Multiline */ +.cp { color: $green; background-color: transparent; font-style: inherit; } /* Comment.Preproc */ +.c1 { color: $base01; background-color: transparent; font-style: inherit; } /* Comment.Single */ +.cs { color: $green; background-color: transparent; font-style: inherit; } /* Comment.Special */ +.gd { color: $cyan; background-color: transparent; font-style: inherit; } /* Generic.Deleted */ +.ge { color: $base1; background-color: transparent; font-style: italic; } /* Generic.Emph */ +.gr { color: $red; background-color: transparent; font-style: inherit; } /* Generic.Error */ +.gh { color: $orange; background-color: transparent; font-style: inherit; } /* Generic.Heading */ +.gi { color: $green; background-color: transparent; font-style: inherit; } /* Generic.Inserted */ +.go { color: $base1; background-color: transparent; font-style: inherit; } /* Generic.Output */ +.gp { color: $base1; background-color: transparent; font-style: inherit; } /* Generic.Prompt */ +.gs { color: $base1; background-color: transparent; font-weight: bold; } /* Generic.Strong */ +.gu { color: $orange; background-color: transparent; font-style: inherit; } /* Generic.Subheading */ +.gt { color: $base1; background-color: transparent; font-style: inherit; } /* Generic.Traceback */ +.kc { color: $orange; background-color: transparent; font-style: inherit; } /* Keyword.Constant */ +.kd { color: $blue; background-color: transparent; font-style: inherit; } /* Keyword.Declaration */ +.kn { color: $green; background-color: transparent; font-style: inherit; } /* Keyword.Namespace */ +.kp { color: $green; background-color: transparent; font-style: inherit; } /* Keyword.Pseudo */ +.kr { color: $blue; background-color: transparent; font-style: inherit; } /* Keyword.Reserved */ +.kt { color: $red; background-color: transparent; font-style: inherit; } /* Keyword.Type */ +.ld { color: $base1; background-color: transparent; font-style: inherit; } /* Literal.Date */ +.m { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.Number */ +.s { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.String */ +.na { color: $base1; background-color: transparent; font-style: inherit; } /* Name.Attribute */ +.nb { color: $yellow; background-color: transparent; font-style: inherit; } /* Name.Builtin */ +.nc { color: $blue; background-color: transparent; font-style: inherit; } /* Name.Class */ +.no { color: $orange; background-color: transparent; font-style: inherit; } /* Name.Constant */ +.nd { color: $blue; background-color: transparent; font-style: inherit; } /* Name.Decorator */ +.ni { color: $orange; background-color: transparent; font-style: inherit; } /* Name.Entity */ +.ne { color: $orange; background-color: transparent; font-style: inherit; } /* Name.Exception */ +.nf { color: $blue; background-color: transparent; font-style: inherit; } /* Name.Function */ +.nl { color: $base1; background-color: transparent; font-style: inherit; } /* Name.Label */ +.nn { color: $base1; background-color: transparent; font-style: inherit; } /* Name.Namespace */ +.nx { color: $base1; background-color: transparent; font-style: inherit; } /* Name.Other */ +.py { color: $base1; background-color: transparent; font-style: inherit; } /* Name.Property */ +.nt { color: $blue; background-color: transparent; font-style: inherit; } /* Name.Tag */ +.nv { color: $blue; background-color: transparent; font-style: inherit; } /* Name.Variable */ +.ow { color: $green; background-color: transparent; font-style: inherit; } /* Operator.Word */ +.w { color: $base1; background-color: transparent; font-style: inherit; } /* Text.Whitespace */ +.mf { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.Number.Float */ +.mh { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.Number.Hex */ +.mi { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.Number.Integer */ +.mo { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.Number.Oct */ +.sb { color: $base01; background-color: transparent; font-style: inherit; } /* Literal.String.Backtick */ +.sc { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.String.Char */ +.sd { color: $base1; background-color: transparent; font-style: inherit; } /* Literal.String.Doc */ +.s2 { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.String.Double */ +.se { color: $orange; background-color: transparent; font-style: inherit; } /* Literal.String.Escape */ +.sh { color: $base1; background-color: transparent; font-style: inherit; } /* Literal.String.Heredoc */ +.si { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.String.Interpol */ +.sx { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.String.Other */ +.sr { color: $red; background-color: transparent; font-style: inherit; } /* Literal.String.Regex */ +.s1 { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.String.Single */ +.ss { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.String.Symbol */ +.bp { color: $blue; background-color: transparent; font-style: inherit; } /* Name.Builtin.Pseudo */ +.vc { color: $blue; background-color: transparent; font-style: inherit; } /* Name.Variable.Class */ +.vg { color: $blue; background-color: transparent; font-style: inherit; } /* Name.Variable.Global */ +.vi { color: $blue; background-color: transparent; font-style: inherit; } /* Name.Variable.Instance */ +.il { color: $cyan; background-color: transparent; font-style: inherit; } /* Literal.Number.Integer.Long */ From 7edd3a25823706fe5601bd4e04f8ebe8b2110aca Mon Sep 17 00:00:00 2001 From: Robin Daugherty Date: Tue, 15 Dec 2020 09:17:54 -0500 Subject: [PATCH 4/4] Fix syntax highlighting of console commands --- lib/better_errors/error_page.rb | 3 ++- lib/better_errors/style/main.scss | 11 +++++++++-- lib/better_errors/templates/main.erb | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/better_errors/error_page.rb b/lib/better_errors/error_page.rb index 7d5e08af..afb28f0d 100644 --- a/lib/better_errors/error_page.rb +++ b/lib/better_errors/error_page.rb @@ -1,6 +1,7 @@ require "cgi" require "json" require "securerandom" +require "rouge" require "better_errors/error_page_style" module BetterErrors @@ -158,7 +159,7 @@ def eval_and_respond(index, code) result, prompt, prefilled_input = @repls[index].send_input(code) { - highlighted_input: CodeRay.scan(code, :ruby).div(wrap: nil), + highlighted_input: Rouge::Formatters::HTML.new.format(Rouge::Lexers::Ruby.lex(code)), prefilled_input: prefilled_input, prompt: prompt, result: result diff --git a/lib/better_errors/style/main.scss b/lib/better_errors/style/main.scss index af2e0f8a..db0d7d1e 100644 --- a/lib/better_errors/style/main.scss +++ b/lib/better_errors/style/main.scss @@ -507,13 +507,20 @@ p.no-javascript-notice { padding:0 12px; } -.code { - @import "syntax_highlighting"; +.code, .be-console .syntax-highlighted { + text-shadow: none; + @import "syntax_highlighting"; +} +.code { + // For now, the syntax-highlighted console only supports light mode. + // Once the entire page has a dark theme, this should change. @media (prefers-color-scheme: dark) { @import "syntax_highlighting_dark"; } +} +.code { margin-bottom: -1px; border-top-left-radius:2px; padding: 10px 0; diff --git a/lib/better_errors/templates/main.erb b/lib/better_errors/templates/main.erb index 7829c568..37dc30de 100644 --- a/lib/better_errors/templates/main.erb +++ b/lib/better_errors/templates/main.erb @@ -249,7 +249,7 @@ self.writeOutput(response.error + "\n"); } self.writeOutput(self._prompt + " "); - self.writeRawOutput(response.highlighted_input + "\n"); + self.writeRawOutput("" + response.highlighted_input + "\n"); self.writeOutput(response.result); self.setPrompt(response.prompt); self.setInput(response.prefilled_input);