How can i customize code formatting for javascript? - visual-studio-code

For example i need spaces between parentheses and arguments:
var myPrivateMethod = function( name, value ) {
for ( var i = 0, l = this.stack.lenght; i < l; i-- ) {
this.stack[ i ][ name ] = value;
}
};
vs default VS Code JS formatting
var myPrivateMethod = function (name, value) {
for (var i = 0, l = this.stack.lenght; i < l; i--) {
this.stack[i][name] = value;
}
}

You can check this 'beautify' extension for visual studio code: https://marketplace.visualstudio.com/items?itemName=HookyQR.beautify
I am not sure it has the ability to add spaces between parentheses and arguments but it allows you customize code formatting.

Related

Render lit / lit-html TemplateResult as string

In lit/lit-html/lit-element, a standard component is the TemplateResult (usually HTMLTemplateResult), created like:
function renderMe(msg) {
return html`<div>Hello ${msg}!</div>`;
}
and of course the power and efficiency of the library is that subsequent calls will reuse the same <div> Element and only replace the changed fragments.
For testing the renderMe() function above, however, it would be helpful to be able to see the return value as a standard string, like:
assert.equal(RENDER_AS_STRING(renderMe('kimiko')), '<div>Hello kimiko!</div>');
and fix any bugs in the function before testing how it renders into the browser itself.
Is there a function like RENDER_AS_STRING either in lit itself or in a testing library? I have searched and not found one.
The result of execution contains html strings and values that alternate:
We can combine them in the same order:
function renderMe(msg) {
return html`<div>Hello ${msg}!</div>`;
}
const getRenderString = (data) => {
const {strings, values} = data;
const v = [...values, ''] // + last emtpty part
return strings.reduce((acc,s, i) => acc + s + v[i], '')
}
console.log(getRenderString(renderMe('SO')))
You can test it in the playground
And the recursive version
import {html, css, LitElement} from 'lit';
function renderMe(msg) {
return html`<p>Hello ${msg}!</p>`;
}
function renderBlock(msg) {
return html`<div>${renderMe(msg)}</div>`;
}
const getRenderString = (data) => {
const {strings, values} = data;
const v = [...values, ''].map(e => typeof e === 'object' ? getRenderString(e) : e )
return strings.reduce((acc,s, i) => acc + s + v[i], '')
}
document.getElementById('output').textContent = getRenderString(renderBlock('SO'))
#Daniil Loban's answer works great if the arguments are strings, but if they might themselves be TemplateResults or arrays of TemplateResults (which are all allowed by spec), it needs a more complex answer:
export function template_as_string(data) {
const {strings, values} = data;
const value_list = [...values, '']; // + last empty part
let output = '';
for (let i = 0; i < strings.length; i++) {
let v = value_list[i];
if (v._$litType$ !== undefined) {
v = template_as_string(v); // embedded Template
} else if (v instanceof Array) {
// array of strings or templates.
let new_v = '';
for (const inner_v of [...v]) {
new_v += template_as_string(inner_v);
}
v = new_v;
}
output += strings[i] + v;
}
return output;
}

VS Code extension how to edit in context?

Her is the class I use automatically capitalize true, false, ...
export class StUpdater {
private _lines: number;
private _strings: Array<string>;
constructor() {
this._lines = 0;
this._strings = ['true', 'false', 'exit', 'continue', 'return'];
}
Update(Cntx: boolean = false) {
let editor = window.activeTextEditor;
if (!editor || (editor.document.languageId != 'st')) {
window.showErrorMessage('No editor!')
return;
}
let doc = editor.document;
if (Cntx == false) {
if (this._lines >= doc.lineCount) {
this._lines = doc.lineCount;
return;
}
this._lines = doc.lineCount;
let AutoFormat = workspace.getConfiguration('st').get('autoFormat');
if (!AutoFormat) {
return;
}
}
let edit = new WorkspaceEdit();
for (let line = 0; line < doc.lineCount; line++) {
const element = doc.lineAt(line);
for (let i = 0; i < this._strings.length; i++) {
let str = this._strings[i];
let last_char = 0;
while (element.text.indexOf(str, last_char) >= 0) {
let char = element.text.indexOf(str, last_char);
last_char = char + str.length;
edit.replace(
doc.uri,
new Range(
new Position(line, char),
new Position(line, last_char)
),
str.toUpperCase()
);
}
}
}
return workspace.applyEdit(edit);
}
public dispose() {
}
}
This code works fine, but I do not want to replace it inside the string or comment. How do I do that? I cannot find preg version of replace and even if I do, in one line I do not know if it is comment or not if it is multiple line comment.
If I understand you correctly you want capitalize only certain elements (identifiers probably), but not words in comments or strings, correct? That requires to identify lexical elements in the text, which is a mapping of a range of letters to a lexical type. This is usually done by a lexer. It's not difficult to write one by hand which walks over the characters on top of your current processing and find those ranges that can be manipulated.

