You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
6.5 KiB
214 lines
6.5 KiB
// Scintilla source code edit control |
|
/** @file LexPO.cxx |
|
** Lexer for GetText Translation (PO) files. |
|
**/ |
|
// Copyright 2012 by Colomban Wendling <ban@herbesfolles.org> |
|
// The License.txt file describes the conditions under which this software may be distributed. |
|
|
|
// see https://www.gnu.org/software/gettext/manual/gettext.html#PO-Files for the syntax reference |
|
// some details are taken from the GNU msgfmt behavior (like that indent is allows in front of lines) |
|
|
|
// TODO: |
|
// * add keywords for flags (fuzzy, c-format, ...) |
|
// * highlight formats inside c-format strings (%s, %d, etc.) |
|
// * style for previous untranslated string? ("#|" comment) |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdio.h> |
|
#include <stdarg.h> |
|
#include <assert.h> |
|
#include <ctype.h> |
|
|
|
#include <string> |
|
#include <string_view> |
|
|
|
#include "ILexer.h" |
|
#include "Scintilla.h" |
|
#include "SciLexer.h" |
|
|
|
#include "WordList.h" |
|
#include "LexAccessor.h" |
|
#include "Accessor.h" |
|
#include "StyleContext.h" |
|
#include "CharacterSet.h" |
|
#include "LexerModule.h" |
|
|
|
using namespace Lexilla; |
|
|
|
static void ColourisePODoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], Accessor &styler) { |
|
StyleContext sc(startPos, length, initStyle, styler); |
|
bool escaped = false; |
|
Sci_Position curLine = styler.GetLine(startPos); |
|
// the line state holds the last state on or before the line that isn't the default style |
|
int curLineState = curLine > 0 ? styler.GetLineState(curLine - 1) : SCE_PO_DEFAULT; |
|
|
|
for (; sc.More(); sc.Forward()) { |
|
// whether we should leave a state |
|
switch (sc.state) { |
|
case SCE_PO_COMMENT: |
|
case SCE_PO_PROGRAMMER_COMMENT: |
|
case SCE_PO_REFERENCE: |
|
case SCE_PO_FLAGS: |
|
case SCE_PO_FUZZY: |
|
if (sc.atLineEnd) |
|
sc.SetState(SCE_PO_DEFAULT); |
|
else if (sc.state == SCE_PO_FLAGS && sc.Match("fuzzy")) |
|
// here we behave like the previous parser, but this should probably be highlighted |
|
// on its own like a keyword rather than changing the whole flags style |
|
sc.ChangeState(SCE_PO_FUZZY); |
|
break; |
|
|
|
case SCE_PO_MSGCTXT: |
|
case SCE_PO_MSGID: |
|
case SCE_PO_MSGSTR: |
|
if (isspacechar(sc.ch)) |
|
sc.SetState(SCE_PO_DEFAULT); |
|
break; |
|
|
|
case SCE_PO_ERROR: |
|
if (sc.atLineEnd) |
|
sc.SetState(SCE_PO_DEFAULT); |
|
break; |
|
|
|
case SCE_PO_MSGCTXT_TEXT: |
|
case SCE_PO_MSGID_TEXT: |
|
case SCE_PO_MSGSTR_TEXT: |
|
if (sc.atLineEnd) { // invalid inside a string |
|
if (sc.state == SCE_PO_MSGCTXT_TEXT) |
|
sc.ChangeState(SCE_PO_MSGCTXT_TEXT_EOL); |
|
else if (sc.state == SCE_PO_MSGID_TEXT) |
|
sc.ChangeState(SCE_PO_MSGID_TEXT_EOL); |
|
else if (sc.state == SCE_PO_MSGSTR_TEXT) |
|
sc.ChangeState(SCE_PO_MSGSTR_TEXT_EOL); |
|
sc.SetState(SCE_PO_DEFAULT); |
|
escaped = false; |
|
} else { |
|
if (escaped) |
|
escaped = false; |
|
else if (sc.ch == '\\') |
|
escaped = true; |
|
else if (sc.ch == '"') |
|
sc.ForwardSetState(SCE_PO_DEFAULT); |
|
} |
|
break; |
|
} |
|
|
|
// whether we should enter a new state |
|
if (sc.state == SCE_PO_DEFAULT) { |
|
// forward to the first non-white character on the line |
|
bool atLineStart = sc.atLineStart; |
|
if (atLineStart) { |
|
// reset line state if it is set to comment state so empty lines don't get |
|
// comment line state, and the folding code folds comments separately, |
|
// and anyway the styling don't use line state for comments |
|
if (curLineState == SCE_PO_COMMENT) |
|
curLineState = SCE_PO_DEFAULT; |
|
|
|
while (sc.More() && ! sc.atLineEnd && isspacechar(sc.ch)) |
|
sc.Forward(); |
|
} |
|
|
|
if (atLineStart && sc.ch == '#') { |
|
if (sc.chNext == '.') |
|
sc.SetState(SCE_PO_PROGRAMMER_COMMENT); |
|
else if (sc.chNext == ':') |
|
sc.SetState(SCE_PO_REFERENCE); |
|
else if (sc.chNext == ',') |
|
sc.SetState(SCE_PO_FLAGS); |
|
else |
|
sc.SetState(SCE_PO_COMMENT); |
|
} else if (atLineStart && sc.Match("msgid")) { // includes msgid_plural |
|
sc.SetState(SCE_PO_MSGID); |
|
} else if (atLineStart && sc.Match("msgstr")) { // includes [] suffixes |
|
sc.SetState(SCE_PO_MSGSTR); |
|
} else if (atLineStart && sc.Match("msgctxt")) { |
|
sc.SetState(SCE_PO_MSGCTXT); |
|
} else if (sc.ch == '"') { |
|
if (curLineState == SCE_PO_MSGCTXT || curLineState == SCE_PO_MSGCTXT_TEXT) |
|
sc.SetState(SCE_PO_MSGCTXT_TEXT); |
|
else if (curLineState == SCE_PO_MSGID || curLineState == SCE_PO_MSGID_TEXT) |
|
sc.SetState(SCE_PO_MSGID_TEXT); |
|
else if (curLineState == SCE_PO_MSGSTR || curLineState == SCE_PO_MSGSTR_TEXT) |
|
sc.SetState(SCE_PO_MSGSTR_TEXT); |
|
else |
|
sc.SetState(SCE_PO_ERROR); |
|
} else if (! isspacechar(sc.ch)) |
|
sc.SetState(SCE_PO_ERROR); |
|
|
|
if (sc.state != SCE_PO_DEFAULT) |
|
curLineState = sc.state; |
|
} |
|
|
|
if (sc.atLineEnd) { |
|
// Update the line state, so it can be seen by next line |
|
curLine = styler.GetLine(sc.currentPos); |
|
styler.SetLineState(curLine, curLineState); |
|
} |
|
} |
|
sc.Complete(); |
|
} |
|
|
|
static int FindNextNonEmptyLineState(Sci_PositionU startPos, Accessor &styler) { |
|
Sci_PositionU length = styler.Length(); |
|
for (Sci_PositionU i = startPos; i < length; i++) { |
|
if (! isspacechar(styler[i])) { |
|
return styler.GetLineState(styler.GetLine(i)); |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
static void FoldPODoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) { |
|
if (! styler.GetPropertyInt("fold")) |
|
return; |
|
bool foldCompact = styler.GetPropertyInt("fold.compact") != 0; |
|
bool foldComment = styler.GetPropertyInt("fold.comment") != 0; |
|
|
|
Sci_PositionU endPos = startPos + length; |
|
Sci_Position curLine = styler.GetLine(startPos); |
|
int lineState = styler.GetLineState(curLine); |
|
int nextLineState; |
|
int level = styler.LevelAt(curLine) & SC_FOLDLEVELNUMBERMASK; |
|
int nextLevel; |
|
int visible = 0; |
|
int chNext = styler[startPos]; |
|
|
|
for (Sci_PositionU i = startPos; i < endPos; i++) { |
|
int ch = chNext; |
|
chNext = styler.SafeGetCharAt(i+1); |
|
|
|
if (! isspacechar(ch)) { |
|
visible++; |
|
} else if ((ch == '\r' && chNext != '\n') || ch == '\n' || i+1 >= endPos) { |
|
int lvl = level; |
|
Sci_Position nextLine = curLine + 1; |
|
|
|
nextLineState = styler.GetLineState(nextLine); |
|
if ((lineState != SCE_PO_COMMENT || foldComment) && |
|
nextLineState == lineState && |
|
FindNextNonEmptyLineState(i, styler) == lineState) |
|
nextLevel = SC_FOLDLEVELBASE + 1; |
|
else |
|
nextLevel = SC_FOLDLEVELBASE; |
|
|
|
if (nextLevel > level) |
|
lvl |= SC_FOLDLEVELHEADERFLAG; |
|
if (visible == 0 && foldCompact) |
|
lvl |= SC_FOLDLEVELWHITEFLAG; |
|
|
|
styler.SetLevel(curLine, lvl); |
|
|
|
lineState = nextLineState; |
|
curLine = nextLine; |
|
level = nextLevel; |
|
visible = 0; |
|
} |
|
} |
|
} |
|
|
|
static const char *const poWordListDesc[] = { |
|
0 |
|
}; |
|
|
|
LexerModule lmPO(SCLEX_PO, ColourisePODoc, "po", FoldPODoc, poWordListDesc);
|
|
|