From 97aa11b74372ae75996cfadb081647d61b426c39 Mon Sep 17 00:00:00 2001 From: Matt Gilman Date: Fri, 11 Oct 2024 14:45:01 -0400 Subject: [PATCH] NIFI-13863: Fixing syntax highlighting for quoted parameters in property editors. (#9378) This closes #9378 --- .../editors/nf-editor/modes/nfel.ts | 110 ++++++++++++++++-- .../editors/nf-editor/modes/nfpr.ts | 107 +++++++++++++++-- .../property-table.component.ts | 2 +- 3 files changed, 203 insertions(+), 16 deletions(-) diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/modes/nfel.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/modes/nfel.ts index 9be49a436b..a22dac4076 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/modes/nfel.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/modes/nfel.ts @@ -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; } diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/modes/nfpr.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/modes/nfpr.ts index d1be4fe84a..4960dff64f 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/modes/nfpr.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/modes/nfpr.ts @@ -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; } diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.ts index 88e00f3a19..e88d965299 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.ts @@ -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);