public void selectionChanged(SelectionChangedEvent event) {
    if (!VrapperPlugin.isVrapperEnabled()
        || !(event.getSelection() instanceof TextSelection)
        || selectionService.isSelectionInProgress()) {
      return;
    }

    TextSelection selection = (TextSelection) event.getSelection();
    // selection.isEmpty() is false even if length == 0, don't use it
    if (selection.getLength() == 0) {
      // Explicitly reset selection. EclipseCursorAndSelection's SelectionChangeListener is
      // only fired after this listener, returning a stale selection during a mode switch.
      selectionService.setSelection(null);
      try {
        int offset = selection.getOffset();
        IRegion lineInfo = textViewer.getDocument().getLineInformationOfOffset(offset);
        // Checks if cursor is just before line end because Normalmode will move it.
        if (lineInfo.getOffset() + lineInfo.getLength() == offset) {
          selectionResetOffset = offset;
        } else {
          selectionResetOffset = -1;
        }
      } catch (BadLocationException e) {
        VrapperLog.error("Received bad selection offset in selectionchange handler", e);
      }
      EditorMode currentMode = editorAdaptor.getMode(editorAdaptor.getCurrentModeName());
      // User cleared selection or moved caret with mouse in a temporary mode.
      if (currentMode instanceof TemporaryMode) {
        editorAdaptor.changeModeSafely(InsertMode.NAME);
      } else if (currentMode instanceof AbstractVisualMode) {
        editorAdaptor.changeModeSafely(NormalMode.NAME);
        // Cursor can be after the line if an Eclipse operation cleared the selection, e.g. undo
      } else if (currentMode instanceof CommandBasedMode) {
        CommandBasedMode commandMode = (CommandBasedMode) currentMode;
        commandMode.placeCursor(StickyColumnPolicy.RESET_EOL);
      }
    } else if (!VrapperPlugin.isMouseDown()
        || !editorAdaptor.getConfiguration().get(Options.VISUAL_MOUSE)) {
      // Mark selection as "conflicted" - we're in Normal mode but somehow a selection exists
      if (NormalMode.NAME.equals(editorAdaptor.getCurrentModeName())) {
        editorAdaptor.getCursorService().setCaret(CaretType.UNDERLINE);
      }
      return;
      // Detect if a reverse selection got its last character chopped off.
    } else if (selectionResetOffset != -1
        && (selection.getOffset() + selection.getLength() + 1) == selectionResetOffset) {
      textViewer.setSelectedRange(selectionResetOffset, -(selection.getLength() + 1));
      selectionResetOffset = -1;
    } else if (selection.getLength() != 0) {
      if (NormalMode.NAME.equals(editorAdaptor.getCurrentModeName())) {
        editorAdaptor.changeModeSafely(VisualMode.NAME, AbstractVisualMode.KEEP_SELECTION_HINT);
      } else if (InsertMode.NAME.equals(editorAdaptor.getCurrentModeName())) {
        editorAdaptor.changeModeSafely(
            TempVisualMode.NAME,
            AbstractVisualMode.KEEP_SELECTION_HINT,
            InsertMode.DONT_MOVE_CURSOR);
      }
      // Store the selection - user might click with mouse and immediately destroy selection
      editorAdaptor.rememberLastActiveSelection();
    }
  }