diff --git a/Makefile b/Makefile index ea9f3bc..3ef909f 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ SRCS= \ libraries/cJSON/cJSON.c \ src/json_rpc.c \ src/main.c \ - src/pawn_lsp.c + src/pawn_ls.c OBJS=$(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(SRCS)))) DEPS=$(addprefix $(BUILD_DIR)/,$(addsuffix .d,$(basename $(SRCS)))) diff --git a/README.md b/README.md index 25ec240..87d54ad 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # 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_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. diff --git a/src/main.c b/src/main.c index d817f3b..335e76e 100644 --- a/src/main.c +++ b/src/main.c @@ -6,12 +6,12 @@ #include "log.h" #include "json_rpc.h" -#include "pawn_lsp.h" +#include "pawn_ls.h" void sighandler(int signal); static json_rpc_t jrpc; -static pawn_lsp_t pawn; +static pawn_ls_t pawn; FILE *logger = NULL; @@ -22,11 +22,11 @@ main(int argc, char **argv) struct termios term_new; if (argc < 2) { - fprintf(stderr, "Usage: pawn_lsp [...pawncc options]\r\n"); + fprintf(stderr, "Usage: pawn_ls [...pawncc options]\r\n"); return (-1); } - logger = fopen("pawn_lsp.log", "w"); + logger = fopen("pawn_ls.log", "w"); if (signal(SIGINT, sighandler) == SIG_ERR) { log_error("Failed to intialize signal handling\r\n"); @@ -45,13 +45,13 @@ main(int argc, char **argv) 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"); return (-1); } json_rpc_run(&jrpc); - pawn_lsp_deinit(&pawn); + pawn_ls_deinit(&pawn); if (logger) { fclose(logger); diff --git a/src/pawn_engine.c b/src/pawn_engine.c new file mode 100644 index 0000000..e69de29 diff --git a/src/pawn_engine.h b/src/pawn_engine.h new file mode 100644 index 0000000..8a48cdb --- /dev/null +++ b/src/pawn_engine.h @@ -0,0 +1,59 @@ +#ifndef _PAWN_ENGINE_H_ +#define _PAWN_ENGINE_H_ + +#include + +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_ */ + diff --git a/src/pawn_lsp.c b/src/pawn_ls.c similarity index 56% rename from src/pawn_lsp.c rename to src/pawn_ls.c index 1739a6f..86ef960 100644 --- a/src/pawn_lsp.c +++ b/src/pawn_ls.c @@ -4,10 +4,10 @@ #include "cJSON/cJSON.h" #include "json_rpc.h" #include "log.h" -#include "pawn_lsp.h" +#include "pawn_ls.h" -#define PAWN_LSP_VESRION "v0.0.1" -#define PAWN_LSP_NAME "pawn_lsp" +#define PAWN_LS_VESRION "v0.0.1" +#define PAWN_LS_NAME "pawn_ls" 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 _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); int -pawn_lsp_init(pawn_lsp_t *pawn, +pawn_ls_init(pawn_ls_t *pawn, json_rpc_t *jrpc, const char *pawncc, char **pawn_args, @@ -45,7 +46,7 @@ pawn_lsp_init(pawn_lsp_t *pawn, } void -pawn_lsp_deinit(pawn_lsp_t *pawn) +pawn_ls_deinit(pawn_ls_t *pawn) { free(pawn->pawncc); 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); - if (!strcmp(method, "exit")) { + if (!strcmp(method, "textDocument/didOpen")) { + _didopen_notf(req, arg); + } else if (!strcmp(method, "exit")) { _exit_notf(req, arg); } else { log_warn("Unrecognized notification method\r\n"); @@ -90,26 +93,54 @@ _notify_handler(json_rpc_request_t *req, void *arg) int _initialize(json_rpc_request_t *req, cJSON **resp, void *arg) { + cJSON *capabilities; cJSON *val; (*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"); if (val == NULL) { return (-1); } - if ((cJSON_AddStringToObject(val, "name", PAWN_LSP_NAME) == NULL) || - (cJSON_AddStringToObject(val, "version", PAWN_LSP_VESRION) == NULL)) { + if ((cJSON_AddStringToObject(val, "name", PAWN_LS_NAME) == 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); } @@ -123,10 +154,35 @@ _shutdown(json_rpc_request_t *req, cJSON **resp, void *arg) 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 _exit_notf(json_rpc_request_t *req, void *arg) { - pawn_lsp_t *pawn = arg; + pawn_ls_t *pawn = arg; json_rpc_cancel(pawn->jrpc); } diff --git a/src/pawn_lsp.h b/src/pawn_ls.h similarity index 68% rename from src/pawn_lsp.h rename to src/pawn_ls.h index e833a38..4285432 100644 --- a/src/pawn_lsp.h +++ b/src/pawn_ls.h @@ -1,27 +1,27 @@ -#ifndef _PAWN_LSP_H_ -#define _PAWN_LSP_H_ +#ifndef _PAWN_LS_H_ +#define _PAWN_LS_H_ #include "json_rpc.h" -typedef struct pawn_lsp_struct { +typedef struct pawn_ls_struct { /** Active JRPC instance */ json_rpc_t *jrpc; /** Path to the pawncc compiler */ char *pawncc; /** User provided args for pawncc */ char **pawn_args; -} pawn_lsp_t; +} pawn_ls_t; /** * @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, const char *pawncc, char **pawn_argv, 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_ */