vscode API: get Position of last character of line - visual-studio-code

Following up on this still unanswered question regarding VS Code Extensions with the VS Code API. I didn't answer it because it specifically asked for a solution using the with method of the Position object. I couldn't make that work, nor was I able to loop through the object to get the last character. Trying to manipulate the selection with vscode.commands.executeCommand didn't work either, because vscode.window.activeTextEditor doesn't appear to reflect the actual selection in the window as soon as the Execution Development Host starts running. The only solution I could find was the hoop-jumping exercise below, which gets the first character of one line and the first character of the next line, sets a Range, gets the text of that Range, then reduces the length of that text string by 1 to get the last character of the previous line.
function getCursorPosition() {
const position = editor.selection.active;
curPos = selection.start;
return curPos;
}
curPos = getCursorPosition();
var curLineStart = new vscode.Position(curPos.line, 0);
var nextLineStart = new vscode.Position(curPos.line + 1, 0);
var rangeWithFirstCharOfNextLine = new vscode.Range( curLineStart, nextLineStart);
var contentWithFirstCharOfNextLine = editor.document.getText(rangeWithFirstCharOfNextLine);
var firstLineLength = contentWithFirstCharOfNextLine.length - 1;
var curLinePos = new vscode.Position(curPos.line, firstLineLength);
var curLineEndPos = curLinePos.character;
console.log('curLineEndPos :>> ' + curLineEndPos);
I'm obviously missing something - it can't be impossible to get the last character of a line using the VSCode API without mickey-mousing the thing like this. So the question is simply, what is the right way to do this?

Once you have the cursor Position the TextDocument.lineAt() function returns a TextLine. From which you can get its range and that range.end.character will give you the character number of the last character. - not including the linebreak which if you want to include that see TextLine.rangeIncludingLineBreak().
const editor = vscode.window.activeTextEditor;
const document = editor.document;
const cursorPos = editor.selection.active;
document.lineAt(cursorPos).range.end.character;
Note (from [TextDocument.lineAt documentation][1] ):
Returns a text line denoted by the position. Note that the returned
object is not live and changes to the document are not reflected.

Related

How to convert Date and Time with mailmerge google slides from sheets as it is in cell

I have created a table with which I can record our check in times of our employees with the help of a generated Qr code in each line.The data in the table is generated as slides and converted into pdf. For this I use a script that I got to work with your help and it works. Here I would like to thank you especially #tanaike.
My problem is that the date and time are not copied to the slides to be generated as indicated in the cell but completely with Central European time and I added in the script to look in column if its empty to generate the slide. If it's not empty don't do anything. As I said everything is working except this two things.
I must confess I did not try to correct it somehow because I had already shot the script and I made some here despair. It would be really great if you write me the solutions and I can take them over. I will share the spreadsheet with you and the screenshot with ae and time. Thanks for your time and effort to help people like us; we are really trying.
As another approach, when I saw your question, I thought that if your Spreadsheet has the correct date values you expect, and in your script, you are retrieving the values using getValues, getValues is replaced with getDisplayValues(), it might be your expected result.
When I saw your provided sample Spreadsheet, I found your current script, when your script is modified, how about the following modification?
From:
var sheetContents = dataRange.getValues();
To:
sheetContents = dataRange.getDisplayValues();
Note:
When I saw your sample Spreadsheet, it seems that the column of the date has mixed values of both the string value and the date object. So, if you want to use the values as the date object using getValues, please be careful about this.
Reference:
getDisplayValues()
Added:
About your 2nd question of I mean that when a slide has been generated, the script saves the link from the slide in column D if the word YES is in column L. How do I make the script create the slide if there is JA in the column L and there is no link in column D. is a link in column D, the script should not generate a slide again. Thus, the script should only generate a slide if column D is empty and at the same time the word JA is in column L., when I proposed to modify from if (row[2] === "" && row[11] === "JA") { to if (row[3] == "" && ["JA", "YES"].includes(row[11])) {, you say as follows.
If ichanged as you descripted if (row[3] == "" && ["JA", "YES"].includes(row[11])) { i got this error. Syntax error: Unexpected token 'else' Line: 21 File: Code.gs
In this case, I'm worried that you might correctly reflect my proposed script. Because when I tested it, no error occurs. So, just in case, I add the modified script from your provided Spreadsheet as follows. Please test this.
Modified script:
function mailMergeSlidesFromSheets() {
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getDataRange();
sheetContents = dataRange.getDisplayValues(); // Modified
sheetContents.shift();
var updatedContents = [];
var check = 0;
sheetContents.forEach(function (row) {
if (row[3] == "" && ["JA", "YES"].includes(row[11])) { // Modified
check++;
var slides = createSlidesFromRow(row);
var slidesId = slides.getId();
var slidesUrl = `https://docs.google.com/presentation/d/${slidesId}/edit`;
updatedContents.push([slidesUrl]);
slides.saveAndClose();
var pdf = UrlFetchApp.fetch(`https://docs.google.com/feeds/download/presentations/Export?exportFormat=pdf&id=${slidesId}`, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } }).getBlob().setName(slides.getName() + ".pdf");
DriveApp.getFolderById("1tRC505IWtTj8nnPB7XyydvTtCJmOb6Ek").createFile(pdf);
// Or DriveApp.getFolderById("###folderId###").createFile(pdf);
} else {
updatedContents.push([row[3]]);
}
});
if (check == 0) return;
sheet.getRange(2, 4, updatedContents.length).setValues(updatedContents);
}
function todaysDateAndTime() {
const dt = Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"MM:dd:yyyy");
const tm = Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"HH:mm:ss");
Logger.log(dt);
Logger.log(tm);
}

