refactoring load-config and Config.struct

This commit is contained in:
Jorge Morante 2023-10-26 13:01:44 +02:00
parent c9c6d8e79b
commit 5cf5765160
10 changed files with 433 additions and 183 deletions

125
spec/lib/config_spec.cr Normal file
View File

@ -0,0 +1,125 @@
require "../spec_helper"
require "../../src/fingers/config"
describe Fingers::Config do
describe "errors" do
it "should valid when there are noerrors" do
conf = Fingers::Config.build
conf.valid?.should eq(true)
end
it "should not be valid when there are errors" do
conf = Fingers::Config.build
conf.errors << "shit"
conf.valid?.should eq(false)
end
it "errors should not be serialized" do
conf = Fingers::Config.build
has_errors = !!conf.to_json.match(/errors/)
has_errors.should eq(false)
end
end
describe "keyboard-layout" do
it "is valid for known layouts" do
conf = Fingers::Config.build
conf.keyboard_layout = "qwerty"
conf.valid?.should eq(true)
end
it "is should not include disallowed chars" do
conf = Fingers::Config.build
conf.keyboard_layout = "qwerty"
conf.alphabet.includes?("c").should eq(false)
conf.alphabet.includes?("i").should eq(false)
conf.alphabet.includes?("m").should eq(false)
conf.alphabet.includes?("q").should eq(false)
conf.alphabet.includes?("n").should eq(false)
end
it "is not valid for unknown layouts" do
conf = Fingers::Config.build
conf.keyboard_layout = "potato"
conf.valid?.should eq(false)
end
it "is qwerty by default" do
conf = Fingers::Config.build
conf.keyboard_layout.should eq("qwerty")
end
it "populates alphabet" do
conf = Fingers::Config.build
conf.alphabet.empty?.should eq(false)
end
end
describe "patterns" do
it "is valid for correct regexp" do
conf = Fingers::Config.build
conf.patterns = ["(foo|bar)"]
conf.valid?.should eq(true)
conf.patterns.size.should be > 0
end
it "is not valid for incorrect regexps" do
conf = Fingers::Config.build
conf.patterns = ["(unbalanced"]
conf.valid?.should eq(false)
end
it "is empty by default" do
conf = Fingers::Config.build
conf.patterns.size.should eq(0)
end
end
describe "styles" do
it "is valid for correct style" do
conf = Fingers::Config.build
conf.highlight_style = "fg=blue"
conf.valid?.should eq(true)
end
it "is not valid for incorrect style" do
conf = Fingers::Config.build
conf.highlight_style = "fg=shit"
conf.valid?.should eq(false)
end
end
describe "hint_position" do
it "is valid for correct value" do
conf = Fingers::Config.build
conf.hint_position = "left"
conf.valid?.should eq(true)
end
it "is not valid for incorrect value" do
conf = Fingers::Config.build
conf.hint_position = "behind"
conf.valid?.should eq(false)
end
end
describe "set_option" do
it "can set known options" do
conf = Fingers::Config.build
conf.set_option("keyboard_layout", "qwerty")
conf.valid?.should eq(true)
end
it "can set known options with invalid values" do
conf = Fingers::Config.build
conf.set_option("keyboard_layout", "caca")
conf.valid?.should eq(false)
end
it "is invalid when setting wrong option names" do
conf = Fingers::Config.build
conf.set_option("potato", "tomato")
conf.valid?.should eq(false)
end
end
end

View File

