diff --git a/ChangeLog b/ChangeLog index c8d4666d3b..4cc3b1c975 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,13 @@ Rename 'Reflow lines/paragraph' to 'Reflow lines/block' because in future using an indent block is more useful e.g. for ChangeLog files. + * scintilla/LexMarkdown.cxx, scintilla/makefile.win32, + scintilla/include/SciLexer.h, scintilla/include/Scintilla.iface, + scintilla/KeyWords.cxx, scintilla/Makefile.am, src/highlighting.c, + src/about.c, src/filetypes.c, src/filetypes.h, THANKS, + tagmanager/parsers.h, tagmanager/makefile.win32, + tagmanager/markdown.c, tagmanager/Makefile.am, wscript: + Add Markdown filetype (patch by Jon Strait, thanks). 2009-07-29 Nick Treleaven diff --git a/THANKS b/THANKS index 2d7f50a61d..007a726cce 100644 --- a/THANKS +++ b/THANKS @@ -66,6 +66,7 @@ Simon Treny - Documents sidebar stock icons pat Elias Pschernig - Recent Projects menu patch Jesse Mayes - Tango'ish Save All icon Eugene Arshinov - Reload color schemes, split lines KB patches +Jon Strait - Markdown filetype patch Translators: ------------ diff --git a/scintilla/KeyWords.cxx b/scintilla/KeyWords.cxx index 7241ccf65c..2e05163aa0 100644 --- a/scintilla/KeyWords.cxx +++ b/scintilla/KeyWords.cxx @@ -158,6 +158,7 @@ int Scintilla_LinkLexers() { LINK_LEXER(lmLatex); LINK_LEXER(lmLua); LINK_LEXER(lmMake); + LINK_LEXER(lmMarkdown); LINK_LEXER(lmMatlab); LINK_LEXER(lmNsis); LINK_LEXER(lmNull); diff --git a/scintilla/LexMarkdown.cxx b/scintilla/LexMarkdown.cxx new file mode 100644 index 0000000000..34f79ac225 --- /dev/null +++ b/scintilla/LexMarkdown.cxx @@ -0,0 +1,433 @@ +/****************************************************************** + * LexMarkdown.cxx + * + * A simple Markdown lexer for scintilla. + * + * Includes highlighting for some extra features from the + * Pandoc implementation; strikeout, using '#.' as a default + * ordered list item marker, and delimited code blocks. + * + * Limitations: + * + * Standard indented code blocks are not highlighted at all, + * as it would conflict with other indentation schemes. Use + * delimited code blocks for blanket highlighting of an + * entire code block. Embedded HTML is not highlighted either. + * Blanket HTML highlighting has issues, because some Markdown + * implementations allow Markdown markup inside of the HTML. Also, + * there is a following blank line issue that can't be ignored, + * explained in the next paragraph. Embedded HTML and code + * blocks would be better supported with language specific + * highlighting; something Scintilla isn't really architected + * to support yet. + * + * The highlighting aims to accurately reflect correct syntax, + * but a few restrictions are relaxed. Delimited code blocks are + * highlighted, even if the line following the code block is not blank. + * Requiring a blank line after a block, breaks the highlighting + * in certain cases, because of the way Scintilla ends up calling + * the lexer. + * + * Written by Jon Strait - jstrait@moonloop.net + * + * This source code is released for free distribution under the + * terms of the GNU General Public License. + * + *****************************************************************/ + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +static inline bool IsNewline(const int ch) { + return (ch == '\n' || ch == '\r'); +} + +// True if can follow ch down to the end with possibly trailing whitespace +static bool FollowToLineEnd(const int ch, const int state, const int endPos, StyleContext &sc) { + int i = 0; + while (sc.GetRelative(++i) == ch) + ; + // Skip over whitespace + while (IsASpaceOrTab(sc.GetRelative(i)) && sc.currentPos + i < endPos) + ++i; + if (IsNewline(sc.GetRelative(i)) || sc.currentPos + i == endPos) { + sc.Forward(i); + sc.ChangeState(state); + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + return true; + } + else return false; +} + +// Set the state on text section from current to length characters, +// then set the rest until the newline to default, except for any characters matching token +static void SetStateAndZoom(const int state, const int length, const int token, StyleContext &sc) { + int i = 0; + sc.SetState(state); + sc.Forward(length); + sc.SetState(SCE_MARKDOWN_DEFAULT); + sc.Forward(); + while (sc.More() && !IsNewline(sc.ch)) { + bool started = false; + if (sc.ch == token && !started) { + sc.SetState(state); + started = true; + } + else if (sc.ch != token) { + sc.SetState(SCE_MARKDOWN_DEFAULT); + started = false; + } + sc.Forward(); + } + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); +} + +// Does the previous line have more than spaces and tabs? +static bool HasPrevLineContent(StyleContext &sc) { + int i = 0; + // Go back to the previous newline + while ((--i + sc.currentPos) && !IsNewline(sc.GetRelative(i))) + ; + while (--i + sc.currentPos) { + if (IsNewline(sc.GetRelative(i))) + break; + if (!IsASpaceOrTab(sc.GetRelative(i))) + return true; + } + return false; +} + +static bool IsValidHrule(const int endPos, StyleContext &sc) { + int c, i = 0, count = 1; + while (++i) { + c = sc.GetRelative(i); + if (c == sc.ch) + ++count; + // hit a terminating character + else if (!IsASpaceOrTab(c) || sc.currentPos + i == endPos) { + // Are we a valid HRULE + if ((IsNewline(c) || sc.currentPos + i == endPos) && + count >= 3 && !HasPrevLineContent(sc)) { + sc.SetState(SCE_MARKDOWN_HRULE); + sc.Forward(i); + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + return true; + } + else { + sc.SetState(SCE_MARKDOWN_DEFAULT); + return false; + } + } + } +} + +// Only consume if already valid. Doesn't work for delimiting multiple lines. +static void ConsumeEnd(const int state, const int origPos, const int endPos, + const char *token, StyleContext &sc) { + int targetPos; + while (sc.currentPos + 1 < endPos) { + sc.Forward(); + if (sc.Match(token) && sc.chPrev != '\\' && sc.chPrev != ' ') { + targetPos = sc.currentPos + strlen(token); + sc.currentPos = origPos; + sc.SetState(state); + sc.Forward(targetPos - origPos); + sc.SetState(SCE_MARKDOWN_DEFAULT); + break; + } + } +} + +static void ColorizeMarkdownDoc(unsigned int startPos, int length, int initStyle, + WordList *keywordlists[], Accessor &styler) { + + int digitCount = 0; + int endPos = startPos + length; + int precharCount; + // Don't advance on a new loop iteration and retry at the same position. + // Useful in the corner case of having to start at the beginning file position + // in the default state. + bool freezeCursor = false; + + StyleContext sc(startPos, length, initStyle, styler); + + while (sc.More()) { + // Skip past escaped characters + if (sc.ch == '\\') { + sc.Forward(); + continue; + } + + // A blockquotes resets the line semantics + if (sc.state == SCE_MARKDOWN_BLOCKQUOTE) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + + // Conditional state-based actions + if (sc.state == SCE_MARKDOWN_CODE2) { + if (sc.Match("``") && sc.GetRelative(-2) != ' ') { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_CODE) { + if (sc.ch == '`' && sc.chPrev != ' ') + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + /* De-activated because it gets in the way of other valid indentation + * schemes, for example multiple paragraphs inside a list item. + // Code block + else if (sc.state == SCE_MARKDOWN_CODEBK) { + bool d = true; + if (IsNewline(sc.ch)) { + if (sc.chNext != '\t') { + for (int c = 1; c < 5; ++c) { + if (sc.GetRelative(c) != ' ') + d = false; + } + } + } + else if (sc.atLineStart) { + if (sc.ch != '\t' ) { + for (int i = 0; i < 4; ++i) { + if (sc.GetRelative(i) != ' ') + d = false; + } + } + } + if (!d) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + */ + // Strong + else if (sc.state == SCE_MARKDOWN_STRONG1) { + if (sc.Match("**") && sc.chPrev != ' ') { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_STRONG2) { + if (sc.Match("__") && sc.chPrev != ' ') { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + // Emphasis + else if (sc.state == SCE_MARKDOWN_EM1) { + if (sc.ch == '*' && sc.chPrev != ' ') + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.state == SCE_MARKDOWN_EM2) { + if (sc.ch == '_' && sc.chPrev != ' ') + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.state == SCE_MARKDOWN_CODEBK) { + if (sc.atLineStart && sc.Match("~~~")) { + int i = 1; + while (!IsNewline(sc.GetRelative(i)) && sc.currentPos + i < endPos) + i++; + sc.Forward(i); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_STRIKEOUT) { + if (sc.Match("~~") && sc.chPrev != ' ') { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_LINE_BEGIN) { + // Header + if (sc.Match("######")) + SetStateAndZoom(SCE_MARKDOWN_HEADER6, 6, '#', sc); + else if (sc.Match("#####")) + SetStateAndZoom(SCE_MARKDOWN_HEADER5, 5, '#', sc); + else if (sc.Match("####")) + SetStateAndZoom(SCE_MARKDOWN_HEADER4, 4, '#', sc); + else if (sc.Match("###")) + SetStateAndZoom(SCE_MARKDOWN_HEADER3, 3, '#', sc); + else if (sc.Match("##")) + SetStateAndZoom(SCE_MARKDOWN_HEADER2, 2, '#', sc); + else if (sc.Match("#")) { + // Catch the special case of an unordered list + if (sc.chNext == '.' && IsASpaceOrTab(sc.GetRelative(2))) { + precharCount = 0; + sc.SetState(SCE_MARKDOWN_PRECHAR); + } + else + SetStateAndZoom(SCE_MARKDOWN_HEADER1, 1, '#', sc); + } + // Code block + else if (sc.Match("~~~")) { + if (!HasPrevLineContent(sc)) + sc.SetState(SCE_MARKDOWN_CODEBK); + else + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.ch == '=') { + if (HasPrevLineContent(sc) && FollowToLineEnd('=', SCE_MARKDOWN_HEADER1, endPos, sc)) + ; + else + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.ch == '-') { + if (HasPrevLineContent(sc) && FollowToLineEnd('-', SCE_MARKDOWN_HEADER2, endPos, sc)) + ; + else { + precharCount = 0; + sc.SetState(SCE_MARKDOWN_PRECHAR); + } + } + else if (IsNewline(sc.ch)) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + else { + precharCount = 0; + sc.SetState(SCE_MARKDOWN_PRECHAR); + } + } + + // The header lasts until the newline + else if (sc.state == SCE_MARKDOWN_HEADER1 || sc.state == SCE_MARKDOWN_HEADER2 || + sc.state == SCE_MARKDOWN_HEADER3 || sc.state == SCE_MARKDOWN_HEADER4 || + sc.state == SCE_MARKDOWN_HEADER5 || sc.state == SCE_MARKDOWN_HEADER6) { + if (IsNewline(sc.ch)) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + + // New state only within the initial whitespace + if (sc.state == SCE_MARKDOWN_PRECHAR) { + // Blockquote + if (sc.ch == '>' && precharCount < 5) + sc.SetState(SCE_MARKDOWN_BLOCKQUOTE); + /* + // Begin of code block + else if (!HasPrevLineContent(sc) && (sc.chPrev == '\t' || precharCount >= 4)) + sc.SetState(SCE_MARKDOWN_CODEBK); + */ + // HRule - Total of three or more hyphens, asterisks, or underscores + // on a line by themselves + else if ((sc.ch == '-' || sc.ch == '*' || sc.ch == '_') && IsValidHrule(endPos, sc)) + ; + // Unordered list + else if ((sc.ch == '-' || sc.ch == '*' || sc.ch == '+') && IsASpaceOrTab(sc.chNext)) { + sc.SetState(SCE_MARKDOWN_ULIST_ITEM); + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + // Ordered list + else if (IsADigit(sc.ch)) { + digitCount = 0; + while (IsADigit(sc.GetRelative(++digitCount))) + ; + if (sc.GetRelative(digitCount) == '.' && + IsASpaceOrTab(sc.GetRelative(digitCount + 1))) { + sc.SetState(SCE_MARKDOWN_OLIST_ITEM); + sc.Forward(digitCount + 1); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + // Alternate Ordered list + else if (sc.ch == '#' && sc.chNext == '.' && IsASpaceOrTab(sc.GetRelative(2))) { + sc.SetState(SCE_MARKDOWN_OLIST_ITEM); + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.ch != ' ' || precharCount > 2) + sc.SetState(SCE_MARKDOWN_DEFAULT); + else + ++precharCount; + } + + // New state anywhere in doc + if (sc.state == SCE_MARKDOWN_DEFAULT) { + int origPos = sc.currentPos; + if (sc.atLineStart && sc.ch == '#') { + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + freezeCursor = true; + } + // Links and Images + if (sc.Match("![") || sc.ch == '[') { + int i = 0, j = 0, k = 0; + int len = endPos - sc.currentPos; + while (i < len && (sc.GetRelative(++i) != ']' || sc.GetRelative(i - 1) == '\\')) + ; + if (sc.GetRelative(i) == ']') { + j = i; + if (sc.GetRelative(++i) == '(') { + while (i < len && (sc.GetRelative(++i) != ')' || sc.GetRelative(i - 1) == '\\')) + ; + if (sc.GetRelative(i) == ')') + k = i; + } + else if (sc.GetRelative(i) == '[' || sc.GetRelative(++i) == '[') { + while (i < len && (sc.GetRelative(++i) != ']' || sc.GetRelative(i - 1) == '\\')) + ; + if (sc.GetRelative(i) == ']') + k = i; + } + } + // At least a link text + if (j) { + sc.SetState(SCE_MARKDOWN_LINK); + sc.Forward(j); + // Also has a URL or reference portion + if (k) + sc.Forward(k - j); + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + } + // Code - also a special case for alternate inside spacing + if (sc.Match("``") && sc.GetRelative(3) != ' ') { + sc.SetState(SCE_MARKDOWN_CODE2); + sc.Forward(); + } + else if (sc.ch == '`' && sc.chNext != ' ') { + sc.SetState(SCE_MARKDOWN_CODE); + } + // Strong + else if (sc.Match("**") && sc.GetRelative(2) != ' ') { + sc.SetState(SCE_MARKDOWN_STRONG1); + sc.Forward(); + } + else if (sc.Match("__") && sc.GetRelative(2) != ' ') { + sc.SetState(SCE_MARKDOWN_STRONG2); + sc.Forward(); + } + // Emphasis + else if (sc.ch == '*' && sc.chNext != ' ') + sc.SetState(SCE_MARKDOWN_EM1); + else if (sc.ch == '_' && sc.chNext != ' ') + sc.SetState(SCE_MARKDOWN_EM2); + // Strikeout + else if (sc.Match("~~") && sc.GetRelative(2) != ' ') { + sc.SetState(SCE_MARKDOWN_STRIKEOUT); + sc.Forward(); + } + // Beginning of line + else if (IsNewline(sc.ch)) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + // Advance if not holding back the cursor for this iteration. + if (!freezeCursor) + sc.Forward(); + freezeCursor = false; + } + sc.Complete(); +} + +LexerModule lmMarkdown(SCLEX_MARKDOWN, ColorizeMarkdownDoc, "markdown"); + diff --git a/scintilla/Makefile.am b/scintilla/Makefile.am index b05a705189..70b2c41c5b 100644 --- a/scintilla/Makefile.am +++ b/scintilla/Makefile.am @@ -19,10 +19,10 @@ LexD.cxx \ LexFortran.cxx \ LexHaskell.cxx \ LexHTML.cxx \ -LexYAML.cxx \ LexLua.cxx \ -LexNsis.cxx \ +LexMarkdown.cxx \ LexMatlab.cxx \ +LexNsis.cxx \ LexOthers.cxx \ LexPascal.cxx \ LexPerl.cxx \ @@ -31,7 +31,8 @@ LexR.cxx \ LexRuby.cxx \ LexSQL.cxx \ LexTCL.cxx \ -LexVHDL.cxx +LexVHDL.cxx \ +LexYAML.cxx SRCS= \ CallTip.cxx \ diff --git a/scintilla/include/SciLexer.h b/scintilla/include/SciLexer.h index e741ff4862..3f68908beb 100644 --- a/scintilla/include/SciLexer.h +++ b/scintilla/include/SciLexer.h @@ -110,6 +110,7 @@ #define SCLEX_POWERPRO 95 #define SCLEX_NIMROD 96 #define SCLEX_SML 97 +#define SCLEX_MARKDOWN 98 #define SCLEX_AUTOMATIC 1000 #define SCE_P_DEFAULT 0 #define SCE_P_COMMENTLINE 1 @@ -1277,6 +1278,28 @@ #define SCE_MYSQL_USER2 19 #define SCE_MYSQL_USER3 20 #define SCE_MYSQL_HIDDENCOMMAND 21 +#define SCE_MARKDOWN_DEFAULT 0 +#define SCE_MARKDOWN_LINE_BEGIN 1 +#define SCE_MARKDOWN_STRONG1 2 +#define SCE_MARKDOWN_STRONG2 3 +#define SCE_MARKDOWN_EM1 4 +#define SCE_MARKDOWN_EM2 5 +#define SCE_MARKDOWN_HEADER1 6 +#define SCE_MARKDOWN_HEADER2 7 +#define SCE_MARKDOWN_HEADER3 8 +#define SCE_MARKDOWN_HEADER4 9 +#define SCE_MARKDOWN_HEADER5 10 +#define SCE_MARKDOWN_HEADER6 11 +#define SCE_MARKDOWN_PRECHAR 12 +#define SCE_MARKDOWN_ULIST_ITEM 13 +#define SCE_MARKDOWN_OLIST_ITEM 14 +#define SCE_MARKDOWN_BLOCKQUOTE 15 +#define SCE_MARKDOWN_STRIKEOUT 16 +#define SCE_MARKDOWN_HRULE 17 +#define SCE_MARKDOWN_LINK 18 +#define SCE_MARKDOWN_CODE 19 +#define SCE_MARKDOWN_CODE2 20 +#define SCE_MARKDOWN_CODEBK 21 #define SCE_PO_DEFAULT 0 #define SCE_PO_COMMENT 1 #define SCE_PO_MSGID 2 diff --git a/scintilla/include/Scintilla.iface b/scintilla/include/Scintilla.iface index 91d19ef852..d3b7531214 100644 --- a/scintilla/include/Scintilla.iface +++ b/scintilla/include/Scintilla.iface @@ -2135,6 +2135,7 @@ val SCLEX_SORCUS=94 val SCLEX_POWERPRO=95 val SCLEX_NIMROD=96 val SCLEX_SML=97 +val SCLEX_MARKDOWN=98 # When a lexer specifies its language as SCLEX_AUTOMATIC it receives a # value assigned in sequence from SCLEX_AUTOMATIC+1. @@ -3545,6 +3546,30 @@ val SCE_SML_COMMENT=12 val SCE_SML_COMMENT1=13 val SCE_SML_COMMENT2=14 val SCE_SML_COMMENT3=15 +# Lexical state for SCLEX_MARKDOWN +lex Markdown=SCLEX_MARKDOWN SCE_MARKDOWN_ +val SCE_MARKDOWN_DEFAULT=0 +val SCE_MARKDOWN_LINE_BEGIN=1 +val SCE_MARKDOWN_STRONG1=2 +val SCE_MARKDOWN_STRONG2=3 +val SCE_MARKDOWN_EM1=4 +val SCE_MARKDOWN_EM2=5 +val SCE_MARKDOWN_HEADER1=6 +val SCE_MARKDOWN_HEADER2=7 +val SCE_MARKDOWN_HEADER3=8 +val SCE_MARKDOWN_HEADER4=9 +val SCE_MARKDOWN_HEADER5=10 +val SCE_MARKDOWN_HEADER6=11 +val SCE_MARKDOWN_PRECHAR=12 +val SCE_MARKDOWN_ULIST_ITEM=13 +val SCE_MARKDOWN_OLIST_ITEM=14 +val SCE_MARKDOWN_BLOCKQUOTE=15 +val SCE_MARKDOWN_STRIKEOUT=16 +val SCE_MARKDOWN_HRULE=17 +val SCE_MARKDOWN_LINK=18 +val SCE_MARKDOWN_CODE=19 +val SCE_MARKDOWN_CODE2=20 +val SCE_MARKDOWN_CODEBK=21 # Events diff --git a/scintilla/makefile.win32 b/scintilla/makefile.win32 index 9bb13a1d7a..5bc937af38 100644 --- a/scintilla/makefile.win32 +++ b/scintilla/makefile.win32 @@ -61,7 +61,8 @@ MARSHALLER=scintilla-marshal.o #**LEXOBJS=\\\n\(\*.o \) LEXOBJS=\ LexAda.o LexBash.o LexAsm.o LexCSS.o LexCPP.o LexCrontab.o LexHTML.o LexOthers.o LexPascal.o \ -LexPerl.o LexPython.o LexSQL.o LexCaml.o LexTCL.o LexRuby.o LexFortran.o LexVHDL.o LexMatlab.o \ +LexPerl.o LexPython.o LexSQL.o LexCaml.o LexTCL.o LexRuby.o LexFortran.o LexVHDL.o \ +LexMarkdown.o LexMatlab.o \ LexD.o LexLua.o LexHaskell.o LexBasic.o LexR.o LexYAML.o LexCmake.o LexNsis.o #--Autogenerated -- end of automatically generated section diff --git a/src/about.c b/src/about.c index 96cc1be63e..bc6335aa65 100644 --- a/src/about.c +++ b/src/about.c @@ -84,7 +84,7 @@ static const gchar *contributors = "Chris Macksey, Christoph Berg, Colomban Wendling, Conrad Steenberg, Daniel Richard G., Dave Moore, Dirk Weber, " "Elias Pschernig, Eugene Arshinov, Felipe Pena, François Cami, " "Giuseppe Torelli, Guillaume de Rorthais, Guillaume Hoffmann, Herbert Voss, Jason Oster, Jean-François Wauthy, Jeff Pohlmeyer, " -"Jesse Mayes, John Gabriele, Josef Whiter, Kevin Ellwood, Kristoffer A. Tjernås, Marko Peric, Matti Mårds, Moritz Barsnick, " +"Jesse Mayes, John Gabriele, Jon Strait, Josef Whiter, Kevin Ellwood, Kristoffer A. Tjernås, Marko Peric, Matti Mårds, Moritz Barsnick, " "Peter Strand, Philipp Gildein, Pierre Joye, Rob van der Linde, Robert McGinley, Roland Baudin, S Jagannathan, Saleem Abdulrasool, " "Sebastian Kraft, Shiv, Slava Semushin, Stefan Oltmanns, Tamim, Thomas Martitz, Tomás Vírseda, " "Tyler Mulligan, Walery Studennikov, Yura Siamashka"; diff --git a/src/filetypes.c b/src/filetypes.c index 19baa0edec..f0232b50e8 100644 --- a/src/filetypes.c +++ b/src/filetypes.c @@ -341,6 +341,17 @@ static void init_builtin_filetypes(void) ft->comment_close = NULL; ft->group = GEANY_FILETYPE_GROUP_COMPILED; +#define MARKDOWN + ft = filetypes[GEANY_FILETYPES_MARKDOWN]; + ft->lang = 36; + ft->name = g_strdup("Markdown"); + ft->title = g_strdup_printf(_("%s source file"), "Markdown"); + ft->extension = g_strdup("md"); + ft->pattern = utils_strv_new("*.mdml", "*.mdwn", "*.markdown", "*.md", NULL); + ft->comment_open = NULL; + ft->comment_close = NULL; + ft->group = GEANY_FILETYPE_GROUP_MISC; + #define SH ft = filetypes[GEANY_FILETYPES_SH]; ft->lang = 16; diff --git a/src/filetypes.h b/src/filetypes.h index 01c50d63ce..cb0e99eabd 100644 --- a/src/filetypes.h +++ b/src/filetypes.h @@ -76,7 +76,7 @@ typedef enum GEANY_FILETYPES_VHDL, GEANY_FILETYPES_ADA, GEANY_FILETYPES_CMAKE, - + GEANY_FILETYPES_MARKDOWN, /* ^ append items here */ GEANY_MAX_BUILT_IN_FILETYPES /* Don't use this, use filetypes_array->len instead */ } diff --git a/src/highlighting.c b/src/highlighting.c index 595c46e095..ef1b487a56 100644 --- a/src/highlighting.c +++ b/src/highlighting.c @@ -2725,6 +2725,64 @@ static void styleset_sql(ScintillaObject *sci) } +static void styleset_markdown_init(gint ft_id, GKeyFile *config, GKeyFile *config_home) +{ + new_style_array(GEANY_FILETYPES_MARKDOWN, 17); + + get_keyfile_hex(config, config_home, "default", 0x000000, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[0]); + get_keyfile_hex(config, config_home, "strong", 0xff0000, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[1]); + get_keyfile_hex(config, config_home, "emphasis", 0xff0000, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[2]); + get_keyfile_hex(config, config_home, "header1", 0x0000bb, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[3]); + get_keyfile_hex(config, config_home, "header2", 0x0000bb, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[4]); + get_keyfile_hex(config, config_home, "header3", 0x0000bb, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[5]); + get_keyfile_hex(config, config_home, "header4", 0x0000bb, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[6]); + get_keyfile_hex(config, config_home, "header5", 0x0000bb, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[7]); + get_keyfile_hex(config, config_home, "header6", 0x0000bb, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[8]); + get_keyfile_hex(config, config_home, "ulist_item", 0x007f00, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[9]); + get_keyfile_hex(config, config_home, "olist_item", 0x007f00, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[10]); + get_keyfile_hex(config, config_home, "blockquote", 0xff0000, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[11]); + get_keyfile_hex(config, config_home, "strikeout", 0xaa00ff, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[12]); + get_keyfile_hex(config, config_home, "hrule", 0xff901e, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[13]); + get_keyfile_hex(config, config_home, "link", 0x0000ff, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[14]); + get_keyfile_hex(config, config_home, "code", 0x009f00, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[15]); + get_keyfile_hex(config, config_home, "codebk", 0x005f00, 0xffffff, FALSE, &style_sets[GEANY_FILETYPES_MARKDOWN].styling[16]); + + style_sets[GEANY_FILETYPES_MARKDOWN].keywords = NULL; + + get_keyfile_wordchars(config, config_home, &style_sets[GEANY_FILETYPES_MARKDOWN].wordchars); +} + +static void styleset_markdown(ScintillaObject *sci) +{ + const filetype_id ft_id = GEANY_FILETYPES_MARKDOWN; + + apply_filetype_properties(sci, SCLEX_MARKDOWN, ft_id); + + set_sci_style(sci, STYLE_DEFAULT, GEANY_FILETYPES_MARKDOWN, 0); + set_sci_style(sci, SCE_MARKDOWN_DEFAULT, GEANY_FILETYPES_MARKDOWN, 0); + set_sci_style(sci, SCE_MARKDOWN_LINE_BEGIN, GEANY_FILETYPES_MARKDOWN, 0); + set_sci_style(sci, SCE_MARKDOWN_PRECHAR, GEANY_FILETYPES_MARKDOWN, 0); + set_sci_style(sci, SCE_MARKDOWN_STRONG1, GEANY_FILETYPES_MARKDOWN, 1); + set_sci_style(sci, SCE_MARKDOWN_STRONG2, GEANY_FILETYPES_MARKDOWN, 1); + set_sci_style(sci, SCE_MARKDOWN_EM1, GEANY_FILETYPES_MARKDOWN, 2); + set_sci_style(sci, SCE_MARKDOWN_EM2, GEANY_FILETYPES_MARKDOWN, 2); + set_sci_style(sci, SCE_MARKDOWN_HEADER1, GEANY_FILETYPES_MARKDOWN, 3); + set_sci_style(sci, SCE_MARKDOWN_HEADER2, GEANY_FILETYPES_MARKDOWN, 4); + set_sci_style(sci, SCE_MARKDOWN_HEADER3, GEANY_FILETYPES_MARKDOWN, 5); + set_sci_style(sci, SCE_MARKDOWN_HEADER4, GEANY_FILETYPES_MARKDOWN, 6); + set_sci_style(sci, SCE_MARKDOWN_HEADER5, GEANY_FILETYPES_MARKDOWN, 7); + set_sci_style(sci, SCE_MARKDOWN_HEADER6, GEANY_FILETYPES_MARKDOWN, 8); + set_sci_style(sci, SCE_MARKDOWN_ULIST_ITEM, GEANY_FILETYPES_MARKDOWN, 9); + set_sci_style(sci, SCE_MARKDOWN_OLIST_ITEM, GEANY_FILETYPES_MARKDOWN, 10); + set_sci_style(sci, SCE_MARKDOWN_BLOCKQUOTE, GEANY_FILETYPES_MARKDOWN, 11); + set_sci_style(sci, SCE_MARKDOWN_STRIKEOUT, GEANY_FILETYPES_MARKDOWN, 12); + set_sci_style(sci, SCE_MARKDOWN_HRULE, GEANY_FILETYPES_MARKDOWN, 13); + set_sci_style(sci, SCE_MARKDOWN_LINK, GEANY_FILETYPES_MARKDOWN, 14); + set_sci_style(sci, SCE_MARKDOWN_CODE, GEANY_FILETYPES_MARKDOWN, 15); + set_sci_style(sci, SCE_MARKDOWN_CODE2, GEANY_FILETYPES_MARKDOWN, 15); + set_sci_style(sci, SCE_MARKDOWN_CODEBK, GEANY_FILETYPES_MARKDOWN, 16); +} + static void styleset_haskell_init(gint ft_id, GKeyFile *config, GKeyFile *config_home) { new_style_array(GEANY_FILETYPES_HASKELL, 17); @@ -3599,6 +3657,7 @@ void highlighting_init_styles(gint filetype_idx, GKeyFile *config, GKeyFile *con init_styleset_case(GEANY_FILETYPES_LUA, styleset_lua_init); init_styleset_case(GEANY_FILETYPES_MAKE, styleset_makefile_init); init_styleset_case(GEANY_FILETYPES_MATLAB, styleset_matlab_init); + init_styleset_case(GEANY_FILETYPES_MARKDOWN, styleset_markdown_init); init_styleset_case(GEANY_FILETYPES_NSIS, styleset_nsis_init); init_styleset_case(GEANY_FILETYPES_PASCAL, styleset_pascal_init); init_styleset_case(GEANY_FILETYPES_PERL, styleset_perl_init); @@ -3660,6 +3719,7 @@ void highlighting_set_styles(ScintillaObject *sci, gint filetype_idx) styleset_case(GEANY_FILETYPES_LATEX, styleset_latex); styleset_case(GEANY_FILETYPES_LUA, styleset_lua); styleset_case(GEANY_FILETYPES_MAKE, styleset_makefile); + styleset_case(GEANY_FILETYPES_MARKDOWN, styleset_markdown); styleset_case(GEANY_FILETYPES_MATLAB, styleset_matlab); styleset_case(GEANY_FILETYPES_NSIS, styleset_nsis); styleset_case(GEANY_FILETYPES_PASCAL, styleset_pascal); diff --git a/tagmanager/Makefile.am b/tagmanager/Makefile.am index f0f901fae4..da4617d966 100644 --- a/tagmanager/Makefile.am +++ b/tagmanager/Makefile.am @@ -51,6 +51,7 @@ libtagmanager_a_SOURCES =\ latex.c\ lregex.c\ matlab.c\ + markdown.c\ pascal.c\ perl.c\ rest.c\ diff --git a/tagmanager/makefile.win32 b/tagmanager/makefile.win32 index a69c19c095..92f1e87722 100644 --- a/tagmanager/makefile.win32 +++ b/tagmanager/makefile.win32 @@ -43,7 +43,7 @@ $(COMPLIB): args.o c.o fortran.o make.o conf.o pascal.o perl.o php.o diff.o vhdl actionscript.o nsis.o \ haskell.o haxe.o html.o python.o lregex.o rest.o sh.o ctags.o entry.o get.o keyword.o nestlevel.o \ options.o \ -parse.o basic.o read.o sort.o strlist.o latex.o matlab.o docbook.o tcl.o ruby.o asm.o sql.o css.o \ +parse.o basic.o read.o sort.o strlist.o latex.o markdown.o matlab.o docbook.o tcl.o ruby.o asm.o sql.o css.o \ vstring.o regex.o tm_workspace.o tm_work_object.o tm_source_file.o tm_project.o tm_tag.o \ tm_symbol.o tm_file_entry.o tm_tagmanager.o $(AR) rc $@ $^ diff --git a/tagmanager/markdown.c b/tagmanager/markdown.c new file mode 100644 index 0000000000..e5c669443e --- /dev/null +++ b/tagmanager/markdown.c @@ -0,0 +1,105 @@ +/* +* +* Copyright (c) 2009, Jon Strait +* +* This source code is released for free distribution under the terms of the +* GNU General Public License. +* +* This module contains functions for generating tags for Markdown files. +*/ + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include +#include + +#include "parse.h" +#include "read.h" +#include "vstring.h" + +/* +* DATA DEFINITIONS +*/ + +static kindOption MarkdownKinds[] = { + { TRUE, 'v', "variable", "sections" } +}; + +/* +* FUNCTION DEFINITIONS +*/ + +/* checks if str is all the same character */ +static boolean issame(const char *str) +{ + char first = *str; + + while (*(++str)) + { + if (*str && *str != first) + return FALSE; + } + return TRUE; +} + +static void makeMarkdownTag (const vString* const name, boolean name_before) +{ + tagEntryInfo e; + initTagEntry (&e, vStringValue(name)); + + if (name_before) + e.lineNumber--; /* we want the line before the underline chars */ + e.kindName = "variable"; + e.kind = 'v'; + + makeTagEntry(&e); +} + + +static void findMarkdownTags (void) +{ + vString *name = vStringNew(); + const unsigned char *line; + + while ((line = fileReadLine()) != NULL) + { + int name_len = vStringLength(name); + + /* underlines must be the same length or more */ + if (name_len > 0 && (line[0] == '=' || line[0] == '-') && issame((const char*) line)) + { + makeMarkdownTag(name, TRUE); + } + else if (line[0] == '#') { + vStringClear(name); + vStringCatS(name, (const char *) line); + vStringTerminate(name); + makeMarkdownTag(name, FALSE); + } + else { + vStringClear (name); + if (! isspace(*line)) + vStringCatS(name, (const char*) line); + vStringTerminate(name); + } + } + vStringDelete (name); +} + +extern parserDefinition* MarkdownParser (void) +{ + static const char *const patterns [] = { "*.md", NULL }; + static const char *const extensions [] = { "md", NULL }; + parserDefinition* const def = parserNew ("Markdown"); + + def->kinds = MarkdownKinds; + def->kindCount = KIND_COUNT (MarkdownKinds); + def->patterns = patterns; + def->extensions = extensions; + def->parser = findMarkdownTags; + return def; +} + diff --git a/tagmanager/parsers.h b/tagmanager/parsers.h index 745747929d..86c622fe95 100644 --- a/tagmanager/parsers.h +++ b/tagmanager/parsers.h @@ -50,8 +50,8 @@ MatlabParser, \ ValaParser, \ ActionScriptParser, \ - NsisParser - + NsisParser, \ + MarkdownParser /* langType of each parser 0 CParser @@ -90,6 +90,7 @@ langType of each parser 33 ValaParser 34 ActionScriptParser 35 NsisParser +36 MarkdownParser */ #endif /* _PARSERS_H */ diff --git a/wscript b/wscript index cceaf9c956..037c662b65 100644 --- a/wscript +++ b/wscript @@ -58,7 +58,7 @@ tagmanager_sources = [ 'tagmanager/docbook.c', 'tagmanager/entry.c', 'tagmanager/fortran.c', 'tagmanager/get.c', 'tagmanager/haskell.c', 'tagmanager/haxe.c', 'tagmanager/html.c', 'tagmanager/js.c', 'tagmanager/keyword.c', 'tagmanager/latex.c', 'tagmanager/lregex.c', 'tagmanager/lua.c', - 'tagmanager/make.c', 'tagmanager/matlab.c', 'tagmanager/nsis.c', + 'tagmanager/make.c', 'tagmanager/markdown.c', 'tagmanager/matlab.c', 'tagmanager/nsis.c', 'tagmanager/nestlevel.c', 'tagmanager/options.c', 'tagmanager/parse.c', 'tagmanager/pascal.c', 'tagmanager/perl.c', 'tagmanager/php.c', 'tagmanager/python.c', 'tagmanager/read.c', @@ -73,15 +73,18 @@ scintilla_sources = [ 'scintilla/CharClassify.cxx', 'scintilla/ContractionState.cxx', 'scintilla/Decoration.cxx', 'scintilla/DocumentAccessor.cxx', 'scintilla/Document.cxx', 'scintilla/Editor.cxx', 'scintilla/ExternalLexer.cxx', 'scintilla/Indicator.cxx', 'scintilla/KeyMap.cxx', - 'scintilla/KeyWords.cxx', 'scintilla/LexAda.cxx', 'scintilla/LexAsm.cxx', 'scintilla/LexBash.cxx', + 'scintilla/KeyWords.cxx', + 'scintilla/LexAda.cxx', 'scintilla/LexAsm.cxx', 'scintilla/LexBash.cxx', 'scintilla/LexBasic.cxx', 'scintilla/LexCaml.cxx', 'scintilla/LexCmake.cxx', 'scintilla/LexCPP.cxx', 'scintilla/LexCrontab.cxx', 'scintilla/LexCSS.cxx', 'scintilla/LexD.cxx', 'scintilla/LexFortran.cxx', 'scintilla/LexHaskell.cxx', 'scintilla/LexHTML.cxx', - 'scintilla/LexLua.cxx', 'scintilla/LexNsis.cxx', 'scintilla/LexOthers.cxx', + 'scintilla/LexLua.cxx', 'scintilla/LexMarkdown.cxx', 'scintilla/LexMatlab.cxx', + 'scintilla/LexNsis.cxx', 'scintilla/LexOthers.cxx', 'scintilla/LexPascal.cxx', 'scintilla/LexPerl.cxx', 'scintilla/LexPython.cxx', - 'scintilla/LexR.cxx', 'scintilla/LexMatlab.cxx', 'scintilla/LexYAML.cxx', + 'scintilla/LexR.cxx', 'scintilla/LexRuby.cxx', 'scintilla/LexSQL.cxx', 'scintilla/LexTCL.cxx', - 'scintilla/LexVHDL.cxx', 'scintilla/LineMarker.cxx', 'scintilla/PerLine.cxx', + 'scintilla/LexVHDL.cxx', 'scintilla/LexYAML.cxx', + 'scintilla/LineMarker.cxx', 'scintilla/PerLine.cxx', 'scintilla/PlatGTK.cxx', 'scintilla/PositionCache.cxx', 'scintilla/PropSet.cxx', 'scintilla/RESearch.cxx', 'scintilla/RunStyles.cxx', 'scintilla/ScintillaBase.cxx', 'scintilla/ScintillaGTK.cxx',