Added docStrings in hover provider for imported modules.

This commit is contained in:
Eric Traut 2019-05-27 19:00:09 -07:00
parent 33a36f6b02
commit 36cf83a768
4 changed files with 40 additions and 18 deletions

View File

@ -13,6 +13,7 @@ import { DiagnosticTextPosition, DiagnosticTextRange } from '../common/diagnosti
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
import { ModuleNameNode, NameNode, ParseNode } from '../parser/parseNodes';
import { ParseResults } from '../parser/parser';
import { ImportMap } from './analyzerFileInfo';
import { AnalyzerNodeInfo } from './analyzerNodeInfo';
import { ImportType } from './importResult';
import { ParseTreeUtils } from './parseTreeUtils';
@ -31,8 +32,8 @@ export interface HoverResults {
}
export class HoverProvider {
static getHoverForPosition(parseResults: ParseResults,
position: DiagnosticTextPosition): HoverResults | undefined {
static getHoverForPosition(parseResults: ParseResults, position: DiagnosticTextPosition,
importMap: ImportMap): HoverResults | undefined {
let offset = convertPositionToOffset(position, parseResults.lines);
if (offset === undefined) {
@ -73,14 +74,25 @@ export class HoverProvider {
}
if (importInfo.resolvedPaths[pathOffset]) {
this._addResultsPart(results, '(module) "' + importInfo.resolvedPaths[pathOffset] + '"', true);
const resolvedPath = importInfo.resolvedPaths[pathOffset];
this._addResultsPart(results, '(module) "' + resolvedPath + '"', true);
if (importInfo.importType === ImportType.ThirdParty && !importInfo.isStubFile) {
this._addResultsPart(results,
'No type stub found for this module. Imported symbol types are unknown.');
}
this._addDocumentationPart(results, node);
// If the module has been resolved and already analyzed,
// we can add the docString for it as well.
if (importMap[resolvedPath]) {
const moduleNode = importMap[resolvedPath].parseTree;
if (moduleNode) {
const moduleType = AnalyzerNodeInfo.getExpressionType(moduleNode) as ModuleType;
if (moduleType) {
this._addDocumentationPartForType(results, moduleType);
}
}
}
return results;
}
@ -204,7 +216,12 @@ export class HoverProvider {
private static _addDocumentationPart(results: HoverResults, node: ParseNode) {
const type = this._getTypeFromNode(node);
if (type) {
this._addDocumentationPartForType(results, type);
}
}
private static _addDocumentationPartForType(results: HoverResults, type: Type) {
if (type instanceof ModuleType) {
const docString = type.getDocString();
if (docString) {

View File

@ -618,12 +618,13 @@ export class Program {
getHoverForPosition(filePath: string, position: DiagnosticTextPosition):
HoverResults | undefined {
const sourceFile = this.getSourceFile(filePath);
if (!sourceFile) {
const sourceFileInfo = this._sourceFileMap[filePath];
if (!sourceFileInfo) {
return undefined;
}
return sourceFile.getHoverForPosition(position);
return sourceFileInfo.sourceFile.getHoverForPosition(position,
this._buildImportMap(sourceFileInfo));
}
getSignatureHelpForPosition(filePath: string, position: DiagnosticTextPosition,

View File

@ -24,8 +24,9 @@ import { PythonVersion } from '../common/pythonVersion';
import { TextRange } from '../common/textRange';
import { AwaitExpressionNode, ClassNode, ErrorExpressionNode,
ExpressionNode, FunctionNode, GlobalNode, IfNode, LambdaNode, ModuleNameNode,
ModuleNode, NonlocalNode, RaiseNode, StatementListNode, StringNode, SuiteNode,
TryNode, WhileNode, YieldExpressionNode,
ModuleNode, NonlocalNode, RaiseNode, StatementListNode, StatementNode, StringNode,
SuiteNode, TryNode, WhileNode,
YieldExpressionNode,
YieldFromExpressionNode } from '../parser/parseNodes';
import { StringTokenFlags } from '../parser/tokenizerTypes';
import { ScopeUtils } from '../scopeUtils';
@ -154,7 +155,8 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
}
let classType = new ClassType(node.name.nameToken.value, classFlags,
AnalyzerNodeInfo.getTypeSourceId(node), this._getDocString(node.suite));
AnalyzerNodeInfo.getTypeSourceId(node),
this._getDocString(node.suite.statements));
// Don't walk the arguments for stub files because of forward
// declarations.
@ -223,7 +225,8 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
functionFlags &= ~FunctionTypeFlags.InstanceMethod;
}
let functionType = new FunctionType(functionFlags, this._getDocString(node.suite));
let functionType = new FunctionType(functionFlags,
this._getDocString(node.suite.statements));
this.walkMultiple(node.decorators);
node.parameters.forEach(param => {
@ -459,19 +462,19 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
symbol.setInferredTypeForSource(type, typeSourceId);
}
private _getDocString(suiteNode: SuiteNode): string | undefined {
protected _getDocString(statemetns: StatementNode[]): string | undefined {
// See if the first statement in the suite is a triple-quote string.
if (suiteNode.statements.length === 0) {
if (statemetns.length === 0) {
return undefined;
}
if (!(suiteNode.statements[0] instanceof StatementListNode)) {
if (!(statemetns[0] instanceof StatementListNode)) {
return undefined;
}
// If the first statement in the suite isn't a StringNode,
// assume there is no docString.
const statementList = suiteNode.statements[0] as StatementListNode;
const statementList = statemetns[0] as StatementListNode;
if (statementList.statements.length === 0 || !(statementList.statements[0] instanceof StringNode)) {
return undefined;
}
@ -566,7 +569,8 @@ export class ModuleScopeAnalyzer extends SemanticAnalyzer {
this.walkChildren(this._scopedNode);
// Associate the module's scope with the module type.
let moduleType = new ModuleType(this._currentScope.getSymbolTable());
const moduleType = new ModuleType(this._currentScope.getSymbolTable(),
this._getDocString((this._scopedNode as ModuleNode).statements));
AnalyzerNodeInfo.setExpressionType(this._scopedNode, moduleType);
}

View File

@ -468,14 +468,14 @@ export class SourceFile {
this._analysisJob.parseResults, position);
}
getHoverForPosition(position: DiagnosticTextPosition): HoverResults | undefined {
getHoverForPosition(position: DiagnosticTextPosition, importMap: ImportMap): HoverResults | undefined {
// If we have no completed analysis job, there's nothing to do.
if (!this._analysisJob.parseResults) {
return undefined;
}
return HoverProvider.getHoverForPosition(
this._analysisJob.parseResults, position);
this._analysisJob.parseResults, position, importMap);
}
getSignatureHelpForPosition(position: DiagnosticTextPosition): SignatureHelpResults | undefined {