public static Editor create(
     @NotNull final DocumentWindowImpl documentRange,
     @NotNull final EditorImpl editor,
     @NotNull final PsiFile injectedFile) {
   assert documentRange.isValid();
   assert injectedFile.isValid();
   EditorWindow window;
   synchronized (allEditors) {
     for (EditorWindow editorWindow : allEditors) {
       if (editorWindow.getDocument() == documentRange && editorWindow.getDelegate() == editor) {
         editorWindow.myInjectedFile = injectedFile;
         if (editorWindow.isValid()) {
           return editorWindow;
         }
       }
       if (editorWindow.getDocument().areRangesEqual(documentRange)) {
         // int i = 0;
       }
     }
     window = new EditorWindow(documentRange, editor, injectedFile, documentRange.isOneLine());
     allEditors.add(window);
   }
   assert window.isValid();
   return window;
 }
 @Override
 public int calcColumnNumber(
     @NotNull final CharSequence text, final int start, final int offset, final int tabSize) {
   int hostStart = myDocumentWindow.injectedToHost(start);
   int hostOffset = myDocumentWindow.injectedToHost(offset);
   return myDelegate.calcColumnNumber(
       myDelegate.getDocument().getText(), hostStart, hostOffset, tabSize);
 }
 @Override
 @NotNull
 public LogicalPosition offsetToLogicalPosition(final int offset, boolean softWrapAware) {
   assert isValid();
   int lineNumber = myDocumentWindow.getLineNumber(offset);
   int lineStartOffset = myDocumentWindow.getLineStartOffset(lineNumber);
   int column = calcLogicalColumnNumber(offset - lineStartOffset, lineNumber, lineStartOffset);
   return new LogicalPosition(lineNumber, column);
 }
  private int calcOffset(int col, int lineNumber, int lineStartOffset) {
    if (myDocumentWindow.getTextLength() == 0) return 0;

    int end = myDocumentWindow.getLineEndOffset(lineNumber);

    CharSequence text = myDocumentWindow.getCharsSequence();
    return EditorUtil.calcOffset(
        this, text, lineStartOffset, end, col, EditorUtil.getTabSize(myDelegate), null);
  }
 private LogicalPosition fitInsideEditor(LogicalPosition pos) {
   int lineCount = myDocumentWindow.getLineCount();
   if (pos.line >= lineCount) {
     pos = new LogicalPosition(lineCount - 1, pos.column);
   }
   int lineLength =
       myDocumentWindow.getLineEndOffset(pos.line) - myDocumentWindow.getLineStartOffset(pos.line);
   if (pos.column >= lineLength) {
     pos = new LogicalPosition(pos.line, Math.max(0, lineLength - 1));
   }
   return pos;
 }
  private int calcLogicalColumnNumber(int offsetInLine, int lineNumber, int lineStartOffset) {
    if (myDocumentWindow.getTextLength() == 0) return 0;

    if (offsetInLine == 0) return 0;
    int end = myDocumentWindow.getLineEndOffset(lineNumber);
    if (offsetInLine > end - lineStartOffset) offsetInLine = end - lineStartOffset;

    CharSequence text = myDocumentWindow.getCharsSequence();
    return EditorUtil.calcColumnNumber(
        this,
        text,
        lineStartOffset,
        lineStartOffset + offsetInLine,
        EditorUtil.getTabSize(myDelegate));
  }
  public boolean equals(final Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    final EditorWindow that = (EditorWindow) o;

    DocumentWindow thatWindow = that.getDocument();
    return myDelegate.equals(that.myDelegate) && myDocumentWindow.equals(thatWindow);
  }
  @NotNull
  public LogicalPosition hostToInjected(@NotNull LogicalPosition hPos) {
    assert isValid();
    DocumentEx hostDocument = myDelegate.getDocument();
    int hLineEndOffset =
        hPos.line >= hostDocument.getLineCount()
            ? hostDocument.getTextLength()
            : hostDocument.getLineEndOffset(hPos.line);
    LogicalPosition hLineEndPos = myDelegate.offsetToLogicalPosition(hLineEndOffset);
    if (hLineEndPos.column < hPos.column) {
      // in virtual space
      LogicalPosition iPos = myDocumentWindow.hostToInjectedInVirtualSpace(hPos);
      if (iPos != null) {
        return iPos;
      }
    }

    int hOffset = myDelegate.logicalPositionToOffset(hPos);
    int iOffset = myDocumentWindow.hostToInjected(hOffset);
    return offsetToLogicalPosition(iOffset);
  }
  @NotNull
  public LogicalPosition injectedToHost(@NotNull LogicalPosition pos) {
    assert isValid();
    // beware the virtual space
    int column = pos.column;
    int lineStartOffset = myDocumentWindow.getLineStartOffset(pos.line);
    int lineEndOffset = myDocumentWindow.getLineEndOffset(pos.line);
    if (column > lineEndOffset - lineStartOffset) {
      // in virtual space, calculate the host pos as an offset from the line end
      int delta = column - (lineEndOffset - lineStartOffset);

      int baseOffsetInHost = myDocumentWindow.injectedToHost(lineEndOffset);
      LogicalPosition lineStartPosInHost = myDelegate.offsetToLogicalPosition(baseOffsetInHost);
      return new LogicalPosition(lineStartPosInHost.line, lineStartPosInHost.column + delta);
    } else {
      int offset = lineStartOffset + column;
      int hostOffset = getDocument().injectedToHost(offset);
      int hostLineNumber = getDocument().getDelegate().getLineNumber(hostOffset);
      int hostLineStart = getDocument().getDelegate().getLineStartOffset(hostLineNumber);

      return new LogicalPosition(hostLineNumber, hostOffset - hostLineStart);
    }
  }
 @Override
 public int calcColumnNumber(int offset, int lineIndex) {
   return myDelegate.calcColumnNumber(
       myDocumentWindow.injectedToHost(offset), myDocumentWindow.injectedToHostLine(lineIndex));
 }
 public int hashCode() {
   return myDocumentWindow.hashCode();
 }
 @Override
 public int logicalPositionToOffset(@NotNull final LogicalPosition pos) {
   int lineStartOffset = myDocumentWindow.getLineStartOffset(pos.line);
   return calcOffset(pos.column, pos.line, lineStartOffset);
 }
 @Override
 public void repaint(final int startOffset, final int endOffset) {
   assert isValid();
   myDelegate.repaint(
       myDocumentWindow.injectedToHost(startOffset), myDocumentWindow.injectedToHost(endOffset));
 }
 private boolean isValid() {
   return !isDisposed()
       && !myInjectedFile.getProject().isDisposed()
       && myInjectedFile.isValid()
       && myDocumentWindow.isValid();
 }