📄 finddefinition.py
字号:
from __future__ import generators
from bike.query.common import Match, MatchFinder, \
getScopeForLine, indexToCoordinates, \
translateSourceCoordsIntoASTNode, scanScopeForMatches, \
isAMethod, convertNodeToMatchObject, walkLinesContainingStrings
from bike.parsing.parserutils import generateLogicalLines,\
generateLogicalLinesAndLineNumbers, \
splitLogicalLines, makeLineParseable
import compiler
from compiler.ast import Getattr, Name, AssName, AssAttr
from bike.parsing.fastparserast import getRoot, Package, Class, \
Module, Function, Instance
import re
from bike.query.getTypeOf import getTypeOfExpr, UnfoundType, \
isWordInLine, resolveImportedModuleOrPackage
from bike.parsing import visitor
from bike.parsing.visitor import walkAndGenerate
from bike.parsing.parserutils import makeLineParseable,splitLogicalLines
from bike.parsing.newstuff import getSourceNodesContainingRegex
from bike.parsing.load import getSourceNode
from bike import log
class CantFindDefinitionException:
pass
def findAllPossibleDefinitionsByCoords(filepath,lineno,col):
#try:
node = translateSourceCoordsIntoASTNode(filepath,lineno,col)
#except:
# import traceback
# traceback.print_exc()
if node is None:
raise "selected node type not supported"
scope = getScopeForLine(getSourceNode(filepath),lineno)
match = findDefinitionFromASTNode(scope,node)
if match is not None:
yield match
if isinstance(node,Getattr) and (match is None or match.confidence != 100):
root = getRoot()
name = node.attrname
for match in scanPythonPathForMatchingMethodNames(name,filepath):
yield match
print >>log.progress,"done"
def findDefinitionFromASTNode(scope,node):
assert node is not None
if isinstance(node,Name) or isinstance(node,AssName):
while 1:
# try scope children
childscope = scope.getChild(node.name)
if childscope is not None:
return convertNodeToMatchObject(childscope,100)
if isinstance(scope,Package):
scope = scope.getChild("__init__")
# try arguments and assignments
match = scanScopeAST(scope,node.name,
AssignmentAndFnArgsSearcher(node.name))
if match is not None:
return match
# try imports
match = searchImportedModulesForDefinition(scope,node)
if match is not None:
return match
if not isinstance(scope,Module):
# try parent scope
scope = scope.getParent()
else:
break
assert isinstance(scope,Module)
elif isinstance(node,Getattr) or isinstance(node,AssAttr):
exprtype = getTypeOfExpr(scope,node.expr)
if not (exprtype is None or isinstance(exprtype,UnfoundType)):
if isinstance(exprtype,Instance):
exprtype = exprtype.getType()
match = findDefinitionOfAttributeFromASTNode(exprtype,
node.attrname)
else:
match = findDefinitionFromASTNode(exprtype,
Name(node.attrname))
if match is not None:
return match
elif isinstance(node,compiler.ast.Function) or \
isinstance(node,compiler.ast.Class):
if isAMethod(scope,node):
match = findDefinitionOfAttributeFromASTNode(scope,
node.name)
else:
match = findDefinitionFromASTNode(scope,Name(node.name))
if match is not None:
return match
type = getTypeOfExpr(scope,node)
if type is not None and (not isinstance(type,UnfoundType)) and \
(not isinstance(type,Instance)):
return convertNodeToMatchObject(type,100)
else:
return None
def findDefinitionOfAttributeFromASTNode(type,name):
assert isinstance(type,Class)
attrfinder = AttrbuteDefnFinder([type],name)
# first scan the method names:
for child in type.getChildNodes():
if child.name == name:
return convertNodeToMatchObject(child,100)
# then scan the method source for attribues
for child in type.getChildNodes():
if isinstance(child,Function):
try:
return scanScopeForMatches(child.module.getSourceNode(),
child, attrfinder,
name).next()
except StopIteration:
continue
class AttrbuteDefnFinder(MatchFinder):
def __init__(self,targetClasses,targetAttribute):
self.targetClasses = targetClasses
self.targetAttributeName = targetAttribute
def visitAssAttr(self, node):
for c in node.getChildNodes():
self.visit(c)
if node.attrname == self.targetAttributeName:
exprtype = getTypeOfExpr(self.scope,node.expr)
if isinstance(exprtype,Instance) and \
exprtype.getType() in self.targetClasses:
self.appendMatch(self.targetAttributeName)
#else:
# self.appendMatch(self.targetAttributeName,50)
self.popWordsUpTo(node.attrname)
def searchImportedModulesForDefinition(scope,node):
lines = scope.module.getSourceNode().getLines()
for lineno in scope.getImportLineNumbers():
logicalline = getLogicalLine(lines,lineno)
logicalline = makeLineParseable(logicalline)
ast = compiler.parse(logicalline)
class ImportVisitor:
def __init__(self,node):
self.target = node
self.match = None
assert isinstance(self.target,Name), \
"Getattr not supported"
def visitFrom(self, node):
module = resolveImportedModuleOrPackage(scope,node.modname)
if module is None: # couldn't find module
return
if node.names[0][0] == '*': # e.g. from foo import *
match = findDefinitionFromASTNode(module,self.target)
if match is not None:
self.match = match
return
for name, alias in node.names:
if alias is None and name == self.target.name:
match = findDefinitionFromASTNode(module,self.target)
if match is not None:
self.match = match
return
match = visitor.walk(ast, ImportVisitor(node)).match
if match:
return match
# loop
def getLogicalLine(lines,lineno):
return generateLogicalLines(lines[lineno-1:]).next()
class AssignmentAndFnArgsSearcher(MatchFinder):
def __init__(self,name):
self.targetname = name
self.match = None
def visitAssName(self, node):
if node.name == self.targetname:
idx = self.getNextIndexOfWord(self.targetname)
self.match = idx
return
def visitFunction(self, node):
self.popWordsUpTo(node.name)
for arg, default in self.zipArgs(node.argnames, node.defaults):
if arg == self.targetname:
idx = self.getNextIndexOfWord(self.targetname)
self.match = idx
return
self.popWordsUpTo(arg)
if default is not None:
self.visit(default)
self.visit(node.code)
def getMatch(self):
return self.match
# scans for lines containing keyword, and then runs the visitor over
# the parsed AST for that line
def scanScopeAST(scope,keyword,matchfinder):
lines = scope.generateLinesNotIncludingThoseBelongingToChildScopes()
match = None
for line,linenum in generateLogicalLinesAndLineNumbers(lines):
if isWordInLine(keyword, line):
doctoredline = makeLineParseable(line)
ast = compiler.parse(doctoredline)
matchfinder.reset(line)
match = visitor.walk(ast,matchfinder).getMatch()
if match is not None:
column,yoffset = indexToCoordinates(line,match)
m = createMatch(scope,linenum + yoffset,column)
return m
return None
def createMatch(scope,lineno,x):
m = Match()
m.sourcenode = scope.module.getSourceNode()
m.filename = m.sourcenode.filename
m.lineno = lineno
m.colno = x
m.confidence = 100
return m
# scan for methods globally (from perspective of 'perspectiveFilename')
def scanPythonPathForMatchingMethodNames(name, contextFilename):
class MethodFinder:
def __init__(self,srcnode):
self.matches = []
self.srcnode = srcnode
def visitFunction(self,node):
node = getScopeForLine(self.srcnode, self.lineno)
if isinstance(node.getParent(),Class):
if node.name == name:
self.matches.append(convertNodeToMatchObject(node,50))
for srcnode in getSourceNodesContainingRegex(name,contextFilename):
m = MethodFinder(srcnode)
walkLinesContainingStrings(srcnode.fastparseroot,m,[name])
for match in m.matches:
yield match
def getIndexOfWord(line,targetword):
words = re.split("(\w+)", line)
idx = 0
for word in words:
if word == targetword:
break
idx += len(word)
return idx
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -