Remove old SCM API

This commit is contained in:
Allan Odgaard
2013-02-21 17:01:24 +01:00
parent c3e9fafb2d
commit af4f1e16a1
5 changed files with 361 additions and 631 deletions

View File

@@ -1,205 +1,385 @@
#include "drivers/api.h"
#include "scm.h"
#include "drivers/api.h"
#include "snapshot.h"
#include "fs_events.h"
#include <io/path.h>
#include <cf/cf.h>
#include <oak/oak.h>
#include <text/format.h>
#include <settings/settings.h>
OAK_DEBUG_VAR(SCM);
namespace scm
{
static std::map<std::string, info_ptr>& cache () { static std::map<std::string, info_ptr> cache; return cache; }
driver_t* git_driver ();
driver_t* hg_driver ();
driver_t* p4_driver ();
driver_t* svn_driver ();
} /* scm */
namespace scm { namespace ng
{
static bool Disabled = false;
static std::map<std::string, shared_info_weak_ptr> PendingUpdates;
// =================
// = shared_info_t =
// =================
struct shared_info_t : std::enable_shared_from_this<shared_info_t>
{
shared_info_t (std::string const& rootPath, scm::driver_t const* driver);
~shared_info_t ();
std::string const& root_path () const { return _root_path; }
std::map<std::string, std::string> const& variables () const { return _variables; }
std::map<std::string, scm::status::type> const& status () const { return _status; }
bool tracks_directories () const { return _driver->tracks_directories(); }
void add_client (info_t* client);
void remove_client (info_t* client);
private:
friend void enable ();
void schedule_update ();
static void async_update (shared_info_weak_ptr weakThis);
void fs_did_change (std::set<std::string> const& changedPaths);
void update (std::map<std::string, std::string> const& variables, std::map<std::string, scm::status::type> const& status, fs::snapshot_t const& fsSnapshot);
std::string _root_path;
scm::driver_t const* _driver;
std::map<std::string, std::string> _variables;
std::map<std::string, scm::status::type> _status;
fs::snapshot_t _fs_snapshot;
bool _pending_update = false;
std::chrono::steady_clock::time_point _no_check_before = std::chrono::steady_clock::now();
std::shared_ptr<watcher_t> _watcher;
std::set<info_t*> _clients;
};
// ==========
// = info_t =
// ==========
info_t::info_t (std::string const& wcPath, driver_t const* driver) : _wc_path(wcPath), _driver(driver)
info_t::info_t (std::string const& path)
{
ASSERTF(path::is_directory(_wc_path) || !path::exists(_wc_path) || _wc_path == NULL_STR, "Path: %s\n", _wc_path.c_str());
}
std::string info_t::scm_name () const { auto vars = variables(); auto iter = vars.find("TM_SCM_NAME"); return iter != vars.end() ? iter->second : NULL_STR; }
std::string info_t::path () const { return _wc_path; }
std::string info_t::branch () const { auto vars = variables(); auto iter = vars.find("TM_SCM_BRANCH"); return iter != vars.end() ? iter->second : NULL_STR; }
bool info_t::tracks_directories () const { return _driver->tracks_directories(); }
void info_t::setup ()
info_t::~info_t ()
{
if(_did_setup)
return;
if(_shared_info)
_shared_info->remove_client(this);
_file_status = _driver->status(_wc_path);
_variables = std::map<std::string, std::string>{
{ "TM_SCM_NAME", _driver->name() },
{ "TM_SCM_BRANCH", _driver->branch_name(_wc_path) }
};
_watcher.reset(new scm::watcher_t(_wc_path, std::bind(&info_t::callback, this, std::placeholders::_1)));
_did_setup = true;
for(auto block : _callbacks)
Block_release(block);
}
status::type info_t::status (std::string const& path)
void info_t::set_shared_info (shared_info_ptr sharedInfo)
{
D(DBF_SCM, bug("%s\n", path.c_str()););
setup();
status_map_t::const_iterator res = _file_status.find(path);
return res != _file_status.end() ? res->second : status::none;
if(_shared_info)
_shared_info->remove_client(this);
if(_shared_info = sharedInfo)
_shared_info->add_client(this);
}
bool info_t::dry () const
{
return !_shared_info;
}
std::string info_t::root_path () const
{
return dry() ? NULL_STR : _shared_info->root_path();
}
std::map<std::string, std::string> info_t::variables () const
{
const_cast<info_t*>(this)->setup();
return _variables;
return dry() ? std::map<std::string, std::string>() : _shared_info->variables();
}
status_map_t info_t::files_with_status (int mask)
std::map<std::string, scm::status::type> const& info_t::status () const
{
setup();
static std::map<std::string, scm::status::type> const EmptyMap;
return dry() ? EmptyMap : _shared_info->status();
}
status_map_t res;
iterate(status, _file_status)
scm::status::type info_t::status (std::string const& path) const
{
auto const& map = status();
auto it = map.find(path);
return dry() || path == NULL_STR ? scm::status::unknown : (it != map.end() ? it->second : scm::status::none);
}
bool info_t::tracks_directories () const
{
return dry() ? false : _shared_info->tracks_directories();
}
void info_t::add_callback (void (^block)(info_t const&))
{
_callbacks.push_back(Block_copy(block));
if(!dry())
did_update_shared_info();
}
void info_t::did_update_shared_info ()
{
for(auto callback : _callbacks)
callback(*this);
}
// =================
// = shared_info_t =
// =================
shared_info_t::shared_info_t (std::string const& rootPath, scm::driver_t const* driver) : _root_path(rootPath), _driver(driver)
{
}
shared_info_t::~shared_info_t ()
{
}
void shared_info_t::add_client (info_t* client)
{
_clients.insert(client);
if(_clients.size() == 1)
{
if((status->second & mask) == status->second)
res.insert(*status);
_watcher.reset(new scm::watcher_t(_root_path, std::bind(&shared_info_t::fs_did_change, this, std::placeholders::_1)));
schedule_update();
}
else
{
client->did_update_shared_info();
}
}
void shared_info_t::remove_client (info_t* client)
{
if(_clients.size() == 1)
_watcher.reset();
_clients.erase(client);
}
void shared_info_t::update (std::map<std::string, std::string> const& variables, std::map<std::string, scm::status::type> const& status, fs::snapshot_t const& fsSnapshot)
{
bool shouldNotify = _variables != variables || _status != status;
_variables = variables;
_status = status;
_fs_snapshot = fsSnapshot;
if(shouldNotify)
{
for(info_t* client : _clients)
client->did_update_shared_info();
}
}
// =================
// = Update Status =
// =================
void shared_info_t::async_update (shared_info_weak_ptr weakThis)
{
if(shared_info_ptr info = weakThis.lock())
{
if(!info->_driver->may_touch_filesystem() || info->_fs_snapshot != fs::snapshot_t(info->_root_path))
{
std::map<std::string, std::string> variables{ { "TM_SCM_NAME", info->_driver->name() } };
std::string const branch = info->_driver->branch_name(info->_root_path);
if(branch != NULL_STR)
variables.insert(std::make_pair("TM_SCM_BRANCH", branch));
scm::status_map_t const status = info->_driver->status(info->_root_path);
fs::snapshot_t const snapshot = info->_driver->may_touch_filesystem() ? fs::snapshot_t(info->_root_path) : fs::snapshot_t();
dispatch_async(dispatch_get_main_queue(), ^{
info->update(variables, status, snapshot);
});
}
dispatch_async(dispatch_get_main_queue(), ^{
info->_pending_update = false;
info->_no_check_before = std::chrono::steady_clock::now() + std::chrono::seconds(3);
});
}
}
void shared_info_t::schedule_update ()
{
if(Disabled)
{
PendingUpdates[_root_path] = shared_from_this();
return;
}
if(_pending_update)
return;
_pending_update = true;
auto now = std::chrono::steady_clock::now();
dispatch_time_t delay = now < _no_check_before ? dispatch_time(DISPATCH_TIME_NOW, std::chrono::duration_cast<std::chrono::duration<int64_t, std::nano>>(_no_check_before - now).count()) : DISPATCH_TIME_NOW;
shared_info_weak_ptr weakThis = shared_from_this();
static dispatch_queue_t queue = dispatch_queue_create("org.textmate.scm.status", DISPATCH_QUEUE_SERIAL);
dispatch_after(delay, queue, ^{
async_update(weakThis);
});
}
void shared_info_t::fs_did_change (std::set<std::string> const& changedPaths)
{
schedule_update();
}
// =========
// = Other =
// =========
static std::map<std::string, shared_info_weak_ptr>& cache ()
{
static auto res = new std::map<std::string, shared_info_weak_ptr>;
return *res;
}
static dispatch_queue_t cache_access_queue ()
{
static dispatch_queue_t res = dispatch_queue_create("org.textmate.scm.info-cache", DISPATCH_QUEUE_SERIAL);
return res;
}
// ====================
// = Callback Related =
// ====================
void info_t::add_callback (callback_t* cb)
static std::vector<driver_t*> const& drivers ()
{
D(DBF_SCM, bug("%s / %p\n", path().c_str(), cb););
_callbacks.add(cb);
cb->status_changed(*this, std::set<std::string>());
static auto const res = new std::vector<driver_t*>{ scm::git_driver(), scm::hg_driver(), scm::p4_driver(), scm::svn_driver() };
return *res;
}
void info_t::remove_callback (callback_t* cb)
static shared_info_ptr find_shared_info_for (std::string const& path)
{
_callbacks.remove(cb);
}
static bool test_and_set (std::string const& key, bool flag)
{
static dispatch_queue_t queue = dispatch_queue_create("org.textmate.scm.coordination", DISPATCH_QUEUE_SERIAL);
static std::set<std::string> keys;
__block bool foundKey = false;
dispatch_sync(queue, ^{
auto it = keys.find(key);
foundKey = it != keys.end();
if(flag && !foundKey)
keys.insert(key);
else if(!flag && foundKey)
keys.erase(it);
});
return foundKey;
}
void info_t::callback (std::set<std::string> const& pathsChangedOnDisk)
{
D(DBF_SCM, bug("( %s )\n", text::join(pathsChangedOnDisk, ", ").c_str()););
if(test_and_set(_wc_path, true))
return;
static dispatch_queue_t queue = dispatch_queue_create("org.textmate.scm.status", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
double elapsed = oak::date_t::now() - _updated;
if(elapsed < 3)
usleep((3 - elapsed) * 1000000);
bool shouldCheck = !_driver->may_touch_filesystem() || _snapshot != fs::snapshot_t(_wc_path);
test_and_set(_wc_path, false);
if(shouldCheck)
{
scm::status_map_t const status = _driver->status(_wc_path);
std::map<std::string, std::string> const variables{
{ "TM_SCM_NAME", _driver->name() },
{ "TM_SCM_BRANCH", _driver->branch_name(_wc_path) }
};
fs::snapshot_t const snapshot = _driver->may_touch_filesystem() ? fs::snapshot_t(_wc_path) : fs::snapshot_t();
dispatch_async(dispatch_get_main_queue(), ^{
update_status(_wc_path, snapshot, status, variables);
});
}
});
}
void info_t::update_status (std::string const& path, fs::snapshot_t const& snapshot, scm::status_map_t const& newStatus, std::map<std::string, std::string> const& newVariables)
{
auto it = cache().find(path);
if(it != cache().end())
for(std::string cwd = path; cwd != "/"; cwd = path::parent(cwd))
{
std::set<std::string> changedPaths;
auto const oldStatus = it->second->_file_status;
auto oldStatusIter = oldStatus.begin();
auto newStatusIter = newStatus.begin();
while(oldStatusIter != oldStatus.end() || newStatusIter != newStatus.end())
auto it = cache().find(cwd);
if(it != cache().end())
{
if(newStatusIter == newStatus.end() || oldStatusIter != oldStatus.end() && oldStatusIter->first < newStatusIter->first)
{
changedPaths.insert(oldStatusIter->first);
++oldStatusIter;
continue;
}
if(oldStatusIter == oldStatus.end() || newStatusIter != newStatus.end() && newStatusIter->first < oldStatusIter->first)
{
changedPaths.insert(newStatusIter->first);
++newStatusIter;
continue;
}
if(oldStatusIter->second != newStatusIter->second)
changedPaths.insert(newStatusIter->first);
++oldStatusIter;
++newStatusIter;
if(shared_info_ptr res = it->second.lock())
return res;
}
it->second->_updated = oak::date_t::now();
it->second->_snapshot = snapshot;
it->second->_file_status = newStatus;
it->second->_variables = newVariables;
it->second->_callbacks(&callback_t::status_changed, *it->second, changedPaths);
for(driver_t* driver : drivers())
{
if(driver && driver->has_info_for_directory(cwd))
{
shared_info_ptr res(new shared_info_t(cwd, driver));
cache()[cwd] = res;
return res;
}
}
}
return shared_info_ptr();
}
// ==============
// = Public API =
// ==============
info_ptr info (std::string const& dir)
static bool scm_enabled_for_path (std::string const& path)
{
if(!settings_for_path(NULL_STR, scope::scope_t(), dir).get(kSettingsSCMStatusKey, true))
return path != "" && path != "/" && path[0] == '/' && path != path::home() && path::is_local(path) && settings_for_path(NULL_STR, "", path).get(kSettingsSCMStatusKey, true);
}
void disable ()
{
Disabled = true;
}
void enable ()
{
Disabled = false;
for(auto pair : PendingUpdates)
{
if(shared_info_ptr info = pair.second.lock())
info->schedule_update();
}
PendingUpdates.clear();
}
std::string root_for_path (std::string const& path)
{
if(!scm_enabled_for_path(path))
return NULL_STR;
__block std::string res = NULL_STR;
dispatch_sync(cache_access_queue(), ^{
for(std::string cwd = path; res == NULL_STR && cwd != "/"; cwd = path::parent(cwd))
{
auto it = cache().find(cwd);
if(it != cache().end())
{
res = cwd;
}
else
{
for(driver_t* driver : drivers())
{
if(driver && driver->has_info_for_directory(cwd))
{
res = cwd;
break;
}
}
}
}
});
return res;
}
info_ptr info (std::string path)
{
if(!scm_enabled_for_path(path))
return info_ptr();
std::string wcPath;
if(driver_t const* driver = driver_for_path(dir, &wcPath))
info_ptr res(new info_t(path));
__block bool performBackgroundDriverSearch = true;
dispatch_sync(cache_access_queue(), ^{
for(std::string cwd = path; cwd != "/"; cwd = path::parent(cwd))
{
auto it = cache().find(cwd);
if(it != cache().end())
{
if(shared_info_ptr sharedInfo = it->second.lock())
{
res->set_shared_info(sharedInfo);
performBackgroundDriverSearch = cwd != path;
}
break;
}
}
});
if(performBackgroundDriverSearch)
{
if(wcPath == NULL_STR || wcPath == "/" || wcPath == path::home() || !path::is_local(wcPath))
return info_ptr();
std::map<std::string, info_ptr>::iterator it = cache().find(wcPath);
if(it == cache().end())
it = cache().insert(std::make_pair(wcPath, info_ptr(new info_t(wcPath, driver)))).first;
return it->second;
weak_info_ptr weakInfo = res;
dispatch_async(cache_access_queue(), ^{
if(shared_info_ptr sharedInfo = find_shared_info_for(path))
{
dispatch_async(dispatch_get_main_queue(), ^{
if(info_ptr info = weakInfo.lock())
info->set_shared_info(sharedInfo);
});
}
});
}
return info_ptr();
return res;
}
status_map_t tracked_files (std::string const& dir, int mask)
void wait_for_status (info_ptr info)
{
if(info_ptr const& driver = info(path::join(dir, ".scm-kludge")))
return driver->files_with_status(mask);
return status_map_t();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
info->add_callback(^(scm::ng::info_t const& unused){
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// FIXME remove_callback
dispatch_release(semaphore);
}
}
} /* ng */ } /* scm */

