├── .github └── workflows │ ├── npm-publish.yml │ └── release-package.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── data ├── api.mpack ├── builtin-api.mpack ├── builtin-docs.json ├── builtin.txt ├── diagnostic.mpack ├── lsp.mpack ├── lua.mpack └── treesitter.mpack ├── docs.md ├── func_signature.ne ├── index.d.ts ├── package.json ├── src ├── index.ts ├── mods │ ├── api.ts │ ├── constants.ts │ ├── fn.ts │ ├── index.ts │ ├── option.ts │ ├── types.ts │ └── utils.ts ├── preview.ts ├── ts │ ├── ast │ │ ├── index.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── constants.ts │ ├── index.ts │ ├── types.ts │ └── utils.ts ├── utils.ts └── vim │ ├── constants.ts │ ├── help-doc.ts │ ├── index.ts │ ├── types.ts │ └── utils.ts ├── tsconfig.json ├── types ├── .gitignore ├── index.d.ts ├── tsconfig.json └── utils.d.ts └── yarn.lock /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: building 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | tags-ignore: 8 | # Do not build on version tags to avoid racing npm publish with main branch 9 | - v** 10 | - latest 11 | - beta 12 | 13 | jobs: 14 | try-build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: actions/setup-node@v3 19 | with: 20 | node-version: 16 21 | registry-url: https://registry.npmjs.org/ 22 | - run: yarn in-one-go 23 | -------------------------------------------------------------------------------- /.github/workflows/release-package.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | # reference: https://superface.ai/blog/npm-publish-gh-actions-changelog 3 | # PS: changelog action superfaceai/release-changelog-action@v1 is replaced 4 | # by thomaseizinger/keep-a-changelog-new-release@v1 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | release-type: 9 | description: 'Release type (one of): patch, minor, major, prepatch, preminor, premajor, prerelease' 10 | required: true 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | steps: 15 | # Checkout project repository 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | # Setup Node.js environment 19 | - name: Setup Node.js 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 16 23 | registry-url: https://registry.npmjs.org/ 24 | 25 | - name: Install dependencies, build and run code 26 | run: yarn in-one-go 27 | 28 | # Configure Git 29 | - name: Git configuration 30 | run: | 31 | git config --global user.email "36144635+gkzhb@users.noreply.github.com" 32 | git config --global user.name "GitHub Actions" 33 | 34 | # Bump package version 35 | # Use tag latest 36 | - name: Bump release version 37 | if: startsWith(github.event.inputs.release-type, 'pre') != true 38 | run: | 39 | echo "NEW_VERSION=$(npm --no-git-tag-version version $RELEASE_TYPE)" >> $GITHUB_ENV 40 | echo "RELEASE_TAG=latest" >> $GITHUB_ENV 41 | env: 42 | RELEASE_TYPE: ${{ github.event.inputs.release-type }} 43 | 44 | # Bump package pre-release version 45 | # Use tag beta for pre-release versions 46 | - name: Bump pre-release version 47 | if: startsWith(github.event.inputs.release-type, 'pre') 48 | run: | 49 | echo "NEW_VERSION=$(npm --no-git-tag-version --preid=beta version $RELEASE_TYPE)" >> $GITHUB_ENV 50 | echo "RELEASE_TAG=beta" >> $GITHUB_ENV 51 | env: 52 | RELEASE_TYPE: ${{ github.event.inputs.release-type }} 53 | 54 | # Update changelog unreleased section with new version 55 | - name: Update changelog 56 | uses: thomaseizinger/keep-a-changelog-new-release@v1 57 | with: 58 | version: ${{ env.NEW_VERSION }} 59 | 60 | # Commit changes 61 | - name: Commit CHANGELOG.md and package.json changes and create tag 62 | run: | 63 | git add "package.json" 64 | git add "CHANGELOG.md" 65 | git commit -m "chore: release ${{ env.NEW_VERSION }}" 66 | git tag ${{ env.NEW_VERSION }} 67 | 68 | # Publish version to public repository 69 | - name: Publish 70 | run: yarn publish --verbose --access public --tag ${{ env.RELEASE_TAG }} 71 | env: 72 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 73 | 74 | # Push repository changes 75 | - name: Push changes to repository 76 | env: 77 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 78 | run: | 79 | git push origin && git push --tags 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | func_signature_railroad_diagrams.html 4 | *.log 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [v0.0.7] - 2023-02-08 11 | 12 | ## [v0.0.6] - 2022-09-07 13 | 14 | - Reorganize project file structure 15 | - Update neovim v0.7.2 module mpack files in `data/` which also provide function 16 | declarations from C source code 17 | 18 | ## [0.0.5] 19 | 20 | [Unreleased]: https://github.com/gkzhb/lua-types-nvim/compare/v0.0.7...HEAD 21 | 22 | [v0.0.7]: https://github.com/gkzhb/lua-types-nvim/compare/v0.0.6...v0.0.7 23 | 24 | [v0.0.6]: https://github.com/gkzhb/lua-types-nvim/compare/0.0.5...v0.0.6 25 | 26 | [0.0.5]: https://github.com/gkzhb/lua-types-nvim/compare/v0.0.4..v0.0.5 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Haibin Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lua Types for Neovim Lua APIs 2 | 3 | [![GitHub workflow](https://github.com/gkzhb/lua-types-nvim/actions/workflows/npm-publish.yml/badge.svg?branch=main)](https://github.com/gkzhb/lua-types-nvim/actions/workflows/npm-publish.yml) 4 | [![npm version](https://img.shields.io/npm/v/@gkzhb/lua-types-nvim)](https://www.npmjs.com/package/@gkzhb/lua-types-nvim) 5 | [![GitHub stars](https://img.shields.io/github/stars/gkzhb/lua-types-nvim)](https://github.com/gkzhb/lua-types-nvim/stargazers) 6 | [![GitHub issues](https://img.shields.io/github/issues/gkzhb/lua-types-nvim)](https://github.com/gkzhb/lua-types-nvim/issues) 7 | [![GitHub license](https://img.shields.io/github/license/gkzhb/lua-types-nvim)](https://github.com/gkzhb/lua-types-nvim/blob/main/LICENSE) 8 | 9 | [@gkzhb/lua-types-nvim](https://www.npmjs.com/package/@gkzhb/lua-types-nvim) 10 | provides TypeScript definitions of Neovim(v0.7.2) Lua APIs for 11 | [TypeScriptToLua](https://typescripttolua.github.io/) projects. 12 | 13 | Now this project provides types for 14 | 15 | * `vim.api` 16 | * `vim.lsp` 17 | * `vim.treesitter` 18 | * `vim.diagnostic` 19 | * `vim.fn` 20 | * `vim.opt`, `vim.go`, `vim.bo`, etc 21 | 22 | Inspired by [hrsh7th/deno-nvim-types: The Nvim API type definition for TypeScript](https://github.com/hrsh7th/deno-nvim-types) 23 | and [folke/lua-dev.nvim](https://github.com/folke/lua-dev.nvim). 24 | And thanks for [folke/lua-dev.nvim](https://github.com/folke/lua-dev.nvim)'s 25 | `data` files. 26 | 27 | ## Usage 28 | 29 | 1. Add this npm package to your dev dependencies: 30 | 31 | ```bash 32 | npm install -D @gkzhb/lua-types-nvim 33 | ``` 34 | 35 | 2. Add this package in your `tsconfig.json` of TypeScriptToLua: 36 | 37 | ```json 38 | { 39 | "compilerOptions": { 40 | ... 41 | "types": [ 42 | ... 43 | "@gkzhb/lua-types-nvim" 44 | ], 45 | ... 46 | } 47 | } 48 | ``` 49 | 50 | ## TODO 51 | 52 | * [ ] Replace TypeScript types with Lua types. 53 | * [ ] Manually add function parameter types for `vim.fn` which are not provided 54 | in any strctured data. 55 | * [ ] Generate `types/index.d.ts` by code 56 | 57 | ## Development 58 | 59 | TypeScript files in `src/` are node.js code that are executed to generate ts definition 60 | files from Neovim mpack API metadata. 61 | 62 | While `.d.ts` files in `types/` are type definitions for Neovim Lua APIs in TypeScriptToLua. 63 | 64 | `yarn` scripts: 65 | 66 | * Use `yarn` to install dependencies 67 | * `yarn build` to compile TypeScript in `src` 68 | * `yarn dev` to watch for ts file changes and compile to JS 69 | * `yarn parse-nearley` to build parser required to process `vim.fn` documentations 70 | * And `yarn build-dts` to run the compiled js file to generate Neovim API 71 | type definitions. 72 | * `yarn build-api-dts` to process mpack files 73 | * `yarn build-fn-dts` to generate definitions for `vim.fn` from 74 | [`builtin-docs.json`](./data/builtin-docs.json) and [`builtin.txt`](./data/builtin.txt) 75 | * `yarn --silent preview [module]` to output JSON format content of mpack 76 | data, like 77 | 78 | ```bash 79 | yarn --silent preview lua 80 | ``` 81 | 82 | will output JSON format from `data/lua.mpack`. 83 | * Use `yarn in-one-go` to install dependencies, compile TS and run JS to 84 | generate types in one command. 85 | 86 | This project uses TypeScript `factory` to generate ASTs and print them to files. 87 | 88 | Refer to [docs.md](./docs.md) for more about development. 89 | 90 | ### APIs 91 | 92 | The structure data about APIs are in `data/` folder. 93 | 94 | [`api.mpack`](./data/api.mpack), [`lsp.mpack`](./data/lsp.mpack), 95 | [`lua.mpack`](./data/lua.mpack), [`diagnostic.mpack`](./data/diagnostic.mpack), 96 | [`treesitter.mpack`](./data/treesitter.mpack) and 97 | [`builtin-docs.json`](./data/builtin-docs.json) are from 98 | [folke/lua-dev.nvim](https://github.com/folke/lua-dev.nvim). 99 | Data in these mpack files are the same type `NvimApiFunctions`. 100 | 101 | [`builtin-api.mpack`](./data/builtin-api.mpack) is from Neovim command line 102 | 103 | ```bash 104 | nvim --api-info 105 | ``` 106 | 107 | The data type is `NvimCliApiFunctions`. 108 | 109 | [`builtin.txt`](./data/builtin.txt) is from Neovim documentation file in 110 | `$VIMRUNTIME` which contains `vim.fn` summary information in Section 1 Overview. 111 | 112 | From this file and [`builtin-docs.json`](./data/builtin-docs.json) I get vim 113 | function name, parameter names, return type and not only brief but also detailed 114 | documentations. 115 | 116 | ### References 117 | 118 | * [factory.createSourceFile doesn't accept JSDoc node (typings issue) · Issue #44151 · microsoft/TypeScript](https://github.com/microsoft/TypeScript/issues/44151) 119 | * [Add capability of transforming and emitting JSDoc comments · Issue #17146 · microsoft/TypeScript](https://github.com/microsoft/TypeScript/issues/17146) 120 | * Thank [TypeScript AST Viewer](https://ts-ast-viewer.com/#) so much for the 121 | useful tool that helps to develop with Typescript AST. 122 | * Also thank [Nearley Parser Playground](https://omrelli.ug/nearley-playground/) 123 | for the great DX with [nearley.js - JS Parsing Toolkit](https://nearley.js.org/). 124 | -------------------------------------------------------------------------------- /data/api.mpack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkzhb/lua-types-nvim/33c01596056a10c3893fb3145f9bd99253346a3e/data/api.mpack -------------------------------------------------------------------------------- /data/builtin-api.mpack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkzhb/lua-types-nvim/33c01596056a10c3893fb3145f9bd99253346a3e/data/builtin-api.mpack -------------------------------------------------------------------------------- /data/diagnostic.mpack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkzhb/lua-types-nvim/33c01596056a10c3893fb3145f9bd99253346a3e/data/diagnostic.mpack -------------------------------------------------------------------------------- /data/lsp.mpack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkzhb/lua-types-nvim/33c01596056a10c3893fb3145f9bd99253346a3e/data/lsp.mpack -------------------------------------------------------------------------------- /data/lua.mpack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkzhb/lua-types-nvim/33c01596056a10c3893fb3145f9bd99253346a3e/data/lua.mpack -------------------------------------------------------------------------------- /data/treesitter.mpack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkzhb/lua-types-nvim/33c01596056a10c3893fb3145f9bd99253346a3e/data/treesitter.mpack -------------------------------------------------------------------------------- /docs.md: -------------------------------------------------------------------------------- 1 | # About source code 2 | 3 | ## How this project works 4 | 5 | [Neovim](https://github.com/neovim/neovim) provides a Python script 6 | [`gen_vimdoc.py`](https://github.com/neovim/neovim/blob/master/scripts/gen_vimdoc.py) 7 | to generate vim doc and structural mpack files from the C source code or lua 8 | source code, utilizing [Doxygen](https://doxygen.nl/) (a documentation generator). 9 | 10 | The script will generate `api.mpack`, `diagnostic.mapck`, `lsp.mpack`, 11 | `lua.mpack`, `treesitter.mpack` and corresponding vim doc which are located at 12 | `/runtime/doc/` if you run `python scripts/gen_vimdoc.py` 13 | in the root of neovim project. 14 | 15 | With `INCLUDE_C_DECL` environment variable defined, running the above script 16 | will generate mpack files that contains C function declarations. 17 | 18 | However, these mpack files do not contain all of the information about neovim 19 | lua APIs: 20 | 21 | 1. some `vim` properties are manually maintained in the 22 | `lua.txt` help file. Part of this vim help file is automatically generated which 23 | is after `lua-vim` help tag (including this). 24 | 1. Builtin VimL functions are also not provided. 25 | 1. Lua modules which only exclue `vim.api` can't get any type information 26 | 27 | We can only get these information from manually maintained vim doc files. 28 | 29 | - For buitlin VimL functions, this project tries to extract data from 30 | `builtin.txt` help file. 31 | - For lua module types, we have to manually maintain type definitions in our 32 | source code. That is what code in `src/data/` will accomplish. 33 | - For omitted APIs, we can directly add them to TS type definitions files. 34 | 35 | ## Project structure 36 | 37 | ```text 38 | src/ 39 | ├── data/ -- TODO: manually created type information 40 | ├── index.ts -- aggregate all functions to generate type definitions 41 | ├── mods/ -- generate type definitions for vim.api, vim.fn, vim.lsp, vim.treesitter, vim.diagnostic and vim.fn 42 | │   ├── api.ts -- vim.api, vim.fn, vim.lsp, treesitter, vim.diagnostic 43 | │   ├── constants.ts 44 | │   ├── fn.ts -- vim.fn 45 | │   ├── index.ts 46 | │   ├── option.ts -- vim options for windows, buffers, etc 47 | │   ├── types.ts 48 | │   └── utils.ts 49 | ├── preview.ts -- view mpack file content in JSON format 50 | ├── ts/ -- TypeScript related 51 | │   ├── ast/ -- TypeScript AST related 52 | │ │ ├── index.ts 53 | │ │ ├── types.ts 54 | │ │ └── utils.ts 55 | │   ├── constants.ts 56 | │   ├── index.ts 57 | │   ├── types.ts 58 | │   └── utils.ts 59 | ├── utils.ts 60 | └── vim/ -- vim related stuff 61 | ├── constants.ts 62 | ├── help-doc.ts 63 | ├── index.ts 64 | ├── types.ts 65 | └── utils.ts 66 | ``` 67 | 68 | ## Prosedure of generating type definitions from mapck files 69 | 70 | 1. Read from mpack file to get `NvimApiFunctions` data; 71 | 1. Analyze function names to recursively search property objects and generate 72 | corresponding data structure; 73 | 1. From the previous generated data structure, create TypeScript AST nodes; 74 | 1. Write these ASTs to `d.ts` files. 75 | 76 | ## Nearley parser 77 | 78 | Use `yarn parse-nearley` command to parse `func_signature.ne` to 79 | `dist/func_signature.js` which later will be used by `src/fn.ts`. 80 | 81 | `yarn build-parser-diag` will generate `func_signature_railroad_diagrams.html` 82 | file which shows visualized railroad diagrams for non-terminals. 83 | 84 | In `func_signature.ne` most non-terminals eat up the beginning whitespaces 85 | but not trailing whitespaces to avoid ambiguity in the grammar. 86 | 87 | For example, `param` eats up beginning and trailing whitespaces, 88 | and another non-terminal `remainReqParamList` also eats up beginning whitespaces. 89 | And now we have a rule that concatenates these two: 90 | 91 | ```nearley 92 | reqParamList -> param remainReqParamList 93 | ``` 94 | 95 | Whitespaces between them can belong to `param` or belong to `remainReqParamList`, 96 | which leads to ambiguous grammar. 97 | 98 | -------------------------------------------------------------------------------- /func_signature.ne: -------------------------------------------------------------------------------- 1 | @{% 2 | // Moo lexer documention is here: 3 | // https://github.com/no-context/moo 4 | 5 | const moo = require("moo"); 6 | const lexer = moo.compile({ 7 | dots: /\.{3}/, 8 | number: /-?\d+/, 9 | ws: /[ \t]+/, 10 | lparen: '(', 11 | rparen: ')', 12 | lbracket: '{', 13 | rbracket: '}', 14 | lr: '[', 15 | rr: ']', 16 | comma: ',', 17 | id: /[a-zA-Z][a-zA-Z0-9-_\.]*/, 18 | }); 19 | const emptyParamList = () => ({ type: 'paramList', params: [] }); 20 | const cloneParams = (params) => params.map(item => ({...item})); 21 | %} 22 | 23 | # Pass your lexer with @lexer: 24 | @lexer lexer 25 | 26 | main -> function {% id %} 27 | 28 | # space is optional 29 | space -> %ws | null 30 | opComma -> %comma | null 31 | optionalComma -> space %comma {% () => ({ comma: true }) %} 32 | | null {% () => ({ comma: false }) %} 33 | 34 | param -> space %lbracket %id %rbracket {% (list) => ({ type: 'param', id: list[2], optional: false, more: false }) %} 35 | | space %id {% (list) => ({ type: 'param', id: list[1], optional: false, more: false }) %} 36 | | space %number {% (list) => ({ type: 'param', id: list[1], numeric: true, optional: false, more: false }) %} 37 | 38 | optionalParamList1 -> optionalParamList optionalParamList1 {% list => { 39 | const ret = { type: 'optinalParamList', params: cloneParams(list[0].params) }; 40 | if (list[1].params.length) { 41 | ret.params.push(...cloneParams(list[1].params)); 42 | } 43 | ret.params.forEach(item => item.optional = true); 44 | return ret; 45 | } %} 46 | | null {% emptyParamList %} 47 | 48 | optionalParamListOneLevel -> optionalComma space %lr nonEmptyParamList space %rr {% list => { 49 | const ret = { type: 'optinalParamList', params: cloneParams(list[3].params), comma: undefined }; 50 | ret.params.forEach(item => item.optional = true); 51 | return ret; 52 | } %} 53 | 54 | optionalParamList -> optionalParamListOneLevel optionalParamList1 {% list => { 55 | const ret = { type: 'optinalParamList', params: cloneParams(list[0].params) }; 56 | if (list[1].params.length) { 57 | ret.params.push(...cloneParams(list[1].params)); 58 | } 59 | return ret; 60 | } %} 61 | 62 | dots -> space %dots {% () => 63 | ({ type: 'paramList', params: [{ type: 'param', id: { text: 'args' }, optional: true, more: true }]}) 64 | %} 65 | nonEmptyRemainReqParamList -> space %comma reqParamList {% list => list[2] %} 66 | | optionalComma dots {% list => ({ ...list[1], comma: list[0].comma }) %} 67 | remainReqParamList -> nonEmptyRemainReqParamList {% id %} 68 | | null {% emptyParamList %} 69 | optionalParam -> param {% id %} | null {% () => null %} 70 | # at least one param 71 | reqParamList -> nonEmptyRemainReqParamList {% id %} 72 | | param remainReqParamList {% (list) => { 73 | const ret = { type: 'paramList', params: [ list[0] ] }; 74 | if (list[1].params.length > 0) { 75 | ret.params.push(...list[1].params); 76 | } 77 | if (list[1].comma === false) { 78 | if (ret.params.length > 1) { 79 | ret.params.pop(); 80 | ret.params[ret.params.length - 1].more = true; 81 | } else { 82 | ret.comma === false; 83 | } 84 | } 85 | return ret; 86 | } %} 87 | optOptionalParamList -> optionalParamList {% id %} | null {% emptyParamList %} 88 | # eat up commas from begin 89 | nonEmptyParamList -> reqParamList optOptionalParamList {% (list) => { 90 | const ret = { type: 'paramList', params: cloneParams(list[0].params) }; 91 | if (list[1].params.length > 0) { 92 | ret.params.push(...cloneParams(list[1].params)); 93 | } else { 94 | ret.comma = list[0].comma; 95 | } 96 | return ret; 97 | } %} 98 | | optionalParamList {% list => list[0] %} 99 | 100 | paramList -> nonEmptyParamList space {% id %} | space {% emptyParamList %} 101 | 102 | function -> %id %lparen paramList %rparen {% list => ({ type: 'function', params: list[2].params, id: list[0] }) %} 103 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@gkzhb/lua-types-nvim", 3 | "version": "0.0.7", 4 | "description": "TypeScript definitions for Neovim Lua APIs", 5 | "keywords": [ 6 | "neovim", 7 | "typescript", 8 | "lua", 9 | "tstl" 10 | ], 11 | "main": "", 12 | "types": "index.d.ts", 13 | "files": [ 14 | "index.d.ts", 15 | "types/*.d.ts", 16 | "types/tsconfig.json", 17 | "README.md", 18 | "CHANGELOG.md", 19 | "LICENSE" 20 | ], 21 | "author": "gkzhb ", 22 | "license": "MIT", 23 | "private": false, 24 | "repository": { 25 | "url": "https://github.com/gkzhb/lua-types-nvim", 26 | "type": "git" 27 | }, 28 | "devDependencies": { 29 | "@msgpack/msgpack": "^2.7.2", 30 | "@types/node": "^18.0.3", 31 | "better-nearley-railroad": "^1.3.0", 32 | "lua-types": "^2.11.0", 33 | "nearley": "^2.20.1", 34 | "tsc": "^2.0.4", 35 | "typescript": "^4.7.4", 36 | "typescript-to-lua": "^1.6.0", 37 | "typescript-tstl-plugin": "^0.3.2" 38 | }, 39 | "scripts": { 40 | "build": "tsc", 41 | "dev": "tsc -w", 42 | "build-api-dts": "node dist/api.js", 43 | "build-fn-dts": "node dist/fn.js", 44 | "build-option-dts": "node dist/option.js", 45 | "build-dts": "node dist/index.js", 46 | "preview": "node dist/preview.js", 47 | "in-one-go": "yarn && yarn build && yarn parse-nearley && yarn build-dts", 48 | "clean": "rm -r dist/ && cd types/ && rm -f api.d.ts lsp.d.ts lua.d.ts diagnostic.d.ts treesitter.d.ts fn.d.ts option.d.ts", 49 | "parse-nearley": "nearleyc ./func_signature.ne -o dist/func_signature.js;", 50 | "build-parser-diag": "npx nearley-rr ./dist/func_signature.js func_signature_railroad_diagrams.html" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { generateFnTypes, generateApiTypes, generateOptionTypes } from "./mods"; 2 | 3 | export * from './preview'; 4 | export { generateFnTypes, generateApiTypes, generateOptionTypes }; 5 | 6 | if (require.main === module) { 7 | generateApiTypes(); 8 | generateFnTypes(); 9 | generateOptionTypes(); 10 | } 11 | -------------------------------------------------------------------------------- /src/mods/api.ts: -------------------------------------------------------------------------------- 1 | import { moduleList } from "./constants"; 2 | import { processMod } from "./utils"; 3 | 4 | export const generateApiTypes = () => { 5 | for (const mod of moduleList) { 6 | console.log(`=== start process module ${mod} ===`); 7 | processMod(mod); 8 | } 9 | }; 10 | 11 | if (require.main === module) { 12 | generateApiTypes(); 13 | } 14 | -------------------------------------------------------------------------------- /src/mods/constants.ts: -------------------------------------------------------------------------------- 1 | import { Modules } from './types'; 2 | 3 | export const moduleList = [ 4 | Modules.API, 5 | Modules.LUA, 6 | Modules.LSP, 7 | Modules.TREESITTER, 8 | Modules.DIAGNOSTIC, 9 | ]; 10 | 11 | /** Get mpack file path for a module */ 12 | export const mod2MpackPath = (mod: string) => `./data/${mod}.mpack`; 13 | /** Get ts definition file path for a module */ 14 | export const mod2DefFilePath = (mod: string) => `./types/${mod}.d.ts`; 15 | -------------------------------------------------------------------------------- /src/mods/fn.ts: -------------------------------------------------------------------------------- 1 | // process `builtin.txt` help doc to generate types for `vim.fn` 2 | // @ts-expect-error 3 | import * as nearley from 'nearley'; 4 | import { SyntaxKind } from "typescript"; 5 | import { isNumeric } from "../utils"; 6 | import { 7 | writeTSFile, 8 | IFunction, 9 | IInterface, 10 | IParameter, 11 | IProp, 12 | typeNodes, 13 | headAstNodes, 14 | attachInlineJSDoc2Node, 15 | getInterface, 16 | } from "../ts"; 17 | import { 18 | divideSections, 19 | findSectionByHelpTag, 20 | builtinData, 21 | convertType, 22 | processDocLines, 23 | NVIM_TYPE_MAP, 24 | } from "../vim"; 25 | // @ts-expect-error 26 | import * as funcParser from '../func_signature.js'; 27 | import { mod2DefFilePath } from "./constants"; 28 | 29 | interface IParserParam { 30 | type: 'param'; 31 | id: { 32 | text: string; 33 | }; 34 | more?: boolean; 35 | optional: boolean; 36 | } 37 | interface IParserFunction { 38 | type: 'function'; 39 | params: IParserParam[]; 40 | } 41 | /** read lines from doc text file and combine wrapped lines */ 42 | const readFnDocs = () => { 43 | const sections = divideSections('./data/builtin.txt'); 44 | // process overview 45 | const tableLineReg = /^USAGE\t+RESULT\t+DESCRIPTION/; 46 | let tableLineIdx = -1; 47 | const sectionIdx = findSectionByHelpTag(sections, 'builtin-function-list'); 48 | if (sectionIdx === -1) { 49 | throw new Error('Cannot find builtin-function-list in help file'); 50 | } 51 | for (let i = 0; i < sections[sectionIdx].length; i++) { 52 | if (tableLineReg.test(sections[sectionIdx][i])) { 53 | tableLineIdx = i; 54 | break; 55 | } 56 | } 57 | const content = sections[sectionIdx]; 58 | const tableLineList: string[] = []; 59 | let tableLines = 0; 60 | const newRowReg = /^\w/; 61 | const descNewLineReg = /^(\t){5}[^\s]/; 62 | const resultNewLineReg = /^(\t){4}[^\s]/; 63 | // merge multiple lines of one table row into one line 64 | for (let i = tableLineIdx + 1; i < content.length; i++) { 65 | const line = content[i]; 66 | if (!line.trim().length) { 67 | // skip empty lines 68 | continue; 69 | } 70 | if (descNewLineReg.test(line)) { 71 | // description in new line, combine to the last line 72 | // this may be multiple description lines, or after return column 73 | tableLineList[tableLines - 1] = `${tableLineList[tableLines - 1]} ${content[i].trimStart()}`; 74 | } else { 75 | if (resultNewLineReg.test(line)) { 76 | // result in new line, combine to the last line 77 | tableLineList[tableLines - 1] = 78 | tableLineList[tableLines - 1] + "\t" + content[i].trimStart(); 79 | } else if (newRowReg.test(line)) { 80 | tableLineList.push(line); 81 | tableLines++; 82 | } else { 83 | console.warn('invalid line', line); 84 | } 85 | } 86 | } 87 | return tableLineList; 88 | }; 89 | 90 | const resultTypeReg = /^[A-Z][a-z]+|any/; 91 | const twoReturnTypeReg = /^(\w+)( or |\/)(\w+)\s/; 92 | const oneReturnTypeReg = /^(\w+)\s/; 93 | const divideRow = (row: string): [string, string[], string] | null => { 94 | if (!row.trim().length) { 95 | return null; 96 | } 97 | let divider1 = row.indexOf(')') + 1; 98 | if (divider1 === 0) { 99 | // one function doesn't have ')', so we have to use '\t' to find column divider position 100 | // If we only use \t to find the first column divider, we may encounter the situation like `settabwinvar` 101 | // where there is no return type and spaces as divider before `DESCRIPTION` column 102 | divider1 = row.indexOf('\t'); 103 | } 104 | const col1 = row.slice(0, divider1); 105 | let remain = row.slice(divider1).trim(); 106 | // multiple return types are or logic 107 | let returnType: string[] = ['any']; 108 | if (resultTypeReg.test(remain)) { 109 | // return type column exists 110 | let length = -1; 111 | if (twoReturnTypeReg.test(remain)) { 112 | const result = twoReturnTypeReg.exec(remain)!; 113 | returnType = [result[1], result[3]]; 114 | length = result[0].length; 115 | } else { 116 | const result = oneReturnTypeReg.exec(remain)!; 117 | if (!(result[1].toLowerCase() in NVIM_TYPE_MAP)) { 118 | // line wrap with 4 tabs starting with is description, not return type 119 | console.log(`${col1} wrapped line starts with description: ${remain}`); 120 | return [col1, returnType, remain]; 121 | } 122 | returnType = [result[1]]; 123 | length = result[0].length; 124 | } 125 | remain = remain 126 | .slice(length) 127 | .trim(); 128 | } 129 | 130 | return [col1, returnType, remain]; 131 | } 132 | 133 | const processParamText = (text: string, idx: number, paramSet: Set) => { 134 | let retVal = text; 135 | if (isNumeric(retVal)) { 136 | retVal = 'numericValue'; 137 | } else if (text === 'default') { 138 | retVal = 'defaultValue'; 139 | } 140 | retVal = retVal.replace(/-/g, '_'); 141 | if (paramSet.has(retVal)) { 142 | retVal += (idx + 1).toString(); 143 | } 144 | paramSet.add(retVal); 145 | return retVal; 146 | }; 147 | 148 | export const generateFnTypes = () => { 149 | const tableLineList = readFnDocs(); 150 | const fnMap: Record = {}; 151 | const table = tableLineList.map(line => divideRow(line)).filter((row): row is [string, string[], string] => Boolean(row)); 152 | const nameReg = /^(?[\w_\.]+)\(((, | )?\{(?[\d\w-]+)\})*(?[\[\]\{\}\d\w-_, \.]+)?\)$/; 153 | table.forEach(row => { 154 | if (!row[0].endsWith(')')) { 155 | // fix typo that forgets ')' 156 | row[0] += ')'; 157 | } 158 | const result = nameReg.exec(row[0])?.groups; 159 | const fnName = result?.funcName; 160 | if (!fnName) { 161 | console.log(`cannot extract function name "${row[0]}"`); 162 | return; 163 | } 164 | if (!(fnName in builtinData.signatureHelp)) { 165 | console.log(`no fn signature for "${row[0]}"`); 166 | return; 167 | } 168 | // cannot reuse parser 169 | const parser = new nearley.Parser(nearley.Grammar.fromCompiled(funcParser)); 170 | try { 171 | parser.feed(row[0]); 172 | } catch(e) { 173 | console.error('parse error', e, 'for line', row[0]); 174 | process.exit(1); 175 | } 176 | if (parser.results?.length) { 177 | const paramSet: Set = new Set(); 178 | const parserParams = (parser.results[0] as IParserFunction).params; 179 | const params = parserParams.map((param, idx): IParameter => { 180 | const more = idx === parserParams.length - 1 && Boolean(param.more); 181 | return { 182 | kind: "parameter", 183 | id: processParamText(param.id.text, idx, paramSet), 184 | type: more ? typeNodes.unknownArray() : typeNodes.unknown(), 185 | optional: param.optional, 186 | more, 187 | }; 188 | }); 189 | const returnTypes = row[1].map(type => convertType(type).type); 190 | let returnType = returnTypes.length > 1 ? returnTypes[0] : returnTypes[0]; 191 | const signatureReturn = builtinData.signatureHelp[fnName][1]; 192 | if (signatureReturn !== row[1][0]) { 193 | if (signatureReturn === 'none') { 194 | returnType = typeNodes.void(); 195 | } else { 196 | console.log(`"${fnName}" ret type mismatch: "${signatureReturn}" "${row[1]}"`); 197 | } 198 | } 199 | const funcType: IFunction = { 200 | kind: 'function', 201 | return: returnType, 202 | parameters: params, 203 | }; 204 | fnMap[fnName] = { 205 | kind: 'property', 206 | id: fnName, 207 | comments: [row[2]], 208 | type: funcType, 209 | }; 210 | if (fnName in builtinData.documents.functions) { 211 | fnMap[fnName].comments.push(...processDocLines(builtinData.documents.functions[fnName])); 212 | } else { 213 | console.log(`cannot find docs for "${fnName}"`); 214 | } 215 | // console.log(JSON.stringify(fnMap[fnName])); 216 | } else { 217 | console.error(`empty parser result for "${row[0]}"`); 218 | } 219 | }); 220 | const fnInterface: IInterface = { 221 | kind: 'interface', 222 | props: fnMap, 223 | id: 'Fn', 224 | modifiers: [SyntaxKind.ExportKeyword], 225 | }; 226 | const astData = headAstNodes.slice(); 227 | const interfaceNode = getInterface(fnInterface); 228 | attachInlineJSDoc2Node(interfaceNode, ['@noSelf']); 229 | astData.push(interfaceNode); 230 | writeTSFile(mod2DefFilePath('fn'), astData); 231 | }; 232 | 233 | if (require.main === module) { 234 | generateFnTypes(); 235 | } 236 | -------------------------------------------------------------------------------- /src/mods/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './constants'; 3 | export * from './utils'; 4 | export * from './api'; 5 | export * from './fn'; 6 | export * from './option'; 7 | -------------------------------------------------------------------------------- /src/mods/option.ts: -------------------------------------------------------------------------------- 1 | import { SyntaxKind } from "typescript"; 2 | import { builtinData, convertType, processDocLines } from "../vim"; 3 | import { 4 | IBaseType, 5 | IInterface, 6 | IProp, 7 | writeTSFile, 8 | headAstNodes, 9 | attachInlineJSDoc2Node, 10 | getInterface, 11 | } from "../ts"; 12 | import { mod2DefFilePath } from "./constants"; 13 | 14 | /** Vim option scopes */ 15 | export enum EVimOptionType { 16 | /** full options */ 17 | Option = "opt", 18 | /** global: global options */ 19 | Global = "global", 20 | /** buffer: buffer options */ 21 | Buffer = "buffer", 22 | Window = "window", 23 | } 24 | 25 | export const generateOptionTypes = () => { 26 | console.log("=== start to build options types ==="); 27 | const scopedOptions: Record = { 28 | [EVimOptionType.Option]: { 29 | kind: "interface", 30 | props: {}, 31 | id: "Option", 32 | modifiers: [SyntaxKind.ExportKeyword], 33 | }, 34 | [EVimOptionType.Global]: { 35 | kind: "interface", 36 | props: {}, 37 | id: "Global", 38 | modifiers: [SyntaxKind.ExportKeyword], 39 | }, 40 | [EVimOptionType.Buffer]: { 41 | kind: "interface", 42 | props: {}, 43 | id: "Buffer", 44 | modifiers: [SyntaxKind.ExportKeyword], 45 | }, 46 | [EVimOptionType.Window]: { 47 | kind: "interface", 48 | props: {}, 49 | id: "Window", 50 | modifiers: [SyntaxKind.ExportKeyword], 51 | }, 52 | }; 53 | const reg = /^(?\w+)\s*(\(.*\))?$/; 54 | const globalOptionTypeReg = /^\s*global\s.*$/; 55 | const localOptionTypeReg = /local to (?[a-zA-z]+).*/; 56 | const options = builtinData.documents.options; 57 | const scopeStrings: Set = new Set(); 58 | for (const option in options) { 59 | const result = reg.exec(options[option][0])?.groups; 60 | if (result) { 61 | const prop: IProp = { 62 | kind: "property", 63 | id: option, 64 | type: { 65 | kind: "type", 66 | type: convertType(result.type).type, 67 | } as IBaseType, 68 | comments: processDocLines(options[option]), 69 | }; 70 | scopedOptions.opt.props[option] = prop; 71 | const scopeString = options[option][1]; 72 | if (globalOptionTypeReg.test(scopeString)) { 73 | scopedOptions[EVimOptionType.Global].props[option] = prop; 74 | } 75 | if (localOptionTypeReg.test(scopeString)) { 76 | const result = localOptionTypeReg.exec(scopeString)?.groups; 77 | if (result && result.scope in scopedOptions) { 78 | scopedOptions[result.scope as EVimOptionType].props[option] = prop; 79 | } else if (!scopeStrings.has(scopeString)) { 80 | console.log( 81 | "unknown scope:", 82 | result?.scope, 83 | ", original string:", 84 | scopeString 85 | ); 86 | } 87 | } 88 | if (!scopeStrings.has(scopeString)) { 89 | scopeStrings.add(scopeString); 90 | } 91 | } 92 | } 93 | // console.log('scope strings:\n', [...scopeStrings].join('\n')); 94 | const astData = headAstNodes.slice(); 95 | for (const optType in scopedOptions) { 96 | const interfaceASTNode = getInterface( 97 | scopedOptions[optType as EVimOptionType] 98 | ); 99 | attachInlineJSDoc2Node(interfaceASTNode, ["@noSelf"]); 100 | astData.push(interfaceASTNode); 101 | } 102 | writeTSFile(mod2DefFilePath("option"), astData); 103 | }; 104 | 105 | if (require.main === module) { 106 | generateOptionTypes(); 107 | } 108 | -------------------------------------------------------------------------------- /src/mods/types.ts: -------------------------------------------------------------------------------- 1 | import { NVIM_TYPE_MAP } from "../vim"; 2 | 3 | // types for mpack data 4 | /** Neovim API function */ 5 | export interface IApiFunction { 6 | /** annotations for function */ 7 | annotations: string[]; 8 | /** function signature */ 9 | signature: string; 10 | /** function parameters' type and name */ 11 | parameters: [type: keyof NVIM_TYPE_MAP, name: string][]; 12 | /** documentation for function parameters */ 13 | parameters_doc: Record; 14 | /** documentation for this function */ 15 | doc: string[]; 16 | /** documentation for the return value of function */ 17 | return: string[]; 18 | /** references for this function */ 19 | seealso: string[]; 20 | } 21 | 22 | /** api.mpack data type */ 23 | export type NvimApiFunctions = Record; 24 | 25 | export type LuaObjectPropMap = Record; 26 | 27 | export interface ICliFunction { 28 | since: number; 29 | name: string; 30 | return_type: string; 31 | method: boolean; 32 | /** [type, name] pairs */ 33 | parameters: [string, string][]; 34 | } 35 | 36 | export interface ICliUIEvent { 37 | name: string; 38 | since: number; 39 | /** [type, name] pairs */ 40 | parameters: [string, string][]; 41 | } 42 | /** builtin-api.mpack data type */ 43 | export interface NvimCliApiFunctions { 44 | version: { 45 | major: number; 46 | minor: number; 47 | patch: number; 48 | api_level: number; 49 | api_compatible: number; 50 | api_prerelease: boolean; 51 | }; 52 | functions: ICliFunction[]; 53 | ui_options: string[]; 54 | types: Record; 55 | error_types: Record; 56 | ui_events: ICliUIEvent[]; 57 | } 58 | 59 | export enum Modules { 60 | API = "api", 61 | LUA = "lua", 62 | LSP = "lsp", 63 | TREESITTER = "treesitter", 64 | DIAGNOSTIC = "diagnostic", 65 | } 66 | -------------------------------------------------------------------------------- /src/mods/utils.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import { factory, ModifierSyntaxKind, SyntaxKind } from "typescript"; 3 | import { decode } from "@msgpack/msgpack"; 4 | 5 | import { 6 | checkFunctionObjectProperty, 7 | convertType, 8 | processDocLines, 9 | } from "../vim"; 10 | import { 11 | attachInlineJSDoc2Node, 12 | getInterface, 13 | headAstNodes, 14 | IFunction, 15 | IInterface, 16 | ILiteralType, 17 | IParameter, 18 | IProp, 19 | writeTSFile, 20 | } from "../ts"; 21 | import { 22 | IApiFunction, 23 | ICliFunction, 24 | LuaObjectPropMap, 25 | Modules, 26 | NvimApiFunctions, 27 | NvimCliApiFunctions, 28 | } from "./types"; 29 | import { mod2DefFilePath, mod2MpackPath } from "./constants"; 30 | 31 | /** 32 | * Convert IApiFunction to IFunction 33 | */ 34 | export const convertFunction = ( 35 | fname: string, 36 | f: IApiFunction, 37 | mod: Record | undefined> 38 | ): { node: IFunction; comments: string[] } | null => { 39 | if (checkFunctionObjectProperty(fname)) { 40 | // process treesitter lua object properties 41 | // add property to `mod` 42 | const divideIdx = fname.indexOf(":"); 43 | const prop = fname.slice(0, divideIdx); 44 | const newFuncName = fname.slice(divideIdx + 1); 45 | if (!mod[prop]) { 46 | mod[prop] = {}; 47 | } 48 | mod[prop]![newFuncName] = f; 49 | return null; 50 | } 51 | 52 | const parametersExist = 53 | f.parameters.length && 54 | !(f.parameters.length === 1 && f.parameters[0][0] === "void"); 55 | const node: IFunction = { 56 | kind: "function", 57 | parameters: [] as IParameter[], 58 | return: factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword), 59 | }; 60 | if (parametersExist) { 61 | if (f.parameters[0][1] === "self") { 62 | // convert 'self' parameter to `this: any` 63 | f.parameters[0] = ["Any", "this"]; 64 | } 65 | const paramLen = f.parameters.length; 66 | if (f.parameters[paramLen - 1][1] === "...") { 67 | // deal with remaining parameters 68 | f.parameters[paramLen - 1] = [ 69 | `ArrayOf(${f.parameters[paramLen - 1][0]})`, 70 | "...args", 71 | ]; 72 | } 73 | f.parameters.forEach(([type, name], idx) => { 74 | const result = convertType(type); 75 | const temp: IParameter = { 76 | kind: "parameter", 77 | id: name.replace("...", ""), 78 | optional: result.optional, 79 | type: result.type, 80 | more: idx === f.parameters.length && name.startsWith("..."), 81 | }; 82 | node.parameters.push(temp); 83 | }); 84 | } 85 | 86 | // Generate TSDoc comments for the API 87 | const docs: string[] = []; 88 | docs.push(...f.doc); 89 | for (const param in f.parameters_doc) { 90 | docs.push(`@param ${param} ${f.parameters_doc[param]}`); 91 | } 92 | if (f.return.length) { 93 | docs.push(`@returns ${f.return[0]}`); 94 | docs.push(...f.return.slice(1)); 95 | } 96 | 97 | if (f.signature.length) { 98 | docs.push(`@signature \`${f.signature}\``); 99 | } 100 | 101 | if (f.annotations.length) { 102 | docs.push(`@annotations ${f.annotations[0]}`); 103 | docs.push(...f.annotations.slice(1)); 104 | } 105 | 106 | if (f.seealso.length) { 107 | docs.push(`@reference ${f.seealso[0]}`); 108 | docs.push(...f.seealso.slice(1)); 109 | } 110 | const comments = processDocLines(docs); 111 | return { node, comments }; 112 | }; 113 | 114 | export const updateApiASTNodes = ( 115 | node: IInterface, 116 | functions: ICliFunction[] 117 | ) => { 118 | const addNvimPrefix = (str: string) => `nvim_${str}`; 119 | for (const func of functions) { 120 | if (!(func.name in node.props)) { 121 | const newName = addNvimPrefix(func.name); 122 | if (!(newName in node.props)) { 123 | console.log(`"${func.name}" is not in API`); 124 | continue; 125 | } 126 | func.name = newName; 127 | } 128 | // Api exists in AST 129 | const f = node.props[func.name].type; 130 | if (f.kind === "function") { 131 | // f is function 132 | if (func.parameters.length > f.parameters.length) { 133 | // mismatch API length 134 | console.log( 135 | `${func.name} parameters length issue`, 136 | func.parameters, 137 | f.parameters 138 | ); 139 | } else { 140 | for (let i = 0; i < func.parameters.length; i++) { 141 | if (func.parameters[i][1] !== f.parameters[i].id) { 142 | console.log( 143 | `${func.name} parameter name mismatches ${func.parameters[i][1]} ${f.parameters[i].id}` 144 | ); 145 | } 146 | const newType = convertType(func.parameters[i][0]); 147 | f.parameters[i].type = newType.type; 148 | } 149 | f.return = convertType(func.return_type).type; 150 | } 151 | } else { 152 | // f is LiteralType 153 | console.log(`${func.name} is not a function`); 154 | } 155 | } 156 | }; 157 | 158 | export const processMod = (mod: Modules) => { 159 | const file = fs.readFileSync(mod2MpackPath(mod)); 160 | const result = decode(file) as NvimApiFunctions; 161 | const modName = mod.charAt(0).toUpperCase() + mod.slice(1); 162 | const interfaceNode = processApiFunctions(result, modName, [ 163 | SyntaxKind.ExportKeyword, 164 | ]); 165 | 166 | if (mod === Modules.API) { 167 | console.log("== process API with cli mpack data =="); 168 | const cliApiContent = fs.readFileSync(mod2MpackPath("builtin-api")); 169 | const cliData = decode(cliApiContent) as NvimCliApiFunctions; 170 | updateApiASTNodes(interfaceNode, cliData.functions); 171 | } 172 | 173 | const targetFilePath = mod2DefFilePath(mod); 174 | const astData = headAstNodes.slice(); 175 | const interfaceASTNode = getInterface(interfaceNode); 176 | attachInlineJSDoc2Node(interfaceASTNode, ["@noSelf"]); 177 | astData.push(interfaceASTNode); 178 | writeTSFile(targetFilePath, astData); 179 | }; 180 | 181 | export const processApiFunctions = (obj: NvimApiFunctions, module: string, modifiers: ModifierSyntaxKind[]): IInterface => { 182 | const node: IInterface = { 183 | kind: 'interface', 184 | props: {}, 185 | id: module, 186 | modifiers, 187 | }; 188 | const mod: LuaObjectPropMap = {}; 189 | for (const fname in obj) { 190 | const func = convertFunction(fname, obj[fname], mod); 191 | if (func) { 192 | const propNode: IProp = { 193 | kind: 'property', 194 | id: fname, 195 | type: func.node, 196 | comments: func.comments, 197 | } 198 | node.props[fname] = propNode; 199 | } 200 | } 201 | // process lua object properties stored in `mod` 202 | const props = processLuaObjectProps(mod); 203 | node.props = { 204 | ...node.props, 205 | ...props, 206 | } 207 | return node; 208 | }; 209 | 210 | /** process methods in property */ 211 | export const processApis = (obj: NvimApiFunctions): ILiteralType => { 212 | const node: ILiteralType = { 213 | kind: 'literalType', 214 | props: {}, 215 | }; 216 | const mod: LuaObjectPropMap = {}; 217 | 218 | for (const fname in obj) { 219 | const func = convertFunction(fname, obj[fname], mod); 220 | if (func) { 221 | const propNode: IProp = { 222 | kind: 'property', 223 | id: fname, 224 | type: func.node, 225 | comments: func.comments, 226 | } 227 | node.props[fname] = propNode; 228 | } 229 | } 230 | // process lua object properties stored in `mod` 231 | const props = processLuaObjectProps(mod); 232 | node.props = { 233 | ...node.props, 234 | ...props, 235 | } 236 | return node; 237 | }; 238 | /** process all methods with object namespace, like 'treesitter:get_node' */ 239 | export const processLuaObjectProps = (mod: LuaObjectPropMap) => { 240 | const props: Record = {}; 241 | for (const prop in mod) { 242 | if (mod[prop]) { 243 | const result = processApis(mod[prop]!); 244 | // a property 245 | const propType: IProp = { 246 | kind: 'property', 247 | id: prop, 248 | type: result, 249 | comments: [], 250 | }; 251 | props[prop] = propType; 252 | } 253 | } 254 | return props; 255 | }; 256 | -------------------------------------------------------------------------------- /src/preview.ts: -------------------------------------------------------------------------------- 1 | import { mod2MpackPath } from "./mods"; 2 | import * as fs from 'fs'; 3 | import { decode } from '@msgpack/msgpack'; 4 | 5 | /** Preview mpack file content in JSON format 6 | * @param mod module name 7 | * @return JSON string 8 | */ 9 | export const previewMpackContent = (mod: string) => { 10 | const mpackPath = mod2MpackPath(mod); 11 | if (!fs.existsSync(mpackPath)) { 12 | console.log(`Module ${mod} does not exist.`); 13 | process.exit(0); 14 | } 15 | 16 | const file = fs.readFileSync(mpackPath); 17 | const result = decode(file) as any; 18 | return JSON.stringify(result, undefined, 2); 19 | }; 20 | 21 | if (require.main === module) { 22 | const mod = process.argv.length > 2 ? process.argv[2] : null; 23 | 24 | if (!mod) { 25 | console.log("Please provide module name."); 26 | process.exit(0); 27 | } 28 | 29 | console.log(previewMpackContent(mod)); 30 | } 31 | -------------------------------------------------------------------------------- /src/ts/ast/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './utils'; 3 | -------------------------------------------------------------------------------- /src/ts/ast/types.ts: -------------------------------------------------------------------------------- 1 | import { ModifierSyntaxKind, TypeNode } from "typescript"; 2 | 3 | // types for generating ASTs 4 | export interface INode { 5 | /** node type */ 6 | kind: string; 7 | } 8 | /** function parameter */ 9 | export interface IParameter extends INode { 10 | kind: 'parameter'; 11 | id: string; 12 | type: TypeNode; 13 | /** whether to add question mark */ 14 | optional?: boolean; 15 | /** ``...args`, variable number of parameters */ 16 | more: boolean; 17 | } 18 | /** function */ 19 | export interface IFunction extends INode { 20 | kind: 'function'; 21 | /** function parameters */ 22 | parameters: IParameter[]; 23 | /** function return value type */ 24 | return: TypeNode; 25 | } 26 | 27 | /** basic data type */ 28 | export interface IBaseType extends INode { 29 | kind: 'type'; 30 | type: TypeNode; 31 | } 32 | /** object property */ 33 | export interface IProp extends INode { 34 | kind: 'property'; 35 | /** property name */ 36 | id: string; 37 | /** property type */ 38 | type: ILiteralType | IFunction | IBaseType; 39 | /** JSDoc comments */ 40 | comments: string[]; 41 | /** whether to add question mark */ 42 | optional?: boolean; 43 | } 44 | /** `{ prop: Type, ... }` like type */ 45 | export interface ILiteralType extends INode { 46 | kind: 'literalType'; 47 | props: Record 48 | }; 49 | 50 | export const createLiteralType = (props: Record) => ({ 51 | kind: 'literalType', 52 | props, 53 | }); 54 | 55 | export interface IInterface extends INode { 56 | kind: 'interface'; 57 | props: Record 58 | id: string; 59 | modifiers: ModifierSyntaxKind[]; 60 | } 61 | 62 | export interface IImport extends INode { 63 | kind: 'import'; 64 | names: string[]; 65 | modulePath: string; 66 | } 67 | 68 | export type ParamData = string | TypeNode; 69 | -------------------------------------------------------------------------------- /src/ts/ast/utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | factory as f, 3 | SyntaxKind, 4 | TypeElement, 5 | setSyntheticLeadingComments, 6 | Node, 7 | } from "typescript"; 8 | import { typeNodes } from "../constants"; 9 | import { IFunction, IImport, IInterface, ILiteralType, IProp } from "./types"; 10 | 11 | export const questionToken = f.createToken(SyntaxKind.QuestionToken); 12 | /** IImport to AST node */ 13 | export const getImportNode = (node: IImport) => { 14 | const ids = node.names.map((mod) => 15 | f.createImportSpecifier(false, undefined, f.createIdentifier(mod)) 16 | ); 17 | return f.createImportDeclaration( 18 | undefined, 19 | undefined, 20 | f.createImportClause(true, undefined, f.createNamedImports(ids)), 21 | f.createStringLiteral(node.modulePath) 22 | ); 23 | }; 24 | 25 | export const getInlineJSDocString = (comments: string) => { 26 | return `* ${comments} `; 27 | }; 28 | 29 | /** add multiple inline JSDoc to AST node */ 30 | export const attachInlineJSDoc2Node = (node: Node, comments: string[]) => { 31 | setSyntheticLeadingComments( 32 | node, 33 | comments.map((comment) => ({ 34 | kind: SyntaxKind.MultiLineCommentTrivia, 35 | text: getInlineJSDocString(comment), 36 | hasTrailingNewLine: true, 37 | hasLeadingNewline: false, 38 | pos: -1, 39 | end: -1, 40 | })) 41 | ); 42 | }; 43 | 44 | export const getJSDocString = (comments: string[]) => { 45 | return `* 46 | ${comments.map((s) => " * " + s).join("\n")} 47 | `; 48 | }; 49 | 50 | export const attachJSDoc2Node = (node: Node, comments: string[]) => { 51 | setSyntheticLeadingComments(node, [ 52 | { 53 | kind: SyntaxKind.MultiLineCommentTrivia, 54 | text: getJSDocString(comments), 55 | hasTrailingNewLine: true, 56 | hasLeadingNewline: true, 57 | pos: -1, 58 | end: -1, 59 | }, 60 | ]); 61 | } 62 | /** properties of Record to AST nodes */ 63 | export const getProps = (props: Record) => { 64 | const nodes: TypeElement[] = []; 65 | for (const propName in props) { 66 | const prop = props[propName]; 67 | let propTypeNode = typeNodes.unknown(); 68 | switch (prop.type.kind) { 69 | case "function": 70 | propTypeNode = getFunction(prop.type); 71 | break; 72 | case "literalType": 73 | propTypeNode = getTypeLiteral(prop.type); 74 | break; 75 | case "type": 76 | propTypeNode = prop.type.type; 77 | break; 78 | default: 79 | } 80 | const node = f.createPropertySignature( 81 | undefined, 82 | f.createIdentifier(prop.id), 83 | prop.optional ? questionToken : undefined, 84 | propTypeNode 85 | ); 86 | if (prop.comments.length) { 87 | attachJSDoc2Node(node, prop.comments); 88 | } 89 | nodes.push(node); 90 | } 91 | return nodes; 92 | } 93 | 94 | /** IInterface to AST node */ 95 | export const getInterface = (node: IInterface) => { 96 | const props = getProps(node.props); 97 | return f.createInterfaceDeclaration( 98 | undefined, 99 | node.modifiers.map((modifier) => f.createModifier(modifier)), 100 | f.createIdentifier(node.id), 101 | undefined, 102 | undefined, 103 | props 104 | ); 105 | }; 106 | 107 | /** IFunction to AST node */ 108 | export const getFunction = (func: IFunction) => { 109 | return f.createFunctionTypeNode( 110 | undefined, 111 | func.parameters.map((param) => 112 | f.createParameterDeclaration( 113 | undefined, 114 | undefined, 115 | param.more ? f.createToken(SyntaxKind.DotDotDotToken) : undefined, 116 | f.createIdentifier(param.id), 117 | param.optional && !param.more ? questionToken : undefined, 118 | param.type 119 | ) 120 | ), 121 | func.return 122 | ); 123 | }; 124 | 125 | /** ILiteralType to AST node */ 126 | export const getTypeLiteral = (type: ILiteralType) => { 127 | return f.createTypeLiteralNode(getProps(type.props)); 128 | }; 129 | -------------------------------------------------------------------------------- /src/ts/constants.ts: -------------------------------------------------------------------------------- 1 | import { factory, Node, SyntaxKind } from "typescript"; 2 | import { ETSType, TypeNodeFactory } from "./types"; 3 | import { getRecordTypeNode } from "./utils"; 4 | import { attachInlineJSDoc2Node, getImportNode } from "./ast"; 5 | 6 | export const typeNodes: Record = { 7 | [ETSType.Number]: () => factory.createKeywordTypeNode(SyntaxKind.NumberKeyword), 8 | [ETSType.String]: () => factory.createKeywordTypeNode(SyntaxKind.StringKeyword), 9 | [ETSType.Boolean]: () => factory.createKeywordTypeNode(SyntaxKind.BooleanKeyword), 10 | [ETSType.Unknown]: () => factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword), 11 | [ETSType.Void]: () => factory.createKeywordTypeNode(SyntaxKind.VoidKeyword), 12 | [ETSType.Any]: () => factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), 13 | [ETSType.Function]: () => factory.createTypeReferenceNode(factory.createIdentifier('Function')), 14 | 15 | [ETSType.UnknownArray]: () => factory.createArrayTypeNode(typeNodes.unknown()), 16 | /** Record */ 17 | [ETSType.Record]: () => getRecordTypeNode(typeNodes.unknown()), 18 | }; 19 | 20 | export const headAstNodes: Node[] = [ 21 | getImportNode({ 22 | kind: "import", 23 | names: ["INvimFloatWinConfig"], 24 | modulePath: "./utils", 25 | }), 26 | ]; 27 | attachInlineJSDoc2Node(headAstNodes[0], [ 28 | "This is automatically generated file. Do not modify this file manually.", 29 | "@noResolution", 30 | "@noSelfInFile", 31 | ]); 32 | -------------------------------------------------------------------------------- /src/ts/index.ts: -------------------------------------------------------------------------------- 1 | /** TypeScript related */ 2 | export * from './types'; 3 | export * from './constants'; 4 | export * from './utils'; 5 | export * from './ast'; 6 | -------------------------------------------------------------------------------- /src/ts/types.ts: -------------------------------------------------------------------------------- 1 | import { TypeNode } from "typescript"; 2 | 3 | export type TypeNodeFactory = () => TypeNode; 4 | 5 | /** enum ts basic types */ 6 | export enum ETSType { 7 | Number = 'number', 8 | String = 'string', 9 | Boolean = 'boolean', 10 | Unknown = 'unknown', 11 | Void = 'void', 12 | Any = 'any', 13 | Function = 'function', 14 | Record = 'record', 15 | UnknownArray = 'unknownArray', 16 | } 17 | -------------------------------------------------------------------------------- /src/ts/utils.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { factory, TypeNode, Identifier } from "typescript"; 3 | import { isNumeric, writeFile } from "../utils"; 4 | import { typeNodes } from './constants'; 5 | import { ParamData } from "./ast"; 6 | 7 | /** complex config objects */ 8 | export const cfgTypes: Record = { 9 | float_config: factory.createIdentifier("INvimFloatWinConfig"), 10 | }; 11 | 12 | const recordId = factory.createIdentifier("Record"); 13 | /** return Node for `Record` */ 14 | export const getRecordTypeNode = (T: TypeNode) => { 15 | return factory.createTypeReferenceNode(recordId, [typeNodes.string(), T]); 16 | }; 17 | /** return predefined interface type node */ 18 | export const getInterfaceNode = (config: string) => { 19 | if (config in cfgTypes) { 20 | return factory.createTypeReferenceNode(cfgTypes[config]); 21 | } 22 | console.log(`unknown config ${config}`); 23 | return typeNodes.unknown(); 24 | }; 25 | export const getDictNode = (param: ParamData) => { 26 | if (typeof param === 'string') { 27 | return getInterfaceNode(param); 28 | } 29 | console.log('pass AST node to dict', param); 30 | return typeNodes.unknown(); 31 | } 32 | 33 | export const getArrayTypeNode = (type: TypeNode) => { 34 | return factory.createArrayTypeNode(type); 35 | }; 36 | 37 | /** 38 | * Process ParamData type, return TypeNode. 39 | * If param is string type, return unknown type 40 | */ 41 | export const checkParamTypeGuard = (param: ParamData): TypeNode => { 42 | if (typeof param === "string") { 43 | console.log(`get param type error: ${param}`); 44 | return typeNodes.unknown(); 45 | } 46 | return param as TypeNode; 47 | }; 48 | 49 | export const checkParamType = (convert: (type: TypeNode) => TypeNode) => { 50 | return (param: ParamData) => { 51 | return convert(checkParamTypeGuard(param)); 52 | }; 53 | }; 54 | 55 | export const getArrayTypeNodeWithValues = (...params: ParamData[]) => { 56 | params[0] = checkParamTypeGuard(params[0]); 57 | if (params.length === 2 && typeof params[1] === "string") { 58 | // array with specified length 59 | let paramCount = -1; 60 | paramCount = parseInt(params[1]); 61 | if (!isNumeric(params[1])) { 62 | console.log(`string param ${params[1]} is not a number`); 63 | paramCount = -1; 64 | } 65 | if (paramCount === -1) { 66 | // invalid number string 67 | return getArrayTypeNode(params[0]); 68 | } 69 | return factory.createTupleTypeNode(new Array(paramCount).fill(params[0])); 70 | } 71 | // normal tuple 72 | return factory.createTupleTypeNode( 73 | params.map((param) => checkParamTypeGuard(param)) 74 | ); 75 | }; 76 | 77 | /** output AST to TS file */ 78 | export const writeTSFile = (targetFilePath: string, nodes: ts.Node[]) => { 79 | const resultFile = ts.createSourceFile(targetFilePath, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS); 80 | const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); 81 | const content: string[] = []; 82 | for (const node of nodes) { 83 | content.push(printer.printNode(ts.EmitHint.Unspecified, node, resultFile)); 84 | } 85 | 86 | if (content.length) { 87 | writeFile(targetFilePath, content.join('\n')); 88 | } else { 89 | console.warn(`Empty content for ${targetFilePath}.`); 90 | } 91 | }; 92 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | // https://stackoverflow.com/questions/18082/validate-decimal-numbers-in-javascript-isnumeric 3 | export const isNumeric = (n: string) => { 4 | const val = Number.parseFloat(n); 5 | return !Number.isNaN(val) && Number.isFinite(val); 6 | }; 7 | 8 | export const writeFile = (fileName: string, content: string) => { 9 | try { 10 | fs.writeFileSync(fileName, content); 11 | } catch (err) { 12 | console.error("write error", err); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/vim/constants.ts: -------------------------------------------------------------------------------- 1 | import { TypeNode } from "typescript"; 2 | import * as fs from "fs"; 3 | import { IBuiltinDocs } from "./types"; 4 | import { 5 | typeNodes, 6 | getArrayTypeNode, 7 | checkParamType, 8 | getRecordTypeNode, 9 | getArrayTypeNodeWithValues, 10 | getDictNode, 11 | ETSType, 12 | ParamData, 13 | } from "../ts"; 14 | /** 15 | * The type mapping between neovim types(lower case) and TypeScript(Lua) type. 16 | */ 17 | export const NVIM_TYPE_MAP: Record TypeNode> = { 18 | number: typeNodes[ETSType.Number], 19 | integer: typeNodes[ETSType.Number], 20 | float: typeNodes[ETSType.Number], 21 | string: typeNodes[ETSType.String], 22 | boolean: typeNodes[ETSType.Boolean], 23 | array: typeNodes[ETSType.UnknownArray], 24 | list: typeNodes[ETSType.UnknownArray], 25 | dict: typeNodes[ETSType.Record], 26 | dictionary: typeNodes[ETSType.Record], 27 | object: typeNodes[ETSType.Record], 28 | buffer: typeNodes[ETSType.Number], 29 | window: typeNodes[ETSType.Number], 30 | tabpage: typeNodes[ETSType.Number], 31 | void: typeNodes[ETSType.Void], 32 | luaref: typeNodes[ETSType.Unknown], 33 | "": typeNodes[ETSType.Unknown], 34 | funcref: typeNodes[ETSType.Function], 35 | /** 36 | * A Blob mostly behaves like a |List| of numbers, 37 | * where each number has the value of an 8-bit byte, from 0 to 255. 38 | */ 39 | blob: () => getArrayTypeNode(typeNodes[ETSType.Number]()), 40 | 41 | any: typeNodes[ETSType.Any], 42 | // @TODO: check for these types 43 | error: typeNodes[ETSType.Unknown], 44 | }; 45 | 46 | interface IContainerMap { 47 | one: (param: ParamData) => TypeNode; 48 | multi?: (...param: ParamData[]) => TypeNode; 49 | } 50 | export const NVIM_CONTAINER_TYPE_MAP: Record = { 51 | Dict: { 52 | one: getDictNode, 53 | }, 54 | ArrayOf: { 55 | one: checkParamType(getArrayTypeNode), 56 | multi: getArrayTypeNodeWithValues, 57 | }, 58 | DictionaryOf: { one: checkParamType(getRecordTypeNode) }, 59 | }; 60 | 61 | export type NVIM_TYPE_MAP = typeof NVIM_TYPE_MAP; 62 | 63 | /** data in builtin-docs.json */ 64 | export const builtinData = JSON.parse( 65 | fs.readFileSync("./data/builtin-docs.json", { encoding: "utf-8" }) 66 | ) as IBuiltinDocs; 67 | -------------------------------------------------------------------------------- /src/vim/help-doc.ts: -------------------------------------------------------------------------------- 1 | // functions related to process vim help text file 2 | import * as fs from 'fs'; 3 | 4 | /** read vim help text file from `path` and divide lines into sections */ 5 | export const divideSections = (helpFilePath: string): string[][] => { 6 | const file = fs.readFileSync(helpFilePath, { encoding: 'utf-8' }); 7 | const sections: string[][] = []; 8 | const lines = file.split('\n'); 9 | // delete last vim mode line 10 | lines.pop(); 11 | const sectionDivReg = /^=+$/; 12 | let prevSection = 0; 13 | for (let i = 0; i < lines.length; i++) { 14 | if (sectionDivReg.test(lines[i])) { 15 | sections.push(lines.slice(prevSection, i - 1)); 16 | prevSection = i + 1; 17 | } 18 | } 19 | sections.push(lines.slice(prevSection, lines.length - 1)); 20 | return sections; 21 | } 22 | 23 | /** 24 | * @returns section index 25 | */ 26 | export const findSectionByHelpTag = (sections: string[][], helpTag: string): number => { 27 | const tagStr = `*${helpTag}*`; 28 | for (let i = 0; i < sections.length; i++) { 29 | if (sections[i][0].includes(tagStr)) { 30 | return i; 31 | } 32 | } 33 | return -1; 34 | }; 35 | -------------------------------------------------------------------------------- /src/vim/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './constants'; 3 | export * from './utils'; 4 | export * from './help-doc'; 5 | -------------------------------------------------------------------------------- /src/vim/types.ts: -------------------------------------------------------------------------------- 1 | // types for builtin-docs.json 2 | export interface IBaseCompletionItem { 3 | /** item name */ 4 | label: string; 5 | kind: number; 6 | sortText: string; 7 | insertText: string; 8 | insertTextFormat: number; 9 | } 10 | export interface IOptionItem extends IBaseCompletionItem { 11 | /** option variable type */ 12 | detail: string; 13 | /** empty string */ 14 | documentation: string; 15 | } 16 | 17 | export interface IBuiltinDocs { 18 | completionItems: { 19 | options: IOptionItem, 20 | variables: IBaseCompletionItem, 21 | }; 22 | documents: { 23 | functions: Record, 24 | options: Record, 25 | variables: Record, 26 | }; 27 | signatureHelp: Record; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/vim/utils.ts: -------------------------------------------------------------------------------- 1 | import { TypeNode } from "typescript"; 2 | import { isNumeric } from "../utils"; 3 | import { typeNodes, ParamData } from "../ts"; 4 | import { NVIM_TYPE_MAP, NVIM_CONTAINER_TYPE_MAP } from "./constants"; 5 | 6 | /** convert base types, may inclue string of integer */ 7 | export const convertTypeDirectly = ( 8 | type: string, 9 | allowUnknownString: boolean = false 10 | ): TypeNode | string => { 11 | type = type.toLowerCase(); 12 | if (!(type in NVIM_TYPE_MAP)) { 13 | if (isNumeric(type)) { 14 | // return string of number 15 | return type; 16 | } 17 | if (allowUnknownString) { 18 | return type; 19 | } 20 | console.log("Unknown type name detected: " + type); 21 | return typeNodes.unknown(); 22 | } 23 | return NVIM_TYPE_MAP[type](); 24 | }; 25 | 26 | /** process comma ',' separated parameters */ 27 | export const processParams = ( 28 | params: string, 29 | allowUnknownString: boolean = false 30 | ): ParamData[] => { 31 | if (params.includes(",")) { 32 | // multiple parameters 33 | const parameters = params.split(",").map((param) => param.trim()); 34 | return parameters.map((param) => convertTypeDirectly(param)); 35 | } else { 36 | // one parameter 37 | return [convertTypeDirectly(params, allowUnknownString)]; 38 | } 39 | }; 40 | 41 | /** Process generic type string */ 42 | export const extractTypeContainer = ( 43 | type: string 44 | ): { container: string | null; t: string } => { 45 | if (!type.endsWith(")")) { 46 | return { 47 | container: null, 48 | t: type, 49 | }; 50 | } else { 51 | const leftBracketIdx = type.indexOf("("); 52 | const container = type.slice(0, leftBracketIdx); 53 | const t = type.slice(leftBracketIdx + 1, type.length - 1).trim(); 54 | return { container, t }; 55 | } 56 | }; 57 | 58 | /** Convert vim types from neovim msgpack to TypeScript Type AST node */ 59 | export const convertType = ( 60 | vimType: string 61 | ): { type: TypeNode; optional: boolean } => { 62 | if (vimType.length === 0) { 63 | return { 64 | type: typeNodes.unknown(), 65 | optional: false, 66 | }; 67 | } 68 | let type = vimType.slice(); 69 | let optional = false; 70 | if (type.endsWith("*")) { 71 | // remove ending star '*' 72 | // star indicates the parameter is optional? @TODO: verify this 73 | optional = true; 74 | type = type.slice(0, type.length - 1).trim(); 75 | } 76 | const result = extractTypeContainer(type); 77 | let finalType: TypeNode = typeNodes.unknown(); 78 | if (result.container) { 79 | // container exists 80 | if (result.container in NVIM_CONTAINER_TYPE_MAP) { 81 | // container matches from map 82 | const params = processParams(result.t, result.container === "Dict"); 83 | const typeMapVal = NVIM_CONTAINER_TYPE_MAP[result.container]; 84 | if (params.length > 1) { 85 | // multiple parameters 86 | if (typeMapVal.multi) { 87 | finalType = typeMapVal.multi(...params); 88 | } else { 89 | // not set for multi parameters 90 | const message = `multi parameter for container ${result.container}: ${result.t}`; 91 | console.log(message); 92 | } 93 | } else { 94 | // only one parameter 95 | finalType = typeMapVal.one(params[0]); 96 | } 97 | } else { 98 | // container not found 99 | const message = `container ${result.container} not found`; 100 | console.log(message); 101 | } 102 | } else { 103 | const temp = convertTypeDirectly(type); 104 | if (typeof temp === "string") { 105 | console.error(`invalid type ${temp} from ${type}`); 106 | } else { 107 | finalType = temp; 108 | } 109 | } 110 | return { 111 | type: finalType!, 112 | optional, 113 | }; 114 | }; 115 | 116 | /** whether this method is from an object property */ 117 | export const checkFunctionObjectProperty = (fname: string): boolean => { 118 | return fname.includes(":"); 119 | }; 120 | 121 | export const processDocLines = (docs: string[]) => { 122 | const comments: string[] = []; 123 | for (let doc of docs) { 124 | // prevent '*/' ends multiline comments 125 | doc = doc.replace(/\*\//g, "*\\/"); 126 | comments.push(...doc.split("\n")); 127 | } 128 | return comments; 129 | }; 130 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "allowJs": true, 5 | "target": "esnext", 6 | "lib": ["esnext", "ES2021", "DOM"], 7 | "module": "CommonJS", 8 | "moduleResolution": "node", 9 | "types": ["node"], 10 | "outDir": "dist", 11 | "baseUrl": "./src", 12 | "rootDir": "./src", 13 | "paths": { 14 | "@/*": ["src/*"] 15 | } 16 | }, 17 | "include": [ 18 | "src/**/*.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /types/.gitignore: -------------------------------------------------------------------------------- 1 | api.d.ts 2 | lua.d.ts 3 | lsp.d.ts 4 | treesitter.d.ts 5 | diagnostic.d.ts 6 | fn.d.ts 7 | option.d.ts 8 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // vim: nomodeline: 3 | // about vim modeline: https://vim.fandom.com/wiki/Modeline_magic 4 | 5 | import { Api } from './api.d'; 6 | import { Lua } from './lua.d'; 7 | import { Lsp } from './lsp.d'; 8 | import { Treesitter } from './treesitter.d'; 9 | import { Diagnostic } from './diagnostic.d'; 10 | import { Fn } from './fn.d'; 11 | import { Option, Window, Global, Buffer } from './option.d'; 12 | 13 | declare global { 14 | /** `lua-builtin` */ 15 | interface IVim extends Lua { 16 | /** 17 | * Invokes Nvim |API| function {func} with arguments {...}. 18 | * Example: call the "nvim_get_current_line()" API function: > 19 | * print(tostring(vim.api.nvim_get_current_line())) 20 | */ 21 | api: Api; 22 | lsp: Lsp; 23 | diagnostic: Diagnostic; 24 | treesitter: Treesitter; 25 | fn: Fn; 26 | opt: Option; 27 | go: Global; 28 | bo: Buffer; 29 | wo: Window; 30 | /** 31 | * Global (|g:|) editor variables. 32 | * Key with no value returns `nil`. 33 | */ 34 | g: LuaTable; 35 | /** 36 | * Buffer-scoped (|b:|) variables for the current buffer. 37 | * Invalid or unset key returns `nil`. Can be indexed with 38 | * an integer to access variables for a specific buffer. 39 | */ 40 | b: LuaTable; 41 | /** 42 | * Window-scoped (|w:|) variables for the current window. 43 | * Invalid or unset key returns `nil`. Can be indexed with 44 | * an integer to access variables for a specific window. 45 | */ 46 | w: LuaTable; 47 | /** 48 | * Tabpage-scoped (|t:|) variables for the current tabpage. 49 | * Invalid or unset key returns `nil`. Can be indexed with 50 | * an integer to access variables for a specific tabpage. 51 | */ 52 | t: LuaTable; 53 | /** 54 | * |v:| variables. 55 | * Invalid or unset key returns `nil`. 56 | */ 57 | v: LuaTable; 58 | /** 59 | * Environment variables defined in the editor session. 60 | * See |expand-env| and |:let-environment| for the Vimscript behavior. 61 | * Invalid or unset key returns `nil`. 62 | * Example: > 63 | * vim.env.FOO = 'bar' 64 | * print(vim.env.TERM) 65 | */ 66 | env: LuaTable; 67 | 68 | /** 69 | * Gets the version of the current Nvim build. 70 | */ 71 | version: () => LuaTable; 72 | /** 73 | * Returns true if the code is executing as part of a "fast" event 74 | * handler, where most of the API is disabled. These are low-level events 75 | * (e.g. |lua-loop-callbacks|) which can be invoked whenever Nvim polls 76 | * for input. When this is `false` most API functions are callable (but 77 | * may be subject to other restrictions such as |textlock|). 78 | */ 79 | in_fast_event: () => boolean; 80 | /** 81 | * Special value representing NIL in |RPC| and |v:null| in Vimscript 82 | * conversion, and similar cases. Lua `nil` cannot be used as part of 83 | * a Lua table representing a Dictionary or Array, because it is 84 | * treated as missing: `{"foo", nil}` is the same as `{"foo"}`. 85 | */ 86 | NIL: null; 87 | /** 88 | * Creates a special empty table (marked with a metatable), which Nvim 89 | * converts to an empty dictionary when translating Lua values to 90 | * Vimscript or API types. Nvim by default converts an empty table `{}` 91 | * without this metatable to an list/array. 92 | 93 | * Note: if numeric keys are present in the table, Nvim ignores the 94 | * metatable marker and converts the dict to a list/array anyway. 95 | */ 96 | empty_dict: () => LuaTable; 97 | /** 98 | * Log levels are one of the values defined in `vim.log.levels`: 99 | */ 100 | log: { 101 | levels: { 102 | TRACE: 0; 103 | DEBUG: 1; 104 | INFO: 2; 105 | WARN: 3; 106 | ERROR: 4; 107 | }; 108 | }; 109 | } 110 | const vim: IVim; 111 | } 112 | -------------------------------------------------------------------------------- /types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "esnext", 5 | "lib": ["esnext"], 6 | "module": "CommonJS", 7 | "moduleResolution": "node", 8 | "types": ["typescript-to-lua/language-extensions", "lua-types/jit"], 9 | "outDir": "lua", 10 | "baseUrl": ".", 11 | "rootDir": "." 12 | }, 13 | "tstl": { 14 | "noImplicitSelf": true, 15 | "luaTarget": "JIT" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/utils.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Style of (optional) window border. 3 | * 4 | * This can either be a string or an array. The string values are 5 | * * "none": No border (default). 6 | * * "single": A single line box. 7 | * * "double": A double line box. 8 | * * "rounded": Like "single", but with rounded corners ("╭" etc.). 9 | * * "solid": Adds padding by a single whitespace cell. 10 | * * "shadow": A drop shadow effect by blending with the background. 11 | */ 12 | export type NvimFloatWinBorderString = 13 | | "none" 14 | | "single" 15 | | "double" 16 | | "rounded" 17 | | "solid" 18 | | "shadow"; 19 | /** 20 | * Style of (optional) window border. 21 | * 22 | * This can either be a string or an array. 23 | * * If it is an array, it should have a length of eight or any divisor of eight. The array will specifify the eight chars building up the border in a clockwise fashion starting with the top-left corner. 24 | * As an example, the double box style could be specified as [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ]. If the number of chars are less than eight, they will be repeated. Thus an ASCII border could be specified as [ "/", "-", "\\", "|" ], or all chars the same as [ "x" ]. 25 | * An empty string can be used to turn off a specific border, for instance, [ "", "", "", ">", "", "", "", "<" ] will only make vertical borders but not horizontal ones. 26 | * By default, `FloatBorder` highlight is used, which links to `WinSeparator` when not defined. It could also be specified by character: [ {"+", "MyCorner"}, {"x", "MyBorder"} ]. 27 | */ 28 | export type NvimFloatWinBorderArray = string[]; 29 | /** 30 | * Style of (optional) window border. 31 | * 32 | * This can either be a string or an array. 33 | */ 34 | export type NvimFloatWinBorder = 35 | | NvimFloatWinBorderString 36 | | NvimFloatWinBorderArray; 37 | 38 | /** 39 | * config parameter type for *nvim_open_win()* API 40 | * 41 | * documentation from neovim help |nvim_open_win()| 42 | */ 43 | export declare interface INvimFloatWinConfig { 44 | /** 45 | * Sets the window layout to "floating", placed at (row,col) coordinates 46 | * relative to: 47 | * * "editor" The global editor grid 48 | * * "win" Window given by the `win` field, or current window. 49 | * * "cursor" Cursor position in current window. 50 | */ 51 | relative: "editor" | "win" | "cursor"; 52 | /** 53 | * |window-ID| for relative="win". 54 | */ 55 | win: number; 56 | /** 57 | * Decides which corner of the float to place at (row,col): 58 | * * "NW" northwest (default) 59 | * * "NE" northeast 60 | * * "SW" southwest 61 | * * "SE" southeast 62 | */ 63 | anchor: "NW" | "NE" | "SW" | "SE"; 64 | /** 65 | * Window width (in character cells). Minimum of 1. 66 | */ 67 | width: number; 68 | /** 69 | * Window height (in character cells). Minimum of 1. 70 | */ 71 | height: number; 72 | /** 73 | * Places float relative to buffer text (only when relative="win"). Takes a tuple of zero-indexed [line, column]. `row` and `col` if given are applied relative to this position, else they default to: 74 | * * `row=1` and `col=0` if `anchor` is "NW" or "NE" 75 | * * `row=0` and `col=0` if `anchor` is "SW" or "SE" (thus like a tooltip near the buffer text). 76 | */ 77 | bufpos: [number, number]; 78 | /** 79 | * Row position in units of "screen cell height", may be fractional. 80 | */ 81 | row: number; 82 | /** 83 | * Row position in units of "screen cell height", may be fractional. 84 | */ 85 | col: number; 86 | /** 87 | * Enable focus by user actions (wincmds, mouse events). Defaults to true. 88 | * Non-focusable windows can be entered by |nvim_set_current_win()|. 89 | */ 90 | focusable: boolean; 91 | /** 92 | * GUI should display the window as an external top-level window. Currently accepts no other positioning configuration together with this. 93 | */ 94 | external: any; 95 | /** 96 | * Stacking order. floats with higher `zindex` go on top on floats with lower indices. Must 97 | * be larger than zero. The following screen elements have hard-coded z-indices: 98 | * * 100: insert completion popupmenu 99 | * * 200: message scrollback 100 | * * 250: cmdline completion popupmenu (when wildoptions+=pum) The default value for floats are 50. In general, values below 100 are recommended, unless there is a good reason to overshadow builtin elements. 101 | */ 102 | zindex: number; 103 | /** 104 | * Configure the appearance of the window. 105 | * 106 | * Currently only takes one non-empty value: 107 | * * "minimal" Nvim will display the window with 108 | * many UI options disabled. This is useful 109 | * when displaying a temporary float where the 110 | * text should not be edited. Disables 111 | * 'number', 'relativenumber', 'cursorline', 112 | * 'cursorcolumn', 'foldcolumn', 'spell' and 113 | * 'list' options. 'signcolumn' is changed to 114 | * `auto` and 'colorcolumn' is cleared. The 115 | * end-of-buffer region is hidden by setting 116 | * `eob` flag of 'fillchars' to a space char, 117 | * and clearing the |EndOfBuffer| region in 118 | * 'winhighlight'. 119 | */ 120 | style: "minimal"; 121 | /** 122 | * Style of (optional) window border. 123 | * 124 | * This can either be a string or an array. The 125 | * string values are 126 | * * "none": No border (default). 127 | * * "single": A single line box. 128 | * * "double": A double line box. 129 | * * "rounded": Like "single", but with rounded 130 | * corners ("╭" etc.). 131 | * * "solid": Adds padding by a single whitespace 132 | * cell. 133 | * * "shadow": A drop shadow effect by blending 134 | * with the background. 135 | * * If it is an array, it should have a length 136 | * of eight or any divisor of eight. The array 137 | * will specifify the eight chars building up 138 | * the border in a clockwise fashion starting 139 | * with the top-left corner. As an example, the 140 | * double box style could be specified as [ 141 | * "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ]. If 142 | * the number of chars are less than eight, 143 | * they will be repeated. Thus an ASCII border 144 | * could be specified as [ "/", "-", "\\", "|" 145 | * ], or all chars the same as [ "x" ]. An 146 | * empty string can be used to turn off a 147 | * specific border, for instance, [ "", "", "", 148 | * ">", "", "", "", "<" ] will only make 149 | * vertical borders but not horizontal ones. By 150 | * default, `FloatBorder` highlight is used, 151 | * which links to `WinSeparator` when not 152 | * defined. It could also be specified by 153 | * character: [ {"+", "MyCorner"}, {"x", 154 | * "MyBorder"} ]. 155 | */ 156 | border?: NvimFloatWinBorder; 157 | /** 158 | * If true then no buffer-related autocommand events such as |BufEnter|, 159 | * |BufLeave| or |BufWinEnter| may fire from calling this function. 160 | */ 161 | noautocmd?: boolean; 162 | } 163 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@msgpack/msgpack@^2.7.2": 6 | version "2.7.2" 7 | resolved "https://registry.npmmirror.com/@msgpack/msgpack/-/msgpack-2.7.2.tgz#f34b8aa0c49f0dd55eb7eba577081299cbf3f90b" 8 | integrity sha512-rYEi46+gIzufyYUAoHDnRzkWGxajpD9vVXFQ3g1vbjrBm6P7MBmm+s/fqPa46sxa+8FOUdEuRQKaugo5a4JWpw== 9 | 10 | "@types/node@^18.0.3": 11 | version "18.0.3" 12 | resolved "https://registry.npmmirror.com/@types/node/-/node-18.0.3.tgz#463fc47f13ec0688a33aec75d078a0541a447199" 13 | integrity sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ== 14 | 15 | better-nearley-railroad@^1.3.0: 16 | version "1.3.0" 17 | resolved "https://registry.npmmirror.com/better-nearley-railroad/-/better-nearley-railroad-1.3.0.tgz#18a47e4330338d19304c9dbaa0b4e0af23b227c6" 18 | integrity sha512-jvuU73X3Lbvnkc+adCNDtmWp36k3xTe632F4jtOd52mC8Kt56/itX9uCS7xLRrrfs6U2D8p5K/bmdQ919HLKmA== 19 | dependencies: 20 | commander "^6.0.0" 21 | mustache "^4.0.1" 22 | nearley "^2.19.5" 23 | railroad-diagrams "^1.0.0" 24 | 25 | commander@^2.19.0: 26 | version "2.20.3" 27 | resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" 28 | integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== 29 | 30 | commander@^6.0.0: 31 | version "6.2.1" 32 | resolved "https://registry.npmmirror.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" 33 | integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== 34 | 35 | discontinuous-range@1.0.0: 36 | version "1.0.0" 37 | resolved "https://registry.npmmirror.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" 38 | integrity sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ== 39 | 40 | enhanced-resolve@^5.8.2: 41 | version "5.10.0" 42 | resolved "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" 43 | integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== 44 | dependencies: 45 | graceful-fs "^4.2.4" 46 | tapable "^2.2.0" 47 | 48 | function-bind@^1.1.1: 49 | version "1.1.1" 50 | resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 51 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 52 | 53 | get-caller-file@^1.0.2: 54 | version "1.0.3" 55 | resolved "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" 56 | integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== 57 | 58 | global-dirs@^0.1.1: 59 | version "0.1.1" 60 | resolved "https://registry.npmmirror.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" 61 | integrity sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg== 62 | dependencies: 63 | ini "^1.3.4" 64 | 65 | graceful-fs@^4.2.4: 66 | version "4.2.10" 67 | resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" 68 | integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== 69 | 70 | has@^1.0.3: 71 | version "1.0.3" 72 | resolved "https://registry.npmmirror.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 73 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 74 | dependencies: 75 | function-bind "^1.1.1" 76 | 77 | ini@^1.3.4: 78 | version "1.3.8" 79 | resolved "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" 80 | integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== 81 | 82 | is-core-module@^2.9.0: 83 | version "2.9.0" 84 | resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" 85 | integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== 86 | dependencies: 87 | has "^1.0.3" 88 | 89 | lua-types@^2.11.0: 90 | version "2.11.0" 91 | resolved "https://registry.npmmirror.com/lua-types/-/lua-types-2.11.0.tgz#f4ecdc1eb9914a8af996f4aeb51a0e32284e5058" 92 | integrity sha512-J/8qHrOjkZuPBDhnuAg0DWoaJEImqXNzgEnUigCTVrSVCcchM5Y5zpcI3aWAZPhuZlb0QKC/f3nc7VGmZmBg7w== 93 | 94 | mock-require@^3.0.3: 95 | version "3.0.3" 96 | resolved "https://registry.npmmirror.com/mock-require/-/mock-require-3.0.3.tgz#ccd544d9eae81dd576b3f219f69ec867318a1946" 97 | integrity sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg== 98 | dependencies: 99 | get-caller-file "^1.0.2" 100 | normalize-path "^2.1.1" 101 | 102 | moo@^0.5.0: 103 | version "0.5.1" 104 | resolved "https://registry.npmmirror.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4" 105 | integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w== 106 | 107 | mustache@^4.0.1: 108 | version "4.2.0" 109 | resolved "https://registry.npmmirror.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" 110 | integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== 111 | 112 | nearley@^2.19.5, nearley@^2.20.1: 113 | version "2.20.1" 114 | resolved "https://registry.npmmirror.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474" 115 | integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== 116 | dependencies: 117 | commander "^2.19.0" 118 | moo "^0.5.0" 119 | railroad-diagrams "^1.0.0" 120 | randexp "0.4.6" 121 | 122 | normalize-path@^2.1.1: 123 | version "2.1.1" 124 | resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" 125 | integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== 126 | dependencies: 127 | remove-trailing-separator "^1.0.1" 128 | 129 | path-parse@^1.0.7: 130 | version "1.0.7" 131 | resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 132 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 133 | 134 | railroad-diagrams@^1.0.0: 135 | version "1.0.0" 136 | resolved "https://registry.npmmirror.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" 137 | integrity sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A== 138 | 139 | randexp@0.4.6: 140 | version "0.4.6" 141 | resolved "https://registry.npmmirror.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" 142 | integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== 143 | dependencies: 144 | discontinuous-range "1.0.0" 145 | ret "~0.1.10" 146 | 147 | remove-trailing-separator@^1.0.1: 148 | version "1.1.0" 149 | resolved "https://registry.npmmirror.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" 150 | integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== 151 | 152 | resolve-from@^5.0.0: 153 | version "5.0.0" 154 | resolved "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" 155 | integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== 156 | 157 | resolve-global@^1.0.0: 158 | version "1.0.0" 159 | resolved "https://registry.npmmirror.com/resolve-global/-/resolve-global-1.0.0.tgz#a2a79df4af2ca3f49bf77ef9ddacd322dad19255" 160 | integrity sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw== 161 | dependencies: 162 | global-dirs "^0.1.1" 163 | 164 | resolve@^1.15.1: 165 | version "1.22.1" 166 | resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 167 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 168 | dependencies: 169 | is-core-module "^2.9.0" 170 | path-parse "^1.0.7" 171 | supports-preserve-symlinks-flag "^1.0.0" 172 | 173 | ret@~0.1.10: 174 | version "0.1.15" 175 | resolved "https://registry.npmmirror.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" 176 | integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== 177 | 178 | source-map@^0.7.3: 179 | version "0.7.4" 180 | resolved "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" 181 | integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== 182 | 183 | supports-preserve-symlinks-flag@^1.0.0: 184 | version "1.0.0" 185 | resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 186 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 187 | 188 | tapable@^2.2.0: 189 | version "2.2.1" 190 | resolved "https://registry.npmmirror.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" 191 | integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== 192 | 193 | tsc@^2.0.4: 194 | version "2.0.4" 195 | resolved "https://registry.npmmirror.com/tsc/-/tsc-2.0.4.tgz#5f6499146abea5dca4420b451fa4f2f9345238f5" 196 | integrity sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q== 197 | 198 | tslib@^1.14.1: 199 | version "1.14.1" 200 | resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" 201 | integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== 202 | 203 | typescript-to-lua@^0.36.0: 204 | version "0.36.1" 205 | resolved "https://registry.npmmirror.com/typescript-to-lua/-/typescript-to-lua-0.36.1.tgz#d9ce5e7ba4ac52fe654db498d59bd31158e650fc" 206 | integrity sha512-Ib1QAJGKizf8BBOJeiy2HOx2jJ1HvqODEUurBCHl8wg/5JwpMAUesSm8vqPcUovhJo6uOpbmpKIFvLK0aDNNIA== 207 | dependencies: 208 | resolve "^1.15.1" 209 | source-map "^0.7.3" 210 | typescript ">=4.0.2" 211 | 212 | typescript-to-lua@^1.6.0: 213 | version "1.6.3" 214 | resolved "https://registry.npmmirror.com/typescript-to-lua/-/typescript-to-lua-1.6.3.tgz#a4afdabf6dba3d51bea12100c827202401dc37cf" 215 | integrity sha512-yfXxGjOwX6453wSgEB37lLdxZZ8m+LH1g3PfebNfKaZHGGtbm9owxE6oRKB3sWcy2CICkfobAjWf3VdhKnEETg== 216 | dependencies: 217 | enhanced-resolve "^5.8.2" 218 | resolve "^1.15.1" 219 | source-map "^0.7.3" 220 | 221 | typescript-tstl-plugin@^0.3.2: 222 | version "0.3.2" 223 | resolved "https://registry.npmmirror.com/typescript-tstl-plugin/-/typescript-tstl-plugin-0.3.2.tgz#15ce191d21e56e4ea866470a91c85103884db41a" 224 | integrity sha512-KIz/Xl5AjDyMLPmYW8Bvco3KHJrKuXww4BlplYQ+piHR4wKyxB9th8d//0tkR3Qypjzyqt+QNgek0Gp26HV+Iw== 225 | dependencies: 226 | mock-require "^3.0.3" 227 | resolve-from "^5.0.0" 228 | resolve-global "^1.0.0" 229 | tslib "^1.14.1" 230 | typescript-to-lua "^0.36.0" 231 | 232 | typescript@>=4.0.2, typescript@^4.7.4: 233 | version "4.7.4" 234 | resolved "https://registry.npmmirror.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" 235 | integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== 236 | --------------------------------------------------------------------------------