-
-
Notifications
You must be signed in to change notification settings - Fork 85
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
initial nix language support #1911
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you're making some good progress! I would advise recording tests while you're developing rather than at the end. If something works like you want it to, record a test. That way you won't break it later. It's ok if a scope doesn't work as you'd like it to in some ways: I would still advise recording tests to capture the ways in which it works like you do want it to.
Recording tests is absolutely trivial once you're set up; takes about as long as manually trying a command. In addition, it's really hard for me to review this code without tests, so I won't be able to give you early feedback, because I'm not intimately familiar with tree-sitter-nix 😅
Ok thanks for the tests, though we prefer "change" instead of "take" because it makes the tests much easier to read. See the PR template. Also, "chuck" tests are only necessary in cases where "chuck" is interesting, eg removing commas, etc If you don't want to re-record the "take" tests, you can just find and replace |
I'd be happy to try this out on some real files once the corresponding parse-tree PR is merged. |
One thing I can't entirely decide is if I have no functional programming background, so someone with experience might think differently? To be clear, in nix technically But if the cursor is in the This also gets a bit cumbersome if your cursor is in the I think one possible exception to this should be if one of the lambdas is explicitly wrapped in Maybe this was already discussed/decided for other languages, but I didn't check yet. Worth noting that atm I also made it so above works as a single "named function" where and Not sure what to think of that yet, or how it currently works in other languages. But my thinking is because nix is only lambda functions, meaning named functions won't be used elsewhere, this might be nice to use. |
huh interesting. so is currying the only way to define a lambda with multiple parameters? If so, then I think I'd lean towards treating @auscompgeek @josharian @AndreasArvidsson curious to hear your thoughts tho |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking really good! Excellent work. Left a bunch of small comments
packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArgBat.yml
Outdated
Show resolved
Hide resolved
packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArgBlueDot.yml
Outdated
Show resolved
Hide resolved
packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch.yml
Outdated
Show resolved
Hide resolved
packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryIfState.yml
Show resolved
Hide resolved
packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap.yml
Outdated
Show resolved
Hide resolved
@auscompgeek fyi nix support merged into parse-tree a little while ago in case you didn't see. |
9ea7601
to
7c5e12a
Compare
(#not-parent-type? @functionCall apply_expression) | ||
) | ||
|
||
;; This is gross, but not sure how to do fuzzy matching against an unknown number of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious if someone is aware of a better way to do this? I couldn't see anything in other .scm files, the tree sitter query documentation, or chatgpt. Assuming there isn't something I missed, it almost seems like something a special predicate could handle more elegantly?
...rsorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairTextBased.ts
Outdated
Show resolved
Hide resolved
packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getDelimiterMaps.ts
Outdated
Show resolved
Hide resolved
packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getDelimiterMaps.ts
Outdated
Show resolved
Hide resolved
queries/nix.scm
Outdated
"assert" @statement.start | ||
condition: (_) | ||
";" @statement.end | ||
body: (_) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok a couple surprising things when exploring the assert
flie:
"call"
To be honest, I wasn't expecting asserts to match "call" at all. It feels more like a statement to me than calling an "assert" function. But I could see that going either way cc/ @auscompgeek
The more surprising thing is that while the range seems reasonable (assuming we want asserts to be calls), the domain was quite surprising. It seems to contain everything after the assert statement. Hence the increasingly light color of each subsequent assert in the above screenshot.
I'm guessing that has something to do with this whole thing being pure functional, so it's kinda like how let x in ...
technically includes the ...
in langs like clojure. But I'm not sure it's desirable behaviour for the "call" scope
This "call" in particular confused me:
Should it not include "a"
? If not, should it really include the ;
? Is that assert statement even valid? I made it up 😅
"state"
Agreed that assert
should be a statement, and I'm happy with your ranges here. However, as with "call"
above, it seems to be exhibiting the same surprising nested domain behaviour
And agreed that it shouldn't be a statement if it's the rhs of an assignment. Tbh I have no idea what that's supposed to do, but it doesn't look like a statement to me 😅
"branch"
This one was also surprising to me. I guess it makes more sense in a one-line assert? cc/ @auscompgeek
"callee"
As above, the fact this is a "callee", as well as the domain, were surprising to me
"condition"
This one makes the most sense of any to me, even the domain. I guess if people see assert statements this way, then maybe the other domains make sense as well? Still not totally sure bout "call" tho
Tests
Looks like there are no tests for assert
- Assert as statement
- Assert condition
- Assert as call, if we decide to keep that
- Assert callee, if we decide to keep that
- Assert branch, if we decide to keep that
- Negative test for assert not being statement when on rhs of assignment (say "cursorless record error" and then do "change state")
Hmm nix really does blur the line between statement and item, doesn't it? My mind kind of expects the single-line ones not to be statements 😅. But that doesn't feel like a good distinction to draw. Idk cc/ @auscompgeek . Looks like there's no difference between a "statement block" and a "map"? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok left some comments. Haven't done a thorough review, but given it's marked draft, I assume you're still just looking for early feedback?
Also, would be good to make sure you're formatting with @AndreasArvidsson 's formatter; I'm getting a big autoformat when I save one of your files locally |
Ya. Really there isn't much similarity to what you'd normally think of as a statement in other (non functional) languages at least. Originally I was leaning towards statements because there are so many complex expressions and stuff when setting key/value pairs, but I think using items feels more natural after playing with it for awhile. I've disabled most statement matching for now to test more, and also added better item support. |
Yeah scm autoformat doesn't run as a hook yet as it's just implemented in that VSCode extension. We need to turn it into a cli |
Things seem a lot nicer with most of these assert issues addressed (not pushed yet). I like the idea of having branch apply only if it's a one-liner, but from searching the repo I don't see how its possible to actually have that as a query predicate? Is there some existing example of where you've already done this or would I have to add a new predicate? |
Oh I wasn't suggesting actually changing definition of "branch" depending on whether it's one or multiple lines. I think Python has taught us that making whitespace significant is a deeply problematic notion 😅. I think it would be quite surprising if a scope behaved differently if it was on one or multiple lines. Unless nix devs see them as significantly different constructs? cc/ @auscompgeek Lmk if you want me to take another look, or to drop into a meet-up to discuss some of this stuff |
Fair enough. As much as I hate python whitespace, I actually think there are maybe a couple cases in nix where it could sort of make sense to be whitespace dependent to feel "natural", but I don't have time to explain right atm. I'm new to coding nix so I'd rather hold off implementing weird things I assume maybe make sense and instead just push something that has the basics covered and then wait until more people (hopefully more experienced devs) get to play with it. You can feel free to take another look now ya. There are two issues that I think might require new query predicates but aren't show stoppers (maybe... will see how much you groan at how I handled call arguments in apply/select expressions lol), so happy to solve them as follow up. Will explain in more detail in a comment tomorrow. I do hope to actually make it to a meetup at some point :D |
So to elaborate on a few changes and issues: The statement stuff has changed to only be lines within the first There are I think 3 things could be done that I'm not sure how without possibly building new query predicates, so I'll just list them here.
In this case intuitively I don't think the
In the latter I find it feels natural to want to target the There is a similar case with the
Anyway, I think you are right that avoiding whitespace-based differences, but I wanted to explain anyway.
It works-ish, but the problem is that there are two types of comment both which match as
But this doesn't work since So this made me think we probably want something like a I think that's it. Definitely understand why you like lots of small PRs, as it gets hard to track all these different issues (even for me actively working on it, let alone you lol). |
Here is a proposal for a new query predicate operator: fidgetingbits#1. I think it's actually fairly general purpose
I would be tempted to just err on the side of more scopes. So if it makes sense multi-line, let's include it always. Can always "grand state" your way out of it or target part of the scope not trapped
I would just include the
Ha yeah 😅 Also, looks like this PR has some of the same leading / trailing / removal issues I fixed in #1962, so would be awesome if you could take a crack at fixing them. Basically:
|
We discussed this one in a meet-up and decided it was simpler to just introduce a query predicate operator called |
Sounds good. I do really need to get more familar with typescript, so will try to give it a whirl when I find the time. |
As proposed above, I don't think we'll end up going the route of fidgetingbits#1, but I captured it in a tag just in case we want it in the future, and the PR disappears from @fidgetingbits's repo for whatever reason (not sure how things work with forks). Here's the commit 52ce5b1 |
This is me breaking out some functionality that was part of PR #1911 since that PR has lots of work to be done and it may still benefit other languages that get in sooner. I've applied the suggested changes from pokey in that PR's code review. I've also added a placeholder map for lua which uses `[[ ]]` for multiline support as noted in PR #1962, however I realized as I went to add it that it's not as simple as the way I did it for nix because I can't just reuse the existing `singleQuote` entry since lua actually uses single quotes. So this may actually be a bit harder, and reminds me of [this discussion](#1992 (comment)). So before I go randomly hacking stuff I'm curious what you think the best approach here is. These are the comments I had left in the Nix PR about these changes: * I called returning delimiterToText as getSimpleDelimiterMap to kind of mirror complexDelimiterMap. * I'm not sure is if you still want a delimiterMap.ts standalone file, and then a getSimpleDelimiterMap.ts only for that function. Now delimiterToText isn't referenced anywhere else, so seemed maybe okay to keep them together. * I also thought about adding getComplexDelimiterMap(), but atm because complexDelimiterMap only ever uses the keys from the delimiterToText it won't change even if the language has different values, so seemed unnecessary. These are all things I could guess at your preferences, but may as well ask instead. * I also noticed leftToRightMap in that file isn't actually used anywhere so can be deleted I think, but also not sure about doing that as part of a totally unrelated code change, to keep commits clean. ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [x] I have not broken the cheatsheet --------- Co-authored-by: fidgetingbits <[email protected]> Co-authored-by: Pokey Rule <[email protected]>
@fidgetingbits I've tried to read through the history of this one, but I think it's better if I just ask you. Is this one ready? Are you happy with the current state? |
No it's got some bugs and some decisions I made early on I think need to change now that I'm much more familiar with the language. I will come back to it eventually but I haven't been able to actually work on it in months, although I do use it every day. I guess one question for you is, given what a mess this PR became, would it be better/easier to just close it and make smaller PRs over time? |
Okay I will convert it to draft in the meantime. You can decide if you want to continue to work on this pull request or close it and make multiple smaller ones. I unfortunately don't have the time to review languages I don't know in the kind of detail that Pokey did. I will more or less entrust the behavior to you. ie when you say that it's ready and you are happy with it we will merged unless I find something I definitely don't like. Sounds good? |
EDIT: I removed the original PR text, as no longer relevant and will have made it harder to review.
This PR adds nix language support.
Checklist
"change"
/"clear"
instead of"take"
for selection tests to make recorded tests easier to read"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@textFragment
captures. Usually you want to put these on comment and string nodes. This enables"take round"
to work within comments and strings."change round"
inside a string, eg"hello (there)"
"type"
both for type annotations (egfoo: string
) and declarations (eginterface Foo {}
) (and added tests for this behaviour 😊)"item"
both for map pairs and list entries (with tests of course)list
@list
inside list
@list.interior
map
@map
inside map
@map.interior
key
@collectionKey
funk
@namedFunction
inside funk
@namedFunction.interior
funk name
@functionName
lambda
@anonymousFunction
inside lambda
@anonymousFunction.interior
name
@name
value
@value
value
@value
value
@value
state
@statement
if state
@ifStatement
condition
@condition
condition
@condition
condition
@condition
condition
@condition
condition
@condition
branch
@branch
inside branch
@branch.interior
comment
@comment
comment
@comment
string
@string
string
@string
@textFragment
call
@functionCall
callee
@functionCallee
arg
@argumentOrParameter
arg
@argumentOrParameter
arg
@argumentOrParameter
class
@class
inside class
@class.interior
class name
@className
type
@type
Checklist