Giter VIP home page Giter VIP logo

lua_code_formatter's Introduction

Description

Formats any valid Lua 5.3 code.

Lines with code are wrapped to fit inside given margins.

Files with invalid Lua syntax may lose the content after the syntax error! If this is a problem - verify the correctness of the file before running reformatter. For example via $ luac -p <lua_file>.

Installation deploys three command-line scripts:

  • lua.reformat
  • lua.get_ast
  • lua.get_formatter_ast

Last two for people who love tinkering.


Requirements

  • LuaRocks. For easy installation.

  • Lua interpreter. Version 5.3 is preferred. Another options described in a following table.

    Lua version required Syntax supported Branch Notes
    5.3 5.3 master May contain experimental features.
    5.3 5.3 5.3
    5.1 5.3 5.1
    5.1 5.1 5.1-syntax_5.1
  • OS: Linux. Possibly it can work on other OSs but I've not tested it there.

Installation

Luarocks repo Git repo, luarocks
sudo luarocks install lcf git clone https://github.com/martin-eden/lua_code_formatter
cd ./lua_code_formatter
sudo luarocks make lcf-scm-1.rockspec

Deinstallation

Luarocks repo Git repo, luarocks
sudo luarocks remove lcf sudo luarocks remove lcf
rm lua_code_formatter

Usage

From command-line

lua.reformat [<lua_file>]

From Lua interpreter

Suppose you have a string with Lua code and wish to get another string with formatted code.

do
  local lua_code_str = 'do return end' -- < fill it

  require('lcf.workshop.base')
  local get_ast = request('!.lua.code.get_ast')
  local get_formatted_code = request('!.lua.code.ast_as_code')

  return get_formatted_code(get_ast(lua_code_str))
end
Passing formatting parameters

You may override default parameters by passing a table with new values of changed parameters:

