public static Promise<List<? extends XLineBreakpointType.XLineBreakpointVariant>>
  getLineBreakpointVariants(@NotNull final Project project,
                            @NotNull List<? extends XLineBreakpointType> types,
                            @NotNull final XSourcePosition position) {
    if (types.isEmpty()) {
      return rejectedPromise("non-empty types are expected");
    }

    boolean multipleTypes = types.size() > 1;
    List<Promise<List<? extends XLineBreakpointType.XLineBreakpointVariant>>> promises = new SmartList<>();
    for (XLineBreakpointType type : types) {
      promises.add(type.computeVariantsAsync(project, position).then(o -> {
        if (((List<?>)o).isEmpty() && multipleTypes) {
          // We have multiple types, but no non-default variants for this type. So we just create one.
          return Collections.singletonList(createDefaultBreakpointVariant(position, type));
        }
        else {
          return o;
        }
      }));
    }
    return Promises.collectResults(promises).then(v -> {
      var variants = StreamEx.of(v).toFlatList(l -> l);
      if (variants.isEmpty()) {
        assert !multipleTypes;
        return Collections.singletonList(createDefaultBreakpointVariant(position, types.get(0)));
      } else {
        return variants;
      }
    });
  }
public static Promise<List<? extends XLineBreakpointType.XLineBreakpointVariant>>
  getLineBreakpointVariants(@NotNull final Project project,
                            @NotNull List<? extends XLineBreakpointType> types,
                            @NotNull final XSourcePosition position)