diff options
-rw-r--r-- | cocoa/ScintillaCocoa.h | 2 | ||||
-rw-r--r-- | cocoa/ScintillaCocoa.mm | 44 | ||||
-rw-r--r-- | gtk/ScintillaGTK.cxx | 42 | ||||
-rw-r--r-- | gtk/makefile | 2 | ||||
-rw-r--r-- | qt/ScintillaEdit/ScintillaDocument.cpp | 1 | ||||
-rw-r--r-- | qt/ScintillaEdit/ScintillaEdit.pro | 2 | ||||
-rw-r--r-- | qt/ScintillaEditBase/ScintillaEditBase.pro | 5 | ||||
-rw-r--r-- | qt/ScintillaEditBase/ScintillaQt.cpp | 45 | ||||
-rw-r--r-- | qt/ScintillaEditBase/ScintillaQt.h | 2 | ||||
-rw-r--r-- | scripts/GenerateCaseConvert.py | 122 | ||||
-rw-r--r-- | src/CaseConvert.cxx | 620 | ||||
-rw-r--r-- | src/CaseConvert.h | 47 | ||||
-rw-r--r-- | src/CaseFolder.cxx | 68 | ||||
-rw-r--r-- | src/CaseFolder.h | 45 | ||||
-rw-r--r-- | src/Document.cxx | 42 | ||||
-rw-r--r-- | src/Document.h | 18 | ||||
-rw-r--r-- | src/Editor.cxx | 1 | ||||
-rw-r--r-- | src/PositionCache.cxx | 1 | ||||
-rw-r--r-- | src/ScintillaBase.cxx | 1 | ||||
-rw-r--r-- | src/UnicodeFromUTF8.h | 19 | ||||
-rw-r--r-- | test/simpleTests.py | 70 | ||||
-rw-r--r-- | win32/ScintillaWin.cxx | 193 | ||||
-rw-r--r-- | win32/makefile | 10 | ||||
-rw-r--r-- | win32/scintilla.mak | 25 |
24 files changed, 1093 insertions, 334 deletions
diff --git a/cocoa/ScintillaCocoa.h b/cocoa/ScintillaCocoa.h index b609f2343..9b2c9fa1e 100644 --- a/cocoa/ScintillaCocoa.h +++ b/cocoa/ScintillaCocoa.h @@ -44,12 +44,14 @@ #include "ViewStyle.h" #include "CharClassify.h" #include "Decoration.h" +#include "CaseFolder.h" #include "Document.h" #include "Selection.h" #include "PositionCache.h" #include "Editor.h" #include "ScintillaBase.h" +#include "CaseConvert.h" extern "C" NSString* ScintillaRecPboardType; diff --git a/cocoa/ScintillaCocoa.mm b/cocoa/ScintillaCocoa.mm index 3e5c46f00..3845bd5d0 100644 --- a/cocoa/ScintillaCocoa.mm +++ b/cocoa/ScintillaCocoa.mm @@ -522,37 +522,6 @@ static char *EncodedBytes(CFStringRef cfsRef, CFStringEncoding encoding) { * Case folders. */ -class CaseFolderUTF8 : public CaseFolderTable { -public: - CaseFolderUTF8() { - StandardASCII(); - } - virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) { - if ((lenMixed == 1) && (sizeFolded > 0)) { - folded[0] = mapping[static_cast<unsigned char>(mixed[0])]; - return 1; - } else { - CFStringRef cfsVal = CFStringCreateWithBytes(kCFAllocatorDefault, - reinterpret_cast<const UInt8 *>(mixed), - lenMixed, kCFStringEncodingUTF8, false); - - NSString *sMapped = [(NSString *)cfsVal stringByFoldingWithOptions:NSCaseInsensitiveSearch - locale:[NSLocale currentLocale]]; - - const char *cpMapped = [sMapped UTF8String]; - size_t lenMapped = cpMapped ? strlen(cpMapped) : 0; - if (lenMapped < sizeFolded) { - memcpy(folded, cpMapped, lenMapped); - } else { - lenMapped = 0; - } - if (cfsVal) - CFRelease(cfsVal); - return lenMapped; - } - } -}; - class CaseFolderDBCS : public CaseFolderTable { CFStringEncoding encoding; public: @@ -592,7 +561,7 @@ public: CaseFolder *ScintillaCocoa::CaseFolderForEncoding() { if (pdoc->dbcsCodePage == SC_CP_UTF8) { - return new CaseFolderUTF8(); + return new CaseFolderUnicode(); } else { CFStringEncoding encoding = EncodingFromCharacterSet(IsUnicodeMode(), vs.styles[STYLE_DEFAULT].characterSet); @@ -637,6 +606,17 @@ CaseFolder *ScintillaCocoa::CaseFolderForEncoding() { */ std::string ScintillaCocoa::CaseMapString(const std::string &s, int caseMapping) { + if ((s.size() == 0) || (caseMapping == cmSame)) + return s; + + if (IsUnicodeMode()) { + std::string retMapped(s.length() * maxExpansionCaseConversion, 0); + size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(), + (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower); + retMapped.resize(lenMapped); + return retMapped; + } + CFStringEncoding encoding = EncodingFromCharacterSet(IsUnicodeMode(), vs.styles[STYLE_DEFAULT].characterSet); CFStringRef cfsVal = CFStringCreateWithBytes(kCFAllocatorDefault, diff --git a/gtk/ScintillaGTK.cxx b/gtk/ScintillaGTK.cxx index 5f596e84c..82ca0b34e 100644 --- a/gtk/ScintillaGTK.cxx +++ b/gtk/ScintillaGTK.cxx @@ -46,12 +46,14 @@ #include "ViewStyle.h" #include "Decoration.h" #include "CharClassify.h" +#include "CaseFolder.h" #include "Document.h" #include "Selection.h" #include "PositionCache.h" #include "Editor.h" #include "ScintillaBase.h" #include "UniConversion.h" +#include "CaseConvert.h" #include "scintilla-marshal.h" @@ -1238,29 +1240,6 @@ const char *ScintillaGTK::CharacterSetID() const { return ::CharacterSetID(vs.styles[STYLE_DEFAULT].characterSet); } -class CaseFolderUTF8 : public CaseFolderTable { -public: - CaseFolderUTF8() { - StandardASCII(); - } - virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) { - if ((lenMixed == 1) && (sizeFolded > 0)) { - folded[0] = mapping[static_cast<unsigned char>(mixed[0])]; - return 1; - } else { - gchar *mapped = g_utf8_casefold(mixed, lenMixed); - size_t lenMapped = strlen(mapped); - if (lenMapped < sizeFolded) { - memcpy(folded, mapped, lenMapped); - } else { - lenMapped = 0; - } - g_free(mapped); - return lenMapped; - } - } -}; - class CaseFolderDBCS : public CaseFolderTable { const char *charSet; public: @@ -1295,7 +1274,7 @@ public: CaseFolder *ScintillaGTK::CaseFolderForEncoding() { if (pdoc->dbcsCodePage == SC_CP_UTF8) { - return new CaseFolderUTF8(); + return new CaseFolderUnicode(); } else { const char *charSetBuffer = CharacterSetID(); if (charSetBuffer) { @@ -1349,15 +1328,20 @@ struct CaseMapper { } std::string ScintillaGTK::CaseMapString(const std::string &s, int caseMapping) { - if (s.size() == 0) - return std::string(); - - if (caseMapping == cmSame) + if ((s.size() == 0) || (caseMapping == cmSame)) return s; + if (IsUnicodeMode()) { + std::string retMapped(s.length() * maxExpansionCaseConversion, 0); + size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(), + (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower); + retMapped.resize(lenMapped); + return retMapped; + } + const char *charSetBuffer = CharacterSetID(); - if (IsUnicodeMode() || !*charSetBuffer) { + if (!*charSetBuffer) { CaseMapper mapper(s, caseMapping == cmUpper); return std::string(mapper.mapped, strlen(mapper.mapped)); } else { diff --git a/gtk/makefile b/gtk/makefile index 4a2ffd3c2..5bcea9af8 100644 --- a/gtk/makefile +++ b/gtk/makefile @@ -88,7 +88,7 @@ deps: $(CC) -MM $(CONFIGFLAGS) $(CXXTFLAGS) *.cxx ../src/*.cxx | sed -e 's/\/usr.* //' | grep [a-zA-Z] >deps.mak $(COMPLIB): Accessor.o CharacterSet.o LexerBase.o LexerModule.o LexerSimple.o StyleContext.o WordList.o \ - CharClassify.o Decoration.o Document.o PerLine.o Catalogue.o CallTip.o \ + CharClassify.o Decoration.o Document.o PerLine.o Catalogue.o CallTip.o CaseConvert.o CaseFolder.o \ ScintillaBase.o ContractionState.o Editor.o ExternalLexer.o PropSetSimple.o PlatGTK.o \ KeyMap.o LineMarker.o PositionCache.o ScintillaGTK.o CellBuffer.o CharacterCategory.o ViewStyle.o \ RESearch.o RunStyles.o Selection.o Style.o Indicator.o AutoComplete.o UniConversion.o XPM.o \ diff --git a/qt/ScintillaEdit/ScintillaDocument.cpp b/qt/ScintillaEdit/ScintillaDocument.cpp index 801f6a385..6240a7699 100644 --- a/qt/ScintillaEdit/ScintillaDocument.cpp +++ b/qt/ScintillaEdit/ScintillaDocument.cpp @@ -25,6 +25,7 @@ #include "ViewStyle.h" #include "CharClassify.h" #include "Decoration.h" +#include "CaseFolder.h" #include "Document.h" class WatcherHelper : public DocWatcher { diff --git a/qt/ScintillaEdit/ScintillaEdit.pro b/qt/ScintillaEdit/ScintillaEdit.pro index 8c8dda39e..65aa9b2ae 100644 --- a/qt/ScintillaEdit/ScintillaEdit.pro +++ b/qt/ScintillaEdit/ScintillaEdit.pro @@ -40,6 +40,8 @@ SOURCES += \ ../../src/CharClassify.cxx \ ../../src/CellBuffer.cxx \ ../../src/Catalogue.cxx \ + ../../src/CaseFolder.cxx \ + ../../src/CaseConvert.cxx \ ../../src/CallTip.cxx \ ../../src/AutoComplete.cxx \ ../../lexlib/WordList.cxx \ diff --git a/qt/ScintillaEditBase/ScintillaEditBase.pro b/qt/ScintillaEditBase/ScintillaEditBase.pro index 9aa5cd41a..df5e4adbc 100644 --- a/qt/ScintillaEditBase/ScintillaEditBase.pro +++ b/qt/ScintillaEditBase/ScintillaEditBase.pro @@ -38,6 +38,8 @@ SOURCES += \ ../../src/CharClassify.cxx \ ../../src/CellBuffer.cxx \ ../../src/Catalogue.cxx \ + ../../src/CaseFolder.cxx \ + ../../src/CaseConvert.cxx \ ../../src/CallTip.cxx \ ../../src/AutoComplete.cxx \ ../../lexlib/WordList.cxx \ @@ -59,6 +61,7 @@ HEADERS += \ ../../src/XPM.h \ ../../src/ViewStyle.h \ ../../src/UniConversion.h \ + ../../src/UnicodeFromUTF8.h \ ../../src/Style.h \ ../../src/SplitVector.h \ ../../src/Selection.h \ @@ -80,6 +83,8 @@ HEADERS += \ ../../src/CharClassify.h \ ../../src/CellBuffer.h \ ../../src/Catalogue.h \ + ../../src/CaseFolder.h \ + ../../src/CaseConvert.h \ ../../src/CallTip.h \ ../../src/AutoComplete.h \ ../../include/Scintilla.h \ diff --git a/qt/ScintillaEditBase/ScintillaQt.cpp b/qt/ScintillaEditBase/ScintillaQt.cpp index 4d0d62082..a956ff714 100644 --- a/qt/ScintillaEditBase/ScintillaQt.cpp +++ b/qt/ScintillaEditBase/ScintillaQt.cpp @@ -488,30 +488,6 @@ QByteArray ScintillaQt::BytesForDocument(const QString &text) const } -class CaseFolderUTF8 : public CaseFolderTable { -public: - CaseFolderUTF8() { - StandardASCII(); - } - virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) { - if ((lenMixed == 1) && (sizeFolded > 0)) { - folded[0] = mapping[static_cast<unsigned char>(mixed[0])]; - return 1; - } else { - QString su = QString::fromUtf8(mixed, lenMixed); - QString suFolded = su.toCaseFolded(); - QByteArray bytesFolded = suFolded.toUtf8(); - if (bytesFolded.length() < static_cast<int>(sizeFolded)) { - memcpy(folded, bytesFolded, bytesFolded.length()); - return bytesFolded.length(); - } else { - folded[0] = '\0'; - return 0; - } - } - } -}; - class CaseFolderDBCS : public CaseFolderTable { QTextCodec *codec; public: @@ -541,7 +517,7 @@ public: CaseFolder *ScintillaQt::CaseFolderForEncoding() { if (pdoc->dbcsCodePage == SC_CP_UTF8) { - return new CaseFolderUTF8(); + return new CaseFolderUnicode(); } else { const char *charSetBuffer = CharacterSetIDOfDocument(); if (charSetBuffer) { @@ -571,21 +547,20 @@ CaseFolder *ScintillaQt::CaseFolderForEncoding() std::string ScintillaQt::CaseMapString(const std::string &s, int caseMapping) { - if (s.size() == 0) - return std::string(); - - if (caseMapping == cmSame) + if ((s.size() == 0) || (caseMapping == cmSame)) return s; - QTextCodec *codec = 0; - QString text; if (IsUnicodeMode()) { - text = QString::fromUtf8(s.c_str(), s.length()); - } else { - codec = QTextCodec::codecForName(CharacterSetIDOfDocument()); - text = codec->toUnicode(s.c_str(), s.length()); + std::string retMapped(s.length() * maxExpansionCaseConversion, 0); + size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(), + (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower); + retMapped.resize(lenMapped); + return retMapped; } + QTextCodec *codec = QTextCodec::codecForName(CharacterSetIDOfDocument()); + QString text = codec->toUnicode(s.c_str(), s.length()); + if (caseMapping == cmUpper) { text = text.toUpper(); } else { diff --git a/qt/ScintillaEditBase/ScintillaQt.h b/qt/ScintillaEditBase/ScintillaQt.h index f7f7fe3ee..d3c5be594 100644 --- a/qt/ScintillaEditBase/ScintillaQt.h +++ b/qt/ScintillaEditBase/ScintillaQt.h @@ -39,11 +39,13 @@ #include "ViewStyle.h" #include "CharClassify.h" #include "Decoration.h" +#include "CaseFolder.h" #include "Document.h" #include "Selection.h" #include "PositionCache.h" #include "Editor.h" #include "ScintillaBase.h" +#include "CaseConvert.h" #ifdef SCI_LEXER #include "SciLexer.h" diff --git a/scripts/GenerateCaseConvert.py b/scripts/GenerateCaseConvert.py new file mode 100644 index 000000000..841cdcc51 --- /dev/null +++ b/scripts/GenerateCaseConvert.py @@ -0,0 +1,122 @@ +# Script to generate CaseConvert.cxx from Python's Unicode data +# Should be run rarely when a Python with a new version of Unicode data is available. +# Should not be run with old versions of Python. + +# Current best approach divides case conversions into two cases: +# simple symmetric and complex. +# Simple symmetric is where a lower and upper case pair convert to each +# other and the folded form is the same as the lower case. +# There are 1006 symmetric pairs. +# These are further divided into ranges (stored as lower, upper, range length, +# range pitch and singletons (stored as lower, upper). +# Complex is for cases that don't fit the above: where there are multiple +# characters in one of the forms or fold is different to lower or +# lower(upper(x)) or upper(lower(x)) are not x. These are represented as UTF-8 +# strings with original, folded, upper, and lower separated by '|'. +# There are 126 complex cases. + +import codecs, itertools, os, string, sys, unicodedata + +from FileGenerator import Regenerate + +def contiguousRanges(l, diff): + # l is s list of lists + # group into lists where first element of each element differs by diff + out = [[l[0]]] + for s in l[1:]: + if s[0] != out[-1][-1][0] + diff: + out.append([]) + out[-1].append(s) + return out + +def flatten(listOfLists): + "Flatten one level of nesting" + return itertools.chain.from_iterable(listOfLists) + +def conversionSets(): + # For all Unicode characters, see whether they have case conversions + # Return 2 sets: one of simple symmetric conversion cases and another + # with complex cases. + complexes = [] + symmetrics = [] + for ch in range(sys.maxunicode): + if ch >= 0xd800 and ch <= 0xDBFF: + continue + if ch >= 0xdc00 and ch <= 0xDFFF: + continue + uch = chr(ch) + + fold = uch.casefold() + upper = uch.upper() + lower = uch.lower() + symmetric = False + if uch != upper and len(upper) == 1 and uch == lower and uch == fold: + lowerUpper = upper.lower() + foldUpper = upper.casefold() + if lowerUpper == foldUpper and lowerUpper == uch: + symmetric = True + symmetrics.append((ch, ord(upper), ch - ord(upper))) + if uch != lower and len(lower) == 1 and uch == upper and lower == fold: + upperLower = lower.upper() + if upperLower == uch: + symmetric = True + + if fold == uch: + fold = "" + if upper == uch: + upper = "" + if lower == uch: + lower = "" + + if (fold or upper or lower) and not symmetric: + complexes.append((uch, fold, upper, lower)) + + return symmetrics, complexes + +def groupRanges(symmetrics): + # Group the symmetrics into groups where possible, returning a list + # of ranges and a list of symmetrics that didn't fit into a range + + def distance(s): + return s[2] + + groups = [] + uniquekeys = [] + for k, g in itertools.groupby(symmetrics, distance): + groups.append(list(g)) # Store group iterator as a list + uniquekeys.append(k) + + contiguousGroups = flatten([contiguousRanges(g, 1) for g in groups]) + longGroups = [(x[0][0], x[0][1], len(x), 1) for x in contiguousGroups if len(x) > 4] + + oneDiffs = [s for s in symmetrics if s[2] == 1] + contiguousOnes = flatten([contiguousRanges(g, 2) for g in [oneDiffs]]) + longOneGroups = [(x[0][0], x[0][1], len(x), 2) for x in contiguousOnes if len(x) > 4] + + rangeGroups = sorted(longGroups+longOneGroups, key=lambda s: s[0]) + + rangeCoverage = list(flatten([range(r[0], r[0]+r[2]*r[3], r[3]) for r in rangeGroups])) + + nonRanges = [(l, u) for l, u, d in symmetrics if l not in rangeCoverage] + + return rangeGroups, nonRanges + +def updateCaseConvert(): + symmetrics, complexes = conversionSets() + + rangeGroups, nonRanges = groupRanges(symmetrics) + + print(len(rangeGroups), "ranges") + rangeLines = ["%d,%d,%d,%d, " % x for x in rangeGroups] + + print(len(nonRanges), "non ranges") + nonRangeLines = ["%d,%d, " % x for x in nonRanges] + + print(len(symmetrics), "symmetric") + + complexLines = ['"%s|%s|%s|%s|"' % x for x in complexes] + print(len(complexLines), "complex") + + Regenerate("../src/CaseConvert.cxx", "//", rangeLines, nonRangeLines, complexLines) + +updateCaseConvert() diff --git a/src/CaseConvert.cxx b/src/CaseConvert.cxx new file mode 100644 index 000000000..d9ecc3b68 --- /dev/null +++ b/src/CaseConvert.cxx @@ -0,0 +1,620 @@ +// Scintilla source code edit control +// Encoding: UTF-8 +/** @file CaseConvert.cxx + ** Case fold characters and convert them to upper or lower case. + ** Tables automatically regenerated by scripts/GenerateCharacterCategory.py + ** Should only be rarely regenerated for new versions of Unicode. + **/ +// Copyright 2013 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <cstring> + +#include <vector> +#include <algorithm> + +#include "CaseConvert.h" +#include "UniConversion.h" +#include "UnicodeFromUTF8.h" + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +namespace { + // Use an unnamed namespace to protect the declarations from name conflicts + +// Unicode code points are ordered by groups and follow patterns. +// Most characters (pitch==1) are in ranges for a particular alphabet and their +// upper case forms are a fixed distance away. +// Another pattern (pitch==2) is where each lower case letter is preceded by +// the upper case form. These are also grouped into ranges. + +int symmetricCaseConversionRanges[] = { +//lower, upper, range length, range pitch +//++Autogenerated -- start of section automatically generated +//**\(\*\n\) +97,65,26,1, +224,192,23,1, +248,216,7,1, +257,256,24,2, +314,313,8,2, +331,330,23,2, +462,461,8,2, +479,478,9,2, +505,504,20,2, +547,546,9,2, +583,582,5,2, +945,913,17,1, +963,931,9,1, +985,984,12,2, +1072,1040,32,1, +1104,1024,16,1, +1121,1120,17,2, +1163,1162,27,2, +1218,1217,7,2, +1233,1232,44,2, +1377,1329,38,1, +7681,7680,75,2, +7841,7840,48,2, +7936,7944,8,1, +7952,7960,6,1, +7968,7976,8,1, +7984,7992,8,1, +8000,8008,6,1, +8032,8040,8,1, +8560,8544,16,1, +9424,9398,26,1, +11312,11264,47,1, +11393,11392,50,2, +11520,4256,38,1, +42561,42560,23,2, +42625,42624,12,2, +42787,42786,7,2, +42803,42802,31,2, +42879,42878,5,2, +42913,42912,5,2, +65345,65313,26,1, +66600,66560,40,1, + +//--Autogenerated -- end of section automatically generated +}; + +// Code points that are symmetric but don't fit into a range of similar characters +// are listed here. + +int symmetricCaseConversions[] = { +//lower, upper +//++Autogenerated -- start of section automatically generated +//**1 \(\*\n\) +255,376, +307,306, +309,308, +311,310, +378,377, +380,379, +382,381, +384,579, +387,386, +389,388, +392,391, +396,395, +402,401, +405,502, +409,408, +410,573, +414,544, +417,416, +419,418, +421,420, +424,423, +429,428, +432,431, +436,435, +438,437, +441,440, +445,444, +447,503, +454,452, +457,455, +460,458, +477,398, +499,497, +501,500, +572,571, +575,11390, +576,11391, +578,577, +592,11375, +593,11373, +594,11376, +595,385, +596,390, +598,393, +599,394, +601,399, +603,400, +608,403, +611,404, +613,42893, +614,42922, +616,407, +617,406, +619,11362, +623,412, +625,11374, +626,413, +629,415, +637,11364, +640,422, +643,425, +648,430, +649,580, +650,433, +651,434, +652,581, +658,439, +881,880, +883,882, +887,886, +891,1021, +892,1022, +893,1023, +940,902, +941,904, +942,905, +943,906, +972,908, +973,910, +974,911, +983,975, +1010,1017, +1016,1015, +1019,1018, +1231,1216, +7545,42877, +7549,11363, +8017,8025, +8019,8027, +8021,8029, +8023,8031, +8048,8122, +8049,8123, +8050,8136, +8051,8137, +8052,8138, +8053,8139, +8054,8154, +8055,8155, +8056,8184, +8057,8185, +8058,8170, +8059,8171, +8060,8186, +8061,8187, +8112,8120, +8113,8121, +8144,8152, +8145,8153, +8160,8168, +8161,8169, +8165,8172, +8526,8498, +8580,8579, +11361,11360, +11365,570, +11366,574, +11368,11367, +11370,11369, +11372,11371, +11379,11378, +11382,11381, +11500,11499, +11502,11501, +11507,11506, +11559,4295, +11565,4301, +42874,42873, +42876,42875, +42892,42891, +42897,42896, +42899,42898, + +//--Autogenerated -- end of section automatically generated +}; + +// Characters that have complex case conversions are listed here. +// This includes cases where more than one character is needed for a conversion, +// folding is different to lowering, or (as appropriate) upper(lower(x)) != x or +// lower(upper(x)) != x. + +const char *complexCaseConversions = +// Original | Folded | Upper | Lower | +//++Autogenerated -- start of section automatically generated +//**2 \(\*\n\) +"µ|μ|Μ||" +"ß|ss|SS||" +"İ|i̇||i̇|" +"ı||I||" +"ʼn|ʼn|ʼN||" +"ſ|s|S||" +"Dž|dž|DŽ|dž|" +"Lj|lj|LJ|lj|" +"Nj|nj|NJ|nj|" +"ǰ|ǰ|J̌||" +"Dz|dz|DZ|dz|" +"ͅ|ι|Ι||" +"ΐ|ΐ|Ϊ́||" +"ΰ|ΰ|Ϋ́||" +"ς|σ|Σ||" +"ϐ|β|Β||" +"ϑ|θ|Θ||" +"ϕ|φ|Φ||" +"ϖ|π|Π||" +"ϰ|κ|Κ||" +"ϱ|ρ|Ρ||" +"ϴ|θ||θ|" +"ϵ|ε|Ε||" +"և|եւ|ԵՒ||" +"ẖ|ẖ|H̱||" +"ẗ|ẗ|T̈||" +"ẘ|ẘ|W̊||" +"ẙ|ẙ|Y̊||" +"ẚ|aʾ|Aʾ||" +"ẛ|ṡ|Ṡ||" +"ẞ|ss||ß|" +"ὐ|ὐ|Υ̓||" +"ὒ|ὒ|Υ̓̀||" +"ὔ|ὔ|Υ̓́||" +"ὖ|ὖ|Υ̓͂||" +"ᾀ|ἀι|ἈΙ||" +"ᾁ|ἁι|ἉΙ||" +"ᾂ|ἂι|ἊΙ||" +"ᾃ|ἃι|ἋΙ||" +"ᾄ|ἄι|ἌΙ||" +"ᾅ|ἅι|ἍΙ||" +"ᾆ|ἆι|ἎΙ||" +"ᾇ|ἇι|ἏΙ||" +"ᾈ|ἀι|ἈΙ|ᾀ|" +"ᾉ|ἁι|ἉΙ|ᾁ|" +"ᾊ|ἂι|ἊΙ|ᾂ|" +"ᾋ|ἃι|ἋΙ|ᾃ|" +"ᾌ|ἄι|ἌΙ|ᾄ|" +"ᾍ|ἅι|ἍΙ|ᾅ|" +"ᾎ|ἆι|ἎΙ|ᾆ|" +"ᾏ|ἇι|ἏΙ|ᾇ|" +"ᾐ|ἠι|ἨΙ||" +"ᾑ|ἡι|ἩΙ||" +"ᾒ|ἢι|ἪΙ||" +"ᾓ|ἣι|ἫΙ||" +"ᾔ|ἤι|ἬΙ||" +"ᾕ|ἥι|ἭΙ||" +"ᾖ|ἦι|ἮΙ||" +"ᾗ|ἧι|ἯΙ||" +"ᾘ|ἠι|ἨΙ|ᾐ|" +"ᾙ|ἡι|ἩΙ|ᾑ|" +"ᾚ|ἢι|ἪΙ|ᾒ|" +"ᾛ|ἣι|ἫΙ|ᾓ|" +"ᾜ|ἤι|ἬΙ|ᾔ|" +"ᾝ|ἥι|ἭΙ|ᾕ|" +"ᾞ|ἦι|ἮΙ|ᾖ|" +"ᾟ|ἧι|ἯΙ|ᾗ|" +"ᾠ|ὠι|ὨΙ||" +"ᾡ|ὡι|ὩΙ||" +"ᾢ|ὢι|ὪΙ||" +"ᾣ|ὣι|ὫΙ||" +"ᾤ|ὤι|ὬΙ||" +"ᾥ|ὥι|ὭΙ||" +"ᾦ|ὦι|ὮΙ||" +"ᾧ|ὧι|ὯΙ||" +"ᾨ|ὠι|ὨΙ|ᾠ|" +"ᾩ|ὡι|ὩΙ|ᾡ|" +"ᾪ|ὢι|ὪΙ|ᾢ|" +"ᾫ|ὣι|ὫΙ|ᾣ|" +"ᾬ|ὤι|ὬΙ|ᾤ|" +"ᾭ|ὥι|ὭΙ|ᾥ|" +"ᾮ|ὦι|ὮΙ|ᾦ|" +"ᾯ|ὧι|ὯΙ|ᾧ|" +"ᾲ|ὰι|ᾺΙ||" +"ᾳ|αι|ΑΙ||" +"ᾴ|άι|ΆΙ||" +"ᾶ|ᾶ|Α͂||" +"ᾷ|ᾶι|Α͂Ι||" +"ᾼ|αι|ΑΙ|ᾳ|" +"ι|ι|Ι||" +"ῂ|ὴι|ῊΙ||" +"ῃ|ηι|ΗΙ||" +"ῄ|ήι|ΉΙ||" +"ῆ|ῆ|Η͂||" +"ῇ|ῆι|Η͂Ι||" +"ῌ|ηι|ΗΙ|ῃ|" +"ῒ|ῒ|Ϊ̀||" +"ΐ|ΐ|Ϊ́||" +"ῖ|ῖ|Ι͂||" +"ῗ|ῗ|Ϊ͂||" +"ῢ|ῢ|Ϋ̀||" +"ΰ|ΰ|Ϋ́||" +"ῤ|ῤ|Ρ̓||" +"ῦ|ῦ|Υ͂||" +"ῧ|ῧ|Ϋ͂||" +"ῲ|ὼι|ῺΙ||" +"ῳ|ωι|ΩΙ||" +"ῴ|ώι|ΏΙ||" +"ῶ|ῶ|Ω͂||" +"ῷ|ῶι|Ω͂Ι||" +"ῼ|ωι|ΩΙ|ῳ|" +"Ω|ω||ω|" +"K|k||k|" +"Å|å||å|" +"ff|ff|FF||" +"fi|fi|FI||" +"fl|fl|FL||" +"ffi|ffi|FFI||" +"ffl|ffl|FFL||" +"ſt|st|ST||" +"st|st|ST||" +"ﬓ|մն|ՄՆ||" +"ﬔ|մե|ՄԵ||" +"ﬕ|մի|ՄԻ||" +"ﬖ|վն|ՎՆ||" +"ﬗ|մխ|ՄԽ||" + +//--Autogenerated -- end of section automatically generated +; + +class CaseConverter : public ICaseConverter { + // Maximum length of a case conversion result is 6 bytes in UTF-8 + enum { maxConversionLength=6 }; + struct ConversionString { + char conversion[maxConversionLength+1]; + }; + // Conversions are initially store in a vector of structs but then decomposed into + // parallel arrays as that is about 10% faster to search. + struct CharacterConversion { + int character; + ConversionString conversion; + CharacterConversion(int character_=0, const char *conversion_="") : character(character_) { + strcpy(conversion.conversion, conversion_); + } + bool operator<(const CharacterConversion &other) const { + return character < other.character; + } + }; + typedef std::vector<CharacterConversion> CharacterToConversion; + CharacterToConversion characterToConversion; + // The parallel arrays + std::vector<int> characters; + std::vector<ConversionString> conversions; + +public: + CaseConverter() { + } + bool Initialised() const { + return characters.size() > 0; + } + void Add(int character, const char *conversion) { + characterToConversion.push_back(CharacterConversion(character, conversion)); + } + const char *Find(int character) { + const std::vector<int>::iterator it = std::lower_bound(characters.begin(), characters.end(), character); + if (*it == character) + return conversions[it - characters.begin()].conversion; + else + return 0; + } + size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed) { + size_t lenConverted = 0; + size_t mixedPos = 0; + unsigned char bytes[UTF8MaxBytes + 1]; + while (mixedPos < lenMixed) { + const unsigned char leadByte = static_cast<unsigned char>(mixed[mixedPos]); + const char *caseConverted = 0; + size_t lenMixedChar = 1; + if (UTF8IsAscii(leadByte)) { + caseConverted = Find(leadByte); + } else { + bytes[0] = leadByte; + const int widthCharBytes = UTF8BytesOfLead[leadByte]; + for (int b=1; b<widthCharBytes; b++) { + bytes[b] = (mixedPos+b < lenMixed) ? mixed[mixedPos+b] : 0; + } + int classified = UTF8Classify(bytes, widthCharBytes); + if (!(classified & UTF8MaskInvalid)) { + // valid UTF-8 + lenMixedChar = classified & UTF8MaskWidth; + int character = UnicodeFromUTF8(bytes); + caseConverted = Find(character); + } + } + if (caseConverted) { + // Character has a conversion so copy that conversion in + while (*caseConverted) { + converted[lenConverted++] = *caseConverted++; + if (lenConverted >= sizeConverted) + return 0; + } + } else { + // Character has no conversion so copy the input to output + for (size_t i=0; i<lenMixedChar; i++) { + converted[lenConverted++] = mixed[mixedPos+i]; + if (lenConverted >= sizeConverted) + return 0; + } + } + mixedPos += lenMixedChar; + } + return lenConverted; + } + void FinishedAdding() { + std::sort(characterToConversion.begin(), characterToConversion.end()); + characters.reserve(characterToConversion.size()); + conversions.reserve(characterToConversion.size()); + for (CharacterToConversion::iterator it = characterToConversion.begin(); it != characterToConversion.end(); ++it) { + characters.push_back(it->character); + conversions.push_back(it->conversion); + } + // Empty the original calculated data completely + CharacterToConversion().swap(characterToConversion); + } +}; + +CaseConverter caseConvFold; +CaseConverter caseConvUp; +CaseConverter caseConvLow; + +void UTF8FromUTF32Character(int uch, char *putf) { + size_t k = 0; + if (uch < 0x80) { + putf[k++] = static_cast<char>(uch); + } else if (uch < 0x800) { + putf[k++] = static_cast<char>(0xC0 | (uch >> 6)); + putf[k++] = static_cast<char>(0x80 | (uch & 0x3f)); + } else if (uch < 0x10000) { + putf[k++] = static_cast<char>(0xE0 | (uch >> 12)); + putf[k++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f)); + putf[k++] = static_cast<char>(0x80 | (uch & 0x3f)); + } else { + putf[k++] = static_cast<char>(0xF0 | (uch >> 18)); + putf[k++] = static_cast<char>(0x80 | ((uch >> 12) & 0x3f)); + putf[k++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f)); + putf[k++] = static_cast<char>(0x80 | (uch & 0x3f)); + } + putf[k] = 0; +} + +void AddSymmetric(enum CaseConversion conversion, int lower,int upper) { + char lowerUTF8[UTF8MaxBytes+1]; + UTF8FromUTF32Character(lower, lowerUTF8); + char upperUTF8[UTF8MaxBytes+1]; + UTF8FromUTF32Character(upper, upperUTF8); + + switch (conversion) { + case CaseConversionFold: + caseConvFold.Add(upper, lowerUTF8); + break; + case CaseConversionUpper: + caseConvUp.Add(lower, upperUTF8); + break; + case CaseConversionLower: + caseConvLow.Add(upper, lowerUTF8); + break; + } +} + +void SetupConversions(enum CaseConversion conversion) { + // First initialize for the symmetric ranges + for (size_t i=0; i<sizeof(symmetricCaseConversionRanges)/sizeof(symmetricCaseConversionRanges[0]);) { + int lower = symmetricCaseConversionRanges[i++]; + int upper = symmetricCaseConversionRanges[i++]; + int length = symmetricCaseConversionRanges[i++]; + int pitch = symmetricCaseConversionRanges[i++]; + for (int j=0;j<length*pitch;j+=pitch) { + AddSymmetric(conversion, lower+j, upper+j); + } + } + // Add the symmetric singletons + for (size_t i=0; i<sizeof(symmetricCaseConversions)/sizeof(symmetricCaseConversions[0]);) { + int lower = symmetricCaseConversions[i++]; + int upper = symmetricCaseConversions[i++]; + AddSymmetric(conversion, lower, upper); + } + // Add the complex cases + const char *sComplex = complexCaseConversions; + while (*sComplex) { + // Longest ligature is 3 character so 5 for safety + const size_t lenUTF8 = 5*UTF8MaxBytes+1; + char originUTF8[lenUTF8]; + char foldedUTF8[lenUTF8]; + char lowerUTF8[lenUTF8]; + char upperUTF8[lenUTF8]; + size_t i = 0; + while (*sComplex && *sComplex != '|') { + originUTF8[i++] = *sComplex; + sComplex++; + } + sComplex++; + originUTF8[i] = 0; + i = 0; + while (*sComplex && *sComplex != '|') { + foldedUTF8[i++] = *sComplex; + sComplex++; + } + sComplex++; + foldedUTF8[i] = 0; + i = 0; + while (*sComplex && *sComplex != '|') { + upperUTF8[i++] = *sComplex; + sComplex++; + } + sComplex++; + upperUTF8[i] = 0; + i = 0; + while (*sComplex && *sComplex != '|') { + lowerUTF8[i++] = *sComplex; + sComplex++; + } + sComplex++; + lowerUTF8[i] = 0; + + int character = UnicodeFromUTF8(reinterpret_cast<unsigned char *>(originUTF8)); + + if (conversion == CaseConversionFold && foldedUTF8[0]) { + caseConvFold.Add(character, foldedUTF8); + } + + if (conversion == CaseConversionUpper && upperUTF8[0]) { + caseConvUp.Add(character, upperUTF8); + } + + if (conversion == CaseConversionLower && lowerUTF8[0]) { + caseConvLow.Add(character, lowerUTF8); + } + } + + switch (conversion) { + case CaseConversionFold: + caseConvFold.FinishedAdding(); + break; + case CaseConversionUpper: + caseConvUp.FinishedAdding(); + break; + case CaseConversionLower: + caseConvLow.FinishedAdding(); + break; + } +} + +CaseConverter *ConverterForConversion(enum CaseConversion conversion) { + switch (conversion) { + case CaseConversionFold: + return &caseConvFold; + case CaseConversionUpper: + return &caseConvUp; + case CaseConversionLower: + return &caseConvLow; + } + return 0; +} + +} + +ICaseConverter *ConverterFor(enum CaseConversion conversion) { + CaseConverter *pCaseConv = ConverterForConversion(conversion); + if (!pCaseConv->Initialised()) + SetupConversions(conversion); + return pCaseConv; +} + +const char *CaseConvert(int character, enum CaseConversion conversion) { + CaseConverter *pCaseConv = ConverterForConversion(conversion); + if (!pCaseConv->Initialised()) + SetupConversions(conversion); + return pCaseConv->Find(character); +} + +size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed, enum CaseConversion conversion) { + CaseConverter *pCaseConv = ConverterForConversion(conversion); + if (!pCaseConv->Initialised()) + SetupConversions(conversion); + return pCaseConv->CaseConvertString(converted, sizeConverted, mixed, lenMixed); +} diff --git a/src/CaseConvert.h b/src/CaseConvert.h new file mode 100644 index 000000000..60de22799 --- /dev/null +++ b/src/CaseConvert.h @@ -0,0 +1,47 @@ +// Scintilla source code edit control +// Encoding: UTF-8 +/** @file CaseConvert.h + ** Performs Unicode case conversions. + ** Does not handle locale-sensitive case conversion. + **/ +// Copyright 2013 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef CASECONVERT_H +#define CASECONVERT_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +enum CaseConversion { + CaseConversionFold, + CaseConversionUpper, + CaseConversionLower +}; + +class ICaseConverter { +public: + virtual size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed) = 0; +}; + +ICaseConverter *ConverterFor(enum CaseConversion conversion); + +// Returns a UTF-8 string. Empty when no conversion +const char *CaseConvert(int character, enum CaseConversion conversion); + +// When performing CaseConvertString, the converted value may be up to 3 times longer than the input. +// Ligatures are often decomposed into multiple characters and long cases include: +// ΐ "\xce\x90" folds to ΐ "\xce\xb9\xcc\x88\xcc\x81" +const int maxExpansionCaseConversion=3; + +// Converts a mixed case string using a particular conversion. +// Result may be a different length to input and the length is the return value. +// If there is not enough space then 0 is returned. +size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed, enum CaseConversion conversion); + +#ifdef SCI_NAMESPACE +} +#endif + +#endif diff --git a/src/CaseFolder.cxx b/src/CaseFolder.cxx new file mode 100644 index 000000000..44a94da6f --- /dev/null +++ b/src/CaseFolder.cxx @@ -0,0 +1,68 @@ +// Scintilla source code edit control +/** @file CaseFolder.cxx + ** Classes for case folding. + **/ +// Copyright 1998-2013 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <vector> +#include <algorithm> + +#include "CaseConvert.h" +#include "UniConversion.h" +#include "CaseFolder.h" + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +CaseFolder::~CaseFolder() { +} + +CaseFolderTable::CaseFolderTable() { + for (size_t iChar=0; iChar<sizeof(mapping); iChar++) { + mapping[iChar] = static_cast<char>(iChar); + } +} + +CaseFolderTable::~CaseFolderTable() { +} + +size_t CaseFolderTable::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) { + if (lenMixed > sizeFolded) { + return 0; + } else { + for (size_t i=0; i<lenMixed; i++) { + folded[i] = mapping[static_cast<unsigned char>(mixed[i])]; + } + return lenMixed; + } +} + +void CaseFolderTable::SetTranslation(char ch, char chTranslation) { + mapping[static_cast<unsigned char>(ch)] = chTranslation; +} + +void CaseFolderTable::StandardASCII() { + for (size_t iChar=0; iChar<sizeof(mapping); iChar++) { + if (iChar >= 'A' && iChar <= 'Z') { + mapping[iChar] = static_cast<char>(iChar - 'A' + 'a'); + } else { + mapping[iChar] = static_cast<char>(iChar); + } + } +} + +CaseFolderUnicode::CaseFolderUnicode() { + StandardASCII(); + converter = ConverterFor(CaseConversionFold); +} + +size_t CaseFolderUnicode::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) { + if ((lenMixed == 1) && (sizeFolded > 0)) { + folded[0] = mapping[static_cast<unsigned char>(mixed[0])]; + return 1; + } else { + return converter->CaseConvertString(folded, sizeFolded, mixed, lenMixed); + } +} diff --git a/src/CaseFolder.h b/src/CaseFolder.h new file mode 100644 index 000000000..2d754d4f3 --- /dev/null +++ b/src/CaseFolder.h @@ -0,0 +1,45 @@ +// Scintilla source code edit control +/** @file CaseFolder.h + ** Classes for case folding. + **/ +// Copyright 1998-2013 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef CASEFOLDER_H +#define CASEFOLDER_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +class CaseFolder { +public: + virtual ~CaseFolder(); + virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) = 0; +}; + +class CaseFolderTable : public CaseFolder { +protected: + char mapping[256]; +public: + CaseFolderTable(); + virtual ~CaseFolderTable(); + virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed); + void SetTranslation(char ch, char chTranslation); + void StandardASCII(); +}; + +class ICaseConverter; + +class CaseFolderUnicode : public CaseFolderTable { + ICaseConverter *converter; +public: + CaseFolderUnicode(); + virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed); +}; + +#ifdef SCI_NAMESPACE +} +#endif + +#endif diff --git a/src/Document.cxx b/src/Document.cxx index a00fc9fc2..0637c8d50 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -28,6 +28,7 @@ #include "CharClassify.h" #include "CharacterSet.h" #include "Decoration.h" +#include "CaseFolder.h" #include "Document.h" #include "RESearch.h" #include "UniConversion.h" @@ -1496,47 +1497,6 @@ bool Document::IsWordAt(int start, int end) const { return IsWordStartAt(start) && IsWordEndAt(end); } -static inline char MakeLowerCase(char ch) { - if (ch < 'A' || ch > 'Z') - return ch; - else - return static_cast<char>(ch - 'A' + 'a'); -} - -CaseFolderTable::CaseFolderTable() { - for (size_t iChar=0; iChar<sizeof(mapping); iChar++) { - mapping[iChar] = static_cast<char>(iChar); - } -} - -CaseFolderTable::~CaseFolderTable() { -} - -size_t CaseFolderTable::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) { - if (lenMixed > sizeFolded) { - return 0; - } else { - for (size_t i=0; i<lenMixed; i++) { - folded[i] = mapping[static_cast<unsigned char>(mixed[i])]; - } - return lenMixed; - } -} - -void CaseFolderTable::SetTranslation(char ch, char chTranslation) { - mapping[static_cast<unsigned char>(ch)] = chTranslation; -} - -void CaseFolderTable::StandardASCII() { - for (size_t iChar=0; iChar<sizeof(mapping); iChar++) { - if (iChar >= 'A' && iChar <= 'Z') { - mapping[iChar] = static_cast<char>(iChar - 'A' + 'a'); - } else { - mapping[iChar] = static_cast<char>(iChar); - } - } -} - bool Document::MatchesWordOptions(bool word, bool wordStart, int pos, int length) const { return (!word && !wordStart) || (word && IsWordAt(pos, pos + length)) || diff --git a/src/Document.h b/src/Document.h index 5c7e8f8a0..5147875b1 100644 --- a/src/Document.h +++ b/src/Document.h @@ -155,24 +155,6 @@ public: bool isEnabled; }; -class CaseFolder { -public: - virtual ~CaseFolder() { - } - virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) = 0; -}; - -class CaseFolderTable : public CaseFolder { -protected: - char mapping[256]; -public: - CaseFolderTable(); - virtual ~CaseFolderTable(); - virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed); - void SetTranslation(char ch, char chTranslation); - void StandardASCII(); -}; - class Document; class LexInterface { diff --git a/src/Editor.cxx b/src/Editor.cxx index 16e3e8b56..acb840fdf 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -36,6 +36,7 @@ #include "ViewStyle.h" #include "CharClassify.h" #include "Decoration.h" +#include "CaseFolder.h" #include "Document.h" #include "UniConversion.h" #include "Selection.h" diff --git a/src/PositionCache.cxx b/src/PositionCache.cxx index 31eac8592..742a226b9 100644 --- a/src/PositionCache.cxx +++ b/src/PositionCache.cxx @@ -32,6 +32,7 @@ #include "CharClassify.h" #include "Decoration.h" #include "ILexer.h" +#include "CaseFolder.h" #include "Document.h" #include "Selection.h" #include "PositionCache.h" diff --git a/src/ScintillaBase.cxx b/src/ScintillaBase.cxx index 5d886f5a5..05768799d 100644 --- a/src/ScintillaBase.cxx +++ b/src/ScintillaBase.cxx @@ -42,6 +42,7 @@ #include "AutoComplete.h" #include "CharClassify.h" #include "Decoration.h" +#include "CaseFolder.h" #include "Document.h" #include "Selection.h" #include "PositionCache.h" diff --git a/src/UnicodeFromUTF8.h b/src/UnicodeFromUTF8.h new file mode 100644 index 000000000..24517e8a2 --- /dev/null +++ b/src/UnicodeFromUTF8.h @@ -0,0 +1,19 @@ +// Scintilla source code edit control +/** @file UnicodeFromUTF8.h + ** Lexer infrastructure. + **/ +// Copyright 2013 by Neil Hodgson <neilh@scintilla.org> +// This file is in the public domain. + +inline int UnicodeFromUTF8(const unsigned char *us) { + if (us[0] < 0xC2) { + return us[0]; + } else if (us[0] < 0xE0) { + return ((us[0] & 0x1F) << 6) + (us[1] & 0x3F); + } else if (us[0] < 0xF0) { + return ((us[0] & 0xF) << 12) + ((us[1] & 0x3F) << 6) + (us[2] & 0x3F); + } else if (us[0] < 0xF5) { + return ((us[0] & 0x7) << 18) + ((us[1] & 0x3F) << 12) + ((us[2] & 0x3F) << 6) + (us[3] & 0x3F); + } + return us[0]; +} diff --git a/test/simpleTests.py b/test/simpleTests.py index 4c669f297..ed55a67f9 100644 --- a/test/simpleTests.py +++ b/test/simpleTests.py @@ -1414,39 +1414,35 @@ class TestCaseMapping(unittest.TestCase): self.assertEquals(self.ed.Contents(), r) def testUTFGrows(self): - # FIXME: change to using Unicode compliant case conversion on Windows - if sys.platform != "win32": - # This crashed at one point in debug builds due to looking past end of shorter string - self.ed.SetCodePage(65001) - # ﬖ is a single character ligature taking 3 bytes in UTF8: EF AC 96 - t = 'ﬖﬖ'.encode("UTF-8") - self.ed.SetContents(t) - self.assertEquals(self.ed.Length, 6) - self.ed.SetSel(0,self.ed.Length) - self.ed.UpperCase() - # To convert to upper case the ligature is separated into վ and ն then uppercased to Վ and Ն - # each of which takes 2 bytes in UTF-8: D5 8E D5 86 - r = 'ՎՆՎՆ'.encode("UTF-8") - self.assertEquals(self.ed.Length, 8) - self.assertEquals(self.ed.Contents(), r) - self.assertEquals(self.ed.SelectionEnd, self.ed.Length) + # This crashed at one point in debug builds due to looking past end of shorter string + self.ed.SetCodePage(65001) + # ﬖ is a single character ligature taking 3 bytes in UTF8: EF AC 96 + t = 'ﬖﬖ'.encode("UTF-8") + self.ed.SetContents(t) + self.assertEquals(self.ed.Length, 6) + self.ed.SetSel(0,self.ed.Length) + self.ed.UpperCase() + # To convert to upper case the ligature is separated into վ and ն then uppercased to Վ and Ն + # each of which takes 2 bytes in UTF-8: D5 8E D5 86 + r = 'ՎՆՎՆ'.encode("UTF-8") + self.assertEquals(self.ed.Length, 8) + self.assertEquals(self.ed.Contents(), r) + self.assertEquals(self.ed.SelectionEnd, self.ed.Length) def testUTFShrinks(self): - # FIXME: change to using Unicode compliant case conversion on Windows - if sys.platform != "win32": - self.ed.SetCodePage(65001) - # fi is a single character ligature taking 3 bytes in UTF8: EF AC 81 - t = 'fifi'.encode("UTF-8") - self.ed.SetContents(t) - self.assertEquals(self.ed.Length, 6) - self.ed.SetSel(0,self.ed.Length) - self.ed.UpperCase() - # To convert to upper case the ligature is separated into f and i then uppercased to F and I - # each of which takes 1 byte in UTF-8: 46 49 - r = 'FIFI'.encode("UTF-8") - self.assertEquals(self.ed.Length, 4) - self.assertEquals(self.ed.Contents(), r) - self.assertEquals(self.ed.SelectionEnd, self.ed.Length) + self.ed.SetCodePage(65001) + # fi is a single character ligature taking 3 bytes in UTF8: EF AC 81 + t = 'fifi'.encode("UTF-8") + self.ed.SetContents(t) + self.assertEquals(self.ed.Length, 6) + self.ed.SetSel(0,self.ed.Length) + self.ed.UpperCase() + # To convert to upper case the ligature is separated into f and i then uppercased to F and I + # each of which takes 1 byte in UTF-8: 46 49 + r = 'FIFI'.encode("UTF-8") + self.assertEquals(self.ed.Length, 4) + self.assertEquals(self.ed.Contents(), r) + self.assertEquals(self.ed.SelectionEnd, self.ed.Length) class TestCaseInsensitiveSearch(unittest.TestCase): def setUp(self): @@ -1514,15 +1510,9 @@ class TestCaseInsensitiveSearch(unittest.TestCase): def testUTFDifferentLength(self): # Searching for a two byte string finds a single byte self.ed.SetCodePage(65001) - # The platforms currently have different folding behaviour (should be fixed) - if sys.platform == "win32": - # two byte string "İ" single byte "i" - text = "Fråndi Ååİ $".encode("UTF-8") - searchString = "İ".encode("UTF-8") - else: - # two byte string "ſ" single byte "s" - text = "Frånds Ååſ $".encode("UTF-8") - searchString = "ſ".encode("UTF-8") + # two byte string "ſ" single byte "s" + text = "Frånds Ååſ $".encode("UTF-8") + searchString = "ſ".encode("UTF-8") firstPosition = len("Frånd".encode("UTF-8")) self.assertEquals(len(searchString), 2) self.ed.SetContents(text) diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index 8c08d6ea1..cf5750cde 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -60,12 +60,15 @@ #include "ViewStyle.h" #include "CharClassify.h" #include "Decoration.h" +#include "CaseFolder.h" #include "Document.h" #include "Selection.h" #include "PositionCache.h" #include "Editor.h" #include "ScintillaBase.h" #include "UniConversion.h" +#include "CaseConvert.h" + #include "PlatWin.h" #ifdef SCI_LEXER @@ -1443,55 +1446,6 @@ void ScintillaWin::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) MAKELPARAM(pt.x, pt.y)); } -class CaseFolderUTF8 : public CaseFolderTable { - // Allocate the expandable storage here so that it does not need to be reallocated - // for each call to Fold. - std::vector<wchar_t> utf16Mixed; - std::vector<wchar_t> utf16Folded; -public: - CaseFolderUTF8() { - StandardASCII(); - } - virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) { - if ((lenMixed == 1) && (sizeFolded > 0)) { - folded[0] = mapping[static_cast<unsigned char>(mixed[0])]; - return 1; - } else { - if (lenMixed > utf16Mixed.size()) { - utf16Mixed.resize(lenMixed + 8); - } - size_t nUtf16Mixed = ::MultiByteToWideChar(65001, 0, mixed, - static_cast<int>(lenMixed), - &utf16Mixed[0], - static_cast<int>(utf16Mixed.size())); - - if (nUtf16Mixed == 0) { - // Failed to convert -> bad UTF-8 - folded[0] = '\0'; - return 1; - } - - if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4 - utf16Folded.resize(nUtf16Mixed * 4 + 8); - } - int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, - LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE, - &utf16Mixed[0], - static_cast<int>(nUtf16Mixed), - &utf16Folded[0], - static_cast<int>(utf16Folded.size())); - - size_t lenOut = UTF8Length(&utf16Folded[0], lenFlat); - if (lenOut < sizeFolded) { - UTF8FromUTF16(&utf16Folded[0], lenFlat, folded, static_cast<int>(lenOut)); - return lenOut; - } else { - return 0; - } - } - } -}; - class CaseFolderDBCS : public CaseFolderTable { // Allocate the expandable storage here so that it does not need to be reallocated // for each call to Fold. @@ -1521,15 +1475,23 @@ public: return 1; } - if (nUtf16Mixed * 4 > utf16Folded.size()) { // Maximum folding expansion factor of 4 - utf16Folded.resize(nUtf16Mixed * 4 + 8); + unsigned int lenFlat = 0; + for (size_t mixIndex=0; mixIndex < nUtf16Mixed; mixIndex++) { + if ((lenFlat + 20) > utf16Folded.size()) + utf16Folded.resize(lenFlat + 60); + const char *foldedUTF8 = CaseConvert(utf16Mixed[mixIndex], CaseConversionFold); + if (foldedUTF8) { + // Maximum length of a case conversion is 6 bytes, 3 characters + wchar_t wFolded[20]; + unsigned int charsConverted = UTF16FromUTF8(foldedUTF8, + static_cast<unsigned int>(strlen(foldedUTF8)), + wFolded, sizeof(wFolded)/sizeof(wFolded[0])); + for (size_t j=0;j<charsConverted;j++) + utf16Folded[lenFlat++] = wFolded[j]; + } else { + utf16Folded[lenFlat++] = utf16Mixed[mixIndex]; + } } - int lenFlat = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, - LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE, - &utf16Mixed[0], - static_cast<int>(nUtf16Mixed), - &utf16Folded[0], - static_cast<int>(utf16Folded.size())); size_t lenOut = ::WideCharToMultiByte(cp, 0, &utf16Folded[0], lenFlat, @@ -1550,7 +1512,7 @@ public: CaseFolder *ScintillaWin::CaseFolderForEncoding() { UINT cpDest = CodePageOfDocument(); if (cpDest == SC_CP_UTF8) { - return new CaseFolderUTF8(); + return new CaseFolderUnicode(); } else { if (pdoc->dbcsCodePage == 0) { CaseFolderTable *pcf = new CaseFolderTable(); @@ -1564,16 +1526,21 @@ CaseFolder *ScintillaWin::CaseFolderForEncoding() { unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, sCharacter, 1, wCharacter, sizeof(wCharacter)/sizeof(wCharacter[0])); if (lengthUTF16 == 1) { - wchar_t wLower[20]; - int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, - LCMAP_LINGUISTIC_CASING | LCMAP_LOWERCASE, - wCharacter, lengthUTF16, wLower, sizeof(wLower)/sizeof(wLower[0])); - char sCharacterLowered[20]; - unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0, - wLower, charsConverted, - sCharacterLowered, sizeof(sCharacterLowered), NULL, 0); - if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) { - pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]); + const char *caseFolded = CaseConvert(wCharacter[0], CaseConversionFold); + if (caseFolded) { + wchar_t wLower[20]; + unsigned int charsConverted = UTF16FromUTF8(caseFolded, + static_cast<unsigned int>(strlen(caseFolded)), + wLower, sizeof(wLower)/sizeof(wLower[0])); + if (charsConverted == 1) { + char sCharacterLowered[20]; + unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0, + wLower, charsConverted, + sCharacterLowered, sizeof(sCharacterLowered), NULL, 0); + if ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) { + pcf->SetTranslation(sCharacter[0], sCharacterLowered[0]); + } + } } } } @@ -1585,13 +1552,17 @@ CaseFolder *ScintillaWin::CaseFolderForEncoding() { } std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) { - if (s.size() == 0) - return std::string(); - - if (caseMapping == cmSame) + if ((s.size() == 0) || (caseMapping == cmSame)) return s; UINT cpDoc = CodePageOfDocument(); + if (cpDoc == SC_CP_UTF8) { + std::string retMapped(s.length() * maxExpansionCaseConversion, 0); + size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(), + (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower); + retMapped.resize(lenMapped); + return retMapped; + } unsigned int lengthUTF16 = ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), NULL, 0); @@ -1601,63 +1572,27 @@ std::string ScintillaWin::CaseMapString(const std::string &s, int caseMapping) { DWORD mapFlags = LCMAP_LINGUISTIC_CASING | ((caseMapping == cmUpper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE); - // Many conversions performed by search function are short so optimize this case. - enum { shortSize=20 }; - - if (s.size() > shortSize) { - // Use dynamic allocations for long strings - - // Change text to UTF-16 - std::vector<wchar_t> vwcText(lengthUTF16); - ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16); - - // Change case - int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, - &vwcText[0], lengthUTF16, NULL, 0); - std::vector<wchar_t> vwcConverted(charsConverted); - ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, - &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted); - - // Change back to document encoding - unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0, - &vwcConverted[0], static_cast<int>(vwcConverted.size()), - NULL, 0, NULL, 0); - std::vector<char> vcConverted(lengthConverted); - ::WideCharToMultiByte(cpDoc, 0, - &vwcConverted[0], static_cast<int>(vwcConverted.size()), - &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0); - - return std::string(&vcConverted[0], vcConverted.size()); - - } else { - // Use static allocations for short strings as much faster - // A factor of 15 for single character strings - - // Change text to UTF-16 - wchar_t vwcText[shortSize]; - ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), - vwcText, lengthUTF16); - - // Change case - int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, - vwcText, lengthUTF16, NULL, 0); - // Full mapping may produce up to 3 characters per input character - wchar_t vwcConverted[shortSize*3]; - ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, vwcText, lengthUTF16, - vwcConverted, charsConverted); - - // Change back to document encoding - unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0, - vwcConverted, charsConverted, - NULL, 0, NULL, 0); - // Each UTF-16 code unit may need up to 3 bytes in UTF-8 - char vcConverted[shortSize * 3 * 3]; - ::WideCharToMultiByte(cpDoc, 0, - vwcConverted, charsConverted, - vcConverted, lengthConverted, NULL, 0); - - return std::string(vcConverted, lengthConverted); - } + // Change text to UTF-16 + std::vector<wchar_t> vwcText(lengthUTF16); + ::MultiByteToWideChar(cpDoc, 0, s.c_str(), static_cast<int>(s.size()), &vwcText[0], lengthUTF16); + + // Change case + int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, + &vwcText[0], lengthUTF16, NULL, 0); + std::vector<wchar_t> vwcConverted(charsConverted); + ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags, + &vwcText[0], lengthUTF16, &vwcConverted[0], charsConverted); + + // Change back to document encoding + unsigned int lengthConverted = ::WideCharToMultiByte(cpDoc, 0, + &vwcConverted[0], static_cast<int>(vwcConverted.size()), + NULL, 0, NULL, 0); + std::vector<char> vcConverted(lengthConverted); + ::WideCharToMultiByte(cpDoc, 0, + &vwcConverted[0], static_cast<int>(vwcConverted.size()), + &vcConverted[0], static_cast<int>(vcConverted.size()), NULL, 0); + + return std::string(&vcConverted[0], vcConverted.size()); } void ScintillaWin::Copy() { diff --git a/win32/makefile b/win32/makefile index efcb518cb..c9086969a 100644 --- a/win32/makefile +++ b/win32/makefile @@ -48,6 +48,8 @@ LEXOBJS:=$(addsuffix .o,$(basename $(notdir $(wildcard ../lexers/Lex*.cxx)))) BASEOBJS = \ AutoComplete.o \ CallTip.o \ + CaseConvert.o \ + CaseFolder.o \ CellBuffer.o \ CharacterCategory.o \ CharacterSet.o \ @@ -111,7 +113,7 @@ ScintillaBaseL.o: ScintillaBase.cxx Platform.h \ CharClassify.h Decoration.h Document.h \ Selection.h PositionCache.h Editor.h \ ScintillaBase.h LexAccessor.h Accessor.h \ - LexerModule.h Catalogue.h + LexerModule.h Catalogue.h CaseFolder.h ScintillaWinL.o: ScintillaWin.cxx Platform.h \ ILexer.h Scintilla.h SplitVector.h \ @@ -122,7 +124,8 @@ ScintillaWinL.o: ScintillaWin.cxx Platform.h \ Document.h Selection.h PositionCache.h \ Editor.h ScintillaBase.h UniConversion.h \ LexAccessor.h Accessor.h \ - LexerModule.h Catalogue.h + LexerModule.h Catalogue.h CaseConvert.h \ + CaseFolder.h ScintillaWinS.o: ScintillaWin.cxx Platform.h \ ILexer.h Scintilla.h SplitVector.h \ @@ -131,7 +134,8 @@ ScintillaWinS.o: ScintillaWin.cxx Platform.h \ XPM.h LineMarker.h Style.h AutoComplete.h \ ViewStyle.h CharClassify.h Decoration.h \ Document.h Selection.h PositionCache.h \ - Editor.h ScintillaBase.h UniConversion.h + Editor.h ScintillaBase.h UniConversion.h \ + CaseConvert.h CaseFolder.h ScintillaBaseL.o: $(CC) $(CXXFLAGS) -D SCI_LEXER -c $< -o $@ diff --git a/win32/scintilla.mak b/win32/scintilla.mak index 742c2d3c0..13ecd43cc 100644 --- a/win32/scintilla.mak +++ b/win32/scintilla.mak @@ -60,6 +60,8 @@ clean: SOBJS=\ $(DIR_O)\AutoComplete.obj \ $(DIR_O)\CallTip.obj \ + $(DIR_O)\CaseConvert.obj \ + $(DIR_O)\CaseFolder.obj \ $(DIR_O)\CellBuffer.obj \ $(DIR_O)\CharacterCategory.obj \ $(DIR_O)\CharacterSet.obj \ @@ -185,6 +187,8 @@ LOBJS=\ $(DIR_O)\Accessor.obj \ $(DIR_O)\AutoComplete.obj \ $(DIR_O)\CallTip.obj \ + $(DIR_O)\CaseConvert.obj \ + $(DIR_O)\CaseFolder.obj \ $(DIR_O)\Catalogue.obj \ $(DIR_O)\CellBuffer.obj \ $(DIR_O)\CharacterCategory.obj \ @@ -263,6 +267,10 @@ $(DIR_O)\AutoComplete.obj: ../src/AutoComplete.cxx ../include/Platform.h \ $(DIR_O)\Accessor.obj: ../lexlib/Accessor.cxx ../lexlib/Accessor.h $(DIR_O)\CallTip.obj: ../src/CallTip.cxx ../include/Platform.h \ ../include/Scintilla.h ../src/CallTip.h +$(DIR_O)\CaseConvert.obj: ../src/CaseConvert.cxx ../src/CaseConvert.h \ + ../src/UnicodeFromUTF8.h ../src/UniConversion.h +$(DIR_O)\CaseFolder.obj: ../src/CaseFolder.cxx ../src/CaseFolder.h \ + ../src/CaseConvert.h ../src/UniConversion.h $(DIR_O)\CellBuffer.obj: ../src/CellBuffer.cxx ../include/Platform.h \ ../include/Scintilla.h ../src/SplitVector.h \ ../src/Partitioning.h ../src/CellBuffer.h @@ -279,13 +287,14 @@ $(DIR_O)\Document.obj: ../src/Document.cxx ../include/Platform.h \ ../include/Scintilla.h ../src/SplitVector.h \ ../src/Partitioning.h ../src/RunStyles.h ../src/CellBuffer.h \ ../src/CharClassify.h ../src/Decoration.h ../src/Document.h \ - ../src/RESearch.h ../src/PerLine.h + ../src/RESearch.h ../src/PerLine.h ../src/CaseFolder.h $(DIR_O)\Editor.obj: ../src/Editor.cxx ../include/Platform.h ../include/Scintilla.h \ ../src/ContractionState.h ../src/SplitVector.h \ ../src/Partitioning.h ../src/CellBuffer.h ../src/KeyMap.h \ ../src/RunStyles.h ../src/Indicator.h ../src/XPM.h ../src/LineMarker.h \ ../src/Style.h ../src/ViewStyle.h ../src/CharClassify.h \ - ../src/Decoration.h ../src/Document.h ../src/Editor.h ../src/Selection.h ../src/PositionCache.h + ../src/Decoration.h ../src/Document.h ../src/Editor.h ../src/Selection.h ../src/PositionCache.h \ + ../src/CaseFolder.h $(DIR_O)\ExternalLexer.obj: ../src/ExternalLexer.cxx ../include/Platform.h \ ../include/Scintilla.h ../include/SciLexer.h \ ../lexlib/Accessor.h ../src/ExternalLexer.h @@ -495,7 +504,8 @@ $(DIR_O)\PositionCache.obj: ../src/PositionCache.cxx ../include/Platform.h ../in ../src/Partitioning.h ../src/CellBuffer.h ../src/KeyMap.h \ ../src/RunStyles.h ../src/Indicator.h ../src/XPM.h ../src/LineMarker.h \ ../src/Style.h ../src/ViewStyle.h ../src/CharClassify.h \ - ../src/Decoration.h ../src/Document.h ../src/Editor.h ../src/Selection.h ../src/PositionCache.h + ../src/Decoration.h ../src/Document.h ../src/Editor.h ../src/Selection.h ../src/PositionCache.h \ + ../src/CaseFolder.h $(DIR_O)\PropSetSimple.obj: ../lexlib/PropSetSimple.cxx ../include/Platform.h $(DIR_O)\RESearch.obj: ../src/RESearch.cxx ../src/CharClassify.h ../src/RESearch.h $(DIR_O)\RunStyles.obj: ../src/RunStyles.cxx ../include/Platform.h \ @@ -508,7 +518,8 @@ $(DIR_O)\ScintillaBase.obj: ../src/ScintillaBase.cxx ../include/Platform.h \ ../src/CallTip.h ../src/KeyMap.h ../src/Indicator.h ../src/XPM.h \ ../src/LineMarker.h ../src/Style.h ../src/ViewStyle.h \ ../src/AutoComplete.h ../src/CharClassify.h ../src/Decoration.h \ - ../src/Document.h ../src/Editor.h ../src/Selection.h ../src/ScintillaBase.h + ../src/Document.h ../src/Editor.h ../src/Selection.h ../src/ScintillaBase.h \ + ../src/CaseFolder.h $(DIR_O)\ScintillaBaseL.obj: ../src/ScintillaBase.cxx ../include/Platform.h \ ../include/Scintilla.h \ ../src/ContractionState.h ../src/SplitVector.h \ @@ -516,7 +527,8 @@ $(DIR_O)\ScintillaBaseL.obj: ../src/ScintillaBase.cxx ../include/Platform.h \ ../src/CallTip.h ../src/KeyMap.h ../src/Indicator.h ../src/XPM.h \ ../src/LineMarker.h ../src/Style.h ../src/ViewStyle.h \ ../src/AutoComplete.h ../src/CharClassify.h ../src/Decoration.h \ - ../src/Document.h ../src/Editor.h ../src/Selection.h ../src/ScintillaBase.h + ../src/Document.h ../src/Editor.h ../src/Selection.h ../src/ScintillaBase.h \ + ../src/CaseFolder.h $(DIR_O)\ScintillaWin.obj: ScintillaWin.cxx ../include/Platform.h \ ../include/Scintilla.h ../src/ContractionState.h \ ../src/SplitVector.h ../src/Partitioning.h \ @@ -524,7 +536,8 @@ $(DIR_O)\ScintillaWin.obj: ScintillaWin.cxx ../include/Platform.h \ ../src/Indicator.h ../src/XPM.h ../src/LineMarker.h ../src/Style.h \ ../src/AutoComplete.h ../src/ViewStyle.h ../src/CharClassify.h \ ../src/Decoration.h ../src/Document.h ../src/Editor.h \ - ../src/ScintillaBase.h ../src/Selection.h ../src/UniConversion.h + ../src/ScintillaBase.h ../src/Selection.h ../src/UniConversion.h \ + ../src/CaseConvert.h ../src/CaseFolder.h $(DIR_O)\ScintillaWinS.obj: ScintillaWin.cxx ../include/Platform.h \ ../include/Scintilla.h ../src/ContractionState.h \ ../src/SplitVector.h ../src/Partitioning.h \ |