fix: refactor to service + add regex diagram + ui enhancements
This commit is contained in:
		
							parent
							
								
									f61db56abb
								
							
						
					
					
						commit
						c9f9bb1273
					
				
							
								
								
									
										3
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -134,6 +134,7 @@ declare module '@vue/runtime-core' { | |||||||
|     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] |     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] | ||||||
|     NDivider: typeof import('naive-ui')['NDivider'] |     NDivider: typeof import('naive-ui')['NDivider'] | ||||||
|     NEllipsis: typeof import('naive-ui')['NEllipsis'] |     NEllipsis: typeof import('naive-ui')['NEllipsis'] | ||||||
|  |     NForm: typeof import('naive-ui')['NForm'] | ||||||
|     NFormItem: typeof import('naive-ui')['NFormItem'] |     NFormItem: typeof import('naive-ui')['NFormItem'] | ||||||
|     NGi: typeof import('naive-ui')['NGi'] |     NGi: typeof import('naive-ui')['NGi'] | ||||||
|     NGrid: typeof import('naive-ui')['NGrid'] |     NGrid: typeof import('naive-ui')['NGrid'] | ||||||
| @ -146,8 +147,10 @@ declare module '@vue/runtime-core' { | |||||||
|     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] |     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] | ||||||
|     NMenu: typeof import('naive-ui')['NMenu'] |     NMenu: typeof import('naive-ui')['NMenu'] | ||||||
|     NScrollbar: typeof import('naive-ui')['NScrollbar'] |     NScrollbar: typeof import('naive-ui')['NScrollbar'] | ||||||
|  |     NSlider: typeof import('naive-ui')['NSlider'] | ||||||
|     NSpace: typeof import('naive-ui')['NSpace'] |     NSpace: typeof import('naive-ui')['NSpace'] | ||||||
|     NSpin: typeof import('naive-ui')['NSpin'] |     NSpin: typeof import('naive-ui')['NSpin'] | ||||||
|  |     NSwitch: typeof import('naive-ui')['NSwitch'] | ||||||
|     NTable: typeof import('naive-ui')['NTable'] |     NTable: typeof import('naive-ui')['NTable'] | ||||||
|     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] |     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] | ||||||
|     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] |     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] | ||||||
|  | |||||||
| @ -37,6 +37,7 @@ | |||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@it-tools/bip39": "^0.0.4", |     "@it-tools/bip39": "^0.0.4", | ||||||
|     "@it-tools/oggen": "^1.3.0", |     "@it-tools/oggen": "^1.3.0", | ||||||
|  |     "@regexper/render": "^1.0.0", | ||||||
|     "@sindresorhus/slugify": "^2.2.1", |     "@sindresorhus/slugify": "^2.2.1", | ||||||
|     "@tiptap/pm": "2.1.6", |     "@tiptap/pm": "2.1.6", | ||||||
|     "@tiptap/starter-kit": "2.1.6", |     "@tiptap/starter-kit": "2.1.6", | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -11,6 +11,9 @@ dependencies: | |||||||
|   '@it-tools/oggen': |   '@it-tools/oggen': | ||||||
|     specifier: ^1.3.0 |     specifier: ^1.3.0 | ||||||
|     version: 1.3.0 |     version: 1.3.0 | ||||||
|  |   '@regexper/render': | ||||||
|  |     specifier: ^1.0.0 | ||||||
|  |     version: 1.0.0 | ||||||
|   '@sindresorhus/slugify': |   '@sindresorhus/slugify': | ||||||
|     specifier: ^2.2.1 |     specifier: ^2.2.1 | ||||||
|     version: 2.2.1 |     version: 2.2.1 | ||||||
| @ -2468,6 +2471,17 @@ packages: | |||||||
|     resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} |     resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} | ||||||
|     dev: false |     dev: false | ||||||
| 
 | 
 | ||||||
