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)
},
'Should add the Ballot.vy #group1': function (browser: NightwatchBrowser) {
browser.click('button[data-id="add-ballot"]')
'Should clone the Vyper repo #group1': function (browser: NightwatchBrowser) {
browser.click('button[data-id="add-repository"]')
.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')
// @ts-ignore
.frame(0)
.click('[data-id="remote-compiler"]')
.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
browser
.frameParent()
.clickLaunchIcon('filePanel')
.switchWorkspace('default_workspace')
.addFile('test.vy', { content: testContract })
.clickLaunchIcon('vyper')
// @ts-ignore

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

@ -7,7 +7,6 @@ import {
} from '../utils';
import Tabs from 'react-bootstrap/Tabs'
import Tab from 'react-bootstrap/Tab'
import { Ballot } from '../examples/ballot';
import Button from 'react-bootstrap/Button';
import JSONTree from 'react-json-view'
import { CopyToClipboard } from '@remix-ui/clipboard'
@ -17,14 +16,20 @@ interface VyperResultProps {
output?: VyperCompilationOutput;
}
export type ExampleContract = {
name: string,
address: string
}
function VyperResult({ output }: VyperResultProps) {
const [ active, setActive ] = useState<keyof VyperCompilationResult>('abi');
const [ active, setActive ] = useState<keyof VyperCompilationResult>('abi')
if (!output) return (
<div id="result">
<p>No contract compiled yet.</p>
<Button data-id="add-ballot" variant="info" onClick={() => remixClient.loadContract(Ballot)}>
Create Ballot.vy example
<Button data-id="add-repository" variant="info" onClick={() => remixClient.cloneVyperRepo()}>
Clone Vyper repository and play with the contract examples
</Button>
</div>
)
@ -33,7 +38,7 @@ function VyperResult({ output }: VyperResultProps) {
return (
<div id="result" className="error">
<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>)
}
@ -41,7 +46,7 @@ function VyperResult({ output }: VyperResultProps) {
<Tabs id="result" activeKey={active} onSelect={(key: any) => setActive(key)}>
<Tab eventKey="abi" title="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>
<JSONTree src={output.abi} />
</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 { PluginClient } from '@remixproject/plugin';
import { Contract } from './compiler';
import { ExampleContract } from '../components/VyperResult';
export class RemixClient extends PluginClient {
private client = createClient<Api, Readonly<RemixApi>>(this);
@ -26,28 +27,58 @@ export class RemixClient extends PluginClient {
}
/** Load Ballot contract example into the file manager */
async loadContract({name, content}: Contract) {
async loadContract({name, address}: ExampleContract) {
try {
await this.client.call('fileManager', 'setFile', name, content)
await this.client.call('fileManager', 'switchFile', name)
const content = await this.client.call('contentImport', 'resolve', address)
await this.client.call('fileManager', 'setFile', content.cleanUrl, content.content)
await this.client.call('fileManager', 'switchFile', content.cleanUrl)
} catch (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 */
changeStatus(status: Status) {
this.client.emit('statusChanged', status);
}
/** Highlight a part of the editor */
highlight(lineColumnPos: HighlightPosition, name: string, color: string) {
return this.client.call('editor', 'highlight', lineColumnPos, name, color)
async highlight(lineColumnPos: HighlightPosition, name: string, message: string) {
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 */
discardHighlight() {
return this.client.call('editor', 'discardHighlight')
async discardHighlight() {
await this.client.call('editor', 'discardHighlight')
await this.client.call('editor', 'clearAnnotations')
}
/** Get the name of the current contract */

Loading…
Cancel
Save