2022-07-26 23:21:09 +03:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# Ruby 3.2+ has a cleaner way to hook into Ruby that doesn't use `require`
|
|
|
|
if SyntaxError.method_defined?(:detailed_message)
|
|
|
|
module SyntaxSuggest
|
2022-12-09 09:12:51 +03:00
|
|
|
# Mini String IO [Private]
|
|
|
|
#
|
|
|
|
# Acts like a StringIO with reduced API, but without having to require that
|
|
|
|
# class.
|
2022-07-26 23:21:09 +03:00
|
|
|
class MiniStringIO
|
|
|
|
def initialize(isatty: $stderr.isatty)
|
|
|
|
@string = +""
|
|
|
|
@isatty = isatty
|
|
|
|
end
|
|
|
|
|
|
|
|
attr_reader :isatty
|
|
|
|
def puts(value = $/, **)
|
|
|
|
@string << value
|
|
|
|
end
|
|
|
|
|
|
|
|
attr_reader :string
|
|
|
|
end
|
|
|
|
|
2023-06-14 09:53:42 +03:00
|
|
|
# SyntaxSuggest.module_for_detailed_message [Private]
|
2022-12-09 09:12:51 +03:00
|
|
|
#
|
|
|
|
# Used to monkeypatch SyntaxError via Module.prepend
|
|
|
|
def self.module_for_detailed_message
|
|
|
|
Module.new {
|
|
|
|
def detailed_message(highlight: true, syntax_suggest: true, **kwargs)
|
|
|
|
return super unless syntax_suggest
|
2022-07-26 23:21:09 +03:00
|
|
|
|
2022-12-09 09:12:51 +03:00
|
|
|
require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
|
2022-07-26 23:21:09 +03:00
|
|
|
|
2022-12-09 09:12:51 +03:00
|
|
|
message = super
|
2022-12-01 22:27:59 +03:00
|
|
|
|
2022-12-09 09:12:51 +03:00
|
|
|
if path
|
|
|
|
file = Pathname.new(path)
|
|
|
|
io = SyntaxSuggest::MiniStringIO.new
|
2022-07-26 23:21:09 +03:00
|
|
|
|
2022-12-09 09:12:51 +03:00
|
|
|
SyntaxSuggest.call(
|
|
|
|
io: io,
|
|
|
|
source: file.read,
|
|
|
|
filename: file,
|
|
|
|
terminal: highlight
|
|
|
|
)
|
|
|
|
annotation = io.string
|
2022-12-01 13:45:09 +03:00
|
|
|
|
[ruby/syntax_suggest] The annotation must end with a new line
syntax_suggest did not work great when there is no new line at the end
of the input file.
Input:
```
def foo
end
end # No newline at end of file
```
Previous output:
```
$ ruby test.rb
test.rb: --> test.rb
Unmatched `end', missing keyword (`do', `def`, `if`, etc.) ?
> 1 def foo
> 2 end
> 3 end # No newline at end of filetest.rb:3: syntax error, unexpected `end' (SyntaxError)
end # No newline at end of file
^~~
```
Note that "test.rb:3: ..." is appended to the last line of the
annotation.
This change makes sure that the annotation ends with a new line.
New output:
```
$ ruby test.rb
test.rb: --> test.rb
Unmatched `end', missing keyword (`do', `def`, `if`, etc.) ?
> 1 def foo
> 2 end
> 3 end # No newline at end of file
test.rb:3: syntax error, unexpected `end' (SyntaxError)
end # No newline at end of file
^~~
```
https://github.com/ruby/syntax_suggest/commit/db4cf9147d
2023-03-17 02:52:27 +03:00
|
|
|
annotation += "\n" unless annotation.end_with?("\n")
|
|
|
|
|
2022-12-09 09:12:51 +03:00
|
|
|
annotation + message
|
|
|
|
else
|
|
|
|
message
|
|
|
|
end
|
|
|
|
rescue => e
|
|
|
|
if ENV["SYNTAX_SUGGEST_DEBUG"]
|
|
|
|
$stderr.warn(e.message)
|
|
|
|
$stderr.warn(e.backtrace)
|
|
|
|
end
|
2022-07-26 23:21:09 +03:00
|
|
|
|
2022-12-09 09:12:51 +03:00
|
|
|
# Ignore internal errors
|
|
|
|
message
|
|
|
|
end
|
|
|
|
}
|
2022-07-26 23:21:09 +03:00
|
|
|
end
|
2022-12-09 09:12:51 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
SyntaxError.prepend(SyntaxSuggest.module_for_detailed_message)
|
2022-07-26 23:21:09 +03:00
|
|
|
else
|
|
|
|
autoload :Pathname, "pathname"
|
|
|
|
|
2023-01-07 12:45:15 +03:00
|
|
|
#--
|
2022-07-26 23:21:09 +03:00
|
|
|
# Monkey patch kernel to ensure that all `require` calls call the same
|
|
|
|
# method
|
2023-01-07 12:45:15 +03:00
|
|
|
#++
|
2022-07-26 23:21:09 +03:00
|
|
|
module Kernel
|
2023-01-07 12:45:15 +03:00
|
|
|
# :stopdoc:
|
|
|
|
|
2022-07-26 23:21:09 +03:00
|
|
|
module_function
|
|
|
|
|
|
|
|
alias_method :syntax_suggest_original_require, :require
|
|
|
|
alias_method :syntax_suggest_original_require_relative, :require_relative
|
|
|
|
alias_method :syntax_suggest_original_load, :load
|
|
|
|
|
|
|
|
def load(file, wrap = false)
|
|
|
|
syntax_suggest_original_load(file)
|
|
|
|
rescue SyntaxError => e
|
|
|
|
require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
|
|
|
|
|
|
|
|
SyntaxSuggest.handle_error(e)
|
|
|
|
end
|
|
|
|
|
|
|
|
def require(file)
|
|
|
|
syntax_suggest_original_require(file)
|
|
|
|
rescue SyntaxError => e
|
|
|
|
require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
|
|
|
|
|
|
|
|
SyntaxSuggest.handle_error(e)
|
|
|
|
end
|
|
|
|
|
|
|
|
def require_relative(file)
|
|
|
|
if Pathname.new(file).absolute?
|
|
|
|
syntax_suggest_original_require file
|
|
|
|
else
|
|
|
|
relative_from = caller_locations(1..1).first
|
|
|
|
relative_from_path = relative_from.absolute_path || relative_from.path
|
|
|
|
syntax_suggest_original_require File.expand_path("../#{file}", relative_from_path)
|
|
|
|
end
|
|
|
|
rescue SyntaxError => e
|
|
|
|
require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
|
|
|
|
|
|
|
|
SyntaxSuggest.handle_error(e)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|