diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..34a59d0 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/dist/**/*.js" + ], + "preLaunchTask": "${defaultBuildTask}" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..34edf97 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,18 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/.vscodeignore b/.vscodeignore index d255964..5dd7b05 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -12,3 +12,4 @@ vsc-extension-quickstart.md **/*.map **/*.ts **/.vscode-test.* +*.vsix diff --git a/CHANGELOG.md b/CHANGELOG.md index 336c6c0..17444d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,17 @@ All notable changes to the "sheetjs-demo" extension will be documented in this file. -Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. +## [0.1.0] -## [Unreleased] +- Added commands to disable/enable viewer for specific file extensions +- Added right-click context menu options for spreadsheet files +- Automatic file reloading when files are edited externally +- Command palette integration for quick toggling -- Initial release \ No newline at end of file +## [0.0.9] + +- View spreadsheets directly in VSCode +- Support for 30+ file formats (XLSX, XLS, CSV, ODS, and more) +- Multi-level caching for workbooks and sheets +- On-demand sheet loading +- Pagination for large files \ No newline at end of file diff --git a/LICENSE b/LICENSE index 5723c45..ffaa914 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright (C) 2014-present SheetJS LLC + Copyright (C) 2025-present Asadbek Karimov Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 9cda12f..852df6e 100644 --- a/README.md +++ b/README.md @@ -17,19 +17,12 @@ Powered by [SheetJS](http://sheetjs.com/) a powerful VSCode extension that lets ## Key Features -The extension uses sophisticated optimization techniques to ensure smooth performance: -- **Multi-level Caching** - - Workbook cache prevents redundant parsing of the same files - - Sheet HTML cache eliminates regeneration of previously viewed sheets -- **Smart Loading Strategy** - - First sheet loads immediately for instant feedback - - Additional sheets load on-demand when selected - - Preserved webview context maintains your state even when hidden -- **Responsive Interface** - - Immediate loading spinner provides visual feedback - - Sheet-switching indicators keep you informed - - Persistent state across view changes +- Caches workbooks and sheets to avoid re-parsing files +- Loads sheets on-demand when switching between them +- Automatically reloads when files are edited externally +- Handles mega large files with pagination +- Toggle viewer on/off for specific file extensions via command palette or context menu ## Supported File Formats @@ -65,11 +58,41 @@ The extension uses sophisticated optimization techniques to ensure smooth perfor | *.wb3 | | *.qpw | | *.xlr | -| *.eth | +| *.eth | + +## Usage + +### Disabling/Enabling the Viewer + +You can easily disable the SheetJS viewer for specific file extensions: + +SheetJS VSCode Extension Preview +SheetJS VSCode Extension Preview + +**Command Palette** (Ctrl/Cmd+Shift+P): +- `SheetJS: Disable Viewer for Current File Extension` - Switches to default text editor for that extension +- `SheetJS: Enable Viewer for Current File Extension` - Re-enables the viewer + +**Context Menu**: Right-click any spreadsheet file in the Explorer to access the same commands. + +**Built-in VSCode**: Right-click any file and select "Open With..." to choose between SheetJS Viewer and other editors. ## Getting Started Want to integrate SheetJS in your own VSCode extension? Check out our [detailed tutorial](https://docs.sheetjs.com/docs/) to learn how to implement these capabilities in your projects. +## Development + +To run the extension in development mode, install dependencies with `pnpm install` and press F5 in VSCode. This opens a new Extension Development Host window where you can test the extension by opening any spreadsheet file. + + +Build for production with `pnpm run package`. + +## Publishing +```bash +npx vsce login foo +npx vsce publish +``` + ## Learn More For more information on using this extension and integrating SheetJS capabilities in your own projects, visit our [documentation](https://docs.sheetjs.com/docs/). diff --git a/asset/toggle_context_menu.png b/asset/toggle_context_menu.png new file mode 100644 index 0000000..df47e0e Binary files /dev/null and b/asset/toggle_context_menu.png differ diff --git a/asset/toggle_ext_via_palette.png b/asset/toggle_ext_via_palette.png new file mode 100644 index 0000000..95dacad Binary files /dev/null and b/asset/toggle_ext_via_palette.png differ diff --git a/package.json b/package.json index e05c4af..df84984 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "author": "Asadbek Karimov ", "publisher": "asadbek", "icon": "img/logo.png", - "version": "0.0.8", + "version": "0.1.0", "license": "Apache-2.0", "bugs": { "url": "https://git.sheetjs.com/asadbek064/sheetjs-vscode-extension/issues" @@ -30,6 +30,47 @@ "activationEvents": [], "main": "./dist/extension.js", "contributes": { + "commands": [ + { + "command": "sheetjs.disableForExtension", + "title": "SheetJS: Disable Viewer for Current File Extension" + }, + { + "command": "sheetjs.enableForExtension", + "title": "SheetJS: Enable Viewer for Current File Extension" + }, + { + "command": "sheetjs.openWithViewer", + "title": "SheetJS: Open with Spreadsheet Viewer" + } + ], + "menus": { + "commandPalette": [ + { + "command": "sheetjs.disableForExtension", + "when": "editorIsOpen" + }, + { + "command": "sheetjs.enableForExtension", + "when": "editorIsOpen" + }, + { + "command": "sheetjs.openWithViewer" + } + ], + "explorer/context": [ + { + "command": "sheetjs.disableForExtension", + "group": "2_workspace", + "when": "resourceExtname =~ /\\.(xlsx|xls|csv|ods|xlsm|xlsb|numbers)$/" + }, + { + "command": "sheetjs.enableForExtension", + "group": "2_workspace", + "when": "resourceExtname =~ /\\.(xlsx|xls|csv|ods|xlsm|xlsb|numbers)$/" + } + ] + }, "customEditors": [ { "viewType": "excelViewer.spreadsheet", diff --git a/src/excelEditorProvider.ts b/src/excelEditorProvider.ts index e94ba04..2c81a6d 100644 --- a/src/excelEditorProvider.ts +++ b/src/excelEditorProvider.ts @@ -7,7 +7,9 @@ import { WorkbookCache } from './cacheManagement/workbookCache'; export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider { private cache: WorkbookCache; - + private fileWatchers: Map = new Map(); + private webviewPanels: Map = new Map(); + public static register(context: vscode.ExtensionContext): vscode.Disposable { return vscode.window.registerCustomEditorProvider( 'excelViewer.spreadsheet', @@ -30,6 +32,45 @@ export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider< webviewPanel.webview.options = { enableScripts: true }; + // store the webview panel for this document + const uriKey = document.uri.toString(); + this.webviewPanels.set(uriKey, webviewPanel); + + // setup file watcher for changes to this specific file + const watcher = vscode.workspace.createFileSystemWatcher( + document.uri.fsPath + ); + + watcher.onDidChange(async () => { + console.log(`File changed: ${document.uri.fsPath}, reloading...`); + + // clear the cache for this file + this.cache.clearCachesForUri(document.uri.toString()); + + // reload the file + this.setLoadingView(webviewPanel); + await new Promise(resolve => setTimeout(resolve, 50)); + + try { + await this.processExcelFile(document, webviewPanel); + } catch (error) { + console.error('Error reloading file:', error); + this.setErrorView(webviewPanel, error); + } + }); + + this.fileWatchers.set(uriKey, watcher); + + // clean up when the panel is disposed + webviewPanel.onDidDispose(() => { + this.webviewPanels.delete(uriKey); + const watcher = this.fileWatchers.get(uriKey); + if (watcher) { + watcher.dispose(); + this.fileWatchers.delete(uriKey); + } + }); + this.setLoadingView(webviewPanel); // small timeout to ensure the loading view is rendered before heavy processing begins diff --git a/src/extension.ts b/src/extension.ts index de3261e..2e0deeb 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,10 +3,124 @@ import { ExcelEditorProvider } from './excelEditorProvider'; export function activate(context: vscode.ExtensionContext) { console.log('SheetJS Spreadsheet Viewer extension activating...'); - + const provider = ExcelEditorProvider.register(context); context.subscriptions.push(provider); - + + // Command to disable viewer for current file extension + const disableCommand = vscode.commands.registerCommand('sheetjs.disableForExtension', async (uri?: vscode.Uri) => { + // Try to get URI from parameter, active text editor, or prompt user + if (!uri) { + const activeTab = vscode.window.tabGroups.activeTabGroup.activeTab; + if (activeTab?.label) { + // Try to find the file by label in workspace + const files = await vscode.workspace.findFiles(`**/${activeTab.label}`); + if (files.length > 0) { + uri = files[0]; + } + } + } + + if (!uri) { + vscode.window.showWarningMessage('No file is currently open or could not determine file path'); + return; + } + + const ext = uri.fsPath.split('.').pop()?.toLowerCase(); + if (!ext) { + vscode.window.showWarningMessage('Could not determine file extension'); + return; + } + + // Update workbench.editorAssociations to use default editor + const workbenchConfig = vscode.workspace.getConfiguration('workbench'); + const associations = workbenchConfig.get>('editorAssociations') || {}; + + if (associations[`*.${ext}`] === 'default') { + vscode.window.showInformationMessage(`SheetJS viewer is already disabled for .${ext} files`); + return; + } + + associations[`*.${ext}`] = 'default'; + await workbenchConfig.update('editorAssociations', associations, vscode.ConfigurationTarget.Global); + + // Close the current tab and reopen with default editor + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await vscode.commands.executeCommand('vscode.open', uri); + + vscode.window.showInformationMessage(`SheetJS viewer disabled for .${ext} files`); + }); + + // Command to enable viewer for current file extension + const enableCommand = vscode.commands.registerCommand('sheetjs.enableForExtension', async (uri?: vscode.Uri) => { + // Try to get URI from parameter or active tab + if (!uri) { + uri = vscode.window.activeTextEditor?.document.uri; + if (!uri) { + const activeTab = vscode.window.tabGroups.activeTabGroup.activeTab; + if (activeTab?.label) { + const files = await vscode.workspace.findFiles(`**/${activeTab.label}`); + if (files.length > 0) { + uri = files[0]; + } + } + } + } + + if (!uri) { + vscode.window.showWarningMessage('No file is currently open or could not determine file path'); + return; + } + + const ext = uri.fsPath.split('.').pop()?.toLowerCase(); + if (!ext) { + vscode.window.showWarningMessage('Could not determine file extension'); + return; + } + + // Remove from workbench.editorAssociations + const workbenchConfig = vscode.workspace.getConfiguration('workbench'); + const associations = workbenchConfig.get>('editorAssociations') || {}; + + // If not set to 'default', it's already enabled + if (associations[`*.${ext}`] !== 'default') { + vscode.window.showInformationMessage(`SheetJS viewer is already enabled for .${ext} files`); + return; + } + + // Create a new object without the extension (to avoid proxy issues) + const newAssociations: Record = {}; + for (const key in associations) { + if (key !== `*.${ext}`) { + newAssociations[key] = associations[key]; + } + } + await workbenchConfig.update('editorAssociations', newAssociations, vscode.ConfigurationTarget.Global); + + // Close the current tab and reopen with SheetJS viewer + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await vscode.commands.executeCommand('vscode.openWith', uri, 'excelViewer.spreadsheet'); + + vscode.window.showInformationMessage(`SheetJS viewer enabled for .${ext} files`); + }); + + // Command to open with viewer + const openCommand = vscode.commands.registerCommand('sheetjs.openWithViewer', async () => { + const uris = await vscode.window.showOpenDialog({ + canSelectMany: false, + openLabel: 'Open with SheetJS Viewer', + filters: { + 'Spreadsheets': ['xlsx', 'xls', 'csv', 'ods', 'xlsm', 'xlsb', 'numbers'] + } + }); + + if (uris && uris[0]) { + await vscode.commands.executeCommand('vscode.openWith', uris[0], 'excelViewer.spreadsheet'); + } + }); + + context.subscriptions.push(disableCommand, enableCommand, openCommand); + console.log('SheetJS Spreadsheet Viewer extension is now active'); vscode.window.showInformationMessage('SheetJS Spreadsheet Viewer is ready!'); }