clone vyper repo and fix highlighting

pull/5370/head
yann300 2 years ago
parent 254a72d745
commit c0d736dc8e
  1. 17
      apps/remix-ide-e2e/src/tests/vyper_api.ts
  2. 2
      apps/vyper/src/app/components/CompilerButton.tsx
  3. 17
      apps/vyper/src/app/components/VyperResult.tsx
  4. 161
      apps/vyper/src/app/examples/ballot.tsx
  5. 45
      apps/vyper/src/app/utils/remix-client.tsx

@ -21,25 +21,30 @@ module.exports = {
.frame(0) .frame(0)
}, },
'Should add the Ballot.vy #group1': function (browser: NightwatchBrowser) { 'Should clone the Vyper repo #group1': function (browser: NightwatchBrowser) {
browser.click('button[data-id="add-ballot"]') browser.click('button[data-id="add-repository"]')
.frameParent() .frameParent()
.openFile('ballot.vy') .waitForElementContainsText('*[data-shared="tooltipPopup"]', 'Vyper repository cloned', 30000)
.openFile('examples')
.openFile('examples/auctions')
.openFile('examples/auctions/blind_auction.vy')
}, },
'Compile ballot.vy should error #group1': function (browser: NightwatchBrowser) { 'Compile blind_auction should success #group1': function (browser: NightwatchBrowser) {
browser.clickLaunchIcon('vyper') browser.clickLaunchIcon('vyper')
// @ts-ignore // @ts-ignore
.frame(0) .frame(0)
.click('[data-id="remote-compiler"]') .click('[data-id="remote-compiler"]')
.click('[data-id="compile"]') .click('[data-id="compile"]')
.assert.containsText('[data-id="error-message"]', 'unexpected indent') .waitForElementVisible('[data-id="copy-abi"]')
}, },
'Compile test contract should success #group1': function (browser: NightwatchBrowser) { 'Compile test contract and deploy to remix VM #group1': function (browser: NightwatchBrowser) {
let contractAddress let contractAddress
browser browser
.frameParent() .frameParent()
.clickLaunchIcon('filePanel')
.switchWorkspace('default_workspace')
.addFile('test.vy', { content: testContract }) .addFile('test.vy', { content: testContract })
.clickLaunchIcon('vyper') .clickLaunchIcon('vyper')
// @ts-ignore // @ts-ignore

@ -67,7 +67,7 @@ function CompilerButton({ contract, setOutput, compilerUrl }: Props) {
const location = errorLocation.replace('line ', '').split(':') const location = errorLocation.replace('line ', '').split(':')
let message = errors[errorIndex] let message = errors[errorIndex]
errorIndex = errorIndex + 4 errorIndex = errorIndex + 4
if (message) { if (message && message.split('\n\n').length > 0) {
try { try {
message = message.split('\n\n')[1] message = message.split('\n\n')[1]
} catch (e) {} } catch (e) {}

@ -7,7 +7,6 @@ import {
} from '../utils'; } from '../utils';
import Tabs from 'react-bootstrap/Tabs' import Tabs from 'react-bootstrap/Tabs'
import Tab from 'react-bootstrap/Tab' import Tab from 'react-bootstrap/Tab'
import { Ballot } from '../examples/ballot';
import Button from 'react-bootstrap/Button'; import Button from 'react-bootstrap/Button';
import JSONTree from 'react-json-view' import JSONTree from 'react-json-view'
import { CopyToClipboard } from '@remix-ui/clipboard' import { CopyToClipboard } from '@remix-ui/clipboard'
@ -17,14 +16,20 @@ interface VyperResultProps {
output?: VyperCompilationOutput; output?: VyperCompilationOutput;
} }
export type ExampleContract = {
name: string,
address: string
}
function VyperResult({ output }: VyperResultProps) { function VyperResult({ output }: VyperResultProps) {
const [ active, setActive ] = useState<keyof VyperCompilationResult>('abi'); const [ active, setActive ] = useState<keyof VyperCompilationResult>('abi')
if (!output) return ( if (!output) return (
<div id="result"> <div id="result">
<p>No contract compiled yet.</p> <p>No contract compiled yet.</p>
<Button data-id="add-ballot" variant="info" onClick={() => remixClient.loadContract(Ballot)}> <Button data-id="add-repository" variant="info" onClick={() => remixClient.cloneVyperRepo()}>
Create Ballot.vy example Clone Vyper repository and play with the contract examples
</Button> </Button>
</div> </div>
) )
@ -33,7 +38,7 @@ function VyperResult({ output }: VyperResultProps) {
return ( return (
<div id="result" className="error"> <div id="result" className="error">
<i className="fas fa-exclamation-circle text-danger"></i> <i className="fas fa-exclamation-circle text-danger"></i>
<p data-id="error-message" className="alert alert-danger">{output.message}</p> <pre data-id="error-message" className="alert alert-danger">{output.message}</pre>
</div>) </div>)
} }
@ -41,7 +46,7 @@ function VyperResult({ output }: VyperResultProps) {
<Tabs id="result" activeKey={active} onSelect={(key: any) => setActive(key)}> <Tabs id="result" activeKey={active} onSelect={(key: any) => setActive(key)}>
<Tab eventKey="abi" title="ABI"> <Tab eventKey="abi" title="ABI">
<CopyToClipboard getContent={() => JSON.stringify(output.abi)}> <CopyToClipboard getContent={() => JSON.stringify(output.abi)}>
<Button variant="info" className="copy">Copy ABI</Button> <Button variant="info" className="copy" data-id="copy-abi">Copy ABI</Button>
</CopyToClipboard> </CopyToClipboard>
<JSONTree src={output.abi} /> <JSONTree src={output.abi} />
</Tab> </Tab>

@ -1,161 +0,0 @@
export const Ballot = {
name: 'browser/ballot.vy',
content: `# Voting with delegation.
# Information about voters
struct Voter:
# weight is accumulated by delegation
weight: int128
# if true, that person already voted (which includes voting by delegating)
voted: bool
# person delegated to
delegate: address
# index of the voted proposal, which is not meaningful unless 'voted' is True.
vote: int128
# Users can create proposals
struct Proposal:
# short name (up to 32 bytes)
name: bytes32
# number of accumulated votes
voteCount: int128
voters: public(map(address, Voter))
proposals: public(map(int128, Proposal))
voterCount: public(int128)
chairperson: public(address)
int128Proposals: public(int128)
@public
@constant
def delegated(addr: address) -> bool:
return self.voters[addr].delegate != ZERO_ADDRESS
@public
@constant
def directlyVoted(addr: address) -> bool:
return self.voters[addr].voted and (self.voters[addr].delegate == ZERO_ADDRESS)
# Setup global variables
@public
def __init__(_proposalNames: bytes32[2]):
self.chairperson = msg.sender
self.voterCount = 0
for i in range(2):
self.proposals[i] = Proposal({
name: _proposalNames[i],
voteCount: 0
})
self.int128Proposals += 1
# Give a 'voter' the right to vote on this ballot.
# This may only be called by the 'chairperson'.
@public
def giveRightToVote(voter: address):
# Throws if the sender is not the chairperson.
assert msg.sender == self.chairperson
# Throws if the voter has already voted.
assert not self.voters[voter].voted
# Throws if the voter's voting weight isn't 0.
assert self.voters[voter].weight == 0
self.voters[voter].weight = 1
self.voterCount += 1
# Used by 'delegate' below, and can be called by anyone.
@public
def forwardWeight(delegate_with_weight_to_forward: address):
assert self.delegated(delegate_with_weight_to_forward)
# Throw if there is nothing to do:
assert self.voters[delegate_with_weight_to_forward].weight > 0
target: address = self.voters[delegate_with_weight_to_forward].delegate
for i in range(4):
if self.delegated(target):
target = self.voters[target].delegate
# The following effectively detects cycles of length <= 5,
# in which the delegation is given back to the delegator.
# This could be done for any int128ber of loops,
# or even infinitely with a while loop.
# However, cycles aren't actually problematic for correctness;
# they just result in spoiled votes.
# So, in the production version, this should instead be
# the responsibility of the contract's client, and this
# check should be removed.
assert target != delegate_with_weight_to_forward
else:
# Weight will be moved to someone who directly voted or
# hasn't voted.
break
weight_to_forward: int128 = self.voters[delegate_with_weight_to_forward].weight
self.voters[delegate_with_weight_to_forward].weight = 0
self.voters[target].weight += weight_to_forward
if self.directlyVoted(target):
self.proposals[self.voters[target].vote].voteCount += weight_to_forward
self.voters[target].weight = 0
# To reiterate: if target is also a delegate, this function will need
# to be called again, similarly to as above.
# Delegate your vote to the voter 'to'.
@public
def delegate(to: address):
# Throws if the sender has already voted
assert not self.voters[msg.sender].voted
# Throws if the sender tries to delegate their vote to themselves or to
# the default address value of 0x0000000000000000000000000000000000000000
# (the latter might not be problematic, but I don't want to think about it).
assert to != msg.sender
assert to != ZERO_ADDRESS
self.voters[msg.sender].voted = True
self.voters[msg.sender].delegate = to
# This call will throw if and only if this delegation would cause a loop
# of length <= 5 that ends up delegating back to the delegator.
self.forwardWeight(msg.sender)
# Give your vote (including votes delegated to you)
# to proposal 'proposals[proposal].name'.
@public
def vote(proposal: int128):
# can't vote twice
assert not self.voters[msg.sender].voted
# can only vote on legitimate proposals
assert proposal < self.int128Proposals
self.voters[msg.sender].vote = proposal
self.voters[msg.sender].voted = True
# transfer msg.sender's weight to proposal
self.proposals[proposal].voteCount += self.voters[msg.sender].weight
self.voters[msg.sender].weight = 0
# Computes the winning proposal taking all
# previous votes into account.
@public
@constant
def winningProposal() -> int128:
winning_vote_count: int128 = 0
winning_proposal: int128 = 0
for i in range(2):
if self.proposals[i].voteCount > winning_vote_count:
winning_vote_count = self.proposals[i].voteCount
winning_proposal = i
return winning_proposal
# Calls winningProposal() function to get the index
# of the winner contained in the proposals array and then
# returns the name of the winner
@public
@constant
def winnerName() -> bytes32:
return self.proposals[self.winningProposal()].name
`
}

@ -3,6 +3,7 @@ import { Api, Status } from '@remixproject/plugin-utils';
import { createClient } from '@remixproject/plugin-webview' import { createClient } from '@remixproject/plugin-webview'
import { PluginClient } from '@remixproject/plugin'; import { PluginClient } from '@remixproject/plugin';
import { Contract } from './compiler'; import { Contract } from './compiler';
import { ExampleContract } from '../components/VyperResult';
export class RemixClient extends PluginClient { export class RemixClient extends PluginClient {
private client = createClient<Api, Readonly<RemixApi>>(this); private client = createClient<Api, Readonly<RemixApi>>(this);
@ -26,28 +27,58 @@ export class RemixClient extends PluginClient {
} }
/** Load Ballot contract example into the file manager */ /** Load Ballot contract example into the file manager */
async loadContract({name, content}: Contract) { async loadContract({name, address}: ExampleContract) {
try { try {
await this.client.call('fileManager', 'setFile', name, content) const content = await this.client.call('contentImport', 'resolve', address)
await this.client.call('fileManager', 'switchFile', name) await this.client.call('fileManager', 'setFile', content.cleanUrl, content.content)
await this.client.call('fileManager', 'switchFile', content.cleanUrl)
} catch (err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
async cloneVyperRepo() {
try {
// @ts-ignore
this.call('notification', 'toast', 'cloning Vyper repository...')
await this.call('manager', 'activatePlugin', 'dGitProvider')
// @ts-ignore
await this.call('dGitProvider', 'clone', { url: 'https://github.com/vyperlang/vyper', token: null }, 'vyper-lang')
// @ts-ignore
this.call('notification', 'toast', 'Vyper repository cloned, the workspace Vyper has been created.')
} catch (e) {
// @ts-ignore
this.call('notification', 'toast', e.message)
}
}
/** Update the status of the plugin in remix */ /** Update the status of the plugin in remix */
changeStatus(status: Status) { changeStatus(status: Status) {
this.client.emit('statusChanged', status); this.client.emit('statusChanged', status);
} }
/** Highlight a part of the editor */ /** Highlight a part of the editor */
highlight(lineColumnPos: HighlightPosition, name: string, color: string) { async highlight(lineColumnPos: HighlightPosition, name: string, message: string) {
return this.client.call('editor', 'highlight', lineColumnPos, name, color) await this.client.call('editor', 'highlight', lineColumnPos, name)
/*
column: -1
row: -1
text: "browser/Untitled1.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.↵"
type: "warning"
*/
const annotation = {
column: 0,
row: lineColumnPos.start.line,
type: 'error',
text: message
}
await this.client.call('editor', 'addAnnotation', annotation, name)
} }
/** Remove current Hightlight */ /** Remove current Hightlight */
discardHighlight() { async discardHighlight() {
return this.client.call('editor', 'discardHighlight') await this.client.call('editor', 'discardHighlight')
await this.client.call('editor', 'clearAnnotations')
} }
/** Get the name of the current contract */ /** Get the name of the current contract */

Loading…
Cancel
Save