feat: handle additional struct components & enable full pass on almost all completion specs

Signed-off-by: cpendery <cpendery@vt.edu>
This commit is contained in:
cpendery
2023-09-10 23:51:29 -04:00
parent 4cd3cc1225
commit e3dfa9e7fa
7 changed files with 633 additions and 75 deletions
+20 -6
View File
@@ -13,17 +13,31 @@ type Suggestion struct {
}
var (
cmdDelimiter = regexp.MustCompile(`(\|\|)|(&&)|(;)`)
cmdDelimiter = regexp.MustCompile(`(\|\|)|(&&)|(;)`)
lastSuggestionCmd = ""
lastSuggestion = []Suggestion{}
)
func LoadSuggestions(cmd string) []Suggestion {
func loadSuggestions(cmd string) (suggestions []Suggestion) {
activeCmd := ParseCommand(cmd)
if len(activeCmd) <= 0 {
return []Suggestion{}
return
}
log.Println(activeCmd, activeCmd[0].token)
if spec, ok := specs.Specs[activeCmd[0].token]; ok {
rootToken := activeCmd[0]
if !rootToken.complete {
return
}
if spec, ok := specs.Specs[rootToken.token]; ok {
log.Println(spec.Description)
}
return []Suggestion{}
return
}
func LoadSuggestions(cmd string) []Suggestion {
if cmd == lastSuggestionCmd {
return lastSuggestion
}
suggestions := loadSuggestions(cmd)
lastSuggestionCmd, lastSuggestion = cmd, suggestions
return suggestions
}
+142 -49
View File
@@ -3,6 +3,20 @@ import * as fs from "fs";
import * as path from "path";
import * as process from "process";
import * as child_process from "child_process";
import ProgressBar = require("progress");
const exclusions = new Set(["deno.ts", "rush.ts"]);
const clearAllNewlines = (input: string): string => {
return input.replaceAll(/[\r\n]+/g, "");
};
function chunk<T>(arr: T[], chuckSize: number): T[][] {
const arrays: T[][] = [];
for (let i = 0; i < arr.length; i += chuckSize)
arrays.push(arr.slice(i, i + chuckSize));
return arrays;
}
const main = async () => {
const basePath = path.join(process.cwd(), ".fig");
@@ -15,70 +29,149 @@ const main = async () => {
const directoryItems = await fsAsync.readdir(basePath, {
withFileTypes: true,
});
await Promise.all(
[directoryItems[10]].map(async (directoryItem) => {
if (!directoryItem.isFile()) {
return;
}
const spec: Fig.Spec = (
await import(path.join(basePath, directoryItem.name))
).default;
if (typeof spec === "function") {
return;
}
const filenameWithoutExtension = path.parse(directoryItem.name).name;
const subcommand = spec as unknown as Fig.Subcommand;
const generatedCode = generateGolang(
subcommand,
filenameWithoutExtension
);
await fsAsync.writeFile(
path.join(
process.cwd(),
"..",
"specs",
`${filenameWithoutExtension}.go`
),
generatedCode
);
})
const progressBar = new ProgressBar(
"extracting [:bar] :percent (:current/:total)",
{
total: directoryItems.length,
complete: "=",
incomplete: " ",
width: 20,
}
);
const dirItemChunks = chunk(directoryItems, 10);
for (const dirChunk of dirItemChunks) {
await Promise.all(
dirChunk.map(async (directoryItem) => {
if (!directoryItem.isFile()) {
progressBar.tick();
return;
}
if (exclusions.has(directoryItem.name)) {
progressBar.tick();
return;
}
const spec: Fig.Spec = (
await import(path.join(basePath, directoryItem.name))
).default;
if (typeof spec === "function") {
progressBar.tick();
return;
}
const filenameWithoutExtension = path.parse(directoryItem.name).name;
const subcommand = spec as unknown as Fig.Subcommand;
const generatedCode = generateGolang(
subcommand,
filenameWithoutExtension
);
await fsAsync.writeFile(
path.join(
process.cwd(),
"..",
"specs",
`${filenameWithoutExtension}.go`
),
generatedCode
);
progressBar.tick();
})
);
}
const generatedFilesPath = path.join(process.cwd(), "..", "specs");
child_process.exec(`gofmt -w ${generatedFilesPath}`);
};
const generateTemplate = (template: Fig.Template): string => {
switch (template) {
case "filepaths":
return "model.TemplateFilepaths";
case "folders":
return "model.TemplateFolders";
case "history":
return "model.TemplateHistory";
case "help":
return "model.TemplateHelp";
}
throw Error("unknown template value");
};
const genName = (name: Fig.SingleOrArray<string> | undefined): string => {
if (name == null) return "";
return Array.isArray(name)
? `Name: []string{${name.map((n) => `"${n}"`).join(",")}},`
: `Name: "${name}",`;
};
const genDescription = (description: string | undefined): string => {
return description != null
? `Description: "${clearAllNewlines(description)}",`
: "";
};
const genTemplates = (
template: Fig.SingleOrArray<Fig.Template> | undefined
): string => {
if (template == null) return "";
const templates = Array.isArray(template) ? template : [template];
return `Templates: []model.Template{${templates
.map((t) => generateTemplate(t))
.join(",")}},`;
};
const genOptions = (options: Fig.Option[] | undefined): string => {
return options != null ? generateOptions(options) : "";
};
const genArgs = (args: Fig.SingleOrArray<Fig.Arg> | undefined): string => {
return args != null ? generateArgs(args) : "";
};
const genSubcommands = (
subcommand: Fig.SingleOrArray<Fig.Subcommand> | undefined
): string => {
if (subcommand == null) return "";
const subcommands = Array.isArray(subcommand) ? subcommand : [subcommand];
return `Subcommands: []model.Subcommand{${subcommands
.map((s) => generateSubcommand(s))
.join(",")}},`;
};
const generateArgs = (args: Fig.SingleOrArray<Fig.Arg>): string => {
return "";
const argList = Array.isArray(args) ? args : [args];
const generatedArgs = argList.map((arg) => {
return `{
${genTemplates(arg.template)}
${genName(arg.name)}
${genDescription(arg.description)}
},`;
});
return `Args: []model.Arg{${generatedArgs}},`;
};
const generateOptions = (options: Fig.Option[]): string => {
return "";
const generatedOptions = options.map((option) => {
return `{
${genName(option.name)}
${genDescription(option.description)}
${genArgs(option.args)}
}`;
});
return `Options: []model.Option{${generatedOptions}},`;
};
const generateSubcommand = (subcommand: Fig.Subcommand): string => {
const name = Array.isArray(subcommand.name)
? `Name: []string{${subcommand.name.map((n) => `"${n}"`).join(",")}},`
: `Name: "${subcommand.name}",`;
const description =
subcommand.description != null
? `Description: "${subcommand.description}",`
: "";
const args = subcommand.args != null ? generateArgs(subcommand.args) : "";
const options =
subcommand.options != null ? generateOptions(subcommand.options) : "";
return `model.Subcommand{
${name}
${description}
${args}
${options}
}`.replaceAll(/\s/g, "");
${genName(subcommand.name)}
${genDescription(subcommand.description)}
${genArgs(subcommand.args)}
${genOptions(subcommand.options)}
${genSubcommands(subcommand.subcommands)}
}`;
};
const generateGolang = (
+441 -4
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -4,8 +4,19 @@
},
"devDependencies": {
"@types/node": "^20.6.0",
"@types/progress": "^2.0.5",
"@withfig/autocomplete-types": "^1.28.0",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
},
"dependencies": {
"@fig/autocomplete-generators": "^2.2.5",
"@fig/autocomplete-helpers": "^1.0.7",
"@fig/autocomplete-hooks": "^1.0.2",
"@withfig/api-bindings": "^0.30.3",
"progress": "^2.0.3",
"semver": "^7.5.4",
"strip-json-comments": "^5.0.1",
"yaml": "^2.3.2"
}
}
+13 -1
View File
@@ -1,12 +1,24 @@
{
"ts-node": {
"transpileOnly": true,
"files": true,
"compilerOptions": {
"module": "commonjs"
}
},
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"moduleResolution": "node",
"declaration": true,
"outDir": "./build",
"strict": true,
"esModuleInterop": true,
"types": ["@withfig/autocomplete-types", "node"]
"types": ["@withfig/autocomplete-types", "node"],
// rule changes
"noImplicitAny": false,
"strictNullChecks": false,
"allowSyntheticDefaultImports": true
},
"exclude": ["node_modules/", "fig/", "build/"]
}
+6 -4
View File
@@ -3,8 +3,9 @@ package model
type Subcommand struct {
Name interface{} //single or array string, required
Description string
Args interface{} //single or array Arg, optional
Args []Arg
Options []Option
Subcommands []Subcommand
}
type Option struct {
@@ -14,9 +15,9 @@ type Option struct {
}
type Arg struct {
Name string //single, optional
Description string //single, optional
Template Template //single, optional
Name string //single, optional
Description string //single, optional
Templates []Template
}
type Template string
@@ -25,6 +26,7 @@ const (
TemplateFilepaths Template = "filepaths"
TemplateFolders Template = "folders"
TemplateHistory Template = "history"
TemplateHelp Template = "help"
)
var (
-11
View File
@@ -1,11 +0,0 @@
// Code generated by autocomplete/extract/extract.ts. DO NOT EDIT.
package specs
import (
"github.com/cpendery/clac/autocomplete/model"
)
func init() {
Specs["afplay"] = model.Subcommand{Name: "afplay", Description: "AudioFilePlay"}
}