123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- // Copyright 2008-2010 Sergiu Dotenco. The License.txt file describes the
- // conditions under which this software may be distributed.
- /**
- * @file LexBibTeX.cxx
- * @brief General BibTeX coloring scheme.
- * @author Sergiu Dotenco
- * @date April 18, 2009
- */
- #include <stdlib.h>
- #include <string.h>
- #include <cassert>
- #include <cctype>
- #include <string>
- #include <algorithm>
- #include <functional>
- #include "ILexer.h"
- #include "Scintilla.h"
- #include "SciLexer.h"
- #include "PropSetSimple.h"
- #include "WordList.h"
- #include "LexAccessor.h"
- #include "Accessor.h"
- #include "StyleContext.h"
- #include "CharacterSet.h"
- #include "LexerModule.h"
- #ifdef SCI_NAMESPACE
- using namespace Scintilla;
- #endif
- namespace {
- bool IsAlphabetic(unsigned int ch)
- {
- return IsASCII(ch) && std::isalpha(ch) != 0;
- }
- bool IsAlphaNumeric(char ch)
- {
- return IsASCII(ch) && std::isalnum(ch);
- }
- bool EqualCaseInsensitive(const char* a, const char* b)
- {
- return CompareCaseInsensitive(a, b) == 0;
- }
- bool EntryWithoutKey(const char* name)
- {
- return EqualCaseInsensitive(name,"string");
- }
- char GetClosingBrace(char openbrace)
- {
- char result = openbrace;
- switch (openbrace) {
- case '(': result = ')'; break;
- case '{': result = '}'; break;
- }
- return result;
- }
- bool IsEntryStart(char prev, char ch)
- {
- return prev != '\\' && ch == '@';
- }
- bool IsEntryStart(const StyleContext& sc)
- {
- return IsEntryStart(sc.chPrev, sc.ch);
- }
- void ColorizeBibTeX(Sci_PositionU start_pos, Sci_Position length, int /*init_style*/, WordList* keywordlists[], Accessor& styler)
- {
- WordList &EntryNames = *keywordlists[0];
- bool fold_compact = styler.GetPropertyInt("fold.compact", 1) != 0;
- std::string buffer;
- buffer.reserve(25);
- // We always colorize a section from the beginning, so let's
- // search for the @ character which isn't escaped, i.e. \@
- while (start_pos > 0 && !IsEntryStart(styler.SafeGetCharAt(start_pos - 1),
- styler.SafeGetCharAt(start_pos))) {
- --start_pos; ++length;
- }
- styler.StartAt(start_pos);
- styler.StartSegment(start_pos);
- Sci_Position current_line = styler.GetLine(start_pos);
- int prev_level = styler.LevelAt(current_line) & SC_FOLDLEVELNUMBERMASK;
- int current_level = prev_level;
- int visible_chars = 0;
- bool in_comment = false ;
- StyleContext sc(start_pos, length, SCE_BIBTEX_DEFAULT, styler);
- bool going = sc.More(); // needed because of a fuzzy end of file state
- char closing_brace = 0;
- bool collect_entry_name = false;
- for (; going; sc.Forward()) {
- if (!sc.More())
- going = false; // we need to go one behind the end of text
- if (in_comment) {
- if (sc.atLineEnd) {
- sc.SetState(SCE_BIBTEX_DEFAULT);
- in_comment = false;
- }
- }
- else {
- // Found @entry
- if (IsEntryStart(sc)) {
- sc.SetState(SCE_BIBTEX_UNKNOWN_ENTRY);
- sc.Forward();
- ++current_level;
- buffer.clear();
- collect_entry_name = true;
- }
- else if ((sc.state == SCE_BIBTEX_ENTRY || sc.state == SCE_BIBTEX_UNKNOWN_ENTRY)
- && (sc.ch == '{' || sc.ch == '(')) {
- // Entry name colorization done
- // Found either a { or a ( after entry's name, e.g. @entry(...) @entry{...}
- // Closing counterpart needs to be stored.
- closing_brace = GetClosingBrace(sc.ch);
- sc.SetState(SCE_BIBTEX_DEFAULT); // Don't colorize { (
- // @string doesn't have any key
- if (EntryWithoutKey(buffer.c_str()))
- sc.ForwardSetState(SCE_BIBTEX_PARAMETER);
- else
- sc.ForwardSetState(SCE_BIBTEX_KEY); // Key/label colorization
- }
- // Need to handle the case where entry's key is empty
- // e.g. @book{,...}
- if (sc.state == SCE_BIBTEX_KEY && sc.ch == ',') {
- // Key/label colorization done
- sc.SetState(SCE_BIBTEX_DEFAULT); // Don't colorize the ,
- sc.ForwardSetState(SCE_BIBTEX_PARAMETER); // Parameter colorization
- }
- else if (sc.state == SCE_BIBTEX_PARAMETER && sc.ch == '=') {
- sc.SetState(SCE_BIBTEX_DEFAULT); // Don't colorize the =
- sc.ForwardSetState(SCE_BIBTEX_VALUE); // Parameter value colorization
- Sci_Position start = sc.currentPos;
- // We need to handle multiple situations:
- // 1. name"one two {three}"
- // 2. name={one {one two {two}} three}
- // 3. year=2005
- // Skip ", { until we encounter the first alphanumerical character
- while (sc.More() && !(IsAlphaNumeric(sc.ch) || sc.ch == '"' || sc.ch == '{'))
- sc.Forward();
- if (sc.More()) {
- // Store " or {
- char ch = sc.ch;
- // Not interested in alphanumerical characters
- if (IsAlphaNumeric(ch))
- ch = 0;
- int skipped = 0;
- if (ch) {
- // Skip preceding " or { such as in name={{test}}.
- // Remember how many characters have been skipped
- // Make sure that empty values, i.e. "" are also handled correctly
- while (sc.More() && (sc.ch == ch && (ch != '"' || skipped < 1))) {
- sc.Forward();
- ++skipped;
- }
- }
- // Closing counterpart for " is the same character
- if (ch == '{')
- ch = '}';
- // We have reached the parameter value
- // In case the open character was a alnum char, skip until , is found
- // otherwise until skipped == 0
- while (sc.More() && (skipped > 0 || (!ch && !(sc.ch == ',' || sc.ch == closing_brace)))) {
- // Make sure the character isn't escaped
- if (sc.chPrev != '\\') {
- // Parameter value contains a { which is the 2nd case described above
- if (sc.ch == '{')
- ++skipped; // Remember it
- else if (sc.ch == '}')
- --skipped;
- else if (skipped == 1 && sc.ch == ch && ch == '"') // Don't ignore cases like {"o}
- skipped = 0;
- }
- sc.Forward();
- }
- }
- // Don't colorize the ,
- sc.SetState(SCE_BIBTEX_DEFAULT);
- // Skip until the , or entry's closing closing_brace is found
- // since this parameter might be the last one
- while (sc.More() && !(sc.ch == ',' || sc.ch == closing_brace))
- sc.Forward();
- int state = SCE_BIBTEX_PARAMETER; // The might be more parameters
- // We've reached the closing closing_brace for the bib entry
- // in case no " or {} has been used to enclose the value,
- // as in 3rd case described above
- if (sc.ch == closing_brace) {
- --current_level;
- // Make sure the text between entries is not colored
- // using parameter's style
- state = SCE_BIBTEX_DEFAULT;
- }
- Sci_Position end = sc.currentPos;
- current_line = styler.GetLine(end);
- // We have possibly skipped some lines, so the folding levels
- // have to be adjusted separately
- for (Sci_Position i = styler.GetLine(start); i <= styler.GetLine(end); ++i)
- styler.SetLevel(i, prev_level);
- sc.ForwardSetState(state);
- }
- if (sc.state == SCE_BIBTEX_PARAMETER && sc.ch == closing_brace) {
- sc.SetState(SCE_BIBTEX_DEFAULT);
- --current_level;
- }
- // Non escaped % found which represents a comment until the end of the line
- if (sc.chPrev != '\\' && sc.ch == '%') {
- in_comment = true;
- sc.SetState(SCE_BIBTEX_COMMENT);
- }
- }
- if (sc.state == SCE_BIBTEX_UNKNOWN_ENTRY || sc.state == SCE_BIBTEX_ENTRY) {
- if (!IsAlphabetic(sc.ch) && collect_entry_name)
- collect_entry_name = false;
- if (collect_entry_name) {
- buffer += static_cast<char>(tolower(sc.ch));
- if (EntryNames.InList(buffer.c_str()))
- sc.ChangeState(SCE_BIBTEX_ENTRY);
- else
- sc.ChangeState(SCE_BIBTEX_UNKNOWN_ENTRY);
- }
- }
- if (sc.atLineEnd) {
- int level = prev_level;
- if (visible_chars == 0 && fold_compact)
- level |= SC_FOLDLEVELWHITEFLAG;
- if ((current_level > prev_level))
- level |= SC_FOLDLEVELHEADERFLAG;
- // else if (current_level < prev_level)
- // level |= SC_FOLDLEVELBOXFOOTERFLAG; // Deprecated
- if (level != styler.LevelAt(current_line)) {
- styler.SetLevel(current_line, level);
- }
- ++current_line;
- prev_level = current_level;
- visible_chars = 0;
- }
- if (!isspacechar(sc.ch))
- ++visible_chars;
- }
- sc.Complete();
- // Fill in the real level of the next line, keeping the current flags as they will be filled in later
- int flagsNext = styler.LevelAt(current_line) & ~SC_FOLDLEVELNUMBERMASK;
- styler.SetLevel(current_line, prev_level | flagsNext);
- }
- }
- static const char * const BibTeXWordLists[] = {
- "Entry Names",
- 0,
- };
- LexerModule lmBibTeX(SCLEX_BIBTEX, ColorizeBibTeX, "bib", 0, BibTeXWordLists);
- // Entry Names
- // article, book, booklet, conference, inbook,
- // incollection, inproceedings, manual, mastersthesis,
- // misc, phdthesis, proceedings, techreport, unpublished,
- // string, url
|