mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
add peg-markdown-highlight for markdown editing highlight
[peg-markdown-highlight](https://github.com/ali-rantakari/peg-markdown-highlight.git) Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
parent
772e0e95c8
commit
22bf96c6e3
@ -27,7 +27,9 @@ SOURCES += main.cpp\
|
||||
vnotefile.cpp \
|
||||
vdocument.cpp \
|
||||
utils/vutils.cpp \
|
||||
vpreviewpage.cpp
|
||||
vpreviewpage.cpp \
|
||||
utils/peg-highlight/pmh_parser.c \
|
||||
hgmarkdownhighlighter.cpp
|
||||
|
||||
HEADERS += vmainwindow.h \
|
||||
vdirectorytree.h \
|
||||
@ -44,7 +46,10 @@ HEADERS += vmainwindow.h \
|
||||
vnotefile.h \
|
||||
vdocument.h \
|
||||
utils/vutils.h \
|
||||
vpreviewpage.h
|
||||
vpreviewpage.h \
|
||||
utils/peg-highlight/pmh_parser.h \
|
||||
hgmarkdownhighlighter.h \
|
||||
utils/peg-highlight/pmh_definitions.h
|
||||
|
||||
RESOURCES += \
|
||||
vnote.qrc
|
||||
|
229
hgmarkdownhighlighter.cpp
Normal file
229
hgmarkdownhighlighter.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
/* PEG Markdown Highlight
|
||||
* Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
|
||||
* Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
|
||||
*
|
||||
* highlighter.cpp
|
||||
*
|
||||
* Qt 4.7 example for highlighting a rich text widget.
|
||||
*/
|
||||
|
||||
#include <QtGui>
|
||||
#include "hgmarkdownhighlighter.h"
|
||||
|
||||
WorkerThread::~WorkerThread()
|
||||
{
|
||||
if (result != NULL)
|
||||
pmh_free_elements(result);
|
||||
free(content);
|
||||
}
|
||||
void WorkerThread::run()
|
||||
{
|
||||
if (content == NULL)
|
||||
return;
|
||||
pmh_markdown_to_elements(content, pmh_EXT_NONE, &result);
|
||||
}
|
||||
|
||||
HGMarkdownHighlighter::HGMarkdownHighlighter(QTextDocument *parent,
|
||||
int aWaitInterval) : QObject(parent)
|
||||
{
|
||||
highlightingStyles = NULL;
|
||||
workerThread = NULL;
|
||||
cached_elements = NULL;
|
||||
waitInterval = aWaitInterval;
|
||||
timer = new QTimer(this);
|
||||
timer->setSingleShot(true);
|
||||
timer->setInterval(aWaitInterval);
|
||||
connect(timer, SIGNAL(timeout()), this, SLOT(timerTimeout()));
|
||||
document = parent;
|
||||
connect(document, SIGNAL(contentsChange(int,int,int)),
|
||||
this, SLOT(handleContentsChange(int,int,int)));
|
||||
|
||||
this->parse();
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::setStyles(QVector<HighlightingStyle> &styles)
|
||||
{
|
||||
this->highlightingStyles = &styles;
|
||||
}
|
||||
|
||||
#define STY(type, format) styles->append((HighlightingStyle){type, format})
|
||||
|
||||
void HGMarkdownHighlighter::setDefaultStyles()
|
||||
{
|
||||
QVector<HighlightingStyle> *styles = new QVector<HighlightingStyle>();
|
||||
|
||||
QTextCharFormat headers; headers.setForeground(QBrush(Qt::darkBlue));
|
||||
headers.setBackground(QBrush(QColor(230,230,240)));
|
||||
STY(pmh_H1, headers);
|
||||
STY(pmh_H2, headers);
|
||||
STY(pmh_H3, headers);
|
||||
STY(pmh_H4, headers);
|
||||
STY(pmh_H5, headers);
|
||||
STY(pmh_H6, headers);
|
||||
|
||||
QTextCharFormat hrule; hrule.setForeground(QBrush(Qt::darkGray));
|
||||
hrule.setBackground(QBrush(Qt::lightGray));
|
||||
STY(pmh_HRULE, hrule);
|
||||
|
||||
QTextCharFormat list; list.setForeground(QBrush(Qt::magenta));
|
||||
STY(pmh_LIST_BULLET, list);
|
||||
STY(pmh_LIST_ENUMERATOR, list);
|
||||
|
||||
QTextCharFormat link; link.setForeground(QBrush(Qt::darkCyan));
|
||||
link.setBackground(QBrush(QColor(205,240,240)));
|
||||
STY(pmh_LINK, link);
|
||||
STY(pmh_AUTO_LINK_URL, link);
|
||||
STY(pmh_AUTO_LINK_EMAIL, link);
|
||||
|
||||
QTextCharFormat image; image.setForeground(QBrush(Qt::darkCyan));
|
||||
image.setBackground(QBrush(Qt::cyan));
|
||||
STY(pmh_IMAGE, image);
|
||||
|
||||
QTextCharFormat ref; ref.setForeground(QBrush(QColor(213,178,178)));
|
||||
STY(pmh_REFERENCE, ref);
|
||||
|
||||
QTextCharFormat code; code.setForeground(QBrush(Qt::darkGreen));
|
||||
code.setBackground(QBrush(QColor(217,231,217)));
|
||||
STY(pmh_CODE, code);
|
||||
STY(pmh_VERBATIM, code);
|
||||
|
||||
QTextCharFormat emph; emph.setForeground(QBrush(Qt::darkYellow));
|
||||
emph.setFontItalic(true);
|
||||
STY(pmh_EMPH, emph);
|
||||
|
||||
QTextCharFormat strong; strong.setForeground(QBrush(Qt::magenta));
|
||||
strong.setFontWeight(QFont::Bold);
|
||||
STY(pmh_STRONG, strong);
|
||||
|
||||
QTextCharFormat comment; comment.setForeground(QBrush(Qt::gray));
|
||||
STY(pmh_COMMENT, comment);
|
||||
|
||||
QTextCharFormat blockquote; blockquote.setForeground(QBrush(Qt::darkRed));
|
||||
STY(pmh_BLOCKQUOTE, blockquote);
|
||||
|
||||
this->setStyles(*styles);
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::clearFormatting()
|
||||
{
|
||||
QTextBlock block = document->firstBlock();
|
||||
while (block.isValid()) {
|
||||
block.layout()->clearAdditionalFormats();
|
||||
block = block.next();
|
||||
}
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::highlight()
|
||||
{
|
||||
if (cached_elements == NULL) {
|
||||
qDebug() << "cached_elements is NULL";
|
||||
return;
|
||||
}
|
||||
|
||||
if (highlightingStyles == NULL)
|
||||
this->setDefaultStyles();
|
||||
|
||||
this->clearFormatting();
|
||||
|
||||
for (int i = 0; i < highlightingStyles->size(); i++)
|
||||
{
|
||||
HighlightingStyle style = highlightingStyles->at(i);
|
||||
pmh_element *elem_cursor = cached_elements[style.type];
|
||||
while (elem_cursor != NULL)
|
||||
{
|
||||
if (elem_cursor->end <= elem_cursor->pos) {
|
||||
elem_cursor = elem_cursor->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// "The QTextLayout object can only be modified from the
|
||||
// documentChanged implementation of a QAbstractTextDocumentLayout
|
||||
// subclass. Any changes applied from the outside cause undefined
|
||||
// behavior." -- we are breaking this rule here. There might be
|
||||
// a better (more correct) way to do this.
|
||||
|
||||
int startBlockNum = document->findBlock(elem_cursor->pos).blockNumber();
|
||||
int endBlockNum = document->findBlock(elem_cursor->end).blockNumber();
|
||||
for (int j = startBlockNum; j <= endBlockNum; j++)
|
||||
{
|
||||
QTextBlock block = document->findBlockByNumber(j);
|
||||
|
||||
QTextLayout *layout = block.layout();
|
||||
QList<QTextLayout::FormatRange> list = layout->additionalFormats();
|
||||
int blockpos = block.position();
|
||||
QTextLayout::FormatRange r;
|
||||
r.format = style.format;
|
||||
|
||||
if (j == startBlockNum) {
|
||||
r.start = elem_cursor->pos - blockpos;
|
||||
r.length = (startBlockNum == endBlockNum)
|
||||
? elem_cursor->end - elem_cursor->pos
|
||||
: block.length() - r.start;
|
||||
} else if (j == endBlockNum) {
|
||||
r.start = 0;
|
||||
r.length = elem_cursor->end - blockpos;
|
||||
} else {
|
||||
r.start = 0;
|
||||
r.length = block.length();
|
||||
}
|
||||
|
||||
list.append(r);
|
||||
layout->setAdditionalFormats(list);
|
||||
}
|
||||
|
||||
elem_cursor = elem_cursor->next;
|
||||
}
|
||||
}
|
||||
|
||||
document->markContentsDirty(0, document->characterCount());
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::parse()
|
||||
{
|
||||
if (workerThread != NULL && workerThread->isRunning()) {
|
||||
parsePending = true;
|
||||
return;
|
||||
}
|
||||
|
||||
QString content = document->toPlainText();
|
||||
QByteArray ba = content.toUtf8();
|
||||
char *content_cstring = strdup((char *)ba.data());
|
||||
|
||||
if (workerThread != NULL)
|
||||
delete workerThread;
|
||||
workerThread = new WorkerThread();
|
||||
workerThread->content = content_cstring;
|
||||
connect(workerThread, SIGNAL(finished()), this, SLOT(threadFinished()));
|
||||
parsePending = false;
|
||||
workerThread->start();
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::threadFinished()
|
||||
{
|
||||
if (parsePending) {
|
||||
this->parse();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cached_elements != NULL)
|
||||
pmh_free_elements(cached_elements);
|
||||
cached_elements = workerThread->result;
|
||||
workerThread->result = NULL;
|
||||
|
||||
this->highlight();
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::handleContentsChange(int position, int charsRemoved,
|
||||
int charsAdded)
|
||||
{
|
||||
if (charsRemoved == 0 && charsAdded == 0)
|
||||
return;
|
||||
|
||||
timer->stop();
|
||||
timer->start();
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::timerTimeout()
|
||||
{
|
||||
this->parse();
|
||||
}
|
67
hgmarkdownhighlighter.h
Normal file
67
hgmarkdownhighlighter.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* PEG Markdown Highlight
|
||||
* Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
|
||||
* Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
|
||||
*
|
||||
* highlighter.h
|
||||
*
|
||||
* Qt 4.7 example for highlighting a rich text widget.
|
||||
*/
|
||||
|
||||
#ifndef HGMARKDOWNHIGHLIGHTER_H
|
||||
#define HGMARKDOWNHIGHLIGHTER_H
|
||||
|
||||
#include <QTextCharFormat>
|
||||
#include <QThread>
|
||||
|
||||
extern "C" {
|
||||
#include "utils/peg-highlight/pmh_parser.h"
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextDocument;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class WorkerThread : public QThread
|
||||
{
|
||||
public:
|
||||
~WorkerThread();
|
||||
void run();
|
||||
char *content;
|
||||
pmh_element **result;
|
||||
};
|
||||
|
||||
struct HighlightingStyle
|
||||
{
|
||||
pmh_element_type type;
|
||||
QTextCharFormat format;
|
||||
};
|
||||
|
||||
class HGMarkdownHighlighter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HGMarkdownHighlighter(QTextDocument *parent = 0, int aWaitInterval = 2000);
|
||||
void setStyles(QVector<HighlightingStyle> &styles);
|
||||
int waitInterval;
|
||||
|
||||
private slots:
|
||||
void handleContentsChange(int position, int charsRemoved, int charsAdded);
|
||||
void threadFinished();
|
||||
void timerTimeout();
|
||||
|
||||
private:
|
||||
QTimer *timer;
|
||||
QTextDocument *document;
|
||||
WorkerThread *workerThread;
|
||||
bool parsePending;
|
||||
pmh_element **cached_elements;
|
||||
QVector<HighlightingStyle> *highlightingStyles;
|
||||
|
||||
void clearFormatting();
|
||||
void highlight();
|
||||
void parse();
|
||||
void setDefaultStyles();
|
||||
};
|
||||
|
||||
#endif
|
125
utils/peg-highlight/pmh_definitions.h
Executable file
125
utils/peg-highlight/pmh_definitions.h
Executable file
@ -0,0 +1,125 @@
|
||||
/* PEG Markdown Highlight
|
||||
* Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
|
||||
* Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
|
||||
*
|
||||
* pmh_definitions.h
|
||||
*/
|
||||
|
||||
#ifndef pmh_MARKDOWN_DEFINITIONS
|
||||
#define pmh_MARKDOWN_DEFINITIONS
|
||||
|
||||
/** \file
|
||||
* \brief Global definitions for the parser.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief Element types.
|
||||
*
|
||||
* The first (documented) ones are language element types.
|
||||
*
|
||||
* The last (non-documented) ones are utility types used
|
||||
* by the parser itself.
|
||||
*
|
||||
* \sa pmh_element
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
pmh_LINK, /**< Explicit link */
|
||||
pmh_AUTO_LINK_URL, /**< Implicit URL link */
|
||||
pmh_AUTO_LINK_EMAIL, /**< Implicit email link */
|
||||
pmh_IMAGE, /**< Image definition */
|
||||
pmh_CODE, /**< Code (inline) */
|
||||
pmh_HTML, /**< HTML */
|
||||
pmh_HTML_ENTITY, /**< HTML special entity definition */
|
||||
pmh_EMPH, /**< Emphasized text */
|
||||
pmh_STRONG, /**< Strong text */
|
||||
pmh_LIST_BULLET, /**< Bullet for an unordered list item */
|
||||
pmh_LIST_ENUMERATOR, /**< Enumerator for an ordered list item */
|
||||
pmh_COMMENT, /**< (HTML) Comment */
|
||||
|
||||
// Code assumes that pmh_H1-6 are in order.
|
||||
pmh_H1, /**< Header, level 1 */
|
||||
pmh_H2, /**< Header, level 2 */
|
||||
pmh_H3, /**< Header, level 3 */
|
||||
pmh_H4, /**< Header, level 4 */
|
||||
pmh_H5, /**< Header, level 5 */
|
||||
pmh_H6, /**< Header, level 6 */
|
||||
|
||||
pmh_BLOCKQUOTE, /**< Blockquote */
|
||||
pmh_VERBATIM, /**< Verbatim (e.g. block of code) */
|
||||
pmh_HTMLBLOCK, /**< Block of HTML */
|
||||
pmh_HRULE, /**< Horizontal rule */
|
||||
pmh_REFERENCE, /**< Reference */
|
||||
pmh_NOTE, /**< Note */
|
||||
pmh_STRIKE, /**< Strike-through */
|
||||
|
||||
// Utility types used by the parser itself:
|
||||
|
||||
// List of pmh_RAW element lists, each to be processed separately from
|
||||
// others (for each element in linked lists of this type, `children` points
|
||||
// to a linked list of pmh_RAW elements):
|
||||
pmh_RAW_LIST, /**< Internal to parser. Please ignore. */
|
||||
|
||||
// Span marker for positions in original input to be post-processed
|
||||
// in a second parsing step:
|
||||
pmh_RAW, /**< Internal to parser. Please ignore. */
|
||||
|
||||
// Additional text to be parsed along with spans in the original input
|
||||
// (these may be added to linked lists of pmh_RAW elements):
|
||||
pmh_EXTRA_TEXT, /**< Internal to parser. Please ignore. */
|
||||
|
||||
// Separates linked lists of pmh_RAW elements into parts to be processed
|
||||
// separate from each other:
|
||||
pmh_SEPARATOR, /**< Internal to parser. Please ignore. */
|
||||
|
||||
// Placeholder element used while parsing:
|
||||
pmh_NO_TYPE, /**< Internal to parser. Please ignore. */
|
||||
|
||||
// Linked list of *all* elements created while parsing:
|
||||
pmh_ALL /**< Internal to parser. Please ignore. */
|
||||
} pmh_element_type;
|
||||
|
||||
/**
|
||||
* \brief Number of types in pmh_element_type.
|
||||
* \sa pmh_element_type
|
||||
*/
|
||||
#define pmh_NUM_TYPES 31
|
||||
|
||||
/**
|
||||
* \brief Number of *language element* types in pmh_element_type.
|
||||
* \sa pmh_element_type
|
||||
*/
|
||||
#define pmh_NUM_LANG_TYPES (pmh_NUM_TYPES - 6)
|
||||
|
||||
|
||||
/**
|
||||
* \brief A Language element occurrence.
|
||||
*/
|
||||
struct pmh_Element
|
||||
{
|
||||
pmh_element_type type; /**< \brief Type of element */
|
||||
unsigned long pos; /**< \brief Unicode code point offset marking the
|
||||
beginning of this element in the
|
||||
input. */
|
||||
unsigned long end; /**< \brief Unicode code point offset marking the
|
||||
end of this element in the input. */
|
||||
struct pmh_Element *next; /**< \brief Next element in list */
|
||||
char *label; /**< \brief Label (for links and references) */
|
||||
char *address; /**< \brief Address (for links and references) */
|
||||
};
|
||||
typedef struct pmh_Element pmh_element;
|
||||
|
||||
/**
|
||||
* \brief Bitfield enumeration of supported Markdown extensions.
|
||||
*/
|
||||
enum pmh_extensions
|
||||
{
|
||||
pmh_EXT_NONE = 0, /**< No extensions */
|
||||
pmh_EXT_NOTES = (1 << 0), /**< Footnote syntax:
|
||||
http://pandoc.org/README.html#footnotes */
|
||||
pmh_EXT_STRIKE = (1 << 1) /**< Strike-through syntax:
|
||||
http://pandoc.org/README.html#strikeout */
|
||||
};
|
||||
|
||||
#endif
|
6550
utils/peg-highlight/pmh_parser.c
Normal file
6550
utils/peg-highlight/pmh_parser.c
Normal file
File diff suppressed because it is too large
Load Diff
89
utils/peg-highlight/pmh_parser.h
Normal file
89
utils/peg-highlight/pmh_parser.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* PEG Markdown Highlight
|
||||
* Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
|
||||
* Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
|
||||
*
|
||||
* pmh_parser.h
|
||||
*/
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
/** \file
|
||||
* \brief Parser public interface.
|
||||
*/
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "pmh_definitions.h"
|
||||
|
||||
|
||||
/**
|
||||
* \brief Parse Markdown text, return elements
|
||||
*
|
||||
* Parses the given Markdown text and returns the results as an
|
||||
* array of linked lists of elements, indexed by type.
|
||||
*
|
||||
* \param[in] text The Markdown text to parse for highlighting.
|
||||
* \param[in] extensions The extensions to use in parsing (a bitfield
|
||||
* of pmh_extensions values).
|
||||
* \param[out] out_result A pmh_element array, indexed by type, containing
|
||||
* the results of the parsing (linked lists of elements).
|
||||
* You must pass this to pmh_free_elements() when it's
|
||||
* not needed anymore.
|
||||
*
|
||||
* \sa pmh_element_type
|
||||
*/
|
||||
void pmh_markdown_to_elements(char *text, int extensions,
|
||||
pmh_element **out_result[]);
|
||||
|
||||
/**
|
||||
* \brief Sort elements in list by start offset.
|
||||
*
|
||||
* Sorts the linked lists of elements in the list returned by
|
||||
* pmh_markdown_to_elements() by their start offsets (pos).
|
||||
*
|
||||
* \param[in] element_lists Array of linked lists of elements (output
|
||||
* from pmh_markdown_to_elements()).
|
||||
*
|
||||
* \sa pmh_markdown_to_elements
|
||||
* \sa pmh_element::pos
|
||||
*/
|
||||
void pmh_sort_elements_by_pos(pmh_element *element_lists[]);
|
||||
|
||||
/**
|
||||
* \brief Free pmh_element array
|
||||
*
|
||||
* Frees an pmh_element array returned by pmh_markdown_to_elements().
|
||||
*
|
||||
* \param[in] elems The pmh_element array resulting from calling
|
||||
* pmh_markdown_to_elements().
|
||||
*
|
||||
* \sa pmh_markdown_to_elements
|
||||
*/
|
||||
void pmh_free_elements(pmh_element **elems);
|
||||
|
||||
/**
|
||||
* \brief Get element type name
|
||||
*
|
||||
* \param[in] type The type value to get the name for.
|
||||
*
|
||||
* \return The name of the given type as a null-terminated string.
|
||||
*
|
||||
* \sa pmh_element_type
|
||||
*/
|
||||
char *pmh_element_name_from_type(pmh_element_type type);
|
||||
|
||||
/**
|
||||
* \brief Get element type from a name
|
||||
*
|
||||
* \param[in] name The name of the type.
|
||||
*
|
||||
* \return The element type corresponding to the given name.
|
||||
*
|
||||
* \sa pmh_element_type
|
||||
*/
|
||||
pmh_element_type pmh_element_type_from_name(char *name);
|
||||
|
932
utils/peg-highlight/pmh_styleparser.c
Normal file
932
utils/peg-highlight/pmh_styleparser.c
Normal file
@ -0,0 +1,932 @@
|
||||
/* PEG Markdown Highlight
|
||||
* Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
|
||||
* Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
|
||||
*
|
||||
* styleparser.c
|
||||
*
|
||||
* Parser for custom syntax highlighting stylesheets.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "pmh_styleparser.h"
|
||||
#include "pmh_parser.h"
|
||||
|
||||
|
||||
#if pmh_DEBUG_OUTPUT
|
||||
#define pmhsp_PRINTF(x, ...) fprintf(stderr, x, ##__VA_ARGS__)
|
||||
#else
|
||||
#define pmhsp_PRINTF(x, ...)
|
||||
#endif
|
||||
|
||||
|
||||
// vasprintf is not in the C standard nor in POSIX so we provide our own
|
||||
static int our_vasprintf(char **strptr, const char *fmt, va_list argptr)
|
||||
{
|
||||
int ret;
|
||||
va_list argptr2;
|
||||
*strptr = NULL;
|
||||
|
||||
va_copy(argptr2, argptr);
|
||||
ret = vsnprintf(NULL, 0, fmt, argptr2);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
*strptr = (char *)malloc(ret+1);
|
||||
if (*strptr == NULL)
|
||||
return -1;
|
||||
|
||||
va_copy(argptr2, argptr);
|
||||
ret = vsnprintf(*strptr, ret+1, fmt, argptr2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Parsing context data
|
||||
typedef struct
|
||||
{
|
||||
char *input;
|
||||
void (*error_callback)(char*,int,void*);
|
||||
void *error_callback_context;
|
||||
int styles_pos;
|
||||
pmh_style_collection *styles;
|
||||
} style_parser_data;
|
||||
|
||||
typedef struct raw_attribute
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
int line_number;
|
||||
struct raw_attribute *next;
|
||||
} raw_attribute;
|
||||
|
||||
static raw_attribute *new_raw_attribute(char *name, char *value,
|
||||
int line_number)
|
||||
{
|
||||
raw_attribute *v = (raw_attribute *)malloc(sizeof(raw_attribute));
|
||||
v->name = name;
|
||||
v->value = value;
|
||||
v->line_number = line_number;
|
||||
v->next = NULL;
|
||||
return v;
|
||||
}
|
||||
|
||||
static void free_raw_attributes(raw_attribute *list)
|
||||
{
|
||||
raw_attribute *cur = list;
|
||||
while (cur != NULL)
|
||||
{
|
||||
if (cur->name != NULL) free(cur->name);
|
||||
if (cur->value != NULL) free(cur->value);
|
||||
raw_attribute *this = cur;
|
||||
cur = cur->next;
|
||||
free(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void report_error(style_parser_data *p_data,
|
||||
int line_number, char *str, ...)
|
||||
{
|
||||
if (p_data->error_callback == NULL)
|
||||
return;
|
||||
va_list argptr;
|
||||
va_start(argptr, str);
|
||||
char *errmsg;
|
||||
our_vasprintf(&errmsg, str, argptr);
|
||||
va_end(argptr);
|
||||
p_data->error_callback(errmsg, line_number,
|
||||
p_data->error_callback_context);
|
||||
free(errmsg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static char *trim_str(char *str)
|
||||
{
|
||||
while (isspace(*str))
|
||||
str++;
|
||||
if (*str == '\0')
|
||||
return str;
|
||||
char *end = str + strlen(str) - 1;
|
||||
while (end > str && isspace(*end))
|
||||
end--;
|
||||
*(end+1) = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *trim_str_dup(char *str)
|
||||
{
|
||||
size_t start = 0;
|
||||
while (isspace(*(str + start)))
|
||||
start++;
|
||||
size_t end = strlen(str) - 1;
|
||||
while (start < end && isspace(*(str + end)))
|
||||
end--;
|
||||
|
||||
size_t len = end - start + 1;
|
||||
char *ret = (char *)malloc(sizeof(char)*len + 1);
|
||||
*ret = '\0';
|
||||
strncat(ret, (str + start), len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *strcpy_lower(char *str)
|
||||
{
|
||||
char *low = strdup(str);
|
||||
int i;
|
||||
int len = strlen(str);
|
||||
for (i = 0; i < len; i++)
|
||||
*(low+i) = tolower(*(low+i));
|
||||
return low;
|
||||
}
|
||||
|
||||
static char *standardize_str(char *str)
|
||||
{
|
||||
return strcpy_lower(trim_str(str));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static pmh_attr_argb_color *new_argb_color(int r, int g, int b, int a)
|
||||
{
|
||||
pmh_attr_argb_color *c = (pmh_attr_argb_color *)
|
||||
malloc(sizeof(pmh_attr_argb_color));
|
||||
c->red = r; c->green = g; c->blue = b; c->alpha = a;
|
||||
return c;
|
||||
}
|
||||
static pmh_attr_argb_color *new_argb_from_hex(long long hex, bool has_alpha)
|
||||
{
|
||||
// 0xaarrggbb
|
||||
int a = has_alpha ? ((hex >> 24) & 0xFF) : 255;
|
||||
int r = ((hex >> 16) & 0xFF);
|
||||
int g = ((hex >> 8) & 0xFF);
|
||||
int b = (hex & 0xFF);
|
||||
return new_argb_color(r,g,b,a);
|
||||
}
|
||||
static pmh_attr_argb_color *new_argb_from_hex_str(style_parser_data *p_data,
|
||||
int attr_line_number,
|
||||
char *str)
|
||||
{
|
||||
// "aarrggbb"
|
||||
int len = strlen(str);
|
||||
if (len != 6 && len != 8) {
|
||||
report_error(p_data, attr_line_number,
|
||||
"Value '%s' is not a valid color value: it should be a "
|
||||
"hexadecimal number, 6 or 8 characters long.",
|
||||
str);
|
||||
return NULL;
|
||||
}
|
||||
char *endptr = NULL;
|
||||
long long num = strtoll(str, &endptr, 16);
|
||||
if (*endptr != '\0') {
|
||||
report_error(p_data, attr_line_number,
|
||||
"Value '%s' is not a valid color value: the character "
|
||||
"'%c' is invalid. The color value should be a hexadecimal "
|
||||
"number, 6 or 8 characters long.",
|
||||
str, *endptr);
|
||||
return NULL;
|
||||
}
|
||||
return new_argb_from_hex(num, (len == 8));
|
||||
}
|
||||
|
||||
static pmh_attr_value *new_attr_value()
|
||||
{
|
||||
return (pmh_attr_value *)malloc(sizeof(pmh_attr_value));
|
||||
}
|
||||
|
||||
static pmh_attr_font_styles *new_font_styles()
|
||||
{
|
||||
pmh_attr_font_styles *ret = (pmh_attr_font_styles *)
|
||||
malloc(sizeof(pmh_attr_font_styles));
|
||||
ret->italic = false;
|
||||
ret->bold = false;
|
||||
ret->underlined = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static pmh_attr_font_size *new_font_size()
|
||||
{
|
||||
pmh_attr_font_size *ret = (pmh_attr_font_size *)
|
||||
malloc(sizeof(pmh_attr_font_size));
|
||||
ret->is_relative = false;
|
||||
ret->size_pt = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static pmh_style_attribute *new_attr(char *name, pmh_attr_type type)
|
||||
{
|
||||
pmh_style_attribute *attr = (pmh_style_attribute *)malloc(sizeof(pmh_style_attribute));
|
||||
attr->name = strdup(name);
|
||||
attr->type = type;
|
||||
attr->next = NULL;
|
||||
return attr;
|
||||
}
|
||||
|
||||
static void free_style_attributes(pmh_style_attribute *list)
|
||||
{
|
||||
pmh_style_attribute *cur = list;
|
||||
while (cur != NULL)
|
||||
{
|
||||
if (cur->name != NULL)
|
||||
free(cur->name);
|
||||
if (cur->value != NULL)
|
||||
{
|
||||
if (cur->type == pmh_attr_type_foreground_color
|
||||
|| cur->type == pmh_attr_type_background_color
|
||||
|| cur->type == pmh_attr_type_caret_color
|
||||
|| cur->type == pmh_attr_type_strike_color)
|
||||
free(cur->value->argb_color);
|
||||
else if (cur->type == pmh_attr_type_font_family)
|
||||
free(cur->value->font_family);
|
||||
else if (cur->type == pmh_attr_type_font_style)
|
||||
free(cur->value->font_styles);
|
||||
else if (cur->type == pmh_attr_type_font_size_pt)
|
||||
free(cur->value->font_size);
|
||||
else if (cur->type == pmh_attr_type_other)
|
||||
free(cur->value->string);
|
||||
free(cur->value);
|
||||
}
|
||||
pmh_style_attribute *this = cur;
|
||||
cur = cur->next;
|
||||
free(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define IF_ATTR_NAME(x) if (strcmp(x, name) == 0)
|
||||
pmh_attr_type pmh_attr_type_from_name(char *name)
|
||||
{
|
||||
IF_ATTR_NAME("color") return pmh_attr_type_foreground_color;
|
||||
else IF_ATTR_NAME("foreground") return pmh_attr_type_foreground_color;
|
||||
else IF_ATTR_NAME("foreground-color") return pmh_attr_type_foreground_color;
|
||||
else IF_ATTR_NAME("background") return pmh_attr_type_background_color;
|
||||
else IF_ATTR_NAME("background-color") return pmh_attr_type_background_color;
|
||||
else IF_ATTR_NAME("caret") return pmh_attr_type_caret_color;
|
||||
else IF_ATTR_NAME("caret-color") return pmh_attr_type_caret_color;
|
||||
else IF_ATTR_NAME("strike") return pmh_attr_type_strike_color;
|
||||
else IF_ATTR_NAME("strike-color") return pmh_attr_type_strike_color;
|
||||
else IF_ATTR_NAME("font-size") return pmh_attr_type_font_size_pt;
|
||||
else IF_ATTR_NAME("font-family") return pmh_attr_type_font_family;
|
||||
else IF_ATTR_NAME("font-style") return pmh_attr_type_font_style;
|
||||
return pmh_attr_type_other;
|
||||
}
|
||||
|
||||
char *pmh_attr_name_from_type(pmh_attr_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case pmh_attr_type_foreground_color:
|
||||
return "foreground-color"; break;
|
||||
case pmh_attr_type_background_color:
|
||||
return "background-color"; break;
|
||||
case pmh_attr_type_caret_color:
|
||||
return "caret-color"; break;
|
||||
case pmh_attr_type_strike_color:
|
||||
return "strike-color"; break;
|
||||
case pmh_attr_type_font_size_pt:
|
||||
return "font-size"; break;
|
||||
case pmh_attr_type_font_family:
|
||||
return "font-family"; break;
|
||||
case pmh_attr_type_font_style:
|
||||
return "font-style"; break;
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct multi_value
|
||||
{
|
||||
char *value;
|
||||
size_t length;
|
||||
int line_number;
|
||||
struct multi_value *next;
|
||||
} multi_value;
|
||||
|
||||
static multi_value *split_multi_value(char *input, char separator)
|
||||
{
|
||||
multi_value *head = NULL;
|
||||
multi_value *tail = NULL;
|
||||
|
||||
char *c = input;
|
||||
while (*c != '\0')
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; (*(c+i) != '\0' && *(c+i) != separator); i++);
|
||||
|
||||
multi_value *mv = (multi_value *)malloc(sizeof(multi_value));
|
||||
mv->value = (char *)malloc(sizeof(char)*i + 1);
|
||||
mv->length = i;
|
||||
mv->line_number = 0;
|
||||
mv->next = NULL;
|
||||
*mv->value = '\0';
|
||||
strncat(mv->value, c, i);
|
||||
|
||||
if (head == NULL) {
|
||||
head = mv;
|
||||
tail = mv;
|
||||
} else {
|
||||
tail->next = mv;
|
||||
tail = mv;
|
||||
}
|
||||
|
||||
if (*(c+i) == separator)
|
||||
i++;
|
||||
c += i;
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
static void free_multi_value(multi_value *val)
|
||||
{
|
||||
multi_value *cur = val;
|
||||
while (cur != NULL)
|
||||
{
|
||||
multi_value *this = cur;
|
||||
multi_value *next_cur = cur->next;
|
||||
free(this->value);
|
||||
free(this);
|
||||
cur = next_cur;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#define EQUALS(a,b) (strcmp(a, b) == 0)
|
||||
|
||||
static pmh_style_attribute *interpret_attributes(style_parser_data *p_data,
|
||||
pmh_element_type lang_element_type,
|
||||
raw_attribute *raw_attributes)
|
||||
{
|
||||
pmh_style_attribute *attrs = NULL;
|
||||
|
||||
raw_attribute *cur = raw_attributes;
|
||||
while (cur != NULL)
|
||||
{
|
||||
pmh_attr_type atype = pmh_attr_type_from_name(cur->name);
|
||||
pmh_style_attribute *attr = new_attr(cur->name, atype);
|
||||
attr->lang_element_type = lang_element_type;
|
||||
attr->value = new_attr_value();
|
||||
|
||||
if (atype == pmh_attr_type_foreground_color
|
||||
|| atype == pmh_attr_type_background_color
|
||||
|| atype == pmh_attr_type_caret_color
|
||||
|| atype == pmh_attr_type_strike_color)
|
||||
{
|
||||
char *hexstr = trim_str(cur->value);
|
||||
// new_argb_from_hex_str() reports conversion errors
|
||||
attr->value->argb_color =
|
||||
new_argb_from_hex_str(p_data, cur->line_number, hexstr);
|
||||
if (attr->value->argb_color == NULL) {
|
||||
free_style_attributes(attr);
|
||||
attr = NULL;
|
||||
}
|
||||
}
|
||||
else if (atype == pmh_attr_type_font_size_pt)
|
||||
{
|
||||
pmh_attr_font_size *fs = new_font_size();
|
||||
attr->value->font_size = fs;
|
||||
|
||||
char *trimmed_value = trim_str_dup(cur->value);
|
||||
|
||||
fs->is_relative = (*trimmed_value == '+' || *trimmed_value == '-');
|
||||
char *endptr = NULL;
|
||||
fs->size_pt = (int)strtol(cur->value, &endptr, 10);
|
||||
if (endptr == cur->value) {
|
||||
report_error(p_data, cur->line_number,
|
||||
"Value '%s' is invalid for attribute '%s'",
|
||||
cur->value, cur->name);
|
||||
free_style_attributes(attr);
|
||||
attr = NULL;
|
||||
}
|
||||
|
||||
free(trimmed_value);
|
||||
}
|
||||
else if (atype == pmh_attr_type_font_family)
|
||||
{
|
||||
attr->value->font_family = trim_str_dup(cur->value);
|
||||
}
|
||||
else if (atype == pmh_attr_type_font_style)
|
||||
{
|
||||
attr->value->font_styles = new_font_styles();
|
||||
multi_value *values = split_multi_value(cur->value, ',');
|
||||
multi_value *value_cur = values;
|
||||
while (value_cur != NULL)
|
||||
{
|
||||
char *standardized_value = standardize_str(value_cur->value);
|
||||
|
||||
if (EQUALS(standardized_value, "italic"))
|
||||
attr->value->font_styles->italic = true;
|
||||
else if (EQUALS(standardized_value, "bold"))
|
||||
attr->value->font_styles->bold = true;
|
||||
else if (EQUALS(standardized_value, "underlined"))
|
||||
attr->value->font_styles->underlined = true;
|
||||
else {
|
||||
report_error(p_data, cur->line_number,
|
||||
"Value '%s' is invalid for attribute '%s'",
|
||||
standardized_value, cur->name);
|
||||
}
|
||||
|
||||
free(standardized_value);
|
||||
value_cur = value_cur->next;
|
||||
}
|
||||
free_multi_value(values);
|
||||
}
|
||||
else if (atype == pmh_attr_type_other)
|
||||
{
|
||||
attr->value->string = trim_str_dup(cur->value);
|
||||
}
|
||||
|
||||
if (attr != NULL) {
|
||||
// add to linked list
|
||||
attr->next = attrs;
|
||||
attrs = attr;
|
||||
}
|
||||
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
return attrs;
|
||||
}
|
||||
|
||||
|
||||
static void interpret_and_add_style(style_parser_data *p_data,
|
||||
char *style_rule_name,
|
||||
int style_rule_line_number,
|
||||
raw_attribute *raw_attributes)
|
||||
{
|
||||
bool isEditorType = false;
|
||||
bool isCurrentLineType = false;
|
||||
bool isSelectionType = false;
|
||||
pmh_element_type type = pmh_element_type_from_name(style_rule_name);
|
||||
if (type == pmh_NO_TYPE)
|
||||
{
|
||||
if (EQUALS(style_rule_name, "editor"))
|
||||
isEditorType = true, type = pmh_NO_TYPE;
|
||||
else if (EQUALS(style_rule_name, "editor-current-line"))
|
||||
isCurrentLineType = true, type = pmh_NO_TYPE;
|
||||
else if (EQUALS(style_rule_name, "editor-selection"))
|
||||
isSelectionType = true, type = pmh_NO_TYPE;
|
||||
else {
|
||||
report_error(p_data, style_rule_line_number,
|
||||
"Style rule '%s' is not a language element type name or "
|
||||
"one of the following: 'editor', 'editor-current-line', "
|
||||
"'editor-selection'",
|
||||
style_rule_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pmh_style_attribute *attrs = interpret_attributes(p_data, type, raw_attributes);
|
||||
if (isEditorType)
|
||||
p_data->styles->editor_styles = attrs;
|
||||
else if (isCurrentLineType)
|
||||
p_data->styles->editor_current_line_styles = attrs;
|
||||
else if (isSelectionType)
|
||||
p_data->styles->editor_selection_styles = attrs;
|
||||
else
|
||||
p_data->styles->element_styles[(p_data->styles_pos)++] = attrs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static bool char_is_whitespace(char c)
|
||||
{
|
||||
return (c == ' ' || c == '\t');
|
||||
}
|
||||
|
||||
static bool char_begins_linecomment(char c)
|
||||
{
|
||||
return (c == '#');
|
||||
}
|
||||
|
||||
static bool line_is_comment(multi_value *line)
|
||||
{
|
||||
char *c;
|
||||
for (c = line->value; *c != '\0'; c++)
|
||||
{
|
||||
if (!char_is_whitespace(*c))
|
||||
return char_begins_linecomment(*c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool line_is_empty(multi_value *line)
|
||||
{
|
||||
char *c;
|
||||
for (c = line->value; *c != '\0'; c++)
|
||||
{
|
||||
if (!char_is_whitespace(*c))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct block
|
||||
{
|
||||
multi_value *lines;
|
||||
struct block *next;
|
||||
} block;
|
||||
|
||||
static block *new_block()
|
||||
{
|
||||
block *ret = (block *)malloc(sizeof(block));
|
||||
ret->next = NULL;
|
||||
ret->lines = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void free_blocks(block *val)
|
||||
{
|
||||
block *cur = val;
|
||||
while (cur != NULL)
|
||||
{
|
||||
block *this = cur;
|
||||
block *next = this->next;
|
||||
free_multi_value(this->lines);
|
||||
free(this);
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
static block *get_blocks(char *input)
|
||||
{
|
||||
block *head = NULL;
|
||||
block *tail = NULL;
|
||||
block *current_block = NULL;
|
||||
|
||||
multi_value *discarded_lines = NULL;
|
||||
|
||||
int line_number_counter = 1;
|
||||
|
||||
multi_value *lines = split_multi_value(input, '\n');
|
||||
multi_value *previous_line = NULL;
|
||||
multi_value *line_cur = lines;
|
||||
while (line_cur != NULL)
|
||||
{
|
||||
bool discard_line = false;
|
||||
|
||||
line_cur->line_number = line_number_counter++;
|
||||
|
||||
if (line_is_empty(line_cur))
|
||||
{
|
||||
discard_line = true;
|
||||
|
||||
if (current_block != NULL)
|
||||
{
|
||||
// terminate block
|
||||
if (tail != current_block)
|
||||
tail->next = current_block;
|
||||
tail = current_block;
|
||||
current_block = NULL;
|
||||
previous_line->next = NULL;
|
||||
}
|
||||
}
|
||||
else if (line_is_comment(line_cur))
|
||||
{
|
||||
// Do not discard (i.e. free()) comment lines within blocks:
|
||||
if (current_block == NULL)
|
||||
discard_line = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (current_block == NULL)
|
||||
{
|
||||
// start block
|
||||
current_block = new_block();
|
||||
current_block->lines = line_cur;
|
||||
if (previous_line != NULL)
|
||||
previous_line->next = NULL;
|
||||
}
|
||||
if (head == NULL) {
|
||||
head = current_block;
|
||||
tail = current_block;
|
||||
}
|
||||
}
|
||||
|
||||
multi_value *next_cur = line_cur->next;
|
||||
previous_line = (discard_line) ? NULL : line_cur;
|
||||
|
||||
if (discard_line) {
|
||||
line_cur->next = discarded_lines;
|
||||
discarded_lines = line_cur;
|
||||
}
|
||||
|
||||
line_cur = next_cur;
|
||||
}
|
||||
|
||||
if (current_block != NULL && tail != current_block)
|
||||
tail->next = current_block;
|
||||
|
||||
free_multi_value(discarded_lines);
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
|
||||
#define ASSIGNMENT_OP_UITEXT "':' or '='"
|
||||
#define IS_ASSIGNMENT_OP(c) ((c) == ':' || (c) == '=')
|
||||
#define IS_STYLE_RULE_NAME_CHAR(c) \
|
||||
( (c) != '\0' && !isspace(c) \
|
||||
&& !char_begins_linecomment(c) && !IS_ASSIGNMENT_OP(c) )
|
||||
#define IS_ATTRIBUTE_NAME_CHAR(c) \
|
||||
( (c) != '\0' && !char_begins_linecomment(c) && !IS_ASSIGNMENT_OP(c) )
|
||||
#define IS_ATTRIBUTE_VALUE_CHAR(c) \
|
||||
( (c) != '\0' && !char_begins_linecomment(c) )
|
||||
|
||||
static char *get_style_rule_name(multi_value *line)
|
||||
{
|
||||
char *str = line->value;
|
||||
|
||||
// Scan past leading whitespace:
|
||||
size_t start_index;
|
||||
for (start_index = 0;
|
||||
(*(str+start_index) != '\0' && isspace(*(str+start_index)));
|
||||
start_index++);
|
||||
|
||||
// Scan until style rule name characters end:
|
||||
size_t value_end_index;
|
||||
for (value_end_index = start_index;
|
||||
IS_STYLE_RULE_NAME_CHAR(*(str + value_end_index));
|
||||
value_end_index++);
|
||||
|
||||
// Copy style rule name:
|
||||
size_t value_len = value_end_index - start_index;
|
||||
char *value = (char *)malloc(sizeof(char)*value_len + 1);
|
||||
*value = '\0';
|
||||
strncat(value, (str + start_index), value_len);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool parse_attribute_line(style_parser_data *p_data, multi_value *line,
|
||||
char **out_attr_name, char **out_attr_value)
|
||||
{
|
||||
char *str = line->value;
|
||||
|
||||
// Scan past leading whitespace:
|
||||
size_t name_start_index;
|
||||
for (name_start_index = 0;
|
||||
( *(str+name_start_index) != '\0' &&
|
||||
isspace(*(str+name_start_index)) );
|
||||
name_start_index++);
|
||||
|
||||
// Scan until attribute name characters end:
|
||||
size_t name_end_index;
|
||||
for (name_end_index = name_start_index;
|
||||
IS_ATTRIBUTE_NAME_CHAR(*(str + name_end_index));
|
||||
name_end_index++);
|
||||
// Scan backwards to trim trailing whitespace off:
|
||||
while (name_start_index < name_end_index
|
||||
&& isspace(*(str + name_end_index - 1)))
|
||||
name_end_index--;
|
||||
|
||||
// Scan until just after the first assignment operator:
|
||||
size_t assignment_end_index;
|
||||
for (assignment_end_index = name_end_index;
|
||||
( *(str + assignment_end_index) != '\0' &&
|
||||
!IS_ASSIGNMENT_OP(*(str + assignment_end_index)) );
|
||||
assignment_end_index++);
|
||||
|
||||
// Scan over the found assignment operator, or report error:
|
||||
if (IS_ASSIGNMENT_OP(*(str + assignment_end_index)))
|
||||
assignment_end_index++;
|
||||
else
|
||||
{
|
||||
report_error(p_data, line->line_number,
|
||||
"Invalid attribute definition: str does not contain "
|
||||
"an assignment operator (%s): '%s'",
|
||||
ASSIGNMENT_OP_UITEXT, str);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t value_start_index = assignment_end_index;
|
||||
// Scan until attribute value characters end:
|
||||
size_t value_end_index;
|
||||
for (value_end_index = value_start_index;
|
||||
IS_ATTRIBUTE_VALUE_CHAR(*(str + value_end_index));
|
||||
value_end_index++);
|
||||
|
||||
// Copy attribute name:
|
||||
size_t name_len = name_end_index - name_start_index;
|
||||
char *attr_name = (char *)malloc(sizeof(char)*name_len + 1);
|
||||
*attr_name = '\0';
|
||||
strncat(attr_name, (str + name_start_index), name_len);
|
||||
*out_attr_name = attr_name;
|
||||
|
||||
// Copy attribute value:
|
||||
size_t attr_value_len = value_end_index - assignment_end_index;
|
||||
char *attr_value_str = (char *)malloc(sizeof(char)*attr_value_len + 1);
|
||||
*attr_value_str = '\0';
|
||||
strncat(attr_value_str, (str + assignment_end_index), attr_value_len);
|
||||
*out_attr_value = attr_value_str;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#define HAS_UTF8_BOM(x) ( ((*x & 0xFF) == 0xEF)\
|
||||
&& ((*(x+1) & 0xFF) == 0xBB)\
|
||||
&& ((*(x+2) & 0xFF) == 0xBF) )
|
||||
|
||||
// - Removes UTF-8 BOM
|
||||
// - Standardizes line endings to \n
|
||||
static char *strcpy_preformat_style(char *str)
|
||||
{
|
||||
char *new_str = (char *)malloc(sizeof(char) * strlen(str) + 1);
|
||||
|
||||
char *c = str;
|
||||
int i = 0;
|
||||
|
||||
if (HAS_UTF8_BOM(c))
|
||||
c += 3;
|
||||
|
||||
while (*c != '\0')
|
||||
{
|
||||
if (*c == '\r' && *(c+1) == '\n')
|
||||
{
|
||||
*(new_str+i) = '\n';
|
||||
i++;
|
||||
c += 2;
|
||||
}
|
||||
else if (*c == '\r')
|
||||
{
|
||||
*(new_str+i) = '\n';
|
||||
i++;
|
||||
c++;
|
||||
}
|
||||
else
|
||||
{
|
||||
*(new_str+i) = *c;
|
||||
i++;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
*(new_str+i) = '\0';
|
||||
|
||||
return new_str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void _sty_parse(style_parser_data *p_data)
|
||||
{
|
||||
// We don't have to worry about leaking the original p_data->input;
|
||||
// the user of the library is responsible for that:
|
||||
p_data->input = strcpy_preformat_style(p_data->input);
|
||||
|
||||
block *blocks = get_blocks(p_data->input);
|
||||
|
||||
block *block_cur = blocks;
|
||||
while (block_cur != NULL)
|
||||
{
|
||||
pmhsp_PRINTF("Block:\n");
|
||||
multi_value *header_line = block_cur->lines;
|
||||
if (header_line == NULL) {
|
||||
block_cur = block_cur->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
pmhsp_PRINTF(" Head line (len %ld): '%s'\n",
|
||||
header_line->length, header_line->value);
|
||||
char *style_rule_name = get_style_rule_name(header_line);
|
||||
pmhsp_PRINTF(" Style rule name: '%s'\n", style_rule_name);
|
||||
|
||||
multi_value *attr_line_cur = header_line->next;
|
||||
if (attr_line_cur == NULL)
|
||||
report_error(p_data, header_line->line_number,
|
||||
"No style attributes defined for style rule '%s'",
|
||||
style_rule_name);
|
||||
|
||||
raw_attribute *attributes_head = NULL;
|
||||
raw_attribute *attributes_tail = NULL;
|
||||
|
||||
while (attr_line_cur != NULL)
|
||||
{
|
||||
if (line_is_comment(attr_line_cur))
|
||||
{
|
||||
attr_line_cur = attr_line_cur->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
pmhsp_PRINTF(" Attr line (len %ld): '%s'\n",
|
||||
attr_line_cur->length, attr_line_cur->value);
|
||||
char *attr_name_str;
|
||||
char *attr_value_str;
|
||||
bool success = parse_attribute_line(p_data,
|
||||
attr_line_cur,
|
||||
&attr_name_str,
|
||||
&attr_value_str);
|
||||
if (success)
|
||||
{
|
||||
pmhsp_PRINTF(" Attr: '%s' Value: '%s'\n",
|
||||
attr_name_str, attr_value_str);
|
||||
raw_attribute *attribute =
|
||||
new_raw_attribute(attr_name_str, attr_value_str,
|
||||
attr_line_cur->line_number);
|
||||
if (attributes_head == NULL) {
|
||||
attributes_head = attribute;
|
||||
attributes_tail = attribute;
|
||||
} else {
|
||||
attributes_tail->next = attribute;
|
||||
attributes_tail = attribute;
|
||||
}
|
||||
}
|
||||
|
||||
attr_line_cur = attr_line_cur->next;
|
||||
}
|
||||
|
||||
if (attributes_head != NULL)
|
||||
{
|
||||
interpret_and_add_style(p_data, style_rule_name,
|
||||
header_line->line_number, attributes_head);
|
||||
free_raw_attributes(attributes_head);
|
||||
}
|
||||
|
||||
free(style_rule_name);
|
||||
|
||||
block_cur = block_cur->next;
|
||||
}
|
||||
|
||||
free_blocks(blocks);
|
||||
free(p_data->input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static pmh_style_collection *new_style_collection()
|
||||
{
|
||||
pmh_style_collection *sc = (pmh_style_collection *)
|
||||
malloc(sizeof(pmh_style_collection));
|
||||
|
||||
sc->element_styles = (pmh_style_attribute**)
|
||||
malloc(sizeof(pmh_style_attribute*)
|
||||
* pmh_NUM_LANG_TYPES);
|
||||
int i;
|
||||
for (i = 0; i < pmh_NUM_LANG_TYPES; i++)
|
||||
sc->element_styles[i] = NULL;
|
||||
|
||||
sc->editor_styles = NULL;
|
||||
sc->editor_current_line_styles = NULL;
|
||||
sc->editor_selection_styles = NULL;
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
void pmh_free_style_collection(pmh_style_collection *coll)
|
||||
{
|
||||
free_style_attributes(coll->editor_styles);
|
||||
free_style_attributes(coll->editor_current_line_styles);
|
||||
free_style_attributes(coll->editor_selection_styles);
|
||||
int i;
|
||||
for (i = 0; i < pmh_NUM_LANG_TYPES; i++)
|
||||
free_style_attributes(coll->element_styles[i]);
|
||||
free(coll->element_styles);
|
||||
free(coll);
|
||||
}
|
||||
|
||||
static style_parser_data *new_style_parser_data(char *input)
|
||||
{
|
||||
style_parser_data *p_data = (style_parser_data*)
|
||||
malloc(sizeof(style_parser_data));
|
||||
p_data->input = input;
|
||||
p_data->styles_pos = 0;
|
||||
p_data->styles = new_style_collection();
|
||||
return p_data;
|
||||
}
|
||||
|
||||
pmh_style_collection *pmh_parse_styles(char *input,
|
||||
void(*error_callback)(char*,int,void*),
|
||||
void *error_callback_context)
|
||||
{
|
||||
style_parser_data *p_data = new_style_parser_data(input);
|
||||
p_data->error_callback = error_callback;
|
||||
p_data->error_callback_context = error_callback_context;
|
||||
|
||||
_sty_parse(p_data);
|
||||
|
||||
pmh_style_collection *ret = p_data->styles;
|
||||
free(p_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
148
utils/peg-highlight/pmh_styleparser.h
Normal file
148
utils/peg-highlight/pmh_styleparser.h
Normal file
@ -0,0 +1,148 @@
|
||||
/* PEG Markdown Highlight
|
||||
* Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
|
||||
* Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
|
||||
*
|
||||
* pmh_styleparser.h
|
||||
*
|
||||
* Public interface of a parser for custom syntax highlighting stylesheets.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief Style parser public interface.
|
||||
*/
|
||||
|
||||
#include "pmh_definitions.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* \brief Color (ARGB) attribute value.
|
||||
*
|
||||
* All values are 0-255.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int red; /**< Red color component (0-255) */
|
||||
int green; /**< Green color component (0-255) */
|
||||
int blue; /**< Blue color component (0-255) */
|
||||
int alpha; /**< Alpha (opacity) color component (0-255) */
|
||||
} pmh_attr_argb_color;
|
||||
|
||||
/** \brief Font style attribute value. */
|
||||
typedef struct
|
||||
{
|
||||
bool italic;
|
||||
bool bold;
|
||||
bool underlined;
|
||||
} pmh_attr_font_styles;
|
||||
|
||||
/** \brief Font size attribute value. */
|
||||
typedef struct
|
||||
{
|
||||
int size_pt; /**< The font point size */
|
||||
bool is_relative; /**< Whether the size is relative (i.e. size_pt points
|
||||
larger than the default font) */
|
||||
} pmh_attr_font_size;
|
||||
|
||||
/** \brief Style attribute types. */
|
||||
typedef enum
|
||||
{
|
||||
pmh_attr_type_foreground_color, /**< Foreground color */
|
||||
pmh_attr_type_background_color, /**< Background color */
|
||||
pmh_attr_type_caret_color, /**< Caret (insertion point) color */
|
||||
pmh_attr_type_font_size_pt, /**< Font size (in points) */
|
||||
pmh_attr_type_font_family, /**< Font family */
|
||||
pmh_attr_type_font_style, /**< Font style */
|
||||
pmh_attr_type_strike_color, /**< Strike-through color */
|
||||
pmh_attr_type_other /**< Arbitrary custom attribute */
|
||||
} pmh_attr_type;
|
||||
|
||||
/**
|
||||
* \brief Style attribute value.
|
||||
*
|
||||
* Determine which member to access in this union based on the
|
||||
* 'type' value of the pmh_style_attribute.
|
||||
*
|
||||
* \sa pmh_style_attribute
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
pmh_attr_argb_color *argb_color; /**< ARGB color */
|
||||
pmh_attr_font_styles *font_styles; /**< Font styles */
|
||||
pmh_attr_font_size *font_size; /**< Font size */
|
||||
char *font_family; /**< Font family */
|
||||
char *string; /**< Arbitrary custom string value
|
||||
(use this if the attribute's type
|
||||
is pmh_attr_type_other) */
|
||||
} pmh_attr_value;
|
||||
|
||||
/** \brief Style attribute. */
|
||||
typedef struct pmh_style_attribute
|
||||
{
|
||||
pmh_element_type lang_element_type; /**< The Markdown language element this
|
||||
style applies to */
|
||||
pmh_attr_type type; /**< The type of the attribute */
|
||||
char *name; /**< The name of the attribute (if type
|
||||
is pmh_attr_type_other, you can
|
||||
use this value to determine what
|
||||
the attribute is) */
|
||||
pmh_attr_value *value; /**< The value of the attribute */
|
||||
struct pmh_style_attribute *next; /**< Next attribute in linked list */
|
||||
} pmh_style_attribute;
|
||||
|
||||
/** \brief Collection of styles. */
|
||||
typedef struct
|
||||
{
|
||||
/** Styles that apply to the editor in general */
|
||||
pmh_style_attribute *editor_styles;
|
||||
|
||||
/** Styles that apply to the line in the editor where the caret (insertion
|
||||
point) resides */
|
||||
pmh_style_attribute *editor_current_line_styles;
|
||||
|
||||
/** Styles that apply to the range of selected text in the editor */
|
||||
pmh_style_attribute *editor_selection_styles;
|
||||
|
||||
/** Styles that apply to specific Markdown language elements */
|
||||
pmh_style_attribute **element_styles;
|
||||
} pmh_style_collection;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Parse stylesheet string, return style collection
|
||||
*
|
||||
* \param[in] input The stylesheet string to parse.
|
||||
* \param[in] error_callback Callback function to be called when errors
|
||||
* occur during parsing. The first argument
|
||||
* to the callback function is the error
|
||||
* message and the second one the line number
|
||||
* in the original input where the error
|
||||
* occurred. The last argument will always
|
||||
* get the value you pass in for the
|
||||
* error_callback_context argument to this
|
||||
* function.
|
||||
* Pass in NULL to suppress error reporting.
|
||||
* \param[in] error_callback_context Arbitrary context pointer for the error
|
||||
* callback function; will be passed in as
|
||||
* the last argument to error_callback.
|
||||
*
|
||||
* \return A pmh_style_collection. You must pass this value to
|
||||
* pmh_free_style_collection() when it's not needed anymore.
|
||||
*/
|
||||
pmh_style_collection *pmh_parse_styles(char *input,
|
||||
void(*error_callback)(char*,int,void*),
|
||||
void *error_callback_context);
|
||||
|
||||
/**
|
||||
* \brief Free a pmh_style_collection.
|
||||
*
|
||||
* Frees a pmh_style_collection value returned by pmh_parse_styles().
|
||||
*
|
||||
* \param[in] collection The collection to free.
|
||||
*/
|
||||
void pmh_free_style_collection(pmh_style_collection *collection);
|
||||
|
||||
|
||||
char *pmh_attr_name_from_type(pmh_attr_type type);
|
||||
|
||||
pmh_attr_type pmh_attr_type_from_name(char *name);
|
||||
|
8
utils/peg-highlight/styles/error.style
Normal file
8
utils/peg-highlight/styles/error.style
Normal file
@ -0,0 +1,8 @@
|
||||
NONEXISTENT_TYPE
|
||||
x: 3
|
||||
|
||||
STRONG
|
||||
font-style: funkadelic, bold, snazzy
|
||||
foreground: 13bx12
|
||||
background: 5
|
||||
|
87
utils/peg-highlight/styles/fontsizes.style
Normal file
87
utils/peg-highlight/styles/fontsizes.style
Normal file
@ -0,0 +1,87 @@
|
||||
# Styles using 'Solarized' color scheme
|
||||
# by Ethan Schoonover: http://ethanschoonover.com/solarized
|
||||
#
|
||||
# (dark background version)
|
||||
|
||||
editor
|
||||
foreground: 93a1a1 # base1
|
||||
background: 002b36 # base03
|
||||
caret: ffffff
|
||||
font-size: 13
|
||||
|
||||
H1
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
font-size: +6
|
||||
|
||||
H2
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
font-size: +5
|
||||
|
||||
H3
|
||||
foreground: 6c71c4 # violet
|
||||
font-size: +4
|
||||
|
||||
H4
|
||||
foreground: 268bd2 # blue
|
||||
font-size: +3
|
||||
|
||||
H5
|
||||
foreground: 268bd2 # blue
|
||||
font-size: +2
|
||||
|
||||
H6
|
||||
foreground: 268bd2 # blue
|
||||
font-size: +1
|
||||
|
||||
HRULE
|
||||
foreground: 586e75 # base01
|
||||
|
||||
LIST_BULLET
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LIST_ENUMERATOR
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LINK
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_URL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_EMAIL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
IMAGE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
REFERENCE
|
||||
foreground: 80b58900 # yellow, reduced alpha
|
||||
font-size: -2
|
||||
|
||||
CODE
|
||||
foreground: 859900 # green
|
||||
|
||||
EMPH
|
||||
foreground: cb4b16 # orange
|
||||
font-style: italic
|
||||
|
||||
STRONG
|
||||
foreground: dc322f # red
|
||||
font-style: bold
|
||||
|
||||
HTML_ENTITY
|
||||
foreground: 6c71c4 # violet
|
||||
|
||||
COMMENT
|
||||
foreground: 93a1a1 # base1
|
||||
|
||||
VERBATIM
|
||||
foreground: 859900 # green
|
||||
|
||||
BLOCKQUOTE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
STRIKE
|
||||
strike-color: 93a1a1 # base1
|
1
utils/peg-highlight/styles/macoslineseparator.style
Normal file
1
utils/peg-highlight/styles/macoslineseparator.style
Normal file
@ -0,0 +1 @@
|
||||
# linecomment
editor # comment
foreground : 13ff13
background : 000000 # comment
# linecomment
STRONG:
EMPH=
# comment
foreground: 00ff00
comment asd
background: AB0000ff
STRONG :
dog: 1 # something
cat: 4
font-style: underlined, Italic , BoLD #hi, hello
font-size: 14pt
font-family: Courier New, Times
# linecomment
BOO
x: 3
editor-selection:
foreground: abcdef
background: abcdef
editor-current-line:
background: ffffff
|
22
utils/peg-highlight/styles/playground.style
Normal file
22
utils/peg-highlight/styles/playground.style
Normal file
@ -0,0 +1,22 @@
|
||||
editor :
|
||||
foreground : 13ff13
|
||||
background : 000000
|
||||
caret: ffffff
|
||||
|
||||
EMPH
|
||||
font-style: italic
|
||||
|
||||
STRONG
|
||||
font-style: bold
|
||||
|
||||
LINK
|
||||
font-style: underlined
|
||||
|
||||
editor-selection:
|
||||
foreground: ff0000
|
||||
background: eeeeee
|
||||
font-style: underlined
|
||||
|
||||
editor-current-line:
|
||||
background: ffffff
|
||||
|
79
utils/peg-highlight/styles/solarized-dark.style
Normal file
79
utils/peg-highlight/styles/solarized-dark.style
Normal file
@ -0,0 +1,79 @@
|
||||
# Styles using 'Solarized' color scheme
|
||||
# by Ethan Schoonover: http://ethanschoonover.com/solarized
|
||||
#
|
||||
# (dark background version)
|
||||
|
||||
editor
|
||||
foreground: 93a1a1 # base1
|
||||
background: 002b36 # base03
|
||||
caret: ffffff
|
||||
|
||||
H1
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
|
||||
H2
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
|
||||
H3
|
||||
foreground: 6c71c4 # violet
|
||||
|
||||
H4
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
H5
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
H6
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
HRULE
|
||||
foreground: 586e75 # base01
|
||||
|
||||
LIST_BULLET
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LIST_ENUMERATOR
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LINK
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_URL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_EMAIL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
IMAGE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
REFERENCE
|
||||
foreground: 80b58900 # yellow, reduced alpha
|
||||
|
||||
CODE
|
||||
foreground: 859900 # green
|
||||
|
||||
EMPH
|
||||
foreground: cb4b16 # orange
|
||||
font-style: italic
|
||||
|
||||
STRONG
|
||||
foreground: dc322f # red
|
||||
font-style: bold
|
||||
|
||||
HTML_ENTITY
|
||||
foreground: 6c71c4 # violet
|
||||
|
||||
COMMENT
|
||||
foreground: 93a1a1 # base1
|
||||
|
||||
VERBATIM
|
||||
foreground: 859900 # green
|
||||
|
||||
BLOCKQUOTE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
STRIKE
|
||||
strike-color: 93a1a1 # base1
|
80
utils/peg-highlight/styles/solarized-light.style
Normal file
80
utils/peg-highlight/styles/solarized-light.style
Normal file
@ -0,0 +1,80 @@
|
||||
# Styles using 'Solarized' color scheme
|
||||
# by Ethan Schoonover: http://ethanschoonover.com/solarized
|
||||
#
|
||||
# (light background version)
|
||||
|
||||
editor
|
||||
foreground: 586e75 # base01
|
||||
background: fdf6e3 # base3
|
||||
caret: 000000
|
||||
|
||||
H1
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
|
||||
H2
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
|
||||
H3
|
||||
foreground: 6c71c4 # violet
|
||||
|
||||
H4
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
H5
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
H6
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
HRULE
|
||||
foreground: 586e75 # base01
|
||||
|
||||
LIST_BULLET
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LIST_ENUMERATOR
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LINK
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_URL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_EMAIL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
IMAGE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
REFERENCE
|
||||
foreground: 80b58900 # yellow, reduced alpha
|
||||
|
||||
CODE
|
||||
foreground: 859900 # green
|
||||
|
||||
EMPH
|
||||
foreground: cb4b16 # orange
|
||||
font-style: italic
|
||||
|
||||
STRONG
|
||||
foreground: dc322f # red
|
||||
font-style: bold
|
||||
|
||||
HTML_ENTITY
|
||||
foreground: 6c71c4 # violet
|
||||
|
||||
COMMENT
|
||||
foreground: 93a1a1 # base1
|
||||
|
||||
VERBATIM
|
||||
foreground: 859900 # green
|
||||
|
||||
BLOCKQUOTE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
STRIKE
|
||||
strike-color: 586e75 # base01
|
||||
|
33
utils/peg-highlight/styles/teststyle.style
Normal file
33
utils/peg-highlight/styles/teststyle.style
Normal file
@ -0,0 +1,33 @@
|
||||
# linecomment
|
||||
|
||||
editor # comment
|
||||
foreground : 13ff13
|
||||
background : 000000 # comment
|
||||
|
||||
# linecomment
|
||||
STRONG:
|
||||
|
||||
EMPH=
|
||||
# comment
|
||||
foreground: 00ff00
|
||||
comment asd
|
||||
background: AB0000ff
|
||||
|
||||
STRONG :
|
||||
dog: 1 # something
|
||||
cat: 4
|
||||
font-style: underlined, Italic , BoLD #hi, hello
|
||||
font-size: 14pt
|
||||
font-family: Courier New, Times
|
||||
|
||||
# linecomment
|
||||
BOO
|
||||
x: 3
|
||||
|
||||
editor-selection:
|
||||
foreground: abcdef
|
||||
background: abcdef
|
||||
|
||||
editor-current-line:
|
||||
background: ffffff
|
||||
|
33
utils/peg-highlight/styles/winlineseparator.style
Normal file
33
utils/peg-highlight/styles/winlineseparator.style
Normal file
@ -0,0 +1,33 @@
|
||||
# linecomment
|
||||
|
||||
editor # comment
|
||||
foreground : 13ff13
|
||||
background : 000000 # comment
|
||||
|
||||
# linecomment
|
||||
STRONG:
|
||||
|
||||
EMPH=
|
||||
# comment
|
||||
foreground: 00ff00
|
||||
comment asd
|
||||
background: AB0000ff
|
||||
|
||||
STRONG :
|
||||
dog: 1 # something
|
||||
cat: 4
|
||||
font-style: underlined, Italic , BoLD #hi, hello
|
||||
font-size: 14pt
|
||||
font-family: Courier New, Times
|
||||
|
||||
# linecomment
|
||||
BOO
|
||||
x: 3
|
||||
|
||||
editor-selection:
|
||||
foreground: abcdef
|
||||
background: abcdef
|
||||
|
||||
editor-current-line:
|
||||
background: ffffff
|
||||
|
199
utils/peg-highlight/stylesheet_syntax.md
Normal file
199
utils/peg-highlight/stylesheet_syntax.md
Normal file
@ -0,0 +1,199 @@
|
||||
|
||||
The Syntax of PEG Markdown Highlight Stylesheets
|
||||
================================================
|
||||
|
||||
[PEG Markdown Highlight][pmh] includes a parser for stylesheets that define how different Markdown language elements are to be highlighted. This document describes the syntax of these stylesheets.
|
||||
|
||||
[pmh]: http://hasseg.org/peg-markdown-highlight/
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
Here is a quick, simple example of a stylesheet:
|
||||
|
||||
<style>
|
||||
.codetable { border-collapse: collapse; }
|
||||
.codetable .left { text-align: right; padding-right: 10px; }
|
||||
.codetable .right { text-align: left; padding-left: 10px; }
|
||||
.codetable .content { font-family: monospace; background: #eee; padding: 0 5px; }
|
||||
.codetable .comment { color: #174EB3; }
|
||||
.codetable .rule { color: #491B8F; }
|
||||
.codetable .attrname { color: #48B317; }
|
||||
.codetable .attrvalue { color: #A65C1F; }
|
||||
</style>
|
||||
|
||||
<table class="codetable">
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"># The first comment lines</td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"># describe the stylesheet.</td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"> </td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><span class="rule">Style rule →</span></td>
|
||||
<td class="content"><span class="rule">editor:</span></td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"> foreground: ff0000 <span class="comment"># red text</span></td>
|
||||
<td class="right"><span class="comment">← Comment</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><span class="attrname">Attribute name →</span></td>
|
||||
<td class="content"> <span class="attrname">font-family</span>: <span class="attrvalue">Consolas</span></td>
|
||||
<td class="right"><span class="attrvalue">← Attribute value</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"> </td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content">EMPH:</td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"> font-size: 14</td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"> font-style: bold, underlined</td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
Style Rules
|
||||
-----------
|
||||
|
||||
A stylesheet is composed of one or more *rules*. Rules are separated from each other by **empty lines** like so:
|
||||
|
||||
H2:
|
||||
foreground: ff0000
|
||||
|
||||
H3:
|
||||
foreground: 00ff00
|
||||
|
||||
Each begins with the ***name* of the rule**, which is always on its own line, and may be one of the following:
|
||||
|
||||
- **`editor`**: Styles that apply to the whole document/editor
|
||||
- **`editor-current-line`**: Styles that apply to the current line in the editor (i.e. the line where the caret is)
|
||||
- **`editor-selection`**: Styles that apply to the selected range in the editor when the user makes a selection in the text
|
||||
- A Markdown element type (like `EMPH`, `REFERENCE` or `H1`): Styles that apply to occurrences of that particular element. The supported element types are:
|
||||
- **`LINK`:** Explicit link (like `[click here][ref]`)
|
||||
- **`AUTO_LINK_URL`:** Implicit URL link (like `<http://google.com>`)
|
||||
- **`AUTO_LINK_EMAIL`:** Implicit email link (like `<first.last@google.com>`)
|
||||
- **`IMAGE`:** Image definition
|
||||
- **`REFERENCE`:** Reference (like `[id]: http://www.google.com`)
|
||||
- **`CODE`:** Inline code
|
||||
- **`EMPH`:** Emphasized text
|
||||
- **`STRONG`:** Strong text
|
||||
- **`LIST_BULLET`:** Bullet for an unordered list item
|
||||
- **`LIST_ENUMERATOR`:** Enumerator for an ordered list item
|
||||
- **`H1`:** Header, level 1
|
||||
- **`H2`:** Header, level 2
|
||||
- **`H3`:** Header, level 3
|
||||
- **`H4`:** Header, level 4
|
||||
- **`H5`:** Header, level 5
|
||||
- **`H6`:** Header, level 6
|
||||
- **`BLOCKQUOTE`:** Blockquote marker
|
||||
- **`VERBATIM`:** Block of code
|
||||
- **`HRULE`:** Horizontal rule
|
||||
- **`HTML`:** HTML tag
|
||||
- **`HTML_ENTITY`:** HTML special entity definition (like `…`)
|
||||
- **`HTMLBLOCK`:** Block of HTML
|
||||
- **`COMMENT`:** (HTML) Comment
|
||||
- **`NOTE`:** Note
|
||||
- **`STRIKE`:** Strike-through
|
||||
|
||||
The name may be optionally followed by an assignment operator (either `:` or `=`):
|
||||
|
||||
H1:
|
||||
foreground: ff00ff
|
||||
|
||||
H2 =
|
||||
foreground: ff0000
|
||||
|
||||
H3
|
||||
foreground: 00ff00
|
||||
|
||||
The **order of style rules is significant**; it defines the order in which different language elements should be highlighted. *(Of course applications that use PEG Markdown Highlight and the style parser may disregard this and highlight elements in whatever order they desire.)*
|
||||
|
||||
After the name of the rule, there can be one or more *attributes*.
|
||||
|
||||
|
||||
Style Attributes
|
||||
----------------
|
||||
|
||||
Attribute assignments are each on their own line, and they consist of the *name* of the attribute as well as the *value* assigned to it. An assignment operator (either `:` or `=`) separates the name from the value:
|
||||
|
||||
attribute-name: value
|
||||
attribute-name= value
|
||||
|
||||
Attribute assignment lines **may be indented**.
|
||||
|
||||
### Attribute Names and Types
|
||||
|
||||
The following is a list of the names of predefined attributes, and the values they may be assigned:
|
||||
|
||||
- `foreground-color` *(aliases: `foreground` and `color`)*
|
||||
- See the *Color Attribute Values* subsection for information about valid values for this attribute.
|
||||
- `background-color` *(alias: `background`)*
|
||||
- See the *Color Attribute Values* subsection for information about valid values for this attribute.
|
||||
- `caret-color` *(alias: `caret`)*
|
||||
- See the *Color Attribute Values* subsection for information about valid values for this attribute.
|
||||
- `strike-color` *(alias: `strike`)*
|
||||
- See the *Color Attribute Values* subsection for information about valid values for this attribute.
|
||||
- `font-size`
|
||||
- An integer value for the font size, *in points* (i.e. not in pixels). The number may have a textual suffix such as `pt`.
|
||||
- If the value begins with `+` or `-`, it is considered *relative* to some base font size (as defined by the host application). For example, the value `3` defines the font size as 3 (absolute) while `+3` defines it as +3 (relative), i.e. 3 point sizes larger than the base font size.
|
||||
- `font-family`
|
||||
- A comma-separated list of one or more arbitrary font family names. *(It is up to the application that uses the PEG Markdown Highlight library to resolve this string to actual fonts on the system.)*
|
||||
- `font-style`
|
||||
- A comma-separated list of one or more of the following:
|
||||
- `italic`
|
||||
- `bold`
|
||||
- `underlined`
|
||||
|
||||
Applications may also include support for any **custom attribute names and values** they desire — attributes other than the ones listed above will be included in the style parser results, with their values stored as strings.
|
||||
|
||||
|
||||
## Color Attribute Values
|
||||
|
||||
Colors can be specified either in **RGB** (red, green, blue) or **ARGB** (alpha, red, green, blue) formats. In both, each component is a two-character hexadecimal value (from `00` to `FF`):
|
||||
|
||||
foreground: ff00ee # red = ff, green = 00, blue = ee (and implicitly, alpha = ff)
|
||||
background: 99ff00ee # alpha = 99, red = ff, green = 00, blue = ee
|
||||
|
||||
|
||||
Comments
|
||||
--------
|
||||
|
||||
Each line in a stylesheet may have a comment. The `#` character begins a line comment that continues until the end of the line:
|
||||
|
||||
# this line has only this comment
|
||||
H1: # this line has a style rule name and then a comment
|
||||
foreground: ff0000 # this line has an attribute and then a comment
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "vnote.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "vpreviewpage.h"
|
||||
#include "hgmarkdownhighlighter.h"
|
||||
|
||||
VEditor::VEditor(const QString &path, const QString &name, bool modifiable,
|
||||
QWidget *parent)
|
||||
@ -18,6 +19,7 @@ VEditor::VEditor(const QString &path, const QString &name, bool modifiable,
|
||||
noteFile = new VNoteFile(path, name, fileText, docType, modifiable);
|
||||
|
||||
isEditMode = false;
|
||||
mdHighlighter = NULL;
|
||||
|
||||
setupUI();
|
||||
|
||||
@ -38,6 +40,8 @@ void VEditor::setupUI()
|
||||
case DocType::Markdown:
|
||||
setupMarkdownPreview();
|
||||
textBrowser = NULL;
|
||||
|
||||
mdHighlighter = new HGMarkdownHighlighter(textEditor->document(), 500);
|
||||
break;
|
||||
|
||||
case DocType::Html:
|
||||
|
@ -10,6 +10,7 @@
|
||||
class QTextBrowser;
|
||||
class VEdit;
|
||||
class QWebEngineView;
|
||||
class HGMarkdownHighlighter;
|
||||
|
||||
class VEditor : public QStackedWidget
|
||||
{
|
||||
@ -38,6 +39,7 @@ private:
|
||||
VEdit *textEditor;
|
||||
QWebEngineView *webPreviewer;
|
||||
VDocument document;
|
||||
HGMarkdownHighlighter *mdHighlighter;
|
||||
};
|
||||
|
||||
#endif // VEDITOR_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user