|  |   /@regexper/parser@1.0.0: | ||||||
|  |     resolution: {integrity: sha512-S8AWIGpCNdl9PNHdbhI6TpXZsPk6FDU/RTZI+6UFF4rVFqDQKjCIbZSgFu7NihoEZKq57wKFPbbT1EzrjVvPHA==} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|  |   /@regexper/render@1.0.0: | ||||||
|  |     resolution: {integrity: sha512-xYm9RUgnhhZotTtf8UZpK1PG2CcTRXQ3JPwfTlYUZsy2J+UcTVc7BaO/MJadpMoVuT8jrIyptH4Y0HLzqhI3hQ==} | ||||||
|  |     dependencies: | ||||||
|  |       '@regexper/parser': 1.0.0 | ||||||
|  |       '@svgdotjs/svg.js': 3.2.0 | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /@remirror/core-constants@2.0.1: |   /@remirror/core-constants@2.0.1: | ||||||
|     resolution: {integrity: sha512-ZR4aihtnnT9lMbhh5DEbsriJRlukRXmLZe7HmM+6ufJNNUDoazc75UX26xbgQlNUqgAqMcUdGFAnPc1JwgAdLQ==} |     resolution: {integrity: sha512-ZR4aihtnnT9lMbhh5DEbsriJRlukRXmLZe7HmM+6ufJNNUDoazc75UX26xbgQlNUqgAqMcUdGFAnPc1JwgAdLQ==} | ||||||
|     dependencies: |     dependencies: | ||||||
| @ -2617,6 +2631,10 @@ packages: | |||||||
|       string.prototype.matchall: 4.0.10 |       string.prototype.matchall: 4.0.10 | ||||||
|     dev: true |     dev: true | ||||||
| 
 | 
 | ||||||
|  |   /@svgdotjs/svg.js@3.2.0: | ||||||
|  |     resolution: {integrity: sha512-Tr8p+QVP7y+QT1GBlq1Tt57IvedVH8zCPoYxdHLX0Oof3a/PqnC/tXAkVufv1JQJfsDHlH/UrjcDfgxSofqSNA==} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /@tiptap/core@2.1.12(@tiptap/pm@2.1.6): |   /@tiptap/core@2.1.12(@tiptap/pm@2.1.6): | ||||||
|     resolution: {integrity: sha512-ZGc3xrBJA9KY8kln5AYTj8y+GDrKxi7u95xIl2eccrqTY5CQeRu6HRNM1yT4mAjuSaG9jmazyjGRlQuhyxCKxQ==} |     resolution: {integrity: sha512-ZGc3xrBJA9KY8kln5AYTj8y+GDrKxi7u95xIl2eccrqTY5CQeRu6HRNM1yT4mAjuSaG9jmazyjGRlQuhyxCKxQ==} | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|  | |||||||
							
								
								
									
										106
									
								
								src/tools/regex-tester/regex-tester.service.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/tools/regex-tester/regex-tester.service.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,106 @@ | |||||||
|  | import { describe, expect, it } from 'vitest'; | ||||||
|  | import { matchRegex } from './regex-tester.service'; | ||||||
|  | 
 | ||||||
|  | const regexesData = [ | ||||||
|  |   { | ||||||
|  |     regex: '', | ||||||
|  |     text: '', | ||||||
|  |     flags: '', | ||||||
|  |     result: [], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     regex: '.*', | ||||||
|  |     text: '', | ||||||
|  |     flags: '', | ||||||
|  |     result: [], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     regex: '', | ||||||
|  |     text: 'aaa', | ||||||
|  |     flags: '', | ||||||
|  |     result: [], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     regex: 'a', | ||||||
|  |     text: 'baaa', | ||||||
|  |     flags: '', | ||||||
|  |     result: [ | ||||||
|  |       { | ||||||
|  |         captures: [], | ||||||
|  |         groups: [], | ||||||
|  |         index: 1, | ||||||
|  |         value: 'a', | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     regex: '(.)(?<g>r)', | ||||||
|  |     text: 'azertyr', | ||||||
|  |     flags: 'g', | ||||||
|  |     result: [ | ||||||
|  |       { | ||||||
|  |         captures: [ | ||||||
|  |           { | ||||||
|  |             end: 3, | ||||||
|  |             name: '1', | ||||||
|  |             start: 2, | ||||||
|  |             value: 'e', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             end: 4, | ||||||
|  |             name: '2', | ||||||
|  |             start: 3, | ||||||
|  |             value: 'r', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         groups: [ | ||||||
|  |           { | ||||||
|  |             end: 4, | ||||||
|  |             name: 'g', | ||||||
|  |             start: 3, | ||||||
|  |             value: 'r', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         index: 2, | ||||||
|  |         value: 'er', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         captures: [ | ||||||
|  |           { | ||||||
|  |             end: 6, | ||||||
|  |             name: '1', | ||||||
|  |             start: 5, | ||||||
|  |             value: 'y', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             end: 7, | ||||||
|  |             name: '2', | ||||||
|  |             start: 6, | ||||||
|  |             value: 'r', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         groups: [ | ||||||
|  |           { | ||||||
|  |             end: 7, | ||||||
|  |             name: 'g', | ||||||
|  |             start: 6, | ||||||
|  |             value: 'r', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         index: 5, | ||||||
|  |         value: 'yr', | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
|  | describe('regex-tester', () => { | ||||||
|  |   for (const reg of regexesData) { | ||||||
|  |     const { regex, text, flags, result: expected_result } = reg; | ||||||
|  |     it(`Should matchRegex("${regex}","${text}","${flags}") return correct result`, async () => { | ||||||
|  |       const result = matchRegex(regex, text, `${flags}d`); | ||||||
|  | 
 | ||||||
|  |       expect(result).to.deep.equal(expected_result); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | }); | ||||||
							
								
								
									
										61
									
								
								src/tools/regex-tester/regex-tester.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/tools/regex-tester/regex-tester.service.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | interface RegExpGroupIndices { | ||||||
|  |   [name: string]: [number, number] | ||||||
|  | } | ||||||
|  | interface RegExpIndices extends Array<[number, number]> { | ||||||
|  |   groups: RegExpGroupIndices | ||||||
|  | } | ||||||
|  | interface RegExpExecArrayWithIndices extends RegExpExecArray { | ||||||
|  |   indices: RegExpIndices | ||||||
|  | } | ||||||
|  | interface GroupCapture { | ||||||
|  |   name: string | ||||||
|  |   value: string | ||||||
|  |   start: number | ||||||
|  |   end: number | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function matchRegex(regex: string, text: string, flags: string) { | ||||||
|  |   // if (regex === '' || text === '') {
 | ||||||
|  |   //   return [];
 | ||||||
|  |   // }
 | ||||||
|  | 
 | ||||||
|  |   let lastIndex = -1; | ||||||
|  |   const re = new RegExp(regex, flags); | ||||||
|  |   const results = []; | ||||||
|  |   let match = re.exec(text) as RegExpExecArrayWithIndices; | ||||||
|  |   while (match !== null) { | ||||||
|  |     if (re.lastIndex === lastIndex || match[0] === '') { | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     const indices = match.indices; | ||||||
|  |     const captures: Array<GroupCapture> = []; | ||||||
|  |     Object.entries(match).forEach(([captureName, captureValue]) => { | ||||||
|  |       if (captureName !== '0' && captureName.match(/\d+/)) { | ||||||
|  |         captures.push({ | ||||||
|  |           name: captureName, | ||||||
|  |           value: captureValue, | ||||||
|  |           start: indices[Number(captureName)][0], | ||||||
|  |           end: indices[Number(captureName)][1], | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     const groups: Array<GroupCapture> = []; | ||||||
|  |     Object.entries(match.groups || {}).forEach(([groupName, groupValue]) => { | ||||||
|  |       groups.push({ | ||||||
|  |         name: groupName, | ||||||
|  |         value: groupValue, | ||||||
|  |         start: indices.groups[groupName][0], | ||||||
|  |         end: indices.groups[groupName][1], | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |     results.push({ | ||||||
|  |       index: match.index, | ||||||
|  |       value: match[0], | ||||||
|  |       captures, | ||||||
|  |       groups, | ||||||
|  |     }); | ||||||
|  |     lastIndex = re.lastIndex; | ||||||
|  |     match = re.exec(text) as RegExpExecArrayWithIndices; | ||||||
|  |   } | ||||||
|  |   return results; | ||||||
|  | } | ||||||
| @ -1,24 +1,10 @@ | |||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import RandExp from 'randexp'; | import RandExp from 'randexp'; | ||||||
|  | import { render } from '@regexper/render'; | ||||||
|  | import { matchRegex } from './regex-tester.service'; | ||||||
| import { useValidation } from '@/composable/validation'; | import { useValidation } from '@/composable/validation'; | ||||||
| import { useQueryParamOrStorage } from '@/composable/queryParams'; | import { useQueryParamOrStorage } from '@/composable/queryParams'; | ||||||
| 
 | 
 | ||||||
| interface RegExpGroupIndices { |  | ||||||
|   [name: string]: [number, number] |  | ||||||
| } |  | ||||||
| interface RegExpIndices extends Array<[number, number]> { |  | ||||||
|   groups: RegExpGroupIndices |  | ||||||
| } |  | ||||||
| interface RegExpExecArrayWithIndices extends RegExpExecArray { |  | ||||||
|   indices: RegExpIndices |  | ||||||
| } |  | ||||||
| interface GroupCapture { |  | ||||||
|   name: string |  | ||||||
|   value: string |  | ||||||
|   start: number |  | ||||||
|   end: number |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const regex = useQueryParamOrStorage({ name: 'regex', storageName: 'regex-tester:regex', defaultValue: '' }); | const regex = useQueryParamOrStorage({ name: 'regex', storageName: 'regex-tester:regex', defaultValue: '' }); | ||||||
| const text = ref(''); | const text = ref(''); | ||||||
| const global = ref(true); | const global = ref(true); | ||||||
| @ -27,6 +13,7 @@ const multiline = ref(false); | |||||||
| const dotAll = ref(true); | const dotAll = ref(true); | ||||||
| const unicode = ref(true); | const unicode = ref(true); | ||||||
| const unicodeSets = ref(false); | const unicodeSets = ref(false); | ||||||
|  | const visualizerSVG = ref() as Ref<SVGSVGElement>; | ||||||
| 
 | 
 | ||||||
| const regexValidation = useValidation({ | const regexValidation = useValidation({ | ||||||
|   source: regex, |   source: regex, | ||||||
| @ -42,10 +29,6 @@ const regexValidation = useValidation({ | |||||||
|   ], |   ], | ||||||
| }); | }); | ||||||
| const results = computed(() => { | const results = computed(() => { | ||||||
|   if (regex.value === '' || text.value === '') { |  | ||||||
|     return []; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   let flags = 'd'; |   let flags = 'd'; | ||||||
|   if (global.value) { |   if (global.value) { | ||||||
|     flags += 'g'; |     flags += 'g'; | ||||||
| @ -67,40 +50,7 @@ const results = computed(() => { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   try { |   try { | ||||||
|     const re = new RegExp(regex.value, flags); |     return matchRegex(regex.value, text.value, flags); | ||||||
|     const results = []; |  | ||||||
|     let match = re.exec(text.value) as RegExpExecArrayWithIndices; |  | ||||||
|     while (match !== null) { |  | ||||||
|       const indices = match.indices; |  | ||||||
|       const captures: Array<GroupCapture> = []; |  | ||||||
|       Object.entries(match).forEach(([captureName, captureValue]) => { |  | ||||||
|         if (captureName !== '0' && captureName.match(/\d+/)) { |  | ||||||
|           captures.push({ |  | ||||||
|             name: captureName, |  | ||||||
|             value: captureValue, |  | ||||||
|             start: indices[Number(captureName)][0], |  | ||||||
|             end: indices[Number(captureName)][1], |  | ||||||
|           }); |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|       const groups: Array<GroupCapture> = []; |  | ||||||
|       Object.entries(match.groups || {}).forEach(([groupName, groupValue]) => { |  | ||||||
|         groups.push({ |  | ||||||
|           name: groupName, |  | ||||||
|           value: groupValue, |  | ||||||
|           start: indices.groups[groupName][0], |  | ||||||
|           end: indices.groups[groupName][1], |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
|       results.push({ |  | ||||||
|         index: match.index, |  | ||||||
|         value: match[0], |  | ||||||
|         captures, |  | ||||||
|         groups, |  | ||||||
|       }); |  | ||||||
|       match = re.exec(text.value) as RegExpExecArrayWithIndices; |  | ||||||
|     } |  | ||||||
|     return results; |  | ||||||
|   } |   } | ||||||
|   catch (_) { |   catch (_) { | ||||||
|     return []; |     return []; | ||||||
| @ -116,6 +66,19 @@ const sample = computed(() => { | |||||||
|     return ''; |     return ''; | ||||||
|   } |   } | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | watchEffect( | ||||||
|  |   async () => { | ||||||
|  |     const regexValue = regex.value; | ||||||
|  |     const svg = visualizerSVG.value; | ||||||
|  |     svg.childNodes.forEach(n => n.remove()); | ||||||
|  |     try { | ||||||
|  |       await render(regexValue, svg); | ||||||
|  |     } | ||||||
|  |     catch (_) { | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | ); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
| @ -164,7 +127,7 @@ const sample = computed(() => { | |||||||
|       /> |       /> | ||||||
|     </c-card> |     </c-card> | ||||||
| 
 | 
 | ||||||
|     <c-card title="Matches" mb-1> |     <c-card title="Matches" mb-1 mt-3> | ||||||
|       <n-table v-if="results?.length > 0"> |       <n-table v-if="results?.length > 0"> | ||||||
|         <thead> |         <thead> | ||||||
|           <tr> |           <tr> | ||||||
| @ -208,8 +171,12 @@ const sample = computed(() => { | |||||||
|       </c-alert> |       </c-alert> | ||||||
|     </c-card> |     </c-card> | ||||||
| 
 | 
 | ||||||
|     <c-card title="Sample matching text"> |     <c-card title="Sample matching text" mt-3> | ||||||
|       <pre style="white-space: pre-wrap">{{ sample }}</pre> |       <pre style="white-space: pre-wrap; word-break: break-all;">{{ sample }}</pre> | ||||||
|  |     </c-card> | ||||||
|  | 
 | ||||||
|  |     <c-card title="Regex Diagram" style="overflow-x: scroll;"  mt-3> | ||||||
|  |       <svg ref="visualizerSVG" /> | ||||||
|     </c-card> |     </c-card> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user