Merge branch 'master' of https://github.com/ethereum/remix-project into desktopofflinenoscript
commit
1e3e2726fd
@ -0,0 +1,28 @@ |
|||||||
|
import { NightwatchBrowser } from 'nightwatch' |
||||||
|
import EventEmitter from 'events' |
||||||
|
|
||||||
|
class waitForElementNotContainsText extends EventEmitter { |
||||||
|
command (this: NightwatchBrowser, id: string, value: string, timeout = 10000): NightwatchBrowser { |
||||||
|
let waitId // eslint-disable-line
|
||||||
|
let currentValue |
||||||
|
const runid = setInterval(() => { |
||||||
|
this.api.getText(id, (result) => { |
||||||
|
currentValue = result.value |
||||||
|
if (typeof result.value === 'string' && result.value.indexOf(value) !== -1) { |
||||||
|
clearInterval(runid) |
||||||
|
clearTimeout(waitId) |
||||||
|
this.api.assert.ok(false, `WaitForElementContainsText ${id} contains ${value} . It should not`) |
||||||
|
this.emit('complete') |
||||||
|
} |
||||||
|
}) |
||||||
|
}, 200) |
||||||
|
|
||||||
|
waitId = setTimeout(() => { |
||||||
|
clearInterval(runid) |
||||||
|
this.api.assert.ok(true, `"${value}" wasn't found.`) |
||||||
|
}, timeout) |
||||||
|
return this |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = waitForElementNotContainsText |
@ -0,0 +1,79 @@ |
|||||||
|
import {Plugin} from '@remixproject/engine' |
||||||
|
import {SuggestionService, SuggestOptions} from './suggestion-service' |
||||||
|
const _paq = (window._paq = window._paq || []) //eslint-disable-line
|
||||||
|
|
||||||
|
const profile = { |
||||||
|
name: 'copilot-suggestion', |
||||||
|
displayName: 'copilot-suggestion', |
||||||
|
description: 'Get Solidity suggestions in editor', |
||||||
|
methods: ['suggest', 'init', 'uninstall', 'status', 'isActivate'], |
||||||
|
version: '0.1.0-alpha', |
||||||
|
maintainedBy: "Remix" |
||||||
|
} |
||||||
|
|
||||||
|
export class CopilotSuggestion extends Plugin { |
||||||
|
service: SuggestionService |
||||||
|
context: string |
||||||
|
ready: boolean |
||||||
|
constructor() { |
||||||
|
super(profile) |
||||||
|
this.service = new SuggestionService() |
||||||
|
this.context = '' |
||||||
|
this.service.events.on('progress', (data) => { |
||||||
|
this.emit('loading', data) |
||||||
|
}) |
||||||
|
this.service.events.on('done', (data) => { |
||||||
|
}) |
||||||
|
this.service.events.on('ready', (data) => { |
||||||
|
this.ready = true |
||||||
|
})
|
||||||
|
} |
||||||
|
|
||||||
|
status () { |
||||||
|
return this.ready |
||||||
|
} |
||||||
|
|
||||||
|
async isActivate () { |
||||||
|
try { |
||||||
|
return await this.call('settings', 'get', 'settings/copilot/suggest/activate') |
||||||
|
} catch (e) { |
||||||
|
console.error(e) |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
async suggest(content: string) { |
||||||
|
if (!await this.call('settings', 'get', 'settings/copilot/suggest/activate')) return { output: [{ generated_text: ''}]} |
||||||
|
|
||||||
|
const max_new_tokens = await this.call('settings', 'get', 'settings/copilot/suggest/max_new_tokens') |
||||||
|
const temperature = await this.call('settings', 'get', 'settings/copilot/suggest/temperature') |
||||||
|
const options: SuggestOptions = { |
||||||
|
do_sample: false, |
||||||
|
top_k: 0, |
||||||
|
temperature, |
||||||
|
max_new_tokens |
||||||
|
} |
||||||
|
return this.service.suggest(this.context ? this.context + '\n\n' + content : content, options) |
||||||
|
} |
||||||
|
|
||||||
|
async loadModeContent() { |
||||||
|
let importsContent = '' |
||||||
|
const imports = await this.call('codeParser', 'getImports') |
||||||
|
for (const imp of imports.modules) { |
||||||
|
try { |
||||||
|
importsContent += '\n\n' + (await this.call('contentImport', 'resolve', imp)).content |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
}
|
||||||
|
} |
||||||
|
return importsContent |
||||||
|
} |
||||||
|
|
||||||
|
async init() { |
||||||
|
return this.service.init() |
||||||
|
} |
||||||
|
|
||||||
|
async uninstall() { |
||||||
|
this.service.terminate() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
import EventEmitter from 'events' |
||||||
|
|
||||||
|
export type SuggestOptions = { max_new_tokens: number, temperature: number, top_k: number, do_sample: boolean } |
||||||
|
|
||||||
|
export class SuggestionService { |
||||||
|
worker: Worker |
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
responses: { [key: number]: Function } |
||||||
|
events: EventEmitter |
||||||
|
current: number |
||||||
|
constructor() { |
||||||
|
this.worker = new Worker(new URL('./worker.js', import.meta.url), { |
||||||
|
type: 'module' |
||||||
|
}); |
||||||
|
this.events = new EventEmitter() |
||||||
|
this.responses = {} |
||||||
|
this.current |
||||||
|
} |
||||||
|
|
||||||
|
terminate(): void { |
||||||
|
this.worker.terminate() |
||||||
|
this.worker = new Worker(new URL('./worker.js', import.meta.url), { |
||||||
|
type: 'module' |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
async init() { |
||||||
|
const onMessageReceived = (e) => { |
||||||
|
switch (e.data.status) { |
||||||
|
case 'initiate': |
||||||
|
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) |
||||||
|
// Model file progress: update one of the progress items.
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'done': |
||||||
|
this.events.emit(e.data.status, e.data) |
||||||
|
// Model file loaded: remove the progress item from the list.
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'ready': |
||||||
|
this.events.emit(e.data.status, e.data) |
||||||
|
// Pipeline ready: the worker is ready to accept messages.
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'update': |
||||||
|
this.events.emit(e.data.status, e.data) |
||||||
|
// Generation update: update the output text.
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'complete': |
||||||
|
if (this.responses[e.data.id]) { |
||||||
|
if (this.current === e.data.id) { |
||||||
|
this.responses[e.data.id](null, e.data) |
||||||
|
} else { |
||||||
|
this.responses[e.data.id]('aborted') |
||||||
|
}
|
||||||
|
delete this.responses[e.data.id] |
||||||
|
this.current = null |
||||||
|
} |
||||||
|
|
||||||
|
// 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/finetuned_sol' |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
suggest (content: string, options: SuggestOptions) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
if (this.current) return reject(new Error('already running')) |
||||||
|
const timespan = Date.now() |
||||||
|
this.current = timespan |
||||||
|
this.worker.postMessage({ |
||||||
|
id: timespan, |
||||||
|
cmd: 'suggest', |
||||||
|
text: content, |
||||||
|
max_new_tokens: options.max_new_tokens, |
||||||
|
temperature: options.temperature, |
||||||
|
top_k: options.top_k, |
||||||
|
do_sample: options.do_sample |
||||||
|
}) |
||||||
|
this.responses[timespan] = (error, result) => { |
||||||
|
if (error) return reject(error) |
||||||
|
resolve(result) |
||||||
|
} |
||||||
|
})
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
|
||||||
|
import { pipeline, env } from '@xenova/transformers'; |
||||||
|
|
||||||
|
env.allowLocalModels = true; |
||||||
|
|
||||||
|
const instance = null |
||||||
|
/** |
||||||
|
* This class uses the Singleton pattern to ensure that only one instance of the pipeline is loaded. |
||||||
|
*/ |
||||||
|
class CodeCompletionPipeline { |
||||||
|
static task = 'text-generation'; |
||||||
|
static model = null |
||||||
|
static instance = null; |
||||||
|
|
||||||
|
static async getInstance(progress_callback = null) { |
||||||
|
if (this.instance === null) { |
||||||
|
this.instance = pipeline(this.task, this.model, { progress_callback }); |
||||||
|
} |
||||||
|
|
||||||
|
return this.instance; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Listen for messages from the main thread
|
||||||
|
self.addEventListener('message', async (event) => { |
||||||
|
const { |
||||||
|
id, model, text, max_new_tokens, cmd, |
||||||
|
|
||||||
|
// Generation parameters
|
||||||
|
temperature, |
||||||
|
top_k, |
||||||
|
do_sample, |
||||||
|
} = event.data; |
||||||
|
|
||||||
|
if (cmd === 'init') { |
||||||
|
// Retrieve the code-completion pipeline. When called for the first time,
|
||||||
|
// this will load the pipeline and save it for future use.
|
||||||
|
CodeCompletionPipeline.model = model |
||||||
|
await CodeCompletionPipeline.getInstance(x => { |
||||||
|
// We also add a progress callback to the pipeline so that we can
|
||||||
|
// track model loading.
|
||||||
|
self.postMessage(x); |
||||||
|
}); |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if (!CodeCompletionPipeline.instance) { |
||||||
|
// Send the output back to the main thread
|
||||||
|
self.postMessage({ |
||||||
|
id, |
||||||
|
status: 'error', |
||||||
|
message: 'model not yet loaded' |
||||||
|
});
|
||||||
|
} |
||||||
|
|
||||||
|
if (cmd === 'suggest') { |
||||||
|
// Retrieve the code-completion pipeline. When called for the first time,
|
||||||
|
// this will load the pipeline and save it for future use.
|
||||||
|
let generator = await CodeCompletionPipeline.getInstance(x => { |
||||||
|
// We also add a progress callback to the pipeline so that we can
|
||||||
|
// track model loading.
|
||||||
|
self.postMessage(x); |
||||||
|
}); |
||||||
|
|
||||||
|
// Actually perform the code-completion
|
||||||
|
let output = await generator(text, { |
||||||
|
max_new_tokens, |
||||||
|
temperature, |
||||||
|
top_k, |
||||||
|
do_sample, |
||||||
|
|
||||||
|
// Allows for partial output
|
||||||
|
callback_function: x => { |
||||||
|
/*self.postMessage({ |
||||||
|
id, |
||||||
|
status: 'update', |
||||||
|
output: generator.tokenizer.decode(x[0].output_token_ids, { skip_special_tokens: true }) |
||||||
|
}); |
||||||
|
*/ |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
// Send the output back to the main thread
|
||||||
|
self.postMessage({ |
||||||
|
id, |
||||||
|
status: 'complete', |
||||||
|
output: output, |
||||||
|
});
|
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,11 @@ |
|||||||
|
{ |
||||||
|
"homeReleaseDetails.title": "v0.38.0 RELEASE HIGHLIGHTS", |
||||||
|
"homeReleaseDetails.highlight1": "Alpha release for Solidity co-pilot", |
||||||
|
"homeReleaseDetails.highlight2": "Define Solidity remappings in remappings.txt file", |
||||||
|
"homeReleaseDetails.highlight3": "Run free function for any selected environment", |
||||||
|
"homeReleaseDetails.highlight4": "New Circom ZKP templates: Hash Checker & Rate Limiting Nullifier", |
||||||
|
"homeReleaseDetails.more": "Read More" |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 919 B |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 3.3 KiB |
File diff suppressed because one or more lines are too long
@ -0,0 +1,60 @@ |
|||||||
|
import React, {useContext, useEffect, useState} from 'react' |
||||||
|
import {AppContext} from '../../context/context' |
||||||
|
import {UsageTypes} from '../../types' |
||||||
|
import { type } from 'os' |
||||||
|
|
||||||
|
interface EnterDialogProps { |
||||||
|
hide: boolean, |
||||||
|
handleUserChoice: (userChoice: UsageTypes) => void, |
||||||
|
} |
||||||
|
|
||||||
|
const EnterDialog = (props: EnterDialogProps) => { |
||||||
|
const [visibility, setVisibility] = useState<boolean>(false) |
||||||
|
const {showEnter} = useContext(AppContext) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
setVisibility(!props.hide) |
||||||
|
}, [props.hide]) |
||||||
|
|
||||||
|
const enterAs = async (uType) => { |
||||||
|
props.handleUserChoice(uType) |
||||||
|
} |
||||||
|
|
||||||
|
const modalClass = (visibility && showEnter) ? "d-flex" : "d-none" |
||||||
|
return ( |
||||||
|
<div |
||||||
|
data-id={`EnterModalDialogContainer-react`} |
||||||
|
data-backdrop="static" |
||||||
|
data-keyboard="false" |
||||||
|
className={"modal " + modalClass} |
||||||
|
role="dialog" |
||||||
|
> |
||||||
|
<div className="modal-dialog align-self-center pb-4" role="document"> |
||||||
|
<div |
||||||
|
tabIndex={-1} |
||||||
|
className={'modal-content remixModalContent mb-4'} |
||||||
|
onKeyDown={({keyCode}) => { |
||||||
|
}} |
||||||
|
> |
||||||
|
<div className="modal-header d-flex flex-column"> |
||||||
|
<h3 className='text-dark'>Welcome to Remix IDE</h3> |
||||||
|
<div className='d-flex flex-row pt-2'> |
||||||
|
<h6 className="modal-title" data-id={`EnterModalDialogModalTitle-react`}> |
||||||
|
To load the project with the most efficient setup we would like to know your experience type. |
||||||
|
</h6> |
||||||
|
<i className="text-dark fal fa-door-open text-center" style={{minWidth: "100px", fontSize: "xxx-large"}}></i> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div className="modal-body text-break remixModalBody d-flex flex-row p-3 justify-content-between" data-id={`EnterModalDialogModalBody-react`}> |
||||||
|
<button className="btn-secondary" data-id="beginnerbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Beginner)}}>Beginner</button> |
||||||
|
<button className="btn-secondary" data-id="tutorbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Tutor)}}>Teacher</button> |
||||||
|
<button className="btn-secondary" data-id="prototyperbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Prototyper)}}>Prototyper</button> |
||||||
|
<button className="btn-secondary" data-id="productionbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Production)}}>Production User</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export default EnterDialog |
@ -1,8 +1,16 @@ |
|||||||
export const enum ModalTypes { |
export const enum ModalTypes { |
||||||
alert = 'alert', |
alert = 'alert', |
||||||
confirm = 'confirm', |
confirm = 'confirm', |
||||||
prompt = 'prompt', |
prompt = 'prompt', |
||||||
password = 'password', |
password = 'password', |
||||||
default = 'default', |
default = 'default', |
||||||
form = 'form', |
form = 'form', |
||||||
|
forceChoice = 'forceChoice' |
||||||
} |
} |
||||||
|
|
||||||
|
export const enum UsageTypes { |
||||||
|
Beginner = 1, |
||||||
|
Tutor, |
||||||
|
Prototyper, |
||||||
|
Production, |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
/* eslint-disable no-control-regex */ |
||||||
|
import { EditorUIProps, monacoTypes } from '@remix-ui/editor'; |
||||||
|
import axios, {AxiosResponse} from 'axios' |
||||||
|
const controller = new AbortController(); |
||||||
|
const { signal } = controller; |
||||||
|
const result: string = '' |
||||||
|
|
||||||
|
export class RemixInLineCompletionProvider implements monacoTypes.languages.InlineCompletionsProvider { |
||||||
|
props: EditorUIProps |
||||||
|
monaco: any |
||||||
|
constructor(props: any, monaco: any) { |
||||||
|
this.props = props |
||||||
|
this.monaco = monaco |
||||||
|
} |
||||||
|
|
||||||
|
async provideInlineCompletions(model: monacoTypes.editor.ITextModel, position: monacoTypes.Position, context: monacoTypes.languages.InlineCompletionContext, token: monacoTypes.CancellationToken): Promise<monacoTypes.languages.InlineCompletions<monacoTypes.languages.InlineCompletion>> { |
||||||
|
if (context.selectedSuggestionInfo) { |
||||||
|
return; |
||||||
|
} |
||||||
|
// get text before the position of the completion
|
||||||
|
const word = model.getValueInRange({ |
||||||
|
startLineNumber: 1, |
||||||
|
startColumn: 1, |
||||||
|
endLineNumber: position.lineNumber, |
||||||
|
endColumn: position.column, |
||||||
|
}); |
||||||
|
|
||||||
|
if (!word.endsWith(' ') && !word.endsWith('\n') && !word.endsWith(';') && !word.endsWith('.')) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
const isActivate = await this.props.plugin.call('copilot-suggestion', 'isActivate') |
||||||
|
if (!isActivate) return |
||||||
|
} catch (err) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
const split = word.split('\n') |
||||||
|
if (split.length < 2) return |
||||||
|
const ask = split[split.length - 2].trimStart() |
||||||
|
if (split[split.length - 1].trim() === '' && ask.startsWith('///')) { |
||||||
|
// use the code generation model
|
||||||
|
const {data} = await axios.post('https://gpt-chat.remixproject.org/infer', {comment: ask.replace('///', '')}) |
||||||
|
const parsedData = JSON.parse(data).trimStart() |
||||||
|
const item: monacoTypes.languages.InlineCompletion = { |
||||||
|
insertText: parsedData |
||||||
|
}; |
||||||
|
return { |
||||||
|
items: [item], |
||||||
|
enableForwardStability: true |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
console.error(e) |
||||||
|
}
|
||||||
|
|
||||||
|
// abort if there is a signal
|
||||||
|
if (token.isCancellationRequested) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
let result |
||||||
|
try { |
||||||
|
result = await this.props.plugin.call('copilot-suggestion', 'suggest', word) |
||||||
|
} catch (err) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
const generatedText = (result as any).output[0].generated_text as string |
||||||
|
// the generated text remove a space from the context...
|
||||||
|
const clean = generatedText.replace('@custom:dev-run-script', '@custom:dev-run-script ').replace(word, '') |
||||||
|
const item: monacoTypes.languages.InlineCompletion = { |
||||||
|
insertText: clean |
||||||
|
}; |
||||||
|
|
||||||
|
// abort if there is a signal
|
||||||
|
if (token.isCancellationRequested) { |
||||||
|
return |
||||||
|
} |
||||||
|
return { |
||||||
|
items: [item], |
||||||
|
enableForwardStability: true |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
handleItemDidShow?(completions: monacoTypes.languages.InlineCompletions<monacoTypes.languages.InlineCompletion>, item: monacoTypes.languages.InlineCompletion, updatedInsertText: string): void { |
||||||
|
|
||||||
|
} |
||||||
|
handlePartialAccept?(completions: monacoTypes.languages.InlineCompletions<monacoTypes.languages.InlineCompletion>, item: monacoTypes.languages.InlineCompletion, acceptedCharacters: number): void { |
||||||
|
|
||||||
|
} |
||||||
|
freeInlineCompletions(completions: monacoTypes.languages.InlineCompletions<monacoTypes.languages.InlineCompletion>): void { |
||||||
|
|
||||||
|
} |
||||||
|
groupId?: string; |
||||||
|
yieldsToGroupIds?: string[]; |
||||||
|
toString?(): string { |
||||||
|
throw new Error('Method not implemented.'); |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue