handle vscode

This commit is contained in:
bobo.yang 2023-05-31 16:10:53 +08:00
parent cad2bbb69d
commit 562a125902
24 changed files with 461 additions and 127 deletions

248
package-lock.json generated
View File

@ -46,6 +46,7 @@
"@types/react-dom": "^18.2.3",
"@types/react-syntax-highlighter": "^15.5.6",
"@types/shell-escape": "^0.2.1",
"@types/sinon": "^10.0.15",
"@types/uuid": "^9.0.1",
"@types/vscode": "^1.77.0",
"@typescript-eslint/eslint-plugin": "^5.56.0",
@ -68,6 +69,7 @@
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"redux": "^4.2.1",
"sinon": "^15.1.0",
"style-loader": "^3.3.2",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2",
@ -3141,6 +3143,35 @@
"dev": true
},
"node_modules/@sinonjs/commons": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
"integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
"dev": true,
"dependencies": {
"type-detect": "4.0.8"
}
},
"node_modules/@sinonjs/fake-timers": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz",
"integrity": "sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==",
"dev": true,
"dependencies": {
"@sinonjs/commons": "^3.0.0"
}
},
"node_modules/@sinonjs/samsam": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz",
"integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==",
"dev": true,
"dependencies": {
"@sinonjs/commons": "^2.0.0",
"lodash.get": "^4.4.2",
"type-detect": "^4.0.8"
}
},
"node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
"integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
@ -3149,14 +3180,11 @@
"type-detect": "4.0.8"
}
},
"node_modules/@sinonjs/fake-timers": {
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
"integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
"dev": true,
"dependencies": {
"@sinonjs/commons": "^2.0.0"
}
"node_modules/@sinonjs/text-encoding": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz",
"integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==",
"dev": true
},
"node_modules/@tabler/icons": {
"version": "2.17.0",
@ -3973,6 +4001,21 @@
"integrity": "sha512-95hZXmBvwtvsLMPefKT9xquUSAJXsVDUaipyUiYoYi3ZdLhZ3w30w230Ugs96IdoJQb5ECvj0D82Jj/op00qWQ==",
"dev": true
},
"node_modules/@types/sinon": {
"version": "10.0.15",
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.15.tgz",
"integrity": "sha512-3lrFNQG0Kr2LDzvjyjB6AMJk4ge+8iYhQfdnSwIwlG88FUOV43kPcQqDZkDa/h3WSZy6i8Fr0BSjfQtB1B3xuQ==",
"dev": true,
"dependencies": {
"@types/sinonjs__fake-timers": "*"
}
},
"node_modules/@types/sinonjs__fake-timers": {
"version": "8.1.2",
"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz",
"integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==",
"dev": true
},
"node_modules/@types/sockjs": {
"version": "0.3.33",
"resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz",
@ -8893,6 +8936,12 @@
"setimmediate": "^1.0.5"
}
},
"node_modules/just-extend": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
"integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==",
"dev": true
},
"node_modules/kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@ -9026,6 +9075,12 @@
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"dev": true
},
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
"dev": true
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@ -9916,6 +9971,43 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
"node_modules/nise": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz",
"integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==",
"dev": true,
"dependencies": {
"@sinonjs/commons": "^2.0.0",
"@sinonjs/fake-timers": "^10.0.2",
"@sinonjs/text-encoding": "^0.7.1",
"just-extend": "^4.0.2",
"path-to-regexp": "^1.7.0"
}
},
"node_modules/nise/node_modules/@sinonjs/commons": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
"integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
"dev": true,
"dependencies": {
"type-detect": "4.0.8"
}
},
"node_modules/nise/node_modules/isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
"dev": true
},
"node_modules/nise/node_modules/path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"dev": true,
"dependencies": {
"isarray": "0.0.1"
}
},
"node_modules/no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@ -12186,6 +12278,24 @@
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true
},
"node_modules/sinon": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.1.0.tgz",
"integrity": "sha512-cS5FgpDdE9/zx7no8bxROHymSlPLZzq0ChbbLk1DrxBfc+eTeBK3y8nIL+nu/0QeYydhhbLIr7ecHJpywjQaoQ==",
"dev": true,
"dependencies": {
"@sinonjs/commons": "^3.0.0",
"@sinonjs/fake-timers": "^10.2.0",
"@sinonjs/samsam": "^8.0.0",
"diff": "^5.1.0",
"nise": "^5.1.4",
"supports-color": "^7.2.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/sinon"
}
},
"node_modules/sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@ -16010,23 +16120,51 @@
"dev": true
},
"@sinonjs/commons": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
"integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
"integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
"dev": true,
"requires": {
"type-detect": "4.0.8"
}
},
"@sinonjs/fake-timers": {
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
"integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz",
"integrity": "sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==",
"dev": true,
"requires": {
"@sinonjs/commons": "^2.0.0"
"@sinonjs/commons": "^3.0.0"
}
},
"@sinonjs/samsam": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz",
"integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==",
"dev": true,
"requires": {
"@sinonjs/commons": "^2.0.0",
"lodash.get": "^4.4.2",
"type-detect": "^4.0.8"
},
"dependencies": {
"@sinonjs/commons": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
"integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
"dev": true,
"requires": {
"type-detect": "4.0.8"
}
}
}
},
"@sinonjs/text-encoding": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz",
"integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==",
"dev": true
},
"@tabler/icons": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.17.0.tgz",
@ -16664,6 +16802,21 @@
"integrity": "sha512-95hZXmBvwtvsLMPefKT9xquUSAJXsVDUaipyUiYoYi3ZdLhZ3w30w230Ugs96IdoJQb5ECvj0D82Jj/op00qWQ==",
"dev": true
},
"@types/sinon": {
"version": "10.0.15",
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.15.tgz",
"integrity": "sha512-3lrFNQG0Kr2LDzvjyjB6AMJk4ge+8iYhQfdnSwIwlG88FUOV43kPcQqDZkDa/h3WSZy6i8Fr0BSjfQtB1B3xuQ==",
"dev": true,
"requires": {
"@types/sinonjs__fake-timers": "*"
}
},
"@types/sinonjs__fake-timers": {
"version": "8.1.2",
"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz",
"integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==",
"dev": true
},
"@types/sockjs": {
"version": "0.3.33",
"resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz",
@ -20320,6 +20473,12 @@
"setimmediate": "^1.0.5"
}
},
"just-extend": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
"integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==",
"dev": true
},
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@ -20429,6 +20588,12 @@
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"dev": true
},
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
"dev": true
},
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@ -21008,6 +21173,45 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
"nise": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz",
"integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==",
"dev": true,
"requires": {
"@sinonjs/commons": "^2.0.0",
"@sinonjs/fake-timers": "^10.0.2",
"@sinonjs/text-encoding": "^0.7.1",
"just-extend": "^4.0.2",
"path-to-regexp": "^1.7.0"
},
"dependencies": {
"@sinonjs/commons": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
"integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
"dev": true,
"requires": {
"type-detect": "4.0.8"
}
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
"dev": true
},
"path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"dev": true,
"requires": {
"isarray": "0.0.1"
}
}
}
},
"no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@ -22721,6 +22925,20 @@
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true
},
"sinon": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.1.0.tgz",
"integrity": "sha512-cS5FgpDdE9/zx7no8bxROHymSlPLZzq0ChbbLk1DrxBfc+eTeBK3y8nIL+nu/0QeYydhhbLIr7ecHJpywjQaoQ==",
"dev": true,
"requires": {
"@sinonjs/commons": "^3.0.0",
"@sinonjs/fake-timers": "^10.2.0",
"@sinonjs/samsam": "^8.0.0",
"diff": "^5.1.0",
"nise": "^5.1.4",
"supports-color": "^7.2.0"
}
},
"sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",

