GithubHelp home page GithubHelp logo

prettier-solidity / prettier-plugin-solidity Goto Github PK

View Code? Open in Web Editor NEW
719.0 12.0 73.0 3.49 MB

A Prettier plugin for automatically formatting your Solidity code.

Home Page: https://t.me/+kgTgkFgIwJkwMjcx

License: MIT License

JavaScript 46.56% Solidity 53.44%
ethereum solidity prettier-plugin prettier

prettier-plugin-solidity's Introduction

prettier-plugin-solidity

Telegram Twitter Follow GitPOAP Badge

A Prettier plugin for automatically formatting your Solidity code.

Installation and usage

Using in NodeJS

Install both prettier and prettier-plugin-solidity:

npm install --save-dev prettier prettier-plugin-solidity

Run prettier in your contracts:

npx prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol'

You can add a script for running prettier on all your contracts:

"prettier": "prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol'"

Or you can use it as part of your linting to check that all your code is prettified:

"lint": "prettier --list-different --plugin=prettier-plugin-solidity 'contracts/**/*.sol'"

Prettier Solidity only works with valid code. If there is a syntax error, nothing will be done and a parser error will be thrown.

Using in the Browser

Added in v1.1.0

To use this package in the browser, you need to load Prettier's standalone bundle before loading the build provided in this package.

<script src="https://unpkg.com/prettier@latest"></script>
<script src="https://unpkg.com/prettier-plugin-solidity@latest"></script>

Prettier's unpkg field points to https://unpkg.com/prettier/standalone.js, in a similar way this plugin points to https://unpkg.com/prettier-plugin-solidity/dist/standalone.js.

Once the scripts are loaded you will have access the globals prettier and prettierPlugins.

We follow Prettier's strategy for populating their plugins in the object prettierPlugins, you can load other plugins like https://unpkg.com/[email protected]/parser-markdown.js and Prettier will have access to multiple parsers.

<script>
  async function format(code) {
    return await prettier.format(code, {
      parser: 'solidity-parse',
      plugins: [solidityPlugin]
    });
  }

  const originalCode = 'contract Foo {}';
  const formattedCode = format(originalCode);
</script>

For more details and please have a look at Prettier's documentation.

Creating a package for the Browser

Added in v1.2.0

If you are creating your own package to be run in a browser, you might want to import the standalone files directly.

import prettier from 'prettier/standalone';
import solidityPlugin from 'prettier-plugin-solidity/standalone';

async function format(code) {
  return await prettier.format(code, {
    parser: "solidity-parse",
    plugins: [solidityPlugin],
  });
}

const originalCode = 'contract Foo {}';
const formattedCode = format(originalCode);

Configuration File

Prettier provides a flexible system to configure the formatting rules of a project. For more information please refer to the documentation. The following is the default configuration internally used by this plugin.

{
  "plugins": ["prettier-plugin-solidity"],
  "overrides": [
    {
      "files": "*.sol",
      "options": {
        "parser": "solidity-parse",
        "printWidth": 80,
        "tabWidth": 4,
        "useTabs": false,
        "singleQuote": false,
        "bracketSpacing": false,
      }
    }
  ]
}

Note the use of the overrides property which allows for multiple configurations in case there are other languages in the project (i.e. JavaScript, JSON, Markdown).

Since Prettier v3.0.0, the plugin search feature has been removed so we encourage adding our plugin to the configuration file.

Most options are described in Prettier's documentation.

Compiler

Many versions of the Solidity compiler have changes that affect how the code should be formatted. This plugin, by default, tries to format the code in the most compatible way that it's possible, but you can use the compiler option to nudge it in the right direction.

One example of this is import directives. Before 0.7.4, the compiler didn't accept multi-line import statements, so we always format them in a single line. But if you use the compiler option to indicate that you are using a version greater or equal than 0.7.4, the plugin will use multi-line imports when it makes sense.

