diff --git a/SciTE4AutoHotkey.sln b/SciTE4AutoHotkey.sln new file mode 100644 index 0000000..e7be725 --- /dev/null +++ b/SciTE4AutoHotkey.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SciTE", "scite\SciTE.vcxproj", "{2DC33974-65FD-4DAF-B522-ACA7A7AC9721}" + ProjectSection(ProjectDependencies) = postProject + {8FBDA743-F364-430E-BC7E-207D7469215A} = {8FBDA743-F364-430E-BC7E-207D7469215A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Scintilla", "Scintilla\Scintilla.vcxproj", "{8FBDA743-F364-430E-BC7E-207D7469215A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2DC33974-65FD-4DAF-B522-ACA7A7AC9721}.Debug|Win32.ActiveCfg = Debug|Win32 + {2DC33974-65FD-4DAF-B522-ACA7A7AC9721}.Debug|Win32.Build.0 = Debug|Win32 + {2DC33974-65FD-4DAF-B522-ACA7A7AC9721}.Debug|x64.ActiveCfg = Debug|x64 + {2DC33974-65FD-4DAF-B522-ACA7A7AC9721}.Debug|x64.Build.0 = Debug|x64 + {2DC33974-65FD-4DAF-B522-ACA7A7AC9721}.Release|Win32.ActiveCfg = Release|Win32 + {2DC33974-65FD-4DAF-B522-ACA7A7AC9721}.Release|Win32.Build.0 = Release|Win32 + {2DC33974-65FD-4DAF-B522-ACA7A7AC9721}.Release|x64.ActiveCfg = Release|x64 + {2DC33974-65FD-4DAF-B522-ACA7A7AC9721}.Release|x64.Build.0 = Release|x64 + {8FBDA743-F364-430E-BC7E-207D7469215A}.Debug|Win32.ActiveCfg = Debug|Win32 + {8FBDA743-F364-430E-BC7E-207D7469215A}.Debug|Win32.Build.0 = Debug|Win32 + {8FBDA743-F364-430E-BC7E-207D7469215A}.Debug|x64.ActiveCfg = Debug|x64 + {8FBDA743-F364-430E-BC7E-207D7469215A}.Debug|x64.Build.0 = Debug|x64 + {8FBDA743-F364-430E-BC7E-207D7469215A}.Release|Win32.ActiveCfg = Release|Win32 + {8FBDA743-F364-430E-BC7E-207D7469215A}.Release|Win32.Build.0 = Release|Win32 + {8FBDA743-F364-430E-BC7E-207D7469215A}.Release|x64.ActiveCfg = Release|x64 + {8FBDA743-F364-430E-BC7E-207D7469215A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/scintilla/Scintilla.vcxproj b/scintilla/Scintilla.vcxproj new file mode 100644 index 0000000..5780195 --- /dev/null +++ b/scintilla/Scintilla.vcxproj @@ -0,0 +1,395 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {8FBDA743-F364-430E-BC7E-207D7469215A} + Win32Proj + Scintilla + + + + DynamicLibrary + true + Unicode + v140_xp + + + DynamicLibrary + true + Unicode + v140_xp + + + DynamicLibrary + false + true + Unicode + v140_xp + + + DynamicLibrary + false + true + Unicode + v140_xp + + + + + + + + + + + + + + + + + + + true + + $(ProjectDir)include;$(ProjectDir)src;$(ProjectDir)lexlib;$(IncludePath) + SciLexer + + + true + $(SolutionDir)debug\x64\ + $(SolutionDir)debug\x64\temp\Scintilla\ + SciLexer + $(ProjectDir)include;$(ProjectDir)src;$(ProjectDir)lexlib;$(IncludePath) + + + + false + $(ProjectDir)include;$(ProjectDir)src;$(ProjectDir)lexlib;$(IncludePath) + + + $(SolutionDir)bin\x86\ + $(SolutionDir)obj\x86\Scintilla\ + SciLexer + + + false + $(ProjectDir)include;$(ProjectDir)src;$(ProjectDir)lexlib;$(IncludePath) + + + $(SolutionDir)bin\x64\ + $(SolutionDir)obj\x64\Scintilla\ + SciLexer + $(VCInstallDir)..\Common7\IDE;$(ExecutablePath) + + + + + + Level3 + Disabled + WIN32;SCI_LEXER;_DEBUG;_WINDOWS;_USRDLL;SCINTILLA_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + imm32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;SCI_LEXER;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Windows + true + imm32.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;SCI_LEXER;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + false + true + NoExtensions + true + + + Windows + false + true + true + imm32.lib;%(AdditionalDependencies) + false + false + + + + + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;SCI_LEXER;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + false + true + true + + + Windows + false + true + true + imm32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/scintilla/Scintilla.vcxproj.filters b/scintilla/Scintilla.vcxproj.filters new file mode 100644 index 0000000..8a0204c --- /dev/null +++ b/scintilla/Scintilla.vcxproj.filters @@ -0,0 +1,628 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/scintilla/include/SciLexer.h b/scintilla/include/SciLexer.h index 034060a..2e0d891 100644 --- a/scintilla/include/SciLexer.h +++ b/scintilla/include/SciLexer.h @@ -1770,4 +1770,51 @@ #define SCE_HEX_GARBAGE 18 /* --Autogenerated -- end of section automatically generated from Scintilla.iface */ +// AutoHotkey v1.x lexer +#define SCLEX_AHK1 200 +#define SCE_AHK_DEFAULT 0 +#define SCE_AHK_COMMENTLINE 1 +#define SCE_AHK_COMMENTBLOCK 2 +#define SCE_AHK_ESCAPE 3 +#define SCE_AHK_SYNOPERATOR 4 +#define SCE_AHK_EXPOPERATOR 5 +#define SCE_AHK_STRING 6 +#define SCE_AHK_NUMBER 7 +#define SCE_AHK_IDENTIFIER 8 +#define SCE_AHK_VARREF 9 +#define SCE_AHK_LABEL 10 +#define SCE_AHK_WORD_CF 11 +#define SCE_AHK_WORD_CMD 12 +#define SCE_AHK_WORD_FN 13 +#define SCE_AHK_WORD_DIR 14 +#define SCE_AHK_WORD_KB 15 +#define SCE_AHK_WORD_VAR 16 +#define SCE_AHK_WORD_SP 17 +#define SCE_AHK_WORD_UD 18 +#define SCE_AHK_VARREFKW 19 +#define SCE_AHK_ERROR 20 + +// AutoHotkey v2 lexer +#define SCLEX_AHK2 201 +#define SCE_AHK2_DEFAULT 0 +#define SCE_AHK2_COMMENTLINE 1 +#define SCE_AHK2_COMMENTBLOCK 2 +#define SCE_AHK2_ESCAPE 3 +#define SCE_AHK2_OPERATOR 4 +#define SCE_AHK2_STRING 5 +#define SCE_AHK2_NUMBER 6 +#define SCE_AHK2_WORDOP 7 +#define SCE_AHK2_VAR 8 +#define SCE_AHK2_FUNC 9 +#define SCE_AHK2_DIRECTIVE 10 +#define SCE_AHK2_LABEL 11 +#define SCE_AHK2_FLOW 12 +#define SCE_AHK2_BIV 13 +#define SCE_AHK2_BIF 14 +#define SCE_AHK2_ERROR 15 +#define SCE_AHK2_OBJPROP 16 +#define SCE_AHK2_OBJMETHOD 17 +#define SCE_AHK2_OBJPROP_BI 18 +#define SCE_AHK2_OBJMETHOD_BI 19 + #endif diff --git a/scintilla/lexers/LexAHK1.cxx b/scintilla/lexers/LexAHK1.cxx new file mode 100644 index 0000000..b002e3f --- /dev/null +++ b/scintilla/lexers/LexAHK1.cxx @@ -0,0 +1,510 @@ +// Scintilla source code edit control +/** @file LexAHK1.cxx + ** Lexer for AutoHotkey, simplified version + ** Written by Philippe Lhoste (PhiLho) + ** Some hacks by fincs to: + ** - Support object syntax + ** - Support ternary operators (? :) + ** - Fix folding + ** - Fix expression lines starting with ( as being misdetected as continuation sections + ** - Add ;{ and ;} section folding support + **/ +// Copyright 1998-2012 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(disable: 4786) +#endif + +#include +#include +#include +#include + +#include "ILexer.h" +#include "Scintilla.h" +#include "SciLexer.h" + +#include "PropSetSimple.h" +#include "WordList.h" +#include "LexAccessor.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "CharacterSet.h" +#include "LexerModule.h" +#include "OptionSet.h" + + +static inline bool IsAWordChar(const int ch) { + return ch >= 0x80 || (isascii(ch) && isalnum(ch)) || + ch == '_' || ch == '$' || //ch == '[' || ch == ']' || // fincs-edit + ch == '#' || ch == '@'; //|| ch == '?'; // fincs-edit +} + +// Expression operator +// ( ) + - * ** / // ! ~ ^ & << >> . < > <= >= = == != <> && || [ ] ? : +static inline bool IsExpOperator(const int ch) { + if (ch >= 0x80 || (isascii(ch) && isalnum(ch))) // Fast exit + return false; + return ch == '+' || ch == '-' || ch == '*' || ch == '/' || + ch == '(' || ch == ')' || ch == '.' || + ch == '=' || ch == '<' || ch == '>' || + ch == '&' || ch == '|' || ch == '^' || ch == '~' || ch == '!' || + ch == '[' || ch == ']' || ch == '?' || ch == ':'; // fincs-edit +} + +static void HighlightKeyword( + char currentWord[], + StyleContext &sc, + WordList *keywordlists[], + Accessor &styler) { + + WordList &controlFlow = *keywordlists[0]; + WordList &commands = *keywordlists[1]; + WordList &functions = *keywordlists[2]; + WordList &directives = *keywordlists[3]; + WordList &keysButtons = *keywordlists[4]; + WordList &variables = *keywordlists[5]; + WordList &specialParams = *keywordlists[6]; + WordList &userDefined = *keywordlists[7]; + + if (controlFlow.InList(currentWord)) { + sc.ChangeState(SCE_AHK_WORD_CF); + } else if (commands.InList(currentWord)) { + sc.ChangeState(SCE_AHK_WORD_CMD); + } else if (functions.InList(currentWord)) { + sc.ChangeState(SCE_AHK_WORD_FN); + } else if (currentWord[0] == '#' && directives.InList(currentWord + 1)) { + sc.ChangeState(SCE_AHK_WORD_DIR); + } else if (keysButtons.InList(currentWord)) { + sc.ChangeState(SCE_AHK_WORD_KB); + } else if (variables.InList(currentWord)) { + sc.ChangeState(SCE_AHK_WORD_VAR); + } else if (specialParams.InList(currentWord)) { + sc.ChangeState(SCE_AHK_WORD_SP); + } else if (userDefined.InList(currentWord)) { + sc.ChangeState(SCE_AHK_WORD_UD); + } else { + sc.ChangeState(SCE_AHK_DEFAULT); + } +} + +static bool LineHasChar(Accessor &styler, int pos, int ch) +{ + for (;;) + { + int c = styler.SafeGetCharAt(pos++, 0); + if (c == 0 || c == '\r' || c == '\n') + return false; + if (c == ch) + return true; + } +} + +static void ColouriseAHK1Doc( + unsigned int startPos, + int length, + int initStyle, + WordList *keywordlists[], + Accessor &styler) { + + WordList &keysButtons = *keywordlists[4]; + WordList &variables = *keywordlists[5]; + char currentWord[256]; + + // Do not leak onto next line + if (initStyle != SCE_AHK_COMMENTBLOCK && + initStyle != SCE_AHK_STRING) { + initStyle = SCE_AHK_DEFAULT; + } + int currentState = initStyle; + int nextState = -1; + + /* The AutoHotkey syntax is heavily context-dependent. + For example, for each command, the lexer knows if parameter #n + is a string, a variable, a number, an expression, etc. + I won't go this far, but I will try to handle most regular cases. + */ + // True if in a continuation section + bool bContinuationSection = (initStyle == SCE_AHK_STRING); + // Indicate if the lexer has seen only spaces since the start of the line + bool bOnlySpaces = (!bContinuationSection); + // Indicate if since the start of the line, lexer met only legal label chars + bool bIsLabel = false; + // Distinguish hotkeys from hotstring + bool bIsHotkey = false; + bool bIsHotstring = false; + // In an expression + bool bInExpression = false; + // A quoted string in an expression (share state with continuation section string) + bool bInExprString = false; + // To accept A-F chars in a number + bool bInHexNumber = false; + + StyleContext sc(startPos, length, initStyle, styler); + + for (; sc.More(); sc.Forward()) { + if (nextState >= 0) { + // I need to reset a state before checking new char + sc.SetState(nextState); + nextState = -1; + } + if (sc.state == SCE_AHK_SYNOPERATOR) { + // Only one char (if two detected, we move Forward() anyway) + sc.SetState(SCE_AHK_DEFAULT); + } + if (sc.atLineEnd && (bIsHotkey || bIsHotstring)) { + // I make the hotkeys and hotstrings more visible + // by changing the line end to LABEL style (if style uses eolfilled) + bIsHotkey = bIsHotstring = false; + sc.SetState(SCE_AHK_LABEL); + } + if (sc.atLineStart) { + if (sc.state != SCE_AHK_COMMENTBLOCK && + !bContinuationSection) { + // Prevent some styles from leaking back to previous line + sc.SetState(SCE_AHK_DEFAULT); + } + bOnlySpaces = true; + bIsLabel = false; + bInExpression = false; // I don't manage multiline expressions yet! + bInHexNumber = false; + } + + // Manage cases occuring in (almost) all states (not in comments) + if (sc.state != SCE_AHK_COMMENTLINE && + sc.state != SCE_AHK_COMMENTBLOCK && + !IsASpace(sc.ch)) { + if (sc.ch == '`') { + // Backtick, escape sequence + currentState = sc.state; + sc.SetState(SCE_AHK_ESCAPE); + sc.Forward(); + nextState = currentState; + continue; + } + if (sc.ch == '%' && !bIsHotstring && !bInExprString && + sc.state != SCE_AHK_VARREF && + sc.state != SCE_AHK_VARREFKW && + sc.state != SCE_AHK_ERROR) { + if (IsASpace(sc.chNext)) { + if (sc.state == SCE_AHK_STRING) { + // Illegal unquoted character! + sc.SetState(SCE_AHK_ERROR); + } else { + // % followed by a space is expression start + bInExpression = true; + } + } else { + // Variable reference + currentState = sc.state; + sc.SetState(SCE_AHK_SYNOPERATOR); + nextState = SCE_AHK_VARREF; + continue; + } + } + if (sc.state != SCE_AHK_STRING && !bInExpression) { + // Management of labels, hotkeys, hotstrings and remapping + + // Check if the starting string is a label candidate + if (bOnlySpaces && + sc.ch != ',' && sc.ch != ';' && sc.ch != ':' && + sc.ch != '%' && sc.ch != '`') { + // A label cannot start with one of the above chars + bIsLabel = true; + } + + // The current state can be IDENTIFIER or DEFAULT, + // depending if the label starts with a word char or not + if (bIsLabel && sc.ch == ':' && + (IsASpace(sc.chNext) || sc.atLineEnd)) { + // ?l/a|b\e^l!: + // Only ; comment should be allowed after + sc.ChangeState(SCE_AHK_LABEL); + sc.SetState(SCE_AHK_SYNOPERATOR); + nextState = SCE_AHK_DEFAULT; + continue; + } else if (sc.Match(':', ':')) { + if (bOnlySpaces) { + // Hotstring ::aa::Foo + bIsHotstring = true; + sc.SetState(SCE_AHK_SYNOPERATOR); + sc.Forward(); + nextState = SCE_AHK_LABEL; + continue; + } + // Hotkey F2:: or remapping a::b + bIsHotkey = true; + // Check if it is a known key + sc.GetCurrentLowered(currentWord, sizeof(currentWord)); + if (keysButtons.InList(currentWord)) { + sc.ChangeState(SCE_AHK_WORD_KB); + } + sc.SetState(SCE_AHK_SYNOPERATOR); + sc.Forward(); + if (bIsHotstring) { + nextState = SCE_AHK_STRING; + } + continue; + } + } + } + // Check if the current string is still a label candidate + // Labels are much more permissive than regular identifiers... + if (bIsLabel && + (sc.ch == ',' || sc.ch == '%' || sc.ch == '`' || IsASpace(sc.ch))) { + // Illegal character in a label + bIsLabel = false; + } + + // Determine if the current state should terminate. + if (sc.state == SCE_AHK_COMMENTLINE) { + if (sc.atLineEnd) { + sc.SetState(SCE_AHK_DEFAULT); + } + } else if (sc.state == SCE_AHK_COMMENTBLOCK) { + if (bOnlySpaces && sc.Match('*', '/')) { + // End of comment at start of line (skipping white space) + sc.Forward(); + sc.ForwardSetState(SCE_C_DEFAULT); + } + } else if (sc.state == SCE_AHK_EXPOPERATOR) { + if (!IsExpOperator(sc.ch)) { + sc.SetState(SCE_AHK_DEFAULT); + } + } else if (sc.state == SCE_AHK_STRING) { + if (bContinuationSection) { + if (bOnlySpaces && sc.ch == ')') { + // End of continuation section + bContinuationSection = false; + sc.SetState(SCE_AHK_SYNOPERATOR); + } + } else if (bInExprString) { + if (sc.ch == '\"') { + if (sc.chNext == '\"') { + // In expression string, double quotes are doubled to escape them + sc.Forward(); // Skip it + } else { + bInExprString = false; + sc.ForwardSetState(SCE_AHK_DEFAULT); + } + } else if (sc.atLineEnd) { + sc.ChangeState(SCE_AHK_ERROR); + } + } else { + if (sc.ch == ';' && IsASpace(sc.chPrev)) { + // Line comments after code must be preceded by a space + sc.SetState(SCE_AHK_COMMENTLINE); + } + } + } else if (sc.state == SCE_AHK_NUMBER) { + if (bInHexNumber) { + if (!IsADigit(sc.ch, 16)) { + bInHexNumber = false; + sc.SetState(SCE_AHK_DEFAULT); + } + } else if (!(IsADigit(sc.ch) || sc.ch == '.')) { + sc.SetState(SCE_AHK_DEFAULT); + } + } else if (sc.state == SCE_AHK_IDENTIFIER) { + if (!IsAWordChar(sc.ch)) { + sc.GetCurrentLowered(currentWord, sizeof(currentWord)); + HighlightKeyword(currentWord, sc, keywordlists, styler); + if (strcmp(currentWord, "if") == 0) { + bInExpression = true; + } + sc.SetState(SCE_AHK_DEFAULT); + } + } else if (sc.state == SCE_AHK_VARREF) { + if (sc.ch == '%') { + // End of variable reference + sc.GetCurrentLowered(currentWord, sizeof(currentWord)); + if (variables.InList(currentWord)) { + sc.ChangeState(SCE_AHK_VARREFKW); + } + sc.SetState(SCE_AHK_SYNOPERATOR); + nextState = currentState; + continue; + } else if (!IsAWordChar(sc.ch)) { + // Oops! Probably no terminating % + sc.ChangeState(SCE_AHK_ERROR); + } + } else if (sc.state == SCE_AHK_LABEL) { + // Hotstring -- modifier or trigger string :*:aa::Foo or ::aa::Foo + if (sc.ch == ':') { + sc.SetState(SCE_AHK_SYNOPERATOR); + if (sc.chNext == ':') { + sc.Forward(); + } + nextState = SCE_AHK_LABEL; + continue; + } + } + + // Determine if a new state should be entered + if (sc.state == SCE_AHK_DEFAULT) { + if (sc.ch == ';' && + (bOnlySpaces || IsASpace(sc.chPrev))) { + // Line comments are alone on the line or are preceded by a space + sc.SetState(SCE_AHK_COMMENTLINE); + } else if (bOnlySpaces && sc.Match('/', '*')) { + // Comment at start of line (skipping white space) + sc.SetState(SCE_AHK_COMMENTBLOCK); + sc.Forward(); + } else if (sc.ch == '{' || sc.ch == '}') { + // Code block or special key {Enter} + sc.SetState(SCE_AHK_SYNOPERATOR); + } else if (bOnlySpaces && sc.ch == '(' && !LineHasChar(styler, sc.currentPos, ')')) { + // Continuation section + bContinuationSection = true; + sc.SetState(SCE_AHK_SYNOPERATOR); + nextState = SCE_AHK_STRING; // !!! Can be an expression! + } else if (sc.Match(':', '=') || + sc.Match('+', '=') || + sc.Match('-', '=') || + sc.Match('/', '=') || + sc.Match('*', '=')) { + // Expression assignment + bInExpression = true; + sc.SetState(SCE_AHK_SYNOPERATOR); + sc.Forward(); + nextState = SCE_AHK_DEFAULT; + } else if (IsExpOperator(sc.ch)) { + sc.SetState(SCE_AHK_EXPOPERATOR); + } else if (sc.ch == '\"') { + bInExprString = true; + sc.SetState(SCE_AHK_STRING); + } else if (sc.ch == '0' && (sc.chNext == 'x' || sc.chNext == 'X')) { + // Hexa, skip forward as we don't accept any other alpha char (beside A-F) inside + bInHexNumber = true; + sc.SetState(SCE_AHK_NUMBER); + sc.Forward(2); + } else if (isdigit(sc.ch) || (sc.ch == '.' && isdigit(sc.chNext))) { + sc.SetState(SCE_AHK_NUMBER); + } else if (IsAWordChar(sc.ch)) { + sc.SetState(SCE_AHK_IDENTIFIER); + } else if (sc.ch == ',') { + sc.SetState(SCE_AHK_SYNOPERATOR); + nextState = SCE_AHK_DEFAULT; + } else if (sc.ch == ':') { + if (bOnlySpaces) { + // Start of hotstring :*:foo::Stuff or ::btw::Stuff + bIsHotstring = true; + sc.SetState(SCE_AHK_SYNOPERATOR); + if (sc.chNext == ':') { + sc.Forward(); + } + nextState = SCE_AHK_LABEL; + } + } else if (IsAWordChar(sc.ch)) { + sc.SetState(SCE_AHK_IDENTIFIER); + } + } + if (!IsASpace(sc.ch)) { + bOnlySpaces = false; + } + } + // End of file: complete any pending changeState + if (sc.state == SCE_AHK_IDENTIFIER) { + sc.GetCurrentLowered(currentWord, sizeof(currentWord)); + HighlightKeyword(currentWord, sc, keywordlists, styler); + } else if (sc.state == SCE_AHK_STRING && bInExprString) { + sc.ChangeState(SCE_AHK_ERROR); + } else if (sc.state == SCE_AHK_VARREF) { + sc.ChangeState(SCE_AHK_ERROR); + } + sc.Complete(); +} + +static void FoldAHK1Doc(unsigned int startPos, int length, int initStyle, + WordList *[], Accessor &styler) { + bool foldComment = styler.GetPropertyInt("fold.comment") != 0; + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + unsigned int endPos = startPos + length; + bool bOnlySpaces = true; + int lineCurrent = styler.GetLine(startPos); + int levelCurrent = SC_FOLDLEVELBASE; + if (lineCurrent > 0) { + levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16; + } + int levelNext = levelCurrent; + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + for (unsigned int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int stylePrev = style; + style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + if (foldComment && style == SCE_AHK_COMMENTBLOCK) { + if (stylePrev != SCE_AHK_COMMENTBLOCK) { + levelNext++; + } else if ((styleNext != SCE_AHK_COMMENTBLOCK) && !atEOL) { + // Comments don't end at end of line and the next character may be unstyled. + levelNext--; + } + } + if (foldComment && style == SCE_AHK_COMMENTLINE) { + if (ch == ';') { + if (chNext == '{') { + levelNext ++; + } else if (chNext == '}') { + levelNext --; + } + } + } + if (style == SCE_AHK_SYNOPERATOR) { + if (ch == '(' || ch == '{') { + levelNext++; + } else if (ch == ')' || ch == '}') { + levelNext--; + } + } + if (atEOL || (i == endPos-1)) { + int level = levelCurrent | (levelNext << 16); + if (bOnlySpaces && foldCompact) { + // Empty line + level |= SC_FOLDLEVELWHITEFLAG; + } + if (levelCurrent < levelNext) { + level |= SC_FOLDLEVELHEADERFLAG; + } + if (level != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, level); + } + lineCurrent++; + levelCurrent = levelNext; + if (atEOL && (i == static_cast(styler.Length()-1))) { + // There is an empty line at end of file so give it same level and empty + styler.SetLevel(lineCurrent, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG); + } + bOnlySpaces = true; + } + if (!isspacechar(ch)) { + bOnlySpaces = false; + } + } +} + +static const char * const ahkWordListDesc[] = { + "Flow of control", + "Commands", + "Functions", + "Directives", + "Keys & buttons", + "Variables", + "Special Parameters (keywords)", + "User defined", + 0 +}; + +LexerModule lmAHK1(SCLEX_AHK1, ColouriseAHK1Doc, "ahk1", FoldAHK1Doc, ahkWordListDesc); diff --git a/scintilla/lexers/LexAHK2.cxx b/scintilla/lexers/LexAHK2.cxx new file mode 100644 index 0000000..5c51104 --- /dev/null +++ b/scintilla/lexers/LexAHK2.cxx @@ -0,0 +1,1606 @@ +// Scintilla source code edit control +/** @file LexAHK2.cxx + ** Lexer for AutoHotkey v2 + ** Written by fincs + **/ +// Copyright 1998-2014 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ILexer.h" +#include "Scintilla.h" +#include "SciLexer.h" + +#include "WordList.h" +#include "LexAccessor.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "CharacterSet.h" +#include "LexerModule.h" +#include "OptionSet.h" +#include "SparseState.h" + +struct OptionsAHK2 +{ + bool Fold; + bool FoldComment; + bool FoldCompact; + + OptionsAHK2() { } +}; + +static const char* const ahk2WordListDesc[] = +{ + "Built-in Functions", + "Built-in Variables", + "Flow of Control", + "Word Operators", + "Keys and Buttons", + "Built-in Object Properties", + "Built-in Object Methods", + NULL +}; + +struct OptionSetAHK2 : public OptionSet +{ + OptionSetAHK2() + { + DefineWordListSets(ahk2WordListDesc); + + DefineProperty("fold", &OptionsAHK2::Fold); + + DefineProperty("fold.compact", &OptionsAHK2::FoldCompact); + + DefineProperty("fold.comment", &OptionsAHK2::FoldComment, + "This option enables folding multi-line comments and explicit fold points when using the AutoHotkey v2 lexer." + " Explicit fold points allows adding extra folding by placing a ;{ comment at the start and a ;}" + " at the end of a section that should fold."); + } +}; + +typedef enum +{ + LINE_NODATA, + LINE_BLANK, LINE_MLCOMMENT, LINE_COMMAND, LINE_EXPR, LINE_CONTSECT, LINE_LABEL, LINE_HOTKEY, LINE_REMAP, LINE_HOTSTRING, LINE_DIRECTIVE, +} AhkLineType; + +// These are used for the %expression% stack +enum +{ + DT_VAR, // Double deref in expression + DT_STRING, // Quoted string with %interpolation% + DT_CMDSTRING, // String in traditional command syntax with %interpolation% + DT_CMDSTRING2, // Used for % forceExpressionMode in command syntax +}; + +#define MAX_NESTED_EXPR 5 // Same as Ruby lexer. + +struct AhkLineInfo +{ + AhkLineType type; + AhkLineType context; + + bool bCSWouldBeCmdExpr, bEndedInStr; + bool bCSCmdExpr2; + bool bIsIf; + int CSCmdExprBraceNest; + bool CSCmdExprSQString; + int specialState; + + // These are used in continuation sections + bool bLiteralComment, bLiteralQuote, bLiteralPercent, bLiteralComma, bLiteralEscape; + + // Nested expr information + int NestedExprPos; + char NestedExprCtx[MAX_NESTED_EXPR]; + char NestedExprBrace[MAX_NESTED_EXPR+1]; + bool NestedExprSQString[MAX_NESTED_EXPR+1]; + + AhkLineInfo() + { + Init(); + } + + void Init() + { + type = LINE_NODATA; + context = LINE_BLANK; + bCSWouldBeCmdExpr = false; + bEndedInStr = false; + bIsIf = false; + CSCmdExprBraceNest = 0; + CSCmdExprSQString = false; + specialState = 0; + + bLiteralComment = false; + bLiteralQuote = false; + bLiteralPercent = false; + bLiteralComma = false; + bLiteralEscape = false; + + NestedExprPos = 0; + NestedExprBrace[0] = 0; + NestedExprSQString[0] = false; + } + + void InitContext(const AhkLineInfo& base) + { + memcpy(this, &base, sizeof(*this)); + } + + bool EnterNestedExpr(int type) + { + if (NestedExprPos == MAX_NESTED_EXPR) + return false; + NestedExprCtx[NestedExprPos++] = type; + NestedExprBrace[NestedExprPos] = 0; + NestedExprSQString[NestedExprPos] = false; + return true; + } + + int LeaveNestedExpr() + { + if (NestedExprPos == 0) + return -1; + return NestedExprCtx[--NestedExprPos]; + } + + bool InNestedExpr() + { + return !!NestedExprPos; + } + + int CurNestedExprCtx() + { + return NestedExprPos ? NestedExprCtx[NestedExprPos-1] : -1; + } + + void ResetStack(bool varsToo = true) + { + if (varsToo) + { + bCSWouldBeCmdExpr = false; + bEndedInStr = false; + } + NestedExprPos = 0; + NestedExprBrace[0] = 0; + } + + void EnterBrace() { NestedExprBrace[NestedExprPos] ++; } + void LeaveBrace() { NestedExprBrace[NestedExprPos] --; } + bool IsOutsideBrace() { return !NestedExprBrace[NestedExprPos]; } + + bool IsSQString() { return NestedExprSQString[NestedExprPos]; } + void SetSQString(bool b) { NestedExprSQString[NestedExprPos] = b; } + + bool IsSpecial() { return context == LINE_HOTKEY || context == LINE_REMAP || context == LINE_HOTSTRING; } +}; + +class AhkProvideLineInfo +{ + std::vector st; +public: + AhkLineInfo& get(int line) + { + assert (line >= 0); + if ((size_t) line >= st.size()) + { + st.resize(line+1); + st[line] = AhkLineInfo(); + return st[line]; + }else + return st[line]; + } +}; + +class LexerAHK2 : public ILexer +{ + OptionsAHK2 options; + OptionSetAHK2 opSet; + WordList bifList, bivList, flowOfControl, wordOps, keys, biObjProps, biObjMethods; + AhkProvideLineInfo linfos; + + LexerAHK2() { } + ~LexerAHK2() { } + +public: + + virtual int SCI_METHOD Version() const { return lvOriginal; } + virtual void SCI_METHOD Release() { delete this; } + virtual const char* SCI_METHOD PropertyNames() { return opSet.PropertyNames(); } + virtual int SCI_METHOD PropertyType(const char* name) { return opSet.PropertyType(name); } + virtual const char* SCI_METHOD DescribeProperty(const char* name) { return opSet.DescribeProperty(name); } + virtual int SCI_METHOD PropertySet(const char* key, const char* val); + virtual const char* SCI_METHOD DescribeWordListSets() { return opSet.DescribeWordListSets(); } + virtual int SCI_METHOD WordListSet(int n, const char* wl); + virtual void SCI_METHOD Lex(unsigned int startPos, int lengthDoc, int initStyle, IDocument* pAccess); + virtual void SCI_METHOD Fold(unsigned int startPos, int lengthDoc, int initStyle, IDocument* pAccess); + virtual void* SCI_METHOD PrivateCall(int operation, void* pointer) { return NULL; } + + static ILexer* LexerFactoryAHK2() + { + try { return new LexerAHK2(); } + catch(...) { return NULL; } + } +}; + +int SCI_METHOD LexerAHK2::PropertySet(const char* key, const char* val) +{ + return opSet.PropertySet(&options, key, val) ? 0 : -1; +} + +int SCI_METHOD LexerAHK2::WordListSet(int n, const char* wl) +{ + WordList* pWl = NULL; + switch(n) + { + case 0: pWl = &bifList; break; + case 1: pWl = &bivList; break; + case 2: pWl = &flowOfControl; break; + case 3: pWl = &wordOps; break; + case 4: pWl = &keys; break; + case 5: pWl = &biObjProps; break; + case 6: pWl = &biObjMethods; break; + default: return -1; + } + pWl->Set(wl); + return 0; +} + +LexerModule lmAHK2(SCLEX_AHK2, LexerAHK2::LexerFactoryAHK2, "ahk2", ahk2WordListDesc); + +// +// End of boilerplate +// + +static inline bool isWhitespace(int c) +{ + return c == ' ' || c == '\t'; +} + +static inline bool isNumeric(int c, bool allowHex = false) +{ + return (c >= '0' && c <= '9') || (allowHex && ((c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))); +} + +static inline bool isIdChar(int c, bool allowNumeric = true) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (allowNumeric && isNumeric(c)) || c == '_' || c > 0x7F; +} + +static inline bool isExprOp(int c) +{ + return c == '+' || c == '-' || c == '*' || c == '/' || c == '.' || c == '(' || c == ')' || c == '=' || c == '[' || c == ']' + || c == '!' || c == '<' || c == '>' || c == '&' || c == '|' || c == '^' || c == '~' || c == '?' || c == ':' || c == ','; +} + +static inline bool isOpeningBrace(int c) +{ + return c == '(' || c == '[' || c == '{'; +} + +static inline bool isClosingBrace(int c) +{ + return c == ')' || c == ']' || c == '}'; +} + +static inline bool isHotkeyModifier(int c) +{ + return c == '#' || c == '!' || c == '^' || c == '+' || c == '&' || c == '<' || c == '>' || c == '*' || c == '~' || c == '$'; +} + +static inline void chopcr(char* buf) +{ + size_t pos = strlen(buf) - 1; + if (buf[pos] == '\r') buf[pos] = 0; +} + +static inline bool andOrOrFollows(StyleContext& sc) +{ + return sc.MatchIgnoreCase("and ") || sc.MatchIgnoreCase("and\t") || + sc.MatchIgnoreCase("or ") || sc.MatchIgnoreCase("or\t"); +} + +static bool isValidKey(char* key, bool allowUp, WordList& keyList, bool* bAllowsForRemap = nullptr) +{ + // Check and remove 'up' modifier + if (allowUp) do + { + char* ws = key; + for (; *ws && !isWhitespace(*ws); ws++); + if (!*ws) break; + *ws++ = 0; + for (; *ws && isWhitespace(*ws); ws++); + if (ws[0] != 'u' || ws[1] != 'p' || ws[2]) + return false; + if (*bAllowsForRemap) + bAllowsForRemap = false; + } while(0); + + // Check for vkNN or scNNN + if ((key[0] == 'v' && key[1] == 'k') || (key[0] == 's' && key[1] == 'c')) + { + char* pos = key; + for (key+=2; *key && isNumeric(*key, true); key++); + if (*key) + return false; //garbage + // Check that the length of the sequence is appropriate + // Looks like AHK does not enforce it having to be 2 or 3 + int len = key-pos-2; + return len > 0; // && len <= (*pos == 'v' ? 3 : 2); + } + + // Check that it's one of the keys + if (keyList.InList(key)) + return true; + + // Else check for single character + return key[0] && !key[1]; +} + +static bool validateHotkey(const char* hkt, WordList& keyList, bool& allowsForRemap) +{ + // Copy argument so we can manipulate it + char buf[64]; + strncpy(buf, hkt, sizeof(buf)-1); + buf[sizeof(buf)-1] = 0; + char* hk = buf; + + // Shortcut for single-character hotkey definitions + if (!hk[1]) + return true; + + // Ignore modifiers + for (; *hk && isHotkeyModifier(*hk); hk++); + // Ignore escape sequence + if (*hk == '`') hk++; + char* key2 = strstr(hk, " & "); + // Parse second argument: remove whitespace + if (key2) + { + allowsForRemap = false; + char* k = key2; + for (; isWhitespace(k[-1]); k--); + *k = 0; + key2 += 3; + for (; *key2 && isWhitespace(*key2); key2++); + } + // Validate first key + if (!isValidKey(hk, true, keyList, &allowsForRemap)) + return false; + // Validate second key (if specified) + if (key2 && !isValidKey(key2, false, keyList)) + return false; + return true; +} + +static bool validateRemapTarget(const char* hkt, WordList& keyList) +{ + // Copy argument so we can manipulate it + char buf[64]; + strncpy(buf, hkt, sizeof(buf)-1); + buf[sizeof(buf)-1] = 0; + + // Remove comment and trailing whitespace + char* ws = buf; + for (; *ws && !isWhitespace(*ws); ws++); + if (*ws) + { + *ws++ = 0; + for (; *ws && isWhitespace(*ws); ws++); + if (*ws && *ws != ';') + return false; // WRONG + } + + // Check for empty lines + if (!*buf) + return false; + + // Exceptions + if (strcmp(buf, "pause")==0 || strcmp(buf, "break")==0 || strcmp(buf, "return")==0) + return false; + + // Shortcut for single-character target + if (!buf[1]) + return true; + + // Any other case + ws = buf; + for (; *ws && isHotkeyModifier(*ws); ws++); + if (*ws == '`') ws++; + return *ws && isValidKey(ws, false, keyList); +} + +static AhkLineType guessLineType(char* buf, WordList& keyList, AhkLineInfo& info) +{ + // Check for hotstring: this section is based off AutoHotkey source code + if (buf[0] == ':' && buf[1]) do + { + char* hsStart = NULL; + if (buf[1] == ':') + hsStart = buf+2; + else + { + if (!(hsStart = strchr(buf+1,':'))) + break; + hsStart++; + } + for (; *hsStart; hsStart++) + { + if (hsStart[0] == ':' && hsStart[1] == ':') + return LINE_HOTSTRING; + if (hsStart[0] == '`') + hsStart++; + } + // otherwise NOT a hotstring + } while(0); + + // Check for hotkey + char* pos = NULL; + if ((pos = strstr(buf, "::"))) + { + // The following hack makes ::: (i.e. colon hotkey) work + if (pos == buf && buf[2] == ':') + pos++; + char* hkSuc = pos+2; + // Trim whitespace + for (; pos > buf && isWhitespace(pos[-1]); pos--); + for (; *hkSuc && isWhitespace(*hkSuc); hkSuc++); + int temp = *pos; + *pos = 0; + bool bAllowsForRemap = true; + if (validateHotkey(buf, keyList, bAllowsForRemap)) + return (bAllowsForRemap && validateRemapTarget(hkSuc, keyList)) ? LINE_REMAP : LINE_HOTKEY; + *pos = temp; + } + + // Check for directive + if (buf[0] == '#') + return LINE_DIRECTIVE; + + // If this is not a potential type 2 continuation section... + if (buf[0] != '(') + { + // Check for Loop subcommands + if (buf[0] == 'l' && buf[1] == 'o' && buf[2] == 'o' && buf[3] == 'p') do + { + char* pos = buf+4; + bool alreadyHadComma = false; + if (*pos == ',') pos++, alreadyHadComma = true; + else if (!*pos || !isWhitespace(*pos)) break; + for (; *pos && isWhitespace(*pos); pos++); + if (!*pos) break; + if (*pos == ',' && !alreadyHadComma) + { + pos++; + for (; *pos && isWhitespace(*pos); pos++); + if (!*pos) break; + } + + int wl = 0; + if (strncmp("files", pos, 5) == 0 || strncmp("parse", pos, 5) == 0) + wl = 5; + else if (strncmp("read", pos, 4) == 0) + wl = 4; + else if (strncmp("reg", pos, 3) == 0) + wl = 3; + else + break; + + for (pos += wl; *pos && isWhitespace(*pos); pos++); + + if (*pos == ',') + // This is indeed a Loop subcommand + return LINE_COMMAND; + } while(0); + + // Let the normal rules process this line + return LINE_NODATA; + } + + // Before assuring that this is a type 2 continuation section, + // we must check that there are no )'s (except inside the Join option) + bool argStart = true, isJoin = false; + bool litComment = true, litQuote = true, litPerc = false, litComma = true, litEscape = false; + for (buf++; *buf; buf++) + { + int c = *buf; + if (!c || c == '\n' || c == '\r') + break; + if (isWhitespace(c)) + { + argStart = true, isJoin = false; + continue; + } + if (argStart) + { + // Check for Join option + if (c == 'j' && buf[1] == 'o' && buf[2] == 'i' && buf[3] == 'n') + isJoin = true; + // Check for other options + switch (c) + { + case 'c': litComment = false; break; + case 'q': litQuote = false; break; + case '%': litPerc = true; break; + case ',': litComma = false; break; + case '`': litEscape = true; break; + } + } + if (!isJoin && c == ')') + return LINE_EXPR; + argStart = false; + } + + // Fill in CS information + info.bLiteralComma = litComma; + info.bLiteralComment = litComment; + info.bLiteralEscape = litEscape; + info.bLiteralPercent = litPerc; + info.bLiteralQuote = litQuote; + + // Above section did not object to this being a CS + return LINE_CONTSECT; +} + +static AhkLineType guessLineType(StyleContext& sc, WordList& keyList, AhkLineInfo& info) +{ + char buf[512]; + for (int i = 0; i < sizeof(buf)-1; i ++) + { + int c = sc.GetRelativeCharacter(i); + // Translate uppercase characters to lowercase and non-ASCII characters into dummy 0x80 + c = (c >= 0 && c < 0x80) ? tolower(c) : 0x80; + if (c == '\n' || c == '\r') + c = 0; + buf[i] = c; + if (!c) + break; + } + buf[sizeof(buf)-1] = 0; + return guessLineType(buf, keyList, info); +} + +void SCI_METHOD LexerAHK2::Lex(unsigned int startPos, int lengthDoc, int initStyle, IDocument* pAccess) +{ + LexAccessor styler(pAccess); + + int curLine = styler.GetLine(startPos); + AhkLineInfo prevInfo = curLine > 0 ? linfos.get(curLine - 1) : AhkLineInfo(); // make copy + AhkLineInfo* curInfo = &linfos.get(curLine); + curInfo->InitContext(prevInfo); + if (curInfo->type != LINE_CONTSECT) + { + curInfo->type = LINE_BLANK; + if (initStyle != SCE_AHK2_COMMENTBLOCK) + initStyle = SCE_AHK2_DEFAULT; + } + + bool bNewLine = true, bInitialWS = true, bBegOfLine = true, bPhysBegOfLine = true, bKnowType = false, bIsHex = false; + int backtrackPos = 0; + int cmdObjSynCount = 0; + bool bContSectArgs = false; + bool bIsElse = false; + + char wordbuf[256]; + + StyleContext sc(startPos, lengthDoc, initStyle, styler); + + for (; sc.More(); sc.Forward()) + { + int ch = sc.ch; + if (bNewLine) + { + bNewLine = false; + bInitialWS = true; + bKnowType = false; + bIsElse = false; + } + + _loopHead: + + bBegOfLine = bInitialWS; + if (!bBegOfLine) bPhysBegOfLine = false; + + if (ch == '\r' && sc.chNext == '\n') + { + sc.Forward(); + ch = sc.ch; + } + + if (ch == '\n') + { + if (sc.state == SCE_AHK2_VAR) + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + chopcr(wordbuf); + if (bivList.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_BIV); + if (wordOps.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_WORDOP); + } else if (sc.state == SCE_AHK2_OBJPROP) + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + chopcr(wordbuf); + if (biObjProps.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_OBJPROP_BI); + } else if (sc.state == SCE_AHK2_FUNC) + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + chopcr(wordbuf); + if (flowOfControl.InList(wordbuf)) + { + sc.ChangeState(SCE_AHK2_FLOW); + curInfo->bIsIf = strcmp(wordbuf, "if") == 0; + } else if (bifList.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_BIF); + } else if (sc.state == SCE_AHK2_OBJMETHOD) + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + chopcr(wordbuf); + if (biObjMethods.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_OBJMETHOD_BI); + } else if (sc.state == SCE_AHK2_ESCAPE) + sc.SetState(SCE_AHK2_STRING); + + if (curInfo->type == LINE_CONTSECT) + { + if (bContSectArgs) + { + if (curInfo->bEndedInStr) + { + sc.SetState(SCE_AHK2_STRING); + curInfo->bEndedInStr = false; + } else + sc.SetState(SCE_AHK2_DEFAULT); + bContSectArgs = false; + } else if (sc.state != SCE_AHK2_STRING) + sc.SetState(SCE_AHK2_DEFAULT); + bNewLine = true; + bPhysBegOfLine = true; + prevInfo.InitContext(*curInfo); + curInfo = &linfos.get(++curLine); + curInfo->InitContext(prevInfo); + continue; + } + + int nestedExprCtx = curInfo->CurNestedExprCtx(); + if (nestedExprCtx == DT_CMDSTRING2 || nestedExprCtx == DT_CMDSTRING) + { + curInfo->CSCmdExprBraceNest = curInfo->NestedExprBrace[curInfo->NestedExprPos]; + curInfo->CSCmdExprSQString = curInfo->NestedExprSQString[curInfo->NestedExprPos]; + curInfo->LeaveNestedExpr(); + curInfo->bCSWouldBeCmdExpr = true; + curInfo->bCSCmdExpr2 = nestedExprCtx == DT_CMDSTRING2; + if (sc.state != SCE_AHK2_STRING) + sc.SetState(SCE_AHK2_DEFAULT); + } + + if (sc.state == SCE_AHK2_STRING) + curInfo->bEndedInStr = true; + + // New line will be reached + bNewLine = true; + bPhysBegOfLine = true; + prevInfo.InitContext(*curInfo); + curInfo = &linfos.get(++ curLine); + curInfo->InitContext(prevInfo); + curInfo->type = LINE_BLANK; + if (sc.state != SCE_AHK2_COMMENTBLOCK) + sc.SetState(SCE_AHK2_DEFAULT); + else + curInfo->type = LINE_MLCOMMENT; + continue; + } + + if (bInitialWS) + { + if(!isWhitespace(ch)) + bInitialWS = false; + else + continue; + } + + if (curInfo->type == LINE_CONTSECT) + { + bKnowType = true; + + if (bContSectArgs) + continue; + + // Check for CS end + if (bPhysBegOfLine && ch == ')') + { + int prevState = sc.state; + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + sc.SetState(prevState); + ch = sc.ch; + curInfo->type = curInfo->context; + curInfo->bLiteralComma = false; + curInfo->bLiteralComment = false; + curInfo->bLiteralEscape = false; + curInfo->bLiteralPercent = false; + curInfo->bLiteralQuote = false; + goto _loopHead; + } + } + + if (sc.state == SCE_AHK2_COMMENTBLOCK) + { + if (bPhysBegOfLine && sc.Match('*', '/')) + { + // Fake new line + curInfo->type = LINE_BLANK; + bInitialWS = true; + bKnowType = false; + bPhysBegOfLine = false; + sc.Forward(2); + sc.SetState(SCE_AHK2_DEFAULT); + ch = sc.ch; + goto _loopHead; + } + continue; + } + + if (sc.state == SCE_AHK2_COMMENTLINE || sc.state == SCE_AHK2_ERROR) + continue; + + if (ch == ';' && !curInfo->bLiteralComment && (bBegOfLine || isWhitespace(sc.chPrev))) + { + if (bKnowType && sc.state == SCE_AHK2_STRING) + curInfo->bEndedInStr = true; + sc.SetState(SCE_AHK2_COMMENTLINE); + continue; + } + + if (curInfo->type == LINE_CONTSECT) + goto _CSJump; + + if (bPhysBegOfLine && sc.Match('/', '*')) + { + curInfo->type = LINE_MLCOMMENT; + sc.SetState(SCE_AHK2_COMMENTBLOCK); + continue; + } + + AhkLineType guess; + if (bPhysBegOfLine && !bKnowType && (guess = guessLineType(sc, keys, *curInfo)) != LINE_NODATA) + { + bKnowType = true; + curInfo->type = guess; + if (guess != LINE_CONTSECT) + { + curInfo->context = guess; + curInfo->specialState = 0; + curInfo->ResetStack(); + } + + switch (guess) + { + case LINE_DIRECTIVE: + sc.SetState(SCE_AHK2_DIRECTIVE); + break; + case LINE_HOTKEY: + sc.SetState(SCE_AHK2_LABEL); + break; + case LINE_REMAP: + sc.SetState(SCE_AHK2_STRING); + break; + case LINE_HOTSTRING: + sc.SetState(SCE_AHK2_OPERATOR); + if (sc.chNext == ':') + { + curInfo->specialState ++; + sc.Forward(); + } + break; + case LINE_COMMAND: + sc.SetState(SCE_AHK2_FLOW); + break; + case LINE_EXPR: + sc.SetState(SCE_AHK2_DEFAULT); + goto _loopHead; + case LINE_CONTSECT: + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + sc.SetState(SCE_AHK2_STRING); + bContSectArgs = true; + if (curInfo->bCSWouldBeCmdExpr) + { + curInfo->EnterNestedExpr(curInfo->bCSCmdExpr2 ? DT_CMDSTRING2 : DT_CMDSTRING); + curInfo->NestedExprBrace[curInfo->NestedExprPos] = curInfo->CSCmdExprBraceNest; + curInfo->NestedExprSQString[curInfo->NestedExprPos] = curInfo->CSCmdExprSQString; + curInfo->bCSWouldBeCmdExpr = false; + } + ch = sc.ch; + goto _loopHead; + } + continue; + } + + if (bBegOfLine && (ch == '{' || ch == '}')) + { + sc.SetState(SCE_AHK2_OPERATOR); + // Fake new line + curInfo->context = LINE_BLANK; + bInitialWS = true; + bKnowType = false; + bPhysBegOfLine = false; + sc.Forward(); + sc.SetState(SCE_AHK2_DEFAULT); + ch = sc.ch; + goto _loopHead; + } + + bool isCSWordOp = false; + if (bBegOfLine && (isExprOp(ch) || (isCSWordOp = andOrOrFollows(sc)))) + { + bKnowType = true; + if (!isCSWordOp && (sc.Match('+', '+') || sc.Match('-', '-'))) + { + // Expression + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + curInfo->type = LINE_EXPR; + curInfo->context = LINE_EXPR; + curInfo->ResetStack(); + continue; + } + switch (curInfo->context) + { + case LINE_COMMAND: + curInfo->type = LINE_COMMAND; + if (curInfo->InNestedExpr() || (curInfo->bCSWouldBeCmdExpr && curInfo->EnterNestedExpr(curInfo->bCSCmdExpr2 ? DT_CMDSTRING2 : DT_CMDSTRING))) + { + // This is a continuation of the last inner expression + curInfo->type = LINE_EXPR; + bool wasInForceExpr = curInfo->bCSWouldBeCmdExpr; + curInfo->bCSWouldBeCmdExpr = false; + if (wasInForceExpr) + { + curInfo->NestedExprBrace[curInfo->NestedExprPos] = curInfo->CSCmdExprBraceNest; + curInfo->NestedExprSQString[curInfo->NestedExprPos] = curInfo->CSCmdExprSQString; + } + if (!curInfo->bEndedInStr) + { + if (isCSWordOp) + sc.SetState(SCE_AHK2_VAR); + else + { + sc.SetState(SCE_AHK2_OPERATOR); + if (wasInForceExpr && curInfo->bCSCmdExpr2 && curInfo->IsOutsideBrace()) + { + // The inner expression finishes here! + sc.Forward(); + sc.SetState(SCE_AHK2_STRING); + curInfo->LeaveNestedExpr(); + ch = sc.ch; + goto _loopHead; + } + } + } else + { + sc.SetState(SCE_AHK2_STRING); + curInfo->bEndedInStr = false; + } + } else if (!curInfo->bEndedInStr) + sc.SetState(ch != ',' ? (isCSWordOp ? SCE_AHK2_VAR : SCE_AHK2_STRING) : SCE_AHK2_OPERATOR); + else + { + curInfo->bEndedInStr = false; + sc.SetState(SCE_AHK2_STRING); + } + continue; + case LINE_EXPR: + curInfo->type = LINE_EXPR; + curInfo->bCSWouldBeCmdExpr = false; + if (!curInfo->bEndedInStr) + sc.SetState(isCSWordOp ? SCE_AHK2_VAR : SCE_AHK2_OPERATOR); + else + { + curInfo->bEndedInStr = false; + sc.SetState(SCE_AHK2_STRING); + } + continue; + case LINE_HOTSTRING: + sc.SetState(SCE_AHK2_STRING); + curInfo->bCSWouldBeCmdExpr = false; + goto _loopHead; // Retry + default: + sc.SetState(SCE_AHK2_ERROR); + curInfo->bCSWouldBeCmdExpr = false; + curInfo->type = LINE_BLANK; + curInfo->context = LINE_BLANK; + curInfo->ResetStack(); + continue; + } + } + + if (!bKnowType) + { + if (bBegOfLine) + { + if (ch == '%') + { + curInfo->type = LINE_EXPR; + curInfo->context = LINE_EXPR; + curInfo->ResetStack(); + goto _loopHead; + } + + if (!isIdChar(ch, false)) + { + sc.SetState(SCE_AHK2_ERROR); + curInfo->type = LINE_BLANK; + curInfo->context = LINE_BLANK; + curInfo->ResetStack(); + continue; + } + + curInfo->type = LINE_COMMAND; // for now + curInfo->context = LINE_COMMAND; + curInfo->ResetStack(); + sc.SetState(SCE_AHK2_FUNC); + backtrackPos = sc.currentPos; + cmdObjSynCount = 0; + continue; + } + + if (sc.state != SCE_AHK2_DEFAULT) + { + if (isIdChar(ch)) continue; + + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + + if (ch == ',') + { + // Definitely command + bKnowType = true; + + bool bIsFOC = false; + if (!cmdObjSynCount) + { + bIsFOC = flowOfControl.InList(wordbuf); + if (bIsFOC) + { + sc.ChangeState(SCE_AHK2_FLOW); + curInfo->bIsIf = strcmp(wordbuf, "if") == 0; + bIsElse = strcmp(wordbuf, "else") == 0; + } else if (bifList.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_BIF); + } else if (biObjMethods.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_OBJMETHOD_BI); + + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + + if (bIsFOC) + { + if (bIsElse) + { + bIsElse = false; + sc.SetState(SCE_AHK2_DEFAULT); + // Fake new line + curInfo->type = LINE_BLANK; + curInfo->context = LINE_BLANK; + bInitialWS = true; + bKnowType = false; + bPhysBegOfLine = false; + } else + { + curInfo->type = LINE_EXPR; + curInfo->context = LINE_EXPR; + } + } else + sc.SetState(SCE_AHK2_STRING); + + ch = sc.ch; + goto _loopHead; + } else if (ch == '(') + { + // Function call or definition + bKnowType = true; + curInfo->type = LINE_EXPR; + curInfo->context = LINE_EXPR; + if (!cmdObjSynCount) + { + if ((curInfo->bIsIf = strcmp(wordbuf, "if") == 0) || strcmp(wordbuf, "while") == 0) + sc.ChangeState(SCE_AHK2_FLOW); + else + sc.ChangeState(bifList.InList(wordbuf) ? SCE_AHK2_BIF : SCE_AHK2_FUNC); + } else if (biObjMethods.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_OBJMETHOD_BI); + sc.SetState(SCE_AHK2_OPERATOR); + curInfo->EnterBrace(); + continue; + } else if (ch == '.') + { + // May be command object syntax + if (cmdObjSynCount++) + sc.ChangeState(biObjProps.InList(wordbuf) ? SCE_AHK2_OBJPROP_BI : SCE_AHK2_OBJPROP); + else + sc.ChangeState(bivList.InList(wordbuf) ? SCE_AHK2_BIV : SCE_AHK2_VAR); + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + sc.SetState(SCE_AHK2_OBJMETHOD); + ch = sc.ch; + goto _loopHead; + } else if (ch == '[' || ch == '?' || (ch == '+' && sc.chNext == '+') || (ch == '-' && sc.chNext == '-')) + { + // Expression + bKnowType = true; + curInfo->type = LINE_EXPR; + curInfo->context = LINE_EXPR; + sc.ChangeState(bivList.InList(wordbuf) ? SCE_AHK2_BIV : SCE_AHK2_VAR); + sc.SetState(SCE_AHK2_OPERATOR); + if (ch == '[') curInfo->EnterBrace(); + continue; + } else if (ch == ':' && sc.chNext != ':') + { + if (sc.chNext != '=') + { + // Label + bKnowType = true; + curInfo->type = LINE_LABEL; + curInfo->context = LINE_BLANK; // hack + sc.ChangeState(SCE_AHK2_LABEL); + sc.SetState(SCE_AHK2_OPERATOR); + continue; + } + // Assignment + bKnowType = true; + curInfo->type = LINE_EXPR; + curInfo->context = LINE_EXPR; + sc.ChangeState(bivList.InList(wordbuf) ? SCE_AHK2_BIV : SCE_AHK2_VAR); + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + continue; + } else if (ch && !isWhitespace(ch)) + { + // Error + sc.SetState(SCE_AHK2_ERROR); + curInfo->type = LINE_BLANK; + curInfo->context = LINE_BLANK; + continue; + } + + // Check for 'new' wordop --> expression + if (strcmp(wordbuf, "new") == 0) + { + sc.ChangeState(SCE_AHK2_WORDOP); + sc.SetState(SCE_AHK2_DEFAULT); + bKnowType = true; + curInfo->type = LINE_EXPR; + curInfo->context = LINE_EXPR; + goto _loopHead; + } + + // For now, change the sym type if needed + if (!cmdObjSynCount && flowOfControl.InList(wordbuf)) + { + sc.ChangeState(SCE_AHK2_FLOW); + curInfo->bIsIf = strcmp(wordbuf, "if") == 0; + bIsElse = strcmp(wordbuf, "else") == 0; + } else if (!cmdObjSynCount && bifList.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_BIF); + else if (cmdObjSynCount && biObjMethods.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_OBJMETHOD_BI); + + // Search more + sc.SetState(SCE_AHK2_DEFAULT); + continue; + } + + if (isWhitespace(ch)) continue; + + if (sc.Match(':', '=') || sc.Match('+', '=') || sc.Match('-', '=') || sc.Match('*', '=') || sc.Match('/', '=') + || sc.Match("//=") || sc.Match('.', '=') || sc.Match('|', '=') || sc.Match('&', '=') || sc.Match('^', '=') + || sc.Match(">>=") || sc.Match("<<=")) + { + // Assignment -- we need to backtrack and reprocess this entire line + bKnowType = true; + curInfo->type = LINE_EXPR; + curInfo->context = LINE_EXPR; + styler.Flush(); + styler.StartAt(backtrackPos); + styler.StartSegment(backtrackPos); + sc.currentPos = backtrackPos; + sc.state = SCE_AHK2_VAR; + sc.chPrev = styler.SafeGetCharAt(backtrackPos-1); + sc.ch = styler.SafeGetCharAt(backtrackPos); + sc.chNext = styler.SafeGetCharAt(backtrackPos+1); + ch = sc.ch; + goto _loopHead; + } else if (!cmdObjSynCount && flowOfControl.InList(wordbuf)) + { + // Flow of control + if (ch == ',') + { + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + sc.SetState(SCE_AHK2_DEFAULT); + ch = sc.ch; + } + if (bIsElse) + { + bIsElse = false; + // Fake new line + curInfo->type = LINE_BLANK; + curInfo->context = LINE_BLANK; + bInitialWS = true; + bPhysBegOfLine = false; + goto _loopHead; + } + + bKnowType = true; + curInfo->type = LINE_EXPR; + curInfo->context = LINE_EXPR; + // fall down + } else + { + // Definitely command + bKnowType = true; + if (ch == ',') + { + sc.SetState(SCE_AHK2_OPERATOR); + continue; + } + // else fall down + sc.SetState(SCE_AHK2_STRING); + } + } + + _CSJump: + + // Catch-all string handler + if (sc.state == SCE_AHK2_STRING) + { + if (ch == '`' && !curInfo->bLiteralEscape) + { + sc.SetState(SCE_AHK2_ESCAPE); + sc.Forward(); + continue; + } + + if (ch == '%' && !curInfo->bLiteralPercent && !curInfo->IsSpecial()) + { + int nexprType = DT_STRING; + if (curInfo->context == LINE_COMMAND && !curInfo->InNestedExpr()) + nexprType = isWhitespace(sc.chNext) ? DT_CMDSTRING2 : DT_CMDSTRING; + if (curInfo->EnterNestedExpr(nexprType)) + { + sc.SetState(SCE_AHK2_OPERATOR); + continue; + } + } + }else + + // Catch-all escape handler + if (sc.state == SCE_AHK2_ESCAPE) + { + if (ch != '`') + { + sc.SetState(SCE_AHK2_STRING); + goto _loopHead; + } else + { + sc.Forward(); + continue; + } + } else + + // Catch-all identifier handler + if (sc.state == SCE_AHK2_VAR) + { + if (isIdChar(ch)) + continue; + + if (ch == '(') + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + sc.ChangeState(bifList.InList(wordbuf) ? SCE_AHK2_BIF : SCE_AHK2_FUNC); + } else if (ch == '%' && (!curInfo->InNestedExpr() || !curInfo->IsOutsideBrace() || curInfo->CurNestedExprCtx() == DT_CMDSTRING2) && curInfo->EnterNestedExpr(DT_VAR)) + { + sc.SetState(SCE_AHK2_OPERATOR); + continue; + } else + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + if (bivList.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_BIV); + if (wordOps.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_WORDOP); + } + + // Let it fall down + sc.SetState(SCE_AHK2_DEFAULT); + } else + + // Catch-all object identifier handler + if (sc.state == SCE_AHK2_OBJPROP) + { + if (isIdChar(ch)) + continue; + + if (ch == '(') + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + sc.ChangeState(biObjMethods.InList(wordbuf) ? SCE_AHK2_OBJMETHOD_BI : SCE_AHK2_OBJMETHOD); + } else if (ch == '%' && (!curInfo->InNestedExpr() || !curInfo->IsOutsideBrace() || curInfo->CurNestedExprCtx() == DT_CMDSTRING2)) + { + sc.SetState(SCE_AHK2_ERROR); + continue; + } else + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + if (biObjProps.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_OBJPROP_BI); + } + + // Let it fall down + sc.SetState(SCE_AHK2_DEFAULT); + } else + + // Catch-all number handler + if (sc.state == SCE_AHK2_NUMBER) + { + if (isNumeric(ch, bIsHex) || (bIsHex && sc.LengthCurrent() == 1 && ch == 'x')) + continue; + + // Let it fall down + sc.SetState(SCE_AHK2_DEFAULT); + }else + + // This is only triggered by Loop subcommands + if (sc.state == SCE_AHK2_FLOW) + { + if (ch == ' ') + sc.SetState(SCE_AHK2_STRING); + else if (ch == ',') + sc.SetState(SCE_AHK2_OPERATOR); + continue; + }else + + // End operator state + if (sc.state == SCE_AHK2_OPERATOR) + sc.SetState(SCE_AHK2_DEFAULT); + + switch (curInfo->context) + { + case LINE_COMMAND: + if (curInfo->InNestedExpr()) + goto _parseAsExpr; + + if (ch == ',' && !curInfo->bLiteralComma) + { + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + sc.SetState(SCE_AHK2_STRING); + ch = sc.ch; + goto _loopHead; + } + + if (sc.state == SCE_AHK2_DEFAULT) + sc.ChangeState(SCE_AHK2_STRING); + break; + + case LINE_EXPR: + { + _parseAsExpr: + bool bTokBegin = sc.state == SCE_AHK2_DEFAULT; + + switch (sc.state) + { + // Token begin + case SCE_AHK2_DEFAULT: + if (isWhitespace(ch)) + continue; + + if (isNumeric(ch)) + { + sc.SetState(SCE_AHK2_NUMBER); + bIsHex = sc.Match('0', 'x'); + continue; + } + + if (isIdChar(ch, false)) + { + sc.SetState(sc.chPrev != '.' ? SCE_AHK2_VAR : SCE_AHK2_OBJPROP); + continue; + } + + if (ch == ',' && curInfo->bIsIf && !curInfo->InNestedExpr() && curInfo->IsOutsideBrace()) + { + // If with same-line action + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + sc.SetState(SCE_AHK2_DEFAULT); + curInfo->Init(); + bInitialWS = true; + bBegOfLine = true; + bKnowType = false; + ch = sc.ch; + goto _loopHead; + } + + if (curInfo->CurNestedExprCtx() == DT_CMDSTRING2 && ch == ',' && !curInfo->bLiteralComma && curInfo->IsOutsideBrace()) + { + // Exit! + curInfo->LeaveNestedExpr(); + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + sc.SetState(SCE_AHK2_STRING); + ch = sc.ch; + goto _loopHead; + } + + if (curInfo->IsOutsideBrace() && ch == '%' && !curInfo->bLiteralPercent && curInfo->InNestedExpr() && curInfo->CurNestedExprCtx() != DT_CMDSTRING2) + { + int oldState = curInfo->LeaveNestedExpr(); + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + switch (oldState) + { + case DT_STRING: + case DT_CMDSTRING: + sc.SetState(SCE_AHK2_STRING); + break; + case DT_VAR: + sc.SetState(SCE_AHK2_VAR); + break; + } + ch = sc.ch; + goto _loopHead; + } + + if (ch == '%' && !curInfo->bLiteralPercent && curInfo->EnterNestedExpr(DT_VAR)) + { + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(); + sc.SetState(SCE_AHK2_DEFAULT); + ch = sc.ch; + goto _loopHead; + } + + if (isExprOp(ch) || ch == '{' || ch == '}') + { + sc.SetState(SCE_AHK2_OPERATOR); + if (isOpeningBrace(ch)) + curInfo->EnterBrace(); + else if (isClosingBrace(ch)) + { + if (curInfo->IsOutsideBrace()) + sc.SetState(SCE_AHK2_ERROR); + else + curInfo->LeaveBrace(); + } + continue; + } + + if (ch == '"' || ch == '\'') + { + sc.SetState(SCE_AHK2_STRING); + curInfo->SetSQString(ch == '\''); + continue; + } + + sc.SetState(SCE_AHK2_ERROR); + curInfo->type = LINE_BLANK; + curInfo->context = LINE_BLANK; + break; + + // String + case SCE_AHK2_STRING: + if (ch == (!curInfo->IsSQString() ? '"' : '\'') && !curInfo->bLiteralQuote) + { + sc.Forward(); + sc.SetState(SCE_AHK2_DEFAULT); + ch = sc.ch; + goto _loopHead; + } + break; + } + break; + } + case LINE_DIRECTIVE: + { + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) continue; + + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + bool bShouldBeExpr = strcmp(wordbuf, "#if") == 0; + sc.SetState(SCE_AHK2_DEFAULT); + + if (ch == ',' || isWhitespace(ch)) + { + curInfo->type = bShouldBeExpr ? LINE_EXPR : LINE_COMMAND; + curInfo->context = curInfo->type; + goto _loopHead; + } + + curInfo->type = LINE_BLANK; + curInfo->context = LINE_BLANK; + sc.SetState(SCE_AHK2_ERROR); + break; + } + case LINE_HOTKEY: + { + if (ch == ':' && sc.chNext == ':') + { + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(2); + sc.SetState(SCE_AHK2_DEFAULT); + // Fake start of line + curInfo->Init(); + bInitialWS = true; + bBegOfLine = true; + bKnowType = false; + ch = sc.ch; + goto _loopHead; + } + break; + } + case LINE_REMAP: + { + if (curInfo->specialState == 0 && ch == ':' && sc.chNext == ':') + { + sc.SetState(SCE_AHK2_OPERATOR); + sc.Forward(2); + sc.SetState(SCE_AHK2_STRING); + ch = sc.ch; + goto _loopHead; + } + break; + } + case LINE_HOTSTRING: + { + if (sc.state == SCE_AHK2_DEFAULT) + { + sc.SetState(SCE_AHK2_STRING); + continue; + } + if (ch != ':') + break; + if (curInfo->specialState > 1) + break; + if (curInfo->specialState == 1 && sc.chNext != ':') + break; + + sc.SetState(SCE_AHK2_OPERATOR); + if (curInfo->specialState == 1) + sc.Forward(); + + curInfo->specialState ++; + break; + } + } + } + + if (!bNewLine) + { + if (sc.state == SCE_AHK2_VAR) + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + chopcr(wordbuf); + if (bivList.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_BIV); + if (wordOps.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_WORDOP); + } else if (sc.state == SCE_AHK2_OBJPROP) + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + chopcr(wordbuf); + if (biObjProps.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_OBJPROP_BI); + } else if (sc.state == SCE_AHK2_FUNC) + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + chopcr(wordbuf); + if (flowOfControl.InList(wordbuf)) + { + sc.ChangeState(SCE_AHK2_FLOW); + curInfo->bIsIf = strcmp(wordbuf, "if") == 0; + } else if (bifList.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_BIF); + } else if (sc.state == SCE_AHK2_OBJMETHOD) + { + sc.GetCurrentLowered(wordbuf, _countof(wordbuf)); + chopcr(wordbuf); + if (biObjMethods.InList(wordbuf)) + sc.ChangeState(SCE_AHK2_OBJMETHOD_BI); + } else if (sc.state == SCE_AHK2_STRING || sc.state == SCE_AHK2_ESCAPE) + curInfo->bEndedInStr = true; + } + + sc.Complete(); +} + +void SCI_METHOD LexerAHK2::Fold(unsigned int startPos, int lengthDoc, int initStyle, IDocument* pAccess) +{ + if (!options.Fold) return; + + bool bFoldComment = options.FoldComment; + bool bFoldCompact = options.FoldCompact; + + LexAccessor styler(pAccess); + unsigned int endPos = startPos + lengthDoc; + bool bOnlySpaces = true; + + int lineCurrent = styler.GetLine(startPos); + int levelCurrent = SC_FOLDLEVELBASE; + if (lineCurrent > 0) + levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16; + + int levelNext = levelCurrent; + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + + for (unsigned int i = startPos; i < endPos; i ++) + { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int stylePrev = style; + style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + + if (bFoldComment && style == SCE_AHK2_COMMENTBLOCK) + { + if (stylePrev != SCE_AHK2_COMMENTBLOCK) + levelNext ++; + else if ((styleNext != SCE_AHK2_COMMENTBLOCK) && !atEOL) + // Comments don't end at end of line and the next character may be unstyled. + levelNext --; + } + + if (bFoldComment && style == SCE_AHK2_COMMENTLINE) + { + if (ch == ';') + { + if (chNext == '{') + levelNext ++; + else if (chNext == '}') + levelNext --; + } + } + + if (style == SCE_AHK2_OPERATOR) + { + if (ch == '(' || ch == '{' || ch == '[') + levelNext ++; + else if (ch == ')' || ch == '}' || ch == ']') + levelNext --; + } + if (atEOL || (i == endPos-1)) + { + int level = levelCurrent | (levelNext << 16); + if (bOnlySpaces && bFoldCompact) + // Empty line + level |= SC_FOLDLEVELWHITEFLAG; + if (levelCurrent < levelNext) + level |= SC_FOLDLEVELHEADERFLAG; + if (level != styler.LevelAt(lineCurrent)) + styler.SetLevel(lineCurrent, level); + lineCurrent ++; + levelCurrent = levelNext; + if (atEOL && (i == static_cast(styler.Length()-1))) + // There is an empty line at end of file so give it same level and empty + styler.SetLevel(lineCurrent, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG); + bOnlySpaces = true; + } + if (!isWhitespace(ch)) + bOnlySpaces = false; + } +} diff --git a/scintilla/src/Catalogue.cxx b/scintilla/src/Catalogue.cxx index ed47aa8..e7d9785 100644 --- a/scintilla/src/Catalogue.cxx +++ b/scintilla/src/Catalogue.cxx @@ -80,6 +80,8 @@ int Scintilla_LinkLexers() { LINK_LEXER(lmA68k); LINK_LEXER(lmAbaqus); LINK_LEXER(lmAda); + LINK_LEXER(lmAHK1); + LINK_LEXER(lmAHK2); LINK_LEXER(lmAPDL); LINK_LEXER(lmAs); LINK_LEXER(lmAsm); diff --git a/scintilla/win32/ScintRes.rc b/scintilla/win32/ScintRes.rc index 5f3f09c..264b6b4 100644 --- a/scintilla/win32/ScintRes.rc +++ b/scintilla/win32/ScintRes.rc @@ -25,11 +25,11 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Neil Hodgson neilh@scintilla.org\0" - VALUE "FileDescription", "Scintilla.DLL - a Source Editing Component\0" + VALUE "FileDescription", "Scintilla.dll (with AHK support) - a Source Editing Component\0" VALUE "FileVersion", VERSION_SCINTILLA "\0" VALUE "InternalName", "Scintilla\0" - VALUE "LegalCopyright", "Copyright 1998-2012 by Neil Hodgson\0" - VALUE "OriginalFilename", "Scintilla.DLL\0" + VALUE "LegalCopyright", "Copyright 1998-2013 by Neil Hodgson\0" + VALUE "OriginalFilename", "Scintilla.dll\0" VALUE "ProductName", "Scintilla\0" VALUE "ProductVersion", VERSION_SCINTILLA "\0" END diff --git a/scite/SciTE.vcxproj b/scite/SciTE.vcxproj new file mode 100644 index 0000000..90a44e1 --- /dev/null +++ b/scite/SciTE.vcxproj @@ -0,0 +1,312 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {2DC33974-65FD-4DAF-B522-ACA7A7AC9721} + Win32Proj + SciTE + + + + Application + true + Unicode + v140_xp + + + Application + true + Unicode + v140_xp + + + Application + false + true + Unicode + v140_xp + + + Application + false + true + Unicode + v140_xp + + + + + + + + + + + + + + + + + + + true + + $(SolutionDir)scintilla\include;$(SolutionDir)scintilla\win32;$(ProjectDir)src;$(ProjectDir)lua\include;$(IncludePath) + false + + + true + $(SolutionDir)debug\x64\ + $(SolutionDir)debug\x64\temp\SciTE\ + SciTE + $(SolutionDir)scintilla\include;$(SolutionDir)scintilla\win32;$(ProjectDir)src;$(ProjectDir)lua\include;$(IncludePath) + false + + + + false + $(SolutionDir)scintilla\include;$(SolutionDir)scintilla\win32;$(ProjectDir)src;$(ProjectDir)lua\include;$(IncludePath) + $(SolutionDir)bin\x86\ + $(SolutionDir)obj\x86\scite\ + SciTE + + + false + false + + + false + $(SolutionDir)scintilla\include;$(SolutionDir)scintilla\win32;$(ProjectDir)src;$(ProjectDir)lua\include;$(IncludePath) + $(SolutionDir)bin\x64\ + $(SolutionDir)obj\x64\scite\ + SciTE + + + false + $(VCInstallDir)..\Common7\IDE;$(ExecutablePath) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + comctl32.lib;Msimg32.lib;uxtheme.lib;shlwapi.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;LUA_USER_H="scite_lua_win.h";%(PreprocessorDefinitions) + + + Windows + true + comctl32.lib;imm32.lib;uxtheme.lib;shlwapi.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;LUA_USER_H="scite_lua_win.h";%(PreprocessorDefinitions) + true + true + MultiThreaded + false + NoExtensions + true + + + Windows + false + true + true + comctl32.lib;Msimg32.lib;uxtheme.lib;shlwapi.lib;%(AdditionalDependencies) + false + false + + + + + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;LUA_USER_H="scite_lua_win.h";%(PreprocessorDefinitions) + true + true + MultiThreaded + false + true + + + Windows + false + true + true + comctl32.lib;Msimg32.lib;imm32.lib;uxtheme.lib;shlwapi.lib;msimg32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/scite/SciTE.vcxproj.filters b/scite/SciTE.vcxproj.filters new file mode 100644 index 0000000..b1ab86b --- /dev/null +++ b/scite/SciTE.vcxproj.filters @@ -0,0 +1,359 @@ + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + \ No newline at end of file diff --git a/scite/src/Credits.cxx b/scite/src/Credits.cxx index 353c812..de425a5 100644 --- a/scite/src/Credits.cxx +++ b/scite/src/Credits.cxx @@ -436,9 +436,12 @@ void SciTEBase::SetAboutMessage(GUI::ScintillaWindow &wsci, const char *appTitle sVersion += "."; sVersion += StdStringFromInteger(GTK_MICRO_VERSION); #else - int fontSize = 15; +#if defined(WIN32) + wsci.Send(SCI_STYLESETFONT, STYLE_DEFAULT, reinterpret_cast("Segoe UI")); #endif - sVersion += "\n"; + int fontSize = 14; +#endif + //sVersion += "\n"; wsci.Send(SCI_SETCODEPAGE, SC_CP_UTF8, 0); @@ -450,21 +453,30 @@ void SciTEBase::SetAboutMessage(GUI::ScintillaWindow &wsci, const char *appTitle wsci.Send(SCI_STYLESETSIZE, 0, fontSize); wsci.Send(SCI_STYLESETBACK, 0, ColourRGB(0, 0, 0x80)); AddStyledText(wsci, appTitle, 0); - AddStyledText(wsci, "\n", 0); +#ifndef _WIN64 + AddStyledText(wsci, "\n", 1); +#else + AddStyledText(wsci, " (64-bit)\n", 1); +#endif SetAboutStyle(wsci, 1, ColourRGB(0, 0, 0)); int trsSty = 5; // define the stylenumber to assign font for translators. std::string translator = GetTranslationToAbout("TranslationCredit", false); SetAboutStyle(wsci, trsSty, ColourRGB(0, 0, 0)); AddStyledText(wsci, GetTranslationToAbout("Version").c_str(), trsSty); AddStyledText(wsci, sVersion.c_str(), 1); - AddStyledText(wsci, " " __DATE__ " " __TIME__ "\n", 1); + AddStyledText(wsci, "- Based on SciTE " VERSION_ORGSCITE "\n", 1); + AddStyledText(wsci, " Built on " __DATE__ " " __TIME__ "\n", 1); SetAboutStyle(wsci, 2, ColourRGB(0, 0, 0)); wsci.Send(SCI_STYLESETITALIC, 2, 1); AddStyledText(wsci, GetTranslationToAbout("by").c_str(), trsSty); - AddStyledText(wsci, " Neil Hodgson.\n", 2); + AddStyledText(wsci, " fincs", 2); + AddStyledText(wsci, " - Original SciTE by ", trsSty); + AddStyledText(wsci, "Neil Hodgson\n", 2); SetAboutStyle(wsci, 3, ColourRGB(0, 0, 0)); - AddStyledText(wsci, COPYRIGHT_DATES ".\n", 3); + AddStyledText(wsci, "SciTE: " COPYRIGHT_DATES " Neil Hodgson.\nSciTE4AutoHotkey: " COPYRIGHT_YEARS_S4AHK " fincs.\n", 3); SetAboutStyle(wsci, 4, ColourRGB(0, 0x7f, 0x7f)); + AddStyledText(wsci, "http://fincs.ahk4.net/scite4ahk\n", 4); + AddStyledText(wsci, "http://www.ahkscript.org\n", 4); AddStyledText(wsci, "http://www.scintilla.org\n", 4); AddStyledText(wsci, "Lua scripting language by TeCGraf, PUC-Rio\n", 3); AddStyledText(wsci, " http://www.lua.org\n", 4); diff --git a/scite/src/Extender.h b/scite/src/Extender.h index 8b04be8..43866d3 100644 --- a/scite/src/Extender.h +++ b/scite/src/Extender.h @@ -66,7 +66,7 @@ public: } virtual bool OnDoubleClick() { return false; } virtual bool OnUpdateUI() { return false; } - virtual bool OnMarginClick() { return false; } + virtual bool OnMarginClick(int, int) { return false; } // fincs-edit virtual bool OnMacro(const char *, const char *) { return false; } virtual bool OnUserListSelection(int, const char *) { return false; } diff --git a/scite/src/IFaceTable.cxx b/scite/src/IFaceTable.cxx index 186c49f..4af9535 100644 --- a/scite/src/IFaceTable.cxx +++ b/scite/src/IFaceTable.cxx @@ -411,6 +411,47 @@ static IFaceConstant ifaceConstants[] = { {"SCE_ADA_STRING",7}, {"SCE_ADA_STRINGEOL",8}, {"SCE_ADA_WORD",1}, + {"SCE_AHK_COMMENTBLOCK",2}, + {"SCE_AHK_COMMENTLINE",1}, + {"SCE_AHK_DEFAULT",0}, + {"SCE_AHK_ERROR",20}, + {"SCE_AHK_ESCAPE",3}, + {"SCE_AHK_EXPOPERATOR",5}, + {"SCE_AHK_IDENTIFIER",8}, + {"SCE_AHK_LABEL",10}, + {"SCE_AHK_NUMBER",7}, + {"SCE_AHK_STRING",6}, + {"SCE_AHK_SYNOPERATOR",4}, + {"SCE_AHK_VARREF",9}, + {"SCE_AHK_VARREFKW",19}, + {"SCE_AHK_WORD_CF",11}, + {"SCE_AHK_WORD_CMD",12}, + {"SCE_AHK_WORD_DIR",14}, + {"SCE_AHK_WORD_FN",13}, + {"SCE_AHK_WORD_KB",15}, + {"SCE_AHK_WORD_SP",17}, + {"SCE_AHK_WORD_UD",18}, + {"SCE_AHK_WORD_VAR",16}, + {"SCE_AHK2_BIF",14}, + {"SCE_AHK2_BIV",13}, + {"SCE_AHK2_COMMENTBLOCK",2}, + {"SCE_AHK2_COMMENTLINE",1}, + {"SCE_AHK2_DEFAULT",0}, + {"SCE_AHK2_DIRECTIVE",10}, + {"SCE_AHK2_ERROR",15}, + {"SCE_AHK2_ESCAPE",3}, + {"SCE_AHK2_FLOW",12}, + {"SCE_AHK2_FUNC",9}, + {"SCE_AHK2_LABEL",11}, + {"SCE_AHK2_NUMBER",6}, + {"SCE_AHK2_OBJMETHOD",17}, + {"SCE_AHK2_OBJMETHOD_BI",19}, + {"SCE_AHK2_OBJPROP",16}, + {"SCE_AHK2_OBJPROP_BI",18}, + {"SCE_AHK2_OPERATOR",4}, + {"SCE_AHK2_STRING",5}, + {"SCE_AHK2_VAR",8}, + {"SCE_AHK2_WORDOP",7}, {"SCE_APDL_ARGUMENT",11}, {"SCE_APDL_COMMAND",8}, {"SCE_APDL_COMMENT",1}, @@ -2387,6 +2428,8 @@ static IFaceConstant ifaceConstants[] = { {"SCLEX_A68K",100}, {"SCLEX_ABAQUS",84}, {"SCLEX_ADA",20}, + {"SCLEX_AHK1",200}, + {"SCLEX_AHK2",201}, {"SCLEX_APDL",61}, {"SCLEX_AS",113}, {"SCLEX_ASM",34}, @@ -3260,7 +3303,7 @@ static IFaceProperty ifaceProperties[] = { enum { ifaceFunctionCount = 293, - ifaceConstantCount = 2601, + ifaceConstantCount = sizeof(ifaceConstants) / sizeof(IFaceConstant), ifacePropertyCount = 221 }; diff --git a/scite/src/LuaExtension.cxx b/scite/src/LuaExtension.cxx index 1c9a280..bda057b 100644 --- a/scite/src/LuaExtension.cxx +++ b/scite/src/LuaExtension.cxx @@ -1329,6 +1329,11 @@ static bool InitGlobalScope(bool checkProperties, bool forceReload = false) { // ...register standard libraries luaL_openlibs(luaState); +#ifdef WIN32 + extern int MessagePumpLibInit(lua_State* L); + MessagePumpLibInit(luaState); +#endif + lua_register(luaState, "_ALERT", cf_global_print); // although this is mostly redundant with output:append @@ -2034,8 +2039,8 @@ bool LuaExtension::OnUpdateUI() { return CallNamedFunction("OnUpdateUI"); } -bool LuaExtension::OnMarginClick() { - return CallNamedFunction("OnMarginClick"); +bool LuaExtension::OnMarginClick(int position, int margin) { // fincs-edit + return CallNamedFunction("OnMarginClick", position, margin); // fincs-edit } bool LuaExtension::OnUserListSelection(int listType, const char *selection) { diff --git a/scite/src/LuaExtension.h b/scite/src/LuaExtension.h index b4acf12..c303f72 100644 --- a/scite/src/LuaExtension.h +++ b/scite/src/LuaExtension.h @@ -34,7 +34,7 @@ public: virtual bool OnStyle(unsigned int startPos, int lengthDoc, int initStyle, StyleWriter *styler); virtual bool OnDoubleClick(); virtual bool OnUpdateUI(); - virtual bool OnMarginClick(); + virtual bool OnMarginClick(int position, int margin); // fincs-edit virtual bool OnUserListSelection(int listType, const char *selection); virtual bool OnKey(int keyval, int modifiers); virtual bool OnDwellStart(int pos, const char *word); diff --git a/scite/src/MultiplexExtension.cxx b/scite/src/MultiplexExtension.cxx index fc44aa3..65f40e2 100644 --- a/scite/src/MultiplexExtension.cxx +++ b/scite/src/MultiplexExtension.cxx @@ -197,10 +197,10 @@ bool MultiplexExtension::OnUpdateUI() { return handled; } -bool MultiplexExtension::OnMarginClick() { +bool MultiplexExtension::OnMarginClick(int p, int q) { // fincs-edit bool handled = false; for (int i = 0; i < extensionCount && !handled; ++i) - if (extensions[i]->OnMarginClick()) + if (extensions[i]->OnMarginClick(p, q)) // fincs-edit handled = true; return handled; } diff --git a/scite/src/MultiplexExtension.h b/scite/src/MultiplexExtension.h index 661e5af..03e948f 100644 --- a/scite/src/MultiplexExtension.h +++ b/scite/src/MultiplexExtension.h @@ -64,7 +64,7 @@ public: virtual bool OnStyle(unsigned int, int, int, StyleWriter *); virtual bool OnDoubleClick(); virtual bool OnUpdateUI(); - virtual bool OnMarginClick(); + virtual bool OnMarginClick(int, int); // fincs-edit virtual bool OnMacro(const char *, const char *); virtual bool OnUserListSelection(int, const char *); diff --git a/scite/src/SciTE.h b/scite/src/SciTE.h index 5b969be..ed0c507 100644 --- a/scite/src/SciTE.h +++ b/scite/src/SciTE.h @@ -9,10 +9,12 @@ #define SCITE_H // Version numbers and dates -#define VERSION_SCITE "3.6.0" -#define VERSION_WORDS 3, 6, 0, 0 +#define VERSION_SCITE "3.0.07" +#define VERSION_ORGSCITE "3.6.0" +#define VERSION_WORDS 3, 0, 7, 0 #define COPYRIGHT_DATES "December 1998-August 2015" #define COPYRIGHT_YEARS "1998-2015" +#define COPYRIGHT_YEARS_S4AHK "2007-2015" // Menu IDs. // These are located 100 apart. No one will want more than 100 in each menu ;) @@ -272,6 +274,8 @@ #define IDBM_BACKSLASH 104 #define IDBM_AROUND 105 #define IDBM_UP 106 +#define IDR_CLOSEFILE_BIG 107 +#define IDI_MAINICON 500 #define IDBM_20_WORD 201 #define IDBM_20_CASE 202 diff --git a/scite/src/SciTEBase.cxx b/scite/src/SciTEBase.cxx index 52af55b..7a94169 100644 --- a/scite/src/SciTEBase.cxx +++ b/scite/src/SciTEBase.cxx @@ -14,6 +14,9 @@ #include #include #include +#ifdef WIN32 +#include // for MulDiv() +#endif #include #include @@ -147,6 +150,7 @@ SciTEBase::SciTEBase(Extension *ext) : apis(true), extender(ext) { bracesCheck = true; bracesSloppy = false; bracesStyle = 0; + bracesStyle2 = 0; braceCount = 0; indentationWSVisible = true; @@ -155,6 +159,7 @@ SciTEBase::SciTEBase(Extension *ext) : apis(true), extender(ext) { imeAutoComplete = false; callTipUseEscapes = false; callTipIgnoreCase = false; + callTipSkipBareWords = false; autoCCausedByOnlyOne = false; startCalltipWord = 0; currentCallTip = 0; @@ -184,6 +189,7 @@ SciTEBase::SciTEBase(Extension *ext) : apis(true), extender(ext) { needReadProperties = false; quitting = false; + debugging = false; timerMask = 0; delayBeforeAutoSave = 0; @@ -477,6 +483,7 @@ bool SciTEBase::FindMatchingBracePosition(bool editor, int &braceAtCaret, int &b return false; int bracesStyleCheck = editor ? bracesStyle : 0; + int bracesStyleCheck2 = editor ? bracesStyle2 : 0; int caretPos = win.Call(SCI_GETCURRENTPOS, 0, 0); braceAtCaret = -1; braceOpposite = -1; @@ -493,7 +500,7 @@ bool SciTEBase::FindMatchingBracePosition(bool editor, int &braceAtCaret, int &b } // Priority goes to character before caret if (charBefore && IsBrace(charBefore) && - ((styleBefore == bracesStyleCheck) || (!bracesStyle))) { + (((styleBefore == bracesStyleCheck) || (!bracesStyle)) || ((styleBefore == bracesStyleCheck2) || (!bracesStyle2)))) { braceAtCaret = caretPos - 1; } bool colonMode = false; @@ -509,7 +516,7 @@ bool SciTEBase::FindMatchingBracePosition(bool editor, int &braceAtCaret, int &b if (win.Send(SCI_POSITIONAFTER, caretPos) == (caretPos + 1)) { char charAfter = acc[caretPos]; int styleAfter = acc.StyleAt(caretPos); - if (charAfter && IsBrace(charAfter) && ((styleAfter == bracesStyleCheck) || (!bracesStyle))) { + if (charAfter && IsBrace(charAfter) && (((styleAfter == bracesStyleCheck) || (!bracesStyle)) || ((styleAfter == bracesStyleCheck2) || (!bracesStyle2)))) { braceAtCaret = caretPos; isAfter = false; } @@ -611,6 +618,9 @@ void SciTEBase::SetWindowName() { windowName += GUI_TEXT("]"); } + if (debugging) + windowName += GUI_TEXT(" [Debugging]"); + wSciTE.SetTitle(windowName.c_str()); } @@ -964,6 +974,11 @@ void SciTEBase::MarkAll(MarkPurpose purpose) { SetIdler(true); } +void SciTEBase::ClearAllBookmarks() { + wEditor.Call(SCI_MARKERDELETEALL, markerBookmark); + RemoveFindMarks(); +} + int SciTEBase::IncrementSearchMode() { FindIncrement(); return 0; @@ -1380,10 +1395,18 @@ void SciTEBase::ToggleOutputVisible() { WindowSetFocus(wEditor); } else { if (previousHeightOutput < 20) { +#ifndef WIN32 if (splitVertical) heightOutput = NormaliseSplit(300); else heightOutput = NormaliseSplit(100); +#else + extern int g_ScreenDPI; + if (splitVertical) + heightOutput = NormaliseSplit(MulDiv(300, g_ScreenDPI, 96)); + else + heightOutput = NormaliseSplit(MulDiv(100, g_ScreenDPI, 96)); +#endif previousHeightOutput = heightOutput; } else { heightOutput = NormaliseSplit(previousHeightOutput); @@ -1461,13 +1484,10 @@ void SciTEBase::Redraw() { } std::string SciTEBase::GetNearestWords(const char *wordStart, size_t searchLen, - const char *separators, bool ignoreCase /*=false*/, bool exactLen /*=false*/) { - std::string words; - while (words.empty() && *separators) { - words = apis.GetNearestWords(wordStart, searchLen, ignoreCase, *separators, exactLen); - separators++; - } - return words; + const char *separators, bool ignoreCase /*=false*/, bool exactLen /*=false*/, + bool separatorIsRequired /*=false*/) { + return apis.GetNearestWords(wordStart, searchLen, ignoreCase, separators, exactLen, + separatorIsRequired); } void SciTEBase::FillFunctionDefinition(int pos /*= -1*/) { @@ -1476,7 +1496,7 @@ void SciTEBase::FillFunctionDefinition(int pos /*= -1*/) { } if (apis) { std::string words = GetNearestWords(currentCallTipWord.c_str(), currentCallTipWord.length(), - calltipParametersStart.c_str(), callTipIgnoreCase, true); + calltipParametersStart.c_str(), callTipIgnoreCase, true, callTipSkipBareWords); if (words.empty()) return; // Counts how many call tips @@ -1484,7 +1504,7 @@ void SciTEBase::FillFunctionDefinition(int pos /*= -1*/) { // Should get current api definition std::string word = apis.GetNearestWord(currentCallTipWord.c_str(), currentCallTipWord.length(), - callTipIgnoreCase, calltipWordCharacters, currentCallTip); + callTipIgnoreCase, calltipWordCharacters, currentCallTip, callTipSkipBareWords); if (word.length()) { functionDefinition = word; if (maxCallTips > 1) { @@ -1554,6 +1574,8 @@ bool SciTEBase::StartCallTip() { while (startCalltipWord > 0 && Contains(calltipWordCharacters, line[startCalltipWord - 1])) { startCalltipWord--; + if (line[startCalltipWord] == '.' && (lexLanguage == SCLEX_AHK1 || lexLanguage == SCLEX_AHK2)) + break; } line.at(current) = '\0'; @@ -1588,7 +1610,7 @@ void SciTEBase::ContinueCallTip() { commas--; // If it reached the end of the argument list it means that the user typed in more // arguments than the ones listed in the calltip - if (Contains(calltipParametersEnd, functionDefinition[startHighlight])) + if (functionDefinition[startHighlight] && Contains(calltipParametersEnd, functionDefinition[startHighlight])) commas = 0; else startHighlight++; @@ -1654,12 +1676,15 @@ bool SciTEBase::StartAutoComplete() { (Contains(calltipWordCharacters, line[startword - 1]) || Contains(autoCompleteStartCharacters, line[startword - 1]))) { startword--; + if (line[startword] == '.' && (lexLanguage == SCLEX_AHK1 || lexLanguage == SCLEX_AHK2)) + break; } std::string root = line.substr(startword, current - startword); if (apis) { std::string words = GetNearestWords(root.c_str(), root.length(), - calltipParametersStart.c_str(), autoCompleteIgnoreCase); + calltipParametersStart.c_str(), autoCompleteIgnoreCase, + false, false); if (words.length()) { EliminateDuplicateWords(words); wEditor.Call(SCI_AUTOCSETSEPARATOR, ' '); @@ -3970,7 +3995,7 @@ void SciTEBase::Notify(const SCNotification *notification) { case SCN_MARGINCLICK: { if (extender) - handled = extender->OnMarginClick(); + handled = extender->OnMarginClick(notification->position, notification->margin); // fincs-edit if (!handled) { if (notification->margin == 2) { MarginClick(notification->position, notification->modifiers); diff --git a/scite/src/SciTEBase.h b/scite/src/SciTEBase.h index 38c385a..5d88dd8 100644 --- a/scite/src/SciTEBase.h +++ b/scite/src/SciTEBase.h @@ -311,6 +311,7 @@ public: virtual void HideMatch() = 0; enum MarkPurpose { markWithBookMarks, markIncremental }; virtual void MarkAll(MarkPurpose purpose=markWithBookMarks) = 0; + virtual void ClearAllBookmarks() = 0; virtual int ReplaceAll(bool inSelection) = 0; virtual void ReplaceOnce(bool showWarnings=true) = 0; virtual void UIClosed() = 0; @@ -432,6 +433,7 @@ protected: Extension *extender; bool needReadProperties; bool quitting; + bool debugging; int timerMask; enum { timerAutoSave=1 }; @@ -447,7 +449,7 @@ protected: bool bufferedDraw; bool bracesCheck; bool bracesSloppy; - int bracesStyle; + int bracesStyle, bracesStyle2; int braceCount; bool indentationWSVisible; @@ -462,6 +464,7 @@ protected: std::string calltipParametersEnd; std::string calltipParametersSeparators; std::string calltipEndDefinition; + bool callTipSkipBareWords; std::string autoCompleteStartCharacters; std::string autoCompleteFillUpCharacters; std::string wordCharacters; @@ -742,7 +745,8 @@ protected: void GoMessage(int dir); virtual bool StartCallTip(); std::string GetNearestWords(const char *wordStart, size_t searchLen, - const char *separators, bool ignoreCase=false, bool exactLen=false); + const char *separators, bool ignoreCase=false, bool exactLen=false, + bool separatorIsRequired=false); virtual void FillFunctionDefinition(int pos = -1); void ContinueCallTip(); virtual void EliminateDuplicateWords(std::string &words); @@ -805,6 +809,7 @@ protected: void RemoveFindMarks(); void MarkAll(MarkPurpose purpose=markWithBookMarks); + void ClearAllBookmarks(); void BookmarkAdd(int lineno = -1); void BookmarkDelete(int lineno = -1); bool BookmarkPresent(int lineno = -1); diff --git a/scite/src/SciTEBuffers.cxx b/scite/src/SciTEBuffers.cxx index f0dd65b..019c97d 100644 --- a/scite/src/SciTEBuffers.cxx +++ b/scite/src/SciTEBuffers.cxx @@ -801,6 +801,7 @@ void SciTEBase::SetIndentSettings() { int useTabs = props.GetInt("use.tabs", 1); int tabSize = props.GetInt("tabsize"); int indentSize = props.GetInt("indent.size"); + // Either set the settings related to the extension or the default ones std::string fileNameForExtension = ExtensionFileName(); std::string useTabsChars = props.GetNewExpandString("use.tabs.", diff --git a/scite/src/SciTEIO.cxx b/scite/src/SciTEIO.cxx index d1dfe8c..517d8b5 100644 --- a/scite/src/SciTEIO.cxx +++ b/scite/src/SciTEIO.cxx @@ -588,6 +588,19 @@ bool SciTEBase::Open(FilePath file, OpenFlags of) { wEditor.Call(SCI_EMPTYUNDOBUFFER); } CurrentBuffer()->isReadOnly = props.GetInt("read.only"); + extern bool _IsProtectedFile(FilePath); + if (_IsProtectedFile(filePath)) + { + GUI::gui_string msg = LocaliseMessage( + "File '^0' is an internal SciTE4AutoHotkey program file.\n\n" + "It is strongly advised not to edit this file. Future SciTE4AutoHotkey updates will overwrite your changes.\n" + "In order to change a setting configured here, copy it to your personal settings file " + "(Tools > Open User properties) and change it.\n" + "If you rather want to delete a setting, add a line to the User properties that sets it to blank." + , filePath.AsInternal()); + WindowMessageBox(wSciTE, msg); + CurrentBuffer()->isReadOnly = true; + } wEditor.Call(SCI_SETREADONLY, CurrentBuffer()->isReadOnly); } RemoveFileFromStack(filePath); diff --git a/scite/src/SciTEProps.cxx b/scite/src/SciTEProps.cxx index 3457205..898a25e 100644 --- a/scite/src/SciTEProps.cxx +++ b/scite/src/SciTEProps.cxx @@ -13,6 +13,9 @@ #include #include #include +#ifdef WIN32 +#include // for MulDiv() +#endif #include #include @@ -887,6 +890,8 @@ void SciTEBase::ReadProperties() { char bracesStyleKey[200]; sprintf(bracesStyleKey, "braces.%s.style", language.c_str()); bracesStyle = props.GetInt(bracesStyleKey, 0); + strcat(bracesStyleKey, "2"); + bracesStyle2 = props.GetInt(bracesStyleKey, 0); char key[200]; std::string sval; @@ -904,6 +909,9 @@ void SciTEBase::ReadProperties() { calltipEndDefinition = FindLanguageProperty("calltip.*.end.definition"); + sval = FindLanguageProperty("calltip.*.skip.bare.words"); + callTipSkipBareWords = sval == "1"; + sprintf(key, "autocomplete.%s.start.characters", language.c_str()); autoCompleteStartCharacters = props.GetExpandedString(key); if (autoCompleteStartCharacters == "") @@ -1395,6 +1403,10 @@ void SciTEBase::SetPropertiesInitial() { lineNumbers = props.GetInt("line.margin.visible"); margin = props.GetInt("margin.width"); foldMargin = props.GetInt("fold.margin.width", foldMarginWidthDefault); +#ifdef WIN32 + extern int g_ScreenDPI; + foldMarginWidth = MulDiv(foldMarginWidth, g_ScreenDPI, 96); +#endif matchCase = props.GetInt("find.replace.matchcase"); regExp = props.GetInt("find.replace.regexp"); @@ -1479,7 +1491,12 @@ void SciTEBase::ReadPropertiesInitial() { (splitVertical && (sizeHorizontal > 0) && (heightOutput < sizeHorizontal))) { previousHeightOutput = splitVertical ? sizeHorizontal : sizeVertical; if (!hideOutput) { +#ifndef WIN32 heightOutput = NormaliseSplit(previousHeightOutput); +#else + extern int g_ScreenDPI; + heightOutput = NormaliseSplit(MulDiv(previousHeightOutput, g_ScreenDPI, 96)); +#endif SizeSubWindows(); Redraw(); } diff --git a/scite/src/StringList.cxx b/scite/src/StringList.cxx index 20c6327..11bac6e 100644 --- a/scite/src/StringList.cxx +++ b/scite/src/StringList.cxx @@ -156,13 +156,19 @@ struct CompareStringInsensitive { }; template - std::string GetMatch(std::vector::iterator start, std::vector::iterator end, const char *wordStart, const std::string &wordCharacters, int wordIndex, Compare comp) { + std::string GetMatch(std::vector::iterator start, std::vector::iterator end, const char *wordStart, const std::string &wordCharacters, int wordIndex, Compare comp, bool skipBareWords = false) { std::vector::iterator elem = std::lower_bound(start, end, wordStart, comp); if (!comp(wordStart, *elem) && !comp(*elem, wordStart)) { // Found a matching element, now move forward wordIndex matching elements for (; elem < end; ++elem) { const char *word = *elem; if (!word[comp.searchLen] || !Contains(wordCharacters, word[comp.searchLen])) { + if (skipBareWords) { + unsigned int i; + for (i = comp.searchLen; (i < strlen(word)) && IsASpace(word[i]); i++); + if (!word[i]) + continue; + } if (wordIndex <= 0) { return std::string(word); } @@ -181,32 +187,40 @@ template * The length of the word to compare is passed too. * Letter case can be ignored or preserved. */ -std::string StringList::GetNearestWord(const char *wordStart, size_t searchLen, bool ignoreCase, const std::string &wordCharacters, int wordIndex) { +std::string StringList::GetNearestWord(const char *wordStart, size_t searchLen, bool ignoreCase, const std::string &wordCharacters, int wordIndex, bool skipBareWords /*= false*/) { if (words.empty()) return std::string(); SortIfNeeded(ignoreCase); if (ignoreCase) { - return GetMatch(wordsNoCase.begin(), wordsNoCase.end(), wordStart, wordCharacters, wordIndex, CompareStringInsensitive(searchLen)); + return GetMatch(wordsNoCase.begin(), wordsNoCase.end(), wordStart, wordCharacters, wordIndex, CompareStringInsensitive(searchLen), skipBareWords); } else { // preserve the letter case - return GetMatch(words.begin(), words.end(), wordStart, wordCharacters, wordIndex, CompareString(searchLen)); + return GetMatch(words.begin(), words.end(), wordStart, wordCharacters, wordIndex, CompareString(searchLen), skipBareWords); } } +static const char* strchrany(const char* str, const char* delim) +{ + for (; *str; str++) + if (strchr(delim, *str)) + return str; + return NULL; +} + /** * Find the length of a 'word' which is actually an identifier in a string * which looks like "identifier(..." or "identifier" and where * there may be extra spaces after the identifier that should not be * counted in the length. */ -static size_t LengthWord(const char *word, char otherSeparator) { +static size_t LengthWord(const char *word, const char* otherSeparators, bool separatorIsRequired=false) { const char *endWord = 0; // Find an otherSeparator - if (otherSeparator) - endWord = strchr(word, otherSeparator); + if (otherSeparators) + endWord = strchrany(word, otherSeparators); // Find a '('. If that fails go to the end of the string. if (!endWord) endWord = strchr(word, '('); - if (!endWord) + if (!endWord && !separatorIsRequired) endWord = word + strlen(word); // Last case always succeeds so endWord != 0 @@ -222,16 +236,16 @@ static size_t LengthWord(const char *word, char otherSeparator) { } template -static std::string GetMatches(std::vector::iterator start, std::vector::iterator end, const char *wordStart, char otherSeparator, bool exactLen, Compare comp) { +static std::string GetMatches(std::vector::iterator start, std::vector::iterator end, const char *wordStart, const char* otherSeparators, bool exactLen, Compare comp, bool separatorIsRequired = false) { std::string wordList; - const size_t wordStartLength = LengthWord(wordStart, otherSeparator); + const size_t wordStartLength = LengthWord(wordStart, otherSeparators); std::vector::iterator elem = std::lower_bound(start, end, wordStart, comp); // Found a matching element, now accumulate all matches for (; elem < end; ++elem) { if (comp(wordStart, *elem) || comp(*elem, wordStart)) break; // Not a match so stop // length of the word part (before the '(' brace) of the api array element - const size_t wordlen = LengthWord(*elem, otherSeparator); + const size_t wordlen = LengthWord(*elem, otherSeparators, separatorIsRequired); if (!exactLen || (wordlen == wordStartLength)) { if (wordList.length() > 0) wordList.append(" ", 1); @@ -253,16 +267,17 @@ std::string StringList::GetNearestWords( const char *wordStart, size_t searchLen, bool ignoreCase, - char otherSeparator /*= '\0'*/, - bool exactLen /*=false*/) { + const char* otherSeparators /*= '\0'*/, + bool exactLen /*=false*/, + bool separatorIsRequired /*=false*/) { if (words.empty()) return std::string(); SortIfNeeded(ignoreCase); if (ignoreCase) { - return GetMatches(wordsNoCase.begin(), wordsNoCase.end(), wordStart, otherSeparator, exactLen, CompareStringInsensitive(searchLen)); + return GetMatches(wordsNoCase.begin(), wordsNoCase.end(), wordStart, otherSeparators, exactLen, CompareStringInsensitive(searchLen), separatorIsRequired); } else { // Preserve the letter case - return GetMatches(words.begin(), words.end(), wordStart, otherSeparator, exactLen, CompareString(searchLen)); + return GetMatches(words.begin(), words.end(), wordStart, otherSeparators, exactLen, CompareString(searchLen), separatorIsRequired); } } diff --git a/scite/src/StringList.h b/scite/src/StringList.h index 447f459..a6f5d70 100644 --- a/scite/src/StringList.h +++ b/scite/src/StringList.h @@ -28,7 +28,9 @@ public: void Set(const char *s); void Set(const std::vector &data); std::string GetNearestWord(const char *wordStart, size_t searchLen, - bool ignoreCase, const std::string &wordCharacters, int wordIndex); + bool ignoreCase, const std::string &wordCharacters, int wordIndex = -1, + bool skipBareWords = false); std::string GetNearestWords(const char *wordStart, size_t searchLen, - bool ignoreCase, char otherSeparator='\0', bool exactLen=false); + bool ignoreCase, const char* otherSeparators=NULL, bool exactLen=false, + bool separatorIsRequired=false); }; diff --git a/scite/win32/MessagePump.cxx b/scite/win32/MessagePump.cxx new file mode 100644 index 0000000..a415252 --- /dev/null +++ b/scite/win32/MessagePump.cxx @@ -0,0 +1,166 @@ +// SciTE4AutoHotkey v3 Lua message pumper + +// Includes +#include +#include +extern "C" +{ +#include "lauxlib.h" +} + +// Some defines +#define RET_OK 1 +#define RET_FAIL 0 +#define MAX_TITLE 255 + +// Global variables +static HWND cWindow = 0, tWindow = 0; +static const char* cWinTitle; // variable pointer to constant char + +// Private callback function to enumerate the windows. +static BOOL CALLBACK _lib_winsearchproc(HWND hWnd, LPARAM lParam) +{ + char wTitle[MAX_TITLE+1]; + // Get window title + GetWindowTextA(hWnd, wTitle, MAX_TITLE); + if (strncmp(wTitle, cWinTitle, lParam) == 0) + { + // Window found. + cWindow = hWnd; + return 0; // Cancel the enumeration + } + return 1; // Continue enumerating the windows +} + +// localizewin(wintitle) -- Localizes the window with the specified window title to +// further send messages to it. True = sucess, false = failure. +static int lib_localizewin(lua_State* L) +{ + // set the global variables + cWinTitle = luaL_checkstring(L, 1); + tWindow = cWindow, cWindow = 0; + + // look for the window + EnumWindows((WNDENUMPROC)_lib_winsearchproc, strlen(cWinTitle)); + if (!cWindow) // no window found? + { + // just restore the old window and return false. + cWindow = tWindow; + lua_pushboolean(L, RET_FAIL); + return 1; + } + // return true. + lua_pushboolean(L, RET_OK); + return 1; +} + +// pumpmsg(msg, wParam, lParam) -- Sends a message to the current window. +static int lib_pumpmsg(lua_State* L) +{ + int result; + + // get the parameters + int iMsg = luaL_checkint(L, 1); + int wParam = luaL_checkint(L, 2); + int lParam = luaL_checkint(L, 3); + + if (!IsWindow(cWindow)) // invalid window? + return luaL_error(L, "Invalid window handle."); + + // just dispatch the message to the window + result = (int) SendMessageA(cWindow, (UINT)iMsg, (WPARAM)wParam, (LPARAM)lParam); + + // return the number that the window gave to us + lua_pushinteger(L, result); + return 1; +} + +// postmsg(msg, wParam, lParam) -- Posts a message to the current window. +static int lib_postmsg(lua_State* L) +{ + int result; + + // get the parameters + int iMsg = luaL_checkint(L, 1); + int wParam = luaL_checkint(L, 2); + int lParam = luaL_checkint(L, 3); + + if (!IsWindow(cWindow)) // invalid window? + return luaL_error(L, "Invalid window handle."); + + // just dispatch the message to the window + result = PostMessageA(cWindow, (UINT)iMsg, (WPARAM)wParam, (LPARAM)lParam); + + // return the return code + lua_pushboolean(L, result); + return 1; +} + +// pumpmsg(msg, wparam, lparam) -- Sends a message in which lParam will be received as a string. +static int lib_pumpmsgstr(lua_State* L) +{ + DWORD pID; + HANDLE hProcess; + void* rlParam; + int result; + + // get the parameters + int iMsg = luaL_checkint(L, 1); + int wParam = luaL_checkint(L, 2); + const char* lParam = luaL_checkstring(L, 3); + // get the string length + size_t lParamSize = strlen(lParam) + 1; + + if (!IsWindow(cWindow)) // invalid window? + return luaL_error(L, "Invalid window handle."); + + // inject the string at the process. + GetWindowThreadProcessId(cWindow, &pID); + hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, 0, pID); + if (!hProcess) + return luaL_error(L, "Couldn't open the memory of the window!"); + rlParam = VirtualAllocEx(hProcess, 0, lParamSize, MEM_COMMIT, PAGE_READWRITE); + if (!rlParam) + return luaL_error(L, "Couldn't allocate the memory at the window!"); + if (!WriteProcessMemory(hProcess, rlParam, lParam, lParamSize, NULL)) + return luaL_error(L, "Couldn't inject the string parameter at the window!"); + + // just dispatch the message to the window + result = (int) SendMessageA(cWindow, (UINT)iMsg, (WPARAM)wParam, (LPARAM)rlParam); + + // free the memory used by the string + if (!VirtualFreeEx(hProcess, rlParam, 0, MEM_RELEASE)) + return luaL_error(L, "Failed to free the memory at the window!"); + if (!CloseHandle(hProcess)) + return luaL_error(L, "Couldn't close the process handle!"); + + // return the number that the window gave to us + lua_pushinteger(L, result); + return 1; +} + +// sleep(time) -- sleeps for the specified amount of time +static int lib_sleep(lua_State* L) +{ + Sleep(luaL_checkint(L, 1)); + return 0; +} + +static const luaL_Reg pumpLib[] = +{ + { "localizewin", lib_localizewin }, + { "pumpmsg", lib_pumpmsg }, + { "pumpmsgstr", lib_pumpmsgstr }, + { "postmsg", lib_postmsg }, + { "sleep", lib_sleep }, + { NULL, NULL } +}; + +int MessagePumpLibInit(lua_State* L) +{ + // register the library's functions in the Lua engine + for (const luaL_Reg* it = pumpLib; it->name; it ++) + lua_register(L, it->name, it->func); + + return 0; +} diff --git a/scite/win32/SciBall.ico b/scite/win32/SciBall.ico index 28b4ff4..cfa3a68 100644 Binary files a/scite/win32/SciBall.ico and b/scite/win32/SciBall.ico differ diff --git a/scite/win32/SciTERes.rc b/scite/win32/SciTERes.rc index 666ef83..c65f45e 100644 --- a/scite/win32/SciTERes.rc +++ b/scite/win32/SciTERes.rc @@ -6,7 +6,9 @@ #include "SciTE.h" -SciTE ICON SciBall.ico +// fincs-edit +//SciTE ICON SciBall.ico +IDI_MAINICON ICON SciBall.ico 1 RT_MANIFEST SciTE.exe.manifest @@ -179,24 +181,24 @@ END POPUP "&Help" BEGIN MENUITEM "&Help\tF1", IDM_HELP + MENUITEM "&SciTE4AutoHotkey Help", IDM_HELP_SCITE #ifdef STATIC_BUILD - MENUITEM "&Sc1 Help", IDM_HELP_SCITE - MENUITEM "&About Sc1", IDM_ABOUT + MENUITEM "&About SciTE4AutoHotkey Lite", IDM_ABOUT #else - MENUITEM "&SciTE Help", IDM_HELP_SCITE - MENUITEM "&About SciTE", IDM_ABOUT + MENUITEM "&About SciTE4AutoHotkey", IDM_ABOUT #endif END END ABOUT DIALOGEX 26, 41, 350, 242 -CAPTION "About SciTE" +CAPTION "About SciTE4AutoHotkey" STYLE DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU FONT 8, "MS Shell Dlg" BEGIN CONTROL "", IDABOUTSCINTILLA, "Scintilla", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 1, 1, 346, 218 - ICON "SciTE", -1, 1, 221, 32, 32, WS_CHILD | WS_VISIBLE + //ICON "SciTE", -1, 1, 221, 32, 32, WS_CHILD | WS_VISIBLE + ICON IDI_MAINICON, -1, 1, 221, 32, 32, WS_CHILD | WS_VISIBLE // fincs-edit DEFPUSHBUTTON "OK", IDOK, 26, 222, 322, 20, WS_TABSTOP END @@ -565,6 +567,7 @@ BEGIN END IDR_CLOSEFILE BITMAP "closefile.bmp" +IDR_CLOSEFILE_BIG BITMAP "closefile_big.bmp" IDBM_WORD BITMAP "word.bmp" IDBM_CASE BITMAP "case.bmp" @@ -617,13 +620,13 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "CompanyName", "Neil Hodgson neilh@scintilla.org\0" - VALUE "FileDescription", "SciTE - a Scintilla based Text Editor\0" + VALUE "CompanyName", "fincs - Original SciTE by Neil Hodgson: neilh@scintilla.org\0" + VALUE "FileDescription", "SciTE4AutoHotkey - a SciTE distribution for AutoHotkey\0" VALUE "FileVersion", VERSION_SCITE "\0" - VALUE "InternalName", "SciTE\0" - VALUE "LegalCopyright", "Copyright " COPYRIGHT_YEARS " by Neil Hodgson\0" - VALUE "OriginalFilename", "SciTE.EXE\0" - VALUE "ProductName", "SciTE\0" + VALUE "InternalName", "SciTE4AutoHotkey\0" + VALUE "LegalCopyright", "SciTE is (c) " COPYRIGHT_YEARS " Neil Hodgson, SciTE4AutoHotkey is (c) " COPYRIGHT_YEARS_S4AHK " by fincs\0" + VALUE "OriginalFilename", "SciTE.exe\0" + VALUE "ProductName", "SciTE4AutoHotkey\0" VALUE "ProductVersion", VERSION_SCITE "\0" END END diff --git a/scite/win32/SciTEWin.cxx b/scite/win32/SciTEWin.cxx index 8b0b69e..d0fc3ca 100644 --- a/scite/win32/SciTEWin.cxx +++ b/scite/win32/SciTEWin.cxx @@ -23,9 +23,9 @@ #endif #ifdef STATIC_BUILD -const GUI::gui_char appName[] = GUI_TEXT("Sc1"); +const GUI::gui_char appName[] = GUI_TEXT("SciTE4AutoHotkey Lite"); // fincs-edit #else -const GUI::gui_char appName[] = GUI_TEXT("SciTE"); +const GUI::gui_char appName[] = GUI_TEXT("SciTE4AutoHotkey"); // fincs-edit #endif static GUI::gui_string GetErrorMessage(DWORD nRet) { @@ -182,6 +182,11 @@ SciTEWin::SciTEWin(Extension *ext) : SciTEBase(ext) { // System type properties are also stored in the embedded properties. propsEmbed.Set("PLAT_WIN", "1"); propsEmbed.Set("PLAT_WINNT", "1"); +#ifdef _WIN64 + propsEmbed.Set("PLAT_WIN64", "1"); +#else + propsEmbed.Set("PLAT_WIN32", "1"); +#endif HRSRC handProps = ::FindResource(hInstance, TEXT("Embedded"), TEXT("Properties")); if (handProps) { @@ -203,7 +208,8 @@ SciTEWin::SciTEWin(Extension *ext) : SciTEBase(ext) { SetScaleFactor(0); - tbLarge = props.GetInt("toolbar.large"); + extern int g_ScreenDPI; + tbLarge = props.GetInt("toolbar.large", g_ScreenDPI >= 120); /// Need to copy properties to variables before setting up window SetPropertiesInitial(); ReadAbbrevPropFile(); @@ -249,12 +255,14 @@ void SciTEWin::Register(HINSTANCE hInstance_) { // Register the frame window className = TEXT("SciTEWindow"); - wndclass.style = 0; + wndclass.style = CS_DBLCLKS; // fincs-edit wndclass.lpfnWndProc = SciTEWin::TWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = sizeof(SciTEWin*); wndclass.hInstance = hInstance; - wndclass.hIcon = ::LoadIcon(hInstance, resourceName); + // fincs-edit + //wndclass.hIcon = ::LoadIcon(hInstance, resourceName); + wndclass.hIcon = (HICON) ::LoadImage(hInstance, MAKEINTRESOURCE(IDI_MAINICON), IMAGE_ICON, 0, 0, LR_SHARED); wndclass.hCursor = NULL; wndclass.hbrBackground = NULL; wndclass.lpszMenuName = resourceName; @@ -436,10 +444,82 @@ FilePath SciTEWin::GetSciteDefaultHome() { return GetSciTEPath(home); } +// fincs-edit START +static bool _FileExistsInSciTEDir(PCWSTR aFileName) +{ + wchar_t aBuf[MAX_PATH+1]; + wchar_t* aBufPtr = aBuf; + + GetModuleFileNameW(NULL, aBuf, MAX_PATH+1); + aBufPtr += wcslen(aBuf); + while(*--aBufPtr != '\\'); + wcscpy(aBufPtr+1, aFileName); + + return GetFileAttributes(aBuf) != INVALID_FILE_ATTRIBUTES; +} + +bool _IsPortable() +{ + static bool bInit = false, ret = false; + if (!bInit) + { + bInit = true; + ret = _FileExistsInSciTEDir(L"$PORTABLE"); + } + return ret; +} + +bool _IsDeveloper() +{ + static bool bInit = false, ret = false; + if (!bInit) + { + bInit = true; + ret = _FileExistsInSciTEDir(L"$DEV"); + } + return ret; +} + +bool _IsProtectedFile(FilePath file) +{ + if (_IsPortable() || _IsDeveloper()) return false; + GUI::gui_string basePath = GetSciTEPath(FilePath()).AsInternal(); + basePath.append(GUI_TEXT("\\")); + return wcsncmp(file.AsInternal(), basePath.c_str(), basePath.length()) == 0 + && !file.Name().SameNameAs(FilePath(GUI_TEXT("TestSuite.ahk"))); +} +// fincs-edit END + FilePath SciTEWin::GetSciteUserHome() { + bool is_portable = _IsPortable(); // fincs-edit + GUI::gui_char *home = _wgetenv(GUI_TEXT("SciTE_HOME")); if (!home) - home = _wgetenv(GUI_TEXT("USERPROFILE")); + { // fincs-edit START + WCHAR szPath[MAX_PATH+1]; + bool success = false; + + if(!is_portable) + { + if(SUCCEEDED(::SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL, 0, szPath))) + { + ::PathAppendW(szPath, GUI_TEXT("AutoHotkey\\SciTE")); + home = szPath; + success = true; + } + }else + { + ::GetModuleFileNameW(NULL, szPath, MAX_PATH+1); + ::PathRemoveFileSpecW(szPath); + ::PathAppendW(szPath, GUI_TEXT("user")); + home = szPath; + success = true; + } + + if(!success) + home = _wgetenv(GUI_TEXT("USERPROFILE")); + } // fincs-edit END + return GetSciTEPath(home); } @@ -591,6 +671,10 @@ HWND SciTEWin::MainHWND() { return HwndOf(wSciTE); } +HWND SciTEWin::ToolHWND() { // fincs-edit + return reinterpret_cast(wToolBar.GetID()); // fincs-edit +} // fincs-edit + void SciTEWin::Command(WPARAM wParam, LPARAM lParam) { int cmdID = ControlIDOfWParam(wParam); if (wParam & 0x10000) { @@ -1436,6 +1520,21 @@ void SciTEWin::Run(const GUI::gui_char *cmdLine) { return; // Don't do anything else } +#ifndef STATIC_BUILD + // fincs-edit + do + { + std::string sMainHwnd = StdStringFromSizeT(reinterpret_cast(MainHWND())); + props.Set("scite.hwnd", sMainHwnd.c_str()); + + std::string autorun = props.GetExpandedString("command.autorun"); + if (autorun.length() == 0) + break; + + ShellExec(autorun, ""); + } while(0); +#endif + // OK, the instance will be displayed SizeSubWindows(); wSciTE.Show(); @@ -1868,6 +1967,10 @@ LRESULT SciTEWin::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) { WorkerCommand(static_cast(wParam), reinterpret_cast(lParam)); break; + case WM_LBUTTONDBLCLK: // fincs-edit + ::SendMessage(MainHWND(), WM_COMMAND, IDM_NEW, 0); + return 0; + case WM_NOTIFY: Notify(reinterpret_cast(lParam)); break; @@ -1966,6 +2069,20 @@ LRESULT SciTEWin::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) { case WM_COPYDATA: return uniqueInstance.CopyData(reinterpret_cast(lParam)); + case WM_USER: // fincs-edit + SIZE uTSize; // fincs-edit + ::SendMessage(ToolHWND(), TB_GETMAXSIZE, 0, (LPARAM)&uTSize); // fincs-edit + return (LRESULT) uTSize.cx; // fincs-edit + + case WM_USER+1: // fincs-edit + ReloadProperties(); // fincs-edit + return 0l; // fincs-edit + + case WM_USER+2: // fincs-edit, yet again + debugging = !!wParam; + SetWindowName(); + break; + default: return ::DefWindowProcW(MainHWND(), iMessage, wParam, lParam); } @@ -2129,6 +2246,20 @@ uptr_t SciTEWin::EventLoop() { #pragma warning(disable: 28251) #endif +// fincs-edit START + +static int GetScreenDPI() +{ + HDC hdc = GetDC(NULL); + int dpi = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(NULL, hdc); + return dpi; +} + +int g_ScreenDPI = GetScreenDPI(); + +// fincs-edit END + int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) { typedef BOOL (WINAPI *SetDllDirectorySig)(LPCTSTR lpPathName); diff --git a/scite/win32/SciTEWin.h b/scite/win32/SciTEWin.h index c4ed7e1..bff6686 100644 --- a/scite/win32/SciTEWin.h +++ b/scite/win32/SciTEWin.h @@ -44,12 +44,13 @@ typedef void *HTHEME; #endif #include #include -#include #include #include #include #include +#include +#include #if defined(DTBG_CLIPRECT) && !defined(DISABLE_THEMES) #define THEME_AVAILABLE @@ -309,6 +310,7 @@ protected: void FullScreenToggle(); void Command(WPARAM wParam, LPARAM lParam); HWND MainHWND(); + HWND ToolHWND(); // fincs-edit virtual void UserStripShow(const char *description); virtual void UserStripSet(int control, const char *value); diff --git a/scite/win32/SciTEWinBar.cxx b/scite/win32/SciTEWinBar.cxx index 61184a4..fba7dbf 100644 --- a/scite/win32/SciTEWinBar.cxx +++ b/scite/win32/SciTEWinBar.cxx @@ -892,7 +892,7 @@ void SciTEWin::Creation() { tbLarge ? IDB_STD_LARGE_COLOR : IDB_STD_SMALL_COLOR, reinterpret_cast(HINST_COMMCTRL)); - TBADDBITMAP addbmp = { hInstance, IDR_CLOSEFILE }; + TBADDBITMAP addbmp = { hInstance, tbLarge ? IDR_CLOSEFILE_BIG : IDR_CLOSEFILE }; ::SendMessage(hwndToolBar, TB_ADDBITMAP, 1, (LPARAM)&addbmp); TBBUTTON tbb[ELEMENTS(bbs)]; diff --git a/scite/win32/SciTEWinDlg.cxx b/scite/win32/SciTEWinDlg.cxx index 6a8c046..d3d0ce2 100644 --- a/scite/win32/SciTEWinDlg.cxx +++ b/scite/win32/SciTEWinDlg.cxx @@ -250,6 +250,11 @@ bool SciTEWin::OpenDialog(FilePath directory, const GUI::gui_char *filesFilter) if (props.GetInt("open.dialog.in.file.directory")) { ofn.lpstrInitialDir = directory.AsInternal(); } + // fincs-edit START + GUI::gui_string defExt = GUI::StringFromUTF8(props.GetExpandedString("default.file.ext")); + if (defExt.length() > 0) + ofn.lpstrDefExt = defExt.c_str() + 1; + // fincs-edit END ofn.Flags = OFN_HIDEREADONLY; if (buffers.size > 1) { @@ -301,6 +306,11 @@ FilePath SciTEWin::ChooseSaveName(FilePath directory, const char *title, const G ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; ofn.lpstrFilter = filesFilter; ofn.lpstrInitialDir = directory.AsInternal(); + // fincs-edit START + GUI::gui_string defExt = GUI::StringFromUTF8(props.GetExpandedString("default.file.ext")); + if (defExt.length() > 0) + ofn.lpstrDefExt = defExt.c_str() + 1; + // fincs-edit END dialogsOnScreen++; if (::GetSaveFileNameW(&ofn)) { @@ -318,6 +328,17 @@ bool SciTEWin::SaveAsDialog() { GUI::StringFromUTF8(props.GetExpandedString("save.filter")).c_str()); FilePath path = ChooseSaveName(filePath.Directory(), "Save File", saveFilter.c_str()); if (path.IsSet()) { + extern bool _IsProtectedFile(FilePath); + if (_IsProtectedFile(path)) + { + GUI::gui_string msg = LocaliseMessage( + "Folder '^0' is reserved for SciTE4AutoHotkey program files.\n\n" + "The SciTE4AutoHotkey program folder is deleted and repopulated everytime the program is updated. " + "In order to prevent data loss, please select another one." + , path.Directory().AsInternal()); + WindowMessageBox(wSciTE, msg); + return SaveAsDialog(); + } return SaveIfNotOpen(path, false); } return false; @@ -1697,7 +1718,7 @@ BOOL SciTEWin::AboutMessage(HWND hDlg, UINT message, WPARAM wParam) { LONG_PTR subclassedProc = ::SetWindowLongPtr(hwndCredits, GWLP_WNDPROC, reinterpret_cast(CreditsWndProc)); ::SetWindowLongPtr(hwndCredits, GWLP_USERDATA, subclassedProc); ss.SetID(hwndCredits); - SetAboutMessage(ss, staticBuild ? "Sc1 " : "SciTE"); + SetAboutMessage(ss, staticBuild ? "SciTE4AutoHotkey Lite" : "SciTE4AutoHotkey"); // fincs-edit } return TRUE; diff --git a/scite/win32/Strips.cxx b/scite/win32/Strips.cxx index 30e9aff..54eeec8 100644 --- a/scite/win32/Strips.cxx +++ b/scite/win32/Strips.cxx @@ -97,6 +97,7 @@ static const char *textFindPrompt = "Fi&nd:"; static const char *textReplacePrompt = "Rep&lace:"; static const char *textFindNext = "&Find Next"; static const char *textMarkAll = "&Mark All"; +static const char *textUnmarkAll = "&Unmark All"; static const char *textReplace = "&Replace"; static const char *textReplaceAll = "Replace &All"; @@ -962,6 +963,7 @@ void FindStrip::Creation() { wButton = CreateButton(textFindNext, IDOK); wButtonMarkAll = CreateButton(textMarkAll, IDMARKALL); + wButtonUnmarkAll = CreateButton(textUnmarkAll, IDM_BOOKMARK_CLEARALL); wCheckWord = CreateButton(toggles[SearchOption::tWord].label, toggles[SearchOption::tWord].id, true); wCheckCase = CreateButton(toggles[SearchOption::tCase].label, toggles[SearchOption::tCase].id, true); @@ -1010,6 +1012,10 @@ void FindStrip::Size() { wCheckWord.SetPosition(rcButton); rcButton.right = rcButton.left - 4; + rcButton.left = rcButton.right - WidthControl(wButtonUnmarkAll); + wButtonUnmarkAll.SetPosition(rcButton); + + rcButton.right = rcButton.left - 4; rcButton.left = rcButton.right - WidthControl(wButtonMarkAll); wButtonMarkAll.SetPosition(rcButton); @@ -1110,6 +1116,9 @@ bool FindStrip::Command(WPARAM wParam) { } else if (control == IDMARKALL) { Next(true, false); return true; + } else if (control == IDM_BOOKMARK_CLEARALL) { + pSearcher->ClearAllBookmarks(); + return true; } else if (control == IDFINDWHAT) { if (subCommand == CBN_EDITCHANGE) { NextIncremental(changingEdit); diff --git a/scite/win32/Strips.h b/scite/win32/Strips.h index e360f4f..a7d19fd 100644 --- a/scite/win32/Strips.h +++ b/scite/win32/Strips.h @@ -159,6 +159,7 @@ public: class FindStrip : public FindReplaceStrip { GUI::Window wButton; GUI::Window wButtonMarkAll; + GUI::Window wButtonUnmarkAll; GUI::Window wCheckUp; public: FindStrip() { diff --git a/scite/win32/closefile.bmp b/scite/win32/closefile.bmp index 1050a29..2aabd4f 100644 Binary files a/scite/win32/closefile.bmp and b/scite/win32/closefile.bmp differ diff --git a/scite/win32/closefile_big.bmp b/scite/win32/closefile_big.bmp new file mode 100644 index 0000000..37268aa Binary files /dev/null and b/scite/win32/closefile_big.bmp differ