@ -0,0 +1,77 @@
require "../../../spec_helper.cr"
require "../../../../src/fingers/commands/load_config"
class FakeShell < Shell
@known_cmds = {} of String => String
def exec(cmd) : String
output = @known_cmds[cmd]?
return "" if cmd =~ /bind-key.*send-input/
if output.nil?
puts "Unknown cmd #{cmd}"
""
else
output
end
end
def expect(cmd, output)
@known_cmds[cmd] = output
end
def clear!
@known_cmds = {} of String => String
end
end
describe Fingers::Commands::LoadConfig do
it "can be instantiated" do
cmd = Fingers::Commands::LoadConfig.new(FakeShell.new)
end
it "can run" do
shell = FakeShell.new
cmd = Fingers::Commands::LoadConfig.new(shell: shell, executable_path: "/path/to/fingers", log_path: "/tmp/log_path")
shell.expect("tmux show-options -g | grep ^@fingers", "@fingers-key")
shell.expect("tmux show-option -gv @fingers-key", "F")
shell.expect("tmux -V", "3.3a")
shell.expect(%(tmux bind-key F run-shell -b "/path/to/fingers start '\#{pane_id}' self >>/tmp/log_path 2>&1"), "")
cmd.run
end
it "assigns options to config struct" do
shell = FakeShell.new
cmd = Fingers::Commands::LoadConfig.new(shell: shell, executable_path: "/path/to/fingers", log_path: "/tmp/log_path")
shell.expect("tmux show-options -g | grep ^@fingers", "@fingers-key")
shell.expect("tmux show-option -gv @fingers-key", "A")
shell.expect("tmux -V", "3.3a")
shell.expect(%(tmux bind-key A run-shell -b "/path/to/fingers start '\#{pane_id}' self >>/tmp/log_path 2>&1"), "")
cmd.run
Fingers.config.key.should eq("A")
end
it "propagates config errors" do
shell = FakeShell.new
output = IO::Memory.new
cmd = Fingers::Commands::LoadConfig.new(shell: shell, executable_path: "/path/to/fingers", log_path: "/tmp/log_path", output: output)
shell.expect("tmux show-options -g | grep ^@fingers", "@fingers-hint-style")
shell.expect("tmux show-option -gv @fingers-hint-style", "fg=caca")
shell.expect("tmux -V", "3.3a")
shell.expect(%(tmux bind-key F run-shell -b "/path/to/fingers start '\#{pane_id}' self >>/tmp/log_path 2>&1"), "")
shell.expect(%(tmux set-option -ug @fingers-hint-style), "")
cmd.run
output.rewind
cmd.errors.empty?.should eq(false)
(output.gets || "").size.should be > 0
end
end

View File

@ -1,7 +1,7 @@
require "spec" require "spec"
require "../../src/tmux_style_printer" require "../../src/tmux_style_printer"
class FakeShell < TmuxStylePrinter::Shell class FakeShell < Shell
def exec(cmd) def exec(cmd)
"$(#{cmd})" "$(#{cmd})"
end end

View File

@ -11,7 +11,7 @@ module Fingers
when "start" when "start"
Fingers::Commands::Start.new(args) Fingers::Commands::Start.new(args)
when "load-config" when "load-config"
Fingers::Commands::LoadConfig.new(args) Fingers::Commands::LoadConfig.new
when "send-input" when "send-input"
Fingers::Commands::SendInput.new(args) Fingers::Commands::SendInput.new(args)
when "version" when "version"

View File

