[gnome-builder/wip/tintou/vala-subprocess: 33/33] vala: put the vala language support in another process



commit 76c8d8b75c91f14ab4a4a3e3676995e1ec4280a1
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

 src/plugins/vala-pack/config.vapi                  |   1 +
 src/plugins/vala-pack/ide-vala-client.vala         | 444 ++++++++++++++++
 src/plugins/vala-pack/ide-vala-code-indexer.vala   | 142 ++---
 .../vala-pack/ide-vala-completion-item.vala        |  29 +-
 .../vala-pack/ide-vala-completion-provider.vala    |  35 +-
 .../vala-pack/ide-vala-diagnostic-provider.vala    |  16 +-
 src/plugins/vala-pack/ide-vala-diagnostics.vala    |  64 ---
 src/plugins/vala-pack/ide-vala-index.vala          | 579 ---------------------
 src/plugins/vala-pack/ide-vala-pipeline-addin.vala |   1 -
 src/plugins/vala-pack/ide-vala-service.vala        |  94 ----
 .../vala-pack/ide-vala-symbol-resolver.vala        | 120 ++---
 src/plugins/vala-pack/ide-vala-symbol-tree.vala    | 146 ++----
 .../vala-pack/lang-server/gnome-builder-vala.vala  | 314 +++++++++++
 src/plugins/vala-pack/lang-server/ide-utils.vala   | 108 ++++
 .../{ => lang-server}/ide-vala-completion.vala     |  53 +-
 .../lang-server/ide-vala-diagnostics.vala          | 102 ++++
 .../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      |  43 ++
 src/plugins/vala-pack/meson.build                  |  17 +-
 src/plugins/vala-pack/vala-pack-plugin.vala        |   2 +-
 23 files changed, 2000 insertions(+), 1177 deletions(-)
---
diff --git a/src/plugins/vala-pack/config.vapi b/src/plugins/vala-pack/config.vapi
index f46b186c8..a46833d9d 100644
--- a/src/plugins/vala-pack/config.vapi
+++ b/src/plugins/vala-pack/config.vapi
@@ -1,6 +1,7 @@
 [CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "")]
 namespace Config {
        public const string PACKAGE_DATADIR;
+       public const string PACKAGE_LIBEXECDIR;
        public const string VALA_VERSION;
 }
 
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..088f13c89
--- /dev/null
+++ b/src/plugins/vala-pack/ide-vala-client.vala
@@ -0,0 +1,444 @@
+/* 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, Ide.Service {
+               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> ();
+                       root_uri = context.vcs.working_directory;
+
+                       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 (Config.PACKAGE_LIBEXECDIR + "/gnome-builder-vala");
+                       supervisor = new Ide.SubprocessSupervisor ();
+                       supervisor.set_launcher (launcher);
+                       supervisor.spawned.connect (subprocess_spawned);
+                       supervisor.exited.connect (subprocess_exited);
+                       context.buffer_manager.buffer_saved.connect (buffer_saved);
+               }
+
+               ~ValaClient () {
+                       state = State.SHUTDOWN;
+                       if (supervisor != null) {
+                               var _supervisor = supervisor;
+                               supervisor = null;
+                               _supervisor.stop ();
+                       }
+               }
+
+               public unowned string get_name () {
+                       return "vala client";
+               }
+
+               public void start () {
+
+               }
+
+               public void 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.
+                        */
+
+                       unowned GLib.File gfile = buffer.file.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 (context, 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.UnsavedFiles unsaved_files_object = context.unsaved_files;
+                       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 (null);
+                               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 ba24c64aa..3b271f824 100644
--- a/src/plugins/vala-pack/ide-vala-code-indexer.vala
+++ b/src/plugins/vala-pack/ide-vala-code-indexer.vala
@@ -33,45 +33,32 @@ namespace Ide
                                                                    GLib.Cancellable? cancellable)
                        throws GLib.Error
                {
-                       var context = this.get_context ();
-                       var service = (Ide.ValaService)context.get_service_typed (typeof (Ide.ValaService));
-                       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.ValaClient? client = (Ide.ValaClient)context.get_service_typed (typeof 
(Ide.ValaClient));
+                       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.SourceLocation location,
-                                                       string[]? build_flags,
+                                                       [CCode (array_length = false, array_null_terminated = 
true)] string[]? build_flags,
                                                        GLib.Cancellable? cancellable)
                        throws GLib.Error
                {
-                       var context = this.get_context ();
-                       var service = (Ide.ValaService)context.get_service_typed (typeof (Ide.ValaService));
-                       var index = service.index;
-                       var file = location.get_file ();
+                       unowned Ide.ValaClient? client = (Ide.ValaClient)context.get_service_typed (typeof 
(Ide.ValaClient));
+                       unowned Ide.File ifile = 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.get_file (), (int)line, 
(int)column);
-
-                       if (symbol == null)
-                               throw new GLib.IOError.FAILED ("failed to locate symbol");
-
-                       return symbol.get_full_name ();
+                       try {
+                               return yield client.get_index_key_async (ifile.file, build_flags, line, 
column, cancellable);
+                       } catch (Error e) {
+                               throw e;
+                       }
                }
        }
 
@@ -86,11 +73,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 ()
@@ -116,67 +128,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 2174f0306..a5d8aee15 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;
+               unowned Ide.ValaClient client;
 
                public void load (Ide.Context context)
                {
-                       this.service = context.get_service_typed (typeof (Ide.ValaService)) as 
Ide.ValaService;
+                       client = (Ide.ValaClient)context.get_service_typed (typeof (Ide.ValaClient));
                }
 
                public bool is_trigger (Gtk.TextIter iter, unichar ch)
@@ -76,27 +75,8 @@ namespace Ide
                        var line = iter.get_line ();
                        var line_offset = iter.get_line_offset ();
 
-                       var index = this.service.index;
-                       var unsaved_files = this.get_context ().get_unsaved_files ();
-
-                       /* 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.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;
+                       warning ("ICI");
+                       var proposals = yield client.proposals_populate_async (file.file, line + 1, 
line_offset + 1, line_text, cancellable);
 
                        if (cancellable.is_cancelled () || results == null)
                                throw new GLib.IOError.CANCELLED ("operation was cancelled");
@@ -144,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,
@@ -251,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 de7f0bbbe..a086a79fb 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
 {
@@ -29,10 +28,17 @@ namespace Ide
                                                              GLib.Cancellable? cancellable)
                        throws GLib.Error
                {
-                       var service = (Ide.ValaService)get_context ().get_service_typed (typeof 
(Ide.ValaService));
-                       yield service.index.parse_file (file.file, get_context ().unsaved_files, cancellable);
-                       var results = yield service.index.get_diagnostics (file.file, cancellable);
-                       return results;
+                       var build_system = this.context.get_build_system ();
+
+                       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)get_context ().get_service_typed 
(typeof (Ide.ValaClient));
+                       return yield client.diagnose_async (file.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 566a686fb..773f428c0 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-symbol-resolver.vala 
b/src/plugins/vala-pack/ide-vala-symbol-resolver.vala
index 4d98a3b1c..9855a0e0e 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,89 +28,38 @@ namespace Ide
                                                                    GLib.Cancellable? cancellable)
                        throws GLib.Error
                {
-                       var context = this.get_context ();
-                       var service = (Ide.ValaService)context.get_service_typed (typeof (Ide.ValaService));
-                       var index = service.index;
-                       var symbol_tree = yield index.get_symbol_tree (file, cancellable);
-
-                       return symbol_tree;
-               }
-
-               Ide.Symbol? create_symbol (Ide.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.SourceLocation (file,
-                                                                                                 
source_reference.begin.line - 1,
-                                                                                                 
source_reference.begin.column - 1,
-                                                                                                 0);
-                               return new Ide.Symbol (symbol.name, kind, flags, loc, loc, loc);
+                       var build_system = this.context.get_build_system ();
+                       var ifile = new Ide.File (context, file);
+
+                       string[] flags = {};
+                       try {
+                               flags = yield build_system.get_build_flags_async (ifile, cancellable);
+                       } catch (GLib.Error err) {
+                               warning (err.message);
                        }
 
-                       return null;
+                       unowned Ide.ValaClient client = (Ide.ValaClient)get_context ().get_service_typed 
(typeof (Ide.ValaClient));
+                       return yield client.get_symbol_tree_async (file, flags, cancellable);
                }
 
                public async Ide.Symbol? lookup_symbol_async (Ide.SourceLocation location,
                                                              GLib.Cancellable? cancellable)
                        throws GLib.Error
                {
-                       var context = this.get_context ();
-                       var service = (Ide.ValaService)context.get_service_typed (typeof (Ide.ValaService));
-                       var index = service.index;
-                       var file = location.get_file ();
+                       var build_system = context.get_build_system ();
+                       unowned Ide.File ifile = 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.get_file (), line, column);
-
-                       if (symbol != null)
-                               return create_symbol (file, symbol);
-
-                       return null;
-               }
+                       string[] flags = {};
+                       try {
+                               flags = yield build_system.get_build_flags_async (ifile, 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)get_context ().get_service_typed 
(typeof (Ide.ValaClient));
+                       return yield client.locate_symbol_async (ifile.file, flags, line, column, 
cancellable);
                }
 
                public void load () {}
@@ -128,30 +76,22 @@ namespace Ide
                                                                   GLib.Cancellable? cancellable)
                        throws GLib.Error
                {
-                       var context = this.get_context ();
-                       var service = (Ide.ValaService)context.get_service_typed (typeof (Ide.ValaService));
-                       var index = service.index;
-                       var file = location.get_file ();
+                       var build_system = context.get_build_system ();
+                       unowned Ide.File ifile = 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.get_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;
+                       string[] flags = {};
+                       try {
+                               flags = yield build_system.get_build_flags_async (ifile, cancellable);
+                       } catch (GLib.Error err) {
+                               warning (err.message);
                        }
 
+                       unowned Ide.ValaClient client = (Ide.ValaClient)get_context ().get_service_typed 
(typeof (Ide.ValaClient));
+                       var symbol = yield client.locate_symbol_async (ifile.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 d5140a11a..f71aa63ab 100644
--- a/src/plugins/vala-pack/ide-vala-symbol-tree.vala
+++ b/src/plugins/vala-pack/ide-vala-symbol-tree.vala
@@ -18,139 +18,77 @@
 
 using GLib;
 using Ide;
-using Vala;
 
 namespace Ide
 {
-       public class ValaSymbolTreeVisitor: Vala.CodeVisitor
+       public class ValaSymbolTree : Ide.Object, Ide.SymbolTree
        {
-               HashMap<Vala.CodeNode?,ArrayList<Vala.CodeNode>> table;
-               GLib.Queue<ArrayList<Vala.CodeNode>> queue;
+               public GLib.File file { get; construct; }
+               public GLib.Variant tree { get; construct; }
 
-               public ValaSymbolTreeVisitor ()
+               public ValaSymbolTree (Ide.Context context, GLib.File file, GLib.Variant tree)
                {
-                       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);
+                       GLib.Object (context: context, file: file, tree: tree);
                }
 
-               public Ide.SymbolTree? build_tree ()
+               public uint get_n_children (Ide.SymbolNode? node)
                {
-                       return new Ide.ValaSymbolTree (this.table);
+                       if (node != null)
+                               return (node as ValaSymbolNode).n_children;
+
+                       return (uint)tree.n_children ();
                }
 
-               void visit_generic (Vala.CodeNode node)
+               public Ide.SymbolNode? get_nth_child (Ide.SymbolNode? node, uint nth)
                {
-                       var current = this.queue.peek_head ();
-                       current.add (node);
-
-                       var list = new ArrayList<Vala.CodeNode> ();
-                       this.queue.push_head (list);
+                       if (node != null)
+                               return (node as ValaSymbolNode).get_nth_child (nth);
 
-                       this.table [node] = list;
-
-                       node.accept_children (this);
-
-                       this.queue.pop_head ();
+                       var child_val = tree.get_child_value (nth);
+                       return new ValaSymbolNode (context, child_val);
                }
-
-               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
+       public class ValaSymbolNode : Ide.SymbolNode
        {
-               HashMap<Vala.CodeNode?,ArrayList<Vala.CodeNode>> table;
-
-               public ValaSymbolTree (HashMap<Vala.CodeNode?,ArrayList<Vala.CodeNode>> table)
-               {
-                       this.table = table;
-
-                       debug ("Tree created with %u rows", table.size);
-               }
+               public GLib.Variant children { get; construct; }
+               public Ide.Symbol symbol { get; construct; }
 
-               ArrayList<Vala.CodeNode>? find (Ide.SymbolNode? node)
+               public uint n_children
                {
-                       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;
+                       get {
+                               return (uint)children.n_children ();
                        }
-
-                       return this.table [key];
                }
 
-               public uint get_n_children (Ide.SymbolNode? node)
+               public ValaSymbolNode (Ide.Context context, GLib.Variant node)
                {
-                       var list = find (node);
-
-                       if (list == null)
-                               debug ("Failed to find child! %p", node);
-                       else
-                               debug ("node has %u children.", list.size);
-
-                       return list != null ? list.size : 0;
-               }
-
-               public Ide.SymbolNode? get_nth_child (Ide.SymbolNode? node, uint nth)
-               {
-                       var list = find (node);
-
-                       if (list != null && list.size > nth)
-                               return new Ide.ValaSymbolNode (list [(int)nth]);
+                       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;
+                       }
 
-                       return null;
+                       GLib.Object (context: context,
+                                    children: tmp_children,
+                                    symbol: _symbol,
+                                    kind: _symbol.get_kind (),
+                                    flags: _symbol.get_flags (),
+                                    name: _symbol.get_name ());
                }
-       }
-
-       public class ValaSymbolNode : Ide.SymbolNode
-       {
-               public Vala.CodeNode? node;
 
-               public ValaSymbolNode (Vala.CodeNode node)
-               {
-                       this.node = node;
-
-                       this.name = (node as Vala.Symbol).name;
-                       this.kind = Ide.SymbolKind.NONE;
-                       this.flags = Ide.SymbolFlags.NONE;
-
-                       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.SourceLocation? 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.SourceLocation (file,
-                                                      source_reference.begin.line - 1,
-                                                      source_reference.begin.column - 1,
-                                                      0);
+                       var child_val = children.get_child_value (nth);
+                       return new ValaSymbolNode (context, 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..ed202899c
--- /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_size ();
+                       for (int i = 0; i < size; i++) {
+                               unowned Ide.Diagnostic diag = diagnostics.index (i);
+                               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..1079d05dd
--- /dev/null
+++ b/src/plugins/vala-pack/lang-server/ide-utils.vala
@@ -0,0 +1,108 @@
+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 = new Ide.File (null, GLib.File.new_for_path 
(source_reference.file.filename));
+                       var loc = new Ide.SourceLocation (file,
+                                                         source_reference.begin.line - 1,
+                                                         source_reference.begin.column - 1,
+                                                         0);
+
+                       return new Ide.Symbol (name, kind, flags, loc, loc, loc);
+               }
+
+               return new Ide.Symbol (name, kind, flags, null, 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..65c6efb67
--- /dev/null
+++ b/src/plugins/vala-pack/lang-server/ide-vala-diagnostics.vala
@@ -0,0 +1,102 @@
+/* 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;
+                       Ide.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 = new Ide.File (null, GLib.File.new_for_path (filename));
+                               var list = new Vala.ArrayList<Ide.Diagnostic> ();
+                               diagnosed_files[filename] = list;
+                               diagnosed_file = list;
+                       }
+
+                       var begin = new Ide.SourceLocation (file,
+                                                           source_reference.begin.line - 1,
+                                                           source_reference.begin.column - 1,
+                                                           0);
+                       var end = new Ide.SourceLocation (file,
+                                                         source_reference.end.line - 1,
+                                                         source_reference.end.column - 1,
+                                                         0);
+
+                       var diag = new Ide.Diagnostic (severity, message, begin);
+                       diag.take_range (new Ide.SourceRange (begin, end));
+                       diagnosed_file.add (diag);
+               }
+
+               public Ide.Diagnostics get_diagnostic_from_file (string filename)
+               {
+                       var ar = new GLib.GenericArray<Ide.Diagnostic> ();
+                       unowned Vala.ArrayList<Ide.Diagnostic> diagnosed_file = diagnosed_files[filename];
+                       if (diagnosed_file != null) {
+                               foreach (var diag in diagnosed_file) {
+                                       ar.add (diag);
+                               }
+                       }
+
+                       return new Ide.Diagnostics (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..200c4cbdc
--- /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 = Config.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 (Config.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 59%
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 9572fb7ab..6caea1f82 100644
--- a/src/plugins/vala-pack/ide-vala-source-file.vala
+++ b/src/plugins/vala-pack/lang-server/ide-vala-source-file.vala
@@ -37,16 +37,12 @@
  */
 
 using GLib;
