VSCode exstension works while debugging but not once packaged into VSIX - javascript

I was trying to learn a little bit about building extensions for vscode and I've hit a road block. My extension is only supposed to paste from clipboard with a little extra text depending on the file type. (for quick debugging purposes) The problem is I have this working while debugging but once it is packaged I get a rejected promise not handled within 1 second: Error: TextEditor#edit not possible on closed editors
This makes sense to me but I can't figure out how to keep the activeTextEditor updated nor do I know why it would act different once packaged. I've gotta be missing something simple I'm sure.
function activate(context) {
const dbugText = vscode.commands.registerCommand('extension.dbug', () => {
vscode.env.clipboard.readText().then((text) => {
let editor = vscode.window.activeTextEditor;
if(!editor){
return;
}
var document = editor.document;
var selection = editor.selection;
var fileType = document.fileName.split('.')[1];
console.log(text);
if (fileType == 'php') {
text = "dbug(" + text + ");";
} else if (fileType == 'js') {
text = "console.log(" + text + ");";
} else {
console.log('Unknown file type');
return;
}
vscode.window.showInformationMessage(text);
editor.edit((editBuilder) => {
return editBuilder.insert(new vscode.Position(selection.start.line, selection.start.character), text);
})
.catch(err => console.error(err.message));
})
.catch(err => console.error(err.message));
});
context.subscriptions.push(dbugText);
}

Related

VSCode API: Editor.edit editbuilder.replace fails without reason (possibly due to formatting?)

In my extension I want to edit the document on a few specific document edits.
My actual use case is a bit complicated so I have created a minimal example. The code below listens for any document edit. If the word "hello" exists in the edit (i.e. the user pasted some code that contains the word "hello") then we replace the change range with the pasted text but just make it upper case.
We also console.log if the edit was successful, and any potential reason the edit was rejected.
vscode.workspace.onDidChangeTextDocument(event => {
for (const change of event.contentChanges) {
if (change.text.includes("hello")) {
activeEditor.edit(editBuilder => {
editBuilder.replace(change.range, change.text.toUpperCase());
}).then(
value => console.log("SUCCESS: "+value),
reason => console.log("FAIL REASON: "+reason),
);
}
}
});
A working example would be selecting some text in a document and pasting in the text const hello = 5;. As expected, the extension replaces the text with CONST HELLO = 5; and logs SUCCESS: true.
But when I paste in some text that automatically get formatted I run into problems. If I were to paste in:
const hello = 5;
const lol = 10;
const lmao = 20;
Including all the whitespaces/tabs, then vscode wants to "format" or correct my lines, i.e. remove the whitespace. So the resulting text will be:
const hello = 5;
const lol = 10;
const lmao = 20;
The extension tries to make it uppercase still but only prints SUCCESS: false. No reason is logged at all; the reject function is not executed.
Why does the edit not succeed? Should I await the other edits somehow or keep re-trying the edit until it succeeds? Am I logging the rejection incorrectly?
In case it helps, here is code I use - I found it better to have the editBuilder outside the loop. I think you can adapt it for your purposes:
editor.edit( (editBuilder) => {
// put your for (const change of event.contentChanges) {} here
for (const match of matches) {
resolvedReplace = variables.buildReplace(args, "replace", match, editor.selection, null, index);
const matchStartPos = document.positionAt(match.index);
const matchEndPos = document.positionAt(match.index + match[0].length);
const matchRange = new vscode.Range(matchStartPos, matchEndPos);
editBuilder.replace(matchRange, resolvedReplace);
}
}).then(success => {
if (!success) {
return;
}
if (success) { ... do something here if you need to }
});
One solution is just to "keep trying again". I do not like this solution, but it is a solution nevertheless, and it currently works for my use-case.
async function makeReplaceEdit(range: vscode.Range, text: string, maxRetries = 10) {
for (let i = 0; i <= maxRetries; i++) {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const success = await editor.edit(editBuilder => {
editBuilder.replace(
range,
text
);
}, { undoStopBefore: false, undoStopAfter: false });
if (success) break;
}
};
vscode.workspace.onDidChangeTextDocument((event) => {
// See if any change contained "hello"
let foundHello = false;
for (const change of event.contentChanges) {
if (change.text.includes("hello")) {
foundHello = true;
}
}
if (foundHello) {
console.log("inside1");
const editor = vscode.window.activeTextEditor;
if (!editor) return;
makeReplaceEdit(editor.document.lineAt(0).range, "Change");
}
});

I want to read a certain amount of lines from a text file, and with them to create an object after reading the lines using JavaScript

okay, so this is my requirement: The application must read each line from the text file. A class can be used so that after reading a certain number of lines an object is created (in this way the application is more clearly structured). After reading a data set that corresponds to a student's data, it will add this data set to a string (separate so that it is presented in consecutive rows).
So i have these information of 2 students which are one under the other like in the picture below but without the name address etc.(it doesn't show quite right in here).
Ebonie Rangel
7175 Yukon Street
(507) 833-3567
Geography
Keenan Ellwood
2 Elm Lane
(894) 831-6482
History
which are in that file. and after reading every line, I am supposed to add Name in front of the first line, Address in front of the second.. phone and Course and so on.
The result should look like this:
This is what i have for now (I have to use Fetch to get to the file, async and await. or with Promise)
let button = document.getElementById("text-button");
let textArea = document.getElementById("text-area");
button.addEventListener("click", function () {
getData();
});
//cod fetch
async function getData() {
try {
let response = await fetch('fileName.txt');
if (response.status !== 200) {
throw new Error("Error while reading file");
}
let text = await response.text();
textArea.innerHtml = text;
} catch (err) {
textArea.innerHTML = 'Problem occurred: ' + err.message;
}
}
please help! I am stuck since forever on this.
Since you're pulling from a .txt file I think its important to understand the line breaks being used in the file. Here's a decent link I found that says all you need at the top of the article: End of Line or Newline Characters
I opened up the .txt file in Notepad++ like the article recommended and saw this:
The [CR][LF] being displayed after each line means that the newline characters used are \r\n.
When you understand that you realize you can use those line breaks to separate your string at each line break.
Here's the MDN for String.split() String.prototype.split()
String.split('\r\n') will return an Array of items, specifically the strings that were between but not including the \r\n characters.
Let's add this to the getData function:
let button = document.getElementById("text-button");
let textArea = document.getElementById("text-area");
button.addEventListener("click", function () {
getData();
});
//cod fetch
async function getData() {
try {
let response = await fetch('fileName.txt');
if (response.status !== 200) {
throw new Error("Error while reading file");
}
let text = await response.text();
//New stuff:
let arrayOfText = text.split('\r\n');
//Now we could add what we want before the text.
//We need to do every 4 lines so lets use this as a chance to learn % better
arrayOfText = arrayOfText.map((textItem, index) => {
let remainder = (index) % 4 //This will return 0, 1, 2, 3
//switch but you could use anything
switch (remainder) {
case 0:
textItem = 'Name: ' + textItem + '\r\n';
break;
case 1:
textItem = 'Address: ' + textItem + '\r\n';
break;
case 2:
textItem = 'Phone: ' + textItem + '\r\n';
break;
case 3:
textItem = 'Course: ' + textItem + '\r\n\r\n'; //two here to separate the groups
break;
//we need a default so lets make it just return textItem if something goes wrong
default:
break;
};
//Our new array has all the info so we can use
//Array.prototype.join('') with an empty string to make it a string.
//We need those old line breaks though so lets put them
//in the switch returns above.
text = arrayOfText.join('');
//End of my changes/////////////
textArea.innerHtml = text;
} catch (err) {
textArea.innerHTML = 'Problem occurred: ' + err.message;
}
}
I hope this works out for you. Its not the most glamorous solution but its a good learning solution because it uses only things you learn early on in your studies.
Let me know if I can clarify anything!
async function getData() {
try {
let response = await fetch('https://v-dresevic.github.io/Advanced-JavaScript-Programming/data/students.txt');
if (response.status !== 200) {
throw new Error("Error while reading file");
}
let text = await response.text();
const lines = text.split('\n');
const CHUNK_SIZE = 4;
textArea.innerHTML = new Array(Math.ceil(lines.length / CHUNK_SIZE))
.fill()
.map(_ => lines.splice(0, CHUNK_SIZE))
.map(chunk => {
const [Name, Address, Phone, Course] = chunk;
return {Name, Address, Phone, Course};
})
.reduce((text, record) => {
text += Object.keys(record).map(key => `${key} ${record[key]}`).join('\n') + '\n';
return text;
}, '');
} catch (err) {
textArea.innerHTML = 'Problem occurred: ' + err.message;
}
}

How to improve flags in function?

I have the following code. I dislike this only aesthetically:
public createDocument() {
try {
if (this.isOpenDialog) throw 'Dialog already opened!';
this.loading = true;
this.isOpenDialog = true;
this.documentService.loadDocuments(this.application.reglamentid).then((response) => {
this.documentService.setTypeDocuments(response.typedocuments);
this.loading = false;
this.documentDialogFormService.open({ title: 'Документ', application: this.application }).subscribe(() => {
this.isOpenDialog = false;
});
});
} catch (e) {
console.log('ERROR: ' + e);
}
}
As you can see there are two flags: this.loading and this.isOpenDialog. First controls opening dialog, The second indicates loading.
Is it possible somehow to improve it?
The pattern you have is typically what people use since it is two distinct states for two different things. Reason why is the dialog could be loading and be shown at the same time. That means both could be true. Depending on what you do with it might make this the optimal solution.
If you want you could have one state that holds both. It might seem cleaner, but as you can see the check to see if it is opened is a bit more confusing since it has to check two states.
enum ModalStates {
Closed,
Loading,
Opened
}
public createDocument() {
try {
if (this.modalState === ModalStates.Loading || this.modalState === ModalStates.Opened ) throw 'Dialog already opened!';
this.modalState = ModalStates.Loading;
this.documentService.loadDocuments(this.application.reglamentid).then((response) => {
this.documentService.setTypeDocuments(response.typedocuments);
this.modalState = ModalStates.Opened
this.documentDialogFormService.open({
title: 'Документ',
application: this.application
}).subscribe(() => {
this.modalState = ModalStates.Closed
});
});
} catch (e) {
console.log('ERROR: ' + e);
}
}

Finding Sub Spans within Element XPath using Protractor Framework

I have the following lines of code:
.then(function click(services) {
var tryItElement;
if (services.length < 1) {
return;
}
buttonElement = element(by.xpath('//div[#class="my-class" and contains(., \'' + services[0].title + '\')]//span[contains(., \'Button Text\')]'));
return browser.wait(protractor.ExpectedConditions.elementToBeClickable(buttonElement), getWaitTime())
.then(function() {
return buttonElement.getWebElement();
})
.then(function(buttonElement) {
var protocol = url.parse(services[0].actionUrl).protocol;
if (protocol === null) {
throw new Error('expected ' + protocol + ' not to be null');
}
})
.then(function() {
return buttonElement.click();
})
.then(function() {
return browser.wait(helper.ExpectedConditions.responseCompleted(proxy, services[0].actionUrl), getWaitTime());
})
.then(function() {
return browser.get(browser.baseUrl);
})
.then(function() {
return click(services.slice(1));
})
})
.catch(fail)
.then(done);
I expect the first to find an element on my page with the class that contains some text as specified from services[0].title. It does and the following line finds a span within that element that contains the text Button Text.
I have this in a loop and for some reason, the buttonElement stays the same even though performing the action manually in the Chrome dev tools gives me the expected results.
Any other ways of trying this or suggestions? I can clarify some more if needed.

programmatically use w3c css/html validators with custom html

edit
I've got it working for CSS, only HTML validation to go.
let css = Array.from(document.styleSheets)
.reduce((combinedsheet,sheet) => sheet.rules?
combinedsheet + Array.from(sheet.rules)
.reduce((p,c) => p+c.cssText+'\n', ''):
combinedsheet, '')
try {
document.querySelector('a.validation.css').href =
'https://jigsaw.w3.org/css-validator/validator?text=' +
encodeURIComponent(css) +
'&profile=css3&usermedium=all&warning=1&vextwarning='
document.querySelector('a.validation.html').href =
'https://validator.w3.org/nu/?doc=' +
encodeURIComponent(document.querySelector('html'))
} catch (e) {
// this will fail before page fully loads, and we can be silent about that
}
edit #2
I've got it working. The only problem is that this uses a "popup" instead of opening a window silently like target="blank" would. I'm using an onlick method:
get_html_validation: function () {
let fd = new FormData()
fd.append('fragment', '<!DOCTYPE html>' +
document.querySelector('html').outerHTML)
fd.append('prefill', 0)
fd.append('doctype', 'Inline')
fd.append('prefill_doctype', 'html401')
fd.append('group', 0)
axios.post("https://validator.w3.org/nu/#textarea", fd)
.then(response => {
let win=window.open('about:blank')
console.log(win)
with(win.document)
{
open()
write(response.data)
close()
}
})
.catch(e => console.error(e))
}
original
I'd like to use these validators programmatically:
https://validator.w3.org/nu
http://jigsaw.w3.org/css-validator/validator
— they work just fine if you pass a URL as a parameter, so long as the document doesn't have javascript that manipulates the dom. But... mine does.
How can I hand these sites custom html to check using javascript in the browser? I'm looking to do something like:
onValidateDOM = () => {
let toValidate = '<!DOCTYPE html>' + document.querySelector('html').outerHTML
let returnURLs = []
returnURLs.push(w3cCSS.validate(toValidate), w3cHTML.validate(toValidate)
return returnURLs
}
Set toValidate passed to encodeURIComponent() as value to doc= query
fetch(`https://validator.w3.org/nu/?doc=${encodeURIComponent(toValidate)}`)
.then(response => response.text())
.then(text => console.log(text));

Categories

Resources