Skip to content

ES6/7 Refactor #466

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jun 11, 2016
Merged

ES6/7 Refactor #466

merged 22 commits into from
Jun 11, 2016

Conversation

amadeus
Copy link
Collaborator

@amadeus amadeus commented Jun 6, 2016

Ok, this one has been a long time coming, and I finally found some time to sit down and work on it.

This PR refactors quite a bit of the syntax file to allow us to better support both current ES6 features, and make it far easier to support upcoming ES7 features.

The big thing it does is actually create many more distinctions around block types. By block types I mean anytime you use { and }. This includes being able to detect an if statement, vs a switch statement, vs a for/do/while loop, vs an actual object definition, vs function blocks, etc.

There are probably going to be some bugs.

One thing this PR does do, is provide a fix/support for:

var obj = {
    func(){},
    func2(){},
    [CONST.SOMETHING],
    [CONST.SOMETINGELSE]: function(){}
};

It also lays the framework for me to fix/add support for #433, #425, #393

I've made this branch on this repo, so please check it out and try using it so we can get it a bit more battle tested before merging it in.

amadeus added 10 commits June 5, 2016 14:01
* Re-organized a couple things
* Added some TODO comments to remind myself what needs to be done
I truly believe this has no place in a JS syntax file...
* Checked all aspects of jsExpression - In preparation for it to be
  properly used
* Code style tweaks - contained is such an important option, ensuring
  it's always first
With the refactoring of jsExpression, I was able to add a ton of finer
granularity to various new region types.

Current new regions:

* jsObject
* jsArray
* jsParenIfElse
* jsParenRepeat
* jsParenSwitch
* jsClassBlock
* jsBlock
* jsSwitchBlock
* jsTernaryIf
I think this is actually in a really good place. It seems to work well
in my test phaser.js file
Trying to make argument order more uniform
I don't think it needs to be a region, and infact incurs some annoying
complexity if so
@amadeus
Copy link
Collaborator Author

amadeus commented Jun 6, 2016

I've also reworked the file a bit to make it more organized, and tried to add more consistency with how we organize arguments and stuff. Please do not merge anything related to syntax files in until this gets merged in, or else it might be a nightmare for me to fix the merge conflicts.

@bounceme
Copy link
Collaborator

bounceme commented Jun 6, 2016

hey, just tried it out. by a quick test with indenting,since it relies on some syntax items, there seems to be a difference with line-comments. I couldn't pretend to be a great judge on the syntax side of things but i think you have done a great job! also seems faster, even with indenting

@amadeus
Copy link
Collaborator Author

amadeus commented Jun 6, 2016

I actually removed line comments as an item and just made them part of jsComment. Is this going to mess things up for you? I could put that back in.

@bounceme
Copy link
Collaborator

bounceme commented Jun 6, 2016

i'll try a few things out in terms of the line comment detection, i don't know for sure but maybe line comments are simple enough for a regex in the indent script

There is one caveat right now, to properly support it, we sort of
require semicolons. Not sure if this is something people will be happy
with or not...
@amadeus
Copy link
Collaborator Author

amadeus commented Jun 6, 2016

I've gone ahead and made a first pass at supporting/fixing the following issues: #433, #425, #393

@iansinnott, @daGrevis, @paxunix if you guys can check out the es6-refactor branch and see if it fixes your issues, that would be super helpful. There will certainly bugs or things I have not yet forseen that still need to be fixed, but the more eyes on this the better.

@amadeus amadeus changed the title ES6 Refactor ES6/7 Refactor Jun 6, 2016
Because it's not ALWAYS an array
@amadeus
Copy link
Collaborator Author

amadeus commented Jun 6, 2016

@bounceme @davidchambers I added a couple DISCUSS comments in the changes, would be curious to get your thoughts on it as well.

syntax keyword jsBuiltins decodeURI decodeURIComponent encodeURI encodeURIComponent eval isFinite isNaN parseFloat parseInt uneval

