Skip to content

Commit

Permalink
Merge pull request #6 from HarshKothari88:HarshKothari88/issue1
Browse files Browse the repository at this point in the history
HarshKothari88/issue1
  • Loading branch information
HarshKothari88 authored Jan 16, 2025
2 parents f5d9db8 + ffeac42 commit c33b71d
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 19 deletions.
22 changes: 21 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@
"title": "Clear Search",
"category": "Svelte Radar",
"icon": "$(clear-all)"
},
{
"command": "svelteRadar.toggleSorting",
"title": "Toggle Sorting Type",
"category": "Svelte Radar",
"icon": "$(sort-precedence)"
}
],
"viewsContainers": {
Expand Down Expand Up @@ -120,6 +126,11 @@
"command": "svelteRadar.search",
"when": "view == routesView",
"group": "navigation"
},
{
"command": "svelteRadar.toggleSorting",
"when": "view == routesView",
"group": "navigation"
}
],
"view/item/context": [
Expand Down Expand Up @@ -151,6 +162,15 @@
],
"default": "flat",
"description": "Display routes in flat or hierarchical view"
},
"svelteRadar.sortingType": {
"type": "string",
"enum": [
"natural",
"basic"
],
"default": "natural",
"description": "Route sorting type (natural: natural number sorting, basic: basic string comparison)"
}
}
}
Expand Down Expand Up @@ -191,4 +211,4 @@
"glob": "^11.0.0",
"path": "^0.12.7"
}
}
}
10 changes: 10 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ export function activate(context: vscode.ExtensionContext) {
vscode.window.showTextDocument(vscode.Uri.file(route.filePath));
}
}
},
{
command: 'svelteRadar.toggleSorting',
callback: async () => {
const config = vscode.workspace.getConfiguration('svelteRadar');
const currentType = config.get('sortingType', 'natural');
const newType = currentType === 'natural' ? 'basic' : 'natural';
await config.update('sortingType', newType, true);
routesProvider.refresh();
}
}
];

Expand Down
57 changes: 39 additions & 18 deletions src/providers/routesProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,19 @@ export class RoutesProvider implements vscode.TreeDataProvider<RouteItem> {
private buildRoutesTree(dir: string, basePath: string): RouteItem[] {
const entries = fs.readdirSync(dir).filter(file => !file.startsWith("."));
const routes: RouteItem[] = [];

entries.sort((a, b) => this.compareRoutes(a, b));

for (const entry of entries) {
const fullPath = path.join(dir, entry);
const stat = fs.statSync(fullPath);

if (stat.isDirectory()) {
const routePath = path.join(basePath, entry);
const routeType = this.determineRouteType(entry);
const pageInfo = this.findPageInfo(fullPath);
const children = this.buildRoutesTree(fullPath, routePath);

if (this.flatView) {
// In flat view, only add the route if it has a page file
if (pageInfo.filePath) {
Expand Down Expand Up @@ -130,7 +130,7 @@ export class RoutesProvider implements vscode.TreeDataProvider<RouteItem> {
}
}
}

return routes;
}

Expand Down Expand Up @@ -249,19 +249,40 @@ export class RoutesProvider implements vscode.TreeDataProvider<RouteItem> {
}

private compareRoutes(a: string, b: string): number {
// Static segments win over dynamic segments
const aIsDynamic = a.includes('[');
const bIsDynamic = b.includes('[');
if (!aIsDynamic && bIsDynamic) { return -1; }
if (aIsDynamic && !bIsDynamic) { return 1; }

// Rest/optional parameters have lowest priority
const aIsSpecial = a.startsWith('[...') || a.startsWith('[[');
const bIsSpecial = b.startsWith('[...') || b.startsWith('[[');
if (!aIsSpecial && bIsSpecial) { return -1; }
if (aIsSpecial && !bIsSpecial) { return 1; }

return 0;
const sortingType = vscode.workspace.getConfiguration('svelteRadar').get<'natural' | 'basic'>('sortingType', 'natural');

// Helper to get route type priority
const getRoutePriority = (route: string): number => {
const segment = route.split('/').pop() || '';

// Check if it's a special parameter first (rest/optional)
const isSpecial = segment.startsWith('[...') || segment.startsWith('[[');
if (isSpecial) {
if (segment.startsWith('[...')) { return 0; } // rest parameters (lowest)
if (segment.startsWith('[[')) { return 1; } // optional parameters
}

// Then handle static vs dynamic
const isDynamic = segment.includes('[');
if (isDynamic) { return 2; } // dynamic parameters
return 3; // static routes (highest)
};

// Compare route types first
const aPriority = getRoutePriority(a);
const bPriority = getRoutePriority(b);

if (aPriority !== bPriority) {
return bPriority - aPriority; // Higher priority comes first
}

// For routes of same type, use selected sorting method
if (sortingType === 'natural') {
return RouteUtils.naturalSort(a, b);
}

// Default string comparison
return a.localeCompare(b);
}

async search() {
Expand Down
127 changes: 127 additions & 0 deletions src/test/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,131 @@ suite('Route Matching Test Suite', () => {
);
});
});
});

suite('Route Sorting Test Suite', () => {
let routesProvider: RoutesProvider;
let workspaceDir: string;

suiteSetup(async () => {
workspaceDir = path.resolve(__dirname, '../../test-fixtures');
routesProvider = new RoutesProvider(workspaceDir);
});

test('Basic sorting should maintain standard string order', async () => {
// Set sorting to default
await vscode.workspace.getConfiguration('svelteRadar').update('sortingType', 'basic', true);

const testRoutes = [
'blog/1-first',
'blog/10-tenth',
'blog/2-second',
'blog/20-twentieth'
];

// Sort using the provider's compareRoutes method
const sorted = testRoutes.sort((a, b) => routesProvider['compareRoutes'](a, b));

// In default sorting, string comparison means 10 comes before 2
assert.deepStrictEqual(sorted, [
'blog/1-first',
'blog/10-tenth',
'blog/2-second',
'blog/20-twentieth'
]);
});

test('Natural sorting should order numbers correctly', async () => {
// Set sorting to natural
await vscode.workspace.getConfiguration('svelteRadar').update('sortingType', 'natural', true);

const testRoutes = [
'blog/1-first',
'blog/10-tenth',
'blog/2-second',
'blog/20-twentieth'
];

// Sort using the provider's compareRoutes method
const sorted = testRoutes.sort((a, b) => routesProvider['compareRoutes'](a, b));

// In natural sorting, numbers should be ordered naturally
assert.deepStrictEqual(sorted, [
'blog/1-first',
'blog/2-second',
'blog/10-tenth',
'blog/20-twentieth'
]);
});

test('Route type priorities should be maintained with natural sorting', async () => {
// Set sorting to natural
await vscode.workspace.getConfiguration('svelteRadar').update('sortingType', 'natural', true);

const testRoutes = [
'blog/1-first',
'blog/[slug]',
'blog/2-second',
'blog/[...rest]',
'blog/10-tenth',
'blog/[[optional]]'
];

// Sort using the provider's compareRoutes method
const sorted = testRoutes.sort((a, b) => routesProvider['compareRoutes'](a, b));

// Static routes first, then dynamic, then optional, then rest
assert.deepStrictEqual(sorted, [
'blog/1-first',
'blog/2-second',
'blog/10-tenth',
'blog/[slug]',
'blog/[[optional]]',
'blog/[...rest]'
]);
});

test('Natural sorting should handle numbers anywhere in path', async () => {
await vscode.workspace.getConfiguration('svelteRadar').update('sortingType', 'natural', true);

const testRoutes = [
'item10',
'item1',
'item2',
'path/to/page1/section10',
'path/to/page1/section2',
'article-1-draft',
'article-10-final',
'article-2-review',
'section5-part10',
'section5-part2',
'xyz10abc20',
'xyz2abc10',
'xyz10abc10',
'1article',
'10article',
'2article'
];

const sorted = testRoutes.sort((a, b) => routesProvider['compareRoutes'](a, b));

assert.deepStrictEqual(sorted, [
'1article',
'2article',
'10article',
'article-1-draft',
'article-2-review',
'article-10-final',
'item1',
'item2',
'item10',
'path/to/page1/section2',
'path/to/page1/section10',
'section5-part2',
'section5-part10',
'xyz2abc10',
'xyz10abc10',
'xyz10abc20'
]);
});
});
10 changes: 10 additions & 0 deletions src/test/fixtures/create-test-fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ export async function createTestFixtures() {
await createDir('products/[slug]');
await createFile('products/[slug]/+page.svelte');

// Natural sorting test
await createDir('blog');
await createFile('blog/+layout.svelte');
await createFile('blog/1-first/+page.svelte');
await createFile('blog/2-second/+page.svelte');
await createFile('blog/10-tenth/+page.svelte');
await createFile('blog/[slug]/+page.svelte');
await createFile('blog/[[optional]]/+page.svelte');
await createFile('blog/[...rest]/+page.svelte');


console.log('Test fixtures created successfully!');
console.log('Fixtures location:', fixturesDir);
Expand Down
31 changes: 31 additions & 0 deletions src/utils/routeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,35 @@ export class RouteUtils {
if (name.startsWith('[') && name.endsWith(']')) { return `:${name.slice(1, -1)}`; }
return name;
}

static naturalSort(a: string, b: string): number {
// Helper function to convert string into sortable parts
// It splits the string into chunks of text and numbers
const splitIntoPartsWithNumbers = (str: string) => {
return str.split(/(\d+)/).map(part => {
// Convert number strings to actual numbers for proper comparison
const num = parseInt(part);
return isNaN(num) ? part : num;
});
};

// Split both paths into their components
const partsA = splitIntoPartsWithNumbers(a);
const partsB = splitIntoPartsWithNumbers(b);

// Compare each part
for (let i = 0; i < Math.min(partsA.length, partsB.length); i++) {
if (partsA[i] !== partsB[i]) {
// If both parts are numbers, do numeric comparison
if (typeof partsA[i] === 'number' && typeof partsB[i] === 'number') {
return (partsA[i] as number) - (partsB[i] as number);
}
// Otherwise do string comparison
return String(partsA[i]).localeCompare(String(partsB[i]));
}
}

// If all parts match up to this point, shorter string comes first
return partsA.length - partsB.length;
}
}

0 comments on commit c33b71d

Please sign in to comment.