eclipse-jdt / eclipse.jdt.core

Eclipse Public License 2.0
162 stars 131 forks source link

API for faster UI bulk Operations #1614

Closed jukzi closed 10 months ago

jukzi commented 11 months ago

Currently performing UI Operations like a "fast" type hierarchy can be kinda slow. By example for java.lang.Object. Sampling i see most time is spend on opening binary JARs. Debugging that i see the same Zip files are opened again and again. JDT CORE already has an internal mechanism to prevent that: org.eclipse.jdt.internal.core.JavaModelManager.cacheZipFiles(Object) . However there is no way to use that in jdt.ui. Example stacktrace: image The loop is in the UI and there is not CORE code around it - only jdt ui code. As a proof of concept i added the internal cacheZipFiles() call around org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControl.setInput(Object) and org.eclipse.jdt.internal.core.SearchableEnvironment.SearchableEnvironment(JavaProject, ICompilationUnit[], boolean) Which results in the time spend in org.eclipse.jdt.internal.core.JavaModelManager.getZipFile () is reduced from 48seconds to 40ms - more then thousand times faster! (SWT then becomes the bottle neck) Any objection that JDT provides public API like org.eclipse.jdt.core.IJavaModel.runBatch(Runnable) ? Other suggestions for the naming? @iloveeclipse @jjohnstn

iloveeclipse commented 11 months ago

In general makes sense.

I would not add anything to IJavaModel for consistency with existing code in JavaCore. Please take a look at:

I assume you might want either update that run() above or (if that would be too dangerous/too complicated) add something like below

which has no need to have any "workspace" relationship.

jukzi commented 11 months ago

@iloveeclipse thanks for feedback. The problem with JavaCore.run is that this does tons of other things because it is meant to be used for write operation. How about JavaCore.runRead(Runnable)?

jukzi commented 11 months ago

or JavaCore.call(JavaCallable) like org.eclipse.swt.widgets.Display.syncCall(SwtCallable<T, E>)

jukzi commented 11 months ago

the call hierarchy view is a bit more complicated: i don't see jdt.ui in the callstack around the sort:

