diff options
author | Jakob Stendahl <jakob.stendahl@outlook.com> | 2018-01-09 15:01:04 +0100 |
---|---|---|
committer | Jakob Stendahl <jakob.stendahl@outlook.com> | 2018-01-09 15:01:04 +0100 |
commit | bb6e704ab011a497ac8c735afc0fd4c52a1425ce (patch) | |
tree | bed462d18c4f976f30c690b529d6de8b2a6ba2ff /love2dToAPK/tools/tools/zbstudio-old-win/src/editor/outline.lua | |
parent | d2c375c015fe3e216234a887ab75e08d40f5f5db (diff) | |
download | Love2dToAPK-bb6e704ab011a497ac8c735afc0fd4c52a1425ce.tar.gz Love2dToAPK-bb6e704ab011a497ac8c735afc0fd4c52a1425ce.zip |
Added old project
Diffstat (limited to 'love2dToAPK/tools/tools/zbstudio-old-win/src/editor/outline.lua')
-rw-r--r-- | love2dToAPK/tools/tools/zbstudio-old-win/src/editor/outline.lua | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/love2dToAPK/tools/tools/zbstudio-old-win/src/editor/outline.lua b/love2dToAPK/tools/tools/zbstudio-old-win/src/editor/outline.lua new file mode 100644 index 0000000..cc1fbac --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-old-win/src/editor/outline.lua @@ -0,0 +1,679 @@ +-- Copyright 2014-15 Paul Kulchenko, ZeroBrane LLC + +local ide = ide +ide.outline = { + outlineCtrl = nil, + imglist = ide:CreateImageList("OUTLINE", "FILE-NORMAL", "VALUE-LCALL", + "VALUE-GCALL", "VALUE-ACALL", "VALUE-SCALL", "VALUE-MCALL"), + settings = { + symbols = {}, + ignoredirs = {}, + }, + needsaving = false, + indexqueue = {[0] = {}}, + indexpurged = false, -- flag that the index has been purged from old records; once per session +} + +local outline = ide.outline +local image = { FILE = 0, LFUNCTION = 1, GFUNCTION = 2, AFUNCTION = 3, + SMETHOD = 4, METHOD = 5, +} +local q = EscapeMagic +local caches = {} + +local function setData(ctrl, item, value) + if ide.wxver >= "2.9.5" then + local data = wx.wxLuaTreeItemData() + data:SetData(value) + ctrl:SetItemData(item, data) + end +end + +local function resetOutlineTimer() + if ide.config.outlineinactivity then + ide.timers.outline:Start(ide.config.outlineinactivity*1000, wx.wxTIMER_ONE_SHOT) + end +end + +local function resetIndexTimer(interval) + if ide.config.symbolindexinactivity and not ide.timers.symbolindex:IsRunning() then + ide.timers.symbolindex:Start(interval or ide.config.symbolindexinactivity*1000, wx.wxTIMER_ONE_SHOT) + end +end + +local function outlineRefresh(editor, force) + if not editor then return end + local tokens = editor:GetTokenList() + local sep = editor.spec.sep + local varname = "([%w_][%w_"..q(sep:sub(1,1)).."]*)" + local funcs = {updated = TimeGet()} + local var = {} + local outcfg = ide.config.outline or {} + local scopes = {} + local funcnum = 0 + local SCOPENUM, FUNCNUM = 1, 2 + local text + for _, token in ipairs(tokens) do + local op = token[1] + if op == 'Var' or op == 'Id' then + var = {name = token.name, fpos = token.fpos, global = token.context[token.name] == nil} + elseif outcfg.showcurrentfunction and op == 'Scope' then + local fundepth = #scopes + if token.name == '(' then -- a function starts a new scope + funcnum = funcnum + 1 -- increment function count + local nested = fundepth == 0 or scopes[fundepth][SCOPENUM] > 0 + scopes[fundepth + (nested and 1 or 0)] = {1, funcnum} + elseif fundepth > 0 then + scopes[fundepth][SCOPENUM] = scopes[fundepth][SCOPENUM] + 1 + end + elseif outcfg.showcurrentfunction and op == 'EndScope' then + local fundepth = #scopes + if fundepth > 0 and scopes[fundepth][SCOPENUM] > 0 then + scopes[fundepth][SCOPENUM] = scopes[fundepth][SCOPENUM] - 1 + if scopes[fundepth][SCOPENUM] == 0 then + local funcnum = scopes[fundepth][FUNCNUM] + if funcs[funcnum] then + funcs[funcnum].poe = token.fpos + (token.name and #token.name or 0) + end + table.remove(scopes) + end + end + elseif op == 'Function' then + local depth = token.context['function'] or 1 + local name, pos = token.name, token.fpos + text = text or editor:GetTextDyn() + local _, _, rname, params = text:find('([^%(]*)(%b())', pos) + if name and rname:find(token.name, 1, true) ~= 1 then + name = rname:gsub("%s+$","") + end + if not name then + local s = editor:PositionFromLine(editor:LineFromPosition(pos-1)) + local rest + rest, pos, name = text:sub(s+1, pos-1):match('%s*(.-)()'..varname..'%s*=%s*function%s*$') + if rest then + pos = s + pos + -- guard against "foo, bar = function() end" as it would get "bar" + if #rest>0 and rest:find(',') then name = nil end + end + end + local ftype = image.LFUNCTION + if not name then + ftype = image.AFUNCTION + elseif outcfg.showmethodindicator and name:find('['..q(sep)..']') then + ftype = name:find(q(sep:sub(1,1))) and image.SMETHOD or image.METHOD + elseif var.name == name and var.fpos == pos + or var.name and name:find('^'..var.name..'['..q(sep)..']') then + ftype = var.global and image.GFUNCTION or image.LFUNCTION + end + name = name or outcfg.showanonymous + funcs[#funcs+1] = { + name = ((name or '~')..params):gsub("%s+", " "), + skip = (not name) and true or nil, + depth = depth, + image = ftype, + pos = name and pos or token.fpos, + } + end + end + + if force == nil then return funcs end + + local ctrl = outline.outlineCtrl + local cache = caches[editor] or {} + caches[editor] = cache + + -- add file + local filename = ide:GetDocument(editor):GetTabText() + local fileitem = cache.fileitem + if not fileitem then + local root = ctrl:GetRootItem() + if not root or not root:IsOk() then return end + + if outcfg.showonefile then + fileitem = root + else + fileitem = ctrl:AppendItem(root, filename, image.FILE) + setData(ctrl, fileitem, editor) + ctrl:SetItemBold(fileitem, true) + ctrl:SortChildren(root) + end + cache.fileitem = fileitem + end + + do -- check if any changes in the cached function list + local prevfuncs = cache.funcs or {} + local nochange = #funcs == #prevfuncs + local resort = {} -- items that need to be re-sorted + if nochange then + for n, func in ipairs(funcs) do + func.item = prevfuncs[n].item -- carry over cached items + if func.depth ~= prevfuncs[n].depth then + nochange = false + elseif nochange and prevfuncs[n].item then + if func.name ~= prevfuncs[n].name then + ctrl:SetItemText(prevfuncs[n].item, func.name) + if outcfg.sort then resort[ctrl:GetItemParent(prevfuncs[n].item)] = true end + end + if func.image ~= prevfuncs[n].image then + ctrl:SetItemImage(prevfuncs[n].item, func.image) + end + end + end + end + cache.funcs = funcs -- set new cache as positions may change + if nochange and not force then -- return if no visible changes + if outcfg.sort then -- resort items for all parents that have been modified + for item in pairs(resort) do ctrl:SortChildren(item) end + end + return + end + end + + -- refresh the tree + -- refreshing shouldn't change the focus of the current element, + -- but it appears that DeleteChildren (wxwidgets 2.9.5 on Windows) + -- moves the focus from the current element to wxTreeCtrl. + -- need to save the window having focus and restore after the refresh. + local win = ide:GetMainFrame():FindFocus() + + ctrl:Freeze() + + -- disabling event handlers is not strictly necessary, but it's expected + -- to fix a crash on Windows that had DeleteChildren in the trace (#442). + ctrl:SetEvtHandlerEnabled(false) + ctrl:DeleteChildren(fileitem) + ctrl:SetEvtHandlerEnabled(true) + + local edpos = editor:GetCurrentPos()+1 + local stack = {fileitem} + local resort = {} -- items that need to be re-sorted + for n, func in ipairs(funcs) do + local depth = outcfg.showflat and 1 or func.depth + local parent = stack[depth] + while not parent do depth = depth - 1; parent = stack[depth] end + if not func.skip then + local item = ctrl:AppendItem(parent, func.name, func.image) + if ide.config.outline.showcurrentfunction + and edpos >= func.pos and func.poe and edpos <= func.poe then + ctrl:SetItemBold(item, true) + end + if outcfg.sort then resort[parent] = true end + setData(ctrl, item, n) + func.item = item + stack[func.depth+1] = item + end + func.skip = nil + end + if outcfg.sort then -- resort items for all parents that have been modified + for item in pairs(resort) do ctrl:SortChildren(item) end + end + if outcfg.showcompact then ctrl:Expand(fileitem) else ctrl:ExpandAllChildren(fileitem) end + + -- scroll to the fileitem, but only if it's not a root item (as it's hidden) + if fileitem:GetValue() ~= ctrl:GetRootItem():GetValue() then + ctrl:ScrollTo(fileitem) + ctrl:SetScrollPos(wx.wxHORIZONTAL, 0, true) + else -- otherwise, scroll to the top + ctrl:SetScrollPos(wx.wxVERTICAL, 0, true) + end + ctrl:Thaw() + + if win and win ~= ide:GetMainFrame():FindFocus() then win:SetFocus() end +end + +local function indexFromQueue() + if #outline.indexqueue == 0 then return end + + local editor = ide:GetEditor() + local inactivity = ide.config.symbolindexinactivity + if editor and inactivity and editor.updated > TimeGet()-inactivity then + -- reschedule timer for later time + resetIndexTimer() + else + local fname = table.remove(outline.indexqueue, 1) + outline.indexqueue[0][fname] = nil + -- check if fname is already loaded + ide:SetStatusFor(TR("Indexing %d files: '%s'..."):format(#outline.indexqueue+1, fname)) + local content, err = FileRead(fname) + if content then + local editor = ide:CreateBareEditor() + editor:SetupKeywords(GetFileExt(fname)) + editor:SetTextDyn(content) + editor:Colourise(0, -1) + editor:ResetTokenList() + while IndicateAll(editor) do end + + outline:UpdateSymbols(fname, outlineRefresh(editor)) + editor:Destroy() + else + DisplayOutputLn(TR("Can't open file '%s': %s"):format(fname, err)) + end + if #outline.indexqueue == 0 then + outline:SaveSettings() + ide:SetStatusFor(TR("Indexing completed.")) + end + ide:DoWhenIdle(indexFromQueue) + end + return +end + +local function createOutlineWindow() + local REFRESH, REINDEX = 1, 2 + local width, height = 360, 200 + local ctrl = wx.wxTreeCtrl(ide.frame, wx.wxID_ANY, + wx.wxDefaultPosition, wx.wxSize(width, height), + wx.wxTR_LINES_AT_ROOT + wx.wxTR_HAS_BUTTONS + + wx.wxTR_HIDE_ROOT + wx.wxNO_BORDER) + + outline.outlineCtrl = ctrl + ide.timers.outline = wx.wxTimer(ctrl, REFRESH) + ide.timers.symbolindex = wx.wxTimer(ctrl, REINDEX) + + ctrl:AddRoot("Outline") + ctrl:SetImageList(outline.imglist) + ctrl:SetFont(ide.font.fNormal) + + function ctrl:ActivateItem(item_id) + local data = ctrl:GetItemData(item_id) + if ctrl:GetItemImage(item_id) == image.FILE then + -- activate editor tab + local editor = data:GetData() + if not ide:GetEditorWithFocus(editor) then ide:GetDocument(editor):SetActive() end + else + -- activate tab and move cursor based on stored pos + -- get file parent + local onefile = (ide.config.outline or {}).showonefile + local parent = ctrl:GetItemParent(item_id) + if not onefile then -- find the proper parent + while parent:IsOk() and ctrl:GetItemImage(parent) ~= image.FILE do + parent = ctrl:GetItemParent(parent) + end + if not parent:IsOk() then return end + end + -- activate editor tab + local editor = onefile and GetEditor() or ctrl:GetItemData(parent):GetData() + local cache = caches[editor] + if editor and cache then + -- move to position in the file + editor:GotoPosEnforcePolicy(cache.funcs[data:GetData()].pos-1) + -- only set editor active after positioning as this may change focus, + -- which may regenerate the outline, which may invalidate `data` value + if not ide:GetEditorWithFocus(editor) then ide:GetDocument(editor):SetActive() end + end + end + end + + local function activateByPosition(event) + local mask = (wx.wxTREE_HITTEST_ONITEMINDENT + wx.wxTREE_HITTEST_ONITEMLABEL + + wx.wxTREE_HITTEST_ONITEMICON + wx.wxTREE_HITTEST_ONITEMRIGHT) + local item_id, flags = ctrl:HitTest(event:GetPosition()) + + if item_id and item_id:IsOk() and bit.band(flags, mask) > 0 then + ctrl:ActivateItem(item_id) + else + event:Skip() + end + return true + end + + ctrl:Connect(wx.wxEVT_TIMER, function(event) + if event:GetId() == REFRESH then outlineRefresh(GetEditor(), false) end + if event:GetId() == REINDEX then ide:DoWhenIdle(indexFromQueue) end + end) + ctrl:Connect(wx.wxEVT_LEFT_DOWN, activateByPosition) + ctrl:Connect(wx.wxEVT_LEFT_DCLICK, activateByPosition) + ctrl:Connect(wx.wxEVT_COMMAND_TREE_ITEM_ACTIVATED, function(event) + ctrl:ActivateItem(event:GetItem()) + end) + + ctrl:Connect(ID_OUTLINESORT, wx.wxEVT_COMMAND_MENU_SELECTED, + function() + ide.config.outline.sort = not ide.config.outline.sort + for editor, cache in pairs(caches) do + ide:SetStatus(("Refreshing '%s'..."):format(ide:GetDocument(editor):GetFileName())) + local isexpanded = ctrl:IsExpanded(cache.fileitem) + outlineRefresh(editor, true) + if not isexpanded then ctrl:Collapse(cache.fileitem) end + end + ide:SetStatus('') + end) + + ctrl:Connect(wx.wxEVT_COMMAND_TREE_ITEM_MENU, + function (event) + local menu = wx.wxMenu { + { ID_OUTLINESORT, TR("Sort By Name"), "", wx.wxITEM_CHECK }, + } + menu:Check(ID_OUTLINESORT, ide.config.outline.sort) + + PackageEventHandle("onMenuOutline", menu, ctrl, event) + + ctrl:PopupMenu(menu) + end) + + + local function reconfigure(pane) + pane:TopDockable(false):BottomDockable(false) + :MinSize(150,-1):BestSize(300,-1):FloatingSize(200,300) + end + + local layout = ide:GetSetting("/view", "uimgrlayout") + if not layout or not layout:find("outlinepanel") then + ide:AddPanelDocked(ide:GetProjectNotebook(), ctrl, "outlinepanel", TR("Outline"), reconfigure, false) + else + ide:AddPanel(ctrl, "outlinepanel", TR("Outline"), reconfigure) + end +end + +local function eachNode(eachFunc, root, recursive) + local ctrl = outline.outlineCtrl + local item = ctrl:GetFirstChild(root or ctrl:GetRootItem()) + while true do + if not item:IsOk() then break end + if eachFunc and eachFunc(ctrl, item) then break end + if recursive and ctrl:ItemHasChildren(item) then eachNode(eachFunc, item, recursive) end + item = ctrl:GetNextSibling(item) + end +end + +createOutlineWindow() + +local pathsep = GetPathSeparator() +local function isInSubDir(name, path) + return #name > #path and path..pathsep == name:sub(1, #path+#pathsep) +end + +local function isIgnoredInIndex(name) + local ignoredirs = outline.settings.ignoredirs + if ignoredirs[name] then return true end + + -- check through ignored dirs to see if any of them match the file + for path in pairs(ignoredirs) do + if isInSubDir(name, path) then return true end + end + + return false +end + +local function purgeIndex(path) + local symbols = outline.settings.symbols + for name in pairs(symbols) do + if isInSubDir(name, path) then outline:UpdateSymbols(name, nil) end + end +end + +local function purgeQueue(path) + local curqueue = outline.indexqueue + local newqueue = {[0] = {}} + for _, name in ipairs(curqueue) do + if not isInSubDir(name, path) then + table.insert(newqueue, name) + newqueue[0][name] = true + end + end + outline.indexqueue = newqueue +end + +local function disableIndex(path) + outline.settings.ignoredirs[path] = true + outline:SaveSettings(true) + + -- purge the path from the index and the (current) queue + purgeIndex(path) + purgeQueue(path) +end + +local function enableIndex(path) + outline.settings.ignoredirs[path] = nil + outline:SaveSettings(true) + outline:RefreshSymbols(path) +end + +local package = ide:AddPackage('core.outline', { + -- remove the editor from the list + onEditorClose = function(self, editor) + local cache = caches[editor] + local fileitem = cache and cache.fileitem + caches[editor] = nil -- remove from cache + if (ide.config.outline or {}).showonefile then return end + if fileitem then outline.outlineCtrl:Delete(fileitem) end + end, + + -- handle rename of the file in the current editor + onEditorSave = function(self, editor) + if (ide.config.outline or {}).showonefile then return end + local cache = caches[editor] + local fileitem = cache and cache.fileitem + local doc = ide:GetDocument(editor) + local ctrl = outline.outlineCtrl + if doc and fileitem and ctrl:GetItemText(fileitem) ~= doc:GetTabText() then + ctrl:SetItemText(fileitem, doc:GetTabText()) + end + local path = doc and doc:GetFilePath() + if path and cache and cache.funcs then + outline:UpdateSymbols(path, cache.funcs.updated > editor.updated and cache.funcs or nil) + outline:SaveSettings() + end + end, + + -- go over the file items to turn bold on/off or collapse/expand + onEditorFocusSet = function(self, editor) + if (ide.config.outline or {}).showonefile and ide.config.outlineinactivity then + outlineRefresh(editor, true) + return + end + + local cache = caches[editor] + local fileitem = cache and cache.fileitem + local ctrl = outline.outlineCtrl + local itemname = ide:GetDocument(editor):GetTabText() + + -- update file name if it changed in the editor + if fileitem and ctrl:GetItemText(fileitem) ~= itemname then + ctrl:SetItemText(fileitem, itemname) + end + + -- if the editor is not in the cache, which may happen if the user + -- quickly switches between tabs that don't have outline generated, + -- regenerate it manually + if not cache then resetOutlineTimer() end + resetIndexTimer() + + eachNode(function(ctrl, item) + local found = fileitem and item:GetValue() == fileitem:GetValue() + if not found and ctrl:IsBold(item) then + ctrl:SetItemBold(item, false) + ctrl:CollapseAllChildren(item) + end + end) + + if fileitem and not ctrl:IsBold(fileitem) then + -- run the following changes on idle as doing them inline is causing a strange + -- issue on OSX when clicking on a tab may skip several tabs (#546); + -- this is somehow caused by `ExpandAllChildren` triggered from `SetFocus` inside + -- `PAGE_CHANGED` handler for the notebook. + ide:DoWhenIdle(function() + ctrl:SetItemBold(fileitem, true) + if (ide.config.outline or {}).showcompact then + ctrl:Expand(fileitem) + else + ctrl:ExpandAllChildren(fileitem) + end + ctrl:ScrollTo(fileitem) + ctrl:SetScrollPos(wx.wxHORIZONTAL, 0, true) + end) + end + end, + + onMenuFiletree = function(self, menu, tree, event) + local item_id = event:GetItem() + local name = tree:GetItemFullName(item_id) + local symboldirmenu = wx.wxMenu { + {ID_SYMBOLDIRREFRESH, TR("Refresh Index"), TR("Refresh indexed symbols from files in the selected directory")}, + {ID_SYMBOLDIRDISABLE, TR("Disable Indexing For '%s'"):format(name), TR("Ignore and don't index symbols from files in the selected directory")}, + } + local _, _, projdirpos = ide:FindMenuItem(ID_PROJECTDIR, menu) + if projdirpos then + local ignored = isIgnoredInIndex(name) + local enabledirmenu = wx.wxMenu() + local paths = {} + for path in pairs(outline.settings.ignoredirs) do table.insert(paths, path) end + table.sort(paths) + for i, path in ipairs(paths) do + local id = ID("file.enablesymboldir."..i) + enabledirmenu:Append(id, path, "") + tree:Connect(id, wx.wxEVT_COMMAND_MENU_SELECTED, function() enableIndex(path) end) + end + + symboldirmenu:Append(wx.wxMenuItem(symboldirmenu, ID_SYMBOLDIRENABLE, + TR("Enable Indexing"), "", wx.wxITEM_NORMAL, enabledirmenu)) + menu:Insert(projdirpos+1, wx.wxMenuItem(menu, ID_SYMBOLDIRINDEX, + TR("Symbol Index"), "", wx.wxITEM_NORMAL, symboldirmenu)) + + -- disable "enable" if it's empty + menu:Enable(ID_SYMBOLDIRENABLE, #paths > 0) + -- disable "refresh" and "disable" if the directory is ignored + -- or if any of the directories above it are ignored + menu:Enable(ID_SYMBOLDIRREFRESH, tree:IsDirectory(item_id) and not ignored) + menu:Enable(ID_SYMBOLDIRDISABLE, tree:IsDirectory(item_id) and not ignored) + + tree:Connect(ID_SYMBOLDIRREFRESH, wx.wxEVT_COMMAND_MENU_SELECTED, function() + -- purge files in this directory as some might have been removed; + -- files will be purged based on time, but this is a good time to clean. + purgeIndex(name) + outline:RefreshSymbols(name) + resetIndexTimer(1) -- start after 1ms + end) + tree:Connect(ID_SYMBOLDIRDISABLE, wx.wxEVT_COMMAND_MENU_SELECTED, function() + disableIndex(name) + end) + end + end, + + onEditorPainted = function(self, editor) + local ctrl = ide.outline.outlineCtrl + if not ide:IsWindowShown(ctrl) then return end + + local cache = caches[editor] + if not cache or not ide.config.outline.showcurrentfunction then return end + + local edpos = editor:GetCurrentPos()+1 + local edline = editor:LineFromPosition(edpos-1)+1 + if cache.pos and cache.pos == edpos then return end + if cache.line and cache.line == edline then return end + + cache.pos = edpos + cache.line = edline + + local n = 0 + local MIN, MAX = 1, 2 + local visible = {[MIN] = math.huge, [MAX] = 0} + local needshown = {[MIN] = math.huge, [MAX] = 0} + + ctrl:Unselect() + -- scan all items recursively starting from the current file + eachNode(function(ctrl, item) + local func = cache.funcs[ctrl:GetItemData(item):GetData()] + local val = edpos >= func.pos and func.poe and edpos <= func.poe + if edline == editor:LineFromPosition(func.pos)+1 + or (func.poe and edline == editor:LineFromPosition(func.poe)+1) then + cache.line = nil + end + ctrl:SetItemBold(item, val) + if val then ctrl:SelectItem(item, val) end + + if not ide.config.outline.jumptocurrentfunction then return end + n = n + 1 + -- check that this and the items around it are all visible; + -- this is to avoid the situation when the current item is only partially visible + local isvisible = ctrl:IsVisible(item) and ctrl:GetNextVisible(item):IsOk() and ctrl:GetPrevVisible(item):IsOk() + if val and not isvisible then + needshown[MIN] = math.min(needshown[MIN], n) + needshown[MAX] = math.max(needshown[MAX], n) + elseif isvisible then + visible[MIN] = math.min(visible[MIN], n) + visible[MAX] = math.max(visible[MAX], n) + end + end, cache.fileitem, true) + + if not ide.config.outline.jumptocurrentfunction then return end + if needshown[MAX] > visible[MAX] then + ctrl:ScrollLines(needshown[MAX]-visible[MAX]) -- scroll forward to the last hidden line + elseif needshown[MIN] < visible[MIN] then + ctrl:ScrollLines(needshown[MIN]-visible[MIN]) -- scroll backward to the first hidden line + end + end, + }) + +local function queuePath(path) + -- only queue if symbols inactivity is set, so files will be indexed + if ide.config.symbolindexinactivity and not outline.indexqueue[0][path] then + outline.indexqueue[0][path] = true + table.insert(outline.indexqueue, 1, path) + end +end + +function outline:GetFileSymbols(path) + local symbols = self.settings.symbols[path] + -- queue path to process when appropriate + if not symbols then queuePath(path) end + return symbols +end + +function outline:GetEditorSymbols(editor) + -- force token refresh (as these may be not updated yet) + if #editor:GetTokenList() == 0 then + while IndicateAll(editor) do end + end + + -- only refresh the functions when none is present + if not caches[editor] or #caches[editor].funcs == 0 then outlineRefresh(editor, true) end + return caches[editor].funcs +end + +function outline:RefreshSymbols(path, callback) + if isIgnoredInIndex(path) then return end + + local exts = {} + for _, ext in pairs(ide:GetKnownExtensions()) do + local spec = GetSpec(ext) + if spec and spec.marksymbols then table.insert(exts, ext) end + end + + local opts = {sort = false, folder = false, skipbinary = true, yield = true, + -- skip those directories that are on the "ignore" list + ondirectory = function(name) return outline.settings.ignoredirs[name] == nil end + } + local nextfile = coroutine.wrap(function() FileSysGetRecursive(path, true, table.concat(exts, ";"), opts) end) + while true do + local file = nextfile() + if not file then break end + if not isIgnoredInIndex(file) then (callback or queuePath)(file) end + end +end + +function outline:UpdateSymbols(fname, symb) + local symbols = self.settings.symbols + symbols[fname] = symb + + -- purge outdated records + local threshold = TimeGet() - 60*60*24*7 -- cache for 7 days + if not self.indexpurged then + for k, v in pairs(symbols) do + if v.updated < threshold then symbols[k] = nil end + end + self.indexpurged = true + end + + self.needsaving = true +end + +function outline:SaveSettings(force) + if self.needsaving or force then + ide:PushStatus(TR("Updating symbol index and settings...")) + package:SetSettings(self.settings, {keyignore = {depth = true, image = true, poe = true, item = true, skip = true}}) + ide:PopStatus() + self.needsaving = false + end +end + +MergeSettings(outline.settings, package:GetSettings()) |