refactoring load-config and Config.struct
This commit is contained in:
parent
c9c6d8e79b
commit
5cf5765160
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
28
src/tmux.cr
28
src/tmux.cr
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
Loading…
Reference in New Issue