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 + -
显示快捷键?