From cfa33d7d29d54193a2c67725f485acb111dcbd34 Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Tue, 21 Apr 2020 17:07:07 +0100 Subject: [PATCH 01/17] Anti Oomph code --- com.xored.f4.core/fan/manifest/ProjectPrefs.fan | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/com.xored.f4.core/fan/manifest/ProjectPrefs.fan b/com.xored.f4.core/fan/manifest/ProjectPrefs.fan index 5bc75dff..667ac0ec 100644 --- a/com.xored.f4.core/fan/manifest/ProjectPrefs.fan +++ b/com.xored.f4.core/fan/manifest/ProjectPrefs.fan @@ -45,7 +45,10 @@ class ProjectPrefs { // when using the Fantom Project Wizard, the compileEnvType can be an empty string // I suspect it is asked for, before the preferences have been initialised name := delegate.getString(qualifier, compileEnvName)?.trimToNull ?: DefaultCompileEnv#.qname - type := Type.find(name) + + // Eclipse's stoopid Oomph plugin somehow keeps setting the env to afFpm even though it's not installed! + // so let's not error and instead just resort to something sensible + type := Type.find(name, false) ?: DefaultCompileEnv#.qname return type } } From 0b658e9c5cf60763b490ee056a577a2a2edfc335 Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Thu, 23 Apr 2020 13:19:21 +0100 Subject: [PATCH 02/17] Prevent crippling errors when duplicate files and classes are detected --- com.xored.f4.core/fan/model/DltkPod.fan | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/com.xored.f4.core/fan/model/DltkPod.fan b/com.xored.f4.core/fan/model/DltkPod.fan index af5009a1..0be02f8a 100644 --- a/com.xored.f4.core/fan/model/DltkPod.fan +++ b/com.xored.f4.core/fan/model/DltkPod.fan @@ -11,7 +11,12 @@ internal const class DltkPod : IFanPod { override const Str[] typeNames new make(Str name, Fragment[] fragments) { this.name = name - this.typesMap = [Str:IFanType][:].addList(allTypes(fragments)) |type| { type.name } + + // SlimerDude (Apr 2020) + // use setList() not addList() so duplicate class and filenames don't cause an error + // yes, a compilation error will still occur, but we'll still be able parse the source + // and do highlighting + this.typesMap = [Str:IFanType][:].setList(allTypes(fragments)) |type| { type.name } this.typeNames = typesMap.keys } From 9361c488c8331be41681648d912d377c3e8e4bb2 Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Thu, 23 Apr 2020 13:41:12 +0100 Subject: [PATCH 03/17] Prevent errors due to closed projects --- com.xored.f4.core/fan/manifest/FantomProject.fan | 15 +++++++-------- .../fan/internal/FanJavaContainer.fan | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/com.xored.f4.core/fan/manifest/FantomProject.fan b/com.xored.f4.core/fan/manifest/FantomProject.fan index 95b91c87..6378b994 100644 --- a/com.xored.f4.core/fan/manifest/FantomProject.fan +++ b/com.xored.f4.core/fan/manifest/FantomProject.fan @@ -163,19 +163,18 @@ const class FantomProject { .map |IBuildpathEntry bp -> File?| { switch(bp.getEntryKind) { case IBuildpathEntry.BPE_PROJECT: - projectName := bp.getPath.segments.first - project := ResourcesPlugin.getWorkspace.getRoot.getProject(projectName) - if (project.isAccessible) { - fp := FantomProjectManager.instance[project] - return fp.podOutFile - } - // Return null if project is not accessible - return null + projectName := bp.getPath.segments.first + project := ResourcesPlugin.getWorkspace.getRoot.getProject(projectName) + return project.isOpen && project.isAccessible + ? FantomProjectManager.instance[project].podOutFile + : null + case IBuildpathEntry.BPE_LIBRARY: // libs are gotten from an older, cached, and workspace wide version of Interpreter libs (Fantom-1.0.68) // whereas we want libs specific to this project, so return null here and add our resolved pods // return PathUtil.resolveLibPath(bp) return null + default: return null } diff --git a/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan b/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan index 7f788008..740bf858 100644 --- a/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan +++ b/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan @@ -44,7 +44,7 @@ class FanJavaContainer : IClasspathContainer { podFP := FantomProjectManager.instance.getByPod(name) if (podFP != null) { IProject prj := podFP.project - if (prj.hasNature("org.eclipse.jdt.core.javanature")) { + if (prj.isAccessible && prj.hasNature("org.eclipse.jdt.core.javanature")) { // do not need to add entry return } From d4196e70694de060dc124ab692bcfbd05a770238 Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Tue, 28 Apr 2020 13:15:00 +0100 Subject: [PATCH 04/17] Rename FanJavaLaunchUtil to what it's supposed to be. --- .../fan/internal/FanJavaLaunchConfig.fan | 10 +++++----- .../{TargetJavaConfig.fan => FanJavaLaunchUtil.fan} | 3 ++- com.xored.f4.testing/fan/FanTestingLaunchConfig.fan | 10 +++++----- 3 files changed, 12 insertions(+), 11 deletions(-) rename com.xored.f4.jdt.launching/fan/internal/{TargetJavaConfig.fan => FanJavaLaunchUtil.fan} (98%) diff --git a/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchConfig.fan b/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchConfig.fan index 98d5e7be..08e5ad4a 100644 --- a/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchConfig.fan +++ b/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchConfig.fan @@ -18,23 +18,23 @@ class FanJavaLaunchConfig : JavaLaunchDelegate, TargetLaunchConfig { protected static const Str mainLaunchType := "fanx.tools.Fan" override Void launch(ILaunchConfiguration? config, Str? mode, ILaunch? launch, IProgressMonitor? m) { - if (JavaLaunchUtil.confirmLaunch(config) == true) + if (FanJavaLaunchUtil.confirmLaunch(config) == true) super.launch(config, mode, launch, m) } override Str?[]? getEnvironment(ILaunchConfiguration? config) { - JavaLaunchUtil.environment(config, super.getEnvironment(config)) + FanJavaLaunchUtil.environment(config, super.getEnvironment(config)) } override Str?[]? getClasspath(ILaunchConfiguration? config) { - JavaLaunchUtil.classpath(config, super.getClasspath(config)) + FanJavaLaunchUtil.classpath(config, super.getClasspath(config)) } override protected Void setDefaultSourceLocator(ILaunch? launch, ILaunchConfiguration? config) { - JavaLaunchUtil.setSourceLocator(launch, config, getLaunchManager) + FanJavaLaunchUtil.setSourceLocator(launch, config, getLaunchManager) } override Void config(ILaunchConfiguration? src, ILaunchConfigurationWorkingCopy? target, Str mode ) { - JavaLaunchUtil.config(src,target,mode,mainLaunchType) + FanJavaLaunchUtil.config(src,target,mode,mainLaunchType) } } diff --git a/com.xored.f4.jdt.launching/fan/internal/TargetJavaConfig.fan b/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan similarity index 98% rename from com.xored.f4.jdt.launching/fan/internal/TargetJavaConfig.fan rename to com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan index 28bf70a7..1a89e1d0 100644 --- a/com.xored.f4.jdt.launching/fan/internal/TargetJavaConfig.fan +++ b/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan @@ -15,7 +15,8 @@ using [java] org.eclipse.ui::PlatformUI using f4launching using f4core -class JavaLaunchUtil { +** Used by FanJavaLaunchConfig & f4testing::FanTestingLaunchConfig +class FanJavaLaunchUtil { static Void config(ILaunchConfiguration? src, ILaunchConfigurationWorkingCopy? target, Str mode, Str mainLaunchType) { scriptName := AbstractScriptLaunchConfigurationDelegate.getScriptProjectName(src) diff --git a/com.xored.f4.testing/fan/FanTestingLaunchConfig.fan b/com.xored.f4.testing/fan/FanTestingLaunchConfig.fan index b97166e9..5bff2c39 100644 --- a/com.xored.f4.testing/fan/FanTestingLaunchConfig.fan +++ b/com.xored.f4.testing/fan/FanTestingLaunchConfig.fan @@ -24,23 +24,23 @@ class FanTestingLaunchConfig : JavaLaunchDelegate, TargetLaunchConfig { DLTKTestingCore.registerTestingProcessor(launch, FanTestProcessor(launch)) launch.setAttribute(DLTKTestingConstants.ATTR_ENGINE_ID, FanTestingEngine.id) - if (JavaLaunchUtil.confirmLaunch(conf) == true) + if (FanJavaLaunchUtil.confirmLaunch(conf) == true) super.launch(wc, mode, launch, m) } override Str?[]? getClasspath(ILaunchConfiguration? config) { - JavaLaunchUtil.classpath(config, super.getClasspath(config)) + FanJavaLaunchUtil.classpath(config, super.getClasspath(config)) } override protected Void setDefaultSourceLocator(ILaunch? launch, ILaunchConfiguration? config) { - JavaLaunchUtil.setSourceLocator(launch, config, getLaunchManager) + FanJavaLaunchUtil.setSourceLocator(launch, config, getLaunchManager) } override Void config(ILaunchConfiguration? src, ILaunchConfigurationWorkingCopy? target, Str mode) { - JavaLaunchUtil.config(src,target,mode,mainLaunchType) + FanJavaLaunchUtil.config(src,target,mode,mainLaunchType) } override Str?[]? getEnvironment(ILaunchConfiguration? config) { - JavaLaunchUtil.environment(config, super.getEnvironment(config)) + FanJavaLaunchUtil.environment(config, super.getEnvironment(config)) } } From 1f701e24d3124d2d9def53e4443e3663b5eaae37 Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Wed, 29 Apr 2020 12:39:07 +0100 Subject: [PATCH 05/17] Re-wrote FantomProjectManager to reduce build thrashing Looked at all usages of, to only update / resolve pods when required. --- com.xored.f4.astView/fan/AstView.fan | 3 +- com.xored.f4.builder/fan/CompileFan.fan | 3 +- com.xored.f4.builder/fan/InternalBuilder.fan | 7 +- .../fan/afConcurrent/SynchronizedState.fan | 129 ++++++ .../fan/manifest/BuildfanListener.fan | 312 ++++++------- .../fan/manifest/ContainerResetter.fan | 51 ++- .../fan/manifest/DefaultCompileEnv.fan | 2 +- .../fan/manifest/FantomProject.fan | 136 ++++-- .../fan/manifest/FantomProjectManager.fan | 409 +++++++++--------- .../fan/manifest/FantomProjectManager2.fan | 277 ++++++++++++ .../fan/manifest/InterpreterContainer.fan | 8 +- com.xored.f4.core/fan/manifest/Manifest.fan | 9 +- .../fan/manifest/ProjectPrefs.fan | 2 +- com.xored.f4.core/fan/model/DltkNamespace.fan | 3 +- .../fan/parser/SourceIndexer.fan | 3 +- .../fan/selection/SelectionEngine.fan | 3 +- com.xored.f4.core/fan/util/ParseUtil.fan | 10 +- .../fan/FanConsoleTracker.fan | 9 +- .../fan/FanEditorDebugAdapterFactory.fan | 7 +- com.xored.f4.debug.ui/fan/FanTabGroup.fan | 7 +- com.xored.f4.debug.ui/fan/ProjectTab.fan | 6 +- com.xored.f4.debug/fan/FanSourceContainer.fan | 2 +- .../fan/SourceLookupParticipant.fan | 5 +- .../fan/internal/FanJavaContainer.fan | 8 +- .../fan/internal/FanJavaLaunchUtil.fan | 9 +- com.xored.f4.launchEnv/fan/F4LaunchEnv.fan | 16 +- .../fan/LaunchConfigDelegate.fan | 4 +- .../fan/callHierarchy/CalleeProcessor.fan | 7 +- com.xored.f4.testing/fan/FanTestRunnerUI.fan | 3 +- .../fan/FanTestingLaunchShortcut.fan | 3 +- .../fan/FanTestingTabGroup.fan | 3 +- .../folding/FanCodeFoldingBlockProvider.fan | 2 +- 32 files changed, 979 insertions(+), 479 deletions(-) create mode 100644 com.xored.f4.core/fan/afConcurrent/SynchronizedState.fan create mode 100644 com.xored.f4.core/fan/manifest/FantomProjectManager2.fan diff --git a/com.xored.f4.astView/fan/AstView.fan b/com.xored.f4.astView/fan/AstView.fan index d2bda64d..0ae16164 100644 --- a/com.xored.f4.astView/fan/AstView.fan +++ b/com.xored.f4.astView/fan/AstView.fan @@ -1,4 +1,5 @@ using f4core +using f4core::FantomProjectManager2 using f4model using f4parser using f4uiText @@ -61,7 +62,7 @@ class AstView : ViewPart, IPartListener private static IFanNamespace getNamespace(ISourceModule? content) { project := content?.getScriptProject?.getProject if (project == null || !project.isOpen) return EmptyNamespace() - return FantomProjectManager.instance[project].ns + return FantomProjectManager2.instance.get(project).ns } override Void partActivated(IWorkbenchPart? part) { diff --git a/com.xored.f4.builder/fan/CompileFan.fan b/com.xored.f4.builder/fan/CompileFan.fan index 964d9908..37deee63 100644 --- a/com.xored.f4.builder/fan/CompileFan.fan +++ b/com.xored.f4.builder/fan/CompileFan.fan @@ -1,5 +1,6 @@ using compiler using f4core +using f4core::FantomProjectManager2 using [java]java.util::List as JList using [java]java.util::Set as JSet @@ -129,7 +130,7 @@ class CompileFan : IScriptBuilder { ////////////////////////////////////////////////////////////////////////// private FantomProject fantomProject(IScriptProject project) { - FantomProjectManager.instance[project.getProject] + FantomProjectManager2.instance.get(project.getProject) } ////////////////////////////////////////////////////////////////////////// diff --git a/com.xored.f4.builder/fan/InternalBuilder.fan b/com.xored.f4.builder/fan/InternalBuilder.fan index 070b524f..a70126bc 100644 --- a/com.xored.f4.builder/fan/InternalBuilder.fan +++ b/com.xored.f4.builder/fan/InternalBuilder.fan @@ -1,5 +1,5 @@ using f4core::FantomProject -using f4core::FantomProjectManager +using f4core::FantomProjectManager2 using f4core::LogUtil using compiler @@ -38,7 +38,8 @@ class InternalBuilder : Builder { compileDir.create compileDir.listFiles.each { it.delete } - resolvedPods := fp.resolvePods +`/f4log.txt`.toFile.out(true).writeChars("$fp.podName BUILDING\n").close + resolvedPods := fp.resolvedPods bldLoc := Loc(fp.buildFile) if (fp.resolveErrs.size > 0) { @@ -53,7 +54,7 @@ class InternalBuilder : Builder { // SlimerDude - Apr 2020 - Beta feature to turn this off if (fp.prefs.referencedPodsOnly == false) - FantomProjectManager.instance.listProjects.each |p| { + FantomProjectManager2.instance.allProjects.each |p| { resolvedPods[p.podName] = p.podOutFile } diff --git a/com.xored.f4.core/fan/afConcurrent/SynchronizedState.fan b/com.xored.f4.core/fan/afConcurrent/SynchronizedState.fan new file mode 100644 index 00000000..93f423d9 --- /dev/null +++ b/com.xored.f4.core/fan/afConcurrent/SynchronizedState.fan @@ -0,0 +1,129 @@ +using concurrent::Actor +using concurrent::ActorPool +using concurrent::AtomicInt +using concurrent::Future + +** Provides 'synchronized' access to a (non- 'const') mutable state object. +** +** 'SynchronizedState' creates the state object in its own thread and provides access to it via the +** 'sync()' and 'async()' methods. Note that by their nature, these methods are immutable +** boundaries. Meaning that while data in the State object can be mutable, data passed in and out +** of these these boundaries can not be. +** +** 'SynchronizedState' is designed to be *scope safe*, that is you cannot accidently call methods +** on your State object outside of the 'sync()' and 'async()' methods. +** +** Example usage: +** +** pre> +** syntax: fantom +** +** sync := SynchronizedState(ActorPool(), Mutable#) +** msg := "That's cool, dude!" +** +** val := sync.sync |Mutable state -> Int| { +** state.buf.writeChars(msg) +** return state.buf.size +** } +** +** class Mutable { +** Buf buf := Buf() +** } +**
+** syntax: fantom +** sync := SynchronizedState(ActorPool(), Mutable#) +** +** size := sync->buf->size //--> returns size of Mutable.buf +**Obj?| stateFactory + @NoDoc // advanced use only + const LocalRef stateRef + + ** The 'lock' object should you need to 'synchronize' on the state. + const Synchronized lock + + // Note we can't create a ctor(syncPool, type) 'cos that leads to ambiguous ctors in existing libs like afBedSheet / afParrotSdk2 + @NoDoc // advanced use only - for setting own lock + new make(|This| f) { + f(this) + if (this.stateRef == null) + this.stateRef = LocalRef(SynchronizedState#.name) + } + + ** Creates a 'SynchronizedState' instance. + ** + ** The given state type must have a public no-args ctor as per [Type.make]`sys::Type.make`. + new makeWithType(ActorPool actorPool, Type stateType) { + this.lock = Synchronized(actorPool) + this.stateRef = LocalRef(stateType.name) + this.stateFactory = |->Obj?| { stateType.make } + } + + ** Creates a 'SynchronizedState' instance. + ** + ** The given (immutable) factory func is used to create the state object inside it's thread. + new makeWithFactory(ActorPool actorPool, |->Obj?| stateFactory) { + this.lock = Synchronized(actorPool) + this.stateRef = LocalRef(SynchronizedState#.name) + this.stateFactory = stateFactory + } + + ** Calls the given func synchronously, passing in the State object and returning the func's + ** response. + ** + ** The given func should be immutable. + Obj? sync(|Obj state -> Obj?| func) { + iFunc := func.toImmutable + return lock.synchronized |->Obj?| { callFunc(iFunc) } + } + + ** Calls the given func asynchronously, passing in the State object. + ** + ** The given func should be immutable. + Future async(|Obj state -> Obj?| func) { + iFunc := func.toImmutable + return lock.async |->Obj?| { callFunc(iFunc) } + } + + ** Calls the given func asynchronously, passing in the State object. + ** + ** The given func should be immutable. + Future asyncLater(Duration duration, |Obj state -> Obj?| func) { + iFunc := func.toImmutable + return lock.asyncLater(duration) |->Obj?| { callFunc(iFunc) } + } + + ** Routes trap() to the enclosed state object. Allows convenience calls for calling 'sync()' + ** and returning state: + ** + ** pre> + ** syntax: fantom + ** sync := SynchronizedState(ActorPool(), Buf#) + ** + ** size := sync->size //--> returns size of Buf + **Obj?| { + state.trap(name, iargs) + } + } + + private Obj? callFunc(|Obj?->Obj?| func) { + if (stateRef.val == null) + stateRef.val = stateFactory.call + return func.call(stateRef.val) + } + + ** Returns a string representation the state. + override Str toStr() { + sync { it.toStr } + } +} + diff --git a/com.xored.f4.core/fan/manifest/BuildfanListener.fan b/com.xored.f4.core/fan/manifest/BuildfanListener.fan index f5fdd2f1..769e4b0f 100644 --- a/com.xored.f4.core/fan/manifest/BuildfanListener.fan +++ b/com.xored.f4.core/fan/manifest/BuildfanListener.fan @@ -1,156 +1,156 @@ -using [java]org.eclipse.core.resources::IResourceChangeListener -using [java]org.eclipse.core.resources::IResourceDeltaVisitor -using [java]org.eclipse.core.resources::IResourceChangeEvent -using [java]org.eclipse.core.resources::IResourceDelta -using [java]org.eclipse.core.resources::IProject -using [java]org.eclipse.core.resources::IResource -using [java]org.eclipse.dltk.core::DLTKCore -using [java]org.eclipse.jdt.core::IJavaProject -using concurrent - -const class BuildfanListener : Actor, IResourceChangeListener { - ////////////////////////////////////////////////////////////////////////// - // Interface methods - ////////////////////////////////////////////////////////////////////////// - override Void resourceChanged(IResourceChangeEvent? event) { - if(event.getType == IResourceChangeEvent.POST_CHANGE) { - visitor := DeltaVisitor() - event.getDelta.accept(visitor) - if(visitor.change.isEmpty) return - notifyListeners(visitor.change) - } - } - - ////////////////////////////////////////////////////////////////////////// - // Constructor and overriden methods - ////////////////////////////////////////////////////////////////////////// - new make(ActorPool pool := ActorPool()) : super(pool) {} - - override Obj? receive(Obj? msg) { - try - { - if(msg isnot Obj[]) return null - list := msg as Obj[] - if(list.first isnot Method) return null - method := list.first as Method - Obj?[] args := list[1..-1] - if(list.size == 2 && list.last is Unsafe) - args = list.last->val - return method.callOn(this, args) - } catch (Err e) { - e.trace //TODO: add normal error reporting - throw e - } - } - - ////////////////////////////////////////////////////////////////////////// - // Public API - ////////////////////////////////////////////////////////////////////////// - - Void addListener(BuildfanChangeListener listener) { - send([#doAddListener, listener].toImmutable) - } - - Void removeListener(BuildfanChangeListener listener) { - send([#doRemoveListener, listener].toImmutable) - } - - Void subscribe() { - DLTKCore.addPreProcessingResourceChangedListener(this, IResourceChangeEvent.POST_CHANGE) - } - - ////////////////////////////////////////////////////////////////////////// - // Helper methods - ////////////////////////////////////////////////////////////////////////// - - private Void notifyListeners(WorkspaceChange change) { - send([#doNotify, Unsafe([change])].toImmutable) - } - - private Void doNotify(WorkspaceChange change) { - listeners.each { notify(change) } - } - - private Void doAddListener(BuildfanChangeListener listener) { - listeners.add(listener) - } - - private Void doRemoveListener(BuildfanChangeListener listener) { - listeners.remove(listener) - } - - ////////////////////////////////////////////////////////////////////////// - // Actor.locals properties - ////////////////////////////////////////////////////////////////////////// - - BuildfanChangeListener[] listeners() { - locals.getOrAdd("listeners") |->Obj| { BuildfanChangeListener[,] } - } -} - -************************************************************************** -** DeltaVisitor -************************************************************************** - -** Visits resource delta and collects changes -class DeltaVisitor : IResourceDeltaVisitor { - WorkspaceChange change := WorkspaceChange() - - override Bool visit(IResourceDelta? delta) { - resource := delta.getResource - switch(resource.getType) { - case IResource.PROJECT: - IProject project := resource - - if(!project.exists || projectClosed(delta, project)) { - change.closedProjects.add(project) - return false - } - - if(projectOpened(delta, project)) { - change.openedProjects.add(project) - return false - } - - return true - case IResource.FOLDER: - return false - case IResource.FILE: - if((resource.getName == Manifest.filename || resource.getName == IJavaProject.CLASSPATH_FILE_NAME) - && contentChanged(delta)) - change.updatedProjects.add(resource.getProject) - return false - default: return true - } - } - - private static Bool contentChanged(IResourceDelta delta) { - switch(delta.getKind) { - case IResourceDelta.CHANGED: - return delta.getFlags.and(IResourceDelta.CONTENT) != 0 - default: return true - } - } - - private static Bool projectClosed(IResourceDelta delta, IProject project) { openChange(delta) && !project.isOpen } - private static Bool projectOpened(IResourceDelta delta, IProject project) { openChange(delta) && project.isOpen } - private static Bool openChange(IResourceDelta delta) { delta.getFlags.and(IResourceDelta.OPEN) != 0 } -} - -class WorkspaceChange { - IProject[] closedProjects := IProject[,] - IProject[] openedProjects := IProject[,] - IProject[] updatedProjects := IProject[,] - - Bool isEmpty() { [closedProjects, openedProjects, updatedProjects].all { it.isEmpty } } -} - -************************************************************************** -** BuildfanChangeListener -************************************************************************** - -** Base mixin for all listeners of buildfan changes -const mixin BuildfanChangeListener { - abstract Void notify(WorkspaceChange change) -} - +//using [java]org.eclipse.core.resources::IResourceChangeListener +//using [java]org.eclipse.core.resources::IResourceDeltaVisitor +//using [java]org.eclipse.core.resources::IResourceChangeEvent +//using [java]org.eclipse.core.resources::IResourceDelta +//using [java]org.eclipse.core.resources::IProject +//using [java]org.eclipse.core.resources::IResource +//using [java]org.eclipse.dltk.core::DLTKCore +//using [java]org.eclipse.jdt.core::IJavaProject +//using concurrent +// +//const class BuildfanListener : Actor, IResourceChangeListener { +// ////////////////////////////////////////////////////////////////////////// +// // Interface methods +// ////////////////////////////////////////////////////////////////////////// +// override Void resourceChanged(IResourceChangeEvent? event) { +// if(event.getType == IResourceChangeEvent.POST_CHANGE) { +// visitor := DeltaVisitor() +// event.getDelta.accept(visitor) +// if(visitor.change.isEmpty) return +// notifyListeners(visitor.change) +// } +// } +// +// ////////////////////////////////////////////////////////////////////////// +// // Constructor and overriden methods +// ////////////////////////////////////////////////////////////////////////// +// new make(ActorPool pool := ActorPool()) : super(pool) {} +// +// override Obj? receive(Obj? msg) { +// try +// { +// if(msg isnot Obj[]) return null +// list := msg as Obj[] +// if(list.first isnot Method) return null +// method := list.first as Method +// Obj?[] args := list[1..-1] +// if(list.size == 2 && list.last is Unsafe) +// args = list.last->val +// return method.callOn(this, args) +// } catch (Err e) { +// e.trace //TODO: add normal error reporting +// throw e +// } +// } +// +// ////////////////////////////////////////////////////////////////////////// +// // Public API +// ////////////////////////////////////////////////////////////////////////// +// +// Void addListener(BuildfanChangeListener listener) { +// send([#doAddListener, listener].toImmutable) +// } +// +// Void removeListener(BuildfanChangeListener listener) { +// send([#doRemoveListener, listener].toImmutable) +// } +// +// Void subscribe() { +// DLTKCore.addPreProcessingResourceChangedListener(this, IResourceChangeEvent.POST_CHANGE) +// } +// +// ////////////////////////////////////////////////////////////////////////// +// // Helper methods +// ////////////////////////////////////////////////////////////////////////// +// +// private Void notifyListeners(WorkspaceChange change) { +// send([#doNotify, Unsafe([change])].toImmutable) +// } +// +// private Void doNotify(WorkspaceChange change) { +// listeners.each { notify(change) } +// } +// +// private Void doAddListener(BuildfanChangeListener listener) { +// listeners.add(listener) +// } +// +// private Void doRemoveListener(BuildfanChangeListener listener) { +// listeners.remove(listener) +// } +// +// ////////////////////////////////////////////////////////////////////////// +// // Actor.locals properties +// ////////////////////////////////////////////////////////////////////////// +// +// BuildfanChangeListener[] listeners() { +// locals.getOrAdd("listeners") |->Obj| { BuildfanChangeListener[,] } +// } +//} +// +//************************************************************************** +//** DeltaVisitor +//************************************************************************** +// +//** Visits resource delta and collects changes +//class DeltaVisitor : IResourceDeltaVisitor { +// WorkspaceChange change := WorkspaceChange() +// +// override Bool visit(IResourceDelta? delta) { +// resource := delta.getResource +// switch(resource.getType) { +// case IResource.PROJECT: +// IProject project := resource +// +// if(!project.exists || projectClosed(delta, project)) { +// change.closedProjects.add(project) +// return false +// } +// +// if(projectOpened(delta, project)) { +// change.openedProjects.add(project) +// return false +// } +// +// return true +// case IResource.FOLDER: +// return false +// case IResource.FILE: +// if((resource.getName == Manifest.filename || resource.getName == IJavaProject.CLASSPATH_FILE_NAME) +// && contentChanged(delta)) +// change.updatedProjects.add(resource.getProject) +// return false +// default: return true +// } +// } +// +// private static Bool contentChanged(IResourceDelta delta) { +// switch(delta.getKind) { +// case IResourceDelta.CHANGED: +// return delta.getFlags.and(IResourceDelta.CONTENT) != 0 +// default: return true +// } +// } +// +// private static Bool projectClosed(IResourceDelta delta, IProject project) { openChange(delta) && !project.isOpen } +// private static Bool projectOpened(IResourceDelta delta, IProject project) { openChange(delta) && project.isOpen } +// private static Bool openChange(IResourceDelta delta) { delta.getFlags.and(IResourceDelta.OPEN) != 0 } +//} +// +//class WorkspaceChange { +// IProject[] closedProjects := IProject[,] +// IProject[] openedProjects := IProject[,] +// IProject[] updatedProjects := IProject[,] +// +// Bool isEmpty() { [closedProjects, openedProjects, updatedProjects].all { it.isEmpty } } +//} +// +//************************************************************************** +//** BuildfanChangeListener +//************************************************************************** +// +//** Base mixin for all listeners of buildfan changes +//const mixin BuildfanChangeListener { +// abstract Void notify(WorkspaceChange change) +//} +// diff --git a/com.xored.f4.core/fan/manifest/ContainerResetter.fan b/com.xored.f4.core/fan/manifest/ContainerResetter.fan index d6bb3322..1bcb3fa6 100644 --- a/com.xored.f4.core/fan/manifest/ContainerResetter.fan +++ b/com.xored.f4.core/fan/manifest/ContainerResetter.fan @@ -14,16 +14,16 @@ using concurrent ** Listens for Build.fan changes and updates container ** Automatically groups update requests by project const class ContainerResetter : Actor { + new make(ActorPool pool) : super.makeCoalescing ( pool, - |Unsafe val -> Str| - { - //key is project location + |Unsafe val -> Str| { + // key is project location (val.val as IProject).getLocation.toOSString }, - null, //we don't need coalesce func, last message wins - null //we override receive method, so no need to pass it + null, // we don't need coalesce func, last message wins + null // we override receive method, so no need to pass it ) {} public Void reset(IProject project) { @@ -31,28 +31,27 @@ const class ContainerResetter : Actor { } override Obj? receive(Obj? msg) { - try { - project := (msg as Unsafe).val as IProject - if( !project.isAccessible) { - return null - } - scriptProject := DLTKCore.create(project) - if( scriptProject.exists) { - DLTKCore.getBuildpathContainerInitializer(ScriptRuntime.INTERPRETER_CONTAINER) - .initialize(containerPath(scriptProject), scriptProject) - } - // Reinitialize also Java container - javaProject := JavaCore.create(project) - if( javaProject.exists) { - JavaCore.getClasspathContainerInitializer("com.xored.fanide.jdt.launching.FANJAVA_CONTAINER") - .initialize(javaContainerPath(javaProject), javaProject) - } - - } catch(Err e) { - e.trace - throw e - //TODO: add normal error reporting + project := (msg as Unsafe).val as IProject + if (!project.isAccessible) { + return null + } + +`/f4log.txt`.toFile.out(true).writeChars("$project.getName Resetting\n").close +echo("Resetting $project.getName") + + scriptProject := DLTKCore.create(project) + if (scriptProject.exists) { + DLTKCore.getBuildpathContainerInitializer(ScriptRuntime.INTERPRETER_CONTAINER) + .initialize(containerPath(scriptProject), scriptProject) } + + // Reinitialize also Java container + javaProject := JavaCore.create(project) + if (javaProject.exists) { + JavaCore.getClasspathContainerInitializer("com.xored.fanide.jdt.launching.FANJAVA_CONTAINER") + .initialize(javaContainerPath(javaProject), javaProject) + } + return null } diff --git a/com.xored.f4.core/fan/manifest/DefaultCompileEnv.fan b/com.xored.f4.core/fan/manifest/DefaultCompileEnv.fan index ff67fb54..2d283a9a 100644 --- a/com.xored.f4.core/fan/manifest/DefaultCompileEnv.fan +++ b/com.xored.f4.core/fan/manifest/DefaultCompileEnv.fan @@ -24,7 +24,7 @@ const class DefaultCompileEnv : CompileEnv { podFiles[podFile.basename] = podFile } - buildConsole.debug("DefaultEnv - Resolved ${podFiles.size} pods for ${fanProj.podName} from: ${workDir.osPath}") + buildConsole.debug("DefaultCompileEnv - Resolved ${podFiles.size} pods for ${fanProj.podName} from: ${workDir.osPath}") return podFiles.toImmutable } } diff --git a/com.xored.f4.core/fan/manifest/FantomProject.fan b/com.xored.f4.core/fan/manifest/FantomProject.fan index 6378b994..1913f8c4 100644 --- a/com.xored.f4.core/fan/manifest/FantomProject.fan +++ b/com.xored.f4.core/fan/manifest/FantomProject.fan @@ -53,9 +53,8 @@ const class FantomProject { } private const AtomicRef resolveErrsRef := AtomicRef(Err#.emptyList) - Err[] resolveErrs { - get { resolveErrsRef.val } - set { resolveErrsRef.val = it } + Err[] resolveErrs() { + resolveErrsRef.val } private const Unsafe iProjectRef @@ -75,9 +74,12 @@ const class FantomProject { projErrs.add(ProjectErr(e.toStr)) projectErrs = projErrs podName = "" + buildFanStr = "" return } + buildFanStr = manifest.buildFanStr + if (manifest.podName != null) podName = manifest.podName else { @@ -148,6 +150,13 @@ const class FantomProject { // Build info ////////////////////////////////////////////////////////////////////////// + private const AtomicRef classpathDependsRef := AtomicRef(null) + Str:File classpathDepends() { + if (classpathDependsRef.val == null) + update + return classpathDependsRef.val + } + ** (SlimerDude Jun 2016) - not really sure what this returns. ** Seems to be absolute locations of required pods ** Used by com.xored.f4.jdt.launching::FanJavaContainer --> Fantom Native Libraries (Java) @@ -156,7 +165,9 @@ const class FantomProject { ** - IProject.getReferencedProjects() ** - called by f4jdtLaunching.FanJavaContainer.getClasspathEntries() ** - called by f4core.FantomProjectManager.doListReferencedProjects() - Str:File classpathDepends() { + private Str:File doClasspathDepends() { +`/f4log.txt`.toFile.out(true).writeChars("$project.getName - classpathDepends()\n").close + buildPathFiles := (Str:File) scriptProject.getResolvedBuildpath(false).findAll |IBuildpathEntry bp->Bool| { !bp.getPath.segments.first.toStr.startsWith(IBuildpathEntry.BUILDPATH_SPECIAL) } @@ -166,7 +177,7 @@ const class FantomProject { projectName := bp.getPath.segments.first project := ResourcesPlugin.getWorkspace.getRoot.getProject(projectName) return project.isOpen && project.isAccessible - ? FantomProjectManager.instance[project].podOutFile + ? FantomProjectManager2.instance.get(project).podOutFile : null case IBuildpathEntry.BPE_LIBRARY: @@ -185,12 +196,12 @@ const class FantomProject { return r } - // new beta behaviour - if (prefs.referencedPodsOnly) - return buildPathFiles +// // new beta behaviour +// if (prefs.referencedPodsOnly) +// return buildPathFiles // old behaviour - podFiles := resolvePods.rw.setAll(buildPathFiles) + podFiles := resolvedPods.rw.setAll(buildPathFiles) return podFiles } @@ -202,47 +213,112 @@ const class FantomProject { }, projectDir.uri).sort } - ** Returns a map of pod names to pod files. - Str:File resolvePods() { + + private const AtomicRef resolvedPodsRef := AtomicRef(null) + Str:File resolvedPods() { + if (resolvedPodsRef.val == null) + update + return resolvedPodsRef.val + } + + ** The workspace has changed somehow (projects updates) and we're involved somehow + internal Void update() { +`/f4log.txt`.toFile.out(true).writeChars("$project.getName - UPDATING resolved PODS\n").close + podFiles := doResolvePods.rw - resolveErrs = compileEnv.resolveErrs.toImmutable // overwrite entries with workspace pods - if (prefs.referencedPodsOnly) { - // new beta behaviour - podFiles.setAll(classpathDepends) - - } else { - // old behaviour - FantomProjectManager.instance.listProjects.each |fp| { - if (podFiles.containsKey(fp.podName) || rawDepends.any { it.name == fp.podName }) - podFiles[fp.podName] = fp.podOutFile - } + dependentProjects.each { + podFiles[it.podName] = it.podOutFile } // prevent errs such as "Project cannot reference itself: poo" podFiles.remove(podName) + +`/f4log.txt`.toFile.out(true).writeChars("$project.getName - setting resolved pods to ${podFiles}\n").close + this.resolvedPodsRef.val = podFiles.toImmutable + + + this.classpathDependsRef.val = doClasspathDepends.toImmutable + } + +// ** Returns a map of pod names to pod files. +// ** Only called by InterpreterContainer.processEntres() and InternalBuilder.buildPod() +// Str:File resolvePods() { +//`/f4log.txt`.toFile.out(true).writeChars("$podName resolvingPods\n").close +// podFiles := doResolvePods.rw +// resolveErrs = compileEnv.resolveErrs.toImmutable +// +//// // overwrite entries with workspace pods +//// if (prefs.referencedPodsOnly) { +//// // new beta behaviour +//// podFiles.setAll(classpathDepends) +//// } +// +// // overwrite entries with workspace pods +// // new behaviour - kinda the same as the old +//// if (prefs.referencedPodsOnly) { +// dependentProjects.each { +// podFiles[it.podName] = it.podOutFile +// } +//// } +// +//// // old behaviour +//// if (prefs.referencedPodsOnly == false) { +//// FantomProjectManager2.instance.allProjects.each |fp| { +//// if (podFiles.containsKey(fp.podName) || rawDepends.any { it.name == fp.podName }) +//// podFiles[fp.podName] = fp.podOutFile +//// } +//// } +// +// // prevent errs such as "Project cannot reference itself: poo" +// podFiles.remove(podName) +// +// return podFiles +// } + + + + FantomProject[] dependentProjects() { + projectManager := FantomProjectManager2.instance + return projectManager.dependentProjects(this) + +// fantomProjects := FantomProject[,] +// for (i := 0; i < rawDepends.size; ++i) { +// project := projectManager.getByPodName(rawDepends[i].name) +// if (project != null) +// fantomProjects.add(project) +// } +// return fantomProjects + } - return podFiles + private const Str buildFanStr + Bool buildFanHasChanged() { + // TODO quick cache this? + try return buildFile.readAllStr != buildFanStr + catch return true } - private const AtomicRef dependsStrRef := AtomicRef() +// private const AtomicRef dependsStrRef := AtomicRef() private const AtomicRef resolvePodsRef := AtomicRef() private const AtomicRef resolveFutureRef := AtomicRef() private Str:File doResolvePods() { // cache the resolved pods until the dependencies change // this MASSIVELY reduces the F4 build churn - dependsStr := rawDepends.rw.sort |p1, p2| { p1.name <=> p2.name }.join("; ") - if (dependsStr == dependsStrRef.val) - return resolvePodsRef.val +// dependsStr := rawDepends.rw.sort |p1, p2| { p1.name <=> p2.name }.join("; ") +// if (dependsStr == dependsStrRef.val) +// return resolvePodsRef.val + +// CompileEnv caches pods for 3 secs, so don't worry about that! // coalesce multiple calls into one future := resolveFutureRef.val as Future if (future == null) { future = Synchronized(ActorPool()).async |->Obj?| { resolvedPods := compileEnv.resolvePods - resolvePodsRef.val = resolvedPods - dependsStrRef.val = dependsStr + resolvePodsRef.val = resolvedPods.toImmutable +// dependsStrRef.val = dependsStr + resolveErrsRef.val = compileEnv.resolveErrs.toImmutable return resolvedPods } resolveFutureRef.val = future @@ -287,6 +363,8 @@ const class FantomProject { } return result } + + override Str toStr() { podName } } ************************************************************************** diff --git a/com.xored.f4.core/fan/manifest/FantomProjectManager.fan b/com.xored.f4.core/fan/manifest/FantomProjectManager.fan index c3f2ab86..e63a153c 100644 --- a/com.xored.f4.core/fan/manifest/FantomProjectManager.fan +++ b/com.xored.f4.core/fan/manifest/FantomProjectManager.fan @@ -1,203 +1,206 @@ -using [java]org.eclipse.core.resources::IProject -using [java]org.eclipse.dltk.core::IScriptModel -using [java]org.eclipse.dltk.core::IScriptProject -using [java]org.eclipse.dltk.core::DLTKCore -using [java]org.eclipse.core.resources::ResourcesPlugin -using concurrent - -const class FantomProjectManager : Actor, BuildfanChangeListener { - static const FantomProjectManager? instance := FantomProjectManager() - private const ContainerResetter resetter - const BuildfanListener buildFanChange - - - ////////////////////////////////////////////////////////////////////////// - // Constructor and fields - ////////////////////////////////////////////////////////////////////////// - - private new make() : super(ActorPool()) { - try { - resetter = ContainerResetter(pool) - buildFanChange = BuildfanListener(pool) - buildFanChange.subscribe - buildFanChange.addListener(this) - init - } catch(Err e) { - e.trace - throw e - } - } - - ////////////////////////////////////////////////////////////////////////// - // Overriden methods - ////////////////////////////////////////////////////////////////////////// - - override Obj? receive(Obj? msg) { - // rewritten to fail fast - try { - list := (Obj[])msg - return ((Method)list[0]).callOn(this, ((Unsafe)list[1]).val) - } catch(Err e) { - e.trace //TODO: Add normal error reporting - throw e - } - } - - override Void notify(WorkspaceChange change) { - runInManager(#doNotify, [change]) - } - - ////////////////////////////////////////////////////////////////////////// - // Public API - ////////////////////////////////////////////////////////////////////////// - - internal Void init() { send([#doInit,Unsafe([,])].toImmutable) } - - @Operator FantomProject? get(IProject project) { runInManager(#doGet, [project]) } - - FantomProject? getByPod(Str podName) { runInManager(#doGetByPod, [podName]) } - - FantomProject[] listProjects() { runInManager(#doListProjects, [,]) } - - ** SlimerDude - Apr 2020 - A beta feature to cut down on build thrashing - ** 'cos I dunno why we need ALL projects as dependencies!? - FantomProject[] listReferencedProjects(Str podName) { runInManager(#doListReferencedProjects, [podName]) } - - ////////////////////////////////////////////////////////////////////////// - // Method handlers - ////////////////////////////////////////////////////////////////////////// - - private Obj? runInManager(Method method, Obj?[] args) { marker ? method.callOn(this,args) : send([method,Unsafe(args)].toImmutable).get } - - private Void doNotify(WorkspaceChange change) { - // we need to reset containers for all projects that depend on - // closed or opened projects and for all projects with updated content - projectsToUpdate := [Str:IProject][:] - - //add opened projects - change.openedProjects.each |IProject p| { - if (isFantomProject(p)) addProject(p) - } - - [change.closedProjects, change.openedProjects].flatten.each |IProject p| { - dependentProjects(p).each |IProject d| { - projectsToUpdate[d.getName] = d - } - } - - // add updated and opened projects to projectsToUpdate - [change.updatedProjects, change.openedProjects].flatten.each |IProject p| { - if(isFantomProject(p)) projectsToUpdate[p.getName] = p - } - - // now we need to exclude all removed projects for this list - projectsToUpdate = projectsToUpdate.exclude |v, k| { - change.closedProjects.any { getName == k } - } - - // remove all closed projects - change.closedProjects.each |p| { removeProject(p) } - - // buildfanChanged(change.updatedProjects.first) - projectsToUpdate.vals.each |p| { - addProject(p) - } - - projectsToUpdate.vals.each |p| { - resetter.reset(p) - } - } - - private Bool isFantomProject(IProject p) { - if (!p.exists) return projects[getKey(p)] != null - return p.getNature(F4Nature.id) != null - } - - private Void removeProject(IProject p) { - key := getKey(p) - fp := projects[key] - if (fp == null) return - projects.remove(key) - pods.remove(fp.podName) - } - - private static Str getKey(IProject p) { p.getName } - - private FantomProject addProject(IProject p) { - fp := FantomProject.makeFromProject(p) - removeProject(p) - projects[getKey(p)] = fp - pods[fp.podName] = fp - return fp - } - - private IProject[] dependentProjects(IProject project) { - fp := projects[getKey(project)] - if(fp == null) return IProject[,] - podName := fp.podName - ps := projects - return ps.vals.findAll |p| { - p.rawDepends.any |d| { - d.name == podName - } - }.map { it.project } - } - - private FantomProject? doGet(IProject project) { - // SlimerDude - Apr 2020 - not sure why a null location prevents us from returning the project? - if (project.getLocation == null) { - echo("location is null for project $project.getName") - return null - } - key := getKey(project) - if (projects.containsKey(key)) return projects[key] - return addProject(project) - } - - private Void doInit() { - setMarker - DLTKCore.create(ResourcesPlugin.getWorkspace.getRoot).getScriptProjects(F4Nature.id).each |IScriptProject sp| { - addProject(sp.getProject) - } - } - - private FantomProject[] doListProjects() { projects.vals.dup.toImmutable } - - private FantomProject? doGetByPod(Str podName) { pods[podName] } - - private FantomProject[] doListReferencedProjects(Str podName) { - fp := pods[podName] - projs := fp.project.getReferencedProjects.map { doGet(it) }.exclude { it == null } - return projs - } - - ////////////////////////////////////////////////////////////////////////// - // Private hepler methods - ////////////////////////////////////////////////////////////////////////// - - private FantomProject? createProject(IProject project) { - try { - return FantomProject.makeFromProject(project) - } catch (Err e) { - e.trace - return null - } - } - - ////////////////////////////////////////////////////////////////////////// - // Locals-backed properties - ////////////////////////////////////////////////////////////////////////// - - ** Fantom projects by location - private Str:FantomProject projects() { - locals.getOrAdd("projects") |->Obj| { [Str:FantomProject][:] } - } - - ** Fantom projects by pod name - private Str:FantomProject pods() { - locals.getOrAdd("pods") |->Obj| { [Str:FantomProject][:] } - } - - private Bool marker() { locals[toStr] as Bool ?: false } - private Void setMarker() { locals[toStr] = true } -} +//using [java]org.eclipse.core.resources::IProject +//using [java]org.eclipse.dltk.core::IScriptModel +//using [java]org.eclipse.dltk.core::IScriptProject +//using [java]org.eclipse.dltk.core::DLTKCore +//using [java]org.eclipse.core.resources::ResourcesPlugin +//using concurrent +// +//const class FantomProjectManager : Actor, BuildfanChangeListener { +// static const FantomProjectManager? instance := FantomProjectManager() +// private const ContainerResetter resetter +// const BuildfanListener buildFanChange +// +// +// ////////////////////////////////////////////////////////////////////////// +// // Constructor and fields +// ////////////////////////////////////////////////////////////////////////// +// +// private new make() : super(ActorPool()) { +// try { +// resetter = ContainerResetter(pool) +// buildFanChange = BuildfanListener(pool) +// buildFanChange.subscribe +// buildFanChange.addListener(this) +// init +// } catch(Err e) { +// e.trace +// throw e +// } +// } +// +// ////////////////////////////////////////////////////////////////////////// +// // Overriden methods +// ////////////////////////////////////////////////////////////////////////// +// +// override Obj? receive(Obj? msg) { +// // rewritten to fail fast +// try { +// list := (Obj[])msg +// return ((Method)list[0]).callOn(this, ((Unsafe)list[1]).val) +// } catch(Err e) { +// e.trace //TODO: Add normal error reporting +// throw e +// } +// } +// +// override Void notify(WorkspaceChange change) { +// runInManager(#doNotify, [change]) +// } +// +// ////////////////////////////////////////////////////////////////////////// +// // Public API +// ////////////////////////////////////////////////////////////////////////// +// +// internal Void init() { send([#doInit,Unsafe([,])].toImmutable) } +// +// @Operator FantomProject? get(IProject project) { runInManager(#doGet, [project]) } +// +// FantomProject? getByPod(Str podName) { runInManager(#doGetByPod, [podName]) } +// +// FantomProject[] listProjects() { runInManager(#doListProjects, [,]) } +// +// ** SlimerDude - Apr 2020 - A beta feature to cut down on build thrashing +// ** 'cos I dunno why we need ALL projects as dependencies!? +// FantomProject[] listReferencedProjects(Str podName) { runInManager(#doListReferencedProjects, [podName]) } +// +// ////////////////////////////////////////////////////////////////////////// +// // Method handlers +// ////////////////////////////////////////////////////////////////////////// +// +// private Obj? runInManager(Method method, Obj?[] args) { marker ? method.callOn(this,args) : send([method,Unsafe(args)].toImmutable).get } +// +// private Void doNotify(WorkspaceChange change) { +// // we need to reset containers for all projects that depend on +// // closed or opened projects and for all projects with updated content +// projectsToUpdate := [Str:IProject][:] +// +// //add opened projects +// change.openedProjects.each |IProject p| { +// if (isFantomProject(p)) addProject(p) +// } +// +// [change.closedProjects, change.openedProjects].flatten.each |IProject p| { +// dependentProjects(p).each |IProject d| { +// projectsToUpdate[d.getName] = d +// } +// } +// +// // add updated and opened projects to projectsToUpdate +// [change.updatedProjects, change.openedProjects].flatten.each |IProject p| { +// if(isFantomProject(p)) projectsToUpdate[p.getName] = p +// } +// +// // now we need to exclude all removed projects for this list +// projectsToUpdate = projectsToUpdate.exclude |v, k| { +// change.closedProjects.any { getName == k } +// } +// +// // remove all closed projects +// change.closedProjects.each |p| { removeProject(p) } +// +// // buildfanChanged(change.updatedProjects.first) +// projectsToUpdate.vals.each |p| { +// addProject(p) +// } +// +// projectsToUpdate.vals.each |p| { +// resetter.reset(p) +// } +// } +// +// private Bool isFantomProject(IProject p) { +// if (!p.exists) return projects[getKey(p)] != null +// return p.getNature(F4Nature.id) != null +// } +// +// private Void removeProject(IProject p) { +// key := getKey(p) +// fp := projects[key] +// if (fp == null) return +// projects.remove(key) +// pods.remove(fp.podName) +// } +// +// private static Str getKey(IProject p) { p.getName } +// +// private FantomProject addProject(IProject p) { +// fp := FantomProject.makeFromProject(p) +// removeProject(p) +// projects[getKey(p)] = fp +// pods[fp.podName] = fp +// return fp +// } +// +// private IProject[] dependentProjects(IProject project) { +// fp := projects[getKey(project)] +// if(fp == null) return IProject[,] +// podName := fp.podName +// ps := projects +// return ps.vals.findAll |p| { +// p.rawDepends.any |d| { +// d.name == podName +// } +// }.map { it.project } +// } +// +// private FantomProject? doGet(IProject project) { +// if (!project.isOpen || !project.isAccessible) +// return null +// +// // SlimerDude - Apr 2020 - not sure why a null location prevents us from returning the project? +// if (project.getLocation == null) { +// echo("location is null for project $project.getName") +// return null +// } +// key := getKey(project) +// if (projects.containsKey(key)) return projects[key] +// return addProject(project) +// } +// +// private Void doInit() { +// setMarker +// DLTKCore.create(ResourcesPlugin.getWorkspace.getRoot).getScriptProjects(F4Nature.id).each |IScriptProject sp| { +// addProject(sp.getProject) +// } +// } +// +// private FantomProject[] doListProjects() { projects.vals.toImmutable } +// +// private FantomProject? doGetByPod(Str podName) { pods[podName] } +// +// private FantomProject[] doListReferencedProjects(Str podName) { +// fp := pods[podName] +// projs := fp.project.getReferencedProjects.map { doGet(it) }.exclude { it == null } +// return projs.toImmutable +// } +// +// ////////////////////////////////////////////////////////////////////////// +// // Private hepler methods +// ////////////////////////////////////////////////////////////////////////// +// +// private FantomProject? createProject(IProject project) { +// try { +// return FantomProject.makeFromProject(project) +// } catch (Err e) { +// e.trace +// return null +// } +// } +// +// ////////////////////////////////////////////////////////////////////////// +// // Locals-backed properties +// ////////////////////////////////////////////////////////////////////////// +// +// ** Fantom projects by location +// private Str:FantomProject projects() { +// locals.getOrAdd("projects") |->Obj| { [Str:FantomProject][:] } +// } +// +// ** Fantom projects by pod name +// private Str:FantomProject pods() { +// locals.getOrAdd("pods") |->Obj| { [Str:FantomProject][:] } +// } +// +// private Bool marker() { locals[toStr] as Bool ?: false } +// private Void setMarker() { locals[toStr] = true } +//} diff --git a/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan b/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan new file mode 100644 index 00000000..c0a39e05 --- /dev/null +++ b/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan @@ -0,0 +1,277 @@ +using [java] org.eclipse.core.resources::IProject +using [java] org.eclipse.dltk.core::IScriptProject +using [java] org.eclipse.dltk.core::DLTKCore +using [java] org.eclipse.core.resources::ResourcesPlugin +using concurrent::ActorPool + +using [java] org.eclipse.core.resources::IResourceChangeListener +using [java] org.eclipse.core.resources::IResourceChangeEvent +using [java] org.eclipse.core.resources::IResource +using [java] org.eclipse.core.resources::IResourceDeltaVisitor +using [java] org.eclipse.core.resources::IResourceDelta +using [java]org.eclipse.jdt.core::IJavaProject + + +const class FantomProjectManager2 : IResourceChangeListener { + static const FantomProjectManager2? instance := FantomProjectManager2() + + private const SynchronizedState fantomProjects + + private new make() { + this.fantomProjects = SynchronizedState.makeWithType(ActorPool(), FantomProjectManagerState#) + init + } + + FantomProject? get(IProject project) { + projectRef := Unsafe(project) + return call { it.getOrAdd(projectRef) } + } + + FantomProject? getByPodName(Str podName) { + call { it.getByPodName(podName) } + } + + FantomProject[] allProjects() { + call { it.allProjects } + } + + FantomProject[] dependentProjects(FantomProject fp) { + call { it.dependentProjects(fp) } + } + + override Void resourceChanged(IResourceChangeEvent? event) { + // TODO move to deltaVis - use the Fantom closure interface thing + if (event.getType == IResourceChangeEvent.POST_CHANGE) { + visitor := DeltaVisitor2() + event.getDelta.accept(visitor) + notify(visitor.workspaceChanges) + } + } + + private Void notify(WorkspaceChange2 change) { + changeRef := Unsafe(change) + call { it.applyChanges(changeRef.val) } + } + + private Void init() { + call { it.init } + DLTKCore.addPreProcessingResourceChangedListener(this, IResourceChangeEvent.POST_CHANGE) + } + + private Obj? call(|FantomProjectManagerState->Obj?| state) { + fantomProjects.sync(state) + } +} + +internal class FantomProjectManagerState { + private ContainerResetter resetter + private Str:FantomProject projects + + new make() { + this.resetter = ContainerResetter(ActorPool()) + this.projects = Str:FantomProject[:] + } + + Obj? init() { + DLTKCore.create(ResourcesPlugin.getWorkspace.getRoot).getScriptProjects(F4Nature.id).each |IScriptProject sp| { + updateProject(sp.getProject) + } + return null + } + + Obj? applyChanges(WorkspaceChange2 change) { + // reset containers for all projects that depend on + // closed or opened projects and for all projects with updated content + + projectsToUpdate := [Str:IProject][:] { it.ordered = true } + + // add updated and opened Fantom projects + change.openedProjects.each { + if (isFantomProject(it)) projectsToUpdate[it.getName] = it + } + change.updatedProjects.each { + if (isFantomProject(it)) projectsToUpdate[it.getName] = it + } + + + // add parent projects + change.openedProjects.each { + parentProjects(it).each { projectsToUpdate[it.getName] = it } + } + change.closedProjects.each { + parentProjects(it).each { projectsToUpdate[it.getName] = it } + } + + + // remove all closed projects + change.closedProjects.each { + projectsToUpdate.remove(it.getName) + projects .remove(it.getName) + } + + + // do the update + projectsToUpdate.vals.each { + updated := updateProject(it) + if (updated) + resetter.reset(it) + else + projects[it.getName].update + } + + return null + } + + private IProject[] parentProjects(IProject ip) { + fp := projects[ip.getName] + if (fp == null) + return IProject#.emptyList + + parents := IProject[,] + projs := projects.vals + for (i := 0; i < projs.size; ++i) { + proj := projs[i] + deps := proj.rawDepends + for (j := 0; j < deps.size; ++j) { + if (deps[j].name == fp.podName) + parents.add(proj.project) + } + } + return parents + } + + FantomProject? getOrAdd(Unsafe projectRef) { + ip := (IProject) projectRef.val + updateProject(ip) + return projects[ip.getName] + } + + FantomProject? getByPodName(Str podName) { + projects.find { it.podName == podName } + } + + FantomProject[] allProjects() { + projects.vals.toImmutable + } + + FantomProject[] dependentProjects(FantomProject fp) { +`/f4log.txt`.toFile.out(true).writeChars("----\n").close + des:=doDependentProjects(fp, Str:FantomProject[:]).vals.toImmutable +`/f4log.txt`.toFile.out(true).writeChars("$fp ==> $des\n").close + return des + } + + private Str:FantomProject doDependentProjects(FantomProject fp, Str:FantomProject fps) { +`/f4log.txt`.toFile.out(true).writeChars(" $fp --> ${fp.rawDepends}\n").close + for (i := 0; i < fp.rawDepends.size; ++i) { + podName := fp.rawDepends[i].name + +`/f4log.txt`.toFile.out(true).writeChars(" $fp --> has ${podName} == ${fps.containsKey(podName)} \n").close + + // prevent infinite cyclic dependencies + if (!fps.containsKey(podName)) { + project := getByPodName(podName) +`/f4log.txt`.toFile.out(true).writeChars(" $fp --> pro ${podName} == ${project}\n").close + + if (project != null) { +`/f4log.txt`.toFile.out(true).writeChars(" $fp --> $podName\n").close + fps[podName] = project + doDependentProjects(project, fps) + } + } + } + return fps + } + + ** Returns 'true' if the project was updated + private Bool updateProject(IProject ip) { + // if the existing project looks okay, let's keep it! + fp := projects[ip.getName] + if (fp != null) { +//`/f4log.txt`.toFile.out(true).writeChars("$ip.getName Proj exists (build.fan diff-${fp.buildFanHasChanged}) (${fp.resolveErrs}) \n").close + if (fp.resolveErrs.isEmpty && fp.buildFanHasChanged == false) { + return false + } + } + +`/f4log.txt`.toFile.out(true).writeChars("$ip.getName MAKING NEW!\n").close + fp = FantomProject.makeFromProject(ip) + projects[ip.getName] = fp + return true + } + + private Bool isFantomProject(IProject ip) { + ip.exists + ? ip.getNature(F4Nature.id) != null + : projects[ip.getName] != null + } +} + +** Visits resource delta and collects changes +class DeltaVisitor2 : IResourceDeltaVisitor { + IProject[] closedProjects := IProject[,] + IProject[] openedProjects := IProject[,] + IProject[] updatedProjects := IProject[,] + + override Bool visit(IResourceDelta? delta) { + resource := delta.getResource + + switch (resource.getType) { + case IResource.PROJECT: + project := (IProject) resource + + if (!project.exists || projectClosed(delta, project)) { +`/f4log.txt`.toFile.out(true).writeChars("$project.getName - PROJ CLOSED\n").close + closedProjects.add(project) + return false + } + + if (projectOpened(delta, project)) { +`/f4log.txt`.toFile.out(true).writeChars("$project.getName - PROJ OPENED\n").close + openedProjects.add(project) + return false + } + + return true + + case IResource.FOLDER: + return false + + case IResource.FILE: + if ((resource.getName == Manifest.filename || resource.getName == IJavaProject.CLASSPATH_FILE_NAME) && contentChanged(delta)) +`/f4log.txt`.toFile.out(true).writeChars("$resource.getProject.getName - FILE CHANGE\n").close + updatedProjects.add(resource.getProject) + return false + + default: + return true + } + } + + WorkspaceChange2 workspaceChanges() { + WorkspaceChange2 { + it.closedProjects = this.closedProjects + it.openedProjects = this.openedProjects + it.updatedProjects = this.updatedProjects + } + } + + private static Bool contentChanged(IResourceDelta delta) { + switch (delta.getKind) { + case IResourceDelta.CHANGED: + return delta.getFlags.and(IResourceDelta.CONTENT) != 0 + default: + return true + } + } + + private static Bool projectClosed(IResourceDelta delta, IProject project) { openChange(delta) && !project.isOpen } + private static Bool projectOpened(IResourceDelta delta, IProject project) { openChange(delta) && project.isOpen } + private static Bool openChange (IResourceDelta delta) { delta.getFlags.and(IResourceDelta.OPEN) != 0 } +} + +class WorkspaceChange2 { + IProject[] closedProjects := IProject[,] + IProject[] openedProjects := IProject[,] + IProject[] updatedProjects := IProject[,] +} diff --git a/com.xored.f4.core/fan/manifest/InterpreterContainer.fan b/com.xored.f4.core/fan/manifest/InterpreterContainer.fan index 1274fd95..b1690c1a 100644 --- a/com.xored.f4.core/fan/manifest/InterpreterContainer.fan +++ b/com.xored.f4.core/fan/manifest/InterpreterContainer.fan @@ -33,12 +33,12 @@ class InterpreterContainer : IInterpreterContainerExtension, IInterpreterContain if (project == null || librariesList == null) return entries := IBuildpathEntry[,] - fpm := FantomProjectManager.instance - fantomProject := fpm[project.getProject] + fpm := FantomProjectManager2.instance + fantomProject := fpm.get(project.getProject) interpreter := fantomProject?.interpreterInstall?.getInstallLocation?.getPath - fantomProject.resolvePods.each |podFile, podName| { - podProject := fpm.getByPod(podName) + fantomProject.resolvedPods.each |podFile, podName| { + podProject := fpm.getByPodName(podName) if (podProject != null) { entries.add(DLTKCore.newProjectEntry(podProject.project.getFullPath)) return diff --git a/com.xored.f4.core/fan/manifest/Manifest.fan b/com.xored.f4.core/fan/manifest/Manifest.fan index 4f37d522..a41065cb 100644 --- a/com.xored.f4.core/fan/manifest/Manifest.fan +++ b/com.xored.f4.core/fan/manifest/Manifest.fan @@ -5,14 +5,14 @@ using f4parser using f4model class Manifest { - private Range:Int lineOffsets + private Range:Int lineOffsets new make(FantomProject fantomProject) { project := fantomProject.project - content := PathUtil.resolveRes(project.getFile(filename)).readAllStr - parser := Parser(content, EmptyNamespace()) - lineOffsets = buildOffsets(content) + buildFanStr = PathUtil.resolveRes(project.getFile(filename)).readAllStr + lineOffsets = buildOffsets(buildFanStr) + parser := Parser(buildFanStr, EmptyNamespace()) MethodDef? method := parser.cunit.types.find { it.name.text == "Build" }?.slots?.find { it->name->text == "make" } if (method == null) throw ArgErr("Can't parse build.fan in $project.getName") @@ -37,6 +37,7 @@ class Manifest { public static const Str filename := "build.fan" ** Lines of field initializers + Str buildFanStr Str:Int lines { private set } Str:Obj? vals { private set } Str? podName() { vals["podName"] } diff --git a/com.xored.f4.core/fan/manifest/ProjectPrefs.fan b/com.xored.f4.core/fan/manifest/ProjectPrefs.fan index 667ac0ec..64d6f646 100644 --- a/com.xored.f4.core/fan/manifest/ProjectPrefs.fan +++ b/com.xored.f4.core/fan/manifest/ProjectPrefs.fan @@ -48,7 +48,7 @@ class ProjectPrefs { // Eclipse's stoopid Oomph plugin somehow keeps setting the env to afFpm even though it's not installed! // so let's not error and instead just resort to something sensible - type := Type.find(name, false) ?: DefaultCompileEnv#.qname + type := Type.find(name, false) ?: DefaultCompileEnv# return type } } diff --git a/com.xored.f4.core/fan/model/DltkNamespace.fan b/com.xored.f4.core/fan/model/DltkNamespace.fan index e8357699..bb8a4197 100644 --- a/com.xored.f4.core/fan/model/DltkNamespace.fan +++ b/com.xored.f4.core/fan/model/DltkNamespace.fan @@ -30,7 +30,8 @@ internal class DltkNamespace : IFanNamespace { switch(fragment.getKind) { case Fragment.K_SOURCE: // source fragment - return FantomProjectManager.instance[(fragment.getParent as IScriptProject).getProject].podName + project := (fragment.getParent as IScriptProject).getProject + return FantomProjectManager2.instance.get(project).podName default: // pod fragment // TODO maybe podName resolution should be deferred to the CompileEnv as that provides the pod Files in the first place diff --git a/com.xored.f4.core/fan/parser/SourceIndexer.fan b/com.xored.f4.core/fan/parser/SourceIndexer.fan index 49ae6fd1..c4dbb791 100644 --- a/com.xored.f4.core/fan/parser/SourceIndexer.fan +++ b/com.xored.f4.core/fan/parser/SourceIndexer.fan @@ -17,7 +17,8 @@ class SourceIndexer { private static CUnit cunit(IModuleSource module) { if(module is ISourceModule) return SourceParserUtil.parse(module as ISourceModule, null)->unit - ns := FantomProjectManager.instance[module.getModelElement.getScriptProject.getProject].ns + proj := module.getModelElement.getScriptProject.getProject + ns := FantomProjectManager2.instance.get(proj).ns return Parser(module.getSourceContents, ns).cunit } } diff --git a/com.xored.f4.core/fan/selection/SelectionEngine.fan b/com.xored.f4.core/fan/selection/SelectionEngine.fan index 4ef96ab4..ee15478a 100644 --- a/com.xored.f4.core/fan/selection/SelectionEngine.fan +++ b/com.xored.f4.core/fan/selection/SelectionEngine.fan @@ -26,7 +26,8 @@ class SelectionEngine : ISelectionEngine { override IModelElement?[]? select(IModuleSource? module, Int start, Int end) { DltkAst ast := SourceParserUtil.parse(module as ISourceModule, null) src = module.getSourceContents - fp := FantomProjectManager.instance[module.getModelElement.getScriptProject.getProject] + ip := module.getModelElement.getScriptProject.getProject + fp := FantomProjectManager2.instance.get(ip) ns = ParseUtil.ns((ISourceModule)module.getModelElement) unit = ast.unit diff --git a/com.xored.f4.core/fan/util/ParseUtil.fan b/com.xored.f4.core/fan/util/ParseUtil.fan index 7a931126..3e5cb86c 100644 --- a/com.xored.f4.core/fan/util/ParseUtil.fan +++ b/com.xored.f4.core/fan/util/ParseUtil.fan @@ -39,12 +39,10 @@ class ParseUtil : TypeUtil Parser(module.getSource, ns(module)).cunit } - static IFanNamespace ns(ISourceModule module) - { + static IFanNamespace ns(ISourceModule module) { sp := module.getScriptProject - fp := FantomProjectManager.instance[sp.getProject] - if( module.isBinary) - { + fp := FantomProjectManager2.instance.get(sp.getProject) + if( module.isBinary) { fragment := module.getAncestor(IModelElement.PROJECT_FRAGMENT) if(fragment is PodFragment) { @@ -52,7 +50,7 @@ class ParseUtil : TypeUtil // TODO maybe podName resolution should be deferred to the CompileEnv as that provides the pod Files in the first place fileName := fg.getPath.removeFileExtension.lastSegment podName := fileName.contains("-") ? fileName[0.. Bool| { if(res.getType == IResource.FILE && res.getName.equalsIgnoreCase(name)) { @@ -91,7 +92,7 @@ class SourceLookupParticipant : AbstractSourceLookupParticipant { private IModelElement[] findInLibs(Str pod, Str name) { result := IModelElement[,] - FantomProjectManager.instance.listProjects.each |fp| { + FantomProjectManager2.instance.allProjects.each |fp| { fp.scriptProject.getProjectFragments.each |IProjectFragment pf| { if (pf isnot PodFragment) return podFragment := pf as PodFragment diff --git a/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan b/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan index 740bf858..99b419de 100644 --- a/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan +++ b/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan @@ -5,6 +5,7 @@ using [java] org.eclipse.dltk.launching using [java] org.eclipse.jdt.core using f4core +using f4core::FantomProjectManager2 ** ** This adds .jars to the "Fantom Native Libraries (Java)" @@ -39,9 +40,9 @@ class FanJavaContainer : IClasspathContainer { createLibrary(f.normalize, home + `src/${f.basename}/java/`) // maybe it's a core Fantom pod } - FantomProject fp := FantomProjectManager.instance[project.getProject] + FantomProject fp := FantomProjectManager2.instance.get(project.getProject) fp.classpathDepends.each |loc, name| { - podFP := FantomProjectManager.instance.getByPod(name) + podFP := FantomProjectManager2.instance.getByPodName(name) if (podFP != null) { IProject prj := podFP.project if (prj.isAccessible && prj.hasNature("org.eclipse.jdt.core.javanature")) { @@ -81,6 +82,9 @@ class FanJavaContainer : IClasspathContainer { private static Bool isJavaPod(File f) { if (!f.exists) return false + // FIXME Slimer is this okay? I mean, why open the zip during a build thrash!? + return f.ext == "jar" || f.ext == "pod" || f.ext == ".zip" + zip := Zip.read(f.in) try { File? entry diff --git a/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan b/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan index 1a89e1d0..d3db3b84 100644 --- a/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan +++ b/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan @@ -14,6 +14,7 @@ using [java] org.eclipse.swt.widgets::Display using [java] org.eclipse.ui::PlatformUI using f4launching using f4core +using f4core::FantomProjectManager2 ** Used by FanJavaLaunchConfig & f4testing::FanTestingLaunchConfig class FanJavaLaunchUtil { @@ -64,7 +65,7 @@ class FanJavaLaunchUtil { static Str?[]? environment(ILaunchConfiguration? config, Str?[]? base) { iProj := AbstractScriptLaunchConfigurationDelegate.getProject(config) - proj := FantomProjectManager.instance.get(iProj) + proj := FantomProjectManager2.instance.get(iProj) compileEnv := proj.compileEnv copy := config.getWorkingCopy @@ -110,7 +111,7 @@ class FanJavaLaunchUtil { static Bool confirmLaunch(ILaunchConfiguration config) { iProj := AbstractScriptLaunchConfigurationDelegate.getProject(config) - proj := FantomProjectManager.instance.get(iProj) + proj := FantomProjectManager2.instance.get(iProj) projsInErr := findOpenProjects(proj, config).findAll { it.hasBuildErrs } if (projsInErr.isEmpty) return true @@ -129,8 +130,8 @@ class FanJavaLaunchUtil { projectList := (Str[]) config.getAttribute(LaunchConsts.projectList, ArrayList()).toArray otherProjects := fp.prefs.referencedPodsOnly - ? FantomProjectManager.instance.listReferencedProjects(fp.podName) - : FantomProjectManager.instance.listProjects + ? FantomProjectManager2.instance.getByPodName(fp.podName).dependentProjects.add(fp) + : FantomProjectManager2.instance.allProjects return otherProjects .exclude { it.isPlugin } diff --git a/com.xored.f4.launchEnv/fan/F4LaunchEnv.fan b/com.xored.f4.launchEnv/fan/F4LaunchEnv.fan index 6fd58e9f..7f4bc696 100644 --- a/com.xored.f4.launchEnv/fan/F4LaunchEnv.fan +++ b/com.xored.f4.launchEnv/fan/F4LaunchEnv.fan @@ -9,7 +9,7 @@ const class F4LaunchEnv : Env { static new make() { // Env.cur defaults to BootEnv until we create ourselves - podLocs := Env.cur.vars["FAN_ENV_PODS"]?.trim?.split(File.pathSep.chars.first, true) + podLocs := Env.cur.vars["FAN_ENV_PODS"]?.trim?.split(File.pathSep.chars.first, true)?.exclude { it.trimToNull == null } if (podLocs != null && podLocs.size == 0) podLocs = null @@ -27,12 +27,16 @@ const class F4LaunchEnv : Env { private const Str:File podLocations - private new makeInternal(Str[] podLocs, Env parent) : super.make(parent) { - podLocations = Str:File[:] { it.ordered=true }.addList(podLocs.map { File.os(it) }) { it.basename } - podLocations.vals.each { - if (!it.exists) - throw Err("Pod file does not exist - ${it.osPath}") + private new makeInternal(Str[]? podLocs, Env parent) : super.make(parent) { + podLocations := Str:File[:] { it.ordered=true } + if (podLocs != null) { + podLocations.addList(podLocs.map { File.os(it).normalize }) { it.basename } + podLocations.vals.each { + if (!it.exists) + throw Err("Pod file does not exist - ${it.osPath}") + } } + this.podLocations = podLocations } override File? findPodFile(Str podName) { diff --git a/com.xored.f4.launching/fan/LaunchConfigDelegate.fan b/com.xored.f4.launching/fan/LaunchConfigDelegate.fan index 17f7d049..aef08289 100644 --- a/com.xored.f4.launching/fan/LaunchConfigDelegate.fan +++ b/com.xored.f4.launching/fan/LaunchConfigDelegate.fan @@ -13,6 +13,7 @@ using [java] org.eclipse.core.runtime using [java] org.eclipse.core.resources using [java] org.eclipse.core.variables using f4core +using f4core::FantomProjectManager2 class LaunchConfigDelegate : AbstractScriptLaunchConfigurationDelegate { override Str? getLanguageId() { return F4Nature.id } @@ -53,7 +54,8 @@ class LaunchConfigDelegate : AbstractScriptLaunchConfigurationDelegate { Bool useClassOnly := config.getAttribute(LaunchConsts.useClassOnly, false) if(projectName.isEmpty || useClassOnly) return className - podName := FantomProjectManager.instance[ResourcesPlugin.getWorkspace.getRoot.getProject(projectName)].podName + proj := ResourcesPlugin.getWorkspace.getRoot.getProject(projectName) + podName := FantomProjectManager2.instance.get(proj).podName if(className.isEmpty) return podName return "$podName::$className" diff --git a/com.xored.f4.search/fan/callHierarchy/CalleeProcessor.fan b/com.xored.f4.search/fan/callHierarchy/CalleeProcessor.fan index cf609590..fad88f13 100644 --- a/com.xored.f4.search/fan/callHierarchy/CalleeProcessor.fan +++ b/com.xored.f4.search/fan/callHierarchy/CalleeProcessor.fan @@ -18,9 +18,8 @@ using "[java]org.eclipse.dltk.internal.core" using f4core using f4parser using f4model -** -** -** +using f4core::FantomProjectManager2 + class CalleeProcessor : ICalleeProcessor { private IMethod? method @@ -74,7 +73,7 @@ class MethodVisitor : AstVisitor { private static IMethod[] resolve(SlotRef slot) { IMethod[] methods := [,] - project := FantomProjectManager.instance.getByPod(slot.modelSlot.type.pod) + project := FantomProjectManager2.instance.getByPodName(slot.modelSlot.type.pod) if( project == null) return methods project.scriptProject.getScriptFolders.each { ((IScriptFolder)it).getSourceModules.each { diff --git a/com.xored.f4.testing/fan/FanTestRunnerUI.fan b/com.xored.f4.testing/fan/FanTestRunnerUI.fan index f0833ea3..7238f383 100644 --- a/com.xored.f4.testing/fan/FanTestRunnerUI.fan +++ b/com.xored.f4.testing/fan/FanTestRunnerUI.fan @@ -20,6 +20,7 @@ using [java] java.lang::Class using [java] com.xored.f4.debug.ui::JavaOpenEditorAction using f4core +using f4core::FantomProjectManager2 using f4model using f4debugUi @@ -117,7 +118,7 @@ class FanTestRunnerUI : AbstractTestRunnerUI, ITestElementResolver { method := podTypeMethod[2] if(pod == null) return null - fp := FantomProjectManager.instance.getByPod(pod) + fp := FantomProjectManager2.instance.getByPodName(pod) if(fp == null) return null IMethod? result := diff --git a/com.xored.f4.testing/fan/FanTestingLaunchShortcut.fan b/com.xored.f4.testing/fan/FanTestingLaunchShortcut.fan index 2707d90d..908ad29c 100644 --- a/com.xored.f4.testing/fan/FanTestingLaunchShortcut.fan +++ b/com.xored.f4.testing/fan/FanTestingLaunchShortcut.fan @@ -23,6 +23,7 @@ using [java] org.eclipse.dltk.core.IProjectFragment using f4debugUi using f4core +using f4core::FantomProjectManager2 using f4launching class FanTestingLaunchShortcut : AbstractScriptLaunchShortcut { @@ -71,7 +72,7 @@ class FanTestingLaunchShortcut : AbstractScriptLaunchShortcut { attrs := [Str:Obj?][:] typeName:="" if (!(script is IProject)) { - fp := FantomProjectManager.instance[script.getProject] + fp := FantomProjectManager2.instance.get(script.getProject) if(fp == null) return null //not a fantom project sourceModule := DLTKCore.createSourceModuleFrom(script) diff --git a/com.xored.f4.testing/fan/FanTestingTabGroup.fan b/com.xored.f4.testing/fan/FanTestingTabGroup.fan index 5cfd075a..dfd564b8 100644 --- a/com.xored.f4.testing/fan/FanTestingTabGroup.fan +++ b/com.xored.f4.testing/fan/FanTestingTabGroup.fan @@ -16,6 +16,7 @@ using [java] org.eclipse.swt.layout using [java] org.eclipse.swt.events using [java] org.eclipse.swt using f4core +using f4core::FantomProjectManager2 using f4launching using f4model @@ -95,7 +96,7 @@ class FanTestingMainTab : MainLaunchConfigurationTab { className := mainClassText if(className.isEmpty) return true //that's ok, tests from entire pod - ns := FantomProjectManager.instance[getProject.getProject].ns + ns := FantomProjectManager2.instance.get(getProject.getProject).ns type := ns.currPod.findType(className,false) if(type == null) { setErrorMessage("Class $className is not found") diff --git a/com.xored.f4.ui.text/fan/folding/FanCodeFoldingBlockProvider.fan b/com.xored.f4.ui.text/fan/folding/FanCodeFoldingBlockProvider.fan index 8dc0b788..1bd412b8 100644 --- a/com.xored.f4.ui.text/fan/folding/FanCodeFoldingBlockProvider.fan +++ b/com.xored.f4.ui.text/fan/folding/FanCodeFoldingBlockProvider.fan @@ -52,7 +52,7 @@ class FanCodeFoldingBlockProvider : IFoldingBlockProvider project := content?.getModelElement?.getScriptProject?.getProject if(project == null) return EmptyNamespace() if (!(project?.isOpen ?: false)) return EmptyNamespace() - return FantomProjectManager.instance[project].ns + return FantomProjectManager2.instance.get(project).ns } override Void computeFoldableBlocks(IFoldingContent? content) { From 222c7173089b27a5078e69361bcc109f9a2888fd Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Wed, 29 Apr 2020 14:33:42 +0100 Subject: [PATCH 06/17] Tweak out crippling DLTK errors --- com.xored.f4.builder/fan/InternalBuilder.fan | 2 +- com.xored.f4.core/fan/manifest/FantomProject.fan | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/com.xored.f4.builder/fan/InternalBuilder.fan b/com.xored.f4.builder/fan/InternalBuilder.fan index a70126bc..fb7868dd 100644 --- a/com.xored.f4.builder/fan/InternalBuilder.fan +++ b/com.xored.f4.builder/fan/InternalBuilder.fan @@ -55,7 +55,7 @@ class InternalBuilder : Builder { // SlimerDude - Apr 2020 - Beta feature to turn this off if (fp.prefs.referencedPodsOnly == false) FantomProjectManager2.instance.allProjects.each |p| { - resolvedPods[p.podName] = p.podOutFile + resolvedPods = resolvedPods.rw[p.podName] = p.podOutFile } logger := ConsoleLogger(consumer) diff --git a/com.xored.f4.core/fan/manifest/FantomProject.fan b/com.xored.f4.core/fan/manifest/FantomProject.fan index 1913f8c4..075a9053 100644 --- a/com.xored.f4.core/fan/manifest/FantomProject.fan +++ b/com.xored.f4.core/fan/manifest/FantomProject.fan @@ -168,7 +168,14 @@ const class FantomProject { private Str:File doClasspathDepends() { `/f4log.txt`.toFile.out(true).writeChars("$project.getName - classpathDepends()\n").close - buildPathFiles := (Str:File) scriptProject.getResolvedBuildpath(false).findAll |IBuildpathEntry bp->Bool| { + entries := IBuildpathEntry?[,] + + try entries = scriptProject.getResolvedBuildpath(false) + catch (Err err) + // F4 hangs when getResolvedBuildpath() errors + resolveErrsRef.val = resolveErrs.rw.add(err) + + buildPathFiles := (Str:File) entries.findAll |IBuildpathEntry bp->Bool| { !bp.getPath.segments.first.toStr.startsWith(IBuildpathEntry.BUILDPATH_SPECIAL) } .map |IBuildpathEntry bp -> File?| { @@ -235,7 +242,7 @@ const class FantomProject { // prevent errs such as "Project cannot reference itself: poo" podFiles.remove(podName) -`/f4log.txt`.toFile.out(true).writeChars("$project.getName - setting resolved pods to ${podFiles}\n").close +//`/f4log.txt`.toFile.out(true).writeChars("$project.getName - setting resolved pods to ${podFiles}\n").close this.resolvedPodsRef.val = podFiles.toImmutable From bb91e568b025cb7906aef06ed6a1ea262b49dbbf Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Wed, 29 Apr 2020 15:10:38 +0100 Subject: [PATCH 07/17] Deleted tmp log file logging --- com.xored.f4.builder/fan/InternalBuilder.fan | 1 - .../fan/manifest/ContainerResetter.fan | 3 --- .../fan/manifest/FantomProject.fan | 18 +++--------------- .../fan/manifest/FantomProjectManager2.fan | 12 ------------ 4 files changed, 3 insertions(+), 31 deletions(-) diff --git a/com.xored.f4.builder/fan/InternalBuilder.fan b/com.xored.f4.builder/fan/InternalBuilder.fan index fb7868dd..e2d78cc1 100644 --- a/com.xored.f4.builder/fan/InternalBuilder.fan +++ b/com.xored.f4.builder/fan/InternalBuilder.fan @@ -38,7 +38,6 @@ class InternalBuilder : Builder { compileDir.create compileDir.listFiles.each { it.delete } -`/f4log.txt`.toFile.out(true).writeChars("$fp.podName BUILDING\n").close resolvedPods := fp.resolvedPods bldLoc := Loc(fp.buildFile) diff --git a/com.xored.f4.core/fan/manifest/ContainerResetter.fan b/com.xored.f4.core/fan/manifest/ContainerResetter.fan index 1bcb3fa6..5ca025a3 100644 --- a/com.xored.f4.core/fan/manifest/ContainerResetter.fan +++ b/com.xored.f4.core/fan/manifest/ContainerResetter.fan @@ -36,9 +36,6 @@ const class ContainerResetter : Actor { return null } -`/f4log.txt`.toFile.out(true).writeChars("$project.getName Resetting\n").close -echo("Resetting $project.getName") - scriptProject := DLTKCore.create(project) if (scriptProject.exists) { DLTKCore.getBuildpathContainerInitializer(ScriptRuntime.INTERPRETER_CONTAINER) diff --git a/com.xored.f4.core/fan/manifest/FantomProject.fan b/com.xored.f4.core/fan/manifest/FantomProject.fan index 075a9053..caee4a78 100644 --- a/com.xored.f4.core/fan/manifest/FantomProject.fan +++ b/com.xored.f4.core/fan/manifest/FantomProject.fan @@ -166,14 +166,12 @@ const class FantomProject { ** - called by f4jdtLaunching.FanJavaContainer.getClasspathEntries() ** - called by f4core.FantomProjectManager.doListReferencedProjects() private Str:File doClasspathDepends() { -`/f4log.txt`.toFile.out(true).writeChars("$project.getName - classpathDepends()\n").close - entries := IBuildpathEntry?[,] try entries = scriptProject.getResolvedBuildpath(false) catch (Err err) - // F4 hangs when getResolvedBuildpath() errors - resolveErrsRef.val = resolveErrs.rw.add(err) + // F4 hangs when getResolvedBuildpath() errors - usually when there's an unknown Java JDK + resolveErrsRef.val = resolveErrs.rw.add(err).toImmutable buildPathFiles := (Str:File) entries.findAll |IBuildpathEntry bp->Bool| { !bp.getPath.segments.first.toStr.startsWith(IBuildpathEntry.BUILDPATH_SPECIAL) @@ -202,12 +200,7 @@ const class FantomProject { r[v.basename] = v return r } - -// // new beta behaviour -// if (prefs.referencedPodsOnly) -// return buildPathFiles - - // old behaviour + podFiles := resolvedPods.rw.setAll(buildPathFiles) return podFiles } @@ -230,8 +223,6 @@ const class FantomProject { ** The workspace has changed somehow (projects updates) and we're involved somehow internal Void update() { -`/f4log.txt`.toFile.out(true).writeChars("$project.getName - UPDATING resolved PODS\n").close - podFiles := doResolvePods.rw // overwrite entries with workspace pods @@ -242,17 +233,14 @@ const class FantomProject { // prevent errs such as "Project cannot reference itself: poo" podFiles.remove(podName) -//`/f4log.txt`.toFile.out(true).writeChars("$project.getName - setting resolved pods to ${podFiles}\n").close this.resolvedPodsRef.val = podFiles.toImmutable - this.classpathDependsRef.val = doClasspathDepends.toImmutable } // ** Returns a map of pod names to pod files. // ** Only called by InterpreterContainer.processEntres() and InternalBuilder.buildPod() // Str:File resolvePods() { -//`/f4log.txt`.toFile.out(true).writeChars("$podName resolvingPods\n").close // podFiles := doResolvePods.rw // resolveErrs = compileEnv.resolveErrs.toImmutable // diff --git a/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan b/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan index c0a39e05..2d2e64f7 100644 --- a/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan +++ b/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan @@ -155,26 +155,19 @@ internal class FantomProjectManagerState { } FantomProject[] dependentProjects(FantomProject fp) { -`/f4log.txt`.toFile.out(true).writeChars("----\n").close des:=doDependentProjects(fp, Str:FantomProject[:]).vals.toImmutable -`/f4log.txt`.toFile.out(true).writeChars("$fp ==> $des\n").close return des } private Str:FantomProject doDependentProjects(FantomProject fp, Str:FantomProject fps) { -`/f4log.txt`.toFile.out(true).writeChars(" $fp --> ${fp.rawDepends}\n").close for (i := 0; i < fp.rawDepends.size; ++i) { podName := fp.rawDepends[i].name -`/f4log.txt`.toFile.out(true).writeChars(" $fp --> has ${podName} == ${fps.containsKey(podName)} \n").close - // prevent infinite cyclic dependencies if (!fps.containsKey(podName)) { project := getByPodName(podName) -`/f4log.txt`.toFile.out(true).writeChars(" $fp --> pro ${podName} == ${project}\n").close if (project != null) { -`/f4log.txt`.toFile.out(true).writeChars(" $fp --> $podName\n").close fps[podName] = project doDependentProjects(project, fps) } @@ -188,13 +181,11 @@ internal class FantomProjectManagerState { // if the existing project looks okay, let's keep it! fp := projects[ip.getName] if (fp != null) { -//`/f4log.txt`.toFile.out(true).writeChars("$ip.getName Proj exists (build.fan diff-${fp.buildFanHasChanged}) (${fp.resolveErrs}) \n").close if (fp.resolveErrs.isEmpty && fp.buildFanHasChanged == false) { return false } } -`/f4log.txt`.toFile.out(true).writeChars("$ip.getName MAKING NEW!\n").close fp = FantomProject.makeFromProject(ip) projects[ip.getName] = fp return true @@ -221,13 +212,11 @@ class DeltaVisitor2 : IResourceDeltaVisitor { project := (IProject) resource if (!project.exists || projectClosed(delta, project)) { -`/f4log.txt`.toFile.out(true).writeChars("$project.getName - PROJ CLOSED\n").close closedProjects.add(project) return false } if (projectOpened(delta, project)) { -`/f4log.txt`.toFile.out(true).writeChars("$project.getName - PROJ OPENED\n").close openedProjects.add(project) return false } @@ -239,7 +228,6 @@ class DeltaVisitor2 : IResourceDeltaVisitor { case IResource.FILE: if ((resource.getName == Manifest.filename || resource.getName == IJavaProject.CLASSPATH_FILE_NAME) && contentChanged(delta)) -`/f4log.txt`.toFile.out(true).writeChars("$resource.getProject.getName - FILE CHANGE\n").close updatedProjects.add(resource.getProject) return false From 738233b39f262ab3df8f86c7ee19ba3cb435f2b0 Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Wed, 29 Apr 2020 16:08:35 +0100 Subject: [PATCH 08/17] Code tidy Split eclipse project stuff in to own ProjectListener class --- .../fan/manifest/BuildfanListener.fan | 156 ------------------ .../fan/manifest/FantomProject.fan | 83 ++-------- .../fan/manifest/FantomProjectListener.fan | 99 +++++++++++ .../fan/manifest/FantomProjectManager2.fan | 109 +----------- com.xored.f4.core/fan/util/QuickCash.fan | 2 +- 5 files changed, 120 insertions(+), 329 deletions(-) delete mode 100644 com.xored.f4.core/fan/manifest/BuildfanListener.fan create mode 100644 com.xored.f4.core/fan/manifest/FantomProjectListener.fan diff --git a/com.xored.f4.core/fan/manifest/BuildfanListener.fan b/com.xored.f4.core/fan/manifest/BuildfanListener.fan deleted file mode 100644 index 769e4b0f..00000000 --- a/com.xored.f4.core/fan/manifest/BuildfanListener.fan +++ /dev/null @@ -1,156 +0,0 @@ -//using [java]org.eclipse.core.resources::IResourceChangeListener -//using [java]org.eclipse.core.resources::IResourceDeltaVisitor -//using [java]org.eclipse.core.resources::IResourceChangeEvent -//using [java]org.eclipse.core.resources::IResourceDelta -//using [java]org.eclipse.core.resources::IProject -//using [java]org.eclipse.core.resources::IResource -//using [java]org.eclipse.dltk.core::DLTKCore -//using [java]org.eclipse.jdt.core::IJavaProject -//using concurrent -// -//const class BuildfanListener : Actor, IResourceChangeListener { -// ////////////////////////////////////////////////////////////////////////// -// // Interface methods -// ////////////////////////////////////////////////////////////////////////// -// override Void resourceChanged(IResourceChangeEvent? event) { -// if(event.getType == IResourceChangeEvent.POST_CHANGE) { -// visitor := DeltaVisitor() -// event.getDelta.accept(visitor) -// if(visitor.change.isEmpty) return -// notifyListeners(visitor.change) -// } -// } -// -// ////////////////////////////////////////////////////////////////////////// -// // Constructor and overriden methods -// ////////////////////////////////////////////////////////////////////////// -// new make(ActorPool pool := ActorPool()) : super(pool) {} -// -// override Obj? receive(Obj? msg) { -// try -// { -// if(msg isnot Obj[]) return null -// list := msg as Obj[] -// if(list.first isnot Method) return null -// method := list.first as Method -// Obj?[] args := list[1..-1] -// if(list.size == 2 && list.last is Unsafe) -// args = list.last->val -// return method.callOn(this, args) -// } catch (Err e) { -// e.trace //TODO: add normal error reporting -// throw e -// } -// } -// -// ////////////////////////////////////////////////////////////////////////// -// // Public API -// ////////////////////////////////////////////////////////////////////////// -// -// Void addListener(BuildfanChangeListener listener) { -// send([#doAddListener, listener].toImmutable) -// } -// -// Void removeListener(BuildfanChangeListener listener) { -// send([#doRemoveListener, listener].toImmutable) -// } -// -// Void subscribe() { -// DLTKCore.addPreProcessingResourceChangedListener(this, IResourceChangeEvent.POST_CHANGE) -// } -// -// ////////////////////////////////////////////////////////////////////////// -// // Helper methods -// ////////////////////////////////////////////////////////////////////////// -// -// private Void notifyListeners(WorkspaceChange change) { -// send([#doNotify, Unsafe([change])].toImmutable) -// } -// -// private Void doNotify(WorkspaceChange change) { -// listeners.each { notify(change) } -// } -// -// private Void doAddListener(BuildfanChangeListener listener) { -// listeners.add(listener) -// } -// -// private Void doRemoveListener(BuildfanChangeListener listener) { -// listeners.remove(listener) -// } -// -// ////////////////////////////////////////////////////////////////////////// -// // Actor.locals properties -// ////////////////////////////////////////////////////////////////////////// -// -// BuildfanChangeListener[] listeners() { -// locals.getOrAdd("listeners") |->Obj| { BuildfanChangeListener[,] } -// } -//} -// -//************************************************************************** -//** DeltaVisitor -//************************************************************************** -// -//** Visits resource delta and collects changes -//class DeltaVisitor : IResourceDeltaVisitor { -// WorkspaceChange change := WorkspaceChange() -// -// override Bool visit(IResourceDelta? delta) { -// resource := delta.getResource -// switch(resource.getType) { -// case IResource.PROJECT: -// IProject project := resource -// -// if(!project.exists || projectClosed(delta, project)) { -// change.closedProjects.add(project) -// return false -// } -// -// if(projectOpened(delta, project)) { -// change.openedProjects.add(project) -// return false -// } -// -// return true -// case IResource.FOLDER: -// return false -// case IResource.FILE: -// if((resource.getName == Manifest.filename || resource.getName == IJavaProject.CLASSPATH_FILE_NAME) -// && contentChanged(delta)) -// change.updatedProjects.add(resource.getProject) -// return false -// default: return true -// } -// } -// -// private static Bool contentChanged(IResourceDelta delta) { -// switch(delta.getKind) { -// case IResourceDelta.CHANGED: -// return delta.getFlags.and(IResourceDelta.CONTENT) != 0 -// default: return true -// } -// } -// -// private static Bool projectClosed(IResourceDelta delta, IProject project) { openChange(delta) && !project.isOpen } -// private static Bool projectOpened(IResourceDelta delta, IProject project) { openChange(delta) && project.isOpen } -// private static Bool openChange(IResourceDelta delta) { delta.getFlags.and(IResourceDelta.OPEN) != 0 } -//} -// -//class WorkspaceChange { -// IProject[] closedProjects := IProject[,] -// IProject[] openedProjects := IProject[,] -// IProject[] updatedProjects := IProject[,] -// -// Bool isEmpty() { [closedProjects, openedProjects, updatedProjects].all { it.isEmpty } } -//} -// -//************************************************************************** -//** BuildfanChangeListener -//************************************************************************** -// -//** Base mixin for all listeners of buildfan changes -//const mixin BuildfanChangeListener { -// abstract Void notify(WorkspaceChange change) -//} -// diff --git a/com.xored.f4.core/fan/manifest/FantomProject.fan b/com.xored.f4.core/fan/manifest/FantomProject.fan index caee4a78..0827dd1e 100644 --- a/com.xored.f4.core/fan/manifest/FantomProject.fan +++ b/com.xored.f4.core/fan/manifest/FantomProject.fan @@ -223,7 +223,7 @@ const class FantomProject { ** The workspace has changed somehow (projects updates) and we're involved somehow internal Void update() { - podFiles := doResolvePods.rw + podFiles := doResolvePods.rw // overwrite entries with workspace pods dependentProjects.each { @@ -233,86 +233,41 @@ const class FantomProject { // prevent errs such as "Project cannot reference itself: poo" podFiles.remove(podName) - this.resolvedPodsRef.val = podFiles.toImmutable - + this.resolvedPodsRef.val = podFiles.toImmutable this.classpathDependsRef.val = doClasspathDepends.toImmutable } -// ** Returns a map of pod names to pod files. -// ** Only called by InterpreterContainer.processEntres() and InternalBuilder.buildPod() -// Str:File resolvePods() { -// podFiles := doResolvePods.rw -// resolveErrs = compileEnv.resolveErrs.toImmutable -// -//// // overwrite entries with workspace pods -//// if (prefs.referencedPodsOnly) { -//// // new beta behaviour -//// podFiles.setAll(classpathDepends) -//// } -// -// // overwrite entries with workspace pods -// // new behaviour - kinda the same as the old -//// if (prefs.referencedPodsOnly) { -// dependentProjects.each { -// podFiles[it.podName] = it.podOutFile -// } -//// } -// -//// // old behaviour -//// if (prefs.referencedPodsOnly == false) { -//// FantomProjectManager2.instance.allProjects.each |fp| { -//// if (podFiles.containsKey(fp.podName) || rawDepends.any { it.name == fp.podName }) -//// podFiles[fp.podName] = fp.podOutFile -//// } -//// } -// -// // prevent errs such as "Project cannot reference itself: poo" -// podFiles.remove(podName) -// -// return podFiles -// } - - - FantomProject[] dependentProjects() { projectManager := FantomProjectManager2.instance return projectManager.dependentProjects(this) - -// fantomProjects := FantomProject[,] -// for (i := 0; i < rawDepends.size; ++i) { -// project := projectManager.getByPodName(rawDepends[i].name) -// if (project != null) -// fantomProjects.add(project) -// } -// return fantomProjects + } + + IScriptProject scriptProject() { DLTKCore.create(project) } + + IFanNamespace ns() { + DltkNamespace(this, podName) + } + + ProjectPrefs prefs() { + ProjectPrefs(this) } + private const QuickCash buildFanStrRef := QuickCash(1sec) private const Str buildFanStr Bool buildFanHasChanged() { - // TODO quick cache this? - try return buildFile.readAllStr != buildFanStr + try return buildFanStrRef.get |->Bool| { buildFile.readAllStr != buildFanStr } catch return true } -// private const AtomicRef dependsStrRef := AtomicRef() private const AtomicRef resolvePodsRef := AtomicRef() private const AtomicRef resolveFutureRef := AtomicRef() private Str:File doResolvePods() { - // cache the resolved pods until the dependencies change - // this MASSIVELY reduces the F4 build churn -// dependsStr := rawDepends.rw.sort |p1, p2| { p1.name <=> p2.name }.join("; ") -// if (dependsStr == dependsStrRef.val) -// return resolvePodsRef.val - -// CompileEnv caches pods for 3 secs, so don't worry about that! - // coalesce multiple calls into one future := resolveFutureRef.val as Future if (future == null) { future = Synchronized(ActorPool()).async |->Obj?| { resolvedPods := compileEnv.resolvePods resolvePodsRef.val = resolvedPods.toImmutable -// dependsStrRef.val = dependsStr resolveErrsRef.val = compileEnv.resolveErrs.toImmutable return resolvedPods } @@ -324,16 +279,6 @@ const class FantomProject { finally resolveFutureRef.val = null } - IScriptProject scriptProject() { DLTKCore.create(project) } - - IFanNamespace ns() { - DltkNamespace(this, podName) - } - - ProjectPrefs prefs() { - ProjectPrefs(this) - } - private const AtomicRef compileEnvRef := AtomicRef() CompileEnv compileEnv() { // only bother making a new one if the type / preferences change diff --git a/com.xored.f4.core/fan/manifest/FantomProjectListener.fan b/com.xored.f4.core/fan/manifest/FantomProjectListener.fan new file mode 100644 index 00000000..1e315753 --- /dev/null +++ b/com.xored.f4.core/fan/manifest/FantomProjectListener.fan @@ -0,0 +1,99 @@ +using [java] org.eclipse.jdt.core::IJavaProject +using [java] org.eclipse.dltk.core::IScriptProject +using [java] org.eclipse.dltk.core::DLTKCore +using [java] org.eclipse.core.resources::ResourcesPlugin +using [java] org.eclipse.core.resources::IResourceChangeEvent +using [java] org.eclipse.core.resources::IResource +using [java] org.eclipse.core.resources::IResourceDelta +using [java] org.eclipse.core.resources::IProject + +internal const class FantomProjectListener { + const FantomProjectManager2 fantomProjects + + new make(FantomProjectManager2 fantomProjects) { + this.fantomProjects = fantomProjects + + DLTKCore.create(ResourcesPlugin.getWorkspace.getRoot).getScriptProjects(F4Nature.id).each |IScriptProject sp| { + // this "adds" the project + fantomProjects.get(sp.getProject) + } + + // Java interfaces with ONE method may be represented as a closure - cool, huh!? + DLTKCore.addPreProcessingResourceChangedListener(|IResourceChangeEvent? event| { + if (event.getType == IResourceChangeEvent.POST_CHANGE) { + visitor := DeltaVisitor2() + // do that interface closure thing again! + event.getDelta.accept |IResourceDelta delta -> Bool| { + visitor.visit(delta) + } + fantomProjects.notify(visitor.workspaceChanges) + } + }, IResourceChangeEvent.POST_CHANGE) + } +} + +** Visits resource delta and collects changes +internal class DeltaVisitor2 { + IProject[] closedProjects := IProject[,] + IProject[] openedProjects := IProject[,] + IProject[] updatedProjects := IProject[,] + + Bool visit(IResourceDelta? delta) { + resource := delta.getResource + + switch (resource.getType) { + case IResource.PROJECT: + project := (IProject) resource + + if (!project.exists || projectClosed(delta, project)) { + closedProjects.add(project) + return false + } + + if (projectOpened(delta, project)) { + openedProjects.add(project) + return false + } + + return true + + case IResource.FOLDER: + return false + + case IResource.FILE: + if ((resource.getName == Manifest.filename || resource.getName == IJavaProject.CLASSPATH_FILE_NAME) && contentChanged(delta)) + updatedProjects.add(resource.getProject) + return false + + default: + return true + } + } + + WorkspaceChange2 workspaceChanges() { + WorkspaceChange2 { + it.closedProjects = this.closedProjects + it.openedProjects = this.openedProjects + it.updatedProjects = this.updatedProjects + } + } + + private static Bool contentChanged(IResourceDelta delta) { + switch (delta.getKind) { + case IResourceDelta.CHANGED: + return delta.getFlags.and(IResourceDelta.CONTENT) != 0 + default: + return true + } + } + + private static Bool projectClosed(IResourceDelta delta, IProject project) { openChange(delta) && !project.isOpen } + private static Bool projectOpened(IResourceDelta delta, IProject project) { openChange(delta) && project.isOpen } + private static Bool openChange (IResourceDelta delta) { delta.getFlags.and(IResourceDelta.OPEN) != 0 } +} + +internal class WorkspaceChange2 { + IProject[] closedProjects := IProject[,] + IProject[] openedProjects := IProject[,] + IProject[] updatedProjects := IProject[,] +} diff --git a/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan b/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan index 2d2e64f7..cf1478d2 100644 --- a/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan +++ b/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan @@ -1,25 +1,15 @@ using [java] org.eclipse.core.resources::IProject -using [java] org.eclipse.dltk.core::IScriptProject -using [java] org.eclipse.dltk.core::DLTKCore -using [java] org.eclipse.core.resources::ResourcesPlugin using concurrent::ActorPool -using [java] org.eclipse.core.resources::IResourceChangeListener -using [java] org.eclipse.core.resources::IResourceChangeEvent -using [java] org.eclipse.core.resources::IResource -using [java] org.eclipse.core.resources::IResourceDeltaVisitor -using [java] org.eclipse.core.resources::IResourceDelta -using [java]org.eclipse.jdt.core::IJavaProject - - -const class FantomProjectManager2 : IResourceChangeListener { - static const FantomProjectManager2? instance := FantomProjectManager2() +const class FantomProjectManager2 { + static const FantomProjectManager2 instance := FantomProjectManager2() private const SynchronizedState fantomProjects + private const FantomProjectListener projectListener private new make() { - this.fantomProjects = SynchronizedState.makeWithType(ActorPool(), FantomProjectManagerState#) - init + this.fantomProjects = SynchronizedState.makeWithType(ActorPool(), FantomProjectManagerState#) + this.projectListener = FantomProjectListener(this) } FantomProject? get(IProject project) { @@ -38,26 +28,12 @@ const class FantomProjectManager2 : IResourceChangeListener { FantomProject[] dependentProjects(FantomProject fp) { call { it.dependentProjects(fp) } } - - override Void resourceChanged(IResourceChangeEvent? event) { - // TODO move to deltaVis - use the Fantom closure interface thing - if (event.getType == IResourceChangeEvent.POST_CHANGE) { - visitor := DeltaVisitor2() - event.getDelta.accept(visitor) - notify(visitor.workspaceChanges) - } - } - private Void notify(WorkspaceChange2 change) { + internal Void notify(WorkspaceChange2 change) { changeRef := Unsafe(change) call { it.applyChanges(changeRef.val) } } - private Void init() { - call { it.init } - DLTKCore.addPreProcessingResourceChangedListener(this, IResourceChangeEvent.POST_CHANGE) - } - private Obj? call(|FantomProjectManagerState->Obj?| state) { fantomProjects.sync(state) } @@ -72,13 +48,6 @@ internal class FantomProjectManagerState { this.projects = Str:FantomProject[:] } - Obj? init() { - DLTKCore.create(ResourcesPlugin.getWorkspace.getRoot).getScriptProjects(F4Nature.id).each |IScriptProject sp| { - updateProject(sp.getProject) - } - return null - } - Obj? applyChanges(WorkspaceChange2 change) { // reset containers for all projects that depend on // closed or opened projects and for all projects with updated content @@ -197,69 +166,3 @@ internal class FantomProjectManagerState { : projects[ip.getName] != null } } - -** Visits resource delta and collects changes -class DeltaVisitor2 : IResourceDeltaVisitor { - IProject[] closedProjects := IProject[,] - IProject[] openedProjects := IProject[,] - IProject[] updatedProjects := IProject[,] - - override Bool visit(IResourceDelta? delta) { - resource := delta.getResource - - switch (resource.getType) { - case IResource.PROJECT: - project := (IProject) resource - - if (!project.exists || projectClosed(delta, project)) { - closedProjects.add(project) - return false - } - - if (projectOpened(delta, project)) { - openedProjects.add(project) - return false - } - - return true - - case IResource.FOLDER: - return false - - case IResource.FILE: - if ((resource.getName == Manifest.filename || resource.getName == IJavaProject.CLASSPATH_FILE_NAME) && contentChanged(delta)) - updatedProjects.add(resource.getProject) - return false - - default: - return true - } - } - - WorkspaceChange2 workspaceChanges() { - WorkspaceChange2 { - it.closedProjects = this.closedProjects - it.openedProjects = this.openedProjects - it.updatedProjects = this.updatedProjects - } - } - - private static Bool contentChanged(IResourceDelta delta) { - switch (delta.getKind) { - case IResourceDelta.CHANGED: - return delta.getFlags.and(IResourceDelta.CONTENT) != 0 - default: - return true - } - } - - private static Bool projectClosed(IResourceDelta delta, IProject project) { openChange(delta) && !project.isOpen } - private static Bool projectOpened(IResourceDelta delta, IProject project) { openChange(delta) && project.isOpen } - private static Bool openChange (IResourceDelta delta) { delta.getFlags.and(IResourceDelta.OPEN) != 0 } -} - -class WorkspaceChange2 { - IProject[] closedProjects := IProject[,] - IProject[] openedProjects := IProject[,] - IProject[] updatedProjects := IProject[,] -} diff --git a/com.xored.f4.core/fan/util/QuickCash.fan b/com.xored.f4.core/fan/util/QuickCash.fan index 125ef907..a9e417e3 100644 --- a/com.xored.f4.core/fan/util/QuickCash.fan +++ b/com.xored.f4.core/fan/util/QuickCash.fan @@ -1,6 +1,6 @@ using concurrent -** Caches a value for X amount of time. +** Caches a value for X amount of time - used to alleviate build thrashing. ** Factory funcs may be specified in ctor or overridden during get(). const class QuickCash { From dd8fb0f6139cf787c5380ef1bc688896f0b6770d Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Wed, 29 Apr 2020 16:16:18 +0100 Subject: [PATCH 09/17] Remove beta tickbox in compiler options The new code has proven itself! --- .../fan/CompilerPreferencePage.fan | 9 +-------- com.xored.f4.builder/fan/InternalBuilder.fan | 12 ------------ .../fan/manifest/FantomProjectManager2.fan | 3 +-- com.xored.f4.core/fan/manifest/ProjectPrefs.fan | 6 ------ .../fan/internal/FanJavaLaunchUtil.fan | 15 ++++++--------- 5 files changed, 8 insertions(+), 37 deletions(-) diff --git a/com.xored.f4.builder.ui/fan/CompilerPreferencePage.fan b/com.xored.f4.builder.ui/fan/CompilerPreferencePage.fan index 92108cee..b249aa7d 100644 --- a/com.xored.f4.builder.ui/fan/CompilerPreferencePage.fan +++ b/com.xored.f4.builder.ui/fan/CompilerPreferencePage.fan @@ -48,9 +48,6 @@ class CompilerOptionsBlock : AbstractOptionsBlock { SWTFactory.createLabel(composite, "", 2) bindControl(SWTFactory.createCheckButton(composite, "Use external compiler", null, false, 2), useExternalBuilderKey, null) - SWTFactory.createLabel(composite, "", 2) - bindControl(SWTFactory.createCheckButton(composite, "Use referenced projects only (beta)", null, false, 2), referencedPodsOnlyKey, null) - SWTFactory.createLabel(composite, "", 2) SWTFactory.createLabel(composite, "Note: Projects need to be re-built to make use of changes made above", 2) @@ -65,11 +62,7 @@ class CompilerOptionsBlock : AbstractOptionsBlock { PreferenceKey(ProjectPrefs.qualifier, ProjectPrefs.useExternalBuilderName) } - private static PreferenceKey referencedPodsOnlyKey() { - PreferenceKey(ProjectPrefs.qualifier, ProjectPrefs.referencedPodsOnlyName) - } - private static PreferenceKey[] allKeys() { - [podOutputDir, useExternalBuilderKey, referencedPodsOnlyKey] + [podOutputDir, useExternalBuilderKey] } } diff --git a/com.xored.f4.builder/fan/InternalBuilder.fan b/com.xored.f4.builder/fan/InternalBuilder.fan index e2d78cc1..69d84795 100644 --- a/com.xored.f4.builder/fan/InternalBuilder.fan +++ b/com.xored.f4.builder/fan/InternalBuilder.fan @@ -44,18 +44,6 @@ class InternalBuilder : Builder { if (fp.resolveErrs.size > 0) { return fp.resolveErrs.map { CompilerErr(it.toStr, bldLoc) } } - - // Blindly add all workspace pods - seems to be the only way to get F4 to compile itself (...!?) - // Without this, we get compilation errors similar to "pod not found: f4parser". - // These aren't actual dependencies and don't seem to be transitive dependencies. - // Note that adding them as actual project dependencies also solves the issue, - // But because I don't know why, I'm loath to do so - hence these 3 little lines. - - // SlimerDude - Apr 2020 - Beta feature to turn this off - if (fp.prefs.referencedPodsOnly == false) - FantomProjectManager2.instance.allProjects.each |p| { - resolvedPods = resolvedPods.rw[p.podName] = p.podOutFile - } logger := ConsoleLogger(consumer) input := CompilerInput.make diff --git a/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan b/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan index cf1478d2..6c5714fb 100644 --- a/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan +++ b/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan @@ -124,8 +124,7 @@ internal class FantomProjectManagerState { } FantomProject[] dependentProjects(FantomProject fp) { - des:=doDependentProjects(fp, Str:FantomProject[:]).vals.toImmutable - return des + doDependentProjects(fp, Str:FantomProject[:]).vals.toImmutable } private Str:FantomProject doDependentProjects(FantomProject fp, Str:FantomProject fps) { diff --git a/com.xored.f4.core/fan/manifest/ProjectPrefs.fan b/com.xored.f4.core/fan/manifest/ProjectPrefs.fan index 64d6f646..46476862 100644 --- a/com.xored.f4.core/fan/manifest/ProjectPrefs.fan +++ b/com.xored.f4.core/fan/manifest/ProjectPrefs.fan @@ -8,7 +8,6 @@ class ProjectPrefs { static const Str qualifier := "com.xored.f4.core" // from com.xored.f4.builder::CompileFan.pluginId static const Str podOutputDirName := "podOutputDir" static const Str useExternalBuilderName := "useExternalBuilder" - static const Str referencedPodsOnlyName := "referencedPodsOnly" static const Str buildDependantsName := "buildDependants" static const Str publishPodName := "publishPod" static const Str compileEnvName := "compileEnv" @@ -33,10 +32,6 @@ class ProjectPrefs { delegate.getBoolean(qualifier, useExternalBuilderName) } - Bool referencedPodsOnly() { - delegate.getBoolean(qualifier, referencedPodsOnlyName) - } - Bool publishPod() { delegate.getBoolean(qualifier, publishPodName) } @@ -63,6 +58,5 @@ class ProjectPrefsInitializer : AbstractPreferenceInitializer { store.putBoolean(ProjectPrefs.buildDependantsName, true) store.putBoolean(ProjectPrefs.publishPodName, false) store.put (ProjectPrefs.compileEnvName, DefaultCompileEnv#.qname) - store.putBoolean(ProjectPrefs.referencedPodsOnlyName, false) } } diff --git a/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan b/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan index d3db3b84..e211f539 100644 --- a/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan +++ b/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan @@ -118,20 +118,17 @@ class FanJavaLaunchUtil { gogogo := true PlatformUI.getWorkbench.getDisplay.syncExec |->| { - shell := PlatformUI.getWorkbench.getActiveWorkbenchWindow.getShell - msg := "The following projects contain errors and have not been built:\n\n" + projsInErr.map { " ${it.project.getName}" }.join("\n") + "\n\nOld pod versions will be used until errors are resolved.\n\nDo you wish to continue?" - dialog := MessageDialog(shell, "Project Errors", null, msg, MessageDialog.WARNING, Str[,].add("Yes").add("No"), 0) - gogogo = dialog.open == 0 + shell := PlatformUI.getWorkbench.getActiveWorkbenchWindow.getShell + msg := "The following projects contain errors and have not been built:\n\n" + projsInErr.map { " ${it.project.getName}" }.join("\n") + "\n\nOld pod versions will be used until errors are resolved.\n\nDo you wish to continue?" + dialog := MessageDialog(shell, "Project Errors", null, msg, MessageDialog.WARNING, Str[,].add("Yes").add("No"), 0) + gogogo = dialog.open == 0 } return gogogo } private static FantomProject[] findOpenProjects(FantomProject fp, ILaunchConfiguration config) { - projectList := (Str[]) config.getAttribute(LaunchConsts.projectList, ArrayList()).toArray - - otherProjects := fp.prefs.referencedPodsOnly - ? FantomProjectManager2.instance.getByPodName(fp.podName).dependentProjects.add(fp) - : FantomProjectManager2.instance.allProjects + projectList := (Str[]) config.getAttribute(LaunchConsts.projectList, ArrayList()).toArray + otherProjects := FantomProjectManager2.instance.getByPodName(fp.podName).dependentProjects.rw.add(fp) return otherProjects .exclude { it.isPlugin } From da30449491597831e5978e27e35ae3231f050810 Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Wed, 29 Apr 2020 16:30:34 +0100 Subject: [PATCH 10/17] FantomProjectManager2 -> FantomProjectManager --- com.xored.f4.astView/fan/AstView.fan | 16 +- com.xored.f4.builder/fan/CompileFan.fan | 4 +- com.xored.f4.builder/fan/InternalBuilder.fan | 1 - .../fan/manifest/FantomProject.fan | 4 +- .../fan/manifest/FantomProjectListener.fan | 10 +- .../fan/manifest/FantomProjectManager.fan | 373 ++++++++---------- .../fan/manifest/FantomProjectManager2.fan | 167 -------- .../fan/manifest/InterpreterContainer.fan | 2 +- com.xored.f4.core/fan/model/DltkNamespace.fan | 2 +- .../fan/parser/SourceIndexer.fan | 2 +- .../fan/selection/SelectionEngine.fan | 2 +- com.xored.f4.core/fan/util/ParseUtil.fan | 2 +- .../fan/FanConsoleTracker.fan | 6 +- .../fan/FanEditorDebugAdapterFactory.fan | 4 +- com.xored.f4.debug.ui/fan/FanTabGroup.fan | 4 +- com.xored.f4.debug.ui/fan/ProjectTab.fan | 4 +- com.xored.f4.debug/fan/FanSourceContainer.fan | 15 +- .../fan/SourceLookupParticipant.fan | 8 +- .../fan/internal/FanJavaContainer.fan | 6 +- .../fan/internal/FanJavaLaunchUtil.fan | 8 +- .../fan/LaunchConfigDelegate.fan | 4 +- 21 files changed, 219 insertions(+), 425 deletions(-) delete mode 100644 com.xored.f4.core/fan/manifest/FantomProjectManager2.fan diff --git a/com.xored.f4.astView/fan/AstView.fan b/com.xored.f4.astView/fan/AstView.fan index 0ae16164..a267831b 100644 --- a/com.xored.f4.astView/fan/AstView.fan +++ b/com.xored.f4.astView/fan/AstView.fan @@ -1,14 +1,14 @@ using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager using f4model using f4parser using f4uiText -using [java]org.eclipse.ui -using [java]org.eclipse.ui.part -using [java]org.eclipse.swt.widgets -using [java]org.eclipse.jface.viewers -using [java]org.eclipse.dltk.ui -using [java]org.eclipse.dltk.core +using [java] org.eclipse.ui +using [java] org.eclipse.ui.part +using [java] org.eclipse.swt.widgets +using [java] org.eclipse.jface.viewers +using [java] org.eclipse.dltk.ui +using [java] org.eclipse.dltk.core class AstView : ViewPart, IPartListener { @@ -62,7 +62,7 @@ class AstView : ViewPart, IPartListener private static IFanNamespace getNamespace(ISourceModule? content) { project := content?.getScriptProject?.getProject if (project == null || !project.isOpen) return EmptyNamespace() - return FantomProjectManager2.instance.get(project).ns + return FantomProjectManager.instance.get(project).ns } override Void partActivated(IWorkbenchPart? part) { diff --git a/com.xored.f4.builder/fan/CompileFan.fan b/com.xored.f4.builder/fan/CompileFan.fan index 37deee63..3c3f6059 100644 --- a/com.xored.f4.builder/fan/CompileFan.fan +++ b/com.xored.f4.builder/fan/CompileFan.fan @@ -1,6 +1,6 @@ using compiler using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager using [java]java.util::List as JList using [java]java.util::Set as JSet @@ -130,7 +130,7 @@ class CompileFan : IScriptBuilder { ////////////////////////////////////////////////////////////////////////// private FantomProject fantomProject(IScriptProject project) { - FantomProjectManager2.instance.get(project.getProject) + FantomProjectManager.instance.get(project.getProject) } ////////////////////////////////////////////////////////////////////////// diff --git a/com.xored.f4.builder/fan/InternalBuilder.fan b/com.xored.f4.builder/fan/InternalBuilder.fan index 69d84795..ae583d76 100644 --- a/com.xored.f4.builder/fan/InternalBuilder.fan +++ b/com.xored.f4.builder/fan/InternalBuilder.fan @@ -1,5 +1,4 @@ using f4core::FantomProject -using f4core::FantomProjectManager2 using f4core::LogUtil using compiler diff --git a/com.xored.f4.core/fan/manifest/FantomProject.fan b/com.xored.f4.core/fan/manifest/FantomProject.fan index 0827dd1e..da8c4dd3 100644 --- a/com.xored.f4.core/fan/manifest/FantomProject.fan +++ b/com.xored.f4.core/fan/manifest/FantomProject.fan @@ -182,7 +182,7 @@ const class FantomProject { projectName := bp.getPath.segments.first project := ResourcesPlugin.getWorkspace.getRoot.getProject(projectName) return project.isOpen && project.isAccessible - ? FantomProjectManager2.instance.get(project).podOutFile + ? FantomProjectManager.instance.get(project).podOutFile : null case IBuildpathEntry.BPE_LIBRARY: @@ -238,7 +238,7 @@ const class FantomProject { } FantomProject[] dependentProjects() { - projectManager := FantomProjectManager2.instance + projectManager := FantomProjectManager.instance return projectManager.dependentProjects(this) } diff --git a/com.xored.f4.core/fan/manifest/FantomProjectListener.fan b/com.xored.f4.core/fan/manifest/FantomProjectListener.fan index 1e315753..580939c7 100644 --- a/com.xored.f4.core/fan/manifest/FantomProjectListener.fan +++ b/com.xored.f4.core/fan/manifest/FantomProjectListener.fan @@ -8,9 +8,9 @@ using [java] org.eclipse.core.resources::IResourceDelta using [java] org.eclipse.core.resources::IProject internal const class FantomProjectListener { - const FantomProjectManager2 fantomProjects + const FantomProjectManager fantomProjects - new make(FantomProjectManager2 fantomProjects) { + new make(FantomProjectManager fantomProjects) { this.fantomProjects = fantomProjects DLTKCore.create(ResourcesPlugin.getWorkspace.getRoot).getScriptProjects(F4Nature.id).each |IScriptProject sp| { @@ -70,8 +70,8 @@ internal class DeltaVisitor2 { } } - WorkspaceChange2 workspaceChanges() { - WorkspaceChange2 { + WorkspaceChange workspaceChanges() { + WorkspaceChange { it.closedProjects = this.closedProjects it.openedProjects = this.openedProjects it.updatedProjects = this.updatedProjects @@ -92,7 +92,7 @@ internal class DeltaVisitor2 { private static Bool openChange (IResourceDelta delta) { delta.getFlags.and(IResourceDelta.OPEN) != 0 } } -internal class WorkspaceChange2 { +internal class WorkspaceChange { IProject[] closedProjects := IProject[,] IProject[] openedProjects := IProject[,] IProject[] updatedProjects := IProject[,] diff --git a/com.xored.f4.core/fan/manifest/FantomProjectManager.fan b/com.xored.f4.core/fan/manifest/FantomProjectManager.fan index e63a153c..efb5db5b 100644 --- a/com.xored.f4.core/fan/manifest/FantomProjectManager.fan +++ b/com.xored.f4.core/fan/manifest/FantomProjectManager.fan @@ -1,206 +1,167 @@ -//using [java]org.eclipse.core.resources::IProject -//using [java]org.eclipse.dltk.core::IScriptModel -//using [java]org.eclipse.dltk.core::IScriptProject -//using [java]org.eclipse.dltk.core::DLTKCore -//using [java]org.eclipse.core.resources::ResourcesPlugin -//using concurrent -// -//const class FantomProjectManager : Actor, BuildfanChangeListener { -// static const FantomProjectManager? instance := FantomProjectManager() -// private const ContainerResetter resetter -// const BuildfanListener buildFanChange -// -// -// ////////////////////////////////////////////////////////////////////////// -// // Constructor and fields -// ////////////////////////////////////////////////////////////////////////// -// -// private new make() : super(ActorPool()) { -// try { -// resetter = ContainerResetter(pool) -// buildFanChange = BuildfanListener(pool) -// buildFanChange.subscribe -// buildFanChange.addListener(this) -// init -// } catch(Err e) { -// e.trace -// throw e -// } -// } -// -// ////////////////////////////////////////////////////////////////////////// -// // Overriden methods -// ////////////////////////////////////////////////////////////////////////// -// -// override Obj? receive(Obj? msg) { -// // rewritten to fail fast -// try { -// list := (Obj[])msg -// return ((Method)list[0]).callOn(this, ((Unsafe)list[1]).val) -// } catch(Err e) { -// e.trace //TODO: Add normal error reporting -// throw e -// } -// } -// -// override Void notify(WorkspaceChange change) { -// runInManager(#doNotify, [change]) -// } -// -// ////////////////////////////////////////////////////////////////////////// -// // Public API -// ////////////////////////////////////////////////////////////////////////// -// -// internal Void init() { send([#doInit,Unsafe([,])].toImmutable) } -// -// @Operator FantomProject? get(IProject project) { runInManager(#doGet, [project]) } -// -// FantomProject? getByPod(Str podName) { runInManager(#doGetByPod, [podName]) } -// -// FantomProject[] listProjects() { runInManager(#doListProjects, [,]) } -// -// ** SlimerDude - Apr 2020 - A beta feature to cut down on build thrashing -// ** 'cos I dunno why we need ALL projects as dependencies!? -// FantomProject[] listReferencedProjects(Str podName) { runInManager(#doListReferencedProjects, [podName]) } -// -// ////////////////////////////////////////////////////////////////////////// -// // Method handlers -// ////////////////////////////////////////////////////////////////////////// -// -// private Obj? runInManager(Method method, Obj?[] args) { marker ? method.callOn(this,args) : send([method,Unsafe(args)].toImmutable).get } -// -// private Void doNotify(WorkspaceChange change) { -// // we need to reset containers for all projects that depend on -// // closed or opened projects and for all projects with updated content -// projectsToUpdate := [Str:IProject][:] -// -// //add opened projects -// change.openedProjects.each |IProject p| { -// if (isFantomProject(p)) addProject(p) -// } -// -// [change.closedProjects, change.openedProjects].flatten.each |IProject p| { -// dependentProjects(p).each |IProject d| { -// projectsToUpdate[d.getName] = d -// } -// } -// -// // add updated and opened projects to projectsToUpdate -// [change.updatedProjects, change.openedProjects].flatten.each |IProject p| { -// if(isFantomProject(p)) projectsToUpdate[p.getName] = p -// } -// -// // now we need to exclude all removed projects for this list -// projectsToUpdate = projectsToUpdate.exclude |v, k| { -// change.closedProjects.any { getName == k } -// } -// -// // remove all closed projects -// change.closedProjects.each |p| { removeProject(p) } -// -// // buildfanChanged(change.updatedProjects.first) -// projectsToUpdate.vals.each |p| { -// addProject(p) -// } -// -// projectsToUpdate.vals.each |p| { -// resetter.reset(p) -// } -// } -// -// private Bool isFantomProject(IProject p) { -// if (!p.exists) return projects[getKey(p)] != null -// return p.getNature(F4Nature.id) != null -// } -// -// private Void removeProject(IProject p) { -// key := getKey(p) -// fp := projects[key] -// if (fp == null) return -// projects.remove(key) -// pods.remove(fp.podName) -// } -// -// private static Str getKey(IProject p) { p.getName } -// -// private FantomProject addProject(IProject p) { -// fp := FantomProject.makeFromProject(p) -// removeProject(p) -// projects[getKey(p)] = fp -// pods[fp.podName] = fp -// return fp -// } -// -// private IProject[] dependentProjects(IProject project) { -// fp := projects[getKey(project)] -// if(fp == null) return IProject[,] -// podName := fp.podName -// ps := projects -// return ps.vals.findAll |p| { -// p.rawDepends.any |d| { -// d.name == podName -// } -// }.map { it.project } -// } -// -// private FantomProject? doGet(IProject project) { -// if (!project.isOpen || !project.isAccessible) -// return null -// -// // SlimerDude - Apr 2020 - not sure why a null location prevents us from returning the project? -// if (project.getLocation == null) { -// echo("location is null for project $project.getName") -// return null -// } -// key := getKey(project) -// if (projects.containsKey(key)) return projects[key] -// return addProject(project) -// } -// -// private Void doInit() { -// setMarker -// DLTKCore.create(ResourcesPlugin.getWorkspace.getRoot).getScriptProjects(F4Nature.id).each |IScriptProject sp| { -// addProject(sp.getProject) -// } -// } -// -// private FantomProject[] doListProjects() { projects.vals.toImmutable } -// -// private FantomProject? doGetByPod(Str podName) { pods[podName] } -// -// private FantomProject[] doListReferencedProjects(Str podName) { -// fp := pods[podName] -// projs := fp.project.getReferencedProjects.map { doGet(it) }.exclude { it == null } -// return projs.toImmutable -// } -// -// ////////////////////////////////////////////////////////////////////////// -// // Private hepler methods -// ////////////////////////////////////////////////////////////////////////// -// -// private FantomProject? createProject(IProject project) { -// try { -// return FantomProject.makeFromProject(project) -// } catch (Err e) { -// e.trace -// return null -// } -// } -// -// ////////////////////////////////////////////////////////////////////////// -// // Locals-backed properties -// ////////////////////////////////////////////////////////////////////////// -// -// ** Fantom projects by location -// private Str:FantomProject projects() { -// locals.getOrAdd("projects") |->Obj| { [Str:FantomProject][:] } -// } -// -// ** Fantom projects by pod name -// private Str:FantomProject pods() { -// locals.getOrAdd("pods") |->Obj| { [Str:FantomProject][:] } -// } -// -// private Bool marker() { locals[toStr] as Bool ?: false } -// private Void setMarker() { locals[toStr] = true } -//} +using [java] org.eclipse.core.resources::IProject +using concurrent::ActorPool + +const class FantomProjectManager { + static const FantomProjectManager instance := FantomProjectManager() + + private const SynchronizedState fantomProjects + private const FantomProjectListener projectListener + + private new make() { + this.fantomProjects = SynchronizedState.makeWithType(ActorPool(), FantomProjectManagerState#) + this.projectListener = FantomProjectListener(this) + } + + FantomProject? get(IProject project) { + projectRef := Unsafe(project) + return call { it.getOrAdd(projectRef) } + } + + FantomProject? getByPodName(Str podName) { + call { it.getByPodName(podName) } + } + + FantomProject[] allProjects() { + call { it.allProjects } + } + + FantomProject[] dependentProjects(FantomProject fp) { + call { it.dependentProjects(fp) } + } + + internal Void notify(WorkspaceChange change) { + changeRef := Unsafe(change) + call { it.applyChanges(changeRef.val) } + } + + private Obj? call(|FantomProjectManagerState->Obj?| state) { + fantomProjects.sync(state) + } +} + +internal class FantomProjectManagerState { + private ContainerResetter resetter + private Str:FantomProject projects + + new make() { + this.resetter = ContainerResetter(ActorPool()) + this.projects = Str:FantomProject[:] + } + + Obj? applyChanges(WorkspaceChange change) { + // reset containers for all projects that depend on + // closed or opened projects and for all projects with updated content + + projectsToUpdate := [Str:IProject][:] { it.ordered = true } + + // add updated and opened Fantom projects + change.openedProjects.each { + if (isFantomProject(it)) projectsToUpdate[it.getName] = it + } + change.updatedProjects.each { + if (isFantomProject(it)) projectsToUpdate[it.getName] = it + } + + + // add parent projects + change.openedProjects.each { + parentProjects(it).each { projectsToUpdate[it.getName] = it } + } + change.closedProjects.each { + parentProjects(it).each { projectsToUpdate[it.getName] = it } + } + + + // remove all closed projects + change.closedProjects.each { + projectsToUpdate.remove(it.getName) + projects .remove(it.getName) + } + + + // do the update + projectsToUpdate.vals.each { + updated := updateProject(it) + if (updated) + resetter.reset(it) + else + projects[it.getName].update + } + + return null + } + + private IProject[] parentProjects(IProject ip) { + fp := projects[ip.getName] + if (fp == null) + return IProject#.emptyList + + parents := IProject[,] + projs := projects.vals + for (i := 0; i < projs.size; ++i) { + proj := projs[i] + deps := proj.rawDepends + for (j := 0; j < deps.size; ++j) { + if (deps[j].name == fp.podName) + parents.add(proj.project) + } + } + return parents + } + + FantomProject? getOrAdd(Unsafe projectRef) { + ip := (IProject) projectRef.val + updateProject(ip) + return projects[ip.getName] + } + + FantomProject? getByPodName(Str podName) { + projects.find { it.podName == podName } + } + + FantomProject[] allProjects() { + projects.vals.toImmutable + } + + FantomProject[] dependentProjects(FantomProject fp) { + doDependentProjects(fp, Str:FantomProject[:]).vals.toImmutable + } + + private Str:FantomProject doDependentProjects(FantomProject fp, Str:FantomProject fps) { + for (i := 0; i < fp.rawDepends.size; ++i) { + podName := fp.rawDepends[i].name + + // prevent infinite cyclic dependencies + if (!fps.containsKey(podName)) { + project := getByPodName(podName) + + if (project != null) { + fps[podName] = project + doDependentProjects(project, fps) + } + } + } + return fps + } + + ** Returns 'true' if the project was updated + private Bool updateProject(IProject ip) { + // if the existing project looks okay, let's keep it! + fp := projects[ip.getName] + if (fp != null) { + if (fp.resolveErrs.isEmpty && fp.buildFanHasChanged == false) { + return false + } + } + + fp = FantomProject.makeFromProject(ip) + projects[ip.getName] = fp + return true + } + + private Bool isFantomProject(IProject ip) { + ip.exists + ? ip.getNature(F4Nature.id) != null + : projects[ip.getName] != null + } +} diff --git a/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan b/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan deleted file mode 100644 index 6c5714fb..00000000 --- a/com.xored.f4.core/fan/manifest/FantomProjectManager2.fan +++ /dev/null @@ -1,167 +0,0 @@ -using [java] org.eclipse.core.resources::IProject -using concurrent::ActorPool - -const class FantomProjectManager2 { - static const FantomProjectManager2 instance := FantomProjectManager2() - - private const SynchronizedState fantomProjects - private const FantomProjectListener projectListener - - private new make() { - this.fantomProjects = SynchronizedState.makeWithType(ActorPool(), FantomProjectManagerState#) - this.projectListener = FantomProjectListener(this) - } - - FantomProject? get(IProject project) { - projectRef := Unsafe(project) - return call { it.getOrAdd(projectRef) } - } - - FantomProject? getByPodName(Str podName) { - call { it.getByPodName(podName) } - } - - FantomProject[] allProjects() { - call { it.allProjects } - } - - FantomProject[] dependentProjects(FantomProject fp) { - call { it.dependentProjects(fp) } - } - - internal Void notify(WorkspaceChange2 change) { - changeRef := Unsafe(change) - call { it.applyChanges(changeRef.val) } - } - - private Obj? call(|FantomProjectManagerState->Obj?| state) { - fantomProjects.sync(state) - } -} - -internal class FantomProjectManagerState { - private ContainerResetter resetter - private Str:FantomProject projects - - new make() { - this.resetter = ContainerResetter(ActorPool()) - this.projects = Str:FantomProject[:] - } - - Obj? applyChanges(WorkspaceChange2 change) { - // reset containers for all projects that depend on - // closed or opened projects and for all projects with updated content - - projectsToUpdate := [Str:IProject][:] { it.ordered = true } - - // add updated and opened Fantom projects - change.openedProjects.each { - if (isFantomProject(it)) projectsToUpdate[it.getName] = it - } - change.updatedProjects.each { - if (isFantomProject(it)) projectsToUpdate[it.getName] = it - } - - - // add parent projects - change.openedProjects.each { - parentProjects(it).each { projectsToUpdate[it.getName] = it } - } - change.closedProjects.each { - parentProjects(it).each { projectsToUpdate[it.getName] = it } - } - - - // remove all closed projects - change.closedProjects.each { - projectsToUpdate.remove(it.getName) - projects .remove(it.getName) - } - - - // do the update - projectsToUpdate.vals.each { - updated := updateProject(it) - if (updated) - resetter.reset(it) - else - projects[it.getName].update - } - - return null - } - - private IProject[] parentProjects(IProject ip) { - fp := projects[ip.getName] - if (fp == null) - return IProject#.emptyList - - parents := IProject[,] - projs := projects.vals - for (i := 0; i < projs.size; ++i) { - proj := projs[i] - deps := proj.rawDepends - for (j := 0; j < deps.size; ++j) { - if (deps[j].name == fp.podName) - parents.add(proj.project) - } - } - return parents - } - - FantomProject? getOrAdd(Unsafe projectRef) { - ip := (IProject) projectRef.val - updateProject(ip) - return projects[ip.getName] - } - - FantomProject? getByPodName(Str podName) { - projects.find { it.podName == podName } - } - - FantomProject[] allProjects() { - projects.vals.toImmutable - } - - FantomProject[] dependentProjects(FantomProject fp) { - doDependentProjects(fp, Str:FantomProject[:]).vals.toImmutable - } - - private Str:FantomProject doDependentProjects(FantomProject fp, Str:FantomProject fps) { - for (i := 0; i < fp.rawDepends.size; ++i) { - podName := fp.rawDepends[i].name - - // prevent infinite cyclic dependencies - if (!fps.containsKey(podName)) { - project := getByPodName(podName) - - if (project != null) { - fps[podName] = project - doDependentProjects(project, fps) - } - } - } - return fps - } - - ** Returns 'true' if the project was updated - private Bool updateProject(IProject ip) { - // if the existing project looks okay, let's keep it! - fp := projects[ip.getName] - if (fp != null) { - if (fp.resolveErrs.isEmpty && fp.buildFanHasChanged == false) { - return false - } - } - - fp = FantomProject.makeFromProject(ip) - projects[ip.getName] = fp - return true - } - - private Bool isFantomProject(IProject ip) { - ip.exists - ? ip.getNature(F4Nature.id) != null - : projects[ip.getName] != null - } -} diff --git a/com.xored.f4.core/fan/manifest/InterpreterContainer.fan b/com.xored.f4.core/fan/manifest/InterpreterContainer.fan index b1690c1a..9aecbf3b 100644 --- a/com.xored.f4.core/fan/manifest/InterpreterContainer.fan +++ b/com.xored.f4.core/fan/manifest/InterpreterContainer.fan @@ -33,7 +33,7 @@ class InterpreterContainer : IInterpreterContainerExtension, IInterpreterContain if (project == null || librariesList == null) return entries := IBuildpathEntry[,] - fpm := FantomProjectManager2.instance + fpm := FantomProjectManager.instance fantomProject := fpm.get(project.getProject) interpreter := fantomProject?.interpreterInstall?.getInstallLocation?.getPath diff --git a/com.xored.f4.core/fan/model/DltkNamespace.fan b/com.xored.f4.core/fan/model/DltkNamespace.fan index bb8a4197..255594f6 100644 --- a/com.xored.f4.core/fan/model/DltkNamespace.fan +++ b/com.xored.f4.core/fan/model/DltkNamespace.fan @@ -31,7 +31,7 @@ internal class DltkNamespace : IFanNamespace { case Fragment.K_SOURCE: // source fragment project := (fragment.getParent as IScriptProject).getProject - return FantomProjectManager2.instance.get(project).podName + return FantomProjectManager.instance.get(project).podName default: // pod fragment // TODO maybe podName resolution should be deferred to the CompileEnv as that provides the pod Files in the first place diff --git a/com.xored.f4.core/fan/parser/SourceIndexer.fan b/com.xored.f4.core/fan/parser/SourceIndexer.fan index c4dbb791..0f6957db 100644 --- a/com.xored.f4.core/fan/parser/SourceIndexer.fan +++ b/com.xored.f4.core/fan/parser/SourceIndexer.fan @@ -18,7 +18,7 @@ class SourceIndexer { if(module is ISourceModule) return SourceParserUtil.parse(module as ISourceModule, null)->unit proj := module.getModelElement.getScriptProject.getProject - ns := FantomProjectManager2.instance.get(proj).ns + ns := FantomProjectManager.instance.get(proj).ns return Parser(module.getSourceContents, ns).cunit } } diff --git a/com.xored.f4.core/fan/selection/SelectionEngine.fan b/com.xored.f4.core/fan/selection/SelectionEngine.fan index ee15478a..440112e3 100644 --- a/com.xored.f4.core/fan/selection/SelectionEngine.fan +++ b/com.xored.f4.core/fan/selection/SelectionEngine.fan @@ -27,7 +27,7 @@ class SelectionEngine : ISelectionEngine { DltkAst ast := SourceParserUtil.parse(module as ISourceModule, null) src = module.getSourceContents ip := module.getModelElement.getScriptProject.getProject - fp := FantomProjectManager2.instance.get(ip) + fp := FantomProjectManager.instance.get(ip) ns = ParseUtil.ns((ISourceModule)module.getModelElement) unit = ast.unit diff --git a/com.xored.f4.core/fan/util/ParseUtil.fan b/com.xored.f4.core/fan/util/ParseUtil.fan index 3e5cb86c..a9d26e12 100644 --- a/com.xored.f4.core/fan/util/ParseUtil.fan +++ b/com.xored.f4.core/fan/util/ParseUtil.fan @@ -41,7 +41,7 @@ class ParseUtil : TypeUtil static IFanNamespace ns(ISourceModule module) { sp := module.getScriptProject - fp := FantomProjectManager2.instance.get(sp.getProject) + fp := FantomProjectManager.instance.get(sp.getProject) if( module.isBinary) { fragment := module.getAncestor(IModelElement.PROJECT_FRAGMENT) if(fragment is PodFragment) diff --git a/com.xored.f4.debug.ui/fan/FanConsoleTracker.fan b/com.xored.f4.debug.ui/fan/FanConsoleTracker.fan index b6b9a118..06ec67c5 100644 --- a/com.xored.f4.debug.ui/fan/FanConsoleTracker.fan +++ b/com.xored.f4.debug.ui/fan/FanConsoleTracker.fan @@ -18,7 +18,7 @@ using [java] org.eclipse.dltk.core using [java] org.eclipse.dltk.debug.ui using "[java]org.eclipse.dltk.internal.ui.editor" using [java] com.xored.fanide.core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager using f4core class FanConsoleTracker : IPatternMatchListenerDelegate @@ -99,7 +99,7 @@ class SourceSearchJob : Job private Obj? tryFindInProjects() { found := IResource[,] - FantomProjectManager2.instance.allProjects.each |fp| + FantomProjectManager.instance.allProjects.each |fp| { if(link.projectName != null && link.projectName != fp.podName) return @@ -116,7 +116,7 @@ class SourceSearchJob : Job private Obj? tryFindSourceModule() { found := Obj[,] - FantomProjectManager2.instance.allProjects.each |fp| + FantomProjectManager.instance.allProjects.each |fp| { if(fp.podName == link.projectName || fp.project.getName == link.projectName) diff --git a/com.xored.f4.debug.ui/fan/FanEditorDebugAdapterFactory.fan b/com.xored.f4.debug.ui/fan/FanEditorDebugAdapterFactory.fan index 32abfa35..3b8beca6 100644 --- a/com.xored.f4.debug.ui/fan/FanEditorDebugAdapterFactory.fan +++ b/com.xored.f4.debug.ui/fan/FanEditorDebugAdapterFactory.fan @@ -26,7 +26,7 @@ using [java] org.eclipse.core.resources using [java] org.eclipse.osgi.util using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager using f4parser class FanEditorDebugAdapterFactory : ScriptEditorDebugAdapterFactory @@ -221,7 +221,7 @@ class FanToggleBreakpointAdapter : ScriptToggleBreakpointAdapter private Str? getResourcePodName(IResource? res) { - FantomProjectManager2.instance.get(res.getProject).podName + FantomProjectManager.instance.get(res.getProject).podName } } diff --git a/com.xored.f4.debug.ui/fan/FanTabGroup.fan b/com.xored.f4.debug.ui/fan/FanTabGroup.fan index f0254680..12af9d49 100644 --- a/com.xored.f4.debug.ui/fan/FanTabGroup.fan +++ b/com.xored.f4.debug.ui/fan/FanTabGroup.fan @@ -16,7 +16,7 @@ using [java] org.eclipse.swt.layout using [java] org.eclipse.swt.events using [java] org.eclipse.swt using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager using f4launching using f4model @@ -85,7 +85,7 @@ class FanMainConfigTab : MainLaunchConfigurationTab className := mainClassText if(className.isEmpty) className = "Main" - ns := FantomProjectManager2.instance.get(getProject.getProject).ns + ns := FantomProjectManager.instance.get(getProject.getProject).ns type := ns.currPod.findType(className,false) if(type == null) { diff --git a/com.xored.f4.debug.ui/fan/ProjectTab.fan b/com.xored.f4.debug.ui/fan/ProjectTab.fan index a6f3fca5..2cc8652b 100644 --- a/com.xored.f4.debug.ui/fan/ProjectTab.fan +++ b/com.xored.f4.debug.ui/fan/ProjectTab.fan @@ -20,7 +20,7 @@ using [java] org.eclipse.jface.viewers using [java] org.eclipse.ui.plugin::AbstractUIPlugin using [java] java.util::ArrayList using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager using f4launching class ProjectTab : AbstractLaunchConfigurationTab @@ -159,7 +159,7 @@ class ProjectTab : AbstractLaunchConfigurationTab private FantomProject[] getFantomProjects() { - return FantomProjectManager2.instance.allProjects.exclude { it.isPlugin } + return FantomProjectManager.instance.allProjects.exclude { it.isPlugin } } private Void toggleProjects(Bool select) diff --git a/com.xored.f4.debug/fan/FanSourceContainer.fan b/com.xored.f4.debug/fan/FanSourceContainer.fan index 65360866..9284e972 100644 --- a/com.xored.f4.debug/fan/FanSourceContainer.fan +++ b/com.xored.f4.debug/fan/FanSourceContainer.fan @@ -1,9 +1,10 @@ -using [java]java.lang -using [java]org.eclipse.core.resources -using [java]org.eclipse.core.runtime -using [java]org.eclipse.debug.core.sourcelookup -using [java]org.eclipse.debug.core.sourcelookup.containers -using [java]com.xored.fanide.core +using [java] java.lang +using [java] org.eclipse.core.resources +using [java] org.eclipse.core.runtime +using [java] org.eclipse.debug.core.sourcelookup +using [java] org.eclipse.debug.core.sourcelookup.containers +using [java] com.xored.fanide.core +using f4core::FantomProjectManager using f4core class FanSourceContainer : AbstractSourceContainer @@ -14,7 +15,7 @@ class FanSourceContainer : AbstractSourceContainer override Obj?[]? findSourceElements(Str? name) { Path path := Path(name) if (!path.segment(0).equals("fan")) return [,] - FantomProject? project := FantomProjectManager2.instance.getByPodName(path.segment(1)) + FantomProject? project := FantomProjectManager.instance.getByPodName(path.segment(1)) if (project == null) return [,] Obj[] sources := [,] project.srcDirs.each { diff --git a/com.xored.f4.jdt.launching.ui/fan/SourceLookupParticipant.fan b/com.xored.f4.jdt.launching.ui/fan/SourceLookupParticipant.fan index 7df99329..9b38a82e 100644 --- a/com.xored.f4.jdt.launching.ui/fan/SourceLookupParticipant.fan +++ b/com.xored.f4.jdt.launching.ui/fan/SourceLookupParticipant.fan @@ -17,7 +17,7 @@ using "[java]com.xored.fanide.internal.core.model" using [java] java.lang::Class using [java] java.io::File as JavaFile using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager class SourceLookupParticipant : AbstractSourceLookupParticipant { @@ -78,8 +78,8 @@ class SourceLookupParticipant : AbstractSourceLookupParticipant { private IModelElement[] findInWorkspace(Str pod, Str name) { result := IModelElement[,] - FantomProjectManager2.instance.allProjects.each |fp| { - if(fp.podName != pod) return + FantomProjectManager.instance.allProjects.each |fp| { + if (fp.podName != pod) return fp.project.accept |IResource res -> Bool| { if(res.getType == IResource.FILE && res.getName.equalsIgnoreCase(name)) { result.add(DLTKCore.create(res as IFile)) @@ -92,7 +92,7 @@ class SourceLookupParticipant : AbstractSourceLookupParticipant { private IModelElement[] findInLibs(Str pod, Str name) { result := IModelElement[,] - FantomProjectManager2.instance.allProjects.each |fp| { + FantomProjectManager.instance.allProjects.each |fp| { fp.scriptProject.getProjectFragments.each |IProjectFragment pf| { if (pf isnot PodFragment) return podFragment := pf as PodFragment diff --git a/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan b/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan index 99b419de..f40016f8 100644 --- a/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan +++ b/com.xored.f4.jdt.launching/fan/internal/FanJavaContainer.fan @@ -5,7 +5,7 @@ using [java] org.eclipse.dltk.launching using [java] org.eclipse.jdt.core using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager ** ** This adds .jars to the "Fantom Native Libraries (Java)" @@ -40,9 +40,9 @@ class FanJavaContainer : IClasspathContainer { createLibrary(f.normalize, home + `src/${f.basename}/java/`) // maybe it's a core Fantom pod } - FantomProject fp := FantomProjectManager2.instance.get(project.getProject) + FantomProject fp := FantomProjectManager.instance.get(project.getProject) fp.classpathDepends.each |loc, name| { - podFP := FantomProjectManager2.instance.getByPodName(name) + podFP := FantomProjectManager.instance.getByPodName(name) if (podFP != null) { IProject prj := podFP.project if (prj.isAccessible && prj.hasNature("org.eclipse.jdt.core.javanature")) { diff --git a/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan b/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan index e211f539..b08139a6 100644 --- a/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan +++ b/com.xored.f4.jdt.launching/fan/internal/FanJavaLaunchUtil.fan @@ -14,7 +14,7 @@ using [java] org.eclipse.swt.widgets::Display using [java] org.eclipse.ui::PlatformUI using f4launching using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager ** Used by FanJavaLaunchConfig & f4testing::FanTestingLaunchConfig class FanJavaLaunchUtil { @@ -65,7 +65,7 @@ class FanJavaLaunchUtil { static Str?[]? environment(ILaunchConfiguration? config, Str?[]? base) { iProj := AbstractScriptLaunchConfigurationDelegate.getProject(config) - proj := FantomProjectManager2.instance.get(iProj) + proj := FantomProjectManager.instance.get(iProj) compileEnv := proj.compileEnv copy := config.getWorkingCopy @@ -111,7 +111,7 @@ class FanJavaLaunchUtil { static Bool confirmLaunch(ILaunchConfiguration config) { iProj := AbstractScriptLaunchConfigurationDelegate.getProject(config) - proj := FantomProjectManager2.instance.get(iProj) + proj := FantomProjectManager.instance.get(iProj) projsInErr := findOpenProjects(proj, config).findAll { it.hasBuildErrs } if (projsInErr.isEmpty) return true @@ -128,7 +128,7 @@ class FanJavaLaunchUtil { private static FantomProject[] findOpenProjects(FantomProject fp, ILaunchConfiguration config) { projectList := (Str[]) config.getAttribute(LaunchConsts.projectList, ArrayList()).toArray - otherProjects := FantomProjectManager2.instance.getByPodName(fp.podName).dependentProjects.rw.add(fp) + otherProjects := FantomProjectManager.instance.getByPodName(fp.podName).dependentProjects.rw.add(fp) return otherProjects .exclude { it.isPlugin } diff --git a/com.xored.f4.launching/fan/LaunchConfigDelegate.fan b/com.xored.f4.launching/fan/LaunchConfigDelegate.fan index aef08289..ba068da9 100644 --- a/com.xored.f4.launching/fan/LaunchConfigDelegate.fan +++ b/com.xored.f4.launching/fan/LaunchConfigDelegate.fan @@ -13,7 +13,7 @@ using [java] org.eclipse.core.runtime using [java] org.eclipse.core.resources using [java] org.eclipse.core.variables using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager class LaunchConfigDelegate : AbstractScriptLaunchConfigurationDelegate { override Str? getLanguageId() { return F4Nature.id } @@ -55,7 +55,7 @@ class LaunchConfigDelegate : AbstractScriptLaunchConfigurationDelegate { if(projectName.isEmpty || useClassOnly) return className proj := ResourcesPlugin.getWorkspace.getRoot.getProject(projectName) - podName := FantomProjectManager2.instance.get(proj).podName + podName := FantomProjectManager.instance.get(proj).podName if(className.isEmpty) return podName return "$podName::$className" From ea62eab8ca67078e7c73e97c07ea956a6a815d61 Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Wed, 29 Apr 2020 16:50:00 +0100 Subject: [PATCH 11/17] Stoopid Git --- com.xored.f4.search/fan/callHierarchy/CalleeProcessor.fan | 4 ++-- com.xored.f4.testing/fan/FanTestRunnerUI.fan | 4 ++-- com.xored.f4.testing/fan/FanTestingLaunchShortcut.fan | 4 ++-- com.xored.f4.testing/fan/FanTestingTabGroup.fan | 4 ++-- .../fan/folding/FanCodeFoldingBlockProvider.fan | 3 ++- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/com.xored.f4.search/fan/callHierarchy/CalleeProcessor.fan b/com.xored.f4.search/fan/callHierarchy/CalleeProcessor.fan index fad88f13..34c35d95 100644 --- a/com.xored.f4.search/fan/callHierarchy/CalleeProcessor.fan +++ b/com.xored.f4.search/fan/callHierarchy/CalleeProcessor.fan @@ -18,7 +18,7 @@ using "[java]org.eclipse.dltk.internal.core" using f4core using f4parser using f4model -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager class CalleeProcessor : ICalleeProcessor { @@ -73,7 +73,7 @@ class MethodVisitor : AstVisitor { private static IMethod[] resolve(SlotRef slot) { IMethod[] methods := [,] - project := FantomProjectManager2.instance.getByPodName(slot.modelSlot.type.pod) + project := FantomProjectManager.instance.getByPodName(slot.modelSlot.type.pod) if( project == null) return methods project.scriptProject.getScriptFolders.each { ((IScriptFolder)it).getSourceModules.each { diff --git a/com.xored.f4.testing/fan/FanTestRunnerUI.fan b/com.xored.f4.testing/fan/FanTestRunnerUI.fan index 7238f383..7eb7ae31 100644 --- a/com.xored.f4.testing/fan/FanTestRunnerUI.fan +++ b/com.xored.f4.testing/fan/FanTestRunnerUI.fan @@ -20,7 +20,7 @@ using [java] java.lang::Class using [java] com.xored.f4.debug.ui::JavaOpenEditorAction using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager using f4model using f4debugUi @@ -118,7 +118,7 @@ class FanTestRunnerUI : AbstractTestRunnerUI, ITestElementResolver { method := podTypeMethod[2] if(pod == null) return null - fp := FantomProjectManager2.instance.getByPodName(pod) + fp := FantomProjectManager.instance.getByPodName(pod) if(fp == null) return null IMethod? result := diff --git a/com.xored.f4.testing/fan/FanTestingLaunchShortcut.fan b/com.xored.f4.testing/fan/FanTestingLaunchShortcut.fan index 908ad29c..6146abdf 100644 --- a/com.xored.f4.testing/fan/FanTestingLaunchShortcut.fan +++ b/com.xored.f4.testing/fan/FanTestingLaunchShortcut.fan @@ -23,7 +23,7 @@ using [java] org.eclipse.dltk.core.IProjectFragment using f4debugUi using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager using f4launching class FanTestingLaunchShortcut : AbstractScriptLaunchShortcut { @@ -72,7 +72,7 @@ class FanTestingLaunchShortcut : AbstractScriptLaunchShortcut { attrs := [Str:Obj?][:] typeName:="" if (!(script is IProject)) { - fp := FantomProjectManager2.instance.get(script.getProject) + fp := FantomProjectManager.instance.get(script.getProject) if(fp == null) return null //not a fantom project sourceModule := DLTKCore.createSourceModuleFrom(script) diff --git a/com.xored.f4.testing/fan/FanTestingTabGroup.fan b/com.xored.f4.testing/fan/FanTestingTabGroup.fan index dfd564b8..1af20701 100644 --- a/com.xored.f4.testing/fan/FanTestingTabGroup.fan +++ b/com.xored.f4.testing/fan/FanTestingTabGroup.fan @@ -16,7 +16,7 @@ using [java] org.eclipse.swt.layout using [java] org.eclipse.swt.events using [java] org.eclipse.swt using f4core -using f4core::FantomProjectManager2 +using f4core::FantomProjectManager using f4launching using f4model @@ -96,7 +96,7 @@ class FanTestingMainTab : MainLaunchConfigurationTab { className := mainClassText if(className.isEmpty) return true //that's ok, tests from entire pod - ns := FantomProjectManager2.instance.get(getProject.getProject).ns + ns := FantomProjectManager.instance.get(getProject.getProject).ns type := ns.currPod.findType(className,false) if(type == null) { setErrorMessage("Class $className is not found") diff --git a/com.xored.f4.ui.text/fan/folding/FanCodeFoldingBlockProvider.fan b/com.xored.f4.ui.text/fan/folding/FanCodeFoldingBlockProvider.fan index 1bd412b8..c1f12bb0 100644 --- a/com.xored.f4.ui.text/fan/folding/FanCodeFoldingBlockProvider.fan +++ b/com.xored.f4.ui.text/fan/folding/FanCodeFoldingBlockProvider.fan @@ -17,6 +17,7 @@ using [java]org.eclipse.dltk.core::SourceParserUtil using [java]org.eclipse.dltk.core::ISourceModule using f4parser using f4model +using f4core::FantomProjectManager using f4core class FanCodeFoldingBlockProvider : IFoldingBlockProvider @@ -52,7 +53,7 @@ class FanCodeFoldingBlockProvider : IFoldingBlockProvider project := content?.getModelElement?.getScriptProject?.getProject if(project == null) return EmptyNamespace() if (!(project?.isOpen ?: false)) return EmptyNamespace() - return FantomProjectManager2.instance.get(project).ns + return FantomProjectManager.instance.get(project).ns } override Void computeFoldableBlocks(IFoldingContent? content) { From 5cb57a5830d28b7c7e8b0f4a8e2ec2e202dadbfd Mon Sep 17 00:00:00 2001 From: Steve Eynon <3326741+SlimerDude@users.noreply.github.com> Date: Wed, 29 Apr 2020 20:00:25 +0100 Subject: [PATCH 12/17] New pods --- com.xored.f4.astView/f4astView.pod | Bin 14940 -> 14932 bytes com.xored.f4.builder.ui/f4builderUI.pod | Bin 12751 -> 12620 bytes com.xored.f4.builder/f4builder.pod | Bin 74420 -> 73600 bytes com.xored.f4.core/f4core.pod | Bin 163425 -> 165469 bytes com.xored.f4.debug.ui/f4debugUi.pod | Bin 61769 -> 61749 bytes com.xored.f4.debug/f4debug.pod | Bin 7342 -> 7328 bytes com.xored.f4.fcode.ui/f4fcodeUi.pod | Bin 40245 -> 40225 bytes com.xored.f4.fcode/f4fcode.pod | Bin 13825 -> 13804 bytes .../f4jdtLaunchingUI.pod | Bin 14871 -> 14861 bytes com.xored.f4.jdt.launching/f4jdtLaunching.pod | Bin 26028 -> 26237 bytes com.xored.f4.launchEnv/f4launchEnv.pod | Bin 5332 -> 5749 bytes com.xored.f4.launching/f4launching.pod | Bin 24558 -> 24558 bytes com.xored.f4.model/f4model.pod | Bin 24984 -> 24965 bytes com.xored.f4.parser/f4parser.pod | Bin 273664 -> 273645 bytes com.xored.f4.pathEnv/f4pathEnv.pod | Bin 15674 -> 15651 bytes com.xored.f4.search/f4search.pod | Bin 54204 -> 54184 bytes com.xored.f4.testing/f4testing.pod | Bin 24400 -> 24412 bytes com.xored.f4.ui.core/f4uiCore.pod | Bin 55934 -> 55915 bytes com.xored.f4.ui.jdt/f4uiJdt.pod | Bin 16069 -> 16049 bytes com.xored.f4.ui.text/f4uiText.pod | Bin 186726 -> 186702 bytes 20 files changed, 0 insertions(+), 0 deletions(-) diff --git a/com.xored.f4.astView/f4astView.pod b/com.xored.f4.astView/f4astView.pod index a8ac9e4b00bbee639ba640356818dc316b5b734a..3692ccbc3730788a9016b1eb5f82c520b94d1c98 100644 GIT binary patch delta 5334 zcmZWt1z1$w)~0iYk{o&nDTSe1T513VL{e%9LAn)2P^1KbLr5bjDXmCJD_s)Opdc_K zB1nnw5Ac8Y!guF+&NKV$^PaWWepl^vX1vC{z &CbSsr6F)2w_`3 ziE*GZ=Z=oowduRcScMuxO5Q%*5rhoH*4W=T5|*D|eOG`p?j-a(kf?vW=Uq5BR*eS6 z3DZ#4gY(@ynW)n>|1Fj~j7CoBQ@v5QXN^qptfmK>gm-dM$!!?7`;Bhu&K_6%SPb?i z>{|ETtnqF6Byrc{ 4`MStEEM9bL&*r zn^)kL%|+T9yH LQHj8|{I1 zGgb+4{^C*p=unh%TZ6aLHdg?@n6DUWMpI67FO+pR?h8K{2d8wsEYO+a&-#1EGR4@< z#lEX#fn9V|8 9^-z?L5g0tr|i!ZA+NOv)I<8>5Rza~ z)u=`26TVpAC&A?EbG8e(7>6Px@__t?8ezn^V6ICJ26z+0kfLI>H8ot7kx4spAhDut zJ+NUVuJ`>1%sA$vd^D7l?jI7b BN7=ex zZAdmGpx)p1q>(1SCJwkDKFMhD27x8=O6Gatfzd@|Yx--xF6Gm|4dteqV@Wi7QVv zxAoO7;R!M)7i+p7bnSL;McDiFsJvjS+WI;5o@@3f!XSd}I!gsxj zk{xnnxobZ>srz?73{wV&Q5sCqgtB-t>HPf2%q@!$br?sQnXM2+r~c{-9P@Kg>mgmW z$G3ot87|Tk4jsLbBDQJ#OhCWN4Bmt;%3ZmW{yfs38}Eh?iT%MxDapaB1q(P?Wk}e7 zL_eKfNP6;qG3ZlcrG@H3M6bBP_q0|-@Ft}}ZFO 7B3txm$I?Wb1-;r-bs z@RY2&Kl=#7^+vM2G&1suL1C$R|KJ p}K+0>}&wc643!LirTZuH46U!PF|;?LK;$ZA{*RC zYhtckxhnB) p=W1um652F(2j=QUR{K)w>9Tyc?6M=fLGs&74Vx?98`L#qj#8R^45B`) z$x=%ue)N6jh*p9*=Ix@DV~}RIO8kq*eZ^hju0Otp+a%X(Y$I0IP|Zu>?OP=&X>V-h zVN0<3FPu5Ly@$=WR3bbgJR^G*qom3Os040Pw$zkUy;?k)Y_`0sf!^v-ZC|JQOn`pr z6H4AQhQspN@gZ0mmO-N1tAaha;Z-vhy>@3`a~N?Q|B-oNT9sIOTNRSnnA0i(TsC7p zRe8_SyUil)8ABs}n0=LBT)S44pDCFj*(7Zy3UR&gwf%2$lftFgfh}m1fp&=~=d*x} zgVG8*vk(6APZHxQ
ea`j%F=Yz|*_K`m!TjkIpL zCf!X#+zk~b((wAzC_ts_bc=p>%&fey;bbDgyOT#87GcM~etw-u8`ZpeU zE&C?Xk$hic(gS{+yeV*ni_S(*l@0NFiY=&Rn7fXXu{eR0e|$sJqbay7G!cp;3QsbR z{-mNFmdZf9o1eG_mhDAm=2IFkHro$YIevYhPJJaJdNI}J3UN2m^Y)L>vBnN!e>v3@ zPtAKqfP{g^G`rcdR+x~bC8b)~fug)ziIQZ0y1l@xqut1u$=KIK2gbPpGIb+_bA4Xg z(f9bdl{)yLe(H`rne)gV6FM^ht%8#LBFF1P@A+M2#tX`#!zcAxDE3x?lMs75mFcHO zxqS;Q-c5)L{oE&Hk^=%IVsI;|U%aJRcATZf-FE7ze9mN~r>d(x7R~^F=JIun2N{7z z1PFIgy&a9}8X})e7@8UYBha6k^KZ@jW=kA!JWPJB@cGA1i0s1Pl^It_VF<;;JlR(J z7bE6$eqk*Z_p}p=tlIu8z1(BV7GKP$Lj{*|B=5;1Pw3_i_PiPQ`Rljh1bJ4WGC$%c z`{M=P&Eg*q)4+Y+HmJB}`k`rew05#sAN^Wuf05Pb5V*5%yuMF&nweX_XswB3lD-4M zzvDNCMgylh0hppmW=QV{P^iEJDc`@(2S5a^GH!V+92lf+WC()a@|nb-qW=e#M|Trx zO3bbV!M$~ovtKRi%Z(isYyGT#Rm`5CJDt &2VU6|XL^rwsQu_ik#t o`V&4p8GGN%nZ*Xy8zgnk2n+la-qF zx?+ZD(|DSw+~E&{61wR|=N8|R-y18Q+qJW^ErK_oe+f^i@;SUp`31AT#Q)h}C;i}A zdP`=2Bjt436{w%Km diDq*TE?Ava#csTBC zb6pCF*H#?u=LSKMy0Uk6^qh0oEWc|8Cf+Yq`kKim!~XQOxW>IPTKO3C(Lf_{xw?Ak zuYK#48XFwN=9C8EB+_3l2)H(tm96nWMXajgQ>2a*`7n&Hi?)X0=SoT6&zj$Z~C_ zJLO{N%k*9f*DU@wu%zl*@o{J(8w1DgJIXrPVT|}k_`K?$558*E+^N;V8^XMtVJ*C0 z84ii85@>{pDtITJfCE$I! bJSb7>3t&Nin4{p!smJ}SEaTHFE zh?Dq(A?fkINvb}Cc%A2TcP99G`fg>OILqg0ER_FD&;#A95M*us_y=e$A5mmTK1+vk zs`lGqXFCmU)4zLmqbAgMUWEk^v-imi%CPcAI-?v5tU6cPl(&xfAX_2jeToqChaaB| zI4y>6i+^XItGZ#GIn>nO+Vf2?n9BU2L1INuA*sp08%TxM)l!~T#BsZ4&({J5@U1a1UYYG%KRm3J94gmrIF$-jA zL8=FuB~&~CDVB!06L_mAOSH=~lLs#+AA6c8);+V2_Mv{N!M(r56PLIP!`|^p%mM*3 zxaPT_Ap5`*o;Bq%>qWUcuSf`_iw&R6jo4b7vce>ZeR+{JC3CKFD}NPxSR1_ZMPE6A zuM;ThF3Od+)mCh*ZbuvB{N6!~0oiY4p8yGkv)PrLvnp@-z=aP#=k&5V)otH>mV0fp z&59>kw3B!Ik4h%l+m8_u4HXlII)il;S`;V4!?%!Hjc=A;%m@m^Y+gC_ZOv*p&qIvK z-r`}>0O(>9RPD&Fc5b&S=*@F89dZ1`;||TxtNzKEjD0?e;?6rC0U|uB=Tj`asn~e* zsTq1eBOg0b%XM0kqIuP*LV`p+6sMt3;C8O}?GKZAPGdSAJOP%kE87#G`0*a^9k%CX z9)%Zt`vRsVrH}pOAGqD`R`6D-?9;k#IXHc%a}kBY)z^9Jnadj7;8ZXP(Z1v8jVqib zZRgC6>ZEF}_c}WID2^f>wll{~&Z*YhTY6cl=6drv5)G7OsZ }!(}x9cED-lm z)~daKb8{Fk^g};!a-Yesf%m36ngih{eUxjwDF=gz6=c-$KQq!PMi?rs$Ucr>%@9yu zS^9}&Kg2WRzs?Ct>Ojq=XKa?-+GBGLjrmz9VfozQ@yz{U3KuNqheG;g>BI3l-qVi! zOxi^8B*-4auQI7|k3-Yxe!f1@${peL*EcjStD`u2h4s+t$uLNjw#$ekir K*9P2aajSmcG;^>^*M`*DH1wCEAjP-;^_X;uI6{6D;sh=$3k0$z7CDSk9@Bh z -M7n_v#svBxrbSq1L2)CPahlA4Z2XPKKzy7+n |A&VI9P8hpFM{*8J3s&0Oq()8?mE3v%PjwEU7s+t %_eq0wtI-v}YSJ(|<9Img4UOoyx5&8YZ3YvAauV|4k^GN2zE89cv`t-ox27H4= zWYNie^n{X?Nn}{JBwq1-#M{6+nV2II^1ztqqFzauj?vAE?wc2`hwPv6tWNotbk<6D zv=2xJZw}dXUeVxl^oUNAGA>|_lM6L&X}Pmk=V~aMe~){A?t9SRg`|L@1`a8ficHNL zJr;d%813!%i=oWBNNkiU{{FR=l)87h7W^M)unn5QFK|fEJ4a^`fA1bvM>wYNCzD(( z{AXe9f4$}Z!&+7df(^!;!}b32g#fUD@t@VYI2a=~Jx~T?VLg3*v45n(r2qf^ihy+( z`x)k96L{sn0L-Wv;1h 1EVqfXR97nAU~m(prB( z03VnQ=obN H3*UJ3v$#sQd% zQn8(?cJZWjdHV;HzzflHjxL_Gm@h5m1`LV{oT+p1oJDg9;{-s&IL|N_&ngU;FwX#U zF`hGwpPna4B<9%<%mulGDFpJwc+W857s|QxC5%6S64T&1 pX)I zYwKzQz`)dii#XMpL|0=n7$F9|jbWYfl0 k0_*vH)5V=h80Lt;>EF2RxEEw?(!DBepisC~ Im3le3 >b(JyOWW`kyY6w zWEKB|-|zd=_dVD3Uhn(f*FBzlKKK0$dEvZ3NIhZ_FaZSx1p&e?@ezm}jY{a#Z{-4O zZUx4!TxM4-$c~I~yZb4|JnWlH*p!&XXmUgSNM?47VnfSR&=gtmb$*fP7(E1(P1Ly4 zF(i6aQOzMM{`@F+_McS^0`mXEePYOJMk&)J@8wT#`kmXMWJo@;4Giw$yA<;tL6_ zyr7HHao3cGE9okBQ{EUXqd(g*6tiJ3fF)+_#aUco%#V@ho3+(_TIOQP((fwIc!&QA zA6oq)xVqmfYgUes%X(58zjjC5kG-NR4BZ3uW_y4D9-3HG)H?KS=I$+RyTXGY(*jph zZF=Q&pjn)dUj>q-7#_nzKYGE;0L9=mtPf14PoM^3Szjtbu(U?d=TO-Mj+$}Ht@ z!gx|*ynTR5GB{dKkKKSp+2j@SKGPdyrkAno@XbN%n;Dcsn9K}jc Sa z`75Az?YCFcIWv zaJ_r{WweUT16RgZI7IPNdFdKzkZoAU#TvLxJ$7e8F|>ARKOTy(NMT>f9pP!>)c?${ z^bSkk6&T#Y=8?qXT8EfE8&qyh6z_rg ^H`CL+tRmX!N0y~DrNiQ< zDAdn|Ng bl;M H>)hJQ}Q7hp#z zj&`|~Cs4_2Qu??dN{ul}!9?A7RMUmy{8jNujDhgg*l6OSRDruZ(d@FA=L2!V9;-zf zb2pG{?rW(*4I$nu-dJUCzX}Q$_;VPRugv8&JJnJKKLyiVmT-%GY$%*c?dDB48q8>Q z+k4KYam<|Ve5h+G7j*9I&J&!U=F6h(3uu461`aFC)J)mKKGFMc8Obndz?| K73!~|?AF2HrH_)CQ z&)A1 `4mc?qmc}O;?J13+0=|B^HX;FsP<;8 z8Qj`iV{AtQs`5o_73UfS%WZ347#kY7J9((g-Ss3cc(k7Guq|k~xK`Nr2Qx>?yu+cU zbv5llHqw*>{TAfC?OX~I@$Ivf#Kk3kJs>=E>{iIg;yZlgG!p&oSbJ*qqq 7c@`#9&9i%F5=B>VY&Q;yaGui?R#M;_S7m0Eiv?*XA z!CF3{{G4h^1fePklI4}Pi9}0bfetGj@>{)WO|!;zq&YLC!B~b4xAF9XBuem<*peXT z2ATd@n@5JZ0S?bh!QEXFOy1;F;O|v0vA0qmsU_6)D#7-Zb?o!>(VA^fPI^9b$Pg%IKRDXKw0U zCv8w4UyrYiSWS5J6&qLX0;~f~!K$ZVf%|j$y{$_Nd^Ft5^$Du?sR|i_&ZyXk4VZ;n z{Hj {mh})8S?=G^QNS&E(R7pJ*=3)GPf? zXV&h=hP!$1S5rpE?@^csOwmWuh%|q7Y)Hc1;t3x-=i5Gz<^U5e?4ueZv}_UHA^dE> z9%x$8L02?qxiX4TvDZLPr;GS~b)@qe)^APEQckdqqKwjS<=~&5DggYYBPH@SKzz24 z+sc-eXD1eOzAi?xM?T+S_C~{}f=l_@%Bs7>utje=ij~S-f(Ab?3}*O0hiEKyI!fN- zc^&pscq!e1%TCBc*58rFAEwBlfWA|8GY$oHoOD BJ`P&Hki*Z5R@i@US_K$A++f>wj+jrh zGU_w_HO?d64V h;$xY(?T@VpU)_6%yY4xfX@8b+5*p zbJT|GFn_qHmYQQmYS|^WKb*ZV5h1JQwuSI%$Trv6o1+VEoat+O`6#ye70!;0DJ3`i zRs428d0Uhoa@M4Hlkf7w2E~sHZ8BnGtK+x+)a>8_T)nqedmd^Zmp7Jm2<<3dzr_F2 zuYv`8RMsL{FQfbPn^nOPjmhzaBTr4P>shceUMq1L*(Qh#X9|rKo{4;t$D7SJKyE!X z!}_3|{3orkY40?ySAJ_20)8P^FrPW1HQ4kaahLC7E7c?aP{{UV*65``1M|0LmFtS@ zqHp=QV-s7>Mtpp~MzUjX(_ 8+!D5e#k3F zV|p;@ea=?(6vU?y7keE(^FLemgV(_(nNfa}uwF&1B2+MTsdq3dlIb}$UEBI|$5!eZ z334Z|Wf+}8aRkr%v5Y;Rp8aUwi&d#%iN%d1x?al$#?3-6L>=WDUEU28mS9MdcIdD> z 8CHD# zuwk|VY2uLi6Z 46t>RptI{)pV zWsOnFhc{$rwbZba%0 47>| zuA9Yon188mK{gD~IGC+6%5%CtGGz3&Dc+2`QdvSxf6(6#G(Gh(?SNKWnL#G_537Lf z_Kvq7-eXlHhgE?3#3)pL4W@bQHVn7cv6q&3evqg&ZtIs%@+J`;fBEyhJpKZB1ZIyx zw1O%BfC_|QXiH~yd2k@M7%8KpQdsS~PcFLGan-n(TW6Q3>Q4p34FswZmR|)Ku)O z*6*@h<18LWQbm?}HNIC?s97jPI+yJN&cytI*DGoWlTA4!nGrXB*-~0rUjK5T3{*R4 zZ%LelDMxNiN9VR%M5!+W`7bV&lR41sDiyIr`SDCrqI+$MHEYB=5=i_Vcf$4TMSR`I zLEm}ZV-9-Hd=pIG!M-Y?1y>%#rP2?umfFyE4iMl-Xc(t|plx @06wCA*7ZUo-{Z} z(tK_B6P=w`X6_36s@{T|1DQyIud%qtaf^Od?i@gSXKmPDE+?SGQ5I&?Aqq3Q>TM_0 zp3~|3et%q@D6_m-plJ{={Jlq6VE_DD{uWDJNZr=898DF{`wq7V?Rn%7O0hp#Bzv&@ zdzWOJxeHavWzg!r#3I8ZT0cw$toLXUY2BJg9<;Xh4tbxyUJv6hV=_Ub&0#p|hwakp zt4VQ<326bSg8AH++iwRhiy6qeZqr`#ho_pt K0s z>BbmevV~B+&Z6i;mQ^h`CFOQCD{D|g?i-niPcR34bNQlR^jPMjIaVKR;HAcZPCIL- zz@X~ivwkv3f3D{kWD6g>w9t0+c-!hS#xsU?Mfojnf;aCORZu%bgYHeJR@4V2nO^~V zpAyVY31*2e3=M8PdSk5^U`Xa_Doq!9A=oX_==-yA5}p#JVd#f@nkv)p){y`_p?*@+ zD+uRjmkM1&X9rOrz5=TgUHKJJ-m??5Mi(C5uk<`{B{W7YMvu+Qz#DC;P|mk9OGMCZ zQ!? ZNq;e1LB}hidHy*rPsjoBkh}*AfYK&X?MCX>9RHHaB6Ec^(VYk$V1M|S%u)BSl z`aS#Zh8Ka#H`zhviPvW(ya^JMCJj`1)bz0`N5j|6Jx;4&{CR(i>g0KUl+GTajsK*x z)Oj{S5Wek39^VDvuL1ybap9vQE=HVP?;O5T*xR|^M2WfCxS~8oZ=&pxdW1w^g8y8g zAWsgF4EW>V|5rV}(Ksgd^1Y1$&P#yL9ZEVm6yon5POJ|U{}R|cp l!3#WxCj3Ktic<+NCBbnd?c|6;cM9h| zz#++bB<+GEI}t4iFqb@c^c#!+EusagX*mIZNgA%hJ$Ix8Da7Ncd-2a{B|-GSnk4K< z)VOJC^=CYS72eAHr$pfZaVZ&!Lq>;P8{jU*Ohm;1L`lJpV24c_P=berFaZykpn$m) z%@MzoZ5h)kegOc3H184ZVQT|ikY*-K0*s{vj tKvj}`d}t9?MR=atc)Zgg2Nxh9 z@VsLursnCTkIzC;dmFct8#^RUeez!JGM?~o6z4g$F?zs2UgAi=$x?7yK#|aY1eiFX sy#JSgKc8>^4!{ezf!6>Z0VkF-Fd;8+tU0v;8=)w`ryxM4D}IRae{?JAg#Z8m diff --git a/com.xored.f4.builder.ui/f4builderUI.pod b/com.xored.f4.builder.ui/f4builderUI.pod index 039db0e5553efe99f3eed0569302e0cbeaa4345e..ce2876c1f571d74f7726991d1b15c9d8426c2244 100644 GIT binary patch delta 6839 zcmZu$1yq#J-lkcQ&ZS#&>5}erVd?HhKmjR 245^P6a6eCB-GB6vc1h z-g7Vh?>A@XoO$PcX5RVDub!EmvKzO>hiIZ>kf8to02Jq(+!TBoDEI|fJCdJB?(i4x z0haDUe0>S_h2v>-9)9zCH_{~+$N146%7yBjbN<5#qovpqf ytB)nJ9xWZj6FRXcm=S$PW L^D^JUH zG0ZHvm{>`MtCz``^E-5P48RCaL3^o 3moGC %`-($YESYL(F^ zNx)_o#5m5}*&J^(dYlbjIhMaZRj(&n w=53>?i`Gp_=e=l*%LvWCV7rF zah9!%A#zxaRsCzCBY+ZnOnN%G Ie%|W(Vh^L8-`k*++Qi`eq 6cU#NvP^kQrM4d{{Gq!DC}0a{tXkaURexv$U2KA7db}dVrN{Ba`8;exs^~J} zGE+#Ns4|Y7O=8%dZ#mR--;3(Q{q?9b+71h5+Qd)4lli8z#%x~eUIcm>Uz+tZB2Z~? zmMOp3gkj>7P0Z|NX2vzTSrmMc@6X7EetD&ouhd5TR_vZqA33q-*mEymt5 1;)|!r#-oes1mVEqmb`J>hO3JXlE8{ zji7jVYQ|` !g+gA^)WPz9>>h_tohSaLm>hA~cL1 zQu-XNxo2l0F2CaQ8T$aD;dt%Nwj-@6Md*{KWqgW(EAUkb8F#IkPi-Wl1P7Zqs1U*m z;m2zoJcvP3d|gHUxe?=hGUG}y=m^t??DGxrju^31z@OC!_Ss7VgHBZj)phkE-$BMX z2Ml^%>?3}84xE>^bMgc`jRNS7@bDOTXgxC%46^M(wf!8iG 183 z(oodwCqaCLS$XPPv@s~ib_5XCovAD_C_7s#TopuI8|~xn6%mdniqldH7EhvkE9~pD zFGDJ3-2|W9%AS9ck4aG6*(*=1>WH7f)<=5et#L>-iNp6q-9IXRvoieSJHPrm%L~Qy ziR~b 96`g4P@mCe+=qJe>qX|wdYyu<}4?`U^0&HJX#VGVP=F6s*{*dF+I zE$dL~@kdtKKPW;qDjCP%z+j>BHd%?^^%=b#X(7{X+Bb?LTZpMHL8cKc^gD{X)!R5W zoBfz1iJTl6<_s@J$Y-+SVUPSxg|<4*`ChNC_K~=DbvZ@y=H`YjI)vK9_@vUdJ mFg0!0&y-C`<5FiZ)D_Fb8h|FMHmL5B8^>%7` P-vc$B1fVz7a?yIH|j0~|HaM%%-A19|#mNN!f2R=xg3NU5`cMP$*fRfcEaCp7m z3P01HQ25%@K{aU_m{JumSqxX^HTHr|WK@~RavM4%@tJtP(RJV8ep$zB_tEWv$=*h^ z3HRz}iZ%701b!djJXkAwaeo$D%31~tIVxu&>ipQYW}rM1>c6tc)BOcIhZ;nVS&P(n zBWXaGSDpM0yp Y&KS_QB~2LTB#S=YJ~Uhply+*}TE2ssMQ z^#tp@7%y?T_n rDvZDQC*Z;*7ZrB2{5TxRYq2Ktb1TG^qb?p(U zJkijOJm#@H5!nECjDq^T5fxTKHbRX@dbq`3!weY4RzI10?5`fw$xd$f_?=gSnF~(c z{dOc@W&fCbYnDaUOzbzW! %TsX`2fH>Vb*L@_)=e`lE@dqw2!Mi^hddf); ~upwWd4B15%J#i}DQN`!V#iyR S%Zg0|dQ#Y4L&w_~X{AP= z2+}3cZ}^aOx3vV=w9C@IqYD+5;&}YdJ!8bxKZRS*Qlp=ZPO7cADaObw%WGcP9xdon zL&78v(L?xN6dt{wSO%Q0c}#HFko9Qm+f(I7(?j8sCk7vWn`(-W($B9DjH0G+U3LT4 z{_wIzh9-;m&CF6$AKQF4!G$hYpw>ZsJ3Alrw3{Qen?_5eevrwO!hvadQ$kd(dHc|j z=UW#q5tl!Mu#Xz2X9N8WCsL!1I&+%PvENxWo~1CQGhbW0!+J->uzfi;k|7qK?$g@h zCE6U=bL)qT86&5QNQ?0Nr^El-B4GA-E;qK&FpU?5dTSKM1Oia^6nU+;5TfXwj~UI7 z+Ncv(B0Du!B}k=;T|u16(~Z<(GuwCpRAG2;G4>HN{aeM>BVMgnOhl|;C&52gjuMA_ zkbF!Kb^tRCi8-e5(Me wK*%bNiuCd*i>(ssl_@oT Rx_ec LB==#t6r?Jhm0Qdw}gIn4}{Z~6tpp3&;XK@kpE6N)$ c>DvsQ9 z#eaGs?2?e59AIayV`FV?V@+(0ER+sJDfUEjFli!bs*j+LvwBGXv`5yzV jj+6UTMO3kmnzE!T zsL7Ah-8-uyOnt0o*XydyzCXv2hRUX4X5r!! jqWirx9it_YyUGW-e2{nnz- zpjn~8@REInR1e?gVT6|^$U^7%5tp7_p21h&wAR4=?&Qq#70acALg~EjrPA-^-`Het zR6+zYHhO?m$+LgD0gQq~3YypwnI>AiHJ#>$^M#}Jb3u5#Vn2St7Gr@Z&Jf@%{3`rK z>t>SCx{p6#H5A(#!QqNYY+41Kx}*t2Q02?rGetca3{b-%pO%x%F5R{73zetx${@{P zEPkQGB)a)6vT}0(;JGPb`o@POTtdJ6oyKz$??)D{f+wjW?7_U^i_pRid+|CId#zP9 zKpEfl!}J1l{b;t_3ffHdig}t0Tczp&Wlcp=5pfrn`{PdL8_!0|6KLKjZ?Y{%Mx_mk zwrJ2cGzxE}7031xZR%;!h{1w0g*@KJaXhTspudOz8RH&d9go6TzcUx=FTRoV*n-z( zai#giA4+V@9qV<~#9>gc`Fzlh?qrL}=~eXm-zDO!76xtoQS}Z66FS*+jNMCkR)YOT ze}=@$50cC=c-7N+HQlS9z$&HEJYys~w|N;BHbVo0He}WUdI_5^)swBHzKA6k|I*ga zb)VZRUs}&z+HQL`b uC4b-obrR;NDxL4}BJ^Tj+ajq2XDPuSqU3|KJ7U_dR9Z z7CbsQos!b;8B6|%h%vgA3+X3v@31DozJ_QO$|+;TJG|fH2QzT##k0w{^|hKD*KBI5 z^l>u=cC!{pbD3VpA)?grQf69n`uV<*1{V5FPY>{&ez8(+C@$gi`Ybpea?(J^E}P)A z2DZC2l)14 >}09W6}5 zXhf_%)M8~`Uc1|2t3`b%WWl``l(9z1>rCi%0!ig=Q|zIp!D584eARfukSw6`BtR+R zyJr{TdHZWppn%t_=_)AsL}Zq;1zr5oft!0}6=<|eq@&)I>+vn~unae>K?*{MRKnk} zi-`gP1p{i*aujCK!XqP%F!OrkhRKa|!c>Sb@-GJwGaBQQh=Y}J&^o_@H|5i%(0GWj zef>7mS@iQ|!y~4&(UQ0mH7Q5>@0{}JkLBd1S$_D0%z@Z32qb=nafPYjnLRBxW3e&& ziO8yVVhUpG#0x1<&P`ofr!2=m5SFjEql%Hycy4^1o)O}M2(jo2E>r{xHV*AB$;LS* zBnJ-H48654;s3Ent4&GjRN9#qbelFw_$6pNkm6AOKjKJ0h5l_%$rGCWazumB4xH#z zV?_mBX+St?`E*hK*@k2Osu{Ty k$;-~icIq-#ghdHQ5`ALx4BJaRSMD0b%E+sJ);ZkqlrC+E z-EK@8I{NT|6~eSNGtSGSc;dc# 5u7^>CF#HDks8JZpk`vdxkPSJ+&{OdAsL9cr4kJx z)IR^>6z4Q!w `fD(D}hq63i>l$MbXnrmR8y=+Vr+zcJ$-~+RM)UA{;Sz<=h zd`^w(YRm*um`jw_;%z%VTTMo(xIE1LR rw%6JhHSFEtsGWdR9 zdbz&C*yFA0vzL)yGoRSBsX?MLRA-JAXa;S)nH$phs%)dj+_>yJyLAOs9rBVIHZ^SG zp|}mengx>5cy>*U5}Q$BU*{szXyCoPM|g*N6n0r+ maKglKb~>xmlYcf=mXL(fG>kV_Eg%VZ#X3E7Dry2 zs?Ano6I`iOoQ2aHc6=%O5$#!eG;tzf3_UyI^%fJcNZ1bsNaD*LxmZ8m5&P(*#5MOm zr0r*4B%ucwFQxS)jjTVsbRL+EkGZ<3>)p_E#cN*6?gAzYF6@$ah^E7{ vqx0 pV^}2dWM3so!9&7pV-E^QbN17-)0~qBjSKMp&0J}@V z{!p;#5{I-!)UwN%ZWXIX-hK9dH}nD_+hacFD++S33LDrCq>|~4uJZhK{kHRU6j&{^ zxp6|;fuCu2eAid{h$io1yj+5&tI!3mjZ5Vs;2^p2Q)C(!aiDL^EUdmE$X@29xWu}1 zjv|A7#2NY%66v(luvl}LDLeJ2tFu)m4_p?t%Kd@LEFUN~j+#(18UEr)6jXU7R?fv^ zHQY(v;Qb}}C|oPG_tBma&EES+;nB_6oRu8C>Tuf{_I~@7H_jTo7U=0X2mFvYT+HB& z7Jy2)iH$Tocjd)t2)&vo7= HMK zYMZgh!FV5_d)1M(s scMd`1U_!U)1%*Iym+FEdor zGg|U`uObNmUHMJP`6s#2cq6F71(>q~G87Ho$&+1Lz+tj4Z@Y3 h_*!onD-P&B;o6qO+l|y(btqY6f;j%mjrCY>c*U4)B zf&1y7k4MxVWvR;@Ka~s+Ef(L;aJ#s*PZ(|-zQ4>K1V`rApnG>i{Qan02uzT*9pk3a zaKu`HiH|%^K>l-Q4J+m1X1M9OduK$5j2r(gq_ATy`diH1>j{$o0+6kG7%w-|E#~gc z0_k6vESLv3^DXA?IhgD(3=^z|o8=Z`^+~%I5BZK7Sqc8aw7`zJ=@@U;eE0P9H|7rx zjF*S@_P_+@p+O~sx$w~6KE?CUpvB<9*6>(iZ$NmrU%Go9VEpS#gs^iSzFW*)?tinQ z|CV%^5{UB_bC<-@{TG02@xu~8Jb;@O-w+{KFNgxIkr>9rO9?v!k=#mhmu&F=HFlU3 z36#100K=Um{x_%=#>mTqWXgokz9*_mr$%l8_HHPe>p@t<=Gr@59|Fx-3=3cxpNI|-hmFh2n zU@&2Rj@w~Y{4}UGut0w9+oy7V8Z=dUSQ|a<%_NM->;t)L()52#(#8NI5a78rYM6`w qHQH+ym>esR`>ymi2LEs@pxprz1!afmpR2Hf x{9J;$hx RgrFcnNJAQ~<%E}Dw! z__4Ht-vqZ{PIin?8^QuI$eCa63Oi)s;QjH9tAVSeYwPfo*4TGXekVymJUU@k6?1G} zn?m#*qr!UT2Jb*XTd-@SP FlnRC?(A*jQim6YDKpMOOnsVa zq@|i?=n3<4V6)9f-)29s!Wq=0&S1T?i8gm>Vsw xmqt_kmqoJW?1JKdX{*VchiI55@ zhE4=$5d%uwdNSEC$>2?v6gZXsa4f*}eN-IwOo_2+ypR>65kjH+LU{dws?^Un-_Q?2 zf8Bj7^>QK6--2CQ`uH%&KKLpdltFCO6I62PxmwBF8R=2-EBcm>=IW?4jn?f(IAg*F zg9c*AzPM -p2D=tXn5z zSyqWd_)8zVgyHqK213logbF~S#UBjw9S@lp=^rX;E6n)3EGwzu5dli8Y<1oe-Ye7q z!trX9DtEb*3BR$4Z|95b?Ks_jl`iP(&6kY`ecwdTW2=EL|BC4Rr26HI0I|>U6o7o^ zXJjPPH=&0`ZBLT(Y7O_sOPV8WMJOhP^hBSki8=G=xWGEzT!3l{-X|>ZYsKOCQx6$U zWl7xbt*V3RF|k_X7YMVbh(}nn*D^UwRl+~@(CUEs8GUv{A2f#Q!`L&fWJy)AjYV9B zbu{reg%iljhI5kjtQged?}S**n`Jk&z@oFag4~`ZwR*K~uQ|NM`PBz~#ZyfrQQ6Ji zmjfiNZT~r&UtourQ{TEfP_!UBZE2l6 zQTh&BfjzVzxKsH;wpspO$FGgcPgDlOn*#d9ji%y*7Z0rfKO=)8nj!n|?xlgE;gkj^ z-))!5jHrtICn}vIE;&d!HT$|&o7l`&74hB`XbLu0d^D7~lkP-d8(D5*QgyC2NnowA z`KX$Gr|h{ql*!+t$c9#S-}=C)v(USuyF(zuY|-?@$kYR56z}>|T=1z56D=b@FKt-Q z4p^cKaJwMLPxt2?r*f}C$;ji*Gx*U{kcyhyn Q1oyZrb>VTPtj6B%o!iXB=DOXFe|TWT-*b9NsAYO{3%zwac>ZMZ=X!v^jYKUIJM`;IM9728Cu* zvRy!<7Cvp@Imxwi@)Sig@&sa`p?$#nw{vR67KKOdDl3mAL2*gBb>!dH5B;>RpLGQ@ z9QUQ`^FKkmJ2AMRqBLtWJe!*Ncx%vv7IQnNo+>De^hcgOPAIiiv_*=iW~{qFv|yo! zs(_kh8eU2jMW8R`PJ>g9cJG;Nw`O =&SngB7ro)<=1Z5SfsXcj<7E7*~}DVq)*(_=W=V+UAjmupQO@<9^oy zI-uP2@tDsf%#2#YS8|;BcCj&uH`{Cq456~Qb5h=zS8&zlm$9C@TZY^>$Eo>AjptBz zp`@!Z;)x`%d#=sC6h2!Ld*sr6>6Li2_`J~hQR0h}x&T8z4e}lJi%QlHxxbvBzk)-9 z^&tZ%cJEteCk7cZpBTohm@41hUf{UaoF#)M>@_N)sAK+HaRoSR_JhTw!7@OsQ9c=W zLm7V8E=u|bXtWPx2?4yVJyMgCgfa@GQ*~8k-&{2My84x@?1@Pp&q0z%4rHZ!aZVW- z$m-8H7EXFs2M#c=&r;@?6;VNjuwc~uGlKEqa=?xG vMzB z(C+or7V|&^*Bt$yX80ve{TfMu=4v$O?Dc~MRtC w~D`HpP>|E zcoz}!>2)y(vROnpRki{@x!)=N)n!xjfTb#eRANl1h|RK+8uU@}3<>cK$7nG*yvo*R zadO fIfD!k1R97oVHK-Sc_>kww%@Z7In*0;>kveua6Gz>!L zn>HWftsc_R^JK&=w!2-M<)BSb)hE>DNdCtK;QXC!qcThP>pM}CL>P{Kb2(^<1>r%@ z=an%ufr!>S@xovHsWatM$mE!3EhV(Du$8f=Uc6jXjwMYA*BG%S8HxD;XjUQ_^>Ox% zw4zKmj5*rfHLo#zj7dr4kkad^4Y1 T~$=I+!%LFl+1<8m&_b!ePfgZV@8DC`GM#A+&K_dDfpj1EGS28%wtN!!1 z8P30Am$W3BWc)-HSM9hmjk7DDK@YUYiPhz%YbI0)Ni_I4x?Vo`vaOsUX722z(c<&S zrIbn|VVES(f9zFPr$1T3?%L&wbUY%oYAbE|S`SAm##~w`JyibxMv5|35VDelnIh7{ zLetX1!qS4&0yU^?0WWL- CbDUa5kv oqDtrJ;Gd>@n56k%tG7dT?Y~k_I;x7+eP}t3l@i`tXP-o@vhwx`pVy2kQR+SxCFy=aK1-It$@^7&^c zFI{}Pq6gdgHx?%7yerlPQxkgiK9CWs#Sn;nYP%zKS{K4!&w=Hs(#z9^bwXAU m0MWV2Tu lF>%8VR}2u!iRBpeo}LeB7B1bJxGP*u%V;sTU?Tc0v0etNP#1skg~XlmAoLE zNe-i8B+K-ORaqh+ANHCa!fV&r9RRi2;4M|ZW#TCW&)u={vuft>tLEU!Ccenxmd7cg zRT0&l<$I}BCdRWFDHlGFtexhBOtzI?_5MiYYR9v&U?q~vImymrS1Y+t?B3z2=q0r7 za?b)cnntB($Yyafqsf~^S^Lda)d)bnau0WarPd^top=SVkQKuT?oa!L%4{W|aiyxv z1!0G98Y;p$PeJQ~&Rlb`jB>H={qG2FTI`bK__RDxN!h?YLUV~u`>TDm#wIqLr;xFN z(y?^sg)NB$+l93(=r`$C{%z>sz;&kOM_nXYdr0Y4|9-cu2WPKJlhh{H!;L-_%x%2* z)$8oj2EMWi2z*+BZB%PDTa*T?$Z?*~27OpIo|e=gxt;Mq>@wHyPIG7VYoeY}kMQIS zA*TJ~40?^3Lp+8Jb1UK|>C1&>hIqtT=f1_v@)FHf-nXjzjc}Bu;NUjn_ZtnM^egdS z`fUbEAW49Da3+!t0RUpPzQ7WripBuOf?gxT9^pFn>moed`&~316hjtwFTDYQhqcAm zu>1HqCLtIqHd~0M6UU5H88F?Vh=2g4_JsPViee|zf 7j&S`zMa02?o9({d7)`)@h|>Sr5mF(@k^ z`7cXv1}ejwu&^lzds7vaWC(br7-Z73t#%-hCSSD-42%s7PGb1KnvqFbrds+Tc5gGX zDtM8Su*j&vpqoOUxC44Dn9KaDwa8kzAJ>rDE?CboIh<(*y|E4B61$k!B1yq5R#5>` zLJg+)7e<(DY!X@hgc2@wL>)q9%JL!YFX)A9LIxDLO)!_!r;Bg`u<)X;!iZ6v$Xg9I z-hP !PopyXZ27?k2&t Cvg!Nv&X I(w$7|B@)5i&zJp=>;Z*G;wMFD) z;B(fh_Bd`OU!L_5WqbO8*5@4+v5OT?+|0KD0YAP9HvZ7arCC}3ey9>4df!X*v^{^e zuj>O3KP$sEOZF2_8k So7FpI0Aa0?#s <=Fe-kDiI* z+(UPz@W!Zht5Ck}r%5CDU{E3W(75HCfA`hs;WrJAty<>$c={OAZ(rBluFRTS82c2D zcO2L1R|^w=#Q9OWx2&~ti4%EHI}kVbc3Mm#JPDeV`)M?WI>C1v%HbVlzc~ z_%!04|!U_ <*D6ye76pncglQXv4Bk!7n zm}K+L&-CU`lBw4QJEbkfbYH74s?R%Rl{21#9)!2J9oC=Sud^meT(~vt4Z`W0ws35S z)WlAXSUuGOcQbo2jfYjdu|f=ZGuzarLQ->t&JLkj5V$JL61198=AP^SUd{r ftZ>c<_W&j|^mMp*`o0a7PB65CHGd*E=)?{;qPBa}Vo>89 zu3b)4$PsATj1g{Rq>$*DdqB$K^(KsG(_@taq0iHN`vrk!iStV3ZvGt4XIoQ_``u!( zl8D{HX?O>By4ZlpwD(e70@DQTWmnMf9N%}>3eF;(vvuzyBwp4+3N`-Pn6}mr;Vc7d zn>i}~^an#j`(Grz+JBCv37m&+{d`X2JLX?tP`5?;)ST3LIRR@NWWvY?drO7h5s0}G zv3r`ekdupsOOW?5_~gaeI!}f5FabQgJy9e-YXV*e)mNyL<#0fvwPz`oNd%Go07nlB z4vH7*MSgc#P)sU(VcD7VT)-y8W79hduO{S)cfP-L-V#%P@KjGPZ&Gb`yU$3}sgYC~ zy4l4MaZ`Djc&HMS@Qf5#?sfDtF11_I>4HfT9$O=em~@#&yB#9%utYGpg7%Hx0)DGp z*#ca#<#?_2J^etT7?G`h2yp0FD=>t2er0bx>QN9mm=yh?W18fch1|2jZ9C~bOtbhO zpBcfKWyPYesD;+7qCFKi1xIivBq)HmFUBkPXcz)9(^%`jS)T|YhOIfnncp>Lsb8In zo;|rZ{<`%uP$HKszzz1=CCbV`fxgpXO%DLKDi^2+12~U{j1QpF8_>YeYQ_g-{whaK zazXT@_i2XT^u1}=aLO1yDq@Ih67lJ4aL$Id<6#yQ!A9s> I0*ShO*%F;*+LV$m^K*_|hE;;_3|8%55pF^@dmytZub9U5Q z@$6$VlvwwW0#vG1w3oSXYF2m>Zv_u!pIjrYl{PXu6XdbUwiS?qc|}N^ObfM>$JZ*C z9kTZm&0F!+E=lG{a%_AOe;>Xxlwb4t?(hb$i8=c*NkKnD?sEOnPOp~F0i2Vs>FOp9 zuMLj#_Mu|%6yfjDK(_Tn9py6q8I-7%7qX3|80-2?%|q4_Y$8;t0{f>=j%4MAFkC;m zX-@s!n AvUT9x?xc>|l zBoBn;24lXc(S~9T4TPG+{{f&@n@Cp(9pm*k|6Y^*iQ&aZmO^N6im`qO7#)V(g3xnZ zlc>41MqQ_;aFpnn|Jb$@b()>En~g2 ;~#hxVkCr^8G>pTV*t_q z`Sk+(y|TFe_Wz6>WIGSn&8ENS$aMb!pjHR~QWd~} 3IHq z`tR9}z#jy3KokKLa*qOtO44wUBY&Upds6Wy!7Yk^2srr2Z$9VuaQ!D3NclGyjEv j0*0<@qe%^$$e$SQu$n==;p!5H 9CBQ$d+XIRP$4RM3R|Zd~+#Y)!(5j1l0uu_k1b00@JX71_)}jocL= e{Oj430O-~lE6V6RkxyxXNMS+pTk+i2JNysXqU(MD diff --git a/com.xored.f4.builder/f4builder.pod b/com.xored.f4.builder/f4builder.pod index f0df2b477264b97db0866a78ff8d4b96e7f4ca47..c2360cf65b5d53572961902e35ab40d51a1fb5b7 100644 GIT binary patch delta 47992 zcmZU)V~}pk)+JoFZQI5!+qKKKZR;u9wry*dZF84x+kMV`zmBi__KL{JwQ~I%nK|c} zBgSkV2J`O)gIACN1w#XZf`S6F$jnKA#|5ay%h?ALA%)(2!`#F`=x(mqa=FCW+COhA zV5y_@u=KTle{`0ALE(82Pz-i?P+CvI*C+Uqm5>|kVY-6DO=zwxku`xKszG2JD==Iu zavh{Vd_vBCPBdXvI6!SEK9~#_Uz%36kPrvP9BuQE8FS6h1;GRf9+Bd9ASgr+NC94X z # zXr_bnv}C6GE3PFVLh*rMAW%SmmA>CUI?W--o+jQQBUpokW?g;@^jr(=aaP8 c8zjJa zrpkAt#Z}^#3-29t?VHG#v}P-qT#d9|2B&29hXw%|W-*|YlU?z20$1_}mzj}_MO68b zU2Z5b7S4O oC;!saE@52(!`# zLjMdeiMD#^%ACWGQelC?zHDR~+Dw&^ptL6D8b6maM0dIyc=IHB?tszffUX5(sc96- z2h!Uzi3+4?md5(PixO(zFQkeZOSb!Z+YixMy+u)koVg7D80nkG)5}{AK%ESrQ%-`4 ztcmHNjKJ584HhImgDl?}E5r|jT;-klk^ehrLg4-MCy3)R3l#^IanahFT6O0HS5tlR zP5=u3zI9s!S#wX?SRi1V?bBtKTv@<{#a=XoX+qR>Q!~+HE=_481->}oWLRaLRY_|! z5UKe@R+-M#RZh;k;sb3K5NEE*yVx`(zIu7&hyOqXpi)a!;=bFb8b5`Ga4c4IGOjJh znF@{b28ZF3qf}81Qb4Ba`9w4zQQ->LsjSBzH#j27#Pc9@5C@M-k!Y5poz0C4xTGm6 zoa}5{2 N}eYe6D8y`0FC0t9gy!gZ`FP;}k^*u$O-CM*Y1Tv(Lf zEk27#%4wp0?$S5co1pV8iG@7xaRsNxvW>}^BeYCOH}#8OIN
JB3h$ zX<`!*?(;(QTT_byFk|Ig$>(s`_x9a~X?;D}g=$aogtS%E7n0d}kUtw_9E4HV;3>pB z1xoqvr_S0DyL4kz0cc<9YrKQCB>AWs_=(G5eVyYlQK)g<;ip|t1!kgGYlr%6_QRgT zE2Zf(43DlfFxa=FZot0nbGe`!Di{egsPy|he7cT?5eSk2Th1C3vD2w7JeBGovl$Yu z>}zQ=6KEHu_^Dz*)>{UoI)&?ajNmDWxE{MZnO3ls)SSe#*ACMY$etCazLlF9Q?^Vw zhjUU4Vo-WqUKPT#kdQd z{ukUoZ4Ip>P zum1_8@1IHrId>gqRk||XH{T{Qf?RUhvtyaHxZd{!q@Qm7?eKmDM|TKSk?CSa_|87? z07XGoB1uJYOVOeA1{(;kuc^C+J)a+1RzBx=Ha*t9C0MlMGKvX9gb}6@GCDw86Ni2l z^~!2my3iAfFF?goTd{`^@56+caAf->=R UON{&pb YVVsiU_nYXpg%-#(2 zHdG+E`8%8YsKXEhUK#<-7|gbaIDsJqa9m=cv*bsKTr!Iov=u^J9o>cJ(Nloiz02 zuIQ98P-86e+%^4iEf#)k{UCJ4$wd`-M3rs|aL+9{Ev|NtsplZSXf}ssjYrOGZZ*g! ze%9LUH#ZhQn=kuNG+N3fOx~#~nv+ikwI6;})(S%-O0t}Vt2SJXvR6OUSli*(o!ib< zv3>!bR08&*Rg;;^9GUZy5+dmE)e`8$llc}RkDxZ;w&an@wx;>0$lf9S(jp`R2|YOg zFwyz-X>05Ixyg~67UFt!p@IicGF4W%*|icOsA=hM@7v9 ADIS!U^j+G&t}VLc>@c;)8D}@ zIx%dYiQRNJg%E}z5CnzvgK(-EbAbT{5NTJO$aT>+zA9i3_ w%@+iWMf|>Xf z*7RURC?`Fii^=Bgti(hqmKHh=njsnSdi!?q{FdT?kRO;J1}%L1og4&?8uh0@d^_nu zlJTpZZ?bUd4d@++_zJF*$Aq=&mi4X-UDTq)7+rFO&^!B2LraKHCp+(S5W{ByV2T$Z z$XaU^at(e<5@@W~8h9HyU=2R9v3_`986DUZNr1Fp+8;L_msg)Y7d~Kl=mc~5{I3O8 zE6(T8RpSDAW&PN%1VIes@K-{D dzuGuA+ww$ji1pNci)5 &hLP#vRn^&@Qo3_qwUa1+oY;X2L*|ZGUl ^>6m{gd zW|unmmG9e5!jCZ(>@xiG_xPXE0fDddNX2NA-pgq5g+a%-{2ZcNmOBZ$fLOa~qFI)8 zB&r)XBdg1sd&>)GaGOj+gJhCFx@$% RGmxn>4Q^dm$fU?__YAx%D?T2sio|y>gi24c1
uxRPx 4gf)(6U03UhR=}a2J61dFk zydJp_!jiJ&13m09E*v?(xT-6Ib)={-FkyQ<(6J5Z9r>?4 z=LDfvaLz`G$k!XP0ybZJ* zorq~~S^256cc22G0hW0liG113maI*wD^ZX`%_8V#%Bi&%XN7n00>jQJ&oS*z%mq#m zEwLji@2YsLRT0T_I_4bQ>aKqheWygMC%rMEY)K)K*G3Iiz=`lKUjn1NR;(%cP6CBe zI14>~4yL~tF5%Yu|7Q7d;P&jFhmt~>6=o73iUj-k_%iyX1Ev^O;{4eNvLES%v%yk0 zi)8Tie@+M-&MnRJh&5%}eB_dI|5Cuf-3k^q4qEP+U-jp^b8zwVb6LOW rt^x7Qkv%rf+xhPrxXi(NczKl7thIueU{@`*dzw7OcWXTsd_#9-h02Ck;uA z)LB%P@HBnb6aGGYP}4hsIt}~g##lj&kXzmfsSK5j!&dKBNbjK(e%~aAl+5&XInaQh zqMPSJ(@txE$Rtc&f2>MMd*wTxN~KkJR`k9Lwt_;oC16$A{L*lM8IqJ5NviM%idp72 zvzc<9vcKoFRG+V|*D7@l1>xRUd*|!4T)-B|$~zBw_HE|*Wy&-N1_qLerC8^-hM`&_ z6mka&G?GNsM1T^zz2yf;%L?LSSr~!6Kv+zv*%=Ck7pV3K&8y}e_dS=*5e)9v9!1IL z^vd&d5a73aISgT{MKs5w$g647={b_0F3i~=K0;+9(aU5RaUKj^IJEm3tx6ZIK$049 z0C39QfY&QMxRMjS+u(Y#bQ4#oo62YetKv~6g-v1EJ{(VpQs5&R6Ha7r-t61#NBvCA z+RxgG<7OA#I>QCWv+rn8a&A}eLy9@4R2$X`F@S3O!63{LXHJp4t~YhT|4bcUo){R5 zG2yq&g~qWuR(}O&2?nXh*5nLn_ZL4$ES`p6WrYAVJ`r-T<}6>_>4o;^PCl@N zKH#=w#+h63O@huk1r&WmZFEcwgg4l1_XojG#Up7ACYtR@gp3BhNeTl>lZ&h)@^ydZ z-i*LVA|)#3W~M}q#1gcxNS K2?DA>j+IW0)2%)BeJZ*k1aK=WMp9DNM)6|W!p_=r ojaF=*--} zX@3H*r+CzVN$f4)uesX#o!<3;ms;$arVLROl=5Qsh-zUAm@s*jc-fRH*6BfvcKu>? ziD8UaiP=Y`>fV)ZAA$ug?xCG%2Y`(AF-b5?81i~*Z%L>EmM8DfXZv^$oFY_uQpI1N z_eQdR+FQCev@NbKm0I$=Wz5)azOw7ST^!pPIqfH;8eI&!a+(&4^x6PEx2=C9z@Pc= zN9c}&C1~r6&g@?zZPpW--Fi|!!`f)4V3;`(F|6M+dJKyh?Q2E{-*Oju0KtwMTP{j!Xqw6EanQHRL!dy3{S{1X7-nlJV4sAGs-Iw2ML1S?Q97wa3y! zn#?fp#fbH5WcZ3N!xu|`;hD*|2F~=%Mu <}y(n82EvmM4UEg^V{>sw`= zC&Kiy`%&KsIK1_-#Dv0$1F#!)fU3{CG&4v9KS%-6%UwpmHoKJPxeBY4(iQoh__Ie~ zQOu(o6JcZOirpE#wVly^SQaiMhd4O#V@ac0PtN0CL&3R@;?B>lTTL5`h@IxXAIpl~ z4u3D4u!%AC pEIt z=OSdWxhokk?kcq5OYodKa5N* giutsiTCalSPGl%M$!S%-vq?$# zHLYbHVB%E9=}D0Jg%8*_*x?3=ht~d)PGLXi{?ha8+cFU;$%`@A1bjDd3G=Wkp%Zq& zkIxv-0lnm*_Xl@dZ=gJ`dc+y8t> p95%9Ia|p^(`%xoisuSW%ke5TWv|SbHa}j`^qE9I5eZ z%i=9dG=Jy>SH1<^))$pxwNs|WO5KK4ozuL+Y~fpMR(I3&Pb&{7eO1i1X4zJls$1Oq zbD!Tv$ezC!U?LBczKr=X<9*5~!Z_dh&5|Kb>u=y0;|u<0V=mTc1K!Z<A%f{Qi#= z$^D779)|LwUe_a~HQG3s3A&G?Di%qZ30 A}u*MNX#C{Jy!iYW8b^4|je~^*)UW!-P{cpjCCKlewl_|F~qI3Lb1s|8F?9 zovrh0X2}T@H?ul($66T+M4qput}zL ZK(7+*fqinkfLpsl zGmb#nQxVmq%y+eOwATZze`yWB`9d_tD08{W( oInapQHRHI+4~~&x3mO$+k9ol z4IHL8psix+N$lG#9^W_MBEIU5L%*W<^BI)9^5%_&ti8B0@1}Y8u1!O!)rMioQP8~N zPxt0lK?-8Clc#=SPt4LP-Wi{=o`PYd7@cGARE`szk8g3P+kFA%5)wR{m4dklTTWu< z1TB++mV!~r(9OnH5FZVp8)RMF3sOE m` zv$JJPfWo^tZzjxez*$3Y5>$U;b)SV@4XS4(>$38 7OGG2W@z9! zAo6-4p5zA_d$}!-l1OGP(^>m{j1 z+ovYd*ocxRXz$mLGTH9{>GR7fzuaFtFbPbwA-HRL$p?BacplZ81X{GryFETJeG%nW zY-m<<-R}XuOME=P_ ;;2j5 zzUWO+@oE?b9?1O 8%?I!VI`o }QL^(%jtfm!j`C{+>lAfZ?Sl){!sqj&qkmas^5LF}};ps+qqnG8b>C?ZFZ zh~k>cNhv5WDvKH^(uQCUOI7TdcS!f>td?3`ORi G&6| zhwkVLGoe0a>N5dazx(WeleFd~LaCoyKEcTx2LNV)`G5bUEz^d7qH1{J-@>0hQRqCj zVwzOnO7$s^SQEnJx&y#o8_0kn(mM>NHP1x*&%zciYf9;s_={%(H_Sty%+q~lk{FlL z*xs~!jhXU5AMk%Czw0r5egG8Z4>|?whY9nmAeMd`!AZP%27;br_UUK5`M<*_2}BnQ z7_RGIGS0Yy(GxY(qaH|Vd1=PoLnukc9X%F|BAb@v3PSNI{w^FBotCm{u1&-PjVoC` zZ2aTl%%3MCZglE2Pmfn~;{U+AHyiJa1`o|j#{^o2Us4BIO-Ny y1@6BH1MMQ zQts5Xn?=`)(EL3wZc34Me$z|8v7Jhm33tlVXO8OT3hZXg2f2oL$ 47kbWoDc&-=hAUP0nidsB*G}}!YR5-2= M_SbImKDC_EsZCTtFtPsz;%cgZr^dW?LNVL|Dgi%R0J9C%TougTa*R4OUBE~nh zD_J<%T_OaU1^teWe@DM{@9PEnNeI_FmJs72b~laGBe#?)`J 5T|1*5HweTAmM#0cPTDpMJMz9(wNPK<0LLDIHj zB$9OZ iq 4SA-8uV3p zh+^BV#S_3T9RA|bJNqcx_fR?RWoOJB0ckU)^;@E7S1f4)#&r Yk1y%<`#X+}$ZC zEn7n|x9z+28#oCt{B{6xGab`B6JgL>!-u?zKtI4b*{T@#R+FV~u