/** * Copyright (c) 2003-2008 by Leif Frenzel - see http://leiffrenzel.de * (c) 2012 by JP Moresmau * This code is made available under the terms of the Eclipse Public License, * version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html * */ package net.sf.eclipsefp.haskell.ui.internal.editors.haskell; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import net.sf.eclipsefp.haskell.buildwrapper.BWFacade; import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin; import net.sf.eclipsefp.haskell.buildwrapper.JobFacade; import net.sf.eclipsefp.haskell.buildwrapper.types.EvalHandler; import net.sf.eclipsefp.haskell.buildwrapper.types.Location; import net.sf.eclipsefp.haskell.buildwrapper.types.NameDef; import net.sf.eclipsefp.haskell.buildwrapper.types.NameDefHandler; import net.sf.eclipsefp.haskell.buildwrapper.types.Note; import net.sf.eclipsefp.haskell.buildwrapper.types.OutlineDef; import net.sf.eclipsefp.haskell.buildwrapper.types.OutlineDef.OutlineDefType; import net.sf.eclipsefp.haskell.buildwrapper.types.OutlineHandler; import net.sf.eclipsefp.haskell.buildwrapper.types.OutlineResult; import net.sf.eclipsefp.haskell.core.HaskellCorePlugin; import net.sf.eclipsefp.haskell.core.util.ResourceUtil; import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin; import net.sf.eclipsefp.haskell.ui.editor.actions.IEditorActionDefinitionIds; import net.sf.eclipsefp.haskell.ui.internal.backend.CabalFileChangeListenerManager; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.actions.FormatAction; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.actions.HaddockBlockDocumentFollowingAction; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.actions.HaddockDocumentFollowingAction; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.actions.HaddockDocumentPreviousAction; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.actions.OrganizeImportAction; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.actions.PragmaCommentAction; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.imports.ImportsManager; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.text.HaskellFoldingStructureProvider; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.text.ScionTokenScanner; import net.sf.eclipsefp.haskell.ui.internal.editors.text.HaskellViewer; import net.sf.eclipsefp.haskell.ui.internal.editors.text.MarkOccurrenceComputer; import net.sf.eclipsefp.haskell.ui.internal.preferences.IPreferenceConstants; import net.sf.eclipsefp.haskell.ui.internal.preferences.editor.IEditorPreferenceNames; import net.sf.eclipsefp.haskell.ui.internal.resolve.SelectAnnotationForQuickFix; import net.sf.eclipsefp.haskell.ui.internal.util.UITexts; import net.sf.eclipsefp.haskell.ui.internal.views.outline.HaskellOutlinePage; import net.sf.eclipsefp.haskell.ui.internal.views.worksheet.WorkSheetView; import net.sf.eclipsefp.haskell.ui.util.CabalFileChangeListener; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IStorage; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.ISourceViewerExtension2; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.IVerticalRulerInfo; import org.eclipse.jface.text.source.projection.ProjectionSupport; import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IStorageEditorInput; import org.eclipse.ui.IViewPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.contexts.IContextService; import org.eclipse.ui.editors.text.TextEditor; import org.eclipse.ui.progress.UIJob; import org.eclipse.ui.texteditor.GotoAnnotationAction; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; /** * The main editor class for the Haskell editor. * * @author Leif Frenzel */ public class HaskellEditor extends TextEditor implements IEditorPreferenceNames, NameDefHandler,CabalFileChangeListener { /** The Haskell editor's identifier. */ public static final String ID = HaskellEditor.class.getName(); /** Action string associated with a following Haddock documentation comment */ public static final String HADDOCK_DOCUMENT_FOLLOWING_ACTION = "Haddock.Follow"; //$NON-NLS-1$ /** Action string associated with a previous Haddock documentation comment */ public static final String HADDOCK_DOCUMENT_PREVIOUS_ACTION = "Haddock.Previous"; //$NON-NLS-1$ /** Action string associated with a following Haddock documentation comment */ public static final String HADDOCK_BLOCK_DOCUMENT_FOLLOWING_ACTION = "Haddock.Block.Follow"; //$NON-NLS-1$ /** Action string associated with line comment insertion */ public static final String LINE_COMMENT_ACTION = "Comment"; //$NON-NLS-1$ /** Action string associated with line uncommenting */ public static final String LINE_UNCOMMENT_ACTION = "Uncomment"; //$NON-NLS-1$ /** Action string associated with pragma comments insertion */ public static final String COMMENT_PRAGMA_ACTION = "Comment.Pragma"; //$NON-NLS-1$ /** Action string associated with formatting */ public static final String FORMAT_ACTION = "Format"; //$NON-NLS-1$ /** Action string associated with renaming */ public static final String RENAME_ACTION = "Rename"; //$NON-NLS-1$ /** Action string associated with formatting */ public static final String IMPORTS_ACTION = "Imports"; //$NON-NLS-1$ /** Resource prefix used to query properties for line comments (see plugin.properties) */ public static final String commentResourcePrefix = "CommentAction"; //$NON-NLS-1$ /** Resource prefix used to query properties for line commenting */ public static final String uncommentResourcePrefix = "UncommentAction"; /** Resource prefix used to query properties for pragma comments */ private static final String commentPragmaResourcePrefix = "CommentPragmaAction"; //$NON-NLS-1$ /** Resource prefix used to query properties for format */ private static final String formatResourcePrefix = "FormatAction"; //$NON-NLS-1$ /** Resource prefix used to query properties for format */ private static final String importsResourcePrefix = "ImportsAction"; //$NON-NLS-1$ /** The key binding context active while the Haskell editor is active */ private static final String CONTEXT_ID = HaskellEditor.class.getName() + ".context"; //$NON-NLS-1$ private static final String SIMPLE_CONTEXT_ID = HaskellEditor.class.getSimpleName() + "Context"; public static final String TEXT_CONTENTTYPE="__haskell_text_content_type"; private HaskellOutlinePage outlinePage; private ProjectionSupport projectionSupport; private MarkOccurrenceComputer markOccurrencesComputer; private HaskellFoldingStructureProvider foldingStructureProvider; //private List<OutlineDef> outline; private Map<String,List<OutlineDef>> defByName; private ScionTokenScanner tokenScanner; private ImportsManager importsManager; /** * the names in scope as given by GHC */ private final Collection<NameDef> names=new ArrayList<>(); /** * The scion-server supporting this editor. * * @note This variable isn't actually used to communicate with the * scion-server. It's sole purpose is change detection, since the editor * can be reused between different projects. */ //private ScionInstance instance = null; private OutlineResult lastOutlineResult=null; private final OutlineHandler outlineHandler = new OutlineHandler() { @Override public void handleOutline( final OutlineResult or ) { if (!or.getOutlineDefs().isEmpty() || or.isBuildOK()){// avoid removing all outline on error if (outlinePage!=null){ outlinePage.setInput( or.getOutlineDefs() ); } lastOutlineResult=or; if (foldingStructureProvider!=null){ foldingStructureProvider.updateFoldingRegions( or.getOutlineDefs() ); } } else if (!or.isBuildOK() && or.getNotes()!=null && or.getNotes().size()>0 && (lastOutlineResult==null || lastOutlineResult.isEmpty())){ List<OutlineDef> errorsOutline=new ArrayList<>(); for (Note n:or.getNotes()){ if (n.getKind().equals( Note.Kind.ERROR )){ OutlineDef def=new OutlineDef(n.getMessage(),OutlineDefType.ERROR,n.getLocation()) ; errorsOutline.add( def ); } } if (outlinePage!=null){ outlinePage.setInput( errorsOutline); } lastOutlineResult=or; if (foldingStructureProvider!=null){ foldingStructureProvider.updateFoldingRegions( Collections.<OutlineDef>emptyList() ); } } } }; /** Default constructor */ public HaskellEditor() { super(); } // public void reveal( final IHaskellLanguageElement element ) { // Assert.isNotNull( element ); // IDocument doc = getSourceViewer().getDocument(); // ISourceLocation srcLoc = element.getSourceLocation(); // int offset = -1; // try { // offset = doc.getLineOffset( srcLoc.getLine() ) + srcLoc.getColumn(); // } catch( final BadLocationException badlox ) { // // ignore // } // int length = element.getName().length(); // getSourceViewer().revealRange( offset, length ); // } public IDocument getDocument() { IDocumentProvider docProvider = getDocumentProvider(); if (docProvider!=null){ return docProvider.getDocument( getEditorInput() ); } return null; } // interface methods of TextEditor // //////////////////////////////// @Override protected void initializeEditor() { super.initializeEditor(); setSourceViewerConfiguration( new HaskellSourceViewerConfiguration( this ) ); setEditorContextMenuId( "#" + SIMPLE_CONTEXT_ID ); //$NON-NLS-1$ setPreferenceStore( HaskellUIPlugin.getEditorPreferenceStore()); initMarkOccurrences(); foldingStructureProvider = new HaskellFoldingStructureProvider( this ); } public HaskellFoldingStructureProvider getFoldingStructureProvider() { return foldingStructureProvider; } /* (non-Javadoc) * @see org.eclipse.ui.editors.text.TextEditor#handlePreferenceStoreChanged(org.eclipse.jface.util.PropertyChangeEvent) */ @Override protected void handlePreferenceStoreChanged( final PropertyChangeEvent event ) { // ensure token scanner is updated first tokenScanner.propertyChange( event ); super.handlePreferenceStoreChanged( event ); if (event.getProperty().equals( EDITOR_TAB_WIDTH )){ /*getSourceViewer().getTextWidget().setTabs((Integer)event.getNewValue()); for (String t : getSourceViewerConfiguration().getConfiguredContentTypes(getSourceViewer())) { getSourceViewer().setAutoEditStrategies(getSourceViewerConfiguration().getAutoEditStrategies(getSourceViewer(), t), t); } */ ISourceViewer viewer=getSourceViewer(); ((ISourceViewerExtension2)viewer).unconfigure(); viewer.configure(getSourceViewerConfiguration()); } } @Override protected boolean affectsTextPresentation( final PropertyChangeEvent evt ) { String prop = evt.getProperty(); return super.affectsTextPresentation( evt ) || isAffectingProperty( prop ); } @Override protected void configureSourceViewerDecorationSupport( final SourceViewerDecorationSupport support ) { super.configureSourceViewerDecorationSupport( support ); //support.setCharacterPairMatcher( new HaskellCharacterPairMatcher() ); support.setCharacterPairMatcher( new DefaultCharacterPairMatcher( new char[]{ '{', '}', '(', ')', '[', ']' } ) ); String bracketsKey = EDITOR_MATCHING_BRACKETS; String colorKey = EDITOR_MATCHING_BRACKETS_COLOR; support.setMatchingCharacterPainterPreferenceKeys( bracketsKey, colorKey ); support.setSymbolicFontName( getFontPropertyPreferenceKey() ); } @Override protected String[] collectContextMenuPreferencePages() { List<String> ls=new ArrayList<>(Arrays.asList( super.collectContextMenuPreferencePages())); ls.add( "net.sf.eclipsefp.haskell.ui.internal.preferences.editor.AppearancePP" ); //ls.add("net.sf.eclipsefp.haskell.ui.internal.preferences.editor.AnnotationsPP"); ls.add( "net.sf.eclipsefp.haskell.ui.internal.preferences.editor.SyntaxPP"); ls.add("net.sf.eclipsefp.haskell.ui.internal.preferences.templates.HSCodeTemplatePreferences"); return ls.toArray( new String[ls.size()] ); } @Override protected String[] collectOverviewRulerMenuPreferencePages() { List<String> ls=new ArrayList<>(Arrays.asList( super.collectOverviewRulerMenuPreferencePages())); ls.add( "net.sf.eclipsefp.haskell.ui.internal.preferences.editor.AppearancePP" ); //ls.add("net.sf.eclipsefp.haskell.ui.internal.preferences.editor.AnnotationsPP"); return ls.toArray( new String[ls.size()] ); } @Override public void editorContextMenuAboutToShow( final IMenuManager menu ) { super.editorContextMenuAboutToShow( menu ); if( isEditable() ) { IMenuManager mmSource = new MenuManager( UITexts.editor_actions_source, "source" ); //$NON-NLS-1$ menu.prependToGroup( ITextEditorActionConstants.GROUP_EDIT, mmSource ); mmSource.add( new Separator( "comments" ) ); //$NON-NLS-1$ mmSource.add( new Separator( "formatting" ) ); //$NON-NLS-1$ mmSource.add( new Separator( "organize" ) ); //$NON-NLS-1$ addAction( mmSource, "comments", LINE_COMMENT_ACTION ); //$NON-NLS-1$ addAction( mmSource, "comments", LINE_UNCOMMENT_ACTION ); //$NON-NLS-1$ addAction( mmSource, "comments", COMMENT_PRAGMA_ACTION); //$NON-NLS-1$ addAction( mmSource, "comments", HADDOCK_DOCUMENT_FOLLOWING_ACTION); //$NON-NLS-1$ addAction( mmSource, "comments", HADDOCK_DOCUMENT_PREVIOUS_ACTION); //$NON-NLS-1$ addAction( mmSource, "comments", HADDOCK_BLOCK_DOCUMENT_FOLLOWING_ACTION); //$NON-NLS-1$ addAction( mmSource, "formatting", FORMAT_ACTION); //$NON-NLS-1$ addAction( mmSource, "formatting", IMPORTS_ACTION); //$NON-NLS-1$ } } /** * get the location in display coordinates of the current selection * @return */ public Point getSelectedPoint(){ Point p2=getSourceViewer().getTextWidget().getLocationAtOffset( getSourceViewer().getTextWidget().getSelectionRange().x ); return getSourceViewer().getTextWidget().toDisplay(p2 ); } public ISourceViewer getViewer(){ return super.getSourceViewer(); } @Override protected void createActions() { super.createActions(); // content assist String defId = ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS; setAction("ContentAssistProposal", HaskellViewer.createTextOpAction(this, "ContentAssistProposal", "ContentAssistProposal", ISourceViewer.CONTENTASSIST_PROPOSALS, defId )); //$NON-NLS-1$ // comment/uncomment HaskellViewer.createTextOpAction(this, LINE_COMMENT_ACTION, commentResourcePrefix,HaskellViewer.TOGGLE_COMMENT, IEditorActionDefinitionIds.COMMENT ); // createTextOpAction(this, LINE_UNCOMMENT_ACTION, uncommentResourcePrefix, ITextOperationTarget.STRIP_PREFIX, // IEditorActionDefinitionIds.UNCOMMENT ); // New actions that we contribute: ResourceBundle bundle = HaskellUIPlugin.getDefault().getResourceBundle(); setAction(FORMAT_ACTION, new FormatAction( bundle, formatResourcePrefix + ".", this )); setAction(IMPORTS_ACTION, new OrganizeImportAction( bundle, importsResourcePrefix + ".", this )); setAction(COMMENT_PRAGMA_ACTION, new PragmaCommentAction( bundle, commentPragmaResourcePrefix + ".", this )); setAction( HADDOCK_DOCUMENT_FOLLOWING_ACTION, new HaddockDocumentFollowingAction( bundle, "HaddockDocumentFollowing.", this )); setAction( HADDOCK_DOCUMENT_PREVIOUS_ACTION, new HaddockDocumentPreviousAction( bundle, "HaddockDocumentPrevious.", this )); setAction( HADDOCK_BLOCK_DOCUMENT_FOLLOWING_ACTION, new HaddockBlockDocumentFollowingAction( bundle, "HaddockDocumentBlockFollow.", this )); addRulerContextMenuListener( new IMenuListener() { @Override public void menuAboutToShow( final IMenuManager manager ) { IVerticalRulerInfo service= (IVerticalRulerInfo)getAdapter(IVerticalRulerInfo.class); if (service!=null){ int line=service.getLineOfLastMouseButtonActivity(); for (Iterator<?> it=getSourceViewer().getAnnotationModel().getAnnotationIterator();it.hasNext();){ Annotation ann = (Annotation) it.next(); //if (ann instanceof MarkerAnnotation){ Position p=getSourceViewer().getAnnotationModel().getPosition( ann ); try { if (p!=null && getDocument().getLineOfOffset(p.getOffset())==line){ if (((ProjectionViewer)getSourceViewer()).getQuickAssistAssistant().canFix( ann )){ IAction action=new SelectAnnotationForQuickFix( HaskellEditor.this , (ProjectionViewer)getSourceViewer(), ann ); manager.add( action ); //return; } } } catch (BadLocationException ble){ // ignore } // } } } } }); QuickFixAction action = new QuickFixAction(HaskellUIPlugin.getDefault().getResourceBundle(), "RulerQuickFixAction", this, getVerticalRuler()); //$NON-NLS-1$ setAction(ITextEditorActionConstants.RULER_CLICK, action); /**navigation actions * http://sourceforge.net/projects/eclipsefp/forums/forum/371922/topic/6192123 * **/ GotoAnnotationAction gf=new GotoAnnotationAction( this, true ); gf.setActionDefinitionId( ITextEditorActionConstants.NEXT ); setAction( ITextEditorActionConstants.NEXT, gf); markAsStateDependentAction( ITextEditorActionConstants.NEXT, true ); getEditorSite().getActionBars().setGlobalActionHandler( ITextEditorActionConstants.NEXT, gf ); GotoAnnotationAction gb=new GotoAnnotationAction( this, false ); gb.setActionDefinitionId( ITextEditorActionConstants.PREVIOUS ); setAction( ITextEditorActionConstants.PREVIOUS, gb); markAsStateDependentAction( ITextEditorActionConstants.PREVIOUS, true ); getEditorSite().getActionBars().setGlobalActionHandler( ITextEditorActionConstants.PREVIOUS, gb ); } @Override protected ISourceViewer createSourceViewer( final Composite parent, final IVerticalRuler ruler, final int styles ) { // copied this from the super class, replaced source viewer with // projection viewer fAnnotationAccess = createAnnotationAccess(); fOverviewRuler = createOverviewRuler( getSharedColors() ); ProjectionViewer viewer = new HaskellViewer( parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles ); // ensure decoration support has been created and configured. getSourceViewerDecorationSupport( viewer ); return viewer; } @Override public void createPartControl( final Composite parent ) { super.createPartControl( parent ); ProjectionViewer projectionViewer = ( ProjectionViewer )getSourceViewer(); projectionSupport = new ProjectionSupport( projectionViewer, getAnnotationAccess(), getSharedColors() ); projectionSupport.install(); projectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$ projectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$ projectionViewer.doOperation( ProjectionViewer.TOGGLE ); if( markOccurrencesComputer != null ) { ISelectionChangedListener listener = new ISelectionChangedListener() { private UIJob previous; @Override public void selectionChanged( final SelectionChangedEvent event ) { IDocument doc = getSourceViewer().getDocument(); if( markOccurrencesComputer != null ) { markOccurrencesComputer.setDocument( doc ); if (previous!=null){ previous.cancel(); } previous=new UIJob(UITexts.editor_occurrences_job) { @Override public IStatus runInUIThread(final IProgressMonitor monitor) { if( markOccurrencesComputer != null ) { markOccurrencesComputer.compute(monitor); } return Status.OK_STATUS; } }; previous.schedule(); } } }; projectionViewer.addPostSelectionChangedListener( listener ); } activateContext(); } private void activateContext() { IContextService contextService = ( IContextService )getSite().getService( IContextService.class ); contextService.activateContext( CONTEXT_ID ); } @Override public Object getAdapter( @SuppressWarnings("rawtypes") final Class required ) { Object result = null; // adapt the displayed source file to the outline viewer if( IContentOutlinePage.class.equals( required ) ) { if( outlinePage == null ) { //updateOutline(); synchronize(); } result = outlinePage; } else if( projectionSupport != null ) { result = projectionSupport.getAdapter( getSourceViewer(), required ); } if( result == null ) { result = super.getAdapter( required ); } return result; } // supplement some TextEditor functionality with specific handling // needed because we have an attached outline page // /////////////////////////////////////////////////////////////// @Override public void dispose() { CabalFileChangeListenerManager.removeDynamicListener( this ); if( outlinePage != null ) { outlinePage.setInput( null ); outlinePage.dispose(); outlinePage=null; } if (tokenScanner!=null){ tokenScanner.dispose(); tokenScanner=null; } names.clear(); lastOutlineResult=null; importsManager=null; if (markOccurrencesComputer!=null){ markOccurrencesComputer.dispose(); markOccurrencesComputer=null; } final IFile file=findFile(); if (file!=null){ HaskellCorePlugin.getModifiedByEditors().remove( file ); // if we close a dirty editor, we need to recalculate if (isDirty()){ JobFacade jf=BuildWrapperPlugin.getJobFacade( file.getProject() ); if (jf!=null){ jf.updateFromEditor( file,getDocument(), null, null, true, true,null ); } } else { BWFacade f=BuildWrapperPlugin.getFacade( file.getProject() ); if (f!=null){ f.endLongRunning( file ); } } } super.dispose(); } @Override public void doRevertToSaved() { super.doRevertToSaved(); editorSaved(); } /* (non-Javadoc) * @see org.eclipse.ui.texteditor.AbstractTextEditor#doSave(org.eclipse.core.runtime.IProgressMonitor) */ @Override public void doSave( final IProgressMonitor progressMonitor ) { if (getPreferenceStore().getBoolean( IPreferenceConstants.STYLISHHASKELL_SAVE )){ getAction( FORMAT_ACTION ).run(); } super.doSave( progressMonitor ); } @Override protected void editorSaved() { // Reload the file on the buildwrapper server side IFile file = findFile(); if( file != null) { synchronize(); BuildWrapperPlugin.getDefault().getUsageThread().addProject(file.getProject()); if (tokenScanner!=null){ tokenScanner.markTaskTags(); } } super.editorSaved(); } @Override public void init( final IEditorSite site, final IEditorInput input ) throws PartInitException { super.init( site, input ); CabalFileChangeListenerManager.addDynamicListener( this ); outlinePage = new HaskellOutlinePage( this ); if (lastOutlineResult!=null){ outlinePage.setInput( lastOutlineResult.getOutlineDefs() ); } IFile file = findFile(); if (file!=null){ BWFacade f=BuildWrapperPlugin.getFacade( findFile().getProject() ); if (f!=null){ f.synchronize1( file,true ); } } } /** * Get the editor's current input file. * * @return An IFile object if the editor's input is a file, otherwise null. */ public IFile findFile() { IEditorInput input = getEditorInput(); if( input instanceof IFileEditorInput ) { return ( ( IFileEditorInput ) input ).getFile(); } if (input instanceof IStorageEditorInput){ try { IStorage st=((IStorageEditorInput)input).getStorage(); if (st instanceof IFile){ return (IFile)st; } } catch (CoreException ce){ HaskellUIPlugin.log( ce ); } } HaskellUIPlugin.log( NLS.bind( UITexts.unsupported_input, this.getClass().getName(),input.getClass().getName() ), IStatus.WARNING ); return null; } private String moduleName=null; public String getModuleName(){ if (moduleName==null){ moduleName=ResourceUtil.getModuleName( findFile() ); } return moduleName; } public void setModuleName(final String module){ moduleName=module; } @Override public void doSetInput( final IEditorInput input ) throws CoreException { super.doSetInput( input ); IFile f=findFile(); if (f!=null){ HaskellCorePlugin.getModifiedByEditors().add( f ); } // Ensure we synchronize to the correct file, which ought to have been set by the call to super. synchronize(); // file may have been renamed moduleName=null; new TabChecker( this ); } /* (non-Javadoc) * @see org.eclipse.ui.texteditor.StatusTextEditor#handleElementContentReplaced() */ @Override protected void handleElementContentReplaced() { super.handleElementContentReplaced(); synchronize(); } // helping methods // //////////////// private boolean isAffectingProperty( final String property ) { return property.equals( EDITOR_COMMENT_COLOR ) || property.equals( EDITOR_COMMENT_BOLD ) || property.equals( EDITOR_DOC_COLOR ) || property.equals( EDITOR_DOC_BOLD ) || property.equals( EDITOR_PRAGMA_COLOR ) || property.equals( EDITOR_PRAGMA_BOLD ) || property.equals( EDITOR_LITERATE_COMMENT_COLOR ) || property.equals( EDITOR_LITERATE_COMMENT_BOLD ) || property.equals( EDITOR_DEFAULT_COLOR ) || property.equals( EDITOR_DEFAULT_BOLD ) || property.equals( EDITOR_FUNCTION_COLOR ) || property.equals( EDITOR_FUNCTION_BOLD ) || property.equals( EDITOR_KEYWORD_COLOR ) || property.equals( EDITOR_KEYWORD_BOLD ) || property.equals( EDITOR_STRING_COLOR ) || property.equals( EDITOR_STRING_BOLD ) || property.equals( EDITOR_CHAR_COLOR ) || property.equals( EDITOR_CHAR_BOLD ) || property.equals( EDITOR_NUMBER_COLOR ) || property.equals( EDITOR_NUMBER_BOLD ) || property.equals( EDITOR_VAR_COLOR ) || property.equals( EDITOR_VAR_BOLD ) || property.equals( EDITOR_VARSYM_COLOR ) || property.equals( EDITOR_VARSYM_BOLD ) || property.equals( EDITOR_CON_COLOR ) || property.equals( EDITOR_CON_BOLD ) || property.equals( EDITOR_SYMBOL_COLOR ) || property.equals( EDITOR_SYMBOL_BOLD ) || property.equals( EDITOR_CPP_COLOR ) || property.equals( EDITOR_CPP_BOLD ) || property.equals( EDITOR_TH_COLOR ) || property.equals( EDITOR_TH_BOLD ) || property.equals( EDITOR_TAB_WIDTH ); } @Override public void setFocus() { super.setFocus(); //IFile f=findFile(); //HaskellUIPlugin.getDefault().getScionInstanceManager(f).backgroundTypecheckFile( f ); } private void initMarkOccurrences() { // TODO TtC replace by something not Cohatoe-based /* * CohatoeServer server = CohatoeServer.getInstance(); IMarkOccurrences mo = * server.createFunction( IMarkOccurrences.class ); if( mo != null ) { * markOccurrencesComputer = new MarkOccurrenceComputer( this, mo ); } */ markOccurrencesComputer = new MarkOccurrenceComputer( this); } // public void synchronize() { // IDocument document = getDocument(); // // if( markOccurrencesComputer != null ) { // WorkspaceJob occurances = new WorkspaceJob("Haskell occurance marker") { // @Override // public IStatus runInWorkspace( final IProgressMonitor monitor ) { // markOccurrencesComputer.compute(); // return Status.OK_STATUS; // } // }; // // markOccurrencesComputer.setDocument( document ); // occurances.schedule(); // } // } /** * Update the outline page using the current editor file. */ // public void updateOutline() { // updateOutline(findFile()); // } //private boolean needWrite=false; public void synchronize(){ IFile file=findFile(); if(importsManager!=null){ importsManager.reset(); } /*BWFacade f=BuildWrapperPlugin.getFacade( file.getProject() ); if (f!=null){ f.write( file,getDocument().get() ); }*/ if (file!=null && ResourceUtil.isInHaskellProject( file )){ JobFacade jf=BuildWrapperPlugin.getJobFacade( file.getProject() ); if (jf!=null){ /*if (!needWrite && isDirty()){ // we need to write the docs if we're dirty needWrite=true; }*/ //needWrite?getDocument():null List<? extends EvalHandler> handlers=null; if (!isDirty() && PlatformUI.getWorkbench().getActiveWorkbenchWindow()!=null && PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()!=null){ IViewPart v=PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().findView( WorkSheetView.ID ); if (v instanceof WorkSheetView){ handlers=((WorkSheetView)v).getEvalComposites( this ); } } jf.updateFromEditor( file,getDocument(), outlineHandler,this,false,false,handlers ); /*if (!isDirty()){ // now we've written and not dirty needWrite=false; }*/ } else { outlineHandler.handleOutline( new OutlineResult()); } } } /* (non-Javadoc) * @see net.sf.eclipsefp.haskell.ui.util.CabalFileChangeListener#cabalFileChanged(org.eclipse.core.resources.IFile) */ @Override public void cabalFileChanged( final IFile cabalF ) { IFile file=findFile(); if (file!=null){ BWFacade f=BuildWrapperPlugin.getFacade( file.getProject() ); if (f!=null){ // cabal has changed: restart long running job to take new packages, flags, etc f.endLongRunning( file ); synchronize(); } } } /** * @return the importsManager */ public ImportsManager getImportsManager() { if(importsManager==null){ importsManager=new ImportsManager(findFile(),getDocument(),names); } return importsManager; } // private void updateOutline(final IFile currentFile) { // if ( currentFile != null && outlinePage != null ) { // //ScionInstance instance = getInstance( currentFile ); // JobFacade jf=BuildWrapperPlugin.getJobFacade( currentFile.getProject() ); //// getDocument(), // if (jf!=null){ // jf.outline( currentFile, outlineHandler); // } else { // outlineHandler.handleOutline( Collections.<OutlineDef>emptyList() ); // } // } // } public synchronized Location getOutlineLocation(final String name) { if ( defByName==null ){ buildDefByName(); } if (defByName!=null){ List<OutlineDef> l = defByName.get( name ); if (l!=null && l.size()>0){ return l.iterator().next().getLocation(); } } return null; } public boolean hasOutline() { return lastOutlineResult!=null; } public OutlineResult getLastOutlineResult() { return lastOutlineResult; } // public void showOutlineLocation(final String shortName){ // Location location = getOutlineLocation( shortName ); // if( location != null ) { // IDocument document = getDocument(); // int startOffset = location.getStartOffset( document ); // int length = location.getLength( document ); // selectAndReveal( startOffset, length ); // } // } public List<String> getLocalNames(){ List<String> ls=new ArrayList<>(); if (outlinePage!=null && outlinePage.getInput()!=null){ for (OutlineDef od:outlinePage.getInput()){ ls.add(od.getName()); } } return ls; } private void buildDefByName(){ if (outlinePage!=null && outlinePage.getInput()!=null){ defByName=new HashMap<>(); for (OutlineDef od:outlinePage.getInput()){ buildDefByName(od); } } } private void buildDefByName(final OutlineDef od){ List<OutlineDef> l=defByName.get( od.getName()); if(l==null){ l=new ArrayList<>(); defByName.put( od.getName(), l ); } l.add( od ); for (OutlineDef od2:od.getChildren()){ buildDefByName(od2); } } /** * is the start of the selection contained in an outline element * useful to find if a selection for a breakpoint is in real code */ public boolean isInOutline(final ISelection sel){ if (outlinePage!=null && outlinePage.getInput()!=null && sel instanceof ITextSelection){ ITextSelection tsel=(ITextSelection)sel; int line=tsel.getStartLine()+1; for (OutlineDef od:outlinePage.getInput()){ // here we could filter out if inside some type of outlinedef we do not want breakpoints if (od.getLocation().getStartLine()<=line && od.getLocation().getEndLine()>=line){ return true; } } } return false; } public ScionTokenScanner getTokenScanner() { return tokenScanner; } public void setTokenScanner( final ScionTokenScanner tokenScanner ) { this.tokenScanner = tokenScanner; } /* (non-Javadoc) * @see net.sf.eclipsefp.haskell.buildwrapper.types.NameDefHandler#handleNameDefs(java.util.Collection) */ @Override public void handleNameDefs( final Collection<NameDef> names ) { // we keep the old names if the build failed if (names!=null){ this.names.clear(); this.names.addAll( names ); } } /** * @return the names */ public Collection<NameDef> getNames() { return names; } public Location getOutlineSpan(final int offset){ Location odL=null; IDocument doc=getDocument(); if (doc!=null){ try { int line=doc.getLineOfOffset( offset ); int col=offset-doc.getLineOffset( line ); OutlineResult or=getLastOutlineResult(); if (or!=null){ for (OutlineDef od:or.getOutlineDefs()){ Location l=od.getLocation(); if (l.contains( line, col )){ odL=l; for (OutlineDef odc:od.getChildren()){ Location lc=odc.getLocation(); if (lc.contains( line, col )){ odL=lc; break; } } break; } } } } catch (BadLocationException ble){ HaskellUIPlugin.log( ble ); } } return odL; } // Interface methods for IScionEventListener // /** // * Process a scion-server status change event // * // * @param ev // * The {@link ScionEvent} indicating the type of server event that // * happened. // */ // public void processScionServerEvent( final ScionEvent ev ) { // switch (ev.getEventType()) { // case EXECUTABLE_CHANGED: { // //final IFile file = findFile(); // //if (file != null && ResourceUtil.isInHaskellProject( file ) ) { // //updateOutline( file ); // synchronize(); // //} // break; // } // // default: // break; // } // } }