// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.collide.client.code;
import com.google.collide.client.editor.Buffer;
import com.google.collide.client.editor.Editor;
import com.google.collide.client.editor.selection.SelectionModel;
import com.google.collide.client.util.PathUtil;
import com.google.collide.shared.document.Document;
import com.google.collide.shared.document.LineFinder;
import com.google.collide.shared.document.LineInfo;
import com.google.collide.shared.document.util.LineUtils;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import elemental.js.util.JsMapFromStringTo;
/**
* A simple class for saving/restoring the user's selection as he moves between
* files. This only persists the saved selections in the client's memory, not to
* the server. This will try to restore the relative position of the cursor on
* the screen too (e.g. if the cursor was 30px from the top of the viewport, it
* will stay 30px from the top of the viewport when restored).
*
* The client must call {@link #onBeforeDocumentChanged()} and
* {@link #onDocumentChanged(PathUtil)}.
*/
class SelectionRestorer {
private static class Selection {
final int baseColumn;
final int baseLineNumber;
final int cursorColumn;
final int cursorLineNumber;
/**
* Tracks the gap between the top of the viewport and the top of the
* cursor's line
*/
final int cursorScrollTopOffset;
Selection(int baseLineNumber, int baseColumn, int cursorLineNumber, int cursorColumn,
int cursorScrollTopOffset) {
this.baseColumn = baseColumn;
this.baseLineNumber = baseLineNumber;
this.cursorColumn = cursorColumn;
this.cursorLineNumber = cursorLineNumber;
this.cursorScrollTopOffset = cursorScrollTopOffset;
}
}
private final Editor editor;
private String fileEditSessionKey;
/** Map from file edit session key to {@link Selection} */
private final JsMapFromStringTo<Selection> selections = JsMapFromStringTo.create();
SelectionRestorer(Editor editor) {
this.editor = editor;
}
void onBeforeDocumentChanged() {
saveSelection();
}
private void saveSelection() {
if (fileEditSessionKey == null) {
return;
}
SelectionModel selectionModel = editor.getSelection();
Buffer buffer = editor.getBuffer();
int cursorLineNumber = selectionModel.getCursorLineNumber();
int cursorScrollTopOffset = buffer.calculateLineTop(cursorLineNumber) - buffer.getScrollTop();
selections.put(fileEditSessionKey, new Selection(selectionModel.getBaseLineNumber(),
selectionModel.getBaseColumn(), cursorLineNumber, selectionModel.getCursorColumn(),
cursorScrollTopOffset));
}
void onDocumentChanged(String fileEditSessionKey) {
this.fileEditSessionKey = fileEditSessionKey;
restoreSelection();
}
private void restoreSelection() {
if (fileEditSessionKey == null) {
return;
}
final Selection selection = selections.get(fileEditSessionKey);
if (selection == null) {
return;
}
Document document = editor.getDocument();
LineFinder lineFinder = document.getLineFinder();
LineInfo baseLineInfo =
lineFinder.findLine(Math.min(selection.baseLineNumber, document.getLastLineNumber()));
int baseColumn = LineUtils.rubberbandColumn(baseLineInfo.line(), selection.baseColumn);
final LineInfo cursorLineInfo =
lineFinder.findLine(Math.min(selection.cursorLineNumber, document.getLastLineNumber()));
int cursorColumn = LineUtils.rubberbandColumn(cursorLineInfo.line(), selection.cursorColumn);
editor.getSelection().setSelection(baseLineInfo, baseColumn, cursorLineInfo, cursorColumn);
// Defer to match editor's initially deferred scrolling
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
Buffer buffer = editor.getBuffer();
int targetScrollTop = buffer.calculateLineTop(cursorLineInfo.number())
- selection.cursorScrollTopOffset;
buffer.setScrollTop(Math.max(0, targetScrollTop));
}
});
}
}