diff options
author | schneems <[email protected]> | 2022-07-26 15:21:09 -0500 |
---|---|---|
committer | Hiroshi SHIBATA <[email protected]> | 2022-08-19 10:02:24 +0900 |
commit | 490af8dbdb66263f29d0b4e43752fbb298b94862 (patch) | |
tree | 5f161e99d27a1417f446e8b1516263fd76d6f0bc /lib/syntax_suggest/explain_syntax.rb | |
parent | a50df1ab0eb312e5cdcf010d2c1b362ec41f3c59 (diff) |
Sync SyntaxSuggest
```
$ tool/sync_default_gems.rb syntax_suggest
```
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/5859
Diffstat (limited to 'lib/syntax_suggest/explain_syntax.rb')
-rw-r--r-- | lib/syntax_suggest/explain_syntax.rb | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/lib/syntax_suggest/explain_syntax.rb b/lib/syntax_suggest/explain_syntax.rb new file mode 100644 index 0000000000..142ed2e269 --- /dev/null +++ b/lib/syntax_suggest/explain_syntax.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +require_relative "left_right_lex_count" + +module SyntaxSuggest + # Explains syntax errors based on their source + # + # example: + # + # source = "def foo; puts 'lol'" # Note missing end + # explain ExplainSyntax.new( + # code_lines: CodeLine.from_source(source) + # ).call + # explain.errors.first + # # => "Unmatched keyword, missing `end' ?" + # + # When the error cannot be determined by lexical counting + # then ripper is run against the input and the raw ripper + # errors returned. + # + # Example: + # + # source = "1 * " # Note missing a second number + # explain ExplainSyntax.new( + # code_lines: CodeLine.from_source(source) + # ).call + # explain.errors.first + # # => "syntax error, unexpected end-of-input" + class ExplainSyntax + INVERSE = { + "{" => "}", + "}" => "{", + "[" => "]", + "]" => "[", + "(" => ")", + ")" => "(", + "|" => "|" + }.freeze + + def initialize(code_lines:) + @code_lines = code_lines + @left_right = LeftRightLexCount.new + @missing = nil + end + + def call + @code_lines.each do |line| + line.lex.each do |lex| + @left_right.count_lex(lex) + end + end + + self + end + + # Returns an array of missing elements + # + # For example this: + # + # ExplainSyntax.new(code_lines: lines).missing + # # => ["}"] + # + # Would indicate that the source is missing + # a `}` character in the source code + def missing + @missing ||= @left_right.missing + end + + # Converts a missing string to + # an human understandable explanation. + # + # Example: + # + # explain.why("}") + # # => "Unmatched `{', missing `}' ?" + # + def why(miss) + case miss + when "keyword" + "Unmatched `end', missing keyword (`do', `def`, `if`, etc.) ?" + when "end" + "Unmatched keyword, missing `end' ?" + else + inverse = INVERSE.fetch(miss) { + raise "Unknown explain syntax char or key: #{miss.inspect}" + } + "Unmatched `#{inverse}', missing `#{miss}' ?" + end + end + + # Returns an array of syntax error messages + # + # If no missing pairs are found it falls back + # on the original ripper error messages + def errors + if missing.empty? + return RipperErrors.new(@code_lines.map(&:original).join).call.errors + end + + missing.map { |miss| why(miss) } + end + end +end |