@NotNull
  LogicalPosition visualToLogicalPosition(@NotNull VisualPosition pos) {
    int line = pos.line;
    int column = pos.column;
    int visualLineCount = myView.getEditor().getVisibleLineCount();
    if (line >= visualLineCount) {
      return new LogicalPosition(line - visualLineCount + myDocument.getLineCount(), column, pos.leansRight);
    }
    int offset = visualLineToOffset(line);
    int logicalLine = myDocument.getLineNumber(offset);
    int maxVisualColumn = 0;
    int maxLogicalColumn = 0;
    int maxOffset = offset;
    LogicalPosition delayedResult = null;
    boolean delayedInlay = false;
    for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false)) {
      FoldRegion foldRegion = fragment.getCurrentFoldRegion();
      if (foldRegion instanceof CustomFoldRegion) {
        return new LogicalPosition(fragment.getStartLogicalLine(), fragment.getStartLogicalColumn());
      }
      int minColumn = fragment.getStartVisualColumn();
      int maxColumn = fragment.getEndVisualColumn();
      if (delayedResult != null && minColumn != maxColumn) {
        return delayedInlay ? delayedResult.leanForward(fragment.getCurrentInlay() == null) : delayedResult;
      }
      if (column < minColumn || column == minColumn && !pos.leansRight && minColumn != maxColumn) {
        return offsetToLogicalPosition(offset);
      }
      if (column > minColumn && column < maxColumn ||
          column == minColumn ||
          column == maxColumn && !pos.leansRight) {
        // for visual positions between adjacent inlays, we return same result as for visual position before the first one
        delayedInlay = fragment.getCurrentInlay() != null;
        delayedResult =
          new LogicalPosition(column == maxColumn ? fragment.getEndLogicalLine() : fragment.getStartLogicalLine(),
                              fragment.visualToLogicalColumn(column),
                              foldRegion != null ? column < maxColumn :
                              !delayedInlay && fragment.isRtl() ^ pos.leansRight);
        // delaying result to check whether there's an 'invisible' fold region going next
        if (column != maxColumn) return delayedResult;
      }
      maxLogicalColumn = logicalLine == fragment.getEndLogicalLine() ? Math.max(maxLogicalColumn, fragment.getMaxLogicalColumn()) :
                         fragment.getMaxLogicalColumn();
      maxVisualColumn = maxColumn;
      logicalLine = fragment.getEndLogicalLine();
      maxOffset = Math.max(maxOffset, fragment.getMaxOffset());
    }
    if (delayedResult != null && !delayedInlay) return delayedResult;
    if (myView.getEditor().getSoftWrapModel().getSoftWrap(maxOffset) == null) {
      boolean[] leansForward = new boolean[] {pos.leansRight};
      int resultColumn = maxLogicalColumn + visToLogWithInlays(logicalLine, column - maxVisualColumn, leansForward);
      if (resultColumn < 0) resultColumn = Integer.MAX_VALUE; // guarding against overflow
      return new LogicalPosition(logicalLine, resultColumn, leansForward[0]);
    }
    else {
      return offsetToLogicalPosition(maxOffset).leanForward(true);
    }
  }
@NotNull
  LogicalPosition visualToLogicalPosition(@NotNull VisualPosition pos)