[gnome-code-assistance/wip/arch] wip
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-code-assistance/wip/arch] wip
- Date: Sun, 10 Nov 2013 23:23:43 +0000 (UTC)
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]