From 9398cb0a8b7152c19bad39e5a2523cb676d72cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Tetsing?= Date: Tue, 20 Feb 2024 14:41:30 +0100 Subject: [PATCH] added code explain on selection and right click. Gpt function explanation still available --- .../src/app/tabs/locales/en/editor.json | 2 + .../lib/providers/inlineCompletionProvider.ts | 3 +- .../editor/src/lib/remix-ui-editor.tsx | 30 ++++++- .../suggestion-service/suggestion-service.ts | 89 +++++++++++++++++++ libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx | 2 +- 5 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 libs/remix-ui/editor/src/lib/suggestion-service/suggestion-service.ts diff --git a/apps/remix-ide/src/app/tabs/locales/en/editor.json b/apps/remix-ide/src/app/tabs/locales/en/editor.json index fac7f7a60e..33643eb0bb 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/editor.json +++ b/apps/remix-ide/src/app/tabs/locales/en/editor.json @@ -23,8 +23,10 @@ "editor.generateDocumentation2": "Generate documentation for the function \"{name}\"", "editor.generateDocumentationByAI": "solidity code: {content}\n Generate the documentation for the function {currentFunction} using the Doxygen style syntax", "editor.explainFunction": "Explain this function", + "editor.explainFunctionSol": "Explain this code", "editor.explainFunction2": "Explain the function \"{name}\"", "editor.explainFunctionByAI": "solidity code: {content}\n Explain the function {currentFunction}", + "editor.explainFunctionByAISol": "solidity code: {content}\n Explain the function {currentFunction}", "editor.executeFreeFunction": "Run a free function", "editor.executeFreeFunction2": "Run the free function \"{name}\"", "editor.toastText1": "This can only execute free function", diff --git a/libs/remix-ui/editor/src/lib/providers/inlineCompletionProvider.ts b/libs/remix-ui/editor/src/lib/providers/inlineCompletionProvider.ts index bd81c26fc4..bbf453277c 100644 --- a/libs/remix-ui/editor/src/lib/providers/inlineCompletionProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/inlineCompletionProvider.ts @@ -31,8 +31,7 @@ export class RemixInLineCompletionProvider implements monacoTypes.languages.Inli }); - if (!word.endsWith(' ') && - !word.endsWith('\n') && + if (!word.endsWith('\n') && !word.endsWith(';') && !word.endsWith('.') && !word.endsWith('(')) { diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index 300d418360..adc5aadf85 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -3,7 +3,7 @@ import { FormattedMessage, useIntl } from 'react-intl' import { isArray } from 'lodash' import Editor, { loader, Monaco } from '@monaco-editor/react' import { AlertModal } from '@remix-ui/app' -import { QueryParams } from '@remix-project/remix-lib' +import { ConsoleLogs, QueryParams } from '@remix-project/remix-lib' import { reducerActions, reducerListener, initialState } from './actions/editor' import { solidityTokensProvider, solidityLanguageConfig } from './syntaxes/solidity' import { cairoTokensProvider, cairoLanguageConfig } from './syntaxes/cairo' @@ -741,6 +741,24 @@ export const EditorUI = (props: EditorUIProps) => { }, } + let solgptExplainFunctionAction + const executeSolgptExplainFunctionAction = { + id: 'explainFunction', + label: intl.formatMessage({id: 'editor.explainFunctionSol'}), + contextMenuOrder: 1, // choose the order + contextMenuGroupId: 'sol-gtp', // create a new grouping + keybindings: [], + run: async () => { + const file = await props.plugin.call('fileManager', 'getCurrentFile') + const content = await props.plugin.call('fileManager', 'readFile', file) + const selectedCode = editor.getModel().getValueInRange(editor.getSelection()) + + await props.plugin.call('solcoder', 'code_explaining', selectedCode) + _paq.push(['trackEvent', 'ai', 'solcoder', 'explainFunction']) + }, + } + + const freeFunctionCondition = editor.createContextKey('freeFunctionCondition', false) let freeFunctionAction const executeFreeFunctionAction = { @@ -775,6 +793,7 @@ export const EditorUI = (props: EditorUIProps) => { freeFunctionAction = editor.addAction(executeFreeFunctionAction) gptGenerateDocumentationAction = editor.addAction(executeGptGenerateDocumentationAction) gptExplainFunctionAction = editor.addAction(executegptExplainFunctionAction) + solgptExplainFunctionAction = editor.addAction(executeSolgptExplainFunctionAction) // we have to add the command because the menu action isn't always available (see onContextMenuHandlerForFreeFunction) editor.addCommand(monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR, () => executeFreeFunctionAction.run()) @@ -794,6 +813,10 @@ export const EditorUI = (props: EditorUIProps) => { gptExplainFunctionAction.dispose() gptExplainFunctionAction = null } + if (solgptExplainFunctionAction) { + solgptExplainFunctionAction.dispose() + solgptExplainFunctionAction = null + } const file = await props.plugin.call('fileManager', 'getCurrentFile') if (!file.endsWith('.sol')) { @@ -811,8 +834,11 @@ export const EditorUI = (props: EditorUIProps) => { currentFunction.current = functionImpl.name executeGptGenerateDocumentationAction.label = intl.formatMessage({id: 'editor.generateDocumentation2'}, {name: functionImpl.name}) gptGenerateDocumentationAction = editor.addAction(executeGptGenerateDocumentationAction) - executegptExplainFunctionAction.label = intl.formatMessage({id: 'editor.explainFunction2'}, {name: functionImpl.name}) + executegptExplainFunctionAction.label = intl.formatMessage({id: 'editor.explainFunction'}, {name: functionImpl.name}) gptExplainFunctionAction = editor.addAction(executegptExplainFunctionAction) + }else{ + executeSolgptExplainFunctionAction.label = intl.formatMessage({id: 'editor.explainFunctionSol'}) + solgptExplainFunctionAction = editor.addAction(executeSolgptExplainFunctionAction) } freeFunctionCondition.set(!!freeFunctionNode) } diff --git a/libs/remix-ui/editor/src/lib/suggestion-service/suggestion-service.ts b/libs/remix-ui/editor/src/lib/suggestion-service/suggestion-service.ts new file mode 100644 index 0000000000..d5efeea610 --- /dev/null +++ b/libs/remix-ui/editor/src/lib/suggestion-service/suggestion-service.ts @@ -0,0 +1,89 @@ +import EventEmitter from 'events' + +export class SuggestionService { + worker: Worker + responses: Array + events: EventEmitter + constructor() { + console.log('SuggestionService instanciate worker') + this.worker = new Worker(new URL('./worker.js', import.meta.url), { + type: 'module' + }); + this.init() + this.events = new EventEmitter() + this.responses = [] + } + + async init() { + const onMessageReceived = (e) => { + switch (e.data.status) { + case 'initiate': + console.log(e.data) + this.events.emit(e.data.status, e.data) + // Model file start load: add a new progress item to the list. + break; + + case 'progress': + this.events.emit(e.data.status, e.data) + console.log(e.data) + // Model file progress: update one of the progress items. + break; + + case 'done': + this.events.emit(e.data.status, e.data) + console.log(e.data) + // Model file loaded: remove the progress item from the list. + break; + + case 'ready': + this.events.emit(e.data.status, e.data) + console.log(e.data) + // Pipeline ready: the worker is ready to accept messages. + break; + + case 'update': + this.events.emit(e.data.status, e.data) + console.log(e.data) + // Generation update: update the output text. + break; + + case 'complete': + console.log(e.data) + if (this.responses[e.data.id]) { + this.responses[e.data.id](null, e.data) + } else { + console.log('no callback for', e.data) + } + + // Generation complete: re-enable the "Generate" button + break; + } + }; + + // Attach the callback function as an event listener. + this.worker.addEventListener('message', onMessageReceived) + + this.worker.postMessage({ + cmd: 'init', + model: 'Pipper/solstarcoder' + }) + } + + suggest (content: string, max_new_tokens: number, temperature: number, top_k: number, do_sample: boolean) { + return new Promise((resolve, reject) => { + this.worker.postMessage({ + id: this.responses.length, + cmd: 'suggest', + text: content, + max_new_tokens, + temperature, + top_k, + do_sample + }) + this.responses.push((error, result) => { + if (error) return reject(error) + resolve(result) + }) + }) + } +} \ No newline at end of file diff --git a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx index fb110ea411..95dcedbbdc 100644 --- a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx +++ b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx @@ -62,7 +62,7 @@ export const TabsUI = (props: TabsUIProps) => { const currentIndexRef = useRef(-1) const tabsRef = useRef({}) const tabsElement = useRef(null) - const [ai_switch, setAI_switch] = useState(true) + const [ai_switch, setAI_switch] = useState(false) const tabs = useRef(props.tabs) tabs.current = props.tabs // we do this to pass the tabs list to the onReady callbacks