Update Scintilla from 5.2.1 to 5.2.2 and Lexilla from 5.1.5 to 5.1.6

Close #11537
This commit is contained in:
Christian Grasser 2022-04-13 13:10:12 +02:00 committed by Don Ho
parent 3b0d5242ac
commit 100d45f7cf
62 changed files with 1077 additions and 383 deletions

View File

@ -19,6 +19,7 @@
**.def text
**.manifest text
**.properties text
**.session text
**.styled text
**.folded text
**.adoc text

View File

@ -55,7 +55,7 @@ std::wstring WideStringFromUTF8(std::string_view sv) {
const int sLength = static_cast<int>(sv.length());
const int cchWide = ::MultiByteToWideChar(CP_UTF8, 0, sv.data(), sLength, nullptr, 0);
std::wstring sWide(cchWide, 0);
::MultiByteToWideChar(CP_UTF8, 0, sv.data(), sLength, &sWide[0], cchWide);
::MultiByteToWideChar(CP_UTF8, 0, sv.data(), sLength, sWide.data(), cchWide);
return sWide;
}

View File

@ -9,7 +9,7 @@
<meta name="keywords" content="Scintilla, SciTE, Editing Component, Text Editor" />
<meta name="Description"
content="www.scintilla.org is the home of the Scintilla editing component and SciTE text editor application." />
<meta name="Date.Modified" content="20220209" />
<meta name="Date.Modified" content="20220331" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
.logo {
@ -61,8 +61,8 @@
<font color="#FFCC99" size="4"> A library of language lexers for use with Scintilla</font>
</td>
<td width="40%" align="right">
<font color="#FFCC99" size="3">Release version 5.1.5<br />
Site last modified February 9 2022</font>
<font color="#FFCC99" size="3">Release version 5.1.6<br />
Site last modified March 31 2022</font>
</td>
<td width="20%">
&nbsp;
@ -77,6 +77,7 @@
</tr>
</table>
<ul id="versionlist">
<li>Version 5.1.6 improves Markdown and Ruby.</li>
<li>Version 5.1.5 improves Bash, Batch, F#, HTML, Inno Setup, and Python.</li>
<li>Version 5.1.4 adds lexers for AsciiDoc and GDScript.</li>
<li>Version 5.1.3 improves Rust.</li>

View File

@ -26,9 +26,9 @@
<table bgcolor="#CCCCCC" width="100%" cellspacing="0" cellpadding="8" border="0">
<tr>
<td>
<font size="4"> <a href="https://www.scintilla.org/lexilla515.zip">
<font size="4"> <a href="https://www.scintilla.org/lexilla516.zip">
Windows</a>&nbsp;&nbsp;
<a href="https://www.scintilla.org/lexilla515.tgz">
<a href="https://www.scintilla.org/lexilla516.tgz">
GTK/Linux</a>&nbsp;&nbsp;
</font>
</td>
@ -42,7 +42,7 @@
containing very few restrictions.
</p>
<h3>
Release 5.1.5
Release 5.1.6
</h3>
<h4>
Source Code
@ -50,8 +50,8 @@
The source code package contains all of the source code for Lexilla but no binary
executable code and is available in
<ul>
<li><a href="https://www.scintilla.org/lexilla515.zip">zip format</a> (1.1M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/lexilla515.tgz">tgz format</a> (0.9M) commonly used on Linux and compatible operating systems</li>
<li><a href="https://www.scintilla.org/lexilla516.zip">zip format</a> (1.1M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/lexilla516.tgz">tgz format</a> (0.9M) commonly used on Linux and compatible operating systems</li>
</ul>
Instructions for building on both Windows and Linux are included in the readme file.
<h4>

View File

@ -577,6 +577,7 @@
<td>Arkadiusz Michalski</td>
</tr><tr>
<td>Red_M</td>
<td>cdbdev</td>
</tr>
</table>
<h2>Releases</h2>
@ -585,13 +586,49 @@
</h3>
<ul>
<li>
Released 9 February 2022.
Released 31 March 2022.
</li>
<li>
Implement conditional statements "if" and "match", comparison function "$(=", and "FileNameExt"
property in TestLexers to allow varying lexer properties over different files.
<a href="https://github.com/ScintillaOrg/lexilla/issues/62">Issue #62</a>.
</li>
<li>
Add LexAccessor::BufferStyleAt to retrieve style values to simplify logic and
improve performance.
<a href="https://github.com/ScintillaOrg/lexilla/issues/54">Issue #54</a>.
</li>
<li>
Markdown: Optionally style all of Markdown header lines.
Enabled with lexer.markdown.header.eolfill=1.
<a href="https://github.com/ScintillaOrg/lexilla/issues/60">Issue #60</a>.
</li>
<li>
Ruby: Fix operator method styling so next word not treated as method name.
<a href="https://github.com/ScintillaOrg/lexilla/issues/65">Issue #65</a>.
</li>
<li>
Ruby: Fix folding for Ruby 3 endless method definition.
<a href="https://github.com/ScintillaOrg/lexilla/issues/65">Issue #65</a>.
</li>
<li>
Ruby: Fold string array SCE_RB_STRING_QW.
<a href="https://github.com/ScintillaOrg/lexilla/issues/65">Issue #65</a>.
</li>
<li>
Ruby: Fix final \n in indented heredoc to be SCE_RB_HERE_Q.
<a href="https://github.com/ScintillaOrg/lexilla/issues/66">Issue #66</a>.
</li>
<li>
Ruby: Fix heredoc recognition when '.' and ',' used in method calls and after SCE_RB_GLOBAL.
Classify word after heredoc delimiter instead of styling as keyword.
<a href="https://github.com/ScintillaOrg/lexilla/issues/67">Issue #67</a>.
</li>
<li>
Ruby: Improve method highlighting so method name is styled as SCE_RB_DEFNAME and class/object
is styled appropriately.
<a href="https://github.com/ScintillaOrg/lexilla/issues/68">Issue #68</a>.
</li>
</ul>
<h3>
<a href="https://www.scintilla.org/lexilla515.zip">Release 5.1.5</a>

View File

@ -66,6 +66,7 @@ constexpr bool IsNewline(const int ch) {
}
// True if can follow ch down to the end with possibly trailing whitespace
// Does not set the state SCE_MARKDOWN_LINE_BEGIN as to allow further processing
static bool FollowToLineEnd(const int ch, const int state, const Sci_PositionU endPos, StyleContext &sc) {
Sci_Position i = 0;
while (sc.GetRelative(++i) == ch)
@ -74,9 +75,8 @@ static bool FollowToLineEnd(const int ch, const int state, const Sci_PositionU e
while (IsASpaceOrTab(sc.GetRelative(i)) && sc.currentPos + i < endPos)
++i;
if (IsNewline(sc.GetRelative(i)) || sc.currentPos + i == endPos) {
sc.SetState(state);
sc.Forward(i);
sc.ChangeState(state);
sc.SetState(SCE_MARKDOWN_LINE_BEGIN);
return true;
}
else return false;
@ -126,15 +126,15 @@ static bool AtTermStart(StyleContext &sc) {
static bool IsCompleteStyleRegion(StyleContext &sc, const char *token) {
bool found = false;
const size_t start = strlen(token);
const size_t start = strlen(token);
Sci_Position i = static_cast<Sci_Position>(start);
while (!IsNewline(sc.GetRelative(i))) {
// make sure an empty pair of single-char tokens doesn't match
// with a longer token: {*}{*} != {**}
if (sc.GetRelative(i) == *token && sc.GetRelative(i - 1) != *token) {
found = start > 1U ? sc.GetRelative(i + 1) == token[1] : true;
break;
}
if (sc.GetRelative(i) == *token && sc.GetRelative(i - 1) != *token) {
found = start > 1U ? sc.GetRelative(i + 1) == token[1] : true;
break;
}
i++;
}
return AtTermStart(sc) && found;
@ -160,14 +160,14 @@ static bool IsValidHrule(const Sci_PositionU endPos, StyleContext &sc) {
}
else {
sc.SetState(SCE_MARKDOWN_DEFAULT);
return false;
return false;
}
}
}
}
static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
WordList **, Accessor &styler) {
WordList **, Accessor &styler) {
Sci_PositionU endPos = startPos + length;
int precharCount = 0;
bool isLinkNameDetecting = false;
@ -176,6 +176,10 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int
// in the default state.
bool freezeCursor = false;
// property lexer.markdown.header.eolfill
// Set to 1 to highlight all ATX header text.
bool headerEOLFill = styler.GetPropertyInt("lexer.markdown.header.eolfill", 0) == 1;
StyleContext sc(startPos, static_cast<Sci_PositionU>(length), initStyle, styler);
while (sc.More()) {
@ -265,22 +269,45 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int
}
else if (sc.state == SCE_MARKDOWN_LINE_BEGIN) {
// Header
if (sc.Match("######"))
SetStateAndZoom(SCE_MARKDOWN_HEADER6, 6, '#', sc);
else if (sc.Match("#####"))
SetStateAndZoom(SCE_MARKDOWN_HEADER5, 5, '#', sc);
else if (sc.Match("####"))
SetStateAndZoom(SCE_MARKDOWN_HEADER4, 4, '#', sc);
else if (sc.Match("###"))
SetStateAndZoom(SCE_MARKDOWN_HEADER3, 3, '#', sc);
else if (sc.Match("##"))
SetStateAndZoom(SCE_MARKDOWN_HEADER2, 2, '#', sc);
if (sc.Match("######")) {
if (headerEOLFill)
sc.SetState(SCE_MARKDOWN_HEADER6);
else
SetStateAndZoom(SCE_MARKDOWN_HEADER6, 6, '#', sc);
}
else if (sc.Match("#####")) {
if (headerEOLFill)
sc.SetState(SCE_MARKDOWN_HEADER5);
else
SetStateAndZoom(SCE_MARKDOWN_HEADER5, 5, '#', sc);
}
else if (sc.Match("####")) {
if (headerEOLFill)
sc.SetState(SCE_MARKDOWN_HEADER4);
else
SetStateAndZoom(SCE_MARKDOWN_HEADER4, 4, '#', sc);
}
else if (sc.Match("###")) {
if (headerEOLFill)
sc.SetState(SCE_MARKDOWN_HEADER3);
else
SetStateAndZoom(SCE_MARKDOWN_HEADER3, 3, '#', sc);
}
else if (sc.Match("##")) {
if (headerEOLFill)
sc.SetState(SCE_MARKDOWN_HEADER2);
else
SetStateAndZoom(SCE_MARKDOWN_HEADER2, 2, '#', sc);
}
else if (sc.Match("#")) {
// Catch the special case of an unordered list
if (sc.chNext == '.' && IsASpaceOrTab(sc.GetRelative(2))) {
precharCount = 0;
sc.SetState(SCE_MARKDOWN_PRECHAR);
}
else if (headerEOLFill) {
sc.SetState(SCE_MARKDOWN_HEADER1);
}
else
SetStateAndZoom(SCE_MARKDOWN_HEADER1, 1, '#', sc);
}
@ -292,14 +319,18 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int
sc.SetState(SCE_MARKDOWN_DEFAULT);
}
else if (sc.ch == '=') {
if (HasPrevLineContent(sc) && FollowToLineEnd('=', SCE_MARKDOWN_HEADER1, endPos, sc))
;
if (HasPrevLineContent(sc) && FollowToLineEnd('=', SCE_MARKDOWN_HEADER1, endPos, sc)) {
if (!headerEOLFill)
sc.SetState(SCE_MARKDOWN_LINE_BEGIN);
}
else
sc.SetState(SCE_MARKDOWN_DEFAULT);
}
else if (sc.ch == '-') {
if (HasPrevLineContent(sc) && FollowToLineEnd('-', SCE_MARKDOWN_HEADER2, endPos, sc))
;
if (HasPrevLineContent(sc) && FollowToLineEnd('-', SCE_MARKDOWN_HEADER2, endPos, sc)) {
if (!headerEOLFill)
sc.SetState(SCE_MARKDOWN_LINE_BEGIN);
}
else {
precharCount = 0;
sc.SetState(SCE_MARKDOWN_PRECHAR);
@ -315,9 +346,15 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int
// The header lasts until the newline
else if (sc.state == SCE_MARKDOWN_HEADER1 || sc.state == SCE_MARKDOWN_HEADER2 ||
sc.state == SCE_MARKDOWN_HEADER3 || sc.state == SCE_MARKDOWN_HEADER4 ||
sc.state == SCE_MARKDOWN_HEADER5 || sc.state == SCE_MARKDOWN_HEADER6) {
if (IsNewline(sc.ch))
sc.state == SCE_MARKDOWN_HEADER3 || sc.state == SCE_MARKDOWN_HEADER4 ||
sc.state == SCE_MARKDOWN_HEADER5 || sc.state == SCE_MARKDOWN_HEADER6) {
if (headerEOLFill) {
if (sc.atLineStart) {
sc.SetState(SCE_MARKDOWN_LINE_BEGIN);
freezeCursor = true;
}
}
else if (IsNewline(sc.ch))
sc.SetState(SCE_MARKDOWN_LINE_BEGIN);
}
@ -367,21 +404,21 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int
// Any link
if (sc.state == SCE_MARKDOWN_LINK) {
if (sc.Match("](") && sc.GetRelative(-1) != '\\') {
sc.Forward(2);
isLinkNameDetecting = true;
sc.Forward(2);
isLinkNameDetecting = true;
}
else if (sc.Match("]:") && sc.GetRelative(-1) != '\\') {
sc.Forward(2);
sc.SetState(SCE_MARKDOWN_DEFAULT);
sc.Forward(2);
sc.SetState(SCE_MARKDOWN_DEFAULT);
}
else if (!isLinkNameDetecting && sc.ch == ']' && sc.GetRelative(-1) != '\\') {
sc.Forward();
sc.SetState(SCE_MARKDOWN_DEFAULT);
sc.Forward();
sc.SetState(SCE_MARKDOWN_DEFAULT);
}
else if (isLinkNameDetecting && sc.ch == ')' && sc.GetRelative(-1) != '\\') {
sc.Forward();
sc.SetState(SCE_MARKDOWN_DEFAULT);
isLinkNameDetecting = false;
sc.Forward();
sc.SetState(SCE_MARKDOWN_DEFAULT);
isLinkNameDetecting = false;
}
}
@ -393,11 +430,11 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int
}
// Links and Images
if (sc.Match("![")) {
sc.SetState(SCE_MARKDOWN_LINK);
sc.Forward(1);
sc.SetState(SCE_MARKDOWN_LINK);
sc.Forward(1);
}
else if (sc.ch == '[' && sc.GetRelative(-1) != '\\') {
sc.SetState(SCE_MARKDOWN_LINK);
sc.SetState(SCE_MARKDOWN_LINK);
}
// Code - also a special case for alternate inside spacing
else if (sc.Match("``") && sc.GetRelative(3) != ' ' && AtTermStart(sc)) {
@ -412,7 +449,7 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int
else if (sc.Match("**") && sc.GetRelative(2) != ' ' && IsCompleteStyleRegion(sc, "**")) {
sc.SetState(SCE_MARKDOWN_STRONG1);
sc.Forward();
}
}
else if (sc.Match("__") && sc.GetRelative(2) != ' ' && IsCompleteStyleRegion(sc, "__")) {
sc.SetState(SCE_MARKDOWN_STRONG2);
sc.Forward();
@ -420,12 +457,13 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int
// Emphasis
else if (sc.ch == '*' && sc.chNext != ' ' && IsCompleteStyleRegion(sc, "*")) {
sc.SetState(SCE_MARKDOWN_EM1);
} else if (sc.ch == '_' && sc.chNext != ' ' && IsCompleteStyleRegion(sc, "_")) {
}
else if (sc.ch == '_' && sc.chNext != ' ' && IsCompleteStyleRegion(sc, "_")) {
sc.SetState(SCE_MARKDOWN_EM2);
}
// Strikeout
else if (sc.Match("~~") && !(sc.GetRelative(2) == '~' || sc.GetRelative(2) == ' ') &&
IsCompleteStyleRegion(sc, "~~")) {
IsCompleteStyleRegion(sc, "~~")) {
sc.SetState(SCE_MARKDOWN_STRIKEOUT);
sc.Forward();
}

View File

@ -102,7 +102,7 @@ static bool keywordIsModifier(const char *word,
Sci_Position pos,
Accessor &styler);
static int ClassifyWordRb(Sci_PositionU start, Sci_PositionU end, WordList &keywords, Accessor &styler, char *prevWord) {
static int ClassifyWordRb(Sci_PositionU start, Sci_PositionU end, char ch, WordList &keywords, Accessor &styler, char *prevWord) {
char s[MAX_KEYWORD_LENGTH];
Sci_PositionU i, j;
Sci_PositionU lim = end - start + 1; // num chars to copy
@ -113,13 +113,22 @@ static int ClassifyWordRb(Sci_PositionU start, Sci_PositionU end, WordList &keyw
s[j] = styler[i];
}
s[j] = '\0';
int chAttr;
int chAttr = SCE_RB_IDENTIFIER;
int style = SCE_RB_DEFAULT;
if (0 == strcmp(prevWord, "class"))
chAttr = SCE_RB_CLASSNAME;
else if (0 == strcmp(prevWord, "module"))
chAttr = SCE_RB_MODULE_NAME;
else if (0 == strcmp(prevWord, "def"))
else if (0 == strcmp(prevWord, "def")) {
chAttr = SCE_RB_DEFNAME;
if (ch == '.') {
if (strcmp(s, "self") == 0) {
style = SCE_RB_WORD_DEMOTED;
} else {
style = SCE_RB_IDENTIFIER;
}
}
}
else if (keywords.InList(s) && ((start == 0) || !followsDot(start - 1, styler))) {
if (keywordIsAmbiguous(s)
&& keywordIsModifier(s, start, styler)) {
@ -136,15 +145,15 @@ static int ClassifyWordRb(Sci_PositionU start, Sci_PositionU end, WordList &keyw
chAttr = SCE_RB_WORD_DEMOTED;
} else {
chAttr = SCE_RB_WORD;
style = SCE_RB_WORD;
strcpy(prevWord, s);
}
} else
chAttr = SCE_RB_IDENTIFIER;
styler.ColourTo(end, chAttr);
if (chAttr == SCE_RB_WORD) {
strcpy(prevWord, s);
} else {
}
if (style == SCE_RB_DEFAULT) {
style = chAttr;
prevWord[0] = 0;
}
styler.ColourTo(end, style);
return chAttr;
}
@ -461,7 +470,6 @@ static Sci_Position findExpressionStart(Sci_Position pos,
static bool sureThisIsNotHeredoc(Sci_Position lt2StartPos,
Accessor &styler) {
int prevStyle;
// Use full document, not just part we're styling
Sci_Position lengthDoc = styler.Length();
Sci_Position lineStart = styler.GetLine(lt2StartPos);
@ -478,9 +486,10 @@ static bool sureThisIsNotHeredoc(Sci_Position lt2StartPos,
if (firstWordPosn >= lt2StartPos) {
return definitely_not_a_here_doc;
}
prevStyle = styler.StyleAt(firstWordPosn);
int prevStyle = styler.StyleAt(firstWordPosn);
// If we have '<<' following a keyword, it's not a heredoc
if (prevStyle != SCE_RB_IDENTIFIER
&& prevStyle != SCE_RB_GLOBAL // $stdout and $stderr
&& prevStyle != SCE_RB_SYMBOL
&& prevStyle != SCE_RB_INSTANCE_VAR
&& prevStyle != SCE_RB_CLASS_VAR) {
@ -595,7 +604,7 @@ static bool sureThisIsNotHeredoc(Sci_Position lt2StartPos,
return definitely_not_a_here_doc;
} else {
char ch = styler[j];
if (ch == '#' || isEOLChar(ch)) {
if (ch == '#' || isEOLChar(ch) || ch == '.' || ch == ',') {
// This is OK, so break and continue;
break;
} else {
@ -714,6 +723,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
false);
bool preferRE = true;
bool afterDef = false;
int state = initStyle;
Sci_Position lengthDoc = startPos + length;
@ -791,7 +801,12 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
if (HereDoc.State == 1 && isEOLChar(ch)) {
// Begin of here-doc (the line after the here-doc delimiter):
HereDoc.State = 2;
styler.ColourTo(i-1, state);
if (state == SCE_RB_WORD) {
const Sci_Position wordStartPos = styler.GetStartSegment();
ClassifyWordRb(wordStartPos, i - 1, ch, keywords, styler, prevWord);
} else {
styler.ColourTo(i - 1, state);
}
// Don't check for a missing quote, just jump into
// the here-doc state
state = SCE_RB_HERE_Q;
@ -862,7 +877,10 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
Quote.New();
Quote.Open(ch);
} else if (ch == '<' && chNext == '<' && chNext2 != '=') {
if (afterDef) {
afterDef = false;
prevWord[0] = 0;
}
// Recognise the '<<' symbol - either a here document or a binary op
styler.ColourTo(i - 1, state);
i++;
@ -893,6 +911,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
}
preferRE = (state != SCE_RB_HERE_DELIM);
} else if (ch == ':') {
afterDef = false;
styler.ColourTo(i - 1, state);
if (chNext == ':') {
// Mark "::" as an operator, not symbol start
@ -1009,7 +1028,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
state = SCE_RB_DEFAULT;
preferRE = true;
}
} else if (ch == '%') {
} else if (ch == '%' && !afterDef) {
styler.ColourTo(i - 1, state);
bool have_string = false;
if (strchr(q_chars, chNext) && !isSafeWordcharOrHigh(chNext2)) {
@ -1046,6 +1065,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
preferRE = true;
}
} else if (ch == '?') {
afterDef = false;
styler.ColourTo(i - 1, state);
if (iswhitespace(chNext) || chNext == '\n' || chNext == '\r') {
styler.ColourTo(i, SCE_RB_OPERATOR);
@ -1057,6 +1077,16 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
}
} else if (isoperator(ch) || ch == '.') {
styler.ColourTo(i - 1, state);
if (afterDef && ch != '.') {
afterDef = false;
prevWord[0] = 0;
if (chNext == '@' && (ch == '+' || ch == '-' || ch == '!')) {
// unary operator method
ch = chNext;
chNext = chNext2;
i += 1;
}
}
styler.ColourTo(i, SCE_RB_OPERATOR);
// If we're ending an expression or block,
// assume it ends an object, and the ambivalent
@ -1082,6 +1112,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
}
// Stay in default state
} else if (isEOLChar(ch)) {
afterDef = false;
// Make sure it's a true line-end, with no backslash
if ((ch == '\r' || (ch == '\n' && chPrev != '\r'))
&& chPrev != '\\') {
@ -1089,6 +1120,9 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
preferRE = true;
}
}
if (afterDef && state != SCE_RB_DEFAULT) {
afterDef = false;
}
} else if (state == SCE_RB_WORD) {
if (ch == '.' || !isSafeWordcharOrHigh(ch)) {
// Words include x? in all contexts,
@ -1127,9 +1161,10 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
preferRE = false;
} else {
Sci_Position wordStartPos = styler.GetStartSegment();
int word_style = ClassifyWordRb(wordStartPos, i - 1, keywords, styler, prevWord);
int word_style = ClassifyWordRb(wordStartPos, i - 1, ch, keywords, styler, prevWord);
switch (word_style) {
case SCE_RB_WORD:
afterDef = strcmp(prevWord, "def") == 0;
preferRE = RE_CanFollowKeyword(prevWord);
break;
@ -1152,6 +1187,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
if (ch == '.') {
// We might be redefining an operator-method
preferRE = false;
afterDef = word_style == SCE_RB_DEFNAME;
}
// And if it's the first
redo_char(i, ch, chNext, chNext2, state); // pass by ref
@ -1300,7 +1336,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
i - HereDoc.DelimiterLength + 1,
lengthDoc,
HereDoc.Delimiter)) {
styler.ColourTo(i - 1 - HereDoc.DelimiterLength, state);
styler.ColourTo(i - HereDoc.DelimiterLength, state);
styler.ColourTo(i, SCE_RB_HERE_DELIM);
state = SCE_RB_DEFAULT;
preferRE = false;
@ -1459,7 +1495,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init
if (state == SCE_RB_WORD) {
// We've ended on a word, possibly at EOF, and need to
// classify it.
(void) ClassifyWordRb(styler.GetStartSegment(), lengthDoc - 1, keywords, styler, prevWord);
(void) ClassifyWordRb(styler.GetStartSegment(), lengthDoc - 1, '\0', keywords, styler, prevWord);
} else {
styler.ColourTo(lengthDoc - 1, state);
}
@ -1754,10 +1790,21 @@ static void FoldRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle
& SC_FOLDLEVELNUMBERMASK
& ~SC_FOLDLEVELBASE);
int levelCurrent = levelPrev;
char chPrev = '\0';
char chNext = styler[startPos];
int styleNext = styler.StyleAt(startPos);
int stylePrev = startPos <= 1 ? SCE_RB_DEFAULT : styler.StyleAt(startPos - 1);
bool buffer_ends_with_eol = false;
// detect endless method definition to fix up code folding
enum class MethodDefinition {
None,
Define,
Operator,
Name,
Argument,
};
MethodDefinition method_definition = MethodDefinition::None;
int argument_paren_count = 0;
for (Sci_PositionU i = startPos; i < endPos; i++) {
char ch = chNext;
chNext = styler.SafeGetCharAt(i + 1);
@ -1800,8 +1847,10 @@ static void FoldRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle
// Don't decrement below 0
if (levelCurrent > 0)
levelCurrent--;
} else if (!strcmp(prevWord, "def")) {
levelCurrent++;
method_definition = MethodDefinition::Define;
} else if (!strcmp(prevWord, "if")
|| !strcmp(prevWord, "def")
|| !strcmp(prevWord, "class")
|| !strcmp(prevWord, "module")
|| !strcmp(prevWord, "begin")
@ -1820,8 +1869,64 @@ static void FoldRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle
} else if (styleNext == SCE_RB_DEFAULT) {
levelCurrent--;
}
} else if (style == SCE_RB_STRING_QW) {
if (stylePrev != style) {
levelCurrent++;
} else if (styleNext != style) {
levelCurrent--;
}
}
if (atEOL) {
if (method_definition != MethodDefinition::None) {
switch (method_definition) {
case MethodDefinition::Define:
if (style == SCE_RB_OPERATOR) {
method_definition = MethodDefinition::Operator;
} else if (style == SCE_RB_DEFNAME || style == SCE_RB_WORD_DEMOTED || style == SCE_RB_CLASSNAME || style == SCE_RB_IDENTIFIER) {
method_definition = MethodDefinition::Name;
} else if (!(style == SCE_RB_WORD || IsASpaceOrTab(ch))) {
method_definition = MethodDefinition::None;
}
if (method_definition <= MethodDefinition::Define) {
break;
}
// fall through for unary operator or single letter name
[[fallthrough]];
case MethodDefinition::Operator:
case MethodDefinition::Name:
if (isEOLChar(chNext) || chNext == '#') {
method_definition = MethodDefinition::None;
} else if (chNext == '(' || chNext <= ' ') {
// setter method cannot be defined in an endless method definition.
if (ch == '=' && (method_definition == MethodDefinition::Name || chPrev == ']')) {
method_definition = MethodDefinition::None;
} else {
method_definition = MethodDefinition::Argument;
argument_paren_count = 0;
}
}
break;
case MethodDefinition::Argument:
if (style == SCE_RB_OPERATOR) {
if (ch == '(') {
++argument_paren_count;
} else if (ch == ')') {
--argument_paren_count;
} else if (argument_paren_count == 0) {
method_definition = MethodDefinition::None;
if (ch == '=' && levelCurrent > 0) {
levelCurrent--;
}
}
} else if (argument_paren_count == 0 && !IsASpaceOrTab(ch)) {
// '=' must be first character after method name or right parenthesis
method_definition = MethodDefinition::None;
}
break;
default:
break;
}
}
if (atEOL || (i == endPos - 1)) {
int lev = levelPrev;
if (visibleChars == 0 && foldCompact)
lev |= SC_FOLDLEVELWHITEFLAG;
@ -1831,23 +1936,14 @@ static void FoldRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle
lineCurrent++;
levelPrev = levelCurrent;
visibleChars = 0;
buffer_ends_with_eol = true;
method_definition = MethodDefinition::None;
argument_paren_count = 0;
} else if (!isspacechar(ch)) {
visibleChars++;
buffer_ends_with_eol = false;
}
chPrev = ch;
stylePrev = style;
}
// Fill in the real level of the next line, keeping the current flags as they will be filled in later
if (!buffer_ends_with_eol) {
int new_lev = levelCurrent;
if (visibleChars == 0 && foldCompact)
new_lev |= SC_FOLDLEVELWHITEFLAG;
if ((levelCurrent > levelPrev) && (visibleChars > 0))
new_lev |= SC_FOLDLEVELHEADERFLAG;
levelCurrent = new_lev;
}
styler.SetLevel(lineCurrent, levelCurrent|SC_FOLDLEVELBASE);
}
static const char *const rubyWordListDesc[] = {

View File

@ -122,12 +122,12 @@ public:
}
// Return style value from buffer when in buffer, else retrieve from document.
// This is faster and can avoid calls to Flush() as that may be expensive.
char BufferStyleAt(Sci_Position position) const {
int BufferStyleAt(Sci_Position position) const {
const Sci_Position index = position - startPosStyling;
if (index >= 0 && index < validLen) {
return styleBuf[index];
return static_cast<unsigned char>(styleBuf[index]);
}
return pAccess->StyleAt(position);
return static_cast<unsigned char>(pAccess->StyleAt(position));
}
Sci_Position GetLine(Sci_Position position) const {
return pAccess->LineFromPosition(position);

View File

@ -6,6 +6,7 @@
// This file is in the public domain.
#include <cstdlib>
#include <cstdint>
#include <cassert>
#include <string>
@ -20,6 +21,33 @@
using namespace Lexilla;
StyleContext::StyleContext(Sci_PositionU startPos, Sci_PositionU length,
int initStyle, LexAccessor &styler_, char chMask) :
styler(styler_),
multiByteAccess((styler.Encoding() == EncodingType::eightBit) ? nullptr : styler.MultiByteAccess()),
lengthDocument(static_cast<Sci_PositionU>(styler.Length())),
endPos(((startPos + length) < lengthDocument) ? (startPos + length) : (lengthDocument+1)),
lineDocEnd(styler.GetLine(lengthDocument)),
currentPosLastRelative(SIZE_MAX),
currentPos(startPos),
currentLine(styler.GetLine(startPos)),
lineEnd(styler.LineEnd(currentLine)),
lineStartNext(styler.LineStart(currentLine + 1)),
atLineStart(static_cast<Sci_PositionU>(styler.LineStart(currentLine)) == startPos),
// Mask off all bits which aren't in the chMask.
state(initStyle &chMask) {
styler.StartAt(startPos /*, chMask*/);
styler.StartSegment(startPos);
// Variable width is now 0 so GetNextChar gets the char at currentPos into chNext/widthNext
GetNextChar();
ch = chNext;
width = widthNext;
GetNextChar();
}
bool StyleContext::MatchIgnoreCase(const char *s) {
if (MakeLowerCase(ch) != static_cast<unsigned char>(*s))
return false;
@ -54,19 +82,6 @@ bool StyleContext::MatchIgnoreCase2(const char *s) {
return true;
}
static void getRange(Sci_PositionU start,
Sci_PositionU end,
LexAccessor &styler,
char *s,
Sci_PositionU len) {
Sci_PositionU i = 0;
while ((i < end - start + 1) && (i < len-1)) {
s[i] = styler[start + i];
i++;
}
s[i] = '\0';
}
void StyleContext::GetCurrent(char *s, Sci_PositionU len) {
styler.GetRange(styler.GetStartSegment(), currentPos, s, len);
}

View File

@ -16,21 +16,21 @@ namespace Lexilla {
// syntactically significant. UTF-8 avoids this as all trail bytes are >= 0x80
class StyleContext {
LexAccessor &styler;
Scintilla::IDocument *multiByteAccess;
Sci_PositionU endPos;
Sci_PositionU lengthDocument;
Scintilla::IDocument * const multiByteAccess;
const Sci_PositionU lengthDocument;
const Sci_PositionU endPos;
const Sci_Position lineDocEnd;
// Used for optimizing GetRelativeCharacter
Sci_PositionU posRelative;
Sci_PositionU posRelative = 0;
Sci_PositionU currentPosLastRelative;
Sci_Position offsetRelative;
Sci_Position offsetRelative = 0;
void GetNextChar() {
if (multiByteAccess) {
chNext = multiByteAccess->GetCharacterAndWidth(currentPos+width, &widthNext);
} else {
chNext = static_cast<unsigned char>(styler.SafeGetCharAt(currentPos+width, 0));
widthNext = 1;
}
// End of line determined from line end position, allowing CR, LF,
// CRLF and Unicode line ends as set by document.
@ -43,59 +43,19 @@ class StyleContext {
public:
Sci_PositionU currentPos;
Sci_Position currentLine;
Sci_Position lineDocEnd;
Sci_Position lineEnd;
Sci_Position lineStartNext;
bool atLineStart;
bool atLineEnd;
bool atLineEnd = false;
int state;
int chPrev;
int ch;
Sci_Position width;
int chNext;
Sci_Position widthNext;
int chPrev = 0;
int ch = 0;
Sci_Position width = 0;
int chNext = 0;
Sci_Position widthNext = 1;
StyleContext(Sci_PositionU startPos, Sci_PositionU length,
int initStyle, LexAccessor &styler_, char chMask='\377') :
styler(styler_),
multiByteAccess(nullptr),
endPos(startPos + length),
posRelative(0),
currentPosLastRelative(0x7FFFFFFF),
offsetRelative(0),
currentPos(startPos),
currentLine(-1),
lineEnd(-1),
lineStartNext(-1),
atLineEnd(false),
state(initStyle & chMask), // Mask off all bits which aren't in the chMask.
chPrev(0),
ch(0),
width(0),
chNext(0),
widthNext(1) {
if (styler.Encoding() != EncodingType::eightBit) {
multiByteAccess = styler.MultiByteAccess();
}
styler.StartAt(startPos /*, chMask*/);
styler.StartSegment(startPos);
currentLine = styler.GetLine(startPos);
lineEnd = styler.LineEnd(currentLine);
lineStartNext = styler.LineStart(currentLine+1);
lengthDocument = static_cast<Sci_PositionU>(styler.Length());
if (endPos == lengthDocument)
endPos++;
lineDocEnd = styler.GetLine(lengthDocument);
atLineStart = static_cast<Sci_PositionU>(styler.LineStart(currentLine)) == startPos;
// Variable width is now 0 so GetNextChar gets the char at currentPos into chNext/widthNext
width = 0;
GetNextChar();
ch = chNext;
width = widthNext;
GetNextChar();
}
int initStyle, LexAccessor &styler_, char chMask = '\377');
// Deleted so StyleContext objects can not be copied.
StyleContext(const StyleContext &) = delete;
StyleContext &operator=(const StyleContext &) = delete;

View File

@ -26,6 +26,7 @@
// C++ wrappers of C standard library
#include <cstdlib>
#include <cstdint>
#include <cassert>
#include <cstring>
#include <cctype>

View File

@ -1,7 +1,7 @@
rem Test lexers
rem build lexilla.dll and TestLexers.exe then run TestLexers.exe
cd ../src
make DEBUG=1
make --jobs=4 DEBUG=1
cd ../test
make DEBUG=1
make test

View File

@ -1,7 +1,12 @@
# Test lexers
# build lexilla.so and TestLexers then run TestLexers
JOBS="--jobs=$(getconf _NPROCESSORS_ONLN)"
(
cd ../src
make DEBUG=1
make "$JOBS" DEBUG=1
)
(
cd ../test
make DEBUG=1
make test
)

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>5.1.5</string>
<string>5.1.6</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>

View File

@ -851,7 +851,7 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5.1.5;
CURRENT_PROJECT_VERSION = 5.1.6;
DEVELOPMENT_TEAM = 4F446KW87E;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@ -877,7 +877,7 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5.1.5;
CURRENT_PROJECT_VERSION = 5.1.6;
DEVELOPMENT_TEAM = 4F446KW87E;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -4,8 +4,8 @@
#include <windows.h>
#define VERSION_LEXILLA "5.1.5"
#define VERSION_WORDS 5, 1, 5, 0
#define VERSION_LEXILLA "5.1.6"
#define VERSION_WORDS 5, 1, 6, 0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_WORDS

View File

@ -100,8 +100,14 @@ Other settings are treated as lexer or folder properties and forwarded to the le
It is often necessary to set 'fold' in SciTE.properties to cause folding.
If there is a need to test additional configurations of keywords or properties then
create another subdirectory with the different settings in a new SciTE.properties.
Properties can be set for a particular file with an "if $(=" or "match" expression like so:
if $(= $(FileNameExt);HeaderEOLFill_1.md)
lexer.markdown.header.eolfill=1
match Header*1.md
lexer.markdown.header.eolfill=1
More complex tests with additional configurations of keywords or properties can be performed
by creating another subdirectory with the different settings in a new SciTE.properties.
There is some support for running benchmarks on lexers and folders. The properties
testlexers.repeat.lex and testlexers.repeat.fold specify the number of times example

View File

@ -40,6 +40,7 @@ namespace {
};
int UnicodeFromUTF8(const unsigned char *us) noexcept {
assert(us);
switch (UTF8BytesOfLead[us[0]]) {
case 1:
return us[0];
@ -56,6 +57,56 @@ namespace {
return (ch >= 0x80) && (ch < 0xc0);
}
constexpr unsigned char TrailByteValue(unsigned char c) {
// The top 2 bits are 0b10 to indicate a trail byte.
// The lower 6 bits contain the value.
return c & 0b0011'1111;
}
}
std::u32string UTF32FromUTF8(std::string_view svu8) {
std::u32string ret;
for (size_t i = 0; i < svu8.length();) {
unsigned char ch = svu8.at(i);
const unsigned int byteCount = UTF8BytesOfLead[ch];
unsigned int value = 0;
if (i + byteCount > svu8.length()) {
// Trying to read past end
ret.push_back(ch);
break;
}
i++;
switch (byteCount) {
case 1:
value = ch;
break;
case 2:
value = (ch & 0x1F) << 6;
ch = svu8.at(i++);
value += TrailByteValue(ch);
break;
case 3:
value = (ch & 0xF) << 12;
ch = svu8.at(i++);
value += TrailByteValue(ch) << 6;
ch = svu8.at(i++);
value += TrailByteValue(ch);
break;
default:
value = (ch & 0x7) << 18;
ch = svu8.at(i++);
value += TrailByteValue(ch) << 12;
ch = svu8.at(i++);
value += TrailByteValue(ch) << 6;
ch = svu8.at(i++);
value += TrailByteValue(ch);
break;
}
ret.push_back(value);
}
return ret;
}
void TestDocument::Set(std::string_view sv) {
@ -65,7 +116,7 @@ void TestDocument::Set(std::string_view sv) {
endStyled = 0;
lineStarts.push_back(0);
for (size_t pos = 0; pos < text.length(); pos++) {
if (text[pos] == '\n') {
if (text.at(pos) == '\n') {
lineStarts.push_back(pos + 1);
}
}
@ -145,15 +196,16 @@ void SCI_METHOD TestDocument::StartStyling(Sci_Position position) {
bool SCI_METHOD TestDocument::SetStyleFor(Sci_Position length, char style) {
for (Sci_Position i = 0; i < length; i++) {
textStyles[endStyled] = style;
textStyles.at(endStyled) = style;
endStyled++;
}
return true;
}
bool SCI_METHOD TestDocument::SetStyles(Sci_Position length, const char *styles) {
assert(styles);
for (Sci_Position i = 0; i < length; i++) {
textStyles[endStyled] = styles[i];
textStyles.at(endStyled) = styles[i];
endStyled++;
}
return true;
@ -252,8 +304,9 @@ int SCI_METHOD TestDocument::GetCharacterAndWidth(Sci_Position position, Sci_Pos
}
const int widthCharBytes = UTF8BytesOfLead[leadByte];
unsigned char charBytes[] = { leadByte,0,0,0 };
for (int b = 1; b < widthCharBytes; b++)
charBytes[b] = text[position + b];
for (int b = 1; b < widthCharBytes; b++) {
charBytes[b] = text.at(position + b);
}
if (pWidth) {
*pWidth = widthCharBytes;

View File

@ -8,6 +8,8 @@
#ifndef TESTDOCUMENT_H
#define TESTDOCUMENT_H
std::u32string UTF32FromUTF8(std::string_view svu8);
class TestDocument : public Scintilla::IDocument {
std::string text;
std::string textStyles;

View File

@ -29,9 +29,233 @@
namespace {
constexpr char MakeLowerCase(char c) noexcept {
if (c >= 'A' && c <= 'Z') {
return c - 'A' + 'a';
} else {
return c;
}
}
[[maybe_unused]] void LowerCaseAZ(std::string &s) {
std::transform(s.begin(), s.end(), s.begin(), MakeLowerCase);
}
int IntFromString(std::u32string_view s) noexcept {
if (s.empty()) {
return 0;
}
const bool negate = s.front() == '-';
if (negate) {
s.remove_prefix(1);
}
int value = 0;
while (!s.empty()) {
value = value * 10 + s.front() - '0';
s.remove_prefix(1);
}
return negate ? -value : value;
}
bool PatternMatch(std::u32string_view pattern, std::u32string_view text) noexcept {
if (pattern == text) {
return true;
} else if (pattern.empty()) {
return false;
} else if (pattern.front() == '\\') {
pattern.remove_prefix(1);
if (pattern.empty()) {
// Escape with nothing being escaped
return false;
}
if (text.empty()) {
return false;
}
if (pattern.front() == text.front()) {
pattern.remove_prefix(1);
text.remove_prefix(1);
return PatternMatch(pattern, text);
}
return false;
} else if (pattern.front() == '*') {
pattern.remove_prefix(1);
if (!pattern.empty() && pattern.front() == '*') {
pattern.remove_prefix(1);
// "**" matches anything including "/"
while (!text.empty()) {
if (PatternMatch(pattern, text)) {
return true;
}
text.remove_prefix(1);
}
} else {
while (!text.empty()) {
if (PatternMatch(pattern, text)) {
return true;
}
if (text.front() == '/') {
// "/" not matched by single "*"
return false;
}
text.remove_prefix(1);
}
}
assert(text.empty());
// Consumed whole text with wildcard so match if pattern consumed
return pattern.empty();
} else if (text.empty()) {
return false;
} else if (pattern.front() == '?') {
if (text.front() == '/') {
return false;
}
pattern.remove_prefix(1);
text.remove_prefix(1);
return PatternMatch(pattern, text);
} else if (pattern.front() == '[') {
pattern.remove_prefix(1);
if (pattern.empty()) {
return false;
}
const bool positive = pattern.front() != '!';
if (!positive) {
pattern.remove_prefix(1);
if (pattern.empty()) {
return false;
}
}
bool inSet = false;
if (!pattern.empty() && pattern.front() == ']') {
// First is allowed to be ']'
if (pattern.front() == text.front()) {
inSet = true;
}
pattern.remove_prefix(1);
}
char32_t start = 0;
while (!pattern.empty() && pattern.front() != ']') {
if (pattern.front() == '-') {
pattern.remove_prefix(1);
if (!pattern.empty()) {
const char32_t end = pattern.front();
if ((text.front() >= start) && (text.front() <= end)) {
inSet = true;
}
}
} else if (pattern.front() == text.front()) {
inSet = true;
}
if (!pattern.empty()) {
start = pattern.front();
pattern.remove_prefix(1);
}
}
if (!pattern.empty()) {
pattern.remove_prefix(1);
}
if (inSet != positive) {
return false;
}
text.remove_prefix(1);
return PatternMatch(pattern, text);
} else if (pattern.front() == '{') {
if (pattern.length() < 2) {
return false;
}
const size_t endParen = pattern.find('}');
if (endParen == std::u32string_view::npos) {
// Malformed {x} pattern
return false;
}
std::u32string_view parenExpression = pattern.substr(1, endParen - 1);
bool inSet = false;
const size_t dotdot = parenExpression.find(U"..");
if (dotdot != std::u32string_view::npos) {
// Numeric range: {10..20}
const std::u32string_view firstRange = parenExpression.substr(0, dotdot);
const std::u32string_view lastRange = parenExpression.substr(dotdot+2);
if (firstRange.empty() || lastRange.empty()) {
// Malformed {s..e} range pattern
return false;
}
const size_t endInteger = text.find_last_of(U"-0123456789");
if (endInteger == std::u32string_view::npos) {
// No integer in text
return false;
}
const std::u32string_view intPart = text.substr(0, endInteger+1);
const int first = IntFromString(firstRange);
const int last = IntFromString(lastRange);
const int value = IntFromString(intPart);
if ((value >= first) && (value <= last)) {
inSet = true;
text.remove_prefix(intPart.length());
}
} else {
// Alternates: {a,b,cd}
size_t comma = parenExpression.find(',');
for (;;) {
const bool finalAlt = comma == std::u32string_view::npos;
const std::u32string_view oneAlt = finalAlt ? parenExpression :
parenExpression.substr(0, comma);
if (oneAlt == text.substr(0, oneAlt.length())) {
// match
inSet = true;
text.remove_prefix(oneAlt.length());
break;
}
if (finalAlt) {
break;
}
parenExpression.remove_prefix(oneAlt.length() + 1);
comma = parenExpression.find(',');
}
}
if (!inSet) {
return false;
}
pattern.remove_prefix(endParen + 1);
return PatternMatch(pattern, text);
} else if (pattern.front() == text.front()) {
pattern.remove_prefix(1);
text.remove_prefix(1);
return PatternMatch(pattern, text);
}
return false;
}
bool PathMatch(std::string pattern, std::string relPath) {
#if defined(_WIN32)
// Convert Windows path separators to Unix
std::replace(relPath.begin(), relPath.end(), '\\', '/');
#endif
#if defined(_WIN32) || defined(__APPLE__)
// Case-insensitive, only does ASCII but fine for test example files
LowerCaseAZ(pattern);
LowerCaseAZ(relPath);
#endif
const std::u32string patternU32 = UTF32FromUTF8(pattern);
const std::u32string relPathU32 = UTF32FromUTF8(relPath);
if (PatternMatch(patternU32, relPathU32)) {
return true;
}
const size_t lastSlash = relPathU32.rfind('/');
if (lastSlash == std::string::npos) {
return false;
}
// Match against just filename
const std::u32string fileNameU32 = relPathU32.substr(lastSlash+1);
return PatternMatch(patternU32, fileNameU32);
}
constexpr std::string_view suffixStyled = ".styled";
constexpr std::string_view suffixFolded = ".folded";
constexpr std::string_view prefixIf = "if ";
constexpr std::string_view prefixMatch = "match ";
constexpr std::string_view prefixEqual = "= ";
constexpr std::string_view prefixComment = "#";
std::string ReadFile(std::filesystem::path path) {
std::ifstream ifs(path, std::ios::binary);
std::string content((std::istreambuf_iterator<char>(ifs)),
@ -40,6 +264,7 @@ std::string ReadFile(std::filesystem::path path) {
}
std::string MarkedDocument(const Scintilla::IDocument *pdoc) {
assert(pdoc);
std::ostringstream os(std::ios::binary);
char prevStyle = -1;
for (Sci_Position pos = 0; pos < pdoc->Length(); pos++) {
@ -71,6 +296,7 @@ void PrintLevel(std::ostringstream &os, int level) {
}
std::string FoldedDocument(const Scintilla::IDocument *pdoc) {
assert(pdoc);
std::ostringstream os(std::ios::binary);
Sci_Position linePrev = -1;
char ch = '\0';
@ -94,12 +320,115 @@ std::pair<std::string, std::string> MarkedAndFoldedDocument(const Scintilla::IDo
return { MarkedDocument(pdoc), FoldedDocument(pdoc) };
}
std::vector<std::string> StringSplit(const std::string_view &text, int separator) {
std::vector<std::string> vs(text.empty() ? 0 : 1);
for (std::string_view::const_iterator it = text.begin(); it != text.end(); ++it) {
if (*it == separator) {
vs.push_back(std::string());
} else {
vs.back() += *it;
}
}
return vs;
}
static constexpr bool IsSpaceOrTab(char ch) noexcept {
return (ch == ' ') || (ch == '\t');
}
class PropertyMap {
std::string Evaluate(std::string_view text) {
if (text.find(' ') != std::string_view::npos) {
if (text.starts_with(prefixEqual)) {
const std::string_view sExpressions = text.substr(prefixEqual.length());
std::vector<std::string> parts = StringSplit(sExpressions, ';');
if (parts.size() > 1) {
for (size_t part = 1; part < parts.size(); part++) {
if (parts.at(part) != parts.at(0)) {
return "0";
}
}
return "1";
}
}
return {};
} else {
std::optional<std::string> value = GetProperty(text);
if (value) {
return *value;
}
return {};
}
}
std::string Expand(std::string withVars) {
constexpr size_t maxVars = 100;
size_t varStart = withVars.rfind("$(");
for (size_t count = 0; (count < maxVars) && (varStart != std::string::npos); count++) {
const size_t varEnd = withVars.find(')', varStart + 2);
if (varEnd == std::string::npos) {
break;
}
const std::string_view whole = withVars;
const std::string_view var = whole.substr(varStart + 2, varEnd - (varStart + 2));
const std::string val = Evaluate(var);
withVars.erase(varStart, varEnd - varStart + 1);
withVars.insert(varStart, val);
varStart = withVars.rfind("$(");
}
return withVars;
}
bool ProcessLine(std::string_view text, bool ifIsTrue) {
// If clause ends with first non-indented line
if (!ifIsTrue && (text.empty() || IsSpaceOrTab(text.at(0)))) {
return false;
}
ifIsTrue = true;
if (text.starts_with(prefixIf)) {
const std::string value = Expand(std::string(text.substr(prefixIf.length())));
if (value == "0" || value == "") {
ifIsTrue = false;
}
} else if (text.starts_with(prefixMatch)) {
std::optional<std::string> fileNameExt = GetProperty("FileNameExt");
if (fileNameExt) {
std::string pattern(text.substr(prefixMatch.length()));
// Remove trailing white space
while (!pattern.empty() && IsSpaceOrTab(pattern.back())) {
pattern.pop_back();
}
ifIsTrue = PathMatch(pattern, *fileNameExt);
} else {
ifIsTrue = false;
}
} else {
while (!text.empty() && IsSpaceOrTab(text.at(0))) {
text.remove_prefix(1);
}
if (text.starts_with(prefixComment)) {
return ifIsTrue;
}
const size_t positionEquals = text.find("=");
if (positionEquals != std::string::npos) {
const std::string key(text.substr(0, positionEquals));
const std::string_view value = text.substr(positionEquals + 1);
properties[key] = value;
}
}
return ifIsTrue;
}
public:
using PropMap = std::map<std::string, std::string>;
PropMap properties;
void ReadFromFile(std::filesystem::path path) {
bool ifIsTrue = true;
std::ifstream ifs(path);
std::string line;
std::string logicalLine;
@ -112,12 +441,7 @@ public:
if (logicalLine.ends_with("\\")) {
logicalLine.pop_back();
} else {
const size_t positionEquals = logicalLine.find("=");
if (positionEquals != std::string::npos) {
const std::string key = logicalLine.substr(0, positionEquals);
const std::string value = logicalLine.substr(positionEquals + 1);
properties[key] = value;
}
ifIsTrue = ProcessLine(logicalLine, ifIsTrue);
logicalLine.clear();
}
}
@ -171,7 +495,8 @@ bool CheckSame(std::string_view augmentedText, std::string_view augmentedTextNew
}
const size_t lineNumber = FirstLineDifferent(augmentedText, augmentedTextNew) + 1;
std::cout << "\n" << path.string() << ":" << lineNumber << ":";
std::cout << " has different " << item << "\n\n";
const std::string differenceType = augmentedText.empty() ? "new" : "different";
std::cout << " has " << differenceType << " " << item << "\n\n";
std::filesystem::path pathNew = path;
pathNew += suffix;
pathNew += ".new";
@ -204,6 +529,7 @@ int UnixToWindows(std::string &s) {
const std::string BOM = "\xEF\xBB\xBF";
void StyleLineByLine(TestDocument &doc, Scintilla::ILexer5 *plex) {
assert(plex);
Scintilla::IDocument *pdoc = &doc;
const Sci_Position lines = doc.LineFromPosition(doc.Length());
Sci_Position startLine = 0;
@ -218,7 +544,8 @@ void StyleLineByLine(TestDocument &doc, Scintilla::ILexer5 *plex) {
}
}
void TestCRLF(std::filesystem::path path, const std::string s, Scintilla::ILexer5 *plex, bool disablePerLineTests) {
bool TestCRLF(std::filesystem::path path, const std::string s, Scintilla::ILexer5 *plex, bool disablePerLineTests) {
bool success = true;
// Convert all line ends to \r\n to check if styles change between \r and \n which makes
// it difficult to test on different platforms when files may have line ends changed.
std::string text = s;
@ -244,6 +571,7 @@ void TestCRLF(std::filesystem::path path, const std::string s, Scintilla::ILexer
std::cout << path.string() << ":" << line << ":" <<
" different styles between \\r and \\n at " <<
pos << ": " << prevStyle << ", " << styleNow << "\n";
success = false;
}
line++;
}
@ -265,9 +593,11 @@ void TestCRLF(std::filesystem::path path, const std::string s, Scintilla::ILexer
if (styledText != styledTextUnix) {
std::cout << "\n" << path.string() << ":1: has different styles with \\n versus \\r\\n line ends\n\n";
success = false;
}
if (foldedText != foldedTextUnix) {
std::cout << "\n" << path.string() << ":1: has different folds with \\n versus \\r\\n line ends\n\n";
success = false;
}
// Test line by line lexing/folding with Unix \n line ends
@ -277,14 +607,21 @@ void TestCRLF(std::filesystem::path path, const std::string s, Scintilla::ILexer
// Convert results from \n to \r\n run
UnixToWindows(styledTextNewPerLine);
UnixToWindows(foldedTextNewPerLine);
CheckSame(styledTextUnix, styledTextNewPerLine, "per-line styles \\n", suffixStyled, path);
CheckSame(foldedTextUnix, foldedTextNewPerLine, "per-line folds \\n", suffixFolded, path);
if (!CheckSame(styledTextUnix, styledTextNewPerLine, "per-line styles \\n", suffixStyled, path)) {
success = false;
}
if (!CheckSame(foldedTextUnix, foldedTextNewPerLine, "per-line folds \\n", suffixFolded, path)) {
success = false;
}
}
plex->Release();
return success;
}
void TestILexer(Scintilla::ILexer5 *plex) {
assert(plex);
// Test each method of the ILexer interface.
// Mostly ensures there are no crashes when calling methods.
// Some methods are tested later (Release, Lex, Fold).
@ -320,11 +657,11 @@ void TestILexer(Scintilla::ILexer5 *plex) {
[[maybe_unused]] const int lineEndTypes = plex->LineEndTypesSupported();
assert(lineEndTypes == 0 || lineEndTypes == 1);
if (const char *bases = plex->GetSubStyleBases()) {
if (std::string_view bases = plex->GetSubStyleBases(); !bases.empty()) {
// Allocate a substyle for each possible style
while (*bases) {
while (!bases.empty()) {
constexpr int newStyles = 3;
const int base = *bases;
const int base = bases.front();
const int baseStyle = plex->AllocateSubStyles(base, newStyles);
[[maybe_unused]] const int styleBack = plex->StyleFromSubStyle(baseStyle);
assert(styleBack == base);
@ -333,7 +670,7 @@ void TestILexer(Scintilla::ILexer5 *plex) {
assert(start == baseStyle);
[[maybe_unused]] const int len = plex->SubStylesLength(base);
assert(len == newStyles);
bases++;
bases.remove_prefix(1);
}
plex->FreeSubStyles();
}
@ -359,6 +696,8 @@ void TestILexer(Scintilla::ILexer5 *plex) {
}
void SetProperties(Scintilla::ILexer5 *plex, const PropertyMap &propertyMap, std::string_view fileName) {
assert(plex);
// Set keywords, keywords2, ... keywords9, for this file
for (int kw = 0; kw < 10; kw++) {
std::string kwChoice("keywords");
@ -374,9 +713,7 @@ void SetProperties(Scintilla::ILexer5 *plex, const PropertyMap &propertyMap, std
// Set parameters of lexer
for (auto const &[key, val] : propertyMap.properties) {
if (key.starts_with("#")) {
// Ignore comments
} else if (key.starts_with("lexer.*")) {
if (key.starts_with("lexer.*")) {
// Ignore as processed earlier
} else if (key.starts_with("keywords")) {
// Ignore as processed earlier
@ -455,16 +792,16 @@ bool TestFile(const std::filesystem::path &path, const PropertyMap &propertyMap)
plex->Release();
Scintilla::ILexer5 *plexCRLF = Lexilla::MakeLexer(*language);
SetProperties(plexCRLF, propertyMap, path.filename().string());
TestCRLF(path, text, plexCRLF, disablePerLineTests);
if (success) {
Scintilla::ILexer5 *plexCRLF = Lexilla::MakeLexer(*language);
SetProperties(plexCRLF, propertyMap, path.filename().string());
success = TestCRLF(path, text, plexCRLF, disablePerLineTests);
}
return success;
}
bool TestDirectory(std::filesystem::path directory, std::filesystem::path basePath) {
PropertyMap properties;
properties.ReadFromFile(directory / "SciTE.properties");
bool success = true;
for (auto &p : std::filesystem::directory_iterator(directory)) {
if (!p.is_directory()) {
@ -473,6 +810,9 @@ bool TestDirectory(std::filesystem::path directory, std::filesystem::path basePa
extension != suffixFolded) {
const std::filesystem::path relativePath = p.path().lexically_relative(basePath);
std::cout << "Lexing " << relativePath.string() << '\n';
PropertyMap properties;
properties.properties["FileNameExt"] = p.path().filename().string();
properties.ReadFromFile(directory / "SciTE.properties");
if (!TestFile(p, properties)) {
success = false;
}

View File

@ -1,3 +1,9 @@
code.page=65001
lexer.*.md=markdown
fold=1
fold=1
# Tests for the lexer.markdown.header.eolfill property, issue #62
if $(= $(FileNameExt);HeaderEOLFill_0.md)
lexer.markdown.header.eolfill=0
if $(= $(FileNameExt);HeaderEOLFill_1.md)
lexer.markdown.header.eolfill=1

View File

@ -1,3 +1,4 @@
lexer.*.rb=ruby
keywords.*.rb=class def end
keywords.*.rb=begin class def do end if module return self super true while \
__FILE__ __LINE__
fold=1

View File

@ -3,4 +3,4 @@
0 402 0 | i = 1
0 402 0 | puts "Example"
0 402 0 | end
0 400 0 end
0 401 0 | end

View File

@ -1 +1 @@
515
516

View File

@ -96,8 +96,7 @@ char ScintillaCall::CharacterAt(Position position) {
}
int ScintillaCall::UnsignedStyleAt(Position position) {
// Returns signed value but easier to use as unsigned
return static_cast<unsigned char>(Call(Message::GetStyleAt, position));
return static_cast<int>(Call(Message::GetStyleIndexAt, position));
}
std::string ScintillaCall::StringOfSpan(Span span) {
@ -192,6 +191,10 @@ int ScintillaCall::StyleAt(Position pos) {
return static_cast<int>(Call(Message::GetStyleAt, pos));
}
int ScintillaCall::StyleIndexAt(Position pos) {
return static_cast<int>(Call(Message::GetStyleIndexAt, pos));
}
void ScintillaCall::Redo() {
Call(Message::Redo);
}

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>5.2.1</string>
<string>5.2.2</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>

View File

@ -565,7 +565,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 5.2.1;
CURRENT_PROJECT_VERSION = 5.2.2;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@ -627,7 +627,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 5.2.1;
CURRENT_PROJECT_VERSION = 5.2.2;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@ -657,7 +657,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 5.2.1;
CURRENT_PROJECT_VERSION = 5.2.2;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
@ -691,7 +691,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 5.2.1;
CURRENT_PROJECT_VERSION = 5.2.2;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;

View File

@ -1100,7 +1100,7 @@ void ScintillaCocoa::CTPaint(void *gc, NSRect rc) {
#pragma unused(rc)
std::unique_ptr<Surface> surfaceWindow(Surface::Allocate(Technology::Default));
surfaceWindow->Init(gc, wMain.GetID());
surfaceWindow->SetMode(SurfaceMode(ct.codePage, BidirectionalR2L()));
surfaceWindow->SetMode(CurrentSurfaceMode());
ct.PaintCT(surfaceWindow.get());
surfaceWindow->Release();
}
@ -1456,7 +1456,7 @@ void ScintillaCocoa::StartDrag() {
// To get a bitmap of the text we're dragging, we just use Paint on a pixmap surface.
SurfaceImpl si;
si.SetMode(SurfaceMode(CodePage(), BidirectionalR2L()));
si.SetMode(CurrentSurfaceMode());
std::unique_ptr<SurfaceImpl> sw = si.AllocatePixMapImplementation(static_cast<int>(client.Width()), static_cast<int>(client.Height()));
const bool lastHideSelection = view.hideSelection;

View File

@ -129,7 +129,7 @@
<h1>Scintilla Documentation</h1>
<p>Last edited 2 February 2021 NH</p>
<p>Last edited 9 March 2022 NH</p>
<p style="background:#90F0C0">Scintilla 5 has moved the lexers from Scintilla into a new
<a href="Lexilla.html">Lexilla</a> project.<br />
@ -543,6 +543,7 @@
<a class="message" href="#SCI_CLEARDOCUMENTSTYLE">SCI_CLEARDOCUMENTSTYLE</a><br />
<a class="message" href="#SCI_GETCHARAT">SCI_GETCHARAT(position pos) &rarr; int</a><br />
<a class="message" href="#SCI_GETSTYLEAT">SCI_GETSTYLEAT(position pos) &rarr; int</a><br />
<a class="message" href="#SCI_GETSTYLEINDEXAT">SCI_GETSTYLEINDEXAT(position pos) &rarr; int</a><br />
<a class="message" href="#SCI_GETSTYLEDTEXT">SCI_GETSTYLEDTEXT(&lt;unused&gt;, Sci_TextRange *tr) &rarr; position</a><br />
<a class="message" href="#SCI_RELEASEALLEXTENDEDSTYLES">SCI_RELEASEALLEXTENDEDSTYLES</a><br />
<a class="message" href="#SCI_ALLOCATEEXTENDEDSTYLES">SCI_ALLOCATEEXTENDEDSTYLES(int numberStyles) &rarr; int</a><br />
@ -694,9 +695,15 @@
This returns the character at <code class="parameter">pos</code> in the document or 0 if <code class="parameter">pos</code> is
negative or past the end of the document.</p>
<p><b id="SCI_GETSTYLEAT">SCI_GETSTYLEAT(position pos) &rarr; int</b><br />
<p>
<b id="SCI_GETSTYLEAT">SCI_GETSTYLEAT(position pos) &rarr; int</b><br />
<b id="SCI_GETSTYLEINDEXAT">SCI_GETSTYLEINDEXAT(position pos) &rarr; int</b><br />
This returns the style at <code class="parameter">pos</code> in the document, or 0 if <code class="parameter">pos</code> is
negative or past the end of the document.</p>
negative or past the end of the document.
<code>SCI_GETSTYLEAT</code> may return a negative number for styles over 127 whereas <code>SCI_GETSTYLEINDEXAT</code>
will only return positive numbers.
<code>SCI_GETSTYLEINDEXAT</code> should be preferred as it handles styles more consistently and may avoid problems
with lexers that define more than 128 styles.</p>
<p><b id="SCI_RELEASEALLEXTENDEDSTYLES">SCI_RELEASEALLEXTENDEDSTYLES</b><br />
<b id="SCI_ALLOCATEEXTENDEDSTYLES">SCI_ALLOCATEEXTENDEDSTYLES(int numberStyles) &rarr; int</b><br />

View File

@ -26,9 +26,9 @@
<table bgcolor="#CCCCCC" width="100%" cellspacing="0" cellpadding="8" border="0">
<tr>
<td>
<font size="4"> <a href="https://www.scintilla.org/scintilla521.zip">
<font size="4"> <a href="https://www.scintilla.org/scintilla522.zip">
Windows</a>&nbsp;&nbsp;
<a href="https://www.scintilla.org/scintilla521.tgz">
<a href="https://www.scintilla.org/scintilla522.tgz">
GTK/Linux</a>&nbsp;&nbsp;
</font>
</td>
@ -42,7 +42,7 @@
containing very few restrictions.
</p>
<h3>
Release 5.2.1
Release 5.2.2
</h3>
<h4>
Source Code
@ -50,8 +50,8 @@
The source code package contains all of the source code for Scintilla but no binary
executable code and is available in
<ul>
<li><a href="https://www.scintilla.org/scintilla521.zip">zip format</a> (1.3M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/scintilla521.tgz">tgz format</a> (1.2M) commonly used on Linux and compatible operating systems</li>
<li><a href="https://www.scintilla.org/scintilla522.zip">zip format</a> (1.3M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/scintilla522.tgz">tgz format</a> (1.2M) commonly used on Linux and compatible operating systems</li>
</ul>
Instructions for building on both Windows and Linux are included in the readme file.
<h4>

View File

@ -569,9 +569,37 @@
</tr><tr>
<td>Arkadiusz Michalski</td>
<td>Christian Schmitz</td>
<td>Michael Berlenz</td>
</tr>
</table>
<h2>Releases</h2>
<h3>
<a href="https://www.scintilla.org/scintilla522.zip">Release 5.2.2</a>
</h3>
<ul>
<li>
Released 31 March 2022.
</li>
<li>
Add SCI_GETSTYLEINDEXAT API to return styles over 127 as positive integers.
<a href="https://sourceforge.net/p/scintilla/feature-requests/1431/">Feature #1431</a>.
</li>
<li>
On GTK, scroll horizontally with shift + scroll wheel.
</li>
<li>
Fix crash with unexpected right-to-left text on GTK.
<a href="https://sourceforge.net/p/scintilla/bugs/2309/">Bug #2309</a>.
</li>
<li>
Fix position of end-of-line annotation when fold display text is visible.
<a href="https://sourceforge.net/p/scintilla/bugs/2320/">Bug #2320</a>.
</li>
<li>
On Direct2D, support per-monitor text rendering parameters.
<a href="https://sourceforge.net/p/scintilla/feature-requests/1432/">Feature #1432</a>.
</li>
</ul>
<h3>
<a href="https://www.scintilla.org/scintilla521.zip">Release 5.2.1</a>
</h3>

View File

@ -9,7 +9,7 @@
<meta name="keywords" content="Scintilla, SciTE, Editing Component, Text Editor" />
<meta name="Description"
content="www.scintilla.org is the home of the Scintilla editing component and SciTE text editor application." />
<meta name="Date.Modified" content="20220224" />
<meta name="Date.Modified" content="20220331" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
#versionlist {
@ -56,8 +56,8 @@
GTK, and macOS</font>
</td>
<td width="40%" align="right">
<font color="#FFCC99" size="3"> Release version 5.2.1<br />
Site last modified February 24 2022</font>
<font color="#FFCC99" size="3"> Release version 5.2.2<br />
Site last modified March 31 2022</font>
</td>
<td width="20%">
&nbsp;
@ -72,6 +72,7 @@
</tr>
</table>
<ul id="versionlist">
<li>Version 5.2.2 on GTK, scroll horizontally with Shift + Scroll Wheel.</li>
<li>Version 5.2.1 fixes leaks on GTK.</li>
<li>Version 5.2.0 adds multithreaded layout to significantly improve performance for very wide lines.</li>
<li>Version 5.1.5 changes string-returning APIs to be more consistent and removes ScintillaEditPy.</li>

View File

@ -822,6 +822,8 @@ void SurfaceImpl::DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITI
}
}
namespace {
class ClusterIterator {
UniquePangoLayoutIter iter;
PangoRectangle pos {};
@ -836,6 +838,7 @@ public:
lenPositions(static_cast<int>(text.length())) {
LayoutSetText(layout, text);
iter.reset(pango_layout_get_iter(layout));
curIndex = pango_layout_iter_get_index(iter.get());
pango_layout_iter_get_cluster_extents(iter.get(), nullptr, &pos);
}
@ -848,12 +851,24 @@ public:
} else {
finished = true;
position = pango_units_to_double(pos.x + pos.width);
curIndex = lenPositions;
curIndex = pango_layout_iter_get_index(iter.get());
}
distance = position - positionStart;
}
};
// Something has gone wrong so set all the characters as equally spaced.
void EquallySpaced(PangoLayout *layout, XYPOSITION *positions, size_t lenPositions) {
int widthLayout = 0;
pango_layout_get_size(layout, &widthLayout, nullptr);
const XYPOSITION widthTotal = pango_units_to_double(widthLayout);
for (size_t bytePos=0; bytePos<lenPositions; bytePos++) {
positions[bytePos] = widthTotal / lenPositions * (bytePos + 1);
}
}
}
void SurfaceImpl::MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) {
if (PFont(font_)->fd) {
UniquePangoContext contextMeasure = MeasuringContext();
@ -863,8 +878,13 @@ void SurfaceImpl::MeasureWidths(const Font *font_, std::string_view text, XYPOSI
pango_layout_set_font_description(layoutMeasure.get(), PFont(font_)->fd.get());
if (et == EncodingType::utf8) {
// Simple and direct as UTF-8 is native Pango encoding
int i = 0;
ClusterIterator iti(layoutMeasure.get(), text);
int i = iti.curIndex;
if (i != 0) {
// Unexpected start to iteration, could be bidirectional text
EquallySpaced(layoutMeasure.get(), positions, text.length());
return;
}
while (!iti.finished) {
iti.Next();
const int places = iti.curIndex - i;
@ -889,8 +909,13 @@ void SurfaceImpl::MeasureWidths(const Font *font_, std::string_view text, XYPOSI
// character byte lengths.
Converter convMeasure("UCS-2", CharacterSetID(characterSet), false);
int i = 0;
int clusterStart = 0;
ClusterIterator iti(layoutMeasure.get(), utfForm);
int clusterStart = iti.curIndex;
if (clusterStart != 0) {
// Unexpected start to iteration, could be bidirectional text
EquallySpaced(layoutMeasure.get(), positions, text.length());
return;
}
while (!iti.finished) {
iti.Next();
const int clusterEnd = iti.curIndex;
@ -920,22 +945,22 @@ void SurfaceImpl::MeasureWidths(const Font *font_, std::string_view text, XYPOSI
utfForm = UTF8FromLatin1(text);
}
size_t i = 0;
int clusterStart = 0;
// Each 8-bit input character may take 1 or 2 bytes in UTF-8
// and groups of up to 3 may be represented as ligatures.
ClusterIterator iti(layoutMeasure.get(), utfForm);
int clusterStart = iti.curIndex;
if (clusterStart != 0) {
// Unexpected start to iteration, could be bidirectional text
EquallySpaced(layoutMeasure.get(), positions, lenPositions);
return;
}
while (!iti.finished) {
iti.Next();
const int clusterEnd = iti.curIndex;
const int ligatureLength = g_utf8_strlen(utfForm.c_str() + clusterStart, clusterEnd - clusterStart);
if (rtlCheck && ((clusterEnd <= clusterStart) || (ligatureLength == 0) || (ligatureLength > 3))) {
// Something has gone wrong: exit quickly but pretend all the characters are equally spaced:
int widthLayout = 0;
pango_layout_get_size(layoutMeasure.get(), &widthLayout, nullptr);
const XYPOSITION widthTotal = pango_units_to_double(widthLayout);
for (size_t bytePos=0; bytePos<lenPositions; bytePos++) {
positions[bytePos] = widthTotal / lenPositions * (bytePos + 1);
}
EquallySpaced(layoutMeasure.get(), positions, lenPositions);
return;
}
PLATFORM_ASSERT(ligatureLength > 0 && ligatureLength <= 3);
@ -1029,8 +1054,13 @@ void SurfaceImpl::MeasureWidthsUTF8(const Font *font_, std::string_view text, XY
pango_layout_set_font_description(layoutMeasure.get(), PFont(font_)->fd.get());
// Simple and direct as UTF-8 is native Pango encoding
int i = 0;
ClusterIterator iti(layoutMeasure.get(), text);
int i = iti.curIndex;
if (i != 0) {
// Unexpected start to iteration, could be bidirectional text
EquallySpaced(layoutMeasure.get(), positions, text.length());
return;
}
while (!iti.finished) {
iti.Next();
const int places = iti.curIndex - i;

View File

@ -1999,11 +1999,6 @@ gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) {
// issues spurious button 2 mouse events during wheeling, which can cause
// problems (a patch for both was submitted by archaeopteryx.com on 13Jun2001)
// Data zoom not supported
if (event->state & GDK_SHIFT_MASK) {
return FALSE;
}
#if GTK_CHECK_VERSION(3,4,0)
// Smooth scrolling not supported
if (event->direction == GDK_SCROLL_SMOOTH) {
@ -2012,8 +2007,10 @@ gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) {
#endif
// Horizontal scrolling
if (event->direction == GDK_SCROLL_LEFT || event->direction == GDK_SCROLL_RIGHT) {
sciThis->HorizontalScrollTo(sciThis->xOffset + cLineScroll);
if (event->direction == GDK_SCROLL_LEFT || event->direction == GDK_SCROLL_RIGHT || event->state & GDK_SHIFT_MASK) {
int hScroll = gtk_adjustment_get_step_increment(sciThis->adjustmenth);
hScroll *= cLineScroll; // scroll by this many characters
sciThis->HorizontalScrollTo(sciThis->xOffset + hScroll);
// Text font size zoom
} else if (event->state & GDK_CONTROL_MASK) {

View File

@ -15,10 +15,8 @@
typedef ptrdiff_t Sci_Position;
// Unsigned variant used for ILexer::Lex and ILexer::Fold
// Definitions of common types
typedef size_t Sci_PositionU;
// For Sci_CharacterRange which is defined as long to be compatible with Win32 CHARRANGE
typedef intptr_t Sci_PositionCR;

View File

@ -57,6 +57,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP
#define SCI_GETCURRENTPOS 2008
#define SCI_GETANCHOR 2009
#define SCI_GETSTYLEAT 2010
#define SCI_GETSTYLEINDEXAT 2038
#define SCI_REDO 2011
#define SCI_SETUNDOCOLLECTION 2012
#define SCI_SELECTALL 2013

View File

@ -130,6 +130,9 @@ get position GetAnchor=2009(,)
# Returns the style byte at the position.
get int GetStyleAt=2010(position pos,)
# Returns the unsigned style byte at the position.
get int GetStyleIndexAt=2038(position pos,)
# Redoes the next action on the undo history.
fun void Redo=2011(,)

View File

@ -88,6 +88,7 @@ public:
Position CurrentPos();
Position Anchor();
int StyleAt(Position pos);
int StyleIndexAt(Position pos);
void Redo();
void SetUndoCollection(bool collectUndo);
void SelectAll();

View File

@ -28,6 +28,7 @@ enum class Message {
GetCurrentPos = 2008,
GetAnchor = 2009,
GetStyleAt = 2010,
GetStyleIndexAt = 2038,
Redo = 2011,
SetUndoCollection = 2012,
SelectAll = 2013,

View File

@ -13,7 +13,7 @@ TEMPLATE = lib
CONFIG += lib_bundle
CONFIG += c++1z
VERSION = 5.2.1
VERSION = 5.2.2
SOURCES += \
ScintillaEdit.cpp \

View File

@ -339,19 +339,8 @@ void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern)
{
// Tile pattern over rectangle
SurfaceImpl *surface = dynamic_cast<SurfaceImpl *>(&surfacePattern);
// Currently assumes 8x8 pattern
int widthPat = 8;
int heightPat = 8;
for (int xTile = rc.left; xTile < rc.right; xTile += widthPat) {
int widthx = (xTile + widthPat > rc.right) ? rc.right - xTile : widthPat;
for (int yTile = rc.top; yTile < rc.bottom; yTile += heightPat) {
int heighty = (yTile + heightPat > rc.bottom) ? rc.bottom - yTile : heightPat;
QRect source(0, 0, widthx, heighty);
QRect target(xTile, yTile, widthx, heighty);
QPixmap *pixmap = static_cast<QPixmap *>(surface->GetPaintDevice());
GetPainter()->drawPixmap(target, *pixmap, source);
}
}
const QPixmap *pixmap = static_cast<QPixmap *>(surface->GetPaintDevice());
GetPainter()->drawTiledPixmap(QRectFromPRect(rc), *pixmap);
}
void SurfaceImpl::RoundedRectangle(PRectangle rc, FillStroke fillStroke)

View File

@ -12,7 +12,7 @@ TEMPLATE = lib
CONFIG += lib_bundle
CONFIG += c++1z
VERSION = 5.2.1
VERSION = 5.2.2
SOURCES += \
PlatQt.cpp \

View File

@ -272,24 +272,14 @@ void CallTip::MouseClick(Point pt) noexcept {
}
PRectangle CallTip::CallTipStart(Sci::Position pos, Point pt, int textHeight, const char *defn,
const char *faceName, int size,
int codePage_, CharacterSet characterSet,
Technology technology,
const char *localeName,
const Window &wParent) {
int codePage_, Surface *surfaceMeasure, std::shared_ptr<Font> font_) {
clickPlace = 0;
val = defn;
codePage = codePage_;
std::unique_ptr<Surface> surfaceMeasure = Surface::Allocate(technology);
surfaceMeasure->Init(wParent.GetID());
surfaceMeasure->SetMode(SurfaceMode(codePage, false));
highlight = Chunk();
inCallTipMode = true;
posStartCallTip = pos;
const XYPOSITION deviceHeight = static_cast<XYPOSITION>(surfaceMeasure->DeviceHeightFont(size));
const FontParameters fp(faceName, deviceHeight / FontSizeMultiplier, FontWeight::Normal,
false, FontQuality::QualityDefault, technology, characterSet, localeName);
font = Font::Allocate(fp);
font = font_;
// Look for multiple lines in the text
// Only support \n here - simply means container must avoid \r!
const int numLines = 1 + static_cast<int>(std::count(val.begin(), val.end(), '\n'));
@ -300,7 +290,7 @@ PRectangle CallTip::CallTipStart(Sci::Position pos, Point pt, int textHeight, co
#if !PLAT_CURSES
widthArrow = lineHeight * 9 / 10;
#endif
const int width = PaintContents(surfaceMeasure.get(), false) + insetX;
const int width = PaintContents(surfaceMeasure, false) + insetX;
// The returned
// rectangle is aligned to the right edge of the last arrow encountered in
@ -350,7 +340,7 @@ bool CallTip::UseStyleCallTip() const noexcept {
// It might be better to have two access functions for this and to use
// them for all settings of colours.
void CallTip::SetForeBack(const ColourRGBA &fore, const ColourRGBA &back) noexcept {
void CallTip::SetForeBack(ColourRGBA fore, ColourRGBA back) noexcept {
colourBG = back;
colourUnSel = fore;
}

View File

@ -71,9 +71,7 @@ public:
/// Setup the calltip and return a rectangle of the area required.
PRectangle CallTipStart(Sci::Position pos, Point pt, int textHeight, const char *defn,
const char *faceName, int size, int codePage_,
Scintilla::CharacterSet characterSet, Scintilla::Technology technology, const char *localeName,
const Window &wParent);
int codePage_, Surface *surfaceMeasure, std::shared_ptr<Font> font_);
void CallTipCancel() noexcept;
@ -91,7 +89,7 @@ public:
bool UseStyleCallTip() const noexcept;
// Modify foreground and background colours
void SetForeBack(const ColourRGBA &fore, const ColourRGBA &back) noexcept;
void SetForeBack(ColourRGBA fore, ColourRGBA back) noexcept;
};
}

View File

@ -101,6 +101,10 @@ bool EditModel::BidirectionalR2L() const noexcept {
return bidirectional == Bidirectional::R2L;
}
SurfaceMode EditModel::CurrentSurfaceMode() const noexcept {
return SurfaceMode(pdoc->dbcsCodePage, BidirectionalR2L());
}
void EditModel::SetDefaultFoldDisplayText(const char *text) {
defaultFoldDisplayText = IsNullOrEmpty(text) ? UniqueString() : UniqueStringCopy(text);
}

View File

@ -66,6 +66,7 @@ public:
virtual Sci::Line LinesOnScreen() const = 0;
bool BidirectionalEnabled() const noexcept;
bool BidirectionalR2L() const noexcept;
SurfaceMode CurrentSurfaceMode() const noexcept;
void SetDefaultFoldDisplayText(const char *text);
const char *GetDefaultFoldDisplayText() const noexcept;
const char *GetFoldDisplayText(Sci::Line lineDoc) const noexcept;

View File

@ -388,7 +388,7 @@ bool ViewIsASCII(std::string_view text) {
void LayoutSegments(IPositionCache *pCache,
Surface *surface,
const ViewStyle &vstyle,
LineLayout *ll,
LineLayout *ll,
const std::vector<TextSegment> &segments,
std::atomic<uint32_t> &nextIndex,
const bool textUnicode,
@ -774,8 +774,8 @@ Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, S
}
}
pt.y += (lineVisible - topLine) * vs.lineHeight;
pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
}
pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
return pt;
}
@ -1485,7 +1485,9 @@ void EditView::DrawEOLAnnotationText(Surface *surface, const EditModel &model, c
const char *textFoldDisplay = model.GetFoldDisplayText(line);
if (textFoldDisplay) {
const std::string_view foldDisplayText(textFoldDisplay);
rcSegment.left += (static_cast<int>(surface->WidthText(fontText, foldDisplayText)) + vsDraw.aveCharWidth);
rcSegment.left += static_cast<int>(
surface->WidthText(vsDraw.styles[StyleFoldDisplayText].font.get(), foldDisplayText)) +
vsDraw.aveCharWidth;
}
rcSegment.right = rcSegment.left + static_cast<XYPOSITION>(widthEOLAnnotationText);
@ -2441,7 +2443,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectan
surface = pixmapLine.get();
PLATFORM_ASSERT(pixmapLine->Initialised());
}
surface->SetMode(SurfaceMode(model.pdoc->dbcsCodePage, model.BidirectionalR2L()));
surface->SetMode(model.CurrentSurfaceMode());
const Point ptOrigin = model.GetVisibleOriginInMain();

View File

@ -204,7 +204,6 @@ Editor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) {
Editor::~Editor() {
pdoc->RemoveWatcher(this, nullptr);
DropGraphics();
}
void Editor::Finalise() {
@ -1707,6 +1706,7 @@ void Editor::PaintSelMargin(Surface *surfaceWindow, const PRectangle &rc) {
} else {
surface = surfaceWindow;
}
surface->SetMode(CurrentSurfaceMode());
// Clip vertically to paint area to avoid drawing line numbers
if (rcMargin.bottom > rc.bottom)
@ -5687,6 +5687,26 @@ int Editor::CodePage() const noexcept {
return 0;
}
std::unique_ptr<Surface> Editor::CreateMeasurementSurface() const {
if (!wMain.GetID()) {
return {};
}
std::unique_ptr<Surface> surf = Surface::Allocate(technology);
surf->Init(wMain.GetID());
surf->SetMode(CurrentSurfaceMode());
return surf;
}
std::unique_ptr<Surface> Editor::CreateDrawingSurface(SurfaceID sid, std::optional<Scintilla::Technology> technologyOpt) const {
if (!wMain.GetID()) {
return {};
}
std::unique_ptr<Surface> surf = Surface::Allocate(technologyOpt ? *technologyOpt : technology);
surf->Init(sid, wMain.GetID());
surf->SetMode(CurrentSurfaceMode());
return surf;
}
Sci::Line Editor::WrapCount(Sci::Line line) {
AutoSurface surface(this);
std::shared_ptr<LineLayout> ll = view.RetrieveLineLayout(line, *this);
@ -6464,6 +6484,12 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
else
return pdoc->StyleAt(PositionFromUPtr(wParam));
case Message::GetStyleIndexAt:
if (PositionFromUPtr(wParam) >= pdoc->Length())
return 0;
else
return pdoc->StyleIndexAt(PositionFromUPtr(wParam));
case Message::Redo:
Redo();
break;

View File

@ -592,6 +592,8 @@ protected: // ScintillaBase subclass needs access to much of Editor
virtual bool ValidCodePage(int /* codePage */) const { return true; }
virtual std::string UTF8FromEncoded(std::string_view encoded) const = 0;
virtual std::string EncodedFromUTF8(std::string_view utf8) const = 0;
virtual std::unique_ptr<Surface> CreateMeasurementSurface() const;
virtual std::unique_ptr<Surface> CreateDrawingSurface(SurfaceID sid, std::optional<Scintilla::Technology> technologyOpt = {}) const;
Sci::Line WrapCount(Sci::Line line);
void AddStyledText(const char *buffer, Sci::Position appendLength);
@ -684,19 +686,11 @@ class AutoSurface {
private:
std::unique_ptr<Surface> surf;
public:
AutoSurface(const Editor *ed) {
if (ed->wMain.GetID()) {
surf = Surface::Allocate(ed->technology);
surf->Init(ed->wMain.GetID());
surf->SetMode(SurfaceMode(ed->CodePage(), ed->BidirectionalR2L()));
}
AutoSurface(const Editor *ed) :
surf(ed->CreateMeasurementSurface()) {
}
AutoSurface(SurfaceID sid, Editor *ed, std::optional<Scintilla::Technology> technology = {}) {
if (ed->wMain.GetID()) {
surf = Surface::Allocate(technology ? *technology : ed->technology);
surf->Init(sid, ed->wMain.GetID());
surf->SetMode(SurfaceMode(ed->CodePage(), ed->BidirectionalR2L()));
}
AutoSurface(SurfaceID sid, Editor *ed, std::optional<Scintilla::Technology> technology = {}) :
surf(ed->CreateDrawingSurface(sid, technology)) {
}
// Deleted so AutoSurface objects can not be copied.
AutoSurface(const AutoSurface &) = delete;

View File

@ -465,22 +465,20 @@ void ScintillaBase::CallTipShow(Point pt, const char *defn) {
// StyleDefault for the face name, size and character set. Also use it
// for the foreground and background colour.
const int ctStyle = ct.UseStyleCallTip() ? StyleCallTip : StyleDefault;
const Style &style = vs.styles[ctStyle];
if (ct.UseStyleCallTip()) {
ct.SetForeBack(vs.styles[StyleCallTip].fore, vs.styles[StyleCallTip].back);
ct.SetForeBack(style.fore, style.back);
}
if (wMargin.Created()) {
pt = pt + GetVisibleOriginInMain();
}
AutoSurface surfaceMeasure(this);
PRectangle rc = ct.CallTipStart(sel.MainCaret(), pt,
vs.lineHeight,
defn,
vs.styles[ctStyle].fontName,
vs.styles[ctStyle].sizeZoomed,
CodePage(),
vs.styles[ctStyle].characterSet,
vs.technology,
vs.localeName.c_str(),
wMain);
surfaceMeasure,
style.font);
// If the call-tip window would be out of the client
// space
const PRectangle rcClient = GetClientRectangle();

View File

@ -133,7 +133,6 @@ ViewStyle::ViewStyle(size_t stylesSize_) :
Element::SelectionSecondaryText,
Element::SelectionSecondaryBack,
Element::SelectionInactiveText,
Element::SelectionBack,
Element::SelectionInactiveBack,
});

View File

@ -48,13 +48,16 @@ class TestSimple(unittest.TestCase):
def testAddStyledText(self):
self.assertEquals(self.ed.EndStyled, 0)
self.ed.AddStyledText(2, b"x\002")
self.assertEquals(self.ed.Length, 1)
self.ed.AddStyledText(4, b"x\002y\377")
self.assertEquals(self.ed.Length, 2)
self.assertEquals(self.ed.GetCharAt(0), ord("x"))
self.assertEquals(self.ed.GetStyleAt(0), 2)
self.assertEquals(self.ed.GetStyleIndexAt(0), 2)
self.assertEquals(self.ed.GetStyleIndexAt(1), 255)
self.assertEquals(self.ed.StyledTextRange(0, 1), b"x\002")
self.assertEquals(self.ed.StyledTextRange(1, 2), b"y\377")
self.ed.ClearDocumentStyle()
self.assertEquals(self.ed.Length, 1)
self.assertEquals(self.ed.Length, 2)
self.assertEquals(self.ed.GetCharAt(0), ord("x"))
self.assertEquals(self.ed.GetStyleAt(0), 0)
self.assertEquals(self.ed.StyledTextRange(0, 1), b"x\0")

View File

@ -1 +1 @@
521
522

View File

@ -19,14 +19,6 @@
namespace Scintilla::Internal::HanjaDict {
struct BSTRDeleter {
void operator()(BSTR bstr) const noexcept {
SysFreeString(bstr);
}
};
using UniqueBSTR = std::unique_ptr<OLECHAR[], BSTRDeleter>;
interface IRadical;
interface IHanja;
interface IStrokes;
@ -66,6 +58,37 @@ interface IHanjaDic : IUnknown {
extern "C" const GUID __declspec(selectany) IID_IHanjaDic =
{ 0xad75f3ac, 0x18cd, 0x48c6, { 0xa2, 0x7d, 0xf1, 0xe9, 0xa7, 0xdc, 0xe4, 0x32 } };
class ScopedBSTR {
BSTR bstr = nullptr;
public:
ScopedBSTR() noexcept = default;
explicit ScopedBSTR(const OLECHAR *psz) noexcept :
bstr(SysAllocString(psz)) {
}
explicit ScopedBSTR(OLECHAR character) noexcept :
bstr(SysAllocStringLen(&character, 1)) {
}
// Deleted so ScopedBSTR objects can not be copied. Moves are OK.
ScopedBSTR(const ScopedBSTR &) = delete;
ScopedBSTR &operator=(const ScopedBSTR &) = delete;
// Moves are OK.
ScopedBSTR(ScopedBSTR &&) = default;
ScopedBSTR &operator=(ScopedBSTR &&) = default;
~ScopedBSTR() {
SysFreeString(bstr);
}
BSTR get() const noexcept {
return bstr;
}
void reset(BSTR value=nullptr) noexcept {
// https://en.cppreference.com/w/cpp/memory/unique_ptr/reset
BSTR const old = bstr;
bstr = value;
SysFreeString(old);
}
};
class HanjaDic {
std::unique_ptr<IHanjaDic, UnknownReleaser> HJinterface;
@ -77,7 +100,7 @@ class HanjaDic {
hr = CoCreateInstance(CLSID_HanjaDic, nullptr,
CLSCTX_INPROC_SERVER, IID_IHanjaDic,
(LPVOID *)&instance);
if (SUCCEEDED(hr)) {
if (SUCCEEDED(hr) && instance) {
HJinterface.reset(instance);
hr = instance->OpenMainDic();
return SUCCEEDED(hr);
@ -102,9 +125,9 @@ public:
return SUCCEEDED(hr) && hanjaType > HANJA_UNKNOWN;
}
bool HanjaToHangul(BSTR bstrHanja, UniqueBSTR &bstrHangul) const noexcept {
bool HanjaToHangul(const ScopedBSTR &bstrHanja, ScopedBSTR &bstrHangul) const noexcept {
BSTR result = nullptr;
const HRESULT hr = HJinterface->HanjaToHangul(bstrHanja, &result);
const HRESULT hr = HJinterface->HanjaToHangul(bstrHanja.get(), &result);
bstrHangul.reset(result);
return SUCCEEDED(hr);
}
@ -121,11 +144,10 @@ bool GetHangulOfHanja(std::wstring &inout) noexcept {
if (dict.Open()) {
for (wchar_t &character : inout) {
if (dict.IsHanja(character)) { // Pass hanja only!
const UniqueBSTR bstrHanja{SysAllocStringLen(&character, 1)};
UniqueBSTR bstrHangul;
if (dict.HanjaToHangul(bstrHanja.get(), bstrHangul)) {
ScopedBSTR bstrHangul;
if (dict.HanjaToHangul(ScopedBSTR(character), bstrHangul)) {
changed = true;
character = bstrHangul[0];
character = *(bstrHangul.get());
}
}
}

View File

@ -28,9 +28,9 @@
#define NOMINMAX
#endif
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#define _WIN32_WINNT 0x0A00
#undef WINVER
#define WINVER 0x0500
#define WINVER 0x0A00
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <commctrl.h>
@ -58,14 +58,6 @@
#include "WinTypes.h"
#include "PlatWin.h"
#ifndef SPI_GETFONTSMOOTHINGCONTRAST
#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
#endif
#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
#endif
// __uuidof is a Microsoft extension but makes COM code neater, so disable warning
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wlanguage-extension-token"
@ -80,8 +72,6 @@ UINT CodePageFromCharSet(CharacterSet characterSet, UINT documentCodePage) noexc
#if defined(USE_D2D)
IDWriteFactory *pIDWriteFactory = nullptr;
ID2D1Factory *pD2DFactory = nullptr;
IDWriteRenderingParams *defaultRenderingParams = nullptr;
IDWriteRenderingParams *customClearTypeRenderingParams = nullptr;
D2D1_DRAW_TEXT_OPTIONS d2dDrawTextOptions = D2D1_DRAW_TEXT_OPTIONS_NONE;
static HMODULE hDLLD2D {};
@ -131,24 +121,6 @@ void LoadD2DOnce() noexcept {
reinterpret_cast<IUnknown**>(&pIDWriteFactory));
}
}
if (pIDWriteFactory) {
const HRESULT hr = pIDWriteFactory->CreateRenderingParams(&defaultRenderingParams);
if (SUCCEEDED(hr)) {
unsigned int clearTypeContrast = 0;
if (::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &clearTypeContrast, 0)) {
FLOAT gamma;
if (clearTypeContrast >= 1000 && clearTypeContrast <= 2200)
gamma = static_cast<FLOAT>(clearTypeContrast) / 1000.0f;
else
gamma = defaultRenderingParams->GetGamma();
pIDWriteFactory->CreateCustomRenderingParams(gamma, defaultRenderingParams->GetEnhancedContrast(), defaultRenderingParams->GetClearTypeLevel(),
defaultRenderingParams->GetPixelGeometry(), defaultRenderingParams->GetRenderingMode(), &customClearTypeRenderingParams);
}
}
}
}
bool LoadD2D() {
@ -159,10 +131,6 @@ bool LoadD2D() {
#endif
#ifndef CLEARTYPE_QUALITY
#define CLEARTYPE_QUALITY 5
#endif
void *PointerFromWindow(HWND hWnd) noexcept {
return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
}
@ -1307,7 +1275,7 @@ constexpr D2D1_RECT_F RectangleInset(D2D1_RECT_F rect, FLOAT inset) noexcept {
class BlobInline;
class SurfaceD2D : public Surface {
class SurfaceD2D : public Surface, public ISetRenderingParams {
SurfaceMode mode;
ID2D1RenderTarget *pRenderTarget = nullptr;
@ -1317,8 +1285,10 @@ class SurfaceD2D : public Surface {
ID2D1SolidColorBrush *pBrush = nullptr;
FontQuality fontQuality = FontQuality::QualityMask;
static constexpr FontQuality invalidFontQuality = FontQuality::QualityMask;
FontQuality fontQuality = invalidFontQuality;
int logPixelsY = USER_DEFAULT_SCREEN_DPI;
std::shared_ptr<RenderingParams> renderingParams;
void Clear() noexcept;
void SetFontQuality(FontQuality extraFontFlag);
@ -1392,6 +1362,8 @@ public:
void PopClip() override;
void FlushCachedState() override;
void FlushDrawing() override;
void SetRenderingParams(std::shared_ptr<RenderingParams> renderingParams_) override;
};
SurfaceD2D::SurfaceD2D() noexcept {
@ -1443,7 +1415,7 @@ void SurfaceD2D::Release() noexcept {
}
void SurfaceD2D::SetScale(WindowID wid) noexcept {
fontQuality = FontQuality::QualityMask;
fontQuality = invalidFontQuality;
logPixelsY = DpiForWindow(wid);
}
@ -1471,7 +1443,9 @@ void SurfaceD2D::Init(SurfaceID sid, WindowID wid) {
}
std::unique_ptr<Surface> SurfaceD2D::AllocatePixMap(int width, int height) {
return std::make_unique<SurfaceD2D>(pRenderTarget, width, height, mode, logPixelsY);
std::unique_ptr<SurfaceD2D> surf = std::make_unique<SurfaceD2D>(pRenderTarget, width, height, mode, logPixelsY);
surf->SetRenderingParams(renderingParams);
return surf;
}
void SurfaceD2D::SetMode(SurfaceMode mode_) {
@ -1498,15 +1472,14 @@ void SurfaceD2D::D2DPenColourAlpha(ColourRGBA fore) noexcept {
}
void SurfaceD2D::SetFontQuality(FontQuality extraFontFlag) {
if (fontQuality != extraFontFlag) {
if ((fontQuality != extraFontFlag) && renderingParams) {
fontQuality = extraFontFlag;
const D2D1_TEXT_ANTIALIAS_MODE aaMode = DWriteMapFontQuality(extraFontFlag);
if (aaMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && customClearTypeRenderingParams)
pRenderTarget->SetTextRenderingParams(customClearTypeRenderingParams);
else if (defaultRenderingParams)
pRenderTarget->SetTextRenderingParams(defaultRenderingParams);
if (aaMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && renderingParams->customRenderingParams) {
pRenderTarget->SetTextRenderingParams(renderingParams->customRenderingParams.get());
} else if (renderingParams->defaultRenderingParams) {
pRenderTarget->SetTextRenderingParams(renderingParams->defaultRenderingParams.get());
}
pRenderTarget->SetTextAntialiasMode(aaMode);
}
}
@ -2642,6 +2615,10 @@ void SurfaceD2D::FlushDrawing() {
}
}
void SurfaceD2D::SetRenderingParams(std::shared_ptr<RenderingParams> renderingParams_) {
renderingParams = renderingParams_;
}
#endif
std::unique_ptr<Surface> Surface::Allocate(Technology technology) {
@ -3876,8 +3853,6 @@ void Platform_Initialise(void *hInstance) noexcept {
void Platform_Finalise(bool fromDllMain) noexcept {
#if defined(USE_D2D)
if (!fromDllMain) {
ReleaseUnknown(defaultRenderingParams);
ReleaseUnknown(customClearTypeRenderingParams);
ReleaseUnknown(pIDWriteFactory);
ReleaseUnknown(pD2DFactory);
if (hDLLDWrite) {

View File

@ -53,6 +53,15 @@ HCURSOR LoadReverseArrowCursor(UINT dpi) noexcept;
extern bool LoadD2D();
extern ID2D1Factory *pD2DFactory;
extern IDWriteFactory *pIDWriteFactory;
struct RenderingParams {
std::unique_ptr<IDWriteRenderingParams, UnknownReleaser> defaultRenderingParams;
std::unique_ptr<IDWriteRenderingParams, UnknownReleaser> customRenderingParams;
};
struct ISetRenderingParams {
virtual void SetRenderingParams(std::shared_ptr<RenderingParams> renderingParams_) = 0;
};
#endif
}

View File

@ -4,8 +4,8 @@
#include <windows.h>
#define VERSION_SCINTILLA "5.2.1"
#define VERSION_WORDS 5, 2, 1, 0
#define VERSION_SCINTILLA "5.2.2"
#define VERSION_WORDS 5, 2, 2, 0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_WORDS

View File

@ -32,9 +32,9 @@
#define NOMINMAX
#endif
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#define _WIN32_WINNT 0x0A00
#undef WINVER
#define WINVER 0x0500
#define WINVER 0x0A00
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <commctrl.h>
@ -147,12 +147,6 @@ constexpr int IndicatorTarget = IndicatorInput + 1;
constexpr int IndicatorConverted = IndicatorInput + 2;
constexpr int IndicatorUnknown = IndicatorInput + 3;
#ifndef SCS_CAP_SETRECONVERTSTRING
#define SCS_CAP_SETRECONVERTSTRING 0x00000004
#define SCS_QUERYRECONVERTSTRING 0x00020000
#define SCS_SETRECONVERTSTRING 0x00010000
#endif
typedef UINT_PTR (WINAPI *SetCoalescableTimerSig)(HWND hwnd, UINT_PTR nIDEvent,
UINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);
@ -356,6 +350,9 @@ class ScintillaWin :
#if defined(USE_D2D)
ID2D1RenderTarget *pRenderTarget;
bool renderTargetValid;
// rendering parameters for current monitor
HMONITOR hCurrentMonitor;
std::shared_ptr<RenderingParams> renderingParams;
#endif
explicit ScintillaWin(HWND hwnd);
@ -368,6 +365,7 @@ class ScintillaWin :
void Finalise() override;
#if defined(USE_D2D)
bool UpdateRenderingParams(bool force) noexcept;
void EnsureRenderTarget(HDC hdc);
#endif
void DropRenderTarget() noexcept;
@ -392,6 +390,8 @@ class ScintillaWin :
Sci::Position TargetAsUTF8(char *text) const;
Sci::Position EncodedFromUTF8(const char *utf8, char *encoded) const;
void SetRenderingParams(Surface *psurf) const;
bool PaintDC(HDC hdc);
sptr_t WndPaint();
@ -571,6 +571,7 @@ ScintillaWin::ScintillaWin(HWND hwnd) {
#if defined(USE_D2D)
pRenderTarget = nullptr;
renderTargetValid = true;
hCurrentMonitor = {};
#endif
caret.period = ::GetCaretBlinkTime();
@ -615,6 +616,38 @@ void ScintillaWin::Finalise() {
#if defined(USE_D2D)
bool ScintillaWin::UpdateRenderingParams(bool force) noexcept {
if (!renderingParams) {
renderingParams = std::make_shared<RenderingParams>();
}
HMONITOR monitor = ::MonitorFromWindow(MainHWND(), MONITOR_DEFAULTTONEAREST);
if (!force && monitor == hCurrentMonitor && renderingParams->defaultRenderingParams) {
return false;
}
IDWriteRenderingParams *monitorRenderingParams = nullptr;
IDWriteRenderingParams *customClearTypeRenderingParams = nullptr;
const HRESULT hr = pIDWriteFactory->CreateMonitorRenderingParams(monitor, &monitorRenderingParams);
UINT clearTypeContrast = 0;
if (SUCCEEDED(hr) && ::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &clearTypeContrast, 0) != 0) {
if (clearTypeContrast >= 1000 && clearTypeContrast <= 2200) {
const FLOAT gamma = static_cast<FLOAT>(clearTypeContrast) / 1000.0f;
pIDWriteFactory->CreateCustomRenderingParams(gamma,
monitorRenderingParams->GetEnhancedContrast(),
monitorRenderingParams->GetClearTypeLevel(),
monitorRenderingParams->GetPixelGeometry(),
monitorRenderingParams->GetRenderingMode(),
&customClearTypeRenderingParams);
}
}
hCurrentMonitor = monitor;
renderingParams->defaultRenderingParams.reset(monitorRenderingParams);
renderingParams->customRenderingParams.reset(customClearTypeRenderingParams);
return true;
}
void ScintillaWin::EnsureRenderTarget(HDC hdc) {
if (!renderTargetValid) {
DropRenderTarget();
@ -684,7 +717,6 @@ void ScintillaWin::EnsureRenderTarget(HDC hdc) {
}
#endif
void ScintillaWin::DropRenderTarget() noexcept {
#if defined(USE_D2D)
ReleaseUnknown(pRenderTarget);
@ -911,6 +943,17 @@ Sci::Position ScintillaWin::EncodedFromUTF8(const char *utf8, char *encoded) con
}
}
void ScintillaWin::SetRenderingParams([[maybe_unused]] Surface *psurf) const {
#if defined(USE_D2D)
if (psurf) {
ISetRenderingParams *setDrawingParams = dynamic_cast<ISetRenderingParams *>(psurf);
if (setDrawingParams) {
setDrawingParams->SetRenderingParams(renderingParams);
}
}
#endif
}
bool ScintillaWin::PaintDC(HDC hdc) {
if (technology == Technology::Default) {
AutoSurface surfaceWindow(hdc, this);
@ -924,6 +967,7 @@ bool ScintillaWin::PaintDC(HDC hdc) {
if (pRenderTarget) {
AutoSurface surfaceWindow(pRenderTarget, this);
if (surfaceWindow) {
SetRenderingParams(surfaceWindow);
pRenderTarget->BeginDraw();
Paint(surfaceWindow, rcPaint);
surfaceWindow->Release();
@ -1887,9 +1931,11 @@ sptr_t ScintillaWin::SciMessage(Message iMessage, uptr_t wParam, sptr_t lParam)
if (technology != technologyNew) {
if (technologyNew > Technology::Default) {
#if defined(USE_D2D)
if (!LoadD2D())
if (!LoadD2D()) {
// Failed to load Direct2D or DirectWrite so no effect
return 0;
}
UpdateRenderingParams(true);
#else
return 0;
#endif
@ -1900,7 +1946,6 @@ sptr_t ScintillaWin::SciMessage(Message iMessage, uptr_t wParam, sptr_t lParam)
technology = technologyNew;
view.bufferedDraw = technologyNew == Technology::Default;
// Invalidate all cached information including layout.
DropGraphics();
InvalidateStyleRedraw();
}
}
@ -1913,7 +1958,6 @@ sptr_t ScintillaWin::SciMessage(Message iMessage, uptr_t wParam, sptr_t lParam)
bidirectional = static_cast<Bidirectional>(wParam);
}
// Invalidate all cached information including layout.
DropGraphics();
InvalidateStyleRedraw();
break;
@ -2017,10 +2061,15 @@ sptr_t ScintillaWin::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
case WM_SETTINGCHANGE:
//Platform::DebugPrintf("Setting Changed\n");
#if defined(USE_D2D)
if (technology != Technology::Default) {
UpdateRenderingParams(true);
}
#endif
UpdateBaseElements();
InvalidateStyleData();
// Get Intellimouse scroll line parameters
GetIntelliMouseParameters();
InvalidateStyleRedraw();
break;
case WM_GETDLGCODE:
@ -2081,7 +2130,17 @@ sptr_t ScintillaWin::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
case WM_NCLBUTTONDOWN:
case WM_SYSCOMMAND:
case WM_WINDOWPOSCHANGING:
return ::DefWindowProc(MainHWND(), msg, wParam, lParam);
case WM_WINDOWPOSCHANGED:
#if defined(USE_D2D)
if (technology != Technology::Default) {
if (UpdateRenderingParams(false)) {
DropGraphics();
Redraw();
}
}
#endif
return ::DefWindowProc(MainHWND(), msg, wParam, lParam);
case WM_GETTEXTLENGTH:
@ -3562,11 +3621,11 @@ LRESULT PASCAL ScintillaWin::CTWndProc(
#endif
RECT rc;
GetClientRect(hWnd, &rc);
// Create a Direct2D render target.
if (sciThis->technology == Technology::Default) {
surfaceWindow->Init(ps.hdc, hWnd);
} else {
#if defined(USE_D2D)
// Create a Direct2D render target.
D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp {};
dhrtp.hwnd = hWnd;
dhrtp.pixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
@ -3595,7 +3654,8 @@ LRESULT PASCAL ScintillaWin::CTWndProc(
}
#endif
}
surfaceWindow->SetMode(SurfaceMode(sciThis->ct.codePage, sciThis->BidirectionalR2L()));
surfaceWindow->SetMode(sciThis->CurrentSurfaceMode());
sciThis->SetRenderingParams(surfaceWindow.get());
sciThis->ct.PaintCT(surfaceWindow.get());
#if defined(USE_D2D)
if (pCTRenderTarget)

View File

@ -21,7 +21,7 @@ LD=link
!IFDEF SUPPORT_XP
ADD_DEFINE=-D_USING_V110_SDK71_
# Different subsystems for 32-bit and 64-bit Windows XP so detect based on Platform
# environment vairable set by vcvars*.bat to be either x86 or x64
# environment variable set by vcvars*.bat to be either x86 or x64
!IF "$(PLATFORM)" == "x64"
SUBSYSTEM=-SUBSYSTEM:WINDOWS,5.02
!ELSE