java.lang.Exception: Warning: Zipfile was opened just 1ms ago in same thread Thread[main,6,main], consider caching: C:/Users/jkubitz/egit-2023-08-16/ws/.metadata/.plugins/org.eclipse.pde.core/.bundle_pool/plugins/org.eclipse.osgi_3.18.400.v20230509-2241.jar
    at org.eclipse.jdt.internal.core.JavaModelManager.getZipFile(JavaModelManager.java:2974)
    at org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory.rawReadTypeTestForExists(BinaryTypeFactory.java:144)
    at org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory.rawReadType(BinaryTypeFactory.java:120)
    at org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory.readType(BinaryTypeFactory.java:115)
    at org.eclipse.jdt.internal.core.ClassFile.getJarBinaryTypeInfo(ClassFile.java:246)
    at org.eclipse.jdt.internal.core.ClassFile.getBinaryTypeInfo(ClassFile.java:201)
    at org.eclipse.jdt.internal.core.ClassFile.buildStructure(ClassFile.java:92)
    at org.eclipse.jdt.internal.core.Openable.generateInfos(Openable.java:266)
    at org.eclipse.jdt.internal.core.SourceRefElement.generateInfos(SourceRefElement.java:113)
    at org.eclipse.jdt.internal.core.JavaElement.openWhenClosed(JavaElement.java:572)
    at org.eclipse.jdt.internal.core.BinaryType.getElementInfo(BinaryType.java:288)
    at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:289)
    at org.eclipse.jdt.internal.core.BinaryType.isAnonymous(BinaryType.java:737)
    at org.eclipse.jdt.internal.core.manipulation.JavaElementLabelComposerCore.appendTypeLabel(JavaElementLabelComposerCore.java:916)
    at org.eclipse.jdt.internal.core.manipulation.JavaElementLabelComposerCore.appendTypeLabel(JavaElementLabelComposerCore.java:890)
    at org.eclipse.jdt.internal.core.manipulation.JavaElementLabelComposerCore.appendTypeLabel(JavaElementLabelComposerCore.java:890)
    at org.eclipse.jdt.internal.core.manipulation.JavaElementLabelComposerCore.appendMethodLabel(JavaElementLabelComposerCore.java:458)
    at org.eclipse.jdt.internal.core.manipulation.JavaElementLabelComposerCore.appendElementLabel(JavaElementLabelComposerCore.java:174)
    at org.eclipse.jdt.ui.JavaElementLabels.getElementLabel(JavaElementLabels.java:532)
    at org.eclipse.jdt.ui.JavaElementLabels.getElementLabel(JavaElementLabels.java:505)
    at org.eclipse.jdt.ui.JavaElementLabels.getTextLabel(JavaElementLabels.java:409)
    at org.eclipse.jdt.internal.ui.viewsupport.JavaUILabelProvider.getText(JavaUILabelProvider.java:167)
    at org.eclipse.jdt.internal.ui.callhierarchy.CallHierarchyLabelProvider.getElementLabel(CallHierarchyLabelProvider.java:188)
    at org.eclipse.jdt.internal.ui.callhierarchy.CallHierarchyLabelProvider.getStyledText(CallHierarchyLabelProvider.java:106)
    at org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.getStyledText(DelegatingStyledCellLabelProvider.java:209)
    at org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider.getStyledText(DecoratingStyledCellLabelProvider.java:197)
    at org.eclipse.jdt.internal.ui.viewsupport.ColoringLabelProvider.getText(ColoringLabelProvider.java:91)
    at org.eclipse.jface.viewers.ViewerComparator.getLabel(ViewerComparator.java:161)
    at org.eclipse.jface.viewers.ViewerComparator.compare(ViewerComparator.java:137)
    at org.eclipse.jface.viewers.ViewerComparator.lambda$0(ViewerComparator.java:206)
    at java.base/java.util.TimSort.binarySort(TimSort.java:296)
    at java.base/java.util.TimSort.sort(TimSort.java:239)
    at java.base/java.util.Arrays.sort(Arrays.java:1233)
    at org.eclipse.jface.viewers.ViewerComparator.sort(ViewerComparator.java:206)
    at org.eclipse.jface.viewers.AbstractTreeViewer.internalAdd(AbstractTreeViewer.java:290)
    at org.eclipse.jface.viewers.TreeViewer.internalAdd(TreeViewer.java:634)
    at org.eclipse.jface.viewers.AbstractTreeViewer.add(AbstractTreeViewer.java:171)
    at org.eclipse.ui.progress.DeferredTreeContentManager$3.runInUIThread(DeferredTreeContentManager.java:312)
    at org.eclipse.ui.progress.UIJob.lambda$0(UIJob.java:148)
jukzi commented 11 months ago

the first time JDTs callstack is more usefull - CallHierarchyViewer could be overloaded:

java.util.Arrays.sort(T[], java.util.Comparator<? super T>) line: 1233  
org.eclipse.jdt.internal.ui.callhierarchy.CallHierarchyViewPart$4(org.eclipse.jface.viewers.ViewerComparator).sort(org.eclipse.jface.viewers.Viewer, java.lang.Object[]) line: 206  
org.eclipse.jdt.internal.ui.callhierarchy.CallHierarchyViewer(org.eclipse.jface.viewers.AbstractTreeViewer).internalAdd(org.eclipse.swt.widgets.Widget, java.lang.Object, java.lang.Object[]) line: 290 
org.eclipse.jdt.internal.ui.callhierarchy.CallHierarchyViewer(org.eclipse.jface.viewers.TreeViewer).internalAdd(org.eclipse.swt.widgets.Widget, java.lang.Object, java.lang.Object[]) line: 634 
org.eclipse.jdt.internal.ui.callhierarchy.CallHierarchyViewer(org.eclipse.jface.viewers.AbstractTreeViewer).add(java.lang.Object, java.lang.Object...) line: 171    
org.eclipse.ui.progress.DeferredTreeContentManager$3.runInUIThread(org.eclipse.core.runtime.IProgressMonitor) line: 312 
HannesWell commented 11 months ago

