diff --git a/packages/pyright-internal/src/analyzer/binder.ts b/packages/pyright-internal/src/analyzer/binder.ts index 270f4ff04..7ed7bdf6f 100644 --- a/packages/pyright-internal/src/analyzer/binder.ts +++ b/packages/pyright-internal/src/analyzer/binder.ts @@ -22,6 +22,7 @@ import { DiagnosticLevel } from '../common/configOptions'; import { assert, assertNever, fail } from '../common/debug'; import { CreateTypeStubFileAction, Diagnostic } from '../common/diagnostic'; import { DiagnosticRule } from '../common/diagnosticRules'; +import { DocStringService } from '../common/docStringService'; import { stripFileExtension } from '../common/pathUtils'; import { convertTextRangeToRange } from '../common/positionUtils'; import { TextRange, getEmptyRange } from '../common/textRange'; @@ -250,7 +251,11 @@ export class Binder extends ParseTreeWalker { // the current function. private _codeFlowComplexity = 0; - constructor(fileInfo: AnalyzerFileInfo, private _moduleSymbolOnly = false) { + constructor( + fileInfo: AnalyzerFileInfo, + private _docStringService: DocStringService, + private _moduleSymbolOnly = false + ) { super(); this._fileInfo = fileInfo; diff --git a/packages/pyright-internal/src/analyzer/docStringUtils.ts b/packages/pyright-internal/src/analyzer/docStringUtils.ts index a7cdb5f8f..e1f33c36d 100644 --- a/packages/pyright-internal/src/analyzer/docStringUtils.ts +++ b/packages/pyright-internal/src/analyzer/docStringUtils.ts @@ -9,11 +9,6 @@ * (https://www.python.org/dev/peps/pep-0257/). */ -// Cleans the a docstring as inspect.cleandoc does. -export function cleanDocString(rawString: string): string { - return cleanAndSplitDocString(rawString).join('\n'); -} - export function cleanAndSplitDocString(rawString: string): string[] { // Remove carriage returns and replace tabs. const unescaped = rawString.replace(/\r/g, '').replace(/\t/g, ' '); diff --git a/packages/pyright-internal/src/analyzer/sourceFile.ts b/packages/pyright-internal/src/analyzer/sourceFile.ts index fbe221aa4..b5d31418f 100644 --- a/packages/pyright-internal/src/analyzer/sourceFile.ts +++ b/packages/pyright-internal/src/analyzer/sourceFile.ts @@ -17,7 +17,6 @@ import { assert } from '../common/debug'; import { Diagnostic, DiagnosticCategory, TaskListToken, convertLevelToCategory } from '../common/diagnostic'; import { DiagnosticRule } from '../common/diagnosticRules'; import { DiagnosticSink, TextRangeDiagnosticSink } from '../common/diagnosticSink'; -import { ServiceProvider } from '../common/extensibility'; import { FileSystem } from '../common/fileSystem'; import { LogTracker, getPathForLogging } from '../common/logTracker'; import { stripFileExtension } from '../common/pathUtils'; @@ -47,6 +46,8 @@ import { SourceMapper } from './sourceMapper'; import { SymbolTable } from './symbol'; import { TestWalker } from './testWalker'; import { TypeEvaluator } from './typeEvaluatorTypes'; +import '../common/serviceProviderExtensions'; +import { ServiceProvider } from '../common/serviceProvider'; // Limit the number of import cycles tracked per source file. const _maxImportCyclesPerFile = 4; @@ -833,7 +834,11 @@ export class SourceFile { ); AnalyzerNodeInfo.setFileInfo(this._writableData.parserOutput!.parseTree, fileInfo); - const binder = new Binder(fileInfo, configOptions.indexGenerationMode); + const binder = new Binder( + fileInfo, + this.serviceProvider.docStringService(), + configOptions.indexGenerationMode + ); this._writableData.isBindingInProgress = true; binder.bindModule(this._writableData.parserOutput!.parseTree); diff --git a/packages/pyright-internal/src/analyzer/typeDocStringUtils.ts b/packages/pyright-internal/src/analyzer/typeDocStringUtils.ts index e6b1402a5..eeb6c1e2c 100644 --- a/packages/pyright-internal/src/analyzer/typeDocStringUtils.ts +++ b/packages/pyright-internal/src/analyzer/typeDocStringUtils.ts @@ -182,6 +182,13 @@ export function getVariableInStubFileDocStrings(decl: VariableDeclaration, sourc return docStrings; } +export function isBuiltInModule(uri: Uri | undefined) { + if (uri) { + return uri.getPath().includes('typeshed-fallback/stdlib'); + } + return false; +} + export function getModuleDocStringFromModuleNodes(modules: ModuleNode[]): string | undefined { for (const module of modules) { if (module.statements) { diff --git a/packages/pyright-internal/src/common/docStringService.ts b/packages/pyright-internal/src/common/docStringService.ts new file mode 100644 index 000000000..e2319a6be --- /dev/null +++ b/packages/pyright-internal/src/common/docStringService.ts @@ -0,0 +1,50 @@ +/* + * docStringService.ts + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * + * Interface for service that parses docstrings and converts them to other formats. + */ + +import { convertDocStringToMarkdown, convertDocStringToPlainText } from '../analyzer/docStringConversion'; +import { extractParameterDocumentation } from '../analyzer/docStringUtils'; + +export interface DocStringService { + convertDocStringToPlainText(docString: string): string; + convertDocStringToMarkdown(docString: string, forceLiteral?: boolean): string; + extractParameterDocumentation( + functionDocString: string, + paramName: string, + forceLiteral?: boolean + ): string | undefined; + clone(): DocStringService; +} + +export namespace DocStringService { + export function is(value: any): value is DocStringService { + return ( + !!value.convertDocStringToMarkdown && + !!value.convertDocStringToPlainText && + !!value.extractParameterDocumentation + ); + } +} + +export class PyrightDocStringService implements DocStringService { + convertDocStringToPlainText(docString: string): string { + return convertDocStringToPlainText(docString); + } + + convertDocStringToMarkdown(docString: string): string { + return convertDocStringToMarkdown(docString); + } + + extractParameterDocumentation(functionDocString: string, paramName: string): string | undefined { + return extractParameterDocumentation(functionDocString, paramName); + } + + clone() { + // No need to clone, no internal state + return this; + } +} diff --git a/packages/pyright-internal/src/common/envVarUtils.ts b/packages/pyright-internal/src/common/envVarUtils.ts index c5fdcfa53..17911db80 100644 --- a/packages/pyright-internal/src/common/envVarUtils.ts +++ b/packages/pyright-internal/src/common/envVarUtils.ts @@ -12,6 +12,7 @@ import { Workspace, WorkspaceFolder } from '../workspaceFactory'; import { Uri } from './uri/uri'; import { isRootedDiskPath, normalizeSlashes } from './pathUtils'; import { ServiceKeys } from './serviceKeys'; +import { escapeRegExp } from './stringUtils'; export function resolvePathWithEnvVariables( workspace: Workspace, @@ -65,7 +66,8 @@ export function expandPathVariables(path: string, rootPath: Uri, workspaces: Wor continue; } - const ws_regexp = RegExp(`\\$\\{workspaceFolder:${workspace.workspaceName}\\}`, 'g'); + const escapedWorkspaceName = escapeRegExp(workspace.workspaceName); + const ws_regexp = RegExp(`\\$\\{workspaceFolder:${escapedWorkspaceName}\\}`, 'g'); path = path.replace(ws_regexp, workspace.rootUri.getPath()); } if (process.env.HOME !== undefined) { diff --git a/packages/pyright-internal/src/common/extensibility.ts b/packages/pyright-internal/src/common/extensibility.ts index 253e808fd..1167b5a68 100644 --- a/packages/pyright-internal/src/common/extensibility.ts +++ b/packages/pyright-internal/src/common/extensibility.ts @@ -22,7 +22,7 @@ import { ParseFileResults, ParserOutput } from '../parser/parser'; import { ConfigOptions } from './configOptions'; import { ConsoleInterface } from './console'; import { ReadOnlyFileSystem } from './fileSystem'; -import { GroupServiceKey, ServiceKey } from './serviceProvider'; +import { ServiceProvider } from './serviceProvider'; import { Range } from './textRange'; import { Uri } from './uri/uri'; @@ -64,14 +64,6 @@ export interface SourceFileInfo { readonly shadowedBy: readonly SourceFileInfo[]; } -export interface ServiceProvider { - tryGet(key: ServiceKey): T | undefined; - tryGet(key: GroupServiceKey): readonly T[] | undefined; - - get(key: ServiceKey): T; - get(key: GroupServiceKey): readonly T[]; -} - // Readonly wrapper around a Program. Makes sure it doesn't mutate the program. export interface ProgramView { readonly id: string; diff --git a/packages/pyright-internal/src/common/languageServerInterface.ts b/packages/pyright-internal/src/common/languageServerInterface.ts index fbcac4598..ad276acc8 100644 --- a/packages/pyright-internal/src/common/languageServerInterface.ts +++ b/packages/pyright-internal/src/common/languageServerInterface.ts @@ -15,7 +15,6 @@ import { DiagnosticSeverityOverridesMap } from './commandLineOptions'; import { SignatureDisplayType } from './configOptions'; import { ConsoleInterface, LogLevel } from './console'; import { TaskListToken } from './diagnostic'; -import * as ext from './extensibility'; import { FileSystem } from './fileSystem'; import { FileWatcherHandler } from './fileWatcher'; import { ServiceProvider } from './serviceProvider'; @@ -124,7 +123,7 @@ export interface LanguageServerBaseInterface { readonly console: ConsoleInterface; readonly window: WindowInterface; readonly supportAdvancedEdits: boolean; - readonly serviceProvider: ext.ServiceProvider; + readonly serviceProvider: ServiceProvider; createBackgroundAnalysis(serviceId: string): BackgroundAnalysisBase | undefined; reanalyze(): void; diff --git a/packages/pyright-internal/src/common/serviceKeys.ts b/packages/pyright-internal/src/common/serviceKeys.ts index 43379e182..33cb84f39 100644 --- a/packages/pyright-internal/src/common/serviceKeys.ts +++ b/packages/pyright-internal/src/common/serviceKeys.ts @@ -11,6 +11,7 @@ import { ISourceFileFactory } from '../analyzer/programTypes'; import { SupportPartialStubs } from '../pyrightFileSystem'; import { CaseSensitivityDetector } from './caseSensitivityDetector'; import { ConsoleInterface } from './console'; +import { DocStringService } from './docStringService'; import { DebugInfoInspector, StatusMutationListener, @@ -32,4 +33,5 @@ export namespace ServiceKeys { export const cacheManager = new ServiceKey(); export const debugInfoInspector = new ServiceKey(); export const caseSensitivityDetector = new ServiceKey(); + export const docStringService = new ServiceKey(); } diff --git a/packages/pyright-internal/src/common/serviceProvider.ts b/packages/pyright-internal/src/common/serviceProvider.ts index f984b779a..99597d290 100644 --- a/packages/pyright-internal/src/common/serviceProvider.ts +++ b/packages/pyright-internal/src/common/serviceProvider.ts @@ -92,6 +92,8 @@ export class ServiceProvider { this._container.forEach((value, key) => { if (key.kind === 'group') { serviceProvider._container.set(key, [...(value ?? [])]); + } else if (value.clone !== undefined) { + serviceProvider._container.set(key, value.clone()); } else { serviceProvider._container.set(key, value); } diff --git a/packages/pyright-internal/src/common/serviceProviderExtensions.ts b/packages/pyright-internal/src/common/serviceProviderExtensions.ts index d2878dda3..61a15926a 100644 --- a/packages/pyright-internal/src/common/serviceProviderExtensions.ts +++ b/packages/pyright-internal/src/common/serviceProviderExtensions.ts @@ -12,11 +12,11 @@ import { SupportPartialStubs } from '../pyrightFileSystem'; import { ServiceKeys } from './serviceKeys'; import { CaseSensitivityDetector } from './caseSensitivityDetector'; import { ConsoleInterface } from './console'; -import { ServiceProvider as ReadOnlyServiceProvider } from './extensibility'; import { FileSystem, TempFile } from './fileSystem'; import { LogTracker } from './logTracker'; import { ServiceProvider } from './serviceProvider'; import { Uri } from './uri/uri'; +import { DocStringService, PyrightDocStringService } from './docStringService'; declare module './serviceProvider' { interface ServiceProvider { @@ -26,6 +26,7 @@ declare module './serviceProvider' { sourceFileFactory(): ISourceFileFactory; partialStubs(): SupportPartialStubs; cacheManager(): CacheManager | undefined; + docStringService(): DocStringService; } } @@ -55,6 +56,9 @@ export function createServiceProvider(...services: any): ServiceProvider { if (CacheManager.is(service)) { sp.add(ServiceKeys.cacheManager, service); } + if (DocStringService.is(service)) { + sp.add(ServiceKeys.docStringService, service); + } }); return sp; } @@ -76,6 +80,11 @@ ServiceProvider.prototype.sourceFileFactory = function () { return result || DefaultSourceFileFactory; }; +ServiceProvider.prototype.docStringService = function () { + const result = this.tryGet(ServiceKeys.docStringService); + return result || new PyrightDocStringService(); +}; + ServiceProvider.prototype.cacheManager = function () { const result = this.tryGet(ServiceKeys.cacheManager); return result; @@ -83,7 +92,7 @@ ServiceProvider.prototype.cacheManager = function () { const DefaultSourceFileFactory: ISourceFileFactory = { createSourceFile( - serviceProvider: ReadOnlyServiceProvider, + serviceProvider: ServiceProvider, fileUri: Uri, moduleName: string, isThirdPartyImport: boolean, diff --git a/packages/pyright-internal/src/common/stringUtils.ts b/packages/pyright-internal/src/common/stringUtils.ts index 5edd8fe10..25393e23a 100644 --- a/packages/pyright-internal/src/common/stringUtils.ts +++ b/packages/pyright-internal/src/common/stringUtils.ts @@ -166,3 +166,7 @@ export function truncate(text: string, maxLength: number) { } return text; } + +export function escapeRegExp(text: string) { + return text.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'); +} diff --git a/packages/pyright-internal/src/common/uri/uriUtils.ts b/packages/pyright-internal/src/common/uri/uriUtils.ts index a366335c7..4588e8103 100644 --- a/packages/pyright-internal/src/common/uri/uriUtils.ts +++ b/packages/pyright-internal/src/common/uri/uriUtils.ts @@ -15,9 +15,9 @@ import { stripTrailingDirectorySeparator, } from '../pathUtils'; import { Uri } from './uri'; -import { ServiceProvider } from '../extensibility'; import { ServiceKeys } from '../serviceKeys'; import { CaseSensitivityDetector } from '../caseSensitivityDetector'; +import { ServiceProvider } from '../serviceProvider'; export interface FileSpec { // File specs can contain wildcard characters (**, *, ?). This @@ -149,7 +149,7 @@ export function tryStat(fs: ReadOnlyFileSystem, uri: Uri): Stats | undefined { export function tryRealpath(fs: ReadOnlyFileSystem, uri: Uri): Uri | undefined { try { - return fs.realCasePath(uri); + return fs.realpathSync(uri); } catch (e: any) { return undefined; } diff --git a/packages/pyright-internal/src/languageServerBase.ts b/packages/pyright-internal/src/languageServerBase.ts index 06f6944de..606f2d5bf 100644 --- a/packages/pyright-internal/src/languageServerBase.ts +++ b/packages/pyright-internal/src/languageServerBase.ts @@ -565,11 +565,11 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis this.connection.onShutdown(async (token) => this.onShutdown(token)); } - protected initialize( + protected async initialize( params: InitializeParams, supportedCommands: string[], supportedCodeActions: string[] - ): InitializeResult { + ): Promise { if (params.locale) { setLocaleOverride(params.locale); } @@ -904,6 +904,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis this.client.hasSignatureLabelOffsetCapability, this.client.hasActiveParameterCapability, params.context, + program.serviceProvider.docStringService(), token ).getSignatureHelp(); }, token); diff --git a/packages/pyright-internal/src/languageService/completionProvider.ts b/packages/pyright-internal/src/languageService/completionProvider.ts index ce585bad2..8d2b944f9 100644 --- a/packages/pyright-internal/src/languageService/completionProvider.ts +++ b/packages/pyright-internal/src/languageService/completionProvider.ts @@ -29,7 +29,6 @@ import { VariableDeclaration, } from '../analyzer/declaration'; import { isDefinedInFile } from '../analyzer/declarationUtils'; -import { convertDocStringToMarkdown, convertDocStringToPlainText } from '../analyzer/docStringConversion'; import { ImportedModuleDescriptor, ImportResolver } from '../analyzer/importResolver'; import { ImportResult } from '../analyzer/importResult'; import { getParameterListDetails, ParameterKind } from '../analyzer/parameterUtils'; @@ -41,7 +40,7 @@ import { Symbol, SymbolTable } from '../analyzer/symbol'; import * as SymbolNameUtils from '../analyzer/symbolNameUtils'; import { getLastTypedDeclarationForSymbol, isVisibleExternally } from '../analyzer/symbolUtils'; import { getTypedDictMembersForClass } from '../analyzer/typedDicts'; -import { getModuleDocStringFromUris } from '../analyzer/typeDocStringUtils'; +import { getModuleDocStringFromUris, isBuiltInModule } from '../analyzer/typeDocStringUtils'; import { CallSignatureInfo, TypeEvaluator } from '../analyzer/typeEvaluatorTypes'; import { printLiteralValue } from '../analyzer/typePrinter'; import { @@ -127,6 +126,7 @@ import { } from './completionProviderUtils'; import { DocumentSymbolCollector } from './documentSymbolCollector'; import { getAutoImportText, getDocumentationPartsForTypeAndDecl } from './tooltipUtils'; +import '../common/serviceProviderExtensions'; namespace Keywords { const base: string[] = [ @@ -351,22 +351,24 @@ export class CompletionProvider { Uri.parse(completionItemData.moduleUri, this.program.serviceProvider) ) ) { - const documentation = getModuleDocStringFromUris( - [Uri.parse(completionItemData.moduleUri, this.program.serviceProvider)], - this.sourceMapper - ); + const moduleUri = Uri.parse(completionItemData.moduleUri, this.program.serviceProvider); + const documentation = getModuleDocStringFromUris([moduleUri], this.sourceMapper); if (!documentation) { return; } if (this.options.format === MarkupKind.Markdown) { - const markdownString = convertDocStringToMarkdown(documentation); + const markdownString = this.program.serviceProvider + .docStringService() + .convertDocStringToMarkdown(documentation, isBuiltInModule(moduleUri)); completionItem.documentation = { kind: MarkupKind.Markdown, value: markdownString, }; } else if (this.options.format === MarkupKind.PlainText) { - const plainTextString = convertDocStringToPlainText(documentation); + const plainTextString = this.program.serviceProvider + .docStringService() + .convertDocStringToPlainText(documentation); completionItem.documentation = { kind: MarkupKind.PlainText, value: plainTextString, @@ -683,9 +685,11 @@ export class CompletionProvider { if (this.options.format === MarkupKind.Markdown || this.options.format === MarkupKind.PlainText) { this.itemToResolve.documentation = getCompletionItemDocumentation( + this.program.serviceProvider, typeDetail, documentation, - this.options.format + this.options.format, + primaryDecl ); } else { fail(`Unsupported markup type: ${this.options.format}`); @@ -966,7 +970,9 @@ export class CompletionProvider { if (detail?.documentation) { markdownString += '---\n'; - markdownString += convertDocStringToMarkdown(detail.documentation); + markdownString += this.program.serviceProvider + .docStringService() + .convertDocStringToMarkdown(detail.documentation, isBuiltInModule(detail.moduleUri)); } markdownString = markdownString.trimEnd(); @@ -993,7 +999,9 @@ export class CompletionProvider { } if (detail?.documentation) { - plainTextString += '\n' + convertDocStringToPlainText(detail.documentation); + plainTextString += + '\n' + + this.program.serviceProvider.docStringService().convertDocStringToPlainText(detail.documentation); } plainTextString = plainTextString.trimEnd(); diff --git a/packages/pyright-internal/src/languageService/completionProviderUtils.ts b/packages/pyright-internal/src/languageService/completionProviderUtils.ts index 9e26317c5..ae5a3208d 100644 --- a/packages/pyright-internal/src/languageService/completionProviderUtils.ts +++ b/packages/pyright-internal/src/languageService/completionProviderUtils.ts @@ -9,7 +9,6 @@ import { InsertTextFormat, MarkupContent, MarkupKind, TextEdit } from 'vscode-languageserver-types'; import { Declaration, DeclarationType } from '../analyzer/declaration'; -import { convertDocStringToMarkdown, convertDocStringToPlainText } from '../analyzer/docStringConversion'; import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes'; import { isProperty } from '../analyzer/typeUtils'; import { @@ -28,6 +27,8 @@ import { SignatureDisplayType } from '../common/configOptions'; import { TextEditAction } from '../common/editAction'; import { Uri } from '../common/uri/uri'; import { getToolTipForType } from './tooltipUtils'; +import { ServiceProvider } from '../common/serviceProvider'; +import { isBuiltInModule } from '../analyzer/typeDocStringUtils'; export interface Edits { format?: InsertTextFormat; @@ -152,16 +153,20 @@ export function getTypeDetail( } export function getCompletionItemDocumentation( + serviceProvider: ServiceProvider, typeDetail: string | undefined, documentation: string | undefined, - markupKind: MarkupKind + markupKind: MarkupKind, + declaration: Declaration | undefined ): MarkupContent | undefined { if (markupKind === MarkupKind.Markdown) { let markdownString = '```python\n' + typeDetail + '\n```\n'; if (documentation) { markdownString += '---\n'; - markdownString += convertDocStringToMarkdown(documentation); + markdownString += serviceProvider + .docStringService() + .convertDocStringToMarkdown(documentation, isBuiltInModule(declaration?.uri)); } markdownString = markdownString.trimEnd(); @@ -175,7 +180,7 @@ export function getCompletionItemDocumentation( if (documentation) { plainTextString += '\n'; - plainTextString += convertDocStringToPlainText(documentation); + plainTextString += serviceProvider.docStringService().convertDocStringToPlainText(documentation); } plainTextString = plainTextString.trimEnd(); diff --git a/packages/pyright-internal/src/languageService/definitionProvider.ts b/packages/pyright-internal/src/languageService/definitionProvider.ts index f5e437937..b4c888dae 100644 --- a/packages/pyright-internal/src/languageService/definitionProvider.ts +++ b/packages/pyright-internal/src/languageService/definitionProvider.ts @@ -27,13 +27,14 @@ import { TypeCategory, isOverloadedFunction } from '../analyzer/types'; import { throwIfCancellationRequested } from '../common/cancellationUtils'; import { appendArray } from '../common/collectionUtils'; import { isDefined } from '../common/core'; -import { ProgramView, ServiceProvider } from '../common/extensibility'; +import { ProgramView } from '../common/extensibility'; import { convertPositionToOffset } from '../common/positionUtils'; import { ServiceKeys } from '../common/serviceKeys'; import { DocumentRange, Position, rangesAreEqual } from '../common/textRange'; import { Uri } from '../common/uri/uri'; import { ParseNode, ParseNodeType } from '../parser/parseNodes'; import { ParseFileResults } from '../parser/parser'; +import { ServiceProvider } from '../common/serviceProvider'; export enum DefinitionFilter { All = 'all', diff --git a/packages/pyright-internal/src/languageService/hoverProvider.ts b/packages/pyright-internal/src/languageService/hoverProvider.ts index 8cc7bded0..f409c62b4 100644 --- a/packages/pyright-internal/src/languageService/hoverProvider.ts +++ b/packages/pyright-internal/src/languageService/hoverProvider.ts @@ -17,7 +17,6 @@ import { VariableDeclaration, isUnresolvedAliasDeclaration, } from '../analyzer/declaration'; -import { convertDocStringToMarkdown, convertDocStringToPlainText } from '../analyzer/docStringConversion'; import * as ParseTreeUtils from '../analyzer/parseTreeUtils'; import { SourceMapper } from '../analyzer/sourceMapper'; import { PrintTypeOptions, TypeEvaluator } from '../analyzer/typeEvaluatorTypes'; @@ -50,6 +49,8 @@ import { getToolTipForType, getTypeForToolTip, } from './tooltipUtils'; +import { ServiceProvider } from '../common/serviceProvider'; +import { isBuiltInModule } from '../analyzer/typeDocStringUtils'; export interface HoverTextPart { python?: boolean; @@ -91,13 +92,21 @@ export function convertHoverResults(hoverResults: HoverResults | null, format: M }; } -export function addDocumentationResultsPart(docString: string | undefined, format: MarkupKind, parts: HoverTextPart[]) { +export function addDocumentationResultsPart( + serviceProvider: ServiceProvider, + docString: string | undefined, + format: MarkupKind, + parts: HoverTextPart[], + resolvedDecl: Declaration | undefined +) { if (!docString) { return; } if (format === MarkupKind.Markdown) { - const markDown = convertDocStringToMarkdown(docString); + const markDown = serviceProvider + .docStringService() + .convertDocStringToMarkdown(docString, isBuiltInModule(resolvedDecl?.uri)); if (parts.length > 0 && markDown.length > 0) { parts.push({ text: '---\n' }); @@ -108,7 +117,7 @@ export function addDocumentationResultsPart(docString: string | undefined, forma } if (format === MarkupKind.PlainText) { - parts.push({ text: convertDocStringToPlainText(docString), python: false }); + parts.push({ text: serviceProvider.docStringService().convertDocStringToPlainText(docString), python: false }); return; } @@ -495,7 +504,7 @@ export class HoverProvider { name, }); - addDocumentationResultsPart(docString, this._format, parts); + addDocumentationResultsPart(this._program.serviceProvider, docString, this._format, parts, resolvedDecl); return !!docString; } diff --git a/packages/pyright-internal/src/languageService/signatureHelpProvider.ts b/packages/pyright-internal/src/languageService/signatureHelpProvider.ts index 462626dc7..9ce148221 100644 --- a/packages/pyright-internal/src/languageService/signatureHelpProvider.ts +++ b/packages/pyright-internal/src/languageService/signatureHelpProvider.ts @@ -20,8 +20,6 @@ import { SignatureInformation, } from 'vscode-languageserver'; -import { convertDocStringToMarkdown, convertDocStringToPlainText } from '../analyzer/docStringConversion'; -import { extractParameterDocumentation } from '../analyzer/docStringUtils'; import * as ParseTreeUtils from '../analyzer/parseTreeUtils'; import { getCallNodeAndActiveParameterIndex } from '../analyzer/parseTreeUtils'; import { SourceMapper } from '../analyzer/sourceMapper'; @@ -35,6 +33,9 @@ import { Uri } from '../common/uri/uri'; import { CallNode, NameNode, ParseNodeType } from '../parser/parseNodes'; import { ParseFileResults } from '../parser/parser'; import { getDocumentationPartsForTypeAndDecl, getFunctionDocStringFromType } from './tooltipUtils'; +import { DocStringService } from '../common/docStringService'; +import { getFileInfo } from '../analyzer/analyzerNodeInfo'; +import { isBuiltInModule } from '../analyzer/typeDocStringUtils'; export class SignatureHelpProvider { private readonly _parseResults: ParseFileResults | undefined; @@ -48,6 +49,7 @@ export class SignatureHelpProvider { private _hasSignatureLabelOffsetCapability: boolean, private _hasActiveParameterCapability: boolean, private _context: SignatureHelpContext | undefined, + private _docStringService: DocStringService, private _token: CancellationToken ) { this._parseResults = this._program.getParseResults(this._fileUri); @@ -224,6 +226,7 @@ export class SignatureHelpProvider { const functionDocString = getFunctionDocStringFromType(functionType, this._sourceMapper, this._evaluator) ?? this._getDocStringFromCallNode(callNode); + const fileInfo = getFileInfo(callNode); let label = '('; let activeParameter: number | undefined; @@ -241,7 +244,7 @@ export class SignatureHelpProvider { startOffset: label.length, endOffset: label.length + paramString.length, text: paramString, - documentation: extractParameterDocumentation(functionDocString || '', paramName), + documentation: this._docStringService.extractParameterDocumentation(functionDocString || '', paramName), }); // Name match for active parameter. The set of parameters from the function @@ -275,12 +278,15 @@ export class SignatureHelpProvider { if (this._format === MarkupKind.Markdown) { sigInfo.documentation = { kind: MarkupKind.Markdown, - value: convertDocStringToMarkdown(functionDocString), + value: this._docStringService.convertDocStringToMarkdown( + functionDocString, + isBuiltInModule(fileInfo?.fileUri) + ), }; } else { sigInfo.documentation = { kind: MarkupKind.PlainText, - value: convertDocStringToPlainText(functionDocString), + value: this._docStringService.convertDocStringToPlainText(functionDocString), }; } } diff --git a/packages/pyright-internal/src/localization/package.nls.cs.json b/packages/pyright-internal/src/localization/package.nls.cs.json index 02cbc300e..d266c781c 100644 --- a/packages/pyright-internal/src/localization/package.nls.cs.json +++ b/packages/pyright-internal/src/localization/package.nls.cs.json @@ -156,6 +156,7 @@ "enumClassOverride": "Třída výčtu {name} je konečná a nemůže být podtřídou", "enumMemberDelete": "Člen výčtu {name} se nedá odstranit.", "enumMemberSet": "Člen výčtu {name} se nedá přiřadit.", + "enumMemberTypeAnnotation": "Poznámky typu nejsou pro členy výčtu povolené", "exceptionGroupIncompatible": "Syntaxe skupiny výjimek (except*) vyžaduje Python 3.11 nebo novější", "exceptionTypeIncorrect": "„{type}“ se neodvozuje od BaseException", "exceptionTypeNotClass": "{type} není platná třída výjimky", @@ -624,7 +625,7 @@ "yieldFromIllegal": "Použití příkazu yield from vyžaduje Python 3.3 nebo novější", "yieldFromOutsideAsync": "yield from není v asynchronní funkci povoleno", "yieldOutsideFunction": "„yield“ není povoleno mimo funkci nebo lambdu", - "yieldWithinComprehension": "yield není povolen uvnitř seznamu porozumění", + "yieldWithinComprehension": "„yield“ není povolené uvnitř porozumění", "zeroCaseStatementsFound": "Výraz shody obsahovat alespoň jeden výraz velikosti písmen", "zeroLengthTupleNotAllowed": "Řazená kolekce členů s nulovou délkou není v tomto kontextu povolená" }, diff --git a/packages/pyright-internal/src/localization/package.nls.de.json b/packages/pyright-internal/src/localization/package.nls.de.json index 6fa8d64ac..fa38976d3 100644 --- a/packages/pyright-internal/src/localization/package.nls.de.json +++ b/packages/pyright-internal/src/localization/package.nls.de.json @@ -156,6 +156,7 @@ "enumClassOverride": "Die Enumerationsklasse \"{name}\" ist final und kann nicht in eine Unterklasse aufgenommen werden.", "enumMemberDelete": "Das Enumerationselement \"{name}\" kann nicht gelöscht werden.", "enumMemberSet": "Das Enumerationselement \"{name}\" kann nicht zugewiesen werden.", + "enumMemberTypeAnnotation": "Typanmerkungen sind für Enumerationsmember nicht zulässig", "exceptionGroupIncompatible": "Die Ausnahmegruppensyntax (\"except*\") erfordert Python 3.11 oder höher.", "exceptionTypeIncorrect": "\"{type}\" ist nicht von BaseException abgeleitet.", "exceptionTypeNotClass": "\"{type}\" ist keine gültige Ausnahmeklasse.", @@ -624,7 +625,7 @@ "yieldFromIllegal": "Die Verwendung von \"yield from\" erfordert Python 3.3 oder höher.", "yieldFromOutsideAsync": "\"yield from\" ist in einer asynchronen Funktion nicht zulässig.", "yieldOutsideFunction": "\"yield\" ist außerhalb einer Funktion oder eines Lambdas nicht zulässig.", - "yieldWithinComprehension": "\"yield\" ist innerhalb eines Listenverständnisses nicht zulässig.", + "yieldWithinComprehension": "„yield“ ist innerhalb eines Verständnisses nicht zulässig", "zeroCaseStatementsFound": "Die match-Anweisung muss mindestens eine case-Anweisung enthalten", "zeroLengthTupleNotAllowed": "Ein Tupel mit der Länge Null ist in diesem Kontext nicht zulässig." }, diff --git a/packages/pyright-internal/src/localization/package.nls.es.json b/packages/pyright-internal/src/localization/package.nls.es.json index ef5db5f48..e912b1730 100644 --- a/packages/pyright-internal/src/localization/package.nls.es.json +++ b/packages/pyright-internal/src/localization/package.nls.es.json @@ -156,6 +156,7 @@ "enumClassOverride": "La clase Enum \"{name}\" es final y no puede ser subclasificada", "enumMemberDelete": "No se puede eliminar el miembro de enumeración \"{name}\"", "enumMemberSet": "No se puede asignar el miembro de enumeración \"{name}\"", + "enumMemberTypeAnnotation": "No se permiten anotaciones de tipo para miembros de enumeración", "exceptionGroupIncompatible": "La sintaxis de grupo de excepciones (\"except*\") requiere Python 3.11 o posterior.", "exceptionTypeIncorrect": "\"{type}\" no se deriva de BaseException", "exceptionTypeNotClass": "\"{type}\" no es una clase de excepción válida", diff --git a/packages/pyright-internal/src/localization/package.nls.fr.json b/packages/pyright-internal/src/localization/package.nls.fr.json index d438418cb..79c7060fe 100644 --- a/packages/pyright-internal/src/localization/package.nls.fr.json +++ b/packages/pyright-internal/src/localization/package.nls.fr.json @@ -156,6 +156,7 @@ "enumClassOverride": "La classe Enum « {name} » est finale et ne peut pas être sous-classée", "enumMemberDelete": "Le membre enum « {name} » ne peut pas être supprimé", "enumMemberSet": "Le membre enum « {name} » ne peut pas être affecté", + "enumMemberTypeAnnotation": "Les annotations de type ne sont pas autorisées pour les membres enum", "exceptionGroupIncompatible": "La syntaxe du groupe d’exceptions (« except* ») nécessite Python 3.11 ou version ultérieure", "exceptionTypeIncorrect": "\"{type}\" ne dérive pas de BaseException", "exceptionTypeNotClass": "« {type} » n’est pas une classe d’exception valide", @@ -624,7 +625,7 @@ "yieldFromIllegal": "L’utilisation de « yield from » nécessite Python 3.3 ou version ultérieure", "yieldFromOutsideAsync": "« yield from » non autorisé dans une fonction asynchrone", "yieldOutsideFunction": "\"rendement\" non autorisé en dehors d'une fonction ou d'un lambda", - "yieldWithinComprehension": "« yield » non autorisé dans une compréhension de liste", + "yieldWithinComprehension": "« yield » n’est pas autorisé dans une compréhension de liste", "zeroCaseStatementsFound": "L'instruction de correspondance doit inclure au moins une instruction case", "zeroLengthTupleNotAllowed": "Le tuple de longueur nulle n’est pas autorisé dans ce contexte" }, diff --git a/packages/pyright-internal/src/localization/package.nls.it.json b/packages/pyright-internal/src/localization/package.nls.it.json index 0b700fc6b..85dca4102 100644 --- a/packages/pyright-internal/src/localization/package.nls.it.json +++ b/packages/pyright-internal/src/localization/package.nls.it.json @@ -156,6 +156,7 @@ "enumClassOverride": "La classe di enumerazione \"{name}\" è finale e non può essere sottoclassata", "enumMemberDelete": "Non è possibile eliminare il membro di enumerazione \"{name}\"", "enumMemberSet": "Non è possibile assegnare il membro di enumerazione \"{name}\"", + "enumMemberTypeAnnotation": "Le annotazioni di tipo non sono consentite per i membri di enumerazione", "exceptionGroupIncompatible": "La sintassi del gruppo di eccezioni (\"except*\") richiede Python 3.11 o versione successiva", "exceptionTypeIncorrect": "\"{type}\" non deriva da BaseException", "exceptionTypeNotClass": "\"{type}\" non è una classe di eccezione valida", @@ -624,7 +625,7 @@ "yieldFromIllegal": "L'uso di \"yield from\" richiede Python 3.3 o versione successiva", "yieldFromOutsideAsync": "\"yield from\" non consentito in una funzione asincrona", "yieldOutsideFunction": "\"yield\" non consentito all'esterno di una funzione o di un'espressione lambda", - "yieldWithinComprehension": "\"yield\" non consentito all'interno di una comprensione di elenco", + "yieldWithinComprehension": "\"yield\" non consentito all'interno di una comprensione", "zeroCaseStatementsFound": "L’istruzione Match deve includere almeno un’istruzione case", "zeroLengthTupleNotAllowed": "Tupla di lunghezza zero non è consentita in questo contesto" }, diff --git a/packages/pyright-internal/src/localization/package.nls.ja.json b/packages/pyright-internal/src/localization/package.nls.ja.json index ebd469a29..d1edefd0b 100644 --- a/packages/pyright-internal/src/localization/package.nls.ja.json +++ b/packages/pyright-internal/src/localization/package.nls.ja.json @@ -156,6 +156,7 @@ "enumClassOverride": "列挙型クラス \"{name}\" は最終的なクラスであり、サブクラス化できません", "enumMemberDelete": "列挙型メンバー \"{name}\" を削除できません", "enumMemberSet": "列挙型メンバー \"{name}\" を割り当てることはできません", + "enumMemberTypeAnnotation": "列挙型メンバーには型注釈を使用できません", "exceptionGroupIncompatible": "例外グループの構文 (\"except*\") には Python 3.11 以降が必要です", "exceptionTypeIncorrect": "\"{type}\" は BaseException から派生していません", "exceptionTypeNotClass": "\"{type}\" は有効な例外クラスではありません", @@ -624,7 +625,7 @@ "yieldFromIllegal": "\"yield from\" を使用するには Python 3.3 以降が必要です", "yieldFromOutsideAsync": "非同期関数では \"yield from\" は使用できません", "yieldOutsideFunction": "関数またはラムダの外部では \"yield\" は許可されません", - "yieldWithinComprehension": "\"yield\" はリスト理解内では使用できません", + "yieldWithinComprehension": "\"yield\" は内包表記内では使用できません", "zeroCaseStatementsFound": "Match ステートメントには、少なくとも 1 つの case ステートメントを含める必要があります", "zeroLengthTupleNotAllowed": "このコンテキストでは長さ 0 のタプルは使用できません" }, diff --git a/packages/pyright-internal/src/localization/package.nls.ko.json b/packages/pyright-internal/src/localization/package.nls.ko.json index 8f71386dc..465ced38d 100644 --- a/packages/pyright-internal/src/localization/package.nls.ko.json +++ b/packages/pyright-internal/src/localization/package.nls.ko.json @@ -156,6 +156,7 @@ "enumClassOverride": "열거형 클래스 \"{name}\"은(는) 최종 클래스이며 서브클래스할 수 없습니다.", "enumMemberDelete": "열거형 멤버 \"{name}\"을(를) 삭제할 수 없음", "enumMemberSet": "열거형 멤버 \"{name}\"을(를) 할당할 수 없음", + "enumMemberTypeAnnotation": "열거형 멤버에는 형식 주석을 사용할 수 없습니다.", "exceptionGroupIncompatible": "예외 그룹 구문(\"except*\")에는 Python 3.11 이상이 필요합니다.", "exceptionTypeIncorrect": "‘{type}’은 BaseException에서 파생되지 않습니다.", "exceptionTypeNotClass": "\"{type}\"은(는) 올바른 예외 클래스가 아닙니다.", @@ -624,7 +625,7 @@ "yieldFromIllegal": "\"yield from\"을 사용하려면 Python 3.3 이상이 필요합니다.", "yieldFromOutsideAsync": "비동기 함수에서는 \"yield from\"을 사용할 수 없습니다.", "yieldOutsideFunction": "함수 또는 람다 외부에서는 ‘yield’를 사용할 수 없습니다.", - "yieldWithinComprehension": "목록 이해 내에서는 \"yield\"를 사용할 수 없습니다.", + "yieldWithinComprehension": "이해력 내에서는 \"일시 중단\"을 사용할 수 없습니다.", "zeroCaseStatementsFound": "Match 문에는 Case 문이 하나 이상 포함되어야 합니다.", "zeroLengthTupleNotAllowed": "길이가 0인 튜플은 이 컨텍스트에서 허용되지 않습니다." }, diff --git a/packages/pyright-internal/src/localization/package.nls.pl.json b/packages/pyright-internal/src/localization/package.nls.pl.json index 2bc6665d2..824287a2a 100644 --- a/packages/pyright-internal/src/localization/package.nls.pl.json +++ b/packages/pyright-internal/src/localization/package.nls.pl.json @@ -156,6 +156,7 @@ "enumClassOverride": "Klasa wyliczenia „{name}” jest ostateczna i nie można jej podzielić na podklasy", "enumMemberDelete": "Nie można usunąć składowej wyliczenia \"{name}\"", "enumMemberSet": "Nie można przypisać składowej wyliczenia „{name}”", + "enumMemberTypeAnnotation": "Adnotacje typu nie są dozwolone dla elementów członkowskich wyliczenia", "exceptionGroupIncompatible": "Składnia grupy wyjątków („except*”) wymaga języka Python w wersji 3.11 lub nowszej", "exceptionTypeIncorrect": "Typ „{type}” nie pochodzi od parametru BaseException", "exceptionTypeNotClass": "Typ „{type}” nie jest prawidłową klasą wyjątku", @@ -624,7 +625,7 @@ "yieldFromIllegal": "Użycie wartości „yield from” wymaga języka Python w wersji 3.3 lub nowszej", "yieldFromOutsideAsync": "Instrukcja „yield from” jest niedozwolona w funkcji asynchronicznej", "yieldOutsideFunction": "Instrukcja „yield” jest niedozwolona poza funkcją lub wyrażeniem lambda", - "yieldWithinComprehension": "Instrukcja „yield” nie jest dozwolona w rozumieniu listy", + "yieldWithinComprehension": "Instrukcja „yield” nie jest dozwolona w rozumieniu", "zeroCaseStatementsFound": "Instrukcja dopasowania musi zawierać co najmniej jedną instrukcję dotyczącą wielkości liter", "zeroLengthTupleNotAllowed": "Krotka o zerowej długości jest niedozwolona w tym kontekście" }, diff --git a/packages/pyright-internal/src/localization/package.nls.pt-br.json b/packages/pyright-internal/src/localization/package.nls.pt-br.json index a8791edf8..65acfa7af 100644 --- a/packages/pyright-internal/src/localization/package.nls.pt-br.json +++ b/packages/pyright-internal/src/localization/package.nls.pt-br.json @@ -156,6 +156,7 @@ "enumClassOverride": "A classe Enum \"{name}\" é final e não pode ser subclasse", "enumMemberDelete": "O membro enumerado \"{name}\" não pode ser excluído", "enumMemberSet": "O membro enumerado \"{name}\" não pode ser atribuído", + "enumMemberTypeAnnotation": "Anotações de tipo não são permitidas para membros de enumeração", "exceptionGroupIncompatible": "A sintaxe do grupo de exceção (\"exceto*\") requer o Python 3.11 ou mais recente", "exceptionTypeIncorrect": "\"{type}\" não deriva de BaseException", "exceptionTypeNotClass": "\"{type}\" não é uma classe de exceção válida", @@ -624,7 +625,7 @@ "yieldFromIllegal": "O uso de \"yield from\" requer o Python 3.3 ou mais recente", "yieldFromOutsideAsync": "\"yield from\" não é permitido em uma função assíncrona", "yieldOutsideFunction": "\"yield\" não permitido fora de uma função ou lambda", - "yieldWithinComprehension": "\"yield\" não é permitido dentro de uma compreensão de lista", + "yieldWithinComprehension": "\"yield\" não é permitido dentro de uma compreensão", "zeroCaseStatementsFound": "A instrução Match deve incluir pelo menos uma instrução case", "zeroLengthTupleNotAllowed": "Tupla de comprimento zero não é permitida neste contexto" }, diff --git a/packages/pyright-internal/src/localization/package.nls.qps-ploc.json b/packages/pyright-internal/src/localization/package.nls.qps-ploc.json index 22f56afad..f9a83b064 100644 --- a/packages/pyright-internal/src/localization/package.nls.qps-ploc.json +++ b/packages/pyright-internal/src/localization/package.nls.qps-ploc.json @@ -156,6 +156,7 @@ "enumClassOverride": "[2JsL1][นั้Ëñµm çlæss \"{ñæmë}\" ïs fïñæl æñð çæññøt þë sµþçlæssëðẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्นั้ढूँ]", "enumMemberDelete": "[5wmRY][นั้Ëñµm mëmþër \"{ñæmë}\" çæññøt þë ðëlëtëðẤğ倪İЂҰक्र्तिृまẤนั้ढूँ]", "enumMemberSet": "[mBLro][นั้Ëñµm mëmþër \"{ñæmë}\" çæññøt þë æssïgñëðẤğ倪İЂҰक्र्तिृまẤğนั้ढूँ]", + "enumMemberTypeAnnotation": "[z8FaL][นั้Tÿpë æññøtætïøñs ærë ñøt ælløwëð før ëñµm mëmþërsẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰนั้ढूँ]", "exceptionGroupIncompatible": "[d0SLP][นั้Ëxçëptïøñ grøµp sÿñtæx (\"ëxçëpt*\") rëqµïrës Pÿthøñ 3.11 ør ñëwërẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्र्तिृนั้ढूँ]", "exceptionTypeIncorrect": "[G7AZt][นั้\"{tÿpë}\" ðøës ñøt ðërïvë frøm ßæsëËxçëptïøñẤğ倪İЂҰक्र्तिृまẤğ倪นั้ढूँ]", "exceptionTypeNotClass": "[v1FmY][นั้\"{tÿpë}\" ïs ñøt æ vælïð ëxçëptïøñ çlæssẤğ倪İЂҰक्र्तिृまẤğนั้ढूँ]", @@ -624,7 +625,7 @@ "yieldFromIllegal": "[DkXto][นั้Üsë øf \"ÿïëlð frøm\" rëqµïrës Pÿthøñ 3.3 ør ñëwërẤğ倪İЂҰक्र्तिृまẤğ倪İЂนั้ढूँ]", "yieldFromOutsideAsync": "[ZONEz][นั้\"ÿïëlð frøm\" ñøt ælløwëð ïñ æñ æsÿñç fµñçtïøñẤğ倪İЂҰक्र्तिृまẤğ倪İนั้ढूँ]", "yieldOutsideFunction": "[2lDBQ][นั้\"ÿïëlð\" ñøt ælløwëð øµtsïðë øf æ fµñçtïøñ ør læmþðæẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰนั้ढूँ]", - "yieldWithinComprehension": "[3Rv4s][นั้\"ÿïëlð\" ñøt ælløwëð ïñsïðë æ lïst çømprëhëñsïøñẤğ倪İЂҰक्र्तिृまẤğ倪İЂนั้ढूँ]", + "yieldWithinComprehension": "[yALS5][นั้\"ÿïëlð\" ñøt ælløwëð ïñsïðë æ çømprëhëñsïøñẤğ倪İЂҰक्र्तिृまẤğ倪นั้ढूँ]", "zeroCaseStatementsFound": "[ArU3j][นั้Mætçh stætëmëñt mµst ïñçlµðë æt lëæst øñë çæsë stætëmëñtẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्นั้ढूँ]", "zeroLengthTupleNotAllowed": "[3gVpF][นั้Zërø-lëñgth tµplë ïs ñøt ælløwëð ïñ thïs çøñtëxtẤğ倪İЂҰक्र्तिृまẤğ倪İЂนั้ढूँ]" }, diff --git a/packages/pyright-internal/src/localization/package.nls.ru.json b/packages/pyright-internal/src/localization/package.nls.ru.json index 4be08a05b..29a5a5a11 100644 --- a/packages/pyright-internal/src/localization/package.nls.ru.json +++ b/packages/pyright-internal/src/localization/package.nls.ru.json @@ -156,6 +156,7 @@ "enumClassOverride": "Перечислимый класс \"{name}\" является окончательным и не может иметь производных классов", "enumMemberDelete": "Не удается удалить элемент перечисления \"{name}\"", "enumMemberSet": "Не удается назначить элемент перечисления \"{name}\"", + "enumMemberTypeAnnotation": "Аннотации типов не разрешены для элементов перечисления", "exceptionGroupIncompatible": "Синтаксис группы исключений (\"except*\") можно использовать в Python версии не ранее 3.11", "exceptionTypeIncorrect": "\"{type}\" не является производным от BaseException", "exceptionTypeNotClass": "\"{type}\" не является допустимым классом исключений", @@ -624,7 +625,7 @@ "yieldFromIllegal": "\"Yield from\" можно использовать в Python версии не ниже 3.3", "yieldFromOutsideAsync": "\"yield from\" не допускается в асинхронной функции", "yieldOutsideFunction": "\"yield\" не допускается за пределами функции или лямбда-выражении", - "yieldWithinComprehension": "\"yield\" не допускается внутри понимания списка", + "yieldWithinComprehension": "\"yield\" не допускается внутри понимания", "zeroCaseStatementsFound": "Операторе match должен включать по крайней мере один оператор case", "zeroLengthTupleNotAllowed": "Кортеж нулевой длины не допускается в этом контексте" }, diff --git a/packages/pyright-internal/src/localization/package.nls.tr.json b/packages/pyright-internal/src/localization/package.nls.tr.json index 609ad7f8c..0c8707c85 100644 --- a/packages/pyright-internal/src/localization/package.nls.tr.json +++ b/packages/pyright-internal/src/localization/package.nls.tr.json @@ -156,6 +156,7 @@ "enumClassOverride": "\"{name}\" sabit listesi sınıfı final niteliğinde ve alt sınıf olamaz", "enumMemberDelete": "Sabit liste üyesi \"{name}\" silinemiyor", "enumMemberSet": "Sabit liste üyesi \"{name}\" atanamıyor", + "enumMemberTypeAnnotation": "Sabit listesi üyeleri için tür ek açıklamalarına izin verilmiyor", "exceptionGroupIncompatible": "Özel durum grubu söz dizimi (\"except*\") için Python 3.11 veya daha yeni bir sürümü gerekiyor", "exceptionTypeIncorrect": "\"{type}\", BaseException türevi değil", "exceptionTypeNotClass": "\"{type}\" geçerli bir özel durum sınıfı değil", @@ -624,7 +625,7 @@ "yieldFromIllegal": "\"yield from\" kullanımı için Python 3.3 veya daha yeni bir sürümü gerekiyor", "yieldFromOutsideAsync": "Zaman uyumsuz bir işlevde \"yield from\" öğesine izin verilmez", "yieldOutsideFunction": "\"yield\", işlev veya lambda dışında kullanılamaz", - "yieldWithinComprehension": "Liste anlama içinde \"yield\" kullanılamaz", + "yieldWithinComprehension": "Bir anlama içinde “yield” kullanılamaz", "zeroCaseStatementsFound": "Match deyimi en az bir case deyimi içermeli", "zeroLengthTupleNotAllowed": "Bu bağlamda sıfır uzunluklu demete izin verilmiyor" }, diff --git a/packages/pyright-internal/src/localization/package.nls.zh-cn.json b/packages/pyright-internal/src/localization/package.nls.zh-cn.json index cdca7effa..13b573002 100644 --- a/packages/pyright-internal/src/localization/package.nls.zh-cn.json +++ b/packages/pyright-internal/src/localization/package.nls.zh-cn.json @@ -156,6 +156,7 @@ "enumClassOverride": "枚举类“{name}”是最终类,不能为子类", "enumMemberDelete": "无法删除枚举成员“{name}”", "enumMemberSet": "无法分配枚举成员“{name}”", + "enumMemberTypeAnnotation": "枚举成员不允许使用类型注释", "exceptionGroupIncompatible": "异常组语法 (\"except*\") 需要 Python 3.11 或更高版本", "exceptionTypeIncorrect": "\"{type}\" 不是派生自 BaseException", "exceptionTypeNotClass": "“{type}”不是有效的异常类", @@ -624,7 +625,7 @@ "yieldFromIllegal": "使用“yield from”需要 Python 3.3 或更高版本", "yieldFromOutsideAsync": "异步函数中不允许使用“yield from”", "yieldOutsideFunction": "不允许在函数或 lambda 之外使用“yield”", - "yieldWithinComprehension": "不允许在列表理解中使用“yield”", + "yieldWithinComprehension": "不允许在理解中使用 \"yield\"", "zeroCaseStatementsFound": "Match 语句必须至少包含一个 case 语句", "zeroLengthTupleNotAllowed": "此上下文中不允许使用零长度元组" }, diff --git a/packages/pyright-internal/src/localization/package.nls.zh-tw.json b/packages/pyright-internal/src/localization/package.nls.zh-tw.json index 9ea5e5a72..5b42218ba 100644 --- a/packages/pyright-internal/src/localization/package.nls.zh-tw.json +++ b/packages/pyright-internal/src/localization/package.nls.zh-tw.json @@ -156,6 +156,7 @@ "enumClassOverride": "列舉類別 \"{name}\" 為 Final,且不能設為子類別", "enumMemberDelete": "無法刪除列舉成員 \"{name}\"", "enumMemberSet": "無法指派列舉成員 \"{name}\"", + "enumMemberTypeAnnotation": "列舉成員不允許類型註釋", "exceptionGroupIncompatible": "例外群組語法 (\"except*\") 需要 Python 3.11 或更新版本", "exceptionTypeIncorrect": "\"{type}\" 不是衍生自 BaseException", "exceptionTypeNotClass": "\"{type}\" 不是有效的例外類別", @@ -624,7 +625,7 @@ "yieldFromIllegal": "使用 \"yield from\" 需要 Python 3.3 或更新版本", "yieldFromOutsideAsync": "非同步函式中不允許 \"yield from\"", "yieldOutsideFunction": "在函式或 lambda 外部不允許 \"yield\"", - "yieldWithinComprehension": "清單理解內不允許 \"yield\"", + "yieldWithinComprehension": "理解內不允許 \"yield\"", "zeroCaseStatementsFound": "Match 陳述式必須至少包含一個 case 陳述式", "zeroLengthTupleNotAllowed": "此內容中不允許零長度 Tuple" }, diff --git a/packages/pyright-internal/src/tests/docStringConversion.test.ts b/packages/pyright-internal/src/tests/docStringConversion.test.ts index 7e9254655..0854d4616 100644 --- a/packages/pyright-internal/src/tests/docStringConversion.test.ts +++ b/packages/pyright-internal/src/tests/docStringConversion.test.ts @@ -7,7 +7,7 @@ */ import assert = require('assert'); -import { convertDocStringToMarkdown, convertDocStringToPlainText } from '../analyzer/docStringConversion'; +import { PyrightDocStringService } from '../common/docStringService'; // For substitution in the test data strings // Produces more readable test data than escaping the back ticks @@ -16,62 +16,63 @@ const doubleTick = '``'; const tripleTick = '```'; const tripleTilda = '~~~'; -test('PlaintextIndention', () => { - const all: string[][] = [ - ['A\nB', 'A\nB'], - ['A\n\nB', 'A\n\nB'], - ['A\n B', 'A\nB'], - [' A\n B', 'A\nB'], - ['\nA\n B', 'A\n B'], - ['\n A\n B', 'A\nB'], - ['\nA\nB\n', 'A\nB'], - [' \n\nA \n \nB \n ', 'A\n\nB'], - ]; +export function docStringTests(docStringService = new PyrightDocStringService()) { + test('PlaintextIndention', () => { + const all: string[][] = [ + ['A\nB', 'A\nB'], + ['A\n\nB', 'A\n\nB'], + ['A\n B', 'A\nB'], + [' A\n B', 'A\nB'], + ['\nA\n B', 'A\n B'], + ['\n A\n B', 'A\nB'], + ['\nA\nB\n', 'A\nB'], + [' \n\nA \n \nB \n ', 'A\n\nB'], + ]; - all.forEach((v) => _testConvertToPlainText(v[0], v[1])); -}); + all.forEach((v) => _testConvertToPlainText(v[0], v[1])); + }); -test('MarkdownIndention', () => { - const all: string[][] = [ - ['A\nB', 'A\nB'], - ['A\n\nB', 'A\n\nB'], - ['A\n B', 'A\nB'], - [' A\n B', 'A\nB'], - ['\nA\n B', 'A \n    B'], - ['\n A\n B', 'A\nB'], - ['\nA\nB\n', 'A\nB'], - [' \n\nA \n \nB \n ', 'A\n\nB'], - ]; + test('MarkdownIndention', () => { + const all: string[][] = [ + ['A\nB', 'A\nB'], + ['A\n\nB', 'A\n\nB'], + ['A\n B', 'A\nB'], + [' A\n B', 'A\nB'], + ['\nA\n B', 'A \n    B'], + ['\n A\n B', 'A\nB'], + ['\nA\nB\n', 'A\nB'], + [' \n\nA \n \nB \n ', 'A\n\nB'], + ]; - all.forEach((v) => _testConvertToMarkdown(v[0], v[1])); -}); + all.forEach((v) => _testConvertToMarkdown(v[0], v[1])); + }); -test('NormalText', () => { - const docstring = `This is just some normal text + test('NormalText', () => { + const docstring = `This is just some normal text that extends over multiple lines. This will appear as-is without modification. `; - const markdown = `This is just some normal text + const markdown = `This is just some normal text that extends over multiple lines. This will appear as-is without modification. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('InlineLiterals', () => { - const docstring = - 'This paragraph talks about ``foo``\n' + - 'which is related to :something:`bar`, and probably `qux`:something_else:.\n'; + test('InlineLiterals', () => { + const docstring = + 'This paragraph talks about ``foo``\n' + + 'which is related to :something:`bar`, and probably `qux`:something_else:.\n'; - const markdown = 'This paragraph talks about `foo` \n' + 'which is related to `bar`, and probably `qux`.\n'; + const markdown = 'This paragraph talks about `foo` \n' + 'which is related to `bar`, and probably `qux`.\n'; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('Headings', () => { - const docstring = `Heading 1 + test('Headings', () => { + const docstring = `Heading 1 ========= Heading 2 @@ -84,7 +85,7 @@ Heading 4 +++++++++ `; - const markdown = `Heading 1 + const markdown = `Heading 1 ========= Heading 2 @@ -97,11 +98,11 @@ Heading 4 --------- `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('AsterisksAtStartOfArgs', () => { - const docstring = `Foo: + test('AsterisksAtStartOfArgs', () => { + const docstring = `Foo: Args: foo (Foo): Foo! @@ -109,7 +110,7 @@ test('AsterisksAtStartOfArgs', () => { **kwargs: These are named args. `; - const markdown = `Foo: + const markdown = `Foo: Args:     foo (Foo): Foo! @@ -117,27 +118,27 @@ Args:     \\*\\*kwargs: These are named args. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('CopyrightAndLicense', () => { - const docstring = `This is a test. + test('CopyrightAndLicense', () => { + const docstring = `This is a test. :copyright: Fake Name :license: ABCv123 `; - const markdown = `This is a test. + const markdown = `This is a test. :copyright: Fake Name :license: ABCv123 `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('CommonRestFieldLists', () => { - const docstring = `This function does something. + test('CommonRestFieldLists', () => { + const docstring = `This function does something. :param foo: This is a description of the foo parameter which does something interesting. @@ -149,7 +150,7 @@ test('CommonRestFieldLists', () => { :raises ValueError: If something goes wrong. `; - const markdown = `This function does something. + const markdown = `This function does something. :param foo: This is a description of the foo parameter     which does something interesting. @@ -161,17 +162,17 @@ test('CommonRestFieldLists', () => { :raises ValueError: If something goes wrong. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('Doctest', () => { - const docstring = `This is a doctest: + test('Doctest', () => { + const docstring = `This is a doctest: >>> print('foo') foo `; - const markdown = `This is a doctest: + const markdown = `This is a doctest: ${tripleTick} >>> print('foo') @@ -179,17 +180,17 @@ foo ${tripleTick} `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('DoctestIndented', () => { - const docstring = `This is a doctest: + test('DoctestIndented', () => { + const docstring = `This is a doctest: >>> print('foo') foo `; - const markdown = `This is a doctest: + const markdown = `This is a doctest: ${tripleTick} >>> print('foo') @@ -197,11 +198,11 @@ foo ${tripleTick} `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('DoctestTextAfter', () => { - const docstring = `This is a doctest: + test('DoctestTextAfter', () => { + const docstring = `This is a doctest: >>> print('foo') foo @@ -209,7 +210,7 @@ foo This text comes after. `; - const markdown = `This is a doctest: + const markdown = `This is a doctest: ${tripleTick} >>> print('foo') @@ -219,18 +220,18 @@ ${tripleTick} This text comes after. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('DoctestIndentedTextAfter', () => { - const docstring = `This is a doctest: + test('DoctestIndentedTextAfter', () => { + const docstring = `This is a doctest: >>> print('foo') foo This line has a different indent. `; - const markdown = `This is a doctest: + const markdown = `This is a doctest: ${tripleTick} >>> print('foo') @@ -240,11 +241,11 @@ ${tripleTick} This line has a different indent. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('MarkdownStyleBacktickBlock', () => { - const docstring = `Backtick block: + test('MarkdownStyleBacktickBlock', () => { + const docstring = `Backtick block: ${tripleTick} print(foo_bar) @@ -256,7 +257,7 @@ ${tripleTick} And some text after. `; - const markdown = `Backtick block: + const markdown = `Backtick block: ${tripleTick} print(foo_bar) @@ -268,11 +269,11 @@ ${tripleTick} And some text after. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('MarkdownStyleTildaBlock', () => { - const docstring = `Backtick block: + test('MarkdownStyleTildaBlock', () => { + const docstring = `Backtick block: ${tripleTilda} print(foo_bar) @@ -284,7 +285,7 @@ ${tripleTilda} And some text after. `; - const markdown = `Backtick block: + const markdown = `Backtick block: ${tripleTilda} print(foo_bar) @@ -296,11 +297,11 @@ ${tripleTilda} And some text after. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('RestLiteralBlock', () => { - const docstring = ` + test('RestLiteralBlock', () => { + const docstring = ` Take a look at this code:: if foo: @@ -311,7 +312,7 @@ Take a look at this code:: This text comes after. `; - const markdown = `Take a look at this code: + const markdown = `Take a look at this code: ${tripleTick} if foo: @@ -323,11 +324,11 @@ ${tripleTick} This text comes after. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('RestLiteralBlockEmptyDoubleColonLine', () => { - const docstring = ` + test('RestLiteralBlockEmptyDoubleColonLine', () => { + const docstring = ` :: if foo: @@ -336,7 +337,7 @@ test('RestLiteralBlockEmptyDoubleColonLine', () => { print('not foo!') `; - const markdown = `${tripleTick} + const markdown = `${tripleTick} if foo: print(foo) else: @@ -344,11 +345,11 @@ test('RestLiteralBlockEmptyDoubleColonLine', () => { ${tripleTick} `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('RestLiteralBlockExtraSpace', () => { - const docstring = ` + test('RestLiteralBlockExtraSpace', () => { + const docstring = ` Take a look at this code:: @@ -362,7 +363,7 @@ Take a look at this code:: This text comes after. `; - const markdown = `Take a look at this code: + const markdown = `Take a look at this code: ${tripleTick} if foo: @@ -374,11 +375,11 @@ ${tripleTick} This text comes after. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('RestLiteralBlockNoIndentOneLiner', () => { - const docstring = ` + test('RestLiteralBlockNoIndentOneLiner', () => { + const docstring = ` The next code is a one-liner:: print(a + foo + 123) @@ -386,7 +387,7 @@ print(a + foo + 123) And now it's text. `; - const markdown = `The next code is a one-liner: + const markdown = `The next code is a one-liner: ${tripleTick} print(a + foo + 123) @@ -395,11 +396,11 @@ ${tripleTick} And now it's text. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('DirectiveRemoval', () => { - const docstring = `This is a test. + test('DirectiveRemoval', () => { + const docstring = `This is a test. .. ignoreme:: example @@ -416,25 +417,25 @@ This text is in-between. This text comes after. `; - const markdown = `This is a test. + const markdown = `This is a test. This text is in-between. This text comes after. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('ClassDirective', () => { - const docstring = ` + test('ClassDirective', () => { + const docstring = ` .. class:: FooBar() This is a description of ${doubleTick}FooBar${doubleTick}. ${doubleTick}FooBar${doubleTick} is interesting. `; - const markdown = `${tripleTick} + const markdown = `${tripleTick} FooBar() ${tripleTick} @@ -443,11 +444,11 @@ This is a description of ${singleTick}FooBar${singleTick}. ${singleTick}FooBar${singleTick} is interesting. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('CodeBlockDirective', () => { - const docstring = `Take a look at this + test('CodeBlockDirective', () => { + const docstring = `Take a look at this .. code-block:: Python if foo: @@ -458,7 +459,7 @@ test('CodeBlockDirective', () => { This text comes after. `; - const markdown = `Take a look at this + const markdown = `Take a look at this ${tripleTick} if foo: @@ -469,72 +470,72 @@ ${tripleTick} This text comes after.`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('UnfinishedBacktickBlock', () => { - const docstring = '```\nsomething\n'; + test('UnfinishedBacktickBlock', () => { + const docstring = '```\nsomething\n'; - const markdown = '```\nsomething\n```\n'; + const markdown = '```\nsomething\n```\n'; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('UnfinishedTildaBlock', () => { - const docstring = '~~~\nsomething\n'; + test('UnfinishedTildaBlock', () => { + const docstring = '~~~\nsomething\n'; - const markdown = '~~~\nsomething\n~~~\n'; + const markdown = '~~~\nsomething\n~~~\n'; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('UnfinishedInlineLiteral', () => { - const docstring = '`oops\n'; + test('UnfinishedInlineLiteral', () => { + const docstring = '`oops\n'; - const markdown = '`oops`'; + const markdown = '`oops`'; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('DashList', () => { - const docstring = ` + test('DashList', () => { + const docstring = ` This is a list: - Item 1 - Item 2 `; - const markdown = `This is a list: + const markdown = `This is a list: - Item 1 - Item 2 `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('AsteriskList', () => { - const docstring = ` + test('AsteriskList', () => { + const docstring = ` This is a list: * Item 1 * Item 2 `; - const markdown = `This is a list: + const markdown = `This is a list: * Item 1 * Item 2 `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('SquareBrackets', () => { - const docstring = 'Optional[List[str]]'; - const markdown = 'Optional\\[List\\[str\\]\\]'; + test('SquareBrackets', () => { + const docstring = 'Optional[List[str]]'; + const markdown = 'Optional\\[List\\[str\\]\\]'; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('ListDashMultiline', () => { - const docstring = `Keyword Arguments: + test('ListDashMultiline', () => { + const docstring = `Keyword Arguments: - option_strings -- A list of command-line option strings which should be associated with this action. @@ -542,18 +543,18 @@ test('ListDashMultiline', () => { - dest -- The name of the attribute to hold the created object(s) `; - const markdown = `Keyword Arguments: + const markdown = `Keyword Arguments: - option\\_strings -- A list of command-line option strings which should be associated with this action. - dest -- The name of the attribute to hold the created object(s)`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('HalfIndentOnLeadingDash', () => { - const docstring = `Dash List + test('HalfIndentOnLeadingDash', () => { + const docstring = `Dash List - foo - foo - bar @@ -562,7 +563,7 @@ test('HalfIndentOnLeadingDash', () => { - aaa `; - const markdown = `Dash List + const markdown = `Dash List - foo - foo - bar @@ -570,11 +571,11 @@ test('HalfIndentOnLeadingDash', () => { - qux - aaa`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('AsteriskMultilineList', () => { - const docstring = ` + test('AsteriskMultilineList', () => { + const docstring = ` This is a list: * this is a long, multi-line paragraph. It seems to go on and on. @@ -583,7 +584,7 @@ This is a list: seems to go on and on. `; - const markdown = `This is a list: + const markdown = `This is a list: * this is a long, multi-line paragraph. It seems to go on and on. @@ -591,27 +592,27 @@ seems to go on and on. seems to go on and on. `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('ListAsteriskAddLeadingSpace', () => { - const docstring = `Title + test('ListAsteriskAddLeadingSpace', () => { + const docstring = `Title * First line bullet, no leading space with second line. * Second line bullet, no leading space with second line.`; - const markdown = `Title + const markdown = `Title * First line bullet, no leading space with second line. * Second line bullet, no leading space with second line.`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('PandasReadCsvListIndent', () => { - const docstring = `Title + test('PandasReadCsvListIndent', () => { + const docstring = `Title keep_default_na : bool, default True Whether or not to include the default NaN values when parsing the data. @@ -620,7 +621,7 @@ keep_default_na : bool, default True na_filter : bool, default True`; - const markdown = `Title + const markdown = `Title keep\\_default\\_na : bool, default True     Whether or not to include the default NaN values when parsing the data. @@ -629,65 +630,65 @@ is appended to the default NaN values used for parsing. na\\_filter : bool, default True`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('FieldListEpyText', () => { - const docstring = ` + test('FieldListEpyText', () => { + const docstring = ` 1. Epytext: @param param1: description`; - const markdown = ` + const markdown = ` 1. Epytext:      @param param1: description`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('FieldListRest', () => { - const docstring = ` + test('FieldListRest', () => { + const docstring = ` 2. reST: :param param1: description`; - const markdown = ` + const markdown = ` 2. reST:      :param param1: description`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('FieldListGoogleV1', () => { - const docstring = ` + test('FieldListGoogleV1', () => { + const docstring = ` 3. Google (variant 1): Args: param1: description`; - const markdown = ` + const markdown = ` 3. Google (variant 1):      Args:          param1: description`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('FieldListGoogleV2', () => { - const docstring = ` + test('FieldListGoogleV2', () => { + const docstring = ` 4. Google (variant 2): Args: param1 (type): description param2 (type): description`; - const markdown = ` + const markdown = ` 4. Google (variant 2):      Args:          param1 (type): description          param2 (type): description`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('Googlewithreturntypes', () => { - const docstring = ` + test('Googlewithreturntypes', () => { + const docstring = ` Example function with types documented in the docstring. \`PEP 484\`_ type annotations are supported. If attribute, parameter, and @@ -704,7 +705,7 @@ test('Googlewithreturntypes', () => { .. _PEP 484: https://www.python.org/dev/peps/pep-0484/`; - const markdown = ` + const markdown = ` Example function with types documented in the docstring. \`PEP 484\`\\_ type annotations are supported. If attribute, parameter, and @@ -718,11 +719,11 @@ Args: Returns:     bool: The return value. True for success, False otherwise.`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('GoogleWithComplexTypes', () => { - const docstring = ` + test('GoogleWithComplexTypes', () => { + const docstring = ` Example function with types documented in the docstring. Args: @@ -733,7 +734,7 @@ test('GoogleWithComplexTypes', () => { bool: The return value. True for success, False otherwise. `; - const markdown = ` + const markdown = ` Example function with types documented in the docstring. Args: @@ -743,25 +744,25 @@ Args: Returns:     bool: The return value. True for success, False otherwise.`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('FieldListDontAddLineBreaksToHeaders', () => { - const docstring = ` + test('FieldListDontAddLineBreaksToHeaders', () => { + const docstring = ` Parameters ---------- ThisIsAFieldAfterAHeader : str`; - const markdown = ` + const markdown = ` Parameters ---------- ThisIsAFieldAfterAHeader : str`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('EpyDocCv2Imread', () => { - const docstring = `imread(filename[, flags]) -> retval + test('EpyDocCv2Imread', () => { + const docstring = `imread(filename[, flags]) -> retval . @brief Loads an image from a file. . @anchor imread . @@ -773,7 +774,7 @@ test('EpyDocCv2Imread', () => { . - Windows bitmaps - \\*.bmp, \\*.dib (always supported) . - JPEG files - \\*.jpeg, \\*.jpg, \\*.jpe (see the *Note* section)`; - const markdown = `imread(filename\\[, flags\\]) -> retval + const markdown = `imread(filename\\[, flags\\]) -> retval @brief Loads an image from a file. @@ -787,11 +788,11 @@ Currently, the following file formats are supported: - Windows bitmaps - \\*.bmp, \\*.dib (always supported) - JPEG files - \\*.jpeg, \\*.jpg, \\*.jpe (see the \\*Note\\* section)`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('Non EpyDocCv2Imread', () => { - const docstring = `imread(filename[, flags]) -> retval + test('Non EpyDocCv2Imread', () => { + const docstring = `imread(filename[, flags]) -> retval . @brief Loads an image from a file. . @anchor imread . @@ -803,7 +804,7 @@ test('Non EpyDocCv2Imread', () => { . - Windows bitmaps - \\*.bmp, \\*.dib (always supported) . - JPEG files - \\*.jpeg, \\*.jpg, \\*.jpe (see the *Note* section)`; - const markdown = `imread(filename\\[, flags\\]) -> retval + const markdown = `imread(filename\\[, flags\\]) -> retval . @brief Loads an image from a file. . @anchor imread . @@ -815,11 +816,11 @@ test('Non EpyDocCv2Imread', () => { . - Windows bitmaps - \\*.bmp, \\*.dib (always supported) . - JPEG files - \\*.jpeg, \\*.jpg, \\*.jpe (see the \\*Note\\* section)`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('EpyDocTest', () => { - const docstring = `Return the x intercept of the line M{y=m*x+b}. The X{x intercept} + test('EpyDocTest', () => { + const docstring = `Return the x intercept of the line M{y=m*x+b}. The X{x intercept} of a line is the point at which it crosses the x axis (M{y=0}). This function can be used in conjuction with L{z_transform} to @@ -833,7 +834,7 @@ find an arbitrary function's zeros. @rtype: number @return: the x intercept of the line M{y=m*x+b}.`; - const markdown = `Return the x intercept of the line M{y=m\\*x+b}. The X{x intercept} + const markdown = `Return the x intercept of the line M{y=m\\*x+b}. The X{x intercept} of a line is the point at which it crosses the x axis (M{y=0}). This function can be used in conjuction with L{z\\_transform} to @@ -852,27 +853,27 @@ find an arbitrary function's zeros. @return: the x intercept of the line M{y=m\\*x+b}.`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('DontEscapeHtmlTagsInsideCodeBlocks', () => { - const docstring = 'hello ``'; + test('DontEscapeHtmlTagsInsideCodeBlocks', () => { + const docstring = 'hello ``'; - const markdown = 'hello ``'; + const markdown = 'hello ``'; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('EscapeHtmlTagsOutsideCodeBlocks', () => { - const docstring = 'hello '; + test('EscapeHtmlTagsOutsideCodeBlocks', () => { + const docstring = 'hello '; - const markdown = 'hello <noncode>'; + const markdown = 'hello <noncode>'; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('IndentedCodeBlock', () => { - const docstring = ` + test('IndentedCodeBlock', () => { + const docstring = ` Expected: ${tripleTick}python def some_fn(): @@ -883,7 +884,7 @@ Expected: ${tripleTick} `; - const markdown = ` + const markdown = ` Expected: ${tripleTick}python def some_fn(): @@ -893,11 +894,11 @@ ${tripleTick}python """ ${tripleTick} `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('IndentedCodeBlockTilda', () => { - const docstring = ` + test('IndentedCodeBlockTilda', () => { + const docstring = ` Expected: ${tripleTilda}python def some_fn(): @@ -908,7 +909,7 @@ Expected: ${tripleTilda} `; - const markdown = ` + const markdown = ` Expected: ${tripleTilda}python def some_fn(): @@ -918,11 +919,11 @@ ${tripleTilda}python """ ${tripleTilda} `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('MixedCodeBlockBacktick', () => { - const docstring = ` + test('MixedCodeBlockBacktick', () => { + const docstring = ` Expected: ${tripleTick}python def some_fn(): @@ -941,7 +942,7 @@ Expected: ${tripleTilda} `; - const markdown = ` + const markdown = ` Expected: ${tripleTick}python def some_fn(): @@ -960,11 +961,11 @@ ${tripleTilda}python """ ${tripleTilda} `; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('RestTableWithHeader', () => { - const docstring = ` + test('RestTableWithHeader', () => { + const docstring = ` =============== ========================================================= Generator --------------- --------------------------------------------------------- @@ -972,7 +973,7 @@ Generator Class implementing all of the random number distributions default_rng Default constructor for \`\`Generator\`\` =============== =========================================================`; - const markdown = ` + const markdown = ` |Generator | | |---------------|---------------------------------------------------------| |Generator |Class implementing all of the random number distributions| @@ -980,11 +981,11 @@ default_rng Default constructor for \`\`Generator\`\`
`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('RestTablesMultilineHeader', () => { - const docstring = ` + test('RestTablesMultilineHeader', () => { + const docstring = ` ==================== ========================================================= Compatibility functions - removed @@ -993,18 +994,18 @@ in the new API rand Uniformly distributed values. ==================== =========================================================`; - const markdown = ` + const markdown = ` |Compatibility
functions - removed
in the new API |

| |--------------------|---------------------------------------------------------| |rand |Uniformly distributed values.|
`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('RestTableSimple', () => { - const docstring = ` + test('RestTableSimple', () => { + const docstring = ` ============================== ===================================== Scalar Type Array Type ============================== ===================================== @@ -1013,18 +1014,18 @@ Scalar Type Array Type ============================== ===================================== `; - const markdown = `|Scalar Type |Array Type | + const markdown = `|Scalar Type |Array Type | |------------------------------|-------------------------------------| |:class:\`pandas.Interval\` |:class:\`pandas.arrays.IntervalArray\`| |:class:\`pandas.Period\` |:class:\`pandas.arrays.PeriodArray\`|
`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test('ReSTTableIndented', () => { - const docstring = ` + test('ReSTTableIndented', () => { + const docstring = ` data : dtype : str, np.dtype, or ExtensionDtype, optional @@ -1036,7 +1037,7 @@ test('ReSTTableIndented', () => { :class:\`pandas.Period\` :class:\`pandas.arrays.PeriodArray\` ============================== =====================================`; - const markdown = ` + const markdown = ` data : dtype : str, np.dtype, or ExtensionDtype, optional @@ -1048,39 +1049,39 @@ dtype : str, np.dtype, or ExtensionDtype, optional
`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -test(`OddnumberOfColons`, () => { - const docstring = ` + test(`OddnumberOfColons`, () => { + const docstring = ` @param 'original:str' or 'original:list': original string to compare @param 'new:str': the new string to compare @return 'int': levenshtein difference @return 'list': levenshtein difference if list `; - const markdown = `@param 'original:str' or 'original:list': original string to compare\n\n@param 'new:str': the new string to compare\n\n@return 'int': levenshtein difference\n\n@return 'list': levenshtein difference if list`; + const markdown = `@param 'original:str' or 'original:list': original string to compare\n\n@param 'new:str': the new string to compare\n\n@return 'int': levenshtein difference\n\n@return 'list': levenshtein difference if list`; - _testConvertToMarkdown(docstring, markdown); -}); + _testConvertToMarkdown(docstring, markdown); + }); -function _testConvertToMarkdown(docstring: string, expectedMarkdown: string) { - const actualMarkdown = convertDocStringToMarkdown(docstring); + function _testConvertToMarkdown(docstring: string, expectedMarkdown: string) { + const actualMarkdown = docStringService.convertDocStringToMarkdown(docstring); - assert.equal(_normalizeLineEndings(actualMarkdown).trim(), _normalizeLineEndings(expectedMarkdown).trim()); -} + assert.equal(_normalizeLineEndings(actualMarkdown).trim(), _normalizeLineEndings(expectedMarkdown).trim()); + } -function _testConvertToPlainText(docstring: string, expectedPlainText: string) { - const actualMarkdown = convertDocStringToPlainText(docstring); + function _testConvertToPlainText(docstring: string, expectedPlainText: string) { + const actualMarkdown = docStringService.convertDocStringToPlainText(docstring); - assert.equal(_normalizeLineEndings(actualMarkdown).trim(), _normalizeLineEndings(expectedPlainText).trim()); -} + assert.equal(_normalizeLineEndings(actualMarkdown).trim(), _normalizeLineEndings(expectedPlainText).trim()); + } -function _normalizeLineEndings(text: string): string { - return text.split(/\r?\n/).join('\n'); -} + function _normalizeLineEndings(text: string): string { + return text.split(/\r?\n/).join('\n'); + } -test('RPYCLiteralBlockTransition', () => { - const docstring = ` + test('RPYCLiteralBlockTransition', () => { + const docstring = ` :: ##### ##### #### @@ -1096,7 +1097,7 @@ test('RPYCLiteralBlockTransition', () => { Remote Python Call (RPyC) `; - const markdown = ` + const markdown = ` ${tripleTick} ##### ##### #### @@ -1113,5 +1114,10 @@ ${tripleTick} Remote Python Call (RPyC) `; - _testConvertToMarkdown(docstring, markdown); + _testConvertToMarkdown(docstring, markdown); + }); +} + +describe('Doc String Conversion', () => { + docStringTests(); }); diff --git a/packages/pyright-internal/src/tests/docStringUtils.test.ts b/packages/pyright-internal/src/tests/docStringUtils.test.ts deleted file mode 100644 index d08eeda77..000000000 --- a/packages/pyright-internal/src/tests/docStringUtils.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * docStringUtils.test.ts - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT license. - * - * Unit tests for the docStringUtils.ts module. - */ - -import * as assert from 'assert'; - -import { cleanDocString } from '../analyzer/docStringUtils'; - -test('EmptyDocString', () => { - const input = ''; - const result = cleanDocString(input); - assert.strictEqual(result, ''); -}); - -test('OneLine', () => { - const input = 'Simple text'; - const result = cleanDocString(input); - assert.strictEqual(result, input); -}); - -test('OneLineLeftTrim', () => { - const input = 'Simple text'; - const inputWithSpaces = ' ' + input; - const result = cleanDocString(inputWithSpaces); - assert.strictEqual(result, input); -}); - -test('OneLineRightTrim', () => { - const input = 'Simple text'; - const inputWithSpaces = input + ' '; - const result = cleanDocString(inputWithSpaces); - assert.strictEqual(result, input); -}); - -test('OneLineTrimBoth', () => { - const input = 'Simple text'; - const inputWithSpaces = ' ' + input + ' '; - const result = cleanDocString(inputWithSpaces); - assert.strictEqual(result, input); -}); - -test('TwoLines', () => { - const input = 'Simple text'; - const inputWithSpaces = input + ' \n '; - const result = cleanDocString(inputWithSpaces); - assert.strictEqual(result, input); -}); - -test('TwoLinesIndentation', () => { - const input = 'Line 1 \n Line2 \n Line3\n Line4\n '; - const result = cleanDocString(input); - assert.strictEqual(result, 'Line 1\nLine2\n Line3\nLine4'); -}); diff --git a/packages/pyright-internal/src/tests/harness/fourslash/testState.ts b/packages/pyright-internal/src/tests/harness/fourslash/testState.ts index 7a7922fae..100b62151 100644 --- a/packages/pyright-internal/src/tests/harness/fourslash/testState.ts +++ b/packages/pyright-internal/src/tests/harness/fourslash/testState.ts @@ -97,6 +97,7 @@ import { import { TestFeatures, TestLanguageService } from './testLanguageService'; import { createVfsInfoFromFourSlashData, getMarkerByName, getMarkerName, getMarkerNames } from './testStateUtils'; import { verifyWorkspaceEdit } from './workspaceEditTestUtils'; +import { PyrightDocStringService } from '../../../common/docStringService'; export interface TextChange { span: TextRange; @@ -1126,6 +1127,7 @@ export class TestState { /* hasSignatureLabelOffsetCapability */ true, /* hasActiveParameterCapability */ true, /* context */ undefined, + new PyrightDocStringService(), CancellationToken.None ).getSignatureHelp(); diff --git a/packages/pyright-internal/src/tests/pathUtils.test.ts b/packages/pyright-internal/src/tests/pathUtils.test.ts index fefaeda7b..df18aa626 100644 --- a/packages/pyright-internal/src/tests/pathUtils.test.ts +++ b/packages/pyright-internal/src/tests/pathUtils.test.ts @@ -186,6 +186,13 @@ test('getWildcardRegexPattern4', () => { assert.ok(!regex.test(fixSeparators('//server/share/dix/foo.py'))); }); +test('getWildcardRegexPattern5', () => { + const pattern = getWildcardRegexPattern('//server/share/dir++', '.'); + const regex = new RegExp(pattern); + assert.ok(regex.test(fixSeparators('//server/share/dir++/foo.py'))); + assert.ok(!regex.test(fixSeparators('//server/share/dix++/foo.py'))); +}); + test('isDirectoryWildcardPatternPresent1', () => { const isPresent = isDirectoryWildcardPatternPresent('./**/*.py'); assert.equal(isPresent, true); diff --git a/packages/pyright-internal/src/tests/signatureHelp.test.ts b/packages/pyright-internal/src/tests/signatureHelp.test.ts index a2a9c6797..1d7843215 100644 --- a/packages/pyright-internal/src/tests/signatureHelp.test.ts +++ b/packages/pyright-internal/src/tests/signatureHelp.test.ts @@ -12,6 +12,7 @@ import { CancellationToken, MarkupKind } from 'vscode-languageserver'; import { convertOffsetToPosition } from '../common/positionUtils'; import { SignatureHelpProvider } from '../languageService/signatureHelpProvider'; import { parseAndGetTestState } from './harness/fourslash/testState'; +import { PyrightDocStringService } from '../common/docStringService'; test('invalid position in format string segment', () => { const code = ` @@ -88,6 +89,7 @@ function checkSignatureHelp(code: string, expects: boolean) { /*hasSignatureLabelOffsetCapability*/ true, /*hasActiveParameterCapability*/ true, /*context*/ undefined, + new PyrightDocStringService(), CancellationToken.None ).getSignatureHelp(); diff --git a/packages/pyright-internal/src/tests/uri.test.ts b/packages/pyright-internal/src/tests/uri.test.ts index 12c1c4c98..28a49ea5e 100644 --- a/packages/pyright-internal/src/tests/uri.test.ts +++ b/packages/pyright-internal/src/tests/uri.test.ts @@ -674,6 +674,13 @@ test('getWildcardRegexPattern4', () => { assert.ok(!regex.test('//server/share/dix/foo.py')); }); +test('getWildcardRegexPattern4', () => { + const pattern = getWildcardRegexPattern(Uri.parse('//server/share/dir++/.bar*/bid', caseDetector), '.'); + const regex = new RegExp(pattern); + assert.ok(regex.test('//server/share/dir++/.bar*/bidfoo.py')); + assert.ok(!regex.test('//server/share/dix++/.bar*/bidfoo.py')); +}); + test('getWildcardRoot1', () => { const p = getWildcardRoot(Uri.parse('foo:/users/me', caseDetector), './blah/'); assert.equal(p.toString(), 'foo:/users/me/blah'); diff --git a/packages/pyright-internal/src/workspaceFactory.ts b/packages/pyright-internal/src/workspaceFactory.ts index 865723028..c756726c5 100644 --- a/packages/pyright-internal/src/workspaceFactory.ts +++ b/packages/pyright-internal/src/workspaceFactory.ts @@ -10,7 +10,7 @@ import { AnalyzerService } from './analyzer/service'; import { ConsoleInterface } from './common/console'; import { createDeferred } from './common/deferred'; import { Uri } from './common/uri/uri'; -import { ServiceProvider } from './common/extensibility'; +import { ServiceProvider } from './common/serviceProvider'; let WorkspaceFactoryIdCounter = 0;