Skip to content
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

Adding Rust Support #546

Closed
wants to merge 7 commits into from
Closed

Conversation

trace-andreason
Copy link
Contributor

@trace-andreason trace-andreason commented Feb 4, 2022

What

Adds support for the rust programming language

Checklist

  • Recorded tests for the new language
  • Used "change" / "clear" instead of "take" for selection tests to make recorded tests easier to read
  • Added a few specific tests that use "chuck" instead of "change" to test removal behaviour when it's interesting, especially:
    • "chuck arg" with single argument in list
    • "chuck arg" with multiple arguments in list
    • "chuck item" with single argument in list
    • "chuck item" with multiple arguments in list
  • Added a test for "change round" inside a string, eg "hello (there)"
  • Supported "type" both for type annotations (eg foo: string) and declarations (eg interface Foo {}) (and added tests for this behaviour 😊)
  • Supported "item" both for map pairs and list entries (with tests of course)

@trace-andreason trace-andreason mentioned this pull request Feb 4, 2022
1 task
@trace-andreason
Copy link
Contributor Author

@pokey think this PR is in a pretty good spot now.

@trace-andreason trace-andreason changed the title my adding rust support, for @purpleP to compare with his branch Adding Rust Support Feb 21, 2022
Copy link
Member

@pokey pokey left a comment

Choose a reason for hiding this comment

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

This is looking really good. Left a few inline comments. In addition, I've taken the liberty of editing the description to use the PR template for adding a language. It would be great if you could make sure you check all the boxes there

In particular, I see that you've used "chuck" for all your tests, but we generally use that one just when the removal is interesting, eg it needs to clean up a , or :. We use "change" test (or "clear", if you're still using that spoken form) as the default action for tests. Otherwise you're basically just testing our whitespace removal code in addition to your intended test case 😅

I don't think you need to re-record all your test cases, but for the ones where removal is interesting, (eg "arg", "type", etc), it would be good to add a "change" test as well

Also:

  • Looks like you're missing a few tests:
    • "lambda"
    • "if state"
    • "state"
    • type declarations (as opposed to just type annotations)

src/languages/rust.ts Outdated Show resolved Hide resolved

// Generated by the following command:
// `curl https://raw.githubusercontent.com/tree-sitter/tree-sitter-rust/master/src/node-types.json | jq '[.[] | select(.type == "_declaration_statement" or .type == "_expression") | .subtypes[].type]'`
const STATEMENT_TYPES = [
Copy link
Member

Choose a reason for hiding this comment

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

This is going to be way too broad. You'll prob want to just look for children of _declaration_statement, and then add expression_statement to the list. You don't want to get trapped in an expression when you say "take state"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think _declaration_statement will be too narrow, it wouldn't include if statements for example

Copy link
Member

Choose a reason for hiding this comment

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

Got it. Looks like the Rust parser is like Ruby, where the expression_statement node doesn't appear in the parse tree. The problem is that we need to distinguish between eg an if statement as a statement, vs if statement as an expression. For example, when you say "take state", you'll capture the if statement here:

let foo = if bar { "hello" } else { "there" }

which you don't want, because that if statement is actually functioning as an expression. In Ruby, we resorted to inspecting the parent node to see if the expression is at statement level vs a nested expression. See https://github.com/cursorless-dev/cursorless-vscode/blob/993fbc8d14846414dbfc374c6fa56d3098f03465/src/languages/ruby.ts#L157-L166. Should be able to do something similar here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ahhhh I see what you are saying now

src/languages/rust.ts Show resolved Hide resolved
"let_declaration.identifier!",
"parameter.identifier!",
],
};
Copy link
Member

@pokey pokey Feb 22, 2022

Choose a reason for hiding this comment

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

Looks like you're missing a few scope types here:

  • class. Might also want to include structs in this one
  • item. This is for list elements as well as pairs in a map
  • map
  • value. This one is for RHS of an assignment, as well as value in a map
  • key

Maybe a few others? Worth looking at eg Typescript impl for inspiration here. I think the other Rust PR might have a couple of these as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the map/key/value thing doesn't really exist in rust as far as I know: https://doc.rust-lang.org/book/ch08-03-hash-maps.html. Given the syntax, it would probably make more sense to just target key, values as args.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

items are proving hard to implement...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added class, I'm not sure I can actually implement item though

Copy link
Member

@pokey pokey Mar 18, 2022

Choose a reason for hiding this comment

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

Re map/key/value, fair enough; let's leave that out

Re item, did argumentMatcher("array_expression") not work?

Copy link
Member

Choose a reason for hiding this comment

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

Also, although there are no "values" in the map sense of the word, you'll still want "value" as right-hand side of assignment, default value of parameter, return value of function, etc

src/languages/rust.ts Outdated Show resolved Hide resolved
initialState:
documentContents: |-
let expensive_closure = |num| {
println!("calculating slowly...");
Copy link
Member

@pokey pokey Mar 18, 2022

Choose a reason for hiding this comment

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

This test is nice. Worth also testing that "change state" with cursor inside calculating clears this line exactly

@pokey
Copy link
Member

pokey commented May 30, 2022

@trace-andreason shall I take this one to the finish line? I think it's getting pretty close

@purpleP
Copy link
Contributor

purpleP commented Jun 11, 2022

@pokey

Hi, I wanted to continue the work on this. I’ve done a rebase and tried to run this, but instructions aren’t clear to me. This is what I see when I try to run the extension.

out.mp4

@pokey
Copy link
Member

pokey commented Jun 11, 2022

Hi @purpleP great to hear you are interested in picking it back up!

It's a bit tough to tell what's happening in your video, but you will probably want to run step 3 from https://www.cursorless.org/docs/contributing/#steps if you haven't already

@purpleP
Copy link
Contributor

purpleP commented Jun 12, 2022

Hi @purpleP great to hear you are interested in picking it back up!

It's a bit tough to tell what's happening in your video, but you will probably want to run step 3 from https://www.cursorless.org/docs/contributing/#steps if you haven't already

Oh, yeah. I forgot to say that it gives me an error about fs/promises not being installed.

@pokey
Copy link
Member

pokey commented Jun 12, 2022

Strange. Does that also happen on main?

@purpleP
Copy link
Contributor

purpleP commented Jun 12, 2022

Strange. Does that also happen on main?

Yep.

700D8C00-173D-4ACB-81D4-255B9941F27D

@pokey
Copy link
Member

pokey commented Jun 12, 2022

Huh. Maybe remove your node_modules and repeat the incantation I linked above?

@auscompgeek
Copy link
Member

fs/promises was added in Node 14. Any particular reason for using an EOL version of Node?

@purpleP
Copy link
Contributor

purpleP commented Jun 12, 2022

fs/promises was added in Node 14. Any particular reason for using an EOL version of Node?

Thanks, updating to 16 LTS helped.

@purpleP
Copy link
Contributor

purpleP commented Jun 14, 2022

@pokey How do I match a function call?

Right now cursorless select the whole call_expression node and I want it to select field_identifier with all arguments.
19120E0C-884B-464E-9166-76CACD198C29
A9227635-90FE-48A2-B29E-F54002370BB1
5381F461-2CE8-4F93-B42B-D649BC53B649

@pokey
Copy link
Member

pokey commented Jun 14, 2022

That's actually considered correct behaviour for cursorless "call" scope type. I believe what you're after is #707. That should probably happen as a separate PR so that we can introduce it in a few languages for consistency

@purpleP
Copy link
Contributor

purpleP commented Jun 16, 2022

@pokey

What should I do with type constructors and type arguments? I think they should work the same way functions and function arguments work.

Also what should I do when I want to delete things like mut modifier? Removing just the token leaves trailing space.

Image 16 06 2022 at 12 14

@pokey
Copy link
Member

pokey commented Jun 17, 2022

What should I do with type constructors and type arguments? I think they should work the same way functions and function arguments work.

I think it's fine to support "arg" for those type arguments. But I wouldn't support "type" for them. That's just for the top-level one (green in your annotations). Be sure to clean up that arrow on "chuck" if you're not already

Also what should I do when I want to delete things like mut modifier? Removing just the token leaves trailing space.

Sorry not sure I'm following. Is this for "type", "arg", or something else?

@purpleP
Copy link
Contributor

purpleP commented Jun 17, 2022

Sorry not sure I'm following. Is this for "type", "arg", or something else?

I’m talking about fn f(&**mut** self

@auscompgeek
Copy link
Member

auscompgeek commented Jun 17, 2022

my 2c: in param: &mut Param, the type is &mut Param, and the argument is param: &mut Param. I'm not sure how you would select the type without modifiers though (i.e. Param), or just the modifiers though 🤔

@auscompgeek
Copy link
Member

either way, rustfmt will clean up any lingering whitespace, right? probably not a huge problem for deleting mut in a borrow type

@pokey
Copy link
Member

pokey commented Jun 17, 2022

Re modifiers, a few thoughts:

  • I agree that modifiers should be considered part of the type, so eg

    fn f(&mut self, param: &mut Param)
      // ^--^              ^--------^

    And the removal range should include the trailing space or the leading colon, so for example:

    fn f(&mut self, param: &mut Param)
      // ^---^           ^----------^

    This should be accomplished by using a trailing delimiter of on &mut and a leading delimiter of : on &mut Param

  • When referring to mut as a token, today it won't clean up whitespace because it's a bit conservative about merging tokens, but we are considering changing it so that it will remove spaces if it detects that it won't merge tokens. Either way that is probably independent of rust support

  • It might be worth adding a new scope type called "modifier" that can be used to refer to something like mut. Alternately we maybe we could repurpose "attribute"

@pokey pokey mentioned this pull request Jun 20, 2022
14 tasks
@pokey
Copy link
Member

pokey commented Jul 5, 2022

Finished in #775

@pokey pokey closed this Jul 5, 2022
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.

4 participants