Skip to content

Commit a1540e0

Browse files
authored
Implement shift-click range selection (#48)
* feat: implement shift-click range selection * Improve shift-click range selection * Enable shift+arrow range selection
1 parent 9279c4a commit a1540e0

File tree

1 file changed

+61
-7
lines changed

1 file changed

+61
-7
lines changed

src/extension.ts

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
602602
document.body.setAttribute('tabindex', '0'); document.body.focus();
603603
const vscode = acquireVsCodeApi();
604604
let lastContextIsHeader = false; // remembers whether we right-clicked a <th>
605-
let isUpdating = false, isSelecting = false, anchorCell = null, currentSelection = [];
605+
let isUpdating = false, isSelecting = false, anchorCell = null, rangeEndCell = null, currentSelection = [];
606606
let startCell = null, endCell = null, selectionMode = "cell";
607607
let editingCell = null, originalCellValue = "";
608608
const table = document.querySelector('table');
@@ -704,8 +704,32 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
704704
705705
table.addEventListener('mousedown', e => {
706706
if(e.target.tagName !== 'TD' && e.target.tagName !== 'TH') return;
707-
if(editingCell){ if(e.target !== editingCell) editingCell.blur(); else return; } else clearSelection();
708707
const target = e.target;
708+
709+
// ──────── NEW: Shift+Click range selection ────────
710+
if (
711+
e.shiftKey &&
712+
anchorCell &&
713+
!editingCell &&
714+
target.getAttribute('data-row') !== null &&
715+
target.getAttribute('data-col') !== null &&
716+
anchorCell.getAttribute('data-row') !== null &&
717+
anchorCell.getAttribute('data-col') !== null &&
718+
target.getAttribute('data-col') !== '-1' &&
719+
anchorCell.getAttribute('data-col') !== '-1'
720+
) {
721+
e.preventDefault();
722+
selectRange(
723+
getCellCoords(anchorCell),
724+
getCellCoords(target)
725+
);
726+
rangeEndCell = target;
727+
anchorCell.focus();
728+
return;
729+
}
730+
731+
if(editingCell){ if(e.target !== editingCell) editingCell.blur(); else return; } else clearSelection();
732+
709733
/* ──────── NEW: select-all via top-left header cell ──────── */
710734
if (
711735
target.tagName === 'TH' && // header cell
@@ -722,7 +746,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
722746
/* ──────── END NEW BLOCK ──────── */
723747
724748
selectionMode = (target.tagName === 'TH') ? "column" : (target.getAttribute('data-col') === '-1' ? "row" : "cell");
725-
startCell = target; endCell = target; isSelecting = true; e.preventDefault();
749+
startCell = target; endCell = target; rangeEndCell = target; isSelecting = true; e.preventDefault();
726750
target.focus();
727751
});
728752
table.addEventListener('mousemove', e => {
@@ -731,6 +755,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
731755
if(selectionMode === "cell"){
732756
if(target.tagName === 'TD' || target.tagName === 'TH'){
733757
endCell = target;
758+
rangeEndCell = target;
734759
selectRange(getCellCoords(startCell), getCellCoords(endCell));
735760
}
736761
} else if(selectionMode === "column"){
@@ -739,6 +764,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
739764
target = table.querySelector('thead th[data-col="'+col+'"]') || target;
740765
}
741766
endCell = target;
767+
rangeEndCell = target;
742768
const startCol = parseInt(startCell.getAttribute('data-col'));
743769
const endCol = parseInt(endCell.getAttribute('data-col'));
744770
selectFullColumnRange(startCol, endCol);
@@ -748,6 +774,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
748774
target = table.querySelector('td[data-col="-1"][data-row="'+row+'"]') || target;
749775
}
750776
endCell = target;
777+
rangeEndCell = target;
751778
const startRow = parseInt(startCell.getAttribute('data-row'));
752779
const endRow = parseInt(endCell.getAttribute('data-row'));
753780
selectFullRowRange(startRow, endRow);
@@ -758,16 +785,20 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
758785
isSelecting = false;
759786
if(selectionMode === "cell"){
760787
if(startCell === endCell){
761-
clearSelection(); startCell.classList.add('selected'); currentSelection.push(startCell); anchorCell = startCell;
762-
} else { anchorCell = startCell; }
788+
clearSelection();
789+
startCell.classList.add('selected');
790+
currentSelection.push(startCell);
791+
}
792+
anchorCell = startCell;
793+
rangeEndCell = endCell;
763794
} else if(selectionMode === "column"){
764795
const startCol = parseInt(startCell.getAttribute('data-col'));
765796
const endCol = parseInt(endCell.getAttribute('data-col'));
766-
selectFullColumnRange(startCol, endCol); anchorCell = startCell;
797+
selectFullColumnRange(startCol, endCol); anchorCell = startCell; rangeEndCell = endCell;
767798
} else if(selectionMode === "row"){
768799
const startRow = parseInt(startCell.getAttribute('data-row'));
769800
const endRow = parseInt(endCell.getAttribute('data-row'));
770-
selectFullRowRange(startRow, endRow); anchorCell = startCell;
801+
selectFullRowRange(startRow, endRow); anchorCell = startCell; rangeEndCell = endCell;
771802
}
772803
});
773804
const selectRange = (start, end) => {
@@ -887,6 +918,28 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
887918
}
888919
889920
/* ──────── ARROW KEY NAVIGATION ──────── */
921+
if (!editingCell && anchorCell && e.shiftKey && ['ArrowUp','ArrowDown','ArrowLeft','ArrowRight'].includes(e.key)) {
922+
const { row, col } = getCellCoords(rangeEndCell || anchorCell);
923+
let targetRow = row, targetCol = col;
924+
switch(e.key){
925+
case 'ArrowUp': targetRow = row - 1; break;
926+
case 'ArrowDown': targetRow = row + 1; break;
927+
case 'ArrowLeft': targetCol = col - 1; break;
928+
case 'ArrowRight':targetCol = col + 1; break;
929+
}
930+
if(targetRow < 0 || targetCol < 0) return;
931+
const tag = (hasHeader && targetRow === 0 ? 'th' : 'td');
932+
const nextCell = table.querySelector(\`\${tag}[data-row="\${targetRow}"][data-col="\${targetCol}"]\`);
933+
if(nextCell){
934+
e.preventDefault();
935+
rangeEndCell = nextCell;
936+
selectRange(getCellCoords(anchorCell), getCellCoords(rangeEndCell));
937+
anchorCell.focus({preventScroll:true});
938+
rangeEndCell.scrollIntoView({ block:'nearest', inline:'nearest', behavior:'smooth' });
939+
}
940+
return;
941+
}
942+
890943
if (!editingCell && anchorCell && ['ArrowUp','ArrowDown','ArrowLeft','ArrowRight'].includes(e.key)) {
891944
const { row, col } = getCellCoords(anchorCell);
892945
let targetRow = row, targetCol = col;
@@ -905,6 +958,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider {
905958
nextCell.classList.add('selected');
906959
currentSelection.push(nextCell);
907960
anchorCell = nextCell;
961+
rangeEndCell = nextCell;
908962
nextCell.focus({preventScroll:true});
909963
nextCell.scrollIntoView({ block:'nearest', inline:'nearest', behavior:'smooth' });
910964
}

0 commit comments

Comments
 (0)