# HG changeset patch
# User Mahlon E. Smith <mahlon@martini.nu>
# Date 1293249670 28800
# Node ID cd1f3381c1edd489c2c8edd82ee0603bd521ef70
# Parent  09c4f575f62768342b9965561565df3c0d460985
Emit file and line for failure source (use gF to jump straight to it!).
Show context lines for exception source.  Put spec summary run at the
top of the screen.  Small documentation fixes.

diff -r 09c4f575f627 -r cd1f3381c1ed specky/doc/specky.txt
--- a/specky/doc/specky.txt	Sat Dec 18 23:38:55 2010 -0800
+++ b/specky/doc/specky.txt	Fri Dec 24 20:01:10 2010 -0800
@@ -91,11 +91,11 @@
 Otherwise, you'll need to locate it, and tell rspec to use it in one of two
 ways.
 
-	1) Set the *g:speckyRunSpecCmd* variable explicitly:
+	1) Set the 'g:speckyRunSpecCmd' variable explicitly:
 
-		let g:speckyRunRdocCmd = "rspec -r ~/.vim/ruby/specky_formatter.rb -f SpeckyFormatter" ~
+		let g:speckyRunSpecCmd = "rspec -r ~/.vim/ruby/specky_formatter.rb -f SpeckyFormatter" ~
 
-	2) or, leave *g:speckyRunSpecCmd* at its default value, and instead use
+	2) or, leave 'g:speckyRunSpecCmd' at its default value, and instead use
 	   an '.rspec' settings file in the root directory of the the project
 	   you're working in.  I find this method much more flexible -- the
 	   '.rspec' file can be carried with your project, and customized to
diff -r 09c4f575f627 -r cd1f3381c1ed specky/ruby/specky_formatter.rb
--- a/specky/ruby/specky_formatter.rb	Sat Dec 18 23:38:55 2010 -0800
+++ b/specky/ruby/specky_formatter.rb	Fri Dec 24 20:01:10 2010 -0800
@@ -14,8 +14,11 @@
 		@indent_level  = 0
 		@failure_index = 0
 		@failures      = []
+		@txt           = ''
+		@summary       = ''
 	end
 
+
 	########################################################################
 	### R S P E C  H O O K S
 	########################################################################
@@ -23,7 +26,6 @@
 	### Example group hook -- increase indentation, emit description
 	###
 	def example_group_started( example_group )
-		output.puts
 		self.out '+', '-' * (example_group.description.length + 2), '+'
 		self.out '| ', example_group.description, ' |'
 		self.out '+', '-' * (example_group.description.length + 2), '+'
@@ -73,13 +75,18 @@
 	### for Vim to fold.
 	###
 	def dump_failures
-		self.out "\n\n\n" unless @failures.empty?
+		self.out "\n" unless @failures.empty?
 
 		@failures.each_with_index do |example, index|
 			desc      = example.metadata[ :full_description ]
 			exception = example.execution_result[ :exception ]
+			file = line = nil
 
+			if exception.backtrace.first =~ /(.*):(\d+)/
+				file, line = $1, $2.to_i
+			end
 			self.out "FAILURE - #%d)" % [ index + 1 ]
+			self.out "%s:%d" % [ file, line ]
 
 			if RSpec::Core::PendingExampleFixedError === exception
 				self.out "%s FIXED" % [ desc ]
@@ -100,27 +107,55 @@
 					end
 				end
 			end
-			self.out "\n"
+
+			self.out exception_source( file, line ) if file && line
 		end
 	end
 
 
+	### Emit the source of the exception, with context lines.
+	###
+	def exception_source( file, line )
+		context = ''
+		low, high = line - 3, line + 3
+
+		File.open( file ).each_with_index do |cline, i|
+			cline.chomp!.rstrip!
+			next unless i >= low && i <= high
+			context << "  %s%4d: %s\n" % [ ( i == line ? '>>' : ' |' ), i, cline ]
+		end
+
+		return context
+
+	rescue
+		'Unable to parse exception context lines.'
+	end
+
+
 	### Emit summary data for all examples.
 	###
 	def dump_summary( duration, example_count, failure_count, pending_count )
 		succeeded = example_count - failure_count - pending_count
-		self.out '+', '-' * 49, '+'
-		self.out '|', ' ' * 18, '-- Summary --', ' ' * 18, '|'
-		self.out '+----------+-----------+--------+---------+-------+'
-		self.out '| Duration | Succeeded | Failed | Pending | Total |'
-		self.out '+----------+-----------+--------+---------+-------+'
+		@summary << "+%s+\n" % [ '-' * 49 ]
+		@summary << "|%s-- Summary --%s|\n" % [ ' ' * 18, ' ' * 18 ]
+		@summary << "+----------+-----------+--------+---------+-------+\n"
+		@summary << "| Duration | Succeeded | Failed | Pending | Total |\n"
+		@summary << "+----------+-----------+--------+---------+-------+\n"
 
-		self.out "| %7ss | %9s | %6s | %7s | %5s |" % [
+		@summary << "| %7ss | %9s | %6s | %7s | %5s |\n" % [
 			"%0.3f" % duration, succeeded, failure_count,
 			pending_count, example_count
 		]
 
-		self.out '+----------+-----------+--------+---------+-------+'
+		@summary << "+----------+-----------+--------+---------+-------+\n\n"
+	end
+
+
+	### End of run.  Dump it all out!
+	###
+	def close
+		output.puts @summary
+		output.puts @txt
 	end
 
 
@@ -132,7 +167,7 @@
 	###
 	def out( *msg )
 		msg = msg.join
-		output.puts "%s%s" % [ '  ' * @indent_level, msg ]
+		@txt << "%s%s\n" % [ '  ' * @indent_level, msg ]
 	end
 
 	### Format the basic example information, along with the run duration.
diff -r 09c4f575f627 -r cd1f3381c1ed specky/syntax/specrun.vim
--- a/specky/syntax/specrun.vim	Sat Dec 18 23:38:55 2010 -0800
+++ b/specky/syntax/specrun.vim	Fri Dec 24 20:01:10 2010 -0800
@@ -27,7 +27,9 @@
 syntax keyword specFailedKeyword Failed
 
 " Failure details
-syntax region specFailedDetails start="^FAILURE - #\d\+)" end="^$" fold contains=specCallout
+syntax region specFailedDetails start="^FAILURE - #\d\+)" end="^$" fold contains=specCallout,specErrorLine
+syntax match specErrorLine /^  >>/
+
 
 " Boxes
 syntax match specBox /^\(\s\+\)\?\(+[+-]\+\||.*|\)$/ contains=specFailedKeyword,specDurationKeyword,specPendingKeyword,specPassedKeyword,specBoxContent
@@ -51,6 +53,7 @@
 highlight def link specBox LineNr
 highlight def link specBoxContent Constant
 highlight def link specBoxLine LineNr
+highlight def link specErrorLine ErrorMsg
 
 let b:current_syntax = "specrun"