Skip to content

Commit

Permalink
Merge pull request #2638 from anjiazhuyouxing/feat/json-viewer-error-…
Browse files Browse the repository at this point in the history
…display

Feat/json viewer error display
  • Loading branch information
DaiQiangReal authored Dec 20, 2024
2 parents 229a05b + 5e4741f commit ed78b05
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dist
build
_site
packages/**/lib
packages/**/workerLib
packages/semi-theme-default/css/semi.css
packages/semi-theme-default/semi.scss
public
Expand Down
22 changes: 20 additions & 2 deletions .storybook/base/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const path = require('path');
const _ = require('lodash');
const chalk = require('chalk').default;
const utils = require('./utils');

const fs = require('fs');
let AnalyzePlugin = null
if(process.env.__ENABLE_ANALYZE__ === 'true') {
AnalyzePlugin = require("@ies/semi-page-analyze-inject/src/AnalyzePlugin")
Expand Down Expand Up @@ -79,6 +79,23 @@ module.exports = {
},
]
})
rules.push({
test: /jsonWorkerManager\.ts$/,
use: [
{
loader: 'webpack-replace-loader',
options: {
search: '%WORKER_RAW%',
replace: () => {
const workFilePath = resolve('packages/semi-json-viewer-core/workerLib/worker.js');
const result = fs.readFileSync(workFilePath, 'utf-8');
const encodedResult = encodeURIComponent(result);
return encodedResult;
}
}
}
]
});
config.module.rules = rules;
config.resolve.extensions.push('.js', '.jsx', '.ts', '.tsx');
config.resolve.symlinks = false;
Expand All @@ -92,7 +109,8 @@ module.exports = {
'@douyinfe/semi-illustrations': resolve('packages/semi-illustrations/src'),
'@douyinfe/semi-animation': resolve('packages/semi-animation'),
'@douyinfe/semi-animation-react': resolve('packages/semi-animation-react'),
'@douyinfe/semi-animation-styled': resolve('packages/semi-animation-styled')
'@douyinfe/semi-animation-styled': resolve('packages/semi-animation-styled'),
'@douyinfe/semi-json-viewer-core': resolve('packages/semi-json-viewer-core/src'),
};
config.devtool = 'source-map';
// config.output.publicPath = "/storybook/"
Expand Down
21 changes: 18 additions & 3 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ exports.onCreateWebpackConfig = ({ stage, rules, loaders, plugins, actions }) =>
'semi-site-header': process.env.SEMI_SITE_HEADER || '@douyinfe/semi-site-header',
'semi-site-banner': process.env.SEMI_SITE_BANNER || '@douyinfe/semi-site-banner',
'univers-webview': process.env.SEMI_SITE_UNIVERS_WEBVIEW || resolve('packages/semi-ui'),
'@douyinfe/semi-json-viewer-core': resolve('packages/semi-json-viewer-core'),
'@douyinfe/semi-json-viewer-core': resolve('packages/semi-json-viewer-core/src'),
'@douyinfe/semi-ui': resolve('packages/semi-ui'),
'@douyinfe/semi-foundation': resolve('packages/semi-foundation'),
'@douyinfe/semi-icons': resolve('packages/semi-icons/src/'),
Expand Down Expand Up @@ -170,16 +170,31 @@ exports.onCreateWebpackConfig = ({ stage, rules, loaders, plugins, actions }) =>
},
},
},
{
test: /jsonWorkerManager\.ts$/,
use: [{
loader: 'webpack-replace-loader',
options: {
search: '%WORKER_RAW%',
replace: () => {
const workFilePath = resolve('packages/semi-json-viewer-core/workerLib/worker.js');
const result = fs.readFileSync(workFilePath, 'utf-8');
const encodedResult = encodeURIComponent(result);
return encodedResult;
}
}
}],
},
{
test: [/\.tsx?$/],
include: [path.resolve(__dirname, 'src')],
use: {
use: [{
loader: 'esbuild-loader',
options: {
loader: 'tsx', // Remove this if you're not using JSX
target: 'esnext' // Syntax to compile to (see options below for possible values)
},
},
}],
},
{
test: /\.mjs$/,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@
"webpack": "^5.77.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^3.11.2",
"webpack-replace-loader": "^5.0.1",
"webpackbar": "^5.0.0-3",
"worker-loader": "^3.0.8"
},
Expand Down
6 changes: 5 additions & 1 deletion packages/semi-foundation/jsonViewer/jsonViewer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,9 @@ $module: #{$prefix}-json-viewer;
cursor: pointer;
}


