Compare commits
10 Commits
bbf97eba2f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ab6639afb | ||
|
|
7a73a982d2 | ||
|
|
51f1fd1b4f | ||
|
|
930620a49e | ||
|
|
8cb1b49cd8 | ||
|
|
830be12b24 | ||
|
|
ed3be39037 | ||
|
|
33d623268a | ||
|
|
5198c11fc6 | ||
|
|
bc7c52e1ec |
@@ -7,7 +7,7 @@
|
||||
- Window position: vscode opens in fullscreen and then remembers what position it was close in (could be a config option)
|
||||
- On Linux: Try to implement is_debugger_present()
|
||||
- Maybe IPC for te.exe when it's already open and file arguments are passed it should perhaps open a buffer in current window??
|
||||
- Add <<File>> <<ProjectDirectory>> template strings to Open (Then remove SEtWorkdirhere)
|
||||
- Add <<File>> <<ProjectFolder>> template strings to Open (Then remove SEtWorkdirhere)
|
||||
- :Set Filename to name current buffer ??? :O and others like that!!
|
||||
- Make a fuzzy command !> grep and fuzzy over it??? (doesn't seem very useful for grep)
|
||||
- Make the equivalent of SearchOpenBuffers but for cmds like !@git grep -n "@>"
|
||||
|
||||
@@ -85,6 +85,14 @@
|
||||
#define IF_DEBUG(x)
|
||||
#endif
|
||||
|
||||
#if OS_WINDOWS
|
||||
#define IF_OS_WINDOWS_ELSE(x, y) x
|
||||
#define IF_OS_WINDOWS(x) x
|
||||
#else
|
||||
#define IF_OS_WINDOWS_ELSE(x, y) y
|
||||
#define IF_OS_WINDOWS(x)
|
||||
#endif
|
||||
|
||||
#if OS_WINDOWS
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
|
||||
@@ -257,204 +257,6 @@ API FileIter IterateFiles(Allocator alo, String path) {
|
||||
return it;
|
||||
}
|
||||
|
||||
struct UnixProcess {
|
||||
pid_t pid;
|
||||
int child_stdout_read;
|
||||
int stdin_write;
|
||||
};
|
||||
|
||||
API Array<char *> SplitCommand(Allocator allocator, String command_line) {
|
||||
Array<char *> cmd = {allocator};
|
||||
|
||||
String curr = {};
|
||||
for (int i = 0; i < command_line.len; i += 1) {
|
||||
if (command_line.data[i] == ' ') {
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
curr = {};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (curr.len == 0) {
|
||||
curr.data = command_line.data + i;
|
||||
}
|
||||
curr.len += 1;
|
||||
}
|
||||
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
API Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
Scratch scratch;
|
||||
const int PIPE_READ = 0;
|
||||
const int PIPE_WRITE = 1;
|
||||
bool error = false;
|
||||
|
||||
working_dir = Copy(scratch, working_dir);
|
||||
chdir(working_dir.data);
|
||||
|
||||
Process process = {};
|
||||
UnixProcess *plat = (UnixProcess *)&process.platform;
|
||||
Array<char *> args = SplitCommand(scratch, command_line);
|
||||
Array<char *> env = {scratch};
|
||||
|
||||
For (enviroment) {
|
||||
Add(&env, Copy(scratch, it).data);
|
||||
}
|
||||
|
||||
int stdout_desc[2] = {};
|
||||
int stdin_desc[2] = {};
|
||||
posix_spawn_file_actions_t actions = {};
|
||||
|
||||
if (posix_spawn_file_actions_init(&actions) != 0) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_init, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
};
|
||||
|
||||
if (pipe(stdout_desc) == -1) {
|
||||
Error("Libc function failed: pipe, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
if (error) {
|
||||
close(stdout_desc[PIPE_READ]);
|
||||
close(stdout_desc[PIPE_WRITE]);
|
||||
} else {
|
||||
close(stdout_desc[PIPE_WRITE]);
|
||||
}
|
||||
};
|
||||
|
||||
if (pipe(stdin_desc) == -1) {
|
||||
Error("Libc function failed: pipe, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
if (error) {
|
||||
close(stdin_desc[PIPE_READ]);
|
||||
close(stdin_desc[PIPE_WRITE]);
|
||||
} else {
|
||||
close(stdin_desc[PIPE_READ]);
|
||||
}
|
||||
};
|
||||
|
||||
error = posix_spawn_file_actions_addclose(&actions, stdout_desc[PIPE_READ]) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDOUT_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDOUT_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDERR_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDERR_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_addclose(&actions, stdin_desc[PIPE_WRITE]) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdin_desc[PIPE_READ], STDIN_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDIN_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
pid_t process_pid = 0;
|
||||
error = posix_spawnp(&process_pid, args[0], &actions, NULL, args.data, env.data) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: failed to create process\n, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
|
||||
plat->child_stdout_read = stdout_desc[PIPE_READ];
|
||||
plat->stdin_write = stdin_desc[PIPE_WRITE];
|
||||
plat->pid = process_pid;
|
||||
|
||||
if (write_stdin.len) {
|
||||
WriteStdin(&process, write_stdin);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
process.id = process_pid;
|
||||
process.is_valid = true;
|
||||
return process;
|
||||
}
|
||||
|
||||
API bool IsValid(Process *process) {
|
||||
UnixProcess *plat = (UnixProcess *)&process->platform;
|
||||
if (process->is_valid == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
pollfd p = {};
|
||||
p.fd = plat->child_stdout_read;
|
||||
p.events = POLLRDHUP | POLLERR | POLLHUP | POLLNVAL;
|
||||
int res = poll(&p, 1, 0);
|
||||
if (res > 0) {
|
||||
pid_t result = waitpid(plat->pid, &status, 0);
|
||||
Assert(result != -1);
|
||||
process->exit_code = WEXITSTATUS(status);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
API void KillProcess(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
kill(plat->pid, SIGKILL);
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
API String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
Assert(process->is_valid);
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
|
||||
String result = {};
|
||||
result.data = AllocArray(allocator, char, 16 * 4096);
|
||||
|
||||
pollfd p = {};
|
||||
p.fd = plat->child_stdout_read;
|
||||
p.events = POLLIN;
|
||||
int res = poll(&p, 1, 0);
|
||||
if (res > 0 || force_read) {
|
||||
result.len = read(plat->child_stdout_read, result.data, 4 * 4096);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
ssize_t size = write(plat->stdin_write, string.data, string.len);
|
||||
|
||||
Assert(size == string.len);
|
||||
}
|
||||
|
||||
API void CloseStdin(Process *process) {
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
close(plat->stdin_write);
|
||||
}
|
||||
|
||||
#elif OS_WINDOWS
|
||||
|
||||
@@ -725,210 +527,6 @@ API MakeDirResult MakeDir(String path) {
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Win32Process {
|
||||
HANDLE handle;
|
||||
HANDLE child_stdout_read;
|
||||
HANDLE child_stdout_write;
|
||||
HANDLE child_stdin_read;
|
||||
HANDLE child_stdin_write;
|
||||
};
|
||||
// static_assert(sizeof(Win32Process) < sizeof(Process::platform));
|
||||
|
||||
static void Win32ReportError(String msg, String cmd) {
|
||||
LPVOID lpMsgBuf;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||
defer { LocalFree(lpMsgBuf); };
|
||||
char *buff = (char *)lpMsgBuf;
|
||||
size_t buffLen = strlen((const char *)buff);
|
||||
if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0;
|
||||
Error("%S: %S! %s", msg, cmd, (char *)lpMsgBuf);
|
||||
}
|
||||
|
||||
static void Win32CloseProcess(Process *process) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle);
|
||||
if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write);
|
||||
if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read);
|
||||
if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read);
|
||||
if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write);
|
||||
process->is_valid = false;
|
||||
}
|
||||
|
||||
static void Win32ProcessError(Process *process, String msg, String cmd) {
|
||||
Win32ReportError(msg, cmd);
|
||||
Win32CloseProcess(process);
|
||||
}
|
||||
|
||||
API void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
Assert(p->child_stdin_write != INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD written = 0;
|
||||
bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0;
|
||||
|
||||
Assert(write_error == false);
|
||||
Assert(written == string.len);
|
||||
}
|
||||
|
||||
API void CloseStdin(Process *process) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
CloseHandle(p->child_stdin_write);
|
||||
p->child_stdin_write = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
API Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
Process process = {};
|
||||
Win32Process *p = (Win32Process *)process.platform;
|
||||
|
||||
Scratch scratch;
|
||||
command_line = Format(scratch, "/C %S", command_line);
|
||||
|
||||
p->handle = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_write = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdin_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdin_write = INVALID_HANDLE_VALUE;
|
||||
|
||||
SECURITY_ATTRIBUTES security_atrb = {};
|
||||
security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
security_atrb.bInheritHandle = TRUE;
|
||||
|
||||
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
STARTUPINFOW startup = {};
|
||||
startup.cb = sizeof(STARTUPINFO);
|
||||
startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||
startup.hStdInput = p->child_stdin_read;
|
||||
startup.hStdOutput = p->child_stdout_write;
|
||||
startup.hStdError = p->child_stdout_write;
|
||||
startup.wShowWindow = SW_HIDE;
|
||||
|
||||
String16 cwd = ToString16(scratch, working_dir);
|
||||
String16 cmd = ToString16(scratch, command_line);
|
||||
|
||||
char *env = NULL;
|
||||
if (enviroment.len) {
|
||||
Int size = GetSize(enviroment) + enviroment.len + 1;
|
||||
env = AllocArray(scratch, char, size);
|
||||
Int i = 0;
|
||||
For(enviroment) {
|
||||
MemoryCopy(env + i, it.data, it.len);
|
||||
i += it.len;
|
||||
|
||||
env[i++] = 0;
|
||||
}
|
||||
env[i++] = 0;
|
||||
}
|
||||
|
||||
DWORD dwCreationFlags = 0;
|
||||
BOOL bInheritHandles = TRUE;
|
||||
|
||||
PROCESS_INFORMATION info = {};
|
||||
if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", (wchar_t *)cmd.data, 0, 0, bInheritHandles, dwCreationFlags, env, (wchar_t *)cwd.data, &startup, &info)) {
|
||||
Win32ProcessError(&process, "failed to create process", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
// Close handles to the stdin and stdout pipes no longer needed by the child process.
|
||||
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
|
||||
CloseHandle(info.hThread);
|
||||
CloseHandle(p->child_stdin_read);
|
||||
CloseHandle(p->child_stdout_write);
|
||||
p->child_stdin_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_write = INVALID_HANDLE_VALUE;
|
||||
|
||||
p->handle = info.hProcess;
|
||||
process.is_valid = true;
|
||||
process.id = (int64_t)p->handle;
|
||||
|
||||
if (write_stdin.len) {
|
||||
WriteStdin(&process, write_stdin);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
API bool IsValid(Process *process) {
|
||||
if (process->is_valid == false) return false;
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD exit_code;
|
||||
bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0;
|
||||
if (get_exit_code_failed) {
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
process->exit_code = (int)exit_code;
|
||||
Win32CloseProcess(process);
|
||||
return false;
|
||||
}
|
||||
|
||||
API void KillProcess(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
bool terminate_process_error = TerminateProcess(p->handle, -1) == 0;
|
||||
if (terminate_process_error) {
|
||||
Assert(0);
|
||||
}
|
||||
Win32CloseProcess(process);
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
API String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
DWORD bytes_avail = 0;
|
||||
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
|
||||
if (peek_error) {
|
||||
return {};
|
||||
} else if (bytes_avail == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16));
|
||||
char *buffer = AllocArray(allocator, char, buffer_size);
|
||||
|
||||
DWORD bytes_read = 0;
|
||||
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
|
||||
if (read_error) {
|
||||
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
|
||||
Dealloc(allocator, buffer);
|
||||
Win32CloseProcess(process);
|
||||
return {};
|
||||
}
|
||||
|
||||
String result = {buffer, bytes_read};
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif OS_WASM
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -1122,54 +720,6 @@ API FileIter IterateFiles(Allocator alo, String path) {
|
||||
return it;
|
||||
}
|
||||
|
||||
API Array<char *> SplitCommand(Allocator allocator, String command_line) {
|
||||
Array<char *> cmd = {allocator};
|
||||
|
||||
String curr = {};
|
||||
for (int i = 0; i < command_line.len; i += 1) {
|
||||
if (command_line.data[i] == ' ') {
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
curr = {};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (curr.len == 0) {
|
||||
curr.data = command_line.data + i;
|
||||
}
|
||||
curr.len += 1;
|
||||
}
|
||||
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
API Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
return {};
|
||||
}
|
||||
|
||||
API bool IsValid(Process *process) {
|
||||
return false;
|
||||
}
|
||||
|
||||
API void KillProcess(Process *process) {
|
||||
}
|
||||
|
||||
API String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
return {};
|
||||
}
|
||||
|
||||
API void WriteStdin(Process *process, String string) {
|
||||
return;
|
||||
}
|
||||
|
||||
API void CloseStdin(Process *process) {
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
API double GetTimeSeconds() {
|
||||
|
||||
@@ -33,22 +33,9 @@ String GetWorkingDir(Allocator arena);
|
||||
bool IsAbsolute(String path);
|
||||
int64_t GetFileModTime(String file);
|
||||
|
||||
struct Process {
|
||||
bool is_valid;
|
||||
int exit_code;
|
||||
char platform[6 * 8];
|
||||
int64_t id;
|
||||
|
||||
int64_t view_id; // text editor view
|
||||
bool scroll_to_end;
|
||||
};
|
||||
|
||||
Process SpawnProcess(String command_line, String working_dir, String write_stdin = {}, Array<String> enviroment = {});
|
||||
bool IsValid(Process *process);
|
||||
void KillProcess(Process *process);
|
||||
String PollStdout(Allocator allocator, Process *process, bool force_read);
|
||||
void WriteStdin(Process *process, String string);
|
||||
void CloseStdin(Process *process);
|
||||
|
||||
double GetTimeMicros(void);
|
||||
|
||||
enum MakeDirResult {
|
||||
|
||||
@@ -73,10 +73,10 @@ static const char *glsl_fshader_es3 = R"==(#version 300 es
|
||||
}
|
||||
)==";
|
||||
|
||||
void ReportWarningf(const char *fmt, ...);
|
||||
void ReportErrorf(const char *fmt, ...);
|
||||
void GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user) {
|
||||
Unused(source); Unused(type); Unused(id); Unused(length); Unused(user);
|
||||
ReportWarningf("OpenGL message: %s", message);
|
||||
ReportErrorf("OpenGL message: %s", message);
|
||||
if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OpenGL error", message, NULL);
|
||||
}
|
||||
|
||||
@@ -415,8 +415,7 @@ API Int OnCharClassBoundary_GetNextWordEnd(Buffer *buffer, Int pos) {
|
||||
}
|
||||
|
||||
API Int OnCharClassBoundary_GetPrevWordStart(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
Int i = pos - 1;
|
||||
Int i = Clamp(pos - 1, (Int)0, buffer->len);
|
||||
|
||||
if (i >= 0 && GetCharClass(buffer->str[i]) == CharClass_Whitespace) {
|
||||
i -= 1;
|
||||
@@ -1558,7 +1557,7 @@ void SaveBuffer(Buffer *buffer) {
|
||||
buffer->dirty = false;
|
||||
buffer->temp = false;
|
||||
} else {
|
||||
ReportWarningf("Failed to save file with name: %S", buffer->name);
|
||||
ReportErrorf("Failed to save file with name: %S", buffer->name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,12 @@ void Appendf(View *view, const char *fmt, ...) {
|
||||
Append(view, string, true);
|
||||
}
|
||||
|
||||
void ReportConsolef(const char *fmt, ...) {
|
||||
Scratch scratch;
|
||||
STRING_FORMAT(scratch, fmt, string);
|
||||
Appendf(LogView, "%S\n", string);
|
||||
}
|
||||
|
||||
void ReportErrorf(const char *fmt, ...) {
|
||||
ErrorCount += 1;
|
||||
Scratch scratch;
|
||||
@@ -77,28 +83,11 @@ void ReportErrorf(const char *fmt, ...) {
|
||||
|
||||
if (LogView) {
|
||||
Appendf(LogView, "%S\n", string);
|
||||
ShowUIMessagef("%S", string);
|
||||
} else {
|
||||
printf("%.*s\n", (int)string.len, string.data);
|
||||
}
|
||||
}
|
||||
|
||||
void ReportConsolef(const char *fmt, ...) {
|
||||
Scratch scratch;
|
||||
STRING_FORMAT(scratch, fmt, string);
|
||||
Appendf(LogView, "%S\n", string);
|
||||
}
|
||||
|
||||
void ReportWarningf(const char *fmt, ...) {
|
||||
ErrorCount += 1;
|
||||
Scratch scratch;
|
||||
STRING_FORMAT(scratch, fmt, string);
|
||||
if (BreakOnError) {
|
||||
BREAK();
|
||||
}
|
||||
Appendf(LogView, "%S\n", string);
|
||||
}
|
||||
|
||||
void CMD_CenterView() {
|
||||
CenterView(PrimaryWindowID);
|
||||
} RegisterCommand(CMD_CenterView, "", "");
|
||||
@@ -216,6 +205,12 @@ void CMD_OpenLogs() {
|
||||
Open(LogBuffer->name);
|
||||
} RegisterCommand(CMD_OpenLogs, "", "Opens the text editor logs and clear error counter");
|
||||
|
||||
void CMD_OpenScratch() {
|
||||
ErrorCount = 0;
|
||||
Buffer *buffer = GetBuffer(NullBufferID);
|
||||
Open(buffer->name);
|
||||
} RegisterCommand(CMD_OpenScratch, "", "Opens the scratch buffer");
|
||||
|
||||
void CMD_Errors() {
|
||||
CMD_OpenLogs();
|
||||
} RegisterCommand(CMD_Errors, "", "Opens the text editor logs and clear error counter");
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
struct Lexer {
|
||||
Allocator allocator;
|
||||
char *at;
|
||||
char *start;
|
||||
char *end;
|
||||
char *name;
|
||||
int line, column;
|
||||
};
|
||||
|
||||
enum TriggerKind {
|
||||
TriggerKind_Error,
|
||||
TriggerKind_Key,
|
||||
@@ -34,35 +25,7 @@ struct CachedTrigger {
|
||||
};
|
||||
Array<CachedTrigger> CachedTriggers;
|
||||
|
||||
void Advance(Lexer *lex) {
|
||||
if (lex->at < lex->end) {
|
||||
if (lex->at[0] == '\n') {
|
||||
lex->line += 1;
|
||||
lex->column = 0;
|
||||
} else {
|
||||
lex->column += 1;
|
||||
}
|
||||
lex->at += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Advance(Lexer *lex, int n) {
|
||||
for (int i = 0; i < n; i += 1) Advance(lex);
|
||||
}
|
||||
|
||||
char At(Lexer *lex) {
|
||||
if (lex->at < lex->end) {
|
||||
return lex->at[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
String AsString(Lexer *lex) {
|
||||
String result = {lex->at, lex->end - lex->at};
|
||||
return result;
|
||||
}
|
||||
|
||||
void EatWhitespace(Lexer *lex) {
|
||||
void EatWhitespaceEx(Lexer *lex) {
|
||||
while (At(lex) != '\n' && IsWhitespace(At(lex))) {
|
||||
Advance(lex);
|
||||
}
|
||||
@@ -80,7 +43,7 @@ Trigger *TriggerBinary(Lexer *lex, Trigger *left, Trigger *right, int key) {
|
||||
Trigger *ParseKeyAtom(Lexer *lex) {
|
||||
Trigger *result = AllocType(lex->allocator, Trigger);
|
||||
result->kind = TriggerKind_Key;
|
||||
EatWhitespace(lex);
|
||||
EatWhitespaceEx(lex);
|
||||
for (;;) {
|
||||
String lex_string = AsString(lex);
|
||||
if (StartsWith(lex_string, "ctrl")) {
|
||||
@@ -118,12 +81,12 @@ Trigger *ParseKeyAtom(Lexer *lex) {
|
||||
|
||||
if (!found) {
|
||||
result->kind = TriggerKind_Error;
|
||||
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected identifier: '%d'", lex->name, lex->line, lex->column, result->key);
|
||||
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected identifier: '%d'", lex->file, lex->line, lex->column, result->key);
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
result->kind = TriggerKind_Error;
|
||||
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected character: '%c'", lex->name, lex->line, lex->column, At(lex));
|
||||
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected character: '%c'", lex->file, lex->line, lex->column, At(lex));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -138,21 +101,21 @@ Trigger *ParseKeyAtom(Lexer *lex) {
|
||||
|
||||
Trigger *ParseKeyChord(Lexer *lex) {
|
||||
Trigger *left = ParseKeyAtom(lex);
|
||||
EatWhitespace(lex);
|
||||
EatWhitespaceEx(lex);
|
||||
while (IsAlphanumeric(At(lex))) {
|
||||
left = TriggerBinary(lex, left, ParseKeyChord(lex), ' ');
|
||||
EatWhitespace(lex);
|
||||
EatWhitespaceEx(lex);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
Trigger *ParseKeyOr(Lexer *lex) {
|
||||
Trigger *left = ParseKeyChord(lex);
|
||||
EatWhitespace(lex);
|
||||
EatWhitespaceEx(lex);
|
||||
while (At(lex) == '|') {
|
||||
Advance(lex);
|
||||
left = TriggerBinary(lex, left, ParseKeyOr(lex), '|');
|
||||
EatWhitespace(lex);
|
||||
EatWhitespaceEx(lex);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ void UpdateCoroutines(Event *event) {
|
||||
_CoroutineContext = ⁢
|
||||
mco_result ok = mco_resume(it.co);
|
||||
if (ok != MCO_SUCCESS) {
|
||||
ReportWarningf("failed to resume coroutine %d", ok);
|
||||
ReportErrorf("failed to resume coroutine %d", ok);
|
||||
DestroyCoroutine(&it);
|
||||
remove_item = true;
|
||||
}
|
||||
|
||||
219
src/text_editor/data_desc.cpp
Normal file
219
src/text_editor/data_desc.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
typedef U32 TokenKind;
|
||||
enum {
|
||||
TokenKind_EOF = 0,
|
||||
TokenKind_Ident = (1<<0),
|
||||
TokenKind_String = (1<<1),
|
||||
TokenKind_Numeric = (1<<2),
|
||||
TokenKind_Comment = (1<<3),
|
||||
TokenKind_Error = (1<<4),
|
||||
|
||||
TokenKind_OpenBrace = (1<<5),
|
||||
TokenKind_CloseBrace = (1<<6),
|
||||
TokenKind_Colon = (1<<7),
|
||||
TokenKind_Comma = (1<<8),
|
||||
TokenKind_Minus = (1<<9),
|
||||
TokenKind_Tag = (1<<10),
|
||||
TokenKind_Or = (1<<11),
|
||||
};
|
||||
|
||||
typedef U32 TokenGroup;
|
||||
enum {
|
||||
TokenGroup_String = (TokenKind_Numeric|TokenKind_String|TokenKind_Ident),
|
||||
TokenGroup_Symbol = (TokenKind_OpenBrace|TokenKind_CloseBrace|TokenKind_Colon|TokenKind_Comma|TokenKind_Minus|TokenKind_Tag|TokenKind_Or),
|
||||
};
|
||||
|
||||
struct Token {
|
||||
TokenKind kind;
|
||||
union {
|
||||
struct {char *data; Int len;};
|
||||
String string;
|
||||
};
|
||||
Float f;
|
||||
Int line, column;
|
||||
char *file;
|
||||
};
|
||||
|
||||
struct Lexer {
|
||||
Allocator allocator;
|
||||
char *at;
|
||||
char *start;
|
||||
char *end;
|
||||
char *file;
|
||||
Int line, column;
|
||||
};
|
||||
|
||||
Lexer MakeLexer(Allocator allocator, String string, char *file, Int line, Int column) {
|
||||
Lexer lexer = {allocator, string.data, string.data, string.data + string.len, file, line, column};
|
||||
return lexer;
|
||||
}
|
||||
|
||||
void Advance(Lexer *lex) {
|
||||
if (lex->at < lex->end) {
|
||||
if (lex->at[0] == '\n') {
|
||||
lex->line += 1;
|
||||
lex->column = 0;
|
||||
}
|
||||
lex->column += 1;
|
||||
lex->at += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Advance(Lexer *lex, int n) {
|
||||
for (int i = 0; i < n; i += 1) Advance(lex);
|
||||
}
|
||||
|
||||
String AsString(Lexer *lex) {
|
||||
String result = {lex->at, lex->end - lex->at};
|
||||
return result;
|
||||
}
|
||||
|
||||
char At(Lexer *lex) {
|
||||
if (lex->at < lex->end) {
|
||||
return lex->at[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EatWhitespace(Lexer *lex) {
|
||||
while (IsWhitespace(At(lex))) {
|
||||
Advance(lex);
|
||||
}
|
||||
}
|
||||
|
||||
void LexString(Lexer *lex, Token *t) {
|
||||
t->kind = TokenKind_String;
|
||||
char end_char = t->data[0];
|
||||
t->data += 1;
|
||||
for (;;) {
|
||||
char c = At(lex);
|
||||
if (c == 0) {
|
||||
break;
|
||||
}
|
||||
if (c == end_char) {
|
||||
break;
|
||||
}
|
||||
if (c == '\\') {
|
||||
Advance(lex);
|
||||
}
|
||||
Advance(lex);
|
||||
}
|
||||
Advance(lex);
|
||||
t->len = (Int)(lex->at - t->data) - 1;
|
||||
}
|
||||
|
||||
void LexDigit(Lexer *lex, Token *t) {
|
||||
t->kind = TokenKind_Numeric;
|
||||
for (;;) {
|
||||
char c = At(lex);
|
||||
if (c == 0) {
|
||||
break;
|
||||
}
|
||||
if (!IsDigit(c)) {
|
||||
break;
|
||||
}
|
||||
Advance(lex);
|
||||
}
|
||||
t->len = (Int)(lex->at - t->data);
|
||||
t->f = (Float)strtoll(t->data, NULL, 10);
|
||||
}
|
||||
|
||||
bool IsOkForIdent(Lexer *lex, char c) {
|
||||
bool result = IsAlphanumeric(c) || c == '_' || c == '/';
|
||||
return result;
|
||||
}
|
||||
|
||||
Token Next(Lexer *lex) {
|
||||
EatWhitespace(lex);
|
||||
Token t = {};
|
||||
t.data = lex->at;
|
||||
t.len = 1;
|
||||
t.line = lex->line;
|
||||
t.column = lex->column;
|
||||
t.file = lex->file;
|
||||
char c = At(lex);
|
||||
Advance(lex);
|
||||
|
||||
if (c == 0) {
|
||||
return t;
|
||||
} else if (c == '{') {
|
||||
t.kind = TokenKind_OpenBrace;
|
||||
} else if (c == '}') {
|
||||
t.kind = TokenKind_CloseBrace;
|
||||
} else if (c == ':') {
|
||||
t.kind = TokenKind_Colon;
|
||||
} else if (c == ',') {
|
||||
t.kind = TokenKind_Comma;
|
||||
} else if (IsDigit(c)) {
|
||||
LexDigit(lex, &t);
|
||||
} else if (c == '"') {
|
||||
LexString(lex, &t);
|
||||
} else if (c == '`') {
|
||||
LexString(lex, &t);
|
||||
} else if (IsOkForIdent(lex, c)) {
|
||||
t.kind = TokenKind_String;
|
||||
for (;;) {
|
||||
char cc = At(lex);
|
||||
bool ok = IsOkForIdent(lex, cc);
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
Advance(lex);
|
||||
}
|
||||
t.len = (Int)(lex->at - t.data);
|
||||
} else {
|
||||
t.kind = TokenKind_Error;
|
||||
t.string = Format(lex->allocator, "Got invalid character when parsing: '%c'", c);
|
||||
return t;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void TestDataDesc() {
|
||||
Scratch scratch;
|
||||
String s = "{/usr/bin/python3, `Hidden`, Input:Clipboard}";
|
||||
Lexer lexer = MakeLexer(scratch, s, "test", 0, 0);
|
||||
{
|
||||
Token tok = {};
|
||||
tok = Next(&lexer);
|
||||
Assert(tok.kind == TokenKind_OpenBrace);
|
||||
Assert(tok.len == 1);
|
||||
Assert(tok.data[0] == '{');
|
||||
|
||||
tok = Next(&lexer);
|
||||
Assert(tok.kind == TokenKind_String);
|
||||
Assert(tok.string == "/usr/bin/python3");
|
||||
|
||||
tok = Next(&lexer);
|
||||
Assert(tok.kind == TokenKind_Comma);
|
||||
Assert(tok.string == ",");
|
||||
|
||||
tok = Next(&lexer);
|
||||
Assert(tok.kind == TokenKind_String);
|
||||
Assert(tok.string == "Hidden");
|
||||
|
||||
tok = Next(&lexer);
|
||||
Assert(tok.kind == TokenKind_Comma);
|
||||
Assert(tok.string == ",");
|
||||
|
||||
tok = Next(&lexer);
|
||||
Assert(tok.kind == TokenKind_String);
|
||||
Assert(tok.string == "Input");
|
||||
|
||||
tok = Next(&lexer);
|
||||
Assert(tok.kind == TokenKind_Colon);
|
||||
Assert(tok.string == ":");
|
||||
|
||||
tok = Next(&lexer);
|
||||
Assert(tok.kind == TokenKind_String);
|
||||
Assert(tok.string == "Clipboard");
|
||||
|
||||
tok = Next(&lexer);
|
||||
Assert(tok.kind == TokenKind_CloseBrace);
|
||||
Assert(tok.string == "}");
|
||||
|
||||
tok = Next(&lexer);
|
||||
Assert(tok.kind == TokenKind_EOF);
|
||||
}
|
||||
|
||||
} RegisterFunction(&TestFunctions, TestDataDesc);
|
||||
@@ -103,6 +103,7 @@ void DrawUnderline(Window *window, View *view, Buffer *buffer, Range range, Colo
|
||||
Vec2I max = {xy_max.col * window->font->char_spacing, (xy_max.line + 1) * window->font->line_spacing};
|
||||
Rect2I rect = {min, max};
|
||||
Rect2I scrolled_rect = rect - view->scroll + window->document_rect.min;
|
||||
if (scrolled_rect.min.x < window->document_rect.min.x) scrolled_rect.min.x = window->document_rect.min.x;
|
||||
DrawRectOutline(scrolled_rect, color);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ bool BreakOnError = false;
|
||||
Int ErrorCount;
|
||||
|
||||
Allocator SysAllocator = {SystemAllocatorProc};
|
||||
String ConfigDir;
|
||||
float DPIScale = 1.0f;
|
||||
|
||||
// @WARNING: be careful about using this, should only be used for debugging
|
||||
@@ -175,10 +174,12 @@ RegisterVariable(String, InternetBrowser, "firefox");
|
||||
RegisterVariable(String, OpenCodePatterns, ".c .h .cpp .hpp .cc .cxx .rs .go .zig .py .lua .js .ts .jsx .tsx .java .kt .swift .cs .rb .php .html .css .scss .bat .sh .bash .zsh .sql .asm .s .cmake .make .json .yaml .toml .ini .txt .md .rst .Makefile .Dockerfile .gitignore .bashrc .zshrc");
|
||||
RegisterVariable(String, OpenCodeExcludePatterns, "");
|
||||
RegisterVariable(Int, TrimTrailingWhitespace, 1);
|
||||
RegisterVariable(String, HomeFolder, "");
|
||||
RegisterVariable(String, ConfigFolder, "");
|
||||
|
||||
// PROJECT_MANAGEMENT
|
||||
// Set at the beginning of the program to current directory
|
||||
RegisterVariable(String, ProjectDirectory, "");
|
||||
RegisterVariable(String, ProjectFolder, "");
|
||||
|
||||
// PLUGIN_BUILD_WINDOW
|
||||
RegisterVariable(String, Build1OnWindows, "build.bat slow");
|
||||
|
||||
@@ -5,7 +5,7 @@ BufferID BuildBufferID;
|
||||
void InitBuildWindow() {
|
||||
Window *window = CreateWind();
|
||||
BuildWindowID = window->id;
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "build"));
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectFolder, "build"));
|
||||
buffer->special = true;
|
||||
buffer->no_history = true;
|
||||
BuildBufferID = buffer->id;
|
||||
@@ -32,16 +32,20 @@ void LayoutBuildWindow(Rect2I *rect, int16_t wx, int16_t wy) {
|
||||
window->document_rect = window->total_rect = CutBottom(rect, barsize);
|
||||
}
|
||||
|
||||
BSet ExecBuild(String windows_cmd, String unix_cmd, String working_dir = ProjectDirectory) {
|
||||
BSet ExecBuild(String windows_cmd, String unix_cmd, String working_dir = ProjectFolder) {
|
||||
SaveAll();
|
||||
Scratch scratch;
|
||||
BSet build = GetBSet(BuildWindowID);
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
SelectRange(build.view, Range{});
|
||||
ResetBuffer(build.buffer);
|
||||
if (OS_WINDOWS) {
|
||||
Exec(build.view->id, true, windows_cmd, working_dir);
|
||||
} else {
|
||||
Exec(build.view->id, true, unix_cmd, working_dir);
|
||||
{
|
||||
ExecArgs args = ShellArgs(scratch, build.view->id, windows_cmd, working_dir);
|
||||
args.scroll_to_end = true;
|
||||
if (!OS_WINDOWS) {
|
||||
args.cmd = unix_cmd;
|
||||
}
|
||||
Exec(args);
|
||||
}
|
||||
main.window->active_goto_list = build.view->id;
|
||||
main.window->goto_list_pos = 0;
|
||||
|
||||
@@ -67,7 +67,7 @@ void LayoutCommandWindow(Rect2I *rect, int16_t wx, int16_t wy) {
|
||||
void InitCommandWindow() {
|
||||
Window *window = CreateWind();
|
||||
CommandWindowID = window->id;
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "command_bar"));
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectFolder, "command_bar"));
|
||||
buffer->special = true;
|
||||
buffer->no_history = true;
|
||||
View *view = CreateView(buffer->id);
|
||||
|
||||
@@ -56,17 +56,6 @@ void CMD_EvalCommandsLineByLine() {
|
||||
EvalCommandsLineByLine(set);
|
||||
} RegisterCommand(CMD_EvalCommandsLineByLine, "", "Goes line by line over a buffer and evaluates every line as a command, ignores empty or lines starting with '//'");
|
||||
|
||||
Variable *GetVariable(String name) {
|
||||
Variable *var = NULL;
|
||||
For (Variables) {
|
||||
if (name == it.name) {
|
||||
var = ⁢
|
||||
break;
|
||||
}
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
BufferID LoadConfig(String config_path) {
|
||||
ReportConsolef("Loading config %S...", config_path);
|
||||
Window *window = GetWindow(NullWindowID);
|
||||
@@ -128,8 +117,8 @@ void Set(String string) {
|
||||
}
|
||||
|
||||
#if PLUGIN_PROJECT_MANAGEMENT
|
||||
if (name == "ProjectDirectory") {
|
||||
SetProjectDirectory(*var->string);
|
||||
if (name == "ProjectFolder") {
|
||||
SetProjectFolder(*var->string);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
@@ -164,4 +153,4 @@ void Set(String string) {
|
||||
ReportErrorf("Failed to :Set, no such variable found: %S", name);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if PLUGIN_CONFIG
|
||||
void Set(String string);
|
||||
String InsertVariables(Allocator allocator, String string);
|
||||
#endif
|
||||
@@ -12,7 +12,7 @@ void InitDebugWindow() {
|
||||
window->primary = false;
|
||||
window->jump_history = false;
|
||||
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "debug"));
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectFolder, "debug"));
|
||||
DebugBufferID = buffer->id;
|
||||
buffer->no_history = true;
|
||||
buffer->special = true;
|
||||
|
||||
@@ -2,10 +2,13 @@ void Windows_SetupVCVarsall(mco_coro *co) {
|
||||
View *view = NULL;
|
||||
{
|
||||
Scratch scratch;
|
||||
String working_dir = ProjectDirectory;
|
||||
String working_dir = ProjectFolder;
|
||||
String buffer_name = GetUniqueBufferName(working_dir, "vcvarsall-");
|
||||
String cmd = Format(scratch, "\"%S\" && set", VCVarsPath);
|
||||
view = ExecHidden(buffer_name, cmd, working_dir);
|
||||
view = OpenBufferView(buffer_name);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
buffer->dont_try_to_save_in_bulk_ops = true;
|
||||
Exec(ShellArgs(scratch, view->id, cmd, working_dir));
|
||||
}
|
||||
for (;;) {
|
||||
if (!ProcessIsActive(view->id)) {
|
||||
@@ -19,6 +22,7 @@ void Windows_SetupVCVarsall(mco_coro *co) {
|
||||
String16 string16 = GetString(buffer);
|
||||
String string = ToString(scratch, string16);
|
||||
Array<String> lines = Split(scratch, string, "\n");
|
||||
ProcessEnviroment.len = 0;
|
||||
For (lines) {
|
||||
String s = Trim(it);
|
||||
Array<String> values = Split(scratch, s, "=");
|
||||
@@ -27,8 +31,6 @@ void Windows_SetupVCVarsall(mco_coro *co) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LoadVCVars() {
|
||||
RemoveCoroutine("Windows_SetupVCVarsall");
|
||||
CCtx *co_data = AddCoroutine(Windows_SetupVCVarsall);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
void SetProjectDirectory(String dir) {
|
||||
ProjectDirectory = Intern(&GlobalInternTable, dir);
|
||||
void SetProjectFolder(String dir) {
|
||||
ProjectFolder = Intern(&GlobalInternTable, dir);
|
||||
Scratch scratch;
|
||||
For (Buffers) {
|
||||
if (it->special) {
|
||||
@@ -14,7 +14,7 @@ void CO_OpenCode(mco_coro *co) {
|
||||
Array<String> patterns = SplitWhitespace(ctx->arena, OpenCodePatterns);
|
||||
Array<String> exclude_patterns = SplitWhitespace(ctx->arena, OpenCodeExcludePatterns);
|
||||
Array<String> dirs = {ctx->arena};
|
||||
Add(&dirs, Copy(ctx->arena, ProjectDirectory));
|
||||
Add(&dirs, Copy(ctx->arena, ProjectFolder));
|
||||
for (int diri = 0; diri < dirs.len; diri += 1) {
|
||||
for (FileIter it = IterateFiles(ctx->arena, dirs[diri]); IsValid(it); Advance(&it)) {
|
||||
bool should_open = true;
|
||||
@@ -52,12 +52,12 @@ void CO_OpenCode(mco_coro *co) {
|
||||
} RegisterCoroutineCommand(
|
||||
CO_OpenCode,
|
||||
"",
|
||||
"Open all code files in current ProjectDirectory, the code files are determined through NonCodePatterns_EndsWith config variable list",
|
||||
"Open all code files in current ProjectFolder, the code files are determined through NonCodePatterns_EndsWith config variable list",
|
||||
data->dont_wait_until_resolved = true;
|
||||
);
|
||||
|
||||
void OpenProject(String directory) {
|
||||
SetProjectDirectory(directory);
|
||||
SetProjectFolder(directory);
|
||||
#if PLUGIN_CONFIG
|
||||
Scratch scratch;
|
||||
for (FileIter it = IterateFiles(scratch, directory); IsValid(it); Advance(&it)) {
|
||||
@@ -111,7 +111,7 @@ void CO_CreateProject(mco_coro *co) {
|
||||
String src_dir = Format(scratch, "%S/src", project_dir);
|
||||
MakeDir(project_dir);
|
||||
MakeDir(src_dir);
|
||||
SetProjectDirectory(project_dir);
|
||||
SetProjectFolder(project_dir);
|
||||
|
||||
Buffer *project = BufferOpenFile(Format(scratch, "%S/project.te", project_dir));
|
||||
RawAppendf(project, ":OpenCode\n:Set BinaryUnderDebug '%S/build/main.exe'\n", project_dir);
|
||||
|
||||
@@ -1 +1 @@
|
||||
void SetProjectDirectory(String name);
|
||||
void SetProjectFolder(String name);
|
||||
|
||||
@@ -2124,7 +2124,7 @@ bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
|
||||
|
||||
if (file.len == 0) {
|
||||
Scratch scratch;
|
||||
for (FileIter it = IterateFiles(scratch, ProjectDirectory); IsValid(it); Advance(&it)) {
|
||||
for (FileIter it = IterateFiles(scratch, ProjectFolder); IsValid(it); Advance(&it)) {
|
||||
if (EndsWith(it.filename, ".rdbg")) {
|
||||
file = Intern(&GlobalInternTable, it.absolute_path);
|
||||
break;
|
||||
@@ -2133,7 +2133,7 @@ bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
|
||||
}
|
||||
|
||||
if (file.len == 0) {
|
||||
ReportWarningf("Couldn't find neither .rdbg file, nor use the BinaryUnderDebug variable to locate the binary");
|
||||
ReportErrorf("Couldn't find neither .rdbg file, nor use the BinaryUnderDebug variable to locate the binary");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2141,7 +2141,9 @@ bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
|
||||
String session_name = Format(ctx->arena, "te%llu", GetTimeNanos());
|
||||
String remedy_string = Format(ctx->arena, "%S --servername %S", RemedyBGPath, session_name);
|
||||
ReportConsolef("Remedybg: %S", remedy_string);
|
||||
Exec(LogView->id, true, remedy_string, GetPrimaryDirectory());
|
||||
ExecArgs args = ShellArgs(ctx->arena, LogView->id, remedy_string, GetPrimaryDirectory());
|
||||
args.scroll_to_end = true;
|
||||
Exec(args);
|
||||
MemoryZero(&RDBG_Ctx, sizeof(RDBG_Ctx));
|
||||
RDBG_Ctx.cmd.data = command_buf;
|
||||
RDBG_Ctx.cmd.capacity = sizeof(command_buf);
|
||||
@@ -2176,7 +2178,7 @@ bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
|
||||
rdbg_Id cfg_id;
|
||||
char *exe = file.data;
|
||||
char *args = NULL;
|
||||
char *work_dir = ProjectDirectory.data;
|
||||
char *work_dir = ProjectFolder.data;
|
||||
char *env = NULL;
|
||||
AddSessionConfig(&RDBG_Ctx, exe, args, work_dir, env, true, true, &res, &cfg_id);
|
||||
if (ContextHadError(&RDBG_Ctx)) {
|
||||
|
||||
@@ -63,7 +63,7 @@ void CMD_ToggleSearchWordBoundary() {
|
||||
void InitSearchWindow() {
|
||||
Window *window = CreateWind();
|
||||
SearchWindowID = window->id;
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "search"));
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectFolder, "search"));
|
||||
buffer->special = true;
|
||||
SearchBufferID = buffer->id;
|
||||
View *view = CreateView(buffer->id);
|
||||
|
||||
@@ -3,7 +3,7 @@ WindowID StatusWindowID;
|
||||
void InitStatusWindow() {
|
||||
Window *window = CreateWind();
|
||||
StatusWindowID = window->id;
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "status_bar"));
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectFolder, "status_bar"));
|
||||
buffer->special = true;
|
||||
View *view = CreateView(buffer->id);
|
||||
view->special = true;
|
||||
@@ -71,10 +71,10 @@ void UpdateStatusWindow() {
|
||||
|
||||
String commands = Format(scratch, ":Errors[%d]", ErrorCount);
|
||||
if (main.buffer->changed_on_disk) {
|
||||
commands = Format(scratch, "%S :Reopen");
|
||||
commands = Format(scratch, "%S :Reopen", commands);
|
||||
}
|
||||
For (ActiveProcesses) {
|
||||
if (it.view_id == main.view->id.id) {
|
||||
if (it.args.output_view == main.view->id) {
|
||||
commands = Format(scratch, "%S :KillProcess", commands);
|
||||
break;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ void UpdateStatusWindow() {
|
||||
|
||||
String s = Format(scratch, "%S:%lld:%lld %S | :Prev :Next :Close %S", main.buffer->name, (long long)xy.line + 1ll, (long long)xy.col + 1ll, indicators, commands);
|
||||
For (ActiveProcesses) {
|
||||
if (it.view_id == main.view->id.id) {
|
||||
if (it.args.output_view == main.view->id) {
|
||||
s = Format(scratch, "%S %lld :KillProcess", s, (long long)it.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,20 @@ void CMD_FocusWindow3() {
|
||||
}
|
||||
} RegisterCommand(CMD_FocusWindow3, "ctrl-3", "Select the 3rd window, counting from left");
|
||||
|
||||
void CMD_FocusWindow4() {
|
||||
Window *first = GetOverlappingWindow({0,0});
|
||||
if (first) {
|
||||
Window *second = GetOverlappingWindow(GetSideOfWindow(first, DIR_RIGHT));
|
||||
if (second) {
|
||||
Window *third = GetOverlappingWindow(GetSideOfWindow(second, DIR_RIGHT));
|
||||
if (third) {
|
||||
Window *fourth = GetOverlappingWindow(GetSideOfWindow(third, DIR_RIGHT));
|
||||
if (fourth) NextActiveWindowID = fourth->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
} RegisterCommand(CMD_FocusWindow4, "ctrl-4", "Select the 4th window, counting from left");
|
||||
|
||||
void CMD_NewWindow() {
|
||||
CreateWind();
|
||||
} RegisterCommand(CMD_NewWindow, "ctrl-backslash", "Creates a new window");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// MAAAAAAAAAAAAN I DONT LIKE THIS CODE, BUT HOPE IT WORKS
|
||||
|
||||
// @todo: potentially bad that we are not checking against end! lexer->at[0] == 0 check is not enough
|
||||
struct Lexer2 {
|
||||
char16_t *at;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,456 @@
|
||||
#if OS_WINDOWS
|
||||
struct Win32Process {
|
||||
HANDLE handle;
|
||||
HANDLE child_stdout_read;
|
||||
HANDLE child_stdout_write;
|
||||
HANDLE child_stdin_read;
|
||||
HANDLE child_stdin_write;
|
||||
};
|
||||
static_assert(sizeof(Win32Process) < sizeof(Process::platform));
|
||||
|
||||
static void Win32ReportError(String msg, String cmd) {
|
||||
LPVOID lpMsgBuf;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||
defer { LocalFree(lpMsgBuf); };
|
||||
char *buff = (char *)lpMsgBuf;
|
||||
size_t buffLen = strlen((const char *)buff);
|
||||
if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0;
|
||||
Error("%S: %S! %s", msg, cmd, (char *)lpMsgBuf);
|
||||
}
|
||||
|
||||
static void Win32CloseProcess(Process *process) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle);
|
||||
if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write);
|
||||
if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read);
|
||||
if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read);
|
||||
if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write);
|
||||
process->is_valid = false;
|
||||
}
|
||||
|
||||
static void Win32ProcessError(Process *process, String msg, String cmd) {
|
||||
Win32ReportError(msg, cmd);
|
||||
Win32CloseProcess(process);
|
||||
}
|
||||
|
||||
void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
Assert(p->child_stdin_write != INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD written = 0;
|
||||
bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0;
|
||||
|
||||
Assert(write_error == false);
|
||||
Assert(written == string.len);
|
||||
}
|
||||
|
||||
void CloseStdin(Process *process) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
CloseHandle(p->child_stdin_write);
|
||||
p->child_stdin_write = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
Process SpawnProcess(ExecArgs args) {
|
||||
Scratch scratch;
|
||||
Process process = {};
|
||||
process.args = args;
|
||||
Win32Process *p = (Win32Process *)process.platform;
|
||||
|
||||
p->handle = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_write = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdin_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdin_write = INVALID_HANDLE_VALUE;
|
||||
|
||||
SECURITY_ATTRIBUTES security_atrb = {};
|
||||
security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
security_atrb.bInheritHandle = TRUE;
|
||||
|
||||
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", args.cmd);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", args.cmd);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (args.open_stdin) {
|
||||
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", args.cmd);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", args.cmd);
|
||||
return process;
|
||||
}
|
||||
}
|
||||
|
||||
STARTUPINFOW startup = {};
|
||||
startup.cb = sizeof(STARTUPINFO);
|
||||
startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||
startup.hStdOutput = p->child_stdout_write;
|
||||
startup.hStdError = p->child_stdout_write;
|
||||
startup.wShowWindow = SW_HIDE;
|
||||
if (args.open_stdin) {
|
||||
startup.hStdInput = p->child_stdin_read;
|
||||
}
|
||||
|
||||
String16 cwd = ToString16(scratch, args.cwd);
|
||||
String16 cmd = ToString16(scratch, args.cmd);
|
||||
String16 exe = ToString16(scratch, args.exe);
|
||||
|
||||
char *env = NULL;
|
||||
if (args.env.len) {
|
||||
Int size = GetSize(args.env) + args.env.len + 1;
|
||||
env = AllocArray(scratch, char, size);
|
||||
Int i = 0;
|
||||
For (args.env) {
|
||||
MemoryCopy(env + i, it.data, it.len);
|
||||
i += it.len;
|
||||
|
||||
env[i++] = 0;
|
||||
}
|
||||
env[i++] = 0;
|
||||
}
|
||||
|
||||
DWORD dwCreationFlags = 0;
|
||||
BOOL bInheritHandles = TRUE;
|
||||
|
||||
PROCESS_INFORMATION info = {};
|
||||
if (!CreateProcessW((wchar_t *)exe.data, (wchar_t *)cmd.data, 0, 0, bInheritHandles, dwCreationFlags, env, (wchar_t *)cwd.data, &startup, &info)) {
|
||||
Win32ProcessError(&process, "failed to create process", args.cmd);
|
||||
return process;
|
||||
}
|
||||
|
||||
// Close handles to the stdin and stdout pipes no longer needed by the child process.
|
||||
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
|
||||
CloseHandle(info.hThread);
|
||||
CloseHandle(p->child_stdout_write);
|
||||
p->child_stdout_write = INVALID_HANDLE_VALUE;
|
||||
|
||||
p->handle = info.hProcess;
|
||||
process.is_valid = true;
|
||||
process.id = (int64_t)p->handle;
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
bool IsValid(Process *process) {
|
||||
if (process->is_valid == false) return false;
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD exit_code;
|
||||
bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0;
|
||||
if (get_exit_code_failed) {
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
process->exit_code = (int)exit_code;
|
||||
Win32CloseProcess(process);
|
||||
return false;
|
||||
}
|
||||
|
||||
void KillProcess(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
bool terminate_process_error = TerminateProcess(p->handle, -1) == 0;
|
||||
if (terminate_process_error) {
|
||||
Assert(0);
|
||||
}
|
||||
Win32CloseProcess(process);
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
DWORD bytes_avail = 0;
|
||||
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
|
||||
if (peek_error) {
|
||||
return {};
|
||||
} else if (bytes_avail == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16));
|
||||
char *buffer = AllocArray(allocator, char, buffer_size);
|
||||
|
||||
DWORD bytes_read = 0;
|
||||
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
|
||||
if (read_error) {
|
||||
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
|
||||
Dealloc(allocator, buffer);
|
||||
Win32CloseProcess(process);
|
||||
return {};
|
||||
}
|
||||
|
||||
String result = {buffer, bytes_read};
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif OS_POSIX
|
||||
|
||||
struct UnixProcess {
|
||||
pid_t pid;
|
||||
int child_stdout_read;
|
||||
int stdin_write;
|
||||
};
|
||||
|
||||
Array<char *> SplitCommand(Allocator allocator, String command_line) {
|
||||
Array<char *> cmd = {allocator};
|
||||
|
||||
String curr = {};
|
||||
for (int i = 0; i < command_line.len; i += 1) {
|
||||
if (command_line.data[i] == ' ') {
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
curr = {};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (curr.len == 0) {
|
||||
curr.data = command_line.data + i;
|
||||
}
|
||||
curr.len += 1;
|
||||
}
|
||||
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
Scratch scratch;
|
||||
const int PIPE_READ = 0;
|
||||
const int PIPE_WRITE = 1;
|
||||
bool error = false;
|
||||
|
||||
working_dir = Copy(scratch, working_dir);
|
||||
chdir(working_dir.data);
|
||||
|
||||
Process process = {};
|
||||
process.args = args;
|
||||
UnixProcess *plat = (UnixProcess *)&process.platform;
|
||||
Array<char *> args = SplitCommand(scratch, command_line);
|
||||
Array<char *> env = {scratch};
|
||||
|
||||
For (enviroment) {
|
||||
Add(&env, Copy(scratch, it).data);
|
||||
}
|
||||
|
||||
int stdout_desc[2] = {};
|
||||
int stdin_desc[2] = {};
|
||||
posix_spawn_file_actions_t actions = {};
|
||||
|
||||
if (posix_spawn_file_actions_init(&actions) != 0) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_init, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
};
|
||||
|
||||
if (pipe(stdout_desc) == -1) {
|
||||
Error("Libc function failed: pipe, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
if (error) {
|
||||
close(stdout_desc[PIPE_READ]);
|
||||
close(stdout_desc[PIPE_WRITE]);
|
||||
} else {
|
||||
close(stdout_desc[PIPE_WRITE]);
|
||||
}
|
||||
};
|
||||
|
||||
if (pipe(stdin_desc) == -1) {
|
||||
Error("Libc function failed: pipe, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
if (error) {
|
||||
close(stdin_desc[PIPE_READ]);
|
||||
close(stdin_desc[PIPE_WRITE]);
|
||||
} else {
|
||||
close(stdin_desc[PIPE_READ]);
|
||||
}
|
||||
};
|
||||
|
||||
error = posix_spawn_file_actions_addclose(&actions, stdout_desc[PIPE_READ]) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDOUT_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDOUT_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDERR_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDERR_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_addclose(&actions, stdin_desc[PIPE_WRITE]) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdin_desc[PIPE_READ], STDIN_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDIN_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
pid_t process_pid = 0;
|
||||
error = posix_spawnp(&process_pid, args[0], &actions, NULL, args.data, env.data) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: failed to create process\n, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
|
||||
plat->child_stdout_read = stdout_desc[PIPE_READ];
|
||||
plat->stdin_write = stdin_desc[PIPE_WRITE];
|
||||
plat->pid = process_pid;
|
||||
|
||||
if (write_stdin.len) {
|
||||
WriteStdin(&process, write_stdin);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
process.id = process_pid;
|
||||
process.is_valid = true;
|
||||
return process;
|
||||
}
|
||||
|
||||
bool IsValid(Process *process) {
|
||||
UnixProcess *plat = (UnixProcess *)&process->platform;
|
||||
if (process->is_valid == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
pollfd p = {};
|
||||
p.fd = plat->child_stdout_read;
|
||||
p.events = POLLRDHUP | POLLERR | POLLHUP | POLLNVAL;
|
||||
int res = poll(&p, 1, 0);
|
||||
if (res > 0) {
|
||||
pid_t result = waitpid(plat->pid, &status, 0);
|
||||
Assert(result != -1);
|
||||
process->exit_code = WEXITSTATUS(status);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KillProcess(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
kill(plat->pid, SIGKILL);
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
Assert(process->is_valid);
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
|
||||
String result = {};
|
||||
result.data = AllocArray(allocator, char, 16 * 4096);
|
||||
|
||||
pollfd p = {};
|
||||
p.fd = plat->child_stdout_read;
|
||||
p.events = POLLIN;
|
||||
int res = poll(&p, 1, 0);
|
||||
if (res > 0 || force_read) {
|
||||
result.len = read(plat->child_stdout_read, result.data, 4 * 4096);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
ssize_t size = write(plat->stdin_write, string.data, string.len);
|
||||
|
||||
Assert(size == string.len);
|
||||
}
|
||||
|
||||
void CloseStdin(Process *process) {
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
close(plat->stdin_write);
|
||||
}
|
||||
|
||||
#else
|
||||
Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) { return {}; }
|
||||
bool IsValid(Process *process) { return false; }
|
||||
void KillProcess(Process *process) { }
|
||||
String PollStdout(Allocator allocator, Process *process, bool force_read) { return {}; }
|
||||
void WriteStdin(Process *process, String string) { return; }
|
||||
void CloseStdin(Process *process) { return; }
|
||||
#endif
|
||||
|
||||
// @todo: perhaps rework the PATH ProcessEnviroment into an object from data_desc
|
||||
String GetEnv(String name) {
|
||||
For (ProcessEnviroment) {
|
||||
if (StartsWith(it, name, true)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
String FindPython(Allocator allocator) {
|
||||
Scratch scratch(allocator);
|
||||
String path = GetEnv("PATH="); // ignore case on windows so it's fine. 'Path=' not needed
|
||||
Assert(path.len);
|
||||
String delim = IF_OS_WINDOWS_ELSE(";", ":");
|
||||
Array<String> paths = Split(scratch, path, delim);
|
||||
String tries[] = {"python3", "python", "py"};
|
||||
for (int i = 0; i < Lengthof(tries); i += 1) {
|
||||
For (paths) {
|
||||
String path_it = Format(scratch, "%S/%S" IF_OS_WINDOWS_ELSE(".exe", ""), it, tries[i]);
|
||||
bool is_bad_bad_ms = EndsWith(it, "AppData\\Local\\Microsoft\\WindowsApps") || EndsWith(it, "AppData/Local/Microsoft/WindowsApps");
|
||||
if (FileExists(path_it) IF_OS_WINDOWS(&& !is_bad_bad_ms)) {
|
||||
return Copy(allocator, path_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void SetShell(Allocator allocator, String *exe, String *cmd, String in_cmd) {
|
||||
#if OS_WINDOWS
|
||||
*exe = "c:\\windows\\system32\\cmd.exe";
|
||||
*cmd = Format(allocator, "%S /C %S", *exe, in_cmd);
|
||||
#else
|
||||
*exe = "/usr/bin/bash";
|
||||
String temp_file = WriteTempFile(in_cmd); // @todo: maybe try to pass by stdio here
|
||||
*cmd = Format(allocator, "%S %S", *exe, temp_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
// WARNING: seems that this maybe can't work reliably?
|
||||
// in case of 'git grep a' it's possible that child process spawns it's own
|
||||
// child process and then it won't print anything because it won't have
|
||||
@@ -8,11 +461,11 @@ void UpdateProcesses() {
|
||||
IterRemove(ActiveProcesses) {
|
||||
IterRemovePrepare(ActiveProcesses);
|
||||
Scratch scratch;
|
||||
View *view = GetView(ViewID{it.view_id});
|
||||
View *view = GetView(it.args.output_view);
|
||||
|
||||
String poll = PollStdout(scratch, &it, false);
|
||||
if (poll.len) {
|
||||
Append(view, poll, it.scroll_to_end);
|
||||
Append(view, poll, it.args.scroll_to_end);
|
||||
}
|
||||
if (!IsValid(&it)) {
|
||||
ReportConsolef("process %lld exit code = %d", it.id, it.exit_code);
|
||||
@@ -21,20 +474,23 @@ void UpdateProcesses() {
|
||||
}
|
||||
}
|
||||
|
||||
void Exec(ViewID view, bool scroll_to_end, String cmd, String working_dir) {
|
||||
Process process = SpawnProcess(cmd, working_dir, {}, ProcessEnviroment);
|
||||
ReportConsolef("process %lld start. is_valid = %d cmd = %S working_dir = %S", process.id, process.is_valid, cmd, working_dir);
|
||||
process.view_id = view.id;
|
||||
process.scroll_to_end = scroll_to_end;
|
||||
if (process.is_valid) {
|
||||
Add(&ActiveProcesses, process);
|
||||
}
|
||||
ExecArgs ShellArgs(Allocator allocator, ViewID output_view, String cmd, String working_directory) {
|
||||
ExecArgs args = {};
|
||||
SetShell(allocator, &args.exe, &args.cmd, cmd);
|
||||
args.env = ProcessEnviroment;
|
||||
args.cwd = working_directory;
|
||||
args.output_view = output_view;
|
||||
args.poll_process = 1;
|
||||
return args;
|
||||
}
|
||||
|
||||
void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) {
|
||||
Scratch scratch;
|
||||
String cmd = ToString(scratch, cmd16);
|
||||
Exec(view, scroll_to_end, cmd, working_dir);
|
||||
Process Exec(ExecArgs args) {
|
||||
Process process = SpawnProcess(args);
|
||||
ReportConsolef("process %lld start. is_valid = %d cmd = %S working_dir = %S", process.id, process.is_valid, args.cmd, args.cwd);
|
||||
if (process.is_valid && args.poll_process) {
|
||||
Add(&ActiveProcesses, process);
|
||||
}
|
||||
return process;
|
||||
}
|
||||
|
||||
struct ExecResult {
|
||||
@@ -44,9 +500,14 @@ struct ExecResult {
|
||||
|
||||
ExecResult ExecAndWait(Allocator allocator, String cmd, String working_dir, String stdin_string = {}) {
|
||||
ReportConsolef("ExecAndWait cmd = %S working_dir = %S stdin_string = %S", cmd, working_dir, stdin_string);
|
||||
|
||||
Buffer *scratch_buff = CreateScratchBuffer(allocator, 4096 * 4);
|
||||
Process process = SpawnProcess(cmd, working_dir, stdin_string, ProcessEnviroment);
|
||||
|
||||
ExecArgs args = ShellArgs(allocator, {}, cmd, working_dir);
|
||||
args.poll_process = 0;
|
||||
args.open_stdin = 1;
|
||||
Process process = Exec(args);
|
||||
WriteStdin(&process, stdin_string);
|
||||
CloseStdin(&process);
|
||||
for (;IsValid(&process);) {
|
||||
Scratch scratch(allocator);
|
||||
String poll = PollStdout(scratch, &process, true);
|
||||
@@ -60,12 +521,12 @@ void KillProcess(View *view) {
|
||||
IterRemove(ActiveProcesses) {
|
||||
IterRemovePrepare(ActiveProcesses);
|
||||
|
||||
ViewID view_id = {it.view_id};
|
||||
ViewID view_id = {it.args.output_view};
|
||||
if (view_id == view->id) {
|
||||
KillProcess(&it);
|
||||
remove_item = true;
|
||||
String string = "process was killed by user\n";
|
||||
Append(view, string, it.scroll_to_end);
|
||||
Append(view, string, it.args.scroll_to_end);
|
||||
// dont break because that will fuck with removal ...
|
||||
}
|
||||
}
|
||||
@@ -73,27 +534,9 @@ void KillProcess(View *view) {
|
||||
|
||||
bool ProcessIsActive(ViewID view) {
|
||||
For (ActiveProcesses) {
|
||||
if (it.view_id == view.id) {
|
||||
if (it.args.output_view == view) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
View *ExecHidden(String buffer_name, String cmd, String working_dir) {
|
||||
View *view = OpenBufferView(buffer_name);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
buffer->dont_try_to_save_in_bulk_ops = true;
|
||||
Exec(view->id, true, cmd, working_dir);
|
||||
return view;
|
||||
}
|
||||
|
||||
BSet Exec(String cmd, String working_dir, bool set_active = true) {
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
if (set_active) {
|
||||
NextActiveWindowID = main.window->id;
|
||||
}
|
||||
JumpTempBuffer(&main);
|
||||
Exec(main.view->id, true, cmd, working_dir);
|
||||
return main;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "text_editor.h"
|
||||
#include "globals.cpp"
|
||||
#include "coroutines.cpp"
|
||||
#include "data_desc.cpp"
|
||||
#include "buffer.cpp"
|
||||
#include "view.cpp"
|
||||
#include "window.cpp"
|
||||
@@ -853,13 +854,44 @@ extern char **environ;
|
||||
int main(int argc, char **argv)
|
||||
#endif
|
||||
{
|
||||
InitScratch();
|
||||
InitOS(ReportErrorf);
|
||||
#if OS_WINDOWS
|
||||
int argc = __argc;
|
||||
char **argv = __argv;
|
||||
AttachConsole(ATTACH_PARENT_PROCESS);
|
||||
#endif
|
||||
InitScratch();
|
||||
InitOS(ReportErrorf);
|
||||
|
||||
ProjectFolder = GetWorkingDir(Perm);
|
||||
HomeFolder = SDL_GetUserFolder(SDL_FOLDER_HOME);
|
||||
{
|
||||
String sdl_config_path = SDL_GetPrefPath("krzosa", "text_editor");
|
||||
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '\\') {
|
||||
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
|
||||
}
|
||||
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '/') {
|
||||
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
|
||||
}
|
||||
ConfigFolder = NormalizePath(Perm, sdl_config_path);
|
||||
SDL_free(sdl_config_path.data);
|
||||
}
|
||||
|
||||
#if OS_WINDOWS
|
||||
{
|
||||
wchar_t *p = GetEnvironmentStringsW();
|
||||
for (;p && p[0];) {
|
||||
String16 string16((char16_t *)p);
|
||||
String string = ToString(Perm, string16);
|
||||
Add(&ProcessEnviroment, string);
|
||||
p += string16.len + 1;
|
||||
}
|
||||
// FreeEnvironmentStringsW(p); // I get a trap here? why?
|
||||
}
|
||||
#else
|
||||
for (int i = 0; environ[i]; i += 1) {
|
||||
Add(&ProcessEnviroment, Copy(Perm, environ[i]));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (1) {
|
||||
RunArenaTest();
|
||||
@@ -871,25 +903,6 @@ int main(int argc, char **argv)
|
||||
// return 0;
|
||||
}
|
||||
|
||||
#if !OS_WINDOWS
|
||||
for (int i = 0; environ[i]; i += 1) {
|
||||
Add(&ProcessEnviroment, Copy(Perm, environ[i]));
|
||||
}
|
||||
#endif
|
||||
|
||||
ProjectDirectory = GetWorkingDir(Perm);
|
||||
{
|
||||
String sdl_config_path = SDL_GetPrefPath("krzosa", "text_editor");
|
||||
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '\\') {
|
||||
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
|
||||
}
|
||||
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '/') {
|
||||
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
|
||||
}
|
||||
ConfigDir = NormalizePath(Perm, sdl_config_path);
|
||||
SDL_free(sdl_config_path.data);
|
||||
}
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
ReportErrorf("Couldn't initialize SDL! %s", SDL_GetError());
|
||||
return 1;
|
||||
@@ -967,13 +980,13 @@ int main(int argc, char **argv)
|
||||
{
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
Scratch scratch;
|
||||
Buffer *null_buffer = CreateBuffer(sys_allocator, Format(scratch, "%S/scratch", ProjectDirectory));
|
||||
Buffer *null_buffer = CreateBuffer(sys_allocator, Format(scratch, "%S/scratch", ProjectFolder));
|
||||
null_buffer->special = true;
|
||||
View *null_view = CreateView(null_buffer->id);
|
||||
null_view->special = true;
|
||||
Assert(null_buffer->id == NullBufferID && null_view->id == NullViewID);
|
||||
|
||||
Buffer *logs_buffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectDirectory, "logs", ""));
|
||||
Buffer *logs_buffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectFolder, "logs", ""));
|
||||
logs_buffer->special = true;
|
||||
View *logs_view = CreateView(logs_buffer->id);
|
||||
logs_view->special = true;
|
||||
@@ -981,13 +994,13 @@ int main(int argc, char **argv)
|
||||
LogView = logs_view;
|
||||
|
||||
#if PLUGIN_RECORD_GC
|
||||
GCInfoBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectDirectory, "gc"));
|
||||
GCInfoBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectFolder, "gc"));
|
||||
GCInfoBuffer->special = true;
|
||||
GCInfoBuffer->no_history = true;
|
||||
#endif
|
||||
|
||||
#if PLUGIN_RECORD_EVENTS
|
||||
EventBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectDirectory, "events"));
|
||||
EventBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectFolder, "events"));
|
||||
EventBuffer->no_history = true;
|
||||
EventBuffer->special = true;
|
||||
#endif
|
||||
@@ -997,7 +1010,7 @@ int main(int argc, char **argv)
|
||||
ReloadFont(PathToFont, (U32)FontSize);
|
||||
CreateWind();
|
||||
ReopenBuffer(GetBuffer(NullBufferID));
|
||||
InitOS(ReportWarningf);
|
||||
InitOS(ReportErrorf);
|
||||
|
||||
For (GlobalCommands) {
|
||||
if (it.binding.len != 0) {
|
||||
@@ -1016,7 +1029,7 @@ int main(int argc, char **argv)
|
||||
#if PLUGIN_CONFIG
|
||||
{
|
||||
Scratch scratch;
|
||||
GlobalConfigBufferID = LoadConfig(Format(scratch, "%S/config.te", ConfigDir));
|
||||
GlobalConfigBufferID = LoadConfig(Format(scratch, "%S/config.te", ConfigFolder));
|
||||
}
|
||||
#endif
|
||||
for (int i = 1; i < argc; i += 1) {
|
||||
|
||||
@@ -45,11 +45,33 @@ enum UIAction {
|
||||
UIAction_No,
|
||||
};
|
||||
|
||||
struct ExecArgs {
|
||||
String exe;
|
||||
String cmd;
|
||||
String cwd;
|
||||
Array<String> env;
|
||||
|
||||
ViewID output_view; // poll_process
|
||||
struct {
|
||||
U32 poll_process : 1;
|
||||
U32 open_stdin : 1;
|
||||
U32 scroll_to_end : 1;
|
||||
};
|
||||
};
|
||||
|
||||
struct Process {
|
||||
bool is_valid;
|
||||
int exit_code;
|
||||
char platform[6 * 8];
|
||||
Int id;
|
||||
|
||||
ExecArgs args; // memory for exe and all that is invalid
|
||||
};
|
||||
|
||||
enum OpenKind {
|
||||
OpenKind_Invalid,
|
||||
OpenKind_Skip,
|
||||
OpenKind_Exec,
|
||||
OpenKind_BackgroundExec,
|
||||
OpenKind_Goto,
|
||||
OpenKind_Command,
|
||||
#if PLUGIN_CONFIG
|
||||
@@ -68,7 +90,12 @@ struct ResolvedOpen {
|
||||
OpenKind kind;
|
||||
String path;
|
||||
Int line, col;
|
||||
bool existing_buffer;
|
||||
|
||||
struct {
|
||||
U32 existing_buffer : 1;
|
||||
U32 exec_in_background : 1;
|
||||
U32 use_python_shell : 1;
|
||||
};
|
||||
};
|
||||
|
||||
struct Buffer {
|
||||
|
||||
@@ -217,13 +217,133 @@ bool IsOpenBoundary(char c) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Variable *GetVariable(String name) {
|
||||
Variable *var = NULL;
|
||||
For (Variables) {
|
||||
if (name == it.name) {
|
||||
var = ⁢
|
||||
break;
|
||||
}
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
String InsertVariables(Allocator allocator, String string) {
|
||||
Scratch scratch(allocator);
|
||||
Array<String> parts = {scratch};
|
||||
String it = string;
|
||||
for (;;) {
|
||||
int64_t idx = 0;
|
||||
bool found = Seek(it, "@", &idx, SeekFlag_None);
|
||||
if (!found) {
|
||||
if (it.len > 0) {
|
||||
Add(&parts, it);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
String prev = GetPrefix(it, idx);
|
||||
if (prev.len > 0) {
|
||||
Add(&parts, prev);
|
||||
}
|
||||
|
||||
it = Skip(it, idx + 1);
|
||||
char c = At(it, 0);
|
||||
String name = {};
|
||||
if (c == '@') {
|
||||
Add(&parts, String{"@", 1});
|
||||
it = Skip(it, 1);
|
||||
continue;
|
||||
} else if (c == '(') {
|
||||
char *start = it.data + 1;
|
||||
while (At(it, 0) && At(it, 0) != ')') {
|
||||
it = Skip(it, 1);
|
||||
}
|
||||
Int len = it.data - start;
|
||||
name = {start, len};
|
||||
it = Skip(it, 1); // skip ')'
|
||||
} else {
|
||||
char *start = it.data;
|
||||
while (IsAlphanumeric(At(it, 0))) {
|
||||
it = Skip(it, 1);
|
||||
}
|
||||
Int len = it.data - start;
|
||||
name = {start, len};
|
||||
}
|
||||
Variable *variable = GetVariable(name);
|
||||
if (!variable) {
|
||||
ReportErrorf("Variable: %S, not found", name);
|
||||
return string;
|
||||
}
|
||||
if (variable->type != VariableType_String) {
|
||||
// @todo: this will not report - open will override
|
||||
ReportErrorf("Variable: %S, not of type String", variable->type);
|
||||
return string;
|
||||
}
|
||||
|
||||
Add(&parts, *variable->string);
|
||||
}
|
||||
|
||||
String result = Merge(allocator, parts, "");
|
||||
return result;
|
||||
}
|
||||
|
||||
void TestInsertVariable() {
|
||||
Scratch scratch;
|
||||
|
||||
String a = "Thing/@(ProjectFolder)/Another";
|
||||
String b = "Thing/@ProjectFolder/Another";
|
||||
Assert(InsertVariables(scratch, a) == InsertVariables(scratch, b));
|
||||
int c = 10;
|
||||
|
||||
} RegisterFunction(&TestFunctions, TestInsertVariable);
|
||||
|
||||
/*
|
||||
|
||||
Variables:
|
||||
@ProjectFolder/build/te
|
||||
@(ProjectFolder)/build/te
|
||||
|
||||
Start of string matchers:
|
||||
! Execute with default shell
|
||||
!! Execute hidden with default shell
|
||||
:Command Execute editor command
|
||||
:Set Special case for config stuff
|
||||
http://
|
||||
https://
|
||||
commit
|
||||
py: @todo, execute in python shell, this will use the python3 shell and pass the rest to it's stdin or maybe as a file
|
||||
|
||||
More comprehensive syntax for commands:
|
||||
!{/usr/bin/python,Hidden,Input:Clipboard,Output:Clipboard,WorkingDirectory:@ProjectFolder,Env:{Thing:asd}}
|
||||
|
||||
Otherwise it does filepath parsing:
|
||||
C:/windows/path
|
||||
/unix/path
|
||||
./it/also/does/linenums:32:3
|
||||
/as/well/as/this/format(111,1)
|
||||
./thing(10)
|
||||
|
||||
|
||||
USECASE: Wouldn't it be cool to just select a part of codebase pipe that into a script
|
||||
and get a result in a clipboard or capture the output and change the selection?
|
||||
|
||||
PREV IDEA:
|
||||
!{bash,Out:Sel} SCRIPT
|
||||
!{bash,Out:Clip} SCRIPT
|
||||
Use variables for injecting selection: @Sel
|
||||
|
||||
TODO: I would pause the data desc language, seems a bit cumbersome... think of more concrete, constrained ideas
|
||||
TODO: Unify lexers (Set and Trigger)
|
||||
|
||||
*/
|
||||
ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpenMeta meta) {
|
||||
ResolvedOpen result = {};
|
||||
path = Trim(path);
|
||||
bool exec = !(ResolveOpenMeta_DontExec & meta);
|
||||
|
||||
#if PLUGIN_CONFIG
|
||||
// @todo: variable substitution {{ProjectDirectory}}/build/te.exe
|
||||
path = InsertVariables(alo, path);
|
||||
|
||||
if (exec && result.kind == OpenKind_Invalid && StartsWith(path, ":Set ")) {
|
||||
result.kind = OpenKind_Set;
|
||||
@@ -246,7 +366,8 @@ ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpen
|
||||
|
||||
// !!exec_hidden
|
||||
if (exec && result.kind == OpenKind_Invalid && StartsWith(path, "!!")) {
|
||||
result.kind = OpenKind_BackgroundExec;
|
||||
result.kind = OpenKind_Exec;
|
||||
result.exec_in_background = 1;
|
||||
result.path = Skip(path, 2);
|
||||
}
|
||||
|
||||
@@ -254,22 +375,20 @@ ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpen
|
||||
if (exec && result.kind == OpenKind_Invalid && StartsWith(path, "!")) {
|
||||
result.kind = OpenKind_Exec;
|
||||
result.path = Skip(path, 1);
|
||||
}
|
||||
|
||||
Int idx = 0;
|
||||
String needle = "{{TEMP}}";
|
||||
if (Seek(result.path, needle, &idx, SeekFlag_None)) {
|
||||
String rest = Skip(result.path, idx + needle.len);
|
||||
String begin = GetPrefix(result.path, idx);
|
||||
String temp_filename = WriteTempFile(rest);
|
||||
result.path = Format(alo, "%S%S", begin, temp_filename);
|
||||
}
|
||||
if (exec && result.kind == OpenKind_Invalid && StartsWith(path, "py:")) {
|
||||
result.kind = OpenKind_Exec;
|
||||
result.path = Skip(path, 3);
|
||||
result.use_python_shell = 1;
|
||||
}
|
||||
|
||||
// https://web
|
||||
bool web = StartsWith(path, "https://") || StartsWith(path, "http://");
|
||||
if (exec && result.kind == OpenKind_Invalid && web) {
|
||||
result.path = Format(alo, "%S %S", InternetBrowser, path);
|
||||
result.kind = OpenKind_BackgroundExec;
|
||||
result.kind = OpenKind_Exec;
|
||||
result.exec_in_background = 1;
|
||||
}
|
||||
|
||||
// commit 3kc09as92
|
||||
@@ -317,7 +436,7 @@ ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpen
|
||||
Int start = i;
|
||||
String b = {path.data + 1 + start, (end - start)};
|
||||
|
||||
if (At(path, i) == ')') {
|
||||
if (At(path, i) == '(') {
|
||||
i -= 1;
|
||||
path.len = i + 1;
|
||||
result.line = strtoll(b.data, NULL, 10);
|
||||
@@ -367,7 +486,9 @@ ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpen
|
||||
String rel_path = Format(alo, "%S/%S", GetDirectory(window), path);
|
||||
existing_buffer = GetBuffer(rel_path, NULL);
|
||||
if (existing_buffer || FileExists(rel_path)) {
|
||||
result.existing_buffer = existing_buffer;
|
||||
if (existing_buffer) {
|
||||
result.existing_buffer = 1;
|
||||
}
|
||||
result.path = rel_path;
|
||||
result.kind = OpenKind_Goto;
|
||||
}
|
||||
@@ -409,15 +530,28 @@ BSet Open(Window *window, String path, ResolveOpenMeta meta, bool set_active = t
|
||||
}
|
||||
CenterView(window->id);
|
||||
} else if (o.kind == OpenKind_Exec) {
|
||||
if (set_active) {
|
||||
NextActiveWindowID = set.window->id;
|
||||
ExecArgs args = {};// ShellArgs(scratch, LogView->id, o.path, GetPrimaryDirectory());
|
||||
args.poll_process = 1;
|
||||
args.output_view = LogView->id;
|
||||
args.cwd = GetPrimaryDirectory();
|
||||
if (o.exec_in_background == 0) {
|
||||
if (set_active) {
|
||||
NextActiveWindowID = set.window->id;
|
||||
}
|
||||
JumpTempBuffer(&set);
|
||||
RawAppend(set.buffer, u"\n");
|
||||
args.output_view = set.view->id;
|
||||
}
|
||||
JumpTempBuffer(&set);
|
||||
RawAppend(set.buffer, u"\n");
|
||||
Exec(set.view->id, false, o.path, GetPrimaryDirectory());
|
||||
} else if (o.kind == OpenKind_BackgroundExec) {
|
||||
// this shouldn't change the focus/window/view
|
||||
Exec(LogView->id, false, o.path, GetPrimaryDirectory());
|
||||
|
||||
if (o.use_python_shell == 1) {
|
||||
args.exe = FindPython(scratch);
|
||||
String temp_file = WriteTempFile(o.path);
|
||||
args.cmd = Format(scratch, "%S %S", args.exe, temp_file);
|
||||
} else {
|
||||
SetShell(scratch, &args.exe, &args.cmd, o.path);
|
||||
}
|
||||
|
||||
Exec(args);
|
||||
} else if (o.kind == OpenKind_Command) {
|
||||
EvalCommand(o.path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user