Skip to content

Instantly share code, notes, and snippets.

@krainboltgreene
Created April 1, 2019 15:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krainboltgreene/cd78670507eb5bc162a26e6c59c20e75 to your computer and use it in GitHub Desktop.
Save krainboltgreene/cd78670507eb5bc162a26e6c59c20e75 to your computer and use it in GitHub Desktop.
require "highline"
class RenderingEngine < Concurrent::Actor::Context
attr_reader :search
attr_reader :terminal
attr_reader :previous
attr_reader :current
def initialize()
@search = SearchEngine.spawn(:search)
@terminal = TerminalEngine.spawn(:terminal)
@previous = []
@current = []
end
def on_message(_)
render
collect
push
end
private def collect
terminal.tell("catch")
end
private def push
search.tell(["push", terminal.ask!("pull")])
terminal.ask("clear")
end
private def render
Rails.logger.debug("Rendering")
flush
pad
report unless empty?
loading if waiting?
idle
pipe if different?
end
private def pad
output("\n" * (terminal.ask!("height") + 1), false)
end
private def loading
output("Search...", true)
end
private def empty?
search.ask!("empty?")
end
private def waiting?
search.ask!("waiting?")
end
private def idle
output("Search: #{search.ask!("query")}", false)
end
private def report
Rails.logger.debug("#{self.class}: Printing out results")
output(search.ask!("results"), true)
end
private def output(value, newline)
current << [value, newline]
end
private def different?
previous != current
end
private def flush
@previous = current.dup
@current = []
end
private def pipe
current.each.with_index do |(value, newline), index|
if newline
Rails.logger.info(value + "\n")
else
Rails.logger.info(value)
end
end
end
end
class TerminalEngine < Concurrent::Actor::Context
attr_reader :client
attr_accessor :input
def initialize()
@client = HighLine.new.terminal
@input = nil
end
def on_message(event)
case event
when "height" then client.terminal_size.last
when "catch" then Concurrent::Promises.future do
client.raw_no_echo_mode
self.input = client.get_character.chomp.tap do
client.restore_mode
end
end
when "pull" then input
when "clear" then self.input = nil;
else pass
end
end
end
class SearchEngine < Concurrent::Actor::Context
DELETE = "\u007F"
EXIT = "\u0003"
attr_reader :query
attr_accessor :waiting
def initialize
@results = nil
@query = ""
@waiting = false
end
def on_message(event)
if event.is_a?(Array) && event.first == "push"
lookup(event.last)
else
case event
when "waiting?"
waiting
when "empty?"
results.empty?
when "query"
query
when "results"
results
else pass
end
end
end
def lookup(input)
return if input.nil? || input.empty?
return if input.include?(":")
exit if input.include?(EXIT)
return if input.include?(DELETE) && query.each_char.size.zero?
if input.include?(DELETE)
Rails.logger.debug("#{self.class}: Deleting last character (#{query} -> #{query.each_char.to_a.first(query.size - 1).join("")})")
@query = query.each_char.to_a.first(query.size - 1).join("")
else
Rails.logger.debug("#{self.class}: Appending input to the query (#{input})")
@query += input
end
return Rails.logger.debug("#{self.class}: Query is too small") if query.size <= 3
Rails.logger.debug("#{self.class}: Waiting for results")
self.waiting = true
Concurrent::Promises.future do
@results = Search.find(query)
Rails.logger.debug("#{self.class}: Results came back")
self.waiting = false
end
end
private def results
if @results.present?
@results.to_tables
else
""
end
end
end
Rails.logger.debug("Starting render")
engine = RenderingEngine.spawn(:output)
engine.tell(:empty)
while true
engine.tell(:empty) && sleep(1)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment