Scite Ext Man

lua-users home
wiki

Making Scripts Play Nicely Together

This latest version (2006-11-10) corrects a serious bug in the Linux version, and uses a more robust scheme to determine whether an entered line is in the editor or output pane. It also allows you to reload Lua scripts controlled by extman with 'Reload Script' (Ctrl+Shift+R). There are some extra examples included: * prompt.lua, which provides a simple interactive Lua prompt. * select_string.lua, which allows you to select the whole of a string (or a comment) with a double-click * select_block.lua, which allows you to select blocks by a margin click next to a fold line.

You can find extman at Files:wiki_insecure/editors/SciTE/extman.lua; the examples and code are in Files:wiki_insecure/editors/SciTE/extman.zip; it is entirely written in Lua and works with any recent SciTE release.

The SciTE Lua interface is very powerful, but presently there is no way to make non-trivial scripts 'play nice' with each other. Consider SciteBufferSwitch; the handlers OnOpen,OnSwitchFile and OnUserListSelection are all overriden to keep track of buffer changes and present a drop-down list of buffers. Such a script would interfere with any other script that had a need to watch these events.

Using extman, this script looks like this (the function buffer_switch is the same)

scite_OnOpen(buffer_switch)
scite_OnSwitchFile(buffer_switch)

scite_Command 'Switch Buffer|do_buffer_list|Ctrl+J'

function do_buffer_list()
     scite_UserListShow(buffers,2,scite.Open)
end

(The latest version of extman also provides OnOpenSwitch, which is called when a file is made active, either by opening or by switching buffers. Have a look at the switch_buffers.lua example)

Internally, extman keeps handler lists. For instance, scite_OnOpen will add a function to the list of handlers called by the OnOpen event. It is now perfectly possible for another script to listen to the OnOpen event, without causing conflict. In a similar fashion, there is scite_OnChar, scite_OnSave, etc - except for OnUserListSelection, which is handled differently.

Extended Events

In addition to the standard SciTE Lua events, extman provides OnWord, OnEditorLine and OnOutputLine. They are built on the basic events, and are included for convenience.

Here is the 'lazy' word substitution example rewritten for extman. The OnWord handler receives a table, which has fields word, startp, endp, ch which are respectively the word found, its initial position, the final position, and the character found immediately after the word.

function on_word(w)
  local subst = word_substitute(w.word)
  if subst ~= w.word then
         editor:SetSel(w.startp-1,w.endp-1)
         local was_whitespace = string.find(w.ch,'%s')
         if was_whitespace then
            subst = subst..w.ch
         end
         editor:ReplaceSel(subst)
         local word_end = editor.CurrentPos
         if not was_whitespace then
            editor:GotoPos(word_end + 1)
         end
      end
end  

scite_OnWord(on_word)

OnOutputLine only fires when a line is typed into the output pane. Here is a simple but effective Lua console for SciTE:

 local prompt = '> '
 print 'Scite/Lua'
 trace(prompt)
  
 scite_OnOutputLine (function (line)
   local sub = string.sub
   if sub(line,1,2) == prompt then
        line = sub(line,3)
    end	
    if sub(line,1,1) == '=' then
        line = 'print('..sub(line,2)..')'
    end    
    local f,err = loadstring(line,'local')
    if not f then 
      print(err)
    else
      local ok,res = pcall(f)
      if ok then
         if res then print('result= '..res) end
      else
         print(res)
      end      
    end
    trace(prompt)
    return true
end)

OnEditorLine is a similar event, except it happens when the user enters a line in the editor pane. One key difference is that it never interferes with normal character processing. One could use it to keep track of any declarations typed, etc. The following example is fairly strange, but shows how one can bind a shortcut followed by a letter to an operation.

 scite_Command 'AltX|do_altx_commands|Alt+X'

 function do_altx_commands()
    editor:BeginUndoAction()
    scite_OnChar('once',function (ch)
       editor:EndUndoAction()
       editor:Undo()
       if ch == 's' then
          print('ess')
       elseif ch == 'f' then
          editor:BeginUndoAction()
          scite_OnEditorLine(handle_line)
        end
       return true
    end)
  end

