From 2baf640c0944c7fe74235b3376d7b3f0e5e0d893 Mon Sep 17 00:00:00 2001 From: "hugues.verlin" Date: Sun, 10 Nov 2024 20:25:34 -0500 Subject: [PATCH] init --- .github/workflows/ci.yml | 73 ++++++++ .gitignore | 6 + .vscodeignore | 12 ++ biome.json | 33 ++++ mise.toml | 19 ++ package.json | 91 +++++++++ pnpm-lock.yaml | 324 +++++++++++++++++++++++++++++++++ resources/icon.png | Bin 0 -> 3659 bytes resources/icon.svg | 6 + rsbuild.config.ts | 19 ++ src/extension.ts | 60 ++++++ src/miseService.ts | 66 +++++++ src/providers/envProvider.ts | 33 ++++ src/providers/tasksProvider.ts | 33 ++++ src/providers/toolsProvider.ts | 38 ++++ src/types.ts | 19 ++ src/utils/logger.ts | 110 +++++++++++ tsconfig.json | 12 ++ 18 files changed, 954 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .vscodeignore create mode 100644 biome.json create mode 100644 mise.toml create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 resources/icon.png create mode 100644 resources/icon.svg create mode 100644 rsbuild.config.ts create mode 100644 src/extension.ts create mode 100644 src/miseService.ts create mode 100644 src/providers/envProvider.ts create mode 100644 src/providers/tasksProvider.ts create mode 100644 src/providers/toolsProvider.ts create mode 100644 src/types.ts create mode 100644 src/utils/logger.ts create mode 100644 tsconfig.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ecbe872 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,73 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [22.x] + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - run: corepack enable && pnpm i --frozen-lockfile + - run: pnpm lint + - run: pnpm ts-check + - run: pnpm compile + + publish-vscode-openvsx: + needs: build + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: '22.x' + + - run: corepack enable && pnpm i --frozen-lockfile + - run: npm run package + + - name: Publish to Open VSX Registry + uses: HaaLeo/publish-vscode-extension@v1 + with: + pat: ${{ secrets.OPEN_VSX_TOKEN }} + dependencies: false + + publish-vscode-marketplace: + needs: build + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: '22.x' + + - run: corepack enable && pnpm i --frozen-lockfile + - run: npm run package + + - name: Publish to Visual Studio Marketplace + uses: HaaLeo/publish-vscode-extension@v1 + with: + pat: ${{ secrets.VS_MARKETPLACE_TOKEN }} + registryUrl: https://marketplace.visualstudio.com + dependencies: false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..42ab88b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea +.vscode/** +.vscode-test/** +node_modules +mise.local.toml +dist/ \ No newline at end of file diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..95f6a3e --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,12 @@ +.vscode/** +.vscode-test/** +src/** +node_modules/** +.gitignore +.yarnrc +webpack.config.js +vsc-extension-quickstart.md +**/tsconfig.json +**/.eslintrc.json +**/*.map +**/*.ts \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..14b8b3a --- /dev/null +++ b/biome.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "ignore": ["node_modules", "dist", ".vscode"] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noConfusingVoidType": "off" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..162e1b0 --- /dev/null +++ b/mise.toml @@ -0,0 +1,19 @@ +[tools] +node = '22' + +[tasks.dev] +run = 'pnpm dev' + +[tasks.ts-check] +run = 'pnpm ts-check' +sources = ['src/**/*.ts'] + +[tasks.biome-lint] +run = 'pnpm lint' +sources = ['src/**/*.ts'] + +[tasks.lint] +depends = ['ts-check', 'biome-lint'] + +[tasks.lint-fix] +run = 'pnpm lint-fix' \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..d2de143 --- /dev/null +++ b/package.json @@ -0,0 +1,91 @@ +{ + "name": "mise-vscode", + "displayName": "Mise VSCode", + "publisher": "hverlin", + "description": "VSCode extension for mise (manged dev tools, tasks and environment variables)", + "version": "0.0.2", + "repository": { + "type": "git", + "url": "https://github.com/hverlin/mise-vscode" + }, + "bugs": { + "url": "https://github.com/hverlin/mise-vscode/issues" + }, + "license": "MIT", + "engines": { + "vscode": "^1.85.0" + }, + "categories": ["Other"], + "keywords": [ + "mise", + "mise-en-place", + "rtx", + "devtools", + "tasks", + "envs", + "environment variables", + "scripts" + ], + "icon": "resources/icon.png", + "main": "./dist/extension.js", + "contributes": { + "viewsContainers": { + "activitybar": [ + { + "id": "mise-panel", + "title": "Mise panel", + "icon": "$(terminal)" + } + ] + }, + "views": { + "mise-panel": [ + { + "id": "miseTasksView", + "name": "Mise Tasks" + }, + { + "id": "miseToolsView", + "name": "Mise Tools" + }, + { + "id": "miseEnvsView", + "name": "Environment Variables" + } + ] + }, + "commands": [ + { + "command": "mise.refreshEntry", + "title": "Refresh", + "icon": "$(refresh)" + } + ], + "menus": { + "view/title": [ + { + "command": "mise.refreshEntry", + "when": "view == miseTasksView || view == miseToolsView || view == miseEnvsView", + "group": "navigation" + } + ] + } + }, + "scripts": { + "vscode:prepublish": "npm run package --no-optional", + "compile": "rsbuild build", + "dev": "rsbuild dev", + "ts-check": "tsc --noEmit", + "lint-fix": "biome check --fix", + "lint": "biome ci", + "package": "rsbuild build" + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@rsbuild/core": "^1.1.0", + "@types/node": "^22.9.0", + "@types/vscode": "^1.85.0", + "typescript": "^5.6.3" + }, + "packageManager": "pnpm@9.12.2+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..f60fffb --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,324 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@biomejs/biome': + specifier: 1.9.4 + version: 1.9.4 + '@rsbuild/core': + specifier: ^1.1.0 + version: 1.1.0 + '@types/node': + specifier: ^22.9.0 + version: 22.9.0 + '@types/vscode': + specifier: ^1.85.0 + version: 1.95.0 + typescript: + specifier: ^5.6.3 + version: 5.6.3 + +packages: + + '@biomejs/biome@1.9.4': + resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@1.9.4': + resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@1.9.4': + resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@1.9.4': + resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@1.9.4': + resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@1.9.4': + resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@1.9.4': + resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@1.9.4': + resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@1.9.4': + resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@module-federation/runtime-tools@0.5.1': + resolution: {integrity: sha512-nfBedkoZ3/SWyO0hnmaxuz0R0iGPSikHZOAZ0N/dVSQaIzlffUo35B5nlC2wgWIc0JdMZfkwkjZRrnuuDIJbzg==} + + '@module-federation/runtime@0.5.1': + resolution: {integrity: sha512-xgiMUWwGLWDrvZc9JibuEbXIbhXg6z2oUkemogSvQ4LKvrl/n0kbqP1Blk669mXzyWbqtSp6PpvNdwaE1aN5xQ==} + + '@module-federation/sdk@0.5.1': + resolution: {integrity: sha512-exvchtjNURJJkpqjQ3/opdbfeT2wPKvrbnGnyRkrwW5o3FH1LaST1tkiNviT6OXTexGaVc2DahbdniQHVtQ7pA==} + + '@module-federation/webpack-bundler-runtime@0.5.1': + resolution: {integrity: sha512-mMhRFH0k2VjwHt3Jol9JkUsmI/4XlrAoBG3E0o7HoyoPYv1UFOWyqAflfANcUPgbYpvqmyLzDcO+3IT36LXnrA==} + + '@rsbuild/core@1.1.0': + resolution: {integrity: sha512-SyQlJjWgR1VwLt4nuiY0g6L9INv2koH232TeDZuopvNgbRytskD3kJ8bbGWBBXsQjZjtqBEh5ishqf8CIfF8dQ==} + engines: {node: '>=16.7.0'} + hasBin: true + + '@rspack/binding-darwin-arm64@1.1.0': + resolution: {integrity: sha512-02YmzmtKMNHCSMzVT5sgbJuPDn+HunkrtWq0D95Fh9sGKYap9cs0JOpzTfyAL3KXJ9JzVfOAZA3VgVQOBaQNWQ==} + cpu: [arm64] + os: [darwin] + + '@rspack/binding-darwin-x64@1.1.0': + resolution: {integrity: sha512-HtBh8p6hml7BWNtZaqWFtGbOFP/tvFDn1uPWmA3R32WTILUXNRWXIsLDY95U3Z2U1Gt3SL58SOpJjXlFIb6wZg==} + cpu: [x64] + os: [darwin] + + '@rspack/binding-linux-arm64-gnu@1.1.0': + resolution: {integrity: sha512-Q/i50Pieii3akdv5Q6my6QelV5Dpc8O/Ir4udpjYl0pbSdKamdI8M85fxrMxGAGcoNSD+X52fDvxJujXWMcP0w==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-arm64-musl@1.1.0': + resolution: {integrity: sha512-H7Eu3xC7LWPpxrI47n8X361eEGGpQOjZIWTz8tLdn4oNS2D9kqsBYES7LsuuLTTH4ueHTDuEtDdfZpBsE+qesw==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-x64-gnu@1.1.0': + resolution: {integrity: sha512-dIZSutPo2z/OaO2f6SVlcYA6lGBH+4TrRtWmMyPshpTNPrkCGGfDhC43fZ4jCiUj2PO/Hcn8jyKhci4leBsVBA==} + cpu: [x64] + os: [linux] + + '@rspack/binding-linux-x64-musl@1.1.0': + resolution: {integrity: sha512-f6L2JWgbG9PKWnVw2YNZdntjzia1V2w2Xq458HkCQUDwhnEipWXaZ2zhfD9jcb4UYoMP8/2uD3B96sSFFNTdrQ==} + cpu: [x64] + os: [linux] + + '@rspack/binding-win32-arm64-msvc@1.1.0': + resolution: {integrity: sha512-opo6XR4iXh/QkHiauVQBlU2xR2JyjDmSwgkION27oszu81nr+IajTSXQX96x5I6Bq48GQLU4rItHse/doctQDA==} + cpu: [arm64] + os: [win32] + + '@rspack/binding-win32-ia32-msvc@1.1.0': + resolution: {integrity: sha512-FBcG+OPJokSE3nPi1+ZamLK2V4IWdNC+GMr0z7LUrBiKc5lO70y5VkldfyPV1Z+doSuroVINlhK+lRHdQgGwYg==} + cpu: [ia32] + os: [win32] + + '@rspack/binding-win32-x64-msvc@1.1.0': + resolution: {integrity: sha512-H/6Glp1nZvxWAD5+2hRrp1kBs9f+pLb/un2TdFSUNd2tyXq5GyHCe70+N9psbe/jjGxD8e1vPNQtN/VvkuR0Zg==} + cpu: [x64] + os: [win32] + + '@rspack/binding@1.1.0': + resolution: {integrity: sha512-zLduWacrw/bBYiFvhjN70f+AJxXnTzevywXp54vso8d0Nz7z4KIycdz/Ua5AGRUkG2ZuQw6waypN5pXf48EBcA==} + + '@rspack/core@1.1.0': + resolution: {integrity: sha512-+IYWSe9D3wB97VVBfaojuWLv3wGIBe9pfJkxNObkorN60Nj3UHYzBLuACrHn4hW2mZjAWrv06ReHXJUEGzQqaQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@swc/helpers': '>=0.5.1' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@rspack/lite-tapable@1.0.1': + resolution: {integrity: sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==} + engines: {node: '>=16.0.0'} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@types/node@22.9.0': + resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} + + '@types/vscode@1.95.0': + resolution: {integrity: sha512-0LBD8TEiNbet3NvWsmn59zLzOFu/txSlGxnv5yAFHCrhG9WvAnR3IvfHzMOs2aeWqgvNjq9pO99IUw8d3n+unw==} + + caniuse-lite@1.0.30001680: + resolution: {integrity: sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==} + + core-js@3.39.0: + resolution: {integrity: sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + +snapshots: + + '@biomejs/biome@1.9.4': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 1.9.4 + '@biomejs/cli-darwin-x64': 1.9.4 + '@biomejs/cli-linux-arm64': 1.9.4 + '@biomejs/cli-linux-arm64-musl': 1.9.4 + '@biomejs/cli-linux-x64': 1.9.4 + '@biomejs/cli-linux-x64-musl': 1.9.4 + '@biomejs/cli-win32-arm64': 1.9.4 + '@biomejs/cli-win32-x64': 1.9.4 + + '@biomejs/cli-darwin-arm64@1.9.4': + optional: true + + '@biomejs/cli-darwin-x64@1.9.4': + optional: true + + '@biomejs/cli-linux-arm64-musl@1.9.4': + optional: true + + '@biomejs/cli-linux-arm64@1.9.4': + optional: true + + '@biomejs/cli-linux-x64-musl@1.9.4': + optional: true + + '@biomejs/cli-linux-x64@1.9.4': + optional: true + + '@biomejs/cli-win32-arm64@1.9.4': + optional: true + + '@biomejs/cli-win32-x64@1.9.4': + optional: true + + '@module-federation/runtime-tools@0.5.1': + dependencies: + '@module-federation/runtime': 0.5.1 + '@module-federation/webpack-bundler-runtime': 0.5.1 + + '@module-federation/runtime@0.5.1': + dependencies: + '@module-federation/sdk': 0.5.1 + + '@module-federation/sdk@0.5.1': {} + + '@module-federation/webpack-bundler-runtime@0.5.1': + dependencies: + '@module-federation/runtime': 0.5.1 + '@module-federation/sdk': 0.5.1 + + '@rsbuild/core@1.1.0': + dependencies: + '@rspack/core': 1.1.0(@swc/helpers@0.5.15) + '@rspack/lite-tapable': 1.0.1 + '@swc/helpers': 0.5.15 + core-js: 3.39.0 + optionalDependencies: + fsevents: 2.3.3 + + '@rspack/binding-darwin-arm64@1.1.0': + optional: true + + '@rspack/binding-darwin-x64@1.1.0': + optional: true + + '@rspack/binding-linux-arm64-gnu@1.1.0': + optional: true + + '@rspack/binding-linux-arm64-musl@1.1.0': + optional: true + + '@rspack/binding-linux-x64-gnu@1.1.0': + optional: true + + '@rspack/binding-linux-x64-musl@1.1.0': + optional: true + + '@rspack/binding-win32-arm64-msvc@1.1.0': + optional: true + + '@rspack/binding-win32-ia32-msvc@1.1.0': + optional: true + + '@rspack/binding-win32-x64-msvc@1.1.0': + optional: true + + '@rspack/binding@1.1.0': + optionalDependencies: + '@rspack/binding-darwin-arm64': 1.1.0 + '@rspack/binding-darwin-x64': 1.1.0 + '@rspack/binding-linux-arm64-gnu': 1.1.0 + '@rspack/binding-linux-arm64-musl': 1.1.0 + '@rspack/binding-linux-x64-gnu': 1.1.0 + '@rspack/binding-linux-x64-musl': 1.1.0 + '@rspack/binding-win32-arm64-msvc': 1.1.0 + '@rspack/binding-win32-ia32-msvc': 1.1.0 + '@rspack/binding-win32-x64-msvc': 1.1.0 + + '@rspack/core@1.1.0(@swc/helpers@0.5.15)': + dependencies: + '@module-federation/runtime-tools': 0.5.1 + '@rspack/binding': 1.1.0 + '@rspack/lite-tapable': 1.0.1 + caniuse-lite: 1.0.30001680 + optionalDependencies: + '@swc/helpers': 0.5.15 + + '@rspack/lite-tapable@1.0.1': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@types/node@22.9.0': + dependencies: + undici-types: 6.19.8 + + '@types/vscode@1.95.0': {} + + caniuse-lite@1.0.30001680: {} + + core-js@3.39.0: {} + + fsevents@2.3.3: + optional: true + + tslib@2.8.1: {} + + typescript@5.6.3: {} + + undici-types@6.19.8: {} diff --git a/resources/icon.png b/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a37c550e5ac0bd1ef1daeab41052ddc2eca7c8cb GIT binary patch literal 3659 zcmeI!e@v8R902g|^WJ;hb;+-eA0g1)LW1T38VjiqP%s3Hn4w-Y;MG5bfI!sn0Og)2 zWNZesM09gOIakRP8u4QSds)oUf|xYcUNPu3a}1FoQw|F5-k$GPY)$`Ay0(_xANPIs zeBST(`99CR+db8(DGAQHwK@QBPTU&59e`&;9<=Nm(u)sfvB2#~PKXDks&+^OaN!f< zH>H`!X2!bDo!$XQudORY&xv+Y|f296W^m!?R?rd zt~6O6e`c<(KYHkzvipb9#!2qzsrfs-ZF}27v%2}Tj3V9$lr6;;cGSRel~y|Xzog?% zz;TuS_<1;48w)^y2RCrkz;TsUI{Fuuq(PaMtNO}Z$XvQPxUg}%^y5ggeny$zP!qj4 zm+k6VLCihMKw|=FdXkC@El{_nFJ@^MXq{xywjO@rZjahMVGl9yRdnzLM0Hx!G!-%> zk&Ar>KD(7gj8D+lF-_a&gK4}x#L~7I=mBdBgq6VXxXAU5k}iRz>oxM%h`!Vr@hylL zV|biM`w*Rk>xka;Im9=h12K(A`w`W`G@_pRBPK#Iq6d+R5P4ye;Ti+oh-d~Y;$t zJ~&hV@xXjp(6Bf8W`Y#pt5>dGRlE1;EI*YtOr%dg-@EW#;c&wZ_U}DdGWk(^=C32! z;!Byl+`3?ytZ%m}Q-^B2Nvp*TmNX9*cvwn+R?9sh`>BB%!FzV1q~z?ba`W_s>zAD6 zR#6d6c}i`;)fi5;yK;hJ1na{EEm&(oQ2q?m#!3kao0CLa09XqZ?SoFe2T}}s_-}@X zTXUsZ2ds>ZA6E&=WoK%O+rZ9ooJ}134^H%dUo#{2DvkMaUC)%$$~#l%dY^sw=epPp z@76?TpoNvOJjdlhQe3Ad!oQ7UljOI`;C#ExYfl=cJfp?(_wnpoAsC ziBM|mk0hpx>?{M+!WBeM8u&QFbU3k|p_!P@Avy`C5PfJMX6AwwGrNe?iKr1oM1Oh& zu@s6C7m3u)Fq{`U84kJA48&yUK#U?%2clZ&M)ag_Bfbd{h=+;v6{3^ShUi0IMa+Yc z5QaUZ>vKenyo~5i?;w`JFybJ>;~TZjvUP_I7wCwe-`_LZvZYg4yc za@6t^OCRNKTgWX#DOtpQT!N@)+4JFG@$9qj_8ka6a#CZ+uuTM1=0M1}I9t8>ZqPj! w|92W%UR3Q#(cg%7yMxD=9}=I75a<{D + + + + \ No newline at end of file diff --git a/rsbuild.config.ts b/rsbuild.config.ts new file mode 100644 index 0000000..b70fda2 --- /dev/null +++ b/rsbuild.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from "@rsbuild/core"; + +export default defineConfig({ + dev: { + writeToDisk: true, + hmr: false, + }, + server: {}, + source: { + entry: { extension: "./src/extension.ts" }, + }, + output: { + sourceMap: { js: "source-map" }, + target: "node", + externals: { + vscode: "commonjs vscode", + }, + }, +}); diff --git a/src/extension.ts b/src/extension.ts new file mode 100644 index 0000000..846045b --- /dev/null +++ b/src/extension.ts @@ -0,0 +1,60 @@ +import * as vscode from "vscode"; +import { MiseService } from "./miseService"; +import { MiseEnvsProvider } from "./providers/envProvider"; +import { MiseTasksProvider } from "./providers/tasksProvider"; +import { MiseToolsProvider } from "./providers/toolsProvider"; + +let statusBarItem: vscode.StatusBarItem; + +export function activate(context: vscode.ExtensionContext) { + const miseService = new MiseService(); + + const tasksProvider = new MiseTasksProvider(miseService); + const toolsProvider = new MiseToolsProvider(miseService); + const envsProvider = new MiseEnvsProvider(miseService); + + statusBarItem = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Right, + 100, + ); + statusBarItem.show(); + statusBarItem.text = "$(tools) Mise"; + statusBarItem.tooltip = "Click to refresh Mise"; + + vscode.window.registerTreeDataProvider("miseTasksView", tasksProvider); + vscode.window.registerTreeDataProvider("miseToolsView", toolsProvider); + vscode.window.registerTreeDataProvider("miseEnvsView", envsProvider); + + statusBarItem.command = "mise.refreshEntry"; + statusBarItem.show(); + + context.subscriptions.push(statusBarItem); + + context.subscriptions.push( + vscode.commands.registerCommand("mise.refreshEntry", async () => { + await vscode.commands.executeCommand( + "workbench.view.extension.mise-panel", + ); + + statusBarItem.text = "$(sync~spin) Mise"; + try { + statusBarItem.text = "$(check) Mise"; + tasksProvider.refresh(); + toolsProvider.refresh(); + envsProvider.refresh(); + statusBarItem.text = "$(tools) Mise"; + } catch (error) { + statusBarItem.text = "$(error) Mise"; + vscode.window.showErrorMessage( + `Failed to refresh Mise views: ${error}`, + ); + } + }), + ); +} + +export function deactivate() { + if (statusBarItem) { + statusBarItem.dispose(); + } +} diff --git a/src/miseService.ts b/src/miseService.ts new file mode 100644 index 0000000..84892af --- /dev/null +++ b/src/miseService.ts @@ -0,0 +1,66 @@ +import { exec } from "node:child_process"; +import { promisify } from "node:util"; +import * as vscode from "vscode"; +import { logger } from "./utils/logger"; + +const execAsync = promisify(exec); + +export class MiseService { + async getTasks(): Promise { + try { + const { stdout } = await execAsync("mise tasks ls --json", { + cwd: vscode.workspace.rootPath, + }); + return JSON.parse(stdout).map((task: MiseTask) => ({ + name: task.name, + source: task.source, + description: task.description, + })); + } catch (error: unknown) { + logger.error("Error fetching mise tasks:", error as Error); + return []; + } + } + + async getTools(): Promise> { + logger.info("Executing mise ls 4 command"); + + try { + const { stdout } = await execAsync("mise ls --current --offline --json", { + cwd: vscode.workspace.rootPath, + }); + logger.info(`Got stdout from mise ls 4 command ${stdout}`); + return Object.entries(JSON.parse(stdout)).flatMap(([toolName, tools]) => { + return (tools as MiseTool[]).map((tool) => { + return { + name: toolName, + version: tool.version, + requested_version: tool.requested_version, + active: tool.active, + installed: tool.installed, + install_path: tool.install_path, + } satisfies MiseTool; + }); + }); + } catch (error) { + logger.error("Error fetching mise tools:", error as Error); + return []; + } + } + + async getEnvs(): Promise { + try { + const { stdout } = await execAsync("mise env --json", { + cwd: vscode.workspace.rootPath, + }); + + return Object.entries(JSON.parse(stdout)).map(([key, value]) => ({ + name: key, + value: value as string, + })); + } catch (error) { + logger.error("Error fetching mise environments:", error as Error); + return []; + } + } +} diff --git a/src/providers/envProvider.ts b/src/providers/envProvider.ts new file mode 100644 index 0000000..6355538 --- /dev/null +++ b/src/providers/envProvider.ts @@ -0,0 +1,33 @@ +import * as vscode from "vscode"; +import type { MiseService } from "../miseService"; + +export class MiseEnvsProvider implements vscode.TreeDataProvider { + private _onDidChangeTreeData: vscode.EventEmitter< + void | EnvItem | null | undefined + > = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event< + EnvItem | undefined | null | void + > = this._onDidChangeTreeData.event; + + constructor(private miseService: MiseService) {} + + refresh(): void { + this._onDidChangeTreeData.fire(); + } + + getTreeItem(element: EnvItem): vscode.TreeItem { + return element; + } + + async getChildren(): Promise { + const envs = await this.miseService.getEnvs(); + return envs.map((env) => new EnvItem(env)); + } +} + +class EnvItem extends vscode.TreeItem { + constructor(env: MiseEnv) { + const label = env.value ? `${env.name}=${env.value}` : env.name; + super(label, vscode.TreeItemCollapsibleState.None); + } +} diff --git a/src/providers/tasksProvider.ts b/src/providers/tasksProvider.ts new file mode 100644 index 0000000..f151a9f --- /dev/null +++ b/src/providers/tasksProvider.ts @@ -0,0 +1,33 @@ +import * as vscode from "vscode"; +import type { MiseService } from "../miseService"; + +export class MiseTasksProvider implements vscode.TreeDataProvider { + private _onDidChangeTreeData: vscode.EventEmitter< + TaskItem | undefined | null | void + > = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event< + TaskItem | undefined | null | void + > = this._onDidChangeTreeData.event; + + constructor(private miseService: MiseService) {} + + refresh(): void { + this._onDidChangeTreeData.fire(); + } + + getTreeItem(element: TaskItem): vscode.TreeItem { + return element; + } + + async getChildren(): Promise { + const tasks = await this.miseService.getTasks(); + return tasks.map((task) => new TaskItem(task)); + } +} + +class TaskItem extends vscode.TreeItem { + constructor(task: MiseTask) { + super(task.name, vscode.TreeItemCollapsibleState.None); + this.tooltip = `Task: ${task.name}\nSource: ${task.source}\nDescription: ${task.description}`; + } +} diff --git a/src/providers/toolsProvider.ts b/src/providers/toolsProvider.ts new file mode 100644 index 0000000..041c959 --- /dev/null +++ b/src/providers/toolsProvider.ts @@ -0,0 +1,38 @@ +import * as vscode from "vscode"; +import type { MiseService } from "../miseService"; + +export class MiseToolsProvider implements vscode.TreeDataProvider { + private _onDidChangeTreeData: vscode.EventEmitter< + ToolItem | undefined | null | void + > = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event< + ToolItem | undefined | null | void + > = this._onDidChangeTreeData.event; + + constructor(private miseService: MiseService) {} + + refresh(): void { + this._onDidChangeTreeData.fire(); + } + + getTreeItem(element: ToolItem): vscode.TreeItem { + return element; + } + + async getChildren(): Promise { + const tools = await this.miseService.getTools(); + return tools.map((tool) => new ToolItem(tool)); + } +} + +class ToolItem extends vscode.TreeItem { + constructor(tool: MiseTool) { + super(`${tool.name} ${tool.version}`, vscode.TreeItemCollapsibleState.None); + this.tooltip = `Tool: ${tool.name} +Version: ${tool.version} +Requested Version: ${tool.requested_version} +Activated: ${tool.active} +Installed: ${tool.installed} +Install Path: ${tool.install_path}`; + } +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..025697d --- /dev/null +++ b/src/types.ts @@ -0,0 +1,19 @@ +type MiseTask = { + name: string; + source: string; + description: string; +}; + +type MiseTool = { + name: string; + version: string; + requested_version: string; + installed: boolean; + active: boolean; + install_path: string; +}; + +type MiseEnv = { + name: string; + value: string; +}; diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 0000000..1a5e5b7 --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,110 @@ +import * as vscode from "vscode"; + +export enum LogLevel { + DEBUG = 0, + INFO = 1, + WARN = 2, + ERROR = 3, +} + +export class Logger { + private static instance: Logger; + private outputChannel: vscode.OutputChannel; + private logLevel: LogLevel; + + private constructor() { + this.outputChannel = vscode.window.createOutputChannel("Mise"); + this.logLevel = LogLevel.INFO; // Default log level + } + + public static getInstance(): Logger { + if (!Logger.instance) { + Logger.instance = new Logger(); + } + return Logger.instance; + } + + public setLogLevel(level: LogLevel) { + this.logLevel = level; + this.info(`Log level set to ${LogLevel[level]}`); + } + + private formatMessage(level: string, message: string): string { + const timestamp = new Date().toISOString(); + return `[${timestamp}] [${level}] ${message}`; + } + + private shouldLog(level: LogLevel): boolean { + return level >= this.logLevel; + } + + private log(level: LogLevel, message: string, error?: Error) { + if (!this.shouldLog(level)) { + return; + } + + const formattedMessage = this.formatMessage(LogLevel[level], message); + this.outputChannel.appendLine(formattedMessage); + + if (error) { + this.outputChannel.appendLine( + this.formatMessage(LogLevel[level], `Error Details: ${error.message}`), + ); + if (error.stack) { + this.outputChannel.appendLine( + this.formatMessage(LogLevel[level], `Stack Trace: ${error.stack}`), + ); + } + } + + // Show in Problem panel for warnings and errors + if (level >= LogLevel.WARN) { + const severity = + level === LogLevel.WARN + ? vscode.DiagnosticSeverity.Warning + : vscode.DiagnosticSeverity.Error; + + vscode.window.showErrorMessage(`Mise: ${message}`); + } + } + + public debug(message: string) { + this.log(LogLevel.DEBUG, message); + } + + public info(message: string) { + this.log(LogLevel.INFO, message); + } + + public warn(message: string) { + this.log(LogLevel.WARN, message); + } + + public error(message: string, error?: Error) { + this.log(LogLevel.ERROR, message, error); + } + + public group(title: string) { + if (!this.shouldLog(LogLevel.DEBUG)) { + return; + } + this.outputChannel.appendLine(`\n▼ ${title}`); + } + + public groupEnd() { + if (!this.shouldLog(LogLevel.DEBUG)) { + return; + } + this.outputChannel.appendLine("▲ End\n"); + } + + public show() { + this.outputChannel.show(); + } + + public dispose() { + this.outputChannel.dispose(); + } +} + +export const logger = Logger.getInstance(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9c00fc0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "out", + "lib": ["ES2020"], + "sourceMap": true, + "rootDir": "src", + "strict": true + }, + "exclude": ["node_modules", ".vscode-test", "rsbuild.config.ts"] +}