String transformation for subject course code for Dart/Flutter

For interaction with an API, I need to pass the course code in <string><space><number> format. For example, MCTE 2333, CCUB 3621, BTE 1021.
Yes, the text part can be 3 or 4 letters.
Most users enter the code without the space, eg: MCTE2333. But that causes error to the API. So how can I add a space between string and numbers so that it follows the correct format.
You can achieve the desired behaviour by using regular expressions:
void main() {
String a = "MCTE2333";
String aStr = a.replaceAll(RegExp(r'[^0-9]'), ''); //extract the number
String bStr = a.replaceAll(RegExp(r'[^A-Za-z]'), ''); //extract the character
print("$bStr $aStr"); //MCTE 2333
}
Note: This will produce the same result, regardless of how many whitespaces your user enters between the characters and numbers.
Try this.You have to give two texfields. One is for name i.e; MCTE and one is for numbers i.e; 1021. (for this textfield you have to change keyboard type only number).
After that you can join those string with space between them and send to your DB.
It's just like hack but it will work.
Scrolling down the course codes list, I noticed some unusual formatting.
Example: TQB 1001E, TQB 1001E etc. (With extra letter at the end)
So, this special format doesn't work with #Jahidul Islam's answer. However, inspired by his answer, I manage to come up with this logic:
var code = "TQB2001M";
var i = course.indexOf(RegExp(r'[^A-Za-z]')); // get the index
var j = course.substring(0, i); // extract the first half
var k = course.substring(i).trim(); // extract the others
var formatted = '$j $k'.toUpperCase(); // combine & capitalize
print(formatted); // TQB 1011M
Works with other formats too. Check out the DartPad here.
Here is the entire logic you need (also works for multiple whitespaces!):
void main() {
String courseCode= "MMM 111";
String parsedCourseCode = "";
if (courseCode.contains(" ")) {
final ensureSingleWhitespace = RegExp(r"(?! )\s+| \s+");
parsedCourseCode = courseCode.split(ensureSingleWhitespace).join(" ");
} else {
final r1 = RegExp(r'[0-9]', caseSensitive: false);
final r2 = RegExp(r'[a-z]', caseSensitive: false);
final letters = courseCode.split(r1);
final numbers = courseCode.split(r2);
parsedCourseCode = "${letters[0].trim()} ${numbers.last}";
}
print(parsedCourseCode);
}
Play around with the input value (courseCode) to test it - also use dart pad if you want. You just have to add this logic to your input value, before submitting / handling the input form of your user :)

VS Code Extension: Appending text to the end of a document

How do you append text past then last line of a document in the editor using an extension?
I have an extension that either creates a new, untitled document, or it appends text to the bottom/end only of an existing document. It is the latter case that I am having trouble with. The extension does not depend on caret/cursor/selection position. I've tried both edit.insert() and edit.replace(), with various position/range values of getting past the last character, but my text addition is always placed above the last line:
Before operation (line 20 is the last line of the document):
What I get. Note the existing blank line is below the inserted text:
What I want. Note the existing blank line is above the inserted text.:
The code:
var lastLine = editor.document.lineAt(editor.document.lineCount - 1);
const replaceContent = 'Inserted Text';
editor.edit((editBuilder) => {
editBuilder.replace(lastLine.range.end, replaceContent);
});
I've found lots of SO articles for inserting/replacing text, just nothing specific to adding to the very end of an editor buffer.
// Get the text editor object
const editor = vscode.window.activeTextEditor;
// Get the current document object
const document = editor.document;
// Get the last line of the document
const lastLine = document.lineAt(document.lineCount - 1);
// Get the last line text range
const range = new vscode.Range(lastLine.range.start, lastLine.range.end);
// Append the text to the document
editor.edit((editBuilder) => {
editBuilder.insert(range.end, "\nAppended text");
});

Mirth String Handling