After you type Alt+X, this function installs a run-once OnChar handler. It's only interested in 's' or 'f', but always 'eats up' the next character pressed. Emacs users may find such key combinations fairly natural, and they're probably easier to type than Alt+Ctrl+Shift combinations. OnChar will not see special characters, so one is limited to letters and punctuation. (My fingers still remember the Ctrl+Q followed by a digit to move to a marker in the Borland environments - see SciteNumberedBookmarks).

Alt+X followed by 'f' is meant to allow a user to enter a filename in the buffer! The filename is immediately removed by editor:Undo and the file opened.

 local function handle_line(line)
    editor:EndUndoAction()
    editor:Undo()
    scite_OnEditorLine(handle_line,'remove')
    scite.Open(line)	  
 end

Utility Functions

extman also supplies some useful utility functions. In some cases (like file access) they make up for missing functionality in the Lua library. If (for instance) SciTE includes the lfs (Lua File System), then users can continue to use scite_Files even although the implementation changes.

scite_UserListShow(list,start,fn) is a simplified way to access Scintilla userlists - it will construct a properly separated string, etc. You can specify a start index for the list - here I've used it to avoid showing the current buffer.

scite_GetProp(key,default) is a simple wrapper around the props pseudo-table. If a property key doesn't exist, then props[key] returns an empty string, not nil; if default isn't specified, then this function will indeed return nil if the property doesn't exist.

scite_Files(mask) returns all the files in the supplied path+mask (e.g. "d:/downloads/scite_lua/*.lua" - forward slashes are accepted on Windows as well). If the SciteOther library is found, then it will use the quiet Execute, otherwise os.execute.

scite_FileExists(f) returns true if the file can be opened for reading.

scite_dofile(f) is like dofile, except that it always loads files relative to SciTE's default home, and fails quietly.

scite_Command(cmds) is a very useful function for associating a Lua function with a Tools menu item and key shortcut. You either pass it a string, or a list of strings; the string is of the form <name>|<function>|<shortcut>, where <shortcut> is optional.

Installation and Initialization

Unzip the files in your SciTE directory, remembering to preserve folder names. Extman is meant to be the main Lua startup script (you can of course put it somewhere else)

ext.lua.startup.script=$(SciteDefaultHome)/extman.lua

On startup, it will look for all files with a .lua extension inside the scite_lua directory, which by default is in the default home directory. You can force it elsewhere with ext.lua.directory.

These files will be loaded, so you should *not* put extman.lua in that directory! They will have a chance to call scite_Command to register their functions. They may well need other scripts loaded in advance, so scite_require() has been added. If a file has been loaded explicitly with this function, then extman will consider it loaded.

Enabling Scripts to Optionally Use extman

The snippet of code below allows a Lua extension to optionally use extman for OnChar, enabling extman-capable scripts to still work without extman.

It first checks to see if a handler already exists. Next, if an extman function is missing, say scite_OnChar, a very simple replacement is created. Of course, this test for extman is not foolproof. The rest of the code can then utilize extman functions as if everything is normal. The simple scite_OnChar function can only handle one handler; anything much more complex and you might as well force the user to install extman.

if OnChar and not scite_OnChar then
  error("Please use extman if you want to run more than one handler")
elseif not scite_OnChar then
  local _OnChar
  scite_OnChar = function(f, remove)
    if remove then _OnChar = nil else _OnChar = f end
  end
  OnChar = function(c) if _OnChar then return _OnChar(c) end end
end

--SteveDonovan

User Comments

Extman is great, although I'd prefer if I could control where it generated the scite_temp1 file. I usually create it once and comment out the generation code and then redirect extman to open the precreated file in the directory where the scite executable exists.

Was trying out the switch_headers.lua that comes with extman.zip using SciTE 1.74 and had some problems running. Had to change the following lines. From:

   for i,v in list do
To:
   for i,v in pairs(list) do
and From:
   for i,ext in extensions do
To:
   for i,ext in pairs (extensions) do


