howl.editing.auto_pair

handle(event, editor)

local buffer, editor, cursor

event = (character, key_name = character) -> :character, :key_name, key_code: 65

before_each ->
  buffer = Buffer!
  editor = Editor buffer
  cursor = editor.cursor
  buffer.text = ''

returns non-true when the character does not match a known pair

assert.is_not_true auto_pair.handle event('x'), editor

skips processing if the auto_pair config variable is false

buffer.mode.auto_pairs = { ['(']: ')' }
buffer.config.auto_pair = false
assert.is_not_true auto_pair.handle event('('), editor
assert.equal '', buffer.text

skips processing if the buffer is read only

buffer.mode.auto_pairs = { ['(']: ')' }
buffer.read_only = true
assert.is_not_true auto_pair.handle event('('), editor
assert.equal '', buffer.text

takes sub modes into account

buffer.mode.auto_pairs = { ['(']: ')' }
mode2 = auto_pairs: { ['[']: ']' }
mode2_reg = name: 'auto_pair_test', create: -> mode2
howl.mode.register mode2_reg

buffer.text = '('
buffer._buffer.styling\apply 1, {
  1, { 1, 's1', 2 }, 'auto_pair_test|s1',
}
assert.is_true auto_pair.handle event('['), editor

howl.mode.unregister 'auto_pair_test'

(when the character matches a known pair from buffer.mode.auto_pairs)

before_each ->
  buffer.mode.auto_pairs = {
    '(': ')'
    '[': ']'
    '"': '"'
  }

(.. when there is an active selection)

before_each ->
  buffer.text = ' foo '
  editor.selection\set 2, 5

surrounds the selection with the pair as one undo operation

auto_pair.handle event('('), editor
assert.equal ' (foo) ', buffer.text
buffer\undo!
assert.equal ' foo ', buffer.text

returns true

assert.is_true auto_pair.handle event('('), editor

(.. with no selection active)

returns true

assert.is_true auto_pair.handle event('('), editor

inserts the pair in the buffer, as one undo operation

for start_c, end_c in pairs buffer.mode.auto_pairs
  auto_pair.handle event(start_c), editor
  assert.equal "#{start_c}#{end_c}", buffer.text
  buffer\undo!
  assert.equal '', buffer.text

positions the cursor within the pair

auto_pair.handle event('['), editor
assert.equal 2, cursor.pos

does not trigger for a same character pair if the current balance is uneven

buffer.text = '"foo'
cursor.pos = 5
assert.is_not_true auto_pair.handle event('"'), editor

does not trigger when the next character is a word character

buffer.text = 'foo'
cursor.pos = 1
assert.is_not_true auto_pair.handle event('('), editor

(overtyping companion characters)

before_each ->
  buffer.mode.auto_pairs = {
    '(': ')'
    '"': '"'
  }

overtypes any companion characters if the current pair-balance is even

buffer.text = '()'
cursor.pos = 2
assert.is_true auto_pair.handle event(')'), editor
assert.equal '()', buffer.text
assert.equal 3, cursor.pos

overtypes any companion characters for even pair-balance when the start characters and end character is the same

buffer.text = '""'
cursor.pos = 2
assert.is_true auto_pair.handle event('"'), editor
assert.equal '""', buffer.text
assert.equal 3, cursor.pos

does not overtype if the current pair-balance is non-even

buffer.text = '(foo'
cursor.pos = 5
assert.is_not_true auto_pair.handle event(')'), editor

does not overtype if the current character is different

buffer.text = '(foo)'
cursor.pos = 6
assert.is_not_true auto_pair.handle event(')'), editor

(deleting back inside a pair)

before_each -> buffer.mode.auto_pairs = ['(']: ')'

returns true

buffer.text = '()'
cursor.pos = 2
assert.is_true auto_pair.handle event('\8', 'backspace'), editor

deletes both characters as one undo

buffer.text = '()'
cursor.pos = 2
auto_pair.handle event('\8', 'backspace'), editor
assert.equal '', buffer.text
buffer\undo!
assert.equal '()', buffer.text

does nothing if a selection is active

buffer.text = 'x()'
editor.selection\set 1, 3
assert.is_not_true auto_pair.handle event('\8', 'backspace'), editor
assert.equal 'x()', buffer.text