📄 pyselection.java
字号:
/*
* @author: ptoofani
* @author Fabio Zadrozny
* Created: June 2004
* License: Common Public License v1.0
*/
package org.python.pydev.core.docutils;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.texteditor.ITextEditor;
import org.python.pydev.core.IPythonPartitions;
import org.python.pydev.core.Tuple;
import org.python.pydev.core.Tuple3;
import org.python.pydev.core.ICodeCompletionASTManager.ImportInfo;
import org.python.pydev.core.log.Log;
import org.python.pydev.core.uiutils.RunInUiThread;
/**
* Redone the whole class, so that the interface is better defined and no
* duplication of information is given.
*
* Now, it is just used as 'shortcuts' to document and selection settings.
*
* @author Fabio Zadrozny
* @author Parhaum Toofanian
*/
public class PySelection {
private IDocument doc;
private ITextSelection textSelection;
public static final String[] DEDENT_TOKENS = new String[]{
"return",
"break",
"continue",
"pass",
"raise",
"yield"
};
public static final String[] CLASS_AND_FUNC_TOKENS = new String[]{
"def" ,
"class" ,
};
public static final String[] INDENT_TOKENS = new String[]{
"if" ,
"for" ,
"except" ,
"def" ,
"class" ,
"else" ,
"elif" ,
"while" ,
"try" ,
"with" ,
"finally"
};
/**
* Alternate constructor for PySelection. Takes in a text editor from Eclipse.
*
* @param textEditor The text editor operating in Eclipse
*/
public PySelection(ITextEditor textEditor) {
this(textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput()),
(ITextSelection) textEditor.getSelectionProvider().getSelection());
}
/**
* This is a 'factory' method so that we can create a PySelection from a non-ui thread (it syncs the execution
* if needed).
*
* @param textEditor the text editor that will be used to create the selection
* @return the pyselection created
*/
public static PySelection createFromNonUiThread(final ITextEditor textEditor) {
final Tuple<PySelection, Object> t = new Tuple<PySelection, Object>(null, null);
Runnable r = new Runnable(){
public void run() {
try {
t.o1 = new PySelection(textEditor);
} catch (NullPointerException e) {
// this can happen if the selection was still not set up
}
}
};
RunInUiThread.sync(r);
return t.o1;
}
/**
* @param document the document we are using to make the selection
* @param selection that's the actual selection. It might have an offset and a number of selected chars
*/
public PySelection(IDocument doc, ITextSelection selection) {
this.doc = doc;
this.textSelection = selection;
}
/**
* Creates a selection from a document
* @param doc the document to be used
* @param line the line (starts at 0)
* @param col the col (starts at 0)
*/
public PySelection(IDocument doc, int line, int col) {
this(doc, line, col, 0);
}
public PySelection(IDocument doc, int line, int col, int len) {
this.doc = doc;
this.textSelection = new TextSelection(doc, getAbsoluteCursorOffset(line, col), len);
}
public static int getAbsoluteCursorOffset(IDocument doc, int line, int col) {
try {
IRegion offsetR = doc.getLineInformation(line);
return offsetR.getOffset() + col;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public int getAbsoluteCursorOffset(int line, int col) {
return getAbsoluteCursorOffset(doc, line, col);
}
/**
* @param document the document we are using to make the selection
* @param offset the offset where the selection will happen (0 characters will be selected)
*/
public PySelection(IDocument doc, int offset) {
this.doc = doc;
this.textSelection = new TextSelection(doc, offset, 0);
}
/**
* Changes the selection
* @param absoluteStart this is the offset of the start of the selection
* @param absoluteEnd this is the offset of the end of the selection
*/
public void setSelection(int absoluteStart, int absoluteEnd) {
this.textSelection = new TextSelection(doc, absoluteStart, absoluteEnd-absoluteStart);
}
/**
* Creates a selection for the document, so that no characters are selected and the offset is position 0
* @param doc the document where we are doing the selection
*/
public PySelection(IDocument doc) {
this(doc, 0);
}
/**
* In event of partial selection, used to select the full lines involved.
*/
public void selectCompleteLine() {
IRegion endLine = getEndLine();
IRegion startLine = getStartLine();
this.textSelection = new TextSelection(doc, startLine.getOffset(), endLine.getOffset() + endLine.getLength() - startLine.getOffset());
}
/**
* @return the line where a global import would be able to happen.
*
* The 'usual' structure that we take into consideration for a py file here is:
*
* #coding ...
*
* '''
* multiline comment...
* '''
*
* imports #that's what we want to find out
*
* code
*
*/
public int getLineAvailableForImport() {
StringBuffer multiLineBuf = new StringBuffer();
int[] firstGlobalLiteral = getFirstGlobalLiteral(multiLineBuf, 0);
if (multiLineBuf.length() > 0 && firstGlobalLiteral[0] >= 0 && firstGlobalLiteral[1] >= 0) {
//ok, multiline found
int startingMultilineComment = getLineOfOffset(firstGlobalLiteral[0]);
if(startingMultilineComment < 4){
//let's see if the multiline comment found is in the beginning of the document
int lineOfOffset = getLineOfOffset(firstGlobalLiteral[1]);
return getFirstNonCommentLineOrAfterCurrentImports(lineOfOffset + 1);
}else{
return getFirstNonCommentLineOrAfterCurrentImports();
}
} else {
//ok, no multiline comment, let's get the first line that is not a comment
return getFirstNonCommentLineOrAfterCurrentImports();
}
}
private int getFirstNonCommentLineOrAfterCurrentImports() {
return getFirstNonCommentLineOrAfterCurrentImports(0);
}
/**
* @return the first line found that is not a comment.
*/
private int getFirstNonCommentLineOrAfterCurrentImports(int startingAtLine) {
int firstNonCommentLine = -1;
int afterFirstImports = -1;
IDocument document = getDoc();
int lines = document.getNumberOfLines();
for (int line = startingAtLine; line < lines; line++) {
String str = getLine(line);
if (str.startsWith("#")) {
continue;
}else{
int i;
if((i = str.indexOf('#')) != -1){
str = str.substring(0, i);
}
if(firstNonCommentLine == -1){
firstNonCommentLine = line;
}
ImportInfo importInfo = ImportsSelection.getImportsTipperStr(str, false);
if(importInfo != null && importInfo.importsTipperStr != null && importInfo.importsTipperStr.trim().length() > 0){
if((i = str.indexOf('(')) != -1){
StringBuffer buf = new StringBuffer();
//start of a multiline import
int lineOffset = -1;
try {
lineOffset = document.getLineOffset(line);
} catch (BadLocationException e1) {
throw new RuntimeException(e1);
}
int j = ParsingUtils.eatPar(document, lineOffset+i, buf);
try {
line = document.getLineOfOffset(j);
} catch (BadLocationException e) {
Log.log(e);
}
}else if(str.endsWith("\\")){
while(str.endsWith("\\") && line < lines){
line++;
str = getLine(line);
}
}
afterFirstImports = line+1;
}else if(str.trim().length() > 0){
//found some non-empty, non-import, non-comment line (break it here)
break;
}
}
}
return afterFirstImports > firstNonCommentLine?afterFirstImports:firstNonCommentLine;
}
/**
* @param initialOffset this is the offset we should use to analyze it
* @param buf (out) this is the comment itself
* @return a tuple with the offset of the start and end of the first multiline comment found
*/
public int[] getFirstGlobalLiteral(StringBuffer buf, int initialOffset){
try {
IDocument d = getDoc();
String strDoc = d.get(initialOffset, d.getLength() - initialOffset);
if(initialOffset > strDoc.length()-1){
return new int[]{-1, -1};
}
char current = strDoc.charAt(initialOffset);
//for checking if it is global, it must be in the beggining of a line (must be right after a \r or \n).
while (current != '\'' && current != '"' && initialOffset < strDoc.length()-1) {
//if it is inside a parenthesis, we will not take it into consideration.
if(current == '('){
initialOffset = ParsingUtils.eatPar(strDoc, initialOffset, buf);
}
initialOffset += 1;
if(initialOffset < strDoc.length()-1){
current = strDoc.charAt(initialOffset);
}
}
//either, we are at the end of the document or we found a literal
if(initialOffset < strDoc.length()-1){
if(initialOffset == 0){ //first char of the document... this is ok
int i = ParsingUtils.eatLiterals(strDoc, buf, initialOffset);
return new int[]{initialOffset, i};
}
char lastChar = strDoc.charAt(initialOffset-1);
//it is only global if after \r or \n
if(lastChar == '\r' || lastChar == '\n'){
int i = ParsingUtils.eatLiterals(strDoc, buf, initialOffset);
return new int[]{initialOffset, i};
}
//ok, still not found, let's keep going
return getFirstGlobalLiteral(buf, initialOffset+1);
}else{
return new int[]{-1, -1};
}
} catch (BadLocationException e) {
throw new RuntimeException(e);
}
}
protected static void beep(Exception e) {
Log.log(e);
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell().getDisplay().beep();
}
public static String getLineWithoutCommentsOrLiterals(String l) {
StringBuffer buf = new StringBuffer(l);
ParsingUtils.removeCommentsWhitespacesAndLiterals(buf, false);
return buf.toString();
}
public String getLineWithoutCommentsOrLiterals() {
return getLineWithoutCommentsOrLiterals(getLine());
}
public static String getLineWithoutLiterals(String line) {
StringBuffer buf = new StringBuffer(line);
ParsingUtils.removeLiterals(buf);
return buf.toString();
}
/**
* @return the current column that is selected from the cursor.
*/
public int getCursorColumn() {
try {
int absoluteOffset = getAbsoluteCursorOffset();
IRegion region = doc.getLineInformationOfOffset(absoluteOffset);
return absoluteOffset - region.getOffset();
} catch (BadLocationException e) {
throw new RuntimeException(e);
}
}
/**
* Gets current line from document.
*
* @return String line in String form
*/
public String getLine() {
return getLine(getDoc(), getCursorLine());
}
/**
* Gets line from document.
*
* @param i Line number
* @return String line in String form
*/
public String getLine(int i) {
return getLine(getDoc(), i);
}
/**
* Gets line from document.
*
* @param i Line number
* @return String line in String form
*/
public static String getLine(IDocument doc, int i) {
try {
return doc.get(doc.getLineInformation(i).getOffset(), doc.getLineInformation(i).getLength());
} catch (Exception e) {
return "";
}
}
public int getLineOfOffset() {
return getLineOfOffset(this.getAbsoluteCursorOffset());
}
public int getLineOfOffset(int offset) {
return getLineOfOffset(getDoc(), offset);
}
/**
* @param offset the offset we want to get the line
* @return the line of the passed offset
*/
public static int getLineOfOffset(IDocument doc, int offset) {
try {
return doc.getLineOfOffset(offset);
} catch (BadLocationException e) {
return 0;
}
}
/**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -