| /*=============================================================================# |
| # Copyright (c) 2005, 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.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()); |
| } |
| } |
| |
| } |