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

Camelcade / Perl5-IDEA / #525521565

02 Jun 2025 09:13AM UTC coverage: 82.339% (+0.007%) from 82.332%
#525521565

push

github

hurricup
Added language attributes to HTML templates

30858 of 37477 relevant lines covered (82.34%)

0.82 hits per line

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

21.21
/plugin/core/src/main/java/com/perl5/lang/perl/idea/sdk/host/PerlHostHandler.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.sdk.host;
18

19
import com.intellij.openapi.Disposable;
20
import com.intellij.openapi.application.ApplicationManager;
21
import com.intellij.openapi.fileChooser.FileChooser;
22
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
23
import com.intellij.openapi.options.UnnamedConfigurable;
24
import com.intellij.openapi.project.Project;
25
import com.intellij.openapi.projectRoots.Sdk;
26
import com.intellij.openapi.ui.InputValidator;
27
import com.intellij.openapi.ui.Messages;
28
import com.intellij.openapi.util.NlsActions.ActionText;
29
import com.intellij.openapi.util.Ref;
30
import com.intellij.openapi.util.io.FileUtil;
31
import com.intellij.openapi.util.text.StringUtil;
32
import com.intellij.openapi.vfs.VirtualFile;
33
import com.intellij.openapi.vfs.VirtualFileSystem;
34
import com.perl5.lang.perl.idea.sdk.AbstractPerlHandler;
35
import com.perl5.lang.perl.idea.sdk.PerlHandlerBean;
36
import com.perl5.lang.perl.idea.sdk.PerlHandlerCollector;
37
import com.perl5.lang.perl.idea.sdk.host.os.PerlOsHandler;
38
import org.jdom.Element;
39
import org.jetbrains.annotations.Contract;
40
import org.jetbrains.annotations.NotNull;
41
import org.jetbrains.annotations.Nullable;
42

43
import javax.swing.*;
44
import java.io.File;
45
import java.util.List;
46
import java.util.Objects;
47
import java.util.function.BiConsumer;
48
import java.util.function.Consumer;
49
import java.util.function.Function;
50
import java.util.function.Predicate;
51
import java.util.stream.Stream;
52

53
/**
54
 * Know how to work with different types of hosts
55
 */
