aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--doc/LPegLexer.html77
-rw-r--r--lexers/LexLPeg.cxx146
2 files changed, 153 insertions, 70 deletions
diff --git a/doc/LPegLexer.html b/doc/LPegLexer.html
index 52e0202d4..02b1dbdd2 100644
--- a/doc/LPegLexer.html
+++ b/doc/LPegLexer.html
@@ -147,7 +147,19 @@
selecting it as the lexer to use via
<a class="message" href="ScintillaDoc.html#SCI_SETLEXER">SCI_SETLEXER</a> or
<a class="message" href="ScintillaDoc.html#SCI_SETLEXERLANGUAGE">SCI_SETLEXERLANGUAGE</a>,
- the following property <em>must</em> be set via
+ you <em>must</em> call
+ <a class="message" href="ScintillaDoc.html#SCI_PRIVATELEXERCALL">SCI_PRIVATELEXERCALL</a>
+ with
+ <a class="message" href="#SCI_LOADLEXERLIBRARY">SCI_LOADLEXERLIBRARY</a>
+ and the path to where you included Scintilla's <code>lexlua/</code>
+ directory in your application's installation location. If you prefer not to
+ use <code>SCI_PRIVATELEXERCALL</code>, you may instead set via
+ <a class="message" href="ScintillaDoc.html#SCI_SETPROPERTY">SCI_SETPROPERTY</a>
+ the <code>lexer.lpeg.home</code> lexer property to the location of
+ <code>lexlua/</code>.
+
+ <p>The following properties are recognized by the LPeg lexer and can be set
+ via
<a class="message" href="ScintillaDoc.html#SCI_SETPROPERTY">SCI_SETPROPERTY</a>:</p>
<table class="standard" summary="Search flags">
@@ -157,21 +169,23 @@
<td>The directory containing the Lua lexers. This is the path
where you included Scintilla's <code>lexlua/</code> directory in
- your application's installation location.</td>
+ your application's installation location. It is automatically set when
+ calling
+ <a class="message" href="ScintillaDoc.html#SCI_PRIVATELEXERCALL">SCI_PRIVATELEXERCALL</a>
+ with
+ <a class="message" href="#SCI_LOADLEXERLIBRARY">SCI_LOADLEXERLIBRARY</a>.
+ Lexers across multiple directories can be used by separating
+ directories with a <code>;</code> character.</td>
</tr>
- </tbody>
- </table>
-
- <p>The following properties are optional and may or may not be set:</p>
- <table class="standard" summary="Search flags">
- <tbody>
<tr>
<td><code>lexer.lpeg.color.theme</code></td>
<td>The color theme to use. Color themes are located in the
- <code>lexlua/themes/</code> directory. Currently supported themes
- are <code>light</code>, <code>dark</code>, <code>scite</code>, and
+ <code>lexlua/themes/</code> directory. (They can also be located in
+ any <code>themes</code> subdirectories of additional paths specified
+ in <code>lexer.lpeg.home</code>.) Currently supported themes are
+ <code>light</code>, <code>dark</code>, <code>scite</code>, and
<code>curses</code>. Your application can define colors and styles
manually through Scintilla properties. The theme files have
examples.</td>
@@ -243,13 +257,13 @@
doc = SendScintilla(sci, SCI_CREATEDOCUMENT, 0, 0)
SendScintilla(sci, SCI_SETDOCPOINTER, 0, doc)
SendScintilla(sci, SCI_SETLEXERLANGUAGE, 0, "lpeg")
- home = "/home/mitchell/app/lua_lexers"
- SendScintilla(sci, SCI_SETPROPERTY, "lexer.lpeg.home", home)
SendScintilla(sci, SCI_SETPROPERTY, "lexer.lpeg.color.theme", "light")
fn = SendScintilla(sci, SCI_GETDIRECTFUNCTION, 0, 0)
SendScintilla(sci, SCI_PRIVATELEXERCALL, SCI_GETDIRECTFUNCTION, fn)
psci = SendScintilla(sci, SCI_GETDIRECTPOINTER, 0, 0)
SendScintilla(sci, SCI_PRIVATELEXERCALL, SCI_SETDOCPOINTER, psci)
+ home = "/home/mitchell/app/lua_lexers"
+ SendScintilla(sci, SCI_PRIVATELEXERCALL, SCI_LOADLEXERLIBRARY, home);
SendScintilla(sci, SCI_PRIVATELEXERCALL, SCI_SETLEXERLANGUAGE, "lua")
}
@@ -264,6 +278,8 @@
<a class="message" href="#SCI_GETDIRECTFUNCTION">SCI_PRIVATELEXERCALL(SCI_GETDIRECTFUNCTION, int SciFnDirect)</a><br/>
<a class="message" href="#SCI_GETLEXERLANGUAGE">SCI_PRIVATELEXERCALL(SCI_GETLEXERLANGUAGE, char *languageName) &rarr; int</a><br/>
<a class="message" href="#SCI_GETSTATUS">SCI_PRIVATELEXERCALL(SCI_GETSTATUS, char *errorMessage) &rarr; int</a><br/>
+ <a class="message" href="#SCI_LOADLEXERLIBRARY">SCI_PRIVATELEXERCALL(SCI_LOADLEXERLIBRARY, const char *path)</a><br/>
+ <a class="message" href="#SCI_PROPERTYNAMES">SCI_PRIVATELEXERCALL(SCI_PROPERTYNAMES, char *names) &rarr; int</a><br/>
<a class="message" href="#styleNum">SCI_PRIVATELEXERCALL(int styleNum, char *styleName) &rarr; int</a><br/>
<a class="message" href="#SCI_SETDOCPOINTER">SCI_PRIVATELEXERCALL(SCI_SETDOCPOINTER, int sci)</a><br/>
<a class="message" href="#SCI_SETLEXERLANGUAGE">SCI_PRIVATELEXERCALL(SCI_SETLEXERLANGUAGE, languageName)</a><br/>
@@ -352,6 +368,39 @@
if (strlen(errmsg) &gt; 0) { /* handle error */ }
</code></pre>
+ <p><b id="SCI_LOADLEXERLIBRARY">SCI_PRIVATELEXERCALL(SCI_LOADLEXERLIBRARY, const char *path)</b><br/>
+ Tells the LPeg lexer that the given path is where a copy of Scintilla's
+ <code>lexlua/</code> is located, or is a path that contains additional
+ lexers and/or themes to load (e.g. user-defined lexers/themes).</p>
+
+ <p>This call may be made multiple times in order to support lexers and
+ themes across multiple directories.</p>
+
+ <p>Usage:</p>
+
+ <pre><code>
+ SendScintilla(sci, SCI_PRIVATELEXERCALL, SCI_LOADLEXERLIBRARY, "path/to/lexlua")
+ </code></pre>
+
+ <p><b id="SCI_PROPERTYNAMES">SCI_PRIVATELEXERCALL(SCI_PRIVATELEXERCALL, char *names) &rarr; int</b><br/>
+ Returns the length of a '\n'-separated list of known lexer names, or stores
+ the lexer list into the given buffer. If the buffer is long enough, the
+ string is terminated by a <code>0</code> character.</p>
+
+ <p>The lexers in this list can be passed to the
+ <a class="message" href="#SCI_SETLEXERLANGUAGE"><code>SCI_SETLEXERLANGUAGE</code></a>
+ LPeg Lexer API call.</p>
+
+ <p>Usage:</p>
+
+ <pre><code>
+ SendScintilla(sci, SCI_PRIVATELEXERCALL, SCI_PROPERTYNAMES, lexers)
+ // lexers now contains a '\n'-separated list of known lexer names
+ </code></pre>
+
+ <p>See also:
+ <a class="message" href="#SCI_SETLEXERLANGUAGE"><code>SCI_SETLEXERLANGUAGE</code></a></p>
+
<p><b id="SCI_PRIVATELEXERCALL">SCI_PRIVATELEXERCALL(int styleNum, char *styleName) &rarr; int</b><br/>
Returns the length of the token name associated with the given style number
or stores the style name into the given buffer. If the buffer is long
@@ -403,7 +452,9 @@
SendScintilla(sci, SCI_PRIVATELEXERCALL, SCI_SETLEXERLANGUAGE, "lua")
</code></pre>
- <p>See also: <a class="message" href="#SCI_SETDOCPOINTER"><code>SCI_SETDOCPOINTER</code></a></p>
+ <p>See also:
+ <a class="message" href="#SCI_SETDOCPOINTER"><code>SCI_SETDOCPOINTER</code></a>,
+ <a class="message" href="#SCI_PROPERTYNAMES"><code>SCI_PROPERTYNAMES</code></a></p>
<h2>Using Scintilla as a Lua Library</h3>
diff --git a/lexers/LexLPeg.cxx b/lexers/LexLPeg.cxx
index 6d5167387..2d54e2547 100644
--- a/lexers/LexLPeg.cxx
+++ b/lexers/LexLPeg.cxx
@@ -17,8 +17,16 @@
#include <curses.h>
#endif
+#include <set>
+#include <sstream>
#include <vector>
+#if !_WIN32
+#include <dirent.h>
+#else
+#include <io.h>
+#endif
+
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
@@ -191,7 +199,7 @@ class LexerLPeg : public ILexer {
// Lexer property keys.
const char * const LexerErrorKey = "lexer.lpeg.error";
const char * const LexerHomeKey = "lexer.lpeg.home";
- const char * const LexerNameKey = "lexer.name";
+ const char * const LexerNameKey = "lexer.lpeg.name";
const char * const LexerThemeKey = "lexer.lpeg.color.theme";
/**
@@ -229,6 +237,34 @@ class LexerLPeg : public ILexer {
* determine which lexer grammar to use.
*/
bool ws[STYLE_MAX + 1];
+ /** List of known lexer names. */
+ std::set<std::string> lexer_names;
+
+ /**
+ * Searches the given directory for lexers and records their names.
+ * @param path Path to a directory containing lexers.
+ */
+ void ReadLexerNames(const char *path) {
+#if !_WIN32
+ DIR *dir = opendir(path);
+ if (!dir) return;
+ struct dirent *entry;
+ while ((entry = readdir(dir))) {
+ char *p = strstr(entry->d_name, ".lua");
+ if (p) lexer_names.emplace(entry->d_name, p - entry->d_name);
+ }
+ closedir(dir);
+#else
+ struct __finddata_t file;
+ intptr_t handle = _findfirst(path + "/*", &file); // TODO:
+ while (handle != -1) {
+ char *p = strstr(file.name, ".lua");
+ if (p) lexer_names.emplace(file.name, p - file.name);
+ handle = _findnext(handle, &file)
+ }
+ _findclose(handle);
+#endif
+ }
/**
* Logs the given error message or a Lua error message, prints it, and clears
@@ -238,7 +274,7 @@ class LexerLPeg : public ILexer {
* @param str The error message to log and print. If `nullptr`, logs and
* prints the Lua error message at the top of the stack.
*/
- void log_error(lua_State *L, const char *str = nullptr) {
+ void LogError(lua_State *L, const char *str = nullptr) {
const char *value = str ? str : lua_tostring(L, -1);
PropertySet(LexerErrorKey, value);
fprintf(stderr, "Lua Error: %s.\n", value);
@@ -417,9 +453,11 @@ class LexerLPeg : public ILexer {
if (!props.GetExpanded(LexerHomeKey, nullptr) ||
!*props.Get(LexerNameKey) || !L)
return false;
- char *home = reinterpret_cast<char *>(
+ char *_home = reinterpret_cast<char *>(
malloc(props.GetExpanded(LexerHomeKey, nullptr) + 1));
- props.GetExpanded(LexerHomeKey, home);
+ props.GetExpanded(LexerHomeKey, _home);
+ std::string home(_home);
+ free(_home);
const char *lexer = props.Get(LexerNameKey);
RECORD_STACK_TOP(L);
@@ -432,18 +470,19 @@ class LexerLPeg : public ILexer {
lua_setfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg");
// Determine where to look for the lexer module and themes.
- std::vector<char *> dirs;
- dirs.push_back(home);
- for (char *p = strstr(home, ";"); p; p = strstr(p, ";")) {
- *p++ = '\0';
- dirs.push_back(p);
+ std::vector<std::string> dirs;
+ size_t start = 0, end;
+ while ((end = home.find(';', start)) != std::string::npos) {
+ dirs.emplace_back(home, start, end - start);
+ start = end + 1;
}
+ dirs.emplace_back(home, start);
// If necessary, load the lexer module.
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
if (lua_getfield(L, -1, "lexer") == LUA_TNIL) {
- for (char *dir : dirs) {
- lua_pushstring(L, dir);
+ for (const std::string& dir : dirs) {
+ lua_pushstring(L, dir.c_str());
lua_pushstring(L, "/lexer.lua");
lua_concat(L, 2);
int status = luaL_loadfile(L, lua_tostring(L, -1));
@@ -455,7 +494,7 @@ class LexerLPeg : public ILexer {
lua_pushcfunction(L, lua_error_handler);
lua_insert(L, -2);
if (status == LUA_OK && lua_pcall(L, 0, 1, -2) == LUA_OK) break;
- return (free(home), log_error(L), false);
+ return (LogError(L), false);
}
lua_remove(L, -2); // lua_error_handler
lua_replace(L, -2); // nil
@@ -480,12 +519,10 @@ class LexerLPeg : public ILexer {
// Load the language lexer.
if (lua_getfield(L, -1, "load") != LUA_TFUNCTION)
- return (
- free(home), log_error(L, "'lexer.load' function not found"), false);
+ return (LogError(L, "'lexer.load' function not found"), false);
lua_pushcfunction(L, lua_error_handler), lua_insert(L, -2);
lua_pushstring(L, lexer), lua_pushnil(L), lua_pushboolean(L, 1);
- if (lua_pcall(L, 3, 1, -5) != LUA_OK)
- return (free(home), log_error(L), false);
+ if (lua_pcall(L, 3, 1, -5) != LUA_OK) return (LogError(L), false);
lua_remove(L, -2); // lua_error_handler
lua_remove(L, -2); // lexer module
lua_pushlightuserdata(L, reinterpret_cast<void *>(&props));
@@ -498,8 +535,8 @@ class LexerLPeg : public ILexer {
malloc(props.GetExpanded(LexerThemeKey, nullptr) + 1));
props.GetExpanded(LexerThemeKey, theme);
if (!strstr(theme, "/") && !strstr(theme, "\\")) { // theme name
- for (char *dir : dirs) {
- lua_pushstring(L, dir);
+ for (const std::string& dir : dirs) {
+ lua_pushstring(L, dir.c_str());
lua_pushstring(L, "/themes/");
lua_pushstring(L, theme);
lua_pushstring(L, ".lua");
@@ -518,7 +555,7 @@ class LexerLPeg : public ILexer {
lua_pcall(L, 0, 0, -3) == LUA_OK)
lua_pop(L, 2); // theme, lua_error_handler
else
- log_error(L);
+ LogError(L);
free(theme);
}
SetStyles();
@@ -540,7 +577,6 @@ class LexerLPeg : public ILexer {
reinit = false;
PropertySet(LexerErrorKey, "");
- free(home);
ASSERT_STACK_TOP(L);
return true;
}
@@ -658,14 +694,14 @@ public:
}
if (lua_getfield(L, -1, "lex") != LUA_TFUNCTION)
- return log_error(L, "'lexer.lex' function not found");
+ return LogError(L, "'lexer.lex' function not found");
lua_pushcfunction(L, lua_error_handler), lua_insert(L, -2);
lua_pushvalue(L, -3);
lua_pushlstring(L, buffer->BufferPointer() + startPos, lengthDoc);
lua_pushinteger(L, styler.StyleAt(startPos));
- if (lua_pcall(L, 3, 1, -5) != LUA_OK) return log_error(L);
+ if (lua_pcall(L, 3, 1, -5) != LUA_OK) return LogError(L);
if (!lua_istable(L, -1))
- return log_error(L, "Table of tokens expected from 'lexer.lex'");
+ return LogError(L, "Table of tokens expected from 'lexer.lex'");
// Style the text from the token table returned.
int len = lua_rawlen(L, -1);
if (len > 0) {
@@ -684,10 +720,8 @@ public:
lua_pop(L, 1); // pos
if (style >= 0 && style <= STYLE_MAX)
styler.ColourTo(startPos + position - 1, style);
- else {
- lua_pushfstring(L, "Bad style number: %d", style);
- log_error(L);
- }
+ else
+ lua_pushfstring(L, "Bad style number: %d", style), LogError(L);
if (position > startPos + lengthDoc) break;
}
lua_pop(L, 1); // _TOKENSTYLES
@@ -719,7 +753,7 @@ public:
LexAccessor styler(buffer);
if (lua_getfield(L, -1, "fold") != LUA_TFUNCTION)
- return log_error(L, "'lexer.fold' function not found");
+ return LogError(L, "'lexer.fold' function not found");
lua_pushcfunction(L, lua_error_handler), lua_insert(L, -2);
lua_pushvalue(L, -3);
Sci_Position currentLine = styler.GetLine(startPos);
@@ -727,9 +761,9 @@ public:
lua_pushinteger(L, startPos);
lua_pushinteger(L, currentLine);
lua_pushinteger(L, styler.LevelAt(currentLine) & SC_FOLDLEVELNUMBERMASK);
- if (lua_pcall(L, 5, 1, -7) != LUA_OK) return log_error(L);
+ if (lua_pcall(L, 5, 1, -7) != LUA_OK) return LogError(L);
if (!lua_istable(L, -1))
- return log_error(L, "Table of folds expected from 'lexer.fold'");
+ return LogError(L, "Table of folds expected from 'lexer.fold'");
// Fold the text from the fold table returned.
lua_pushnil(L);
while (lua_next(L, -2)) { // line = level
@@ -759,6 +793,8 @@ public:
const char *key, const char *value) override
{
props.Set(key, value, strlen(key), strlen(value));
+ if (strcmp(key, LexerHomeKey) == 0 && lexer_names.empty())
+ ReadLexerNames(value); // not using SCI_LOADLEXERLIBRARY private call
if (reinit &&
(strcmp(key, LexerHomeKey) == 0 || strcmp(key, LexerNameKey) == 0))
Init();
@@ -815,18 +851,16 @@ public:
return nullptr;
case SCI_LOADLEXERLIBRARY: {
const char *path = reinterpret_cast<const char*>(arg);
- const char *old_home = props.Get(LexerHomeKey);
- char *home = reinterpret_cast<char *>(
- malloc(strlen(old_home) + 1 + strlen(path) + 1));
- char *p = home;
- if (*old_home) {
- strcpy(p, old_home), p += strlen(old_home);
- *p++ = ';';
- }
- strcpy(p, path);
- PropertySet(LexerHomeKey, home);
- free(home);
+ ReadLexerNames(path);
+ std::string home(props.Get(LexerHomeKey));
+ if (!home.empty()) home.push_back(';');
+ home.append(path);
+ PropertySet(LexerHomeKey, home.c_str());
return nullptr;
+ } case SCI_PROPERTYNAMES: {
+ std::stringstream names;
+ for (const std::string& name : lexer_names) names << name << '\n';
+ return StringResult(lParam, names.str().c_str());
} case SCI_SETLEXERLANGUAGE:
if (strcmp(
props.Get(LexerNameKey),
@@ -842,24 +876,22 @@ public:
RECORD_STACK_TOP(L);
lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this));
lua_getfield(L, -1, "_NAME");
- if (SS && sci && multilang) {
- int pos = SS(sci, SCI_GETCURRENTPOS, 0, 0);
- while (pos >= 0 && !ws[SS(sci, SCI_GETSTYLEAT, pos, 0)]) pos--;
- const char *name = nullptr, *p = nullptr;
- if (pos >= 0) {
- name = GetStyleName(SS(sci, SCI_GETSTYLEAT, pos, 0));
- if (name) p = strstr(name, "_whitespace");
+ std::string val(lua_tostring(L, -1));
+ lua_pop(L, 2); // lexer name, lexer object
+ ASSERT_STACK_TOP(L);
+ if (!SS || !sci || !multilang) return StringResult(lParam, val.c_str());
+ val.push_back('/');
+ int pos = SS(sci, SCI_GETCURRENTPOS, 0, 0);
+ while (pos >= 0 && !ws[SS(sci, SCI_GETSTYLEAT, pos, 0)]) pos--;
+ if (pos >= 0) {
+ const char *name = GetStyleName(SS(sci, SCI_GETSTYLEAT, pos, 0)), *p;
+ if (name && (p = strstr(name, "_whitespace"))) {
+ val.append(name, p - name);
+ return StringResult(lParam, val.c_str());
}
- if (!name) name = lua_tostring(L, -1); // "lexer:lexer" fallback
- if (!p) p = name + strlen(name); // "lexer:lexer" fallback
- lua_pushstring(L, "/");
- lua_pushlstring(L, name, p - name);
- lua_concat(L, 3);
}
- const char *val = lua_tostring(L, -1); // no copy needed; remains in mem
- lua_pop(L, 2); // lexer_name or lexer language string, lexer object
- ASSERT_STACK_TOP(L);
- return StringResult(lParam, val);
+ val.append(val, 0, val.length() - 1); // "lexer/lexer" fallback
+ return StringResult(lParam, val.c_str());
} case SCI_GETSTATUS:
return StringResult(lParam, props.Get(LexerErrorKey));
default: // retrieve style names