Google Spreadsheet - How to avoid sending email duplicates?

I am having an issue with a script. I used the following script from Google Developers Website in order to do a simple merge mail. See https://developers.google.com/apps-script/articles/mail_merge
I modified a bit the script so to prevent email duplicates. However, even if the script seems to work as it marks 'EMAIL_SENT' in each row every time an email is sent. It does not pay attention if the mail as already been marked and still send the mail.
I believe there is an error at line 16 "var emailSent = rowData[6];"
I would really appreciate if someone could help me. Whoever you are thanks in advance.
Here is the modified script :
var EMAIL_SENT = "EMAIL_SENT";
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheets()[0];
var dataRange = dataSheet.getRange(2, 1, dataSheet.getMaxRows() - 1, 7);
var templateSheet = ss.getSheets()[1];
var emailTemplate = templateSheet.getRange("A2").getValue();
var objects = getRowsData(dataSheet, dataRange);
for (var i = 0; i < objects.length; ++i) {
var Resume = DriveApp.getFilesByName('Resume.pdf') var Portfolio = DriveApp.getFilesByName('Portfolio.pdf') var rowData = objects[i];
var emailText = fillInTemplateFromObject(emailTemplate, rowData);
var emailSubject = "Architectural Internship";
var emailSent = rowData[6];
if (emailSent != EMAIL_SENT) {
MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText, {
attachments: [Resume.next(), Portfolio.next()]
});
dataSheet.getRange(2 + i, 7).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
}
function fillInTemplateFromObject(template, data) {
var email = template;
var templateVars = template.match(/\${\"[^\"]+\"}/g);
for (var i = 0; i < templateVars.length; ++i) {
var variableData = data[normalizeHeader(templateVars[i])];
email = email.replace(templateVars[i], variableData || "");
}
return email;
}
function getRowsData(sheet, range, columnHeadersRowIndex) {
columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
var numColumns = range.getEndColumn() - range.getColumn() + 1;
var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
var headers = headersRange.getValues()[0];
return getObjects(range.getValues(), normalizeHeaders(headers));
}
function getObjects(data, keys) {
var objects = [];
for (var i = 0; i < data.length; ++i) {
var object = {};
var hasData = false;
for (var j = 0; j < data[i].length; ++j) {
var cellData = data[i][j];
if (isCellEmpty(cellData)) {
continue;
}
object[keys[j]] = cellData;
hasData = true;
}
if (hasData) {
objects.push(object);
}
}
return objects;
}
function normalizeHeaders(headers) {
var keys = [];
for (var i = 0; i < headers.length; ++i) {
var key = normalizeHeader(headers[i]);
if (key.length > 0) {
keys.push(key);
}
}
return keys;
}
function normalizeHeader(header) {
var key = "";
var upperCase = false;
for (var i = 0; i < header.length; ++i) {
var letter = header[i];
if (letter == " " && key.length > 0) {
upperCase = true;
continue;
}
if (!isAlnum(letter)) {
continue;
}
if (key.length == 0 && isDigit(letter)) {
continue;
}
if (upperCase) {
upperCase = false;
key += letter.toUpperCase();
} else {
key += letter.toLowerCase();
}
}
return key;
}
// Returns true if the cell where cellData was read from is empty. // Arguments: // - cellData: string function isCellEmpty(cellData) {
return typeof(cellData) == "string" && cellData == "";
}
// Returns true if the character char is alphabetical, false otherwise. function isAlnum(char) { return char >= 'A' && char <= 'Z' || char >= 'a' && char <= 'z' || isDigit(char); }
// Returns true if the character char is a digit, false otherwise. function isDigit(char) { return char >= '0' && char <= '9'; }
Your code is really hard to read and the functions that return 2 or more objects make it even harder...you are using variable names that are also a bit confusing.... but that is probably a personal pov :-)
Anyway, I think I've found the issue: when you write var rowData = objects[i];
This "object" is actually the result of the getRowData function but if you look at this function, you'll see that it returns 2 objects, the first one being itself the result of another function (getObjects) ...
You are checking the value is the 6th element of the array which is actually an object and compare it to a string. The equality will never be true.
I didn't go further in the analyse since I found it really confusing ( as I already said) but at least you have a first element to check .
I would suggest you rewrite this code in a more simple way and use more appropriate variable names to help you while debugging.
I would recommend logging both values before executing to make sure they are the same. I would also guess that the email_sent and EMAIL_SENT are different data types. Can also try forcing the value to string for comparison.
To clarify:
logger.Log(emailSent);
logger.Log(EMAIL_SENT);
if (emailSent.toString() != EMAIL_SENT.toString())
{...
Error is in this line of code -
var dataRange = sheet.getRange(startRow, 1, numRows, 2)
It's considering only 2 columns in the range. Changed 2 to 3 and it worked fine.

Append divs changing his content dynamic

I have a dif called cdefualt that has some inputs from a form inside of it and I want to do something like this to clone it and change that input names:
var i = 2;
function add() {
var item = $('#cdefault').clone();
item.attr({'style': ''});
$xpto = 'gtitle'+i;
$xpto2 = 'gmessage'+i;
item.id = $xpto;
$('#'+$xpto+' input[id="gtitle1"]').attr('name', $xpto);
$('#'+$xpto+' textarea[id="gmessage1"]').attr('name',$xpto2);
$(item).appendTo('#ccontainer');
i++;
}
But this doesnt work. I've tried this already as well but it only works twice (for the original and first clone):
var i = 2;
function add() {
var item = $('#cdefault').clone();
item.attr({'style': ''});
$xpto = 'gtitle'+i;
$xpto2 = 'gmessage'+i;
$('#cdefault input[id="gtitle1"]').attr('id', $xpto);
$('#cdefault textarea[id="gmessage1"]').attr('id',$xpto2);
$('#cdefault input[name="gtitle1"]').attr('name', $xpto);
$('#cdefault textarea[name="gmessage1"]').attr('name', $xpto2);
$(item).appendTo('#ccontainer');
i++;
}
Even tryed this way:
function add() {
$xpto = 'gtitle'+i;
$xpto2 = 'gmessage'+i;
var div = document.getElementById('cdefault');
clone = div.cloneNode(true); // true means clone all childNodes and all event handlers
clone.id = $xpto;
clone.style.display = '';
$("#"+$xpto+" input[id='gtitle1']").attr('name', $xpto);
$("#"+$xpto+" textarea[id='gmessage1']").attr('name',$xpto2);
document.getElementById('ccontainer').appendChild(clone);
i++;
}
http://jsfiddle.net/Theopt/xNfSd/
fixed. changed cdefault id to id0 and this java script:
var i = 2;
var c = 0;
function add() {
$xpto = 'gtitle'+i;
$xpto2 = 'gmessage'+i;
var klon = $( '#id'+ c );
klon.clone().attr('id', 'id'+(++c) ).insertAfter( '#inserthere' );
document.getElementById('id'+(c)).style.display = '' ;
$("#id"+(c)+" input[id='gtitle1']").attr('name', $xpto);
$("#id"+(c)+" textarea[id='gmessage1']").attr('name',$xpto2);
i++;
}

execCommand without a selection? (set font, size etc)

I can get my custom WYSIWYG editor to apply styling to selected text no problem:
pnlDocumentEditor_IFrame.document.execCommand(cmd, ui, opt)
.. but what I need to be able to do is allow the user to set a font, or font size, or bold etc so that text typed AFTER this command is issued will have that style applied.
Is this possible? All execCommands I've tried seem to only work on selected text.
Appling execCommand on certain element, WITHOUT selecting it with mouse, can be done with this function:
jsFiddle example
function execCommandOnElement(el, commandName, value) {
if (typeof value == "undefined") {
value = null;
}
if (typeof window.getSelection != "undefined") {
// Non-IE case
var sel = window.getSelection();
// Save the current selection
var savedRanges = [];
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
savedRanges[i] = sel.getRangeAt(i).cloneRange();
}
// Temporarily enable designMode so that
// document.execCommand() will work
document.designMode = "on";
// Select the element's content
sel = window.getSelection();
var range = document.createRange();
range.selectNodeContents(el);
sel.removeAllRanges();
sel.addRange(range);
// Execute the command
document.execCommand(commandName, false, value);
// Disable designMode
document.designMode = "off";
// Restore the previous selection
sel = window.getSelection();
sel.removeAllRanges();
for (var i = 0, len = savedRanges.length; i < len; ++i) {
sel.addRange(savedRanges[i]);
}
} else if (typeof document.body.createTextRange != "undefined") {
// IE case
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.execCommand(commandName, false, value);
}
}
And example usage:
var testDiv = document.getElementById("test");
execCommandOnElement(testDiv, "Bold");
execCommandOnElement(testDiv, "ForeColor", "red");
Source: set execcommand just for a div