Created
October 22, 2018 03:27
-
-
Save elebow/4e0d2bad14f6c21d2c4698e38c543fdc to your computer and use it in GitHub Desktop.
xcompose-to-osx-defaultkeybindingdict.rb
This file contains hidden or 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
# Please don't even look at this horrible, write-only code | |
require 'parslet' | |
require 'pry' | |
class XComposeParser < Parslet::Parser | |
rule(:line) do | |
input.as(:input) >> spaces >> | |
match(':') >> spaces >> | |
output.as(:output) >> match['\n'].maybe | |
end | |
rule(:input) { str('<Multi_key>') >> spaces >> key.repeat.as(:keys) } | |
rule(:key) { str('<') >> key_char.as(:key_char) >> str('>') >> spaces } | |
rule(:key_char) { match['\w'].repeat } | |
rule(:output) { output_char >> match['^\n'].repeat.as(:char_desc) } | |
rule(:output_char) { str('"') >> any.as(:output_char) >> str('"') } | |
rule(:spaces) { match['\s'].repeat } | |
root :line | |
end | |
class SequenceTransform < Parslet::Transform | |
rule(key_char: simple(:x)) { x.to_s } | |
rule(output_char: simple(:x)) { x.to_s } | |
rule(char_desc: simple(:x)) { x.to_s } | |
end | |
class Converter | |
def initialize(filename) | |
@infile_name = filename | |
end | |
def convert | |
infile = File.new(@infile_name) | |
sequences = infile.readlines.map do |line| | |
next if line == "\n" || line.start_with?('#') | |
tree = parse(line) | |
SequenceTransform.new.apply(tree) | |
end.compact | |
@osx_sequences = {} | |
sequences.each do |sequence| | |
h = build_nested_hash(sequence[:input][:keys], sequence[:output][:output_char]) | |
begin | |
recursive_merge!(@osx_sequences, h) | |
rescue TypeError | |
STDERR.puts "string/hash collision with #{h}" | |
end | |
end | |
@osx_sequences = { '§' => @osx_sequences } | |
print_output | |
end | |
def parse(str) | |
parser = XComposeParser.new | |
parser.parse(str) | |
rescue Parslet::ParseFailed => failure | |
STDERR.puts str | |
STDERR.puts failure.parse_failure_cause.ascii_tree | |
end | |
def print_output | |
puts '{' | |
print_hashes_with_indentation(@osx_sequences, 1) | |
puts '}' | |
end | |
def recursive_merge!(h1, h2) | |
# https://rexchung.com/2007/02/01/hash-recursive-merge-in-ruby/ | |
h1.merge!(h2) do |_key, old, new| | |
if old.class == Hash | |
recursive_merge!(old, new) | |
else | |
new | |
end | |
end | |
end | |
def build_nested_hash(keys, output_char) | |
keys.push(insert_command_for_char(output_char)) | |
.reverse | |
.reduce { |tail, key| { key => tail } } | |
end | |
def insert_command_for_char(char) | |
"(\"insertText:\", \"#{char}\");" | |
end | |
def tr(ch) | |
{ | |
'ampersand' => '&', | |
'apostrophe' => '\'', | |
'asciicircum' => '\136', | |
'asciitilde' => '~', | |
'asterisk' => '*', | |
'at' => '@', | |
'backslash' => '\\\\', | |
'bar' => '|', | |
'braceleft' => '{', | |
'braceright' => '}', | |
'bracketleft' => '[', | |
'bracketright' => ']', | |
'colon' => ':', | |
'comma' => ',', | |
'equal' => '=', | |
'exclam' => '!', | |
'grave' => '`', | |
'greater' => '>', | |
'less' => '<', | |
'minus' => '-', | |
'numbersign' => '#', | |
'parenleft' => '(', | |
'parenright' => ')', | |
'period' => '.', | |
'plus' => '+', | |
'question' => '?', | |
'quotedbl' => '\"', | |
'slash' => '/', | |
'space' => ' ', | |
'underscore' => '_' | |
}.fetch(ch, ch) | |
end | |
def print_hashes_with_indentation(h, level) | |
h.each_pair do |key, value| | |
k = tr(key) | |
if value.instance_of?(Hash) | |
puts_level(level, "\"#{k}\" = {") | |
print_hashes_with_indentation(value, level + 1) | |
puts_level(level, '};') | |
else | |
puts_level(level, "\"#{k}\" = #{value}") | |
end | |
end | |
end | |
def puts_level(level, str) | |
puts "\t" * level + str | |
end | |
end | |
Converter.new('.XCompose').convert |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment