[gnome-code-assistance/wip/arch] wip



commit 0ad79eb95e19c514deca7c824aaea2b03b853b0e
Author: Jesse van den Kieboom <jessevdk gmail com>
Date:   Mon Nov 11 00:23:07 2013 +0100

    wip

 .../gnome/codeassistance/transport/dbus.rb         |  355 ++++++-------
 backends/rbcommon/gnome/codeassistance/types.rb    |   29 +-
 backends/vala/dbus.vala                            |   21 +-
 backends/vala/makefileintegration.vala             |  553 ++++++++++++++++++++
 backends/vala/service.vala                         |   21 +-
 backends/vala/types.vala                           |    6 -
 backends/vala/valaoptionparser.vala                |  268 ++++++++++
 7 files changed, 1033 insertions(+), 220 deletions(-)
---
diff --git a/backends/rbcommon/gnome/codeassistance/transport/dbus.rb 
b/backends/rbcommon/gnome/codeassistance/transport/dbus.rb
index 6d5ac03..ee41d17 100644
--- a/backends/rbcommon/gnome/codeassistance/transport/dbus.rb
+++ b/backends/rbcommon/gnome/codeassistance/transport/dbus.rb
@@ -20,261 +20,248 @@ require 'gnome/codeassistance/types'
 require 'pathname'
 
 module Gnome; end
+module Gnome::CodeAssistance; end
 
-module Gnome::CodeAssistance
-    class Service
-        class << self; attr_reader :language; end
-        @language = nil
-
-        def initialize(id, name, document)
-            @id = id
-            @name = name
-            @document = document
-        end
 
-        def data_path(path, unsaved)
-            unsaved.each do |u|
-                return u.data_path if u.path == path
-            end
-
-            return path
-        end
+class Gnome::CodeAssistance::Service
+    class << self; attr_reader :language; end
+    @language = nil
 
-        def new_document(*args)
-            @document.call(*args)
-        end
-
-        def parse(path, cursor, unsaved, options, doc)
-            return doc
-        end
+    def parse(doc, options)
+    end
 
-        def dispose(doc)
-        end
+    def dispose(doc)
     end
+end
 
+module Gnome::CodeAssistance::DBus
     class Document < DBus::Object
-        dbus_interface 'org.gnome.CodeAssist.Document' do
+        def initialize(path, doc)
+            super(path)
+            @_doc = doc
         end
+    end
 
-        def self.extended_modules
-            (class << self; self end).included_modules
+    module Diagnostics
+        def self.included(base)
+            base.instance_eval do
+                dbus_interface 'org.gnome.CodeAssist.Diagnostics' do
+                    dbus_method :Diagnostics, "out diagnostics:a(ua((x(xx)(xx))s)a(x(xx)(xx))s)" do
+                        return [ _doc diagnostics collect { |d| d.to_tuple }]
+                    end
+                end
+            end
         end
+    end
+end
 
-        def initialize(*args)
+module Gnome::CodeAssistance::Services
+    module Diagnostics
+        def diagnostics
+            @diagnostics || []
         end
 
-        def self.new(path, *args)
-            obj = self.allocate
-
-            DBus::Object.instance_method(:initialize).bind(obj).call(path)
-            self.instance_method(:initialize).bind(obj).call(*args)
-
-            obj
+        def diagnostics=(val)
+            @diagnostics = val
         end
-    end
 
-    module Services
-        module Diagnostics
-            def self.extended(base)
-                base.instance_eval do
-                    dbus_interface 'org.gnome.CodeAssist.Diagnostics' do
-                        dbus_method :Diagnostics, "out diagnostics:a(ua((x(xx)(xx))s)a(x(xx)(xx))s)" do
-                            return [diagnostics.collect { |d| d.to_tuple }]
-                        end
-                    end
-                end
-            end
+        def self.included(base)
+            base._dbus.send(:include, Gnome::CodeAssistance::DBus::Diagnostics)
         end
     end
+end
+
+module Gnome::CodeAssistance::Servers
+    module Service
+        def dispose
+            a = app(@sender)
 
-    class Server < DBus::Object
-        class App
-            attr_accessor :id, :name, :docs, :ids, :nextid, :service
-
-            def initialize
-                @id = 0
-                @name = ''
-                @docs = {}
-                @ids = {}
-                @nextid = 0
-                @service = nil
+            if path.length != 0
+                path = Pathname.new(path).cleanpath.to_s
             end
-        end
 
-        dbus_interface 'org.gnome.CodeAssist.Service' do
-            dbus_method :SupportedServices, "out services:as" do
-                app(@sender)
+            if a.ids.include?(path)
+                id = a.ids[path]
+                dispose_document(a.docs[id])
 
-                [ services]
-            end
+                a.docs.delete(id)
+                a.ids.delete(path)
 
-            dbus_method :Parse, "in path:s, in cursor:x, in unsaved:a(ss), in options:a{sv}, out document:o" 
do |path, cursor, unsaved, options|
-                begin
-                    return parse(path, cursor, unsaved, options)
-                rescue Exception => e
-                    p e
-                    raise
+                if a.ids.length == 0
+                    dispose_app(@sender)
                 end
             end
+        end
 
-            dbus_method :Dispose, "in path:s" do |path|
-                a = app(@sender)
-
-                if path.length != 0
-                    path = Pathname.new(path).cleanpath.to_s
-                end
+        def self.included(base)
+            base.instance_eval do
+                dbus_interface 'org.gnome.CodeAssist.Service' do
+                    dbus_method :Parse, "in path:s, in cursor:x, in data_path:s, in options:a{sv}, out 
document:o" do |path, cursor, data_path, options|
+                        app = ensure_app(@sender)
+                        doc = ensure_document(app, path, data_path, cursor)
 
-                if a.ids.include?(path)
-                    id = a.ids[path]
-                    dispose_document(a.docs[id])
+                        app.service.parse(doc, options)
 
-                    a.docs.delete(id)
-                    a.ids.delete(path)
+                        return doc.path
+                    end
 
-                    if a.ids.length == 0
-                        dispose_app(@sender)
+                    dbus_method :Dispose, "in path:s" do |path|
+                        if @apps.include?(@sender)
+                            dispose(@apps[ sender], Pathname.new(path).cleanpath.to_s)
+                        end
                     end
                 end
             end
         end
+    end
 
-        def initialize(bus, name, path, service, document)
-            super(path)
+    module Project
+        def self.included(base)
+            base.instance_eval do
+                dbus_interface 'org.gnome.CodeAssist.Project' do
+                    dbus_method :ParseProject, "in path:s, in cursor:x, in docs:a(ss), in options:a{sv}, out 
documents:a(so)" do |path, cursor, docs, options|
+                        begin
+                            return parse_project(path, cursor, docs, options)
+                        rescue Exception => e
+                            p e
+                            raise
+                        end
+                    end
+                end
+            end
+        end
+    end
+end
 
-            @apps = {}
-            @nextid = 0
+module Gnome::CodeAssistance
+    class Service
+        def parse(doc, options)
+        end
 
-            @bus = bus
-            @server = @bus.request_service(name)
-            @appservice = service
-            @document = document
+        def dispose(doc)
+        end
+    end
 
-            extract_services
+    module Project
+        def parse_all(doc, docs, options)
+        end
+    end
 
-            @server.export(self)
+    class Document
+        @@_dbus = Gnome::CodeAssistance::DBus::Document
 
-            dbus_service = @bus.service('org.freedesktop.DBus')
-            dbus = dbus_service.object('/org/freedesktop/DBus')
-            dbus.default_iface = 'org.freedesktop.DBus'
-            dbus.introspect
+        def self._dbus
+            @@_dbus
+        end
 
-            dbus.on_signal('NameOwnerChanged') do |_, oldname, newname|
-                if newname.empty?
-                    dispose_app(oldname)
-                end
-            end
+        def initialize(path)
+            @_dbus = @@_dbus.new(path, self)
         end
+    end
+end
 
-        def parse(path, cursor, unsaved, options)
-            a = app(@sender)
-            doc = nil
+class Gnome::CodeAssistance::Server < DBus::Object
+    class App
+        attr_accessor :id, :name, :docs, :ids, :nextid, :service
 
-            if path.length != 0
-                path = Pathname.new(path).cleanpath.to_s
-            end
+        def initialize
+            @id = 0
+            @name = ''
+            @docs = {}
+            @nextid = 0
+            @service = nil
+        end
+    end
 
-            if a.ids.include?(path)
-                docid = a.ids[path]
-                doc = a.docs[docid]
-            end
+    def initialize(bus, name, path, service, document)
+        super(path)
 
-            unsaved = unsaved.collect{ |u| UnsavedDocument.new(u[0], u[1]) }
-            doc = a.service.parse(path, cursor, unsaved, options, doc)
+        @apps = {}
+        @nextid = 0
 
-            if doc == nil
-                raise DBus::Error.new("Failed to parse document")
-            end
+        @bus = bus
+        @server = @bus.request_service(name)
 
-            unless a.ids.include?(path)
-                docid = a.nextid
+        @appservice = service
+        @document = document
 
-                a.ids[path] = docid
-                a.docs[docid] = doc
+        @server.export(self)
 
-                @server.export(doc)
-                a.nextid += 1
-            end
+        dbus_service = @bus.service('org.freedesktop.DBus')
+        dbus = dbus_service.object('/org/freedesktop/DBus')
+        dbus.default_iface = 'org.freedesktop.DBus'
+        dbus.introspect
 
-            return [doc.path]
+        dbus.on_signal('NameOwnerChanged') do |_, oldname, newname|
+            if newname.empty?
+                dispose_app(oldname)
+            end
         end
+    end
 
-        def document_path(a, docid)
-            "#{ path}/#{a.id}/documents/#{docid}"
-        end
+    def document_path(a, docid)
+        "#{ path}/#{a.id}/documents/#{docid}"
+    end
 
-        def dispose_document(a, doc)
-            a.service.dispose(doc)
-            @server.unexport(doc)
-        end
+    def dispose_document(a, doc)
+        a.service.dispose(doc)
+        @server.unexport(doc)
+    end
 
-        def dispose_app(appid)
-            if @apps.include?(appid)
-                a = @apps[appid]
+    def dispose_app(appid)
+        if @apps.include?(appid)
+            a = @apps[appid]
 
-                a.docs.each do |docid, doc|
-                    dispose_document(a, doc)
-                end
+            a.docs.each do |docid, doc|
+                dispose_document(a, doc)
+            end
 
-                @apps.delete(appid)
+            @apps.delete(appid)
 
-                if @apps.length == 0
-                    exit(0)
-                end
+            if @apps.length == 0
+                exit(0)
             end
         end
+    end
 
-        def app(appid)
-            unless @apps.include?(appid)
-                a = App.new()
-                a.id = @nextid
-                a.name = appid
-
-                a.service = @appservice.new(a.id, a.name, Proc.new do |*args|
-                    @document.new(document_path(a, a.nextid), *args)
-                end)
+    def app(appid)
+        unless @apps.include?(appid)
+            a = App.new()
 
