461 lines
14 KiB
C++
461 lines
14 KiB
C++
lua_State *LuaState = NULL;
|
|
String16 LuaCommandResult = {};
|
|
|
|
String FieldString(lua_State *L, String name) {
|
|
String result = {};
|
|
if (lua_istable(L, -1)) {
|
|
lua_pushlstring(L, name.data, name.len);
|
|
lua_gettable(L, -2);
|
|
defer { lua_pop(L, 1); };
|
|
if (lua_isstring(L, -1)) {
|
|
result = lua_tostring(L, -1);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ExecInNewBuffer(String cmd, String working_dir) {
|
|
BSet main = GetActiveMainSet();
|
|
CheckpointBeforeGoto(main.window);
|
|
|
|
Scratch scratch;
|
|
String buffer_name = GetUniqueBufferName(scratch, working_dir, "+cmd-");
|
|
|
|
View *view = WindowOpenBufferView(main.window, buffer_name);
|
|
Buffer *buffer = GetBuffer(view->active_buffer);
|
|
buffer->gc = true;
|
|
Command_SelectRangeOneCursor(view, Rng(0));
|
|
|
|
Exec(view->id, false, cmd, working_dir);
|
|
ActiveWindow = main.window->id;
|
|
}
|
|
|
|
void Open(String path) {
|
|
Scratch scratch;
|
|
|
|
lua_getglobal(LuaState, "ApplyRules");
|
|
lua_pushlstring(LuaState, path.data, path.len);
|
|
if (lua_pcall(LuaState, 1, 1, 0) != 0) {
|
|
const char *error_message = lua_tostring(LuaState, -1);
|
|
ReportWarningf("Failed the call to ApplyRules! %s", error_message);
|
|
lua_pop(LuaState, 1);
|
|
return;
|
|
}
|
|
|
|
if (FieldString(LuaState, "kind") == "text") {
|
|
String file_path = FieldString(LuaState, "file_path");
|
|
String line_string = FieldString(LuaState, "line");
|
|
Int line = strtoll(line_string.data, NULL, 10);
|
|
String col_string = FieldString(LuaState, "col");
|
|
Int col = strtoll(col_string.data, NULL, 10);
|
|
|
|
BSet main = GetActiveMainSet();
|
|
CheckpointBeforeGoto(main.window);
|
|
View *view = WindowOpenBufferView(main.window, file_path);
|
|
Buffer *buffer = GetBuffer(view->active_buffer);
|
|
if (line != -1 && col != -1) {
|
|
Int pos = XYToPos(buffer, {col - 1, line - 1});
|
|
view->carets[0] = MakeCaret(pos);
|
|
}
|
|
UpdateScroll(main.window, true);
|
|
ActiveWindow = main.window->id;
|
|
} else if (FieldString(LuaState, "kind") == "exec") {
|
|
String cmd = FieldString(LuaState, "cmd");
|
|
String working_dir = FieldString(LuaState, "working_dir");
|
|
ExecInNewBuffer(cmd, working_dir);
|
|
} else {
|
|
ReportWarningf("Failed to match any of ApplyRules results!");
|
|
}
|
|
}
|
|
|
|
void Open(String16 path) {
|
|
Scratch scratch;
|
|
String string = ToString(scratch, path);
|
|
Open(string);
|
|
}
|
|
|
|
int Lua_FuzzySort(lua_State *L) {
|
|
String string = lua_tostring(L, 1);
|
|
lua_pop(L, 1);
|
|
Scratch scratch;
|
|
String16 string16 = ToString16(scratch, string);
|
|
|
|
BSet main = GetActiveMainSet();
|
|
Command_FuzzySort(main.view, string16);
|
|
return 0;
|
|
}
|
|
|
|
int Lua_AppendCmd(lua_State *L) {
|
|
String string = lua_tostring(L, 1);
|
|
lua_pop(L, 1);
|
|
String working_dir = GetActiveMainWindowBufferDir();
|
|
Exec(NullViewID, true, string, working_dir);
|
|
return 0;
|
|
}
|
|
|
|
int Lua_C(lua_State *L) {
|
|
String string = lua_tostring(L, 1);
|
|
lua_pop(L, 1);
|
|
|
|
String working_dir = GetActiveMainWindowBufferDir();
|
|
ExecInNewBuffer(string, working_dir);
|
|
return 0;
|
|
}
|
|
|
|
int Lua_Kill(lua_State *L) {
|
|
BSet main = GetActiveMainSet();
|
|
KillProcess(main.view);
|
|
return 0;
|
|
}
|
|
|
|
int Lua_Open(lua_State *L) {
|
|
Scratch scratch;
|
|
String path = luaL_checkstring(L, 1);
|
|
lua_pop(L, 1);
|
|
Open(path);
|
|
return 0;
|
|
}
|
|
|
|
int Lua_Reopen(lua_State *L) {
|
|
BSet main = GetActiveMainSet();
|
|
ReopenBuffer(main.view, main.buffer);
|
|
return 0;
|
|
}
|
|
|
|
int Lua_Print(lua_State *L) {
|
|
Scratch scratch;
|
|
String string = lua_tostring(L, 1);
|
|
lua_pop(L, 1);
|
|
|
|
View *null_view = GetView(NullViewID);
|
|
Command_Append(null_view, string, true);
|
|
return 0;
|
|
}
|
|
|
|
int Lua_ListBuffers(lua_State *L) {
|
|
Command_ListBuffers();
|
|
return 0;
|
|
}
|
|
|
|
int Lua_GetBufferList(lua_State *L) {
|
|
lua_createtable(L, 0, (int)Buffers.len);
|
|
|
|
int i = 1;
|
|
For(Buffers) {
|
|
if (StartsWith(it.o->name, "+titlebar")) continue;
|
|
lua_pushinteger(L, i++);
|
|
lua_pushlstring(L, it.o->name.data, it.o->name.len);
|
|
lua_settable(L, -3); /* 3rd element from the stack top */
|
|
}
|
|
/* We still have table left on top of the Lua stack. */
|
|
return 1;
|
|
}
|
|
|
|
int Lua_FileExists(lua_State *L) {
|
|
String path = luaL_checkstring(L, 1);
|
|
lua_pop(L, 1);
|
|
bool exists = FileExists(path);
|
|
lua_pushboolean(L, exists);
|
|
return 1;
|
|
}
|
|
|
|
int Lua_GetWorkingDir(lua_State *L) {
|
|
lua_pushlstring(L, WorkingDir.data, WorkingDir.len);
|
|
return 1;
|
|
}
|
|
|
|
int Lua_GetActiveMainWindowBufferName(lua_State *L) {
|
|
String name = GetActiveMainWindowBufferName();
|
|
lua_pushlstring(L, name.data, name.len);
|
|
return 1;
|
|
}
|
|
|
|
int Lua_GetActiveMainWindowBufferDir(lua_State *L) {
|
|
String name = GetActiveMainWindowBufferDir();
|
|
lua_pushlstring(L, name.data, name.len);
|
|
return 1;
|
|
}
|
|
|
|
void ListFilesRecursive(Buffer *buffer, String filename) {
|
|
Scratch scratch(buffer->line_starts.allocator);
|
|
for (FileIter it = IterateFiles(scratch, filename); IsValid(it); Advance(&it)) {
|
|
if (it.filename == ".git") continue;
|
|
if (it.is_directory) {
|
|
ListFilesRecursive(buffer, it.absolute_path);
|
|
} else {
|
|
IKnowWhatImDoing_Appendf(buffer, "%.*s\n", FmtString(it.absolute_path));
|
|
}
|
|
}
|
|
}
|
|
|
|
int Lua_Ls(lua_State *L) {
|
|
BSet main = GetActiveMainSet();
|
|
|
|
Scratch scratch;
|
|
String buffer_name = GetUniqueBufferName(scratch, GetDir(main.buffer), "+ls-");
|
|
|
|
Buffer *buffer = CreateBuffer(GetSystemAllocator(), buffer_name, 4096 * 4);
|
|
ListFilesRecursive(buffer, GetActiveMainWindowBufferDir());
|
|
WindowOpenBufferView(main.window, buffer_name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Lua_Search(lua_State *L) {
|
|
BSet main = GetActiveMainSet();
|
|
main.window->search_string = lua_tostring(L, 1);
|
|
lua_pop(L, 1);
|
|
|
|
Scratch scratch;
|
|
Command_Find(main.view, ToString16(scratch, main.window->search_string), true);
|
|
return 0;
|
|
}
|
|
|
|
int Lua_SearchB(lua_State *L) {
|
|
BSet main = GetActiveMainSet();
|
|
main.window->search_string = lua_tostring(L, 1);
|
|
lua_pop(L, 1);
|
|
Scratch scratch;
|
|
Command_Find(main.view, ToString16(scratch, main.window->search_string), false);
|
|
return 0;
|
|
}
|
|
|
|
int Lua_Rename(lua_State *L) {
|
|
String string = lua_tostring(L, 1);
|
|
lua_pop(L, 1);
|
|
return 0;
|
|
}
|
|
|
|
static void HookLuaForceExit(lua_State *L, lua_Debug *debug) {
|
|
SDL_PumpEvents();
|
|
int numkeys = 0;
|
|
const uint8_t *keys = SDL_GetKeyboardState(&numkeys);
|
|
if (keys[SDL_SCANCODE_F9] > 0)
|
|
luaL_error(L, "lua execution got interrupted");
|
|
}
|
|
|
|
extern luaL_Reg LuaFunctions[];
|
|
|
|
Int GetStyleInt(String name, Int default_int) {
|
|
Int result = default_int;
|
|
lua_getglobal(LuaState, "Style");
|
|
defer { lua_pop(LuaState, 1); };
|
|
if (lua_istable(LuaState, -1)) {
|
|
lua_pushlstring(LuaState, name.data, name.len);
|
|
lua_gettable(LuaState, -2);
|
|
defer { lua_pop(LuaState, 1); };
|
|
if (lua_isnumber(LuaState, -1) || lua_isboolean(LuaState, -1) || lua_isinteger(LuaState, -1)) {
|
|
lua_Integer num = lua_tointeger(LuaState, -1);
|
|
result = (Int)num;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
String GetStyleString(String name, String default_string) {
|
|
String result = default_string;
|
|
lua_getglobal(LuaState, "Style");
|
|
defer { lua_pop(LuaState, 1); };
|
|
if (lua_istable(LuaState, -1)) {
|
|
lua_pushlstring(LuaState, name.data, name.len);
|
|
lua_gettable(LuaState, -2);
|
|
defer { lua_pop(LuaState, 1); };
|
|
if (lua_isstring(LuaState, -1)) {
|
|
const char *string = lua_tostring(LuaState, -1);
|
|
result = Intern(&GlobalInternTable, string);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Color GetColor(String name, Color default_color) {
|
|
Color result = default_color;
|
|
lua_getglobal(LuaState, "Color");
|
|
defer { lua_pop(LuaState, 1); };
|
|
if (lua_istable(LuaState, -1)) {
|
|
lua_pushlstring(LuaState, name.data, name.len);
|
|
lua_gettable(LuaState, -2);
|
|
defer { lua_pop(LuaState, 1); };
|
|
if (lua_isnumber(LuaState, -1)) {
|
|
lua_Integer num = lua_tointeger(LuaState, -1);
|
|
result.r = (uint8_t)((0xFF000000 & num) >> 24);
|
|
result.g = (uint8_t)((0x00FF0000 & num) >> 16);
|
|
result.b = (uint8_t)((0x0000FF00 & num) >> 8);
|
|
result.a = (uint8_t)((0x000000FF & num) >> 0);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Int GetInt(lua_State *L, const char *name) {
|
|
lua_getfield(L, -1, name);
|
|
lua_Integer num = lua_tointeger(L, -1);
|
|
lua_pop(L, 1);
|
|
return (Int)num;
|
|
}
|
|
|
|
const char *GetString(lua_State *L, const char *name) {
|
|
lua_getfield(L, -1, name);
|
|
const char *result = lua_tostring(L, -1);
|
|
lua_pop(L, 1);
|
|
return result;
|
|
}
|
|
|
|
Vec2 GetVec2(lua_State *L, const char *name) {
|
|
Vec2 result = {};
|
|
lua_getfield(L, -1, name);
|
|
defer { lua_pop(L, 1); };
|
|
|
|
{
|
|
lua_getfield(L, -1, "1");
|
|
defer { lua_pop(L, 1); };
|
|
result.x = (float)lua_tonumber(L, -1);
|
|
}
|
|
|
|
{
|
|
lua_getfield(L, -1, "2");
|
|
defer { lua_pop(L, 1); };
|
|
result.y = (float)lua_tonumber(L, -1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// :Event
|
|
int Lua_Play(lua_State *L) {
|
|
if (!lua_istable(L, -1)) luaL_error(L, "expected a table of events");
|
|
defer { lua_pop(L, 1); };
|
|
|
|
int size = (int)lua_rawlen(L, -1);
|
|
for (int i = 0; i < size; i += 1) {
|
|
lua_geti(L, -1, i + 1);
|
|
if (!lua_istable(L, -1)) luaL_error(L, "expected a table of events");
|
|
defer { lua_pop(L, 1); };
|
|
|
|
Event event = {};
|
|
event.kind = (EventKind)GetInt(L, "kind");
|
|
event.key = (SDL_Keycode)GetInt(L, "key");
|
|
event.xmouse = (int16_t)GetInt(L, "xmouse");
|
|
event.ymouse = (int16_t)GetInt(L, "ymouse");
|
|
event.xwindow = (int16_t)GetInt(L, "xwindow");
|
|
event.ywindow = (int16_t)GetInt(L, "ywindow");
|
|
event.clicks = (uint8_t)GetInt(L, "clicks");
|
|
event.flags = (uint8_t)GetInt(L, "flags");
|
|
event.text = (char *)GetString(L, "text");
|
|
event.wheel = GetVec2(L, "wheel");
|
|
Add(&EventPlayback, event);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ReloadStyle();
|
|
extern String BaseLuaConfig;
|
|
|
|
int LoadLuaBuffer(Buffer *lua_buffer) {
|
|
if (!lua_buffer || lua_buffer->dirty || lua_buffer->change_id == lua_buffer->user_change_id) return false;
|
|
ReportConsolef("reloading config: %.*s", FmtString(lua_buffer->name));
|
|
|
|
Scratch scratch;
|
|
String string = AllocCharString(scratch, lua_buffer);
|
|
if (luaL_dostring(LuaState, string.data) == LUA_OK) {
|
|
if (lua_isstring(LuaState, -1)) {
|
|
const char *text = lua_tostring(LuaState, -1);
|
|
ReportConsolef(text);
|
|
lua_pop(LuaState, 1);
|
|
}
|
|
} else {
|
|
const char *error_message = lua_tostring(LuaState, -1);
|
|
ReportWarningf("Failed to load user config! %s", error_message);
|
|
lua_pop(LuaState, 1);
|
|
}
|
|
lua_buffer->user_change_id = lua_buffer->change_id;
|
|
return true;
|
|
}
|
|
|
|
void UpdateLua() {
|
|
int base_config_reload = LoadLuaBuffer(LuaConfigBuffer);
|
|
int project_config_reload = LoadLuaBuffer(LuaProjectBuffer);
|
|
|
|
if (base_config_reload || project_config_reload) {
|
|
ReportConsolef("reloading style variables because config changed");
|
|
ReloadStyle();
|
|
ReloadFont(); // @todo: maybe don't reload font if nothing changed
|
|
For(Windows) {
|
|
Window *window = it.o;
|
|
if (!window->visible || window->absolute_position || window->is_title_bar) continue;
|
|
window->draw_scrollbar = StyleDrawScrollbar;
|
|
window->draw_line_numbers = StyleDrawLineNumbers;
|
|
}
|
|
}
|
|
|
|
if (luaL_dostring(LuaState, "OnUpdate()") != 0) {
|
|
const char *error_message = lua_tostring(LuaState, -1);
|
|
ReportWarningf("OnUpdate failed! %s", error_message);
|
|
lua_pop(LuaState, 1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void InitLuaConfig() {
|
|
LuaState = luaL_newstate();
|
|
luaL_openlibs(LuaState);
|
|
lua_sethook(LuaState, HookLuaForceExit, LUA_MASKCOUNT, 100000000);
|
|
|
|
for (int i = 0; LuaFunctions[i].name; i += 1) {
|
|
lua_pushcfunction(LuaState, LuaFunctions[i].func);
|
|
lua_setglobal(LuaState, LuaFunctions[i].name);
|
|
}
|
|
|
|
// Init base config, test that it works and initialize the lua stuff
|
|
ReportConsolef("load base lua config");
|
|
if (!luaL_dostring(LuaState, BaseLuaConfig.data) == LUA_OK) {
|
|
const char *error_message = lua_tostring(LuaState, -1);
|
|
ReportErrorf("Failed to load base lua config! %s", error_message);
|
|
lua_pop(LuaState, 1);
|
|
Assert(!"Invalid codepath");
|
|
}
|
|
|
|
// Init user config
|
|
Scratch scratch;
|
|
String lua_config_path = Format(scratch, "%.*s/init.lua", FmtString(ConfigDir));
|
|
|
|
#if DEBUG_BUILD
|
|
// WARNING! Delete config to make sure we are running this code more frequently
|
|
SDL_RemovePath(lua_config_path.data);
|
|
ReportConsolef("deleting config for debug purposes!");
|
|
#endif
|
|
|
|
Buffer *lua_buffer = BufferOpenFile(lua_config_path);
|
|
if (lua_buffer->len == 0) {
|
|
String16 string16 = ToString16(scratch, BaseLuaConfig);
|
|
IKnowWhatImDoing_ReplaceText(lua_buffer, {}, string16);
|
|
ReportConsolef("no config at: %.*s - creating config buffer", FmtString(lua_config_path));
|
|
}
|
|
|
|
lua_buffer->user_change_id = -1; // if we loaded from file this should force to read
|
|
LuaConfigBuffer = lua_buffer;
|
|
|
|
UpdateLua();
|
|
}
|
|
|
|
//
|
|
|
|
bool EvalString(Allocator allocator, String16 string16) {
|
|
Scratch scratch((Arena *)allocator.object);
|
|
String string = ToString(scratch, string16);
|
|
if (luaL_dostring(LuaState, string.data) != LUA_OK) {
|
|
const char *error_message = lua_tostring(LuaState, -1);
|
|
ReportConsolef("Execution error! %s", error_message);
|
|
lua_pop(LuaState, 1);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Command_EvalLua(View *view, String16 string) {
|
|
Scratch scratch;
|
|
Buffer *buffer = GetBuffer(view->active_buffer);
|
|
return EvalString(scratch, string);
|
|
}
|