aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2024-12-21 20:57:40 +0300
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2024-12-22 19:33:48 +0300
commitc174f9be70855e89f606547cfa5471942d238038 (patch)
treef7e000c4168b8b9ac1ab76330b09c87a6e845955 /src
parent157f4235a0cb2d6a37b131e0c52fecef9566f32c (diff)
downloadsciteco-c174f9be70855e89f606547cfa5471942d238038.tar.gz
support external Scintilla lexer libraries and Scintillua in particular
* @ES/SCI_SETILEXER/lib^@name/ now opens the lexer <name> in library <lib>. * You need to define the environment variable $SCITECO_SCINTILLUA_LEXERS to point to the lexers/ subdirectory (containing the *.lua files). Perhaps this should default to the dirname of <lib>? * The semantics of SCI_NAMEOFSTYLE have been changed: It now returns style ids when given style names, so you can actually write Scintillua lexer *.tes files. This will be superfluous if we had a way to return strings from Scintilla messages into Q-Registers, e.g. 23@EPq/SCI_NAMEOFSTYLE/. * We now depend on gmodule as well, but it should always be part of glib. It does not change the library dependencies of any package. It might result in gmodule shared libraries to be bundled in the Win32 and Mac OS packages if they weren't already.
Diffstat (limited to 'src')
-rw-r--r--src/error.h9
-rw-r--r--src/symbols.c108
2 files changed, 109 insertions, 8 deletions
diff --git a/src/error.h b/src/error.h
index c51f528..021f759 100644
--- a/src/error.h
+++ b/src/error.h
@@ -17,6 +17,7 @@
#pragma once
#include <glib.h>
+#include <gmodule.h>
#include "sciteco.h"
#include "string-utils.h"
@@ -53,6 +54,7 @@ typedef enum {
TECO_ERROR_MEMLIMIT,
TECO_ERROR_CLIPBOARD,
TECO_ERROR_WIN32,
+ TECO_ERROR_MODULE,
/** Interrupt current operation */
TECO_ERROR_INTERRUPTED,
@@ -165,6 +167,13 @@ teco_error_win32_set(GError **error, const gchar *prefix, gint err)
#endif
static inline void
+teco_error_module_set(GError **error, const gchar *prefix)
+{
+ g_set_error(error, TECO_ERROR, TECO_ERROR_MODULE, "%s: %s",
+ prefix, g_module_error());
+}
+
+static inline void
teco_error_interrupted_set(GError **error)
{
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_INTERRUPTED, "Interrupted");
diff --git a/src/symbols.c b/src/symbols.c
index 944d01d..7198639 100644
--- a/src/symbols.c
+++ b/src/symbols.c
@@ -23,6 +23,7 @@
#include <string.h>
#include <glib.h>
+#include <gmodule.h>
#include <Scintilla.h>
#ifdef HAVE_LEXILLA
@@ -288,6 +289,27 @@ gboolean teco_state_scintilla_symbols_process_edit_cmd(teco_machine_main_t *ctx,
* Lexilla lexer name as a string argument for the \fBSCI_SETILEXER\fP
* message, i.e. in order to load a Lexilla lexer
* (this works similar to the old \fBSCI_SETLEXERLANGUAGE\fP message).
+ * If the lexer name contains a null-byte, the second string
+ * argument is split into two:
+ * Up until the null-byte, the path of an external lexer library
+ * (shared library or DLL) is expected,
+ * that implements the Lexilla protocol.
+ * The \(lq.so\(rq or \(lq.dll\(rq extension is optional.
+ * The concrete lexer name is the remaining of the string after
+ * the null-byte.
+ * This allows you to use lexers from external lexer libraries
+ * like Scintillua.
+ * When detecting Scintillua, \*(ST will automatically pass down
+ * the \fBSCITECO_SCINTILLUA_LEXERS\fP environment variable as
+ * the \(lqscintillua.lexers\(rq library property for specifying
+ * the location of Scintillua's Lua lexer files.
+ *
+ * In order to facilitate the use of Scintillua lexers, the semantics
+ * of \fBSCI_NAMEOFSTYLE\fP have also been changed.
+ * Instead of returning the name for a given style id, it now
+ * returns the style id when given the name of a style in the
+ * second string argument of \fBES\fP, i.e. it allows you
+ * to look up style ids by name.
*
* .BR Warning :
* Almost all Scintilla messages may be dispatched using
@@ -321,24 +343,94 @@ teco_state_scintilla_lparam_done(teco_machine_main_t *ctx, const teco_string_t *
sptr_t lParam = 0;
-#ifdef HAVE_LEXILLA
- if (ctx->scintilla.iMessage == SCI_SETILEXER) {
+ if (ctx->scintilla.iMessage == SCI_NAMEOFSTYLE) {
+ /*
+ * FIXME: This customized version of SCI_NAMEOFSTYLE could be avoided
+ * if we had a way to call Scintilla messages that return strings into
+ * Q-Registers.
+ */
if (teco_string_contains(str, '\0')) {
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
- "Lexer name must not contain null-byte.");
+ "Style name must not contain null-byte.");
return NULL;
}
- const gchar *lexer = str->data ? : "";
- lParam = (sptr_t)CreateLexer(lexer);
+ /*
+ * FIXME: Should we cache the name to style id?
+ */
+ guint count = teco_interface_ssm(SCI_GETNAMEDSTYLES, 0, 0);
+ for (guint id = 0; id < count; id++) {
+ gchar style[128] = "";
+ teco_interface_ssm(SCI_NAMEOFSTYLE, id, (sptr_t)style);
+ if (!teco_string_cmp(str, style, strlen(style))) {
+ teco_expressions_push(id);
+ return &teco_state_start;
+ }
+ }
+
+ g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Style name \"%s\" not found.", str->data ? : "");
+ return NULL;
+ }
+#ifdef HAVE_LEXILLA
+ else if (ctx->scintilla.iMessage == SCI_SETILEXER) {
+ CreateLexerFn CreateLexerFn = CreateLexer;
+
+ const gchar *lexer = memchr(str->data ? : "", '\0', str->len);
+ if (lexer) {
+ /* external lexer */
+ lexer++;
+
+ /*
+ * NOTE: The same module can be opened multiple times.
+ * They are internally reference counted.
+ */
+ GModule *module = g_module_open(str->data, G_MODULE_BIND_LAZY);
+ if (!module) {
+ teco_error_module_set(error, "Error opening lexer module");
+ return NULL;
+ }
+
+ GetNameSpaceFn GetNameSpaceFn;
+ SetLibraryPropertyFn SetLibraryPropertyFn;
+
+ if (!g_module_symbol(module, LEXILLA_GETNAMESPACE, (gpointer *)&GetNameSpaceFn) ||
+ !g_module_symbol(module, LEXILLA_SETLIBRARYPROPERTY, (gpointer *)&SetLibraryPropertyFn) ||
+ !g_module_symbol(module, LEXILLA_CREATELEXER, (gpointer *)&CreateLexerFn)) {
+ teco_error_module_set(error, "Cannot find lexer function");
+ return NULL;
+ }
+
+ if (!g_strcmp0(GetNameSpaceFn(), "scintillua")) {
+ /*
+ * Scintillua's lexer directory must be configured before calling CreateLexer().
+ *
+ * FIXME: In Scintillua distributions, the lexers are usually contained in the
+ * same directory as the prebuilt shared libraries.
+ * Perhaps we should default scintillua.lexers to the dirname in str->data?
+ */
+ teco_qreg_t *reg = teco_qreg_table_find(&teco_qreg_table_globals, "$SCITECO_SCINTILLUA_LEXERS", 26);
+ if (reg) {
+ teco_string_t dir;
+ if (!reg->vtable->get_string(reg, &dir.data, &dir.len, NULL, error))
+ return NULL;
+ SetLibraryPropertyFn("scintillua.lexers", dir.data ? : "");
+ }
+ }
+ } else {
+ /* Lexilla lexer */
+ lexer = str->data ? : "";
+ }
+
+ lParam = (sptr_t)CreateLexerFn(lexer);
if (!lParam) {
g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
- "Lexilla lexer \"%s\" not found.", lexer);
+ "Lexer \"%s\" not found.", lexer);
return NULL;
}
- } else
+ }
#endif
- if (str->len > 0) {
+ else if (str->len > 0) {
/*
* NOTE: There may even be messages that read strings
* with embedded nulls.