sourcecamembert::PluginManager.fan

// History:
//  Jan 08 13 tcolar Creation
//

using gfx
using concurrent

**
** Manages plugins
**
const class PluginManager : Service
{
  ** Pod name / Plugin implementation instance map
  const Str:Plugin plugins := [:] {ordered = true}

  const File configDir

  ** Plugin name(pod) -> PluginConfig of each plugin
  private const AtomicRef _pluginConfs := AtomicRef()

  new make(File configDir)
  {
    this.configDir = configDir
    pods := Pod.list.findAll {it.meta.containsKey("camembert.plugin")}
    Str:Plugin temp := [:]

    // find the plugins
    pods.sort|a, b|{a.name <=> b.name}.each |pod|
    {
      typeName := pod.meta["camembert.plugin"]
      type := pod.type(typeName, false)
      if(type == null)
        Sys.log.info("Type $typeName not found for plugin $pod.name !")
      else if( ! type.fits(Plugin#))
        Sys.log.info("Type $typeName of plugin $pod.name doesn't implement Plugin mixin !")
      else
      {
        try
        {
          temp[pod.name] = (Plugin) type.make
          Sys.log.info("Found plugin : ${pod.name}.$typeName")
        }
        catch(Err e)
        {
          Sys.log.info("Failed instanciating $typeName of plugin $pod.name")
          e.trace
        }
        // Fail fast check if space doesn't have the loadSession method
        // it's too easy to forget it and then get runtime errors
        pod.types.each
        {
          if(it.fits(Space#))
            it.slot("loadSession") // will throw an Err if slot missing
        }
      }
    }
    plugins = temp
  }

  internal Void onInit()
  {
    plugins.vals.each |plugin| {plugin.onInit(configDir)}
  }

  internal Void onConfigLoaded(Sys newSys)
  {
    confs := Str:PluginConfig?[:]
    plugins.each |plugin, name|
    {
      try
        confs[plugin.name] = plugin.readConfig(newSys)
      catch(Err e){e.trace}
    }
    _pluginConfs.val = confs.toImmutable
  }

  internal Void onChangedProjects(Project[] projects)
  {
    plugins.vals.each |plugin|
    {
      plugin.onChangedProjects(projects.findAll{it.plugin == plugin.typeof.pod.name})
    }
  }

  internal Void onFileSaved(File f)
  {
    plugins.vals.each |plugin| {plugin.onFileSaved(f)}
  }

  internal Void onFrameReady(Frame f, Bool initial:= true)
  {
    plugins.vals.sort|a, b|{a.name <=> b.name}.each |plugin| {plugin.onFrameReady(f, initial)}
  }

  internal Void onShutdown(Bool isKill := false)
  {
    plugins.vals.each |plugin| {plugin.onShutdown(isKill)}
  }

  override Void onStop()
  {
    onShutdown
  }

  override Void onStart()
  {
    onInit
  }

  static PluginManager cur()
  {
    return (PluginManager) Service.find(PluginManager#)
  }

  Image? iconForFile(File f)
  {
    plugins.vals.eachWhile |Plugin p -> Image?| {p.iconForFile(f)}
  }

  ** Wether any plugins are currenty indexing
  Bool anyIndexing()
  {
    if(plugins.vals.find{it.isIndexing} != null)
      return true
    return false
  }

  ** Config of a named plugin
  PluginConfig? conf(Str pluginName)
  {
    confs := ([Str:PluginConfig]?) _pluginConfs.val
    if(confs != null)
      return (PluginConfig?) confs[pluginName]
    return null
  }
}