Merge pull request #11 from ethereum/update-remixide

Update Remix-IDE v0.10.2
pull/5370/head
David Disu 4 years ago committed by GitHub
commit 678dd87146
  1. 6
      .env
  2. 3
      apps/remix-ide/.babelrc
  3. 38
      apps/remix-ide/.circleci/config.yml
  4. 1
      apps/remix-ide/.dockerignore
  5. 6
      apps/remix-ide/.env
  6. 4
      apps/remix-ide/.gitignore
  7. 6
      apps/remix-ide/Dockerfile
  8. 28
      apps/remix-ide/Dockerfile.dev
  9. 37
      apps/remix-ide/README.md
  10. BIN
      apps/remix-ide/assets/img/deployAndRun.webp
  11. BIN
      apps/remix-ide/assets/img/fileManager.webp
  12. BIN
      apps/remix-ide/assets/img/ipfs.webp
  13. BIN
      apps/remix-ide/assets/img/localPlugin.webp
  14. BIN
      apps/remix-ide/assets/img/pluginManager.webp
  15. BIN
      apps/remix-ide/assets/img/remixLogo.webp
  16. BIN
      apps/remix-ide/assets/img/settings.webp
  17. BIN
      apps/remix-ide/assets/img/solidity.webp
  18. BIN
      apps/remix-ide/assets/img/staticAnalysis.webp
  19. BIN
      apps/remix-ide/assets/img/swarm.webp
  20. BIN
      apps/remix-ide/assets/img/unitTesting.webp
  21. 181
      apps/remix-ide/assets/js/editor/darkTheme.js
  22. 15
      apps/remix-ide/build.yaml
  23. 6
      apps/remix-ide/ci/browser_tests.sh
  24. 4
      apps/remix-ide/ci/browser_tests_chrome.sh
  25. 4
      apps/remix-ide/ci/browser_tests_firefox.sh
  26. 16
      apps/remix-ide/ci/build_and_publish_docker_images.sh
  27. 18
      apps/remix-ide/docker-compose.yaml
  28. 2
      apps/remix-ide/docs/plugin_manager.md
  29. 12
      apps/remix-ide/docs/run.md
  30. 2
      apps/remix-ide/embark/index.js
  31. 40
      apps/remix-ide/nginx.conf
  32. 17984
      apps/remix-ide/package-lock.json
  33. 26
      apps/remix-ide/package.json
  34. 55
      apps/remix-ide/src/app.js
  35. 6
      apps/remix-ide/src/app/compiler/compiler-abstract.js
  36. 28
      apps/remix-ide/src/app/compiler/compiler-artefacts.js
  37. 6
      apps/remix-ide/src/app/compiler/compiler-helpers.js
  38. 8
      apps/remix-ide/src/app/compiler/compiler-sourceVerifier-fetchAndCompile.js
  39. 54
      apps/remix-ide/src/app/compiler/compiler-utils.js
  40. 2
      apps/remix-ide/src/app/components/local-plugin.js
  41. 2
      apps/remix-ide/src/app/components/plugin-manager-component.js
  42. 2
      apps/remix-ide/src/app/editor/contextView.js
  43. 2
      apps/remix-ide/src/app/editor/contextualListener.js
  44. 17
      apps/remix-ide/src/app/editor/editor.js
  45. 4
      apps/remix-ide/src/app/editor/example-contracts.js
  46. 43
      apps/remix-ide/src/app/files/file-explorer.js
  47. 51
      apps/remix-ide/src/app/files/fileManager.js
  48. 69
      apps/remix-ide/src/app/files/fileProvider.js
  49. 245
      apps/remix-ide/src/app/files/remixDProvider.js
  50. 54
      apps/remix-ide/src/app/files/remixd-handle.js
  51. 6
      apps/remix-ide/src/app/panels/file-panel.js
  52. 11
      apps/remix-ide/src/app/panels/main-view.js
  53. 35
      apps/remix-ide/src/app/panels/styles/terminal-styles.js
  54. 13
      apps/remix-ide/src/app/panels/terminal.js
  55. 2
      apps/remix-ide/src/app/tabs/analysis-tab.js
  56. 63
      apps/remix-ide/src/app/tabs/compile-tab.js
  57. 4
      apps/remix-ide/src/app/tabs/compileTab/compileTab.js
  58. 107
      apps/remix-ide/src/app/tabs/compileTab/compilerContainer.js
  59. 2
      apps/remix-ide/src/app/tabs/compileTab/contractParser.js
  60. 16
      apps/remix-ide/src/app/tabs/debugger-tab.js
  61. 97
      apps/remix-ide/src/app/tabs/debugger/debuggerUI.js
  62. 4
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/ButtonNavigator.js
  63. 2
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/Slider.js
  64. 9
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/StepManager.js
  65. 16
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/TxBrowser.js
  66. 48
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/VmDebugger.js
  67. 8
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/styles/basicStyles.js
  68. 13
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/CodeListView.js
  69. 43
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/DropdownPanel.js
  70. 17
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/FunctionPanel.js
  71. 3
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityState.js
  72. 2
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/StepDetail.js
  73. 2
      apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js
  74. 2
      apps/remix-ide/src/app/tabs/runTab/contractDropdown.js
  75. 5
      apps/remix-ide/src/app/tabs/runTab/model/dropdownlogic.js
  76. 2
      apps/remix-ide/src/app/tabs/runTab/model/recorder.js
  77. 2
      apps/remix-ide/src/app/tabs/runTab/recorder.js
  78. 2
      apps/remix-ide/src/app/tabs/runTab/settings.js
  79. 4
      apps/remix-ide/src/app/tabs/settings-tab.js
  80. 4
      apps/remix-ide/src/app/tabs/staticanalysis/staticAnalysisView.js
  81. 93
      apps/remix-ide/src/app/tabs/test-tab.js
  82. 53
      apps/remix-ide/src/app/tabs/testTab/testTab.js
  83. 10
      apps/remix-ide/src/app/tabs/theme-module.js
  84. 4
      apps/remix-ide/src/app/udapp/make-udapp.js
  85. 2
      apps/remix-ide/src/app/udapp/run-tab.js
  86. 32
      apps/remix-ide/src/app/ui/TreeView.js
  87. 2
      apps/remix-ide/src/app/ui/auto-complete-popup.js
  88. 4
      apps/remix-ide/src/app/ui/landing-page/landing-page.js
  89. 2
      apps/remix-ide/src/app/ui/multiParamManager.js
  90. 2
      apps/remix-ide/src/app/ui/sendTxCallbacks.js
  91. 2
      apps/remix-ide/src/app/ui/txLogger.js
  92. 2
      apps/remix-ide/src/app/ui/universal-dapp-ui.js
  93. 2
      apps/remix-ide/src/blockchain/blockchain.js
  94. 10
      apps/remix-ide/src/framingService.js
  95. 2
      apps/remix-ide/src/lib/cmdInterpreterAPI.js
  96. 2
      apps/remix-ide/src/lib/events.js
  97. 1
      apps/remix-ide/src/lib/helper.js
  98. 18
      apps/remix-ide/src/lib/offsetToLineColumnConverter.js
  99. 2
      apps/remix-ide/src/migrateFileSystem.js
  100. 3
      apps/remix-ide/test-browser/commands/clickLaunchIcon.js
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,3 +1,3 @@
gist_token = <token> gist_token=<token>
account_passphrase = <passphrase> account_passphrase=<passphrase>
account_password = <password> account_password=<password>

@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

@ -7,7 +7,7 @@ jobs:
remix-ide-chrome: remix-ide-chrome:
docker: docker:
# specify the version you desire here # specify the version you desire here
- image: circleci/node:10.18.0-browsers - image: circleci/node:9.11.2-browsers
# Specify service dependencies here if necessary # Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images # CircleCI maintains a library of pre-built images
@ -41,7 +41,7 @@ jobs:
remix-ide-firefox: remix-ide-firefox:
docker: docker:
# specify the version you desire here # specify the version you desire here
- image: circleci/node:10.18.0-browsers - image: circleci/node:9.11.2-browsers
# Specify service dependencies here if necessary # Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images # CircleCI maintains a library of pre-built images
@ -81,7 +81,7 @@ jobs:
remix-ide-run-deploy: remix-ide-run-deploy:
docker: docker:
# specify the version you desire here # specify the version you desire here
- image: circleci/node:10.18.0-browsers - image: circleci/node:9.11.2-browsers
# Specify service dependencies here if necessary # Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images # CircleCI maintains a library of pre-built images
@ -113,7 +113,7 @@ jobs:
deploy-remix-live: deploy-remix-live:
docker: docker:
# specify the version you desire here # specify the version you desire here
- image: circleci/node:10.18.0-browsers - image: circleci/node:9.11.2-browsers
# Specify service dependencies here if necessary # Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images # CircleCI maintains a library of pre-built images
@ -139,10 +139,33 @@ jobs:
- store_artifacts: - store_artifacts:
path: ./reports/screenshots path: ./reports/screenshots
publish-docker:
docker:
# specify the version you desire here
- image: circleci/node:10.19.0-buster
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
- FILES_TO_PACKAGE: "assets background.js build icon.png index.html manifest.json README.md soljson.js package.json"
working_directory: ~/remix-ide
steps:
- checkout
- setup_remote_docker
- run: npm install
- run: npm run build
- run: ./ci/build_and_publish_docker_images.sh
deploy-remix-alpha: deploy-remix-alpha:
docker: docker:
# specify the version you desire here # specify the version you desire here
- image: circleci/node:10.18.0-browsers - image: circleci/node:9.11.2-browsers
# Specify service dependencies here if necessary # Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images # CircleCI maintains a library of pre-built images
@ -175,6 +198,11 @@ workflows:
- remix-ide-chrome - remix-ide-chrome
- remix-ide-firefox - remix-ide-firefox
- remix-ide-run-deploy - remix-ide-run-deploy
- publish-docker:
requires:
- remix-ide-chrome
- remix-ide-firefox
- remix-ide-run-deploy
- deploy-remix-live: - deploy-remix-live:
requires: requires:
- remix-ide-chrome - remix-ide-chrome

@ -1,3 +1,3 @@
gist_token = <token> gist_token=<token>
account_passphrase = <passphrase> account_passphrase=<passphrase>
account_password = <password> account_password=<password>

@ -1,3 +1,6 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
.idea .idea
.vscode .vscode
build build
@ -12,3 +15,4 @@ remix
contracts contracts
TODO TODO
.tern-port .tern-port
temp_publish_docker

@ -0,0 +1,6 @@
FROM nginx:alpine
WORKDIR /
COPY ./temp_publish_docker/ /usr/share/nginx/html/
EXPOSE 80

@ -0,0 +1,28 @@
# This dockerfile is to build each branch seperately (for dev purpouses)
FROM node:10
# Create Remix user, don't use root!
# RUN yes | adduser --disabled-password remix && mkdir /app
# USER remix
# #Now do remix stuff
# USER remix
WORKDIR /home/remix
COPY ./ ./
RUN npm ci
RUN npm run build
FROM nginx:alpine
WORKDIR /
COPY --from=0 /home/remix/build/ /usr/share/nginx/html/build/
COPY --from=0 /home/remix/index.html /usr/share/nginx/html/index.html
COPY --from=0 /home/remix/nginx.conf /etc/nginx/nginx.conf
COPY --from=0 /home/remix/assets/ /usr/share/nginx/html/assets/
COPY --from=0 /home/remix/icon.png /usr/share/nginx/html/icon.png
COPY --from=0 /home/remix/background.js /usr/share/nginx/html/background.js
COPY --from=0 /home/remix/soljson.js /usr/share/nginx/html/soljson.js
COPY --from=0 /home/remix/package.json /usr/share/nginx/html/package.json
EXPOSE 80

