Commit 30a986de authored by Christian Hergert's avatar Christian Hergert
Browse files

langserv: add symbol tree support to langserv symbol resolver

We still need to implement the Tree portion of this (right now it is a
flattened list), but that should be easy enough based on the container
type (for languages that support it).
parent e9412565
......@@ -72,7 +72,9 @@ libide_1_0_la_public_headers = \
langserv/ide-langserv-client.h \
langserv/ide-langserv-completion-provider.h \
langserv/ide-langserv-diagnostic-provider.h \
langserv/ide-langserv-symbol-node.h \
langserv/ide-langserv-symbol-resolver.h \
langserv/ide-langserv-symbol-tree.h \
local/ide-local-device.h \
logging/ide-log.h \
plugins/ide-extension-adapter.h \
......@@ -245,7 +247,11 @@ libide_1_0_la_public_sources = \
langserv/ide-langserv-client.c \
langserv/ide-langserv-completion-provider.c \
langserv/ide-langserv-diagnostic-provider.c \
langserv/ide-langserv-symbol-node.c \
langserv/ide-langserv-symbol-node-private.h \
langserv/ide-langserv-symbol-resolver.c \
langserv/ide-langserv-symbol-tree.c \
langserv/ide-langserv-symbol-tree-private.h \
local/ide-local-device.c \
logging/ide-log.c \
plugins/ide-extension-adapter.c \
......
/* ide-langserv-symbol-node-private.h
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IDE_LANGSERV_SYMBOL_NODE_PRIVATE_H
#define IDE_LANGSERV_SYMBOL_NODE_PRIVATE_H
#include "ide-langserv-symbol-node.h"
G_BEGIN_DECLS
IdeLangservSymbolNode *ide_langserv_symbol_node_new (GFile *file,
const gchar *name,
const gchar *parent_name,
gint kind,
guint begin_line,
guint begin_column,
guint end_line,
guint end_column);
G_END_DECLS
#endif /* IDE_LANGSERV_SYMBOL_NODE_PRIVATE_H */
/* ide-langserv-symbol-node.c
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define G_LOG_DOMAIN "ide-langserv-symbol-node"
#include "ide-debug.h"
#include "diagnostics/ide-source-location.h"
#include "files/ide-file.h"
#include "langserv/ide-langserv-symbol-node.h"
typedef struct
{
GFile *file;
gchar *parent_name;
IdeSymbolKind kind;
struct {
guint line;
guint column;
} begin, end;
} IdeLangservSymbolNodePrivate;
struct _IdeLangservSymbolNode
{
IdeSymbolNode parent_instance;
};
G_DEFINE_TYPE_WITH_PRIVATE (IdeLangservSymbolNode, ide_langserv_symbol_node, IDE_TYPE_SYMBOL_NODE)
static void
ide_langserv_symbol_node_get_location_async (IdeSymbolNode *node,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
IdeLangservSymbolNode *self = (IdeLangservSymbolNode *)node;
IdeLangservSymbolNodePrivate *priv = ide_langserv_symbol_node_get_instance_private (self);
g_autoptr(GTask) task = NULL;
g_autoptr(IdeFile) ifile = NULL;
g_autoptr(IdeSourceLocation) location = NULL;
IDE_ENTRY;
g_assert (IDE_IS_LANGSERV_SYMBOL_NODE (node));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, ide_langserv_symbol_node_get_location_async);
ifile = ide_file_new (NULL, priv->file);
location = ide_source_location_new (ifile, priv->begin.line, priv->begin.column, 0);
g_task_return_pointer (task, g_steal_pointer (&location), (GDestroyNotify)ide_source_location_unref);
IDE_EXIT;
}
static IdeSourceLocation *
ide_langserv_symbol_node_get_location_finish (IdeSymbolNode *node,
GAsyncResult *result,
GError **error)
{
IdeSourceLocation *ret;
IDE_ENTRY;
g_assert (IDE_IS_LANGSERV_SYMBOL_NODE (node));
g_assert (G_IS_TASK (result));
ret = g_task_propagate_pointer (G_TASK (result), error);
IDE_RETURN (ret);
}
static void
ide_langserv_symbol_node_finalize (GObject *object)
{
IdeLangservSymbolNode *self = (IdeLangservSymbolNode *)object;
IdeLangservSymbolNodePrivate *priv = ide_langserv_symbol_node_get_instance_private (self);
g_clear_pointer (&priv->parent_name, g_free);
g_clear_object (&priv->file);
G_OBJECT_CLASS (ide_langserv_symbol_node_parent_class)->finalize (object);
}
static void
ide_langserv_symbol_node_class_init (IdeLangservSymbolNodeClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
IdeSymbolNodeClass *symbol_node_class = IDE_SYMBOL_NODE_CLASS (klass);
object_class->finalize = ide_langserv_symbol_node_finalize;
symbol_node_class->get_location_async = ide_langserv_symbol_node_get_location_async;
symbol_node_class->get_location_finish = ide_langserv_symbol_node_get_location_finish;
}
static void
ide_langserv_symbol_node_init (IdeLangservSymbolNode *self)
{
}
IdeLangservSymbolNode *
ide_langserv_symbol_node_new (GFile *file,
const gchar *name,
const gchar *parent_name,
gint kind,
guint begin_line,
guint begin_column,
guint end_line,
guint end_column)
{
IdeLangservSymbolNode *self;
IdeLangservSymbolNodePrivate *priv;
g_return_val_if_fail (G_IS_FILE (file), NULL);
switch (kind)
{
case 1: kind = IDE_SYMBOL_FILE; break;
case 2: kind = IDE_SYMBOL_MODULE; break;
case 3: kind = IDE_SYMBOL_NAMESPACE; break;
case 4: kind = IDE_SYMBOL_PACKAGE; break;
case 5: kind = IDE_SYMBOL_CLASS; break;
case 6: kind = IDE_SYMBOL_METHOD; break;
case 7: kind = IDE_SYMBOL_PROPERTY; break;
case 8: kind = IDE_SYMBOL_FIELD; break;
case 9: kind = IDE_SYMBOL_CONSTRUCTOR; break;
case 10: kind = IDE_SYMBOL_ENUM; break;
case 11: kind = IDE_SYMBOL_INTERFACE; break;
case 12: kind = IDE_SYMBOL_FUNCTION; break;
case 13: kind = IDE_SYMBOL_VARIABLE; break;
case 14: kind = IDE_SYMBOL_CONSTANT; break;
case 15: kind = IDE_SYMBOL_STRING; break;
case 16: kind = IDE_SYMBOL_NUMBER; break;
case 17: kind = IDE_SYMBOL_BOOLEAN; break;
case 18: kind = IDE_SYMBOL_ARRAY; break;
default: kind = IDE_SYMBOL_NONE; break;
}
self = g_object_new (IDE_TYPE_LANGSERV_SYMBOL_NODE,
"flags", 0,
"kind", kind,
"name", name,
NULL);
priv = ide_langserv_symbol_node_get_instance_private (self);
priv->file = g_object_ref (file);
priv->parent_name = g_strdup (parent_name);
priv->begin.line = begin_line;
priv->begin.column = begin_column;
priv->end.line = end_line;
priv->end.column = end_column;
return self;
}
/* ide-langserv-symbol-node.h
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IDE_LANGSERV_SYMBOL_NODE_H
#define IDE_LANGSERV_SYMBOL_NODE_H
#include "symbols/ide-symbol-node.h"
G_BEGIN_DECLS
#define IDE_TYPE_LANGSERV_SYMBOL_NODE (ide_langserv_symbol_node_get_type())
G_DECLARE_FINAL_TYPE (IdeLangservSymbolNode, ide_langserv_symbol_node, IDE, LANGSERV_SYMBOL_NODE, IdeSymbolNode)
G_END_DECLS
#endif /* IDE_LANGSERV_SYMBOL_NODE_H */
......@@ -24,7 +24,11 @@
#include "diagnostics/ide-source-location.h"
#include "files/ide-file.h"
#include "langserv/ide-langserv-symbol-node.h"
#include "langserv/ide-langserv-symbol-node-private.h"
#include "langserv/ide-langserv-symbol-resolver.h"
#include "langserv/ide-langserv-symbol-tree.h"
#include "langserv/ide-langserv-symbol-tree-private.h"
typedef struct
{
......@@ -282,9 +286,172 @@ ide_langserv_symbol_resolver_lookup_symbol_finish (IdeSymbolResolver *resolver,
IDE_RETURN (ret);
}
static void
ide_langserv_symbol_resolver_document_symbol_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
IdeLangservClient *client = (IdeLangservClient *)object;
g_autoptr(GTask) task = user_data;
g_autoptr(IdeLangservSymbolTree) tree = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(JsonNode) return_value = NULL;
g_autoptr(GPtrArray) symbols = NULL;
JsonArray *array;
guint length;
IDE_ENTRY;
g_assert (IDE_IS_LANGSERV_CLIENT (client));
g_assert (G_IS_TASK (task));
if (!ide_langserv_client_call_finish (client, result, &return_value, &error))
{
g_task_return_error (task, g_steal_pointer (&error));
IDE_EXIT;
}
if (!JSON_NODE_HOLDS_ARRAY (return_value) ||
NULL == (array = json_node_get_array (return_value)))
{
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"Invalid result for textDocument/documentSymbol");
IDE_EXIT;
}
length = json_array_get_length (array);
symbols = g_ptr_array_new_with_free_func (g_object_unref);
for (guint i = 0; i < length; i++)
{
JsonNode *node = json_array_get_element (array, i);
g_autoptr(IdeLangservSymbolNode) symbol = NULL;
g_autoptr(GFile) file = NULL;
const gchar *name = NULL;
const gchar *container_name = NULL;
const gchar *uri = NULL;
gboolean success;
gint kind = -1;
struct {
gint line;
gint column;
} begin, end;
/* Mandatory fields */
success = JCON_EXTRACT (node,
"name", JCONE_STRING (name),
"kind", JCONE_INT (kind),
"location", "{",
"uri", JCONE_STRING (uri),
"range", "{",
"start", "{",
"line", JCONE_INT (begin.line),
"character", JCONE_INT (begin.column),
"}",
"end", "{",
"line", JCONE_INT (end.line),
"character", JCONE_INT (end.column),
"}",
"}",
"}"
);
if (!success)
continue;
/* Optional fields */
JCON_EXTRACT (node, "containerName", &container_name);
file = g_file_new_for_uri (uri);
symbol = ide_langserv_symbol_node_new (file, name, container_name, kind,
begin.line, begin.column,
end.line, end.column);
g_ptr_array_add (symbols, g_steal_pointer (&symbol));
}
tree = ide_langserv_symbol_tree_new (g_steal_pointer (&symbols));
g_task_return_pointer (task, g_steal_pointer (&tree), g_object_unref);
IDE_EXIT;
}
static void
ide_langserv_symbol_resolver_get_symbol_tree_async (IdeSymbolResolver *resolver,
GFile *file,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
IdeLangservSymbolResolver *self = (IdeLangservSymbolResolver *)resolver;
IdeLangservSymbolResolverPrivate *priv = ide_langserv_symbol_resolver_get_instance_private (self);
g_autoptr(GTask) task = NULL;
g_autoptr(JsonNode) params = NULL;
g_autofree gchar *uri = NULL;
IDE_ENTRY;
g_assert (IDE_IS_LANGSERV_SYMBOL_RESOLVER (self));
g_assert (G_IS_FILE (file));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, ide_langserv_symbol_resolver_get_symbol_tree_async);
if (priv->client == NULL)
{
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_NOT_CONNECTED,
"Cannot query language server, not connected");
IDE_EXIT;
}
uri = g_file_get_uri (file);
params = JCON_NEW (
"textDocument", "{",
"uri", JCON_STRING (uri),
"}"
);
ide_langserv_client_call_async (priv->client,
"textDocument/documentSymbol",
g_steal_pointer (&params),
cancellable,
ide_langserv_symbol_resolver_document_symbol_cb,
g_steal_pointer (&task));
IDE_EXIT;
}
static IdeSymbolTree *
ide_langserv_symbol_resolver_get_symbol_tree_finish (IdeSymbolResolver *resolver,
GAsyncResult *result,
GError **error)
{
IdeSymbolTree *ret;
IDE_ENTRY;
g_return_val_if_fail (IDE_IS_LANGSERV_SYMBOL_RESOLVER (resolver), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
ret = g_task_propagate_pointer (G_TASK (result), error);
IDE_RETURN (ret);
}
static void
symbol_resolver_iface_init (IdeSymbolResolverInterface *iface)
{
iface->lookup_symbol_async = ide_langserv_symbol_resolver_lookup_symbol_async;
iface->lookup_symbol_finish = ide_langserv_symbol_resolver_lookup_symbol_finish;
iface->get_symbol_tree_async = ide_langserv_symbol_resolver_get_symbol_tree_async;
iface->get_symbol_tree_finish = ide_langserv_symbol_resolver_get_symbol_tree_finish;
}
/* ide-langserv-symbol-tree-private.h
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IDE_LANGSERV_SYMBOL_TREE_PRIVATE_H
#define IDE_LANGSERV_SYMBOL_TREE_PRIVATE_H
#include "ide-langserv-symbol-tree.h"
G_BEGIN_DECLS
IdeLangservSymbolTree *ide_langserv_symbol_tree_new (GPtrArray *symbols);
G_END_DECLS
#endif /* IDE_LANGSERV_SYMBOL_TREE_PRIVATE_H */
/* ide-langserv-symbol-tree.c
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define G_LOG_DOMAIN "ide-langserv-symbol-tree"
#include "ide-langserv-symbol-tree.h"
typedef struct
{
GPtrArray *symbols;
} IdeLangservSymbolTreePrivate;
struct _IdeLangservSymbolTree
{
GObject parent_instance;
};
static void symbol_tree_iface_init (IdeSymbolTreeInterface *iface);
G_DEFINE_TYPE_WITH_CODE (IdeLangservSymbolTree, ide_langserv_symbol_tree, G_TYPE_OBJECT,
G_ADD_PRIVATE (IdeLangservSymbolTree)
G_IMPLEMENT_INTERFACE (IDE_TYPE_SYMBOL_TREE, symbol_tree_iface_init))
static guint
ide_langserv_symbol_tree_get_n_children (IdeSymbolTree *tree,
IdeSymbolNode *parent)
{
IdeLangservSymbolTree *self = (IdeLangservSymbolTree *)tree;
IdeLangservSymbolTreePrivate *priv = ide_langserv_symbol_tree_get_instance_private (self);
g_assert (IDE_IS_LANGSERV_SYMBOL_TREE (self));
g_assert (!parent || IDE_IS_SYMBOL_NODE (parent));
/* TODO: FIXME: XXX: Take symbol parent into account */
if (parent != NULL)
return 0;
return priv->symbols->len;
}
static IdeSymbolNode *
ide_langserv_symbol_tree_get_nth_child (IdeSymbolTree *tree,
IdeSymbolNode *parent,
guint nth)
{
IdeLangservSymbolTree *self = (IdeLangservSymbolTree *)tree;
IdeLangservSymbolTreePrivate *priv = ide_langserv_symbol_tree_get_instance_private (self);
g_return_val_if_fail (IDE_IS_LANGSERV_SYMBOL_TREE (self), NULL);
g_return_val_if_fail (!parent || IDE_IS_SYMBOL_NODE (parent), NULL);
g_return_val_if_fail (nth < priv->symbols->len, NULL);
return g_object_ref (g_ptr_array_index (priv->symbols, nth));
}
static void
symbol_tree_iface_init (IdeSymbolTreeInterface *iface)
{
iface->get_n_children = ide_langserv_symbol_tree_get_n_children;
iface->get_nth_child = ide_langserv_symbol_tree_get_nth_child;
}
static void
ide_langserv_symbol_tree_finalize (GObject *object)
{
IdeLangservSymbolTree *self = (IdeLangservSymbolTree *)object;
IdeLangservSymbolTreePrivate *priv = ide_langserv_symbol_tree_get_instance_private (self);
g_clear_pointer (&priv->symbols, g_ptr_array_unref);
G_OBJECT_CLASS (ide_langserv_symbol_tree_parent_class)->finalize (object);
}
static void
ide_langserv_symbol_tree_class_init (IdeLangservSymbolTreeClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = ide_langserv_symbol_tree_finalize;
}
static void
ide_langserv_symbol_tree_init (IdeLangservSymbolTree *self)
{
}
/**
* ide_langserv_symbol_tree_new:
* @symbols: (transfer container) (element-type Ide.LangservSymbolNode): The symbols
*
* Creates a new #IdeLangservSymbolTree but takes ownership of @ar.
*
* Returns: (transfer full): A newly allocated #IdeLangservSymbolTree.
*/
IdeLangservSymbolTree *
ide_langserv_symbol_tree_new (GPtrArray *symbols)
{
IdeLangservSymbolTreePrivate *priv;
IdeLangservSymbolTree *self;
g_return_val_if_fail (symbols != NULL, NULL);
self = g_object_new (IDE_TYPE_LANGSERV_SYMBOL_TREE, NULL);
priv = ide_langserv_symbol_tree_get_instance_private (self);
priv->symbols = symbols;
return self;
}
/* ide-langserv-symbol-tree.h
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*