Reputation: 1498
I am inserting text into a document and each text insertion is added to a named range so that I can look them all up with getNamedRanges(NAME)
and getNamedRangesById(ID)
.
Now I need to check if the current cursor position is on a named range and I have yet to figure out how.
This post is similar: How to determine the named range from position in Google Docs through Google Apps Script
But when the cursor is on a namedrange cursor.getElement()
returns Text
object, not the named range.
How can I determine if the cursor is currently positioned on a named range?
Upvotes: 3
Views: 476
Reputation: 201378
In this workaround, I checked whether the cursor position is included in the namedRange by comparing the indexes of paragraph of both the namedRange and the cursor position.
The flow of the script is as follows.
cursor
becomes null
.myFunction()
returns true
.
myFunction()
returns false
.Before you use this script, please set the namedRange ID.
function myFunction() {
var nameRangeId = "###"; // Please set namedRange ID here.
var getIndex = function(doc, e) {
while (e.getParent().getType() != DocumentApp.ElementType.BODY_SECTION) e = e.getParent();
return doc.getBody().getChildIndex(e);
};
var doc = DocumentApp.getActiveDocument();
// For namedRange
var namedRange = doc.getNamedRangeById(nameRangeId);
if (namedRange) {
var indexOfNamedRange = namedRange.getRange().getRangeElements().map(function(e) {return getIndex(doc, e.getElement())});
} else {
throw new Error("No namedRange.");
}
var name = namedRange.getName();
// For cursor
var cursor = doc.getCursor();
if (cursor) {
var indexOfCursor = getIndex(doc, cursor.getElement());
if (~indexOfNamedRange.indexOf(indexOfCursor)) {
Logger.log("Inside of %s", name);
return true;
}
Logger.log("Outside of %s", name);
return false;
}
// For select
var select = doc.getSelection();
if (select) {
var indexOfSelect = select.getRangeElements().map(function(e) {return getIndex(doc, e.getElement())});
if (indexOfSelect.some(function(e) {return ~indexOfNamedRange.indexOf(e)})) {
Logger.log("Inside of %s", name);
return true;
}
Logger.log("Outside of %s", name);
return false;
}
throw new Error("No cursor and select.");
}
// For select
.true
is returned. About this, please modify for your situation.I had understood that from this situation, OP has set the named range to the paragraph. When I proposed a sample script for this, I thought that I correctly understood OP's goal. But, from gaspar's following comment,
this only shows whether the cursor is in the same element as the named range, but in case of named range partial text it gives a false positive finding if the cursor is in the same element but not in the same text part
If OP sets the part of the paragraph as the named range, and OP wants to check whether the cursor is included in the named range, the sample script is as follows.
function myFunction() {
var nameRangeId = "###"; // Please set namedRange ID here.
var getIndex = function (doc, e) {
while (e.getParent().getType() != DocumentApp.ElementType.BODY_SECTION) e = e.getParent();
return doc.getBody().getChildIndex(e);
};
var doc = DocumentApp.getActiveDocument();
// For namedRange
var namedRange = doc.getNamedRangeById(nameRangeId);
if (namedRange) {
var indexOfNamedRange = namedRange.getRange().getRangeElements().map(e => ({ idx: getIndex(doc, e.getElement()), start: e.getStartOffset(), end: e.getEndOffsetInclusive() }));
} else {
throw new Error("No namedRange.");
}
var name = namedRange.getName();
// For cursor
var cursor = doc.getCursor();
if (cursor) {
var indexOfCursor = getIndex(doc, cursor.getElement());
var offset = cursor.getOffset();
if (indexOfNamedRange.some(({ idx, start, end }) => idx == indexOfCursor && ((start == -1 && end == -1) || (offset > start && offset < end)))) {
Logger.log("Inside of %s", name);
return true;
}
Logger.log("Outside of %s", name);
return false;
}
// For select
var select = doc.getSelection();
if (select) {
var indexOfSelect = select.getRangeElements().map(e => ({ idx: getIndex(doc, e.getElement()), start: e.getStartOffset(), end: e.getEndOffsetInclusive() }));
if (indexOfSelect.some(e => indexOfNamedRange.some(({ idx, start, end }) => idx == e.idx && ((start == -1 && end == -1) || ((e.start > start && e.start < end) || (e.end > start && e.end < end)))))) {
Logger.log("Inside of %s", name);
return true;
}
Logger.log("Outside of %s", name);
return false;
}
throw new Error("No cursor and select.");
}
Upvotes: 1
Reputation: 1068
Since I also needed this and none of the previous answers work correctly (I tested them all; see my comments), I wrote my own function that actually works, see below.
const cursorIndex = getIndex(cursor.getElement())
let found = false;
let rangeIndex = 0;
// note: to search for any named range, just omit the name
// (hence just give "doc.getNamedRanges()")
// then you can get the name of the found named range via "getName()"
for (const rangeEntry of doc.getNamedRanges('the_named_range_name')) {
for (const element of rangeEntry.getRange().getRangeElements()) {
rangeIndex = getIndex(element.getElement());
if (cursorIndex === rangeIndex) {
if (element.isPartial()) {
let cursorOffset = cursor.getSurroundingTextOffset()
if (cursorOffset >= element.getStartOffset() && cursorOffset <= element.getEndOffsetInclusive() + 1) {
found = true;
break;
}
} else {
found = true;
break;
}
}
}
if (found || rangeIndex > cursorIndex) {
break;
}
}
if (found) {
DocumentApp.getUi().alert("There is a named range here.");
}
(With small modifications, this could also be an answer to this question.)
Upvotes: 0
Reputation: 26796
The solution proposed in the post to which you refer implies looping through your range of interest and checking if one of your range elements equals the element on which the cursor lies.
The code should look like this:
function myFunction() {
var doc = DocumentApp.getActiveDocument();
var cursor = doc.getCursor();
var el=cursor.getElement().asText().getText();
var range;
//specify the name of the range of interest in getNamedRanges()
doc.getNamedRanges('testy').forEach(function(rangeEntry){
(rangeEntry.getRange().getRangeElements().forEach(function(element){
var child=element.getElement().getText();
if(child==el){
Logger.log("Cursor is on named range "+rangeEntry.getName())
}
}))
})
}
Upvotes: 1