Skip to content

Commit

Permalink
Support multiple workspace folders configured by new project_root_mar…
Browse files Browse the repository at this point in the history
…ker_patterns settings
  • Loading branch information
techee committed Jun 7, 2024
1 parent 0bfd21f commit c98a149
Show file tree
Hide file tree
Showing 10 changed files with 435 additions and 16 deletions.
23 changes: 21 additions & 2 deletions lsp/data/lsp.conf
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,26 @@ enable_by_default=false
# Defines whether the server should be used when no project is open. Servers
# may not work correctly without a project because most of them need to know
# the path to the project directory which corresponds to the path defined under
# Project->Properties->Base path
# Project->Properties->Base path. This option can be partially overridden
# by project_root_marker_patterns, see below
use_without_project=false
# Defines whether the server should be used for files whose path is not within
# the project directory
# the project directory. This option can be partially overridden by
# project_root_marker_patterns, see below
use_outside_project_dir=false
# A semicolon-separated list of glob patterns of files that are typically stored
# inside the root directory of the project. Language servers supporting
# changeNotifications of workspaceFolders (these two values should appear inside
# the server initialize response) can use these marker files to detect root
# project directories of open files. Starting from the open file directory,
# the plugin goes up in the directory structure and tests whether a file
# matching project_root_marker_patterns exists - if it does, such a directory
# is considered to be the root directory of the project. This allows to
# detect projects without any Geany project open and allows the plugin to work
# on multiple projects simultaneously. Typically, the pattern contains
# files/directories such as .git, configure.ac, go.mod, etc. If a pattern is
# found, enable_by_default and use_without_project are ignored
project_root_marker_patterns=

# The number of keybindings that can be assigned to LSP code action commands.
# This option is valid only within the [all] section and changing the value
Expand Down Expand Up @@ -186,6 +201,10 @@ format_on_save=false

# This is a dummy language server configuration describing the available
# language-specific options
# For an extensive list of various servers and their configurations, check
# https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md
# While the configuration options names of neovim differ from Geany, the
# general concepts are similar and applicable here
[DummyLanguage]
# The command (including parameters and possibly also the full path) used to
# start the LSP server. Instead of starting a new server, it is also possible to
Expand Down
4 changes: 3 additions & 1 deletion lsp/src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ lsp_la_SOURCES = \
lsp-sync.c \
lsp-sync.h \
lsp-utils.c \
lsp-utils.h
lsp-utils.h \
lsp-workspace-folders.c \
lsp-workspace-folders.h

lsp_la_CPPFLAGS = $(AM_CPPFLAGS) \
-DG_LOG_DOMAIN=\"LSP\" \
Expand Down
6 changes: 6 additions & 0 deletions lsp/src/lsp-main.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "lsp-code-lens.h"
#include "lsp-symbol.h"
#include "lsp-extension.h"
#include "lsp-workspace-folders.h"

#include <sys/time.h>
#include <string.h>
Expand Down Expand Up @@ -459,6 +460,8 @@ static void on_document_visible(GeanyDocument *doc)
if (srv && !lsp_sync_is_document_open(doc))
lsp_sync_text_document_did_open(srv, doc);

lsp_workspace_folders_doc_open(doc);

on_update_idle(doc);

#ifndef HAVE_GEANY_PLUGIN_EXTENSION
Expand Down Expand Up @@ -504,6 +507,7 @@ static void on_document_close(G_GNUC_UNUSED GObject * obj, GeanyDocument *doc,
lsp_diagnostics_clear(doc);
lsp_semtokens_clear(doc);
lsp_sync_text_document_did_close(srv, doc);
lsp_workspace_folders_doc_closed(doc);
}


Expand All @@ -512,6 +516,7 @@ static void destroy_all(void)
lsp_diagnostics_destroy();
lsp_semtokens_destroy();
lsp_symbols_destroy();
lsp_workspace_folders_destroy();
}


Expand All @@ -524,6 +529,7 @@ static void stop_and_init_all_servers(void)

lsp_sync_init();
lsp_diagnostics_init();
lsp_workspace_folders_init();
}


Expand Down
50 changes: 50 additions & 0 deletions lsp/src/lsp-rpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "lsp-progress.h"
#include "lsp-log.h"
#include "lsp-utils.h"
#include "lsp-workspace-folders.h"

#include <jsonrpc-glib.h>
#include <stdio.h>
Expand All @@ -47,6 +48,8 @@ struct LspRpc
};


extern GeanyData *geany_data;

GHashTable *client_table;


Expand Down Expand Up @@ -189,6 +192,53 @@ static gboolean handle_call(JsonrpcClient *client, gchar* method, GVariant *id,
g_variant_unref(edit);
return TRUE;
}
else if (g_strcmp0(method, "workspace/workspaceFolders") == 0)
{
GPtrArray *folders = lsp_workspace_folders_get();
guint num = 0;

foreach_document(num)
{
if (num > 1)
break;
}

if (num > 1) // non-single-open document variant
{
GPtrArray *arr = g_ptr_array_new_full(1, (GDestroyNotify) g_variant_unref);
GVariant *folders_variant;
gchar *folder;
guint i;

foreach_ptr_array(folder, i, folders)
{
gchar *uri = g_filename_to_uri(folder, NULL, NULL);
GVariant *folder_variant;

folder_variant = JSONRPC_MESSAGE_NEW(
"uri", JSONRPC_MESSAGE_PUT_STRING(uri),
"name", JSONRPC_MESSAGE_PUT_STRING(folder)
);
g_ptr_array_add(arr, folder_variant);

g_free(uri);
}

folders_variant = g_variant_take_ref(g_variant_new_array(G_VARIANT_TYPE_VARDICT,
(GVariant **)arr->pdata, arr->len));

reply_async(srv, method, client, id, folders_variant);

g_variant_unref(folders_variant);
g_ptr_array_free(arr, TRUE);
}
else
reply_async(srv, method, client, id, NULL);

g_ptr_array_free(folders, TRUE);

return TRUE;
}
else
{
GVariant *variant;
Expand Down
73 changes: 63 additions & 10 deletions lsp/src/lsp-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ static void free_config(LspServerConfig *cfg)
g_free(cfg->initialization_options);
g_strfreev(cfg->lang_id_mappings);
g_strfreev(cfg->command_regexes);
g_strfreev(cfg->project_root_marker_patterns);
}