56
public abstract class PerlHostHandler<Data extends PerlHostData<Data, Handler>, Handler extends PerlHostHandler<Data, Handler>>
57
  extends AbstractPerlHandler<Data, Handler> {
58
  private static final String TAG_NAME = "host";
59

60
  private static final PerlHandlerCollector<PerlHostHandler<?, ?>> EP = new PerlHandlerCollector<>("com.perl5.hostHandler");
1✔
61

62
  public PerlHostHandler(@NotNull PerlHandlerBean bean) {
63
    super(bean);
1✔
64
  }
1✔
65

66
  /**
67
   * Chooses a path conforming with {@code pathPredicate} on user-selected host and passes selected path and host data
68
   * to the {@code selectionConsumer}
69
   *
70
   * @param defaultPathFunction function computing a default path from {@code hostData}
71
   * @param useDefaultIfExists  true if default path should be used silently if exists
72
   * @param nameValidator       restricts visible file names
73
   * @param pathValidator       validates a path selected by user and returns error message or null if everything is fine
74
   * @param selectionConsumer   a callback for selected result. Accepts path selected and the host data
75
   * @param disposable          session-bound things may be attached to this disposable, which is going to be disposed by parent configurable
76
   */
77
  public void chooseFileInteractively(@NotNull String dialogTitle,
78
                                      @Nullable Function<? super PerlHostData<?, ?>, ? extends File> defaultPathFunction,
79
                                      boolean useDefaultIfExists,
80
                                      @NotNull Predicate<? super String> nameValidator,
81
                                      @NotNull Function<? super String, String> pathValidator,
82
                                      @NotNull BiConsumer<? super String, ? super PerlHostData<?, ?>> selectionConsumer,
83
                                      @NotNull Disposable disposable) {
84
    Data hostData = createDataInteractively();
×
85
    if (hostData == null) {
×
86
      return;
×
87
    }
88
    VirtualFileSystem fileSystem = hostData.getFileSystem(disposable);
×
89
    File defaultPath = defaultPathFunction == null ? null : defaultPathFunction.apply(hostData);
×
90
    Consumer<String> resultConsumer = it -> selectionConsumer.accept(it, hostData);
×
91
    if (fileSystem != null) {
×
92
      chooseFileInteractively(
×
93
        dialogTitle, defaultPath, useDefaultIfExists, nameValidator, pathValidator, resultConsumer, hostData, fileSystem);
94
    }
95
    else {
96
      chooseFileInteractively(
×
97
        dialogTitle, defaultPath, useDefaultIfExists, nameValidator, pathValidator, resultConsumer, hostData);
98
    }
99
  }
×
100

101
  /**
102
   * Choose a file if host does not provide a file system
103
   */
104
  protected void chooseFileInteractively(@NotNull String dialogTitle,
105
                                         @Nullable File defaultPath,
106
                                         boolean useDefaultIfExists,
107
                                         @NotNull Predicate<? super String> nameValidator,
108
                                         @NotNull Function<? super String, String> pathValidator,
109
                                         @NotNull Consumer<? super String> selectionConsumer,
110
                                         @NotNull Data hostData) {
111
    Ref<String> pathRef = Ref.create();
×
112
    ApplicationManager.getApplication().invokeAndWait(
×
113
      () -> pathRef.set(Messages.showInputDialog(
×
114
        (Project)null, null, dialogTitle, null,
115
        defaultPath == null ? null : FileUtil.toSystemIndependentName(defaultPath.getPath()),
×
116
        new InputValidator() {
×
117
          @Override
118
          public boolean checkInput(String inputString) {
119
            if (StringUtil.isEmpty(inputString)) {
×
120
              return false;
×
121
            }
122
            return nameValidator.test(new File(inputString).getName());
×
123
          }
124

125
          @Override
126
          public boolean canClose(String inputString) {
127
            return StringUtil.isNotEmpty(inputString) && pathValidator.apply(inputString) == null;
×
128
          }
129
        })));
130
    String chosenPath = pathRef.get();
×
131
    if (StringUtil.isNotEmpty(chosenPath)) {
×
132
      selectionConsumer.accept(chosenPath);
×
133
    }
134
  }
×
135

136
  /**
137
   * Choose a file if host provides a file system
138
   */
139
  protected void chooseFileInteractively(@NotNull String dialogTitle,
140
                                         @Nullable File defaultPath,
141
                                         boolean useDefaultIfExists,
142
                                         @NotNull Predicate<? super String> nameValidator,
143
                                         @NotNull Function<? super String, String> pathValidator,
144
                                         @NotNull Consumer<? super String> selectionConsumer,
145
                                         @NotNull Data hostData,
146
                                         @NotNull VirtualFileSystem fileSystem) {
147
    VirtualFile defaultFile = defaultPath == null ? null : fileSystem.findFileByPath(defaultPath.getPath());
×
148

149
    if (useDefaultIfExists && defaultFile != null && defaultFile.exists() && !defaultFile.isDirectory()) {
×
150
      selectionConsumer.accept(defaultFile.getPath());
×
151
      return;
×
152
    }
153

154
    final var descriptor = createDescriptor(dialogTitle, nameValidator, pathValidator);
×
155
    customizeFileChooser(descriptor, fileSystem);
×
156
    Ref<String> pathRef = Ref.create();
×
157
    ApplicationManager.getApplication().invokeAndWait(() -> FileChooser.chooseFiles(descriptor, null, defaultFile, chosen -> {
×
158
      String selectedPath = chosen.getFirst().getPath();
×
159
      if (StringUtil.isEmpty(pathValidator.apply(selectedPath))) {
×
160
        pathRef.set(selectedPath);
×
161
      }
162
    }));
×
163
    if (!pathRef.isNull()) {
×
164
      selectionConsumer.accept(pathRef.get());
×
165
    }
166
  }
×
167

168
  private @NotNull FileChooserDescriptor createDescriptor(@NotNull String dialogTitle,
169
                                                          @NotNull Predicate<? super String> nameValidator,
170
                                                          @NotNull Function<? super String, String> pathValidator) {
171
    final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, isChooseFolders(), false, false, false, false) {
×
172
      @SuppressWarnings("deprecation")
173
      @Override
174
      public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
175
        return super.isFileVisible(file, showHiddenFiles) && (file.isDirectory() || nameValidator.test(file.getName()));
×
176
      }
177

178
      @Override
179
      public void validateSelectedFiles(VirtualFile[] files) throws Exception {
180
        if (files.length != 0) {
×
181
          String errorMessage = pathValidator.apply(files[0].getPath());
×
182
          if (StringUtil.isNotEmpty(errorMessage)) {
×
183
            throw new Exception(errorMessage);
×
184
          }
185
        }
186
      }
×
187
    };
