Skip to content
Snippets Groups Projects
Unverified Commit 79cede1f authored by Tom Robinson's avatar Tom Robinson Committed by GitHub
Browse files

Allow . separator in expression identifiers (#12148)

* Allow . separator in expression identifiers

* Make fk separator configurable
parent 9bf90b61
No related branches found
No related tags found
No related merge requests found
...@@ -29,6 +29,11 @@ export const EDITOR_QUOTES = { ...@@ -29,6 +29,11 @@ export const EDITOR_QUOTES = {
// identifierAlwaysQuoted: false, // identifierAlwaysQuoted: false,
// }; // };
export const EDITOR_FK_SYMBOLS = {
symbols: [".", ""],
default: "",
};
// copied relevant parts from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence // copied relevant parts from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
export const OPERATOR_PRECEDENCE = { export const OPERATOR_PRECEDENCE = {
not: 17, not: 17,
......
export * from "./config"; export * from "./config";
import Dimension from "metabase-lib/lib/Dimension"; import Dimension from "metabase-lib/lib/Dimension";
import { OPERATORS, FUNCTIONS, EDITOR_QUOTES, getMBQLName } from "./config"; import { FK_SYMBOL } from "metabase/lib/formatting";
import {
OPERATORS,
FUNCTIONS,
EDITOR_QUOTES,
EDITOR_FK_SYMBOLS,
getMBQLName,
} from "./config";
// IDENTIFIERS // IDENTIFIERS
...@@ -60,15 +67,22 @@ export function parseDimension(name, { query }) { ...@@ -60,15 +67,22 @@ export function parseDimension(name, { query }) {
return query return query
.dimensionOptions() .dimensionOptions()
.all() .all()
.find(d => getDimensionName(d) === name); .find(d =>
EDITOR_FK_SYMBOLS.symbols.some(
separator => getDimensionName(d, separator) === name,
),
);
} }
export function formatDimensionName(dimension, options) { export function formatDimensionName(dimension, options) {
return formatIdentifier(getDimensionName(dimension), options); return formatIdentifier(getDimensionName(dimension), options);
} }
export function getDimensionName(dimension) { export function getDimensionName(
return dimension.render(); dimension,
separator = EDITOR_FK_SYMBOLS.default,
) {
return dimension.render().replace(` ${FK_SYMBOL} `, separator);
} }
// STRING LITERALS // STRING LITERALS
......
...@@ -37,7 +37,7 @@ function createClauseToken(name, options = {}) { ...@@ -37,7 +37,7 @@ function createClauseToken(name, options = {}) {
export const Identifier = createToken({ export const Identifier = createToken({
name: "Identifier", name: "Identifier",
pattern: /\w+/, pattern: /(\w|\.)+/,
label: "identfier", label: "identfier",
}); });
export const IdentifierString = createToken({ export const IdentifierString = createToken({
......
...@@ -52,6 +52,7 @@ import { ...@@ -52,6 +52,7 @@ import {
isExpressionType, isExpressionType,
getFunctionArgType, getFunctionArgType,
EXPRESSION_TYPES, EXPRESSION_TYPES,
EDITOR_FK_SYMBOLS,
} from "./config"; } from "./config";
const FUNCTIONS_BY_TYPE = {}; const FUNCTIONS_BY_TYPE = {};
...@@ -91,13 +92,23 @@ export function suggest({ ...@@ -91,13 +92,23 @@ export function suggest({
if ( if (
lastInputToken && lastInputToken &&
((isTokenType(lastInputToken.tokenType, Identifier) && ((isTokenType(lastInputToken.tokenType, Identifier) &&
/\w/.test(partialSource[partialSource.length - 1])) || Identifier.PATTERN.test(partialSource[partialSource.length - 1])) ||
lastInputTokenIsUnclosedIdentifierString) lastInputTokenIsUnclosedIdentifierString)
) { ) {
tokenVector = tokenVector.slice(0, -1); tokenVector = tokenVector.slice(0, -1);
partialSuggestionMode = true; partialSuggestionMode = true;
} }
const identifierTrimOptions = lastInputTokenIsUnclosedIdentifierString
? {
// use the last token's pattern anchored to the end of the text
prefixTrim: new RegExp(lastInputToken.tokenType.PATTERN.source + "$"),
}
: {
prefixTrim: new RegExp(Identifier.PATTERN.source + "$"),
postfixTrim: new RegExp("^" + Identifier.PATTERN.source + "\\s*"),
};
const context = getContext({ const context = getContext({
cst, cst,
tokenVector, tokenVector,
...@@ -136,15 +147,6 @@ export function suggest({ ...@@ -136,15 +147,6 @@ export function suggest({
parentRule === "metricExpression" && parentRule === "metricExpression" &&
isExpressionType(expectedType, "aggregation"); isExpressionType(expectedType, "aggregation");
const trimOptions = lastInputTokenIsUnclosedIdentifierString
? {
// use the last token's pattern anchored to the end of the text
prefixTrim: new RegExp(
lastInputToken.tokenType.PATTERN.source + "$",
),
}
: { prefixTrim: /\w+$/, postfixTrim: /^\w+\s*/ };
if (isDimension) { if (isDimension) {
let dimensions = []; let dimensions = [];
if ( if (
...@@ -178,7 +180,10 @@ export function suggest({ ...@@ -178,7 +180,10 @@ export function suggest({
type: "fields", type: "fields",
name: getDimensionName(dimension), name: getDimensionName(dimension),
text: formatDimensionName(dimension) + " ", text: formatDimensionName(dimension) + " ",
...trimOptions, alternates: EDITOR_FK_SYMBOLS.symbols.map(symbol =>
getDimensionName(dimension, symbol),
),
...identifierTrimOptions,
})), })),
); );
} }
...@@ -188,7 +193,7 @@ export function suggest({ ...@@ -188,7 +193,7 @@ export function suggest({
type: "segments", type: "segments",
name: segment.name, name: segment.name,
text: formatSegmentName(segment), text: formatSegmentName(segment),
...trimOptions, ...identifierTrimOptions,
})), })),
); );
} }
...@@ -198,7 +203,7 @@ export function suggest({ ...@@ -198,7 +203,7 @@ export function suggest({
type: "metrics", type: "metrics",
name: metric.name, name: metric.name,
text: formatMetricName(metric), text: formatMetricName(metric),
...trimOptions, ...identifierTrimOptions,
})), })),
); );
} }
...@@ -315,11 +320,16 @@ export function suggest({ ...@@ -315,11 +320,16 @@ export function suggest({
// throw away any suggestion that is not a suffix of the last partialToken. // throw away any suggestion that is not a suffix of the last partialToken.
if (partialSuggestionMode) { if (partialSuggestionMode) {
const input = lastInputToken.image;
const partial = lastInputTokenIsUnclosedIdentifierString const partial = lastInputTokenIsUnclosedIdentifierString
? lastInputToken.image.slice(1).toLowerCase() ? input.slice(1).toLowerCase()
: lastInputToken.image.toLowerCase(); : input.toLowerCase();
for (const suggestion of finalSuggestions) { for (const suggestion of finalSuggestions) {
suggestion: for (const text of [suggestion.name, suggestion.text]) { suggestion: for (const text of [
suggestion.name,
suggestion.text,
...(suggestion.alternates || []),
]) {
const lower = (text || "").toLowerCase(); const lower = (text || "").toLowerCase();
if (lower.startsWith(partial)) { if (lower.startsWith(partial)) {
suggestion.range = [0, partial.length]; suggestion.range = [0, partial.length];
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment