This is an automated email from the ASF dual-hosted git repository.

rfellows pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 5765c7317d NIFI-15009 Codemirror json highlighting (#10339)
5765c7317d is described below

commit 5765c7317d05409e12f55474079324f7818b607a
Author: Scott Aslan <[email protected]>
AuthorDate: Wed Sep 24 15:39:40 2025 -0400

    NIFI-15009 Codemirror json highlighting (#10339)
    
    This closes #10339
---
 .../apps/nifi-jolt-transform-ui/project.json       |   2 +-
 .../feature/jolt-transform-json-ui.component.ts    |  10 +-
 .../apps/standard-content-viewer/project.json      |   2 +-
 .../standard-content-viewer.component.spec.ts      | 371 ++++++++++++++++++++-
 .../feature/standard-content-viewer.component.ts   |  45 +--
 .../frontend/apps/update-attribute/project.json    |   2 +-
 .../codemirror/codemirror.component.spec.ts        |  14 +-
 .../components/codemirror/themes/defaultTheme.ts   |  49 +++
 8 files changed, 448 insertions(+), 47 deletions(-)

diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi-jolt-transform-ui/project.json 
b/nifi-frontend/src/main/frontend/apps/nifi-jolt-transform-ui/project.json
index b76fe3ca40..d9b0a5f404 100644
--- a/nifi-frontend/src/main/frontend/apps/nifi-jolt-transform-ui/project.json
+++ b/nifi-frontend/src/main/frontend/apps/nifi-jolt-transform-ui/project.json
@@ -89,7 +89,7 @@
                 },
                 "development": {
                     "buildTarget": "nifi-jolt-transform-ui:build:development",
-                    "servePath": "/nifi-jolt-transform-json-ui-2.6.0-SNAPSHOT/"
+                    "servePath": "/nifi-jolt-transform-json-ui-2.7.0-SNAPSHOT/"
                 }
             },
             "defaultConfiguration": "development",
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi-jolt-transform-ui/src/app/pages/jolt-transform-json-ui/feature/jolt-transform-json-ui.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi-jolt-transform-ui/src/app/pages/jolt-transform-json-ui/feature/jolt-transform-json-ui.component.ts
index 095df5cc5a..edf5d4dfc8 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi-jolt-transform-ui/src/app/pages/jolt-transform-json-ui/feature/jolt-transform-json-ui.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi-jolt-transform-ui/src/app/pages/jolt-transform-json-ui/feature/jolt-transform-json-ui.component.ts
@@ -25,7 +25,8 @@ import {
     MapTableHelperService,
     MapTableEntry,
     Codemirror,
-    CodeMirrorConfig
+    CodeMirrorConfig,
+    jsonHighlightStyle
 } from '@nifi/shared';
 import {
     selectClientIdFromRoute,
@@ -58,7 +59,6 @@ import {
 } from '@codemirror/view';
 import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
 import {
-    defaultHighlightStyle,
     syntaxHighlighting,
     indentOnInput,
     bracketMatching,
@@ -245,7 +245,7 @@ export class JoltTransformJsonUi implements OnDestroy {
             EditorState.allowMultipleSelections.of(true),
             indentOnInput(),
             highlightSpecialChars(),
-            syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+            syntaxHighlighting(jsonHighlightStyle),
             bracketMatching(),
             highlightActiveLine(),
             [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
@@ -279,7 +279,7 @@ export class JoltTransformJsonUi implements OnDestroy {
             EditorState.allowMultipleSelections.of(true),
             indentOnInput(),
             highlightSpecialChars(),
-            syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+            syntaxHighlighting(jsonHighlightStyle),
             bracketMatching(),
             highlightActiveLine(),
             [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
@@ -308,7 +308,7 @@ export class JoltTransformJsonUi implements OnDestroy {
             EditorView.lineWrapping,
             EditorState.readOnly.of(true),
             highlightSpecialChars(),
-            syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
+            syntaxHighlighting(jsonHighlightStyle),
             bracketMatching(),
             highlightActiveLine(),
             [highlightActiveLineGutter(), Prec.highest(lineNumbers())],
diff --git 
a/nifi-frontend/src/main/frontend/apps/standard-content-viewer/project.json 
b/nifi-frontend/src/main/frontend/apps/standard-content-viewer/project.json
index 5f3a1f1746..7f770a2621 100644
--- a/nifi-frontend/src/main/frontend/apps/standard-content-viewer/project.json
+++ b/nifi-frontend/src/main/frontend/apps/standard-content-viewer/project.json
@@ -88,7 +88,7 @@
                 },
                 "development": {
                     "buildTarget": "standard-content-viewer:build:development",
-                    "servePath": 
"/nifi-standard-content-viewer-2.6.0-SNAPSHOT/"
+                    "servePath": 
"/nifi-standard-content-viewer-2.7.0-SNAPSHOT/"
                 }
             },
             "defaultConfiguration": "development",
diff --git 
a/nifi-frontend/src/main/frontend/apps/standard-content-viewer/src/app/pages/standard-content-viewer/feature/standard-content-viewer.component.spec.ts
 
b/nifi-frontend/src/main/frontend/apps/standard-content-viewer/src/app/pages/standard-content-viewer/feature/standard-content-viewer.component.spec.ts
index 4adad0fccd..785c1691ad 100644
--- 
a/nifi-frontend/src/main/frontend/apps/standard-content-viewer/src/app/pages/standard-content-viewer/feature/standard-content-viewer.component.spec.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/standard-content-viewer/src/app/pages/standard-content-viewer/feature/standard-content-viewer.component.spec.ts
@@ -23,29 +23,396 @@ import { HttpClientTestingModule } from 
'@angular/common/http/testing';
 import { MatButtonToggleModule } from '@angular/material/button-toggle';
 import { ReactiveFormsModule } from '@angular/forms';
 import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
+import { ContentViewerService } from '../service/content-viewer.service';
+import { of } from 'rxjs';
+
+// Mock CodeMirror dependencies
+jest.mock('@codemirror/state', () => ({
+    EditorState: {
+        create: jest.fn().mockReturnValue({
+            mockState: true,
+            doc: {
+                length: 0,
+                lines: 1,
+                toString: jest.fn().mockReturnValue('')
+            }
+        }),
+        allowMultipleSelections: {
+            of: jest.fn().mockReturnValue({ mockAllowMultipleSelections: true 
})
+        },
+        readOnly: {
+            of: jest.fn().mockReturnValue({ mockReadOnly: true })
+        }
+    },
+    Prec: {
+        highest: jest.fn().mockReturnValue({ mockPrecHighest: true })
+    },
+    Annotation: {
+        define: jest.fn().mockReturnValue({
+            of: jest.fn().mockReturnValue({ mockAnnotation: true })
+        })
+    },
+    Compartment: jest.fn().mockImplementation(() => ({
+        of: jest.fn().mockReturnValue({ mockCompartment: true }),
+        reconfigure: jest.fn().mockReturnValue({ mockReconfigure: true })
+    }))
+}));
+
+jest.mock('@codemirror/view', () => ({
+    EditorView: Object.assign(
+        jest.fn().mockImplementation(() => ({
+            contentDOM: {
+                addEventListener: jest.fn(),
+                removeEventListener: jest.fn()
+            },
+            state: {
+                doc: {
+                    length: 10,
+                    lines: 2,
+                    toString: jest.fn().mockReturnValue('test content')
+                }
+            },
+            dispatch: jest.fn(),
+            focus: jest.fn(),
+            destroy: jest.fn()
+        })),
+        {
+            lineWrapping: { mockLineWrapping: true },
+            contentAttributes: {
+                of: jest.fn().mockReturnValue({ mockContentAttributes: true })
+            },
+            theme: jest.fn().mockReturnValue({ mockTheme: true })
+        }
+    ),
+    lineNumbers: jest.fn().mockReturnValue({ mockLineNumbers: true }),
+    highlightActiveLine: jest.fn().mockReturnValue({ mockHighlightActiveLine: 
true }),
+    highlightActiveLineGutter: jest.fn().mockReturnValue({ 
mockHighlightActiveLineGutter: true }),
+    rectangularSelection: jest.fn().mockReturnValue({ 
mockRectangularSelection: true }),
+    crosshairCursor: jest.fn().mockReturnValue({ mockCrosshairCursor: true }),
+    keymap: {
+        of: jest.fn().mockReturnValue({ mockKeymap: true })
+    }
+}));
+
+jest.mock('@codemirror/language', () => ({
+    syntaxHighlighting: jest.fn().mockReturnValue({ mockSyntaxHighlighting: 
true }),
+    indentOnInput: jest.fn().mockReturnValue({ mockIndentOnInput: true }),
+    bracketMatching: jest.fn().mockReturnValue({ mockBracketMatching: true }),
+    foldGutter: jest.fn().mockReturnValue({ mockFoldGutter: true }),
+    foldKeymap: [{ mockFoldKeymap: true }],
+    indentUnit: {
+        of: jest.fn().mockReturnValue({ mockIndentUnit: true })
+    },
+    HighlightStyle: {
+        define: jest.fn().mockReturnValue({ mockHighlightStyle: true })
+    }
+}));
+
+jest.mock('@codemirror/commands', () => ({
+    history: jest.fn().mockReturnValue({ mockHistory: true }),
+    defaultKeymap: [{ mockDefaultKeymap: true }],
+    historyKeymap: [{ mockHistoryKeymap: true }]
+}));
+
+jest.mock('@codemirror/lang-json', () => ({
+    json: jest.fn().mockReturnValue({ mockJson: true })
+}));
+
+jest.mock('@codemirror/lang-xml', () => ({
+    xml: jest.fn().mockReturnValue({ mockXml: true })
+}));
+
+jest.mock('@codemirror/lang-yaml', () => ({
+    yaml: jest.fn().mockReturnValue({ mockYaml: true })
+}));
+
+jest.mock('@codemirror/lang-markdown', () => ({
+    markdown: jest.fn().mockReturnValue({ mockMarkdown: true })
+}));
+
+jest.mock('@codemirror/autocomplete', () => ({
+    CompletionContext: jest.fn(),
+    autocompletion: jest.fn().mockReturnValue({ mockAutocompletion: true }),
+    Completion: jest.fn()
+}));
+
+// Mock @lezer/highlight
+jest.mock('@lezer/highlight', () => ({
+    tags: {
+        keyword: 'keyword',
+        string: 'string',
+        comment: 'comment',
+        number: 'number',
+        operator: 'operator',
+        punctuation: 'punctuation',
+        bracket: 'bracket',
+        variableName: 'variableName',
+        function: jest.fn().mockReturnValue('function'),
+        special: jest.fn().mockReturnValue('special'),
+        definition: jest.fn().mockReturnValue('definition'),
+        labelName: 'labelName',
+        typeName: 'typeName',
+        className: 'className',
+        changed: 'changed',
+        annotation: 'annotation',
+        modifier: 'modifier',
+        self: 'self',
+        namespace: 'namespace',
+        operatorKeyword: 'operatorKeyword',
+        url: 'url',
+        escape: 'escape',
+        regexp: 'regexp',
+        link: 'link',
+        heading: 'heading',
+        atom: 'atom',
+        bool: 'bool',
+        processingInstruction: 'processingInstruction',
+        inserted: 'inserted',
+        invalid: 'invalid',
+        name: 'name',
+        separator: 'separator',
+        tagName: 'tagName',
+        attributeName: 'attributeName',
+        attributeValue: 'attributeValue',
+        angleBracket: 'angleBracket',
+        squareBracket: 'squareBracket',
+        content: 'content',
+        meta: 'meta',
+        brace: 'brace',
+        propertyName: 'propertyName'
+    }
+}));
+
+// Mock the highlight styles
+jest.mock('@nifi/shared', () => ({
+    ...jest.requireActual('@nifi/shared'),
+    jsonHighlightStyle: { mockJsonHighlightStyle: true },
+    xmlHighlightStyle: { mockXmlHighlightStyle: true },
+    yamlHighlightStyle: { mockYamlHighlightStyle: true }
+}));
 
 describe('StandardContentViewer', () => {
     let component: StandardContentViewer;
     let fixture: ComponentFixture<StandardContentViewer>;
+    let contentViewerService: jest.Mocked<ContentViewerService>;
 
     beforeEach(() => {
+        const contentViewerServiceSpy = {
+            getContent: jest.fn().mockReturnValue(of('mock content'))
+        };
+
         TestBed.configureTestingModule({
             declarations: [StandardContentViewer],
             imports: [HttpClientTestingModule, MatButtonToggleModule, 
ReactiveFormsModule, NgxSkeletonLoaderModule],
             providers: [
                 provideMockStore({
                     initialState: {
-                        [DEFAULT_ROUTER_FEATURENAME]: {}
+                        [DEFAULT_ROUTER_FEATURENAME]: {
+                            state: {
+                                queryParams: {}
+                            }
+                        }
                     }
-                })
+                }),
+                { provide: ContentViewerService, useValue: 
contentViewerServiceSpy }
             ]
         });
         fixture = TestBed.createComponent(StandardContentViewer);
         component = fixture.componentInstance;
+        contentViewerService = TestBed.inject(ContentViewerService) as 
jest.Mocked<ContentViewerService>;
+
         fixture.detectChanges();
     });
 
     it('should create', () => {
         expect(component).toBeTruthy();
     });
+
+    describe('Syntax Highlighting', () => {
+        beforeEach(() => {
+            // Clear mocks before each test
+            jest.clearAllMocks();
+        });
+
+        it('should apply jsonHighlightStyle for json MIME type', () => {
+            const { syntaxHighlighting } = 
jest.requireMock('@codemirror/language');
+            const { json } = jest.requireMock('@codemirror/lang-json');
+
+            // Set up component properties using bracket notation to access 
private properties
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = 'json';
+
+            // Call loadContent
+            component.loadContent();
+
+            // Verify json language and jsonHighlightStyle are used
+            expect(json).toHaveBeenCalled();
+            expect(syntaxHighlighting).toHaveBeenCalledWith({ 
mockJsonHighlightStyle: true });
+        });
+
+        it('should apply jsonHighlightStyle for avro MIME type', () => {
+            const { syntaxHighlighting } = 
jest.requireMock('@codemirror/language');
+            const { json } = jest.requireMock('@codemirror/lang-json');
+
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = 'avro';
+
+            component.loadContent();
+
+            expect(json).toHaveBeenCalled();
+            expect(syntaxHighlighting).toHaveBeenCalledWith({ 
mockJsonHighlightStyle: true });
+        });
+
+        it('should apply xmlHighlightStyle for xml MIME type', () => {
+            const { syntaxHighlighting } = 
jest.requireMock('@codemirror/language');
+            const { xml } = jest.requireMock('@codemirror/lang-xml');
+
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = 'xml';
+
+            component.loadContent();
+
+            expect(xml).toHaveBeenCalled();
+            expect(syntaxHighlighting).toHaveBeenCalledWith({ 
mockXmlHighlightStyle: true });
+        });
+
+        it('should apply yamlHighlightStyle for yaml MIME type', () => {
+            const { syntaxHighlighting } = 
jest.requireMock('@codemirror/language');
+            const { yaml } = jest.requireMock('@codemirror/lang-yaml');
+
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = 'yaml';
+
+            component.loadContent();
+
+            expect(yaml).toHaveBeenCalled();
+            expect(syntaxHighlighting).toHaveBeenCalledWith({ 
mockYamlHighlightStyle: true });
+        });
+
+        it('should apply markdown language for markdown MIME type without 
custom highlighting', () => {
+            const { syntaxHighlighting } = 
jest.requireMock('@codemirror/language');
+            const { markdown } = jest.requireMock('@codemirror/lang-markdown');
+
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = 'markdown';
+
+            component.loadContent();
+
+            expect(markdown).toHaveBeenCalled();
+            // Markdown doesn't use custom syntax highlighting
+            expect(syntaxHighlighting).not.toHaveBeenCalled();
+        });
+
+        it('should not apply language-specific highlighting for text MIME 
type', () => {
+            const { syntaxHighlighting } = 
jest.requireMock('@codemirror/language');
+            const { json } = jest.requireMock('@codemirror/lang-json');
+            const { xml } = jest.requireMock('@codemirror/lang-xml');
+            const { yaml } = jest.requireMock('@codemirror/lang-yaml');
+
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = 'text';
+
+            component.loadContent();
+
+            expect(json).not.toHaveBeenCalled();
+            expect(xml).not.toHaveBeenCalled();
+            expect(yaml).not.toHaveBeenCalled();
+            expect(syntaxHighlighting).not.toHaveBeenCalled();
+        });
+
+        it('should not apply language-specific highlighting for csv MIME 
type', () => {
+            const { syntaxHighlighting } = 
jest.requireMock('@codemirror/language');
+            const { json } = jest.requireMock('@codemirror/lang-json');
+            const { xml } = jest.requireMock('@codemirror/lang-xml');
+            const { yaml } = jest.requireMock('@codemirror/lang-yaml');
+
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = 'csv';
+
+            component.loadContent();
+
+            expect(json).not.toHaveBeenCalled();
+            expect(xml).not.toHaveBeenCalled();
+            expect(yaml).not.toHaveBeenCalled();
+            expect(syntaxHighlighting).not.toHaveBeenCalled();
+        });
+
+        it('should not apply language-specific highlighting for unknown MIME 
type', () => {
+            const { syntaxHighlighting } = 
jest.requireMock('@codemirror/language');
+            const { json } = jest.requireMock('@codemirror/lang-json');
+            const { xml } = jest.requireMock('@codemirror/lang-xml');
+            const { yaml } = jest.requireMock('@codemirror/lang-yaml');
+
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = 'unknown';
+
+            component.loadContent();
+
+            expect(json).not.toHaveBeenCalled();
+            expect(xml).not.toHaveBeenCalled();
+            expect(yaml).not.toHaveBeenCalled();
+            expect(syntaxHighlighting).not.toHaveBeenCalled();
+        });
+    });
+
+    describe('CodeMirror Configuration', () => {
+        it('should include base extensions for all MIME types', () => {
+            const {
+                lineNumbers,
+                rectangularSelection,
+                crosshairCursor,
+                highlightActiveLine,
+                highlightActiveLineGutter
+            } = jest.requireMock('@codemirror/view');
+            const { history } = jest.requireMock('@codemirror/commands');
+            const { indentOnInput, bracketMatching, indentUnit } = 
jest.requireMock('@codemirror/language');
+            const { EditorState, Prec } = 
jest.requireMock('@codemirror/state');
+
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = 'json';
+
+            component.loadContent();
+
+            // Verify base extensions are included
+            expect(lineNumbers).toHaveBeenCalled();
+            expect(history).toHaveBeenCalled();
+            expect(rectangularSelection).toHaveBeenCalled();
+            expect(crosshairCursor).toHaveBeenCalled();
+            expect(highlightActiveLine).toHaveBeenCalled();
+            expect(highlightActiveLineGutter).toHaveBeenCalled();
+            expect(indentOnInput).toHaveBeenCalled();
+            expect(bracketMatching).toHaveBeenCalled();
+            expect(indentUnit.of).toHaveBeenCalledWith('    ');
+            
expect(EditorState.allowMultipleSelections.of).toHaveBeenCalledWith(true);
+        });
+
+        it('should include fold functionality for structured languages', () => 
{
+            const { foldGutter } = jest.requireMock('@codemirror/language');
+
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = 'json';
+
+            component.loadContent();
+
+            expect(foldGutter).toHaveBeenCalled();
+        });
+
+        it('should not call loadContent when ref or mimeTypeDisplayName is 
missing', () => {
+            const { syntaxHighlighting } = 
jest.requireMock('@codemirror/language');
+
+            // Test with missing ref
+            (component as any).ref = '';
+            (component as any).mimeTypeDisplayName = 'json';
+            component.loadContent();
+            expect(syntaxHighlighting).not.toHaveBeenCalled();
+
+            jest.clearAllMocks();
+
+            // Test with missing mimeTypeDisplayName
+            (component as any).ref = 'test-ref';
+            (component as any).mimeTypeDisplayName = '';
+            component.loadContent();
+            expect(syntaxHighlighting).not.toHaveBeenCalled();
+        });
+    });
 });
