Renders all whitespace for text visualization & introduces decoration support.
This commit is contained in:
@@ -105,6 +105,7 @@ export class MonacoDiffEditor extends React.Component<{
|
||||
fixedOverflowWidgets: true,
|
||||
readOnly: true,
|
||||
theme: this.props.theme.kind === "dark" ? "vs-dark" : "vs",
|
||||
renderWhitespace: "all",
|
||||
});
|
||||
if (this.originalModel && this.modifiedModel) {
|
||||
this.editor.setModel({
|
||||
|
||||
@@ -6,11 +6,17 @@ import { Theme } from "@hediet/visualization-core";
|
||||
import { getLoadedMonaco } from "@hediet/monaco-editor-react";
|
||||
import { getLanguageId } from "./getLanguageId";
|
||||
|
||||
export interface Decoration {
|
||||
label?: string;
|
||||
range: monacoTypes.IRange;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class MonacoEditor extends React.Component<{
|
||||
text: string;
|
||||
theme: Theme;
|
||||
fileName?: string;
|
||||
decorations?: Decoration[];
|
||||
}> {
|
||||
@observable private editor:
|
||||
| monacoTypes.editor.IStandaloneCodeEditor
|
||||
@@ -31,18 +37,82 @@ export class MonacoEditor extends React.Component<{
|
||||
|
||||
private model: monacoTypes.editor.ITextModel | undefined = undefined;
|
||||
|
||||
private prevDecorations: string[] = [];
|
||||
|
||||
@disposeOnUnmount
|
||||
private _updateText = autorun(() => {
|
||||
if (this.editor) {
|
||||
const decorations = new Array<
|
||||
monacoTypes.editor.IModelDeltaDecoration
|
||||
>();
|
||||
const propDecorations = this.props.decorations || [];
|
||||
const lines = this.props.text.split("\n");
|
||||
const mappedLines = lines.map((l, idx) => {
|
||||
if (idx === lines.length - 1) {
|
||||
return l;
|
||||
}
|
||||
|
||||
if (
|
||||
!propDecorations.some(
|
||||
d =>
|
||||
((d.range.endLineNumber === idx + 2 &&
|
||||
d.range.endColumn === 1) ||
|
||||
(d.range.startLineNumber === idx + 1 &&
|
||||
d.range.startColumn >= l.length + 1)) &&
|
||||
(d.range.startColumn !== d.range.endColumn ||
|
||||
d.range.startLineNumber !==
|
||||
d.range.endLineNumber)
|
||||
)
|
||||
) {
|
||||
return l;
|
||||
}
|
||||
|
||||
decorations.push({
|
||||
range: {
|
||||
startLineNumber: idx + 1,
|
||||
endLineNumber: idx + 1,
|
||||
startColumn: l.length + 1,
|
||||
endColumn: l.length + 2,
|
||||
},
|
||||
options: {
|
||||
inlineClassName: "decoration-whitespace",
|
||||
},
|
||||
});
|
||||
return l + "␊"; // ␍␊
|
||||
});
|
||||
const text = mappedLines.join("\n");
|
||||
|
||||
const model = getLoadedMonaco().editor.createModel(
|
||||
this.props.text,
|
||||
text,
|
||||
this.languageId,
|
||||
undefined
|
||||
);
|
||||
|
||||
this.editor.setModel(model);
|
||||
|
||||
/* this.editor!.updateOptions({ readOnly: false });
|
||||
this.prevDecorations = this.editor.deltaDecorations(
|
||||
[],
|
||||
[
|
||||
...decorations,
|
||||
...propDecorations.map(d => {
|
||||
const r = getLoadedMonaco().Range.lift(d.range);
|
||||
return {
|
||||
range: r,
|
||||
options: {
|
||||
hoverMessage: d.label
|
||||
? { value: d.label, isTrusted: false }
|
||||
: undefined,
|
||||
className: r.isEmpty()
|
||||
? "decoration-empty"
|
||||
: "decoration",
|
||||
},
|
||||
};
|
||||
}),
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
this.editor!.updateOptions({ readOnly: false });
|
||||
|
||||
setTimeout(() => {
|
||||
const a = this.editor!.getAction(
|
||||
@@ -52,6 +122,7 @@ export class MonacoEditor extends React.Component<{
|
||||
this.editor!.updateOptions({ readOnly: true });
|
||||
});
|
||||
}, 200);*/
|
||||
|
||||
if (this.model) {
|
||||
this.model.dispose();
|
||||
}
|
||||
@@ -71,6 +142,7 @@ export class MonacoEditor extends React.Component<{
|
||||
fixedOverflowWidgets: true,
|
||||
readOnly: true,
|
||||
theme: this.props.theme.kind === "dark" ? "vs-dark" : "vs",
|
||||
renderWhitespace: "all",
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ import {
|
||||
sString,
|
||||
sOptionalProp,
|
||||
sProp,
|
||||
sArrayOf,
|
||||
sNumber,
|
||||
sUnion,
|
||||
} from "@hediet/semantic-json";
|
||||
import * as React from "react";
|
||||
import {
|
||||
@@ -13,11 +16,31 @@ import {
|
||||
} from "@hediet/visualization-core";
|
||||
import { visualizationNs } from "../../consts";
|
||||
import { makeLazyLoadable } from "../../utils/LazyLoadable";
|
||||
import { Decoration } from "./MonacoEditor";
|
||||
|
||||
export const MonacoEditorLazyLoadable = makeLazyLoadable(
|
||||
async () => (await import("./MonacoEditor")).MonacoEditor
|
||||
);
|
||||
|
||||
export const LineColumnPosition = sOpenObject({
|
||||
line: sProp(sNumber(), { description: "The 0-based line number" }),
|
||||
column: sProp(sNumber(), { description: "The 0-based column number" }),
|
||||
}).defineAs(visualizationNs("LineColumnPosition"));
|
||||
|
||||
export const LineColumnRange = sOpenObject({
|
||||
start: sProp(LineColumnPosition, { description: "The start position" }),
|
||||
end: sProp(LineColumnPosition, { description: "The end position" }),
|
||||
}).defineAs(visualizationNs("LineColumnRange"));
|
||||
|
||||
export const OffsetPosition = sNumber().defineAs(
|
||||
visualizationNs("OffsetPosition")
|
||||
);
|
||||
|
||||
export const OffsetRange = sOpenObject({
|
||||
start: sProp(OffsetPosition, { description: "The start position" }),
|
||||
end: sProp(OffsetPosition, { description: "The end position" }),
|
||||
}).defineAs(visualizationNs("OffsetRange"));
|
||||
|
||||
export const monacoTextVisualizer = createVisualizer({
|
||||
id: "monaco-text",
|
||||
name: "Text",
|
||||
@@ -26,6 +49,14 @@ export const monacoTextVisualizer = createVisualizer({
|
||||
text: sLiteral(true),
|
||||
}),
|
||||
text: sProp(sString(), { description: "The text to show" }),
|
||||
decorations: sOptionalProp(
|
||||
sArrayOf(
|
||||
sOpenObject({
|
||||
range: sUnion([LineColumnRange]),
|
||||
label: sOptionalProp(sString()),
|
||||
})
|
||||
)
|
||||
),
|
||||
fileName: sOptionalProp(sString(), {
|
||||
description:
|
||||
"An optional filename that might be used for chosing a syntax highlighter",
|
||||
@@ -35,13 +66,28 @@ export const monacoTextVisualizer = createVisualizer({
|
||||
createReactVisualization(
|
||||
self,
|
||||
{ priority: 500, preload: MonacoEditorLazyLoadable.preload },
|
||||
({ theme }) => (
|
||||
<MonacoEditorLazyLoadable
|
||||
text={data.text}
|
||||
fileName={data.fileName}
|
||||
theme={theme}
|
||||
/>
|
||||
)
|
||||
function({ theme }) {
|
||||
const decorations = data.decorations
|
||||
? data.decorations.map<Decoration>(d => ({
|
||||
label: d.label,
|
||||
range: {
|
||||
startLineNumber: d.range.start.line + 1,
|
||||
startColumn: d.range.start.column + 1,
|
||||
endLineNumber: d.range.end.line + 1,
|
||||
endColumn: d.range.end.column + 1,
|
||||
},
|
||||
}))
|
||||
: [];
|
||||
|
||||
return (
|
||||
<MonacoEditorLazyLoadable
|
||||
text={data.text}
|
||||
fileName={data.fileName}
|
||||
theme={theme}
|
||||
decorations={decorations}
|
||||
/>
|
||||
);
|
||||
}
|
||||
),
|
||||
});
|
||||
|
||||
|
||||
@@ -25,3 +25,17 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.decoration-whitespace {
|
||||
color: black !important;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.decoration-empty {
|
||||
margin-left: -1px;
|
||||
border-left: solid rgba(255, 0, 0, 0.815) 3px;
|
||||
}
|
||||
|
||||
.decoration {
|
||||
background-color: rgba(250, 36, 36, 0.459);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user