1 // Scintilla source code edit control
3 ** Lexer for GetText Translation (PO) files.
5 // Copyright 2012 by Colomban Wendling <ban@herbesfolles.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 // see https://www.gnu.org/software/gettext/manual/gettext.html#PO-Files for the syntax reference
9 // some details are taken from the GNU msgfmt behavior (like that indent is allows in front of lines)
12 // * add keywords for flags (fuzzy, c-format, ...)
13 // * highlight formats inside c-format strings (%s, %d, etc.)
14 // * style for previous untranslated string? ("#|" comment)
24 #include "Scintilla.h"
28 #include "LexAccessor.h"
30 #include "StyleContext.h"
31 #include "CharacterSet.h"
32 #include "LexerModule.h"
35 using namespace Scintilla
;
38 static void ColourisePODoc(unsigned int startPos
, int length
, int initStyle
, WordList
*[], Accessor
&styler
) {
39 StyleContext
sc(startPos
, length
, initStyle
, styler
);
41 int curLine
= styler
.GetLine(startPos
);
42 // the line state holds the last state on or before the line that isn't the default style
43 int curLineState
= curLine
> 0 ? styler
.GetLineState(curLine
- 1) : SCE_PO_DEFAULT
;
45 for (; sc
.More(); sc
.Forward()) {
46 // whether we should leave a state
49 case SCE_PO_PROGRAMMER_COMMENT
:
50 case SCE_PO_REFERENCE
:
54 sc
.SetState(SCE_PO_DEFAULT
);
55 else if (sc
.state
== SCE_PO_FLAGS
&& sc
.Match("fuzzy"))
56 // here we behave like the previous parser, but this should probably be highlighted
57 // on its own like a keyword rather than changing the whole flags style
58 sc
.ChangeState(SCE_PO_FUZZY
);
64 if (isspacechar(sc
.ch
))
65 sc
.SetState(SCE_PO_DEFAULT
);
70 sc
.SetState(SCE_PO_DEFAULT
);
73 case SCE_PO_MSGCTXT_TEXT
:
74 case SCE_PO_MSGID_TEXT
:
75 case SCE_PO_MSGSTR_TEXT
:
76 if (sc
.atLineEnd
) { // invalid inside a string
77 if (sc
.state
== SCE_PO_MSGCTXT_TEXT
)
78 sc
.ChangeState(SCE_PO_MSGCTXT_TEXT_EOL
);
79 else if (sc
.state
== SCE_PO_MSGID_TEXT
)
80 sc
.ChangeState(SCE_PO_MSGID_TEXT_EOL
);
81 else if (sc
.state
== SCE_PO_MSGSTR_TEXT
)
82 sc
.ChangeState(SCE_PO_MSGSTR_TEXT_EOL
);
83 sc
.SetState(SCE_PO_DEFAULT
);
88 else if (sc
.ch
== '\\')
90 else if (sc
.ch
== '"')
91 sc
.ForwardSetState(SCE_PO_DEFAULT
);
96 // whether we should enter a new state
97 if (sc
.state
== SCE_PO_DEFAULT
) {
98 // forward to the first non-white character on the line
99 bool atLineStart
= sc
.atLineStart
;
101 while (sc
.More() && ! sc
.atLineEnd
&& isspacechar(sc
.ch
))
105 if (atLineStart
&& sc
.ch
== '#') {
106 if (sc
.chNext
== '.')
107 sc
.SetState(SCE_PO_PROGRAMMER_COMMENT
);
108 else if (sc
.chNext
== ':')
109 sc
.SetState(SCE_PO_REFERENCE
);
110 else if (sc
.chNext
== ',')
111 sc
.SetState(SCE_PO_FLAGS
);
113 sc
.SetState(SCE_PO_COMMENT
);
114 } else if (atLineStart
&& sc
.Match("msgid")) { // includes msgid_plural
115 sc
.SetState(SCE_PO_MSGID
);
116 } else if (atLineStart
&& sc
.Match("msgstr")) { // includes [] suffixes
117 sc
.SetState(SCE_PO_MSGSTR
);
118 } else if (atLineStart
&& sc
.Match("msgctxt")) {
119 sc
.SetState(SCE_PO_MSGCTXT
);
120 } else if (sc
.ch
== '"') {
121 if (curLineState
== SCE_PO_MSGCTXT
|| curLineState
== SCE_PO_MSGCTXT_TEXT
)
122 sc
.SetState(SCE_PO_MSGCTXT_TEXT
);
123 else if (curLineState
== SCE_PO_MSGID
|| curLineState
== SCE_PO_MSGID_TEXT
)
124 sc
.SetState(SCE_PO_MSGID_TEXT
);
125 else if (curLineState
== SCE_PO_MSGSTR
|| curLineState
== SCE_PO_MSGSTR_TEXT
)
126 sc
.SetState(SCE_PO_MSGSTR_TEXT
);
128 sc
.SetState(SCE_PO_ERROR
);
129 } else if (! isspacechar(sc
.ch
))
130 sc
.SetState(SCE_PO_ERROR
);
132 if (sc
.state
!= SCE_PO_DEFAULT
)
133 curLineState
= sc
.state
;
137 // Update the line state, so it can be seen by next line
138 curLine
= styler
.GetLine(sc
.currentPos
);
139 styler
.SetLineState(curLine
, curLineState
);
145 static const char *const poWordListDesc
[] = {
149 LexerModule
lmPO(SCLEX_PO
, ColourisePODoc
, "po", 0, poWordListDesc
);