Did some cleanup in prep for next round of changes: Renamed vfs.ts to fileSystem.ts and VirtualFileSystem to FileSystem for consistency and clarity. Added more comments. Removed extraneous "event" parameter from createFileSystemWatcher interface.

This commit is contained in:
Eric Traut 2020-04-05 11:47:42 -07:00
parent aae44cbdb3
commit d4f3f2f96c
15 changed files with 67 additions and 65 deletions

View File

@ -9,6 +9,7 @@
*/
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
import { FileSystem } from '../common/fileSystem';
import {
combinePaths,
ensureTrailingDirectorySeparator,
@ -23,7 +24,6 @@ import {
} from '../common/pathUtils';
import { versionToString } from '../common/pythonVersion';
import * as StringUtils from '../common/stringUtils';
import { VirtualFileSystem } from '../common/vfs';
import { ImplicitImport, ImportResult, ImportType } from './importResult';
import * as PythonPathUtils from './pythonPathUtils';
import { isDunderName } from './symbolNameUtils';
@ -49,9 +49,9 @@ export class ImportResolver {
private _cachedTypeshedStdLibPath: string | undefined;
private _cachedTypeshedThirdPartyPath: string | undefined;
readonly fileSystem: VirtualFileSystem;
readonly fileSystem: FileSystem;
constructor(fs: VirtualFileSystem, configOptions: ConfigOptions) {
constructor(fs: FileSystem, configOptions: ConfigOptions) {
this.fileSystem = fs;
this._configOptions = configOptions;
}
@ -1020,4 +1020,4 @@ export class ImportResolver {
}
}
export type ImportResolverFactory = (fs: VirtualFileSystem, options: ConfigOptions) => ImportResolver;
export type ImportResolverFactory = (fs: FileSystem, options: ConfigOptions) => ImportResolver;

View File

@ -10,6 +10,7 @@
import * as child_process from 'child_process';
import { ConfigOptions } from '../common/configOptions';
import { FileSystem } from '../common/fileSystem';
import * as pathConsts from '../common/pathConsts';
import {
combinePaths,
@ -20,11 +21,10 @@ import {
isDirectory,
normalizePath
} from '../common/pathUtils';
import { VirtualFileSystem } from '../common/vfs';
const cachedSearchPaths = new Map<string, PythonPathResult>();
export function getTypeShedFallbackPath(fs: VirtualFileSystem) {
export function getTypeShedFallbackPath(fs: FileSystem) {
let moduleDirectory = fs.getModulePath();
if (!moduleDirectory) {
return undefined;
@ -52,7 +52,7 @@ export function getTypeshedSubdirectory(typeshedPath: string, isStdLib: boolean)
}
export function findPythonSearchPaths(
fs: VirtualFileSystem,
fs: FileSystem,
configOptions: ConfigOptions,
venv: string | undefined,
importFailureInfo: string[],
@ -135,7 +135,7 @@ interface PythonPathResult {
}
export function getPythonPathFromPythonInterpreter(
fs: VirtualFileSystem,
fs: FileSystem,
interpreterPath: string | undefined,
importFailureInfo: string[]
): PythonPathResult {

View File

@ -25,6 +25,7 @@ import { Diagnostic } from '../common/diagnostic';
import { FileDiagnostics } from '../common/diagnosticSink';
import { FileEditAction, TextEditAction } from '../common/editAction';
import { LanguageServiceExtension } from '../common/extensibility';
import { FileSystem, FileWatcher } from '../common/fileSystem';
import {
combinePaths,
FileSpec,
@ -39,7 +40,6 @@ import {
} from '../common/pathUtils';
import { DocumentRange, Position, Range } from '../common/textRange';
import { Duration, timingStats } from '../common/timing';
import { FileWatcher, VirtualFileSystem } from '../common/vfs';
import { HoverResults } from '../languageService/hoverProvider';
import { SignatureHelpResults } from '../languageService/signatureHelpProvider';
import { ImportedModuleDescriptor, ImportResolver, ImportResolverFactory } from './importResolver';
@ -94,7 +94,7 @@ export class AnalyzerService {
constructor(
instanceName: string,
fs: VirtualFileSystem,
fs: FileSystem,
console?: ConsoleInterface,
importResolverFactory?: ImportResolverFactory,
configOptions?: ConfigOptions,
@ -129,7 +129,7 @@ export class AnalyzerService {
this._clearLibraryReanalysisTimer();
}
static createImportResolver(fs: VirtualFileSystem, options: ConfigOptions): ImportResolver {
static createImportResolver(fs: FileSystem, options: ConfigOptions): ImportResolver {
return new ImportResolver(fs, options);
}
@ -864,7 +864,7 @@ export class AnalyzerService {
this._console.log(`Adding fs watcher for directories:\n ${fileList.join('\n')}`);
}
this._sourceFileWatcher = this._fs.createFileSystemWatcher(fileList, 'all', (event, path) => {
this._sourceFileWatcher = this._fs.createFileSystemWatcher(fileList, (event, path) => {
if (this._verboseOutput) {
this._console.log(`Received fs event '${event}' for path '${path}'`);
}
@ -901,7 +901,7 @@ export class AnalyzerService {
return;
}
// watch the library paths for package install/uninstall
// Watch the library paths for package install/uninstall
const importFailureInfo: string[] = [];
const watchList = findPythonSearchPaths(
this._fs,
@ -967,7 +967,7 @@ export class AnalyzerService {
this._removeConfigFileWatcher();
if (this._configFilePath) {
this._configFileWatcher = this._fs.createFileSystemWatcher([this._configFilePath], 'all', event => {
this._configFileWatcher = this._fs.createFileSystemWatcher([this._configFilePath], event => {
if (this._verboseOutput) {
this._console.log(`Received fs event '${event}' for config file`);
}

View File

@ -22,12 +22,12 @@ import { assert } from '../common/debug';
import { Diagnostic, DiagnosticCategory } from '../common/diagnostic';
import { DiagnosticSink, TextRangeDiagnosticSink } from '../common/diagnosticSink';
import { TextEditAction } from '../common/editAction';
import { FileSystem } from '../common/fileSystem';
import { getFileName, normalizeSlashes } from '../common/pathUtils';
import * as StringUtils from '../common/stringUtils';
import { DocumentRange, getEmptyRange, Position, TextRange } from '../common/textRange';
import { TextRangeCollection } from '../common/textRangeCollection';
import { timingStats } from '../common/timing';
import { VirtualFileSystem } from '../common/vfs';
import { CompletionItemData, CompletionProvider, ModuleSymbolMap } from '../languageService/completionProvider';
import { DefinitionProvider } from '../languageService/definitionProvider';
import { DocumentSymbolProvider } from '../languageService/documentSymbolProvider';
@ -144,10 +144,10 @@ export class SourceFile {
private _typingModulePath?: string;
private _collectionsModulePath?: string;
readonly fileSystem: VirtualFileSystem;
readonly fileSystem: FileSystem;
constructor(
fs: VirtualFileSystem,
fs: FileSystem,
filePath: string,
isTypeshedStubFile: boolean,
isThirdPartyImport: boolean,

View File

@ -12,9 +12,9 @@ import { isAbsolute } from 'path';
import * as pathConsts from '../common/pathConsts';
import { ConsoleInterface } from './console';
import { DiagnosticRule } from './diagnosticRules';
import { FileSystem } from './fileSystem';
import { combinePaths, ensureTrailingDirectorySeparator, FileSpec, getFileSpec, normalizePath } from './pathUtils';
import { latestStablePythonVersion, PythonVersion, versionFromString } from './pythonVersion';
import { VirtualFileSystem } from './vfs';
export class ExecutionEnvironment {
// Default to "." which indicates every file in the project.
@ -423,7 +423,7 @@ export class ConfigOptions {
return execEnv;
}
addExecEnvironmentForAutoSearchPaths(fs: VirtualFileSystem) {
addExecEnvironmentForAutoSearchPaths(fs: FileSystem) {
// Auto-detect the common scenario where the sources are under the src folder
const srcPath = combinePaths(this.projectRoot, pathConsts.src);
if (fs.existsSync(srcPath) && !fs.existsSync(combinePaths(srcPath, '__init__.py'))) {

View File

@ -1,10 +1,11 @@
/*
* vfs.ts
* fileSystem.ts
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Defines virtual file system interface that our code will operate upon and
* factory method to expose real file system as virtual file system
* Defines a "file system provider" abstraction used throughout the
* code base. This abstraction allows us to swap out a real file system
* implementation for a virtual (mocked) implementation for testing.
*/
/* eslint-disable no-dupe-class-members */
@ -15,7 +16,7 @@ import * as fs from 'fs';
import { ConsoleInterface, NullConsole } from './console';
export type Listener = (
export type FileWatcherEventHandler = (
eventName: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir',
path: string,
stats?: Stats
@ -37,7 +38,7 @@ export interface Stats {
isSocket(): boolean;
}
export interface VirtualFileSystem {
export interface FileSystem {
existsSync(path: string): boolean;
mkdirSync(path: string): void;
chdir(path: string): void;
@ -55,15 +56,11 @@ export interface VirtualFileSystem {
recursive?: boolean,
listener?: (event: string, filename: string) => void
): FileWatcher;
createFileSystemWatcher(paths: string[], event: 'all', listener: Listener): FileWatcher;
createFileSystemWatcher(paths: string[], listener: FileWatcherEventHandler): FileWatcher;
}
/**
* expose real file system as virtual file system
* @param console console to log messages
*/
export function createFromRealFileSystem(console?: ConsoleInterface): VirtualFileSystem {
return new FileSystem(console ?? new NullConsole());
export function createFromRealFileSystem(console?: ConsoleInterface): FileSystem {
return new RealFileSystem(console ?? new NullConsole());
}
const _isMacintosh = process.platform === 'darwin';
@ -77,30 +74,36 @@ class LowLevelWatcher implements FileWatcher {
}
}
class FileSystem implements VirtualFileSystem {
class RealFileSystem implements FileSystem {
constructor(private _console: ConsoleInterface) {}
existsSync(path: string) {
return fs.existsSync(path);
}
mkdirSync(path: string) {
fs.mkdirSync(path);
}
chdir(path: string) {
process.chdir(path);
}
readdirSync(path: string) {
return fs.readdirSync(path);
}
readFileSync(path: string, encoding?: null): Buffer;
readFileSync(path: string, encoding: string): string;
readFileSync(path: string, encoding?: string | null): Buffer | string;
readFileSync(path: string, encoding: string | null = null) {
return fs.readFileSync(path, { encoding });
}
writeFileSync(path: string, data: string | Buffer, encoding: string | null) {
fs.writeFileSync(path, data, { encoding });
}
statSync(path: string) {
return fs.statSync(path);
}
@ -130,8 +133,8 @@ class FileSystem implements VirtualFileSystem {
return new LowLevelWatcher(paths);
}
createFileSystemWatcher(paths: string[], event: 'all', listener: Listener): FileWatcher {
return this._createBaseFileSystemWatcher(paths).on(event, listener);
createFileSystemWatcher(paths: string[], listener: FileWatcherEventHandler): FileWatcher {
return this._createBaseFileSystemWatcher(paths).on('all', listener);
}
private _createBaseFileSystemWatcher(paths: string[]): chokidar.FSWatcher {

View File

@ -14,6 +14,7 @@ import { URI } from 'vscode-uri';
import { some } from './collectionUtils';
import { compareValues, Comparison, GetCanonicalFileName, identity } from './core';
import * as debug from './debug';
import { FileSystem } from './fileSystem';
import {
compareStringsCaseInsensitive,
compareStringsCaseSensitive,
@ -21,7 +22,6 @@ import {
equateStringsCaseSensitive,
getStringComparer
} from './stringUtils';
import { VirtualFileSystem } from './vfs';
export interface FileSpec {
// File specs can contain wildcard characters (**, *, ?). This
@ -153,7 +153,7 @@ export function getRelativePath(dirPath: string, relativeTo: string) {
}
// Creates a directory hierarchy for a path, starting from some ancestor path.
export function makeDirectories(fs: VirtualFileSystem, dirPath: string, startingFromDirPath: string) {
export function makeDirectories(fs: FileSystem, dirPath: string, startingFromDirPath: string) {
if (!dirPath.startsWith(startingFromDirPath)) {
return;
}
@ -170,7 +170,7 @@ export function makeDirectories(fs: VirtualFileSystem, dirPath: string, starting
}
}
export function getFileSize(fs: VirtualFileSystem, path: string) {
export function getFileSize(fs: FileSystem, path: string) {
try {
const stat = fs.statSync(path);
if (stat.isFile()) {
@ -182,11 +182,11 @@ export function getFileSize(fs: VirtualFileSystem, path: string) {
return 0;
}
export function fileExists(fs: VirtualFileSystem, path: string): boolean {
export function fileExists(fs: FileSystem, path: string): boolean {
return fileSystemEntryExists(fs, path, FileSystemEntryKind.File);
}
export function directoryExists(fs: VirtualFileSystem, path: string): boolean {
export function directoryExists(fs: FileSystem, path: string): boolean {
return fileSystemEntryExists(fs, path, FileSystemEntryKind.Directory);
}
@ -317,12 +317,12 @@ export function changeAnyExtension(
extensions?: string | readonly string[],
ignoreCase?: boolean
): string {
const pathext =
const pathExt =
extensions !== undefined && ignoreCase !== undefined
? getAnyExtensionFromPath(path, extensions, ignoreCase)
: getAnyExtensionFromPath(path);
return pathext ? path.slice(0, path.length - pathext.length) + (ext.startsWith('.') ? ext : '.' + ext) : path;
return pathExt ? path.slice(0, path.length - pathExt.length) + (ext.startsWith('.') ? ext : '.' + ext) : path;
}
/**
@ -374,7 +374,7 @@ export function getAnyExtensionFromPath(
/**
* Returns the path except for its containing directory name.
* Semantics align with NodeJS's `path.basename` except that we support URL's as well.
* Semantics align with NodeJS's `path.basename` except that we support URLs as well.
*
* ```ts
* // POSIX
@ -391,7 +391,7 @@ export function getAnyExtensionFromPath(
export function getBaseFileName(pathString: string): string;
/**
* Gets the portion of a path following the last (non-terminal) separator (`/`).
* Semantics align with NodeJS's `path.basename` except that we support URL's as well.
* Semantics align with NodeJS's `path.basename` except that we support URLs as well.
* If the base name has any one of the provided extensions, it is removed.
*
* ```ts
@ -516,7 +516,7 @@ export function normalizePath(pathString: string): string {
return normalizeSlashes(path.normalize(pathString));
}
export function isDirectory(fs: VirtualFileSystem, path: string): boolean {
export function isDirectory(fs: FileSystem, path: string): boolean {
let stat: any;
try {
stat = fs.statSync(path);
@ -527,7 +527,7 @@ export function isDirectory(fs: VirtualFileSystem, path: string): boolean {
return stat.isDirectory();
}
export function isFile(fs: VirtualFileSystem, path: string): boolean {
export function isFile(fs: FileSystem, path: string): boolean {
let stat: any;
try {
stat = fs.statSync(path);
@ -538,7 +538,7 @@ export function isFile(fs: VirtualFileSystem, path: string): boolean {
return stat.isFile();
}
export function getFileSystemEntries(fs: VirtualFileSystem, path: string): FileSystemEntries {
export function getFileSystemEntries(fs: FileSystem, path: string): FileSystemEntries {
try {
const entries = fs.readdirSync(path || '.').sort();
const files: string[] = [];
@ -810,7 +810,7 @@ const enum FileSystemEntryKind {
Directory
}
function fileSystemEntryExists(fs: VirtualFileSystem, path: string, entryKind: FileSystemEntryKind): boolean {
function fileSystemEntryExists(fs: FileSystem, path: string, entryKind: FileSystemEntryKind): boolean {
try {
const stat = fs.statSync(path);
switch (entryKind) {

View File

@ -2,6 +2,9 @@
* languageServerBase.ts
*
* Implements common language server functionality.
* This is split out as a base class to allow for
* different language server variants to be created
* from the same core functionality.
*/
import './common/extensions';
@ -42,9 +45,9 @@ import { ConfigOptions } from './common/configOptions';
import { ConsoleInterface } from './common/console';
import { Diagnostic as AnalyzerDiagnostic, DiagnosticCategory } from './common/diagnostic';
import { LanguageServiceExtension } from './common/extensibility';
import { createFromRealFileSystem, FileSystem } from './common/fileSystem';
import { convertPathToUri, convertUriToPath } from './common/pathUtils';
import { Position } from './common/textRange';
import { createFromRealFileSystem, VirtualFileSystem } from './common/vfs';
import { AnalyzerServiceExecutor } from './languageService/analyzerServiceExecutor';
import { CompletionItemData } from './languageService/completionProvider';
import { convertHoverResults } from './languageService/hoverProvider';
@ -84,7 +87,7 @@ export interface LanguageServerInterface {
readonly rootPath: string;
readonly console: ConsoleInterface;
readonly window: WindowInterface;
readonly fs: VirtualFileSystem;
readonly fs: FileSystem;
}
export abstract class LanguageServerBase implements LanguageServerInterface {
@ -101,7 +104,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
rootPath = '';
// File system abstraction.
fs: VirtualFileSystem;
fs: FileSystem;
constructor(private _productName: string, rootDirectory: string, private _extension?: LanguageServiceExtension) {
this._connection.console.log(`${_productName} language server starting`);
@ -146,7 +149,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
return undefined;
}
protected createImportResolver(fs: VirtualFileSystem, options: ConfigOptions): ImportResolver {
protected createImportResolver(fs: FileSystem, options: ConfigOptions): ImportResolver {
return new ImportResolver(fs, options);
}

View File

@ -25,7 +25,7 @@ import { DiagnosticCategory } from './common/diagnostic';
import { FileDiagnostics } from './common/diagnosticSink';
import { combinePaths, normalizePath } from './common/pathUtils';
import { Range } from './common/textRange';
import { createFromRealFileSystem } from './common/vfs';
import { createFromRealFileSystem } from './common/fileSystem';
const toolName = 'pyright';

View File

@ -13,8 +13,8 @@ import { AnalyzerService } from '../analyzer/service';
import { CommandLineOptions } from '../common/commandLineOptions';
import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
import { NullConsole } from '../common/console';
import { createFromRealFileSystem } from '../common/fileSystem';
import { combinePaths, getBaseFileName, normalizePath, normalizeSlashes } from '../common/pathUtils';
import { createFromRealFileSystem } from '../common/vfs';
test('FindFilesWithConfigFile', () => {
const cwd = normalizePath(combinePaths(process.cwd(), '../server'));

View File

@ -3,14 +3,14 @@
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* test mock that implements LanguageServiceInterface
* Test mock that implements LanguageServiceInterface
*/
import * as path from 'path';
import { ConsoleInterface } from '../../../common/console';
import * as debug from '../../../common/debug';
import { VirtualFileSystem } from '../../../common/vfs';
import { FileSystem } from '../../../common/fileSystem';
import {
LanguageServerInterface,
ServerSettings,
@ -21,11 +21,7 @@ import {
export class TestLanguageService implements LanguageServerInterface {
private readonly _workspace: WorkspaceServiceInstance;
constructor(
workspace: WorkspaceServiceInstance,
readonly console: ConsoleInterface,
readonly fs: VirtualFileSystem
) {
constructor(workspace: WorkspaceServiceInstance, readonly console: ConsoleInterface, readonly fs: FileSystem) {
this._workspace = workspace;
}

View File

@ -8,6 +8,7 @@ import * as os from 'os';
import * as pathModule from 'path';
import { NullConsole } from '../../common/console';
import { createFromRealFileSystem } from '../../common/fileSystem';
import {
combinePaths,
directoryExists,
@ -17,7 +18,6 @@ import {
resolvePaths
} from '../../common/pathUtils';
import { compareStringsCaseInsensitive, compareStringsCaseSensitive } from '../../common/stringUtils';
import { createFromRealFileSystem } from '../../common/vfs';
export const HOST: TestHost = createHost();

View File

@ -7,8 +7,8 @@
*/
/* eslint-disable no-dupe-class-members */
import { FileWatcher, FileWatcherEventHandler } from '../../../common/fileSystem';
import * as pathUtil from '../../../common/pathUtils';
import { FileWatcher, Listener } from '../../../common/vfs';
import { bufferFrom, createIOError } from '../utils';
import { closeIterator, getIterator, Metadata, nextResult, SortedMap } from './../utils';
import { validate, ValidationFlags } from './pathValidation';
@ -305,7 +305,7 @@ export class FileSystem {
return results;
}
createFileSystemWatcher(paths: string[], event: 'all', listener: Listener): FileWatcher {
createFileSystemWatcher(paths: string[], listener: FileWatcherEventHandler): FileWatcher {
return {
close: () => {
/* left empty */

View File

@ -10,8 +10,8 @@
import { ImportResolver } from '../analyzer/importResolver';
import { SourceFile } from '../analyzer/sourceFile';
import { ConfigOptions } from '../common/configOptions';
import { createFromRealFileSystem } from '../common/fileSystem';
import { combinePaths } from '../common/pathUtils';
import { createFromRealFileSystem } from '../common/vfs';
test('Empty', () => {
const filePath = combinePaths(process.cwd(), 'tests/samples/test_file1.py');

View File

@ -19,7 +19,7 @@ import { cloneDiagnosticSettings, ConfigOptions, ExecutionEnvironment } from '..
import { fail } from '../common/debug';
import { Diagnostic, DiagnosticCategory } from '../common/diagnostic';
import { DiagnosticSink, TextRangeDiagnosticSink } from '../common/diagnosticSink';
import { createFromRealFileSystem } from '../common/vfs';
import { createFromRealFileSystem } from '../common/fileSystem';
import { ParseOptions, Parser, ParseResults } from '../parser/parser';
// This is a bit gross, but it's necessary to allow the fallback typeshed