From 0b2cc404ff9218057222ebf468524085cb54d83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 10 Aug 2017 09:33:17 +0200 Subject: [PATCH] ace: working on editor completion stuff --- .../components/code_editor/code_editor.ts | 44 ++--- .../datasource/prometheus/completer.ts | 24 +++ .../datasource/prometheus/datasource.ts | 152 +++++++++--------- .../prometheus/partials/query.editor.html | 2 +- .../datasource/prometheus/query_ctrl.ts | 16 +- 5 files changed, 136 insertions(+), 102 deletions(-) create mode 100644 public/app/plugins/datasource/prometheus/completer.ts diff --git a/public/app/core/components/code_editor/code_editor.ts b/public/app/core/components/code_editor/code_editor.ts index 043b231f50a..582c58592f9 100644 --- a/public/app/core/components/code_editor/code_editor.ts +++ b/public/app/core/components/code_editor/code_editor.ts @@ -37,7 +37,7 @@ const GRAFANA_MODULES = ['mode-prometheus', 'snippets-prometheus', 'theme-grafan const GRAFANA_MODULE_BASE = "public/app/core/components/code_editor/"; // Trick for loading additional modules -function fixModuleUrl(moduleType, name) { +function setModuleUrl(moduleType, name) { let baseUrl = ACE_SRC_BASE; let aceModeName = `ace/${moduleType}/${name}`; let moduleName = `${moduleType}-${name}`; @@ -54,7 +54,7 @@ function fixModuleUrl(moduleType, name) { ace.config.setModuleUrl(aceModeName, baseUrl + componentName); } -fixModuleUrl("ext", "language_tools"); +setModuleUrl("ext", "language_tools"); let editorTemplate = `
`; @@ -129,38 +129,42 @@ function link(scope, elem, attrs) { }); let extCompleter = { - getCompletions: getCompletions + identifierRegexps: [/[\[\]a-zA-Z_0-9=]/], + getCompletions: function (editor, session, pos, prefix, callback) { + console.log(pos); + scope.getCompletions({$query: prefix}).then(results => { + callback(null, results.map(hit => { + return { + caption: hit.word, + value: hit.word, + meta: hit.type + }; + })); + }); + }, }; - function getCompletions(editor, session, pos, prefix, callback) { - scope.getCompletions({$query: prefix}).then(results => { - callback(null, results.map(hit => { - return { - caption: hit.word, - value: hit.word, - meta: hit.type - }; - })); - }); - } - function setLangMode(lang) { let aceModeName = `ace/mode/${lang}`; - fixModuleUrl("mode", lang); - fixModuleUrl("snippets", lang); + setModuleUrl("mode", lang); + setModuleUrl("snippets", lang); editorSession.setMode(aceModeName); + ace.config.loadModule("ace/ext/language_tools", (language_tools) => { codeEditor.setOptions({ enableBasicAutocompletion: true, enableLiveAutocompletion: true, enableSnippets: true }); - codeEditor.completers.push(extCompleter); + + if (scope.getCompleter) { + codeEditor.completers.push(scope.getCompleter()); + } }); } function setThemeMode(theme) { - fixModuleUrl("theme", theme); + setModuleUrl("theme", theme); codeEditor.setTheme(`ace/theme/${theme}`); } @@ -177,7 +181,7 @@ export function codeEditorDirective() { scope: { content: "=", onChange: "&", - getCompletions: "&" + getCompleter: "&" }, link: link }; diff --git a/public/app/plugins/datasource/prometheus/completer.ts b/public/app/plugins/datasource/prometheus/completer.ts new file mode 100644 index 00000000000..da5f8102ac0 --- /dev/null +++ b/public/app/plugins/datasource/prometheus/completer.ts @@ -0,0 +1,24 @@ +/// + +import {PrometheusDatasource} from "./datasource"; + +export class PromCompleter { + identifierRegexps = [/[\[\]a-zA-Z_0-9=]/]; + + constructor(private datasource: PrometheusDatasource) { + } + + getCompletions(editor, session, pos, prefix, callback) { + var query = prefix; + return this.datasource.performSuggestQuery(query).then(metricNames => { + callback(null, metricNames.map(name => { + return { + caption: name, + value: name, + meta: 'metric', + }; + })); + }); + } + +} diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 36fc1e5de4b..2c733d47616 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -11,18 +11,37 @@ import TableModel from 'app/core/table_model'; var durationSplitRegexp = /(\d+)(ms|s|m|h|d|w|M|y)/; -/** @ngInject */ -export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateSrv, timeSrv) { - this.type = 'prometheus'; - this.editorSrc = 'app/features/prometheus/partials/query.editor.html'; - this.name = instanceSettings.name; - this.supportMetrics = true; - this.url = instanceSettings.url; - this.directUrl = instanceSettings.directUrl; - this.basicAuth = instanceSettings.basicAuth; - this.withCredentials = instanceSettings.withCredentials; +function prometheusSpecialRegexEscape(value) { + return value.replace(/[\\^$*+?.()|[\]{}]/g, '\\\\$&'); +} - this._request = function(method, url, requestId) { +export class PrometheusDatasource { + type: string; + editorSrc: string; + name: string; + supportMetrics: boolean; + url: string; + directUrl: string; + basicAuth: any; + withCredentials: any; + + /** @ngInject */ + constructor(instanceSettings, + private $q, + private backendSrv, + private templateSrv, + private timeSrv) { + this.type = 'prometheus'; + this.editorSrc = 'app/features/prometheus/partials/query.editor.html'; + this.name = instanceSettings.name; + this.supportMetrics = true; + this.url = instanceSettings.url; + this.directUrl = instanceSettings.directUrl; + this.basicAuth = instanceSettings.basicAuth; + this.withCredentials = instanceSettings.withCredentials; + } + + _request(method, url, requestId?) { var options: any = { url: this.url + url, method: method, @@ -32,20 +51,17 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS if (this.basicAuth || this.withCredentials) { options.withCredentials = true; } + if (this.basicAuth) { options.headers = { "Authorization": this.basicAuth }; } - return backendSrv.datasourceRequest(options); - }; - - function prometheusSpecialRegexEscape(value) { - return value.replace(/[\\^$*+?.()|[\]{}]/g, '\\\\$&'); + return this.backendSrv.datasourceRequest(options); } - this.interpolateQueryExpr = function(value, variable, defaultFormatFn) { + interpolateQueryExpr(value, variable, defaultFormatFn) { // if no multi or include all do not regexEscape if (!variable.multi && !variable.includeAll) { return value; @@ -57,14 +73,13 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS var escapedValues = _.map(value, prometheusSpecialRegexEscape); return escapedValues.join('|'); - }; + } - this.targetContainsTemplate = function(target) { - return templateSrv.variableExists(target.expr); - }; + targetContainsTemplate(target) { + return this.templateSrv.variableExists(target.expr); + } - // Called once per panel (graph) - this.query = function(options) { + query(options) { var self = this; var start = this.getPrometheusTime(options.range.from, false); var end = this.getPrometheusTime(options.range.to, true); @@ -82,10 +97,10 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS activeTargets.push(target); var query: any = {}; - query.expr = templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr); + query.expr = this.templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr); query.requestId = options.panelId + target.refId; - var interval = templateSrv.replace(target.interval, options.scopedVars) || options.interval; + var interval = this.templateSrv.replace(target.interval, options.scopedVars) || options.interval; var intervalFactor = target.intervalFactor || 1; target.step = query.step = this.calculateInterval(interval, intervalFactor); var range = Math.ceil(end - start); @@ -95,14 +110,14 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS // No valid targets, return the empty result to save a round trip. if (_.isEmpty(queries)) { - return $q.when({ data: [] }); + return this.$q.when({ data: [] }); } var allQueryPromise = _.map(queries, query => { return this.performTimeSeriesQuery(query, start, end); }); - return $q.all(allQueryPromise).then(responseList => { + return this.$q.all(allQueryPromise).then(responseList => { var result = []; var index = 0; @@ -122,27 +137,27 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS return { data: result }; }); - }; + } - this.adjustStep = function(step, autoStep, range) { + adjustStep(step, autoStep, range) { // Prometheus drop query if range/step > 11000 // calibrate step if it is too big if (step !== 0 && range / step > 11000) { return Math.ceil(range / 11000); } return Math.max(step, autoStep); - }; + } - this.performTimeSeriesQuery = function(query, start, end) { + performTimeSeriesQuery(query, start, end) { if (start > end) { throw { message: 'Invalid time range' }; } var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end + '&step=' + query.step; return this._request('GET', url, query.requestId); - }; + } - this.performSuggestQuery = function(query) { + performSuggestQuery(query) { var url = '/api/v1/label/__name__/values'; return this._request('GET', url).then(function(result) { @@ -150,41 +165,30 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS return metricName.indexOf(query) !== 1; }); }); - }; + } - this.metricFindQuery = function(query) { - if (!query) { return $q.when([]); } + metricFindQuery(query) { + if (!query) { return this.$q.when([]); } - var interpolated; - try { - interpolated = templateSrv.replace(query, {}, this.interpolateQueryExpr); - } catch (err) { - return $q.reject(err); - } - - var metricFindQuery = new PrometheusMetricFindQuery(this, interpolated, timeSrv); + let interpolated = this.templateSrv.replace(query, {}, this.interpolateQueryExpr); + var metricFindQuery = new PrometheusMetricFindQuery(this, interpolated, this.timeSrv); return metricFindQuery.process(); - }; + } - this.annotationQuery = function(options) { + annotationQuery(options) { var annotation = options.annotation; var expr = annotation.expr || ''; var tagKeys = annotation.tagKeys || ''; var titleFormat = annotation.titleFormat || ''; var textFormat = annotation.textFormat || ''; - if (!expr) { return $q.when([]); } + if (!expr) { return this.$q.when([]); } - var interpolated; - try { - interpolated = templateSrv.replace(expr, {}, this.interpolateQueryExpr); - } catch (err) { - return $q.reject(err); - } + var interpolated = this.templateSrv.replace(expr, {}, this.interpolateQueryExpr); var step = '60s'; if (annotation.step) { - step = templateSrv.replace(annotation.step); + step = this.templateSrv.replace(annotation.step); } var start = this.getPrometheusTime(options.range.from, false); @@ -222,19 +226,19 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS return eventList; }); - }; + } - this.testDatasource = function() { + testDatasource() { return this.metricFindQuery('metrics(.*)').then(function() { return { status: 'success', message: 'Data source is working', title: 'Success' }; }); - }; + } - this.calculateInterval = function(interval, intervalFactor) { + calculateInterval(interval, intervalFactor) { return Math.ceil(this.intervalSeconds(interval) * intervalFactor); - }; + } - this.intervalSeconds = function(interval) { + intervalSeconds(interval) { var m = interval.match(durationSplitRegexp); var dur = moment.duration(parseInt(m[1]), m[2]); var sec = dur.asSeconds(); @@ -243,9 +247,9 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS } return sec; - }; + } - this.transformMetricData = function(md, options, start, end) { + transformMetricData(md, options, start, end) { var dps = [], metricLabel = null; @@ -273,9 +277,9 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS } return { target: metricLabel, datapoints: dps }; - }; + } - this.transformMetricDataToTable = function(md) { + transformMetricDataToTable(md) { var table = new TableModel(); var i, j; var metricLabels = {}; @@ -325,17 +329,17 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS }); return table; - }; + } - this.createMetricLabel = function(labelData, options) { + createMetricLabel(labelData, options) { if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) { return this.getOriginalMetricName(labelData); } - return this.renderTemplate(templateSrv.replace(options.legendFormat), labelData) || '{}'; - }; + return this.renderTemplate(this.templateSrv.replace(options.legendFormat), labelData) || '{}'; + } - this.renderTemplate = function(aliasPattern, aliasData) { + renderTemplate(aliasPattern, aliasData) { var aliasRegex = /\{\{\s*(.+?)\s*\}\}/g; return aliasPattern.replace(aliasRegex, function(match, g1) { if (aliasData[g1]) { @@ -343,21 +347,21 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS } return g1; }); - }; + } - this.getOriginalMetricName = function(labelData) { + getOriginalMetricName(labelData) { var metricName = labelData.__name__ || ''; delete labelData.__name__; var labelPart = _.map(_.toPairs(labelData), function(label) { return label[0] + '="' + label[1] + '"'; }).join(','); return metricName + '{' + labelPart + '}'; - }; + } - this.getPrometheusTime = function(date, roundUp) { + getPrometheusTime(date, roundUp) { if (_.isString(date)) { date = dateMath.parse(date, roundUp); } return Math.ceil(date.valueOf() / 1000); - }; + } } diff --git a/public/app/plugins/datasource/prometheus/partials/query.editor.html b/public/app/plugins/datasource/prometheus/partials/query.editor.html index f9016c2daca..fc62089ce85 100644 --- a/public/app/plugins/datasource/prometheus/partials/query.editor.html +++ b/public/app/plugins/datasource/prometheus/partials/query.editor.html @@ -2,7 +2,7 @@
+ get-completer="ctrl.getCompleter()" data-mode="prometheus">
diff --git a/public/app/plugins/datasource/prometheus/query_ctrl.ts b/public/app/plugins/datasource/prometheus/query_ctrl.ts index a3ce097e5f4..17a564d0b46 100644 --- a/public/app/plugins/datasource/prometheus/query_ctrl.ts +++ b/public/app/plugins/datasource/prometheus/query_ctrl.ts @@ -6,6 +6,7 @@ import moment from 'moment'; import * as dateMath from 'app/core/utils/datemath'; import {QueryCtrl} from 'app/plugins/sdk'; +import {PromCompleter} from './completer'; class PrometheusQueryCtrl extends QueryCtrl { static templateUrl = 'partials/query.editor.html'; @@ -40,13 +41,14 @@ class PrometheusQueryCtrl extends QueryCtrl { this.updateLink(); } - getCompletions(query) { - console.log(query); - return this.datasource.performSuggestQuery(query).then(res => { - return res.map(item => { - return {word: item, type: 'metric'}; - }); - }); + getCompleter(query) { + return new PromCompleter(this.datasource); + // console.log('getquery); + // return this.datasource.performSuggestQuery(query).then(res => { + // return res.map(item => { + // return {word: item, type: 'metric'}; + // }); + // }); } getDefaultFormat() {