The Solidity versions taken into consideration during formatting are:

  • v0.7.4: Versions prior 0.7.4 had a bug that would not interpret correctly imports unless they are formatted in a single line.

    // Input
    import { Foo as Bar } from "/an/extremely/long/location";
    
    // "compiler": undefined
    import { Foo as Bar } from "/an/extremely/long/location";
    
    // "compiler": "0.7.3" (or lesser)
    import { Foo as Bar } from "/an/extremely/long/location";
    
    // "compiler": "0.7.4" (or greater)
    import {
        Foo as Bar
    } from "/an/extremely/long/location";
  • v0.8.0: Introduced these changes

    • The type byte has been removed. It was an alias of bytes1.
    • Exponentiation is right associative, i.e., the expression a**b**c is parsed as a**(b**c). Before 0.8.0, it was parsed as (a**b)**c.
    // Input
    bytes1 public a;
    byte public b;
    
    uint public c = 1 ** 2 ** 3;
    
    // "compiler": undefined
    bytes1 public a;
    byte public b;
    
    uint public c = 1**2**3;
    
    // "compiler": "0.7.6" (or lesser)
    bytes1 public a;
    byte public b;
    
    uint public c = (1**2)**3;
    
    // "compiler": "0.8.0" (or greater)
    bytes1 public a;
    bytes1 public b;
    
    uint public c = 1**(2**3);

You might have a multi-version project, where different files are compiled with different compilers. If that's the case, you can use overrides to have a more granular configuration:

{
  "overrides": [
    {
      "files": "contracts/v1/**/*.sol",
      "options": {
        "compiler": "0.6.3"
      }
    },
    {
      "files": "contracts/v2/**/*.sol",
      "options": {
        "compiler": "0.8.4"
      }
    }
  ]
}
Default CLI Override API Override
None --compiler <string> compiler: "<string>"

Experimental Ternaries

Added in v1.3.0

Mimicking prettier's new ternary formatting for the community to try.

Valid options:

  • true - Use curious ternaries, with the question mark after the condition.
  • false - Retain the default behavior of ternaries; keep question marks on the same line as the consequent.
Default CLI Override API Override
false --experimental-ternaries experimentalTernaries: <bool>

Integrations

Vim

To integrate this plugin with vim, first install vim-prettier. These instructions assume you are using vim-plug. Add this to your configuration:

Plug 'prettier/vim-prettier', {
  \ 'do': 'yarn install && yarn add prettier-plugin-solidity',
  \ 'for': [
    \ 'javascript',
    \ 'typescript',
    \ 'css',
    \ 'less',
    \ 'scss',
    \ 'json',
    \ 'graphql',
    \ 'markdown',
    \ 'vue',
    \ 'lua',
    \ 'php',
    \ 'python',
    \ 'ruby',
    \ 'html',
    \ 'swift',
    \ 'solidity'] }

We modified the do instruction to also install this plugin. Then you'll have to configure the plugin to always use the version installed in the vim plugin's directory. The vim-plug directory depends on value you use in call plug#begin('~/.vim/<dir>'):

let g:prettier#exec_cmd_path = '~/.vim/plugged/vim-prettier/node_modules/.bin/prettier'

To check that everything is working, open a Solidity file and run :Prettier.

If you also want to autoformat every time you write the buffer, add these lines:

let g:prettier#autoformat = 0
autocmd BufWritePre *.sol Prettier

Now Prettier will be run every time the file is saved.

VSCode

VSCode is not familiar with the Solidity language. There are 2 extensions that you can install to provide support for Solidity:

code --install-extension JuanBlanco.solidity
# or
code --install-extension NomicFoundation.hardhat-solidity

โš ๏ธ These 2 extensions offer similar functionality and will clash with each other: Please choose which one matches your projects better.

These extensions provide basic integration with Prettier; in most cases, no further action is needed.

Make sure your editor has format on save set to true. When you save VSCode will ask you what formatter would you like to use for the Solidity language, you can choose JuanBlanco.solidity or NomicFoundation.hardhat-solidity.

At this point VSCode's settings.json should have a configuration similar to this:

{
  "editor.formatOnSave": true,
  "solidity.formatter": "prettier", // This is the default so it might be missing.
  "[solidity]": {
    // "editor.defaultFormatter": "JuanBlanco.solidity"
    // "editor.defaultFormatter": "NomicFoundation.hardhat-solidity"
  }
}

If you want more control over other details, you should proceed to install prettier-vscode.

code --install-extension esbenp.prettier-vscode

To interact with 3rd party plugins, prettier-vscode will look in the project's npm modules, so you'll need to have prettier and prettier-plugin-solidity in your package.json

npm install --save-dev prettier prettier-plugin-solidity

This will allow you to specify the version of the plugin in case you want to use the latest version of the plugin or need to freeze the formatting since new versions of this plugin will implement tweaks on the possible formats.

You'll have to let VSCode what formatter you prefer. This can be done by opening the command palette and executing:

>Preferences: Configure Language Specific Settings...

# Select Language
solidity

Now VSCode's settings.json should have this:

{
  "editor.formatOnSave": true,
  "[solidity]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

Note: By design, Prettier prioritizes a local over a global configuration. If you have a .prettierrc file in your project, your VSCode's default settings or rules in settings.json are ignored (prettier/prettier-vscode#1079).

Pnpm

To make Prettier Solidity work in your project, you have to add a .prettierrc file as shown here.

Then, if you are using VSCode, you also need to add this to your VSCode settings:

{
  "prettier.documentSelectors": ["**/*.sol"]
}

Edge cases

Prettier Solidity does its best to be pretty and consistent, but in some cases it falls back to doing things that are less than ideal.

Modifiers in constructors

Modifiers with no arguments are formatted with their parentheses removed, except for constructors. The reason for this is that Prettier Solidity cannot always tell apart a modifier from a base constructor. So modifiers in constructors are not modified. For example, this:

contract Foo is Bar {
  constructor() Bar() modifier1 modifier2() modifier3(42) {}

  function f() modifier1 modifier2() modifier3(42) {}
}

will be formatted as

contract Foo is Bar {
  constructor() Bar() modifier1 modifier2() modifier3(42) {}

  function f() modifier1 modifier2 modifier3(42) {}
}

Notice that the unnecessary parentheses in modifier2 were removed in the function but not in the constructor.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/fooBar)
  3. Commit your changes (git commit -am 'Add some fooBar')
  4. All existing tests and coverage must pass (npm run test:all), if coverage drops below 100% add missing tests.
  5. Push to the branch (git push origin feature/fooBar)
  6. Create a new Pull Request

Who's using it?

These are some of the projects using Prettier Solidity:

License

Distributed under the MIT license. See LICENSE for more information.

prettier-plugin-solidity's People

Contributors

benignmop avatar dependabot-preview[bot] avatar dependabot[bot] avatar dependencies-bot avatar dependencies[bot] avatar fvictorio avatar iainnash avatar jablko avatar janther avatar mattiaerre avatar maxsam4 avatar minggas avatar omahs avatar passabilities avatar paulrberg avatar ritikm avatar svechinsky avatar timdaub avatar yhuard avatar yudrywet avatar zemse avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

prettier-plugin-solidity's Issues

the more the merrier

I'll be moving this repo to the prettier-plugin organization that I've just created in GitHub as @federicobond suggested. he wrote the parser used in this plugin as well as started working on the print function he shares in this Gist. I'll send an invite also to @j-f1 as he is tremendously helping me to properly shape this prettier plugin.

Preserve comments

Comments are being dropped out right now. They should at least be preserved; properly formatting them could be another issue (which will probably need some definition: for example, should long comment lines be split? What does prettier do in js?).