-using Ide;
 using Vala;
 
 namespace Ide
 {
        public class ValaSourceFile: Vala.SourceFile
        {
-               ArrayList<Ide.Diagnostic> diagnostics;
-               internal Ide.File file;
-
                public ValaSourceFile (Vala.CodeContext context,
                                       Vala.SourceFileType type,
                                       string filename,
@@ -55,34 +51,24 @@ namespace Ide
                {
                        base (context, type, filename, content, cmdline);
 
-                       this.file = new Ide.File (null, 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.file;
-               }
-
                public void reset ()
                {
-                       this.diagnostics.clear ();
-
                        /* 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;
@@ -90,56 +76,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;
-               }
-
-               public void sync (GenericArray<Ide.UnsavedFile> unsaved_files)
-               {
-                       var gfile = this.file.file;
-                       unsaved_files.foreach((unsaved_file) => {
-                               if (unsaved_file.get_file ().equal (gfile)) {
-                                       var bytes = unsaved_file.get_content ();
-
-                                       if (bytes.get_data () != (uint8[]) this.content) {
-                                               this.content = (string)bytes.get_data ();
-                                               this.reset ();
-                                               return;
-                                       }
-                               }
-                       });
+                       add_default_namespace ();
+                       dirty = true;
                }
 
-               public void report (Vala.SourceReference source_reference,
-                                   string message,
-                                   Ide.DiagnosticSeverity severity)
+               public void sync (GLib.Bytes? bytes)
                {
-                       var begin = new Ide.SourceLocation (this.file,
-                                                           source_reference.begin.line - 1,
-                                                           source_reference.begin.column - 1,
-                                                           0);
-                       var end = new Ide.SourceLocation (this.file,
-                                                         source_reference.end.line - 1,
-                                                         source_reference.end.column - 1,
-                                                         0);
-
-                       var diag = new Ide.Diagnostic (severity, message, begin);
-                       diag.take_range (new Ide.SourceRange (begin, end));
-                       this.diagnostics.add (diag);
-               }
+                       if (bytes == null) {
+                               content = null;
+                               reset ();
+                       }
 
-               public Ide.Diagnostics? diagnose ()
-               {
-                       var ar = new GLib.GenericArray<Ide.Diagnostic> ();
-                       foreach (var diag in this.diagnostics) {
-                               ar.add (diag);
+                       unowned uint8[] data = bytes.get_data ();
+                       if (data != content.data) {
+                               content = (string)data;
+                               reset ();
                        }
-                       return new Ide.Diagnostics (ar);
                }
 
                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..6acbdfe69
--- /dev/null
+++ b/src/plugins/vala-pack/lang-server/meson.build
@@ -0,0 +1,43 @@
+gnome_builder_vala_sources = files(
+  '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 = [
+  libvala,
+  libide_vapi,
+  libgiounix_dep,
+  libjsonrpc_glib_dep,
+  libide_dep,
+  vala_config_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,
+
+     vala_args: [ '--target-glib=2.52',
+                  '--pkg=posix',
+                  '--pkg=gio-2.0',
+                  '--pkg=gio-unix-2.0',
+                  '--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 a8c53c6e5..c4b413efe 100644
--- a/src/plugins/vala-pack/meson.build
+++ b/src/plugins/vala-pack/meson.build
@@ -9,32 +9,27 @@ add_languages('vala')
 valac = meson.get_compiler('vala')
 libvala_version = run_command(valac.cmd_array()[0], '--api-version').stdout().strip()
 libvala = dependency('libvala-@0@'.format(libvala_version))
+vala_config_dep = meson.get_compiler('vala').find_library('config', dirs: 
join_paths(meson.current_source_dir()))
 
 vala_pack_sources = [
-  'config.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_pack_deps = [
-  libvala,
   libide_vapi,
   libpeas_dep,
   libide_plugin_dep,
+  vala_config_dep
 ]
 
 shared_module('vala-pack-plugin', vala_pack_sources,
@@ -50,17 +45,21 @@ shared_module('vala-pack-plugin', vala_pack_sources,
                   '--pkg=libpeas-1.0',
                   '--pkg=gtksourceview-4',
                   '--pkg=gio-2.0',
-                  '--pkg=libvala-' + libvala_version,
+                  '--pkg=gio-unix-2.0',
+                  '--pkg=jsonrpc-glib-1.0',
                   '--pkg=libdazzle-1.0',
                 ],
         c_args: [ '-DVALA_VERSION="@0@"'.format(libvala_version),
                   '-DLOG_DOMAIN="vala-pack-plugin"',
                   '-DGETTEXT_PACKAGE="gnome-builder"',
+                  '-DPACKAGE_LIBEXECDIR="@0@"'.format(join_paths(get_option('prefix'), 
get_option('libexecdir'))),
                   '-DPACKAGE_DATADIR="@0@"'.format(join_paths(get_option('prefix'), get_option('datadir'), 
'gnome-builder')),
                  '-Wno-incompatible-pointer-types',
                 ],
 )
 
+subdir('lang-server')
+
 configure_file(
           input: 'vala-pack.plugin',
          output: 'vala-pack.plugin',
diff --git a/src/plugins/vala-pack/vala-pack-plugin.vala b/src/plugins/vala-pack/vala-pack-plugin.vala
index df206e6bb..9a13c19b8 100644
--- a/src/plugins/vala-pack/vala-pack-plugin.vala
+++ b/src/plugins/vala-pack/vala-pack-plugin.vala
@@ -31,6 +31,6 @@ public void peas_register_types (GLib.TypeModule module)
        peas.register_extension_type (typeof (Ide.DiagnosticProvider), typeof (Ide.ValaDiagnosticProvider));
        peas.register_extension_type (typeof (Ide.Indenter), typeof (Ide.ValaIndenter));
        peas.register_extension_type (typeof (Ide.PreferencesAddin), typeof (Ide.ValaPreferencesAddin));
-       peas.register_extension_type (typeof (Ide.Service), typeof (Ide.ValaService));
        peas.register_extension_type (typeof (Ide.SymbolResolver), typeof (Ide.ValaSymbolResolver));
+       peas.register_extension_type (typeof (Ide.Service), typeof (Ide.ValaClient));
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]