Google(文档)Apps 脚本 - 无法检查光标是否在命名范围内

Google (Docs) Apps Script - Can't check if cursor on named range

我正在向文档中插入文本,每次插入的文本都会添加到一个命名范围内,以便我可以使用 getNamedRanges(NAME)getNamedRangesById(ID).

查找它们

现在我需要检查当前光标位置是否在命名范围内,我还没有弄清楚如何。

这个post是相似的:

但是当光标在命名范围 cursor.getElement() returns Text 对象上时,而不是命名范围。

如何确定光标当前是否位于命名范围内?

您提到的 post 中提出的解决方案意味着遍历您感兴趣的范围并检查您的范围元素之一是否等于光标所在的元素。

代码应如下所示:

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())
      }
    }))
  })
}
  • 您想确认当前光标位置是否在 Google 文档的命名范围内。
  • 您想使用 Google Apps 脚本实现此目的。

解决方法:

在此解决方法中,我通过比较 namedRange 和光标位置的段落索引来检查光标位置是否包含在 namedRange 中。

流量:

脚本流程如下

  1. 检索命名范围段落的索引。
    • 在这个示例脚本中,根据您的问题,使用了 namedRange ID。
    • 在这种情况下,可能会有多个段落,包括table、列表等。因此检索了 namedRange 中的所有索引。
  2. 检索光标所在段落的索引。
  3. 检索所选范围的段落索引。
    • 此示例脚本还检查所选范围是否在命名范围内。因为选中文字后,cursor变成了null.
  4. 如果光标或选定范围停留在命名范围内,myFunction() returns true
    • 如果光标或选定范围不在命名范围内,myFunction() returns false.
    • 你也可以在日志中确认。

示例脚本:

在使用此脚本之前,请设置命名范围 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.
  • 的脚本
  • 在这个脚本中,即使所选范围内只有一个索引被包含在namedRange中,也会返回true。关于这一点,请根据您的情况进行修改。
  • 在当前阶段,此脚本不考虑页眉和页脚部分。

参考文献:

已添加:

我从这种情况了解到,OP将命名范围设置为该段落。当我为此提出示例脚本时,我认为我正确理解了 OP 的目标。但是,从

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

如果OP将段落的一部分设置为命名范围,并且OP要检查光标是否包含在命名范围内,示例脚本如下。

示例脚本:

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.");
}
  • 当我发布答案时,Google Apps 脚本无法使用 V8 运行时。但是,现在可以使用 V8 运行时了。所以我使用 V8 运行时修改了脚本。请注意这一点。

因为我也需要这个,并且 none 之前的答案都可以正常工作(我测试了所有这些;请参阅我的评论),所以我编写了自己的实际工作的函数,见下文。

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.");
}

(稍作修改,这也可以作为 问题的答案。)