66import java .util .List ;
77import java .util .Objects ;
88import java .util .Set ;
9+ import java .util .concurrent .atomic .AtomicInteger ;
910import java .util .regex .Matcher ;
1011import java .util .regex .Pattern ;
1112import java .util .regex .PatternSyntaxException ;
1213
1314import org .jetbrains .annotations .NotNull ;
1415
16+ import com .intellij .openapi .application .ReadAction ;
17+ import com .intellij .openapi .application .WriteAction ;
1518import com .intellij .openapi .diagnostic .Logger ;
1619import com .intellij .openapi .editor .Document ;
1720import com .intellij .openapi .fileEditor .FileDocumentManager ;
21+ import com .intellij .openapi .progress .ProgressIndicator ;
1822import com .intellij .openapi .project .Project ;
1923import com .intellij .openapi .roots .ProjectRootManager ;
24+ import com .intellij .openapi .util .ThrowableComputable ;
2025import com .intellij .psi .PsiDocumentManager ;
2126import com .intellij .psi .PsiFile ;
27+ import com .intellij .util .ApplicationKt ;
2228import com .intellij .util .PsiErrorElementUtil ;
29+ import com .intellij .util .ThrowableRunnable ;
2330
2431import software .xdev .saveactions .core .ExecutionMode ;
2532import software .xdev .saveactions .core .service .SaveActionsService ;
@@ -62,7 +69,9 @@ public Engine(
6269 this .mode = mode ;
6370 }
6471
65- public void processPsiFilesIfNecessary ()
72+ public void processPsiFilesIfNecessary (
73+ @ NotNull final ProgressIndicator indicator ,
74+ final boolean async )
6675 {
6776 if (this .psiFiles == null )
6877 {
@@ -73,36 +82,112 @@ public void processPsiFilesIfNecessary()
7382 LOGGER .info (String .format ("Action \" %s\" not enabled on %s" , this .activation .getText (), this .project ));
7483 return ;
7584 }
85+
86+ indicator .setIndeterminate (true );
87+ final Set <PsiFile > psiFilesEligible = this .getEligiblePsiFiles (indicator , async );
88+ if (psiFilesEligible .isEmpty ())
89+ {
90+ LOGGER .info ("No files are eligible" );
91+ return ;
92+ }
93+
94+ final List <SaveCommand > processorsEligible = this .getEligibleProcessors (indicator , psiFilesEligible );
95+ if (processorsEligible .isEmpty ())
96+ {
97+ LOGGER .info ("No processors are eligible" );
98+ return ;
99+ }
100+
101+ this .flushPsiFiles (indicator , async , psiFilesEligible );
102+
103+ this .execute (indicator , processorsEligible , psiFilesEligible );
104+ }
105+
106+ private Set <PsiFile > getEligiblePsiFiles (final @ NotNull ProgressIndicator indicator , final boolean async )
107+ {
76108 LOGGER .info (String .format ("Processing %s files %s mode %s" , this .project , this .psiFiles , this .mode ));
77- final Set <PsiFile > psiFilesEligible = this .psiFiles .stream ()
78- .filter (psiFile -> this .isPsiFileEligible (this .project , psiFile ))
79- .collect (toSet ());
109+ indicator .checkCanceled ();
110+ indicator .setText2 ("Collecting files to process" );
111+
112+ final ThrowableComputable <Set <PsiFile >, RuntimeException > psiFilesEligibleFunc =
113+ () -> this .psiFiles .stream ()
114+ .filter (psiFile -> this .isPsiFileEligible (this .project , psiFile ))
115+ .collect (toSet ());
116+ final Set <PsiFile > psiFilesEligible = async
117+ ? ReadAction .compute (psiFilesEligibleFunc )
118+ : psiFilesEligibleFunc .compute ();
80119 LOGGER .info (String .format ("Valid files %s" , psiFilesEligible ));
81- this . processPsiFiles ( this . project , psiFilesEligible , this . mode ) ;
120+ return psiFilesEligible ;
82121 }
83122
84- private void processPsiFiles (final Project project , final Set <PsiFile > psiFiles , final ExecutionMode mode )
123+ private @ NotNull List <SaveCommand > getEligibleProcessors (
124+ final @ NotNull ProgressIndicator indicator ,
125+ final Set <PsiFile > psiFilesEligible )
85126 {
86- if (psiFiles .isEmpty ())
87- {
88- return ;
89- }
90127 LOGGER .info (String .format ("Start processors (%d)" , this .processors .size ()));
128+ indicator .checkCanceled ();
129+ indicator .setText2 ("Collecting processors" );
130+
91131 final List <SaveCommand > processorsEligible = this .processors .stream ()
92- .map (processor -> processor .getSaveCommand (project , psiFiles ))
132+ .map (processor -> processor .getSaveCommand (this . project , psiFilesEligible ))
93133 .filter (command -> this .storage .isEnabled (command .getAction ()))
94- .filter (command -> command .getModes ().contains (mode ))
134+ .filter (command -> command .getModes ().contains (this . mode ))
95135 .toList ();
96136 LOGGER .info (String .format ("Filtered processors %s" , processorsEligible ));
97- if (!processorsEligible .isEmpty ())
137+ return processorsEligible ;
138+ }
139+
140+ private void flushPsiFiles (
141+ final @ NotNull ProgressIndicator indicator ,
142+ final boolean async ,
143+ final Set <PsiFile > psiFilesEligible )
144+ {
145+ LOGGER .info (String .format ("Flushing files (%d)" , psiFilesEligible .size ()));
146+ indicator .checkCanceled ();
147+ indicator .setText2 ("Flushing files" );
148+
149+ final ThrowableRunnable <RuntimeException > flushFilesFunc = () -> {
150+ final PsiDocumentManager psiDocumentManager = PsiDocumentManager .getInstance (this .project );
151+ psiFilesEligible .forEach (psiFile -> this .commitDocumentAndSave (psiFile , psiDocumentManager ));
152+ };
153+ if (async )
154+ {
155+ ApplicationKt .getApplication ().invokeAndWait (() -> WriteAction .run (flushFilesFunc ));
156+ }
157+ else
98158 {
99- final PsiDocumentManager psiDocumentManager = PsiDocumentManager .getInstance (project );
100- psiFiles .forEach (psiFile -> this .commitDocumentAndSave (psiFile , psiDocumentManager ));
159+ flushFilesFunc .run ();
101160 }
102- final List <SimpleEntry <Action , Result <ResultCode >>> results = processorsEligible .stream ()
161+ }
162+
163+ private void execute (
164+ final @ NotNull ProgressIndicator indicator ,
165+ final List <SaveCommand > processorsEligible ,
166+ final Set <PsiFile > psiFilesEligible )
167+ {
168+ indicator .checkCanceled ();
169+ indicator .setIndeterminate (false );
170+ indicator .setFraction (0d );
171+
172+ final List <SaveCommand > saveCommands = processorsEligible .stream ()
103173 .filter (Objects ::nonNull )
104- .peek (command -> LOGGER .info (String .format ("Execute command %s on %d files" , command , psiFiles .size ())))
105- .map (command -> new SimpleEntry <>(command .getAction (), command .execute ()))
174+ .toList ();
175+
176+ final AtomicInteger executedCount = new AtomicInteger ();
177+ final List <SimpleEntry <Action , Result <ResultCode >>> results = saveCommands .stream ()
178+ .map (command -> {
179+ LOGGER .info (String .format ("Execute command %s on %d files" , command , psiFilesEligible .size ()));
180+
181+ indicator .checkCanceled ();
182+ indicator .setText2 ("Executing '" + command .getAction ().getText () + "'" );
183+
184+ final SimpleEntry <Action , Result <ResultCode >> entry =
185+ new SimpleEntry <>(command .getAction (), command .execute ());
186+
187+ indicator .setFraction ((double )executedCount .incrementAndGet () / saveCommands .size ());
188+
189+ return entry ;
190+ })
106191 .toList ();
107192 LOGGER .info (String .format ("Exit engine with results %s" , results .stream ()
108193 .map (entry -> entry .getKey () + ":" + entry .getValue ())
0 commit comments