ParserError: extraneous input 'default' expecting {'from', 'constant', 'internal', 'private', 'public', Identifier}

Description

How to reproduce

Run the plugin 2 times against this contract

Stack trace

[error] contracts/05_greeter.sol: ParserError: extraneous input 'default' expecting {'from', 'constant', 'internal', 'private', 'public', Identifier}
[error]     at Object.parse (/Users/mrichetto/projects/prettier-plugin-solidity/node_modules/solidity-parser-antlr/dist/index.js:74:11)
[error]     at Object.parse (/Users/mrichetto/projects/prettier-plugin-solidity/src/parser.js:4:30)
[error]     at Object.parse$2 [as parse] (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:7140:19)
[error]     at coreFormat (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:10400:23)
[error]     at format (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:10572:16)
[error]     at formatWithCursor (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:10584:12)
[error]     at Object.formatWithCursor (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:34926:15)
[error]     at format$1 (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:36846:21)
[error]     at /Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:37035:16
[error]     at /Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:36981:14

Add space after `mapping`

Similar to #87, there some examples of well-known solidity code that adds a space after the mapping keyword:

mapping (address => uint256)

But, unlike #87, here I've seen the two variations being used. And, speaking just for myself, I have a strong preference for the non-space form:

mapping(address => uint256)

Some numbers:

There doesn't seem to be a strong preference for one or another, so it's probably up to us.

.eslintrc

Description

  • add implicit-arrow-linebreak

Base contracts don't respect print width

As discussed in 0xProject/0x-monorepo#1495, if a contract has a lot of base contracts, these should be split to respect the print width.

contract Foo is AContract, AnotherContract, YetAnotherContract, ThisShouldBeSplit {
  function foo(uint x, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8) {
    a = 1;
  }
}

This should split the base contracts, but just the function parameters are split.

Comments before else are not formatted correctly

contract test {
  function fun(uint256 a) returns (uint) {
    if (something) foo();
    // comment
    else if (somethingElse) bar();
    else whatever();
  }
}

gets formatted as

contract test {
  function fun(uint256 a) returns(uint) {
    if (something) foo();
    else // comment
    if (somethingElse) bar();
    else whatever();
  }
}

This also happens if the first if body has braces.

Originally posted by @fvictorio in #68

Migrate to TypeScript

There typescript typeings for solidity-parser-antlr and prettier.

I'm currently improving the AST typings to the point where most of the grammar rules are checked at compile time. With this you can write code transforms that don't corrupt the AST.

It also helps to track changes in the grammar over time. As the Solidity syntax evolves, the new typings will show where the printer and transforms need to be changed.

Finally it allows us to use more modern features of JS like lambda functions safely. Functional programming features are very useful for language processing.

Comments in assembly function calls are being merged

This

contract Foo {
  function f() {
    g(
      1, // one
      2, // two
      3 // three
    );

    assembly {
      g(
        1, // one
        2, // two
        3 // three
      )
    }
  }
}

is being printed as:

contract Foo {
  function f() {
    g(
      1, // one
      2, // two
      3 // three
    );

    assembly {
      g(1, 2, 3) // one // two // three
    }
  }
}

Notice that it seems to work fine for normal function calls; the problem seems to be inside assembly blocks.

beta version

Description

I'm adding a todo list of things we should be doing prior the 1st publishing on npm of this plugin; feel free to comment on this issue if I've missed something

Todos

Dependencies.io troubleshooting

We're opening this issue to inform you about problems with your dependencies.io configuration or build failures.

We'll add a comment here when we see problems. You can close this issue when
you have things resolved -- we'll open it back up if we detect new problems.

Helpful links:

Note: To help keep your repo as clean as possible, this is the only issue
that we're going to open and comment on. Feel free to delete the comments
that are made as you resolve them, if you want to keep this issue short and tidy.

Don't add empty lines between documenting comments and functions

#40 makes the plugin preserve comments, but creates a new problem. If you have some documentation above a function:

