mirror of
https://github.com/prometheus/prometheus
synced 2025-03-04 20:49:51 +00:00
Enable autocomplete anywhere in expression.
This enables metric name autocompletion for every word in an expression, not just the very first one. It would be great to also support all language keywords during autocompletion in the future.
This commit is contained in:
parent
bbdfb10da1
commit
15c58c0f3e
@ -73,7 +73,6 @@ Prometheus.Graph.prototype.initialize = function() {
|
||||
self.rangeInput = self.queryForm.find("input[name=range_input]");
|
||||
self.stackedBtn = self.queryForm.find(".stacked_btn");
|
||||
self.stacked = self.queryForm.find("input[name=stacked]");
|
||||
self.insertMetric = self.queryForm.find("select[name=insert_metric]");
|
||||
self.refreshInterval = self.queryForm.find("select[name=refresh]");
|
||||
|
||||
self.consoleTab = graphWrapper.find(".console");
|
||||
@ -90,14 +89,6 @@ Prometheus.Graph.prototype.initialize = function() {
|
||||
}
|
||||
});
|
||||
|
||||
// Return moves focus back to expr instead of submitting.
|
||||
self.insertMetric.bind("keydown", "return", function(e) {
|
||||
self.expr.focus();
|
||||
self.expr.val(self.expr.val());
|
||||
|
||||
return e.preventDefault();
|
||||
})
|
||||
|
||||
self.error = graphWrapper.find(".error").hide();
|
||||
self.graphArea = graphWrapper.find(".graph_area");
|
||||
self.graph = self.graphArea.find(".graph");
|
||||
@ -160,19 +151,83 @@ Prometheus.Graph.prototype.initialize = function() {
|
||||
self.queryForm.find("button[name=inc_end]").click(function() { self.increaseEnd(); });
|
||||
self.queryForm.find("button[name=dec_end]").click(function() { self.decreaseEnd(); });
|
||||
|
||||
self.insertMetric.change(function() {
|
||||
self.expr.selection("replace", {text: self.insertMetric.val(), mode: "before"});
|
||||
self.expr.focus(); // refocusing
|
||||
});
|
||||
|
||||
self.populateInsertableMetrics();
|
||||
self.populateAutocompleteMetrics();
|
||||
|
||||
if (self.expr.val()) {
|
||||
self.submitQuery();
|
||||
}
|
||||
};
|
||||
|
||||
Prometheus.Graph.prototype.populateInsertableMetrics = function() {
|
||||
// Returns true if the character at "pos" in the expression string "str" looks
|
||||
// like it could be a metric name (if it's not in a string or a label matchers
|
||||
// section).
|
||||
function isPotentialMetric(str, pos) {
|
||||
var quote = null;
|
||||
var inMatchers = false;
|
||||
|
||||
for (var i = 0; i < pos; i++) {
|
||||
var ch = str[i];
|
||||
|
||||
// Skip over escaped characters (quotes or otherwise) in strings.
|
||||
if (quote !== null && ch === "\\") {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Track if we are entering or leaving a string.
|
||||
switch (ch) {
|
||||
case quote:
|
||||
quote = null;
|
||||
break;
|
||||
case '"':
|
||||
case "'":
|
||||
quote = ch;
|
||||
break;
|
||||
}
|
||||
|
||||
// Ignore curly braces in strings.
|
||||
if (quote) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Track whether we are in curly braces (label matchers).
|
||||
switch (ch) {
|
||||
case "{":
|
||||
inMatchers = true;
|
||||
break;
|
||||
case "}":
|
||||
inMatchers = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return !inMatchers && quote === null;
|
||||
}
|
||||
|
||||
// Returns the current word under the cursor position in $input.
|
||||
function currentWord($input) {
|
||||
var wordRE = new RegExp("[a-zA-Z0-9:_]");
|
||||
var pos = $input.prop("selectionStart");
|
||||
var str = $input.val();
|
||||
var len = str.length;
|
||||
var start = pos;
|
||||
var end = pos;
|
||||
|
||||
while (start > 0 && str[start-1].match(wordRE)) {
|
||||
start--;
|
||||
}
|
||||
while (end < len && str[end].match(wordRE)) {
|
||||
end++;
|
||||
}
|
||||
|
||||
return {
|
||||
start: start,
|
||||
end: end,
|
||||
word: $input.val().substring(start, end)
|
||||
};
|
||||
}
|
||||
|
||||
Prometheus.Graph.prototype.populateAutocompleteMetrics = function() {
|
||||
var self = this;
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
@ -183,12 +238,40 @@ Prometheus.Graph.prototype.populateInsertableMetrics = function() {
|
||||
self.showError("Error loading available metrics!")
|
||||
return;
|
||||
}
|
||||
var metrics = json.data;
|
||||
for (var i = 0; i < metrics.length; i++) {
|
||||
self.insertMetric[0].options.add(new Option(metrics[i], metrics[i]));
|
||||
}
|
||||
|
||||
// For the typeahead autocompletion, we need to remember where to put
|
||||
// the cursor after inserting an autocompleted word (we want to put it
|
||||
// after that word, not at the end of the entire input string).
|
||||
var afterUpdatePos = null;
|
||||
|
||||
self.expr.typeahead({
|
||||
source: metrics,
|
||||
// Needs to return true for autocomplete items that should be matched
|
||||
// by the current input.
|
||||
matcher: function(item) {
|
||||
var cw = currentWord(self.expr);
|
||||
if (cw.word.length !== 0 &&
|
||||
item.toLowerCase().indexOf(cw.word.toLowerCase()) > -1 &&
|
||||
isPotentialMetric(self.expr.val(), cw.start)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
// Returns the entire string to which the input field should be set
|
||||
// upon selecting an item from the autocomplete list.
|
||||
updater: function(item) {
|
||||
var str = self.expr.val();
|
||||
var cw = currentWord(self.expr);
|
||||
afterUpdatePos = cw.start + item.length;
|
||||
return str.substring(0, cw.start) + item + str.substring(cw.end, str.length);
|
||||
},
|
||||
// Is called *after* the input field has been set to the string
|
||||
// returned by the "updater" callback. We want to move the cursor to
|
||||
// the end of the actually inserted word here.
|
||||
afterSelect: function(item) {
|
||||
self.expr.prop("selectionStart", afterUpdatePos);
|
||||
self.expr.prop("selectionEnd", afterUpdatePos);
|
||||
},
|
||||
source: json.data,
|
||||
items: "all"
|
||||
});
|
||||
// This needs to happen after attaching the typeahead plugin, as it
|
||||
|
@ -12,9 +12,6 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-10">
|
||||
<input class="btn btn-primary execute_btn" type="submit" value="Execute" name="submit">
|
||||
<select class="form-control expression_select" name="insert_metric">
|
||||
<option value="">- insert metric at cursor -</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
Loading…
Reference in New Issue
Block a user