-                @apps[a.name] = a
-                @nextid += 1
+            a.id = @nextid
+            a.name = appid
 
-                return a
-            end
+            a.service = @appservice.new
 
-            return @apps[appid]
-        end
+            @apps[a.name] = a
+            @nextid += 1
 
-        def dispatch(msg)
-            @sender = msg.sender
-            super(msg)
+            return a
         end
 
-        def extract_services
-            @services = ['org.gnome.CodeAssist.Document']
-
-            ex = @document.extended_modules
+        return @apps[appid]
+    end
 
-            Services.constants.each do |s|
-                if ex.include?(Services.const_get(s))
-                    @services << 'org.gnome.CodeAssist.' + s.to_s()
-                end
-            end
-        end
+    def dispatch(msg)
+        @sender = msg.sender
+        super(msg)
     end
+end
 
-    class Transport
-        def initialize(service, document)
-            @bus = DBus::SessionBus.instance
+class Gnome::CodeAssistance::Transport
+    def initialize(service, document)
+        @bus = DBus::SessionBus.instance
 
-            name = 'org.gnome.CodeAssist.' + service.language
-            path = '/org/gnome/CodeAssist/' + service.language
+        name = 'org.gnome.CodeAssist.' + service.language
+        path = '/org/gnome/CodeAssist/' + service.language
 
-            @server = Server.new(@bus, name, path, service, document)
-        end
+        @server = srvtype.new(@bus, name, path, service, document)
+    end
 
-        def run
-            main = DBus::Main.new
-            main << @bus
-            main.run
-        end
+    def run
+        main = DBus::Main.new
+        main << @bus
+        main.run
     end
 end
 
diff --git a/backends/rbcommon/gnome/codeassistance/types.rb b/backends/rbcommon/gnome/codeassistance/types.rb
index 5c1327d..38226f6 100644
--- a/backends/rbcommon/gnome/codeassistance/types.rb
+++ b/backends/rbcommon/gnome/codeassistance/types.rb
@@ -20,7 +20,7 @@ require 'pathname'
 module Gnome; end
 
 module Gnome::CodeAssistance
-    class UnsavedDocument
+    class OpenDocument
         attr_accessor :path, :data_path
 
         def initialize(path='', data_path='')
@@ -33,12 +33,31 @@ module Gnome::CodeAssistance
             if data_path.length != 0
                 @data_path = Pathname.new(data_path).cleanpath.to_s
             else
-                @data_path = data_path
+                @data_path = @path
             end
         end
 
+        def self.from_tuple(tp)
+            OpenDocument.new(tp[0], tp[1])
+        end
+
         def to_s
-            "<UnsavedDocument: #{ path}, #{ data_path}>"
+            "<OpenDocument: #{ path}, #{ data_path}>"
+        end
+    end
+
+    class RemoteDocument
+        def initialize(path='', remote_path='')
+            @path = path
+            @remote_path = remote_path
+        end
+
+        def to_s
+            "<RemoteDocument: #{ path}, #{ remote_path}>"
+        end
+
+        def to_tuple
+            [ path, @remote_path]
         end
     end
 
@@ -56,7 +75,7 @@ module Gnome::CodeAssistance
 
         def to_range(file=0)
             s = SourceLocation.new(@line, @column)
-            e = SourceLocation.new(@line, @column + 1)
+            e = SourceLocation.new(@line, @column)
 
             SourceRange.new(file, s, e)
         end
@@ -69,7 +88,7 @@ module Gnome::CodeAssistance
     class SourceRange
         attr_accessor :file, :start, :end
 
-        def initialize(file=0, s=SourceLocation.new(), e=None)
+        def initialize(file=0, s=SourceLocation.new(), e=nil)
             @file = file
             @start = s
             @end = e
diff --git a/backends/vala/dbus.vala b/backends/vala/dbus.vala
index 3bcd904..ed66d52 100644
--- a/backends/vala/dbus.vala
+++ b/backends/vala/dbus.vala
@@ -156,7 +156,7 @@ public class Service : Object
                return File.new_for_path(path).get_path();
        }
 
-       public ObjectPath parse(string path, int64 cursor, UnsavedDocument[] unsaved, HashTable<string, 
Variant> options, GLib.BusName sender)
+       public ObjectPath parse(string path, int64 cursor, string data_path, HashTable<string, Variant> 
options, GLib.BusName sender)
        {
                var a = app(sender);
                Gca.Backends.Vala.Document? doc = null;
@@ -164,18 +164,24 @@ public class Service : Object
 
                var cpath = clean_path(path);
 
-               for (var i = 0; i < unsaved.length; i++) {
-                       unsaved[i].path = clean_path(unsaved[i].path);
-                       unsaved[i].data_path = clean_path(unsaved[i].data_path);
-               }
-
                if (a.ids.has_key(cpath))
                {
                        ddoc = a.docs[a.ids[cpath]];
                        doc = ddoc.document;
                }
 
-               doc = a.service.parse(cpath, cursor, unsaved, options, doc);
+               string dpath;
+
+               if (data_path == null || data_path == "")
+               {
+                       dpath = path;
+               }
+               else
+               {
+                       dpath = data_path;
+               }
+
+               doc = a.service.parse(cpath, cursor, dpath, options, doc);
 
                if (!a.ids.has_key(cpath))
                {
@@ -267,6 +273,7 @@ public class Service : Object
                return new string[] {
                        "org.gnome.CodeAssist.Document",
                        "org.gnome.CodeAssist.Diagnostics",
+                       "org.gnome.CodeAssist.Service",
                };
        }
 }
