+ Fix empty link titles in Markdown.
+ Bug #2235.
+ Also fix detection of whether the previous line had content which treated '\n' and '\r\n' line endings differently.
+
+
+ Remove nested comment and long string support as these were removed from Lua.
+ Bug #2205.
+
Release 5.0.2
diff --git a/lexilla/include/LexicalStyles.iface b/lexilla/include/LexicalStyles.iface
index 5c276c161..0852c400b 100644
--- a/lexilla/include/LexicalStyles.iface
+++ b/lexilla/include/LexicalStyles.iface
@@ -141,6 +141,7 @@ val SCLEX_DATAFLEX=129
val SCLEX_HOLLYWOOD=130
val SCLEX_RAKU=131
val SCLEX_FSHARP=132
+val SCLEX_JULIA=133
# When a lexer specifies its language as SCLEX_AUTOMATIC it receives a
# value assigned in sequence from SCLEX_AUTOMATIC+1.
@@ -1047,6 +1048,28 @@ val SCE_ERLANG_MODULES_ATT=24
val SCE_ERLANG_UNKNOWN=31
# Lexical states for SCLEX_OCTAVE are identical to MatLab
lex Octave=SCLEX_OCTAVE SCE_MATLAB_
+# Lexical states for SCLEX_JULIA
+lex Julia=SCLEX_JULIA SCE_JULIA_
+val SCE_JULIA_DEFAULT=0
+val SCE_JULIA_COMMENT=1
+val SCE_JULIA_NUMBER=2
+val SCE_JULIA_KEYWORD1=3
+val SCE_JULIA_KEYWORD2=4
+val SCE_JULIA_KEYWORD3=5
+val SCE_JULIA_CHAR=6
+val SCE_JULIA_OPERATOR=7
+val SCE_JULIA_BRACKET=8
+val SCE_JULIA_IDENTIFIER=9
+val SCE_JULIA_STRING=10
+val SCE_JULIA_SYMBOL=11
+val SCE_JULIA_MACRO=12
+val SCE_JULIA_STRINGINTERP=13
+val SCE_JULIA_DOCSTRING=14
+val SCE_JULIA_STRINGLITERAL=15
+val SCE_JULIA_COMMAND=16
+val SCE_JULIA_COMMANDLITERAL=17
+val SCE_JULIA_TYPEANNOT=18
+val SCE_JULIA_LEXERROR=19
# Lexical states for SCLEX_MSSQL
lex MSSQL=SCLEX_MSSQL SCE_MSSQL_
val SCE_MSSQL_DEFAULT=0
diff --git a/lexilla/include/SciLexer.h b/lexilla/include/SciLexer.h
index e64a578d7..a3ba58129 100644
--- a/lexilla/include/SciLexer.h
+++ b/lexilla/include/SciLexer.h
@@ -145,6 +145,7 @@
#define SCLEX_HOLLYWOOD 130
#define SCLEX_RAKU 131
#define SCLEX_FSHARP 132
+#define SCLEX_JULIA 133
#define SCLEX_AUTOMATIC 1000
#define SCE_P_DEFAULT 0
#define SCE_P_COMMENTLINE 1
@@ -932,6 +933,26 @@
#define SCE_ERLANG_MODULES 23
#define SCE_ERLANG_MODULES_ATT 24
#define SCE_ERLANG_UNKNOWN 31
+#define SCE_JULIA_DEFAULT 0
+#define SCE_JULIA_COMMENT 1
+#define SCE_JULIA_NUMBER 2
+#define SCE_JULIA_KEYWORD1 3
+#define SCE_JULIA_KEYWORD2 4
+#define SCE_JULIA_KEYWORD3 5
+#define SCE_JULIA_CHAR 6
+#define SCE_JULIA_OPERATOR 7
+#define SCE_JULIA_BRACKET 8
+#define SCE_JULIA_IDENTIFIER 9
+#define SCE_JULIA_STRING 10
+#define SCE_JULIA_SYMBOL 11
+#define SCE_JULIA_MACRO 12
+#define SCE_JULIA_STRINGINTERP 13
+#define SCE_JULIA_DOCSTRING 14
+#define SCE_JULIA_STRINGLITERAL 15
+#define SCE_JULIA_COMMAND 16
+#define SCE_JULIA_COMMANDLITERAL 17
+#define SCE_JULIA_TYPEANNOT 18
+#define SCE_JULIA_LEXERROR 19
#define SCE_MSSQL_DEFAULT 0
#define SCE_MSSQL_COMMENT 1
#define SCE_MSSQL_LINE_COMMENT 2
diff --git a/lexilla/lexers/LexBatch.cxx b/lexilla/lexers/LexBatch.cxx
index 9775c2174..123ed712d 100644
--- a/lexilla/lexers/LexBatch.cxx
+++ b/lexilla/lexers/LexBatch.cxx
@@ -423,14 +423,54 @@ static void ColouriseBatchDoc(
// Reset Offset to re-process remainder of word
offset -= (wbl - 2);
// Check for Expanded Argument (%~...) / Variable (%%~...)
+ // Expanded Argument: %~[]
+ // Expanded Variable: %%~[]
+ // Path operators are exclusively alphabetic.
+ // Expanded arguments have a single digit at the end.
+ // Expanded variables have a single identifier character as variable name.
} else if (((wbl > 1) && (wordBuffer[1] == '~')) ||
((wbl > 2) && (wordBuffer[1] == '%') && (wordBuffer[2] == '~'))) {
// Check for External Command / Program
if (cmdLoc == offset - wbl) {
cmdLoc = offset - (wbl - wbo);
}
- // Colorize Expanded Argument / Variable
- styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
+ bool isArgument = (wordBuffer[1] == '~');
+ if (isArgument) {
+ Sci_PositionU expansionStopOffset = 2;
+ bool isValid = false;
+ for (; expansionStopOffset < wbl; expansionStopOffset++) {
+ if (isArgument && Is0To9(wordBuffer[expansionStopOffset])) {
+ expansionStopOffset++;
+ isValid = true;
+ wbo = expansionStopOffset;
+ // Colorize Expanded Argument
+ styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
+ break;
+ }
+ }
+ if (!isValid) {
+ // not a valid expanded argument or variable
+ styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT);
+ }
+ // Expanded Variable
+ } else {
+ // start after ~
+ wbo = 3;
+ // Search to end of word for another % (can be a long path)
+ while ((wbo < wbl) &&
+ (wordBuffer[wbo] != '%') &&
+ (!IsBOperator(wordBuffer[wbo])) &&
+ (!IsBSeparator(wordBuffer[wbo]))) {
+ wbo++;
+ }
+ if (wbo > 3) {
+ // Colorize Expanded Variable
+ styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
+ } else {
+ // not a valid expanded argument or variable
+ styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT);
+ }
+ }
// Reset Offset to re-process remainder of word
offset -= (wbl - wbo);
// Check for Environment Variable (%x...%)
diff --git a/lexilla/lexers/LexJulia.cxx b/lexilla/lexers/LexJulia.cxx
new file mode 100644
index 000000000..a41b0e700
--- /dev/null
+++ b/lexilla/lexers/LexJulia.cxx
@@ -0,0 +1,1220 @@
+// Scintilla source code edit control
+// Encoding: UTF-8
+/** @file LexJulia.cxx
+ ** Lexer for Julia.
+ ** Reusing code from LexMatlab, LexPython and LexRust
+ **
+ ** Written by Bertrand Lacoste
+ **
+ **/
+// Copyright 1998-2001 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
diff --git a/src/StyleLexers/EditLexer.h b/src/StyleLexers/EditLexer.h
index aeb95a53a..3e01f2c83 100644
--- a/src/StyleLexers/EditLexer.h
+++ b/src/StyleLexers/EditLexer.h
@@ -113,6 +113,7 @@ extern EDITLEXER lexINNO; // Inno Setup Script
extern EDITLEXER lexJAVA; // Java Source Code
extern EDITLEXER lexJS; // JavaScript
extern EDITLEXER lexJSON; // JSON
+extern EDITLEXER lexJulia; // Julia
extern EDITLEXER lexKotlin; // Kotlin
extern EDITLEXER lexLATEX; // LaTeX Files
extern EDITLEXER lexLUA; // Lua Script
diff --git a/src/StyleLexers/styleLexJulia.c b/src/StyleLexers/styleLexJulia.c
new file mode 100644
index 000000000..b045bc995
--- /dev/null
+++ b/src/StyleLexers/styleLexJulia.c
@@ -0,0 +1,88 @@
+#include "StyleLexers.h"
+
+// ----------------------------------------------------------------------------
+
+//KEYWORDLIST KeyWords_Julia = EMPTY_KEYWORDLIST;
+KEYWORDLIST KeyWords_Julia =
+{
+ // 0 Primary keywords and identifiers
+ "abstract baremodule begin break catch const continue do else elseif end export finally for function global "
+ "if import in isa let local macro module mutable primitive quote return struct try type using var where while "
+
+ , // 1 Built in types
+ "AbstractArray AbstractArrayStyle AbstractChannel AbstractChar AbstractDict AbstractDisplay AbstractFloat "
+ "AbstractIrrational AbstractLock AbstractLogger AbstractMatrix AbstractRNG AbstractRange "
+ "AbstractSet AbstractSparseArray AbstractSparseMatrix AbstractSparseVector AbstractString AbstractUnitRange "
+ "AbstractVecOrMat AbstractVector AbstractWorkerPool Adjoint Anonymous Any ArgumentError Array ArrayStyle "
+ "AssertionError AsyncCondition Atomic "
+ "Base64DecodePipe Base64EncodePipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitSet BitVector Bool BoundsError "
+ "BroadcastStyle "
+ "CFunction CapturedException CartesianIndex CartesianIndices Cchar Cdouble Cfloat Channel Char Cint Cintmax_t "
+ "Clong Clonglong ClusterManager Cmd "
+ "Colon Complex ComplexF16 ComplexF32 ComplexF64 CompositeException CompoundPeriod Condition ConsoleLogger Cptrdiff_t "
+ "Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cwchar_t Cwstring "
+ "DataType Date DateFormat DateTime Day DefaultArrayStyle DenseArray DenseMatrix DenseVecOrMat DenseVector "
+ "Diagonal Dict DimensionMismatch Dims DivideError DomainError "
+ "EOFError Enum EnvDict ErrorException Event Exception ExponentialBackOff Expr Float16 Float32 Float64 Function Future "
+ "Givens GlobalRef Hermitian Hour "
+ "IO IOBuffer IOContext IOStream IPAddr IPv4 IPv6 IdDict ImmutableDict IndexCartesian IndexLinear IndexStyle InexactError "
+ "InitError Instant Int Int128 Int16 Int32 Int64 Int8 Integer InterruptException InvalidStateException Irrational "
+ "IteratorEltype IteratorSize "
+ "KeyError LinRange LineNumberNode LinearIndices LoadError LogLevel LowerTriangular "
+ "MIME Matrix MersenneTwister Method MethodError Microsecond Millisecond Minute Missing MissingException Module Month "
+ "Mutex "
+ "NTuple NamedTuple Nanosecond Nothing NullLogger Number OneTo OrdinalRange OutOfMemoryError OverflowError "
+ "Pair Pairs PartialQuickSort Period PermutedDimsArray Pipe PipeBuffer PosDefException ProcessFailedException Ptr "
+ "QR QRCompactWY QRPivoted QuoteNode "
+ "Random RandomDevice Rational RawFD "
+ "ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RemoteChannel RemoteException RoundingMode "
+ "Second SegmentationFault Semaphore Serialization Set SharedArray SharedMatrix SharedVector "
+ "Signed SimpleLogger SingularException Some SparseMatrixCSC SparseVector SpinLock "
+ "StackFrame StackOverflowError StackTrace Stateful StepRange StepRangeLen "
+ "StridedArray StridedMatrix StridedVecOrMat StridedVector String StringIndexError SubArray SubString SubstitutionString "
+ "SymTridiagonal Symbol Symmetric SystemError "
+ "TCPSocket Task TextDisplay Time TimeType Timer TmStruct Transpose Tridiagonal Tuple Type TypeError TypeVar "
+ "UDPSocket UInt UInt128 UInt16 UInt32 UInt64 UInt8 UTInstant "
+ "UndefInitializer UndefKeywordError UndefRefError UndefVarError "
+ "UniformScaling Union UnionAll UnitLowerTriangular UnitRange UnitUpperTriangular Unsigned UpperTriangular "
+ "Val Vararg VecElement VecOrMat Vector VersionNumber WeakKeyDict WeakRef Week WorkerConfig WorkerPool Year "
+
+ , // 2 Other keywords
+ "allocated assert async boundscheck cfunction debug deprecate distributed dump "
+ "elapsed enum error eval evalpoly everywhere fastmath fetch generated gensym goto "
+ "inbounds inferred info inline isdefined label logmsg lower macroexpand macroexpand1 noinline nospecialize "
+ "polly preserve printf profile propagate_inbounds pure show spawn spawnat specialize sprintf static sync "
+ "task test test_broken test_deprecated test_logs test_nowarn test_skip test_throws test_warn testset threads "
+ "time timed timev "
+ "view views warn "
+
+ , // 3 Raw string literals
+ "raw "
+
+ , NULL
+};
+
+
+EDITLEXER lexJulia =
+{
+ SCLEX_JULIA, "julia", IDS_LEX_JULIA_SCR, L"Julia Script", L"jl", L"",
+ &KeyWords_Julia, {
+ { { STYLE_DEFAULT }, IDS_LEX_STR_63126, L"Default", L"", L"" },
+ //{ {SCE_JULIA_DEFAULT}, IDS_LEX_STR_63126, L"Default", L"", L"" },
+ { { SCE_JULIA_COMMENT }, IDS_LEX_STR_63127, L"Comment", L"fore:#608060", L"" },
+ { { SCE_JULIA_NUMBER }, IDS_LEX_STR_63130, L"Number", L"fore:#FF0000", L"" },
+ { { SCE_JULIA_KEYWORD1 }, IDS_LEX_STR_63128, L"Keyword", L"bold; fore:#0000FF", L"" },
+ { { MULTI_STYLE(SCE_JULIA_KEYWORD2, SCE_JULIA_KEYWORD3, 0, 0) }, IDS_LEX_STR_63260, L"Keyword 2nd", L"bold; fore:#8A008A", L"" },
+ { { MULTI_STYLE(SCE_JULIA_CHAR, SCE_JULIA_STRING, 0, 0) }, IDS_LEX_STR_63131, L"String", L"fore:#BB2F00", L"" },
+ { { MULTI_STYLE(SCE_JULIA_OPERATOR, SCE_JULIA_BRACKET, 0, 0) }, IDS_LEX_STR_63132, L"Operator", L"fore:#B53A00", L"" },
+ { { SCE_JULIA_IDENTIFIER }, IDS_LEX_STR_63129, L"Identifier", L"fore:#00007B", L"" },
+ { { SCE_JULIA_SYMBOL }, IDS_LEX_STR_63293, L"Symbol", L"fore:#C0A030", L"" },
+ { { SCE_JULIA_MACRO }, IDS_LEX_STR_63280, L"Macro Def", L"fore:#0080FF", L"" },
+ { { SCE_JULIA_DOCSTRING }, IDS_LEX_STR_63259, L"Comment Doc", L"fore:#808080", L"" },
+ { { MULTI_STYLE(SCE_JULIA_STRINGINTERP, SCE_JULIA_STRINGLITERAL, 0, 0) }, IDS_LEX_STR_63301, L"Literal String", L"fore:#B000B0", L"" },
+ { { MULTI_STYLE(SCE_JULIA_COMMAND, SCE_JULIA_COMMANDLITERAL, 0, 0) }, IDS_LEX_STR_63236, L"Command", L"bold; fore:#0000DD", L"" },
+ { { SCE_JULIA_TYPEANNOT }, IDS_LEX_STR_63370, L"Annotation", L"fore:#FF8000", L"" },
+ { { SCE_JULIA_LEXERROR }, IDS_LEX_STR_63252, L"Parsing Error", L"fore:#FFFF00; back:#A00000; eolfilled", L"" },
+ EDITLEXER_SENTINEL
+ }
+};
diff --git a/src/Styles.c b/src/Styles.c
index 1c39567f9..94c6fcf8f 100644
--- a/src/Styles.c
+++ b/src/Styles.c
@@ -27,6 +27,8 @@
#include "Lexilla.h"
#include "lexers_x/SciXLexer.h"
+#include "uthash/utarray.h"
+
#include "Edit.h"
#include "Dialogs.h"
#include "Encoding.h"
@@ -49,7 +51,7 @@ extern COLORREF g_colorCustom[16];
// This array holds all the lexers...
// Don't forget to change the number of the lexer for HTML and XML
// in Notepad2.c ParseCommandLine() if you change this array!
-static PEDITLEXER g_pLexArray[NUMLEXERS] = {
+static PEDITLEXER g_pLexArray[] = {
&lexStandard, // Default Text
&lexStandard2nd, // 2nd Default Text
&lexTEXT, // Pure Text Files (Constants.StdDefaultLexerID = 2)
@@ -76,6 +78,7 @@ static PEDITLEXER g_pLexArray[NUMLEXERS] = {
&lexJAVA, // Java Source Code
&lexJS, // JavaScript
&lexJSON, // JSON
+ &lexJulia, // Julia
&lexKotlin, // Kotlin
&lexLATEX, // LaTeX Files
&lexLUA, // Lua Script
@@ -105,6 +108,10 @@ static PEDITLEXER g_pLexArray[NUMLEXERS] = {
&lexYAML, // YAML
};
+int Style_NumOfLexers() {
+ return COUNTOF(g_pLexArray);
+}
+
// Currently used lexer
static PEDITLEXER s_pLexCurrent = &lexStandard;
@@ -3968,6 +3975,19 @@ static void _UpdateTitleText(HWND hwnd)
}
+static void _style_copy(void* _dst, const void* _src) {
+ LPCWSTR const * src = (LPWSTR const *)_src;
+ LPCWSTR * dst = (LPWSTR *)_dst;
+ *dst = *src ? StrDup(*src) : NULL;
+}
+
+static void _style_dtor(void *_elt) {
+ LPWSTR * const elt = (LPWSTR *)_elt;
+ if (*elt) { LocalFree(*elt); }
+}
+
+static UT_icd _style_icd = { sizeof(LPWSTR), NULL, _style_copy, _style_dtor };
+
INT_PTR CALLBACK Style_CustomizeSchemesDlgProc(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam)
{
static HWND hwndTV;
@@ -3980,9 +4000,9 @@ INT_PTR CALLBACK Style_CustomizeSchemesDlgProc(HWND hwnd, UINT umsg, WPARAM wPar
static HBRUSH hbrBack = { 0 };
static bool bIsStyleSelected = false;
static bool bWarnedNoIniFile = false;
- static WCHAR* Style_StylesBackup[NUMLEXERS * AVG_NUM_OF_STYLES_PER_LEXER];
static WCHAR tchTmpBuffer[max(BUFSIZE_STYLE_VALUE, BUFZIZE_STYLE_EXTENTIONS)] = {L'\0'};
+ static UT_array *pStylesBackup = NULL;
switch (umsg) {
case WM_INITDIALOG: {
@@ -3993,6 +4013,12 @@ INT_PTR CALLBACK Style_CustomizeSchemesDlgProc(HWND hwnd, UINT umsg, WPARAM wPar
_UpdateTitleText(hwnd);
+ if (pStylesBackup) {
+ utarray_free(pStylesBackup);
+ pStylesBackup = NULL;
+ }
+ utarray_new(pStylesBackup, &_style_icd);
+
#ifdef D_NP3_WIN10_DARK_MODE
if (UseDarkMode()) {
SetExplorerTheme(GetDlgItem(hwnd, IDOK));
@@ -4018,14 +4044,13 @@ INT_PTR CALLBACK Style_CustomizeSchemesDlgProc(HWND hwnd, UINT umsg, WPARAM wPar
SetDlgItemText(hwnd, IDC_STYLEEDIT_HELP, tchTmpBuffer);
// Backup Styles
- ZeroMemory(&Style_StylesBackup, NUMLEXERS * AVG_NUM_OF_STYLES_PER_LEXER * sizeof(WCHAR*));
- int cnt = 0;
for (int iLexer = 0; iLexer < COUNTOF(g_pLexArray); ++iLexer) {
- Style_StylesBackup[cnt++] = StrDup(g_pLexArray[iLexer]->szExtensions);
- int i = 0;
+ LPCWSTR const pExt = g_pLexArray[iLexer]->szExtensions;
+ utarray_push_back(pStylesBackup, &pExt);
+ int i = 0;
while (g_pLexArray[iLexer]->Styles[i].iStyle != -1) {
- Style_StylesBackup[cnt++] = StrDup(g_pLexArray[iLexer]->Styles[i].szValue);
- ++i;
+ LPCWSTR const pVal = g_pLexArray[iLexer]->Styles[i++].szValue;
+ utarray_push_back(pStylesBackup, &pVal);
}
}
@@ -4222,23 +4247,11 @@ CASE_WM_CTLCOLOR_SET:
DeleteBitmapButton(hwnd, IDC_NEXTSTYLE);
// free old backup
- int cnt = 0;
- for (int iLexer = 0; iLexer < COUNTOF(g_pLexArray); ++iLexer) {
- if (Style_StylesBackup[cnt]) {
- LocalFree(Style_StylesBackup[cnt]); // StrDup()
- Style_StylesBackup[cnt] = NULL;
- }
- ++cnt;
- int i = 0;
- while (g_pLexArray[iLexer]->Styles[i].iStyle != -1) {
- if (Style_StylesBackup[cnt]) {
- LocalFree(Style_StylesBackup[cnt]); // StrDup()
- Style_StylesBackup[cnt] = NULL;
- }
- ++cnt;
- ++i;
- }
+ if (pStylesBackup) {
+ utarray_free(pStylesBackup);
+ pStylesBackup = NULL;
}
+
if (hFontTitle) {
DeleteObject(hFontTitle);
hFontTitle = NULL;
@@ -4618,19 +4631,18 @@ CASE_WM_CTLCOLOR_SET:
_ApplyDialogItemText(hwnd, pCurrentLexer, pCurrentStyle, iCurStyleIdx, bIsStyleSelected);
// Restore Styles from Backup
- int cnt = 0;
+ LPWSTR * pStyle = (LPWSTR*)utarray_front(pStylesBackup);
for (int iLexer = 0; iLexer < COUNTOF(g_pLexArray); ++iLexer) {
- StringCchCopy(g_pLexArray[iLexer]->szExtensions, COUNTOF(g_pLexArray[iLexer]->szExtensions), Style_StylesBackup[cnt]);
-
- ++cnt;
+ StringCchCopy(g_pLexArray[iLexer]->szExtensions, COUNTOF(g_pLexArray[iLexer]->szExtensions), *pStyle);
+ pStyle = (LPWSTR*)utarray_next(pStylesBackup, (void*)pStyle);
int i = 0;
- while (g_pLexArray[iLexer]->Styles[i].iStyle != -1) {
+ while (pStyle && (g_pLexArray[iLexer]->Styles[i].iStyle != -1)) {
// normalize
tchTmpBuffer[0] = L'\0'; // clear
- Style_CopyStyles_IfNotDefined(Style_StylesBackup[cnt], tchTmpBuffer, COUNTOF(tchTmpBuffer));
+ Style_CopyStyles_IfNotDefined(*pStyle, tchTmpBuffer, COUNTOF(tchTmpBuffer));
StringCchCopy(g_pLexArray[iLexer]->Styles[i].szValue, COUNTOF(g_pLexArray[iLexer]->Styles[i].szValue), tchTmpBuffer);
- ++cnt;
++i;
+ pStyle = (LPWSTR*)utarray_next(pStylesBackup, (void*)pStyle);
}
}
Style_ResetCurrentLexer(Globals.hwndEdit);
diff --git a/src/Styles.h b/src/Styles.h
index 675a04792..c74b82178 100644
--- a/src/Styles.h
+++ b/src/Styles.h
@@ -27,9 +27,7 @@
#define MARGIN_SCI_FOLDING 2
#define NUMBER_OF_MARGINS 3
-// Number of Lexers in pLexArray
-#define NUMLEXERS 53
-#define AVG_NUM_OF_STYLES_PER_LEXER 20
+int Style_NumOfLexers(); // Number of Lexers in pLexArray
#define Theme_FactoryLightMode (0U)
#define Theme_FactoryDarkMode (1U)
diff --git a/src/VersionEx.h b/src/VersionEx.h
index 4cd5c3b6d..f00c75c0f 100644
--- a/src/VersionEx.h
+++ b/src/VersionEx.h
@@ -8,7 +8,7 @@
#define SAPPNAME "Notepad3"
#define VERSION_MAJOR 5
#define VERSION_MINOR 21
-#define VERSION_REV 515
+#define VERSION_REV 525
#define VERSION_BUILD 1
#define SCINTILLA_VER 502
#define LEXILLA_VER 502
diff --git a/test/test_files/StyleLexers/styleLexJulia/x.jl b/test/test_files/StyleLexers/styleLexJulia/x.jl
new file mode 100644
index 000000000..b3e531abf
--- /dev/null
+++ b/test/test_files/StyleLexers/styleLexJulia/x.jl
@@ -0,0 +1,434 @@
+
+# Comment here
+const bar = '\n'
+
+"""
+ test_fun(a::Int)
+For test only
+"""
+function test_fun(a::Int, b::T) where T <: Number
+ println(a)
+ println("foo $(bar)")
+end
+
+@enum Unicode α=1 β=2
+
+res = [√i for i in 1:10]
+
+#= Dummy function =#
+test_fun²(:sym, true, raw"test", `echo 1`)
+
+# function to calculate the volume of a sphere
+function sphere_vol(r)
+ # julia allows Unicode names (in UTF-8 encoding)
+ # so either "pi" or the symbol π can be used
+ return 4/3*pi*r^3
+end
+
+# functions can also be defined more succinctly
+quadratic(a, sqr_term, b) = (-b + sqr_term) / 2a
+
+# calculates x for 0 = a*x^2+b*x+c, arguments types can be defined in function definitions
+function quadratic2(a::Float64, b::Float64, c::Float64)
+ # unlike other languages 2a is equivalent to 2*a
+ # a^2 is used instead of a**2 or pow(a,2)
+ sqr_term = sqrt(b^2-4a*c)
+ r1 = quadratic(a, sqr_term, b)
+ r2 = quadratic(a, -sqr_term, b)
+ # multiple values can be returned from a function using tuples
+ # if the return keyword is omitted, the last term is returned
+ r1, r2
+end
+
+vol = sphere_vol(3)
+# @printf allows number formatting but does not automatically append the \n to statements, see below
+using Printf
+@printf "volume = %0.3f\n" vol
+#> volume = 113.097
+
+quad1, quad2 = quadratic2(2.0, -2.0, -12.0)
+println("result 1: ", quad1)
+#> result 1: 3.0
+println("result 2: ", quad2)
+#> result 2: -2.0
+
+s1 = "The quick brown fox jumps over the lazy dog α,β,γ"
+
+# search returns the first index of a char
+i = findfirst(isequal('b'), s1)
+println(i)
+#> 11
+# the second argument is equivalent to the second argument of split, see below
+
+# or a range if called with another string
+r = findfirst("brown", s1)
+println(r)
+#> 11:15
+
+
+# string replace is done thus:
+r = replace(s1, "brown" => "red")
+show(r); println()
+#> "The quick red fox jumps over the lazy dog α,β,γ"
+
+# search and replace can also take a regular expressions by preceding the string with 'r'
+r = findfirst(r"b[\w]*n", s1)
+println(r)
+#> 11:15
+
+# again with a regular expression
+r = replace(s1, r"b[\w]*n" => "red")
+show(r); println()
+#> "The quick red fox jumps over the lazy dog α,β,γ"
+
+# there are also functions for regular expressions that return RegexMatch types
+# match scans left to right for the first match (specified starting index optional)
+r = match(r"b[\w]*n", s1)
+println(r)
+#> RegexMatch("brown")
+
+# RegexMatch types have a property match that holds the matched string
+show(r.match); println()
+#> "brown"
+
+# eachmatch returns an iterator over all the matches
+r = eachmatch(r"[\w]{4,}", s1)
+for i in r print("\"$(i.match)\" ") end
+#> "quick" "brown" "jumps" "over" "lazy"
+println()
+
+
+r = collect(m.match for m = eachmatch(r"[\w]{4,}", s1))
+println(r)
+#> SubString{String}["quick", "brown", "jumps", "over", "lazy"]
+
+# a string can be repeated using the repeat function,
+# or more succinctly with the ^ syntax:
+r = "hello "^3
+show(r); println() #> "hello hello hello "
+
+# the strip function works the same as python:
+# e.g., with one argument it strips the outer whitespace
+r = strip("hello ")
+show(r); println() #> "hello"
+# or with a second argument of an array of chars it strips any of them;
+r = strip("hello ", ['h', ' '])
+show(r); println() #> "ello"
+# (note the array is of chars and not strings)
+
+# similarly split works in basically the same way as python:
+r = split("hello, there,bob", ',')
+show(r); println() #> SubString{String}["hello", " there", "bob"]
+r = split("hello, there,bob", ", ")
+show(r); println() #> SubString{String}["hello", "there,bob"]
+r = split("hello, there,bob", [',', ' '], limit=0, keepempty=false)
+show(r); println() #> SubString{String}["hello", "there", "bob"]
+# (the last two arguements are limit and include_empty, see docs)
+
+# the opposite of split: join is simply
+r = join(collect(1:10), ", ")
+println(r) #> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
+
+function printsum(a)
+ # summary generates a summary of an object
+ println(summary(a), ": ", repr(a))
+end
+
+# arrays can be initialised directly:
+a1 = [1,2,3]
+printsum(a1)
+#> 3-element Array{Int64,1}: [1, 2, 3]
+
+# or initialised empty:
+a2 = []
+printsum(a2)
+#> 0-element Array{Any,1}: Any[]
+
+# since this array has no type, functions like push! (see below) don't work
+# instead arrays can be initialised with a type:
+a3 = Int64[]
+printsum(a3)
+#> 0-element Array{Int64,1}: Int64[]
+
+# ranges are different from arrays:
+a4 = 1:20
+printsum(a4)
+#> 20-element UnitRange{Int64}: 1:20
+
+# however they can be used to create arrays thus:
+a4 = collect(1:20)
+printsum(a4)
+#> 20-element Array{Int64,1}: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+#> 15, 16, 17, 18, 19, 20]
+
+# arrays can also be generated from comprehensions:
+a5 = [2^i for i = 1:10]
+printsum(a5)
+#> 10-element Array{Int64,1}: [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
+
+# arrays can be any type, so arrays of arrays can be created:
+a6 = (Array{Int64, 1})[]
+printsum(a6)
+#> 0-element Array{Array{Int64,1},1}: Array{Int64,1}[]
+# (note this is a "jagged array" (i.e., an array of arrays), not a multidimensional array,
+# these are not covered here)
+
+# Julia provided a number of "Dequeue" functions, the most common
+# for appending to the end of arrays is push!
+# ! at the end of a function name indicates that the first argument is updated.
+
+push!(a1, 4)
+printsum(a1)
+#> 4-element Array{Int64,1}: [1, 2, 3, 4]
+
+# push!(a2, 1) would cause error:
+
+push!(a3, 1)
+printsum(a3) #> 1-element Array{Int64,1}: [1]
+#> 1-element Array{Int64,1}: [1]
+
+push!(a6, [1,2,3])
+printsum(a6)
+#> 1-element Array{Array{Int64,1},1}: Array{Int64,1}[[1, 2, 3]]
+
+# using repeat() to create arrays
+# you must use the keywords "inner" and "outer"
+# all arguments must be arrays (not ranges)
+a7 = repeat(a1,inner=[2],outer=[1])
+printsum(a7)
+#> 8-element Array{Int64,1}: [1, 1, 2, 2, 3, 3, 4, 4]
+a8 = repeat(collect(4:-1:1),inner=[1],outer=[2])
+printsum(a8)
+#> 8-element Array{Int64,1}: [4, 3, 2, 1, 4, 3, 2, 1]
+
+# try, catch can be used to deal with errors as with many other languages
+try
+ push!(a,1)
+catch err
+ showerror(stdout, err, backtrace());println()
+end
+#> UndefVarError: a not defined
+#> Stacktrace:
+#> [1] top-level scope at C:\JuliaByExample\src\error_handling.jl:5
+#> [2] include at .\boot.jl:317 [inlined]
+#> [3] include_relative(::Module, ::String) at .\loading.jl:1038
+#> [4] include(::Module, ::String) at .\sysimg.jl:29
+#> [5] exec_options(::Base.JLOptions) at .\client.jl:229
+#> [6] _start() at .\client.jl:421
+println("Continuing after error")
+#> Continuing after error
+
+# repeat can be useful to expand a grid
+# as in R's expand.grid() function:
+
+m1 = hcat(repeat([1,2],inner=[1],outer=[3*2]),
+ repeat([1,2,3],inner=[2],outer=[2]),
+ repeat([1,2,3,4],inner=[3],outer=[1]))
+printsum(m1)
+#> 12×3 Array{Int64,2}: [1 1 1; 2 1 1; 1 2 1; 2 2 2; 1 3 2; 2 3 2; 1 1 3; 2 1 3;
+#> 1 2 3; 2 2 4; 1 3 4; 2 3 4]
+
+# for simple repetitions of arrays,
+# use repeat
+m2 = repeat(m1,1,2) # replicate a9 once into dim1 and twice into dim2
+println("size: ", size(m2))
+#> size: (12, 6)
+
+m3 = repeat(m1,2,1) # replicate a9 twice into dim1 and once into dim2
+println("size: ", size(m3))
+#> size: (24, 3)
+
+# Julia comprehensions are another way to easily create
+# multidimensional arrays
+
+m4 = [i+j+k for i=1:2, j=1:3, k=1:2] # creates a 2x3x2 array of Int64
+m5 = ["Hi Im # $(i+2*(j-1 + 3*(k-1)))" for i=1:2, j=1:3, k=1:2]
+# expressions are very flexible
+# you can specify the type of the array by just
+# placing it in front of the expression
+
+import LegacyStrings
+m5 = LegacyStrings.ASCIIString["Hi Im element # $(i+2*(j-1 + 3*(k-1)))" for i=1:2, j=1:3, k=1:2]
+printsum(m5)
+#> 2×3×2 Array{LegacyStrings.ASCIIString,3}: LegacyStrings.ASCIIString[
+#> "Hi Im element # 1" "Hi Im element # 3" "Hi Im element # 5";
+#> "Hi Im element # 2" "Hi Im element # 4" "Hi Im element # 6"]
+#>
+#> LegacyStrings.ASCIIString["Hi Im element # 7" "Hi Im element # 9"
+#> "Hi Im element # 11"; "Hi Im element # 8" "Hi Im element # 10" "Hi Im element # 12"]
+
+# Array reductions
+# many functions in Julia have an array method
+# to be applied to specific dimensions of an array:
+
+sum(m4, dims=3) # takes the sum over the third dimension
+sum(m4, dims=(1,3)) # sum over first and third dim
+
+maximum(m4, dims=2) # find the max elt along dim 2
+findmax(m4, dims=3) # find the max elt and its index along dim 3
+ # (available only in very recent Julia versions)
+
+# Broadcasting
+# when you combine arrays of different sizes in an operation,
+# an attempt is made to "spread" or "broadcast" the smaller array
+# so that the sizes match up. broadcast operators are preceded by a dot:
+
+m4 .+ 3 # add 3 to all elements
+m4 .+ [1,2] # adds vector [1,2] to all elements along first dim
+
+# slices and views
+m4=m4[:,:,1] # holds dim 3 fixed
+m4[:,2,:] # that's a 2x1x2 array. not very intuititive to look at
+
+# get rid of dimensions with size 1:
+dropdims(m4[:,2,:], dims=2) # that's better
+
+# assign new values to a certain view
+m4[:,:,1] = rand(1:6,2,3)
+printsum(m4)
+#> 2×3 Array{Int64,2}: [3 5 3; 1 3 5]
+
+# (for more examples of try, catch see Error Handling above)
+try
+ # this will cause an error, you have to assign the correct type
+ m4[:,:,1] = rand(2,3)
+catch err
+ println(err)
+end
+#> InexactError(:Int64, Int64, 0.7603891754678744)
+
+try
+ # this will cause an error, you have to assign the right shape
+ m4[:,:,1] = rand(1:6,3,2)
+catch err
+ println(err)
+end
+#> DimensionMismatch("tried to assign 3×2 array to 2×3×1 destination")
+
+# dicts can be initialised directly:
+a1 = Dict(1=>"one", 2=>"two")
+printsum(a1)
+#> Dict{Int64,String} with 2 entries: Dict(2=>"two",1=>"one")
+
+# then added to:
+a1[3]="three"
+printsum(a1)
+#> Dict{Int64,String} with 3 entries: Dict(2=>"two",3=>"three",1=>"one")
+# (note dicts cannot be assumed to keep their original order)
+
+# dicts may also be created with the type explicitly set
+a2 = Dict{Int64, AbstractString}()
+a2[0]="zero"
+printsum(a2)
+#> Dict{Int64,AbstractString} with 1 entry: Dict{Int64,AbstractString}(0=>"zero")
+
+# dicts, like arrays, may also be created from comprehensions
+using Printf
+a3 = Dict([i => @sprintf("%d", i) for i = 1:10])
+printsum(a3)
+#> Dict{Int64,String} with 10 entries: Dict(7=>"7",4=>"4",9=>"9",10=>"10",
+#> 2=>"2",3=>"3",5=>"5",8=>"8",6=>"6",1=>"1")
+
+# as you would expect, Julia comes with all the normal helper functions
+# for dicts, e.g., haskey
+println(haskey(a1,1)) #> true
+
+# which is equivalent to
+println(1 in keys(a1)) #> true
+# where keys creates an iterator over the keys of the dictionary
+
+# similar to keys, values get iterators over the dict's values:
+printsum(values(a1))
+#> Base.ValueIterator for a Dict{Int64,String} with 3 entries: ["two", "three", "one"]
+
+# use collect to get an array:
+printsum(collect(values(a1)))
+#> 3-element Array{String,1}: ["two", "three", "one"]
+
+if true
+ println("It's true!")
+else
+ println("It's false!")
+end
+#> It's true!
+
+if false
+ println("It's true!")
+else
+ println("It's false!")
+end
+#> It's false!
+
+# Numbers can be compared with opperators like <, >, ==, !=
+
+1 == 1.
+#> true
+
+1 > 2
+#> false
+
+"foo" != "bar"
+#> true
+
+# and many functions return boolean values
+
+occursin("that", "this and that")
+#> true
+
+# More complex logical statments can be achieved with `elseif`
+
+function checktype(x)
+ if x isa Int
+ println("Look! An Int!")
+ elseif x isa AbstractFloat
+ println("Look! A Float!")
+ elseif x isa Complex
+ println("Whoa, that's complex!")
+ else
+ println("I have no idea what that is")
+ end
+end
+
+checktype(2)
+#> Look! An Int!
+
+checktype(√2)
+#> Look! A Float!
+
+checktype(√Complex(-2))
+#> Whoa, that's complex!
+
+checktype("who am I?")
+#> I have no idea what that is
+
+# For simple logical statements, one can be more terse using the "ternary operator",
+# which takes the form `predicate ? do_if_true : do_if_false`
+
+1 > 2 ? println("that's true!") : println("that's false!")
+#> that's false!
+
+noisy_sqrt(x) = x ≥ 0 ? sqrt(x) : "That's negative!"
+noisy_sqrt(4)
+#> 2.0
+noisy_sqrt(-4)
+#> That's negative!
+
+# "Short-circuit evaluation" offers another option for conditional statements.
+# The opperators `&&` for AND and `||` for OR only evaluate the right-hand
+# statement if necessary based on the predicate.
+# Logically, if I want to know if `42 == 0 AND x < y`,
+# it doesn't matter what `x` and `y` are, since the first statement is false.
+# This can be exploited to only evaluate a statement if something is true -
+# the second statement doesn't even have to be boolean!
+
+everything = 42
+everything < 100 && println("that's true!")
+#> "that's true!"
+everything == 0 && println("that's true!")
+#> false
+
+√everything > 0 || println("that's false!")
+#> true
+√everything == everything || println("that's false!")
+#> that's false!
+