2026-05-24 21:17:10 -05:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include "cJSON/cJSON.h"
|
|
|
|
|
#include "json_rpc.h"
|
|
|
|
|
#include "log.h"
|
2026-06-20 12:22:10 -05:00
|
|
|
#include "pawn_ls.h"
|
2026-05-24 21:17:10 -05:00
|
|
|
|
2026-06-20 12:22:10 -05:00
|
|
|
#define PAWN_LS_VESRION "v0.0.1"
|
|
|
|
|
#define PAWN_LS_NAME "pawn_ls"
|
2026-05-24 21:17:10 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
static int _request_handler(json_rpc_request_t *req, cJSON **resp, void *arg);
|
|
|
|
|
static void _notify_handler(json_rpc_request_t *req, void *arg);
|
|
|
|
|
|
|
|
|
|
static int _initialize(json_rpc_request_t *req, cJSON **resp, void *arg);
|
|
|
|
|
static int _shutdown(json_rpc_request_t *req, cJSON **resp, void *arg);
|
|
|
|
|
|
2026-06-20 01:53:38 -05:00
|
|
|
static void _didopen_notf(json_rpc_request_t *req, void *arg);
|
2026-05-24 21:17:10 -05:00
|
|
|
static void _exit_notf(json_rpc_request_t *req, void *arg);
|
|
|
|
|
|
|
|
|
|
int
|
2026-06-20 12:22:10 -05:00
|
|
|
pawn_ls_init(pawn_ls_t *pawn,
|
2026-05-24 21:17:10 -05:00
|
|
|
json_rpc_t *jrpc,
|
|
|
|
|
const char *pawncc,
|
|
|
|
|
char **pawn_args,
|
|
|
|
|
int pawn_argc)
|
|
|
|
|
{
|
|
|
|
|
pawn->jrpc = jrpc;
|
|
|
|
|
pawn->pawncc = strdup(pawncc);
|
|
|
|
|
|
|
|
|
|
/* execv(pawncc <filename> -d0 -v=2 -o/tmp/... [...args] <NULL>)*/
|
|
|
|
|
pawn->pawn_args = calloc(pawn_argc + 5, sizeof(char *));
|
|
|
|
|
/* pawn->pawn_args[0] = <filename>, set when compiling */
|
|
|
|
|
pawn->pawn_args[1] = strdup("-d0");
|
|
|
|
|
pawn->pawn_args[2] = strdup("-v=2");
|
|
|
|
|
/* pawn->pawn_args[3] = -o/tmp/..., set when compiling */
|
|
|
|
|
for (int i = 0; i < pawn_argc; i++) {
|
|
|
|
|
pawn->pawn_args[i + 4] = strdup(pawn_args[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_rpc_set_request_handler(jrpc, _request_handler, &pawn);
|
|
|
|
|
json_rpc_set_notify_handler(jrpc, _notify_handler, &pawn);
|
|
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2026-06-20 12:22:10 -05:00
|
|
|
pawn_ls_deinit(pawn_ls_t *pawn)
|
2026-05-24 21:17:10 -05:00
|
|
|
{
|
|
|
|
|
free(pawn->pawncc);
|
|
|
|
|
for (int i = 0; pawn->pawn_args[i] != NULL; i++) {
|
|
|
|
|
free(pawn->pawn_args[i]);
|
|
|
|
|
}
|
|
|
|
|
free(pawn->pawn_args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
_request_handler(json_rpc_request_t *req, cJSON **resp, void *arg)
|
|
|
|
|
{
|
|
|
|
|
char *method = req->method->valuestring;
|
|
|
|
|
|
|
|
|
|
log_info("Handle request \"%s\"\r\n", method);
|
|
|
|
|
|
|
|
|
|
if (!strcmp(method, "initialize")) {
|
|
|
|
|
return _initialize(req, resp, arg);
|
|
|
|
|
} else if (!strcmp(method, "shutdown")) {
|
|
|
|
|
return _shutdown(req, resp, arg);
|
|
|
|
|
} else {
|
|
|
|
|
log_warn("Unrecognized request method\r\n");
|
|
|
|
|
(*resp) = cJSON_CreateNull();
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_notify_handler(json_rpc_request_t *req, void *arg)
|
|
|
|
|
{
|
|
|
|
|
char *method = req->method->valuestring;
|
|
|
|
|
|
|
|
|
|
log_info("Handle notification \"%s\"\r\n", method);
|
|
|
|
|
|
2026-06-20 01:53:38 -05:00
|
|
|
if (!strcmp(method, "textDocument/didOpen")) {
|
|
|
|
|
_didopen_notf(req, arg);
|
|
|
|
|
} else if (!strcmp(method, "exit")) {
|
2026-05-24 21:17:10 -05:00
|
|
|
_exit_notf(req, arg);
|
|
|
|
|
} else {
|
|
|
|
|
log_warn("Unrecognized notification method\r\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
_initialize(json_rpc_request_t *req, cJSON **resp, void *arg)
|
|
|
|
|
{
|
2026-06-16 16:12:45 -05:00
|
|
|
cJSON *capabilities;
|
2026-05-24 21:17:10 -05:00
|
|
|
cJSON *val;
|
|
|
|
|
|
|
|
|
|
(*resp) = cJSON_CreateObject();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val = cJSON_AddObjectToObject((*resp), "serverInfo");
|
|
|
|
|
if (val == NULL) {
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-20 12:22:10 -05:00
|
|
|
if ((cJSON_AddStringToObject(val, "name", PAWN_LS_NAME) == NULL) ||
|
|
|
|
|
(cJSON_AddStringToObject(val, "version", PAWN_LS_VESRION) == NULL)) {
|
2026-05-24 21:17:10 -05:00
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 16:12:45 -05:00
|
|
|
|
|
|
|
|
/* All capabilities */
|
|
|
|
|
capabilities = cJSON_AddObjectToObject((*resp), "capabilities");
|
|
|
|
|
if (capabilities == NULL) {
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* utf-8 encoding only */
|
|
|
|
|
if (cJSON_AddStringToObject(capabilities, "positionEncoding", "utf-8") == NULL) {
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TextDocumentSync */
|
|
|
|
|
val = cJSON_AddObjectToObject(capabilities, "textDocumentSync");
|
|
|
|
|
if (val == NULL) {
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((cJSON_AddTrueToObject(val, "openClose") == NULL) ||
|
|
|
|
|
/* 1 = full content */
|
|
|
|
|
(cJSON_AddNumberToObject(val, "change", 1) == NULL)) {
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hover */
|
|
|
|
|
if (cJSON_AddTrueToObject(capabilities, "hoverProvider") == NULL) {
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Go to's */
|
|
|
|
|
if ((cJSON_AddTrueToObject(capabilities, "declarationProvider") == NULL) ||
|
|
|
|
|
(cJSON_AddTrueToObject(capabilities, "definitionProvider") == NULL)) {
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-24 21:17:10 -05:00
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
_shutdown(json_rpc_request_t *req, cJSON **resp, void *arg)
|
|
|
|
|
{
|
|
|
|
|
(*resp) = cJSON_CreateNull();
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-20 01:53:38 -05:00
|
|
|
static void
|
|
|
|
|
_didopen_notf(json_rpc_request_t *req, void *arg) {
|
|
|
|
|
cJSON *textdoc = cJSON_GetObjectItem(req->params, "textDocument");
|
|
|
|
|
cJSON *val;
|
|
|
|
|
|
|
|
|
|
if ((textdoc == NULL) || (!cJSON_IsObject(textdoc))) {
|
|
|
|
|
log_error("Invalid textDocument field\r\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val = cJSON_GetObjectItem(textdoc, "uri");
|
|
|
|
|
if (val && cJSON_IsString(val)) {
|
|
|
|
|
log_info("textdoc uri: %s\r\n", val->valuestring);
|
|
|
|
|
} else {
|
|
|
|
|
log_warn("Invalid uri\r\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val = cJSON_GetObjectItem(textdoc, "text");
|
|
|
|
|
if (val && cJSON_IsString(val)) {
|
|
|
|
|
log_info("textdoc text: \"%s\"\r\n", val->valuestring);
|
|
|
|
|
} else {
|
|
|
|
|
log_warn("Invalid text\r\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-24 21:17:10 -05:00
|
|
|
static void
|
|
|
|
|
_exit_notf(json_rpc_request_t *req, void *arg)
|
|
|
|
|
{
|
2026-06-20 12:22:10 -05:00
|
|
|
pawn_ls_t *pawn = arg;
|
2026-05-24 21:17:10 -05:00
|
|
|
json_rpc_cancel(pawn->jrpc);
|
|
|
|
|
}
|
|
|
|
|
|