/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.editor.orion.client; import com.google.web.bindery.event.shared.HandlerRegistration; import org.eclipse.che.ide.editor.orion.client.jso.ModelChangedEventOverlay; import org.eclipse.che.ide.editor.orion.client.jso.OrionEditorOverlay; import org.eclipse.che.ide.editor.orion.client.jso.OrionPixelPositionOverlay; import org.eclipse.che.ide.editor.orion.client.jso.OrionSelectionOverlay; import org.eclipse.che.ide.editor.orion.client.jso.OrionTextModelOverlay; import org.eclipse.che.ide.editor.orion.client.jso.OrionTextModelOverlay.EventHandler; import org.eclipse.che.ide.editor.orion.client.jso.OrionTextViewOverlay; import org.eclipse.che.ide.api.editor.document.AbstractDocument; import org.eclipse.che.ide.api.editor.document.Document; import org.eclipse.che.ide.api.editor.events.CursorActivityHandler; import org.eclipse.che.ide.api.editor.events.DocumentChangeEvent; import org.eclipse.che.ide.api.editor.events.HasCursorActivityHandlers; import org.eclipse.che.ide.api.editor.position.PositionConverter; import org.eclipse.che.ide.api.editor.text.LinearRange; import org.eclipse.che.ide.api.editor.text.TextPosition; import org.eclipse.che.ide.api.editor.text.TextRange; /** * The implementation of {@link Document} for Orion. * * @author "Mickaƫl Leduque" */ public class OrionDocument extends AbstractDocument { /** The maximum number of lines that may be visible at the top of the text view after setting selection range. */ private final static int MARGIN_TOP = 15; private final OrionTextViewOverlay textViewOverlay; private final OrionPositionConverter positionConverter; private final HasCursorActivityHandlers hasCursorActivityHandlers; private final OrionEditorOverlay editorOverlay; public OrionDocument(OrionTextViewOverlay textViewOverlay, HasCursorActivityHandlers hasCursorActivityHandlers, OrionEditorOverlay editorOverlay) { this.textViewOverlay = textViewOverlay; this.hasCursorActivityHandlers = hasCursorActivityHandlers; this.editorOverlay = editorOverlay; this.positionConverter = new OrionPositionConverter(); this.editorOverlay.getModel().addEventListener("Changed", new EventHandler<ModelChangedEventOverlay>() { @Override public void onEvent(ModelChangedEventOverlay parameter) { fireDocumentChangeEvent(parameter); } }, true); } private void fireDocumentChangeEvent(final ModelChangedEventOverlay param) { int startOffset = param.start(); int addedCharCount = param.addedCharCount(); int removedCharCount = param.removedCharCount(); String text = editorOverlay.getModel().getText(startOffset, startOffset + addedCharCount); final DocumentChangeEvent event = new DocumentChangeEvent(this, startOffset, addedCharCount, text, removedCharCount); // according to https://github.com/codenvy/che-core/pull/122 getDocEventBus().fireEvent(event); } @Override public TextPosition getPositionFromIndex(final int index) { final int line = this.editorOverlay.getModel().getLineAtOffset(index); if (line == -1) { return null; } final int lineStart = this.editorOverlay.getModel().getLineStart(line); if (lineStart == -1) { return null; } final int character = index - lineStart; if (character < 0) { return null; } return new TextPosition(line, character); } @Override public int getIndexFromPosition(final TextPosition position) { final int lineStart = this.editorOverlay.getModel().getLineStart(position.getLine()); if (lineStart == -1) { return -1; } final int result = lineStart + position.getCharacter(); final int lineEnd = this.editorOverlay.getModel().getLineEnd(position.getLine()); if (lineEnd < result) { return -1; } return result; } @Override public void setCursorPosition(final TextPosition position) { this.editorOverlay.setCaretOffset(getIndexFromPosition(position)); } @Override public int getLineAtOffset(int offset) { return this.editorOverlay.getTextView().getLineAtOffset(offset); } @Override public int getLineStart(int lineIndex) { return editorOverlay.getTextView().getLineStart(lineIndex); } @Override public TextPosition getCursorPosition() { final int offset = this.editorOverlay.getCaretOffset(); return getPositionFromIndex(offset); } public int getCursorOffset() { return this.editorOverlay.getTextView().getCaretOffset(); } @Override public int getLineCount() { return this.editorOverlay.getModel().getLineCount(); } @Override public HandlerRegistration addCursorHandler(final CursorActivityHandler handler) { return this.hasCursorActivityHandlers.addCursorActivityHandler(handler); } @Override public String getContents() { return editorOverlay.getText(); } @Override public String getContentRange(final int offset, final int length) { return this.editorOverlay.getModel().getText(offset, offset + length); } @Override public String getContentRange(final TextRange range) { final int startOffset = getIndexFromPosition(range.getFrom()); final int endOffset = getIndexFromPosition(range.getTo()); return this.editorOverlay.getModel().getText(startOffset, endOffset); } public PositionConverter getPositionConverter() { return this.positionConverter; } private class OrionPositionConverter implements PositionConverter { @Override public PixelCoordinates textToPixel(TextPosition textPosition) { final int textOffset = getIndexFromPosition(textPosition); return offsetToPixel(textOffset); } @Override public PixelCoordinates offsetToPixel(int textOffset) { OrionPixelPositionOverlay location = textViewOverlay.getLocationAtOffset(textOffset); location.setY(location.getY() + textViewOverlay.getLineHeight()); location = textViewOverlay.convert(location, "document", "page"); return new PixelCoordinates(location.getX(), location.getY()); } @Override public TextPosition pixelToText(PixelCoordinates coordinates) { final int offset = pixelToOffset(coordinates); return getPositionFromIndex(offset); } @Override public int pixelToOffset(PixelCoordinates coordinates) { return textViewOverlay.getOffsetAtLocation(coordinates.getX(), coordinates.getY()); } } public void replace(int offset, int length, String text) { this.editorOverlay.getModel().setText(text, offset, offset + length); } @Override public void replace(int startLine, int startChar, int endLine, int endChar, String text) { OrionTextModelOverlay model = editorOverlay.getModel(); int lineStart = model.getLineStart(startLine); int lineEnd = model.getLineStart(endLine); editorOverlay.setText(text, lineStart + startChar, lineEnd + endChar); } public int getContentsCharCount() { return this.editorOverlay.getModel().getCharCount(); } @Override public String getLineContent(final int line) { return this.editorOverlay.getModel().getLine(line); } @Override public TextRange getTextRangeForLine(final int line) { final int startOffset = this.textViewOverlay.getModel().getLineStart(line); final int endOffset = this.textViewOverlay.getModel().getLineEnd(line); final int length = endOffset - startOffset; return new TextRange(new TextPosition(line, 0), new TextPosition(line, length)); } @Override public LinearRange getLinearRangeForLine(final int line) { return LinearRange.createWithStart(this.textViewOverlay.getModel().getLineStart(line)) .andEnd(textViewOverlay.getModel().getLineEnd(line)); } @Override public TextRange getSelectedTextRange() { final OrionSelectionOverlay selection = this.textViewOverlay.getSelection(); final int start = selection.getStart(); final TextPosition startPosition = getPositionFromIndex(start); final int end = selection.getEnd(); final TextPosition endPosition = getPositionFromIndex(end); return new TextRange(startPosition, endPosition); } @Override public LinearRange getSelectedLinearRange() { final OrionSelectionOverlay selection = this.textViewOverlay.getSelection(); final int start = selection.getStart(); final int end = selection.getEnd(); return LinearRange.createWithStart(start).andEnd(end); } /** {@inheritDoc} */ @Override public void setSelectedRange(LinearRange range, boolean show) { int startOffset = range.getStartOffset(); editorOverlay.setSelection(startOffset, startOffset + range.getLength(), show); TextPosition position = getPositionFromIndex(startOffset); if (show && position != null) { int lineNumber = position.getLine(); int topIndex = lineNumber - MARGIN_TOP; editorOverlay.getTextView().setTopIndex(topIndex > 0 ? topIndex : 0); } } @Override public void setSelectedRange(TextRange range) { setSelectedRange(range, false); } @Override public void setSelectedRange(TextRange range, boolean show) { int lineStart = getLineStart(range.getFrom().getLine()); int lineEnd = getLineStart(range.getTo().getLine()); LinearRange linearRange = LinearRange.createWithStart(lineStart + range.getFrom().getCharacter()).andEnd(lineEnd + range.getTo().getCharacter()); setSelectedRange(linearRange, show); } }