Compare commits

...
Sign in to create a new pull request.
Author SHA1 Message Date
c778c1aa82 Pawn engine WIP 2026-06-20 23:03:35 -05:00
255c28f26c Rename pawn_lsp to pawn_ls 2026-06-20 12:25:49 -05:00
d86d2af6d0 Add didOpen handler (WIP) 2026-06-20 01:55:04 -05:00
81229426da Add capabilities to init request 2026-06-20 01:54:31 -05:00
7 changed files with 143 additions and 28 deletions

View file

@ -18,7 +18,7 @@ SRCS= \
libraries/cJSON/cJSON.c \ libraries/cJSON/cJSON.c \
src/json_rpc.c \ src/json_rpc.c \
src/main.c \ src/main.c \
src/pawn_lsp.c src/pawn_ls.c
OBJS=$(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(SRCS)))) OBJS=$(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(SRCS))))
DEPS=$(addprefix $(BUILD_DIR)/,$(addsuffix .d,$(basename $(SRCS)))) DEPS=$(addprefix $(BUILD_DIR)/,$(addsuffix .d,$(basename $(SRCS))))

View file

@ -1,8 +1,8 @@
# Pawn Language Server # Pawn Language Server
`pawn_lsp` is a project to provide basic LSP functionality for the `pawn_ls` is a project to provide basic language server functionality for the
[Pawn scripting language](https://codeberg.org/compuphase/pawn). [Pawn scripting language](https://codeberg.org/compuphase/pawn).
`pawn_lsp` is currently in development and has no releases, refer to the `pawn_ls` is currently in development and has no releases, refer to the
repository work items for development status. repository work items for development status.

View file

@ -6,12 +6,12 @@
#include "log.h" #include "log.h"
#include "json_rpc.h" #include "json_rpc.h"
#include "pawn_lsp.h" #include "pawn_ls.h"
void sighandler(int signal); void sighandler(int signal);
static json_rpc_t jrpc; static json_rpc_t jrpc;
static pawn_lsp_t pawn; static pawn_ls_t pawn;
FILE *logger = NULL; FILE *logger = NULL;
@ -22,11 +22,11 @@ main(int argc, char **argv)
struct termios term_new; struct termios term_new;
if (argc < 2) { if (argc < 2) {
fprintf(stderr, "Usage: pawn_lsp <pawncc> [...pawncc options]\r\n"); fprintf(stderr, "Usage: pawn_ls <pawncc> [...pawncc options]\r\n");
return (-1); return (-1);
} }
logger = fopen("pawn_lsp.log", "w"); logger = fopen("pawn_ls.log", "w");
if (signal(SIGINT, sighandler) == SIG_ERR) { if (signal(SIGINT, sighandler) == SIG_ERR) {
log_error("Failed to intialize signal handling\r\n"); log_error("Failed to intialize signal handling\r\n");
@ -45,13 +45,13 @@ main(int argc, char **argv)
return (-1); return (-1);
} }
if (pawn_lsp_init(&pawn, &jrpc, argv[1], &argv[2], argc - 2)) { if (pawn_ls_init(&pawn, &jrpc, argv[1], &argv[2], argc - 2)) {
log_error("Failed to register Pawn language server\r\n"); log_error("Failed to register Pawn language server\r\n");
return (-1); return (-1);
} }
json_rpc_run(&jrpc); json_rpc_run(&jrpc);
pawn_lsp_deinit(&pawn); pawn_ls_deinit(&pawn);
if (logger) { if (logger) {
fclose(logger); fclose(logger);

0
src/pawn_engine.c Normal file
View file

59
src/pawn_engine.h Normal file
View file

@ -0,0 +1,59 @@
#ifndef _PAWN_ENGINE_H_
#define _PAWN_ENGINE_H_
#include <stddef.h>
typedef struct str_struct {
/** Actual value of the string */
char *val;
/** Current length of the string in bytes */
size_t len;
/** Capacity of the string buffer */
size_t cap;
} str_t;
typedef struct file_position_struct {
/** Line number in the file (1-indexed) */
size_t line;
/** Column number in the file (1-indexed) */
size_t column;
} file_position_t;
typedef struct file_range_struct {
/** Start of the range, inclusive */
file_position_t start;
/** End of the range, inclusive */
file_position_t end;
} file_range_t;
typedef struct file_diagnostic_struct {
/** Message for the diagnostic */
str_t message;
/** Range of text the diagnostic applies to */
file_range_t range;
} file_diagnostic_t;
typedef struct pawn_diagnostics_struct {
/** Errors present in a Pawn script */
file_diagnostic_t *errors;
/** Number of errors */
size_t errors_len;
/** Warnings present in a Pawn script */
file_diagnostic_t *warnings;
/** Number of warnings */
size_t warnings_len;
} pawn_diagnostics_t;
typedef struct pawn_engine_settings_struct {
/** Path to the pawncc executable */
const char *pawncc;
/** Extra options to run pawncc with, not including the pawncc or script paths */
const char **argv;
/** Number of extra options */
int argc;
} pawn_engine_settings_t;
pawn_diagnostics_t *pawn_engine_compile(const char *path, pawn_engine_settings_t *settings);
#endif /* _PAWN_ENGINE_H_ */

View file

@ -4,10 +4,10 @@
#include "cJSON/cJSON.h" #include "cJSON/cJSON.h"
#include "json_rpc.h" #include "json_rpc.h"
#include "log.h" #include "log.h"
#include "pawn_lsp.h" #include "pawn_ls.h"
#define PAWN_LSP_VESRION "v0.0.1" #define PAWN_LS_VESRION "v0.0.1"
#define PAWN_LSP_NAME "pawn_lsp" #define PAWN_LS_NAME "pawn_ls"
static int _request_handler(json_rpc_request_t *req, cJSON **resp, void *arg); static int _request_handler(json_rpc_request_t *req, cJSON **resp, void *arg);
@ -16,10 +16,11 @@ 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 _initialize(json_rpc_request_t *req, cJSON **resp, void *arg);
static int _shutdown(json_rpc_request_t *req, cJSON **resp, void *arg); static int _shutdown(json_rpc_request_t *req, cJSON **resp, void *arg);
static void _didopen_notf(json_rpc_request_t *req, void *arg);
static void _exit_notf(json_rpc_request_t *req, void *arg); static void _exit_notf(json_rpc_request_t *req, void *arg);
int int
pawn_lsp_init(pawn_lsp_t *pawn, pawn_ls_init(pawn_ls_t *pawn,
json_rpc_t *jrpc, json_rpc_t *jrpc,
const char *pawncc, const char *pawncc,
char **pawn_args, char **pawn_args,
@ -45,7 +46,7 @@ pawn_lsp_init(pawn_lsp_t *pawn,
} }
void void
pawn_lsp_deinit(pawn_lsp_t *pawn) pawn_ls_deinit(pawn_ls_t *pawn)
{ {
free(pawn->pawncc); free(pawn->pawncc);
for (int i = 0; pawn->pawn_args[i] != NULL; i++) { for (int i = 0; pawn->pawn_args[i] != NULL; i++) {
@ -79,7 +80,9 @@ _notify_handler(json_rpc_request_t *req, void *arg)
log_info("Handle notification \"%s\"\r\n", method); log_info("Handle notification \"%s\"\r\n", method);
if (!strcmp(method, "exit")) { if (!strcmp(method, "textDocument/didOpen")) {
_didopen_notf(req, arg);
} else if (!strcmp(method, "exit")) {
_exit_notf(req, arg); _exit_notf(req, arg);
} else { } else {
log_warn("Unrecognized notification method\r\n"); log_warn("Unrecognized notification method\r\n");
@ -90,26 +93,54 @@ _notify_handler(json_rpc_request_t *req, void *arg)
int int
_initialize(json_rpc_request_t *req, cJSON **resp, void *arg) _initialize(json_rpc_request_t *req, cJSON **resp, void *arg)
{ {
cJSON *capabilities;
cJSON *val; cJSON *val;
(*resp) = cJSON_CreateObject(); (*resp) = cJSON_CreateObject();
val = cJSON_AddObjectToObject((*resp), "capabilities");
if (val == NULL) {
return (-1);
}
if (cJSON_AddStringToObject(val, "positionEncoding", "utf-8") == NULL) {
return (-1);
}
val = cJSON_AddObjectToObject((*resp), "serverInfo"); val = cJSON_AddObjectToObject((*resp), "serverInfo");
if (val == NULL) { if (val == NULL) {
return (-1); return (-1);
} }
if ((cJSON_AddStringToObject(val, "name", PAWN_LSP_NAME) == NULL) || if ((cJSON_AddStringToObject(val, "name", PAWN_LS_NAME) == NULL) ||
(cJSON_AddStringToObject(val, "version", PAWN_LSP_VESRION) == NULL)) { (cJSON_AddStringToObject(val, "version", PAWN_LS_VESRION) == NULL)) {
return (-1);
}
/* 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); return (-1);
} }
@ -123,10 +154,35 @@ _shutdown(json_rpc_request_t *req, cJSON **resp, void *arg)
return (0); return (0);
} }
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");
}
}
static void static void
_exit_notf(json_rpc_request_t *req, void *arg) _exit_notf(json_rpc_request_t *req, void *arg)
{ {
pawn_lsp_t *pawn = arg; pawn_ls_t *pawn = arg;
json_rpc_cancel(pawn->jrpc); json_rpc_cancel(pawn->jrpc);
} }

View file

@ -1,27 +1,27 @@
#ifndef _PAWN_LSP_H_ #ifndef _PAWN_LS_H_
#define _PAWN_LSP_H_ #define _PAWN_LS_H_
#include "json_rpc.h" #include "json_rpc.h"
typedef struct pawn_lsp_struct { typedef struct pawn_ls_struct {
/** Active JRPC instance */ /** Active JRPC instance */
json_rpc_t *jrpc; json_rpc_t *jrpc;
/** Path to the pawncc compiler */ /** Path to the pawncc compiler */
char *pawncc; char *pawncc;
/** User provided args for pawncc */ /** User provided args for pawncc */
char **pawn_args; char **pawn_args;
} pawn_lsp_t; } pawn_ls_t;
/** /**
* @brief Register JSON RPC methods for the Pawn language server * @brief Register JSON RPC methods for the Pawn language server
*/ */
int pawn_lsp_init(pawn_lsp_t *pawn, int pawn_ls_init(pawn_ls_t *pawn,
json_rpc_t *jrpc, json_rpc_t *jrpc,
const char *pawncc, const char *pawncc,
char **pawn_argv, char **pawn_argv,
int pawn_argc); int pawn_argc);
void pawn_lsp_deinit(pawn_lsp_t *pawn); void pawn_ls_deinit(pawn_ls_t *pawn);
#endif /* _PAWN_LSP_H_ */ #endif /* _PAWN_LS_H_ */