rfellows commented on code in PR #10144:
URL: https://github.com/apache/nifi/pull/10144#discussion_r2240616575
##########
nifi-frontend/src/main/frontend/package.json:
##########
@@ -25,7 +25,14 @@
"@angular/platform-browser": "19.2.14",
"@angular/platform-browser-dynamic": "19.2.14",
"@angular/router": "19.2.14",
- "@ctrl/ngx-codemirror": "^7.0.0",
+ "@codemirror/autocomplete": "^6.8.14",
+ "@codemirror/commands": "^6.8.0",
+ "@codemirror/language": "^6.10.8",
+ "@codemirror/language-data": "^6.5.1",
+ "@codemirror/lang-javascript": "^6.2.2",
Review Comment:
I see we depend on the javascript language from code mirror but it seems we
actually import languages from other packages as well that are not in the
package.json. Is that by design? How does it work (xml, yaml, json, markdown)
do we also need to add these?
```
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-xml": "^6.1.0",
"@codemirror/lang-yaml": "^6.1.1",
"@codemirror/lang-markdown": "^6.2.5"
```
##########
nifi-frontend/src/main/frontend/libs/shared/src/services/codemirror-nifi-language-package.service.ts:
##########
@@ -676,375 +957,176 @@ export class NfEl {
stream.next();
return null;
}
- };
- });
- }
-
- private parameterKeyRegex = /^[a-zA-Z0-9-_. ]+/;
-
- private parameters: string[] = [];
- private parameterRegex = new RegExp('^$');
-
- private parameterDetails: { [key: string]: Parameter } = {};
- private parametersSupported = false;
-
- private subjectlessFunctions: string[] = [];
- private functions: string[] = [];
-
- private subjectlessFunctionRegex = new RegExp('^$');
- private functionRegex = new RegExp('^$');
-
- private functionDetails: { [key: string]: ElFunction } = {};
-
- // valid context states
- private static readonly SUBJECT: string = 'subject';
- private static readonly FUNCTION: string = 'function';
- private static readonly SUBJECT_OR_FUNCTION: string =
'subject-or-function';
- private static readonly EXPRESSION: string = 'expression';
- 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';
-
- /**
- * Handles dollars identifies on the stream.
- *
- * @param {string} startChar The start character
- * @param {string} context The context to transition to if we match
on the specified start character
- * @param {object} stream The character stream
- * @param {object} states The states
- */
- private handleStart(startChar: string, context: string, stream:
StringStream, states: any): null | string {
- // determine the number of sequential start chars
- let startCharCount = 0;
- stream.eatWhile(function (ch: string) {
- if (ch === startChar) {
- startCharCount++;
- return true;
- }
- return false;
- });
-
- // if there is an even number of consecutive start chars this
expression is escaped
- if (startCharCount % 2 === 0) {
- // do not style an escaped expression
- return null;
- }
-
- // if there was an odd number of consecutive start chars and there was
more than 1
- if (startCharCount > 1) {
- // back up one char so we can process the start sequence next
iteration
- stream.backUp(1);
-
- // do not style the preceding start chars
- return null;
- }
-
- // if the next character isn't the start of an expression
- if (stream.peek() === '{') {
- // consume the open curly
- stream.next();
-
- 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();
-
- return 'bracket';
- }
- // not a valid start sequence
- return null;
- }
-
- /**
- * Handles dollars identifies on the stream.
- *
- * @param {StringStream} stream The character stream
- * @param {object} state The current state
- */
- private handleStringLiteral(stream: StringStream, state: any): string |
null {
- const current: string | null = stream.next();
- let foundTrailing = false;
- let foundEscapeChar = false;
-
- // locate a closing string delimitor
- const foundStringLiteral: boolean = stream.eatWhile(function (ch:
string) {
- // we've just found the trailing delimitor, stop
- if (foundTrailing) {
- return false;
- }
-
- // if this is the trailing delimitor, only consume
- // if we did not see the escape character on the
- // previous iteration
- if (ch === current) {
- foundTrailing = !foundEscapeChar;
- }
-
- // reset the escape character flag
- foundEscapeChar = false;
-
- // if this is the escape character, set the flag
- if (ch === '\\') {
- foundEscapeChar = true;
- }
-
- // consume this character
- return true;
- });
-
- // if we found the trailing delimitor
- if (foundStringLiteral) {
- return 'string';
- }
-
- // there is no trailing delimitor... clear the current context
- state.context = NfEl.INVALID;
- stream.skipToEnd();
- 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
- // ----------
+ },
+ getAutocompletions: (viewContainerRef: ViewContainerRef) => {
+ return (context: CompletionContext): CompletionResult | null
=> {
+ if (!context.explicit) {
+ return null;
+ }
- // consume and move along
- stream.skipToEnd();
- state.context = NfEl.INVALID;
+ // Analyze the text around the cursor position to
determine context
+ const cursorPos = context.pos;
+ const doc = context.state.doc;
+ const lineInfo = doc.lineAt(cursorPos);
+ const lineText = lineInfo.text;
+ const cursorInLine = cursorPos - lineInfo.from;
+
+ // Function to determine if cursor is within specific
syntax
+ const getCursorContext = (): string => {
+ // Look backward from cursor to find opening syntax
+ let parameterStart = -1;
+ let elStart = -1;
+ let parameterEnd = -1;
+ let elEnd = -1;
+
+ // Find the most recent parameter or EL function start
before cursor
+ for (let i = cursorInLine - 1; i >= 0; i--) {
+ if (lineText.charAt(i) === '{' && i > 0) {
+ if (lineText.charAt(i - 1) === '#' &&
parameterStart === -1) {
+ parameterStart = i - 1;
+ }
+ if (lineText.charAt(i - 1) === '$' && elStart
=== -1) {
+ elStart = i - 1;
+ }
+ }
+ }
- // unexpected...
- return null;
- }
- } else {
- // ----------
- // unexpected
- // ----------
+ // Find the next closing brace after cursor
+ for (let i = cursorInLine; i < lineText.length; i++) {
+ if (lineText.charAt(i) === '}') {
+ if (parameterStart !== -1 && parameterEnd ===
-1) {
+ parameterEnd = i;
+ }
+ if (elStart !== -1 && elEnd === -1) {
+ elEnd = i;
+ }
+ break;
+ }
+ }
- // consume and move along
- stream.skipToEnd();
- state.context = NfEl.INVALID;
+ // Determine which context we're in based on the most
recent opening
+ if (parameterStart !== -1 && parameterEnd !== -1) {
+ if (elStart === -1 || parameterStart > elStart) {
+ return CodemirrorNifiLanguagePackage.PARAMETER;
+ }
+ }
- // unexpected...
- return null;
- }
- }
+ if (elStart !== -1 && elEnd !== -1) {
+ if (parameterStart === -1 || elStart >
parameterStart) {
+ return
CodemirrorNifiLanguagePackage.EXPRESSION;
+ }
+ }
- public setViewContainerRef(viewContainerRef: ViewContainerRef, renderer:
Renderer2): void {
- this.viewContainerRef = viewContainerRef;
- this.renderer = renderer;
- }
+ return CodemirrorNifiLanguagePackage.INVALID;
+ };
- public configureAutocomplete(): void {
- const self: NfEl = this;
-
- CodeMirror.commands.autocomplete = function (cm: Editor) {
- CodeMirror.showHint(cm, function (editor: Editor) {
- // Find the token at the cursor
- const cursor: CodeMirror.Position = editor.getCursor();
- const token: CodeMirror.Token = editor.getTokenAt(cursor);
- let includeAll = false;
- const state = token.state.get();
-
- // whether the current context is within a function
- const isFunction = function (context: string): boolean {
- // attempting to match a function name or already
successfully matched a function name
- return context === NfEl.FUNCTION || context ===
NfEl.ARGUMENTS;
- };
+ const detectedContext = getCursorContext();
- // whether the current context is within a subject-less
funciton
- const isSubjectlessFunction = function (context: string):
boolean {
- // within an expression when no characters are found or
when the string may be an attribute or a function
- return context === NfEl.EXPRESSION || context ===
NfEl.SUBJECT_OR_FUNCTION;
- };
+ // whether the current context is within a function
+ const isFunction = (context: string): boolean => {
+ // attempting to match a function name or already
successfully matched a function name
+ return (
+ context === CodemirrorNifiLanguagePackage.FUNCTION
||
+ context ===
CodemirrorNifiLanguagePackage.ARGUMENTS ||
+ context ===
CodemirrorNifiLanguagePackage.EXPRESSION ||
+ context ===
CodemirrorNifiLanguagePackage.SUBJECT_OR_FUNCTION
+ );
+ };
+
+ // whether the current context is within a parameter
reference
+ const isParameterReference = (context: string): boolean =>
{
+ // attempting to match a parameter name or already
successfully matched a parameter name
+ return (
+ context ===
CodemirrorNifiLanguagePackage.PARAMETER ||
+ context ===
CodemirrorNifiLanguagePackage.SINGLE_QUOTE_PARAMETER ||
+ context ===
CodemirrorNifiLanguagePackage.DOUBLE_QUOTE_PARAMETER
+ );
+ };
- // 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 ||
- context === NfEl.SINGLE_QUOTE_PARAMETER ||
- context === NfEl.DOUBLE_QUOTE_PARAMETER
- );
- };
+ let options: string[] = [];
+ let useFunctionDetails = true;
- // only support suggestion in certain cases
- const context: string = state.context;
- if (!isSubjectlessFunction(context) && !isFunction(context) &&
!isParameterReference(context)) {
- return null;
- }
+ // determine the options based on the detected context
+ console.log('detectedContext', detectedContext);
Review Comment:
Probably left here on accident, please remove.
##########
nifi-frontend/src/main/frontend/apps/update-attribute/src/app/pages/update-attribute/ui/ua-editor/ua-editor.component.ts:
##########
@@ -84,44 +157,78 @@ export class UaEditor implements OnDestroy {
if (this.isRequired) {
this.uaEditorForm.get('value')?.setValidators([Validators.required]);
}
-
- this.nfel.setViewContainerRef(this.viewContainerRef, this.renderer);
- this.nfel.configureAutocomplete();
}
- codeMirrorLoaded(codeEditor: any): void {
- this.editor = codeEditor.codeMirror;
+ initializeCodeMirror(): void {
+ if (this.valueSet && this.requiredSet) {
+ const setup: Extension[] = [
+ lineNumbers(),
+ history(),
+ indentUnit.of(' '),
+ elFunctionHighlightPlugin,
+ EditorView.lineWrapping,
+ rectangularSelection(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ indentOnInput(),
+ highlightSpecialChars(),
+ syntaxHighlighting(highlightStyle),
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+ bracketMatching(),
+ closeBrackets(),
+ highlightActiveLine(),
+ highlightSelectionMatches(),
+ [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
+ foldGutter(),
+ autocompletion(),
+ markdown(),
+ xml(),
+ EditorView.contentAttributes.of({ 'aria-label': 'Code Editor'
}),
+ keymap.of([
+ { key: 'Mod-Enter', run: () => true }, // ignore Mod-Enter
in `defaultKeymap` which is handled by `QueryShortcuts.ts`
+ { key: 'Ctrl-Enter', run: () => true }, // ignore
Ctrl-Enter in `defaultKeymap` which is handled by `QueryShortcuts.ts`
+ { key: 'Mod-y', run: redoSelection },
+ { key: 'Shift-Ctrl-k', run: deleteLine },
Review Comment:
```suggestion
{ key: 'Shift-Mod-k', run: deleteLine },
```
##########
nifi-frontend/src/main/frontend/apps/nifi-jolt-transform-ui/src/app/pages/jolt-transform-json-ui/feature/jolt-transform-json-ui.component.ts:
##########
@@ -177,33 +237,102 @@ export class JoltTransformJsonUi implements OnDestroy {
this.store.dispatch(resetJoltTransformJsonPropertyState());
}
- getJoltSpecOptions(): any {
- return {
- theme: 'nifi',
- lineNumbers: true,
- gutters: ['CodeMirror-lint-markers'],
- mode: 'application/json',
- lint: true,
- extraKeys: {
- 'Shift-Ctrl-F': () => {
- this.formatSpecification();
- }
- }
- };
+ getJoltSpecExtensions(): Extension[] {
+ return [
+ lineNumbers(),
+ history(),
+ indentUnit.of(' '),
+ EditorView.lineWrapping,
+ rectangularSelection(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ indentOnInput(),
+ highlightSpecialChars(),
+ syntaxHighlighting(highlightStyle),
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+ bracketMatching(),
+ closeBrackets(),
+ highlightActiveLine(),
+ highlightSelectionMatches(),
+ [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
+ foldGutter(),
+ autocompletion(),
+ json(),
+ linter(jsonParseLinter()),
+ EditorView.contentAttributes.of({ 'aria-label': 'Jolt
Specification Editor' }),
+ keymap.of([
+ {
+ key: 'Shift-Ctrl-f',
+ run: () => {
+ this.formatSpecification();
+ return true;
+ }
+ },
+ ...closeBracketsKeymap,
+ ...defaultKeymap,
+ ...historyKeymap,
+ ...foldKeymap,
+ ...searchKeymap,
+ ...completionKeymap
+ ])
+ ];
}
- getExampleDataOptions(): any {
- return this.exampleDataOptions;
+ getExampleDataExtensions(): Extension[] {
+ return [
+ lineNumbers(),
+ history(),
+ indentUnit.of(' '),
+ EditorView.lineWrapping,
+ rectangularSelection(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ indentOnInput(),
+ highlightSpecialChars(),
+ syntaxHighlighting(highlightStyle),
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+ bracketMatching(),
+ closeBrackets(),
+ highlightActiveLine(),
+ highlightSelectionMatches(),
+ [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
+ foldGutter(),
+ autocompletion(),
+ json(),
+ EditorView.contentAttributes.of({ 'aria-label': 'Example Data
Editor' }),
+ keymap.of([
+ {
+ key: 'Shift-Ctrl-f',
Review Comment:
```suggestion
key: 'Shift-Mod-f',
```
##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts:
##########
@@ -88,40 +129,130 @@ export class NfEditor implements OnDestroy {
@Output() exit: EventEmitter<void> = new EventEmitter<void>();
itemSet = false;
- getParametersSet = false;
-
+ parameterConfigSet = false;
+ nfLanguageDefinition: NfLanguageDefinition | null = null;
nfEditorForm: FormGroup;
showSensitiveHelperText = false;
supportsEl = false;
supportsParameters = false;
blank = false;
- mode!: string;
parameters: Parameter[] | null = null;
-
- editor!: Editor;
+ private _codemirrorConfig: CodeMirrorConfig = {
+ extensions: [],
+ autoFocus: true
+ };
+
+ // Styling configuration
+ editorStyling = {
+ width: '100%',
+ height: '108px'
+ };
+
+ // Language configuration
+ languageConfig = {
+ language: this.nifiLanguagePackage.getLanguageId(),
+ languages: [] as LanguageDescription[]
+ };
+
+ // Dynamic config getter that includes disabled state
+ get codemirrorConfig(): CodeMirrorConfig {
+ return {
+ ...this._codemirrorConfig,
+ viewDisabled: this.readonly || this.blank,
+ readonly: this.readonly || this.blank
+ };
+ }
constructor(
private formBuilder: FormBuilder,
private viewContainerRef: ViewContainerRef,
- private renderer: Renderer2,
- private nfel: NfEl,
- private nfpr: NfPr
+ private nifiLanguagePackage: CodemirrorNifiLanguagePackage
) {
this.nfEditorForm = this.formBuilder.group({
value: new FormControl(''),
setEmptyString: new FormControl(false)
});
}
- codeMirrorLoaded(codeEditor: any): void {
- this.editor = codeEditor.codeMirror;
+ initializeCodeMirror(): void {
+ if (this.itemSet && this.parameterConfigSet) {
+ const setup: Extension[] = [
+ lineNumbers(),
+ history(),
+ indentUnit.of(' '),
+ parameterHighlightPlugin,
+ elFunctionHighlightPlugin,
+ EditorView.lineWrapping,
+ rectangularSelection(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ indentOnInput(),
+ highlightSpecialChars(),
+ syntaxHighlighting(highlightStyle),
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+ bracketMatching(),
+ closeBrackets(),
+ highlightActiveLine(),
+ highlightSelectionMatches(),
+ [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
+ foldGutter(),
+ autocompletion(),
+ markdown(),
+ xml(),
+ EditorView.contentAttributes.of({ 'aria-label': 'Code Editor'
}),
+ keymap.of([
+ { key: 'Mod-Enter', run: () => true }, // ignore Mod-Enter
in `defaultKeymap` which is handled by `QueryShortcuts.ts`
+ { key: 'Ctrl-Enter', run: () => true }, // ignore
Ctrl-Enter in `defaultKeymap` which is handled by `QueryShortcuts.ts`
+ { key: 'Mod-y', run: redoSelection },
+ { key: 'Shift-Ctrl-k', run: deleteLine },
Review Comment:
```suggestion
{ key: 'Shift-Mod-k', run: deleteLine },
```
##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts:
##########
@@ -88,40 +129,130 @@ export class NfEditor implements OnDestroy {
@Output() exit: EventEmitter<void> = new EventEmitter<void>();
itemSet = false;
- getParametersSet = false;
-
+ parameterConfigSet = false;
+ nfLanguageDefinition: NfLanguageDefinition | null = null;
nfEditorForm: FormGroup;
showSensitiveHelperText = false;
supportsEl = false;
supportsParameters = false;
blank = false;
- mode!: string;
parameters: Parameter[] | null = null;
-
- editor!: Editor;
+ private _codemirrorConfig: CodeMirrorConfig = {
+ extensions: [],
+ autoFocus: true
+ };
+
+ // Styling configuration
+ editorStyling = {
+ width: '100%',
+ height: '108px'
+ };
+
+ // Language configuration
+ languageConfig = {
+ language: this.nifiLanguagePackage.getLanguageId(),
+ languages: [] as LanguageDescription[]
+ };
+
+ // Dynamic config getter that includes disabled state
+ get codemirrorConfig(): CodeMirrorConfig {
+ return {
+ ...this._codemirrorConfig,
+ viewDisabled: this.readonly || this.blank,
+ readonly: this.readonly || this.blank
+ };
+ }
constructor(
private formBuilder: FormBuilder,
private viewContainerRef: ViewContainerRef,
- private renderer: Renderer2,
- private nfel: NfEl,
- private nfpr: NfPr
+ private nifiLanguagePackage: CodemirrorNifiLanguagePackage
) {
this.nfEditorForm = this.formBuilder.group({
value: new FormControl(''),
setEmptyString: new FormControl(false)
});
}
- codeMirrorLoaded(codeEditor: any): void {
- this.editor = codeEditor.codeMirror;
+ initializeCodeMirror(): void {
+ if (this.itemSet && this.parameterConfigSet) {
+ const setup: Extension[] = [
+ lineNumbers(),
+ history(),
+ indentUnit.of(' '),
+ parameterHighlightPlugin,
+ elFunctionHighlightPlugin,
+ EditorView.lineWrapping,
+ rectangularSelection(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ indentOnInput(),
+ highlightSpecialChars(),
+ syntaxHighlighting(highlightStyle),
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+ bracketMatching(),
+ closeBrackets(),
+ highlightActiveLine(),
+ highlightSelectionMatches(),
+ [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
+ foldGutter(),
Review Comment:
Do we want foldgutter here? It adds spacing between the line numbers and the
right edge of the gutter for the fold arrow but this doesn't seem to offer
detection of content type to know where the folds need to go... so it seems a
waste of space:
<img width="1112" height="749" alt="Screenshot 2025-07-29 at 15 08 10"
src="https://github.com/user-attachments/assets/8fb99f08-862b-4700-b987-cfc969667769"
/>
##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts:
##########
@@ -88,40 +129,130 @@ export class NfEditor implements OnDestroy {
@Output() exit: EventEmitter<void> = new EventEmitter<void>();
itemSet = false;
- getParametersSet = false;
-
+ parameterConfigSet = false;
+ nfLanguageDefinition: NfLanguageDefinition | null = null;
nfEditorForm: FormGroup;
showSensitiveHelperText = false;
supportsEl = false;
supportsParameters = false;
blank = false;
- mode!: string;
parameters: Parameter[] | null = null;
-
- editor!: Editor;
+ private _codemirrorConfig: CodeMirrorConfig = {
+ extensions: [],
+ autoFocus: true
+ };
+
+ // Styling configuration
+ editorStyling = {
+ width: '100%',
+ height: '108px'
+ };
+
+ // Language configuration
+ languageConfig = {
+ language: this.nifiLanguagePackage.getLanguageId(),
+ languages: [] as LanguageDescription[]
+ };
+
+ // Dynamic config getter that includes disabled state
+ get codemirrorConfig(): CodeMirrorConfig {
+ return {
+ ...this._codemirrorConfig,
+ viewDisabled: this.readonly || this.blank,
+ readonly: this.readonly || this.blank
+ };
+ }
constructor(
private formBuilder: FormBuilder,
private viewContainerRef: ViewContainerRef,
- private renderer: Renderer2,
- private nfel: NfEl,
- private nfpr: NfPr
+ private nifiLanguagePackage: CodemirrorNifiLanguagePackage
) {
this.nfEditorForm = this.formBuilder.group({
value: new FormControl(''),
setEmptyString: new FormControl(false)
});
}
- codeMirrorLoaded(codeEditor: any): void {
- this.editor = codeEditor.codeMirror;
+ initializeCodeMirror(): void {
+ if (this.itemSet && this.parameterConfigSet) {
+ const setup: Extension[] = [
+ lineNumbers(),
+ history(),
+ indentUnit.of(' '),
+ parameterHighlightPlugin,
+ elFunctionHighlightPlugin,
+ EditorView.lineWrapping,
+ rectangularSelection(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ indentOnInput(),
+ highlightSpecialChars(),
+ syntaxHighlighting(highlightStyle),
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+ bracketMatching(),
+ closeBrackets(),
+ highlightActiveLine(),
+ highlightSelectionMatches(),
+ [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
+ foldGutter(),
+ autocompletion(),
+ markdown(),
+ xml(),
+ EditorView.contentAttributes.of({ 'aria-label': 'Code Editor'
}),
+ keymap.of([
+ { key: 'Mod-Enter', run: () => true }, // ignore Mod-Enter
in `defaultKeymap` which is handled by `QueryShortcuts.ts`
+ { key: 'Ctrl-Enter', run: () => true }, // ignore
Ctrl-Enter in `defaultKeymap` which is handled by `QueryShortcuts.ts`
Review Comment:
this isn't needed. the `Mod-Enter` covers both CMD+Enter on mac and
CTRL+Enter on other platforms: https://codemirror.net/docs/ref/#view.KeyBinding
```suggestion
```
##########
nifi-frontend/src/main/frontend/apps/update-attribute/src/app/pages/update-attribute/ui/ua-editor/ua-editor.component.ts:
##########
@@ -84,44 +157,78 @@ export class UaEditor implements OnDestroy {
if (this.isRequired) {
this.uaEditorForm.get('value')?.setValidators([Validators.required]);
}
-
- this.nfel.setViewContainerRef(this.viewContainerRef, this.renderer);
- this.nfel.configureAutocomplete();
}
- codeMirrorLoaded(codeEditor: any): void {
- this.editor = codeEditor.codeMirror;
+ initializeCodeMirror(): void {
+ if (this.valueSet && this.requiredSet) {
+ const setup: Extension[] = [
+ lineNumbers(),
+ history(),
+ indentUnit.of(' '),
+ elFunctionHighlightPlugin,
+ EditorView.lineWrapping,
+ rectangularSelection(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ indentOnInput(),
+ highlightSpecialChars(),
+ syntaxHighlighting(highlightStyle),
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+ bracketMatching(),
+ closeBrackets(),
+ highlightActiveLine(),
+ highlightSelectionMatches(),
+ [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
+ foldGutter(),
+ autocompletion(),
+ markdown(),
+ xml(),
+ EditorView.contentAttributes.of({ 'aria-label': 'Code Editor'
}),
+ keymap.of([
+ { key: 'Mod-Enter', run: () => true }, // ignore Mod-Enter
in `defaultKeymap` which is handled by `QueryShortcuts.ts`
+ { key: 'Ctrl-Enter', run: () => true }, // ignore
Ctrl-Enter in `defaultKeymap` which is handled by `QueryShortcuts.ts`
Review Comment:
not needed.
```suggestion
```
##########
nifi-frontend/src/main/frontend/libs/shared/src/components/map-table/editors/text-editor/text-editor.component.ts:
##########
@@ -122,50 +158,68 @@ export class TextEditor {
// needed to display the 'Set Empty String' checkbox, the action
buttons,
// and the resize handle. If the amount of spacing needed for
additional UX is needed for the `.editor` is
// changed then this value should also be updated.
- this.editor.setSize('100%', event.height - 112);
+ this.editorStyling.width = '100%';
+ this.editorStyling.height = `${event.height - 152}px`;
}
preventDrag(event: MouseEvent): void {
event.stopPropagation();
}
- codeMirrorLoaded(codeEditor: any): void {
- this.editor = codeEditor.codeMirror;
- // The `.text-editor` minimum height is set to 220px. This is the
height of the `.editor` overlay. The
- // height of the codemirror needs to be set in order to handle large
amounts of text in the codemirror editor.
- // The height of the codemirror should be the height of the `.editor`
overlay minus the 112px of spacing
- // needed to display the 'Set Empty String' checkbox, the action
buttons,
- // and the resize handle so the initial height of the codemirror when
opening should be 108px for a 220px tall
- // `.editor` overlay. If the initial height of that overlay changes
then this initial height should also be
- // updated.
- this.editor.setSize('100%', 108);
-
- if (!this.readonly) {
- this.editor.focus();
- this.editor.execCommand('selectAll');
- }
-
+ codeMirrorLoaded(): void {
// disabling of the input through the form isn't supported until
codemirror
// has loaded so we must disable again if the value is an empty string
if (this.textEditorForm.get('setEmptyString')?.value) {
this.textEditorForm.get('value')?.disable();
- this.editor.setOption('readOnly', 'nocursor');
}
}
- getOptions(): any {
- return {
- readOnly: this.readonly,
- lineNumbers: true,
- matchBrackets: true,
- theme: 'nifi',
- extraKeys: {
- Enter: () => {
- if (this.textEditorForm.dirty &&
this.textEditorForm.valid) {
- this.okClicked();
+ getExtensions(): Extension[] {
+ const setup: Extension[] = [
+ lineNumbers(),
+ history(),
+ indentUnit.of(' '),
+ EditorView.lineWrapping,
+ rectangularSelection(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ indentOnInput(),
+ highlightSpecialChars(),
+ syntaxHighlighting(highlightStyle),
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+ bracketMatching(),
+ closeBrackets(),
+ highlightActiveLine(),
+ highlightSelectionMatches(),
+ [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
+ foldGutter(),
+ markdown(),
+ xml(),
+ EditorView.contentAttributes.of({ 'aria-label': 'Code Editor' }),
+ keymap.of([
+ { key: 'Mod-Enter', run: () => true }, // ignore Mod-Enter in
`defaultKeymap` which is handled by `QueryShortcuts.ts`
+ { key: 'Ctrl-Enter', run: () => true }, // ignore Ctrl-Enter
in `defaultKeymap` which is handled by `QueryShortcuts.ts`
Review Comment:
not needed
```suggestion
```
##########
nifi-frontend/src/main/frontend/apps/nifi-jolt-transform-ui/src/app/pages/jolt-transform-json-ui/feature/jolt-transform-json-ui.component.ts:
##########
@@ -177,33 +237,102 @@ export class JoltTransformJsonUi implements OnDestroy {
this.store.dispatch(resetJoltTransformJsonPropertyState());
}
- getJoltSpecOptions(): any {
- return {
- theme: 'nifi',
- lineNumbers: true,
- gutters: ['CodeMirror-lint-markers'],
- mode: 'application/json',
- lint: true,
- extraKeys: {
- 'Shift-Ctrl-F': () => {
- this.formatSpecification();
- }
- }
- };
+ getJoltSpecExtensions(): Extension[] {
+ return [
+ lineNumbers(),
+ history(),
+ indentUnit.of(' '),
+ EditorView.lineWrapping,
+ rectangularSelection(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ indentOnInput(),
+ highlightSpecialChars(),
+ syntaxHighlighting(highlightStyle),
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+ bracketMatching(),
+ closeBrackets(),
+ highlightActiveLine(),
+ highlightSelectionMatches(),
+ [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
+ foldGutter(),
+ autocompletion(),
+ json(),
+ linter(jsonParseLinter()),
+ EditorView.contentAttributes.of({ 'aria-label': 'Jolt
Specification Editor' }),
+ keymap.of([
+ {
+ key: 'Shift-Ctrl-f',
Review Comment:
```suggestion
key: 'Shift-Mod-f',
```
##########
nifi-frontend/src/main/frontend/libs/shared/src/components/map-table/editors/text-editor/text-editor.component.ts:
##########
@@ -122,50 +158,68 @@ export class TextEditor {
// needed to display the 'Set Empty String' checkbox, the action
buttons,
// and the resize handle. If the amount of spacing needed for
additional UX is needed for the `.editor` is
// changed then this value should also be updated.
- this.editor.setSize('100%', event.height - 112);
+ this.editorStyling.width = '100%';
+ this.editorStyling.height = `${event.height - 152}px`;
}
preventDrag(event: MouseEvent): void {
event.stopPropagation();
}
- codeMirrorLoaded(codeEditor: any): void {
- this.editor = codeEditor.codeMirror;
- // The `.text-editor` minimum height is set to 220px. This is the
height of the `.editor` overlay. The
- // height of the codemirror needs to be set in order to handle large
amounts of text in the codemirror editor.
- // The height of the codemirror should be the height of the `.editor`
overlay minus the 112px of spacing
- // needed to display the 'Set Empty String' checkbox, the action
buttons,
- // and the resize handle so the initial height of the codemirror when
opening should be 108px for a 220px tall
- // `.editor` overlay. If the initial height of that overlay changes
then this initial height should also be
- // updated.
- this.editor.setSize('100%', 108);
-
- if (!this.readonly) {
- this.editor.focus();
- this.editor.execCommand('selectAll');
- }
-
+ codeMirrorLoaded(): void {
// disabling of the input through the form isn't supported until
codemirror
// has loaded so we must disable again if the value is an empty string
if (this.textEditorForm.get('setEmptyString')?.value) {
this.textEditorForm.get('value')?.disable();
- this.editor.setOption('readOnly', 'nocursor');
}
}
- getOptions(): any {
- return {
- readOnly: this.readonly,
- lineNumbers: true,
- matchBrackets: true,
- theme: 'nifi',
- extraKeys: {
- Enter: () => {
- if (this.textEditorForm.dirty &&
this.textEditorForm.valid) {
- this.okClicked();
+ getExtensions(): Extension[] {
+ const setup: Extension[] = [
+ lineNumbers(),
+ history(),
+ indentUnit.of(' '),
+ EditorView.lineWrapping,
+ rectangularSelection(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ indentOnInput(),
+ highlightSpecialChars(),
+ syntaxHighlighting(highlightStyle),
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+ bracketMatching(),
+ closeBrackets(),
+ highlightActiveLine(),
+ highlightSelectionMatches(),
+ [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
+ foldGutter(),
+ markdown(),
+ xml(),
+ EditorView.contentAttributes.of({ 'aria-label': 'Code Editor' }),
+ keymap.of([
+ { key: 'Mod-Enter', run: () => true }, // ignore Mod-Enter in
`defaultKeymap` which is handled by `QueryShortcuts.ts`
+ { key: 'Ctrl-Enter', run: () => true }, // ignore Ctrl-Enter
in `defaultKeymap` which is handled by `QueryShortcuts.ts`
+ { key: 'Mod-y', run: redoSelection },
+ { key: 'Shift-Ctrl-k', run: deleteLine },
Review Comment:
```suggestion
{ key: 'Shift-Mod-k', run: deleteLine },
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]