-- Lua backend for SciTE Sidebar
-- (c) Valentin Schmidt 2016
require 'winapi'
require 'wmcopydata'
require 'bit'
-- Simple Lua pattern based function detection vor various languages - please improve and add more!
local FUNCTION_TABLE = {}
-- C/C++
-- Notice: C/C++ is tricky, therefor for now just weak implementation: only works for nicely intended code
FUNCTION_TABLE['c'] = {
regex = {
'([%a]+[%a%d_ :]+)%s*%('
},
fmt = {
'%s'
}
}
FUNCTION_TABLE['cpp'] = FUNCTION_TABLE['c']
FUNCTION_TABLE['cxx'] = FUNCTION_TABLE['c']
FUNCTION_TABLE['cc'] = FUNCTION_TABLE['c']
FUNCTION_TABLE['mm'] = FUNCTION_TABLE['c']
-- INI
FUNCTION_TABLE['ini'] = {
regex = {'%[([%a%d_/%\\ ]*)%]'},
fmt = {'%%[%s%%]'}
}
-- JavaScript
FUNCTION_TABLE['js'] = {
regex = {
'[ \t]*function[ \t]+([%a%d_]+)', -- function foo()
'[ \t]*([%a%d_]+)[ |\t]*=[ |\t]*function[^%%a%%d_]' -- foo = function()
},
fmt = {
'[ \t]*function[ \t]+%s[^%%a%%d_]',
'[ \t]*%s[ |\t]*=[ |\t]*function[^%%a%%d_]'
}
}
-- Lingo
FUNCTION_TABLE['ls'] = {
regex = {
'[ \t]*on[ \t]+([%a%d_]+)'
},
fmt = {
'[ \t]*on[ \t]+%s[^%%a%%d_]'
}
}
FUNCTION_TABLE['lsw'] = FUNCTION_TABLE['ls']
-- Lua
FUNCTION_TABLE['lua'] = {
regex = {
'[ \t]*function[ \t]+([%a%d_]+)',
'[ \t]*local[ \t]+function[ \t]+([%a%d_]+)'
},
fmt = {
'[ \t]*function[ \t]+%s[^%%a%%d_]',
'[ \t]*local[ \t]+function[ \t]+%s[^%%a%%d_]'
}
}
-- PHP
FUNCTION_TABLE['php'] = {
regex = {
'[ \t]*function[ \t]+([%a%d_]*)'
},
fmt = {
'[ \t]*function[ \t]+%s[^%%a%%d_]'
}
}
----------------------------------------
-- Toggles visibility of sidebar
----------------------------------------
function toggleSidebar ()
-- check if sidebar is already open
local win = winapi.find_window ('QWidget', 'Sidebar')
if win:get_handle()==0 then
winapi.shell_exec('open', props['SciteDefaultHome']..'\\lingo\\sidebar.exe')
else
if win:is_visible() then
win:show(0) -- SW_HIDE
else
win:show(4) -- SW_SHOWNOACTIVATE
end
end
end
----------------------------------------
-- Sends WM_COPDATA message to sidebar
----------------------------------------
function notifySidebar (data)
local win = winapi.find_window ('ImlWinCls', '~Sidebar')
local hwnd = win:get_handle()
if hwnd~=0 then
local msg = ':'..hwnd..':'..data
local res = wmcopydata.send(hwnd, msg)
end
end
----------------------------------------
-- Called by sidebar: scroll to and select specified function in current buffer
----------------------------------------
function onFunctionSelected(name)
local ext = getExt(props['FilePath'])
local t = FUNCTION_TABLE[ext]
if t==nil then return end
local code = editor:GetText()
local search
for i,fmt in ipairs(t.fmt) do
search = string.format(fmt, name)
if string.sub(code, 1, string.len(search))==search then
pos = 0
else
pos = string.find(code, "[\n\r]"..search)
end
if pos then
-- goto line
local lineNum = editor:LineFromPosition(pos)
editor:GotoLine(lineNum)
-- select line
posEnd = editor.LineEndPosition[lineNum]
editor:SetSel(pos, posEnd) -- Select a range of text.
editor.FirstVisibleLine = lineNum - 3
return
end
end
end
----------------------------------------
-- Returns lower case extension of specified file
----------------------------------------
function getExt(fn)
local i = #fn
while i > 0 do
if fn:sub(i,i) == '.' then
return fn:sub(i+1):lower()
end
i = i - 1
end
end
----------------------------------------
-- Updates the function list in sidebar (if sidebar is running)
----------------------------------------
function sidebarUpdateFunctions() -- fn
local fn = props['FilePath']
-- send list of functions to sidebar
local functions = ''
local ext = getExt(fn)
local t = FUNCTION_TABLE[ext]
if t~=nil then
local code = "\r\n"..editor:GetText()
-- loop over regexps
for i,regex in ipairs(t.regex) do
for res in string.gmatch(code, '[\r\n]'..regex) do
functions = functions..res..','
end
end
if (functions) then functions = string.sub(functions, 1, -2) end
end
notifySidebar("functions:"..functions)
end
----------------------------------------
-- Shows current file (buffer) in Windows Explorer
-- (if file wasn't saved yet, nothing happens)
----------------------------------------
function showInWindowsExplorer()
local fn = props['FilePath']
if fn:sub(#fn, #fn)~='\\' then
winapi.execute('explorer.exe /select,"'..props['FilePath']..'"')
end
end
----------------------------------------
-- Shows current file (buffer) in Sidebar Explorer
-- (if sidebar wasn't started or file wasn't saved yet, nothing happens)
----------------------------------------
function showInSidebarExplorer()
local fn = props['FilePath']
if fn:sub(#fn, #fn)~='\\' then
local win = winapi.find_window ('QWidget', 'Sidebar')
if win:get_handle()~=0 then
win:show(4) -- SW_SHOWNOACTIVATE
notifySidebar("show:"..fn)
else
print('>Sidebar not started yet. First start it with Alt+E.')
end
end
end
-- EVENT HANDLERS --
scite_OnOpen(sidebarUpdateFunctions)
scite_OnSwitchFile(sidebarUpdateFunctions)
scite_OnSave(sidebarUpdateFunctions)
scite_OnSave(function(fn)
notifySidebar("saved:"..fn)
end)
scite_OnClose(function(fn)
if fn~="" then
notifySidebar("closed:"..fn)
end
end)
-- ADD MENU ITEMS --
scite_Command {
'Sidebar|toggleSidebar|Alt+E',
'Show in Windows Explorer|showInWindowsExplorer|Alt+W',
'Show in Sidebar Explorer|showInSidebarExplorer|Alt+X'
}