/*
 * This function does stuff.
 */
function doStuff() public {
  ...
}

The prettified version will be:

/*
 * This function does stuff.
 */

function doStuff() public {
  ...
}

The cause of this is that we prepend function definitions with empty lines. Possible solutions:

  1. Improve the printer function to separate functions with empty lines in a more intelligent way (for example, append an empty line to all function definitions except the last).
  2. Remove that feature completely. Prettier does not separate functions in javascript classes โ€”maybe we shouldn't either, and this should be a responsibility of a linter.

Error: Cannot find module 'emoji-regex'

If I want to run prettier in my library, I get the following error:

$ yarn format
yarn run v1.12.3
$ prettier --write src/**/*.ts
internal/modules/cjs/loader.js:583
    throw err;
    ^

Error: Cannot find module 'emoji-regex'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
    at Function.Module._load (internal/modules/cjs/loader.js:507:25)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:20:18)
    at Object.<anonymous> (/Users/schmidsi/Development/@melonproject/protocol/node_modules/prettier-plugin-solidity/src/prettier-comments/common/util.js:4:20)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
error Command failed with exit code 1.

It suppose that the error comes from this line of code:

const emojiRegex = require("emoji-regex")();

A possible fix was to install 'emoji-regex' in the consuming project, but that is a bad work around. You could probably remove this dependency completely?

solidity 0.5: breaking change

as per @maxsam4 comment on Gitter:

The JSON AST field isConstructor of the FunctionDefinition node was replaced by a field called kind which can have the value "constructor", "fallback" or "function". this is also a breaking change.

Errors in for statements

A for statement like this:

for (uint256 i = 0; i < 10; i++) {}

is formatted as:

  for (uint256 i = 0;i < 10; i++;) {

  }}

Things to notice:

  1. The closing brace is duplicated and an extra semicolon is added after the loop condition. Both of these things cause syntax errors.
  2. There is no space after the first semicolon.

I think this is a blocker (hopefully the last one) for publishing an alpha version.

Issue with payable address array

address payable[] is getting prettified to address[]
For example,

pragma solidity ^0.5.0;

contract test {
    address payable[] yo;
    function hello(address payable[] memory _yo) public {
        yo = _yo;
        yo[0].transfer(1337);
    }
}

Got prettified to

pragma solidity ^0.5.0;

contract test {
    address[] yo;
    function hello(address[] memory _yo) public {
        yo = _yo;
        yo[0].transfer(1337);
    }
}

can't have standalone comments in constructor

prettier does not like when I have a standalone comment in the body of a constructor (or any other function):

 constructor() public setMsgFields {
    // This constructor will be used to test the creation via multi-sig wallet
  }
[error] @noridoteco/contracts/contracts/wallet/TestCalls.sol: Error: Comment "This constructor will be used to test the creation via multi-sig wallet" was not printed. Please report this error!
[error]     at /home/ubuntu/dev/nori/node_modules/prettier/bin-prettier.js:10371:13
[error]     at Array.forEach (<anonymous>)
[error]     at ensureAllCommentsPrinted (/home/ubuntu/dev/nori/node_modules/prettier/bin-prettier.js:10369:15)
[error]     at coreFormat (/home/ubuntu/dev/nori/node_modules/prettier/bin-prettier.js:10417:3)
[error]     at format (/home/ubuntu/dev/nori/node_modules/prettier/bin-prettier.js:10572:16)
[error]     at formatWithCursor (/home/ubuntu/dev/nori/node_modules/prettier/bin-prettier.js:10584:12)
[error]     at Object.formatWithCursor (/home/ubuntu/dev/nori/node_modules/prettier/bin-prettier.js:34926:15)
[error]     at format$1 (/home/ubuntu/dev/nori/node_modules/prettier/bin-prettier.js:36846:21)
[error]     at /home/ubuntu/dev/nori/node_modules/prettier/bin-prettier.js:37035:16
[error]     at /home/ubuntu/dev/nori/node_modules/prettier/bin-prettier.js:36981:14

