Skip to content

Commit 50b5925

Browse files
committed
Add Column sorting
1 parent d74a7bd commit 50b5925

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

src/extension.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
122122
case 'deleteRow':
123123
await this.deleteRow(e.index);
124124
break;
125+
case 'sortColumn':
126+
await this.sortColumn(e.index, e.ascending);
127+
break;
125128
}
126129
});
127130

@@ -266,6 +269,58 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
266269
this.isUpdatingDocument = false;
267270
this.updateWebviewContent();
268271
}
272+
/* ───────────── NEW SORT COLUMN METHOD ───────────── */
273+
/**
274+
* Sorts the CSV rows (skipping the header if it is being treated as one)
275+
* by the given column index, ascending ⇧ or descending ⇩.
276+
*/
277+
private async sortColumn(index: number, ascending: boolean) {
278+
this.isUpdatingDocument = true;
279+
280+
const config = vscode.workspace.getConfiguration('csv');
281+
const separator = config.get<string>('separator', ',');
282+
const treatHeader = config.get<boolean>('treatFirstRowAsHeader', true);
283+
284+
const text = this.document.getText();
285+
const result = Papa.parse(text, { dynamicTyping: false, delimiter: separator });
286+
const rows = result.data as string[][];
287+
288+
let header: string[] = [];
289+
let body: string[][] = rows;
290+
291+
if (treatHeader && rows.length) {
292+
header = rows[0];
293+
body = rows.slice(1);
294+
}
295+
296+
const cmp = (a: string, b: string) => {
297+
const na = parseFloat(a), nb = parseFloat(b);
298+
if (!isNaN(na) && !isNaN(nb)) return na - nb; // numeric compare
299+
return a.localeCompare(b, undefined, { sensitivity: 'base' });
300+
};
301+
302+
body.sort((r1, r2) => {
303+
const diff = cmp(r1[index] ?? '', r2[index] ?? '');
304+
return ascending ? diff : -diff;
305+
});
306+
307+
const newCsv = Papa.unparse(treatHeader ? [header, ...body] : body, { delimiter: separator });
308+
309+
const fullRange = new vscode.Range(
310+
0, 0,
311+
this.document.lineCount,
312+
this.document.lineCount ? this.document.lineAt(this.document.lineCount - 1).text.length : 0
313+
);
314+
315+
const edit = new vscode.WorkspaceEdit();
316+
edit.replace(this.document.uri, fullRange, newCsv);
317+
await vscode.workspace.applyEdit(edit);
318+
319+
this.isUpdatingDocument = false;
320+
this.updateWebviewContent();
321+
console.log(`CSV: Sorted column ${index + 1} (${ascending ? 'A-Z' : 'Z-A'})`);
322+
}
323+
269324

270325
/* ───────────── NEW ROW METHODS ───────────── */
271326

@@ -513,6 +568,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
513568
document.body.setAttribute('tabindex', '0'); document.body.focus();
514569
window.addEventListener('mousedown', () => document.body.focus());
515570
const vscode = acquireVsCodeApi();
571+
let lastContextIsHeader = false; // remembers whether we right-clicked a <th>
516572
let isUpdating = false, isSelecting = false, anchorCell = null, currentSelection = [];
517573
let startCell = null, endCell = null, selectionMode = "cell";
518574
let editingCell = null, originalCellValue = "";
@@ -566,8 +622,18 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
566622
contextMenu.appendChild(d);
567623
};
568624
let addedRowItems = false;
625+
626+
/* Header-only: SORT functionality */
627+
if (lastContextIsHeader) {
628+
item('Sort: A-Z', () =>
629+
vscode.postMessage({ type: 'sortColumn', index: col, ascending: true }));
630+
item('Sort: Z-A', () =>
631+
vscode.postMessage({ type: 'sortColumn', index: col, ascending: false }));
632+
}
633+
569634
/* Row section */
570635
if (!isNaN(row) && row >= 0) {
636+
if (contextMenu.children.length) divider();
571637
item('Add ROW: above', () => vscode.postMessage({ type: 'insertRow', index: row }));
572638
item('Add ROW: below', () => vscode.postMessage({ type: 'insertRow', index: row + 1 }));
573639
item('Delete ROW', () => vscode.postMessage({ type: 'deleteRow', index: row }));
@@ -581,6 +647,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
581647
item('Add COLUMN: right', () => vscode.postMessage({ type: 'insertColumn', index: col + 1 }));
582648
item('Delete COLUMN', () => vscode.postMessage({ type: 'deleteColumn', index: col }));
583649
}
650+
584651
contextMenu.style.left = x + 'px';
585652
contextMenu.style.top = y + 'px';
586653
contextMenu.style.display = 'block';
@@ -598,6 +665,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
598665
const row = parseInt(rowAttr);
599666
if ((isNaN(col) || col === -1) && (isNaN(row) || row === -1)) return;
600667
e.preventDefault();
668+
lastContextIsHeader = target.tagName === 'TH';
601669
showContextMenu(e.pageX, e.pageY, row, col);
602670
});
603671

0 commit comments

Comments
 (0)