fornwall / rust-script

Run Rust files and expressions as scripts without any setup or compilation step.
https://rust-script.org
Apache License 2.0
1.21k stars 41 forks source link

IDE support #11

Open jeromegn opened 3 years ago

jeromegn commented 3 years ago

Is there a known way for something like rust-analyzer to work on these scripts? I think if I add a Cargo.toml with all the crates I use in my scripts, I might be able to get some support.

However, that won't work with the special template syntax.

Any tips regarding using IDEs with rust-script? I believe cargo-eval and cargo-script suffer from the same issues.

fornwall commented 3 years ago

Going to be challenging, but leaving this open for ideas and discussion!

Hecatron commented 3 years ago

I think the issue with rust-analyser would need to be done within rust-analyser maybe by handling .ers files specially (since I think that's an extension rust-script can use for scripts by association)

I had a quick look at how debugging might work (such as breakpoints)

The debugger extension would need to run something like the below but automatically switch out the program field with the output from rust-script and also maybe add in a sourceMap entry to map the original source script to the one in the build directory

        {
            "name": "Launch2",
            "type": "lldb",
            "request": "launch",
            "program": "${workspaceFolder}/target/debug/example.exe",
            "args": [],
            "cwd": "${workspaceFolder}",
            "stopOnEntry": false,
            "sourceLanguages": ["rust"],
        },
SafariMonkey commented 3 years ago

I was looking into this literally this evening, and it looks to me like the easiest way to start is by forking rust-analyzer and implementing some changes without significant regard for existing features. I believe this could reach proof of concept quite rapidly. Then, once the fork works, one can think about how and whether integration into rust-analyzer itself might be done. (Given how the suggestion to support individual rs files was received in rust-analyzer/rust-analyzer#4477, I'm unsure as to whether they would be receptive to the idea. On the other hand, rust-script is well enough known that they might be.)

I've added the proof of concept project near the top of my large pile of project ideas. If I get around to trying it, I'll report back.

SafariMonkey commented 3 years ago

I am making progress on this. I will share when I have something to show.

boehs commented 3 years ago

@SafariMonkey Anything to show yet?

I think there are a few things to talk about, detection and IDE support


Detection

the way I see it there are two possible methods

  1. IDE scans all .rs files for a header in the first (10?) lines looking for ``cargoand then assumes it is rust-script than preforms the needed actions or
  2. match for .ers or other? alternative file extension

IDE

Via: https://areweideyet.com/

Obviously supporting the majority of these platforms is silly for our small project, so I crossed out most things that we probably should not focus any effort on. that leaves

As plausible candidates. I feel drawn to VScode as a user of it, so thats my vote for the first focus

SafariMonkey commented 3 years ago

I have spent a decent amount of time on this, with various branches, ending in byte-accurate tracking of the location of the span of the cargo manifest, which I have succeeded in feeding into rust-analyzer. (branch) rust-analyzer is happy to treat an ers file as a Rust source file if you add it to the package.json and some other places, but I'm pretty stuck on how rust-script stuff should be integrated into rust-analyzer. I've considered trying to get some input on the zulip, but I don't exactly even know how to formulate the question.

The best I've come up with is to represent the script as a pseudo-macro that is invoked from the generated path, which generates the code, or possibly representing the source file as a custom source code path ([path]) for the binary in the Cargo.toml. Both of these run into a design decision in rust-analyzer, which is:

To solve (or explicitly refuse to solve) these problems rust-analyzer uses the concept of a "source root". Roughly speaking, source roots are the contents of a directory on a file systems, like /home/matklad/projects/rustraytracer/**.rs.

More precisely, all files (FileIds) are partitioned into disjoint SourceRoots. Each file has a relative UTF-8 path within the SourceRoot. SourceRoot has an identity (integer ID). Crucially, the root path of the source root itself is unknown to the analyzer: A client is supposed to maintain a mapping between SourceRoot IDs (which are assigned by the client) and actual PathBufs. SourceRoots give a sane tree model of the file system to the analyzer.

Note that mod, #[path] and include!() can only reference files from the same source root. It is of course possible to explicitly add extra files to the source root, even /dev/random.

(source)

Overall, this project has been on my mind I haven't touched the code since late March now because I've been unable to find the best approach, nor to formulate my thoughts in a constructive way to ask e.g. the rust-analyzer folks. I've been meaning to write this up for weeks, so thanks for giving me the push to finally do so!

I welcome any thoughts and feedback on how to proceed.

canofworm commented 3 years ago

Helpwanted

mu-us61 commented 1 year ago

Hello there ,

here this library gives some insights about how and why rust-analyzer not working and workaround ways https://lib.rs/crates/rv

and here this library fixes auto-completion, but not code check on the fly , reminder the library expects rust files under "exercises" folder https://crates.io/crates/rustlings-fix


in the end for me as a rust learner the most practical way is building a cargo package and then creating a folder named "examples" which cargo does not complain having multiple separated rust files, and everything works perfectly , You can run individual executable examples with the cargo run command with the --example option , but more easy way is in vs-code installing code runner then defining "rust-script" as run command , then you don't have to worry about the ".exe" output everything is clean and fast, works perfect

image

Andlon commented 1 year ago

It seems to me that forking rust-analyzer or similar solutions is a very significant task, and would mean that you could not get things easily working out of the box with rustup.

Let me suggest a perhaps more pragmatic workaround: rust-analyzer supports non-Cargo based projects. The gist of it is that you need a rust-project.json file that explains to rust-analyzer the structure of the project. This file will be picked up by rust-analyzer in a few different ways, the most obvious being that it will look for it in the root directory of the project. However, for scripts this is impractical, as you might want to have multiple unrelated scripts in the same directory. There's this alternative:

Specify "rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ] in the settings (and make sure that your LSP client sends settings as a part of initialize request).

Based on this, here's what I envision:

  1. Add a --generate-rust-analyzer-config parameter to rust-script that when run generates precisely the needed rust-project.json file for rust-analyzer to be able to understand the file, and output the requisite rust-analyzer settings.
  2. The user then takes the output of this command and pastes it into their rust-analyzer settings.

This way at least rust-analyzer itself requires no upstream changes. Of course, I hope in the future we can have canonical support for rust-script (or a similar solution) in rust-analyzer, but this appears like something that would take a lot of time to accomplish. In the intermediate time, perhaps a solution like this might be a decent workaround?

Andlon commented 1 year ago

To add to that, once such support is available in rust-script, one could imagine building small plugins for specific IDEs like VS Code that would, when confronted with an .ers file, run rust-script to obtain the necessary config and automatically add it to rust-analyzer configuration. I don't have enough experience with rust-analyzer and VS Code to say if this is actually feasible to do, but it doesn't sound so farfetched.

MiSawa commented 1 year ago

Yet another approach would be to create a language server that proxies almost all requests/response to/from rust-analyzer. It would launch rust-analyzer on the cached package, maybe remove some capabilities, translate some file path and file position (for templated scripts), invoke rust-script --gen-pkg-only for some type of requests, etc.. I wouldn't say it's an easy thing to do, but probably easier and more maintainable than forking rust-analyzer, and possible to support templated scripts. Disclaimer: I've never written language-server-related things, so maybe I'm missing something obvious.

xbladesub commented 1 year ago

Any updates on this?

MiSawa commented 1 year ago

Here's a proof-of-concept of what I said in the last comment. It kinda works at some extent, but haven't tested extensively. It'd have been nicer if rust-script didn't remove shebang, as things are off by one line due to it if the script had a shebang, I believe.

fornwall commented 1 year ago

It'd have been nicer if rust-script didn't remove shebang, as things are off by one line due to it if the script had a shebang, I believe.

@MiSawa Great suggestion! Should be fixed by the just released 0.25.0 version of rust-script.

MiSawa commented 1 year ago

Ah nice, thank you for the quick fix! I'm willing to investigate into the other approach (use rust-project.json) later this month, which perhaps is easier, if others hasn't.

MiSawa commented 1 year ago

So... here's a script that generates rust-project.json file for a given rust-script. I also tried integrating it with the language server.

What I learned about rust-project.json

Although many things work well without too much code, rust-project.json isn't a silver bullet. Especially, rust-analyzer relies on cargo for fly check (done through something like cargo check --workspace --message-format=json --all-targets, and diagnostics are emitted based on the result), but since we use non-cargo thing to declare the project, we need to either

What I found

Suggestion

Would it cause any issue if we used the absolute path of the original .ers source as the [[bin]] path=... of Cargo.toml when it has the main function? With that change, I think this (unpublished) revision of rscls works well on that situation.

fornwall commented 1 year ago

@MiSawa Nice work - https://github.com/fornwall/rust-script/pull/103 has just been released in version 0.28.0.

Perhaps we should move towards this setup where the source input file is unmodified always, and deprecate (or at least avoid encouraging it in the the documentation) functionality such as automatically wrapping the script with a main function?

MiSawa commented 1 year ago

Thank you, released rscls 0.2.0 that works well with rust-script 0.28.0 (except templated and non-main) in my environment.

Hmmm, I personally always use scripts including main, and that writing of main doesn't really bother me, so I'd +1 for not encouraging in favor of lsp support. There's a RFC for official support of single file scripts, and I guess they won't support such features initially (at least MVP doesn't). So explicit main may be better for compatibility with that thing.

kurtlawrence commented 11 months ago

+1 @MiSawa to rscls! Works seamlessly with Neovim!