Updated to Scintilla 5.4.2 & Lexilla 5.3.1

https://www.scintilla.org/scintilla542.zip
Release 5.4.2

    Released 5 March 2024.
    Significantly reduce memory used for undo actions, often to a half or quarter of previous versions. Feature #1458.
    Add APIs for saving and restoring undo history.
    For GTK, when laying out text, detect runs with both left-to-right and right-to-left ranges and divide into an ASCII prefix and more complex suffix. Lay out the ASCII prefix in the standard manner but, for the suffix, measure the whole width and spread that over the suffix bytes. This produces more usable results where the caret moves over the ASCII prefix correctly and over the suffix reasonably but not accurately.
    For ScintillaEdit on Qt, fix reference from ScintillaDocument to Document to match change in 5.4.1 using IDocumentEditable for SCI_GETDOCPOINTER and SCI_SETDOCPOINTER.
    For Direct2D on Win32, use the multi-threaded option to avoid crashes when Scintilla instances created on different threads. There may be more problems with this scenario so it should be avoided. Bug #2420.
    For Win32, ensure keyboard-initiated context menu appears in multi-screen situations.

https://www.scintilla.org/lexilla531.zip
Release 5.3.1

    Released 5 March 2024.
    Assembler: After comments, treat \r\n line ends the same as \n. This makes testing easier.
    Bash: Fix folding when line changed to/from comment and previous line is comment. Issue #224.
    Batch: Fix handling ':' next to keywords. Issue #222.
    JavaScript: in cpp lexer, add lexer.cpp.backquoted.strings=2 mode to treat ` back-quoted strings as template literals which allow embedded ${expressions}. Issue #94.
    Python: fix lexing of rb'' and rf'' strings. Issue #223, Pull request #227.
    Ruby: fix lexing of methods on numeric literals like '3.times' so the '.' and method name do not appear in numeric style. Issue #225.
This commit is contained in:
Christian Grasser 2024-03-06 22:05:54 +01:00 committed by Don Ho
parent aa0be9973b
commit 0f3ae2e3d5
126 changed files with 3950 additions and 880 deletions

View File

@ -29,10 +29,12 @@
**.aspx text
**.php text
**.vb text
**.asm text
**.cmake text
**.d text
**.diff text
**.erl text
**.f text
**.gd text
**.gui text
**.iss text

1
lexilla/.gitignore vendored
View File

@ -1,6 +1,5 @@
*.o
*.a
*.asm
*.lib
*.obj
*.iobj

View File

@ -21,6 +21,10 @@ passedByValue
// cppcheck 2.11 can't find system headers on Win32.
missingIncludeSystem
// Passing temporary string into hidden object creator functions: they do not hold the argument
danglingTemporaryLifetime:lexilla/access/LexillaAccess.cxx
returnDanglingLifetime:lexilla/access/LexillaAccess.cxx
// cppcheck seems to believe that unique_ptr<char *[]>::get returns void* instead of char**
arithOperationsOnVoidPointer:lexilla/lexlib/WordList.cxx
@ -41,6 +45,7 @@ constVariablePointer:lexilla/examples/CheckLexilla/CheckLexilla.c
// Suppress most lexer warnings since the lexers are maintained by others
redundantCondition:lexilla/lexers/LexA68k.cxx
constParameterReference:lexilla/lexers/LexAbaqus.cxx
constParameterReference:lexilla/lexers/LexAda.cxx
constParameterReference:lexilla/lexers/LexAsciidoc.cxx
constParameterCallback:lexilla/lexers/LexAsn1.cxx
knownConditionTrueFalse:lexilla/lexers/LexAU3.cxx
@ -53,16 +58,20 @@ constParameterReference:lexilla/lexers/LexBash.cxx
knownConditionTrueFalse:lexilla/lexers/LexBash.cxx
variableScope:lexilla/lexers/LexBash.cxx
constVariable:lexilla/lexers/LexBasic.cxx
variableScope:lexilla/lexers/LexCmake.cxx
knownConditionTrueFalse:lexilla/lexers/LexCmake.cxx
constParameterReference:lexilla/lexers/LexCLW.cxx
knownConditionTrueFalse:lexilla/lexers/LexCLW.cxx
variableScope:lexilla/lexers/LexCmake.cxx
knownConditionTrueFalse:lexilla/lexers/LexCmake.cxx
constParameterReference:lexilla/lexers/LexCmake.cxx
constParameterReference:lexilla/lexers/LexCOBOL.cxx
constParameterReference:lexilla/lexers/LexCoffeeScript.cxx
constParameterPointer:lexilla/lexers/LexCoffeeScript.cxx
knownConditionTrueFalse:lexilla/lexers/LexCoffeeScript.cxx
constVariableReference:lexilla/lexers/LexConf.cxx
constParameterReference:lexilla/lexers/LexCPP.cxx
variableScope:lexilla/lexers/LexCSS.cxx
knownConditionTrueFalse:lexilla/lexers/LexDataflex.cxx
constParameterReference:lexilla/lexers/LexDataflex.cxx
variableScope:lexilla/lexers/LexDataflex.cxx
knownConditionTrueFalse:lexilla/lexers/LexECL.cxx
variableScope:lexilla/lexers/LexECL.cxx
@ -75,6 +84,7 @@ constParameter:lexilla/lexers/LexFortran.cxx
constParameterReference:lexilla/lexers/LexFortran.cxx
redundantContinue:lexilla/lexers/LexFortran.cxx
knownConditionTrueFalse:lexilla/lexers/LexFSharp.cxx
constParameterReference:lexilla/lexers/LexGAP.cxx
constParameterReference:lexilla/lexers/LexGDScript.cxx
variableScope:lexilla/lexers/LexGui4Cli.cxx
constParameterReference:lexilla/lexers/LexHaskell.cxx
@ -83,6 +93,8 @@ knownConditionTrueFalse:lexilla/lexers/LexHex.cxx
constParameterReference:lexilla/lexers/LexHTML.cxx
constVariable:lexilla/lexers/LexHollywood.cxx
variableScope:lexilla/lexers/LexInno.cxx
constVariableReference:lexilla/lexers/LexInno.cxx
constParameterReference:lexilla/lexers/LexJSON.cxx
constParameterPointer:lexilla/lexers/LexJulia.cxx
constParameterReference:lexilla/lexers/LexJulia.cxx
knownConditionTrueFalse:lexilla/lexers/LexJulia.cxx
@ -108,18 +120,24 @@ constParameterReference:lexilla/lexers/LexNimrod.cxx
knownConditionTrueFalse:lexilla/lexers/LexNimrod.cxx
variableScope:lexilla/lexers/LexNimrod.cxx
variableScope:lexilla/lexers/LexNsis.cxx
constParameterReference:lexilla/lexers/LexNsis.cxx
knownConditionTrueFalse:lexilla/lexers/LexNsis.cxx
variableScope:lexilla/lexers/LexOpal.cxx
constParameterReference:lexilla/lexers/LexOpal.cxx
knownConditionTrueFalse:lexilla/lexers/LexOpal.cxx
constParameterReference:lexilla/lexers/LexOScript.cxx
constParameterReference:lexilla/lexers/LexPascal.cxx
variableScope:lexilla/lexers/LexPB.cxx
constParameterReference:lexilla/lexers/LexPerl.cxx
constVariableReference:lexilla/lexers/LexPerl.cxx
knownConditionTrueFalse:lexilla/lexers/LexPerl.cxx
constParameterReference:lexilla/lexers/LexPLM.cxx
constParameterReference:lexilla/lexers/LexPO.cxx
constParameterReference:lexilla/lexers/LexPython.cxx
shadowVariable:lexilla/lexers/LexPowerPro.cxx
knownConditionTrueFalse:lexilla/lexers/LexPowerPro.cxx
variableScope:lexilla/lexers/LexProgress.cxx
constParameterReference:lexilla/lexers/LexProgress.cxx
constParameterReference:lexilla/lexers/LexRaku.cxx
variableScope:lexilla/lexers/LexRaku.cxx
redundantInitialization:lexilla/lexers/LexRegistry.cxx
@ -128,10 +146,14 @@ constParameterReference:lexilla/lexers/LexRust.cxx
knownConditionTrueFalse:lexilla/lexers/LexScriptol.cxx
variableScope:lexilla/lexers/LexSpecman.cxx
unreadVariable:lexilla/lexers/LexSpice.cxx
constParameterReference:lexilla/lexers/LexSpice.cxx
constParameterReference:lexilla/lexers/LexSTTXT.cxx
constParameterReference:lexilla/lexers/LexTACL.cxx
knownConditionTrueFalse:lexilla/lexers/LexTACL.cxx
clarifyCalculation:lexilla/lexers/LexTADS3.cxx
constParameterReference:lexilla/lexers/LexTADS3.cxx
constParameterReference:lexilla/lexers/LexTAL.cxx
constVariableReference:lexilla/lexers/LexTCL.cxx
invalidscanf:lexilla/lexers/LexTCMD.cxx
constParameterReference:lexilla/lexers/LexTeX.cxx
variableScope:lexilla/lexers/LexTeX.cxx
@ -149,6 +171,7 @@ variableScope:lexilla/lexers/LexVHDL.cxx
unreadVariable:lexilla/lexers/LexVisualProlog.cxx
variableScope:lexilla/lexers/LexVisualProlog.cxx
shiftTooManyBitsSigned:lexilla/lexers/LexVisualProlog.cxx
iterateByValue:lexilla/lexers/LexVisualProlog.cxx
unreadVariable:lexilla/lexers/LexX12.cxx
constVariableReference:lexilla/lexers/LexX12.cxx
constParameterPointer:lexilla/lexers/LexX12.cxx
@ -165,6 +188,7 @@ constParameterCallback:lexilla/lexers/LexPython.cxx
constParameterCallback:lexilla/lexers/LexScriptol.cxx
constParameterCallback:lexilla/lexers/LexVB.cxx
constVariableReference:lexilla/lexers/LexBibTeX.cxx
constVariableReference:lexilla/lexers/LexCSS.cxx
constVariableReference:lexilla/lexers/LexCrontab.cxx
constVariableReference:lexilla/lexers/LexGui4Cli.cxx

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="20231227" />
<meta name="Date.Modified" content="20240305" />
<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.3.0<br />
Site last modified December 27 2023</font>
<font color="#FFCC99" size="3">Release version 5.3.1<br />
Site last modified March 5 2024</font>
</td>
<td width="20%">
&nbsp;
@ -77,11 +77,11 @@
</tr>
</table>
<ul id="versionlist">
<li>Version 5.3.1 improves Assembler, Bash, Batch, JavaScript, Python, and Ruby.</li>
<li>Version 5.3.0 improves Bash, HTML, and Lua.</li>
<li>Version 5.2.9 fixes potential problems on macOS 12 and older when built with Xcode 15.0.</li>
<li>Version 5.2.8 improves Python and R.</li>
<li>Version 5.2.7 improves Bash, F#, and HTML.</li>
<li>Version 5.2.6 improves Bash, Errorlist, HTML, Matlab, and Visual Prolog.</li>
</ul>
<ul id="menu">
<li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></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/lexilla530.zip">
<font size="4"> <a href="https://www.scintilla.org/lexilla531.zip">
Windows</a>&nbsp;&nbsp;
<a href="https://www.scintilla.org/lexilla530.tgz">
<a href="https://www.scintilla.org/lexilla531.tgz">
GTK/Linux</a>&nbsp;&nbsp;
</font>
</td>
@ -42,7 +42,7 @@
containing very few restrictions.
</p>
<h3>
Release 5.3.0
Release 5.3.1
</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/lexilla530.zip">zip format</a> (1.3M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/lexilla530.tgz">tgz format</a> (0.9M) commonly used on Linux and compatible operating systems</li>
<li><a href="https://www.scintilla.org/lexilla531.zip">zip format</a> (1.3M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/lexilla531.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

@ -584,9 +584,43 @@
<td>German Aizek</td>
<td>Tsuyoshi Miyake</td>
<td>Martin Schäfer</td>
<td>RainRat</td>
</tr>
</table>
<h2>Releases</h2>
<h3>
<a href="https://www.scintilla.org/lexilla531.zip">Release 5.3.1</a>
</h3>
<ul>
<li>
Released 5 March 2024.
</li>
<li>
Assembler: After comments, treat \r\n line ends the same as \n. This makes testing easier.
</li>
<li>
Bash: Fix folding when line changed to/from comment and previous line is comment.
<a href="https://github.com/ScintillaOrg/lexilla/issues/224">Issue #224</a>.
</li>
<li>
Batch: Fix handling ':' next to keywords.
<a href="https://github.com/ScintillaOrg/lexilla/issues/222">Issue #222</a>.
</li>
<li>
JavaScript: in cpp lexer, add lexer.cpp.backquoted.strings=2 mode to treat ` back-quoted
strings as template literals which allow embedded ${expressions}.
<a href="https://github.com/ScintillaOrg/lexilla/issues/94">Issue #94</a>.
</li>
<li>
Python: fix lexing of rb'' and rf'' strings.
<a href="https://github.com/ScintillaOrg/lexilla/issues/223">Issue #223</a>,
<a href="https://github.com/ScintillaOrg/lexilla/pull/227">Pull request #227</a>.
</li>
<li>
Ruby: fix lexing of methods on numeric literals like '3.times' so the '.' and method name do not appear in numeric style.
<a href="https://github.com/ScintillaOrg/lexilla/issues/225">Issue #225</a>.
</li>
</ul>
<h3>
<a href="https://www.scintilla.org/lexilla530.zip">Release 5.3.0</a>
</h3>
@ -1964,7 +1998,7 @@
</li>
<li>
Add SCI_SETTABMINIMUMWIDTH to set the minimum width of tabs.
This allows minimaps or overviews to be layed out to match the full size editing view.
This allows minimaps or overviews to be laid out to match the full size editing view.
<a href="https://sourceforge.net/p/scintilla/bugs/2118/">Bug #2118</a>.
</li>
<li>
@ -2533,7 +2567,7 @@
<a href="https://sourceforge.net/p/scintilla/feature-requests/1210/">Feature #1210.</a>
</li>
<li>
Fix hang in Lua lexer when lexing a label upto the terminating "::".
Fix hang in Lua lexer when lexing a label up to the terminating "::".
<a href="https://sourceforge.net/p/scintilla/bugs/1999/">Bug #1999</a>.
</li>
<li>
@ -2760,7 +2794,7 @@
'Ctrl+L|SCI_LINEDELETE|'.
</li>
<li>
The Matlab lexer treats 'end' as a number rather than a keyword when used as a index.
The Matlab lexer treats 'end' as a number rather than a keyword when used as an index.
This also stops incorrect folding.
<a href="https://sourceforge.net/p/scintilla/bugs/1951/">Bug #1951</a>.
</li>
@ -9803,7 +9837,7 @@
SciTE crashing bug fixed in incremental search on Windows ME.
</li>
<li>
SciTE on Windows has a optional find and replace dialogs that can search through
SciTE on Windows has an optional find and replace dialogs that can search through
all buffers and search within a particular style number.
</li>
</ul>
@ -11372,7 +11406,7 @@
SciTE's Export as LaTeX output improved.
</li>
<li>
Better choice of autocompletion displaying above the caret rather then
Better choice of autocompletion displaying above the caret rather than
below when that is more sensible.
</li>
<li>
@ -13957,7 +13991,7 @@
<li>
Scintilla supports a 'savepoint' in the undo stack which can be set by the container when
the document is saved. Notifications are sent to the container when the savepoint is
entered or left, allowing the container to to display a dirty indicator and change its
entered or left, allowing the container to display a dirty indicator and change its
menus.
</li>
<li>

View File