get_formatted_code(
  get_ast(lua_code_str),
  {
    indent_chunk = '  ',
    right_margin = 96,
    max_text_width = math.huge,
    keep_unparsed_tail = true,
    keep_comments = true,
  }
)
Parameter Default Description Notes
indent_chunk String used for building one indent. You may try value |.. to see it's effect.
right_margin 96 Maximum line length with indent. Setting it makes sense for printing.
max_text_width +inf Maximum line length without indent. Setting it makes sense for viewing in editor.
keep_unparsed_tail true Keep text after point where we failed to parse source. Syntactically incorrect code may still lose parts even with this flag. For example f() = a is formatted as f(). (It's parsed as assignment but formatted as function call.)
keep_comments true Keep comments. Comment text is not changed so comments may last beyond right margin.
Comments handling

Comments are raised to statements level.

So text

function(a, --parameter "a"
  b) --parameter "b"
end

is formatted as

--parameter "a"
function(a, b)
  --parameter "b"
end

This is done to keep formatting routines simple.


Changing formatting logic

First, general workflow of this formatter:

  1. Load .lua file as string.
  2. Parse that string to tree in table.
  3. Change nodes of that tree to make it easier to handle in formatting routines.
  4. Run formatting routines for all nodes in tree. Formatting routines output to virtual printer.
  5. Write contents of virtual printer to file.

We are at point 4. Start tinkering from something simple but usable as "repeat" block handling: workshop/formats/lua/formatter/handlers/statements/blocks/repeat_block.lua


Contributors

  • Several people were asking to keep comments. This was done and --keep-comments option was added.
  • Peter Melnichenko โ™ฐ shown me how to write cross-platform .rockspec file.
  • Oliver Jan Krylow added cautions in readme.md that file with invalid syntax loses tail. I've mentioned $ luac -p workaround and added --keep-unparsed-tail option to detect and prevent this.
  • keneanung adoped formatter to Lua 5.1 syntax and pushed branch 5.1-syntax_5.1. Main audience of this is LuaJIT users.

Further development

I feel this project is done. Original goal to reformat ugly code from World of Warcraft addons accomplished.

  • I'm planning to keep it compatible with current PuC-Lua version.
  • Maybe I'll add more documentation about inner mechanics.
  • Maybe I'll add more advanced formatting like empty lines before long for blocks.

See also


2016-08-16
2017-01-28
2017-09-26
2018-02-23

lua_code_formatter's People

Contributors

bugabinga avatar martin-eden avatar mpeterv 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

Watchers

 avatar  avatar  avatar  avatar  avatar

lua_code_formatter's Issues

5.1 branch does not load correctly anymore

I'm getting the following:

$ git clone https://github.com/martin-eden/lua_code_formatter.git
Cloning into 'lua_code_formatter'...
remote: Counting objects: 1393, done.
remote: Compressing objects: 100% (116/116), done.
remote: Total 1393 (delta 15), reused 92 (delta 11), pack-reused 1258
Receiving objects: 100% (1393/1393), 253.32 KiB | 778.00 KiB/s, done.
Resolving deltas: 100% (476/476), done.
vadi@volga:~/Programs$ cd lua_code_formatter/
vadi@volga:~/Programs/lua_code_formatter$ git checkout 5.1
Branch 5.1 set up to track remote branch 5.1 from origin.
Switched to a new branch '5.1'
vadi@volga:~/Programs/lua_code_formatter$ sudo luarocks make lcf-scm-1.rockspec
[sudo] password for vadi: 
lcf scm-1 is now installed in /usr/local (license: GPL v3)

Missing dependencies for formatter 0.1.1-1:
   penlight ~> 0.9 (1.5.4-1 installed)
vadi@volga:~/Programs/lua_code_formatter$ lua
Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
> do
>>   local lua_code_str = 'do return end' -- < fill it
>> 
>>   require('lcf.workshop.base')
>>   local get_ast = request('!.lua.code.get_ast')
>>   local get_formatted_code = request('!.formats.lua.save')
>> 
>>   return get_formatted_code(get_ast(lua_code_str))
>> end
/usr/local/share/lua/5.1/lcf/workshop/base.lua:124: module 'lcf.workshop.formats.lua.save' not found:
	no field package.preload['lcf.workshop.formats.lua.save']
	no file './lcf/workshop/formats/lua/save.lua'
	no file '/usr/local/share/lua/5.1/lcf/workshop/formats/lua/save.lua'
	no file '/usr/local/share/lua/5.1/lcf/workshop/formats/lua/save/init.lua'
	no file '/usr/local/lib/lua/5.1/lcf/workshop/formats/lua/save.lua'
	no file '/usr/local/lib/lua/5.1/lcf/workshop/formats/lua/save/init.lua'
	no file '/usr/share/lua/5.1/lcf/workshop/formats/lua/save.lua'
	no file '/usr/share/lua/5.1/lcf/workshop/formats/lua/save/init.lua'
	no file './lcf/workshop/formats/lua/save.so'
	no file '/usr/local/lib/lua/5.1/lcf/workshop/formats/lua/save.so'
	no file '/usr/lib/x86_64-linux-gnu/lua/5.1/lcf/workshop/formats/lua/save.so'
	no file '/usr/lib/lua/5.1/lcf/workshop/formats/lua/save.so'
	no file '/usr/local/lib/lua/5.1/loadall.so'
	no file './lcf.so'
	no file '/usr/local/lib/lua/5.1/lcf.so'
	no file '/usr/lib/x86_64-linux-gnu/lua/5.1/lcf.so'
	no file '/usr/lib/lua/5.1/lcf.so'
	no file '/usr/local/lib/lua/5.1/loadall.so'
stack traceback:
	[C]: in function 'require'
	/usr/local/share/lua/5.1/lcf/workshop/base.lua:124: in function 'request'
	stdin:6: in main chunk
	[C]: ?
> 

Customization options

(It's open-ended question, currently I have no stable vision. You are free to share your thoughts in this topic.)

People wish from code formatter to have a bit different formatting style. Say,

  • Why not format tables with keeping opening brace { at initial line?
  • Why not add option to convert string quotes from double quotes "a" to apostrophes 'a' (as was in initial formatter version)?
  • Why not add/remove parenthesis at function call: f('a') / f'a'?

Problem is that there are thousands of features that can be easily implemented but for managing it will require at least thousand-lines configuration file. (And each feature needs to be described in text form (which is usually too boring to read).)

Also I'm not going to implement each possible feature by myself. I'd prefer to build good framework to implement them by others.

I'd wish to have some GUI program that displays list of available features as groups of checkboxes and radio buttons. After selection it produces text file in some simple format. Which is later interpreted by code formatter.

So I'd like to see

  • list of required formatting style features
  • thoughts/intentions on GUI style configuration tool

Rundundent files left behind by modifications to support Lua 5.1

It seems that #15 left behind two unneeded files in the 5.1-syntax_5.1 branch that we use (now, we had forgotten to switch over when that and #16 were done!) in Mudlet:

  • workshop/formats/lua/formatter/handlers/statements/goto_statement.lua
  • workshop/formats/lua/formatter/handlers/statements/label_statement.lua

these are not relevant for 5.1 as, of course that version does not support the goto functionality regarded as leading to ๐Ÿ code in other coding languages that was adopted in a later Lua version!

Invalid code is corrupted

Is intended behaviour that if Lua-invalid code is passed to the formatter, it is corrupted? It would make sense if it just wasn't indented at all and returned as-is.

For example:

local size = matches[2]
if size == "biggest" then
  mapper = Geyser.Mapper:new({name = "mapper", x = 0, y = 0, width = "100%", height = "97%"})
  mmp.echo("Map size set to pretty damn big.")
elseif size == "small" then
  mapper = Geyser.Mapper:new({x = "70%", y = 0, width = "28%", height = "50%", 
	aass = 223,
	3431434=3434})
  mmp.echo("Map size set to comfortable.")
elseif size == "big" then
  local window_width, window_height = getMainWindowSize()
  local used_width = getMainConsoleWidth()
  local available_space = window_width - used_width
  if matches[3] then
    available_space = available_space - tonumber(matches[3])
  end
  mapper =
    Geyser.Mapper:new({x = available_space * -1, y = 0, width = available_space, height = "100%"})
  mmp.echo(
    "Set the map size to big - it'll cover all of the space on the right that game text isn't using. You'll want to call this alias again if you resize the window to update."
  )
end

Tables with a number as a key in the shorthand syntax are invalid, so the whole if block gets deleted by the formatter.

Loss of data on invalid input

Hello!

Thanks for releasing this formatter. I use it in my editor as a pretty printer on save.

I realize, that the Readme states, that this is a formatter for valid Lua 5.3 code, but I have not found a statement what behavior is expected when receiving invalid code.

My expectation is that the formatter does nothing upon receiving garbage, but it seems to discard the invalid part and anything after that.

Small example

--[[
Demonstrates a bug in [LCF](https://github.com/martin-eden/lua_code_formatter)
where invalid table syntax causes it to discard everything after the table.
]]

require('lcf.workshop.base')
local request = request
local get_ast = request('!.lua.code.get_ast')
local get_formatted_code = request('!.formats.lua.save')


local test_lua_code=[[
local bad_table = {  "a"    =  1,  b   =   2,  c    =   3 }
local good_table = {1,2,3,4,5}]]
local formatted =
  get_formatted_code(
    get_ast(
      test_lua_code,
      {
        indent_chunk = '  ';
        right_margin = 80;
        max_text_width = 80;
        keep_comments = true;
      }
    )
  )

local expected = [[
local bad_table = {a = 1, b = 2, c = 3}
local good_table = {1, 2, 3, 4, 5}]]

print(formatted)
print(expected)

assert(formatted == expected)

Incorporate into Textadept?

I'd like to roll this awesome formatter into Textadept. Is there a way to get all of the functionality in one file? I've been looking through the code and I'm not sure where everything is yet.

Commas get changed to semicolons if max_text_width is small enough

Given the following input:

local picturelabel = Geyser.Label:new({
  name = "picturelabel",
  x = "50%", y = "90%",
  width = "97px", height = "46px",
})

The script outputs the following for max width 65:

local picturelabel =
  Geyser.Label:new(
    {
      name = "picturelabel";
      x = "50%";
      y = "90%";
      width = "97px";
      height = "46px";
    }
  )

Notice how semicolons are now used for the table output. Changing max width to 120 gives gives commas as expected:

local picturelabelclose =
  Geyser.Label:new(
    {name = "picturelabelclose", x = "-20px", y = "5px", width = "15px", height = "15px"},
    picturelabel
  )

Unexpect formatting result

the format functionality might mess up code comments when I use format functionality in right-click in code editor, especially when code comments are in the middle line with two the real code lines, formattor seems to shuffle all the code comments up to the top..

before formatting below:
unknown (2)

after formatting below:
unknown (3)

How to keep blank lines?

> lua_code_str = [[echo("hello")
>> 
>> 
>> echo("bye")
>> 
>> cecho("no")]]
>   return get_formatted_code(get_ast(lua_code_str))
echo("hello")
echo("bye")
cecho("no")
> 

How can I keep the blank lines? They are important to have for formatting reasons, some people use them to space elements out.

Accept valid lua 5.1 code

This is more or less a result of #13: I modified the 5.1 branch in such a way that it should format valid lua 5.1 code, namely to allow the variable name goto and remove some binary operators, though those should not break the formatter.

However, i am unsure whether the changes are welcome in the main repository and how you want to include them. I created a new branch from the common ancestor of the code we use and this repository, so it should be easy to include in some way.

You can find the branch at https://github.com/keneanung/lua_code_formatter/tree/code_5.1-syntax_5.1

How to improve on this formatting?

Given the following code:

GUI.MGaugeBackCSS = CSSMan.new([[
    background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0,stop:0 rgba(25,5,5,250), stop:.099 rgba(20,0,0,250), stop:.1 rgba(35,15,5,250), stop:.199 rgba(30,10,0,250), stop:.2 rgba(45,25,15,250), stop:.299 rgba(40,20,10,250), stop:.3 rgba(55,35,25,250), stop:.399 rgba(50,30,20,250), stop:.4 rgba(65,45,35,250), stop:.499 rgba(60,40,30,250), stop:.5 rgba(75,55,45,250), stop:.599 rgba(70,50,40,250), stop:.6 rgba(85,65,55,250), stop:.699 rgba(80,60,50,250), stop:.7 rgba(95,75,65,250), stop:.799 rgba(90,70,60,250), stop:.8 rgba(105,85,75,250), stop:.899 rgba(100,80,70,250), stop:.9 rgba(115,95,85,250), stop:1 rgba(110,90,80,250));
    border-width: 1px;
    border-color: rgba(0,0,0,200);
    border-style: solid;
    margin: 1px 0;
]])

the formatter formats it into the following:

GUI.MGaugeBackCSS =
  CSSMan.new(
    [[
    background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0,stop:0 rgba(25,5,5,250), stop:.099 rgba(20,0,0,250), stop:.1 rgba(35,15,5,250), stop:.199 rgba(30,10,0,250), stop:.2 rgba(45,25,15,250), stop:.299 rgba(40,20,10,250), stop:.3 rgba(55,35,25,250), stop:.399 rgba(50,30,20,250), stop:.4 rgba(65,45,35,250), stop:.499 rgba(60,40,30,250), stop:.5 rgba(75,55,45,250), stop:.599 rgba(70,50,40,250), stop:.6 rgba(85,65,55,250), stop:.699 rgba(80,60,50,250), stop:.7 rgba(95,75,65,250), stop:.799 rgba(90,70,60,250), stop:.8 rgba(105,85,75,250), stop:.899 rgba(100,80,70,250), stop:.9 rgba(115,95,85,250), stop:1 rgba(110,90,80,250));
    border-width: 1px;
    border-color: rgba(0,0,0,200);
    border-style: solid;
    margin: 1px 0;
]]
  )

Which is longer and somewhat of a downgrade in readability. How could I get ([[ and ]]) to stay on the same line? Also, what is the rationale for splitting up GUI.MGaugeBackCSS = CSSMan.new for a single-argument function call?

Commit disappeared

Our project used the tip of the 5.1-syntax_5.1 branch as submodule which used to be commit e152ada. However, this commit seems to have disappeared (a force push?) and our builds are breaking now.

Could you restore that commit?

Is it possible to keep shorthand syntax?

do for k,v in pairs{} do print(k,v) end end

Gets converted to:

do
  for k, v in pairs({}) do
    print(k, v)
  end
end

Where the shorthand syntax for calling functions with a single table argument is lost. Is it possible to keep it?

Hi~

Hi, I was shopping around for code formatters in lua and saw yours on the mailing list. I'd like to support it as an optional backend for my LSP server, but I have a few questions that your readme didn't resolve:

  • Does the implementation only run on 5.3, or does it only implement formatting for 5.3 source code? Both?
  • looking at the API example, it seems like "lcf.workshop.base" installs global variables. is this correct?
  • How would a user of the CLI interface pass in formatting parameters? Is there a project specific file, like .luacheckrc for luacheck?
  • How would I implement "range" formatting, where only a subset of a larger file is formatted? Presumably the formatting of this block would change depending on its context.
  • How does it compare in terms of output to the other formatters listed at Alloyed/lua-lsp#2?

Thanks in advance~

How to implement range-based formatting?

As range-based formatting isn't supported out of the box #2 (comment), what would be the recommended way of implementing it?

The difficulty I see is how to figure out what the initial indent should be - can the AST help here at all?

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.