Here's a patch (created using diff) for switch_buffers.lua to eliminate the Untitled tab (blank line in buffer list):
9a10
>    if string.find(f,'[\\/]$') then return end
11a13
>   if (f ~= "") then
20a23
>   end
30c33,35
<      scite_UserListShow(buffers,2,scite.Open)
---
>    if (table.getn(buffers) > 0) then
>      scite_UserListShow(buffers,1,scite.Open)
>    end


I ran into a problem on Windows where scite_Files matched both test.lua and test.lua~ against *.lua and loaded both (the latter overriding the former). The latter was a temporary backup copy saved by Emacs, and it took some time to determine why my edits to test.lua were being loaded but not having any effect. Under Windows, I suggest using a dir *.* to match all files in a directory and then filtering the list using the globtopattern function in FileGlob.

BTW, SciteDebug [1] has a different implementation of scite_Files in extman.lua. It calls scite_Popen, which selectively wraps either spawner.popen or os.execute depending on availability. I'm not sure the full purpose of that, but it does avoid the DOS command window that momentarily may pop up when calling this function (at least when I tried it). It may be useful for the two versions of extman.lua to be merged. --DavidManura

-- In reply to the above comment on the other version of extman.lua, if I remember properly, spawner.popen uses a shared library (a dll on Windows). It was created to handle the issue of the DOS command window opening and closing and the SciTE program losing focus in Windows because of it. It still had a noticeable visual side-effect when running, but it is better than os.execute since focus isn't lost. My personal preference is to generate scite_temp1 once whenever the number of scripts in my lua directory actually change and read the file directly. That prevents having to shell out using either method and having some kind of visual disruption whenever I open SciTE.

Just ran a search for latest extman.lua code on the web and didn't realize there are now quite a few versions of it, some with major extensions. It would be nice if someone coule put together a new wiki page with one version including the best of all those features or at least could add a link here to a version on the web somewhere that is the most up-to-date and works best with the most lua scripts. --(Lua User)

In my opinion, files should be manually included instead of including everything from a folder. This also removes the need of using spawner dll or viewing a temporary command prompt. For an example, you see my mini version of extman.lua in SciteReadTags. --ParsKorata


Auto Continue Comments

Here is a script to, ... well, auto continue comments!

Save it in the ../scite_lua/ folder as <filename>.lua and restart SciTE. (It should be platform independent!)

--auto_continue_comments.lua

--determine correct eol marker (later) from this lookup table
local eol_tbl = {[0]="\r\n", [1]="\r", [2]="\n"}

function auto_continue_comment(line)

    -- Multiline comment continue
    local multi = line:match("^(%s*)==%s*$")
    if multi then --line contains "==", start multiline comment
        local eol = eol_tbl[editor.EOLMode]
        editor:BeginUndoAction()
            editor:LineUp()
            editor:LineDelete()
            editor:AddText("--[=[" .. eol .. eol .. "--]=]" .. eol)
            editor:LineUp(); editor:LineUp()
            editor:AddText(multi)
        editor:EndUndoAction()

    -- block comment continue
    elseif line:find("^%s*%-%-%s*$") then --line is only '--', terminate comment
        editor:LineUp()
        editor:LineDelete()
    elseif line:find("^%s*%-%-") then --line is comment, auto continue it
        editor:AddText("-- ")
    end

    return false --allow other methods to respond to event
end

--register handler with extman
scite_OnEditorLine(auto_continue_comment)

Type '--' followed by text, hit enter and the '--' is already inserted on the next line! If you don't want it, just hit enter again and its gone! Type '==' and hit enter to create a multiline comment and put the caret inside! Thanks to SirAlaran? for the inspiration. See below for a sample session. --PeterMacDonald

-- <- followed by text starts a block comment
-- and it carries on until a line with only "--"

	-- A tab or spaces before "-- some text", will
	-- start an indented block comment, like this!

--[=[
Entering "==" on a line, like this:
==
will start a multiline comment, like this one!
--]=]

--[=[
	A tab or spaces before the "==" will start
	an indented multiline comment, like this!
--]=]


(end user comments)
RecentChanges · preferences
edit · history
Last edited November 15, 2012 6:37 am GMT (diff)