" DISCUSS: How imporant is this, really? Perhaps it should be linked to an error because I assume the keywords are reserved?
syntax keyword jsFutureKeys abstract enum int short boolean interface byte long char final native synchronized float package throws goto private transient debugger implements protected volatile double public
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're to highlight these as errors, let's ensure they're not highlighted when used as property names. The throws in assert.throws, for example, should not be considered an error.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these showing as warnings would help, but i think these should be detected by linters,unless we can make them errors only if used as identifiers

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think we shouldn't even try to match or error correct these. I think leaving it up to a linter is a far better and more accurate solution. My vote would be to remove these completely since it's hard to figure what contexts they should and shouldn't be matched.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think leaving it up to a linter is a far better and more accurate solution.

Seems like a nice separation of concerns to me. 👍

@daGrevis
Copy link

daGrevis commented Jun 6, 2016

Bug in #393 seems to be fixed, good job!

Unfortunately I found a problem with HTML not being highlighted.

class MyComponent extends React.Component {

    // HTML syntax works if I remove this.
    state = {
        isActive: false,
    }

    render() {
        return (
            <div>
                HTML doesn't have syntax highlighting. Syntax breaks on ' character.
            </div>
        )
    }

}

@amadeus
Copy link
Collaborator Author

amadeus commented Jun 6, 2016

@daGrevis that's JSX syntax, which you can get proper syntax highlighting for using this plugin mxw/vim-jsx (it uses ours as a foundation, so it plays well).

Also, what you are seeing is a very tricky issue to fix. If you place a semicolon after that object definition, it should correct the issue you are seeing.

I am not sure there is valid/elegant way for me to fix that issue without requiring a semicolon

@kimfiedler
Copy link

I just tried the es6-refactor branch out on a few files in some of my projects and it seems to fix the issues with class instance fields and class static properties for me and I don't see any new problems.

It does - as you mentioned - require me to insert semicolons after object definitions. Would be great to find a solution for this but this is definitely a step up.

Thanks for working on this 👍

amadeus added 2 commits June 6, 2016 13:20
We can use this as a test. By unlinking it, it shouldn't ever have any
color theme associated with it, and if people complain, they can still
highlight it. Otherwise if we hear no complaints after a while, we just
pull it completely.
It's now technically a statement
@daGrevis
Copy link

daGrevis commented Jun 9, 2016

@daGrevis that's JSX syntax, which you can get proper syntax highlighting for using this plugin mxw/vim-jsx (it uses ours as a foundation, so it plays well).

Yes, I have that plugin already. Just saying that example above also breaks JSX highlighting.

Also, what you are seeing is a very tricky issue to fix. If you place a semicolon after that object definition, it should correct the issue you are seeing.

I am not sure there is valid/elegant way for me to fix that issue without requiring a semicolon

You are right, putting a semicolon at the end fixes the problem.

The thing is that me and a lot of other people are not using semicolons in JavaScript because they are optional. I'm not sure how I feel about cluttering the source code with them just to get correct syntax highlighting. Here's a commit where I added semicolons to a single file — quite a lot of noise. This also won't work for other people JavaScript code if they are not using semicolons.

What makes this issue so tricky to fix? Maybe we can put out heads together and try to fix it anyway? :)

Thanks for your work, I really appreciate it. 👍

* Fixes to jsSpreadOperator
* Added fold to jsObject
* Organized fold/extend arguments better
@amadeus
Copy link
Collaborator Author

amadeus commented Jun 10, 2016

@daGrevis so it might be hard to give you a short answer as to why, but let me see what I can come up with here.

There's a high level discussion to be had for how accurate a syntax highlighter should be, but our philosophy has been that we want to catch obvious and simple syntax errors yet not get into more logical type problems that Flow, Tern or even ESLint would catch.

When creating a syntax regex in Vim, you have a few options - match, which takes a single regex and matches that to a groupname, keyword, which is basically a shorthand to match keywords without needing to write a regex, and finally the big one, region, which maps a specific region or scope based on a start and end regex.

Regions are special because they provide a new blank cope to work from that you can then opt other types of regions, matches or keywords into. There is one caveat with regions however, as long as the starting regex matches, the region begins. It does not care if there is an ending region or not.

Something we do with this syntax file is create a ton of regexes that we then group into a special cluster called jsExpression. As the name implies, these are basically any sort of expression in JS. this include a variable, string, function, object, equations, etc. We pass this cluster into various regions as needed.

