Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to the "CSV" extension will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.3] - 2025-06-11
- Added: TSV file support with automatic tab delimiter.

## [1.1.2] - 2025-06-11
- Fixed: fontFamily

Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Working with CSV files shouldn’t be a chore. With CSV, you get:
- **Preserved CSV Integrity:** All modifications respect CSV formatting—no unwanted extra characters or formatting issues.
- **Optimized for Performance:** Designed for medium-sized datasets, ensuring a smooth editing experience without compromising on functionality.
- **Large File Support:** Loads big CSVs in chunks so even large datasets open quickly.
- **TSV Support:** `.tsv` files are recognized automatically and use tabs as the default separator.

---

Expand All @@ -56,10 +57,10 @@ Cursor (built on VS Code 1.99) and the latest VS Code releases (1.102).
- Go to the Extensions view (`Ctrl+Shift+X` or `Cmd+Shift+X` on macOS).
- Search for **CSV** and click **Install**.

### 2. Open a CSV File
### 2. Open a CSV or TSV File

- Open any `.csv` file in VS Code.
- The CSV will automatically load, presenting your file in an interactive grid view.
- Open any `.csv` or `.tsv` file in VS Code.
- The file will automatically load, presenting your data in an interactive grid view.

### 3. Edit and Navigate

Expand Down
12 changes: 11 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"categories": ["Data Science", "Programming Languages", "Other"],
"activationEvents": [
"onLanguage:csv",
"onLanguage:tsv",
"onCommand:csv.toggleExtension",
"onCommand:csv.toggleHeader",
"onCommand:csv.toggleSerialIndex",
Expand All @@ -30,6 +31,12 @@
"extensions": [".csv"],
"aliases": ["CSV", "csv"],
"configuration": "./language-configuration.json"
},
{
"id": "tsv",
"extensions": [".tsv"],
"aliases": ["TSV", "tsv"],
"configuration": "./language-configuration.json"
}
],
"commands": [
Expand Down Expand Up @@ -80,7 +87,10 @@
{
"viewType": "csv.editor",
"displayName": "CSV",
"selector": [{ "filenamePattern": "*.csv" }]
"selector": [
{ "filenamePattern": "*.csv" },
{ "filenamePattern": "*.tsv" }
]
}
]
},
Expand Down
32 changes: 20 additions & 12 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,7 @@
*/
private async updateDocument(row: number, col: number, value: string) {
this.isUpdatingDocument = true;
const config = vscode.workspace.getConfiguration('csv');
const separator = config.get<string>('separator', ',');
const separator = this.getSeparator();
const oldText = this.document.getText();
const lines = oldText.split(/\r?\n/);
let editSucceeded = false;
Expand All @@ -227,8 +226,8 @@
if (!editSucceeded) {
const result = Papa.parse(oldText, { dynamicTyping: false, delimiter: separator });
const data = result.data as string[][];
while (data.length <= row) data.push([]);

Check warning on line 229 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Expected { after 'while' condition
while (data[row].length <= col) data[row].push('');

Check warning on line 230 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Expected { after 'while' condition
data[row][col] = value;
const newCsvText = Papa.unparse(data, { delimiter: separator });
const fullRange = new vscode.Range(0, 0, this.document.lineCount, this.document.lineCount ? this.document.lineAt(this.document.lineCount - 1).text.length : 0);
Expand Down Expand Up @@ -261,14 +260,13 @@
*/
private async insertColumn(index: number) {
this.isUpdatingDocument = true;
const config = vscode.workspace.getConfiguration('csv');
const separator = config.get<string>('separator', ',');
const separator = this.getSeparator();
const text = this.document.getText();
const result = Papa.parse(text, { dynamicTyping: false, delimiter: separator });
const data = result.data as string[][];
for (const row of data) {
if (index > row.length) {
while (row.length < index) row.push('');

Check warning on line 269 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Expected { after 'while' condition
}
row.splice(index, 0, '');
}
Expand All @@ -286,8 +284,7 @@
*/
private async deleteColumn(index: number) {
this.isUpdatingDocument = true;
const config = vscode.workspace.getConfiguration('csv');
const separator = config.get<string>('separator', ',');
const separator = this.getSeparator();
const text = this.document.getText();
const result = Papa.parse(text, { dynamicTyping: false, delimiter: separator });
const data = result.data as string[][];
Expand All @@ -313,7 +310,7 @@
this.isUpdatingDocument = true;

const config = vscode.workspace.getConfiguration('csv');
const separator = config.get<string>('separator', ',');
const separator = this.getSeparator();
const treatHeader = config.get<boolean>('treatFirstRowAsHeader', true);

const text = this.document.getText();
Expand All @@ -330,7 +327,7 @@

const cmp = (a: string, b: string) => {
const na = parseFloat(a), nb = parseFloat(b);
if (!isNaN(na) && !isNaN(nb)) return na - nb; // numeric compare

Check warning on line 330 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Expected { after 'if' condition
return a.localeCompare(b, undefined, { sensitivity: 'base' });
};

Expand Down Expand Up @@ -364,15 +361,14 @@
*/
private async insertRow(index: number) {
this.isUpdatingDocument = true;
const config = vscode.workspace.getConfiguration('csv');
const separator = config.get<string>('separator', ',');
const separator = this.getSeparator();
const text = this.document.getText();
const result = Papa.parse(text, { dynamicTyping: false, delimiter: separator });
const data = result.data as string[][];
const numColumns = Math.max(...data.map(r => r.length), 0);
const newRow = Array(numColumns).fill('');
if (index > data.length) {
while (data.length < index) data.push(Array(numColumns).fill(''));

Check warning on line 371 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Expected { after 'while' condition
}
data.splice(index, 0, newRow);
const newText = Papa.unparse(data, { delimiter: separator });
Expand All @@ -389,8 +385,7 @@
*/
private async deleteRow(index: number) {
this.isUpdatingDocument = true;
const config = vscode.workspace.getConfiguration('csv');
const separator = config.get<string>('separator', ',');
const separator = this.getSeparator();
const text = this.document.getText();
const result = Papa.parse(text, { dynamicTyping: false, delimiter: separator });
const data = result.data as string[][];
Expand All @@ -415,7 +410,7 @@
const config = vscode.workspace.getConfiguration('csv');
const treatHeader = config.get<boolean>('treatFirstRowAsHeader', true);
const addSerialIndex = config.get<boolean>('addSerialIndex', false);
const separator = config.get<string>('separator', ',');
const separator = this.getSeparator();
const text = this.document.getText();
let result;
try {
Expand Down Expand Up @@ -478,8 +473,8 @@
}

// keep **only** the first chunk in the initial render
if (treatHeader) bodyData.length = CHUNK_SIZE;

Check warning on line 476 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Expected { after 'if' condition
else data.length = CHUNK_SIZE;

Check warning on line 477 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Expected { after 'else'
}
/* ────────── END VIRTUAL-SCROLL SUPPORT ────────── */

Expand Down Expand Up @@ -1043,6 +1038,19 @@
return widths;
}

/**
* Determines the delimiter to use based on configuration and file extension.
* `.tsv` files default to a tab separator when the setting is untouched.
*/
private getSeparator(): string {
const config = vscode.workspace.getConfiguration('csv');
let sep = config.get<string>('separator', ',');
if (sep === ',' && this.document?.uri.fsPath.toLowerCase().endsWith('.tsv')) {
sep = '\t';
}
return sep;
}

/**
* Escapes HTML special characters in a string to prevent injection.
*/
Expand Down Expand Up @@ -1071,11 +1079,11 @@
for (const cell of column) {
const items = cell.split(',').map(item => item.trim());
for (const item of items){
if (item === '') continue;

Check warning on line 1082 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Expected { after 'if' condition
allEmpty = false;
const lower = item.toLowerCase();
if (!(lower === 'true' || lower === 'false')) allBoolean = false;

Check warning on line 1085 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Expected { after 'if' condition
if (!this.isDate(item)) allDate = false;

Check warning on line 1086 in src/extension.ts

View workflow job for this annotation

GitHub Actions / build

Expected { after 'if' condition
const num = Number(item);
if (!Number.isInteger(num)) allInteger = false;
if (isNaN(num)) allFloat = false;
Expand Down
Loading