From e8bfd95ba62ce4973f388f14654e544e9aeba41c Mon Sep 17 00:00:00 2001 From: Heipi <793181018@qq.com> Date: Thu, 5 Sep 2024 18:10:00 +0800 Subject: [PATCH 01/17] Add code to retrieve class information (unimplemented feature) --- prompts/genius/zh-cn/code/auto-method.vm | 12 +- src/ProviderContainer.config.ts | 8 ++ .../autoMethod/AutoMethodActionExecutor.ts | 34 +++-- .../autoMethod/AutoMethodTemplateContext.ts | 67 ++++++++- .../csharp/model/CsharpClassExtractor.ts | 37 +++++ .../csharp/model/CsharpFieldExtractor.ts | 80 +++++++++++ .../csharp/model/CsharpMethodExtractor.ts | 132 ++++++++++++++++++ 7 files changed, 349 insertions(+), 21 deletions(-) create mode 100644 src/code-context/csharp/model/CsharpClassExtractor.ts create mode 100644 src/code-context/csharp/model/CsharpFieldExtractor.ts create mode 100644 src/code-context/csharp/model/CsharpMethodExtractor.ts diff --git a/prompts/genius/zh-cn/code/auto-method.vm b/prompts/genius/zh-cn/code/auto-method.vm index 81d8fde6..d62c358e 100644 --- a/prompts/genius/zh-cn/code/auto-method.vm +++ b/prompts/genius/zh-cn/code/auto-method.vm @@ -1,14 +1,12 @@ -Write documentation for user's given ${context.language} code. + #if($context.chatContext.length > 0 ) ${context.chatContext} #end #if($context.forbiddenRules.length > 0) ${context.forbiddenRules} #end -- Start your documentation with ${context.startSymbol} here, and ends with `${context.endSymbol}`. -Here is User's code: -```${context.language} -${context.code} -``` -Please write documentation for this code inside the Markdown code block. +${context.customFrameworkCodeFileContext} +Refer to the relevant ${context.language} code and documentation above, as well as the comments above the method, +Complete the following additional code: +${context.code} diff --git a/src/ProviderContainer.config.ts b/src/ProviderContainer.config.ts index 05e473bc..152fba26 100644 --- a/src/ProviderContainer.config.ts +++ b/src/ProviderContainer.config.ts @@ -35,6 +35,9 @@ import { SpringContextProvider } from './toolchain-context/framework/jvm/SpringC import { ToolchainContextProvider } from './toolchain-context/ToolchainContextProvider'; import { JavaVersionProvider } from './toolchain-context/version/JavaVersionProvider'; import { AutoMethodActionCreator } from './action/autoMethod/AutoMethodActionCreator'; +import { CsharpProfile } from './code-context/csharp/CsharpProfile'; +import { CsharpRelevantCodeProvider } from './code-context/csharp/CsharpRelevantCodeProvider'; +import { CsharpStructurerProvider } from './code-context/csharp/CsharpStructurerProvider'; // Action Register providerContainer.bind(IActionCreator).to(AutoDocActionCreator); @@ -57,10 +60,15 @@ export interface LanguageProvider { providerContainer.bind(IToolchainContextProvider).to(SpringContextProvider); providerContainer.bind(IToolchainContextProvider).to(JavaVersionProvider); + + providerContainer.bind(IRelevantCodeProvider).to(JavaRelevantCodeProvider); providerContainer.bind(ITestGenProvider).to(JavaTestGenProvider); providerContainer.bind(IBuildToolProvider).to(GradleBuildToolProvider); providerContainer.bind(IStructurerProvider).to(JavaStructurerProvider); +// Csharp +providerContainer.bind(IRelevantCodeProvider).to(CsharpRelevantCodeProvider); +providerContainer.bind(IStructurerProvider).to(CsharpStructurerProvider); // TypeScript providerContainer.bind(IToolchainContextProvider).to(JavaScriptContextProvider); diff --git a/src/action/autoMethod/AutoMethodActionExecutor.ts b/src/action/autoMethod/AutoMethodActionExecutor.ts index 4682cda3..d55c1719 100644 --- a/src/action/autoMethod/AutoMethodActionExecutor.ts +++ b/src/action/autoMethod/AutoMethodActionExecutor.ts @@ -1,5 +1,8 @@ +import fs from 'fs'; import { AutoDevExtension } from 'src/AutoDevExtension'; +import { CsharpClassExtractor } from 'src/code-context/csharp/model/CsharpClassExtractor'; import { CancellationTokenSource, Position, TextDocument, WorkspaceEdit } from 'vscode'; +import vscode from 'vscode'; import { ChatMessageRole, IChatMessage } from 'base/common/language-models/languageModels'; import { LanguageModelsService } from 'base/common/language-models/languageModelsService'; @@ -17,8 +20,7 @@ import { CreateToolchainContext } from '../../toolchain-context/ToolchainContext import { ActionExecutor } from '../_base/ActionExecutor'; import { AutoDocTemplateContext } from '../autodoc/AutoDocTemplateContext'; import { AutoMethodTemplateContext } from './AutoMethodTemplateContext'; -import fs from 'fs' -import vscode from 'vscode'; + export class AutoMethodActionExecutor implements ActionExecutor { type: ActionType = ActionType.AutoDoc; @@ -30,6 +32,7 @@ export class AutoMethodActionExecutor implements ActionExecutor { private range: NamedElement; private edit?: WorkspaceEdit; private language: string; + private autodev: AutoDevExtension; constructor(autodev: AutoDevExtension, document: TextDocument, range: NamedElement, edit?: WorkspaceEdit) { this.lm = autodev.lm; @@ -40,6 +43,7 @@ export class AutoMethodActionExecutor implements ActionExecutor { this.range = range; this.edit = edit; this.language = document.languageId; + this.autodev = autodev; } async execute() { @@ -49,24 +53,28 @@ export class AutoMethodActionExecutor implements ActionExecutor { const startSymbol = LANGUAGE_BLOCK_COMMENT_MAP[language]!.start; const endSymbol = LANGUAGE_BLOCK_COMMENT_MAP[language]!.end; + //帮忙,看一下这里 + if (this.language == 'csharp') { + let csharpClassExtractor = new CsharpClassExtractor(); + csharpClassExtractor.Init(document, this.autodev); + let classInfo = csharpClassExtractor.extractClassInfo(); + } - const config=vscode.workspace.getConfiguration('autodev.WorkSpeace'); - const customFrameworkCodeFilesPath=config.get('customFrameworkCodeFiles', []); - let customFrameworkCodeFileContext:string=""; + const config = vscode.workspace.getConfiguration('autodev.WorkSpeace'); + const customFrameworkCodeFilesPath = config.get('customFrameworkCodeFiles', []); + let customFrameworkCodeFileContext: string = ''; if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { const workspaceFolder = vscode.workspace.workspaceFolders[0]; const workspacePath = workspaceFolder.uri.fsPath; - if(customFrameworkCodeFilesPath.length>0) - { for(let i=0;i 0) { + for (let i = 0; i < customFrameworkCodeFilesPath.length; i++) { + let codeFileNormalPath = workspacePath + '\\' + customFrameworkCodeFilesPath[i]; + customFrameworkCodeFileContext += fs.readFileSync(codeFileNormalPath).toString(); + customFrameworkCodeFileContext += '\n'; } } } - const templateContext: AutoMethodTemplateContext = { language: language, startSymbol: startSymbol, @@ -75,7 +83,7 @@ export class AutoMethodActionExecutor implements ActionExecutor { forbiddenRules: [], // 原有代码块 originalMethodCodes: [], - customFrameworkCodeFileContext + customFrameworkCodeFileContext, }; if (range.commentRange) { diff --git a/src/action/autoMethod/AutoMethodTemplateContext.ts b/src/action/autoMethod/AutoMethodTemplateContext.ts index 0a4801d5..b85548ce 100644 --- a/src/action/autoMethod/AutoMethodTemplateContext.ts +++ b/src/action/autoMethod/AutoMethodTemplateContext.ts @@ -1,3 +1,5 @@ +import { Interface } from 'readline'; + import { TemplateContext } from '../../prompt-manage/template/TemplateContext'; export interface AutoMethodTemplateContext extends TemplateContext { @@ -6,5 +8,68 @@ export interface AutoMethodTemplateContext extends TemplateContext { code: string; forbiddenRules: string[]; originalMethodCodes: string[]; - customFrameworkCodeFileContext?:string; + customFrameworkCodeFileContext?: string; + classInfo?: ClassInfo; + completedMethodInfo?: CompletedMethodInfo[]; + classDescriptionInfo?: string; + classMemberVariable?: string[]; + classMemberMethodInfo?: string; + customFrameworkCodeFragments?: FrameworkCodeFragment[]; +} +/** + * 自定义框架代码片段 + */ +export interface FrameworkCodeFragment { + doc: string; + context: string; + code: string; +} +/** + * + * + **/ +export interface CompletedMethodInfo { + doc: string; + context: string; + code: string; + paramters: Parameter[]; +} + +export interface NeedCompletedMethodInfo { + name: string; + accessModifier: string; + returnValueType: string; + purpose: string; + paramters: Parameter[]; +} +export interface Parameter { + name: string; + type: string; + doc: string; +} +export interface ClassInfo { + name: string; + nameSpace: string; + purpose: string; + doc: string; + memberVariables: MemberVariableInfo[]; + memberMethods: MemberMethod[]; +} + +export interface MemberVariableInfo { + name: string; + doc: string; + type: string; +} +export interface MemberMethod { + name: string; + doc: string; + returnType: string; + parameterInfos: ParameterInfo[]; +} + +export interface ParameterInfo { + name: string; + doc: string; + type: string; } diff --git a/src/code-context/csharp/model/CsharpClassExtractor.ts b/src/code-context/csharp/model/CsharpClassExtractor.ts new file mode 100644 index 00000000..04c9e2c0 --- /dev/null +++ b/src/code-context/csharp/model/CsharpClassExtractor.ts @@ -0,0 +1,37 @@ +import { AutoDevExtension } from 'src/AutoDevExtension'; +import { TreeSitterFile } from 'src/code-context/ast/TreeSitterFile'; +import { TreeSitterFileManager } from 'src/editor/cache/TreeSitterFileManager'; +import vscode, { TextDocumentChangeEvent, Uri } from 'vscode'; + +import { ILanguageServiceProvider } from 'base/common/languages/languageService'; + +import { CsharpFieldExtractor, FieldInfo } from './CsharpFieldExtractor'; +import { CsharpMethodExtractor, MethodInfo } from './CsharpMethodExtractor'; + +export class CsharpClassExtractor { + private fieldExtractor?: CsharpFieldExtractor; + private methodExtractor?: CsharpMethodExtractor; + + constructor() {} + public async Init(text: vscode.TextDocument, autoDev: AutoDevExtension) { + let parser = await autoDev.treeSitterFileManager.create(text); + this.fieldExtractor = new CsharpFieldExtractor(text, parser); + this.methodExtractor = new CsharpMethodExtractor(text, parser); + } + public extractClassInfo(): ClassInfo | null { + if (this.fieldExtractor == null || this.methodExtractor == null) return null; + + const fields = this.fieldExtractor.extractFields(); + const methods = this.methodExtractor.extractMethods(); + + return { + fields, + methods, + }; + } +} + +export interface ClassInfo { + fields: FieldInfo[]; + methods: MethodInfo[]; +} diff --git a/src/code-context/csharp/model/CsharpFieldExtractor.ts b/src/code-context/csharp/model/CsharpFieldExtractor.ts new file mode 100644 index 00000000..2d89c785 --- /dev/null +++ b/src/code-context/csharp/model/CsharpFieldExtractor.ts @@ -0,0 +1,80 @@ +import { LanguageProfile, MemoizedQuery } from 'src/code-context/_base/LanguageProfile'; +import { TreeSitterFile } from 'src/code-context/ast/TreeSitterFile'; +import { TreeSitterFileManager } from 'src/editor/cache/TreeSitterFileManager'; +import vscode, { TextDocumentChangeEvent, Uri } from 'vscode'; +import Parser from 'web-tree-sitter'; + +import { ILanguageServiceProvider } from 'base/common/languages/languageService'; + +export class CsharpFieldExtractor { + private parser: TreeSitterFile; + private src: vscode.TextDocument; + + constructor(text: vscode.TextDocument, Parser: TreeSitterFile) { + this.src = text; + this.parser = Parser; + } + + public extractFields(): FieldInfo[] { + const tree = this.parser.tree; + const queryObj = new MemoizedQuery(` + (field_declaration + (attribute_list)? @attribute + (modifier)* @modifier + (variable_declaration + (variable_declarator + (identifier) @name + ) + (type) @type + ) + (comment)? @comment + ) + `); + const query = queryObj.query(this.parser.tsLanguage); + const fields: FieldInfo[] = []; + const matches = query.matches(tree.rootNode); + for (const match of matches) { + const field: FieldInfo = { + attributes: this.extractAttributes(match), + modifiers: this.extractModifiers(match), + type: this.extractType(match), + name: this.extractName(match), + comment: this.extractComment(match) + }; + fields.push(field); + } + + return fields; + } + + private extractAttributes(match: Parser.QueryMatch): string[] { + return match.captures.filter(capture => capture.name === 'attribute').map(capture => capture.node.text); + } + + private extractModifiers(match: Parser.QueryMatch): string[] { + return match.captures.filter(capture => capture.name === 'modifier').map(capture => capture.node.text); + } + + private extractType(match: Parser.QueryMatch): string { + const capture = match.captures.find(capture => capture.name === 'type'); + return capture ? capture.node.text : ''; + } + + private extractName(match: Parser.QueryMatch): string { + const capture = match.captures.find(capture => capture.name === 'name'); + return capture ? capture.node.text : ''; + } + + private extractComment(match: Parser.QueryMatch): string { + const capture = match.captures.find(capture => capture.name === 'comment'); + return capture ? capture.node.text : ''; + } +} + +export interface FieldInfo { + attributes: string[]; + modifiers: string[]; + type: string; + name: string; + comment: string; +} diff --git a/src/code-context/csharp/model/CsharpMethodExtractor.ts b/src/code-context/csharp/model/CsharpMethodExtractor.ts new file mode 100644 index 00000000..8a3ea5b4 --- /dev/null +++ b/src/code-context/csharp/model/CsharpMethodExtractor.ts @@ -0,0 +1,132 @@ +import { ILanguageServiceProvider } from "base/common/languages/languageService"; +import { LanguageProfile, MemoizedQuery } from "src/code-context/_base/LanguageProfile"; +import { TreeSitterFile } from "src/code-context/ast/TreeSitterFile"; +import { TreeSitterFileManager } from "src/editor/cache/TreeSitterFileManager"; +import vscode, { TextDocumentChangeEvent, Uri } from 'vscode'; +import Parser from "web-tree-sitter"; + +export class CsharpMethodExtractor { + private parser: TreeSitterFile; + private src:vscode.TextDocument + + + constructor(text: vscode.TextDocument,Parser:TreeSitterFile) { + this.src=text; + this.parser= Parser; + } + + public extractMethods(): MethodInfo[] { + const tree =this.parser.tree; + const queryObj= new MemoizedQuery(` + (method_declaration + (attribute_list)? @attribute + (modifier)* @modifier + (type) @return_type + (identifier) @name + (parameter_list + (parameter + (type) @param_type + (identifier) @param_name + )* + ) @parameters + (block) @body + ) + `) + const query= queryObj.query(this.parser.tsLanguage) + const methods: MethodInfo[] = []; + + + + + const matches = query.matches(tree.rootNode); + for (const match of matches) { + const method: MethodInfo = { + attributes: this.extractAttributes(match), + modifiers: this.extractModifiers(match), + returnType: this.extractReturnType(match), + name: this.extractName(match), + parameters: this.extractParameters(match), + body: this.extractBody(match), + methodComment: this.extractMethodComment(match), + parameterComments: this.extractParameterComments(match) + }; + methods.push(method); + } + + return methods; + } + + + private extractAttributes(match: Parser.QueryMatch): string[] { + return match.captures.filter(capture => capture.name === 'attribute').map(capture => capture.node.text); + } + + private extractModifiers(match: Parser.QueryMatch): string[] { + return match.captures.filter(capture => capture.name === 'modifier').map(capture => capture.node.text); + } + + private extractReturnType(match: Parser.QueryMatch): string { + const capture = match.captures.find(capture => capture.name === 'return_type'); + return capture ? capture.node.text : ''; + } + + private extractName(match: Parser.QueryMatch): string { + const capture = match.captures.find(capture => capture.name === 'name'); + return capture ? capture.node.text : ''; + } + + private extractParameters(match: Parser.QueryMatch): ParameterInfo[] { + const parameters: ParameterInfo[] = []; + const parameterCaptures = match.captures.filter(capture => capture.name === 'param_type' || capture.name === 'param_name'); + for (let i = 0; i < parameterCaptures.length; i += 2) { + parameters.push({ + type: parameterCaptures[i].node.text, + name: parameterCaptures[i + 1].node.text + }); + } + return parameters; + } + + private extractBody(match: Parser.QueryMatch): string { + const capture = match.captures.find(capture => capture.name === 'body'); + return capture ? capture.node.text : ''; + } + + private extractMethodComment(match: Parser.QueryMatch): string { + const capture = match.captures.find(capture => capture.name === 'method_comment'); + return capture ? capture.node.text : ''; + } + + private extractParameterComments(match: Parser.QueryMatch): { [key: string]: string } { + const parameterComments: { [key: string]: string } = {}; + const parameterList = match.captures.find(capture => capture.name === 'parameters')?.node; + if (parameterList) { + const parameterNodes = parameterList.children.filter(child => child.type === 'parameter'); + for (const parameterNode of parameterNodes) { + const parameterName = parameterNode.children.find(child => child.type === 'identifier')?.text; + const commentNode = parameterNode.previousSibling; + if (parameterName && commentNode && commentNode.type === 'comment') { + parameterComments[parameterName] = commentNode.text; + } + } + } + return parameterComments; + } +} + + +export interface MethodInfo { + attributes: string[]; + modifiers: string[]; + returnType: string; + name: string; + parameters: ParameterInfo[]; + body: string; + methodComment: string; + parameterComments: { [key: string]: string }; +} + +export interface ParameterInfo { + type: string; + name: string; +} From c035b314dac2e529d50041bb62189f48943a926a Mon Sep 17 00:00:00 2001 From: Heipi <793181018@qq.com> Date: Mon, 23 Sep 2024 14:26:00 +0800 Subject: [PATCH 02/17] Add the following support for CSharp language 1. Add code instances through the codelens feature to add references to inference services 2. Add custom code snippets through the codelens feature to add contextual references to inference services --- l10n/bundle.l10n.zh-cn.json | 3 + package.json | 8 +- package.nls.json | 3 + package.nls.zh-cn.json | 25 +-- prompts/genius/zh-cn/code/auto-doc.vm | 5 + src/AutoDevExtension.ts | 14 ++ .../AddCodeSample/AddCodeSampleExecutor.ts | 108 +++++++++++ .../AddFrameworkCodeFragmentExecutor.ts | 64 +++++++ .../autoMethod/AutoMethodActionExecutor.ts | 42 ++--- .../autoMethod/AutoMethodTemplateContext.ts | 72 +------- src/action/autodoc/AutoDocActionCreator.ts | 1 + .../providers/AutoDevCodeLensProvider.ts | 37 +++- .../common/configuration/configuration.ts | 7 +- .../common/workspace/WorkspaceSerializer.ts | 89 +++++++++ src/base/common/workspace/WorkspaceService.ts | 174 ++++++++++++++++++ .../csharp/model/BaseCsharpElement.ts | 18 ++ .../csharp/model/CodeSampleExtractor.ts | 50 +++++ .../csharp/model/CsharpClassExtractor.ts | 138 +++++++++++--- .../csharp/model/CsharpFieldExtractor.ts | 80 -------- .../csharp/model/CsharpMethodExtractor.ts | 132 ------------- src/code-context/csharp/model/FieldInfo.ts | 87 +++++++++ .../model/FrameworkCodeFragmentExtractor.ts | 76 ++++++++ src/code-context/csharp/model/MethodInfo.ts | 105 +++++++++++ src/code-context/csharp/model/PropertyInfo.ts | 102 ++++++++++ src/code-search/schemas/indexes/c_sharp.scm | 40 ++-- src/commands/commandsService.ts | 25 +++ src/editor/ast/NamedElement.ts | 5 +- src/editor/ast/NamedElementBuilder.ts | 1 + src/extension.ts | 3 + 29 files changed, 1136 insertions(+), 378 deletions(-) create mode 100644 src/action/AddCodeSample/AddCodeSampleExecutor.ts create mode 100644 src/action/addCodeFragment/AddFrameworkCodeFragmentExecutor.ts create mode 100644 src/base/common/workspace/WorkspaceSerializer.ts create mode 100644 src/base/common/workspace/WorkspaceService.ts create mode 100644 src/code-context/csharp/model/BaseCsharpElement.ts create mode 100644 src/code-context/csharp/model/CodeSampleExtractor.ts delete mode 100644 src/code-context/csharp/model/CsharpFieldExtractor.ts delete mode 100644 src/code-context/csharp/model/CsharpMethodExtractor.ts create mode 100644 src/code-context/csharp/model/FieldInfo.ts create mode 100644 src/code-context/csharp/model/FrameworkCodeFragmentExtractor.ts create mode 100644 src/code-context/csharp/model/MethodInfo.ts create mode 100644 src/code-context/csharp/model/PropertyInfo.ts diff --git a/l10n/bundle.l10n.zh-cn.json b/l10n/bundle.l10n.zh-cn.json index 486c5421..a46aa1f4 100644 --- a/l10n/bundle.l10n.zh-cn.json +++ b/l10n/bundle.l10n.zh-cn.json @@ -3,10 +3,13 @@ "AutoComment": "生成注释", "Custom Action": "自定义动作", "Explain Code": "解释代码", + "Not Completed": "(未完成)", "Optimize Code": "优化代码", "Fix Code": "修复代码", "Quick Chat": "快速聊天", "AutoMethod": "填充当前方法", + "addFrameworkCodeFragment": "添加自定义代码块", + "addCodeSample": "添加自定义样例", "Optimize the code": "请优化下面的代码", "How do I fix this problem in the above code?": "请帮我修复下面代码中的问题:", "I got the following error, can you please help explain how to fix it?": "我遇到了以下错误,请帮我解释如何修复它?", diff --git a/package.json b/package.json index 87e9183b..25f97e33 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,9 @@ "autoComment", "autoTest", "customAction", - "AutoMethod" + "AutoMethod", + "addCodeSample", + "addFrameworkCodeFragment" ] }, "uniqueItems": true, @@ -97,7 +99,9 @@ "quickChat", "autoTest", "autoComment", - "AutoMethod" + "AutoMethod", + "addCodeSample", + "addFrameworkCodeFragment" ], "description": "%configuration.codelensDisplayItems.description%", "order": 4 diff --git a/package.nls.json b/package.nls.json index 6878c0ee..ceb5593d 100644 --- a/package.nls.json +++ b/package.nls.json @@ -86,6 +86,7 @@ "configuration.transformers.onnxWasmNumThreads.description": "Number of threads to use for the WebAssembly backend. Defaults to cpus numbers.", "configuration.transformers.onnxLoglevel.description": "Log level for the ONNX runtime. Defaults to error.", + "Not Completed": "(Not Completed)", "command.autoTest.title": "Generate Test", "command.genApiData.title": "Generate API Data", "command.openSettings.title": "Open Settings", @@ -93,6 +94,8 @@ "command.feedback.title": "Feedback", "command.quickChat.title": "Quick Chat", "command.AutoMethod.title": "Generate Method Codes", + "command.addFrameworkCodeFragment.title": "Add Framework Code Fragment", + "command.addCodeSample.title": "Add Code Sample", "command.explainCode.title": "Explain Code", "command.optimizeCode.title": "Optimize Code", "command.quickFix.title": "Quick Fix", diff --git a/package.nls.zh-cn.json b/package.nls.zh-cn.json index 8bfccf83..8a62ca27 100644 --- a/package.nls.zh-cn.json +++ b/package.nls.zh-cn.json @@ -1,19 +1,15 @@ { "view.chat.title": "聊天", - "configuration.general.title": "通用", "configuration.enableRenameSuggestion.description": "启用重命名建议", "configuration.customPromptDir.description": "自定义提示目录", - "configuration.codelensDisplayMode.item.expand": "操作平铺", "configuration.codelensDisplayMode.item.collapse": "操作收起", "configuration.codelensDisplayMode.description": "控制行间按钮的显示方式", "configuration.codelensDisplayItems.description": "配置生成注释、生成测试、代码解释、代码优化等行间按钮的展示", - "configuration.chat.title": "对话", "configuration.chat.enable.description": "启用或禁用聊天功能", "configuration.chat.models.description": "可供聊天界面选择的模型列表", - "configuration.completions.title": "代码补全", "configuration.completions.enable.description": "启用或禁用代码补全", "configuration.completions.provider.description": "模型供应商", @@ -23,15 +19,12 @@ "configuration.v.fimSpecialTokens.description": "中间填充(FIM)是代码自动完成模型支持的一种特殊提示格式,可以在两个已编写的代码块之间完成代码。", "configuration.completions.requestDelay.markdownDescription": "发起自动补全的请求延迟时间,避免过度消耗 API 令牌。只有启用 `#autodev.completions.enable#` 后,`requestDelay` 配置才生效。", "configuration.completions.enableLegacyMode.markdownDescription": "启用兼容模型,使用旧的 `/v1/completions` 代替 `/v1/chat/completions`,仅 openai 服务支持。", - "configuration.embeddings.title": "向量生成", "configuration.embeddings.provider.description": "模型供应商", "configuration.embeddings.model.description": "覆盖模型供应商中的初始模型", "configuration.embeddings.model.markdownDescription": "为代码生成向量的模型。默认为 transformers.js 支持的模型,点击 [这里](https://vscode.unitmesh.cc) 以了解更多。", - "configuration.storages.title": "数据存储", "configuration.providers.title": "模型供应商", - "configuration.openai.apiType.description": "OpenAI 的服务提供商", "configuration.openai.baseURL.markdownDescription": "OpenAI 的 API URL。详见 [API 文档](https://platform.openai.com/docs/api-reference/introduction)", "configuration.openai.apiKey.markdownDescription": "OpenAI 的遗留密钥。提供对用户已添加到的所有组织和所有项目的访问;访问 [API密钥](https://platform.openai.com/account/api-keys) 以查看可用的密钥。目前仍可正常使用,但强烈建议过渡到项目密钥以获得最佳安全实践。", @@ -39,30 +32,24 @@ "configuration.openai.organization.description": "对于多组织用户或使用旧的 API 密钥访问的用户,可配置组织ID将请求归入所选组织及项目。", "configuration.openai.model.description": "模型名称", "configuration.openai.embeddingModel.description": "模型名称", - "configuration.anthropic.baseURL.markdownDescription": "Anthropic API URL,详见 [API 文档](https://docs.anthropic.com/en/api/getting-started)", "configuration.anthropic.apiKey.description": "Anthropic API 密钥", "configuration.anthropic.model.description": "模型名称", - "configuration.qianfan.apiKey.description": "Baidu Cloud API Key", "configuration.qianfan.apiKey.markdownDescription": "百度云的 API Key。参考 [创建应用程序](https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application)。", "configuration.qianfan.secretKey.description": "Baidu Cloud Secret Key.", "configuration.qianfan.secretKey.markdownDescription": "百度云的 Secret Key。", "configuration.qianfan.model.description": "模型名称", - "configuration.tongyi.apiKey.description": "阿里云的 API Key。", "configuration.tongyi.apiKey.markdownDescription": "阿里云的 API Key。参考 [开通 DashScope 并创建 API-KEY](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key).", "configuration.tongyi.model.description": "模型名称", "configuration.tongyi.enableSearch.description": "启用互联网搜索", - "configuration.zhipuai.apiKey.description": "智谱 AI 的 API Key。", "configuration.zhipuai.apiKey.markdownDescription": "智谱 AI 的 API Key。参考 [智谱 AI 开放平台](https://open.bigmodel.cn/dev/api).", "configuration.zhipuai.model.description": "模型名称", "configuration.zhipuai.embeddingModel.description": "向量化模型名称", - "configuration.ollama.baseURL.markdownDescription": "Ollama 的 API URL, Key。参考 [API Reference](https://github.com/ollama/ollama/blob/main/docs/api.md)", "configuration.ollama.model.description": "模型名称", - "configuration.transformers.remoteHost.description": "加载模型的主机 URL,默认为 Hugging Face Hub。", "configuration.transformers.remoteHost.markdownDescription": "加载模型的主机 URL,默认为 [Hugging Face Hub](https://huggingface.co),国内推荐走 https://hf-mirror.com 代理站点。", "configuration.transformers.remotePathTemplate.description": "加载模型时填写并合并到 remoteHost 的路径模板。", @@ -72,22 +59,20 @@ "configuration.transformers.model.markdownDescription": "设置嵌入模型名称。默认为 `all-MiniLM-L6-v2`。", "configuration.transformers.onnxWasmNumThreads.description": "设置 ONNX WebAssembly 运行时的线程数。默认为你的 CPU 数量。", "configuration.transformers.onnxLoglevel.description": "设置 ONNX 运行时的日志级别。默认为错误。", - "command.showChatPanel.title": "开始对话", "command.explainCode.title": "解释代码", "command.optimizeCode.title": "优化代码", "command.quickFix.title": "快速修复", "command.autoComment.title": "生成注释", - "command.AutoMethod.title":"填充当前方法", + "command.AutoMethod.title": "填充当前方法", "command.autoTest.title": "生成测试", - + "command.addFrameworkCodeFragment.title": "添加自定义代码块", + "command.addCodeSample.title": "添加自定义样例", "command.openSettings.title": "打开设置", "command.feedback.title": "意见反馈", - "command.genApiData.title": "生成 API 数据", - "command.codebase.createIndexes.title": "索引生成", "command.codebase.retrievalCode.title": "代码检索", - "autodev.WorkSpeace.customFrameworkCodeFiles.title":"自定义项目框架上下文代码文件组", - "autodev.WorkSpeace.customFrameworkCodeFiles.description":"提示:自定义项目框架上下文代码文件组" + "autodev.WorkSpeace.customFrameworkCodeFiles.title": "自定义项目框架上下文代码文件组", + "autodev.WorkSpeace.customFrameworkCodeFiles.description": "提示:自定义项目框架上下文代码文件组" } diff --git a/prompts/genius/zh-cn/code/auto-doc.vm b/prompts/genius/zh-cn/code/auto-doc.vm index b91d0e23..c0faf176 100644 --- a/prompts/genius/zh-cn/code/auto-doc.vm +++ b/prompts/genius/zh-cn/code/auto-doc.vm @@ -11,3 +11,8 @@ Here is User's code: ${context.code} Please write documentation for this code inside the Markdown code block. +#foreach($item in ${context.FrameworkCodeFragments}) + 自定义代码段说明:$item.doc + 自定义代码的上下文:$items.context + 自定义代码的代码实例:$items.code +#end diff --git a/src/AutoDevExtension.ts b/src/AutoDevExtension.ts index 89f9a5a8..7ceefe10 100644 --- a/src/AutoDevExtension.ts +++ b/src/AutoDevExtension.ts @@ -43,6 +43,11 @@ import { TemplateContext } from './prompt-manage/template/TemplateContext'; import { TemplateRender } from './prompt-manage/template/TemplateRender'; import { IProjectService } from './ProviderTypes'; import { ToolchainContextManager } from './toolchain-context/ToolchainContextManager'; +import { AddFrameworkCodeFragmentExecutor } from './action/addCodeFragment/AddFrameworkCodeFragmentExecutor'; +import { AddCodeSampleExecutor } from './action/AddCodeSample/AddCodeSampleExecutor'; +import { WorkspaceService } from 'base/common/workspace/WorkspaceService'; + + @injectable() @@ -93,6 +98,9 @@ export class AutoDevExtension { @inject(IProjectService) public teamTerm: TeamTermService, + @inject(WorkspaceService) + public workSpace: WorkspaceService, + ) { this.ideAction = new VSCodeAction(); this.statusBarManager = new AutoDevStatusManager(); @@ -229,6 +237,12 @@ export class AutoDevExtension { executeAutoMethodAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) { return new AutoMethodActionExecutor(this, document, nameElement, edit).execute(); } + executeAddCodeSampleExecutorAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) { + return new AddCodeSampleExecutor(this, document, nameElement, edit).execute(); + } + executeAddFrameworkCodeFragmentAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) { + return new AddFrameworkCodeFragmentExecutor(this, document, nameElement, edit).execute(); + } executeAutoTestAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) { return new AutoTestActionExecutor(this, document, nameElement, edit).execute(); diff --git a/src/action/AddCodeSample/AddCodeSampleExecutor.ts b/src/action/AddCodeSample/AddCodeSampleExecutor.ts new file mode 100644 index 00000000..8b2384b5 --- /dev/null +++ b/src/action/AddCodeSample/AddCodeSampleExecutor.ts @@ -0,0 +1,108 @@ +import fs from 'fs'; +import { AutoDevExtension } from 'src/AutoDevExtension'; +import { BaseCsharpElement } from 'src/code-context/csharp/model/BaseCsharpElement'; +import { CsharpClassExtractor } from 'src/code-context/csharp/model/CsharpClassExtractor'; +import { Position, TextDocument, WorkspaceEdit } from 'vscode'; +import vscode from 'vscode'; +import { SyntaxNode } from 'web-tree-sitter'; + +import { ChatMessageRole, IChatMessage } from 'base/common/language-models/languageModels'; +import { LanguageModelsService } from 'base/common/language-models/languageModelsService'; +import { LANGUAGE_BLOCK_COMMENT_MAP } from 'base/common/languages/docstring'; +import { log } from 'base/common/log/log'; +import { MarkdownTextProcessor } from 'base/common/markdown/MarkdownTextProcessor'; +import { StreamingMarkdownCodeBlock } from 'base/common/markdown/StreamingMarkdownCodeBlock'; +import { IDataStorage } from 'base/common/workspace/WorkspaceService'; + +import { type NamedElement } from '../../editor/ast/NamedElement'; +import { insertCodeByRange, selectCodeInRange } from '../../editor/ast/PositionUtil'; +import { AutoDevStatus, AutoDevStatusManager } from '../../editor/editor-api/AutoDevStatusManager'; +import { ActionType } from '../../prompt-manage/ActionType'; +import { PromptManager } from '../../prompt-manage/PromptManager'; +import { CreateToolchainContext } from '../../toolchain-context/ToolchainContextProvider'; +import { ActionExecutor } from '../_base/ActionExecutor'; + +export class AddCodeSampleExecutor implements ActionExecutor { + type: ActionType = ActionType.AutoDoc; + + private lm: LanguageModelsService; + private promptManager: PromptManager; + private statusBarManager: AutoDevStatusManager; + + private document: TextDocument; + private range: NamedElement; + private edit?: WorkspaceEdit; + private language: string; + private autodev: AutoDevExtension; + + constructor(autodev: AutoDevExtension, document: TextDocument, range: NamedElement, edit?: WorkspaceEdit) { + this.lm = autodev.lm; + this.promptManager = autodev.promptManager; + this.statusBarManager = autodev.statusBarManager; + + this.document = document; + this.range = range; + this.edit = edit; + this.language = document.languageId; + this.autodev = autodev; + } + + async execute() { + const document = this.document; + const range = this.range; + const language = document.languageId; + + const startSymbol = LANGUAGE_BLOCK_COMMENT_MAP[language]!.start; + const endSymbol = LANGUAGE_BLOCK_COMMENT_MAP[language]!.end; + const classInfo = new CsharpClassExtractor(this.range.node).ExtractClass(); + this.statusBarManager.setStatus(AutoDevStatus.InProgress); + + selectCodeInRange(range.blockRange.start, range.blockRange.end); + if (range.commentRange) { + selectCodeInRange(range.commentRange.start, range.commentRange.end); + } + this.autodev.workSpace.AddDataStorage(language,new CodeSample(range.node, document.uri.fsPath)); + } + + // 在vscode插件开发下,写一个类,将对象转json字符串并保存的代码要求如下 + // 1.当工作区关闭时,json字符串保存在workspace的.vscode文件夹下,名称为当前代码文件的编程语言名外接.json后缀 + // 2.当工作区打开时,读取保存在workspace的.vscode文件夹下的名称为当前代码文件的编程语言名的json文件,并将其反序列化为特定对象 +} +export class CodeSample extends BaseCsharpElement implements IDataStorage { + public readonly code: string; + public readonly doc: string; + public readonly context: string; + public readonly filePath: string; + constructor(node: SyntaxNode, filePath: string) { + super(); + this.code = node.text; + this.doc = ''; + if (node.previousSibling) { + this.doc = this.Getcommits(node.previousSibling, []).reverse().toString(); + } + + this.context = 'context'; + this.filePath = filePath; + } + equals(other: CodeSample): boolean { + if ( + other.code === this.code && + other.doc === this.doc && + other.context === this.context && + other.filePath === this.filePath + ) { + return true; + } + + return false; + } + GetType(): string { + return CodeSample.name; + } + Save(): void { + throw new Error('Method not implemented.'); + } + Load(): void { + throw new Error('Method not implemented.'); + } +} diff --git a/src/action/addCodeFragment/AddFrameworkCodeFragmentExecutor.ts b/src/action/addCodeFragment/AddFrameworkCodeFragmentExecutor.ts new file mode 100644 index 00000000..78ac4420 --- /dev/null +++ b/src/action/addCodeFragment/AddFrameworkCodeFragmentExecutor.ts @@ -0,0 +1,64 @@ +import fs from 'fs'; +import { AutoDevExtension } from 'src/AutoDevExtension'; +import { Position, TextDocument, WorkspaceEdit } from 'vscode'; +import vscode from 'vscode'; + +import { ChatMessageRole, IChatMessage } from 'base/common/language-models/languageModels'; +import { LanguageModelsService } from 'base/common/language-models/languageModelsService'; +import { LANGUAGE_BLOCK_COMMENT_MAP } from 'base/common/languages/docstring'; +import { log } from 'base/common/log/log'; +import { MarkdownTextProcessor } from 'base/common/markdown/MarkdownTextProcessor'; +import { StreamingMarkdownCodeBlock } from 'base/common/markdown/StreamingMarkdownCodeBlock'; + +import { type NamedElement } from '../../editor/ast/NamedElement'; +import { insertCodeByRange, selectCodeInRange } from '../../editor/ast/PositionUtil'; +import { AutoDevStatus, AutoDevStatusManager } from '../../editor/editor-api/AutoDevStatusManager'; +import { ActionType } from '../../prompt-manage/ActionType'; +import { PromptManager } from '../../prompt-manage/PromptManager'; +import { CreateToolchainContext } from '../../toolchain-context/ToolchainContextProvider'; +import { ActionExecutor } from '../_base/ActionExecutor'; +import { CsharpClassExtractor } from 'src/code-context/csharp/model/CsharpClassExtractor'; +import { FrameworkCodeFragmentExtractor } from 'src/code-context/csharp/model/FrameworkCodeFragmentExtractor'; + +export class AddFrameworkCodeFragmentExecutor implements ActionExecutor { + type: ActionType = ActionType.AutoDoc; + + private lm: LanguageModelsService; + private promptManager: PromptManager; + private statusBarManager: AutoDevStatusManager; + + private document: TextDocument; + private range: NamedElement; + private edit?: WorkspaceEdit; + private language: string; + private autodev: AutoDevExtension; + + constructor(autodev: AutoDevExtension, document: TextDocument, range: NamedElement, edit?: WorkspaceEdit) { + this.lm = autodev.lm; + this.promptManager = autodev.promptManager; + this.statusBarManager = autodev.statusBarManager; + + this.document = document; + this.range = range; + this.edit = edit; + this.language = document.languageId; + this.autodev = autodev; + } + + async execute() { + + const document = this.document; + const range = this.range; + const language = document.languageId; + + const startSymbol = LANGUAGE_BLOCK_COMMENT_MAP[language]!.start; + const endSymbol = LANGUAGE_BLOCK_COMMENT_MAP[language]!.end; + const frameworkCodeFragmentInfo=new FrameworkCodeFragmentExtractor(range.node,document.uri.fsPath).ExtractFrameworkCodeFragment(); + this.autodev.workSpace.AddDataStorage(language,frameworkCodeFragmentInfo) + this.statusBarManager.setStatus(AutoDevStatus.InProgress); + selectCodeInRange(range.blockRange.start, range.blockRange.end); + if (range.commentRange) { + selectCodeInRange(range.commentRange.start, range.commentRange.end); + } + } +} diff --git a/src/action/autoMethod/AutoMethodActionExecutor.ts b/src/action/autoMethod/AutoMethodActionExecutor.ts index d55c1719..4dcfb5cd 100644 --- a/src/action/autoMethod/AutoMethodActionExecutor.ts +++ b/src/action/autoMethod/AutoMethodActionExecutor.ts @@ -1,7 +1,6 @@ import fs from 'fs'; import { AutoDevExtension } from 'src/AutoDevExtension'; -import { CsharpClassExtractor } from 'src/code-context/csharp/model/CsharpClassExtractor'; -import { CancellationTokenSource, Position, TextDocument, WorkspaceEdit } from 'vscode'; +import { Position, TextDocument, WorkspaceEdit } from 'vscode'; import vscode from 'vscode'; import { ChatMessageRole, IChatMessage } from 'base/common/language-models/languageModels'; @@ -18,8 +17,11 @@ import { ActionType } from '../../prompt-manage/ActionType'; import { PromptManager } from '../../prompt-manage/PromptManager'; import { CreateToolchainContext } from '../../toolchain-context/ToolchainContextProvider'; import { ActionExecutor } from '../_base/ActionExecutor'; -import { AutoDocTemplateContext } from '../autodoc/AutoDocTemplateContext'; import { AutoMethodTemplateContext } from './AutoMethodTemplateContext'; +import { CsharpClassExtractor } from 'src/code-context/csharp/model/CsharpClassExtractor'; +import { FrameworkCodeFragment } from 'src/code-context/csharp/model/FrameworkCodeFragmentExtractor'; +import { CodeSample } from '../AddCodeSample/AddCodeSampleExecutor'; +import { MethodInfo } from 'src/code-context/csharp/model/MethodInfo'; export class AutoMethodActionExecutor implements ActionExecutor { type: ActionType = ActionType.AutoDoc; @@ -47,33 +49,16 @@ export class AutoMethodActionExecutor implements ActionExecutor { } async execute() { + const document = this.document; const range = this.range; const language = document.languageId; const startSymbol = LANGUAGE_BLOCK_COMMENT_MAP[language]!.start; const endSymbol = LANGUAGE_BLOCK_COMMENT_MAP[language]!.end; - //帮忙,看一下这里 - if (this.language == 'csharp') { - let csharpClassExtractor = new CsharpClassExtractor(); - csharpClassExtractor.Init(document, this.autodev); - let classInfo = csharpClassExtractor.extractClassInfo(); - } - - const config = vscode.workspace.getConfiguration('autodev.WorkSpeace'); - const customFrameworkCodeFilesPath = config.get('customFrameworkCodeFiles', []); - let customFrameworkCodeFileContext: string = ''; - if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { - const workspaceFolder = vscode.workspace.workspaceFolders[0]; - const workspacePath = workspaceFolder.uri.fsPath; - if (customFrameworkCodeFilesPath.length > 0) { - for (let i = 0; i < customFrameworkCodeFilesPath.length; i++) { - let codeFileNormalPath = workspacePath + '\\' + customFrameworkCodeFilesPath[i]; - customFrameworkCodeFileContext += fs.readFileSync(codeFileNormalPath).toString(); - customFrameworkCodeFileContext += '\n'; - } - } - } + const classExtractor=new CsharpClassExtractor(this.range.node) + const classInfo=classExtractor.ExtractClass(); + let unfinishedMethods: MethodInfo[]=[]; const templateContext: AutoMethodTemplateContext = { language: language, @@ -81,14 +66,11 @@ export class AutoMethodActionExecutor implements ActionExecutor { endSymbol: endSymbol, code: document.getText(range.blockRange), forbiddenRules: [], - // 原有代码块 - originalMethodCodes: [], - customFrameworkCodeFileContext, + classInfo:classInfo, + customFrameworkCodeFragments:this.autodev.workSpace.GetDataStorage(language,FrameworkCodeFragment.name) as FrameworkCodeFragment[], + codeSamples:this.autodev.workSpace.GetDataStorage(language,CodeSample.name) as CodeSample[] }; - if (range.commentRange) { - templateContext.originalMethodCodes.push(document.getText(range.commentRange)); - } this.statusBarManager.setStatus(AutoDevStatus.InProgress); diff --git a/src/action/autoMethod/AutoMethodTemplateContext.ts b/src/action/autoMethod/AutoMethodTemplateContext.ts index b85548ce..f533fd66 100644 --- a/src/action/autoMethod/AutoMethodTemplateContext.ts +++ b/src/action/autoMethod/AutoMethodTemplateContext.ts @@ -1,75 +1,19 @@ import { Interface } from 'readline'; import { TemplateContext } from '../../prompt-manage/template/TemplateContext'; +import { ClassInfo } from 'src/code-context/csharp/model/CsharpClassExtractor'; +import { FrameworkCodeFragment } from 'src/code-context/csharp/model/FrameworkCodeFragmentExtractor'; +import { CodeSample } from '../AddCodeSample/AddCodeSampleExecutor'; +import { MethodInfo } from 'src/code-context/csharp/model/MethodInfo'; + export interface AutoMethodTemplateContext extends TemplateContext { startSymbol: string; endSymbol: string; code: string; forbiddenRules: string[]; - originalMethodCodes: string[]; - customFrameworkCodeFileContext?: string; - classInfo?: ClassInfo; - completedMethodInfo?: CompletedMethodInfo[]; + classInfo?: ClassInfo|null; classDescriptionInfo?: string; - classMemberVariable?: string[]; - classMemberMethodInfo?: string; - customFrameworkCodeFragments?: FrameworkCodeFragment[]; -} -/** - * 自定义框架代码片段 - */ -export interface FrameworkCodeFragment { - doc: string; - context: string; - code: string; -} -/** - * - * - **/ -export interface CompletedMethodInfo { - doc: string; - context: string; - code: string; - paramters: Parameter[]; -} - -export interface NeedCompletedMethodInfo { - name: string; - accessModifier: string; - returnValueType: string; - purpose: string; - paramters: Parameter[]; -} -export interface Parameter { - name: string; - type: string; - doc: string; -} -export interface ClassInfo { - name: string; - nameSpace: string; - purpose: string; - doc: string; - memberVariables: MemberVariableInfo[]; - memberMethods: MemberMethod[]; -} - -export interface MemberVariableInfo { - name: string; - doc: string; - type: string; -} -export interface MemberMethod { - name: string; - doc: string; - returnType: string; - parameterInfos: ParameterInfo[]; -} - -export interface ParameterInfo { - name: string; - doc: string; - type: string; + codeSamples?:CodeSample[] + customFrameworkCodeFragments?: FrameworkCodeFragment[]|null; } diff --git a/src/action/autodoc/AutoDocActionCreator.ts b/src/action/autodoc/AutoDocActionCreator.ts index 1b08335b..91ee498a 100644 --- a/src/action/autodoc/AutoDocActionCreator.ts +++ b/src/action/autodoc/AutoDocActionCreator.ts @@ -28,6 +28,7 @@ export class AutoDocActionCreator extends CodeActionCreator { codeAction.isPreferred = false; codeAction.edit = new vscode.WorkspaceEdit(); + codeAction.command = { command: 'autodev.autoComment', title: title, diff --git a/src/action/providers/AutoDevCodeLensProvider.ts b/src/action/providers/AutoDevCodeLensProvider.ts index 5b7d9b61..e4098dbf 100644 --- a/src/action/providers/AutoDevCodeLensProvider.ts +++ b/src/action/providers/AutoDevCodeLensProvider.ts @@ -28,6 +28,8 @@ import { CMD_CODELENS_SHOW_CUSTOM_ACTION, CMD_SHOW_CODELENS_DETAIL_QUICKPICK, CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS, + CMD_CODELENS_SHOW_CODE_ADD_FRAMEWORK_CODE_FRAGMENT, + CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE } from 'base/common/configuration/configuration'; import { ConfigurationService } from 'base/common/configuration/configurationService'; import { isFileTooLarge } from 'base/common/files/files'; @@ -38,7 +40,8 @@ import { logger } from 'base/common/log/log'; import { type AutoDevExtension } from '../../AutoDevExtension'; import { CodeElementType } from 'src/editor/codemodel/CodeElementType'; -type CodeLensItemType = 'quickChat' | 'explainCode' | 'optimizeCode' | 'autoComment' | 'autoTest' | 'customAction'|'AutoMethod'; +type CodeLensItemType = 'quickChat' | 'explainCode' | 'optimizeCode' | 'autoComment' |'addCodeSample'| + 'autoTest' | 'customAction'|'AutoMethod'|'addFrameworkCodeFragment'; export class AutoDevCodeLensProvider implements CodeLensProvider { private config: ConfigurationService; @@ -128,6 +131,14 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { commands.registerCommand(CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS, (document: TextDocument, nameElement: NamedElement) => { autodev.executeAutoMethodAction(document, nameElement); }), + commands.registerCommand(CMD_CODELENS_SHOW_CODE_ADD_FRAMEWORK_CODE_FRAGMENT, (document: TextDocument, nameElement: NamedElement) => { + autodev.executeAddFrameworkCodeFragmentAction(document, nameElement); + }), + commands.registerCommand(CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE, (document: TextDocument, nameElement: NamedElement) => { + autodev.executeAddCodeSampleExecutorAction(document, nameElement); + }), + + ]; } @@ -281,6 +292,30 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { continue; } + if (type === 'addCodeSample') { + if (element.codeElementType==CodeElementType.Method||element.codeElementType==CodeElementType.Structure) { + codelenses.push( + new CodeLens(element.identifierRange, { + title: l10n.t('addCodeSample'), + command: CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE, + arguments: [document, element], + }), + ); + } + continue; + } + if (type === 'addFrameworkCodeFragment') { + if (element.codeElementType==CodeElementType.Method||element.codeElementType==CodeElementType.Structure) { + codelenses.push( + new CodeLens(element.identifierRange, { + title: l10n.t('addFrameworkCodeFragment'), + command: CMD_CODELENS_SHOW_CODE_ADD_FRAMEWORK_CODE_FRAGMENT, + arguments: [document, element], + }), + ); + } + continue; + } } result.push(codelenses); diff --git a/src/base/common/configuration/configuration.ts b/src/base/common/configuration/configuration.ts index 18c6947f..dfb09a35 100644 --- a/src/base/common/configuration/configuration.ts +++ b/src/base/common/configuration/configuration.ts @@ -18,7 +18,7 @@ export const CMD_FIX_THIS = 'autodev.fixThis'; export const CMD_EXPLAIN_CODE = 'autodev.explainCode'; export const CMD_OPTIMIZE_CODE = 'autodev.optimizeCode'; export const CMD_GEN_DOCSTRING = 'autodev.autoComment'; -export const CMD_GEN_CODE_METHOD_COMPLETIONS= 'autodev.methodCompletions'; +export const CMD_GEN_CODE_METHOD_COMPLETIONS = 'autodev.methodCompletions'; export const CMD_CREATE_UNIT_TEST = 'autodev.autoTest'; // Codelens Commands @@ -28,8 +28,9 @@ export const CMD_CODELENS_OPTIMIZE_CODE = 'autodev.codelens.optimizeCode'; export const CMD_CODELENS_GEN_DOCSTRING = 'autodev.codelens.autoComment'; export const CMD_CODELENS_CREATE_UNIT_TEST = 'autodev.codelens.autoTest'; export const CMD_CODELENS_SHOW_CUSTOM_ACTION = 'autodev.codelens.customAction'; -export const CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS= 'autodev.codelens.methodCompletions'; - +export const CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS = 'autodev.codelens.methodCompletions'; +export const CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE = 'autodev.codelen.addCodeSample'; +export const CMD_CODELENS_SHOW_CODE_ADD_FRAMEWORK_CODE_FRAGMENT = 'autodev.codelen.addFrameworkCodeFragment'; // Chat Commands export const CMD_SHOW_CHAT_PANEL = 'autodev.showChatPanel'; export const CMD_QUICK_CHAT = 'autodev.quickChat'; diff --git a/src/base/common/workspace/WorkspaceSerializer.ts b/src/base/common/workspace/WorkspaceSerializer.ts new file mode 100644 index 00000000..001d5fc5 --- /dev/null +++ b/src/base/common/workspace/WorkspaceSerializer.ts @@ -0,0 +1,89 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as vscode from 'vscode'; + +class WorkspaceSerializer { + private document: vscode.TextDocument; + constructor(document: vscode.TextDocument) { + this.document=document; + } + + /** + * 将对象序列化为 JSON 字符串并保存到指定路径 + * @param obj 要序列化的对象 + * @param filePath 保存 JSON 文件的路径 + */ + private saveJsonToFile(obj: any, filePath: string): void { + const jsonString = JSON.stringify(obj, null, 2); + fs.writeFileSync(filePath, jsonString, 'utf8'); + } + + /** + * 从指定路径读取 JSON 文件并反序列化为对象 + * @param filePath 读取 JSON 文件的路径 + * @returns 反序列化后的对象 + */ + private loadJsonFromFile(filePath: string): T | null { + if (fs.existsSync(filePath)) { + const jsonString = fs.readFileSync(filePath, 'utf8'); + return JSON.parse(jsonString) as T; + } + return null; + } + + /** + * 获取当前代码文件的编程语言名 + * @returns 当前代码文件的编程语言名 + */ + private getCurrentLanguage(): string | undefined { + return this.document.languageId; + } + + /** + * 获取保存 JSON 文件的路径 + * @returns 保存 JSON 文件的路径 + */ + private getJsonFilePath(language:string,fileName: string): string | undefined { + if (language) { + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (workspaceFolder) { + const filePath = `${language}/${fileName}.json`; + const fileDir=path.join(workspaceFolder.uri.fsPath, '.vscode', `${language}`); + if(fs.existsSync(fileDir)) + { + return path.join(workspaceFolder.uri.fsPath, '.vscode', filePath); + } + fs.mkdirSync(fileDir) + return path.join(workspaceFolder.uri.fsPath, '.vscode', filePath); + } + } + return undefined; + } + + /** + * 保存对象到 JSON 文件 + * @param obj 要保存的对象 + */ + public saveObject(obj: any, fileName: string,language:string): void { + if(obj==null) + return; + const filePath = this.getJsonFilePath(language,fileName); + if (filePath) { + this.saveJsonToFile(obj, filePath); + } + } + + /** + * 从 JSON 文件加载对象 + * @returns 加载的对象 + */ + public loadObject(language:string,fileName: string): T | null { + const filePath = this.getJsonFilePath(language,fileName); + if (filePath) { + return this.loadJsonFromFile(filePath); + } + return null; + } +} + +export default WorkspaceSerializer; diff --git a/src/base/common/workspace/WorkspaceService.ts b/src/base/common/workspace/WorkspaceService.ts new file mode 100644 index 00000000..3b87e5c9 --- /dev/null +++ b/src/base/common/workspace/WorkspaceService.ts @@ -0,0 +1,174 @@ +import os from 'node:os'; +import path from 'node:path'; + +import { inject, injectable } from 'inversify'; +import _, { forEach } from 'lodash'; +import { data } from 'node_modules/cheerio/dist/commonjs/api/attributes'; +import { CodeSample } from 'src/action/AddCodeSample/AddCodeSampleExecutor'; +import { FrameworkCodeFragment } from 'src/code-context/csharp/model/FrameworkCodeFragmentExtractor'; +import { Disposable, type Event, EventEmitter, TextDocument, Uri, workspace } from 'vscode'; + +import { IExtensionContext } from '../configuration/context'; +import WorkspaceSerializer from './WorkspaceSerializer'; + +type DocDealCallback = (document: TextDocument) => void; + +@injectable() +export class WorkspaceService { + protected readonly _disposables: Disposable[]; + private readonly _listenerMap: Map; + private readonly _saveDataMap: Map>; + constructor( + @inject(IExtensionContext) + private readonly extensionContext: IExtensionContext, + ) { + this._saveDataMap = new Map>(); + this._disposables = []; + this._disposables.push(); + this._listenerMap = new Map(); + + workspace.onDidCloseTextDocument(document => { + let language = document.languageId; + let workspaceSerializer = new WorkspaceSerializer(document); + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + if (languageDataStorageMap != undefined) { + let dataStorages = languageDataStorageMap.get('CodeSample'); + if (dataStorages != undefined) workspaceSerializer.saveObject(dataStorages, 'CodeSample', language); + console.log('test'); + } + } + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + if (languageDataStorageMap != undefined) { + let dataStorages = languageDataStorageMap.get('FrameworkCodeFragment'); + if (dataStorages != undefined) + workspaceSerializer.saveObject(dataStorages, 'FrameworkCodeFragment', language); + console.log('test'); + } + } + }); + workspace.onDidOpenTextDocument(document => { + let language = document.languageId; + let workspaceSerializer = new WorkspaceSerializer(document); + let codeSamples = workspaceSerializer.loadObject(language, 'CodeSample'); + if (codeSamples != null) { + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + languageDataStorageMap?.set('CodeSample', codeSamples); + } else { + let languageDataStorageMap = new Map(); + languageDataStorageMap.set('CodeSample', codeSamples); + this._saveDataMap.set(language, languageDataStorageMap); + } + } + let frameworkCodeFragments = workspaceSerializer.loadObject( + language, + 'FrameworkCodeFragment', + ); + if (frameworkCodeFragments != null) { + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + languageDataStorageMap?.set('FrameworkCodeFragment', frameworkCodeFragments); + } else { + let languageDataStorageMap = new Map(); + languageDataStorageMap.set('FrameworkCodeFragment', frameworkCodeFragments); + this._saveDataMap.set(language, languageDataStorageMap); + } + } + }); + } + dispose(): void { + Disposable.from(...this._disposables).dispose(); + this._disposables.length = 0; + this._listenerMap.clear(); + this._saveDataMap.clear(); + } + public AddOpenListener(method: DocDealCallback) { + const disposable = workspace.onDidOpenTextDocument(document => { + method(document); + }); + this._disposables.push(disposable); + this._listenerMap.set(method, disposable); + } + + public RemoveOpenListener(method: DocDealCallback) { + const disposable = this._listenerMap.get(method); + if (disposable) { + disposable.dispose(); + this._disposables.splice(this._disposables.indexOf(disposable), 1); + this._listenerMap.delete(method); + } + } + public AddCloseListener(method: DocDealCallback) { + const disposable = workspace.onDidCloseTextDocument(document => { + method(document); + }); + this._disposables.push(disposable); + this._listenerMap.set(method, disposable); + } + + public RemoveCloseListener(method: DocDealCallback) { + const disposable = this._listenerMap.get(method); + if (disposable) { + disposable.dispose(); + this._disposables.splice(this._disposables.indexOf(disposable), 1); + this._listenerMap.delete(method); + } + } + public AddDataStorage(language: string, dataStorage: IDataStorage) { + let key = dataStorage.GetType(); + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + if (languageDataStorageMap != undefined) { + let dataStorages = languageDataStorageMap?.get(key); + let dataEqualed = dataStorages?.filter(item => { + return dataStorage.equals(item) == true; + }); + if (dataEqualed != undefined) { + if (dataEqualed.length > 0) return; + } + dataStorages?.push(dataStorage); + } else { + languageDataStorageMap = new Map(); + let dataStorages:IDataStorage[]=[] + dataStorages.push(dataStorage) + languageDataStorageMap.set(key,dataStorages); + } + } else { + const dataStorages: IDataStorage[] = []; + dataStorages.push(dataStorage); + let dataDataStorageMap = new Map(); + dataDataStorageMap.set(key, dataStorages); + this._saveDataMap.set(language, dataDataStorageMap); + } + } + public GetDataStorage(language: string, key: string): IDataStorage[] | undefined { + let dataDataStorageMap = this._saveDataMap.get(language); + if (dataDataStorageMap) { + let datas = dataDataStorageMap.get(key); + return datas; + } else { + return undefined; + } + } + public RemoveDataStorage(language: string, key: string, dataStorage: IDataStorage) { + if (!this._saveDataMap.has(language)) return; + let languageDataStorageMap = this._saveDataMap.get(language); + let dataStorages = languageDataStorageMap?.get(key); + let newData = dataStorages?.reduce((acc, item) => { + if (dataStorage.equals(item) !== false) { + acc.push(item); + } + return acc; + }, [] as IDataStorage[]); + if (newData) languageDataStorageMap?.set(key, newData); + } +} + +export interface IDataStorage { + Save(): void; + Load(): void; + GetType(): string; + equals(t: IDataStorage): boolean; +} diff --git a/src/code-context/csharp/model/BaseCsharpElement.ts b/src/code-context/csharp/model/BaseCsharpElement.ts new file mode 100644 index 00000000..3ebbffef --- /dev/null +++ b/src/code-context/csharp/model/BaseCsharpElement.ts @@ -0,0 +1,18 @@ +import Parser from 'web-tree-sitter'; + +export abstract class BaseCsharpElement { + + public constructor() {} + protected Getcommits(node: Parser.SyntaxNode, commits: string[]): string[] { + if (node.type === 'comment') { + commits.push(node.text); + if (node.previousSibling) { + return this.Getcommits(node.previousSibling, commits); + } else { + return commits; + } + } else { + return commits; + } + } +} diff --git a/src/code-context/csharp/model/CodeSampleExtractor.ts b/src/code-context/csharp/model/CodeSampleExtractor.ts new file mode 100644 index 00000000..08a30f6d --- /dev/null +++ b/src/code-context/csharp/model/CodeSampleExtractor.ts @@ -0,0 +1,50 @@ +import { SyntaxNode } from 'web-tree-sitter'; + +import { BaseCsharpElement } from './BaseCsharpElement'; + +type DocDealCallback = (result: string) => string; +export class CodeSampleExtractor { + private codeSampleNode: SyntaxNode; + private filePath: string; + constructor(codeSampleNode: SyntaxNode, filePath: string) { + this.codeSampleNode = codeSampleNode; + this.filePath = filePath; + } + public ExtractCodeSample(): CodeSample { + let context: string = ''; + let code: string = this.codeSampleNode.text; + if (this.codeSampleNode.type === 'class_declaration') { + + } else if (this.codeSampleNode.type === 'method_declaration') { + } + return new CodeSample(this.codeSampleNode, context, code, this.filePath, (doc: string) => { + let match = doc.match('(.*?)/g'); + if (match) { + if (match.groups) { + return match.groups[0]; + } + } + return ''; + }); + } +} + +export class CodeSample extends BaseCsharpElement { + doc: string; + context: string; + code: string; + filePath: string; + public constructor( + frameworkCodeFragmentNode: SyntaxNode, + context: string, + code: string, + filePath: string, + docDealCallback: DocDealCallback, + ) { + super(); + this.doc = docDealCallback(this.Getcommits(frameworkCodeFragmentNode, []).toString()); + this.context = context; + this.code = code; + this.filePath = filePath; + } +} diff --git a/src/code-context/csharp/model/CsharpClassExtractor.ts b/src/code-context/csharp/model/CsharpClassExtractor.ts index 04c9e2c0..78b5cbb1 100644 --- a/src/code-context/csharp/model/CsharpClassExtractor.ts +++ b/src/code-context/csharp/model/CsharpClassExtractor.ts @@ -1,37 +1,123 @@ -import { AutoDevExtension } from 'src/AutoDevExtension'; -import { TreeSitterFile } from 'src/code-context/ast/TreeSitterFile'; -import { TreeSitterFileManager } from 'src/editor/cache/TreeSitterFileManager'; -import vscode, { TextDocumentChangeEvent, Uri } from 'vscode'; +import { l10n } from 'vscode'; +import Parser, { SyntaxNode } from 'web-tree-sitter'; -import { ILanguageServiceProvider } from 'base/common/languages/languageService'; - -import { CsharpFieldExtractor, FieldInfo } from './CsharpFieldExtractor'; -import { CsharpMethodExtractor, MethodInfo } from './CsharpMethodExtractor'; +import { FieldInfo } from './FieldInfo'; +import { MethodInfo } from './MethodInfo'; +import { PropertyInfo } from './PropertyInfo'; export class CsharpClassExtractor { - private fieldExtractor?: CsharpFieldExtractor; - private methodExtractor?: CsharpMethodExtractor; + private classNode: SyntaxNode; - constructor() {} - public async Init(text: vscode.TextDocument, autoDev: AutoDevExtension) { - let parser = await autoDev.treeSitterFileManager.create(text); - this.fieldExtractor = new CsharpFieldExtractor(text, parser); - this.methodExtractor = new CsharpMethodExtractor(text, parser); + constructor(classNode: SyntaxNode) { + this.classNode = classNode; } - public extractClassInfo(): ClassInfo | null { - if (this.fieldExtractor == null || this.methodExtractor == null) return null; - const fields = this.fieldExtractor.extractFields(); - const methods = this.methodExtractor.extractMethods(); + public ExtractClass(): ClassInfo | null { + return this.traverse(this.classNode); + } - return { - fields, - methods, - }; + private traverse(classNode: Parser.SyntaxNode): ClassInfo { + const methodInfos: MethodInfo[] = []; + const propertyInfos: PropertyInfo[] = []; + const fieldInfos: FieldInfo[] = []; + let className: string = ''; + let fatherClassName: string = ''; + let classXmlDoc = ''; + if (classNode.type === 'class_declaration') { + classXmlDoc = this.getClassXmlDoc(classNode); + className = this.getClassName(classNode); + fatherClassName = this.getFatherClassName(classNode); + for (const child of classNode.children) { + if (child.type === 'declaration_list') { + for (const childLevel1 of child.children) { + if (childLevel1.type === 'field_declaration') { + const fieldInfo: FieldInfo = new FieldInfo(childLevel1); + fieldInfos.push(fieldInfo); + } else if (childLevel1.type === 'method_declaration') { + const methodInfo: MethodInfo = new MethodInfo(childLevel1); + methodInfos.push(methodInfo); + } else if (childLevel1.type === 'property_declaration') { + const propertyInfo: PropertyInfo = new PropertyInfo(childLevel1); + propertyInfos.push(propertyInfo); + } + } + } + } + } + const classInfo = new ClassInfo(className, fatherClassName, classXmlDoc, fieldInfos, methodInfos, propertyInfos); + return classInfo; + } + + // 获取类的 XML 注释 + private getClassXmlDoc(classNode: Parser.SyntaxNode): string { + const xmlDocNode = classNode.previousSibling; + if (xmlDocNode && xmlDocNode.type === 'comment') { + const commits: string[] = []; + this.getcommits(xmlDocNode, commits); + return commits.toString(); + } + return ''; + } + + private getcommits(node: Parser.SyntaxNode, commits: string[]): string[] { + if (node.type === 'comment') { + commits.push(node.text); + if (node.previousSibling) { + return this.getcommits(node.previousSibling, commits); + } else { + return commits; + } + } else { + return commits; + } + } + private getClassName(node: Parser.SyntaxNode): string { + if (node.type === 'identifier') { + return node.text; + } + return ''; + } + private getFatherClassName(node: Parser.SyntaxNode): string { + if (node.type === 'base_list') { + return node.text; + } + return ''; } } +export class ClassInfo { + name: string; + fatherName?: string; + xmlDoc?: string; + fields?: FieldInfo[]; + unfinishedMethods?: MethodInfo[]; + completedMethods?: MethodInfo[]; + propertyInfos?: PropertyInfo[]; + constructor( + className: string, + fatherName?: string, + xmlDoc?: string, + fields?: FieldInfo[], + methods?: MethodInfo[], + propertyInfos?: PropertyInfo[], + ) { + this.name = className; + this.fatherName = fatherName; + this.fields = fields; + this.propertyInfos = propertyInfos; + this.xmlDoc = xmlDoc; + this.unfinishedMethods = []; + this.completedMethods = []; + if (methods) { + for (let item of methods) { + if(item.methodXmlDoc.includes(l10n.t('Not Completed'))) + { + this.unfinishedMethods.push(item); + }else + { + this.completedMethods.push(item); + } + } + } -export interface ClassInfo { - fields: FieldInfo[]; - methods: MethodInfo[]; + } } diff --git a/src/code-context/csharp/model/CsharpFieldExtractor.ts b/src/code-context/csharp/model/CsharpFieldExtractor.ts deleted file mode 100644 index 2d89c785..00000000 --- a/src/code-context/csharp/model/CsharpFieldExtractor.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { LanguageProfile, MemoizedQuery } from 'src/code-context/_base/LanguageProfile'; -import { TreeSitterFile } from 'src/code-context/ast/TreeSitterFile'; -import { TreeSitterFileManager } from 'src/editor/cache/TreeSitterFileManager'; -import vscode, { TextDocumentChangeEvent, Uri } from 'vscode'; -import Parser from 'web-tree-sitter'; - -import { ILanguageServiceProvider } from 'base/common/languages/languageService'; - -export class CsharpFieldExtractor { - private parser: TreeSitterFile; - private src: vscode.TextDocument; - - constructor(text: vscode.TextDocument, Parser: TreeSitterFile) { - this.src = text; - this.parser = Parser; - } - - public extractFields(): FieldInfo[] { - const tree = this.parser.tree; - const queryObj = new MemoizedQuery(` - (field_declaration - (attribute_list)? @attribute - (modifier)* @modifier - (variable_declaration - (variable_declarator - (identifier) @name - ) - (type) @type - ) - (comment)? @comment - ) - `); - const query = queryObj.query(this.parser.tsLanguage); - const fields: FieldInfo[] = []; - const matches = query.matches(tree.rootNode); - for (const match of matches) { - const field: FieldInfo = { - attributes: this.extractAttributes(match), - modifiers: this.extractModifiers(match), - type: this.extractType(match), - name: this.extractName(match), - comment: this.extractComment(match) - }; - fields.push(field); - } - - return fields; - } - - private extractAttributes(match: Parser.QueryMatch): string[] { - return match.captures.filter(capture => capture.name === 'attribute').map(capture => capture.node.text); - } - - private extractModifiers(match: Parser.QueryMatch): string[] { - return match.captures.filter(capture => capture.name === 'modifier').map(capture => capture.node.text); - } - - private extractType(match: Parser.QueryMatch): string { - const capture = match.captures.find(capture => capture.name === 'type'); - return capture ? capture.node.text : ''; - } - - private extractName(match: Parser.QueryMatch): string { - const capture = match.captures.find(capture => capture.name === 'name'); - return capture ? capture.node.text : ''; - } - - private extractComment(match: Parser.QueryMatch): string { - const capture = match.captures.find(capture => capture.name === 'comment'); - return capture ? capture.node.text : ''; - } -} - -export interface FieldInfo { - attributes: string[]; - modifiers: string[]; - type: string; - name: string; - comment: string; -} diff --git a/src/code-context/csharp/model/CsharpMethodExtractor.ts b/src/code-context/csharp/model/CsharpMethodExtractor.ts deleted file mode 100644 index 8a3ea5b4..00000000 --- a/src/code-context/csharp/model/CsharpMethodExtractor.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { ILanguageServiceProvider } from "base/common/languages/languageService"; -import { LanguageProfile, MemoizedQuery } from "src/code-context/_base/LanguageProfile"; -import { TreeSitterFile } from "src/code-context/ast/TreeSitterFile"; -import { TreeSitterFileManager } from "src/editor/cache/TreeSitterFileManager"; -import vscode, { TextDocumentChangeEvent, Uri } from 'vscode'; -import Parser from "web-tree-sitter"; - -export class CsharpMethodExtractor { - private parser: TreeSitterFile; - private src:vscode.TextDocument - - - constructor(text: vscode.TextDocument,Parser:TreeSitterFile) { - this.src=text; - this.parser= Parser; - } - - public extractMethods(): MethodInfo[] { - const tree =this.parser.tree; - const queryObj= new MemoizedQuery(` - (method_declaration - (attribute_list)? @attribute - (modifier)* @modifier - (type) @return_type - (identifier) @name - (parameter_list - (parameter - (type) @param_type - (identifier) @param_name - )* - ) @parameters - (block) @body - ) - `) - const query= queryObj.query(this.parser.tsLanguage) - const methods: MethodInfo[] = []; - - - - - const matches = query.matches(tree.rootNode); - for (const match of matches) { - const method: MethodInfo = { - attributes: this.extractAttributes(match), - modifiers: this.extractModifiers(match), - returnType: this.extractReturnType(match), - name: this.extractName(match), - parameters: this.extractParameters(match), - body: this.extractBody(match), - methodComment: this.extractMethodComment(match), - parameterComments: this.extractParameterComments(match) - }; - methods.push(method); - } - - return methods; - } - - - private extractAttributes(match: Parser.QueryMatch): string[] { - return match.captures.filter(capture => capture.name === 'attribute').map(capture => capture.node.text); - } - - private extractModifiers(match: Parser.QueryMatch): string[] { - return match.captures.filter(capture => capture.name === 'modifier').map(capture => capture.node.text); - } - - private extractReturnType(match: Parser.QueryMatch): string { - const capture = match.captures.find(capture => capture.name === 'return_type'); - return capture ? capture.node.text : ''; - } - - private extractName(match: Parser.QueryMatch): string { - const capture = match.captures.find(capture => capture.name === 'name'); - return capture ? capture.node.text : ''; - } - - private extractParameters(match: Parser.QueryMatch): ParameterInfo[] { - const parameters: ParameterInfo[] = []; - const parameterCaptures = match.captures.filter(capture => capture.name === 'param_type' || capture.name === 'param_name'); - for (let i = 0; i < parameterCaptures.length; i += 2) { - parameters.push({ - type: parameterCaptures[i].node.text, - name: parameterCaptures[i + 1].node.text - }); - } - return parameters; - } - - private extractBody(match: Parser.QueryMatch): string { - const capture = match.captures.find(capture => capture.name === 'body'); - return capture ? capture.node.text : ''; - } - - private extractMethodComment(match: Parser.QueryMatch): string { - const capture = match.captures.find(capture => capture.name === 'method_comment'); - return capture ? capture.node.text : ''; - } - - private extractParameterComments(match: Parser.QueryMatch): { [key: string]: string } { - const parameterComments: { [key: string]: string } = {}; - const parameterList = match.captures.find(capture => capture.name === 'parameters')?.node; - if (parameterList) { - const parameterNodes = parameterList.children.filter(child => child.type === 'parameter'); - for (const parameterNode of parameterNodes) { - const parameterName = parameterNode.children.find(child => child.type === 'identifier')?.text; - const commentNode = parameterNode.previousSibling; - if (parameterName && commentNode && commentNode.type === 'comment') { - parameterComments[parameterName] = commentNode.text; - } - } - } - return parameterComments; - } -} - - -export interface MethodInfo { - attributes: string[]; - modifiers: string[]; - returnType: string; - name: string; - parameters: ParameterInfo[]; - body: string; - methodComment: string; - parameterComments: { [key: string]: string }; -} - -export interface ParameterInfo { - type: string; - name: string; -} diff --git a/src/code-context/csharp/model/FieldInfo.ts b/src/code-context/csharp/model/FieldInfo.ts new file mode 100644 index 00000000..781ef07c --- /dev/null +++ b/src/code-context/csharp/model/FieldInfo.ts @@ -0,0 +1,87 @@ + + +import Parser from "web-tree-sitter"; +import { BaseCsharpElement } from "./BaseCsharpElement"; +export class FieldInfo extends BaseCsharpElement { + type: string; + name: string; + accessModifier?: string[]; + doc?: string; + public constructor(fieldNode: Parser.SyntaxNode) { + super(); + this.doc = this.getFieldXmlDoc(fieldNode); + this.accessModifier = this.getFieldAccessModifier(fieldNode); + this.type = this.getFieldType(fieldNode); + this.name = this.getFieldName(fieldNode); + } + + // 获取字段的 XML 注释 + + private getFieldXmlDoc(fieldNode: Parser.SyntaxNode): string { + const xmlDocNode = fieldNode.previousSibling; + if (xmlDocNode && xmlDocNode.type === 'comment') { + const commits: string[] = []; + this.Getcommits(xmlDocNode, commits); + return commits.toString(); + } + return ''; + } + + // 获取字段名称 + getFieldName(fieldNode: Parser.SyntaxNode): string { + const variableNode = fieldNode.children.find(item => { + return item.type == 'variable_declaration'; + }); + if (variableNode == null) return ''; + const variableDeclaratorNode = variableNode.children.find(item => { + return item.type == 'variable_declarator'; + }); + if (variableDeclaratorNode == null) return ''; + const fieldNameNode = variableDeclaratorNode.children.find(item => { + return item.type == 'identifier'; + }); + if (fieldNameNode) { + return fieldNameNode ? fieldNameNode.text : ''; + } + return ''; + } + + // 获取字段类型 + getFieldType(fieldNode: Parser.SyntaxNode): string { + const variableNode = fieldNode.children.find(item => { + return item.type == 'variable_declaration'; + }); + if (variableNode == null) return ''; + let fieldTypeNode = variableNode.children.find(item => { + return item.type == 'identifier'; + }); + if (fieldTypeNode) { + return fieldTypeNode ? fieldTypeNode.text : ''; + }else + { + fieldTypeNode=variableNode.children.find(item => { + return item.type == 'predefined_type'; + }); + } + if (fieldTypeNode) { + return fieldTypeNode ? fieldTypeNode.text : ''; + } + return ''; + } + + // 获取字段访问修饰符 + getFieldAccessModifier(fieldNode: Parser.SyntaxNode): string[] { + const accessModifier: string[] = []; + const modifierNods = fieldNode.children.filter(item => { + return item.type == 'modifier'; + }); + + if (modifierNods.length > 0) { + for (const item of modifierNods) { + accessModifier.push(item.text); + } + return accessModifier; + } + return accessModifier; + } +} diff --git a/src/code-context/csharp/model/FrameworkCodeFragmentExtractor.ts b/src/code-context/csharp/model/FrameworkCodeFragmentExtractor.ts new file mode 100644 index 00000000..e66cc225 --- /dev/null +++ b/src/code-context/csharp/model/FrameworkCodeFragmentExtractor.ts @@ -0,0 +1,76 @@ +import { SyntaxNode } from 'web-tree-sitter'; + +import { IDataStorage } from 'base/common/workspace/WorkspaceService'; + +import { BaseCsharpElement } from './BaseCsharpElement'; + +type DocDealCallback = (result: string) => string; +export class FrameworkCodeFragmentExtractor { + private frameworkCodeFragmentNode: SyntaxNode; + private filePath: string; + constructor(frameworkCodeFragmentNode: SyntaxNode, filePath: string) { + this.frameworkCodeFragmentNode = frameworkCodeFragmentNode; + this.filePath = filePath; + } + public ExtractFrameworkCodeFragment(): FrameworkCodeFragment { + let context: string = ''; + let code: string = this.frameworkCodeFragmentNode.text; + if (this.frameworkCodeFragmentNode.type === 'class_declaration') { + } else if (this.frameworkCodeFragmentNode.type === 'method_declaration') { + } + return new FrameworkCodeFragment(this.frameworkCodeFragmentNode, context, code, this.filePath, (doc: string) => { + let match = /(.*?)<\/summary>/g; + let matchResult= match.exec(doc) + if (matchResult) { + return matchResult[0]; + } + return ''; + }); + } +} + +export class FrameworkCodeFragment extends BaseCsharpElement implements IDataStorage { + doc: string; + context: string; + code: string; + filePath: string; + public constructor( + frameworkCodeFragmentNode: SyntaxNode, + context: string, + code: string, + filePath: string, + docDealCallback: DocDealCallback, + ) { + super(); + if (frameworkCodeFragmentNode.previousSibling) { + this.doc = docDealCallback(this.Getcommits(frameworkCodeFragmentNode.previousSibling, []).reverse().toString()); + } else { + this.doc = ''; + } + + this.context = context; + this.code = code; + this.filePath = filePath; + } + equals(other: FrameworkCodeFragment): boolean { + if ( + other.code === this.code && + other.doc === this.doc && + other.context === this.context && + other.filePath === this.filePath + ) { + return true; + } + + return false; + } + GetType(): string { + return FrameworkCodeFragment.name.toString(); + } + Save(): void { + throw new Error('Method not implemented.'); + } + Load(): void { + throw new Error('Method not implemented.'); + } +} diff --git a/src/code-context/csharp/model/MethodInfo.ts b/src/code-context/csharp/model/MethodInfo.ts new file mode 100644 index 00000000..f1a7a320 --- /dev/null +++ b/src/code-context/csharp/model/MethodInfo.ts @@ -0,0 +1,105 @@ +import Parser from "web-tree-sitter"; +import { BaseCsharpElement } from "./BaseCsharpElement"; + +export class MethodInfo extends BaseCsharpElement { + attributes?: string[]; + modifiers?: string[]; + returnType?: string; + returnDoc?: string; + name: string; + parameters?: IParameterInfo[]; + methodXmlDoc: string; + + public constructor(methodNode: Parser.SyntaxNode) { + super(); + + const methodXmlDocTeam = this.getMethodXmlDocTeam(methodNode); + this.returnDoc=methodXmlDocTeam[0].includes('')?methodXmlDocTeam[0]:'' + const methodNameAndReturnType = this.getMethodNameAndReturnType(methodNode); + this.parameters = this.getMethodParameters(methodNode, methodXmlDocTeam); + this.modifiers = this.getMethodAccessModifier(methodNode); + this.name = methodNameAndReturnType[0]; + this.returnType = methodNameAndReturnType[1]; + this.methodXmlDoc = methodXmlDocTeam.reverse().toString(); + } + // 获取方法的 XML 注释 + getMethodXmlDocTeam(methodNode: Parser.SyntaxNode): string[] { + const xmlDocNode = methodNode.previousSibling; + if (xmlDocNode && xmlDocNode.type === 'comment') { + const commits: string[] = []; + this.Getcommits(xmlDocNode, commits); + return commits; + } + return []; + } + + // 获取方法访问修饰符 + getMethodAccessModifier(methodNode: Parser.SyntaxNode): string[] { + const modifierNodes = methodNode.children.filter(item => { + return item.type == 'modifier'; + }); + const modifiers: string[] = []; + if (modifierNodes) { + for (const item of modifierNodes) { + modifiers.push(item.text); + } + return modifiers; + } + return modifiers; + } + + // 获取方法名称 + getMethodNameAndReturnType(methodNode: Parser.SyntaxNode): string[] { + const modifierNodes = methodNode.children.filter(item => { + return item.type == 'identifier'; + }); + const identifiers: string[] = []; + if (modifierNodes) { + for (const item of modifierNodes) { + identifiers.push(item.text); + } + if (identifiers.length < 2) { + identifiers.push('void'); + identifiers.reverse(); + } + return identifiers; + } + + return identifiers; + } + // 获取方法参数 + getMethodParameters(methodNode: Parser.SyntaxNode, methoddocTeam: string[]): IParameterInfo[] { + const parameterListNode = methodNode.children.find(item => { + return item.type == 'parameter_list'; + }); + const parameterInfos: IParameterInfo[] = []; + if (parameterListNode) { + const parameterNodes = parameterListNode.children.filter(item => { + return item.type == 'parameter'; + }); + for (const item of parameterNodes) { + const identifiers = item.children.filter(item => { + return item.type == 'identifier'; + }); + + const paramDoc = methoddocTeam.find(item => item.includes(identifiers[1].text)); + const param = { + type: identifiers[0].text, + name: identifiers[1].text, + doc: paramDoc, + }; + parameterInfos.push(param); + } + } + return parameterInfos; + } + + + +} + +export interface IParameterInfo { + type: string; + name: string; + doc?: string; +} diff --git a/src/code-context/csharp/model/PropertyInfo.ts b/src/code-context/csharp/model/PropertyInfo.ts new file mode 100644 index 00000000..8d30b240 --- /dev/null +++ b/src/code-context/csharp/model/PropertyInfo.ts @@ -0,0 +1,102 @@ + +import Parser from "web-tree-sitter"; +import { BaseCsharpElement } from "./BaseCsharpElement"; +export class PropertyInfo extends BaseCsharpElement { + type: string; + name: string; + doc?: string; + accessModifier?: string[]; + modifier_get?: string; + modifier_set?: string; + public constructor(propertyNode: Parser.SyntaxNode) { + super(); + this.type = this.getPropertyType(propertyNode); + this.name = this.getPropertyName(propertyNode); + this.doc = this.getPropertyXmlDoc(propertyNode); + this.accessModifier = this.getPropertyAccessModifier(propertyNode); + const modifierGetAndSet = this.getPropertyAccessModifier_getOrSet(propertyNode); + this.modifier_get = modifierGetAndSet[0]; + this.modifier_set = modifierGetAndSet[1]; + } + // 获取属性的 XML 注释 + getPropertyXmlDoc(propertyNode: Parser.SyntaxNode): string { + const xmlDocNode = propertyNode.previousSibling; + if (xmlDocNode && xmlDocNode.type === 'comment') { + const commits: string[] = []; + this.Getcommits(xmlDocNode, commits); + return commits.toString(); + } + return ''; + } + + // 获取属性名称 + getPropertyName(propertyNode: Parser.SyntaxNode): string { + const variableNode = propertyNode.children.find(item => { + return item.type == 'identifier'; + }); + + if (variableNode) { + return variableNode ? variableNode.text : ''; + } + return ''; + } + + // 获取属性类型 + getPropertyType(propertyNode: Parser.SyntaxNode): string { + const typeNode = propertyNode.children.find(item => { + return item.type == 'predefined_type'; + }); + if (typeNode) { + return typeNode ? typeNode.text : ''; + } + return ''; + } + + // 获取属性访问修饰符 + getPropertyAccessModifier(propertyNode: Parser.SyntaxNode): string[] { + const accessModifier: string[] = []; + const modifierNods = propertyNode.children.filter(item => { + return item.type == 'modifier'; + }); + + if (modifierNods.length > 0) { + for (const item of modifierNods) { + accessModifier.push(item.text); + } + return accessModifier; + } + return accessModifier; + } + getPropertyAccessModifier_getOrSet(propertyNode: Parser.SyntaxNode): string[] { + const accessorListNode = propertyNode.children.find(item => { + return item.type == 'accessor_list'; + }); + const accessModifiers: string[] = ['', '']; + if (accessorListNode == null) return accessModifiers; + const accessorNodes = accessorListNode.children.filter(item => { + return item.type == 'accessor_declaration'; + }); + let accessModifier_get: Parser.SyntaxNode | undefined; + let accessModifier_set: Parser.SyntaxNode | undefined; + for (const item of accessorNodes) { + if (accessModifier_get == undefined) { + accessModifier_get = item.children.find(item => { + return item.type == 'get'; + }); + } + if (accessModifier_set == undefined) { + accessModifier_set = item.children.find(item => { + return item.type == 'set'; + }); + } + } + if (accessModifier_get == undefined) return accessModifiers; + if (accessModifier_get.previousSibling == null) return accessModifiers; + accessModifiers[0] = accessModifier_get.previousSibling?.text; + + if (accessModifier_set == undefined) return accessModifiers; + if (accessModifier_set.previousSibling == null) return accessModifiers; + accessModifiers[1] = accessModifier_set.previousSibling?.text; + return accessModifiers; + } +} diff --git a/src/code-search/schemas/indexes/c_sharp.scm b/src/code-search/schemas/indexes/c_sharp.scm index dcfd842e..baa91baf 100644 --- a/src/code-search/schemas/indexes/c_sharp.scm +++ b/src/code-search/schemas/indexes/c_sharp.scm @@ -22,7 +22,9 @@ (destructor_declaration) (indexer_declaration) (method_declaration) - + + + ;; enum items (enum_member_declaration_list) @@ -40,7 +42,7 @@ (catch_clause) ;; using statement: - ;; + ;; ;; using (var a = b) { .. } (using_statement) @@ -66,7 +68,7 @@ (identifier) @local.definition.local) ;; namespaces -(namespace_declaration +(namespace_declaration (identifier) @hoist.definition.namespace) ;; classes @@ -80,9 +82,9 @@ name: (identifier) @hoist.definition.class) (constructor_declaration name: (identifier) @hoist.definition.method) -(destructor_declaration +(destructor_declaration name: (identifier) @hoist.definition.method) -(method_declaration +(method_declaration name: (identifier) @hoist.definition.method) ;; enums @@ -98,10 +100,10 @@ ;; records ;; ;; record F {} -(record_declaration +(record_declaration name: (identifier) @hoist.definition.class) ;; record struct F {} -(record_struct_declaration +(record_struct_declaration name: (identifier) @hoist.definition.struct) ;; structs @@ -139,7 +141,7 @@ name: (identifier) @local.definition.local) ;; type params make defs -(type_parameter +(type_parameter (identifier) @local.definition.typedef) ;; [params string[] args] @@ -157,7 +159,7 @@ name: (identifier) @local.definition.local) ;; foreach(Type x in y) { .. } -;; +;; ;; `Type` is a ref ;; `x` is a def ;; `y` is a ref @@ -167,7 +169,7 @@ ;; imports ;; using System.Text -;; +;; ;; `Text` is an import (using_directive . @@ -200,7 +202,7 @@ ;; x is int (is_expression (identifier) @local.reference) -;; x is String +;; x is String (is_pattern_expression (identifier) @local.reference) @@ -240,13 +242,13 @@ (identifier) @local.reference) ;; function or array args -(argument +(argument (identifier) @local.reference) ;; ident switch {} (switch_expression (identifier) @local.reference) -(switch_expression_arm +(switch_expression_arm (identifier) @local.reference) ;; checked(ident) @@ -281,12 +283,12 @@ (object_creation_expression (identifier) @local.reference) -;; foo() +;; foo() (invocation_expression (identifier) @local.reference) ;; A.b -(member_access_expression +(member_ . (identifier) @local.reference) @@ -314,7 +316,7 @@ (identifier) @local.reference) ;; lambda body -(lambda_expression +(lambda_expression body: (identifier) @local.reference) ;; new [] {a, b, c} @@ -369,7 +371,7 @@ (identifier) @local.reference) ;; foreach(Type x in y) { .. } -;; +;; ;; `Type` is a ref ;; `x` is a def ;; `y` is a ref @@ -425,7 +427,7 @@ ;; type constraints (type_parameter_constraints_clause (identifier) @local.reference) -(type_constraint +(type_constraint (identifier) @local.reference) ;; base types in enums & interfaces @@ -441,5 +443,5 @@ (identifier) @local.reference)) ;; function return type -(local_function_statement +(local_function_statement type: (identifier) @local.reference) diff --git a/src/commands/commandsService.ts b/src/commands/commandsService.ts index 31ad71f3..eab96095 100644 --- a/src/commands/commandsService.ts +++ b/src/commands/commandsService.ts @@ -23,6 +23,7 @@ import { CMD_CODEASPACE_KEYWORDS_ANALYSIS, CMD_CODEBASE_INDEXING, CMD_CODEBASE_RETRIEVAL, + CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE, CMD_CREATE_UNIT_TEST, CMD_EXPLAIN_CODE, CMD_FEEDBACK, @@ -180,7 +181,29 @@ export class CommandsService { await setTimeout(800); chat.input(`${l10n.t('I got the following error, can you please help explain how to fix it?')}: ${input}`); } + async addCodeSample() { + const editor = window.activeTextEditor; + if (!editor) { + return; + } + + try { + const document = editor.document; + const edit = new WorkspaceEdit(); + const elementBuilder = await createNamedElement(this.autodev.treeSitterFileManager, document); + const currentLine = editor.selection.active.line; + const ranges = elementBuilder.getElementForAction(currentLine); + + if (ranges.length === 0) { + return; + } + await this.autodev.executeAddCodeSampleExecutorAction(document, ranges[0], edit); + } catch (error) { + logger.error(`Commands error`, error); + showErrorMessage('Command Call Error'); + } + } async generateMethod() { const editor = window.activeTextEditor; if (!editor) { @@ -401,6 +424,7 @@ export class CommandsService { commands.registerCommand(CMD_QUICK_FIX, this.quickFix, this), commands.registerCommand(CMD_GEN_DOCSTRING, this.generateDocstring, this), commands.registerCommand(CMD_GEN_CODE_METHOD_COMPLETIONS, this.generateMethod, this), + commands.registerCommand(CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE,this.addCodeSample,this), commands.registerCommand(CMD_CREATE_UNIT_TEST, this.generateUnitTest, this), // Codebase Commands commands.registerCommand(CMD_CODEBASE_INDEXING, this.startCodebaseIndexing, this), @@ -420,6 +444,7 @@ export class CommandsService { commands.registerCommand(CMD_GIT_MESSAGE_COMMIT_GENERATE, this.generateCommitMessage, this), ); } + } interface CodebaseResultItem extends QuickPickItem { diff --git a/src/editor/ast/NamedElement.ts b/src/editor/ast/NamedElement.ts index e5526114..2f644b63 100644 --- a/src/editor/ast/NamedElement.ts +++ b/src/editor/ast/NamedElement.ts @@ -1,3 +1,4 @@ +import { SyntaxNode } from 'web-tree-sitter'; import { TreeSitterFile } from '../../code-context/ast/TreeSitterFile'; import { CodeElementType } from '../codemodel/CodeElementType'; import { TextInRange } from './TextInRange'; @@ -29,19 +30,21 @@ export class NamedElement { blockContent: string; commentRange: TextInRange | undefined; file: TreeSitterFile; - + node:SyntaxNode; constructor( blockRange: TextInRange, identifierRange: TextInRange, codeElementType: CodeElementType, blockContent: string, file: TreeSitterFile, + node:SyntaxNode ) { this.blockRange = blockRange; this.identifierRange = identifierRange; this.blockContent = blockContent; this.codeElementType = codeElementType; this.file = file; + this.node=node; } isTestFile(): boolean { diff --git a/src/editor/ast/NamedElementBuilder.ts b/src/editor/ast/NamedElementBuilder.ts index 24af9bc1..f1f1bd5c 100644 --- a/src/editor/ast/NamedElementBuilder.ts +++ b/src/editor/ast/NamedElementBuilder.ts @@ -148,6 +148,7 @@ export class NamedElementBuilder { elementType, blockNode.text, this.file, + blockNode ); let commentNode = TreeSitterUtil.previousNodesOfType(blockNode, ['block_comment', 'line_comment']); diff --git a/src/extension.ts b/src/extension.ts index 287d1f29..d6aed6f2 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -14,6 +14,7 @@ import { AutoDevExtension } from './AutoDevExtension'; import { LanguageModelsService } from './base/common/language-models/languageModelsService'; import { CommandsService } from './commands/commandsService'; import { ChatViewService } from './editor/views/chat/chatViewService'; +import { WorkspaceService } from 'base/common/workspace/WorkspaceService'; (globalThis as any).self = globalThis; @@ -28,12 +29,14 @@ export async function activate(context: ExtensionContext) { context.subscriptions.push( instantiationService.registerSingleton(ConfigurationService), + instantiationService.registerSingleton(WorkspaceService), instantiationService.registerSingleton(ContextStateService), instantiationService.registerSingleton(LanguageModelsService), instantiationService.registerSingleton(ILanguageServiceProvider, LanguageServiceProvider), instantiationService.registerSingleton(ChatViewService).register(), instantiationService.registerSingleton(AutoDevExtension).register(), instantiationService.registerSingleton(CommandsService).register(), + //ArchiverService ); instantiationService.get(AutoDevExtension).run(); From 342776612f76b8aafdb42aed4f75144aeda6894a Mon Sep 17 00:00:00 2001 From: Heipi <793181018@qq.com> Date: Sun, 29 Sep 2024 01:18:29 +0800 Subject: [PATCH 03/17] Complete the function of adding and deleting custom code instances and custom code snippets (but with bugs) --- l10n/bundle.l10n.zh-cn.json | 2 + package.json | 8 +- package.nls.json | 2 + package.nls.zh-cn.json | 2 + src/AutoDevExtension.ts | 22 +- .../AddCodeSample/AddCodeSampleExecutor.ts | 6 +- .../autoMethod/AutoMethodActionExecutor.ts | 2 +- .../autoMethod/AutoMethodTemplateContext.ts | 2 +- .../providers/AutoDevCodeLensProvider.ts | 222 +++++++++++++++--- .../RemoveFrameworkCodeFragmentExecutor.ts | 62 +++++ .../RemoveCodeSampleExecutor.ts | 63 +++++ .../common/configuration/configuration.ts | 2 + src/base/common/workspace/WorkspaceService.ts | 181 ++++++++++++-- .../model/FrameworkCodeFragmentExtractor.ts | 12 +- src/code-context/csharp/model/MethodInfo.ts | 2 + 15 files changed, 512 insertions(+), 78 deletions(-) create mode 100644 src/action/removeCodeFragment/RemoveFrameworkCodeFragmentExecutor.ts create mode 100644 src/action/removeCodeSample/RemoveCodeSampleExecutor.ts diff --git a/l10n/bundle.l10n.zh-cn.json b/l10n/bundle.l10n.zh-cn.json index a46aa1f4..84d72a41 100644 --- a/l10n/bundle.l10n.zh-cn.json +++ b/l10n/bundle.l10n.zh-cn.json @@ -10,6 +10,8 @@ "AutoMethod": "填充当前方法", "addFrameworkCodeFragment": "添加自定义代码块", "addCodeSample": "添加自定义样例", + "removeFrameworkCodeFragment": "移除自定义代码块", + "removeCodeSample": "移除自定义样例", "Optimize the code": "请优化下面的代码", "How do I fix this problem in the above code?": "请帮我修复下面代码中的问题:", "I got the following error, can you please help explain how to fix it?": "我遇到了以下错误,请帮我解释如何修复它?", diff --git a/package.json b/package.json index 25f97e33..53368e2e 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,9 @@ "customAction", "AutoMethod", "addCodeSample", - "addFrameworkCodeFragment" + "addFrameworkCodeFragment", + "removeCodeSample", + "removeFrameworkCodeFragment" ] }, "uniqueItems": true, @@ -101,7 +103,9 @@ "autoComment", "AutoMethod", "addCodeSample", - "addFrameworkCodeFragment" + "addFrameworkCodeFragment", + "removeCodeSample", + "removeFrameworkCodeFragment" ], "description": "%configuration.codelensDisplayItems.description%", "order": 4 diff --git a/package.nls.json b/package.nls.json index ceb5593d..f151b85e 100644 --- a/package.nls.json +++ b/package.nls.json @@ -96,6 +96,8 @@ "command.AutoMethod.title": "Generate Method Codes", "command.addFrameworkCodeFragment.title": "Add Framework Code Fragment", "command.addCodeSample.title": "Add Code Sample", + "command.removeFrameworkCodeFragment.title": "Remove Framework Code Fragment", + "command.removeCodeSample.title": "Remove Code Sample", "command.explainCode.title": "Explain Code", "command.optimizeCode.title": "Optimize Code", "command.quickFix.title": "Quick Fix", diff --git a/package.nls.zh-cn.json b/package.nls.zh-cn.json index 8a62ca27..2107cae3 100644 --- a/package.nls.zh-cn.json +++ b/package.nls.zh-cn.json @@ -67,7 +67,9 @@ "command.AutoMethod.title": "填充当前方法", "command.autoTest.title": "生成测试", "command.addFrameworkCodeFragment.title": "添加自定义代码块", + "command.removeFrameworkCodeFragment.title": "移除自定义代码块", "command.addCodeSample.title": "添加自定义样例", + "command.removeCodeSample.title": "移除自定义样例", "command.openSettings.title": "打开设置", "command.feedback.title": "意见反馈", "command.genApiData.title": "生成 API 数据", diff --git a/src/AutoDevExtension.ts b/src/AutoDevExtension.ts index 7ceefe10..d346006e 100644 --- a/src/AutoDevExtension.ts +++ b/src/AutoDevExtension.ts @@ -7,11 +7,13 @@ import { ContextStateService } from 'base/common/configuration/contextState'; import { WorkspaceFileSystem } from 'base/common/fs'; import { ILanguageServiceProvider } from 'base/common/languages/languageService'; import { logger } from 'base/common/log/log'; +import { WorkspaceService } from 'base/common/workspace/WorkspaceService'; +import { AddFrameworkCodeFragmentExecutor } from './action/addCodeFragment/AddFrameworkCodeFragmentExecutor'; +import { AddCodeSampleExecutor } from './action/addCodeSample/AddCodeSampleExecutor'; import { AutoDocActionExecutor } from './action/autodoc/AutoDocActionExecutor'; -import { AutoTestActionExecutor } from './action/autotest/AutoTestActionExecutor'; import { AutoMethodActionExecutor } from './action/autoMethod/AutoMethodActionExecutor'; - +import { AutoTestActionExecutor } from './action/autotest/AutoTestActionExecutor'; import { registerAutoDevProviders, registerCodeLensProvider, @@ -19,6 +21,7 @@ import { registerQuickFixProvider, registerRenameAction, } from './action/ProviderRegister'; +import { RemoveCodeSampleExecutor } from './action/removeCodeSample/RemoveCodeSampleExecutor'; import { SystemActionService } from './action/setting/SystemActionService'; import { Catalyser } from './agent/catalyser/Catalyser'; import { LanguageModelsService } from './base/common/language-models/languageModelsService'; @@ -43,12 +46,6 @@ import { TemplateContext } from './prompt-manage/template/TemplateContext'; import { TemplateRender } from './prompt-manage/template/TemplateRender'; import { IProjectService } from './ProviderTypes'; import { ToolchainContextManager } from './toolchain-context/ToolchainContextManager'; -import { AddFrameworkCodeFragmentExecutor } from './action/addCodeFragment/AddFrameworkCodeFragmentExecutor'; -import { AddCodeSampleExecutor } from './action/AddCodeSample/AddCodeSampleExecutor'; -import { WorkspaceService } from 'base/common/workspace/WorkspaceService'; - - - @injectable() export class AutoDevExtension { @@ -100,7 +97,6 @@ export class AutoDevExtension { public teamTerm: TeamTermService, @inject(WorkspaceService) public workSpace: WorkspaceService, - ) { this.ideAction = new VSCodeAction(); this.statusBarManager = new AutoDevStatusManager(); @@ -240,10 +236,18 @@ export class AutoDevExtension { executeAddCodeSampleExecutorAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) { return new AddCodeSampleExecutor(this, document, nameElement, edit).execute(); } + executeRemoveCodeSampleExecutorAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) { + return new RemoveCodeSampleExecutor(this, document, nameElement, edit).execute(); + } + executeAddFrameworkCodeFragmentAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) { return new AddFrameworkCodeFragmentExecutor(this, document, nameElement, edit).execute(); } + executeRemoveFrameworkCodeFragmentAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) { + return new RemoveCodeSampleExecutor(this, document, nameElement, edit).execute(); + } + executeAutoTestAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) { return new AutoTestActionExecutor(this, document, nameElement, edit).execute(); } diff --git a/src/action/AddCodeSample/AddCodeSampleExecutor.ts b/src/action/AddCodeSample/AddCodeSampleExecutor.ts index 8b2384b5..d3b3814b 100644 --- a/src/action/AddCodeSample/AddCodeSampleExecutor.ts +++ b/src/action/AddCodeSample/AddCodeSampleExecutor.ts @@ -51,10 +51,6 @@ export class AddCodeSampleExecutor implements ActionExecutor { const document = this.document; const range = this.range; const language = document.languageId; - - const startSymbol = LANGUAGE_BLOCK_COMMENT_MAP[language]!.start; - const endSymbol = LANGUAGE_BLOCK_COMMENT_MAP[language]!.end; - const classInfo = new CsharpClassExtractor(this.range.node).ExtractClass(); this.statusBarManager.setStatus(AutoDevStatus.InProgress); selectCodeInRange(range.blockRange.start, range.blockRange.end); @@ -82,7 +78,7 @@ export class CodeSample extends BaseCsharpElement implements IDataStorage { } this.context = 'context'; - this.filePath = filePath; + this.filePath = filePath; } equals(other: CodeSample): boolean { if ( diff --git a/src/action/autoMethod/AutoMethodActionExecutor.ts b/src/action/autoMethod/AutoMethodActionExecutor.ts index 4dcfb5cd..c5f25bff 100644 --- a/src/action/autoMethod/AutoMethodActionExecutor.ts +++ b/src/action/autoMethod/AutoMethodActionExecutor.ts @@ -20,7 +20,7 @@ import { ActionExecutor } from '../_base/ActionExecutor'; import { AutoMethodTemplateContext } from './AutoMethodTemplateContext'; import { CsharpClassExtractor } from 'src/code-context/csharp/model/CsharpClassExtractor'; import { FrameworkCodeFragment } from 'src/code-context/csharp/model/FrameworkCodeFragmentExtractor'; -import { CodeSample } from '../AddCodeSample/AddCodeSampleExecutor'; +import { CodeSample } from '../addCodeSample/AddCodeSampleExecutor'; import { MethodInfo } from 'src/code-context/csharp/model/MethodInfo'; export class AutoMethodActionExecutor implements ActionExecutor { diff --git a/src/action/autoMethod/AutoMethodTemplateContext.ts b/src/action/autoMethod/AutoMethodTemplateContext.ts index f533fd66..8d22e913 100644 --- a/src/action/autoMethod/AutoMethodTemplateContext.ts +++ b/src/action/autoMethod/AutoMethodTemplateContext.ts @@ -3,7 +3,7 @@ import { Interface } from 'readline'; import { TemplateContext } from '../../prompt-manage/template/TemplateContext'; import { ClassInfo } from 'src/code-context/csharp/model/CsharpClassExtractor'; import { FrameworkCodeFragment } from 'src/code-context/csharp/model/FrameworkCodeFragmentExtractor'; -import { CodeSample } from '../AddCodeSample/AddCodeSampleExecutor'; +import { CodeSample } from '../addCodeSample/AddCodeSampleExecutor'; import { MethodInfo } from 'src/code-context/csharp/model/MethodInfo'; diff --git a/src/action/providers/AutoDevCodeLensProvider.ts b/src/action/providers/AutoDevCodeLensProvider.ts index e4098dbf..422294c1 100644 --- a/src/action/providers/AutoDevCodeLensProvider.ts +++ b/src/action/providers/AutoDevCodeLensProvider.ts @@ -2,9 +2,11 @@ import { setTimeout } from 'node:timers/promises'; import { convertToErrorMessage, TreeSitterFile } from 'src/code-context/ast/TreeSitterFile'; +import { FrameworkCodeFragment } from 'src/code-context/csharp/model/FrameworkCodeFragmentExtractor'; import { NamedElement } from 'src/editor/ast/NamedElement'; import { NamedElementBuilder } from 'src/editor/ast/NamedElementBuilder'; import { TreeSitterFileManager } from 'src/editor/cache/TreeSitterFileManager'; +import { CodeElementType } from 'src/editor/codemodel/CodeElementType'; import { ChatViewService } from 'src/editor/views/chat/chatViewService'; import { CancellationToken, @@ -14,6 +16,7 @@ import { commands, Disposable, l10n, + ProviderResult, TextDocument, window, WorkspaceEdit, @@ -25,11 +28,13 @@ import { CMD_CODELENS_GEN_DOCSTRING, CMD_CODELENS_OPTIMIZE_CODE, CMD_CODELENS_QUICK_CHAT, + CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE, + CMD_CODELENS_SHOW_CODE_ADD_FRAMEWORK_CODE_FRAGMENT, + CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS, + CMD_CODELENS_SHOW_CODE_REMOVE_CODE_SAMPLE, + CMD_CODELENS_SHOW_CODE_REMOVE_FRAMEWORK_CODE_FRAGMENT, CMD_CODELENS_SHOW_CUSTOM_ACTION, CMD_SHOW_CODELENS_DETAIL_QUICKPICK, - CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS, - CMD_CODELENS_SHOW_CODE_ADD_FRAMEWORK_CODE_FRAGMENT, - CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE } from 'base/common/configuration/configuration'; import { ConfigurationService } from 'base/common/configuration/configurationService'; import { isFileTooLarge } from 'base/common/files/files'; @@ -38,16 +43,26 @@ import { ILanguageServiceProvider } from 'base/common/languages/languageService' import { logger } from 'base/common/log/log'; import { type AutoDevExtension } from '../../AutoDevExtension'; -import { CodeElementType } from 'src/editor/codemodel/CodeElementType'; - -type CodeLensItemType = 'quickChat' | 'explainCode' | 'optimizeCode' | 'autoComment' |'addCodeSample'| - 'autoTest' | 'customAction'|'AutoMethod'|'addFrameworkCodeFragment'; +import { CodeSample } from '../addCodeSample/AddCodeSampleExecutor'; + +type CodeLensItemType = + | 'quickChat' + | 'explainCode' + | 'optimizeCode' + | 'autoComment' + | 'addCodeSample' + | 'autoTest' + | 'customAction' + | 'AutoMethod' + | 'addFrameworkCodeFragment' + | 'removeCodeSample' + | 'removeFrameworkCodeFragment'; export class AutoDevCodeLensProvider implements CodeLensProvider { private config: ConfigurationService; private lsp: ILanguageServiceProvider; private fileManager: TreeSitterFileManager; - + private autoDev: AutoDevExtension; private disposables: Disposable[]; constructor(private autodev: AutoDevExtension) { @@ -56,7 +71,7 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { this.fileManager = autodev.treeSitterFileManager; const chat = autodev.chat; - + this.autoDev = autodev; this.disposables = [ commands.registerCommand(CMD_SHOW_CODELENS_DETAIL_QUICKPICK, (items: Command[]) => { const quickPick = window.createQuickPick<{ @@ -128,17 +143,36 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { commands.registerCommand(CMD_CODELENS_SHOW_CUSTOM_ACTION, (document: TextDocument, nameElement: NamedElement) => { autodev.executeCustomAction(document, nameElement); }), - commands.registerCommand(CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS, (document: TextDocument, nameElement: NamedElement) => { - autodev.executeAutoMethodAction(document, nameElement); - }), - commands.registerCommand(CMD_CODELENS_SHOW_CODE_ADD_FRAMEWORK_CODE_FRAGMENT, (document: TextDocument, nameElement: NamedElement) => { - autodev.executeAddFrameworkCodeFragmentAction(document, nameElement); - }), - commands.registerCommand(CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE, (document: TextDocument, nameElement: NamedElement) => { - autodev.executeAddCodeSampleExecutorAction(document, nameElement); - }), - - + commands.registerCommand( + CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS, + (document: TextDocument, nameElement: NamedElement) => { + autodev.executeAutoMethodAction(document, nameElement); + }, + ), + commands.registerCommand( + CMD_CODELENS_SHOW_CODE_ADD_FRAMEWORK_CODE_FRAGMENT, + (document: TextDocument, nameElement: NamedElement) => { + autodev.executeAddFrameworkCodeFragmentAction(document, nameElement); + }, + ), + commands.registerCommand( + CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE, + (document: TextDocument, nameElement: NamedElement) => { + autodev.executeAddCodeSampleExecutorAction(document, nameElement); + }, + ), + commands.registerCommand( + CMD_CODELENS_SHOW_CODE_REMOVE_FRAMEWORK_CODE_FRAGMENT, + (document: TextDocument, nameElement: NamedElement) => { + autodev.executeRemoveFrameworkCodeFragmentAction(document, nameElement); + }, + ), + commands.registerCommand( + CMD_CODELENS_SHOW_CODE_REMOVE_CODE_SAMPLE, + (document: TextDocument, nameElement: NamedElement) => { + autodev.executeRemoveCodeSampleExecutorAction(document, nameElement); + }, + ), ]; } @@ -169,13 +203,13 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { } const elements = await this.parseToNamedElements(document); -// elements为空导致codelens组没有数据,无法生成codelens + // elements为空导致codelens组没有数据,无法生成codelens if (token.isCancellationRequested || elements.length === 0) { return []; } - const groups = this.buildCodeLensGroups(displayItems, elements, document, token); + let groups = this.buildCodeLensGroups(displayItems, elements, document, token); if (groups.length === 0) { return []; } @@ -186,6 +220,11 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { return groups.flat(); } + resolveCodeLens(codeLens: CodeLens): ProviderResult { + // 解析 CodeLens 的详细信息 + + return codeLens; + } private buildQuickPickCodeLens(codelenses: CodeLens[]): CodeLens { const [head] = codelenses; @@ -205,13 +244,13 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { document: TextDocument, token: CancellationToken, ) { - const result: CodeLens[][] = []; - const hasCustomPromps = this.hasCustomPromps(); + let result: CodeLens[][] = []; + let hasCustomPromps = this.hasCustomPromps(); - for (const element of elements) { - const codelenses: CodeLens[] = []; + for (let element of elements) { + let codelenses: CodeLens[] = []; - for (const type of displaySet) { + for (let type of displaySet) { if (type === 'quickChat') { codelenses.push( new CodeLens(element.identifierRange, { @@ -280,7 +319,7 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { continue; } if (type === 'AutoMethod') { - if (element.codeElementType==CodeElementType.Method) { + if (element.codeElementType == CodeElementType.Method) { codelenses.push( new CodeLens(element.identifierRange, { title: l10n.t('AutoMethod'), @@ -293,7 +332,18 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { } if (type === 'addCodeSample') { - if (element.codeElementType==CodeElementType.Method||element.codeElementType==CodeElementType.Structure) { + let hasDataStorage = this.autoDev.workSpace.HasDataStorage( + document.languageId, + new CodeSample(element.node, document.uri.fsPath), + ); + if (element.codeElementType == CodeElementType.Method) { + console.log('------'); + } + if ( + (element.codeElementType == CodeElementType.Method || + element.codeElementType == CodeElementType.Structure) && + hasDataStorage == false + ) { codelenses.push( new CodeLens(element.identifierRange, { title: l10n.t('addCodeSample'), @@ -305,7 +355,45 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { continue; } if (type === 'addFrameworkCodeFragment') { - if (element.codeElementType==CodeElementType.Method||element.codeElementType==CodeElementType.Structure) { + let frameworkCodeFragment: FrameworkCodeFragment | undefined; + switch (element.codeElementType) { + case CodeElementType.Method: + frameworkCodeFragment = new FrameworkCodeFragment( + element.node, + '', + element.node.text, + document.uri.fsPath, + (doc: string) => { + let match = /(.*?)<\/summary>/g; + let matchResult = match.exec(doc); + if (matchResult) { + return matchResult[0]; + } + return ''; + }, + ); + break; + case CodeElementType.Structure: + frameworkCodeFragment = new FrameworkCodeFragment( + element.node, + '', + element.node.text, + document.uri.fsPath, + ); + break; + } + if (frameworkCodeFragment == undefined) { + continue; + } + let hasDataStorage = this.autoDev.workSpace.HasDataStorage(document.languageId, frameworkCodeFragment); + if (element.codeElementType == CodeElementType.Method) { + console.log('------'); + } + if ( + (element.codeElementType == CodeElementType.Method || + element.codeElementType == CodeElementType.Structure) && + hasDataStorage == false + ) { codelenses.push( new CodeLens(element.identifierRange, { title: l10n.t('addFrameworkCodeFragment'), @@ -316,6 +404,80 @@ export class AutoDevCodeLensProvider implements CodeLensProvider { } continue; } + if (type === 'removeCodeSample') { + let hasDataStorage = this.autoDev.workSpace.HasDataStorage( + document.languageId, + new CodeSample(element.node, document.uri.fsPath), + ); + if (element.codeElementType == CodeElementType.Method) { + console.log('------'); + } + if ( + (element.codeElementType == CodeElementType.Method || + element.codeElementType == CodeElementType.Structure) && + hasDataStorage == true + ) { + codelenses.push( + new CodeLens(element.identifierRange, { + title: l10n.t('removeCodeSample'), + command: CMD_CODELENS_SHOW_CODE_REMOVE_CODE_SAMPLE, + arguments: [document, element], + }), + ); + } + continue; + } + if (type === 'removeFrameworkCodeFragment') { + let frameworkCodeFragment: FrameworkCodeFragment | undefined; + switch (element.codeElementType) { + case CodeElementType.Method: + frameworkCodeFragment = new FrameworkCodeFragment( + element.node, + '', + element.node.text, + document.uri.fsPath, + (doc: string) => { + let match = /(.*?)<\/summary>/g; + let matchResult = match.exec(doc); + if (matchResult) { + return matchResult[0]; + } + return ''; + }, + ); + break; + case CodeElementType.Structure: + frameworkCodeFragment = new FrameworkCodeFragment( + element.node, + '', + element.node.text, + document.uri.fsPath, + ); + break; + } + if (frameworkCodeFragment == undefined) { + continue; + } + let hasDataStorage = this.autoDev.workSpace.HasDataStorage(document.languageId, frameworkCodeFragment); + if (element.codeElementType == CodeElementType.Method) { + console.log('------'); + } + + if ( + (element.codeElementType == CodeElementType.Method || + element.codeElementType == CodeElementType.Structure) && + hasDataStorage == true + ) { + codelenses.push( + new CodeLens(element.identifierRange, { + title: l10n.t('removeFrameworkCodeFragment'), + command: CMD_CODELENS_SHOW_CODE_REMOVE_FRAMEWORK_CODE_FRAGMENT, + arguments: [document, element], + }), + ); + } + continue; + } } result.push(codelenses); diff --git a/src/action/removeCodeFragment/RemoveFrameworkCodeFragmentExecutor.ts b/src/action/removeCodeFragment/RemoveFrameworkCodeFragmentExecutor.ts new file mode 100644 index 00000000..c3ed82bc --- /dev/null +++ b/src/action/removeCodeFragment/RemoveFrameworkCodeFragmentExecutor.ts @@ -0,0 +1,62 @@ +import fs from 'fs'; +import { AutoDevExtension } from 'src/AutoDevExtension'; +import { Position, TextDocument, WorkspaceEdit } from 'vscode'; +import vscode from 'vscode'; + +import { ChatMessageRole, IChatMessage } from 'base/common/language-models/languageModels'; +import { LanguageModelsService } from 'base/common/language-models/languageModelsService'; +import { LANGUAGE_BLOCK_COMMENT_MAP } from 'base/common/languages/docstring'; +import { log } from 'base/common/log/log'; +import { MarkdownTextProcessor } from 'base/common/markdown/MarkdownTextProcessor'; +import { StreamingMarkdownCodeBlock } from 'base/common/markdown/StreamingMarkdownCodeBlock'; + +import { type NamedElement } from '../../editor/ast/NamedElement'; +import { insertCodeByRange, selectCodeInRange } from '../../editor/ast/PositionUtil'; +import { AutoDevStatus, AutoDevStatusManager } from '../../editor/editor-api/AutoDevStatusManager'; +import { ActionType } from '../../prompt-manage/ActionType'; +import { PromptManager } from '../../prompt-manage/PromptManager'; +import { CreateToolchainContext } from '../../toolchain-context/ToolchainContextProvider'; +import { ActionExecutor } from '../_base/ActionExecutor'; +import { CsharpClassExtractor } from 'src/code-context/csharp/model/CsharpClassExtractor'; +import { FrameworkCodeFragmentExtractor } from 'src/code-context/csharp/model/FrameworkCodeFragmentExtractor'; + +export class RemoveFrameworkCodeFragmentExecutor implements ActionExecutor { + type: ActionType = ActionType.AutoDoc; + + private lm: LanguageModelsService; + private promptManager: PromptManager; + private statusBarManager: AutoDevStatusManager; + + private document: TextDocument; + private range: NamedElement; + private edit?: WorkspaceEdit; + private language: string; + private autodev: AutoDevExtension; + + constructor(autodev: AutoDevExtension, document: TextDocument, range: NamedElement, edit?: WorkspaceEdit) { + this.lm = autodev.lm; + this.promptManager = autodev.promptManager; + this.statusBarManager = autodev.statusBarManager; + + this.document = document; + this.range = range; + this.edit = edit; + this.language = document.languageId; + this.autodev = autodev; + } + + async execute() { + + const document = this.document; + const range = this.range; + const language = document.languageId; + const frameworkCodeFragmentInfo=new FrameworkCodeFragmentExtractor(range.node,document.uri.fsPath) + .ExtractFrameworkCodeFragment(); + this.autodev.workSpace.RemoveDataStorage(language,frameworkCodeFragmentInfo) + this.statusBarManager.setStatus(AutoDevStatus.InProgress); + selectCodeInRange(range.blockRange.start, range.blockRange.end); + if (range.commentRange) { + selectCodeInRange(range.commentRange.start, range.commentRange.end); + } + } +} diff --git a/src/action/removeCodeSample/RemoveCodeSampleExecutor.ts b/src/action/removeCodeSample/RemoveCodeSampleExecutor.ts new file mode 100644 index 00000000..da744f2e --- /dev/null +++ b/src/action/removeCodeSample/RemoveCodeSampleExecutor.ts @@ -0,0 +1,63 @@ +import fs from 'fs'; +import { AutoDevExtension } from 'src/AutoDevExtension'; +import { BaseCsharpElement } from 'src/code-context/csharp/model/BaseCsharpElement'; +import { CsharpClassExtractor } from 'src/code-context/csharp/model/CsharpClassExtractor'; +import { Position, TextDocument, WorkspaceEdit } from 'vscode'; +import vscode from 'vscode'; +import { SyntaxNode } from 'web-tree-sitter'; + +import { ChatMessageRole, IChatMessage } from 'base/common/language-models/languageModels'; +import { LanguageModelsService } from 'base/common/language-models/languageModelsService'; +import { LANGUAGE_BLOCK_COMMENT_MAP } from 'base/common/languages/docstring'; +import { log } from 'base/common/log/log'; +import { MarkdownTextProcessor } from 'base/common/markdown/MarkdownTextProcessor'; +import { StreamingMarkdownCodeBlock } from 'base/common/markdown/StreamingMarkdownCodeBlock'; +import { IDataStorage } from 'base/common/workspace/WorkspaceService'; + +import { type NamedElement } from '../../editor/ast/NamedElement'; +import { insertCodeByRange, selectCodeInRange } from '../../editor/ast/PositionUtil'; +import { AutoDevStatus, AutoDevStatusManager } from '../../editor/editor-api/AutoDevStatusManager'; +import { ActionType } from '../../prompt-manage/ActionType'; +import { PromptManager } from '../../prompt-manage/PromptManager'; +import { CreateToolchainContext } from '../../toolchain-context/ToolchainContextProvider'; +import { ActionExecutor } from '../_base/ActionExecutor'; +import { CodeSample } from '../addCodeSample/AddCodeSampleExecutor'; + +export class RemoveCodeSampleExecutor implements ActionExecutor { + type: ActionType = ActionType.AutoDoc; + + private lm: LanguageModelsService; + private promptManager: PromptManager; + private statusBarManager: AutoDevStatusManager; + + private document: TextDocument; + private range: NamedElement; + private edit?: WorkspaceEdit; + private language: string; + private autodev: AutoDevExtension; + + constructor(autodev: AutoDevExtension, document: TextDocument, range: NamedElement, edit?: WorkspaceEdit) { + this.lm = autodev.lm; + this.promptManager = autodev.promptManager; + this.statusBarManager = autodev.statusBarManager; + + this.document = document; + this.range = range; + this.edit = edit; + this.language = document.languageId; + this.autodev = autodev; + } + + async execute() { + const document = this.document; + const range = this.range; + const language = document.languageId; + this.statusBarManager.setStatus(AutoDevStatus.InProgress); + + selectCodeInRange(range.blockRange.start, range.blockRange.end); + if (range.commentRange) { + selectCodeInRange(range.commentRange.start, range.commentRange.end); + } + this.autodev.workSpace.RemoveDataStorage(language,new CodeSample(range.node, document.uri.fsPath)); + } +} diff --git a/src/base/common/configuration/configuration.ts b/src/base/common/configuration/configuration.ts index dfb09a35..dd9417fd 100644 --- a/src/base/common/configuration/configuration.ts +++ b/src/base/common/configuration/configuration.ts @@ -30,7 +30,9 @@ export const CMD_CODELENS_CREATE_UNIT_TEST = 'autodev.codelens.autoTest'; export const CMD_CODELENS_SHOW_CUSTOM_ACTION = 'autodev.codelens.customAction'; export const CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS = 'autodev.codelens.methodCompletions'; export const CMD_CODELENS_SHOW_CODE_ADD_CODE_SAMPLE = 'autodev.codelen.addCodeSample'; +export const CMD_CODELENS_SHOW_CODE_REMOVE_CODE_SAMPLE = 'autodev.codelen.removeCodeSample'; export const CMD_CODELENS_SHOW_CODE_ADD_FRAMEWORK_CODE_FRAGMENT = 'autodev.codelen.addFrameworkCodeFragment'; +export const CMD_CODELENS_SHOW_CODE_REMOVE_FRAMEWORK_CODE_FRAGMENT = 'autodev.codelen.removeFrameworkCodeFragment'; // Chat Commands export const CMD_SHOW_CHAT_PANEL = 'autodev.showChatPanel'; export const CMD_QUICK_CHAT = 'autodev.quickChat'; diff --git a/src/base/common/workspace/WorkspaceService.ts b/src/base/common/workspace/WorkspaceService.ts index 3b87e5c9..b63c6323 100644 --- a/src/base/common/workspace/WorkspaceService.ts +++ b/src/base/common/workspace/WorkspaceService.ts @@ -2,10 +2,11 @@ import os from 'node:os'; import path from 'node:path'; import { inject, injectable } from 'inversify'; -import _, { forEach } from 'lodash'; +import _, { difference, forEach } from 'lodash'; import { data } from 'node_modules/cheerio/dist/commonjs/api/attributes'; -import { CodeSample } from 'src/action/AddCodeSample/AddCodeSampleExecutor'; +import { CodeSample } from 'src/action/addCodeSample/AddCodeSampleExecutor'; import { FrameworkCodeFragment } from 'src/code-context/csharp/model/FrameworkCodeFragmentExtractor'; +import vscode from 'vscode'; import { Disposable, type Event, EventEmitter, TextDocument, Uri, workspace } from 'vscode'; import { IExtensionContext } from '../configuration/context'; @@ -26,7 +27,7 @@ export class WorkspaceService { this._disposables = []; this._disposables.push(); this._listenerMap = new Map(); - + console.log('WorkspaceService :constructor'); workspace.onDidCloseTextDocument(document => { let language = document.languageId; let workspaceSerializer = new WorkspaceSerializer(document); @@ -35,7 +36,6 @@ export class WorkspaceService { if (languageDataStorageMap != undefined) { let dataStorages = languageDataStorageMap.get('CodeSample'); if (dataStorages != undefined) workspaceSerializer.saveObject(dataStorages, 'CodeSample', language); - console.log('test'); } } if (this._saveDataMap.has(language)) { @@ -44,11 +44,85 @@ export class WorkspaceService { let dataStorages = languageDataStorageMap.get('FrameworkCodeFragment'); if (dataStorages != undefined) workspaceSerializer.saveObject(dataStorages, 'FrameworkCodeFragment', language); - console.log('test'); } } }); - workspace.onDidOpenTextDocument(document => { + vscode.window.onDidChangeActiveTextEditor(editor => { + if (editor) { + let document = editor.document; + let language = document.languageId; + let workspaceSerializer = new WorkspaceSerializer(document); + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + if (languageDataStorageMap != undefined) { + let dataStorages = languageDataStorageMap.get('CodeSample'); + if (dataStorages != undefined) workspaceSerializer.saveObject(dataStorages, 'CodeSample', language); + } + } + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + if (languageDataStorageMap != undefined) { + let dataStorages = languageDataStorageMap.get('FrameworkCodeFragment'); + if (dataStorages != undefined) + workspaceSerializer.saveObject(dataStorages, 'FrameworkCodeFragment', language); + } + } + + this._saveDataMap.clear(); + let codeSamples = workspaceSerializer.loadObject(language, 'CodeSample'); + if (codeSamples != null) { + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + languageDataStorageMap?.set('CodeSample', codeSamples); + } else { + let languageDataStorageMap = new Map(); + languageDataStorageMap.set('CodeSample', codeSamples); + this._saveDataMap.set(language, languageDataStorageMap); + } + } + + let frameworkCodeFragments = workspaceSerializer.loadObject( + language, + 'FrameworkCodeFragment', + ); + if (frameworkCodeFragments != null) { + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + languageDataStorageMap?.set('FrameworkCodeFragment', frameworkCodeFragments); + } else { + let languageDataStorageMap = new Map(); + languageDataStorageMap.set('FrameworkCodeFragment', frameworkCodeFragments); + this._saveDataMap.set(language, languageDataStorageMap); + } + } + + vscode.commands.executeCommand('vscode.executeCodeLensProvider', document.uri); + } + }); + vscode.workspace.onDidSaveTextDocument((document) => { + let language = document.languageId; + let workspaceSerializer = new WorkspaceSerializer(document); + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + if (languageDataStorageMap != undefined) { + let dataStorages = languageDataStorageMap.get('CodeSample'); + if (dataStorages != undefined) workspaceSerializer.saveObject(dataStorages, 'CodeSample', language); + } + } + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + if (languageDataStorageMap != undefined) { + let dataStorages = languageDataStorageMap.get('FrameworkCodeFragment'); + if (dataStorages != undefined) + workspaceSerializer.saveObject(dataStorages, 'FrameworkCodeFragment', language); + } + } + vscode.commands.executeCommand('vscode.executeCodeLensProvider', document.uri); + }); + vscode.workspace.onDidOpenTextDocument(documentA => { + let editor = vscode.window.activeTextEditor; + if (editor == undefined) return; + let document = editor.document; let language = document.languageId; let workspaceSerializer = new WorkspaceSerializer(document); let codeSamples = workspaceSerializer.loadObject(language, 'CodeSample'); @@ -62,6 +136,7 @@ export class WorkspaceService { this._saveDataMap.set(language, languageDataStorageMap); } } + let frameworkCodeFragments = workspaceSerializer.loadObject( language, 'FrameworkCodeFragment', @@ -76,6 +151,7 @@ export class WorkspaceService { this._saveDataMap.set(language, languageDataStorageMap); } } + vscode.commands.executeCommand('vscode.executeCodeLensProvider', document.uri); }); } dispose(): void { @@ -122,27 +198,77 @@ export class WorkspaceService { let languageDataStorageMap = this._saveDataMap.get(language); if (languageDataStorageMap != undefined) { let dataStorages = languageDataStorageMap?.get(key); - let dataEqualed = dataStorages?.filter(item => { - return dataStorage.equals(item) == true; - }); - if (dataEqualed != undefined) { - if (dataEqualed.length > 0) return; + if (dataStorages != undefined) { + let dataEqualed = dataStorages?.filter(item => { + return dataStorage.equals(item) == true; + }); + if (dataEqualed != undefined) { + if (dataEqualed.length > 0) return; + } + dataStorages.push(dataStorage); + let dataDataStorageMap = new Map(); + dataDataStorageMap.set(key, dataStorages); + for (const [key, value] of languageDataStorageMap) { + dataDataStorageMap.set(key, value); + } + this._saveDataMap.set(language, dataDataStorageMap); + } else { + dataStorages = []; + dataStorages.push(dataStorage); + languageDataStorageMap.set(key, dataStorages); } - dataStorages?.push(dataStorage); } else { languageDataStorageMap = new Map(); - let dataStorages:IDataStorage[]=[] - dataStorages.push(dataStorage) - languageDataStorageMap.set(key,dataStorages); + let dataStorages: IDataStorage[] = []; + dataStorages.push(dataStorage); + languageDataStorageMap.set(key, dataStorages); } } else { const dataStorages: IDataStorage[] = []; dataStorages.push(dataStorage); let dataDataStorageMap = new Map(); dataDataStorageMap.set(key, dataStorages); + let temp = this._saveDataMap.get(language); + if (temp != undefined) { + for (const [key, value] of temp) { + dataDataStorageMap.set(key, value); + } + } + this._saveDataMap.set(language, dataDataStorageMap); } + let editor = vscode.window.activeTextEditor; + if (editor) { + let document = editor.document; + vscode.commands.executeCommand('vscode.executeCodeLensProvider', document.uri); + } } + public RemoveDataStorage(language: string, dataStorage: IDataStorage) { + if (!this._saveDataMap.has(language)) { + return; + } else { + let key = dataStorage.GetType(); + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + if (languageDataStorageMap != undefined) { + let dataStorages = languageDataStorageMap?.get(key); + let dataEqualed = dataStorages?.filter(item => { + return dataStorage.equals(item) == true; + }); + if (dataEqualed != undefined && dataStorages != undefined) { + let results = difference(dataEqualed, dataStorages); + languageDataStorageMap.set(key, results); + } + } + } + } + let editor = vscode.window.activeTextEditor; + if (editor) { + let document = editor.document; + vscode.commands.executeCommand('vscode.executeCodeLensProvider', document.uri); + } + } + public GetDataStorage(language: string, key: string): IDataStorage[] | undefined { let dataDataStorageMap = this._saveDataMap.get(language); if (dataDataStorageMap) { @@ -152,20 +278,23 @@ export class WorkspaceService { return undefined; } } - public RemoveDataStorage(language: string, key: string, dataStorage: IDataStorage) { - if (!this._saveDataMap.has(language)) return; - let languageDataStorageMap = this._saveDataMap.get(language); - let dataStorages = languageDataStorageMap?.get(key); - let newData = dataStorages?.reduce((acc, item) => { - if (dataStorage.equals(item) !== false) { - acc.push(item); + public HasDataStorage(language: string, dataStorage: IDataStorage): boolean { + let key = dataStorage.GetType(); + if (this._saveDataMap.has(language)) { + let languageDataStorageMap = this._saveDataMap.get(language); + if (languageDataStorageMap != undefined) { + let dataStorages = languageDataStorageMap?.get(key); + let dataEqualed = dataStorages?.filter(item => { + return dataStorage.equals(item) == true; + }); + if (dataEqualed != undefined) { + if (dataEqualed.length > 0) return true; + } } - return acc; - }, [] as IDataStorage[]); - if (newData) languageDataStorageMap?.set(key, newData); + } + return false; } } - export interface IDataStorage { Save(): void; Load(): void; diff --git a/src/code-context/csharp/model/FrameworkCodeFragmentExtractor.ts b/src/code-context/csharp/model/FrameworkCodeFragmentExtractor.ts index e66cc225..c2830f22 100644 --- a/src/code-context/csharp/model/FrameworkCodeFragmentExtractor.ts +++ b/src/code-context/csharp/model/FrameworkCodeFragmentExtractor.ts @@ -16,11 +16,11 @@ export class FrameworkCodeFragmentExtractor { let context: string = ''; let code: string = this.frameworkCodeFragmentNode.text; if (this.frameworkCodeFragmentNode.type === 'class_declaration') { - } else if (this.frameworkCodeFragmentNode.type === 'method_declaration') { + return new FrameworkCodeFragment(this.frameworkCodeFragmentNode, context, code, this.filePath); } return new FrameworkCodeFragment(this.frameworkCodeFragmentNode, context, code, this.filePath, (doc: string) => { let match = /(.*?)<\/summary>/g; - let matchResult= match.exec(doc) + let matchResult = match.exec(doc); if (matchResult) { return matchResult[0]; } @@ -39,11 +39,15 @@ export class FrameworkCodeFragment extends BaseCsharpElement implements IDataSto context: string, code: string, filePath: string, - docDealCallback: DocDealCallback, + docDealCallback?: DocDealCallback, ) { super(); if (frameworkCodeFragmentNode.previousSibling) { - this.doc = docDealCallback(this.Getcommits(frameworkCodeFragmentNode.previousSibling, []).reverse().toString()); + if (docDealCallback == undefined) { + this.doc = this.Getcommits(frameworkCodeFragmentNode.previousSibling, []).toString(); + } else { + this.doc = docDealCallback(this.Getcommits(frameworkCodeFragmentNode.previousSibling, []).reverse().toString()); + } } else { this.doc = ''; } diff --git a/src/code-context/csharp/model/MethodInfo.ts b/src/code-context/csharp/model/MethodInfo.ts index f1a7a320..aa21e712 100644 --- a/src/code-context/csharp/model/MethodInfo.ts +++ b/src/code-context/csharp/model/MethodInfo.ts @@ -9,6 +9,7 @@ export class MethodInfo extends BaseCsharpElement { name: string; parameters?: IParameterInfo[]; methodXmlDoc: string; + code:string; public constructor(methodNode: Parser.SyntaxNode) { super(); @@ -21,6 +22,7 @@ export class MethodInfo extends BaseCsharpElement { this.name = methodNameAndReturnType[0]; this.returnType = methodNameAndReturnType[1]; this.methodXmlDoc = methodXmlDocTeam.reverse().toString(); + this.code=methodNode.text; } // 获取方法的 XML 注释 getMethodXmlDocTeam(methodNode: Parser.SyntaxNode): string[] { From 5280e949b759cb124b0b7bd767c44157d220f282 Mon Sep 17 00:00:00 2001 From: Heipi <793181018@qq.com> Date: Thu, 7 Nov 2024 23:50:40 +0800 Subject: [PATCH 04/17] 1.create workspaceService to manage some datas form project 2.add CodeSample model 3.add FrameworkCodeFragment Model 4.add CodeSample and FrameworkCodeFragment manage Panel 5.create CodeSample and FrameworkCodeFragment support base --- .vscode/settings.json | 6 +- gui-sidebar/src/App.tsx | 6 + gui-sidebar/src/components/Layout.tsx | 13 + .../src/hooks/useNavigationListener.tsx | 13 + gui-sidebar/src/pages/codeContextPanel.tsx | 531 ++++++++++++++++++ gui-sidebar/src/shims/webviewProtocol.ts | 219 ++++---- l10n/bundle.l10n.zh-cn.json | 1 + package.json | 45 +- package.nls.json | 5 + package.nls.zh-cn.json | 5 + prompts/genius/zh-cn/code/auto-method.vm | 108 +++- src/AutoDevExtension.ts | 20 +- .../AddCodeSample/AddCodeSampleExecutor.ts | 83 +-- .../AddFrameworkCodeFragmentExecutor.ts | 5 +- .../autoMethod/AutoMethodActionExecutor.ts | 13 +- .../autoMethod/AutoMethodTemplateContext.ts | 45 +- .../providers/AutoDevCodeLensProvider.ts | 41 +- .../RemoveFrameworkCodeFragmentExecutor.ts | 4 +- .../RemoveCodeSampleExecutor.ts | 1 - .../common/configuration/configuration.ts | 3 + src/base/common/workspace/WorkspaceService.ts | 67 ++- .../ClassExtarctorFactory.ts | 24 + .../ClassELementFactory/FieldInfoFactory.ts | 26 + .../ClassELementFactory/MethodInfoFactory.ts | 24 + .../ClassElement/ClassExtractorBase.ts | 17 + .../ClassElement/ClassInfoBase.ts | 35 ++ .../ClassElement/CodeSampleExtractorBase.ts | 0 .../LanguageModel/ClassElement/ElementBase.ts | 9 + .../ClassElement/FieldInfoBase.ts | 21 + .../FrameworkCodeFragmentExtractorBase.ts | 89 +++ .../ClassElement/MethodInfoBase.ts | 26 + .../ClassElement/PropertyInfoBase.ts | 28 + .../csharp/model/BaseCsharpElement.ts | 18 - .../csharp/model/CodeSampleExtractor.ts | 18 +- .../csharp/model/CsharpClassExtractor.ts | 154 +++-- .../{FieldInfo.ts => CsharpFieldInfo.ts} | 37 +- .../CsharpFrameworkCodeFragmentExtractor.ts | 27 + .../{MethodInfo.ts => CsharpMethodInfo.ts} | 57 +- .../model/FrameworkCodeFragmentExtractor.ts | 80 --- src/code-context/csharp/model/PropertyInfo.ts | 74 ++- src/commands/commandsService.ts | 7 + src/editor/views/chat/chatViewService.ts | 8 +- .../views/chat/continue/continueMessages.ts | 30 +- .../chat/continue/continueViewProvider.ts | 68 ++- .../codeSample/codeSampleViewProvider.ts | 107 ++++ 45 files changed, 1781 insertions(+), 437 deletions(-) create mode 100644 gui-sidebar/src/pages/codeContextPanel.tsx create mode 100644 src/code-context/_base/LanguageModel/ClassELementFactory/ClassExtarctorFactory.ts create mode 100644 src/code-context/_base/LanguageModel/ClassELementFactory/FieldInfoFactory.ts create mode 100644 src/code-context/_base/LanguageModel/ClassELementFactory/MethodInfoFactory.ts create mode 100644 src/code-context/_base/LanguageModel/ClassElement/ClassExtractorBase.ts create mode 100644 src/code-context/_base/LanguageModel/ClassElement/ClassInfoBase.ts create mode 100644 src/code-context/_base/LanguageModel/ClassElement/CodeSampleExtractorBase.ts create mode 100644 src/code-context/_base/LanguageModel/ClassElement/ElementBase.ts create mode 100644 src/code-context/_base/LanguageModel/ClassElement/FieldInfoBase.ts create mode 100644 src/code-context/_base/LanguageModel/ClassElement/FrameworkCodeFragmentExtractorBase.ts create mode 100644 src/code-context/_base/LanguageModel/ClassElement/MethodInfoBase.ts create mode 100644 src/code-context/_base/LanguageModel/ClassElement/PropertyInfoBase.ts delete mode 100644 src/code-context/csharp/model/BaseCsharpElement.ts rename src/code-context/csharp/model/{FieldInfo.ts => CsharpFieldInfo.ts} (68%) create mode 100644 src/code-context/csharp/model/CsharpFrameworkCodeFragmentExtractor.ts rename src/code-context/csharp/model/{MethodInfo.ts => CsharpMethodInfo.ts} (69%) delete mode 100644 src/code-context/csharp/model/FrameworkCodeFragmentExtractor.ts create mode 100644 src/editor/views/codeSample/codeSampleViewProvider.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 83b13217..e320e23e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,7 +12,9 @@ "typescript.tsc.autoDetect": "off", "cmake.configureOnOpen": true, "cSpell.words": [ + "datas", "hoverable", "treesitter" - ] -} \ No newline at end of file + ], + "Codegeex.RepoIndex": true +} diff --git a/gui-sidebar/src/App.tsx b/gui-sidebar/src/App.tsx index 0bc0034c..57eeb94c 100644 --- a/gui-sidebar/src/App.tsx +++ b/gui-sidebar/src/App.tsx @@ -18,6 +18,8 @@ import useSubmenuContextProviders from "./hooks/useSubmenuContextProviders"; import { useVscTheme } from "./hooks/useVscTheme"; import Stats from "./pages/stats"; +import CodeContextPanel from "./pages/codeContextPanel"; + const router = createMemoryRouter([ { path: "/", @@ -35,6 +37,10 @@ const router = createMemoryRouter([ { path: "/history", element: , + }, + { + path: "/CodeContextPanel", + element: , }, { path: "/stats", diff --git a/gui-sidebar/src/components/Layout.tsx b/gui-sidebar/src/components/Layout.tsx index 7e136df4..40935e03 100644 --- a/gui-sidebar/src/components/Layout.tsx +++ b/gui-sidebar/src/components/Layout.tsx @@ -171,6 +171,19 @@ const Layout = () => { }, [location, navigate], ); + useWebviewListener( + "viewDataStorage", + async () => { + // Toggle the history page / main page + if (location.pathname === "/CodeContextPanel") { + navigate("/"); + } else { + navigate("/CodeContextPanel"); + } + }, + [location, navigate], + ); + useWebviewListener("indexProgress", async (data) => { setIndexingProgress(data.progress); diff --git a/gui-sidebar/src/hooks/useNavigationListener.tsx b/gui-sidebar/src/hooks/useNavigationListener.tsx index 0c4ef547..b95af7d9 100644 --- a/gui-sidebar/src/hooks/useNavigationListener.tsx +++ b/gui-sidebar/src/hooks/useNavigationListener.tsx @@ -47,4 +47,17 @@ export const useNavigationListener = () => { }, [location, navigate] ); + useWebviewListener( + "viewDataStorage", + async () => { + // Toggle the history page / main page + if (location.pathname === "/viewDataStorage") { + navigate("/"); + } else { + navigate("/codeContextPanel"); + } + }, + [location, navigate] + ); + }; diff --git a/gui-sidebar/src/pages/codeContextPanel.tsx b/gui-sidebar/src/pages/codeContextPanel.tsx new file mode 100644 index 00000000..4dd1d997 --- /dev/null +++ b/gui-sidebar/src/pages/codeContextPanel.tsx @@ -0,0 +1,531 @@ +import React, { useState, useEffect } from 'react'; +import styled from 'styled-components'; +import { ideRequest } from '../util/ide'; +import { useWebviewListener } from '../hooks/useWebviewListener'; + +interface CodeSample { + id: string; // 添加 id 成员 + filePath: string; + codeContext: string; + code: string; + doc: string; +} + +interface CodeContext { + id: string; // 添加 id 成员 + filePath: string; + codeContext: string; + code: string; + doc: string; +} + +interface Group { + name: string; + items: string[]; // 存储 id 而不是直接存储对象 +} + +const Container = styled.div` + font-family: Arial, sans-serif; + margin: 20px; + background-color: #000; /* 黑色背景 */ + color: #fff; /* 白色文本 */ +`; + +const Tabs = styled.div` + display: flex; + margin-bottom: 20px; +`; + +const Tab = styled.div<{ active: boolean }>` + padding: 10px 20px; + cursor: pointer; + border: 1px solid #ddd; + background-color: ${({ active }) => (active ? '#555' : '#333')}; /* 深灰色背景 */ + color: #fff; /* 白色文本 */ + margin-right: 10px; +`; + +const TabContent = styled.div<{ active: boolean }>` + display: ${({ active }) => (active ? 'block' : 'none')}; + padding: 20px; + border: 1px solid #555; + background-color: #222; /* 深灰色背景 */ + color: #fff; /* 白色文本 */ +`; + +const FormContainer = styled.div` + margin-top: 20px; + padding: 20px; + border: 1px solid #555; + background-color: #333; /* 深灰色背景 */ + color: #fff; /* 白色文本 */ +`; + +const Input = styled.input` + width: 100%; + padding: 10px; + margin-bottom: 10px; + box-sizing: border-box; + background-color: #444; /* 浅灰色背景 */ + color: #fff; /* 白色文本 */ + border: 1px solid #555; +`; + +const TextArea = styled.textarea` + width: 100%; + padding: 10px; + margin-bottom: 10px; + box-sizing: border-box; + background-color: #444; /* 浅灰色背景 */ + color: #fff; /* 白色文本 */ + border: 1px solid #555; +`; + +const Button = styled.button` + padding: 10px 20px; + cursor: pointer; + background-color: #555; /* 浅灰色背景 */ + color: #fff; /* 白色文本 */ + border: 1px solid #777; +`; + +const CodeContent = styled.div` + white-space: pre; /* 保留文本格式 */ + font-family: monospace; /* 使用等宽字体 */ + max-height: 200px; /* 设置最大高度 */ + overflow-y: auto; /* 添加纵向滚动条 */ + border: 1px solid #555; + padding: 10px; + box-sizing: border-box; + background-color: #444; /* 浅灰色背景 */ + color: #fff; /* 白色文本 */ +`; + +const CodeSample = styled.div` + margin-top: 20px; + padding: 20px; + border: 1px solid #555; + background-color: #333; /* 深灰色背景 */ + color: #fff; /* 白色文本 */ +`; + +const Actions = styled.div` + margin-top: 10px; + display: flex; + gap: 10px; +`; + +const Checkbox = styled.input.attrs({ type: 'checkbox' })` + margin-right: 10px; +`; + +const GroupNameModal = styled.div` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; +`; + +const GroupNameContent = styled.div` + background-color: #333; + padding: 20px; + border-radius: 5px; + width: 300px; +`; + +const GroupItem = styled.div` + margin-top: 20px; + padding: 20px; + border: 1px solid #555; + background-color: #333; /* 深灰色背景 */ + color: #fff; /* 白色文本 */ +`; + +const CodeContextPanel: React.FC = () => { + const [activeTab, setActiveTab] = useState<'CodeSample' | 'FrameworkCodeFragment' | 'Groups'>('CodeSample'); + const [codeSamples, setCodeSamples] = useState([]); + const [codeContexts, setCodeContexts] = useState([]); + const [groups, setGroups] = useState([]); + const [formData, setFormData] = useState({ + id: '', // 初始化 id + filePath: '', + code: '', + doc: '', + codeContext: '', + }); + const [formTitle, setFormTitle] = useState('添加代码样例'); + const [editIndex, setEditIndex] = useState(null); + const [originalItem, setOriginalItem] = useState(null); + const [selectedItems, setSelectedItems] = useState([]); // 存储 id 而不是索引 + const [showGroupNameModal, setShowGroupNameModal] = useState(false); + const [groupName, setGroupName] = useState(''); + + // 从IDE获取数据 + useEffect(() => { + ideRequest("WorkspaceService.GetDataStorage", "CodeSample"); + ideRequest("WorkspaceService.GetDataStorage", "FrameworkCodeFragment"); + }, []); + + // 监听从IDE发送的数据 + useWebviewListener("WorkspaceService_GetDataStorage", async (data) => { + switch (data.key) { + case "CodeSample": + let temp = JSON.parse(data.storages) as CodeSample[]; + setCodeSamples(temp); + break; + case "FrameworkCodeFragment": + let temp2 = JSON.parse(data.storages) as CodeContext[]; + setCodeContexts(temp2); + break; + default: + break; + } + }); + + // 监听从IDE发送的数据 + useWebviewListener("WorkspaceService_AddDataStorage", async (data) => { + refreshItems(); + }); + + const refreshItems = () => { + ideRequest("WorkspaceService.GetDataStorage", "CodeSample"); + ideRequest("WorkspaceService.GetDataStorage", "FrameworkCodeFragment"); + }; + + const handleTabChange = (tabName: 'CodeSample' | 'FrameworkCodeFragment' | 'Groups') => { + setActiveTab(tabName); + setFormTitle(tabName === 'CodeSample' ? '添加代码样例' : tabName === 'FrameworkCodeFragment' ? '添加代码上下文' : '管理编组'); + clearForm(); + setSelectedItems([]); // 切换标签时清空选中的项 + }; + + const handleInputChange = (event: React.ChangeEvent) => { + const { name, value } = event.target; + setFormData({ ...formData, [name]: value }); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + + if (editIndex === null) { + // 添加新样例或上下文 + const newItem = { + id: Date.now().toString(), // 使用时间戳作为唯一 id + filePath: formData.filePath, + code: formData.code, + doc: formData.doc, + codeContext: formData.codeContext, + }; + + if (activeTab === 'CodeSample') { + setCodeSamples([...codeSamples, newItem as CodeSample]); + } else { + setCodeContexts([...codeContexts, newItem as CodeContext]); + } + let dataformat = { + key: activeTab, + originalItem: JSON.stringify(newItem), + } + ideRequest("WorkspaceService.AddDataStorage", dataformat); + } else { + // 更新现有样例或上下文的说明 + const items = activeTab === 'CodeSample' ? codeSamples : codeContexts; + items[editIndex] = { + id: formData.id, + filePath: formData.filePath, + code: formData.code, + doc: formData.doc, + codeContext: formData.codeContext, + }; + + if (activeTab === 'CodeSample') { + setCodeSamples([...items]); + } else { + setCodeContexts([...items]); + } + + let originalItemJson = JSON.stringify(originalItem); + let formDataJson = JSON.stringify(formData); + let dataformat = { + key: activeTab, + originalItem: originalItemJson, + newItem: formDataJson + } + ideRequest("WorkspaceService.ChangeDataStorage", dataformat); + // 打印编辑前后的数据 + console.log('编辑前的数据:', originalItem); + console.log('编辑后的数据:', formData); + } + + clearForm(); + }; + + const editItem = (index: number) => { + const items = activeTab === 'CodeSample' ? codeSamples : codeContexts; + const item = items[index]; + setFormData({ + id: item.id, + filePath: item.filePath, + code: item.code, + doc: item.doc, + codeContext: formData.codeContext, + }); + setOriginalItem({ ...item }); // 保存编辑前的数据 + setEditIndex(index); + setFormTitle(activeTab === 'CodeSample' ? '编辑样例说明' : '编辑上下文说明'); + }; + + const deleteItem = (index: number) => { + if (activeTab === 'CodeSample') { + let dataformat = { + key: activeTab, + originalItem: JSON.stringify(codeSamples[index]), + } + ideRequest("WorkspaceService.RemoveDataStorage", dataformat); + setCodeSamples(codeSamples.filter((_, i) => i !== index)); + } else { + let dataformat = { + key: activeTab, + originalItem: JSON.stringify(codeContexts[index]), + } + ideRequest("WorkspaceService.RemoveDataStorage", dataformat); + setCodeContexts(codeContexts.filter((_, i) => i !== index)); + } + }; + + const clearForm = () => { + setFormData({ + id: '', + filePath: '', + code: '', + doc: '', + codeContext: '', + }); + setOriginalItem(null); // 清空编辑前的数据 + setEditIndex(null); + setFormTitle(activeTab === 'CodeSample' ? '添加代码样例' : '添加代码上下文'); + }; + + const handleCheckboxChange = (id: string) => { + if (selectedItems.includes(id)) { + setSelectedItems(selectedItems.filter(i => i !== id)); + } else { + setSelectedItems([...selectedItems, id]); + } + }; + + const handleGroupItems = () => { + setShowGroupNameModal(true); + }; + + const handleGroupNameChange = (event: React.ChangeEvent) => { + setGroupName(event.target.value); + }; + + const handleGroupNameSubmit = () => { + const newGroup: Group = { + name: groupName, + items: selectedItems, + }; + setGroups([...groups, newGroup]); + setShowGroupNameModal(false); + setSelectedItems([]); + setGroupName(''); + }; + + const deleteGroup = (index: number) => { + setGroups(groups.filter((_, i) => i !== index)); + }; + + const getItemById = (id: string) => { + const sample = codeSamples.find(sample => sample.id === id); + if (sample) return sample; + const context = codeContexts.find(context => context.id === id); + return context; + }; + + return ( + +

代码样例管理

+ + + handleTabChange('CodeSample')}> + 代码样例 + + handleTabChange('FrameworkCodeFragment')}> + 代码上下文 + + handleTabChange('Groups')}> + 编组管理 + + + + + +

{formTitle}

+
+ +