Skip to content

Commit

Permalink
Add caching to LanguageService pugin (#761)
Browse files Browse the repository at this point in the history
Fixes: #759
  • Loading branch information
ajafff authored Feb 14, 2021
1 parent 87bcc17 commit ad0e5b8
Show file tree
Hide file tree
Showing 22 changed files with 286 additions and 112 deletions.
1 change: 1 addition & 0 deletions baselines/packages/wotan/api/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from '@fimbul/ymir';
export * from './src/services/default/builtin-resolver';
export * from './src/services/default/cache-factory';
export * from './src/services/default/configuration-provider';
export * from './src/services/default/content-hasher';
export * from './src/services/default/deprecation-handler';
export * from './src/services/default/directory-service';
export * from './src/services/default/file-filter';
Expand Down
1 change: 1 addition & 0 deletions baselines/packages/wotan/api/language-service/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export declare class LanguageServiceInterceptor implements PartialLanguageServic
updateConfig(config: Record<string, unknown>): void;
getSemanticDiagnostics(diagnostics: ts.Diagnostic[], fileName: string): ts.Diagnostic[];
getSupportedCodeFixes(fixes: string[]): string[];
cleanupSemanticCache(): void;
dispose(): void;
}
3 changes: 3 additions & 0 deletions baselines/packages/wotan/api/packlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ src/services/default/cache-factory.js.map
src/services/default/configuration-provider.d.ts
src/services/default/configuration-provider.js
src/services/default/configuration-provider.js.map
src/services/default/content-hasher.d.ts
src/services/default/content-hasher.js
src/services/default/content-hasher.js.map
src/services/default/deprecation-handler.d.ts
src/services/default/deprecation-handler.js
src/services/default/deprecation-handler.js.map
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { ContentId, ContentIdHost } from '@fimbul/ymir';
export declare class ContentHasher implements ContentId {
forFile(fileName: string, host: ContentIdHost): string;
}
6 changes: 3 additions & 3 deletions baselines/packages/wotan/api/src/services/program-state.d.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import * as ts from 'typescript';
import { DependencyResolver, DependencyResolverFactory, DependencyResolverHost } from './dependency-resolver';
import { Finding, StatePersistence } from '@fimbul/ymir';
import { ContentId, Finding, StatePersistence } from '@fimbul/ymir';
export interface ProgramState {
update(program: ts.Program, updatedFile: string): void;
getUpToDateResult(fileName: string, configHash: string): readonly Finding[] | undefined;
setFileResult(fileName: string, configHash: string, result: readonly Finding[]): void;
save(): void;
}
export declare class ProgramStateFactory {
constructor(resolverFactory: DependencyResolverFactory, statePersistence: StatePersistence);
constructor(resolverFactory: DependencyResolverFactory, statePersistence: StatePersistence, contentId: ContentId);
create(program: ts.Program, host: ProgramStateHost & DependencyResolverHost, tsconfigPath: string): ProgramStateImpl;
}
export declare type ProgramStateHost = Pick<ts.CompilerHost, 'useCaseSensitiveFileNames'>;
declare const oldStateSymbol: unique symbol;
declare class ProgramStateImpl implements ProgramState {
constructor(host: ProgramStateHost, program: ts.Program, resolver: DependencyResolver, statePersistence: StatePersistence, project: string);
constructor(host: ProgramStateHost, program: ts.Program, resolver: DependencyResolver, statePersistence: StatePersistence, contentId: ContentId, project: string);
update(program: ts.Program, updatedFile: string): void;
getUpToDateResult(fileName: string, configHash: string): readonly Finding[] | undefined;
setFileResult(fileName: string, configHash: string, result: ReadonlyArray<Finding>): void;
Expand Down
74 changes: 48 additions & 26 deletions baselines/packages/wotan/test/program-state.spec.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,24 @@ Generated by [AVA](https://avajs.dev).
`cs: false␊
files:␊
- config: '1234'␊
hash: '-2704852577'␊
id: '-2704852577'␊
result: []␊
- config: '1234'␊
dependencies:␊
./c:␊
- 0␊
hash: '-5185547329'␊
id: '-5185547329'␊
result: []␊
- dependencies:␊
./c:␊
- 0␊
hash: '-5185547329'␊
id: '-5185547329'␊
- dependencies:␊
./b:␊
- 1␊
./d:␊
- 2␊
hash: '-2126001415'␊
id: '-2126001415'␊
global: []␊
lookup:␊
a.ts: 3␊
Expand All @@ -44,31 +44,31 @@ Generated by [AVA](https://avajs.dev).
`cs: false␊
files:␊
- config: '1234'␊
hash: '-2704852577'␊
id: '-2704852577'␊
result: []␊
- config: '1234'␊
dependencies:␊
./c:␊
- 0␊
hash: '-5185547329'␊
id: '-5185547329'␊
result: []␊
- config: '1234'␊
dependencies:␊
"\\0":␊
- 3␊
hash: '5381'␊
id: '5381'␊
result: []␊
- dependencies:␊
./e:␊
- 2␊
- 3␊
hash: '910822549'␊
id: '910822549'␊
- dependencies:␊
./b:␊
- 1␊
./d:␊
- 3␊
hash: '-2126001415'␊
id: '-2126001415'␊
global: []␊
lookup:␊
a.ts: 4␊
Expand All @@ -84,27 +84,27 @@ Generated by [AVA](https://avajs.dev).
`cs: false␊
files:␊
- hash: '-2704852577'␊
- id: '-2704852577'␊
- dependencies:␊
./c:␊
- 0␊
hash: '-5185547329'␊
id: '-5185547329'␊
- dependencies:␊
"\\0":␊
- 3␊
e: null␊
hash: '8844149038'␊
id: '8844149038'␊
- dependencies:␊
./e:␊
- 2␊
- 3␊
hash: '910822549'␊
id: '910822549'␊
- dependencies:␊
./b:␊
- 1␊
./d:␊
- 3␊
hash: '-2126001415'␊
id: '-2126001415'␊
global:␊
- 2␊
lookup:␊
Expand All @@ -124,16 +124,16 @@ Generated by [AVA](https://avajs.dev).
`cs: false␊
files:␊
- config: '1234'␊
hash: '-3360789062'␊
id: '-3360789062'␊
result: []␊
- config: '1234'␊
hash: '574235295'␊
id: '574235295'␊
result: []␊
- config: '5678'␊
dependencies:␊
./c:␊
- 1␊
hash: '-5185547329'␊
id: '-5185547329'␊
result: []␊
global:␊
- 1␊
Expand All @@ -149,14 +149,14 @@ Generated by [AVA](https://avajs.dev).
`cs: false␊
files:␊
- hash: '-3360789062'␊
- id: '-3360789062'␊
- config: '5678'␊
hash: '4830905933'␊
id: '4830905933'␊
result: []␊
- dependencies:␊
./c:␊
- 1␊
hash: '-5185547329'␊
id: '-5185547329'␊
global:␊
- 1␊
lookup:␊
Expand All @@ -174,13 +174,13 @@ Generated by [AVA](https://avajs.dev).
`cs: false␊
files:␊
- config: '1234'␊
hash: '-3360789062'␊
id: '-3360789062'␊
result: []␊
- config: '5678'␊
dependencies:␊
./d:␊
- 3␊
hash: '-2335134369'␊
id: '-2335134369'␊
result: []␊
- dependencies:␊
./a:␊
Expand All @@ -189,13 +189,13 @@ Generated by [AVA](https://avajs.dev).
- 1␊
./d:␊
- 3␊
hash: '-10011708814'␊
id: '-10011708814'␊
- dependencies:␊
./b:␊
- 2␊
./e:␊
- 0␊
hash: '970287226'␊
id: '970287226'␊
- dependencies:␊
./b:␊
- 2␊
Expand All @@ -205,11 +205,11 @@ Generated by [AVA](https://avajs.dev).
- 3␊
./e:␊
- 0␊
hash: '3028017583'␊
id: '3028017583'␊
- dependencies:␊
./a:␊
- 4␊
hash: '-5185549507'␊
id: '-5185549507'␊
global: []␊
lookup:␊
a.ts: 4␊
Expand All @@ -221,3 +221,25 @@ Generated by [AVA](https://avajs.dev).
options: '5864093'␊
v: 1␊
`

## doesn't throw if resolved file is not in project

> Snapshot 1
`cs: false␊
files:␊
- config: '1234'␊
dependencies:␊
./b.js:␊
- 1␊
./tsconfig.json: null␊
id: '5754526661'␊
result: []␊
- id: N/A␊
global: []␊
lookup:␊
a.ts: 0␊
b.js: 1␊
options: '5864093'␊
v: 1␊
`
Binary file modified baselines/packages/wotan/test/program-state.spec.ts.snap
Binary file not shown.
4 changes: 2 additions & 2 deletions baselines/packages/wotan/test/runner.spec.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ Generated by [AVA](https://avajs.dev).
## updated-state

`files:␊
- hash: '5901983184'␊
- id: '5901983184'␊
- config: '-10079848193'␊
hash: '5860249'␊
id: '5860249'␊
result: []␊
global:␊
- 0␊
Expand Down
Binary file modified baselines/packages/wotan/test/runner.spec.ts.snap
Binary file not shown.
12 changes: 9 additions & 3 deletions baselines/packages/ymir/api/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,12 @@ export interface FileFilter {
/** @returns `true` if the file should be linted, false if it should be filtered out. Intended for use in `Array.prototype.filter`. */
filter(file: ts.SourceFile): boolean;
}
export declare type ContentIdHost = Pick<ts.CompilerHost, 'readFile'>;
export interface ContentId {
forFile(fileName: string, host: ContentIdHost): string;
}
export declare abstract class ContentId {
}
export interface StatePersistence {
loadState(project: string): StaticProgramState | undefined;
saveState(project: string, state: StaticProgramState): void;
Expand All @@ -387,15 +393,15 @@ export interface StaticProgramState {
}
export declare namespace StaticProgramState {
interface FileState {
/** Hash of file contents */
readonly hash: string;
/** ID of file contents (typically a hash) */
readonly id: string;
/**
* Key: module specifier as referenced in the file, order may be random
* Value: - `null` if dependency could not be resolved
* - List of files (or rather their index) that the module specifier resolves to.
* That is the actual file at that path and/or files containing `declare module "..."` for that module specifier.
* May contain the current file.
* This list is ordered by the hash of the files ascending,
* This list is ordered by the ID of the files ascending,
*/
readonly dependencies?: Readonly<Record<string, null | readonly number[]>>;
/** The list of findings if this file has up-to-date results */
Expand Down
3 changes: 2 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ There are several core services that are provided by Wotan through the Container
* `FormatterLoader` loads core and custom formatters via `FormatterLoaderHost`.
* `Linter` executes a given set of rules on a SourceFile. It automatically loads enabled rules using `RuleLoader` and filters out disabled findings using `FindingFilterFactory`. `Linter` can also automatically fix findings and return the fixed source code. It does not access the file system.
* `ProcessorLoader` loads and caches processors using `Resolver`.
* `ProgramStateFactory` creates a service to get lint results for up-to-date files from cache and update the cache as necessary. Uses `StatePersistence` to load the cache for the current project. Uses `DependencyResolverFactory` to find out about file dependencies.
* `ProgramStateFactory` creates a service to get lint results for up-to-date files from cache and update the cache as necessary. Uses `StatePersistence` to load the cache for the current project. Uses `DependencyResolverFactory` to find out about file dependencies. `ContentId` is used to detect changes to files without storing the whole file content in cache.
* `RuleLoader` loads and caches core and custom rules via `RuleLoaderHost`.
* `Runner` is used to lint a collection of files. If you want to lint a project, you provide the path of one or more `tsconfig.json` and it creates the project internally. `Runner` loads the source code from the file system, loads configuration from `ConfigurationManager`, applies processors if specified in the configuration and lints all (matching) files using `Linter`. It uses `FileFilterFactory` to filter out non-user code. If caching is enabled, it uses `ProgramStateFactory` to load the cached results and update the cache.

Expand All @@ -23,6 +23,7 @@ The default implementations (targeting the Node.js runtime environment) are prov
* `BuiltinResolver` (`DefaultBuiltinResolver`) resolves the path to core rules, formatters and configs in `@fimbul/mimir`.
* `CacheFactory` (`DefaultCacheFactory`) is responsible for creating cache objects that are used by other services to store their data.
* `ConfigurationProvider` (`DefaultConfigurationProvider`) is responsible to find, resolve and load configuration files.
* `ContentId` (`ContentHasher`) computes an ID representing the file's content (typically a hash).
* `DeprecationHandler` (`DefaultDeprecationHandler`) is notified everytime a deprecated rule, formatter of processor is used. This service can choose to inform the user or just swallow the event.
* `DirectoryService` (`NodeDirectoryService`) provides the current directory. None of the builtin services cache the current directory. Therefore you can change it dynamically if you need to.
* `FileFilterFactory` (`DefaultFileFilterFactory`) creates a `FileFilter` for a given Program, that is responsible for filtering out non-user code. By default it excludes `lib.xxx.d.ts`, `@types`, declaration and javascript files of imported modules, json files and declaration files of project references.
Expand Down
1 change: 1 addition & 0 deletions packages/wotan/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from '@fimbul/ymir';
export * from './src/services/default/builtin-resolver';
export * from './src/services/default/cache-factory';
export * from './src/services/default/configuration-provider';
export * from './src/services/default/content-hasher';
export * from './src/services/default/deprecation-handler';
export * from './src/services/default/directory-service';
export * from './src/services/default/file-filter';
Expand Down
Loading

0 comments on commit ad0e5b8

Please sign in to comment.