diff --git a/backends/vala/makefileintegration.vala b/backends/vala/makefileintegration.vala
new file mode 100644
index 0000000..6e4ab3e
--- /dev/null
+++ b/backends/vala/makefileintegration.vala
@@ -0,0 +1,553 @@
+/*
+ * This file is part of gedit-code-assistant.
+ *
+ * Copyright (C) 2011 - Jesse van den Kieboom
+ *
+ * gedit-code-assistant 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.
+ *
+ * gedit-code-assistant 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 gedit-code-assistant.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Gca.Backends.Vala
+{
+
+public errordomain MakefileIntegrationError
+{
+       MISSING_MAKEFILE,
+       MISSING_TARGET,
+       MISSING_MAKE_OUTPUT
+}
+
+class MakefileIntegration : Object
+{
+       private class Cache
+       {
+               private File d_source;
+               private File? d_makefile;
+               private string[] d_args;
+
+               public Cache(File source, File? makefile, string[] args)
+               {
+                       d_source = source;
+                       d_makefile = makefile;
+                       d_args = args;
+               }
+
+               public File makefile
+               {
+                       get { return d_makefile; }
+               }
+
+               public File source
+               {
+                       get { return d_source; }
+               }
+
+               public string[] args
+               {
+                       get { return d_args; }
+                       set { d_args = value; }
+               }
+       }
+
+       private class Makefile
+       {
+               private File d_file;
+               private Gee.ArrayList<File> d_sources;
+               private FileMonitor ?d_monitor;
+               private uint d_timeoutid;
+
+               public signal void changed();
+
+               public Makefile(File file)
+               {
+                       d_file = file;
+                       d_timeoutid = 0;
+                       d_monitor = null;
+
+                       try
+                       {
+                               d_monitor = file.monitor(FileMonitorFlags.NONE);
+                       }
+                       catch (Error error)
+                       {
+                               return;
+                       }
+
+                       d_sources = new Gee.ArrayList<File>();
+
+                       d_monitor.changed.connect(on_makefile_changed);
+               }
+
+               public bool valid
+               {
+                       get
+                       {
+                               return d_monitor != null;
+                       }
+               }
+
+               public void add(File source)
+               {
+                       d_sources.add(source);
+               }
+
+               public bool remove(File source)
+               {
+                       d_sources.remove(source);
+
+                       return (d_sources.size == 0);
+               }
+
+               public Gee.ArrayList<File> sources
+               {
+                       get { return d_sources; }
+               }
+
+               public File file
+               {
+                       get { return d_file; }
+               }
+
+               private void on_makefile_changed(File file, File ?other, FileMonitorEvent event_type)
+               {
+                       if (event_type == FileMonitorEvent.CHANGED ||
+                           event_type == FileMonitorEvent.CREATED)
+                       {
+                               if (d_timeoutid != 0)
+                               {
+                                       Source.remove(d_timeoutid);
+                               }
+
+                               d_timeoutid = Timeout.add(100, on_makefile_timeout);
+                       }
+               }
+
+               private bool on_makefile_timeout()
+               {
+                       d_timeoutid = 0;
+
+                       changed();
+
+                       return false;
+               }
+               
+       }
+
+       private Gee.HashMap<File, Cache> d_argsCache;
+       private Gee.HashMap<File, Makefile> d_makefileCache;
+
+       public signal void arguments_changed(File file);
+
+       construct
+       {
+               d_argsCache = new Gee.HashMap<File, Cache>();
+               d_makefileCache = new Gee.HashMap<File, Makefile>();
+       }
+
+       private File ?makefile_for(File file,
+                                  Cancellable ?cancellable = null) throws IOError,
+                                                                          Error
+       {
+               File ?ret = null;
+
+               File? par = file.get_parent();
+
+               while (par != null && ret == null)
+               {
+                       File makefile = par.get_child("Makefile");
+
+                       if (makefile.query_exists(cancellable))
+                       {
+                               ret = makefile;
+                       }
+
+                       par = par.get_parent();
+               }
+
+               if (ret != null)
+               {
+                       log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
+                           "Resolved makefile for `%s': `%s'",
+                           file.get_path(),
+                           ret.get_path());
+               }
+
+               return ret;
+       }
+
+       private string[] targets_from_make(File makefile,
+                                          File source) throws SpawnError,
+                                                              RegexError,
+                                                              MakefileIntegrationError
+       {
+               File wd = makefile.get_parent();
+               string basen = wd.get_relative_path(source);
+
+               string[] args = new string[] {
+                       "make",
+                       "-p",
+                       "-n",
+                       null
+               };
+
+               string outstr;
+
+               /* Spawn make to find out which target has the source as a
+                  dependency */
+               Process.spawn_sync(wd.get_path(),
+                                  args,
+                                  null,
+                                  SpawnFlags.SEARCH_PATH |
+                                  SpawnFlags.STDERR_TO_DEV_NULL,
+                                  null,
+                                  out outstr);
+
+               /* Scan the output to find the target */
+               string reg = "^([^:\n]*?(\\.stamp:|:)).*%s".printf(Regex.escape_string(basen));
+
+               Regex regex = new Regex(reg, RegexCompileFlags.MULTILINE);
+               MatchInfo info;
+
+               var ret = new string[1];
+
+               if (regex.match(outstr, 0, out info))
+               {
+                       while (true)
+                       {
+                               var target = info.fetch(1);
+                               target = target.substring(0, target.length - 1);
+
+                               if (target.has_suffix(".stamp"))
+                               {
+                                       ret[0] = target;
+                               }
+                               else
+                               {
+                                       ret += target;
+                               }
+
+                               if (!info.next())
+                               {
+                                       break;
+                               }
+                       }
+               }
+
+               if (ret[0] == null)
+               {
+                       ret = ret[1:ret.length];
+               }
+
+               if (ret.length != 0)
+               {
+                       return ret;
+               }
+
+               throw new MakefileIntegrationError.MISSING_TARGET(
+                       "Could not find make target for %s".printf(basen));
+       }
+
+       private string[] ?flags_from_targets(File     makefile,
+                                            File     source,
+                                            string[] targets) throws SpawnError,
+                                                                     MakefileIntegrationError,
+                                                                     ShellError
+       {
+               /* Fake make to build the target and extract the flags */
+               var wd = makefile.get_parent();
+               string relsource = wd.get_relative_path(source);
+
+               string fakecc = "__GCA_VALA_COMPILE_ARGS__";
+
+               string?[] args = new string?[] {
+                       "make",
+                       "-s",
+                       "-i",
+                       "-n",
+                       "-W",
+                       relsource,
+                       "V=1",
+                       "VALAC=" + fakecc
+               };
+
+               foreach (var target in targets)
+               {
+                       args += target;
+               }
+
+               args += null;
+
+               log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
+                   "Running: %s",
+                   string.joinv(" ", args));
+
+               string outstr;
+
+               Process.spawn_sync(makefile.get_parent().get_path(),
+                                  args,
+                                  null,
+                                  SpawnFlags.SEARCH_PATH |
+                                  SpawnFlags.STDERR_TO_DEV_NULL,
+                                  null,
+                                  out outstr);
+
+               /* Extract args */
+               int idx = outstr.last_index_of(fakecc);
+
+               if (idx < 0)
+               {
+                       throw new MakefileIntegrationError.MISSING_MAKE_OUTPUT("Make output did not contain 
flags");
+               }
+
+               string[] retargs;
+               string[] parts = outstr.substring(idx).split("\n");
+
+               Shell.parse_argv(parts[0], out retargs);
+
+               log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
+                   "Parsed command: %s => '%s'\n",
+                   parts[0],
+                   string.joinv("', '", retargs));
+
+               return retargs;
+       }
+
+       private async void makefile_changed_async(Makefile makefile)
+       {
+               ThreadFunc<void *> func = () => {
+                       foreach (File file in makefile.sources)
+                       {
+                               find_for_makefile(makefile.file, file);
+                       }
+
+                       return null;
+               };
+
+               try
+               {
+                       new Thread<void *>.try("find makefile", func);
+                       yield;
+               }
+               catch
+               {
+               }
+       }
+
+       private void on_makefile_changed(Makefile makefile)
+       {
+               makefile_changed_async.begin(makefile);
+       }
+
+       private void find_for_makefile(File makefile, File file)
+       {
+               string[] targets;
+               string[] args = {};
+
+               try
+               {
+                       targets = targets_from_make(makefile, file);
+
+                       log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
+                           "Makefile make targets for `%s': `%s'",
+                           file.get_path(),
+                           string.joinv(", ", targets));
+
+                       args = flags_from_targets(makefile, file, targets);
+
+                       log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
+                           "Compile flags for `%s': `%s`",
+                           file.get_path(),
+                           string.joinv("`, `", args));
+               }
+               catch (Error e)
+               {
+                       stderr.printf("Makefile error: %s\n", e.message);
+               }
+
+               lock(d_makefileCache)
+               {
+                       lock(d_argsCache)
+                       {
+                               if (d_argsCache.has_key(file))
+                               {
+                                       d_argsCache[file].args = args;
+                               }
+                               else
+                               {
+                                       Cache c = new Cache(file, makefile, args);
+                                       d_argsCache[file] = c;
+                               }
+
+                               if (!d_makefileCache.has_key(makefile))
+                               {
+                                       Makefile m = new Makefile(makefile);
+                                       m.add(file);
+
+                                       m.changed.connect(on_makefile_changed);
+                                       d_makefileCache[makefile] = m;
+                               }
+                       }
+               }
+
+               changed_in_idle(file);
+       }
+
+       private void changed_in_idle(File file)
+       {
+               Idle.add(() => {
+                       arguments_changed(file);
+                       return false;
+               });
+       }
+
+       private async void find_async(File file)
+       {
+               ThreadFunc<void *> func = () => {
+                       File ?makefile = null;
+
+                       try
+                       {
+                               makefile = makefile_for(file);
+                       }
+                       catch (Error e)
+                       {
+                               makefile = null;
+                       }
+
+                       if (makefile == null)
+                       {
+                               Cache c = new Cache(file, null, new string[] {});
+                               d_argsCache[file] = c;
+
+                               changed_in_idle(file);
+                               return null;
+                       }
+
+                       find_for_makefile(makefile, file);
+
+                       lock(d_makefileCache)
+                       {
+                               if (d_makefileCache.has_key(file))
+                               {
+                                       d_makefileCache[makefile].add(file);
+                               }
+                       }
+
+                       return null;
+               };
+
+               try
+               {
+                       new Thread<void *>.try("findasync", func);
+                       yield;
+               }
+               catch
+               {
+               }
+       }
+
+       public new string[]? get(File file)
+       {
+               string[] ?ret = null;
+
+               lock(d_argsCache)
+               {
+                       if (d_argsCache.has_key(file))
+                       {
+                               ret = d_argsCache[file].args;
+                       }
+                       else
+                       {
+                               monitor(file);
+                       }
+               }
+
+               return ret;
+       }
+
+       public async string[] args_for_file(File file) throws MakefileIntegrationError {
+               lock(d_argsCache)
+               {
+                       if (d_argsCache.has_key(file))
+                       {
+                               return d_argsCache[file].;
+
+                               lock(d_makefileCache)
+                               {
+                                       return 
+                               }
+                       }
+               }
+       }
+
+       public void monitor(File file)
+       {
+               bool hascache;
+
+               lock(d_argsCache)
+               {
+                       hascache = d_argsCache.has_key(file);
+               }
+
+               if (hascache)
+               {
+                       arguments_changed(file);
+               }
+               else
+               {
+                       find_async.begin(file, (source, res) => find_async.end(res));
+               }
+       }
+
+       public void remove_monitor(File file)
+       {
+               lock(d_argsCache)
+               {
+                       if (d_argsCache.has_key(file))
+                       {
+                               Cache c = d_argsCache[file];
+
+                               lock (d_makefileCache)
+                               {
+                                       if (d_makefileCache.has_key(c.makefile))
+                                       {
+                                               Makefile m = d_makefileCache[c.makefile];
+
+                                               if (m.remove(file))
+                                               {
+                                                       d_makefileCache.unset(c.makefile);
+                                               }
+                                       }
+                               }
+
+                               d_argsCache.unset(file);
+                       }
+               }
+       }
+}
+
+public static int main(string[] a){
+       var ml = new MainLoop();
+
+       MakefileIntegration it = new MakefileIntegration();
+
+       it.monitor(File.new_for_commandline_arg("dbus.vala"));
+
+       ml.run();
+
+       return 0;
+}
+
+}
+
+/* vi:ex:ts=4 */
diff --git a/backends/vala/service.vala b/backends/vala/service.vala
index 019ea4e..7062019 100644
--- a/backends/vala/service.vala
+++ b/backends/vala/service.vala
@@ -123,20 +123,7 @@ public class Diagnostics : Report
 
 public class Service : Object
 {
-       private string data_path(string path, UnsavedDocument[] unsaved)
-       {
-               foreach (var u in unsaved)
-               {
-                       if (u.path == path)
-                       {
-                               return u.data_path;
-                       }
-               }
-
-               return path;
-       }
-
-       public Document parse(string path, int64 cursor, UnsavedDocument[] unsaved, HashTable<string, 
Variant> options, Document? document)
+       public Document parse(string path, int64 cursor, string data_path, HashTable<string, Variant> 
options, Document? document)
        {
                var doc = document;
 
@@ -145,16 +132,14 @@ public class Service : Object
                        doc = new Document(path);
                }
 
-               var dpath = data_path(path, unsaved);
-
                CodeContext context = new CodeContext();
 
-               var diags = new Diagnostics(dpath);
+               var diags = new Diagnostics(data_path);
                context.report = diags;
 
                CodeContext.push(context);
 
-               var sf = new SourceFile(context, SourceFileType.SOURCE, dpath, null, true);
+               var sf = new SourceFile(context, SourceFileType.SOURCE, data_path, null, true);
                context.add_source_file(sf);
 
                Parser ast = new Parser();
diff --git a/backends/vala/types.vala b/backends/vala/types.vala
index efac33c..cf80ad3 100644
--- a/backends/vala/types.vala
+++ b/backends/vala/types.vala
@@ -20,12 +20,6 @@
 namespace Gca.Backends.Vala
 {
 
-public struct UnsavedDocument
-{
-       public string path;
-       public string data_path;
-}
-
 public struct SourceLocation
 {
        public int64 line;
diff --git a/backends/vala/valaoptionparser.vala b/backends/vala/valaoptionparser.vala
new file mode 100644
index 0000000..f88d6f9
--- /dev/null
+++ b/backends/vala/valaoptionparser.vala
@@ -0,0 +1,268 @@
+/* valaoptionparser.vala
+ *
+ * Copyright (C) 2013  Jesse van den Kieboom
+ * Copyright (C) 2006-2012  Jürg Billeter
+ * Copyright (C) 1996-2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library 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
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ *     Jürg Billeter <j bitron ch>
+ */
+
+using GLib;
+
+class Vala.OptionParser {
+       static string basedir;
+       static string directory;
+       static bool version;
+       static bool api_version;
+       [CCode (array_length = false, array_null_terminated = true)]
+       static string[] sources;
+       [CCode (array_length = false, array_null_terminated = true)]
+       static string[] vapi_directories;
+       [CCode (array_length = false, array_null_terminated = true)]
+       static string[] gir_directories;
+       [CCode (array_length = false, array_null_terminated = true)]
+       static string[] metadata_directories;
+       static string vapi_filename;
+       static string library;
+       static string gir;
+       [CCode (array_length = false, array_null_terminated = true)]
+       static string[] packages;
+       [CCode (array_length = false, array_null_terminated = true)]
+       static string[] fast_vapis;
+       static string target_glib;
+       [CCode (array_length = false, array_null_terminated = true)]
+       static string[] gresources;
+
+       static bool ccode_only;
+       static string header_filename;
+       static bool use_header;
+       static string internal_header_filename;
+       static string internal_vapi_filename;
+       static string fast_vapi_filename;
+       static string symbols_filename;
+       static string includedir;
+       static bool compile_only;
+       static string output;
+       static bool debug;
+       static bool thread;
+       static bool mem_profiler;
+       static bool disable_assert;
+       static bool enable_checking;
+       static bool deprecated;
+       static bool experimental;
+       static bool experimental_non_null;
+       static bool gobject_tracing;
+       static bool disable_warnings;
+       static string cc_command;
+       [CCode (array_length = false, array_null_terminated = true)]
+       static string[] cc_options;
+       static string dump_tree;
+       static bool save_temps;
+       [CCode (array_length = false, array_null_terminated = true)]
+       static string[] defines;
+       static bool quiet_mode;
+       static bool verbose_mode;
+       static string profile;
+       static bool nostdpkg;
+       static bool enable_version_header;
+       static bool disable_version_header;
+       static bool fatal_warnings;
+       static string dependencies;
+
+       static string entry_point;
+       static bool run_output;
+
+       static const OptionEntry[] options = {
+               { "vapidir", 0, 0, OptionArg.FILENAME_ARRAY, ref vapi_directories, "Look for package bindings 
in DIRECTORY", "DIRECTORY..." },
+               { "girdir", 0, 0, OptionArg.FILENAME_ARRAY, ref gir_directories, "Look for .gir files in 
DIRECTORY", "DIRECTORY..." },
+               { "metadatadir", 0, 0, OptionArg.FILENAME_ARRAY, ref metadata_directories, "Look for GIR 
.metadata files in DIRECTORY", "DIRECTORY..." },
+               { "pkg", 0, 0, OptionArg.STRING_ARRAY, ref packages, "Include binding for PACKAGE", 
"PACKAGE..." },
+               { "vapi", 0, 0, OptionArg.FILENAME, ref vapi_filename, "Output VAPI file name", "FILE" },
+               { "library", 0, 0, OptionArg.STRING, ref library, "Library name", "NAME" },
+               { "gir", 0, 0, OptionArg.STRING, ref gir, "GObject-Introspection repository file name", 
"NAME-VERSION.gir" },
+               { "basedir", 'b', 0, OptionArg.FILENAME, ref basedir, "Base source directory", "DIRECTORY" },
+               { "directory", 'd', 0, OptionArg.FILENAME, ref directory, "Output directory", "DIRECTORY" },
+               { "version", 0, 0, OptionArg.NONE, ref version, "Display version number", null },
+               { "api-version", 0, 0, OptionArg.NONE, ref api_version, "Display API version number", null },
+               { "ccode", 'C', 0, OptionArg.NONE, ref ccode_only, "Output C code", null },
+               { "header", 'H', 0, OptionArg.FILENAME, ref header_filename, "Output C header file", "FILE" },
+               { "use-header", 0, 0, OptionArg.NONE, ref use_header, "Use C header file", null },
+               { "includedir", 0, 0, OptionArg.FILENAME, ref includedir, "Directory used to include the C 
header file", "DIRECTORY" },
+               { "internal-header", 'h', 0, OptionArg.FILENAME, ref internal_header_filename, "Output 
internal C header file", "FILE" },
+               { "internal-vapi", 0, 0, OptionArg.FILENAME, ref internal_vapi_filename, "Output vapi with 
internal api", "FILE" },
+               { "fast-vapi", 0, 0, OptionArg.STRING, ref fast_vapi_filename, "Output vapi without 
performing symbol resolution", null },
+               { "use-fast-vapi", 0, 0, OptionArg.STRING_ARRAY, ref fast_vapis, "Use --fast-vapi output 
during this compile", null },
+               { "deps", 0, 0, OptionArg.STRING, ref dependencies, "Write make-style dependency information 
to this file", null },
+               { "symbols", 0, 0, OptionArg.FILENAME, ref symbols_filename, "Output symbols file", "FILE" },
+               { "compile", 'c', 0, OptionArg.NONE, ref compile_only, "Compile but do not link", null },
+               { "output", 'o', 0, OptionArg.FILENAME, ref output, "Place output in file FILE", "FILE" },
+               { "debug", 'g', 0, OptionArg.NONE, ref debug, "Produce debug information", null },
+               { "thread", 0, 0, OptionArg.NONE, ref thread, "Enable multithreading support", null },
+               { "enable-mem-profiler", 0, 0, OptionArg.NONE, ref mem_profiler, "Enable GLib memory 
profiler", null },
+               { "define", 'D', 0, OptionArg.STRING_ARRAY, ref defines, "Define SYMBOL", "SYMBOL..." },
+               { "main", 0, 0, OptionArg.STRING, ref entry_point, "Use SYMBOL as entry point", "SYMBOL..." },
+               { "nostdpkg", 0, 0, OptionArg.NONE, ref nostdpkg, "Do not include standard packages", null },
+               { "disable-assert", 0, 0, OptionArg.NONE, ref disable_assert, "Disable assertions", null },
+               { "enable-checking", 0, 0, OptionArg.NONE, ref enable_checking, "Enable additional run-time 
checks", null },
+               { "enable-deprecated", 0, 0, OptionArg.NONE, ref deprecated, "Enable deprecated features", 
null },
+               { "enable-experimental", 0, 0, OptionArg.NONE, ref experimental, "Enable experimental 
features", null },
+               { "disable-warnings", 0, 0, OptionArg.NONE, ref disable_warnings, "Disable warnings", null },
+               { "fatal-warnings", 0, 0, OptionArg.NONE, ref fatal_warnings, "Treat warnings as fatal", null 
},
+               { "enable-experimental-non-null", 0, 0, OptionArg.NONE, ref experimental_non_null, "Enable 
experimental enhancements for non-null types", null },
+               { "enable-gobject-tracing", 0, 0, OptionArg.NONE, ref gobject_tracing, "Enable GObject 
creation tracing", null },
+               { "cc", 0, 0, OptionArg.STRING, ref cc_command, "Use COMMAND as C compiler command", 
"COMMAND" },
+               { "Xcc", 'X', 0, OptionArg.STRING_ARRAY, ref cc_options, "Pass OPTION to the C compiler", 
"OPTION..." },
+               { "dump-tree", 0, 0, OptionArg.FILENAME, ref dump_tree, "Write code tree to FILE", "FILE" },
+               { "save-temps", 0, 0, OptionArg.NONE, ref save_temps, "Keep temporary files", null },
+               { "profile", 0, 0, OptionArg.STRING, ref profile, "Use the given profile instead of the 
default", "PROFILE" },
+               { "quiet", 'q', 0, OptionArg.NONE, ref quiet_mode, "Do not print messages to the console", 
null },
+               { "verbose", 'v', 0, OptionArg.NONE, ref verbose_mode, "Print additional messages to the 
console", null },
+               { "target-glib", 0, 0, OptionArg.STRING, ref target_glib, "Target version of glib for code 
generation", "MAJOR.MINOR" },
+               { "gresources", 0, 0, OptionArg.STRING_ARRAY, ref gresources, "XML of gresources", "FILE..." 
},
+               { "enable-version-header", 0, 0, OptionArg.NONE, ref enable_version_header, "Write vala build 
version in generated files", null },
+               { "disable-version-header", 0, 0, OptionArg.NONE, ref disable_version_header, "Do not write 
vala build version in generated files", null },
+               { "", 0, 0, OptionArg.FILENAME_ARRAY, ref sources, null, "FILE..." },
+               { null }
+       };
+
+       private static bool apply (global::Vala.CodeContext context, string cwd) {
+               context.assert = !disable_assert;
+               context.checking = enable_checking;
+               context.deprecated = deprecated;
+               context.experimental = experimental;
+               context.experimental_non_null = experimental_non_null;
+               context.gobject_tracing = gobject_tracing;
+               context.report.enable_warnings = !disable_warnings;
+               context.report.set_verbose_errors (!quiet_mode);
+               context.verbose_mode = verbose_mode;
+               context.version_header = !disable_version_header;
+
+               context.ccode_only = ccode_only;
+               context.compile_only = compile_only;
+               context.header_filename = header_filename;
+               if (header_filename == null && use_header) {
+                       Report.error (null, "--use-header may only be used in combination with --header");
+               }
+               context.use_header = use_header;
+               context.internal_header_filename = internal_header_filename;
+               context.symbols_filename = symbols_filename;
+               context.includedir = includedir;
+               context.output = output;
+               if (basedir == null) {
+                       context.basedir = CodeContext.realpath (cwd);
+               } else {
+                       context.basedir = CodeContext.realpath (basedir);
+               }
+               if (directory != null) {
+                       context.directory = CodeContext.realpath (directory);
+               } else {
+                       context.directory = context.basedir;
+               }
+               context.vapi_directories = vapi_directories;
+               context.gir_directories = gir_directories;
+               context.metadata_directories = metadata_directories;
+               context.debug = debug;
+               context.thread = thread;
+               context.mem_profiler = mem_profiler;
+               context.save_temps = save_temps;
+               if (profile == "gobject-2.0" || profile == "gobject" || profile == null) {
+                       // default profile
+                       context.profile = Profile.GOBJECT;
+                       context.add_define ("GOBJECT");
+               } else {
+                       Report.error (null, "Unknown profile %s".printf (profile));
+               }
+               nostdpkg |= fast_vapi_filename != null;
+               context.nostdpkg = nostdpkg;
+
+               context.entry_point_name = entry_point;
+
+               context.run_output = run_output;
+
+               if (defines != null) {
+                       foreach (string define in defines) {
+                               context.add_define (define);
+                       }
+               }
+
+               for (int i = 2; i <= 24; i += 2) {
+                       context.add_define ("VALA_0_%d".printf (i));
+               }
+
+               int glib_major = 2;
+               int glib_minor = 18;
+               if (target_glib != null && target_glib.scanf ("%d.%d", out glib_major, out glib_minor) != 2) {
+                       Report.error (null, "Invalid format for --target-glib");
+               }
+
+               context.target_glib_major = glib_major;
+               context.target_glib_minor = glib_minor;
+               if (context.target_glib_major != 2) {
+                       Report.error (null, "This version of valac only supports GLib 2");
+               }
+
+               for (int i = 16; i <= glib_minor; i += 2) {
+                       context.add_define ("GLIB_2_%d".printf (i));
+               }
+
+               if (!nostdpkg) {
+                       /* default packages */
+                       context.add_external_package ("glib-2.0");
+                       context.add_external_package ("gobject-2.0");
+               }
+
+               if (packages != null) {
+                       foreach (string package in packages) {
+                               context.add_external_package (package);
+                       }
+                       packages = null;
+               }
+
+               if (fast_vapis != null) {
+                       foreach (string vapi in fast_vapis) {
+                               var rpath = CodeContext.realpath (vapi);
+                               var source_file = new SourceFile (context, SourceFileType.FAST, rpath);
+                               context.add_source_file (source_file);
+                       }
+                       context.use_fast_vapi = true;
+               }
+
+               return (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () 
0));
+       }
+
+       public static bool parse(string[] args) {
+               try {
+                       var opt_context = new OptionContext ("- Vala Compiler");
+                       opt_context.set_help_enabled (false);
+                       opt_context.add_main_entries (options, null);
+                       opt_context.set_ignore_unknown_options (true);
+                       opt_context.parse (ref args);
+               } catch { return false; }
+
+               return true;
+       }
+
+       public static bool parse_and_apply(string cwd, global::Vala.CodeContext context, string[] args) {
+               if (!parse(args))
+               {
+                       return false;
+               }
+
+               return apply(context, cwd);
+       }
+}



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