feat(web): document the API
This commit is contained in:
parent
a4b20c1c56
commit
09cb4c5729
17 changed files with 1155 additions and 198 deletions
|
|
@ -2,7 +2,7 @@ import { C, INTERNAL, Internal, assertInternal, SIZE_OF_INT, SIZE_OF_SHORT } fro
|
|||
import { LookaheadIterator } from './lookahead_iterator';
|
||||
import { Node } from './node';
|
||||
import { TRANSFER_BUFFER } from './parser';
|
||||
import { CaptureQuantifier, Predicate, PredicateStep, Properties, Query, TextPredicate } from './query';
|
||||
import { CaptureQuantifier, QueryPredicate, PredicateStep, QueryProperties, Query, TextPredicate } from './query';
|
||||
|
||||
const PREDICATE_STEP_TYPE_CAPTURE = 1;
|
||||
const PREDICATE_STEP_TYPE_STRING = 2;
|
||||
|
|
@ -10,14 +10,27 @@ const PREDICATE_STEP_TYPE_STRING = 2;
|
|||
const QUERY_WORD_REGEX = /[\w-]+/g;
|
||||
const LANGUAGE_FUNCTION_REGEX = /^tree_sitter_\w+$/;
|
||||
|
||||
/**
|
||||
* An opaque object that defines how to parse a particular language.
|
||||
* The code for each `Language` is generated by the Tree-sitter CLI.
|
||||
*/
|
||||
export class Language {
|
||||
/** @internal */
|
||||
private [0] = 0; // Internal handle for WASM
|
||||
|
||||
/**
|
||||
* A list of all node types in the language. The index of each type in this
|
||||
* array is its node type id.
|
||||
*/
|
||||
types: string[];
|
||||
|
||||
/**
|
||||
* A list of all field names in the language. The index of each field name in
|
||||
* this array is its field id.
|
||||
*/
|
||||
fields: (string | null)[];
|
||||
|
||||
/** @internal */
|
||||
constructor(internal: Internal, address: number) {
|
||||
assertInternal(internal);
|
||||
this[0] = address;
|
||||
|
|
@ -38,33 +51,54 @@ export class Language {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the name of the language.
|
||||
*/
|
||||
get name(): string | null {
|
||||
const ptr = C._ts_language_name(this[0]);
|
||||
if (ptr === 0) return null;
|
||||
return C.UTF8ToString(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the version of the language.
|
||||
*/
|
||||
get version(): number {
|
||||
return C._ts_language_version(this[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the language.
|
||||
*/
|
||||
get fieldCount(): number {
|
||||
return this.fields.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of states in the language.
|
||||
*/
|
||||
get stateCount(): number {
|
||||
return C._ts_language_state_count(this[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field id for a field name.
|
||||
*/
|
||||
fieldIdForName(fieldName: string): number | null {
|
||||
const result = this.fields.indexOf(fieldName);
|
||||
return result !== -1 ? result : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field name for a field id.
|
||||
*/
|
||||
fieldNameForId(fieldId: number): string | null {
|
||||
return this.fields[fieldId] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node type id for a node type name.
|
||||
*/
|
||||
idForNodeType(type: string, named: boolean): number | null {
|
||||
const typeLength = C.lengthBytesUTF8(type);
|
||||
const typeAddress = C._malloc(typeLength + 1);
|
||||
|
|
@ -74,23 +108,42 @@ export class Language {
|
|||
return result || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of node types in the language.
|
||||
*/
|
||||
get nodeTypeCount(): number {
|
||||
return C._ts_language_symbol_count(this[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node type name for a node type id.
|
||||
*/
|
||||
nodeTypeForId(typeId: number): string | null {
|
||||
const name = C._ts_language_symbol_name(this[0], typeId);
|
||||
return name ? C.UTF8ToString(name) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node type is named.
|
||||
*
|
||||
* @see {@link https://tree-sitter.github.io/tree-sitter/using-parsers/2-basic-parsing.html#named-vs-anonymous-nodes}
|
||||
*/
|
||||
nodeTypeIsNamed(typeId: number): boolean {
|
||||
return C._ts_language_type_is_named_wasm(this[0], typeId) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node type is visible.
|
||||
*/
|
||||
nodeTypeIsVisible(typeId: number): boolean {
|
||||
return C._ts_language_type_is_visible_wasm(this[0], typeId) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the supertypes ids of this language.
|
||||
*
|
||||
* @see {@link https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types.html?highlight=supertype#supertype-nodes}
|
||||
*/
|
||||
get supertypes(): number[] {
|
||||
C._ts_language_supertypes_wasm(this[0]);
|
||||
const count = C.getValue(TRANSFER_BUFFER, 'i32');
|
||||
|
|
@ -108,6 +161,9 @@ export class Language {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subtype ids for a given supertype node id.
|
||||
*/
|
||||
subtypes(supertype: number): number[] {
|
||||
C._ts_language_subtypes_wasm(this[0], supertype);
|
||||
const count = C.getValue(TRANSFER_BUFFER, 'i32');
|
||||
|
|
@ -125,16 +181,44 @@ export class Language {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next state id for a given state id and node type id.
|
||||
*/
|
||||
nextState(stateId: number, typeId: number): number {
|
||||
return C._ts_language_next_state(this[0], stateId, typeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new lookahead iterator for this language and parse state.
|
||||
*
|
||||
* This returns `null` if state is invalid for this language.
|
||||
*
|
||||
* Iterating {@link LookaheadIterator} will yield valid symbols in the given
|
||||
* parse state. Newly created lookahead iterators will return the `ERROR`
|
||||
* symbol from {@link LookaheadIterator#currentType}.
|
||||
*
|
||||
* Lookahead iterators can be useful for generating suggestions and improving
|
||||
* syntax error diagnostics. To get symbols valid in an `ERROR` node, use the
|
||||
* lookahead iterator on its first leaf node state. For `MISSING` nodes, a
|
||||
* lookahead iterator created on the previous non-extra leaf node may be
|
||||
* appropriate.
|
||||
*/
|
||||
lookaheadIterator(stateId: number): LookaheadIterator | null {
|
||||
const address = C._ts_lookahead_iterator_new(this[0], stateId);
|
||||
if (address) return new LookaheadIterator(INTERNAL, address, this);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new query from a string containing one or more S-expression
|
||||
* patterns.
|
||||
*
|
||||
* The query is associated with a particular language, and can only be run
|
||||
* on syntax nodes parsed with that language. References to Queries can be
|
||||
* shared between multiple threads.
|
||||
*
|
||||
* @link {@see https://tree-sitter.github.io/tree-sitter/using-parsers/queries}
|
||||
*/
|
||||
query(source: string): Query {
|
||||
const sourceLength = C.lengthBytesUTF8(source);
|
||||
const sourceAddress = C._malloc(sourceLength + 1);
|
||||
|
|
@ -219,10 +303,10 @@ export class Language {
|
|||
stringValues[i] = C.UTF8ToString(valueAddress, nameLength);
|
||||
}
|
||||
|
||||
const setProperties = new Array<Properties>(patternCount);
|
||||
const assertedProperties = new Array<Properties>(patternCount);
|
||||
const refutedProperties = new Array<Properties>(patternCount);
|
||||
const predicates = new Array<Predicate[]>(patternCount);
|
||||
const setProperties = new Array<QueryProperties>(patternCount);
|
||||
const assertedProperties = new Array<QueryProperties>(patternCount);
|
||||
const refutedProperties = new Array<QueryProperties>(patternCount);
|
||||
const predicates = new Array<QueryPredicate[]>(patternCount);
|
||||
const textPredicates = new Array<TextPredicate[]>(patternCount);
|
||||
|
||||
for (let i = 0; i < patternCount; i++) {
|
||||
|
|
@ -236,7 +320,11 @@ export class Language {
|
|||
predicates[i] = [];
|
||||
textPredicates[i] = [];
|
||||
|
||||
const steps: PredicateStep[] = [];
|
||||
const steps = new Array<PredicateStep>();
|
||||
const isStringStep = (step: PredicateStep): step is { type: 'string', value: string } => {
|
||||
return step.type === 'string';
|
||||
}
|
||||
|
||||
let stepAddress = predicatesAddress;
|
||||
for (let j = 0; j < stepCount; j++) {
|
||||
const stepType = C.getValue(stepAddress, 'i32');
|
||||
|
|
@ -329,7 +417,7 @@ export class Language {
|
|||
}
|
||||
if (steps[2].type !== 'string') {
|
||||
throw new Error(
|
||||
`Second argument of \`#${operator}\` predicate must be a string. Got @${steps[2].value}.`,
|
||||
`Second argument of \`#${operator}\` predicate must be a string. Got @${steps[2].name}.`,
|
||||
);
|
||||
}
|
||||
captureName = steps[1].name;
|
||||
|
|
@ -359,13 +447,13 @@ export class Language {
|
|||
`Wrong number of arguments to \`#set!\` predicate. Expected 1 or 2. Got ${steps.length - 1}.`,
|
||||
);
|
||||
}
|
||||
if (steps.some((s) => s.type !== 'string')) {
|
||||
if (!steps.every(isStringStep)) {
|
||||
throw new Error(
|
||||
`Arguments to \`#set!\` predicate must be a strings.".`,
|
||||
`Arguments to \`#set!\` predicate must be strings.".`,
|
||||
);
|
||||
}
|
||||
if (!setProperties[i]) setProperties[i] = {};
|
||||
setProperties[i][steps[1].value!] = steps[2]?.value ?? null;
|
||||
setProperties[i][steps[1].value] = steps[2]?.value ?? null;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -376,14 +464,14 @@ export class Language {
|
|||
`Wrong number of arguments to \`#${operator}\` predicate. Expected 1 or 2. Got ${steps.length - 1}.`,
|
||||
);
|
||||
}
|
||||
if (steps.some((s) => s.type !== 'string')) {
|
||||
if (!steps.every(isStringStep)) {
|
||||
throw new Error(
|
||||
`Arguments to \`#${operator}\` predicate must be a strings.".`,
|
||||
`Arguments to \`#${operator}\` predicate must be strings.".`,
|
||||
);
|
||||
}
|
||||
const properties = operator === 'is?' ? assertedProperties : refutedProperties;
|
||||
if (!properties[i]) properties[i] = {};
|
||||
properties[i][steps[1].value!] = steps[2]?.value ?? null;
|
||||
properties[i][steps[1].value] = steps[2]?.value ?? null;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -400,15 +488,16 @@ export class Language {
|
|||
`First argument of \`#${operator}\` predicate must be a capture. Got "${steps[1].value}".`,
|
||||
);
|
||||
}
|
||||
for (let i = 2; i < steps.length; i++) {
|
||||
if (steps[i].type !== 'string') {
|
||||
throw new Error(
|
||||
`Arguments to \`#${operator}\` predicate must be a strings.".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
captureName = steps[1].name;
|
||||
const values = steps.slice(2).map((s) => s.value);
|
||||
|
||||
const stringSteps = steps.slice(2);
|
||||
if (!stringSteps.every(isStringStep)) {
|
||||
throw new Error(
|
||||
`Arguments to \`#${operator}\` predicate must be strings.".`,
|
||||
);
|
||||
}
|
||||
const values = stringSteps.map((s) => s.value);
|
||||
|
||||
textPredicates[i].push((captures) => {
|
||||
const nodes = [];
|
||||
for (const c of captures) {
|
||||
|
|
@ -447,6 +536,10 @@ export class Language {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a language from a WebAssembly module.
|
||||
* The module can be provided as a path to a file or as a buffer.
|
||||
*/
|
||||
static async load(input: string | Uint8Array): Promise<Language> {
|
||||
let bytes: Promise<Uint8Array>;
|
||||
if (input instanceof Uint8Array) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue