Skip to content

Instantly share code, notes, and snippets.

@Geczy
Created August 8, 2021 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Geczy/98b423867b865c2b36a73a32a0f757f0 to your computer and use it in GitHub Desktop.
Save Geczy/98b423867b865c2b36a73a32a0f757f0 to your computer and use it in GitHub Desktop.
const vscode = require('vscode');
const path = require('path');
const fetch = require("node-fetch");
const jsdom = require('jsdom')
const getmac = require('getmac')
let global_questions = [];
let global_question_index = 0;
let global_last_response = null;
let global_current_language = ""
let panel = null;
async function google_analytics(event_name){
try{
const params = {
v: 1,
t: 'event',
tid: 'UA-19034238-5',
cid: callMac(),
ec: 'general',
el: global_current_language,
ea: event_name
};
let url = 'https://www.google-analytics.com/collect' + '?' + ( new URLSearchParams( params ) ).toString();
const rawResponse = fetch(url);
}catch(err){
}
}
async function get_json_stackoverflow(base_url, params){
base_url += '?' + ( new URLSearchParams( params ) ).toString();
const res = await fetch(base_url)
const json = await res.json()
return json
}
function string_replacements_stack(post){
return post.replaceAll("<code>", "")
.replaceAll("</code>", "")
.replaceAll("&gt;", ">")
.replaceAll("&lt;", "<")
.replaceAll(">>> ", "")
.replaceAll(">>>", "")
}
function process_code_stack(post){
return post.replaceAll("<pre><code>", '<button class="btn btn-send-to-editor">SEND TO EDITOR</button><br><pre><code>')
}
async function set_global_stack_questions_response(title){
const params = {
order: 'desc',
sort: 'relevance',
title: title,
site: 'stackoverflow',
tagged: global_current_language,
filter: '!6VvPDzQ)wlg1u',
key: 'x50f*DV4VX2ZzPIuvA)BmQ(('
};
let url = 'https://api.stackexchange.com/2.3/similar'
global_last_response = await get_json_stackoverflow(url, params)
global_questions = [];
global_question_index = 0;
try{
for(var question_i in global_last_response['items']){
let question = global_last_response['items'][question_i]
if(question['is_answered']){
global_questions.push(question)
}
}
}catch(err){}
}
async function build_html_body(logoSrc){
let html = '<div style="text-align:center"> <img style="margin-top:10px; margin-bottom:10px; width:200px;" src="'+logoSrc+'">'
try{
if (global_last_response['quota_remaining'] < 5000){
html += '<h3>Searches left: ' + global_last_response['quota_remaining'].toString() + ' / ' + global_last_response['quota_max'].toString() + '</h3>'
}
html += '<h4>Like this extension? Please consider <a href="https://www.buymeacoffee.com/markfobert">buying me a coffee.</a></h4>'
html += '<h6 style="color:gray">Content sourced from the <a style="color:gray" href="https://stackexchange.com/">Stack Exchange Network</a></a></h6></div><hr>'
}catch(err){}
if (global_questions.length == 0) {
html += '<div style="text-align:center">'
html += '<h3>No Results Found</h3>';
html += '<h4>Try refining your search</h4>';
html += '</div>'
return html;
}
let question = global_questions[global_question_index]
let date = new Date(parseInt(question['creation_date']) * 1000)
html += '<div style="position:relative;text-align:center">'
html += '<h3 style="display:inline-block;margin-bottom:5px"><a style="color:white" href="' + question['link'] + '">' + question['title'] + "</a></h3>"
html += '<h5 >Asked by <a style="color:white" href="' + question['owner']['link'] + '">' + question['owner']['display_name'] + "</a> on " + date.toDateString() + " </h5>"
if(global_question_index > 0){
html += '<button class="btn btn-previous-page" style="display:inline-block;margin-right:10px;font-size:1.5rem"> &lt;&lt; </button>'
}else{
html += '<button class="btn btn-previous-page" style="display:inline-block;margin-right:10px;font-size:1.5rem;visibility:hidden"> &lt;&lt; </button>'
}
if(global_questions.length > 1){
html += '<button class="btn btn-show-results" style="display:inline-block;margin-right:5px;margin-left:5px;font-size:1.5rem"> = </button>'
}
html += '<div id="title_popup" style="display:none;position:absolute;padding:20px;margin-left:auto;margin-right:auto;background:rgba(0, 0, 0, 0.9); border-radius: 10px; width: 80%;left:10%;right:10%;z-index:100;">'
for(var question_i in global_questions){
let tmp_question = global_questions[question_i]
html += '<h4><a id="' + question_i.toString() + '" class="question" href="#">' + tmp_question['title'] + '</a></h4>'
}
html += '</div>'
if(global_question_index < global_questions.length - 1){
html += '<button class="btn btn-next-page" style="display:inline-block;margin-left:10px;font-size:1.5rem"> &gt;&gt; </button>'
}else{
html += '<button class="btn btn-next-page" style="display:inline-block;margin-left:10px;font-size:1.5rem;visibility:hidden"> &gt;&gt; </button>'
}
html += '</div>'
html += process_code_stack(question['body'].toString()) + "<br><hr>"
for(var answer_i in question['answers']){
let answer = question['answers'][answer_i]
date = new Date(parseInt(answer['creation_date']) * 1000)
html += '<div style="text-align:center"><h5>Answered by <a style="color:white" href="' + answer['owner']['link'] + '">' + answer['owner']['display_name'] + "</a> on " + date.toDateString() + " </h5></div>"
html += process_code_stack(answer['body'].toString()) + "<br><hr>"
}
const {JSDOM} = jsdom;
const dom = new JSDOM(html.toString());
const $ = (require('jquery'))(dom.window);
const code_blocks = ($('pre'))
for (let i = 0; i < code_blocks.length; i++) {
const code_block = code_blocks[i]
$(code_block).clone().css('display', 'none').attr('class','nohighlight').insertBefore( $(code_block) )
}
html = $('body').html()
return html.toString();
}
function create_panel(){
panel = vscode.window.createWebviewPanel(
'stackFinder',
'StackFinder',
vscode.ViewColumn.Two,
{
enableScripts: true
}
);
}
//authorization https://stackoverflow.com/oauth/dialog?client_id=20721&scope=no_expiry&redirect_uri=https://stackoverflow.com/oauth/login_success
const callMac = () =>{
return getmac.default().toString()
}
/**
* @param {vscode.ExtensionContext} context
*/
async function activate(context) {
let disposable = vscode.commands.registerCommand('stackfinder.search', async function () {
let activeEditor = vscode.window.activeTextEditor;
if (!activeEditor) { return; }
global_current_language = activeEditor.document.languageId
align_languages()
google_analytics('search')
//edit text in console
let line_start = activeEditor.document.lineAt(activeEditor.selection.active.line);
const line_number_start = line_start.lineNumber;
const selection_current_line = new vscode.Selection(line_number_start, line_start.range.start.character,
line_number_start, line_start.range.end.character)
activeEditor.selection = selection_current_line;
const current_line_text = activeEditor.document.getText(selection_current_line);
//const stack_questions = await get_stack_answers(current_line_text);
activeEditor.edit(editBuilder => {
editBuilder.replace(selection_current_line, "");
});
if(panel == null){
create_panel()
}
if(panel._store._isDisposed){
create_panel()
}
const logoPath = vscode.Uri.file(
path.join(context.extensionPath, 'StackFinderLogo.png')
);
const logoSrc = panel.webview.asWebviewUri(logoPath);
await set_global_stack_questions_response(current_line_text)
if(global_questions.length == 0){
global_current_language=""
await set_global_stack_questions_response(current_line_text)
}
const stack_questions = await build_html_body(logoSrc)
panel.webview.html = build_webview(stack_questions)
panel.webview.onDidReceiveMessage(
async message => {
if(message.command == "code"){
activeEditor.edit(edit => {
let pos = new vscode.Position(activeEditor.selection.start.line,
activeEditor.selection.start.character)
edit.insert(pos, string_replacements_stack(message.text));
google_analytics('send_code')
});
// vscode.window.activeTerminal.show();
// vscode.window.activeTerminal.sendText("hello world", false)
}
if(message.command == "next_page"){
global_question_index += 1;
const new_html = await build_html_body(logoSrc)
panel.webview.html = build_webview(new_html)
};
if(message.command == "previous_page"){
global_question_index -= 1;
const new_html = await build_html_body(logoSrc)
panel.webview.html = build_webview(new_html)
};
if(message.command == "change_page"){
global_question_index = message.id;
const new_html = await build_html_body(logoSrc)
panel.webview.html = build_webview(new_html)
};
},
undefined,
context.subscriptions
);
});
context.subscriptions.push(disposable);
}
function deactivate() {}
module.exports = {
activate,
deactivate
}
function build_webview(body_content){
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Title</title>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.2.0/styles/a11y-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.2.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
<style>
a {
color: #E7B710;
}
a:hover {
color: #E1D89F;
}
h1, h2, h3, h4, h5, h6 {
margin-bottom: 15px;
margin-top: 0px;
padding-bottom: 0px;
padding-top: 0px;
}
body {
font-size: 1.0rem;
font-family:arial;
}
.btn {
text-decoration: none;
border: 1px solid rgb(146, 148, 248);
position: relative;
overflow: hidden;
background: none;
font-size: .70rem;
padding: .2rem .2rem;
color: #f4f4f4;
}
.btn:hover {
box-shadow: 1px 1px 10px 5px rgba(146, 148, 248, 0.4);
}
.btn:before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
120deg,
transparent,
rgba(146, 148, 248, 0.4),
transparent
);
transition: all 650ms;
}
.btn:hover:before {
left: 100%;
}
</style>
</head>
<body>
` + body_content + `
<script>
const vscode = acquireVsCodeApi();
$("button").click(function() {
if($(this).hasClass( "btn-send-to-editor" )){
vscode.postMessage({command: 'code',text: $(this).next().next().html()})
}
if($(this).hasClass( "btn-next-page" )){
vscode.postMessage({command: 'next_page'})
}
if($(this).hasClass( "btn-previous-page" )){
vscode.postMessage({command: 'previous_page'})
}
if($(this).hasClass( "btn-show-results" )){
if($("#title_popup").css("display")=="none"){
$("#title_popup").css("display", "block")
}else{
$("#title_popup").css("display", "none")
}
}else{
$("#title_popup").css("display", "none")
}
});
$(".question").click(function() {
vscode.postMessage({command: 'change_page',id: $(this).attr("id")})
$("#title_popup").css("display", "none")
});
</script>
</body>
</html>`
}
function align_languages(){
if(global_current_language == "plaintext"){
global_current_language=""
}
if(global_current_language == "cpp"){
global_current_language="c++"
}
if(global_current_language == "shellscript"){
global_current_language="shell"
}
if(global_current_language == "cuda-cpp"){
global_current_language="c++"
}
if(global_current_language == "csharp"){
global_current_language="c#"
}
if(global_current_language == "fsharp"){
global_current_language="f#"
}
if(global_current_language == "perl6"){
global_current_language="perl"
}
if(global_current_language == "pip-requirements"){
global_current_language="python"
}
if(global_current_language == "ignore"){
global_current_language=""
}
if(global_current_language == "jsonc"){
global_current_language="json"
}
if(global_current_language == "typescriptreact"){
global_current_language="reactjs"
}
if(global_current_language == "javascriptreact"){
global_current_language="reactjs"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment