From f01e455ba93e7e9fa23d41e073b8e400e48016a9 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Tue, 10 Aug 2021 23:25:21 +0550 Subject: [PATCH 1/5] fix: respect separators while sorting fixes issue #161 --- src/lib/commands.ts | 6 ++++-- src/utils/index.ts | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/lib/commands.ts b/src/lib/commands.ts index 18e50e2..30548bc 100644 --- a/src/lib/commands.ts +++ b/src/lib/commands.ts @@ -5,7 +5,7 @@ import { StyleSheet } from 'windicss/utils/style'; import { Log } from '../utils/console'; import { HTMLParser } from '../utils/parser'; -import { sortClassNames } from '../utils'; +import { combineSeparators, getAllSeparators, sortClassNames } from '../utils'; import { toggleConfig } from '../utils/helpers'; import type { Processor } from 'windicss/lib'; @@ -80,7 +80,9 @@ export default class Commands { for (const p of classes) { const sortedP = sortClassNames(p.result, variantsMap); - textEdit.replace(new Range(textEditor.document.positionAt(p.start), textEditor.document.positionAt(p.end)), sortedP); + const separators = getAllSeparators(p.result); + const toReplace = combineSeparators(separators, sortedP); + textEdit.replace(new Range(textEditor.document.positionAt(p.start), textEditor.document.positionAt(p.end)), toReplace); } }); } diff --git a/src/utils/index.ts b/src/utils/index.ts index 983b43b..3b34ad3 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -36,7 +36,7 @@ export function connectList(list: T[][]) { export function sortClassNames(classNames: string, variantsMap: { [key: string]: number }) { const variantsArray = Object.keys(variantsMap); - const ast = new ClassParser(classNames, ":", variantsArray).parse(); + const ast = new ClassParser(classNames, ':', variantsArray).parse(); return ast.map(({ raw, variants, important }) => { const head = variants.join(':') + ':'; const utility = raw.replace(head, ''); @@ -45,9 +45,41 @@ export function sortClassNames(classNames: string, variantsMap: { [key: string]: const offset = variants.map(i => variantsMap[i] * 100).reduce((p, c) => p + c, 0) + (important ? 500 : 0) + (hasDynamicValue ? 25 : 0); if (key === null) return { raw, weight: offset }; return { raw, weight: (keyOrder[key[0]] ?? 300) + offset }; - }).sort((a, b) => a.weight - b.weight).map(i => i.raw).join(' '); + }).sort((a, b) => a.weight - b.weight).map(i => i.raw); } +export function getAllSeparators(text: string) { + const separators = ['']; + let last = ''; + if (!/\s/g.test(text[0])) { + separators.push(''); + } + for (const ch of text) { + if (/\s/.test(ch)) { + separators[separators.length - 1] += ch; + last = ch; + } else { + if (/\s/g.test(last)) { + separators.push(''); + last = ch; + } + } + } + return separators; +} + +export function combineSeparators(separators: string[], sortedP: string[]) { + let ret = ''; + let i = 0; + for (i = 0; i < sortedP.length; i++) { + ret += separators[i]; + ret += sortedP[i]; + } + ret += separators[i]; + return ret; +} + + export function rem2px(str?: string) { if (!str) return; let index = 0; From 151d6ad7a94fd4fde12c381e6046c23d36e406f2 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 11 Aug 2021 04:41:24 +0530 Subject: [PATCH 2/5] test(sort.test.ts): add tests for sorting --- test/sort.test.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 test/sort.test.ts diff --git a/test/sort.test.ts b/test/sort.test.ts new file mode 100644 index 0000000..b5a550f --- /dev/null +++ b/test/sort.test.ts @@ -0,0 +1,44 @@ +import { HTMLParser } from 'windicss/utils/parser'; +import { + sortClassNames, + getAllSeparators, + combineSeparators, +} from '../src/utils'; + +it('sort classes', () => { + const parser = new HTMLParser(); + expect(parser.parseClasses().length).toBe(0); + parser.html = ` +
+
+ `; + const classes = parser.parseClasses(); + const variants = Object.keys({}); + const variantsMap = Object.assign( + {}, + ...variants.map((value, index) => ({ [value]: index + 1 })) + ); + const expected = [ + 'bg-transparent text-transparent p-4 backdrop-blur', + ` + bg-transparent + text-transparent + p-4 + backdrop-blur + `, + ]; + for (let i = 0; i < classes.length; i++) { + const p = classes[i]; + const sortedP = sortClassNames(p.result, variantsMap); + const separators = getAllSeparators(p.result); + const toReplace = combineSeparators(separators, sortedP); + expect(toReplace).toBe(expected[i]); + } +}); From 046732fc3f5b34ad687d5b615690afc9de5dfc43 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 11 Aug 2021 08:02:09 +0550 Subject: [PATCH 3/5] test(sort.test.ts): add tests for malformed HTML --- test/sort.test.ts | 82 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 21 deletions(-) diff --git a/test/sort.test.ts b/test/sort.test.ts index b5a550f..7f44d57 100644 --- a/test/sort.test.ts +++ b/test/sort.test.ts @@ -5,11 +5,35 @@ import { combineSeparators, } from '../src/utils'; -it('sort classes', () => { +it('sorts classes single line', () => { const parser = new HTMLParser(); - expect(parser.parseClasses().length).toBe(0); parser.html = `
+ `; + const p = parser.parseClasses()[0]; + const expected = 'bg-transparent text-transparent p-4 backdrop-blur'; + const sortedP = sortClassNames(p.result, {}); + const separators = getAllSeparators(p.result); + const toReplace = combineSeparators(separators, sortedP); + expect(toReplace).toBe(expected); +}); + +it('sorts classes single line but malformed', () => { + const parser = new HTMLParser(); + parser.html = ` +
+ `; + const p = parser.parseClasses()[0]; + const expected = ' bg-transparent text-transparent p-4 backdrop-blur'; + const sortedP = sortClassNames(p.result, {}); + const separators = getAllSeparators(p.result); + const toReplace = combineSeparators(separators, sortedP); + expect(toReplace).toBe(expected); +}); + +it('sorts classes multi line', () => { + const parser = new HTMLParser(); + parser.html = `
- `; - const classes = parser.parseClasses(); - const variants = Object.keys({}); - const variantsMap = Object.assign( - {}, - ...variants.map((value, index) => ({ [value]: index + 1 })) - ); - const expected = [ - 'bg-transparent text-transparent p-4 backdrop-blur', - ` +`; + const p = parser.parseClasses()[0]; + const expected = ` bg-transparent text-transparent p-4 backdrop-blur - `, - ]; - for (let i = 0; i < classes.length; i++) { - const p = classes[i]; - const sortedP = sortClassNames(p.result, variantsMap); - const separators = getAllSeparators(p.result); - const toReplace = combineSeparators(separators, sortedP); - expect(toReplace).toBe(expected[i]); - } + `; + + const sortedP = sortClassNames(p.result, {}); + const separators = getAllSeparators(p.result); + const toReplace = combineSeparators(separators, sortedP); + expect(toReplace).toBe(expected); +}); + +it('sorts classes multi line but malformed', () => { + const parser = new HTMLParser(); + parser.html = ` +
+`; + const p = parser.parseClasses()[0]; + const expected = ` + bg-transparent + text-transparent + p-4 +backdrop-blur + `; + + const sortedP = sortClassNames(p.result, {}); + const separators = getAllSeparators(p.result); + const toReplace = combineSeparators(separators, sortedP); + expect(toReplace).toBe(expected); }); From eeaa4bb9ffc40ee6c46dbfe5b688b1e0a97959b7 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 11 Aug 2021 09:11:24 +0550 Subject: [PATCH 4/5] perf(commands.ts, index.ts): improve sorting Use new strategy to sort classes and preserve formatting. --- src/lib/commands.ts | 5 ++--- src/utils/index.ts | 54 ++++++++++++++++++++++----------------------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/lib/commands.ts b/src/lib/commands.ts index 30548bc..3f87092 100644 --- a/src/lib/commands.ts +++ b/src/lib/commands.ts @@ -5,7 +5,7 @@ import { StyleSheet } from 'windicss/utils/style'; import { Log } from '../utils/console'; import { HTMLParser } from '../utils/parser'; -import { combineSeparators, getAllSeparators, sortClassNames } from '../utils'; +import { rearrangeClasses, sortClassNames } from '../utils'; import { toggleConfig } from '../utils/helpers'; import type { Processor } from 'windicss/lib'; @@ -80,8 +80,7 @@ export default class Commands { for (const p of classes) { const sortedP = sortClassNames(p.result, variantsMap); - const separators = getAllSeparators(p.result); - const toReplace = combineSeparators(separators, sortedP); + const toReplace = rearrangeClasses(p.result, sortedP); textEdit.replace(new Range(textEditor.document.positionAt(p.start), textEditor.document.positionAt(p.end)), toReplace); } }); diff --git a/src/utils/index.ts b/src/utils/index.ts index 3b34ad3..ee9135c 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -48,38 +48,38 @@ export function sortClassNames(classNames: string, variantsMap: { [key: string]: }).sort((a, b) => a.weight - b.weight).map(i => i.raw); } -export function getAllSeparators(text: string) { - const separators = ['']; - let last = ''; - if (!/\s/g.test(text[0])) { - separators.push(''); - } - for (const ch of text) { - if (/\s/.test(ch)) { - separators[separators.length - 1] += ch; - last = ch; - } else { - if (/\s/g.test(last)) { - separators.push(''); - last = ch; - } - } - } - return separators; +function replaceBetweenIndices( + origin: string, + startIndex: number, + endIndex: number, + find: string, + replace: string +) { + return ( + origin.substring(0, startIndex) + + origin.substring(startIndex, endIndex).replace(find, replace) + ); } -export function combineSeparators(separators: string[], sortedP: string[]) { - let ret = ''; - let i = 0; - for (i = 0; i < sortedP.length; i++) { - ret += separators[i]; - ret += sortedP[i]; +export function rearrangeClasses(classesText: string, sortedP: string[]) { + const unsortedClasses = classesText + .split(/\s+/) + .map((s) => s.trim()) + .filter(Boolean); + let occurrenceIndex = 0; + for (let i = 0; i < sortedP.length; i++) { + classesText = replaceBetweenIndices( + classesText, + occurrenceIndex, + classesText.length, + unsortedClasses[i], + sortedP[i] + ); + occurrenceIndex = occurrenceIndex + sortedP[0].length + 1; } - ret += separators[i]; - return ret; + return classesText; } - export function rem2px(str?: string) { if (!str) return; let index = 0; From 635ecc7cbc509d0cce13ba5927e2ad29f6076a58 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 11 Aug 2021 09:11:57 +0550 Subject: [PATCH 5/5] test(sort.test.ts): use new functions for testing --- test/sort.test.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/test/sort.test.ts b/test/sort.test.ts index 7f44d57..eafb42b 100644 --- a/test/sort.test.ts +++ b/test/sort.test.ts @@ -1,8 +1,7 @@ import { HTMLParser } from 'windicss/utils/parser'; import { sortClassNames, - getAllSeparators, - combineSeparators, + rearrangeClasses, } from '../src/utils'; it('sorts classes single line', () => { @@ -13,8 +12,7 @@ it('sorts classes single line', () => { const p = parser.parseClasses()[0]; const expected = 'bg-transparent text-transparent p-4 backdrop-blur'; const sortedP = sortClassNames(p.result, {}); - const separators = getAllSeparators(p.result); - const toReplace = combineSeparators(separators, sortedP); + const toReplace = rearrangeClasses(p.result, sortedP); expect(toReplace).toBe(expected); }); @@ -26,8 +24,7 @@ it('sorts classes single line but malformed', () => { const p = parser.parseClasses()[0]; const expected = ' bg-transparent text-transparent p-4 backdrop-blur'; const sortedP = sortClassNames(p.result, {}); - const separators = getAllSeparators(p.result); - const toReplace = combineSeparators(separators, sortedP); + const toReplace = rearrangeClasses(p.result, sortedP); expect(toReplace).toBe(expected); }); @@ -52,8 +49,7 @@ it('sorts classes multi line', () => { `; const sortedP = sortClassNames(p.result, {}); - const separators = getAllSeparators(p.result); - const toReplace = combineSeparators(separators, sortedP); + const toReplace = rearrangeClasses(p.result, sortedP); expect(toReplace).toBe(expected); }); @@ -78,7 +74,6 @@ backdrop-blur `; const sortedP = sortClassNames(p.result, {}); - const separators = getAllSeparators(p.result); - const toReplace = combineSeparators(separators, sortedP); + const toReplace = rearrangeClasses(p.result, sortedP); expect(toReplace).toBe(expected); });