[neomutt-users] Neomutt: now featuring Lua scripting!

Floyd Anderson f.a at 31c0.net
Sun Apr 30 13:47:52 CEST 2017

On Fr, 28 Apr 13:26:06 +0200
Guyzmo <z+mutt+neomutt at m0g.net> wrote:
>On Fri, Apr 28, 2017 at 03:31:12AM +0200, Floyd Anderson wrote:
>> On Fr, 28 Apr Guyzmo <z+mutt+neomutt at m0g.net> wrote:
>> I have a question about this. Will it be mandatory to mixing ‘normal’
>> {,Neo}mutt configuration directives with Lua code or can both be placed
>> in separated files and sourced from there? That probably would prevent (new)
>> user from getting confused, I think.
>well yes! as said in the first post of this thread you can either call Lua
>commands from Neomuttrc (or the `enter-command` line) by prefixing Lua code
>with the Neomuttrc `lua` command…
>Or you can source a lua script, within the context of Neomutt's Lua
>runtime using the `lua-source path/to/source-file.lua` command.

Oh yes, ‘lua-source’ was what I have thought of but I’ve overlooked it 
more than once. Sorry for the noise and thanks for the brief examples, 
next time I will be more attentively — hopefully. So with:

    ifndef USE_LUA finish
    lua-source /some/dir/neomutt-startup.lua

at the end of the run-command file, I got all what I need. ;-)

>Also, to make it a bit more comfortable to mix Muttrc and lua code, I'd
>like to add to the Neomuttrc parser a syntax (inspired by vim's) like:
>lua << EOS
>function fubar()
>  mutt.message("This is some multiline lua code")
>macro generic z :lua fubar()

I think, having alternate ways doing things is always fine as long as 
the realised result is the same. This way everyone can pick what meets 
the needs. Although I dislike mixing of configuration and code (at least 
in the same file), your example above is comfortable and really useful 
while working on both, configuration and code (e.g. reloading, testing). 

>> I’m sure I will when I found the way how to compile, install and testing
>> Neomutt without influencing my existing ‘old dog’. ;-)
>that's easy:
>wget https://github.com/neomutt/neomutt/archive/neomutt-20170421.tar.gz
>tar xvzf neomutt-20170421.tar.gz
>cd neomutt-neomutt-20170421
># I like to keep the compilation output in a separate directory
>mkdir build
># build all stable features, plus lua, and skip all doc and i18n
>../configure --enable-everything --enable-lua --disable-doc --disable-po --disable-full-doc

Thank you for pointing this out, that helps especially `mkdir build` and 
`make` (without ‘install’). It was a little bit bumpy (due to some other 
packages depends on the formerly installed Lua 5.1.5 version) but lastly 
I got it and here is my little interim conclusion:

# Working with Lua scripts

Within my sourced Lua start script I can do what I want, all works fine 
(e.g. overriding native Lua functions, set NeoMutt related search path 
via ‘package.path’, pull in pseudo Class modules with the ‘require()’ 
statement, extending global Lua table ‘_G’).

# Structure of mutt namespace

Before the mutt namespace grows up, I think it probably needs to be a 
little bit more structured, for instance:

    _G.mutt = {
        const = { VERSION = '', QUAD_NO = 0, QUAD_YES = 1, … },
        props = { … }, command = { … }, sidebar = { … },
        index = { … }, pager = { … },
        event = { OnFolderHook = function(mailbox),
                  OnSendHook = function(header), … },

This way constants like QUAD_* can be easier kept as read-only by users 
by manipulating the metatable of ‘const’. Currently the expressions:

    :lua mutt.QUAD_YES = 'The answer is 42!'
    :lua mutt:set('copy', mutt.QUAD_YES)

results as one does ‘set copy = no’. But on the other hand, if you would 
argue: “When a user put nonsense in, he should not wonder why nonsense 
comes out!“, I would say you are right — it’s just only an idea.

Another one is to let NeoMutt call user declared event handler until the 
queue of handler functions is empty or one of them returns true (means 
event is consumed). But for now something like:

    folder-hook .   :lua mutt.mailbox:defaults()

might do the job.

# Extending of mutt:set()

Since mutt:set() is a function, it can probably do more by default for 
users convenience and may also results in shorter, clearly Lua scripts.
For instance:

 1. reset an option to its default by passing a nil (not in list) value
        :lua mutt:set('option', nil)
 2. set a boolean option to true and return its current value afterwards
        local current = mutt:set('option', true)
 3. same as 2. but return its new and old value
        local new,old = mutt:set('option', true)
 4. same as 3. but return its new, old and default value
        local _,_,def = mutt:set('option', true)

The second and third can be realised in pure Lua. While the fourth is a 
little bit overkill, my favourite would be the second additionally with 
the capability from the first.

# Visual behaviour of global print()

With the Environment:

    NeoMutt:	neomutt-20170428 (1.8.2)
    Lua:		5.2.3
    Terminal:	rxvt-unicode (urxvt) and xfce4-terminal

I got a different (visual) behaviour on NeoMutt’s command line with 
Lua’s global print() function which should usually be the same as with 
mutt:print(). Currently a command like:

    :lua print('hello world!')

results in a command line which is two lines high (second one is empty) 
while the first looks like:

    :lua print('hello world!')hello world!

After invoking the ‘refresh’ or ‘redraw-screen’ command (dependent on 
mode), all looks as before.

These are just my first impressions with Lua enabled in NeoMutt. Keep up 
the great work and let me know if I can help within the limits of my 


More information about the neomutt-users mailing list