&-error {
text-decoration: underline wavy var(--semi-color-danger);
text-decoration-thickness: 1px;
text-underline-position: under;
}
}
2 changes: 2 additions & 0 deletions packages/semi-json-viewer-core/script/compileLib.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ const compile = async ()=>{
const finalRaw = mainRaw.replaceAll("%WORKER_RAW%", encodeURIComponent(workerRaw));

const saveDir = path.join(__dirname, "..", "lib");
const workerSaveDir = path.join(__dirname, "..", "workerLib");

if (!fs.existsSync(saveDir)) {
fs.mkdirSync(saveDir);
}
fs.writeFileSync(path.join(workerSaveDir, "worker.js"), workerRaw, 'utf8');
fs.writeFileSync(path.join(saveDir, "index.js"), finalRaw, 'utf8');
};

Expand Down
35 changes: 32 additions & 3 deletions packages/semi-json-viewer-core/src/model/jsonModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,22 @@ export class JSONModel {
const command = this._undoStack.pop()!;
command.undo();
this._redoStack.push(command);
this.emitter?.emit('contentChanged', command.operation);
if (!isInWorkerThread()) {
this.emitter?.emit('contentChanged', command.operation);
}
if (this._jsonWorkerManager) {
this._jsonWorkerManager
.undo()
.then(res => {
return this._jsonWorkerManager?.validate();
})
.then(result => {
this.emitter?.emit('problemsChanged', {
problems: result.problems,
root: result.root,
});
});
}
}

redo() {
Expand All @@ -195,10 +210,24 @@ export class JSONModel {
const command = this._redoStack.pop()!;
command.execute();
this._undoStack.push(command);
this.emitter?.emit('contentChanged', command.operation);
if (!isInWorkerThread()) {
this.emitter?.emit('contentChanged', command.operation);
}
if (this._jsonWorkerManager) {
this._jsonWorkerManager
.redo()
.then(res => {
return this._jsonWorkerManager?.validate();
})
.then(result => {
this.emitter?.emit('problemsChanged', {
problems: result.problems,
root: result.root,
});
});
}
}


/**
* 获取值
* @returns 值
Expand Down
2 changes: 1 addition & 1 deletion packages/semi-json-viewer-core/src/service/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ export function parseJson(jsonModel: JSONModel) {
skipUntil: Json.SyntaxKind[] = []
): T | undefined {
let start = scanner.getTokenOffset();
let end = scanner.getPosition() + scanner.getTokenLength();
let end = scanner.getTokenOffset() + scanner.getTokenLength();
if (start === end && start > 0) {
start--;
while (start > 0 && /\s/.test(text.charAt(start))) {
Expand Down
41 changes: 41 additions & 0 deletions packages/semi-json-viewer-core/src/view/error/errorWidget.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Diagnostic } from '../../service/jsonTypes';
import { Emitter, getEmitter } from '../../common/emitter';
import { View } from '../view';
import { GlobalEvents } from '../../common/emitterEvents';

export class ErrorWidget {
private _view: View;
private emitter: Emitter<GlobalEvents> = getEmitter();
constructor(view: View) {
this._view = view;
this._attachEventListeners();
}

private _attachEventListeners() {
this.emitter.on('problemsChanged', (result: any) => {
this.renderErrorLine(result.problems);
});
}

private renderErrorLine(problems: Diagnostic[]) {
problems.forEach((problem: Diagnostic) => {
const { start, end } = problem.range;
const errMessage = problem.message;
this.findDomByPos(start, end, errMessage);
});
}

private findDomByPos(start: { lineNumber: number; column: number }, end: { lineNumber: number; column: number }, errMessage: string) {
const line = this._view.getLineElement(start.lineNumber);
if (!line) return;
let offset = 1;
for (let i = 0; i < line.children.length; i++) {
const child = line.children[i];
offset += child.textContent?.length || 0;
if (offset > start.column && offset <= end.column) {
const className = 'semi-json-viewer-error';
child.classList.add(className);
}
}
}
}
3 changes: 3 additions & 0 deletions packages/semi-json-viewer-core/src/view/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ScalingCellSizeAndPositionManager } from './virtualized/ScalingCellSize
import { CompleteWidget } from './complete/completeWidget';
import { HoverWidget } from './hover/hoverWidget';
import { GlobalEvents } from '../common/emitterEvents';
import { ErrorWidget } from './error/errorWidget';
//TODO 实现ViewModel抽离代码

/**
Expand Down Expand Up @@ -45,6 +46,7 @@ export class View {
private _foldWidget: FoldWidget;
private _completeWidget: CompleteWidget;
private _hoverWidget: HoverWidget;
private _errorWidget: ErrorWidget;
private _jsonWorkerManager: JsonWorkerManager = getJsonWorkerManager();
private _tokenizationJsonModelPart: TokenizationJsonModelPart;
private _scalingCellSizeAndPositionManager: ScalingCellSizeAndPositionManager;
Expand Down Expand Up @@ -79,6 +81,7 @@ export class View {
this._editWidget = new EditWidget(this, this._jsonModel, this._selectionModel, this._foldingModel);
this._completeWidget = new CompleteWidget(this, this._jsonModel, this._selectionModel);
this._hoverWidget = new HoverWidget(this);
this._errorWidget = new ErrorWidget(this);

this._tokenizationJsonModelPart = new TokenizationJsonModelPart(this._jsonModel);

Expand Down
7 changes: 6 additions & 1 deletion packages/semi-json-viewer-core/src/worker/json.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ self.onmessage = (e: MessageEvent) => {
switch (method) {
case 'updateModel':
jsonWorker.updateModel(params.op);
result = jsonWorker.getModel()?.getValue();
break;
case 'undo':
jsonWorker.undo();
break;
case 'redo':
jsonWorker.redo();
break;
case 'format':
result = jsonWorker.format(params.options as FormattingOptions);
Expand Down
8 changes: 8 additions & 0 deletions packages/semi-json-viewer-core/src/worker/jsonWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ export class JsonWorker {
return op;
}

undo() {
this._model?.undo();
}

redo() {
this._model?.redo();
}

parse() {
if (!this._model) throw new Error('Model not initialized');
return parseJsonAst(this._model);
Expand Down
28 changes: 21 additions & 7 deletions packages/semi-json-viewer-core/src/worker/jsonWorkerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ import { getCurrentNameSpaceId } from '../common/nameSpace';
/**
* JsonWorkerManager 类用于管理 JSON Worker
*/
type WorkerMethod = 'init' | 'updateModel' | 'format' | 'foldRange' | 'validate';
type WorkerParams = {
value?: string;
options?: FormattingOptions;
op?: IModelContentChangeEvent | IModelContentChangeEvent[]

type WorkerService = {
init: { value: string };
updateModel: {
op: IModelContentChangeEvent | IModelContentChangeEvent[]
};
format: { options: FormattingOptions };
foldRange: Record<string, never>;
validate: Record<string, never>;
undo: Record<string, never>;
redo: Record<string, never>
};

const workerManagerMap = new Map<string, JsonWorkerManager>();
Expand All @@ -21,7 +27,7 @@ export class JsonWorkerManager {
private _callbacks: Map<number, (result: any) => void>;

constructor() {
const workerRaw = decodeURIComponent('%WORKER_RAW%');
const workerRaw = decodeURIComponent(`%WORKER_RAW%`);
const blob = new Blob([workerRaw], { type: 'application/javascript' });
const workerURL = URL.createObjectURL(blob);
this._worker = new Worker(workerURL);
Expand All @@ -38,6 +44,14 @@ export class JsonWorkerManager {
return this._sendRequest('updateModel', { op });
}

undo() {
return this._sendRequest('undo', {});
}

redo() {
return this._sendRequest('redo', {});
}

formatJson(options: FormattingOptions) {
return this._sendRequest('format', { options });
}
Expand All @@ -50,7 +64,7 @@ export class JsonWorkerManager {
return this._sendRequest('validate', {});
}

private _sendRequest(method: WorkerMethod, params: WorkerParams): Promise<any> {
private _sendRequest<T extends keyof WorkerService>(method: T, params: WorkerService[T]): Promise<any> {
return new Promise((resolve, reject) => {
const messageId = Date.now() + Math.random();
this._callbacks.set(messageId, resolve);
Expand Down

0 comments on commit ed78b05

Please sign in to comment.