Created
August 8, 2023 17:44
-
-
Save Developerayo/25ffba9ff3e59d909de9b9bf8fedd7c3 to your computer and use it in GitHub Desktop.
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
import * as vscode from "vscode"; | |
import axios from "axios"; | |
import * as fs from "fs"; | |
import * as path from "path"; | |
const GITHUB_API_URL = "https://api.github.com/gists"; | |
const GITHUB_AUTH_SCHEME = "github"; | |
const GITHUB_AUTH_SCOPES = ["gist"]; | |
const COMMAND_CREATE_GIST_ACTIVE_EDITOR = "code-to-gist.createGistFromActiveEditor"; | |
const COMMAND_CREATE_GIST_EXPLORER = "code-to-gist.createGistFromExplorer"; | |
const COMMAND_CREATE_GIST_FILE_PICKER = "code-to-gist.createGistFromFilePicker"; | |
const COMMAND_CREATE_GIST_SELECTION = "code-to-gist.createGistFromSelection"; | |
const EXTENSION_ID = "developerayo.code-to-gist "; | |
interface Gist { | |
[key: string]: { content: string }; | |
} | |
const extUpdates = async () => { | |
const extension = vscode.extensions.getExtension(EXTENSION_ID); | |
const currentVersion = extension?.packageJSON.version; | |
const latestVersion = await getLatestVersionFromMarketplace(); | |
if (currentVersion && latestVersion && currentVersion !== latestVersion) { | |
const updateButton = "Update Now"; | |
const selectedButton = await vscode.window.showInformationMessage( | |
`A new version of Code-To-Gist is available! Current: ${currentVersion}, Latest: ${latestVersion}`, | |
updateButton | |
); | |
if (selectedButton === updateButton) { | |
vscode.env.openExternal(vscode.Uri.parse("https://marketplace.visualstudio.com/items?itemName=" + EXTENSION_ID)); | |
} | |
} | |
}; | |
async function getLatestVersionFromMarketplace() { | |
const extensionPublisher = "developerayo"; | |
const extensionName = "code-to-gist"; | |
const url = `https://marketplace.visualstudio.com/items?itemName=developerayo.code-to-gist`; | |
const body = { | |
filters: [ | |
{ | |
criteria: [ | |
{ filterType: 8, value: extensionPublisher + "." + extensionName }, | |
{ filterType: 12, value: "4096" }, | |
], | |
}, | |
], | |
flags: 914, | |
}; | |
// https://stackoverflow.com/questions/56918767/disable-check-of-camel-case-rule-in-eslint | |
/* eslint-disable @typescript-eslint/naming-convention */ | |
const headers = { | |
"Content-Type": "application/json", | |
Accept: "application/json;api-version=6.1-preview.1", | |
}; | |
/* eslint-enable @typescript-eslint/naming-convention */ | |
try { | |
const response = await axios.post(url, body, { headers }); | |
const latestVersion = response.data.results[0].extensions[0].versions[0].version; | |
return latestVersion; | |
} catch (error) { | |
console.error("Failed to fetch the latest version:", error); | |
return null; | |
} | |
} | |
const getSessionToken = async () => { | |
const session = await vscode.authentication.getSession(GITHUB_AUTH_SCHEME, GITHUB_AUTH_SCOPES, { | |
createIfNone: true, | |
}); | |
if (!session) { | |
throw new Error("Failed to authenticate with GitHub. Please check your credentials."); | |
} | |
return session.accessToken; | |
}; | |
const getGistVisibility = async () => { | |
const gistVisibility = await vscode.window.showQuickPick( | |
["public", "secret"], | |
{ | |
placeHolder: "Choose the gist visibility", | |
} | |
); | |
if (!gistVisibility) { | |
throw new Error("Gist visibility selection was cancelled or invalid."); | |
} | |
return gistVisibility === "public"; | |
}; | |
const readFilesRecursively = (filePath: string | undefined) => { | |
const gists: Gist[] = []; | |
if (!filePath) { | |
return gists; | |
} | |
const isMultiple = fs.statSync(filePath).isDirectory(); | |
const _readFilesRecursively = (_filePath: string) => { | |
if (_filePath.split("/").pop() === "node_modules") { | |
vscode.window.showInformationMessage( | |
"Can't create a gist from node_modules" | |
); | |
return; | |
} | |
const isFile = !fs.statSync(_filePath).isDirectory(); | |
/* eslint-disable @typescript-eslint/naming-convention */ | |
const readSingleFile = (__filePath: string) => { | |
const fileName = __filePath.split("/").pop() || ""; | |
const fileContent = fs.readFileSync(__filePath, "utf8"); | |
if (!isMultiple) { | |
vscode.window.showInformationMessage(`Read file ${fileName}`); | |
} | |
gists.push({ | |
[fileName]: { | |
content: fileContent, | |
}, | |
}); | |
}; | |
if (isFile) { | |
readSingleFile(_filePath); | |
return; | |
} | |
const files = fs.readdirSync(_filePath); | |
files.forEach((file) => { | |
const itemPath = path.join(_filePath, file); | |
const isDirectory = fs.statSync(itemPath).isDirectory(); | |
if (isDirectory) { | |
_readFilesRecursively(itemPath); | |
vscode.window.showInformationMessage(`Read multiple files...`); | |
} else { | |
readSingleFile(itemPath); | |
} | |
}); | |
}; | |
_readFilesRecursively(filePath); | |
return gists; | |
}; | |
const uploadGistAndHandleErrors = async ( | |
gists: Gist[], | |
isPublic: boolean, | |
token: string | |
) => { | |
try { | |
const response = await axios.post( | |
GITHUB_API_URL, | |
{ | |
files: Object.assign({}, ...gists), | |
public: isPublic, | |
}, | |
{ | |
headers: { | |
Authorization: `token ${token}`, | |
}, | |
} | |
); | |
if (response.status === 201) { | |
const gistUrl = response.data.html_url; | |
const openGistButton = "Go to Gist"; | |
const selectedButton = await vscode.window.showInformationMessage( | |
`Gist Created: ${gistUrl}`, | |
openGistButton | |
); | |
if (selectedButton === openGistButton) { | |
vscode.env.openExternal(vscode.Uri.parse(gistUrl)); | |
} | |
} else { | |
throw new Error( | |
`GitHub API responded with status code ${response.status}` | |
); | |
} | |
} catch (e) { | |
if (axios.isAxiosError(e)) { | |
if (e.response?.status === 403) { | |
vscode.window.showErrorMessage( | |
"Rate limit exceeded. Please try again later." | |
); | |
} else if (e.response?.status === 401) { | |
vscode.window.showErrorMessage( | |
"Invalid GitHub PAT. Please check your token." | |
); | |
} else { | |
vscode.window.showErrorMessage(`Failed to create a gist: ${e.message}`); | |
} | |
} else { | |
vscode.window.showErrorMessage( | |
(e as string) || "An unexpected error occurred while creating a gist" | |
); | |
} | |
} | |
}; | |
const createGistFromActiveEditor = async () => { | |
const token = await getSessionToken(); | |
const activeDocument = vscode.window.activeTextEditor?.document; | |
const activeEditorFileName = activeDocument?.fileName.split("/").pop() || ""; | |
const activeEditorFileContent = activeDocument?.getText() || ""; | |
if (!activeEditorFileName || !activeEditorFileContent) { | |
throw new Error("Please open a file in editor"); | |
} | |
const isPublic = await getGistVisibility(); | |
const gists: Gist[] = []; | |
gists.push({ | |
[activeEditorFileName]: { | |
content: activeEditorFileContent, | |
}, | |
}); | |
return uploadGistAndHandleErrors(gists, isPublic, token); | |
}; | |
const createGistFromExplorer = async (uri: vscode.Uri | undefined) => { | |
const token = await getSessionToken(); | |
const gists = readFilesRecursively(uri?.fsPath); | |
if (gists.length < 1) { | |
throw new Error("Select a valid file or folder from the vscode explorer"); | |
} | |
const isPublic = await getGistVisibility(); | |
return uploadGistAndHandleErrors(gists, isPublic, token); | |
}; | |
const createGistFromFilePicker = async () => { | |
const token = await getSessionToken(); | |
const filesToUpload = await vscode.window.showOpenDialog({ | |
canSelectMany: true, | |
canSelectFolders: false, | |
}); | |
if (!filesToUpload) { | |
throw new Error("Please select a file"); | |
} | |
const gists: Gist[] = []; | |
for (const fileUri of filesToUpload) { | |
const fileName = fileUri.path.split("/").pop() || ""; | |
let fileContent; | |
try { | |
fileContent = fs.readFileSync(fileUri.fsPath, "utf8"); | |
} catch (e) { | |
vscode.window.showErrorMessage(`Failed to read the file: ${fileName}`); | |
throw e; | |
} | |
gists.push({ | |
[fileName]: { | |
content: fileContent, | |
}, | |
}); | |
} | |
const isPublic = await getGistVisibility(); | |
return uploadGistAndHandleErrors(gists, isPublic, token); | |
}; | |
const createGistFromSelection = async () => { | |
const token = await getSessionToken(); | |
const activeEditor = vscode.window.activeTextEditor; | |
const activeEditorFileName = | |
activeEditor?.document.fileName.split("/").pop() || ""; | |
const selectedTextContent = | |
(activeEditor && activeEditor.document.getText(activeEditor.selection)) || | |
""; | |
if (!activeEditorFileName || !selectedTextContent) { | |
throw new Error("Please select some text to create gist"); | |
} | |
const isPublic = await getGistVisibility(); | |
const gists: Gist[] = []; | |
gists.push({ | |
[activeEditorFileName]: { | |
content: selectedTextContent, | |
}, | |
}); | |
return uploadGistAndHandleErrors(gists, isPublic, token); | |
}; | |
export async function activate(context: vscode.ExtensionContext) { | |
const createGistFromActiveEditorDisposable = vscode.commands.registerCommand( | |
COMMAND_CREATE_GIST_ACTIVE_EDITOR, | |
createGistFromActiveEditor | |
); | |
const createGistFromExplorerDisposable = vscode.commands.registerCommand( | |
COMMAND_CREATE_GIST_EXPLORER, | |
createGistFromExplorer | |
); | |
const createGistFromFilePickerDisposable = vscode.commands.registerCommand( | |
COMMAND_CREATE_GIST_FILE_PICKER, | |
createGistFromFilePicker | |
); | |
const createGistFromSelectionDisposable = vscode.commands.registerCommand( | |
COMMAND_CREATE_GIST_SELECTION, | |
createGistFromSelection | |
); | |
context.subscriptions.push(createGistFromActiveEditorDisposable); | |
context.subscriptions.push(createGistFromExplorerDisposable); | |
context.subscriptions.push(createGistFromFilePickerDisposable); | |
context.subscriptions.push(createGistFromSelectionDisposable); | |
let myStatusBarItem = vscode.window.createStatusBarItem( | |
vscode.StatusBarAlignment.Left, | |
1 | |
); | |
myStatusBarItem.command = "code-to-gist.createGistFromFilePicker"; | |
myStatusBarItem.text = `$(file-code) Create Gist`; | |
myStatusBarItem.tooltip = "Create Gist"; | |
context.subscriptions.push(myStatusBarItem); | |
myStatusBarItem.show(); | |
extUpdates(); | |
} | |
export function deactivate() {} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment