Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEV2-4340 - inline chat interaction added #1406

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,10 @@
{
"key": "ctrl+shift+q",
"command": "tabnine.chat.focus-input"
},
{
"key": "ctrl+i",
"command": "tabnine.chat.commands.inline.action"
}
]
},
Expand Down
66 changes: 66 additions & 0 deletions src/tabnineChatWidget/extensionCommands/ChatActionProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* eslint-disable class-methods-use-this */
import {
CodeAction,
CodeActionContext,
CodeActionKind,
ExtensionContext,
Range,
TextDocument,
languages,
CodeActionProvider,
} from "vscode";
import { languagesFilter } from "./const";

export function registerChatActionProvider(context: ExtensionContext) {
context.subscriptions.push(
languages.registerCodeActionsProvider(
languagesFilter,
new ChatActionProvider(),
{
providedCodeActionKinds: [
CodeActionKind.RefactorRewrite,
CodeActionKind.QuickFix,
],
}
)
);
}

class ChatActionProvider implements CodeActionProvider {
provideCodeActions(
document: TextDocument,
range: Range,
codeActionContext: CodeActionContext & {
triggerKind: number;
}
): CodeAction[] {
if (codeActionContext.triggerKind !== 1) {
return [];
}
const resultActions: CodeAction[] = [];

if (codeActionContext.diagnostics[0]?.range) {
const fixAction = new CodeAction(
"Fix with Tabnine",
CodeActionKind.QuickFix
);

fixAction.command = {
title: fixAction.title,
command: "tabnine.chat.commands.fix-code",
};
resultActions.push(fixAction);
}
const refactor = new CodeAction(
"Ask Tabnine",
CodeActionKind.RefactorRewrite
);

refactor.command = {
title: refactor.title,
command: "tabnine.chat.commands.inline.action",
};
resultActions.push(refactor);
return resultActions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,37 @@ import {
CancellationToken,
CodeLens,
CodeLensProvider,
commands,
Diagnostic,
DiagnosticSeverity,
DocumentSymbol,
Event,
EventEmitter,
ExtensionContext,
languages,
Location,
SymbolInformation,
SymbolKind,
TextDocument,
Uri,
} from "vscode";
import { fireEvent } from "../../../binary/requests/requests";

export type Intents = "test" | "fix" | "explain" | "document";
export type Actions =
| "generate-test-for-code"
| "fix-code"
| "explain-code"
| "document-code";

const CODE_LENS_ACTIONS: [Intents, Actions][] = [
["test", "generate-test-for-code"],
["fix", "fix-code"],
["explain", "explain-code"],
["document", "document-code"],
];

const VALID_SYMBOLS = [SymbolKind.Function, SymbolKind.Method];
import { fireEvent } from "../../binary/requests/requests";
import { getFuctionsSymbols } from "./getFuctionsSymbols";
import { Action, COMANDS } from "./commands";
import tabnineExtensionProperties from "../../globals/tabnineExtensionProperties";
import { languagesFilter } from "./const";

const MAX_LINES = 2500;

export class ChatCodeLensProvider implements CodeLensProvider {
export default function registerChatCodeLens(context: ExtensionContext) {
if (!tabnineExtensionProperties.codeLensEnabled) {
return;
}
context.subscriptions.push(
languages.registerCodeLensProvider(
languagesFilter,
new ChatCodeLensProvider()
)
);
}

class ChatCodeLensProvider implements CodeLensProvider {
private visitedFiles: Set<Uri> = new Set();

private didChangeCodeLenses = new EventEmitter<void>();
Expand All @@ -54,9 +51,8 @@ export class ChatCodeLensProvider implements CodeLensProvider {
return [];
}

const documnetSymbols = await commands.executeCommand<
(SymbolInformation & DocumentSymbol)[]
>("vscode.executeDocumentSymbolProvider", document.uri);
const documnetSymbols = await getFuctionsSymbols(document);

if (!documnetSymbols?.length) {
return [];
}
Expand All @@ -72,25 +68,9 @@ export class ChatCodeLensProvider implements CodeLensProvider {

const lenses: CodeLens[] = [];

documnetSymbols
?.filter((fn) => VALID_SYMBOLS.includes(fn.kind))
.forEach(({ location }) =>
lenses.push(...toIntentLens(location, diagnostic), toAskLens(location))
);

documnetSymbols
?.filter((symbol) => symbol.kind === SymbolKind.Class)
.forEach((classSymbol) =>
classSymbol.children
.filter((child) => VALID_SYMBOLS.includes(child.kind))
.forEach((method) => {
const { location } = (method as unknown) as SymbolInformation;
lenses.push(
...toIntentLens(location, diagnostic),
toAskLens(location)
);
})
);
documnetSymbols.forEach(({ location }) => {
lenses.push(...toIntentLens(location, diagnostic), toAskLens(location));
});

if (!this.visitedFiles.has(document.uri)) {
this.visitedFiles.add(document.uri);
Expand Down Expand Up @@ -128,20 +108,26 @@ function toIntentLens(
location: Location,
diagnostics: Diagnostic[]
): CodeLens[] {
return CODE_LENS_ACTIONS.filter(([text]) =>
filterRelevantActions(text, location, diagnostics)
).map(
([text, action], index) =>
new CodeLens(location.range, {
title: `${index === 0 ? "tabnine: " : ""}${text}`,
tooltip: `tabnine ${text}`,
command: "tabnine.chat.commands.any",
arguments: [location.range, action],
})
return (
COMANDS.filter(
({ text, lensOrder }) =>
lensOrder && filterRelevantActions(text, location, diagnostics)
)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
.sort((a, b) => a.lensOrder! - b.lensOrder!)
.map(
({ text, intent }, index) =>
new CodeLens(location.range, {
title: `${index === 0 ? "tabnine: " : ""}${text}`,
tooltip: `tabnine ${text}`,
command: "tabnine.chat.commands.any",
arguments: [location.range, intent],
})
)
);
}
function filterRelevantActions(
text: Intents,
text: Action,
location: Location,
diagnostics: Diagnostic[]
): boolean {
Expand Down
85 changes: 0 additions & 85 deletions src/tabnineChatWidget/extensionCommands/codeLens/index.ts

This file was deleted.

73 changes: 73 additions & 0 deletions src/tabnineChatWidget/extensionCommands/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
type SCOPE = "block" | "selection" | "none";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type SCOPE = "block" | "selection" | "none";
type Scope = "block" | "selection" | "none";

?
is this a known type in vscode sdk? I don't see its usage

export type Action = "test" | "fix" | "explain" | "document" | "workspace";
export type Intent =
| "/generate-test-for-code"
| "/fix-code"
| "/explain-code"
| "/document-code"
| "/workspace";

enum LensOrder {
test = 1,
fix = 2,
explain = 3,
document = 4,
}

export type Command = {
label: string;
text: Action;
intent: Intent;
description: string;
scope: SCOPE[];
multistep: boolean;
lensOrder?: LensOrder | undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
lensOrder?: LensOrder | undefined;
lensOrder?: LensOrder;

};

export const COMANDS: Command[] = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about splitting it to CODE_LENS_COMMANDS and spread it into COMMANDS, so you won't need this lint ignore:

    COMANDS.filter(
      ({ text, lensOrder }) =>
        lensOrder && filterRelevantActions(text, location, diagnostics)
    )
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      .sort((a, b) => a.lensOrder! - b.lensOrder!)

{
label: "$(feedback) explain",
text: "explain",
intent: "/explain-code",
description: "Explain the selected code",
scope: ["selection", "block"],
multistep: false,
lensOrder: LensOrder.explain,
},
{
label: "$(beaker) test",
text: "test",
intent: "/generate-test-for-code",
description: "Write tests for the selected code",
scope: ["block"],
multistep: false,
lensOrder: LensOrder.test,
},
{
label: "$(checklist) document",
text: "document",
intent: "/document-code",
description: "Add documentation for the selected code",
scope: ["block"],
multistep: false,
lensOrder: LensOrder.document,
},
{
label: "$(symbol-property) fix",
text: "fix",
intent: "/fix-code",
description: "Find errors in the selected code and fix them",
scope: ["block"],
multistep: false,
lensOrder: LensOrder.fix,
},
{
label: "$(search) workspace",
text: "workspace",
intent: "/workspace",
description:
"Ask a question related to any code within your current workspace",
scope: ["none"],
multistep: true,
},
];
16 changes: 16 additions & 0 deletions src/tabnineChatWidget/extensionCommands/const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const languagesFilter = [
{ language: "javascript" },
{ pattern: "**/*[!d].ts", scheme: "file" },
{ language: "javascriptreact" },
{ language: "typescriptreact" },
{ language: "python" },
{ language: "ruby" },
{ language: "go" },
{ language: "rust" },
{ language: "swift" },
{ language: "java" },
{ language: "c" },
{ language: "cpp" },
{ language: "csharp" },
{ language: "php" },
];
Loading
Loading