minitest-heat 0.0.7 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/Gemfile.lock +6 -6
- data/README.md +1 -1
- data/lib/minitest/heat/backtrace/line_parser.rb +25 -0
- data/lib/minitest/heat/backtrace.rb +39 -43
- data/lib/minitest/heat/hit.rb +41 -28
- data/lib/minitest/heat/issue.rb +96 -77
- data/lib/minitest/heat/location.rb +115 -116
- data/lib/minitest/heat/locations.rb +105 -0
- data/lib/minitest/heat/map.rb +16 -20
- data/lib/minitest/heat/output/backtrace.rb +52 -44
- data/lib/minitest/heat/output/issue.rb +69 -62
- data/lib/minitest/heat/output/map.rb +115 -32
- data/lib/minitest/heat/output/marker.rb +5 -3
- data/lib/minitest/heat/output/results.rb +3 -2
- data/lib/minitest/heat/output/source_code.rb +1 -1
- data/lib/minitest/heat/output/token.rb +2 -1
- data/lib/minitest/heat/output.rb +66 -6
- data/lib/minitest/heat/results.rb +26 -4
- data/lib/minitest/heat/source.rb +1 -1
- data/lib/minitest/heat/version.rb +1 -1
- data/lib/minitest/heat.rb +3 -2
- data/lib/minitest/heat_plugin.rb +9 -17
- data/lib/minitest/heat_reporter.rb +26 -39
- metadata +5 -4
- data/lib/minitest/heat/line.rb +0 -74
@@ -2,179 +2,178 @@
|
|
2
2
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# - 'final' represents the final line of the backtrace regardless of where it is
|
9
|
-
# - 'test' represents the last line from the project's tests. It is further differentiated by
|
10
|
-
# the line where the test is defined and the actual line of code in the test that geneated
|
11
|
-
# the failure or exception
|
12
|
-
# - 'source_code' represents the last line from the project's source code
|
13
|
-
# - 'project' represents the last source line, but falls back to the last test line
|
14
|
-
# - 'most_relevant' represents the most specific file to investigate starting with the source
|
15
|
-
# code and then looking to the test code with final line of the backtrace as a fallback
|
5
|
+
# Consistent structure for extracting information about a given location. In addition to the
|
6
|
+
# pathname to the file and the line number in the file, it can also include information about
|
7
|
+
# the containing method or block and retrieve source code for the location.
|
16
8
|
class Location
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@line_number = Integer(line_number)
|
21
|
-
super
|
22
|
-
end
|
23
|
-
end
|
9
|
+
UNRECOGNIZED = '(Unrecognized File)'
|
10
|
+
UNKNOWN_MODIFICATION_TIME = Time.at(0)
|
11
|
+
UNKNOWN_MODIFICATION_SECONDS = -1
|
24
12
|
|
25
|
-
|
13
|
+
attr_accessor :raw_pathname, :raw_line_number, :raw_container
|
26
14
|
|
27
|
-
|
28
|
-
|
29
|
-
|
15
|
+
# Initialize a new Location
|
16
|
+
#
|
17
|
+
# @param [Pathname, String] pathname: the pathname to the file
|
18
|
+
# @param [Integer] line_number: the line number of the location within the file
|
19
|
+
# @param [String] container: nil the containing method or block for the issue
|
20
|
+
#
|
21
|
+
# @return [self]
|
22
|
+
def initialize(pathname:, line_number:, container: nil)
|
23
|
+
@raw_pathname = pathname
|
24
|
+
@raw_line_number = line_number
|
25
|
+
@raw_container = container
|
30
26
|
end
|
31
27
|
|
32
|
-
#
|
33
|
-
# test failure
|
28
|
+
# Generates a formatted string describing the line of code similar to the original backtrace
|
34
29
|
#
|
35
|
-
# @return [String]
|
30
|
+
# @return [String] a consistently-formatted, human-readable string about the line of code
|
36
31
|
def to_s
|
37
|
-
"#{
|
38
|
-
end
|
39
|
-
|
40
|
-
def local?
|
41
|
-
broken_test? || proper_failure?
|
32
|
+
"#{absolute_path}#{filename}:#{line_number} in `#{container}`"
|
42
33
|
end
|
43
34
|
|
44
|
-
#
|
45
|
-
# test, and it raises an exception, then it's really a broken test rather than a proper
|
46
|
-
# faiure.
|
35
|
+
# Generates a simplified location array with the pathname and line number
|
47
36
|
#
|
48
|
-
# @return [
|
49
|
-
def
|
50
|
-
|
37
|
+
# @return [Array<Pathname, Integer>] a no-frills location pair
|
38
|
+
def to_a
|
39
|
+
[
|
40
|
+
pathname,
|
41
|
+
line_number
|
42
|
+
]
|
51
43
|
end
|
52
44
|
|
53
|
-
#
|
54
|
-
# an external piece of code like a gem.
|
45
|
+
# A short relative pathname and line number pair
|
55
46
|
#
|
56
|
-
# @return [
|
57
|
-
|
58
|
-
|
59
|
-
!source_code_file.nil? && !broken_test?
|
47
|
+
# @return [String] the short filename/line number combo. ex. `dir/file.rb:23`
|
48
|
+
def short
|
49
|
+
"#{relative_filename}:#{line_number}"
|
60
50
|
end
|
61
51
|
|
62
|
-
#
|
63
|
-
# backtrace files will be a gem or external library that's failing indirectly as a result
|
64
|
-
# of a problem with local source code (not always, but frequently). In that case, the best
|
65
|
-
# first place to focus is on the code you control.
|
52
|
+
# Determine if there is a file and text at the given line number
|
66
53
|
#
|
67
|
-
# @return [
|
68
|
-
def
|
69
|
-
|
54
|
+
# @return [Boolean] true if the file exists and has text at the given line number
|
55
|
+
def exists?
|
56
|
+
pathname.exist? && source_code.lines.any?
|
70
57
|
end
|
71
58
|
|
72
|
-
# The
|
59
|
+
# The pathanme for the location. Written to be safe and fallbackto the project directory if
|
60
|
+
# an exception is raised ocnverting the value to a pathname
|
73
61
|
#
|
74
|
-
# @return [
|
75
|
-
def
|
76
|
-
|
62
|
+
# @return [Pathname] a pathname instance for the relevant file
|
63
|
+
def pathname
|
64
|
+
Pathname(raw_pathname)
|
65
|
+
rescue ArgumentError
|
66
|
+
Pathname(Dir.pwd)
|
77
67
|
end
|
78
68
|
|
79
|
-
#
|
69
|
+
# A safe interface to getting a string representing the path portion of the file
|
80
70
|
#
|
81
|
-
# @return [String] the
|
82
|
-
|
83
|
-
|
71
|
+
# @return [String] either the path/directory portion of the file name or '(Unrecognized File)'
|
72
|
+
# if the offending file can't be found for some reason
|
73
|
+
def path
|
74
|
+
pathname.exist? ? pathname.dirname.to_s : UNRECOGNIZED
|
84
75
|
end
|
85
76
|
|
86
|
-
|
87
|
-
|
88
|
-
# @return [Integer] line number
|
89
|
-
def final_failure_line
|
90
|
-
final_location.line_number
|
77
|
+
def absolute_path
|
78
|
+
pathname.exist? ? "#{path}/" : UNRECOGNIZED
|
91
79
|
end
|
92
80
|
|
93
|
-
|
94
|
-
|
95
|
-
# @return [String] the relative path to the file from the project root
|
96
|
-
def project_file
|
97
|
-
broken_test? ? test_file : source_code_file
|
81
|
+
def relative_path
|
82
|
+
pathname.exist? ? absolute_path.delete_prefix("#{project_root_dir}/") : UNRECOGNIZED
|
98
83
|
end
|
99
84
|
|
100
|
-
#
|
85
|
+
# A safe interface for getting a string representing the filename portion of the file
|
101
86
|
#
|
102
|
-
# @return [
|
103
|
-
|
104
|
-
|
87
|
+
# @return [String] either the filename portion of the file or '(Unrecognized File)'
|
88
|
+
# if the offending file can't be found for some reason
|
89
|
+
def filename
|
90
|
+
pathname.exist? ? pathname.basename.to_s : UNRECOGNIZED
|
105
91
|
end
|
106
92
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
def source_code_file
|
111
|
-
return nil unless backtrace.source_code_entries.any?
|
93
|
+
def absolute_filename
|
94
|
+
pathname.exist? ? pathname.to_s : UNRECOGNIZED
|
95
|
+
end
|
112
96
|
|
113
|
-
|
97
|
+
def relative_filename
|
98
|
+
pathname.exist? ? pathname.to_s.delete_prefix("#{project_root_dir}/") : UNRECOGNIZED
|
114
99
|
end
|
115
100
|
|
116
|
-
#
|
101
|
+
# Line number identifying the specific line in the file
|
102
|
+
#
|
103
|
+
# @return [Integer] line number for the file
|
117
104
|
#
|
118
|
-
|
119
|
-
|
120
|
-
|
105
|
+
def line_number
|
106
|
+
Integer(raw_line_number)
|
107
|
+
rescue ArgumentError
|
108
|
+
1
|
109
|
+
end
|
121
110
|
|
122
|
-
|
111
|
+
# The containing method or block details for the location
|
112
|
+
#
|
113
|
+
# @return [String] the containing method of the line of code
|
114
|
+
def container
|
115
|
+
raw_container.nil? ? '(Unknown Container)' : String(raw_container)
|
123
116
|
end
|
124
117
|
|
125
|
-
#
|
118
|
+
# Looks up the source code for the location. Can return multiple lines of source code from
|
119
|
+
# the surrounding lines of code for the primary line
|
126
120
|
#
|
127
|
-
# @
|
128
|
-
|
129
|
-
|
121
|
+
# @param [Integer] max_line_count: 1 the maximum number of lines to return from the source
|
122
|
+
#
|
123
|
+
# @return [Source] an instance of Source for accessing lines and their line numbers
|
124
|
+
def source_code(max_line_count: 1)
|
125
|
+
Minitest::Heat::Source.new(
|
126
|
+
pathname.to_s,
|
127
|
+
line_number: line_number,
|
128
|
+
max_line_count: max_line_count
|
129
|
+
)
|
130
130
|
end
|
131
131
|
|
132
|
-
#
|
132
|
+
# Determines if a given file is from the project directory
|
133
133
|
#
|
134
|
-
# @return [
|
135
|
-
def
|
136
|
-
|
134
|
+
# @return [Boolean] true if the file is in the project (source code or test)
|
135
|
+
def project_file?
|
136
|
+
path.include?(project_root_dir)
|
137
137
|
end
|
138
138
|
|
139
|
-
#
|
139
|
+
# Determines if a given file follows the standard approaching to naming test files.
|
140
140
|
#
|
141
|
-
# @return [
|
142
|
-
def
|
143
|
-
|
141
|
+
# @return [Boolean] true if the file name starts with `test_` or ends with `_test.rb`
|
142
|
+
def test_file?
|
143
|
+
filename.to_s.start_with?('test_') || filename.to_s.end_with?('_test.rb')
|
144
144
|
end
|
145
145
|
|
146
|
-
#
|
146
|
+
# Determines if a given file is a non-test file from the project directory
|
147
147
|
#
|
148
|
-
# @return [
|
149
|
-
|
150
|
-
|
151
|
-
backtrace? ? backtrace.final_location : test_location
|
148
|
+
# @return [Boolean] true if the file is in the project but not a test file
|
149
|
+
def source_code_file?
|
150
|
+
project_file? && !test_file?
|
152
151
|
end
|
153
152
|
|
154
|
-
#
|
155
|
-
# backtrace files will be a gem or external library that's failing indirectly as a result
|
156
|
-
# of a problem with local source code (not always, but frequently). In that case, the best
|
157
|
-
# first place to focus is on the code you control.
|
153
|
+
# A safe interface to getting the last modified time for the file in question
|
158
154
|
#
|
159
|
-
# @return [
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
test_location,
|
164
|
-
final_location
|
165
|
-
].compact.first
|
155
|
+
# @return [Time] the timestamp for when the file in question was last modified or `Time.at(0)`
|
156
|
+
# if the offending file can't be found for some reason
|
157
|
+
def mtime
|
158
|
+
pathname.exist? ? pathname.mtime : UNKNOWN_MODIFICATION_TIME
|
166
159
|
end
|
167
160
|
|
168
|
-
|
169
|
-
|
161
|
+
# A safe interface to getting the number of seconds since the file was modified
|
162
|
+
#
|
163
|
+
# @return [Integer] the number of seconds since the file was modified or `-1` if the offending
|
164
|
+
# file can't be found for some reason
|
165
|
+
def age_in_seconds
|
166
|
+
pathname.exist? ? seconds_ago : UNKNOWN_MODIFICATION_SECONDS
|
170
167
|
end
|
171
168
|
|
172
|
-
|
173
|
-
|
169
|
+
private
|
170
|
+
|
171
|
+
def project_root_dir
|
172
|
+
Dir.pwd
|
174
173
|
end
|
175
174
|
|
176
|
-
def
|
177
|
-
|
175
|
+
def seconds_ago
|
176
|
+
(Time.now - mtime).to_i
|
178
177
|
end
|
179
178
|
end
|
180
179
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Minitest
|
4
|
+
module Heat
|
5
|
+
# Convenience methods for determining the file and line number where the problem occurred.
|
6
|
+
# There are several layers of specificity to help make it easy to communicate the relative
|
7
|
+
# location of the failure:
|
8
|
+
# - 'final' represents the final line of the backtrace regardless of where it is
|
9
|
+
# - 'test_definition' represents where the test is defined
|
10
|
+
# - 'test_failure' represents the last line from the project's tests. It is further differentiated by
|
11
|
+
# the line where the test is defined and the actual line of code in the test that geneated
|
12
|
+
# the failure or exception
|
13
|
+
# - 'source_code' represents the last line from the project's source code
|
14
|
+
# - 'project' represents the last source line, but falls back to the last test line
|
15
|
+
# - 'most_relevant' represents the most specific file to investigate starting with the source
|
16
|
+
# code and then looking to the test code with final line of the backtrace as a fallback
|
17
|
+
class Locations
|
18
|
+
attr_reader :test_definition, :backtrace
|
19
|
+
|
20
|
+
def initialize(test_definition_location, backtrace = [])
|
21
|
+
test_definition_pathname, test_definition_line_number = test_definition_location
|
22
|
+
@test_definition = ::Minitest::Heat::Location.new(pathname: test_definition_pathname, line_number: test_definition_line_number)
|
23
|
+
|
24
|
+
@backtrace = Backtrace.new(backtrace)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Prints the pathname and line number of the location most likely to be the source of the
|
28
|
+
# test failure
|
29
|
+
#
|
30
|
+
# @return [String] ex. 'path/to/file.rb:12'
|
31
|
+
def to_s
|
32
|
+
"#{most_relevant.absolute_filename}:#{most_relevant.line_number}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Knows if the failure is contained within the test. For example, if there's bad code in a
|
36
|
+
# test, and it raises an exception, then it's really a broken test rather than a proper
|
37
|
+
# faiure.
|
38
|
+
#
|
39
|
+
# @return [Boolean] true if final file in the backtrace is the same as the test location file
|
40
|
+
def broken_test?
|
41
|
+
!test_failure.nil? && test_failure == final
|
42
|
+
end
|
43
|
+
|
44
|
+
# Knows if the failure occurred in the actual project source code—as opposed to the test or
|
45
|
+
# an external piece of code like a gem.
|
46
|
+
#
|
47
|
+
# @return [Boolean] true if there's a non-test project file in the stacktrace but it's not
|
48
|
+
# a result of a broken test
|
49
|
+
def proper_failure?
|
50
|
+
!source_code.nil? && !broken_test?
|
51
|
+
end
|
52
|
+
|
53
|
+
# The file most likely to be the source of the underlying problem. Often, the most recent
|
54
|
+
# backtrace files will be a gem or external library that's failing indirectly as a result
|
55
|
+
# of a problem with local source code (not always, but frequently). In that case, the best
|
56
|
+
# first place to focus is on the code you control.
|
57
|
+
#
|
58
|
+
# @return [Array] file and line number of the most likely source of the problem
|
59
|
+
def most_relevant
|
60
|
+
[
|
61
|
+
source_code,
|
62
|
+
test_failure,
|
63
|
+
final
|
64
|
+
].compact.first
|
65
|
+
end
|
66
|
+
|
67
|
+
def freshest
|
68
|
+
backtrace.recently_modified_locations.first
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the final test location based on the backtrace if present. Otherwise falls back to
|
72
|
+
# the test location which represents the test definition. The `test_definition` attribute
|
73
|
+
# provides the location of where the test is defined. `test_failure` represents the actual
|
74
|
+
# line from within the test where the problem occurred
|
75
|
+
#
|
76
|
+
# @return [Location] the final location from the test files
|
77
|
+
def test_failure
|
78
|
+
backtrace.test_locations.any? ? backtrace.test_locations.first : test_definition
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the final source code location based on the backtrace
|
82
|
+
#
|
83
|
+
# @return [Location] the final location from the source code files
|
84
|
+
def source_code
|
85
|
+
backtrace.source_code_locations.first
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the final project location based on the backtrace if present. Otherwise falls back
|
89
|
+
# to the test location which represents the test definition.
|
90
|
+
#
|
91
|
+
# @return [Location] the final location from the project files
|
92
|
+
def project
|
93
|
+
backtrace.project_locations.any? ? backtrace.project_locations.first : test_definition
|
94
|
+
end
|
95
|
+
|
96
|
+
# The line number from within the `test_file` test definition where the failure occurred
|
97
|
+
#
|
98
|
+
# @return [Location] the last location from the backtrace or the test location if a backtrace
|
99
|
+
# was not passed to the initializer
|
100
|
+
def final
|
101
|
+
backtrace.locations.any? ? backtrace.locations.first : test_definition
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/minitest/heat/map.rb
CHANGED
@@ -2,43 +2,39 @@
|
|
2
2
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
|
+
# Structured approach to collecting the locations of issues for generating a heat map
|
5
6
|
class Map
|
6
7
|
MAXIMUM_FILES_TO_SHOW = 5
|
7
8
|
|
8
9
|
attr_reader :hits
|
9
10
|
|
10
|
-
# So we can sort hot spots by liklihood of being the most important spot to check out before
|
11
|
-
# trying to fix something. These are ranked based on the possibility they represent ripple
|
12
|
-
# effects where fixing one problem could potentially fix multiple other failures.
|
13
|
-
#
|
14
|
-
# For example, if there's an exception in the file, start there. Broken code can't run. If a
|
15
|
-
# test is broken (i.e. raising an exception), that's a special sort of failure that would be
|
16
|
-
# misleading. It doesn't represent a proper failure, but rather a test that doesn't work.
|
17
|
-
WEIGHTS = {
|
18
|
-
error: 3, # exceptions from source code have the highest liklihood of a ripple effect
|
19
|
-
broken: 2, # broken tests won't have ripple effects but can't help if they can't run
|
20
|
-
failure: 1, # failures are kind of the whole point, and they could have ripple effects
|
21
|
-
skipped: 0, # skips aren't failures, but they shouldn't go ignored
|
22
|
-
painful: 0, # slow tests aren't failures, but they shouldn't be ignored
|
23
|
-
slow: 0
|
24
|
-
}.freeze
|
25
|
-
|
26
11
|
def initialize
|
27
12
|
@hits = {}
|
28
13
|
end
|
29
14
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
15
|
+
# Records a hit to the list of files and issue types
|
16
|
+
# @param filename [String] the unique path and file name for recordings hits
|
17
|
+
# @param line_number [Integer] the line number where the issue was encountered
|
18
|
+
# @param type [Symbol] the type of issue that was encountered (i.e. :failure, :error, etc.)
|
19
|
+
#
|
20
|
+
# @return [void]
|
21
|
+
def add(pathname, line_number, type, backtrace: [])
|
22
|
+
@hits[pathname.to_s] ||= Hit.new(pathname)
|
23
|
+
@hits[pathname.to_s].log(type.to_sym, line_number, backtrace: backtrace)
|
34
24
|
end
|
35
25
|
|
26
|
+
# Returns a subset of affected files to keep the list from being overwhelming
|
27
|
+
#
|
28
|
+
# @return [Array] the list of files and the line numbers for each encountered issue type
|
36
29
|
def file_hits
|
37
30
|
hot_files.take(MAXIMUM_FILES_TO_SHOW)
|
38
31
|
end
|
39
32
|
|
40
33
|
private
|
41
34
|
|
35
|
+
# Sorts the files by hit "weight" so that the most problematic files are at the beginning
|
36
|
+
#
|
37
|
+
# @return [Array] the collection of files that encountred issues
|
42
38
|
def hot_files
|
43
39
|
hits.values.sort_by(&:weight).reverse
|
44
40
|
end
|
@@ -5,14 +5,14 @@ module Minitest
|
|
5
5
|
class Output
|
6
6
|
# Builds the collection of tokens for a backtrace when an exception occurs
|
7
7
|
class Backtrace
|
8
|
-
DEFAULT_LINE_COUNT =
|
8
|
+
DEFAULT_LINE_COUNT = 10
|
9
9
|
DEFAULT_INDENTATION_SPACES = 2
|
10
10
|
|
11
|
-
attr_accessor :
|
11
|
+
attr_accessor :locations, :backtrace
|
12
12
|
|
13
|
-
def initialize(
|
14
|
-
@
|
15
|
-
@backtrace =
|
13
|
+
def initialize(locations)
|
14
|
+
@locations = locations
|
15
|
+
@backtrace = locations.backtrace
|
16
16
|
@tokens = []
|
17
17
|
end
|
18
18
|
|
@@ -21,18 +21,8 @@ module Minitest
|
|
21
21
|
# final backtrace line if it might be relevant/helpful?
|
22
22
|
|
23
23
|
# Iterate over the selected lines from the backtrace
|
24
|
-
|
25
|
-
|
26
|
-
parts = [
|
27
|
-
indentation_token,
|
28
|
-
path_token(backtrace_entry),
|
29
|
-
*file_and_line_number_tokens(backtrace_entry),
|
30
|
-
source_code_line_token(backtrace_entry.source_code)
|
31
|
-
]
|
32
|
-
|
33
|
-
parts << file_freshness(backtrace_entry) if most_recently_modified?(backtrace_entry)
|
34
|
-
|
35
|
-
@tokens << parts
|
24
|
+
backtrace_locations.each do |location|
|
25
|
+
@tokens << backtrace_location_tokens(location)
|
36
26
|
end
|
37
27
|
|
38
28
|
@tokens
|
@@ -51,56 +41,74 @@ module Minitest
|
|
51
41
|
# ...it could be influenced by a "compact" or "robust" reporter super-style?
|
52
42
|
# ...it's smart about exceptions that were raised outside of the project?
|
53
43
|
# ...it's smart about highlighting lines of code differently based on whether it's source code, test code, or external code?
|
54
|
-
def
|
55
|
-
|
44
|
+
def backtrace_locations
|
45
|
+
backtrace.locations.take(line_count)
|
56
46
|
end
|
57
47
|
|
58
48
|
private
|
59
49
|
|
60
|
-
def
|
61
|
-
|
50
|
+
def backtrace_location_tokens(location)
|
51
|
+
[
|
52
|
+
indentation_token,
|
53
|
+
path_token(location),
|
54
|
+
*file_and_line_number_tokens(location),
|
55
|
+
containining_element_token(location),
|
56
|
+
source_code_line_token(location),
|
57
|
+
most_recently_modified_token(location),
|
58
|
+
].compact
|
62
59
|
end
|
63
60
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
backtrace.project_entries.take(line_count)
|
70
|
-
end
|
71
|
-
|
72
|
-
def all_entries
|
73
|
-
backtrace.parsed_entries.take(line_count)
|
61
|
+
# Determines if all lines to be displayed are from within the project directory
|
62
|
+
#
|
63
|
+
# @return [Boolean] true if all lines of the backtrace being displayed are from the project
|
64
|
+
def all_backtrace_from_project?
|
65
|
+
backtrace_locations.all?(&:project_file?)
|
74
66
|
end
|
75
67
|
|
76
|
-
def most_recently_modified?(
|
68
|
+
def most_recently_modified?(location)
|
77
69
|
# If there's more than one line being displayed, and the current line is the freshest
|
78
|
-
|
70
|
+
backtrace_locations.size > 1 && location == locations.freshest
|
79
71
|
end
|
80
72
|
|
81
73
|
def indentation_token
|
82
74
|
[:default, ' ' * indentation]
|
83
75
|
end
|
84
76
|
|
85
|
-
def path_token(
|
86
|
-
|
77
|
+
def path_token(location)
|
78
|
+
# If the line is a project file, help it stand out from the backtrace noise
|
79
|
+
style = location.project_file? ? :default : :muted
|
87
80
|
|
88
|
-
# If all of the backtrace lines are from the project, no point in the added redundant
|
89
|
-
#
|
90
|
-
|
81
|
+
# If *all* of the backtrace lines are from the project, no point in the added redundant
|
82
|
+
# noise of showing the project root directory over and over again
|
83
|
+
path_format = all_backtrace_from_project? ? :relative_path : :absolute_path
|
91
84
|
|
92
|
-
[
|
85
|
+
[style, location.send(path_format)]
|
93
86
|
end
|
94
87
|
|
95
|
-
def file_and_line_number_tokens(
|
96
|
-
|
88
|
+
def file_and_line_number_tokens(location)
|
89
|
+
style = location.to_s.include?(Dir.pwd) ? :bold : :muted
|
90
|
+
[
|
91
|
+
[style, location.filename],
|
92
|
+
[:muted, ':'],
|
93
|
+
[style, location.line_number]
|
94
|
+
]
|
97
95
|
end
|
98
96
|
|
99
|
-
def source_code_line_token(
|
100
|
-
|
97
|
+
def source_code_line_token(location)
|
98
|
+
return nil unless location.project_file?
|
99
|
+
|
100
|
+
[:muted, " #{Output::SYMBOLS[:arrow]} `#{location.source_code.line.strip}`"]
|
101
101
|
end
|
102
102
|
|
103
|
-
def
|
103
|
+
def containining_element_token(location)
|
104
|
+
return nil if !location.project_file? || location.container.nil? || location.container.empty?
|
105
|
+
|
106
|
+
[:muted, " in #{location.container}"]
|
107
|
+
end
|
108
|
+
|
109
|
+
def most_recently_modified_token(location)
|
110
|
+
return nil unless most_recently_modified?(location)
|
111
|
+
|
104
112
|
[:default, " #{Output::SYMBOLS[:middot]} Most Recently Modified File"]
|
105
113
|
end
|
106
114
|
|