diff --git a/src/extension.ts b/src/extension.ts index fc86d1b..45cc3ca 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -602,7 +602,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider { document.body.setAttribute('tabindex', '0'); document.body.focus(); const vscode = acquireVsCodeApi(); let lastContextIsHeader = false; // remembers whether we right-clicked a - let isUpdating = false, isSelecting = false, anchorCell = null, currentSelection = []; + let isUpdating = false, isSelecting = false, anchorCell = null, rangeEndCell = null, currentSelection = []; let startCell = null, endCell = null, selectionMode = "cell"; let editingCell = null, originalCellValue = ""; const table = document.querySelector('table'); @@ -704,8 +704,32 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider { table.addEventListener('mousedown', e => { if(e.target.tagName !== 'TD' && e.target.tagName !== 'TH') return; - if(editingCell){ if(e.target !== editingCell) editingCell.blur(); else return; } else clearSelection(); const target = e.target; + + // ──────── NEW: Shift+Click range selection ──────── + if ( + e.shiftKey && + anchorCell && + !editingCell && + target.getAttribute('data-row') !== null && + target.getAttribute('data-col') !== null && + anchorCell.getAttribute('data-row') !== null && + anchorCell.getAttribute('data-col') !== null && + target.getAttribute('data-col') !== '-1' && + anchorCell.getAttribute('data-col') !== '-1' + ) { + e.preventDefault(); + selectRange( + getCellCoords(anchorCell), + getCellCoords(target) + ); + rangeEndCell = target; + anchorCell.focus(); + return; + } + + if(editingCell){ if(e.target !== editingCell) editingCell.blur(); else return; } else clearSelection(); + /* ──────── NEW: select-all via top-left header cell ──────── */ if ( target.tagName === 'TH' && // header cell @@ -722,7 +746,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider { /* ──────── END NEW BLOCK ──────── */ selectionMode = (target.tagName === 'TH') ? "column" : (target.getAttribute('data-col') === '-1' ? "row" : "cell"); - startCell = target; endCell = target; isSelecting = true; e.preventDefault(); + startCell = target; endCell = target; rangeEndCell = target; isSelecting = true; e.preventDefault(); target.focus(); }); table.addEventListener('mousemove', e => { @@ -731,6 +755,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider { if(selectionMode === "cell"){ if(target.tagName === 'TD' || target.tagName === 'TH'){ endCell = target; + rangeEndCell = target; selectRange(getCellCoords(startCell), getCellCoords(endCell)); } } else if(selectionMode === "column"){ @@ -739,6 +764,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider { target = table.querySelector('thead th[data-col="'+col+'"]') || target; } endCell = target; + rangeEndCell = target; const startCol = parseInt(startCell.getAttribute('data-col')); const endCol = parseInt(endCell.getAttribute('data-col')); selectFullColumnRange(startCol, endCol); @@ -748,6 +774,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider { target = table.querySelector('td[data-col="-1"][data-row="'+row+'"]') || target; } endCell = target; + rangeEndCell = target; const startRow = parseInt(startCell.getAttribute('data-row')); const endRow = parseInt(endCell.getAttribute('data-row')); selectFullRowRange(startRow, endRow); @@ -758,16 +785,20 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider { isSelecting = false; if(selectionMode === "cell"){ if(startCell === endCell){ - clearSelection(); startCell.classList.add('selected'); currentSelection.push(startCell); anchorCell = startCell; - } else { anchorCell = startCell; } + clearSelection(); + startCell.classList.add('selected'); + currentSelection.push(startCell); + } + anchorCell = startCell; + rangeEndCell = endCell; } else if(selectionMode === "column"){ const startCol = parseInt(startCell.getAttribute('data-col')); const endCol = parseInt(endCell.getAttribute('data-col')); - selectFullColumnRange(startCol, endCol); anchorCell = startCell; + selectFullColumnRange(startCol, endCol); anchorCell = startCell; rangeEndCell = endCell; } else if(selectionMode === "row"){ const startRow = parseInt(startCell.getAttribute('data-row')); const endRow = parseInt(endCell.getAttribute('data-row')); - selectFullRowRange(startRow, endRow); anchorCell = startCell; + selectFullRowRange(startRow, endRow); anchorCell = startCell; rangeEndCell = endCell; } }); const selectRange = (start, end) => { @@ -887,6 +918,28 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider { } /* ──────── ARROW KEY NAVIGATION ──────── */ + if (!editingCell && anchorCell && e.shiftKey && ['ArrowUp','ArrowDown','ArrowLeft','ArrowRight'].includes(e.key)) { + const { row, col } = getCellCoords(rangeEndCell || anchorCell); + let targetRow = row, targetCol = col; + switch(e.key){ + case 'ArrowUp': targetRow = row - 1; break; + case 'ArrowDown': targetRow = row + 1; break; + case 'ArrowLeft': targetCol = col - 1; break; + case 'ArrowRight':targetCol = col + 1; break; + } + if(targetRow < 0 || targetCol < 0) return; + const tag = (hasHeader && targetRow === 0 ? 'th' : 'td'); + const nextCell = table.querySelector(\`\${tag}[data-row="\${targetRow}"][data-col="\${targetCol}"]\`); + if(nextCell){ + e.preventDefault(); + rangeEndCell = nextCell; + selectRange(getCellCoords(anchorCell), getCellCoords(rangeEndCell)); + anchorCell.focus({preventScroll:true}); + rangeEndCell.scrollIntoView({ block:'nearest', inline:'nearest', behavior:'smooth' }); + } + return; + } + if (!editingCell && anchorCell && ['ArrowUp','ArrowDown','ArrowLeft','ArrowRight'].includes(e.key)) { const { row, col } = getCellCoords(anchorCell); let targetRow = row, targetCol = col; @@ -905,6 +958,7 @@ class CsvEditorProvider implements vscode.CustomTextEditorProvider { nextCell.classList.add('selected'); currentSelection.push(nextCell); anchorCell = nextCell; + rangeEndCell = nextCell; nextCell.focus({preventScroll:true}); nextCell.scrollIntoView({ block:'nearest', inline:'nearest', behavior:'smooth' }); }