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,