View File

@ -332,6 +332,7 @@
"@types/react-dom": "^18.2.3",
"@types/react-syntax-highlighter": "^15.5.6",
"@types/shell-escape": "^0.2.1",
"@types/sinon": "^10.0.15",
"@types/uuid": "^9.0.1",
"@types/vscode": "^1.77.0",
"@typescript-eslint/eslint-plugin": "^5.56.0",
@ -354,6 +355,7 @@
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"redux": "^4.2.1",
"sinon": "^15.1.0",
"style-loader": "^3.3.2",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2",

View File

@ -1,8 +1,4 @@
import { vs } from "react-syntax-highlighter/dist/esm/styles/hljs";
import CustomCommands from "./customCommand";
import { logger } from "../util/logger";
import * as vscode from 'vscode';
import * as path from 'path';
export interface Command {
name: string;

View File

@ -1,7 +1,7 @@
import * as vscode from 'vscode';
import * as path from 'path';
import { createTempSubdirectory, getLanguageIdByFileName } from '../util/commonUtil';
import { UiUtilWrapper } from '../util/uiUtil';
export async function handleCodeSelected(fileSelected: string, codeSelected: string) {
// get file name from fileSelected
@ -15,7 +15,7 @@ export async function handleCodeSelected(fileSelected: string, codeSelected: str
const languageId = await getLanguageIdByFileName(fileSelected);
// get relative path of workspace
const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath;
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
const relativePath = path.relative(workspaceDir!, fileSelected);
// convert fileContent to markdown code block with languageId and file path
@ -27,7 +27,7 @@ export async function handleCodeSelected(fileSelected: string, codeSelected: str
const jsonData = JSON.stringify(data);
// save markdownCodeBlock to temp file
await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFile), Buffer.from(jsonData));
await UiUtilWrapper.writeFile(tempFile, jsonData);
return `[context|${tempFile}]`;
}

View File

@ -1,15 +1,15 @@
import * as path from 'path';
import * as vscode from 'vscode';
import { ChatContext } from './contextManager';
import { createTempSubdirectory, runCommandStringAndWriteOutput } from '../util/commonUtil';
import { logger } from '../util/logger';
import { UiUtilWrapper } from '../util/uiUtil';
export const customCommandContext: ChatContext = {
name: '<custom command>',
description: 'custorm command',
handler: async () => {
// popup a dialog to ask for the command line to run
const customCommand = await vscode.window.showInputBox({
const customCommand = await UiUtilWrapper.showInputBox({
prompt: 'Input your custom command',
placeHolder: 'for example: ls -l'
});

View File

@ -1,8 +1,8 @@
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import { createTempSubdirectory, getLanguageIdByFileName } from '../util/commonUtil';
import { UiUtilWrapper } from '../util/uiUtil';
export async function handleFileSelected(fileSelected: string) {
// get file name from fileSelected
@ -18,7 +18,7 @@ export async function handleFileSelected(fileSelected: string) {
const languageId = await getLanguageIdByFileName(fileSelected);
// get relative path of workspace
const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath;
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
const relativePath = path.relative(workspaceDir!, fileSelected);
// convert fileContent to markdown code block with languageId and file path
@ -30,7 +30,7 @@ export async function handleFileSelected(fileSelected: string) {
const jsonData = JSON.stringify(data);
// save markdownCodeBlock to temp file
await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFile), Buffer.from(jsonData));
await UiUtilWrapper.writeFile(tempFile, jsonData);
return `[context|${tempFile}]`;
}

View File

@ -1,5 +1,4 @@
import * as vscode from 'vscode';
import * as path from 'path';
import { createTempSubdirectory, runCommandStringAndWriteOutput } from '../util/commonUtil';
import { logger } from '../util/logger';

View File

@ -9,6 +9,7 @@ import { FilePairManager } from '../util/diffFilePairs';
import * as process from 'process';
import { UiUtilWrapper } from '../util/uiUtil';
export function checkDevChatDependency(): boolean {
@ -37,11 +38,10 @@ export function checkDevChatDependency(): boolean {
}
}
export async function checkOpenaiApiKey() {
const secretStorage: vscode.SecretStorage = ExtensionContextHolder.context!.secrets;
let openaiApiKey = await secretStorage.get("devchat_OPENAI_API_KEY");
export async function checkOpenAiAPIKey() {
let openaiApiKey = await UiUtilWrapper.secretStorageGet("devchat_OPENAI_API_KEY");
if (!openaiApiKey) {
openaiApiKey = vscode.workspace.getConfiguration('DevChat').get('API_KEY');
openaiApiKey = UiUtilWrapper.getConfiguration('DevChat', 'API_KEY');
}
if (!openaiApiKey) {
openaiApiKey = process.env.OPENAI_API_KEY;
@ -52,48 +52,6 @@ export async function checkOpenaiApiKey() {
return true;
}
function checkOpenaiKey() {
let openaiApiKey = vscode.workspace.getConfiguration('DevChat').get('API_KEY');
if (!openaiApiKey) {
openaiApiKey = process.env.OPENAI_API_KEY;
}
if (!openaiApiKey) {
// OpenAI key not set
vscode.window.showInputBox({
placeHolder: 'Please input your OpenAI API key (or DevChat access key)'
}).then((value) => {
if (value) {
// 设置用户输入的API Key
vscode.workspace.getConfiguration('DevChat').update('API_KEY', value, true);
}
});
return false;
}
return true;
}
function checkDependencyPackage() {
const dependencyInstalled = checkDevChatDependency();
if (!dependencyInstalled) {
// Prompt the user, whether to install devchat using pip3 install devchat
const installPrompt = 'devchat is not installed. Do you want to install it using pip3 install devchat?';
const installAction = 'Install';
vscode.window.showInformationMessage(installPrompt, installAction).then((selectedAction) => {
if (selectedAction === installAction) {
// Install devchat using pip3 install devchat
const terminal = vscode.window.createTerminal("DevChat Install");
terminal.sendText("pip3 install --upgrade devchat");
terminal.show();
}
});
}
if (!checkOpenaiKey()) {
return;
}
}
function registerOpenChatPanelCommand(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('devchat.openChatPanel',async () => {
await vscode.commands.executeCommand('devchat-view.focus');
@ -278,7 +236,6 @@ export function regApplyDiffResultCommand(context: vscode.ExtensionContext) {
}
export {
checkDependencyPackage,
registerOpenChatPanelCommand,
registerAddContextCommand,
registerAskForCodeCommand,

View File

@ -22,12 +22,16 @@ import { regDevChatView, regTopicView } from './contributes/views';
import ExtensionContextHolder from './util/extensionContext';
import { logger } from './util/logger';
import { LoggerChannelVscode } from './util/logger_vscode';
import { createStatusBarItem } from './panel/statusBarView';
import { UiUtilWrapper } from './util/uiUtil';
function activate(context: vscode.ExtensionContext) {
ExtensionContextHolder.context = context;
logger.init(context);
logger.init(LoggerChannelVscode.getInstance());
UiUtilWrapper.init(new UiUtilVscode());
regLanguageContext();

View File

@ -8,6 +8,7 @@ import { regInMessage, regOutMessage } from '../util/reg_messages';
import { applyCodeChanges, isValidActionString } from '../util/appyDiff';
import { logger } from '../util/logger';
import { FilePairManager } from '../util/diffFilePairs';
import { UiUtilWrapper } from '../util/uiUtil';
@ -64,7 +65,7 @@ export async function diffView(code: string, tarFile: string) {
const tempFile = path.join(tempDir, fileName);
// save code to temp file
await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFile), Buffer.from(code));
await UiUtilWrapper.writeFile(tempFile, code);
// open diff view
FilePairManager.getInstance().addFilePair(curFile, tempFile);

View File

@ -20,7 +20,7 @@ export default class ChatPanel {
// 创建 .chat 目录并复制 workflows
createChatDirectoryAndCopyInstructionsSync(extensionUri);
const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath;
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
if (workspaceDir) {
const workflowsDir = path.join(workspaceDir!, '.chat', 'workflows');
CustomCommands.getInstance().parseCommands(workflowsDir);

View File

@ -27,7 +27,7 @@ export class DevChatViewProvider implements vscode.WebviewViewProvider {
// 创建 .chat 目录并复制 workflows
createChatDirectoryAndCopyInstructionsSync(ExtensionContextHolder.context?.extensionUri!);
const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath;
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
if (workspaceDir) {
const workflowsDir = path.join(workspaceDir!, '.chat', 'workflows');
CustomCommands.getInstance().parseCommands(workflowsDir);

View File

@ -42,7 +42,7 @@ export function createStatusBarItem(context: vscode.ExtensionContext): vscode.St
// 3. ready
if (devchatStatus === '' || devchatStatus === 'waiting install devchat') {
let bOk = true;
let devChat: string | undefined = vscode.workspace.getConfiguration('DevChat').get('DevChatPath');
let devChat: string | undefined = UiUtilWrapper.getConfiguration('DevChat', 'DevChatPath');
if (!devChat) {
bOk = false;
}

View File

@ -1,5 +1,4 @@
// devchat.ts
import * as vscode from 'vscode';
import * as dotenv from 'dotenv';
import * as path from 'path';
import * as fs from 'fs';
@ -7,6 +6,7 @@ import * as fs from 'fs';
import { logger } from '../util/logger';
import { CommandRun } from "../util/commonUtil";
import ExtensionContextHolder from '../util/extensionContext';
import { UiUtilWrapper } from '../util/uiUtil';
@ -84,11 +84,10 @@ class DevChat {
return args;
}
async getOpenaiApiKey(): Promise<string | undefined> {
const secretStorage: vscode.SecretStorage = ExtensionContextHolder.context!.secrets;
let openaiApiKey = await secretStorage.get("devchat_OPENAI_API_KEY");
async getOpenAiApiKey(): Promise<string | undefined> {
let openaiApiKey = await UiUtilWrapper.secretStorageGet("devchat_OPENAI_API_KEY");
if (!openaiApiKey) {
openaiApiKey = vscode.workspace.getConfiguration('DevChat').get('API_KEY');
openaiApiKey = UiUtilWrapper.getConfiguration('DevChat', 'API_KEY');
}
if (!openaiApiKey) {
openaiApiKey = process.env.OPENAI_API_KEY;
@ -166,7 +165,7 @@ class DevChat {
const args = await this.buildArgs(options);
args.push(content);
const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath;
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
let openaiApiKey = await this.getOpenaiApiKey();
if (!openaiApiKey) {
logger.channel()?.error('OpenAI key is invalid!');
@ -177,13 +176,13 @@ class DevChat {
// 如果配置了devchat的TOKEN那么就需要使用默认的代理
let openAiApiBaseObject = this.apiEndpoint(openaiApiKey);
const openaiModel = vscode.workspace.getConfiguration('DevChat').get('OpenAI.model');
const openaiTemperature = vscode.workspace.getConfiguration('DevChat').get('OpenAI.temperature');
const openaiStream = vscode.workspace.getConfiguration('DevChat').get('OpenAI.stream');
const llmModel = vscode.workspace.getConfiguration('DevChat').get('llmModel');
const tokensPerPrompt = vscode.workspace.getConfiguration('DevChat').get('OpenAI.tokensPerPrompt');
const openaiModel = UiUtilWrapper.getConfiguration('DevChat', 'OpenAI.model');
const openaiTemperature = UiUtilWrapper.getConfiguration('DevChat', 'OpenAI.temperature');
const openaiStream = UiUtilWrapper.getConfiguration('DevChat', 'OpenAI.stream');
const llmModel = UiUtilWrapper.getConfiguration('DevChat', 'llmModel');
const tokensPerPrompt = UiUtilWrapper.getConfiguration('DevChat', 'OpenAI.tokensPerPrompt');
let devChat: string | undefined = vscode.workspace.getConfiguration('DevChat').get('DevChatPath');
let devChat: string | undefined = UiUtilWrapper.getConfiguration('DevChat', 'DevChatPath');
if (!devChat) {
devChat = 'devchat';
}
@ -253,7 +252,7 @@ class DevChat {
async log(options: LogOptions = {}): Promise<LogEntry[]> {
const args = this.buildLogArgs(options);
const devChat = this.getDevChatPath();
const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath;
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
const openaiApiKey = process.env.OPENAI_API_KEY;
logger.channel()?.info(`Running devchat with args: ${args.join(" ")}`);
@ -286,7 +285,7 @@ class DevChat {
if (options.maxCount) {
args.push('--max-count', `${options.maxCount}`);
} else {
const maxLogCount = vscode.workspace.getConfiguration('DevChat').get('maxLogCount');
const maxLogCount = UiUtilWrapper.getConfiguration('DevChat', 'maxLogCount');
args.push('--max-count', `${maxLogCount}`);
}
@ -294,7 +293,7 @@ class DevChat {
}
private getDevChatPath(): string {
let devChat: string | undefined = vscode.workspace.getConfiguration('DevChat').get('DevChatPath');
let devChat: string | undefined = UiUtilWrapper.getConfiguration('DevChat', 'DevChatPath');
if (!devChat) {
devChat = 'devchat';
}

View File

@ -1,5 +1,4 @@
import { spawn } from "child_process";
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
@ -17,7 +16,7 @@ class DtmWrapper {
private commandRun: CommandRun;
constructor() {
this.workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath || '.';
this.workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath() || '.';
this.commandRun = new CommandRun();
}

View File

@ -1,5 +1,4 @@
import { v4 as uuidv4 } from 'uuid';
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
@ -194,7 +193,7 @@ export class TopicManager {
if (topic.firstMessageHash) {
// get ${WORKSPACE_ROOT}/.chat/.deletedTopics
const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath;
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
const deletedTopicsPath = path.join(workspaceDir!, '.chat', '.deletedTopics');
// read ${WORKSPACE_ROOT}/.chat/.deletedTopics as String[]
@ -218,7 +217,7 @@ export class TopicManager {
}
isDeleteTopic(topicId: string) {
const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath;
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
const deletedTopicsPath = path.join(workspaceDir!, '.chat', '.deletedTopics');
if (!fs.existsSync(deletedTopicsPath)) {

View File

@ -1,11 +1,11 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as vscode from 'vscode';
import { parseArgsStringToArgv } from 'string-argv';
import { logger } from './logger';
import { spawn, exec } from 'child_process';
import { UiUtilWrapper } from './uiUtil';
export function createTempSubdirectory(subdir: string): string {
// 获取系统临时目录
@ -110,7 +110,7 @@ export async function runCommandAndWriteOutput(
): Promise<CommandResult> {
const run = new CommandRun();
const options = {
cwd: vscode.workspace.workspaceFolders?.[0].uri.fsPath || '.',
cwd: UiUtilWrapper.workspaceFoldersFirstPath() || '.',
};
return run.spawnAsync(command, args, options, undefined, undefined, undefined, outputFile);
@ -122,7 +122,7 @@ export async function runCommandStringAndWriteOutput(
): Promise<CommandResult> {
const run = new CommandRun();
const options = {
cwd: vscode.workspace.workspaceFolders?.[0].uri.fsPath || '.'
cwd: UiUtilWrapper.workspaceFoldersFirstPath() || '.'
};
// Split the commandString into command and args array using string-argv
@ -143,10 +143,7 @@ export async function runCommandStringAndWriteOutput(
export async function getLanguageIdByFileName(fileName: string): Promise<string | undefined> {
try {
// 打开指定的文件
const document = await vscode.workspace.openTextDocument(fileName);
// 获取文件的语言标识符
const languageId = document.languageId;
const languageId = await UiUtilWrapper.languageId(fileName);
return languageId;
} catch (error) {
// 如果无法打开文件或发生其他错误返回undefined

View File

@ -1,12 +1,19 @@
import * as vscode from 'vscode';
export interface LogChannel {
info(message: string, ...args: any[]): void;
warn(message: string, ...args: any[]): void;
error(message: string | Error, ...args: any[]): void;
debug(message: string, ...args: any[]): void;
show(): void;
}
export class logger {
private static _channel: vscode.LogOutputChannel | undefined;
public static init(context: vscode.ExtensionContext): void {
this._channel = vscode.window.createOutputChannel('DevChat', { log: true });
private static _channel: LogChannel | undefined;
public static init(channel: LogChannel): void {
this._channel = channel;
}
public static channel(): vscode.LogOutputChannel | undefined {
public static channel(): LogChannel | undefined {
return this._channel;
}
}

39
src/util/logger_vscode.ts Normal file
View File

@ -0,0 +1,39 @@
import { LogChannel } from "./logger";
import * as vscode from 'vscode';
export class LoggerChannelVscode implements LogChannel {
_channel: vscode.LogOutputChannel;
private static _instance: LoggerChannelVscode;
private constructor() {
this._channel = vscode.window.createOutputChannel('DevChat', { log: true });
}
public static getInstance(): LoggerChannelVscode {
if (!this._instance) {
this._instance = new LoggerChannelVscode();
}
return this._instance;
}
info(message: string, ...args: any[]): void {
this._channel.info(message, ...args);
}
warn(message: string, ...args: any[]): void {
this._channel.warn(message, ...args);
}
error(message: string | Error, ...args: any[]): void {
this._channel.error(message, ...args);
}
debug(message: string, ...args: any[]): void {
this._channel.debug(message, ...args);
}
show(): void {
this._channel.show();
}
}

65
src/util/uiUtil.ts Normal file
View File

@ -0,0 +1,65 @@
export interface UiUtil {
languageId(uri: string): Promise<string>;
workspaceFoldersFirstPath(): string | undefined;
getConfiguration(key1: string, key2: string): string | undefined;
secretStorageGet(key: string): Promise<string | undefined>;
writeFile(uri: string, content: string): Promise<void>;
showInputBox(option: object): Promise<string | undefined>;
}
import * as vscode from 'vscode';
export class UiUtilVscode implements UiUtil {
public async languageId(uri: string): Promise<string> {
const document = await vscode.workspace.openTextDocument(uri);
return document.languageId;
}
public workspaceFoldersFirstPath(): string | undefined {
return vscode.workspace.workspaceFolders?.[0].uri.fsPath;
}
public getConfiguration(key1: string, key2: string): string | undefined {
return vscode.workspace.getConfiguration(key1).get(key2);
}
public async secretStorageGet(key: string): Promise<string | undefined> {
const secretStorage: vscode.SecretStorage = ExtensionContextHolder.context!.secrets;
let openaiApiKey = await secretStorage.get(key);
return openaiApiKey;
}
public async writeFile(uri: string, content: string): Promise<void> {
vscode.workspace.fs.writeFile(vscode.Uri.file(uri), Buffer.from(content));
}
public async showInputBox(option: object): Promise<string | undefined> {
return vscode.window.showInputBox({
prompt: 'Input your custom command',
placeHolder: 'for example: ls -l'
});
}
}
export class UiUtilWrapper {
private static _uiUtil: UiUtil | undefined;
public static init(uiUtil: UiUtil): void {
this._uiUtil = uiUtil;
}
public static async languageId(uri: string): Promise<string | undefined> {
return this._uiUtil?.languageId(uri);
}
public static workspaceFoldersFirstPath(): string | undefined {
return this._uiUtil?.workspaceFoldersFirstPath();
}
public static getConfiguration(key1: string, key2: string): string | undefined {
return this._uiUtil?.getConfiguration(key1, key2);
}
public static async secretStorageGet(key: string): Promise<string | undefined> {
return this._uiUtil?.secretStorageGet(key);
}
public static async writeFile(uri: string, content: string): Promise<void> {
return this._uiUtil?.writeFile(uri, content);
}
public static async showInputBox(option: object): Promise<string | undefined> {
return this._uiUtil?.showInputBox(option);
}
}

View File

View File

@ -1,22 +1,64 @@
// test/util/logger.test.ts
import { expect } from 'chai';
import { describe, it } from 'mocha';
import * as vscode from '../vscode';
import { logger, LogChannel } from '../../src/util/logger';
import proxyquire from 'proxyquire';
class MockLogChannel implements LogChannel {
logs: string[] = [];
// Use proxyquire to replace the 'vscode' module in logger.ts
const { logger } = proxyquire('../../src/util/logger', {
vscode: { ...vscode },
}).logger;
info(message: string, ...args: any[]): void {
this.logs.push(`[INFO] ${message} ${args.join(' ')}`);
}
warn(message: string, ...args: any[]): void {
this.logs.push(`[WARN] ${message} ${args.join(' ')}`);
}
error(message: string | Error, ...args: any[]): void {
this.logs.push(`[ERROR] ${message} ${args.join(' ')}`);
}
debug(message: string, ...args: any[]): void {
this.logs.push(`[DEBUG] ${message} ${args.join(' ')}`);
}
show(): void {
// Do nothing
}
}
describe('logger', () => {
it('should initialize the logger and create a channel', () => {
const context = {} as vscode.vscode.ExtensionContext;
// logger.init(context);
// Arrange
const mockChannel = new MockLogChannel();
// const channel = logger.channel();
// expect(channel).to.not.be.undefined;
// expect(channel?.name).to.equal('DevChat');
expect(true).to.be.true;
// Act
logger.init(mockChannel);
// Assert
const channel = logger.channel();
expect(channel).to.not.be.undefined;
expect(channel).to.equal(mockChannel);
});
it('should log messages using the initialized channel', () => {
// Arrange
const mockChannel = new MockLogChannel();
logger.init(mockChannel);
// Act
logger.channel()?.info('Test info message');
logger.channel()?.warn('Test warn message');
logger.channel()?.error('Test error message');
logger.channel()?.debug('Test debug message');
// Assert
expect(mockChannel.logs).to.deep.equal([
'[INFO] Test info message ',
'[WARN] Test warn message ',
'[ERROR] Test error message ',
'[DEBUG] Test debug message ',
]);
});
});

13
test/util/utils.test.ts Normal file
View File

@ -0,0 +1,13 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';
describe('yourFunction', () => {
it('should return the correct result for input 1', () => {
const input = 1;
const expectedResult = 'expectedResult';
const result = 'expectedResult';
expect(result).to.equal(expectedResult);
});
// Add more test cases here
});

View File

@ -1,3 +0,0 @@
import * as vscodeTest from 'vscode-test';
export const vscode = vscodeTest.vscode;