@ -265,11 +265,19 @@ void SCI_METHOD LexerAsm::Lex(Sci_PositionU startPos, Sci_Position length, int i
for (; sc.More(); sc.Forward())
{
if (sc.atLineStart) {
switch (sc.state) {
case SCE_ASM_STRING:
case SCE_ASM_CHARACTER:
// Prevent SCE_ASM_STRINGEOL from leaking back to previous line
if (sc.atLineStart && (sc.state == SCE_ASM_STRING)) {
sc.SetState(SCE_ASM_STRING);
} else if (sc.atLineStart && (sc.state == SCE_ASM_CHARACTER)) {
sc.SetState(SCE_ASM_CHARACTER);
sc.SetState(sc.state);
break;
case SCE_ASM_COMMENT:
sc.SetState(SCE_ASM_DEFAULT);
break;
default:
break;
}
}
// Handle line continuation generically.
@ -326,15 +334,11 @@ void SCI_METHOD LexerAsm::Lex(Sci_PositionU startPos, Sci_Position length, int i
} else if (sc.state == SCE_ASM_COMMENTDIRECTIVE) {
char delimiter = options.delimiter.empty() ? '~' : options.delimiter.c_str()[0];
if (sc.ch == delimiter) {
while (!sc.atLineEnd) {
while (!sc.MatchLineEnd()) {
sc.Forward();
}
sc.SetState(SCE_ASM_DEFAULT);
}
} else if (sc.state == SCE_ASM_COMMENT ) {
if (sc.atLineEnd) {
sc.SetState(SCE_ASM_DEFAULT);
}
} else if (sc.state == SCE_ASM_STRING) {
if (sc.ch == '\\') {
if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {

View File

@ -16,6 +16,7 @@
#include <string_view>
#include <vector>
#include <map>
#include <initializer_list>
#include <functional>
#include "ILexer.h"
@ -23,6 +24,7 @@
#include "SciLexer.h"
#include "StringCopy.h"
#include "InList.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "StyleContext.h"
@ -63,7 +65,7 @@ enum class CmdState {
Delimiter,
};
enum class CommandSubstitution {
enum class CommandSubstitution : int {
Backtick,
Inside,
InsideTrack,
@ -181,7 +183,7 @@ struct OptionsBash {
bool stylingInsideParameter = false;
bool stylingInsideHeredoc = false;
bool nestedBackticks = true;
int commandSubstitution = static_cast<int>(CommandSubstitution::Backtick);
CommandSubstitution commandSubstitution = CommandSubstitution::Backtick;
std::string specialParameter = BASH_SPECIAL_PARAMETER;
[[nodiscard]] bool stylingInside(int state) const noexcept {
@ -622,7 +624,7 @@ void SCI_METHOD LexerBash::Lex(Sci_PositionU startPos, Sci_Position length, int
QuoteStackCls QuoteStack(setParamStart);
QuoteStack.nestedBackticks = options.nestedBackticks;
QuoteStack.commandSubstitution = static_cast<CommandSubstitution>(options.commandSubstitution);
QuoteStack.commandSubstitution = options.commandSubstitution;
const WordClassifier &classifierIdentifiers = subStyles.Classifier(SCE_SH_IDENTIFIER);
const WordClassifier &classifierScalars = subStyles.Classifier(SCE_SH_SCALAR);
@ -1187,10 +1189,17 @@ void SCI_METHOD LexerBash::Fold(Sci_PositionU startPos_, Sci_Position length, in
LexAccessor styler(pAccess);
const Sci_Position startPos = startPos_;
Sci_Position startPos = startPos_;
const Sci_Position endPos = startPos + length;
int visibleChars = 0;
Sci_Position lineCurrent = styler.GetLine(startPos);
// Backtrack to previous line in case need to fix its fold status
if (lineCurrent > 0) {
lineCurrent--;
startPos = styler.LineStart(lineCurrent);
initStyle = (startPos > 0) ? styler.StyleIndexAt(startPos - 1) : 0;
}
int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
int levelCurrent = levelPrev;
char chNext = styler[startPos];
@ -1215,28 +1224,32 @@ void SCI_METHOD LexerBash::Fold(Sci_PositionU startPos_, Sci_Position length, in
&& !IsCommentLine(lineCurrent + 1, styler))
levelCurrent--;
}
if (style == SCE_SH_WORD) {
switch (style) {
case SCE_SH_WORD:
if ((wordlen + 1) < sizeof(word))
word[wordlen++] = ch;
if (styleNext != style) {
word[wordlen] = '\0';
wordlen = 0;
if (strcmp(word, "if") == 0 || strcmp(word, "case") == 0 || strcmp(word, "do") == 0) {
if (InList(word, {"if", "case", "do"})) {
levelCurrent++;
} else if (strcmp(word, "fi") == 0 || strcmp(word, "esac") == 0 || strcmp(word, "done") == 0) {
} else if (InList(word, {"fi", "esac", "done"})) {
levelCurrent--;
}
}
}
if (style == SCE_SH_OPERATOR) {
break;
case SCE_SH_OPERATOR:
if (ch == '{') {
levelCurrent++;
} else if (ch == '}') {
levelCurrent--;
}
}
break;
// Here Document folding
if (style == SCE_SH_HERE_DELIM) {
case SCE_SH_HERE_DELIM:
if (stylePrev == SCE_SH_HERE_Q) {
levelCurrent--;
} else if (stylePrev != SCE_SH_HERE_DELIM) {
@ -1246,9 +1259,14 @@ void SCI_METHOD LexerBash::Fold(Sci_PositionU startPos_, Sci_Position length, in
}
}
}
} else if (style == SCE_SH_HERE_Q && styleNext == SCE_SH_DEFAULT) {
break;
case SCE_SH_HERE_Q:
if (styleNext == SCE_SH_DEFAULT) {
levelCurrent--;
}
break;
}
if (atEOL) {
int lev = levelPrev;
if (visibleChars == 0 && options.foldCompact)

View File

@ -14,11 +14,13 @@
#include <string>
#include <string_view>
#include <initializer_list>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "InList.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
@ -200,18 +202,19 @@ void ColouriseBatchDoc(
styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
}
char wordBuffer[81]{}; // Word Buffer - large to catch long paths
// Copy word from Line Buffer into Word Buffer
// Copy word from Line Buffer into Word Buffer and convert to lower case
Sci_PositionU wbl = 0; // Word Buffer Length
for (; offset < lengthLine && wbl < 80 &&
!isspacechar(lineBuffer[offset]); wbl++, offset++) {
wordBuffer[wbl] = tolower(lineBuffer[offset]);
wordBuffer[wbl] = MakeLowerCase(lineBuffer[offset]);
}
wordBuffer[wbl] = '\0';
const std::string_view wordView(wordBuffer);
Sci_PositionU wbo = 0; // Word Buffer Offset - also Special Keyword Buffer Length
// Check for Comment - return if found
if (continueProcessing) {
if ((CompareCaseInsensitive(wordBuffer, "rem") == 0) || (wordBuffer[0] == ':' && wordBuffer[1] == ':')) {
if ((wordView == "rem") || (wordBuffer[0] == ':' && wordBuffer[1] == ':')) {
if ((offset == wbl) || !textQuoted(lineBuffer, offset - wbl)) {
styler.ColourTo(startLine + offset - wbl - 1, SCE_BAT_DEFAULT);
styler.ColourTo(endPos, SCE_BAT_COMMENT);
@ -248,19 +251,16 @@ void ColouriseBatchDoc(
} else if ((keywords.InList(wordBuffer)) &&
(continueProcessing)) {
// ECHO, GOTO, PROMPT and SET require no further Regular Keyword Checking
if ((CompareCaseInsensitive(wordBuffer, "echo") == 0) ||
(CompareCaseInsensitive(wordBuffer, "goto") == 0) ||
(CompareCaseInsensitive(wordBuffer, "prompt") == 0)) {
if (InList(wordView, {"echo", "goto", "prompt"})) {
continueProcessing = false;
}
// SET requires additional processing for the assignment operator
if (CompareCaseInsensitive(wordBuffer, "set") == 0) {
if (wordView == "set") {
continueProcessing = false;
isNotAssigned=true;
}
// Identify External Command / Program Location for ERRORLEVEL, and EXIST
if ((CompareCaseInsensitive(wordBuffer, "errorlevel") == 0) ||
(CompareCaseInsensitive(wordBuffer, "exist") == 0)) {
if (InList(wordView, {"errorlevel", "exist"})) {
// Reset External Command / Program Location
cmdLoc = offset;
// Skip next spaces
@ -279,10 +279,7 @@ void ColouriseBatchDoc(
cmdLoc++;
}
// Identify External Command / Program Location for CALL, DO, LOADHIGH and LH
} else if ((CompareCaseInsensitive(wordBuffer, "call") == 0) ||
(CompareCaseInsensitive(wordBuffer, "do") == 0) ||
(CompareCaseInsensitive(wordBuffer, "loadhigh") == 0) ||
(CompareCaseInsensitive(wordBuffer, "lh") == 0)) {
} else if (InList(wordView, {"call", "do", "loadhigh", "lh"})) {
// Reset External Command / Program Location
cmdLoc = offset;
// Skip next spaces
@ -290,6 +287,11 @@ void ColouriseBatchDoc(
(isspacechar(lineBuffer[cmdLoc]))) {
cmdLoc++;
}
// Check if call is followed by a label
if ((lineBuffer[cmdLoc] == ':') &&
(wordView == "call")) {
continueProcessing = false;
}
}
// Colorize Regular keyword
styler.ColourTo(startLine + offset - 1, SCE_BAT_WORD);
@ -317,10 +319,12 @@ void ColouriseBatchDoc(
// Check for Special Keyword in list
if ((keywords.InList(sKeywordBuffer)) &&
((IsBOperator(wordBuffer[wbo])) ||
(IsBSeparator(wordBuffer[wbo])))) {
(IsBSeparator(wordBuffer[wbo])) ||
(wordBuffer[wbo] == ':' &&
(InList(sKeywordBuffer, {"call", "echo", "goto"}) )))) {
sKeywordFound = true;
// ECHO requires no further Regular Keyword Checking
if (CompareCaseInsensitive(sKeywordBuffer, "echo") == 0) {
if (std::string_view(sKeywordBuffer) == "echo") {
continueProcessing = false;
}
// Colorize Special Keyword as Regular Keyword
@ -347,11 +351,11 @@ void ColouriseBatchDoc(
// Reset Offset to re-process remainder of word
offset -= (wbl - wbo);
// CHOICE requires no further Regular Keyword Checking
if (CompareCaseInsensitive(wordBuffer, "choice") == 0) {
if (wordView == "choice") {
continueProcessing = false;
}
// Check for START (and its switches) - What follows is External Command \ Program
if (CompareCaseInsensitive(wordBuffer, "start") == 0) {
if (wordView == "start") {
// Reset External Command / Program Location
cmdLoc = offset;
// Skip next spaces
@ -576,7 +580,7 @@ void ColouriseBatchDoc(
isNotAssigned=false;
}
// Colorize Other Operators
// Do not Colorize Paranthesis, quoted text and escaped operators
// Do not Colorize Parenthesis, quoted text and escaped operators
if (((wordBuffer[0] != ')') && (wordBuffer[0] != '(')
&& !textQuoted(lineBuffer, offset - wbl) && !IsEscaped(lineBuffer,offset - wbl + wbo))
&& !((wordBuffer[0] == '=') && !isNotAssigned ))

View File

@ -167,9 +167,11 @@ class EscapeSequence {
const CharacterSet *escapeSetValid = nullptr;
int digitsLeft = 0;
public:
int outerState = SCE_C_DEFAULT;
EscapeSequence() = default;
void resetEscapeState(int nextChar) noexcept {
void resetEscapeState(int state, int nextChar) noexcept {
digitsLeft = 0;
outerState = state;
escapeSetValid = &setNoneNumeric;
if (nextChar == 'U') {
digitsLeft = 9;
@ -185,7 +187,7 @@ public:
escapeSetValid = &setOctDigits;
}
}
bool atEscapeEnd(int currChar) const noexcept {
[[nodiscard]] bool atEscapeEnd(int currChar) const noexcept {
return (digitsLeft <= 0) || !escapeSetValid->Contains(currChar);
}
void consumeDigit() noexcept {
@ -254,28 +256,27 @@ class LinePPState {
// level is the nesting level of #if constructs
int level = -1;
static const int maximumNestingLevel = 31;
int maskLevel() const noexcept {
[[nodiscard]] int maskLevel() const noexcept {
if (level >= 0) {
return 1 << level;
} else {
return 1;
}
return 1;
}
public:
LinePPState() noexcept = default;
bool ValidLevel() const noexcept {
[[nodiscard]] bool ValidLevel() const noexcept {
return level >= 0 && level < maximumNestingLevel;
}
bool IsActive() const noexcept {
[[nodiscard]] bool IsActive() const noexcept {
return state == 0;
}
bool IsInactive() const noexcept {
[[nodiscard]] bool IsInactive() const noexcept {
return state != 0;
}
int ActiveState() const noexcept {
[[nodiscard]] int ActiveState() const noexcept {
return state ? inactiveFlag : 0;
}
bool CurrentIfTaken() const noexcept {
[[nodiscard]] bool CurrentIfTaken() const noexcept {
return (ifTaken & maskLevel()) != 0;
}
void StartSection(bool on) noexcept {
@ -310,7 +311,7 @@ public:
class PPStates {
std::vector<LinePPState> vlls;
public:
LinePPState ForLine(Sci_Position line) const noexcept {
[[nodiscard]] LinePPState ForLine(Sci_Position line) const noexcept {
if ((line > 0) && (vlls.size() > static_cast<size_t>(line))) {
return vlls[line];
}
@ -322,6 +323,18 @@ public:
}
};
enum class BackQuotedString : int {
None,
RawString,
TemplateLiteral,
};
// string interpolating state
struct InterpolatingState {
int state;
int braceCount;
};
// An individual named option for use in an OptionSet
// Options used for LexerCPP
@ -333,7 +346,7 @@ struct OptionsCPP {
bool verbatimStringsAllowEscapes = false;
bool triplequotedStrings = false;
bool hashquotedStrings = false;
bool backQuotedStrings = false;
BackQuotedString backQuotedStrings = BackQuotedString::None;
bool escapeSequence = false;
bool fold = false;
bool foldSyntaxBased = true;
@ -385,7 +398,10 @@ struct OptionSetCPP : public OptionSet<OptionsCPP> {
"Set to 1 to enable highlighting of hash-quoted strings.");
DefineProperty("lexer.cpp.backquoted.strings", &OptionsCPP::backQuotedStrings,
"Set to 1 to enable highlighting of back-quoted raw strings .");
"Set how to highlighting back-quoted strings. "
"0 (the default) no highlighting. "
"1 highlighted as Go raw string. "
"2 highlighted as JavaScript template literal.");
DefineProperty("lexer.cpp.escape.sequence", &OptionsCPP::escapeSequence,
"Set to 1 to enable highlighting of escape sequences in strings");
@ -480,6 +496,7 @@ class LexerCPP : public ILexer5 {
CharacterSet setWordStart;
PPStates vlls;
std::vector<PPDefinition> ppDefineHistory;
std::map<Sci_Position, std::vector<InterpolatingState>> interpolatingAtEol;
WordList keywords;
WordList keywords2;
WordList keywords3;
@ -497,11 +514,11 @@ class LexerCPP : public ILexer5 {
arguments.clear();
return *this;
}
bool IsMacro() const noexcept {
[[nodiscard]] bool IsMacro() const noexcept {
return !arguments.empty();
}
};
typedef std::map<std::string, SymbolValue> SymbolTable;
using SymbolTable = std::map<std::string, SymbolValue>;
SymbolTable preprocessorDefinitionsStart;
OptionsCPP options;
OptionSetCPP osCPP;
@ -526,8 +543,7 @@ public:
LexerCPP(LexerCPP &&) = delete;
void operator=(const LexerCPP &) = delete;
void operator=(LexerCPP &&) = delete;
virtual ~LexerCPP() {
}
virtual ~LexerCPP() = default;
void SCI_METHOD Release() noexcept override {
delete this;
}
@ -661,7 +677,7 @@ public:
return style & ~inactiveFlag;
}
void EvaluateTokens(Tokens &tokens, const SymbolTable &preprocessorDefinitions);
Tokens Tokenize(const std::string &expr) const;
[[nodiscard]] Tokens Tokenize(const std::string &expr) const;
bool EvaluateExpression(const std::string &expr, const SymbolTable &preprocessorDefinitions);
};
@ -718,21 +734,19 @@ Sci_Position SCI_METHOD LexerCPP::WordListSet(int n, const char *wl) {
const char *cpEquals = strchr(cpDefinition, '=');
if (cpEquals) {
std::string name(cpDefinition, cpEquals - cpDefinition);
std::string val(cpEquals+1);
const std::string val(cpEquals+1);
const size_t bracket = name.find('(');
const size_t bracketEnd = name.find(')');
if ((bracket != std::string::npos) && (bracketEnd != std::string::npos)) {
// Macro
std::string args = name.substr(bracket + 1, bracketEnd - bracket - 1);
const std::string args = name.substr(bracket + 1, bracketEnd - bracket - 1);
name = name.substr(0, bracket);
preprocessorDefinitionsStart[name] = SymbolValue(val, args);
} else {
preprocessorDefinitionsStart[name] = val;
}
} else {
std::string name(cpDefinition);
std::string val("1");
preprocessorDefinitionsStart[name] = val;
preprocessorDefinitionsStart[std::string(cpDefinition)] = std::string("1");
}
}
}
@ -771,7 +785,21 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i
bool inRERange = false;
bool seenDocKeyBrace = false;
std::vector<InterpolatingState> interpolatingStack;
Sci_Position lineCurrent = styler.GetLine(startPos);
if (options.backQuotedStrings == BackQuotedString::TemplateLiteral) {
// code copied from LexPython
auto it = interpolatingAtEol.find(lineCurrent - 1);
if (it != interpolatingAtEol.end()) {
interpolatingStack = it->second;
}
it = interpolatingAtEol.lower_bound(lineCurrent);
if (it != interpolatingAtEol.end()) {
interpolatingAtEol.erase(it, interpolatingAtEol.end());
}
}
if ((MaskActive(initStyle) == SCE_C_PREPROCESSOR) ||
(MaskActive(initStyle) == SCE_C_COMMENTLINE) ||
(MaskActive(initStyle) == SCE_C_COMMENTLINEDOC)) {
@ -864,6 +892,9 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i
if (!rawStringTerminator.empty()) {
rawSTNew.Set(lineCurrent-1, rawStringTerminator);
}
if (!interpolatingStack.empty()) {
interpolatingAtEol[sc.currentLine] = interpolatingStack;
}
}
// Handle line continuation generically.
@ -1051,7 +1082,7 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i
} else {
sc.GetCurrentString(currentText, transform);
assert(!currentText.empty());
std::string currentSuffix = currentText.substr(1);
const std::string currentSuffix = currentText.substr(1);
if (!keywords3.InList(currentSuffix) && !keywords3.InList(currentText)) {
const int subStyleCDKW = classifierDocKeyWords.ValueFor(currentSuffix);
if (subStyleCDKW >= 0) {
@ -1087,8 +1118,8 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i
}
} else if (sc.ch == '\\') {
if (options.escapeSequence) {
escapeSeq.resetEscapeState(sc.state, sc.chNext);
sc.SetState(SCE_C_ESCAPESEQUENCE|activitySet);
escapeSeq.resetEscapeState(sc.chNext);
}
sc.Forward(); // Skip all characters after the backslash
} else if (sc.ch == '\"') {
@ -1101,20 +1132,9 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i
break;
case SCE_C_ESCAPESEQUENCE:
escapeSeq.consumeDigit();
if (!escapeSeq.atEscapeEnd(sc.ch)) {
break;
}
if (sc.ch == '"') {
sc.SetState(SCE_C_STRING|activitySet);
sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
} else if (sc.ch == '\\') {
escapeSeq.resetEscapeState(sc.chNext);
sc.Forward();
} else {
sc.SetState(SCE_C_STRING|activitySet);
if (sc.atLineEnd) {
sc.ChangeState(SCE_C_STRINGEOL|activitySet);
}
if (escapeSeq.atEscapeEnd(sc.ch)) {
sc.SetState(escapeSeq.outerState);
continue;
}
break;
case SCE_C_HASHQUOTEDSTRING:
@ -1131,8 +1151,22 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i
for (size_t termPos=rawStringTerminator.size(); termPos; termPos--)
sc.Forward();
sc.SetState(SCE_C_DEFAULT|activitySet);
if (interpolatingStack.empty()) {
rawStringTerminator.clear();
}
} else if (options.backQuotedStrings == BackQuotedString::TemplateLiteral) {
if (sc.ch == '\\') {
if (options.escapeSequence) {
escapeSeq.resetEscapeState(sc.state, sc.chNext);
sc.SetState(SCE_C_ESCAPESEQUENCE|activitySet);
}
sc.Forward(); // Skip all characters after the backslash
} else if (sc.Match('$', '{')) {
interpolatingStack.push_back({sc.state, 1});
sc.SetState(SCE_C_OPERATOR|activitySet);
sc.Forward();
}
}
break;
case SCE_C_CHARACTER:
if (sc.atLineEnd) {
@ -1222,7 +1256,7 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i
} else if (options.hashquotedStrings && sc.Match('#', '\"')) {
sc.SetState(SCE_C_HASHQUOTEDSTRING|activitySet);
sc.Forward();
} else if (options.backQuotedStrings && sc.Match('`')) {
} else if ((options.backQuotedStrings != BackQuotedString::None) && sc.Match('`')) {
sc.SetState(SCE_C_STRINGRAW|activitySet);
rawStringTerminator = "`";
} else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
@ -1336,7 +1370,7 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i
// Inactive, if expression true then may become active if parent scope active
assert(sc.state == (SCE_C_PREPROCESSOR | inactiveFlag));
// Similar to #if
std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 4, true);
const std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 4, true);
const bool ifGood = EvaluateExpression(restOfLine, preprocessorDefinitions);
if (ifGood) {
preproc.InvertCurrentLevel();
@ -1365,13 +1399,13 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i
size_t endName = startName;
while ((endName < restOfLine.length()) && setWord.Contains(restOfLine[endName]))
endName++;
std::string key = restOfLine.substr(startName, endName-startName);
const std::string key = restOfLine.substr(startName, endName-startName);
if ((endName < restOfLine.length()) && (restOfLine.at(endName) == '(')) {
// Macro
size_t endArgs = endName;
while ((endArgs < restOfLine.length()) && (restOfLine[endArgs] != ')'))
endArgs++;
std::string args = restOfLine.substr(endName + 1, endArgs - endName - 1);
const std::string args = restOfLine.substr(endName + 1, endArgs - endName - 1);
size_t startValue = endArgs+1;
while ((startValue < restOfLine.length()) && IsSpaceOrTab(restOfLine[startValue]))
startValue++;
@ -1410,6 +1444,19 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i
}
} else if (isoperator(sc.ch)) {
sc.SetState(SCE_C_OPERATOR|activitySet);
if (!interpolatingStack.empty() && AnyOf(sc.ch, '{', '}')) {
InterpolatingState &current = interpolatingStack.back();
if (sc.ch == '{') {
current.braceCount += 1;
} else {
current.braceCount -= 1;
if (current.braceCount == 0) {
sc.ForwardSetState(current.state);
interpolatingStack.pop_back();
continue;
}
}
}
}
}

View File

@ -5,20 +5,21 @@
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cstdio>
#include <cstdarg>
#include <string>
#include <string_view>
#include <initializer_list>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "InList.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
@ -42,11 +43,7 @@ constexpr bool Is1To9(char ch) noexcept {
return (ch >= '1') && (ch <= '9');
}
bool IsAlphabetic(int ch) {
return IsASCII(ch) && isalpha(ch);
}
inline bool AtEOL(Accessor &styler, Sci_PositionU i) {
bool AtEOL(Accessor &styler, Sci_Position i) {
return (styler[i] == '\n') ||
((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
}
@ -255,18 +252,16 @@ int RecogniseErrorListLine(const char *lineBuffer, Sci_PositionU lengthLine, Sci
} else if ((ch == ':' && chNext == ' ') || (ch == ' ')) {
// Possibly Delphi.. don't test against chNext as it's one of the strings below.
char word[512];
unsigned numstep;
unsigned numstep = 0;
if (ch == ' ')
numstep = 1; // ch was ' ', handle as if it's a delphi errorline, only add 1 to i.
else
numstep = 2; // otherwise add 2.
Sci_PositionU chPos = 0;
for (Sci_PositionU j = i + numstep; j < lengthLine && IsAlphabetic(lineBuffer[j]) && chPos < sizeof(word) - 1; j++)
for (Sci_PositionU j = i + numstep; j < lengthLine && IsUpperOrLowerCase(lineBuffer[j]) && chPos < sizeof(word) - 1; j++)
word[chPos++] = lineBuffer[j];
word[chPos] = 0;
if (!CompareCaseInsensitive(word, "error") || !CompareCaseInsensitive(word, "warning") ||
!CompareCaseInsensitive(word, "fatal") || !CompareCaseInsensitive(word, "catastrophic") ||
!CompareCaseInsensitive(word, "note") || !CompareCaseInsensitive(word, "remark")) {
if (InListCaseInsensitive(word, {"error", "warning", "fatal", "catastrophic", "note", "remark"})) {
state = stMsVc;
} else {
state = stUnrecognized;
@ -363,12 +358,12 @@ void ColouriseErrorListLine(
int portionStyle = style;
while (const char *startSeq = strstr(linePortion, CSI)) {
if (startSeq > linePortion) {
styler.ColourTo(startPortion + static_cast<int>(startSeq - linePortion), portionStyle);
styler.ColourTo(startPortion + (startSeq - linePortion), portionStyle);
}
const char *endSeq = startSeq + 2;
while (!SequenceEnd(*endSeq))
endSeq++;
const Sci_Position endSeqPosition = startPortion + static_cast<Sci_Position>(endSeq - linePortion) + 1;
const Sci_Position endSeqPosition = startPortion + (endSeq - linePortion) + 1;
switch (*endSeq) {
case 0:
styler.ColourTo(endPos, SCE_ERR_ESCSEQ_UNKNOWN);
@ -433,4 +428,4 @@ const char *const emptyWordListDesc[] = {
}
LexerModule lmErrorList(SCLEX_ERRORLIST, ColouriseErrorListDoc, "errorlist", 0, emptyWordListDesc);
LexerModule lmErrorList(SCLEX_ERRORLIST, ColouriseErrorListDoc, "errorlist", nullptr, emptyWordListDesc);

View File

@ -19,7 +19,6 @@
#include "Scintilla.h"
#include "SciLexer.h"
#include "StringCopy.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
@ -109,7 +108,7 @@ class LexerLua : public DefaultLexer {
OptionSetLua osLua;
public:
explicit LexerLua() :
DefaultLexer("lua", SCLEX_LUA, lexicalClasses, ELEMENTS(lexicalClasses)) {
DefaultLexer("lua", SCLEX_LUA, lexicalClasses, std::size(lexicalClasses)) {
}
~LexerLua() override = default;
void SCI_METHOD Release() noexcept override {
@ -324,7 +323,7 @@ void LexerLua::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, I
sc.SetState(SCE_LUA_DEFAULT);
}
} else if (sc.state == SCE_LUA_IDENTIFIER) {
idenPos--; // commit already-scanned identitier/word parts
idenPos--; // commit already-scanned identifier/word parts
if (idenWordPos > 0) {
idenWordPos--;
sc.ChangeState(idenStyle);
@ -405,7 +404,7 @@ void LexerLua::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, I
if (sc.state == SCE_LUA_DEFAULT) {
if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
sc.SetState(SCE_LUA_NUMBER);
if (sc.ch == '0' && toupper(sc.chNext) == 'X') {
if (sc.ch == '0' && AnyOf(sc.chNext, 'x', 'X')) {
sc.Forward();
}
} else if (setWordStart.Contains(sc.ch)) {

View File

@ -20,7 +20,6 @@
#include "Scintilla.h"
#include "SciLexer.h"
#include "StringCopy.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
@ -87,6 +86,9 @@ constexpr bool IsRawPrefix(int ch) {
}
bool IsPyStringStart(int ch, int chNext, int chNext2, literalsAllowed allowed) noexcept {
// To cover both python2 and python3 lex character prefixes as --
// ur'' is a string, but ru'' is not
// fr'', rf'', br'', rb'' are all strings
if (IsQuote(ch))
return true;
if (IsPyStringTypeChar(ch, allowed)) {
@ -95,40 +97,39 @@ bool IsPyStringStart(int ch, int chNext, int chNext2, literalsAllowed allowed) n
if (IsRawPrefix(chNext) && IsQuote(chNext2))
return true;
}
if (IsRawPrefix(ch) && IsQuote(chNext))
if (IsRawPrefix(ch)) {
if (IsQuote(chNext))
return true;
if (IsPyStringTypeChar(chNext, allowed) && !AnyOf(chNext, 'u', 'U') && IsQuote(chNext2))
return true;
}
return false;
}
constexpr bool IsPyFStringState(int st) noexcept {
return ((st == SCE_P_FCHARACTER) || (st == SCE_P_FSTRING) ||
(st == SCE_P_FTRIPLE) || (st == SCE_P_FTRIPLEDOUBLE));
return AnyOf(st, SCE_P_FCHARACTER, SCE_P_FSTRING, SCE_P_FTRIPLE, SCE_P_FTRIPLEDOUBLE);
}
constexpr bool IsPySingleQuoteStringState(int st) noexcept {
return ((st == SCE_P_CHARACTER) || (st == SCE_P_STRING) ||
(st == SCE_P_FCHARACTER) || (st == SCE_P_FSTRING));
return AnyOf(st, SCE_P_CHARACTER, SCE_P_STRING, SCE_P_FCHARACTER, SCE_P_FSTRING);
}
constexpr bool IsPyTripleQuoteStringState(int st) noexcept {
return ((st == SCE_P_TRIPLE) || (st == SCE_P_TRIPLEDOUBLE) ||
(st == SCE_P_FTRIPLE) || (st == SCE_P_FTRIPLEDOUBLE));
return AnyOf(st, SCE_P_TRIPLE, SCE_P_TRIPLEDOUBLE, SCE_P_FTRIPLE, SCE_P_FTRIPLEDOUBLE);
}
char GetPyStringQuoteChar(int st) noexcept {
if ((st == SCE_P_CHARACTER) || (st == SCE_P_FCHARACTER) ||
(st == SCE_P_TRIPLE) || (st == SCE_P_FTRIPLE))
if (AnyOf(st, SCE_P_CHARACTER, SCE_P_FCHARACTER, SCE_P_TRIPLE, SCE_P_FTRIPLE))
return '\'';
if ((st == SCE_P_STRING) || (st == SCE_P_FSTRING) ||
(st == SCE_P_TRIPLEDOUBLE) || (st == SCE_P_FTRIPLEDOUBLE))
if (AnyOf(st, SCE_P_STRING, SCE_P_FSTRING, SCE_P_TRIPLEDOUBLE, SCE_P_FTRIPLEDOUBLE))
return '"';
return '\0';
}
void PushStateToStack(int state, std::vector<SingleFStringExpState> &stack, SingleFStringExpState *&currentFStringExp) {
SingleFStringExpState single = {state, 0};
const SingleFStringExpState single = {state, 0};
stack.push_back(single);
currentFStringExp = &stack.back();
@ -155,14 +156,22 @@ int PopFromStateStack(std::vector<SingleFStringExpState> &stack, SingleFStringEx
int GetPyStringState(Accessor &styler, Sci_Position i, Sci_PositionU *nextIndex, literalsAllowed allowed) {
char ch = styler.SafeGetCharAt(i);
char chNext = styler.SafeGetCharAt(i + 1);
const int firstIsF = (ch == 'f' || ch == 'F');
bool isFString = false;
// Advance beyond r, u, or ur prefix (or r, b, or br in Python 2.7+ and r, f, or fr in Python 3.6+), but bail if there are any unexpected chars
// Advance beyond r, a type char, or both (in either order)
// Note that this depends on IsPyStringStart to enforce ru'' not being a string
if (IsRawPrefix(ch)) {
i++;
if (IsPyStringTypeChar(chNext, allowed)) {
if (AnyOf(chNext, 'f', 'F'))
isFString = true;
i++;
}
ch = styler.SafeGetCharAt(i);
chNext = styler.SafeGetCharAt(i + 1);
} else if (IsPyStringTypeChar(ch, allowed)) {
if (AnyOf(ch, 'f', 'F'))
isFString = true;
if (IsRawPrefix(chNext))
i += 2;
else
@ -171,6 +180,7 @@ int GetPyStringState(Accessor &styler, Sci_Position i, Sci_PositionU *nextIndex,
chNext = styler.SafeGetCharAt(i + 1);
}
// ch and i will be the first quote
if (!IsQuote(ch)) {
*nextIndex = i + 1;
return SCE_P_DEFAULT;
@ -180,16 +190,16 @@ int GetPyStringState(Accessor &styler, Sci_Position i, Sci_PositionU *nextIndex,
*nextIndex = i + 3;
if (ch == '"')
return (firstIsF ? SCE_P_FTRIPLEDOUBLE : SCE_P_TRIPLEDOUBLE);
return (isFString ? SCE_P_FTRIPLEDOUBLE : SCE_P_TRIPLEDOUBLE);
else
return (firstIsF ? SCE_P_FTRIPLE : SCE_P_TRIPLE);
return (isFString ? SCE_P_FTRIPLE : SCE_P_TRIPLE);
} else {
*nextIndex = i + 1;
if (ch == '"')
return (firstIsF ? SCE_P_FSTRING : SCE_P_STRING);
return (isFString ? SCE_P_FSTRING : SCE_P_STRING);
else
return (firstIsF ? SCE_P_FCHARACTER : SCE_P_CHARACTER);
return (isFString ? SCE_P_FCHARACTER : SCE_P_CHARACTER);
}
}
@ -362,7 +372,7 @@ struct OptionSetPython : public OptionSet<OptionsPython> {
const char styleSubable[] = { SCE_P_IDENTIFIER, 0 };
LexicalClass lexicalClasses[] = {
const LexicalClass lexicalClasses[] = {
// Lexer Python SCLEX_PYTHON SCE_P_:
0, "SCE_P_DEFAULT", "default", "White space",
1, "SCE_P_COMMENTLINE", "comment line", "Comment",
@ -387,8 +397,6 @@ LexicalClass lexicalClasses[] = {
20, "SCE_P_ATTRIBUTE", "identifier", "Attribute of identifier",
};
}
class LexerPython : public DefaultLexer {
WordList keywords;
WordList keywords2;
@ -399,7 +407,7 @@ class LexerPython : public DefaultLexer {
std::map<Sci_Position, std::vector<SingleFStringExpState> > ftripleStateAtEol;
public:
explicit LexerPython() :
DefaultLexer("python", SCLEX_PYTHON, lexicalClasses, ELEMENTS(lexicalClasses)),
DefaultLexer("python", SCLEX_PYTHON, lexicalClasses, std::size(lexicalClasses)),
subStyles(styleSubable, 0x80, 0x40, 0) {
}
~LexerPython() override = default;
@ -568,10 +576,8 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
// Look for backslash-continued lines
while (lineCurrent > 0) {
const Sci_Position eolPos = styler.LineStart(lineCurrent) - 1;
const int eolStyle = styler.StyleAt(eolPos);
if (eolStyle == SCE_P_STRING
|| eolStyle == SCE_P_CHARACTER
|| eolStyle == SCE_P_STRINGEOL) {
const int eolStyle = styler.StyleIndexAt(eolPos);
if (AnyOf(eolStyle, SCE_P_STRING, SCE_P_CHARACTER, SCE_P_STRINGEOL)) {
lineCurrent -= 1;
} else {
break;
@ -579,7 +585,7 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
}
startPos = styler.LineStart(lineCurrent);
}
initStyle = startPos == 0 ? SCE_P_DEFAULT : styler.StyleAt(startPos - 1);
initStyle = startPos == 0 ? SCE_P_DEFAULT : styler.StyleIndexAt(startPos - 1);
}
const literalsAllowed allowedLiterals = options.AllowedLiterals();
@ -653,12 +659,12 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
}
} else if (sc.state == SCE_P_IDENTIFIER) {
if ((sc.ch == '.') || (!IsAWordChar(sc.ch, options.unicodeIdentifiers))) {
char s[100];
sc.GetCurrent(s, sizeof(s));
char identifier[100];
sc.GetCurrent(identifier, sizeof(identifier));
int style = SCE_P_IDENTIFIER;
if ((kwLast == kwImport) && (strcmp(s, "as") == 0)) {
if ((kwLast == kwImport) && (strcmp(identifier, "as") == 0)) {
style = SCE_P_WORD;
} else if (keywords.InList(s) && !IsMatchOrCaseIdentifier(sc, styler, s)) {
} else if (keywords.InList(identifier) && !IsMatchOrCaseIdentifier(sc, styler, identifier)) {
style = SCE_P_WORD;
} else if (kwLast == kwClass) {
style = SCE_P_CLASSNAME;
@ -681,7 +687,7 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
break;
}
}
} else if (keywords2.InList(s)) {
} else if (keywords2.InList(identifier)) {
if (options.keywords2NoSubIdentifiers) {
// We don't want to highlight keywords2
// that are used as a sub-identifier,
@ -693,7 +699,7 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
style = SCE_P_WORD2;
}
} else {
const int subStyle = classifierIdentifiers.ValueFor(s);
const int subStyle = classifierIdentifiers.ValueFor(identifier);
if (subStyle >= 0) {
style = subStyle;
}
@ -738,17 +744,17 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
sc.ChangeState(style);
sc.SetState(SCE_P_DEFAULT);
if (style == SCE_P_WORD) {
if (0 == strcmp(s, "class"))
if (0 == strcmp(identifier, "class"))
kwLast = kwClass;
else if (0 == strcmp(s, "def"))
else if (0 == strcmp(identifier, "def"))
kwLast = kwDef;
else if (0 == strcmp(s, "import"))
else if (0 == strcmp(identifier, "import"))
kwLast = kwImport;
else if (0 == strcmp(s, "cdef"))
else if (0 == strcmp(identifier, "cdef"))
kwLast = kwCDef;
else if (0 == strcmp(s, "cpdef"))
else if (0 == strcmp(identifier, "cpdef"))
kwLast = kwCPDef;
else if (0 == strcmp(s, "cimport"))
else if (0 == strcmp(identifier, "cimport"))
kwLast = kwImport;
else if (kwLast != kwCDef && kwLast != kwCPDef)
kwLast = kwOther;
@ -922,21 +928,21 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in
sc.Complete();
}
static bool IsCommentLine(Sci_Position line, Accessor &styler) {
bool IsCommentLine(Sci_Position line, Accessor &styler) {
const Sci_Position pos = styler.LineStart(line);
const Sci_Position eol_pos = styler.LineStart(line + 1) - 1;
for (Sci_Position i = pos; i < eol_pos; i++) {
const char ch = styler[i];
if (ch == '#')
return true;
else if (ch != ' ' && ch != '\t')
if (!IsASpaceOrTab(ch))
return false;
}
return false;
}
static bool IsQuoteLine(Sci_Position line, const Accessor &styler) {
const int style = styler.StyleAt(styler.LineStart(line));
bool IsQuoteLine(Sci_Position line, const Accessor &styler) {
const int style = styler.StyleIndexAt(styler.LineStart(line));
return IsPyTripleQuoteStringState(style);
}
@ -972,7 +978,7 @@ void SCI_METHOD LexerPython::Fold(Sci_PositionU startPos, Sci_Position length, i
startPos = styler.LineStart(lineCurrent);
int prev_state = SCE_P_DEFAULT;
if (lineCurrent >= 1)
prev_state = styler.StyleAt(startPos - 1);
prev_state = styler.StyleIndexAt(startPos - 1);
int prevQuote = options.foldQuotes && IsPyTripleQuoteStringState(prev_state);
// Process all characters to end of requested range or end of any triple quote
@ -989,7 +995,7 @@ void SCI_METHOD LexerPython::Fold(Sci_PositionU startPos, Sci_Position length, i
// Information about next line is only available if not at end of document
indentNext = styler.IndentAmount(lineNext, &spaceFlags, nullptr);
const Sci_Position lookAtPos = (styler.LineStart(lineNext) == styler.Length()) ? styler.Length() - 1 : styler.LineStart(lineNext);
const int style = styler.StyleAt(lookAtPos);
const int style = styler.StyleIndexAt(lookAtPos);
quote = options.foldQuotes && IsPyTripleQuoteStringState(style);
}
const bool quote_start = (quote && !prevQuote);
@ -1079,5 +1085,7 @@ void SCI_METHOD LexerPython::Fold(Sci_PositionU startPos, Sci_Position length, i
//styler.SetLevel(lineCurrent, indentCurrent);
}
}
LexerModule lmPython(SCLEX_PYTHON, LexerPython::LexerFactoryPython, "python",
pythonWordListDesc);

View File

@ -747,9 +747,6 @@ void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
QuoteCls Quote;
int numDots = 0; // For numbers --
// Don't start lexing in the middle of a num
synchronizeDocStart(startPos, length, initStyle, styler, false);
bool preferRE = true;
@ -880,7 +877,6 @@ void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
styler.ColourTo(i - 1, state);
state = SCE_RB_NUMBER;
is_real_number = true;
numDots = 0;
} else if (isHighBitChar(ch) || iswordstart(ch)) {
styler.ColourTo(i - 1, state);
state = SCE_RB_WORD;
@ -1285,14 +1281,11 @@ void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
preferRE = false;
advance_char(i, ch, chNext, chNext2);
}
} else if (isSafeAlnumOrHigh(ch) || ch == '_') {
} else if (isSafeAlnumOrHigh(ch) || ch == '_' || (ch == '.' && isSafeDigit(chNext))) {
// Keep going
} else if (ch == '.' && chNext == '.') {
++numDots;
styler.ColourTo(i - 1, state);
redo_char(i, ch, chNext, chNext2, state); // pass by ref
} else if (ch == '.' && ++numDots == 1) {
// Keep going
} else {
styler.ColourTo(i - 1, state);
redo_char(i, ch, chNext, chNext2, state); // pass by ref

View File

@ -9,6 +9,8 @@
#include <cstdlib>
#include <cassert>
#include <string_view>
#include "CharacterSet.h"
using namespace Lexilla;
@ -30,6 +32,18 @@ int CompareCaseInsensitive(const char *a, const char *b) noexcept {
return *a - *b;
}
bool EqualCaseInsensitive(std::string_view a, std::string_view b) noexcept {
if (a.length() != b.length()) {
return false;
}
for (size_t i = 0; i < a.length(); i++) {
if (MakeUpperCase(a[i]) != MakeUpperCase(b[i])) {
return false;
}
}
return true;
}
int CompareNCaseInsensitive(const char *a, const char *b, size_t len) noexcept {
while (*a && *b && len) {
if (*a != *b) {

View File

@ -187,6 +187,7 @@ constexpr T MakeLowerCase(T ch) noexcept {
}
int CompareCaseInsensitive(const char *a, const char *b) noexcept;
bool EqualCaseInsensitive(std::string_view a, std::string_view b) noexcept;
int CompareNCaseInsensitive(const char *a, const char *b, size_t len) noexcept;
}

37
lexilla/lexlib/InList.cxx Normal file
View File

@ -0,0 +1,37 @@
// Scintilla source code edit control
/** @file InList.cxx
** Check if a string is in a list.
**/
// Copyright 2024 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cassert>
#include <string>
#include <string_view>
#include <initializer_list>
#include "InList.h"
#include "CharacterSet.h"
namespace Lexilla {
bool InList(std::string_view value, std::initializer_list<std::string_view> list) noexcept {
for (const std::string_view element : list) {
if (value == element) {
return true;
}
}
return false;
}
bool InListCaseInsensitive(std::string_view value, std::initializer_list<std::string_view> list) noexcept {
for (const std::string_view element : list) {
if (EqualCaseInsensitive(value, element)) {
return true;
}
}
return false;
}
}

18
lexilla/lexlib/InList.h Normal file
View File

@ -0,0 +1,18 @@
// Scintilla source code edit control
/** @file InList.h
** Check if a string is in a list.
**/
// Copyright 2024 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef INLIST_H
#define INLIST_H
namespace Lexilla {
bool InList(std::string_view value, std::initializer_list<std::string_view> list) noexcept;
bool InListCaseInsensitive(std::string_view value, std::initializer_list<std::string_view> list) noexcept;
}
#endif

View File

@ -97,6 +97,15 @@ public:
nameToDef[name] = Option(ps, description);
AppendName(name);
}
template <typename E>
void DefineProperty(const char *name, E T::*pe, std::string_view description="") {
static_assert(std::is_enum<E>::value);
plcoi pi {};
static_assert(sizeof(pe) == sizeof(pi));
memcpy(&pi, &pe, sizeof(pe));
nameToDef[name] = Option(pi, description);
AppendName(name);
}
const char *PropertyNames() const noexcept {
return names.c_str();
}

View File

@ -242,7 +242,7 @@ bool WordList::InListAbbreviated(const char *s, const char marker) const noexcep
return false;
}
/** similar to InListAbbreviated, but word s can be a abridged version of a keyword.
/** similar to InListAbbreviated, but word s can be an abridged version of a keyword.
* eg. the keyword is defined as "after.~:". This means the word must have a prefix (begins with) of
* "after." and suffix (ends with) of ":" to be a keyword, Hence "after.field:" , "after.form.item:" are valid.
* Similarly "~.is.valid" keyword is suffix only... hence "field.is.valid" , "form.is.valid" are valid.

View File

@ -13,7 +13,7 @@ namespace Lexilla {
/**
*/
class WordList {
// Each word contains at least one character - a empty word acts as sentinel at the end.
// Each word contains at least one character - an empty word acts as sentinel at the end.
char **words;
char *list;
size_t len;

View File

@ -81,6 +81,7 @@
// lexlib
#include "StringCopy.h"
#include "PropSetSimple.h"
#include "InList.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"

View File

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

View File

@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */
00D544CC992062D2E3CD4BF6 /* LexGDScript.cxx in Sources */ = {isa = PBXBuildFile; fileRef = A383409E9A994F461550FEC1 /* LexGDScript.cxx */; };
283639BC268FD4EA009D58A1 /* LexAccessor.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 283639BB268FD4EA009D58A1 /* LexAccessor.cxx */; };
283A17AE2B47E61100DF5C82 /* InList.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 283A17AC2B47E61100DF5C82 /* InList.cxx */; };
283A17AF2B47E61100DF5C82 /* InList.h in Headers */ = {isa = PBXBuildFile; fileRef = 283A17AD2B47E61100DF5C82 /* InList.h */; };
28BA72AB24E34D5B00272C2D /* LexerBase.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 28BA728F24E34D5A00272C2D /* LexerBase.cxx */; };
28BA72AC24E34D5B00272C2D /* LexAccessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 28BA729024E34D5A00272C2D /* LexAccessor.h */; };
28BA72AD24E34D5B00272C2D /* DefaultLexer.h in Headers */ = {isa = PBXBuildFile; fileRef = 28BA729124E34D5A00272C2D /* DefaultLexer.h */; };
@ -161,6 +163,8 @@
/* Begin PBXFileReference section */
280262A5246DF655000DF3B8 /* liblexilla.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = liblexilla.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
283639BB268FD4EA009D58A1 /* LexAccessor.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LexAccessor.cxx; path = ../../lexlib/LexAccessor.cxx; sourceTree = "<group>"; };
283A17AC2B47E61100DF5C82 /* InList.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = InList.cxx; path = ../../lexlib/InList.cxx; sourceTree = "<group>"; };
283A17AD2B47E61100DF5C82 /* InList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InList.h; path = ../../lexlib/InList.h; sourceTree = "<group>"; };
28BA728F24E34D5A00272C2D /* LexerBase.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LexerBase.cxx; path = ../../lexlib/LexerBase.cxx; sourceTree = "<group>"; };
28BA729024E34D5A00272C2D /* LexAccessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LexAccessor.h; path = ../../lexlib/LexAccessor.h; sourceTree = "<group>"; };
28BA729124E34D5A00272C2D /* DefaultLexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DefaultLexer.h; path = ../../lexlib/DefaultLexer.h; sourceTree = "<group>"; };
@ -488,6 +492,8 @@
28BA72A124E34D5B00272C2D /* CharacterSet.h */,
28BA729C24E34D5A00272C2D /* DefaultLexer.cxx */,
28BA729124E34D5A00272C2D /* DefaultLexer.h */,
283A17AC2B47E61100DF5C82 /* InList.cxx */,
283A17AD2B47E61100DF5C82 /* InList.h */,
283639BB268FD4EA009D58A1 /* LexAccessor.cxx */,
28BA729024E34D5A00272C2D /* LexAccessor.h */,
28BA728F24E34D5A00272C2D /* LexerBase.cxx */,
@ -529,6 +535,7 @@
28BA72B324E34D5B00272C2D /* Accessor.h in Headers */,
28BA72BE24E34D5B00272C2D /* StyleContext.h in Headers */,
28BA72BB24E34D5B00272C2D /* OptionSet.h in Headers */,
283A17AF2B47E61100DF5C82 /* InList.h in Headers */,
28BA72B024E34D5B00272C2D /* LexerModule.h in Headers */,
28BA72AC24E34D5B00272C2D /* LexAccessor.h in Headers */,
28BA72C524E34D5B00272C2D /* CharacterCategory.h in Headers */,
@ -684,6 +691,7 @@
28BA73A624E34D9700272C2D /* LexMatlab.cxx in Sources */,
28BA736324E34D9700272C2D /* LexRaku.cxx in Sources */,
28BA736224E34D9700272C2D /* LexCPP.cxx in Sources */,
283A17AE2B47E61100DF5C82 /* InList.cxx in Sources */,
28BA738A24E34D9700272C2D /* LexOpal.cxx in Sources */,
28BA736E24E34D9700272C2D /* LexRegistry.cxx in Sources */,
28BA738224E34D9700272C2D /* LexMMIXAL.cxx in Sources */,
@ -858,7 +866,7 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5.3.0;
CURRENT_PROJECT_VERSION = 5.3.1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 4F446KW87E;
DYLIB_COMPATIBILITY_VERSION = 1;
@ -886,7 +894,7 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5.3.0;
CURRENT_PROJECT_VERSION = 5.3.1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 4F446KW87E;
DYLIB_COMPATIBILITY_VERSION = 1;

View File

@ -4,8 +4,8 @@
#include <windows.h>
#define VERSION_LEXILLA "5.3.0"
#define VERSION_WORDS 5, 3, 0, 0
#define VERSION_LEXILLA "5.3.1"
#define VERSION_WORDS 5, 3, 1, 0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_WORDS

View File

@ -33,6 +33,10 @@ $(DIR_O)/DefaultLexer.o: \
../lexlib/Accessor.h \
../lexlib/LexerModule.h \
../lexlib/DefaultLexer.h
$(DIR_O)/InList.o: \
../lexlib/InList.cxx \
../lexlib/InList.h \
../lexlib/CharacterSet.h
$(DIR_O)/LexAccessor.o: \
../lexlib/LexAccessor.cxx \
../../scintilla/include/ILexer.h \
@ -257,6 +261,7 @@ $(DIR_O)/LexBash.o: \
../../scintilla/include/Scintilla.h \
../include/SciLexer.h \
../lexlib/StringCopy.h \
../lexlib/InList.h \
../lexlib/WordList.h \
../lexlib/LexAccessor.h \
../lexlib/StyleContext.h \
@ -284,6 +289,7 @@ $(DIR_O)/LexBatch.o: \
../../scintilla/include/Sci_Position.h \
../../scintilla/include/Scintilla.h \
../include/SciLexer.h \
../lexlib/InList.h \
../lexlib/WordList.h \
../lexlib/LexAccessor.h \
../lexlib/Accessor.h \
@ -568,6 +574,7 @@ $(DIR_O)/LexErrorList.o: \
../../scintilla/include/Sci_Position.h \
../../scintilla/include/Scintilla.h \
../include/SciLexer.h \
../lexlib/InList.h \
../lexlib/WordList.h \
../lexlib/LexAccessor.h \
../lexlib/Accessor.h \
@ -854,7 +861,6 @@ $(DIR_O)/LexLua.o: \
../../scintilla/include/Sci_Position.h \
../../scintilla/include/Scintilla.h \
../include/SciLexer.h \
../lexlib/StringCopy.h \
../lexlib/WordList.h \
../lexlib/LexAccessor.h \
../lexlib/Accessor.h \
@ -1212,7 +1218,6 @@ $(DIR_O)/LexPython.o: \
../../scintilla/include/Sci_Position.h \
../../scintilla/include/Scintilla.h \
../include/SciLexer.h \
../lexlib/StringCopy.h \
../lexlib/WordList.h \
../lexlib/LexAccessor.h \
../lexlib/Accessor.h \

View File

@ -209,6 +209,7 @@ LEXLIB_OBJS=\
$(DIR_O)\CharacterCategory.obj \
$(DIR_O)\CharacterSet.obj \
$(DIR_O)\DefaultLexer.obj \
$(DIR_O)\InList.obj \
$(DIR_O)\LexAccessor.obj \
$(DIR_O)\LexerBase.obj \
$(DIR_O)\LexerModule.obj \

View File

@ -116,6 +116,7 @@ LEXLIB_OBJS=\
CharacterCategory.o \
CharacterSet.o \
DefaultLexer.o \
InList.o \
LexAccessor.o \
LexerBase.o \
LexerModule.o \

View File

@ -33,6 +33,10 @@ $(DIR_O)/DefaultLexer.obj: \
../lexlib/Accessor.h \
../lexlib/LexerModule.h \
../lexlib/DefaultLexer.h
$(DIR_O)/InList.obj: \
../lexlib/InList.cxx \
../lexlib/InList.h \
../lexlib/CharacterSet.h
$(DIR_O)/LexAccessor.obj: \
../lexlib/LexAccessor.cxx \
../../scintilla/include/ILexer.h \
@ -257,6 +261,7 @@ $(DIR_O)/LexBash.obj: \
../../scintilla/include/Scintilla.h \
../include/SciLexer.h \
../lexlib/StringCopy.h \
../lexlib/InList.h \
../lexlib/WordList.h \
../lexlib/LexAccessor.h \
../lexlib/StyleContext.h \
@ -284,6 +289,7 @@ $(DIR_O)/LexBatch.obj: \
../../scintilla/include/Sci_Position.h \
../../scintilla/include/Scintilla.h \
../include/SciLexer.h \
../lexlib/InList.h \
../lexlib/WordList.h \
../lexlib/LexAccessor.h \
../lexlib/Accessor.h \
@ -568,6 +574,7 @@ $(DIR_O)/LexErrorList.obj: \
../../scintilla/include/Sci_Position.h \
../../scintilla/include/Scintilla.h \
../include/SciLexer.h \
../lexlib/InList.h \
../lexlib/WordList.h \
../lexlib/LexAccessor.h \
../lexlib/Accessor.h \
@ -854,7 +861,6 @@ $(DIR_O)/LexLua.obj: \
../../scintilla/include/Sci_Position.h \
../../scintilla/include/Scintilla.h \
../include/SciLexer.h \
../lexlib/StringCopy.h \
../lexlib/WordList.h \
../lexlib/LexAccessor.h \
../lexlib/Accessor.h \
@ -1212,7 +1218,6 @@ $(DIR_O)/LexPython.obj: \
../../scintilla/include/Sci_Position.h \
../../scintilla/include/Scintilla.h \
../include/SciLexer.h \
../lexlib/StringCopy.h \
../lexlib/WordList.h \
../lexlib/LexAccessor.h \
../lexlib/Accessor.h \

View File

@ -0,0 +1,52 @@
; Enumerate all styles: 0 to 15 except for 11(comment block) which is not yet implemented.
; This is not a viable source file, it just illustrates the different states in isolation.
; comment=1
; Comment
; whitespace=0
; w
; number=2
11
; string=3
"String"
; operator=4
+
; identifier=5
identifier
; CPU instruction=6
add
; math Instruction=7
fadd
; register=8
ECX
; directive=9
section
; directive operand=10
rel
; comment block=11 is for future expansion
; character=12
'character'
; string EOL=13
"no line end
; extended instruction=14
movq
; comment directive=15
comment ~ A multiple-line
comment directive~
;end

View File

@ -0,0 +1,53 @@
0 400 0 ; Enumerate all styles: 0 to 15 except for 11(comment block) which is not yet implemented.
0 400 0 ; This is not a viable source file, it just illustrates the different states in isolation.
0 400 0
0 400 0 ; comment=1
0 400 0 ; Comment
0 400 0
0 400 0 ; whitespace=0
0 400 0 ; w
0 400 0
0 400 0 ; number=2
0 400 0 11
0 400 0
0 400 0 ; string=3
0 400 0 "String"
0 400 0
0 400 0 ; operator=4
0 400 0 +
0 400 0
0 400 0 ; identifier=5
0 400 0 identifier
0 400 0
0 400 0 ; CPU instruction=6
0 400 0 add
0 400 0
0 400 0 ; math Instruction=7
0 400 0 fadd
0 400 0
0 400 0 ; register=8
0 400 0 ECX
0 400 0
0 400 0 ; directive=9
0 400 0 section
0 400 0
0 400 0 ; directive operand=10
0 400 0 rel
0 400 0
0 400 0 ; comment block=11 is for future expansion
0 400 0
0 400 0 ; character=12
0 400 0 'character'
0 400 0
0 400 0 ; string EOL=13
0 400 0 "no line end
0 400 0
0 400 0 ; extended instruction=14
0 400 0 movq
0 400 0
0 400 0 ; comment directive=15
0 400 0 comment ~ A multiple-line
0 400 0 comment directive~
0 400 0
0 400 0 ;end
0 400 0

View File

@ -0,0 +1,52 @@
{1}; Enumerate all styles: 0 to 15 except for 11(comment block) which is not yet implemented.
; This is not a viable source file, it just illustrates the different states in isolation.
{0}
{1}; comment=1
; Comment
{0}
{1}; whitespace=0
{0} {1}; w
{0}
{1}; number=2
{2}11{0}
{1}; string=3
{3}"String"{0}
{1}; operator=4
{4}+{0}
{1}; identifier=5
{5}identifier{0}
{1}; CPU instruction=6
{6}add{0}
{1}; math Instruction=7
{7}fadd{0}
{1}; register=8
{8}ECX{0}
{1}; directive=9
{9}section{0}
{1}; directive operand=10
{10}rel{0}
{1}; comment block=11 is for future expansion
{0}
{1}; character=12
{12}'character'{0}
{1}; string EOL=13
{13}"no line end
{0}
{1}; extended instruction=14
{14}movq{0}
{1}; comment directive=15
{0} {9}comment{0} {15}~ A multiple-line
comment directive~{0}
{1};end

View File

@ -0,0 +1,9 @@
lexer.*.asm=asm
keywords.*.asm=add sub xor mov lea call
keywords2.*.asm=fadd
keywords3.*.asm=rsp rax rcx rdx r8 r9 ecx
keywords4.*.asm=db section alignb resq resqdb global extern equ .bss .text .data start comment
keywords5.*.asm=qword rel
keywords6.*.asm=movd movq

View File

@ -0,0 +1,26 @@
rem Keywords with colon
rem with spacing
call file.bat arg1
call "file.bat" arg1
call :label arg1
goto :label
goto :eof
goto label
echo: %var%
echo: text
echo text
rem no spacing
call:label arg1
goto:label
goto:eof
echo:%var%
echo:text
(call)
(echo:)
(goto)
rem call internal commands
call echo text
call set "a=b"

View File

@ -0,0 +1,27 @@
0 400 0 rem Keywords with colon
0 400 0
0 400 0 rem with spacing
0 400 0 call file.bat arg1
0 400 0 call "file.bat" arg1
0 400 0 call :label arg1
0 400 0 goto :label
0 400 0 goto :eof
0 400 0 goto label
0 400 0 echo: %var%
0 400 0 echo: text
0 400 0 echo text
0 400 0
0 400 0 rem no spacing
0 400 0 call:label arg1
0 400 0 goto:label
0 400 0 goto:eof
0 400 0 echo:%var%
0 400 0 echo:text
0 400 0 (call)
0 400 0 (echo:)
0 400 0 (goto)
0 400 0
0 400 0 rem call internal commands
0 400 0 call echo text
0 400 0 call set "a=b"
0 400 0

View File

@ -0,0 +1,26 @@
{1}rem Keywords with colon
{0}
{1}rem with spacing
{2}call{5} file.bat{0} arg1
{2}call{0} "file.bat" arg1
{2}call{0} :label arg1
{2}goto{0} :label
{2}goto{0} :eof
{2}goto{0} label
{2}echo{0}: {6}%var%{0}
{2}echo{0}: text
{2}echo{0} text
{1}rem no spacing
{2}call{0}:label arg1
{2}goto{0}:label
{2}goto{0}:eof
{2}echo{0}:{6}%var%{0}
{2}echo{0}:text
({2}call{0})
({2}echo{0}:)
({2}goto{0})
{1}rem call internal commands
{2}call echo{0} text
{2}call set{0} "a=b"

View File

@ -0,0 +1,31 @@
// Test JavaScript template expressions for issue 94
// Basic
var basic = `${identifier}`;
// Nested
var nested = ` ${ ` ${ 1 } ` } `;
// With escapes
var xxx = {
'1': `\`\u0020\${a${1 + 1}b}`,
'2': `\${a${ `b${1 + 2}c`.charCodeAt(2) }d}`,
'3': `\${a${ `b${ `c${ JSON.stringify({
'4': {},
}) }d` }e` }f}`,
};
// Original request
fetchOptions.body = `
{
"accountNumber" : "248796",
"customerType" : "Shipper",
"destinationCity" : "${order.destination.city}",
"destinationState" : "${order.destination.stateProvince}",
"destinationZip" : ${order.destination.postalCode},
"paymentType" : "Prepaid",
"shipmentInfo" :
{
"items" : [ { "shipmentClass" : "50", "shipmentWeight" : "${order.totalWeight.toString()}" } ]
}
}`;

View File

@ -0,0 +1,32 @@
0 400 400 // Test JavaScript template expressions for issue 94
1 400 400
0 400 400 // Basic
0 400 400 var basic = `${identifier}`;
1 400 400
0 400 400 // Nested
0 400 400 var nested = ` ${ ` ${ 1 } ` } `;
1 400 400
0 400 400 // With escapes
2 400 401 + var xxx = {
0 401 401 | '1': `\`\u0020\${a${1 + 1}b}`,
0 401 401 | '2': `\${a${ `b${1 + 2}c`.charCodeAt(2) }d}`,
2 401 406 + '3': `\${a${ `b${ `c${ JSON.stringify({
0 406 406 | '4': {},
0 406 401 | }) }d` }e` }f}`,
0 401 400 | };
1 400 400
0 400 400 // Original request
0 400 400 fetchOptions.body = `
0 400 400 {
0 400 400 "accountNumber" : "248796",
0 400 400 "customerType" : "Shipper",
0 400 400 "destinationCity" : "${order.destination.city}",
0 400 400 "destinationState" : "${order.destination.stateProvince}",
0 400 400 "destinationZip" : ${order.destination.postalCode},
0 400 400 "paymentType" : "Prepaid",
0 400 400 "shipmentInfo" :
0 400 400 {
0 400 400 "items" : [ { "shipmentClass" : "50", "shipmentWeight" : "${order.totalWeight.toString()}" } ]
0 400 400 }
0 400 400 }`;
1 400 400

View File

@ -0,0 +1,31 @@
{2}// Test JavaScript template expressions for issue 94
{0}
{2}// Basic
{5}var{0} {11}basic{0} {10}={0} {20}`{10}${{11}identifier{10}}{20}`{10};{0}
{2}// Nested
{5}var{0} {11}nested{0} {10}={0} {20}` {10}${{0} {20}` {10}${{0} {4}1{0} {10}}{20} `{0} {10}}{20} `{10};{0}
{2}// With escapes
{5}var{0} {11}xxx{0} {10}={0} {10}{{0}
{7}'1'{10}:{0} {20}`{27}\`\u0020\${20}{a{10}${{4}1{0} {10}+{0} {4}1{10}}{20}b}`{10},{0}
{7}'2'{10}:{0} {20}`{27}\${20}{a{10}${{0} {20}`b{10}${{4}1{0} {10}+{0} {4}2{10}}{20}c`{10}.{16}charCodeAt{10}({4}2{10}){0} {10}}{20}d}`{10},{0}
{7}'3'{10}:{0} {20}`{27}\${20}{a{10}${{0} {20}`b{10}${{0} {20}`c{10}${{0} {19}JSON{10}.{16}stringify{10}({{0}
{7}'4'{10}:{0} {10}{},{0}
{10}}){0} {10}}{20}d`{0} {10}}{20}e`{0} {10}}{20}f}`{10},{0}
{10}};{0}
{2}// Original request
{11}fetchOptions{10}.{11}body{0} {10}={0} {20}`
{
"accountNumber" : "248796",
"customerType" : "Shipper",
"destinationCity" : "{10}${{11}order{10}.{11}destination{10}.{11}city{10}}{20}",
"destinationState" : "{10}${{11}order{10}.{11}destination{10}.{11}stateProvince{10}}{20}",
"destinationZip" : {10}${{11}order{10}.{11}destination{10}.{11}postalCode{10}}{20},
"paymentType" : "Prepaid",
"shipmentInfo" :
{
"items" : [ { "shipmentClass" : "50", "shipmentWeight" : "{10}${{11}order{10}.{11}totalWeight{10}.{11}toString{10}()}{20}" } ]
}
}`{10};{0}

View File

@ -1,9 +1,9 @@
# coding: utf-8
lexer.*.cxx=cpp
keywords.*.cxx=int let uuid
keywords2.*.cxx=second Upper
keywords.*.cxx=int let uuid var
keywords2.*.cxx=charCodeAt second stringify Upper
keywords3.*.cxx=file
keywords4.*.cxx=global
keywords4.*.cxx=global JSON
keywords5.*.cxx=
keywords6.*.cxx=TODO
@ -20,6 +20,7 @@ lexer.cpp.escape.sequence=1
styling.within.preprocessor=0
lexer.cpp.triplequoted.strings=1
lexer.cpp.hashquoted.strings=1
lexer.cpp.backquoted.strings=2
fold=1
fold.preprocessor=1

View File

@ -0,0 +1,47 @@
! Enumerate all styles: 0 to 14
! This is not a viable source file, it just illustrates the different states in isolation.
! comment=1
! Comment
! default=0
! w
! number=2
.37
! string1=3
'string'
! string2=4
"string"
! stringeol=5
" unclosed
! operator=6
+
! identifier=7
variable
! word=8
program
! word2=9
system_clock
! word3=10
doublecomplex
! preprocessor=11
!DEC$ ATTRIBUTES DLLEXPORT::sr1
! operator2=12
.lt.
! label=13
999
! continuation=14
&

View File

@ -0,0 +1,48 @@
0 400 0 ! Enumerate all styles: 0 to 14
0 400 0 ! This is not a viable source file, it just illustrates the different states in isolation.
1 400 0
0 400 0 ! comment=1
0 400 0 ! Comment
1 400 0
0 400 0 ! default=0
0 400 0 ! w
1 400 0
0 400 0 ! number=2
0 400 0 .37
1 400 0
0 400 0 ! string1=3
0 400 0 'string'
1 400 0
0 400 0 ! string2=4
0 400 0 "string"
1 400 0
0 400 0 ! stringeol=5
0 400 0 " unclosed
1 400 0
0 400 0 ! operator=6
0 400 0 +
1 400 0
0 400 0 ! identifier=7
0 400 0 variable
1 400 0
0 400 0 ! word=8
2 400 0 + program
1 401 0 |
0 401 0 | ! word2=9
0 401 0 | system_clock
1 401 0 |
0 401 0 | ! word3=10
0 401 0 | doublecomplex
1 401 0 |
0 401 0 | ! preprocessor=11
0 401 0 | !DEC$ ATTRIBUTES DLLEXPORT::sr1
1 401 0 |
0 401 0 | ! operator2=12
0 401 0 | .lt.
1 401 0 |
0 401 0 | ! label=13
0 401 0 | 999
1 401 0 |
0 401 0 | ! continuation=14
0 401 0 | &
0 400 0

View File

@ -0,0 +1,47 @@
{1}! Enumerate all styles: 0 to 14{0}
{1}! This is not a viable source file, it just illustrates the different states in isolation.{0}
{1}! comment=1{0}
{1}! Comment{0}
{1}! default=0{0}
{1}! w{0}
{1}! number=2{0}
{2}.37{0}
{1}! string1=3{0}
{3}'string'{0}
{1}! string2=4{0}
{4}"string"{0}
{1}! stringeol=5{0}
{5}" unclosed
{0}
{1}! operator=6{0}
{6}+{0}
{1}! identifier=7{0}
{7}variable{0}
{1}! word=8{0}
{8}program{0}
{1}! word2=9{0}
{9}system_clock{0}
{1}! word3=10{0}
{10}doublecomplex{0}
{1}! preprocessor=11{0}
{11}!DEC$ ATTRIBUTES DLLEXPORT::sr1{0}
{1}! operator2=12{0}
{12}.lt.{0}
{1}! label=13{0}
{13}999{0}
{1}! continuation=14{0}
{14}&{0}

View File

@ -0,0 +1,6 @@
lexer.*.f=fortran
keywords.*.f=do end if program
keywords2.*.f=system_clock
keywords3.*.f=doublecomplex
fold=1
fold.compact=1

View File

@ -1,4 +1,4 @@
% All the exaples here should yeild folding
% All the examples here should yield folding
classdef
% Some code

View File

@ -1,4 +1,4 @@
0 400 400 % All the exaples here should yeild folding
0 400 400 % All the examples here should yield folding
1 400 400
2 400 401 + classdef
0 401 401 | % Some code

View File

@ -1,4 +1,4 @@
{1}% All the exaples here should yeild folding{0}
{1}% All the examples here should yield folding{0}
{4}classdef{0}
{1}% Some code{0}

View File

@ -0,0 +1,70 @@
-- Enumerate all styles: 0 to 22, except for 3 (variable) and 11 (string) which are not implemented
-- Hidden commands with other states (ored with ox40) are also possible inside /*! ... */
/* commentline=2 */
# comment
# default=0
# w
# comment=1
/* comment */
# variable=3 is not implemented
@variable
# systemvariable=4
@@systemvariable
# knownsystemvariable=5
@@known
# number=6
6
# majorkeyword=7
select
# keyword=8
in
# databaseobject=9
object
# procedurekeyword=10
procedure
# string=11 is not implemented
# sqstring=12
'string'
# dqstring=13
"string"
# operator=14
+
# function=15
function()
# identifier=16
identifier
# quotedidentifier=17
`quoted`
# user1=18
user1
# user2=19
user2
# user3=20
user3
# hiddencommand=21
/*!*/
# placeholder=22
<{placeholder>}

View File

@ -0,0 +1,71 @@
0 400 400 -- Enumerate all styles: 0 to 22, except for 3 (variable) and 11 (string) which are not implemented
0 400 400 -- Hidden commands with other states (ored with ox40) are also possible inside /*! ... */
1 400 400
0 400 400 /* commentline=2 */
0 400 400 # comment
1 400 400
0 400 400 # default=0
0 400 400 # w
1 400 400
0 400 400 # comment=1
0 400 400 /* comment */
1 400 400
0 400 400 # variable=3 is not implemented
0 400 400 @variable
1 400 400
0 400 400 # systemvariable=4
0 400 400 @@systemvariable
1 400 400
0 400 400 # knownsystemvariable=5
0 400 400 @@known
1 400 400
0 400 400 # number=6
0 400 400 6
1 400 400
0 400 400 # majorkeyword=7
0 400 400 select
1 400 400
0 400 400 # keyword=8
0 400 400 in
1 400 400
0 400 400 # databaseobject=9
0 400 400 object
1 400 400
0 400 400 # procedurekeyword=10
0 400 400 procedure
1 400 400
0 400 400 # string=11 is not implemented
1 400 400
0 400 400 # sqstring=12
0 400 400 'string'
1 400 400
0 400 400 # dqstring=13
0 400 400 "string"
1 400 400
0 400 400 # operator=14
0 400 400 +
1 400 400
0 400 400 # function=15
0 400 400 function()
1 400 400
0 400 400 # identifier=16
0 400 400 identifier
1 400 400
0 400 400 # quotedidentifier=17
0 400 400 `quoted`
1 400 400
0 400 400 # user1=18
0 400 400 user1
1 400 400
0 400 400 # user2=19
0 400 400 user2
1 400 400
0 400 400 # user3=20
0 400 400 user3
1 400 400
0 400 400 # hiddencommand=21
0 400 400 /*!*/
1 400 400
0 400 400 # placeholder=22
0 400 400 <{placeholder>}
0 400 0

View File

@ -0,0 +1,70 @@
{2}-- Enumerate all styles: 0 to 22, except for 3 (variable) and 11 (string) which are not implemented
-- Hidden commands with other states (ored with ox40) are also possible inside /*! ... */
{0}
{1}/* commentline=2 */{0}
{2}# comment
{0}
{2}# default=0
{0} {2}# w
{0}
{2}# comment=1
{1}/* comment */{0}
{2}# variable=3 is not implemented
{14}@{16}variable{0}
{2}# systemvariable=4
{4}@@systemvariable{0}
{2}# knownsystemvariable=5
{5}@@known{0}
{2}# number=6
{6}6{0}
{2}# majorkeyword=7
{7}select{0}
{2}# keyword=8
{8}in{0}
{2}# databaseobject=9
{9}object{0}
{2}# procedurekeyword=10
{10}procedure{0}
{2}# string=11 is not implemented
{0}
{2}# sqstring=12
{12}'string'{0}
{2}# dqstring=13
{13}"string"{0}
{2}# operator=14
{14}+{0}
{2}# function=15
{15}function{14}(){0}
{2}# identifier=16
{16}identifier{0}
{2}# quotedidentifier=17
{17}`quoted`{0}
{2}# user1=18
{18}user1{0}
{2}# user2=19
{19}user2{0}
{2}# user3=20
{20}user3{0}
{2}# hiddencommand=21
{21}/*!*/{0}
{2}# placeholder=22
{22}<{placeholder>}

View File

@ -0,0 +1,18 @@
lexer.*.sql=mysql
keywords.*.sql=select
keywords2.*.sql=in
keywords3.*.sql=object
keywords4.*.sql=function
keywords5.*.sql=known
keywords6.*.sql=procedure
keywords7.*.sql=user1
keywords8.*.sql=user2
keywords9.*.sql=user3
lexer.sql.backticks.identifier=1
lexer.sql.numbersign.comment=1
lexer.sql.allow.dotted.word=1
fold=1
fold.compact=1

View File

@ -0,0 +1,16 @@
# Simple raw string
r''
# Raw f-string
rf''
fr''
# Raw byte string
rb''
br''
# Raw unicode strings: ur'' is valid in 2.7 (but not in 3) -- always lexed as
# valid; ru'' is never valid
ru''
ur''

View File

@ -0,0 +1,17 @@
0 400 0 # Simple raw string
0 400 0 r''
1 400 0
0 400 0 # Raw f-string
0 400 0 rf''
0 400 0 fr''
1 400 0
0 400 0 # Raw byte string
0 400 0 rb''
0 400 0 br''
1 400 0
0 400 0 # Raw unicode strings: ur'' is valid in 2.7 (but not in 3) -- always lexed as
0 400 0 # valid; ru'' is never valid
0 400 0 ru''
0 400 0 ur''
1 400 0
1 400 0

View File

@ -0,0 +1,16 @@
{1}# Simple raw string{0}
{4}r''{0}
{1}# Raw f-string{0}
{17}rf''{0}
{17}fr''{0}
{1}# Raw byte string{0}
{4}rb''{0}
{4}br''{0}
{1}# Raw unicode strings: ur'' is valid in 2.7 (but not in 3) -- always lexed as{0}
{1}# valid; ru'' is never valid{0}
{11}ru{4}''{0}
{4}ur''{0}

View File

@ -0,0 +1,12 @@
# Float Literals
12.34
1234e-2
1.234E1
# Range Literals
(1..2)
(2.0..3)
# Method on number
1.5.ceil
1ri.abs
3.times {|i| puts i}
3. times {|i| puts i}

View File

@ -0,0 +1,13 @@
0 400 0 # Float Literals
0 400 0 12.34
0 400 0 1234e-2
0 400 0 1.234E1
0 400 0 # Range Literals
0 400 0 (1..2)
0 400 0 (2.0..3)
0 400 0 # Method on number
0 400 0 1.5.ceil
0 400 0 1ri.abs
0 400 0 3.times {|i| puts i}
0 400 0 3. times {|i| puts i}
0 400 0

View File

@ -0,0 +1,12 @@
{2}# Float Literals{0}
{4}12.34{0}
{4}1234e{10}-{4}2{0}
{4}1.234E1{0}
{2}# Range Literals{0}
{10}({4}1{10}..{4}2{10}){0}
{10}({4}2.0{10}..{4}3{10}){0}
{2}# Method on number{0}
{4}1.5{10}.{11}ceil{0}
{4}1ri{10}.{11}abs{0}
{4}3{10}.{11}times{0} {10}{|{11}i{10}|{0} {11}puts{0} {11}i{10}}{0}
{4}3{10}.{0} {11}times{0} {10}{|{11}i{10}|{0} {11}puts{0} {11}i{10}}{0}

View File

@ -0,0 +1,70 @@
-- Enumerate all styles: 0 to 24
-- comment=1
/* comment */
-- whitespace=0
-- w
/* commentline=2 */
-- commentline
-- commentdoc=3
/** commentdoc */
-- number=4
4
-- word=5
select
-- string=6
"string"
-- character=7
'character'
-- sqlplus=8
append
-- sqlplus_prompt=9
prompt SQL+Prompt
-- operator=10
+
-- identifier=11
identifier
-- sqlplus_comment=13
remark sqlplus comment
-- commentlinedoc=15
# commentlinedoc
-- word2=16
object
-- commentdockeyword=17
/** @return */
-- commentdockeyworderror=18
/** @error */
-- user1=19
dbms_output.disable
-- user2=20
analyze
-- user3=21
array
-- user4=22
false
-- quotedidentifier=23
`quotedidentifier`
-- qoperator=24
q'{ today's }'

View File

@ -0,0 +1,71 @@
0 400 400 -- Enumerate all styles: 0 to 24
1 400 400
0 400 400 -- comment=1
0 400 400 /* comment */
1 400 400
0 400 400 -- whitespace=0
0 400 400 -- w
1 400 400
0 400 400 /* commentline=2 */
0 400 400 -- commentline
1 400 400
0 400 400 -- commentdoc=3
0 400 400 /** commentdoc */
1 400 400
0 400 400 -- number=4
0 400 400 4
1 400 400
0 400 400 -- word=5
0 400 400 select
1 400 400
0 400 400 -- string=6
0 400 400 "string"
1 400 400
0 400 400 -- character=7
0 400 400 'character'
1 400 400
0 400 400 -- sqlplus=8
0 400 400 append
1 400 400
0 400 400 -- sqlplus_prompt=9
0 400 400 prompt SQL+Prompt
1 400 400
0 400 400 -- operator=10
0 400 400 +
1 400 400
0 400 400 -- identifier=11
0 400 400 identifier
1 400 400
0 400 400 -- sqlplus_comment=13
0 400 400 remark sqlplus comment
1 400 400
0 400 400 -- commentlinedoc=15
0 400 400 # commentlinedoc
1 400 400
0 400 400 -- word2=16
0 400 400 object
1 400 400
0 400 400 -- commentdockeyword=17
0 400 400 /** @return */
1 400 400
0 400 400 -- commentdockeyworderror=18
0 400 400 /** @error */
1 400 400
0 400 400 -- user1=19
0 400 400 dbms_output.disable
1 400 400
0 400 400 -- user2=20
0 400 400 analyze
1 400 400
0 400 400 -- user3=21
0 400 400 array
1 400 400
0 400 400 -- user4=22
0 400 400 false
1 400 400
0 400 400 -- quotedidentifier=23
0 400 400 `quotedidentifier`
1 400 400
0 400 400 -- qoperator=24
0 400 400 q'{ today's }'
0 400 0

View File

@ -0,0 +1,70 @@
{2}-- Enumerate all styles: 0 to 24
{0}
{2}-- comment=1
{1}/* comment */{0}
{2}-- whitespace=0
{0} {2}-- w
{0}
{1}/* commentline=2 */{0}
{2}-- commentline
{0}
{2}-- commentdoc=3
{3}/** commentdoc */{0}
{2}-- number=4
{4}4{0}
{2}-- word=5
{5}select{0}
{2}-- string=6
{6}"string"{0}
{2}-- character=7
{7}'character'{0}
{2}-- sqlplus=8
{8}append{0}
{2}-- sqlplus_prompt=9
{8}prompt{9} SQL+Prompt
{0}
{2}-- operator=10
{10}+{0}
{2}-- identifier=11
{11}identifier{0}
{2}-- sqlplus_comment=13
{8}remark{13} sqlplus comment
{0}
{2}-- commentlinedoc=15
{15}# commentlinedoc
{0}
{2}-- word2=16
{16}object{0}
{2}-- commentdockeyword=17
{3}/** {17}@return{3} */{0}
{2}-- commentdockeyworderror=18
{3}/** {18}@error{3} */{0}
{2}-- user1=19
{19}dbms_output.disable{0}
{2}-- user2=20
{20}analyze{0}
{2}-- user3=21
{21}array{0}
{2}-- user4=22
{22}false{0}
{2}-- quotedidentifier=23
{23}`quotedidentifier`{0}
{2}-- qoperator=24
{24}q'{ today's }'{0}

View File

@ -0,0 +1,17 @@
lexer.*.sql=sql
keywords.*.sql=select
keywords2.*.sql=object
keywords3.*.sql=return
keywords4.*.sql=a~ppend pro~mpt rem~ark
keywords5.*.sql=dbms_output.disable
keywords6.*.sql=analyze
keywords7.*.sql=array
keywords8.*.sql=false
lexer.sql.backticks.identifier=1
lexer.sql.numbersign.comment=1
lexer.sql.allow.dotted.word=1
fold=1
fold.compact=1

View File

@ -152,6 +152,7 @@
<ItemGroup>
<ClCompile Include="..\..\lexlib\Accessor.cxx" />
<ClCompile Include="..\..\lexlib\CharacterSet.cxx" />
<ClCompile Include="..\..\lexlib\InList.cxx" />
<ClCompile Include="..\..\lexlib\LexerBase.cxx" />
<ClCompile Include="..\..\lexlib\LexerModule.cxx" />
<ClCompile Include="..\..\lexlib\LexerSimple.cxx" />

View File

@ -54,6 +54,7 @@ TESTOBJ=$(TESTSRC:.cxx=.o)
TESTEDOBJ=\
Accessor.o \
CharacterSet.o \
InList.o \
LexerBase.o \
LexerModule.o \
LexerSimple.o \

View File

@ -14,6 +14,7 @@ TESTSRC=test*.cxx
TESTEDSRC=\
../../lexlib/Accessor.cxx \
../../lexlib/CharacterSet.cxx \
../../lexlib/InList.cxx \
../../lexlib/LexerBase.cxx \
../../lexlib/LexerModule.cxx \
../../lexlib/LexerSimple.cxx \

View File

@ -5,6 +5,8 @@
#include <cstdlib>
#include <cassert>
#include <string_view>
#include "CharacterSet.h"
#include "catch.hpp"
@ -138,4 +140,12 @@ TEST_CASE("Functions") {
REQUIRE(CompareNCaseInsensitive("aa", "A", 1) == 0);
}
SECTION("EqualCaseInsensitive") {
REQUIRE(EqualCaseInsensitive(" ", " "));
REQUIRE(EqualCaseInsensitive("A", "A"));
REQUIRE(EqualCaseInsensitive("a", "A"));
REQUIRE(!EqualCaseInsensitive("b", "A"));
REQUIRE(!EqualCaseInsensitive("aa", "A"));
REQUIRE(!EqualCaseInsensitive("a", "AA"));
}
}

View File

@ -0,0 +1,28 @@
/** @file testInList.cxx
** Unit Tests for Lexilla internal data structures
**/
#include <cstdlib>
#include <cassert>
#include <string_view>
#include <initializer_list>
#include "InList.h"
#include "catch.hpp"
using namespace Lexilla;
// Test InList.
TEST_CASE("InList") {
SECTION("Basic") {
REQUIRE(InList("dog", {"cat", "dog", "frog"}));
REQUIRE(!InList("fly", {"cat", "dog", "frog"}));
REQUIRE(InListCaseInsensitive("DOG", {"cat", "dog", "frog"}));
REQUIRE(!InListCaseInsensitive("fly", {"cat", "dog", "frog"}));
}
}

View File

@ -1 +1 @@
530
531

View File

@ -799,6 +799,66 @@ void ScintillaCall::EndUndoAction() {
Call(Message::EndUndoAction);
}
int ScintillaCall::UndoActions() {
return static_cast<int>(Call(Message::GetUndoActions));
}
void ScintillaCall::SetUndoSavePoint(int action) {
Call(Message::SetUndoSavePoint, action);
}
int ScintillaCall::UndoSavePoint() {
return static_cast<int>(Call(Message::GetUndoSavePoint));
}
void ScintillaCall::SetUndoDetach(int action) {
Call(Message::SetUndoDetach, action);
}
int ScintillaCall::UndoDetach() {
return static_cast<int>(Call(Message::GetUndoDetach));
}
void ScintillaCall::SetUndoTentative(int action) {
Call(Message::SetUndoTentative, action);
}
int ScintillaCall::UndoTentative() {
return static_cast<int>(Call(Message::GetUndoTentative));
}
void ScintillaCall::SetUndoCurrent(int action) {
Call(Message::SetUndoCurrent, action);
}
int ScintillaCall::UndoCurrent() {
return static_cast<int>(Call(Message::GetUndoCurrent));
}
void ScintillaCall::PushUndoActionType(int type, Position pos) {
Call(Message::PushUndoActionType, type, pos);
}
void ScintillaCall::ChangeLastUndoActionText(Position length, const char *text) {
CallString(Message::ChangeLastUndoActionText, length, text);
}
int ScintillaCall::UndoActionType(int action) {
return static_cast<int>(Call(Message::GetUndoActionType, action));
}
Position ScintillaCall::UndoActionPosition(int action) {
return Call(Message::GetUndoActionPosition, action);
}
int ScintillaCall::UndoActionText(int action, char *text) {
return static_cast<int>(CallPointer(Message::GetUndoActionText, action, text));
}
std::string ScintillaCall::UndoActionText(int action) {
return CallReturnString(Message::GetUndoActionText, action);
}
void ScintillaCall::IndicSetStyle(int indicator, Scintilla::IndicatorStyle indicatorStyle) {
Call(Message::IndicSetStyle, indicator, static_cast<intptr_t>(indicatorStyle));
}

View File

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

View File

@ -98,6 +98,8 @@
286F8EE0260448C300EC8D60 /* Geometry.h in Headers */ = {isa = PBXBuildFile; fileRef = 286F8EDD260448C300EC8D60 /* Geometry.h */; };
287F3C6A246F90240040E76F /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 287F3C69246F90240040E76F /* Cocoa.framework */; };
287F3C6C246F90300040E76F /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 287F3C6B246F90300040E76F /* QuartzCore.framework */; };
28B962A52B6AF44F00ACCD96 /* UndoHistory.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 28B962A32B6AF44F00ACCD96 /* UndoHistory.cxx */; };
28B962A62B6AF44F00ACCD96 /* UndoHistory.h in Headers */ = {isa = PBXBuildFile; fileRef = 28B962A42B6AF44F00ACCD96 /* UndoHistory.h */; };
28EA9CAE255894B4007710C4 /* CharacterCategoryMap.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 28EA9CAA255894B4007710C4 /* CharacterCategoryMap.cxx */; };
28EA9CAF255894B4007710C4 /* CharacterType.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 28EA9CAB255894B4007710C4 /* CharacterType.cxx */; };
28EA9CB0255894B4007710C4 /* CharacterCategoryMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 28EA9CAC255894B4007710C4 /* CharacterCategoryMap.h */; };
@ -199,6 +201,8 @@
287F3C69246F90240040E76F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
287F3C6B246F90300040E76F /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
287F3E0F246F9AE50040E76F /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = SOURCE_ROOT; };
28B962A32B6AF44F00ACCD96 /* UndoHistory.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UndoHistory.cxx; path = ../../src/UndoHistory.cxx; sourceTree = "<group>"; };
28B962A42B6AF44F00ACCD96 /* UndoHistory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UndoHistory.h; path = ../../src/UndoHistory.h; sourceTree = "<group>"; };
28EA9CAA255894B4007710C4 /* CharacterCategoryMap.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CharacterCategoryMap.cxx; path = ../../src/CharacterCategoryMap.cxx; sourceTree = "<group>"; };
28EA9CAB255894B4007710C4 /* CharacterType.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CharacterType.cxx; path = ../../src/CharacterType.cxx; sourceTree = "<group>"; };
28EA9CAC255894B4007710C4 /* CharacterCategoryMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CharacterCategoryMap.h; path = ../../src/CharacterCategoryMap.h; sourceTree = "<group>"; };
@ -306,6 +310,8 @@
2829372124E2D58700C84BA2 /* SplitVector.h */,
2829370424E2D58500C84BA2 /* Style.cxx */,
282936F224E2D58400C84BA2 /* Style.h */,
28B962A32B6AF44F00ACCD96 /* UndoHistory.cxx */,
28B962A42B6AF44F00ACCD96 /* UndoHistory.h */,
2829370F24E2D58500C84BA2 /* UniConversion.cxx */,
282936FC24E2D58400C84BA2 /* UniConversion.h */,
282936F324E2D58400C84BA2 /* UniqueString.cxx */,
@ -397,6 +403,7 @@
2829376F24E2D58800C84BA2 /* Document.h in Headers */,
2829374A24E2D58800C84BA2 /* ContractionState.h in Headers */,
2829376024E2D58800C84BA2 /* XPM.h in Headers */,
28B962A62B6AF44F00ACCD96 /* UndoHistory.h in Headers */,
2829372F24E2D58800C84BA2 /* ElapsedPeriod.h in Headers */,
28EA9CB1255894B4007710C4 /* CharacterType.h in Headers */,
2829373A24E2D58800C84BA2 /* MarginView.h in Headers */,
@ -522,6 +529,7 @@
2829373C24E2D58800C84BA2 /* CaseFolder.cxx in Sources */,
2829374824E2D58800C84BA2 /* RESearch.cxx in Sources */,
2829377024E2D58800C84BA2 /* EditView.cxx in Sources */,
28B962A52B6AF44F00ACCD96 /* UndoHistory.cxx in Sources */,
2829376724E2D58800C84BA2 /* ScintillaBase.cxx in Sources */,
2829374724E2D58800C84BA2 /* Style.cxx in Sources */,
2829375A24E2D58800C84BA2 /* ViewStyle.cxx in Sources */,
@ -574,7 +582,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 5.4.1;
CURRENT_PROJECT_VERSION = 5.4.2;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@ -639,7 +647,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 5.4.1;
CURRENT_PROJECT_VERSION = 5.4.2;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
@ -672,7 +680,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 5.4.1;
CURRENT_PROJECT_VERSION = 5.4.2;
DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
@ -707,7 +715,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 5.4.1;
CURRENT_PROJECT_VERSION = 5.4.2;
DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";

View File

@ -27,12 +27,14 @@ constStatement:scintilla/src/Document.cxx
// ILexer5* is not pointing at logically const
constParameterPointer:scintilla/src/Document.cxx
// Doesn't seem to understand that values change in loops
knownConditionTrueFalse:scintilla/src/Document.cxx
// Some non-explicit constructors are used for conversions or are private to lexers
noExplicitConstructor
// MarginView access to all bits is safe and is better defined in later versions of C++
shiftTooManyBitsSigned:scintilla/src/MarginView.cxx
integerOverflow:scintilla/src/MarginView.cxx
// DLL entry points are unused inside Scintilla
unusedFunction:scintilla/win32/ScintillaDLL.cxx

View File

@ -129,7 +129,7 @@
<h1>Scintilla Documentation</h1>
<p>Last edited 19 December 2023 NH</p>
<p>Last edited 16 February 2024 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 />
@ -341,97 +341,101 @@
<tr>
<td>&cir; <a class="toc" href="#UndoAndRedo">Undo and Redo</a></td>
<td>&cir; <a class="toc" href="#UndoSaveRestore">Undo save and restore</a></td>
<td>&cir; <a class="toc" href="#ChangeHistory">Change history</a></td>
<td>&cir; <a class="toc" href="#ScrollingAndAutomaticScrolling">Scrolling and automatic scrolling</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#ScrollingAndAutomaticScrolling">Scrolling and automatic scrolling</a></td>
<td>&cir; <a class="toc" href="#WhiteSpace">White space</a></td>
<td>&cir; <a class="toc" href="#Cursor">Cursor</a></td>
<td>&cir; <a class="toc" href="#MouseCapture">Mouse capture</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#MouseCapture">Mouse capture</a></td>
<td>&cir; <a class="toc" href="#LineEndings">Line endings</a></td>
<td>&cir; <a class="toc" href="#Words">Words</a></td>
<td>&cir; <a class="toc" href="#Styling">Styling</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#Styling">Styling</a></td>
<td>&cir; <a class="toc" href="#StyleDefinition">Style definition</a></td>
<td>&cir; <a class="toc" href="#ElementColours">Element colours</a></td>
<td>&cir; <a class="toc" href="#CaretAndSelectionStyles">Selection, caret, and hotspot styles</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#CaretAndSelectionStyles">Selection, caret, and hotspot styles</a></td>
<td>&cir; <a class="toc" href="#CharacterRepresentations">Character representations</a></td>
<td>&cir; <a class="toc" href="#Margins">Margins</a></td>
<td>&cir; <a class="toc" href="#Annotations">Annotations</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#Annotations">Annotations</a></td>
<td>&cir; <a class="toc" href="#EndOfLineAnnotations">End of Line Annotations</a></td>
<td>&cir; <a class="toc" href="#OtherSettings">Other settings</a></td>
<td>&cir; <a class="toc" href="#BraceHighlighting">Brace highlighting</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#BraceHighlighting">Brace highlighting</a></td>
<td>&cir; <a class="toc" href="#TabsAndIndentationGuides">Tabs and Indentation Guides</a></td>
<td>&cir; <a class="toc" href="#Markers">Markers</a></td>
<td>&cir; <a class="toc" href="#Indicators">Indicators</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#Indicators">Indicators</a></td>
<td>&cir; <a class="toc" href="#Autocompletion">Autocompletion</a></td>
<td>&cir; <a class="toc" href="#UserLists">User lists</a></td>
<td>&cir; <a class="toc" href="#CallTips">Call tips</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#CallTips">Call tips</a></td>
<td>&cir; <a class="toc" href="#KeyboardCommands">Keyboard commands</a></td>
<td>&cir; <a class="toc" href="#KeyBindings">Key bindings</a></td>
<td>&cir; <a class="toc" href="#PopupEditMenu">Popup edit menu</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#PopupEditMenu">Popup edit menu</a></td>
<td>&cir; <a class="toc" href="#MacroRecording">Macro recording</a></td>
<td>&cir; <a class="toc" href="#Printing">Printing</a></td>
<td>&cir; <a class="toc" href="#DirectAccess">Direct access</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#DirectAccess">Direct access</a></td>
<td>&cir; <a class="toc" href="#MultipleViews">Multiple views</a></td>
<td>&cir; <a class="toc" href="#BackgroundLoadSave">Background loading and saving</a></td>
<td>&cir; <a class="toc" href="#DocumentInterface">Document interface</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#DocumentInterface">Document interface</a></td>
<td>&cir; <a class="toc" href="#Folding">Folding</a></td>
<td>&cir; <a class="toc" href="#LineWrapping">Line wrapping</a></td>
<td>&cir; <a class="toc" href="#Zooming">Zooming</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#Zooming">Zooming</a></td>
<td>&cir; <a class="toc" href="#LongLines">Long lines</a></td>
<td>&cir; <a class="toc" href="#Accessibility">Accessibility</a></td>
<td>&cir; <a class="toc" href="#Lexer">Lexer</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#Lexer">Lexer</a></td>
<td>&cir; <a class="toc" href="#LexerObjects">Lexer objects</a></td>
<td>&cir; <a class="toc" href="#Notifications">Notifications</a></td>
<td>&cir; <a class="toc" href="#Images">Images</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#Images">Images</a></td>
<td>&cir; <a class="toc" href="#GTK">GTK</a></td>
<td>&cir; <a class="toc" href="#ProvisionalMessages"><span class="provisional">Provisional messages</span></a></td>
<td>&cir; <a class="toc" href="#DeprecatedMessages">Deprecated messages</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#DeprecatedMessages">Deprecated messages</a></td>
<td>&cir; <a class="toc" href="#EditMessagesNeverSupportedByScintilla">Edit messages never supported by Scintilla</a></td>
<td>&cir; <a class="toc" href="#RemovedFeatures">Removed features</a></td>
</tr>
<tr>
<td>&cir; <a class="toc" href="#BuildingScintilla">Building Scintilla</a></td>
</tr>
</tbody>
@ -1988,6 +1992,117 @@ struct Sci_TextToFindFull {
look like typing or deletions that look like multiple uses of the Backspace or Delete keys.
</p>
<h2 id="UndoSaveRestore">Undo Save and Restore</h2>
<p>This feature is unfinished and has limitations.
When change history is active, it may show different changes than the previous session as undo actions
performed in that session are discarded in some circumstances such as when detaching from a save point.
A future version may add an API for archiving change history alongside undo history.
The operation sequences discussed here are a 'golden path' that has been tested to some extent and calling
the APIs in other circumstances or with out-of-bounds values may fail.</p>
<p>The behaviour of tentative actions in save and restore is uncertain as these are meant to be short-term states in language input
and which need to synchronize with a language IME (input method editor).
For now, restore the tentative point to -1 as it seems safest to regard the tentative change as committed.</p>
<p>Scintilla stores each change in an undo stack.
Various inter-action points play roles in undo behaviour: actions count, save point, detach point,
tentative point, and current action. Just like positions in documents, these points are between actions in the
undo stack. Thus the current action specifies the number of actions that led to the current document state and
actions after this are for redo.
The save point specifies that the actions before this point are in the most recent save and actions after this are not yet saved.</p>
<p>When the user undoes from a save point and then performs a new change, the save point can no longer be reached and is -1.
The detach point is the point at which the undo stack branched away from the saved state and is used by
change history.</p>
<p>It is possible to retrieve the undo stack from Scintilla and subsequently restore the state of the stack.</p>
<p>An application may save both the document and its save stack between sessions to enable the user to return
to the same state the next time they edit.
For this to work, the loaded file must be exactly the same as when the undo stack was saved.
If the file was changed, even in minor ways like converting line ends from Windows to Unix style then
the undo actions will not line up so undo may fail completely and will produce unexpected results.</p>
<code><a class="message" href="#SCI_GETUNDOACTIONS">SCI_GETUNDOACTIONS &rarr; int</a><br />
<a class="message" href="#SCI_SETUNDOSAVEPOINT">SCI_SETUNDOSAVEPOINT(int action)</a><br />
<a class="message" href="#SCI_GETUNDOSAVEPOINT">SCI_GETUNDOSAVEPOINT &rarr; int</a><br />
<a class="message" href="#SCI_SETUNDODETACH">SCI_SETUNDODETACH(int action)</a><br />
<a class="message" href="#SCI_GETUNDODETACH">SCI_GETUNDODETACH &rarr; int</a><br />
<a class="message" href="#SCI_SETUNDOTENTATIVE">SCI_SETUNDOTENTATIVE(int action)</a><br />
<a class="message" href="#SCI_GETUNDOTENTATIVE">SCI_GETUNDOTENTATIVE &rarr; int</a><br />
<a class="message" href="#SCI_SETUNDOCURRENT">SCI_SETUNDOCURRENT(int action)</a><br />
<a class="message" href="#SCI_GETUNDOCURRENT">SCI_GETUNDOCURRENT &rarr; int</a><br />
<a class="message" href="#SCI_PUSHUNDOACTIONTYPE">SCI_PUSHUNDOACTIONTYPE(int type, position pos)</a><br />
<a class="message" href="#SCI_CHANGELASTUNDOACTIONTEXT">SCI_CHANGELASTUNDOACTIONTEXT(position length, const char *text)</a><br />
<a class="message" href="#SCI_GETUNDOACTIONTYPE">SCI_GETUNDOACTIONTYPE(int action) &rarr; int</a><br />
<a class="message" href="#SCI_GETUNDOACTIONPOSITION">SCI_GETUNDOACTIONPOSITION(int action) &rarr; position</a><br />
<a class="message" href="#SCI_GETUNDOACTIONTEXT ">SCI_GETUNDOACTIONTEXT(int action, char *text) &rarr; int</a><br />
</code>
<h3 id="UndoSave">Save</h3>
<p>The retrieval APIs are the 'GET*' ones:
<code>SCI_GETUNDOACTIONS</code>,
<code>SCI_GETUNDOSAVEPOINT</code>,
<code>SCI_GETUNDODETACH</code>,
<code>SCI_GETUNDOTENTATIVE</code>,
<code>SCI_GETUNDOCURRENT</code>,
<code>SCI_GETUNDOACTIONTYPE</code>,
<code>SCI_GETUNDOACTIONPOSITION</code>, and
<code>SCI_GETUNDOACTIONTEXT</code>.
</p>
<p>The <code>SCI_GETUNDOACTIONS</code>,
<code>SCI_GETUNDOSAVEPOINT</code>, <code>SCI_GETUNDODETACH</code>,
<code>SCI_GETUNDOTENTATIVE</code>, and <code>SCI_GETUNDOCURRENT</code>
APIs each return a single value and may be called in any order.
</p>
<p>The <code>SCI_GETUNDOACTIONTYPE</code>,
<code>SCI_GETUNDOACTIONPOSITION</code>, and <code>SCI_GETUNDOACTIONTEXT</code>
APIs take an action index and should be called with indices from 0 to one less than the result of
<code>SCI_GETUNDOACTIONS</code>.
The actions should only be iterated in the positive direction and should start from 0.
That is because undo stack data is not all randomly accessible and iterating in other orders may take O(n^2) time.
Data may also be inaccurate if a cursor is not initialised first with 0 index calls.
</p>
<h3 id="UndoRestore">Restore</h3>
<p>Restoration is only possible when the undo state is empty so <code>SCI_EMPTYUNDOBUFFER</code>
should be called first if there may already be some undo actions.</p>
<p>The restore APIs are the 'SET*' and others:
<code>SCI_SETUNDOSAVEPOINT</code>,
<code>SCI_SETUNDODETACH</code>,
<code>SCI_SETUNDOTENTATIVE</code>,
<code>SCI_SETUNDOCURRENT</code>,
<code>SCI_PUSHUNDOACTIONTYPE</code>, and
<code>SCI_CHANGELASTUNDOACTIONTEXT</code>.
</p>
<p>The history should first be set up with <code>SCI_PUSHUNDOACTIONTYPE</code> and
<code>SCI_CHANGELASTUNDOACTIONTEXT</code> then the save, detach, tentative, and current points set
with <code>SCI_SETUNDOSAVEPOINT</code>, <code>SCI_SETUNDODETACH</code>, <code>SCI_SETUNDOTENTATIVE</code>, and
<code>SCI_SETUNDOCURRENT</code>.
</p>
<p><code>SCI_PUSHUNDOACTIONTYPE(int type, position pos)</code> appends an action to the undo stack
with a particular type and position then the text and length of that action are set with
<code>SCI_CHANGELASTUNDOACTIONTEXT(position length, const char *text)</code>.
</p>
<p>
The last restoration API called should be <code>SCI_SETUNDOCURRENT</code> as this validates
the restored history and values against the document. For example, an undo history that could cause a negative
document length or insert / remove text outside the document is invalid.
If the restored undo state is invalid then a failure status is set and the undo history cleared.
Check for failure with <a class="seealso" href="#SCI_GETSTATUS">SCI_GETSTATUS</a>.
</p>
<p>The current implementation may only work when there is no tentative point.
</p>
<h2 id="ChangeHistory">Change history</h2>
<p>Scintilla can display document changes (modified, saved, ...) in the margin or in the text.</p>

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/scintilla541.zip">
<font size="4"> <a href="https://www.scintilla.org/scintilla542.zip">
Windows</a>&nbsp;&nbsp;
<a href="https://www.scintilla.org/scintilla541.tgz">
<a href="https://www.scintilla.org/scintilla542.tgz">
GTK/Linux</a>&nbsp;&nbsp;
</font>
</td>
@ -42,7 +42,7 @@
containing very few restrictions.
</p>
<h3>
Release 5.4.1
Release 5.4.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/scintilla541.zip">zip format</a> (1.8M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/scintilla541.tgz">tgz format</a> (1.6M) commonly used on Linux and compatible operating systems</li>
<li><a href="https://www.scintilla.org/scintilla542.zip">zip format</a> (1.8M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/scintilla542.tgz">tgz format</a> (1.7M) 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

@ -583,6 +583,39 @@
</tr>
</table>
<h2>Releases</h2>
<h3>
<a href="https://www.scintilla.org/scintilla542.zip">Release 5.4.2</a>
</h3>
<ul>
<li>
Released 5 March 2024.
</li>
<li>
Significantly reduce memory used for undo actions, often to a half or quarter of previous versions.
<a href="https://sourceforge.net/p/scintilla/feature-requests/1458/">Feature #1458</a>.
</li>
<li>
Add APIs for saving and restoring undo history.
</li>
<li>
For GTK, when laying out text, detect runs with both left-to-right and right-to-left ranges and divide into an ASCII prefix and
more complex suffix. Lay out the ASCII prefix in the standard manner but, for the suffix, measure the whole width
and spread that over the suffix bytes. This produces more usable results where the caret moves over the ASCII prefix correctly
and over the suffix reasonably but not accurately.
</li>
<li>
For ScintillaEdit on Qt, fix reference from ScintillaDocument to Document to match change in 5.4.1
using IDocumentEditable for SCI_GETDOCPOINTER and SCI_SETDOCPOINTER.
</li>
<li>
For Direct2D on Win32, use the multi-threaded option to avoid crashes when Scintilla instances
created on different threads. There may be more problems with this scenario so it should be avoided.
<a href="https://sourceforge.net/p/scintilla/bugs/2420/">Bug #2420</a>.
</li>
<li>
For Win32, ensure keyboard-initiated context menu appears in multi-screen situations.
</li>
</ul>
<h3>
<a href="https://www.scintilla.org/scintilla541.zip">Release 5.4.1</a>
</h3>
@ -592,7 +625,7 @@
</li>
<li>
Add IDocumentEditable interface to allow efficient interaction with document objects which may not be visible in
a Scintilla instance. This feature is provisonal and may change before being declared stable.
a Scintilla instance. This feature is provisional and may change before being declared stable.
For better type-safety, the ScintillaCall C++ API uses IDocumentEditable* where void* was used before which may require
changes to client code that uses document pointer APIs
DocPointer, SetDocPointer, CreateDocument, AddRefDocument, and ReleaseDocument.
@ -2024,7 +2057,7 @@
</li>
<li>
Add SCI_SETTABMINIMUMWIDTH to set the minimum width of tabs.
This allows minimaps or overviews to be layed out to match the full size editing view.
This allows minimaps or overviews to be laid out to match the full size editing view.
<a href="https://sourceforge.net/p/scintilla/bugs/2118/">Bug #2118</a>.
</li>
<li>
@ -2593,7 +2626,7 @@
<a href="https://sourceforge.net/p/scintilla/feature-requests/1210/">Feature #1210.</a>
</li>
<li>
Fix hang in Lua lexer when lexing a label upto the terminating "::".
Fix hang in Lua lexer when lexing a label up to the terminating "::".
<a href="https://sourceforge.net/p/scintilla/bugs/1999/">Bug #1999</a>.
</li>
<li>
@ -2820,7 +2853,7 @@
'Ctrl+L|SCI_LINEDELETE|'.
</li>
<li>
The Matlab lexer treats 'end' as a number rather than a keyword when used as a index.
The Matlab lexer treats 'end' as a number rather than a keyword when used as an index.
This also stops incorrect folding.
<a href="https://sourceforge.net/p/scintilla/bugs/1951/">Bug #1951</a>.
</li>
@ -9863,7 +9896,7 @@
SciTE crashing bug fixed in incremental search on Windows ME.
</li>
<li>
SciTE on Windows has a optional find and replace dialogs that can search through
SciTE on Windows has an optional find and replace dialogs that can search through
all buffers and search within a particular style number.
</li>
</ul>
@ -11432,7 +11465,7 @@
SciTE's Export as LaTeX output improved.
</li>
<li>
Better choice of autocompletion displaying above the caret rather then
Better choice of autocompletion displaying above the caret rather than
below when that is more sensible.
</li>
<li>
@ -14017,7 +14050,7 @@
<li>
Scintilla supports a 'savepoint' in the undo stack which can be set by the container when
the document is saved. Notifications are sent to the container when the savepoint is
entered or left, allowing the container to to display a dirty indicator and change its
entered or left, allowing the container to display a dirty indicator and change its
menus.
</li>
<li>

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="20231227" />
<meta name="Date.Modified" content="20240305" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
.logo {
@ -60,8 +60,8 @@
GTK, and macOS</font>
</td>
<td width="40%" align="right">
<font color="#FFCC99" size="3"> Release version 5.4.1<br />
Site last modified December 27 2023</font>
<font color="#FFCC99" size="3"> Release version 5.4.2<br />
Site last modified March 5 2024</font>
</td>
<td width="20%">
&nbsp;
@ -76,11 +76,11 @@
</tr>
</table>
<ul id="versionlist">
<li>Version 5.4.2 can save and restore undo history.</li>
<li>Version 5.4.1 adds IDocumentEditable interface to allow efficient interaction with document objects.</li>
<li>Version 5.4.0 fixes crashes on macOS 12 and older when built with Xcode 15.0.</li>
<li>Version 5.3.8 fixes excesssive memory use when deleting contiguous ranges backwards and is compatible with new macOS 14.</li>
<li>Version 5.3.7 fixes platform-specific issues on GTK, Qt, and Win32.</li>
<li>Version 5.3.6 improves cursor behaviour on Win32 and IME support on Win32 and Qt.</li>
</ul>
<ul id="menu">
<li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></li>

View File

@ -36,6 +36,7 @@
#include "Scintilla.h"
#include "ScintillaWidget.h"
#include "CharacterType.h"
#include "XPM.h"
#include "UniConversion.h"
@ -1085,6 +1086,27 @@ void SurfaceImpl::MeasureWidthsUTF8(const Font *font_, std::string_view text, XY
}
while (!iti.finished) {
iti.Next();
if (iti.curIndex < i) {
// Backwards movement indicater bidirectional.
// Divide into ASCII prefix and non-ASCII suffix as this is common case
// and produces accurate positions for the ASCII prefix.
size_t lenASCII=0;
while (lenASCII<text.length() && IsASCII(text[lenASCII])) {
lenASCII++;
}
const std::string_view asciiPrefix = text.substr(0, lenASCII);
const std::string_view bidiSuffix = text.substr(lenASCII);
// Recurse for ASCII prefix.
MeasureWidthsUTF8(font_, asciiPrefix, positions);
// Measure the whole bidiSuffix and spread its width evenly
const XYPOSITION endASCII = positions[lenASCII-1];
const XYPOSITION widthBidi = WidthText(font_, bidiSuffix);
const XYPOSITION widthByteBidi = widthBidi / bidiSuffix.length();
for (size_t bidiPos=0; bidiPos<bidiSuffix.length(); bidiPos++) {
positions[bidiPos+lenASCII] = endASCII + widthByteBidi * (bidiPos + 1);
}
return;
}
const int places = iti.curIndex - i;
while (i < iti.curIndex) {
// Evenly distribute space among bytes of this cluster.

View File

@ -9,6 +9,7 @@ PlatGTK.o: \
../include/Scintilla.h \
../include/Sci_Position.h \
../include/ScintillaWidget.h \
../src/CharacterType.h \
../src/XPM.h \
../src/UniConversion.h \
Wrappers.h \
@ -142,6 +143,7 @@ CellBuffer.o: \
../src/SparseVector.h \
../src/ChangeHistory.h \
../src/CellBuffer.h \
../src/UndoHistory.h \
../src/UniConversion.h
ChangeHistory.o: \
../src/ChangeHistory.cxx \
@ -478,6 +480,18 @@ Style.o: \
../src/Geometry.h \
../src/Platform.h \
../src/Style.h
UndoHistory.o: \
../src/UndoHistory.cxx \
../include/ScintillaTypes.h \
../src/Debugging.h \
../src/Position.h \
../src/SplitVector.h \
../src/Partitioning.h \
../src/RunStyles.h \
../src/SparseVector.h \
../src/ChangeHistory.h \
../src/CellBuffer.h \
../src/UndoHistory.h
UniConversion.o: \
../src/UniConversion.cxx \
../src/UniConversion.h

View File

@ -149,6 +149,7 @@ SRC_OBJS = \
RunStyles.o \
Selection.o \
Style.o \
UndoHistory.o \
UniConversion.o \
UniqueString.o \
ViewStyle.o \

View File

@ -339,6 +339,20 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP
#define SCI_GETCHARACTERCATEGORYOPTIMIZATION 2721
#define SCI_BEGINUNDOACTION 2078
#define SCI_ENDUNDOACTION 2079
#define SCI_GETUNDOACTIONS 2790
#define SCI_SETUNDOSAVEPOINT 2791
#define SCI_GETUNDOSAVEPOINT 2792
#define SCI_SETUNDODETACH 2793
#define SCI_GETUNDODETACH 2794
#define SCI_SETUNDOTENTATIVE 2795
#define SCI_GETUNDOTENTATIVE 2796
#define SCI_SETUNDOCURRENT 2797
#define SCI_GETUNDOCURRENT 2798
#define SCI_PUSHUNDOACTIONTYPE 2800
#define SCI_CHANGELASTUNDOACTIONTEXT 2801
#define SCI_GETUNDOACTIONTYPE 2802
#define SCI_GETUNDOACTIONPOSITION 2803
#define SCI_GETUNDOACTIONTEXT 2804
#define INDIC_PLAIN 0
#define INDIC_SQUIGGLE 1
#define INDIC_TT 2

View File

@ -833,6 +833,48 @@ fun void BeginUndoAction=2078(,)
# End a sequence of actions that is undone and redone as a unit.
fun void EndUndoAction=2079(,)
# How many undo actions are in the history?
get int GetUndoActions=2790(,)
# Set action as the save point
set void SetUndoSavePoint=2791(int action,)
# Which action is the save point?
get int GetUndoSavePoint=2792(,)
# Set action as the detach point
set void SetUndoDetach=2793(int action,)
# Which action is the detach point?
get int GetUndoDetach=2794(,)
# Set action as the tentative point
set void SetUndoTentative=2795(int action,)
# Which action is the tentative point?
get int GetUndoTentative=2796(,)
# Set action as the current point
set void SetUndoCurrent=2797(int action,)
# Which action is the current point?
get int GetUndoCurrent=2798(,)
# Push one action onto undo history with no text
fun void PushUndoActionType=2800(int type, position pos)
# Set the text and length of the most recently pushed action
fun void ChangeLastUndoActionText=2801(position length, string text)
# What is the type of an action?
get int GetUndoActionType=2802(int action,)
# What is the position of an action?
get position GetUndoActionPosition=2803(int action,)
# What is the text of an action?
get int GetUndoActionText=2804(int action, stringresult text)
# Indicator style enumeration and some constants
enu IndicatorStyle=INDIC_
val INDIC_PLAIN=0

View File

@ -245,6 +245,21 @@ public:
int CharacterCategoryOptimization();
void BeginUndoAction();
void EndUndoAction();
int UndoActions();
void SetUndoSavePoint(int action);
int UndoSavePoint();
void SetUndoDetach(int action);
int UndoDetach();
void SetUndoTentative(int action);
int UndoTentative();
void SetUndoCurrent(int action);
int UndoCurrent();
void PushUndoActionType(int type, Position pos);
void ChangeLastUndoActionText(Position length, const char *text);
int UndoActionType(int action);
Position UndoActionPosition(int action);
int UndoActionText(int action, char *text);
std::string UndoActionText(int action);
void IndicSetStyle(int indicator, Scintilla::IndicatorStyle indicatorStyle);
Scintilla::IndicatorStyle IndicGetStyle(int indicator);
void IndicSetFore(int indicator, Colour fore);

View File

@ -171,6 +171,20 @@ enum class Message {
GetCharacterCategoryOptimization = 2721,
BeginUndoAction = 2078,
EndUndoAction = 2079,
GetUndoActions = 2790,
SetUndoSavePoint = 2791,
GetUndoSavePoint = 2792,
SetUndoDetach = 2793,
GetUndoDetach = 2794,
SetUndoTentative = 2795,
GetUndoTentative = 2796,
SetUndoCurrent = 2797,
GetUndoCurrent = 2798,
PushUndoActionType = 2800,
ChangeLastUndoActionText = 2801,
GetUndoActionType = 2802,
GetUndoActionPosition = 2803,
GetUndoActionText = 2804,
IndicSetStyle = 2080,
IndicGetStyle = 2081,
IndicSetFore = 2082,

View File

@ -89,7 +89,7 @@ void WatcherHelper::NotifyErrorOccurred(Document *, void *, Status status) {
}
ScintillaDocument::ScintillaDocument(QObject *parent, void *pdoc_) :
QObject(parent), pdoc(pdoc_), docWatcher(nullptr) {
QObject(parent), pdoc(static_cast<Scintilla::IDocumentEditable *>(pdoc_)), docWatcher(nullptr) {
if (!pdoc) {
pdoc = new Document(DocumentOption::Default);
}
@ -153,8 +153,8 @@ bool ScintillaDocument::is_collecting_undo() {
return (static_cast<Document *>(pdoc))->IsCollectingUndo();
}
void ScintillaDocument::begin_undo_action() {
(static_cast<Document *>(pdoc))->BeginUndoAction();
void ScintillaDocument::begin_undo_action(bool coalesceWithPrior) {
(static_cast<Document *>(pdoc))->BeginUndoAction(coalesceWithPrior);
}
void ScintillaDocument::end_undo_action() {

View File

@ -23,11 +23,16 @@ class WatcherHelper;
#endif
#endif
// Forward declaration
namespace Scintilla {
class IDocumentEditable;
}
class EXPORT_IMPORT_API ScintillaDocument : public QObject
{
Q_OBJECT
void *pdoc;
Scintilla::IDocumentEditable *pdoc;
WatcherHelper *docWatcher;
public:
@ -45,7 +50,7 @@ public:
void delete_undo_history();
bool set_undo_collection(bool collect_undo);
bool is_collecting_undo();
void begin_undo_action();
void begin_undo_action(bool coalesceWithPrior = false);
void end_undo_action();
void set_save_point();
bool is_save_point();

View File

@ -13,7 +13,7 @@ TEMPLATE = lib
CONFIG += lib_bundle
CONFIG += c++1z
VERSION = 5.4.1
VERSION = 5.4.2
SOURCES += \
ScintillaEdit.cpp \
@ -23,6 +23,7 @@ SOURCES += \
../ScintillaEditBase/ScintillaEditBase.cpp \
../../src/XPM.cxx \
../../src/ViewStyle.cxx \
../../src/UndoHistory.cxx \
../../src/UniqueString.cxx \
../../src/UniConversion.cxx \
../../src/Style.cxx \

View File

@ -13,7 +13,7 @@ TEMPLATE = lib
CONFIG += lib_bundle
CONFIG += c++1z
VERSION = 5.4.1
VERSION = 5.4.2
SOURCES += \
PlatQt.cpp \
@ -23,6 +23,7 @@ SOURCES += \
../../src/ViewStyle.cxx \
../../src/UniqueString.cxx \
../../src/UniConversion.cxx \
../../src/UndoHistory.cxx \
../../src/Style.cxx \
../../src/Selection.cxx \
../../src/ScintillaBase.cxx \
@ -58,6 +59,7 @@ HEADERS += \
ScintillaEditBase.h \
../../src/XPM.h \
../../src/ViewStyle.h \
../../src/UndoHistory.h \
../../src/UniConversion.h \
../../src/Style.h \
../../src/SplitVector.h \

View File

@ -122,6 +122,7 @@
#include "ContractionState.h"
#include "ChangeHistory.h"
#include "CellBuffer.h"
#include "UndoHistory.h"
#include "PerLine.h"
#include "CallTip.h"
#include "KeyMap.h"

View File

@ -32,6 +32,7 @@
#include "SparseVector.h"
#include "ChangeHistory.h"
#include "CellBuffer.h"
#include "UndoHistory.h"
#include "UniConversion.h"
namespace Scintilla::Internal {
@ -329,292 +330,13 @@ public:
}
};
Action::Action() noexcept {
at = ActionType::start;
position = 0;
lenData = 0;
mayCoalesce = false;
}
void Action::Create(ActionType at_, Sci::Position position_, const char *data_, Sci::Position lenData_, bool mayCoalesce_) {
data = nullptr;
position = position_;
at = at_;
if (lenData_) {
data = std::make_unique<char[]>(lenData_);
memcpy(&data[0], data_, lenData_);
}
lenData = lenData_;
mayCoalesce = mayCoalesce_;
}
void Action::Clear() noexcept {
data = nullptr;
lenData = 0;
}
// The undo history stores a sequence of user operations that represent the user's view of the
// commands executed on the text.
// Each user operation contains a sequence of text insertion and text deletion actions.
// All the user operations are stored in a list of individual actions with 'start' actions used
// as delimiters between user operations.
// Initially there is one start action in the history.
// As each action is performed, it is recorded in the history. The action may either become
// part of the current user operation or may start a new user operation. If it is to be part of the
// current operation, then it overwrites the current last action. If it is to be part of a new
// operation, it is appended after the current last action.
// After writing the new action, a new start action is appended at the end of the history.
// The decision of whether to start a new user operation is based upon two factors. If a
// compound operation has been explicitly started by calling BeginUndoAction and no matching
// EndUndoAction (these calls nest) has been called, then the action is coalesced into the current
// operation. If there is no outstanding BeginUndoAction call then a new operation is started
// unless it looks as if the new action is caused by the user typing or deleting a stream of text.
// Sequences that look like typing or deletion are coalesced into a single user operation.
UndoHistory::UndoHistory() {
actions.resize(3);
maxAction = 0;
currentAction = 0;
undoSequenceDepth = 0;
savePoint = 0;
tentativePoint = -1;
actions[currentAction].Create(ActionType::start);
}
void UndoHistory::EnsureUndoRoom() {
// Have to test that there is room for 2 more actions in the array
// as two actions may be created by the calling function
if (static_cast<size_t>(currentAction) >= (actions.size() - 2)) {
// Run out of undo nodes so extend the array
actions.resize(actions.size() * 2);
}
}
const char *UndoHistory::AppendAction(ActionType at, Sci::Position position, const char *data, Sci::Position lengthData,
bool &startSequence, bool mayCoalesce) {
EnsureUndoRoom();
//Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
//Platform::DebugPrintf("^ %d action %d %d\n", actions[currentAction - 1].at,
// actions[currentAction - 1].position, actions[currentAction - 1].lenData);
if (currentAction < savePoint) {
savePoint = -1;
if (!detach) {
detach = currentAction;
}
} else if (detach && (*detach > currentAction)) {
detach = currentAction;
}
const int oldCurrentAction = currentAction;
if (currentAction >= 1) {
if (0 == undoSequenceDepth) {
// Top level actions may not always be coalesced
ptrdiff_t targetAct = -1;
const Action *actPrevious = &(actions[currentAction + targetAct]);
// Container actions may forward the coalesce state of Scintilla Actions.
while ((actPrevious->at == ActionType::container) && actPrevious->mayCoalesce) {
targetAct--;
actPrevious = &(actions[currentAction + targetAct]);
}
// See if current action can be coalesced into previous action
// Will work if both are inserts or deletes and position is same
if ((currentAction == savePoint) || (currentAction == tentativePoint)) {
currentAction++;
} else if (!actions[currentAction].mayCoalesce) {
// Not allowed to coalesce if this set
currentAction++;
} else if (!mayCoalesce || !actPrevious->mayCoalesce) {
currentAction++;
} else if (at == ActionType::container || actions[currentAction].at == ActionType::container) {
; // A coalescible containerAction
} else if ((at != actPrevious->at) && (actPrevious->at != ActionType::start)) {
currentAction++;
} else if ((at == ActionType::insert) &&
(position != (actPrevious->position + actPrevious->lenData))) {
// Insertions must be immediately after to coalesce
currentAction++;
} else if (at == ActionType::remove) {
if ((lengthData == 1) || (lengthData == 2)) {
if ((position + lengthData) == actPrevious->position) {
; // Backspace -> OK
} else if (position == actPrevious->position) {
; // Delete -> OK
} else {
// Removals must be at same position to coalesce
currentAction++;
}
} else {
// Removals must be of one character to coalesce
currentAction++;
}
} else {
// Action coalesced.
}
} else {
// Actions not at top level are always coalesced unless this is after return to top level
if (!actions[currentAction].mayCoalesce)
currentAction++;
}
} else {
currentAction++;
}
startSequence = oldCurrentAction != currentAction;
const int actionWithData = currentAction;
actions[currentAction].Create(at, position, data, lengthData, mayCoalesce);
currentAction++;
actions[currentAction].Create(ActionType::start);
maxAction = currentAction;
return actions[actionWithData].data.get();
}
void UndoHistory::BeginUndoAction() {
EnsureUndoRoom();
if (undoSequenceDepth == 0) {
if (actions[currentAction].at != ActionType::start) {
currentAction++;
actions[currentAction].Create(ActionType::start);
maxAction = currentAction;
}
actions[currentAction].mayCoalesce = false;
}
undoSequenceDepth++;
}
void UndoHistory::EndUndoAction() {
PLATFORM_ASSERT(undoSequenceDepth > 0);
EnsureUndoRoom();
undoSequenceDepth--;
if (0 == undoSequenceDepth) {
if (actions[currentAction].at != ActionType::start) {
currentAction++;
actions[currentAction].Create(ActionType::start);
maxAction = currentAction;
}
actions[currentAction].mayCoalesce = false;
}
}
void UndoHistory::DropUndoSequence() {
undoSequenceDepth = 0;
}
void UndoHistory::DeleteUndoHistory() {
for (int i = 1; i < maxAction; i++)
actions[i].Clear();
maxAction = 0;
currentAction = 0;
actions[currentAction].Create(ActionType::start);
savePoint = 0;
tentativePoint = -1;
}
void UndoHistory::SetSavePoint() noexcept {
savePoint = currentAction;
detach.reset();
}
bool UndoHistory::IsSavePoint() const noexcept {
return savePoint == currentAction;
}
bool UndoHistory::BeforeSavePoint() const noexcept {
return (savePoint < 0) || (savePoint > currentAction);
}
bool UndoHistory::BeforeReachableSavePoint() const noexcept {
return (savePoint >= 0) && !detach && (savePoint > currentAction);
}
bool UndoHistory::AfterSavePoint() const noexcept {
return (savePoint >= 0) && (savePoint <= currentAction);
}
bool UndoHistory::AfterDetachPoint() const noexcept {
return detach && (*detach < currentAction);
}
void UndoHistory::TentativeStart() {
tentativePoint = currentAction;
}
void UndoHistory::TentativeCommit() {
tentativePoint = -1;
// Truncate undo history
maxAction = currentAction;
}
bool UndoHistory::TentativeActive() const noexcept {
return tentativePoint >= 0;
}
int UndoHistory::TentativeSteps() noexcept {
// Drop any trailing startAction
if (actions[currentAction].at == ActionType::start && currentAction > 0)
currentAction--;
if (tentativePoint >= 0)
return currentAction - tentativePoint;
else
return -1;
}
bool UndoHistory::CanUndo() const noexcept {
return (currentAction > 0) && (maxAction > 0);
}
int UndoHistory::StartUndo() {
// Drop any trailing startAction
if (actions[currentAction].at == ActionType::start && currentAction > 0)
currentAction--;
// Count the steps in this action
int act = currentAction;
while (actions[act].at != ActionType::start && act > 0) {
act--;
}
return currentAction - act;
}
const Action &UndoHistory::GetUndoStep() const {
return actions[currentAction];
}
void UndoHistory::CompletedUndoStep() {
currentAction--;
}
bool UndoHistory::CanRedo() const noexcept {
return maxAction > currentAction;
}
int UndoHistory::StartRedo() {
// Drop any leading startAction
if (currentAction < maxAction && actions[currentAction].at == ActionType::start)
currentAction++;
// Count the steps in this action
int act = currentAction;
while (act < maxAction && actions[act].at != ActionType::start) {
act++;
}
return act - currentAction;
}
const Action &UndoHistory::GetRedoStep() const {
return actions[currentAction];
}
void UndoHistory::CompletedRedoStep() {
currentAction++;
}
CellBuffer::CellBuffer(bool hasStyles_, bool largeDocument_) :
hasStyles(hasStyles_), largeDocument(largeDocument_) {
readOnly = false;
utf8Substance = false;
utf8LineEnds = LineEndType::Default;
collectingUndo = true;
uh = std::make_unique<UndoHistory>();
if (largeDocument)
plv = std::make_unique<LineVector<Sci::Position>>();
else
@ -704,12 +426,12 @@ const char *CellBuffer::InsertString(Sci::Position position, const char *s, Sci:
if (collectingUndo) {
// Save into the undo/redo stack, but only the characters - not the formatting
// This takes up about half load time
data = uh.AppendAction(ActionType::insert, position, s, insertLength, startSequence);
data = uh->AppendAction(ActionType::insert, position, s, insertLength, startSequence);
}
BasicInsertString(position, s, insertLength);
if (changeHistory) {
changeHistory->Insert(position, insertLength, collectingUndo, uh.BeforeReachableSavePoint());
changeHistory->Insert(position, insertLength, collectingUndo, uh->BeforeReachableSavePoint());
}
}
return data;
@ -756,12 +478,12 @@ const char *CellBuffer::DeleteChars(Sci::Position position, Sci::Position delete
// Save into the undo/redo stack, but only the characters - not the formatting
// The gap would be moved to position anyway for the deletion so this doesn't cost extra
data = substance.RangePointer(position, deleteLength);
data = uh.AppendAction(ActionType::remove, position, data, deleteLength, startSequence);
data = uh->AppendAction(ActionType::remove, position, data, deleteLength, startSequence);
}
if (changeHistory) {
changeHistory->DeleteRangeSavingHistory(position, deleteLength,
uh.BeforeReachableSavePoint(), uh.AfterDetachPoint());
uh->BeforeReachableSavePoint(), uh->AfterOrAtDetachPoint());
}
BasicDeleteChars(position, deleteLength);
@ -908,30 +630,30 @@ bool CellBuffer::HasStyles() const noexcept {
}
void CellBuffer::SetSavePoint() {
uh.SetSavePoint();
uh->SetSavePoint();
if (changeHistory) {
changeHistory->SetSavePoint();
}
}
bool CellBuffer::IsSavePoint() const noexcept {
return uh.IsSavePoint();
return uh->IsSavePoint();
}
void CellBuffer::TentativeStart() {
uh.TentativeStart();
void CellBuffer::TentativeStart() noexcept {
uh->TentativeStart();
}
void CellBuffer::TentativeCommit() {
uh.TentativeCommit();
void CellBuffer::TentativeCommit() noexcept {
uh->TentativeCommit();
}
int CellBuffer::TentativeSteps() noexcept {
return uh.TentativeSteps();
return uh->TentativeSteps();
}
bool CellBuffer::TentativeActive() const noexcept {
return uh.TentativeActive();
return uh->TentativeActive();
}
// Without undo
@ -1330,9 +1052,9 @@ void CellBuffer::BasicDeleteChars(Sci::Position position, Sci::Position deleteLe
}
}
bool CellBuffer::SetUndoCollection(bool collectUndo) {
bool CellBuffer::SetUndoCollection(bool collectUndo) noexcept {
collectingUndo = collectUndo;
uh.DropUndoSequence();
uh->DropUndoSequence();
return collectingUndo;
}
@ -1340,95 +1062,224 @@ bool CellBuffer::IsCollectingUndo() const noexcept {
return collectingUndo;
}
void CellBuffer::BeginUndoAction() {
uh.BeginUndoAction();
void CellBuffer::BeginUndoAction(bool mayCoalesce) noexcept {
uh->BeginUndoAction(mayCoalesce);
}
void CellBuffer::EndUndoAction() {
uh.EndUndoAction();
void CellBuffer::EndUndoAction() noexcept {
uh->EndUndoAction();
}
void CellBuffer::AddUndoAction(Sci::Position token, bool mayCoalesce) {
bool startSequence = false;
uh.AppendAction(ActionType::container, token, nullptr, 0, startSequence, mayCoalesce);
uh->AppendAction(ActionType::container, token, nullptr, 0, startSequence, mayCoalesce);
}
void CellBuffer::DeleteUndoHistory() {
uh.DeleteUndoHistory();
void CellBuffer::DeleteUndoHistory() noexcept {
uh->DeleteUndoHistory();
}
bool CellBuffer::CanUndo() const noexcept {
return uh.CanUndo();
return uh->CanUndo();
}
int CellBuffer::StartUndo() {
return uh.StartUndo();
int CellBuffer::StartUndo() noexcept {
return uh->StartUndo();
}
const Action &CellBuffer::GetUndoStep() const {
return uh.GetUndoStep();
Action CellBuffer::GetUndoStep() const noexcept {
return uh->GetUndoStep();
}
void CellBuffer::PerformUndoStep() {
const Action &actionStep = uh.GetUndoStep();
if (changeHistory && uh.BeforeSavePoint()) {
const Action previousStep = uh->GetUndoStep();
// PreviousBeforeSavePoint and AfterDetachPoint are called since acting on the previous action,
// that is currentAction-1
if (changeHistory && uh->PreviousBeforeSavePoint()) {
changeHistory->StartReversion();
}
if (actionStep.at == ActionType::insert) {
if (substance.Length() < actionStep.lenData) {
if (previousStep.at == ActionType::insert) {
if (substance.Length() < previousStep.lenData) {
throw std::runtime_error(
"CellBuffer::PerformUndoStep: deletion must be less than document length.");
}
if (changeHistory) {
changeHistory->DeleteRange(actionStep.position, actionStep.lenData,
uh.BeforeSavePoint() && !uh.AfterDetachPoint());
changeHistory->DeleteRange(previousStep.position, previousStep.lenData,
uh->PreviousBeforeSavePoint() && !uh->AfterDetachPoint());
}
BasicDeleteChars(actionStep.position, actionStep.lenData);
} else if (actionStep.at == ActionType::remove) {
BasicInsertString(actionStep.position, actionStep.data.get(), actionStep.lenData);
BasicDeleteChars(previousStep.position, previousStep.lenData);
} else if (previousStep.at == ActionType::remove) {
BasicInsertString(previousStep.position, previousStep.data, previousStep.lenData);
if (changeHistory) {
changeHistory->UndoDeleteStep(actionStep.position, actionStep.lenData, uh.AfterDetachPoint());
changeHistory->UndoDeleteStep(previousStep.position, previousStep.lenData, uh->AfterDetachPoint());
}
}
uh.CompletedUndoStep();
uh->CompletedUndoStep();
}
bool CellBuffer::CanRedo() const noexcept {
return uh.CanRedo();
return uh->CanRedo();
}
int CellBuffer::StartRedo() {
return uh.StartRedo();
int CellBuffer::StartRedo() noexcept {
return uh->StartRedo();
}
const Action &CellBuffer::GetRedoStep() const {
return uh.GetRedoStep();
Action CellBuffer::GetRedoStep() const noexcept {
return uh->GetRedoStep();
}
void CellBuffer::PerformRedoStep() {
const Action &actionStep = uh.GetRedoStep();
const Action actionStep = uh->GetRedoStep();
if (actionStep.at == ActionType::insert) {
BasicInsertString(actionStep.position, actionStep.data.get(), actionStep.lenData);
BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);
if (changeHistory) {
changeHistory->Insert(actionStep.position, actionStep.lenData, collectingUndo,
uh.BeforeSavePoint() && !uh.AfterDetachPoint());
uh->BeforeSavePoint() && !uh->AfterOrAtDetachPoint());
}
} else if (actionStep.at == ActionType::remove) {
if (changeHistory) {
changeHistory->DeleteRangeSavingHistory(actionStep.position, actionStep.lenData,
uh.BeforeReachableSavePoint(), uh.AfterDetachPoint());
uh->BeforeReachableSavePoint(), uh->AfterOrAtDetachPoint());
}
BasicDeleteChars(actionStep.position, actionStep.lenData);
}
if (changeHistory && uh.AfterSavePoint()) {
if (changeHistory && uh->AfterSavePoint()) {
changeHistory->EndReversion();
}
uh.CompletedRedoStep();
uh->CompletedRedoStep();
}
int CellBuffer::UndoActions() const noexcept {
return uh->Actions();
}
void CellBuffer::SetUndoSavePoint(int action) noexcept {
uh->SetSavePoint(action);
}
int CellBuffer::UndoSavePoint() const noexcept {
return uh->SavePoint();
}
void CellBuffer::SetUndoDetach(int action) noexcept {
uh->SetDetachPoint(action);
}
int CellBuffer::UndoDetach() const noexcept {
return uh->DetachPoint();
}
void CellBuffer::SetUndoTentative(int action) noexcept {
uh->SetTentative(action);
}
int CellBuffer::UndoTentative() const noexcept {
return uh->TentativePoint();
}
namespace {
void RestoreChangeHistory(const UndoHistory *uh, ChangeHistory *changeHistory) {
// Replay all undo actions into changeHistory
const int savePoint = uh->SavePoint();
const int detachPoint = uh->DetachPoint();
const int currentPoint = uh->Current();
for (int act = 0; act < uh->Actions(); act++) {
const ActionType type = static_cast<ActionType>(uh->Type(act) & ~coalesceFlag);
const Sci::Position position = uh->Position(act);
const Sci::Position length = uh->Length(act);
const bool beforeSave = act < savePoint || ((detachPoint >= 0) && (detachPoint > act));
const bool afterDetach = (detachPoint >= 0) && (detachPoint < act);
switch (type) {
case ActionType::insert:
changeHistory->Insert(position, length, true, beforeSave);
break;
case ActionType::remove:
changeHistory->DeleteRangeSavingHistory(position, length, beforeSave, afterDetach);
break;
default:
// Only insertions and deletions go into change history
break;
}
changeHistory->Check();
}
// Undo back to currentPoint, updating change history
for (int act = uh->Actions() - 1; act >= currentPoint; act--) {
const ActionType type = static_cast<ActionType>(uh->Type(act) & ~coalesceFlag);
const Sci::Position position = uh->Position(act);
const Sci::Position length = uh->Length(act);
const bool beforeSave = act < savePoint;
const bool afterDetach = (detachPoint >= 0) && (detachPoint < act);
if (beforeSave) {
changeHistory->StartReversion();
}
switch (type) {
case ActionType::insert:
changeHistory->DeleteRange(position, length, beforeSave && !afterDetach);
break;
case ActionType::remove:
changeHistory->UndoDeleteStep(position, length, afterDetach);
break;
default:
// Only insertions and deletions go into change history
break;
}
changeHistory->Check();
}
}
}
void CellBuffer::SetUndoCurrent(int action) {
uh->SetCurrent(action, Length());
if (changeHistory) {
if ((uh->DetachPoint() >= 0) && (uh->SavePoint() >= 0)) {
// Can't have a valid save point and a valid detach point at same time
uh->DeleteUndoHistory();
changeHistory.reset();
throw std::runtime_error("UndoHistory::SetCurrent: invalid undo history.");
}
const intptr_t sizeChange = uh->Delta(action);
const intptr_t lengthOriginal = Length() - sizeChange;
// Recreate empty change history
changeHistory = std::make_unique<ChangeHistory>(lengthOriginal);
RestoreChangeHistory(uh.get(), changeHistory.get());
if (Length() != changeHistory->Length()) {
uh->DeleteUndoHistory();
changeHistory.reset();
throw std::runtime_error("UndoHistory::SetCurrent: invalid undo history.");
}
}
}
int CellBuffer::UndoCurrent() const noexcept {
return uh->Current();
}
int CellBuffer::UndoActionType(int action) const noexcept {
return uh->Type(action);
}
Sci::Position CellBuffer::UndoActionPosition(int action) const noexcept {
return uh->Position(action);
}
std::string_view CellBuffer::UndoActionText(int action) const noexcept {
return uh->Text(action);
}
void CellBuffer::PushUndoActionType(int type, Sci::Position position) {
uh->PushUndoActionType(type, position);
}
void CellBuffer::ChangeLastUndoActionText(size_t length, const char *text) {
uh->ChangeLastUndoActionText(length, text);
}
void CellBuffer::ChangeHistorySet(bool set) {
if (set) {
if (!changeHistory && !uh.CanUndo()) {
if (!changeHistory && !uh->CanUndo()) {
changeHistory = std::make_unique<ChangeHistory>(Length());
}
} else {

View File

@ -20,79 +20,25 @@ public:
virtual void RemoveLine(Sci::Line line)=0;
};
class UndoHistory;
class ChangeHistory;
/**
* The line vector contains information about each of the lines in a cell buffer.
*/
class ILineVector;
enum class ActionType { insert, remove, start, container };
enum class ActionType : unsigned char { insert, remove, container };
/**
* Actions are used to store all the information required to perform one undo/redo step.
* Actions are used to return the information required to report one undo/redo step.
*/
class Action {
public:
ActionType at;
Sci::Position position;
std::unique_ptr<char[]> data;
Sci::Position lenData;
bool mayCoalesce;
Action() noexcept;
void Create(ActionType at_, Sci::Position position_=0, const char *data_=nullptr, Sci::Position lenData_=0, bool mayCoalesce_=true);
void Clear() noexcept;
};
/**
*
*/
class UndoHistory {
std::vector<Action> actions;
int maxAction;
int currentAction;
int undoSequenceDepth;
int savePoint;
int tentativePoint;
std::optional<int> detach;
void EnsureUndoRoom();
public:
UndoHistory();
const char *AppendAction(ActionType at, Sci::Position position, const char *data, Sci::Position lengthData, bool &startSequence, bool mayCoalesce=true);
void BeginUndoAction();
void EndUndoAction();
void DropUndoSequence();
void DeleteUndoHistory();
/// The save point is a marker in the undo stack where the container has stated that
/// the buffer was saved. Undo and redo can move over the save point.
void SetSavePoint() noexcept;
bool IsSavePoint() const noexcept;
bool BeforeSavePoint() const noexcept;
bool BeforeReachableSavePoint() const noexcept;
bool AfterSavePoint() const noexcept;
bool AfterDetachPoint() const noexcept;
// Tentative actions are used for input composition so that it can be undone cleanly
void TentativeStart();
void TentativeCommit();
bool TentativeActive() const noexcept;
int TentativeSteps() noexcept;
/// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is
/// called that many times. Similarly for redo.
bool CanUndo() const noexcept;
int StartUndo();
const Action &GetUndoStep() const;
void CompletedUndoStep();
bool CanRedo() const noexcept;
int StartRedo();
const Action &GetRedoStep() const;
void CompletedRedoStep();
struct Action {
ActionType at = ActionType::insert;
bool mayCoalesce = false;
Sci::Position position = 0;
const char *data = nullptr;
Sci::Position lenData = 0;
};
struct SplitView {
@ -137,7 +83,7 @@ private:
Scintilla::LineEndType utf8LineEnds;
bool collectingUndo;
UndoHistory uh;
std::unique_ptr<UndoHistory> uh;
std::unique_ptr<ChangeHistory> changeHistory;
@ -211,29 +157,44 @@ public:
void SetSavePoint();
bool IsSavePoint() const noexcept;
void TentativeStart();
void TentativeCommit();
void TentativeStart() noexcept;
void TentativeCommit() noexcept;
bool TentativeActive() const noexcept;
int TentativeSteps() noexcept;
bool SetUndoCollection(bool collectUndo);
bool SetUndoCollection(bool collectUndo) noexcept;
bool IsCollectingUndo() const noexcept;
void BeginUndoAction();
void EndUndoAction();
void BeginUndoAction(bool mayCoalesce=false) noexcept;
void EndUndoAction() noexcept;
void AddUndoAction(Sci::Position token, bool mayCoalesce);
void DeleteUndoHistory();
void DeleteUndoHistory() noexcept;
/// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is
/// called that many times. Similarly for redo.
bool CanUndo() const noexcept;
int StartUndo();
const Action &GetUndoStep() const;
int StartUndo() noexcept;
Action GetUndoStep() const noexcept;
void PerformUndoStep();
bool CanRedo() const noexcept;
int StartRedo();
const Action &GetRedoStep() const;
int StartRedo() noexcept;
Action GetRedoStep() const noexcept;
void PerformRedoStep();
int UndoActions() const noexcept;
void SetUndoSavePoint(int action) noexcept;
int UndoSavePoint() const noexcept;
void SetUndoDetach(int action) noexcept;
int UndoDetach() const noexcept;
void SetUndoTentative(int action) noexcept;
int UndoTentative() const noexcept;
void SetUndoCurrent(int action);
int UndoCurrent() const noexcept;
int UndoActionType(int action) const noexcept;
Sci::Position UndoActionPosition(int action) const noexcept;
std::string_view UndoActionText(int action) const noexcept;
void PushUndoActionType(int type, Sci::Position position);
void ChangeLastUndoActionText(size_t length, const char *text);
void ChangeHistorySet(bool set);
[[nodiscard]] int EditionAt(Sci::Position pos) const noexcept;
[[nodiscard]] Sci::Position EditionEndRun(Sci::Position pos) const noexcept;

View File

@ -303,7 +303,7 @@ void Document::TentativeUndo() {
//Platform::DebugPrintf("Steps=%d\n", steps);
for (int step = 0; step < steps; step++) {
const Sci::Line prevLinesTotal = LinesTotal();
const Action &action = cb.GetUndoStep();
const Action action = cb.GetUndoStep();
if (action.at == ActionType::remove) {
NotifyModified(DocModification(
ModificationFlags::BeforeInsert | ModificationFlags::Undo, action));
@ -338,7 +338,7 @@ void Document::TentativeUndo() {
modFlags |= ModificationFlags::MultilineUndoRedo;
}
NotifyModified(DocModification(modFlags, action.position, action.lenData,
linesAdded, action.data.get()));
linesAdded, action.data));
}
const bool endSavePoint = cb.IsSavePoint();
@ -351,6 +351,62 @@ void Document::TentativeUndo() {
}
}
int Document::UndoActions() const noexcept {
return cb.UndoActions();
}
void Document::SetUndoSavePoint(int action) noexcept {
cb.SetUndoSavePoint(action);
}
int Document::UndoSavePoint() const noexcept {
return cb.UndoSavePoint();
}
void Document::SetUndoDetach(int action) noexcept {
cb.SetUndoDetach(action);
}
int Document::UndoDetach() const noexcept {
return cb.UndoDetach();
}
void Document::SetUndoTentative(int action) noexcept {
cb.SetUndoTentative(action);
}
int Document::UndoTentative() const noexcept {
return cb.UndoTentative();
}
void Document::SetUndoCurrent(int action) {
cb.SetUndoCurrent(action);
}
int Document::UndoCurrent() const noexcept {
return cb.UndoCurrent();
}
int Document::UndoActionType(int action) const noexcept {
return cb.UndoActionType(action);
}
Sci::Position Document::UndoActionPosition(int action) const noexcept {
return cb.UndoActionPosition(action);
}
std::string_view Document::UndoActionText(int action) const noexcept {
return cb.UndoActionText(action);
}
void Document::PushUndoActionType(int type, Sci::Position position) {
cb.PushUndoActionType(type, position);
}
void Document::ChangeLastUndoActionText(size_t length, const char *text) {
cb.ChangeLastUndoActionText(length, text);
}
int Document::GetMark(Sci::Line line, bool includeChangeHistory) const {
int marksHistory = 0;
if (includeChangeHistory && (line < LinesTotal())) {
@ -1363,13 +1419,10 @@ Sci::Position Document::Undo() {
bool multiLine = false;
const int steps = cb.StartUndo();
//Platform::DebugPrintf("Steps=%d\n", steps);
Sci::Position coalescedRemovePos = -1;
Sci::Position coalescedRemoveLen = 0;
Sci::Position prevRemoveActionPos = -1;
Sci::Position prevRemoveActionLen = 0;
Range coalescedRemove; // Default is empty at 0
for (int step = 0; step < steps; step++) {
const Sci::Line prevLinesTotal = LinesTotal();
const Action &action = cb.GetUndoStep();
const Action action = cb.GetUndoStep();
if (action.at == ActionType::remove) {
NotifyModified(DocModification(
ModificationFlags::BeforeInsert | ModificationFlags::Undo, action));
@ -1377,12 +1430,6 @@ Sci::Position Document::Undo() {
DocModification dm(ModificationFlags::Container | ModificationFlags::Undo);
dm.token = action.position;
NotifyModified(dm);
if (!action.mayCoalesce) {
coalescedRemovePos = -1;
coalescedRemoveLen = 0;
prevRemoveActionPos = -1;
prevRemoveActionLen = 0;
}
} else {
NotifyModified(DocModification(
ModificationFlags::BeforeDelete | ModificationFlags::Undo, action));
@ -1398,22 +1445,15 @@ Sci::Position Document::Undo() {
if (action.at == ActionType::remove) {
newPos += action.lenData;
modFlags |= ModificationFlags::InsertText;
if ((coalescedRemoveLen > 0) &&
(action.position == prevRemoveActionPos || action.position == (prevRemoveActionPos + prevRemoveActionLen))) {
coalescedRemoveLen += action.lenData;
newPos = coalescedRemovePos + coalescedRemoveLen;
if (coalescedRemove.Contains(action.position)) {
coalescedRemove.end += action.lenData;
newPos = coalescedRemove.end;
} else {
coalescedRemovePos = action.position;
coalescedRemoveLen = action.lenData;
coalescedRemove = Range(action.position, action.position + action.lenData);
}
prevRemoveActionPos = action.position;
prevRemoveActionLen = action.lenData;
} else if (action.at == ActionType::insert) {
modFlags |= ModificationFlags::DeleteText;
coalescedRemovePos = -1;
coalescedRemoveLen = 0;
prevRemoveActionPos = -1;
prevRemoveActionLen = 0;
coalescedRemove = Range();
}
if (steps > 1)
modFlags |= ModificationFlags::MultiStepUndoRedo;
@ -1426,7 +1466,7 @@ Sci::Position Document::Undo() {
modFlags |= ModificationFlags::MultilineUndoRedo;
}
NotifyModified(DocModification(modFlags, action.position, action.lenData,
linesAdded, action.data.get()));
linesAdded, action.data));
}
const bool endSavePoint = cb.IsSavePoint();
@ -1449,7 +1489,7 @@ Sci::Position Document::Redo() {
const int steps = cb.StartRedo();
for (int step = 0; step < steps; step++) {
const Sci::Line prevLinesTotal = LinesTotal();
const Action &action = cb.GetRedoStep();
const Action action = cb.GetRedoStep();
if (action.at == ActionType::insert) {
NotifyModified(DocModification(
ModificationFlags::BeforeInsert | ModificationFlags::Redo, action));
@ -1486,7 +1526,7 @@ Sci::Position Document::Redo() {
}
NotifyModified(
DocModification(modFlags, action.position, action.lenData,
linesAdded, action.data.get()));
linesAdded, action.data));
}
const bool endSavePoint = cb.IsSavePoint();
@ -2195,7 +2235,7 @@ Sci::Position Document::FindText(Sci::Position minPos, Sci::Position maxPos, con
for (int b = 1; b < widthCharBytes; b++) {
bytes[b] = cbView.CharAt(posIndexDocument + b);
}
widthChar = UTF8Classify(reinterpret_cast<const unsigned char *>(bytes), widthCharBytes) & UTF8MaskWidth;
widthChar = UTF8Classify(bytes, widthCharBytes) & UTF8MaskWidth;
if (!indexSearch) { // First character
widthFirstCharacter = widthChar;
}

View File

@ -390,22 +390,37 @@ public:
Sci::Position Redo();
bool CanUndo() const noexcept { return cb.CanUndo(); }
bool CanRedo() const noexcept { return cb.CanRedo(); }
void DeleteUndoHistory() { cb.DeleteUndoHistory(); }
bool SetUndoCollection(bool collectUndo) {
void DeleteUndoHistory() noexcept { cb.DeleteUndoHistory(); }
bool SetUndoCollection(bool collectUndo) noexcept {
return cb.SetUndoCollection(collectUndo);
}
bool IsCollectingUndo() const noexcept { return cb.IsCollectingUndo(); }
void BeginUndoAction() { cb.BeginUndoAction(); }
void EndUndoAction() { cb.EndUndoAction(); }
void BeginUndoAction(bool coalesceWithPrior=false) noexcept { cb.BeginUndoAction(coalesceWithPrior); }
void EndUndoAction() noexcept { cb.EndUndoAction(); }
void AddUndoAction(Sci::Position token, bool mayCoalesce) { cb.AddUndoAction(token, mayCoalesce); }
void SetSavePoint();
bool IsSavePoint() const noexcept { return cb.IsSavePoint(); }
void TentativeStart() { cb.TentativeStart(); }
void TentativeCommit() { cb.TentativeCommit(); }
void TentativeStart() noexcept { cb.TentativeStart(); }
void TentativeCommit() noexcept { cb.TentativeCommit(); }
void TentativeUndo();
bool TentativeActive() const noexcept { return cb.TentativeActive(); }
int UndoActions() const noexcept;
void SetUndoSavePoint(int action) noexcept;
int UndoSavePoint() const noexcept;
void SetUndoDetach(int action) noexcept;
int UndoDetach() const noexcept;
void SetUndoTentative(int action) noexcept;
int UndoTentative() const noexcept;
void SetUndoCurrent(int action);
int UndoCurrent() const noexcept;
int UndoActionType(int action) const noexcept;
Sci::Position UndoActionPosition(int action) const noexcept;
std::string_view UndoActionText(int action) const noexcept;
void PushUndoActionType(int type, Sci::Position position);
void ChangeLastUndoActionText(size_t length, const char *text);
void ChangeHistorySet(bool set) { cb.ChangeHistorySet(set); }
[[nodiscard]] int EditionAt(Sci::Position pos) const noexcept { return cb.EditionAt(pos); }
[[nodiscard]] Sci::Position EditionEndRun(Sci::Position pos) const noexcept { return cb.EditionEndRun(pos); }
@ -427,7 +442,7 @@ public:
static std::string TransformLineEnds(const char *s, size_t len, Scintilla::EndOfLine eolModeWanted);
void ConvertLineEnds(Scintilla::EndOfLine eolModeSet);
std::string_view EOLString() const noexcept;
void SetReadOnly(bool set) { cb.SetReadOnly(set); }
void SetReadOnly(bool set) noexcept { cb.SetReadOnly(set); }
bool IsReadOnly() const noexcept { return cb.IsReadOnly(); }
bool IsLarge() const noexcept { return cb.IsLarge(); }
Scintilla::DocumentOption Options() const noexcept;
@ -565,7 +580,7 @@ class UndoGroup {
Document *pdoc;
bool groupNeeded;
public:
UndoGroup(Document *pdoc_, bool groupNeeded_=true) :
UndoGroup(Document *pdoc_, bool groupNeeded_=true) noexcept :
pdoc(pdoc_), groupNeeded(groupNeeded_) {
if (groupNeeded) {
pdoc->BeginUndoAction();
@ -626,7 +641,7 @@ public:
position(act.position),
length(act.lenData),
linesAdded(linesAdded_),
text(act.data.get()),
text(act.data),
line(0),
foldLevelNow(Scintilla::FoldLevel::None),
foldLevelPrev(Scintilla::FoldLevel::None),

View File

@ -6128,6 +6128,15 @@ sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len)
return val ? len : 0;
}
sptr_t Editor::BytesResult(Scintilla::sptr_t lParam, std::string_view sv) noexcept {
// No NUL termination: sv.length() is number of valid/displayed bytes
if (lParam && !sv.empty()) {
char *ptr = CharPtrFromSPtr(lParam);
memcpy(ptr, sv.data(), sv.length());
}
return sv.length();
}
sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
//Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
@ -6374,8 +6383,8 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
break;
case Message::GetTargetText: {
std::string text = RangeText(targetRange.start.Position(), targetRange.end.Position());
return BytesResult(lParam, reinterpret_cast<const unsigned char *>(text.c_str()), text.length());
const std::string text = RangeText(targetRange.start.Position(), targetRange.end.Position());
return BytesResult(lParam, text);
}
case Message::ReplaceTarget:
@ -6586,6 +6595,56 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
pdoc->EndUndoAction();
return 0;
case Message::GetUndoActions:
return pdoc->UndoActions();
case Message::SetUndoSavePoint:
pdoc->SetUndoSavePoint(static_cast<int>(wParam));
break;
case Message::GetUndoSavePoint:
return pdoc->UndoSavePoint();
case Message::SetUndoDetach:
pdoc->SetUndoDetach(static_cast<int>(wParam));
break;
case Message::GetUndoDetach:
return pdoc->UndoDetach();
case Message::SetUndoTentative:
pdoc->SetUndoTentative(static_cast<int>(wParam));
break;
case Message::GetUndoTentative:
return pdoc->UndoTentative();
case Message::SetUndoCurrent:
pdoc->SetUndoCurrent(static_cast<int>(wParam));
break;
case Message::GetUndoCurrent:
return pdoc->UndoCurrent();
case Message::GetUndoActionType:
return pdoc->UndoActionType(static_cast<int>(wParam));
case Message::GetUndoActionPosition:
return pdoc->UndoActionPosition(static_cast<int>(wParam));
case Message::GetUndoActionText: {
const std::string_view text = pdoc->UndoActionText(static_cast<int>(wParam));
return BytesResult(lParam, text);
}
case Message::PushUndoActionType:
pdoc->PushUndoActionType(static_cast<int>(wParam), lParam);
break;
case Message::ChangeLastUndoActionText:
pdoc->ChangeLastUndoActionText(wParam, CharPtrFromSPtr(lParam));
break;
case Message::GetCaretPeriod:
return caret.period;

View File

@ -668,6 +668,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
static Scintilla::sptr_t StringResult(Scintilla::sptr_t lParam, const char *val) noexcept;
static Scintilla::sptr_t BytesResult(Scintilla::sptr_t lParam, const unsigned char *val, size_t len) noexcept;
static Scintilla::sptr_t BytesResult(Scintilla::sptr_t lParam, std::string_view sv) noexcept;
// Set a variable controlling appearance to a value and invalidates the display
// if a change was made. Avoids extra text and the possibility of mistyping.

View File

@ -502,7 +502,7 @@ const char *RESearch::Compile(const char *pattern, Sci::Position length, bool ca
} else {
i++;
p++;
int incr;
int incr = 0;
c2 = GetBackslashExpression(p, incr);
i += incr;
p += incr;
@ -537,7 +537,7 @@ const char *RESearch::Compile(const char *pattern, Sci::Position length, bool ca
} else if (*p == '\\' && p[1]) {
i++;
p++;
int incr;
int incr = 0;
const int c = GetBackslashExpression(p, incr);
i += incr;
p += incr;
@ -653,8 +653,8 @@ const char *RESearch::Compile(const char *pattern, Sci::Position length, bool ca
return badpat("Unmatched \\)");
}
} else {
int incr;
int c = GetBackslashExpression(p, incr);
int incr = 0;
const int c = GetBackslashExpression(p, incr);
i += incr;
p += incr;
if (c >= 0) {
@ -938,8 +938,8 @@ Sci::Position RESearch::PMatch(const CharacterIndexer &ci, Sci::Position lp, Sci
Sci::Position llp = lp; /* lazy lp for LCLO */
Sci::Position e = NOTFOUND; /* extra pointer for CLO */
while (llp >= are) {
Sci::Position q;
if ((q = PMatch(ci, llp, endp, ap)) != NOTFOUND) {
const Sci::Position q = PMatch(ci, llp, endp, ap);
if (q != NOTFOUND) {
e = q;
lp = llp;
if (op != LCLO) return e;

View File

@ -218,19 +218,16 @@ SelectionRange &Selection::Rectangular() noexcept {
}
SelectionSegment Selection::Limits() const noexcept {
if (ranges.empty()) {
return SelectionSegment();
} else {
PLATFORM_ASSERT(!ranges.empty());
SelectionSegment sr(ranges[0].anchor, ranges[0].caret);
for (size_t i=1; i<ranges.size(); i++) {
sr.Extend(ranges[i].anchor);
sr.Extend(ranges[i].caret);
}
return sr;
}
}
SelectionSegment Selection::LimitsForRectangularElseMain() const {
SelectionSegment Selection::LimitsForRectangularElseMain() const noexcept {
if (IsRectangular()) {
return Limits();
} else {
@ -343,10 +340,12 @@ void Selection::TrimOtherSelections(size_t r, SelectionRange range) noexcept {
}
}
void Selection::SetSelection(SelectionRange range) {
ranges.clear();
ranges.push_back(range);
mainRange = ranges.size() - 1;
void Selection::SetSelection(SelectionRange range) noexcept {
if (ranges.size() > 1) {
ranges.erase(ranges.begin() + 1, ranges.end());
}
ranges[0] = range;
mainRange = 0;
}
void Selection::AddSelection(SelectionRange range) {
@ -360,7 +359,7 @@ void Selection::AddSelectionWithoutTrim(SelectionRange range) {
mainRange = ranges.size() - 1;
}
void Selection::DropSelection(size_t r) {
void Selection::DropSelection(size_t r) noexcept {
if ((ranges.size() > 1) && (r < ranges.size())) {
size_t mainNew = mainRange;
if (mainNew >= r) {
@ -375,7 +374,7 @@ void Selection::DropSelection(size_t r) {
}
}
void Selection::DropAdditionalRanges() {
void Selection::DropAdditionalRanges() noexcept {
SetSelection(RangeMain());
}
@ -425,17 +424,18 @@ Sci::Position Selection::VirtualSpaceFor(Sci::Position pos) const noexcept {
return virtualSpace;
}
void Selection::Clear() {
ranges.clear();
ranges.emplace_back();
mainRange = ranges.size() - 1;
void Selection::Clear() noexcept {
if (ranges.size() > 1) {
ranges.erase(ranges.begin() + 1, ranges.end());
}
mainRange = 0;
selType = SelTypes::stream;
moveExtends = false;
ranges[mainRange].Reset();
rangeRectangular.Reset();
}
void Selection::RemoveDuplicates() {
void Selection::RemoveDuplicates() noexcept {
for (size_t i=0; i<ranges.size()-1; i++) {
if (ranges[i].Empty()) {
size_t j=i+1;

View File

@ -166,7 +166,7 @@ public:
// This is for when you want to move the caret in response to a
// user direction command - for rectangular selections, use the range
// that covers all selected text otherwise return the main selection.
SelectionSegment LimitsForRectangularElseMain() const;
SelectionSegment LimitsForRectangularElseMain() const noexcept;
size_t Count() const noexcept;
size_t Main() const noexcept;
void SetMain(size_t r) noexcept;
@ -183,19 +183,19 @@ public:
void MovePositions(bool insertion, Sci::Position startChange, Sci::Position length) noexcept;
void TrimSelection(SelectionRange range) noexcept;
void TrimOtherSelections(size_t r, SelectionRange range) noexcept;
void SetSelection(SelectionRange range);
void SetSelection(SelectionRange range) noexcept;
void AddSelection(SelectionRange range);
void AddSelectionWithoutTrim(SelectionRange range);
void DropSelection(size_t r);
void DropAdditionalRanges();
void DropSelection(size_t r) noexcept;
void DropAdditionalRanges() noexcept;
void TentativeSelection(SelectionRange range);
void CommitTentative() noexcept;
InSelection RangeType(size_t r) const noexcept;
InSelection CharacterInSelection(Sci::Position posCharacter) const noexcept;
InSelection InSelectionForEOL(Sci::Position pos) const noexcept;
Sci::Position VirtualSpaceFor(Sci::Position pos) const noexcept;
void Clear();
void RemoveDuplicates();
void Clear() noexcept;
void RemoveDuplicates() noexcept;
void RotateMain() noexcept;
bool Tentative() const noexcept { return tentativeMain; }
std::vector<SelectionRange> RangesCopy() const {

View File

@ -0,0 +1,622 @@
// Scintilla source code edit control
/** @file UndoHistory.cxx
** Manages undo for the document.
**/
// Copyright 1998-2024 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cstdlib>
#include <cstdint>
#include <cassert>
#include <cstring>
#include <cstdio>
#include <cstdarg>
#include <climits>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
#include "ScintillaTypes.h"
#include "Debugging.h"
#include "Position.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "SparseVector.h"
#include "ChangeHistory.h"
#include "CellBuffer.h"
#include "UndoHistory.h"
namespace Scintilla::Internal {
template <typename T>
void VectorTruncate(std::vector<T> &v, size_t length) noexcept {
v.erase(v.begin() + length, v.end());
}
constexpr size_t byteMask = UINT8_MAX;
constexpr size_t byteBits = 8;
constexpr size_t maxElementSize = 8;
size_t ReadValue(const uint8_t *bytes, size_t length) noexcept {
size_t value = 0;
for (size_t i = 0; i < length; i++) {
value = (value << byteBits) + bytes[i];
}
return value;
}
void WriteValue(uint8_t *bytes, size_t length, size_t value) noexcept {
for (size_t i = 0; i < length; i++) {
bytes[length - i - 1] = value & byteMask;
value = value >> byteBits;
}
}
size_t ScaledVector::Size() const noexcept {
return bytes.size() / element.size;
}
size_t ScaledVector::ValueAt(size_t index) const noexcept {
return ReadValue(bytes.data() + index * element.size, element.size);
}
intptr_t ScaledVector::SignedValueAt(size_t index) const noexcept {
return ReadValue(bytes.data() + index * element.size, element.size);
}
constexpr SizeMax ElementForValue(size_t value) noexcept {
size_t maxN = byteMask;
for (size_t i = 1; i < maxElementSize; i++) {
if (value <= maxN) {
return { i, maxN };
}
maxN = (maxN << byteBits) + byteMask;
}
return { 1, byteMask };
}
void ScaledVector::SetValueAt(size_t index, size_t value) {
// Check if value fits, if not then expand
if (value > element.maxValue) {
const SizeMax elementForValue = ElementForValue(value);
const size_t length = bytes.size() / element.size;
std::vector<uint8_t> bytesNew(elementForValue.size * length);
for (size_t i = 0; i < length; i++) {
const uint8_t *source = bytes.data() + i * element.size;
uint8_t *destination = bytesNew.data() + (i+1) * elementForValue.size - element.size;
memcpy(destination, source, element.size);
}
std::swap(bytes, bytesNew);
element = elementForValue;
}
WriteValue(bytes.data() + index * element.size, element.size, value);
}
void ScaledVector::ClearValueAt(size_t index) noexcept {
// 0 fits in any size element so no expansion needed so no exceptions
WriteValue(bytes.data() + index * element.size, element.size, 0);
}
void ScaledVector::Clear() noexcept {
bytes.clear();
}
void ScaledVector::Truncate(size_t length) noexcept {
VectorTruncate(bytes, length * element.size);
assert(bytes.size() == length * element.size);
}
void ScaledVector::ReSize(size_t length) {
bytes.resize(length * element.size);
}
void ScaledVector::PushBack() {
ReSize(Size() + 1);
}
size_t ScaledVector::SizeInBytes() const noexcept {
return bytes.size();
}
UndoActionType::UndoActionType() noexcept : at(ActionType::insert), mayCoalesce(false) {
}
UndoActions::UndoActions() noexcept = default;
void UndoActions::Truncate(size_t length) noexcept {
VectorTruncate(types, length);
assert(types.size() == length);
positions.Truncate(length);
lengths.Truncate(length);
}
void UndoActions::PushBack() {
types.emplace_back();
positions.PushBack();
lengths.PushBack();
}
void UndoActions::Clear() noexcept {
types.clear();
positions.Clear();
lengths.Clear();
}
intptr_t UndoActions::SSize() const noexcept {
return types.size();
}
void UndoActions::Create(size_t index, ActionType at_, Sci::Position position_, Sci::Position lenData_, bool mayCoalesce_) {
types[index].at = at_;
types[index].mayCoalesce = mayCoalesce_;
positions.SetValueAt(index, position_);
lengths.SetValueAt(index, lenData_);
}
bool UndoActions::AtStart(size_t index) const noexcept {
if (index == 0) {
return true;
}
return !types[index-1].mayCoalesce;
}
size_t UndoActions::LengthTo(size_t index) const noexcept {
size_t sum = 0;
for (size_t act = 0; act < index; act++) {
sum += lengths.ValueAt(act);
}
return sum;
}
void ScrapStack::Clear() noexcept {
stack.clear();
current = 0;
}
const char *ScrapStack::Push(const char *text, size_t length) {
if (current < stack.length()) {
stack.resize(current);
}
stack.append(text, length);
current = stack.length();
return stack.data() + current - length;
}
void ScrapStack::SetCurrent(size_t position) noexcept {
current = position;
}
void ScrapStack::MoveForward(size_t length) noexcept {
if ((current + length) <= stack.length()) {
current += length;
}
}
void ScrapStack::MoveBack(size_t length) noexcept {
if (current >= length) {
current -= length;
}
}
const char *ScrapStack::CurrentText() const noexcept {
return stack.data() + current;
}
const char *ScrapStack::TextAt(size_t position) const noexcept {
return stack.data() + position;
}
// The undo history stores a sequence of user operations that represent the user's view of the
// commands executed on the text.
// Each user operation contains a sequence of text insertion and text deletion actions.
// All the user operations are stored in a list of individual actions.
// A false 'mayCoalesce' flag acts as an end to a user operation.
// Initially there are no actions in the history.
// As each action is performed, it is recorded in the history. The action may either become
// part of the current user operation or may start a new user operation. If it is to be part of the
// current operation, then 'mayCoalesce' is true. If it is to be part of a new operation, the
// 'mayCoalesce' flag of the previous action is set false.
// The decision of whether to start a new user operation is based upon two factors. If a
// compound operation has been explicitly started by calling BeginUndoAction and no matching
// EndUndoAction (these calls nest) has been called, then the action is coalesced into the current
// operation. If there is no outstanding BeginUndoAction call then a new operation is started
// unless it looks as if the new action is caused by the user typing or deleting a stream of text.
// Sequences that look like typing or deletion are coalesced into a single user operation.
int UndoHistory::PreviousAction() const noexcept {
return currentAction - 1;
}
UndoHistory::UndoHistory() {
scraps = std::make_unique<ScrapStack>();
}
UndoHistory::~UndoHistory() noexcept = default;
const char *UndoHistory::AppendAction(ActionType at, Sci::Position position, const char *data, Sci::Position lengthData,
bool &startSequence, bool mayCoalesce) {
//Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
//Platform::DebugPrintf("^ %d action %d %d\n", actions[currentAction - 1].at,
// actions[currentAction - 1].position, actions[currentAction - 1].lenData);
if (currentAction < savePoint) {
savePoint = -1;
if (!detach) {
detach = currentAction;
}
} else if (detach && (*detach > currentAction)) {
detach = currentAction;
}
if (undoSequenceDepth > 0) {
// Actions not at top level are always coalesced unless this is after return to top level
mayCoalesce = true;
}
bool coalesce = true;
if (currentAction >= 1) {
int targetAct = currentAction - 1;
if (0 == undoSequenceDepth) {
// Top level actions may not always be coalesced
// Container actions may forward the coalesce state of Scintilla Actions.
while ((targetAct > 0) && (actions.types[targetAct].at == ActionType::container) && actions.types[targetAct].mayCoalesce) {
targetAct--;
}
// See if current action can be coalesced into previous action
// Will work if both are inserts or deletes and position is same
if ((currentAction == savePoint) || (currentAction == tentativePoint)) {
coalesce = false;
} else if (!mayCoalesce || !actions.types[targetAct].mayCoalesce) {
coalesce = false;
} else if (at == ActionType::container || actions.types[targetAct].at == ActionType::container) {
; // A coalescible containerAction
} else if ((at != actions.types[targetAct].at)) { // } && (!actions.AtStart(targetAct))) {
coalesce = false;
} else if ((at == ActionType::insert) &&
(position != (actions.positions.SignedValueAt(targetAct) + actions.lengths.SignedValueAt(targetAct)))) {
// Insertions must be immediately after to coalesce
coalesce = false;
} else if (at == ActionType::remove) {
if ((lengthData == 1) || (lengthData == 2)) {
if ((position + lengthData) == actions.positions.SignedValueAt(targetAct)) {
; // Backspace -> OK
} else if (position == actions.positions.SignedValueAt(targetAct)) {
; // Delete -> OK
} else {
// Removals must be at same position to coalesce
coalesce = false;
}
} else {
// Removals must be of one character to coalesce
coalesce = false;
}
} else {
// Action coalesced.
}
} else {
// Actions not at top level are always coalesced unless this is after return to top level
if (!actions.types[targetAct].mayCoalesce)
coalesce = false;
}
} else {
coalesce = false;
}
startSequence = !coalesce;
// Maybe close previous action
if ((currentAction > 0) && startSequence) {
actions.types[PreviousAction()].mayCoalesce = false;
}
const char *dataNew = lengthData ? scraps->Push(data, lengthData) : nullptr;
if (currentAction >= actions.SSize()) {
actions.PushBack();
}
actions.Create(currentAction, at, position, lengthData, mayCoalesce);
currentAction++;
return dataNew;
}
void UndoHistory::BeginUndoAction(bool mayCoalesce) noexcept {
if (undoSequenceDepth == 0) {
if (currentAction > 0) {
actions.types[PreviousAction()].mayCoalesce = mayCoalesce;
}
}
undoSequenceDepth++;
}
void UndoHistory::EndUndoAction() noexcept {
PLATFORM_ASSERT(undoSequenceDepth > 0);
undoSequenceDepth--;
if (0 == undoSequenceDepth) {
if (currentAction > 0) {
actions.types[PreviousAction()].mayCoalesce = false;
}
}
}
void UndoHistory::DropUndoSequence() noexcept {
undoSequenceDepth = 0;
}
void UndoHistory::DeleteUndoHistory() noexcept {
actions.Clear();
currentAction = 0;
savePoint = 0;
tentativePoint = -1;
scraps->Clear();
memory = {};
}
int UndoHistory::Actions() const noexcept {
return static_cast<int>(actions.SSize());
}
void UndoHistory::SetSavePoint(int action) noexcept {
savePoint = action;
}
int UndoHistory::SavePoint() const noexcept {
return savePoint;
}
void UndoHistory::SetSavePoint() noexcept {
savePoint = currentAction;
detach.reset();
}
bool UndoHistory::IsSavePoint() const noexcept {
return savePoint == currentAction;
}
bool UndoHistory::BeforeSavePoint() const noexcept {
return (savePoint < 0) || (savePoint > currentAction);
}
bool UndoHistory::PreviousBeforeSavePoint() const noexcept {
return (savePoint < 0) || (savePoint >= currentAction);
}
bool UndoHistory::BeforeReachableSavePoint() const noexcept {
return (savePoint > 0) && (savePoint > currentAction);
}
bool UndoHistory::AfterSavePoint() const noexcept {
return (savePoint >= 0) && (savePoint <= currentAction);
}
void UndoHistory::SetDetachPoint(int action) noexcept {
if (action == -1) {
detach = {};
} else {
detach = action;
}
}
int UndoHistory::DetachPoint() const noexcept {
return detach.value_or(-1);
}
bool UndoHistory::AfterDetachPoint() const noexcept {
return detach && (*detach < currentAction);
}
bool UndoHistory::AfterOrAtDetachPoint() const noexcept {
return detach && (*detach <= currentAction);
}
intptr_t UndoHistory::Delta(int action) noexcept {
intptr_t sizeChange = 0;
for (int act = 0; act < action; act++) {
const intptr_t lengthChange = actions.lengths.SignedValueAt(act);
sizeChange += (actions.types[act].at == ActionType::insert) ? lengthChange : -lengthChange;
}
return sizeChange;
}
bool UndoHistory::Validate(intptr_t lengthDocument) noexcept {
// Check history for validity
const intptr_t sizeChange = Delta(currentAction);
if (sizeChange > lengthDocument) {
// Current document size too small for changes made in undo history.
return false;
}
const intptr_t lengthOriginal = lengthDocument - sizeChange;
intptr_t lengthCurrent = lengthOriginal;
for (int act = 0; act < actions.SSize(); act++) {
const intptr_t lengthChange = actions.lengths.SignedValueAt(act);
if (actions.positions.SignedValueAt(act) > lengthCurrent) {
// Change outside document.
return false;
}
lengthCurrent += (actions.types[act].at == ActionType::insert) ? lengthChange : -lengthChange;
if (lengthCurrent < 0) {
return false;
}
}
return true;
}
void UndoHistory::SetCurrent(int action, intptr_t lengthDocument) {
// Find position in scraps for action
memory = {};
const size_t lengthSum = actions.LengthTo(action);
scraps->SetCurrent(lengthSum);
currentAction = action;
if (!Validate(lengthDocument)) {
currentAction = 0;
DeleteUndoHistory();
throw std::runtime_error("UndoHistory::SetCurrent: invalid undo history.");
}
}
int UndoHistory::Current() const noexcept {
return currentAction;
}
int UndoHistory::Type(int action) const noexcept {
const int baseType = static_cast<int>(actions.types[action].at);
const int open = actions.types[action].mayCoalesce ? coalesceFlag : 0;
return baseType | open;
}
Sci::Position UndoHistory::Position(int action) const noexcept {
return actions.positions.SignedValueAt(action);
}
Sci::Position UndoHistory::Length(int action) const noexcept {
return actions.lengths.SignedValueAt(action);
}
std::string_view UndoHistory::Text(int action) noexcept {
// Assumes first call after any changes is for action 0.
// TODO: may need to invalidate memory in other circumstances
if (action == 0) {
memory = {};
}
int act = 0;
size_t position = 0;
if (memory && memory->act <= action) {
act = memory->act;
position = memory->position;
}
for (; act < action; act++) {
position += actions.lengths.ValueAt(act);
}
const size_t length = actions.lengths.ValueAt(action);
const char *scrap = scraps->TextAt(position);
memory = {action, position};
return {scrap, length};
}
void UndoHistory::PushUndoActionType(int type, Sci::Position position) {
actions.PushBack();
actions.Create(actions.SSize()-1, static_cast<ActionType>(type & byteMask),
position, 0, type & coalesceFlag);
}
void UndoHistory::ChangeLastUndoActionText(size_t length, const char *text) {
assert(actions.lengths.ValueAt(actions.SSize()-1) == 0);
actions.lengths.SetValueAt(actions.SSize()-1, length);
scraps->Push(text, length);
}
void UndoHistory::SetTentative(int action) noexcept {
tentativePoint = action;
}
int UndoHistory::TentativePoint() const noexcept {
return tentativePoint;
}
void UndoHistory::TentativeStart() noexcept {
tentativePoint = currentAction;
}
void UndoHistory::TentativeCommit() noexcept {
tentativePoint = -1;
// Truncate undo history
actions.Truncate(currentAction);
}
bool UndoHistory::TentativeActive() const noexcept {
return tentativePoint >= 0;
}
int UndoHistory::TentativeSteps() const noexcept {
// Drop any trailing startAction
if (tentativePoint >= 0)
return currentAction - tentativePoint;
return -1;
}
bool UndoHistory::CanUndo() const noexcept {
return (currentAction > 0) && (actions.SSize() != 0);
}
int UndoHistory::StartUndo() const noexcept {
assert(currentAction >= 0);
// Count the steps in this action
if (currentAction == 0) {
return 0;
}
int act = currentAction - 1;
while (act > 0 && !actions.AtStart(act)) {
act--;
}
return currentAction - act;
}
Action UndoHistory::GetUndoStep() const noexcept {
const int previousAction = PreviousAction();
Action acta {
actions.types[previousAction].at,
actions.types[previousAction].mayCoalesce,
actions.positions.SignedValueAt(previousAction),
nullptr,
actions.lengths.SignedValueAt(previousAction)
};
if (acta.lenData) {
acta.data = scraps->CurrentText() - acta.lenData;
}
return acta;
}
void UndoHistory::CompletedUndoStep() noexcept {
scraps->MoveBack(actions.lengths.ValueAt(PreviousAction()));
currentAction--;
}
bool UndoHistory::CanRedo() const noexcept {
return actions.SSize() > currentAction;
}
int UndoHistory::StartRedo() const noexcept {
// Count the steps in this action
if (currentAction >= actions.SSize()) {
// Already at end so can't redo
return 0;
}
// Slightly unusual logic handles case where last action still has mayCoalesce.
// Could set mayCoalesce of last action to false in StartUndo but this state is
// visible to applications so should not be changed.
const int maxAction = Actions() - 1;
int act = currentAction;
while (act <= maxAction && actions.types[act].mayCoalesce) {
act++;
}
act = std::min(act, maxAction);
return act - currentAction + 1;
}
Action UndoHistory::GetRedoStep() const noexcept {
Action acta{
actions.types[currentAction].at,
actions.types[currentAction].mayCoalesce,
actions.positions.SignedValueAt(currentAction),
nullptr,
actions.lengths.SignedValueAt(currentAction)
};
if (acta.lenData) {
acta.data = scraps->CurrentText();
}
return acta;
}
void UndoHistory::CompletedRedoStep() noexcept {
scraps->MoveForward(actions.lengths.ValueAt(currentAction));
currentAction++;
}
}

157
scintilla/src/UndoHistory.h Normal file
View File

@ -0,0 +1,157 @@
// Scintilla source code edit control
/** @file UndoHistory.h
** Manages undo for the document.
**/
// Copyright 1998-2024 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef UNDOHISTORY_H
#define UNDOHISTORY_H
namespace Scintilla::Internal {
// ScaledVector is a vector of unsigned integers that uses elements sized to hold the largest value.
// Thus, if an undo history only contains short insertions and deletions the lengths vector may
// only use 2 bytes or even 1 byte for each length.
// This saves much memory often reducing by 50% for 32-bit builds and 75% for 64-bit builds.
struct SizeMax {
size_t size = 1;
size_t maxValue = UINT8_MAX;
};
class ScaledVector {
SizeMax element;
std::vector<uint8_t> bytes;
public:
[[nodiscard]] size_t Size() const noexcept;
[[nodiscard]] size_t ValueAt(size_t index) const noexcept;
[[nodiscard]] intptr_t SignedValueAt(size_t index) const noexcept;
void SetValueAt(size_t index, size_t value);
void ClearValueAt(size_t index) noexcept;
void Clear() noexcept;
void Truncate(size_t length) noexcept;
void ReSize(size_t length);
void PushBack();
// For testing
[[nodiscard]] size_t SizeInBytes() const noexcept;
};
class UndoActionType {
public:
ActionType at : 4;
bool mayCoalesce : 1;
UndoActionType() noexcept;
};
struct UndoActions {
std::vector<UndoActionType> types;
ScaledVector positions;
ScaledVector lengths;
UndoActions() noexcept;
void Truncate(size_t length) noexcept;
void PushBack();
void Clear() noexcept;
[[nodiscard]] intptr_t SSize() const noexcept;
void Create(size_t index, ActionType at_, Sci::Position position_, Sci::Position lenData_, bool mayCoalesce_);
[[nodiscard]] bool AtStart(size_t index) const noexcept;
[[nodiscard]] size_t LengthTo(size_t index) const noexcept;
};
class ScrapStack {
std::string stack;
size_t current = 0;
public:
void Clear() noexcept;
const char *Push(const char *text, size_t length);
void SetCurrent(size_t position) noexcept;
void MoveForward(size_t length) noexcept;
void MoveBack(size_t length) noexcept;
[[nodiscard]] const char *CurrentText() const noexcept;
[[nodiscard]] const char *TextAt(size_t position) const noexcept;
};
constexpr int coalesceFlag = 0x100;
/**
*
*/
class UndoHistory {
UndoActions actions;
int currentAction = 0;
int undoSequenceDepth = 0;
int savePoint = 0;
int tentativePoint = -1;
std::optional<int> detach; // Never set if savePoint set (>= 0)
std::unique_ptr<ScrapStack> scraps;
struct actPos { int act; size_t position; };
std::optional<actPos> memory;
int PreviousAction() const noexcept;
public:
UndoHistory();
~UndoHistory() noexcept;
const char *AppendAction(ActionType at, Sci::Position position, const char *data, Sci::Position lengthData, bool &startSequence, bool mayCoalesce=true);
void BeginUndoAction(bool mayCoalesce=false) noexcept;
void EndUndoAction() noexcept;
void DropUndoSequence() noexcept;
void DeleteUndoHistory() noexcept;
[[nodiscard]] int Actions() const noexcept;
/// The save point is a marker in the undo stack where the container has stated that
/// the buffer was saved. Undo and redo can move over the save point.
void SetSavePoint(int action) noexcept;
[[nodiscard]] int SavePoint() const noexcept;
void SetSavePoint() noexcept;
bool IsSavePoint() const noexcept;
bool BeforeSavePoint() const noexcept;
bool PreviousBeforeSavePoint() const noexcept;
bool BeforeReachableSavePoint() const noexcept;
bool AfterSavePoint() const noexcept;
/// The detach point is the last action that was before an inaccessible missing save point.
void SetDetachPoint(int action) noexcept;
[[nodiscard]] int DetachPoint() const noexcept;
bool AfterDetachPoint() const noexcept;
bool AfterOrAtDetachPoint() const noexcept;
[[nodiscard]] intptr_t Delta(int action) noexcept;
[[nodiscard]] bool Validate(intptr_t lengthDocument) noexcept;
void SetCurrent(int action, intptr_t lengthDocument);
[[nodiscard]] int Current() const noexcept;
[[nodiscard]] int Type(int action) const noexcept;
[[nodiscard]] Sci::Position Position(int action) const noexcept;
[[nodiscard]] Sci::Position Length(int action) const noexcept;
[[nodiscard]] std::string_view Text(int action) noexcept;
void PushUndoActionType(int type, Sci::Position position);
void ChangeLastUndoActionText(size_t length, const char *text);
// Tentative actions are used for input composition so that it can be undone cleanly
void SetTentative(int action) noexcept;
[[nodiscard]] int TentativePoint() const noexcept;
void TentativeStart() noexcept;
void TentativeCommit() noexcept;
bool TentativeActive() const noexcept;
int TentativeSteps() const noexcept;
/// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is
/// called that many times. Similarly for redo.
bool CanUndo() const noexcept;
int StartUndo() const noexcept;
Action GetUndoStep() const noexcept;
void CompletedUndoStep() noexcept;
bool CanRedo() const noexcept;
int StartRedo() const noexcept;
Action GetRedoStep() const noexcept;
void CompletedRedoStep() noexcept;
};
}
#endif

Some files were not shown because too many files have changed in this diff Show More