@ -45,6 +45,43 @@ npm run setupremix # only if you plan to link remix and remix-ide repositories
npm start npm start
``` ```
## Docker:
Prerequisites:
* Docker (https://docs.docker.com/desktop/)
* Docker-compose (https://docs.docker.com/compose/install/)
### Run with docker
If you want to run latest changes that are merged into master branch then run:
```
docker pull remixproject/remix-ide:latest
docker run -p 8080:80 remixproject/remix-ide:latest
```
If you want to run latest remix-live release run.
```
docker pull remixproject/remix-ide:remix_live
docker run -p 8080:80 remixproject/remix-ide:remix_live
```
### Run with docker-compose:
To run locally without building you only need docker-compose.yaml file and you can run:
```
docker-compose pull
docker-compose up -d
```
Then go to http://localhost:8080 and you can use you Remix instance.
To fetch docker-compose file without cloning this repo run:
```
curl https://raw.githubusercontent.com/ethereum/remix-ide/master/docker-compose.yaml > docker-compose.yaml
```
## DEVELOPING: ## DEVELOPING:
Run `npm start` and open `http://127.0.0.1:4200` in your browser. Run `npm start` and open `http://127.0.0.1:4200` in your browser.

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

@ -0,0 +1,181 @@
/* eslint-disable */
ace.define("ace/theme/remixDark",["require","exports","module","ace/lib/dom"], function(acequire, exports, module) {
exports.isDark = true;
exports.cssClass = "ace-remixDark";
exports.cssText = ".ace-remixDark .ace_gutter {\
background: #2a2c3f;\
color: #8789a1;\
border-right: 1px solid #282828;\
}\
.ace-remixDark .ace_gutter-cell.ace_warning {\
background-image: none;\
background: #FC0;\
border-left: none;\
padding-left: 0;\
color: #000;\
}\
.ace-remixDark .ace_gutter-cell.ace_error {\
background-position: -6px center;\
background-image: none;\
background: #F10;\
border-left: none;\
padding-left: 0;\
color: #000;\
}\
.ace-remixDark .ace_print-margin {\
border-left: 1px solid #555;\
right: 0;\
background: #1D1D1D;\
}\
.ace-remixDark {\
background-color: #222336;\
color: #a2a3bd;\
}\
.ace-remixDark .ace_cursor {\
border-left: 2px solid #FFFFFF;\
}\
.ace-remixDark .ace_cursor.ace_overwrite {\
border-left: 0px;\
border-bottom: 1px solid #FFFFFF;\
}\
.ace-remixDark .ace_marker-layer .ace_selection {\
background: #494836;\
}\
.ace-remixDark .ace_marker-layer .ace_step {\
background: rgb(198, 219, 174);\
}\
.ace-remixDark .ace_marker-layer .ace_bracket {\
margin: -1px 0 0 -1px;\
border: 1px solid #FCE94F;\
}\
.ace-remixDark .ace_marker-layer .ace_active-line {\
background: #363950;\
}\
.ace-remixDark .ace_gutter-active-line {\
background-color: #363950;\
}\
.ace-remixDark .ace_invisible {\
color: #404040;\
}\
.ace-remixDark .ace_rparen {\
color: #d4d7ed;\
}\
.ace-remixDark .ace_lparen {\
color: #d4d7ed;\
}\
.ace-remixDark .ace_keyword {\
color:#ffa76d;\
}\
.ace-remixDark .ace_keyword.ace_operator {\
color:#eceeff;\
}\
.ace-remixDark .ace_constant {\
color:#1EDAFB;\
}\
.ace-remixDark .ace_constant.ace_language {\
color:#FDC251;\
}\
.ace-remixDark .ace_constant.ace_library {\
color:#8DFF0A;\
}\
.ace-remixDark .ace_constant.ace_numeric {\
color:#eceeff;\
}\
.ace-remixDark .ace_invalid {\
color:#FFFFFF;\
background-color:#990000;\
}\
.ace-remixDark .ace_invalid.ace_deprecated {\
color:#FFFFFF;\
background-color:#990000;\
}\
.ace-remixDark .ace_support {\
color: #999;\
}\
.ace-remixDark .ace_support.ace_function {\
color:#3fe2a7;\
}\
.ace-remixDark .ace_function {\
color:#3fe2a7;\
}\
.ace-remixDark .ace_string {\
color:#eceeff;\
}\
.ace-remixDark .ace_comment {\
color:#a7a7a7;\
font-style:italic;\
padding-bottom: 0px;\
}\
.ace-remixDark .ace_type {\
color:#75ceef;\
}]\
.ace-remixDark .ace_visibility (\
color:#f7d777;\
)\
.ace-remixDark .ace_identifier {\
color:#bec1dd;\
}\
.ace-remixDark .ace_modifier {\
color:#efff2f;\
}\
.ace-remixDark .ace-boolean {\
color:#ff86ac;\
}\
.ace-remixDark .ace_statemutability {\
color:#FFCC00;\
}\
.ace-remixDark .ace_variable {\
color:#e0bb83;\
}\
.ace-remixDark .ace_meta.ace_tag {\
color:#BE53E6;\
}\
.ace-remixDark .ace_entity.ace_other.ace_attribute-name {\
color:#4aa8fd;\
}\
.ace-remixDark .ace_markup.ace_underline {\
text-decoration: underline;\
}\
.ace-remixDark .ace_fold-widget {\
text-align: center;\
}\
.ace-remixDark .ace_fold-widget:hover {\
color: #777;\
}\
.ace-remixDark .ace_fold-widget.ace_start,\
.ace-remixDark .ace_fold-widget.ace_end,\
.ace-remixDark .ace_fold-widget.ace_closed{\
background: none;\
border: none;\
box-shadow: none;\
}\
.ace-remixDark .ace_fold-widget.ace_start:after {\
content: '▾'\
}\
.ace-remixDark .ace_fold-widget.ace_end:after {\
content: '▴'\
}\
.ace-remixDark .ace_fold-widget.ace_closed:after {\
content: '‣'\
}\
.ace-remixDark .ace_indent-guide {\
border-right:1px dotted #333;\
margin-right:-1px;\
}\
.ace-remixDark .ace_fold { \
background: #222; \
border-radius: 3px; \
color: #7AF; \
border: none; \
}\
.ace-remixDark .ace_fold:hover {\
background: #CCC; \
color: #000;\
}\
";
var dom = acequire("../lib/dom");
dom.importCssString(exports.cssText, exports.cssClass);
});

@ -0,0 +1,15 @@
version: "3.7"
x-project-base:
&project-base
restart: always
networks:
- remixide
networks:
remixide:
services:
remixide:
build:
context: .
dockerfile: Dockerfile

@ -21,8 +21,10 @@ setupRemixd
sleep 5 sleep 5
npm run nightwatch_parallel || TEST_EXITCODE=1 TESTFILES=$(circleci tests glob "./test-browser/tests/**/*.test.js" | circleci tests split --split-by=timings)
npm run nightwatch_local_runAndDeploy || TEST_EXITCODE=1 for TESTFILE in $TESTFILES; do
./node_modules/.bin/nightwatch --config nightwatch.js --env chrome $TESTFILE || TEST_EXITCODE=1
done
echo "$TEST_EXITCODE" echo "$TEST_EXITCODE"
if [ "$TEST_EXITCODE" -eq 1 ] if [ "$TEST_EXITCODE" -eq 1 ]

@ -22,7 +22,9 @@ setupRemixd
sleep 5 sleep 5
TESTFILES=$(circleci tests glob "./apps/remix-ide/test-browser/tests/**/*.test.js" | circleci tests split --split-by=timings) TESTFILES=$(circleci tests glob "./apps/remix-ide/test-browser/tests/**/*.test.js" | circleci tests split --split-by=timings)
npm run nightwatch_local_chrome $TESTFILES for TESTFILE in $TESTFILES; do
./node_modules/.bin/nightwatch --config nightwatch.js --env chrome $TESTFILE || TEST_EXITCODE=1
done
echo "$TEST_EXITCODE" echo "$TEST_EXITCODE"
if [ "$TEST_EXITCODE" -eq 1 ] if [ "$TEST_EXITCODE" -eq 1 ]

@ -22,7 +22,9 @@ setupRemixd
sleep 5 sleep 5
TESTFILES=$(circleci tests glob "./apps/remix-ide/test-browser/tests/**/*.test.js" | circleci tests split --split-by=timings) TESTFILES=$(circleci tests glob "./apps/remix-ide/test-browser/tests/**/*.test.js" | circleci tests split --split-by=timings)
npm run nightwatch_local_firefox $TESTFILES for TESTFILE in $TESTFILES; do
./node_modules/.bin/nightwatch --config nightwatch.js --env firefox $TESTFILE || TEST_EXITCODE=1
done
echo "$TEST_EXITCODE" echo "$TEST_EXITCODE"
if [ "$TEST_EXITCODE" -eq 1 ] if [ "$TEST_EXITCODE" -eq 1 ]

@ -0,0 +1,16 @@
#!/bin/bash
set -e
export TAG="$CIRCLE_BRANCH"
if [ "$CIRCLE_BRANCH" == "master" ]; then
export TAG="latest";
fi
rm -rf temp_publish_docker
mkdir temp_publish_docker
cp -r $FILES_TO_PACKAGE temp_publish_docker
docker login --username $DOCKER_USER --password $DOCKER_PASS
docker-compose -f docker-compose.yaml -f build.yaml build
docker push remixproject/remix-ide:$TAG

@ -0,0 +1,18 @@
version: "3.7"
x-project-base:
&project-base
restart: always
networks:
- remixide
networks:
remixide:
services:
remixide:
<<: *project-base
image: remixproject/remix-ide:$TAG
container_name: remixide-${TAG}
ports:
- 8080:80
- 65520:65520

@ -4,7 +4,7 @@ Plugin Manager
## Everything is a PLUGIN in Remix ## Everything is a PLUGIN in Remix
In order to integrate new tools made by us and by ...you into Remix, we've now made everything a plugin. In order to integrate new tools made by us and by ...you into Remix, we've now made everything a plugin.
This architcture will also allow Remix or just parts of Remix to be integrated into other projects (your's for example). This architecture will also allow Remix or just parts of Remix to be integrated into other projects (your's for example).
This means that you only load the functionality you need. This means that you only load the functionality you need.

@ -108,16 +108,16 @@ Using the Recorder
The Recorder is a tool used to save a bunch of transactions in a JSON file and The Recorder is a tool used to save a bunch of transactions in a JSON file and
rerun them later either in the same environment or in another. rerun them later either in the same environment or in another.
Saving to the JSON file ( by default its called senario.json) allows one to easily check the transaction list, tweak input parameters, change linked library, etc... Saving to the JSON file ( by default its called scenario.json) allows one to easily check the transaction list, tweak input parameters, change linked library, etc...
There are many use cases for the recorder. There are many use cases for the recorder.
For instance: For instance:
- After having coded and tested contracts in a constrained - After having coded and tested contracts in a constrained
environment (like the JavaScript VM), you could then change the environment and redeploy it to a more realistic environment like a test net with an **injected web3** or to a Geth node. By using the generated **senario.json** file, you will be using all the same settings that you used in the Javascript VM. And this mean that you won't need to click the interface 100 times or whatever to get the state that you achieved originally. So the recorder could be a tool to protect your sanity. environment (like the JavaScript VM), you could then change the environment and redeploy it to a more realistic environment like a test net with an **injected web3** or to a Geth node. By using the generated **scenario.json** file, you will be using all the same settings that you used in the Javascript VM. And this mean that you won't need to click the interface 100 times or whatever to get the state that you achieved originally. So the recorder could be a tool to protect your sanity.
You can also change the settings in the senario.json file to customize the playback. You can also change the settings in the scenario.json file to customize the playback.
- Deploying contract does often require more than creating one - Deploying contract does often require more than creating one
transaction and so the recorder will automate this deployment. transaction and so the recorder will automate this deployment.
@ -127,10 +127,10 @@ For instance:
![](images/a-runtab-recorder.png) ![](images/a-runtab-recorder.png)
### senario.json ### scenario.json
To create this file in the recorder, you first of course need to have run some transactions. In the image above - it has a `0` next to **Transactions Recorded**. So this isn't the right moment to save transactions because - well because there aren't any. Each time you make a transaction, that number will increment. Then when you are ready, click the floppy disk icon and the senario.json file will be created. To create this file in the recorder, you first of course need to have run some transactions. In the image above - it has a `0` next to **Transactions Recorded**. So this isn't the right moment to save transactions because - well because there aren't any. Each time you make a transaction, that number will increment. Then when you are ready, click the floppy disk icon and the scenario.json file will be created.
The JSON file below is an example of the senario.json file. The JSON file below is an example of the scenario.json file.
In it, 3 transactions are executed: In it, 3 transactions are executed:

@ -13,7 +13,7 @@ const DEFAULT_OPTIONS = {
module.exports = (embark) => { module.exports = (embark) => {
// plugin options // plugin options
const readOnly = embark.pluginConfig.readOnly || false const readOnly = embark.pluginConfig.readOnly || false
const {protocol, host, port} = merge.recursive(DEFAULT_OPTIONS, embark.pluginConfig.remixIde) const { protocol, host, port } = merge.recursive(DEFAULT_OPTIONS, embark.pluginConfig.remixIde)
// globals // globals
const remixIdeUrl = `${protocol}://${host}` + `${port ? `:${port}` : ''}` const remixIdeUrl = `${protocol}://${host}` + `${port ? `:${port}` : ''}`

@ -0,0 +1,40 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 300;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf;
server {
listen 80 default_server;
listen [::]:80 default_server;
root /usr/share/nginx/html;
index index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ /index.html;
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{ {
"name": "remix-ide", "name": "remix-ide",
"version": "v0.10.1", "version": "v0.10.2",
"description": "Extendable Web IDE for Ethereum", "description": "Extendable Web IDE for Ethereum",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.4.5", "@babel/core": "^7.4.5",
@ -49,20 +49,20 @@
"minixhr": "^3.2.2", "minixhr": "^3.2.2",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"nanohtml": "^1.6.3", "nanohtml": "^1.6.3",
"nightwatch": "^1.3.5", "nightwatch": "^0.9.20",
"notify-error": "^1.2.0", "notify-error": "^1.2.0",
"npm-link-local": "^1.1.0", "npm-link-local": "^1.1.0",
"npm-merge-driver": "^2.3.5", "npm-merge-driver": "^2.3.5",
"npm-run-all": "^4.0.2", "npm-run-all": "^4.0.2",
"onchange": "^3.2.1", "onchange": "^3.2.1",
"remix-analyzer": "0.5.2", "remix-analyzer": "0.5.3",
"remix-debug": "0.4.4", "remix-debug": "0.4.5",
"remix-lib": "0.4.29", "remix-lib": "0.4.30",
"remix-simulator": "0.1.9-beta.5", "remix-simulator": "0.1.9-beta.6",
"remix-solidity": "0.3.30", "remix-solidity": "0.3.31",
"remix-tabs": "1.0.48", "remix-tabs": "1.0.48",
"remix-tests": "0.1.33", "remix-tests": "0.1.34",
"remixd": "0.1.8-alpha.10", "remixd": "0.2.3-alpha.1",
"request": "^2.83.0", "request": "^2.83.0",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
"selenium-standalone": "^6.17.0", "selenium-standalone": "^6.17.0",
@ -79,9 +79,8 @@
"yo-yoify": "^3.7.3" "yo-yoify": "^3.7.3"
}, },
"dependencies": { "dependencies": {
"@remixproject/engine": "^0.2.3", "@remixproject/engine": "^0.2.5",
"http-server": "^0.11.1", "http-server": "^0.11.1",
"remixd": "0.1.8-alpha.10",
"standard": "^8.5.0" "standard": "^8.5.0"
}, },
"repository": { "repository": {
@ -166,6 +165,7 @@
"nightwatch_local_firefox": "nightwatch --config nightwatch.js --env firefox", "nightwatch_local_firefox": "nightwatch --config nightwatch.js --env firefox",
"nightwatch_local_chrome": "nightwatch --config nightwatch.js --env chrome", "nightwatch_local_chrome": "nightwatch --config nightwatch.js --env chrome",
"nightwatch_local_ballot": "nightwatch ./test-browser/tests/ballot.test.js --config nightwatch.js --env chrome ", "nightwatch_local_ballot": "nightwatch ./test-browser/tests/ballot.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_usingWorker": "nightwatch ./test-browser/tests/usingWebWorker.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_libraryDeployment": "nightwatch ./test-browser/tests/libraryDeployment.test.js --config nightwatch.js --env chrome ", "nightwatch_local_libraryDeployment": "nightwatch ./test-browser/tests/libraryDeployment.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_solidityImport": "nightwatch ./test-browser/tests/solidityImport.test.js --config nightwatch.js --env chrome ", "nightwatch_local_solidityImport": "nightwatch ./test-browser/tests/solidityImport.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_recorder": "nightwatch ./test-browser/tests/recorder.test.js --config nightwatch.js --env chrome ", "nightwatch_local_recorder": "nightwatch ./test-browser/tests/recorder.test.js --config nightwatch.js --env chrome ",
@ -185,7 +185,9 @@
"nightwatch_local_fileExplorer": "nightwatch ./test-browser/tests/fileExplorer.test.js --config nightwatch.js --env chrome ", "nightwatch_local_fileExplorer": "nightwatch ./test-browser/tests/fileExplorer.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_debugger": "nightwatch ./test-browser/tests/debugger.test.js --config nightwatch.js --env chrome ", "nightwatch_local_debugger": "nightwatch ./test-browser/tests/debugger.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_editor": "nightwatch ./test-browser/tests/editor.test.js --config nightwatch.js --env chrome ", "nightwatch_local_editor": "nightwatch ./test-browser/tests/editor.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_compiler": "nightwatch ./test-browser/tests/compiler_api.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_txListener": "nightwatch ./test-browser/tests/txListener.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_fileManager": "nightwatch ./test-browser/tests/fileManager_api.test.js --config nightwatch.js --env chrome ",
"nightwatch_local_runAndDeploy": "nightwatch ./test-browser/tests/runAndDeploy.js --config nightwatch.js --env chrome-runAndDeploy ", "nightwatch_local_runAndDeploy": "nightwatch ./test-browser/tests/runAndDeploy.js --config nightwatch.js --env chrome-runAndDeploy ",
"onchange": "onchange build/app.js -- npm-run-all lint", "onchange": "onchange build/app.js -- npm-run-all lint",
"prepublish": "mkdirp build; npm-run-all -ls downloadsolc_root build", "prepublish": "mkdirp build; npm-run-all -ls downloadsolc_root build",

@ -3,9 +3,8 @@
var isElectron = require('is-electron') var isElectron = require('is-electron')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var yo = require('yo-yo') var yo = require('yo-yo')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var registry = require('./global/registry') var registry = require('./global/registry')
var Remixd = require('./lib/remixd')
var loadFileFromParent = require('./loadFilesFromParent') var loadFileFromParent = require('./loadFilesFromParent')
var { OffsetToLineColumnConverter } = require('./lib/offsetToLineColumnConverter') var { OffsetToLineColumnConverter } = require('./lib/offsetToLineColumnConverter')
var QueryParams = require('./lib/query-params') var QueryParams = require('./lib/query-params')
@ -122,6 +121,7 @@ var css = csjs`
class App { class App {
constructor (api = {}, events = {}, opts = {}) { constructor (api = {}, events = {}, opts = {}) {
var self = this var self = this
self.appManager = new RemixAppManager({})
self._components = {} self._components = {}
self._view = {} self._view = {}
self._view.splashScreen = yo` self._view.splashScreen = yo`
@ -135,7 +135,7 @@ class App {
document.body.appendChild(self._view.splashScreen) document.body.appendChild(self._view.splashScreen)
// setup storage // setup storage
var configStorage = new Storage('config-v0.8:') const configStorage = new Storage('config-v0.8:')
// load app config // load app config
const config = new Config(configStorage) const config = new Config(configStorage)
@ -145,14 +145,7 @@ class App {
self._components.filesProviders = {} self._components.filesProviders = {}
self._components.filesProviders['browser'] = new FileProvider('browser') self._components.filesProviders['browser'] = new FileProvider('browser')
registry.put({api: self._components.filesProviders['browser'], name: 'fileproviders/browser'}) registry.put({api: self._components.filesProviders['browser'], name: 'fileproviders/browser'})
self._components.filesProviders['localhost'] = new RemixDProvider(self.appManager)
var remixd = new Remixd(65520)
registry.put({api: remixd, name: 'remixd'})
remixd.event.register('system', (message) => {
if (message.error) toolTip(message.error)
})
self._components.filesProviders['localhost'] = new RemixDProvider(remixd)
registry.put({api: self._components.filesProviders['localhost'], name: 'fileproviders/localhost'}) registry.put({api: self._components.filesProviders['localhost'], name: 'fileproviders/localhost'})
registry.put({api: self._components.filesProviders, name: 'fileproviders'}) registry.put({api: self._components.filesProviders, name: 'fileproviders'})
@ -235,9 +228,15 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
} }
// APP_MANAGER // APP_MANAGER
const appManager = new RemixAppManager({}) const appManager = self.appManager
const workspace = appManager.pluginLoader.get() const pluginLoader = appManager.pluginLoader
const workspace = pluginLoader.get()
const engine = new Engine(appManager) const engine = new Engine(appManager)
engine.setPluginOption = ({ name, kind }) => {
if (kind === 'provider') return {queueTimeout: 60000 * 4}
if (name === 'LearnEth') return {queueTimeout: 60000}
return {queueTimeout: 10000}
}
await engine.onload() await engine.onload()
// SERVICES // SERVICES
@ -258,7 +257,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile()) editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile())
// ----------------- fileManager servive ---------------------------- // ----------------- fileManager servive ----------------------------
const fileManager = new FileManager(editor) const fileManager = new FileManager(editor, appManager)
registry.put({api: fileManager, name: 'filemanager'}) registry.put({api: fileManager, name: 'filemanager'})
const blockchain = new Blockchain(registry.get('config').api) const blockchain = new Blockchain(registry.get('config').api)
@ -397,22 +396,38 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
await appManager.activatePlugin(['contentImport', 'theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) await appManager.activatePlugin(['contentImport', 'theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter'])
await appManager.activatePlugin(['mainPanel', 'menuicons']) await appManager.activatePlugin(['mainPanel', 'menuicons'])
await appManager.activatePlugin(['home', 'sidePanel', 'hiddenPanel', 'pluginManager', 'fileExplorers', 'settings', 'contextualListener', 'scriptRunner', 'terminal', 'fetchAndCompile']) await appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await appManager.activatePlugin(['home', 'hiddenPanel', 'pluginManager', 'fileExplorers', 'settings', 'contextualListener', 'scriptRunner', 'terminal', 'fetchAndCompile'])
const queryParams = new QueryParams()
const params = queryParams.get()
// Set workspace after initial activation // Set workspace after initial activation
if (Array.isArray(workspace)) await appManager.activatePlugin(workspace) if (Array.isArray(workspace)) {
try {
await appManager.activatePlugin(workspace)
} catch (e) {
console.error(e)
}
} else {
// activate solidity plugin
appManager.ensureActivated('solidity')
appManager.ensureActivated('udapp')
}
// Load and start the service who manager layout and frame // Load and start the service who manager layout and frame
const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature) const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature)
framingService.start() framingService.start(params)
// If plugins are loaded from the URL params, we focus on the last one.
if (pluginLoader.current === 'queryParams' && Array.isArray(workspace) && workspace.length > 0) menuicons.select(workspace[workspace.length - 1])
// get the file list from the parent iframe // get the file list from the parent iframe
loadFileFromParent(fileManager) loadFileFromParent(fileManager)
// get the file from gist // get the file from gist
const gistHandler = new GistHandler() const gistHandler = new GistHandler()
const queryParams = new QueryParams() const loadedFromGist = gistHandler.loadFromGist(params, fileManager)
const loadedFromGist = gistHandler.loadFromGist(queryParams.get(), fileManager)
if (!loadedFromGist) { if (!loadedFromGist) {
// insert example contracts if there are no files to show // insert example contracts if there are no files to show
self._components.filesProviders['browser'].resolveDirectory('/', (error, filesList) => { self._components.filesProviders['browser'].resolveDirectory('/', (error, filesList) => {
@ -428,4 +443,6 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
if (isElectron()) { if (isElectron()) {
appManager.activatePlugin('remixd') appManager.activatePlugin('remixd')
} }
if (params.embed) framingService.embed()
} }

@ -1,5 +1,5 @@
'use strict' 'use strict'
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var txHelper = remixLib.execution.txHelper var txHelper = remixLib.execution.txHelper
module.exports = class CompilerAbstract { module.exports = class CompilerAbstract {
@ -32,6 +32,10 @@ module.exports = class CompilerAbstract {
getSourceName (fileIndex) { getSourceName (fileIndex) {
if (this.data && this.data.sources) { if (this.data && this.data.sources) {
return Object.keys(this.data.sources)[fileIndex] return Object.keys(this.data.sources)[fileIndex]
} else if (Object.keys(this.source.sources).length === 1) {
// if we don't have ast, we return the only one filename present.
const sourcesArray = Object.keys(this.source.sources)
return sourcesArray[0]
} }
return null return null
} }

@ -14,24 +14,52 @@ module.exports = class CompilerArtefacts extends Plugin {
constructor () { constructor () {
super(profile) super(profile)
this.compilersArtefacts = {} this.compilersArtefacts = {}
this.compilersArtefactsPerFile = {}
} }
clear () { clear () {
this.compilersArtefacts = {} this.compilersArtefacts = {}
this.compilersArtefactsPerFile = {}
} }
onActivation () { onActivation () {
const saveCompilationPerFileResult = (file, source, languageVersion, data) => {
this.compilersArtefactsPerFile[file] = new CompilerAbstract(languageVersion, data, source)
}
this.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => { this.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
this.compilersArtefacts['__last'] = new CompilerAbstract(languageVersion, data, source) this.compilersArtefacts['__last'] = new CompilerAbstract(languageVersion, data, source)
saveCompilationPerFileResult(file, source, languageVersion, data)
}) })
this.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => { this.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => {
this.compilersArtefacts['__last'] = new CompilerAbstract(languageVersion, data, source) this.compilersArtefacts['__last'] = new CompilerAbstract(languageVersion, data, source)
saveCompilationPerFileResult(file, source, languageVersion, data)
}) })
this.on('lexon', 'compilationFinished', (file, source, languageVersion, data) => { this.on('lexon', 'compilationFinished', (file, source, languageVersion, data) => {
this.compilersArtefacts['__last'] = new CompilerAbstract(languageVersion, data, source) this.compilersArtefacts['__last'] = new CompilerAbstract(languageVersion, data, source)
saveCompilationPerFileResult(file, source, languageVersion, data)
})
this.on('yulp', 'compilationFinished', (file, source, languageVersion, data) => {
this.compilersArtefacts['__last'] = new CompilerAbstract(languageVersion, data, source)
saveCompilationPerFileResult(file, source, languageVersion, data)
})
}
getAllContractDatas () {
const contractsData = {}
Object.keys(this.compilersArtefactsPerFile).map((targetFile) => {
const contracts = this.compilersArtefactsPerFile[targetFile].getContracts()
Object.keys(contracts).map((file) => { contractsData[file] = contracts[file] })
}) })
// making sure we save last compilation result in there
if (this.compilersArtefacts['__last']) {
const contracts = this.compilersArtefacts['__last'].getContracts()
Object.keys(contracts).map((file) => { contractsData[file] = contracts[file] })
}
return contractsData
} }
addResolvedContract (address, compilerData) { addResolvedContract (address, compilerData) {

@ -1,5 +1,5 @@
'use strict' 'use strict'
import { canUseWorker } from './compiler-utils' import { canUseWorker, urlFromVersion } from './compiler-utils'
import { Compiler } from '@remix-project/remix-solidity' import { Compiler } from '@remix-project/remix-solidity'
import CompilerAbstract from './compiler-abstract' import CompilerAbstract from './compiler-abstract'
@ -9,9 +9,9 @@ export const compile = async (compilationTargets, settings) => {
const compiler = new Compiler(() => {}) const compiler = new Compiler(() => {})
compiler.set('evmVersion', settings.evmVersion) compiler.set('evmVersion', settings.evmVersion)
compiler.set('optimize', settings.optimize) compiler.set('optimize', settings.optimize)
compiler.loadVersion(canUseWorker(settings.version), settings.compilerUrl) compiler.set('language', settings.language)
compiler.loadVersion(canUseWorker(settings.version), urlFromVersion(settings.version))
compiler.event.register('compilationFinished', (success, compilationData, source) => { compiler.event.register('compilationFinished', (success, compilationData, source) => {
if (!success) return reject(compilationData)
resolve(new CompilerAbstract(settings.version, compilationData, source)) resolve(new CompilerAbstract(settings.version, compilationData, source))
}) })
compiler.event.register('compilerLoaded', _ => compiler.compile(compilationTargets, '')) compiler.event.register('compilerLoaded', _ => compiler.compile(compilationTargets, ''))

@ -1,11 +1,10 @@
const ethutil = require('ethereumjs-util') const ethutil = require('ethereumjs-util')
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { urlFromVersion } from './compiler-utils'
import { compile } from './compiler-helpers' import { compile } from './compiler-helpers'
import globalRegistry from '../../global/registry' import globalRegistry from '../../global/registry'
import remixLib from 'remix-lib' import remixLib from '@remix-project/remix-lib'
const profile = { const profile = {
name: 'fetchAndCompile', name: 'fetchAndCompile',
@ -106,10 +105,9 @@ export default class FetchAndCompile extends Plugin {
// compile // compile
const settings = { const settings = {
version: data.metadata.compiler.version, version: data.metadata.compiler.version,
languageName: data.metadata.language, language: data.metadata.language,
evmVersion: data.metadata.settings.evmVersion, evmVersion: data.metadata.settings.evmVersion,
optimize: data.metadata.settings.optimizer.enabled, optimize: data.metadata.settings.optimizer.enabled
compilerUrl: urlFromVersion(data.metadata.compiler.version)
} }
try { try {
setTimeout(_ => this.emit('compiling', settings), 0) setTimeout(_ => this.emit('compiling', settings), 0)

@ -1,48 +1,44 @@
const semver = require('semver') const semver = require('semver')
const minixhr = require('minixhr')
/* global Worker */ /* global Worker */
export const baseUrl = 'https://solc-bin.ethereum.org/bin' export const baseURLBin = 'https://solc-bin.ethereum.org/bin'
export const baseURLWasm = 'https://solc-bin.ethereum.org/wasm'
export const pathToURL = {}
/**
* Retrieves the URL of the given compiler version
* @param version is the version of compiler with or without 'soljson-v' prefix and .js postfix
*/
export function urlFromVersion (version) { export function urlFromVersion (version) {
return `${baseUrl}/soljson-v${version}.js` if (!version.startsWith('soljson-v')) version = 'soljson-v' + version
if (!version.endsWith('.js')) version = version + '.js'
return `${pathToURL[version]}/${version}`
} }
/** /**
* Checks if the worker can be used to load a compiler. * Checks if the worker can be used to load a compiler.
* checks a compiler whitelist, browser support and OS. * checks a compiler whitelist, browser support and OS.
*/ */
export function canUseWorker (selectedVersion) { export function canUseWorker (selectedVersion) {
// Following restrictions should be deleted when Solidity will release fixed versions of compilers.
// See https://github.com/ethereum/remix-ide/issues/2461
const isChrome = !!window.chrome
const os = retrieveOS()
// define a whitelist for Linux
const linuxWL = ['0.4.26', '0.5.3', '0.5.4', '0.5.5']
const version = semver.coerce(selectedVersion) const version = semver.coerce(selectedVersion)
// defining whitelist for chrome const isNightly = selectedVersion.includes('nightly')
let isFromWhiteList = false return browserSupportWorker() && (
switch (os) { semver.gt(version, '0.6.3') ||
case 'Windows': semver.gt(version, '0.3.6') && !isNightly
isFromWhiteList = semver.gt(version, '0.5.2') || version === '0.4.26' )
break
case 'Linux':
isFromWhiteList = semver.gt(version, '0.5.13') || linuxWL.includes(version)
break
default :
isFromWhiteList = true
}
return browserSupportWorker() && (!isChrome || (isChrome && isFromWhiteList))
} }
function browserSupportWorker () { function browserSupportWorker () {
return document.location.protocol !== 'file:' && Worker !== undefined return document.location.protocol !== 'file:' && Worker !== undefined
} }
function retrieveOS () { // returns a promise for minixhr
let osName = 'Unknown OS' export function promisedMiniXhr (url) {
if (navigator.platform.indexOf('Win') !== -1) { return new Promise((resolve, reject) => {
osName = 'Windows' minixhr(url, (json, event) => {
} else if (navigator.platform.indexOf('Linux') !== -1) { resolve({ json, event })
osName = 'Linux' })
} })
return osName
} }

@ -37,7 +37,7 @@ module.exports = class LocalPlugin {
*/ */
create () { create () {
const profile = { const profile = {
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xMjYyIDEwNzVxLTM3IDEyMS0xMzggMTk1dC0yMjggNzQtMjI4LTc0LTEzOC0xOTVxLTgtMjUgNC00OC41dDM4LTMxLjVxMjUtOCA0OC41IDR0MzEuNSAzOHEyNSA4MCA5Mi41IDEyOS41dDE1MS41IDQ5LjUgMTUxLjUtNDkuNSA5Mi41LTEyOS41cTgtMjYgMzItMzh0NDktNCAzNyAzMS41IDQgNDguNXptLTQ5NC00MzVxMCA1My0zNy41IDkwLjV0LTkwLjUgMzcuNS05MC41LTM3LjUtMzcuNS05MC41IDM3LjUtOTAuNSA5MC41LTM3LjUgOTAuNSAzNy41IDM3LjUgOTAuNXptNTEyIDBxMCA1My0zNy41IDkwLjV0LTkwLjUgMzcuNS05MC41LTM3LjUtMzcuNS05MC41IDM3LjUtOTAuNSA5MC41LTM3LjUgOTAuNSAzNy41IDM3LjUgOTAuNXptMjU2IDI1NnEwLTEzMC01MS0yNDguNXQtMTM2LjUtMjA0LTIwNC0xMzYuNS0yNDguNS01MS0yNDguNSA1MS0yMDQgMTM2LjUtMTM2LjUgMjA0LTUxIDI0OC41IDUxIDI0OC41IDEzNi41IDIwNCAyMDQgMTM2LjUgMjQ4LjUgNTEgMjQ4LjUtNTEgMjA0LTEzNi41IDEzNi41LTIwNCA1MS0yNDguNXptMTI4IDBxMCAyMDktMTAzIDM4NS41dC0yNzkuNSAyNzkuNS0zODUuNSAxMDMtMzg1LjUtMTAzLTI3OS41LTI3OS41LTEwMy0zODUuNSAxMDMtMzg1LjUgMjc5LjUtMjc5LjUgMzg1LjUtMTAzIDM4NS41IDEwMyAyNzkuNSAyNzkuNSAxMDMgMzg1LjV6Ii8+PC9zdmc+', icon: 'assets/img/localPlugin.webp',
methods: [], methods: [],
location: 'sidePanel', location: 'sidePanel',
type: 'iframe', type: 'iframe',

@ -62,7 +62,7 @@ const profile = {
displayName: 'Plugin manager', displayName: 'Plugin manager',
methods: [], methods: [],
events: [], events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNzU1IDQ1M3EzNyAzOCAzNyA5MC41dC0zNyA5MC41bC00MDEgNDAwIDE1MCAxNTAtMTYwIDE2MHEtMTYzIDE2My0zODkuNSAxODYuNXQtNDExLjUtMTAwLjVsLTM2MiAzNjJoLTE4MXYtMTgxbDM2Mi0zNjJxLTEyNC0xODUtMTAwLjUtNDExLjV0MTg2LjUtMzg5LjVsMTYwLTE2MCAxNTAgMTUwIDQwMC00MDFxMzgtMzcgOTEtMzd0OTAgMzcgMzcgOTAuNS0zNyA5MC41bC00MDAgNDAxIDIzNCAyMzQgNDAxLTQwMHEzOC0zNyA5MS0zN3Q5MCAzN3oiLz48L3N2Zz4=', icon: 'assets/img/pluginManager.webp',
description: 'Start/stop services, modules and plugins', description: 'Start/stop services, modules and plugins',
kind: 'settings', kind: 'settings',
location: 'sidePanel', location: 'sidePanel',

@ -1,6 +1,6 @@
'use strict' 'use strict'
const yo = require('yo-yo') const yo = require('yo-yo')
const remixLib = require('remix-lib') const remixLib = require('@remix-project/remix-lib')
const SourceMappingDecoder = remixLib.SourceMappingDecoder const SourceMappingDecoder = remixLib.SourceMappingDecoder
const globalRegistry = require('../../global/registry') const globalRegistry = require('../../global/registry')

@ -1,5 +1,5 @@
'use strict' 'use strict'
const remixLib = require('remix-lib') const remixLib = require('@remix-project/remix-lib')
const csjs = require('csjs-inject') const csjs = require('csjs-inject')
const SourceMappingDecoder = remixLib.SourceMappingDecoder const SourceMappingDecoder = remixLib.SourceMappingDecoder
const AstWalker = remixLib.AstWalker const AstWalker = remixLib.AstWalker

@ -20,8 +20,9 @@ require('brace/mode/javascript')
require('brace/mode/python') require('brace/mode/python')
require('brace/mode/json') require('brace/mode/json')
require('brace/mode/rust') require('brace/mode/rust')
require('brace/theme/chaos') require('brace/theme/chrome') // for all light themes
require('brace/theme/chrome') require('brace/theme/chaos') // for all dark themes
require('../../../assets/js/editor/darkTheme') // a custom one for remix 'Dark' theme
const css = csjs` const css = csjs`
.ace-editor { .ace-editor {
@ -62,10 +63,11 @@ class Editor extends Plugin {
this._themes = { this._themes = {
'light': 'chrome', 'light': 'chrome',
'dark': 'chaos' 'dark': 'chaos',
'remixDark': 'remixDark'
} }
themeModule.events.on('themeChanged', (theme) => { themeModule.events.on('themeChanged', (theme) => {
this.setTheme(theme.quality) this.setTheme(theme.name === 'Dark' ? 'remixDark' : theme.quality)
}) })
// Init // Init
@ -324,6 +326,13 @@ class Editor extends Plugin {
* @param {string} content Content of the document or update. * @param {string} content Content of the document or update.
*/ */
open (path, content) { open (path, content) {
/*
we have the following cases:
- URL prepended with "localhost"
- URL prepended with "browser"
- URL not prepended with the file explorer. We assume (as it is in the whole app, that this is a "browser" URL
*/
if (!path.startsWith('localhost') && !path.startsWith('browser')) path = `browser/${path}`
if (!this.sessions[path]) { if (!this.sessions[path]) {
const session = this._createSession(content, this._getMode(path)) const session = this._createSession(content, this._getMode(path))
this.sessions[path] = session this.sessions[path] = session

@ -217,7 +217,7 @@ contract Ballot {
var ballotTest = `pragma solidity >=0.4.22 <0.7.0; var ballotTest = `pragma solidity >=0.4.22 <0.7.0;
import "remix_tests.sol"; // this import is automatically injected by Remix. import "remix_tests.sol"; // this import is automatically injected by Remix.
import "./3_Ballot.sol"; import "../3_Ballot.sol";
contract BallotTest { contract BallotTest {
@ -245,5 +245,5 @@ module.exports = {
storage: { name: '1_Storage.sol', content: storage }, storage: { name: '1_Storage.sol', content: storage },
owner: { name: '2_Owner.sol', content: owner }, owner: { name: '2_Owner.sol', content: owner },
ballot: { name: '3_Ballot.sol', content: ballot }, ballot: { name: '3_Ballot.sol', content: ballot },
ballot_test: { name: '4_Ballot_test.sol', content: ballotTest } ballot_test: { name: 'tests/4_Ballot_test.sol', content: ballotTest }
} }

@ -102,10 +102,10 @@ function fileExplorer (localRegistry, files, menuItems) {
function fileAdded (filepath) { function fileAdded (filepath) {
self.ensureRoot(() => { self.ensureRoot(() => {
var folderpath = filepath.split('/').slice(0, -1).join('/') const folderpath = filepath.split('/').slice(0, -1).join('/')
const currentTree = self.treeView.nodeAt(folderpath)
var currentTree = self.treeView.nodeAt(folderpath) if (!self.treeView.isExpanded(folderpath)) self.treeView.expand(folderpath)
if (currentTree && self.treeView.isExpanded(folderpath)) { if (currentTree) {
self.files.resolveDirectory(folderpath, (error, fileTree) => { self.files.resolveDirectory(folderpath, (error, fileTree) => {
if (error) console.error(error) if (error) console.error(error)
if (!fileTree) return if (!fileTree) return
@ -136,15 +136,20 @@ function fileExplorer (localRegistry, files, menuItems) {
if (!fileTree) return if (!fileTree) return
fileTree = normalize(folderpath, fileTree) fileTree = normalize(folderpath, fileTree)
self.treeView.updateNodeFromJSON(folderpath, fileTree, true) self.treeView.updateNodeFromJSON(folderpath, fileTree, true)
if (!self.treeView.isExpanded(folderpath)) self.treeView.expand(folderpath)
}) })
}) })
} }
function fileRemoved (filepath) { function fileRemoved (filepath) {
var label = self.treeView.labelAt(filepath) const label = self.treeView.labelAt(filepath)
filepath = filepath.split('/').slice(0, -1).join('/')
if (label && label.parentElement) { if (label && label.parentElement) {
label.parentElement.removeChild(label) label.parentElement.removeChild(label)
} }
self.updatePath(filepath)
} }
function fileRenamed (oldName, newName, isFolder) { function fileRenamed (oldName, newName, isFolder) {
@ -240,10 +245,6 @@ function fileExplorer (localRegistry, files, menuItems) {
if (!removeFolder) { if (!removeFolder) {
tooltip(`failed to remove ${key}. Make sure the directory is empty before removing it.`) tooltip(`failed to remove ${key}. Make sure the directory is empty before removing it.`)
} else {
const { type } = fileManager.currentFileProvider()
self.updatePath(type)
} }
}, () => {}) }, () => {})
} }
@ -286,9 +287,6 @@ function fileExplorer (localRegistry, files, menuItems) {
if (!removeFile) { if (!removeFile) {
tooltip(`Failed to remove file ${key}.`) tooltip(`Failed to remove file ${key}.`)
} else {
const { type } = fileManager.currentFileProvider()
self.updatePath(type)
} }
}, },
() => {} () => {}
@ -438,12 +436,12 @@ fileExplorer.prototype.uploadFile = function (event) {
let files = this.files let files = this.files
function loadFile () { function loadFile () {
var fileReader = new FileReader() var fileReader = new FileReader()
fileReader.onload = function (event) { fileReader.onload = async function (event) {
if (helper.checkSpecialChars(file.name)) { if (helper.checkSpecialChars(file.name)) {
modalDialogCustom.alert('Special characters are not allowed') modalDialogCustom.alert('Special characters are not allowed')
return return
} }
var success = files.set(name, event.target.result) var success = await files.set(name, event.target.result)
if (!success) { if (!success) {
modalDialogCustom.alert('Failed to create file ' + name) modalDialogCustom.alert('Failed to create file ' + name)
} else { } else {
@ -638,7 +636,7 @@ fileExplorer.prototype.renderMenuItems = function () {
<label <label
id=${action} id=${action}
data-id="fileExplorerUploadFile${action}" data-id="fileExplorerUploadFile${action}"
class="${icon} ${css.newFile}" class="${icon} mb-0 ${css.newFile}"
title="${title}" title="${title}"
> >
<input id="fileUpload" data-id="fileExplorerFileUpload" type="file" onchange=${(event) => { <input id="fileUpload" data-id="fileExplorerFileUpload" type="file" onchange=${(event) => {
@ -649,13 +647,14 @@ fileExplorer.prototype.renderMenuItems = function () {
` `
} else { } else {
return yo` return yo`
<span <span
id=${action} id=${action}
data-id="fileExplorerNewFile${action}" data-id="fileExplorerNewFile${action}"
onclick=${(event) => { event.stopPropagation(); this[ action ]() }} onclick=${(event) => { event.stopPropagation(); this[ action ]() }}
class="newFile ${icon} ${css.newFile}" class="newFile ${icon} ${css.newFile}"
title=${title}> title=${title}
</span> >
</span>
` `
} }
}) })

@ -20,10 +20,10 @@ const profile = {
name: 'fileManager', name: 'fileManager',
displayName: 'File manager', displayName: 'File manager',
description: 'Service - read/write to any files or folders, require giving permissions', description: 'Service - read/write to any files or folders, require giving permissions',
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDM4NHE0MCAwIDY4IDI4dDI4IDY4djEyMTZxMCA0MC0yOCA2OHQtNjggMjhoLTk2MHEtNDAgMC02OC0yOHQtMjgtNjh2LTI4OGgtNTQ0cS00MCAwLTY4LTI4dC0yOC02OHYtNjcycTAtNDAgMjAtODh0NDgtNzZsNDA4LTQwOHEyOC0yOCA3Ni00OHQ4OC0yMGg0MTZxNDAgMCA2OCAyOHQyOCA2OHYzMjhxNjgtNDAgMTI4LTQwaDQxNnptLTU0NCAyMTNsLTI5OSAyOTloMjk5di0yOTl6bS02NDAtMzg0bC0yOTkgMjk5aDI5OXYtMjk5em0xOTYgNjQ3bDMxNi0zMTZ2LTQxNmgtMzg0djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djY0MGg1MTJ2LTI1NnEwLTQwIDIwLTg4dDQ4LTc2em05NTYgODA0di0xMTUyaC0zODR2NDE2cTAgNDAtMjggNjh0LTY4IDI4aC00MTZ2NjQwaDg5NnoiLz48L3N2Zz4=', icon: 'assets/img/fileManager.webp',
permission: true, permission: true,
version: packageJson.version, version: packageJson.version,
methods: ['file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'rename', 'readdir', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile'], methods: ['file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'rename', 'mkdir', 'readdir', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile'],
kind: 'file-system' kind: 'file-system'
} }
const errorMsg = { const errorMsg = {
@ -38,7 +38,7 @@ const createError = (err) => {
} }
class FileManager extends Plugin { class FileManager extends Plugin {
constructor (editor) { constructor (editor, appManager) {
super(profile) super(profile)
this.openedFiles = {} // list all opened files this.openedFiles = {} // list all opened files
this.events = new EventEmitter() this.events = new EventEmitter()
@ -46,6 +46,7 @@ class FileManager extends Plugin {
this._components = {} this._components = {}
this._components.compilerImport = new CompilerImport() this._components.compilerImport = new CompilerImport()
this._components.registry = globalRegistry this._components.registry = globalRegistry
this.appManager = appManager
this.init() this.init()
} }
@ -194,10 +195,24 @@ class FileManager extends Plugin {
* @returns {void} * @returns {void}
*/ */
async rename (oldPath, newPath) { async rename (oldPath, newPath) {
await this.__handleExists(oldPath, `Cannot rename ${oldPath}`) await this._handleExists(oldPath, `Cannot rename ${oldPath}`)
const isFile = await this.isFile(oldPath) const isFile = await this.isFile(oldPath)
const newPathExists = await this.exists(newPath)
const provider = this.fileProviderOf(oldPath)
this.fileRenamedEvent(oldPath, newPath, !isFile) if (isFile) {
if (newPathExists) {
modalDialogCustom.alert('File already exists.')
return
}
return provider.rename(oldPath, newPath, false)
} else {
if (newPathExists) {
modalDialogCustom.alert('Folder already exists.')
return
}
return provider.rename(oldPath, newPath, true)
}
} }
/** /**
@ -479,6 +494,32 @@ class FileManager extends Plugin {
return this._deps.filesProviders['browser'] return this._deps.filesProviders['browser']
} }
// returns the list of directories inside path
dirList (path) {
const dirPaths = []
const collectList = (path) => {
return new Promise((resolve, reject) => {
this.readdir(path).then((ls) => {
const promises = Object.keys(ls).map((item, index) => {
const root = (path.indexOf('/') === -1) ? path : path.substr(0, path.indexOf('/'))
const curPath = `${root}/${item}` // adding 'browser' or 'localhost'
if (ls[item].isDirectory && !dirPaths.includes(curPath)) {
dirPaths.push(curPath)
resolve(dirPaths)
}
return new Promise((resolve, reject) => { resolve() })
})
Promise.all(promises).then(() => { resolve(dirPaths) })
})
})
}
return collectList(path)
}
isRemixDActive () {
return this.appManager.isActive('remixd')
}
saveCurrentFile () { saveCurrentFile () {
var currentFile = this._deps.config.get('currentFile') var currentFile = this._deps.config.get('currentFile')
if (currentFile && this.editor.current()) { if (currentFile && this.editor.current()) {

@ -4,7 +4,7 @@ const CompilerImport = require('../compiler/compiler-imports')
const EventManager = require('../../lib/events') const EventManager = require('../../lib/events')
const modalDialogCustom = require('../ui/modal-dialog-custom') const modalDialogCustom = require('../ui/modal-dialog-custom')
const tooltip = require('../ui/tooltip') const tooltip = require('../ui/tooltip')
const remixLib = require('remix-lib') const remixLib = require('@remix-project/remix-lib')
const Storage = remixLib.Storage const Storage = remixLib.Storage
class FileProvider { class FileProvider {
@ -93,9 +93,13 @@ class FileProvider {
cb = cb || function () {} cb = cb || function () {}
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
var exists = window.remixFileSystem.existsSync(unprefixedpath) var exists = window.remixFileSystem.existsSync(unprefixedpath)
if (exists && window.remixFileSystem.readFileSync(unprefixedpath, 'utf8') === content) return true if (exists && window.remixFileSystem.readFileSync(unprefixedpath, 'utf8') === content) {
cb()
return true
}
if (!exists && unprefixedpath.indexOf('/') !== -1) { if (!exists && unprefixedpath.indexOf('/') !== -1) {
this.createDir(path) // the last element is the filename and we should remove it
this.createDir(path.substr(0, path.lastIndexOf('/')))
} }
try { try {
window.remixFileSystem.writeFileSync(unprefixedpath, content) window.remixFileSystem.writeFileSync(unprefixedpath, content)
@ -113,9 +117,8 @@ class FileProvider {
} }
createDir (path, cb) { createDir (path, cb) {
var unprefixedpath = this.removePrefix(path) const unprefixedpath = this.removePrefix(path)
const paths = unprefixedpath.split('/') const paths = unprefixedpath.split('/')
paths.pop() // last element should the filename
if (paths.length && paths[0] === '') paths.shift() if (paths.length && paths[0] === '') paths.shift()
let currentCheck = '' let currentCheck = ''
paths.forEach((value) => { paths.forEach((value) => {
@ -154,35 +157,38 @@ class FileProvider {
* @param {*} path is the folder to be removed * @param {*} path is the folder to be removed
*/ */
remove (path) { remove (path) {
path = this.removePrefix(path) return new Promise((resolve, reject) => {
if (window.remixFileSystem.existsSync(path)) { path = this.removePrefix(path)
const stat = window.remixFileSystem.statSync(path) if (window.remixFileSystem.existsSync(path)) {
try { const stat = window.remixFileSystem.statSync(path)
if (!stat.isDirectory()) { try {
return this.removeFile(path) if (!stat.isDirectory()) {
} else { resolve(this.removeFile(path))
const items = window.remixFileSystem.readdirSync(path)
if (items.length !== 0) {
items.forEach((item, index) => {
const curPath = `${path}/${item}`
if (window.remixFileSystem.statSync(curPath).isDirectory()) { // delete folder
this.remove(curPath)
} else { // delete file
this.removeFile(curPath)
}
})
if (window.remixFileSystem.readdirSync(path).length === 0) window.remixFileSystem.rmdirSync(path, console.log)
} else { } else {
// folder is empty const items = window.remixFileSystem.readdirSync(path)
window.remixFileSystem.rmdirSync(path, console.log) if (items.length !== 0) {
items.forEach((item, index) => {
const curPath = `${path}/${item}`
if (window.remixFileSystem.statSync(curPath).isDirectory()) { // delete folder
this.remove(curPath)
} else { // delete file
this.removeFile(curPath)
}
})
if (window.remixFileSystem.readdirSync(path).length === 0) window.remixFileSystem.rmdirSync(path, console.log)
} else {
// folder is empty
window.remixFileSystem.rmdirSync(path, console.log)
}
this.event.trigger('fileRemoved', [this._normalizePath(path)])
} }
} catch (e) {
console.log(e)
return resolve(false)
} }
} catch (e) {
console.log(e)
return false
} }
} return resolve(true)
return true })
} }
removeFile (path) { removeFile (path) {
@ -216,10 +222,11 @@ class FileProvider {
window.remixFileSystem.readdir(path, (error, files) => { window.remixFileSystem.readdir(path, (error, files) => {
var ret = {} var ret = {}
if (files) { if (files) {
files.forEach(element => { files.forEach(element => {
const absPath = (path === '/' ? '' : path) + '/' + element const absPath = (path === '/' ? '' : path) + '/' + element
ret[absPath.indexOf('/') === 0 ? absPath.replace('/', '') : absPath] = { isDirectory: window.remixFileSystem.statSync(absPath).isDirectory() } ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: window.remixFileSystem.statSync(absPath).isDirectory() }
// ^ ret does not accept path starting with '/' // ^ ret does not accept path starting with '/'
}) })
} }

@ -1,12 +1,10 @@
'use strict' 'use strict'
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var pathtool = require('path')
module.exports = class RemixDProvider { module.exports = class RemixDProvider {
constructor (remixd) { constructor (appManager) {
this.event = new EventManager() this.event = new EventManager()
this._remixd = remixd this._appManager = appManager
this.remixd = remixapi(remixd, this)
this.type = 'localhost' this.type = 'localhost'
this.error = { 'EEXIST': 'File already exists' } this.error = { 'EEXIST': 'File already exists' }
this._isReady = false this._isReady = false
@ -14,39 +12,34 @@ module.exports = class RemixDProvider {
this._readOnlyMode = false this._readOnlyMode = false
this.filesContent = {} this.filesContent = {}
this.files = {} this.files = {}
}
_registerEvent () {
var remixdEvents = ['connecting', 'connected', 'errored', 'closed'] var remixdEvents = ['connecting', 'connected', 'errored', 'closed']
remixdEvents.forEach((value) => { remixdEvents.forEach((value) => {
remixd.event.register(value, (event) => { this._appManager.on('remixd', value, (event) => {
this.event.trigger(value, [event]) this.event.trigger(value, [event])
}) })
}) })
remixd.event.register('notified', (data) => { this._appManager.on('remixd', 'folderAdded', (path) => {
if (data.scope === 'sharedfolder') { this.event.trigger('folderAdded', [this.addPrefix(path)])
if (data.name === 'created') { })
this.init(() => {
this.event.trigger('fileAdded', [this.type + '/' + data.value.path, data.value.isReadOnly, data.value.isFolder]) this._appManager.on('remixd', 'fileAdded', (path) => {
}) this.event.trigger('fileAdded', [this.addPrefix(path)])
} else if (data.name === 'removed') { })
this.init(() => {
this.event.trigger('fileRemoved', [this.type + '/' + data.value.path]) this._appManager.on('remixd', 'fileChanged', (path) => {
}) this.event.trigger('fileChanged', [this.addPrefix(path)])
} else if (data.name === 'changed') { })
this._remixd.call('sharedfolder', 'get', {path: data.value}, (error, content) => {
if (error) { this._appManager.on('remixd', 'fileRemoved', (path) => {
console.log(error) this.event.trigger('fileRemoved', [this.addPrefix(path)])
} else { })
var path = this.type + '/' + data.value
this.filesContent[path] = content this._appManager.on('remixd', 'fileRenamed', (oldPath, newPath) => {
this.event.trigger('fileExternallyChanged', [path, content]) this.event.trigger('fileRemoved', [this.addPrefix(oldPath), this.addPrefix(newPath)])
}
})
} else if (data.name === 'rootFolderChanged') {
// new path has been set, we should reset
this.event.trigger('folderAdded', [this.type + '/'])
}
}
}) })
} }
@ -55,38 +48,35 @@ module.exports = class RemixDProvider {
} }
close (cb) { close (cb) {
this.remixd.exit()
this._isReady = false this._isReady = false
cb() cb()
} }
init (cb) { init (cb) {
this._remixd.ensureSocket((error) => { if (this._isReady) return cb && cb()
if (error) return cb(error) this._appManager.call('remixd', 'folderIsReadOnly', {})
this._isReady = !error .then((result) => {
this._remixd.call('sharedfolder', 'folderIsReadOnly', {}, (error, result) => { this._isReady = true
this._readOnlyMode = result this._readOnlyMode = result
cb(error) this._registerEvent()
}) cb && cb()
}).catch((error) => {
cb && cb(error)
}) })
} }
// @TODO: refactor all `this._remixd.call(....)` uses into `this.remixd[api](...)` exists (path, cb) {
// where `api = ...`: if (!this._isReady) return cb && cb('provider not ready')
// this.remixd.read(path, (error, content) => {})
// this.remixd.write(path, content, (error, result) => {})
// this.remixd.rename(path1, path2, (error, result) => {})
// this.remixd.remove(path, (error, result) => {})
// this.remixd.dir(path, (error, filesList) => {})
//
// this.remixd.exists(path, (error, isValid) => {})
async exists (path, cb) {
const unprefixedpath = this.removePrefix(path) const unprefixedpath = this.removePrefix(path)
const callId = await this._remixd.call('sharedfolder', 'exists', {path: unprefixedpath})
const result = await this._remixd.receiveResponse(callId)
return cb(null, result) return this._appManager.call('remixd', 'exists', { path: unprefixedpath })
.then((result) => {
if (cb) return cb(null, result)
return result
}).catch((error) => {
if (cb) return cb(error)
throw new Error(error)
})
} }
getNormalizedName (path) { getNormalizedName (path) {
@ -98,67 +88,75 @@ module.exports = class RemixDProvider {
} }
get (path, cb) { get (path, cb) {
if (!this._isReady) return cb && cb('provider not ready')
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
this._remixd.call('sharedfolder', 'get', {path: unprefixedpath}, (error, file) => { this._appManager.call('remixd', 'get', { path: unprefixedpath })
if (!error) { .then((file) => {
this.filesContent[path] = file.content this.filesContent[path] = file.content
if (file.readonly) { this._readOnlyFiles[path] = 1 } if (file.readonly) { this._readOnlyFiles[path] = 1 }
cb(error, file.content) cb(null, file.content)
} else { }).catch((error) => {
// display the last known content. if (error) console.log(error)
// TODO should perhaps better warn the user that the file is not synced. // display the last known content.
cb(null, this.filesContent[path]) // TODO should perhaps better warn the user that the file is not synced.
} return cb(null, this.filesContent[path])
}) })
} }
set (path, content, cb) { async set (path, content, cb) {
var unprefixedpath = this.removePrefix(path) if (!this._isReady) return cb && cb('provider not ready')
this._remixd.call('sharedfolder', 'set', {path: unprefixedpath, content: content}, (error, result) => { const unprefixedpath = this.removePrefix(path)
if (cb) return cb(error, result)
var path = this.type + '/' + unprefixedpath return this._appManager.call('remixd', 'set', { path: unprefixedpath, content: content }).then(async (result) => {
this.event.trigger('fileChanged', [path]) if (cb) return cb(null, result)
}).catch((error) => {
if (cb) return cb(error)
throw new Error(error)
}) })
return true
} }
isReadOnly (path) { isReadOnly (path) {
return this._readOnlyMode || this._readOnlyFiles[path] === 1 return this._readOnlyMode || this._readOnlyFiles[path] === 1
} }
async remove (path) { remove (path) {
var unprefixedpath = this.removePrefix(path) return new Promise((resolve, reject) => {
const callId = await this._remixd.call('sharedfolder', 'remove', {path: unprefixedpath}, (error, result) => { if (!this._isReady) return reject('provider not ready')
if (error) console.log(error) const unprefixedpath = this.removePrefix(path)
var path = this.type + '/' + unprefixedpath this._appManager.call('remixd', 'remove', { path: unprefixedpath })
delete this.filesContent[path] .then(result => {
this.init(() => { const path = this.type + '/' + unprefixedpath
this.event.trigger('fileRemoved', [path])
delete this.filesContent[path]
resolve(true)
this.init()
}).catch(error => {
if (error) console.log(error)
resolve(false)
}) })
}) })
return await this._remixd.receiveResponse(callId)
} }
rename (oldPath, newPath, isFolder) { rename (oldPath, newPath, isFolder) {
var unprefixedoldPath = this.removePrefix(oldPath) const unprefixedoldPath = this.removePrefix(oldPath)
var unprefixednewPath = this.removePrefix(newPath) const unprefixednewPath = this.removePrefix(newPath)
this._remixd.call('sharedfolder', 'rename', {oldPath: unprefixedoldPath, newPath: unprefixednewPath}, (error, result) => { if (!this._isReady) return new Promise((resolve, reject) => reject('provider not ready'))
if (error) { return this._appManager.call('remixd', 'rename', { oldPath: unprefixedoldPath, newPath: unprefixednewPath })
console.log(error) .then(result => {
if (this.error[error.code]) error = this.error[error.code] const newPath = this.type + '/' + unprefixednewPath
this.event.trigger('fileRenamedError', [this.error[error.code]]) const oldPath = this.type + '/' + unprefixedoldPath
} else {
var newPath = this.type + '/' + unprefixednewPath this.filesContent[newPath] = this.filesContent[oldPath]
var oldPath = this.type + '/' + unprefixedoldPath delete this.filesContent[oldPath]
this.filesContent[newPath] = this.filesContent[oldPath] this.init(() => {
delete this.filesContent[oldPath] this.event.trigger('fileRenamed', [oldPath, newPath, isFolder])
this.init(() => { })
this.event.trigger('fileRenamed', [oldPath, newPath, isFolder]) return result
}) }).catch(error => {
} console.log(error)
if (this.error[error.code]) error = this.error[error.code]
this.event.trigger('fileRenamedError', [this.error[error.code]])
}) })
return true
} }
isExternalFolder (path) { isExternalFolder (path) {
@ -171,56 +169,33 @@ module.exports = class RemixDProvider {
return path return path
} }
addPrefix (path) {
if (path.indexOf(this.type + '/') === 0) return path
if (path[0] === '/') return 'localhost' + path
return 'localhost/' + path
}
resolveDirectory (path, callback) { resolveDirectory (path, callback) {
var self = this var self = this
if (path[0] === '/') path = path.substring(1) if (path[0] === '/') path = path.substring(1)
if (!path) return callback(null, { [self.type]: { } }) if (!path) return callback(null, { [self.type]: { } })
path = self.removePrefix(path) const unprefixedpath = this.removePrefix(path)
self.remixd.dir(path, callback)
if (!this._isReady) return callback && callback('provider not ready')
this._appManager.call('remixd', 'resolveDirectory', { path: unprefixedpath }).then((result) => {
callback(null, result)
}).catch(callback)
} }
async isDirectory (path) { async isDirectory (path) {
const unprefixedpath = this.removePrefix(path) const unprefixedpath = this.removePrefix(path)
const callId = await this._remixd.call('sharedfolder', 'isDirectory', {path: unprefixedpath}) if (!this._isReady) throw new Error('provider not ready')
return await this._appManager.call('remixd', 'isDirectory', {path: unprefixedpath})
return await this._remixd.receiveResponse(callId)
} }
async isFile (path) { async isFile (path) {
const unprefixedpath = this.removePrefix(path) const unprefixedpath = this.removePrefix(path)
const callId = await this._remixd.call('sharedfolder', 'isFile', {path: unprefixedpath}) if (!this._isReady) throw new Error('provider not ready')
return await this._appManager.call('remixd', 'isFile', { path: unprefixedpath })
return await this._remixd.receiveResponse(callId)
} }
} }
function remixapi (remixd, self) {
const read = (path, callback) => {
path = '' + (path || '')
path = pathtool.join('./', path)
remixd.call('sharedfolder', 'get', { path }, (error, content) => callback(error, content))
}
const write = (path, content, callback) => {
path = '' + (path || '')
path = pathtool.join('./', path)
remixd.call('sharedfolder', 'set', { path, content }, (error, result) => callback(error, result))
}
const rename = (path, newpath, callback) => {
path = '' + (path || '')
path = pathtool.join('./', path)
remixd.call('sharedfolder', 'rename', { oldPath: path, newPath: newpath }, (error, result) => callback(error, result))
}
const remove = (path, callback) => {
path = '' + (path || '')
path = pathtool.join('./', path)
remixd.call('sharedfolder', 'remove', { path }, (error, result) => callback(error, result))
}
const dir = (path, callback) => {
path = '' + (path || '')
path = pathtool.join('./', path)
remixd.call('sharedfolder', 'resolveDirectory', { path }, (error, filesList) => callback(error, filesList))
}
const exit = () => { remixd.close() }
const api = { read, write, rename, remove, dir, exit, event: remixd.event }
return api
}

@ -1,5 +1,5 @@
import isElectron from 'is-electron' import isElectron from 'is-electron'
import { Plugin } from '@remixproject/engine' import { WebsocketPlugin } from '@remixproject/engine'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
var yo = require('yo-yo') var yo = require('yo-yo')
var modalDialog = require('../ui/modaldialog') var modalDialog = require('../ui/modaldialog')
@ -20,14 +20,16 @@ var css = csjs`
const profile = { const profile = {
name: 'remixd', name: 'remixd',
methods: [], displayName: 'RemixD',
url: 'ws://127.0.0.1:65520',
methods: ['folderIsReadOnly', 'resolveDirectory', 'get', 'exists', 'isFile', 'set', 'rename', 'remove', 'isDirectory', 'list'],
events: [], events: [],
description: 'Using Remixd daemon, allow to access file system', description: 'Using Remixd daemon, allow to access file system',
kind: 'other', kind: 'other',
version: packageJson.version version: packageJson.version
} }
export class RemixdHandle extends Plugin { export class RemixdHandle extends WebsocketPlugin {
constructor (fileSystemExplorer, locahostProvider, appManager) { constructor (fileSystemExplorer, locahostProvider, appManager) {
super(profile) super(profile)
this.fileSystemExplorer = fileSystemExplorer this.fileSystemExplorer = fileSystemExplorer
@ -36,16 +38,19 @@ export class RemixdHandle extends Plugin {
} }
deactivate () { deactivate () {
this.fileSystemExplorer.hide()
if (super.socket) super.deactivate()
this.locahostProvider.close((error) => { this.locahostProvider.close((error) => {
if (error) console.log(error) if (error) console.log(error)
}) })
} }
activate () { activate () {
this.fileSystemExplorer.show()
this.connectToLocalhost() this.connectToLocalhost()
} }
canceled () { async canceled () {
this.appManager.ensureDeactivated('remixd') this.appManager.ensureDeactivated('remixd')
} }
@ -53,9 +58,9 @@ export class RemixdHandle extends Plugin {
* connect to localhost if no connection and render the explorer * connect to localhost if no connection and render the explorer
* disconnect from localhost if connected and remove the explorer * disconnect from localhost if connected and remove the explorer
* *
* @param {String} txHash - hash of the transaction * @param {String} txHash - hash of the transaction
*/ */
connectToLocalhost () { async connectToLocalhost () {
let connection = (error) => { let connection = (error) => {
if (error) { if (error) {
console.log(error) console.log(error)
@ -65,13 +70,22 @@ export class RemixdHandle extends Plugin {
) )
this.canceled() this.canceled()
} else { } else {
this.fileSystemExplorer.ensureRoot() const intervalId = setInterval(() => {
if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed
clearInterval(intervalId)
console.log(error)
modalDialogCustom.alert(
'Connection to remixd terminated' +
'Please make sure remixd is still running in the background.'
)
this.canceled()
}
}, 3000)
this.locahostProvider.init(_ => this.fileSystemExplorer.ensureRoot())
} }
} }
if (this.locahostProvider.isConnected()) { if (this.locahostProvider.isConnected()) {
this.locahostProvider.close((error) => { this.deactivate()
if (error) console.log(error)
})
} else if (!isElectron()) { } else if (!isElectron()) {
// warn the user only if he/she is in the browser context // warn the user only if he/she is in the browser context
modalDialog( modalDialog(
@ -79,7 +93,18 @@ export class RemixdHandle extends Plugin {
remixdDialog(), remixdDialog(),
{ label: 'Connect', { label: 'Connect',
fn: () => { fn: () => {
this.locahostProvider.init((error) => connection(error)) try {
super.activate()
setTimeout(() => {
if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed
connection(new Error('Connection with daemon failed.'))
} else {
connection()
}
}, 3000)
} catch (error) {
connection(error)
}
} }
}, },
{ label: 'Cancel', { label: 'Cancel',
@ -89,7 +114,12 @@ export class RemixdHandle extends Plugin {
} }
) )
} else { } else {
this.locahostProvider.init((error) => connection(error)) try {
super.activate()
setTimeout(() => { connection() }, 2000)
} catch (error) {
connection(error)
}
} }
} }
} }

@ -32,7 +32,7 @@ const profile = {
displayName: 'File explorers', displayName: 'File explorers',
methods: [], methods: [],
events: [], events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDM4NHE0MCAwIDY4IDI4dDI4IDY4djEyMTZxMCA0MC0yOCA2OHQtNjggMjhoLTk2MHEtNDAgMC02OC0yOHQtMjgtNjh2LTI4OGgtNTQ0cS00MCAwLTY4LTI4dC0yOC02OHYtNjcycTAtNDAgMjAtODh0NDgtNzZsNDA4LTQwOHEyOC0yOCA3Ni00OHQ4OC0yMGg0MTZxNDAgMCA2OCAyOHQyOCA2OHYzMjhxNjgtNDAgMTI4LTQwaDQxNnptLTU0NCAyMTNsLTI5OSAyOTloMjk5di0yOTl6bS02NDAtMzg0bC0yOTkgMjk5aDI5OXYtMjk5em0xOTYgNjQ3bDMxNi0zMTZ2LTQxNmgtMzg0djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djY0MGg1MTJ2LTI1NnEwLTQwIDIwLTg4dDQ4LTc2em05NTYgODA0di0xMTUyaC0zODR2NDE2cTAgNDAtMjggNjh0LTY4IDI4aC00MTZ2NjQwaDg5NnoiLz48L3N2Zz4=', icon: 'assets/img/fileManager.webp',
description: ' - ', description: ' - ',
kind: 'fileexplorer', kind: 'fileexplorer',
location: 'sidePanel', location: 'sidePanel',
@ -64,8 +64,8 @@ module.exports = class Filepanel extends ViewPlugin {
const explorers = yo` const explorers = yo`
<div> <div>
<div class=${css.treeview} data-id="filePanelFileExplorerTree">${fileExplorer.init()}</div> <div class="pl-2 ${css.treeview}" data-id="filePanelFileExplorerTree">${fileExplorer.init()}</div>
<div class="filesystemexplorer ${css.treeview}">${fileSystemExplorer.init()}</div> <div class="pl-2 filesystemexplorer ${css.treeview}">${fileSystemExplorer.init()}</div>
</div> </div>
` `

@ -80,7 +80,7 @@ export class MainView {
self.data = { self.data = {
_layout: { _layout: {
top: { top: {
offset: self._deps.config.get('terminal-top-offset') || 150, offset: self._terminalTopOffset(),
show: true show: true
} }
} }
@ -97,6 +97,9 @@ export class MainView {
}) })
} }
} }
_terminalTopOffset () {
return this._deps.config.get('terminal-top-offset') || 150
}
_adjustLayout (direction, delta) { _adjustLayout (direction, delta) {
var limitUp = 0 var limitUp = 0
var limitDown = 32 var limitDown = 32
@ -126,6 +129,12 @@ export class MainView {
self._components.terminal.scroll2bottom() self._components.terminal.scroll2bottom()
} }
} }
minimizeTerminal () {
this._adjustLayout('top')
}
showTerminal (offset) {
this._adjustLayout('top', offset || this._terminalTopOffset())
}
getTerminal () { getTerminal () {
return this._components.terminal return this._components.terminal
} }

File diff suppressed because one or more lines are too long

@ -147,11 +147,11 @@ class Terminal extends Plugin {
self._view.icon = yo` self._view.icon = yo`
<i onmouseenter=${hover} onmouseleave=${hover} onmousedown=${minimize} <i onmouseenter=${hover} onmouseleave=${hover} onmousedown=${minimize}
class="btn btn-secondary btn-sm align-items-center ${css.toggleTerminal} fas fa-angle-double-down" data-id="terminalToggleIcon"></i>` class="mx-2 ${css.toggleTerminal} fas fa-angle-double-down" data-id="terminalToggleIcon"></i>`
self._view.dragbar = yo` self._view.dragbar = yo`
<div onmousedown=${mousedown} class=${css.dragbarHorizontal}></div>` <div onmousedown=${mousedown} class=${css.dragbarHorizontal}></div>`
self._view.pendingTxCount = yo`<div class=${css.pendingTx} title='Pending Transactions'>0</div>` self._view.pendingTxCount = yo`<div class="mx-2" title='Pending Transactions'>0</div>`
self._view.inputSearch = yo`<input self._view.inputSearch = yo`<input
spellcheck="false" spellcheck="false"
type="text" type="text"
@ -166,21 +166,22 @@ class Terminal extends Plugin {
${self._view.dragbar} ${self._view.dragbar}
<div class="${css.menu} border-top border-dark bg-light" data-id="terminalToggleMenu"> <div class="${css.menu} border-top border-dark bg-light" data-id="terminalToggleMenu">
${self._view.icon} ${self._view.icon}
<div class=${css.clear} id="clearConsole" data-id="terminalClearConsole" onclick=${clear}> <div class="mx-2" id="clearConsole" data-id="terminalClearConsole" onclick=${clear}>
<i class="fas fa-ban" aria-hidden="true" title="Clear console" <i class="fas fa-ban" aria-hidden="true" title="Clear console"
onmouseenter=${hover} onmouseleave=${hover}></i> onmouseenter=${hover} onmouseleave=${hover}></i>
</div> </div>
${self._view.pendingTxCount} ${self._view.pendingTxCount}
<div class=${css.verticalLine}></div> <div class=${css.verticalLine}></div>
<div class="form-check"> <div class="mx-2 d-flex pb-1 align-items-center ${css.listenOnNetwork} custom-control custom-checkbox">
<input <input
class="pb-0 form-check-input custom-control-input"
id="listenNetworkCheck" id="listenNetworkCheck"
onchange=${listenOnNetwork} onchange=${listenOnNetwork}
type="checkbox" class="form-check-input " type="checkbox"
title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you" title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you"
> >
<label <label
class="${css.listenOnNetworkLabel} form-check-label" class="pt-1 form-check-label custom-control-label text-nowrap""
title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you" title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you"
for="listenNetworkCheck" for="listenNetworkCheck"
> >

@ -11,7 +11,7 @@ const profile = {
displayName: 'Solidity static analysis', displayName: 'Solidity static analysis',
methods: [], methods: [],
events: [], events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMjA0OCIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMjA0OCAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0yMDQ4IDE1MzZ2MTI4aC0yMDQ4di0xNTM2aDEyOHYxNDA4aDE5MjB6bS0xMjgtMTI0OHY0MzVxMCAyMS0xOS41IDI5LjV0LTM1LjUtNy41bC0xMjEtMTIxLTYzMyA2MzNxLTEwIDEwLTIzIDEwdC0yMy0xMGwtMjMzLTIzMy00MTYgNDE2LTE5Mi0xOTIgNTg1LTU4NXExMC0xMCAyMy0xMHQyMyAxMGwyMzMgMjMzIDQ2NC00NjQtMTIxLTEyMXEtMTYtMTYtNy41LTM1LjV0MjkuNS0xOS41aDQzNXExNCAwIDIzIDl0OSAyM3oiLz48L3N2Zz4=', icon: 'assets/img/staticAnalysis.webp',
description: 'Checks the contract code for security vulnerabilities and bad practices.', description: 'Checks the contract code for security vulnerabilities and bad practices.',
kind: 'analysis', kind: 'analysis',
location: 'sidePanel', location: 'sidePanel',

File diff suppressed because one or more lines are too long

@ -1,6 +1,6 @@
const async = require('async') const async = require('async')
const EventEmitter = require('events') const EventEmitter = require('events')
var remixTests = require('remix-tests') var remixTests = require('@remix-project/remix-tests')
var Compiler = require('@remix-project/remix-solidity').Compiler var Compiler = require('@remix-project/remix-solidity').Compiler
var CompilerImport = require('../../compiler/compiler-imports') var CompilerImport = require('../../compiler/compiler-imports')
@ -68,7 +68,7 @@ class CompileTab {
const sources = { [target]: { content } } const sources = { [target]: { content } }
this.event.emit('startingCompilation') this.event.emit('startingCompilation')
// setTimeout fix the animation on chrome... (animation triggered by 'staringCompilation') // setTimeout fix the animation on chrome... (animation triggered by 'staringCompilation')
setTimeout(() => this.compiler.compile(sources, target), 100) setTimeout(() => { this.compiler.compile(sources, target); resolve() }, 100)
}) })
}) })
} }