diff --git 
a/nifi-frontend/src/main/frontend/apps/standard-content-viewer/src/app/pages/standard-content-viewer/feature/standard-content-viewer.component.ts
 
b/nifi-frontend/src/main/frontend/apps/standard-content-viewer/src/app/pages/standard-content-viewer/feature/standard-content-viewer.component.ts
index 2b98fec65c..1afb15c481 100644
--- 
a/nifi-frontend/src/main/frontend/apps/standard-content-viewer/src/app/pages/standard-content-viewer/feature/standard-content-viewer.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/standard-content-viewer/src/app/pages/standard-content-viewer/feature/standard-content-viewer.component.ts
@@ -19,17 +19,22 @@ import { Component, inject } from '@angular/core';
 import { Store } from '@ngrx/store';
 import { StandardContentViewerState } from '../../../state';
 import { FormBuilder, FormGroup } from '@angular/forms';
-import { isDefinedAndNotNull, selectQueryParams, CodeMirrorConfig } from 
'@nifi/shared';
+import {
+    isDefinedAndNotNull,
+    selectQueryParams,
+    CodeMirrorConfig,
+    jsonHighlightStyle,
+    xmlHighlightStyle,
+    yamlHighlightStyle
+} from '@nifi/shared';
 import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
 import { ContentViewerService } from '../service/content-viewer.service';
 import { HttpErrorResponse } from '@angular/common/http';
 import { EditorState, Extension, Prec } from '@codemirror/state';
 import {
     bracketMatching,
-    defaultHighlightStyle,
     foldGutter,
     foldKeymap,
-    HighlightStyle,
     indentOnInput,
     indentUnit,
     syntaxHighlighting
@@ -43,7 +48,6 @@ import {
     lineNumbers,
     rectangularSelection
 } from '@codemirror/view';
-import { tags as t } from '@lezer/highlight';
 import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
 import { markdown } from '@codemirror/lang-markdown';
 import { xml } from '@codemirror/lang-xml';
@@ -125,37 +129,6 @@ export class StandardContentViewer {
                 EditorView.contentAttributes.of({ 'aria-label': 'Code Editor' 
})
             ];
 
-            // Define highlight styles
-            const xmlHighlightStyle = HighlightStyle.define([
-                { tag: t.tagName, color: 'var(--editor-keyword)' },
-                { tag: t.attributeName, color: 'var(--editor-attribute-name)' 
},
-                { tag: t.attributeValue, color: 'var(--editor-string)' },
-                { tag: t.angleBracket, color: 'var(--editor-bracket)' },
-                { tag: t.keyword, color: 'var(--editor-variable-name)' },
-                { tag: t.typeName, color: 'var(--editor-variable-name) 
!important' },
-                { tag: t.string, color: 'var(--editor-function)' },
-                { tag: t.number, color: 'var(--editor-constant)' },
-                { tag: t.squareBracket, color: 'var(--editor-special)' },
-                { tag: t.comment, color: 'var(--editor-comment)' },
-                { tag: t.separator, color: 'var(--editor-special)' },
-                { tag: t.content, color: 'var(--editor-text)' },
-                { tag: t.punctuation, color: 'var(--editor-variable-name)' },
-                { tag: t.meta, color: 
'var(--editor-function-name-variable-name)' }
-            ]);
-
-            const yamlHighlightStyle = HighlightStyle.define([
-                { tag: t.keyword, color: 'var(--editor-attribute-name)' },
-                { tag: t.comment, color: 'var(--editor-comment)' },
-                { tag: t.separator, color: 'var(--editor-special)' },
-                { tag: t.punctuation, color: 'var(--editor-special)' },
-                { tag: t.squareBracket, color: 'var(--editor-special)' },
-                { tag: t.brace, color: 'var(--editor-special)' },
-                { tag: t.content, color: 'var(--editor-text)' },
-                { tag: t.attributeValue, color: 'var(--editor-special)' },
-                { tag: t.string, color: 'var(--editor-function)' },
-                { tag: t.definition(t.propertyName), color: 
'var(--editor-keyword)' }
-            ]);
-
             // Add language-specific extensions based on mimeTypeDisplayName
             const languageExtensions: Extension[] = [];
             switch (this.mimeTypeDisplayName) {
@@ -163,7 +136,7 @@ export class StandardContentViewer {
                 case 'avro':
                     languageExtensions.push(
                         json(),
-                        syntaxHighlighting(defaultHighlightStyle, { fallback: 
true }),
+                        syntaxHighlighting(jsonHighlightStyle),
                         foldGutter(),
                         keymap.of([...defaultKeymap, ...historyKeymap, 
...foldKeymap])
                     );
diff --git a/nifi-frontend/src/main/frontend/apps/update-attribute/project.json 
b/nifi-frontend/src/main/frontend/apps/update-attribute/project.json
index 152bac5d42..4a396ac440 100644
--- a/nifi-frontend/src/main/frontend/apps/update-attribute/project.json
+++ b/nifi-frontend/src/main/frontend/apps/update-attribute/project.json
@@ -88,7 +88,7 @@
                 },
                 "development": {
                     "buildTarget": "update-attribute:build:development",
-                    "servePath": "/nifi-update-attribute-ui-2.6.0-SNAPSHOT/"
+                    "servePath": "/nifi-update-attribute-ui-2.7.0-SNAPSHOT/"
                 }
             },
             "defaultConfiguration": "development",
diff --git 
a/nifi-frontend/src/main/frontend/libs/shared/src/components/codemirror/codemirror.component.spec.ts
 
b/nifi-frontend/src/main/frontend/libs/shared/src/components/codemirror/codemirror.component.spec.ts
index 2fdd5b9890..c20486c8a7 100644
--- 
a/nifi-frontend/src/main/frontend/libs/shared/src/components/codemirror/codemirror.component.spec.ts
+++ 
b/nifi-frontend/src/main/frontend/libs/shared/src/components/codemirror/codemirror.component.spec.ts
@@ -71,6 +71,7 @@ jest.mock('@lezer/highlight', () => ({
         variableName: 'variableName',
         function: jest.fn().mockReturnValue('function'),
         special: jest.fn().mockReturnValue('special'),
+        definition: jest.fn().mockReturnValue('definition'),
         labelName: 'labelName',
         typeName: 'typeName',
         className: 'className',
@@ -89,7 +90,18 @@ jest.mock('@lezer/highlight', () => ({
         bool: 'bool',
         processingInstruction: 'processingInstruction',
         inserted: 'inserted',
-        invalid: 'invalid'
+        invalid: 'invalid',
+        name: 'name',
+        separator: 'separator',
+        tagName: 'tagName',
+        attributeName: 'attributeName',
+        attributeValue: 'attributeValue',
+        angleBracket: 'angleBracket',
+        squareBracket: 'squareBracket',
+        content: 'content',
+        meta: 'meta',
+        brace: 'brace',
+        propertyName: 'propertyName'
     }
 }));
 
diff --git 
a/nifi-frontend/src/main/frontend/libs/shared/src/components/codemirror/themes/defaultTheme.ts
 
b/nifi-frontend/src/main/frontend/libs/shared/src/components/codemirror/themes/defaultTheme.ts
index bf456b0fb3..b50721689f 100644
--- 
a/nifi-frontend/src/main/frontend/libs/shared/src/components/codemirror/themes/defaultTheme.ts
+++ 
b/nifi-frontend/src/main/frontend/libs/shared/src/components/codemirror/themes/defaultTheme.ts
@@ -17,6 +17,55 @@
 
 import { baseTheme } from './baseTheme';
 import { EditorView } from '@codemirror/view';
+import { tags as t } from '@lezer/highlight';
+import { HighlightStyle } from '@codemirror/language';
+
+export const xmlHighlightStyle = HighlightStyle.define([
+    { tag: t.tagName, color: 'var(--editor-keyword)' },
+    { tag: t.attributeName, color: 'var(--editor-attribute-name)' },
+    { tag: t.attributeValue, color: 'var(--editor-string)' },
+    { tag: t.angleBracket, color: 'var(--editor-bracket)' },
+    { tag: t.keyword, color: 'var(--editor-variable-name)' },
+    { tag: t.typeName, color: 'var(--editor-variable-name) !important' },
+    { tag: t.string, color: 'var(--editor-function)' },
+    { tag: t.number, color: 'var(--editor-constant)' },
+    { tag: t.squareBracket, color: 'var(--editor-special)' },
+    { tag: t.comment, color: 'var(--editor-comment)' },
+    { tag: t.separator, color: 'var(--editor-special)' },
+    { tag: t.content, color: 'var(--editor-text)' },
+    { tag: t.punctuation, color: 'var(--editor-variable-name)' },
+    { tag: t.meta, color: 'var(--editor-function-name-variable-name)' }
+]);
+
+export const jsonHighlightStyle = HighlightStyle.define([
+    { tag: t.keyword, color: 'var(--editor-variable-name)' },
+    { tag: [t.atom, t.bool, t.special(t.variableName)], color: 
'var(--editor-attribute-name)' },
+    { tag: [t.meta, t.comment], color: 'var(--editor-comment)' },
+    {
+        tag: [t.typeName, t.className, t.number, t.changed, t.annotation, 
t.modifier, t.self, t.namespace],
+        color: 'var(--editor-number)'
+    },
+    {
+        tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, 
t.link, t.special(t.string)],
+        color: 'var(--editor-variable-name)'
+    },
+    { tag: t.definition(t.name), color: 'var(--editor-special)' },
+    { tag: t.separator, color: 'var(--editor-text)' },
+    { tag: [t.processingInstruction, t.string, t.inserted], color: 
'var(--editor-string)' }
+]);
+
+export const yamlHighlightStyle = HighlightStyle.define([
+    { tag: t.keyword, color: 'var(--editor-attribute-name)' },
+    { tag: t.comment, color: 'var(--editor-comment)' },
+    { tag: t.separator, color: 'var(--editor-special)' },
+    { tag: t.punctuation, color: 'var(--editor-special)' },
+    { tag: t.squareBracket, color: 'var(--editor-special)' },
+    { tag: t.brace, color: 'var(--editor-special)' },
+    { tag: t.content, color: 'var(--editor-text)' },
+    { tag: t.attributeValue, color: 'var(--editor-special)' },
+    { tag: t.string, color: 'var(--editor-function)' },
+    { tag: t.definition(t.propertyName), color: 'var(--editor-keyword)' }
+]);
 
 export const defaultTheme = EditorView.theme({
     ...baseTheme,

Reply via email to