mirror of
https://github.com/processing/processing4.git
synced 2026-02-12 01:50:44 +01:00
Massive refactoring of the editor plugin. Added a feature plugin and update site controlling three smaller plugins. Tons of code cleanup. Builds and runs, but still missing a proper compile chain.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package processing.plugin.ui;
|
||||
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
|
||||
/**
|
||||
* Container class for all the methods related to logging exceptions and other such
|
||||
* information that is useful to have written to a log file somewhere.
|
||||
*
|
||||
* @author lonnen
|
||||
*/
|
||||
public class ProcessingLog {
|
||||
|
||||
// Who needs a constructor?
|
||||
|
||||
/**
|
||||
* Convenience method for appending a string to the log file.
|
||||
* Don't use this if there is an error.
|
||||
*
|
||||
* @param message something to append to the log file
|
||||
*/
|
||||
public static void logInfo(String message){
|
||||
log(IStatus.INFO, IStatus.OK, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for appending an unexpected exception to the log file
|
||||
*
|
||||
* @param exception some problem
|
||||
*/
|
||||
public static void logError(Throwable exception){
|
||||
logError("Unexpected Exception", exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for appending an exception with a message
|
||||
*
|
||||
* @param message a message, preferably something about the problem
|
||||
* @param exception the problem
|
||||
*/
|
||||
public static void logError(String message, Throwable exception){
|
||||
log(IStatus.ERROR, IStatus.OK, message, exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter method that creates the appropriate status to be logged
|
||||
*
|
||||
* @param severity integer code indicating the type of message
|
||||
* @param code plug-in-specific status code
|
||||
* @param message a human readable message
|
||||
*/
|
||||
public static void log(int severity, int code, String message, Throwable exception){
|
||||
log(createStatus(severity, code, message, exception));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a status object to log
|
||||
*
|
||||
* @param severity integer code indicating the type of message
|
||||
* @param code plug-in-specific status code
|
||||
* @param message a human readable message
|
||||
* @param a low-level exception, or null
|
||||
* @return status object
|
||||
*/
|
||||
public static IStatus createStatus(int severity, int code, String message, Throwable exception){
|
||||
return new Status(severity, ProcessingPlugin.PLUGIN_ID, code, message, exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a status to the log
|
||||
*
|
||||
* @param status
|
||||
*/
|
||||
public static void log(IStatus status){
|
||||
ProcessingPlugin.getDefault().getLog().log(status);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package processing.plugin.ui;
|
||||
|
||||
import org.eclipse.jface.text.rules.RuleBasedScanner;
|
||||
import org.eclipse.ui.plugin.AbstractUIPlugin;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import processing.plugin.ui.processingeditor.ProcessingPartitionScanner;
|
||||
import processing.plugin.ui.processingeditor.language.ProcessingCodeScanner;
|
||||
import processing.plugin.ui.processingeditor.util.ProcessingColorProvider;
|
||||
|
||||
/**
|
||||
* Controls Processing Plugin user interface elements.
|
||||
* <p>
|
||||
* It provides convenience methods and controls elements shared by editors and other UI elements
|
||||
* such as the document provider or the partitioning scanner.
|
||||
*/
|
||||
public class ProcessingPlugin extends AbstractUIPlugin {
|
||||
|
||||
/** The ID of the Processing UI Plugin */
|
||||
public static final String PLUGIN_ID = "processing.plugin.ui"; //$NON-NLS-1$
|
||||
|
||||
/** The ID of a planned but unimplemented Processing Perspective */ //TODO Implement P5 Perspective
|
||||
public static final String ID_PERSPECTIVE = PLUGIN_ID + ".ProcessingPerspective";
|
||||
|
||||
/** The ID of the processing */
|
||||
public static final String PROCESSING_PARTITIONING = "__processing_partitioning";
|
||||
|
||||
/** The shared plugin instance */
|
||||
private static ProcessingPlugin plugin;
|
||||
|
||||
// Supporting Objects
|
||||
private ProcessingPartitionScanner fPartitionScanner;
|
||||
private ProcessingColorProvider fColorProvider;
|
||||
private ProcessingCodeScanner fCodeScanner;
|
||||
|
||||
/** Initialized the shared instance. */
|
||||
public ProcessingPlugin() {
|
||||
super();
|
||||
plugin = this;
|
||||
}
|
||||
|
||||
/* Method declared in plug-in */
|
||||
public void start(BundleContext context) throws Exception {
|
||||
super.start(context);
|
||||
// Other init stuff
|
||||
// Don't forget to shut it down in stop()!
|
||||
}
|
||||
|
||||
/* Method declared in plug-in */
|
||||
public void stop(BundleContext context) throws Exception {
|
||||
try{
|
||||
plugin = null;
|
||||
if(fPartitionScanner != null)
|
||||
fPartitionScanner = null;
|
||||
if(fColorProvider != null){
|
||||
fColorProvider.dispose();
|
||||
fColorProvider = null;
|
||||
}
|
||||
if(fCodeScanner != null)
|
||||
fCodeScanner = null;
|
||||
} finally {
|
||||
super.stop(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shared instance
|
||||
*
|
||||
* @return the shared instance
|
||||
*/
|
||||
public static ProcessingPlugin getDefault() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a scanner for creating Processing partitions.
|
||||
* Processing uses Java's commenting scheme, so our partitioner is almost identical. Unlike
|
||||
* the Java partitioner, however, the Processing scanner treats the JavaDoc comments as
|
||||
* simple multiline comments.
|
||||
*
|
||||
* @return a scanner for creating Processing partitions
|
||||
*/
|
||||
public ProcessingPartitionScanner getProcessingPartitionScanner() {
|
||||
if (fPartitionScanner == null)
|
||||
fPartitionScanner= new ProcessingPartitionScanner();
|
||||
return fPartitionScanner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shared code scanner.
|
||||
*
|
||||
* @return the singleton Processing code scanner
|
||||
*/
|
||||
public RuleBasedScanner getProcessingCodeScanner() {
|
||||
if (fCodeScanner == null)
|
||||
fCodeScanner= new ProcessingCodeScanner(getProcessingColorProvider());
|
||||
return fCodeScanner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shared color provider.
|
||||
*
|
||||
* @return the singleton Processing color provider
|
||||
*/
|
||||
public ProcessingColorProvider getProcessingColorProvider() {
|
||||
if (fColorProvider == null)
|
||||
fColorProvider= new ProcessingColorProvider();
|
||||
return fColorProvider;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package processing.plugin.ui.processingeditor;
|
||||
|
||||
//import org.eclipse.jface.text.BadLocationException;
|
||||
//import org.eclipse.jface.text.IDocument;
|
||||
//import org.eclipse.jface.text.IRegion;
|
||||
import org.eclipse.jface.text.source.IAnnotationHover;
|
||||
import org.eclipse.jface.text.source.ISourceViewer;
|
||||
|
||||
/**
|
||||
* Provides the content in the presentation area of a mouse hover popup window.
|
||||
*
|
||||
* @author lonnen
|
||||
* @see org.eclipse.jface.text.source.IAnnotationHover
|
||||
*/
|
||||
public class ProcessingAnnotationHover implements IAnnotationHover {
|
||||
|
||||
/* */
|
||||
public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) {
|
||||
// TODO write useful annotation hover, will require AST
|
||||
// IDocument document= sourceViewer.getDocument();
|
||||
//
|
||||
// try {
|
||||
// IRegion info= document.getLineInformation(lineNumber);
|
||||
// return document.get(info.getOffset(), info.getLength());
|
||||
// } catch (BadLocationException x) { }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package processing.plugin.ui.processingeditor;
|
||||
|
||||
import org.eclipse.core.filebuffers.IDocumentSetupParticipant;
|
||||
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.IDocumentExtension3;
|
||||
import org.eclipse.jface.text.IDocumentPartitioner;
|
||||
import org.eclipse.jface.text.rules.FastPartitioner;
|
||||
import processing.plugin.ui.ProcessingPlugin;
|
||||
import processing.plugin.ui.processingeditor.ProcessingPartitionScanner;
|
||||
|
||||
public class ProcessingDocumentSetupParticipant implements IDocumentSetupParticipant {
|
||||
|
||||
/** Empty Constructor */
|
||||
public ProcessingDocumentSetupParticipant(){}
|
||||
|
||||
/** Set up the document*/
|
||||
public void setup(IDocument document) {
|
||||
if (document instanceof IDocumentExtension3) {
|
||||
IDocumentExtension3 extension3= (IDocumentExtension3) document;
|
||||
IDocumentPartitioner partitioner= new FastPartitioner(ProcessingPlugin.getDefault().getProcessingPartitionScanner(), ProcessingPartitionScanner.PARTITION_TYPES);
|
||||
extension3.setDocumentPartitioner(ProcessingPlugin.PROCESSING_PARTITIONING, partitioner);
|
||||
partitioner.connect(document);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package processing.plugin.ui.processingeditor;
|
||||
|
||||
import org.eclipse.jface.text.ITextViewerExtension5;
|
||||
import org.eclipse.jface.text.Region;
|
||||
import org.eclipse.jface.text.source.ISourceViewer;
|
||||
import org.eclipse.jface.text.source.IVerticalRuler;
|
||||
import org.eclipse.jface.text.source.projection.ProjectionSupport;
|
||||
import org.eclipse.jface.text.source.projection.ProjectionViewer;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.ui.editors.text.TextEditor;
|
||||
|
||||
public class ProcessingEditor extends TextEditor {
|
||||
|
||||
/** Projection Support handles annotations, etc.*/
|
||||
private ProjectionSupport fProjectionSupport;
|
||||
|
||||
//TODO content assistance
|
||||
//TODO outline
|
||||
//TODO code folding
|
||||
|
||||
/** Default constructor */
|
||||
public ProcessingEditor() { super(); }
|
||||
|
||||
/**
|
||||
* Shut it down.
|
||||
* <p>
|
||||
* This should include any objects the editor privately maintains.
|
||||
* In the future this may involve content assist, outline, etc.
|
||||
*/
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public Object getAdapter(Class required){
|
||||
if (fProjectionSupport != null) {
|
||||
Object adapter= fProjectionSupport.getAdapter(getSourceViewer(), required);
|
||||
if (adapter != null)
|
||||
return adapter;
|
||||
}
|
||||
|
||||
return super.getAdapter(required);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this editor and provides a <code>SourceViewerConfiguration</code>
|
||||
*
|
||||
* @see org.eclipse.jface.text.source.SourceViewerConfiguration
|
||||
*/
|
||||
protected void initializeEditor() {
|
||||
super.initializeEditor();
|
||||
setSourceViewerConfiguration(new ProcessingSourceViewerConfiguration());
|
||||
}
|
||||
|
||||
protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
|
||||
|
||||
fAnnotationAccess= createAnnotationAccess();
|
||||
fOverviewRuler= createOverviewRuler(getSharedColors());
|
||||
|
||||
ISourceViewer viewer= new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles);
|
||||
|
||||
// ensure decoration support has been created and configured.
|
||||
// preferred over SourceViewerDecorationSupport due to impending API changes [lonnen] june 11, 2010
|
||||
fSourceViewerDecorationSupport = getSourceViewerDecorationSupport(viewer);
|
||||
|
||||
return viewer;
|
||||
}
|
||||
|
||||
/* @see org.eclipse.ui.texteditor.ExtendedTextEditor#createPartControl(org.eclipse.swt.widgets.Composite) */
|
||||
public void createPartControl(Composite parent) {
|
||||
super.createPartControl(parent);
|
||||
ProjectionViewer viewer= (ProjectionViewer) getSourceViewer();
|
||||
fProjectionSupport= new ProjectionSupport(viewer, getAnnotationAccess(), getSharedColors());
|
||||
fProjectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$
|
||||
fProjectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$
|
||||
fProjectionSupport.install();
|
||||
viewer.doOperation(ProjectionViewer.TOGGLE);
|
||||
}
|
||||
|
||||
/* @see org.eclipse.ui.texteditor.AbstractTextEditor#adjustHighlightRange(int, int) */
|
||||
protected void adjustHighlightRange(int offset, int length) {
|
||||
ISourceViewer viewer= getSourceViewer();
|
||||
if (viewer instanceof ITextViewerExtension5) {
|
||||
ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
|
||||
extension.exposeModelRange(new Region(offset, length));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package processing.plugin.ui.processingeditor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jface.text.rules.EndOfLineRule;
|
||||
import org.eclipse.jface.text.rules.ICharacterScanner;
|
||||
import org.eclipse.jface.text.rules.IPredicateRule;
|
||||
import org.eclipse.jface.text.rules.IToken;
|
||||
import org.eclipse.jface.text.rules.IWordDetector;
|
||||
import org.eclipse.jface.text.rules.MultiLineRule;
|
||||
import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
|
||||
import org.eclipse.jface.text.rules.SingleLineRule;
|
||||
import org.eclipse.jface.text.rules.Token;
|
||||
import org.eclipse.jface.text.rules.WordRule;
|
||||
|
||||
public class ProcessingPartitionScanner extends RuleBasedPartitionScanner {
|
||||
|
||||
public static final String MULTILINE_COMMENT = "__multiline_comment";
|
||||
public static final String[] PARTITION_TYPES = new String[] { MULTILINE_COMMENT };
|
||||
|
||||
/** Detector for empty comments. */
|
||||
static class EmptyCommentDetector implements IWordDetector {
|
||||
|
||||
/* Method declared on IWordDetector */
|
||||
public boolean isWordStart(char c) {
|
||||
return (c == '/');
|
||||
}
|
||||
|
||||
/* Method declared on IWordDetector */
|
||||
public boolean isWordPart(char c) {
|
||||
return (c == '*' || c == '/');
|
||||
}
|
||||
}
|
||||
|
||||
static class WordPredicateRule extends WordRule implements IPredicateRule {
|
||||
|
||||
private IToken fSuccessToken;
|
||||
|
||||
public WordPredicateRule(IToken successToken) {
|
||||
super(new EmptyCommentDetector());
|
||||
fSuccessToken= successToken;
|
||||
addWord("/**/", fSuccessToken); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* @see org.eclipse.jface.text.rules.IPredicateRule#evaluate(ICharacterScanner, boolean) */
|
||||
public IToken evaluate(ICharacterScanner scanner, boolean resume) {
|
||||
return super.evaluate(scanner);
|
||||
}
|
||||
|
||||
/* @see org.eclipse.jface.text.rules.IPredicateRule#getSuccessToken()*/
|
||||
public IToken getSuccessToken() {
|
||||
return fSuccessToken;
|
||||
}
|
||||
}
|
||||
|
||||
/** Create the partitioner and sets up the appropriate rules. */
|
||||
public ProcessingPartitionScanner() {
|
||||
super();
|
||||
|
||||
IToken comment= new Token(MULTILINE_COMMENT);
|
||||
|
||||
List rules= new ArrayList();
|
||||
|
||||
// Add rule for single line comments.
|
||||
rules.add(new EndOfLineRule("//", Token.UNDEFINED)); //$NON-NLS-1$
|
||||
|
||||
// Add rule for strings and character constants.
|
||||
rules.add(new SingleLineRule("\"", "\"", Token.UNDEFINED, '\\')); //$NON-NLS-2$ //$NON-NLS-1$
|
||||
rules.add(new SingleLineRule("'", "'", Token.UNDEFINED, '\\')); //$NON-NLS-2$ //$NON-NLS-1$
|
||||
|
||||
// Add special case word rule.
|
||||
rules.add(new WordPredicateRule(comment));
|
||||
|
||||
// Add rules for multi-line comments and javadoc.
|
||||
rules.add(new MultiLineRule("/*", "*/", comment, (char) 0, true)); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
IPredicateRule[] result= new IPredicateRule[rules.size()];
|
||||
rules.toArray(result);
|
||||
setPredicateRules(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package processing.plugin.ui.processingeditor;
|
||||
|
||||
import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy;
|
||||
import org.eclipse.jface.text.IAutoEditStrategy;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.ITextDoubleClickStrategy;
|
||||
import org.eclipse.jface.text.TextAttribute;
|
||||
import org.eclipse.jface.text.contentassist.ContentAssistant;
|
||||
import org.eclipse.jface.text.contentassist.IContentAssistant;
|
||||
import org.eclipse.jface.text.presentation.IPresentationReconciler;
|
||||
import org.eclipse.jface.text.presentation.PresentationReconciler;
|
||||
import org.eclipse.jface.text.rules.BufferedRuleBasedScanner;
|
||||
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
|
||||
import org.eclipse.jface.text.rules.Token;
|
||||
import org.eclipse.jface.text.source.IAnnotationHover;
|
||||
import org.eclipse.jface.text.source.ISourceViewer;
|
||||
import org.eclipse.jface.text.source.SourceViewerConfiguration;
|
||||
import org.eclipse.swt.graphics.RGB;
|
||||
|
||||
import processing.plugin.ui.ProcessingPlugin;
|
||||
|
||||
import processing.plugin.ui.processingeditor.language.ProcessingAutoIndentStrategy;
|
||||
import processing.plugin.ui.processingeditor.language.ProcessingCompletionProcessor;
|
||||
import processing.plugin.ui.processingeditor.language.ProcessingDoubleClickSelector;
|
||||
import processing.plugin.ui.processingeditor.util.ProcessingColorProvider;
|
||||
|
||||
public class ProcessingSourceViewerConfiguration extends SourceViewerConfiguration {
|
||||
|
||||
/** token scanner. */
|
||||
static class SingleTokenScanner extends BufferedRuleBasedScanner {
|
||||
public SingleTokenScanner(TextAttribute attribute) {
|
||||
setDefaultReturnToken(new Token(attribute));
|
||||
}
|
||||
}
|
||||
|
||||
/** a boring default constructor */
|
||||
public ProcessingSourceViewerConfiguration() { }
|
||||
|
||||
/* Method declared on SourceViewerConfiguration */
|
||||
public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) { return new ProcessingAnnotationHover(); }
|
||||
|
||||
/* @see org.eclipse.jface.text.source.SourceViewerConfiguration#getAutoEditStrategies(org.eclipse.jface.text.source.ISourceViewer, java.lang.String) */
|
||||
public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
|
||||
IAutoEditStrategy strategy= (IDocument.DEFAULT_CONTENT_TYPE.equals(contentType) ? new ProcessingAutoIndentStrategy() : new DefaultIndentLineAutoEditStrategy());
|
||||
return new IAutoEditStrategy[] { strategy };
|
||||
}
|
||||
|
||||
/* @see org.eclipse.jface.text.source.SourceViewerConfiguration#getConfiguredDocumentPartitioning(org.eclipse.jface.text.source.ISourceViewer) */
|
||||
public String getConfiguredDocumentPartitioning(ISourceViewer sourceViewer) {
|
||||
return ProcessingPlugin.PROCESSING_PARTITIONING;
|
||||
}
|
||||
|
||||
/* Method declared on SourceViewerConfiguration */
|
||||
public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) {
|
||||
return new String[] { IDocument.DEFAULT_CONTENT_TYPE, ProcessingPartitionScanner.MULTILINE_COMMENT };
|
||||
}
|
||||
|
||||
/* Method declared on SourceViewerConfiguration */
|
||||
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
|
||||
|
||||
ContentAssistant assistant= new ContentAssistant();
|
||||
assistant.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
|
||||
assistant.setContentAssistProcessor(new ProcessingCompletionProcessor(), IDocument.DEFAULT_CONTENT_TYPE);
|
||||
|
||||
assistant.enableAutoActivation(true);
|
||||
assistant.setAutoActivationDelay(500);
|
||||
assistant.setProposalPopupOrientation(IContentAssistant.PROPOSAL_OVERLAY);
|
||||
assistant.setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_ABOVE);
|
||||
assistant.setContextInformationPopupBackground(ProcessingPlugin.getDefault().getProcessingColorProvider().getColor(new RGB(150, 150, 0)));
|
||||
|
||||
return assistant;
|
||||
}
|
||||
|
||||
/* Method declared on SourceViewerConfiguration */
|
||||
public String getDefaultPrefix(ISourceViewer sourceViewer, String contentType) {
|
||||
return (IDocument.DEFAULT_CONTENT_TYPE.equals(contentType) ? "//" : null); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* Method declared on SourceViewerConfiguration */
|
||||
public ITextDoubleClickStrategy getDoubleClickStrategy(ISourceViewer sourceViewer, String contentType) {
|
||||
return new ProcessingDoubleClickSelector();
|
||||
}
|
||||
|
||||
/* Method declared on SourceViewerConfiguration */
|
||||
public String[] getIndentPrefixes(ISourceViewer sourceViewer, String contentType) {
|
||||
return new String[] { "\t", " " }; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
/* Method declared on SourceViewerConfiguration */
|
||||
public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
|
||||
|
||||
ProcessingColorProvider provider= ProcessingPlugin.getDefault().getProcessingColorProvider();
|
||||
|
||||
PresentationReconciler reconciler= new PresentationReconciler();
|
||||
reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
|
||||
|
||||
DefaultDamagerRepairer dr= new DefaultDamagerRepairer(ProcessingPlugin.getDefault().getProcessingCodeScanner());
|
||||
reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
|
||||
reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
|
||||
|
||||
dr= new DefaultDamagerRepairer(new SingleTokenScanner(new TextAttribute(provider.getColor(ProcessingColorProvider.COMMENT1))));
|
||||
reconciler.setDamager(dr, ProcessingPartitionScanner.MULTILINE_COMMENT);
|
||||
reconciler.setRepairer(dr, ProcessingPartitionScanner.MULTILINE_COMMENT);
|
||||
|
||||
return reconciler;
|
||||
}
|
||||
|
||||
/* Method declared on SourceViewerConfiguration */
|
||||
public int getTabWidth(ISourceViewer sourceViewer) { return 4; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package processing.plugin.ui.processingeditor.language;
|
||||
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy;
|
||||
import org.eclipse.jface.text.DocumentCommand;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.TextUtilities;
|
||||
|
||||
import processing.plugin.ui.ProcessingLog;
|
||||
|
||||
public class ProcessingAutoIndentStrategy extends
|
||||
DefaultIndentLineAutoEditStrategy {
|
||||
|
||||
/** Boring constructor */
|
||||
public ProcessingAutoIndentStrategy(){}
|
||||
|
||||
/* Method declared on IAutoIndentStrategy */
|
||||
public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
|
||||
if (c.length == 0 && c.text != null && endsWithDelimiter(d, c.text))
|
||||
smartIndentAfterNewLine(d, c);
|
||||
else if ("}".equals(c.text)) { //$NON-NLS-1$
|
||||
smartInsertAfterBracket(d, c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given text ends with one of the documents legal line delimiters.
|
||||
*
|
||||
* @param d the document
|
||||
* @param txt the text
|
||||
* @return <code>true</code> if <code>txt</code> ends with one of the document's line delimiters, <code>false</code> otherwise
|
||||
*/
|
||||
private boolean endsWithDelimiter(IDocument d, String txt) {
|
||||
String[] delimiters= d.getLegalLineDelimiters();
|
||||
if (delimiters != null)
|
||||
return TextUtilities.endsWith(delimiters, txt) > -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the line number of the next bracket after end.
|
||||
*
|
||||
* @param document - the document being parsed
|
||||
* @param line - the line to start searching back from
|
||||
* @param end - the end position to search back from
|
||||
* @param closingBracketIncrease - the number of brackets to skip
|
||||
* @return the line number of the next matching bracket after end
|
||||
* @throws BadLocationException in case the line numbers are invalid in the document
|
||||
*/
|
||||
protected int findMatchingOpenBracket(IDocument document, int line, int end, int closingBracketIncrease) throws BadLocationException {
|
||||
|
||||
int start = document.getLineOffset(line);
|
||||
int brackcount = getBracketCount(document, start, end, false) - closingBracketIncrease;
|
||||
|
||||
// sum up the brackets counts of each line (closing brackets count negative,
|
||||
// opening positive) until we find a line the brings the count to zero
|
||||
while (brackcount < 0) {
|
||||
line--;
|
||||
if (line < 0) {
|
||||
return -1;
|
||||
}
|
||||
start= document.getLineOffset(line);
|
||||
end= start + document.getLineLength(line) - 1;
|
||||
brackcount += getBracketCount(document, start, end, false);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bracket value of a section of text. Closing brackets have a value of -1 and
|
||||
* open brackets have a value of 1.
|
||||
*
|
||||
* @param document - the document being parsed
|
||||
* @param start - the start position for the search
|
||||
* @param end - the end position for the search
|
||||
* @param ignoreCloseBrackets - whether or not to ignore closing brackets in the count
|
||||
* @return the bracket value of a section of text
|
||||
* @throws BadLocationException in case the positions are invalid in the document
|
||||
*/
|
||||
private int getBracketCount(IDocument document, int start, int end, boolean ignoreCloseBrackets) throws BadLocationException {
|
||||
|
||||
int begin = start;
|
||||
int bracketcount = 0;
|
||||
while (begin < end) {
|
||||
char curr = document.getChar(begin);
|
||||
begin++;
|
||||
switch (curr) {
|
||||
case '/' :
|
||||
if (begin < end) {
|
||||
char next = document.getChar(begin);
|
||||
if (next == '*') {
|
||||
// a comment starts, advance to the comment end
|
||||
begin = getCommentEnd(document, begin + 1, end);
|
||||
} else if (next == '/') {
|
||||
// '//'-comment: nothing to do anymore on this line
|
||||
begin= end;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '*' :
|
||||
if (begin < end) {
|
||||
char next = document.getChar(begin);
|
||||
if (next == '/') {
|
||||
// we have been in a comment: forget what we read before
|
||||
bracketcount = 0;
|
||||
begin++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '{' :
|
||||
bracketcount++;
|
||||
ignoreCloseBrackets = false;
|
||||
break;
|
||||
case '}' :
|
||||
if (!ignoreCloseBrackets) {
|
||||
bracketcount--;
|
||||
}
|
||||
break;
|
||||
case '"' :
|
||||
case '\'' :
|
||||
begin = getStringEnd(document, begin, end, curr);
|
||||
break;
|
||||
default :
|
||||
}
|
||||
}
|
||||
return bracketcount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end position of a comment starting at the given <code>position</code>.
|
||||
*
|
||||
* @param document - the document being parsed
|
||||
* @param position - the start position for the search
|
||||
* @param end - the end position for the search
|
||||
* @return the end position of a comment starting at the given <code>position</code>
|
||||
* @throws BadLocationException in case <code>position</code> and <code>end</code> are invalid in the document
|
||||
*/
|
||||
private int getCommentEnd(IDocument document, int position, int end) throws BadLocationException {
|
||||
int currentPosition = position;
|
||||
while (currentPosition < end) {
|
||||
char curr = document.getChar(currentPosition);
|
||||
currentPosition++;
|
||||
if (curr == '*') {
|
||||
if (currentPosition < end && document.getChar(currentPosition) == '/') {
|
||||
return currentPosition + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of the given line without the leading whitespace.
|
||||
*
|
||||
* @param document - the document being parsed
|
||||
* @param line - the line being searched
|
||||
* @return the content of the given line without the leading whitespace
|
||||
* @throws BadLocationException in case <code>line</code> is invalid in the document
|
||||
*/
|
||||
protected String getIndentOfLine(IDocument document, int line) throws BadLocationException {
|
||||
if (line > -1) {
|
||||
int start = document.getLineOffset(line);
|
||||
int end= start + document.getLineLength(line) - 1;
|
||||
int whiteend = findEndOfWhiteSpace(document, start, end);
|
||||
return document.get(start, whiteend - start);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the <code>character</code> in the <code>document</code> after <code>position</code>.
|
||||
*
|
||||
* @param document - the document being parsed
|
||||
* @param position - the position to start searching from
|
||||
* @param end - the end of the document
|
||||
* @param character - the character you are trying to match
|
||||
* @return the next location of <code>character</code>
|
||||
* @throws BadLocationException in case <code>position</code> is invalid in the document
|
||||
*/
|
||||
private int getStringEnd(IDocument document, int position, int end, char character) throws BadLocationException {
|
||||
int currentPosition = position;
|
||||
while (currentPosition < end) {
|
||||
char currentCharacter= document.getChar(currentPosition);
|
||||
currentPosition++;
|
||||
if (currentCharacter == '\\') {
|
||||
// ignore escaped characters
|
||||
currentPosition++;
|
||||
} else if (currentCharacter == character) {
|
||||
return currentPosition;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the indent of a new line based on the command provided in the supplied document.
|
||||
* @param document - the document being parsed
|
||||
* @param command - the command being performed
|
||||
*/
|
||||
protected void smartIndentAfterNewLine(IDocument document, DocumentCommand command) {
|
||||
|
||||
int docLength = document.getLength();
|
||||
if (command.offset == -1 || docLength == 0)
|
||||
return;
|
||||
|
||||
try {
|
||||
int p = (command.offset == docLength ? command.offset - 1 : command.offset);
|
||||
int line = document.getLineOfOffset(p);
|
||||
|
||||
StringBuffer buf = new StringBuffer(command.text);
|
||||
if (command.offset < docLength && document.getChar(command.offset) == '}') {
|
||||
int indLine = findMatchingOpenBracket(document, line, command.offset, 0);
|
||||
if (indLine == -1) {
|
||||
indLine = line;
|
||||
}
|
||||
buf.append(getIndentOfLine(document, indLine));
|
||||
} else {
|
||||
int start = document.getLineOffset(line);
|
||||
int whiteend= findEndOfWhiteSpace(document, start, command.offset);
|
||||
buf.append(document.get(start, whiteend - start));
|
||||
if (getBracketCount(document, start, command.offset, true) > 0) {
|
||||
buf.append('\t');
|
||||
}
|
||||
}
|
||||
command.text = buf.toString();
|
||||
|
||||
} catch (BadLocationException excp) {
|
||||
ProcessingLog.logError(excp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the indent of a bracket based on the command provided in the supplied document.
|
||||
* @param document - the document being parsed
|
||||
* @param command - the command being performed
|
||||
*/
|
||||
protected void smartInsertAfterBracket(IDocument document, DocumentCommand command) {
|
||||
if (command.offset == -1 || document.getLength() == 0)
|
||||
return;
|
||||
|
||||
try {
|
||||
int p= (command.offset == document.getLength() ? command.offset - 1 : command.offset);
|
||||
int line= document.getLineOfOffset(p);
|
||||
int start= document.getLineOffset(line);
|
||||
int whiteend= findEndOfWhiteSpace(document, start, command.offset);
|
||||
|
||||
// shift only when line does not contain any text up to the closing bracket
|
||||
if (whiteend == command.offset) {
|
||||
// evaluate the line with the opening bracket that matches out closing bracket
|
||||
int indLine= findMatchingOpenBracket(document, line, command.offset, 1);
|
||||
if (indLine != -1 && indLine != line) {
|
||||
// take the indent of the found line
|
||||
StringBuffer replaceText= new StringBuffer(getIndentOfLine(document, indLine));
|
||||
// add the rest of the current line including the just added close bracket
|
||||
replaceText.append(document.get(whiteend, command.offset - whiteend));
|
||||
replaceText.append(command.text);
|
||||
// modify document command
|
||||
command.length= command.offset - start;
|
||||
command.offset= start;
|
||||
command.text= replaceText.toString();
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException excp) {
|
||||
ProcessingLog.logError(excp);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package processing.plugin.ui.processingeditor.language;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.core.runtime.FileLocator;
|
||||
import org.eclipse.jface.text.TextAttribute;
|
||||
import org.eclipse.jface.text.rules.EndOfLineRule;
|
||||
import org.eclipse.jface.text.rules.IRule;
|
||||
import org.eclipse.jface.text.rules.IToken;
|
||||
import org.eclipse.jface.text.rules.RuleBasedScanner;
|
||||
import org.eclipse.jface.text.rules.SingleLineRule;
|
||||
import org.eclipse.jface.text.rules.Token;
|
||||
import org.eclipse.jface.text.rules.WhitespaceRule;
|
||||
import org.eclipse.jface.text.rules.WordRule;
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
import processing.plugin.ui.ProcessingPlugin;
|
||||
import processing.plugin.ui.processingeditor.util.ProcessingColorProvider;
|
||||
import processing.plugin.ui.processingeditor.util.ProcessingWhitespaceDetector;
|
||||
import processing.plugin.ui.processingeditor.util.ProcessingWordDetector;
|
||||
|
||||
public class ProcessingCodeScanner extends RuleBasedScanner {
|
||||
|
||||
private static String keywordsFile = "Resources" + File.separatorChar + "keywords.txt"; // name of the syntax highlighting file
|
||||
|
||||
private static String[] fgKeywords1; // keywords (new, return, super)
|
||||
private static String[] fgKeywords2; // PDE methods (setup, random, size)
|
||||
private static String[] fgKeywords3; // primitive types (int, color, float)
|
||||
private static String[] fgLiterals1; // static tokens (null, true, P2D)
|
||||
private static String[] fgLiterals2; // environmental variables (mouseX, width, pixels)
|
||||
private static String[] fgLabels; // possibly unused? Supporting them here because Processing does.
|
||||
private static String[] fgOperators; // mathematical operators (+, -, *)
|
||||
private static String[] fgInvalids; // possibly unused? Supporting them here because Processing does.
|
||||
|
||||
/*
|
||||
* Static initialization block to load Processing Keywords in from keywordsFile
|
||||
*
|
||||
* Reads in the keyword file and splits each line at the tabs. The first string
|
||||
* is the symbol, the second is the category (empty strings indicate operators)
|
||||
* and the third string is ignored. In Processing it is used to lookup reference
|
||||
* HTML.
|
||||
*
|
||||
* A HashMap stores each category as a key corresponding to a HashSet value that
|
||||
* holds the tokens belonging to each category. After the keywordsFile is
|
||||
* processed, the HashMap sets are converted to string arrays and loaded into the
|
||||
* static keyword lists the document expects. Each set of keywords in the rules are
|
||||
* explicitly listed. If the keyword categories change in the future, there will
|
||||
* need to be new categories introduced in the code scanner and ProcessingColorProvider.
|
||||
*
|
||||
* Unexpected categories in the keywords file will be silently ignored.
|
||||
* 'Unsure of how to JavaDoc an init block so I did this' - [lonnen] june 16, 2010
|
||||
* Updated - [lonnen] august 12, 2010
|
||||
*/
|
||||
static {
|
||||
HashMap<String, Set> KeywordMap = new HashMap<String, Set>();
|
||||
//Read in the values
|
||||
try{
|
||||
InputStream is = getFileInputStream(keywordsFile);
|
||||
InputStreamReader isr = new InputStreamReader(is);
|
||||
BufferedReader reader = new BufferedReader(isr);
|
||||
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null){
|
||||
String pieces[] = line.split("\t"); // split the string at \t
|
||||
if (pieces.length >= 2) {
|
||||
String token = pieces[0].trim(); // ex. PVector
|
||||
String coloring = pieces[1].trim(); // ex. KEWORD1
|
||||
//String reference = pieces[2].trim(); // used for reference in PDE, unused here
|
||||
if (coloring.isEmpty()) // catches operators
|
||||
coloring = "OPERATOR";
|
||||
if (KeywordMap.containsKey(coloring)){
|
||||
KeywordMap.get(coloring).add(token);
|
||||
} else {
|
||||
Set<String> tokenSet = new HashSet<String>();
|
||||
tokenSet.add(token);
|
||||
KeywordMap.put(coloring, tokenSet);
|
||||
}
|
||||
//System.out.println(coloring + " " + token); // to print out a list of what is added
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try{
|
||||
fgKeywords1= KeywordMap.containsKey("KEYWORD1") ? (String[]) KeywordMap.get("KEYWORD1").toArray(new String[KeywordMap.get("KEYWORD1").size()]) : new String[] {"test"};
|
||||
fgKeywords2= KeywordMap.containsKey("KEYWORD2") ? (String[]) KeywordMap.get("KEYWORD2").toArray(new String[KeywordMap.get("KEYWORD2").size()]) : new String[] {};
|
||||
fgKeywords3= KeywordMap.containsKey("KEYWORD3") ? (String[]) KeywordMap.get("KEYWORD3").toArray(new String[KeywordMap.get("KEYWORD3").size()]) : new String[] {};
|
||||
fgLiterals1= KeywordMap.containsKey("LITERAL1") ? (String[]) KeywordMap.get("LITERAL1").toArray(new String[KeywordMap.get("LITERAL1").size()]) : new String[] {};
|
||||
fgLiterals2= KeywordMap.containsKey("LITERAL2") ? (String[]) KeywordMap.get("LITERAL2").toArray(new String[KeywordMap.get("LITERAL2").size()]) : new String[] {};
|
||||
fgLabels = KeywordMap.containsKey("LABELS") ? (String[]) KeywordMap.get("LABELS").toArray(new String[KeywordMap.get("LABELS").size()]) : new String[] {}; //unused?
|
||||
fgOperators = KeywordMap.containsKey("OPERATOR") ? (String[]) KeywordMap.get("OPERATOR").toArray(new String[KeywordMap.get("OPERATOR").size()]) : new String[] {};
|
||||
fgInvalids = KeywordMap.containsKey("INVALIDS") ? (String[]) KeywordMap.get("INVALIDS").toArray(new String[KeywordMap.get("INVALIDS").size()]) : new String[] {}; // unused?
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Processing code scanner with the given color provider.
|
||||
*
|
||||
* @param provider the color provider
|
||||
*/
|
||||
public ProcessingCodeScanner(ProcessingColorProvider provider) {
|
||||
|
||||
IToken keyword1= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.KEYWORD1)));
|
||||
IToken keyword2= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.KEYWORD2)));
|
||||
IToken keyword3= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.KEYWORD3)));
|
||||
IToken literal1= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.LITERAL1)));
|
||||
IToken literal2= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.LITERAL2)));
|
||||
IToken label= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.LABEL)));
|
||||
IToken operator= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.OPERATOR)));
|
||||
IToken invalid= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.INVALID)));
|
||||
// leave the rest for now
|
||||
IToken string= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.STRING)));
|
||||
IToken comment= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.COMMENT2)));
|
||||
IToken other= new Token(new TextAttribute(provider.getColor(ProcessingColorProvider.DEFAULT)));
|
||||
|
||||
List rules= new ArrayList();
|
||||
|
||||
// Add rule for single line comments.
|
||||
rules.add(new EndOfLineRule("//", comment)); //$NON-NLS-1$
|
||||
|
||||
// Add rule for strings and character constants.
|
||||
rules.add(new SingleLineRule("\"", "\"", string, '\\')); //$NON-NLS-2$ //$NON-NLS-1$
|
||||
rules.add(new SingleLineRule("'", "'", string, '\\')); //$NON-NLS-2$ //$NON-NLS-1$
|
||||
|
||||
// Add generic whitespace rule.
|
||||
rules.add(new WhitespaceRule(new ProcessingWhitespaceDetector()));
|
||||
|
||||
// Add a rule for each keyword explaining how to color it
|
||||
WordRule wordRule= new WordRule(new ProcessingWordDetector(), other);
|
||||
for (int i= 0; i < fgKeywords1.length; i++)
|
||||
wordRule.addWord(fgKeywords1[i], keyword1);
|
||||
for (int i= 0; i < fgKeywords2.length; i++)
|
||||
wordRule.addWord(fgKeywords2[i], keyword2);
|
||||
for (int i= 0; i < fgKeywords3.length; i++)
|
||||
wordRule.addWord(fgKeywords3[i], keyword3);
|
||||
for (int i= 0; i < fgLiterals1.length; i++)
|
||||
wordRule.addWord(fgLiterals1[i], literal1);
|
||||
for (int i= 0; i < fgLiterals2.length; i++)
|
||||
wordRule.addWord(fgLiterals2[i], literal2);
|
||||
for (int i= 0; i < fgLabels.length; i++)
|
||||
wordRule.addWord(fgLabels[i], label);
|
||||
for (int i= 0; i < fgOperators.length; i++)
|
||||
wordRule.addWord(fgOperators[i], operator);
|
||||
for (int i= 0; i < fgInvalids.length; i++)
|
||||
wordRule.addWord(fgInvalids[i], invalid);
|
||||
// Set these as the colorizing rules for the document
|
||||
rules.add(wordRule);
|
||||
|
||||
IRule[] result= new IRule[rules.size()];
|
||||
rules.toArray(result);
|
||||
setRules(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates the keyword arrays into one array and returns it.
|
||||
*
|
||||
* @return A string array of all the keywords
|
||||
*/
|
||||
public final static String[] getKeywords(){
|
||||
String[] result = new String[fgKeywords1.length + fgKeywords2.length + fgKeywords3.length];
|
||||
System.arraycopy(fgKeywords1, 0, result, 0, fgKeywords1.length);
|
||||
System.arraycopy(fgKeywords2, 0, result, fgKeywords1.length, fgKeywords2.length);
|
||||
System.arraycopy(fgKeywords3, 0, result, fgKeywords1.length+fgKeywords2.length, fgKeywords3.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a buffered input stream for a file in the plug-in directory.
|
||||
*
|
||||
* @param filename the file to be loaded
|
||||
* @return BufferedInputStream to read the file with
|
||||
*/
|
||||
public static BufferedInputStream getFileInputStream(String filename) {
|
||||
Bundle bundle = ProcessingPlugin.getDefault().getBundle();
|
||||
URL fileLocation;
|
||||
try {
|
||||
fileLocation = FileLocator.toFileURL(bundle.getEntry(filename));
|
||||
BufferedInputStream file = new BufferedInputStream(fileLocation.openStream());
|
||||
return file;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package processing.plugin.ui.processingeditor.language;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import org.eclipse.jface.text.ITextViewer;
|
||||
import org.eclipse.jface.text.TextPresentation;
|
||||
import org.eclipse.jface.text.contentassist.ContextInformation;
|
||||
import org.eclipse.jface.text.contentassist.ICompletionProposal;
|
||||
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
|
||||
import org.eclipse.jface.text.contentassist.IContextInformation;
|
||||
import org.eclipse.jface.text.contentassist.IContextInformationPresenter;
|
||||
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
|
||||
|
||||
/**
|
||||
* Naive auto completion provider.
|
||||
* <p>
|
||||
* None of this is particularly useful, but may be extended in the future.
|
||||
*
|
||||
*
|
||||
* @author lonnen
|
||||
*/
|
||||
public class ProcessingCompletionProcessor implements IContentAssistProcessor {
|
||||
|
||||
/** Utility class with methods to determine if the context tip window is still valid */
|
||||
protected static class Validator implements IContextInformationValidator, IContextInformationPresenter {
|
||||
|
||||
protected int fInstallOffset;
|
||||
|
||||
/* @see IContextInformationValidator#isContextInformationValid(int) */
|
||||
public boolean isContextInformationValid(int offset) { return Math.abs(fInstallOffset - offset) < 5; }
|
||||
|
||||
/* @see IContextInformationValidator#install(IContextInformation, ITextViewer, int) */
|
||||
public void install(IContextInformation info, ITextViewer viewer, int offset) { fInstallOffset= offset; }
|
||||
|
||||
/* @see org.eclipse.jface.text.contentassist.IContextInformationPresenter#updatePresentation(int, TextPresentation) */
|
||||
public boolean updatePresentation(int documentPosition, TextPresentation presentation) { return false; }
|
||||
}
|
||||
|
||||
/** Strings to populate the context assistance pop up box. */
|
||||
protected final static String[] fgProposals = {};
|
||||
|
||||
/** Validates the context tip window. */
|
||||
protected IContextInformationValidator fValidator= new Validator();
|
||||
|
||||
/* Method declared on IContentAssistProcessor */
|
||||
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset) {
|
||||
ICompletionProposal[] result= new ICompletionProposal[fgProposals.length];
|
||||
return result; // not useful
|
||||
}
|
||||
|
||||
/* Method declared on IContentAssistProcessor */
|
||||
public IContextInformation[] computeContextInformation(ITextViewer viewer, int documentOffset) {
|
||||
IContextInformation[] result= new IContextInformation[5];
|
||||
for (int i= 0; i < result.length; i++)
|
||||
result[i]= new ContextInformation(
|
||||
MessageFormat.format("proposal {0} at position {1}", new Object[] { new Integer(i), new Integer(documentOffset) }),
|
||||
MessageFormat.format("proposal {0} valid from {1} to {2}", new Object[] { new Integer(i), new Integer(documentOffset - 5), new Integer(documentOffset + 5)}));
|
||||
return result; }
|
||||
|
||||
/* Method declared on IContentAssistProcessor */
|
||||
public char[] getCompletionProposalAutoActivationCharacters() {
|
||||
return new char[] { '.', '(' };
|
||||
}
|
||||
|
||||
/* Method declared on IContentAssistProcessor */
|
||||
public char[] getContextInformationAutoActivationCharacters() {
|
||||
return new char[] { '#' };
|
||||
}
|
||||
|
||||
/* Method declared on IContentAssistProcessor */
|
||||
public IContextInformationValidator getContextInformationValidator() {
|
||||
return fValidator;
|
||||
}
|
||||
|
||||
/* Method declared on IContentAssistProcessor */
|
||||
public String getErrorMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
package processing.plugin.ui.processingeditor.language;
|
||||
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.ITextDoubleClickStrategy;
|
||||
import org.eclipse.jface.text.ITextViewer;
|
||||
|
||||
/**
|
||||
* On a double click, try to automatically highlight a logical section of the source code.
|
||||
* <p>
|
||||
* If the double click is near an opening or closing bracket type, this should highlight to
|
||||
* the other bracket in the set.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the user didn't double click a bracket, assume they wanted the word
|
||||
*
|
||||
* @author lonnen
|
||||
*/
|
||||
public class ProcessingDoubleClickSelector implements ITextDoubleClickStrategy {
|
||||
|
||||
protected ITextViewer fText;
|
||||
protected int fPos;
|
||||
protected int fStartPos;
|
||||
protected int fEndPos;
|
||||
|
||||
/** Supported bracket types */
|
||||
protected static char[] fgBrackets= { '{', '}', '(', ')', '[', ']', '"', '"', '<', '>'};
|
||||
|
||||
/** Boring ol' constructor */
|
||||
public ProcessingDoubleClickSelector() { super(); }
|
||||
|
||||
/* Method declared on ITextDoubleClickStrategy */
|
||||
public void doubleClicked(ITextViewer text) {
|
||||
fPos = text.getSelectedRange().x;
|
||||
|
||||
if (fPos < 0)
|
||||
return;
|
||||
|
||||
fText= text;
|
||||
|
||||
// If a matching bracket wasn't found, assume the user wants the word
|
||||
if (!selectBracketBlock())
|
||||
selectWord();
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the brackets at the current selection.
|
||||
* Return <code>true</code> if successful, <code>false</code> otherwise.
|
||||
*
|
||||
* @return <code>true</code> if brackets match, <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean matchBracketsAt() {
|
||||
|
||||
char prevChar, nextChar;
|
||||
|
||||
int i;
|
||||
int bracketIndex1= fgBrackets.length;
|
||||
int bracketIndex2= fgBrackets.length;
|
||||
|
||||
fStartPos= -1;
|
||||
fEndPos= -1;
|
||||
|
||||
// get the chars preceding and following the start position
|
||||
try {
|
||||
IDocument doc= fText.getDocument();
|
||||
|
||||
prevChar= doc.getChar(fPos - 1);
|
||||
nextChar= doc.getChar(fPos);
|
||||
|
||||
// is the char either an open or close bracket?
|
||||
for (i= 0; i < fgBrackets.length; i= i + 2) {
|
||||
if (prevChar == fgBrackets[i]) {
|
||||
fStartPos= fPos - 1;
|
||||
bracketIndex1= i;
|
||||
}
|
||||
}
|
||||
for (i= 1; i < fgBrackets.length; i= i + 2) {
|
||||
if (nextChar == fgBrackets[i]) {
|
||||
fEndPos= fPos;
|
||||
bracketIndex2= i;
|
||||
}
|
||||
}
|
||||
|
||||
if (fStartPos > -1 && bracketIndex1 < bracketIndex2) {
|
||||
fEndPos= searchForClosingBracket(fStartPos, prevChar, fgBrackets[bracketIndex1 + 1], doc);
|
||||
if (fEndPos > -1)
|
||||
return true;
|
||||
fStartPos= -1;
|
||||
} else if (fEndPos > -1) {
|
||||
fStartPos= searchForOpenBracket(fEndPos, fgBrackets[bracketIndex2 - 1], nextChar, doc);
|
||||
if (fStartPos > -1)
|
||||
return true;
|
||||
fEndPos= -1;
|
||||
}
|
||||
|
||||
} catch (BadLocationException x) {
|
||||
// assume everything is cool
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the word at the current selection location. Return <code>true</code> if successful,
|
||||
* <code>false</code> otherwise.
|
||||
*
|
||||
* @return <code>true</code> if a word can be found at the current selection location, <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean matchWord() {
|
||||
IDocument doc= fText.getDocument();
|
||||
|
||||
try {
|
||||
int pos= fPos;
|
||||
char c;
|
||||
|
||||
while (pos >= 0) {
|
||||
c= doc.getChar(pos);
|
||||
if (!Character.isJavaIdentifierPart(c))
|
||||
break;
|
||||
--pos;
|
||||
}
|
||||
|
||||
fStartPos= pos;
|
||||
|
||||
pos= fPos;
|
||||
int length= doc.getLength();
|
||||
|
||||
while (pos < length) {
|
||||
c= doc.getChar(pos);
|
||||
if (!Character.isJavaIdentifierPart(c))
|
||||
break;
|
||||
++pos;
|
||||
}
|
||||
|
||||
fEndPos= pos;
|
||||
|
||||
return true;
|
||||
|
||||
} catch (BadLocationException x) {
|
||||
//assume everything's cool
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the closing bracket after <code>startPosition</code>.
|
||||
*
|
||||
* @param startPosition - the beginning position
|
||||
* @param openBracket - the character that represents the open bracket
|
||||
* @param closeBracket - the character that represents the close bracket
|
||||
* @param document - the document being searched
|
||||
* @return the location of the closing bracket.
|
||||
* @throws BadLocationException in case <code>startPosition</code> is invalid in the document
|
||||
*/
|
||||
protected int searchForClosingBracket(int startPosition, char openBracket, char closeBracket, IDocument document) throws BadLocationException {
|
||||
int stack= 1;
|
||||
int closePosition= startPosition + 1;
|
||||
int length= document.getLength();
|
||||
char nextChar;
|
||||
|
||||
while (closePosition < length && stack > 0) {
|
||||
nextChar= document.getChar(closePosition);
|
||||
if (nextChar == openBracket && nextChar != closeBracket)
|
||||
stack++;
|
||||
else if (nextChar == closeBracket)
|
||||
stack--;
|
||||
closePosition++;
|
||||
}
|
||||
|
||||
if (stack == 0)
|
||||
return closePosition - 1;
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the open bracket before <code>startPosition</code>.
|
||||
*
|
||||
* @param startPosition - the beginning position
|
||||
* @param openBracket - the character that represents the open bracket
|
||||
* @param closeBracket - the character that represents the close bracket
|
||||
* @param document - the document being searched
|
||||
* @return the location of the starting bracket.
|
||||
* @throws BadLocationException in case <code>startPosition</code> is invalid in the document
|
||||
*/
|
||||
protected int searchForOpenBracket(int startPosition, char openBracket, char closeBracket, IDocument document) throws BadLocationException {
|
||||
int stack= 1;
|
||||
int openPos= startPosition - 1;
|
||||
char nextChar;
|
||||
|
||||
while (openPos >= 0 && stack > 0) {
|
||||
nextChar= document.getChar(openPos);
|
||||
if (nextChar == closeBracket && nextChar != openBracket)
|
||||
stack++;
|
||||
else if (nextChar == openBracket)
|
||||
stack--;
|
||||
openPos--;
|
||||
}
|
||||
|
||||
if (stack == 0)
|
||||
return openPos + 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the area between the selected bracket and the closing bracket.
|
||||
*
|
||||
* @return <code>true</code> if selection was successful, <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean selectBracketBlock() {
|
||||
if (matchBracketsAt()) {
|
||||
if (fStartPos == fEndPos)
|
||||
fText.setSelectedRange(fStartPos, 0);
|
||||
else
|
||||
fText.setSelectedRange(fStartPos + 1, fEndPos - fStartPos - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the word at the current selection.
|
||||
*/
|
||||
protected void selectWord() {
|
||||
if (matchWord()) {
|
||||
if (fStartPos == fEndPos)
|
||||
fText.setSelectedRange(fStartPos, 0);
|
||||
else
|
||||
fText.setSelectedRange(fStartPos + 1, fEndPos - fStartPos - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package processing.plugin.ui.processingeditor.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.swt.graphics.Color;
|
||||
import org.eclipse.swt.graphics.RGB;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
|
||||
/** Manages the colors used in the Java Editor */
|
||||
public class ProcessingColorProvider {
|
||||
|
||||
public static final RGB COMMENT1= new RGB(126, 126, 126); //comment 1
|
||||
public static final RGB COMMENT2= new RGB(126, 126, 126); //comment 2
|
||||
public static final RGB KEYWORD1= new RGB(204, 102, 0);
|
||||
public static final RGB KEYWORD2= new RGB(204, 102, 0);
|
||||
public static final RGB KEYWORD3= new RGB(204, 102, 0);
|
||||
public static final RGB LITERAL1= new RGB(0, 102, 153); // currently unused [lonnen] june 16, 2010
|
||||
public static final RGB LITERAL2= new RGB(0, 102, 153);
|
||||
public static final RGB LABEL= new RGB(0, 0, 128); // use listed as '?' in p5 doc
|
||||
public static final RGB OPERATOR= new RGB(0, 0, 0);
|
||||
public static final RGB INVALID= new RGB(126, 126, 126); // never used [lonnen] june 16, 2010
|
||||
public static final RGB STRING= new RGB(0, 102, 153);
|
||||
public static final RGB DEFAULT= new RGB(0, 0, 0);
|
||||
|
||||
protected Map fColorTable= new HashMap(17);
|
||||
|
||||
/** Release all of the color resources held onto by the receiver. */
|
||||
public void dispose() {
|
||||
Iterator e= fColorTable.values().iterator();
|
||||
while (e.hasNext())
|
||||
((Color) e.next()).dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* convert an RGB value to a Color using the resource table
|
||||
*
|
||||
* @param rgb the RGB value
|
||||
* @return the color stored in the color table for the given RGB value
|
||||
*/
|
||||
public Color getColor(RGB rgb) {
|
||||
Color color= (Color) fColorTable.get(rgb);
|
||||
if (color == null) {
|
||||
color= new Color(Display.getCurrent(), rgb);
|
||||
fColorTable.put(rgb, color);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package processing.plugin.ui.processingeditor.util;
|
||||
|
||||
import org.eclipse.jface.text.rules.IWhitespaceDetector;
|
||||
|
||||
public class ProcessingWhitespaceDetector implements IWhitespaceDetector {
|
||||
|
||||
public boolean isWhitespace(char character){
|
||||
return Character.isWhitespace(character);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package processing.plugin.ui.processingeditor.util;
|
||||
|
||||
import org.eclipse.jface.text.rules.IWordDetector;
|
||||
|
||||
/**
|
||||
* Processing Words are Java words, so this class wraps Character methods to detect
|
||||
* Java words. JavaDoc for the methods can be found in the IWordDetector interface.
|
||||
* */
|
||||
public class ProcessingWordDetector implements IWordDetector {
|
||||
|
||||
public boolean isWordPart(char character) {
|
||||
return Character.isJavaIdentifierPart(character);
|
||||
}
|
||||
|
||||
public boolean isWordStart(char character) {
|
||||
return Character.isJavaIdentifierStart(character);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package processing.plugin.ui.wizards;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IProjectDescription;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.OperationCanceledException;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.SubProgressMonitor;
|
||||
import org.eclipse.jface.dialogs.MessageDialog;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.jface.wizard.Wizard;
|
||||
import org.eclipse.ui.INewWizard;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
import org.eclipse.ui.actions.WorkspaceModifyOperation;
|
||||
import processing.plugin.core.builder.SketchNature;
|
||||
import processing.plugin.ui.wizards.NewSketchWizardPage;
|
||||
|
||||
/**
|
||||
* A wizard to create a new Processing sketch project.
|
||||
* <p>
|
||||
* Contains a single page the requests the name of the sketch
|
||||
* and the sketch book folder to put the sketch in.
|
||||
*
|
||||
* @author lonnen
|
||||
*/
|
||||
public class NewSketchWizard extends Wizard implements INewWizard {
|
||||
|
||||
/** The single page in the wizard */
|
||||
private NewSketchWizardPage page;
|
||||
/** The project to be created */
|
||||
private IProject project;
|
||||
|
||||
/** Constructor */
|
||||
public NewSketchWizard(){
|
||||
// IDialogSettings processingSettings = ProcessingEditorPlugin.getDefault().getDialogSettings();
|
||||
// IDialogSettings wizardSettings = processingSettings.getSection("NewSketchWizard");
|
||||
// if (wizardSettings == null)
|
||||
// wizardSettings = processingSettings.addNewSection("NewSketchWizard");
|
||||
// setDialogSettings(processingSettings);
|
||||
}
|
||||
|
||||
public void init(IWorkbench workbench, IStructuredSelection selection) {
|
||||
// init code
|
||||
}
|
||||
|
||||
/**
|
||||
* When the finish button is called, create the project
|
||||
* <p>
|
||||
* The wizard page ensures that the user cannot click finish
|
||||
* without a valid directory and sketch name. The creation of the
|
||||
* project is wrapped in a runnable object so it can be monitored
|
||||
* and potentially canceled by the user.
|
||||
*/
|
||||
public boolean performFinish() {
|
||||
|
||||
final String sketchName = page.getSketchName();
|
||||
final IPath sketchbookPath = page.getSketchbookLoc().append(sketchName);
|
||||
project = ResourcesPlugin.getWorkspace().getRoot().getProject(sketchName);
|
||||
|
||||
final IProjectDescription sketchDescription = ResourcesPlugin.getWorkspace().newProjectDescription(project.getName());
|
||||
sketchDescription.setLocation(sketchbookPath);
|
||||
|
||||
WorkspaceModifyOperation op = new WorkspaceModifyOperation(){
|
||||
protected void execute(IProgressMonitor monitor) throws CoreException{
|
||||
createNewProject(sketchDescription, project, monitor);
|
||||
}
|
||||
};
|
||||
|
||||
try{
|
||||
getContainer().run(true, true, op);
|
||||
} catch (InvocationTargetException e){
|
||||
Throwable realException = e.getTargetException();
|
||||
MessageDialog.openError(getShell(), "Error!", realException.getMessage());
|
||||
return false;
|
||||
} catch (InterruptedException e){ // "shut it down"
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the project in the workspace
|
||||
*
|
||||
* @param description
|
||||
* @param proj
|
||||
* @param monitor
|
||||
* @throws CoreException
|
||||
* @throws OperationCanceledException
|
||||
*/
|
||||
protected void createNewProject(IProjectDescription description, IProject proj,
|
||||
IProgressMonitor monitor) throws CoreException, OperationCanceledException{
|
||||
monitor.beginTask("Creating a new Sketch", 500);
|
||||
|
||||
try{
|
||||
// create the project root
|
||||
proj.create(description, new SubProgressMonitor(monitor, 100));
|
||||
proj.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(monitor, 100));
|
||||
|
||||
|
||||
if (monitor.isCanceled()){throw new OperationCanceledException();}
|
||||
|
||||
// create the folders
|
||||
IContainer container = (IContainer) proj;
|
||||
//data
|
||||
IFolder dataFolder = container.getFolder(new Path("data"));
|
||||
dataFolder.create(true, true, monitor);
|
||||
monitor.worked(33);
|
||||
//code
|
||||
IFolder codeFolder = container.getFolder(new Path("code"));
|
||||
codeFolder.create(true, true, monitor);
|
||||
monitor.worked(33);
|
||||
//bin
|
||||
IFolder binFolder = container.getFolder(new Path("bin"));
|
||||
binFolder.create(true, true, monitor);
|
||||
monitor.worked(34);
|
||||
|
||||
if (monitor.isCanceled()){throw new OperationCanceledException();}
|
||||
|
||||
//create the main file
|
||||
addFileToContainer(container, new Path(project.getName() + ".pde"), null, new SubProgressMonitor(monitor, 100));
|
||||
} finally{
|
||||
monitor.done();
|
||||
}
|
||||
|
||||
// this is a new project
|
||||
SketchNature.addNature(proj);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new file to a project
|
||||
*
|
||||
* @param container where to add the file
|
||||
* @param path the path to the file
|
||||
* @param contentStream what to put in the file
|
||||
* @param monitor report the progress back to the user
|
||||
* @throws CoreException if there are problems with the resource
|
||||
*/
|
||||
private void addFileToContainer(IContainer container, Path path, InputStream contentStream, IProgressMonitor monitor) throws CoreException{
|
||||
final IFile file = container.getFile(path);
|
||||
if (contentStream == null){
|
||||
contentStream = new ByteArrayInputStream(" void setup(){} \n void draw(){} ".getBytes());
|
||||
}
|
||||
if(file.exists()){
|
||||
file.setContents(contentStream, true, true, monitor);
|
||||
} else{
|
||||
file.create(contentStream, true, monitor);
|
||||
}
|
||||
monitor.done();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the single page and adds it to the wizard.
|
||||
*/
|
||||
public void addPages(){
|
||||
setWindowTitle("New Sketch Wizard");
|
||||
page = new NewSketchWizardPage();
|
||||
addPage(page);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package processing.plugin.ui.wizards;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.jface.wizard.WizardPage;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ModifyEvent;
|
||||
import org.eclipse.swt.events.ModifyListener;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.DirectoryDialog;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
|
||||
import processing.app.Sketch;
|
||||
|
||||
public class NewSketchWizardPage extends WizardPage {
|
||||
|
||||
/** field containing the name of the sketch to be created */
|
||||
private Text sketchNameField;
|
||||
/** field containing path of the sketch book folder that will contain the sketch */
|
||||
private Text sketchbookPathField; // TODO get the default from the plugin preferences
|
||||
/** the actual path of the sketch book folder */
|
||||
private IPath initialSketchbookPath;
|
||||
|
||||
public NewSketchWizardPage() {
|
||||
super("selectFiles");
|
||||
setTitle("New Sketch Wizard");
|
||||
setDescription("Create a new Processing Sketch");
|
||||
}
|
||||
|
||||
public void createControl(Composite parent) {
|
||||
Composite container = new Composite(parent, SWT.NULL);
|
||||
final GridLayout gridLayout = new GridLayout();
|
||||
gridLayout.numColumns = 3;
|
||||
container.setLayout(gridLayout);
|
||||
setControl(container);
|
||||
|
||||
final Label label = new Label(container, SWT.NONE);
|
||||
final GridData gridData = new GridData();
|
||||
gridData.horizontalSpan = 3;
|
||||
label.setLayoutData(gridData);
|
||||
label.setText("Select a name for the new sketch.");
|
||||
|
||||
final Label label_1 = new Label(container, SWT.NONE);
|
||||
final GridData gridData_1 = new GridData(GridData.HORIZONTAL_ALIGN_END);
|
||||
label_1.setLayoutData(gridData_1);
|
||||
label_1.setText("Sketch Name:");
|
||||
|
||||
sketchNameField = new Text(container, SWT.BORDER);
|
||||
sketchNameField.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e){
|
||||
updatePageComplete();
|
||||
}
|
||||
});
|
||||
sketchNameField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
final Label label_2 = new Label(container, SWT.NONE);
|
||||
final GridData gridData_2 = new GridData();
|
||||
gridData_2.horizontalSpan = 3;
|
||||
label_2.setLayoutData(gridData_2);
|
||||
|
||||
final Label label_3 = new Label(container, SWT.NONE);
|
||||
final GridData gridData_3 = new GridData();
|
||||
gridData_3.horizontalSpan = 3;
|
||||
label_3.setLayoutData(gridData_3);
|
||||
label_3.setLayoutData(gridData_3);
|
||||
label_3.setText("Select the sketchbook folder that will contain the sketch:");
|
||||
|
||||
final Label label_4 = new Label(container, SWT.NONE);
|
||||
final GridData gridData_4 = new GridData();
|
||||
gridData_4.horizontalIndent = 20;
|
||||
label_4.setLayoutData(gridData_4);
|
||||
label_4.setText("Sketchbook Folder:");
|
||||
|
||||
sketchbookPathField = new Text(container, SWT.BORDER);
|
||||
sketchbookPathField.addModifyListener(
|
||||
new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e){
|
||||
updatePageComplete();
|
||||
}
|
||||
});
|
||||
sketchbookPathField.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
|
||||
|
||||
final Button button_1 = new Button(container, SWT.NONE);
|
||||
button_1.addSelectionListener(new SelectionAdapter(){
|
||||
public void widgetSelected(SelectionEvent e){browseForDestinationFolder();
|
||||
}
|
||||
});
|
||||
button_1.setText("Browse...");
|
||||
|
||||
initContents();
|
||||
}
|
||||
|
||||
/** initialize the contents of the sketch book path */
|
||||
private void initContents() {
|
||||
if (initialSketchbookPath == null){
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
// see if the sketchbook path has been set in the prefs
|
||||
// IPreferenceStore prefs = ProcessingEditorPlugin.getDefault().getPreferenceStore();
|
||||
//
|
||||
// IPath rootLoc = ResourcesPlugin.getWorkspace().getRoot().getLocation();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Browse button functionality to find a destination folder
|
||||
* <p>
|
||||
* Prettifies the file path if it happens to be in the workspace
|
||||
*/
|
||||
protected void browseForDestinationFolder() {
|
||||
IPath path = browse(getSketchbookLoc());
|
||||
if (path == null)
|
||||
return;
|
||||
IPath rootLoc = ResourcesPlugin.getWorkspace().getRoot().getLocation();
|
||||
if (rootLoc.isPrefixOf(path))
|
||||
path = path.setDevice(null).removeFirstSegments(rootLoc.segmentCount());
|
||||
sketchbookPathField.setText(path.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a dialog box allowing you to select a directory to use for the sketchbook
|
||||
*
|
||||
* @param path the path to be investigated
|
||||
* @return the path chosen in the dialog box
|
||||
*/
|
||||
private IPath browse(IPath path){
|
||||
DirectoryDialog dialog = new DirectoryDialog(getShell());
|
||||
if (path != null){
|
||||
if(path.segmentCount() > 1)
|
||||
dialog.setFilterPath(path.toOSString());
|
||||
}
|
||||
String result = dialog.open();
|
||||
if (result == null)
|
||||
return null;
|
||||
return new Path(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the sketchbook path exists and does not already contain a sketch with that name.
|
||||
*/
|
||||
private void updatePageComplete() {
|
||||
setPageComplete(false);
|
||||
|
||||
// check the sketchbook path first
|
||||
IPath sketchbookLoc = getSketchbookLoc();
|
||||
if (sketchbookLoc == null || !sketchbookLoc.toFile().exists()){
|
||||
setMessage(null);
|
||||
setErrorMessage("Please specify a sketchbook folder.");
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure the sketch isn't already present in the sketchbook
|
||||
String sketchName = getSketchName();
|
||||
for( File child : sketchbookLoc.toFile().listFiles()){
|
||||
if (child.isDirectory() && child.getName().equals(sketchName)){
|
||||
setMessage(null);
|
||||
setErrorMessage("A sketch with that name already exists. Please choose another.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if nothing was caught, enable the finish button
|
||||
setPageComplete(true);
|
||||
setMessage(null);
|
||||
setErrorMessage(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes and returns the sketchNameField contents
|
||||
* <p>
|
||||
* Uses the Processing sanitize method to ensure consistent naming
|
||||
* rules across editors.
|
||||
*
|
||||
* @return the contents of the sketchNameField as a string or null
|
||||
*/
|
||||
protected String getSketchName() {
|
||||
String text = sketchNameField.getText().trim();
|
||||
if (text.length() == 0)
|
||||
return null;
|
||||
text = Sketch.sanitizeName(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to resolve the contents of the sketch book path field to an IPath, and returns it.
|
||||
* If the field contains a relative path it will be resolved relative to the Eclipse workspace folder.
|
||||
*
|
||||
* @return an absolute IPath handle to the contents of the sketchbookPathField or null
|
||||
*/
|
||||
protected IPath getSketchbookLoc() {
|
||||
String text = sketchbookPathField.getText().trim();
|
||||
if (text.length() == 0)
|
||||
return null;
|
||||
IPath path = new Path(text);
|
||||
if (!path.isAbsolute()) // relative paths are relative to the Eclipse workspace
|
||||
path = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user