Source here

Misaligned returns when parameters are split

This

contract Foo {
  function foo() modifier1 modifier2 aLongModifier anotherVeryLongModifier returns (uint aVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongParameterName) {
    a = 42;
  }
}

is prettified as:

contract Foo {
  function foo()
    modifier1
    modifier2
    aLongModifier
    anotherVeryLongModifier
    returns (
    uint aVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongParameterName
  )
  {
    a = 42;
  }
}

Two things are wrong here: the indentation of the returns parameter and closing parentheses, and that the closing parentheses and the opening body brace are not in the same line.

Group modifiers when line is too long

We should format function definitions like this:

contract Foo {
  function f()
    modifier1
    modifier2
    modifier3
    modifier4
    returns (bool)
  {
    return true;
  }

  function h() modifier1 returns (bool) {
    return true;
  }
}

This may be somewhat inconsistent, but I think it's more readable. If the function definition has a lot of parameters and also a lot of modifiers, maybe it should be formatted like this:

contract Foo {
  function f(
    uint a,
    uint b,
    uint c
  ) modifier1
    modifier2
    modifier3
    modifier4
    returns (bool)
  {
    return true;
  }
}

This won't be written on stone, of course, we can change it later. But it's much better that what we have now, with all the modifiers always in the same line no matter how long it is.

Unknown type: "AssemblyLocalDefinition" and "AssemblyAssignment"

Source file: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol#L48

Line 48 has the assembly call that freaks out the parser. The same thing happens with my Proxy contracts.

[error] contracts/external/strings.sol: Error: Unknown type: "AssemblyLocalDefinition"
[error]     at Object.genericPrint [as print] (/Users/roderik/Development/solidity-mint/node_modules/prettier-plugin-solidity/src/printer.js:458:13)
[error]     at genericPrint (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10130:18)
[error]     at /Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10076:16
[error]     at Object.printComments (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:9838:17)
[error]     at printGenerically (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10075:22)
[error]     at FastPath.map (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10007:21)
[error]     at Object.genericPrint [as print] (/Users/roderik/Development/solidity-mint/node_modules/prettier-plugin-solidity/src/printer.js:427:25)
[error]     at genericPrint (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10130:18)
[error]     at /Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10076:16
[error]     at Object.printComments (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:9838:17)

Add a newline at the end of the contract

Right now, linting a contract:

contract Box {
  uint public value = 0;
  function set(uint _value) public {
    value = _value;
  }
}

produces this output:

contract Box {
  uint public value = 0;
  function set(uint _value) public {
    value = _value;
  }
}%

where % represents the end-of-file. I think contracts should have a newline at the end.

Should prettier be a dependency?

As of now, we have prettier inside dependencies. I thought it would make more sense to have it as a peer dependency. But I checked the official plugins and two of them have it as a dependency and other as a peer dependency (python, php, swift).

@j-f1, could you give us some advice here?

How to install this plugin

Is this library publish to npm?
Do I need directly install from git repository, like this?

npm install git+https://github.com/mattiaerre/prettier-plugin-solidity.git

Unknown type: "FunctionTypeName"

I cannot share this contract atm but I assume it is this line:

pragma experimental ABIEncoderV2;
[error] contracts/provenance/statemachine/StateMachine.sol: Error: Unknown type: "FunctionTypeName"
[error]     at Object.genericPrint [as print] (/Users/roderik/Development/solidity-mint/node_modules/prettier-plugin-solidity/src/printer.js:458:13)
[error]     at genericPrint (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10130:18)
[error]     at /Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10076:16
[error]     at Object.printComments (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:9838:17)
[error]     at printGenerically (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10075:22)
[error]     at FastPath.call (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:9949:16)
[error]     at Object.genericPrint [as print] (/Users/roderik/Development/solidity-mint/node_modules/prettier-plugin-solidity/src/printer.js:300:14)
[error]     at genericPrint (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10130:18)
[error]     at /Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:10076:16
[error]     at Object.printComments (/Users/roderik/Development/solidity-mint/node_modules/prettier/bin-prettier.js:9838:17)