Could this also help to speed up PDE's search for unused dependency (in the MANIFEST-Editor's dependency tab)? I recently cleaned up quite a few plugins and since this was very slow I did a quick sampling and IIRC creating/reading some Java index (which also involved a lot of reading from the disk) took by far the most time.

jukzi commented 11 months ago

Could this also help to speed up PDE's search for unused dependency (in the MANIFEST-Editor's dependency tab)?

May be, please try it out. It should be pretty simple to just add JavaCore.run() in the outermost PDE Class in the stacktrace of the problem

trancexpress commented 10 months ago

The first time I try to open a class with an F3, after I restart Eclipse, the editor cannot open due to this exception stack trace:

java.lang.IllegalStateException: Its not allow to modify JavaModel during ReadOnly action.
    at org.eclipse.jdt.internal.core.JavaModelManager.assertModelModifiable(JavaModelManager.java:5786)
    at org.eclipse.jdt.internal.core.DeltaProcessor.registerJavaModelDelta(DeltaProcessor.java:2037)
    at org.eclipse.jdt.internal.core.JavaModelOperation.addDelta(JavaModelOperation.java:176)
    at org.eclipse.jdt.internal.core.BecomeWorkingCopyOperation.executeOperation(BecomeWorkingCopyOperation.java:57)
    at org.eclipse.jdt.internal.core.JavaModelOperation.run(JavaModelOperation.java:742)
    at org.eclipse.jdt.internal.core.JavaModelOperation.runOperation(JavaModelOperation.java:808)
    at org.eclipse.jdt.internal.core.CompilationUnit.becomeWorkingCopy(CompilationUnit.java:98)
    at org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitDocumentProvider.createFileInfo(CompilationUnitDocumentProvider.java:1015)
    at org.eclipse.ui.editors.text.TextFileDocumentProvider.connect(TextFileDocumentProvider.java:481)
    at org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitDocumentProvider.connect(CompilationUnitDocumentProvider.java:1253)
    at org.eclipse.ui.texteditor.AbstractTextEditor.doSetInput(AbstractTextEditor.java:4186)
    at org.eclipse.ui.texteditor.StatusTextEditor.doSetInput(StatusTextEditor.java:262)
    at org.eclipse.ui.texteditor.AbstractDecoratedTextEditor.doSetInput(AbstractDecoratedTextEditor.java:1474)
    at org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.internalDoSetInput(JavaEditor.java:2563)
    at org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.doSetInput(JavaEditor.java:2536)
    at org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor.doSetInput(CompilationUnitEditor.java:1415)
    at org.eclipse.ui.texteditor.AbstractTextEditor.lambda$1(AbstractTextEditor.java:3171)
    at org.eclipse.jface.operation.ModalContext.runInCurrentThread(ModalContext.java:434)
    at org.eclipse.jface.operation.ModalContext.run(ModalContext.java:354)
    at org.eclipse.ui.internal.WorkbenchWindow.lambda$7(WorkbenchWindow.java:2335)
    at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:67)
    at org.eclipse.ui.internal.WorkbenchWindow.run(WorkbenchWindow.java:2332)
    at org.eclipse.ui.texteditor.AbstractTextEditor.internalInit(AbstractTextEditor.java:3188)
    at org.eclipse.ui.texteditor.AbstractTextEditor.init(AbstractTextEditor.java:3213)
    at org.eclipse.ui.internal.EditorReference.initialize(EditorReference.java:353)
    at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart.create(CompatibilityPart.java:344)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58)
    at org.eclipse.e4.core.internal.di.InjectorImpl.processAnnotated(InjectorImpl.java:976)
    at org.eclipse.e4.core.internal.di.InjectorImpl.processAnnotated(InjectorImpl.java:938)
    at org.eclipse.e4.core.internal.di.InjectorImpl.internalInject(InjectorImpl.java:138)
    at org.eclipse.e4.core.internal.di.InjectorImpl.internalMake(InjectorImpl.java:385)
    at org.eclipse.e4.core.internal.di.InjectorImpl.make(InjectorImpl.java:311)
    at org.eclipse.e4.core.contexts.ContextInjectionFactory.make(ContextInjectionFactory.java:203)
    at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.createFromBundle(ReflectionContributionFactory.java:91)
    at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.doCreate(ReflectionContributionFactory.java:60)
    at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.create(ReflectionContributionFactory.java:42)
    at org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer.createWidget(ContributedPartRenderer.java:132)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createWidget(PartRenderingEngine.java:992)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:659)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:763)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:728)
    at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:712)
    at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.createElement(PartServiceImpl.java:1314)
    at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.showPart(PartServiceImpl.java:1275)
    at org.eclipse.ui.internal.WorkbenchPartReference.getPart(WorkbenchPartReference.java:394)
    at org.eclipse.ui.internal.EditorReference.getEditor(EditorReference.java:283)
    at org.eclipse.ui.internal.WorkbenchPage.busyOpenEditor(WorkbenchPage.java:3182)
    at org.eclipse.ui.internal.WorkbenchPage.lambda$11(WorkbenchPage.java:3118)
    at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:67)
    at org.eclipse.ui.internal.WorkbenchPage.openEditor(WorkbenchPage.java:3116)
    at org.eclipse.ui.internal.WorkbenchPage.openEditor(WorkbenchPage.java:3086)
    at org.eclipse.ui.internal.WorkbenchPage.openEditor(WorkbenchPage.java:3077)
    at org.eclipse.jdt.internal.ui.javaeditor.EditorUtility.openInEditor(EditorUtility.java:380)
    at org.eclipse.jdt.internal.ui.javaeditor.EditorUtility.openInEditor(EditorUtility.java:184)
    at org.eclipse.jdt.ui.actions.OpenAction.run(OpenAction.java:289)
    at org.eclipse.jdt.ui.actions.OpenAction.run(OpenAction.java:183)
    at org.eclipse.jdt.ui.actions.SelectionDispatchAction.dispatchRun(SelectionDispatchAction.java:280)
    at org.eclipse.jdt.ui.actions.SelectionDispatchAction.lambda$0(SelectionDispatchAction.java:254)
    at org.eclipse.jdt.core.JavaCore.lambda$0(JavaCore.java:6032)
    at org.eclipse.jdt.internal.core.JavaModelManager.cacheZipFiles(JavaModelManager.java:5821)
    at org.eclipse.jdt.internal.core.JavaModelManager.callReadOnlyUnchecked(JavaModelManager.java:5809)
    at org.eclipse.jdt.internal.core.JavaModelManager.callReadOnly(JavaModelManager.java:5797)
    at org.eclipse.jdt.core.JavaCore.callReadOnly(JavaCore.java:6016)
    at org.eclipse.jdt.core.JavaCore.runReadOnly(JavaCore.java:6031)
    at org.eclipse.jdt.ui.actions.SelectionDispatchAction.run(SelectionDispatchAction.java:254)
    at org.eclipse.jface.action.Action.runWithEvent(Action.java:474)
    at org.eclipse.jface.commands.ActionHandler.execute(ActionHandler.java:121)
    at org.eclipse.ui.internal.handlers.E4HandlerProxy.execute(E4HandlerProxy.java:98)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58)
    at org.eclipse.e4.core.internal.di.InjectorImpl.invokeUsingClass(InjectorImpl.java:298)
    at org.eclipse.e4.core.internal.di.InjectorImpl.invoke(InjectorImpl.java:232)
    at org.eclipse.e4.core.contexts.ContextInjectionFactory.invoke(ContextInjectionFactory.java:174)
    at org.eclipse.e4.core.commands.internal.HandlerServiceHandler.execute(HandlerServiceHandler.java:165)
    at org.eclipse.core.commands.Command.executeWithChecks(Command.java:488)
    at org.eclipse.core.commands.ParameterizedCommand.executeWithChecks(ParameterizedCommand.java:485)
    at org.eclipse.e4.core.commands.internal.HandlerServiceImpl.executeHandler(HandlerServiceImpl.java:204)
    at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher.executeCommand(KeyBindingDispatcher.java:308)
    at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher.press(KeyBindingDispatcher.java:569)
    at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher.processKeyEvent(KeyBindingDispatcher.java:644)
    at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher.filterKeySequenceBindings(KeyBindingDispatcher.java:439)
    at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher$KeyDownFilter.handleEvent(KeyBindingDispatcher.java:96)
    at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:89)
    at org.eclipse.swt.widgets.Display.filterEvent(Display.java:1953)
    at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1528)
    at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1555)
    at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1538)
    at org.eclipse.swt.widgets.Widget.sendKeyEvent(Widget.java:1577)
    at org.eclipse.swt.widgets.Widget.gtk_key_press_event(Widget.java:937)
    at org.eclipse.swt.widgets.Control.gtk_key_press_event(Control.java:4049)
    at org.eclipse.swt.widgets.Composite.gtk_key_press_event(Composite.java:931)
    at org.eclipse.swt.widgets.Widget.windowProc(Widget.java:2507)
    at org.eclipse.swt.widgets.Control.windowProc(Control.java:6883)
    at org.eclipse.swt.widgets.Display.windowProc(Display.java:6162)
    at org.eclipse.swt.internal.gtk3.GTK3.gtk_main_do_event(Native Method)
    at org.eclipse.swt.widgets.Display.eventProc(Display.java:1597)
    at org.eclipse.swt.internal.gtk3.GTK3.gtk_main_iteration_do(Native Method)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4513)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1152)
    at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1043)
    at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:152)
    at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:648)
    at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
    at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:555)
    at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
    at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:152)
    at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:208)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:136)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:402)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:255)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:651)
    at org.eclipse.equinox.launcher.Main.basicRun(Main.java:588)
    at org.eclipse.equinox.launcher.Main.run(Main.java:1459)
    at org.eclipse.equinox.launcher.Main.main(Main.java:1432)

I tried to open TestCaseElement from the body of org.eclipse.jdt.internal.junit.ui.TestViewer.getOpenTestAction(TestSuiteElement):

    private OpenTestAction getOpenTestAction(TestSuiteElement testSuite) {
        String testName= testSuite.getTestName();
        ITestElement[] children= testSuite.getChildren();

        if (children.length > 0 && children[0] instanceof TestCaseElement tce && tce.isDynamicTest()) {
            // a group of parameterized tests
            return new OpenTestAction(fTestRunnerPart, (TestCaseElement) children[0], null);
        }
I selected this one -> * TestCaseElement * child= testSuite.getSingleDynamicChild();
        if (child != null) {
            // a parameterized test that ran only one test
            return new OpenTestAction(fTestRunnerPart, child, null);
        }

        int index= testName.indexOf('(');
        // test factory method
        if (index > 0) {
            return new OpenTestAction(fTestRunnerPart, testSuite.getSuiteTypeName(), testName.substring(0, index), testSuite.getParameterTypes(), true);
        }

        // regular test class
        return new OpenTestAction(fTestRunnerPart, testName);
    }

The editor with TestViewer was already open prior to restarting, maybe that makes a difference - I don't know.

My Eclipse SDK is:

Eclipse SDK
Version: 2024-03 (4.31)
Build id: I20231212-1800

My workspace is very old, I have been re-using it for years now. I don't know if that makes a difference either.

@jukzi sorry I've not been follwing the JDT tracker. If there is an issue for this bug, sorry for the noise.

jukzi commented 10 months ago

already fixed, please update