Compare commits

...

10 Commits

Author SHA1 Message Date
Krzosa Karol
3ab6639afb Fix underline drawing over line numbers 2026-02-04 08:26:32 +01:00
Krzosa Karol
7a73a982d2 Pause on lexing stuff 2026-02-03 23:23:49 +01:00
Krzosa Karol
51f1fd1b4f ProjectDirectory to ProjectFolder 2026-02-03 21:11:02 +01:00
Krzosa Karol
930620a49e Init variables, ReportErrorf now doesn't pop a message cause it doesn't do the queuing 2026-02-03 21:10:33 +01:00
Krzosa Karol
8cb1b49cd8 Fix ctrl-4 2026-02-03 20:06:56 +01:00
Krzosa Karol
830be12b24 Major redesign of the Exec API, in order to make linux more uniform with windows and introduce python shell 2026-02-02 22:21:19 +01:00
Krzosa Karol
ed3be39037 Begin data_desc 2026-02-01 17:45:15 +01:00
Krzosa Karol
33d623268a Fix outside of buffer access and add docs 2026-02-01 12:28:34 +01:00
Krzosa Karol
5198c11fc6 CMD_OpenScratch 2026-02-01 11:21:21 +01:00
Krzosa Karol
bc7c52e1ec Fix formatting crash 2026-02-01 11:16:14 +01:00
29 changed files with 1017 additions and 664 deletions

View File

@@ -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 "@>"

View File

@@ -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

View File

@@ -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() {

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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");

View File

@@ -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;
}

View File

@@ -61,7 +61,7 @@ void UpdateCoroutines(Event *event) {
_CoroutineContext = &it;
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;
}

View 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);

View File

@@ -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);
}

View File

@@ -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");

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 = &it;
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

View File

@@ -1,3 +1,4 @@
#if PLUGIN_CONFIG
void Set(String string);
String InsertVariables(Allocator allocator, String string);
#endif

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -1 +1 @@
void SetProjectDirectory(String name);
void SetProjectFolder(String name);

View File

@@ -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)) {

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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");

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -217,13 +217,133 @@ bool IsOpenBoundary(char c) {
return result;
}
Variable *GetVariable(String name) {
Variable *var = NULL;
For (Variables) {
if (name == it.name) {
var = &it;
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);
}