增加正则调试的功能

This commit is contained in:
microud 2020-04-02 22:16:02 +08:00
parent f1a23bffed
commit 057a8c62e2
16 changed files with 1613 additions and 60 deletions

View File

@ -8,7 +8,7 @@
"build:md": "node ./scripts/md.js",
"build": "npm run test:rules && npm version patch && node ./scripts/genCommond.js && vsce package && npm run build:md",
"vscode:prepublish": "npm run compile",
"compile": "webpack --mode production",
"compile": "webpack --mode development",
"watch": "webpack --mode development",
"pretest": "npm run compile",
"test": "node ./out/test/runTest.js",
@ -335,10 +335,20 @@
"webpack-cli": "^3.3.11"
},
"dependencies": {
"@types/codemirror": "^0.0.88",
"@types/shortid": "^0.0.29",
"antd": "^4.0.4",
"axios": "^0.19.2",
"codemirror": "^5.52.2",
"css-loader": "^3.4.2",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"monaco-editor": "^0.20.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"regulex-cjs": "^0.0.7",
"shortid": "^2.2.15",
"style-loader": "^1.1.3",
"transliteration": "^2.1.8"
}
}

View File

@ -31,6 +31,23 @@ function getWebViewContent(context: ExtensionContext, templatePath: string) {
return html;
}
/**
*
* 1.
* - '/'
* - [] '[', ']', '(', ')'
* - 使 Regulex
* 2.
* -
* -
* -
* -
* @param content
*/
function pickRegularExpressions(content: string) {
// TODO 实现一个可靠的正则表达式提取算法
}
export default function useDiagram(context: ExtensionContext) {
commands.registerTextEditorCommand('extension.showDiagram', (editor, edit) => {
@ -50,6 +67,7 @@ export default function useDiagram(context: ExtensionContext) {
regexpList.push(matches[1]);
}
if (regexpList.length) {
console.log(regexpList);
const panel = window.createWebviewPanel(
'Diagram',
'Diagram',
@ -60,8 +78,20 @@ export default function useDiagram(context: ExtensionContext) {
);
panel.webview.html = getWebViewContent(context, 'out/diagram/index.html')
.replace('{{ inject-script }}', `<script src="${getExtensionFileVscodeResource(context, 'out/diagram/diagram.js')}"></script>`);
panel.webview.onDidReceiveMessage(message => {
switch (message.command) {
case 'getRegexpList':
panel.webview.postMessage({
regexpGroups: regexpList,
regexpList
});
break;
case 'replaceRegexp':
console.log(message.regexp);
break;
case 'insertRegexp':
console.log(message.regexp);
break;
}
});
} else {
window.showWarningMessage('未找到正则表达式');

View File

@ -0,0 +1,49 @@
@primary-color: var(--vscode-button-background);
html,
body {
padding: 0;
font-family: var(--vscode-editor-font-family);
font-size: var(--vscode-font-size);
color: var(--vscode-foreground);
background: var(--vscode-editor-background);
}
body.vscode-light {
color: black;
}
body.vscode-dark {
color: white;
}
.app {
background: transparent;
.ant-collapse-borderless {
background: transparent;
}
.ant-collapse-borderless {
background: transparent;
}
.ant-collapse-header {
background: var(--vscode-sideBarSectionHeader-background);
color: var(--vscode-editor-foreground) !important;
font-weight: 700;
}
.ant-btn-primary {
background: var(--vscode-button-background);
color: var(--vscode-button-foreground);
border: none;
&:hover {
background: var(--vscode-button-hoverBackground);
}
}
.regex-collapse-panel {
border: 0;
}
}

View File

@ -1,12 +1,14 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RegexpDiagramView } from './views/RegexpDiagram';
import { RegexToolView } from './views/RegexTools';
import 'antd/dist/antd.less';
import './App.less';
class App extends React.Component {
render() {
return (
<div className="app">
<RegexpDiagramView />
<RegexToolView />
</div>
);
}

View File

@ -1,20 +1,30 @@
import * as React from 'react';
import { parse, visualize, Raphael } from 'regulex-cjs';
import { useEffect } from 'react';
import { useEffect, useContext, HTMLAttributes } from 'react';
import * as shortid from 'shortid';
import { RegexContext } from './Workbench/Workbench';
interface IDiagramProps extends React.HTMLAttributes<HTMLDivElement> {
regexp: string | RegExp;
}
export const RegExpDiagram: React.FC<IDiagramProps> = (props) => {
useEffect(() => {
const regexpString = typeof props.regexp === 'string' ? props.regexp : props.regexp.source;
const ast = parse(regexpString);
const paper = Raphael('regexp-diagram');
visualize(ast, 'g', paper, { color: { background: 'transparent' } });
});
return (
<div id="regexp-diagram" />
);
const mode = {
light: {},
dark: {},
};
export const RegExpDiagram: React.FC<HTMLAttributes<HTMLDivElement>> = () => {
const id = `regexp-diagram-${shortid.generate()}`;
const { regex } = useContext(RegexContext);
useEffect(() => {
// const regexpString = typeof props.regexp === 'string' ? props.regexp : props.regexp.source;
document.getElementById(id)!.innerHTML = '';
try {
const ast = parse(regex);
const paper = Raphael(id);
visualize(ast, 'g', paper, { color: { background: 'transparent' } });
} catch (e) {
console.log('error occured', e);
document.getElementById(id)!.innerHTML =
'<div stype="padding: 30px; text-align: center;">无法解析正则表达式</div>';
}
}, [regex]);
return <div id={id} />;
};

View File

@ -0,0 +1,47 @@
.regex-editor {
display: flex;
input.ant-input {
border: none;
background: var(--vscode-input-background);
color: var(--vscode-input-foreground);
outline: none;
border-radius: 0;
&:focus {
outline: none;
box-shadow: none;
}
}
.codemirror-wrapper {
width: calc(100% - 32px);
}
.CodeMirror {
background: var(--vscode-input-background) !important;
border: none !important;
height: 32px;
}
.CodeMirror-hscrollbar {
display: none !important;
}
.CodeMirror-scroll {
overflow: hidden !important;
}
.separator {
background: var(--vscode-input-background);
color: #bbb;
width: 16px;
max-width: 16px;
min-width: 16px;
line-height: 32px;
height: 32px;
font-size: 18px;
text-align: center;
font-weight: bold;
}
}

View File

@ -0,0 +1,64 @@
import * as React from 'react';
import { useRef, useEffect, MutableRefObject, useContext } from 'react';
import * as CodeMirror from 'codemirror';
import 'codemirror/addon/display/placeholder';
import 'codemirror/lib/codemirror.css';
import { RegexContext } from '../Workbench/Workbench';
import * as shortid from 'shortid';
import './RegexEditor.less';
export const RegexEditor: React.FC<{}> = () => {
const id = `regex-editor${shortid.generate()}`;
const { regex, flag, dispatch } = useContext(RegexContext);
const codeMirrorElement: MutableRefObject<HTMLDivElement | null> = useRef(
null,
);
const regexEditorElement: MutableRefObject<HTMLDivElement | null> = useRef(
null,
);
let codeMirror: CodeMirror.Editor;
useEffect(() => {
codeMirror = CodeMirror(codeMirrorElement.current!, {
value: regex,
lineWrapping: false,
placeholder: '请输入测试文本',
});
codeMirror.on('focus', () => {
regexEditorElement.current?.classList.add('active');
});
codeMirror.on('blur', () => {
regexEditorElement.current?.classList.remove('active');
});
codeMirror.on('change', editor => {
// setRegexp(codeMirror.getValue());
dispatch({
type: 'regex',
value: editor.getValue(),
});
});
codeMirror.on('beforeChange', (instance, change) => {
var newtext = change.text.join('').replace(/\n/g, ''); // remove ALL \n !
change.update!(change.from, change.to, [newtext]);
return true;
});
}, []);
return (
<div id={id} ref={regexEditorElement} className="regex-editor input-area">
<div className="separator">/</div>
<div className="codemirror-wrapper" ref={codeMirrorElement}></div>
{/* <Input onFocus={onFocus} onBlur={onBlur} value={regexp} onChange={onChange} onKeyPress={onKeyPress} placeholder="请输入正则表达式" /> */}
<div className="separator">/</div>
{/* <Select></Select> */}
<div className="flag-selector">{flag}</div>
</div>
);
};

View File

@ -0,0 +1,22 @@
.CodeMirror {
background: var(--vscode-input-background);
font-family: var(--vscode-editor-font-family);
pre.CodeMirror-placeholder {
color: var(--vscode-editorGutter-commentRangeForeground);
}
.cm-string {
background-color: var(--vscode-button-background);
color: var(--vscode-input-background);
}
}
.test-case-editor {
display: block;
width: 100%;
}
.monaco-editor,
.monaco-editor-background,
.monaco-editor .inputarea.ime-input {
background: var(--vscode-input-background) !important;
}

View File

@ -0,0 +1,66 @@
import * as React from 'react';
import { useContext, useEffect, useRef, useState } from 'react';
import { RegexContext } from '../Workbench/Workbench';
import * as CodeMirror from 'codemirror';
import 'codemirror/addon/display/placeholder';
import 'codemirror/addon/mode/simple';
import 'codemirror/lib/codemirror.css';
import './TestCaseEditor.less';
import shortid = require('shortid');
interface ICodeMirrorEditorProps {
mode: string;
}
const CodeMirrorEditor: React.FC<ICodeMirrorEditorProps> = props => {
const container = useRef<HTMLDivElement>(null);
const [value, setValue] = useState('');
useEffect(() => {
if (container.current) {
container.current.innerHTML = '';
const codeMirror = CodeMirror(container.current!, {
value,
lineWrapping: true,
placeholder: '请输入测试文本',
mode: props.mode,
});
codeMirror.on('focus', () => {
container.current?.classList.add('active');
});
codeMirror.on('blur', () => {
container.current?.classList.remove('active');
});
codeMirror.on('change', cm => {
setValue(cm.getValue());
});
}
}, [props.mode]);
return (
<div className="test-case-editor input-area" ref={container}></div>
);
};
export const TestCaseEditor = () => {
const { regex } = useContext(RegexContext);
const [mode, setMode] = useState('simplemode');
useEffect(() => {
const mode = `mode-${shortid.generate()}`;
// @ts-ignore
CodeMirror.defineSimpleMode(mode, {
start: [
{ regex: new RegExp(regex), token: 'string' },
],
});
setMode(mode);
}, [regex]);
return <CodeMirrorEditor mode={mode}></CodeMirrorEditor>;
};

View File

@ -0,0 +1,10 @@
.anyrule-workbench {
.input-area {
border: none;
}
.input-area.active {
box-shadow: 0 0 0 1px var(--vscode-focusBorder);
}
}

View File

@ -0,0 +1,73 @@
import * as React from 'react';
import { useReducer } from 'react';
import { Row, Col, Button, Input } from 'antd';
import { RegExpDiagram } from '../Diagram';
import { Editor } from '../Editor/Editor';
import './Workbench.less';
import { RegexEditor } from '../RegexEditor/RegexEditor';
import { TestCaseEditor } from '../TestCaseEditor/TestCaseEditor';
interface IWorkspaceProps extends React.HTMLAttributes<HTMLDivElement> {
regexp: string;
}
interface IWorkbenchState {
regex: string;
flag: string;
}
interface IWorkbenchAction {
type: 'regex' | 'flag';
value: string;
}
const reducer: React.Reducer<IWorkbenchState, IWorkbenchAction> = (
state,
action,
) => {
switch (action.type) {
case 'regex':
return { regex: action.value, flag: state.flag };
case 'flag':
return { regex: action.value, flag: state.flag };
}
};
export const RegexContext = React.createContext({
regex: '',
flag: '',
dispatch: (action: IWorkbenchAction) => {},
});
export const RegexWorkbench: React.FC<IWorkspaceProps> = props => {
const rawRegexp = props.regexp;
const [state, dispatch] = useReducer(reducer, {
regex: props.regexp,
flag: '',
});
return (
<div className="anyrule-workbench">
<RegexContext.Provider value={{ regex: state.regex, flag: '', dispatch }}>
<Row gutter={16} style={{ paddingTop: '16px' }}>
<Col span={21}>
<RegexEditor></RegexEditor>
{/* <Input placeholder="请输入正则表达式" value={regexp} onChange={event => setRegexp(event.currentTarget.value)} /> */}
</Col>
<Col span={3}>
<Button type="primary" style={{ width: '100%' }}>
</Button>
</Col>
</Row>
<Row
style={{ maxWidth: '100%', overflowX: 'scroll', margin: '20px 0' }}
>
<RegExpDiagram style={{ marginLeft: 'auto', marginRight: 'auto' }} />
</Row>
<Row>
<TestCaseEditor></TestCaseEditor>
{/* <Editor regexp={props.regexp} /> */}
</Row>
</RegexContext.Provider>
</div>
);
};

View File

@ -1,11 +1,17 @@
import { Render } from "./App";
window.addEventListener('message', event => {
const regexpGroups = event.data.regexpGroups;
console.log(event.data);
// window.addEventListener('message', event => {
// const regexpGroups = event.data.regexpGroups;
// console.log('event', event);
// // @ts-ignore
// window.regexpGroups = regexpGroups;
// // localStorage.setItem('regexp-list', JSON.stringify(regexpGroups));
// // @ts-ignore
// console.log(window.regexpGroups);
// Render();
// });
// @ts-ignore
window.regexpGroups = regexpGroups;
// @ts-ignore
console.log(window.regexpGroups);
window.vscode = acquireVsCodeApi();
Render();
});

View File

@ -0,0 +1,42 @@
import * as React from 'react';
import { useEffect, useState } from 'react';
import { Collapse, Input, Row, Col, Button } from 'antd';
import { RegexWorkbench } from '../components/Workbench/Workbench';
export const RegexToolView: React.FC = () => {
// @ts-ignore
// const regexpGroups = window.regexpGroups;
const [ regexpList, setRegexpList ] = useState([] as string[]);
window.addEventListener('message', event => {
console.log(event);
// return;
if (event.data.regexpList) {
setRegexpList(event.data.regexpList);
}
});
// @ts-ignore
useEffect(() => {
// @ts-ignore
vscode.postMessage({
command: 'getRegexpList',
});
}, []);
// const regexpList = JSON.parse(localStorage.getItem('regexp-list') || '[]');
console.log(regexpList);
const { Panel } = Collapse;
return (
<>
<Collapse accordion bordered={false} defaultActiveKey={['panel-0']}>
{regexpList?.map((regexp: string, index: number) => (
<Panel
header={regexp}
key={'panel-' + String(index)}
className="regex-collapse-panel"
>
<RegexWorkbench regexp={regexp}></RegexWorkbench>
</Panel>
))}
</Collapse>
</>
);
};

View File

@ -1,15 +0,0 @@
import * as React from 'react';
import { RegExpDiagram } from '../components/Diagram';
export const RegexpDiagramView: React.FC = () => {
// @ts-ignore
const regexpGroups = window.regexpGroups;
console.log(regexpGroups);
return (
<>
<div>
{regexpGroups.map((regexp: string) => <RegExpDiagram className="regexp-diagram" regexp={new RegExp(regexp)} />)}
</div>
</>
);
};

View File

@ -65,6 +65,28 @@ const webviewConfig = {
},
],
},
{
test: /\.(css|less)$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'less-loader',
options: {
modifyVars: {
// 'primary-color': 'var(--vscode-button-background)',
// '@link-color': '#1DA57A',
// '@border-radius-base': '0px',
},
javascriptEnabled: true,
},
},
],
},
],
},
plugins: [

1143
yarn.lock

File diff suppressed because it is too large Load Diff