Add space after `returns`

I've been testing the plugin with some well-known projects, mainly 0x and OpenZeppelin. In both I noticed that they add a space after the returns keyword, while we just put the parentheses without any space. I mean, we do this:

returns(uint)

and they do this:

returns (uint)

I don't really have a preference, but since they do it this way, maybe we should adhere to that.

@mattiaerre Since this is an easy change, I'll just go ahead and create a PR for this, but this is of course open for discussion.

Ugly formatting of ternary operator

This

contract Foo {
  function foo() {
    address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_;
  }
}

is prettified as

contract Foo {
  function foo() {
    address contextAddress = currentContextAddress_ == address(
      0
    ) ? msg.sender : currentContextAddress_;
  }
}

I'm not sure what the expected output should be here, though. Suggestions are welcome (cc @abandeali1, @mattiaerre).

divide genericPrint into components

Description

as per now the lenght of the printer.js file is almost 500 lines of code; I'd love to refactor the genericPrint function so that we can incrementally add and replace inner "printers" w/o modifying too much the very same file; an option that I have in mind is the following:

    // omissis
    default: {
      try {
        return [require('./node-types/for-statement')]
          .find(element => element.name === node.type)
          .print(node, path, print);
      } catch (error) {
        throw new Error(`Unknown type: ${JSON.stringify(node.type)}`);
      }
    }
    // omissis

where every module inside the node-type folder, exports an object w/ a name property and a print method; e.g.:

/* eslint-disable implicit-arrow-linebreak */
const {
  doc: {
    builders: { concat }
  }
} = require('prettier');

const ForStatement = {
  name: 'ForStatement',
  print: (node, path, print) =>
    concat([
      'for (',
      node.initExpression ? path.call(print, 'initExpression') : '',
      '; ',
      node.conditionExpression ? path.call(print, 'conditionExpression') : '',
      '; ',
      path.call(print, 'loopExpression'),
      ') ',
      path.call(print, 'body')
    ])
};

module.exports = ForStatement;

what do you think?

Don't split function call without arguments

Running prettier on this contract with a printWidth of 56:

contract Foo {
  function foo() {
    address validatorAddress = signature.popLast20Bytes();
  }
}

outputs this:

contract Foo {
  function foo() {
    address validatorAddress = signature.popLast20Bytes(

    );
  }
}

I think this shouldn't break like this. In this particular case, I think a proper output might be:

contract Foo {
  function foo() {
    address validatorAddress =
      signature.popLast20Bytes();
  }
}

or

contract Foo {
  function foo() {
    address validatorAddress = signature
      .popLast20Bytes();
  }
}

