class SublimeDSL::SublimeText::Keyboard
A keyboard.
Constants
- SUBLIME_ALIAS_MAP
- SUBLIME_ALIAS_RE
- SUBLIME_KEYS
- SUBLIME_MODIFIERS
Attributes
Public Class Methods
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 62 def self.get(name, root) file = nil Dir.chdir(root) do files = Dir['**/*.keyboard.rb'] file = SublimeText.order_config(files).last file or raise Error, "file '#{name}.keyboard.rb' not found" file = File.expand_path(file) end DSLReader.new(file)._keyboard end
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 77 def initialize(name) @name = name @os = nil @modifiers = [] @keys = [] @keystrokes_hash = {} # Vintage generic character add_keystroke CharStroke.new('<character>') end
The standard Sublime Text keyboard.
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 43 def self.sublime @sublime ||= begin kb = Keyboard.new('Sublime Text') SUBLIME_MODIFIERS.each { |name| kb.add_modifier name } SUBLIME_KEYS.each { |name| kb.add_key name } # map_char and map_key call this method while @sublime is not yet set unless @defining_sublime @defining_sublime = true # FIXME: space => key_event nil, but generates a key_event when modified, # because it's a character # => put it in SUBLIME_ALIAS_MAP, but remember it was named 'space' when # generating its to_s ('ctrl+ ' will generate problems...) kb.map_char 'space' => ' ', key_event: nil @defining_sublime = false end kb end end
Public Instance Methods
Create a Key
name
and adds the corresponding KeyStroke
to this keyboard.
-
If
name
is one character long, theKeyStroke
generates a chr_eventname
and no key_event. -
Otherwise, the
KeyStroke
generates no chr_event, and a key_eventname
ifname
is a Sublime Text key name.
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 124 def add_key(name) k = Key.new(name) k.st_name = name if SUBLIME_KEYS.include?(name) @keys << k ks = KeyStroke.new([], k) if name.length == 1 ks.chr_event = name else ks.key_event = k.st_name end add_keystroke ks k end
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 175 def add_keystroke(ks) @keystrokes_hash[ks.to_spec] = ks end
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 104 def add_modifier(name) m = Key.new(name) m.st_name = name if SUBLIME_MODIFIERS.include?(name) @modifiers << m end
Assign the default key_event of a new keystroke (before its registration). It will be the modified key_event of a less specific keystroke.
For instance, if we register 'shift+ctrl+keypad5', and 'shift+keypad5' has key event 'clear', this assigns 'ctrl+clear'. If there are several possibilities, the one(s) with the most modifiers are selected. If there are ex-aequo, the order of precedence is the order of registration of the modifiers.
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 285 def assign_default_key_event(keystroke) # the ST keyboard: just register if self == Keyboard.sublime spec = keystroke.to_spec keystroke.key_event = spec if spec.length > 1 return end # we always have modifiers, as all non-modified keys are already registered keystroke.modifiers.empty? and raise Error, "bug: #{keystroke} is not registered" # if the key has a ST name, assume the ST equivalent if keystroke.key.st_name spec = keystroke.modifiers.map(&:st_name).join('+') << '+' << keystroke.key.st_name keystroke.key_event = spec return end # the candidates are the registered keystrokes for that key with all # modifiers included in the passed modifiers (so at least the keystroke # for the key itself) candidates = keystrokes_hash.values.select do |ks| ks.key == keystroke.key && ks.modifiers - keystroke.modifiers == [] end candidates.empty? and raise Error, "bug: nothing registered for #{keystroke}" if candidates.length > 1 # select the one(s) with the most modifiers max = 0 candidates.each do |ks| max = ks.modifiers.length if ks.modifiers.length > max end candidates.reject! { |ks| ks.modifiers.length < max } # apply modifier priority: # create the bit mask for each keystroke, # and then select the lowest one if candidates.length > 1 sort_array = candidates.map do |ks| mask = 0 ks.modifiers.each do |m| mask += (1 << modifiers_index_hash[m.name]) end [ks, mask] end candidates = sort_array.sort_by(&:last).map(&:first) end end # select the reference keystroke ref = candidates.first if ref.key_event.nil? keystroke.key_event = nil return end # apply the modifier delta versus the reference delta = keystroke.modifiers - ref.modifiers spec = delta.map(&:st_name).join('+') << '+' << ref.key_event keystroke.key_event = Keyboard.sublime.ensure_keystroke(spec).to_spec end
Returns a KeyStroke
or CharStroke
for spec
, and adds it to the registered keystrokes if not already there. Raises an exception if spec
is not valid.
-
If
spec
is one character long, returns aKeyStroke
orCharStroke
if found, otherwise creates a newCharStroke
. -
If
spec
is more than one character long, the key has to exist, as well as the modifiers if any, otherwise an exception is raised. If the correspondingKeyStroke
is not found, it will be created and registered.
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 217 def ensure_keystroke(spec) # split the specification # ctrl++ -> ['ctrl', '+'] # ctrl+num+ -> ['ctrl', 'num+'] *modifier_names, key_name = spec.split(/\+(?!$)/) # normalize the key name self == Keyboard.sublime and key_name.sub! SUBLIME_ALIAS_RE, SUBLIME_ALIAS_MAP # check & reorder the modifiers unless modifier_names.empty? sorted = [] modifier_names.each do |name| i = modifiers_index_hash[name] or raise Error, "invalid modifier #{name.inspect} for keyboard #{self.name}" sorted[i] = name end modifier_names = sorted.compact end # if there is a registered keystroke for this spec, return it std_spec = [*modifier_names, key_name].join('+') ks = keystrokes_hash[std_spec] return ks if ks # shift + character is not ok modifiers = modifier_names.map { |n| modifier(n) } modifiers.map(&:st_name) == ['shift'] && key_name.length == 1 and raise Error, "#{spec.to_source(true)} is invalid: specify the corresponding character" key = key(key_name) # The ST keyboard accepts any character as a valid key if self == Keyboard.sublime && key.nil? key_name.length == 1 or raise Error, "invalid key name in #{spec.to_source(true)}" key = add_key(key_name) return keystrokes_hash[key_name] if modifiers.empty? end if key # registered key ks = KeyStroke.new(modifiers, key) assign_default_key_event ks else # unregistered: has to be a character key_name.length == 1 or raise Error, "#{spec.inspect}: key #{key_name.inspect} is undefined" modifier_names.empty? or raise Error, "#{spec.inspect}: #{key_name.inspect} is not a key" ks = CharStroke.new(key_name) end add_keystroke ks ks end
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 100 def key(name) keys.find { |k| k.name == name } end
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 183 def keystroke_for_sublime_spec(st_spec) st_ks = Keyboard.sublime.ensure_keystroke(st_spec) st_spec = st_ks.to_spec # standardize # return the first one with a key event = the passed spec this_ks = keystrokes.find { |ks| ks.key_event == st_spec } return this_ks if this_ks # if one char, no problem return ensure_keystroke(st_spec) if st_spec.length == 1 || st_spec == '<character>' # not (yet?) registered: find a keystroke with the same key base_ks = keystrokes.find { |ks| ks.key && ks.key.st_name == st_ks.key.name } if base_ks this_spec = st_ks.modifiers.map(&:name).join('+') << '+' << base_ks.key.name this_ks = ensure_keystroke(this_spec) return this_ks end NullStroke.new(st_spec) end
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 179 def keystrokes keystrokes_hash.values end
Map a keystroke to a chr_event. Optionally sets the key_event.
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 161 def map_char(options = {}) ks_name = options.keys.first ks = ensure_keystroke(ks_name) ks.chr_event = options[ks_name] if options.has_key?(:key_event) st_spec = options[:key_event] if st_spec st_ks = Keyboard.sublime.ensure_keystroke(st_spec) st_spec = st_ks.to_spec end ks.key_event = st_spec end end
Assigns the key_event for the keystroke spec
. st_spec
must be a valid ST keystroke, or nil if the keystroke is not seen by ST.
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 143 def map_key(spec, st_spec) ks = ensure_keystroke(spec) if st_spec.nil? ks.key_event = nil else st_ks = Keyboard.sublime.ensure_keystroke(st_spec) st_spec = st_ks.to_spec if ks.modifiers.empty? ks.key.st_name = st_spec ks.key_event = st_spec if st_spec.length > 1 else ks.key_event = st_spec end end end
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 110 def map_modifier(name, st_name) m = modifier(name) or raise Error, "unknown modifier: '#{name}'" SUBLIME_MODIFIERS.include?(st_name) or raise Error, "invalid ST modifier: #{st_name}" m.st_name = st_name end
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 96 def modifier(name) modifiers.find { |k| k.name == name } end
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 353 def modifiers_index_hash @modifiers_index_hash ||= begin h = {} modifiers.each_with_index { |m, i| h[m.name] = i } h end end
# File lib/sublime_dsl/sublime_text/keyboard.rb, line 87 def os=(value) case value.to_s.downcase when 'windows' then @os = 'Windows' when 'osx' then @os = 'OSX' when 'linux' then @os = 'Linux' else raise Error, "invalid OS value: #{value}" end end