diff --git a/.gitignore b/.gitignore index d3f6a852..ab515301 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ /gemfiles/*.gemfile.lock /gemfiles/.bundle + +# This is the debug version of the file and should not be committed. +lib/better_errors/templates/*.development.css diff --git a/Rakefile b/Rakefile index cb84ee87..be793e04 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,6 @@ require "bundler/gem_tasks" require "rspec/core/rake_task" +require "sassc" RSpec::Core::RakeTask.new(:test) task :default => :test @@ -36,3 +37,42 @@ namespace :test do with_each_gemfile { sh "bundle exec rspec" rescue nil } end end + +namespace :style do + desc "Build main.development.css (overrides main.css)" + task :develop => [:build] do + root_dir = File.dirname(__FILE__) + style_dir = "#{root_dir}/style" + output_dir = "#{root_dir}/lib/better_errors/templates" + + engine = SassC::Engine.new( + File.read("#{style_dir}/main.scss"), + filename: "#{style_dir}/main.scss", + style: :expanded, + line_comments: true, + load_paths: [style_dir], + ) + css = engine.render + File.open("#{output_dir}/main.development.css", "w") do |f| + f.write(css) + end + end + + desc "Build main.css from the SASS sources" + task :build do + root_dir = File.dirname(__FILE__) + style_dir = "#{root_dir}/style" + output_dir = "#{root_dir}/lib/better_errors/templates" + + engine = SassC::Engine.new( + File.read("#{style_dir}/main.scss"), + filename: "#{style_dir}/main.scss", + style: :compressed, + load_paths: [style_dir], + ) + css = engine.render + File.open("#{output_dir}/main.css", "w") do |f| + f.write(css) + end + end +end diff --git a/better_errors.gemspec b/better_errors.gemspec index ef996b0d..f8375e0b 100644 --- a/better_errors.gemspec +++ b/better_errors.gemspec @@ -25,6 +25,7 @@ Gem::Specification.new do |s| s.add_development_dependency "rspec-html-matchers" s.add_development_dependency "rspec-its" s.add_development_dependency "yard" + s.add_development_dependency "sassc" # kramdown 2.1 requires Ruby 2.3+ s.add_development_dependency "kramdown", (RUBY_VERSION < '2.3' ? '< 2.0.0' : '> 2.0.0') # simplecov and coveralls must not be included here. See the Gemfiles instead. diff --git a/lib/better_errors/code_formatter/html.rb b/lib/better_errors/code_formatter/html.rb index ec96a214..955e3fe4 100644 --- a/lib/better_errors/code_formatter/html.rb +++ b/lib/better_errors/code_formatter/html.rb @@ -2,7 +2,7 @@ module BetterErrors # @private class CodeFormatter::HTML < CodeFormatter def source_unavailable - "
Source is not available
" + "Source is not available
" end def formatted_lines @@ -15,7 +15,7 @@ def formatted_lines def formatted_nums each_line_of(highlighted_lines) { |highlight, current_line, str| class_name = highlight ? "highlight" : "" - sprintf '%5d', class_name, current_line + sprintf '%4d', class_name, current_line } end diff --git a/lib/better_errors/error_page.rb b/lib/better_errors/error_page.rb index e5826045..148db785 100644 --- a/lib/better_errors/error_page.rb +++ b/lib/better_errors/error_page.rb @@ -118,7 +118,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: CodeRay.scan(code, :ruby).html(wrap: :span), prefilled_input: prefilled_input, prompt: prompt, result: result diff --git a/lib/better_errors/repl/basic.rb b/lib/better_errors/repl/basic.rb index c2144531..3e4f0106 100644 --- a/lib/better_errors/repl/basic.rb +++ b/lib/better_errors/repl/basic.rb @@ -11,7 +11,7 @@ def send_input(str) private def execute(str) - "=> #{@binding.eval(str).inspect}\n" + "=> #{CGI.escapeHTML(@binding.eval(str).inspect)}\n" rescue Exception => e "!! #{e.inspect rescue e.class.to_s rescue "Exception"}\n" end diff --git a/lib/better_errors/templates/main.css b/lib/better_errors/templates/main.css new file mode 100644 index 00000000..5a61bbb2 --- /dev/null +++ b/lib/better_errors/templates/main.css @@ -0,0 +1 @@ +.CodeRay .debug{color:#fff;background:#00f}.CodeRay .annotation{color:#586E75}.CodeRay .attribute-name{color:#93A1A1}.CodeRay .attribute-value{color:#93A1A1}.CodeRay .binary{color:#509}.CodeRay .char .content{color:#d20}.CodeRay .char .delimiter{color:#710}.CodeRay .char{color:#2AA198}.CodeRay .class{color:#268BD2;font-weight:bold}.CodeRay .class-variable{color:#268BD2}.CodeRay .color{color:#eee8d5}.CodeRay .comment{color:#586E75}.CodeRay .comment .char{color:#859900}.CodeRay .comment .delimiter{color:#859900}.CodeRay .complex{color:#a08}.CodeRay .constant{color:#B58900;font-weight:bold}.CodeRay .decorator{color:#268BD2}.CodeRay .definition{color:#099;font-weight:bold}.CodeRay .delimiter{color:#000}.CodeRay .directive{color:#088;font-weight:bold}.CodeRay .doc{color:#93A1A1}.CodeRay .doc-string{color:#93A1A1;font-weight:bold}.CodeRay .doctype{color:#DC322F}.CodeRay .entity{color:#CB4B16;font-weight:bold}.CodeRay .error{color:#93A1A1;background-color:#faa}.CodeRay .escape{color:#CB4B16}.CodeRay .exception{color:#CB4B16;font-weight:bold}.CodeRay .float{color:#2AA198}.CodeRay .function{color:#268BD2;font-weight:bold}.CodeRay .global-variable{color:#268BD2}.CodeRay .hex{color:#2AA198}.CodeRay .imaginary{color:#f00}.CodeRay .include{color:#b44;font-weight:bold}.CodeRay .inline{background-color:transparent;color:#93A1A1 !important}.CodeRay .inline-delimiter{font-weight:bold;color:#DC322F}.CodeRay .instance-variable{color:#268BD2}.CodeRay .integer{color:#2AA198}.CodeRay .key .char{color:#DC322F}.CodeRay .key .delimiter{color:#268BD2}.CodeRay .key{color:#859900}.CodeRay .keyword{color:#859900;font-weight:bold}.CodeRay .label{color:#93A1A1;font-weight:bold}.CodeRay .local-variable{color:#268BD2}.CodeRay .namespace{color:#859900;font-weight:bold}.CodeRay .octal{color:#2AA198}.CodeRay .operator,.CodeRay .predefined{color:#859900;font-weight:bold}.CodeRay .predefined-constant{color:#2AA198}.CodeRay .predefined-type{color:#DC322F;font-weight:bold}.CodeRay .preprocessor{color:#859900}.CodeRay .pseudo-class{color:#859900;font-weight:bold}.CodeRay .regexp .content{color:#2AA198}.CodeRay .regexp .delimiter{color:#DC322F}.CodeRay .regexp .modifier{color:#CB4B16}.CodeRay .regexp{background-color:transparent}.CodeRay .reserved{color:#268BD2;font-weight:bold}.CodeRay .shell .content{color:#2b2}.CodeRay .shell .delimiter{color:#161}.CodeRay .shell{background-color:transparent}.CodeRay .string .char{color:#2AA198}.CodeRay .string .content{color:#2AA198}.CodeRay .string .delimiter{color:#DC322F}.CodeRay .string .modifier{color:#2AA198}.CodeRay .string{background-color:transparent}.CodeRay .symbol .content{color:#2AA198}.CodeRay .symbol .delimiter{color:#2AA198}.CodeRay .symbol{color:#2AA198}.CodeRay .tag{color:#268BD2}.CodeRay .type{color:#DC322F;font-weight:bold}.CodeRay .value{color:#268BD2}.CodeRay .variable{color:#268BD2}.CodeRay .insert{background:transparent}.CodeRay .delete{background:transparent}.CodeRay .change{color:#CB4B16;background:transparent}.CodeRay .head{color:#CB4B16;background:transparent}.CodeRay .head .filename{color:#CB4B16}.CodeRay .delete .eyecatcher{background-color:rgba(255,0,0,0.2);border:1px solid rgba(230,0,0,0.5);margin:-1px;border-bottom:none;border-top-left-radius:5px;border-top-right-radius:5px}.CodeRay .insert .eyecatcher{background-color:rgba(0,255,0,0.2);border:1px solid rgba(0,128,0,0.5);margin:-1px;border-top:none;border-bottom-left-radius:5px;border-bottom-right-radius:5px}.CodeRay .insert .insert{color:#CB4B16;background:transparent;font-weight:bold}.CodeRay .delete .delete{color:#2AA198;background:transparent;font-weight:bold}.CodeRay .change .change{color:#CB4B16}.CodeRay .head .head{color:#CB4B16}*{margin:0;padding:0}table{width:100%;border-collapse:collapse}th,td{vertical-align:top;text-align:left}textarea{resize:none}body{font-size:10pt}body,td,input,textarea{font-family:helvetica neue, lucida grande, sans-serif;line-height:1.5;color:#333}html{background:white}.clearfix::after,.trace_info .code_block::after,.trace_info::after{clear:both;content:".";display:block;height:0;visibility:hidden}@media screen and (max-width: 1100px){html{overflow-y:scroll}body{margin:0 20px}header.exception{margin:0 -20px}nav.frames{padding:0;margin:20px 0}ul.frames{max-height:200px}}@media screen and (min-width: 1100px){header.exception{position:fixed;top:0;left:0;right:0}nav.frames,.frame_info{position:fixed;top:95px;bottom:0;box-sizing:border-box}nav.frames{width:40%;left:0}ul.frames{position:absolute;top:36px;bottom:0;left:0;right:0}.frame_info{right:0;left:40%;border-left:5px solid #38a;overflow-y:auto;overflow-x:hidden}}header.exception{padding:18px 20px;height:59px;min-height:59px;overflow:hidden;background-color:#20202a;color:#aaa;font-weight:200;-webkit-text-smoothing:antialiased}header.exception h2{font-weight:200;font-size:11pt}header.exception h2 strong{font-weight:700;color:#d55}header.exception h2,header.exception p{line-height:1.4em;overflow:hidden;white-space:pre;text-overflow:ellipsis}@media screen and (max-width: 1100px){header.exception h2,header.exception p{height:auto;padding-right:20px;overflow-y:auto;word-wrap:break-word;height:auto;max-height:7em}}header.exception p{font-weight:200;font-size:20pt;color:white}header.exception:hover{height:auto;z-index:2}header.exception:hover h2,header.exception:hover p{padding-right:20px;overflow-y:auto;word-wrap:break-word;white-space:pre-wrap;height:auto;max-height:7.5em}nav.segmented{border-bottom:solid 1px #ddd;text-align:center;padding:6px}nav.segmented a{display:inline-block;height:22px;line-height:22px;padding:0 10px;text-decoration:none;font-size:8pt;font-weight:bold;color:#38a;border:1px solid #38a}nav.segmented a:first-child{border-right:0;border-top-left-radius:8px;border-bottom-left-radius:8px}nav.segmented a:last-child{border-top-right-radius:8px;border-bottom-right-radius:8px}nav.segmented a.selected{color:white;background:#38a}nav.segmented a.disabled{text-decoration:line-through;cursor:default}ul.frames{overflow:auto}ul.frames li{padding:7px 20px;border-bottom:1pt solid #eee;cursor:pointer;overflow:hidden;padding-left:60px;position:relative}ul.frames li .name,ul.frames li .location{overflow:hidden;height:1.5em;white-space:nowrap;word-wrap:none;text-overflow:ellipsis}ul.frames li .method{color:#666}ul.frames li .location{font-size:0.85em;font-weight:400;color:#999}ul.frames li .line{font-weight:bold}ul.frames li .chevron{float:right;margin-right:-10px;color:#ccc;font-size:24px;line-height:34px}@media screen and (max-width: 1100px){ul.frames li .chevron{display:none}}ul.frames li.selected{background:#38a}ul.frames li.selected .name,ul.frames li.selected .method,ul.frames li.selected .location,ul.frames li.selected .chevron{color:white}ul.frames li .icon{display:block;width:20px;height:20px;line-height:20px;border-radius:15px;text-align:center;background:white;border:solid 2px #ccc;font-size:9pt;font-weight:200;font-style:normal;position:absolute;top:14px;left:20px}ul.frames li .icon.application{background:#808090;border-color:#555}ul.frames li .icon.application:before{content:'A';color:white}@media screen and (max-width: 1100px){ul.frames li{padding-top:6px;padding-bottom:6px;padding-left:36px;line-height:1.3}ul.frames li .icon{width:11px;height:11px;line-height:11px;top:7px;left:10px;font-size:5pt}ul.frames li .name,ul.frames li .location{display:inline-block;line-height:1.3;height:1.3em}ul.frames li .name{margin-right:10px}}pre,code,.be-repl .command-line input,.be-repl .command-line .prompt,.be-repl .output .command,textarea,.code_linenums,.title .name,ul.frames li .method{font-family:menlo, lucida console, monospace}.trace_info .title{background:#38a;color:white;overflow:hidden;padding:5px 10px}@media screen and (min-width: 1100px){.trace_info .title{padding:5px 10px 5px 0}}.trace_info .title .name,.trace_info .title .location{font-size:9pt;line-height:26px;height:26px;overflow:hidden}.trace_info .title .location{float:left;font-weight:bold;font-size:10pt}.trace_info .title .location a{color:white;text-decoration:none}.trace_info .title .location a .filename{text-decoration:underline}.trace_info .title .location a:hover{border-color:#666666}.trace_info .title .name{float:right;font-weight:200}.trace_info .code_block{background:#f1f1f1;padding-bottom:1px}.trace_info .code_block .code_linenums{background:#f1f1f1;padding-top:10px;padding-bottom:9px;float:left}.trace_info .code_block .code_linenums pre{padding:0 6px}.trace_info .code_block .code{margin-bottom:-1px;border-top-left-radius:2px;padding:10px 0;overflow:auto}.trace_info .code_block .code pre{padding-left:12px}.trace_info .code_block .code pre,.trace_info .code_block .code_linenums pre{font-size:11px;min-height:16px}.trace_info .code_block .code .highlight,.trace_info .code_block .code_linenums .highlight{background:rgba(220,30,30,0.1);-webkit-animation:highlight 400ms linear 1;-moz-animation:highlight 400ms linear 1;animation:highlight 400ms linear 1}.trace_info .code_block .code-unavailable{padding:20px 0 40px 0;text-align:center;color:#b99;font-weight:bold}.trace_info .code_block .code-unavailable:before{content:'\00d7';display:block;color:#daa;text-align:center;font-size:40pt;font-weight:normal;margin-bottom:-10px}@-webkit-keyframes highlight{0%{background:rgba(220,30,30,0.3)}100%{background:rgba(220,30,30,0.1)}}@-moz-keyframes highlight{0%{background:rgba(220,30,30,0.3)}100%{background:rgba(220,30,30,0.1)}}@keyframes highlight{0%{background:rgba(220,30,30,0.3)}100%{background:rgba(220,30,30,0.1)}}h3{background-color:#38a;color:white;padding:4px;font-size:8pt;-webkit-font-smoothing:antialiased}.be-console{background-color:#002B36;color:white}.be-console .output{max-height:15rem;overflow-x:hidden;overflow-y:auto;padding:8px;border-bottom:1px solid #ccc}.be-console .output:not(.used){padding:0;border-bottom:0}@media screen and (min-height: 800px){.be-console .output{max-height:25rem}}.be-console .output pre,.be-console .output h4{font-size:9pt;word-wrap:break-word;white-space:pre-wrap;width:100%}.be-console .output h4{padding:8px;margin-top:10px;background-color:#555}.be-console .output h4:first-child{margin-top:0}.be-console .output pre{padding:8px}.be-console .command-line{display:table;padding:8px 0;background-color:#555}.be-console .command-line .prompt,.be-console .command-line input{display:table-cell;line-height:12px;font-size:9pt}.be-console .command-line .prompt{width:1%;padding-right:5px;padding-left:10px;white-space:pre}.be-console .command-line input{width:99%}.be-console input,.be-console input:focus{outline:0;border:0;padding:0;background:transparent;margin:0;color:inherit}.hint{margin:15px 0 20px 0;font-size:8pt;color:#8080a0;padding-left:20px}.variables h4{margin-top:5px;padding-left:5px;font-weight:bold;font-size:9pt;word-wrap:break-word}.variables pre{max-height:15em;overflow-y:auto;font-size:9pt;word-wrap:break-word;white-space:normal;margin-left:2rem;border-bottom:1px dotted #ddd}.variables .unsupported{display:block;font-family:sans-serif;color:#777} diff --git a/lib/better_errors/templates/main.erb b/lib/better_errors/templates/main.erb index b373f2f6..4fa2cf6b 100644 --- a/lib/better_errors/templates/main.erb +++ b/lib/better_errors/templates/main.erb @@ -1,703 +1,16 @@ -