Utiliser des plages nommées

L'API Google Docs vous permet d'utiliser des plages nommées pour simplifier certaines tâches d'édition.

Lorsque vous créez une plage nommée, vous identifiez une section du document à laquelle vous pourrez faire référence ultérieurement. Les index de la plage nommée sont automatiquement mis à jour à mesure que du contenu est ajouté ou supprimé dans le document. Cela simplifie la façon dont vous localisez le texte à modifier à l'avenir, car vous n'avez pas besoin de suivre les modifications ou de parcourir le document. Au lieu de cela, vous pouvez obtenir un emplacement à modifier en lisant la plage nommée et en utilisant ses index.

Par exemple, supposons que vous créiez une plage nommée dans laquelle la chaîne "description du produit" doit apparaître dans un document :

Cela vous permet de remplacer la description : vous obtenez simplement les index de début et de fin de la plage nommée, puis vous mettez à jour le texte entre ces index avec votre nouveau contenu.

L'exemple de code suivant montre comment implémenter une fonction d'assistance pour remplacer le contenu d'une plage nommée dans le premier onglet du document, où la plage nommée a été créée précédemment à l'aide de CreateNamedRangeRequest. Pour remplacer les plages nommées de tous les onglets, ce code peut être augmenté pour itérer sur tous les onglets. Pour en savoir plus et obtenir des exemples de code, consultez Utiliser des onglets.

Java

/** Replaces the text in existing named ranges. */
static void replaceNamedRange(Docs service, String documentId, String rangeName, String newText)
    throws IOException {
  // Fetch the document to determine the current indexes of the named ranges.
  Document document =
      service.documents().get(documentId).setIncludeTabsContent(true).execute();

  // Find the matching named ranges in the first tab of the document.
  NamedRanges namedRangeList =
      document.getTabs()[0].getDocumentTab().getNamedRanges().get(rangeName);
  if (namedRangeList == null) {
    throw new IllegalArgumentException("The named range is no longer present in the document.");
  }

  // Determine all the ranges of text to be removed, and at which indexes the replacement text
  // should be inserted.
  List<Range> allRanges = new ArrayList<>();
  Set<Integer> insertIndexes = new HashSet<>();
  for (NamedRange namedRange : namedRangeList.getNamedRanges()) {
    allRanges.addAll(namedRange.getRanges());
    insertIndexes.add(namedRange.getRanges().get(0).getStartIndex());
  }

  // Sort the list of ranges by startIndex, in descending order.
  allRanges.sort(Comparator.comparing(Range::getStartIndex).reversed());

  // Create a sequence of requests for each range.
  List<Request> requests = new ArrayList<>();
  for (Range range : allRanges) {
    // Delete all the content in the existing range.
    requests.add(
        new Request().setDeleteContentRange(new DeleteContentRangeRequest().setRange(range)));

    if (insertIndexes.contains(range.getStartIndex())) {
      // Insert the replacement text.
      requests.add(
          new Request()
              .setInsertText(
                  new InsertTextRequest()
                      .setLocation(
                          new Location()
                              .setSegmentId(range.getSegmentId())
                              .setIndex(range.getStartIndex())
                              .setTabId(range.getTabId()))
                      .setText(newText)));

      // Re-create the named range on the new text.
      requests.add(
          new Request()
              .setCreateNamedRange(
                  new CreateNamedRangeRequest()
                      .setName(rangeName)
                      .setRange(
                          new Range()
                              .setSegmentId(range.getSegmentId())
                              .setStartIndex(range.getStartIndex())
                              .setEndIndex(range.getStartIndex() + newText.length())
                              .setTabId(range.getTabId()))));
    }
  }

  // Make a batchUpdate request to apply the changes, ensuring the document hasn't changed since
  // we fetched it.
  BatchUpdateDocumentRequest batchUpdateRequest =
      new BatchUpdateDocumentRequest()
          .setRequests(requests)
          .setWriteControl(new WriteControl().setRequiredRevisionId(document.getRevisionId()));
  service.documents().batchUpdate(documentId, batchUpdateRequest).execute();
}

Python

def replace_named_range(service, document_id, range_name, new_text):
    """Replaces the text in existing named ranges."""

    # Determine the length of the replacement text, as UTF-16 code units.
    # https://developers.google.com/workspace/docs/api/concepts/structure#start_and_end_index
    new_text_len = len(new_text.encode('utf-16-le')) / 2

    # Fetch the document to determine the current indexes of the named ranges.
    document = (
        service.documents()
        .get(documentId=document_id, includeTabsContent=True)
        .execute()
    )

    # Find the matching named ranges in the first tab of the document.
    named_range_list = (
        document.get('tabs')[0]
        .get('documentTab')
        .get('namedRanges', {})
        .get(range_name)
    )
    if not named_range_list:
        raise Exception('The named range is no longer present in the document.')

    # Determine all the ranges of text to be removed, and at which indices the
    # replacement text should be inserted.
    all_ranges = []
    insert_at = {}
    for named_range in named_range_list.get('namedRanges'):
        ranges = named_range.get('ranges')
        all_ranges.extend(ranges)
        # Most named ranges only contain one range of text, but it's possible
        # for it to be split into multiple ranges by user edits in the document.
        # The replacement text should only be inserted at the start of the first
        # range.
        insert_at[ranges[0].get('startIndex')] = True

    # Sort the list of ranges by startIndex, in descending order.
    all_ranges.sort(key=lambda r: r.get('startIndex'), reverse=True)

    # Create a sequence of requests for each range.
    requests = []
    for r in all_ranges:
        # Delete all the content in the existing range.
        requests.append({
            'deleteContentRange': {
                'range': r
            }
        })

        segment_id = r.get('segmentId')
        start = r.get('startIndex')
        tab_id = r.get('tabId')
        if insert_at[start]:
            # Insert the replacement text.
            requests.append({
                'insertText': {
                    'location': {
                        'segmentId': segment_id,
                        'index': start,
                        'tabId': tab_id
                    },
                    'text': new_text
                }
            })
            # Re-create the named range on the new text.
            requests.append({
                'createNamedRange': {
                    'name': range_name,
                    'range': {
                        'segmentId': segment_id,
                        'startIndex': start,
                        'endIndex': start + new_text_len,
                        'tabId': tab_id
                    }
                }
            })

    # Make a batchUpdate request to apply the changes, ensuring the document
    # hasn't changed since we fetched it.
    body = {
        'requests': requests,
        'writeControl': {
            'requiredRevisionId': document.get('revisionId')
        }
    }
    service.documents().batchUpdate(documentId=document_id, body=body).execute()

Notez que les plages nommées spécifient une plage de contenu de document, mais ne font pas partie de ce contenu. Si vous extrayez du contenu qui inclut une plage nommée, puis que vous l'insérez à un autre emplacement, la plage nommée ne pointe que vers le contenu d'origine et non vers la section dupliquée.