1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
|
// 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);
|