blob: ff6e01f82f2af5b2bc0f84747b7306f1bb3588b2 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.ui.sourceediting;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Region;
import org.eclipse.statet.ltk.ast.core.AstInfo;
import org.eclipse.statet.ltk.ast.core.AstNode;
import org.eclipse.statet.ltk.ast.core.util.AstSelection;
import org.eclipse.statet.ltk.model.core.element.SourceUnit;
/**
* Command handler to expand the text selection according the AST.
*/
public abstract class StructureSelectHandler extends AbstractHandler {
public static class Enclosing extends StructureSelectHandler {
public Enclosing(final SourceEditor editor, final StructureSelectionHistory history) {
super(editor, history);
}
@Override
IRegion concreteNewSelectionRange(final AstSelection selection) {
final AstNode covering= selection.getCovering();
return createRegion(covering.getStartOffset(), covering.getEndOffset());
}
}
public static class Next extends StructureSelectHandler {
public Next(final SourceEditor editor, final StructureSelectionHistory history) {
super(editor, history);
}
@Override
IRegion concreteNewSelectionRange(final AstSelection selection) {
final AstNode covering= selection.getCovering();
AstNode child= selection.getChildLastTouching();
if (child == null || selection.getStopOffset() >= child.getEndOffset()) {
child= selection.getChildAfter();
}
if (child != null) {
return createRegion(selection.getStartOffset(), child.getEndOffset());
}
return createRegion(covering.getStartOffset(), covering.getEndOffset());
}
}
public static class Previous extends StructureSelectHandler {
public Previous(final SourceEditor editor, final StructureSelectionHistory history) {
super(editor, history);
}
@Override
IRegion concreteNewSelectionRange(final AstSelection selection) {
final AstNode covering= selection.getCovering();
AstNode child= selection.getChildFirstTouching();
if (child == null || selection.getStartOffset() <= child.getStartOffset()) {
child= selection.getChildBefore();
}
if (child != null) {
return createRegion(selection.getStopOffset(), child.getStartOffset());
}
return createRegion(covering.getEndOffset(), covering.getStartOffset());
}
}
private final SourceEditor sourceEditor;
private final StructureSelectionHistory selectionHistory;
protected StructureSelectHandler(final SourceEditor editor, final StructureSelectionHistory history) {
super();
assert (editor != null);
assert (history != null);
this.sourceEditor= editor;
this.selectionHistory= history;
}
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
final SourceUnit su= this.sourceEditor.getSourceUnit();
if (su == null) {
return null;
}
final AstInfo astInfo= su.getAstInfo(null, true, new NullProgressMonitor());
if (astInfo == null) {
return null;
}
final ITextSelection selection= getTextSelection();
final IRegion newRange= getNewSelectionRange(selection.getOffset(), selection.getOffset()+selection.getLength(), astInfo);
if (newRange != null) {
this.selectionHistory.remember(new Region(selection.getOffset(), selection.getLength()));
try {
this.selectionHistory.ignoreSelectionChanges();
this.sourceEditor.selectAndReveal(newRange.getOffset(), newRange.getLength());
}
finally {
this.selectionHistory.listenToSelectionChanges();
}
}
return null;
}
public final IRegion getNewSelectionRange(final int oldStart, final int oldStop, final AstInfo ast) {
final AstSelection selection= AstSelection.search(ast.getRoot(),
oldStart, oldStop, AstSelection.MODE_COVERING_GREATER );
if (selection.getCovering() == null) {
return null;
}
return concreteNewSelectionRange(selection);
}
/**
* Subclasses determine the actual new selection.
*/
abstract IRegion concreteNewSelectionRange(AstSelection selection);
protected final ITextSelection getTextSelection() {
return (ITextSelection) this.sourceEditor.getViewer().getSelectionProvider().getSelection();
}
protected final IRegion createRegion(int start, final int stop) {
if (start < 0) {
start= 0;
}
return new Region(start, stop-start);
}
}