I'm using the code below to try and strip the file extension off the incoming file and replace it with "ACK";
Can't use .lastIndexOf as it's not available in Rhino.
var _filename = String(sourceMap.get('originalFilename'));
pos = -1;
var search = ".";
for(var i = 0; i < _filename.length - search.length; i++) {
if (_filename.substr(i, search.length) == search) {
pos = i;
}
}
logger.info('_pos:' + _pos);
Every time I get a pos value of -1
i.e. Last full stop position not found.
BUT if I hardcode the filename in as "2020049.259317052.HC.P.F3M147-G" it works perfectly.
Is it something to do with the sourceMap.get('originalFilename') supplying a non-string or different
character set ?
This was tested on mirth 3.5. Rhino does, in fact, have String.prototype.lastIndexOf for all mirth versions going back to at least mirth 3.0. You were correctly converting the java string from the sourceMap to a javascript string, however, it is not necessary in this case.
Java strings share String.prototype methods as long as there is not a conflict in method name. Java strings themselves have a lastIndexOf method, so that is the one being called in my answer. The java string is able to then borrow the slice method from javascript seamlessly. The javascript method returns a javascript string.
If for some reason the filename starts with a . and doesn't contain any others, this won't leave you with a blank filename.
var filename = $('originalFilename');
var index = filename.lastIndexOf('.');
if (index > 0) filename = filename.slice(0, index);
logger.info('filename: ' + filename);
That being said, I'm not sure why your original code wasn't working. When I replaced the first line with
var originalFilename = new java.lang.String('2020049.259317052.HC.P.F3M147-G');
var _filename = String(originalFilename);
It gave me the correct pos value of 22.
New Answer
After reviewing and testing what agermano said he is correct.
In your sample code you are setting pos = i but logging _pos
New answer var newFilename = _filename.slice(0, _filename.lastIndexOf('.'))
Older Answer
First, you are mixing JavaScript types and Java types.
var _filename = String(sourceMap.get('originalFilename'));
Instead, do
var _filename = '' + sourceMap.get('originalFilename');
This will cause a type conversion from Java String to JS string.
Secondly, there is an easier way to do what you are trying to do.
var _filenameArr = ('' + sourceMap.get('originalFilename')).split('.');
_filenameArr.pop() // throw away last item
var _filename = _filenameArr.join('.') // rejoin the array with out the last item
logger.info('_filename:' + _filename)

What ending marks should be used to extend a range to the end of the paragraph?

I am coding a word add-in and am not clear how to use the getNextTextRange(endingMarks, trimSpacing) method of the Range class.
Specifically I want to select a new Range starting from the currently selected range and going to the end of the paragraph.
The API for for the method states
endingMarks string[]
Required. The punctuation marks and/or other
ending marks as an array of strings
That's clear enough if you want to select up to the next comma, period or even space. But what ending marks should you use for a paragraph, a line break, or the end of the document?
I have tried using '\n', '^p' and '¶' but none of these seem to work.
var nr = selection.getNextTextRange(['¶'],true);
nr.load("isEmpty,text");
await context.sync();
console.log('nr='+nr.text);
} catch(e) {
console.log("error, soz");
console.log(e);
}
Given a document consisting of one paragraph of text with a blank paragraph after it, and the first word of the paragraph highlighted, this add-in throws a RichApi.Error
We couldn't find the item you requested.
I would expect it to instead print out the remainder of the paragraph.
If I understand your scenario, you can work with the ParagraphCollection.getFirst() method. Please install the Script Lab tool. Open the sample called "Get paragraph from insertion point" for an example.
Let me expand on rick-kirkham's answer in case it helps anyone else in my situation. This is basically the same answer as given here https://stackoverflow.com/a/51160690/4114053
Ok, here is my sample word document:
The rain in Spain falls. Mainly on the plain.
Alice stepped through the looking glass. What did she see?
And there endeth the lesson. Amen.
The user selects "stepped" in the second paragraph and I want to know what the text for the rest of the paragraph, from that word, says. I also want to know what the text up to that point says.
var doc = context.document;
var selection = doc.getSelection();
selection.load("isEmpty,text");
await context.sync();
console.log(selection.text); //prints stepped
var startRange = selection.getRange("start");
var endRange = selection.paragraphs.getLast().getRange("start");
var deltaRange = startRange.expandTo(endRange);
context.load(deltaRange);
await context.sync();
console.log(deltaRange.text); //prints "Alice"
startRange = selection.getRange("end");
endRange = selection.paragraphs.getLast().getRange("end");
deltaRange = startRange.expandTo(endRange);
context.load(deltaRange);
await context.sync();
console.log(deltaRange.text); // prints "through the looking glass. What did she see?"
My mistake was to get too caught up in trying to work out what "ending marks" might mean and how to use them to achieve this. (Although I still would like that spelled out in the API specification.)