mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Upgrade dependencies, fix first line code actions, progress bar tweak, logging, package scanning (#743)
Rollup of: - Upgrade dependencies, including an LSP bump for new features. - Fix code actions that appear on the first line. - Tweak progress bar displaying to display less often for behind-the-scenes analysis. - Make leveled logging more consistent, configurable. - Add package scanner for auto-importing top-level package names. - Fix docstring fetching in relative import cases, `-stub` packages.
This commit is contained in:
parent
f28cf16fa1
commit
5b187d19fa
88
client/package-lock.json
generated
88
client/package-lock.json
generated
@ -11,9 +11,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "13.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.11.1.tgz",
|
||||
"integrity": "sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==",
|
||||
"version": "14.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz",
|
||||
"integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==",
|
||||
"dev": true
|
||||
},
|
||||
"agent-base": {
|
||||
@ -380,9 +380,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"entities": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
|
||||
"integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
|
||||
"integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@ -597,9 +597,9 @@
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
|
||||
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
@ -674,9 +674,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz",
|
||||
"integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==",
|
||||
"version": "3.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz",
|
||||
"integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==",
|
||||
"dev": true
|
||||
},
|
||||
"uc.micro": {
|
||||
@ -704,9 +704,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"vsce": {
|
||||
"version": "1.75.0",
|
||||
"resolved": "https://registry.npmjs.org/vsce/-/vsce-1.75.0.tgz",
|
||||
"integrity": "sha512-qyAQTmolxKWc9bV1z0yBTSH4WEIWhDueBJMKB0GUFD6lM4MiaU1zJ9BtzekUORZu094YeNSKz0RmVVuxfqPq0g==",
|
||||
"version": "1.76.1",
|
||||
"resolved": "https://registry.npmjs.org/vsce/-/vsce-1.76.1.tgz",
|
||||
"integrity": "sha512-WNx6JzRywxAOuhVpjmrsI0eHMK0mCA0YKD8u++7sprmhwCHsoQIBpSf0vp6kVMHBmafknr1Z6K7IC5jIjsNL9Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"azure-devops-node-api": "^7.2.0",
|
||||
@ -747,47 +747,61 @@
|
||||
}
|
||||
},
|
||||
"vscode-jsonrpc": {
|
||||
"version": "5.1.0-next.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.1.0-next.1.tgz",
|
||||
"integrity": "sha512-mwLDojZkbmpizSJSmp690oa9FB9jig18SIDGZeBCvFc2/LYSRvMm/WwWtMBJuJ1MfFh7rZXfQige4Uje5Z9NzA=="
|
||||
"version": "6.0.0-next.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.3.tgz",
|
||||
"integrity": "sha512-bkAtRMXbTwU1pAXnlbkAMnrqXZX3q2/zttkcnPZOsDqgoycO2L7GK2aN3K5qCMZmhItwBU185T78Q9YH0v5jEA=="
|
||||
},
|
||||
"vscode-languageclient": {
|
||||
"version": "6.2.0-next.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.2.0-next.2.tgz",
|
||||
"integrity": "sha512-RqOrj+OCZmiMcLgmQSx581O+3rHb/AIFpEL+yoBcqFkqfIwMkoowCChEJGwVe/I8FeEphZ4dzlMm0MPW+p/DNA==",
|
||||
"version": "7.0.0-next.6",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.6.tgz",
|
||||
"integrity": "sha512-3CPGOXCSxpPGONNVg+ZhY4AYyJrjLDJSKJT2acJ6L5mSlzKACFkFd3yQEGVHS/O0v4n/9i/KRNpBGVSR8ngw7Q==",
|
||||
"requires": {
|
||||
"semver": "^6.3.0",
|
||||
"vscode-languageserver-protocol": "3.16.0-next.2"
|
||||
"vscode-languageserver-protocol": "3.16.0-next.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"vscode-jsonrpc": {
|
||||
"version": "6.0.0-next.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.3.tgz",
|
||||
"integrity": "sha512-bkAtRMXbTwU1pAXnlbkAMnrqXZX3q2/zttkcnPZOsDqgoycO2L7GK2aN3K5qCMZmhItwBU185T78Q9YH0v5jEA=="
|
||||
},
|
||||
"vscode-languageserver-protocol": {
|
||||
"version": "3.16.0-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.5.tgz",
|
||||
"integrity": "sha512-MVuLT4d5tdDo/14laViyZY+p1HT7wYDwJdvGmo8mCfkgON5c10AABhtqjD+LccidBcwdbKy+4dwwUUxiNg/8PA==",
|
||||
"requires": {
|
||||
"vscode-jsonrpc": "6.0.0-next.3",
|
||||
"vscode-languageserver-types": "3.16.0-next.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"vscode-languageserver": {
|
||||
"version": "6.2.0-next.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-6.2.0-next.2.tgz",
|
||||
"integrity": "sha512-UCXULa/RmT2zxcb6auNezl9ZIPwYHS15+a0XzybrpT2+xA0RbHEYQCsIQkTRYjGv8cm3yS9iPJMmxLilP+y1Xw==",
|
||||
"version": "7.0.0-next.4",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0-next.4.tgz",
|
||||
"integrity": "sha512-yyFMuKLNkSKYb//6dQHCo0wlDYWkgRPEXoV+/aYXHp9ziIBqQxKJJFrf/rnXYReCz1TroxYMcFGdO5UrBsLAaA==",
|
||||
"requires": {
|
||||
"vscode-languageserver-protocol": "3.16.0-next.2"
|
||||
"vscode-languageserver-protocol": "3.16.0-next.5"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-protocol": {
|
||||
"version": "3.16.0-next.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.2.tgz",
|
||||
"integrity": "sha512-atmkGT/W6tF0cx4SaWFYtFs2UeSeC28RPiap9myv2YZTaTCFvTBEPNWrU5QRKfkyM0tbgtGo6T3UCQ8tkDpjzA==",
|
||||
"version": "3.16.0-next.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.5.tgz",
|
||||
"integrity": "sha512-MVuLT4d5tdDo/14laViyZY+p1HT7wYDwJdvGmo8mCfkgON5c10AABhtqjD+LccidBcwdbKy+4dwwUUxiNg/8PA==",
|
||||
"requires": {
|
||||
"vscode-jsonrpc": "5.1.0-next.1",
|
||||
"vscode-languageserver-types": "3.16.0-next.1"
|
||||
"vscode-jsonrpc": "6.0.0-next.3",
|
||||
"vscode-languageserver-types": "3.16.0-next.2"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-types": {
|
||||
"version": "3.16.0-next.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.1.tgz",
|
||||
"integrity": "sha512-tZFUSbyjUcrh+qQf13ALX4QDdOfDX0cVaBFgy7ktJ0VwS7AW/yRKgGPSxVqqP9OCMNPdqP57O5q47w2pEwfaUg=="
|
||||
"version": "3.16.0-next.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz",
|
||||
"integrity": "sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q=="
|
||||
},
|
||||
"vscode-test": {
|
||||
"version": "0.4.3",
|
||||
@ -846,9 +860,9 @@
|
||||
}
|
||||
},
|
||||
"vscode-uri": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.1.tgz",
|
||||
"integrity": "sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A=="
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz",
|
||||
"integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A=="
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
|
@ -13,7 +13,7 @@
|
||||
"url": "https://github.com/Microsoft/pyright"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.43.0"
|
||||
"vscode": "^1.46.0"
|
||||
},
|
||||
"keywords": [
|
||||
"python"
|
||||
@ -163,14 +163,14 @@
|
||||
"postinstall": "node ./node_modules/vscode/bin/install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^3.9.3",
|
||||
"vsce": "^1.75.0",
|
||||
"typescript": "^3.9.5",
|
||||
"vsce": "^1.76.1",
|
||||
"vscode": "^1.1.37"
|
||||
},
|
||||
"dependencies": {
|
||||
"leven": "^3.1.0",
|
||||
"vscode-languageclient": "^6.2.0-next.2",
|
||||
"vscode-languageserver": "^6.2.0-next.2",
|
||||
"vscode-uri": "^2.1.1"
|
||||
"vscode-languageclient": "^7.0.0-next.6",
|
||||
"vscode-languageserver": "^7.0.0-next.4",
|
||||
"vscode-uri": "^2.1.2"
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,13 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { commands, ExtensionContext, Position, Range, TextEditor, TextEditorEdit } from 'vscode';
|
||||
import { LanguageClient, LanguageClientOptions, ServerOptions, TextEdit, TransportKind } from 'vscode-languageclient';
|
||||
import {
|
||||
LanguageClient,
|
||||
LanguageClientOptions,
|
||||
ServerOptions,
|
||||
TextEdit,
|
||||
TransportKind,
|
||||
} from 'vscode-languageclient/node';
|
||||
|
||||
import { Commands } from '../../server/src/commands/commands';
|
||||
import { FileBasedCancellationStrategy } from './cancellationUtils';
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
import { Progress, ProgressLocation, window } from 'vscode';
|
||||
import { Disposable, LanguageClient } from 'vscode-languageclient';
|
||||
import { Disposable, LanguageClient } from 'vscode-languageclient/node';
|
||||
|
||||
const AnalysisTimeoutInMs = 60000;
|
||||
|
||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@ -92,9 +92,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "13.13.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.9.tgz",
|
||||
"integrity": "sha512-EPZBIGed5gNnfWCiwEIwTE2Jdg4813odnG8iNPMQGrqVxrI+wL68SPtPeCX+ZxGBaA6pKAVc6jaKgP/Q0QzfdQ==",
|
||||
"version": "13.13.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.12.tgz",
|
||||
"integrity": "sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/normalize-package-data": {
|
||||
@ -1725,9 +1725,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz",
|
||||
"integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==",
|
||||
"version": "3.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz",
|
||||
"integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==",
|
||||
"dev": true
|
||||
},
|
||||
"uri-js": {
|
||||
|
@ -24,7 +24,7 @@
|
||||
"package": "npm run install:all && npm run clean && npm run build:serverProd && npm run build:cli && cd client && npx vsce package && cd .."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^13.13.9",
|
||||
"@types/node": "^13.13.12",
|
||||
"@typescript-eslint/eslint-plugin": "^2.34.0",
|
||||
"@typescript-eslint/parser": "^2.34.0",
|
||||
"del-cli": "^3.0.1",
|
||||
@ -32,7 +32,7 @@
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-simple-import-sort": "^5.0.3",
|
||||
"prettier": "2.0.4",
|
||||
"typescript": "^3.9.3"
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
|
2795
server/package-lock.json
generated
2795
server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -25,8 +25,8 @@
|
||||
"command-line-args": "^5.1.1",
|
||||
"leven": "^3.1.0",
|
||||
"typescript-char": "^0.0.0",
|
||||
"vscode-languageserver": "^6.2.0-next.2",
|
||||
"vscode-uri": "^2.1.1"
|
||||
"vscode-languageserver": "^7.0.0-next.4",
|
||||
"vscode-uri": "^2.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chalk": "^2.2.0",
|
||||
@ -34,19 +34,19 @@
|
||||
"@types/command-line-args": "^5.0.0",
|
||||
"@types/fs-extra": "^8.1.1",
|
||||
"@types/jest": "^25.2.3",
|
||||
"@types/node": "^13.13.9",
|
||||
"@types/node": "^13.13.12",
|
||||
"@typescript-eslint/eslint-plugin": "^2.34.0",
|
||||
"@typescript-eslint/parser": "^2.34.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"jest": "^25.5.4",
|
||||
"jest": "^26.0.1",
|
||||
"jest-junit": "^10.0.0",
|
||||
"node-loader": "^0.6.0",
|
||||
"prettier": "2.0.4",
|
||||
"ts-jest": "^25.5.1",
|
||||
"ts-jest": "^26.1.0",
|
||||
"ts-loader": "^7.0.5",
|
||||
"typescript": "^3.9.3",
|
||||
"typescript": "^3.9.5",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.11"
|
||||
},
|
||||
|
@ -78,7 +78,7 @@ export function analyzeProgram(
|
||||
(e.stack ? e.stack.toString() : undefined) ||
|
||||
(typeof e.message === 'string' ? e.message : undefined) ||
|
||||
JSON.stringify(e);
|
||||
console.log('Error performing analysis: ' + message);
|
||||
console.error('Error performing analysis: ' + message);
|
||||
|
||||
callback({
|
||||
diagnostics: [],
|
||||
|
@ -12,15 +12,19 @@ import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
||||
import { FileSystem } from '../common/fileSystem';
|
||||
import {
|
||||
changeAnyExtension,
|
||||
combinePathComponents,
|
||||
combinePaths,
|
||||
containsPath,
|
||||
ensureTrailingDirectorySeparator,
|
||||
getDirectoryPath,
|
||||
getFileExtension,
|
||||
getFileName,
|
||||
getFileSystemEntries,
|
||||
getPathComponents,
|
||||
getRelativePathComponentsFromDirectory,
|
||||
isDirectory,
|
||||
isFile,
|
||||
resolvePaths,
|
||||
stripFileExtension,
|
||||
stripTrailingDirectorySeparator,
|
||||
} from '../common/pathUtils';
|
||||
@ -351,7 +355,7 @@ export class ImportResolver {
|
||||
}
|
||||
|
||||
// Returns the implementation file(s) for the given stub file.
|
||||
getSourceFilesFromStub(stubFilePath: string): string[] {
|
||||
getSourceFilesFromStub(stubFilePath: string, execEnv: ExecutionEnvironment): string[] {
|
||||
const sourceFilePaths: string[] = [];
|
||||
|
||||
// When ImportResolver resolves an import to a stub file, a second resolve is done
|
||||
@ -381,6 +385,71 @@ export class ImportResolver {
|
||||
sourceFilePaths.push(sourceFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceFilePaths.length === 0) {
|
||||
// The stub and the source file may have the same name, but be located
|
||||
// in different folder hierarchies.
|
||||
// Example:
|
||||
// <stubPath>\package\module.pyi
|
||||
// <site-packages>\package\module.py
|
||||
// We get the relative path(s) of the stub to its import root(s),
|
||||
// in theory there can be more than one, then look for source
|
||||
// files in all the import roots using the same relative path(s).
|
||||
const importRootPaths = this.getImportRoots(execEnv, /*useTypeshedVersionedFolders*/ true);
|
||||
|
||||
const relativeStubPaths: string[] = [];
|
||||
for (const importRootPath of importRootPaths) {
|
||||
if (containsPath(importRootPath, stubFilePath, true)) {
|
||||
const parts = getRelativePathComponentsFromDirectory(importRootPath, stubFilePath, true);
|
||||
|
||||
// Note that relative paths have an empty parts[0]
|
||||
if (parts.length > 1) {
|
||||
// Handle the case where the symbol was resolved to a stubs package
|
||||
// rather than the real package. We'll strip off the "-stubs" suffix
|
||||
// in this case.
|
||||
const stubsSuffix = '-stubs';
|
||||
if (parts[1].endsWith(stubsSuffix)) {
|
||||
parts[1] = parts[1].substr(0, parts[1].length - stubsSuffix.length);
|
||||
}
|
||||
|
||||
const relativeStubPath = combinePathComponents(parts);
|
||||
if (relativeStubPath) {
|
||||
relativeStubPaths.push(relativeStubPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const relativeStubPath of relativeStubPaths) {
|
||||
for (const importRootPath of importRootPaths) {
|
||||
const absoluteStubPath = resolvePaths(importRootPath, relativeStubPath);
|
||||
let absoluteSourcePath = changeAnyExtension(absoluteStubPath, '.py');
|
||||
if (this.fileSystem.existsSync(absoluteSourcePath)) {
|
||||
sourceFilePaths.push(absoluteSourcePath);
|
||||
} else {
|
||||
const filePathWithoutExtension = stripFileExtension(absoluteSourcePath);
|
||||
|
||||
if (filePathWithoutExtension.endsWith('__init__')) {
|
||||
// Did not match: <root>/package/__init__.py
|
||||
// Try equivalent: <root>/package.py
|
||||
absoluteSourcePath =
|
||||
filePathWithoutExtension.substr(0, filePathWithoutExtension.length - 9) + '.py';
|
||||
if (this.fileSystem.existsSync(absoluteSourcePath)) {
|
||||
sourceFilePaths.push(absoluteSourcePath);
|
||||
}
|
||||
} else {
|
||||
// Did not match: <root>/package.py
|
||||
// Try equivalent: <root>/package/__init__.py
|
||||
absoluteSourcePath = combinePaths(filePathWithoutExtension, '__init__.py');
|
||||
if (this.fileSystem.existsSync(absoluteSourcePath)) {
|
||||
sourceFilePaths.push(absoluteSourcePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sourceFilePaths;
|
||||
}
|
||||
|
||||
@ -394,7 +463,7 @@ export class ImportResolver {
|
||||
|
||||
const importFailureInfo: string[] = [];
|
||||
|
||||
// Is this ia stdlib typeshed path?
|
||||
// Is this a stdlib typeshed path?
|
||||
const stdLibTypeshedPath = this._getTypeshedPath(true, execEnv, importFailureInfo);
|
||||
if (stdLibTypeshedPath) {
|
||||
moduleName = this._getModuleNameFromPath(stdLibTypeshedPath, filePath, true);
|
||||
@ -466,6 +535,50 @@ export class ImportResolver {
|
||||
return { moduleName: '', importType: ImportType.Local, isLocalTypingsFile };
|
||||
}
|
||||
|
||||
getImportRoots(execEnv: ExecutionEnvironment, useTypeshedVersionedFolders: boolean) {
|
||||
const importFailureInfo: string[] = [];
|
||||
const roots = [];
|
||||
|
||||
const pythonVersion = execEnv.pythonVersion;
|
||||
const minorVersion = pythonVersion & 0xff;
|
||||
const versionFolders = ['2and3', '3'];
|
||||
if (minorVersion > 0) {
|
||||
versionFolders.push(versionToString(0x300 + minorVersion));
|
||||
}
|
||||
|
||||
const stdTypesheds = this._getTypeshedPath(true, execEnv, importFailureInfo);
|
||||
if (stdTypesheds) {
|
||||
if (useTypeshedVersionedFolders) {
|
||||
roots.push(...versionFolders.map((vf) => combinePaths(stdTypesheds, vf)));
|
||||
} else {
|
||||
roots.push(stdTypesheds);
|
||||
}
|
||||
}
|
||||
|
||||
roots.push(execEnv.root);
|
||||
roots.push(...execEnv.extraPaths);
|
||||
|
||||
if (this._configOptions.stubPath) {
|
||||
roots.push(this._configOptions.stubPath);
|
||||
}
|
||||
|
||||
const typesheds = this._getTypeshedPath(false, execEnv, importFailureInfo);
|
||||
if (typesheds) {
|
||||
if (useTypeshedVersionedFolders) {
|
||||
roots.push(...versionFolders.map((vf) => combinePaths(typesheds, vf)));
|
||||
} else {
|
||||
roots.push(typesheds);
|
||||
}
|
||||
}
|
||||
|
||||
const pythonSearchPaths = this._getPythonSearchPaths(execEnv, importFailureInfo);
|
||||
if (pythonSearchPaths.length > 0) {
|
||||
roots.push(...pythonSearchPaths);
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
private _lookUpResultsInCache(
|
||||
execEnv: ExecutionEnvironment,
|
||||
importName: string,
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
} from 'vscode-languageserver';
|
||||
|
||||
import { OperationCanceledException, throwIfCancellationRequested } from '../common/cancellationUtils';
|
||||
import { ConfigOptions } from '../common/configOptions';
|
||||
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
||||
import { ConsoleInterface, StandardConsole } from '../common/console';
|
||||
import { isDebugMode } from '../common/core';
|
||||
import { assert } from '../common/debug';
|
||||
@ -40,6 +40,7 @@ import {
|
||||
AutoImporter,
|
||||
AutoImportResult,
|
||||
buildModuleSymbolsMap,
|
||||
ImportNameMap,
|
||||
ModuleSymbolMap,
|
||||
} from '../languageService/autoImporter';
|
||||
import { HoverResults } from '../languageService/hoverProvider';
|
||||
@ -403,30 +404,30 @@ export class Program {
|
||||
const zeroImportFiles: SourceFile[] = [];
|
||||
|
||||
sortedFiles.forEach((sfInfo) => {
|
||||
this._console.log('');
|
||||
this._console.info('');
|
||||
let filePath = sfInfo.sourceFile.getFilePath();
|
||||
const relPath = getRelativePath(filePath, projectRootDir);
|
||||
if (relPath) {
|
||||
filePath = relPath;
|
||||
}
|
||||
|
||||
this._console.log(`${filePath}`);
|
||||
this._console.info(`${filePath}`);
|
||||
|
||||
this._console.log(
|
||||
this._console.info(
|
||||
` Imports ${sfInfo.imports.length} ` + `file${sfInfo.imports.length === 1 ? '' : 's'}`
|
||||
);
|
||||
if (verbose) {
|
||||
sfInfo.imports.forEach((importInfo) => {
|
||||
this._console.log(` ${importInfo.sourceFile.getFilePath()}`);
|
||||
this._console.info(` ${importInfo.sourceFile.getFilePath()}`);
|
||||
});
|
||||
}
|
||||
|
||||
this._console.log(
|
||||
this._console.info(
|
||||
` Imported by ${sfInfo.importedBy.length} ` + `file${sfInfo.importedBy.length === 1 ? '' : 's'}`
|
||||
);
|
||||
if (verbose) {
|
||||
sfInfo.importedBy.forEach((importInfo) => {
|
||||
this._console.log(` ${importInfo.sourceFile.getFilePath()}`);
|
||||
this._console.info(` ${importInfo.sourceFile.getFilePath()}`);
|
||||
});
|
||||
}
|
||||
|
||||
@ -436,12 +437,12 @@ export class Program {
|
||||
});
|
||||
|
||||
if (zeroImportFiles.length > 0) {
|
||||
this._console.log('');
|
||||
this._console.log(
|
||||
this._console.info('');
|
||||
this._console.info(
|
||||
`${zeroImportFiles.length} file${zeroImportFiles.length === 1 ? '' : 's'}` + ` not explicitly imported`
|
||||
);
|
||||
zeroImportFiles.forEach((importFile) => {
|
||||
this._console.log(` ${importFile.getFilePath()}`);
|
||||
this._console.info(` ${importFile.getFilePath()}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -706,7 +707,7 @@ export class Program {
|
||||
// Don't allow the heap to get close to the 2GB limit imposed by
|
||||
// the OS when running Node in a 32-bit process.
|
||||
if (heapSizeInMb > 1536) {
|
||||
this._console.log(`Emptying type cache to avoid heap overflow. Heap size used: ${heapSizeInMb}MB`);
|
||||
this._console.info(`Emptying type cache to avoid heap overflow. Heap size used: ${heapSizeInMb}MB`);
|
||||
this._createNewEvaluator();
|
||||
}
|
||||
}
|
||||
@ -841,6 +842,7 @@ export class Program {
|
||||
range: Range,
|
||||
similarityLimit: number,
|
||||
nameMap: Map<string, string> | undefined,
|
||||
importMap: ImportNameMap | undefined,
|
||||
token: CancellationToken
|
||||
): AutoImportResult[] {
|
||||
const sourceFileInfo = this._sourceFileMap.get(filePath);
|
||||
@ -876,7 +878,8 @@ export class Program {
|
||||
sourceFile.getFilePath(),
|
||||
this._importResolver,
|
||||
parseTree,
|
||||
map
|
||||
map,
|
||||
importMap
|
||||
);
|
||||
|
||||
// Filter out any name that is already defined in the current scope.
|
||||
@ -956,8 +959,9 @@ export class Program {
|
||||
|
||||
this._bindFile(sourceFileInfo);
|
||||
|
||||
const execEnv = this._configOptions.findExecEnvironment(filePath);
|
||||
return sourceFileInfo.sourceFile.getDefinitionsForPosition(
|
||||
this._createSourceMapper(),
|
||||
this._createSourceMapper(execEnv),
|
||||
position,
|
||||
this._evaluator,
|
||||
token
|
||||
@ -979,8 +983,9 @@ export class Program {
|
||||
|
||||
this._bindFile(sourceFileInfo);
|
||||
|
||||
const execEnv = this._configOptions.findExecEnvironment(filePath);
|
||||
const referencesResult = sourceFileInfo.sourceFile.getDeclarationForPosition(
|
||||
this._createSourceMapper(),
|
||||
this._createSourceMapper(execEnv),
|
||||
position,
|
||||
this._evaluator,
|
||||
token
|
||||
@ -1087,8 +1092,9 @@ export class Program {
|
||||
|
||||
this._bindFile(sourceFileInfo);
|
||||
|
||||
const execEnv = this._configOptions.findExecEnvironment(filePath);
|
||||
return sourceFileInfo.sourceFile.getHoverForPosition(
|
||||
this._createSourceMapper(),
|
||||
this._createSourceMapper(execEnv),
|
||||
position,
|
||||
this._evaluator,
|
||||
token
|
||||
@ -1132,6 +1138,7 @@ export class Program {
|
||||
let completionList = this._runEvaluatorWithCancellationToken(token, () => {
|
||||
this._bindFile(sourceFileInfo);
|
||||
|
||||
const execEnv = this._configOptions.findExecEnvironment(filePath);
|
||||
return sourceFileInfo.sourceFile.getCompletionsForPosition(
|
||||
position,
|
||||
workspacePath,
|
||||
@ -1139,7 +1146,7 @@ export class Program {
|
||||
this._importResolver,
|
||||
this._lookUpImport,
|
||||
this._evaluator,
|
||||
this._createSourceMapper(),
|
||||
this._createSourceMapper(execEnv),
|
||||
() => this._buildModuleSymbolsMap(sourceFileInfo, token),
|
||||
token
|
||||
);
|
||||
@ -1153,7 +1160,7 @@ export class Program {
|
||||
const content = sourceFileInfo.sourceFile.getFileContents();
|
||||
if (pr?.parseTree && content) {
|
||||
const offset = convertPositionToOffset(position, pr.tokenizerOutput.lines);
|
||||
if (offset) {
|
||||
if (offset !== undefined) {
|
||||
completionList = await this._extension.completionListExtension.updateCompletionList(
|
||||
completionList,
|
||||
pr.parseTree,
|
||||
@ -1177,12 +1184,13 @@ export class Program {
|
||||
|
||||
this._bindFile(sourceFileInfo);
|
||||
|
||||
const execEnv = this._configOptions.findExecEnvironment(filePath);
|
||||
sourceFileInfo.sourceFile.resolveCompletionItem(
|
||||
this._configOptions,
|
||||
this._importResolver,
|
||||
this._lookUpImport,
|
||||
this._evaluator,
|
||||
this._createSourceMapper(),
|
||||
this._createSourceMapper(execEnv),
|
||||
() => this._buildModuleSymbolsMap(sourceFileInfo, token),
|
||||
completionItem,
|
||||
token
|
||||
@ -1204,8 +1212,9 @@ export class Program {
|
||||
|
||||
this._bindFile(sourceFileInfo);
|
||||
|
||||
const execEnv = this._configOptions.findExecEnvironment(filePath);
|
||||
const referencesResult = sourceFileInfo.sourceFile.getDeclarationForPosition(
|
||||
this._createSourceMapper(),
|
||||
this._createSourceMapper(execEnv),
|
||||
position,
|
||||
this._evaluator,
|
||||
token
|
||||
@ -1421,9 +1430,10 @@ export class Program {
|
||||
return false;
|
||||
}
|
||||
|
||||
private _createSourceMapper() {
|
||||
private _createSourceMapper(execEnv: ExecutionEnvironment) {
|
||||
const sourceMapper = new SourceMapper(
|
||||
this._importResolver,
|
||||
execEnv,
|
||||
this._evaluator,
|
||||
(stubFilePath: string, implFilePath: string) => {
|
||||
const stubFileInfo = this._sourceFileMap.get(stubFilePath);
|
||||
@ -1517,13 +1527,13 @@ export class Program {
|
||||
}
|
||||
});
|
||||
} else if (options.verboseOutput) {
|
||||
this._console.log(
|
||||
this._console.info(
|
||||
`Could not import '${importResult.importName}' ` +
|
||||
`in file '${sourceFileInfo.sourceFile.getFilePath()}'`
|
||||
);
|
||||
if (importResult.importFailureInfo) {
|
||||
importResult.importFailureInfo.forEach((diag) => {
|
||||
this._console.log(` ${diag}`);
|
||||
this._console.info(` ${diag}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import {
|
||||
} from '../common/pathUtils';
|
||||
import { DocumentRange, Position, Range } from '../common/textRange';
|
||||
import { timingStats } from '../common/timing';
|
||||
import { ImportNameMap } from '../languageService/autoImporter';
|
||||
import { HoverResults } from '../languageService/hoverProvider';
|
||||
import { SignatureHelpResults } from '../languageService/signatureHelpProvider';
|
||||
import { AnalysisCompleteCallback } from './analysis';
|
||||
@ -175,9 +176,10 @@ export class AnalyzerService {
|
||||
range: Range,
|
||||
similarityLimit: number,
|
||||
nameMap: Map<string, string> | undefined,
|
||||
importMap: ImportNameMap | undefined,
|
||||
token: CancellationToken
|
||||
) {
|
||||
return this._program.getAutoImports(filePath, range, similarityLimit, nameMap, token);
|
||||
return this._program.getAutoImports(filePath, range, similarityLimit, nameMap, importMap, token);
|
||||
}
|
||||
|
||||
getDefinitionForPosition(
|
||||
@ -249,11 +251,11 @@ export class AnalyzerService {
|
||||
}
|
||||
|
||||
printStats() {
|
||||
this._console.log('');
|
||||
this._console.log('Analysis stats');
|
||||
this._console.info('');
|
||||
this._console.info('Analysis stats');
|
||||
|
||||
const fileCount = this._program.getFileCount();
|
||||
this._console.log('Total files analyzed: ' + fileCount.toString());
|
||||
this._console.info('Total files analyzed: ' + fileCount.toString());
|
||||
}
|
||||
|
||||
printDependencies(verbose: boolean) {
|
||||
@ -264,6 +266,10 @@ export class AnalyzerService {
|
||||
return this._backgroundAnalysisProgram.getDiagnosticsForRange(filePath, range, token);
|
||||
}
|
||||
|
||||
getConfigOptions() {
|
||||
return this._configOptions;
|
||||
}
|
||||
|
||||
getImportResolver(): ImportResolver {
|
||||
return this._backgroundAnalysisProgram.importResolver;
|
||||
}
|
||||
@ -279,10 +285,6 @@ export class AnalyzerService {
|
||||
}
|
||||
|
||||
// test only APIs
|
||||
get test_configOptions() {
|
||||
return this._configOptions;
|
||||
}
|
||||
|
||||
get test_program() {
|
||||
return this._program;
|
||||
}
|
||||
@ -306,7 +308,7 @@ export class AnalyzerService {
|
||||
// will be used. In this case, all file specs are assumed to be
|
||||
// relative to the current working directory.
|
||||
if (commandLineOptions.configFilePath) {
|
||||
this._console.log('Project cannot be mixed with source files on a command line.');
|
||||
this._console.info('Project cannot be mixed with source files on a command line.');
|
||||
}
|
||||
} else if (commandLineOptions.configFilePath) {
|
||||
// If the config file path was specified, determine whether it's
|
||||
@ -317,7 +319,7 @@ export class AnalyzerService {
|
||||
normalizePath(commandLineOptions.configFilePath)
|
||||
);
|
||||
if (!this._fs.existsSync(configFilePath)) {
|
||||
this._console.log(`Configuration file not found at ${configFilePath}.`);
|
||||
this._console.info(`Configuration file not found at ${configFilePath}.`);
|
||||
configFilePath = commandLineOptions.executionRoot;
|
||||
} else {
|
||||
if (configFilePath.toLowerCase().endsWith('.json')) {
|
||||
@ -326,7 +328,7 @@ export class AnalyzerService {
|
||||
projectRoot = configFilePath;
|
||||
configFilePath = this._findConfigFile(configFilePath);
|
||||
if (!configFilePath) {
|
||||
this._console.log(`Configuration file not found at ${projectRoot}.`);
|
||||
this._console.info(`Configuration file not found at ${projectRoot}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -335,7 +337,7 @@ export class AnalyzerService {
|
||||
if (configFilePath) {
|
||||
projectRoot = getDirectoryPath(configFilePath);
|
||||
} else {
|
||||
this._console.log(`No configuration file found.`);
|
||||
this._console.info(`No configuration file found.`);
|
||||
configFilePath = undefined;
|
||||
}
|
||||
}
|
||||
@ -365,7 +367,7 @@ export class AnalyzerService {
|
||||
|
||||
// If we found a config file, parse it to compute the effective options.
|
||||
if (configFilePath) {
|
||||
this._console.log(`Loading configuration file at ${configFilePath}`);
|
||||
this._console.info(`Loading configuration file at ${configFilePath}`);
|
||||
const configJsonObj = this._parseConfigFile(configFilePath);
|
||||
if (configJsonObj) {
|
||||
configOptions.initializeFromJson(
|
||||
@ -381,14 +383,14 @@ export class AnalyzerService {
|
||||
// If no include paths were provided, assume that all files within
|
||||
// the project should be included.
|
||||
if (configOptions.include.length === 0) {
|
||||
this._console.log(`No include entries specified; assuming ${configFilePath}`);
|
||||
this._console.info(`No include entries specified; assuming ${configFilePath}`);
|
||||
configOptions.include.push(getFileSpec(configFileDir, '.'));
|
||||
}
|
||||
|
||||
// If there was no explicit set of excludes, add a few common ones to avoid long scan times.
|
||||
if (configOptions.exclude.length === 0) {
|
||||
defaultExcludes.forEach((exclude) => {
|
||||
this._console.log(`Auto-excluding ${exclude}`);
|
||||
this._console.info(`Auto-excluding ${exclude}`);
|
||||
configOptions.exclude.push(getFileSpec(configFileDir, exclude));
|
||||
});
|
||||
|
||||
@ -425,7 +427,7 @@ export class AnalyzerService {
|
||||
const settingSource = commandLineOptions.fromVsCodeExtension
|
||||
? 'the VS Code settings'
|
||||
: 'a command-line option';
|
||||
this._console.log(
|
||||
this._console.info(
|
||||
`The ${settingName} has been specified in both the config file and ` +
|
||||
`${settingSource}. The value in the config file (${configOptions.venvPath}) ` +
|
||||
`will take precedence`
|
||||
@ -444,7 +446,7 @@ export class AnalyzerService {
|
||||
}
|
||||
|
||||
if (commandLineOptions.pythonPath) {
|
||||
this._console.log(
|
||||
this._console.info(
|
||||
`Setting pythonPath for service "${this._instanceName}": ` + `"${commandLineOptions.pythonPath}"`
|
||||
);
|
||||
configOptions.pythonPath = commandLineOptions.pythonPath;
|
||||
@ -479,7 +481,7 @@ export class AnalyzerService {
|
||||
// or inconsistent information.
|
||||
if (configOptions.venvPath) {
|
||||
if (!this._fs.existsSync(configOptions.venvPath) || !isDirectory(this._fs, configOptions.venvPath)) {
|
||||
this._console.log(`venvPath ${configOptions.venvPath} is not a valid directory.`);
|
||||
this._console.info(`venvPath ${configOptions.venvPath} is not a valid directory.`);
|
||||
}
|
||||
|
||||
// venvPath without defaultVenv means it won't do anything while resolveImport.
|
||||
@ -490,21 +492,21 @@ export class AnalyzerService {
|
||||
const fullVenvPath = combinePaths(configOptions.venvPath, configOptions.defaultVenv);
|
||||
|
||||
if (!this._fs.existsSync(fullVenvPath) || !isDirectory(this._fs, fullVenvPath)) {
|
||||
this._console.log(
|
||||
this._console.info(
|
||||
`venv ${configOptions.defaultVenv} subdirectory not found ` +
|
||||
`in venv path ${configOptions.venvPath}.`
|
||||
);
|
||||
} else {
|
||||
const importFailureInfo: string[] = [];
|
||||
if (findPythonSearchPaths(this._fs, configOptions, undefined, importFailureInfo) === undefined) {
|
||||
this._console.log(
|
||||
this._console.info(
|
||||
`site-packages directory cannot be located for venvPath ` +
|
||||
`${configOptions.venvPath} and venv ${configOptions.defaultVenv}.`
|
||||
);
|
||||
|
||||
if (configOptions.verboseOutput) {
|
||||
importFailureInfo.forEach((diag) => {
|
||||
this._console.log(` ${diag}`);
|
||||
this._console.info(` ${diag}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -519,22 +521,22 @@ export class AnalyzerService {
|
||||
).paths;
|
||||
if (pythonPaths.length === 0) {
|
||||
if (configOptions.verboseOutput) {
|
||||
this._console.log(`No search paths found for configured python interpreter.`);
|
||||
this._console.info(`No search paths found for configured python interpreter.`);
|
||||
}
|
||||
} else {
|
||||
if (configOptions.verboseOutput) {
|
||||
this._console.log(`Search paths found for configured python interpreter:`);
|
||||
this._console.info(`Search paths found for configured python interpreter:`);
|
||||
pythonPaths.forEach((path) => {
|
||||
this._console.log(` ${path}`);
|
||||
this._console.info(` ${path}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (configOptions.verboseOutput) {
|
||||
if (importFailureInfo.length > 0) {
|
||||
this._console.log(`When attempting to get search paths from python interpreter:`);
|
||||
this._console.info(`When attempting to get search paths from python interpreter:`);
|
||||
importFailureInfo.forEach((diag) => {
|
||||
this._console.log(` ${diag}`);
|
||||
this._console.info(` ${diag}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -543,7 +545,7 @@ export class AnalyzerService {
|
||||
// Is there a reference to a venv? If so, there needs to be a valid venvPath.
|
||||
if (configOptions.defaultVenv || configOptions.executionEnvironments.find((e) => !!e.venv)) {
|
||||
if (!configOptions.venvPath) {
|
||||
this._console.log(`venvPath not specified, so venv settings will be ignored.`);
|
||||
this._console.info(`venvPath not specified, so venv settings will be ignored.`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,13 +554,13 @@ export class AnalyzerService {
|
||||
!this._fs.existsSync(configOptions.typeshedPath) ||
|
||||
!isDirectory(this._fs, configOptions.typeshedPath)
|
||||
) {
|
||||
this._console.log(`typeshedPath ${configOptions.typeshedPath} is not a valid directory.`);
|
||||
this._console.info(`typeshedPath ${configOptions.typeshedPath} is not a valid directory.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (configOptions.stubPath) {
|
||||
if (!this._fs.existsSync(configOptions.stubPath) || !isDirectory(this._fs, configOptions.stubPath)) {
|
||||
this._console.log(`stubPath ${configOptions.stubPath} is not a valid directory.`);
|
||||
this._console.info(`stubPath ${configOptions.stubPath} is not a valid directory.`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -646,7 +648,7 @@ export class AnalyzerService {
|
||||
// We should never get here because we always generate a
|
||||
// default typings path if none was specified.
|
||||
const errMsg = 'No typings path was specified';
|
||||
this._console.log(errMsg);
|
||||
this._console.info(errMsg);
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
const typeStubInputTargetParts = this._typeStubTargetImportName.split('.');
|
||||
@ -705,7 +707,7 @@ export class AnalyzerService {
|
||||
try {
|
||||
configContents = this._fs.readFileSync(configPath, 'utf8');
|
||||
} catch {
|
||||
this._console.log(`Config file "${configPath}" could not be read.`);
|
||||
this._console.info(`Config file "${configPath}" could not be read.`);
|
||||
this._reportConfigParseError();
|
||||
return undefined;
|
||||
}
|
||||
@ -729,7 +731,7 @@ export class AnalyzerService {
|
||||
// resulting in parse errors. We'll give it a little more time and
|
||||
// try again.
|
||||
if (parseAttemptCount++ >= 5) {
|
||||
this._console.log(`Config file "${configPath}" could not be parsed. Verify that JSON is correct.`);
|
||||
this._console.info(`Config file "${configPath}" could not be parsed. Verify that JSON is correct.`);
|
||||
this._reportConfigParseError();
|
||||
return undefined;
|
||||
}
|
||||
@ -815,20 +817,20 @@ export class AnalyzerService {
|
||||
this._backgroundAnalysisProgram.setAllowedThirdPartyImports([this._typeStubTargetImportName]);
|
||||
this._backgroundAnalysisProgram.setTrackedFiles(filesToImport);
|
||||
} else {
|
||||
this._console.log(`Import '${this._typeStubTargetImportName}' not found`);
|
||||
this._console.info(`Import '${this._typeStubTargetImportName}' not found`);
|
||||
}
|
||||
} else {
|
||||
let fileList: string[] = [];
|
||||
this._console.log(`Searching for source files`);
|
||||
this._console.info(`Searching for source files`);
|
||||
fileList = this._getFileNamesFromFileSpecs();
|
||||
|
||||
this._backgroundAnalysisProgram.setTrackedFiles(fileList);
|
||||
this._backgroundAnalysisProgram.markAllFilesDirty(markFilesDirtyUnconditionally);
|
||||
|
||||
if (fileList.length === 0) {
|
||||
this._console.log(`No source files found.`);
|
||||
this._console.info(`No source files found.`);
|
||||
} else {
|
||||
this._console.log(`Found ${fileList.length} ` + `source ${fileList.length === 1 ? 'file' : 'files'}`);
|
||||
this._console.info(`Found ${fileList.length} ` + `source ${fileList.length === 1 ? 'file' : 'files'}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -847,7 +849,7 @@ export class AnalyzerService {
|
||||
const visitDirectory = (absolutePath: string, includeRegExp: RegExp) => {
|
||||
if (this._configOptions.autoExcludeVenv) {
|
||||
if (envMarkers.some((f) => this._fs.existsSync(combinePaths(absolutePath, ...f)))) {
|
||||
this._console.log(`Auto-excluding ${absolutePath}`);
|
||||
this._console.info(`Auto-excluding ${absolutePath}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -895,7 +897,7 @@ export class AnalyzerService {
|
||||
}
|
||||
|
||||
if (!foundFileSpec) {
|
||||
this._console.log(`File or directory "${includeSpec.wildcardRoot}" does not exist.`);
|
||||
this._console.info(`File or directory "${includeSpec.wildcardRoot}" does not exist.`);
|
||||
}
|
||||
});
|
||||
|
||||
@ -925,12 +927,12 @@ export class AnalyzerService {
|
||||
|
||||
try {
|
||||
if (this._verboseOutput) {
|
||||
this._console.log(`Adding fs watcher for directories:\n ${fileList.join('\n')}`);
|
||||
this._console.info(`Adding fs watcher for directories:\n ${fileList.join('\n')}`);
|
||||
}
|
||||
|
||||
this._sourceFileWatcher = this._fs.createFileSystemWatcher(fileList, (event, path) => {
|
||||
if (this._verboseOutput) {
|
||||
this._console.log(`Received fs event '${event}' for path '${path}'`);
|
||||
this._console.info(`Received fs event '${event}' for path '${path}'`);
|
||||
}
|
||||
|
||||
// Delete comes in as a change event, so try to distinguish here.
|
||||
@ -961,7 +963,7 @@ export class AnalyzerService {
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
this._console.log(`Exception caught when installing fs watcher for:\n ${fileList.join('\n')}`);
|
||||
this._console.info(`Exception caught when installing fs watcher for:\n ${fileList.join('\n')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -996,18 +998,18 @@ export class AnalyzerService {
|
||||
if (watchList && watchList.length > 0) {
|
||||
try {
|
||||
if (this._verboseOutput) {
|
||||
this._console.log(`Adding fs watcher for library directories:\n ${watchList.join('\n')}`);
|
||||
this._console.info(`Adding fs watcher for library directories:\n ${watchList.join('\n')}`);
|
||||
}
|
||||
|
||||
this._libraryFileWatcher = this._fs.createFileSystemWatcher(watchList, (event, path) => {
|
||||
if (this._verboseOutput) {
|
||||
this._console.log(`Received fs event '${event}' for path '${path}'`);
|
||||
this._console.info(`Received fs event '${event}' for path '${path}'`);
|
||||
}
|
||||
|
||||
this._scheduleLibraryAnalysis();
|
||||
});
|
||||
} catch {
|
||||
this._console.log(`Exception caught when installing fs watcher for:\n ${watchList.join('\n')}`);
|
||||
this._console.info(`Exception caught when installing fs watcher for:\n ${watchList.join('\n')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1053,7 +1055,7 @@ export class AnalyzerService {
|
||||
if (this._configFilePath) {
|
||||
this._configFileWatcher = this._fs.createFileSystemWatcher([this._configFilePath], (event) => {
|
||||
if (this._verboseOutput) {
|
||||
this._console.log(`Received fs event '${event}' for config file`);
|
||||
this._console.info(`Received fs event '${event}' for config file`);
|
||||
}
|
||||
this._scheduleReloadConfigFile();
|
||||
});
|
||||
@ -1084,7 +1086,7 @@ export class AnalyzerService {
|
||||
if (this._configFilePath) {
|
||||
this._updateConfigFileWatcher();
|
||||
|
||||
this._console.log(`Reloading configuration file at ${this._configFilePath}`);
|
||||
this._console.info(`Reloading configuration file at ${this._configFilePath}`);
|
||||
|
||||
// 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.
|
||||
|
@ -500,7 +500,7 @@ export class SourceFile {
|
||||
(e.stack ? e.stack.toString() : undefined) ||
|
||||
(typeof e.message === 'string' ? e.message : undefined) ||
|
||||
JSON.stringify(e);
|
||||
this._console.log(`An internal error occurred while parsing ${this.getFilePath()}: ` + message);
|
||||
this._console.error(`An internal error occurred while parsing ${this.getFilePath()}: ` + message);
|
||||
|
||||
// Create dummy parse results.
|
||||
this._parseResults = {
|
||||
@ -792,7 +792,7 @@ export class SourceFile {
|
||||
(e.stack ? e.stack.toString() : undefined) ||
|
||||
(typeof e.message === 'string' ? e.message : undefined) ||
|
||||
JSON.stringify(e);
|
||||
this._console.log(
|
||||
this._console.error(
|
||||
`An internal error occurred while performing name binding for ${this.getFilePath()}: ` + message
|
||||
);
|
||||
|
||||
@ -835,7 +835,7 @@ export class SourceFile {
|
||||
(e.stack ? e.stack.toString() : undefined) ||
|
||||
(typeof e.message === 'string' ? e.message : undefined) ||
|
||||
JSON.stringify(e);
|
||||
this._console.log(
|
||||
this._console.error(
|
||||
`An internal error occurred while while performing type checking for ${this.getFilePath()}: ` +
|
||||
message
|
||||
);
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo';
|
||||
import * as ParseTreeUtils from '../analyzer/parseTreeUtils';
|
||||
import { ExecutionEnvironment } from '../common/configOptions';
|
||||
import { getAnyExtensionFromPath } from '../common/pathUtils';
|
||||
import { ClassNode, ModuleNode, ParseNode } from '../parser/parseNodes';
|
||||
import { ClassDeclaration, Declaration, DeclarationType, FunctionDeclaration } from './declaration';
|
||||
@ -21,6 +22,7 @@ export type ShadowFileBinder = (stubFilePath: string, implFilePath: string) => S
|
||||
export class SourceMapper {
|
||||
constructor(
|
||||
private _importResolver: ImportResolver,
|
||||
private _execEnv: ExecutionEnvironment,
|
||||
private _evaluator: TypeEvaluator,
|
||||
private _fileBinder: ShadowFileBinder
|
||||
) {}
|
||||
@ -187,7 +189,7 @@ export class SourceMapper {
|
||||
}
|
||||
|
||||
private _getBoundSourceFiles(stubFilePath: string): SourceFile[] {
|
||||
const paths = this._importResolver.getSourceFilesFromStub(stubFilePath);
|
||||
const paths = this._importResolver.getSourceFilesFromStub(stubFilePath, this._execEnv);
|
||||
return paths.map((fp) => this._fileBinder(stubFilePath, fp)).filter(_isDefined);
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import {
|
||||
throwIfCancellationRequested,
|
||||
} from './common/cancellationUtils';
|
||||
import { ConfigOptions } from './common/configOptions';
|
||||
import { ConsoleInterface } from './common/console';
|
||||
import { ConsoleInterface, log, LogLevel } from './common/console';
|
||||
import * as debug from './common/debug';
|
||||
import { Diagnostic } from './common/diagnostic';
|
||||
import { FileDiagnostics } from './common/diagnosticSink';
|
||||
@ -46,9 +46,11 @@ export class BackgroundAnalysisBase {
|
||||
// global channel to communicate from BG channel to main thread.
|
||||
worker.on('message', (msg: AnalysisResponse) => {
|
||||
switch (msg.requestType) {
|
||||
case 'log':
|
||||
this.log(msg.data);
|
||||
case 'log': {
|
||||
const logData = msg.data as LogData;
|
||||
this.log(logData.level, logData.message);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'analysisResult': {
|
||||
// Change in diagnostics due to host such as file closed rather than
|
||||
@ -65,7 +67,7 @@ export class BackgroundAnalysisBase {
|
||||
// this will catch any exception thrown from background thread,
|
||||
// print log and ignore exception
|
||||
worker.on('error', (msg) => {
|
||||
this.log(`Error occurred on background thread: ${JSON.stringify(msg)}`);
|
||||
this.log(LogLevel.Error, `Error occurred on background thread: ${JSON.stringify(msg)}`);
|
||||
});
|
||||
}
|
||||
|
||||
@ -185,8 +187,8 @@ export class BackgroundAnalysisBase {
|
||||
this._worker.postMessage(request, request.port ? [request.port] : undefined);
|
||||
}
|
||||
|
||||
protected log(msg: string) {
|
||||
this._console.log(msg);
|
||||
protected log(level: LogLevel, msg: string) {
|
||||
log(this._console, level, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,7 +204,7 @@ export class BackgroundAnalysisRunnerBase {
|
||||
|
||||
// Stash the base directory into a global variable.
|
||||
(global as any).__rootDirectory = data.rootDirectory;
|
||||
this.log(`Background analysis root directory: ${data.rootDirectory}`);
|
||||
this.log(LogLevel.Info, `Background analysis root directory: ${data.rootDirectory}`);
|
||||
|
||||
this._fs = createFromRealFileSystem(this._getConsole());
|
||||
|
||||
@ -212,7 +214,7 @@ export class BackgroundAnalysisRunnerBase {
|
||||
}
|
||||
|
||||
start() {
|
||||
this.log(`Background analysis started`);
|
||||
this.log(LogLevel.Info, `Background analysis started`);
|
||||
|
||||
// Get requests from main thread.
|
||||
parentPort?.on('message', (msg: AnalysisRequest) => {
|
||||
@ -355,8 +357,8 @@ export class BackgroundAnalysisRunnerBase {
|
||||
});
|
||||
}
|
||||
|
||||
protected log(msg: string) {
|
||||
parentPort?.postMessage({ requestType: 'log', data: msg });
|
||||
protected log(level: LogLevel, msg: string) {
|
||||
parentPort?.postMessage({ requestType: 'log', data: { level: level, message: msg } });
|
||||
}
|
||||
|
||||
protected createImportResolver(fs: FileSystem, options: ConfigOptions): ImportResolver {
|
||||
@ -388,10 +390,16 @@ export class BackgroundAnalysisRunnerBase {
|
||||
private _getConsole() {
|
||||
return {
|
||||
log: (msg: string) => {
|
||||
this.log(msg);
|
||||
this.log(LogLevel.Log, msg);
|
||||
},
|
||||
info: (msg: string) => {
|
||||
this.log(LogLevel.Info, msg);
|
||||
},
|
||||
warn: (msg: string) => {
|
||||
this.log(LogLevel.Warn, msg);
|
||||
},
|
||||
error: (msg: string) => {
|
||||
this.log(msg);
|
||||
this.log(LogLevel.Error, msg);
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -531,3 +539,8 @@ interface RequestResponse {
|
||||
kind: 'ok' | 'failed' | 'cancelled';
|
||||
data: any;
|
||||
}
|
||||
|
||||
interface LogData {
|
||||
level: LogLevel;
|
||||
message: string;
|
||||
}
|
||||
|
@ -564,14 +564,14 @@ export class ConfigOptions {
|
||||
this.include = [];
|
||||
if (configObj.include !== undefined) {
|
||||
if (!Array.isArray(configObj.include)) {
|
||||
console.log(`Config "include" entry must must contain an array.`);
|
||||
console.info(`Config "include" entry must must contain an array.`);
|
||||
} else {
|
||||
const filesList = configObj.include as string[];
|
||||
filesList.forEach((fileSpec, index) => {
|
||||
if (typeof fileSpec !== 'string') {
|
||||
console.log(`Index ${index} of "include" array should be a string.`);
|
||||
console.info(`Index ${index} of "include" array should be a string.`);
|
||||
} else if (isAbsolute(fileSpec)) {
|
||||
console.log(`Ignoring path "${fileSpec}" in "include" array because it is not relative.`);
|
||||
console.info(`Ignoring path "${fileSpec}" in "include" array because it is not relative.`);
|
||||
} else {
|
||||
this.include.push(getFileSpec(this.projectRoot, fileSpec));
|
||||
}
|
||||
@ -583,14 +583,14 @@ export class ConfigOptions {
|
||||
this.exclude = [];
|
||||
if (configObj.exclude !== undefined) {
|
||||
if (!Array.isArray(configObj.exclude)) {
|
||||
console.log(`Config "exclude" entry must contain an array.`);
|
||||
console.info(`Config "exclude" entry must contain an array.`);
|
||||
} else {
|
||||
const filesList = configObj.exclude as string[];
|
||||
filesList.forEach((fileSpec, index) => {
|
||||
if (typeof fileSpec !== 'string') {
|
||||
console.log(`Index ${index} of "exclude" array should be a string.`);
|
||||
console.info(`Index ${index} of "exclude" array should be a string.`);
|
||||
} else if (isAbsolute(fileSpec)) {
|
||||
console.log(`Ignoring path "${fileSpec}" in "exclude" array because it is not relative.`);
|
||||
console.info(`Ignoring path "${fileSpec}" in "exclude" array because it is not relative.`);
|
||||
} else {
|
||||
this.exclude.push(getFileSpec(this.projectRoot, fileSpec));
|
||||
}
|
||||
@ -602,14 +602,14 @@ export class ConfigOptions {
|
||||
this.ignore = [];
|
||||
if (configObj.ignore !== undefined) {
|
||||
if (!Array.isArray(configObj.ignore)) {
|
||||
console.log(`Config "ignore" entry must contain an array.`);
|
||||
console.info(`Config "ignore" entry must contain an array.`);
|
||||
} else {
|
||||
const filesList = configObj.ignore as string[];
|
||||
filesList.forEach((fileSpec, index) => {
|
||||
if (typeof fileSpec !== 'string') {
|
||||
console.log(`Index ${index} of "ignore" array should be a string.`);
|
||||
console.info(`Index ${index} of "ignore" array should be a string.`);
|
||||
} else if (isAbsolute(fileSpec)) {
|
||||
console.log(`Ignoring path "${fileSpec}" in "ignore" array because it is not relative.`);
|
||||
console.info(`Ignoring path "${fileSpec}" in "ignore" array because it is not relative.`);
|
||||
} else {
|
||||
this.ignore.push(getFileSpec(this.projectRoot, fileSpec));
|
||||
}
|
||||
@ -621,14 +621,14 @@ export class ConfigOptions {
|
||||
this.strict = [];
|
||||
if (configObj.strict !== undefined) {
|
||||
if (!Array.isArray(configObj.strict)) {
|
||||
console.log(`Config "strict" entry must contain an array.`);
|
||||
console.info(`Config "strict" entry must contain an array.`);
|
||||
} else {
|
||||
const filesList = configObj.strict as string[];
|
||||
filesList.forEach((fileSpec, index) => {
|
||||
if (typeof fileSpec !== 'string') {
|
||||
console.log(`Index ${index} of "strict" array should be a string.`);
|
||||
console.info(`Index ${index} of "strict" array should be a string.`);
|
||||
} else if (isAbsolute(fileSpec)) {
|
||||
console.log(`Ignoring path "${fileSpec}" in "strict" array because it is not relative.`);
|
||||
console.info(`Ignoring path "${fileSpec}" in "strict" array because it is not relative.`);
|
||||
} else {
|
||||
this.strict.push(getFileSpec(this.projectRoot, fileSpec));
|
||||
}
|
||||
@ -646,7 +646,7 @@ export class ConfigOptions {
|
||||
) {
|
||||
configTypeCheckingMode = configObj.typeCheckingMode;
|
||||
} else {
|
||||
console.log(`Config "typeCheckingMode" entry must contain "off", "basic", or "strict".`);
|
||||
console.info(`Config "typeCheckingMode" entry must contain "off", "basic", or "strict".`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -953,7 +953,7 @@ export class ConfigOptions {
|
||||
this.venvPath = undefined;
|
||||
if (configObj.venvPath !== undefined) {
|
||||
if (typeof configObj.venvPath !== 'string') {
|
||||
console.log(`Config "venvPath" field must contain a string.`);
|
||||
console.info(`Config "venvPath" field must contain a string.`);
|
||||
} else {
|
||||
this.venvPath = normalizePath(combinePaths(this.projectRoot, configObj.venvPath));
|
||||
}
|
||||
@ -963,7 +963,7 @@ export class ConfigOptions {
|
||||
this.defaultVenv = undefined;
|
||||
if (configObj.venv !== undefined) {
|
||||
if (typeof configObj.venv !== 'string') {
|
||||
console.log(`Config "venv" field must contain a string.`);
|
||||
console.info(`Config "venv" field must contain a string.`);
|
||||
} else {
|
||||
this.defaultVenv = configObj.venv;
|
||||
}
|
||||
@ -977,10 +977,10 @@ export class ConfigOptions {
|
||||
if (version) {
|
||||
this.defaultPythonVersion = version;
|
||||
} else {
|
||||
console.log(`Config "pythonVersion" field contains unsupported version.`);
|
||||
console.info(`Config "pythonVersion" field contains unsupported version.`);
|
||||
}
|
||||
} else {
|
||||
console.log(`Config "pythonVersion" field must contain a string.`);
|
||||
console.info(`Config "pythonVersion" field must contain a string.`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -989,7 +989,7 @@ export class ConfigOptions {
|
||||
if (this.defaultPythonVersion === undefined) {
|
||||
this.defaultPythonVersion = this._getPythonVersionFromPythonInterpreter(pythonPath, console);
|
||||
if (this.defaultPythonVersion !== undefined) {
|
||||
console.log(`Assuming Python version ${versionToString(this.defaultPythonVersion)}`);
|
||||
console.info(`Assuming Python version ${versionToString(this.defaultPythonVersion)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -997,7 +997,7 @@ export class ConfigOptions {
|
||||
this.defaultPythonPlatform = undefined;
|
||||
if (configObj.pythonPlatform !== undefined) {
|
||||
if (typeof configObj.pythonPlatform !== 'string') {
|
||||
console.log(`Config "pythonPlatform" field must contain a string.`);
|
||||
console.info(`Config "pythonPlatform" field must contain a string.`);
|
||||
} else {
|
||||
this.defaultPythonPlatform = configObj.pythonPlatform;
|
||||
}
|
||||
@ -1015,7 +1015,7 @@ export class ConfigOptions {
|
||||
}
|
||||
|
||||
if (this.defaultPythonPlatform !== undefined) {
|
||||
console.log(`Assuming Python platform ${this.defaultPythonPlatform}`);
|
||||
console.info(`Assuming Python platform ${this.defaultPythonPlatform}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1023,7 +1023,7 @@ export class ConfigOptions {
|
||||
this.typeshedPath = undefined;
|
||||
if (configObj.typeshedPath !== undefined) {
|
||||
if (typeof configObj.typeshedPath !== 'string') {
|
||||
console.log(`Config "typeshedPath" field must contain a string.`);
|
||||
console.info(`Config "typeshedPath" field must contain a string.`);
|
||||
} else {
|
||||
this.typeshedPath = configObj.typeshedPath
|
||||
? normalizePath(combinePaths(this.projectRoot, configObj.typeshedPath))
|
||||
@ -1037,16 +1037,16 @@ export class ConfigOptions {
|
||||
// Keep this for backward compatibility
|
||||
if (configObj.typingsPath !== undefined) {
|
||||
if (typeof configObj.typingsPath !== 'string') {
|
||||
console.log(`Config "typingsPath" field must contain a string.`);
|
||||
console.info(`Config "typingsPath" field must contain a string.`);
|
||||
} else {
|
||||
console.log(`Config "typingsPath" is now deprecated. Please, use stubPath instead.`);
|
||||
console.info(`Config "typingsPath" is now deprecated. Please, use stubPath instead.`);
|
||||
this.stubPath = normalizePath(combinePaths(this.projectRoot, configObj.typingsPath));
|
||||
}
|
||||
}
|
||||
|
||||
if (configObj.stubPath !== undefined) {
|
||||
if (typeof configObj.stubPath !== 'string') {
|
||||
console.log(`Config "stubPath" field must contain a string.`);
|
||||
console.info(`Config "stubPath" field must contain a string.`);
|
||||
} else {
|
||||
this.stubPath = normalizePath(combinePaths(this.projectRoot, configObj.stubPath));
|
||||
}
|
||||
@ -1057,7 +1057,7 @@ export class ConfigOptions {
|
||||
// switch to apply if this setting isn't specified in the config file.
|
||||
if (configObj.verboseOutput !== undefined) {
|
||||
if (typeof configObj.verboseOutput !== 'boolean') {
|
||||
console.log(`Config "verboseOutput" field must be true or false.`);
|
||||
console.info(`Config "verboseOutput" field must be true or false.`);
|
||||
} else {
|
||||
this.verboseOutput = configObj.verboseOutput;
|
||||
}
|
||||
@ -1066,7 +1066,7 @@ export class ConfigOptions {
|
||||
// Read the "useLibraryCodeForTypes" setting.
|
||||
if (configObj.useLibraryCodeForTypes !== undefined) {
|
||||
if (typeof configObj.useLibraryCodeForTypes !== 'boolean') {
|
||||
console.log(`Config "useLibraryCodeForTypes" field must be true or false.`);
|
||||
console.info(`Config "useLibraryCodeForTypes" field must be true or false.`);
|
||||
} else {
|
||||
this.useLibraryCodeForTypes = configObj.useLibraryCodeForTypes;
|
||||
}
|
||||
@ -1077,7 +1077,7 @@ export class ConfigOptions {
|
||||
this.executionEnvironments = [];
|
||||
if (configObj.executionEnvironments !== undefined) {
|
||||
if (!Array.isArray(configObj.executionEnvironments)) {
|
||||
console.log(`Config "executionEnvironments" field must contain an array.`);
|
||||
console.info(`Config "executionEnvironments" field must contain an array.`);
|
||||
} else {
|
||||
const execEnvironments = configObj.executionEnvironments as ExecutionEnvironment[];
|
||||
execEnvironments.forEach((env, index) => {
|
||||
@ -1142,18 +1142,20 @@ export class ConfigOptions {
|
||||
if (envObj.root && typeof envObj.root === 'string') {
|
||||
newExecEnv.root = normalizePath(combinePaths(this.projectRoot, envObj.root));
|
||||
} else {
|
||||
console.log(`Config executionEnvironments index ${index}: missing root value.`);
|
||||
console.info(`Config executionEnvironments index ${index}: missing root value.`);
|
||||
}
|
||||
|
||||
// Validate the extraPaths.
|
||||
if (envObj.extraPaths) {
|
||||
if (!Array.isArray(envObj.extraPaths)) {
|
||||
console.log(`Config executionEnvironments index ${index}: extraPaths field must contain an array.`);
|
||||
console.info(
|
||||
`Config executionEnvironments index ${index}: extraPaths field must contain an array.`
|
||||
);
|
||||
} else {
|
||||
const pathList = envObj.extraPaths as string[];
|
||||
pathList.forEach((path, pathIndex) => {
|
||||
if (typeof path !== 'string') {
|
||||
console.log(
|
||||
console.info(
|
||||
`Config executionEnvironments index ${index}:` +
|
||||
` extraPaths field ${pathIndex} must be a string.`
|
||||
);
|
||||
@ -1171,10 +1173,10 @@ export class ConfigOptions {
|
||||
if (version) {
|
||||
newExecEnv.pythonVersion = version;
|
||||
} else {
|
||||
console.log(`Config executionEnvironments index ${index} contains unsupported pythonVersion.`);
|
||||
console.info(`Config executionEnvironments index ${index} contains unsupported pythonVersion.`);
|
||||
}
|
||||
} else {
|
||||
console.log(`Config executionEnvironments index ${index} pythonVersion must be a string.`);
|
||||
console.info(`Config executionEnvironments index ${index} pythonVersion must be a string.`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1183,7 +1185,7 @@ export class ConfigOptions {
|
||||
if (typeof envObj.pythonPlatform === 'string') {
|
||||
newExecEnv.pythonPlatform = envObj.pythonPlatform;
|
||||
} else {
|
||||
console.log(`Config executionEnvironments index ${index} pythonPlatform must be a string.`);
|
||||
console.info(`Config executionEnvironments index ${index} pythonPlatform must be a string.`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1192,13 +1194,13 @@ export class ConfigOptions {
|
||||
if (typeof envObj.venv === 'string') {
|
||||
newExecEnv.venv = envObj.venv;
|
||||
} else {
|
||||
console.log(`Config executionEnvironments index ${index} venv must be a string.`);
|
||||
console.info(`Config executionEnvironments index ${index} venv must be a string.`);
|
||||
}
|
||||
}
|
||||
|
||||
return newExecEnv;
|
||||
} catch {
|
||||
console.log(`Config executionEnvironments index ${index} is not accessible.`);
|
||||
console.info(`Config executionEnvironments index ${index} is not accessible.`);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@ -1221,25 +1223,25 @@ export class ConfigOptions {
|
||||
// Parse the execOutput. It should be something like "Python 3.x.y".
|
||||
const match = execOutput.match(/[0-9]+.[0-9]+.[0-9]+/);
|
||||
if (!match) {
|
||||
console.log(`Unable to get Python version from interpreter. Received "${execOutput}"`);
|
||||
console.info(`Unable to get Python version from interpreter. Received "${execOutput}"`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const versionString = match[0];
|
||||
const version = versionFromString(versionString);
|
||||
if (version === undefined) {
|
||||
console.log(`Unable to get Python version from interpreter. Received "${execOutput}"`);
|
||||
console.info(`Unable to get Python version from interpreter. Received "${execOutput}"`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (version < PythonVersion.V30) {
|
||||
console.log(`Python version from interpreter is unsupported: "${execOutput}"`);
|
||||
console.info(`Python version from interpreter is unsupported: "${execOutput}"`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return version;
|
||||
} catch {
|
||||
console.log('Unable to get Python version from interpreter');
|
||||
console.info('Unable to get Python version from interpreter');
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,20 @@
|
||||
* methods.
|
||||
*/
|
||||
|
||||
import * as debug from './debug';
|
||||
|
||||
export enum LogLevel {
|
||||
Error = 'error',
|
||||
Warn = 'warn',
|
||||
Info = 'info',
|
||||
Log = 'log',
|
||||
}
|
||||
|
||||
export interface ConsoleInterface {
|
||||
log: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
warn: (message: string) => void;
|
||||
info: (message: string) => void;
|
||||
log: (message: string) => void;
|
||||
}
|
||||
|
||||
// Avoids outputting errors to the console but counts
|
||||
@ -18,12 +29,22 @@ export interface ConsoleInterface {
|
||||
// for unit tests.
|
||||
export class NullConsole implements ConsoleInterface {
|
||||
logCount = 0;
|
||||
infoCount = 0;
|
||||
warnCount = 0;
|
||||
errorCount = 0;
|
||||
|
||||
log(message: string) {
|
||||
this.logCount++;
|
||||
}
|
||||
|
||||
info(message: string) {
|
||||
this.infoCount++;
|
||||
}
|
||||
|
||||
warn(message: string) {
|
||||
this.warnCount++;
|
||||
}
|
||||
|
||||
error(message: string) {
|
||||
this.errorCount++;
|
||||
}
|
||||
@ -31,10 +52,108 @@ export class NullConsole implements ConsoleInterface {
|
||||
|
||||
export class StandardConsole implements ConsoleInterface {
|
||||
log(message: string) {
|
||||
console.log(message);
|
||||
console.info(message);
|
||||
}
|
||||
|
||||
info(message: string) {
|
||||
console.info(message);
|
||||
}
|
||||
|
||||
warn(message: string) {
|
||||
console.warn(message);
|
||||
}
|
||||
|
||||
error(message: string) {
|
||||
console.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConsoleWithLogLevel implements ConsoleInterface {
|
||||
private _levelMap: Map<string, number> = new Map([
|
||||
[LogLevel.Error, 0],
|
||||
[LogLevel.Warn, 1],
|
||||
[LogLevel.Info, 2],
|
||||
[LogLevel.Log, 3],
|
||||
]);
|
||||
|
||||
private _maxLevel = 2;
|
||||
|
||||
constructor(private _console: ConsoleInterface) {}
|
||||
|
||||
get level(): LogLevel {
|
||||
switch (this._maxLevel) {
|
||||
case 0:
|
||||
return LogLevel.Error;
|
||||
|
||||
case 1:
|
||||
return LogLevel.Warn;
|
||||
|
||||
case 2:
|
||||
return LogLevel.Info;
|
||||
}
|
||||
|
||||
return LogLevel.Log;
|
||||
}
|
||||
|
||||
set level(value: LogLevel) {
|
||||
let maxLevel = this._levelMap.get(value);
|
||||
if (maxLevel === undefined) {
|
||||
maxLevel = this._levelMap.get(LogLevel.Info)!;
|
||||
}
|
||||
|
||||
this._maxLevel = maxLevel;
|
||||
}
|
||||
|
||||
error(message: string) {
|
||||
this._log(LogLevel.Error, message);
|
||||
}
|
||||
|
||||
warn(message: string) {
|
||||
this._log(LogLevel.Warn, message);
|
||||
}
|
||||
|
||||
info(message: string) {
|
||||
this._log(LogLevel.Info, message);
|
||||
}
|
||||
|
||||
log(message: string) {
|
||||
this._log(LogLevel.Log, message);
|
||||
}
|
||||
|
||||
private _log(level: LogLevel, message: string): void {
|
||||
if (this._getNumericalLevel(level) > this._maxLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
log(this._console, level, message);
|
||||
}
|
||||
|
||||
private _getNumericalLevel(level: LogLevel): number {
|
||||
const numericLevel = this._levelMap.get(level);
|
||||
debug.assert(numericLevel !== undefined, 'Logger: unknown log level.');
|
||||
return numericLevel !== undefined ? numericLevel : 2;
|
||||
}
|
||||
}
|
||||
|
||||
export function log(console: ConsoleInterface, logType: LogLevel, msg: string) {
|
||||
switch (logType) {
|
||||
case LogLevel.Log:
|
||||
console.log(msg);
|
||||
break;
|
||||
|
||||
case LogLevel.Info:
|
||||
console.info(msg);
|
||||
break;
|
||||
|
||||
case LogLevel.Warn:
|
||||
console.warn(msg);
|
||||
break;
|
||||
|
||||
case LogLevel.Error:
|
||||
console.error(msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
debug.fail(`${logType} is not expected`);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
// * NOTE * except tests, this should be only file that import "fs"
|
||||
import * as chokidar from 'chokidar';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
|
||||
import { ConsoleInterface, NullConsole } from './console';
|
||||
import { createDeferred } from './deferred';
|
||||
@ -63,6 +64,7 @@ export interface FileSystem {
|
||||
// Async I/O
|
||||
readFile(path: string): Promise<Buffer>;
|
||||
readFileText(path: string, encoding?: string): Promise<string>;
|
||||
tmpdir(): string;
|
||||
}
|
||||
|
||||
export interface FileWatcherProvider {
|
||||
@ -176,6 +178,10 @@ class RealFileSystem implements FileSystem {
|
||||
});
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
tmpdir() {
|
||||
return os.tmpdir();
|
||||
}
|
||||
}
|
||||
|
||||
class ChokidarFileWatcherProvider implements FileWatcherProvider {
|
||||
@ -223,12 +229,12 @@ class ChokidarFileWatcherProvider implements FileWatcherProvider {
|
||||
|
||||
const watcher = chokidar.watch(paths, watcherOptions);
|
||||
watcher.on('error', (_) => {
|
||||
this._console.log('Error returned from file system watcher.');
|
||||
this._console.error('Error returned from file system watcher.');
|
||||
});
|
||||
|
||||
// Detect if for some reason the native watcher library fails to load
|
||||
if (_isMacintosh && !watcher.options.useFsEvents) {
|
||||
this._console.log('Watcher could not use native fsevents library. File system watcher disabled.');
|
||||
this._console.info('Watcher could not use native fsevents library. File system watcher disabled.');
|
||||
}
|
||||
|
||||
return watcher;
|
||||
|
@ -7,6 +7,7 @@
|
||||
* Pathname utility functions.
|
||||
*/
|
||||
|
||||
import { randomBytes } from 'crypto';
|
||||
import * as path from 'path';
|
||||
import Char from 'typescript-char';
|
||||
import { URI } from 'vscode-uri';
|
||||
@ -450,6 +451,15 @@ export function getRelativePathFromDirectory(
|
||||
fromDirectory: string,
|
||||
to: string,
|
||||
getCanonicalFileNameOrIgnoreCase: GetCanonicalFileName | boolean
|
||||
) {
|
||||
const pathComponents = getRelativePathComponentsFromDirectory(fromDirectory, to, getCanonicalFileNameOrIgnoreCase);
|
||||
return combinePathComponents(pathComponents);
|
||||
}
|
||||
|
||||
export function getRelativePathComponentsFromDirectory(
|
||||
fromDirectory: string,
|
||||
to: string,
|
||||
getCanonicalFileNameOrIgnoreCase: GetCanonicalFileName | boolean
|
||||
) {
|
||||
debug.assert(
|
||||
getRootLength(fromDirectory) > 0 === getRootLength(to) > 0,
|
||||
@ -465,7 +475,7 @@ export function getRelativePathFromDirectory(
|
||||
getCanonicalFileName
|
||||
);
|
||||
|
||||
return combinePathComponents(pathComponents);
|
||||
return pathComponents;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -856,3 +866,44 @@ export function convertUriToPath(uriString: string): string {
|
||||
export function convertPathToUri(path: string): string {
|
||||
return URI.file(path).toString();
|
||||
}
|
||||
|
||||
let _fsCaseSensitivity: boolean | undefined = undefined;
|
||||
export function isFileSystemCaseSensitive(fs: FileSystem) {
|
||||
if (_fsCaseSensitivity !== undefined) {
|
||||
return _fsCaseSensitivity;
|
||||
}
|
||||
|
||||
_fsCaseSensitivity = isFileSystemCaseSensitiveInternal(fs);
|
||||
return _fsCaseSensitivity;
|
||||
}
|
||||
|
||||
export function isFileSystemCaseSensitiveInternal(fs: FileSystem) {
|
||||
let filePath: string | undefined = undefined;
|
||||
try {
|
||||
// Make sure tmp dir exists.
|
||||
if (!fs.existsSync(fs.tmpdir())) {
|
||||
fs.mkdirSync(fs.tmpdir(), { recursive: true });
|
||||
}
|
||||
|
||||
// Make unique file name.
|
||||
let name: string;
|
||||
let mangledFilePath: string;
|
||||
do {
|
||||
name = `${randomBytes(21).toString('hex')}-a`;
|
||||
filePath = path.join(fs.tmpdir(), name);
|
||||
mangledFilePath = path.join(fs.tmpdir(), name.toUpperCase());
|
||||
} while (fs.existsSync(filePath) || fs.existsSync(mangledFilePath));
|
||||
|
||||
fs.writeFileSync(filePath, '', 'utf8');
|
||||
|
||||
// If file exists, then it is insensitive.
|
||||
return !fs.existsSync(mangledFilePath);
|
||||
} catch (e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (filePath) {
|
||||
// remove temp file created
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,12 +61,12 @@ export function convertPositionToOffset(position: Position, lines: TextRangeColl
|
||||
|
||||
export function convertRangeToTextRange(range: Range, lines: TextRangeCollection<TextRange>): TextRange | undefined {
|
||||
const start = convertPositionToOffset(range.start, lines);
|
||||
if (!start) {
|
||||
if (start === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const end = convertPositionToOffset(range.end, lines);
|
||||
if (!end) {
|
||||
if (end === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -75,20 +75,20 @@ export class TimingStats {
|
||||
typeCheckerTime = new TimingStat();
|
||||
|
||||
printSummary(console: ConsoleInterface) {
|
||||
console.log(`Completed in ${this.totalDuration.getDurationInSeconds()}sec`);
|
||||
console.info(`Completed in ${this.totalDuration.getDurationInSeconds()}sec`);
|
||||
}
|
||||
|
||||
printDetails(console: ConsoleInterface) {
|
||||
console.log('');
|
||||
console.log('Timing stats');
|
||||
console.log('Find Source Files: ' + this.findFilesTime.printTime());
|
||||
console.log('Read Source Files: ' + this.readFileTime.printTime());
|
||||
console.log('Tokenize: ' + this.tokenizeFileTime.printTime());
|
||||
console.log('Parse: ' + this.parseFileTime.printTime());
|
||||
console.log('Resolve Imports: ' + this.resolveImportsTime.printTime());
|
||||
console.log('Bind: ' + this.bindTime.printTime());
|
||||
console.log('Check: ' + this.typeCheckerTime.printTime());
|
||||
console.log('Detect Cycles: ' + this.cycleDetectionTime.printTime());
|
||||
console.info('');
|
||||
console.info('Timing stats');
|
||||
console.info('Find Source Files: ' + this.findFilesTime.printTime());
|
||||
console.info('Read Source Files: ' + this.readFileTime.printTime());
|
||||
console.info('Tokenize: ' + this.tokenizeFileTime.printTime());
|
||||
console.info('Parse: ' + this.parseFileTime.printTime());
|
||||
console.info('Resolve Imports: ' + this.resolveImportsTime.printTime());
|
||||
console.info('Bind: ' + this.bindTime.printTime());
|
||||
console.info('Check: ' + this.typeCheckerTime.printTime());
|
||||
console.info('Detect Cycles: ' + this.cycleDetectionTime.printTime());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
Command,
|
||||
CompletionTriggerKind,
|
||||
ConfigurationItem,
|
||||
Connection,
|
||||
ConnectionOptions,
|
||||
createConnection,
|
||||
Diagnostic,
|
||||
@ -30,11 +31,9 @@ import {
|
||||
DidChangeWatchedFilesNotification,
|
||||
DocumentSymbol,
|
||||
ExecuteCommandParams,
|
||||
IConnection,
|
||||
InitializeResult,
|
||||
Location,
|
||||
ParameterInformation,
|
||||
RemoteConsole,
|
||||
RemoteWindow,
|
||||
SignatureInformation,
|
||||
SymbolInformation,
|
||||
@ -42,7 +41,7 @@ import {
|
||||
WatchKind,
|
||||
WorkDoneProgressReporter,
|
||||
WorkspaceEdit,
|
||||
} from 'vscode-languageserver';
|
||||
} from 'vscode-languageserver/node';
|
||||
|
||||
import { AnalysisResults } from './analyzer/analysis';
|
||||
import { ImportResolver } from './analyzer/importResolver';
|
||||
@ -57,7 +56,7 @@ import {
|
||||
getDiagnosticSeverityOverrides,
|
||||
} from './common/commandLineOptions';
|
||||
import { ConfigOptions, getDiagLevelDiagnosticRules } from './common/configOptions';
|
||||
import { ConsoleInterface } from './common/console';
|
||||
import { ConsoleInterface, ConsoleWithLogLevel, LogLevel } from './common/console';
|
||||
import { createDeferred, Deferred } from './common/deferred';
|
||||
import { Diagnostic as AnalyzerDiagnostic, DiagnosticCategory } from './common/diagnostic';
|
||||
import { DiagnosticRule } from './common/diagnosticRules';
|
||||
@ -94,6 +93,7 @@ export interface ServerSettings {
|
||||
watchForSourceChanges?: boolean;
|
||||
watchForLibraryChanges?: boolean;
|
||||
diagnosticSeverityOverrides?: DiagnosticSeverityOverridesMap;
|
||||
logLevel?: LogLevel;
|
||||
}
|
||||
|
||||
export interface WorkspaceServiceInstance {
|
||||
@ -147,7 +147,7 @@ interface InternalFileWatcher extends FileWatcher {
|
||||
|
||||
export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
// Create a connection for the server. The connection type can be changed by the process's arguments
|
||||
protected _connection: IConnection = createConnection(this._GetConnectionOptions());
|
||||
protected _connection: Connection = createConnection(this._GetConnectionOptions());
|
||||
protected _workspaceMap: WorkspaceMap;
|
||||
protected _hasConfigurationCapability = false;
|
||||
protected _hasWatchFileCapability = false;
|
||||
@ -170,13 +170,18 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
// File system abstraction.
|
||||
fs: FileSystem;
|
||||
|
||||
readonly console: ConsoleInterface;
|
||||
|
||||
constructor(private _serverOptions: ServerOptions) {
|
||||
this._connection.console.log(
|
||||
this.console = new ConsoleWithLogLevel(this._connection.console);
|
||||
|
||||
this.console.info(
|
||||
`${_serverOptions.productName} language server ${
|
||||
_serverOptions.version && _serverOptions.version + ' '
|
||||
}starting`
|
||||
);
|
||||
this.fs = createFromRealFileSystem(this._connection.console, this);
|
||||
|
||||
this.fs = createFromRealFileSystem(this.console, this);
|
||||
|
||||
// Set the working directory to a known location within
|
||||
// the extension directory. Otherwise the execution of
|
||||
@ -188,7 +193,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
|
||||
// Stash the base directory into a global variable.
|
||||
(global as any).__rootDirectory = _serverOptions.rootDirectory;
|
||||
this._connection.console.log(`Server root directory: ${_serverOptions.rootDirectory}`);
|
||||
this.console.info(`Server root directory: ${_serverOptions.rootDirectory}`);
|
||||
|
||||
// Create workspace map.
|
||||
this._workspaceMap = new WorkspaceMap(this);
|
||||
@ -271,11 +276,6 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
this._serverOptions.extension = extension;
|
||||
}
|
||||
|
||||
// Provides access to logging to the client output window.
|
||||
get console(): RemoteConsole {
|
||||
return this._connection.console;
|
||||
}
|
||||
|
||||
// Provides access to the client's window.
|
||||
get window(): RemoteWindow {
|
||||
return this._connection.window;
|
||||
@ -284,11 +284,11 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
// Creates a service instance that's used for analyzing a
|
||||
// program within a workspace.
|
||||
createAnalyzerService(name: string): AnalyzerService {
|
||||
this._connection.console.log(`Starting service instance "${name}"`);
|
||||
this.console.log(`Starting service instance "${name}"`);
|
||||
const service = new AnalyzerService(
|
||||
name,
|
||||
this.fs,
|
||||
this._connection.console,
|
||||
this.console,
|
||||
this.createImportResolver.bind(this),
|
||||
undefined,
|
||||
this._serverOptions.extension,
|
||||
@ -435,7 +435,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
);
|
||||
|
||||
this._connection.onDidChangeConfiguration((params) => {
|
||||
this._connection.console.log(`Received updated settings`);
|
||||
this.console.log(`Received updated settings`);
|
||||
if (params?.settings) {
|
||||
this._defaultClientConfig = params?.settings;
|
||||
}
|
||||
@ -507,7 +507,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
|
||||
return locations.map((loc) => Location.create(convertPathToUri(loc.path), loc.range));
|
||||
} finally {
|
||||
progress.done();
|
||||
progress.reporter.done();
|
||||
source.dispose();
|
||||
}
|
||||
});
|
||||
@ -788,7 +788,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
try {
|
||||
executeCommand(source.token);
|
||||
} finally {
|
||||
progress.done();
|
||||
progress.reporter.done();
|
||||
source.dispose();
|
||||
}
|
||||
} else {
|
||||
@ -812,6 +812,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
});
|
||||
});
|
||||
|
||||
if (!this._progressReporter.isEnabled(results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update progress.
|
||||
if (results.filesRequiringAnalysis > 0) {
|
||||
this._progressReporter.begin();
|
||||
@ -830,6 +834,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
|
||||
async updateSettingsForWorkspace(workspace: WorkspaceServiceInstance): Promise<void> {
|
||||
const serverSettings = await this.getSettings(workspace);
|
||||
|
||||
// Set logging level first.
|
||||
(this.console as ConsoleWithLogLevel).level = serverSettings.logLevel ?? LogLevel.Info;
|
||||
|
||||
this.updateOptionsAndRestartService(workspace, serverSettings);
|
||||
workspace.disableLanguageServices = !!serverSettings.disableLanguageServices;
|
||||
workspace.disableOrganizeImports = !!serverSettings.disableOrganizeImports;
|
||||
@ -846,19 +854,41 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
AnalyzerServiceExecutor.runWithOptions(this.rootPath, workspace, serverSettings, typeStubTargetImportName);
|
||||
}
|
||||
|
||||
protected convertLogLevel(logLevel?: string): LogLevel {
|
||||
if (!logLevel) {
|
||||
return LogLevel.Info;
|
||||
}
|
||||
|
||||
switch (logLevel.toLowerCase()) {
|
||||
case 'error':
|
||||
return LogLevel.Error;
|
||||
case 'warning':
|
||||
return LogLevel.Warn;
|
||||
case 'info':
|
||||
return LogLevel.Info;
|
||||
case 'trace':
|
||||
return LogLevel.Log;
|
||||
default:
|
||||
return LogLevel.Info;
|
||||
}
|
||||
}
|
||||
|
||||
private async _getProgressReporter(
|
||||
workDoneToken: string | number | undefined,
|
||||
reporter: WorkDoneProgressReporter,
|
||||
clientReporter: WorkDoneProgressReporter,
|
||||
title: string
|
||||
) {
|
||||
if (workDoneToken) {
|
||||
return reporter;
|
||||
return { reporter: clientReporter, token: CancellationToken.None };
|
||||
}
|
||||
|
||||
const serverInitiatedReporter = await this._connection.window.createWorkDoneProgress();
|
||||
serverInitiatedReporter.begin(title, undefined, undefined, true);
|
||||
|
||||
return serverInitiatedReporter;
|
||||
return {
|
||||
reporter: serverInitiatedReporter,
|
||||
token: serverInitiatedReporter.token,
|
||||
};
|
||||
}
|
||||
|
||||
private _GetConnectionOptions(): ConnectionOptions {
|
||||
@ -913,7 +943,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
||||
// Tell all of the services that the user is actively
|
||||
// interacting with one or more editors, so they should
|
||||
// back off from performing any work.
|
||||
this._workspaceMap.forEach((workspace) => {
|
||||
this._workspaceMap.forEach((workspace: { serviceInstance: { recordUserInteractionTime: () => void } }) => {
|
||||
workspace.serviceInstance.recordUserInteractionTime();
|
||||
});
|
||||
}
|
||||
|
@ -31,6 +31,13 @@ import { ParseResults } from '../parser/parser';
|
||||
|
||||
export type ModuleSymbolMap = Map<string, SymbolTable>;
|
||||
|
||||
export type ImportName = string;
|
||||
export type FullImportName = string;
|
||||
export type FilePath = string;
|
||||
|
||||
export type ImportsMap = Map<FullImportName, FilePath>;
|
||||
export type ImportNameMap = Map<ImportName, ImportsMap>;
|
||||
|
||||
// Build a map of all modules within this program and the module-
|
||||
// level scope that contains the symbol table for the module.
|
||||
export function buildModuleSymbolsMap(files: SourceFileInfo[], token: CancellationToken): ModuleSymbolMap {
|
||||
@ -63,13 +70,22 @@ export interface AutoImportResult {
|
||||
alias?: string;
|
||||
}
|
||||
|
||||
interface ImportParts {
|
||||
namePart?: string;
|
||||
importName: string;
|
||||
importSource: string;
|
||||
filePath: string;
|
||||
moduleAndType: ModuleNameAndType;
|
||||
}
|
||||
|
||||
export class AutoImporter {
|
||||
constructor(
|
||||
private _configOptions: ConfigOptions,
|
||||
private _filePath: string,
|
||||
private _importResolver: ImportResolver,
|
||||
private _parseResults: ParseResults,
|
||||
private _moduleSymbolMap: ModuleSymbolMap
|
||||
private _moduleSymbolMap: ModuleSymbolMap,
|
||||
private _packageMap?: ImportNameMap
|
||||
) {}
|
||||
|
||||
getAutoImportCandidates(
|
||||
@ -80,8 +96,61 @@ export class AutoImporter {
|
||||
token: CancellationToken
|
||||
) {
|
||||
const results: AutoImportResult[] = [];
|
||||
|
||||
const importStatements = getTopLevelImports(this._parseResults.parseTree);
|
||||
|
||||
this._addImportsFromModuleMap(word, similarityLimit, excludes, aliasName, importStatements, results, token);
|
||||
this._addImportsFromPackages(word, similarityLimit, excludes, aliasName, importStatements, results, token);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private _addImportsFromPackages(
|
||||
word: string,
|
||||
similarityLimit: number,
|
||||
excludes: string[],
|
||||
aliasName: string | undefined,
|
||||
importStatements: ImportStatements,
|
||||
results: AutoImportResult[],
|
||||
token: CancellationToken
|
||||
) {
|
||||
if (!this._packageMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._packageMap.forEach((imports, name) => {
|
||||
throwIfCancellationRequested(token);
|
||||
|
||||
// Don't offer imports from files that are named with private
|
||||
// naming semantics like "_ast.py".
|
||||
if (SymbolNameUtils.isPrivateOrProtectedName(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isSimilar = this._isSimilar(word, name, similarityLimit);
|
||||
if (!isSimilar) {
|
||||
return;
|
||||
}
|
||||
|
||||
imports.forEach((filePath, fullImport) => {
|
||||
throwIfCancellationRequested(token);
|
||||
const importParts = this._getImportParts(filePath);
|
||||
if (!importParts) {
|
||||
return;
|
||||
}
|
||||
this._addAutoImports(importParts, excludes, importStatements, aliasName, results);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _addImportsFromModuleMap(
|
||||
word: string,
|
||||
similarityLimit: number,
|
||||
excludes: string[],
|
||||
aliasName: string | undefined,
|
||||
importStatements: ImportStatements,
|
||||
results: AutoImportResult[],
|
||||
token: CancellationToken
|
||||
) {
|
||||
this._moduleSymbolMap.forEach((symbolTable, filePath) => {
|
||||
throwIfCancellationRequested(token);
|
||||
|
||||
@ -161,7 +230,6 @@ export class AutoImporter {
|
||||
const fileDir = getDirectoryPath(filePath);
|
||||
const initPathPy = combinePaths(fileDir, '__init__.py');
|
||||
const initPathPyi = initPathPy + 'i';
|
||||
|
||||
const isStubFile = filePath.endsWith('.pyi');
|
||||
const hasInit = this._moduleSymbolMap.has(initPathPy) || this._moduleSymbolMap.has(initPathPyi);
|
||||
|
||||
@ -172,66 +240,78 @@ export class AutoImporter {
|
||||
return;
|
||||
}
|
||||
|
||||
let importNamePart: string | undefined;
|
||||
let name: string;
|
||||
let importSource: string;
|
||||
let moduleNameAndType: ModuleNameAndType;
|
||||
|
||||
if (hasInit) {
|
||||
importNamePart = stripFileExtension(getFileName(filePath));
|
||||
moduleNameAndType = this._getModuleNameAndTypeFromFilePath(getDirectoryPath(filePath));
|
||||
importSource = moduleNameAndType.moduleName;
|
||||
|
||||
// See if we can import module as "import xxx"
|
||||
if (importNamePart === '__init__') {
|
||||
importNamePart = undefined;
|
||||
name = importSource;
|
||||
} else {
|
||||
name = importNamePart;
|
||||
}
|
||||
} else {
|
||||
// We don't have init.py[i] but this file is a stub file.
|
||||
// See whether we can import it as "import xx"
|
||||
importNamePart = undefined;
|
||||
moduleNameAndType = this._getModuleNameAndTypeFromFilePath(filePath);
|
||||
name = importSource = moduleNameAndType.moduleName;
|
||||
}
|
||||
|
||||
if (!importSource) {
|
||||
const importParts = this._getImportParts(filePath);
|
||||
if (!importParts) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isSimilar = this._isSimilar(word, name, similarityLimit);
|
||||
const isSimilar = this._isSimilar(word, importParts.importName, similarityLimit);
|
||||
if (!isSimilar) {
|
||||
return;
|
||||
}
|
||||
|
||||
const alreadyIncluded = this._containsName(name, importSource, excludes, results);
|
||||
if (alreadyIncluded) {
|
||||
this._addAutoImports(importParts, excludes, importStatements, aliasName, results);
|
||||
});
|
||||
}
|
||||
|
||||
private _addAutoImports(
|
||||
importParts: ImportParts,
|
||||
excludes: string[],
|
||||
importStatements: ImportStatements,
|
||||
aliasName: string | undefined,
|
||||
results: AutoImportResult[]
|
||||
) {
|
||||
const alreadyIncluded = this._containsName(importParts.importName, importParts.importSource, excludes, results);
|
||||
if (alreadyIncluded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const importGroup = this._getImportGroupFromModuleNameAndType(importParts.moduleAndType);
|
||||
const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath(
|
||||
importParts.namePart,
|
||||
importStatements,
|
||||
importParts.filePath,
|
||||
importParts.importSource,
|
||||
importGroup,
|
||||
aliasName
|
||||
);
|
||||
|
||||
results.push({
|
||||
name: importParts.importName,
|
||||
alias: aliasName,
|
||||
symbol: undefined,
|
||||
source: importParts.importSource,
|
||||
edits: autoImportTextEdits,
|
||||
isImportFrom: !!importParts.namePart,
|
||||
});
|
||||
}
|
||||
|
||||
private _getImportParts(filePath: string) {
|
||||
const name = stripFileExtension(getFileName(filePath));
|
||||
|
||||
// See if we can import module as "import xxx"
|
||||
if (name === '__init__') {
|
||||
return createImportParts(this._getModuleNameAndTypeFromFilePath(getDirectoryPath(filePath)));
|
||||
}
|
||||
|
||||
return createImportParts(this._getModuleNameAndTypeFromFilePath(filePath));
|
||||
|
||||
function createImportParts(module: ModuleNameAndType) {
|
||||
const importSource = module.moduleName;
|
||||
if (!importSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
const importGroup = this._getImportGroupFromModuleNameAndType(moduleNameAndType);
|
||||
const autoImportTextEdits = this._getTextEditsForAutoImportByFilePath(
|
||||
importNamePart,
|
||||
importStatements,
|
||||
const index = importSource.lastIndexOf('.');
|
||||
const importNamePart = index > 0 ? importSource.substring(index + 1) : undefined;
|
||||
return {
|
||||
namePart: importNamePart,
|
||||
importName: index > 0 ? importNamePart! : importSource,
|
||||
importSource: index > 0 ? importSource.substring(0, index) : importSource,
|
||||
filePath,
|
||||
importSource,
|
||||
importGroup,
|
||||
aliasName
|
||||
);
|
||||
|
||||
results.push({
|
||||
name: name,
|
||||
alias: aliasName,
|
||||
symbol: undefined,
|
||||
source: importSource,
|
||||
edits: autoImportTextEdits,
|
||||
isImportFrom: !!importNamePart,
|
||||
});
|
||||
});
|
||||
|
||||
return results;
|
||||
moduleAndType: module,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private _isSimilar(word: string, name: string, similarityLimit: number) {
|
||||
|
@ -10,9 +10,9 @@ import {
|
||||
CodeAction,
|
||||
CodeActionParams,
|
||||
Command,
|
||||
Connection,
|
||||
ExecuteCommandParams,
|
||||
IConnection,
|
||||
} from 'vscode-languageserver';
|
||||
} from 'vscode-languageserver/node';
|
||||
import { isMainThread } from 'worker_threads';
|
||||
|
||||
import { AnalysisResults } from './analyzer/analysis';
|
||||
@ -20,6 +20,7 @@ import { BackgroundAnalysis } from './backgroundAnalysis';
|
||||
import { BackgroundAnalysisBase } from './backgroundAnalysisBase';
|
||||
import { CommandController } from './commands/commandController';
|
||||
import { getCancellationFolderName } from './common/cancellationUtils';
|
||||
import { LogLevel } from './common/console';
|
||||
import { isDebugMode, isString } from './common/core';
|
||||
import { convertUriToPath, getDirectoryPath, normalizeSlashes } from './common/pathUtils';
|
||||
import { ProgressReporter } from './common/progressReporter';
|
||||
@ -48,6 +49,7 @@ class PyrightServer extends LanguageServerBase {
|
||||
const serverSettings: ServerSettings = {
|
||||
watchForSourceChanges: true,
|
||||
diagnosticSeverityOverrides: {},
|
||||
logLevel: LogLevel.Info,
|
||||
};
|
||||
|
||||
try {
|
||||
@ -86,6 +88,7 @@ class PyrightServer extends LanguageServerBase {
|
||||
serverSettings.openFilesOnly = !!pythonAnalysisSection.openFilesOnly;
|
||||
}
|
||||
|
||||
serverSettings.logLevel = this.convertLogLevel(pythonAnalysisSection.logLevel);
|
||||
serverSettings.autoSearchPaths = !!pythonAnalysisSection.autoSearchPaths;
|
||||
|
||||
const extraPaths = pythonAnalysisSection.extraPaths;
|
||||
@ -115,7 +118,7 @@ class PyrightServer extends LanguageServerBase {
|
||||
serverSettings.typeCheckingMode = undefined;
|
||||
}
|
||||
} catch (error) {
|
||||
this.console.log(`Error reading settings: ${error}`);
|
||||
this.console.error(`Error reading settings: ${error}`);
|
||||
}
|
||||
return serverSettings;
|
||||
}
|
||||
@ -150,10 +153,10 @@ class PyrightServer extends LanguageServerBase {
|
||||
}
|
||||
}
|
||||
|
||||
function reporterFactory(connection: IConnection): ProgressReporter {
|
||||
function reporterFactory(connection: Connection): ProgressReporter {
|
||||
return {
|
||||
isEnabled(data: AnalysisResults): boolean {
|
||||
return !data.checkingOnlyOpenFiles;
|
||||
return true;
|
||||
},
|
||||
|
||||
begin(): void {
|
||||
|
@ -87,7 +87,7 @@ test('FileSpecNotAnArray', () => {
|
||||
service.test_getConfigOptions(commandLineOptions);
|
||||
|
||||
// The method should return a default config and log an error.
|
||||
assert(nullConsole.logCount > 0);
|
||||
assert(nullConsole.infoCount > 0);
|
||||
});
|
||||
|
||||
test('FileSpecNotAString', () => {
|
||||
@ -101,7 +101,7 @@ test('FileSpecNotAString', () => {
|
||||
service.test_getConfigOptions(commandLineOptions);
|
||||
|
||||
// The method should return a default config and log an error.
|
||||
assert(nullConsole.logCount > 0);
|
||||
assert(nullConsole.infoCount > 0);
|
||||
});
|
||||
|
||||
test('SomeFileSpecsAreInvalid', () => {
|
||||
@ -140,7 +140,7 @@ test('ConfigBadJson', () => {
|
||||
service.test_getConfigOptions(commandLineOptions);
|
||||
|
||||
// The method should return a default config and log an error.
|
||||
assert(nullConsole.logCount > 0);
|
||||
assert(nullConsole.infoCount > 0);
|
||||
});
|
||||
|
||||
test('FindExecEnv1', () => {
|
||||
|
@ -0,0 +1,29 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @filename: package1/__init__.py
|
||||
// @library: true
|
||||
//// from .subpackage import func1
|
||||
|
||||
// @filename: package1/subpackage.py
|
||||
// @library: true
|
||||
//// def func1():
|
||||
//// '''func1 docs'''
|
||||
//// return True
|
||||
|
||||
// @filename: typings/package1/__init__.pyi
|
||||
//// from .subpackage import func1
|
||||
|
||||
// @filename: typings/package1/subpackage/__init__.pyi
|
||||
//// def func1() -> bool: ...
|
||||
|
||||
// @filename: test.py
|
||||
//// from package1 import func1
|
||||
////
|
||||
//// print([|/*func1_docs*/func1|]())
|
||||
|
||||
helper.verifyHover({
|
||||
func1_docs: {
|
||||
value: '```python\n(function) func1: () -> bool\n```\nfunc1 docs',
|
||||
kind: 'markdown',
|
||||
},
|
||||
});
|
@ -0,0 +1,29 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @filename: package1/__init__.py
|
||||
// @library: true
|
||||
//// from .subpackage import func1
|
||||
|
||||
// @filename: package1/subpackage/__init__.py
|
||||
// @library: true
|
||||
//// def func1():
|
||||
//// '''func1 docs'''
|
||||
//// return True
|
||||
|
||||
// @filename: typings/package1/__init__.pyi
|
||||
//// from .subpackage import func1
|
||||
|
||||
// @filename: typings/package1/subpackage.pyi
|
||||
//// def func1() -> bool: ...
|
||||
|
||||
// @filename: test.py
|
||||
//// from package1 import func1
|
||||
////
|
||||
//// print([|/*func1_docs*/func1|]())
|
||||
|
||||
helper.verifyHover({
|
||||
func1_docs: {
|
||||
value: '```python\n(function) func1: () -> bool\n```\nfunc1 docs',
|
||||
kind: 'markdown',
|
||||
},
|
||||
});
|
@ -0,0 +1,49 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @filename: dj/__init__.py
|
||||
// @library: true
|
||||
//// # empty
|
||||
|
||||
// @filename: dj/db/__init__.py
|
||||
// @library: true
|
||||
//// # empty
|
||||
|
||||
// @filename: dj/db/models/__init__.py
|
||||
// @library: true
|
||||
//// from dj.db.models.base import Model
|
||||
|
||||
// @filename: dj/db/models/base.py
|
||||
// @library: true
|
||||
//// class Model:
|
||||
//// def clean_fields(self):
|
||||
//// '''clean_fields docs'''
|
||||
//// pass
|
||||
|
||||
// @filename: typings/dj/__init__.pyi
|
||||
//// # empty
|
||||
|
||||
// @filename: typings/dj/db/__init__.pyi
|
||||
//// # empty
|
||||
|
||||
// @filename: typings/dj/db/models/__init__.pyi
|
||||
//// from .base import Model as Model
|
||||
|
||||
// @filename: typings/dj/db/models/base.pyi
|
||||
//// class Model:
|
||||
//// def clean_fields(self) -> None: ...
|
||||
|
||||
// @filename: test.py
|
||||
//// from dj.db import models
|
||||
////
|
||||
//// class Person(models.Model):
|
||||
//// pass
|
||||
////
|
||||
//// p = Person()
|
||||
//// p.[|/*marker*/clean_fields|]()
|
||||
|
||||
helper.verifyHover({
|
||||
marker: {
|
||||
value: '```python\n(method) clean_fields: () -> None\n```\nclean\\_fields docs',
|
||||
kind: 'markdown',
|
||||
},
|
||||
});
|
@ -0,0 +1,35 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @filename: package1-stubs/__init__.pyi
|
||||
// @library: true
|
||||
//// from .api import func1
|
||||
|
||||
// @filename: package1-stubs/api.pyi
|
||||
// @library: true
|
||||
//// def func1() -> bool: ...
|
||||
|
||||
// @filename: package1/__init__.py
|
||||
// @library: true
|
||||
//// from .api import func1
|
||||
|
||||
// @filename: package1/api.py
|
||||
// @library: true
|
||||
//// def func1():
|
||||
//// '''func1 docs'''
|
||||
//// return True
|
||||
|
||||
// @filename: test.py
|
||||
//// import package1
|
||||
////
|
||||
//// print(package1.[|/*marker*/func1|]())
|
||||
|
||||
helper.verifyHover({
|
||||
marker: {
|
||||
value: '```python\n(function) func1: () -> bool\n```\nfunc1 docs',
|
||||
kind: 'markdown',
|
||||
},
|
||||
marker2: {
|
||||
value: '```python\n(function) func2: () -> bool\n```\nfunc2 docs',
|
||||
kind: 'markdown',
|
||||
},
|
||||
});
|
@ -0,0 +1,23 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @filename: requests/__init__.py
|
||||
// @library: true
|
||||
//// from .api import head
|
||||
|
||||
// @filename: requests/api.py
|
||||
// @library: true
|
||||
//// def head(url, **kwargs):
|
||||
//// r"""Sends a HEAD request."""
|
||||
//// pass
|
||||
|
||||
// @filename: test.py
|
||||
//// import requests
|
||||
////
|
||||
//// print(requests.[|/*marker*/head|](''))
|
||||
|
||||
helper.verifyHover({
|
||||
marker: {
|
||||
value: '```python\n(function) head: (url: str | bytes, **kwargs) -> Response\n```\nSends a HEAD request.',
|
||||
kind: 'markdown',
|
||||
},
|
||||
});
|
@ -56,11 +56,11 @@ export class TestLanguageService implements LanguageServerInterface {
|
||||
|
||||
async getSettings(workspace: WorkspaceServiceInstance): Promise<ServerSettings> {
|
||||
const settings: ServerSettings = {
|
||||
venvPath: this._workspace.serviceInstance.test_configOptions.venvPath,
|
||||
pythonPath: this._workspace.serviceInstance.test_configOptions.pythonPath,
|
||||
typeshedPath: this._workspace.serviceInstance.test_configOptions.typeshedPath,
|
||||
openFilesOnly: this._workspace.serviceInstance.test_configOptions.checkOnlyOpenFiles,
|
||||
useLibraryCodeForTypes: this._workspace.serviceInstance.test_configOptions.useLibraryCodeForTypes,
|
||||
venvPath: this._workspace.serviceInstance.getConfigOptions().venvPath,
|
||||
pythonPath: this._workspace.serviceInstance.getConfigOptions().pythonPath,
|
||||
typeshedPath: this._workspace.serviceInstance.getConfigOptions().typeshedPath,
|
||||
openFilesOnly: this._workspace.serviceInstance.getConfigOptions().checkOnlyOpenFiles,
|
||||
useLibraryCodeForTypes: this._workspace.serviceInstance.getConfigOptions().useLibraryCodeForTypes,
|
||||
disableLanguageServices: this._workspace.disableLanguageServices,
|
||||
};
|
||||
|
||||
|
@ -174,7 +174,7 @@ export class TestState {
|
||||
}
|
||||
|
||||
get configOptions(): ConfigOptions {
|
||||
return this.workspace.serviceInstance.test_configOptions;
|
||||
return this.workspace.serviceInstance.getConfigOptions();
|
||||
}
|
||||
|
||||
get program(): Program {
|
||||
@ -564,6 +564,8 @@ export class TestState {
|
||||
},
|
||||
verifyCodeActionCount?: boolean
|
||||
): Promise<any> {
|
||||
// make sure we don't use cache built from other tests
|
||||
this.workspace.serviceInstance.invalidateAndForceReanalysis();
|
||||
this._analyze();
|
||||
|
||||
for (const range of this.getRanges()) {
|
||||
|
@ -319,6 +319,10 @@ export class TestFileSystem implements FileSystem {
|
||||
return MODULE_PATH;
|
||||
}
|
||||
|
||||
tmpdir(): string {
|
||||
return pathUtil.normalizeSlashes('/tmp');
|
||||
}
|
||||
|
||||
private _scan(path: string, stats: Stats, axis: Axis, traversal: Traversal, noFollow: boolean, results: string[]) {
|
||||
if (axis === 'ancestors-or-self' || axis === 'self' || axis === 'descendants-or-self') {
|
||||
if (!traversal.accept || traversal.accept(path, stats)) {
|
||||
|
@ -31,6 +31,7 @@ import {
|
||||
getWildcardRegexPattern,
|
||||
getWildcardRoot,
|
||||
hasTrailingDirectorySeparator,
|
||||
isFileSystemCaseSensitiveInternal,
|
||||
isRootedDiskPath,
|
||||
normalizeSlashes,
|
||||
reducePathComponents,
|
||||
@ -38,6 +39,7 @@ import {
|
||||
stripFileExtension,
|
||||
stripTrailingDirectorySeparator,
|
||||
} from '../common/pathUtils';
|
||||
import * as vfs from './harness/vfs/filesystem';
|
||||
|
||||
test('getPathComponents1', () => {
|
||||
const components = getPathComponents('');
|
||||
@ -297,3 +299,13 @@ test('getRelativePath', () => {
|
||||
normalizeSlashes('./d/e/f')
|
||||
);
|
||||
});
|
||||
|
||||
test('CaseSensitivity', () => {
|
||||
const cwd = normalizeSlashes('/');
|
||||
|
||||
const fsCaseInsensitive = new vfs.TestFileSystem(/*ignoreCase*/ true, { cwd });
|
||||
assert.equal(isFileSystemCaseSensitiveInternal(fsCaseInsensitive), false);
|
||||
|
||||
const fsCaseSensitive = new vfs.TestFileSystem(/*ignoreCase*/ false, { cwd });
|
||||
assert.equal(isFileSystemCaseSensitiveInternal(fsCaseSensitive), true);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user