//..............................................................................
//
// $(pluginName) syntax highlighter
// by default it uses C programming language syntax
//

//..............................................................................

enum TokenColor
{
	Keyword   = ui.StdColor.LightBlue,
	SlComment = ui.StdColor.BrightBlack,
	MlComment = ui.StdColor.BrightBlack,
	SqLiteral = ui.StdColor.Cyan,
	DqLiteral = ui.StdColor.Cyan,
	DecNumber = ui.StdColor.Purple,
	OctNumber = ui.StdColor.Purple,
	HexNumber = ui.StdColor.Purple,
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

class $(highlighterClass)
{
protected:
	enum StickyMode
	{
		None = 0,
		SlComment,
		MlComment,
		SqLiteral,
		DqLiteral,
	}

protected:
	log.Writer* m_writer;
	jnc.RegexState m_scannerState;
	StickyMode m_stickyMode;
	intptr m_stickyOffset;
	uint_t m_stickyColor;

public:
	void write(
		log.Writer* writer,
		void const* p,
		size_t size
		);

	void finalize(log.Writer* writer);

protected:
	void enterStickyMode(
		StickyMode mode,
		uint_t color
		);

	void leaveStickyMode();

	void stickyColorize();

	void scan(
		char const* p,
		size_t length
		);
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

void $(highlighterClass).write(
	log.Writer* writer,
	void const* p,
	size_t size
	)
{
	m_writer = writer;
	m_stickyOffset = -size;
	m_scannerState.m_currentOffset = -size;
	scan(p, size);

	if (m_stickyMode)
		stickyColorize();
}

void $(highlighterClass).finalize(log.Writer* writer)
{
	m_writer = writer;
	scan(null, 0); // process eof
	m_stickyMode = StickyMode.None;
}

void $(highlighterClass).enterStickyMode(
	StickyMode mode,
	uint_t color
	)
{
	m_writer.retroColorize(
		m_scannerState.m_match.m_offset,
		m_scannerState.m_match.m_length,
		color
		);

	m_stickyMode = mode;
	m_stickyOffset = m_scannerState.m_currentOffset;
	m_stickyColor = color;
}

void $(highlighterClass).leaveStickyMode()
{
	stickyColorize();
	m_stickyMode = StickyMode.None;
}

void $(highlighterClass).stickyColorize()
{
	size_t length = m_scannerState.m_currentOffset - m_stickyOffset;

	if (length)
		m_writer.retroColorize(m_stickyOffset, length, m_stickyColor);
}

void $(highlighterClass).scan(
	char const* p,
	size_t length
	)
{
	char const* end = p + length;

	do
	{
		switch (m_stickyMode)
		{
		case StickyMode.None:
			reswitch (m_scannerState, p, end - p)
			{
			case
				"auto|break|case|char|const|continue|default|do|"
				"double|else|enum|extern|float|for|goto|if|int|long|"
				"register|return|short|signed|sizeof|static|struct|"
				"switch|typedef|union|unsigned|void|volatile|while":

				m_writer.retroColorize(
					m_scannerState.m_match.m_offset,
					m_scannerState.m_match.m_length,
					TokenColor.Keyword
					);
				break;

			case "[_a-zA-Z][_a-zA-Z0-9]*":
				// distinguish identifiers from keywords
				break;

			case "0[0-7]+":
				m_writer.retroColorize(
					m_scannerState.m_match.m_offset,
					m_scannerState.m_match.m_length,
					TokenColor.OctNumber
					);
				break;

			case "[0-9]+":
				m_writer.retroColorize(
					m_scannerState.m_match.m_offset,
					m_scannerState.m_match.m_length,
					TokenColor.DecNumber
					);
				break;

			case "0[xX][0-9a-fA-F]+":
				m_writer.retroColorize(
					m_scannerState.m_match.m_offset,
					m_scannerState.m_match.m_length,
					TokenColor.HexNumber
					);
				break;

			case "//":
				enterStickyMode(StickyMode.SlComment, TokenColor.SlComment);
				break;

			case r"/\*":
				enterStickyMode(StickyMode.MlComment, TokenColor.MlComment);
				break;

			case "\'":
				enterStickyMode(StickyMode.SqLiteral, TokenColor.SqLiteral);
				break;

			case "\"":
				enterStickyMode(StickyMode.DqLiteral, TokenColor.DqLiteral);
				break;

			case ".":
				// ignore the rest
				break;
			}
			break;

		case StickyMode.SlComment:
			reswitch (m_scannerState, p, end - p)
			{
			case "\n":
				leaveStickyMode();
				break;

			case ".":
				// ignore the rest
				break;
			}
			break;

		case StickyMode.MlComment:
			reswitch (m_scannerState, p, end - p)
			{
			case r"\*/":
				leaveStickyMode();
				break;

			case ".":
				// ignore the rest
				break;
			}
			break;

		case StickyMode.SqLiteral:
			reswitch (m_scannerState, p, end - p)
			{
			case "'":
				leaveStickyMode();
				break;

			case "\\\'":
				// distinguish escape-protected quotation mark
				break;

			case ".":
				// ignore the rest
				break;
			}
			break;

		case StickyMode.DqLiteral:
			reswitch (m_scannerState, p, end - p)
			{
			case "\"":
				leaveStickyMode();
				break;

			case "\\\"":
				// distinguish escape-protected quotation mark
				break;

			case ".":
				// ignore the rest
				break;
			}
			break;

		default:
			assert(false);
		}

		p += m_scannerState.m_match.m_length;
	} while (p < end || m_scannerState.m_replayLength);
}

//..............................................................................
