-- ahk.lua -- ======= -- Part of SciTE4AutoHotkey -- This file implements features specific to AutoHotkey in SciTE -- Do NOT edit this file, use UserLuaScript.lua instead! -- Functions: -- AutoIndent for AutoHotkey -- Some AutoComplete tweaks -- Automatic backups -- SciTEDebug.ahk DBGp debugger interface local prepared = false local savedbk = nil -- ================================================== -- -- OnClear event - fired when SciTE changes documents -- -- ================================================== -- function OnClear() if not prepared then -- Remove the current line markers. ClearAllMarkers() end SetMarkerColors() editor.MarginSensitiveN[1] = true end -- ====================================== -- -- OnChar event - needed by some features -- -- ====================================== -- function OnChar(curChar) local ignoreStylesTable = { [SCLEX_AHK1] = {SCE_AHK_COMMENTLINE, SCE_AHK_COMMENTBLOCK, SCE_AHK_STRING, SCE_AHK_ERROR, SCE_AHK_ESCAPE}, [SCLEX_AHK2] = {SCE_AHK2_COMMENTLINE, SCE_AHK2_COMMENTBLOCK, SCE_AHK2_STRING, SCE_AHK2_ERROR, SCE_AHK2_ESCAPE}, } -- This function should only run when the Editor pane is focused. if not editor.Focus then return false end -- This function only works with the AutoHotkey lexer if not InAHKLexer() then return false end local ignoreStyles = ignoreStylesTable[editor.Lexer] if curChar == "\n" then local prevStyle = editor.StyleAt[getPrevLinePos()] if not isInTable(ignoreStyles, prevStyle) then return AutoIndent_OnNewLine() end elseif curChar == "{" then local curStyle = editor.StyleAt[editor.CurrentPos-2] if not isInTable(ignoreStyles, curStyle) then AutoIndent_OnOpeningBrace() end elseif curChar == "}" then local curStyle = editor.StyleAt[editor.CurrentPos-2] if not isInTable(ignoreStyles, curStyle) then AutoIndent_OnClosingBrace() end else local curStyle = editor.StyleAt[editor.CurrentPos-2] local pos = editor:WordStartPosition(editor.CurrentPos) -- Disable AutoComplete on comment/string/error/etc. if isInTable(ignoreStyles, curStyle) then -- ... but allow it in variable %dereferences% (which are set to 'error' -- when they are typed because of the missing closing percent sign. if not IsAHKv2() and curStyle == SCE_AHK_ERROR and editor.CharAt[pos-1] == 37 and not isInTable(ignoreStyles, editor.StyleAt[pos-1]) then return false end return CancelAutoComplete() end end return false end function CancelAutoComplete() if editor:AutoCActive() then editor:AutoCCancel() end return true end -- ================================================== -- -- OnMarginClick event - needed to set up breakpoints -- -- ================================================== -- function OnMarginClick(position, margin) -- This function only works with the AutoHotkey lexer if not InAHKLexer() then return false end if margin == 1 then if prepared then postmsg(4112, 1, editor:LineFromPosition(position)) else line = editor:LineFromPosition(position) if editor:MarkerNext(line, 1024) == line then -- 1024 = BIT(10) editor:MarkerDelete(line, 10) else editor:MarkerAdd(line, 10) end end return true else return false end end -- =============================================== -- -- OnDwellStart event - used to implement hovering -- -- =============================================== -- function OnDwellStart(pos, s) if not prepared then return end if s ~= '' then pumpmsgstr(4112, 4, GetWord(pos)) else pumpmsgstr(4112, 4, "") end end -- =========================================================== -- -- Get direction interface HWND function (used by the toolbar) -- -- =========================================================== -- function get_director_HWND() if prepared then return end if localizewin("scite4ahkToolbarTempWin") == false then print("Window doesn't exist.") return end pumpmsg(4099, 0, props['WindowID']) end -- ============== -- -- DBGp functions -- -- ============== -- -- The following are only reachable when an AutoHotkey script -- is open so there's no need to check the lexer function DBGp_Connect() if prepared then return end if localizewin("SciTEDebugStub") == false then print("Window doesn't exist.") return end -- Initialize pumpmsg(4112, 0, 0) prepared = true --SetMarkerColors() ClearAllMarkers() savedbk = enumBreakpoints() end function enumBreakpoints() line = editor:MarkerNext(0, 1024) -- 1024 = BIT(10) if line ~= -1 then i = 2 tbl = { line } while true do line = editor:MarkerNext(line+1, 1024) if line == -1 then break end tbl[i] = line i = i + 1 end return tbl end return nil end function DBGp_BkReset() if savedbk == nil then return end pumpmsg(4112, 5, 1) editor:MarkerDeleteAll(10) for i,v in ipairs(savedbk) do pumpmsg(4112, 1, v) end postmsg(4112, 5, 0) savedbk = nil end function DBGp_Disconnect() -- Deinitialize u = pumpmsg(4112, 255, 0) if u == 0 then return false end --editor.MarginSensitiveN[1] = false prepared = false ClearAllMarkers() end function DBGp_Inspect() if not prepared then return end pumpmsgstr(4112, 2, GetCurWord()) end function DBGp_Run() if not prepared then return end postmsg(4112, 3, 1) end function DBGp_Stop() if not prepared then return end postmsg(4112, 3, 2) end function DBGp_Pause() if not prepared then return end postmsg(4112, 3, 3) end function DBGp_StepInto() if not prepared then return end postmsg(4112, 3, 4) end function DBGp_StepOver() if not prepared then return end postmsg(4112, 3, 5) end function DBGp_StepOut() if not prepared then return end postmsg(4112, 3, 6) end function DBGp_Stacktrace() if not prepared then return end postmsg(4112, 3, 7) end function DBGp_Varlist() if not prepared then return end postmsg(4112, 3, 8) end -- ============================================================ -- -- AutoIndent section - it implements AutoIndent for AutoHotkey -- -- ============================================================ -- -- Patterns for syntax matching --local varCharPat = "[#_@%w%[%]%$%?]" local varCharPat = "[#_@%w%$]" local ifPat = "[iI][fF]" local altIfPat = ifPat.."%a+" local whilePat = "[wW][hH][iI][lL][eE]" local loopPat = "[lL][oO][oO][pP]" local forPat = "[fF][oO][rR]" local elsePat = "[eE][lL][sS][eE]" local tryPat = "[tT][rR][yY]" local catchPat = "[cC][aA][tT][cC][hH]" local finallyPat = "[fF][iI][nN][aA][lL][lL][yY]" -- Functions to detect certain types of statements function isOpenBraceLine(line) return string.find(line, "^%s*{") ~= nil end function isIfLine(line) return string.find(line, "^%s*"..ifPat.."%s+"..varCharPat) ~= nil or string.find(line, "^%s*"..ifPat.."%s*%(") ~= nil or string.find(line, "^%s*"..ifPat.."%s+!") ~= nil or string.find(line, "^%s*"..altIfPat.."%s*,") ~= nil or string.find(line, "^%s*"..altIfPat.."%s+") ~= nil end function isIfLineNoBraces(line) return isIfLine(line) and string.find(line, "{%s*$") == nil end function isTryLine(line) return string.find(line, "^%s*"..tryPat.."%s+$") ~= nil end function isTryLineAllowBraces(line) return isTryLine(line) or string.find(line, "^%s*"..tryPat.."%s*{%s*$") ~= nil end function isWhileLine(line) return string.find(line, "^%s*"..whilePat.."%s+") ~= nil or string.find(line, "^%s*"..whilePat.."%s*%(") ~= nil end function isLoopLine(line) return string.find(line, "^%s*"..loopPat.."%s*,") ~= nil or string.find(line, "^%s*"..loopPat.."%s+") ~= nil end function isForLine(line) return string.find(line, "^%s*"..forPat.."%s+"..varCharPat) ~= nil end function isLoopLineAllowBraces(line) return isLoopLine(line) or string.find(line, "^%s*"..loopPat.."%s*{%s*$") ~= nil end -- Minor annoyance: this way of doing ELSE detection makes the following -- appear as an one-line indent statement: -- else {commandWhichDoesNotInvolveIndention} -- -- Examples: -- else MsgBox -- else var := value -- -- Those cases seem rare enough not to attempt to fix them, the alternative -- would be breaking "else if" indentation. function isElseLine(line) return string.find(line, "^%s*"..elsePat.."%s*") ~= nil or isElseWithClosingBrace(line) end function isElseWithClosingBrace(line) return string.find(line, "^%s*}%s*"..elsePat.."%s*") ~= nil end function isElseLineAllowBraces(line) return isElseLine(line) or isElseWithClosingBrace(line) or string.find(line, "^%s*"..elsePat.."%s*{%s*$") ~= nil or string.find(line, "^%s*}%s*"..elsePat.."%s*{%s*$") ~= nil end function isCatchLine(line) return string.find(line, "^%s*"..catchPat.."%s*$") ~= nil or string.find(line, "^%s*"..catchPat.."%s+"..varCharPat.."+%s*$") ~= nil end function isCatchAllowClosingBrace(line) return isCatchLine(line) or string.find(line, "^%s*}%s*"..catchPat.."%s*$") ~= nil or string.find(line, "^%s*}%s*"..catchPat.."%s+"..varCharPat.."+%s*$") ~= nil end function isCatchLineAllowBraces(line) return isCatchAllowClosingBrace(line) or string.find(line, "^%s*"..catchPat.."%s*{%s*$") ~= nil or string.find(line, "^%s*"..catchPat.."%s+"..varCharPat.."+%s*{%s*$") ~= nil or string.find(line, "^%s*}%s*"..catchPat.."%s*{%s*$") ~= nil or string.find(line, "^%s*}%s*"..catchPat.."%s+"..varCharPat.."+%s*{%s*$") ~= nil end function isFinallyLine(line) return string.find(line, "^%s*"..finallyPat.."%s*$") ~= nil end function isFinallyAllowClosingBrace(line) return isFinallyLine(line) or string.find(line, "^%s*}%s*"..finallyPat.."%s*$") ~= nil end function isFinallyAllowBraces(line) return isFinallyAllowClosingBrace(line) or string.find(line, "^%s*}%s*"..finallyPat.."%s*{%s*$") ~= nil end function isFuncDef(line) return string.find(line, "^%s*"..varCharPat.."+%(.*%)%s*{%s*$") ~= nil end function isSingleLineIndentStatement(line) return isIfLineNoBraces(line) or isElseLine(line) or isElseWithClosingBrace(line) or isWhileLine(line) or isForLine(line) or isLoopLine(line) or isTryLine(line) or isCatchAllowClosingBrace(line) or isFinallyAllowClosingBrace(line) end function isIndentStatement(line) return isOpenBraceLine(line) or isIfLine(line) or isWhileLine(line) or isForLine(line) or isLoopLineAllowBraces(line) or isElseLineAllowBraces(line) or isFuncDef(line) or isTryLineAllowBraces(line) or isCatchLineAllowBraces(line) or isFinallyAllowBraces(line) end function isStartBlockStatement(line) return isIfLine(line) or isWhileLine(line) or isLoopLine(line) or isForLine(line) or isElseLine(line) or isElseWithClosingBrace(line) or isTryLine(line) or isCatchLineAllowBraces(line) or isFinallyAllowBraces(line) end -- This function is called when the user presses {Enter} function AutoIndent_OnNewLine() local cmtLineStyle = IsAHKv2() and SCE_AHK2_COMMENTLINE or SCE_AHK_COMMENTLINE local cmtBlockStyle = IsAHKv2() and SCE_AHK2_COMMENTBLOCK or SCE_AHK_COMMENTBLOCK local prevprevPos = editor:LineFromPosition(editor.CurrentPos) - 2 local prevPos = editor:LineFromPosition(editor.CurrentPos) - 1 local prevLine = GetFilteredLine(prevPos, cmtLineStyle, cmtBlockStyle) local curPos = prevPos + 1 local curLine = editor:GetLine(curPos) if curLine ~= nil and string.find(curLine, "^%s*[^%s]+") then return end if isIndentStatement(prevLine) then editor:Home() editor:Tab() editor:LineEnd() elseif prevprevPos >= 0 then local prevprevLine = GetFilteredLine(prevprevPos, cmtLineStyle, cmtBlockStyle) local reqLvl = editor.LineIndentation[prevprevPos] + editor.Indent local prevLvl = editor.LineIndentation[prevPos] local curLvl = editor.LineIndentation[curPos] if isSingleLineIndentStatement(prevprevLine) and prevLvl == reqLvl and curLvl == reqLvl then editor:Home() editor:BackTab() editor:LineEnd() return true end end return false end -- This function is called when the user presses { function AutoIndent_OnOpeningBrace() local cmtLineStyle = IsAHKv2() and SCE_AHK2_COMMENTLINE or SCE_AHK_COMMENTLINE local cmtBlockStyle = IsAHKv2() and SCE_AHK2_COMMENTBLOCK or SCE_AHK_COMMENTBLOCK local prevPos = editor:LineFromPosition(editor.CurrentPos) - 1 local curPos = prevPos+1 if prevPos == -1 then return false end if editor.LineIndentation[curPos] == 0 then return false end local prevLine = GetFilteredLine(prevPos, cmtLineStyle, cmtBlockStyle) local curLine = GetFilteredLine(curPos, cmtLineStyle, cmtBlockStyle) if string.find(curLine, "^%s*{%s*$") and isStartBlockStatement(prevLine) and (editor.LineIndentation[curPos] > editor.LineIndentation[prevPos]) then editor:Home() editor:BackTab() editor:LineEnd() end end -- This function is called when the user presses } function AutoIndent_OnClosingBrace() local cmtLineStyle = IsAHKv2() and SCE_AHK2_COMMENTLINE or SCE_AHK_COMMENTLINE local cmtBlockStyle = IsAHKv2() and SCE_AHK2_COMMENTBLOCK or SCE_AHK_COMMENTBLOCK local curPos = editor:LineFromPosition(editor.CurrentPos) local curLine = GetFilteredLine(curPos, cmtLineStyle, cmtBlockStyle) local prevPos = curPos - 1 local prevprevPos = prevPos - 1 local secondChance = false if curPos == 0 then return false end if editor.LineIndentation[curPos] == 0 then return false end if prevprevPos >= 0 then local prevprevLine = GetFilteredLine(prevprevPos, cmtLineStyle, cmtBlockStyle) local lowLvl = editor.LineIndentation[prevprevPos] local highLvl = lowLvl + editor.Indent local prevLvl = editor.LineIndentation[prevPos] local curLvl = editor.LineIndentation[curPos] if isSingleLineIndentStatement(prevprevLine) and prevLvl == highLvl and curLvl == lowLvl then secondChance = true end end if string.find(curLine, "^%s*}%s*$") and (editor.LineIndentation[curPos] >= editor.LineIndentation[prevPos] or secondChance) then editor:Home() editor:BackTab() editor:LineEnd() end end -- ====================== -- -- Script Backup Function -- -- ====================== -- -- this functions creates backups for the files function OnBeforeSave(filename) -- This function only works with the AutoHotkey lexer if not InAHKLexer() then return false end if props['make.backup'] == "1" then os.remove(filename .. ".bak") os.rename(filename, filename .. ".bak") end end -- ============= -- -- Open #Include -- -- ============= -- function OpenInclude() -- This function only works with the AutoHotkey lexer if not InAHKLexer() then return false end local CurrentLine = editor:GetLine(editor:LineFromPosition(editor.CurrentPos)) if not string.find(CurrentLine, "^%s*%#[Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then print("Not an include line!") return end local place = string.find(CurrentLine, "%#[Ii][Nn][Cc][Ll][Uu][Dd][Ee]") local IncFile = string.sub(CurrentLine, place + 8) if string.find(IncFile, "^[Aa][Gg][Aa][Ii][Nn]") then IncFile = string.sub(IncFile, 6) end IncFile = string.gsub(IncFile, "\r", "") -- strip CR IncFile = string.gsub(IncFile, "\n", "") -- strip LF IncFile = string.sub(IncFile, 2) -- strip space at the beginning IncFile = string.gsub(IncFile, "*i ", "") -- strip *i option IncFile = string.gsub(IncFile, "*I ", "") -- Delete comments local cplace = string.find(IncFile, "%s*;") if cplace then IncFile = string.sub(IncFile, 1, cplace-1) end -- Delete spaces at the beginning and the end IncFile = string.gsub(IncFile, "^%s*", "") IncFile = string.gsub(IncFile, "%s*$", "") -- Replace variables IncFile = string.gsub(IncFile, "%%[Aa]_[Ss][Cc][Rr][Ii][Pp][Tt][Dd][Ii][Rr]%%", props['FileDir']) IncFile = string.gsub(IncFile, "%%[Aa]_[Ll][Ii][Nn][Ee][Ff][Ii][Ll][Ee]%%", props['FilePath']) a,b,IncLib = string.find(IncFile, "^<(.+)>$") if IncLib ~= nil then local IncLib2 = IncLib local RawIncLib = IncLib a,b,whatmatch = string.find(IncLib, "^(.-)_") if whatmatch ~= nil and whatmatch ~= "" then IncLib2 = whatmatch end IncLib = "\\"..IncLib..".ahk" IncLib2 = "\\"..IncLib2..".ahk" local GlobalLib = props['AutoHotkeyDir'].."\\Lib" local UserLib = props['SciteUserHome'].."\\..\\Lib" local LocalLib = props['FileDir'].."\\Lib" for i,LibDir in ipairs({GlobalLib, UserLib, LocalLib}) do if FileExists(LibDir..IncLib) then scite.Open(LibDir..IncLib) return elseif FileExists(LibDir..IncLib2) then scite.Open(LibDir..IncLib2) return end end print("Library not found! Specified: '"..RawIncLib.."'") elseif FileExists(IncFile) then scite.Open(IncFile) else print("File not found! Specified: '"..IncFile.."'") end end -- ================ -- -- Helper Functions -- -- ================ -- function InAHKLexer() return editor.Lexer == SCLEX_AHK1 or editor.Lexer == SCLEX_AHK2 end function IsAHKv2() return editor.Lexer == SCLEX_AHK2 end function GetWord(pos) from = editor:WordStartPosition(pos, true) to = editor:WordEndPosition(pos, true) return editor:textrange(from, to) end function GetCurWord() local word = editor:GetSelText() if word == "" then word = GetWord(editor.CurrentPos) end return word end function getPrevLinePos() local line = editor:LineFromPosition(editor.CurrentPos)-1 local linepos = editor:PositionFromLine(line) local linetxt = editor:GetLine(line) return linepos + string.len(linetxt) - 1 end function isInTable(table, elem) if table == null then return false end for k,i in ipairs(table) do if i == elem then return true end end return false end function GetFilteredLine(linen, style1, style2) unline = editor:GetLine(linen) lpos = editor:PositionFromLine(linen) q = 0 for i = 0, string.len(unline)-1 do if(editor.StyleAt[lpos+i] == style1 or editor.StyleAt[lpos+i] == style2) then unline = unline:sub(1, i).."\000"..unline:sub(i+2) end end unline = string.gsub(unline, "%z", "") return unline end function SetMarkerColors() editor:MarkerDefine(10, 0) -- breakpoint editor.MarkerBack[10] = 0x0000FF editor:MarkerDefine(11, 2) -- current line arrow editor.MarkerBack[11] = 0xFFFF00 editor:MarkerDefine(12, 22) -- current line highlighting editor.MarkerBack[12] = 0xFFFF00 editor.MarkerAlpha[12] = 32 end function ClearAllMarkers() --editor:MarkerDeleteAll(10) editor:MarkerDeleteAll(11) editor:MarkerDeleteAll(12) end -- ======================= -- -- User Lua script loading -- -- ======================= -- function FileExists(file) local fobj = io.open(file, "r") if fobj then fobj:close() return true else return false end end function RegisterEvents(events) -- Code originally written by Lexikos (AutoComplete.lua) for evt, func in pairs(events) do local oldfunc = _G[evt] if oldfunc then _G[evt] = function(...) return func(...) or oldfunc(...) end else _G[evt] = func end end end -- Globals for extensions g_SettingsDir = props['SciteUserHome'].."/Settings" local userlua = props['SciteUserHome'].."/UserLuaScript.lua" local extlua = props['SciteUserHome'].."/_extensions.lua" if FileExists(userlua) then dofile(userlua) end if FileExists(extlua) then dofile(extlua) end