Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto suggest environment variables using l2 --env <path> command #10

Merged
merged 9 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Lama2",
"displayName": "Lama2",
"description": "Nifty VSCode UI for Lama2, the plain-text powered API client",
"version": "2.3.0",
"version": "2.6.0",
"publisher": "Hexmos",
"license": "SEE LICENSE IN LICENSE",
"icon": "lama2.png",
Expand Down
301 changes: 120 additions & 181 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import * as vscode from "vscode";
import * as fs from "fs";
import * as path from "path";
import { exec } from "child_process";

import ExecuteCurrentFile from "./executeCurrentFile";
import GenerateCodeSnippet from "./generateCodeSnippet";
import GetRemoteURL from "./getRemoteUrl";
import LanguagesData from "./languages";
import { exec } from 'child_process';
import triggers from "./triggers";

let examplesJSONStr =
'{"0006_cookies.l2":"POST \\nhttps://httpbin.org/post\\n\\n# HEADERS\\nCookie:\\"sessionid=foo;another-cookie=bar\\"\\n\\nHeader1:value1\\nHeader2: Value2\\n\\n# DATA\\nhello=world","0002_sample_post.l2":"POST\\nhttps://httpbin.org/post\\n\\n{\\n \\"a\\": \\"b\\",\\n \\"c\\": \\"d\\"\\n}","0005_headers_simple.l2":"POST \\nhttps://httpbin.org/post\\n\\n# HEADERS\\nX-Parse-Application-Id:\'helloworld\'\\nX-Parse-REST-API-Key:\\"byeworld\\"\\n\\n# DATA\\na=\\"b\\" # double-quoted string\\n\'c\'=d # single-quoted & unquoted strings","0008_base64_image":{"0008_base64_image.l2":"POST\\nhttp://httpbin.org/post\\n\\n{\\n\\t\\"imageb64_field\\": \\"\'${PHOTO}\'\\",\\n}","l2.env":"\\nexport PHOTO=`base64 image.jpeg`"},"0001_sample_post_varjson.l2":"POST\\nhttps://httpbin.org/post\\n\\na=b\\nc=d","0007_multipart_file":{"0007_multipart_file.l2":"POST\\nMULTIPART\\nhttp://httpbin.org/post\\n\\n\'X-Parse-Application-Id\':helloworld \\nX-Parse-REST-API-Key:\\"helloworld\\"\\n\\n# DATA\\nfirst=second\\n\\n# FILES\\nmyfile@./image.jpeg"},"0004_env_switch_root":{"0004_env_switch_root.l2":"POST\\n${REMOTE}/post\\n\\n{\\n \\"lorem\\": \\"ipsum\\"\\n}","l2.env":"export LOCAL=\\"http://localhost:8000\\"\\nexport REMOTE=\\"http://httpbin.org\\""},"0003_comment.l2":"# Pound symbol signifies a comment\\nPOST\\nhttps://httpbin.org/post\\n\\na=b # Comments may start at the end of lines as well\\nc=d\\n\\n# Comments work even after the payload","0000_sample_get.l2":"GET\\nhttps://httpbin.org/get","0009_processor_basic":{"0009_processor_basic.l2":"url = \\"http://google.com\\"\\n---\\n# stage 1\\n\\nPOST\\n${REMOTE_COORD}/anything\\n\\n{\\n \\"username\\": \\"admin\\",\\n \\"password\\": \\"Password@123\\",\\n \\"from\\": \\"${LOCAL_COORD}/anything\\",\\n \\"url\\": \\"${url}\\",\\n \\"Token\\": \\"MySuperSecretToken\\"\\n}\\n\\n---\\n\\n// filtering, store in var\\nconsole.log(\\"@@Result\\", result)\\nlet TOKEN = result[\\"json\\"][\\"Token\\"]\\nconsole.log(TOKEN)\\n\\n---\\n\\n# stage 2\\nGET\\n${REMOTE_COORD}/bearer\\n\\nAuthorization: \'Bearer ${TOKEN}\'\\n\\n{}","l2.env":"\\nexport LOCAL_COORD=\\"http://localhost:8080\\"\\nexport REMOTE_COORD=\\"http://httpbin.org\\""}}';
let examplesJSON = JSON.parse(examplesJSONStr);
let specificExGlobal = "";
let subSpecificExGlobal = "";
import { replaceTextAfterEnvSelected, suggestENVS } from "./suggestEnvironmentVars";