188
    descriptor.setTitle(dialogTitle);
×
189
    return descriptor;
×
190
  }
191

192
  /**
193
   * @return true iff we should allow to choose folders - only case is local mac chooser
194
   */
195
  protected boolean isChooseFolders() {return false;}
×
196

197
  /**
198
   * host handler may customize a chooser descriptor if necessary, e.g. add fs roots
199
   */
200
  protected void customizeFileChooser(@NotNull FileChooserDescriptor descriptor, @NotNull VirtualFileSystem fileSystem) {
201
  }
×
202

203
  /**
204
   * Creates a necessary host data with possible interactions with user
205
   *
206
   * @return host data or null if user cancelled the process
207
   */
208
  protected abstract @Nullable Data createDataInteractively();
209

210
  /**
211
   * @return a menu item title for this handler, used in UI. E.g. new interpreter menu item
212
   */
213
  public abstract @NotNull @ActionText String getMenuItemTitle();
214

215
  /**
216
   * @return short lowercased name, for interpreters list
217
   */
218
  public abstract @NotNull String getShortName();
219

220
  /**
221
   * @return true iff this handler can be used in the application. E.g. has proper os, proper plugins, etc.
222
   */
223
  public abstract boolean isApplicable();
224

225
  /**
226
   * @return an OS handler for this host if it's available without data. E.g for local, wsl, docker cases
227
   */
228
  public abstract @Nullable PerlOsHandler getOsHandler();
229

230
  /**
231
   * @return true  iff it is a localhost handler. Despite docker and wsl are local as well, they considered as remote.
232
   */
233
  public abstract  boolean isLocal();
234

235
  @Override
236
  protected final @NotNull String getTagName() {
237
    return TAG_NAME;
1✔
238
  }
239

240
  /**
241
   * @return configurable for the {@code project} settings related to this host data type if any. E.g. docker has additional cli arguments on project level
242
   */
243
  public @Nullable UnnamedConfigurable getSettingsConfigurable(@NotNull Project project) {
244
    return null;
1✔
245
  }
246

247
  public abstract @Nullable Icon getIcon();
248

249
  public static @NotNull List<? extends PerlHostHandler<?, ?>> all() {
250
    return EP.getExtensionsList();
1✔
251
  }
252

253
  public static void forEach(@NotNull Consumer<? super PerlHostHandler<?, ?>> action) {
254
    all().forEach(action);
×
255
  }
×
256

257
  public static @NotNull Stream<? extends PerlHostHandler<?, ?>> stream() {
258
    return all().stream();
1✔
259
  }
260

261
  @Contract("null->null")
262
  public static @Nullable PerlHostHandler<?, ?> from(@Nullable Sdk sdk) {
263
    PerlHostData<?, ?> perlHostData = PerlHostData.from(sdk);
1✔
264
    return perlHostData == null ? null : perlHostData.getHandler();
1✔
265
  }
266

267
  /**
268
   * Attempts to load {@link PerlHostData} from the {@code parentElement}
269
   *
270
   * @return data read or null if EP not found
271
   */
272
  public static @Nullable PerlHostData<?, ?> load(@NotNull Element parentElement) {
273
    Element element = parentElement.getChild(TAG_NAME);
1✔
274
    if (element == null) {
1✔
275
      return null;
×
276
    }
277
    PerlHostHandler<?, ?> handler = EP.findSingle(element.getAttributeValue(ID_ATTRIBUTE));
1✔
278
    return handler != null ? handler.loadData(element) : null;
1✔
279
  }
280

281
  public static @NotNull PerlHostHandler<?, ?> getDefaultHandler() {
282
    return Objects.requireNonNull(EP.findSingle("localhost"), "Local handler MUST always present");
1✔
283
  }
284
}
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