1 // Copyright 2008-2010 Sergiu Dotenco. The License.txt file describes the
2 // conditions under which this software may be distributed.
6 * @brief General BibTeX coloring scheme.
7 * @author Sergiu Dotenco
22 #include "Scintilla.h"
25 #include "PropSetSimple.h"
27 #include "LexAccessor.h"
29 #include "StyleContext.h"
30 #include "CharacterSet.h"
31 #include "LexerModule.h"
33 using namespace Scintilla
;
36 bool IsAlphabetic(unsigned int ch
)
38 return IsASCII(ch
) && std::isalpha(ch
) != 0;
40 bool IsAlphaNumeric(char ch
)
42 return IsASCII(ch
) && std::isalnum(ch
);
45 bool EqualCaseInsensitive(const char* a
, const char* b
)
47 return CompareCaseInsensitive(a
, b
) == 0;
50 bool EntryWithoutKey(const char* name
)
52 return EqualCaseInsensitive(name
,"string");
55 char GetClosingBrace(char openbrace
)
57 char result
= openbrace
;
60 case '(': result
= ')'; break;
61 case '{': result
= '}'; break;
67 bool IsEntryStart(char prev
, char ch
)
69 return prev
!= '\\' && ch
== '@';
72 bool IsEntryStart(const StyleContext
& sc
)
74 return IsEntryStart(sc
.chPrev
, sc
.ch
);
77 void ColorizeBibTeX(Sci_PositionU start_pos
, Sci_Position length
, int /*init_style*/, WordList
* keywordlists
[], Accessor
& styler
)
79 WordList
&EntryNames
= *keywordlists
[0];
80 bool fold_compact
= styler
.GetPropertyInt("fold.compact", 1) != 0;
85 // We always colorize a section from the beginning, so let's
86 // search for the @ character which isn't escaped, i.e. \@
87 while (start_pos
> 0 && !IsEntryStart(styler
.SafeGetCharAt(start_pos
- 1),
88 styler
.SafeGetCharAt(start_pos
))) {
89 --start_pos
; ++length
;
92 styler
.StartAt(start_pos
);
93 styler
.StartSegment(start_pos
);
95 Sci_Position current_line
= styler
.GetLine(start_pos
);
96 int prev_level
= styler
.LevelAt(current_line
) & SC_FOLDLEVELNUMBERMASK
;
97 int current_level
= prev_level
;
98 int visible_chars
= 0;
100 bool in_comment
= false ;
101 StyleContext
sc(start_pos
, length
, SCE_BIBTEX_DEFAULT
, styler
);
103 bool going
= sc
.More(); // needed because of a fuzzy end of file state
104 char closing_brace
= 0;
105 bool collect_entry_name
= false;
107 for (; going
; sc
.Forward()) {
109 going
= false; // we need to go one behind the end of text
113 sc
.SetState(SCE_BIBTEX_DEFAULT
);
119 if (IsEntryStart(sc
)) {
120 sc
.SetState(SCE_BIBTEX_UNKNOWN_ENTRY
);
125 collect_entry_name
= true;
127 else if ((sc
.state
== SCE_BIBTEX_ENTRY
|| sc
.state
== SCE_BIBTEX_UNKNOWN_ENTRY
)
128 && (sc
.ch
== '{' || sc
.ch
== '(')) {
129 // Entry name colorization done
130 // Found either a { or a ( after entry's name, e.g. @entry(...) @entry{...}
131 // Closing counterpart needs to be stored.
132 closing_brace
= GetClosingBrace(sc
.ch
);
134 sc
.SetState(SCE_BIBTEX_DEFAULT
); // Don't colorize { (
136 // @string doesn't have any key
137 if (EntryWithoutKey(buffer
.c_str()))
138 sc
.ForwardSetState(SCE_BIBTEX_PARAMETER
);
140 sc
.ForwardSetState(SCE_BIBTEX_KEY
); // Key/label colorization
143 // Need to handle the case where entry's key is empty
145 if (sc
.state
== SCE_BIBTEX_KEY
&& sc
.ch
== ',') {
146 // Key/label colorization done
147 sc
.SetState(SCE_BIBTEX_DEFAULT
); // Don't colorize the ,
148 sc
.ForwardSetState(SCE_BIBTEX_PARAMETER
); // Parameter colorization
150 else if (sc
.state
== SCE_BIBTEX_PARAMETER
&& sc
.ch
== '=') {
151 sc
.SetState(SCE_BIBTEX_DEFAULT
); // Don't colorize the =
152 sc
.ForwardSetState(SCE_BIBTEX_VALUE
); // Parameter value colorization
154 Sci_Position start
= sc
.currentPos
;
156 // We need to handle multiple situations:
157 // 1. name"one two {three}"
158 // 2. name={one {one two {two}} three}
161 // Skip ", { until we encounter the first alphanumerical character
162 while (sc
.More() && !(IsAlphaNumeric(sc
.ch
) || sc
.ch
== '"' || sc
.ch
== '{'))
169 // Not interested in alphanumerical characters
170 if (IsAlphaNumeric(ch
))
176 // Skip preceding " or { such as in name={{test}}.
177 // Remember how many characters have been skipped
178 // Make sure that empty values, i.e. "" are also handled correctly
179 while (sc
.More() && (sc
.ch
== ch
&& (ch
!= '"' || skipped
< 1))) {
185 // Closing counterpart for " is the same character
189 // We have reached the parameter value
190 // In case the open character was a alnum char, skip until , is found
191 // otherwise until skipped == 0
192 while (sc
.More() && (skipped
> 0 || (!ch
&& !(sc
.ch
== ',' || sc
.ch
== closing_brace
)))) {
193 // Make sure the character isn't escaped
194 if (sc
.chPrev
!= '\\') {
195 // Parameter value contains a { which is the 2nd case described above
197 ++skipped
; // Remember it
198 else if (sc
.ch
== '}')
200 else if (skipped
== 1 && sc
.ch
== ch
&& ch
== '"') // Don't ignore cases like {"o}
208 // Don't colorize the ,
209 sc
.SetState(SCE_BIBTEX_DEFAULT
);
211 // Skip until the , or entry's closing closing_brace is found
212 // since this parameter might be the last one
213 while (sc
.More() && !(sc
.ch
== ',' || sc
.ch
== closing_brace
))
216 int state
= SCE_BIBTEX_PARAMETER
; // The might be more parameters
218 // We've reached the closing closing_brace for the bib entry
219 // in case no " or {} has been used to enclose the value,
220 // as in 3rd case described above
221 if (sc
.ch
== closing_brace
) {
223 // Make sure the text between entries is not colored
224 // using parameter's style
225 state
= SCE_BIBTEX_DEFAULT
;
228 Sci_Position end
= sc
.currentPos
;
229 current_line
= styler
.GetLine(end
);
231 // We have possibly skipped some lines, so the folding levels
232 // have to be adjusted separately
233 for (Sci_Position i
= styler
.GetLine(start
); i
<= styler
.GetLine(end
); ++i
)
234 styler
.SetLevel(i
, prev_level
);
236 sc
.ForwardSetState(state
);
239 if (sc
.state
== SCE_BIBTEX_PARAMETER
&& sc
.ch
== closing_brace
) {
240 sc
.SetState(SCE_BIBTEX_DEFAULT
);
244 // Non escaped % found which represents a comment until the end of the line
245 if (sc
.chPrev
!= '\\' && sc
.ch
== '%') {
247 sc
.SetState(SCE_BIBTEX_COMMENT
);
251 if (sc
.state
== SCE_BIBTEX_UNKNOWN_ENTRY
|| sc
.state
== SCE_BIBTEX_ENTRY
) {
252 if (!IsAlphabetic(sc
.ch
) && collect_entry_name
)
253 collect_entry_name
= false;
255 if (collect_entry_name
) {
256 buffer
+= static_cast<char>(tolower(sc
.ch
));
257 if (EntryNames
.InList(buffer
.c_str()))
258 sc
.ChangeState(SCE_BIBTEX_ENTRY
);
260 sc
.ChangeState(SCE_BIBTEX_UNKNOWN_ENTRY
);
265 int level
= prev_level
;
267 if (visible_chars
== 0 && fold_compact
)
268 level
|= SC_FOLDLEVELWHITEFLAG
;
270 if ((current_level
> prev_level
))
271 level
|= SC_FOLDLEVELHEADERFLAG
;
272 // else if (current_level < prev_level)
273 // level |= SC_FOLDLEVELBOXFOOTERFLAG; // Deprecated
275 if (level
!= styler
.LevelAt(current_line
)) {
276 styler
.SetLevel(current_line
, level
);
280 prev_level
= current_level
;
284 if (!isspacechar(sc
.ch
))
290 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
291 int flagsNext
= styler
.LevelAt(current_line
) & ~SC_FOLDLEVELNUMBERMASK
;
292 styler
.SetLevel(current_line
, prev_level
| flagsNext
);
295 static const char * const BibTeXWordLists
[] = {
301 LexerModule
lmBibTeX(SCLEX_BIBTEX
, ColorizeBibTeX
, "bib", 0, BibTeXWordLists
);
304 // article, book, booklet, conference, inbook,
305 // incollection, inproceedings, manual, mastersthesis,
306 // misc, phdthesis, proceedings, techreport, unpublished,