Adding simple macros to Textadept

DRY

Textadept is a very programmable text editor. It's as programmable as vim or emacs. Ok, maybe not. But it's much much smaller and simpler than they so even editing some of its less than 2,000 lines of C code is not a big deal. But mostly it's in Lua. The Lua code wraps much of internals of the editor and adds capabilities to build GUI, compose lexers and themes and — following vim/emacs tradition — to write configurations and plugins.

In this article we'll write a module for Textadept for simple macro recording and playback.

Keyboard events

In Textadept we can connect Lua function to any event using the code

events.connect(events.EVENT_NAME, function)

There're KEYPRESS and CHAR_ADDED events. KEYPRESS is fired on any key and receives key code and its modifiers (ctrl,alt,shift,meta), CHAR_ADDED fired only if KEYPRESS wasn't handled (no shortcut). In this case the key is passed to Scincilla. This behavior looks ok, but by default Textadpt dont create shortcuts for all of its actions, for example there're no shortcuts for navigation keys and they are handled directly by Scincilla. This complicates recording of macros so we'll fix this:

-- assign navigation keys to commands for proper recording
keys.left = buffer.char_left
keys.left = buffer.char_left
keys.sleft = buffer.char_left_extend
keys.cleft = buffer.word_left
keys.csleft = buffer.word_left_extend
keys.right = buffer.char_right
keys.sright = buffer.char_right_extend
keys.cright = buffer.word_right
keys.csright = buffer.word_right_extend
keys.up = buffer.line_up  
keys.sup = buffer.line_up_extend  
keys.down = buffer.line_down
keys.sdown = buffer.line_down_extend
keys.home = buffer.home
keys.shome = buffer.home_extend
keys['end'] = buffer.line_end
keys.send = buffer.line_end_extend
keys['\b'] = buffer.delete_back
keys['c\b'] = buffer.del_word_left
keys.del = buffer.clear
keys.cdel = buffer.del_word_right

Now all action keys have associated Lua functions, and we can easily record a macro.

Recording

To do this we need a couple of variables

local recording_macro = false
local macro = {}

and event handler for KEYPRESS event

events.connect(events.KEYPRESS, 
  function(code, shift, control, alt, meta)
    if recording_macro then
      table.insert(macro, {events.emit, {events.KEYPRESS, code, shift, control, alt, meta}})
    end  
  end, 1)

This handler just saves event arguments in table macro if we are recording_macro. This allows as to replay any actions executed by shortcuts.

Also, we add a similar CHAR_ADDED event handler to record letters typed by user too:

events.connect(events.CHAR_ADDED, function(byte)
  if recording_macro then
    table.insert(macro, {buffer.add_text, {string.char(byte)}})
  end
end)

Notice how flexible is Lua here: we can easily save the function to call and its parameters in one table. And later we'll call these functions in universal way too.

Module

To use our new code we would wrap it in module macro.

An module in Textadept is just a Lua file which is placed in ~/.textadept/modules and looks like:

local M = {}

--- body of the module --

return M

Later, we can load this module by local macro = require('macro').

What is the M returned by the module? It is API that we expose to editor. In our module we'll export three functions:

function M.record()
  macro = {}
  recording_macro = true
  ui.statusbar_text = 'recording macro'
end

function M.finish() 
  if recording_macro then
    recording_macro = false
    ui.statusbar_text = 'macro recorded'
  end
end

function M.replay()
  events.emit(events.KEYPRESS, 27, nil, nil, nil, nil)
  for k,cmd in pairs(macro) do
    cmd[1](unpack(cmd[2]))
  end
end

Notice that events.emit line in replay function. This is my workaround without which Textadept for some reason don't handle next commands. (27 is code of Esc button.)

Declared functions can now be accessed through names macro.record, macro.finish and macro.replay. We can link them to some shortcuts using the code like

keys.cr = macro.record
keys.cR = macro.finish
keys.ar = macro.replay

Ctrl+R, Ctrl+Shift+R, Alt+R.

Demonstration

Convert some list of numbers to array:

Links

ta-macro — the module on git.

Textadept home page http://foicica.com/textadept, where you can find a manual, full API reference, wiki and subscribe to the mailing list.

Lua home page www.lua.org, and there's a good text book "Programming in Lua".

Scincilla home page www.scintilla.org.

 

DRY, DRY, DRY]

 

shitpoet@gmail.com

 



 

free hit counters