From b60447f6ae487d9dcb3a2a6f5dc46b3c4d36bad6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 17 Aug 2022 12:43:18 +0200 Subject: [PATCH] clone vyper repo and fix highlighting --- apps/remix-ide-e2e/src/tests/vyper_api.ts | 17 +- .../src/app/components/CompilerButton.tsx | 2 +- apps/vyper/src/app/components/VyperResult.tsx | 19 ++- apps/vyper/src/app/examples/ballot.tsx | 161 ------------------ apps/vyper/src/app/utils/remix-client.tsx | 45 ++++- 5 files changed, 62 insertions(+), 182 deletions(-) delete mode 100644 apps/vyper/src/app/examples/ballot.tsx diff --git a/apps/remix-ide-e2e/src/tests/vyper_api.ts b/apps/remix-ide-e2e/src/tests/vyper_api.ts index f168372963..bb0afc3859 100644 --- a/apps/remix-ide-e2e/src/tests/vyper_api.ts +++ b/apps/remix-ide-e2e/src/tests/vyper_api.ts @@ -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 diff --git a/apps/vyper/src/app/components/CompilerButton.tsx b/apps/vyper/src/app/components/CompilerButton.tsx index 17cf771514..c7fe318f0d 100644 --- a/apps/vyper/src/app/components/CompilerButton.tsx +++ b/apps/vyper/src/app/components/CompilerButton.tsx @@ -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) {} diff --git a/apps/vyper/src/app/components/VyperResult.tsx b/apps/vyper/src/app/components/VyperResult.tsx index f550d17370..e58f901fea 100644 --- a/apps/vyper/src/app/components/VyperResult.tsx +++ b/apps/vyper/src/app/components/VyperResult.tsx @@ -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('abi'); - + const [ active, setActive ] = useState('abi') + if (!output) return ( +

No contract compiled yet.

-
) @@ -33,7 +38,7 @@ function VyperResult({ output }: VyperResultProps) { return (
-

{output.message}

+
{output.message}
) } @@ -41,7 +46,7 @@ function VyperResult({ output }: VyperResultProps) { setActive(key)}> JSON.stringify(output.abi)}> - + diff --git a/apps/vyper/src/app/examples/ballot.tsx b/apps/vyper/src/app/examples/ballot.tsx deleted file mode 100644 index 4b368c6fda..0000000000 --- a/apps/vyper/src/app/examples/ballot.tsx +++ /dev/null @@ -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 - - ` - } - \ No newline at end of file diff --git a/apps/vyper/src/app/utils/remix-client.tsx b/apps/vyper/src/app/utils/remix-client.tsx index 13a1a693f7..913131e594 100644 --- a/apps/vyper/src/app/utils/remix-client.tsx +++ b/apps/vyper/src/app/utils/remix-client.tsx @@ -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>(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: " 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 */