Label virtual corpora by the names they were given

Passing c(before = ..., since = ...) said what the two corpora are to be
called, and only collocationAnalysis() listened. frequencyQuery() dropped
the names in the expand_grid() it builds its queries from and returned no
label at all; corpusStats() let them become row names, which the first
bind_rows() throws away; collocationScoreQuery() ignored them and derived
a label from the corpus definitions instead, so that the pair above came
out as "1990 & pubDat..." and "2010".

All three now prefer the names, falling back to queryStringToLabel()
where a vector is named only in part, as collocationAnalysis() has been
doing. A vector without names is left alone: no label column appears
where there is nothing to put in it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I72c7c8d5f14b649df853cae94a4d90c0be19d1f4
diff --git a/R/misc.R b/R/misc.R
index 6e7340f..e120ea4 100644
--- a/R/misc.R
+++ b/R/misc.R
@@ -162,6 +162,40 @@
   substring(data, leftCommon + 1, nchar(data) - rightCommon)
 }
 
+#' Labels for a vector of virtual corpora
+#'
+#' The names of a named vector are what the caller chose to call their virtual
+#' corpora, so they are what a label should say. Where a vector is named only in
+#' part, [queryStringToLabel()] derives the rest from the definitions.
+#'
+#' @param vc character vector of virtual corpus definitions
+#' @return character vector of labels, or `NULL` if `vc` carries no names at all
+#' @noRd
+vcLabels <- function(vc) {
+  labels <- names(vc)
+  if (is.null(labels)) {
+    return(NULL)
+  }
+  unnamed <- is.na(labels) | !nzchar(labels)
+  if (any(unnamed)) {
+    labels[unnamed] <- queryStringToLabel(vc)[unnamed]
+  }
+  unname(labels)
+}
+
+#' Labels for a vector of virtual corpora, always giving one
+#'
+#' Like [vcLabels()], but falling back to [queryStringToLabel()] where the
+#' vector carries no names, for callers that label unconditionally.
+#'
+#' @param vc character vector of virtual corpus definitions
+#' @return character vector of labels, one per element of `vc`
+#' @noRd
+vcLabelsOrGuess <- function(vc) {
+  labels <- vcLabels(vc)
+  if (is.null(labels)) queryStringToLabel(vc) else labels
+}
+
 
 ## Mute notes: "Undefined global functions or variables:"
 globalVariables(c("conf.high", "conf.low", "onRender", "webUIRequestUrl"))