summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lsp.c341
-rw-r--r--main.c8
-rw-r--r--process-posix.c128
-rw-r--r--process.h16
4 files changed, 455 insertions, 38 deletions
diff --git a/lsp.c b/lsp.c
new file mode 100644
index 0000000..5847642
--- /dev/null
+++ b/lsp.c
@@ -0,0 +1,341 @@
+typedef struct {
+ Process process;
+} LSP;
+
+typedef struct {
+ u32 pos;
+ u32 len;
+} JSONString;
+
+typedef struct JSONValue JSONValue;
+
+typedef struct {
+ u32 len;
+ // this is an index into the values array
+ // values[items..items+len] store the names
+ // values[items+len..items+2*len] store the values
+ u32 items;
+} JSONObject;
+
+typedef struct {
+ u32 len;
+ // this is an index into the values array
+ // values[elements..elements+len] are the elements
+ u32 elements;
+} JSONArray;
+
+enum {
+ JSON_NULL,
+ JSON_FALSE,
+ JSON_TRUE,
+ JSON_NUMBER,
+ JSON_STRING,
+ JSON_OBJECT,
+ JSON_ARRAY
+};
+
+struct JSONValue {
+ u8 type;
+ union {
+ double number;
+ JSONString string;
+ JSONArray array;
+ JSONObject object;
+ } val;
+};
+
+
+typedef struct {
+ char error[64];
+ const char *text;
+ // root = values[0]
+ JSONValue *values;
+} JSON;
+
+static bool json_parse_value(JSON *json, u32 *p_index, JSONValue *val);
+
+// count number of comma-separated values until
+// closing ] or }
+static u32 json_count(JSON *json, u32 index) {
+ int bracket_depth = 0;
+ int brace_depth = 0;
+ u32 count = 1;
+ const char *text = json->text;
+ while (isspace(text[index])) ++index;
+ // special case: empty object/array
+ if (text[index] == '}' || text[index] == ']')
+ return 0;
+
+ int mark[5000] = {0};
+ for (; ; ++index) {
+ switch (text[index]) {
+ case '\0':
+ return 0; // bad no closing bracket
+ case '[':
+ ++bracket_depth;
+ break;
+ case ']':
+ --bracket_depth;
+ if (bracket_depth < 0)
+ return count;
+ break;
+ case '{':
+ ++brace_depth;
+ mark[index] = true;
+ break;
+ case '}':
+ --brace_depth;
+ mark[index] = true;
+ if (brace_depth < 0){
+
+ for (int i = 0; text[i]; ++i ){
+ switch (mark[i]){
+ case 1: printf("\x1b[91m"); break;
+ case 2: printf("\x1b[92m"); break;
+ }
+ printf("%c",text[i]);
+ if (mark[i]) printf("\x1b[0m");
+ }
+ printf("\n");
+
+ return count;
+ }
+ break;
+ case ',':
+ if (bracket_depth == 0 && brace_depth == 0)
+ ++count;
+ break;
+ case '"': {
+ ++index; // skip opening "
+ int escaped = 0;
+ for (; ; ++index) {
+ mark[index] = 2;
+ switch (text[index]) {
+ case '\0': return 0; // bad no closing quote
+ case '\\': escaped = !escaped; break;
+ case '"':
+ if (!escaped)
+ goto done;
+ escaped = false;
+ break;
+ default:
+ escaped = false;
+ break;
+ }
+ }
+ done:;
+ } break;
+ }
+ }
+}
+
+static bool json_parse_object(JSON *json, u32 *p_index, JSONObject *object) {
+ u32 index = *p_index;
+ const char *text = json->text;
+ ++index; // go past {
+ u32 count = json_count(json, index);
+ printf("%u\n",count);
+ exit(0);
+ object->len = count;
+ object->items = arr_len(json->values);
+ arr_set_len(json->values, arr_len(json->values) + 2 * count);
+
+
+ for (u32 i = 0; i < count; ++i) {
+ if (i > 0) {
+ if (text[index] != ',') {
+ strbuf_printf(json->error, "stuff after value in object");
+ return false;
+ }
+ ++index;
+ }
+ JSONValue name = {0}, value = {0};
+ if (!json_parse_value(json, &index, &name))
+ return false;
+ while (isspace(text[index])) ++index;
+ if (text[index] != ':') {
+ strbuf_printf(json->error, "stuff after name in object");
+ return false;
+ }
+ while (isspace(text[index])) ++index;
+ if (!json_parse_value(json, &index, &value))
+ return false;
+ while (isspace(text[index])) ++index;
+ json->values[object->items + i] = name;
+ json->values[object->items + count + i] = value;
+ }
+
+ if (text[index] != '}') {
+ strbuf_printf(json->error, "mismatched brackets or quotes.");
+ return false;
+ }
+ ++index; // skip }
+ *p_index = index;
+ return true;
+}
+
+static bool json_parse_array(JSON *json, u32 *p_index, JSONArray *array) {
+ (void)json;(void)p_index;(void)array;
+ abort();
+}
+
+static bool json_parse_string(JSON *json, u32 *p_index, JSONString *string) {
+ (void)json;(void)p_index;(void)string;
+ abort();
+}
+
+static bool json_parse_number(JSON *json, u32 *p_index, double *number) {
+ (void)json;(void)p_index;(void)number;
+ abort();
+}
+
+static bool json_parse_value(JSON *json, u32 *p_index, JSONValue *val) {
+ const char *text = json->text;
+ u32 index = *p_index;
+ while (isspace(text[index])) ++index;
+ switch (text[index]) {
+ case '{':
+ val->type = JSON_OBJECT;
+ if (!json_parse_object(json, &index, &val->val.object))
+ return false;
+ break;
+ case '[':
+ val->type = JSON_ARRAY;
+ if (!json_parse_array(json, &index, &val->val.array))
+ return false;
+ break;
+ case '"':
+ val->type = JSON_STRING;
+ if (!json_parse_string(json, &index, &val->val.string))
+ return false;
+ break;
+ case ANY_DIGIT:
+ val->type = JSON_NUMBER;
+ if (!json_parse_number(json, &index, &val->val.number))
+ return false;
+ break;
+ case 'f':
+ val->type = JSON_FALSE;
+ if (!str_has_prefix(&text[index], "false"))
+ return false;
+ index += 5;
+ break;
+ case 't':
+ val->type = JSON_TRUE;
+ if (!str_has_prefix(&text[index], "true"))
+ return false;
+ index += 4;
+ break;
+ case 'n':
+ val->type = JSON_NULL;
+ if (!str_has_prefix(&text[index], "null"))
+ return false;
+ index += 4;
+ break;
+ default:
+ strbuf_printf(json->error, "bad value");
+ }
+ *p_index = index;
+ return true;
+}
+
+// NOTE: text must live as long as json!!!
+static bool json_parse(JSON *json, const char *text) {
+ memset(json, 0, sizeof *json);
+ json->text = text;
+ // @TODO: is this a good capacity?
+ arr_reserve(json->values, strlen(text) / 8);
+ arr_addp(json->values); // add root
+ JSONValue val = {0};
+ u32 index = 0;
+ if (!json_parse_value(json, &index, &val))
+ return false;
+
+ while (isspace(text[index])) ++index;
+ if (text[index]) {
+ strbuf_printf(json->error, "extra text after end of root object");
+ return false;
+ }
+ json->values[0] = val;
+ return true;
+}
+
+
+static void json_debug_print_value(const JSON *json, u32 idx) {
+ JSONValue *value = &json->values[idx];
+ switch (value->type) {
+ case JSON_NULL: printf("null"); break;
+ case JSON_FALSE: printf("false"); break;
+ case JSON_TRUE: printf("true"); break;
+ case JSON_NUMBER: printf("%f", value->val.number); break;
+ case JSON_STRING: {
+ JSONString *string = &value->val.string;
+ printf("\"%.*s\"",
+ (int)string->len,
+ json->text + string->pos);
+ } break;
+ case JSON_ARRAY: {
+ JSONArray *array = &value->val.array;
+ printf("[");
+ for (u32 i = 0; i < array->len; ++i) {
+ json_debug_print_value(json, array->elements + i);
+ printf(",");
+ }
+ printf("]");
+ } break;
+ case JSON_OBJECT: {
+ JSONObject *obj = &value->val.object;
+ printf("{");
+ for (u32 i = 0; i < obj->len; ++i) {
+ json_debug_print_value(json, obj->items + i);
+ printf(": ");
+ json_debug_print_value(json, obj->items + obj->len + i);
+ printf(",");
+ }
+ printf("}");
+ } break;
+ }
+}
+
+static void json_debug_print(const JSON *json) {
+ json_debug_print_value(json, 0);
+}
+
+static void send_request(LSP *lsp, const char *content) {
+ char header[128];
+ size_t content_size = strlen(content);
+ strbuf_printf(header, "Content-Length: %zu\r\n\r\n", content_size);
+ process_write(&lsp->process, header, strlen(header));
+ process_write(&lsp->process, content, content_size);
+}
+
+void lsp_create(LSP *lsp, const char *analyzer_command) {
+ ProcessSettings settings = {
+ .stdin_blocking = true,
+ .stdout_blocking = true
+ };
+ process_run_ex(&lsp->process, analyzer_command, &settings);
+ char init_request[1024];
+ strbuf_printf(init_request,
+ "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{"
+ "\"processId\":%d,"
+ "\"capabilities\":{}"
+ "}}",
+ process_get_id());
+ send_request(lsp, init_request);
+ // @TODO: recv_response
+ char response_text[4096] = {0};
+ process_read(&lsp->process, response_text, sizeof response_text);
+ char *rnrn = strstr(response_text, "\r\n\r\n");
+ if (!rnrn) {
+ //@TODO delete me
+ printf("no analyzer ):\n");
+ exit(0);
+ }
+ JSON json = {0};
+ printf("%s\n",rnrn+4);
+ if (!json_parse(&json, rnrn + 4))
+ printf("fail : %s\n",json.error);
+ json_debug_print(&json);
+
+}
diff --git a/main.c b/main.c
index dbc49d5..517faaa 100644
--- a/main.c
+++ b/main.c
@@ -101,6 +101,7 @@ bool tag_goto(Ted *ted, char const *tag);
#include "command.c"
#include "config.c"
#include "session.c"
+#include "lsp.c"
#if PROFILE
#define PROFILE_TIME(var) double var = time_get_seconds();
@@ -279,6 +280,13 @@ int main(int argc, char **argv) {
PROFILE_TIME(init_start)
PROFILE_TIME(basic_init_start)
+ // @TODO TEMPORARY
+ {
+ LSP lsp={0};
+ lsp_create(&lsp, "rust-analyzer");
+ exit(0);
+ }
+
#if __unix__
{
struct sigaction act = {0};
diff --git a/process-posix.c b/process-posix.c
index f35017f..1463487 100644
--- a/process-posix.c
+++ b/process-posix.c
@@ -6,55 +6,101 @@
struct Process {
pid_t pid;
- int pipe;
+ int stdout_pipe;
+ int stdin_pipe;
char error[64];
};
-bool process_run(Process *proc, char const *command) {
+int process_get_id(void) {
+ return getpid();
+}
+
+bool process_run_ex(Process *proc, const char *command, const ProcessSettings *settings) {
memset(proc, 0, sizeof *proc);
+ int stdin_pipe[2] = {0}, stdout_pipe[2] = {0};
+ if (pipe(stdin_pipe) != 0) {
+ strbuf_printf(proc->error, "%s", strerror(errno));
+ return false;
+ }
+ if (pipe(stdout_pipe) != 0) {
+ strbuf_printf(proc->error, "%s", strerror(errno));
+ close(stdin_pipe[0]);
+ close(stdin_pipe[1]);
+ return false;
+ }
+
bool success = false;
- int pipefd[2];
- if (pipe(pipefd) == 0) {
- pid_t pid = fork();
- if (pid == 0) {
- // child process
- // put child in its own group. it will be in this group with all of its descendents,
- // so by killing everything in the group, we kill all the descendents of this process.
- // if we didn't do this, we would just be killing the sh process in process_kill.
- setpgid(0, 0);
- // send stdout and stderr to pipe
- dup2(pipefd[1], STDOUT_FILENO);
- dup2(pipefd[1], STDERR_FILENO);
- close(pipefd[0]);
- close(pipefd[1]);
- char *program = "/bin/sh";
- char *argv[] = {program, "-c", (char *)command, NULL};
- if (execv(program, argv) == -1) {
- dprintf(STDERR_FILENO, "%s: %s\n", program, strerror(errno));
- exit(127);
- }
- } else if (pid > 0) {
- // parent process
- close(pipefd[1]);
- fcntl(pipefd[0], F_SETFL, fcntl(pipefd[0], F_GETFL) | O_NONBLOCK); // set pipe to non-blocking
- proc->pid = pid;
- proc->pipe = pipefd[0];
- success = true;
+ pid_t pid = fork();
+ if (pid == 0) {
+ // child process
+ // put child in its own group. it will be in this group with all of its descendents,
+ // so by killing everything in the group, we kill all the descendents of this process.
+ // if we didn't do this, we would just be killing the sh process in process_kill.
+ setpgid(0, 0);
+ // pipe stuff
+ dup2(stdout_pipe[1], STDOUT_FILENO);
+ dup2(stdout_pipe[1], STDERR_FILENO);
+ dup2(stdin_pipe[0], STDIN_FILENO);
+ // don't need these file descriptors anymore
+ close(stdout_pipe[0]);
+ close(stdout_pipe[1]);
+ close(stdin_pipe[0]);
+ close(stdin_pipe[1]);
+
+ char *program = "/bin/sh";
+ char *argv[] = {program, "-c", (char *)command, NULL};
+ if (execv(program, argv) == -1) {
+ dprintf(STDERR_FILENO, "%s: %s\n", program, strerror(errno));
+ exit(127);
}
- } else {
- strbuf_printf(proc->error, "%s", strerror(errno));
+ } else if (pid > 0) {
+ // parent process
+
+ // we're reading from (the child's) stdout and writing to stdin,
+ // so we don't need the write end of the stdout pipe or the
+ // read end of the stdin pipe.
+ close(stdout_pipe[1]);
+ close(stdin_pipe[0]);
+ // set pipes to non-blocking
+ if (!settings->stdout_blocking)
+ fcntl(stdout_pipe[0], F_SETFL, fcntl(stdout_pipe[0], F_GETFL) | O_NONBLOCK);
+ if (!settings->stdin_blocking)
+ fcntl(stdin_pipe[1], F_SETFL, fcntl(stdin_pipe[1], F_GETFL) | O_NONBLOCK);
+ proc->pid = pid;
+ proc->stdout_pipe = stdout_pipe[0];
+ proc->stdin_pipe = stdin_pipe[1];
+ success = true;
}
return success;
}
+bool process_run(Process *proc, char const *command) {
+ const ProcessSettings settings = {0};
+ return process_run_ex(proc, command, &settings);
+}
+
+
char const *process_geterr(Process *p) {
return *p->error ? p->error : NULL;
}
+long long process_write(Process *proc, const char *data, size_t size) {
+ assert(proc->stdin_pipe); // check that process hasn't been killed
+ ssize_t bytes_written = write(proc->stdin_pipe, data, size);
+ if (bytes_written >= 0) {
+ return (long long)bytes_written;
+ } else if (errno == EAGAIN) {
+ return 0;
+ } else {
+ strbuf_printf(proc->error, "%s", strerror(errno));
+ return -2;
+ }
+}
+
long long process_read(Process *proc, char *data, size_t size) {
- assert(proc->pipe);
- ssize_t bytes_read = read(proc->pipe, data, size);
+ assert(proc->stdout_pipe);
+ ssize_t bytes_read = read(proc->stdout_pipe, data, size);
if (bytes_read >= 0) {
return (long long)bytes_read;
} else if (errno == EAGAIN) {
@@ -66,13 +112,19 @@ long long process_read(Process *proc, char *data, size_t size) {
}
+static void process_close_pipes(Process *proc) {
+ close(proc->stdout_pipe);
+ close(proc->stdin_pipe);
+ proc->stdout_pipe = 0;
+ proc->stdin_pipe = 0;
+}
+
void process_kill(Process *proc) {
kill(-proc->pid, SIGKILL); // kill everything in process group
// get rid of zombie process
waitpid(proc->pid, NULL, 0);
- close(proc->pipe);
proc->pid = 0;
- proc->pipe = 0;
+ process_close_pipes(proc);
}
int process_check_status(Process *proc, char *message, size_t message_size) {
@@ -83,7 +135,7 @@ int process_check_status(Process *proc, char *message, size_t message_size) {
return 0;
} else if (ret > 0) {
if (WIFEXITED(wait_status)) {
- close(proc->pipe); proc->pipe = 0;
+ process_close_pipes(proc);
int code = WEXITSTATUS(wait_status);
if (code == 0) {
str_printf(message, message_size, "exited successfully");
@@ -93,14 +145,14 @@ int process_check_status(Process *proc, char *message, size_t message_size) {
return -1;
}
} else if (WIFSIGNALED(wait_status)) {
- close(proc->pipe);
+ process_close_pipes(proc);
str_printf(message, message_size, "terminated by signal %d", WTERMSIG(wait_status));
return -1;
}
return 0;
} else {
// this process is gone or something?
- close(proc->pipe); proc->pipe = 0;
+ process_close_pipes(proc);
str_printf(message, message_size, "process ended unexpectedly");
return -1;
}
diff --git a/process.h b/process.h
index 4db864e..854899b 100644
--- a/process.h
+++ b/process.h
@@ -4,11 +4,27 @@
typedef struct Process Process;
+// zero everything except what you're using
+typedef struct {
+ bool stdin_blocking;
+ bool stdout_blocking;
+} ProcessSettings;
+
+// get process ID of this process
+int process_get_id(void);
// execute the given command (like if it was passed to system()).
// returns false on failure
+bool process_run_ex(Process *proc, const char *command, const ProcessSettings *props);
+// like process_run_ex, but with the default settings
bool process_run(Process *process, char const *command);
// returns the error last error produced, or NULL if there was no error.
char const *process_geterr(Process *process);
+// write to stdin
+// returns:
+// -2 on error
+// or a non-negative number indicating the number of bytes written.
+long long process_write(Process *process, const char *data, size_t size);
+// read from stdout+stderr
// returns:
// -2 on error
// -1 if no data is available right now