• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Camelcade / Perl5-IDEA / #525521578

05 Jun 2025 08:31AM UTC coverage: 82.354% (+0.06%) from 82.298%
#525521578

push

github

hurricup
Redundant throws

30858 of 37470 relevant lines covered (82.35%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.72
/plugin/core/src/main/java/com/perl5/lang/perl/idea/run/GenericPerlRunConfiguration.java
1
/*
2
 * Copyright 2015-2025 Alexandr Evstigneev
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package com.perl5.lang.perl.idea.run;
18

19
import com.intellij.execution.CommonProgramRunConfigurationParameters;
20
import com.intellij.execution.ExecutionException;
21
import com.intellij.execution.Executor;
22
import com.intellij.execution.configurations.*;
23
import com.intellij.execution.process.ProcessHandler;
24
import com.intellij.execution.runners.ExecutionEnvironment;
25
import com.intellij.execution.runners.RunConfigurationWithSuppressedDefaultRunAction;
26
import com.intellij.execution.ui.ConsoleView;
27
import com.intellij.openapi.application.ApplicationManager;
28
import com.intellij.openapi.application.ReadAction;
29
import com.intellij.openapi.diagnostic.Logger;
30
import com.intellij.openapi.project.Project;
31
import com.intellij.openapi.projectRoots.Sdk;
32
import com.intellij.openapi.projectRoots.impl.PerlSdkTable;
33
import com.intellij.openapi.util.InvalidDataException;
34
import com.intellij.openapi.util.NlsSafe;
35
import com.intellij.openapi.util.WriteExternalException;
36
import com.intellij.openapi.util.text.StringUtil;
37
import com.intellij.openapi.vfs.VfsUtil;
38
import com.intellij.openapi.vfs.VirtualFile;
39
import com.intellij.util.Function;
40
import com.intellij.util.concurrency.annotations.RequiresReadLock;
41
import com.intellij.util.containers.ContainerUtil;
42
import com.intellij.util.execution.ParametersListUtil;
43
import com.intellij.util.net.NetUtils;
44
import com.intellij.util.xmlb.XmlSerializer;
45
import com.perl5.PerlBundle;
46
import com.perl5.lang.perl.idea.execution.PerlCommandLine;
47
import com.perl5.lang.perl.idea.execution.PerlTerminalExecutionConsole;
48
import com.perl5.lang.perl.idea.project.PerlProjectManager;
49
import com.perl5.lang.perl.idea.run.debugger.PerlDebugOptions;
50
import com.perl5.lang.perl.idea.sdk.host.PerlConsoleView;
51
import com.perl5.lang.perl.idea.sdk.host.PerlHostData;
52
import com.perl5.lang.perl.util.PerlFileUtil;
53
import com.perl5.lang.perl.util.PerlRunUtil;
54
import org.jdom.Element;
55
import org.jetbrains.annotations.Contract;
56
import org.jetbrains.annotations.NotNull;
57
import org.jetbrains.annotations.Nullable;
58

59
import java.io.File;
60
import java.nio.charset.Charset;
61
import java.nio.charset.UnsupportedCharsetException;
62
import java.util.*;
63
import java.util.stream.Collectors;
64

65
import static com.intellij.execution.configurations.GeneralCommandLine.ParentEnvironmentType.CONSOLE;
66
import static com.intellij.execution.configurations.GeneralCommandLine.ParentEnvironmentType.NONE;
67

68
public abstract class GenericPerlRunConfiguration extends LocatableConfigurationBase<LocatableRunConfigurationOptions>
69
  implements CommonProgramRunConfigurationParameters,
70
             RunConfigurationWithSuppressedDefaultRunAction,
71
             PerlDebugOptions {
72
  public static final Function<String, List<String>> FILES_PARSER = text -> StringUtil.split(text.trim(), "||");
1✔
73
  public static final Function<List<String>, String> FILES_JOINER = strings ->
1✔
74
    StringUtil.join(ContainerUtil.filter(strings, StringUtil::isNotEmpty), "||");
1✔
75
  public static final Function<String, List<String>> PREREQUISITES_PARSER = text -> Arrays.stream(text.trim().split("\\s*,\\s*"))
1✔
76
    .filter( StringUtil::isNotEmpty).sorted().toList() ;
1✔
77
  @SuppressWarnings("StaticMethodOnlyUsedInOneClass")
78
  public static final Function<List<String>, String> PREREQUISITES_JOINER = strings -> strings.stream()
1✔
79
    .filter(StringUtil::isNotEmpty).map(String::trim).sorted().collect(Collectors.joining(","));
1✔
80

81
  private static final Logger LOG = Logger.getInstance(GenericPerlRunConfiguration.class);
1✔
82

83
  private String myScriptPath;
84
  private String myScriptArguments;
85

86
  private String myPerlArguments = "";
1✔
87
  private String myWorkingDirectory;
88
  private Map<String, String> myEnvironments = new HashMap<>();
1✔
89
  private boolean myPassParentEnvironments = true;
1✔
90
  private String myConsoleCharset;
91
  private boolean myUseAlternativeSdk;
92
  private String myAlternativeSdkName;
93
  private @NotNull String myRequiredModules = "";
1✔
94

95
  // debugging-related options
96
  private String myScriptCharset = "utf8";
1✔
97
  private String myStartMode = "RUN";
1✔
98
  private boolean myIsNonInteractiveModeEnabled = false;
1✔
99
  private boolean myIsCompileTimeBreakpointsEnabled = false;
1✔
100
  private String myInitCode = "";
1✔
101

102
  private transient Integer myDebugPort;
103

104
  public GenericPerlRunConfiguration(@NotNull Project project, @NotNull ConfigurationFactory factory, @Nullable String name) {
105
    super(project, factory, name);
1✔
106
  }
1✔
107

108
  @Override
109
  public void readExternal(@NotNull Element element) throws InvalidDataException {
110
    super.readExternal(element);
×
111
    XmlSerializer.deserializeInto(this, element);
×
112
  }
×
113

114
  @Override
115
  public void writeExternal(@NotNull Element element) throws WriteExternalException {
116
    super.writeExternal(element);
1✔
117
    XmlSerializer.serializeInto(this, element);
1✔
118
  }
1✔
119

120
  @Override
121
  public final @Nullable PerlRunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment executionEnvironment)
122
    throws ExecutionException {
123
    var runner = executionEnvironment.getRunner();
1✔
124
    if (!(runner instanceof GenericPerlProgramRunner genericPerlProgramRunner)) {
1✔
125
      LOG.error("GenericPerlProgramRunner expected, got " + runner);
×
126
      throw new ExecutionException(
×
127
        PerlBundle.message("dialog.message.wrong.runner.used.to.run.perl.configuration.please.report.to.perl.plugin.developers"));
×
128
    }
129
    return genericPerlProgramRunner.createState(executionEnvironment);
1✔
130
  }
131

132
  /**
133
   * @return local path to the perl interpreter for this run configuration
134
   * @throws ExecutionException with human readable error message if interpreter path is empty
135
   */
136
  protected @NotNull String getEffectiveInterpreterPath() throws ExecutionException {
137
    Sdk effectiveSdk = getEffectiveSdk();
1✔
138
    String interpreterPath = PerlProjectManager.getInterpreterPath(effectiveSdk);
1✔
139
    if (StringUtil.isEmpty(interpreterPath)) {
1✔
140
      throw new ExecutionException(PerlBundle.message("perl.run.error.no.interpreter.path", effectiveSdk));
×
141
    }
142
    return interpreterPath;
1✔
143
  }
144

145
  public @NotNull Sdk getEffectiveSdk() throws ExecutionException {
146
    Sdk perlSdk;
147
    if (isUseAlternativeSdk()) {
1✔
148
      String alternativeSdkName = getAlternativeSdkName();
×
149
      if (StringUtil.isEmpty(alternativeSdkName)) {
×
150
        throw new ExecutionException(PerlBundle.message("perl.run.error.no.alternative.sdk.selected"));
×
151
      }
152
      perlSdk = PerlSdkTable.getInstance().findJdk(alternativeSdkName);
×
153
      if (perlSdk == null) {
×
154
        throw new ExecutionException(PerlBundle.message("perl.run.error.no.alternative.sdk", alternativeSdkName));
×
155
      }
156
      return perlSdk;
×
157
    }
158
    else {
159
      perlSdk = PerlProjectManager.getSdk(getProject());
1✔
160
      if (perlSdk == null) {
1✔
161
        throw new ExecutionException(PerlBundle.message("perl.run.error.no.sdk", getProject().getName()));
×
162
      }
163
    }
164
    return perlSdk;
1✔
165
  }
166

167
  @Override
168
  public String suggestedName() {
169
    List<VirtualFile> targetFiles = computeTargetFiles();
1✔
170
    return targetFiles.isEmpty() ? null : targetFiles.getFirst().getName();
1✔
171
  }
172

173
  protected @NotNull List<VirtualFile> computeTargetFiles() {
174
    return computeVirtualFilesFromPaths(getScriptPath());
1✔
175
  }
176

177
  private @NotNull VirtualFile computeNonNullScriptFile() throws ExecutionException {
178
    List<VirtualFile> targetFiles = computeTargetFiles();
1✔
179
    if (targetFiles.isEmpty()) {
1✔
180
      throw new ExecutionException(PerlBundle.message("perl.run.error.script.missing", getScriptPath()));
×
181
    }
182
    return targetFiles.getFirst();
1✔
183
  }
184

185
  public @NotNull String getRequiredModules() {
186
    return myRequiredModules;
1✔
187
  }
188

189
  public @NotNull List<String> getRequiredModulesList() {
190
    return PREREQUISITES_PARSER.fun(myRequiredModules);
1✔
191
  }
192

193
  public void setRequiredModules(@NotNull String requiredModules) {
194
    myRequiredModules = PREREQUISITES_JOINER.fun(PREREQUISITES_PARSER.fun(requiredModules));
1✔
195
  }
1✔
196

197
  public @NlsSafe String getConsoleCharset() {
198
    return myConsoleCharset;
1✔
199
  }
200

201
  public void setConsoleCharset(String charset) {
202
    myConsoleCharset = charset;
1✔
203
  }
1✔
204

205
  public String getScriptPath() {
206
    return myScriptPath;
1✔
207
  }
208

209
  public void setScriptPath(String scriptPath) {
210
    myScriptPath = scriptPath;
1✔
211
  }
1✔
212

213
  public String getAlternativeSdkName() {
214
    return myAlternativeSdkName;
1✔
215
  }
216

217
  public void setAlternativeSdkName(String name) {
218
    myAlternativeSdkName = name;
1✔
219
  }
1✔
220

221
  public boolean isUseAlternativeSdk() {
222
    return myUseAlternativeSdk;
1✔
223
  }
224

225
  public void setUseAlternativeSdk(boolean value) {
226
    myUseAlternativeSdk = value;
1✔
227
  }
1✔
228

229
  @Override
230
  public @Nullable String getProgramParameters() {
231
    return myScriptArguments;
1✔
232
  }
233

234
  @Override
235
  public void setProgramParameters(@Nullable String s) {
236
    myScriptArguments = s;
1✔
237
  }
1✔
238

239
  @Override
240
  public @Nullable String getWorkingDirectory() {
241
    return myWorkingDirectory;
1✔
242
  }
243

244
  /**
245
   * @return virtual file for a working directory explicitly set by user
246
   */
247
  protected @Nullable VirtualFile computeExplicitWorkingDirectory() {
248
    String workingDirectory = getWorkingDirectory();
1✔
249
    if (StringUtil.isEmpty(workingDirectory)) {
1✔
250
      return null;
1✔
251
    }
252
    return VfsUtil.findFileByIoFile(new File(workingDirectory), true);
×
253
  }
254

255
  @Override
256
  public void setWorkingDirectory(@Nullable String s) {
257
    myWorkingDirectory = s;
1✔
258
  }
1✔
259

260
  @Override
261
  public @NotNull Map<String, String> getEnvs() {
262
    return myEnvironments;
1✔
263
  }
264

265
  @Override
266
  public void setEnvs(@NotNull Map<String, String> map) {
267
    myEnvironments = map;
1✔
268
  }
1✔
269

270
  @Override
271
  public boolean isPassParentEnvs() {
272
    return myPassParentEnvironments;
1✔
273
  }
274

275
  @Override
276
  public void setPassParentEnvs(boolean b) {
277
    myPassParentEnvironments = b;
1✔
278
  }
1✔
279

280
  public String getPerlArguments() {
281
    return myPerlArguments;
1✔
282
  }
283

284
  protected @NotNull List<String> getPerlArgumentsList() {
285
    String perlArguments = getPerlArguments();
1✔
286
    return StringUtil.isEmpty(perlArguments) ? Collections.emptyList() : ParametersListUtil.parse(perlArguments);
1✔
287
  }
288

289
  public void setPerlArguments(String perlArguments) {
290
    this.myPerlArguments = perlArguments;
1✔
291
  }
1✔
292

293
  @Override
294
  public String getStartMode() {
295
    return myStartMode;
1✔
296
  }
297

298
  @Override
299
  public void setStartMode(String startMode) {
300
    this.myStartMode = startMode;
1✔
301
  }
1✔
302

303
  @Override
304
  public String getPerlRole() {
305
    return PerlDebugOptions.ROLE_SERVER;
1✔
306
  }
307

308
  @Override
309
  public String getHostToBind() {
310
    return LOCAL_DEBUG_HOST;
1✔
311
  }
312

313
  @Override
314
  public int getDebugPort() throws ExecutionException {
315
    if (myDebugPort == null) {
1✔
316
      myDebugPort = NetUtils.tryToFindAvailableSocketPort();
1✔
317
      if (myDebugPort == -1) {
1✔
318
        throw new ExecutionException(PerlBundle.message("dialog.message.no.free.port.to.work.on"));
×
319
      }
320
    }
321

322
    return myDebugPort;
1✔
323
  }
324

325
  @Override
326
  public String getRemoteProjectRoot() {
327
    return getProject().getBasePath();
×
328
  }
329

330
  @Override
331
  public String getScriptCharset() {
332
    return myScriptCharset;
1✔
333
  }
334

335
  @Override
336
  public void setScriptCharset(String scriptCharset) {
337
    this.myScriptCharset = scriptCharset;
1✔
338
  }
1✔
339

340
  @Override
341
  public boolean isNonInteractiveModeEnabled() {
342
    return myIsNonInteractiveModeEnabled;
1✔
343
  }
344

345
  @Override
346
  public void setNonInteractiveModeEnabled(boolean nonInteractiveModeEnabled) {
347
    myIsNonInteractiveModeEnabled = nonInteractiveModeEnabled;
1✔
348
  }
1✔
349

350
  @Override
351
  public boolean isCompileTimeBreakpointsEnabled() {
352
    return myIsCompileTimeBreakpointsEnabled;
1✔
353
  }
354

355
  @Override
356
  public void setCompileTimeBreakpointsEnabled(boolean compileTimeBreakpointsEnabled) {
357
    myIsCompileTimeBreakpointsEnabled = compileTimeBreakpointsEnabled;
1✔
358
  }
1✔
359

360
  public @NotNull PerlCommandLine createCommandLine(@NotNull PerlRunProfileState perlRunProfileState) throws ExecutionException {
361
    PerlCommandLine commandLine = createBaseCommandLine(perlRunProfileState);
1✔
362
    commandLine.withParentEnvironmentType(isPassParentEnvs() ? CONSOLE : NONE);
1✔
363
    commandLine.withWorkDirectory(computeWorkingDirectory(perlRunProfileState.getEnvironment().getProject()));
1✔
364
    commandLine.withCharset(computeCharset());
1✔
365

366
    return commandLine.withPty(isUsePty());
1✔
367
  }
368

369
  protected boolean isUsePty() {
370
    return true;
1✔
371
  }
372

373
  protected @NotNull Charset computeCharset() throws ExecutionException {
374
    String charsetName = getConsoleCharset();
1✔
375
    if (!StringUtil.isEmpty(charsetName)) {
1✔
376
      try {
377
        return Charset.forName(charsetName);
1✔
378
      }
379
      catch (UnsupportedCharsetException e) {
×
380
        throw new ExecutionException(PerlBundle.message("perl.run.error.unknown.charset", charsetName));
×
381
      }
382
    }
383
    else {
384
      return computeNonNullScriptFile().getCharset();
×
385
    }
386
  }
387

388
  /**
389
   * @see PerlRemoteDebuggingRunProfileState#computeLocalProjectPath(PerlRemoteDebuggingConfiguration)
390
   */
391
  protected @Nullable String computeWorkingDirectory(@NotNull Project project) throws ExecutionException {
392
    String workDirectory = getWorkingDirectory();
1✔
393
    if (StringUtil.isNotEmpty(workDirectory)) {
1✔
394
      return workDirectory;
×
395
    }
396
    return ReadAction.compute(() -> computeWorkingDirectory(project, computeNonNullScriptFile()));
1✔
397
  }
398

399
  protected @NotNull PerlCommandLine createBaseCommandLine(@NotNull PerlRunProfileState perlRunProfileState) throws ExecutionException {
400
    ExecutionEnvironment executionEnvironment = perlRunProfileState.getEnvironment();
1✔
401
    Project project = executionEnvironment.getProject();
1✔
402
    List<String> additionalPerlArguments = perlRunProfileState.getAdditionalPerlArguments(this);
1✔
403
    Map<String, String> additionalEnvironmentVariables = perlRunProfileState.getAdditionalEnvironmentVariables();
1✔
404

405
    PerlCommandLine commandLine = PerlRunUtil.getPerlCommandLine(
1✔
406
      project, getEffectiveSdk(), computeNonNullScriptFile(), ContainerUtil.concat(getPerlArgumentsList(), additionalPerlArguments),
1✔
407
      getScriptArguments());
1✔
408

409
    if (commandLine == null) {
1✔
410
      throw new ExecutionException(PerlBundle.message("perl.run.error.sdk.corrupted", getEffectiveSdk()));
×
411
    }
412

413
    Map<String, String> environment = new HashMap<>(getEnvs());
1✔
414
    environment.putAll(additionalEnvironmentVariables);
1✔
415
    commandLine.withEnvironment(environment);
1✔
416
    return commandLine;
1✔
417
  }
418

419
  @RequiresReadLock
420
  protected static @Nullable String computeWorkingDirectory(@NotNull Project project, @NotNull VirtualFile virtualFile) {
421
    var fileContentRoot = PerlFileUtil.getContentRoot(project, virtualFile);
1✔
422
    if (fileContentRoot != null) {
1✔
423
      return fileContentRoot.getPath();
1✔
424
    }
425
    return project.getBasePath();
×
426
  }
427

428
  protected @NotNull List<String> getScriptArguments() {
429
    String programArguments = getProgramParameters();
1✔
430
    return StringUtil.isEmpty(programArguments) ? Collections.emptyList() : ParametersListUtil.parse(programArguments);
1✔
431
  }
432

433
  @Override
434
  public String getInitCode() {
435
    return myInitCode;
1✔
436
  }
437

438
  @Override
439
  public void setInitCode(String initCode) {
440
    this.myInitCode = initCode;
1✔
441
  }
1✔
442

443
  /**
444
   * @return list of VirtualFiles pointed by  {@code paths} joined with pipe. Reverse of {@link #computePathsFromVirtualFiles(List)}
445
   */
446
  public static @NotNull List<VirtualFile> computeVirtualFilesFromPaths(@Nullable String paths) {
447
    if (StringUtil.isEmpty(paths)) {
1✔
448
      return Collections.emptyList();
×
449
    }
450
    List<String> pathNames = FILES_PARSER.fun(paths);
1✔
451
    if (pathNames.isEmpty()) {
1✔
452
      return Collections.emptyList();
×
453
    }
454
    List<VirtualFile> virtualFiles = new ArrayList<>(pathNames.size());
1✔
455
    for (String pathName : pathNames) {
1✔
456
      if (StringUtil.isEmpty(pathName)) {
1✔
457
        continue;
×
458
      }
459
      ContainerUtil.addIfNotNull(virtualFiles, VfsUtil.findFileByIoFile(new File(pathName), false));
1✔
460
    }
1✔
461
    return virtualFiles;
1✔
462
  }
463

464
  /**
465
   * @return paths of {@code virtualFiles} joined with pipe, reverse of {@link #computeVirtualFilesFromPaths(String)}
466
   */
467
  public static @NotNull String computePathsFromVirtualFiles(@NotNull List<? extends VirtualFile> virtualFiles) {
468
    return FILES_JOINER.fun(ContainerUtil.map(virtualFiles, VirtualFile::getPath));
1✔
469
  }
470

471
  public @NotNull ConsoleView createConsole(@NotNull PerlRunProfileState runProfileState) throws ExecutionException {
472
    ExecutionEnvironment executionEnvironment = runProfileState.getEnvironment();
1✔
473
    PerlConsoleView console = ApplicationManager.getApplication().isUnitTestMode() ?
1✔
474
                              new PerlRunConsole(runProfileState.getEnvironment().getProject()) :
1✔
475
                              new PerlTerminalExecutionConsole(executionEnvironment.getProject());
1✔
476
    return console.withHostData(PerlHostData.from(getEffectiveSdk()));
1✔
477
  }
478

479
  /**
480
   * May patch {@code processHandler} created with {@code runProfileState} from current run configuration.
481
   *
482
   * @return patched process handler
483
   */
484
  protected @NotNull ProcessHandler doPatchProcessHandler(@NotNull ProcessHandler processHandler) {
485
    return processHandler;
1✔
486
  }
487

488
  /**
489
   * Method checks if specified script(s) path is ok.
490
   *
491
   * @throws RuntimeConfigurationException with human-readable error message
492
   */
493
  protected void checkConfigurationScriptPath() throws RuntimeConfigurationException {
494
    if (StringUtil.isEmptyOrSpaces(getScriptPath())) {
1✔
495
      throw new RuntimeConfigurationException(PerlBundle.message("perl.run.error.no.script.set"));
×
496
    }
497
    if (computeTargetFiles().isEmpty()) {
1✔
498
      throw new RuntimeConfigurationException(PerlBundle.message("perl.run.error.no.script.found", getScriptPath()));
×
499
    }
500
  }
1✔
501

502
  @Override
503
  public void checkConfiguration() throws RuntimeConfigurationException {
504
    checkConfigurationScriptPath();
1✔
505
    try {
506
      getEffectiveInterpreterPath();
1✔
507
    }
508
    catch (ExecutionException e) {
×
509
      throw new RuntimeConfigurationException(e.getMessage());
×
510
    }
1✔
511
  }
1✔
512

513
  /**
514
   * If profileState was build with {@link GenericPerlRunConfiguration}, patches {@code processHandler} using
515
   * {@link #doPatchProcessHandler(ProcessHandler)}
516
   *
517
   * @return patched process handler
518
   */
519
  @Contract("null,_->null; !null,_->!null")
520
  static @Nullable ProcessHandler patchProcessHandler(@Nullable ProcessHandler processHandler,
521
                                                      @NotNull PerlRunProfileState runProfileState) {
522
    if (processHandler == null) {
1✔
523
      return null;
×
524
    }
525
    RunProfile runConfiguration = runProfileState.getEnvironment().getRunProfile();
1✔
526
    return runConfiguration instanceof GenericPerlRunConfiguration perlRunConfiguration ?
1✔
527
      perlRunConfiguration.doPatchProcessHandler(processHandler) :
1✔
528
           processHandler;
×
529
  }
530
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc