|
|
|
/******************************************************************
|
|
|
|
* LexAsciidoc.cxx
|
|
|
|
*
|
|
|
|
* A simple Asciidoc lexer for scintilla.
|
|
|
|
*
|
|
|
|
* Based on the LexMarkdown.cxx by Jon Strait - jstrait@moonloop.net
|
|
|
|
*
|
|
|
|
* The License.txt file describes the conditions under which this
|
|
|
|
* software may be distributed.
|
|
|
|
*
|
|
|
|
*****************************************************************/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <assert.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;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
bool start;
|
|
|
|
int len1;
|
|
|
|
int len2;
|
|
|
|
const char *name;
|
|
|
|
} MacroItem;
|
|
|
|
|
|
|
|
static const MacroItem MacroList[] = {
|
|
|
|
// Directives
|
|
|
|
{true, 5, 2, "ifdef::"},
|
|
|
|
{true, 6, 2, "ifeval::"},
|
|
|
|
{true, 6, 2, "ifndef::"},
|
|
|
|
{true, 5, 2, "endif::"},
|
|
|
|
// Macros
|
|
|
|
{true, 5, 2, "audio::"},
|
|
|
|
{true, 7, 2, "include::"},
|
|
|
|
{true, 5, 2, "image::"},
|
|
|
|
{true, 5, 2, "video::"},
|
|
|
|
{false, 8, 1, "asciimath:"},
|
|
|
|
{false, 3, 1, "btn:"},
|
|
|
|
{false, 5, 1, "image:"},
|
|
|
|
{false, 3, 1, "kbd:"},
|
|
|
|
{false, 9, 1, "latexmath:"},
|
|
|
|
{false, 4, 1, "link:"},
|
|
|
|
{false, 6, 1, "mailto:"},
|
|
|
|
{false, 4, 1, "menu:"},
|
|
|
|
{false, 4, 1, "pass:"},
|
|
|
|
{false, 4, 1, "stem:"},
|
|
|
|
{false, 4, 1, "xref:"},
|
|
|
|
// Admonitions
|
|
|
|
{true, 7, 1, "CAUTION:"},
|
|
|
|
{true, 9, 1, "IMPORTANT:"},
|
|
|
|
{true, 4, 1, "NOTE:"},
|
|
|
|
{true, 3, 1, "TIP:"},
|
|
|
|
{true, 7, 1, "WARNING:"},
|
|
|
|
{false, 0, 0, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
constexpr bool IsNewline(const int ch) {
|
|
|
|
// sc.GetRelative(i) returns '\0' if out of range
|
|
|
|
return (ch == '\n' || ch == '\r' || ch == '\0');
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool AtTermStart(StyleContext &sc) {
|
|
|
|
return sc.currentPos == 0 || sc.chPrev == 0 || isspacechar(sc.chPrev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ColorizeAsciidocDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
|
|
|
|
WordList **, Accessor &styler) {
|
|
|
|
bool freezeCursor = false;
|
|
|
|
|
|
|
|
StyleContext sc(startPos, static_cast<Sci_PositionU>(length), initStyle, styler);
|
|
|
|
|
|
|
|
while (sc.More()) {
|
|
|
|
// Skip past escaped characters
|
|
|
|
if (sc.ch == '\\') {
|
|
|
|
sc.Forward();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip newline.
|
|
|
|
if (IsNewline(sc.ch)) {
|
|
|
|
// Newline doesn't end blocks
|
|
|
|
if (sc.state != SCE_ASCIIDOC_CODEBK && \
|
|
|
|
sc.state != SCE_ASCIIDOC_PASSBK && \
|
|
|
|
sc.state != SCE_ASCIIDOC_COMMENTBK && \
|
|
|
|
sc.state != SCE_ASCIIDOC_LITERALBK) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
sc.Forward();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conditional state-based actions
|
|
|
|
switch (sc.state) {
|
|
|
|
|
|
|
|
// Strong
|
|
|
|
case SCE_ASCIIDOC_STRONG1:
|
|
|
|
if (sc.ch == '*' && sc.chPrev != ' ') {
|
|
|
|
sc.Forward();
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
freezeCursor = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SCE_ASCIIDOC_STRONG2:
|
|
|
|
if (sc.Match("**") && sc.chPrev != ' ') {
|
|
|
|
sc.Forward(2);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
freezeCursor = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Emphasis
|
|
|
|
case SCE_ASCIIDOC_EM1:
|
|
|
|
if (sc.ch == '_' && sc.chPrev != ' ') {
|
|
|
|
sc.Forward();
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
freezeCursor = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SCE_ASCIIDOC_EM2:
|
|
|
|
if (sc.Match("__") && sc.chPrev != ' ') {
|
|
|
|
sc.Forward(2);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
freezeCursor = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Link
|
|
|
|
case SCE_ASCIIDOC_LINK:
|
|
|
|
if (sc.ch == ']' && sc.chPrev != '\\') {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Code block
|
|
|
|
case SCE_ASCIIDOC_CODEBK:
|
|
|
|
if (sc.atLineStart && sc.Match("----") && IsNewline(sc.GetRelative(4))) {
|
|
|
|
sc.Forward(4);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Passthrough block
|
|
|
|
case SCE_ASCIIDOC_PASSBK:
|
|
|
|
if (sc.atLineStart && sc.Match("++++") && IsNewline(sc.GetRelative(4))) {
|
|
|
|
sc.Forward(4);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Comment block
|
|
|
|
case SCE_ASCIIDOC_COMMENTBK:
|
|
|
|
if (sc.atLineStart && sc.Match("////") && IsNewline(sc.GetRelative(4))) {
|
|
|
|
sc.Forward(4);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Literal
|
|
|
|
case SCE_ASCIIDOC_LITERAL:
|
|
|
|
if (sc.ch == '+' && sc.chPrev != '\\') {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Literal block
|
|
|
|
case SCE_ASCIIDOC_LITERALBK:
|
|
|
|
if (sc.atLineStart && sc.Match("....") && IsNewline(sc.GetRelative(4))) {
|
|
|
|
sc.Forward(4);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Attribute
|
|
|
|
case SCE_ASCIIDOC_ATTRIB:
|
|
|
|
if (sc.ch == ':' && sc.chPrev != ' ' && sc.chNext == ' ') {
|
|
|
|
sc.Forward();
|
|
|
|
sc.SetState(SCE_ASCIIDOC_ATTRIBVAL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Macro
|
|
|
|
case SCE_ASCIIDOC_MACRO:
|
|
|
|
if (sc.ch == ']' && sc.chPrev != '\\') {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Default
|
|
|
|
case SCE_ASCIIDOC_DEFAULT:
|
|
|
|
// Headers
|
|
|
|
if (sc.atLineStart && sc.Match("====== ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_HEADER6);
|
|
|
|
sc.Forward(6);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("===== ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_HEADER5);
|
|
|
|
sc.Forward(5);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("==== ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_HEADER4);
|
|
|
|
sc.Forward(4);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("=== ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_HEADER3);
|
|
|
|
sc.Forward(3);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("== ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_HEADER2);
|
|
|
|
sc.Forward(2);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("= ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_HEADER1);
|
|
|
|
sc.Forward(1);
|
|
|
|
}
|
|
|
|
// Unordered list item
|
|
|
|
else if (sc.atLineStart && sc.Match("****** ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_ULIST_ITEM);
|
|
|
|
sc.Forward(6);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("***** ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_ULIST_ITEM);
|
|
|
|
sc.Forward(5);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("**** ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_ULIST_ITEM);
|
|
|
|
sc.Forward(4);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("*** ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_ULIST_ITEM);
|
|
|
|
sc.Forward(3);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("** ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_ULIST_ITEM);
|
|
|
|
sc.Forward(2);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("* ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_ULIST_ITEM);
|
|
|
|
sc.Forward(1);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
// Ordered list item
|
|
|
|
else if (sc.atLineStart && sc.Match("...... ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_OLIST_ITEM);
|
|
|
|
sc.Forward(6);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("..... ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_OLIST_ITEM);
|
|
|
|
sc.Forward(5);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match(".... ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_OLIST_ITEM);
|
|
|
|
sc.Forward(4);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match("... ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_OLIST_ITEM);
|
|
|
|
sc.Forward(3);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match(".. ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_OLIST_ITEM);
|
|
|
|
sc.Forward(2);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
else if (sc.atLineStart && sc.Match(". ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_OLIST_ITEM);
|
|
|
|
sc.Forward(1);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
}
|
|
|
|
// Blockquote
|
|
|
|
else if (sc.atLineStart && sc.Match("> ")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_BLOCKQUOTE);
|
|
|
|
sc.Forward();
|
|
|
|
}
|
|
|
|
// Link
|
|
|
|
else if (!sc.atLineStart && sc.ch == '[' && sc.chPrev != '\\' && sc.chNext != ' ') {
|
|
|
|
sc.Forward();
|
|
|
|
sc.SetState(SCE_ASCIIDOC_LINK);
|
|
|
|
freezeCursor = true;
|
|
|
|
}
|
|
|
|
// Code block
|
|
|
|
else if (sc.atLineStart && sc.Match("----") && IsNewline(sc.GetRelative(4))) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_CODEBK);
|
|
|
|
sc.Forward(4);
|
|
|
|
}
|
|
|
|
// Passthrough block
|
|
|
|
else if (sc.atLineStart && sc.Match("++++") && IsNewline(sc.GetRelative(4))) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_PASSBK);
|
|
|
|
sc.Forward(4);
|
|
|
|
}
|
|
|
|
// Comment block
|
|
|
|
else if (sc.atLineStart && sc.Match("////") && IsNewline(sc.GetRelative(4))) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_COMMENTBK);
|
|
|
|
sc.Forward(4);
|
|
|
|
}
|
|
|
|
// Comment
|
|
|
|
else if (sc.atLineStart && sc.Match("//")) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_COMMENT);
|
|
|
|
sc.Forward();
|
|
|
|
}
|
|
|
|
// Literal
|
|
|
|
else if (sc.ch == '+' && sc.chPrev != '\\' && sc.chNext != ' ' && AtTermStart(sc)) {
|
|
|
|
sc.Forward();
|
|
|
|
sc.SetState(SCE_ASCIIDOC_LITERAL);
|
|
|
|
freezeCursor = true;
|
|
|
|
}
|
|
|
|
// Literal block
|
|
|
|
else if (sc.atLineStart && sc.Match("....") && IsNewline(sc.GetRelative(4))) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_LITERALBK);
|
|
|
|
sc.Forward(4);
|
|
|
|
}
|
|
|
|
// Attribute
|
|
|
|
else if (sc.atLineStart && sc.ch == ':' && sc.chNext != ' ') {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_ATTRIB);
|
|
|
|
}
|
|
|
|
// Strong
|
|
|
|
else if (sc.Match("**") && sc.GetRelative(2) != ' ') {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_STRONG2);
|
|
|
|
sc.Forward();
|
|
|
|
}
|
|
|
|
else if (sc.ch == '*' && sc.chNext != ' ' && AtTermStart(sc)) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_STRONG1);
|
|
|
|
}
|
|
|
|
// Emphasis
|
|
|
|
else if (sc.Match("__") && sc.GetRelative(2) != ' ') {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_EM2);
|
|
|
|
sc.Forward();
|
|
|
|
}
|
|
|
|
else if (sc.ch == '_' && sc.chNext != ' ' && AtTermStart(sc)) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_EM1);
|
|
|
|
}
|
|
|
|
// Macro
|
|
|
|
else if (sc.atLineStart && sc.ch == '[' && sc.chNext != ' ') {
|
|
|
|
sc.Forward();
|
|
|
|
sc.SetState(SCE_ASCIIDOC_MACRO);
|
|
|
|
freezeCursor = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int i = 0;
|
|
|
|
bool found = false;
|
|
|
|
while (!found && MacroList[i].name != NULL) {
|
|
|
|
if (MacroList[i].start)
|
|
|
|
found = sc.atLineStart && sc.Match(MacroList[i].name);
|
|
|
|
else
|
|
|
|
found = sc.Match(MacroList[i].name);
|
|
|
|
if (found) {
|
|
|
|
sc.SetState(SCE_ASCIIDOC_MACRO);
|
|
|
|
sc.Forward(MacroList[i].len1);
|
|
|
|
sc.SetState(SCE_ASCIIDOC_DEFAULT);
|
|
|
|
if (MacroList[i].len2 > 1)
|
|
|
|
sc.Forward(MacroList[i].len2 - 1);
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Advance if not holding back the cursor for this iteration.
|
|
|
|
if (!freezeCursor)
|
|
|
|
sc.Forward();
|
|
|
|
freezeCursor = false;
|
|
|
|
}
|
|
|
|
sc.Complete();
|
|
|
|
}
|
|
|
|
|
|
|
|
extern const LexerModule lmAsciidoc(SCLEX_ASCIIDOC, ColorizeAsciidocDoc, "asciidoc");
|