NIFI-13863: Fixing syntax highlighting for quoted parameters in property editors. (#9378)

This closes #9378
This commit is contained in:
Matt Gilman 2024-10-11 14:45:01 -04:00 committed by GitHub
parent 3ab60859ea
commit 97aa11b743
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 203 additions and 16 deletions

View File

@ -579,7 +579,11 @@ export class NfEl {
}
// within a parameter reference
if (state.context === NfEl.PARAMETER) {
if (
state.context === NfEl.PARAMETER ||
state.context === NfEl.SINGLE_QUOTE_PARAMETER ||
state.context === NfEl.DOUBLE_QUOTE_PARAMETER
) {
// attempt to extract a parameter name
const parameterName: string[] = stream.match(self.parameterKeyRegex, false);
@ -610,6 +614,14 @@ export class NfEl {
}
}
if (state.context === NfEl.SINGLE_QUOTE_PARAMETER) {
return self.handleParameterEnd(stream, state, states, () => current === "'");
}
if (state.context === NfEl.DOUBLE_QUOTE_PARAMETER) {
return self.handleParameterEnd(stream, state, states, () => current === '"');
}
if (current === '}') {
// -----------------
// end of expression
@ -695,6 +707,8 @@ export class NfEl {
private static readonly ARGUMENTS: string = 'arguments';
private static readonly ARGUMENT: string = 'argument';
private static readonly PARAMETER: string = 'parameter';
private static readonly SINGLE_QUOTE_PARAMETER: string = 'single-quote-parameter';
private static readonly DOUBLE_QUOTE_PARAMETER: string = 'double-quote-parameter';
private static readonly INVALID: string = 'invalid';
/**
@ -736,10 +750,36 @@ export class NfEl {
// consume the open curly
stream.next();
// new expression start
states.push({
context: context
});
if (NfEl.PARAMETER === context) {
// there may be an optional single/double quote
if (stream.peek() === "'") {
// consume the single quote
stream.next();
// new expression start
states.push({
context: NfEl.SINGLE_QUOTE_PARAMETER
});
} else if (stream.peek() === '"') {
// consume the double quote
stream.next();
// new expression start
states.push({
context: NfEl.DOUBLE_QUOTE_PARAMETER
});
} else {
// new expression start
states.push({
context: NfEl.PARAMETER
});
}
} else {
// new expression start
states.push({
context: context
});
}
// consume any addition whitespace
stream.eatSpace();
@ -798,6 +838,58 @@ export class NfEl {
return null;
}
private handleParameterEnd(
stream: StringStream,
state: any,
states: any,
parameterPredicate: () => boolean
): string | null {
if (parameterPredicate()) {
// consume the single/double quote
stream.next();
// verify the next character closes the parameter reference
if (stream.peek() === '}') {
// -----------------
// end of expression
// -----------------
// consume the close
stream.next();
// signifies the end of a parameter reference
if (typeof states.pop() === 'undefined') {
return null;
} else {
// style as expression
return 'bracket';
}
} else {
// ----------
// unexpected
// ----------
// consume and move along
stream.skipToEnd();
state.context = NfEl.INVALID;
// unexpected...
return null;
}
} else {
// ----------
// unexpected
// ----------
// consume and move along
stream.skipToEnd();
state.context = NfEl.INVALID;
// unexpected...
return null;
}
}
public setViewContainerRef(viewContainerRef: ViewContainerRef, renderer: Renderer2): void {
this.viewContainerRef = viewContainerRef;
this.renderer = renderer;
@ -829,7 +921,11 @@ export class NfEl {
// whether the current context is within a parameter reference
const isParameterReference = function (context: string): boolean {
// attempting to match a function name or already successfully matched a function name
return context === NfEl.PARAMETER;
return (
context === NfEl.PARAMETER ||
context === NfEl.SINGLE_QUOTE_PARAMETER ||
context === NfEl.DOUBLE_QUOTE_PARAMETER
);
};
// only support suggestion in certain cases
@ -845,7 +941,7 @@ export class NfEl {
const trimmed: string = value.trim();
// identify potential patterns and increment the start location appropriately
if (trimmed === '${' || trimmed === ':' || trimmed === '#{') {
if (trimmed === '${' || trimmed === ':' || trimmed === '#{' || trimmed === "#{'" || trimmed === '#{"') {
includeAll = true;
token.start += value.length;
}

View File

@ -99,7 +99,11 @@ export class NfPr {
}
// within a parameter reference
if (state.context === NfPr.PARAMETER) {
if (
state.context === NfPr.PARAMETER ||
state.context === NfPr.SINGLE_QUOTE_PARAMETER ||
state.context === NfPr.DOUBLE_QUOTE_PARAMETER
) {
// attempt to extract a parameter name
const parameterName: string[] = stream.match(self.parameterKeyRegex, false);
@ -128,7 +132,17 @@ export class NfPr {
// style for function
return 'string';
}
} else if (current === '}') {
}
if (state.context === NfPr.SINGLE_QUOTE_PARAMETER) {
return self.handleParameterEnd(stream, state, states, () => current === "'");
}
if (state.context === NfPr.DOUBLE_QUOTE_PARAMETER) {
return self.handleParameterEnd(stream, state, states, () => current === '"');
}
if (current === '}') {
// -----------------
// end of expression
// -----------------
@ -194,6 +208,8 @@ export class NfPr {
// valid context states
private static readonly PARAMETER: string = 'parameter';
private static readonly SINGLE_QUOTE_PARAMETER: string = 'single-quote-parameter';
private static readonly DOUBLE_QUOTE_PARAMETER: string = 'double-quote-parameter';
private static readonly INVALID: string = 'invalid';
/**
@ -233,10 +249,29 @@ export class NfPr {
// consume the open curly
stream.next();
// new expression start
states.push({
context: NfPr.PARAMETER
});
// there may be an optional single/double quote
if (stream.peek() === "'") {
// consume the single quote
stream.next();
// new expression start
states.push({
context: NfPr.SINGLE_QUOTE_PARAMETER
});
} else if (stream.peek() === '"') {
// consume the double quote
stream.next();
// new expression start
states.push({
context: NfPr.DOUBLE_QUOTE_PARAMETER
});
} else {
// new expression start
states.push({
context: NfPr.PARAMETER
});
}
// consume any addition whitespace
stream.eatSpace();
@ -248,6 +283,58 @@ export class NfPr {
}
}
private handleParameterEnd(
stream: StringStream,
state: any,
states: any,
parameterPredicate: () => boolean
): string | null {
if (parameterPredicate()) {
// consume the single/double quote
stream.next();
// verify the next character closes the parameter reference
if (stream.peek() === '}') {
// -----------------
// end of expression
// -----------------
// consume the close
stream.next();
// signifies the end of a parameter reference
if (typeof states.pop() === 'undefined') {
return null;
} else {
// style as expression
return 'bracket';
}
} else {
// ----------
// unexpected
// ----------
// consume and move along
stream.skipToEnd();
state.context = NfPr.INVALID;
// unexpected...
return null;
}
} else {
// ----------
// unexpected
// ----------
// consume and move along
stream.skipToEnd();
state.context = NfPr.INVALID;
// unexpected...
return null;
}
}
public setViewContainerRef(viewContainerRef: ViewContainerRef, renderer: Renderer2): void {
this.viewContainerRef = viewContainerRef;
this.renderer = renderer;
@ -267,7 +354,11 @@ export class NfPr {
// whether the current context is within a function
const isParameterContext = function (context: string) {
// attempting to match a function name or already successfully matched a function name
return context === NfPr.PARAMETER;
return (
context === NfPr.PARAMETER ||
context === NfPr.SINGLE_QUOTE_PARAMETER ||
context === NfPr.DOUBLE_QUOTE_PARAMETER
);
};
// only support suggestion in certain cases
@ -283,7 +374,7 @@ export class NfPr {
const trimmed: string = value.trim();
// identify potential patterns and increment the start location appropriately
if (trimmed === '#{') {
if (trimmed === '#{' || trimmed === "#{'" || trimmed === '#{"') {
includeAll = true;
token.start += value.length;
}

View File

@ -115,7 +115,7 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor {
@Input() propertyHistory: ComponentHistory | undefined;
@Input() supportsParameters: boolean = true;
private static readonly PARAM_REF_REGEX: RegExp = /#{[a-zA-Z0-9-_. ]+}/;
private static readonly PARAM_REF_REGEX: RegExp = /#{(['"]?)[a-zA-Z0-9-_. ]+\1}/;
private destroyRef = inject(DestroyRef);