+upd: Scintilla v5.3.4

This commit is contained in:
METANEOCORTEX\Kotti 2023-03-15 02:10:33 +01:00
parent 9c9a2b3df2
commit 8b442ce38a
19 changed files with 219 additions and 63 deletions

View File

@ -2,4 +2,4 @@ This is only a repository to track Scintilla (https://sourceforge.net/p/scintill
resp. (http://hg.code.sf.net/p/scintilla/code)
resp. GitHub Mirror: (https://github.com/missdeer/scintilla)
changes for Notepad3 (https://github.com/rizonesoft/Notepad3) development.

View File

@ -7982,7 +7982,7 @@ sptr_t CallScintilla(unsigned int iMessage, uptr_t wParam, sptr_t lParam){
<p><b id="SCI_SETLAYOUTTHREADS">SCI_SETLAYOUTTHREADS(int threads)</b><br />
<b id="SCI_GETLAYOUTTHREADS">SCI_GETLAYOUTTHREADS &rarr; int</b><br />
The time taken to measure text runs on wide lines can be improved by performing the task
The time taken to measure text runs on wide lines or when wrapping can be improved by performing the task
concurrently on multiple threads when
<a class="seealso" href="#SCI_SUPPORTSFEATURE">SCI_SUPPORTSFEATURE(SC_SUPPORTS_THREAD_SAFE_MEASURE_WIDTHS)</a>
is available.

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/scintilla533.zip">
<font size="4"> <a href="https://www.scintilla.org/scintilla534.zip">
Windows</a>&nbsp;&nbsp;
<a href="https://www.scintilla.org/scintilla533.tgz">
<a href="https://www.scintilla.org/scintilla534.tgz">
GTK/Linux</a>&nbsp;&nbsp;
</font>
</td>
@ -42,7 +42,7 @@
containing very few restrictions.
</p>
<h3>
Release 5.3.3
Release 5.3.4
</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/scintilla533.zip">zip format</a> (1.4M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/scintilla533.tgz">tgz format</a> (1.3M) commonly used on Linux and compatible operating systems</li>
<li><a href="https://www.scintilla.org/scintilla534.zip">zip format</a> (1.4M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/scintilla534.tgz">tgz format</a> (1.3M) 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,7 +584,10 @@
</h3>
<ul>
<li>
Released 8 February 2023.
Released 8 March 2023.
</li>
<li>
Add multithreaded wrap to significantly improve performance of wrapping large files.
</li>
<li>
More typesafe bindings of *Full APIs in ScintillaCall.

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="20230208" />
<meta name="Date.Modified" content="20230308" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
#versionlist {
@ -56,8 +56,8 @@
GTK, and macOS</font>
</td>
<td width="40%" align="right">
<font color="#FFCC99" size="3"> Release version 5.3.3<br />
Site last modified February 8 2023</font>
<font color="#FFCC99" size="3"> Release version 5.3.4<br />
Site last modified March 8 2023</font>
</td>
<td width="20%">
&nbsp;
@ -72,12 +72,12 @@
</tr>
</table>
<ul id="versionlist">
<li>Version 5.3.4 adds multithreaded wrapping.</li>
<li>Version 5.3.3 fixes minor bugs in APIs and platform layers.</li>
<li>Version 5.3.2 adds SCI_REPLACETARGETMINIMAL to modify text without marking unchanged start and end text in change history.</li>
<li>Version 5.3.1 can represent invisible text with a character to simplify editing and provide summarized views.</li>
<li>Version 5.3.0 adds change history.</li>
<li>Version 5.2.4 fixes failures on GTK with multi-threaded layout.</li>
<li>Version 5.2.3 adds 64-bit safe APIs and fixes scrollbar on GTK with Xorg.</li>
</ul>
<ul id="menu">
<li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></li>

View File

@ -161,7 +161,7 @@ def checkDocumentation():
#~ if api not in headers:
#~ print("No header for ", api)
# Examine definitions
# Examine definitions
#<b id="SCI_SETLAYOUTCACHE">SCI_SETLAYOUTCACHE(int cacheMode)</b>
defPattern = re.compile(r'<b id="([A-Z_0-9]+)">([A-Z][A-Za-z0-9_() *#\"=<>/&;,\n-]+?)</b>')
for api, sig in re.findall(defPattern, docs):
@ -201,7 +201,7 @@ def checkDocumentation():
with open(outName, "wt") as docFile:
docFile.write(docs)
# Examine constant definitions
# Examine constant definitions
#<code>SC_CARETSTICKY_WHITESPACE</code> (2)
constPattern = re.compile(r'<code>(\w+)</code> *\((\w+)\)')
for name, val in re.findall(constPattern, docs):

View File

@ -34,7 +34,7 @@ def UpdateVersionNumbers(sci, root):
UpdateLineInFile(root / "doc/ScintillaDownload.html", " Release",
" Release " + sci.versionDotted)
ReplaceREInFile(root / "doc/ScintillaDownload.html",
r"/www.scintilla.org/([a-zA-Z]+)\d\d\d",
r"/www.scintilla.org/([a-zA-Z]+)\d{3,5}",
r"/www.scintilla.org/\g<1>" + sci.version,
0)
UpdateLineInFile(root / "doc/index.html",

View File

@ -49,8 +49,8 @@ class ScintillaData:
def __init__(self, scintillaRoot):
# Discover version information
self.version = (scintillaRoot / "version.txt").read_text().strip()
self.versionDotted = self.version[0] + '.' + self.version[1] + '.' + \
self.version[2]
self.versionDotted = self.version[0:-2] + '.' + self.version[-2] + '.' + \
self.version[-1]
self.versionCommad = self.versionDotted.replace(".", ", ") + ', 0'
with (scintillaRoot / "doc" / "index.html").open() as f:

View File

@ -605,7 +605,6 @@ class CaseConverter : public ICaseConverter {
return character < other.character;
}
};
CaseConversion conversion;
typedef std::vector<CharacterConversion> CharacterToConversion;
CharacterToConversion characterToConversion;
// The parallel arrays
@ -613,8 +612,7 @@ class CaseConverter : public ICaseConverter {
std::vector<ConversionString> conversions;
public:
explicit CaseConverter(CaseConversion conversion_) : conversion(conversion_) {
};
CaseConverter() noexcept = default;
// Deleted so CaseConverter objects can not be copied.
CaseConverter(const CaseConverter &) = delete;
CaseConverter(CaseConverter &&) = delete;
@ -690,15 +688,13 @@ public:
// Empty the original calculated data completely
CharacterToConversion().swap(characterToConversion);
}
void AddSymmetric(int lower, int upper);
void SetupConversions();
void AddSymmetric(CaseConversion conversion, int lower, int upper);
void SetupConversions(CaseConversion conversion);
};
CaseConverter caseConvFold(CaseConversion::fold);
CaseConverter caseConvUp(CaseConversion::upper);
CaseConverter caseConvLow(CaseConversion::lower);
CaseConverter caseConvList[3];
void CaseConverter::AddSymmetric(int lower, int upper) {
void CaseConverter::AddSymmetric(CaseConversion conversion, int lower, int upper) {
const int character = (conversion == CaseConversion::upper) ? lower : upper;
const int source = (conversion == CaseConversion::upper) ? upper : lower;
char converted[maxConversionLength+1]{};
@ -720,7 +716,7 @@ std::string_view NextField(std::string_view &view) {
return field;
}
void CaseConverter::SetupConversions() {
void CaseConverter::SetupConversions(CaseConversion conversion) {
// First initialize for the symmetric ranges
for (size_t i=0; i<std::size(symmetricCaseConversionRanges);) {
const int lower = symmetricCaseConversionRanges[i++];
@ -728,14 +724,14 @@ void CaseConverter::SetupConversions() {
const int length = symmetricCaseConversionRanges[i++];
const int pitch = symmetricCaseConversionRanges[i++];
for (int j=0; j<length*pitch; j+=pitch) {
AddSymmetric(lower+j, upper+j);
AddSymmetric(conversion, lower+j, upper+j);
}
}
// Add the symmetric singletons
for (size_t i=0; i<std::size(symmetricCaseConversions);) {
const int lower = symmetricCaseConversions[i++];
const int upper = symmetricCaseConversions[i++];
AddSymmetric(lower, upper);
AddSymmetric(conversion, lower, upper);
}
// Add the complex cases
std::string_view sComplex = complexCaseConversions;
@ -768,21 +764,11 @@ void CaseConverter::SetupConversions() {
}
CaseConverter *ConverterForConversion(CaseConversion conversion) {
CaseConverter *pCaseConv = &caseConvFold;
switch (conversion) {
case CaseConversion::fold:
pCaseConv = &caseConvFold;
break;
case CaseConversion::upper:
pCaseConv = &caseConvUp;
break;
case CaseConversion::lower:
default:
pCaseConv = &caseConvLow;
break;
}
const unsigned index = static_cast<unsigned>(conversion);
assert(index < std::size(caseConvList));
CaseConverter *pCaseConv = &caseConvList[index];
if (!pCaseConv->Initialised()) {
pCaseConv->SetupConversions();
pCaseConv->SetupConversions(conversion);
}
return pCaseConv;
}

View File

@ -448,6 +448,10 @@ Sci_Position SCI_METHOD Document::LineStart(Sci_Position line) const noexcept {
return cb.LineStart(line);
}
Range Document::LineRange(Sci::Line line) const noexcept {
return {cb.LineStart(line), cb.LineStart(line + 1)};
}
bool Document::IsLineStartPosition(Sci::Position position) const {
return LineStart(LineFromPosition(position)) == position;
}

View File

@ -54,6 +54,10 @@ public:
return start == end;
}
[[nodiscard]] Sci::Position Length() const noexcept {
return (start <= end) ? (end - start) : (start - end);
}
Sci::Position First() const noexcept {
return (start <= end) ? start : end;
}
@ -454,6 +458,7 @@ public:
int MarkerNumberFromLine(Sci::Line line, int which) const noexcept;
int MarkerHandleFromLine(Sci::Line line, int which) const noexcept;
Sci_Position SCI_METHOD LineStart(Sci_Position line) const noexcept override;
[[nodiscard]] Range LineRange(Sci::Line line) const noexcept;
bool IsLineStartPosition(Sci::Position position) const;
Sci_Position SCI_METHOD LineEnd(Sci_Position line) const noexcept override;
Sci::Position LineEndPosition(Sci::Position position) const;

View File

@ -397,7 +397,7 @@ void LayoutSegments(IPositionCache *pCache,
* Copy the given @a line and its styles from the document into local arrays.
* Also determine the x position at which each character starts.
*/
void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) {
void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width, bool callerMultiThreaded) {
if (!ll)
return;
@ -494,7 +494,7 @@ void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewSt
const size_t threadsForLength = std::max(1, numCharsInLine / bytesPerLayoutThread);
size_t threads = std::min<size_t>({ segments.size(), threadsForLength, maxLayoutThreads });
if (!surface->SupportsFeature(Supports::ThreadSafeMeasureWidths)) {
if (!surface->SupportsFeature(Supports::ThreadSafeMeasureWidths) || callerMultiThreaded) {
threads = 1;
}
@ -502,6 +502,7 @@ void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewSt
const bool textUnicode = CpUtf8 == model.pdoc->dbcsCodePage;
const bool multiThreaded = threads > 1;
const bool multiThreadedContext = multiThreaded || callerMultiThreaded;
IPositionCache *pCache = posCache.get();
// If only 1 thread needed then use the main thread, else spin up multiple
@ -511,8 +512,8 @@ void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewSt
for (size_t th = 0; th < threads; th++) {
// Find relative positions of everything except for tabs
std::future<void> fut = std::async(policy,
[pCache, surface, &vstyle, &ll, &segments, &nextIndex, textUnicode, multiThreaded]() {
LayoutSegments(pCache, surface, vstyle, ll, segments, nextIndex, textUnicode, multiThreaded);
[pCache, surface, &vstyle, &ll, &segments, &nextIndex, textUnicode, multiThreadedContext]() {
LayoutSegments(pCache, surface, vstyle, ll, segments, nextIndex, textUnicode, multiThreadedContext);
});
futures.push_back(std::move(fut));
}

View File

@ -119,7 +119,7 @@ public:
std::shared_ptr<LineLayout> RetrieveLineLayout(Sci::Line lineNumber, const EditModel &model);
void LayoutLine(const EditModel &model, Surface *surface, const ViewStyle &vstyle,
LineLayout *ll, int width);
LineLayout *ll, int width, bool callerMultiThreaded=false);
static void UpdateBidiData(const EditModel &model, const ViewStyle &vstyle, LineLayout *ll);

View File

@ -25,6 +25,9 @@
#include <iterator>
#include <memory>
#include <chrono>
#include <atomic>
#include <thread>
#include <future>
#include "ScintillaTypes.h"
#include "ScintillaMessages.h"
@ -274,6 +277,11 @@ Sci::Line Editor::TopLineOfMain() const noexcept {
return topLine;
}
Point Editor::ClientSize() const {
const PRectangle rcClient = GetClientRectangle();
return Point(rcClient.Width(), rcClient.Height());
}
PRectangle Editor::GetClientRectangle() const {
return wMain.GetClientPosition();
}
@ -290,8 +298,8 @@ PRectangle Editor::GetTextRectangle() const {
}
Sci::Line Editor::LinesOnScreen() const {
const PRectangle rcClient = GetClientRectangle();
const int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
const Point sizeClient = ClientSize();
const int htClient = static_cast<int>(sizeClient.y);
//Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
return htClient / vs.lineHeight;
}
@ -1468,6 +1476,130 @@ bool Editor::WrapOneLine(Surface *surface, Sci::Line lineToWrap) {
return pcs->SetHeight(lineToWrap, linesWrapped);
}
namespace {
// Lines less than lengthToMultiThread are laid out in blocks in parallel.
// Longer lines are multi-threaded inside LayoutLine.
// This allows faster processing when lines differ greatly in length and thus time to lay out.
constexpr Sci::Position lengthToMultiThread = 4000;
}
bool Editor::WrapBlock(Surface *surface, Sci::Line lineToWrap, Sci::Line lineToWrapEnd) {
const size_t linesBeingWrapped = static_cast<size_t>(lineToWrapEnd - lineToWrap);
std::vector<int> linesAfterWrap(linesBeingWrapped);
size_t threads = std::min<size_t>({ linesBeingWrapped, view.maxLayoutThreads });
if (!surface->SupportsFeature(Supports::ThreadSafeMeasureWidths)) {
threads = 1;
}
const bool multiThreaded = threads > 1;
ElapsedPeriod epWrapping;
// Wrap all the short lines in multiple threads
// If only 1 thread needed then use the main thread, else spin up multiple
const std::launch policy = multiThreaded ? std::launch::async : std::launch::deferred;
std::atomic<size_t> nextIndex = 0;
// Lines that are less likely to be re-examined should not be read from or written to the cache.
const SignificantLines significantLines {
pdoc->SciLineFromPosition(sel.MainCaret()),
pcs->DocFromDisplay(topLine),
LinesOnScreen() + 1,
view.llc.GetLevel(),
};
// Protect the line layout cache from being accessed from multiple threads simultaneously
std::mutex mutexRetrieve;
std::vector<std::future<void>> futures;
for (size_t th = 0; th < threads; th++) {
std::future<void> fut = std::async(policy,
[=, &surface, &nextIndex, &linesAfterWrap, &mutexRetrieve]() {
// llTemporary is reused for non-significant lines, avoiding allocation costs.
std::shared_ptr<LineLayout> llTemporary = std::make_shared<LineLayout>(-1, 200);
while (true) {
const size_t i = nextIndex.fetch_add(1, std::memory_order_acq_rel);
if (i >= linesBeingWrapped) {
break;
}
const Sci::Line lineNumber = lineToWrap + i;
const Range rangeLine = pdoc->LineRange(lineNumber);
const Sci::Position lengthLine = rangeLine.Length();
if (lengthLine < lengthToMultiThread) {
std::shared_ptr<LineLayout> ll;
if (significantLines.LineMayCache(lineNumber)) {
std::lock_guard<std::mutex> guard(mutexRetrieve);
ll = view.RetrieveLineLayout(lineNumber, *this);
} else {
ll = llTemporary;
ll->ReSet(lineNumber, lengthLine);
}
view.LayoutLine(*this, surface, vs, ll.get(), wrapWidth, multiThreaded);
linesAfterWrap[i] = ll->lines;
}
}
});
futures.push_back(std::move(fut));
}
for (const std::future<void> &f : futures) {
f.wait();
}
// End of multiple threads
// Multiply duration by number of threads to produce (near) equivalence to duration if single threaded
const double durationShortLines = epWrapping.Duration(true);
const double durationShortLinesThreads = durationShortLines * threads;
// Wrap all the long lines in the main thread.
// LayoutLine may then multi-thread over segments in each line.
std::shared_ptr<LineLayout> llLarge = std::make_shared<LineLayout>(-1, 200);
for (size_t indexLarge = 0; indexLarge < linesBeingWrapped; indexLarge++) {
const Sci::Line lineNumber = lineToWrap + indexLarge;
const Range rangeLine = pdoc->LineRange(lineNumber);
const Sci::Position lengthLine = rangeLine.Length();
if (lengthLine >= lengthToMultiThread) {
std::shared_ptr<LineLayout> ll;
if (significantLines.LineMayCache(lineNumber)) {
ll = view.RetrieveLineLayout(lineNumber, *this);
} else {
ll = llLarge;
ll->ReSet(lineNumber, lengthLine);
}
view.LayoutLine(*this, surface, vs, ll.get(), wrapWidth);
linesAfterWrap[indexLarge] = ll->lines;
}
}
const double durationLongLines = epWrapping.Duration();
const size_t bytesBeingWrapped = pdoc->LineStart(lineToWrap + linesBeingWrapped) - pdoc->LineStart(lineToWrap);
size_t wrapsDone = 0;
for (size_t i = 0; i < linesBeingWrapped; i++) {
const Sci::Line lineNumber = lineToWrap + i;
int linesWrapped = linesAfterWrap[i];
if (vs.annotationVisible != AnnotationVisible::Hidden) {
linesWrapped += pdoc->AnnotationLines(lineNumber);
}
if (pcs->SetHeight(lineNumber, linesWrapped)) {
wrapsDone++;
}
wrapPending.Wrapped(lineNumber);
}
durationWrapOneByte.AddSample(bytesBeingWrapped, durationShortLinesThreads + durationLongLines);
return wrapsDone > 0;
}
// Perform wrapping for a subset of the lines needing wrapping.
// wsAll: wrap all lines which need wrapping in this single call
// wsVisible: wrap currently visible lines
@ -1549,16 +1681,7 @@ bool Editor::WrapLines(WrapScope ws) {
if (surface) {
//Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
const size_t bytesBeingWrapped = pdoc->LineStart(lineToWrapEnd) - pdoc->LineStart(lineToWrap);
ElapsedPeriod epWrapping;
while (lineToWrap < lineToWrapEnd) {
if (WrapOneLine(surface, lineToWrap)) {
wrapOccurred = true;
}
wrapPending.Wrapped(lineToWrap);
lineToWrap++;
}
durationWrapOneByte.AddSample(bytesBeingWrapped, epWrapping.Duration());
wrapOccurred = WrapBlock(surface, lineToWrap, lineToWrapEnd);
goodTopLine = pcs->DisplayFromDoc(lineDocTop) + std::min(
subLineTop, static_cast<Sci::Line>(pcs->GetHeight(lineDocTop)-1));

View File

@ -312,6 +312,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
Point GetVisibleOriginInMain() const override;
PointDocument DocumentPointFromView(Point ptView) const; // Convert a point from view space to document
Sci::Line TopLineOfMain() const noexcept final; // Return the line at Main's y coordinate 0
virtual Point ClientSize() const;
virtual PRectangle GetClientRectangle() const;
virtual PRectangle GetClientDrawingRectangle();
PRectangle GetTextRectangle() const;
@ -403,6 +404,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
bool Wrapping() const noexcept;
void NeedWrapping(Sci::Line docLineStart=0, Sci::Line docLineEnd=WrapPending::lineLarge);
bool WrapOneLine(Surface *surface, Sci::Line lineToWrap);
bool WrapBlock(Surface *surface, Sci::Line lineToWrap, Sci::Line lineToWrapEnd);
enum class WrapScope {wsAll, wsVisible, wsIdle};
bool WrapLines(WrapScope ws);
void LinesJoin();

View File

@ -102,6 +102,13 @@ void LineLayout::Resize(int maxLineLength_) {
}
}
void LineLayout::ReSet(Sci::Line lineNumber_, Sci::Position maxLineLength_) {
lineNumber = lineNumber_;
Resize(static_cast<int>(maxLineLength_));
lines = 0;
Invalidate(ValidLevel::invalid);
}
void LineLayout::EnsureBidiData() {
if (!bidiData) {
bidiData = std::make_unique<BidiData>();
@ -114,6 +121,7 @@ void LineLayout::Free() noexcept {
styles.reset();
positions.reset();
lineStarts.reset();
lenLineStarts = 0;
bidiData.reset();
}
@ -446,6 +454,21 @@ XYPOSITION ScreenLine::TabPositionAfter(XYPOSITION xPosition) const noexcept {
return (std::floor((xPosition + TabWidthMinimumPixels()) / TabWidth()) + 1) * TabWidth();
}
bool SignificantLines::LineMayCache(Sci::Line line) const noexcept {
switch (level) {
case LineCache::None:
return false;
case LineCache::Caret:
return line == lineCaret;
case LineCache::Page:
return (abs(line - lineCaret) < linesOnScreen) ||
((line >= lineTop) && (line <= (lineTop + linesOnScreen)));
case LineCache::Document:
default:
return true;
}
}
LineLayoutCache::LineLayoutCache() :
level(LineCache::None),
allInvalidated(false), styleClock(-1) {

View File

@ -83,6 +83,7 @@ public:
void operator=(LineLayout &&) = delete;
virtual ~LineLayout();
void Resize(int maxLineLength_);
void ReSet(Sci::Line lineNumber_, Sci::Position maxLineLength_);
void EnsureBidiData();
void Free() noexcept;
void ClearPositions();
@ -140,6 +141,14 @@ struct ScreenLine : public IScreenLine {
XYPOSITION TabPositionAfter(XYPOSITION xPosition) const noexcept override;
};
struct SignificantLines {
Sci::Line lineCaret;
Sci::Line lineTop;
Sci::Line linesOnScreen;
Scintilla::LineCache level;
bool LineMayCache(Sci::Line line) const noexcept;
};
/**
*/
class LineLayoutCache {

View File

@ -1 +1 @@
533
534

View File

@ -6,8 +6,8 @@
#include <windows.h>
#define VERSION_SCINTILLA "5.3.3"
#define VERSION_WORDS 5, 3, 3, 0
#define VERSION_SCINTILLA "5.3.4"
#define VERSION_WORDS 5, 3, 4, 0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_WORDS