(list: &[T]) -> &T {
+ let mut largest = &list[0];
+ for item in list.iter() {
+ if item > largest {
+ largest = item;
+ }
+ }
+ largest
+}
+
+let numbers = vec![34, 50, 25, 100, 65];
+println!("The largest number is {}", largest(&numbers));
+
+trait Summary {
+ fn summarize(&self) -> String;
+}
+
+struct Article {
+ title: String,
+ content: String,
+}
+
+impl Summary for Article {
+ fn summarize(&self) -> String {
+ format!("{}: {}", self.title, &self.content[..20])
+ }
+}
+
+let article = Article {
+ title: String::from("Rust Programming"),
+ content: String::from("Rust is a systems programming language focused on safety, speed, and concurrency."),
+};
+
+println!("New article available! {}", article.summarize());
diff --git a/gui-sidebar/src/components/mainInput/ContinueButton.tsx b/gui-sidebar/src/components/mainInput/ContinueButton.tsx
index b550aec4..cebbcfdb 100644
--- a/gui-sidebar/src/components/mainInput/ContinueButton.tsx
+++ b/gui-sidebar/src/components/mainInput/ContinueButton.tsx
@@ -83,7 +83,7 @@ function ContinueButton(props: {
) : (
)}
- CONTINUE
+ AutoDev
>
)}
diff --git a/gui-sidebar/src/pages/error.tsx b/gui-sidebar/src/pages/error.tsx
index 88504398..52980c84 100644
--- a/gui-sidebar/src/pages/error.tsx
+++ b/gui-sidebar/src/pages/error.tsx
@@ -16,12 +16,12 @@ export default function ErrorPage() {
className="text-center"
style={{ backgroundColor: vscBackground }}
>
- Error in Continue React App
+ Error in AutoDev Debug page
{error.statusText || error.message}
- Click below to Continue
+ Click below to continue
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}
+```
+#if($context.originalComments.length > 0)
+Here is code Origin comment: ${context.originalComments}
+Please according to the code to update documentation.
+#end
+Please write documentation for user's code inside the Markdown code block.
diff --git a/prompts/genius/zh-cn/code/auto-doc.vm b/prompts/genius/zh-cn/code/auto-doc.vm
index 81d8fde6..c5226fad 100644
--- a/prompts/genius/zh-cn/code/auto-doc.vm
+++ b/prompts/genius/zh-cn/code/auto-doc.vm
@@ -10,5 +10,4 @@ Here is User's code:
```${context.language}
${context.code}
```
-
Please write documentation for this code inside the Markdown code block.
diff --git a/prompts/genius/zh-cn/code/auto-method.vm b/prompts/genius/zh-cn/code/auto-method.vm
new file mode 100644
index 00000000..81d8fde6
--- /dev/null
+++ b/prompts/genius/zh-cn/code/auto-method.vm
@@ -0,0 +1,14 @@
+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.
diff --git a/src/AutoDevExtension.ts b/src/AutoDevExtension.ts
index b67dc461..baaa6bdc 100644
--- a/src/AutoDevExtension.ts
+++ b/src/AutoDevExtension.ts
@@ -10,6 +10,8 @@ import { logger } from 'base/common/log/log';
import { AutoDocActionExecutor } from './action/autodoc/AutoDocActionExecutor';
import { AutoTestActionExecutor } from './action/autotest/AutoTestActionExecutor';
+import { AutoMethodActionExecutor } from './action/autoMethod/AutoMethodActionExecutor';
+
import {
registerAutoDevProviders,
registerCodeLensProvider,
@@ -19,7 +21,7 @@ import {
} from './action/ProviderRegister';
import { SystemActionService } from './action/setting/SystemActionService';
import { Catalyser } from './agent/catalyser/Catalyser';
-import { LanguageModelsService } from './base/common/language-models/languageModelsService';
+import { LanguageModelsService } from 'base/common/language-models/languageModelsService';
import { ChunkerManager } from './code-search/chunk/ChunkerManager';
import { CodebaseIndexer } from './code-search/indexing/CodebaseIndexer';
import { LanceDbIndex } from './code-search/indexing/LanceDbIndex';
@@ -42,6 +44,7 @@ import { TemplateRender } from './prompt-manage/template/TemplateRender';
import { IProjectService } from './ProviderTypes';
import { ToolchainContextManager } from './toolchain-context/ToolchainContextManager';
+
@injectable()
export class AutoDevExtension {
// Vscode
@@ -223,6 +226,9 @@ export class AutoDevExtension {
executeAutoDocAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) {
return new AutoDocActionExecutor(this, document, nameElement, edit).execute();
}
+ executeAutoMethodAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) {
+ return new AutoMethodActionExecutor(this, document, nameElement, edit).execute();
+ }
executeAutoTestAction(document: TextDocument, nameElement: NamedElement, edit?: WorkspaceEdit) {
return new AutoTestActionExecutor(this, document, nameElement, edit).execute();
diff --git a/src/ProviderContainer.config.ts b/src/ProviderContainer.config.ts
index b9ba3f91..9044754a 100644
--- a/src/ProviderContainer.config.ts
+++ b/src/ProviderContainer.config.ts
@@ -34,9 +34,12 @@ import { JavaScriptContextProvider } from './toolchain-context/framework/javascr
import { SpringContextProvider } from './toolchain-context/framework/jvm/SpringContextProvider';
import { ToolchainContextProvider } from './toolchain-context/ToolchainContextProvider';
import { JavaVersionProvider } from './toolchain-context/version/JavaVersionProvider';
+import { AutoMethodActionCreator } from './action/autoMethod/AutoMethodActionCreator';
+import { KotlinStructurerProvider } from "./code-context/kotlin/KotlinStructurerProvider";
// Action Register
providerContainer.bind(IActionCreator).to(AutoDocActionCreator);
+providerContainer.bind(IActionCreator).to(AutoMethodActionCreator);
providerContainer.bind(IActionCreator).to(AutoTestActionCreator);
providerContainer.bind(IActionCreator).to(GenApiDataActionCreator);
@@ -59,6 +62,7 @@ providerContainer.bind(IRelevantCodeProvider).to(JavaRelevantCodeProvider);
providerContainer.bind(ITestGenProvider).to(JavaTestGenProvider);
providerContainer.bind(IBuildToolProvider).to(GradleBuildToolProvider);
providerContainer.bind(IStructurerProvider).to(JavaStructurerProvider);
+providerContainer.bind(IStructurerProvider).to(KotlinStructurerProvider);
// TypeScript
providerContainer.bind(IToolchainContextProvider).to(JavaScriptContextProvider);
diff --git a/src/ProviderLanguageProfile.config.ts b/src/ProviderLanguageProfile.config.ts
index 1a8c7dad..d33ce8c8 100644
--- a/src/ProviderLanguageProfile.config.ts
+++ b/src/ProviderLanguageProfile.config.ts
@@ -3,8 +3,11 @@ import { Container } from 'inversify';
import { GolangProfile } from './code-context/go/GolangProfile';
import { JavaProfile } from './code-context/java/JavaProfile';
import { PythonProfile } from './code-context/python/PythonProfile';
+import { RustProfile } from './code-context/rust/RustProfile';
import { TypeScriptProfile } from './code-context/typescript/TypeScriptProfile';
import { ILanguageProfile } from './ProviderTypes';
+import { CsharpProfile } from './code-context/csharp/CsharpProfile';
+import { KotlinProfile } from './code-context/kotlin/KotlinProfile';
const languageContainer = new Container();
@@ -12,5 +15,8 @@ languageContainer.bind(ILanguageProfile).to(JavaProfile);
languageContainer.bind(ILanguageProfile).to(TypeScriptProfile);
languageContainer.bind(ILanguageProfile).to(GolangProfile);
languageContainer.bind(ILanguageProfile).to(PythonProfile);
+languageContainer.bind(ILanguageProfile).to(CsharpProfile);
+languageContainer.bind(ILanguageProfile).to(RustProfile);
+languageContainer.bind(ILanguageProfile).to(KotlinProfile);
export { languageContainer };
diff --git a/src/action/autoMethod/AutoMethodActionCreator.ts b/src/action/autoMethod/AutoMethodActionCreator.ts
new file mode 100644
index 00000000..f69a4e97
--- /dev/null
+++ b/src/action/autoMethod/AutoMethodActionCreator.ts
@@ -0,0 +1,40 @@
+import { injectable } from 'inversify';
+import vscode from 'vscode';
+
+import { NamedElement } from '../../editor/ast/NamedElement';
+import { ActionCreatorContext } from '../_base/ActionCreatorContext';
+import { CodeActionCreator } from '../_base/CodeActionCreator';
+import { CMD_GEN_CODE_METHOD_COMPLETIONS } from 'base/common/configuration/configuration';
+
+@injectable()
+export class AutoMethodActionCreator extends CodeActionCreator {
+ static readonly providedCodeActionKinds = [vscode.CodeActionKind.RefactorRewrite];
+
+ isApplicable(creatorContext: ActionCreatorContext): boolean {
+ return true;
+ }
+
+ buildClassAction(context: ActionCreatorContext, elementBlock: NamedElement) {
+ const title = `AutoDoc for class \`${elementBlock.identifierRange.text}\` (AutoDev)`;
+ return this.createMethodAction(title, context.document, elementBlock);
+ }
+
+ buildMethodAction(context: ActionCreatorContext, elementBlock: NamedElement): vscode.CodeAction {
+ const title = `AutoDoc for method \`${elementBlock.identifierRange.text}\` (AutoDev)`;
+ return this.createMethodAction(title, context.document, elementBlock);
+ }
+
+ private createMethodAction(title: string, document: vscode.TextDocument, block: NamedElement): vscode.CodeAction {
+ const codeAction = new vscode.CodeAction(title, AutoMethodActionCreator.providedCodeActionKinds[0]);
+
+ codeAction.isPreferred = false;
+ codeAction.edit = new vscode.WorkspaceEdit();
+ codeAction.command = {
+ command: CMD_GEN_CODE_METHOD_COMPLETIONS,
+ title: title,
+ arguments: [document, block, codeAction.edit],
+ };
+
+ return codeAction;
+ }
+}
diff --git a/src/action/autoMethod/AutoMethodActionExecutor.ts b/src/action/autoMethod/AutoMethodActionExecutor.ts
new file mode 100644
index 00000000..a8eaff29
--- /dev/null
+++ b/src/action/autoMethod/AutoMethodActionExecutor.ts
@@ -0,0 +1,139 @@
+import { AutoDevExtension } from 'src/AutoDevExtension';
+import { Position, TextDocument, WorkspaceEdit } 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 { AutoMethodTemplateContext } from './AutoMethodTemplateContext';
+import fs from 'fs'
+import vscode from 'vscode';
+export class AutoMethodActionExecutor 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;
+
+ 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;
+ }
+
+ 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 config = vscode.workspace.getConfiguration('autodev.Workspace');
+ 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];
+ // add support for linux
+ customFrameworkCodeFileContext += fs.readFileSync(codeFileNormalPath).toString();
+ customFrameworkCodeFileContext += '\n'
+ }
+ }
+ }
+
+ const templateContext: AutoMethodTemplateContext = {
+ language: language,
+ startSymbol: startSymbol,
+ endSymbol: endSymbol,
+ code: document.getText(range.blockRange),
+ forbiddenRules: [],
+ // 原有代码块
+ originalMethodCodes: [],
+ customFrameworkCodeFileContext
+ };
+
+ if (range.commentRange) {
+ templateContext.originalMethodCodes.push(document.getText(range.commentRange));
+ }
+
+ this.statusBarManager.setStatus(AutoDevStatus.InProgress);
+
+ selectCodeInRange(range.blockRange.start, range.blockRange.end);
+ if (range.commentRange) {
+ selectCodeInRange(range.commentRange.start, range.commentRange.end);
+ }
+
+ const creationContext: CreateToolchainContext = {
+ action: 'AutoMethodAction',
+ filename: document.fileName,
+ language: language,
+
+ content: document.getText(),
+ element: range,
+ };
+
+ const contextItems = await this.promptManager.collectToolchain(creationContext);
+ if (contextItems.length > 0) {
+ templateContext.chatContext = contextItems.map(item => item.text).join('\n - ');
+ }
+
+ let content = await this.promptManager.generateInstruction(ActionType.AutoMethod, templateContext);
+ log(`request: ${content}`);
+
+ let msg: IChatMessage = {
+ role: ChatMessageRole.User,
+ content: content,
+ };
+
+ try {
+ const doc = await this.lm.chat([msg], {});
+
+ this.statusBarManager.setStatus(AutoDevStatus.Done);
+ const finalText = StreamingMarkdownCodeBlock.parse(doc).text;
+
+ log(`FencedCodeBlock parsed output: ${finalText}`);
+
+ let codestring = MarkdownTextProcessor.buildDocFromSuggestion(doc, startSymbol, endSymbol);
+
+ let startLine = range.blockRange.start.line;
+ let startChar = range.blockRange.start.character;
+
+ if (startLine === 0) {
+ startLine = 1;
+ }
+
+ // todo: add format by indent.
+
+ const textRange: Position = new Position(startLine - 1, startChar);
+ insertCodeByRange(textRange, codestring);
+ } catch (e) {
+ console.error(e);
+ this.statusBarManager.setStatus(AutoDevStatus.Error);
+ return;
+ }
+ }
+}
diff --git a/src/action/autoMethod/AutoMethodTemplateContext.ts b/src/action/autoMethod/AutoMethodTemplateContext.ts
new file mode 100644
index 00000000..0a4801d5
--- /dev/null
+++ b/src/action/autoMethod/AutoMethodTemplateContext.ts
@@ -0,0 +1,10 @@
+import { TemplateContext } from '../../prompt-manage/template/TemplateContext';
+
+export interface AutoMethodTemplateContext extends TemplateContext {
+ startSymbol: string;
+ endSymbol: string;
+ code: string;
+ forbiddenRules: string[];
+ originalMethodCodes: string[];
+ customFrameworkCodeFileContext?:string;
+}
diff --git a/src/action/providers/AutoDevCodeInlineCompletionProvider.ts b/src/action/providers/AutoDevCodeInlineCompletionProvider.ts
index 68d869ff..b9ea4d56 100644
--- a/src/action/providers/AutoDevCodeInlineCompletionProvider.ts
+++ b/src/action/providers/AutoDevCodeInlineCompletionProvider.ts
@@ -116,7 +116,8 @@ export class AutoDevCodeInlineCompletionProvider implements vscode.InlineComplet
}
if (result) {
- return [new vscode.InlineCompletionItem(result.trimStart())];
+ // return [new vscode.InlineCompletionItem(result.trimStart())];
+ return [new vscode.InlineCompletionItem(result)];
}
} catch (error) {
if (!token.isCancellationRequested) {
diff --git a/src/action/providers/AutoDevCodeLensProvider.ts b/src/action/providers/AutoDevCodeLensProvider.ts
index 949a18fe..5b7d9b61 100644
--- a/src/action/providers/AutoDevCodeLensProvider.ts
+++ b/src/action/providers/AutoDevCodeLensProvider.ts
@@ -27,6 +27,7 @@ import {
CMD_CODELENS_QUICK_CHAT,
CMD_CODELENS_SHOW_CUSTOM_ACTION,
CMD_SHOW_CODELENS_DETAIL_QUICKPICK,
+ CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS,
} from 'base/common/configuration/configuration';
import { ConfigurationService } from 'base/common/configuration/configurationService';
import { isFileTooLarge } from 'base/common/files/files';
@@ -35,8 +36,9 @@ 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' | 'autoTest' | 'customAction';
+type CodeLensItemType = 'quickChat' | 'explainCode' | 'optimizeCode' | 'autoComment' | 'autoTest' | 'customAction'|'AutoMethod';
export class AutoDevCodeLensProvider implements CodeLensProvider {
private config: ConfigurationService;
@@ -123,6 +125,9 @@ 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);
+ }),
];
}
@@ -153,6 +158,7 @@ export class AutoDevCodeLensProvider implements CodeLensProvider {
}
const elements = await this.parseToNamedElements(document);
+// elements为空导致codelens组没有数据,无法生成codelens
if (token.isCancellationRequested || elements.length === 0) {
return [];
@@ -250,7 +256,6 @@ export class AutoDevCodeLensProvider implements CodeLensProvider {
}
continue;
}
-
if (type === 'customAction') {
if (hasCustomPromps) {
codelenses.push(
@@ -263,6 +268,19 @@ export class AutoDevCodeLensProvider implements CodeLensProvider {
}
continue;
}
+ if (type === 'AutoMethod') {
+ if (element.codeElementType==CodeElementType.Method) {
+ codelenses.push(
+ new CodeLens(element.identifierRange, {
+ title: l10n.t('AutoMethod'),
+ command: CMD_CODELENS_SHOW_CODE_METHOD_COMPLETIONS,
+ arguments: [document, element],
+ }),
+ );
+ }
+ continue;
+ }
+
}
result.push(codelenses);
diff --git a/src/base/common/configuration/configuration.ts b/src/base/common/configuration/configuration.ts
index 8c198d9e..18c6947f 100644
--- a/src/base/common/configuration/configuration.ts
+++ b/src/base/common/configuration/configuration.ts
@@ -18,6 +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_CREATE_UNIT_TEST = 'autodev.autoTest';
// Codelens Commands
@@ -27,6 +28,7 @@ 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';
// Chat Commands
export const CMD_SHOW_CHAT_PANEL = 'autodev.showChatPanel';
diff --git a/src/base/common/instantiation/instantiation.ts b/src/base/common/instantiation/instantiation.ts
index cb72e64c..17ccef2f 100644
--- a/src/base/common/instantiation/instantiation.ts
+++ b/src/base/common/instantiation/instantiation.ts
@@ -1,4 +1,4 @@
-import { type interfaces, LazyServiceIdentifer } from 'inversify';
+import { type interfaces, LazyServiceIdentifier } from 'inversify';
export type Newable = interfaces.Newable;
@@ -6,9 +6,9 @@ export type ServiceIdentifier = interfaces.ServiceIdentifier;
export type FactoryFunction = () => interfaces.ServiceIdentifier;
-export type ServiceDescriptor = T | LazyServiceIdentifer | FactoryFunction;
+export type ServiceDescriptor = T | LazyServiceIdentifier | FactoryFunction;
-export { LazyServiceIdentifer };
+export { LazyServiceIdentifier };
export interface ServicesAccessor {
get(id: ServiceIdentifier): T;
@@ -16,7 +16,7 @@ export interface ServicesAccessor {
const _registry: [ServiceIdentifier, ServiceDescriptor][] = [];
-export function registerSingleton(id: ServiceIdentifier, useValue: T | LazyServiceIdentifer): void;
+export function registerSingleton(id: ServiceIdentifier, useValue: T | LazyServiceIdentifier): void;
export function registerSingleton(id: ServiceIdentifier, useFactory: FactoryFunction): void;
export function registerSingleton(
id: ServiceIdentifier,
@@ -25,16 +25,16 @@ export function registerSingleton(
): void;
export function registerSingleton(
id: ServiceIdentifier,
- ctorOrDescriptor: T | LazyServiceIdentifer | Newable | FactoryFunction,
+ ctorOrDescriptor: T | LazyServiceIdentifier | Newable | FactoryFunction,
supportsDelayedInstantiation?: boolean,
): void {
- if (ctorOrDescriptor instanceof LazyServiceIdentifer) {
+ if (ctorOrDescriptor instanceof LazyServiceIdentifier) {
_registry.push([id, ctorOrDescriptor]);
return;
}
if (typeof ctorOrDescriptor === 'function' && supportsDelayedInstantiation) {
- _registry.push([id, new LazyServiceIdentifer(() => ctorOrDescriptor as Newable)]);
+ _registry.push([id, new LazyServiceIdentifier(() => ctorOrDescriptor as Newable)]);
return;
}
diff --git a/src/base/common/instantiation/instantiationService.ts b/src/base/common/instantiation/instantiationService.ts
index 9c64c66b..a1f77427 100644
--- a/src/base/common/instantiation/instantiationService.ts
+++ b/src/base/common/instantiation/instantiationService.ts
@@ -1,8 +1,11 @@
-import { Container, type interfaces, LazyServiceIdentifer } from 'inversify';
+import { Container, type interfaces, LazyServiceIdentifier } from 'inversify';
+
+
import { isDisposable } from '../lifecycle';
import { getSingletonServiceDescriptors, type ServiceIdentifier } from './instantiation';
+
export const providerContainer = new Container();
export class InstantiationService {
@@ -13,7 +16,7 @@ export class InstantiationService {
for (const [identifier, descriptor] of getSingletonServiceDescriptors()) {
const binding = providerContainer.bind(identifier);
- if (descriptor instanceof LazyServiceIdentifer) {
+ if (descriptor instanceof LazyServiceIdentifier) {
binding.toDynamicValue(() => descriptor.unwrap()).inSingletonScope();
} else {
binding.toConstantValue(descriptor);
@@ -104,4 +107,4 @@ export class InstantiationService {
throw new Error('InstantiationService has been disposed');
}
}
-}
+}
\ No newline at end of file
diff --git a/src/base/common/language-models/languageModelsService.ts b/src/base/common/language-models/languageModelsService.ts
index 2ec3cbc6..c3dbd443 100644
--- a/src/base/common/language-models/languageModelsService.ts
+++ b/src/base/common/language-models/languageModelsService.ts
@@ -5,7 +5,6 @@ import { EMBEDDING_BATCH_SIZE } from '../configuration/configuration';
import { ConfigurationService } from '../configuration/configurationService';
import { IChatMessage, IChatResponseFragment, ILanguageModelProvider } from './languageModels';
import { AnthropicLanguageModelProvider } from './providers/anthropicProvider';
-import { HuggingFaceTransformersLanguageModelProvider } from './providers/hgTransformersProvider';
import { OllamaLanguageModelProvider } from './providers/ollamaProvider';
import { OpenAILanguageModelProvider } from './providers/openaiProvider';
import { TongyiLanguageModelProvider } from './providers/TongyiProvider';
diff --git a/src/base/common/language-models/providers/hgTransformersProvider.ts b/src/base/common/language-models/providers/hgTransformersProvider.ts
index 73ee942f..2db2a6c6 100644
--- a/src/base/common/language-models/providers/hgTransformersProvider.ts
+++ b/src/base/common/language-models/providers/hgTransformersProvider.ts
@@ -3,9 +3,7 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { chunkArray } from '@langchain/core/utils/chunk_array';
-import {
- type FeatureExtractionPipeline, // @ts-expect-error vite esm => cjs
-} from '@xenova/transformers';
+import { type FeatureExtractionPipeline } from '@xenova/transformers';
import { type CancellationToken } from 'vscode';
import { ConfigurationService } from '../../configuration/configurationService';
diff --git a/src/base/common/language-models/providers/openaiProvider.ts b/src/base/common/language-models/providers/openaiProvider.ts
index 02795196..3f2e3397 100644
--- a/src/base/common/language-models/providers/openaiProvider.ts
+++ b/src/base/common/language-models/providers/openaiProvider.ts
@@ -48,10 +48,10 @@ export class OpenAILanguageModelProvider implements ILanguageModelProvider {
for await (const chunk of completion) {
const [choice] = chunk.choices || [];
- part = choice.delta.content;
+ part = choice.delta.content || '';
// Note: Empty if finish_reason exists.
- if (choice.finish_reason || part == null) {
+ if (choice.finish_reason) {
break;
}
@@ -209,7 +209,7 @@ export class OpenAILanguageModelProvider implements ILanguageModelProvider {
return model;
}
- return this.configService.get('openai.model', 'gpt-3.5-turbo');
+ return this.configService.get('openai.model', 'gpt-4o-mini');
}
private _resolveComletionModel(model?: string) {
diff --git a/src/base/common/language-models/providers/zhipuaiProvider.ts b/src/base/common/language-models/providers/zhipuaiProvider.ts
index b07901d9..f4f6b00a 100644
--- a/src/base/common/language-models/providers/zhipuaiProvider.ts
+++ b/src/base/common/language-models/providers/zhipuaiProvider.ts
@@ -60,10 +60,10 @@ export class ZhipuAILanguageModelProvider implements ILanguageModelProvider {
for await (const chunk of completion) {
const [choice] = chunk.choices || [];
- part = choice.delta.content;
+ part = choice.delta.content || '';
// Note: Empty if finish_reason exists.
- if (choice.finish_reason || part == null) {
+ if (choice.finish_reason) {
break;
}
diff --git a/src/base/common/languages/languageService.ts b/src/base/common/languages/languageService.ts
index 434e4bd5..4da43fad 100644
--- a/src/base/common/languages/languageService.ts
+++ b/src/base/common/languages/languageService.ts
@@ -45,7 +45,7 @@ export class LanguageServiceProvider implements ILanguageServiceProvider {
}
async parse(identifier: LanguageIdentifier, input: string) {
- if (this.isSupportLanguage(identifier)) {
+ if (!this.isSupportLanguage(identifier)) {
return;
}
diff --git a/src/base/common/languages/languages.ts b/src/base/common/languages/languages.ts
index 67f6e0ab..01d22c6a 100644
--- a/src/base/common/languages/languages.ts
+++ b/src/base/common/languages/languages.ts
@@ -11,7 +11,7 @@ type SupportedLanguage =
| 'python'
| 'rust'
| 'javascript'
- // | 'javascriptreact'
+ | 'javascriptreact'
| 'typescript'
| 'typescriptreact';
@@ -27,7 +27,7 @@ export const SUPPORTED_LANGUAGES: LanguageIdentifier[] = [
'python',
'rust',
'javascript',
- // 'javascriptreact',
+ 'javascriptreact',
'typescript',
'typescriptreact',
];
@@ -55,7 +55,7 @@ const SupportLanguagesList: LanguageItem[] = [
{ languageId: 'java', fileExts: ['.java'] },
{
languageId: 'kotlin',
- fileExts: ['.kt, .kts, .ktm'],
+ fileExts: ['.kt', '.kts', '.ktm'],
},
{ languageId: 'python', fileExts: ['.py'] },
{
diff --git a/src/base/node/tree-sitter/treeSitterLoader.ts b/src/base/node/tree-sitter/treeSitterLoader.ts
index 0e4d441a..fa754f74 100644
--- a/src/base/node/tree-sitter/treeSitterLoader.ts
+++ b/src/base/node/tree-sitter/treeSitterLoader.ts
@@ -95,6 +95,10 @@ export class TreeSitterLoader {
return readFile(pathFactory(languageId));
}
+ if (languageId === 'csharp') {
+ languageId = 'c_sharp';
+ }
+
return readFile(formatWasmFileName(pathTemplate || WASM_FILE_PATH_TEMPLATE, languageId));
}
diff --git a/src/code-context/csharp/CsharpCodeCorrector.ts b/src/code-context/csharp/CsharpCodeCorrector.ts
new file mode 100644
index 00000000..c9b30429
--- /dev/null
+++ b/src/code-context/csharp/CsharpCodeCorrector.ts
@@ -0,0 +1,87 @@
+import vscode from 'vscode';
+
+import { ILanguageServiceProvider } from 'base/common/languages/languageService';
+
+import { PositionUtil } from '../../editor/ast/PositionUtil';
+import { CodeCorrector, CorrectorContext } from '../_base/CodeCorrector';
+import { TreeSitterFile } from '../ast/TreeSitterFile';
+import { textToTreeSitterFile } from '../ast/TreeSitterWrapper';
+
+export class CsharpCodeCorrector implements CodeCorrector {
+ constructor(
+ private context: CorrectorContext,
+ private lsp: ILanguageServiceProvider,
+ ) {}
+
+ async correct(): Promise {
+ let tsfile = await textToTreeSitterFile(this.context.sourcecode, 'csharp', this.lsp);
+
+ if (!tsfile) {
+ return Promise.reject(`Failed to find tree-sitter file for: ${this.context.document.uri}`);
+ }
+
+ await this.fixIncorrectClassName(tsfile, this.context.document);
+ await this.fixIncorrectPackageName(tsfile, this.context.document);
+ }
+
+ /**
+ * Fix LLM generated test file lost class name issue
+ */
+ private async fixIncorrectClassName(tsfile: TreeSitterFile, document: vscode.TextDocument) {
+ let query = tsfile.languageProfile.classQuery.query(tsfile.tsLanguage);
+ const captures = query!!.captures(tsfile.tree.rootNode);
+
+ const queryCapture = captures.find(c => c.name === 'name.definition.class');
+ if (queryCapture) {
+ // compare targetClassName to queryCapture.text if they are different, replace queryCapture.text with targetClassName
+ let classNode = queryCapture.node;
+ if (this.context.targetClassName !== classNode.text) {
+ let edit = new vscode.WorkspaceEdit();
+
+ let classNameRange = new vscode.Range(
+ PositionUtil.fromNode(classNode.startPosition),
+ PositionUtil.fromNode(classNode.endPosition),
+ );
+
+ edit.replace(document.uri, classNameRange, this.context.targetClassName);
+ // applyEdit
+ await vscode.workspace.applyEdit(edit);
+ }
+ }
+ }
+
+ /**
+ * Fix LLM generated test file lost package name issue
+ */
+ private async fixIncorrectPackageName(tsfile: TreeSitterFile, document: vscode.TextDocument) {
+ let packageQuery = tsfile.languageProfile.packageQuery!!.query(tsfile.tsLanguage);
+ const packageCapture = packageQuery!!.captures(tsfile.tree.rootNode);
+
+ // if package is not found, add package to the top of the file
+ if (packageCapture.length === 0) {
+ let content = 'package ' + this.context.packageName + ';\n\n';
+
+ let edit = new vscode.WorkspaceEdit();
+ edit.insert(document.uri, new vscode.Position(0, 0), content);
+
+ await vscode.workspace.applyEdit(edit);
+ }
+
+ // fixme: not tested
+ // if package is found, compare package name to this.packageName if they are different, replace package name
+ if (packageCapture.length > 0) {
+ let packageNode = packageCapture[0].node;
+ if (this.context.packageName !== packageNode.text) {
+ let edit = new vscode.WorkspaceEdit();
+
+ let pkgNameRange = new vscode.Range(
+ PositionUtil.fromNode(packageNode.startPosition),
+ PositionUtil.fromNode(packageNode.endPosition),
+ );
+
+ edit.replace(document.uri, pkgNameRange, this.context.packageName);
+ await vscode.workspace.applyEdit(edit);
+ }
+ }
+ }
+}
diff --git a/src/code-context/csharp/CsharpProfile.ts b/src/code-context/csharp/CsharpProfile.ts
new file mode 100644
index 00000000..a14713a5
--- /dev/null
+++ b/src/code-context/csharp/CsharpProfile.ts
@@ -0,0 +1,97 @@
+import { injectable } from 'inversify';
+
+import { ILanguageServiceProvider } from 'base/common/languages/languageService';
+
+import csharpscm from '../../code-search/schemas/indexes/c_sharp.scm?raw';
+import { LanguageProfile, MemoizedQuery } from '../_base/LanguageProfile';
+
+@injectable()
+export class CsharpProfile implements LanguageProfile {
+ languageIds = ['csharp'];
+ fileExtensions = ['csharp'];
+ grammar = (langService: ILanguageServiceProvider) => langService.getLanguage('csharp');
+ isTestFile = (filePath: string) => filePath.endsWith('Test.cs') && filePath.includes('src/test');
+ scopeQuery = new MemoizedQuery(csharpscm);
+ hoverableQuery = new MemoizedQuery(`
+ [(identifier)
+ (type_identifier)] @hoverable
+ `);
+ methodQuery = new MemoizedQuery(`
+ (method_declaration
+ name: (identifier) @name.definition.method) @definition.method
+ `);
+ /**
+ * (struct_declaration
+ name: (identifier) @definition.struct)
+ */
+ classQuery = new MemoizedQuery(`
+ (class_declaration
+ name: (identifier) @name.definition.class) @definition.class
+ `);
+ blockCommentQuery = new MemoizedQuery(`
+ ((block_comment) @block_comment
+ (#match? @block_comment "^\\\\/\\\\*\\\\*")) @docComment`);
+ packageQuery = new MemoizedQuery(`
+ (package_declaration
+ (scoped_identifier) @package-name)
+ `);
+ structureQuery = new MemoizedQuery(`
+ (struct_declaration
+ name: (identifier) @name.definition.struct) @definition.struct
+ `);
+ methodIOQuery = new MemoizedQuery(`
+ (method_declaration
+ type: (_) @method-returnType
+ name: (identifier) @method-name
+ parameters: (formal_parameters
+ (formal_parameter
+ (type_identifier) @method-param.type
+ (identifier) @method-param.value
+ )?
+ @method-params)
+ body: (block) @method-body
+ )`);
+
+ fieldQuery = new MemoizedQuery(`
+ (field_declaration
+ (type_identifier) @field-type
+ (variable_declarator
+ (identifier) @field-name
+ )
+ ) @field-declaration
+ `);
+ namespaces = [
+ [
+ // variables
+ "local",
+ // types
+ "class",
+ "struct",
+ "enum",
+ "typedef",
+ "interface",
+ "enumerator",
+ // methods
+ "method",
+ // namespaces
+ "namespace",
+ ],
+ ];
+ autoSelectInsideParent = [];
+ builtInTypes = [
+ "bool",
+ "sbyte",
+ "byte",
+ "short",
+ "ushort",
+ "int",
+ "uint",
+ "ulong",
+ "float",
+ "double",
+ "decimal",
+ "char",
+ "string",
+ "object"
+ ];
+}
diff --git a/src/code-context/csharp/CsharpRelevantCodeProvider.ts b/src/code-context/csharp/CsharpRelevantCodeProvider.ts
new file mode 100644
index 00000000..3a68180c
--- /dev/null
+++ b/src/code-context/csharp/CsharpRelevantCodeProvider.ts
@@ -0,0 +1,74 @@
+import { injectable } from 'inversify';
+import vscode from 'vscode';
+
+
+import { TextRange } from '../../code-search/scope-graph/model/TextRange';
+import { ScopeGraph } from '../../code-search/scope-graph/ScopeGraph';
+import { NamedElement } from '../../editor/ast/NamedElement';
+import { CodeFile } from '../../editor/codemodel/CodeElement';
+import { RelevantCodeProvider } from '../_base/RelevantCodeProvider';
+import { TreeSitterFile } from '../ast/TreeSitterFile';
+import { CsharpStructurerProvider } from './CsharpStructurerProvider';
+import { CsharpRelevantLookup } from './utils/CsharpRelevantLookup';
+import { ILanguageServiceProvider } from 'base/common/languages/languageService';
+
+@injectable()
+export class CsharpRelevantCodeProvider implements RelevantCodeProvider {
+ name = 'CsharpRelatedProvider';
+ language = 'csharp';
+ languageService: ILanguageServiceProvider | undefined;
+
+ async setupLanguage(defaultLanguageServiceProvider: ILanguageServiceProvider) {
+ this.languageService = defaultLanguageServiceProvider;
+ }
+
+ async getMethodFanInAndFanOut(file: TreeSitterFile, method: NamedElement): Promise {
+ let graph = await file.scopeGraph();
+ return await this.lookupRelevantClass(method, file, graph);
+ }
+
+ async lookupRelevantClass(element: NamedElement, tsfile: TreeSitterFile, graph: ScopeGraph): Promise {
+ let structurer = new CsharpStructurerProvider();
+ await structurer.init(this.languageService!!);
+
+ const textRange: TextRange = element.blockRange.toTextRange();
+ const source = tsfile.sourcecode;
+ let ios: string[] =
+ (await structurer.retrieveMethodIOImports(graph, tsfile.tree.rootNode, textRange, source)) ?? [];
+
+ let lookup = new CsharpRelevantLookup(tsfile);
+ let paths = lookup.relevantImportToFilePath(ios);
+
+ // read file by path and structurer to parse it to uml
+ async function parseCodeFile(path: string): Promise {
+ const uri = vscode.Uri.file(path);
+ if (!(await vscode.workspace.fs.stat(uri))) {
+ return undefined;
+ }
+
+ try {
+ const document = await vscode.workspace.openTextDocument(uri);
+ return await structurer.parseFile(document.getText(), path);
+ } catch (e) {
+ console.info(`Failed to open file ${path}`);
+ return undefined;
+ }
+ }
+
+ let codeFiles: CodeFile[] = [];
+ for (const path of paths) {
+ let codeFile: CodeFile | undefined = undefined;
+ try {
+ codeFile = await parseCodeFile(path);
+ } catch (e) {
+ console.info(`Failed to parse file ${path}`);
+ }
+
+ if (codeFile !== undefined) {
+ codeFiles.push(codeFile);
+ }
+ }
+
+ return codeFiles;
+ }
+}
diff --git a/src/code-context/csharp/CsharpStructurerProvider.ts b/src/code-context/csharp/CsharpStructurerProvider.ts
new file mode 100644
index 00000000..ddf644b4
--- /dev/null
+++ b/src/code-context/csharp/CsharpStructurerProvider.ts
@@ -0,0 +1,281 @@
+import { injectable } from 'inversify';
+import Parser, { SyntaxNode } from 'web-tree-sitter';
+
+
+import { TextRange } from '../../code-search/scope-graph/model/TextRange';
+import { ScopeGraph } from '../../code-search/scope-graph/ScopeGraph';
+import { CodeFile, CodeFunction, CodeStructure, CodeVariable, StructureType } from '../../editor/codemodel/CodeElement';
+import { LanguageProfile, LanguageProfileUtil } from '../_base/LanguageProfile';
+import { BaseStructurerProvider } from '../_base/StructurerProvider';
+import { LanguageIdentifier } from 'base/common/languages/languages';
+
+@injectable()
+export class CsharpStructurerProvider extends BaseStructurerProvider {
+ protected langId: LanguageIdentifier = 'csharp';
+ protected config: LanguageProfile = LanguageProfileUtil.from(this.langId)!!;
+ protected parser: Parser | undefined;
+ protected language: Parser.Language | undefined;
+
+ constructor() {
+ super();
+ }
+
+ isApplicable(lang: string) {
+ return lang === this.langId;
+ }
+
+ /**
+ * The `parseFile` method is an asynchronous function that parses a given code string and generates a CodeFile object. This object represents the structure of the code.
+ *
+ * @param code - A string representing the code to be parsed.
+ * @param filepath - A string representing the path of the file.
+ *
+ * @returns A Promise that resolves to a CodeFile object. This object contains information about the structure of the parsed code, including the name, filepath, language, functions, path, package, imports, and classes. If the parsing fails, the Promise resolves to undefined.
+ *
+ * The method uses a parser to parse the code and a query to capture the structure of the code. It then iterates over the captures to extract information about the package, imports, classes, methods, and other elements of the code. This information is used to populate the CodeFile object.
+ *
+ * The method also handles nested classes and methods, ensuring that each class and method is correctly associated with its parent class or method.
+ *
+ * Note: This method assumes that the code string is written in a language that the parser can parse. If the parser cannot parse the code, the method may fail or return incorrect results.
+ */
+
+ /**
+ * `parseFile`方法是一个异步函数,它解析给定的代码字符串并生成CodeFile对象。此对象表示代码的结构。
+ *
+ * @param code 表示要解析的代码的字符串。
+ * @param filepath 表示文件路径的字符串。
+ * @returns 解析为CodeFile对象的Promise。此对象包含有关解析代码结构的信息,包括名称、文件路径、语言、函数、路径、包、导入和类。如果解析失败,Promise将解析为undefined。
+ *
+ * 该方法使用解析器来解析代码,并使用查询来捕获代码的结构。然后,它迭代这些捕获,以提取有关包、导入、类、方法和代码其他元素的信息。此信息用于填充CodeFile对象。
+ *
+ * 该方法还处理嵌套的类和方法,确保每个类和方法与其父类或方法正确关联。
+ *
+ * 注意:此方法假定代码字符串是用解析器可以解析的语言编写的。如果解析器无法解析代码,则该方法可能会失败或返回不正确的结果。
+ *
+ */
+ async parseFile(code: string, filepath: string): Promise {
+ const tree = this.parser!!.parse(code);
+ const query = this.config.structureQuery.query(this.language!!);
+ const captures = query!!.captures(tree.rootNode);
+
+ let filename = filepath.split('/')[filepath.split('/').length - 1];
+ const codeFile: CodeFile = {
+ name: filename,
+ filepath: filepath,
+ language: this.langId,
+ functions: [],
+ path: '',
+ package: '',
+ imports: [],
+ classes: [],
+ };
+ let classObj: CodeStructure = {
+ type: StructureType.Class,
+ canonicalName: '',
+ constant: [],
+ extends: [],
+ methods: [],
+ name: '',
+ package: '',
+ implements: [],
+ start: { row: 0, column: 0 },
+ end: { row: 0, column: 0 },
+ };
+ let isLastNode = false;
+ const methods: CodeFunction[] = [];
+ let methodReturnType = '';
+ let methodName = '';
+
+ const fields: CodeVariable[] = [];
+ let lastField: CodeVariable = this.initVariable();
+
+ for (const element of captures) {
+ const capture: Parser.QueryCapture = element!!;
+ const text = capture.node.text;
+
+ switch (capture.name) {
+ case 'package-name':
+ codeFile.package = text;
+ break;
+ case 'import-name':
+ codeFile.imports.push(text);
+ break;
+ case 'class-name':
+ if (classObj.name !== '') {
+ codeFile.classes.push({ ...classObj });
+ classObj = {
+ type: StructureType.Class,
+ canonicalName: '',
+ package: codeFile.package,
+ implements: [],
+ constant: [],
+ extends: [],
+ methods: [],
+ name: '',
+ start: { row: 0, column: 0 },
+ end: { row: 0, column: 0 },
+ };
+ }
+
+ classObj.name = text;
+ classObj.canonicalName = codeFile.package + '.' + classObj.name;
+ const classNode: Parser.SyntaxNode | null = capture.node?.parent ?? null;
+ if (classNode !== null) {
+ this.insertLocation(classNode, classObj);
+ if (!isLastNode) {
+ isLastNode = true;
+ }
+ }
+ break;
+ case 'method-returnType':
+ methodReturnType = text;
+ break;
+ case 'method-name':
+ methodName = text;
+ break;
+ case 'method-body':
+ if (methodName !== '') {
+ const methodNode = capture.node;
+ const methodObj = this.createFunction(capture.node, methodName);
+ if (methodReturnType !== '') {
+ methodObj.returnType = methodReturnType;
+ }
+ if (methodNode !== null) {
+ this.insertLocation(methodNode, classObj);
+ }
+
+ methods.push(methodObj);
+ }
+
+ methodReturnType = '';
+ methodName = '';
+ break;
+ case 'field-type':
+ lastField.type = text;
+ break;
+ case 'field-decl':
+ lastField.name = text;
+ fields.push({ ...lastField });
+ lastField = this.initVariable();
+ break;
+ case 'impl-name':
+ classObj.implements.push(text);
+ break;
+ default:
+ break;
+ }
+ }
+
+ classObj.fields = fields;
+ classObj.methods = methods;
+
+ if (isLastNode && classObj.name !== '') {
+ codeFile.classes.push({ ...classObj });
+ }
+
+ return this.combineSimilarClasses(codeFile);
+ }
+
+ /**
+ * `extractMethodIOImports` is an asynchronous method that extracts the import statements related to the input and output
+ * types of a given method from the source code.
+ *
+ * @param {ScopeGraph} graph - The node graph of the source code.
+ * @param {SyntaxNode} node - The syntax node representing the method in the source code.
+ * @param {TextRange} range - The range of the method in the source code.
+ * @param {string} src - The source code as a string.
+ *
+ * @returns {Promise} A promise that resolves to an array of import statements or undefined if no import statements are found.
+ *
+ * The method works by first finding the syntax node that corresponds to the given range in the source code. It then uses a query to capture the return type and parameter types of the method. For each captured element, it fetches the corresponding import statements from the source code and adds them to an array. Finally, it removes any duplicate import statements from the array before returning it.
+ *
+ * The method uses the `fetchImportsWithinScope` method to fetch the import statements for a given syntax node from the source code.
+ *
+ * Note: The method assumes that the `methodIOQuery` and `language` properties of the `config` object are defined.
+ */
+
+/**
+*`extractMethodIOImports`是一个异步方法,用于提取与输入和输出相关的导入语句
+*源代码中给定方法的类型。
+*
+* @param {ScopeGraph} graph -源代码的节点图。
+* @param {SyntaxNode} 节点 -表示源代码中方法的语法节点。
+* @param {TextRange} range -源代码中方法的范围。
+* @param {string} src-源代码为字符串。
+*
+* @returns {Promise} 一个promise,解析为一个import语句数组,如果找不到import语句,则解析为undefined。
+*
+* 该方法的工作原理是首先在源代码中找到与给定范围对应的语法节点。然后,它使用查询来捕获方法的返回类型和参数类型。对于每个捕获的元素,它从源代码中获取相应的导入语句并将其添加到数组中。最后,在返回数组之前,它会从数组中删除任何重复的导入语句。
+*
+* 该方法使用“fetchImportsWithinScope”方法从源代码中获取给定语法节点的导入语句。
+*
+* 注意:该方法假定定义了`config`对象的`methodIOQuery`和`language`属性。
+*/
+
+ async retrieveMethodIOImports(
+ graph: ScopeGraph,
+ node: SyntaxNode,
+ range: TextRange,
+ src: string,
+ ): Promise {
+ let syntaxNode = node.namedDescendantForPosition(
+ { row: range.start.line, column: range.start.column },
+ { row: range.end.line, column: range.end.column },
+ );
+
+ const query = this.config.methodIOQuery!!.query(this.language!!);
+ const captures = query!!.captures(syntaxNode);
+
+ const inputAndOutput: string[] = [];
+
+ for (const element of captures) {
+ const capture: Parser.QueryCapture = element!!;
+
+ switch (capture.name) {
+ case 'method-returnType':
+ let imports = await this.fetchImportsWithinScope(graph, capture.node, src);
+ inputAndOutput.push(...imports);
+ break;
+ case 'method-param.type':
+ let typeImports = await this.fetchImportsWithinScope(graph, capture.node, src);
+ inputAndOutput.push(...typeImports);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // remove duplicates
+ return [...new Set(inputAndOutput)];
+ }
+
+ async extractFields(node: SyntaxNode) {
+ const query = this.config.fieldQuery!!.query(this.language!!);
+ const captures = query!!.captures(node);
+
+ const fields: CodeVariable[] = [];
+ let fieldObj: CodeVariable = this.initVariable();
+
+ for (const element of captures) {
+ const capture: Parser.QueryCapture = element!!;
+ const text = capture.node.text;
+
+ switch (capture.name) {
+ case 'field-name':
+ fieldObj.name = text;
+ fields.push({ ...fieldObj });
+ fieldObj = this.initVariable();
+ break;
+ case 'field-type':
+ fieldObj.type = text;
+ break;
+ case 'field-declaration':
+ break;
+ default:
+ break;
+ }
+ }
+
+ return fields;
+ }
+}
diff --git a/src/code-context/csharp/CsharpTestGenProvider.ts b/src/code-context/csharp/CsharpTestGenProvider.ts
new file mode 100644
index 00000000..7e009437
--- /dev/null
+++ b/src/code-context/csharp/CsharpTestGenProvider.ts
@@ -0,0 +1,272 @@
+import { inject, injectable } from 'inversify';
+import path from 'path';
+import vscode, { l10n } from 'vscode';
+
+import { LanguageIdentifier } from 'base/common/languages/languages';
+import { ILanguageServiceProvider } from 'base/common/languages/languageService';
+
+import { ScopeGraph } from '../../code-search/scope-graph/ScopeGraph';
+import { NamedElement } from '../../editor/ast/NamedElement';
+import { TreeSitterFileManager } from '../../editor/cache/TreeSitterFileManager';
+import { CommentedUmlPresenter } from '../../editor/codemodel/presenter/CommentedUmlPresenter';
+import { GradleBuildToolProvider } from '../../toolchain-context/buildtool/GradleBuildToolProvider';
+import { ToolchainContextItem } from '../../toolchain-context/ToolchainContextProvider';
+import { AutoTestTemplateContext } from '../_base/test/AutoTestTemplateContext';
+import { TestGenProvider } from '../_base/test/TestGenProvider';
+import { TestTemplateManager } from '../_lookup/TestTemplateManager';
+import { TreeSitterFile } from '../ast/TreeSitterFile';
+import { CsharpCodeCorrector } from './CsharpCodeCorrector';
+import { CsharpStructurerProvider } from './CsharpStructurerProvider';
+
+@injectable()
+export class CsharpTestGenProvider implements TestGenProvider {
+ baseTestPrompt: string = `${l10n.t('lang.java.prompt.basicTestTemplate')}`.trim();
+ importRegex = /import\s+([\w.]+);/g;
+
+ private clazzName = this.constructor.name;
+
+ private graph: ScopeGraph | undefined;
+ private tsfile: TreeSitterFile | undefined;
+ private context: AutoTestTemplateContext | undefined;
+ private packageName = '';
+
+ private lsp!: ILanguageServiceProvider;
+ private treeSitterFileManager!: TreeSitterFileManager;
+
+ constructor() {}
+
+ isApplicable(lang: LanguageIdentifier): boolean {
+ return lang === 'csharp';
+ }
+
+ async setupLanguage(lsp: ILanguageServiceProvider, context?: AutoTestTemplateContext) {
+ this.lsp = lsp;
+
+ // TODO hack, after move
+ this.treeSitterFileManager = new TreeSitterFileManager(lsp);
+ this.context = context;
+ }
+
+ /**
+ * The `setupTestFile` method is an asynchronous function that sets up a test file for a given document and named element.
+ *
+ * @param {vscode.TextDocument} document - The document for which the test file is to be set up.
+ * @param {NamedElement} element - The named element for which the test file is to be set up.
+ *
+ * @returns {Promise} - Returns a promise that resolves to an AutoTestTemplateContext object.
+ *
+ * The method first converts the document to a TreeSitterFile and generates a scope graph for it. It then constructs a target path for the test file by replacing ".java" with "Test.java" and "src/main/java" with "src/test/java" in the document's file name.
+ *
+ * An AutoTestTemplateContext object is then created with the following properties:
+ * - filename: The file name of the document.
+ * - language: The language ID of the document.
+ * - targetPath: The target path for the test file.
+ * - testClassName: The text of the identifier range of the named element.
+ * - sourceCode: The text of the block range of the named element.
+ * - relatedClasses: An empty array.
+ * - imports: An empty array.
+ *
+ * The method then creates a URI for the target path and writes an empty file to it in the workspace's file system.
+ *
+ * Finally, the method returns a promise that resolves to the created AutoTestTemplateContext object.
+ */
+
+
+
+ /**
+ *
+ * @param document -要为其设置测试文件的文档。
+ * @param element -要为其设置测试文件的命名元素。
+ * @returns {Promise} -返回一个解析为AutoTestTemplateContext对象的promise。
+ *
+ * 该方法的工作原理是首先在源代码中找到与给定范围对应的语法节点。然后,它使用查询来捕获方法的返回类型和参数类型。对于每个捕获的元素,它从源代码中获取相应的导入语句并将其添加到数组中。最后,在返回数组之前,它会从数组中删除任何重复的导入语句。**该方法使用`fetchImportsWithinScope`方法从源代码中获取给定语法节点的导入声明。**注意:该方法假设定义了`config`对象的`methodIOQuery`和`language`属性。
+ *
+ * 该方法首先将文档转换为TreeSitterFile并为其生成作用域图。然后,通过在文档文件名中将“.java”替换为“test.java”,将“src/main/java”替换为”src/test/java“,为测试文件构建目标路径。
+ *
+ * 然后创建具有以下属性的AutoTestTemplateContext对象:
+ * filename:文档的文件名。
+ * -language:文档的语言ID。
+ * -targetPath:测试文件的目标路径。
+ * -testClassName:命名元素的标识符范围的文本。
+ * -sourceCode:指定元素的块范围的文本。
+ * -relatedClasses:一个空数组。
+ * -imports:一个空数组。
+ *
+ * 然后,该方法为目标路径创建一个URI,并在工作区的文件系统中向其写入一个空文件。
+ *
+ * 最后,该方法返回一个promise,该promise解析为创建的AutoTestTemplateContext对象。
+*/
+ async setupTestFile(document: vscode.TextDocument, element: NamedElement): Promise {
+ this.tsfile = await this.treeSitterFileManager.create(document);
+ this.graph = await this.tsfile.scopeGraph();
+
+ let query = this.tsfile.languageProfile.packageQuery!!.query(this.tsfile.tsLanguage);
+ let matches = query.captures(this.tsfile.tree.rootNode);
+ if (matches.length > 0) {
+ this.packageName = matches[0].node.text;
+ }
+
+ const targetPath = document.fileName.replace('.java', 'Test.java').replace('src/main/java', 'src/test/java');
+
+ let filename = path.basename(document.uri.fsPath);
+ let classname = filename.replace('.java', '');
+
+ let structurerProvider = new CsharpStructurerProvider();
+ await structurerProvider.init(this.lsp);
+
+ let codeFile = await structurerProvider.parseFile(document.getText(), document.fileName);
+ let currentClass = codeFile?.classes.find(clazz => clazz.name === classname);
+
+ const testContext: AutoTestTemplateContext = {
+ filename: filename,
+ language: document.languageId,
+ targetPath: targetPath,
+ underTestClassName: classname,
+ targetTestClassName: classname + 'Test',
+ sourceCode: element.blockRange.text,
+ relatedClasses: '',
+ currentClass: new CommentedUmlPresenter().presentClass(currentClass!!, 'java'),
+ chatContext: '',
+ imports: [],
+ };
+
+ const targetUri = vscode.Uri.file(targetPath);
+ await vscode.workspace.fs.writeFile(targetUri, new Uint8Array());
+ this.context = testContext;
+ return Promise.resolve(testContext);
+ }
+
+ /**
+ * after test file is created, try to fix the code, like packageName and className, etc.
+ */
+ async postProcessCodeFix(document: vscode.TextDocument, output: string): Promise {
+ let codeFixer = new CsharpCodeCorrector(
+ {
+ document: document,
+ sourcecode: output,
+ packageName: this.packageName,
+ targetClassName: this.context!!.targetTestClassName!!,
+ },
+ this.lsp,
+ );
+
+ await codeFixer.correct();
+ }
+
+ /**
+ * addition test context
+ * @param context
+ */
+ async additionalTestContext(context: AutoTestTemplateContext): Promise {
+ const fileName = context.filename;
+
+ if (context && context.imports === undefined) {
+ context.imports = [];
+ }
+
+ let isSpringRelated = true;
+ if (context) {
+ const imports = this.importRegex.exec(context.sourceCode!!);
+ const importStrings = imports?.map(imp => imp[1]) ?? [];
+ context!!.imports = imports?.map(imp => imp[1]) ?? [];
+ if (this.context) {
+ this.context.imports = context.imports;
+ }
+
+ isSpringRelated = this.checkIsSpringRelated(importStrings) ?? false;
+ }
+
+ let prompt = this.baseTestPrompt + (await this.determineJUnitVersion());
+
+ const testPrompt = new TestTemplateManager();
+ let finalPrompt: ToolchainContextItem;
+
+ if (this.isController(fileName) && isSpringRelated) {
+ let testControllerPrompt = prompt + `\n${l10n.t('lang.java.prompt.testForController')}\n`.trim();
+
+ const lookup = testPrompt.lookup('ControllerTest.java');
+ if (lookup !== null) {
+ testControllerPrompt += `\nTest code template:\n\`\`\`java\n${lookup}\n\`\`\`\n`;
+ }
+
+ finalPrompt = { clazz: this.clazzName, text: testControllerPrompt };
+ } else if (this.isService(fileName) && isSpringRelated) {
+ let testServicePrompt = prompt + `\n${l10n.t('lang.java.prompt.testForService')}\n`.trim();
+
+ const lookup = testPrompt.lookup('ServiceTest.java');
+ if (lookup !== null) {
+ testServicePrompt += `\nTest code template:\n\`\`\`java\n${lookup}\n\`\`\`\n`;
+ }
+
+ finalPrompt = { clazz: this.clazzName, text: testServicePrompt };
+ } else {
+ const lookup = testPrompt.lookup('Test.java');
+ if (lookup !== null) {
+ prompt += `\nTest code template:\n\`\`\`java\n${lookup}\n\`\`\`\n`;
+ }
+ finalPrompt = { clazz: this.clazzName, text: prompt };
+ }
+
+ return [finalPrompt];
+ }
+
+ protected isService(fileName: string | null): boolean {
+ return fileName !== null && MvcUtil.isService(fileName, 'java');
+ }
+
+ protected isController(fileName: string | null): boolean {
+ return fileName !== null && MvcUtil.isController(fileName, 'java');
+ }
+
+ async determineJUnitVersion(): Promise {
+ let dependencies = await GradleBuildToolProvider.instance().getDependencies();
+ let rule = '';
+ let hasJunit5 = false;
+ let hasJunit4 = false;
+
+ const libraryData = dependencies.dependencies;
+ if (libraryData) {
+ for (const lib of libraryData) {
+ if (lib.group === 'org.junit.jupiter') {
+ hasJunit5 = true;
+ break;
+ }
+
+ if (lib.group === 'junit') {
+ hasJunit4 = true;
+ break;
+ }
+ }
+ }
+
+ if (hasJunit5) {
+ rule = l10n.t('lang.java.prompt.useJunit5');
+ } else if (hasJunit4) {
+ rule = l10n.t('lang.java.prompt.useJunit4');
+ }
+
+ return rule;
+ }
+
+ private checkIsSpringRelated(imports: string[]) {
+ for (const imp of imports) {
+ if (imp.startsWith('org.springframework')) {
+ return true;
+ }
+ }
+ }
+}
+
+export namespace MvcUtil {
+ export function isController(fileName: string, lang: string): boolean {
+ return fileName.endsWith(`Controller.${lang.toLowerCase()}`);
+ }
+
+ export function isService(fileName: string, lang: string): boolean {
+ return fileName.endsWith(`Service.${lang.toLowerCase()}`) || fileName.endsWith(`ServiceImpl.${lang.toLowerCase()}`);
+ }
+
+ export function isRepository(fileName: string, lang: string): boolean {
+ return fileName.endsWith(`Repository.${lang.toLowerCase()}`) || fileName.endsWith(`Repo.${lang.toLowerCase()}`);
+ }
+}
diff --git a/src/code-context/csharp/utils/CsharpRelevantLookup.ts b/src/code-context/csharp/utils/CsharpRelevantLookup.ts
new file mode 100644
index 00000000..c3ddc9ba
--- /dev/null
+++ b/src/code-context/csharp/utils/CsharpRelevantLookup.ts
@@ -0,0 +1,88 @@
+import { LanguageProfileUtil } from '../../_base/LanguageProfile';
+import { TreeSitterFile } from '../../ast/TreeSitterFile';
+
+export class CsharpRelevantLookup {
+ tsfile: TreeSitterFile;
+
+ constructor(tsfile: TreeSitterFile) {
+ this.tsfile = tsfile;
+ }
+
+ /**
+ *“relevantImportToFilePath”方法用于从给定的导入中筛选出相关类并返回其文件路径。
+ *
+ *@param import 这是一个字符串数组,其中每个字符串代表一个导入语句。
+ *
+ * @return 此方法返回一个字符串数组,其中每个字符串都是相关导入的文件路径。
+ *
+ *该方法首先通过调用“imports”参数上的“refineImportTypes”方法来细化导入类型。“refineImportTypes”方法预计将返回一个相关导入的数组。
+ *
+ *然后,它映射相关导入的数组,并为每个导入调用`pathByPackageName`方法。`pathByPackageName`方法应返回导入的文件路径。
+ *
+ *然后,`relevantImportToFilePath`方法返回生成的文件路径数组。
+ *
+ *注意:此代码段中未定义“refineImportTypes”和“pathByPackageName”方法。它们应在规范的其他地方定义,并应按照要求使用。
+ **/
+ relevantImportToFilePath(imports: string[]): string[] {
+ const relevantImports = this.refineImportTypes(imports);
+
+ let packages = relevantImports.map(imp => {
+ return this.pathByPackageName(imp);
+ });
+
+ //return packages.filter(p => !this.isCsharpFrameworks(p));
+ return packages;
+ }
+
+ // isCsharpFrameworks(imp: string) {
+ // let javaImport = imp.startsWith('java.') || imp.startsWith('javax.');
+ // let isSpringImport = imp.startsWith('org.springframework');
+ // return javaImport || isSpringImport;
+ // }
+
+ private refineImportTypes(imports: string[]) {
+ return imports.map(imp => {
+ const impArr = imp.split(' ');
+ return impArr[impArr.length - 1].replace(';', '');
+ });
+ }
+
+ extractCurrentPackageName() {
+ let languageProfile = LanguageProfileUtil.from('csharp')!!;
+
+ const query = languageProfile.packageQuery?.query(this.tsfile.tsLanguage)!!;
+ const matches = query.matches(this.tsfile.tree.rootNode);
+
+ if (matches.length === 0) {
+ return '';
+ }
+
+ const packageNameNode = matches[0].captures[0].node;
+ return packageNameNode.text;
+ }
+
+
+
+
+ /**
+ *给定一个包名,如果与当前的tsfile包相似,请尝试在代码库中查找。
+ *
+ *例如,如果当前文件包名为“cc.unitmesh.unttled.demo.service”,则相关的包名
+ *是`cc.unitmesh.unttled.demo.repository`,那么我们可以根据当前的文件路径查找相关的类路径;
+ **/
+ pathByPackageName(packageName: string) {
+ let currentPath = this.tsfile.filePath;
+ const currentPackageName = this.extractCurrentPackageName();
+ if (currentPackageName === '') {
+ return '';
+ }
+
+ // let projectPath = currentPath.split('src/main/java')[0];
+ // we need to support kotlin, scala file path as well, which is src/main/kotlin
+ let projectPath = currentPath.split('src/main')[0];
+ const packagePath = packageName.replace(/\./g, '/');
+ const lang = currentPath.split('src/main')?.[1]?.split('/')?.[1] || 'cs';
+
+ return `${projectPath}src/main/${lang}/${packagePath}.cs`;
+ }
+}
diff --git a/src/code-context/kotlin/KotlinProfile.ts b/src/code-context/kotlin/KotlinProfile.ts
new file mode 100644
index 00000000..62ae8482
--- /dev/null
+++ b/src/code-context/kotlin/KotlinProfile.ts
@@ -0,0 +1,196 @@
+import { injectable } from 'inversify';
+
+import kotlinscm from '../../code-search/schemas/indexes/kotlin.scm?raw';
+import { LanguageProfile, MemoizedQuery } from '../_base/LanguageProfile';
+import { ILanguageServiceProvider } from 'base/common/languages/languageService';
+
+@injectable()
+export class KotlinProfile implements LanguageProfile {
+ languageIds = ['kotlin'];
+ fileExtensions = ['kt'];
+ grammar = (langService: ILanguageServiceProvider) => langService.getLanguage('kotlin');
+ isTestFile = (filePath: string) => filePath.endsWith('Test.kt') && filePath.includes('src/test');
+ scopeQuery = new MemoizedQuery(kotlinscm);
+ hoverableQuery = new MemoizedQuery(`
+ [(simple_identifier)
+ (user_type (type_identifier))] @hoverable
+ `);
+ methodQuery = new MemoizedQuery(`
+ (function_declaration
+ (simple_identifier) @name.definition.method) @definition.method
+ `);
+ classQuery = new MemoizedQuery(`
+ (class_declaration
+ (type_identifier) @name.definition.class) @definition.class
+ `);
+ blockCommentQuery = new MemoizedQuery(`
+ ((block_comment) @block_comment
+ (#match? @block_comment "^\\\\/\\\\*\\\\*")) @docComment`);
+ packageQuery = new MemoizedQuery(`
+ (package_header
+ (identifier) @package-name)
+ `);
+ structureQuery = new MemoizedQuery(`
+ (package_header
+ (identifier) @package-name)?
+
+ (import_header
+ (identifier) @import-name)?
+
+ (class_declaration
+ (type_identifier) @class-name
+ (delegation_specifier
+ (user_type
+ (type_identifier)) @extend-name)
+ )?
+ (primary_constructor
+ (class_parameter
+ (simple_identifier) @field-name
+ (user_type (type_identifier)) @field-type
+ )?
+ )?
+ (class_body
+ (property_declaration
+ (modifiers
+ (member_modifier)?)?
+ (binding_pattern_kind)?
+ (variable_declaration
+ (simple_identifier) @field-name
+ (user_type (type_identifier)) @field-type
+ )
+ )?
+ (function_declaration
+ (modifiers
+ (member_modifier)?)?
+ (simple_identifier) @method-name
+ (function_value_parameters)?
+ (function_body)? @method-body
+ )?
+ )?
+
+ (class_declaration
+ (type_identifier) @class-name
+ (class_body
+ (property_declaration
+ (binding_pattern_kind)?
+ (variable_declaration
+ (simple_identifier) @interface-property-name
+ (user_type (type_identifier)) @interface-property-type
+ )
+ )?
+ (getter
+ (function_body)?
+ )?
+ (function_declaration
+ (simple_identifier) @interface-method-name
+ (function_value_parameters)?
+ (function_body)? @interface-method-body
+ )?
+ )
+ )?
+ `);
+ methodIOQuery = new MemoizedQuery(`
+ (function_declaration
+ type: (_) @method-returnType
+ (simple_identifier) @method-name
+ (function_value_parameters
+ (parameter
+ (simple_identifier) @method-param.value
+ (user_type (type_identifier)) @method-param.type
+ )?
+ @method-params)
+ (function_body) @method-body
+ )`);
+
+ fieldQuery = new MemoizedQuery(`
+ (property_declaration
+ (modifiers (member_modifier))?
+ (binding_pattern_kind)?
+ (variable_declaration
+ (simple_identifier) @field-name
+ (user_type (type_identifier)) @field-type
+ )
+ (integer_literal | string_literal | boolean_literal)? @field-value
+ ) @field-declaration
+ `);
+
+ interfaceQuery = new MemoizedQuery(`
+ (class_declaration
+ (type_identifier) @interface-name
+ (class_body
+ (property_declaration
+ (binding_pattern_kind)?
+ (variable_declaration
+ (simple_identifier) @interface-property-name
+ (user_type (type_identifier)) @interface-property-type
+ )
+ )?
+ (function_declaration
+ (simple_identifier) @interface-method-name
+ (function_value_parameters)?
+ (function_body)? @interface-method-body
+ )?
+ )
+ )
+ `);
+ namespaces = [
+ [
+ // variables
+ 'local',
+ // functions
+ 'method',
+ // namespacing, modules
+ 'package',
+ 'module',
+ // types
+ 'class',
+ 'enum',
+ 'enumConstant',
+ 'interface',
+ 'typealias',
+ // devops.
+ 'label',
+ ],
+ ];
+ autoSelectInsideParent = [];
+ builtInTypes = [
+ 'Boolean',
+ 'Byte',
+ 'Char',
+ 'Short',
+ 'Int',
+ 'Long',
+ 'Float',
+ 'Double',
+ 'Unit',
+ 'String',
+ 'Array',
+ 'List',
+ 'Map',
+ 'Set',
+ 'Collection',
+ 'Iterable',
+ 'Iterator',
+ 'Sequence',
+ 'Any',
+ 'Nothing',
+ 'Unit',
+ 'Boolean',
+ 'Byte',
+ 'Char',
+ 'Short',
+ 'Int',
+ 'Long',
+ 'Float',
+ 'Double',
+ 'String',
+ 'Array',
+ 'List',
+ 'Map',
+ 'Set',
+ 'Collection',
+ 'Iterable',
+ 'Iterator',
+ 'Sequence',
+ ];
+}
diff --git a/src/code-context/kotlin/KotlinStructurerProvider.ts b/src/code-context/kotlin/KotlinStructurerProvider.ts
new file mode 100644
index 00000000..a2ebaecc
--- /dev/null
+++ b/src/code-context/kotlin/KotlinStructurerProvider.ts
@@ -0,0 +1,193 @@
+import { injectable } from 'inversify';
+import Parser from 'web-tree-sitter';
+
+import { CodeFile, CodeFunction, CodeStructure, CodeVariable, StructureType } from '../../editor/codemodel/CodeElement';
+import { LanguageProfile, LanguageProfileUtil } from '../_base/LanguageProfile';
+import { BaseStructurerProvider } from '../_base/StructurerProvider';
+import { LanguageIdentifier } from 'base/common/languages/languages';
+
+
+@injectable()
+export class KotlinStructurerProvider extends BaseStructurerProvider {
+ protected langId: LanguageIdentifier = 'kotlin';
+ protected config: LanguageProfile = LanguageProfileUtil.from(this.langId)!!;
+ protected parser: Parser | undefined;
+ protected language: Parser.Language | undefined;
+
+ constructor() {
+ super();
+ }
+
+ isApplicable(lang: string) {
+ return lang === this.langId;
+ }
+
+ async parseFile(code: string, filepath: string): Promise {
+ const tree = this.parser!!.parse(code);
+ const query = this.config.structureQuery.query(this.language!!);
+ const captures = query!!.captures(tree.rootNode);
+
+ let filename = filepath.split('/')[filepath.split('/').length - 1];
+ const codeFile: CodeFile = {
+ name: filename,
+ filepath: filepath,
+ language: this.langId,
+ functions: [],
+ path: '',
+ package: '',
+ imports: [],
+ classes: [],
+ };
+ let classObj: CodeStructure = this.createEmptyStructure(StructureType.Class);
+ let interfaceObj: CodeStructure = this.createEmptyStructure(StructureType.Interface);
+
+ let isLastNode = false;
+ let isInInterface = false;
+ const methods: CodeFunction[] = [];
+ let methodReturnType = '';
+ let methodName = '';
+
+ const fields: CodeVariable[] = [];
+ let lastField: CodeVariable = this.initVariable();
+
+ for (const element of captures) {
+ const capture: Parser.QueryCapture = element!!;
+ const text = capture.node.text;
+
+ switch (capture.name) {
+ case 'package-name':
+ codeFile.package = text;
+ break;
+ case 'import-name':
+ codeFile.imports.push(text);
+ break;
+ case 'class-name':
+ if (classObj.name !== '') {
+ classObj.fields = fields.slice();
+ classObj.methods = methods.slice();
+ codeFile.classes.push({ ...classObj });
+
+ // 重置字段和方法
+ methods.length = 0;
+ fields.length = 0;
+ }
+
+ classObj = this.createEmptyStructure(StructureType.Class);
+ classObj.name = text;
+ classObj.canonicalName = codeFile.package ? codeFile.package + '.' + classObj.name : classObj.name;
+ isInInterface = false;
+
+ const classNode: Parser.SyntaxNode | null = capture.node?.parent ?? null;
+ if (classNode !== null) {
+ this.insertLocation(classNode, classObj);
+ if (!isLastNode) {
+ isLastNode = true;
+ }
+ }
+ break;
+ case 'interface-name':
+ if (interfaceObj.name !== '') {
+ interfaceObj.fields = fields.slice();
+ interfaceObj.methods = methods.slice();
+ codeFile.classes.push({ ...interfaceObj });
+
+ methods.length = 0;
+ fields.length = 0;
+ }
+
+ interfaceObj = this.createEmptyStructure(StructureType.Interface);
+ interfaceObj.name = text;
+ interfaceObj.canonicalName = codeFile.package ? codeFile.package + '.' + interfaceObj.name : interfaceObj.name;
+ isInInterface = true;
+
+ const interfaceNode: Parser.SyntaxNode | null = capture.node?.parent ?? null;
+ if (interfaceNode !== null) {
+ this.insertLocation(interfaceNode, interfaceObj);
+ if (!isLastNode) {
+ isLastNode = true;
+ }
+ }
+ break;
+ case 'extend-name':
+ if (isInInterface) {
+ interfaceObj.extends?.push(text);
+ } else {
+ classObj.extends?.push(text);
+ }
+ break;
+ case 'implements-name':
+ classObj.implements.push(text);
+ break;
+ case 'method-name':
+ case 'interface-method-name':
+ methodName = text;
+ break;
+ case 'method-body':
+ case 'interface-method-body':
+ if (methodName !== '') {
+ const methodNode = capture.node;
+ const methodObj = this.createFunction(capture.node, methodName);
+ if (methodReturnType !== '') {
+ methodObj.returnType = methodReturnType;
+ }
+
+ methods.push(methodObj);
+ }
+
+ methodReturnType = '';
+ methodName = '';
+ break;
+ case 'field-name':
+ case 'interface-property-name':
+ lastField = this.initVariable();
+ lastField.name = text;
+ break;
+ case 'field-type':
+ case 'interface-property-type':
+ if (lastField.name) {
+ lastField.type = text;
+ fields.push({ ...lastField });
+ lastField = this.initVariable();
+ }
+ break;
+ case 'field-value':
+ if (lastField.name) {
+ lastField.type = text;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // 处理最后一个类或接口
+ if (isInInterface && interfaceObj.name !== '') {
+ interfaceObj.fields = fields;
+ interfaceObj.methods = methods;
+ codeFile.classes.push({ ...interfaceObj });
+ } else if (!isInInterface && classObj.name !== '') {
+ classObj.fields = fields;
+ classObj.methods = methods;
+ codeFile.classes.push({ ...classObj });
+ }
+
+ return this.combineSimilarClasses(codeFile);
+ }
+
+ // 创建空的结构对象
+ private createEmptyStructure(type: StructureType): CodeStructure {
+ return {
+ type: type,
+ canonicalName: '',
+ constant: [],
+ extends: [],
+ methods: [],
+ name: '',
+ package: '',
+ implements: [],
+ fields: [],
+ end: { row: 0, column: 0 },
+ start: { row: 0, column: 0 }
+ }
+ }
+}
diff --git a/src/code-context/rust/RustProfile.ts b/src/code-context/rust/RustProfile.ts
new file mode 100644
index 00000000..374f008b
--- /dev/null
+++ b/src/code-context/rust/RustProfile.ts
@@ -0,0 +1,90 @@
+import { injectable } from 'inversify';
+
+import rust from '../../code-search/schemas/indexes/rust.scm?raw';
+import { LanguageProfile, MemoizedQuery } from '../_base/LanguageProfile';
+import { ILanguageServiceProvider } from 'base/common/languages/languageService';
+
+@injectable()
+export class RustProfile implements LanguageProfile {
+ languageIds = ['rust'];
+ fileExtensions = ['rs'];
+ grammar = (langService: ILanguageServiceProvider) => langService.getLanguage('rust');
+ isTestFile = (filePath: string) => filePath.endsWith('test.rs');
+ scopeQuery = new MemoizedQuery(rust);
+ hoverableQuery = new MemoizedQuery(`
+ [(identifier)
+ (shorthand_field_identifier)
+ (field_identifier)
+ (type_identifier)] @hoverable
+ `);
+ classQuery = new MemoizedQuery(`
+ (struct_item (type_identifier) @type_identifier) @type_declaration
+ `);
+ methodQuery = new MemoizedQuery(`
+ (function_item (identifier) @name.definition.method) @definition.method
+ `);
+ blockCommentQuery = new MemoizedQuery(`
+ (block_comment) @docComment
+ `);
+ methodIOQuery = new MemoizedQuery(`
+ (function_item
+ name: (identifier) @function.identifier
+ return_type: (type_identifier)? @method-returnType
+ ) @function
+ `);
+ structureQuery = new MemoizedQuery(``);
+ namespaces = [[
+ // variables
+ "const",
+ "function",
+ "variable",
+ // types
+ "struct",
+ "enum",
+ "union",
+ "typedef",
+ "interface",
+ // fields
+ "field",
+ "enumerator",
+ // namespacing
+ "module",
+ // misc
+ "label",
+ "lifetime",
+ ]];
+ autoSelectInsideParent = [];
+ builtInTypes = [
+// 基本类型
+ "bool", // 对应 Java 的 boolean
+ "i8", // 对应 Java 的 byte
+ "char", // 对应 Java 的 char
+ "i16", // 对应 Java 的 short
+ "i32", // 对应 Java 的 int
+ "i64", // 对应 Java 的 long
+ "f32", // 对应 Java 的 float
+ "f64", // 对应 Java 的 double
+ "()", // 对应 Java 的 void
+
+ // 包装类对应类型(Rust 没有直接的包装类型,但以下为常用的类型)
+ "bool", // 对应 Java 的 Boolean
+ "i8", // 对应 Java 的 Byte
+ "char", // 对应 Java 的 Character
+ "i16", // 对应 Java 的 Short
+ "i32", // 对应 Java 的 Integer
+ "i64", // 对应 Java 的 Long
+ "f32", // 对应 Java 的 Float
+ "f64", // 对应 Java 的 Double
+ "String", // 对应 Java 的 String
+
+ // 集合类型(Rust 没有直接对应的类型名,但以下为常用的集合类型)
+ "&[T]", // 对应 Java 的 Array,Rust 中的 slice 引用
+ "Vec", // 对应 Java 的 List,Rust 中的动态数组
+ "HashMap",// 对应 Java 的 Map,Rust 中的哈希映射
+ "HashSet", // 对应 Java 的 Set,Rust 中的哈希集合
+ "Vec", // 对应 Java 的 Collection,Rust 中可用 Vec 代表
+ "impl Iterator",// 对应 Java 的 Iterable 和 Iterator,Rust 中的 Iterator trait
+ "impl Iterator",// 对应 Java 的 Stream,Rust 中流式处理可以用 Iterator 实现
+ "Option", // 对应 Java 的 Optional,Rust 中的 Option 类型
+ ];
+}
diff --git a/src/commands/commandsService.ts b/src/commands/commandsService.ts
index 4e9c30d6..31ad71f3 100644
--- a/src/commands/commandsService.ts
+++ b/src/commands/commandsService.ts
@@ -27,6 +27,7 @@ import {
CMD_EXPLAIN_CODE,
CMD_FEEDBACK,
CMD_FIX_THIS,
+ CMD_GEN_CODE_METHOD_COMPLETIONS,
CMD_GEN_DOCSTRING,
CMD_GIT_MESSAGE_COMMIT_GENERATE,
CMD_NEW_CHAT_SESSION,
@@ -90,7 +91,6 @@ export class CommandsService {
return;
}
-
const selection = editor.selection;
if (selection.isEmpty) {
await chat.send('focusAutoDevInput', undefined);
@@ -181,6 +181,30 @@ export class CommandsService {
chat.input(`${l10n.t('I got the following error, can you please help explain how to fix it?')}: ${input}`);
}
+ async generateMethod() {
+ 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.executeAutoMethodAction(document, ranges[0], edit);
+ } catch (error) {
+ logger.error(`Commands error`, error);
+ showErrorMessage('Command Call Error');
+ }
+ }
+
async generateDocstring() {
const editor = window.activeTextEditor;
if (!editor) {
@@ -376,6 +400,7 @@ export class CommandsService {
commands.registerCommand(CMD_FIX_THIS, this.fixThis, this),
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_CREATE_UNIT_TEST, this.generateUnitTest, this),
// Codebase Commands
commands.registerCommand(CMD_CODEBASE_INDEXING, this.startCodebaseIndexing, this),
diff --git a/src/editor/views/chat/continue/continueViewProvider.ts b/src/editor/views/chat/continue/continueViewProvider.ts
index 6dda5857..e26a06b6 100644
--- a/src/editor/views/chat/continue/continueViewProvider.ts
+++ b/src/editor/views/chat/continue/continueViewProvider.ts
@@ -353,8 +353,9 @@ export class ContinueViewProvider extends AbstractWebviewViewProvider implements
await this.lm.chat(
mapToChatMessages(event.data.messages),
{
- ...resource,
+
...completionOptions,
+ model:resource?.model,
},
{
report(fragment) {
diff --git a/src/prompt-manage/ActionType.ts b/src/prompt-manage/ActionType.ts
index 3d97d8df..afeb42a3 100644
--- a/src/prompt-manage/ActionType.ts
+++ b/src/prompt-manage/ActionType.ts
@@ -6,4 +6,5 @@ export enum ActionType {
Rename,
GenCommitMessage,
LlmReranker,
+ AutoMethod,
}
diff --git a/src/prompt-manage/PromptManager.ts b/src/prompt-manage/PromptManager.ts
index ed86a75e..e17635e6 100644
--- a/src/prompt-manage/PromptManager.ts
+++ b/src/prompt-manage/PromptManager.ts
@@ -134,6 +134,9 @@ export class PromptManager {
case ActionType.AutoDoc:
template = await templateRender.getTemplate(`prompts/genius/${humanLanguage}/code/auto-doc.vm`);
break;
+ case ActionType.AutoMethod:
+ template = await templateRender.getTemplate(`prompts/genius/${humanLanguage}/code/auto-Method.vm`);
+ break;
case ActionType.AutoTest:
template = await templateRender.getTemplate(`prompts/genius/${humanLanguage}/code/test-gen.vm`);
break;
diff --git a/src/test/language/kotlin/KotlinStructure.test.ts b/src/test/language/kotlin/KotlinStructure.test.ts
new file mode 100644
index 00000000..75318999
--- /dev/null
+++ b/src/test/language/kotlin/KotlinStructure.test.ts
@@ -0,0 +1,29 @@
+import { TestLanguageServiceProvider } from "src/test/TestLanguageService";
+import { KotlinStructurerProvider } from "../../../code-context/kotlin/KotlinStructurerProvider";
+
+const Parser = require('web-tree-sitter');
+
+describe('KotlinStructure', () => {
+ it('should convert a simple file to CodeFile', async () => {
+ const kotlinHelloWorld = `package com.example
+interface Shape {
+ val vertexCount: Int
+}
+
+class Rectangle(override val vertexCount: Int = 4) : Shape // Always has 4 vertices
+
+class Polygon : Shape {
+ override var vertexCount: Int = 0 // Can be set to any number later
+}`;
+
+ await Parser.init();
+ const parser = new Parser();
+ const languageService = new TestLanguageServiceProvider(parser);
+
+ const structurer = new KotlinStructurerProvider();
+ await structurer.init(languageService);
+
+ const codeFile = await structurer.parseFile(kotlinHelloWorld, '');
+ console.log(codeFile);
+ });
+});
diff --git a/vsc-extension-quickstart.md b/vsc-extension-quickstart.md
deleted file mode 100644
index b2eb4a43..00000000
--- a/vsc-extension-quickstart.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Welcome to your VS Code Extension
-
-## What's in the folder
-
-* This folder contains all of the files necessary for your extension.
-* `package.json` - this is the manifest file in which you declare your extension and command.
- * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
-* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
- * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
- * We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
-
-## Setup
-
-* install the recommended extensions (amodio.tsl-problem-matcher and dbaeumer.vscode-eslint)
-
-
-## Get up and running straight away
-
-* Press `F5` to open a new window with your extension loaded.
-* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
-* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
-* Find output from your extension in the debug console.
-
-## Make changes
-
-* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
-* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
-
-
-## Explore the API
-
-* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
-
-## Run tests
-
-* Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`.
-* Press `F5` to run the tests in a new window with your extension loaded.
-* See the output of the test result in the debug console.
-* Make changes to `src/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder.
- * The provided test runner will only consider files matching the name pattern `**.test.ts`.
- * You can create folders inside the `test` folder to structure your tests any way you want.
-
-## Go further
-
-* Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
-* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
-* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).