pyedit.java

来自「Python Development Environment (Python I」· Java 代码 · 共 1,112 行 · 第 1/3 页

JAVA
1,112
字号
package org.python.pydev.editor;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.ListResourceBundle;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorActionBarContributor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.editors.text.TextFileDocumentProvider;
import org.eclipse.ui.internal.util.SWTResourceUtil;
import org.eclipse.ui.part.EditorActionBarContributor;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.ContentAssistAction;
import org.eclipse.ui.texteditor.DefaultRangeIndicator;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.IEditorStatusLine;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.MarkerUtilities;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.python.copiedfromeclipsesrc.PydevFileEditorInput;
import org.python.pydev.core.ExtensionHelper;
import org.python.pydev.core.IPyEdit;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.REF;
import org.python.pydev.core.Tuple;
import org.python.pydev.core.docutils.PyPartitionScanner;
import org.python.pydev.core.docutils.PySelection;
import org.python.pydev.core.parser.ISimpleNode;
import org.python.pydev.editor.actions.OfflineAction;
import org.python.pydev.editor.actions.OfflineActionTarget;
import org.python.pydev.editor.actions.PyAction;
import org.python.pydev.editor.actions.PyOpenAction;
import org.python.pydev.editor.autoedit.DefaultIndentPrefs;
import org.python.pydev.editor.autoedit.IIndentPrefs;
import org.python.pydev.editor.autoedit.PyAutoIndentStrategy;
import org.python.pydev.editor.codecompletion.shell.AbstractShell;
import org.python.pydev.editor.codefolding.CodeFoldingSetter;
import org.python.pydev.editor.codefolding.PyEditProjection;
import org.python.pydev.editor.model.IModelListener;
import org.python.pydev.editor.scripting.PyEditScripting;
import org.python.pydev.outline.PyOutlinePage;
import org.python.pydev.parser.PyParser;
import org.python.pydev.parser.PyParserManager;
import org.python.pydev.parser.jython.ParseException;
import org.python.pydev.parser.jython.SimpleNode;
import org.python.pydev.parser.jython.Token;
import org.python.pydev.parser.jython.TokenMgrError;
import org.python.pydev.parser.visitors.NodeUtils;
import org.python.pydev.parser.visitors.scope.ASTEntry;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.PydevPrefs;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.plugin.nature.SystemPythonNature;
import org.python.pydev.ui.ColorCache;
import org.python.pydev.ui.NotConfiguredInterpreterException;

/**
 * The TextWidget.
 * 
 * <p>
 * Ties together all the main classes in this plugin.
 * <li>The {@link org.python.pydev.editor.PyEditConfiguration PyEditConfiguration}does preliminary partitioning.
 * <li>The {@link org.python.pydev.parser.PyParser PyParser}does a lazy validating python parse.
 * <li>The {@link org.python.pydev.outline.PyOutlinePage PyOutlinePage}shows the outline
 * 
 * <p>
 * Listens to the parser's events, and displays error markers from the parser.
 * 
 * <p>
 * General notes:
 * <p>
 * TextWidget creates SourceViewer, an SWT control
 * 
 * @see <a href="http://dev.eclipse.org/newslists/news.eclipse.tools/msg61594.html">This eclipse article was an inspiration </a>
 *  
 */
public class PyEdit extends PyEditProjection implements IPyEdit {

    static{
        ParseException.verboseExceptions = true;
    }
    
    public static final String PY_EDIT_CONTEXT = "#PyEditContext";

    static public final String EDITOR_ID = "org.python.pydev.editor.PythonEditor";

    static public final String ACTION_OPEN = "OpenEditor";

    /** color cache */
    private ColorCache colorCache;

    // Listener waits for tab/spaces preferences that affect sourceViewer
    private Preferences.IPropertyChangeListener prefListener;

    /** need it to support GUESS_TAB_SUBSTITUTION preference */
    private PyAutoIndentStrategy indentStrategy;

    /** need to hold onto it to support indentPrefix change through preferences */
    PyEditConfiguration editConfiguration;

    /**
     * AST that created python model
     */
    private volatile SimpleNode ast;
    
    /**
     * The last parsing error description we got.
     */
    private volatile ErrorDescription errorDescription;

    /** Hyperlinking listener */
    Hyperlink fMouseListener;

    /** listeners that get notified of model changes */
    List<IModelListener> modelListeners;

    // ---------------------------- listeners stuff
    /**
     * Those are the ones that register with the PYDEV_PYEDIT_LISTENER extension point
     */
    private static List<IPyEditListener> editListeners;
    
    /**
     * Those are the ones that register at runtime (not throught extensions points).
     */
    private volatile List<IPyEditListener> registeredEditListeners = new ArrayList<IPyEditListener>();

    /**
     * This is the scripting engine that is binded to this interpreter.
     */
	private PyEditScripting pyEditScripting;

	/**
	 * Lock for initialization sync
	 */
	private Object lock = new Object();
    
    public void addPyeditListener(IPyEditListener listener){
    	synchronized (registeredEditListeners) {
    		registeredEditListeners.add(listener);
		}
    }
    
    public void removePyeditListener(IPyEditListener listener){
    	synchronized (registeredEditListeners) {
    		registeredEditListeners.remove(listener);
    	}
    }

    

    @Override
    protected void handleCursorPositionChanged() {
        super.handleCursorPositionChanged();
        if(!initFinished){
        	return;
        }
        for(IPyEditListener listener : getAllListeners()){
            try {
                if(listener instanceof IPyEditListener2){
                    ((IPyEditListener2)listener).handleCursorPositionChanged(this);
                }
            } catch (Throwable e) {
                //must not fail
                PydevPlugin.log(e);
            }
        }
    }

    public List<IPyEditListener> getAllListeners() {
    	while (initFinished == false){
    		synchronized(getLock()){
    			try {
    			    if(initFinished == false){
    			        getLock().wait();
    			    }
				} catch (Exception e) {
					//ignore
					e.printStackTrace();
				}
    		}
    	}
        ArrayList<IPyEditListener> listeners = new ArrayList<IPyEditListener>();
        if(editListeners != null){
            listeners.addAll(editListeners); //no need to sync because editListeners is read-only
        }
    	synchronized (registeredEditListeners) {
    		listeners.addAll(registeredEditListeners);
    	}
        return listeners;
    }

    private Object getLock() {
		return lock;
	}

	/**
     * This map may be used by clients to store info regarding this editor.
     * 
     * Clients should be careful so that this key is unique and does not conflict with other
     * plugins. 
     * 
     * This is not enforced.
     * 
     * The suggestion is that the cache key is always preceded by the class name that will use it.
     */
    public Map<String,Object> cache = new HashMap<String, Object>();
    
    public Map<String,Object> getCache(){
        return cache;
    }


    /**
     * Indicates whether the init was already finished
     */
	protected boolean initFinished = false;

	private PyEditNotifier notifier;

	private boolean disposed = false;
	public boolean isDisposed() {
		return disposed;
	}
    
    // ---------------------------- end listeners stuff
    
    @SuppressWarnings("unchecked")
	public PyEdit() {
        super();
        try{
	        //initialize the 'save' listeners of PyEdit
	        if (editListeners == null){
	        	editListeners = ExtensionHelper.getParticipants(ExtensionHelper.PYDEV_PYEDIT_LISTENER);
	        }
	        
	        modelListeners = new ArrayList<IModelListener>();
	        colorCache = new ColorCache(PydevPlugin.getChainedPrefStore());
	        
	        editConfiguration = new PyEditConfiguration(colorCache, this, PydevPlugin.getDefault().getPreferenceStore());
	        setSourceViewerConfiguration(editConfiguration);
	        indentStrategy = editConfiguration.getPyAutoIndentStrategy();
	        setRangeIndicator(new DefaultRangeIndicator()); // enables standard
	        // vertical ruler
	
	        //Added to set the code folding.
	        CodeFoldingSetter codeFoldingSetter = new CodeFoldingSetter(this);
	        this.addModelListener(codeFoldingSetter);
	        this.addPropertyListener(codeFoldingSetter);
        }catch (Throwable e) {
			PydevPlugin.log(e);
		}
    }

    
    /**
     * Sets the forceTabs preference for auto-indentation.
     * 
     * <p>
     * This is the preference that overrides "use spaces" preference when file contains tabs (like mine do).
     * <p>
     * If the first indented line starts with a tab, then tabs override spaces.
     */
    private void resetForceTabs() {
        IDocument doc = getDocumentProvider().getDocument(getEditorInput());
        if (doc == null){
            return;
        }
        
        if (!PydevPrefs.getPreferences().getBoolean(PydevPrefs.GUESS_TAB_SUBSTITUTION)) {
            getIndentPrefs().setForceTabs(false);
            return;
        }

        int lines = doc.getNumberOfLines();
        boolean forceTabs = false;
        int i = 0;
        // look for the first line that starts with ' ', or '\t'
        while (i < lines) {
            try {
                IRegion r = doc.getLineInformation(i);
                String text = doc.get(r.getOffset(), r.getLength());
                if (text != null)
                    if (text.startsWith("\t")) {
                        forceTabs = true;
                        break;
                    } else if (text.startsWith("  ")) {
                        forceTabs = false;
                        break;
                    }
            } catch (BadLocationException e) {
                PydevPlugin.log(IStatus.ERROR, "Unexpected error forcing tabs", e);
                break;
            }
            i++;
        }
        getIndentPrefs().setForceTabs(forceTabs);
        editConfiguration.resetIndentPrefixes();
        // display a message in the status line
        if (forceTabs) {
            IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter(IEditorStatusLine.class);
            if (statusLine != null)
                statusLine.setMessage(false, "Pydev: forcing tabs", null);
        }
    }

    /**
     * @return the indentation preferences
     */
    public IIndentPrefs getIndentPrefs() {
        return indentStrategy.getIndentPrefs();
    }

    /**
     * Initializes everyone that needs document access
     *  
     */
    public void init(final IEditorSite site, final IEditorInput input) throws PartInitException {
    	try{
	    	notifier = new PyEditNotifier(this);
	        super.init(site, input);
	
	        final IDocument document = getDocument(input);
	        
	        // check the document partitioner (sanity check / fix)
	        PyPartitionScanner.checkPartitionScanner(document);
	
	        // Also adds Python nature to the project.
	    	// The reason this is done here is because I want to assign python
	    	// nature automatically to any project that has active python files.
	        final IPythonNature nature = PythonNature.addNature(input);
	        
	        //we also want to initialize our shells...
	        //we use 2: one for refactoring and one for code completion.
	        Thread thread2 = new Thread() {
	            public void run() {
	                try {
	                    try {
	                        AbstractShell.getServerShell(nature, AbstractShell.COMPLETION_SHELL);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?