-- moltenform_scite_utils.lua -- by Ben Fisher, 2018 -- instructions + examples: -- https://github.com/moltenform/scite-files/tree/master/files/files/helpers/adding_scite_features_with_lua.md -- list of available methods+documentation: -- https://github.com/moltenform/scite-files/tree/master/files/files/helpers/moltenform_scite_utils_api.md ---------------------------------------------------------------------- -- inheritsFrom -- http://lua-users.org/wiki/InheritanceTutorial ---------------------------------------------------------------------- function inheritsFrom(baseClass) local new_class = {} local class_mt = { __index = new_class } function new_class:create() local newinst = {} setmetatable( newinst, class_mt ) return newinst end if nil ~= baseClass then setmetatable( new_class, { __index = baseClass } ) end -- Return the class object of the instance function new_class:class() return new_class end -- Return the super class object of the instance function new_class:superClass() return baseClass end -- Return true if the caller is an instance of theClass function new_class:isa( theClass ) local b_isa = false local cur_class = new_class while ( nil ~= cur_class ) and ( false == b_isa ) do if cur_class == theClass then b_isa = true else cur_class = cur_class:superClass() end end return b_isa end return new_class end ---------------------------------------------------------------------- -- ScAppClass -- I've gathered all the common commands that act on -- the scite application into this class ---------------------------------------------------------------------- local ScAppClass = inheritsFrom(nil) function ScAppClass:Init(s) end function ScAppClass:Trace(s) trace(s) end function ScAppClass:OpenFile(s) scite.Open(s) end function ScAppClass:GetProperty(s) return props[s] end function ScAppClass:SetProperty(s, v) props[s] = v end function ScAppClass:UnsetProperty(s, v) props[s] = nil end function ScAppClass:UpdateStatusBar(updateSlowData) if updateSlowData == nil then updateSlowData = false end scite.UpdateStatusBar(updateSlowData) end function ScAppClass:GetFilePath(cannotBeUntitled) if cannotBeUntitled == nil then cannotBeUntitled = true end if cannotBeUntitled then -- we usually don't want untitled documents to look like they have a path if self:GetProperty('FilePath') ~= nil and self:GetProperty('FilePath'):len() > 0 then return self:GetProperty('FilePath') else return '' end else return self:GetProperty('FilePath') end end function ScAppClass:GetFileName() return self:GetProperty('FileNameExt') end function ScAppClass:GetFileDirectory() return self:GetProperty('FileDir') end function ScAppClass:GetSciteDirectory() return self:GetProperty('SciteDefaultHome') end function ScAppClass:GetSciteUserDirectory() return self:GetProperty('SciteUserHome') end function ScAppClass:RunToolCmd(n) return scite.MenuCommand(1100+n) end -- constants like SCMOD_SHIFT are placed in global scope, so no need for a function. -- no need for MakeKeymod, lua extension has special logic for iface_keymod and takes 2 integers function ScAppClass:MakeColor(red, green, blue) assert((0 <= red) and (red <= 255)) assert((0 <= green) and (green <= 255)) assert((0 <= blue) and (blue<= 255)) return red + (green << 8) + (blue << 16) end function ScAppClass:GetColor(val) local red = val & 0x000000ff local green = (val & 0x0000ff00) >> 8 local blue = (val & 0x000ff0000) >> 16 return red, green, blue end function ScAppClass:abbrev() return scite.MenuCommand(242) end function ScAppClass:about() return scite.MenuCommand(902) end function ScAppClass:activate() return scite.MenuCommand(320) end function ScAppClass:allowaccess() return scite.MenuCommand(119) end function ScAppClass:block_comment() return scite.MenuCommand(243) end function ScAppClass:bookmark_clearall() return scite.MenuCommand(224) end function ScAppClass:bookmark_next() return scite.MenuCommand(221) end function ScAppClass:bookmark_next_select() return scite.MenuCommand(225) end function ScAppClass:bookmark_prev() return scite.MenuCommand(223) end function ScAppClass:bookmark_prev_select() return scite.MenuCommand(226) end function ScAppClass:bookmark_select_all() return scite.MenuCommand(227) end function ScAppClass:bookmark_toggle() return scite.MenuCommand(222) end function ScAppClass:box_comment() return scite.MenuCommand(246) end function ScAppClass:buffer() return scite.MenuCommand(1200) end function ScAppClass:buffersep() return scite.MenuCommand(505) end function ScAppClass:build() return scite.MenuCommand(302) end function ScAppClass:clean() return scite.MenuCommand(308) end function ScAppClass:clear() return scite.MenuCommand(206) end function ScAppClass:clearoutput() return scite.MenuCommand(420) end function ScAppClass:close() return scite.MenuCommand(105) end function ScAppClass:closeall() return scite.MenuCommand(503) end function ScAppClass:compile() return scite.MenuCommand(301) end function ScAppClass:complete() return scite.MenuCommand(233) end function ScAppClass:completeword() return scite.MenuCommand(234) end function ScAppClass:copy() return scite.MenuCommand(204) end function ScAppClass:copyasrtf() return scite.MenuCommand(245) end function ScAppClass:copypath() return scite.MenuCommand(118) end function ScAppClass:cut() return scite.MenuCommand(203) end function ScAppClass:directiondown() return scite.MenuCommand(806) end function ScAppClass:directionup() return scite.MenuCommand(805) end function ScAppClass:duplicate() return scite.MenuCommand(250) end function ScAppClass:encoding_default() return scite.MenuCommand(150) end function ScAppClass:encoding_ucookie() return scite.MenuCommand(154) end function ScAppClass:encoding_ucs2be() return scite.MenuCommand(151) end function ScAppClass:encoding_ucs2le() return scite.MenuCommand(152) end function ScAppClass:encoding_utf8() return scite.MenuCommand(153) end function ScAppClass:enterselection() return scite.MenuCommand(256) end function ScAppClass:eol_convert() return scite.MenuCommand(433) end function ScAppClass:eol_cr() return scite.MenuCommand(431) end function ScAppClass:eol_crlf() return scite.MenuCommand(430) end function ScAppClass:eol_lf() return scite.MenuCommand(432) end function ScAppClass:expand() return scite.MenuCommand(235) end function ScAppClass:expand_ensurechildrenvisible() return scite.MenuCommand(238) end function ScAppClass:filer() return scite.MenuCommand(114) end function ScAppClass:find() return scite.MenuCommand(210) end function ScAppClass:findinfiles() return scite.MenuCommand(215) end function ScAppClass:findnext() return scite.MenuCommand(211) end function ScAppClass:findnextback() return scite.MenuCommand(212) end function ScAppClass:findnextbacksel() return scite.MenuCommand(214) end function ScAppClass:findnextsel() return scite.MenuCommand(213) end function ScAppClass:finishedexecute() return scite.MenuCommand(305) end function ScAppClass:foldmargin() return scite.MenuCommand(406) end function ScAppClass:fullscreen() return scite.MenuCommand(961) end function ScAppClass:go() return scite.MenuCommand(303) end function ScAppClass:dlg_goto() return scite.MenuCommand(220) end function ScAppClass:help() return scite.MenuCommand(901) end function ScAppClass:help_scite() return scite.MenuCommand(903) end function ScAppClass:import() return scite.MenuCommand(1300) end function ScAppClass:incsearch() return scite.MenuCommand(252) end function ScAppClass:ins_abbrev() return scite.MenuCommand(247) end function ScAppClass:join() return scite.MenuCommand(248) end function ScAppClass:language() return scite.MenuCommand(1400) end function ScAppClass:linenumbermargin() return scite.MenuCommand(407) end function ScAppClass:linereverse() return scite.MenuCommand(218) end function ScAppClass:loadsession() return scite.MenuCommand(132) end function ScAppClass:lwrcase() return scite.MenuCommand(241) end function ScAppClass:macrolist() return scite.MenuCommand(314) end function ScAppClass:macroplay() return scite.MenuCommand(313) end function ScAppClass:macrorecord() return scite.MenuCommand(311) end function ScAppClass:macrostoprecord() return scite.MenuCommand(312) end function ScAppClass:macro_sep() return scite.MenuCommand(310) end function ScAppClass:matchbrace() return scite.MenuCommand(230) end function ScAppClass:matchcase() return scite.MenuCommand(801) end function ScAppClass:monofont() return scite.MenuCommand(450) end function ScAppClass:movetableft() return scite.MenuCommand(509) end function ScAppClass:movetabright() return scite.MenuCommand(508) end function ScAppClass:mrufile() return scite.MenuCommand(1000) end function ScAppClass:mru_sep() return scite.MenuCommand(120) end function ScAppClass:mru_sub() return scite.MenuCommand(121) end function ScAppClass:menunew() return scite.MenuCommand(101) end function ScAppClass:nextfile() return scite.MenuCommand(502) end function ScAppClass:nextfilestack() return scite.MenuCommand(507) end function ScAppClass:nextmatchppc() return scite.MenuCommand(262) end function ScAppClass:nextmsg() return scite.MenuCommand(306) end function ScAppClass:ontop() return scite.MenuCommand(960) end function ScAppClass:open() return scite.MenuCommand(102) end function ScAppClass:openabbrevproperties() return scite.MenuCommand(463) end function ScAppClass:opendirectoryproperties() return scite.MenuCommand(465) end function ScAppClass:openfileshere() return scite.MenuCommand(413) end function ScAppClass:openglobalproperties() return scite.MenuCommand(462) end function ScAppClass:openlocalproperties() return scite.MenuCommand(460) end function ScAppClass:openluaexternalfile() return scite.MenuCommand(464) end function ScAppClass:openselected() return scite.MenuCommand(103) end function ScAppClass:openuserproperties() return scite.MenuCommand(461) end function ScAppClass:paste() return scite.MenuCommand(205) end function ScAppClass:pasteanddown() return scite.MenuCommand(208) end function ScAppClass:prevfile() return scite.MenuCommand(501) end function ScAppClass:prevfilestack() return scite.MenuCommand(506) end function ScAppClass:prevmatchppc() return scite.MenuCommand(260) end function ScAppClass:prevmsg() return scite.MenuCommand(307) end function ScAppClass:menuprint() return scite.MenuCommand(131) end function ScAppClass:printsetup() return scite.MenuCommand(130) end function ScAppClass:quit() return scite.MenuCommand(140) end function ScAppClass:readonly() return scite.MenuCommand(416) end function ScAppClass:redo() return scite.MenuCommand(202) end function ScAppClass:regexp() return scite.MenuCommand(802) end function ScAppClass:replace() return scite.MenuCommand(216) end function ScAppClass:revert() return scite.MenuCommand(104) end function ScAppClass:runwin() return scite.MenuCommand(351) end function ScAppClass:save() return scite.MenuCommand(106) end function ScAppClass:saveacopy() return scite.MenuCommand(116) end function ScAppClass:saveall() return scite.MenuCommand(504) end function ScAppClass:saveas() return scite.MenuCommand(110) end function ScAppClass:saveashtml() return scite.MenuCommand(111) end function ScAppClass:saveaspdf() return scite.MenuCommand(113) end function ScAppClass:saveasrtf() return scite.MenuCommand(112) end function ScAppClass:saveastex() return scite.MenuCommand(115) end function ScAppClass:saveasxml() return scite.MenuCommand(117) end function ScAppClass:savesession() return scite.MenuCommand(133) end function ScAppClass:selectall() return scite.MenuCommand(207) end function ScAppClass:selectionaddeach() return scite.MenuCommand(258) end function ScAppClass:selectionaddnext() return scite.MenuCommand(257) end function ScAppClass:selection_for_find() return scite.MenuCommand(217) end function ScAppClass:selecttobrace() return scite.MenuCommand(231) end function ScAppClass:selecttonextmatchppc() return scite.MenuCommand(263) end function ScAppClass:selecttoprevmatchppc() return scite.MenuCommand(261) end function ScAppClass:selmargin() return scite.MenuCommand(405) end function ScAppClass:showcalltip() return scite.MenuCommand(232) end function ScAppClass:split() return scite.MenuCommand(249) end function ScAppClass:splitvertical() return scite.MenuCommand(401) end function ScAppClass:srcwin() return scite.MenuCommand(350) end function ScAppClass:statuswin() return scite.MenuCommand(353) end function ScAppClass:stopexecute() return scite.MenuCommand(304) end function ScAppClass:stream_comment() return scite.MenuCommand(244) end function ScAppClass:switchpane() return scite.MenuCommand(421) end function ScAppClass:tabsize() return scite.MenuCommand(440) end function ScAppClass:tabwin() return scite.MenuCommand(354) end function ScAppClass:toggleoutput() return scite.MenuCommand(409) end function ScAppClass:toggleparameters() return scite.MenuCommand(412) end function ScAppClass:toggle_foldall() return scite.MenuCommand(236) end function ScAppClass:toggle_foldrecursive() return scite.MenuCommand(237) end function ScAppClass:tools() return scite.MenuCommand(1100) end function ScAppClass:toolwin() return scite.MenuCommand(352) end function ScAppClass:undo() return scite.MenuCommand(201) end function ScAppClass:unslash() return scite.MenuCommand(804) end function ScAppClass:uprcase() return scite.MenuCommand(240) end function ScAppClass:vieweol() return scite.MenuCommand(403) end function ScAppClass:viewguides() return scite.MenuCommand(404) end function ScAppClass:viewspace() return scite.MenuCommand(402) end function ScAppClass:viewstatusbar() return scite.MenuCommand(411) end function ScAppClass:viewtabbar() return scite.MenuCommand(410) end function ScAppClass:viewtoolbar() return scite.MenuCommand(408) end function ScAppClass:wholeword() return scite.MenuCommand(800) end function ScAppClass:wrap() return scite.MenuCommand(414) end function ScAppClass:wraparound() return scite.MenuCommand(803) end function ScAppClass:wrapoutput() return scite.MenuCommand(415) end ScApp = ScAppClass:create() ScApp:Init() ---------------------------------------------------------------------- -- ScToolUIManagerClass -- wrapper for showing custom ui in a strip -- you should create an instance of ScToolUIBase and pass it in as toolUI -- refer to https://github.com/moltenform/scite-files/tree/master/files/files/helpers/adding_scite_features_with_lua.md ---------------------------------------------------------------------- local ScToolUIManagerClass = inheritsFrom(nil) function ScToolUIManagerClass:Init() self.currentUserStrip = nil self.eventIsRegisteredWithExtman = false end function ScToolUIManagerClass:UserStripSet(toolUI, control, value) -- toolUI should be an instance of ScToolUIBase self:EnsureActive(toolUI) return scite.StripSet(control, value) end function ScToolUIManagerClass:UserStripSetList(toolUI, control, value) self:EnsureActive(toolUI) return scite.StripSetList(control, value) end function ScToolUIManagerClass:UserStripGetValue(toolUI, control) self:EnsureActive(toolUI) return scite.StripValue(control) end function ScToolUIManagerClass:OnUserStrip(control, eventType) if self.currentUserStrip then self.currentUserStrip:OnRawEvent(control, eventType) end end function ScToolUIManagerClass:Show(toolUI) if toolUI and toolUI.spec then scite.StripShow(toolUI.spec) self.currentUserStrip = toolUI elseif toolUI then assert(false, 'ScToolUIManagerClass:Show given a table but no "spec" found') self.currentUserStrip = nil else scite.StripShow('') self.currentUserStrip = nil end end function ScToolUIManagerClass:EnsureActive(toolUI) if toolUI then if not toolUI.isScToolUIBase then assert(false, 'param toolUI should be an instance of ScToolUIBase') end end if self.currentUserStrip ~= toolUI then assert(false, 'the UI object is no longer active') end end function ScToolUIManagerClass:RegisterEventWithExtman() -- I'd do this automatically, but that would create a hard dependency on extman, -- and it'd be better if people could use this script without needing extman. if self.eventIsRegisteredWithExtman then else scite_OnUserStrip( function(a,b) self:OnUserStrip(a,b) end ) self.eventIsRegisteredWithExtman = true end end ScToolUIManager = ScToolUIManagerClass:create() ScToolUIManager:Init() -- to use ScToolUIManager, you will need to add the following lines to at least one of your scripts: -- require('path/to/extman') -- ScToolUIManager:RegisterEventWithExtman() ---------------------------------------------------------------------- -- ScToolUIBase -- you can use a class derived from ScToolUIBase to draw a strip dialog ui -- refer to https://github.com/moltenform/scite-files/tree/master/files/files/helpers/adding_scite_features_with_lua.md ---------------------------------------------------------------------- ScToolUIBase = inheritsFrom(nil) function ScToolUIBase:Init() self.isScToolUIBase = true self.spec='' self.currentNumber = 0 self.callbackOnClick = {} self.currentlyBuilding = true self:AddControls() self.currentlyBuilding = false -- define constants self.eventTypeUnknown = 0 self.eventTypeClicked = 1 self.eventTypeChange = 2 self.eventTypeFocusIn = 3 self.eventTypeFocusOut = 4 end function ScToolUIBase:Show() ScToolUIManager:Show(self) self:OnOpen() end function ScToolUIBase:Close() ScToolUIManager:Show(nil) end function ScToolUIBase:AddLabel(text) return self:_add(text, "'", "'") end function ScToolUIBase:AddButton(text, callback, closes, default) if callback == nil then callback = nil end if closes == nil then closes = false end if defaultbtn == nil then defaultbtn = false end if defaultbtn then return self:_add(text, '((', '))', callback, closes) else return self:_add(text, '(', ')', callback, closes) end end function ScToolUIBase:AddCombo() return self:_add('', '{', '}') end function ScToolUIBase:AddEntry(text) return self:_add(text, '[', ']') end function ScToolUIBase:AddRow() return self:_add('\n', '', '', nil, nil, true) end function ScToolUIBase:_add(text, start, paramend, callback, closes, noNumber) if callback == nil then callback = nil end if closes == nil then closes = false end if noNumber == nil then noNumber = false end assert(self.currentlyBuilding, 'Controls can only be added within AddControls().') self.spec = self.spec .. start .. text .. paramend self.callbackOnClick[self.currentNumber] = {callback, closes} local ret = self.currentNumber if not noNumber then self.currentNumber = self.currentNumber + 1 end return ret end function ScToolUIBase:OnRawEvent(control, eventType) -- if the implementation class wants, it can see the raw event too local res = self:OnEvent(control, eventType) if res then return nil end if eventType == self.eventTypeClicked then local foundcallback = nil local foundcloses = nil for index, value in ipairs(self.callbackOnClick) do if index == control then foundcallback = value[1] foundcloses = value[2] break end end if foundcallback then foundcallback() end if foundcloses then self:Close() end end end function ScToolUIBase:Get(control) return ScToolUIManager:UserStripGetValue(self, control) end function ScToolUIBase:Set(control, val) return ScToolUIManager:UserStripSet(self, control, val) end function ScToolUIBase:SetList(control, val) -- val delimited by \n newlines return ScToolUIManager:UserStripSetList(self, control, val) end -- for the implementation class to override function ScToolUIBase:AddControls() print('please implement AddControls in your child class') end function ScToolUIBase:OnOpen() print('please implement OnOpen in your child class') end function ScToolUIBase:OnEvent(control, eventType) -- implement, if desired, in a child class end function stringstartswith(s,start) return string.sub(s,1,start:len())==start end function stringendswith(str, ending) return ending == "" or str:sub(-#ending) == ending end ---------------------------------------------------------------------- -- ScEditor and ScOutput -- I've gathered all the common commands that act on -- editing or output panes into this class ---------------------------------------------------------------------- local function isAScintillaFunction(pane, key) local closure = nil local status, err = pcall(function() closure = pane[key] end) if not status then if tostring(err):find("Pane function / readable property") then return nil else assert(false, 'error ' .. tostring(err)) end end return closure end function makePanelObj(paneImpl, paneNum) local ret = {} ret.pane = paneImpl ret.paneNum = paneNum ret.PaneAppend = function(self, txt) -- Append text return self.pane:append(txt) end ret.GetEolCharacter = function(self) -- Return current EOL character, e.g. \r\n local n = self:GetEOLMode() if n == 0 then return '\r\n' elseif n == 1 then return '\r' else return '\n' end end ret.PaneInsertText = function(self, txt, pos) -- Insert text, without changing selection return self.pane:insert(pos, txt) end ret.PaneWrite = function(self, txt, pos) -- Insert text, and update selection as if the text was typed in if not pos then pos = self.pane.CurrentPos end self.pane:insert(pos, txt) self.pane:GotoPos(pos + txt:len()) end ret.PaneRemoveText = function(self, npos1, npos2) -- Remove text between these positions return self.pane:remove(npos1, npos2) end ret.PaneGetTextRange = function(self, n1, n2) -- Get text between these positions return self.pane:textrange(n1, n2) end ret.PaneFindText = function(self, s, pos1, pos2, wholeWord, matchCase, regExp, flags) if pos1 == nil then pos1 = 0 end if pos2 == nil then pos2 = -1 end if wholeWord == nil then wholeWord = false end if matchCase == nil then matchCase = false end if regExp == nil then regExp = false end if flags == nil then flags = 0 end -- Find text if wholeWord then flags = flags|SCFIND_WHOLEWORD end if matchCase then flags = flags|SCFIND_MATCHCASE end if regExp then flags = flags|SCFIND_REGEXP end return self.pane:findtext(s, flags, pos1, pos2) end -- helpers where the Scintilla version is less convenient to use ret.GetLineText = function(self, line) -- Returns text of specified line return self:GetLine(line) end ret.GetSelectedText = function(self) -- Returns selected text return self:GetSelText() end ret.GetCurrentLineText = function(self) -- Returns text of current line return self:GetCurLine() end local mt = { __index = function(self, key) -- in my opinion, the Scintilla api is a bit harder to use -- because when retrieving info, sometimes you have to say -- a = editor.Foo -- and other times -- a = editor:GetBar() -- and also, -- editor.Foo = 2 -- and other times -- editor.SetBar(2) -- with this wrapper, -- the syntax is always :GetBar() and :SetBar(2) -- is it a function? local closure = isAScintillaFunction(self.pane, key) if closure then return closure end -- is it a property? if stringstartswith(key, 'Get') then shortkey = key:sub(4, -1) return function() return self.pane[shortkey] end elseif stringstartswith(key, 'Set') then shortkey = key:sub(4, -1) return function(slf, v) self.pane[shortkey] = v end end -- unknown method assert(false, 'unknown method '..key) end, __newindex = function (t,k,v) assert(false, 'use a "set" method instead, tried to modify '..k) end } setmetatable(ret, mt) return ret end ScEditor = makePanelObj(editor, 0) ScOutput = makePanelObj(output, 1) -- Credit: scite-ru -- https://bitbucket.org/scite-ru/scite-ru.bitbucket.org/src -- is the position part of a comment function IsComment(pos) local style = editor.StyleAt[pos] local lexer = props['Language'] local comment = { abap = {1, 2}, ada = {10}, asm = {1, 11}, au3 = {1, 2}, baan = {1, 2}, bullant = {1, 2, 3}, caml = {12, 13, 14, 15}, cpp = {1, 2, 3, 15, 17, 18}, csound = {1, 9}, css = {9}, d = {1, 2, 3, 4, 15, 16, 17}, escript = {1, 2, 3}, euphoria = {1, 18}, flagship = {1, 2, 3, 4, 5, 6}, forth = {1, 2, 3}, gap = {9}, hypertext = {9, 20, 29, 42, 43, 44, 57, 58, 59, 72, 82, 92, 107, 124, 125}, xml = {9, 29}, inno = {1, 7}, latex = {4}, lua = {1, 2, 3}, script_lua = {4, 5}, mmixal = {1, 17}, nsis = {1, 18}, opal = {1, 2}, pascal = {2, 3, 4}, perl = {2}, bash = {2}, pov = {1, 2}, ps = {1, 2, 3}, python = {1, 12}, rebol = {1, 2}, ruby = {2}, scriptol = {2, 3, 4, 5}, smalltalk = {3}, specman = {2, 3}, spice = {8}, sql = {1, 2, 3, 13, 15, 17, 18}, tcl = {1, 2, 20, 21}, verilog = {1, 2, 3}, vhdl = {1, 2} } for l,ts in pairs(comment) do if l == lexer then for _,s in pairs(ts) do if s == style then return true end end return false end end -- asn1, ave, blitzbasic, cmake, conf, eiffel, eiffelkw, erlang, euphoria, fortran, f77, freebasic, kix, lisp, lout, octave, matlab, metapost, nncrontab, props, batch, makefile, diff, purebasic, vb, yaml if style == 1 then return true end return false end function printTable(o) if type(o) == 'table' then local s = '{ ' for k,v in pairs(o) do if type(k) ~= 'number' then k = '"'..k..'"' end s = s .. '['..k..'] = ' .. printTable(v) .. ',' end return s .. '} ' else return tostring(o) end end