dubreuia/intellij-plugin-save-actions

Run editor action on save

quilicicf opened this issue ยท 74 comments

Hi,

I was wondering if it could be possible to add an option to run an external tool on the file on save.

My use-case

I've got a markdown that I edit and a script that parses it to generate the ToC.
I'd like to run the script each time I save the file.

I'm not exactly sure file watchers is what I'm looking for, correct me if I'm wrong but what I gathered from looking at both these plugins was that:

  • File watchers: is to be used when one wants to generate other files from the file that is being edited.
    • Typical use cases:
      • Less files => one CSS file
      • JS files => one bundle file
  • Save actions: is to be used when the file currently being edited should be edited further by a script
    • Typical use cases:
      • Formatting of code
      • Code style updates

When I try to use file watchers, the script execution triggers the file watcher which make a recursive call that crashes IntelliJ.
I was hoping save actions would not get triggered by external modifications like file watchers.

Hey @quilicicf,

This was already asked for similar purposes see #72, and #61 that was actually implemented by jetbrains. I think what you're asking fits the save-actions plugin: "user modifies file -> save-actions automatically modifies it too -> intellij saves on disk"

The problem is that I'm not going to call a random script on disk, this is a security issue (commit a settings file and a script file in a repo that uses save-actions, bam you have remote execution). I don't even know if that would work, because intellij calling a (bash?) script is probably async, so it would probably save before the script ends.

The only solutions I see for now is to implement your TOC generation directly in save-actions (nope) or call another plugin in intellij (better, but not great).

So I don't really have a solution for you, but I'm not against the idea. Probably @markiewb 's solution is your best way to go for now.

PS hey you work at restlet, say hi to jonathan :)

It looks like I'm gonna keep running the external tool by hand with a shortcut then, it's not that bad.

Thanks for taking the time to get into details!

I'm not sure if I should close this ticket or if you want to keep it open for reference, I'll let you guys see to it?

PS: Jonathan says hi too ;-)

I never realized that you could launch an external tool from Intellij... You can probably retrieve the Action with ActionManager.getInstance().getAction("ACTION ID").actionPerformed(event); so the save actions plugin could have a list of custom configurable actions to launch (ids).

I won't have time to implement that anytime soon, but it's definitely possible to do and and you can submit a PR anytime. Check out the https://github.com/dubreuia/intellij-plugin-save-actions#contributing section to get you started.

I'll get a look (not next month) thanks!

Yes, IDEA supports external tools. I guess there is also API to find and invoke the configured external tools.

The current integration of external tools is as follows
image
image

It would be great to be able to run any webstorm action. This would enable you to use the newly added prettier functionality on save.

Or any other IDE actions Ctrl + Shift + A

I'm working on this one right now.

If someone want to test the feature, I've created a release candidate 1.4.0-RC1. You can configure the plugin to execute on save a "quick list", which is a series of editor actions (pretty much anything that can be executed with "CTRL + SHIFT + A").

For example I've configured the plugin to execute a quick list that launches my test suite when I save.

Any feedback welcome.

Save Actions-1.4.0-RC1.zip

I installed the plugin from zip but don't see the options... Am i missing something?

image

My mistake, I'm fixing.

Reminder set to try that on monday, thanks :)

I added the 1.4.0 RC2 plugin to WebStorm, and can configure it to select a quick list, but it doesn't actually seem to run it. I tried both normal Ctrl+S, and the menu items for Save Actions.

I've verified the quick list items do something by manually running them.

This is how my quick list is configured:
screenshot - 2019-01-18 5_25_16 pm

I also tried adding other items with obvious effects to the quick list like "Backspace".

This is how Save Actions is configured:
screenshot - 2019-01-18 5_29_24 pm

Any ideas?

Works on my machine :-D. Can you send me your idea.log? Check the plugin readme if you don't know the location of the file.

PS: the default shortcut is CTRL + SHIFT + S, but in webstorm is binded to copy, try changing, it I'm not sure you are actually triggering the plugin.

And thanks for testing I really appreciate it <3

I don't see anything out of the ordinary in the logs:

