From 5bc9309ef6df44e8c3c7116c3d0fa28926088f75 Mon Sep 17 00:00:00 2001 From: Nikolaj Kappler Date: Tue, 23 Oct 2018 14:09:58 +0200 Subject: [PATCH 1/9] initial commit for tern types workstream: --- types/tern/index.d.ts | 0 types/tern/lib/infer/index.d.ts | 121 +++++++++ types/tern/lib/tern/index.d.ts | 459 ++++++++++++++++++++++++++++++++ types/tern/test/tern.test.ts | 33 +++ types/tern/tsconfig.json | 25 ++ types/tern/tslint.json | 3 + 6 files changed, 641 insertions(+) create mode 100644 types/tern/index.d.ts create mode 100644 types/tern/lib/infer/index.d.ts create mode 100644 types/tern/lib/tern/index.d.ts create mode 100644 types/tern/test/tern.test.ts create mode 100644 types/tern/tsconfig.json create mode 100644 types/tern/tslint.json diff --git a/types/tern/index.d.ts b/types/tern/index.d.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/types/tern/lib/infer/index.d.ts b/types/tern/lib/infer/index.d.ts new file mode 100644 index 0000000000..161a9a69cb --- /dev/null +++ b/types/tern/lib/infer/index.d.ts @@ -0,0 +1,121 @@ +import * as ESTree from "estree"; + +// #### Context #### +interface ContextConstructor { + new(defs: any[]): Context; +} +export const Context: ContextConstructor; +export interface Context { + topScope: Scope; +} +export function cx(): Context; +export function withContext(context: Context, f: () => void): void; + +// #### Analysis #### +export function parse(text: string, options?: {}): ESTree.Program; +export function analyze(ast: ESTree.Program, name: string, scope?: Scope): void; +export function purgeTypes(origins: string[], start?: number, end?: number): void; +export function markVariablesDefinedBy(scope: Scope, origins: string[], start?: number, end?: number): void; +export function purgeMarkedVariables(): void; + +// #### Types #### +interface ObjConstructor { + new(proto: object | true | null, name?: string): Obj; +} +export const Obj: ObjConstructor; +export interface Obj { + proto: any; + props: Readonly<{ + [key: string]: AVal; + }>; + hasProp(prop: string): AVal | null; + defProp(prop: string, originNode?: ESTree.Node): AVal; +} + +interface FnConstructor { + new(name: string | undefined, self: AVal, args: AVal[], argNames: string[], retval: AVal): Fn; +} +export const Fn: FnConstructor; +export interface Fn extends Obj { } + +interface ArrConstructor { + new(contentType: AVal): Arr; +} +export const Arr: ArrConstructor; +export interface Arr extends Obj { } + +export interface Type { + name: string; + origin: string; + originNode: ESTree.Node; + toString(maxDepth: number): string; + getProp(prop: string): AVal; + forAllProps(f: (prop: string, val: AVal, local: boolean) => void): void; +} + + + +// #### Abstract Values #### +export const ANull: ANull; +export interface ANull { + addType(type?: never, weight?: never): void; + propagate(target: never): void; + getProp(): ANull; + forAllProps(): void; + hasType(type?: never): boolean; //always false + isEmpty(): boolean; //always true + getFunctionType(): void; + getObjType(): void; + getSymbolType(): void; + getType(guess?: never): void; + gatherProperties(): void; + propagatesTo(): void; + typeHint(): void; + propHint(): void; + toString(): "?"; +} + +interface AValConstructor { + new(): AVal; +} +export const AVal: AValConstructor; +export interface AVal extends ANull { + addType(type: Type, weight?: number): void; + propagate(target: Constraint): void; + hasType(type: Type): boolean; + isEmpty(): boolean; + getType(guess?: boolean): Type | undefined; + getFunctionType(): Type | undefined; + originNode?: ESTree.Node; +} + +// #### Constraints #### +interface ConstraintConstructor { + new(methods: object): { new(): any }; +} +export const constraint: ConstraintConstructor; +export interface Constraint { + addType: AVal["addType"]; + typeHint(): Type | undefined; + propHint(): string | undefined; +} + +// #### Scopes #### +interface ScopeConstructor { + new(parent?: Scope): Scope; +} +export const Scope: ScopeConstructor; +export interface Scope { + defVar(name: string, originNode: ESTree.Node): AVal; +} + +// #### Utilities #### +export function findExpressionAt(ast: ESTree.Program, start: number | undefined, end: number, scope?: Scope): { node: ESTree.Node, state: Scope } | null; +export function findExpressionAround(ast: ESTree.Program, start: number | undefined, end: number, scope?: Scope): { node: ESTree.Node, state: Scope } | null; +export function expressionType(expr: { node: ESTree.Node, state: Scope }): AVal | Type; +export function scopeAt(ast: ESTree.Program, pos: number, scope?: Scope): Scope; +export function findRefs(ast: ESTree.Program, scope: Scope, name: string, refScope: Scope, f: (Node: ESTree.Node, Scope: Scope) => void): void; +export function findPropRefs(ast: ESTree.Program, scope: Scope, objType: Obj, propName: string, f: (Node: ESTree.Node) => void): void; +export function didGuess(): boolean; +export function resetGuessing(val?: boolean): void; + diff --git a/types/tern/lib/tern/index.d.ts b/types/tern/lib/tern/index.d.ts new file mode 100644 index 0000000000..00c014c1a4 --- /dev/null +++ b/types/tern/lib/tern/index.d.ts @@ -0,0 +1,459 @@ +// Type definitions for tern 1.3 0.22 +// Project: https://github.com/ternjs/tern +// Definitions by: Nikolaj Kappler +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +// Still WIP! some definitions may be incomplete or missing + +import * as ESTree from "estree"; +import { AVal, Scope } from "../infer"; + +// #### Programming interface #### +export interface ConstructorOptions { + /** Indicates whether `getFile` is asynchronous. Default is `false`. */ + async: boolean; + + /** The definition objects to load into the server’s environment. */ + defs: Def[]; + + /** The ECMAScript version to parse. Should be either 5 or 6. Default is 6. */ + ecmaVersion: 5 | 6; + + /** Indicates the maximum amount of milliseconds to wait for an asynchronous getFile before giving up on it. Defaults to 1000. */ + fetchTimeout: number; + + /** Specifies the set of plugins that the server should load. The property names of the object name the plugins, and their values hold options that will be passed to them. */ + plugins: { [key: string]: object }; + + /** + * Provides a way for the server to try and fetch the content of files. + * Depending on the `async` option, this is either a function that takes a filename and returns a string (when not `async`), or + * a function that takes a `filename` and a `callback`, and calls the callback with an optional `error` as the first argument, + * and the `content` string (if no error) as the second. + */ + getFile(filename: string): string; + /** + * Provides a way for the server to try and fetch the content of files. + * Depending on the `async` option, this is either a function that takes a filename and returns a string (when not `async`), or + * a function that takes a `filename` and a `callback`, and calls the callback with an optional `error` as the first argument, + * and the `content` string (if no error) as the second. + */ + getFile(filename: string, callback: (error: Error | undefined, content?: string) => void): void; + +} + +interface TernConstructor { + new(options?: ConstructorOptions): Server; +} + +export const Server: TernConstructor; + +export interface Server { + + /** + * Add a set of type definitions to the server. If `atFront` is true, they will be added before all other + * existing definitions. Otherwise, they are added at the back. + */ + addDefs(defs: Def[], atFront?: boolean): void; + + /** + * Register a file with the server. Note that files can also be included in requests. When using this + * to automatically load a dependency, specify the name of the file (as Tern knows it) as the third + * argument. That way, the file is counted towards the dependency budget of the root of its dependency graph. + */ + addFile(name: string, text?: string, parent?: string): void; + + /** Unregister a file. */ + delFile(name: string): void; + + /** + * Delete a set of type definitions from the server, by providing the name, taken from + * `defs[!name]` property from the definitions. If that property is not available in the + * current type definitions, it can’t be removed. + */ + deleteDefs(name: string): void; + + /** Forces all files to be fetched an analyzed, and then calls the callback function. */ + flush(callback: () => void): void; + + /** Load a server plugin (or don’t do anything, if the plugin is already loaded). */ + loadPlugin(name: string, options: object): void; + + /** Unregister an event handler. */ + off(eventType: K, handler: Events[K]): void; + + /** Register an event handler for the named type of event. */ + on(eventType: K, handler: Events[K]): void; + + /** + * Perform a request. `doc` is a (parsed) JSON document as described in the protocol documentation. + * The `callback` function will be called when the request completes. If an `error` occurred, + * it will be passed as a first argument. Otherwise, the `response` (parsed) JSON object will be passed as second argument. + * + * When the server hasn’t been configured to be asynchronous, the callback will be called before request returns. + */ + request( + doc: D & { query?: Q }, + callback: ( + error: Error | undefined, + response: (D extends { query: undefined } ? {} : D extends { query: Query } ? QueryResult : {}) | undefined + ) => void + ): void; + +} + +// #### JSON Protocol #### + +type QueryResult = QueryResultMap[Q["type"]]; + +export type Query = CompletionsQuery | TypeQuery | DefinitionQuery + | DocumentationQuery | RefsQuery | RenameQuery | PropertiesQuery | FilesQuery; + +interface QueryResultMap { + completions: CompletionsQueryResult; + type: TypeQueryResult; + definition: DefinitionQueryResult; + documentation: DocumentationQueryResult; + refs: RefsQueryResult; + rename: RenameQueryResult; + properties: PropertiesQueryResult; + files: FilesQueryResult; +} + +export interface Def { + [key: string]: string | Def; +} + +export interface Document { + query?: Query; + files?: File[]; + timeout?: number; +} + +export interface File { + name: string; + text: string; + scope: Scope; + ast: ESTree.Program; + type?: "full" | "part" | "delete"; +} + +export interface IQuery { + type: string; + lineCharPositions?: boolean; + docFormat?: "full"; +} + +export interface Type { + name: string; + origin: string; + originNode: ESTree.Node; + toString(maxDepth: number): string; + getProp(prop: string): AVal; + forAllProps(f: (prop: string, val: AVal, local: boolean) => void): void; +} + +interface Position { + ch: number; + line: number; +} + +/** Asks the server for a set of completions at the given point. */ +export interface CompletionsQuery extends IQuery { + /** Asks the server for a set of completions at the given point. */ + type: "completions"; + /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ + file: string; + /** Specify the location to complete at. */ + end: number | Position; + /** Whether to include the types of the completions in the result data. Default `false` */ + types?: boolean; + /** Whether to include the distance (in scopes for variables, in prototypes for properties) between the completions and the origin position in the result data. Default `false` */ + depths?: boolean; + /** Whether to include documentation strings in the result data. Default `false` */ + docs?: boolean; + /** Whether to include urls in the result data. Default `false` */ + urls?: boolean; + /** Whether to include origin files (if found) in the result data. Default `false` */ + origins?: boolean; + /** When on, only completions that match the current word at the given point will be returned. Turn this off to get all results, so that you can filter on the client side. Default `true` */ + filter?: boolean; + /** Whether to use a case-insensitive compare between the current word and potential completions. Default `false` */ + caseInsensitive?: boolean; + /** When completing a property and no completions are found, Tern will use some heuristics to try and return some properties anyway. Set this to `false` to turn that off. Default `true` */ + guess?: boolean; + /** Determines whether the result set will be sorted. Default `true` */ + sort?: boolean; + /** When disabled, only the text before the given position is considered part of the word. When enabled (the default), the whole variable name that the cursor is on will be included. Default `true` */ + expandWordForward?: boolean; + /** Whether to ignore the properties of `Object.prototype` unless they have been spelled out by at least two characters. Default `true` */ + omitObjectPrototype?: boolean; + /** Whether to include JavaScript keywords when completing something that is not a property. Default `false` */ + includeKeywords?: boolean; + /** If completions should be returned when inside a literal. Default `true` */ + inLiteral?: boolean; +} + +interface CompletionsQueryResult { + /** start offsets of the word that was completed */ + start: number | Position; + /** end offsets of the word that was completed */ + end: number | Position; + /** whether the completion is for a property or a variable */ + isProperty: boolean; + // TODO depends on completionsquery settings -> conditional types? + /** + * array of completions. When one of the `types`, `depths`, `docs`, `urls`, or `origins` + * options was passed, the array will hold objects with a `name` property (the completion text), + * and, depending on the options, `type`, `depth`, `doc`, `url`, and `origin` properties. + * When none of these options are enabled, the result array will hold plain strings. + */ + completions: string[] | { + name: string, + type?: string, + depth?: number, + doc?: string, + url?: string, + origin?: string + }[]; +} + +/** Query the type of something. */ +export interface TypeQuery extends IQuery { + /** Query the type of something. */ + type: "type"; + /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ + file: string; + /** Specify the location of the expression. */ + end: number | Position; + /** Specify the location of the expression. */ + start?: number | Position; + /** Set to `true` when you are interested in a function type. This will cause function types to win when something has multiple types. Default `false` */ + preferFunction?: boolean; + /** Determines how deep the type string must be expanded. Nested objects will only display property types up to this depth, and be represented by their type name or a representation showing only property names below it. Default `0` */ + depth?: number; +} + +interface TypeQueryResult { + /** A description of the type of the value. May be "?" when no type was found. */ + type: string; + /** Whether the given type was guessed, or should be considered reliable. */ + guess: boolean; + /** The name associated with the type. */ + name?: string; + /** When the inspected expression was an identifier or a property access, this will hold the name of the variable or property. */ + exprName?: string; + /** If the type had documentation associated with it, these will also be returned. */ + doc?: string; + /** If the type had urls associated with it, these will also be returned. */ + url?: string; + /** If the type had origin information associated with it, these will also be returned. */ + origin?: string; +} + +/** + * Asks for the definition of something. This will try, for a variable or property, + * to return the point at which it was defined. If that fails, or the chosen + * expression is not an identifier or property reference, it will try to return + * the definition site of the type the expression has. If no type is found, or the + * type is not an object or function (other types don’t store their definition site), + * it will fail to return useful information. + */ +export interface DefinitionQuery extends IQuery { + /** + * Asks for the definition of something. This will try, for a variable or property, + * to return the point at which it was defined. If that fails, or the chosen + * expression is not an identifier or property reference, it will try to return + * the definition site of the type the expression has. If no type is found, or the + * type is not an object or function (other types don’t store their definition site), + * it will fail to return useful information. + */ + type: "definition"; + /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ + file: string; + /** Specify the location of the expression. */ + end: number | Position; + /** Specify the location of the expression. */ + start?: number | Position; +} + +interface DefinitionQueryResult { + /** The start position of the expression. */ + start?: number | Position; + /** The end position of the expression. */ + end?: number | Position; + /** The file in which the definition was defined. */ + file?: string; + /** A slice of the code in front of the definition Can be used to find a definition’s location in a modified file. */ + context?: string; + /** The offset from the start of the context to the actual definition. Can be used to find a definition’s location in a modified file. */ + contextOffset?: number; + /** If the definition had documentation associated with it, these will also be returned. */ + doc?: string; + /** If the definition had urls associated with it, these will also be returned. */ + url?: string; + /** If the definition had origin information associated with it, these will also be returned. */ + origin?: string; +} + +/** Get the documentation string and URL for a given expression, if any. */ +export interface DocumentationQuery extends IQuery { + /** Get the documentation string and URL for a given expression, if any. */ + type: "documentation"; + /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ + file: string; + /** Specify the location of the expression. */ + end: number | Position; + /** Specify the location of the expression. */ + start?: number | Position; +} + +interface DocumentationQueryResult { + /** The documentation string of the definition or value, if any. */ + doc?: string; + /** The url of the definition or value, if any. */ + url?: string; + /** The origin of the definition or value, if any. */ + origin?: string; +} + +/** Used to find all references to a given variable or property. */ +export interface RefsQuery extends IQuery { + /** Used to find all references to a given variable or property. */ + type: "refs"; + /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ + file: string; + /** Specify the location of the expression. */ + end: number | Position; + /** Specify the location of the expression. */ + start?: number | Position; +} + +interface RefsQueryResult { + /** The name of the variable or property */ + name: string; + refs: { + file: string, + start: number | Position, + end: number | Position + }[]; + /** for variables: a type property holding either "global" or "local". */ + type?: "global" | "local"; +} + +/** Rename a variable in a scope-aware way. */ +export interface RenameQuery extends IQuery { + /** Rename a variable in a scope-aware way. */ + type: "rename"; + /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ + file: string; + /** Specify the location of the variable. */ + end: number | Position; + /** Specify the location of the variable. */ + start?: number | Position; + /** The new name of the variable */ + newName: string; +} + +/** + * Returns an object whose `changes` property holds an array of `{file, start, end, text}` objects, which + * give the changes that must be performed to apply the rename. The client is responsible for doing the actual modification. + */ +interface RenameQueryResult { + /** Array of changes that must be performed to apply the rename. The client is responsible for doing the actual modification. */ + changes: { + file: string, + start: number | Position, + end: number | Position, + text: string + }[]; +} + +/** Get a list of all known object property names (for any object). */ +export interface PropertiesQuery extends IQuery { + /** Get a list of all known object property names (for any object). */ + type: "properties"; + /** Causes the server to only return properties that start with the given string. */ + prefix?: string; + /** Whether the result should be sorted. Default `true` */ + sort?: boolean; +} + +interface PropertiesQueryResult { + /** The property names. */ + completions: string[]; +} + +/** Get the files that the server currently holds in its set of analyzed files. */ +export interface FilesQuery extends IQuery { + /** Get the files that the server currently holds in its set of analyzed files. */ + type: "files"; + docFormat?: never; + lineCharPositions?: never; +} + +interface FilesQueryResult { + /** The file names. */ + files: string[]; +} + +export interface Events { + /** When the server throws away its current analysis data and starts a fresh run. */ + reset(): void; + /** Before analyzing a file. file is an object holding {name, text, scope} properties. */ + beforeLoad(file: File): void; + /** After analyzing a file. */ + afterLoad(file: File): void; + /** + * Will be run right before a file is parsed, and passed the given text and options. If a handler + * returns a new text value, the origin text will be overriden. This is useful for + * instance when a plugin is able to extract JavaScript content from an HTML file. + */ + preParse(text: string, options: object): string | void; + /** Run right after a file is parsed, and passed the parse tree and the parsed file as arguments. */ + postParse(ast: ESTree.Program, text: string): void; + /** Run right before the type inference pass, passing the syntax tree and a scope object. */ + preInfer(ast: ESTree.Program, scope: Scope): void; + /** Run after the type inference pass. */ + postInfer(ast: ESTree.Program, scope: Scope): void; + /** + * Run after Tern attempts to find the type at the position end in the given file. + * A handler may return either the given type (already calculated by Tern and earlier "typeAt" passes) + * or an alternate type to be used instead. This is useful when + * a plugin can provide a more helpful type than Tern (e.g. within comments). + */ + typeAt(file: File, end: Position, expr: ESTree.Node, type: Type): Type | void; + /** Run at the start of a completion query. May return a valid completion result to replace the default completion algorithm. */ + completion(file: File, query: Query): CompletionsQueryResult | void; +} + +export const version: string; + +//###### Plugins ######## + +/** + * This can be used to register an initialization function for the plugin with the given name. + * A Tern server, when configured to load this plugin, will call this initialization function, + * passing in the server instance and the options specified for the plugin (if any). This is the + * place where you register event handlers on the server, add type definitions, load other + * plugins as dependencies, and/or initialize the plugin’s state. + * + * See the server’s [list of events](http://ternjs.net/doc/manual.html#events) for ways to wire up plugin behavior. + */ +export function registerPlugin(name: string, init: (server: Server, options?: ConstructorOptions) => void): void; + +interface Desc { + run(Server: Server, query: Extract, file?: File): void; + takesfile?: boolean; +} + +/** + * Defines a new type of query with the server. The `desc` object is a property describing the request. + * It should at least have a `run` property, which holds a function fn(Server, query) that will + * be called to handle queries with a type property that matches the given `name`. It may also have + * a `takesFile` property which, if true, will cause the server to try and resolve the file on which + * the query operates (from its file property) and pass that (a {name, text, scope, ast} object) as + * a third argument to the run function. You will probably need to use the inference + * module’s API to do someting useful in this function. + */ +export function defineQueryType(name: name, desc: Desc): void; diff --git a/types/tern/test/tern.test.ts b/types/tern/test/tern.test.ts new file mode 100644 index 0000000000..799cdc894d --- /dev/null +++ b/types/tern/test/tern.test.ts @@ -0,0 +1,33 @@ +import * as tern from "tern/lib/tern"; + +const server: tern.Server = null; + +server.request({ + query: { + type: "completions", + file: "", + end: 0 + } +}, (error, response) => { + if (response.isProperty) { + // + } +}); + + +server.request({ +}, (error, response) => { + if (response.isProperty) { + // + } +}); + + +server.request({ + query: undefined +}, (error, response) => { + if (response.isProperty) { + // + } +}); + diff --git a/types/tern/tsconfig.json b/types/tern/tsconfig.json new file mode 100644 index 0000000000..2a39303a8e --- /dev/null +++ b/types/tern/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": false, + "strictFunctionTypes": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "lib/tern/index.d.ts", + "lib/infer/index.d.ts", + "test/tern.test.ts" + ] +} \ No newline at end of file diff --git a/types/tern/tslint.json b/types/tern/tslint.json new file mode 100644 index 0000000000..e60c15844f --- /dev/null +++ b/types/tern/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "dtslint/dt.json" +} \ No newline at end of file From c860c5356286547a38580d4169d4af88e645a7ea Mon Sep 17 00:00:00 2001 From: Nikolaj Kappler Date: Tue, 23 Oct 2018 14:22:11 +0200 Subject: [PATCH 2/9] fixed duplicate definition of Type workstream: --- types/tern/lib/tern/index.d.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/types/tern/lib/tern/index.d.ts b/types/tern/lib/tern/index.d.ts index 00c014c1a4..a787d2ffd2 100644 --- a/types/tern/lib/tern/index.d.ts +++ b/types/tern/lib/tern/index.d.ts @@ -6,7 +6,7 @@ // Still WIP! some definitions may be incomplete or missing import * as ESTree from "estree"; -import { AVal, Scope } from "../infer"; +import { Scope, Type } from "../infer"; // #### Programming interface #### export interface ConstructorOptions { @@ -144,15 +144,6 @@ export interface IQuery { docFormat?: "full"; } -export interface Type { - name: string; - origin: string; - originNode: ESTree.Node; - toString(maxDepth: number): string; - getProp(prop: string): AVal; - forAllProps(f: (prop: string, val: AVal, local: boolean) => void): void; -} - interface Position { ch: number; line: number; From e4456fa069569c617df00a30cfb9ebb8b750ef97 Mon Sep 17 00:00:00 2001 From: Nikolaj Kappler Date: Wed, 24 Oct 2018 18:03:05 +0200 Subject: [PATCH 3/9] made constructorOptions more precise, made Query extensible it is necessary to be able to extend the type Query, in order to use the defineQueryType and request functions in a useful way. see tern.test.ts for an example. in the tern constructor options, the signature of getFile now depends on the async flag as it should. workstream: --- types/tern/lib/tern/index.d.ts | 127 ++++++++++++++++++++++----------- types/tern/test/tern.test.ts | 23 ++++++ 2 files changed, 110 insertions(+), 40 deletions(-) diff --git a/types/tern/lib/tern/index.d.ts b/types/tern/lib/tern/index.d.ts index a787d2ffd2..98e702dfc6 100644 --- a/types/tern/lib/tern/index.d.ts +++ b/types/tern/lib/tern/index.d.ts @@ -9,39 +9,45 @@ import * as ESTree from "estree"; import { Scope, Type } from "../infer"; // #### Programming interface #### -export interface ConstructorOptions { - /** Indicates whether `getFile` is asynchronous. Default is `false`. */ - async: boolean; +export type ConstructorOptions = CtorOptions & (SyncConstructorOptions | ASyncConstructorOptions); +interface CtorOptions { /** The definition objects to load into the server’s environment. */ - defs: Def[]; - + defs?: Def[]; /** The ECMAScript version to parse. Should be either 5 or 6. Default is 6. */ - ecmaVersion: 5 | 6; - + ecmaVersion?: 5 | 6; /** Indicates the maximum amount of milliseconds to wait for an asynchronous getFile before giving up on it. Defaults to 1000. */ - fetchTimeout: number; - + fetchTimeout?: number; /** Specifies the set of plugins that the server should load. The property names of the object name the plugins, and their values hold options that will be passed to them. */ - plugins: { [key: string]: object }; - - /** - * Provides a way for the server to try and fetch the content of files. - * Depending on the `async` option, this is either a function that takes a filename and returns a string (when not `async`), or - * a function that takes a `filename` and a `callback`, and calls the callback with an optional `error` as the first argument, - * and the `content` string (if no error) as the second. - */ - getFile(filename: string): string; - /** - * Provides a way for the server to try and fetch the content of files. - * Depending on the `async` option, this is either a function that takes a filename and returns a string (when not `async`), or - * a function that takes a `filename` and a `callback`, and calls the callback with an optional `error` as the first argument, - * and the `content` string (if no error) as the second. - */ - getFile(filename: string, callback: (error: Error | undefined, content?: string) => void): void; - + plugins?: { [key: string]: object }; } +interface SyncConstructorOptions { + /** Indicates whether `getFile` is asynchronous. Default is `false`. */ + async?: false; + /** + * Provides a way for the server to try and fetch the content of files. + * Depending on the `async` option, this is either a function that takes a filename and returns a string (when not `async`), or + * a function that takes a `filename` and a `callback`, and calls the callback with an optional `error` as the first argument, + * and the `content` string (if no error) as the second. + */ + getFile?(filename: string): string; +} + +interface ASyncConstructorOptions { + /** Indicates whether `getFile` is asynchronous. Default is `false`. */ + async: true; + /** + * Provides a way for the server to try and fetch the content of files. + * Depending on the `async` option, this is either a function that takes a filename and returns a string (when not `async`), or + * a function that takes a `filename` and a `callback`, and calls the callback with an optional `error` as the first argument, + * and the `content` string (if no error) as the second. + */ + getFile?(filename: string, callback: (error: Error | undefined, content?: string) => void): void; +} + + + interface TernConstructor { new(options?: ConstructorOptions): Server; } @@ -104,20 +110,43 @@ export interface Server { // #### JSON Protocol #### -type QueryResult = QueryResultMap[Q["type"]]; +type QueryResult = QueryMap[Q["type"]]["result"]; -export type Query = CompletionsQuery | TypeQuery | DefinitionQuery - | DocumentationQuery | RefsQuery | RenameQuery | PropertiesQuery | FilesQuery; +type Query = QueryMap[keyof QueryMap]["query"]; -interface QueryResultMap { - completions: CompletionsQueryResult; - type: TypeQueryResult; - definition: DefinitionQueryResult; - documentation: DocumentationQueryResult; - refs: RefsQueryResult; - rename: RenameQueryResult; - properties: PropertiesQueryResult; - files: FilesQueryResult; +export interface QueryMap { + completions: { + query: CompletionsQuery, + result: CompletionsQueryResult + }; + type: { + query: TypeQuery, + result: TypeQueryResult + }; + definition: { + query: DefinitionQuery, + result: DefinitionQueryResult + }; + documentation: { + query: DocumentationQuery; + result: DocumentationQueryResult; + }; + refs: { + query: RefsQuery; + result: RefsQueryResult; + }; + rename: { + query: RenameQuery, + result: RenameQueryResult + }; + properties: { + query: PropertiesQuery, + result: PropertiesQueryResult + }; + files: { + query: FilesQuery, + result: FilesQueryResult + }; } export interface Def { @@ -434,7 +463,7 @@ export const version: string; export function registerPlugin(name: string, init: (server: Server, options?: ConstructorOptions) => void): void; interface Desc { - run(Server: Server, query: Extract, file?: File): void; + run(Server: Server, query: QueryMap[T]["query"], file?: File): QueryMap[T]["result"]; takesfile?: boolean; } @@ -446,5 +475,23 @@ interface Desc { * the query operates (from its file property) and pass that (a {name, text, scope, ast} object) as * a third argument to the run function. You will probably need to use the inference * module’s API to do someting useful in this function. + * + * To be able to use this function and the `request` function in a useful way, you probably want + * to define an interface for the query and the result of the query and extend the interface `QueryMap` via + * [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) + * in the following manner: + * + * ```typescript + * declare module "tern/lib/tern" { + * interface QueryMap { + * [CustomQueryType]: { + * query: CustomQuery + * result: CustomQueryResult + * } + * } + * } + * ``` + * _Note that your query interface should extend_ `IQuery` _and that its_ `type` _property has to be spelled + * exactly like the key in the_ `QueryMap` _interface._ */ -export function defineQueryType(name: name, desc: Desc): void; +export function defineQueryType(name: T, desc: Desc): void; diff --git a/types/tern/test/tern.test.ts b/types/tern/test/tern.test.ts index 799cdc894d..42f113a64c 100644 --- a/types/tern/test/tern.test.ts +++ b/types/tern/test/tern.test.ts @@ -22,6 +22,29 @@ server.request({ } }); +declare module "tern/lib/tern" { + interface QueryMap { + someUnknownType: { + query: { + type: "someUnknownType" + }, + result: { + abc: boolean + } + }; + } +} + +server.request({ + query: { + type: "someUnknownType" + } +}, (error, response) => { + if (response.abc) { + // + } +}); + server.request({ query: undefined From 74ec6cab604158ce531a541f793736b83e21b129 Mon Sep 17 00:00:00 2001 From: Nikolaj Kappler Date: Thu, 25 Oct 2018 13:54:12 +0200 Subject: [PATCH 4/9] added doc comments to infer and fixed a few things. workstream: --- types/tern/lib/infer/index.d.ts | 169 ++++++++++++++++++++++++++------ 1 file changed, 137 insertions(+), 32 deletions(-) diff --git a/types/tern/lib/infer/index.d.ts b/types/tern/lib/infer/index.d.ts index 161a9a69cb..562fc9fa31 100644 --- a/types/tern/lib/infer/index.d.ts +++ b/types/tern/lib/infer/index.d.ts @@ -7,97 +7,172 @@ interface ContextConstructor { export const Context: ContextConstructor; export interface Context { topScope: Scope; + /** The primitive number type. */ + num: object; + /** The primitive string type. */ + str: object; + /** The primitive boolean type. */ + bool: object; } export function cx(): Context; export function withContext(context: Context, f: () => void): void; // #### Analysis #### +/** Parse a piece of code for use by Tern. Will automatically fall back to the error-tolerant parser if the regular parser can’t parse the code. */ export function parse(text: string, options?: {}): ESTree.Program; +/** + * Analyze a syntax tree. `name` will be used to set the origin of types, properties, and variables produced by this code. + * The optional `scope` argument can be used to specify a scope in which the code should be analyzed. + * It will default to the top-level scope. + */ export function analyze(ast: ESTree.Program, name: string, scope?: Scope): void; +/** + * Purges the types that have one of the origins given from the context. `start` and `end` can be given to only purge + * types that occurred in the source code between those offsets. This is not entirely precise — the state of the + * context won’t be back where it was before the file was analyzed — but it prevents most of the + * noticeable inaccuracies that re-analysis tends to produce. + */ export function purgeTypes(origins: string[], start?: number, end?: number): void; +/** + * Cleaning up variables is slightly trickier than cleaning up types. This does a first pass over the given scope, + * and marks variables defined by the given origins. This is indended to be followed by a call to `analyze` and then a call to `purgeMarkedVariables`. + */ export function markVariablesDefinedBy(scope: Scope, origins: string[], start?: number, end?: number): void; +/** Purges variables that were marked by a call to markVariablesDefinedBy and not re-defined in the meantime. */ export function purgeMarkedVariables(): void; // #### Types #### interface ObjConstructor { new(proto: object | true | null, name?: string): Obj; } +/** Constructor for the type that represents JavaScript objects. `proto` may be another object, or `true` as a short-hand for `Object.prototype`, or `null` for prototype-less objects. */ export const Obj: ObjConstructor; -export interface Obj { +export interface Obj extends Type { + /** The prototype of the object, or null. */ proto: any; + /** An object mapping the object’s known properties to AVals. Don’t manipulate this directly (ever), only use it if you have to iterate over the properties. */ props: Readonly<{ [key: string]: AVal; }>; + /** Looks up the AVal associated with the given property, or returns null if it doesn’t exist. */ hasProp(prop: string): AVal | null; + /** Looks up the given property, or defines it if it did not yet exist (in which case it will be associated with the given AST node). */ defProp(prop: string, originNode?: ESTree.Node): AVal; } interface FnConstructor { new(name: string | undefined, self: AVal, args: AVal[], argNames: string[], retval: AVal): Fn; } +/** Constructor for the type that implements functions. Inherits from `Obj`. The `AVal` types are used to track the input and output types of the function. */ export const Fn: FnConstructor; export interface Fn extends Obj { } interface ArrConstructor { + /** Constructor that creates an array type with the given content type. */ new(contentType: AVal): Arr; } export const Arr: ArrConstructor; export interface Arr extends Obj { } -export interface Type { +export interface Type extends AVal { + /** The name of the type, if any. */ name: string; + /** The origin file of the type. */ origin: string; - originNode: ESTree.Node; + /** The syntax node that defined the type. Only present for object and function types, + * and even for those it may be missing (if the type was created by a type definition file, + * or synthesized in some other way). + */ + originNode?: ESTree.Node | undefined; + /** Return a string that describes the type. maxDepth indicates the depth to which inner types should be shown. */ toString(maxDepth: number): string; + /** Get an `AVal` that represents the named property of this type. */ getProp(prop: string): AVal; + /** Call the given function for all properties of the object, including properties that are added in the future. */ forAllProps(f: (prop: string, val: AVal, local: boolean) => void): void; } - - // #### Abstract Values #### -export const ANull: ANull; -export interface ANull { - addType(type?: never, weight?: never): void; - propagate(target: never): void; - getProp(): ANull; - forAllProps(): void; - hasType(type?: never): boolean; //always false - isEmpty(): boolean; //always true - getFunctionType(): void; - getObjType(): void; - getSymbolType(): void; - getType(guess?: never): void; - gatherProperties(): void; - propagatesTo(): void; - typeHint(): void; - propHint(): void; - toString(): "?"; -} interface AValConstructor { new(): AVal; } export const AVal: AValConstructor; -export interface AVal extends ANull { +export interface AVal { + /** + * Add a type to this abstract value. If the type is already in there, + * this is a no-op. weight can be given to give this type a non-default + * weight, which is mostly useful when adding a provisionary type that + * should be overridden later if a real type is found. The default weight + * is 100, and passing a weight lower than that will make the type + * assignment “weak”. + */ addType(type: Type, weight?: number): void; + /** + * Sets this AVal to propagate all types it receives to the given + * constraint. This is the mechanism by which types are propagated + * through the type graph. + */ propagate(target: Constraint): void; + /** Queries whether the AVal _currently_ holds the given type. */ hasType(type: Type): boolean; + /** Queries whether the AVal is empty. */ isEmpty(): boolean; - getType(guess?: boolean): Type | undefined; + /** + * Asks the abstract value for its current type. May return `null` + * when there is no type, or conflicting types are present. When + * `guess` is true or not given, an empty AVal will try to use + * heuristics based on its propagation edges to guess a type. + */ + getType(guess?: boolean): Type | null; + /** + * Asks the AVal if it contains a function type. Useful when + * you aren’t interested in other kinds of types. + */ getFunctionType(): Type | undefined; - originNode?: ESTree.Node; + /** + * Abstract values that are used to represent variables + * or properties will have, when possible, an `originNode` + * property pointing to an AST node. + */ + originNode?: ESTree.Node | undefined; +} + +export const ANull: ANull; +export interface ANull extends AVal { + addType(): void; + propagate(target: never): void; + // getProp(): ANull; + // forAllProps(): void; + hasType(): false; + isEmpty(): boolean; //always true + getFunctionType(): undefined; + // getObjType(): void; + // getSymbolType(): void; + getType(): null; + // gatherProperties(): void; + // propagatesTo(): void; + // typeHint(): void; + // propHint(): void; + // toString(): "?"; + originNode: undefined; } // #### Constraints #### interface ConstraintConstructor { - new(methods: object): { new(): any }; + new(methods: { [key: string]: Function }): { new(): Constraint }; } +/** + * This is a constructor-constructor for constraints. It’ll create a + * constructor with all the given methods copied into its prototype, + * which will run its construct method on its arguments when instantiated. + */ export const constraint: ConstraintConstructor; -export interface Constraint { - addType: AVal["addType"]; - typeHint(): Type | undefined; - propHint(): string | undefined; +export interface Constraint extends AVal { + /** May return a type that `getType` can use to “guess” its type based on the fact that it propagates to this constraint. */ + typeHint?(): Type | undefined; + /** May return a string when this constraint is indicative of the presence of a specific property in the source AVal. */ + propHint?(): string | undefined; } // #### Scopes #### @@ -105,17 +180,47 @@ interface ScopeConstructor { new(parent?: Scope): Scope; } export const Scope: ScopeConstructor; -export interface Scope { +export interface Scope extends Obj { + /** + * Ensures that this scope or some scope above it has a property by the given name + * (defining it in the top scope if it is missing), and, if the property doesn’t + * already have an `originNode`, assigns the given node to it. + */ defVar(name: string, originNode: ESTree.Node): AVal; } // #### Utilities #### +/** + * Searches the given syntax tree for an expression that ends at the given `end` offset and, + * if `start` is given, starts at the given start offset. `scope` can be given to override the + * outer scope, which defaults to the context’s top scope. Will return a `{node, state}` + * object if successful, where `node` is AST node, and `state` is the scope at that point. + * Returns `null` if unsuccessful. + */ export function findExpressionAt(ast: ESTree.Program, start: number | undefined, end: number, scope?: Scope): { node: ESTree.Node, state: Scope } | null; +/** + * Similar to `findExpressionAt`, except that it will return the innermost expression + * node that spans the given range, rather than only exact matches. + */ export function findExpressionAround(ast: ESTree.Program, start: number | undefined, end: number, scope?: Scope): { node: ESTree.Node, state: Scope } | null; +/** Similar to `findExpressionAround`, except that it use the same AST walker as `findExpressionAt`. */ +export function findClosestExpression(ast: ESTree.Program, start: number | undefined, end: number, scope?: Scope): { node: ESTree.Node, state: Scope } | null; +/** Determine an expression for the given node and scope (as returned by the functions above). Will return an `AVal` or plain `Type`. */ export function expressionType(expr: { node: ESTree.Node, state: Scope }): AVal | Type; +/** Find the scope at a given position in the syntax tree. The `scope` parameter can be used to override the scope used for code that isn’t wrapped in any function. */ export function scopeAt(ast: ESTree.Program, pos: number, scope?: Scope): Scope; +/** + * Will traverse the given syntax tree, using `scope` as the starting scope, looking for references to variable `name` that + * resolve to scope `refScope`, and call `f` with the node of the reference and its local scope for each of them. + */ export function findRefs(ast: ESTree.Program, scope: Scope, name: string, refScope: Scope, f: (Node: ESTree.Node, Scope: Scope) => void): void; +/** + * Analogous to `findRefs`, but used to look for references to a specific property instead. Whereas `findRefs` + * is precise, this is dependent on type inference, and thus can not be relied on to be precise. + */ export function findPropRefs(ast: ESTree.Program, scope: Scope, objType: Obj, propName: string, f: (Node: ESTree.Node) => void): void; +/** Whenever infer guesses a type through fuzzy heuristics (through `getType` or `expressionType`), it sets a flag. `didGuess` tests whether the guessing flag is set. */ export function didGuess(): boolean; +/** Whenever infer guesses a type through fuzzy heuristics (through `getType` or `expressionType`), it sets a flag. `resetGuessing` resets the guessing flag. */ export function resetGuessing(val?: boolean): void; From 961d7e1dcfbe3ff3cadcb935f20c776292b38478 Mon Sep 17 00:00:00 2001 From: Nikolaj Kappler Date: Mon, 29 Oct 2018 10:55:27 +0100 Subject: [PATCH 5/9] comments and fixes workstream: --- types/tern/lib/infer/index.d.ts | 11 ++++++++--- types/tern/lib/tern/index.d.ts | 23 ++++++++++++++--------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/types/tern/lib/infer/index.d.ts b/types/tern/lib/infer/index.d.ts index 562fc9fa31..af953d9832 100644 --- a/types/tern/lib/infer/index.d.ts +++ b/types/tern/lib/infer/index.d.ts @@ -1,3 +1,8 @@ +// Type definitions for tern +// Project: https://github.com/ternjs/tern +// Definitions by: Nikolaj Kappler +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + import * as ESTree from "estree"; // #### Context #### @@ -8,11 +13,11 @@ export const Context: ContextConstructor; export interface Context { topScope: Scope; /** The primitive number type. */ - num: object; + num: Type; /** The primitive string type. */ - str: object; + str: Type; /** The primitive boolean type. */ - bool: object; + bool: Type; } export function cx(): Context; export function withContext(context: Context, f: () => void): void; diff --git a/types/tern/lib/tern/index.d.ts b/types/tern/lib/tern/index.d.ts index 98e702dfc6..15073c4383 100644 --- a/types/tern/lib/tern/index.d.ts +++ b/types/tern/lib/tern/index.d.ts @@ -1,9 +1,14 @@ -// Type definitions for tern 1.3 0.22 +// Type definitions for tern // Project: https://github.com/ternjs/tern // Definitions by: Nikolaj Kappler // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// Still WIP! some definitions may be incomplete or missing +// IMPORTANT Note: These type definitions are oriented closely to the official documentation, +// which does not seem to match the implementation exactly in some places. +// As a result, these type definitions may lack some parts of the API that are actually exposed. +// The type definitions are at a point where they are usable and match the documentation, +// thus if you want to use undocumented APIs, you should extends these definitions with +// ambient declaration merging: https://www.typescriptlang.org/docs/handbook/declaration-merging.html import * as ESTree from "estree"; import { Scope, Type } from "../infer"; @@ -110,11 +115,11 @@ export interface Server { // #### JSON Protocol #### -type QueryResult = QueryMap[Q["type"]]["result"]; +type QueryResult = QueryRegistry[Q["type"]]["result"]; -type Query = QueryMap[keyof QueryMap]["query"]; +type Query = QueryRegistry[keyof QueryRegistry]["query"]; -export interface QueryMap { +export interface QueryRegistry { completions: { query: CompletionsQuery, result: CompletionsQueryResult @@ -463,7 +468,7 @@ export const version: string; export function registerPlugin(name: string, init: (server: Server, options?: ConstructorOptions) => void): void; interface Desc { - run(Server: Server, query: QueryMap[T]["query"], file?: File): QueryMap[T]["result"]; + run(Server: Server, query: QueryRegistry[T]["query"], file?: File): QueryRegistry[T]["result"]; takesfile?: boolean; } @@ -477,13 +482,13 @@ interface Desc { * module’s API to do someting useful in this function. * * To be able to use this function and the `request` function in a useful way, you probably want - * to define an interface for the query and the result of the query and extend the interface `QueryMap` via + * to define an interface for the query and the result of the query and extend the interface `QueryRegistry` via * [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) * in the following manner: * * ```typescript * declare module "tern/lib/tern" { - * interface QueryMap { + * interface QueryRegistry { * [CustomQueryType]: { * query: CustomQuery * result: CustomQueryResult @@ -492,6 +497,6 @@ interface Desc { * } * ``` * _Note that your query interface should extend_ `IQuery` _and that its_ `type` _property has to be spelled - * exactly like the key in the_ `QueryMap` _interface._ + * exactly like the key in the_ `QueryRegistry` _interface._ */ export function defineQueryType(name: T, desc: Desc): void; From 2539f72c36c220148c8fca8e8320a2577e0cc7af Mon Sep 17 00:00:00 2001 From: Nikolaj Kappler Date: Mon, 29 Oct 2018 11:09:44 +0100 Subject: [PATCH 6/9] cleanup, fixes workstream: --- types/tern/index.d.ts | 11 +++++++++++ types/tern/lib/infer/index.d.ts | 1 + types/tern/lib/tern/index.d.ts | 1 + types/tern/test/tern.test.ts | 2 +- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/types/tern/index.d.ts b/types/tern/index.d.ts index e69de29bb2..a2b0a0ded7 100644 --- a/types/tern/index.d.ts +++ b/types/tern/index.d.ts @@ -0,0 +1,11 @@ +// Type definitions for tern +// Project: https://github.com/ternjs/tern +// Definitions by: Nikolaj Kappler +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module "tern" { + import * as infer from "tern/lib/infer"; + import * as tern from "tern/lib/tern"; + const t: typeof tern & typeof infer; + export = t; +} diff --git a/types/tern/lib/infer/index.d.ts b/types/tern/lib/infer/index.d.ts index af953d9832..32d8bfb241 100644 --- a/types/tern/lib/infer/index.d.ts +++ b/types/tern/lib/infer/index.d.ts @@ -2,6 +2,7 @@ // Project: https://github.com/ternjs/tern // Definitions by: Nikolaj Kappler // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.8 import * as ESTree from "estree"; diff --git a/types/tern/lib/tern/index.d.ts b/types/tern/lib/tern/index.d.ts index 15073c4383..a4cee5e0e4 100644 --- a/types/tern/lib/tern/index.d.ts +++ b/types/tern/lib/tern/index.d.ts @@ -2,6 +2,7 @@ // Project: https://github.com/ternjs/tern // Definitions by: Nikolaj Kappler // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.8 // IMPORTANT Note: These type definitions are oriented closely to the official documentation, // which does not seem to match the implementation exactly in some places. diff --git a/types/tern/test/tern.test.ts b/types/tern/test/tern.test.ts index 42f113a64c..dadb195e73 100644 --- a/types/tern/test/tern.test.ts +++ b/types/tern/test/tern.test.ts @@ -23,7 +23,7 @@ server.request({ }); declare module "tern/lib/tern" { - interface QueryMap { + interface QueryRegistry { someUnknownType: { query: { type: "someUnknownType" From 95f368fb9af4079daed98f0fa85f6ac39e0022ee Mon Sep 17 00:00:00 2001 From: Nikolaj Kappler Date: Mon, 29 Oct 2018 12:51:28 +0100 Subject: [PATCH 7/9] lint fixes workstream: --- types/tern/index.d.ts | 18 +++++---- types/tern/lib/infer/index.d.ts | 32 +++++---------- types/tern/lib/tern/index.d.ts | 71 ++++++++++++++++----------------- types/tern/test/tern.test.ts | 7 ++-- 4 files changed, 57 insertions(+), 71 deletions(-) diff --git a/types/tern/index.d.ts b/types/tern/index.d.ts index a2b0a0ded7..5b46fc3dd7 100644 --- a/types/tern/index.d.ts +++ b/types/tern/index.d.ts @@ -1,11 +1,15 @@ -// Type definitions for tern +// Type definitions for tern 1.3 0.22 // Project: https://github.com/ternjs/tern // Definitions by: Nikolaj Kappler // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.8 -declare module "tern" { - import * as infer from "tern/lib/infer"; - import * as tern from "tern/lib/tern"; - const t: typeof tern & typeof infer; - export = t; -} +// IMPORTANT Note: These type definitions are oriented closely to the official documentation, +// which does not seem to match the implementation exactly in some places. +// As a result, these type definitions may lack some parts of the API that are actually exposed. +// The type definitions are at a point where they are usable and match the documentation, +// thus if you want to use undocumented APIs, you should extends these definitions with +// ambient declaration merging: https://www.typescriptlang.org/docs/handbook/declaration-merging.html + +export * from "./lib/tern"; +export * from "./lib/infer"; diff --git a/types/tern/lib/infer/index.d.ts b/types/tern/lib/infer/index.d.ts index 32d8bfb241..58e83ea1c7 100644 --- a/types/tern/lib/infer/index.d.ts +++ b/types/tern/lib/infer/index.d.ts @@ -1,10 +1,5 @@ -// Type definitions for tern -// Project: https://github.com/ternjs/tern -// Definitions by: Nikolaj Kappler -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.8 - import * as ESTree from "estree"; +export { }; // #### Context #### interface ContextConstructor { @@ -71,25 +66,26 @@ interface FnConstructor { } /** Constructor for the type that implements functions. Inherits from `Obj`. The `AVal` types are used to track the input and output types of the function. */ export const Fn: FnConstructor; -export interface Fn extends Obj { } +export type Fn = Obj; interface ArrConstructor { /** Constructor that creates an array type with the given content type. */ new(contentType: AVal): Arr; } export const Arr: ArrConstructor; -export interface Arr extends Obj { } +export type Arr = Obj; export interface Type extends AVal { /** The name of the type, if any. */ name: string; /** The origin file of the type. */ origin: string; - /** The syntax node that defined the type. Only present for object and function types, + /** + * The syntax node that defined the type. Only present for object and function types, * and even for those it may be missing (if the type was created by a type definition file, * or synthesized in some other way). */ - originNode?: ESTree.Node | undefined; + originNode?: ESTree.Node; /** Return a string that describes the type. maxDepth indicates the depth to which inner types should be shown. */ toString(maxDepth: number): string; /** Get an `AVal` that represents the named property of this type. */ @@ -141,32 +137,23 @@ export interface AVal { * or properties will have, when possible, an `originNode` * property pointing to an AST node. */ - originNode?: ESTree.Node | undefined; + originNode?: ESTree.Node; } export const ANull: ANull; export interface ANull extends AVal { addType(): void; propagate(target: never): void; - // getProp(): ANull; - // forAllProps(): void; hasType(): false; - isEmpty(): boolean; //always true + isEmpty(): true; getFunctionType(): undefined; - // getObjType(): void; - // getSymbolType(): void; getType(): null; - // gatherProperties(): void; - // propagatesTo(): void; - // typeHint(): void; - // propHint(): void; - // toString(): "?"; originNode: undefined; } // #### Constraints #### interface ConstraintConstructor { - new(methods: { [key: string]: Function }): { new(): Constraint }; + new(methods: { [key: string]: any }): { new(): Constraint }; } /** * This is a constructor-constructor for constraints. It’ll create a @@ -229,4 +216,3 @@ export function findPropRefs(ast: ESTree.Program, scope: Scope, objType: Obj, pr export function didGuess(): boolean; /** Whenever infer guesses a type through fuzzy heuristics (through `getType` or `expressionType`), it sets a flag. `resetGuessing` resets the guessing flag. */ export function resetGuessing(val?: boolean): void; - diff --git a/types/tern/lib/tern/index.d.ts b/types/tern/lib/tern/index.d.ts index a4cee5e0e4..ee51a79119 100644 --- a/types/tern/lib/tern/index.d.ts +++ b/types/tern/lib/tern/index.d.ts @@ -1,19 +1,8 @@ -// Type definitions for tern -// Project: https://github.com/ternjs/tern -// Definitions by: Nikolaj Kappler -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.8 - -// IMPORTANT Note: These type definitions are oriented closely to the official documentation, -// which does not seem to match the implementation exactly in some places. -// As a result, these type definitions may lack some parts of the API that are actually exposed. -// The type definitions are at a point where they are usable and match the documentation, -// thus if you want to use undocumented APIs, you should extends these definitions with -// ambient declaration merging: https://www.typescriptlang.org/docs/handbook/declaration-merging.html - import * as ESTree from "estree"; import { Scope, Type } from "../infer"; +export { }; + // #### Programming interface #### export type ConstructorOptions = CtorOptions & (SyncConstructorOptions | ASyncConstructorOptions); @@ -52,8 +41,6 @@ interface ASyncConstructorOptions { getFile?(filename: string, callback: (error: Error | undefined, content?: string) => void): void; } - - interface TernConstructor { new(options?: ConstructorOptions): Server; } @@ -61,7 +48,6 @@ interface TernConstructor { export const Server: TernConstructor; export interface Server { - /** * Add a set of type definitions to the server. If `atFront` is true, they will be added before all other * existing definitions. Otherwise, they are added at the back. @@ -111,7 +97,6 @@ export interface Server { response: (D extends { query: undefined } ? {} : D extends { query: Query } ? QueryResult : {}) | undefined ) => void ): void; - } // #### JSON Protocol #### @@ -173,7 +158,7 @@ export interface File { type?: "full" | "part" | "delete"; } -export interface IQuery { +export interface BaseQuery { type: string; lineCharPositions?: boolean; docFormat?: "full"; @@ -185,7 +170,7 @@ interface Position { } /** Asks the server for a set of completions at the given point. */ -export interface CompletionsQuery extends IQuery { +export interface CompletionsQuery extends BaseQuery { /** Asks the server for a set of completions at the given point. */ type: "completions"; /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ @@ -210,7 +195,10 @@ export interface CompletionsQuery extends IQuery { guess?: boolean; /** Determines whether the result set will be sorted. Default `true` */ sort?: boolean; - /** When disabled, only the text before the given position is considered part of the word. When enabled (the default), the whole variable name that the cursor is on will be included. Default `true` */ + /** + * When disabled, only the text before the given position is considered part of the word. When enabled (the default), + * the whole variable name that the cursor is on will be included. Default `true` + */ expandWordForward?: boolean; /** Whether to ignore the properties of `Object.prototype` unless they have been spelled out by at least two characters. Default `true` */ omitObjectPrototype?: boolean; @@ -234,18 +222,18 @@ interface CompletionsQueryResult { * and, depending on the options, `type`, `depth`, `doc`, `url`, and `origin` properties. * When none of these options are enabled, the result array will hold plain strings. */ - completions: string[] | { + completions: string[] | Array<{ name: string, type?: string, depth?: number, doc?: string, url?: string, origin?: string - }[]; + }>; } /** Query the type of something. */ -export interface TypeQuery extends IQuery { +export interface TypeQuery extends BaseQuery { /** Query the type of something. */ type: "type"; /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ @@ -254,9 +242,18 @@ export interface TypeQuery extends IQuery { end: number | Position; /** Specify the location of the expression. */ start?: number | Position; - /** Set to `true` when you are interested in a function type. This will cause function types to win when something has multiple types. Default `false` */ + /** + * Set to `true` when you are interested in a function type. + * This will cause function types to win when something has multiple types. + * Default `false` + */ preferFunction?: boolean; - /** Determines how deep the type string must be expanded. Nested objects will only display property types up to this depth, and be represented by their type name or a representation showing only property names below it. Default `0` */ + /** + * Determines how deep the type string must be expanded. + * Nested objects will only display property types up to this depth, + * and be represented by their type name or a representation showing + * only property names below it. Default `0` + */ depth?: number; } @@ -285,7 +282,7 @@ interface TypeQueryResult { * type is not an object or function (other types don’t store their definition site), * it will fail to return useful information. */ -export interface DefinitionQuery extends IQuery { +export interface DefinitionQuery extends BaseQuery { /** * Asks for the definition of something. This will try, for a variable or property, * to return the point at which it was defined. If that fails, or the chosen @@ -323,7 +320,7 @@ interface DefinitionQueryResult { } /** Get the documentation string and URL for a given expression, if any. */ -export interface DocumentationQuery extends IQuery { +export interface DocumentationQuery extends BaseQuery { /** Get the documentation string and URL for a given expression, if any. */ type: "documentation"; /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ @@ -344,7 +341,7 @@ interface DocumentationQueryResult { } /** Used to find all references to a given variable or property. */ -export interface RefsQuery extends IQuery { +export interface RefsQuery extends BaseQuery { /** Used to find all references to a given variable or property. */ type: "refs"; /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ @@ -358,17 +355,17 @@ export interface RefsQuery extends IQuery { interface RefsQueryResult { /** The name of the variable or property */ name: string; - refs: { + refs: Array<{ file: string, start: number | Position, end: number | Position - }[]; + }>; /** for variables: a type property holding either "global" or "local". */ type?: "global" | "local"; } /** Rename a variable in a scope-aware way. */ -export interface RenameQuery extends IQuery { +export interface RenameQuery extends BaseQuery { /** Rename a variable in a scope-aware way. */ type: "rename"; /** may hold either a filename, or a string in the form "#N", where N should be an integer referring to one of the files included in the request */ @@ -387,16 +384,16 @@ export interface RenameQuery extends IQuery { */ interface RenameQueryResult { /** Array of changes that must be performed to apply the rename. The client is responsible for doing the actual modification. */ - changes: { + changes: Array<{ file: string, start: number | Position, end: number | Position, text: string - }[]; + }>; } /** Get a list of all known object property names (for any object). */ -export interface PropertiesQuery extends IQuery { +export interface PropertiesQuery extends BaseQuery { /** Get a list of all known object property names (for any object). */ type: "properties"; /** Causes the server to only return properties that start with the given string. */ @@ -411,7 +408,7 @@ interface PropertiesQueryResult { } /** Get the files that the server currently holds in its set of analyzed files. */ -export interface FilesQuery extends IQuery { +export interface FilesQuery extends BaseQuery { /** Get the files that the server currently holds in its set of analyzed files. */ type: "files"; docFormat?: never; @@ -455,7 +452,7 @@ export interface Events { export const version: string; -//###### Plugins ######## +// ###### Plugins ######## /** * This can be used to register an initialization function for the plugin with the given name. @@ -497,7 +494,7 @@ interface Desc { * } * } * ``` - * _Note that your query interface should extend_ `IQuery` _and that its_ `type` _property has to be spelled + * _Note that your query interface should extend_ `BaseQuery` _and that its_ `type` _property has to be spelled * exactly like the key in the_ `QueryRegistry` _interface._ */ export function defineQueryType(name: T, desc: Desc): void; diff --git a/types/tern/test/tern.test.ts b/types/tern/test/tern.test.ts index dadb195e73..0e641ac33a 100644 --- a/types/tern/test/tern.test.ts +++ b/types/tern/test/tern.test.ts @@ -1,4 +1,4 @@ -import * as tern from "tern/lib/tern"; +import * as tern from "tern"; const server: tern.Server = null; @@ -14,9 +14,9 @@ server.request({ } }); - server.request({ }, (error, response) => { + // $ExpectError if (response.isProperty) { // } @@ -45,12 +45,11 @@ server.request({ } }); - server.request({ query: undefined }, (error, response) => { + // $ExpectError if (response.isProperty) { // } }); - From 7e12876f17d4a2b75c90fefc208fa6b7f0f1ed3e Mon Sep 17 00:00:00 2001 From: Nikolaj Kappler Date: Mon, 29 Oct 2018 13:17:00 +0100 Subject: [PATCH 8/9] fix strictNullChecks workstream: --- types/tern/test/tern.test.ts | 10 +++++----- types/tern/tsconfig.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/types/tern/test/tern.test.ts b/types/tern/test/tern.test.ts index 0e641ac33a..87cfee1ee6 100644 --- a/types/tern/test/tern.test.ts +++ b/types/tern/test/tern.test.ts @@ -1,6 +1,6 @@ import * as tern from "tern"; -const server: tern.Server = null; +const server: tern.Server = null as any; server.request({ query: { @@ -9,7 +9,7 @@ server.request({ end: 0 } }, (error, response) => { - if (response.isProperty) { + if (response && response.isProperty) { // } }); @@ -17,7 +17,7 @@ server.request({ server.request({ }, (error, response) => { // $ExpectError - if (response.isProperty) { + if (response && response.isProperty) { // } }); @@ -40,7 +40,7 @@ server.request({ type: "someUnknownType" } }, (error, response) => { - if (response.abc) { + if (response && response.abc) { // } }); @@ -49,7 +49,7 @@ server.request({ query: undefined }, (error, response) => { // $ExpectError - if (response.isProperty) { + if (response && response.isProperty) { // } }); diff --git a/types/tern/tsconfig.json b/types/tern/tsconfig.json index 2a39303a8e..24e87522b3 100644 --- a/types/tern/tsconfig.json +++ b/types/tern/tsconfig.json @@ -6,7 +6,7 @@ ], "noImplicitAny": true, "noImplicitThis": true, - "strictNullChecks": false, + "strictNullChecks": true, "strictFunctionTypes": true, "baseUrl": "../", "typeRoots": [ From f78ced30cece0918da9665d03bba7ae1e564aa24 Mon Sep 17 00:00:00 2001 From: Nikolaj Kappler Date: Wed, 31 Oct 2018 10:29:33 +0100 Subject: [PATCH 9/9] fix version workstream: --- types/tern/index.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/tern/index.d.ts b/types/tern/index.d.ts index 5b46fc3dd7..ef01c0e289 100644 --- a/types/tern/index.d.ts +++ b/types/tern/index.d.ts @@ -1,10 +1,10 @@ -// Type definitions for tern 1.3 0.22 +// Type definitions for tern 0.22 // Project: https://github.com/ternjs/tern // Definitions by: Nikolaj Kappler // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.8 -// IMPORTANT Note: These type definitions are oriented closely to the official documentation, +// IMPORTANT Note: These type definitions adhere strictly to the official documentation, // which does not seem to match the implementation exactly in some places. // As a result, these type definitions may lack some parts of the API that are actually exposed. // The type definitions are at a point where they are usable and match the documentation,