blob: ed10a6e9925339f2bb286f1b8e20935b671ee2d1 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 2020 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.templates;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.templates.TemplateBuffer;
import org.eclipse.jface.text.templates.TemplateVariable;
import org.eclipse.text.edits.RangeMarker;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
@NonNullByDefault
public class TemplateUtils {
public static class EvaluatedTemplate {
private String content;
private @Nullable IRegion select;
private final String lineDelimiter;
private @Nullable AbstractDocument postEditDocument;
private @Nullable Position postEditSelectPosition;
public EvaluatedTemplate(final TemplateBuffer buffer, final String lineDelimiter) {
setContent(buffer.getString());
final TemplateVariable selectStartVariable= findVariable(buffer, SourceEditorContextType.SELECT_START_VAR_NAME);
final TemplateVariable selectEndVariable= findVariable(buffer, SourceEditorContextType.SELECT_END_VAR_NAME);
if (selectStartVariable != null && selectStartVariable.getOffsets().length == 1) {
this.select= new Region(selectStartVariable.getOffsets()[0],
(selectEndVariable != null && selectEndVariable.getOffsets().length == 1) ?
Math.max(selectEndVariable.getOffsets()[0] - selectStartVariable.getOffsets()[0], 0) : 0);
}
this.lineDelimiter= lineDelimiter;
}
/**
* Sets the evaluated template text
* @param content the text
*/
public void setContent(final String content) {
this.postEditDocument= null;
this.content= content;
}
/**
* Returns the evaluated template text
* @return the text
* */
public String getContent() {
return this.content;
}
/**
* Returns the region to select, if specified
*/
public @Nullable IRegion getRegionToSelect() {
return this.select;
}
/**
* Returns a document which can be used for further edits in the text.
* After edits are done, {@link #finishPostEdit()} must be called.
*
* @return a document with the template content
* @throws BadLocationException
*/
public AbstractDocument startPostEdit() throws BadLocationException {
if (this.postEditDocument == null) {
this.postEditDocument= new Document(getContent()) {
@Override
public String getDefaultLineDelimiter() {
return EvaluatedTemplate.this.lineDelimiter;
}
};
if (this.select != null) {
this.postEditSelectPosition= new Position(this.select.getOffset(), this.select.getLength());
this.postEditDocument.addPosition(this.postEditSelectPosition);
}
}
return this.postEditDocument;
}
/**
* See {@link #startPostEdit()}.
*/
public void finishPostEdit() {
setContent(this.postEditDocument.get());
if (this.postEditSelectPosition != null) {
this.select= (this.postEditSelectPosition.isDeleted) ? null :
new Region(this.postEditSelectPosition.getOffset(), this.postEditSelectPosition.getLength());
}
}
}
public static String searchIndentation(final IDocument document, final int offset) {
try {
final IRegion region= document.getLineInformationOfOffset(offset);
final String lineContent= document.get(region.getOffset(), region.getLength());
return searchIndentation(lineContent);
}
catch (final BadLocationException e) {
return ""; //$NON-NLS-1$
}
}
private static String searchIndentation(final String text) throws BadLocationException {
int i= 0;
for (; i < text.length(); i++) {
final char c= text.charAt(i);
if (!(c == ' ' || c == '\t')) {
break;
}
}
return text.substring(0, i);
}
public static void positionsToVariables(final List<TextEdit> positions, final TemplateVariable[] variables) {
final Iterator<TextEdit> iterator= positions.iterator();
for (final TemplateVariable variable : variables) {
final int[] offsets= new int[variable.getOffsets().length];
for (int j= 0; j < offsets.length; j++) {
offsets[j]= iterator.next().getOffset();
}
variable.setOffsets(offsets);
}
}
public static List<TextEdit> variablesToPositions(final TemplateVariable[] variables) {
final List<TextEdit> positions= new ArrayList<>(5);
for (final TemplateVariable variable : variables) {
final int[] offsets= variable.getOffsets();
// trim positions off whitespace
final String value= variable.getDefaultValue();
int wsStart= 0;
while (wsStart < value.length() && Character.isWhitespace(value.charAt(wsStart)) && !isLineDelimiterChar(value.charAt(wsStart))) {
wsStart++;
}
variable.getValues()[0]= value.substring(wsStart);
for (int j= 0; j != offsets.length; j++) {
offsets[j]+= wsStart;
positions.add(new RangeMarker(offsets[j], 0));
}
}
return positions;
}
public static @Nullable TemplateVariable findVariable(final TemplateBuffer buffer, final String variableType) {
final TemplateVariable[] variables= buffer.getVariables();
for (final TemplateVariable cand : variables) {
if (variableType.equals(cand.getType())) {
return cand;
}
}
return null;
}
private static boolean isLineDelimiterChar(final char c) {
return (c == '\r' || c == '\n');
}
/**
* Indents each line of the template (document) using the specified indentation (string).
* An empty last line is note indented.
*
* @param doc document with the template
* @param lineIndent string to use as line indentation
* @throws BadLocationException
*/
public static void indentTemplateDocument(final AbstractDocument doc, final String lineIndent)
throws BadLocationException {
final int lastLine= doc.getNumberOfLines()-1;
for (int templateLine= 0; templateLine < lastLine; templateLine++) {
doc.replace(doc.getLineOffset(templateLine), 0, lineIndent);
}
final int lineOffset= doc.getLineOffset(lastLine);
if (lineOffset != doc.getLength()) {
doc.replace(lineOffset, 0, lineIndent);
doc.replace(doc.getLength(), 0, doc.getDefaultLineDelimiter());
}
}
}