Last active
February 8, 2017 13:47
-
-
Save JoshCheek/20cfab07f16d3a2f8d28749687b3a58f to your computer and use it in GitHub Desktop.
Example of how you could use SiB to do dynamic tab completion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Based on an issue someone opened on a gem I'm mirroring | |
# https://github.com/JoshCheek/rcodetools/issues/1#issuecomment-278332781 | |
require_relative 'completion' | |
Completion.new(source: DATA.read, line: 20, column: 12).completions | |
# => [["Foo", "mouse"], | |
# ["Foo", "monster"], | |
# ["Foo", "mongoose"], | |
# ["Object", "mocking_bird"]] | |
__END__ | |
class Foo | |
def mouse | |
:squeek | |
end | |
def monster | |
:rar | |
end | |
def mongoose | |
:blep | |
end | |
end | |
class Object | |
def mocking_bird | |
:oooh_look_at_me_SQUAWK_whoooo_im_an_object_SQUAWK! | |
end | |
end | |
1+Foo.new.mo+2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'seeing_is_believing' | |
require 'parser/ruby24' | |
require 'json' | |
class Completion | |
def initialize(source:, line:, column:) | |
self.code = SeeingIsBelieving::Code.new source | |
self.line = line | |
self.column = column | |
self.index = column + code.linenum_to_index(line) | |
self.ast = find_ast code.root, index | |
end | |
def completions | |
# This is for a :send ast on an explicit receiver, you'd have to figure out | |
# what it should be for each syntactic context you might be completing in | |
receiver, to_complete = ast.children | |
handler = SeeingIsBelieving.call code.raw, rewrite_code: -> * do | |
code.rewriter.replace ast.location.expression, | |
competions_for_send(receiver) | |
code.rewriter.process | |
end | |
handler.result[line] | |
.map(&JSON.method(:parse)) | |
.select { |klass, method| method.start_with? to_complete.to_s } | |
end | |
private | |
attr_accessor :code, :ast, :line, :column, :index | |
def contains?(ast, index) | |
return false unless ast.kind_of? AST::Node | |
expr = ast.location.expression | |
expr.begin_pos <= index && index <= expr.end_pos | |
end | |
def find_ast(ast, index) | |
return nil unless contains?(ast, index) | |
ast.children.map { |child| find_ast child, index }.compact.first || ast | |
end | |
def competions_for_send(ast) | |
receiver_code = ast.location.expression.source | |
<<-RUBY | |
( receiver = (#{receiver_code}) | |
klass = receiver.class | |
begin klass = receiver.singleton_class | |
rescue TypeError | |
# you can't get the singleton class of every object | |
end | |
require 'json' | |
klass.ancestors.each do |ancestor| | |
ancestor.instance_methods(false).each do |method| | |
$SiB.record_result :inspect, #{line}, [ancestor.name, method], &:to_json | |
end | |
end | |
exit 0 | |
) | |
RUBY | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment