mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-17 11:17:17 +03:00
Added literal type support in more places, fixed using wrong console bug and some refactoring around Host environment (#2176)
This commit is contained in:
parent
bf8b5511d3
commit
abd41b7273
@ -3,7 +3,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "npm run bootstrap",
|
"postinstall": "npm run bootstrap",
|
||||||
"bootstrap": "node ./build/skipBootstrap.js || lerna bootstrap --no-ci",
|
"bootstrap": "node ./build/skipBootstrap.js || lerna bootstrap",
|
||||||
"clean": "lerna run --no-bail --stream clean",
|
"clean": "lerna run --no-bail --stream clean",
|
||||||
"install:all": "npm install && lerna exec --no-bail npm install",
|
"install:all": "npm install && lerna exec --no-bail npm install",
|
||||||
"update:all": "node ./build/updateDeps.js",
|
"update:all": "node ./build/updateDeps.js",
|
||||||
|
@ -58,6 +58,10 @@ export class BackgroundAnalysisProgram {
|
|||||||
return this._program;
|
return this._program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get host() {
|
||||||
|
return this._importResolver.host;
|
||||||
|
}
|
||||||
|
|
||||||
get backgroundAnalysis() {
|
get backgroundAnalysis() {
|
||||||
return this._backgroundAnalysis;
|
return this._backgroundAnalysis;
|
||||||
}
|
}
|
||||||
@ -70,14 +74,10 @@ export class BackgroundAnalysisProgram {
|
|||||||
|
|
||||||
setImportResolver(importResolver: ImportResolver) {
|
setImportResolver(importResolver: ImportResolver) {
|
||||||
this._importResolver = importResolver;
|
this._importResolver = importResolver;
|
||||||
|
this._backgroundAnalysis?.setImportResolver(importResolver);
|
||||||
|
|
||||||
this._program.setImportResolver(importResolver);
|
this._program.setImportResolver(importResolver);
|
||||||
|
|
||||||
this._configOptions.getExecutionEnvironments().forEach((e) => this._ensurePartialStubPackages(e));
|
this._configOptions.getExecutionEnvironments().forEach((e) => this._ensurePartialStubPackages(e));
|
||||||
|
|
||||||
// Do nothing for background analysis.
|
|
||||||
// Background analysis updates importer when configOptions is changed rather than
|
|
||||||
// having two APIs to reduce the chance of the program and importer pointing to
|
|
||||||
// two different configOptions.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTrackedFiles(filePaths: string[]) {
|
setTrackedFiles(filePaths: string[]) {
|
||||||
@ -165,7 +165,7 @@ export class BackgroundAnalysisProgram {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._backgroundAnalysis?.startIndexing(this._configOptions, this._getIndices());
|
this._backgroundAnalysis?.startIndexing(this._configOptions, this.host.kind, this._getIndices());
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshIndexing() {
|
refreshIndexing() {
|
||||||
@ -173,7 +173,7 @@ export class BackgroundAnalysisProgram {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._backgroundAnalysis?.refreshIndexing(this._configOptions, this._indices);
|
this._backgroundAnalysis?.refreshIndexing(this._configOptions, this.host.kind, this._indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelIndexing() {
|
cancelIndexing() {
|
||||||
|
@ -13,6 +13,7 @@ import type { Dirent } from 'fs';
|
|||||||
import { getOrAdd } from '../common/collectionUtils';
|
import { getOrAdd } from '../common/collectionUtils';
|
||||||
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
||||||
import { FileSystem } from '../common/fileSystem';
|
import { FileSystem } from '../common/fileSystem';
|
||||||
|
import { Host } from '../common/host';
|
||||||
import { stubsSuffix } from '../common/pathConsts';
|
import { stubsSuffix } from '../common/pathConsts';
|
||||||
import {
|
import {
|
||||||
changeAnyExtension,
|
changeAnyExtension,
|
||||||
@ -74,8 +75,6 @@ export const supportedFileExtensions = ['.py', '.pyi', ...supportedNativeLibExte
|
|||||||
const allowPartialResolutionForThirdPartyPackages = false;
|
const allowPartialResolutionForThirdPartyPackages = false;
|
||||||
|
|
||||||
export class ImportResolver {
|
export class ImportResolver {
|
||||||
protected _configOptions: ConfigOptions;
|
|
||||||
|
|
||||||
private _cachedPythonSearchPaths = new Map<string, string[]>();
|
private _cachedPythonSearchPaths = new Map<string, string[]>();
|
||||||
private _cachedImportResults = new Map<string, CachedImportResults>();
|
private _cachedImportResults = new Map<string, CachedImportResults>();
|
||||||
private _cachedModuleNameResults = new Map<string, Map<string, ModuleNameAndType>>();
|
private _cachedModuleNameResults = new Map<string, Map<string, ModuleNameAndType>>();
|
||||||
@ -87,12 +86,11 @@ export class ImportResolver {
|
|||||||
private _cachedTypeshedThirdPartyPackageRoots: string[] | undefined;
|
private _cachedTypeshedThirdPartyPackageRoots: string[] | undefined;
|
||||||
private _cachedEntriesForPath = new Map<string, Dirent[]>();
|
private _cachedEntriesForPath = new Map<string, Dirent[]>();
|
||||||
|
|
||||||
readonly fileSystem: FileSystem;
|
constructor(
|
||||||
|
public readonly fileSystem: FileSystem,
|
||||||
constructor(fs: FileSystem, configOptions: ConfigOptions) {
|
protected _configOptions: ConfigOptions,
|
||||||
this.fileSystem = fs;
|
public readonly host: Host
|
||||||
this._configOptions = configOptions;
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCache() {
|
invalidateCache() {
|
||||||
this._cachedPythonSearchPaths = new Map<string, string[]>();
|
this._cachedPythonSearchPaths = new Map<string, string[]>();
|
||||||
@ -1233,7 +1231,12 @@ export class ImportResolver {
|
|||||||
// Find the site packages for the configured virtual environment.
|
// Find the site packages for the configured virtual environment.
|
||||||
if (!this._cachedPythonSearchPaths.has(cacheKey)) {
|
if (!this._cachedPythonSearchPaths.has(cacheKey)) {
|
||||||
let paths = (
|
let paths = (
|
||||||
PythonPathUtils.findPythonSearchPaths(this.fileSystem, this._configOptions, importFailureInfo) || []
|
PythonPathUtils.findPythonSearchPaths(
|
||||||
|
this.fileSystem,
|
||||||
|
this._configOptions,
|
||||||
|
this.host,
|
||||||
|
importFailureInfo
|
||||||
|
) || []
|
||||||
).map((p) => this.fileSystem.realCasePath(p));
|
).map((p) => this.fileSystem.realCasePath(p));
|
||||||
|
|
||||||
// Remove duplicates (yes, it happens).
|
// Remove duplicates (yes, it happens).
|
||||||
@ -1892,4 +1895,4 @@ export class ImportResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ImportResolverFactory = (fs: FileSystem, options: ConfigOptions) => ImportResolver;
|
export type ImportResolverFactory = (fs: FileSystem, options: ConfigOptions, host: Host) => ImportResolver;
|
||||||
|
@ -12,6 +12,7 @@ import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
|||||||
import { assert } from '../common/debug';
|
import { assert } from '../common/debug';
|
||||||
import { Diagnostic, DiagnosticAddendum, DiagnosticCategory } from '../common/diagnostic';
|
import { Diagnostic, DiagnosticAddendum, DiagnosticCategory } from '../common/diagnostic';
|
||||||
import { FileSystem } from '../common/fileSystem';
|
import { FileSystem } from '../common/fileSystem';
|
||||||
|
import { FullAccessHost } from '../common/fullAccessHost';
|
||||||
import { combinePaths, getDirectoryPath, getFileExtension, stripFileExtension, tryStat } from '../common/pathUtils';
|
import { combinePaths, getDirectoryPath, getFileExtension, stripFileExtension, tryStat } from '../common/pathUtils';
|
||||||
import { getEmptyRange, Range } from '../common/textRange';
|
import { getEmptyRange, Range } from '../common/textRange';
|
||||||
import { DeclarationType, FunctionDeclaration, VariableDeclaration } from './declaration';
|
import { DeclarationType, FunctionDeclaration, VariableDeclaration } from './declaration';
|
||||||
@ -55,7 +56,11 @@ export class PackageTypeVerifier {
|
|||||||
constructor(private _fileSystem: FileSystem) {
|
constructor(private _fileSystem: FileSystem) {
|
||||||
this._configOptions = new ConfigOptions('');
|
this._configOptions = new ConfigOptions('');
|
||||||
this._execEnv = this._configOptions.findExecEnvironment('.');
|
this._execEnv = this._configOptions.findExecEnvironment('.');
|
||||||
this._importResolver = new ImportResolver(this._fileSystem, this._configOptions);
|
this._importResolver = new ImportResolver(
|
||||||
|
this._fileSystem,
|
||||||
|
this._configOptions,
|
||||||
|
new FullAccessHost(this._fileSystem)
|
||||||
|
);
|
||||||
this._program = new Program(this._importResolver, this._configOptions);
|
this._program = new Program(this._importResolver, this._configOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,11 +7,10 @@
|
|||||||
* Utility routines used to resolve various paths in python.
|
* Utility routines used to resolve various paths in python.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as child_process from 'child_process';
|
|
||||||
|
|
||||||
import { ConfigOptions } from '../common/configOptions';
|
import { ConfigOptions } from '../common/configOptions';
|
||||||
import { compareComparableValues } from '../common/core';
|
import { compareComparableValues } from '../common/core';
|
||||||
import { FileSystem } from '../common/fileSystem';
|
import { FileSystem } from '../common/fileSystem';
|
||||||
|
import { Host } from '../common/host';
|
||||||
import * as pathConsts from '../common/pathConsts';
|
import * as pathConsts from '../common/pathConsts';
|
||||||
import {
|
import {
|
||||||
combinePaths,
|
combinePaths,
|
||||||
@ -24,20 +23,11 @@ import {
|
|||||||
tryStat,
|
tryStat,
|
||||||
} from '../common/pathUtils';
|
} from '../common/pathUtils';
|
||||||
|
|
||||||
interface PythonPathResult {
|
export interface PythonPathResult {
|
||||||
paths: string[];
|
paths: string[];
|
||||||
prefix: string;
|
prefix: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractSys = [
|
|
||||||
'import os, os.path, sys',
|
|
||||||
'normalize = lambda p: os.path.normcase(os.path.normpath(p))',
|
|
||||||
'cwd = normalize(os.getcwd())',
|
|
||||||
'sys.path[:] = [p for p in sys.path if p != "" and normalize(p) != cwd]',
|
|
||||||
'import json',
|
|
||||||
'json.dump(dict(path=sys.path, prefix=sys.prefix), sys.stdout)',
|
|
||||||
].join('; ');
|
|
||||||
|
|
||||||
export const stdLibFolderName = 'stdlib';
|
export const stdLibFolderName = 'stdlib';
|
||||||
export const thirdPartyFolderName = 'stubs';
|
export const thirdPartyFolderName = 'stubs';
|
||||||
|
|
||||||
@ -71,6 +61,7 @@ export function getTypeshedSubdirectory(typeshedPath: string, isStdLib: boolean)
|
|||||||
export function findPythonSearchPaths(
|
export function findPythonSearchPaths(
|
||||||
fs: FileSystem,
|
fs: FileSystem,
|
||||||
configOptions: ConfigOptions,
|
configOptions: ConfigOptions,
|
||||||
|
host: Host,
|
||||||
importFailureInfo: string[],
|
importFailureInfo: string[],
|
||||||
includeWatchPathsOnly?: boolean | undefined,
|
includeWatchPathsOnly?: boolean | undefined,
|
||||||
workspaceRoot?: string | undefined
|
workspaceRoot?: string | undefined
|
||||||
@ -114,7 +105,7 @@ export function findPythonSearchPaths(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fall back on the python interpreter.
|
// Fall back on the python interpreter.
|
||||||
const pathResult = getPythonPathFromPythonInterpreter(fs, configOptions.pythonPath, importFailureInfo);
|
const pathResult = host.getPythonSearchPaths(configOptions.pythonPath, importFailureInfo);
|
||||||
if (includeWatchPathsOnly && workspaceRoot) {
|
if (includeWatchPathsOnly && workspaceRoot) {
|
||||||
const paths = pathResult.paths.filter(
|
const paths = pathResult.paths.filter(
|
||||||
(p) => !containsPath(workspaceRoot, p, true) || containsPath(pathResult.prefix, p, true)
|
(p) => !containsPath(workspaceRoot, p, true) || containsPath(pathResult.prefix, p, true)
|
||||||
@ -126,44 +117,6 @@ export function findPythonSearchPaths(
|
|||||||
return pathResult.paths;
|
return pathResult.paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPythonPathFromPythonInterpreter(
|
|
||||||
fs: FileSystem,
|
|
||||||
interpreterPath: string | undefined,
|
|
||||||
importFailureInfo: string[]
|
|
||||||
): PythonPathResult {
|
|
||||||
let result: PythonPathResult | undefined;
|
|
||||||
|
|
||||||
if (interpreterPath) {
|
|
||||||
result = getPathResultFromInterpreter(fs, interpreterPath, importFailureInfo);
|
|
||||||
} else {
|
|
||||||
// On non-Windows platforms, always default to python3 first. We want to
|
|
||||||
// avoid this on Windows because it might invoke a script that displays
|
|
||||||
// a dialog box indicating that python can be downloaded from the app store.
|
|
||||||
if (process.platform !== 'win32') {
|
|
||||||
result = getPathResultFromInterpreter(fs, 'python3', importFailureInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// On some platforms, 'python3' might not exist. Try 'python' instead.
|
|
||||||
if (!result) {
|
|
||||||
result = getPathResultFromInterpreter(fs, 'python', importFailureInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
result = {
|
|
||||||
paths: [],
|
|
||||||
prefix: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
importFailureInfo.push(`Received ${result.paths.length} paths from interpreter`);
|
|
||||||
result.paths.forEach((path) => {
|
|
||||||
importFailureInfo.push(` ${path}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isPythonBinary(p: string): boolean {
|
export function isPythonBinary(p: string): boolean {
|
||||||
p = p.trim();
|
p = p.trim();
|
||||||
return p === 'python' || p === 'python3';
|
return p === 'python' || p === 'python3';
|
||||||
@ -204,54 +157,6 @@ function findSitePackagesPath(fs: FileSystem, libPath: string, importFailureInfo
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPathResultFromInterpreter(
|
|
||||||
fs: FileSystem,
|
|
||||||
interpreter: string,
|
|
||||||
importFailureInfo: string[]
|
|
||||||
): PythonPathResult | undefined {
|
|
||||||
const result: PythonPathResult = {
|
|
||||||
paths: [],
|
|
||||||
prefix: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const commandLineArgs: string[] = ['-c', extractSys];
|
|
||||||
|
|
||||||
importFailureInfo.push(`Executing interpreter: '${interpreter}'`);
|
|
||||||
const execOutput = child_process.execFileSync(interpreter, commandLineArgs, { encoding: 'utf8' });
|
|
||||||
|
|
||||||
// Parse the execOutput. It should be a JSON-encoded array of paths.
|
|
||||||
try {
|
|
||||||
const execSplit = JSON.parse(execOutput);
|
|
||||||
for (let execSplitEntry of execSplit.path) {
|
|
||||||
execSplitEntry = execSplitEntry.trim();
|
|
||||||
if (execSplitEntry) {
|
|
||||||
const normalizedPath = normalizePath(execSplitEntry);
|
|
||||||
// Skip non-existent paths and broken zips/eggs.
|
|
||||||
if (fs.existsSync(normalizedPath) && isDirectory(fs, normalizedPath)) {
|
|
||||||
result.paths.push(normalizedPath);
|
|
||||||
} else {
|
|
||||||
importFailureInfo.push(`Skipping '${normalizedPath}' because it is not a valid directory`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.prefix = execSplit.prefix;
|
|
||||||
|
|
||||||
if (result.paths.length === 0) {
|
|
||||||
importFailureInfo.push(`Found no valid directories`);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
importFailureInfo.push(`Could not parse output: '${execOutput}'`);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPathsFromPthFiles(fs: FileSystem, parentDir: string): string[] {
|
function getPathsFromPthFiles(fs: FileSystem, parentDir: string): string[] {
|
||||||
const searchPaths: string[] = [];
|
const searchPaths: string[] = [];
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ import { Diagnostic } from '../common/diagnostic';
|
|||||||
import { FileEditAction, TextEditAction } from '../common/editAction';
|
import { FileEditAction, TextEditAction } from '../common/editAction';
|
||||||
import { LanguageServiceExtension } from '../common/extensibility';
|
import { LanguageServiceExtension } from '../common/extensibility';
|
||||||
import { FileSystem, FileWatcher, ignoredWatchEventFunction } from '../common/fileSystem';
|
import { FileSystem, FileWatcher, ignoredWatchEventFunction } from '../common/fileSystem';
|
||||||
|
import { Host, HostFactory, NoAccessHost } from '../common/host';
|
||||||
import {
|
import {
|
||||||
combinePaths,
|
combinePaths,
|
||||||
FileSpec,
|
FileSpec,
|
||||||
@ -75,6 +76,7 @@ const _gitDirectory = normalizeSlashes('/.git/');
|
|||||||
const _includeFileRegex = /\.pyi?$/;
|
const _includeFileRegex = /\.pyi?$/;
|
||||||
|
|
||||||
export class AnalyzerService {
|
export class AnalyzerService {
|
||||||
|
private _hostFactory: HostFactory;
|
||||||
private _instanceName: string;
|
private _instanceName: string;
|
||||||
private _importResolverFactory: ImportResolverFactory;
|
private _importResolverFactory: ImportResolverFactory;
|
||||||
private _executionRootPath: string;
|
private _executionRootPath: string;
|
||||||
@ -104,6 +106,7 @@ export class AnalyzerService {
|
|||||||
instanceName: string,
|
instanceName: string,
|
||||||
fs: FileSystem,
|
fs: FileSystem,
|
||||||
console?: ConsoleInterface,
|
console?: ConsoleInterface,
|
||||||
|
hostFactory?: HostFactory,
|
||||||
importResolverFactory?: ImportResolverFactory,
|
importResolverFactory?: ImportResolverFactory,
|
||||||
configOptions?: ConfigOptions,
|
configOptions?: ConfigOptions,
|
||||||
extension?: LanguageServiceExtension,
|
extension?: LanguageServiceExtension,
|
||||||
@ -120,9 +123,10 @@ export class AnalyzerService {
|
|||||||
this._maxAnalysisTimeInForeground = maxAnalysisTime;
|
this._maxAnalysisTimeInForeground = maxAnalysisTime;
|
||||||
this._backgroundAnalysisProgramFactory = backgroundAnalysisProgramFactory;
|
this._backgroundAnalysisProgramFactory = backgroundAnalysisProgramFactory;
|
||||||
this._cancellationProvider = cancellationProvider ?? new DefaultCancellationProvider();
|
this._cancellationProvider = cancellationProvider ?? new DefaultCancellationProvider();
|
||||||
|
this._hostFactory = hostFactory ?? (() => new NoAccessHost());
|
||||||
|
|
||||||
configOptions = configOptions ?? new ConfigOptions(process.cwd());
|
configOptions = configOptions ?? new ConfigOptions(process.cwd());
|
||||||
const importResolver = this._importResolverFactory(fs, configOptions);
|
const importResolver = this._importResolverFactory(fs, configOptions, this._hostFactory());
|
||||||
|
|
||||||
this._backgroundAnalysisProgram =
|
this._backgroundAnalysisProgram =
|
||||||
backgroundAnalysisProgramFactory !== undefined
|
backgroundAnalysisProgramFactory !== undefined
|
||||||
@ -149,6 +153,7 @@ export class AnalyzerService {
|
|||||||
instanceName,
|
instanceName,
|
||||||
this._fs,
|
this._fs,
|
||||||
this._console,
|
this._console,
|
||||||
|
this._hostFactory,
|
||||||
this._importResolverFactory,
|
this._importResolverFactory,
|
||||||
this._backgroundAnalysisProgram.configOptions,
|
this._backgroundAnalysisProgram.configOptions,
|
||||||
this._extension,
|
this._extension,
|
||||||
@ -173,8 +178,8 @@ export class AnalyzerService {
|
|||||||
return this._backgroundAnalysisProgram;
|
return this._backgroundAnalysisProgram;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createImportResolver(fs: FileSystem, options: ConfigOptions): ImportResolver {
|
static createImportResolver(fs: FileSystem, options: ConfigOptions, host: Host): ImportResolver {
|
||||||
return new ImportResolver(fs, options);
|
return new ImportResolver(fs, options, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCompletionCallback(callback: AnalysisCompleteCallback | undefined): void {
|
setCompletionCallback(callback: AnalysisCompleteCallback | undefined): void {
|
||||||
@ -185,21 +190,22 @@ export class AnalyzerService {
|
|||||||
setOptions(commandLineOptions: CommandLineOptions, reanalyze = true): void {
|
setOptions(commandLineOptions: CommandLineOptions, reanalyze = true): void {
|
||||||
this._commandLineOptions = commandLineOptions;
|
this._commandLineOptions = commandLineOptions;
|
||||||
|
|
||||||
const configOptions = this._getConfigOptions(commandLineOptions);
|
const host = this._hostFactory();
|
||||||
|
const configOptions = this._getConfigOptions(host, commandLineOptions);
|
||||||
|
|
||||||
if (configOptions.pythonPath) {
|
if (configOptions.pythonPath) {
|
||||||
// Make sure we have default python environment set.
|
// Make sure we have default python environment set.
|
||||||
configOptions.ensureDefaultPythonVersion(configOptions.pythonPath, this._console);
|
configOptions.ensureDefaultPythonVersion(host, this._console);
|
||||||
}
|
}
|
||||||
|
|
||||||
configOptions.ensureDefaultPythonPlatform(this._console);
|
configOptions.ensureDefaultPythonPlatform(host, this._console);
|
||||||
|
|
||||||
this._backgroundAnalysisProgram.setConfigOptions(configOptions);
|
this._backgroundAnalysisProgram.setConfigOptions(configOptions);
|
||||||
|
|
||||||
this._executionRootPath = normalizePath(
|
this._executionRootPath = normalizePath(
|
||||||
combinePaths(commandLineOptions.executionRoot, configOptions.projectRoot)
|
combinePaths(commandLineOptions.executionRoot, configOptions.projectRoot)
|
||||||
);
|
);
|
||||||
this._applyConfigOptions(reanalyze);
|
this._applyConfigOptions(host, reanalyze);
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileOpened(path: string, version: number | null, contents: string) {
|
setFileOpened(path: string, version: number | null, contents: string) {
|
||||||
@ -429,7 +435,7 @@ export class AnalyzerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test_getConfigOptions(commandLineOptions: CommandLineOptions): ConfigOptions {
|
test_getConfigOptions(commandLineOptions: CommandLineOptions): ConfigOptions {
|
||||||
return this._getConfigOptions(commandLineOptions);
|
return this._getConfigOptions(this._backgroundAnalysisProgram.host, commandLineOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
test_getFileNamesFromFileSpecs(): string[] {
|
test_getFileNamesFromFileSpecs(): string[] {
|
||||||
@ -438,7 +444,7 @@ export class AnalyzerService {
|
|||||||
|
|
||||||
// Calculates the effective options based on the command-line options,
|
// Calculates the effective options based on the command-line options,
|
||||||
// an optional config file, and default values.
|
// an optional config file, and default values.
|
||||||
private _getConfigOptions(commandLineOptions: CommandLineOptions): ConfigOptions {
|
private _getConfigOptions(host: Host, commandLineOptions: CommandLineOptions): ConfigOptions {
|
||||||
let projectRoot = commandLineOptions.executionRoot;
|
let projectRoot = commandLineOptions.executionRoot;
|
||||||
let configFilePath: string | undefined;
|
let configFilePath: string | undefined;
|
||||||
let pyprojectFilePath: string | undefined;
|
let pyprojectFilePath: string | undefined;
|
||||||
@ -504,6 +510,13 @@ export class AnalyzerService {
|
|||||||
const configOptions = new ConfigOptions(projectRoot, this._typeCheckingMode);
|
const configOptions = new ConfigOptions(projectRoot, this._typeCheckingMode);
|
||||||
const defaultExcludes = ['**/node_modules', '**/__pycache__', '.git'];
|
const defaultExcludes = ['**/node_modules', '**/__pycache__', '.git'];
|
||||||
|
|
||||||
|
if (commandLineOptions.pythonPath) {
|
||||||
|
this._console.info(
|
||||||
|
`Setting pythonPath for service "${this._instanceName}": ` + `"${commandLineOptions.pythonPath}"`
|
||||||
|
);
|
||||||
|
configOptions.pythonPath = commandLineOptions.pythonPath;
|
||||||
|
}
|
||||||
|
|
||||||
// The pythonPlatform and pythonVersion from the command-line can be overridden
|
// The pythonPlatform and pythonVersion from the command-line can be overridden
|
||||||
// by the config file, so initialize them upfront.
|
// by the config file, so initialize them upfront.
|
||||||
configOptions.defaultPythonPlatform = commandLineOptions.pythonPlatform;
|
configOptions.defaultPythonPlatform = commandLineOptions.pythonPlatform;
|
||||||
@ -549,8 +562,8 @@ export class AnalyzerService {
|
|||||||
configJsonObj,
|
configJsonObj,
|
||||||
this._typeCheckingMode,
|
this._typeCheckingMode,
|
||||||
this._console,
|
this._console,
|
||||||
|
host,
|
||||||
commandLineOptions.diagnosticSeverityOverrides,
|
commandLineOptions.diagnosticSeverityOverrides,
|
||||||
commandLineOptions.pythonPath,
|
|
||||||
commandLineOptions.fileSpecs.length > 0
|
commandLineOptions.fileSpecs.length > 0
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -601,13 +614,6 @@ export class AnalyzerService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commandLineOptions.pythonPath) {
|
|
||||||
this._console.info(
|
|
||||||
`Setting pythonPath for service "${this._instanceName}": ` + `"${commandLineOptions.pythonPath}"`
|
|
||||||
);
|
|
||||||
configOptions.pythonPath = commandLineOptions.pythonPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandLineOptions.typeshedPath) {
|
if (commandLineOptions.typeshedPath) {
|
||||||
if (!configOptions.typeshedPath) {
|
if (!configOptions.typeshedPath) {
|
||||||
configOptions.typeshedPath = commandLineOptions.typeshedPath;
|
configOptions.typeshedPath = commandLineOptions.typeshedPath;
|
||||||
@ -664,7 +670,7 @@ export class AnalyzerService {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const importFailureInfo: string[] = [];
|
const importFailureInfo: string[] = [];
|
||||||
if (findPythonSearchPaths(this._fs, configOptions, importFailureInfo) === undefined) {
|
if (findPythonSearchPaths(this._fs, configOptions, host, importFailureInfo) === undefined) {
|
||||||
this._console.error(
|
this._console.error(
|
||||||
`site-packages directory cannot be located for venvPath ` +
|
`site-packages directory cannot be located for venvPath ` +
|
||||||
`${configOptions.venvPath} and venv ${configOptions.venv}.`
|
`${configOptions.venvPath} and venv ${configOptions.venv}.`
|
||||||
@ -738,7 +744,7 @@ export class AnalyzerService {
|
|||||||
// Forces the service to stop all analysis, discard all its caches,
|
// Forces the service to stop all analysis, discard all its caches,
|
||||||
// and research for files.
|
// and research for files.
|
||||||
restart() {
|
restart() {
|
||||||
this._applyConfigOptions();
|
this._applyConfigOptions(this._hostFactory());
|
||||||
|
|
||||||
this._backgroundAnalysisProgram.restart();
|
this._backgroundAnalysisProgram.restart();
|
||||||
}
|
}
|
||||||
@ -1218,6 +1224,7 @@ export class AnalyzerService {
|
|||||||
const watchList = findPythonSearchPaths(
|
const watchList = findPythonSearchPaths(
|
||||||
this._fs,
|
this._fs,
|
||||||
this._backgroundAnalysisProgram.configOptions,
|
this._backgroundAnalysisProgram.configOptions,
|
||||||
|
this._backgroundAnalysisProgram.host,
|
||||||
importFailureInfo,
|
importFailureInfo,
|
||||||
true,
|
true,
|
||||||
this._executionRootPath
|
this._executionRootPath
|
||||||
@ -1339,19 +1346,26 @@ export class AnalyzerService {
|
|||||||
if (this._configFilePath) {
|
if (this._configFilePath) {
|
||||||
this._console.info(`Reloading configuration file at ${this._configFilePath}`);
|
this._console.info(`Reloading configuration file at ${this._configFilePath}`);
|
||||||
|
|
||||||
|
const host = this._backgroundAnalysisProgram.host;
|
||||||
|
|
||||||
// We can't just reload config file when it is changed; we need to consider
|
// We can't just reload config file when it is changed; we need to consider
|
||||||
// command line options as well to construct new config Options.
|
// command line options as well to construct new config Options.
|
||||||
const configOptions = this._getConfigOptions(this._commandLineOptions!);
|
const configOptions = this._getConfigOptions(host, this._commandLineOptions!);
|
||||||
this._backgroundAnalysisProgram.setConfigOptions(configOptions);
|
this._backgroundAnalysisProgram.setConfigOptions(configOptions);
|
||||||
|
|
||||||
this._applyConfigOptions();
|
this._applyConfigOptions(host);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyConfigOptions(reanalyze = true) {
|
private _applyConfigOptions(host: Host, reanalyze = true) {
|
||||||
// Allocate a new import resolver because the old one has information
|
// Allocate a new import resolver because the old one has information
|
||||||
// cached based on the previous config options.
|
// cached based on the previous config options.
|
||||||
const importResolver = this._importResolverFactory(this._fs, this._backgroundAnalysisProgram.configOptions);
|
const importResolver = this._importResolverFactory(
|
||||||
|
this._fs,
|
||||||
|
this._backgroundAnalysisProgram.configOptions,
|
||||||
|
host
|
||||||
|
);
|
||||||
|
|
||||||
this._backgroundAnalysisProgram.setImportResolver(importResolver);
|
this._backgroundAnalysisProgram.setImportResolver(importResolver);
|
||||||
|
|
||||||
if (this._commandLineOptions?.fromVsCodeExtension || this._configOptions.verboseOutput) {
|
if (this._commandLineOptions?.fromVsCodeExtension || this._configOptions.verboseOutput) {
|
||||||
|
@ -29,6 +29,7 @@ import {
|
|||||||
import { doForEachSubtype, isOptionalType, isTupleClass } from './typeUtils';
|
import { doForEachSubtype, isOptionalType, isTupleClass } from './typeUtils';
|
||||||
|
|
||||||
const singleTickRegEx = /'/g;
|
const singleTickRegEx = /'/g;
|
||||||
|
const escapedDoubleQuoteRegEx = /\\"/g;
|
||||||
|
|
||||||
export const enum PrintTypeFlags {
|
export const enum PrintTypeFlags {
|
||||||
None = 0,
|
None = 0,
|
||||||
@ -371,7 +372,7 @@ export function printType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function printLiteralValue(type: ClassType): string {
|
export function printLiteralValue(type: ClassType, quotation = "'"): string {
|
||||||
const literalValue = type.literalValue;
|
const literalValue = type.literalValue;
|
||||||
if (literalValue === undefined) {
|
if (literalValue === undefined) {
|
||||||
return '';
|
return '';
|
||||||
@ -380,8 +381,20 @@ export function printLiteralValue(type: ClassType): string {
|
|||||||
let literalStr: string;
|
let literalStr: string;
|
||||||
if (typeof literalValue === 'string') {
|
if (typeof literalValue === 'string') {
|
||||||
const prefix = type.details.name === 'bytes' ? 'b' : '';
|
const prefix = type.details.name === 'bytes' ? 'b' : '';
|
||||||
|
|
||||||
|
// JSON.stringify will perform proper escaping for " case.
|
||||||
|
// So, we only need to do our own escaping for ' case.
|
||||||
literalStr = JSON.stringify(literalValue).toString();
|
literalStr = JSON.stringify(literalValue).toString();
|
||||||
literalStr = `${prefix}'${literalStr.substring(1, literalStr.length - 1).replace(singleTickRegEx, "\\'")}'`;
|
if (quotation !== '"') {
|
||||||
|
literalStr = `'${literalStr
|
||||||
|
.substring(1, literalStr.length - 1)
|
||||||
|
.replace(escapedDoubleQuoteRegEx, '"')
|
||||||
|
.replace(singleTickRegEx, "\\'")}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefix) {
|
||||||
|
literalStr = `${prefix}${literalStr}`;
|
||||||
|
}
|
||||||
} else if (typeof literalValue === 'boolean') {
|
} else if (typeof literalValue === 'boolean') {
|
||||||
literalStr = literalValue ? 'True' : 'False';
|
literalStr = literalValue ? 'True' : 'False';
|
||||||
} else if (literalValue instanceof EnumLiteral) {
|
} else if (literalValue instanceof EnumLiteral) {
|
||||||
|
@ -8,9 +8,15 @@
|
|||||||
|
|
||||||
import { Worker } from 'worker_threads';
|
import { Worker } from 'worker_threads';
|
||||||
|
|
||||||
import { BackgroundAnalysisBase, BackgroundAnalysisRunnerBase, InitializationData } from './backgroundAnalysisBase';
|
import { ImportResolver } from './analyzer/importResolver';
|
||||||
|
import { BackgroundAnalysisBase, BackgroundAnalysisRunnerBase } from './backgroundAnalysisBase';
|
||||||
|
import { InitializationData } from './backgroundThreadBase';
|
||||||
import { getCancellationFolderName } from './common/cancellationUtils';
|
import { getCancellationFolderName } from './common/cancellationUtils';
|
||||||
|
import { ConfigOptions } from './common/configOptions';
|
||||||
import { ConsoleInterface } from './common/console';
|
import { ConsoleInterface } from './common/console';
|
||||||
|
import { FileSystem } from './common/fileSystem';
|
||||||
|
import { FullAccessHost } from './common/fullAccessHost';
|
||||||
|
import { Host } from './common/host';
|
||||||
|
|
||||||
export class BackgroundAnalysis extends BackgroundAnalysisBase {
|
export class BackgroundAnalysis extends BackgroundAnalysisBase {
|
||||||
constructor(console: ConsoleInterface) {
|
constructor(console: ConsoleInterface) {
|
||||||
@ -32,4 +38,12 @@ export class BackgroundAnalysisRunner extends BackgroundAnalysisRunnerBase {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override createHost(): Host {
|
||||||
|
return new FullAccessHost(this.fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override createImportResolver(fs: FileSystem, options: ConfigOptions, host: Host): ImportResolver {
|
||||||
|
return new ImportResolver(fs, options, host);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
BackgroundThreadBase,
|
BackgroundThreadBase,
|
||||||
createConfigOptionsFrom,
|
createConfigOptionsFrom,
|
||||||
getBackgroundWaiter,
|
getBackgroundWaiter,
|
||||||
|
InitializationData,
|
||||||
LogData,
|
LogData,
|
||||||
run,
|
run,
|
||||||
} from './backgroundThreadBase';
|
} from './backgroundThreadBase';
|
||||||
@ -33,6 +34,7 @@ import {
|
|||||||
getCancellationTokenId,
|
getCancellationTokenId,
|
||||||
} from './common/fileBasedCancellationUtils';
|
} from './common/fileBasedCancellationUtils';
|
||||||
import { FileSystem } from './common/fileSystem';
|
import { FileSystem } from './common/fileSystem';
|
||||||
|
import { Host, HostKind } from './common/host';
|
||||||
import { LogTracker } from './common/logTracker';
|
import { LogTracker } from './common/logTracker';
|
||||||
import { Range } from './common/textRange';
|
import { Range } from './common/textRange';
|
||||||
import { IndexResults } from './languageService/documentSymbolProvider';
|
import { IndexResults } from './languageService/documentSymbolProvider';
|
||||||
@ -82,6 +84,10 @@ export class BackgroundAnalysisBase {
|
|||||||
this._onAnalysisCompletion = callback ?? nullCallback;
|
this._onAnalysisCompletion = callback ?? nullCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setImportResolver(importResolver: ImportResolver) {
|
||||||
|
this.enqueueRequest({ requestType: 'setImportResolver', data: importResolver.host.kind });
|
||||||
|
}
|
||||||
|
|
||||||
setConfigOptions(configOptions: ConfigOptions) {
|
setConfigOptions(configOptions: ConfigOptions) {
|
||||||
this.enqueueRequest({ requestType: 'setConfigOptions', data: configOptions });
|
this.enqueueRequest({ requestType: 'setConfigOptions', data: configOptions });
|
||||||
}
|
}
|
||||||
@ -170,11 +176,11 @@ export class BackgroundAnalysisBase {
|
|||||||
this.enqueueRequest({ requestType, data: cancellationId, port: port2 });
|
this.enqueueRequest({ requestType, data: cancellationId, port: port2 });
|
||||||
}
|
}
|
||||||
|
|
||||||
startIndexing(configOptions: ConfigOptions, indices: Indices) {
|
startIndexing(configOptions: ConfigOptions, kind: HostKind, indices: Indices) {
|
||||||
/* noop */
|
/* noop */
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshIndexing(configOptions: ConfigOptions, indices?: Indices) {
|
refreshIndexing(configOptions: ConfigOptions, kind: HostKind, indices?: Indices) {
|
||||||
/* noop */
|
/* noop */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,10 +252,12 @@ export class BackgroundAnalysisBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BackgroundAnalysisRunnerBase extends BackgroundThreadBase {
|
export abstract class BackgroundAnalysisRunnerBase extends BackgroundThreadBase {
|
||||||
private _configOptions: ConfigOptions;
|
private _configOptions: ConfigOptions;
|
||||||
protected _importResolver: ImportResolver;
|
protected _importResolver: ImportResolver;
|
||||||
private _program: Program;
|
private _program: Program;
|
||||||
|
|
||||||
|
protected _host: Host;
|
||||||
protected _logTracker: LogTracker;
|
protected _logTracker: LogTracker;
|
||||||
|
|
||||||
get program(): Program {
|
get program(): Program {
|
||||||
@ -264,7 +272,8 @@ export class BackgroundAnalysisRunnerBase extends BackgroundThreadBase {
|
|||||||
this.log(LogLevel.Info, `Background analysis(${threadId}) root directory: ${data.rootDirectory}`);
|
this.log(LogLevel.Info, `Background analysis(${threadId}) root directory: ${data.rootDirectory}`);
|
||||||
|
|
||||||
this._configOptions = new ConfigOptions(data.rootDirectory);
|
this._configOptions = new ConfigOptions(data.rootDirectory);
|
||||||
this._importResolver = this.createImportResolver(this.fs, this._configOptions);
|
this._host = this.createHost();
|
||||||
|
this._importResolver = this.createImportResolver(this.fs, this._configOptions, this._host);
|
||||||
|
|
||||||
const console = this.getConsole();
|
const console = this.getConsole();
|
||||||
this._logTracker = new LogTracker(console, `BG(${threadId})`);
|
this._logTracker = new LogTracker(console, `BG(${threadId})`);
|
||||||
@ -354,9 +363,17 @@ export class BackgroundAnalysisRunnerBase extends BackgroundThreadBase {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'setImportResolver': {
|
||||||
|
this._importResolver = this.createImportResolver(this.fs, this._configOptions, this.createHost());
|
||||||
|
|
||||||
|
this.program.setImportResolver(this._importResolver);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'setConfigOptions': {
|
case 'setConfigOptions': {
|
||||||
this._configOptions = createConfigOptionsFrom(msg.data);
|
this._configOptions = createConfigOptionsFrom(msg.data);
|
||||||
this._importResolver = this.createImportResolver(this.fs, this._configOptions);
|
|
||||||
|
this._importResolver = this.createImportResolver(this.fs, this._configOptions, this._host);
|
||||||
this.program.setConfigOptions(this._configOptions);
|
this.program.setConfigOptions(this._configOptions);
|
||||||
this.program.setImportResolver(this._importResolver);
|
this.program.setImportResolver(this._importResolver);
|
||||||
break;
|
break;
|
||||||
@ -417,7 +434,7 @@ export class BackgroundAnalysisRunnerBase extends BackgroundThreadBase {
|
|||||||
|
|
||||||
case 'restart': {
|
case 'restart': {
|
||||||
// recycle import resolver
|
// recycle import resolver
|
||||||
this._importResolver = this.createImportResolver(this.fs, this._configOptions);
|
this._importResolver = this.createImportResolver(this.fs, this._configOptions, this._host);
|
||||||
this.program.setImportResolver(this._importResolver);
|
this.program.setImportResolver(this._importResolver);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -451,11 +468,11 @@ export class BackgroundAnalysisRunnerBase extends BackgroundThreadBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createImportResolver(fs: FileSystem, options: ConfigOptions): ImportResolver {
|
protected abstract createHost(): Host;
|
||||||
return new ImportResolver(fs, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected processIndexing(port: MessagePort, token: CancellationToken) {
|
protected abstract createImportResolver(fs: FileSystem, options: ConfigOptions, host: Host): ImportResolver;
|
||||||
|
|
||||||
|
protected processIndexing(port: MessagePort, token: CancellationToken): void {
|
||||||
/* noop */
|
/* noop */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,12 +543,6 @@ function convertDiagnostics(diagnostics: Diagnostic[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InitializationData {
|
|
||||||
rootDirectory: string;
|
|
||||||
cancellationFolderName: string | undefined;
|
|
||||||
runner: string | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AnalysisRequest {
|
export interface AnalysisRequest {
|
||||||
requestType:
|
requestType:
|
||||||
| 'analyze'
|
| 'analyze'
|
||||||
@ -549,7 +560,8 @@ export interface AnalysisRequest {
|
|||||||
| 'getDiagnosticsForRange'
|
| 'getDiagnosticsForRange'
|
||||||
| 'writeTypeStub'
|
| 'writeTypeStub'
|
||||||
| 'getSemanticTokens'
|
| 'getSemanticTokens'
|
||||||
| 'setExperimentOptions';
|
| 'setExperimentOptions'
|
||||||
|
| 'setImportResolver';
|
||||||
|
|
||||||
data: any;
|
data: any;
|
||||||
port?: MessagePort | undefined;
|
port?: MessagePort | undefined;
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
* Class that holds the configuration options for the analyzer.
|
* Class that holds the configuration options for the analyzer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as child_process from 'child_process';
|
|
||||||
import { isAbsolute } from 'path';
|
import { isAbsolute } from 'path';
|
||||||
|
|
||||||
import * as pathConsts from '../common/pathConsts';
|
import * as pathConsts from '../common/pathConsts';
|
||||||
@ -15,6 +14,7 @@ import { DiagnosticSeverityOverridesMap } from './commandLineOptions';
|
|||||||
import { ConsoleInterface } from './console';
|
import { ConsoleInterface } from './console';
|
||||||
import { DiagnosticRule } from './diagnosticRules';
|
import { DiagnosticRule } from './diagnosticRules';
|
||||||
import { FileSystem } from './fileSystem';
|
import { FileSystem } from './fileSystem';
|
||||||
|
import { Host } from './host';
|
||||||
import {
|
import {
|
||||||
combinePaths,
|
combinePaths,
|
||||||
ensureTrailingDirectorySeparator,
|
ensureTrailingDirectorySeparator,
|
||||||
@ -23,13 +23,7 @@ import {
|
|||||||
normalizePath,
|
normalizePath,
|
||||||
resolvePaths,
|
resolvePaths,
|
||||||
} from './pathUtils';
|
} from './pathUtils';
|
||||||
import {
|
import { latestStablePythonVersion, PythonVersion, versionFromString, versionToString } from './pythonVersion';
|
||||||
latestStablePythonVersion,
|
|
||||||
PythonVersion,
|
|
||||||
versionFromMajorMinor,
|
|
||||||
versionFromString,
|
|
||||||
versionToString,
|
|
||||||
} from './pythonVersion';
|
|
||||||
|
|
||||||
export enum PythonPlatform {
|
export enum PythonPlatform {
|
||||||
Darwin = 'Darwin',
|
Darwin = 'Darwin',
|
||||||
@ -726,8 +720,8 @@ export class ConfigOptions {
|
|||||||
configObj: any,
|
configObj: any,
|
||||||
typeCheckingMode: string | undefined,
|
typeCheckingMode: string | undefined,
|
||||||
console: ConsoleInterface,
|
console: ConsoleInterface,
|
||||||
|
host: Host,
|
||||||
diagnosticOverrides?: DiagnosticSeverityOverridesMap,
|
diagnosticOverrides?: DiagnosticSeverityOverridesMap,
|
||||||
pythonPath?: string,
|
|
||||||
skipIncludeSection = false
|
skipIncludeSection = false
|
||||||
) {
|
) {
|
||||||
// Read the "include" entry.
|
// Read the "include" entry.
|
||||||
@ -1298,7 +1292,7 @@ export class ConfigOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ensureDefaultPythonVersion(pythonPath, console);
|
this.ensureDefaultPythonVersion(host, console);
|
||||||
|
|
||||||
// Read the default "pythonPlatform".
|
// Read the default "pythonPlatform".
|
||||||
if (configObj.pythonPlatform !== undefined) {
|
if (configObj.pythonPlatform !== undefined) {
|
||||||
@ -1309,7 +1303,7 @@ export class ConfigOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ensureDefaultPythonPlatform(console);
|
this.ensureDefaultPythonPlatform(host, console);
|
||||||
|
|
||||||
// Read the "typeshedPath" setting.
|
// Read the "typeshedPath" setting.
|
||||||
this.typeshedPath = undefined;
|
this.typeshedPath = undefined;
|
||||||
@ -1418,37 +1412,35 @@ export class ConfigOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureDefaultPythonPlatform(console: ConsoleInterface) {
|
ensureDefaultPythonPlatform(host: Host, console: ConsoleInterface) {
|
||||||
// If no default python platform was specified, assume that the
|
// If no default python platform was specified, assume that the
|
||||||
// user wants to use the current platform.
|
// user wants to use the current platform.
|
||||||
if (this.defaultPythonPlatform !== undefined) {
|
if (this.defaultPythonPlatform !== undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
this.defaultPythonPlatform = host.getPythonPlatform();
|
||||||
this.defaultPythonPlatform = PythonPlatform.Darwin;
|
|
||||||
} else if (process.platform === 'linux') {
|
|
||||||
this.defaultPythonPlatform = PythonPlatform.Linux;
|
|
||||||
} else if (process.platform === 'win32') {
|
|
||||||
this.defaultPythonPlatform = PythonPlatform.Windows;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.defaultPythonPlatform !== undefined) {
|
if (this.defaultPythonPlatform !== undefined) {
|
||||||
console.info(`Assuming Python platform ${this.defaultPythonPlatform}`);
|
console.info(`Assuming Python platform ${this.defaultPythonPlatform}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureDefaultPythonVersion(pythonPath: string | undefined, console: ConsoleInterface) {
|
ensureDefaultPythonVersion(host: Host, console: ConsoleInterface) {
|
||||||
// If no default python version was specified, retrieve the version
|
// If no default python version was specified, retrieve the version
|
||||||
// from the currently-selected python interpreter.
|
// from the currently-selected python interpreter.
|
||||||
if (this.defaultPythonVersion !== undefined) {
|
if (this.defaultPythonVersion !== undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.defaultPythonVersion = this._getPythonVersionFromPythonInterpreter(pythonPath, console);
|
const importFailureInfo: string[] = [];
|
||||||
|
this.defaultPythonVersion = host.getPythonVersion(this.pythonPath, importFailureInfo);
|
||||||
if (this.defaultPythonVersion !== undefined) {
|
if (this.defaultPythonVersion !== undefined) {
|
||||||
console.info(`Assuming Python version ${versionToString(this.defaultPythonVersion)}`);
|
console.info(`Assuming Python version ${versionToString(this.defaultPythonVersion)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const log of importFailureInfo) {
|
||||||
|
console.info(log);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureDefaultExtraPaths(fs: FileSystem, autoSearchPaths: boolean, extraPaths: string[] | undefined) {
|
ensureDefaultExtraPaths(fs: FileSystem, autoSearchPaths: boolean, extraPaths: string[] | undefined) {
|
||||||
@ -1580,38 +1572,4 @@ export class ConfigOptions {
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getPythonVersionFromPythonInterpreter(
|
|
||||||
interpreterPath: string | undefined,
|
|
||||||
console: ConsoleInterface
|
|
||||||
): PythonVersion | undefined {
|
|
||||||
try {
|
|
||||||
const commandLineArgs: string[] = [
|
|
||||||
'-c',
|
|
||||||
'import sys, json; json.dump(dict(major=sys.version_info[0], minor=sys.version_info[1]), sys.stdout)',
|
|
||||||
];
|
|
||||||
let execOutput: string;
|
|
||||||
|
|
||||||
if (interpreterPath) {
|
|
||||||
execOutput = child_process.execFileSync(interpreterPath, commandLineArgs, { encoding: 'utf8' });
|
|
||||||
} else {
|
|
||||||
execOutput = child_process.execFileSync('python', commandLineArgs, { encoding: 'utf8' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const versionJson: { major: number; minor: number } = JSON.parse(execOutput);
|
|
||||||
|
|
||||||
const version = versionFromMajorMinor(versionJson.major, versionJson.minor);
|
|
||||||
if (version === undefined) {
|
|
||||||
console.warn(
|
|
||||||
`Python version ${versionJson.major}.${versionJson.minor} from interpreter is unsupported`
|
|
||||||
);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return version;
|
|
||||||
} catch {
|
|
||||||
console.info('Unable to get Python version from interpreter');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ export function isNumber(x: unknown): x is number {
|
|||||||
return typeof x === 'number';
|
return typeof x === 'number';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isBoolean(x: unknown): x is number {
|
export function isBoolean(x: unknown): x is boolean {
|
||||||
return typeof x === 'boolean';
|
return typeof x === 'boolean';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
183
packages/pyright-internal/src/common/fullAccessHost.ts
Normal file
183
packages/pyright-internal/src/common/fullAccessHost.ts
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
* fullAccessHost.ts
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* Implementation of host where it is allowed to run external executables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as child_process from 'child_process';
|
||||||
|
|
||||||
|
import { PythonPathResult } from '../analyzer/pythonPathUtils';
|
||||||
|
import { PythonPlatform } from './configOptions';
|
||||||
|
import { assertNever } from './debug';
|
||||||
|
import { FileSystem } from './fileSystem';
|
||||||
|
import { HostKind, NoAccessHost } from './host';
|
||||||
|
import { isDirectory, normalizePath } from './pathUtils';
|
||||||
|
import { PythonVersion, versionFromMajorMinor } from './pythonVersion';
|
||||||
|
|
||||||
|
const extractSys = [
|
||||||
|
'import os, os.path, sys',
|
||||||
|
'normalize = lambda p: os.path.normcase(os.path.normpath(p))',
|
||||||
|
'cwd = normalize(os.getcwd())',
|
||||||
|
'sys.path[:] = [p for p in sys.path if p != "" and normalize(p) != cwd]',
|
||||||
|
'import json',
|
||||||
|
'json.dump(dict(path=sys.path, prefix=sys.prefix), sys.stdout)',
|
||||||
|
].join('; ');
|
||||||
|
|
||||||
|
export class LimitedAccessHost extends NoAccessHost {
|
||||||
|
override get kind(): HostKind {
|
||||||
|
return HostKind.LimitedAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getPythonPlatform(logInfo?: string[]): PythonPlatform | undefined {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
return PythonPlatform.Darwin;
|
||||||
|
} else if (process.platform === 'linux') {
|
||||||
|
return PythonPlatform.Linux;
|
||||||
|
} else if (process.platform === 'win32') {
|
||||||
|
return PythonPlatform.Windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FullAccessHost extends LimitedAccessHost {
|
||||||
|
static createHost(kind: HostKind, fs: FileSystem) {
|
||||||
|
switch (kind) {
|
||||||
|
case HostKind.NoAccess:
|
||||||
|
return new NoAccessHost();
|
||||||
|
case HostKind.LimitedAccess:
|
||||||
|
return new LimitedAccessHost();
|
||||||
|
case HostKind.FullAccess:
|
||||||
|
return new FullAccessHost(fs);
|
||||||
|
default:
|
||||||
|
assertNever(kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(protected _fs: FileSystem) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
override get kind(): HostKind {
|
||||||
|
return HostKind.FullAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getPythonSearchPaths(pythonPath?: string, logInfo?: string[]): PythonPathResult {
|
||||||
|
const importFailureInfo = logInfo ?? [];
|
||||||
|
let result: PythonPathResult | undefined;
|
||||||
|
|
||||||
|
if (pythonPath) {
|
||||||
|
result = this._getSearchPathResultFromInterpreter(this._fs, pythonPath, importFailureInfo);
|
||||||
|
} else {
|
||||||
|
// On non-Windows platforms, always default to python3 first. We want to
|
||||||
|
// avoid this on Windows because it might invoke a script that displays
|
||||||
|
// a dialog box indicating that python can be downloaded from the app store.
|
||||||
|
if (process.platform !== 'win32') {
|
||||||
|
result = this._getSearchPathResultFromInterpreter(this._fs, 'python3', importFailureInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// On some platforms, 'python3' might not exist. Try 'python' instead.
|
||||||
|
if (!result) {
|
||||||
|
result = this._getSearchPathResultFromInterpreter(this._fs, 'python', importFailureInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
result = {
|
||||||
|
paths: [],
|
||||||
|
prefix: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
importFailureInfo.push(`Received ${result.paths.length} paths from interpreter`);
|
||||||
|
result.paths.forEach((path) => {
|
||||||
|
importFailureInfo.push(` ${path}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getPythonVersion(pythonPath?: string, logInfo?: string[]): PythonVersion | undefined {
|
||||||
|
const importFailureInfo = logInfo ?? [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const commandLineArgs: string[] = [
|
||||||
|
'-c',
|
||||||
|
'import sys, json; json.dump(dict(major=sys.version_info[0], minor=sys.version_info[1]), sys.stdout)',
|
||||||
|
];
|
||||||
|
let execOutput: string;
|
||||||
|
|
||||||
|
if (pythonPath) {
|
||||||
|
execOutput = child_process.execFileSync(pythonPath, commandLineArgs, { encoding: 'utf8' });
|
||||||
|
} else {
|
||||||
|
execOutput = child_process.execFileSync('python', commandLineArgs, { encoding: 'utf8' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionJson: { major: number; minor: number } = JSON.parse(execOutput);
|
||||||
|
|
||||||
|
const version = versionFromMajorMinor(versionJson.major, versionJson.minor);
|
||||||
|
if (version === undefined) {
|
||||||
|
importFailureInfo.push(
|
||||||
|
`Python version ${versionJson.major}.${versionJson.minor} from interpreter is unsupported`
|
||||||
|
);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return version;
|
||||||
|
} catch {
|
||||||
|
importFailureInfo.push('Unable to get Python version from interpreter');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getSearchPathResultFromInterpreter(
|
||||||
|
fs: FileSystem,
|
||||||
|
interpreter: string,
|
||||||
|
importFailureInfo: string[]
|
||||||
|
): PythonPathResult | undefined {
|
||||||
|
const result: PythonPathResult = {
|
||||||
|
paths: [],
|
||||||
|
prefix: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const commandLineArgs: string[] = ['-c', extractSys];
|
||||||
|
|
||||||
|
importFailureInfo.push(`Executing interpreter: '${interpreter}'`);
|
||||||
|
const execOutput = child_process.execFileSync(interpreter, commandLineArgs, { encoding: 'utf8' });
|
||||||
|
|
||||||
|
// Parse the execOutput. It should be a JSON-encoded array of paths.
|
||||||
|
try {
|
||||||
|
const execSplit = JSON.parse(execOutput);
|
||||||
|
for (let execSplitEntry of execSplit.path) {
|
||||||
|
execSplitEntry = execSplitEntry.trim();
|
||||||
|
if (execSplitEntry) {
|
||||||
|
const normalizedPath = normalizePath(execSplitEntry);
|
||||||
|
// Skip non-existent paths and broken zips/eggs.
|
||||||
|
if (fs.existsSync(normalizedPath) && isDirectory(fs, normalizedPath)) {
|
||||||
|
result.paths.push(normalizedPath);
|
||||||
|
} else {
|
||||||
|
importFailureInfo.push(`Skipping '${normalizedPath}' because it is not a valid directory`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.prefix = execSplit.prefix;
|
||||||
|
|
||||||
|
if (result.paths.length === 0) {
|
||||||
|
importFailureInfo.push(`Found no valid directories`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
importFailureInfo.push(`Could not parse output: '${execOutput}'`);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
49
packages/pyright-internal/src/common/host.ts
Normal file
49
packages/pyright-internal/src/common/host.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* host.ts
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* Provides accesses to the host the language service runs on
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PythonPathResult } from '../analyzer/pythonPathUtils';
|
||||||
|
import { PythonPlatform } from './configOptions';
|
||||||
|
import { PythonVersion } from './pythonVersion';
|
||||||
|
|
||||||
|
export const enum HostKind {
|
||||||
|
FullAccess,
|
||||||
|
LimitedAccess,
|
||||||
|
NoAccess,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Host {
|
||||||
|
readonly kind: HostKind;
|
||||||
|
getPythonSearchPaths(pythonPath?: string, logInfo?: string[]): PythonPathResult;
|
||||||
|
getPythonVersion(pythonPath?: string, logInfo?: string[]): PythonVersion | undefined;
|
||||||
|
getPythonPlatform(logInfo?: string[]): PythonPlatform | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NoAccessHost implements Host {
|
||||||
|
get kind(): HostKind {
|
||||||
|
return HostKind.NoAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPythonSearchPaths(pythonPath?: string, logInfo?: string[]): PythonPathResult {
|
||||||
|
logInfo?.push('No access to python executable.');
|
||||||
|
|
||||||
|
return {
|
||||||
|
paths: [],
|
||||||
|
prefix: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getPythonVersion(pythonPath?: string, logInfo?: string[]): PythonVersion | undefined {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPythonPlatform(logInfo?: string[]): PythonPlatform | undefined {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HostFactory = () => Host;
|
@ -372,7 +372,7 @@ interface WorkspaceFileWatcher extends FileWatcher {
|
|||||||
export class WorkspaceFileWatcherProvider implements FileWatcherProvider {
|
export class WorkspaceFileWatcherProvider implements FileWatcherProvider {
|
||||||
private _fileWatchers: WorkspaceFileWatcher[] = [];
|
private _fileWatchers: WorkspaceFileWatcher[] = [];
|
||||||
|
|
||||||
constructor(private _workspaceMap: WorkspaceMap) {}
|
constructor(private _workspaceMap: WorkspaceMap, private _console: ConsoleInterface) {}
|
||||||
|
|
||||||
createFileWatcher(paths: string[], listener: FileWatcherEventHandler): FileWatcher {
|
createFileWatcher(paths: string[], listener: FileWatcherEventHandler): FileWatcher {
|
||||||
// Determine which paths are located within one or more workspaces.
|
// Determine which paths are located within one or more workspaces.
|
||||||
@ -404,7 +404,7 @@ export class WorkspaceFileWatcherProvider implements FileWatcherProvider {
|
|||||||
listener(event as FileWatcherEventType, filename)
|
listener(event as FileWatcherEventType, filename)
|
||||||
);
|
);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.warn(`Exception received when installing recursive file system watcher: ${e}`);
|
this._console.warn(`Exception received when installing file system watcher: ${e}`);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -68,6 +68,7 @@ import { Diagnostic as AnalyzerDiagnostic, DiagnosticCategory } from './common/d
|
|||||||
import { DiagnosticRule } from './common/diagnosticRules';
|
import { DiagnosticRule } from './common/diagnosticRules';
|
||||||
import { LanguageServiceExtension } from './common/extensibility';
|
import { LanguageServiceExtension } from './common/extensibility';
|
||||||
import { FileSystem, FileWatcherEventType, FileWatcherProvider, isInZipOrEgg } from './common/fileSystem';
|
import { FileSystem, FileWatcherEventType, FileWatcherProvider, isInZipOrEgg } from './common/fileSystem';
|
||||||
|
import { Host } from './common/host';
|
||||||
import { convertPathToUri, convertUriToPath } from './common/pathUtils';
|
import { convertPathToUri, convertUriToPath } from './common/pathUtils';
|
||||||
import { ProgressReporter, ProgressReportTracker } from './common/progressReporter';
|
import { ProgressReporter, ProgressReportTracker } from './common/progressReporter';
|
||||||
import { DocumentRange, Position } from './common/textRange';
|
import { DocumentRange, Position } from './common/textRange';
|
||||||
@ -206,15 +207,15 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
|||||||
// File system abstraction.
|
// File system abstraction.
|
||||||
fs: FileSystem;
|
fs: FileSystem;
|
||||||
|
|
||||||
readonly console: ConsoleInterface;
|
constructor(
|
||||||
|
protected _serverOptions: ServerOptions,
|
||||||
constructor(protected _serverOptions: ServerOptions, protected _connection: Connection) {
|
protected _connection: Connection,
|
||||||
|
readonly console: ConsoleInterface
|
||||||
|
) {
|
||||||
// Stash the base directory into a global variable.
|
// Stash the base directory into a global variable.
|
||||||
// This must happen before fs.getModulePath().
|
// This must happen before fs.getModulePath().
|
||||||
(global as any).__rootDirectory = _serverOptions.rootDirectory;
|
(global as any).__rootDirectory = _serverOptions.rootDirectory;
|
||||||
|
|
||||||
this.console = new ConsoleWithLogLevel(this._connection.console);
|
|
||||||
|
|
||||||
this.console.info(
|
this.console.info(
|
||||||
`${_serverOptions.productName} language server ${
|
`${_serverOptions.productName} language server ${
|
||||||
_serverOptions.version && _serverOptions.version + ' '
|
_serverOptions.version && _serverOptions.version + ' '
|
||||||
@ -304,9 +305,8 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createImportResolver(fs: FileSystem, options: ConfigOptions): ImportResolver {
|
protected abstract createHost(): Host;
|
||||||
return new ImportResolver(fs, options);
|
protected abstract createImportResolver(fs: FileSystem, options: ConfigOptions, host: Host): ImportResolver;
|
||||||
}
|
|
||||||
|
|
||||||
protected createBackgroundAnalysisProgram(
|
protected createBackgroundAnalysisProgram(
|
||||||
console: ConsoleInterface,
|
console: ConsoleInterface,
|
||||||
@ -343,6 +343,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
|||||||
name,
|
name,
|
||||||
this.fs,
|
this.fs,
|
||||||
this.console,
|
this.console,
|
||||||
|
this.createHost.bind(this),
|
||||||
this.createImportResolver.bind(this),
|
this.createImportResolver.bind(this),
|
||||||
undefined,
|
undefined,
|
||||||
this._serverOptions.extension,
|
this._serverOptions.extension,
|
||||||
|
@ -39,6 +39,7 @@ import {
|
|||||||
getVariableInStubFileDocStrings,
|
getVariableInStubFileDocStrings,
|
||||||
} from '../analyzer/typeDocStringUtils';
|
} from '../analyzer/typeDocStringUtils';
|
||||||
import { CallSignatureInfo, TypeEvaluator } from '../analyzer/typeEvaluator';
|
import { CallSignatureInfo, TypeEvaluator } from '../analyzer/typeEvaluator';
|
||||||
|
import { printLiteralValue } from '../analyzer/typePrinter';
|
||||||
import {
|
import {
|
||||||
ClassType,
|
ClassType,
|
||||||
FunctionType,
|
FunctionType,
|
||||||
@ -61,6 +62,7 @@ import {
|
|||||||
getDeclaringModulesForType,
|
getDeclaringModulesForType,
|
||||||
getMembersForClass,
|
getMembersForClass,
|
||||||
getMembersForModule,
|
getMembersForModule,
|
||||||
|
isLiteralType,
|
||||||
isProperty,
|
isProperty,
|
||||||
} from '../analyzer/typeUtils';
|
} from '../analyzer/typeUtils';
|
||||||
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
import { throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||||
@ -1171,7 +1173,14 @@ export class CompletionProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add call argument completions.
|
// Add call argument completions.
|
||||||
this._addCallArgumentCompletions(parseNode, priorWord, priorText, postText, completionList);
|
this._addCallArgumentCompletions(
|
||||||
|
parseNode,
|
||||||
|
priorWord,
|
||||||
|
priorText,
|
||||||
|
postText,
|
||||||
|
/*atArgument*/ false,
|
||||||
|
completionList
|
||||||
|
);
|
||||||
|
|
||||||
// Add symbols that are in scope.
|
// Add symbols that are in scope.
|
||||||
this._addSymbols(parseNode, priorWord, completionList);
|
this._addSymbols(parseNode, priorWord, completionList);
|
||||||
@ -1209,7 +1218,13 @@ export class CompletionProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (declaredTypeOfTarget) {
|
if (declaredTypeOfTarget) {
|
||||||
this._addLiteralValuesForTargetType(declaredTypeOfTarget, priorText, postText, completionList);
|
this._addLiteralValuesForTargetType(
|
||||||
|
declaredTypeOfTarget,
|
||||||
|
priorText,
|
||||||
|
priorWord,
|
||||||
|
postText,
|
||||||
|
completionList
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1257,6 +1272,7 @@ export class CompletionProvider {
|
|||||||
priorWord: string,
|
priorWord: string,
|
||||||
priorText: string,
|
priorText: string,
|
||||||
postText: string,
|
postText: string,
|
||||||
|
atArgument: boolean,
|
||||||
completionList: CompletionList
|
completionList: CompletionList
|
||||||
) {
|
) {
|
||||||
// If we're within the argument list of a call, add parameter names.
|
// If we're within the argument list of a call, add parameter names.
|
||||||
@ -1285,10 +1301,12 @@ export class CompletionProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (comparePositions(this._position, callNameEnd) > 0) {
|
if (comparePositions(this._position, callNameEnd) > 0) {
|
||||||
this._addNamedParameters(signatureInfo, priorWord, completionList);
|
if (!atArgument) {
|
||||||
|
this._addNamedParameters(signatureInfo, priorWord, completionList);
|
||||||
|
}
|
||||||
|
|
||||||
// Add literals that apply to this parameter.
|
// Add literals that apply to this parameter.
|
||||||
this._addLiteralValuesForArgument(signatureInfo, priorText, postText, completionList);
|
this._addLiteralValuesForArgument(signatureInfo, priorText, priorWord, postText, completionList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1296,6 +1314,7 @@ export class CompletionProvider {
|
|||||||
private _addLiteralValuesForArgument(
|
private _addLiteralValuesForArgument(
|
||||||
signatureInfo: CallSignatureInfo,
|
signatureInfo: CallSignatureInfo,
|
||||||
priorText: string,
|
priorText: string,
|
||||||
|
priorWord: string,
|
||||||
postText: string,
|
postText: string,
|
||||||
completionList: CompletionList
|
completionList: CompletionList
|
||||||
) {
|
) {
|
||||||
@ -1312,7 +1331,7 @@ export class CompletionProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const paramType = type.details.parameters[paramIndex].type;
|
const paramType = type.details.parameters[paramIndex].type;
|
||||||
this._addLiteralValuesForTargetType(paramType, priorText, postText, completionList);
|
this._addLiteralValuesForTargetType(paramType, priorText, priorWord, postText, completionList);
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1320,23 +1339,43 @@ export class CompletionProvider {
|
|||||||
private _addLiteralValuesForTargetType(
|
private _addLiteralValuesForTargetType(
|
||||||
type: Type,
|
type: Type,
|
||||||
priorText: string,
|
priorText: string,
|
||||||
|
priorWord: string,
|
||||||
postText: string,
|
postText: string,
|
||||||
completionList: CompletionList
|
completionList: CompletionList
|
||||||
) {
|
) {
|
||||||
const quoteValue = this._getQuoteValueFromPriorText(priorText);
|
const quoteValue = this._getQuoteValueFromPriorText(priorText);
|
||||||
doForEachSubtype(type, (subtype) => {
|
this._getSubTypesWithLiteralValues(type).forEach((v) => {
|
||||||
if (isClassInstance(subtype) && ClassType.isBuiltIn(subtype, 'str') && subtype.literalValue !== undefined) {
|
if (ClassType.isBuiltIn(v, 'str')) {
|
||||||
this._addStringLiteralToCompletionList(
|
const value = printLiteralValue(v, quoteValue.quoteCharacter);
|
||||||
subtype.literalValue as string,
|
if (quoteValue.stringValue === undefined) {
|
||||||
quoteValue.stringValue,
|
this._addNameToCompletionList(value, CompletionItemKind.Constant, priorWord, completionList, {
|
||||||
postText,
|
sortText: this._makeSortText(SortCategory.LiteralValue, v.literalValue as string),
|
||||||
quoteValue.quoteCharacter,
|
});
|
||||||
completionList
|
} else {
|
||||||
);
|
this._addStringLiteralToCompletionList(
|
||||||
|
value.substr(1, value.length - 2),
|
||||||
|
quoteValue.stringValue,
|
||||||
|
postText,
|
||||||
|
quoteValue.quoteCharacter,
|
||||||
|
completionList
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getSubTypesWithLiteralValues(type: Type) {
|
||||||
|
const values: ClassType[] = [];
|
||||||
|
|
||||||
|
doForEachSubtype(type, (subtype) => {
|
||||||
|
if (isClassInstance(subtype) && isLiteralType(subtype)) {
|
||||||
|
values.push(subtype);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
private _getDictionaryKeys(indexNode: IndexNode, invocationNode: ParseNode) {
|
private _getDictionaryKeys(indexNode: IndexNode, invocationNode: ParseNode) {
|
||||||
if (indexNode.baseExpression.nodeType !== ParseNodeType.Name) {
|
if (indexNode.baseExpression.nodeType !== ParseNodeType.Name) {
|
||||||
// This completion only supports simple name case
|
// This completion only supports simple name case
|
||||||
@ -1349,10 +1388,34 @@ export class CompletionProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Must be dict type
|
// Must be dict type
|
||||||
if (!ClassType.isBuiltIn(baseType, 'dict')) {
|
if (!ClassType.isBuiltIn(baseType, 'dict') && !ClassType.isBuiltIn(baseType, 'Mapping')) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See whether dictionary is typed using Literal types. If it is, return those literal keys.
|
||||||
|
// For now, we are not using __getitem__ since we don't have a way to get effective parameter type of __getitem__.
|
||||||
|
if (baseType.typeArguments?.length === 2) {
|
||||||
|
const keys: string[] = [];
|
||||||
|
|
||||||
|
this._getSubTypesWithLiteralValues(baseType.typeArguments[0]).forEach((v) => {
|
||||||
|
if (
|
||||||
|
!ClassType.isBuiltIn(v, 'str') &&
|
||||||
|
!ClassType.isBuiltIn(v, 'int') &&
|
||||||
|
!ClassType.isBuiltIn(v, 'bool') &&
|
||||||
|
!ClassType.isBuiltIn(v, 'bytes') &&
|
||||||
|
!ClassType.isEnumClass(v)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
keys.push(printLiteralValue(v, this._parseResults.tokenizerOutput.predominantSingleQuoteCharacter));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (keys.length > 0) {
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Must be local variable/parameter
|
// Must be local variable/parameter
|
||||||
const declarations = this._evaluator.getDeclarationsForNameNode(indexNode.baseExpression) ?? [];
|
const declarations = this._evaluator.getDeclarationsForNameNode(indexNode.baseExpression) ?? [];
|
||||||
const declaration = declarations.length > 0 ? declarations[0] : undefined;
|
const declaration = declarations.length > 0 ? declarations[0] : undefined;
|
||||||
@ -1513,16 +1576,20 @@ export class CompletionProvider {
|
|||||||
const declaredTypeOfTarget = this._evaluator.getDeclaredTypeForExpression(parentNode.leftExpression);
|
const declaredTypeOfTarget = this._evaluator.getDeclaredTypeForExpression(parentNode.leftExpression);
|
||||||
|
|
||||||
if (declaredTypeOfTarget) {
|
if (declaredTypeOfTarget) {
|
||||||
this._addLiteralValuesForTargetType(declaredTypeOfTarget, priorText, postText, completionList);
|
this._addLiteralValuesForTargetType(
|
||||||
|
declaredTypeOfTarget,
|
||||||
|
priorText,
|
||||||
|
priorWord,
|
||||||
|
postText,
|
||||||
|
completionList
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Make sure we are not inside of the string literal.
|
|
||||||
debug.assert(parseNode.nodeType === ParseNodeType.String);
|
debug.assert(parseNode.nodeType === ParseNodeType.String);
|
||||||
|
|
||||||
const offset = convertPositionToOffset(this._position, this._parseResults.tokenizerOutput.lines)!;
|
const offset = convertPositionToOffset(this._position, this._parseResults.tokenizerOutput.lines)!;
|
||||||
if (offset <= parentNode.start || TextRange.getEnd(parseNode) <= offset) {
|
const atArgument = parentNode.start < offset && offset < TextRange.getEnd(parseNode);
|
||||||
this._addCallArgumentCompletions(parseNode, priorWord, priorText, postText, completionList);
|
this._addCallArgumentCompletions(parseNode, priorWord, priorText, postText, atArgument, completionList);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { completionList };
|
return { completionList };
|
||||||
|
@ -30,6 +30,7 @@ import { versionFromString } from './common/pythonVersion';
|
|||||||
import { PyrightFileSystem } from './pyrightFileSystem';
|
import { PyrightFileSystem } from './pyrightFileSystem';
|
||||||
import { PackageTypeReport, TypeKnownStatus } from './analyzer/packageTypeReport';
|
import { PackageTypeReport, TypeKnownStatus } from './analyzer/packageTypeReport';
|
||||||
import { createDeferred } from './common/deferred';
|
import { createDeferred } from './common/deferred';
|
||||||
|
import { FullAccessHost } from './common/fullAccessHost';
|
||||||
|
|
||||||
const toolName = 'pyright';
|
const toolName = 'pyright';
|
||||||
|
|
||||||
@ -269,8 +270,7 @@ async function processArgs(): Promise<ExitStatus> {
|
|||||||
options.watchForSourceChanges = watch;
|
options.watchForSourceChanges = watch;
|
||||||
options.watchForConfigChanges = watch;
|
options.watchForConfigChanges = watch;
|
||||||
|
|
||||||
const service = new AnalyzerService('<default>', fileSystem, output);
|
const service = new AnalyzerService('<default>', fileSystem, output, () => new FullAccessHost(fileSystem));
|
||||||
|
|
||||||
const exitStatus = createDeferred<ExitStatus>();
|
const exitStatus = createDeferred<ExitStatus>();
|
||||||
|
|
||||||
service.setCompletionCallback((results) => {
|
service.setCompletionCallback((results) => {
|
||||||
|
@ -16,14 +16,19 @@ import {
|
|||||||
} from 'vscode-languageserver';
|
} from 'vscode-languageserver';
|
||||||
|
|
||||||
import { AnalysisResults } from './analyzer/analysis';
|
import { AnalysisResults } from './analyzer/analysis';
|
||||||
|
import { ImportResolver } from './analyzer/importResolver';
|
||||||
import { isPythonBinary } from './analyzer/pythonPathUtils';
|
import { isPythonBinary } from './analyzer/pythonPathUtils';
|
||||||
import { BackgroundAnalysis } from './backgroundAnalysis';
|
import { BackgroundAnalysis } from './backgroundAnalysis';
|
||||||
import { BackgroundAnalysisBase } from './backgroundAnalysisBase';
|
import { BackgroundAnalysisBase } from './backgroundAnalysisBase';
|
||||||
import { CommandController } from './commands/commandController';
|
import { CommandController } from './commands/commandController';
|
||||||
import { getCancellationFolderName } from './common/cancellationUtils';
|
import { getCancellationFolderName } from './common/cancellationUtils';
|
||||||
import { LogLevel } from './common/console';
|
import { ConfigOptions } from './common/configOptions';
|
||||||
|
import { ConsoleWithLogLevel, LogLevel } from './common/console';
|
||||||
import { isDebugMode, isString } from './common/core';
|
import { isDebugMode, isString } from './common/core';
|
||||||
import { FileBasedCancellationProvider } from './common/fileBasedCancellationUtils';
|
import { FileBasedCancellationProvider } from './common/fileBasedCancellationUtils';
|
||||||
|
import { FileSystem } from './common/fileSystem';
|
||||||
|
import { FullAccessHost } from './common/fullAccessHost';
|
||||||
|
import { Host } from './common/host';
|
||||||
import { convertUriToPath, resolvePaths } from './common/pathUtils';
|
import { convertUriToPath, resolvePaths } from './common/pathUtils';
|
||||||
import { ProgressReporter } from './common/progressReporter';
|
import { ProgressReporter } from './common/progressReporter';
|
||||||
import { createFromRealFileSystem, WorkspaceFileWatcherProvider } from './common/realFileSystem';
|
import { createFromRealFileSystem, WorkspaceFileWatcherProvider } from './common/realFileSystem';
|
||||||
@ -45,9 +50,10 @@ export class PyrightServer extends LanguageServerBase {
|
|||||||
// be __dirname.
|
// be __dirname.
|
||||||
const rootDirectory = (global as any).__rootDirectory || __dirname;
|
const rootDirectory = (global as any).__rootDirectory || __dirname;
|
||||||
|
|
||||||
|
const console = new ConsoleWithLogLevel(connection.console);
|
||||||
const workspaceMap = new WorkspaceMap();
|
const workspaceMap = new WorkspaceMap();
|
||||||
const fileWatcherProvider = new WorkspaceFileWatcherProvider(workspaceMap);
|
const fileWatcherProvider = new WorkspaceFileWatcherProvider(workspaceMap, console);
|
||||||
const fileSystem = createFromRealFileSystem(connection.console, fileWatcherProvider);
|
const fileSystem = createFromRealFileSystem(console, fileWatcherProvider);
|
||||||
|
|
||||||
super(
|
super(
|
||||||
{
|
{
|
||||||
@ -61,7 +67,8 @@ export class PyrightServer extends LanguageServerBase {
|
|||||||
maxAnalysisTimeInForeground,
|
maxAnalysisTimeInForeground,
|
||||||
supportedCodeActions: [CodeActionKind.QuickFix, CodeActionKind.SourceOrganizeImports],
|
supportedCodeActions: [CodeActionKind.QuickFix, CodeActionKind.SourceOrganizeImports],
|
||||||
},
|
},
|
||||||
connection
|
connection,
|
||||||
|
console
|
||||||
);
|
);
|
||||||
|
|
||||||
this._controller = new CommandController(this);
|
this._controller = new CommandController(this);
|
||||||
@ -211,6 +218,14 @@ export class PyrightServer extends LanguageServerBase {
|
|||||||
return new BackgroundAnalysis(this.console);
|
return new BackgroundAnalysis(this.console);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override createHost() {
|
||||||
|
return new FullAccessHost(this.fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override createImportResolver(fs: FileSystem, options: ConfigOptions, host: Host): ImportResolver {
|
||||||
|
return new ImportResolver(fs, options, host);
|
||||||
|
}
|
||||||
|
|
||||||
protected executeCommand(params: ExecuteCommandParams, token: CancellationToken): Promise<any> {
|
protected executeCommand(params: ExecuteCommandParams, token: CancellationToken): Promise<any> {
|
||||||
return this._controller.execute(params, token);
|
return this._controller.execute(params, token);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import { AnalyzerService } from '../analyzer/service';
|
|||||||
import { CommandLineOptions } from '../common/commandLineOptions';
|
import { CommandLineOptions } from '../common/commandLineOptions';
|
||||||
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
||||||
import { NullConsole } from '../common/console';
|
import { NullConsole } from '../common/console';
|
||||||
|
import { NoAccessHost } from '../common/host';
|
||||||
import { combinePaths, getBaseFileName, normalizePath, normalizeSlashes } from '../common/pathUtils';
|
import { combinePaths, getBaseFileName, normalizePath, normalizeSlashes } from '../common/pathUtils';
|
||||||
import { PythonVersion } from '../common/pythonVersion';
|
import { PythonVersion } from '../common/pythonVersion';
|
||||||
import { createFromRealFileSystem } from '../common/realFileSystem';
|
import { createFromRealFileSystem } from '../common/realFileSystem';
|
||||||
@ -190,7 +191,7 @@ test('PythonPlatform', () => {
|
|||||||
"extraPaths" : []
|
"extraPaths" : []
|
||||||
}]}`);
|
}]}`);
|
||||||
|
|
||||||
configOptions.initializeFromJson(json, undefined, nullConsole);
|
configOptions.initializeFromJson(json, undefined, nullConsole, new NoAccessHost());
|
||||||
|
|
||||||
const env = configOptions.executionEnvironments[0];
|
const env = configOptions.executionEnvironments[0];
|
||||||
assert.strictEqual(env.pythonPlatform, 'platform');
|
assert.strictEqual(env.pythonPlatform, 'platform');
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
import { combinePaths, normalizeSlashes } from '../common/pathUtils';
|
import { combinePaths, normalizeSlashes } from '../common/pathUtils';
|
||||||
import * as host from './harness/host';
|
import * as host from './harness/testHost';
|
||||||
import * as factory from './harness/vfs/factory';
|
import * as factory from './harness/vfs/factory';
|
||||||
import * as vfs from './harness/vfs/filesystem';
|
import * as vfs from './harness/vfs/filesystem';
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import { combinePaths, getBaseFileName, normalizeSlashes } from '../common/pathU
|
|||||||
import { compareStringsCaseSensitive } from '../common/stringUtils';
|
import { compareStringsCaseSensitive } from '../common/stringUtils';
|
||||||
import { parseTestData } from './harness/fourslash/fourSlashParser';
|
import { parseTestData } from './harness/fourslash/fourSlashParser';
|
||||||
import { CompilerSettings } from './harness/fourslash/fourSlashTypes';
|
import { CompilerSettings } from './harness/fourslash/fourSlashTypes';
|
||||||
import * as host from './harness/host';
|
import * as host from './harness/testHost';
|
||||||
import * as factory from './harness/vfs/factory';
|
import * as factory from './harness/vfs/factory';
|
||||||
|
|
||||||
test('GlobalOptions', () => {
|
test('GlobalOptions', () => {
|
||||||
|
@ -11,7 +11,7 @@ import * as path from 'path';
|
|||||||
|
|
||||||
import { normalizeSlashes } from '../common/pathUtils';
|
import { normalizeSlashes } from '../common/pathUtils';
|
||||||
import { runFourSlashTest } from './harness/fourslash/runner';
|
import { runFourSlashTest } from './harness/fourslash/runner';
|
||||||
import * as host from './harness/host';
|
import * as host from './harness/testHost';
|
||||||
import { MODULE_PATH } from './harness/vfs/filesystem';
|
import { MODULE_PATH } from './harness/vfs/filesystem';
|
||||||
|
|
||||||
describe('fourslash tests', () => {
|
describe('fourslash tests', () => {
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
/// <reference path="fourslash.ts" />
|
||||||
|
|
||||||
|
// @filename: test.py
|
||||||
|
//// from typing import Literal
|
||||||
|
////
|
||||||
|
//// def thing(foo: Literal["hello", "world"]):
|
||||||
|
//// pass
|
||||||
|
////
|
||||||
|
//// thing([|/*marker1*/|])
|
||||||
|
//// thing(hel[|/*marker2*/|])
|
||||||
|
//// thing([|"/*marker3*/"|])
|
||||||
|
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
await helper.verifyCompletion('included', 'markdown', {
|
||||||
|
marker1: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: '"hello"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '"world"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
marker2: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: '"hello"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await helper.verifyCompletion('exact', 'markdown', {
|
||||||
|
marker3: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: '"hello"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker3'), newText: '"hello"' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '"world"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker3'), newText: '"world"' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,161 @@
|
|||||||
|
/// <reference path="fourslash.ts" />
|
||||||
|
|
||||||
|
// @filename: literal_types.py
|
||||||
|
//// from typing import Mapping, Literal
|
||||||
|
////
|
||||||
|
//// d: Mapping[Literal["key", "key2"], int] = { "key" : 1 }
|
||||||
|
//// d[[|/*marker1*/|]]
|
||||||
|
|
||||||
|
// @filename: parameter_mapping.py
|
||||||
|
//// from typing import Mapping, Literal
|
||||||
|
////
|
||||||
|
//// def foo(d: Mapping[Literal["key", "key2"], int]):
|
||||||
|
//// d[[|/*marker2*/|]]
|
||||||
|
|
||||||
|
// @filename: literal_types_mixed.py
|
||||||
|
//// from typing import Mapping, Literal
|
||||||
|
////
|
||||||
|
//// d: Mapping[Literal["key", 1], int] = { "key" : 1 }
|
||||||
|
//// d[[|/*marker3*/|]]
|
||||||
|
|
||||||
|
// @filename: parameter_dict.py
|
||||||
|
//// from typing import Dict, Literal
|
||||||
|
////
|
||||||
|
//// def foo(d: Dict[Literal["key", "key2"], int]):
|
||||||
|
//// d[[|/*marker4*/|]]
|
||||||
|
|
||||||
|
// @filename: literal_types_boolean.py
|
||||||
|
//// from typing import Dict, Literal
|
||||||
|
////
|
||||||
|
//// d: Dict[Literal[True, False], int] = { True: 1, False: 2 }
|
||||||
|
//// d[[|/*marker5*/|]]
|
||||||
|
|
||||||
|
// @filename: literal_types_enum.py
|
||||||
|
//// from typing import Dict, Literal
|
||||||
|
//// from enum import Enum
|
||||||
|
////
|
||||||
|
//// class MyEnum(Enum):
|
||||||
|
//// red = 1
|
||||||
|
//// blue = 2
|
||||||
|
////
|
||||||
|
//// def foo(d: Dict[Literal[MyEnum.red, MyEum.blue], int]):
|
||||||
|
//// d[[|/*marker6/|]]
|
||||||
|
|
||||||
|
// @filename: literal_bytes.py
|
||||||
|
//// from typing import Mapping, Literal
|
||||||
|
////
|
||||||
|
//// d: Mapping[Literal[b"key", b"key2"], int] = { b"key" : 1 }
|
||||||
|
//// d[[|/*marker7*/|]]
|
||||||
|
|
||||||
|
{
|
||||||
|
helper.openFiles(helper.getMarkers().map((m) => m.fileName));
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await helper.verifyCompletion('exact', 'markdown', {
|
||||||
|
marker1: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: '"key"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker1'), newText: '"key"' },
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '"key2"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker1'), newText: '"key2"' },
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
marker2: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: '"key"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker2'), newText: '"key"' },
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '"key2"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker2'), newText: '"key2"' },
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
marker3: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: '"key"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker3'), newText: '"key"' },
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '1',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
marker4: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: '"key"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker4'), newText: '"key"' },
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '"key2"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker4'), newText: '"key2"' },
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
marker5: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: 'True',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'False',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
marker6: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: 'MyEnum.red',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'MyEnum.blue',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
marker7: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: 'b"key"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'b"key2"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
detail: 'Dictionary key',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
@ -25,7 +25,7 @@
|
|||||||
// @filename: dict_key_name_conflicts.py
|
// @filename: dict_key_name_conflicts.py
|
||||||
//// keyString = "key"
|
//// keyString = "key"
|
||||||
//// d = dict(keyString=1)
|
//// d = dict(keyString=1)
|
||||||
//// d[keyStr/*marker6*/]
|
//// d[keyStr[|/*marker6*/|]]
|
||||||
|
|
||||||
// @filename: dict_key_mixed_literals.py
|
// @filename: dict_key_mixed_literals.py
|
||||||
//// d = { "key": 1, 1 + 2: 1 }
|
//// d = { "key": 1, 1 + 2: 1 }
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
/// <reference path="fourslash.ts" />
|
||||||
|
|
||||||
|
// @filename: test.py
|
||||||
|
//// from typing import Literal
|
||||||
|
////
|
||||||
|
//// def method(foo: Literal["'\"", '"\'', "'mixed'"]):
|
||||||
|
//// pass
|
||||||
|
////
|
||||||
|
//// method([|/*marker1*/|])
|
||||||
|
//// method([|"/*marker2*/"|])
|
||||||
|
//// method([|'/*marker3*/'|])
|
||||||
|
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
await helper.verifyCompletion('included', 'markdown', {
|
||||||
|
marker1: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: '"\'\\""',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '"\\"\'"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '"\'mixed\'"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
marker2: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: '"\'\\""',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker2'), newText: '"\'\\""' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '"\\"\'"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker2'), newText: '"\\"\'"' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '"\'mixed\'"',
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker2'), newText: '"\'mixed\'"' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
marker3: {
|
||||||
|
completions: [
|
||||||
|
{
|
||||||
|
label: "'\\'\"'",
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker3'), newText: "'\\'\"'" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "'\"\\''",
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker3'), newText: "'\"\\''" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "'\\'mixed\\''",
|
||||||
|
kind: Consts.CompletionItemKind.Constant,
|
||||||
|
textEdit: { range: helper.getPositionRange('marker3'), newText: "'\\'mixed\\''" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
//// # escaped quotes
|
//// # escaped quotes
|
||||||
//// [|/*marker9*/singleQuotesWithEscapedQuote|]= '\''
|
//// [|/*marker9*/singleQuotesWithEscapedQuote|]= '\''
|
||||||
//// [|/*marker10*/doubleQuotesWithEscapedQuote|]= "\"""
|
//// [|/*marker10*/doubleQuotesWithEscapedQuote|]= "\""
|
||||||
//// [|/*marker11*/tripleQuotesWithEscapedQuote|]= '''\n\'\'\''''
|
//// [|/*marker11*/tripleQuotesWithEscapedQuote|]= '''\n\'\'\''''
|
||||||
//// [|/*marker12*/tripleDoubleQuotesWithEscapedQuote|]= """\n\"\"\"""""
|
//// [|/*marker12*/tripleDoubleQuotesWithEscapedQuote|]= """\n\"\"\""""
|
||||||
|
|
||||||
//// # mixing quotes
|
//// # mixing quotes
|
||||||
//// [|/*marker13*/singleQuotesWithDouble|]= '"'
|
//// [|/*marker13*/singleQuotesWithDouble|]= '"'
|
||||||
@ -40,13 +40,13 @@ helper.verifyHover('markdown', {
|
|||||||
marker7: `\`\`\`python\n(variable) simpleTripleQuotes: Literal['foo\\nbar']\n\`\`\``,
|
marker7: `\`\`\`python\n(variable) simpleTripleQuotes: Literal['foo\\nbar']\n\`\`\``,
|
||||||
marker8: `\`\`\`python\n(variable) simpleTripleDoubleQuotes: Literal['foo\\nbar']\n\`\`\``,
|
marker8: `\`\`\`python\n(variable) simpleTripleDoubleQuotes: Literal['foo\\nbar']\n\`\`\``,
|
||||||
marker9: `\`\`\`python\n(variable) singleQuotesWithEscapedQuote: Literal['\\\'']\n\`\`\``,
|
marker9: `\`\`\`python\n(variable) singleQuotesWithEscapedQuote: Literal['\\\'']\n\`\`\``,
|
||||||
marker10: `\`\`\`python\n(variable) doubleQuotesWithEscapedQuote: Literal['\\\"']\n\`\`\``,
|
marker10: `\`\`\`python\n(variable) doubleQuotesWithEscapedQuote: Literal['"']\n\`\`\``,
|
||||||
marker11: `\`\`\`python\n(variable) tripleQuotesWithEscapedQuote: Literal['\\n\\'\\'\\'']\n\`\`\``,
|
marker11: `\`\`\`python\n(variable) tripleQuotesWithEscapedQuote: Literal['\\n\\'\\'\\'']\n\`\`\``,
|
||||||
marker12: `\`\`\`python\n(variable) tripleDoubleQuotesWithEscapedQuote: Literal['\\n\\\"\\\"\\\"']\n\`\`\``,
|
marker12: `\`\`\`python\n(variable) tripleDoubleQuotesWithEscapedQuote: Literal['\\n"""']\n\`\`\``,
|
||||||
marker13: `\`\`\`python\n(variable) singleQuotesWithDouble: Literal['\\"']\n\`\`\``,
|
marker13: `\`\`\`python\n(variable) singleQuotesWithDouble: Literal['"']\n\`\`\``,
|
||||||
marker14: `\`\`\`python\n(variable) singleQuotesWithTripleDouble: Literal['\\"\\"\\"']\n\`\`\``,
|
marker14: `\`\`\`python\n(variable) singleQuotesWithTripleDouble: Literal['"""']\n\`\`\``,
|
||||||
marker15: `\`\`\`python\n(variable) singleTripleQuoteWithSingleAndDoubleQuote: Literal[' \\'\\"\\' ']\n\`\`\``,
|
marker15: `\`\`\`python\n(variable) singleTripleQuoteWithSingleAndDoubleQuote: Literal[' \\'"\\' ']\n\`\`\``,
|
||||||
marker16: `\`\`\`python\n(variable) html: Literal['<!DOCTYPE html><html lang=\\"en\\">\\n<head><title>Title</title></head></html>']\n\`\`\``,
|
marker16: `\`\`\`python\n(variable) html: Literal['<!DOCTYPE html><html lang="en">\\n<head><title>Title</title></head></html>']\n\`\`\``,
|
||||||
marker17: `\`\`\`python\n(variable) htmlWithSingleQuotes: Literal['<!DOCTYPE html><html lang=\\"en\\">\\n<head><title>Title\\'s</title></head></html>']\n\`\`\``,
|
marker17: `\`\`\`python\n(variable) htmlWithSingleQuotes: Literal['<!DOCTYPE html><html lang="en">\\n<head><title>Title\\'s</title></head></html>']\n\`\`\``,
|
||||||
marker18: `\`\`\`python\n(variable) htmlWithTripleEscapedQuotes: Literal['<!DOCTYPE html><html lang=\\"en\\">\\n<head><title>Title\\'\\'\\'s</title></head></html>']\n\`\`\``,
|
marker18: `\`\`\`python\n(variable) htmlWithTripleEscapedQuotes: Literal['<!DOCTYPE html><html lang="en">\\n<head><title>Title\\'\\'\\'s</title></head></html>']\n\`\`\``,
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import { combinePaths } from '../../../common/pathUtils';
|
import { combinePaths } from '../../../common/pathUtils';
|
||||||
import * as host from '../host';
|
import * as host from '../testHost';
|
||||||
import { parseTestData } from './fourSlashParser';
|
import { parseTestData } from './fourSlashParser';
|
||||||
import { FourSlashData } from './fourSlashTypes';
|
import { FourSlashData } from './fourSlashTypes';
|
||||||
import { HostSpecificFeatures, TestState } from './testState';
|
import { HostSpecificFeatures, TestState } from './testState';
|
||||||
|
@ -34,6 +34,7 @@ import * as debug from '../../../common/debug';
|
|||||||
import { createDeferred } from '../../../common/deferred';
|
import { createDeferred } from '../../../common/deferred';
|
||||||
import { DiagnosticCategory } from '../../../common/diagnostic';
|
import { DiagnosticCategory } from '../../../common/diagnostic';
|
||||||
import { FileEditAction } from '../../../common/editAction';
|
import { FileEditAction } from '../../../common/editAction';
|
||||||
|
import { NoAccessHost } from '../../../common/host';
|
||||||
import {
|
import {
|
||||||
combinePaths,
|
combinePaths,
|
||||||
comparePaths,
|
comparePaths,
|
||||||
@ -54,7 +55,7 @@ import { convertHoverResults } from '../../../languageService/hoverProvider';
|
|||||||
import { ParseResults } from '../../../parser/parser';
|
import { ParseResults } from '../../../parser/parser';
|
||||||
import { Tokenizer } from '../../../parser/tokenizer';
|
import { Tokenizer } from '../../../parser/tokenizer';
|
||||||
import { PyrightFileSystem } from '../../../pyrightFileSystem';
|
import { PyrightFileSystem } from '../../../pyrightFileSystem';
|
||||||
import * as host from '../host';
|
import * as host from '../testHost';
|
||||||
import { stringify } from '../utils';
|
import { stringify } from '../utils';
|
||||||
import { createFromFileSystem } from '../vfs/factory';
|
import { createFromFileSystem } from '../vfs/factory';
|
||||||
import * as vfs from '../vfs/filesystem';
|
import * as vfs from '../vfs/filesystem';
|
||||||
@ -136,7 +137,7 @@ export class TestState {
|
|||||||
throw new Error(`Failed to parse test ${file.fileName}: ${e.message}`);
|
throw new Error(`Failed to parse test ${file.fileName}: ${e.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
configOptions.initializeFromJson(this.rawConfigJson, 'basic', nullConsole);
|
configOptions.initializeFromJson(this.rawConfigJson, 'basic', nullConsole, new NoAccessHost());
|
||||||
this._applyTestConfigOptions(configOptions);
|
this._applyTestConfigOptions(configOptions);
|
||||||
} else {
|
} else {
|
||||||
files[file.fileName] = new vfs.File(file.content, { meta: file.fileOptions, encoding: 'utf8' });
|
files[file.fileName] = new vfs.File(file.content, { meta: file.fileOptions, encoding: 'utf8' });
|
||||||
@ -1554,7 +1555,14 @@ export class TestState {
|
|||||||
configOptions: ConfigOptions
|
configOptions: ConfigOptions
|
||||||
) {
|
) {
|
||||||
// we do not initiate automatic analysis or file watcher in test.
|
// we do not initiate automatic analysis or file watcher in test.
|
||||||
const service = new AnalyzerService('test service', this.fs, nullConsole, importResolverFactory, configOptions);
|
const service = new AnalyzerService(
|
||||||
|
'test service',
|
||||||
|
this.fs,
|
||||||
|
nullConsole,
|
||||||
|
() => new NoAccessHost(),
|
||||||
|
importResolverFactory,
|
||||||
|
configOptions
|
||||||
|
);
|
||||||
|
|
||||||
// directly set files to track rather than using fileSpec from config
|
// directly set files to track rather than using fileSpec from config
|
||||||
// to discover those files from file system
|
// to discover those files from file system
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import * as pathConsts from '../../../common/pathConsts';
|
import * as pathConsts from '../../../common/pathConsts';
|
||||||
import { combinePaths, getDirectoryPath, normalizeSlashes, resolvePaths } from '../../../common/pathUtils';
|
import { combinePaths, getDirectoryPath, normalizeSlashes, resolvePaths } from '../../../common/pathUtils';
|
||||||
import { GlobalMetadataOptionNames } from '../fourslash/fourSlashTypes';
|
import { GlobalMetadataOptionNames } from '../fourslash/fourSlashTypes';
|
||||||
import { TestHost } from '../host';
|
import { TestHost } from '../testHost';
|
||||||
import { bufferFrom } from '../utils';
|
import { bufferFrom } from '../utils';
|
||||||
import {
|
import {
|
||||||
FileSet,
|
FileSet,
|
||||||
|
@ -8,6 +8,7 @@ import assert from 'assert';
|
|||||||
|
|
||||||
import { ImportResolver } from '../analyzer/importResolver';
|
import { ImportResolver } from '../analyzer/importResolver';
|
||||||
import { ConfigOptions } from '../common/configOptions';
|
import { ConfigOptions } from '../common/configOptions';
|
||||||
|
import { FullAccessHost } from '../common/fullAccessHost';
|
||||||
import { lib, sitePackages, typeshedFallback } from '../common/pathConsts';
|
import { lib, sitePackages, typeshedFallback } from '../common/pathConsts';
|
||||||
import { combinePaths, getDirectoryPath, normalizeSlashes } from '../common/pathUtils';
|
import { combinePaths, getDirectoryPath, normalizeSlashes } from '../common/pathUtils';
|
||||||
import { PyrightFileSystem } from '../pyrightFileSystem';
|
import { PyrightFileSystem } from '../pyrightFileSystem';
|
||||||
@ -100,7 +101,7 @@ test('side by side files', () => {
|
|||||||
|
|
||||||
const fs = createFileSystem(files);
|
const fs = createFileSystem(files);
|
||||||
const configOptions = getConfigOption(fs);
|
const configOptions = getConfigOption(fs);
|
||||||
const importResolver = new ImportResolver(fs, configOptions);
|
const importResolver = new ImportResolver(fs, configOptions, new FullAccessHost(fs));
|
||||||
|
|
||||||
// Real side by side stub file win over virtual one.
|
// Real side by side stub file win over virtual one.
|
||||||
const sideBySideResult = importResolver.resolveImport(myFile, configOptions.findExecEnvironment(myFile), {
|
const sideBySideResult = importResolver.resolveImport(myFile, configOptions.findExecEnvironment(myFile), {
|
||||||
@ -350,7 +351,7 @@ function getImportResult(
|
|||||||
const configOptions = getConfigOption(fs);
|
const configOptions = getConfigOption(fs);
|
||||||
setup(configOptions);
|
setup(configOptions);
|
||||||
|
|
||||||
const importResolver = new ImportResolver(fs, configOptions);
|
const importResolver = new ImportResolver(fs, configOptions, new FullAccessHost(fs));
|
||||||
const importResult = importResolver.resolveImport(file, configOptions.findExecEnvironment(file), {
|
const importResult = importResolver.resolveImport(file, configOptions.findExecEnvironment(file), {
|
||||||
leadingDots: 0,
|
leadingDots: 0,
|
||||||
nameParts: nameParts,
|
nameParts: nameParts,
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import { ImportResolver } from '../analyzer/importResolver';
|
import { ImportResolver } from '../analyzer/importResolver';
|
||||||
import { SourceFile } from '../analyzer/sourceFile';
|
import { SourceFile } from '../analyzer/sourceFile';
|
||||||
import { ConfigOptions } from '../common/configOptions';
|
import { ConfigOptions } from '../common/configOptions';
|
||||||
|
import { FullAccessHost } from '../common/fullAccessHost';
|
||||||
import { combinePaths } from '../common/pathUtils';
|
import { combinePaths } from '../common/pathUtils';
|
||||||
import { createFromRealFileSystem } from '../common/realFileSystem';
|
import { createFromRealFileSystem } from '../common/realFileSystem';
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ test('Empty', () => {
|
|||||||
const fs = createFromRealFileSystem();
|
const fs = createFromRealFileSystem();
|
||||||
const sourceFile = new SourceFile(fs, filePath, '', false, false);
|
const sourceFile = new SourceFile(fs, filePath, '', false, false);
|
||||||
const configOptions = new ConfigOptions(process.cwd());
|
const configOptions = new ConfigOptions(process.cwd());
|
||||||
const importResolver = new ImportResolver(fs, configOptions);
|
const importResolver = new ImportResolver(fs, configOptions, new FullAccessHost(fs));
|
||||||
|
|
||||||
sourceFile.parse(configOptions, importResolver);
|
sourceFile.parse(configOptions, importResolver);
|
||||||
});
|
});
|
||||||
|
@ -21,6 +21,7 @@ import { cloneDiagnosticRuleSet, ConfigOptions, ExecutionEnvironment } from '../
|
|||||||
import { fail } from '../common/debug';
|
import { fail } from '../common/debug';
|
||||||
import { Diagnostic, DiagnosticCategory } from '../common/diagnostic';
|
import { Diagnostic, DiagnosticCategory } from '../common/diagnostic';
|
||||||
import { DiagnosticSink, TextRangeDiagnosticSink } from '../common/diagnosticSink';
|
import { DiagnosticSink, TextRangeDiagnosticSink } from '../common/diagnosticSink';
|
||||||
|
import { FullAccessHost } from '../common/fullAccessHost';
|
||||||
import { createFromRealFileSystem } from '../common/realFileSystem';
|
import { createFromRealFileSystem } from '../common/realFileSystem';
|
||||||
import { ParseOptions, Parser, ParseResults } from '../parser/parser';
|
import { ParseOptions, Parser, ParseResults } from '../parser/parser';
|
||||||
|
|
||||||
@ -151,7 +152,9 @@ export function typeAnalyzeSampleFiles(
|
|||||||
): FileAnalysisResult[] {
|
): FileAnalysisResult[] {
|
||||||
// Always enable "test mode".
|
// Always enable "test mode".
|
||||||
configOptions.internalTestMode = true;
|
configOptions.internalTestMode = true;
|
||||||
const importResolver = new ImportResolver(createFromRealFileSystem(), configOptions);
|
|
||||||
|
const fs = createFromRealFileSystem();
|
||||||
|
const importResolver = new ImportResolver(fs, configOptions, new FullAccessHost(fs));
|
||||||
|
|
||||||
const program = new Program(importResolver, configOptions);
|
const program = new Program(importResolver, configOptions);
|
||||||
const filePaths = fileNames.map((name) => resolveSampleFilePath(name));
|
const filePaths = fileNames.map((name) => resolveSampleFilePath(name));
|
||||||
|
Loading…
Reference in New Issue
Block a user