Last active
February 26, 2020 05:35
-
-
Save tadeokondrak/da159465c43587d9afb1936a9ecb6b3e to your computer and use it in GitHub Desktop.
kakoune_mujs.patch, applies on 93a889bd44ceb5d3d4656d251ddbbd4a88b3fe1b. allows returning one value converted to string, kak.command('args'...) works, and a few other builtin accessors
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/src/Makefile b/src/Makefile | |
| index 123ef1d8..5550e732 100644 | |
| --- a/src/Makefile | |
| +++ b/src/Makefile | |
| @@ -85,8 +85,8 @@ else | |
| $(error "pkg-config not found in PATH") | |
| endif | |
| - LIBS += $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --libs ncursesw) | |
| - NCURSES_CFLAGS += $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags ncursesw) | |
| + LIBS += $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --libs ncursesw mujs) | |
| + NCURSES_CFLAGS += $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags ncursesw mujs) | |
| LDFLAGS += -rdynamic | |
| endif | |
| diff --git a/src/command_manager.cc b/src/command_manager.cc | |
| index 82985b5d..2669e06c 100644 | |
| --- a/src/command_manager.cc | |
| +++ b/src/command_manager.cc | |
| @@ -6,6 +6,7 @@ | |
| #include "context.hh" | |
| #include "flags.hh" | |
| #include "file.hh" | |
| +#include "javascript_manager.hh" | |
| #include "optional.hh" | |
| #include "option_types.hh" | |
| #include "ranges.hh" | |
| @@ -206,6 +207,8 @@ Token::Type token_type(StringView type_name, bool throw_on_invalid) | |
| return Token::Type::RawQuoted; | |
| else if (type_name == "sh") | |
| return Token::Type::ShellExpand; | |
| + else if (type_name == "js") | |
| + return Token::Type::JavascriptExpand; | |
| else if (type_name == "reg") | |
| return Token::Type::RegisterExpand; | |
| else if (type_name == "opt") | |
| @@ -353,6 +356,8 @@ expand_token(const Token& token, const Context& context, const ShellContext& she | |
| str.resize(str.length() - trailing_eol_count, 0); | |
| return {str}; | |
| } | |
| + case Token::Type::JavascriptExpand: | |
| + return {JavascriptManager::instance().eval(content, context, shell_context)}; | |
| case Token::Type::RegisterExpand: | |
| return expand_register(content, context, IsSingle{}); | |
| case Token::Type::OptionExpand: | |
| diff --git a/src/command_manager.hh b/src/command_manager.hh | |
| index affec4e3..18c9e586 100644 | |
| --- a/src/command_manager.hh | |
| +++ b/src/command_manager.hh | |
| @@ -47,6 +47,7 @@ struct Token | |
| RawQuoted, | |
| RawEval, | |
| ShellExpand, | |
| + JavascriptExpand, | |
| RegisterExpand, | |
| OptionExpand, | |
| ValExpand, | |
| diff --git a/src/javascript_manager.cc b/src/javascript_manager.cc | |
| new file mode 100644 | |
| index 00000000..bc4333a7 | |
| --- /dev/null | |
| +++ b/src/javascript_manager.cc | |
| @@ -0,0 +1,254 @@ | |
| +#include "javascript_manager.hh" | |
| + | |
| +#include "buffer_utils.hh" | |
| +#include "command_manager.hh" | |
| +#include "file.hh" | |
| +#include "register_manager.hh" | |
| + | |
| +#include <cstring> | |
| + | |
| +namespace Kakoune | |
| +{ | |
| + | |
| +namespace | |
| +{ | |
| + | |
| +struct JavascriptContext | |
| +{ | |
| + const Context& context; | |
| + const ShellContext& shell_context; | |
| +}; | |
| + | |
| +} | |
| + | |
| +JavascriptManager::JavascriptManager() | |
| + : J(js_newstate(nullptr, nullptr, JS_STRICT)) | |
| +{ | |
| + js_pushglobal(J); | |
| + | |
| + js_newcfunction(J, kak, "kak", 0); | |
| + js_setregistry(J, "kak"); | |
| + | |
| + js_getproperty(J, -1, "Object"); | |
| + js_newuserdatax(J, "kak", nullptr, kak_hasproperty, nullptr, nullptr, nullptr); | |
| + js_defproperty(J, -2, "kak", JS_READONLY | JS_DONTENUM | JS_DONTCONF); | |
| + | |
| + js_newcfunction(J, sh, "sh", 1); | |
| + js_defproperty(J, -2, "sh", JS_READONLY | JS_DONTENUM | JS_DONTCONF); | |
| + | |
| + js_newcfunction(J, file, "file", 1); | |
| + js_defproperty(J, -2, "file", JS_READONLY | JS_DONTENUM | JS_DONTCONF); | |
| + | |
| + js_getproperty(J, -1, "Object"); | |
| + js_newuserdatax(J, "main_reg", nullptr, main_reg_hasproperty, nullptr, nullptr, nullptr); | |
| + js_defproperty(J, -2, "main_reg", JS_READONLY | JS_DONTENUM | JS_DONTCONF); | |
| + | |
| + js_getproperty(J, -1, "Object"); | |
| + js_newuserdatax(J, "reg", nullptr, reg_hasproperty, nullptr, nullptr, nullptr); | |
| + js_defproperty(J, -2, "reg", JS_READONLY | JS_DONTENUM | JS_DONTCONF); | |
| + | |
| + js_getproperty(J, -1, "Object"); | |
| + js_newuserdatax(J, "opts", nullptr, opts_hasproperty, nullptr, nullptr, nullptr); | |
| + js_defproperty(J, -2, "opts", JS_READONLY | JS_DONTENUM | JS_DONTCONF); | |
| + | |
| + js_getproperty(J, -1, "Object"); | |
| + js_newuserdatax(J, "vars", nullptr, vars_hasproperty, nullptr, nullptr, nullptr); | |
| + js_defproperty(J, -2, "vars", JS_READONLY | JS_DONTENUM | JS_DONTCONF); | |
| + | |
| + js_newcfunction(J, args, "args", 0); | |
| + js_pushnull(J); | |
| + js_defaccessor(J, -3, "args", JS_READONLY | JS_DONTENUM | JS_DONTCONF); | |
| + | |
| + js_pop(J, 1); | |
| +} | |
| + | |
| +JavascriptManager::~JavascriptManager() | |
| +{ | |
| + js_gc(J, 0); | |
| + js_freestate(J); | |
| +} | |
| + | |
| +String JavascriptManager::eval(StringView script, const Context& context, | |
| + const ShellContext& shell_context) | |
| +{ | |
| + JavascriptContext jsctx{context, shell_context}; | |
| + void* last_context = js_getcontext(J); | |
| + auto pop_context = on_scope_end([&]{ js_setcontext(J, last_context); }); | |
| + js_setcontext(J, &jsctx); | |
| + if (js_ploadstring(J, "input", script.zstr()) == 0) | |
| + { | |
| + if (js_pcall(J, -1) == 0) | |
| + { | |
| + auto ret = String(js_trystring(J, -1, "")); | |
| + js_pop(J, 1); | |
| + return ret; | |
| + } | |
| + } | |
| + write_to_debug_buffer(format("mujs error: {}", js_trystring(J, -1, ""))); | |
| + js_pop(J, 1); | |
| + return ""; | |
| +} | |
| + | |
| +void JavascriptManager::report(js_State* J, const char* message) | |
| +{ | |
| + write_to_debug_buffer(format("mujs report: {}", message)); | |
| +} | |
| + | |
| +void JavascriptManager::kak(js_State* J) noexcept | |
| +{ | |
| + auto& ctx = *reinterpret_cast<JavascriptContext*>(js_getcontext(J)); | |
| + Vector<String> args; | |
| + int top = js_gettop(J); | |
| + for (int i = 1; i < top; ++i) | |
| + { | |
| + void* save = js_savetry(J); | |
| + jmp_buf buf; | |
| + memcpy(buf, &save, sizeof(void*)); | |
| + if (setjmp(buf)) | |
| + { | |
| + args.~Vector<String>(); | |
| + js_throw(J); | |
| + } | |
| + args.push_back(js_tostring(J, i)); | |
| + js_endtry(J); | |
| + } | |
| + try | |
| + { | |
| + CommandManager::instance().execute_single_command( | |
| + args, const_cast<Context&>(ctx.context), ctx.shell_context, {}); | |
| + } | |
| + catch (exception& error) | |
| + { | |
| + args.~Vector<String>(); | |
| + js_error(J, "%s", static_cast<const char*>(error.what().zstr())); | |
| + } | |
| + js_pushundefined(J); | |
| +} | |
| + | |
| +void JavascriptManager::sh(js_State* J) noexcept | |
| +{ | |
| + auto& ctx = *reinterpret_cast<JavascriptContext*>(js_getcontext(J)); | |
| + int code; | |
| + String content; | |
| + std::tie(content, code) = ShellManager::instance().eval( | |
| + js_trystring(J, -1, ""), ctx.context, js_trystring(J, -2, ""), | |
| + ShellManager::Flags::WaitForStdout, ctx.shell_context); | |
| + if (code == 0) | |
| + { | |
| + js_pushstring(J, content.c_str()); | |
| + return; | |
| + } | |
| + content.~String(); | |
| + js_error(J, "shell command exited with exit code %d", code); | |
| +} | |
| + | |
| +void JavascriptManager::file(js_State* J) noexcept | |
| +{ | |
| + String contents; | |
| + try | |
| + { | |
| + contents = read_file(js_trystring(J, -1, "")); | |
| + } | |
| + catch (exception& error) | |
| + { | |
| + contents.~String(); | |
| + js_error(J, "%s", static_cast<const char*>(error.what().zstr())); | |
| + } | |
| + js_pushstring(J, contents.c_str()); | |
| +} | |
| + | |
| +int JavascriptManager::kak_hasproperty(js_State* J, void*, const char* name) noexcept | |
| +{ | |
| + js_newstring(J, name); | |
| + if (not CommandManager::instance().command_defined(js_tostring(J, -1))) | |
| + return 0; | |
| + js_getglobal(J, "Function"); | |
| + js_getproperty(J, -1, "prototype"); | |
| + js_getproperty(J, -1, "bind"); | |
| + js_getregistry(J, "kak"); | |
| + js_dup(J); | |
| + js_copy(J, -6); | |
| + js_call(J, 2); | |
| + return 1; | |
| +} | |
| + | |
| +int JavascriptManager::main_reg_hasproperty(js_State* J, void*, const char* name) noexcept { | |
| + auto& ctx = *reinterpret_cast<JavascriptContext*>(js_getcontext(J)); | |
| + try | |
| + { | |
| + js_newstring(J, ctx.context.main_sel_register_value(name).zstr()); | |
| + return 1; | |
| + } | |
| + catch (exception& error) | |
| + { | |
| + return 0; | |
| + } | |
| +} | |
| + | |
| +int JavascriptManager::reg_hasproperty(js_State* J, void*, const char* name) noexcept { | |
| + auto& ctx = *reinterpret_cast<JavascriptContext*>(js_getcontext(J)); | |
| + try | |
| + { | |
| + Register& reg = RegisterManager::instance()[name]; | |
| + js_newarray(J); | |
| + int i = 0; | |
| + for (const String& s : reg.get(ctx.context)) | |
| + { | |
| + js_newstring(J, s.c_str()); | |
| + js_setindex(J, -2, i++); | |
| + } | |
| + return 1; | |
| + } | |
| + catch (exception& error) | |
| + { | |
| + return 0; | |
| + } | |
| +} | |
| + | |
| +int JavascriptManager::opts_hasproperty(js_State* J, void*, const char* name) noexcept { | |
| + auto& ctx = *reinterpret_cast<JavascriptContext*>(js_getcontext(J)); | |
| + try | |
| + { | |
| + js_newarray(J); | |
| + int i = 0; | |
| + for (const String& s : ctx.context.options()[name].get_as_strings()) | |
| + { | |
| + js_newstring(J, s.c_str()); | |
| + js_setindex(J, -2, i++); | |
| + } | |
| + return 1; | |
| + } | |
| + catch (exception& error) | |
| + { | |
| + js_pop(J, 1); | |
| + return 0; | |
| + } | |
| +} | |
| + | |
| +int JavascriptManager::vars_hasproperty(js_State* J, void*, const char* name) noexcept { | |
| + auto& ctx = *reinterpret_cast<JavascriptContext*>(js_getcontext(J)); | |
| + try | |
| + { | |
| + String ret = ShellManager::instance().get_val(name, ctx.context, Quoting::Raw); | |
| + js_newstring(J, ret.c_str()); | |
| + return 1; | |
| + } | |
| + catch (exception& error) | |
| + { | |
| + js_pop(J, 1); | |
| + return 0; | |
| + } | |
| +} | |
| + | |
| +void JavascriptManager::args(js_State* J) noexcept { | |
| + auto& ctx = *reinterpret_cast<JavascriptContext*>(js_getcontext(J)); | |
| + js_newarray(J); | |
| + int i = 0; | |
| + for (const String& s : ctx.shell_context.params) | |
| + { | |
| + js_newstring(J, s.c_str()); | |
| + js_setindex(J, -2, i++); | |
| + } | |
| +} | |
| + | |
| +} | |
| diff --git a/src/javascript_manager.hh b/src/javascript_manager.hh | |
| new file mode 100644 | |
| index 00000000..4f2b6495 | |
| --- /dev/null | |
| +++ b/src/javascript_manager.hh | |
| @@ -0,0 +1,39 @@ | |
| +#ifndef javascript_manager_hh_INCLUDED | |
| +#define javascript_manager_hh_INCLUDED | |
| + | |
| +#include <mujs.h> | |
| + | |
| +#include "shell_manager.hh" | |
| +#include "context.hh" | |
| +#include "string.hh" | |
| +#include "utils.hh" | |
| + | |
| +namespace Kakoune | |
| +{ | |
| + | |
| +class JavascriptManager : public Singleton<JavascriptManager> | |
| +{ | |
| +public: | |
| + JavascriptManager(); | |
| + ~JavascriptManager(); | |
| + | |
| + String eval(StringView script, const Context& context, const ShellContext& shell_context); | |
| + | |
| +private: | |
| + js_State* J; | |
| + | |
| + static void report(js_State* J, const char* message); | |
| + static void kak(js_State* J) noexcept; | |
| + static void sh(js_State* J) noexcept; | |
| + static void file(js_State* J) noexcept; | |
| + static int kak_hasproperty(js_State* J, void*, const char* name) noexcept; | |
| + static int main_reg_hasproperty(js_State* J, void*, const char* name) noexcept; | |
| + static int reg_hasproperty(js_State* J, void*, const char* name) noexcept; | |
| + static int opts_hasproperty(js_State* J, void*, const char* name) noexcept; | |
| + static int vars_hasproperty(js_State* J, void*, const char* name) noexcept; | |
| + static void args(js_State* J) noexcept; | |
| +}; | |
| + | |
| +} | |
| + | |
| +#endif // javascript_manager_hh_INCLUDED | |
| diff --git a/src/main.cc b/src/main.cc | |
| index 79e0793d..15176947 100644 | |
| --- a/src/main.cc | |
| +++ b/src/main.cc | |
| @@ -12,6 +12,7 @@ | |
| #include "file.hh" | |
| #include "highlighters.hh" | |
| #include "insert_completer.hh" | |
| +#include "javascript_manager.hh" | |
| #include "json_ui.hh" | |
| #include "ncurses_ui.hh" | |
| #include "option_types.hh" | |
| @@ -710,6 +711,7 @@ int run_server(StringView session, StringView server_init, | |
| StringRegistry string_registry; | |
| GlobalScope global_scope; | |
| ShellManager shell_manager{builtin_env_vars}; | |
| + JavascriptManager javascript_manager; | |
| CommandManager command_manager; | |
| RegisterManager register_manager; | |
| HighlighterRegistry highlighter_registry; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment