sourcecamFantomPlugin::FantomIndex.fan

//
// Copyright (c) 2012, Brian Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   24 Apr 12  Brian Frank  Creation
//

using gfx
using fwt
using syntax
using concurrent
using compilerDoc
using camembert

**
** Index maintains listing of files and pods we've crawled
**
const class FantomIndex
{
  ** Whether we have done the initial pod indexing
  const AtomicBool podsIndexed := AtomicBool()

  ** Are we currently indexing
  Bool isIndexing() { isIndexingRef.val }

  ** Update indexing status
  internal Void setIsIndexing(Bool val)
  {
    isIndexingRef.val = val
    try
    {
      Desktop.callAsync |->|
      {
        frame := Sys.cur.frame
        frame.updateStatus
        if(! val)  // done indexing
        {
          frame.spaces.each
          {
            if(it is IndexSpace)
              it.refresh
          }
          frame.helpPane.indexUpdated
        }
      }
    }
    catch {}
  }

  ** List all pods found
  PodInfo[] pods() { ((Unsafe)cache.send(Msg("pods")).get(timeout)).val }

  ** List all groups found
  PodGroup[] groups() { ((Unsafe)cache.send(Msg("groups")).get(timeout)).val }

  Str:TrioInfo trioInfo() { ((Unsafe)cache.send(Msg("trioInfo")).get(timeout)).val }

  ** Find given pod
  PodInfo? pod(Str name, Bool checked := true)
  {
    PodInfo? pod := cache.send(Msg("pod", name)).get(timeout)
    if (pod == null && checked) throw UnknownPodErr(name)
    return pod
  }

  ** Find given pod
  PodInfo? podForFile(File file)
  {
    cache.send(Msg("podForFile", file)).get(timeout)
  }

  ** Find given pod
  PodGroup? groupForFile(File file)
  {
    cache.send(Msg("groupForFile", file)).get(timeout)
  }

  ** Reindex given sources / pods
  Void reindex(File[] srcDirs, File[] podDirs, Bool clearIndex := false)
  {
    if(clearIndex)
    {
      cache.send(Msg("clearAll"))
      podsIndexed.val = false
    }
    crawler.send(Msg("reindex", srcDirs, podDirs))
  }

  ** Rebuild index for given pod, if null no-op
  Void reindexPod(PodInfo? pod)
  {
    if (pod == null) return
    crawler.send(Msg("reindexPod", pod))
  }

  ** Match pods
  PodInfo[] matchPods(Str pattern, MatchKind kind := MatchKind.startsWith)
  {
    cache.send(Msg("matchPods", pattern, kind)).get(timeout)->val
  }

  ** Match types
  TypeInfo[] matchTypes(Str pattern, MatchKind kind := MatchKind.startsWith)
  {
    cache.send(Msg("matchTypes", pattern, kind)).get(timeout)->val
  }

  ** Match slots
  SlotInfo[] matchSlots(Str pattern, MatchKind kind := MatchKind.startsWith, Bool methodsOnly := true)
  {
    cache.send(Msg("matchSlots", pattern, kind, methodsOnly)).get(timeout)->val
  }

  ** Match funcs
  FuncInfo[] matchFuncs(Str pattern, MatchKind kind := MatchKind.startsWith)
  {
    cache.send(Msg("matchFuncs", pattern, kind)).get(timeout)->val
  }

  ** Match tags
  TagInfo[] matchTags(Str pattern, MatchKind kind := MatchKind.startsWith)
  {
    cache.send(Msg("matchTags", pattern, kind)).get(timeout)->val
  }

  ** Match files
  Item[] matchFiles(Str pattern, MatchKind kind := MatchKind.startsWith)
  {
    cache.send(Msg("matchFiles", pattern, kind)).get(timeout)->val
  }

  ** Whether this is the srcDir(root) of a pod
  ** Returns the matching pod or null if no match
  PodInfo? isPodDir(File f)
  {
    return pods.eachWhile |p|
    {
      if(p.srcDir == null)
        return null
      if(p.srcDir.normalize.uri == f.normalize.uri)
        return p
      return null
    }
  }

  ** Whether this is the srcDir(root) of a group
  ** Returns the matching group or null if no match
  PodGroup? isGroupDir(File f)
  {
    return groups.eachWhile |g|
    {
      if(g.srcDir.normalize.uri == f.normalize.uri)
        return g
      return null
    }
  }


//////////////////////////////////////////////////////////////////////////
// Cache Actor
//////////////////////////////////////////////////////////////////////////

  private Obj? receiveCache(Msg msg)
  {
    c := Actor.locals["cache"] as FantomIndexCache
    if (c == null) Actor.locals["cache"] = c = FantomIndexCache(this)

    id := msg.id
    if (id === "pods")        return Unsafe(c.listPods)
    if (id === "groups")      return Unsafe(c.listGroups)
    if (id === "pod")         return c.pod(msg.a)
    if (id === "trioInfo")    return Unsafe(c.listTrioInfo())
    if (id === "podForFile")  return c.podForFile(msg.a)
    if (id === "groupForFile")  return c.groupForFile(msg.a)
    if (id === "matchTypes")  return Unsafe(c.matchTypes(msg.a, msg.b))
    if (id === "matchSlots")  return Unsafe(c.matchSlots(msg.a, msg.b, msg.c))
    if (id === "matchFuncs")  return Unsafe(c.matchFuncs(msg.a, msg.b))
    if (id === "matchTags")  return Unsafe(c.matchTags(msg.a, msg.b))
    if (id === "matchPods")  return Unsafe(c.matchPods(msg.a, msg.b))
    if (id === "matchFiles")  return Unsafe(c.matchFiles(msg.a, msg.b))
    if (id === "addPodSrc")   return c.addPodSrc(msg.a, msg.b, msg.c)
    if (id === "addPodLib")   return c.addPodLib(msg.a, msg.b, msg.c)
    if (id === "addGroup")   return c.addGroup(msg.a, msg.b)
    if (id === "clearAll")    return Actor.locals["cache"] = FantomIndexCache(this)
    if (id === "addTrioInfo")  return c.addTrioInfo(msg.a)

    Sys.log.info("ERROR: Unknown msg: $msg.id")
    throw Err("Unknown msg: $msg.id")
  }

//////////////////////////////////////////////////////////////////////////
// Crawler Actor
//////////////////////////////////////////////////////////////////////////

  private Obj? receiveCrawler(Msg msg)
  {
    try
    {
      c := Actor.locals["crawl"] as FantomIndexCrawler
      if (c == null) Actor.locals["crawl"] = c = FantomIndexCrawler(this)

      id := msg.id
      if (id === "reindexPod") return c.indexPod(msg.a)
      if (id === "reindex") return c.reindex(msg.a, msg.b)

      Sys.log.info("ERROR: Unknown msg: $msg.id")
      throw Err("Unknown msg: $msg.id")
    }
    catch (Err e) { e.trace; throw e }
  }

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  internal const Duration timeout := 10sec
  internal const Actor cache   := Actor(ActorPool()) |msg| { receiveCache(msg) }
  internal const Actor crawler := Actor(ActorPool()) |msg| { receiveCrawler(msg) }
  private const AtomicBool isIndexingRef := AtomicBool()
}