View File

@@ -1,65 +1,50 @@
#ifndef OAKSCM_H_SUWJ53QQ
#define OAKSCM_H_SUWJ53QQ
#ifndef SCM_NG_H_FXJGXN9B
#define SCM_NG_H_FXJGXN9B
#include "scm_ng.h"
#include "status.h"
#include "snapshot.h"
#include <plist/date.h>
#include <oak/debug.h>
#include <oak/callbacks.h>
#include <oak/duration.h>
namespace scm
namespace scm { namespace ng
{
struct driver_t;
struct info_t;
typedef std::shared_ptr<info_t> info_ptr;
typedef std::weak_ptr<info_t> weak_info_ptr;
struct PUBLIC callback_t
{
virtual void status_changed (scm::info_t const& info, std::set<std::string> const& changedPaths) = 0;
virtual ~callback_t () { }
};
struct watcher_t;
struct shared_info_t;
typedef std::shared_ptr<shared_info_t> shared_info_ptr;
typedef std::weak_ptr<shared_info_t> shared_info_weak_ptr;
struct PUBLIC info_t
{
info_t (std::string const& wcPath, driver_t const* driver);
info_t (std::string const& path);
~info_t ();
std::string scm_name () const;
std::string path () const;
std::string branch () const;
bool tracks_directories () const;
status::type status (std::string const& path);
bool dry () const;
std::string root_path () const;
std::map<std::string, std::string> variables () const;
status_map_t files_with_status (int mask);
std::map<std::string, scm::status::type> const& status () const;
scm::status::type status (std::string const& path) const;
void add_callback (callback_t* cb);
void remove_callback (callback_t* cb);
bool tracks_directories () const;
void add_callback (void (^block)(info_t const&));
private:
void setup ();
bool _did_setup = false;
friend info_ptr info (std::string path);
void set_shared_info (shared_info_ptr info);
std::string _wc_path;
driver_t const* _driver;
status_map_t _file_status;
std::map<std::string, std::string> _variables;
oak::date_t _updated;
fs::snapshot_t _snapshot;
friend shared_info_t;
void did_update_shared_info ();
std::shared_ptr<scm::watcher_t> _watcher;
void callback (std::set<std::string> const& pathsChangedOnDisk);
oak::callbacks_t<callback_t> _callbacks;
static void update_status (std::string const& path, fs::snapshot_t const& snapshot, scm::status_map_t const& status, std::map<std::string, std::string> const& variables);
std::vector<void(^)(info_t const&)> _callbacks;
shared_info_ptr _shared_info;
};
PUBLIC info_ptr info (std::string const& dir);
PUBLIC status_map_t tracked_files (std::string const& dir, int mask);
PUBLIC void disable ();
PUBLIC void enable ();
PUBLIC std::string root_for_path (std::string const& path);
PUBLIC info_ptr info (std::string path);
PUBLIC void wait_for_status (info_ptr info);
} /* scm */
} /* ng */ } /* scm */
#endif /* end of include guard: OAKSCM_H_SUWJ53QQ */
#endif /* end of include guard: SCM_NG_H_FXJGXN9B */

View File

@@ -1,385 +0,0 @@
#include "scm.h"
#include "drivers/api.h"
#include "snapshot.h"
#include "fs_events.h"
#include <io/path.h>
#include <text/format.h>
#include <settings/settings.h>
namespace scm
{
driver_t* git_driver ();
driver_t* hg_driver ();
driver_t* p4_driver ();
driver_t* svn_driver ();
} /* scm */
namespace scm { namespace ng
{
static bool Disabled = false;
static std::map<std::string, shared_info_weak_ptr> PendingUpdates;
// =================
// = shared_info_t =
// =================
struct shared_info_t : std::enable_shared_from_this<shared_info_t>
{
shared_info_t (std::string const& rootPath, scm::driver_t const* driver);
~shared_info_t ();
std::string const& root_path () const { return _root_path; }
std::map<std::string, std::string> const& variables () const { return _variables; }
std::map<std::string, scm::status::type> const& status () const { return _status; }
bool tracks_directories () const { return _driver->tracks_directories(); }
void add_client (info_t* client);
void remove_client (info_t* client);
private:
friend void enable ();
void schedule_update ();
static void async_update (shared_info_weak_ptr weakThis);
void fs_did_change (std::set<std::string> const& changedPaths);
void update (std::map<std::string, std::string> const& variables, std::map<std::string, scm::status::type> const& status, fs::snapshot_t const& fsSnapshot);
std::string _root_path;
scm::driver_t const* _driver;
std::map<std::string, std::string> _variables;
std::map<std::string, scm::status::type> _status;
fs::snapshot_t _fs_snapshot;
bool _pending_update = false;
std::chrono::steady_clock::time_point _no_check_before = std::chrono::steady_clock::now();
std::shared_ptr<watcher_t> _watcher;
std::set<info_t*> _clients;
};
// ==========
// = info_t =
// ==========
info_t::info_t (std::string const& path)
{
}
info_t::~info_t ()
{
if(_shared_info)
_shared_info->remove_client(this);
for(auto block : _callbacks)
Block_release(block);
}
void info_t::set_shared_info (shared_info_ptr sharedInfo)
{
if(_shared_info)
_shared_info->remove_client(this);
if(_shared_info = sharedInfo)
_shared_info->add_client(this);
}
bool info_t::dry () const
{
return !_shared_info;
}
std::string info_t::root_path () const
{
return dry() ? NULL_STR : _shared_info->root_path();
}
std::map<std::string, std::string> info_t::variables () const
{
return dry() ? std::map<std::string, std::string>() : _shared_info->variables();
}
std::map<std::string, scm::status::type> const& info_t::status () const
{
static std::map<std::string, scm::status::type> const EmptyMap;
return dry() ? EmptyMap : _shared_info->status();
}
scm::status::type info_t::status (std::string const& path) const
{
auto const& map = status();
auto it = map.find(path);
return dry() || path == NULL_STR ? scm::status::unknown : (it != map.end() ? it->second : scm::status::none);
}
bool info_t::tracks_directories () const
{
return dry() ? false : _shared_info->tracks_directories();
}
void info_t::add_callback (void (^block)(info_t const&))
{
_callbacks.push_back(Block_copy(block));
if(!dry())
did_update_shared_info();
}
void info_t::did_update_shared_info ()
{
for(auto callback : _callbacks)
callback(*this);
}
// =================
// = shared_info_t =
// =================
shared_info_t::shared_info_t (std::string const& rootPath, scm::driver_t const* driver) : _root_path(rootPath), _driver(driver)
{
}
shared_info_t::~shared_info_t ()
{
}
void shared_info_t::add_client (info_t* client)
{
_clients.insert(client);
if(_clients.size() == 1)
{
_watcher.reset(new scm::watcher_t(_root_path, std::bind(&shared_info_t::fs_did_change, this, std::placeholders::_1)));
schedule_update();
}
else
{
client->did_update_shared_info();
}
}
void shared_info_t::remove_client (info_t* client)
{
if(_clients.size() == 1)
_watcher.reset();
_clients.erase(client);
}
void shared_info_t::update (std::map<std::string, std::string> const& variables, std::map<std::string, scm::status::type> const& status, fs::snapshot_t const& fsSnapshot)
{
bool shouldNotify = _variables != variables || _status != status;
_variables = variables;
_status = status;
_fs_snapshot = fsSnapshot;
if(shouldNotify)
{
for(info_t* client : _clients)
client->did_update_shared_info();
}
}
// =================
// = Update Status =
// =================
void shared_info_t::async_update (shared_info_weak_ptr weakThis)
{
if(shared_info_ptr info = weakThis.lock())
{
if(!info->_driver->may_touch_filesystem() || info->_fs_snapshot != fs::snapshot_t(info->_root_path))
{
std::map<std::string, std::string> variables{ { "TM_SCM_NAME", info->_driver->name() } };
std::string const branch = info->_driver->branch_name(info->_root_path);
if(branch != NULL_STR)
variables.insert(std::make_pair("TM_SCM_BRANCH", branch));
scm::status_map_t const status = info->_driver->status(info->_root_path);
fs::snapshot_t const snapshot = info->_driver->may_touch_filesystem() ? fs::snapshot_t(info->_root_path) : fs::snapshot_t();
dispatch_async(dispatch_get_main_queue(), ^{
info->update(variables, status, snapshot);
});
}
dispatch_async(dispatch_get_main_queue(), ^{
info->_pending_update = false;
info->_no_check_before = std::chrono::steady_clock::now() + std::chrono::seconds(3);
});
}
}
void shared_info_t::schedule_update ()
{
if(Disabled)
{
PendingUpdates[_root_path] = shared_from_this();
return;
}
if(_pending_update)
return;
_pending_update = true;
auto now = std::chrono::steady_clock::now();
dispatch_time_t delay = now < _no_check_before ? dispatch_time(DISPATCH_TIME_NOW, std::chrono::duration_cast<std::chrono::duration<int64_t, std::nano>>(_no_check_before - now).count()) : DISPATCH_TIME_NOW;
shared_info_weak_ptr weakThis = shared_from_this();
static dispatch_queue_t queue = dispatch_queue_create("org.textmate.scm.status", DISPATCH_QUEUE_SERIAL);
dispatch_after(delay, queue, ^{
async_update(weakThis);
});
}
void shared_info_t::fs_did_change (std::set<std::string> const& changedPaths)
{
schedule_update();
}
// =========
// = Other =
// =========
static std::map<std::string, shared_info_weak_ptr>& cache ()
{
static auto res = new std::map<std::string, shared_info_weak_ptr>;
return *res;
}
static dispatch_queue_t cache_access_queue ()
{
static dispatch_queue_t res = dispatch_queue_create("org.textmate.scm.info-cache", DISPATCH_QUEUE_SERIAL);
return res;
}
static std::vector<driver_t*> const& drivers ()
{
static auto const res = new std::vector<driver_t*>{ scm::git_driver(), scm::hg_driver(), scm::p4_driver(), scm::svn_driver() };
return *res;
}
static shared_info_ptr find_shared_info_for (std::string const& path)
{
for(std::string cwd = path; cwd != "/"; cwd = path::parent(cwd))
{
auto it = cache().find(cwd);
if(it != cache().end())
{
if(shared_info_ptr res = it->second.lock())
return res;
}
for(driver_t* driver : drivers())
{
if(driver && driver->has_info_for_directory(cwd))
{
shared_info_ptr res(new shared_info_t(cwd, driver));
cache()[cwd] = res;
return res;
}
}
}
return shared_info_ptr();
}
static bool scm_enabled_for_path (std::string const& path)
{
return path != "" && path != "/" && path[0] == '/' && path != path::home() && path::is_local(path) && settings_for_path(NULL_STR, "", path).get(kSettingsSCMStatusKey, true);
}
void disable ()
{
Disabled = true;
}
void enable ()
{
Disabled = false;
for(auto pair : PendingUpdates)
{
if(shared_info_ptr info = pair.second.lock())
info->schedule_update();
}
PendingUpdates.clear();
}
std::string root_for_path (std::string const& path)
{
if(!scm_enabled_for_path(path))
return NULL_STR;
__block std::string res = NULL_STR;
dispatch_sync(cache_access_queue(), ^{
for(std::string cwd = path; res == NULL_STR && cwd != "/"; cwd = path::parent(cwd))
{
auto it = cache().find(cwd);
if(it != cache().end())
{
res = cwd;
}
else
{
for(driver_t* driver : drivers())
{
if(driver && driver->has_info_for_directory(cwd))
{
res = cwd;
break;
}
}
}
}
});
return res;
}
info_ptr info (std::string path)
{
if(!scm_enabled_for_path(path))
return info_ptr();
info_ptr res(new info_t(path));
__block bool performBackgroundDriverSearch = true;
dispatch_sync(cache_access_queue(), ^{
for(std::string cwd = path; cwd != "/"; cwd = path::parent(cwd))
{
auto it = cache().find(cwd);
if(it != cache().end())
{
if(shared_info_ptr sharedInfo = it->second.lock())
{
res->set_shared_info(sharedInfo);
performBackgroundDriverSearch = cwd != path;
}
break;
}
}
});
if(performBackgroundDriverSearch)
{
weak_info_ptr weakInfo = res;
dispatch_async(cache_access_queue(), ^{
if(shared_info_ptr sharedInfo = find_shared_info_for(path))
{
dispatch_async(dispatch_get_main_queue(), ^{
if(info_ptr info = weakInfo.lock())
info->set_shared_info(sharedInfo);
});
}
});
}
return res;
}
void wait_for_status (info_ptr info)
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
info->add_callback(^(scm::ng::info_t const& unused){
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// FIXME remove_callback
dispatch_release(semaphore);
}
} /* ng */ } /* scm */

View File

@@ -1,50 +0,0 @@
#ifndef SCM_NG_H_FXJGXN9B
#define SCM_NG_H_FXJGXN9B
#include "status.h"
namespace scm { namespace ng
{
struct info_t;
typedef std::shared_ptr<info_t> info_ptr;
typedef std::weak_ptr<info_t> weak_info_ptr;
struct shared_info_t;
typedef std::shared_ptr<shared_info_t> shared_info_ptr;
typedef std::weak_ptr<shared_info_t> shared_info_weak_ptr;
struct PUBLIC info_t
{
info_t (std::string const& path);
~info_t ();
bool dry () const;
std::string root_path () const;
std::map<std::string, std::string> variables () const;
std::map<std::string, scm::status::type> const& status () const;
scm::status::type status (std::string const& path) const;
bool tracks_directories () const;
void add_callback (void (^block)(info_t const&));
private:
friend info_ptr info (std::string path);
void set_shared_info (shared_info_ptr info);
friend shared_info_t;
void did_update_shared_info ();
std::vector<void(^)(info_t const&)> _callbacks;
shared_info_ptr _shared_info;
};
PUBLIC void disable ();
PUBLIC void enable ();
PUBLIC std::string root_for_path (std::string const& path);
PUBLIC info_ptr info (std::string path);
PUBLIC void wait_for_status (info_ptr info);
} /* ng */ } /* scm */
#endif /* end of include guard: SCM_NG_H_FXJGXN9B */

View File

@@ -1,6 +1,6 @@
SOURCES = src/**/*.cc
TESTS = tests/t_*.cc
CP_Resources = resources/*
EXPORT = src/{scm{,_ng},status,snapshot}.h
EXPORT = src/{scm,status,snapshot}.h
LINK += text cf io settings
FRAMEWORKS = Carbon Security