Expand Down Expand Up @@ -359,6 +360,50 @@ static gboolean use_incremental_sync(GVariant *node)
}


static gboolean use_workspace_folders(GVariant *node)
{
gboolean change_notifications = FALSE;
const gchar *notif_id = NULL;
gboolean supported = FALSE;
gboolean success;

JSONRPC_MESSAGE_PARSE(node,
"capabilities", "{",
"workspace", "{",
"workspaceFolders", "{",
"supported", JSONRPC_MESSAGE_GET_BOOLEAN(&supported),
"}",
"}",
"}");

if (!supported)
return FALSE;

success = JSONRPC_MESSAGE_PARSE(node,
"capabilities", "{",
"workspace", "{",
"workspaceFolders", "{",
"changeNotifications", JSONRPC_MESSAGE_GET_BOOLEAN(&change_notifications),
"}",
"}",
"}");

if (!success) // can also be string
{
JSONRPC_MESSAGE_PARSE(node,
"capabilities", "{",
"workspace", "{",
"workspaceFolders", "{",
"changeNotifications", JSONRPC_MESSAGE_GET_STRING(&notif_id),
"}",
"}",
"}");
}

return change_notifications || notif_id;
}


static void update_config(GVariant *variant, gboolean *option, const gchar *key)
{
gboolean val = FALSE;
Expand Down Expand Up @@ -427,6 +472,7 @@ static void initialize_cb(GVariant *return_value, GError *error, gpointer user_d
update_config(return_value, &s->supports_workspace_symbols, "workspaceSymbolProvider");

s->use_incremental_sync = use_incremental_sync(return_value);
s->use_workspace_folders = use_workspace_folders(return_value);

s->initialize_response = lsp_utils_json_pretty_print(return_value);

Expand Down Expand Up @@ -752,6 +798,7 @@ static void load_config(GKeyFile *kf, const gchar *section, LspServer *s)
get_bool(&s->config.autocomplete_enable, kf, section, "autocomplete_enable");

get_strv(&s->config.autocomplete_trigger_sequences, kf, section, "autocomplete_trigger_sequences");
get_strv(&s->config.project_root_marker_patterns, kf, section, "project_root_marker_patterns");

get_int(&s->config.autocomplete_window_max_entries, kf, section, "autocomplete_window_max_entries");
get_int(&s->config.autocomplete_window_max_displayed, kf, section, "autocomplete_window_max_displayed");
Expand Down Expand Up @@ -917,10 +964,21 @@ static gboolean is_lsp_valid_for_doc(LspServerConfig *cfg, GeanyDocument *doc)
gchar *base_path, *real_path, *rel_path;
gboolean inside_project;

if (!cfg->use_without_project && !geany_data->app->project)
if (!doc || !doc->real_path)
return FALSE;

if (!doc || !doc->real_path)
if (cfg->project_root_marker_patterns)
{
gchar *project_root = lsp_utils_find_project_root(doc, cfg);
if (project_root)
{
g_free(project_root);
return TRUE;
}
g_free(project_root);
}

if (!cfg->use_without_project && !geany_data->app->project)
return FALSE;

if (cfg->use_outside_project_dir || !geany_data->app->project)
Expand Down Expand Up @@ -1002,19 +1060,14 @@ GeanyFiletype *lsp_server_get_ft(GeanyDocument *doc, gchar **lsp_lang_id)

static LspServer *server_get_for_doc(GeanyDocument *doc, gboolean launch_server)
{
LspServerConfig *cfg = lsp_server_get_config(doc);
GeanyFiletype *ft;
LspServer *srv;

if (!doc)
if (!doc || !cfg)
return NULL;

ft = lsp_server_get_ft(doc, NULL);
srv = server_get_or_start_for_ft(ft, launch_server);

if (!srv || !is_lsp_valid_for_doc(&srv->config, doc))
return NULL;

return srv;
return server_get_or_start_for_ft(ft, launch_server);
}


Expand Down
4 changes: 3 additions & 1 deletion lsp/src/lsp-server.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct LspRpc;
typedef struct LspRpc LspRpc;


typedef struct
typedef struct LspServerConfig
{
gchar *cmd;
gchar **env;
Expand All @@ -41,6 +41,7 @@ typedef struct
gboolean rpc_log_full;
gchar *initialization_options_file;
gchar *initialization_options;
gchar **project_root_marker_patterns;
gboolean enable_by_default;
gboolean use_outside_project_dir;
gboolean use_without_project;
Expand Down Expand Up @@ -137,6 +138,7 @@ typedef struct LspServer
gchar *signature_trigger_chars;
gchar *initialize_response;
gboolean use_incremental_sync;
gboolean use_workspace_folders;
gboolean supports_workspace_symbols;

guint64 semantic_token_mask;
Expand Down
Loading

0 comments on commit c98a149

Please sign in to comment.