/* * $Id$ * * Copyright (c) 2004-2005 by the TeXlapse Team. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package net.sourceforge.texlipse.editor; import net.sourceforge.texlipse.TexlipsePlugin; import net.sourceforge.texlipse.editor.hover.TexHover; import net.sourceforge.texlipse.editor.partitioner.FastLaTeXPartitionScanner; import net.sourceforge.texlipse.editor.scanner.TexArgScanner; import net.sourceforge.texlipse.editor.scanner.TexCommentScanner; import net.sourceforge.texlipse.editor.scanner.TexMathScanner; import net.sourceforge.texlipse.editor.scanner.TexOptArgScanner; import net.sourceforge.texlipse.editor.scanner.TexScanner; import net.sourceforge.texlipse.editor.scanner.TexTikzScanner; import net.sourceforge.texlipse.properties.TexlipseProperties; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.preference.PreferenceStore; import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.TextAttribute; import org.eclipse.jface.text.TextPresentation; 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.reconciler.IReconciler; import org.eclipse.jface.text.reconciler.IReconcilingStrategy; import org.eclipse.jface.text.reconciler.MonoReconciler; import org.eclipse.jface.text.rules.DefaultDamagerRepairer; import org.eclipse.jface.text.rules.RuleBasedScanner; 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.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; import org.eclipse.ui.texteditor.spelling.SpellingService; /** * Configuration for the source viewer of the LaTeX * editor. * * @author Oskar Ojala * @author Antti Pirinen */ public class TexSourceViewerConfiguration extends TextSourceViewerConfiguration { private TexEditor editor; private TexMathScanner mathScanner; private TexScanner scanner; private TexCommentScanner commentScanner; private TexArgScanner argumentScanner; private TexOptArgScanner optArgumentScanner; private TexTikzScanner tikzScanner; private RuleBasedScanner verbatimScanner; private ColorManager colorManager; private TexAnnotationHover annotationHover; private ContentAssistant assistant; private TexHover textHover; // FIXME check this private IAutoEditStrategy autoIndentStrategy; /** * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getReconciler(org.eclipse.jface.text.source.ISourceViewer) */ @Override public IReconciler getReconciler(ISourceViewer sourceViewer) { if (fPreferenceStore == null || !fPreferenceStore.getBoolean(SpellingService.PREFERENCE_SPELLING_ENABLED)) return null; if (!TexlipsePlugin.getDefault().getPreferenceStore().getBoolean(TexlipseProperties.ECLIPSE_BUILDIN_SPELLCHECKER)) return null; //Set TeXlipse spelling Engine as default PreferenceStore store = new PreferenceStore(); store.setValue(SpellingService.PREFERENCE_SPELLING_ENGINE, "net.sourceforge.texlipse.LaTeXSpellEngine"); store.setValue(SpellingService.PREFERENCE_SPELLING_ENABLED, true); SpellingService spellingService = new SpellingService(store); if (spellingService.getActiveSpellingEngineDescriptor(store) == null) return null; IReconcilingStrategy strategy= new TeXSpellingReconcileStrategy(sourceViewer, spellingService); MonoReconciler reconciler= new MonoReconciler(strategy, true); reconciler.setDelay(500); reconciler.setProgressMonitor(new NullProgressMonitor()); return reconciler; } /** * Creates a new source viewer configuration. * * @param te The editor that this configuration is associated to */ public TexSourceViewerConfiguration(TexEditor editor) { super(EditorsUI.getPreferenceStore()); this.editor = editor; this.colorManager = new ColorManager(); this.annotationHover = new TexAnnotationHover(); // Adds a listener for changing content assistant properties if // these are changed in the preferences TexlipsePlugin.getDefault().getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (assistant == null) return; String property = event.getProperty(); if (TexlipseProperties.TEX_COMPLETION.equals(property)) { assistant.enableAutoActivation( TexlipsePlugin.getDefault().getPreferenceStore().getBoolean( TexlipseProperties.TEX_COMPLETION)); } else if (TexlipseProperties.TEX_COMPLETION_DELAY.equals(property)) { assistant.setAutoActivationDelay( TexlipsePlugin.getDefault().getPreferenceStore().getInt( TexlipseProperties.TEX_COMPLETION_DELAY)); } }; }); } /** * @return the annotation hover text provider for this editor */ public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) { return annotationHover; } /* (non-Javadoc) * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getAutoEditStrategies(org.eclipse.jface.text.source.ISourceViewer, java.lang.String) */ public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) { //return super.getAutoEditStrategies(sourceViewer, contentType); if (autoIndentStrategy == null) { autoIndentStrategy = new TexAutoIndentStrategy(); } return new IAutoEditStrategy[] {autoIndentStrategy}; } /** * Returns the configured partitioning for the given source viewer. * The partitioning is used when the querying content types from the * source viewer's input document. * @param sourceViewer the source viewer to be configured by this * configuration * @return the configured partitioning * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getConfiguredContentTypes(ISourceViewer) */ public String getConfiguredDocumentPartitioning(ISourceViewer sourceViewer) { return TexEditor.TEX_PARTITIONING; } /** * A method to get allowed content types. * @param sourceViewer the source viewer to be configured by this * configuration * @return a new String[] array of content types. */ public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) { return FastLaTeXPartitionScanner.TEX_PARTITION_TYPES; } /* (non-Javadoc) * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getContentAssistant(org.eclipse.jface.text.source.ISourceViewer) */ public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { // ContentAssistant assistant = new ContentAssistant(); assistant = new ContentAssistant(); assistant.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); // note that partitioning affects completions //TexCompletionProcessor tcp = new TexCompletionProcessor(this.editor.getDocumentModel()); TexCompletionProcessor tcp = new TexCompletionProcessor(this.editor.getDocumentModel(), sourceViewer); TexMathCompletionProcessor tmcp = new TexMathCompletionProcessor(this.editor.getDocumentModel(), sourceViewer); // assistant.setContentAssistProcessor(new TexCompletionProcessor(this.editor.getDocumentModel()), // IDocument.DEFAULT_CONTENT_TYPE); assistant.setContentAssistProcessor(tcp, IDocument.DEFAULT_CONTENT_TYPE); //assistant.setContentAssistProcessor(tcp, TexPartitionScanner.TEX_MATH); assistant.setContentAssistProcessor(tmcp, FastLaTeXPartitionScanner.TEX_MATH); assistant.setContentAssistProcessor(tcp, FastLaTeXPartitionScanner.TEX_CURLY_BRACKETS); assistant.setContentAssistProcessor(tcp, FastLaTeXPartitionScanner.TEX_SQUARE_BRACKETS); assistant.setContentAssistProcessor(tcp, FastLaTeXPartitionScanner.TEX_TIKZPIC); assistant.enableAutoActivation(TexlipsePlugin.getDefault().getPreferenceStore().getBoolean(TexlipseProperties.TEX_COMPLETION)); assistant.enableAutoInsert(true); assistant.setAutoActivationDelay(TexlipsePlugin.getDefault().getPreferenceStore().getInt(TexlipseProperties.TEX_COMPLETION_DELAY)); assistant.setProposalPopupOrientation(IContentAssistant.PROPOSAL_OVERLAY); assistant.setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_ABOVE); assistant.setInformationControlCreator(getInformationControlCreator(sourceViewer)); return assistant; } public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { PresentationReconciler reconciler = new PresentationReconciler(); reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); DefaultDamagerRepairer dr = null; dr = new DefaultDamagerRepairer(getTexVerbatimScanner()); reconciler.setDamager(dr, FastLaTeXPartitionScanner.TEX_VERBATIM); reconciler.setRepairer(dr, FastLaTeXPartitionScanner.TEX_VERBATIM); dr = new DefaultDamagerRepairer(getTeXMathScanner()); reconciler.setDamager(dr, FastLaTeXPartitionScanner.TEX_MATH); reconciler.setRepairer(dr, FastLaTeXPartitionScanner.TEX_MATH); dr = new DefaultDamagerRepairer(getTexCommentScanner()); reconciler.setDamager(dr, FastLaTeXPartitionScanner.TEX_COMMENT); reconciler.setRepairer(dr, FastLaTeXPartitionScanner.TEX_COMMENT); dr = new DefaultDamagerRepairer(getTexArgScanner()); reconciler.setDamager(dr, FastLaTeXPartitionScanner.TEX_CURLY_BRACKETS); reconciler.setRepairer(dr, FastLaTeXPartitionScanner.TEX_CURLY_BRACKETS); dr = new DefaultDamagerRepairer(getTexOptArgScanner()); reconciler.setDamager(dr, FastLaTeXPartitionScanner.TEX_SQUARE_BRACKETS); reconciler.setRepairer(dr, FastLaTeXPartitionScanner.TEX_SQUARE_BRACKETS); dr = new DefaultDamagerRepairer(getTexTikzScanner()); reconciler.setDamager(dr, FastLaTeXPartitionScanner.TEX_TIKZPIC); reconciler.setRepairer(dr, FastLaTeXPartitionScanner.TEX_TIKZPIC); dr = new DefaultDamagerRepairer(getTexScanner()); reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); return reconciler; } /** * Defines a default partition scanner and sets the default * color for it * @return a scanner to find default partitions. */ protected TexScanner getTexScanner() { if (scanner == null) { scanner = new TexScanner(colorManager); scanner.setDefaultReturnToken( new Token( new TextAttribute( colorManager.getColor(ColorManager.DEFAULT), null, colorManager.getStyle(ColorManager.DEFAULT_STYLE)))); } return scanner; } /** * Defines a math partition scanner and sets the default * color for it. * @return a scanner to detect math partitions */ protected TexMathScanner getTeXMathScanner() { if (mathScanner == null) { mathScanner = new TexMathScanner(colorManager); mathScanner.setDefaultReturnToken( new Token( new TextAttribute( colorManager.getColor(ColorManager.EQUATION), null, colorManager.getStyle(ColorManager.EQUATION_STYLE)))); } return mathScanner; } /** * Defines a comment scanner and sets the default color for it * @return a scanner to detect comment partitions */ protected TexCommentScanner getTexCommentScanner() { if (commentScanner == null) { commentScanner = new TexCommentScanner(colorManager); commentScanner.setDefaultReturnToken( new Token( new TextAttribute( colorManager.getColor(ColorManager.COMMENT), null, colorManager.getStyle(ColorManager.COMMENT_STYLE)))); } return commentScanner; } /** * Defines an argument (curly bracket) scanner and sets the default color for it * @return a scanner to detect argument partitions */ protected TexArgScanner getTexArgScanner() { if (argumentScanner == null) { argumentScanner = new TexArgScanner(colorManager); argumentScanner.setDefaultReturnToken( new Token( new TextAttribute( colorManager.getColor(ColorManager.CURLY_BRACKETS), null, colorManager.getStyle(ColorManager.CURLY_BRACKETS_STYLE)))); } return argumentScanner; } /** * Defines an optional argument (square bracket) scanner and sets the default color for it * @return a scanner to detect argument partitions */ protected TexOptArgScanner getTexOptArgScanner() { if (optArgumentScanner == null) { optArgumentScanner = new TexOptArgScanner(colorManager); optArgumentScanner.setDefaultReturnToken( new Token( new TextAttribute( colorManager.getColor(ColorManager.SQUARE_BRACKETS), null, colorManager.getStyle(ColorManager.SQUARE_BRACKETS_STYLE)))); } return optArgumentScanner; } /** * Defines a tikz environment or single line scanner and sets the default color for it * @return a scanner to detect argument partitions */ protected TexTikzScanner getTexTikzScanner() { if (tikzScanner == null) { tikzScanner = new TexTikzScanner(colorManager); tikzScanner.setDefaultReturnToken( new Token( new TextAttribute( colorManager.getColor(ColorManager.DEFAULT), null, colorManager.getStyle(ColorManager.DEFAULT_STYLE)))); } return tikzScanner; } /** * Defines a verbatim scanner and sets the default color for it * @return a scanner to detect verbatim style partitions */ protected RuleBasedScanner getTexVerbatimScanner() { if (verbatimScanner == null) { //We need no rules, because the user can write everything inside a verbatim env verbatimScanner = new RuleBasedScanner (); verbatimScanner.setDefaultReturnToken( new Token( new TextAttribute( colorManager.getColor(ColorManager.VERBATIM), null, colorManager.getStyle(ColorManager.VERBATIM_STYLE)))); } return verbatimScanner; } public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { if (textHover == null) { textHover = new TexHover(editor); } return textHover; } /** * Displays all commands in bold and all groups in italic */ private static final DefaultInformationControl.IInformationPresenter presenter = new DefaultInformationControl.IInformationPresenter() { public String updatePresentation(Display display, String infoText, TextPresentation presentation, int maxWidth, int maxHeight) { int cstart = -1; int gstart = -1; // Loop over all characters of information text for (int i = 0; i < infoText.length(); i++) { switch (infoText.charAt(i)) { case '{': // if we get \foo\{ or \{, then a group doesn't start if (cstart >= 0 && infoText.charAt(i-1) != '\\') { boldRange(cstart, i - cstart, presentation, false); cstart = -1; gstart = i; } else if (cstart < 0) { gstart = i; } break; case '}': // if we get \} then it doesn't mean that a group ends if (cstart >= 0 && infoText.charAt(i-1) != '\\') { boldRange(cstart, i - cstart, presentation, true); cstart = -1; if (gstart >= 0) { italicizeRange(gstart, cstart - gstart + 1, presentation); gstart = -1; } } else if (gstart >= 0) { italicizeRange(gstart, i - gstart + 1, presentation); gstart = -1; } break; case '\\': if (cstart < 0 && gstart < 0) { cstart = i; } break; case '\n': case '\r': case '\t': case ' ': if (cstart >= 0) { if (gstart >= 0) { italicizeRange(gstart, cstart - gstart, presentation); boldRange(cstart, i - cstart, presentation, true); gstart = i; } else { boldRange(cstart, i - cstart, presentation, false); } cstart = -1; } break; } } // check if we want to bold to the end of string if (gstart >= 0) { italicizeRange(gstart, infoText.length() - gstart, presentation); } if (cstart >= 0) { boldRange(cstart, infoText.length() - cstart, presentation, false); } // Return the information text return infoText; } private void boldRange(int start, int length, TextPresentation presentation, boolean doItalic) { // We have found a tag and create a new style range int fontStyle = doItalic ? (SWT.BOLD | SWT.ITALIC) : SWT.BOLD; StyleRange range = new StyleRange(start, length, null, null, fontStyle); // Add this style range to the presentation presentation.addStyleRange(range); } private void italicizeRange(int start, int length, TextPresentation presentation) { StyleRange range = new StyleRange(start, length, null, null, SWT.ITALIC); presentation.addStyleRange(range); } }; public IInformationControlCreator getInformationControlCreator (ISourceViewer sourceViewer) { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { return new DefaultInformationControl(parent, presenter); } }; } }