diff options
-rw-r--r-- | doc/ScintillaDoc.html | 54 | ||||
-rw-r--r-- | doc/ScintillaHistory.html | 4 | ||||
-rw-r--r-- | include/Scintilla.h | 8 | ||||
-rw-r--r-- | include/Scintilla.iface | 21 | ||||
-rw-r--r-- | include/ScintillaMessages.h | 5 | ||||
-rw-r--r-- | include/ScintillaTypes.h | 12 | ||||
-rw-r--r-- | src/EditView.cxx | 76 | ||||
-rw-r--r-- | src/Editor.cxx | 33 | ||||
-rw-r--r-- | src/PositionCache.cxx | 94 | ||||
-rw-r--r-- | src/PositionCache.h | 18 | ||||
-rw-r--r-- | test/simpleTests.py | 25 |
11 files changed, 283 insertions, 67 deletions
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index 14969616e..bae44931d 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -3842,7 +3842,12 @@ struct Sci_TextToFind { <a class="message" href="#SCI_SETREPRESENTATION">SCI_SETREPRESENTATION(const char *encodedCharacter, const char *representation)</a><br /> <a class="message" href="#SCI_GETREPRESENTATION">SCI_GETREPRESENTATION(const char *encodedCharacter, char *representation) → int</a><br /> <a class="message" href="#SCI_CLEARREPRESENTATION">SCI_CLEARREPRESENTATION(const char *encodedCharacter)</a><br /> - <a class="message" href="#SCI_SETCONTROLCHARSYMBOL">SCI_SETCONTROLCHARSYMBOL(int symbol)</a><br /> + <a class="message" href="#SCI_CLEARALLREPRESENTATIONS">SCI_CLEARALLREPRESENTATIONS</a><br /> + <a class="message" href="#SCI_SETREPRESENTATIONAPPEARANCE">SCI_SETREPRESENTATIONAPPEARANCE(const char *encodedCharacter, int appearance)</a><br /> + <a class="message" href="#SCI_GETREPRESENTATIONAPPEARANCE">SCI_GETREPRESENTATIONAPPEARANCE(const char *encodedCharacter) → int</a><br /> + <a class="message" href="#SCI_SETREPRESENTATIONCOLOUR">SCI_SETREPRESENTATIONCOLOUR(const char *encodedCharacter, colouralpha colour)</a><br /> + <a class="message" href="#SCI_GETREPRESENTATIONCOLOUR">SCI_GETREPRESENTATIONCOLOUR(const char *encodedCharacter) → colouralpha</a><br /> + <a class="message" href="#SCI_SETCONTROLCHARSYMBOL">SCI_SETCONTROLCHARSYMBOL(int symbol)</a><br /> <a class="message" href="#SCI_GETCONTROLCHARSYMBOL">SCI_GETCONTROLCHARSYMBOL → int</a><br /> </code> @@ -3859,9 +3864,56 @@ struct Sci_TextToFind { <p>The encodedCharacter parameter is a NUL-terminated string of the bytes for one character in the current encoding. This can not be used to set a representation for multiple-character strings. </p> + <p>One exception to the single character restriction is that the two character sequence "\r\n" (Carriage Return + Line Feed) + can have a representation that is visible in line end viewing (<a class="seealso" href="#SCI_SETVIEWEOL">SCI_SETVIEWEOL</a>) mode. + If there is no representation for "\r\n" then the individual '\r' and '\n' representations will be seen.</p> + <p>The NUL (0) character is a special case since the encodedCharacter parameter is NUL terminated, the NUL character is specified as an empty string.</p> + <p><b id="SCI_CLEARALLREPRESENTATIONS">SCI_CLEARALLREPRESENTATIONS</b><br /> + Reset representations to defaults with <code>SCI_CLEARALLREPRESENTATIONS</code>.</p> + + <p><b id="SCI_SETREPRESENTATIONAPPEARANCE">SCI_SETREPRESENTATIONAPPEARANCE(const char *encodedCharacter, int appearance)</b><br /> + <b id="SCI_GETREPRESENTATIONAPPEARANCE">SCI_GETREPRESENTATIONAPPEARANCE(const char *encodedCharacter) → int</b><br /> + The appearance may be changed using these flags. If a colour is set and the appearance is set without + <code>SC_REPRESENTATION_COLOUR</code> then the representation will show in the colour of the underlying text.</p> + + <table class="standard" summary="Layer"> + <tbody valign="top"> + <tr> + <th align="left"><code>SC_REPRESENTATION_PLAIN</code></th> + + <td>0</td> + + <td>Draw the representation text with no decorations.</td> + </tr> + + <tr> + <th align="left"><code>SC_REPRESENTATION_BLOB</code></th> + + <td>1</td> + + <td>Draw the representation text inverted in a rounded rectangle. This is the default appearance.</td> + </tr> + + <tr class="section"> + <th align="left"><code>SC_REPRESENTATION_COLOUR</code></th> + + <td>0x10</td> + + <td>Draw the representation in the colour set with <a class="seealso" href="#SCI_SETREPRESENTATIONCOLOUR">SCI_SETREPRESENTATIONCOLOUR</a> + instead of in the colour of the style of the text being represented.</td> + </tr> + + </tbody> + </table> + + + <p><b id="SCI_SETREPRESENTATIONCOLOUR">SCI_SETREPRESENTATIONCOLOUR(const char *encodedCharacter, colouralpha colour)</b><br /> + <b id="SCI_GETREPRESENTATIONCOLOUR">SCI_GETREPRESENTATIONCOLOUR(const char *encodedCharacter) → colouralpha</b><br /> + The colour and translucency of a representation may be set.</p> + <p><b id="SCI_SETCONTROLCHARSYMBOL">SCI_SETCONTROLCHARSYMBOL(int symbol)</b><br /> <b id="SCI_GETCONTROLCHARSYMBOL">SCI_GETCONTROLCHARSYMBOL → int</b><br /> The mnemonics may be replaced by a nominated symbol with an ASCII code in the diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index 19ba6ca78..30008fc85 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -581,6 +581,10 @@ Released 2 June 2021. </li> <li> + Add APIs for setting appearance (traditional blob or plain text) and colour of representations + and support setting a representation for the "\r\n" line end sequence. + </li> + <li> Fixed bug with SCI_GETLASTCHILD. <a href="https://sourceforge.net/p/scintilla/bugs/2260/">Bug #2260</a>. </li> diff --git a/include/Scintilla.h b/include/Scintilla.h index a9ce7184c..123f1ae96 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -1052,6 +1052,14 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCI_SETREPRESENTATION 2665 #define SCI_GETREPRESENTATION 2666 #define SCI_CLEARREPRESENTATION 2667 +#define SCI_CLEARALLREPRESENTATIONS 2770 +#define SC_REPRESENTATION_PLAIN 0 +#define SC_REPRESENTATION_BLOB 1 +#define SC_REPRESENTATION_COLOUR 0x10 +#define SCI_SETREPRESENTATIONAPPEARANCE 2766 +#define SCI_GETREPRESENTATIONAPPEARANCE 2767 +#define SCI_SETREPRESENTATIONCOLOUR 2768 +#define SCI_GETREPRESENTATIONCOLOUR 2769 #define SCI_EOLANNOTATIONSETTEXT 2740 #define SCI_EOLANNOTATIONGETTEXT 2741 #define SCI_EOLANNOTATIONSETSTYLE 2742 diff --git a/include/Scintilla.iface b/include/Scintilla.iface index 4c576a77b..dd91bf908 100644 --- a/include/Scintilla.iface +++ b/include/Scintilla.iface @@ -2936,6 +2936,27 @@ get int GetRepresentation=2666(string encodedCharacter, stringresult representat # Remove a character representation. fun void ClearRepresentation=2667(string encodedCharacter,) +# Clear representations to default. +fun void ClearAllRepresentations=2770(,) + +# Can draw representations in various ways +enu RepresentationAppearance=SC_REPRESENTATION +val SC_REPRESENTATION_PLAIN=0 +val SC_REPRESENTATION_BLOB=1 +val SC_REPRESENTATION_COLOUR=0x10 + +# Set the appearance of a representation. +set void SetRepresentationAppearance=2766(string encodedCharacter, RepresentationAppearance appearance) + +# Get the appearance of a representation. +get RepresentationAppearance GetRepresentationAppearance=2767(string encodedCharacter,) + +# Set the colour of a representation. +set void SetRepresentationColour=2768(string encodedCharacter, colouralpha colour) + +# Get the colour of a representation. +get colouralpha GetRepresentationColour=2769(string encodedCharacter,) + # Set the end of line annotation text for a line set void EOLAnnotationSetText=2740(line line, string text) diff --git a/include/ScintillaMessages.h b/include/ScintillaMessages.h index ba2a3f2cd..6cf515df9 100644 --- a/include/ScintillaMessages.h +++ b/include/ScintillaMessages.h @@ -723,6 +723,11 @@ enum class Message { SetRepresentation = 2665, GetRepresentation = 2666, ClearRepresentation = 2667, + ClearAllRepresentations = 2770, + SetRepresentationAppearance = 2766, + GetRepresentationAppearance = 2767, + SetRepresentationColour = 2768, + GetRepresentationColour = 2769, EOLAnnotationSetText = 2740, EOLAnnotationGetText = 2741, EOLAnnotationSetStyle = 2742, diff --git a/include/ScintillaTypes.h b/include/ScintillaTypes.h index a964baa81..ca0a07527 100644 --- a/include/ScintillaTypes.h +++ b/include/ScintillaTypes.h @@ -472,6 +472,12 @@ enum class LineEndType { Unicode = 1, }; +enum class RepresentationAppearance { + Plain = 0, + Blob = 1, + Colour = 0x10, +}; + enum class EOLAnnotationVisible { Hidden = 0x0, Standard = 0x1, @@ -738,6 +744,12 @@ constexpr LineEndType operator&(LineEndType a, LineEndType b) noexcept { return static_cast<LineEndType>(static_cast<int>(a) & static_cast<int>(b)); } +// Functions to manipulate fields from a RepresentationAppearance + +constexpr RepresentationAppearance operator|(RepresentationAppearance a, RepresentationAppearance b) noexcept { + return static_cast<RepresentationAppearance>(static_cast<int>(a) | static_cast<int>(b)); +} + // Functions to manipulate fields from a LineCharacterIndexType constexpr LineCharacterIndexType operator|(LineCharacterIndexType a, LineCharacterIndexType b) noexcept { diff --git a/src/EditView.cxx b/src/EditView.cxx index 2774d5f2e..80edd31c6 100644 --- a/src/EditView.cxx +++ b/src/EditView.cxx @@ -474,9 +474,11 @@ void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewSt } else { if (representationWidth <= 0.0) { XYPOSITION positionsRepr[256]; // Should expand when needed - posCache.MeasureWidths(surface, vstyle, StyleControlChar, ts.representation->stringRep.c_str(), - static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, model.pdoc); - representationWidth = positionsRepr[ts.representation->stringRep.length() - 1] + vstyle.ctrlCharPadding; + surface->MeasureWidthsUTF8(vstyle.styles[StyleControlChar].font.get(), ts.representation->stringRep, positionsRepr); + representationWidth = positionsRepr[ts.representation->stringRep.length() - 1]; + if (FlagSet(ts.representation->appearance, RepresentationAppearance::Blob)) { + representationWidth += vstyle.ctrlCharPadding; + } } } for (int ii = 0; ii < ts.length; ii++) @@ -607,7 +609,7 @@ void EditView::UpdateBidiData(const EditModel &model, const ViewStyle &vstyle, L for (int charsInLine = 0; charsInLine < ll->numCharsInLine; charsInLine++) { const int charWidth = UTF8DrawBytes(reinterpret_cast<unsigned char *>(&ll->chars[charsInLine]), ll->numCharsInLine - charsInLine); - const Representation *repr = model.reprs.RepresentationFromCharacter(&ll->chars[charsInLine], charWidth); + const Representation *repr = model.reprs.RepresentationFromCharacter(std::string_view(&ll->chars[charsInLine], charWidth)); ll->bidiData->widthReprs[charsInLine] = 0.0f; if (repr && ll->chars[charsInLine] != '\t') { @@ -911,7 +913,7 @@ static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle r PRectangle rcChar = rcCChar; rcChar.left++; rcChar.right--; - surface->DrawTextClipped(rcChar, ctrlCharsFont, + surface->DrawTextClippedUTF8(rcChar, ctrlCharsFont, rcSegment.top + vsDraw.maxAscent, text, textBack, textFore); } @@ -1000,29 +1002,41 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on XYPOSITION blobsWidth = 0; if (lastSubLine) { - for (Sci::Position eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) { - rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace; - rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart)+virtualSpace; - blobsWidth += rcSegment.Width(); - char hexits[4] = ""; - const char *ctrlChar; - const unsigned char chEOL = ll->chars[eolPos]; + for (Sci::Position eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine;) { const int styleMain = ll->styles[eolPos]; - const ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos); - if (UTF8IsAscii(chEOL)) { - ctrlChar = ControlCharacterString(chEOL); + const std::optional<ColourRGBA> selectionFore = SelectionForeground(model, vsDraw, eolInSelection); + ColourRGBA textFore = selectionFore.value_or(vsDraw.styles[styleMain].fore); + char hexits[4] = ""; + std::string_view ctrlChar; + Sci::Position widthBytes = 1; + RepresentationAppearance appearance = RepresentationAppearance::Blob; + const Representation *repr = model.reprs.RepresentationFromCharacter(std::string_view(&ll->chars[eolPos], ll->numCharsInLine - eolPos)); + if (repr) { + // Representation of whole text + widthBytes = ll->numCharsInLine - eolPos; + } else { + repr = model.reprs.RepresentationFromCharacter(std::string_view(&ll->chars[eolPos], 1)); + } + if (repr) { + ctrlChar = repr->stringRep; + appearance = repr->appearance; + if (FlagSet(appearance, RepresentationAppearance::Colour)) { + textFore = repr->colour; + } } else { - const Representation *repr = model.reprs.RepresentationFromCharacter(&ll->chars[eolPos], ll->numCharsInLine - eolPos); - if (repr) { - ctrlChar = repr->stringRep.c_str(); - eolPos = ll->numCharsInLine; + const unsigned char chEOL = ll->chars[eolPos]; + if (UTF8IsAscii(chEOL)) { + ctrlChar = ControlCharacterString(chEOL); } else { sprintf(hexits, "x%2X", chEOL); ctrlChar = hexits; } } - const std::optional<ColourRGBA> selectionFore = SelectionForeground(model, vsDraw, eolInSelection); - const ColourRGBA textFore = selectionFore.value_or(vsDraw.styles[styleMain].fore); + + rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace; + rcSegment.right = xStart + ll->positions[eolPos + widthBytes] - static_cast<XYPOSITION>(subLineStart)+virtualSpace; + blobsWidth += rcSegment.Width(); + const ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos); if (eolInSelection && (line < model.pdoc->LinesTotal() - 1)) { if (vsDraw.selection.layer == Layer::Base) { surface->FillRectangleAligned(rcSegment, Fill(selectionBack.Opaque())); @@ -1038,10 +1052,16 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle surface->FillRectangleAligned(rcSegment, selectionBack); blobText = textBack.MixedWith(selectionBack, selectionBack.GetAlphaComponent()); } - DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, blobText, textFore, phasesDraw == PhasesDraw::One); + if (FlagSet(appearance, RepresentationAppearance::Blob)) { + DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, blobText, textFore, phasesDraw == PhasesDraw::One); + } else { + surface->DrawTextTransparentUTF8(rcSegment, vsDraw.styles[StyleControlChar].font.get(), + rcSegment.top + vsDraw.maxAscent, ctrlChar, textFore); + } if (drawEOLSelection && (vsDraw.selection.layer == Layer::OverText)) { surface->FillRectangleAligned(rcSegment, selectionBack); } + eolPos += widthBytes; } } @@ -2019,8 +2039,16 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi rcSegment.top + vsDraw.maxAscent, cc, textBack, textFore); } else { - DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep, - textBack, textFore, phasesDraw == PhasesDraw::One); + if (FlagSet(ts.representation->appearance, RepresentationAppearance::Colour)) { + textFore = ts.representation->colour; + } + if (FlagSet(ts.representation->appearance, RepresentationAppearance::Blob)) { + DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep, + textBack, textFore, phasesDraw == PhasesDraw::One); + } else { + surface->DrawTextTransparentUTF8(rcSegment, vsDraw.styles[StyleControlChar].font.get(), + rcSegment.top + vsDraw.maxAscent, ts.representation->stringRep, textFore); + } } } } else { diff --git a/src/Editor.cxx b/src/Editor.cxx index f59eb5785..6860e153f 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -222,7 +222,7 @@ void Editor::SetRepresentations() { }; for (size_t j=0; j < std::size(reps); j++) { const char c[2] = { static_cast<char>(j), 0 }; - reprs.SetRepresentation(c, reps[j]); + reprs.SetRepresentation(std::string_view(c, 1), reps[j]); } reprs.SetRepresentation("\x7f", "DEL"); @@ -8106,7 +8106,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { case Message::GetRepresentation: { const Representation *repr = reprs.RepresentationFromCharacter( - ConstCharPtrFromUPtr(wParam), UTF8MaxBytes); + ConstCharPtrFromUPtr(wParam)); if (repr) { return StringResult(lParam, repr->stringRep.c_str()); } @@ -8117,6 +8117,35 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { reprs.ClearRepresentation(ConstCharPtrFromUPtr(wParam)); break; + case Message::ClearAllRepresentations: + SetRepresentations(); + break; + + case Message::SetRepresentationAppearance: + reprs.SetRepresentationAppearance(ConstCharPtrFromUPtr(wParam), static_cast<RepresentationAppearance>(lParam)); + break; + + case Message::GetRepresentationAppearance: { + const Representation *repr = reprs.RepresentationFromCharacter( + ConstCharPtrFromUPtr(wParam)); + if (repr) { + return static_cast<sptr_t>(repr->appearance); + } + return 0; + } + case Message::SetRepresentationColour: + reprs.SetRepresentationColour(ConstCharPtrFromUPtr(wParam), ColourRGBA(static_cast<int>(lParam))); + break; + + case Message::GetRepresentationColour: { + const Representation *repr = reprs.RepresentationFromCharacter( + ConstCharPtrFromUPtr(wParam)); + if (repr) { + return repr->colour.AsInteger(); + } + return 0; + } + case Message::StartRecord: recordingMacro = true; return 0; diff --git a/src/PositionCache.cxx b/src/PositionCache.cxx index fb3d8fa01..8123552bd 100644 --- a/src/PositionCache.cxx +++ b/src/PositionCache.cxx @@ -534,10 +534,10 @@ std::shared_ptr<LineLayout> LineLayoutCache::Retrieve(Sci::Line lineNumber, Sci: } // Simply pack the (maximum 4) character bytes into an int -static unsigned int KeyFromString(const char *charBytes, size_t len) noexcept { - PLATFORM_ASSERT(len <= 4); +static unsigned int KeyFromString(std::string_view charBytes) noexcept { + PLATFORM_ASSERT(charBytes.length() <= 4); unsigned int k=0; - for (size_t i=0; i<len && charBytes[i]; i++) { + for (size_t i=0; i < charBytes.length(); i++) { k = k * 0x100; const unsigned char uc = charBytes[i]; k += uc; @@ -545,49 +545,74 @@ static unsigned int KeyFromString(const char *charBytes, size_t len) noexcept { return k; } -SpecialRepresentations::SpecialRepresentations() { - constexpr short none = 0; - std::fill(startByteHasReprs, std::end(startByteHasReprs), none); +void SpecialRepresentations::SetRepresentation(std::string_view charBytes, std::string_view value) { + if (charBytes.length() <= 4) { + const unsigned int key = KeyFromString(charBytes); + MapRepresentation::iterator it = mapReprs.find(key); + if (it == mapReprs.end()) { + // New entry so increment for first byte + const unsigned char ucStart = charBytes.empty() ? 0 : charBytes[0]; + startByteHasReprs[ucStart]++; + } + mapReprs[key] = Representation(value); + } +} + +void SpecialRepresentations::SetRepresentationAppearance(std::string_view charBytes, RepresentationAppearance appearance) { + if (charBytes.length() <= 4) { + const unsigned int key = KeyFromString(charBytes); + MapRepresentation::iterator it = mapReprs.find(key); + if (it == mapReprs.end()) { + // Not present so fail + return; + } + mapReprs[key].appearance = appearance; + } } -void SpecialRepresentations::SetRepresentation(const char *charBytes, const char *value) { - const unsigned int key = KeyFromString(charBytes, UTF8MaxBytes); - MapRepresentation::iterator it = mapReprs.find(key); - if (it == mapReprs.end()) { - // New entry so increment for first byte - const unsigned char ucStart = charBytes[0]; - startByteHasReprs[ucStart]++; +void SpecialRepresentations::SetRepresentationColour(std::string_view charBytes, ColourRGBA colour) { + if (charBytes.length() <= 4) { + const unsigned int key = KeyFromString(charBytes); + MapRepresentation::iterator it = mapReprs.find(key); + if (it == mapReprs.end()) { + // Not present so fail + return; + } + mapReprs[key].appearance = mapReprs[key].appearance | RepresentationAppearance::Colour; + mapReprs[key].colour = colour; } - mapReprs[key] = Representation(value); } -void SpecialRepresentations::ClearRepresentation(const char *charBytes) { - MapRepresentation::iterator it = mapReprs.find(KeyFromString(charBytes, UTF8MaxBytes)); - if (it != mapReprs.end()) { - mapReprs.erase(it); - const unsigned char ucStart = charBytes[0]; - startByteHasReprs[ucStart]--; +void SpecialRepresentations::ClearRepresentation(std::string_view charBytes) { + if (charBytes.length() <= 4) { + MapRepresentation::iterator it = mapReprs.find(KeyFromString(charBytes)); + if (it != mapReprs.end()) { + mapReprs.erase(it); + const unsigned char ucStart = charBytes.empty() ? 0 : charBytes[0]; + startByteHasReprs[ucStart]--; + } } } -const Representation *SpecialRepresentations::RepresentationFromCharacter(const char *charBytes, size_t len) const { - PLATFORM_ASSERT(len <= 4); - const unsigned char ucStart = charBytes[0]; - if (!startByteHasReprs[ucStart]) - return nullptr; - MapRepresentation::const_iterator it = mapReprs.find(KeyFromString(charBytes, len)); - if (it != mapReprs.end()) { - return &(it->second); +const Representation *SpecialRepresentations::RepresentationFromCharacter(std::string_view charBytes) const { + if (charBytes.length() <= 4) { + const unsigned char ucStart = charBytes.empty() ? 0 : charBytes[0]; + if (!startByteHasReprs[ucStart]) + return nullptr; + MapRepresentation::const_iterator it = mapReprs.find(KeyFromString(charBytes)); + if (it != mapReprs.end()) { + return &(it->second); + } } return nullptr; } -bool SpecialRepresentations::Contains(const char *charBytes, size_t len) const { - PLATFORM_ASSERT(len <= 4); - const unsigned char ucStart = charBytes[0]; +bool SpecialRepresentations::Contains(std::string_view charBytes) const { + PLATFORM_ASSERT(charBytes.length() <= 4); + const unsigned char ucStart = charBytes.empty() ? 0 : charBytes[0]; if (!startByteHasReprs[ucStart]) return false; - MapRepresentation::const_iterator it = mapReprs.find(KeyFromString(charBytes, len)); + MapRepresentation::const_iterator it = mapReprs.find(KeyFromString(charBytes)); return it != mapReprs.end(); } @@ -675,7 +700,10 @@ TextSegment BreakFinder::Next() { else if (encodingFamily == EncodingFamily::dbcs) charWidth = pdoc->DBCSDrawBytes( std::string_view(&ll->chars[nextBreak], lineRange.end - nextBreak)); - const Representation *repr = preprs->RepresentationFromCharacter(&ll->chars[nextBreak], charWidth); + // Special case \r\n line ends if there is a representation + if (preprs->Contains("\r\n") && ll->chars[nextBreak] == '\r' && ll->chars[nextBreak + 1] == '\n') + charWidth = 2; + const Representation *repr = preprs->RepresentationFromCharacter(std::string_view(&ll->chars[nextBreak], charWidth)); if (((nextBreak > 0) && (ll->styles[nextBreak] != ll->styles[nextBreak - 1])) || repr || (nextBreak == saeNext)) { diff --git a/src/PositionCache.h b/src/PositionCache.h index 16ac42ef8..b3e87bcd5 100644 --- a/src/PositionCache.h +++ b/src/PositionCache.h @@ -199,7 +199,10 @@ public: class Representation { public: std::string stringRep; - explicit Representation(const char *value="") : stringRep(value) { + RepresentationAppearance appearance; + ColourRGBA colour; + explicit Representation(std::string_view value="", RepresentationAppearance appearance_= RepresentationAppearance::Blob) : + stringRep(value), appearance(appearance_) { } }; @@ -207,13 +210,14 @@ typedef std::map<unsigned int, Representation> MapRepresentation; class SpecialRepresentations { MapRepresentation mapReprs; - short startByteHasReprs[0x100]; + short startByteHasReprs[0x100] {}; public: - SpecialRepresentations(); - void SetRepresentation(const char *charBytes, const char *value); - void ClearRepresentation(const char *charBytes); - const Representation *RepresentationFromCharacter(const char *charBytes, size_t len) const; - bool Contains(const char *charBytes, size_t len) const; + void SetRepresentation(std::string_view charBytes, std::string_view value); + void SetRepresentationAppearance(std::string_view charBytes, RepresentationAppearance appearance); + void SetRepresentationColour(std::string_view charBytes, ColourRGBA colour); + void ClearRepresentation(std::string_view charBytes); + const Representation *RepresentationFromCharacter(std::string_view charBytes) const; + bool Contains(std::string_view charBytes) const; void Clear(); }; diff --git a/test/simpleTests.py b/test/simpleTests.py index 3f607bed6..63a752da4 100644 --- a/test/simpleTests.py +++ b/test/simpleTests.py @@ -1309,6 +1309,31 @@ class TestRepresentations(unittest.TestCase): result = self.ed.GetRepresentation(ohmSign) self.assertEquals(result, ohmExplained) + def testNul(self): + self.ed.SetRepresentation(b"", b"Nul") + result = self.ed.GetRepresentation(b"") + self.assertEquals(result, b"Nul") + + def testAppearance(self): + ohmSign = b"\xe2\x84\xa6" + ohmExplained = b"U+2126 \xe2\x84\xa6" + self.ed.SetRepresentation(ohmSign, ohmExplained) + result = self.ed.GetRepresentationAppearance(ohmSign) + self.assertEquals(result, self.ed.SC_REPRESENTATION_BLOB) + self.ed.SetRepresentationAppearance(ohmSign, self.ed.SC_REPRESENTATION_PLAIN) + result = self.ed.GetRepresentationAppearance(ohmSign) + self.assertEquals(result, self.ed.SC_REPRESENTATION_PLAIN) + + def testColour(self): + ohmSign = b"\xe2\x84\xa6" + ohmExplained = b"U+2126 \xe2\x84\xa6" + self.ed.SetRepresentation(ohmSign, ohmExplained) + result = self.ed.GetRepresentationColour(ohmSign) + self.assertEquals(result, 0) + self.ed.SetRepresentationColour(ohmSign, 0x10203040) + result = self.ed.GetRepresentationColour(ohmSign) + self.assertEquals(result, 0x10203040) + @unittest.skipUnless(lexersAvailable, "no lexers included") class TestProperties(unittest.TestCase): |