Example regions include an object (defined by a { and closing }), function definition, (which is defined by a { and } as well, however it's scoped to ONLY come AFTER the ( and ) of a function definition.

Using these scopes allows us, for example, to differentiate between an object key someKey: and a more generic ternary expression test ? "true" : "false".

In the case of your example, we have a class definition block, also defined by { and }. Classes allow for special function type definitions like this (also, ES6 provides a similar shorthand syntax for objects):

class Example {
    // Inside here is a class definition region
    someFunc(){
        // Inside here is a function definition region
    }                   
}

This type of shorthand syntax is technically a syntax error if you put is in another context:

var func = someFunc(){};

In this case, someFunc() would be considered a function call of another function named someFunc, and the {} after it would technically evaluate to an object, this of course is a syntax error when it reaches the opening {.

Therefore, that specific regex to match a function definition, within a class block, is special, and isolated ONLY to the class blocks.

It gets complicated, because class definitions in ES7 syntax, support the ability to create expressions:

class Example {
    someVal = 20
}

This is cool and all, and you're probably thinking, why don't we just throw the entire jsExpression cluster inside there. Well this becomes a problem in a case like this:

var getValue = function(){
    return 2;
};
class Example {
    someVal = getValue()
    someFunc(){}
}

In this case, we need to match getValue() as a function call, but match someFunc as a function definition. If we just make jsExpression contained within the class block generically, then we will will run into a priority problem - whichever regex was defined later (function name or function call) will ALWAYS take precedence. So either you will ALWAYS get matched as a function call or a function definition.

The way to fix this is to create a new special region (called in this case jsClassValue) that then follows an = sign WITHIN a class block that then contains the jsExpression cluster. This works great up until we have to figure out how to END that region. If you remember from earlier, the region will ALWAYS start, regardless of whether it finds an end or not. The = provides our start, however now we have to end that region. A semi colon makes it super easy, since we don't have to worry if newlines are involved, AND other contained regions inside of this new region will then extend the jsClassValue region conveniently. By this I mean if you did something like this:

class Example {
    getValue = () => {
        return this._value;
    };
    someFunc(){}
}

The semicolon within the arrow method will not end the jsClassValue region. And it won't be until we end the function block region defined by the { and } that we match the closing ; for jsClassValue region.

Javascript is introducing so many new fancy shorthand features, that it's becoming a huge nightmare to match using regexes.

Maybe others have some ideas?

This fix may actually work out for us when using jsClassValue. There
still are some cases where it could be tripped up, for example on multi
line equations and stuff, but for the most part I think it should do the
trick.
@amadeus
Copy link
Collaborator Author

amadeus commented Jun 11, 2016

Having said all that, I have just pushed a small change to add a newline \n as a region end to jsClassValue, and I think this may actually work in most cases.

There is still the possibility of a sort of multiline statement breaking things, but I think at least for now, it could help.

Please pull the latest changes and test them when you have a chance.

amadeus added 2 commits June 10, 2016 17:50
This should help with errant detection in files that use Flow. It also
makes things safer so when typing out a ternary expression, you don't
get weird flickering of major syntax region changes.
@daGrevis
Copy link

I can confirm that it works for every case in svg-scene repo. I also tested it on my other private ES6 + JSX repo and there were no problems.

I will test it at work on Monday.

Thank you! 💯

@amadeus
Copy link
Collaborator Author

amadeus commented Jun 11, 2016

Alright, I think this PR may be ready to go in. Obviously it's still for develop, so there is more time to fix things. Any objections to getting this in now?

@amadeus amadeus merged commit 31d5a01 into develop Jun 11, 2016
@amadeus amadeus deleted the es6-refactor branch June 11, 2016 21:19
bounceme added a commit that referenced this pull request Jun 11, 2016
support less common object defs . 
comment detection compatibility.
only use a c style when not in a block
@iansinnott
Copy link

Excellent work @amadeus! So far this is looking great

amadeus pushed a commit that referenced this pull request Jun 14, 2016
support less common object defs . 
comment detection compatibility.
only use a c style when not in a block
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants