-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #47 from codefori/feature/generated_docs
Feature/generated docs
- Loading branch information
Showing
8 changed files
with
1,093 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
import { ClassDeclaration, DefaultDeclaration, File, FunctionDeclaration, InterfaceDeclaration, TypescriptParser, VariableDeclaration } from "typescript-parser"; | ||
import type {Declaration} from "typescript-parser"; | ||
import path from "path"; | ||
import { stat, writeFile, readFile } from "fs/promises"; | ||
|
||
const exists = (path: string): Promise<boolean> => { | ||
return new Promise((resolve) => { | ||
stat(path).then(() => resolve(true)).catch(() => resolve(false)); | ||
}); | ||
} | ||
|
||
const htmlEscape = (str: string): string => { | ||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); | ||
} | ||
|
||
const linkType = (type: string|undefined): string => { | ||
if (!type) { | ||
return 'void'; | ||
} | ||
|
||
type = type.trim(); | ||
|
||
if (type.includes(`\n`)) { | ||
type = type.split(`\n`).map(t => t.trim()).join(` `); | ||
} | ||
|
||
const jsTypes = ['string', 'number', 'boolean', 'object', 'any', 'void', `Date`, `Function`, `undefined`, `Uint8Array`]; | ||
if (jsTypes.includes(type)) { | ||
return type; | ||
} | ||
|
||
if (type.includes(`.`) || type.includes(`=>`) || type.includes(`"`) || type.includes(`{`)) { | ||
return htmlEscape(type); | ||
} | ||
|
||
if (type.startsWith(`Promise<`)) { | ||
return `Promise<${linkType(type.slice(8, -1))}>`; | ||
} | ||
|
||
if (type.startsWith(`Array<`)) { | ||
return `Array<${linkType(type.slice(6, -1))}>`; | ||
} | ||
|
||
if (type.startsWith(`Partial<`)) { | ||
return `Partial<${linkType(type.slice(8, -1))}>`; | ||
} | ||
|
||
if (type.endsWith(`<T>`)) { | ||
return `${linkType(type.slice(0, -3))}<T>`; | ||
} | ||
|
||
if (type.endsWith(`[]`)) { | ||
return `${linkType(type.slice(0, -2))}[]`; | ||
} | ||
|
||
if (type.includes(`|`)) { | ||
return type.split(`|`).map(t => linkType(t)).join(` | `); | ||
} | ||
|
||
return `<a href="#${type.toLowerCase()}">${htmlEscape(type)}</a>`; | ||
} | ||
|
||
const PRIVATE = 0; | ||
let parsedFiles: string[] = []; | ||
let sections: { [key: string]: string } = {}; | ||
const parser = new TypescriptParser(); | ||
|
||
// or a filepath | ||
const HEADER_MDX = 'src/api/header.mdx'; | ||
const WORKSPACE = 'node_modules/@halcyontech/vscode-ibmi-types/'; | ||
|
||
const parseFile = async (tsPath: string) => { | ||
if (parsedFiles.includes(tsPath)) { | ||
return []; | ||
} | ||
|
||
console.log(`Parsing ${tsPath}`); | ||
|
||
const parsed = await parser.parseFile(tsPath, WORKSPACE); | ||
|
||
parsedFiles.push(tsPath); | ||
|
||
const baseName = path.basename(tsPath, '.d.ts'); | ||
console.log(baseName); | ||
|
||
let interfaces: string[] = []; | ||
let functions: string[] = []; | ||
let classes: string[] = []; | ||
let variables: string[] = []; | ||
|
||
function handleDeclatation(symbol: Declaration) { | ||
if (symbol instanceof InterfaceDeclaration) { | ||
interfaces.push(`### ${symbol.name}`, ``, `#### Properties`, ``); | ||
for (const property of symbol.properties) { | ||
interfaces.push(`- ${property.name}${property.isOptional ? `?` : ``}: ${linkType(property.type)}`); | ||
} | ||
interfaces.push(``); | ||
|
||
} else if (symbol instanceof ClassDeclaration) { | ||
classes.push(`### ${symbol.name}`, ``); | ||
|
||
const staticProps = symbol.properties.filter(p => p.isStatic && p.visibility !== PRIVATE); | ||
const staticMethods = symbol.methods.filter(m => m.isStatic && m.visibility !== PRIVATE); | ||
|
||
if (staticProps.length > 0 || staticMethods.length > 0) { | ||
classes.push(`#### Static`, ``); | ||
for (const prop of staticProps) { | ||
classes.push(`- ${prop.name}: ${linkType(prop.type)}`); | ||
} | ||
for (const prop of staticMethods) { | ||
classes.push(`- ${prop.isAsync ? `async ` : ``}**${prop.name}**(${prop.parameters.map(p => `${p.name}: ${linkType(p.type)}`).join(', ')}): ${linkType(prop.type)}`); | ||
} | ||
classes.push(``); | ||
} | ||
|
||
const ctor = symbol.ctor; | ||
|
||
if (ctor) { | ||
classes.push(`#### Constructor`, ``); | ||
classes.push(`- ${symbol.name}(${ctor.parameters.map(p => `${p.name}: ${linkType(p.type)}`).join(', ')}): ${symbol.name}`); | ||
classes.push(``); | ||
} | ||
|
||
const instanceProps = symbol.properties.filter(p => !p.isStatic && p.visibility !== PRIVATE); | ||
const instanceMethods = symbol.methods.filter(m => !m.isStatic && m.visibility !== PRIVATE); | ||
|
||
if (instanceProps.length > 0) { | ||
classes.push(`#### Properties`, ``); | ||
for (const prop of instanceProps) { | ||
classes.push(`* ${prop.name}${prop.isOptional ? `?` : ``}: ${linkType(prop.type)}`); | ||
} | ||
classes.push(``); | ||
} | ||
|
||
if (instanceMethods.length > 0) { | ||
classes.push(`#### Methods`, ``); | ||
for (const prop of instanceMethods) { | ||
classes.push(`* ${prop.isAsync ? `async ` : ``}**${prop.name}**(${prop.parameters.map(p => `${p.name}: ${linkType(p.type)}`).join(', ')}): ${prop.type}`); | ||
} | ||
classes.push(``); | ||
} | ||
|
||
} else if (symbol instanceof FunctionDeclaration) { | ||
functions.push(`### ${symbol.name}()`, ``); | ||
functions.push(`- ${symbol.name}(${symbol.parameters.map(p => `${p.name}: ${linkType(p.type)}`).join(', ')}): ${linkType(symbol.type)}`); | ||
functions.push(``); | ||
|
||
} else if (symbol instanceof VariableDeclaration) { | ||
variables.push(`- ${symbol.name}: ${linkType(symbol.type)}`); | ||
|
||
} else if (symbol instanceof DefaultDeclaration) { | ||
|
||
} else { | ||
console.log(symbol); | ||
} | ||
} | ||
|
||
if (parsed.declarations.length > 0) { | ||
for (const variable of parsed.declarations) { | ||
handleDeclatation(variable); | ||
} | ||
|
||
if (variables.length > 0) { | ||
variables = [`### Variables`, ``, ...variables, ``]; | ||
} | ||
|
||
if (classes.length > 0 || interfaces.length > 0 || functions.length > 0 || variables.length > 0) { | ||
sections[baseName] = [...classes, ...functions, ...variables, ...interfaces, ``].join('\n'); | ||
} | ||
} | ||
|
||
const imports = parsed.imports; | ||
let subLines: string[] = []; | ||
for (const importDetail of imports) { | ||
if (importDetail.libraryName.startsWith('.')) { | ||
const dPath = path.join(parsed.filePath, `..`, importDetail.libraryName + `.d.ts`); | ||
if (await exists(dPath)) { | ||
// console.log(`Found ${dPath}`); | ||
await parseFile(dPath); | ||
} | ||
} | ||
} | ||
} | ||
|
||
await parseFile('node_modules/@halcyontech/vscode-ibmi-types/typings.d.ts'); | ||
|
||
const topMost = [`Instance`, `IBMi`, `IBMiContent`, `component`, `manager`, `Filter`]; | ||
const hidden = [`CustomUI`, `Storage`, `Configuration`]; | ||
|
||
let allLines: string[] = []; | ||
|
||
if (await exists(HEADER_MDX)) { | ||
const header = await readFile(HEADER_MDX, 'utf8'); | ||
allLines.push(header, ``, `---`, ``); | ||
} | ||
|
||
for (const file of topMost) { | ||
if (sections[file]) { | ||
allLines.push(`## ${file}`, sections[file]); | ||
} | ||
} | ||
|
||
for (const file of Object.keys(sections)) { | ||
if (!topMost.includes(file) && !hidden.includes(file)) { | ||
allLines.push(`## ${file}`, sections[file], ``, `---`, ``); | ||
} | ||
} | ||
|
||
Object.keys(sections).map(k => sections[k]).flat(); | ||
|
||
console.log(Object.keys(sections)); | ||
|
||
await writeFile('src/content/docs/dev/api.md', allLines.join('\n')); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
--- | ||
title: API | ||
sidebar: | ||
order: 3 | ||
--- | ||
|
||
It is possible to write VS Code extensions that are based on Code for IBM i. That means your extension can use the connection that the user creates in your extension. This is not an extension tutorial, but an intro on how to access the APIs available within Code for IBM i. | ||
|
||
For example, you might be a vendor that produces lists or HTML that you'd like to be accessible from within Visual Studio Code. | ||
|
||
# Exports | ||
|
||
As well as the basic VS Code command API, you can get access to the Code for IBM i API with the VS Code `getExtension` API. | ||
|
||
```ts | ||
const { instance } = vscode.extensions.getExtension(`halcyontechltd.code-for-ibmi`).exports; | ||
``` | ||
|
||
## Typings | ||
|
||
We provide TS type definitions to make using the Code for IBM i API easier. They can be installed via `npm`: | ||
|
||
```bash title="terminal" | ||
npm i @halcyontech/vscode-ibmi-types | ||
``` | ||
|
||
It can then be imported and used in combination with `getExtension`: | ||
|
||
```ts | ||
import type { CodeForIBMi } from '@halcyontech/vscode-ibmi-types'; | ||
|
||
//... | ||
|
||
const ext = vscode.extensions.getExtension<CodeForIBMi>('halcyontechltd.code-for-ibmi'); | ||
``` | ||
|
||
|
||
**As Code for IBM i updates, the API may change. It is recommended you always keep the types packaged updated as the extension updates, incase the API interfaces change. We plan to make the VS Code command API interfaces stable so they will not break as often after they have been released.** | ||
|
||
## Example import | ||
|
||
This example can be used as a simple way to access the Code for IBM i instance. | ||
|
||
```ts | ||
import { CodeForIBMi } from "@halcyontech/vscode-ibmi-types"; | ||
import Instance from "@halcyontech/vscode-ibmi-types/api/Instance"; | ||
import { Extension, extensions } from "vscode"; | ||
|
||
let baseExtension: Extension<CodeForIBMi>|undefined; | ||
|
||
/** | ||
* This should be used on your extension activation. | ||
*/ | ||
export function loadBase(): CodeForIBMi|undefined { | ||
if (!baseExtension) { | ||
baseExtension = (extensions ? extensions.getExtension(`halcyontechltd.code-for-ibmi`) : undefined); | ||
} | ||
|
||
return (baseExtension && baseExtension.isActive && baseExtension.exports ? baseExtension.exports : undefined); | ||
} | ||
|
||
/** | ||
* Used when you want to fetch the extension 'instance' (the connection) | ||
*/ | ||
export function getInstance(): Instance|undefined { | ||
return (baseExtension && baseExtension.isActive && baseExtension.exports ? baseExtension.exports.instance : undefined); | ||
} | ||
``` |
Oops, something went wrong.