-- Extman is a Lua script manager for SciTE. It enables multiple scripts to capture standard events -- without interfering with each other. For instance, scite_OnDoubleClick() will register handlers -- for scripts that need to know when a double-click event has happened. (To know whether it -- was in the output or editor pane, just test editor.Focus). It provides a useful function scite_Command -- which allows you to define new commands without messing around with property files (see the -- examples in the scite_lua directory.) -- extman defines three new convenience handlers as well: --scite_OnWord (called when user has entered a word) --scite_OnEditorLine (called when a line is entered into the editor) --scite_OnOutputLine (called when a line is entered into the output pane) local _MarginClick,_DoubleClick,_SavePointLeft = {},{},{} local _SavePointReached,_Open,_SwitchFile = {},{},{} local _BeforeSave,_Save,_Char = {},{},{} local _Word,_LineEd,_LineOut = {},{},{} local _OpenSwitch = {} local _UpdateUI = {} local _UserListSelection local _remove = {} local append = table.insert local find = string.find local size = table.getn local sub = string.sub local gsub = string.gsub print '11111111111111' function OnUserListSelection(tp, str) print 'OnUserListSelection' if _UserListSelection then local callback = _UserListSelection _UserListSelection = nil return callback(str) else return false end end local function DispatchOne(handlers, arg) --print 'DispatchOne' for i, handler in pairs(handlers)do local fn = handler if _remove[fn] then handlers[i] = nil _remove[fn] = nil end local ret = fn(arg) if ret then return ret end end return false end -- these are the standard SciTE Lua callbacks - we use them to call installed extman handlers! function OnMarginClick() return DispatchOne(_MarginClick) end function OnDoubleClick() return DispatchOne(_DoubleClick) end function OnSavePointLeft() return DispatchOne(_SavePointLeft) end function OnSavePointReached() return DispatchOne(_SavePointReached) end function OnChar(ch) return DispatchOne(_Char, ch) end function OnSave(file) return DispatchOne(_Save, file) end function OnBeforeSave(file) return DispatchOne(_BeforeSave, file) end function OnSwitchFile(file) return DispatchOne(_SwitchFile, file) end function OnOpen(file) return DispatchOne(_Open, file) end function OnUpdateUI() if editor.Focus then return DispatchOne(_UpdateUI) else return false end end -- may optionally ask that this handler be immediately -- removed after it's called local function append_unique(tbl, fn, remove) local once_only if type(fn)== 'string' then once_only = fn == 'once' fn = remove remove = nil if once_only then _remove[fn] = fn end else _remove[fn] = nil end local idx for i, handler in pairs(tbl)do if handler == fn then idx = i; break end end if idx then if remove then table.remove(tbl, idx) end else if not remove then append(tbl, fn) end end end -- this is how you register your own handlers with extman function scite_OnMarginClick(fn, remove) append_unique(_MarginClick, fn, remove) end function scite_OnDoubleClick(fn, remove) append_unique(_DoubleClick, fn, remove) end function scite_OnSavePointLeft(fn, remove) append_unique(_SavePointLeft, fn, remove) end function scite_OnSavePointReached(fn, remove) append_unique(_SavePointReached, fn, remove) end function scite_OnOpen(fn, remove) append_unique(_Open, fn, remove) end function scite_OnSwitchFile(fn, remove) append_unique(_SwitchFile, fn, remove) end function scite_OnBeforeSave(fn, remove) append_unique(_BeforeSave, fn, remove) end function scite_OnSave(fn, remove) append_unique(_Save, fn, remove) end function scite_OnUpdateUI(fn, remove) append_unique(_UpdateUI, fn, remove) end function scite_OnChar(fn, remove) append_unique(_Char, fn, remove) end function scite_OnOpenSwitch(fn, remove) append_unique(_OpenSwitch, fn, remove) end local function buffer_switch(f) --- OnOpen() is also called if we move to a new folder if not find(f, '[\\/]$')then DispatchOne(_OpenSwitch, f) end end scite_OnOpen(buffer_switch) scite_OnSwitchFile(buffer_switch) local next_user_id = 13 -- arbitrary -- the handler is always reset! function scite_UserListShow(list, start, fn) print 'scite_UserListShow' local s = '' local sep = ';' local n = size(list) for i = start, n-1 do s = s..list[i]..sep end s = s..list[n] _UserListSelection = fn local pane = editor if not pane.Focus then pane = output end pane.AutoCSeparator = string.byte(sep) pane:UserListShow(next_user_id, s) pane.AutoCSeparator = string.byte(' ') end local word_start, in_word, current_word local function on_word_char(s) print 'on_word_char' if not in_word then if find(s, '%w')then -- we have hit a word! word_start = editor.CurrentPos in_word = true current_word = s end else -- we're in a word -- and it's another word character, so collect if find(s, '%w')then current_word = current_word..s else -- leaving a word; call the handler local word_end = editor.CurrentPos DispatchOne(_Word, {word = current_word, startp = word_start, endp = editor.CurrentPos, ch = s }) in_word = false end end -- don't interfere with usual processing! return false end function scite_OnWord(fn, remove) append_unique(_Word, fn, remove) if not remove then scite_OnChar(on_word_char) else scite_OnChar(on_word_char, 'remove') end end local last_pos = 0 local function grab_line_from(pane) local line_pos = pane.CurrentPos local lineno = pane:LineFromPosition(line_pos)-1 -- strip linefeeds (Windows is a special case as usual!) local endl = 2 if scite_GetProp('PLAT_WIN')then endl = 3 end local line = string.sub(pane:GetLine(lineno), 1, -endl) return line end local function on_line_char(ch, result) if ch == '\n' or ch == '\r' then if ch == '\n' then if editor.Focus then DispatchOne(_LineEd, grab_line_from(editor)) else DispatchOne(_LineOut, grab_line_from(output)) end return result end end return false end local function on_line_editor_char(ch) return on_line_char(ch, false) end local function on_line_output_char(ch) return on_line_char(ch, true) end local function set_line_handler(fn, rem, handler, on_char) append_unique(handler, fn, rem) if not rem then scite_OnChar(on_char) else scite_OnChar(on_char, 'remove') end end function scite_OnEditorLine(fn, rem) set_line_handler(fn, rem, _LineEd, on_line_editor_char) end function scite_OnOutputLine(fn, rem) set_line_handler(fn, rem, _LineOut, on_line_output_char) end function scite_GetProp(key, default) local val = props[key] if val and val ~= '' then return val else return default end end local GTK = scite_GetProp('PLAT_GTK') local default_path local tmpfile if GTK then default_path = props['SciteUserHome'] tmpfile = '/tmp/.scite-temp-files' print( 'GTK'..default_path) else default_path = props['SciteDefaultHome'] tmpfile = '\\scite_temp1' print( 'not GTK '..default_path) end function scite_Files(mask) print('scite_Files:mask=='..mask) local f, path if GTK then -- f = io.popen('ls -1 '..mask) os.execute('ls -1 '..mask..' > '..tmpfile) f = io.open(tmpfile) path = '' else mask = gsub(mask, '/', '\\') _, _, path = find(mask, '(.*\\)') local cmd = 'dir /b "'..mask..'" > '..tmpfile print('scite_Files:cmd=='..cmd) if Execute then -- scite_other was found! Execute(cmd) else os.execute(cmd) end f = io.open(tmpfile) end local files = {} if not f then return files end for line in f:lines()do print('scite_Files:line=='..line) append(files, path..line) end f:close() return files end function scite_FileExists(f) local f = io.open(f) if not f then return false else f:close() return true end end function scite_CurrentFile() return props['FilePath'] end function scite_WordAtPos(pos) if not pos then pos = editor.CurrentPos end local p2 = editor:WordEndPosition(pos, true) local p1 = editor:WordStartPosition(pos, true) return editor:textrange(p1, p2) end -- allows you to bind given Lua functions to shortcut keys -- without messing around in the properties files! function split(s, delim) res = {} while true do p = find(s, delim) if not p then append(res, s) return res end append(res, sub(s, 1, p-1)) s = sub(s, p+1) end end function splitv(s, delim) return unpack(split(s, delim)) end local idx = 10 local shortcuts_used = {} function scite_Command(tbl) if type(tbl)== 'string' then print('scite_Command:tbl=='..tbl) tbl = {tbl} end for i, v in pairs(tbl)do local name, cmd, mode, shortcut = splitv(v, '|') if not shortcut then shortcut = mode mode = '.*' else mode = '.'..mode end -- has this command been defined before? local old_idx = 0 for ii = 10, idx do if props['command.name.'..ii..mode] == name then old_idx = ii end end if old_idx == 0 then local which = '.'..idx..mode props['command.name'..which] = name props['command'..which] = cmd props['command.subsystem'..which] = '3' props['command.mode'..which] = 'savebefore:no' if shortcut then local cmd = shortcuts_used[shortcut] if cmd then print('Error: shortcut already used in "'..cmd..'"') else -- print(name,cmd,shortcut) props['command.shortcut'..which] = shortcut shortcuts_used[shortcut] = name end end idx = idx + 1 end end end -- this will quietly fail.... local loaded = {} local function silent_dofile(f) if scite_FileExists(f)then --f = gsub(f,'\\','/') if not loaded[f] then print('silent_dofile:'..f) dofile(f) loaded[f] = true else print('already loaded', f) end end end function scite_dofile(f) f = default_path..'/'..f silent_dofile(f) end local path local lua_dir = scite_GetProp('ext.lua.directory') if lua_dir then path = lua_dir else path = default_path..'/script/scite_lua' end print( 'lua_dir '..path) function scite_require(f) f = path..'/'..f silent_dofile(f) end if not GTK then scite_dofile 'scite_other.lua' end local script_list = scite_Files(path..'/*.lua') if not script_list then print('Error: no files found in '..path) else for i, file in pairs(script_list)do print( i ..file) silent_dofile(file) end end scite_Command 'Reload Script|reload_script|Ctrl+1' function reload_script() current_file = scite_CurrentFile() print('Reloading... '..current_file) loaded[current_file] = false silent_dofile(current_file) end