interface LanguageData {
info: {
Expand All @@ -28,86 +21,91 @@ interface LanguagesData {
[key: string]: LanguageData;
}

export function activate(context: vscode.ExtensionContext) {
console.log('>>> Congratulations, your extension "Lama2" is now active!');
let examplesJSONStr =
'{"0006_cookies.l2":"POST \\nhttps://httpbin.org/post\\n\\n# HEADERS\\nCookie:\\"sessionid=foo;another-cookie=bar\\"\\n\\nHeader1:value1\\nHeader2: Value2\\n\\n# DATA\\nhello=world","0002_sample_post.l2":"POST\\nhttps://httpbin.org/post\\n\\n{\\n \\"a\\": \\"b\\",\\n \\"c\\": \\"d\\"\\n}","0005_headers_simple.l2":"POST \\nhttps://httpbin.org/post\\n\\n# HEADERS\\nX-Parse-Application-Id:\'helloworld\'\\nX-Parse-REST-API-Key:\\"byeworld\\"\\n\\n# DATA\\na=\\"b\\" # double-quoted string\\n\'c\'=d # single-quoted & unquoted strings","0008_base64_image":{"0008_base64_image.l2":"POST\\nhttp://httpbin.org/post\\n\\n{\\n\\t\\"imageb64_field\\": \\"\'${PHOTO}\'\\",\\n}","l2.env":"\\nexport PHOTO=`base64 image.jpeg`"},"0001_sample_post_varjson.l2":"POST\\nhttps://httpbin.org/post\\n\\na=b\\nc=d","0007_multipart_file":{"0007_multipart_file.l2":"POST\\nMULTIPART\\nhttp://httpbin.org/post\\n\\n\'X-Parse-Application-Id\':helloworld \\nX-Parse-REST-API-Key:\\"helloworld\\"\\n\\n# DATA\\nfirst=second\\n\\n# FILES\\nmyfile@./image.jpeg"},"0004_env_switch_root":{"0004_env_switch_root.l2":"POST\\n${REMOTE}/post\\n\\n{\\n \\"lorem\\": \\"ipsum\\"\\n}","l2.env":"export LOCAL=\\"http://localhost:8000\\"\\nexport REMOTE=\\"http://httpbin.org\\""},"0003_comment.l2":"# Pound symbol signifies a comment\\nPOST\\nhttps://httpbin.org/post\\n\\na=b # Comments may start at the end of lines as well\\nc=d\\n\\n# Comments work even after the payload","0000_sample_get.l2":"GET\\nhttps://httpbin.org/get","0009_processor_basic":{"0009_processor_basic.l2":"url = \\"http://google.com\\"\\n---\\n# stage 1\\n\\nPOST\\n${REMOTE_COORD}/anything\\n\\n{\\n \\"username\\": \\"admin\\",\\n \\"password\\": \\"Password@123\\",\\n \\"from\\": \\"${LOCAL_COORD}/anything\\",\\n \\"url\\": \\"${url}\\",\\n \\"Token\\": \\"MySuperSecretToken\\"\\n}\\n\\n---\\n\\n// filtering, store in var\\nconsole.log(\\"@@Result\\", result)\\nlet TOKEN = result[\\"json\\"][\\"Token\\"]\\nconsole.log(TOKEN)\\n\\n---\\n\\n# stage 2\\nGET\\n${REMOTE_COORD}/bearer\\n\\nAuthorization: \'Bearer ${TOKEN}\'\\n\\n{}","l2.env":"\\nexport LOCAL_COORD=\\"http://localhost:8080\\"\\nexport REMOTE_COORD=\\"http://httpbin.org\\""}}';
let examplesJSON = JSON.parse(examplesJSONStr);
let specificExGlobal = "";
let subSpecificExGlobal = "";


function execCurL2File(context: vscode.ExtensionContext) {
let executeCurrentFile = new ExecuteCurrentFile(context);
return vscode.commands.registerCommand("lama2.ExecuteCurrentFile", () =>
executeCurrentFile.execFile()
);
}

// Level1 command pallette
function getRemoteUrl(context: vscode.ExtensionContext) {
let getremoteUrl = new GetRemoteURL(context);
console.log(getremoteUrl);
let getremoteUrlFileDisposable = vscode.commands.registerCommand(
"lama2.GetRemoteURL",
() => getremoteUrl.findURL()
);
context.subscriptions.push(getremoteUrlFileDisposable);

// Level1 command pallette
let executeCurrentFile = new ExecuteCurrentFile(context);

let executeCurrentFileDisposable = vscode.commands.registerCommand(
"lama2.ExecuteCurrentFile",
() => executeCurrentFile.execFile()
);

context.subscriptions.push(executeCurrentFileDisposable);
return getremoteUrlFileDisposable;
}

// Level1 command pallette
function prettifyL2File() {
return vscode.commands.registerCommand("lama2.PrettifyCurrentFile", () => {
console.log("Executing prettify command");
exec(`l2 -b ${vscode.window.activeTextEditor?.document.fileName}`);
});
}

let prettifyCurrentFileDisposable = vscode.commands.registerCommand(
"lama2.PrettifyCurrentFile",
() => {
console.log("Executing prettify command");
exec(`l2 -b ${vscode.window.activeTextEditor?.document.fileName}`);
}
);
context.subscriptions.push(prettifyCurrentFileDisposable);
function genLama2Examples() {
return vscode.commands.registerCommand("lama2.Lama2Examples", async () => {
let specificEx: string | undefined = await vscode.window.showQuickPick(
Object.keys(examplesJSON)
);

if (specificEx) {
specificExGlobal = specificEx;
if (specificEx.endsWith(".env") || specificEx.endsWith(".l2")) {
vscode.window.activeTextEditor?.edit((builder) => {
const doc = vscode.window.activeTextEditor?.document;
builder.replace(
new vscode.Range(
doc!.lineAt(0).range.start,
doc!.lineAt(doc!.lineCount - 1).range.end
),
examplesJSON[specificExGlobal]
);
});
return;
}

let generateCodeSnippet = new GenerateCodeSnippet();
let lama2Examples = vscode.commands.registerCommand(
"lama2.Lama2Examples",
async () => {
let specificEx: string | undefined = await vscode.window.showQuickPick(
Object.keys(examplesJSON)
let subSpecificEx: string | undefined = await vscode.window.showQuickPick(
Object.keys(examplesJSON[specificEx])
);

if (specificEx) {
specificExGlobal = specificEx;
if (specificEx.endsWith(".env") || specificEx.endsWith(".l2")) {
vscode.window.activeTextEditor?.edit((builder) => {
const doc = vscode.window.activeTextEditor?.document;
builder.replace(
new vscode.Range(
doc!.lineAt(0).range.start,
doc!.lineAt(doc!.lineCount - 1).range.end
),
examplesJSON[specificExGlobal]
);
});
return;
}

let subSpecificEx: string | undefined =
await vscode.window.showQuickPick(
Object.keys(examplesJSON[specificEx])
if (subSpecificEx) {
subSpecificExGlobal = subSpecificEx;
vscode.window.activeTextEditor?.edit((builder) => {
const doc = vscode.window.activeTextEditor?.document;
builder.replace(
new vscode.Range(
doc!.lineAt(0).range.start,
doc!.lineAt(doc!.lineCount - 1).range.end
),
examplesJSON[specificExGlobal][subSpecificExGlobal]
);

if (subSpecificEx) {
subSpecificExGlobal = subSpecificEx;
vscode.window.activeTextEditor?.edit((builder) => {
const doc = vscode.window.activeTextEditor?.document;
builder.replace(
new vscode.Range(
doc!.lineAt(0).range.start,
doc!.lineAt(doc!.lineCount - 1).range.end
),
examplesJSON[specificExGlobal][subSpecificExGlobal]
);
});
return;
}
});
return;
}
}
);
});
}

// Level1 command pallette
function isDefault(defaultClient: string, client: string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this function about? not able to infer from context.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it should go into a group of functions in a different file. My best guess is it's related to the code generation library selection.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah moved everything to new files.

if (defaultClient == client) {
return "(Default)";
} else {
return "";
}
}

function genCodeSnip() {
let generateCodeSnippet = new GenerateCodeSnippet();
let generateCodeSnippetDisposable = vscode.commands.registerCommand(
"lama2.GenerateCodeSnippet",
async () => {
Expand Down Expand Up @@ -148,25 +146,25 @@ export function activate(context: vscode.ExtensionContext) {
// Level3 command pallette
const selection:
| {
label: string;
language: string;
client: string;
}
label: string;
language: string;
client: string;
}
| undefined = await vscode.window.showQuickPick(
clientKeys
.sort((a, b) =>
a === defaultClient ? -1 : b === defaultClient ? 1 : 0
)
.map((client) => ({
label: `${language ?? ""}: ${client} ${isDefault(
defaultClient,
client
)}`,
language: language ?? "",
client: client,
})),
{ placeHolder: "Select a client" }
);
clientKeys
.sort((a, b) =>
a === defaultClient ? -1 : b === defaultClient ? 1 : 0
)
.map((client) => ({
label: `${language ?? ""}: ${client} ${isDefault(
defaultClient,
client
)}`,
language: language ?? "",
client: client,
})),
{ placeHolder: "Select a client" }
);

if (selection) {
generateCodeSnippet.execFile(selection.language, selection.client);
Expand All @@ -175,105 +173,46 @@ export function activate(context: vscode.ExtensionContext) {
}
}
);
context.subscriptions.push(generateCodeSnippetDisposable);
return generateCodeSnippetDisposable;
}

let envVariables = [] as string[];
const activeTextEditor = vscode.window.activeTextEditor;
if (activeTextEditor) {
const activeFilePath = activeTextEditor.document.uri.fsPath;
const envFilePath = path.join(path.dirname(activeFilePath), "l2.env");
fs.readFile(envFilePath, "utf8", (err, data) => {
if (err) {
console.error(err);
return;
}

envVariables = data
.split("\n")
.filter((line) => line.startsWith("export"))
.map((line) => line.split("=")[0].replace("export ", ""));
console.log("envVariables -> ", envVariables);
});
}
let cursorPosition: any;
let linePosition: any;
export function activate(context: vscode.ExtensionContext) {
console.log('>>> Congratulations, your extension "Lama2" is now active!');

// Level1 command pallette
let executeCurrentFileDisposable = execCurL2File(context);
context.subscriptions.push(executeCurrentFileDisposable);
console.log(">>> executeCurrentFileDisposable is now active!");

// Level1 command pallette
let getremoteUrlFileDisposable = getRemoteUrl(context);
context.subscriptions.push(getremoteUrlFileDisposable);
console.log(">>> getremoteUrlFileDisposable is now active!");

// Level1 command pallette
let prettifyCurrentFileDisposable = prettifyL2File();
context.subscriptions.push(prettifyCurrentFileDisposable);
console.log(">>> prettifyCurrentFileDisposable is now active!");

let suggestEnvVariables = vscode.languages.registerCompletionItemProvider(
{ language: "lama2", scheme: "file" },
{
// eslint-disable-next-line no-unused-vars
provideCompletionItems(document, position, token, context) {
// get all text until the `position` and check if it reads `${`
// Level1 command pallette
let lama2Examples = genLama2Examples();
console.log(">>> lama2Examples is now active!");

const linePrefix = document
.lineAt(position)
.text.substring(0, position.character);
if (!linePrefix.endsWith("${")) {
return undefined;
}
let createSuggestion = (text: string) => {
let item = new vscode.CompletionItem(
text,
vscode.CompletionItemKind.Text
);
item.range = new vscode.Range(position, position);
item.command = {
title: "",
command: "options",
};
return item;
};
const suggestionsArray = envVariables.map((item, index) => {
return createSuggestion(item);
});
// Level1 command pallette
let generateCodeSnippetDisposable = genCodeSnip();
context.subscriptions.push(generateCodeSnippetDisposable);
console.log(">>> generateCodeSnippetDisposable is now active!");

return suggestionsArray;
},
...triggers, // trigger
resolveCompletionItem(
item: vscode.CompletionItem,
token: vscode.CancellationToken
) {
const editor = vscode.window.activeTextEditor;
if (editor) {
const position = editor.selection.active;
cursorPosition = position.character;
linePosition = position.line;
}
return item;
},
}
);
let suggestEnvVariables = suggestENVS();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestENVs

context.subscriptions.push(
suggestEnvVariables,
vscode.commands.registerCommand("options", () => {
const editor = vscode.window.activeTextEditor;
if (editor) {
const position = editor.selection.active;
const bracketPosition = editor.document
.lineAt(position.line)
.text.indexOf("{");
const start = new vscode.Position(linePosition, bracketPosition + 1); // start position
const end = new vscode.Position(linePosition, cursorPosition); // end position
const range = new vscode.Range(start, end); // create range object
const replaceText = "";
const edit = new vscode.WorkspaceEdit();
edit.replace(editor.document.uri, range, replaceText);
vscode.workspace.applyEdit(edit);
}
vscode.commands.registerCommand("envoptions", () => {
// This method is activated when the user selects a suggested env variable.
replaceTextAfterEnvSelected();
})
);
}

// this method is called when your extension is deactivated
export function deactivate() {}

function isDefault(defaultClient: string, client: string) {
if (defaultClient == client) {
return "(Default)";
} else {
return "";
}
}
export function deactivate() { }
Loading