1 | // Scintilla source code edit control |
2 | /** @file SubStyles.h |
3 | ** Manage substyles for a lexer. |
4 | **/ |
5 | // Copyright 2012 by Neil Hodgson <neilh@scintilla.org> |
6 | // The License.txt file describes the conditions under which this software may be distributed. |
7 | |
8 | #ifndef SUBSTYLES_H |
9 | #define SUBSTYLES_H |
10 | |
11 | namespace Lexilla { |
12 | |
13 | class WordClassifier { |
14 | int baseStyle; |
15 | int firstStyle; |
16 | int lenStyles; |
17 | using WordStyleMap = std::map<std::string, int, std::less<>>; |
18 | WordStyleMap wordToStyle; |
19 | |
20 | public: |
21 | |
22 | explicit WordClassifier(int baseStyle_) : baseStyle(baseStyle_), firstStyle(0), lenStyles(0) { |
23 | } |
24 | |
25 | void Allocate(int firstStyle_, int lenStyles_) { |
26 | firstStyle = firstStyle_; |
27 | lenStyles = lenStyles_; |
28 | wordToStyle.clear(); |
29 | } |
30 | |
31 | int Base() const noexcept { |
32 | return baseStyle; |
33 | } |
34 | |
35 | int Start() const noexcept { |
36 | return firstStyle; |
37 | } |
38 | |
39 | int Last() const noexcept { |
40 | return firstStyle + lenStyles - 1; |
41 | } |
42 | |
43 | int Length() const noexcept { |
44 | return lenStyles; |
45 | } |
46 | |
47 | void Clear() noexcept { |
48 | firstStyle = 0; |
49 | lenStyles = 0; |
50 | wordToStyle.clear(); |
51 | } |
52 | |
53 | int ValueFor(std::string_view s) const { |
54 | WordStyleMap::const_iterator it = wordToStyle.find(s); |
55 | if (it != wordToStyle.end()) |
56 | return it->second; |
57 | else |
58 | return -1; |
59 | } |
60 | |
61 | bool IncludesStyle(int style) const noexcept { |
62 | return (style >= firstStyle) && (style < (firstStyle + lenStyles)); |
63 | } |
64 | |
65 | void RemoveStyle(int style) noexcept { |
66 | WordStyleMap::iterator it = wordToStyle.begin(); |
67 | while (it != wordToStyle.end()) { |
68 | if (it->second == style) { |
69 | it = wordToStyle.erase(it); |
70 | } else { |
71 | ++it; |
72 | } |
73 | } |
74 | } |
75 | |
76 | void SetIdentifiers(int style, const char *identifiers) { |
77 | RemoveStyle(style); |
78 | if (!identifiers) |
79 | return; |
80 | while (*identifiers) { |
81 | const char *cpSpace = identifiers; |
82 | while (*cpSpace && !(*cpSpace == ' ' || *cpSpace == '\t' || *cpSpace == '\r' || *cpSpace == '\n')) |
83 | cpSpace++; |
84 | if (cpSpace > identifiers) { |
85 | std::string word(identifiers, cpSpace - identifiers); |
86 | wordToStyle[word] = style; |
87 | } |
88 | identifiers = cpSpace; |
89 | if (*identifiers) |
90 | identifiers++; |
91 | } |
92 | } |
93 | }; |
94 | |
95 | class SubStyles { |
96 | int classifications; |
97 | const char *baseStyles; |
98 | int styleFirst; |
99 | int stylesAvailable; |
100 | int secondaryDistance; |
101 | int allocated; |
102 | std::vector<WordClassifier> classifiers; |
103 | |
104 | int BlockFromBaseStyle(int baseStyle) const noexcept { |
105 | for (int b=0; b < classifications; b++) { |
106 | if (baseStyle == baseStyles[b]) |
107 | return b; |
108 | } |
109 | return -1; |
110 | } |
111 | |
112 | int BlockFromStyle(int style) const noexcept { |
113 | int b = 0; |
114 | for (const WordClassifier &wc : classifiers) { |
115 | if (wc.IncludesStyle(style)) |
116 | return b; |
117 | b++; |
118 | } |
119 | return -1; |
120 | } |
121 | |
122 | public: |
123 | |
124 | SubStyles(const char *baseStyles_, int styleFirst_, int stylesAvailable_, int secondaryDistance_) : |
125 | classifications(0), |
126 | baseStyles(baseStyles_), |
127 | styleFirst(styleFirst_), |
128 | stylesAvailable(stylesAvailable_), |
129 | secondaryDistance(secondaryDistance_), |
130 | allocated(0) { |
131 | while (baseStyles[classifications]) { |
132 | classifiers.push_back(WordClassifier(baseStyles[classifications])); |
133 | classifications++; |
134 | } |
135 | } |
136 | |
137 | int Allocate(int styleBase, int numberStyles) { |
138 | const int block = BlockFromBaseStyle(styleBase); |
139 | if (block >= 0) { |
140 | if ((allocated + numberStyles) > stylesAvailable) |
141 | return -1; |
142 | const int startBlock = styleFirst + allocated; |
143 | allocated += numberStyles; |
144 | classifiers[block].Allocate(startBlock, numberStyles); |
145 | return startBlock; |
146 | } else { |
147 | return -1; |
148 | } |
149 | } |
150 | |
151 | int Start(int styleBase) noexcept { |
152 | const int block = BlockFromBaseStyle(styleBase); |
153 | return (block >= 0) ? classifiers[block].Start() : -1; |
154 | } |
155 | |
156 | int Length(int styleBase) noexcept { |
157 | const int block = BlockFromBaseStyle(styleBase); |
158 | return (block >= 0) ? classifiers[block].Length() : 0; |
159 | } |
160 | |
161 | int BaseStyle(int subStyle) const noexcept { |
162 | const int block = BlockFromStyle(subStyle); |
163 | if (block >= 0) |
164 | return classifiers[block].Base(); |
165 | else |
166 | return subStyle; |
167 | } |
168 | |
169 | int DistanceToSecondaryStyles() const noexcept { |
170 | return secondaryDistance; |
171 | } |
172 | |
173 | int FirstAllocated() const noexcept { |
174 | int start = 257; |
175 | for (const WordClassifier &wc : classifiers) { |
176 | if ((wc.Length() > 0) && (start > wc.Start())) |
177 | start = wc.Start(); |
178 | } |
179 | return (start < 256) ? start : -1; |
180 | } |
181 | |
182 | int LastAllocated() const noexcept { |
183 | int last = -1; |
184 | for (const WordClassifier &wc : classifiers) { |
185 | if ((wc.Length() > 0) && (last < wc.Last())) |
186 | last = wc.Last(); |
187 | } |
188 | return last; |
189 | } |
190 | |
191 | void SetIdentifiers(int style, const char *identifiers) { |
192 | const int block = BlockFromStyle(style); |
193 | if (block >= 0) |
194 | classifiers[block].SetIdentifiers(style, identifiers); |
195 | } |
196 | |
197 | void Free() noexcept { |
198 | allocated = 0; |
199 | for (WordClassifier &wc : classifiers) { |
200 | wc.Clear(); |
201 | } |
202 | } |
203 | |
204 | const WordClassifier &Classifier(int baseStyle) const noexcept { |
205 | const int block = BlockFromBaseStyle(baseStyle); |
206 | return classifiers[block >= 0 ? block : 0]; |
207 | } |
208 | }; |
209 | |
210 | } |
211 | |
212 | #endif |
213 | |