Initial Commit
This commit is contained in:
371
tests/lsp_test.c
Normal file
371
tests/lsp_test.c
Normal file
@@ -0,0 +1,371 @@
|
||||
#include "../libs/cjson/cJSON.h"
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int lsp_stdin_fd = -1;
|
||||
int lsp_stdout_fd = -1;
|
||||
int cols;
|
||||
|
||||
void start_lsp(const char *cmd, char *const argv[]) {
|
||||
int in_pipe[2];
|
||||
int out_pipe[2];
|
||||
if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) {
|
||||
perror("pipe");
|
||||
exit(1);
|
||||
}
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("fork");
|
||||
exit(1);
|
||||
}
|
||||
if (pid == 0) {
|
||||
dup2(in_pipe[0], STDIN_FILENO);
|
||||
dup2(out_pipe[1], STDOUT_FILENO);
|
||||
int devnull = open("/dev/null", O_WRONLY);
|
||||
if (devnull != -1) {
|
||||
dup2(devnull, STDERR_FILENO);
|
||||
close(devnull);
|
||||
}
|
||||
close(in_pipe[1]);
|
||||
close(out_pipe[0]);
|
||||
execvp(cmd, argv);
|
||||
perror("execvp");
|
||||
_exit(1);
|
||||
}
|
||||
close(in_pipe[0]);
|
||||
close(out_pipe[1]);
|
||||
lsp_stdin_fd = in_pipe[1];
|
||||
lsp_stdout_fd = out_pipe[0];
|
||||
}
|
||||
|
||||
void lsp_send(cJSON *msg) {
|
||||
char *dump = cJSON_PrintUnformatted(msg);
|
||||
int len = strlen(dump);
|
||||
dprintf(lsp_stdin_fd, "Content-Length: %d\r\n\r\n%s", len, dump);
|
||||
free(dump);
|
||||
}
|
||||
|
||||
cJSON *lsp_recv() {
|
||||
char header[256];
|
||||
int content_length = 0;
|
||||
char c;
|
||||
int pos = 0;
|
||||
int newlines = 0;
|
||||
while (read(lsp_stdout_fd, &c, 1) == 1) {
|
||||
header[pos++] = c;
|
||||
if (pos >= sizeof(header) - 1)
|
||||
break;
|
||||
if (c == '\n') {
|
||||
header[pos] = '\0';
|
||||
if (strncmp(header, "Content-Length:", 15) == 0) {
|
||||
content_length = atoi(header + 15);
|
||||
}
|
||||
if (strcmp(header, "\r\n") == 0)
|
||||
break;
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
if (content_length <= 0)
|
||||
return NULL;
|
||||
char *buf = malloc(content_length + 1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
int got = 0;
|
||||
while (got < content_length) {
|
||||
int n = read(lsp_stdout_fd, buf + got, content_length - got);
|
||||
if (n <= 0)
|
||||
break;
|
||||
got += n;
|
||||
}
|
||||
buf[got] = '\0';
|
||||
cJSON *msg = cJSON_Parse(buf);
|
||||
free(buf);
|
||||
return msg;
|
||||
}
|
||||
|
||||
cJSON *make_fold_request(int id, const char *uri) {
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
||||
cJSON_AddNumberToObject(root, "id", id);
|
||||
cJSON_AddStringToObject(root, "method", "textDocument/foldingRange");
|
||||
cJSON *params = cJSON_CreateObject();
|
||||
cJSON *doc = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(doc, "uri", uri);
|
||||
cJSON_AddItemToObject(params, "textDocument", doc);
|
||||
cJSON_AddItemToObject(root, "params", params);
|
||||
return root;
|
||||
}
|
||||
|
||||
cJSON *make_hover_request(int id, const char *uri) {
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
||||
cJSON_AddNumberToObject(root, "id", id);
|
||||
cJSON_AddStringToObject(root, "method", "textDocument/completion");
|
||||
cJSON *params = cJSON_CreateObject();
|
||||
cJSON *doc = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(doc, "uri", uri);
|
||||
cJSON_AddItemToObject(params, "textDocument", doc);
|
||||
cJSON *pos = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(pos, "line", 15);
|
||||
cJSON_AddNumberToObject(pos, "character", 6);
|
||||
cJSON_AddItemToObject(params, "position", pos);
|
||||
cJSON_AddItemToObject(root, "params", params);
|
||||
return root;
|
||||
}
|
||||
|
||||
cJSON *make_document_symbol_request(int id, const char *uri) {
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
||||
cJSON_AddNumberToObject(root, "id", id);
|
||||
cJSON_AddStringToObject(root, "method", "textDocument/documentSymbol");
|
||||
|
||||
cJSON *params = cJSON_CreateObject();
|
||||
cJSON *textDocument = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(textDocument, "uri", uri);
|
||||
cJSON_AddItemToObject(params, "textDocument", textDocument);
|
||||
|
||||
cJSON_AddItemToObject(root, "params", params);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
cJSON *make_features_request(int id, const char *root_uri) {
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
||||
cJSON_AddNumberToObject(root, "id", id);
|
||||
cJSON_AddStringToObject(root, "method", "initialize");
|
||||
cJSON *params = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(params, "processId", "0");
|
||||
cJSON_AddStringToObject(params, "rootUri", root_uri);
|
||||
cJSON *capabilities = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(params, "capabilities", capabilities);
|
||||
cJSON_AddItemToObject(root, "params", params);
|
||||
return root;
|
||||
}
|
||||
|
||||
cJSON *make_folder_init_request(int id, const char *root_uri) {
|
||||
cJSON *init = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(init, "jsonrpc", "2.0");
|
||||
cJSON_AddNumberToObject(init, "id", id);
|
||||
cJSON_AddStringToObject(init, "method", "initialize");
|
||||
|
||||
cJSON *params = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(params, "rootUri", root_uri);
|
||||
|
||||
// capabilities
|
||||
cJSON *capabilities = cJSON_CreateObject();
|
||||
cJSON *textDocument = cJSON_CreateObject();
|
||||
cJSON *diagnosticProvider = cJSON_CreateObject();
|
||||
// Enable diagnostics
|
||||
cJSON_AddBoolToObject(diagnosticProvider, "interFileDependencies", false);
|
||||
cJSON_AddBoolToObject(diagnosticProvider, "workspaceDiagnostics", false);
|
||||
cJSON_AddItemToObject(textDocument, "diagnosticProvider", diagnosticProvider);
|
||||
cJSON_AddItemToObject(capabilities, "textDocument", textDocument);
|
||||
|
||||
cJSON_AddItemToObject(params, "capabilities", capabilities);
|
||||
cJSON_AddItemToObject(init, "params", params);
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
cJSON *make_initialized_request(void) {
|
||||
cJSON *msg = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(msg, "jsonrpc", "2.0");
|
||||
cJSON_AddStringToObject(msg, "method", "initialized");
|
||||
cJSON *params = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(msg, "params", params);
|
||||
return msg;
|
||||
}
|
||||
|
||||
cJSON *make_change_file_request(int id, const char *uri) {
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
||||
cJSON_AddStringToObject(root, "method", "textDocument/didChange");
|
||||
cJSON *params = cJSON_CreateObject();
|
||||
cJSON *doc = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(doc, "uri", uri);
|
||||
cJSON_AddNumberToObject(doc, "version", 2);
|
||||
cJSON_AddItemToObject(params, "textDocument", doc);
|
||||
cJSON *changes = cJSON_CreateArray();
|
||||
cJSON *change = cJSON_CreateObject();
|
||||
cJSON *range = cJSON_CreateObject();
|
||||
cJSON *start = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(start, "line", 0);
|
||||
cJSON_AddNumberToObject(start, "character", 0);
|
||||
cJSON *end = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(end, "line", 0);
|
||||
cJSON_AddNumberToObject(end, "character", 0);
|
||||
cJSON_AddItemToObject(range, "start", start);
|
||||
cJSON_AddItemToObject(range, "end", end);
|
||||
cJSON_AddItemToObject(change, "range", range);
|
||||
cJSON_AddStringToObject(change, "text", "");
|
||||
cJSON_AddItemToArray(changes, change);
|
||||
cJSON_AddItemToObject(params, "contentChanges", changes);
|
||||
cJSON_AddItemToObject(root, "params", params);
|
||||
return root;
|
||||
}
|
||||
|
||||
cJSON *make_open_file_request(const char *uri, const char *languageId,
|
||||
const char *text, int version) {
|
||||
cJSON *msg = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(msg, "jsonrpc", "2.0");
|
||||
cJSON_AddStringToObject(msg, "method", "textDocument/didOpen");
|
||||
|
||||
cJSON *params = cJSON_CreateObject();
|
||||
cJSON *doc = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(doc, "uri", uri);
|
||||
cJSON_AddStringToObject(doc, "languageId", languageId);
|
||||
cJSON_AddNumberToObject(doc, "version", version);
|
||||
cJSON_AddStringToObject(doc, "text", text);
|
||||
|
||||
cJSON_AddItemToObject(params, "textDocument", doc);
|
||||
cJSON_AddItemToObject(msg, "params", params);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
int get_key(void) {
|
||||
struct termios oldt, newt;
|
||||
int ch;
|
||||
tcgetattr(STDIN_FILENO, &oldt);
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||
ch = getchar();
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||
struct winsize ws;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
|
||||
cols = ws.ws_col;
|
||||
return ch;
|
||||
}
|
||||
|
||||
char *read_file(const char *path) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
// seek to end to get size
|
||||
if (fseek(f, 0, SEEK_END) != 0) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
long size = ftell(f);
|
||||
if (size < 0) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
rewind(f);
|
||||
|
||||
char *buf = malloc(size + 1);
|
||||
if (!buf) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t read_size = fread(buf, 1, size, f);
|
||||
buf[read_size] = '\0'; // null terminate
|
||||
fclose(f);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void print_wrapped(const char *text) {
|
||||
struct winsize ws;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
|
||||
int cols = ws.ws_col > 0 ? ws.ws_col : 80; // fallback to 80 if unknown
|
||||
|
||||
int col = 0;
|
||||
for (size_t i = 0; text[i]; i++) {
|
||||
putchar(text[i]);
|
||||
col++;
|
||||
|
||||
if (text[i] == '\n') {
|
||||
col = 0; // reset column on explicit newline
|
||||
} else if (col >= cols) {
|
||||
putchar('\n');
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
char *argv[] = {"clangd", NULL};
|
||||
start_lsp("clangd", argv);
|
||||
cJSON *init =
|
||||
make_folder_init_request(1, "file:///home/syed/main/cubit/tests/");
|
||||
lsp_send(init);
|
||||
cJSON_Delete(init);
|
||||
cJSON *initialized = make_initialized_request();
|
||||
lsp_send(initialized);
|
||||
cJSON_Delete(initialized);
|
||||
char *text = read_file("/home/syed/main/cubit/tests/test.c");
|
||||
printf("%s\n", text);
|
||||
cJSON *req = make_open_file_request(
|
||||
"file:///home/syed/main/cubit/tests/test.c", "c", text, 1);
|
||||
lsp_send(req);
|
||||
cJSON_Delete(req);
|
||||
free(text);
|
||||
// cJSON *doc_sym_req = make_document_symbol_request(
|
||||
// 2, "file:///home/syed/main/cubit/tests/test.c");
|
||||
// lsp_send(doc_sym_req);
|
||||
// cJSON_Delete(doc_sym_req);
|
||||
usleep(1000000);
|
||||
int next_id = 3;
|
||||
while (1) {
|
||||
printf("Press 'f' to request fold ranges, 'q' to quit: ");
|
||||
fflush(stdout);
|
||||
int key = get_key();
|
||||
printf("\n");
|
||||
if (key == 'q')
|
||||
break;
|
||||
if (key == 'f') {
|
||||
cJSON *req = make_fold_request(
|
||||
next_id++, "file:///home/syed/main/cubit/tests/test.c");
|
||||
lsp_send(req);
|
||||
cJSON_Delete(req);
|
||||
}
|
||||
if (key == 'i') {
|
||||
cJSON *req = make_features_request(next_id++,
|
||||
"file:///home/syed/main/cubit/tests/");
|
||||
lsp_send(req);
|
||||
cJSON_Delete(req);
|
||||
}
|
||||
if (key == 'h') {
|
||||
cJSON *req = make_hover_request(
|
||||
next_id++, "file:///home/syed/main/cubit/tests/test.c");
|
||||
lsp_send(req);
|
||||
cJSON_Delete(req);
|
||||
}
|
||||
if (key == 'c') {
|
||||
cJSON *req = make_change_file_request(
|
||||
next_id++, "file:///home/syed/main/cubit/tests/test.c");
|
||||
lsp_send(req);
|
||||
cJSON_Delete(req);
|
||||
}
|
||||
cJSON *resp;
|
||||
if ((resp = lsp_recv()) == NULL)
|
||||
continue;
|
||||
char *dump = cJSON_PrintUnformatted(resp);
|
||||
cJSON *id = cJSON_GetObjectItem(resp, "id");
|
||||
// if (id != NULL && id->valueint == 40) {
|
||||
// cJSON *result = cJSON_GetObjectItem(resp, "result");
|
||||
// cJSON *contents = cJSON_GetObjectItem(result, "contents");
|
||||
// cJSON *value = cJSON_GetObjectItem(contents, "value");
|
||||
// if (value && value->type == cJSON_String)
|
||||
// printf("%s\n", value->valuestring);
|
||||
// }
|
||||
print_wrapped(dump);
|
||||
free(dump);
|
||||
cJSON_Delete(resp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user