@ -2,22 +2,30 @@ require "file_utils"
require "./base" require "./base"
require "../dirs" require "../dirs"
require "../config" require "../config"
require "../types"
require "../../tmux" require "../../tmux"
require "../../persistent_shell"
class Fingers::Commands::LoadConfig < Fingers::Commands::Base class Fingers::Commands::LoadConfig
@fingers_options_names : Array(String) | Nil @fingers_options_names : Array(String) | Nil
property config : Fingers::Config property config : Fingers::Config
property shell : Shell
property log_path : String
property executable_path : String
property errors : Array(String) = [] of String
property output : IO
DISALLOWED_CHARS = /cimqn/ def initialize(
@shell = PersistentShell.new,
def initialize(*args) @log_path = Fingers::Dirs::LOG_PATH.to_s,
super(*args) @executable_path = Process.executable_path.to_s,
@config = Fingers::Config.new @output = STDOUT
)
@config = Fingers::Config.build
end end
def run def run
validate_options!
parse_tmux_conf parse_tmux_conf
setup_bindings setup_bindings
end end
@ -31,75 +39,46 @@ class Fingers::Commands::LoadConfig < Fingers::Commands::Base
Fingers.reset_config Fingers.reset_config
config.tmux_version = `tmux -V`.chomp.split(" ").last config.tmux_version = shell.exec("tmux -V").chomp.split(" ").last
options.each do |option, value| options.each do |option, value|
# TODO generate an enum somehow and use an exhaustive case if option.match(/pattern_[0-9]+/)
case option user_defined_patterns << value
when "key" next
config.key = value
when "keyboard_layout"
config.keyboard_layout = value
when "main_action"
config.main_action = value
when "ctrl_action"
config.ctrl_action = value
when "alt_action"
config.alt_action = value
when "shift_action"
config.shift_action = value
when "benchmark_mode"
config.benchmark_mode = value
when "hint_position"
config.hint_position = value
when "hint_style"
config.hint_style = tmux.parse_style(value)
when "selected_hint_style"
config.selected_hint_style = tmux.parse_style(value)
when "highlight_style"
config.highlight_style = tmux.parse_style(value)
when "backdrop_style"
config.backdrop_style = tmux.parse_style(value)
when "selected_highlight_style"
config.selected_highlight_style = tmux.parse_style(value)
end end
if option.match(/pattern/) config.set_option(option, value)
check_pattern!(value)
user_defined_patterns.push(value) if !config.valid?
unset_tmux_option!(method_to_option(option))
output.puts "Found errors #{config.errors}"
self.errors = config.errors.clone
end end
end end
config.patterns = clean_up_patterns([ config.patterns = [
*enabled_default_patterns, *enabled_default_patterns,
*user_defined_patterns, *user_defined_patterns,
]) ]
config.alphabet = ::Fingers::Config::ALPHABET_MAP[Fingers.config.keyboard_layout].split("").reject do |char| if !config.valid?
char.match(DISALLOWED_CHARS) output.puts "Found errors #{config.errors}"
#exit(1)
end end
config.save config.save
Fingers.reset_config Fingers.reset_config
rescue e : TmuxStylePrinter::InvalidFormat
puts "[tmux-fingers] #{e.message}"
exit(1)
end
def clean_up_patterns(patterns)
patterns.reject(&.empty?)
end end
def setup_bindings def setup_bindings
`tmux bind-key #{Fingers.config.key} run-shell -b "#{cli} start "\#{pane_id}" self >>#{Fingers::Dirs::LOG_PATH} 2>&1"` shell.exec(%(tmux bind-key #{Fingers.config.key} run-shell -b "#{executable_path} start '\#{pane_id}' self >>#{log_path} 2>&1"))
`tmux bind-key O run-shell -b "#{cli} start "\#{pane_id}" other >>#{Fingers::Dirs::LOG_PATH} 2>&1"`
setup_fingers_mode_bindings setup_fingers_mode_bindings
end end
def setup_fingers_mode_bindings def setup_fingers_mode_bindings
("a".."z").to_a.each do |char| ("a".."z").to_a.each do |char|
next if char.match(DISALLOWED_CHARS) next if char.match(Fingers::Config::DISALLOWED_CHARS)
fingers_mode_bind(char, "hint:#{char}:main") fingers_mode_bind(char, "hint:#{char}:main")
fingers_mode_bind(char.upcase, "hint:#{char}:shift") fingers_mode_bind(char.upcase, "hint:#{char}:shift")
@ -124,79 +103,43 @@ class Fingers::Commands::LoadConfig < Fingers::Commands::Base
::Fingers::Config::DEFAULT_PATTERNS.values ::Fingers::Config::DEFAULT_PATTERNS.values
end end
def to_bool(input)
input == "1"
end
def shell_safe_options def shell_safe_options
options = {} of String => String options = {} of String => String
fingers_options_names.each do |option| fingers_options_names.each do |option|
option_method = option_to_method(option) option_method = option_to_method(option)
options[option_method] = `tmux show-option -gv #{option}`.chomp options[option_method] = shell.exec(%(tmux show-option -gv #{option})).chomp
end end
options options
end end
def valid_option?(option)
option_method = option_to_method(option)
@config.members.includes?(option_method) || option_method.match(/pattern_[0-9]+/) || option_method == "skip_wizard"
end
def fingers_options_names def fingers_options_names
@fingers_options_names ||= `tmux show-options -g | grep ^@fingers` @fingers_options_names ||= shell.exec(%(tmux show-options -g | grep ^@fingers))
.chomp.split("\n") .chomp.split("\n")
.map { |line| line.split(" ")[0] } .map { |line| line.split(" ")[0] }
.reject { |option| option.empty? } .reject { |option| option.empty? }
end end
def unset_tmux_option!(option) def unset_tmux_option!(option)
`tmux set-option -ug #{option}` shell.exec(%(tmux set-option -ug #{option}))
end
def check_pattern!(pattern)
begin
Regex.new(pattern)
rescue e: ArgumentError
puts "[tmux-fingers] Invalid pattern: #{pattern}"
puts "[tmux-fingers] #{e.message}"
exit(1)
end
end
def validate_options!
errors = [] of String
fingers_options_names.each do |option|
unless valid_option?(option)
errors << "'#{option}' is not a valid option"
unset_tmux_option!(option)
end
end
return if errors.empty?
puts "[tmux-fingers] Errors found in tmux.conf:"
errors.each { |error| puts " - #{error}" }
exit(1)
end end
def option_to_method(option) def option_to_method(option)
option.gsub(/^@fingers-/, "").tr("-", "_") option.gsub(/^@fingers-/, "").tr("-", "_")
end end
def fingers_mode_bind(key, command) def method_to_option(method)
`tmux bind-key -Tfingers "#{key}" run-shell -b "#{cli} send-input #{command}"` "@fingers-#{method.tr("_", "-")}"
end end
def cli def fingers_mode_bind(key, command)
Process.executable_path shell.exec(%(tmux bind-key -Tfingers "#{key}" run-shell -b "#{executable_path} send-input #{command}"))
end end
def tmux def tmux
Tmux.new(`tmux -V`.chomp.split(" ").last) Tmux.new(shell.exec("tmux -V").chomp.split(" ").last)
end end
end end

View File

@ -1,42 +1,10 @@
require "json" require "json"
require "../tmux_style_printer"
module Fingers module Fingers
struct Config struct Config
include JSON::Serializable
property key : String
property keyboard_layout : String
property patterns : Array(String)
property alphabet : Array(String)
property benchmark_mode : String
property main_action : String
property ctrl_action : String
property alt_action : String
property shift_action : String
property hint_position : String
property hint_style : String
property selected_hint_style : String
property highlight_style : String
property selected_highlight_style : String
property backdrop_style : String
property tmux_version : String
FORMAT_PRINTER = TmuxStylePrinter.new FORMAT_PRINTER = TmuxStylePrinter.new
DEFAULT_PATTERNS = {
"ip": "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}",
"uuid": "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
"sha": "[0-9a-f]{7,128}",
"digit": "[0-9]{4,}",
"url": "((https?://|git@|git://|ssh://|ftp://|file:///)[^\\s()\"]+)",
"path": "(([.\\w\\-~\\$@]+)?(/[.\\w\\-@]+)+/?)",
"hex": "(0x[0-9a-fA-F]+)",
"kubernetes": "(deployment.app|binding|componentstatuse|configmap|endpoint|event|limitrange|namespace|node|persistentvolumeclaim|persistentvolume|pod|podtemplate|replicationcontroller|resourcequota|secret|serviceaccount|service|mutatingwebhookconfiguration.admissionregistration.k8s.io|validatingwebhookconfiguration.admissionregistration.k8s.io|customresourcedefinition.apiextension.k8s.io|apiservice.apiregistration.k8s.io|controllerrevision.apps|daemonset.apps|deployment.apps|replicaset.apps|statefulset.apps|tokenreview.authentication.k8s.io|localsubjectaccessreview.authorization.k8s.io|selfsubjectaccessreviews.authorization.k8s.io|selfsubjectrulesreview.authorization.k8s.io|subjectaccessreview.authorization.k8s.io|horizontalpodautoscaler.autoscaling|cronjob.batch|job.batch|certificatesigningrequest.certificates.k8s.io|events.events.k8s.io|daemonset.extensions|deployment.extensions|ingress.extensions|networkpolicies.extensions|podsecuritypolicies.extensions|replicaset.extensions|networkpolicie.networking.k8s.io|poddisruptionbudget.policy|clusterrolebinding.rbac.authorization.k8s.io|clusterrole.rbac.authorization.k8s.io|rolebinding.rbac.authorization.k8s.io|role.rbac.authorization.k8s.io|storageclasse.storage.k8s.io)[[:alnum:]_#$%&+=/@-]+",
"git-status": "(modified|deleted|new file): +(?<match>.+)",
"git-status-branch": "Your branch is up to date with '(?<match>.*)'.",
"diff": "(---|\\+\\+\\+) [ab]/(?<match>.*)",
}
ALPHABET_MAP = { ALPHABET_MAP = {
"qwerty": "asdfqwerzxcvjklmiuopghtybn", "qwerty": "asdfqwerzxcvjklmiuopghtybn",
"qwerty-homerow": "asdfjklgh", "qwerty-homerow": "asdfjklgh",
@ -60,24 +28,154 @@ module Fingers
"colemak-right-hand": "neioluymjhk", "colemak-right-hand": "neioluymjhk",
} }
def initialize( DISALLOWED_CHARS = /[cimqn]/
@key = "F",
@keyboard_layout = "qwerty", DEFAULT_PATTERNS = {
@alphabet = [] of String, "ip": "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}",
@patterns = [] of String, "uuid": "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
@main_action = ":copy:", "sha": "[0-9a-f]{7,128}",
@ctrl_action = ":open:", "digit": "[0-9]{4,}",
@alt_action = "", "url": "((https?://|git@|git://|ssh://|ftp://|file:///)[^\\s()\"]+)",
@shift_action = ":paste:", "path": "(([.\\w\\-~\\$@]+)?(/[.\\w\\-@]+)+/?)",
@hint_position = "left", "hex": "(0x[0-9a-fA-F]+)",
@hint_style = FORMAT_PRINTER.print("fg=green,bold"), "kubernetes": "(deployment.app|binding|componentstatuse|configmap|endpoint|event|limitrange|namespace|node|persistentvolumeclaim|persistentvolume|pod|podtemplate|replicationcontroller|resourcequota|secret|serviceaccount|service|mutatingwebhookconfiguration.admissionregistration.k8s.io|validatingwebhookconfiguration.admissionregistration.k8s.io|customresourcedefinition.apiextension.k8s.io|apiservice.apiregistration.k8s.io|controllerrevision.apps|daemonset.apps|deployment.apps|replicaset.apps|statefulset.apps|tokenreview.authentication.k8s.io|localsubjectaccessreview.authorization.k8s.io|selfsubjectaccessreviews.authorization.k8s.io|selfsubjectrulesreview.authorization.k8s.io|subjectaccessreview.authorization.k8s.io|horizontalpodautoscaler.autoscaling|cronjob.batch|job.batch|certificatesigningrequest.certificates.k8s.io|events.events.k8s.io|daemonset.extensions|deployment.extensions|ingress.extensions|networkpolicies.extensions|podsecuritypolicies.extensions|replicaset.extensions|networkpolicie.networking.k8s.io|poddisruptionbudget.policy|clusterrolebinding.rbac.authorization.k8s.io|clusterrole.rbac.authorization.k8s.io|rolebinding.rbac.authorization.k8s.io|role.rbac.authorization.k8s.io|storageclasse.storage.k8s.io)[[:alnum:]_#$%&+=/@-]+",
@highlight_style = FORMAT_PRINTER.print("fg=yellow"), "git-status": "(modified|deleted|new file): +(?<match>.+)",
@selected_hint_style = FORMAT_PRINTER.print("fg=blue,bold"), "git-status-branch": "Your branch is up to date with '(?<match>.*)'.",
@selected_highlight_style = FORMAT_PRINTER.print("fg=blue"), "diff": "(---|\\+\\+\\+) [ab]/(?<match>.*)",
@backdrop_style = "", }
@tmux_version = "",
@benchmark_mode = "0" def self.build
) from_json("{}")
end
def self.alphabet_for(layout)
ALPHABET_MAP[layout].split("").reject { |char| char =~ DISALLOWED_CHARS }
end
def self.parse_style(style)
FORMAT_PRINTER.print(style)
end
include JSON::Serializable
property key : String = "F"
getter keyboard_layout : String = "qwerty"
def keyboard_layout=(value)
if !ALPHABET_MAP[value]?
errors << "Invalid layout #{value}"
return
end
@keyboard_layout = value
end
def alphabet
self.class.alphabet_for(keyboard_layout)
end
getter patterns : Array(String) = [] of String
def patterns=(value)
value.each do |pattern|
error = Regex.error?(pattern)
if error
@errors << "Invalid regexp\n\t#{pattern}\n\t#{error}"
return
end
end
@patterns = value
end
getter highlight_style : String = parse_style("fg=green,bold")
def highlight_style=(value)
parsed_style = parse_style!(value)
@highlight_style if parsed_style
end
def parse_style!(style)
begin
self.class.parse_style(style)
rescue TmuxStylePrinter::InvalidFormat
@errors << "Invalid style: #{style}"
end
end
getter highlight_style : String = parse_style("fg=yellow")
def highlight_style=(value)
parsed_style = parse_style!(value)
@highlight_style if parsed_style
end
getter hint_style : String = parse_style("fg=green,bold")
def hint_style=(value)
parsed_style = parse_style!(value)
@hint_style if parsed_style
end
getter selected_highlight_style : String = parse_style("fg=blue")
def highlight_style=(value)
parsed_style = parse_style!(value)
@highlight_style if parsed_style
end
getter selected_hint_style : String = parse_style("fg=blue,bold")
def hint_style=(value)
parsed_style = parse_style!(value)
@hint_style if parsed_style
end
getter backdrop_style : String = ""
def hint_style=(value)
parsed_style = parse_style!(value)
@backdrop_style if parsed_style
end
def parse_style!(style)
begin
self.class.parse_style(style)
rescue TmuxStylePrinter::InvalidFormat
@errors << "Invalid style: #{style}"
end
end
property tmux_version : String = ""
property main_action : String = ":copy:"
property ctrl_action : String = ":open:"
property alt_action : String = ""
property shift_action : String = ":paste: "
getter hint_position : String = "left"
def hint_position=(value)
if !["left", "right"].includes?(value)
@errors << "Invalid hint_position #{value}"
end
@hint_position = value
end
property benchmark_mode : String = "0"
property skip_wizard : String = "0"
@[JSON::Field(ignore: true)]
property errors : Array(String) = [] of String
def valid?
errors.empty?
end
macro define_set_option
def set_option(option : String, value : String | Array(String))
case option
{% for method in @type.methods %}
{% if method.name.split("").last == "=" && method.name != "patterns=" && method.name != "errors=" %}
when "{{method.name.gsub(/=$/, "")}}"
self.{{method.name}} value
{% end %}
{% end %}
else
errors << "#{option} is not a valid option"
end
end
end end
def self.load_from_cache def self.load_from_cache
@ -91,12 +189,16 @@ module Fingers
def members : Array(String) def members : Array(String)
JSON.parse(to_json).as_h.keys JSON.parse(to_json).as_h.keys
end end
macro finished
define_set_option
end
end end
def self.config def self.config
@@config ||= Config.load_from_cache @@config ||= Config.load_from_cache
rescue rescue
@@config ||= Config.new @@config ||= Config.build
end end
def self.reset_config def self.reset_config

View File

@ -8,3 +8,7 @@ module Fingers
abstract def format(hint : String, highlight : String, selected : Bool, offset : Tuple(Int32, Int32) | Nil) abstract def format(hint : String, highlight : String, selected : Bool, offset : Tuple(Int32, Int32) | Nil)
end end
end end
abstract class Shell
abstract def exec(cmd)
end

27
src/persistent_shell.cr Normal file
View File

@ -0,0 +1,27 @@
require "./fingers/types"
class PersistentShell < Shell
def initialize
@sh = Process.new("/bin/sh", input: :pipe, output: :pipe, error: :close)
end
def exec(cmd)
ch = Channel(String).new
spawn do
output = ""
while line = @sh.output.read_line
break if line == "cmd-end"
output += "#{line}\n"
end
ch.send(output)
end
@sh.input.print("#{cmd}; echo cmd-end\n")
@sh.input.flush
output = ch.receive
output
end
end

View File

@ -39,32 +39,6 @@ end
# rubocop:disable Metrics/ClassLength # rubocop:disable Metrics/ClassLength
class Tmux class Tmux
class Shell
def initialize
@sh = Process.new("/bin/sh", input: :pipe, output: :pipe, error: :close)
end
def exec(cmd)
ch = Channel(String).new
spawn do
output = ""
while line = @sh.output.read_line
break if line == "cmd-end"
output += "#{line}\n"
end
ch.send(output)
end
@sh.input.print("#{cmd}; echo cmd-end\n")
@sh.input.flush
output = ch.receive
output
end
end
struct Pane struct Pane
include JSON::Serializable include JSON::Serializable
@ -130,7 +104,7 @@ class Tmux
end end
def initialize(version_string) def initialize(version_string)
@sh = Shell.new @sh = PersistentShell.new
@version = Tmux.tmux_version_to_semver(version_string) @version = Tmux.tmux_version_to_semver(version_string)
end end

View File

@ -1,12 +1,10 @@
require "./fingers/types"
class TmuxStylePrinter class TmuxStylePrinter
class InvalidFormat < Exception class InvalidFormat < Exception
end end
abstract class Shell
abstract def exec(cmd)
end
STYLE_SEPARATOR = /[ ,]+/ STYLE_SEPARATOR = /[ ,]+/
COLOR_MAP = { COLOR_MAP = {