@ -1,12 +1,11 @@
const yo = require('yo-yo') const yo = require('yo-yo')
const minixhr = require('minixhr')
const helper = require('../../../lib/helper') const helper = require('../../../lib/helper')
const addTooltip = require('../../ui/tooltip') const addTooltip = require('../../ui/tooltip')
const semver = require('semver') const semver = require('semver')
const modalDialogCustom = require('../../ui/modal-dialog-custom') const modalDialogCustom = require('../../ui/modal-dialog-custom')
const css = require('../styles/compile-tab-styles') const css = require('../styles/compile-tab-styles')
import { canUseWorker } from '../../compiler/compiler-utils' import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from '../../compiler/compiler-utils'
class CompilerContainer { class CompilerContainer {
@ -24,8 +23,7 @@ class CompilerContainer {
timeout: 300, timeout: 300,
allversions: null, allversions: null,
selectedVersion: null, selectedVersion: null,
defaultVersion: 'soljson-v0.6.6+commit.6c089d02.js', // this default version is defined: in makeMockCompiler (for browser test) and in package.json (downloadsolc_root) for the builtin compiler defaultVersion: 'soljson-v0.6.6+commit.6c089d02.js' // this default version is defined: in makeMockCompiler (for browser test) and in package.json (downloadsolc_root) for the builtin compiler
baseurl: 'https://solc-bin.ethereum.org/bin'
} }
} }
@ -320,17 +318,21 @@ class CompilerContainer {
this.compileIfAutoCompileOn() this.compileIfAutoCompileOn()
} }
/*
The following functions are handlers for internal events.
*/
onchangeOptimize () { onchangeOptimize () {
this.compileTabLogic.setOptimize(!!this._view.optimize.checked) this.compileTabLogic.setOptimize(!!this._view.optimize.checked)
this.compileIfAutoCompileOn() this.compileIfAutoCompileOn()
} }
onchangeLanguage (event) { onchangeLanguage () {
this.compileTabLogic.setLanguage(event.target.value) this.compileTabLogic.setLanguage(this._view.languageSelector.value)
this.compileIfAutoCompileOn() this.compileIfAutoCompileOn()
} }
onchangeEvmVersion (_) { onchangeEvmVersion () {
let s = this._view.evmVersionSelector let s = this._view.evmVersionSelector
let v = s.value let v = s.value
if (v === 'default') { if (v === 'default') {
@ -340,12 +342,44 @@ class CompilerContainer {
this.compileIfAutoCompileOn() this.compileIfAutoCompileOn()
} }
onchangeLoadVersion (event) { onchangeLoadVersion () {
this.data.selectedVersion = this._view.versionSelector.value this.data.selectedVersion = this._view.versionSelector.value
this._updateVersionSelector() this._updateVersionSelector()
this._updateLanguageSelector() this._updateLanguageSelector()
} }
/*
The following functions map with the above event handlers.
They are an external API for modifying the compiler configuration.
*/
setConfiguration (settings) {
this.setLanguage(settings.language)
this.setEvmVersion(settings.evmVersion)
this.setOptimize(settings.optimize)
this.setVersion(settings.version)
}
setOptimize (enabled) {
this._view.optimize.checked = enabled
this.onchangeOptimize()
}
setLanguage (lang) {
this._view.languageSelector.value = lang
this.onchangeLanguage()
}
setEvmVersion (version) {
this._view.evmVersionSelector.value = version || 'default'
this.onchangeEvmVersion()
}
setVersion (version) {
this._view.versionSelector.value = `soljson-v${version}.js`
this.onchangeLoadVersion()
}
_shouldBeAdded (version) { _shouldBeAdded (version) {
return !version.includes('nightly') || return !version.includes('nightly') ||
(version.includes('nightly') && this._view.includeNightlies.checked) (version.includes('nightly') && this._view.includeNightlies.checked)
@ -383,13 +417,13 @@ class CompilerContainer {
if (this.data.selectedVersion.indexOf('soljson') !== 0 || helper.checkSpecialChars(this.data.selectedVersion)) { if (this.data.selectedVersion.indexOf('soljson') !== 0 || helper.checkSpecialChars(this.data.selectedVersion)) {
return console.log('loading ' + this.data.selectedVersion + ' not allowed') return console.log('loading ' + this.data.selectedVersion + ' not allowed')
} }
url = `${this.data.baseurl}/${this.data.selectedVersion}` url = `${urlFromVersion(this.data.selectedVersion)}`
} }
// Workers cannot load js on "file:"-URLs and we get a // Workers cannot load js on "file:"-URLs and we get a
// "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium, // "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium,
// resort to non-worker version in that case. // resort to non-worker version in that case.
if (canUseWorker(this.data.selectedVersion)) { if (this.data.selectedVersion !== 'builtin' && canUseWorker(this.data.selectedVersion)) {
this.compileTabLogic.compiler.loadVersion(true, url) this.compileTabLogic.compiler.loadVersion(true, url)
this.setVersionText('(loading using worker)') this.setVersionText('(loading using worker)')
} else { } else {
@ -413,27 +447,42 @@ class CompilerContainer {
if (this._view.version) this._view.version.innerText = text if (this._view.version) this._view.version.innerText = text
} }
fetchAllVersion (callback) { // fetching both normal and wasm builds and creating a [version, baseUrl] map
minixhr(`${this.data.baseurl}/list.json`, (json, event) => { async fetchAllVersion (callback) {
// @TODO: optimise and cache results to improve app loading times #2461 let allVersions, selectedVersion, allVersionsWasm
var allversions, selectedVersion // fetch normal builds
if (event.type !== 'error') { const binRes = await promisedMiniXhr(`${baseURLBin}/list.json`)
try { // fetch wasm builds
const data = JSON.parse(json) const wasmRes = await promisedMiniXhr(`${baseURLWasm}/list.json`)
allversions = data.builds.slice().reverse() if (binRes.event.type === 'error' && wasmRes.event.type === 'error') {
selectedVersion = this.data.defaultVersion allVersions = [{ path: 'builtin', longVersion: 'latest local version' }]
if (this.queryParams.get().version) selectedVersion = this.queryParams.get().version selectedVersion = 'builtin'
} catch (e) { callback(allVersions, selectedVersion)
addTooltip('Cannot load compiler version list. It might have been blocked by an advertisement blocker. Please try deactivating any of them from this page and reload.') }
} try {
} else { allVersions = JSON.parse(binRes.json).builds.slice().reverse()
allversions = [{ path: 'builtin', longVersion: 'latest local version' }] selectedVersion = this.data.defaultVersion
selectedVersion = 'builtin' if (this.queryParams.get().version) selectedVersion = this.queryParams.get().version
if (wasmRes.event.type !== 'error') {
allVersionsWasm = JSON.parse(wasmRes.json).builds.slice().reverse()
} }
callback(allversions, selectedVersion) } catch (e) {
}) addTooltip('Cannot load compiler version list. It might have been blocked by an advertisement blocker. Please try deactivating any of them from this page and reload. Error: ' + e)
}
// replace in allVersions those compiler builds which exist in allVersionsWasm with new once
if (allVersionsWasm && allVersions) {
allVersions.forEach((compiler, index) => {
const wasmIndex = allVersionsWasm.findIndex(wasmCompiler => { return wasmCompiler.longVersion === compiler.longVersion })
if (wasmIndex !== -1) {
allVersions[index] = allVersionsWasm[wasmIndex]
pathToURL[compiler.path] = baseURLWasm
} else {
pathToURL[compiler.path] = baseURLBin
}
})
}
callback(allVersions, selectedVersion)
} }
scheduleCompilation () { scheduleCompilation () {
if (!this.config.get('autoCompile')) return if (!this.config.get('autoCompile')) return
if (this.data.compileTimeout) window.clearTimeout(this.data.compileTimeout) if (this.data.compileTimeout) window.clearTimeout(this.data.compileTimeout)

@ -1,7 +1,7 @@
'use strict' 'use strict'
var solcTranslate = require('solc/translate') var solcTranslate = require('solc/translate')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var txHelper = remixLib.execution.txHelper var txHelper = remixLib.execution.txHelper
module.exports = (contractName, contract, compiledSource) => { module.exports = (contractName, contract, compiledSource) => {

@ -1,5 +1,5 @@
const yo = require('yo-yo') const yo = require('yo-yo')
const remixLib = require('remix-lib') const remixLib = require('@remix-project/remix-lib')
const css = require('./styles/debugger-tab-styles') const css = require('./styles/debugger-tab-styles')
import toaster from '../ui/tooltip' import toaster from '../ui/tooltip'
const DebuggerUI = require('./debugger/debuggerUI') const DebuggerUI = require('./debugger/debuggerUI')
@ -11,7 +11,7 @@ const profile = {
displayName: 'Debugger', displayName: 'Debugger',
methods: ['debug', 'getTrace'], methods: ['debug', 'getTrace'],
events: [], events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDk2MHEwIDI2LTE5IDQ1dC00NSAxOWgtMjI0cTAgMTcxLTY3IDI5MGwyMDggMjA5cTE5IDE5IDE5IDQ1dC0xOSA0NXEtMTggMTktNDUgMTl0LTQ1LTE5bC0xOTgtMTk3cS01IDUtMTUgMTN0LTQyIDI4LjUtNjUgMzYuNS04MiAyOS05NyAxM3YtODk2aC0xMjh2ODk2cS01MSAwLTEwMS41LTEzLjV0LTg3LTMzLTY2LTM5LTQzLjUtMzIuNWwtMTUtMTQtMTgzIDIwN3EtMjAgMjEtNDggMjEtMjQgMC00My0xNi0xOS0xOC0yMC41LTQ0LjV0MTUuNS00Ni41bDIwMi0yMjdxLTU4LTExNC01OC0yNzRoLTIyNHEtMjYgMC00NS0xOXQtMTktNDUgMTktNDUgNDUtMTloMjI0di0yOTRsLTE3My0xNzNxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5IDQ1IDE5bDE3MyAxNzNoODQ0bDE3My0xNzNxMTktMTkgNDUtMTl0NDUgMTkgMTkgNDUtMTkgNDVsLTE3MyAxNzN2Mjk0aDIyNHEyNiAwIDQ1IDE5dDE5IDQ1em0tNDgwLTU3NmgtNjQwcTAtMTMzIDkzLjUtMjI2LjV0MjI2LjUtOTMuNSAyMjYuNSA5My41IDkzLjUgMjI2LjV6Ii8+PC9zdmc+', icon: 'assets/img/debuggerLogo.webp',
description: 'Debug transactions', description: 'Debug transactions',
kind: 'debugging', kind: 'debugging',
location: 'sidePanel', location: 'sidePanel',
@ -56,19 +56,25 @@ class DebuggerTab extends ViewPlugin {
}) })
this.debuggerUI = new DebuggerUI( this.debuggerUI = new DebuggerUI(
this,
this.el.querySelector('#debugger'), this.el.querySelector('#debugger'),
this.blockchain,
(address, receipt) => { (address, receipt) => {
const target = (address && remixLib.helpers.trace.isContractCreation(address)) ? receipt.contractAddress : address const target = (address && remixLib.helpers.trace.isContractCreation(address)) ? receipt.contractAddress : address
return this.call('fetchAndCompile', 'resolve', target || receipt.contractAddress || receipt.to, '.debug', this.blockchain.web3()) return this.call('fetchAndCompile', 'resolve', target || receipt.contractAddress || receipt.to, '.debug', this.blockchain.web3())
}) }
)
this.call('manager', 'activatePlugin', 'source-verification') this.call('manager', 'activatePlugin', 'source-verification').catch(e => console.log(e.message))
// this.call('manager', 'activatePlugin', 'udapp') // this.call('manager', 'activatePlugin', 'udapp')
return this.el return this.el
} }
deactivate () {
this.debuggerUI.deleteHighlights()
super.deactivate()
}
debug (hash) { debug (hash) {
if (this.debuggerUI) this.debuggerUI.debug(hash) if (this.debuggerUI) this.debuggerUI.debug(hash)
} }

@ -11,7 +11,7 @@ var EventManager = require('../../../lib/events')
var globalRegistry = require('../../../global/registry') var globalRegistry = require('../../../global/registry')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var init = remixLib.init var init = remixLib.init
@ -22,17 +22,12 @@ var css = csjs`
.statusMessage { .statusMessage {
margin-left: 15px; margin-left: 15px;
} }
.innerShift {
padding: 2px;
margin-left: 10px;
}
` `
class DebuggerUI { class DebuggerUI {
constructor (container, blockchain, fetchContractAndCompile) { constructor (debuggerModule, component, fetchContractAndCompile) {
this.registry = globalRegistry this.debuggerModule = debuggerModule
this.blockchain = blockchain
this.fetchContractAndCompile = fetchContractAndCompile this.fetchContractAndCompile = fetchContractAndCompile
this.event = new EventManager() this.event = new EventManager()
@ -48,62 +43,62 @@ class DebuggerUI {
this.view this.view
container.appendChild(this.render()) component.appendChild(this.render())
this.setEditor() this.setEditor()
} }
setEditor () { setEditor () {
const self = this this.editor = globalRegistry.get('editor').api
this.editor = this.registry.get('editor').api
self.editor.event.register('breakpointCleared', (fileName, row) => { this.editor.event.register('breakpointCleared', (fileName, row) => {
if (self.debugger) self.debugger.breakPointManager.remove({fileName: fileName, row: row}) if (this.debugger) this.debugger.breakPointManager.remove({fileName: fileName, row: row})
}) })
self.editor.event.register('breakpointAdded', (fileName, row) => { this.editor.event.register('breakpointAdded', (fileName, row) => {
if (self.debugger) self.debugger.breakPointManager.add({fileName: fileName, row: row}) if (this.debugger) this.debugger.breakPointManager.add({fileName: fileName, row: row})
}) })
self.editor.event.register('contentChanged', function () { this.editor.event.register('contentChanged', () => {
if (self.debugger) self.debugger.unload() if (this.debugger) this.debugger.unload()
}) })
} }
listenToEvents () { listenToEvents () {
const self = this if (!this.debugger) return
if (!self.debugger) return
this.debugger.event.register('debuggerStatus', function (isActive) { this.debugger.event.register('debuggerStatus', async (isActive) => {
self.sourceHighlighter.currentSourceLocation(null) await this.debuggerModule.call('editor', 'discardHighlight')
self.isActive = isActive this.isActive = isActive
}) })
this.debugger.event.register('newSourceLocation', async function (lineColumnPos, rawLocation) { this.debugger.event.register('newSourceLocation', async (lineColumnPos, rawLocation) => {
const contracts = await self.fetchContractAndCompile( const contracts = await this.fetchContractAndCompile(
self.currentReceipt.contractAddress || self.currentReceipt.to, this.currentReceipt.contractAddress || this.currentReceipt.to,
self.currentReceipt) this.currentReceipt)
if (contracts) { if (contracts) {
const path = contracts.getSourceName(rawLocation.file) const path = contracts.getSourceName(rawLocation.file)
if (path) self.sourceHighlighter.currentSourceLocationFromfileName(lineColumnPos, path) if (path) {
await this.debuggerModule.call('editor', 'discardHighlight')
await this.debuggerModule.call('editor', 'highlight', lineColumnPos, path)
}
} }
}) })
this.debugger.event.register('debuggerUnloaded', self.unLoad.bind(this)) this.debugger.event.register('debuggerUnloaded', () => this.unLoad())
} }
startTxBrowser () { startTxBrowser () {
const self = this
let txBrowser = new TxBrowser() let txBrowser = new TxBrowser()
this.txBrowser = txBrowser this.txBrowser = txBrowser
txBrowser.event.register('requestDebug', function (blockNumber, txNumber, tx) { txBrowser.event.register('requestDebug', (blockNumber, txNumber, tx) => {
if (self.debugger) self.debugger.unload() if (this.debugger) this.debugger.unload()
self.startDebugging(blockNumber, txNumber, tx) this.startDebugging(blockNumber, txNumber, tx)
}) })
txBrowser.event.register('unloadRequested', this, function (blockNumber, txIndex, tx) { txBrowser.event.register('unloadRequested', this, (blockNumber, txIndex, tx) => {
if (self.debugger) self.debugger.unload() if (this.debugger) this.debugger.unload()
}) })
} }
@ -113,13 +108,13 @@ class DebuggerUI {
getDebugWeb3 () { getDebugWeb3 () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.blockchain.detectNetwork((error, network) => { this.debuggerModule.blockchain.detectNetwork((error, network) => {
let web3 let web3
if (error || !network) { if (error || !network) {
web3 = init.web3DebugNode(this.blockchain.web3()) web3 = init.web3DebugNode(this.debuggerModule.blockchain.web3())
} else { } else {
const webDebugNode = init.web3DebugNode(network.name) const webDebugNode = init.web3DebugNode(network.name)
web3 = !webDebugNode ? this.blockchain.web3() : webDebugNode web3 = !webDebugNode ? this.debuggerModule.blockchain.web3() : webDebugNode
} }
init.extendWeb3(web3) init.extendWeb3(web3)
resolve(web3) resolve(web3)
@ -134,7 +129,7 @@ class DebuggerUI {
this.currentReceipt = await web3.eth.getTransactionReceipt(txNumber) this.currentReceipt = await web3.eth.getTransactionReceipt(txNumber)
this.debugger = new Debugger({ this.debugger = new Debugger({
web3, web3,
offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api, offsetToLineColumnConverter: globalRegistry.get('offsettolinecolumnconverter').api,
compilationResult: async (address) => { compilationResult: async (address) => {
try { try {
return await this.fetchContractAndCompile(address, this.currentReceipt) return await this.fetchContractAndCompile(address, this.currentReceipt)
@ -164,7 +159,7 @@ class DebuggerUI {
this.currentReceipt = await web3.eth.getTransactionReceipt(hash) this.currentReceipt = await web3.eth.getTransactionReceipt(hash)
const debug = new Debugger({ const debug = new Debugger({
web3, web3,
offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api, offsetToLineColumnConverter: globalRegistry.get('offsettolinecolumnconverter').api,
compilationResult: async (address) => { compilationResult: async (address) => {
try { try {
return await this.fetchContractAndCompile(address, this.currentReceipt) return await this.fetchContractAndCompile(address, this.currentReceipt)
@ -186,26 +181,28 @@ class DebuggerUI {
} }
render () { render () {
this.debuggerPanelsView = yo`<div class="${css.innerShift}"></div>` this.debuggerPanelsView = yo`<div class="px-2"></div>`
this.debuggerHeadPanelsView = yo`<div class="${css.innerShift}"></div>` this.debuggerHeadPanelsView = yo`<div class="px-2"></div>`
this.stepManagerView = yo`<div class="${css.innerShift}"></div>` this.stepManagerView = yo`<div class="px-2"></div>`
var view = yo`<div> var view = yo`
<div class="${css.innerShift}"> <div>
<div class="px-2">
${this.txBrowser.render()} ${this.txBrowser.render()}
${this.debuggerHeadPanelsView}
${this.stepManagerView} ${this.stepManagerView}
${this.debuggerHeadPanelsView}
</div> </div>
<div class="${css.statusMessage}" >${this.statusMessage}</div> <div class="${css.statusMessage}">${this.statusMessage}</div>
${this.debuggerPanelsView} ${this.debuggerPanelsView}
</div>` </div>
`
if (!this.view) { if (!this.view) {
this.view = view this.view = view
} }
return view return view
} }
unLoad () { async unLoad () {
yo.update(this.debuggerHeadPanelsView, yo`<div></div>`) yo.update(this.debuggerHeadPanelsView, yo`<div></div>`)
yo.update(this.debuggerPanelsView, yo`<div></div>`) yo.update(this.debuggerPanelsView, yo`<div></div>`)
yo.update(this.stepManagerView, yo`<div></div>`) yo.update(this.stepManagerView, yo`<div></div>`)
@ -218,6 +215,10 @@ class DebuggerUI {
this.event.trigger('traceUnloaded') this.event.trigger('traceUnloaded')
} }
async deleteHighlights () {
await this.debuggerModule.call('editor', 'discardHighlight')
}
renderDebugger () { renderDebugger () {
yo.update(this.debuggerHeadPanelsView, this.vmDebugger.renderHead()) yo.update(this.debuggerHeadPanelsView, this.vmDebugger.renderHead())
yo.update(this.debuggerPanelsView, this.vmDebugger.render()) yo.update(this.debuggerPanelsView, this.vmDebugger.render())

@ -45,14 +45,14 @@ function ButtonNavigator () {
ButtonNavigator.prototype.render = function () { ButtonNavigator.prototype.render = function () {
var self = this var self = this
var view = yo`<div class="${css.buttons}"> var view = yo`<div class="${css.buttons}">
<div class="${css.stepButtons} btn-group p-1"> <div class="${css.stepButtons} btn-group py-1">
<button id='overback' class='btn btn-primary btn-sm ${css.navigator} ${css.stepButton} fas fa-reply' title='Step over back' onclick=${function () { self.event.trigger('stepOverBack') }} disabled=${this.overBackDisabled} ></button> <button id='overback' class='btn btn-primary btn-sm ${css.navigator} ${css.stepButton} fas fa-reply' title='Step over back' onclick=${function () { self.event.trigger('stepOverBack') }} disabled=${this.overBackDisabled} ></button>
<button id='intoback' data-id="buttonNavigatorIntoBack" class='btn btn-primary btn-sm ${css.navigator} ${css.stepButton} fas fa-level-up-alt' title='Step back' onclick=${function () { self.event.trigger('stepIntoBack') }} disabled=${this.intoBackDisabled} ></button> <button id='intoback' data-id="buttonNavigatorIntoBack" class='btn btn-primary btn-sm ${css.navigator} ${css.stepButton} fas fa-level-up-alt' title='Step back' onclick=${function () { self.event.trigger('stepIntoBack') }} disabled=${this.intoBackDisabled} ></button>
<button id='intoforward' data-id="buttonNavigatorIntoForward" class='btn btn-primary btn-sm ${css.navigator} ${css.stepButton} fas fa-level-down-alt' title='Step into' onclick=${function () { self.event.trigger('stepIntoForward') }} disabled=${this.intoForwardDisabled} ></button> <button id='intoforward' data-id="buttonNavigatorIntoForward" class='btn btn-primary btn-sm ${css.navigator} ${css.stepButton} fas fa-level-down-alt' title='Step into' onclick=${function () { self.event.trigger('stepIntoForward') }} disabled=${this.intoForwardDisabled} ></button>
<button id='overforward' class='btn btn-primary btn-sm ${css.navigator} ${css.stepButton} fas fa-share' title='Step over forward'onclick=${function () { self.event.trigger('stepOverForward') }} disabled=${this.overForwardDisabled} ></button> <button id='overforward' class='btn btn-primary btn-sm ${css.navigator} ${css.stepButton} fas fa-share' title='Step over forward'onclick=${function () { self.event.trigger('stepOverForward') }} disabled=${this.overForwardDisabled} ></button>
</div> </div>
<div class="${css.jumpButtons} btn-group p-1"> <div class="${css.jumpButtons} btn-group py-1">
<button class='btn btn-primary btn-sm ${css.navigator} ${css.jumpButton} fas fa-step-backward' id='jumppreviousbreakpoint' data-id="buttonNavigatorJumpPreviousBreakpoint" title='Jump to the previous breakpoint' onclick=${function () { self.event.trigger('jumpPreviousBreakpoint') }} disabled=${this.jumpPreviousBreakpointDisabled} ></button> <button class='btn btn-primary btn-sm ${css.navigator} ${css.jumpButton} fas fa-step-backward' id='jumppreviousbreakpoint' data-id="buttonNavigatorJumpPreviousBreakpoint" title='Jump to the previous breakpoint' onclick=${function () { self.event.trigger('jumpPreviousBreakpoint') }} disabled=${this.jumpPreviousBreakpointDisabled} ></button>
<button class='btn btn-primary btn-sm ${css.navigator} ${css.jumpButton} fas fa-eject' id='jumpout' title='Jump out' onclick=${function () { self.event.trigger('jumpOut') }} disabled=${this.jumpOutDisabled} ></button> <button class='btn btn-primary btn-sm ${css.navigator} ${css.jumpButton} fas fa-eject' id='jumpout' title='Jump out' onclick=${function () { self.event.trigger('jumpOut') }} disabled=${this.jumpOutDisabled} ></button>
<button class='btn btn-primary btn-sm ${css.navigator} ${css.jumpButton} fas fa-step-forward' id='jumpnextbreakpoint' data-id="buttonNavigatorJumpNextBreakpoint" title='Jump to the next breakpoint' onclick=${function () { self.event.trigger('jumpNextBreakpoint') }} disabled=${this.jumpNextBreakpointDisabled} ></button> <button class='btn btn-primary btn-sm ${css.navigator} ${css.jumpButton} fas fa-step-forward' id='jumpnextbreakpoint' data-id="buttonNavigatorJumpNextBreakpoint" title='Jump to the next breakpoint' onclick=${function () { self.event.trigger('jumpNextBreakpoint') }} disabled=${this.jumpNextBreakpointDisabled} ></button>

@ -43,7 +43,7 @@ class Slider {
render () { render () {
var self = this var self = this
var view = yo`<div> var view = yo`<div>
<input id='slider' data-id="slider" style='width: 100%' type='range' min=0 max=${this.max} value=0 <input id='slider' data-id="slider" class='w-100 my-0' type='range' min=0 max=${this.max} value=0
onchange=${function () { self.onChange() }} oninput=${function () { self.onChange() }} disabled=${this.disabled} /> onchange=${function () { self.onChange() }} oninput=${function () { self.onChange() }} disabled=${this.disabled} />
</div>` </div>`
if (!this.view) { if (!this.view) {

@ -49,10 +49,11 @@ StepManager.prototype.remove = function () {
} }
StepManager.prototype.render = function () { StepManager.prototype.render = function () {
return yo`<div> return yo`
${this.slider.render()} <div class="py-1">
${this.buttonNavigator.render()} ${this.slider.render()}
</div>` ${this.buttonNavigator.render()}
</div>`
} }
module.exports = StepManager module.exports = StepManager

@ -11,11 +11,6 @@ var css = csjs`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.txinputs {
width: 100%;
display: flex;
justify-content: center;
}
.txinput { .txinput {
width: inherit; width: inherit;
font-size: small; font-size: small;
@ -23,16 +18,9 @@ var css = csjs`
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.txbuttons {
width: 100%;
display: flex;
justify-content: center;
}
.txbutton { .txbutton {
width: inherit; width: inherit;
} }
.txbuttonstart {
}
.txbutton:hover { .txbutton:hover {
} }
.vmargin { .vmargin {
@ -110,10 +98,10 @@ TxBrowser.prototype.render = function () {
var view = yo` var view = yo`
<div class="${css.container}"> <div class="${css.container}">
<div class="${css.txContainer}"> <div class="${css.txContainer}">
<div class="${css.txinputs} p-1 input-group"> <div class=" py-1 d-flex justify-content-center w-100 input-group">
${this.state.txNumberInput} ${this.state.txNumberInput}
</div> </div>
<div class="${css.txbuttons} btn-group p-1"> <div class="d-flex justify-content-center w-100 btn-group py-1">
${txButton} ${txButton}
</div> </div>
</div> </div>

@ -1,11 +1,11 @@
'use strict' 'use strict'
var csjs = require('csjs-inject')
var yo = require('yo-yo') var yo = require('yo-yo')
var CodeListView = require('./vmDebugger/CodeListView') var CodeListView = require('./vmDebugger/CodeListView')
var CalldataPanel = require('./vmDebugger/CalldataPanel') var CalldataPanel = require('./vmDebugger/CalldataPanel')
var MemoryPanel = require('./vmDebugger/MemoryPanel') var MemoryPanel = require('./vmDebugger/MemoryPanel')
var CallstackPanel = require('./vmDebugger/CallstackPanel') var CallstackPanel = require('./vmDebugger/CallstackPanel')
var FunctionPanel = require('./vmDebugger/FunctionPanel')
var StackPanel = require('./vmDebugger/StackPanel') var StackPanel = require('./vmDebugger/StackPanel')
var StoragePanel = require('./vmDebugger/StoragePanel') var StoragePanel = require('./vmDebugger/StoragePanel')
var StepDetail = require('./vmDebugger/StepDetail') var StepDetail = require('./vmDebugger/StepDetail')
@ -14,18 +14,6 @@ var SolidityLocals = require('./vmDebugger/SolidityLocals')
var FullStoragesChangesPanel = require('./vmDebugger/FullStoragesChanges') var FullStoragesChangesPanel = require('./vmDebugger/FullStoragesChanges')
var DropdownPanel = require('./vmDebugger/DropdownPanel') var DropdownPanel = require('./vmDebugger/DropdownPanel')
var css = csjs`
.asmCode {
width: 100%;
}
.stepDetail {
width: 100%;
}
.vmheadView {
margin-top:10px;
}
`
function VmDebugger (vmDebuggerLogic) { function VmDebugger (vmDebuggerLogic) {
var self = this var self = this
this.view this.view
@ -48,6 +36,16 @@ function VmDebugger (vmDebuggerLogic) {
this.stackPanel = new StackPanel() this.stackPanel = new StackPanel()
this.vmDebuggerLogic.event.register('traceManagerStackUpdate', this.stackPanel.update.bind(this.stackPanel)) this.vmDebuggerLogic.event.register('traceManagerStackUpdate', this.stackPanel.update.bind(this.stackPanel))
this.functionPanel = new FunctionPanel()
this.vmDebuggerLogic.event.register('functionsStackUpdate', (stack) => {
if (stack === null) return
let functions = []
for (let func of stack) {
functions.push(func.functionDefinition.attributes.name + '(' + func.inputs.join(', ') + ')')
}
this.functionPanel.update(functions)
})
this.storagePanel = new StoragePanel() this.storagePanel = new StoragePanel()
this.vmDebuggerLogic.event.register('traceManagerStorageUpdate', this.storagePanel.update.bind(this.storagePanel)) this.vmDebuggerLogic.event.register('traceManagerStorageUpdate', this.storagePanel.update.bind(this.storagePanel))
@ -107,27 +105,39 @@ function VmDebugger (vmDebuggerLogic) {
self.asmCode.basicPanel.show() self.asmCode.basicPanel.show()
self.stackPanel.basicPanel.show() self.stackPanel.basicPanel.show()
self.functionPanel.basicPanel.show()
self.storagePanel.basicPanel.show() self.storagePanel.basicPanel.show()
self.memoryPanel.basicPanel.show() self.memoryPanel.basicPanel.show()
self.stepDetail.basicPanel.show()
self.calldataPanel.basicPanel.show() self.calldataPanel.basicPanel.show()
self.callstackPanel.basicPanel.show() self.callstackPanel.basicPanel.show()
}) })
this.vmDebuggerLogic.event.register('newCallTree', () => { this.vmDebuggerLogic.event.register('newCallTree', () => {
if (!self.view) return if (!self.view) return
self.functionPanel.basicPanel.show()
self.solidityLocals.basicPanel.show() self.solidityLocals.basicPanel.show()
self.solidityState.basicPanel.show() self.solidityState.basicPanel.show()
self.solidityPanel.hidden = false
}) })
this.vmDebuggerLogic.start() this.vmDebuggerLogic.start()
} }
VmDebugger.prototype.renderHead = function () { VmDebugger.prototype.renderHead = function () {
this.solidityPanel = yo`
<div class="w-100" hidden>
${this.functionPanel.render()}
${this.solidityLocals.render()}
${this.solidityState.render()}
</div>
`
const headView = yo` const headView = yo`
<div id="vmheadView" class="${css.vmheadView} container"> <div id="vmheadView" class="mt-1 px-0">
<div class="row" > <div class="d-flex flex-column">
<div class="${css.asmCode} column">${this.asmCode.render()}</div> ${this.solidityPanel}
<div class="${css.stepDetail} column">${this.stepDetail.render()}</div> <div class="w-100">${this.asmCode.render()}</div>
<div class="w-100">${this.stepDetail.render()}</div>
</div> </div>
</div> </div>
` `
@ -144,10 +154,8 @@ VmDebugger.prototype.remove = function () {
VmDebugger.prototype.render = function () { VmDebugger.prototype.render = function () {
const view = yo` const view = yo`
<div id="vmdebugger" class="pl-2"> <div id="vmdebugger" class="px-2">
<div> <div>
${this.solidityLocals.render()}
${this.solidityState.render()}
${this.stackPanel.render()} ${this.stackPanel.render()}
${this.memoryPanel.render()} ${this.memoryPanel.render()}
${this.storagePanel.render()} ${this.storagePanel.render()}

@ -9,14 +9,6 @@ module.exports = {
font: { font: {
'font-family': 'arial,sans-serif' 'font-family': 'arial,sans-serif'
}, },
innerShift: {
'padding': '2px',
'margin-left': '10px'
},
container: {
'margin': '10px',
'padding': '5px'
},
statusMessage: { statusMessage: {
'margin-left': '15px' 'margin-left': '15px'
}, },

@ -8,7 +8,7 @@ var csjs = require('csjs-inject')
var css = csjs` var css = csjs`
.instructions { .instructions {
overflow-y: scroll; overflow-y: scroll;
max-height: 150px; max-height: 130px;
} }
` `
function CodeListView () { function CodeListView () {
@ -41,12 +41,9 @@ CodeListView.prototype.indexChanged = function (index) {
} }
let codeView = this.view.querySelector('#asmitems') let codeView = this.view.querySelector('#asmitems')
this.itemSelected = codeView.children[index] this.itemSelected = codeView.children[index]
this.itemSelected.style.setProperty('background-color', 'var(--info)') this.itemSelected.style.setProperty('border-color', 'var(--primary)')
this.itemSelected.style.setProperty('color', 'var(--light)') this.itemSelected.style.setProperty('border-style', 'solid')
this.itemSelected.setAttribute('selected', 'selected') this.itemSelected.setAttribute('selected', 'selected')
if (this.itemSelected.firstChild) {
this.itemSelected.firstChild.setAttribute('style', 'margin-left: 2px')
}
codeView.scrollTop = this.itemSelected.offsetTop - parseInt(codeView.offsetTop) codeView.scrollTop = this.itemSelected.offsetTop - parseInt(codeView.offsetTop)
} }
@ -67,9 +64,9 @@ CodeListView.prototype.changed = function (code, address, index) {
CodeListView.prototype.renderAssemblyItems = function () { CodeListView.prototype.renderAssemblyItems = function () {
if (this.code) { if (this.code) {
var codeView = this.code.map(function (item, i) { var codeView = this.code.map(function (item, i) {
return yo`<div key=${i} value=${i}><span>${item}</span></div>` return yo`<div class="px-1" key=${i} value=${i}><span>${item}</span></div>`
}) })
return yo`<div class=${css.instructions} id='asmitems' ref='itemsList'> return yo`<div class="pl-2 my-1 small ${css.instructions}" id='asmitems' ref='itemsList'>
${codeView} ${codeView}
</div>` </div>`
} }

@ -1,12 +1,12 @@
'use strict' 'use strict'
var yo = require('yo-yo') const yo = require('yo-yo')
const copy = require('copy-text-to-clipboard') const copyToClipboard = require('../../../../ui/copy-to-clipboard')
var EventManager = require('../../../../../lib/events') const EventManager = require('../../../../../lib/events')
var TreeView = require('../../../../ui/TreeView') // TODO setup a direct reference to the UI components const TreeView = require('../../../../ui/TreeView') // TODO setup a direct reference to the UI components
var csjs = require('csjs-inject') const csjs = require('csjs-inject')
var css = csjs` const css = csjs`
.title { .title {
display: flex; display: flex;
align-items: center; align-items: center;
@ -24,8 +24,6 @@ var css = csjs`
.eyeButton { .eyeButton {
margin: 3px; margin: 3px;
} }
.eyeButton:hover {
}
.dropdownpanel { .dropdownpanel {
width: 100%; width: 100%;
word-break: break-word; word-break: break-word;
@ -87,7 +85,7 @@ DropdownPanel.prototype.update = function (_data, _header) {
this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'block' this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'block'
this.view.querySelector('.dropdownpanel .dropdownrawcontent').innerText = JSON.stringify(_data, null, '\t') this.view.querySelector('.dropdownpanel .dropdownrawcontent').innerText = JSON.stringify(_data, null, '\t')
if (!this.displayContentOnly) { if (!this.displayContentOnly) {
this.view.querySelector('.title div.btn').style.display = 'block' this.view.querySelector('.title i.fa-copy').style.display = 'block'
this.view.querySelector('.title span').innerText = _header || ' ' this.view.querySelector('.title span').innerText = _header || ' '
} }
this.message('') this.message('')
@ -101,6 +99,11 @@ DropdownPanel.prototype.setContent = function (node) {
yo.update(this.view, this.render(null, node)) yo.update(this.view, this.render(null, node))
} }
DropdownPanel.prototype.copyClipboard = function () {
const content = this.view.querySelector('.dropdownpanel .dropdownrawcontent')
return content.innerText ? content.innerText : content.textContent
}
DropdownPanel.prototype.render = function (overridestyle, node) { DropdownPanel.prototype.render = function (overridestyle, node) {
var content = yo`<div>Empty</div>` var content = yo`<div>Empty</div>`
if (this.json) { if (this.json) {
@ -108,20 +111,21 @@ DropdownPanel.prototype.render = function (overridestyle, node) {
} }
overridestyle === undefined ? {} : overridestyle overridestyle === undefined ? {} : overridestyle
var self = this var self = this
var title = !self.displayContentOnly ? yo`<div class="${css.title} title"> var contentNode = yo`
<div class="${css.icon} fas fa-caret-right" onclick=${function () { self.toggle() }} ></div> <div class='dropdownpanel ${css.dropdownpanel}' style='display:none'>
<div class="${css.name}" onclick=${function () { self.toggle() }} >${this.name}</div><span class="${css.nameDetail}" onclick=${function () { self.toggle() }} ></span>
<div onclick=${function () { self.copyClipboard() }} title='raw' class="${css.eyeButton} btn far fa-clipboard"></div>
</div>` : yo`<div></div>`
var contentNode = yo`<div class='dropdownpanel ${css.dropdownpanel}' style='display:none'>
<i class="${css.refresh} fas fa-sync" aria-hidden="true"></i> <i class="${css.refresh} fas fa-sync" aria-hidden="true"></i>
<div class='dropdowncontent'>${node || content}</div> <div class='dropdowncontent'>${node || content}</div>
<div class='dropdownrawcontent' style='display:none'></div> <div class='dropdownrawcontent' style='display:none'></div>
<div class='message' style='display:none'></div> <div class='message' style='display:none'></div>
</div>` </div>`
var title = !self.displayContentOnly ? yo`<div class="${css.title} py-0 px-1 title">
<div class="${css.icon} fas fa-caret-right" onclick=${function () { self.toggle() }} ></div>
<div class="${css.name}" onclick=${function () { self.toggle() }} >${this.name}</div><span class="${css.nameDetail}" onclick=${function () { self.toggle() }} ></span>
${copyToClipboard(() => this.copyClipboard())}
</div>` : yo`<div></div>`
var view = yo` var view = yo`
<div class="border rounded p-1 m-1 bg-light"> <div class="border rounded px-1 mt-1 bg-light">
<style> <style>
@-moz-keyframes spin { @-moz-keyframes spin {
to { -moz-transform: rotate(359deg); } to { -moz-transform: rotate(359deg); }
@ -143,11 +147,6 @@ DropdownPanel.prototype.render = function (overridestyle, node) {
return view return view
} }
DropdownPanel.prototype.copyClipboard = function () {
var content = this.view.querySelector('.dropdownpanel .dropdownrawcontent')
if (content) copy(content.innerText ? content.innerText : content.textContent)
}
DropdownPanel.prototype.toggle = function () { DropdownPanel.prototype.toggle = function () {
var el = this.view.querySelector('.dropdownpanel') var el = this.view.querySelector('.dropdownpanel')
var caret = this.view.querySelector('.title').firstElementChild var caret = this.view.querySelector('.title').firstElementChild

@ -0,0 +1,17 @@
'use strict'
var DropdownPanel = require('./DropdownPanel')
var yo = require('yo-yo')
function FunctionPanel () {
this.basicPanel = new DropdownPanel('Function Stack', {json: true, displayContentOnly: false})
}
FunctionPanel.prototype.update = function (calldata) {
this.basicPanel.update(calldata)
}
FunctionPanel.prototype.render = function () {
return yo`<div id="FunctionPanel">${this.basicPanel.render()}</div>`
}
module.exports = FunctionPanel

@ -26,7 +26,8 @@ SolidityState.prototype.setUpdating = function () {
SolidityState.prototype.render = function () { SolidityState.prototype.render = function () {
if (this.view) return if (this.view) return
this.view = yo`<div id='soliditystate' > this.view = yo`
<div id='soliditystate' >
${this.basicPanel.render()} ${this.basicPanel.render()}
</div>` </div>`
return this.view return this.view

@ -2,7 +2,7 @@ var yo = require('yo-yo')
var DropdownPanel = require('./DropdownPanel') var DropdownPanel = require('./DropdownPanel')
function StepDetail () { function StepDetail () {
this.basicPanel = new DropdownPanel('Step detail', {json: true, displayContentOnly: true}) this.basicPanel = new DropdownPanel('Step details', {json: true, displayContentOnly: false})
this.detail = { 'vm trace step': '-', 'execution step': '-', 'add memory': '', 'gas': '', 'remaining gas': '-', 'loaded address': '-' } this.detail = { 'vm trace step': '-', 'execution step': '-', 'add memory': '', 'gas': '', 'remaining gas': '-', 'loaded address': '-' }
} }

@ -12,7 +12,7 @@ function formatSelf (key, data) {
if (data.type === 'string') { if (data.type === 'string') {
data.self = JSON.stringify(data.self) data.self = JSON.stringify(data.self)
} }
return yo `<label style='${keyStyle};white-space:pre-wrap;'> ${' ' + key}:<label style=${style}>${' ' + data.self}</label><label style='font-style:italic'> ${data.isProperty || !data.type ? '' : ' ' + data.type}</label></label>` return yo `<label class="mb-0" style='${keyStyle};white-space:pre-wrap;'> ${' ' + key}:<label class="mb-0" style=${style}>${' ' + data.self}</label><label style='font-style:italic'> ${data.isProperty || !data.type ? '' : ' ' + data.type}</label></label>`
} }
function extractData (item, parent, key) { function extractData (item, parent, key) {

@ -1,7 +1,7 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var css = require('../styles/run-tab-styles') var css = require('../styles/run-tab-styles')
var modalDialogCustom = require('../../ui/modal-dialog-custom') var modalDialogCustom = require('../../ui/modal-dialog-custom')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var confirmDialog = require('../../ui/confirmDialog') var confirmDialog = require('../../ui/confirmDialog')
var modalDialog = require('../../ui/modaldialog') var modalDialog = require('../../ui/modaldialog')

@ -1,5 +1,5 @@
var ethJSUtil = require('ethereumjs-util') var ethJSUtil = require('ethereumjs-util')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var txHelper = remixLib.execution.txHelper var txHelper = remixLib.execution.txHelper
var CompilerAbstract = require('../../../compiler/compiler-abstract') var CompilerAbstract = require('../../../compiler/compiler-abstract')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
@ -34,6 +34,9 @@ class DropdownLogic {
this.runView.on('lexon', 'compilationFinished', (file, source, languageVersion, data) => this.runView.on('lexon', 'compilationFinished', (file, source, languageVersion, data) =>
broadcastCompilationResult(file, source, languageVersion, data) broadcastCompilationResult(file, source, languageVersion, data)
) )
this.runView.on('yulp', 'compilationFinished', (file, source, languageVersion, data) =>
broadcastCompilationResult(file, source, languageVersion, data)
)
} }
loadContractFromAddress (address, confirmCb, cb) { loadContractFromAddress (address, confirmCb, cb) {

@ -1,6 +1,6 @@
var async = require('async') var async = require('async')
var ethutil = require('ethereumjs-util') var ethutil = require('ethereumjs-util')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var format = remixLib.execution.txFormat var format = remixLib.execution.txFormat
var txHelper = remixLib.execution.txHelper var txHelper = remixLib.execution.txHelper

@ -1,5 +1,5 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var css = require('../styles/run-tab-styles') var css = require('../styles/run-tab-styles')

@ -1,6 +1,6 @@
const $ = require('jquery') const $ = require('jquery')
const yo = require('yo-yo') const yo = require('yo-yo')
const remixLib = require('remix-lib') const remixLib = require('@remix-project/remix-lib')
const EventManager = remixLib.EventManager const EventManager = remixLib.EventManager
const css = require('../styles/run-tab-styles') const css = require('../styles/run-tab-styles')
const copyToClipboard = require('../../ui/copy-to-clipboard') const copyToClipboard = require('../../ui/copy-to-clipboard')

@ -12,7 +12,7 @@ const profile = {
displayName: 'Settings', displayName: 'Settings',
methods: ['getGithubAccessToken'], methods: ['getGithubAccessToken'],
events: [], events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xMTUyIDg5NnEwLTEwNi03NS0xODF0LTE4MS03NS0xODEgNzUtNzUgMTgxIDc1IDE4MSAxODEgNzUgMTgxLTc1IDc1LTE4MXptNTEyLTEwOXYyMjJxMCAxMi04IDIzdC0yMCAxM2wtMTg1IDI4cS0xOSA1NC0zOSA5MSAzNSA1MCAxMDcgMTM4IDEwIDEyIDEwIDI1dC05IDIzcS0yNyAzNy05OSAxMDh0LTk0IDcxcS0xMiAwLTI2LTlsLTEzOC0xMDhxLTQ0IDIzLTkxIDM4LTE2IDEzNi0yOSAxODYtNyAyOC0zNiAyOGgtMjIycS0xNCAwLTI0LjUtOC41dC0xMS41LTIxLjVsLTI4LTE4NHEtNDktMTYtOTAtMzdsLTE0MSAxMDdxLTEwIDktMjUgOS0xNCAwLTI1LTExLTEyNi0xMTQtMTY1LTE2OC03LTEwLTctMjMgMC0xMiA4LTIzIDE1LTIxIDUxLTY2LjV0NTQtNzAuNXEtMjctNTAtNDEtOTlsLTE4My0yN3EtMTMtMi0yMS0xMi41dC04LTIzLjV2LTIyMnEwLTEyIDgtMjN0MTktMTNsMTg2LTI4cTE0LTQ2IDM5LTkyLTQwLTU3LTEwNy0xMzgtMTAtMTItMTAtMjQgMC0xMCA5LTIzIDI2LTM2IDk4LjUtMTA3LjV0OTQuNS03MS41cTEzIDAgMjYgMTBsMTM4IDEwN3E0NC0yMyA5MS0zOCAxNi0xMzYgMjktMTg2IDctMjggMzYtMjhoMjIycTE0IDAgMjQuNSA4LjV0MTEuNSAyMS41bDI4IDE4NHE0OSAxNiA5MCAzN2wxNDItMTA3cTktOSAyNC05IDEzIDAgMjUgMTAgMTI5IDExOSAxNjUgMTcwIDcgOCA3IDIyIDAgMTItOCAyMy0xNSAyMS01MSA2Ni41dC01NCA3MC41cTI2IDUwIDQxIDk4bDE4MyAyOHExMyAyIDIxIDEyLjV0OCAyMy41eiIvPjwvc3ZnPg==', icon: 'assets/img/settings.webp',
description: 'Remix-IDE settings', description: 'Remix-IDE settings',
kind: 'settings', kind: 'settings',
location: 'sidePanel', location: 'sidePanel',
@ -69,7 +69,7 @@ module.exports = class SettingsTab extends ViewPlugin {
// Gist settings // Gist settings
const token = this.config.get('settings/gist-access-token') const token = this.config.get('settings/gist-access-token')
const gistAccessToken = yo`<input id="gistaccesstoken" data-id="settingsTabGistAccessToken" type="password" class="border form-control-sm mb-2 ${css.inline}" placeholder="Token">` const gistAccessToken = yo`<input id="gistaccesstoken" data-id="settingsTabGistAccessToken" type="password" class="form-control border form-control-sm mb-2 ${css.inline}" placeholder="Token">`
if (token) gistAccessToken.value = token if (token) gistAccessToken.value = token
const removeToken = () => { self.config.set('settings/gist-access-token', ''); gistAccessToken.value = ''; tooltip('Access token removed') } const removeToken = () => { self.config.set('settings/gist-access-token', ''); gistAccessToken.value = ''; tooltip('Access token removed') }
const saveToken = () => { this.config.set('settings/gist-access-token', gistAccessToken.value); tooltip('Access token saved') } const saveToken = () => { this.config.set('settings/gist-access-token', gistAccessToken.value); tooltip('Access token saved') }

@ -2,7 +2,7 @@
var StaticAnalysisRunner = require('remix-analyzer').CodeAnalysis var StaticAnalysisRunner = require('remix-analyzer').CodeAnalysis
var yo = require('yo-yo') var yo = require('yo-yo')
var $ = require('jquery') var $ = require('jquery')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var utils = remixLib.util var utils = remixLib.util
var css = require('./styles/staticAnalysisView-styles') var css = require('./styles/staticAnalysisView-styles')
var Renderer = require('../../ui/renderer') var Renderer = require('../../ui/renderer')
@ -169,7 +169,7 @@ staticAnalysisView.prototype.run = function () {
<span class="d-flex flex-column"> <span class="d-flex flex-column">
<span class="h6 font-weight-bold">${result.name}</span> <span class="h6 font-weight-bold">${result.name}</span>
${item.warning} ${item.warning}
${item.more ? yo`<span><a href="${item.more}" target="blank">more</a></span>` : yo`<span></span>`} ${item.more ? yo`<span><a href="${item.more}" target="_blank">more</a></span>` : yo`<span></span>`}
<span class="" title="Position in ${fileName}">Pos: ${locationString}</span> <span class="" title="Position in ${fileName}">Pos: ${locationString}</span>
</span>` </span>`
self._components.renderer.error( self._components.renderer.error(

@ -2,9 +2,9 @@ var yo = require('yo-yo')
var async = require('async') var async = require('async')
var tooltip = require('../ui/tooltip') var tooltip = require('../ui/tooltip')
var css = require('./styles/test-tab-styles') var css = require('./styles/test-tab-styles')
var remixTests = require('remix-tests') var remixTests = require('@remix-project/remix-tests')
import { ViewPlugin } from '@remixproject/engine' import { ViewPlugin } from '@remixproject/engine'
import { canUseWorker, baseUrl } from '../compiler/compiler-utils' import { canUseWorker, urlFromVersion } from '../compiler/compiler-utils'
const TestTabLogic = require('./testTab/testTab') const TestTabLogic = require('./testTab/testTab')
@ -13,7 +13,7 @@ const profile = {
displayName: 'Solidity unit testing', displayName: 'Solidity unit testing',
methods: ['testFromPath', 'testFromSource'], methods: ['testFromPath', 'testFromSource'],
events: [], events: [],
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4wUDEhQZ0zbrmQAAAfNJREFUWMPF17lrFVEUx/EPaIKovfAScSndjUtULFQSYhHF0r/Dwsa/RywUiTaWgvaChWsiKkSMZte4o7G5A49x7r0zLy/PA6eZOef3PXebuYfu2xCmcQ9b9NgOYw6rwR9ia6/gR7HQBi/8PjavN/w4FivghV9bT/gwlhLwHzjTVPQ8rqAvE3ciA/+O8abwy/gVBG4lijiJ5czIL64FXvhNbCzFnaoBv9AUPo7fEcEb2BDiTuNTAv4NYxX6u/EIM7GZuZoQXcX1sJk+J2K+YrRCexfetsX9xKVyUB9uZ4r4k3j3BSMR+JvIMv2zQfsxkSkiBj9XAd8ZgRf+vmop+nGnAXwlcs534HUm93FsQ9YtIjby7XiVyZ3BntSpyBWxgrMR+FQG/gF76xzNftxtMO1rgo+G5AdBqLBN4d9eCCyHD1En8Oi0j4UPSBE4hcFSERN4Fz7BZRvEZKcjHynBC5/EQI1lGqgJ3xcTmE4kvswUMRBiUvCPKTg8zQi8QKsirxXe5eD7c1N4ALMZoeelIlrhWSpnNmjXsoM1iihmYhueZGIXcKTp7/hQ6UZb5c+Cp2LmglZHVqeIlC+G2/GarNMiFnGsWzfdpkV0Fd7e5czXgC+FvmDdWq35/wVvbzbnI/DhXvV9Q6W+r6fw9hZsKnjX4H8B0Aamri7CrBsAAAAASUVORK5CYII=', icon: 'assets/img/unitTesting.webp',
description: 'Fast tool to generate unit tests for your contracts', description: 'Fast tool to generate unit tests for your contracts',
location: 'sidePanel', location: 'sidePanel',
documentation: 'https://remix-ide.readthedocs.io/en/latest/unittesting.html' documentation: 'https://remix-ide.readthedocs.io/en/latest/unittesting.html'
@ -33,11 +33,13 @@ module.exports = class TestTab extends ViewPlugin {
this.runningTestsNumber = 0 this.runningTestsNumber = 0
this.readyTestsNumber = 0 this.readyTestsNumber = 0
this.areTestsRunning = false this.areTestsRunning = false
this.defaultPath = 'browser/tests'
appManager.event.on('activate', (name) => { appManager.event.on('activate', (name) => {
if (name === 'solidity') this.updateRunAction(fileManager.currentFile()) if (name === 'solidity') this.updateRunAction()
}) })
appManager.event.on('deactivate', (name) => { appManager.event.on('deactivate', (name) => {
if (name === 'solidity') this.updateRunAction(fileManager.currentFile()) if (name === 'solidity') this.updateRunAction()
}) })
} }
@ -56,9 +58,6 @@ module.exports = class TestTab extends ViewPlugin {
}) })
this.fileManager.events.on('noFileSelected', () => { this.fileManager.events.on('noFileSelected', () => {
this.updateGenerateFileAction()
this.updateRunAction()
this.updateTestFileList()
}) })
this.fileManager.events.on('currentFileChanged', (file, provider) => this.updateForNewCurrent(file)) this.fileManager.events.on('currentFileChanged', (file, provider) => this.updateForNewCurrent(file))
@ -86,6 +85,7 @@ module.exports = class TestTab extends ViewPlugin {
} }
listTests () { listTests () {
if (!this.data.allTests) return []
return this.data.allTests.map( return this.data.allTests.map(
testFile => this.createSingleTest(testFile) testFile => this.createSingleTest(testFile)
) )
@ -301,7 +301,7 @@ module.exports = class TestTab extends ViewPlugin {
let runningTest = {} let runningTest = {}
runningTest[path] = { content } runningTest[path] = { content }
const {currentVersion, evmVersion, optimize} = this.compileTab.getCurrentCompilerConfig() const {currentVersion, evmVersion, optimize} = this.compileTab.getCurrentCompilerConfig()
const currentCompilerUrl = baseUrl + '/' + currentVersion const currentCompilerUrl = urlFromVersion(currentVersion)
const compilerConfig = { const compilerConfig = {
currentCompilerUrl, currentCompilerUrl,
evmVersion, evmVersion,
@ -327,7 +327,7 @@ module.exports = class TestTab extends ViewPlugin {
const runningTest = {} const runningTest = {}
runningTest[testFilePath] = { content } runningTest[testFilePath] = { content }
const {currentVersion, evmVersion, optimize} = this.compileTab.getCurrentCompilerConfig() const {currentVersion, evmVersion, optimize} = this.compileTab.getCurrentCompilerConfig()
const currentCompilerUrl = baseUrl + '/' + currentVersion const currentCompilerUrl = urlFromVersion(currentVersion)
const compilerConfig = { const compilerConfig = {
currentCompilerUrl, currentCompilerUrl,
evmVersion, evmVersion,
@ -351,6 +351,13 @@ module.exports = class TestTab extends ViewPlugin {
}) })
} }
updateCurrentPath (e) {
const newValue = e.target.value === '' ? this.defaultPath : e.target.value
this.testTabLogic.setCurrentPath(newValue)
this.updateRunAction()
this.updateForNewCurrent()
}
runTests () { runTests () {
this.areTestsRunning = true this.areTestsRunning = true
this.hasBeenStopped = false this.hasBeenStopped = false
@ -386,21 +393,16 @@ module.exports = class TestTab extends ViewPlugin {
} }
updateGenerateFileAction (currentFile) { updateGenerateFileAction (currentFile) {
let el = yo`<button let el = yo`
class="btn border w-50" <button
data-id="testTabGenerateTestFile" class="btn border w-50"
title="Generate sample test file." data-id="testTabGenerateTestFile"
onclick="${this.testTabLogic.generateTestFile.bind(this.testTabLogic)}" title="Generate sample test file."
> onclick="${this.testTabLogic.generateTestFile.bind(this.testTabLogic)}"
Generate >
</button>` Generate
if ( </button>
!currentFile || `
(currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')
) {
el.setAttribute('disabled', 'disabled')
el.setAttribute('title', 'No solidity file selected')
}
if (!this.generateFileActionElement) { if (!this.generateFileActionElement) {
this.generateFileActionElement = el this.generateFileActionElement = el
} else { } else {
@ -411,13 +413,13 @@ module.exports = class TestTab extends ViewPlugin {
updateRunAction (currentFile) { updateRunAction (currentFile) {
let el = yo` let el = yo`
<button id="runTestsTabRunAction" title="Run tests" data-id="testTabRunTestsTabRunAction" class="w-50 btn btn-primary" onclick="${() => this.runTests()}"> <button id="runTestsTabRunAction" title="Run tests" data-id="testTabRunTestsTabRunAction" class="w-50 btn btn-primary" onclick="${() => this.runTests()}">
<span class="fas fa-play ml-2"></span> <span class="fas fa-play ml-2"></span>
<label class="${css.labelOnBtn} btn btn-primary p-1 ml-2 m-0">Run</label> <label class="${css.labelOnBtn} btn btn-primary p-1 ml-2 m-0">Run</label>
</button> </button>
` `
const isSolidityActive = this.appManager.actives.includes('solidity') const isSolidityActive = this.appManager.isActive('solidity')
if (!currentFile || !isSolidityActive || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) { if (!isSolidityActive || !this.listTests().length) {
el.setAttribute('disabled', 'disabled') el.setAttribute('disabled', 'disabled')
if (!currentFile || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) { if (!currentFile || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) {
el.setAttribute('title', 'No solidity file selected') el.setAttribute('title', 'No solidity file selected')
@ -451,6 +453,7 @@ module.exports = class TestTab extends ViewPlugin {
} else { } else {
yo.update(this.testFilesListElement, el) yo.update(this.testFilesListElement, el)
} }
this.updateRunAction()
return this.testFilesListElement return this.testFilesListElement
} }
@ -482,11 +485,40 @@ module.exports = class TestTab extends ViewPlugin {
return yo`<span class='text-info h6'>Progress: ${ready} finished (of ${this.runningTestsNumber})</span>` return yo`<span class='text-info h6'>Progress: ${ready} finished (of ${this.runningTestsNumber})</span>`
} }
updateDirList () {
for (var o of this.uiPathList.querySelectorAll('option')) o.remove()
this.uiPathList.appendChild(yo`<option>browser</option>`)
if (this.testTabLogic.isRemixDActive()) this.uiPathList.appendChild(yo`<option>localhost</option>`)
if (!this._view.el) return
this.testTabLogic.dirList(this.inputPath.value).then((options) => {
options.forEach((path) => this.uiPathList.appendChild(yo`<option>${path}</option>`))
})
}
render () { render () {
this.onActivationInternal() this.onActivationInternal()
this.testsOutput = yo`<div class="mx-3 mb-2 pb-4 border-top border-primary" hidden='true' id="solidityUnittestsOutput" data-id="testTabSolidityUnitTestsOutput"></a>` this.testsOutput = yo`<div class="mx-3 mb-2 pb-4 border-top border-primary" hidden='true' id="solidityUnittestsOutput" data-id="testTabSolidityUnitTestsOutput"></a>`
this.testsExecutionStopped = yo`<label class="text-warning h6" data-id="testTabTestsExecutionStopped">The test execution has been stopped</label>` this.testsExecutionStopped = yo`<label class="text-warning h6" data-id="testTabTestsExecutionStopped">The test execution has been stopped</label>`
this.testsExecutionStoppedError = yo`<label class="text-danger h6" data-id="testTabTestsExecutionStoppedError">The test execution has been stopped because of error(s) in your test file</label>` this.testsExecutionStoppedError = yo`<label class="text-danger h6" data-id="testTabTestsExecutionStoppedError">The test execution has been stopped because of error(s) in your test file</label>`
this.uiPathList = yo`<datalist id="utPathList"></datalist>`
this.inputPath = yo`<input
placeholder=${this.defaultPath}
list="utPathList"
class="custom-select"
id="utPath"
data-id="uiPathInput"
name="utPath"
style="background-image: var(--primary);"
onkeydown=${(e) => { if (e.keyCode === 191) this.updateDirList() }}
onchange=${(e) => this.updateCurrentPath(e)}/>`
const availablePaths = yo`
<div>
${this.inputPath}
${this.uiPathList}
</div>
`
this.updateDirList()
this.testsExecutionStopped.hidden = true this.testsExecutionStopped.hidden = true
this.testsExecutionStoppedError.hidden = true this.testsExecutionStoppedError.hidden = true
this.resultStatistics = this.createResultLabel() this.resultStatistics = this.createResultLabel()
@ -495,7 +527,9 @@ module.exports = class TestTab extends ViewPlugin {
<div class="${css.testTabView} px-2" id="testView"> <div class="${css.testTabView} px-2" id="testView">
<div class="${css.infoBox}"> <div class="${css.infoBox}">
<p class="text-lg"> Test your smart contract in Solidity.</p> <p class="text-lg"> Test your smart contract in Solidity.</p>
<p> Click on "Generate" to generate a sample test file.</p> <p> Select directory to load and generate test files.</p>
<label>Test directory:</label>
${availablePaths}
</div> </div>
<div class="${css.tests}"> <div class="${css.tests}">
<div class="d-flex p-2"> <div class="d-flex p-2">
@ -518,6 +552,7 @@ module.exports = class TestTab extends ViewPlugin {
</div> </div>
` `
this._view.el = el this._view.el = el
this.testTabLogic.setCurrentPath(this.defaultPath)
this.updateForNewCurrent(this.fileManager.currentFile()) this.updateForNewCurrent(this.fileManager.currentFile())
return el return el
} }

@ -1,52 +1,71 @@
const helper = require('../../../lib/helper.js') const helper = require('../../../lib/helper.js')
const modalDialogCustom = require('../../ui/modal-dialog-custom') const modalDialogCustom = require('../../ui/modal-dialog-custom')
const remixPath = require('path')
class TestTabLogic { class TestTabLogic {
constructor (fileManager) { constructor (fileManager) {
this.fileManager = fileManager this.fileManager = fileManager
this.currentPath = 'browser/tests'
}
setCurrentPath (path) {
if (path.indexOf('/') === 0) return
this.currentPath = path
const fileProvider = this.fileManager.fileProviderOf(path.split('/')[0])
fileProvider.exists(path, (e, res) => { if (!res) fileProvider.createDir(path) })
} }
generateTestFile () { generateTestFile () {
const path = this.fileManager.currentPath() let fileName = this.fileManager.currentFile()
const fileName = this.fileManager.currentFile() const hasCurrent = !!fileName && this.fileManager.currentFile().split('.').pop().toLowerCase() === 'sol'
const fileProvider = this.fileManager.fileProviderOf(path) if (!hasCurrent) fileName = this.currentPath + '/newFile.sol'
const fileProvider = this.fileManager.fileProviderOf(this.currentPath)
if (!fileProvider) return if (!fileProvider) return
helper.createNonClashingNameWithPrefix(fileName, fileProvider, '_test', (error, newFile) => { const splittedFileName = fileName.split('/')
let fileNameToImport = (!hasCurrent) ? fileName : this.currentPath + '/' + splittedFileName[splittedFileName.length - 1]
helper.createNonClashingNameWithPrefix(fileNameToImport, fileProvider, '_test', (error, newFile) => {
if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error) if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error)
const splittedFileName = fileName.split('/') if (!fileProvider.set(newFile, this.generateTestContractSample(hasCurrent, fileName))) return modalDialogCustom.alert('Failed to create test file ' + newFile)
// This is fine for now because test file is created on same path where file to be tested is.
// This should be updated to pass complete path, if test file comes from different directory/path
const fileNameToImport = splittedFileName[splittedFileName.length - 1]
if (!fileProvider.set(newFile, this.generateTestContractSample(fileNameToImport))) return modalDialogCustom.alert('Failed to create test file ' + newFile)
this.fileManager.open(newFile) this.fileManager.open(newFile)
this.fileManager.syncEditor(newFile)
}) })
} }
dirList (path) {
return this.fileManager.dirList(path)
}
isRemixDActive () {
return this.fileManager.isRemixDActive()
}
async getTests (cb) { async getTests (cb) {
const path = this.fileManager.currentPath() if (!this.currentPath) return cb(null, [])
if (!path) return cb(null, []) const provider = this.fileManager.fileProviderOf(this.currentPath)
const provider = this.fileManager.fileProviderOf(path)
if (!provider) return cb(null, []) if (!provider) return cb(null, [])
const tests = [] const tests = []
let files let files
try { try {
files = await this.fileManager.readdir(path) files = await this.fileManager.readdir(this.currentPath)
} catch (e) { } catch (e) {
cb(e.message) cb(e.message)
} }
for (var file in files) { for (var file in files) {
if (/.(_test.sol)$/.exec(file)) tests.push(provider.type + '/' + file) if (/.(_test.sol)$/.exec(file)) tests.push(provider.type + '/' + file)
} }
cb(null, tests, path) cb(null, tests, this.currentPath)
} }
// @todo(#2758): If currently selected file is compiled and compilation result is available, // @todo(#2758): If currently selected file is compiled and compilation result is available,
// 'contractName' should be <compiledContractName> + '_testSuite' // 'contractName' should be <compiledContractName> + '_testSuite'
generateTestContractSample (fileToImport, contractName = 'testSuite') { generateTestContractSample (hasCurrent, fileToImport, contractName = 'testSuite') {
let relative = remixPath.relative(this.currentPath, remixPath.dirname(fileToImport))
if (relative === '') relative = '.'
const comment = hasCurrent ? `import "${relative}/${remixPath.basename(fileToImport)}";` : '// Import here the file to test.'
return `pragma solidity >=0.4.22 <0.7.0; return `pragma solidity >=0.4.22 <0.7.0;
import "remix_tests.sol"; // this import is automatically injected by Remix. import "remix_tests.sol"; // this import is automatically injected by Remix.
import "./${fileToImport}"; ${comment}
// File name has to end with '_test.sol', this file can contain more than one testSuite contracts // File name has to end with '_test.sol', this file can contain more than one testSuite contracts
contract ${contractName} { contract ${contractName} {
@ -65,7 +84,7 @@ contract ${contractName} {
Assert.notEqual(uint(2), uint(3), "2 should not be equal to 3"); Assert.notEqual(uint(2), uint(3), "2 should not be equal to 3");
} }
function checkSuccess2() public view returns (bool) { function checkSuccess2() public pure returns (bool) {
// Use the return value (true or false) to test the contract // Use the return value (true or false) to test the contract
return true; return true;
} }

@ -1,11 +1,12 @@
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import QueryParams from '../../lib/query-params'
import * as packageJson from '../../../package.json' import * as packageJson from '../../../package.json'
import yo from 'yo-yo' import yo from 'yo-yo'
const themes = [ const themes = [
{name: 'Dark', quality: 'dark', url: 'https://res.cloudinary.com/dvtmp0niu/raw/upload/v1584965247/remix-dark_tmkdla.css'}, {name: 'Dark', quality: 'dark', url: 'https://res.cloudinary.com/dvtmp0niu/raw/upload/v1584965247/remix-dark_tmkdla.css'},
{name: 'Light', quality: 'light', url: 'https://res.cloudinary.com/dvtmp0niu/raw/upload/v1584966540/remix-light_t0c780.css'}, {name: 'Light', quality: 'light', url: 'https://res.cloudinary.com/remix/raw/upload/v1594059208/remix-themes/remix-light_csxus2.css'},
{name: 'Cerulean', quality: 'light', url: 'https://bootswatch.com/4/cerulean/bootstrap.min.css'}, {name: 'Cerulean', quality: 'light', url: 'https://bootswatch.com/4/cerulean/bootstrap.min.css'},
{name: 'Flatly', quality: 'light', url: 'https://bootswatch.com/4/flatly/bootstrap.min.css'}, {name: 'Flatly', quality: 'light', url: 'https://bootswatch.com/4/flatly/bootstrap.min.css'},
@ -17,7 +18,6 @@ const themes = [
{name: 'Yeti', quality: 'light', url: 'https://bootswatch.com/4/yeti/bootstrap.min.css'}, {name: 'Yeti', quality: 'light', url: 'https://bootswatch.com/4/yeti/bootstrap.min.css'},
{name: 'Cyborg', quality: 'dark', url: 'https://bootswatch.com/4/cyborg/bootstrap.min.css'}, {name: 'Cyborg', quality: 'dark', url: 'https://bootswatch.com/4/cyborg/bootstrap.min.css'},
{name: 'Darkly', quality: 'dark', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/darkly/bootstrap.min.css'}, {name: 'Darkly', quality: 'dark', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/darkly/bootstrap.min.css'},
{name: 'Slate', quality: 'light', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/slate/bootstrap.min.css'},
{name: 'Superhero', quality: 'dark', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/superhero/bootstrap.min.css'} {name: 'Superhero', quality: 'dark', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/superhero/bootstrap.min.css'}
] ]
@ -38,7 +38,9 @@ export class ThemeModule extends Plugin {
config: registry.get('config').api config: registry.get('config').api
} }
this.themes = themes.reduce((acc, theme) => ({ ...acc, [theme.name]: theme }), {}) this.themes = themes.reduce((acc, theme) => ({ ...acc, [theme.name]: theme }), {})
this.active = this._deps.config.get('settings/theme') ? this._deps.config.get('settings/theme') : 'Dark' const theme = (new QueryParams()).get().theme
this.active = theme || this._deps.config.get('settings/theme') || 'Dark'
this.forced = theme !== undefined
} }
/** Return the active theme */ /** Return the active theme */
@ -76,7 +78,7 @@ export class ThemeModule extends Plugin {
} }
const next = themeName || this.active // Name const next = themeName || this.active // Name
const nextTheme = this.themes[next] // Theme const nextTheme = this.themes[next] // Theme
this._deps.config.set('settings/theme', next) if (!this.forced) this._deps.config.set('settings/theme', next)
document.getElementById('theme-link').setAttribute('href', nextTheme.url) document.getElementById('theme-link').setAttribute('href', nextTheme.url)
document.documentElement.style.setProperty('--theme', nextTheme.quality) document.documentElement.style.setProperty('--theme', nextTheme.quality)
if (themeName) this.active = themeName if (themeName) this.active = themeName

@ -1,5 +1,5 @@
var registry = require('../../global/registry') var registry = require('../../global/registry')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var yo = require('yo-yo') var yo = require('yo-yo')
var EventsDecoder = remixLib.execution.EventsDecoder var EventsDecoder = remixLib.execution.EventsDecoder
var TransactionReceiptResolver = require('../../lib/transactionReceiptResolver') var TransactionReceiptResolver = require('../../lib/transactionReceiptResolver')
@ -32,7 +32,7 @@ export function makeUdapp (blockchain, compilersArtefacts, logHtmlCallback) {
const txlistener = blockchain.getTxListener({ const txlistener = blockchain.getTxListener({
api: { api: {
contracts: function () { contracts: function () {
if (compilersArtefacts['__last']) return compilersArtefacts['__last'].getContracts() if (compilersArtefacts['__last']) return compilersArtefacts.getAllContractDatas()
return null return null
}, },
resolveReceipt: function (tx, cb) { resolveReceipt: function (tx, cb) {

@ -20,7 +20,7 @@ const UniversalDAppUI = require('../ui/universal-dapp-ui')
const profile = { const profile = {
name: 'udapp', name: 'udapp',
displayName: 'Deploy & run transactions', displayName: 'Deploy & run transactions',
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzFfY29weSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4Ig0KCSB5PSIwcHgiIHdpZHRoPSI3NDIuNTQ1cHgiIGhlaWdodD0iNjc2Ljg4NnB4IiB2aWV3Qm94PSIwIC0wLjIwNCA3NDIuNTQ1IDY3Ni44ODYiDQoJIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAtMC4yMDQgNzQyLjU0NSA2NzYuODg2IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxwb2x5Z29uIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBwb2ludHM9IjI5NS45MTEsMC43MTEgNDg4LjkxMSwzMDQuMTg2IDQ4OC45MTEsMzk3LjE4MSAyOTMuOTExLDY3Ni41NTYgDQoJCTc0MS43ODYsMzQ5Ljk0MyAJIi8+DQoJPHBvbHlnb24gc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHBvaW50cz0iNDE3LjA4Myw0MDYuNTg5IDIwOS43OTEsNTE5LjQ5NCAxLjg0Niw0MDYuMjM0IDIwOS43OTEsNjc1Ljg2MyAJIi8+DQoJPHBvbHlnb24gc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHBvaW50cz0iNDE3LjA4MywzMTguNzA3IDIwOS43OTEsMC43MTEgMS44NDYsMzE4LjQyOCAyMDkuNzkxLDQzMS42ODkgCSIvPg0KPC9nPg0KPC9zdmc+DQo=', icon: 'assets/img/deployAndRun.webp',
description: 'execute and save transactions', description: 'execute and save transactions',
kind: 'udapp', kind: 'udapp',
location: 'sidePanel', location: 'sidePanel',

@ -9,7 +9,6 @@ var css = csjs`
-webkit-margin-start: 0px; -webkit-margin-start: 0px;
-webkit-margin-end: 0px; -webkit-margin-end: 0px;
-webkit-padding-start: 0px; -webkit-padding-start: 0px;
margin-left: 10px;
} }
.ul_tv { .ul_tv {
list-style-type: none; list-style-type: none;
@ -22,13 +21,19 @@ var css = csjs`
.caret_tv { .caret_tv {
width: 10px; width: 10px;
flex-shrink: 0; flex-shrink: 0;
padding-right: 5px;
} }
.label_tv { .label_item {
align-items: center; word-break: break-all;
} }
.label_tv>span { .label_key {
min-width: 15%;
max-width: 80%;
word-break: break-word; word-break: break-word;
} }
.label_value {
min-width: 10%;
}
` `
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
@ -75,22 +80,22 @@ class TreeView {
var children = Object.keys(json).map((innerkey) => { var children = Object.keys(json).map((innerkey) => {
return this.renderObject(json[innerkey], json, innerkey, expand, innerkey) return this.renderObject(json[innerkey], json, innerkey, expand, innerkey)
}) })
return yo`<ul key=${key} data-id="treeViewUl${key}" class="${css.ul_tv}">${children}</ul>` return yo`<ul key=${key} data-id="treeViewUl${key}" class="${css.ul_tv} ml-0 px-2">${children}</ul>`
} }
formatData (key, data, children, expand, keyPath) { formatData (key, data, children, expand, keyPath) {
var self = this var self = this
var li = yo`<li key=${keyPath} data-id="treeViewLi${keyPath}" class=${css.li_tv}></li>` var li = yo`<li key=${keyPath} data-id="treeViewLi${keyPath}" class=${css.li_tv}></li>`
var caret = yo`<div class="fas fa-caret-right caret ${css.caret_tv}"></div>` var caret = yo`<div class="px-1 fas fa-caret-right caret ${css.caret_tv}"></div>`
var label = yo` var label = yo`
<div key=${keyPath} data-id="treeViewDiv${keyPath}" class=${css.label_tv}> <div key=${keyPath} data-id="treeViewDiv${keyPath}" class="d-flex flex-row align-items-center">
${caret} ${caret}
<span>${self.formatSelf(key, data, li)}</span> <span class="w-100">${self.formatSelf(key, data, li)}</span>
</div>` </div>`
const expanded = self.expandPath.includes(keyPath) const expanded = self.expandPath.includes(keyPath)
li.appendChild(label) li.appendChild(label)
if (data.children) { if (data.children) {
var list = yo`<ul key=${keyPath} data-id="treeViewUlList${keyPath}" class=${css.ul_tv}>${children}</ul>` var list = yo`<ul key=${keyPath} data-id="treeViewUlList${keyPath}" class="pl-2 ${css.ul_tv}">${children}</ul>`
list.style.display = expanded ? 'block' : 'none' list.style.display = expanded ? 'block' : 'none'
caret.className = list.style.display === 'none' ? `fas fa-caret-right caret ${css.caret_tv}` : `fas fa-caret-down caret ${css.caret_tv}` caret.className = list.style.display === 'none' ? `fas fa-caret-right caret ${css.caret_tv}` : `fas fa-caret-down caret ${css.caret_tv}`
caret.setAttribute('data-id', `treeViewToggle${keyPath}`) caret.setAttribute('data-id', `treeViewToggle${keyPath}`)
@ -99,7 +104,7 @@ class TreeView {
if (self.isExpanded(keyPath)) { if (self.isExpanded(keyPath)) {
if (!self.expandPath.includes(keyPath)) self.expandPath.push(keyPath) if (!self.expandPath.includes(keyPath)) self.expandPath.push(keyPath)
} else { } else {
self.expandPath = self.expandPath.filter(path => path !== keyPath) self.expandPath = self.expandPath.filter(path => !path.startsWith(keyPath))
} }
} }
label.oncontextmenu = function (event) { label.oncontextmenu = function (event) {
@ -164,7 +169,12 @@ class TreeView {
} }
formatSelfDefault (key, data) { formatSelfDefault (key, data) {
return yo`<label>${key}: ${data.self}</label>` return yo`
<div class="d-flex mb-1 flex-row ${css.label_item}">
<label class="small font-weight-bold pr-1 ${css.label_key}">${key}:</label>
<label class="m-0 ${css.label_value}">${data.self}</label>
</div>
`
} }
extractDataDefault (item, parent, key) { extractDataDefault (item, parent, key) {

@ -1,5 +1,5 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var Commands = require('../../lib/commands') var Commands = require('../../lib/commands')

File diff suppressed because one or more lines are too long

@ -3,7 +3,7 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var css = require('../../universal-dapp-styles') var css = require('../../universal-dapp-styles')
var copyToClipboard = require('./copy-to-clipboard') var copyToClipboard = require('./copy-to-clipboard')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var txFormat = remixLib.execution.txFormat var txFormat = remixLib.execution.txFormat
class MultiParamManager { class MultiParamManager {

@ -1,5 +1,5 @@
const yo = require('yo-yo') const yo = require('yo-yo')
const remixLib = require('remix-lib') const remixLib = require('@remix-project/remix-lib')
const confirmDialog = require('./confirmDialog') const confirmDialog = require('./confirmDialog')
const modalCustom = require('./modal-dialog-custom') const modalCustom = require('./modal-dialog-custom')
const modalDialog = require('./modaldialog') const modalDialog = require('./modaldialog')

@ -4,7 +4,7 @@ var copyToClipboard = require('./copy-to-clipboard')
// -------------- styling ---------------------- // -------------- styling ----------------------
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var helper = require('../../lib/helper') var helper = require('../../lib/helper')

@ -9,7 +9,7 @@ var helper = require('../../lib/helper')
var copyToClipboard = require('./copy-to-clipboard') var copyToClipboard = require('./copy-to-clipboard')
var css = require('../../universal-dapp-styles') var css = require('../../universal-dapp-styles')
var MultiParamManager = require('./multiParamManager') var MultiParamManager = require('./multiParamManager')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var txFormat = remixLib.execution.txFormat var txFormat = remixLib.execution.txFormat
const txHelper = remixLib.execution.txHelper const txHelper = remixLib.execution.txHelper
var TreeView = require('./TreeView') var TreeView = require('./TreeView')

@ -1,4 +1,4 @@
const remixLib = require('remix-lib') const remixLib = require('@remix-project/remix-lib')
const txFormat = remixLib.execution.txFormat const txFormat = remixLib.execution.txFormat
const txExecution = remixLib.execution.txExecution const txExecution = remixLib.execution.txExecution
const typeConversion = remixLib.execution.typeConversion const typeConversion = remixLib.execution.typeConversion

@ -8,7 +8,7 @@ export class FramingService {
this.resizeFeature = resizeFeature this.resizeFeature = resizeFeature
} }
start () { start (params) {
this.sidePanel.events.on('toggle', () => { this.sidePanel.events.on('toggle', () => {
this.resizeFeature.panel.clientWidth !== 0 ? this.resizeFeature.hidePanel() : this.resizeFeature.showPanel() this.resizeFeature.panel.clientWidth !== 0 ? this.resizeFeature.hidePanel() : this.resizeFeature.showPanel()
}) })
@ -33,5 +33,13 @@ export class FramingService {
e.preventDefault() e.preventDefault()
} }
}) })
if (params.minimizeterminal) this.mainView.minimizeTerminal()
if (params.minimizesidepanel) this.resizeFeature.hidePanel()
}
embed () {
this.mainView.minimizeTerminal()
this.resizeFeature.hidePanel()
} }
} }

@ -1,7 +1,7 @@
'use strict' 'use strict'
var yo = require('yo-yo') var yo = require('yo-yo')
var async = require('async') var async = require('async')
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var EventManager = require('../lib/events') var EventManager = require('../lib/events')
var CompilerImport = require('../app/compiler/compiler-imports') var CompilerImport = require('../app/compiler/compiler-imports')

@ -1,4 +1,4 @@
var remixLib = require('remix-lib') var remixLib = require('@remix-project/remix-lib')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
module.exports = EventManager module.exports = EventManager

@ -84,4 +84,3 @@ function find (args, query) {
}) })
return isMatch return isMatch
} }

@ -1,5 +1,5 @@
'use strict' 'use strict'
var SourceMappingDecoder = require('remix-lib').SourceMappingDecoder var SourceMappingDecoder = require('@remix-project/remix-lib').SourceMappingDecoder
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../package.json' import * as packageJson from '../../package.json'
@ -19,11 +19,17 @@ export class OffsetToLineColumnConverter extends Plugin {
offsetToLineColumn (rawLocation, file, sources, asts) { offsetToLineColumn (rawLocation, file, sources, asts) {
if (!this.lineBreakPositionsByContent[file]) { if (!this.lineBreakPositionsByContent[file]) {
for (var filename in asts) { const sourcesArray = Object.keys(sources)
var source = asts[filename] if (!asts && file === 0 && sourcesArray.length === 1) {
if (source.id === file) { // if we don't have ast, we process the only one available content
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content) this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[sourcesArray[0]].content)
break } else {
for (var filename in asts) {
const source = asts[filename]
if (source.id === file) {
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content)
break
}
} }
} }
} }

@ -1,5 +1,5 @@
import { Storage } from 'remix-lib' import { Storage } from '@remix-project/remix-lib'
/* /*
Migrating the files to the BrowserFS storage instead or raw localstorage Migrating the files to the BrowserFS storage instead or raw localstorage

@ -2,7 +2,8 @@ const EventEmitter = require('events')
class ClickLaunchIcon extends EventEmitter { class ClickLaunchIcon extends EventEmitter {
command (icon) { command (icon) {
this.api.waitForElementVisible('#icon-panel div[plugin="' + icon + '"]').click('#icon-panel div[plugin="' + icon + '"]').perform(() => { this.api.waitForElementVisible('#icon-panel div[plugin="' + icon + '"]').click('#icon-panel div[plugin="' + icon + '"]').perform((done) => {
done()
this.emit('complete') this.emit('complete')
}) })
return this return this

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save