2019-01-18 17:36:24,858 [2216225]   INFO - re.component.SaveActionManager - [+] Start SaveActionManager#beforeAllDocumentsSaving 
2019-01-18 17:36:24,858 [2216225]   INFO - re.component.SaveActionManager - Unsaved documents (1): [DocumentImpl[file://D:/dev/xxxxxxxxxxxxx/src/index.js]] 
2019-01-18 17:36:24,859 [2216226]   INFO - re.component.SaveActionManager - Processing Project 'D:\dev\xxxxxxxxxxxx' xxxxxxxxxxxx files [JSFile:index.js] mode normal 
2019-01-18 17:36:24,859 [2216226]   INFO - re.component.SaveActionManager - Filtered files [JSFile:index.js] 
2019-01-18 17:36:24,859 [2216226]   INFO - re.component.SaveActionManager - Start processors (7) 
2019-01-18 17:36:24,859 [2216226]   INFO - re.component.SaveActionManager - Filtered processors [reformat, executeAction] 
2019-01-18 17:36:24,859 [2216226]   INFO - re.component.SaveActionManager - Execute command reformat on 1 files 
2019-01-18 17:36:24,876 [2216243]   INFO - re.component.SaveActionManager - Execute command executeAction on 1 files 
2019-01-18 17:36:24,877 [2216244]   INFO - re.component.SaveActionManager - Exit processors with results [reformat:OK, executeAction:OK] 
2019-01-18 17:36:24,877 [2216244]   INFO - re.component.SaveActionManager - End SaveActionManager#beforeAllDocumentsSaving processed 1 documents 

As you see in the log, I turned on the "reformat" command, and it did do the WebStorm reformat.

Also, I had un-mapped the Save As... keyboard shortcut, so CTRL+SHIFT+S is only triggering this plugin.

I noticed in your code changes:

    executeAction(Action.executeAction,
            (project, psiFiles) -> () -> {
                Map<Integer, QuickList> quickListsIds = Arrays

that psiFiles isn't used anywhere. Maybe this currently only works on whole-project commands? (The two commands I tried, 'Reformat with Prettier' and 'Backspace' are both per-file commands).

2019-01-18 17:36:24,876 [2216243] INFO - re.component.SaveActionManager - Execute command executeAction on 1 files the action is triggered, I don't know why it doesn't work. I'll check it out when I get back home

Good point on the argument psiFile, I think I know how to fix that.

Yeah I just checked I'm on my mobile, I forgot the psi file in the DataContext, I'll fix it this weekend

I can't make it work for now, I'll have to debug WebStorm but I don't know how to do it. I'll check next week.

I've tried debugging WebStorm, I'm stuck on this stack. I'll have to ask the question to JetBrains.

The "execute action" still works, just not on the prettier action.

java.lang.Throwable: Await future on EDT may cause a deadlock
	at com.intellij.openapi.diagnostic.Logger.error(Logger.java:134)
	at com.intellij.lang.javascript.service.JSLanguageServiceUtil.awaitFuture(JSLanguageServiceUtil.java:122)
	at com.intellij.lang.javascript.service.JSLanguageServiceUtil.awaitFuture(JSLanguageServiceUtil.java:104)
	at com.intellij.lang.javascript.service.JSLanguageServiceUtil.awaitFuture(JSLanguageServiceUtil.java:94)
	at com.intellij.lang.javascript.service.JSLanguageServiceUtil.awaitFuture(JSLanguageServiceUtil.java:86)
	at com.intellij.prettierjs.ReformatWithPrettierAction.isDetectedAcceptableFile(ReformatWithPrettierAction.java:398)
	at com.intellij.prettierjs.ReformatWithPrettierAction.isAcceptableFile(ReformatWithPrettierAction.java:366)
	at com.intellij.prettierjs.ReformatWithPrettierAction.lambda$null$3(ReformatWithPrettierAction.java:165)
	at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:973)
	at com.intellij.openapi.application.ReadAction.compute(ReadAction.java:57)
	at com.intellij.prettierjs.ReformatWithPrettierAction.lambda$processFileInEditor$4(ReformatWithPrettierAction.java:164)
	at com.intellij.openapi.progress.impl.CoreProgressManager$1.run(CoreProgressManager.java:220)
	at com.intellij.openapi.progress.impl.CoreProgressManager$TaskRunnable.run(CoreProgressManager.java:727)
	at com.intellij.openapi.progress.impl.CoreProgressManager$5.run(CoreProgressManager.java:442)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:164)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:582)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:532)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:87)
	at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:151)
	at com.intellij.openapi.application.impl.ApplicationImpl.runProcessWithProgressSynchronously(ApplicationImpl.java:571)
	at com.intellij.openapi.progress.impl.CoreProgressManager.runProcessWithProgressSynchronously(CoreProgressManager.java:454)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.runProcessWithProgressSynchronously(ProgressManagerImpl.java:110)
	at com.intellij.openapi.progress.impl.CoreProgressManager.runProcessWithProgressSynchronously(CoreProgressManager.java:216)
	at com.intellij.prettierjs.ReformatWithPrettierAction.processFileInEditor(ReformatWithPrettierAction.java:173)
	at com.intellij.prettierjs.ReformatWithPrettierAction.actionPerformed(ReformatWithPrettierAction.java:138)
	at com.dubreuia.processors.BuildProcessor.lambda$null$4(BuildProcessor.java:95)
	at com.dubreuia.processors.GenericCommand.run(GenericCommand.java:30)
	at com.intellij.openapi.application.RunResult.run(RunResult.java:35)
	at com.intellij.openapi.command.WriteCommandAction.lambda$null$1(WriteCommandAction.java:264)
	at com.intellij.openapi.application.impl.ApplicationImpl.runWriteAction(ApplicationImpl.java:1057)
	at com.intellij.openapi.command.WriteCommandAction.lambda$performWriteCommandAction$2(WriteCommandAction.java:263)
	at com.intellij.openapi.command.WriteCommandAction.lambda$doExecuteCommand$4(WriteCommandAction.java:321)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:220)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:188)
	at com.intellij.openapi.command.WriteCommandAction.doExecuteCommand(WriteCommandAction.java:323)
	at com.intellij.openapi.command.WriteCommandAction.performWriteCommandAction(WriteCommandAction.java:262)
	at com.intellij.openapi.command.WriteCommandAction.execute(WriteCommandAction.java:244)
	at com.dubreuia.core.component.Engine.lambda$processPsiFiles$5(Engine.java:80)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:373)
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
	at com.dubreuia.core.component.Engine.processPsiFiles(Engine.java:81)
	at com.dubreuia.core.component.Engine.processPsiFilesIfNecessary(Engine.java:64)
	at com.dubreuia.core.component.SaveActionManager.processPsiFilesIfNecessary(SaveActionManager.java:144)
	at com.dubreuia.core.component.SaveActionManager.lambda$beforeDocumentsSaving$3(SaveActionManager.java:133)
	at java.util.HashMap.forEach(HashMap.java:1289)
	at com.dubreuia.core.component.SaveActionManager.beforeDocumentsSaving(SaveActionManager.java:132)
	at com.dubreuia.core.component.SaveActionManager.beforeAllDocumentsSaving(SaveActionManager.java:111)
	at sun.reflect.GeneratedMethodAccessor101.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.util.messages.impl.MessageBusConnectionImpl.deliverMessage(MessageBusConnectionImpl.java:117)
	at com.intellij.util.messages.impl.MessageBusImpl.doPumpMessages(MessageBusImpl.java:397)
	at com.intellij.util.messages.impl.MessageBusImpl.pumpWaitingBuses(MessageBusImpl.java:358)
	at com.intellij.util.messages.impl.MessageBusImpl.pumpMessages(MessageBusImpl.java:347)
	at com.intellij.util.messages.impl.MessageBusImpl.sendMessage(MessageBusImpl.java:324)
	at com.intellij.util.messages.impl.MessageBusImpl.access$200(MessageBusImpl.java:29)
	at com.intellij.util.messages.impl.MessageBusImpl$2.invoke(MessageBusImpl.java:196)
	at com.sun.proxy.$Proxy26.beforeAllDocumentsSaving(Unknown Source)
	at sun.reflect.GeneratedMethodAccessor101.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.multiCast(FileDocumentManagerImpl.java:140)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.lambda$new$0(FileDocumentManagerImpl.java:120)
	at com.sun.proxy.$Proxy26.beforeAllDocumentsSaving(Unknown Source)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.saveAllDocuments(FileDocumentManagerImpl.java:291)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.saveAllDocuments(FileDocumentManagerImpl.java:281)
	at com.intellij.openapi.components.impl.stores.StoreUtil.saveDocumentsAndProjectsAndApp(StoreUtil.java:105)
	at com.intellij.openapi.application.impl.ApplicationImpl.saveAll(ApplicationImpl.java:1448)
	at com.intellij.ide.SaveAndSyncHandlerImpl.saveProjectsAndDocuments(SaveAndSyncHandlerImpl.java:126)
	at com.intellij.ide.SaveAndSyncHandlerImpl$2.lambda$onFrameDeactivated$0(SaveAndSyncHandlerImpl.java:94)
	at com.intellij.openapi.application.TransactionGuardImpl.runSyncTransaction(TransactionGuardImpl.java:88)
	at com.intellij.openapi.application.TransactionGuardImpl.lambda$submitTransaction$1(TransactionGuardImpl.java:111)
	at com.intellij.openapi.application.TransactionGuardImpl.submitTransaction(TransactionGuardImpl.java:120)
	at com.intellij.openapi.application.TransactionGuard.submitTransaction(TransactionGuard.java:122)
	at com.intellij.ide.SaveAndSyncHandlerImpl$2.onFrameDeactivated(SaveAndSyncHandlerImpl.java:92)
	at com.intellij.ide.FrameStateManagerImpl.fireDeactivationEvent(FrameStateManagerImpl.java:71)
	at com.intellij.ide.FrameStateManagerImpl.access$300(FrameStateManagerImpl.java:31)
	at com.intellij.ide.FrameStateManagerImpl$2.applicationDeactivated(FrameStateManagerImpl.java:58)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.util.messages.impl.MessageBusConnectionImpl.deliverMessage(MessageBusConnectionImpl.java:117)
	at com.intellij.util.messages.impl.MessageBusImpl.doPumpMessages(MessageBusImpl.java:397)
	at com.intellij.util.messages.impl.MessageBusImpl.pumpWaitingBuses(MessageBusImpl.java:358)
	at com.intellij.util.messages.impl.MessageBusImpl.pumpMessages(MessageBusImpl.java:347)
	at com.intellij.util.messages.impl.MessageBusImpl.sendMessage(MessageBusImpl.java:324)
	at com.intellij.util.messages.impl.MessageBusImpl.access$200(MessageBusImpl.java:29)
	at com.intellij.util.messages.impl.MessageBusImpl$2.invoke(MessageBusImpl.java:196)
	at com.sun.proxy.$Proxy97.applicationDeactivated(Unknown Source)
	at com.intellij.ide.ApplicationActivationStateManager.updateState(ApplicationActivationStateManager.java:80)
	at com.intellij.ide.IdeEventQueue.processAppActivationEvents(IdeEventQueue.java:685)
	at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:557)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:363)
	at java.awt.SentEvent.dispatch(SentEvent.java:70)
	at java.awt.DefaultKeyboardFocusManager$DefaultKeyboardFocusManagerSentEvent.dispatch(DefaultKeyboardFocusManager.java:239)
	at java.awt.DefaultKeyboardFocusManager.sendMessage(DefaultKeyboardFocusManager.java:266)
	at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:784)
	at com.intellij.ide.IdeKeyboardFocusManager.dispatchEvent(IdeKeyboardFocusManager.java:27)
	at java.awt.Component.dispatchEventImpl(Component.java:4760)
	at java.awt.Container.dispatchEventImpl(Container.java:2297)
	at java.awt.Window.dispatchEventImpl(Window.java:2746)
	at java.awt.Component.dispatchEvent(Component.java:4711)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:760)
	at java.awt.EventQueue.access$500(EventQueue.java:97)
	at java.awt.EventQueue$3.run(EventQueue.java:709)
	at java.awt.EventQueue$3.run(EventQueue.java:703)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:84)
	at java.awt.EventQueue$4.run(EventQueue.java:733)
	at java.awt.EventQueue$4.run(EventQueue.java:731)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:730)
	at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:719)
	at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:668)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:363)
	at java.awt.SequencedEvent.dispatch(SequencedEvent.java:128)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
	at java.awt.EventQueue.access$500(EventQueue.java:97)
	at java.awt.EventQueue$3.run(EventQueue.java:709)
	at java.awt.EventQueue$3.run(EventQueue.java:703)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:84)
	at java.awt.EventQueue$4.run(EventQueue.java:733)
	at java.awt.EventQueue$4.run(EventQueue.java:731)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:730)
	at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:719)
	at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:668)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:363)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

That's as far as I got too. I decompiled the JSLanguageServiceUtil.class and this is the bit of code that is throwing the exception:

    @Nullable
    public static <T> T awaitFuture(@Nullable Future<T> future, long timeoutMills, long quotaMills, @Nullable ProgressIndicator indicator, boolean cancelFutureIfTimeout, @Nullable Computable<Boolean> cancelCheck, boolean allowEDT) {
        if (future == null) {
            return null;
        } else {
            if (ApplicationManager.getApplication().isDispatchThread() && !ApplicationManager.getApplication().isUnitTestMode()) {
                if (!allowEDT) {
                    JSLanguageServiceQueue.LOGGER.error("Await future on EDT may cause a deadlock");
                    return null;
                }

                if (timeoutMills > 5L) {
                    JSLanguageServiceQueue.LOGGER.error("Timeout is too long for EDT");
                    return null;
                }
            }
            ...

I tried various ways of launching the activity to get it into various threads, but it made no difference. The Prettier action code appears to be eventually controlling what thread it's doing the two phases of its activity in, and I couldn't see any meaningful difference between the VirtualFile[] code and the single editor code.

The code isDispatchThread returns true if we are in a write thread (which is the case see com.dubreuia.processors.GenericCommand#run which extends com.intellij.openapi.command.WriteCommandAction). Using a read thread might work.

@ngbrown Where did you find the JSLanguageServiceUtil class? Can't find it in the webstorm jars.

It works using a read thread @ngbrown. Thanks a lot for the help. I have to refactor a bit of code but I'll post a new RC when I'm done

It was in a "C:\Users\xxxx\AppData\Local\JetBrains\Toolbox\apps\WebStorm\ch-0\183.5153.33\plugins\JavaScriptLanguage\lib\JavaScriptLanguage.jar and when extracted, .\com\intellij\lang\javascript\service\JSLanguageServiceUtil.class in WebStorm.

I don't know anything about writing IntelliJ plugins, so I probably tried to start the read thread incorrectly when I was attempting to get it to work.

Looking forward to your next version.

I've just checked out RC5. When doing a CTRL+S, WebStorm saves all modified files, however, the Quick List actions are only taking effect in the current tab. That is, Prettier is only reformatting one file, but all files are saved.

You didn't push your most recent code, but I checked the .class file, and it looks like you are passing the EDITOR data instead of the VIRTUAL_FILE_ARRAY. Were you not able to get passing a VirtualFile[] to work?

The code is in the branch add_editor_action (https://github.com/dubreuia/intellij-plugin-save-actions/tree/add_editor_action?files=1)

The prettier action doesn't use the virtual file array. When you run prettier, it runs only on the current file right? I might be able to do it by iterating on the psi files, creating one editor for each element, but it's very specific to the prettier action.

I'm starting to think it would be easier to make a specific action for webstorm.

The commit said 3 days ago so I had assumed no changes. I guess you squashed the new commits.

It's so close to working.

Yes, with your other changes, the VIRTUAL_FILE_ARRAY data works. See ngbrown-forks@f208016.

RE: prettier being it's own checkbox in Save Actions is a good idea. My main complaint with the JetBrains plugin is that it doesn't have the option to format before save, so with the recommended approach of using a file watcher my files end up getting saved twice, and external build watchers all trigger twice.

There is a caveat, it appears that package.json files can be saved without hitting Ctrl+S, and doesn't get picked up consistently by the Save Action plugin:

2019-01-20 20:25:52,664 [1468131]   INFO - Json.PackageJsonUpdateNotifier - processPackageJsonFiles [file://D:/dev/Learning/React/test-prettier-on-save/package.json] 
2019-01-20 20:25:59,666 [1475133]   INFO - re.component.SaveActionManager - [+] Start SaveActionManager#beforeAllDocumentsSaving 
2019-01-20 20:25:59,666 [1475133]   INFO - re.component.SaveActionManager - Unsaved documents (2): [DocumentImpl[file://D:/dev/Learning/React/test-prettier-on-save/src/App.js], DocumentImpl[file://D:/dev/Learning/React/test-prettier-on-save/src/index.js]] 
2019-01-20 20:25:59,666 [1475133]   INFO - re.component.SaveActionManager - Processing Project 'D:\dev\Learning\React\test-prettier-on-save' test-prettier-on-save files [JSFile:App.js, JSFile:index.js] mode normal 
2019-01-20 20:25:59,666 [1475133]   INFO - re.component.SaveActionManager - Filtered files [JSFile:App.js, JSFile:index.js] 
2019-01-20 20:25:59,666 [1475133]   INFO - re.component.SaveActionManager - Start processors (7) 
2019-01-20 20:25:59,666 [1475133]   INFO - re.component.SaveActionManager - Filtered processors [executeAction] 
2019-01-20 20:25:59,666 [1475133]   INFO - re.component.SaveActionManager - Execute command executeAction on 2 files 
2019-01-20 20:25:59,701 [1475168]   INFO - re.component.SaveActionManager - Exit processors with results [executeAction:OK] 
2019-01-20 20:25:59,702 [1475169]   INFO - re.component.SaveActionManager - End SaveActionManager#beforeAllDocumentsSaving processed 2 documents 
2019-01-20 20:26:04,624 [1480091]   INFO - re.component.SaveActionManager - [+] Start SaveActionManager#beforeAllDocumentsSaving 
2019-01-20 20:26:04,624 [1480091]   INFO - re.component.SaveActionManager - Unsaved documents (0): [] 
2019-01-20 20:26:04,624 [1480091]   INFO - re.component.SaveActionManager - End SaveActionManager#beforeAllDocumentsSaving processed 0 documents 

The key is the first line; by the time CTL+S pushed, the package.json file is already saved.

Hi @dubreuia,

I tried the last zip for my use-case by unfortunately, it doesn't work :( .
I may be doing something wrong but I can't find what.

I've created a shell script to generate a ToC on my markdown and created an external tool to run it on the current file with this conf:

  • Program: /bin/bash
  • Arguments: <path to script> $FilePath$
  • Working directory: <whatever>

I've added it in a quick list.

When I run it with the execute action modal it works fine but not when run via the save actions plugin.

The file path is not passed to the script in this case.

The idea.log is useless for my untrained eyes but maybe it'll help you.

2019-01-21 10:10:56,964 [2313722]   INFO - re.component.SaveActionManager - Unsaved documents (1): [DocumentImpl[file:///home/cyp/.../README.md]] 
2019-01-21 10:10:56,964 [2313722]   INFO - re.component.SaveActionManager - Processing Project '/home/cyp/...' <folderName> files [Markdown file] mode normal 
2019-01-21 10:10:56,964 [2313722]   INFO - re.component.SaveActionManager - Filtered files [Markdown file] 
2019-01-21 10:10:56,964 [2313722]   INFO - re.component.SaveActionManager - Start processors (24) 
2019-01-21 10:10:56,964 [2313722]   INFO - re.component.SaveActionManager - Filtered processors [organizeImports, reformat, reformatChangedCode, fieldCanBeFinal, localCanBeFinal, missingOverrideAnnotation, useBlocks, unnecessaryThis, finalPrivateMethod, explicitTypeCanBeDiamond, accessCanBeTightened, executeAction] 
2019-01-21 10:10:56,964 [2313722]   INFO - re.component.SaveActionManager - Execute command organizeImports on 1 files 
2019-01-21 10:10:56,965 [2313723]   INFO - re.component.SaveActionManager - Execute command reformat on 1 files 
2019-01-21 10:10:56,966 [2313724]   INFO - re.component.SaveActionManager - Execute command reformatChangedCode on 1 files 
2019-01-21 10:10:56,966 [2313724]   INFO - re.component.SaveActionManager - Execute command fieldCanBeFinal on 1 files 
2019-01-21 10:10:56,969 [2313727]   INFO - re.component.SaveActionManager - Execute command localCanBeFinal on 1 files 
2019-01-21 10:10:56,971 [2313729]   INFO - re.component.SaveActionManager - Execute command missingOverrideAnnotation on 1 files 
2019-01-21 10:10:56,973 [2313731]   INFO - re.component.SaveActionManager - Execute command useBlocks on 1 files 
2019-01-21 10:10:56,975 [2313733]   INFO - re.component.SaveActionManager - Execute command unnecessaryThis on 1 files 
2019-01-21 10:10:56,977 [2313735]   INFO - re.component.SaveActionManager - Execute command finalPrivateMethod on 1 files 
2019-01-21 10:10:56,980 [2313738]   INFO - re.component.SaveActionManager - Execute command explicitTypeCanBeDiamond on 1 files 
2019-01-21 10:10:56,985 [2313743]   INFO - re.component.SaveActionManager - Execute command accessCanBeTightened on 1 files 
2019-01-21 10:10:56,988 [2313746]   INFO - re.component.SaveActionManager - Execute command executeAction on 1 files 
2019-01-21 10:10:57,099 [2313857]   INFO - re.component.SaveActionManager - Plugin already running, stopping invocation 
2019-01-21 10:10:57,103 [2313861]   INFO - re.component.SaveActionManager - Plugin already running, stopping invocation 
2019-01-21 10:10:57,127 [2313885]  ERROR - .impl.MessageBusConnectionImpl - Argument for @NotNull parameter 'action' of com/intellij/openapi/actionSystem/AnActionEvent.createFromAnAction must not be null 
java.lang.IllegalArgumentException: Argument for @NotNull parameter 'action' of com/intellij/openapi/actionSystem/AnActionEvent.createFromAnAction must not be null
	at com.intellij.openapi.actionSystem.AnActionEvent.$$$reportNull$$$0(AnActionEvent.java)
	at com.intellij.openapi.actionSystem.AnActionEvent.createFromAnAction(AnActionEvent.java)
	at com.dubreuia.processors.BuildProcessor.lambda$null$4(BuildProcessor.java:89)
	at com.dubreuia.processors.SaveReadCommand.execute(SaveReadCommand.java:25)
	at com.dubreuia.core.component.Engine.lambda$processPsiFiles$5(Engine.java:80)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:373)
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1376)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
	at com.dubreuia.core.component.Engine.processPsiFiles(Engine.java:81)
	at com.dubreuia.core.component.Engine.processPsiFilesIfNecessary(Engine.java:64)
	at com.dubreuia.core.component.SaveActionManager.processPsiFilesIfNecessary(SaveActionManager.java:144)
	at com.dubreuia.core.component.SaveActionManager.lambda$beforeDocumentsSaving$3(SaveActionManager.java:133)
	at java.util.HashMap.forEach(HashMap.java:1288)
	at com.dubreuia.core.component.SaveActionManager.beforeDocumentsSaving(SaveActionManager.java:132)
	at com.dubreuia.core.component.SaveActionManager.beforeAllDocumentsSaving(SaveActionManager.java:111)
	at sun.reflect.GeneratedMethodAccessor170.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.util.messages.impl.MessageBusConnectionImpl.deliverMessage(MessageBusConnectionImpl.java:117)
	at com.intellij.util.messages.impl.MessageBusImpl.doPumpMessages(MessageBusImpl.java:397)
	at com.intellij.util.messages.impl.MessageBusImpl.pumpWaitingBuses(MessageBusImpl.java:358)
	at com.intellij.util.messages.impl.MessageBusImpl.pumpMessages(MessageBusImpl.java:347)
	at com.intellij.util.messages.impl.MessageBusImpl.sendMessage(MessageBusImpl.java:324)
	at com.intellij.util.messages.impl.MessageBusImpl.access$200(MessageBusImpl.java:29)
	at com.intellij.util.messages.impl.MessageBusImpl$2.invoke(MessageBusImpl.java:196)
	at com.sun.proxy.$Proxy27.beforeAllDocumentsSaving(Unknown Source)
	at sun.reflect.GeneratedMethodAccessor170.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.multiCast(FileDocumentManagerImpl.java:140)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.lambda$new$0(FileDocumentManagerImpl.java:120)
	at com.sun.proxy.$Proxy27.beforeAllDocumentsSaving(Unknown Source)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.saveAllDocuments(FileDocumentManagerImpl.java:291)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.saveAllDocuments(FileDocumentManagerImpl.java:281)
	at com.intellij.openapi.components.impl.stores.StoreUtil.saveDocumentsAndProjectsAndApp(StoreUtil.java:105)
	at com.intellij.ide.actions.SaveAllAction.actionPerformed(SaveAllAction.java:23)
	at com.intellij.openapi.actionSystem.ex.ActionUtil$1.run(ActionUtil.java:258)
	at com.intellij.openapi.actionSystem.ex.ActionUtil.performActionDumbAware(ActionUtil.java:275)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher$1.performAction(IdeKeyEventDispatcher.java:618)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.lambda$processAction$2(IdeKeyEventDispatcher.java:667)
	at com.intellij.openapi.application.TransactionGuardImpl.performUserActivity(TransactionGuardImpl.java:195)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.processAction(IdeKeyEventDispatcher.java:666)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.processActionOrWaitSecondStroke(IdeKeyEventDispatcher.java:519)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.inInitState(IdeKeyEventDispatcher.java:474)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.dispatchKeyEvent(IdeKeyEventDispatcher.java:211)
	at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:643)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:363)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
2019-01-21 10:10:57,129 [2313887]  ERROR - .impl.MessageBusConnectionImpl - IntelliJ IDEA 2018.3.3  Build #IU-183.5153.38 
2019-01-21 10:10:57,129 [2313887]  ERROR - .impl.MessageBusConnectionImpl - JDK: 1.8.0_152-release; VM: OpenJDK 64-Bit Server VM; Vendor: JetBrains s.r.o 
2019-01-21 10:10:57,130 [2313888]  ERROR - .impl.MessageBusConnectionImpl - OS: Linux 
2019-01-21 10:10:57,139 [2313897]  ERROR - .impl.MessageBusConnectionImpl - Plugin to blame: Save Actions version: 1.4.0-RC5 
2019-01-21 10:10:57,140 [2313898]  ERROR - .impl.MessageBusConnectionImpl - Last Action: SaveAll 

@ngbrown Sometime other plugin change the save cycle (for the package.json file). I'll try making the prettier thing, probably by having a specific section for webstorm.

@quilicicf The action is null (Argument for @NotNull parameter 'action'), which means the action ID in the quick list isn't properly configured. I'll add logging.

Tell me if you need me to test something again (or provide reproduction steps to debug on your side).

My tests are that of the other comments above, It feels like this is so close to being working.

tobia commented

I also tested RC5 for my use case, rustfmt, which is the same thing as Prettier but for Rust code. Actually there are a bunch of these reformatting tools, because they are becoming a sort of standard in many circles, after gofmt for Go code, which was the pioneer IIRC.

I also have the issue that it should be run on all open files, not just the one in front.

In any case, I created the quick list, tested it from the actions menu (it works) and enabled it in Save Actions... but nothing. The quick list does not seem to be run by Save actions. This is what I see in idea.log:

2019-02-22 02:35:09,853 [ 724065]   INFO - re.component.SaveActionManager - [+] Start SaveActionManager#beforeAllDocumentsSaving 
2019-02-22 02:35:09,854 [ 724066]   INFO - re.component.SaveActionManager - Unsaved documents (1): [DocumentImpl[file:///home/tobia/******.rs]] 
2019-02-22 02:35:09,854 [ 724066]   INFO - re.component.SaveActionManager - Processing Project '/home/tobia/******' ****** files [FILE] mode normal 
2019-02-22 02:35:09,858 [ 724070]   INFO - re.component.SaveActionManager - Filtered files [FILE] 
2019-02-22 02:35:09,858 [ 724070]   INFO - re.component.SaveActionManager - Start processors (24) 
2019-02-22 02:35:09,858 [ 724070]   INFO - re.component.SaveActionManager - Filtered processors [executeAction] 
2019-02-22 02:35:09,859 [ 724071]   INFO - re.component.SaveActionManager - Execute command executeAction on 1 files 
2019-02-22 02:35:09,859 [ 724071]   INFO - re.component.SaveActionManager - Exit processors with results [executeAction:OK] 
2019-02-22 02:35:09,859 [ 724071]   INFO - re.component.SaveActionManager - End SaveActionManager#beforeAllDocumentsSaving processed 1 documents 
2019-02-22 02:35:11,282 [ 725494]   INFO - re.component.SaveActionManager - [+] Start SaveActionManager#beforeAllDocumentsSaving 
2019-02-22 02:35:11,283 [ 725495]   INFO - re.component.SaveActionManager - Unsaved documents (0): [] 
2019-02-22 02:35:11,284 [ 725496]   INFO - re.component.SaveActionManager - End SaveActionManager#beforeAllDocumentsSaving processed 0 documents 

Using RC5, For me Reformat with Prettier is working but Fix ESLint Problems is not.

image

image

@tobia Thanks for the test I'll check it out

@blowsie When you say it's not working, do you have a stack? Or it just doesn't do anything?

No error is shown in the event log panel or directly in the IDE,

@blowsie
You can actually get more information from ~/.IntelliJIdea<VERSION>/system/log/idea.log.

Note: make sure your IDE outputs there though (tail -F $filePath). It may be in a file suffixed with a number idea.log.2

When this will be completed, would I be able to use the google-java-format plugin as an action? ๐Ÿค”

Maybe I'll try to test this specific formater

Any updates about this ?

My Log for "Fix Eslint Problems" Action

2019-06-13 15:22:26,534 [  29359]   INFO - tellij.openapi.util.IconLoader - replace '/general/inspectionsError.svg' with '/icons/general/inspectionsError.svg' 
2019-06-13 15:22:42,312 [  45137]   INFO - re.component.SaveActionManager - [+] Start SaveActionManager#beforeAllDocumentsSaving 
2019-06-13 15:22:42,312 [  45137]   INFO - re.component.SaveActionManager - Unsaved documents (1): [DocumentImpl[file://C:/Project/layouts/default.vue]] 
2019-06-13 15:22:42,312 [  45137]   INFO - re.component.SaveActionManager - Processing Project 'C:\Project' project files [HtmlFile:default.vue] mode normal 
2019-06-13 15:22:42,312 [  45137]   INFO - re.component.SaveActionManager - Filtered files [HtmlFile:default.vue] 
2019-06-13 15:22:42,312 [  45137]   INFO - re.component.SaveActionManager - Start processors (7) 
2019-06-13 15:22:42,312 [  45137]   INFO - re.component.SaveActionManager - Filtered processors [executeAction] 
2019-06-13 15:22:42,312 [  45137]   INFO - re.component.SaveActionManager - Execute command executeAction on 1 files 
2019-06-13 15:22:42,312 [  45137]   INFO - re.component.SaveActionManager - Exit processors with results [executeAction:OK] 
2019-06-13 15:22:42,312 [  45137]   INFO - re.component.SaveActionManager - End SaveActionManager#beforeAllDocumentsSaving processed 1 documents 
2019-06-13 15:22:42,444 [  45269]   INFO - rationStore.ComponentStoreImpl - Saving appCachedDictionaryState took 15 ms, FindSettings took 16 ms, ScratchFileService took 16 ms 
2019-06-13 15:22:42,586 [  45411]   INFO - rationStore.ComponentStoreImpl - Saving Project 'C:\Project' projectGit.Settings took 15 ms, ProjectCodeStyleConfiguration took 33 ms, RunManager took 16 ms 
2019-06-13 15:22:42,623 [  45448]   INFO - mponents.impl.stores.StoreUtil - saveProjectsAndApp took 326 ms 

@raydaim I won't have time to implement this in any near future, but PR are welcome.
@blowsie thanks for the log

I did the ground work for it already, you can check the branch "add_editor_action" (might be a bit old but the code is there).

I might release this in next version, even if it doesn't work for all actions. It seems some people are actually using the old old RC5 for prettier to work...

Thanks - I've been using the RC5 for prettier along with a bunch of other people I work with so this would be great ๐Ÿ‘

Thank you for your feedback @davecoates, I have no idea who uses what so it is always nice to know.

I second that, RC5 works great for prettier, just not ESLint

I'm also using the RC5 version while staying away from updates. So it would be great if this feature will be in the next version โค๏ธ

ozum commented

For 1.4 RC5 Reformat with Prettier is working but Fix ESLint Problems is not.

@dubreuia could you please include Fix ESLint Problems in upcoming 1.8.

Many thanks,

@ozum I won't have time to implement "Fix ESLint Problems", PRs welcomed.

ozum commented

@dubreuia if I know java, I would happily PR. Anyway, thanks for this awesome plugin. Please keep eslint in roadmap, if you have time in the future.

Can you guys help me test this version please? I've re-added editor actions in the settings, it looks good and could be released in the next 1.8.0 if it works ok for you. I've changed the order of the quick lists, so you might have to re-choose your quick list in the save action settings page.

Here is the jar: intellij-plugin-save-actions-1.8.0-RC1.zip

(it will work on 2019.x only, if you need another version I'll change the manifest version)

ozum commented

It Works
Plugin: 1.8.0-RC1
WebStorm: 2019.3

What
Quick List:

  • "Reformat with Prettier

What Not

I won't have time to implement "Fix ESLint Problems"

Not expecting this to work, but tried anyway:
Quick List

  • "Fix ESLint Problems"

Works for me in Pycharm 2019.3 ๐Ÿ‘

Ok thank you for testing guys. I'm releasing 1.8.0 this weekend.

I'll close this issue, but please reopen an issue for specific actions that doesn't work (ie @ozum for ESLint).

Confirmed working as a charm in Webstorm 2019.3, using prettier from quick lists

๐Ÿ‘ ๐ŸŽ‰ Thanks all

Released in 1.8.0.

@ozum I created #287

ozum commented

@ozum I created #287

Thanks, I added a summary to that issue for those who don't desire to read this long thread.

Since there can be multiple quicklists defined and there is only a checkbox (experimental) to enable this, how does it know what to run?

Sorry, in my theme the drop down to select the quick list wasn't very noticeable.

image

@rcollette Yes, I was about to tell you about the drop down. I can make it show more, I'm using the light theme which makes it more visible.

The code has been written to execute multiple quick lists but I haven't coded the UI to support that (I don't know if its useful or not).

I can't think of a use case for multiple lists. Thanks for the UX consideration.

ozum commented

@dubreuia maybe you can exchange checkbox with an empty field in list. i.e. when it is empty it means no action.

Hi there,

I use Build actions feature to fix stylelint errors and it works great. But I got 1 issue. When I open my project in PhpStorm and save my changes I got error:

java.lang.IllegalArgumentException: Argument for @NotNull parameter 'action' of com/intellij/openapi/actionSystem/AnActionEvent.createFromAnAction must not be null
	at com.intellij.openapi.actionSystem.AnActionEvent.$$$reportNull$$$0(AnActionEvent.java)
	at com.intellij.openapi.actionSystem.AnActionEvent.createFromAnAction(AnActionEvent.java)
	at com.dubreuia.processors.BuildProcessor.lambda$static$4(BuildProcessor.java:84)
	at com.dubreuia.processors.SaveReadCommand.execute(SaveReadCommand.java:25)
	at com.dubreuia.core.component.Engine.lambda$processPsiFiles$5(Engine.java:81)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
	at java.base/java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:442)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1654)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
	at com.dubreuia.core.component.Engine.processPsiFiles(Engine.java:82)
	at com.dubreuia.core.component.Engine.processPsiFilesIfNecessary(Engine.java:65)
	at com.dubreuia.core.component.SaveActionManager.guardedProcessPsiFiles(SaveActionManager.java:144)
	at com.dubreuia.core.component.SaveActionManager.lambda$beforeDocumentsSaving$3(SaveActionManager.java:128)
	at java.base/java.util.HashMap.forEach(HashMap.java:1336)
	at com.dubreuia.core.component.SaveActionManager.beforeDocumentsSaving(SaveActionManager.java:127)
	at com.dubreuia.core.component.SaveActionManager.beforeAllDocumentsSaving(SaveActionManager.java:112)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.intellij.util.messages.impl.MessageBusImpl.invokeListener(MessageBusImpl.java:513)
	at com.intellij.util.messages.impl.MessageBusConnectionImpl.deliverMessage(MessageBusConnectionImpl.java:142)
	at com.intellij.util.messages.impl.MessageBusImpl.doPumpMessages(MessageBusImpl.java:438)
	at com.intellij.util.messages.impl.MessageBusImpl.pumpWaitingBuses(MessageBusImpl.java:398)
	at com.intellij.util.messages.impl.MessageBusImpl.pumpMessages(MessageBusImpl.java:388)
	at com.intellij.util.messages.impl.MessageBusImpl.sendMessage(MessageBusImpl.java:372)
	at com.intellij.util.messages.impl.MessageBusImpl.lambda$createTopicHandler$1(MessageBusImpl.java:241)
	at com.sun.proxy.$Proxy23.beforeAllDocumentsSaving(Unknown Source)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.multiCast(FileDocumentManagerImpl.java:162)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.lambda$new$0(FileDocumentManagerImpl.java:125)
	at com.sun.proxy.$Proxy23.beforeAllDocumentsSaving(Unknown Source)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.saveAllDocuments(FileDocumentManagerImpl.java:311)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.saveAllDocuments(FileDocumentManagerImpl.java:301)
	at com.intellij.ide.actions.SaveAllAction.actionPerformed(SaveAllAction.kt:20)
	at com.intellij.openapi.actionSystem.ex.ActionUtil$1.run(ActionUtil.java:298)
	at com.intellij.openapi.actionSystem.ex.ActionUtil.performActionDumbAware(ActionUtil.java:315)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher$1.performAction(IdeKeyEventDispatcher.java:604)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.lambda$processAction$3(IdeKeyEventDispatcher.java:657)
	at com.intellij.openapi.application.TransactionGuardImpl.performUserActivity(TransactionGuardImpl.java:193)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.processAction(IdeKeyEventDispatcher.java:656)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.processActionOrWaitSecondStroke(IdeKeyEventDispatcher.java:517)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.inInitState(IdeKeyEventDispatcher.java:472)
	at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.dispatchKeyEvent(IdeKeyEventDispatcher.java:221)
	at com.intellij.ide.IdeEventQueue.dispatchKeyEvent(IdeEventQueue.java:830)
	at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:775)
	at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$8(IdeEventQueue.java:424)
	at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:698)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:423)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

And code is not fixed. So every time I have to go to Settings -> Tools -> External Tools and re-apply Stylelint tool. After that there is no errors and stylelint works.

Can you please check if it's 'Save Actions' issue? Thanks

OS: Ubuntu 18.04.3
PhpStorm: 2019.3
Save Actions: 1.9.0+2019.3

External tool: Stylelint
Screenshot settings Stylelint

Quick lists: Force refresh -> Stylelint
Save Actions config:
Screenshot settings Save Actions

Thanks @OlgaLevanova, I've created #292

@dubreuia Works perfect. Thanks!

Would it be possible to pass the file path that is being saved as an input to the script that would be called by the external tools? (or to setup an environment variable with this?)

use case:
to run custom reformattings only on the saved files instead of all modified files