(I prefer the first one and I think it's what prettier-js does.)

Empty contracts and functions

Maybe braces should be in the same line if a contract/function is empty. Right now, this:

contract Foo {

}

is not modified.

For comparison, in javascript this:

class Foo {
}

is prettified as:

class Foo {}

Something similar happens with empty functions.

Consume solium rules

It would be fantastic if the plugin would consume the rules defined in the linter ruleset, such as

{
  "extends": "solium:recommended",
  "plugins": ["security"],
  "rules": {
    "quotes": ["error", "double"],
    "indentation": ["warning", 2],
    "arg-overflow": ["warning", 3],
    "security/enforce-explicit-visibility": ["error"],
    "security/no-block-members": ["warning"],
    "security/no-inline-assembly": ["warning"]
  }
}

So that when printing, if it sees arg-overflow: 3, then it prints only 3 args per line

TypeError: Cannot read property 'type' of undefined

Description

How to reproduce

Run the plugin against the following contract

/*
	This is a very simple demonstration of a while loops. Same as JS/c.
*/

contract BasicIterator {

    address creator;                  // reserve one "address"-type spot
    uint8[10] integers;               // reserve a chunk of storage for 10 8-bit unsigned integers in an array

    function BasicIterator() 
    {
        creator = msg.sender;         // set the creator address
        uint8 x = 0;                  // initialize an 8-bit, unsigned integer to zero
        while(x < integers.length)    // the variable integers was initialized to length 10
        {
        	integers[x] = x;      // set integers to [0,1,2,3,4,5,6,7,8,9] over ten iterations
        	x++;
        }
    }
    
    function getSum() constant returns (uint)  // "constant" just means this function returns something to the caller
    {                                          // which is immediately followed by what type gets returned, in this case a full uint256
    	uint8 sum = 0;
    	uint8 x = 0;
    	while(x < integers.length)
        {
        	sum = sum + integers[x];
        	x++;
        }
    	return sum;
    }
    
    /**********
     Standard kill() function to recover funds 
     **********/
    
    function kill()
    { 
        if (msg.sender == creator)
        {
            suicide(creator);  // kills this contract and sends remaining funds back to creator
        }
    }
}

contract's source

Stack trace

[error] contracts/08_basiciterator.sol: TypeError: Cannot read property 'type' of undefined
[error]     at propagateBreaksOnEnterFn (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:9195:13)
[error]     at traverseDoc (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:9053:11)
[error]     at Object.propagateBreaks (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:9220:3)
[error]     at printAstToDoc (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:10095:14)
[error]     at coreFormat (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:10414:16)
[error]     at format (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:10572:16)
[error]     at formatWithCursor (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:10584:12)
[error]     at Object.formatWithCursor (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:34926:15)
[error]     at format$1 (/Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:36846:21)
[error]     at /Users/mrichetto/projects/solidity-baby-steps/node_modules/prettier/bin-prettier.js:37035:16

issue w/ for statement

Description

missing a semicolon if for statement body is not included in braces.

Source

  function count(string[] data) public pure returns (uint) {
    uint result;
    for(uint i = 0; i < data.length; i++)
      result += 1;
    return result;
  }

Prettified

  function count(string[] data) public pure returns(uint) {
    uint result;
    for (uint i = 0; i < data.length; i++) result += 1
    return result;
  }

N.B. there is a missing ; at the end of the for body

Adding solidity's default visibility to variables and functions.

One of the things that I like about Prettier is the fact that is opinionated and makes some desitions around the formatting using standard industry practices.

I want to open the discussion on whether this formatter should take care of the visibility of variables and functions when the developer forgets to add them explicitly.

The compiled bytecode of the resulting code would not be altered and the resulting code will be more descriptive.

If statement in one line is not being prettified

A contract like this:

contract test {
  function fun(uint256 a) returns (address b) {
    if (a < 0) b = 0x67; else if (a == 0) b = 0x12; else b = 0x78;
  }
}

is prettified like this:

contract test {
  function fun(uint256 a) returns(address b) {
    if (a < 0) b = 0x67; else if (a == 0) b = 0x12; else b = 0x78;
  }
}

Almost no changes.

I'm not sure what is the best output here. Should we add the braces or is that too opinionated?

Separate functions with a blank line

Formatting this:

contract Box {
    uint public value = 0;

    function set(uint _value) public {
        value = _value;
    }

    function inc() public {
      value++;
    }
}

outputs this:

contract Box {
  uint public value = 0;
  function set(uint _value) public {
    value = _value;
  }
  function inc() public {
    value++;
  }
}

I think functions should be separated by a blank line. An output like this:

contract Box {
  uint public value = 0;
  function set(uint _value) public {
    value = _value;
  }

  function inc() public {
    value++;
  }
}

should be enough IMO. Separating the field declarations from the functions would probably be another issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.