[gnome-builder/wip/tintou/vala-subprocess: 562/562] vala: put the vala language support in another process
- From: Corentin Noël <corentinnoel src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/tintou/vala-subprocess: 562/562] vala: put the vala language support in another process
- Date: Mon, 18 Feb 2019 12:02:21 +0000 (UTC)
commit 11a8881becaf207fb77cfaf4c476e5af3088c868
Author: Corentin Noël <corentin noel collabora com>
Date: Thu Jul 19 16:57:44 2018 +0100
vala: put the vala language support in another process
build-aux/flatpak/org.gnome.Builder.json | 13 +
src/libide/code/ide-symbol.c | 2 +
src/plugins/vala-pack/ide-vala-client.vala | 455 +++++++++++++++++
src/plugins/vala-pack/ide-vala-code-indexer.vala | 150 ++----
.../vala-pack/ide-vala-completion-item.vala | 29 +-
.../vala-pack/ide-vala-completion-provider.vala | 34 +-
.../vala-pack/ide-vala-diagnostic-provider.vala | 18 +-
src/plugins/vala-pack/ide-vala-diagnostics.vala | 64 ---
src/plugins/vala-pack/ide-vala-pipeline-addin.vala | 1 -
src/plugins/vala-pack/ide-vala-service.vala | 5 +-
.../vala-pack/ide-vala-symbol-resolver.vala | 131 ++---
src/plugins/vala-pack/ide-vala-symbol-tree.vala | 144 ++----
.../vala-pack/lang-server/gnome-builder-vala.vala | 314 ++++++++++++
src/plugins/vala-pack/lang-server/ide-utils.vala | 107 ++++
.../{ => lang-server}/ide-vala-completion.vala | 53 +-
.../lang-server/ide-vala-diagnostics.vala | 100 ++++
.../vala-pack/lang-server/ide-vala-index.vala | 568 +++++++++++++++++++++
.../{ => lang-server}/ide-vala-locator.vala | 43 +-
.../{ => lang-server}/ide-vala-source-file.vala | 75 +--
.../lang-server/ide-vala-symbol-tree.vala | 181 +++++++
src/plugins/vala-pack/lang-server/meson.build | 55 ++
src/plugins/vala-pack/meson.build | 18 +-
src/plugins/vala-pack/valaconfig.vapi | 1 +
23 files changed, 2048 insertions(+), 513 deletions(-)
---
diff --git a/build-aux/flatpak/org.gnome.Builder.json b/build-aux/flatpak/org.gnome.Builder.json
index 2eee1eab1..4c9d1ceb0 100644
--- a/build-aux/flatpak/org.gnome.Builder.json
+++ b/build-aux/flatpak/org.gnome.Builder.json
@@ -63,6 +63,19 @@
"*.a"
],
"modules" : [
+ {
+ "name" : "meson",
+ "buildsystem" : "simple",
+ "build-commands": [
+ "python3 setup.py install --prefix=${FLATPAK_DEST}"
+ ],
+ "sources" : [
+ {
+ "type" : "git",
+ "url" : "https://github.com/mesonbuild/meson.git"
+ }
+ ]
+ },
"python-deps.json",
{
"name" : "gperftools",
diff --git a/src/libide/code/ide-symbol.c b/src/libide/code/ide-symbol.c
index b1dc0b407..d9e8c41f0 100644
--- a/src/libide/code/ide-symbol.c
+++ b/src/libide/code/ide-symbol.c
@@ -508,6 +508,8 @@ ide_symbol_new_from_variant (GVariant *variant)
/**
* ide_symbol_new:
+ * @location: (nullable):
+ * @header_location: (nullable):
*
* Returns: (transfer full): an #IdeSymbol
*
diff --git a/src/plugins/vala-pack/ide-vala-client.vala b/src/plugins/vala-pack/ide-vala-client.vala
new file mode 100644
index 000000000..db5ba1915
--- /dev/null
+++ b/src/plugins/vala-pack/ide-vala-client.vala
@@ -0,0 +1,455 @@
+/* ide-vala-indenter.vala
+ *
+ * Copyright 2017 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/>.
+ */
+
+using GLib;
+using Ide;
+
+
+namespace Ide
+{
+ public class ValaClient : Ide.Object {
+
+ public static unowned Ide.ValaClient from_context (Ide.Context context) {
+ context.lock ();
+ var client = (Ide.ValaClient)context.ensure_child_typed (typeof (ValaClient));
+
+ if (client == null) {
+ client = new Ide.ValaClient ();
+ context.append (client);
+ }
+ context.unlock ();
+
+ unowned Ide.ValaClient self = client;
+ return self;
+ }
+
+ public unowned string get_name () {
+ return typeof (Ide.ValaClient).name ();
+ }
+
+ private enum State {
+ INITIAL,
+ SPAWNING,
+ RUNNING,
+ SHUTDOWN
+ }
+
+ GLib.Queue<Ide.Task> get_client;
+ Ide.SubprocessSupervisor supervisor;
+ Jsonrpc.Client rpc_client;
+ GLib.File root_uri;
+ //GLib.HashTable<GLib.File, int64?> seq_by_file = null;
+ State state = State.INITIAL;
+
+ construct {
+ get_client = new GLib.Queue<Ide.Task> ();
+ unowned Ide.Context context = get_context ();
+ root_uri = context.ref_workdir ();
+
+ var launcher = new Ide.SubprocessLauncher (GLib.SubprocessFlags.STDOUT_PIPE |
GLib.SubprocessFlags.STDIN_PIPE);
+ if (root_uri.is_native ())
+ launcher.cwd = root_uri.get_path ();
+
+ launcher.clean_env = false;
+ launcher.push_argv (ValaConfig.PACKAGE_LIBEXECDIR + "/gnome-builder-vala");
+ supervisor = new Ide.SubprocessSupervisor ();
+ supervisor.set_launcher (launcher);
+ supervisor.spawned.connect (subprocess_spawned);
+ supervisor.exited.connect (subprocess_exited);
+
+ unowned Ide.BufferManager buffer_manager = Ide.BufferManager.from_context (context);
+ buffer_manager.buffer_saved.connect (buffer_saved);
+ }
+
+ ~ValaClient () {
+ state = State.SHUTDOWN;
+ if (supervisor != null) {
+ var _supervisor = supervisor;
+ supervisor = null;
+ _supervisor.stop ();
+ }
+ }
+
+ public void subprocess_exited (Ide.Subprocess object) {
+ if (state == State.RUNNING)
+ state = State.SPAWNING;
+
+ rpc_client = null;
+ /*lock (seq_by_file) {
+ seq_by_file = null;
+ }*/
+ }
+
+ public void subprocess_spawned (Ide.Subprocess subprocess) {
+ if (state == State.SPAWNING)
+ state = State.RUNNING;
+
+ unowned GLib.UnixInputStream input = subprocess.get_stdout_pipe () as
GLib.UnixInputStream;
+ unowned GLib.UnixOutputStream output = subprocess.get_stdin_pipe () as
GLib.UnixOutputStream;
+ var stream = new GLib.SimpleIOStream (input, output);
+ try {
+ GLib.Unix.set_fd_nonblocking (input.get_fd (), true);
+ } catch (Error e) {}
+
+ try {
+ GLib.Unix.set_fd_nonblocking (output.get_fd (), true);
+ } catch (Error e) {}
+
+ rpc_client = new Jsonrpc.Client (stream);
+ rpc_client.set_use_gvariant (true);
+
+ Ide.Task task = null;
+ while ((task = get_client.pop_head ()) != null) {
+ task.return_object (rpc_client);
+ }
+
+ var @params = Jsonrpc.Message.@new (
+ "rootUri", Jsonrpc.Message.PutString.create (root_uri.get_uri ()),
+ "rootPath", Jsonrpc.Message.PutString.create (root_uri.get_path ()),
+ "processId", Jsonrpc.Message.PutInt64.create (Posix.getpid ()),
+ "capabilities", "{", "}"
+ );
+
+ rpc_client.call_async.begin ("initialize", params, null);
+ }
+
+ public void buffer_saved (Ide.Buffer buffer) {
+ /*
+ * We need to clear the cached buffer on the peer (and potentially
+ * pop the translation unit cache) now that the buffer has been
+ * saved to disk and we no longer need the draft.
+ */
+
+ var gfile = buffer.file;
+ /*lock (seq_by_file) {
+ if (seq_by_file != null) {
+ seq_by_file.remove (gfile);
+ }
+ }*/
+
+ /* skip if thereis no peer */
+ if (rpc_client == null)
+ return;
+
+ if (gfile != null)
+ set_buffer_async.begin (gfile);
+ }
+
+ public async GLib.Variant? call_async (string method,
+ GLib.Variant @params,
+ GLib.Cancellable? cancellable)
+ throws GLib.Error
+ {
+ try {
+ var client = yield get_client_async (cancellable);
+ GLib.Variant reply;
+ yield client.call_async (method, params, cancellable, out reply);
+ return reply;
+ } catch (Error e) {
+ throw e;
+ }
+ }
+
+ public async GLib.Variant index_file_async (GLib.File file,
+ string[]? flags = null,
+ GLib.Cancellable? cancellable = null)
+ throws GLib.Error
+ {
+ if (!file.is_native ())
+ throw new GLib.IOError.NOT_SUPPORTED ("Only native files can be indexed");
+
+ sync_buffers ();
+ var params = Jsonrpc.Message.@new (
+ "path", Jsonrpc.Message.PutString.create (file.get_path ()),
+ "flags", Jsonrpc.Message.PutStrv.create (flags)
+ );
+
+ try {
+ return yield call_async ("vala/indexFile",
+ params,
+ cancellable);
+ } catch (Error e) {
+ throw e;
+ }
+ }
+
+ public async string get_index_key_async (GLib.File file,
+ string[]? flags = null,
+ uint line,
+ uint column,
+ GLib.Cancellable? cancellable = null)
+ throws GLib.Error
+ {
+ if (!file.is_native ())
+ throw new GLib.IOError.NOT_SUPPORTED ("Only native files are supported");
+
+ sync_buffers ();
+ var params = Jsonrpc.Message.@new (
+ "path", Jsonrpc.Message.PutString.create (file.get_path ()),
+ "flags", Jsonrpc.Message.PutStrv.create (flags),
+ "line", Jsonrpc.Message.PutInt64.create (line),
+ "column", Jsonrpc.Message.PutInt64.create (column)
+ );
+
+ try {
+ var reply = yield call_async ("vala/getIndexKey",
+ params,
+ cancellable);
+ if (!reply.is_of_type (GLib.VariantType.STRING)) {
+ throw new GLib.IOError.INVALID_DATA ("Got a result back that was not
a string");
+ }
+
+ return reply.dup_string ();
+ } catch (Error e) {
+ throw e;
+ }
+ }
+
+ public async Ide.SymbolTree get_symbol_tree_async (GLib.File file,
+ string[]? flags = null,
+ GLib.Cancellable? cancellable = null)
+ throws GLib.Error
+ {
+ if (!file.is_native ())
+ throw new GLib.IOError.NOT_SUPPORTED ("Only native files are supported");
+
+ sync_buffers ();
+ var params = Jsonrpc.Message.@new (
+ "path", Jsonrpc.Message.PutString.create (file.get_path ()),
+ "flags", Jsonrpc.Message.PutStrv.create (flags)
+ );
+
+ try {
+ var reply = yield call_async ("vala/getSymbolTree",
+ params,
+ cancellable);
+ return new ValaSymbolTree (file, reply);
+ } catch (Error e) {
+ throw e;
+ }
+ }
+
+ public async Ide.Symbol? locate_symbol_async (GLib.File file,
+ string[]? flags = null,
+ uint line,
+ uint column,
+ GLib.Cancellable? cancellable = null)
+ throws GLib.Error
+ {
+ if (!file.is_native ())
+ throw new GLib.IOError.NOT_SUPPORTED ("Only native files are supported");
+
+ sync_buffers ();
+ var params = Jsonrpc.Message.@new (
+ "path", Jsonrpc.Message.PutString.create (file.get_path ()),
+ "flags", Jsonrpc.Message.PutStrv.create (flags),
+ "line", Jsonrpc.Message.PutInt64.create (line),
+ "column", Jsonrpc.Message.PutInt64.create (column)
+ );
+
+ try {
+ var reply = yield call_async ("vala/locateSymbol",
+ params,
+ cancellable);
+ return new Ide.Symbol.from_variant (reply);
+ } catch (Error e) {
+ throw e;
+ }
+ }
+
+ public async Ide.Symbol? find_nearest_scope_async (GLib.File file,
+ string[]? flags = null,
+ uint line,
+ uint column,
+ GLib.Cancellable? cancellable = null)
+ throws GLib.Error
+ {
+ if (!file.is_native ())
+ throw new GLib.IOError.NOT_SUPPORTED ("Only native files are supported");
+
+ sync_buffers ();
+ var params = Jsonrpc.Message.@new (
+ "path", Jsonrpc.Message.PutString.create (file.get_path ()),
+ "flags", Jsonrpc.Message.PutStrv.create (flags),
+ "line", Jsonrpc.Message.PutInt64.create (line),
+ "column", Jsonrpc.Message.PutInt64.create (column)
+ );
+
+ try {
+ var reply = yield call_async ("vala/findNearestScope",
+ params,
+ cancellable);
+ return new Ide.Symbol.from_variant (reply);
+ } catch (Error e) {
+ throw e;
+ }
+ }
+
+ public async Ide.Symbol? proposals_populate_async (GLib.File file,
+ uint line,
+ uint column,
+ string? line_text,
+ GLib.Cancellable? cancellable = null)
+ throws GLib.Error
+ {
+ if (!file.is_native ())
+ throw new GLib.IOError.NOT_SUPPORTED ("Only native files are supported");
+
+ sync_buffers ();
+ var params = Jsonrpc.Message.@new (
+ "path", Jsonrpc.Message.PutString.create (file.get_path ()),
+ "line", Jsonrpc.Message.PutInt64.create (line),
+ "column", Jsonrpc.Message.PutInt64.create (column),
+ "line_text", Jsonrpc.Message.PutString.create (line_text)
+ );
+
+ try {
+ var reply = yield call_async ("vala/complete",
+ params,
+ cancellable);
+ //return new Ide.Symbol.from_variant (reply);
+ return null;
+ } catch (Error e) {
+ throw e;
+ }
+ }
+
+ private async Jsonrpc.Client get_client_async (GLib.Cancellable? cancellable) throws
GLib.Error {
+ switch (state) {
+ default:
+ state = State.SPAWNING;
+ Idle.add (() => {
+ supervisor.start ();
+ var task = new Ide.Task (this, cancellable,
(GLib.TaskReadyCallback)get_client_async.callback);
+ get_client.push_tail (task);
+ return false;
+ });
+ yield;
+ return rpc_client;
+ case State.SPAWNING:
+ Idle.add (() => {
+ var task = new Ide.Task (this, cancellable,
(GLib.TaskReadyCallback)get_client_async.callback);
+ get_client.push_tail (task);
+ return false;
+ });
+ yield;
+ return rpc_client;
+ case State.RUNNING:
+ return rpc_client;
+ case State.SHUTDOWN:
+ throw new GLib.IOError.CLOSED ("The client has been closed");
+ }
+ }
+
+ private void sync_buffers () {
+ unowned Ide.Context context = get_context ();
+ unowned Ide.UnsavedFiles unsaved_files_object = Ide.UnsavedFiles.from_context
(context);
+ var unsaved_files = unsaved_files_object.to_array ();
+ //lock (seq_by_file) {
+ /*if (seq_by_file == null)
+ seq_by_file = new GLib.HashTable<GLib.File, int64?> (GLib.File.hash,
GLib.File.equal);*/
+
+ /*
+ * We need to sync buffers to the subprocess, but only those that are of any
+ * consequence to us. So that means Vala files.
+ *
+ * Further more, to avoid the chatter, we only want to send updated buffers
+ * for unsaved files which we have not already sent or we'll be way too
+ * chatty and cancel any cached translation units the subprocess has.
+ *
+ * Since the subprocess processes commands in order, we can simply call the
+ * function to set the buffer on the peer and ignore the result (and it will
+ * be used on subsequence commands).
+ */
+ unsaved_files.foreach ((unsaved_file) => {
+ unowned GLib.File file = unsaved_file.get_file ();
+ //int64? prev = seq_by_file[file];
+ int64 seq = unsaved_file.get_sequence ();
+ /*if (prev != null && seq <= prev)
+ return;*/
+
+ string? name = file.get_basename ();
+ if (name == null || !(name.has_suffix (".vala") ||
+ name.has_suffix (".vapi")))
+ return;
+
+ //seq_by_file.insert (file, seq);
+ set_buffer_async.begin (file, unsaved_file.get_content ());
+ });
+ //}
+ }
+
+ public async bool set_buffer_async (GLib.File file, GLib.Bytes? bytes = null,
GLib.Cancellable? cancellable = null) throws GLib.Error {
+ if (!file.is_native ())
+ throw new GLib.IOError.NOT_SUPPORTED ("File must be a local file");
+
+ var dict = new VariantDict ();
+ dict.insert ("path", "s", file.get_path ());
+
+ /* data doesn't need to be utf-8, but it does have to be
+ * a valid byte string (no embedded \0 bytes).
+ */
+ if (bytes != null && bytes.get_data () != null) {
+ dict.insert ("contents", "^ay", bytes.get_data ());
+ }
+
+ try {
+ yield call_async ("vala/setBuffer", dict.end (), cancellable);
+ } catch (Error e) {
+ throw e;
+ }
+
+ return true;
+ }
+
+ public async Ide.Diagnostics diagnose_async (GLib.File file,
+ string[]? flags = null,
+ GLib.Cancellable? cancellable = null)
+ throws GLib.Error
+ {
+ if (!file.is_native ())
+ throw new GLib.IOError.NOT_SUPPORTED ("Only native files are supported");
+
+ sync_buffers ();
+ var params = Jsonrpc.Message.@new (
+ "path", Jsonrpc.Message.PutString.create (file.get_path ()),
+ "flags", Jsonrpc.Message.PutStrv.create (flags)
+ );
+
+ try {
+ var reply = yield call_async ("vala/diagnose",
+ params,
+ cancellable);
+ var iter = reply.iterator ();
+ GLib.Variant variant;
+ var ret = new Ide.Diagnostics ();
+ while ((variant = iter.next_value ()) != null) {
+ var diag = new Ide.Diagnostic.from_variant (variant);
+ if (diag != null) {
+ ret.take (diag);
+ }
+ }
+
+
+ return ret;
+ } catch (Error e) {
+ throw e;
+ }
+ }
+ }
+}
diff --git a/src/plugins/vala-pack/ide-vala-code-indexer.vala
b/src/plugins/vala-pack/ide-vala-code-indexer.vala
index 0ad3ea95b..b4d3b2cd6 100644
--- a/src/plugins/vala-pack/ide-vala-code-indexer.vala
+++ b/src/plugins/vala-pack/ide-vala-code-indexer.vala
@@ -33,26 +33,17 @@ namespace Ide
GLib.Cancellable? cancellable)
throws GLib.Error
{
- var context = this.get_context ();
- var service = Ide.ValaService.from_context (context);
- var index = service.index;
- var tree = index.get_symbol_tree_sync (file, cancellable);
-
- Ide.CodeIndexEntries? ret = null;
-
- Ide.ThreadPool.push (Ide.ThreadPoolKind.INDEXER, () => {
- index.do_locked (_ => {
- ret = new Ide.ValaCodeIndexEntries (file, tree as Ide.ValaSymbolTree);
- });
- GLib.Idle.add(index_file_async.callback);
- });
-
- yield;
-
- if (ret == null)
- throw new GLib.IOError.FAILED ("failed to build entries");
-
- return ret;
+ if (!file.is_native ())
+ throw new GLib.IOError.NOT_SUPPORTED ("Only native files are supported");
+
+ unowned Ide.Context context = this.get_context ();
+ unowned Ide.ValaClient client = Ide.ValaClient.from_context (context);
+ try {
+ var entries = yield client.index_file_async (file, build_flags, cancellable);
+ return new ValaCodeIndexEntries (file, entries);
+ } catch (Error e) {
+ throw e;
+ }
}
public async string generate_key_async (Ide.Location location,
@@ -60,18 +51,16 @@ namespace Ide
GLib.Cancellable? cancellable)
throws GLib.Error
{
- var context = this.get_context ();
- var service = Ide.ValaService.from_context (context);
- var index = service.index;
- var file = location.get_file ();
- var line = location.get_line () + 1;
- var column = location.get_line_offset () + 1;
- Vala.Symbol? symbol = yield index.find_symbol_at (file, (int)line, (int)column);
-
- if (symbol == null)
- throw new GLib.IOError.FAILED ("failed to locate symbol");
-
- return symbol.get_full_name ();
+ unowned Ide.Context context = this.get_context ();
+ unowned Ide.ValaClient client = Ide.ValaClient.from_context (context);
+ var line = location.line + 1;
+ var column = location.line_offset + 1;
+
+ try {
+ return yield client.get_index_key_async (location.file, build_flags, line,
column, cancellable);
+ } catch (Error e) {
+ throw e;
+ }
}
}
@@ -86,11 +75,36 @@ namespace Ide
return this.file;
}
- public ValaCodeIndexEntries (GLib.File file, Ide.ValaSymbolTree tree)
+ public ValaCodeIndexEntries (GLib.File file, GLib.Variant ventries)
{
- this.entries = new GLib.GenericArray<Ide.CodeIndexEntry> ();
+ entries = new GLib.GenericArray<Ide.CodeIndexEntry> ();
this.file = file;
- this.add_children (tree, null, "");
+ var iter = ventries.iterator ();
+ Ide.SymbolFlags flags;
+ Ide.SymbolKind kind;
+ string key;
+ string name;
+ uint begin_line;
+ uint begin_line_offset;
+ uint end_line;
+ uint end_line_offset;
+ while (iter.next ("(usisuuuu)",
+ out flags,
+ out key,
+ out kind,
+ out name,
+ out begin_line,
+ out begin_line_offset,
+ out end_line,
+ out end_line_offset)) {
+ var entry_builder = new Ide.CodeIndexEntryBuilder ();
+ entry_builder.set_flags (flags);
+ entry_builder.set_key (key);
+ entry_builder.set_kind (kind);
+ entry_builder.set_name (name);
+ entry_builder.set_range (begin_line, begin_line_offset, end_line,
end_line_offset);
+ entries.add (entry_builder.build ());
+ }
}
public Ide.CodeIndexEntry? get_next_entry ()
@@ -100,9 +114,9 @@ namespace Ide
return null;
}
- public async GLib.GenericArray<Ide.CodeIndexEntry> next_entries_async (GLib.Cancellable? cancellable)
+ public async GLib.GenericArray<Ide.CodeIndexEntry> next_entries_async (GLib.Cancellable?
cancellable)
throws GLib.Error
- {
+ {
var ret = new GLib.GenericArray<Ide.CodeIndexEntry> ();
for (;;)
@@ -116,67 +130,5 @@ namespace Ide
return ret;
}
-
- void add_children (Ide.ValaSymbolTree tree,
- Ide.SymbolNode? parent,
- string prefix)
- {
- var n_children = tree.get_n_children (parent);
- var builder = new Ide.CodeIndexEntryBuilder ();
-
- for (var i = 0; i < n_children; i++) {
- var child = tree.get_nth_child (parent, i) as Ide.ValaSymbolNode;
- string name = null;
-
- if (is_null_or_empty (prefix))
- name = child.name;
- else if (child.name != null && child.name[0] == '.')
- name = "%s%s".printf (prefix, child.name);
- else if (child.name != null)
- name = "%s.%s".printf (prefix, child.name);
- else
- continue;
-
- if (child.node is Vala.Symbol) {
- var node = child.node as Vala.Symbol;
- var loc = node.source_reference;
- var search_name = name;
-
- // NOTE: I don't like that we do the prefix stuff here,
- // but we don't have a good place to do it yet.
- switch (child.kind) {
- case Ide.SymbolKind.FUNCTION:
- case Ide.SymbolKind.METHOD:
- search_name = "f\x1F%s".printf (name);
- break;
-
- case Ide.SymbolKind.VARIABLE:
- case Ide.SymbolKind.CONSTANT:
- search_name = "v\x1F%s".printf (name);
- break;
-
- case Ide.SymbolKind.CLASS:
- search_name = "c\x1F%s".printf (name);
- break;
-
- default:
- search_name = "x\x1F%s".printf (name);
- break;
- }
-
- builder.set_flags (child.flags | Ide.SymbolFlags.IS_DEFINITION);
- builder.set_name (search_name);
- builder.set_key (node.get_full_name ());
- builder.set_kind (child.kind);
- builder.set_range (loc.begin.line, loc.begin.column, loc.end.line,
loc.end.column);
-
- var entry = builder.build ();
-
- this.entries.add (entry);
- }
-
- this.add_children (tree, child, name);
- }
- }
}
}
diff --git a/src/plugins/vala-pack/ide-vala-completion-item.vala
b/src/plugins/vala-pack/ide-vala-completion-item.vala
index 007d5da14..0d30e6587 100644
--- a/src/plugins/vala-pack/ide-vala-completion-item.vala
+++ b/src/plugins/vala-pack/ide-vala-completion-item.vala
@@ -18,18 +18,17 @@
using GLib;
using Gtk;
-using Vala;
namespace Ide
{
public class ValaCompletionItem : GLib.Object, Ide.CompletionProposal
{
- internal Vala.Symbol symbol;
+ //internal Vala.Symbol symbol;
internal uint priority;
- public ValaCompletionItem (Vala.Symbol symbol)
+ public ValaCompletionItem (Ide.Symbol symbol)
{
- this.symbol = symbol;
+ //this.symbol = symbol;
}
public void set_priority (uint priority)
@@ -39,12 +38,13 @@ namespace Ide
public unowned string get_name ()
{
- return this.symbol.name;
+ //return this.symbol.name;
+ return "";
}
public unowned string? get_icon_name ()
{
- if (symbol is Vala.LocalVariable)
+ /*if (symbol is Vala.LocalVariable)
return "lang-variable-symbolic";
else if (symbol is Vala.Field)
return "lang-struct-field-symbolic";
@@ -70,7 +70,7 @@ namespace Ide
else if (symbol is Vala.EnumValue)
return "lang-enum-value-symbolic";
else if (symbol is Vala.Delegate)
- return "lang-typedef-symbolic";
+ return "lang-typedef-symbolic";*/
return null;
}
@@ -83,7 +83,7 @@ namespace Ide
public string get_markup (string? typed_text)
{
- GLib.StringBuilder str = new GLib.StringBuilder ();
+ /*GLib.StringBuilder str = new GLib.StringBuilder ();
var highlight = Ide.Completion.fuzzy_highlight (this.symbol.name, typed_text != null
? typed_text : "");
@@ -125,11 +125,12 @@ namespace Ide
str.append (")</span>");
}
- return str.str;
+ return str.str;*/
+ return "";
}
public string? get_return_type () {
- if (this.symbol is Vala.Method) {
+ /*if (this.symbol is Vala.Method) {
var method = this.symbol as Vala.Method;
return esc_angle_brackets (method.return_type.to_qualified_string
(this.symbol.owner));
}
@@ -140,12 +141,12 @@ namespace Ide
if (this.symbol is Vala.Variable) {
var variable = this.symbol as Vala.Variable;
return esc_angle_brackets (variable.variable_type.to_qualified_string
(this.symbol.owner));
- }
+ }*/
return null;
}
public string? get_misc () {
- if (this.symbol is Vala.Class) {
+ /*if (this.symbol is Vala.Class) {
var klass = this.symbol as Vala.Class;
if (klass.is_abstract)
return _("Abstract");
@@ -153,7 +154,7 @@ namespace Ide
return _("Compact");
if (klass.is_immutable)
return _("Immutable");
- }
+ }*/
return null;
}
@@ -162,7 +163,7 @@ namespace Ide
var snippet = new Ide.Snippet (null, null);
var chunk = new Ide.SnippetChunk ();
- chunk.set_spec (this.symbol.name);
+ //chunk.set_spec (this.symbol.name);
snippet.add_chunk (chunk);
return snippet;
diff --git a/src/plugins/vala-pack/ide-vala-completion-provider.vala
b/src/plugins/vala-pack/ide-vala-completion-provider.vala
index 510c81841..567b358e3 100644
--- a/src/plugins/vala-pack/ide-vala-completion-provider.vala
+++ b/src/plugins/vala-pack/ide-vala-completion-provider.vala
@@ -19,17 +19,16 @@
using GLib;
using Gtk;
using Ide;
-using Vala;
namespace Ide
{
public class ValaCompletionProvider: Ide.Object, Ide.CompletionProvider
{
- Ide.ValaService? service;
+ Ide.ValaClient client;
public void load (Ide.Context context)
{
- this.service = Ide.ValaService.from_context (context);
+ client = Ide.ValaClient.from_context (context);
}
public bool is_trigger (Gtk.TextIter iter, unichar ch)
@@ -77,27 +76,7 @@ namespace Ide
var line = iter.get_line ();
var line_offset = iter.get_line_offset ();
- var index = this.service.index;
- var unsaved_files = Ide.UnsavedFiles.from_context (this.get_context ());
-
- /* make a copy for threaded access */
- var unsaved_files_copy = unsaved_files.to_array ();
-
- Ide.ThreadPool.push (Ide.ThreadPoolKind.COMPILER, () => {
- int res_line = -1;
- int res_column = -1;
- results = index.code_complete (file,
- line + 1,
- line_offset + 1,
- line_text,
- unsaved_files_copy,
- cancellable,
- out res_line,
- out res_column);
- GLib.Idle.add (this.populate_async.callback);
- });
-
- yield;
+ var proposals = yield client.proposals_populate_async (file, line + 1, line_offset +
1, line_text, cancellable);
if (cancellable.is_cancelled () || results == null)
throw new GLib.IOError.CANCELLED ("operation was cancelled");
@@ -145,12 +124,13 @@ namespace Ide
public string? get_comment (Ide.CompletionProposal proposal)
{
- var comment = (proposal as ValaCompletionItem).symbol.comment;
+ /*var comment = (proposal as ValaCompletionItem).symbol.comment;
if (comment != null && comment.content != null)
return condense (comment.content);
- return (proposal as ValaCompletionItem).symbol.get_full_name ();
+ return (proposal as ValaCompletionItem).symbol.get_full_name ();*/
+ return null;
}
public bool key_activates (Ide.CompletionProposal proposal,
@@ -252,7 +232,7 @@ namespace Ide
return this.filtered[position];
}
- public void add (Vala.Symbol symbol)
+ public void add (Ide.Symbol symbol)
{
var item = new ValaCompletionItem (symbol);
diff --git a/src/plugins/vala-pack/ide-vala-diagnostic-provider.vala
b/src/plugins/vala-pack/ide-vala-diagnostic-provider.vala
index 7771c34dd..1e32afd0a 100644
--- a/src/plugins/vala-pack/ide-vala-diagnostic-provider.vala
+++ b/src/plugins/vala-pack/ide-vala-diagnostic-provider.vala
@@ -18,7 +18,6 @@
using GLib;
using Ide;
-using Vala;
namespace Ide
{
@@ -30,11 +29,18 @@ namespace Ide
GLib.Cancellable? cancellable)
throws GLib.Error
{
- var service = Ide.ValaService.from_context (this.get_context ());
- var unsaved_files = Ide.UnsavedFiles.from_context (this.get_context ());
- yield service.index.parse_file (file, unsaved_files, cancellable);
- var results = yield service.index.get_diagnostics (file, cancellable);
- return results;
+ unowned Ide.Context context = this.get_context ();
+ unowned Ide.BuildSystem? build_system = Ide.BuildSystem.from_context (context);
+
+ string[] flags = {};
+ try {
+ flags = yield build_system.get_build_flags_async (file, cancellable);
+ } catch (GLib.Error err) {
+ warning (err.message);
+ }
+
+ unowned Ide.ValaClient client = Ide.ValaClient.from_context (context);
+ return yield client.diagnose_async (file, flags, cancellable);
}
public void load () {}
diff --git a/src/plugins/vala-pack/ide-vala-pipeline-addin.vala
b/src/plugins/vala-pack/ide-vala-pipeline-addin.vala
index 95279f675..7f1db3870 100644
--- a/src/plugins/vala-pack/ide-vala-pipeline-addin.vala
+++ b/src/plugins/vala-pack/ide-vala-pipeline-addin.vala
@@ -19,7 +19,6 @@
using GLib;
using Gtk;
using Ide;
-using Vala;
namespace Ide
{
diff --git a/src/plugins/vala-pack/ide-vala-service.vala b/src/plugins/vala-pack/ide-vala-service.vala
index 1223abf6c..eed390cd1 100644
--- a/src/plugins/vala-pack/ide-vala-service.vala
+++ b/src/plugins/vala-pack/ide-vala-service.vala
@@ -26,8 +26,9 @@ namespace Ide
{
Ide.ValaIndex _index;
- public static Ide.ValaService from_context (Ide.Context context) {
- return (Ide.ValaService)context.ensure_child_typed (typeof (ValaService));
+ public static unowned Ide.ValaService from_context (Ide.Context context) {
+ var service = (Ide.ValaService)context.ensure_child_typed (typeof (ValaService));
+ return (Ide.ValaService)context.peek_child_typed (typeof (ValaService));
}
public unowned string get_name () {
diff --git a/src/plugins/vala-pack/ide-vala-symbol-resolver.vala
b/src/plugins/vala-pack/ide-vala-symbol-resolver.vala
index 2a8febfae..0326fd075 100644
--- a/src/plugins/vala-pack/ide-vala-symbol-resolver.vala
+++ b/src/plugins/vala-pack/ide-vala-symbol-resolver.vala
@@ -18,7 +18,6 @@
using GLib;
using Ide;
-using Vala;
namespace Ide
{
@@ -29,88 +28,38 @@ namespace Ide
GLib.Cancellable? cancellable)
throws GLib.Error
{
- var context = this.get_context ();
- var service = Ide.ValaService.from_context (context);
- var index = service.index;
- var symbol_tree = yield index.get_symbol_tree (file, cancellable);
-
- return symbol_tree;
- }
-
- Ide.Symbol? create_symbol (GLib.File file, Vala.Symbol symbol)
- {
- var kind = Ide.SymbolKind.NONE;
- if (symbol is Vala.Class)
- kind = Ide.SymbolKind.CLASS;
- else if (symbol is Vala.Subroutine) {
- if (symbol.is_instance_member ())
- kind = Ide.SymbolKind.METHOD;
- else
- kind = Ide.SymbolKind.FUNCTION;
- }
- else if (symbol is Vala.Struct) kind = Ide.SymbolKind.STRUCT;
- else if (symbol is Vala.Field) kind = Ide.SymbolKind.FIELD;
- else if (symbol is Vala.Enum) kind = Ide.SymbolKind.ENUM;
- else if (symbol is Vala.EnumValue) kind = Ide.SymbolKind.ENUM_VALUE;
- else if (symbol is Vala.Variable) kind = Ide.SymbolKind.VARIABLE;
- else if (symbol is Vala.Namespace) kind = Ide.SymbolKind.NAMESPACE;
-
- var flags = Ide.SymbolFlags.NONE;
- if (symbol.is_instance_member ())
- flags |= Ide.SymbolFlags.IS_MEMBER;
-
- var binding = get_member_binding (symbol);
- if (binding != null && binding == Vala.MemberBinding.STATIC)
- flags |= Ide.SymbolFlags.IS_STATIC;
-
- if (symbol.version.deprecated)
- flags |= Ide.SymbolFlags.IS_DEPRECATED;
-
- var source_reference = symbol.source_reference;
-
- if (source_reference != null) {
- var loc = new Ide.Location (file,
- source_reference.begin.line - 1,
- source_reference.begin.column - 1);
- return new Ide.Symbol (symbol.name, kind, flags, loc, loc);
+ unowned Ide.Context context = this.get_context ();
+ unowned Ide.BuildSystem? build_system = Ide.BuildSystem.from_context (context);
+
+ string[] flags = {};
+ try {
+ flags = yield build_system.get_build_flags_async (file, cancellable);
+ } catch (GLib.Error err) {
+ warning (err.message);
}
- return null;
+ unowned Ide.ValaClient client = Ide.ValaClient.from_context (context);
+ return yield client.get_symbol_tree_async (file, flags, cancellable);
}
public async Ide.Symbol? lookup_symbol_async (Ide.Location location,
GLib.Cancellable? cancellable)
throws GLib.Error
{
- var context = this.get_context ();
- var service = Ide.ValaService.from_context (context);
- var index = service.index;
- var file = location.get_file ();
- var line = (int)location.get_line () + 1;
- var column = (int)location.get_line_offset () + 1;
-
- Vala.Symbol? symbol = yield index.find_symbol_at (file, line, column);
-
- if (symbol != null)
- return create_symbol (file, symbol);
-
- return null;
- }
+ unowned Ide.Context context = this.get_context ();
+ unowned Ide.BuildSystem? build_system = Ide.BuildSystem.from_context (context);
+ var line = (int)location.line + 1;
+ var column = (int)location.line_offset + 1;
+
+ string[] flags = {};
+ try {
+ flags = yield build_system.get_build_flags_async (location.file, cancellable);
+ } catch (GLib.Error err) {
+ warning (err.message);
+ }
- // a member binding is Instance, Class, or Static
- private Vala.MemberBinding? get_member_binding (Vala.Symbol sym)
- {
- if (sym is Vala.Constructor)
- return ((Vala.Constructor)sym).binding;
- if (sym is Vala.Destructor)
- return ((Vala.Destructor)sym).binding;
- if (sym is Vala.Field)
- return ((Vala.Field)sym).binding;
- if (sym is Vala.Method)
- return ((Vala.Method)sym).binding;
- if (sym is Vala.Property)
- return ((Vala.Property)sym).binding;
- return null;
+ unowned Ide.ValaClient client = Ide.ValaClient.from_context (context);
+ return yield client.locate_symbol_async (location.file, flags, line, column,
cancellable);
}
public void load () {}
@@ -128,30 +77,22 @@ namespace Ide
GLib.Cancellable? cancellable)
throws GLib.Error
{
- var context = this.get_context ();
- var service = Ide.ValaService.from_context (context);
- var index = service.index;
- var file = location.get_file ();
- var line = (int)location.get_line () + 1;
- var column = (int)location.get_line_offset () + 1;
-
- var symbol = yield index.find_symbol_at (file, line, column);
-
- while (symbol != null) {
- if (symbol is Vala.Class ||
- symbol is Vala.Subroutine ||
- symbol is Vala.Namespace ||
- symbol is Vala.Struct)
- break;
-
- if (symbol.owner != null)
- symbol = symbol.owner.owner;
- else
- symbol = symbol.parent_symbol;
+ unowned Ide.Context context = this.get_context ();
+ unowned Ide.BuildSystem? build_system = Ide.BuildSystem.from_context (context);
+ var line = (int)location.line + 1;
+ var column = (int)location.line_offset + 1;
+
+ string[] flags = {};
+ try {
+ flags = yield build_system.get_build_flags_async (location.file, cancellable);
+ } catch (GLib.Error err) {
+ warning (err.message);
}
+ unowned Ide.ValaClient client = Ide.ValaClient.from_context (context);
+ var symbol = yield client.locate_symbol_async (location.file, flags, line, column,
cancellable);
if (symbol != null)
- return create_symbol (file, symbol);
+ return symbol;
throw new GLib.IOError.NOT_FOUND ("Failed to locate nearest scope");
}
diff --git a/src/plugins/vala-pack/ide-vala-symbol-tree.vala b/src/plugins/vala-pack/ide-vala-symbol-tree.vala
index 9edfde2ad..5278fe27b 100644
--- a/src/plugins/vala-pack/ide-vala-symbol-tree.vala
+++ b/src/plugins/vala-pack/ide-vala-symbol-tree.vala
@@ -18,138 +18,76 @@
using GLib;
using Ide;
-using Vala;
namespace Ide
{
- public class ValaSymbolTreeVisitor: Vala.CodeVisitor
- {
- HashMap<Vala.CodeNode?,ArrayList<Vala.CodeNode>> table;
- GLib.Queue<ArrayList<Vala.CodeNode>> queue;
-
- public ValaSymbolTreeVisitor ()
- {
- this.table = new HashMap<Vala.CodeNode?,ArrayList<Vala.CodeNode>> ();
- this.queue = new GLib.Queue<ArrayList<Vala.CodeNode>> ();
-
- var root = new ArrayList<Vala.CodeNode> ();
- this.table [null] = root;
- this.queue.push_head (root);
- }
-
- public Ide.SymbolTree? build_tree ()
- {
- return new Ide.ValaSymbolTree (this.table);
- }
-
- void visit_generic (Vala.CodeNode node)
- {
- var current = this.queue.peek_head ();
- current.add (node);
-
- var list = new ArrayList<Vala.CodeNode> ();
- this.queue.push_head (list);
-
- this.table [node] = list;
-
- node.accept_children (this);
-
- this.queue.pop_head ();
- }
-
- public override void visit_class (Vala.Class node) { this.visit_generic (node); }
- public override void visit_method (Vala.Method node) { this.visit_generic (node); }
- public override void visit_local_variable (Vala.LocalVariable node) { this.visit_generic
(node); }
- public override void visit_interface (Vala.Interface node) { this.visit_generic (node); }
- public override void visit_struct (Vala.Struct node) { this.visit_generic (node); }
- public override void visit_creation_method (Vala.CreationMethod node) { this.visit_generic
(node); }
- public override void visit_property (Vala.Property node) { this.visit_generic (node); }
- public override void visit_property_accessor (Vala.PropertyAccessor node) {
this.visit_generic (node); }
- public override void visit_constructor (Vala.Constructor node) { this.visit_generic (node); }
- public override void visit_destructor (Vala.Destructor node) { this.visit_generic (node); }
-
- public override void visit_block (Vala.Block node) { node.accept_children (this); }
- public override void visit_source_file (Vala.SourceFile source_file) {
source_file.accept_children (this); }
- }
-
public class ValaSymbolTree : GLib.Object, Ide.SymbolTree
{
- HashMap<Vala.CodeNode?,ArrayList<Vala.CodeNode>> table;
+ public GLib.File file { get; construct; }
+ public GLib.Variant tree { get; construct; }
- public ValaSymbolTree (HashMap<Vala.CodeNode?,ArrayList<Vala.CodeNode>> table)
+ public ValaSymbolTree (GLib.File file, GLib.Variant tree)
{
- this.table = table;
-
- debug ("Tree created with %u rows", table.size);
- }
-
- ArrayList<Vala.CodeNode>? find (Ide.SymbolNode? node)
- {
- Ide.ValaSymbolNode? symbol_node = (Ide.ValaSymbolNode)node;
- Vala.CodeNode? key = null;
-
- if (symbol_node != null) {
- if (!this.table.contains (symbol_node.node))
- return null;
- key = symbol_node.node;
- }
-
- return this.table [key];
+ GLib.Object (file: file, tree: tree);
}
public uint get_n_children (Ide.SymbolNode? node)
{
- var list = find (node);
-
- if (list == null)
- debug ("Failed to find child! %p", node);
- else
- debug ("node has %u children.", list.size);
+ if (node != null)
+ return (node as ValaSymbolNode).n_children;
- return list != null ? list.size : 0;
+ return (uint)tree.n_children ();
}
public Ide.SymbolNode? get_nth_child (Ide.SymbolNode? node, uint nth)
{
- var list = find (node);
+ if (node != null)
+ return (node as ValaSymbolNode).get_nth_child (nth);
- if (list != null && list.size > nth)
- return new Ide.ValaSymbolNode (list [(int)nth]);
-
- return null;
+ var child_val = tree.get_child_value (nth);
+ return new ValaSymbolNode (child_val);
}
}
public class ValaSymbolNode : Ide.SymbolNode
{
- public Vala.CodeNode? node;
+ public GLib.Variant children { get; construct; }
+ public Ide.Symbol symbol { get; construct; }
+
+ public uint n_children
+ {
+ get {
+ return (uint)children.n_children ();
+ }
+ }
- public ValaSymbolNode (Vala.CodeNode node)
+ public ValaSymbolNode (GLib.Variant node)
{
- this.node = node;
+ var _symbol = new Ide.Symbol.from_variant (node);
+
+ var tmp_children = node.lookup_value ("children", null);
+ if (tmp_children.is_of_type (GLib.VariantType.VARIANT)) {
+ tmp_children = tmp_children.get_variant ();
+ } else if (!tmp_children.is_of_type (new GLib.VariantType ("aa{sv}")) &&
+ !tmp_children.is_of_type (new GLib.VariantType ("aa{sv}"))) {
+ tmp_children = null;
+ }
- this.name = (node as Vala.Symbol).name;
- this.kind = Ide.SymbolKind.NONE;
- this.flags = Ide.SymbolFlags.NONE;
+ GLib.Object (children: tmp_children,
+ symbol: _symbol,
+ kind: _symbol.get_kind (),
+ flags: _symbol.get_flags (),
+ name: _symbol.get_name ());
+ }
- if (node is Vala.Method)
- this.kind = Ide.SymbolKind.FUNCTION;
- else if (node is Vala.Class)
- this.kind = Ide.SymbolKind.CLASS;
- else if (node is Vala.Struct)
- this.kind = Ide.SymbolKind.STRUCT;
- else if (node is Vala.Property)
- this.kind = Ide.SymbolKind.FIELD;
+ construct {
+
}
- public override async Ide.Location? get_location_async (GLib.Cancellable? cancellable)
+ public ValaSymbolNode get_nth_child (uint nth)
{
- var source_reference = this.node.source_reference;
- var file = (source_reference.file as Ide.ValaSourceFile).file;
-
- return new Ide.Location (file,
- source_reference.begin.line - 1,
- source_reference.begin.column - 1);
+ var child_val = children.get_child_value (nth);
+ return new ValaSymbolNode (child_val);
}
}
}
diff --git a/src/plugins/vala-pack/lang-server/gnome-builder-vala.vala
b/src/plugins/vala-pack/lang-server/gnome-builder-vala.vala
new file mode 100644
index 000000000..b47884950
--- /dev/null
+++ b/src/plugins/vala-pack/lang-server/gnome-builder-vala.vala
@@ -0,0 +1,314 @@
+/* ide-vala-indenter.vala
+ *
+ * Copyright 2017 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/>.
+ */
+
+using Ide;
+
+namespace Ide
+{
+ public class ValaServer : GLib.Object {
+ public bool is_in_flight
+ {
+ get {
+ return GLib.AtomicInt.@get (ref in_flight) != 0;
+ }
+ }
+
+ private int in_flight;
+ private Ide.ValaIndex? index = null;
+
+ construct
+ {
+ //
+ }
+
+ public void initialize (Jsonrpc.Client client,
+ GLib.Variant id,
+ GLib.Variant @params)
+ {
+ unowned string uri = null;
+ if (Jsonrpc.Message.parse (params, "rootUri", Jsonrpc.Message.GetString.create (ref
uri))) {
+ index = new Ide.ValaIndex (GLib.File.new_for_uri (uri));
+ }
+
+ reply_to_client.begin (client, id);
+ }
+
+ public void get_index_key (Jsonrpc.Client client,
+ GLib.Variant id,
+ GLib.Variant @params)
+ {
+ unowned string path = null;
+ string[] flags = {};
+ int64 line = 0;
+ int64 column = 0;
+
+ if (Jsonrpc.Message.parse (params,
+ "path", Jsonrpc.Message.GetString.create (ref path),
+ "flags", Jsonrpc.Message.GetStrv.create (ref flags),
+ "line", Jsonrpc.Message.GetInt64.create (ref line),
+ "column", Jsonrpc.Message.GetInt64.create (ref column)))
+ {
+ flags.length = (int) GLib.strv_length (flags);
+ reply_to_client.begin (client, id);
+ } else {
+ reply_error_to_client.begin (client, id);
+ }
+ }
+
+ public void diagnose (Jsonrpc.Client client,
+ GLib.Variant id,
+ GLib.Variant @params)
+ {
+ unowned string path = null;
+ if (!Jsonrpc.Message.parse (params, "path", Jsonrpc.Message.GetString.create (ref
path))) {
+ reply_error_to_client.begin (client, id);
+ return;
+ }
+
+ string[] flags = {};
+ Jsonrpc.Message.parse (params, "flags", Jsonrpc.Message.GetStrv.create (ref flags));
+ flags.length = (int) GLib.strv_length (flags);
+
+ var diagnostics = index.get_file_diagnostics (path, flags);
+ var builder = new VariantBuilder (new VariantType ("aa{sv}") );
+ var size = diagnostics.get_n_items ();
+ for (int i = 0; i < size; i++) {
+ Ide.Diagnostic diag = diagnostics.get_item (i) as Ide.Diagnostic;
+ if (diag != null) {
+ builder.add_value (diag.to_variant ());
+ }
+ }
+
+ reply_to_client.begin (client, id, builder.end ());
+ }
+
+ public void set_buffer (Jsonrpc.Client client,
+ GLib.Variant id,
+ GLib.Variant @params)
+ {
+ unowned string path = null;
+ if (!Jsonrpc.Message.parse (params, "path", Jsonrpc.Message.GetString.create (ref
path))) {
+ reply_error_to_client.begin (client, id);
+ return;
+ }
+
+ unowned string content = null;
+ params.lookup ("contents", "^&ay", ref content);
+ GLib.Bytes? bytes = null;
+ if (content != null)
+ bytes = new GLib.Bytes.take (content.data);
+
+ index.set_unsaved_file (path, bytes);
+ reply_to_client.begin (client, id, new GLib.Variant.boolean (true));
+ }
+
+ public void get_symbol_tree (Jsonrpc.Client client,
+ GLib.Variant id,
+ GLib.Variant @params)
+ {
+ unowned string path = null;
+ if (!Jsonrpc.Message.parse (params, "path", Jsonrpc.Message.GetString.create (ref
path))) {
+ reply_error_to_client.begin (client, id);
+ return;
+ }
+
+ string[] flags = {};
+ Jsonrpc.Message.parse (params, "flags", Jsonrpc.Message.GetStrv.create (ref flags));
+ flags.length = (int) GLib.strv_length (flags);
+
+ var symbol_tree = index.get_symbol_tree (path, flags);
+ reply_to_client.begin (client, id, symbol_tree);
+ }
+
+ public void locate_symbol (Jsonrpc.Client client,
+ GLib.Variant id,
+ GLib.Variant @params)
+ {
+ unowned string path = null;
+ string[] flags = {};
+ int64 line = 0;
+ int64 column = 0;
+
+ if (Jsonrpc.Message.parse (params,
+ "path", Jsonrpc.Message.GetString.create (ref path),
+ "flags", Jsonrpc.Message.GetStrv.create (ref flags),
+ "line", Jsonrpc.Message.GetInt64.create (ref line),
+ "column", Jsonrpc.Message.GetInt64.create (ref column)))
+ {
+ flags.length = (int) GLib.strv_length (flags);
+ var symbol = index.locate_symbol (path, flags, (uint)line, (uint)column);
+ Variant? symbol_variant = null;
+ if (symbol != null)
+ symbol_variant = symbol.to_variant ();
+
+ reply_to_client.begin (client, id, symbol_variant);
+ } else {
+ reply_error_to_client.begin (client, id);
+ }
+ }
+
+ public void find_nearest_scope (Jsonrpc.Client client,
+ GLib.Variant id,
+ GLib.Variant @params)
+ {
+ unowned string path = null;
+ string[] flags = {};
+ int64 line = 0;
+ int64 column = 0;
+
+ if (Jsonrpc.Message.parse (params,
+ "path", Jsonrpc.Message.GetString.create (ref path),
+ "flags", Jsonrpc.Message.GetStrv.create (ref flags),
+ "line", Jsonrpc.Message.GetInt64.create (ref line),
+ "column", Jsonrpc.Message.GetInt64.create (ref column)))
+ {
+ flags.length = (int) GLib.strv_length (flags);
+ var symbol = index.find_nearest_scope (path, flags, (uint)line, (uint)column);
+ Variant? symbol_variant = null;
+ if (symbol != null)
+ symbol_variant = symbol.to_variant ();
+
+ reply_to_client.begin (client, id, symbol_variant);
+ } else {
+ reply_error_to_client.begin (client, id);
+ }
+ }
+
+ public void complete (Jsonrpc.Client client,
+ GLib.Variant id,
+ GLib.Variant @params)
+ {
+ unowned string path = null;
+ unowned string? line_text = null;
+ int64 line = 0;
+ int64 column = 0;
+
+ if (Jsonrpc.Message.parse (params,
+ "path", Jsonrpc.Message.GetString.create (ref path),
+ "line", Jsonrpc.Message.GetInt64.create (ref line),
+ "column", Jsonrpc.Message.GetInt64.create (ref column),
+ "line_text", Jsonrpc.Message.GetString.create (ref line_text)))
+ {
+ var results = index.code_complete (path, (uint)line, (uint)column, line_text);
+ reply_to_client.begin (client, id, results);
+ } else {
+ reply_error_to_client.begin (client, id);
+ }
+ }
+
+ public void index_file (Jsonrpc.Client client,
+ GLib.Variant id,
+ GLib.Variant @params)
+ {
+ unowned string path = null;
+ if (!Jsonrpc.Message.parse (params, "path", Jsonrpc.Message.GetString.create (ref
path))) {
+ reply_error_to_client.begin (client, id);
+ return;
+ }
+
+ string[] flags = {};
+ Jsonrpc.Message.parse (params, "flags", Jsonrpc.Message.GetStrv.create (ref flags));
+ flags.length = (int) GLib.strv_length (flags);
+
+ var index_entries = index.get_index_entries (path, flags);
+ reply_to_client.begin (client, id, index_entries);
+ }
+
+ private async void reply_to_client (Jsonrpc.Client client,
+ GLib.Variant id,
+ GLib.Variant? reply = null)
+ {
+ GLib.AtomicInt.inc (ref in_flight);
+ try {
+ yield client.reply_async (id, reply, null);
+ } catch (Error e) {
+ warning ("Reply failed: %s", e.message);
+ }
+
+ if (GLib.AtomicInt.dec_and_test (ref in_flight))
+ notify_property ("is-in-flight");
+ }
+
+ private async void reply_error_to_client (Jsonrpc.Client client,
+ GLib.Variant id,
+ int code = Jsonrpc.ClientError.INVALID_PARAMS,
+ string? message = "Invalid parameters for method
call")
+ {
+ GLib.AtomicInt.inc (ref in_flight);
+ try {
+ yield client.reply_error_async (id, code, message, null);
+ } catch (Error e) {
+ warning ("Reply failed: %s", e.message);
+ }
+
+ if (GLib.AtomicInt.dec_and_test (ref in_flight))
+ notify_property ("is-in-flight");
+ }
+ }
+
+ int main (string[] args)
+ {
+ var input = new GLib.UnixInputStream (Posix.STDIN_FILENO, false);
+ var output = new GLib.UnixOutputStream (Posix.STDOUT_FILENO, false);
+ var stream = new GLib.SimpleIOStream (input, output);
+
+ /* Only write to stderr so that we don't interrupt IPC */
+ GLib.Log.set_handler (null, GLib.LogLevelFlags.LEVEL_MASK, (log_domain, log_levels, message)
=> {
+ GLib.printerr ("%s: %s\n", log_domain, message);
+ });
+
+ var vala = new Ide.ValaServer ();
+ try {
+ GLib.Unix.set_fd_nonblocking (Posix.STDIN_FILENO, true);
+ GLib.Unix.set_fd_nonblocking (Posix.STDOUT_FILENO, true);
+ } catch (Error e) {
+ GLib.printerr ("Failed to set FD non-blocking: %s\n", e.message);
+ return Posix.EXIT_FAILURE;
+ }
+
+ var main_loop = new GLib.MainLoop (null, false);
+ var server = new Jsonrpc.Server ();
+ bool closing = false;
+ server.client_closed.connect (() => {
+ closing = true;
+ if (!vala.is_in_flight)
+ main_loop.quit ();
+ });
+
+ vala.notify["is-in-flight"].connect (() => {
+ if (closing && !vala.is_in_flight) {
+ main_loop.quit ();
+ }
+ });
+
+ server.add_handler ("initialize", (self, client, method, id, @params) => vala.initialize
(client, id, params));
+ server.add_handler ("vala/getIndexKey", (self, client, method, id, @params) =>
vala.get_index_key (client, id, params));
+ server.add_handler ("vala/diagnose", (self, client, method, id, @params) => vala.diagnose
(client, id, params));
+ server.add_handler ("vala/setBuffer", (self, client, method, id, @params) => vala.set_buffer
(client, id, params));
+ server.add_handler ("vala/getSymbolTree", (self, client, method, id, @params) =>
vala.get_symbol_tree (client, id, params));
+ server.add_handler ("vala/locateSymbol", (self, client, method, id, @params) =>
vala.locate_symbol (client, id, params));
+ server.add_handler ("vala/findNearestScope", (self, client, method, id, @params) =>
vala.find_nearest_scope (client, id, params));
+ server.add_handler ("vala/complete", (self, client, method, id, @params) => vala.complete
(client, id, params));
+ server.add_handler ("vala/indexFile", (self, client, method, id, @params) => vala.index_file
(client, id, params));
+
+ server.accept_io_stream (stream);
+
+ main_loop.run ();
+ return Posix.EXIT_SUCCESS;
+ }
+}
diff --git a/src/plugins/vala-pack/lang-server/ide-utils.vala
b/src/plugins/vala-pack/lang-server/ide-utils.vala
new file mode 100644
index 000000000..d555f5039
--- /dev/null
+++ b/src/plugins/vala-pack/lang-server/ide-utils.vala
@@ -0,0 +1,107 @@
+namespace Ide {
+ public static Ide.Symbol? vala_to_ide_symbol (Vala.CodeNode node)
+ {
+ Vala.Symbol? symbol = vala_symbol_from_code_node (node);
+ Ide.SymbolKind kind = vala_symbol_kind_from_code_node (node);
+ Ide.SymbolFlags flags = vala_symbol_flags_from_code_node (node);
+ string name = vala_symbol_name (symbol);
+
+ var source_reference = node.source_reference;
+ if (source_reference != null) {
+ var file = GLib.File.new_for_path (source_reference.file.filename);
+ var loc = new Ide.Location (file,
+ source_reference.begin.line - 1,
+ source_reference.begin.column - 1);
+
+ return new Ide.Symbol (name, kind, flags, loc, loc);
+ }
+
+ return new Ide.Symbol (name, kind, flags, null, null);
+ }
+
+ public static Ide.SymbolKind vala_symbol_kind_from_code_node (Vala.CodeNode node)
+ {
+ if (node is Vala.Class)
+ return Ide.SymbolKind.CLASS;
+ else if (node is Vala.Subroutine) {
+ Vala.Symbol? symbol = vala_symbol_from_code_node (node);
+ if (symbol.is_instance_member ())
+ if (node is Vala.CreationMethod || node is Vala.Constructor) {
+ return Ide.SymbolKind.CONSTRUCTOR;
+ } else {
+ return Ide.SymbolKind.METHOD;
+ }
+ else
+ return Ide.SymbolKind.FUNCTION;
+ }
+ else if (node is Vala.Struct) return Ide.SymbolKind.STRUCT;
+ else if (node is Vala.Field) return Ide.SymbolKind.FIELD;
+ else if (node is Vala.Property) return Ide.SymbolKind.PROPERTY;
+ else if (node is Vala.Enum) return Ide.SymbolKind.ENUM;
+ else if (node is Vala.EnumValue) return Ide.SymbolKind.ENUM_VALUE;
+ else if (node is Vala.Variable) return Ide.SymbolKind.VARIABLE;
+ else if (node is Vala.Namespace) return Ide.SymbolKind.NAMESPACE;
+ else if (node is Vala.Delegate) return Ide.SymbolKind.TEMPLATE;
+ else if (node is Vala.Signal) return Ide.SymbolKind.UI_SIGNAL;
+
+ return Ide.SymbolKind.NONE;
+ }
+
+ public static unowned string vala_symbol_name (Vala.Symbol symbol)
+ {
+ unowned string name = symbol.name;
+ if (symbol is Vala.CreationMethod) {
+ name = (symbol as Vala.CreationMethod).class_name;
+ }
+
+ if (name == null) {
+ critical ("HERE");
+ critical ("%s (%s)", symbol.type_name, symbol.get_full_name ());
+ critical (symbol.to_string ());
+ critical ("~~~~~~~~~~");
+ }
+
+ return name;
+ }
+
+ public static Ide.SymbolFlags vala_symbol_flags_from_code_node (Vala.CodeNode node)
+ {
+ Vala.Symbol? symbol = vala_symbol_from_code_node (node);
+ var flags = Ide.SymbolFlags.NONE;
+ if (symbol.is_instance_member ())
+ flags |= Ide.SymbolFlags.IS_MEMBER;
+
+ var binding = get_member_binding (node);
+ if (binding != null && binding == Vala.MemberBinding.STATIC)
+ flags |= Ide.SymbolFlags.IS_STATIC;
+
+ if (symbol.version.deprecated)
+ flags |= Ide.SymbolFlags.IS_DEPRECATED;
+
+ return flags;
+ }
+
+ public static Vala.Symbol? vala_symbol_from_code_node (Vala.CodeNode node)
+ {
+ if (node is Vala.Expression)
+ return (node as Vala.Expression).symbol_reference;
+ else
+ return (node as Vala.Symbol);
+ }
+
+ // a member binding is Instance, Class, or Static
+ public static Vala.MemberBinding? get_member_binding (Vala.CodeNode sym)
+ {
+ if (sym is Vala.Constructor)
+ return ((Vala.Constructor)sym).binding;
+ if (sym is Vala.Destructor)
+ return ((Vala.Destructor)sym).binding;
+ if (sym is Vala.Field)
+ return ((Vala.Field)sym).binding;
+ if (sym is Vala.Method)
+ return ((Vala.Method)sym).binding;
+ if (sym is Vala.Property)
+ return ((Vala.Property)sym).binding;
+ return null;
+ }
+}
diff --git a/src/plugins/vala-pack/ide-vala-completion.vala
b/src/plugins/vala-pack/lang-server/ide-vala-completion.vala
similarity index 70%
rename from src/plugins/vala-pack/ide-vala-completion.vala
rename to src/plugins/vala-pack/lang-server/ide-vala-completion.vala
index 97aa7a40b..d8312b139 100644
--- a/src/plugins/vala-pack/ide-vala-completion.vala
+++ b/src/plugins/vala-pack/lang-server/ide-vala-completion.vala
@@ -37,10 +37,10 @@ namespace Ide
static construct {
try {
- member_access = new Regex("""((?:\w+(?:\s*\([^()]*\))?\.)*)(\w*)$""");
+ member_access = new Regex ("""((?:\w+(?:\s*\([^()]*\))?\.)*)(\w*)$""");
member_access_split = new Regex ("""(\s*\([^()]*\))?\.""");
} catch (RegexError err) {
- critical("Regular expressions failed to compile : %s", err.message);
+ critical ("Regular expressions failed to compile : %s", err.message);
}
}
@@ -55,64 +55,65 @@ namespace Ide
this.nearest = nearest;
}
- public GLib.List<Vala.Symbol>? run (ref Vala.SourceLocation start_pos)
+ public Vala.ArrayList<Vala.Symbol>? run (ref Vala.SourceLocation start_pos)
{
MatchInfo match_info;
if (!member_access.match (current_text, 0, out match_info))
return null;
+ string fetch_2 = match_info.fetch (2);
start_pos.line = this.location.line;
- start_pos.column = this.location.column - (int)match_info.fetch (2).length;
+ start_pos.column = this.location.column - (int)fetch_2.length;
var names = member_access_split.split (match_info.fetch (1));
var syms = lookup_symbol (construct_member_access (names),
- match_info.fetch (2),
+ fetch_2,
nearest);
return syms;
}
- GLib.List<Vala.Symbol> lookup_symbol (Vala.Expression? inner, string name, Vala.Block? block)
+ Vala.ArrayList<Vala.Symbol> lookup_symbol (Vala.Expression? inner, string name, Vala.Block?
block)
{
- var matching_symbols = new GLib.List<Vala.Symbol> ();
+ var matching_symbols = new Vala.ArrayList<Vala.Symbol> ();
if (block == null)
return matching_symbols;
if (inner == null) {
for (var sym = (Vala.Symbol) block; sym != null; sym = sym.parent_symbol) {
- matching_symbols.concat (symbol_lookup_inherited (sym));
+ matching_symbols.add_all (symbol_lookup_inherited (sym));
}
foreach (var ns in block.source_reference.file.current_using_directives) {
- matching_symbols.concat (symbol_lookup_inherited
(ns.namespace_symbol));
+ matching_symbols.add_all (symbol_lookup_inherited
(ns.namespace_symbol));
}
} else if (inner.symbol_reference != null) {
- matching_symbols.concat (symbol_lookup_inherited
(inner.symbol_reference));
+ matching_symbols.add_all (symbol_lookup_inherited
(inner.symbol_reference));
} else if (inner is Vala.MemberAccess) {
var inner_ma = (Vala.MemberAccess) inner;
var matching = lookup_symbol (inner_ma.inner, inner_ma.member_name, block);
- if (matching != null)
- matching_symbols.concat (symbol_lookup_inherited (matching.data));
+ if (!matching.is_empty)
+ matching_symbols.add_all (symbol_lookup_inherited (matching.first
()));
} else if (inner is Vala.MethodCall) {
var inner_inv = (Vala.MethodCall) inner;
var inner_ma = inner_inv.call as Vala.MemberAccess;
if (inner_ma != null) {
var matching = lookup_symbol (inner_ma.inner, inner_ma.member_name,
block);
- if (matching != null)
- matching_symbols.concat (symbol_lookup_inherited
(matching.data, true));
+ if (!matching.is_empty)
+ matching_symbols.add_all (symbol_lookup_inherited
(matching.first (), true));
}
}
return matching_symbols;
}
- GLib.List<Vala.Symbol> symbol_lookup_inherited (Vala.Symbol? sym,
+ Vala.ArrayList<Vala.Symbol> symbol_lookup_inherited (Vala.Symbol? sym,
bool invocation = false)
{
- GLib.List<Vala.Symbol> result = null;
+ var result = new Vala.ArrayList<Vala.Symbol> ();
// This may happen if we cannot find all the needed packages
if (sym == null)
@@ -121,39 +122,37 @@ namespace Ide
var symbol_table = sym.scope.get_symbol_table ();
if (symbol_table != null) {
- foreach (string key in symbol_table.get_keys()) {
- result.append (symbol_table[key]);
- }
+ result.add_all (symbol_table.get_values ());
}
if (invocation && sym is Vala.Method) {
var func = (Vala.Method) sym;
- result.concat (symbol_lookup_inherited (func.return_type.data_type));
+ result.add_all (symbol_lookup_inherited (func.return_type.data_type));
} else if (sym is Vala.Class) {
var cl = (Vala.Class) sym;
foreach (var base_type in cl.get_base_types ()) {
- result.concat (symbol_lookup_inherited (base_type.data_type));
+ result.add_all (symbol_lookup_inherited (base_type.data_type));
}
} else if (sym is Vala.Struct) {
var st = (Vala.Struct) sym;
- result.concat (symbol_lookup_inherited (st.base_type.data_type));
+ result.add_all (symbol_lookup_inherited (st.base_type.data_type));
} else if (sym is Vala.Interface) {
var iface = (Vala.Interface) sym;
foreach (var prerequisite in iface.get_prerequisites ()) {
- result.concat (symbol_lookup_inherited (prerequisite.data_type));
+ result.add_all (symbol_lookup_inherited (prerequisite.data_type));
}
} else if (sym is Vala.LocalVariable) {
var variable = (Vala.LocalVariable) sym;
- result.concat (symbol_lookup_inherited (variable.variable_type.data_type));
+ result.add_all (symbol_lookup_inherited (variable.variable_type.data_type));
} else if (sym is Vala.Field) {
var field = (Vala.Field) sym;
- result.concat (symbol_lookup_inherited (field.variable_type.data_type));
+ result.add_all (symbol_lookup_inherited (field.variable_type.data_type));
} else if (sym is Vala.Property) {
var prop = (Vala.Property) sym;
- result.concat (symbol_lookup_inherited (prop.property_type.data_type));
+ result.add_all (symbol_lookup_inherited (prop.property_type.data_type));
} else if (sym is Vala.Parameter) {
var fp = (Vala.Parameter) sym;
- result.concat (symbol_lookup_inherited (fp.variable_type.data_type));
+ result.add_all (symbol_lookup_inherited (fp.variable_type.data_type));
}
return result;
diff --git a/src/plugins/vala-pack/lang-server/ide-vala-diagnostics.vala
b/src/plugins/vala-pack/lang-server/ide-vala-diagnostics.vala
new file mode 100644
index 000000000..ba3debc37
--- /dev/null
+++ b/src/plugins/vala-pack/lang-server/ide-vala-diagnostics.vala
@@ -0,0 +1,100 @@
+/* ide-vala-diagnostics.vala
+ *
+ * Copyright 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ */
+
+using Vala;
+
+namespace Ide
+{
+ public class ValaDiagnostics: Vala.Report
+ {
+ private GLib.HashTable<string, Vala.ArrayList<Ide.Diagnostic>> diagnosed_files;
+
+ public void clear ()
+ {
+ this.errors = 0;
+ this.warnings = 0;
+ if (diagnosed_files != null)
+ diagnosed_files.remove_all ();
+
+ diagnosed_files = new GLib.HashTable<string, Vala.ArrayList<Ide.Diagnostic>>
(str_hash, str_equal);
+ }
+
+ private void add (Vala.SourceReference? source_reference,
+ string message,
+ Ide.DiagnosticSeverity severity)
+ {
+ if (source_reference == null)
+ return;
+
+ unowned string filename = source_reference.file.filename;
+ GLib.File file;
+ unowned Vala.ArrayList<Ide.Diagnostic> diagnosed_file = diagnosed_files[filename];
+ if (diagnosed_file != null)
+ file = diagnosed_file[0].get_location ().get_file ();
+ else {
+ file = GLib.File.new_for_path (filename);
+ var list = new Vala.ArrayList<Ide.Diagnostic> ();
+ diagnosed_files[filename] = list;
+ diagnosed_file = list;
+ }
+
+ var begin = new Ide.Location (file,
+ source_reference.begin.line - 1,
+ source_reference.begin.column - 1);
+ var end = new Ide.Location (file,
+ source_reference.end.line - 1,
+ source_reference.end.column - 1);
+
+ var diag = new Ide.Diagnostic (severity, message, begin);
+ diag.take_range (new Ide.Range (begin, end));
+ diagnosed_file.add (diag);
+ }
+
+ public Ide.Diagnostics get_diagnostic_from_file (string filename)
+ {
+ unowned Vala.ArrayList<Ide.Diagnostic> diagnosed_file = diagnosed_files[filename];
+ var ar = new GLib.GenericArray<Ide.Diagnostic> (diagnosed_file != null ?
diagnosed_file.size : 0);
+ if (diagnosed_file != null) {
+ foreach (var diag in diagnosed_file) {
+ ar.add (diag);
+ }
+ }
+
+ return new Ide.Diagnostics.from_array (ar);
+ }
+
+ public override void note (Vala.SourceReference? source_reference, string message) {
+ add (source_reference, message, Ide.DiagnosticSeverity.NOTE);
+ }
+
+ public override void depr (Vala.SourceReference? source_reference, string message) {
+ add (source_reference, message, Ide.DiagnosticSeverity.DEPRECATED);
+ ++warnings;
+ }
+
+ public override void warn (Vala.SourceReference? source_reference, string message) {
+ add (source_reference, message, Ide.DiagnosticSeverity.WARNING);
+ ++warnings;
+ }
+
+ public override void err (Vala.SourceReference? source_reference, string message) {
+ add (source_reference, message, Ide.DiagnosticSeverity.ERROR);
+ ++errors;
+ }
+ }
+}
diff --git a/src/plugins/vala-pack/lang-server/ide-vala-index.vala
b/src/plugins/vala-pack/lang-server/ide-vala-index.vala
new file mode 100644
index 000000000..85ce065af
--- /dev/null
+++ b/src/plugins/vala-pack/lang-server/ide-vala-index.vala
@@ -0,0 +1,568 @@
+/* ide-vala-index.vala
+ *
+ * Copyright 2015 Christian Hergert <christian hergert me>
+ * Copyright 2018 Collabora Ltd.
+ *
+ * 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/>.
+ *
+ * Authors: Christian Hergert <christian hergert me>
+ * Corentin Noël <corentin noel collabora com>
+ */
+
+namespace Ide
+{
+ public class ValaIndex: GLib.Object
+ {
+ Vala.CodeContext code_context;
+ Vala.Parser parser;
+ Ide.ValaDiagnostics report;
+ GLib.File workdir;
+
+ GLib.HashTable<string, GLib.Bytes> unsaved_files;
+
+ public ValaIndex (GLib.File workdir)
+ {
+ this.workdir = workdir;
+ code_context = new Vala.CodeContext ();
+ unsaved_files = new GLib.HashTable<string, GLib.Bytes> (str_hash, str_equal);
+
+ Vala.CodeContext.push (code_context);
+
+ /*
+ * TODO: Some of the following options could be extracted by parsing
+ * the contents of *_VALAFLAGS or AM_VALAFLAGS in automake.
+ * We need to do this in a somewhat build system agnostic fashion
+ * since there seems to a cargo cult of cmake/vala.
+ */
+
+ code_context.assert = true;
+ code_context.checking = false;
+ code_context.deprecated = false;
+ code_context.hide_internal = false;
+ code_context.experimental = false;
+ code_context.experimental_non_null = false;
+ code_context.gobject_tracing = false;
+ code_context.nostdpkg = false;
+ code_context.ccode_only = true;
+ code_context.compile_only = true;
+ code_context.use_header = false;
+ code_context.includedir = null;
+ code_context.basedir = workdir.get_path ();
+ code_context.directory = GLib.Environment.get_current_dir ();
+ code_context.debug = false;
+ code_context.mem_profiler = false;
+ code_context.save_temps = false;
+
+ code_context.profile = Vala.Profile.GOBJECT;
+ code_context.add_define ("GOBJECT");
+
+ code_context.entry_point_name = null;
+
+ code_context.run_output = false;
+
+ int minor = 36;
+ var tokens = ValaConfig.VALA_VERSION.split(".", 2);
+ if (tokens[1] != null) {
+ minor = int.parse(tokens[1]);
+ }
+
+ for (var i = 2; i <= minor; i += 2) {
+ code_context.add_define ("VALA_0_%d".printf (i));
+ }
+
+ for (var i = 16; i < GLib.Version.minor; i+= 2) {
+ code_context.add_define ("GLIB_2_%d".printf (i));
+ }
+
+ code_context.vapi_directories = {};
+
+ /* $prefix/share/vala-0.32/vapi */
+ string versioned_vapidir = get_versioned_vapidir ();
+ if (versioned_vapidir != null) {
+ add_vapidir_locked (versioned_vapidir);
+ }
+
+ /* $prefix/share/vala/vapi */
+ string unversioned_vapidir = get_unversioned_vapidir ();
+ if (unversioned_vapidir != null) {
+ add_vapidir_locked (unversioned_vapidir);
+ }
+
+ code_context.add_external_package ("glib-2.0");
+ code_context.add_external_package ("gobject-2.0");
+
+ report = new Ide.ValaDiagnostics ();
+ code_context.report = report;
+
+ parser = new Vala.Parser ();
+ parser.parse (code_context);
+
+ code_context.check ();
+
+ load_directory (workdir);
+ Vala.CodeContext.pop ();
+ }
+
+ public void set_unsaved_file (string path,
+ GLib.Bytes? bytes)
+ {
+ if (bytes == null) {
+ unsaved_files.remove (path);
+ } else {
+ unsaved_files[path] = bytes;
+ }
+ }
+
+ public Ide.Diagnostics get_file_diagnostics (string path,
+ string[] flags)
+ {
+ lock (this.code_context) {
+ Vala.CodeContext.push (this.code_context);
+ load_build_flags (flags);
+ add_file (GLib.File.new_for_path (path));
+ reparse ();
+ if (report.get_errors () == 0) {
+ code_context.check ();
+ }
+
+ Vala.CodeContext.pop ();
+ }
+
+ return report.get_diagnostic_from_file (path);
+ }
+
+ public GLib.Variant get_symbol_tree (string path,
+ string[] flags)
+ {
+ GLib.Variant symbol_tree;
+ lock (this.code_context) {
+ Vala.CodeContext.push (this.code_context);
+ load_build_flags (flags);
+
+ if (add_file (GLib.File.new_for_path (path)))
+ reparse ();
+
+ var tree_builder = new Ide.ValaSymbolTreeVisitor ();
+ foreach (var source_file in code_context.get_source_files ()) {
+ if (source_file.filename == path) {
+ source_file.accept_children (tree_builder);
+ break;
+ }
+ }
+
+ symbol_tree = tree_builder.build_tree ();
+
+ Vala.CodeContext.pop ();
+ }
+
+ return symbol_tree;
+ }
+
+ public GLib.Variant get_index_entries (string path,
+ string[] flags)
+ {
+ GLib.Variant index_entries;
+ lock (this.code_context) {
+ Vala.CodeContext.push (this.code_context);
+ load_build_flags (flags);
+
+ if (add_file (GLib.File.new_for_path (path)))
+ reparse ();
+
+ var tree_builder = new Ide.ValaSymbolTreeVisitor ();
+ foreach (var source_file in code_context.get_source_files ()) {
+ if (source_file.filename == path) {
+ source_file.accept_children (tree_builder);
+ break;
+ }
+ }
+
+ index_entries = tree_builder.build_index_entries ();
+
+ Vala.CodeContext.pop ();
+ }
+
+ return index_entries;
+ }
+
+ public Ide.Symbol? locate_symbol (string path,
+ string[] flags,
+ uint line,
+ uint column)
+ {
+ Ide.Symbol? symbol = null;
+ lock (this.code_context) {
+ Vala.CodeContext.push (this.code_context);
+ load_build_flags (flags);
+
+ if (add_file (GLib.File.new_for_path (path)))
+ reparse ();
+
+ foreach (var source_file in code_context.get_source_files ()) {
+ if (source_file.filename == path) {
+ var locator = new Ide.ValaLocator ();
+ var vala_node = locator.locate (source_file, line, column);
+ if (vala_node != null && vala_node is Vala.Symbol) {
+ symbol = Ide.vala_to_ide_symbol (vala_node as
Vala.Symbol);
+ }
+
+ break;
+ }
+ }
+
+ Vala.CodeContext.pop ();
+ }
+
+ return symbol;
+ }
+
+ public Ide.Symbol? find_nearest_scope (string path,
+ string[] flags,
+ uint line,
+ uint column)
+ {
+ Ide.Symbol? ide_symbol = null;
+ lock (this.code_context) {
+ Vala.CodeContext.push (this.code_context);
+ load_build_flags (flags);
+ var symbol = find_nearest_symbol (path, line, column);
+ if (symbol != null) {
+ ide_symbol = Ide.vala_to_ide_symbol (symbol);
+ }
+
+ Vala.CodeContext.pop ();
+ }
+
+ return ide_symbol;
+ }
+
+ public GLib.Variant code_complete (string path,
+ uint line,
+ uint column,
+ string? line_text)
+ {
+ GLib.Variant array;
+ lock (this.code_context) {
+ Vala.CodeContext.push (this.code_context);
+ var block = find_nearest_symbol (path, line, column) as Vala.Block;
+ Vala.SourceLocation cursor = Vala.SourceLocation (null, (int)line,
(int)column);
+ var completion = new Ide.ValaCompletion (code_context, cursor, line_text,
block);
+ Vala.ArrayList<Vala.Symbol>? symbols = completion.run (ref cursor);
+ var variant_builder = new GLib.VariantBuilder (new GLib.VariantType
("aa{sv}"));
+ foreach (var symbol in symbols) {
+ var dict = new GLib.VariantDict ();
+ dict.insert ("name", "s", symbol.name);
+ if (symbol is Vala.LocalVariable || symbol is Vala.Variable) {
+ dict.insert ("type", "s", "variable");
+ var variable = symbol as Vala.Variable;
+ dict.insert ("returns", "s",
variable.variable_type.to_qualified_string (symbol.owner));
+ } else if (symbol is Vala.Field)
+ dict.insert ("type", "s", "field");
+ else if (symbol is Vala.Subroutine)
+ dict.insert ("type", "s", "function");
+ else if (symbol is Vala.Namespace)
+ dict.insert ("type", "s", "namespace");
+ else if (symbol is Vala.MemberAccess)
+ dict.insert ("type", "s", "member");
+ else if (symbol is Vala.Property) {
+ dict.insert ("type", "s", "property");
+ var prop = symbol as Vala.Property;
+ dict.insert ("returns", "s",
prop.property_type.to_qualified_string (symbol.owner));
+ } else if (symbol is Vala.Struct) {
+ var str = symbol as Vala.Struct;
+ if (str.is_simple_type ())
+ dict.insert ("type", "s", "simpletype");
+ else
+ dict.insert ("type", "s", "struct");
+ } else if (symbol is Vala.Class) {
+ dict.insert ("type", "s", "class");
+ var cls = symbol as Vala.Class;
+ if (cls.is_abstract) {
+ dict.insert ("misc", "s", "abstract");
+ } else if (cls.is_compact) {
+ dict.insert ("misc", "s", "compact");
+ } else if (cls.is_immutable) {
+ dict.insert ("misc", "s", "immutable");
+ }
+ } else if (symbol is Vala.Enum)
+ dict.insert ("type", "s", "enum");
+ else if (symbol is Vala.EnumValue)
+ dict.insert ("type", "s", "enum-value");
+ else if (symbol is Vala.Delegate)
+ dict.insert ("type", "s", "delegate");
+ else if (symbol is Vala.Method) {
+ dict.insert ("type", "s", "method");
+ var method = symbol as Vala.Method;
+ var type_params = method.get_type_parameters ();
+ if (type_params.size > 0) {
+ string[] params_name = {};
+ foreach (var type_param in type_params) {
+ params_name += type_param.name;
+ }
+
+ dict.insert ("type-parameters", "as", params_name);
+ }
+
+ var params_dict = new GLib.VariantDict ();
+ var parameters = method.get_parameters ();
+ foreach (var param in parameters) {
+ if (param.ellipsis) {
+ params_dict.insert ("dir", "s", "ellipsis");
+ break;
+ }
+
+ if (param.direction == Vala.ParameterDirection.OUT)
+ params_dict.insert ("dir", "s", "out");
+ else if (param.direction ==
Vala.ParameterDirection.REF)
+ params_dict.insert ("dir", "s", "ref");
+
+ params_dict.insert ("type", "s",
param.variable_type.to_qualified_string (method.owner));
+ }
+
+ dict.insert_value ("params", params_dict.end ());
+ }
+
+ variant_builder.add_value (dict.end ());
+ }
+
+ array = variant_builder.end ();
+ Vala.CodeContext.pop ();
+ }
+
+ return array;
+ }
+
+ private void reparse ()
+ {
+ report.clear ();
+
+ foreach (var source_file in code_context.get_source_files ()) {
+ if (source_file.get_nodes ().size == 0) {
+ parser.visit_source_file (source_file);
+ }
+ }
+ }
+
+ private Vala.Symbol? find_nearest_symbol (string path,
+ uint line,
+ uint column)
+ {
+ Vala.Symbol? symbol = null;
+ if (add_file (GLib.File.new_for_path (path)))
+ reparse ();
+
+ apply_unsaved_files ();
+ foreach (var source_file in code_context.get_source_files ()) {
+ if (source_file.filename == path) {
+ var locator = new Ide.ValaLocator ();
+ var vala_node = locator.locate (source_file, line, column) as
Vala.Symbol;
+ while (vala_node != null) {
+ if (vala_node is Vala.Class ||
+ vala_node is Vala.Subroutine ||
+ vala_node is Vala.Namespace ||
+ vala_node is Vala.Struct)
+ break;
+
+ if (vala_node.owner != null)
+ vala_node = vala_node.owner.owner;
+ else
+ vala_node = vala_node.parent_symbol;
+ }
+
+ symbol = vala_node;
+ break;
+ }
+ }
+
+ return symbol;
+ }
+
+ private bool add_file (GLib.File file)
+ {
+ var path = file.get_path ();
+ if (path == null)
+ return false;
+
+ foreach (var source_file in code_context.get_source_files ()) {
+ if (source_file.filename == path)
+ return false;
+ }
+
+ var type = Vala.SourceFileType.SOURCE;
+ if (path.has_suffix ("vapi"))
+ type = Vala.SourceFileType.PACKAGE;
+
+ var source_file = new Ide.ValaSourceFile (code_context, type, path, null, false);
+ code_context.add_source_file (source_file);
+ return true;
+ }
+
+ private void apply_unsaved_files () {
+ foreach (var source_file in code_context.get_source_files ()) {
+ if (source_file is Ide.ValaSourceFile) {
+ GLib.Bytes? content = unsaved_files[source_file.filename];
+ (source_file as Ide.ValaSourceFile).sync (content);
+ unsaved_files.remove (source_file.filename);
+ }
+ }
+ }
+
+ private void load_directory (GLib.File directory,
+ GLib.Cancellable? cancellable = null)
+ {
+ try {
+ var enumerator = directory.enumerate_children
(FileAttribute.STANDARD_NAME+","+FileAttribute.STANDARD_TYPE, 0, cancellable);
+
+ FileInfo file_info;
+ while ((file_info = enumerator.next_file ()) != null) {
+ var name = file_info.get_name ();
+
+ if (name == ".flatpak-builder" || name == ".git")
+ continue;
+
+ if (file_info.get_file_type () == GLib.FileType.DIRECTORY) {
+ var child = directory.get_child (file_info.get_name ());
+ load_directory (child, cancellable);
+ } else if (name.has_suffix (".vala") || name.has_suffix (".vapi")) {
+ add_file (directory.get_child (file_info.get_name ()));
+ }
+ }
+
+ enumerator.close ();
+ } catch (GLib.Error err) {
+ warning (err.message);
+ }
+ }
+
+ private void load_build_flags (string[] flags)
+ {
+ lock (code_context) {
+ Vala.CodeContext.push (code_context);
+
+ var packages = new Vala.ArrayList<string> ();
+
+ var len = flags.length;
+ for (var i = 0; i < len; i++) {
+ string next_param = null;
+ string param = flags[i];
+
+ if (param.contains ("=")) {
+ var offset = param.index_of("=") + 1;
+ next_param = param.offset(offset);
+ } else if (i + 1 < len) {
+ next_param = flags[i + 1];
+ }
+
+ if (next_param != null) {
+ if (param.has_prefix("--pkg")) {
+ packages.add (next_param);
+ } else if (param.has_prefix ("--vapidir")) {
+ add_vapidir_locked (next_param);
+ } else if (param.has_prefix ("--vapi")) {
+ packages.add (next_param);
+ } else if (param.has_prefix ("--girdir")) {
+ add_girdir_locked (next_param);
+ } else if (param.has_prefix ("--metadatadir")) {
+ add_metadatadir_locked (next_param);
+ } else if (param.has_prefix ("--target-glib")) {
+ /* TODO: Parse glib version ~= 2.44 */
+ }
+
+ continue;
+ }
+ else if (param.has_suffix (".vapi")) {
+ if (!GLib.Path.is_absolute (param)) {
+ var child = workdir.get_child (param);
+ add_file (child);
+ } else {
+ add_file (GLib.File.new_for_path (param));
+ }
+ }
+ }
+
+ /* Now add external packages after vapidir/girdir have been added */
+ foreach (var package in packages) {
+ code_context.add_external_package (package);
+ }
+
+ Vala.CodeContext.pop ();
+ }
+ }
+
+ /* Caller is expected to hold code_context lock */
+ private void add_vapidir_locked (string vapidir)
+ {
+ var dirs = code_context.vapi_directories;
+ if (vapidir in dirs)
+ return;
+
+ debug ("Adding vapidir %s", vapidir);
+ dirs += vapidir;
+ code_context.vapi_directories = dirs;
+ }
+
+ /* Caller is expected to hold code_context lock */
+ private void add_girdir_locked (string girdir)
+ {
+ var dirs = code_context.gir_directories;
+ if (girdir in dirs)
+ return;
+
+ dirs += girdir;
+ code_context.gir_directories = dirs;
+ }
+
+ /* Caller is expected to hold code_context lock */
+ private void add_metadatadir_locked (string metadata_dir)
+ {
+ var dirs = code_context.metadata_directories;
+ if (metadata_dir in dirs)
+ return;
+
+ dirs += metadata_dir;
+ code_context.metadata_directories = dirs;
+ }
+
+ static string? get_versioned_vapidir ()
+ {
+ try {
+ var pkgname = "libvala-%s".printf (ValaConfig.VALA_VERSION);
+ string outstr = null;
+ var subprocess = new GLib.Subprocess (GLib.SubprocessFlags.STDOUT_PIPE,
+ "pkg-config",
+ "--variable=vapidir",
+ pkgname,
+ null);
+ subprocess.communicate_utf8 (null, null, out outstr, null);
+ outstr = outstr.strip ();
+ return outstr;
+ } catch (GLib.Error er) {
+ warning ("%s", er.message);
+ return null;
+ }
+ }
+
+ static string? get_unversioned_vapidir ()
+ {
+ string versioned_vapidir = get_versioned_vapidir ();
+
+ if (versioned_vapidir != null) {
+ return GLib.Path.build_filename (versioned_vapidir,
+ "..", "..", "vala", "vapi", null);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/plugins/vala-pack/ide-vala-locator.vala
b/src/plugins/vala-pack/lang-server/ide-vala-locator.vala
similarity index 84%
rename from src/plugins/vala-pack/ide-vala-locator.vala
rename to src/plugins/vala-pack/lang-server/ide-vala-locator.vala
index d8fc61cdb..75e21c223 100644
--- a/src/plugins/vala-pack/ide-vala-locator.vala
+++ b/src/plugins/vala-pack/lang-server/ide-vala-locator.vala
@@ -20,10 +20,10 @@
namespace Ide {
public class ValaLocator: Vala.CodeVisitor {
struct Location {
- int line;
- int column;
+ uint line;
+ uint column;
- public Location (int line, int column) {
+ public Location (uint line, uint column) {
this.line = line;
this.column = column;
}
@@ -45,18 +45,18 @@ namespace Ide {
}
Location location;
- Vala.Symbol innermost;
+ Vala.CodeNode innermost;
Location innermost_begin;
Location innermost_end;
- public Vala.Symbol? locate (Vala.SourceFile file, int line, int column) {
+ public Vala.CodeNode? locate (Vala.SourceFile file, uint line, uint column) {
location = Location (line, column);
innermost = null;
file.accept_children(this);
return innermost;
}
- bool update_location (Vala.Symbol s) {
+ bool update_location (Vala.CodeNode s) {
if (!location.inside (s.source_reference))
return false;
@@ -73,6 +73,21 @@ namespace Ide {
return false;
}
+ /*private bool covers_location (Vala.SourceReference source_ref) {
+ unowned source_ref.begin
+ if (source_ref.begin.line > location.line)
+ return false;
+
+ if (source_ref.end.line < location.line)
+ return false;
+
+ if (source_ref.begin.line == location.line && )
+ return false;
+
+ if (source_ref.end.line < location.line)
+ return false;
+ }*/
+
public override void visit_block (Vala.Block b) {
if (update_location (b))
b.accept_children(this);
@@ -98,12 +113,21 @@ namespace Ide {
return;
iface.accept_children(this);
}
-
+ public override void visit_member_access (Vala.MemberAccess expr) {
+ if (update_location (expr))
+ return;
+ expr.accept_children(this);
+ }
public override void visit_method (Vala.Method m) {
if (update_location (m))
return;
m.accept_children(this);
}
+ public override void visit_method_call (Vala.MethodCall expr) {
+ if (update_location (expr))
+ return;
+ expr.accept_children(this);
+ }
public override void visit_creation_method (Vala.CreationMethod m) {
if (update_location (m))
return;
@@ -177,8 +201,13 @@ namespace Ide {
visit_method ((expr as Vala.LambdaExpression).method);
}
if (expr is Vala.MethodCall) {
+ update_location (expr);
foreach (Vala.Expression e in (expr as Vala.MethodCall).get_argument_list())
visit_expression (e);
+
+ }
+ if (expr is Vala.Assignment) {
+ (expr as Vala.Assignment).accept_children (this);
}
}
}
diff --git a/src/plugins/vala-pack/ide-vala-source-file.vala
b/src/plugins/vala-pack/lang-server/ide-vala-source-file.vala
similarity index 60%
rename from src/plugins/vala-pack/ide-vala-source-file.vala
rename to src/plugins/vala-pack/lang-server/ide-vala-source-file.vala
index f82d580c5..45cc76168 100644
--- a/src/plugins/vala-pack/ide-vala-source-file.vala
+++ b/src/plugins/vala-pack/lang-server/ide-vala-source-file.vala
@@ -37,15 +37,12 @@
*/
using GLib;
-using Ide;
using Vala;
namespace Ide
{
public class ValaSourceFile: Vala.SourceFile
{
- ArrayList<Ide.Diagnostic> diagnostics;
- internal GLib.File file;
public ValaSourceFile (Vala.CodeContext context,
Vala.SourceFileType type,
@@ -54,41 +51,29 @@ namespace Ide
bool cmdline)
{
base (context, type, filename, content, cmdline);
-
- this.file = GLib.File.new_for_path (filename);
- this.diagnostics = new ArrayList<Ide.Diagnostic> ();
-
- this.add_default_namespace ();
- this.dirty = true;
+ add_default_namespace ();
+ dirty = true;
}
public bool dirty { get; set; }
public GLib.File get_file ()
{
- return this.file;
+ return GLib.File.new_for_path (this.filename);
}
public void reset ()
{
- /* clear diagnostics on main thread */
- var old_diags = this.diagnostics;
- this.diagnostics = new ArrayList<Ide.Diagnostic> ();
- GLib.Idle.add(() => {
- old_diags.clear ();
- return false;
- });
-
/* Copy the node list since we will be mutating while iterating */
var copy = new ArrayList<Vala.CodeNode> ();
foreach (var node in this.get_nodes ()) {
copy.add (node);
}
- var entry_point = this.context.entry_point;
+ var entry_point = context.entry_point;
foreach (var node in copy) {
- this.remove_node (node);
+ remove_node (node);
if (node is Vala.Symbol) {
var symbol = (Vala.Symbol)node;
@@ -96,53 +81,27 @@ namespace Ide
symbol.owner.remove (symbol.name);
}
if (symbol == entry_point) {
- this.context.entry_point = null;
+ context.entry_point = null;
}
}
}
- this.add_default_namespace ();
- this.dirty = true;
+ add_default_namespace ();
+ dirty = true;
}
- public void sync (GenericArray<Ide.UnsavedFile> unsaved_files)
+ public void sync (GLib.Bytes? bytes)
{
- unsaved_files.foreach((unsaved_file) => {
- if (unsaved_file.get_file ().equal (this.file)) {
- var bytes = unsaved_file.get_content ();
-
- if (bytes.get_data () != (uint8[]) this.content) {
- this.content = (string)bytes.get_data ();
- this.reset ();
- return;
- }
- }
- });
- }
-
- public void report (Vala.SourceReference source_reference,
- string message,
- Ide.DiagnosticSeverity severity)
- {
- var begin = new Ide.Location (this.file,
- source_reference.begin.line - 1,
- source_reference.begin.column - 1);
- var end = new Ide.Location (this.file,
- source_reference.end.line - 1,
- source_reference.end.column - 1);
-
- var diag = new Ide.Diagnostic (severity, message, begin);
- diag.take_range (new Ide.Range (begin, end));
- this.diagnostics.add (diag);
- }
+ if (bytes == null) {
+ content = null;
+ reset ();
+ }
- public Ide.Diagnostics? diagnose ()
- {
- var ret = new Ide.Diagnostics ();
- foreach (var diag in this.diagnostics) {
- ret.add (diag);
+ unowned uint8[] data = bytes.get_data ();
+ if (data != content.data) {
+ content = (string)data;
+ reset ();
}
- return ret;
}
void add_default_namespace ()
diff --git a/src/plugins/vala-pack/lang-server/ide-vala-symbol-tree.vala
b/src/plugins/vala-pack/lang-server/ide-vala-symbol-tree.vala
new file mode 100644
index 000000000..aee2f43e4
--- /dev/null
+++ b/src/plugins/vala-pack/lang-server/ide-vala-symbol-tree.vala
@@ -0,0 +1,181 @@
+/* ide-vala-symbol-tree.vala
+ *
+ * Copyright 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ */
+
+using GLib;
+using Ide;
+using Vala;
+
+namespace Ide
+{
+ public class ValaSymbolTreeVisitor: Vala.CodeVisitor
+ {
+ Vala.HashMap<Vala.CodeNode?,Vala.ArrayList<Vala.CodeNode>> table;
+ GLib.Queue<Vala.ArrayList<Vala.CodeNode>> queue;
+
+ public ValaSymbolTreeVisitor ()
+ {
+ this.table = new Vala.HashMap<Vala.CodeNode?,Vala.ArrayList<Vala.CodeNode>> ();
+ this.queue = new GLib.Queue<Vala.ArrayList<Vala.CodeNode>> ();
+
+ var root = new Vala.ArrayList<Vala.CodeNode> ();
+ this.table [null] = root;
+ this.queue.push_head (root);
+ }
+
+ /*
+ * Creates the Code Index, a list of all the symbols
+ */
+ public GLib.Variant build_index_entries ()
+ {
+ var variant_builder = new GLib.VariantBuilder (new GLib.VariantType ("a(usisuuuu)"));
+ Vala.ArrayList<Vala.CodeNode>? list = table[null];
+ if (list != null) {
+ foreach (var element in list) {
+ create_index (element, variant_builder);
+ }
+ }
+
+ return variant_builder.end ();
+ }
+
+ private void create_index (Vala.CodeNode node, GLib.VariantBuilder variant_builder) {
+ var symbol = vala_symbol_from_code_node (node);
+ if (symbol == null)
+ return;
+
+ Ide.SymbolKind kind = vala_symbol_kind_from_code_node (node);
+ Ide.SymbolFlags flags = vala_symbol_flags_from_code_node (node) |
Ide.SymbolFlags.IS_DEFINITION;
+ string name = vala_symbol_name (symbol);
+ string search_name;
+ switch (kind) {
+ case Ide.SymbolKind.FUNCTION:
+ case Ide.SymbolKind.METHOD:
+ search_name = "f\x1F%s".printf (name);
+ break;
+
+ case Ide.SymbolKind.VARIABLE:
+ case Ide.SymbolKind.FIELD:
+ case Ide.SymbolKind.CONSTANT:
+ search_name = "v\x1F%s".printf (name);
+ break;
+
+ case Ide.SymbolKind.CLASS:
+ search_name = "c\x1F%s".printf (name);
+ break;
+
+ default:
+ search_name = "x\x1F%s".printf (name);
+ break;
+ }
+
+ unowned Vala.SourceReference? loc = symbol.source_reference;
+ variant_builder.add ("(usisuuuu)", flags,
+ symbol.get_full_name (),
+ kind,
+ search_name,
+ loc.begin.line,
+ loc.begin.column,
+ loc.end.line,
+ loc.end.column);
+
+ Vala.ArrayList<Vala.CodeNode>? list = table[node];
+ if (list != null) {
+ foreach (var element in list) {
+ create_index (element, variant_builder);
+ }
+ }
+ }
+
+ /*
+ * Creates the Symbols Tree
+ */
+ public GLib.Variant build_tree ()
+ {
+ var variant_builder = new GLib.VariantBuilder (new GLib.VariantType ("aa{sv}"));
+ Vala.ArrayList<Vala.CodeNode>? list = table[null];
+ if (list != null) {
+ foreach (var element in list) {
+ var node = create_node (element);
+ if (node != null)
+ variant_builder.add_value (node);
+ }
+ }
+
+ return variant_builder.end ();
+ }
+
+ private GLib.Variant? create_node (Vala.CodeNode node) {
+ GLib.Variant? root_variant = null;
+ var symbol = node as Vala.Symbol;
+ if (symbol != null) {
+ Ide.Symbol? ide_symbol = Ide.vala_to_ide_symbol (symbol);
+ if (ide_symbol == null)
+ return null;
+
+ root_variant = ide_symbol.to_variant ();
+ }
+
+ var variantdict = new GLib.VariantDict (root_variant);
+ var variant_builder = new GLib.VariantBuilder (new GLib.VariantType ("aa{sv}"));
+ Vala.ArrayList<Vala.CodeNode>? list = table[node];
+ if (list != null) {
+ foreach (var element in list) {
+ variant_builder.add_value (create_node (element));
+ }
+ }
+
+ variantdict.insert_value ("children", variant_builder.end ());
+ return variantdict.end ();
+ }
+
+ void visit_generic (Vala.CodeNode node)
+ {
+ var current = this.queue.peek_head ();
+ current.add (node);
+
+ var list = new ArrayList<Vala.CodeNode> ();
+ this.queue.push_head (list);
+
+ this.table [node] = list;
+
+ // Automatic properties have a nested field that we don't want.
+ if (!(node is Vala.Property)) {
+ node.accept_children (this);
+ }
+
+ this.queue.pop_head ();
+ }
+
+ public override void visit_class (Vala.Class node) { this.visit_generic (node); }
+ public override void visit_method (Vala.Method node) { this.visit_generic (node); }
+ public override void visit_local_variable (Vala.LocalVariable node) { this.visit_generic
(node); }
+ public override void visit_interface (Vala.Interface node) { this.visit_generic (node); }
+ public override void visit_struct (Vala.Struct node) { this.visit_generic (node); }
+ public override void visit_creation_method (Vala.CreationMethod node) { this.visit_generic
(node); }
+ public override void visit_property (Vala.Property node) { this.visit_generic (node); }
+ public override void visit_field (Vala.Field node) { this.visit_generic (node); }
+ public override void visit_constant (Vala.Constant node) { this.visit_generic (node); }
+ public override void visit_constructor (Vala.Constructor node) { this.visit_generic (node); }
+ public override void visit_destructor (Vala.Destructor node) { this.visit_generic (node); }
+ public override void visit_signal (Vala.Signal node) { this.visit_generic (node); }
+ public override void visit_delegate (Vala.Delegate node) { this.visit_generic (node); }
+
+ public override void visit_block (Vala.Block node) { node.accept_children (this); }
+ public override void visit_source_file (Vala.SourceFile source_file) {
source_file.accept_children (this); }
+ }
+}
diff --git a/src/plugins/vala-pack/lang-server/meson.build b/src/plugins/vala-pack/lang-server/meson.build
new file mode 100644
index 000000000..bf1b5a04c
--- /dev/null
+++ b/src/plugins/vala-pack/lang-server/meson.build
@@ -0,0 +1,55 @@
+gnome_builder_vala_sources = files(
+ '../valaconfig.vapi',
+ 'gnome-builder-vala.vala',
+ 'ide-utils.vala',
+ 'ide-vala-index.vala',
+ 'ide-vala-completion.vala',
+ 'ide-vala-locator.vala',
+ 'ide-vala-diagnostics.vala',
+ 'ide-vala-symbol-tree.vala',
+ 'ide-vala-source-file.vala'
+)
+
+gnome_builder_vala_deps = [
+ libpeas_dep,
+ libvala,
+ libgiounix_dep,
+ libide_vapi,
+ libgtksource_dep,
+ libvte_dep,
+ libdazzle_dep,
+ libtemplate_glib_dep,
+ libjsonrpc_glib_dep,
+
+ libide_code_dep,
+ libide_core_dep,
+]
+
+executable('gnome-builder-vala', gnome_builder_vala_sources,
+ dependencies: gnome_builder_vala_deps,
+ gui_app: false,
+ install: true,
+ install_dir: get_option('libexecdir'),
+ link_args: exe_link_args,
+ install_rpath: pkglibdir_abs,
+ include_directories: [include_directories('.')] + libide_include_directories,
+
+ vala_args: [ '--target-glib=2.52',
+ '--pkg=posix',
+ '--pkg=libpeas-1.0',
+ '--pkg=gtksourceview-4',
+ '--pkg=gio-2.0',
+ '--pkg=libdazzle-1.0',
+ '--pkg=template-glib-1.0',
+ '--pkg=vte-2.91',
+ '--pkg=libvala-' + libvala_version,
+ '--pkg=jsonrpc-glib-1.0',
+ ],
+ c_args: exe_c_args +
+ [ '-DVALA_VERSION="@0@"'.format(libvala_version),
+ '-DLOG_DOMAIN="vala-pack-plugin"',
+ '-DGETTEXT_PACKAGE="gnome-builder"',
+ '-DPACKAGE_DATADIR="@0@"'.format(join_paths(get_option('prefix'),
get_option('datadir'), 'gnome-builder')),
+ '-Wno-incompatible-pointer-types',
+ ],
+)
diff --git a/src/plugins/vala-pack/meson.build b/src/plugins/vala-pack/meson.build
index 7df58993d..a309a4645 100644
--- a/src/plugins/vala-pack/meson.build
+++ b/src/plugins/vala-pack/meson.build
@@ -8,32 +8,27 @@ libvala = dependency('libvala-@0@'.format(libvala_version))
vala_sources = [
'valaconfig.vapi',
- 'ide-vala-service.vala',
+ 'ide-vala-client.vala',
'ide-vala-code-indexer.vala',
- 'ide-vala-completion.vala',
'ide-vala-completion-item.vala',
'ide-vala-completion-provider.vala',
- 'ide-vala-diagnostics.vala',
'ide-vala-diagnostic-provider.vala',
'ide-vala-indenter.vala',
- 'ide-vala-index.vala',
- 'ide-vala-locator.vala',
'ide-vala-pipeline-addin.vala',
'ide-vala-preferences-addin.vala',
- 'ide-vala-source-file.vala',
'ide-vala-symbol-resolver.vala',
'ide-vala-symbol-tree.vala',
'vala-pack-plugin.vala',
]
vala_deps = [
- libvala,
libpeas_dep,
libide_vapi,
libgtksource_dep,
libvte_dep,
libdazzle_dep,
libtemplate_glib_dep,
+ libjsonrpc_glib_dep
]
shared_module('plugin-vala-pack', vala_sources,
@@ -42,25 +37,28 @@ shared_module('plugin-vala-pack', vala_sources,
install_dir: plugindir,
install_rpath: pkglibdir_abs,
include_directories: [include_directories('.')] + libide_include_directories,
-
vala_args: [ '--target-glib=2.52',
'--pkg=posix',
'--pkg=libpeas-1.0',
'--pkg=gtksourceview-4',
'--pkg=gio-2.0',
- '--pkg=libvala-' + libvala_version,
+ '--pkg=gio-unix-2.0',
'--pkg=libdazzle-1.0',
'--pkg=template-glib-1.0',
'--pkg=vte-2.91',
+ '--pkg=jsonrpc-glib-1.0',
],
c_args: [ '-DVALA_VERSION="@0@"'.format(libvala_version),
'-DLOG_DOMAIN="vala-pack"',
'-DGETTEXT_PACKAGE="gnome-builder"',
'-DPACKAGE_DATADIR="@0@"'.format(join_paths(get_option('prefix'),
get_option('datadir'), 'gnome-builder')),
- '-Wno-incompatible-pointer-types',
+ '-DPACKAGE_LIBEXECDIR="@0@"'.format(join_paths(get_option('prefix'),
get_option('libexecdir'))),
+ '-Wno-incompatible-pointer-types',
],
)
+subdir('lang-server')
+
configure_file(
input: 'vala-pack.plugin',
output: 'vala-pack.plugin',
diff --git a/src/plugins/vala-pack/valaconfig.vapi b/src/plugins/vala-pack/valaconfig.vapi
index 200c36c53..8c90be0a7 100644
--- a/src/plugins/vala-pack/valaconfig.vapi
+++ b/src/plugins/vala-pack/valaconfig.vapi
@@ -1,6 +1,7 @@
[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "")]
namespace ValaConfig {
public const string PACKAGE_DATADIR;
+ public const string PACKAGE_LIBEXECDIR;
public const string VALA_VERSION;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]