blob: 39237ed6934f186f725a8bec3a21e7960a700b9f [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 static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.templates.DocumentTemplateContext;
import org.eclipse.jface.text.templates.GlobalTemplateVariables;
import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver;
import org.eclipse.jface.text.templates.TemplateBuffer;
import org.eclipse.jface.text.templates.TemplateContext;
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.jface.text.templates.TemplateVariable;
import org.eclipse.jface.text.templates.TemplateVariableResolver;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.internal.ltk.ui.TemplatesMessages;
import org.eclipse.statet.ltk.core.ElementName;
import org.eclipse.statet.ltk.core.util.UserInfo;
import org.eclipse.statet.ltk.model.core.elements.ISourceUnit;
import org.eclipse.statet.ltk.model.core.elements.IWorkspaceSourceUnit;
@NonNullByDefault
public class SourceEditorContextType extends TemplateContextType {
public static class AuthorName extends SimpleTemplateVariableResolver {
public AuthorName() {
super("author_name", TemplatesMessages.Templates_Variable_AuthorName_description); //$NON-NLS-1$
}
@Override
protected String resolve(final TemplateContext context) {
return UserInfo.getAuthorInfo().getName();
}
}
public static class AuthorEmail extends SimpleTemplateVariableResolver {
public AuthorEmail() {
super("author_email", TemplatesMessages.Templates_Variable_AuthorEmail_description); //$NON-NLS-1$
}
@Override
protected String resolve(final TemplateContext context) {
return UserInfo.getAuthorInfo().getEmail();
}
}
/**
* Resolver that resolves to the variable defined in the context.
*/
protected static class CodeTemplatesVariableResolver extends TemplateVariableResolver {
public CodeTemplatesVariableResolver(final String type, final String description) {
super(type, description);
}
@Override
protected @Nullable String resolve(final TemplateContext context) {
return context.getVariable(getType());
}
}
protected static class InitialSelectionStart extends TemplateVariableResolver {
public InitialSelectionStart() {
super(SELECT_START_VAR_NAME, TemplatesMessages.Templates_Variable_SelectionBegin_description);
}
@Override
public void resolve(final TemplateVariable variable, final TemplateContext context) {
variable.setValue(""); //$NON-NLS-1$
variable.setUnambiguous(true);
}
}
protected static class InitialSelectionEnd extends TemplateVariableResolver {
public InitialSelectionEnd() {
super(SELECT_END_VAR_NAME, TemplatesMessages.Templates_Variable_SelectionEnd_description);
}
@Override
public void resolve(final TemplateVariable variable, final TemplateContext context) {
variable.setValue(""); //$NON-NLS-1$
variable.setUnambiguous(true);
}
}
/**
* Resolver for file name.
*/
protected static class File extends SimpleTemplateVariableResolver {
public File() {
super(FILE_NAME_VAR_NAME, TemplatesMessages.Templates_Variable_File_description);
}
@Override
protected String resolve(final TemplateContext context) {
if (context instanceof IWorkbenchTemplateContext) {
final ISourceUnit su= ((IWorkbenchTemplateContext) context).getSourceUnit();
if (su != null) {
final ElementName elementName= su.getElementName();
if (elementName != null) {
return elementName.getSegmentName();
}
}
}
return ""; //$NON-NLS-1$
}
}
/**
* Resolver for project name.
*/
protected static class Project extends SimpleTemplateVariableResolver {
public Project() {
super("enclosing_project", TemplatesMessages.Templates_Variable_EnclosingProject_description); //$NON-NLS-1$
}
@Override
protected String resolve(final TemplateContext context) {
if (context instanceof IWorkbenchTemplateContext) {
final ISourceUnit su= ((IWorkbenchTemplateContext) context).getSourceUnit();
if (su instanceof IWorkspaceSourceUnit) {
return ((IWorkspaceSourceUnit) su).getResource().getProject().getName();
}
}
return ""; //$NON-NLS-1$
}
}
/**
* Resolver for ToDo-tags. Intend to extend and overwrite {@link #getTag(ISourceUnit)}
*/
protected static class Todo extends SimpleTemplateVariableResolver {
public Todo() {
super("todo", TemplatesMessages.Templates_Variable_ToDo_description); //$NON-NLS-1$
}
@Override
protected String resolve(final TemplateContext context) {
if (context instanceof IWorkbenchTemplateContext) {
final String tag= getTag(((IWorkbenchTemplateContext) context).getSourceUnit());
if (tag != null) {
return tag;
}
}
return "TODO"; //$NON-NLS-1$
}
protected @Nullable String getTag(final @Nullable ISourceUnit su) {
return null;
}
}
private static class MyLineSelection extends GlobalTemplateVariables.LineSelection {
@Override
protected String resolve(final TemplateContext context) {
String value= super.resolve(context);
final int length= value.length();
if (length > 0 && context instanceof DocumentTemplateContext) {
final char c= value.charAt(length-1);
if (c != '\n' && c != '\r') {
value+= TextUtilities.getDefaultLineDelimiter(((DocumentTemplateContext) context).getDocument());
}
}
return value;
}
}
public static final String FILE_NAME_VAR_NAME= "file_name"; //$NON-NLS-1$
public static final String SELECT_START_VAR_NAME= "selection_begin"; //$NON-NLS-1$
public static final String SELECT_END_VAR_NAME= "selection_end"; ////$NON-NLS-1$
public SourceEditorContextType(final String id, final String name) {
super(id, name);
}
public SourceEditorContextType(final String id) {
super(id);
}
public SourceEditorContextType() {
super();
}
protected void addCommonVariables() {
addResolver(new GlobalTemplateVariables.Dollar());
addResolver(new GlobalTemplateVariables.Date());
addResolver(new GlobalTemplateVariables.Year());
addResolver(new GlobalTemplateVariables.Time());
addResolver(new GlobalTemplateVariables.User());
addResolver(new AuthorName());
addResolver(new AuthorEmail());
addResolver(new Project());
}
protected void addEditorVariables() {
addResolver(new GlobalTemplateVariables.Cursor());
addResolver(new GlobalTemplateVariables.WordSelection());
addResolver(new MyLineSelection());
}
protected void addSourceUnitGenerationVariables() {
addResolver(new File());
addResolver(new InitialSelectionStart());
addResolver(new InitialSelectionEnd());
}
@Override
public void resolve(final TemplateBuffer buffer, final TemplateContext context) throws MalformedTreeException, BadLocationException {
nonNullAssert(context);
final TemplateVariable[] variables= buffer.getVariables();
final IDocument document= new Document(buffer.getString());
final List<TextEdit> positions= TemplateUtils.variablesToPositions(variables);
final List<TextEdit> edits= new ArrayList<>(5);
// iterate over all variables and try to resolve them
for (int i= 0; i != variables.length; i++) {
final TemplateVariable variable= variables[i];
if (variable.isUnambiguous()) {
continue;
}
// remember old values
final int[] oldOffsets= variable.getOffsets();
final int oldLength= variable.getLength();
final String oldValue= variable.getDefaultValue();
final String type= variable.getType();
TemplateVariableResolver resolver= getResolver(type);
if (resolver == null) {
resolver= new TemplateVariableResolver();
resolver.setType(type);
}
resolver.resolve(variable, context);
final String value= variable.getDefaultValue();
final String[] ln= document.getLegalLineDelimiters();
final boolean multiLine= (TextUtilities.indexOf(ln, value, 0)[0] != -1);
if (!oldValue.equals(value)) {
// update buffer to reflect new value
for (int k= 0; k != oldOffsets.length; k++) {
String thisValue= value;
if (multiLine) {
final String indent= TemplateUtils.searchIndentation(document, oldOffsets[k]);
if (indent.length() > 0) {
final StringBuilder temp= new StringBuilder(thisValue);
int offset= 0;
while (true) {
final int[] search= TextUtilities.indexOf(ln, temp.toString(), offset);
if (search[0] == -1) {
break;
}
offset= search[0] + ln[search[1]].length();
temp.insert(offset, indent);
offset+= indent.length();
}
thisValue= temp.toString();
}
}
edits.add(new ReplaceEdit(oldOffsets[k], oldLength, thisValue));
}
}
}
final MultiTextEdit edit= new MultiTextEdit(0, document.getLength());
edit.addChildren(positions.toArray(new TextEdit[positions.size()]));
edit.addChildren(edits.toArray(new TextEdit[edits.size()]));
edit.apply(document, TextEdit.UPDATE_REGIONS);
TemplateUtils.positionsToVariables(positions, variables);
buffer.setContent(document.get(), variables);
}
@Override
public int hashCode() {
return getId().hashCode();
}
@Override
public boolean equals(final @Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof SourceEditorContextType) {
final SourceEditorContextType other= (SourceEditorContextType) obj;
return getId().equals(other.getId());
}
return false;
}
}