rust-lang / rustfix Goto Github PK
View Code? Open in Web Editor NEWAutomatically apply the suggestions made by rustc
License: Apache License 2.0
Automatically apply the suggestions made by rustc
License: Apache License 2.0
After a cargo install rustfix
, the subcommand cargo fix
would be available.
Right now cargo fix
produces no output of its own, mostly relying on Cargo to produce such output. It seems like it'd be a good idea, however, for cargo fix
to produce its own output which may include information like:
i am using VSCode configured to run cargo clippy
on save which shows me lot of warning particularly unused imports. If I run rustfix
, I get I don't have any suggestions for you right now. Check back later!
.
How can I force rustfix to run and fix suggestion provided by clippy? I even tried to delete the target directory but always get the same message.
Right now I think this just executes cargo check --help
which probably isn't too useful
We should consider collapsing mutliple spans/suggestions into one.
As @mcarton mentioned elsewhere, he might add/added lints that yield multiple suggestions to add e.g. #[derive(Default)]
(IIRC), so it makes sense to let user accept the complete change in one step.
I'm not sure when to collapse spans, though.
This will look far better with a diff-like output (cf #13).
I can not find any docs about functionality applying only certain fixes
,
so here is feature request: it would be nice to filter possible fixes and applying only some classes of them.
For example, during development phase you can have bunch of warning unused something
,
this is ok on development stage, but should be fixed on stabializing feature stage.
Also, for example I read about rustfix
here and would like to try to fix use
issues, but only for them.
So, it would be great to have something like --ignore-class=unused
and --apply-only-for-class=use
command line arguments.
Hey!
just curious if you plan to create some kind of, I call it "fixlog", where every replace-to hunk will be logged. I don't know the real use for this yet but I'd like to see what I changed or better said what rustfix suggested. Maybe as a learning/best practice resource afterwards?
greets
Charlie
Right now, if we get a suggestion that has two alternatives, rustfix shows them as single suggestions after each other. Instead it should show that this suggestion has alternatives and require one to enter a number to choose the replacement to use.
The rustfix
tool is going to be a banner feature of the 2018 edition coming up later this year, so I think we'll want to UI to be as good as we can get it to ensure that everyone's got a smooth experience with applying the tool and updating code.
rustfix
work today?I've been reading the code as-is to better understand how it works today, and I'll try to summarize it here for the unfamiliar but please correct me if I'm wrong! Today you can either execute rustfix
or cargo fix
and they'll do the same thing. The CLI takes no arguments and has a few flags that change its operation. Just executing rustfix
means that it'll instead run cargo rustc -- --message-format json
.
Once Cargo/rustc is executed rustfix slurps up all the output of rustc. The stderr stream is then parsed into a list of suggestions and replacements. All suggestions are then iterated over and by default presented to the user to ask whether the fix should be applied or not. If accepted then it's queued up to be applied later. This then happens for all suggestions found, and there's a flag as well to say "don't query me, just apply everything".
Finally after all this the suggestions will be applied one-by-one, updating the files on the filesystem.
I think we're largely undecided on this in the sense that we haven't concretely nailed this down (although I may have missed it!). I'll put forth a strawman though for how I could see this working.
First off before we release the edition we'll want to test out lints, language features, and rustfix. To do that you'll first add #![warn(rust_2018_migration)]
to the crate you'd like to update. This will include src/lib.rs
, src/main.rs
, tests/*.rs
, examples/*.rs
, etc. They'll all need this annotation. After that's applied you'll execute cargo fix
. This'll then apply as many fixes as it can (no user interaction), but also continue to emit normal compiler warnings about things that couldn't be fixed.
The second scenario is when we've actually released the edition itself. The only difference here is that instead of #![warn(..)]
all over the place you'll instead just say rust = '2018'
in your Cargo.toml
.
Rustfix is shaping up to be a pretty simple tool (yay!). It's largely just taking suggestions from the compiler and actually applying them on the filesystem, assuming the compiler is correctly informing rustfix of changes that need to be made.
The actual integration though is going to be pretty tricky. For a the best-quality experience we'll want to make sure that rustfix gracefully handles things like workspaces, multiple crates/targets in a workspace, cross-compilation, etc. I think that we can get all this done with a "hack" which has worked out quite well for rustbuild historically, override rustc
.
I'm envisioning a CLI that looks like this for rustfix:
# Works like `cargo check --all-targets`, fixes all warnings that `cargo check --all-targets`
# would otherwise produce. This involves updating libraries, binaries, examples,
# tests, etc.
$ cargo fix
# Fixes all warning that `cargo build` would otherwise produce
$ cargo fix build
# Fixes all warning that `cargo test` would otherwise produce
$ cargo test
# Fixes all warnings specifically for Windows
$ cargo fix --target x86_64-pc-windows-msvc
# Fixes all warnings for just one example and the library, if any:
$ cargo fix build --example foo
The general idea is that cargo fix
is the main entry point, and it otherwise mirrors Cargo's normal invocations. It will operate "as if" some other cargo command is executing, only a bunch of warnings are fixed along the way.
If a subcommand to fix
isn't specified it's assumed to be check
. If nothing is passed it's the special case check --all-targets
to fix as much as possible. Otherwise flags are all forwarded to cargo
subcommands as usual to execute various compilations.
This should have the benefit of working on workspaces, working on all targets, and hopefully even being future compatible with future Cargo features!
rustc
To actually implement the above interface I think we'll want to execute cargo
(the CLI) with the RUSTC
environment variable set to rustfix's binary itself. That way it'll get reexecuted and have control over the compilation.
In this way we've now provided a hook to all rustc compilations, allowing us to intercept error messages and such to see what's going on. I think the logic of the script will look like:
--emit metadata
mode (injected if not already present). All output is slurped up with --error-format=json
as well.And I think that's enough to basically get the feeling of "automatically fix all the code" while also assuming as little as possible about Cargo itself.
Some possible caveats are:
cargo fix
actually run over code that may need fixing.If we were to do this it's a pretty major rewrite/rethinking of the current CLI, so I'd like to get others' opinions on this! I'm curious if we can improve various aspects or otherwise make us better suited for the edition release!
I was working on a cargo project and I ran rustfix from the src
directory. This seemed to be working but ended with:
An error occured: I/O error
Io(Os { code: 2, kind: NotFound, message: "No such file or directory" })
Cause: Os { code: 2, kind: NotFound, message: "No such file or directory" }
Running from alongside the Cargo.toml
worked fine.
Can we fix 10k suggestions in a reasonable time?
Should cargo fix
keep going if the first cargo check
fails? That is, if we're looking at broken code, should cargo fix
attempt to keep going and fixing code?
In situations like this we can't actually detect if our changes caused further breakage, so we'd run the risk of making the situation worse by adding more errors.
Step 1 in the list in #2
I get
SubcommandError("cargo rustc", "error: extra arguments to `rustc` can only be passed to one target, consider filtering\nthe package by passing e.g. `--lib` or `--bin NAME` to specify a single target\n")
Should probably use cargo_metadata
to find all targets like cargo clippy
does
Let's collect some problems with rustfix encountered in real life here.
Right now cargo fix
is hardcoded to always run cargo check
. That should catch almost all warnings but it won't catch perhaps super-late codegen-time warnings. It may be worthwhile supporting something like cargo fix test
to give the guise of "just put fix
in there and all your worries are taken care of!"
If the cargo project we are in is a {git, hg, pijul, …} repo, we should check if the working tree is dirty. This way, it's much easier to track all the changes rustfix made.
If the tree is dirty, inform the user about it, and exit before applying any fixes. This should be overridable with an --allow-dirty
flag.
Also cf. #73
Would be nice to have a mode where:
We can hardcode the list of edition lints and idiom lints
We've got logic to automatically back out changes in this situation but we should also warn the user when this happens. Ideally we'd even say what the fixes were in the file with the errors and what the relevant code looked like, also ideally with links and instructions of how to report a bug to us.
If we don't manage to find anything like Git/Mercurial/SVN then we should display a warning in one way or another and maybe even wait for the user to confirm before proceeding. Automatically changing code without version control is a scary prospect!
Currently, it replaces the wrong bytes in my input file examples. I haven't investigated if this is an error in the JSON input (it blindly trusts byte_start
/byte_end
) or some mistake on my end.
cargo clippy -- -Z unstable-options --error-format json | rustfix --
cargo clippy --json | rustfix --interactive
cargo fix --with-clippy
The current thinking is that we'll want rustfix to be entirely automatic by default, but that doesn't mean that we don't also want some level of user-interactivity. What's the right amount of user interactivity, how's it enabled, and how should it work?
For each suggestion,
While entering letters to step through suggestions is fun, it might be cooler to have an interactive select box.
The node module inquirer does some cool stuff in that regard, e.g.
or
I think this is a great opportunity to create a new crate. The closest thing I could find in rust (without going all the way to curses-rs) is this snippet which uses termbox-bindings. termion, a pure-rust termbox clone, might also be interesting.
While it's nice to read something like
Suggestion - Replace:
mem::transmute::<*mut c_void,
&mut Box<FnMut(&MenuItem, &Window)>>(data)(&menu_item, &window);
with:
&mut *(data as *mut Box<for<'r, 'r> std::ops::FnMut(&'r menus::MenuItem, &'r windows::Window)>)
it might be more useful to have a bit of context and see exactly what is getting replaced. A diff-view (with character-precision highlighting, not just lines) should work nicely.
This could also be an optional CLI flag, though it might be better to just keep rustfix as simple (and non-configurable) as possible for now.
This might be as easy as adjusting the "apply replacement" function to not always save the output and run print_diff over a slice of max(0, start.line - context)...min(file.lines().count(), end.line + context
.
Test this to hell and back.
Right now we blindly forward all arguments to cargo fix
down to cargo check
, which means that it may be difficult for us to add arguments to cargo fix
itself. We'll want a story for how to add arguments to cargo fix
specifically and how to specifically add arguments to the underlying Cargo subcommand.
Currently, rustfix will explode (or wreck your source file to be more precise) when it applies multiply suggestions to the same file -- The file changes but we still use the original byte offsets given by rustc.
We could instead use a rope-like data structure and remember the original indices. Also, it might be nice to only write the file once, after all suggestions for a file were dealt with by the user (i.e., ≥ 0 were accepted).
replacing "" with "foo" should not display the original text and the new text like it's doing right now. instead it should point at the char where the text will be inserted and say something like Suggestion - Insert "foo" at ...
The opposite, removals are even easier, they can simply skip the with
part and just display the original snippet with the text that should be removed highlighted as it's already happening
Currently if you run cargo +nightly check
and then cargo +nightly fix
it won't actually do anything because Cargo will think it's cached. Somehow we should convince Cargo that it should at least rerun itself for the workspace-related crates.
The positioning of the replacement seems to be off.
Trying https://github.com/killercup/rustfix/tree/6937912bb89da024f6da89a35b2308f31af7663d on https://github.com/bluejekyll/trust-dns/tree/26b44483b0151682a7d835ddbd6ff9261c0651b6
Info: you should consider adding a `Default` implementation for `rr::resource::Record`
#[warn(new_without_default)] on by default
--> src/rr/resource.rs:78:3-78:3
Suggestion - Replace:
pub fn new() -> Record {
with:
impl Default for rr::resource::Record {
fn default() -> Self {
Self::new()
}
}
based on:
{
"message": "you should consider adding a `Default` implementation for `rr::resource::Record`, #[warn(new_without_default)] on by default",
"code": null,
"level": "warning",
"spans": [
{
"file_name": "src/rr/resource.rs",
"byte_start": 40920,
"byte_end": 41185,
"line_start": 78,
"line_end": 87,
"column_start": 3,
"column_end": 4,
"is_primary": true,
"text": [
{
"text": " pub fn new() -> Record {",
"highlight_start": 3,
"highlight_end": 27
},
{
"text": " Record {",
"highlight_start": 1,
"highlight_end": 13
},
{
"text": " // TODO: these really should all be Optionals, I was lazy.",
"highlight_start": 1,
"highlight_end": 65
},
{
"text": " name_labels: domain::Name::new(),",
"highlight_start": 1,
"highlight_end": 40
},
{
"text": " rr_type: RecordType::A,",
"highlight_start": 1,
"highlight_end": 30
},
{
"text": " dns_class: DNSClass::IN,",
"highlight_start": 1,
"highlight_end": 31
},
{
"text": " ttl: 0,",
"highlight_start": 1,
"highlight_end": 14
},
{
"text": " rdata: RData::NULL(NULL::new())",
"highlight_start": 1,
"highlight_end": 38
},
{
"text": " }",
"highlight_start": 1,
"highlight_end": 6
},
{
"text": " }",
"highlight_start": 1,
"highlight_end": 4
}
],
"label": null,
"suggested_replacement": null,
"expansion": null
}
],
"children": [
{
"message": "try this",
"code": null,
"level": "help",
"spans": [
{
"file_name": "src/rr/resource.rs",
"byte_start": 40920,
"byte_end": 40920,
"line_start": 78,
"line_end": 78,
"column_start": 3,
"column_end": 3,
"is_primary": true,
"text": [
{
"text": " pub fn new() -> Record {",
"highlight_start": 3,
"highlight_end": 3
}
],
"label": null,
"suggested_replacement": "impl Default for rr::resource::Record {\n fn default() -> Self {\n Self::new()\n }\n }\n\n ",
"expansion": null
}
],
"children": [],
"rendered": " impl Default for rr::resource::Record {\n fn default() -> Self {\n Self::new()\n }\n }\n\n pub fn new() -> Record {"
},
{
"message": "for further information visit https://github.com/Manishearth/rust-clippy/wiki#new_without_default",
"code": null,
"level": "help",
"spans": [],
"children": [],
"rendered": null
}
],
"rendered": null
}
does this:
@@ -75,6 +75,12 @@ impl Record {
///
/// There are no optional elements in this object, defaults are an empty name, type A, class IN,
/// ttl of 0 and the 0.0.0.0 ip address.
+ impl Default for rr::resource::Record {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+
pub fn new() -> Record {
Record {
// TODO: these really should all be Optionals, I was lazy.
And this:
Info: you should consider deriving a `Default` implementation for `rr::domain::Name`
#[warn(new_without_default_derive)] on by default
--> src/rr/domain.rs:37:3-37:3
Suggestion - Replace:
pub fn new() -> Self {
with:
#[derive(Default)]
based on:
{
"message": "you should consider deriving a `Default` implementation for `rr::domain::Name`, #[warn(new_without_default_derive)] on by default",
"code": null,
"level": "warning",
"spans": [
{
"file_name": "src/rr/domain.rs",
"byte_start": 95979,
"byte_end": 96046,
"line_start": 37,
"line_end": 39,
"column_start": 3,
"column_end": 4,
"is_primary": true,
"text": [
{
"text": " pub fn new() -> Self {",
"highlight_start": 3,
"highlight_end": 25
},
{
"text": " Name { labels: Rc::new(Vec::new()) }",
"highlight_start": 1,
"highlight_end": 41
},
{
"text": " }",
"highlight_start": 1,
"highlight_end": 4
}
],
"label": null,
"suggested_replacement": null,
"expansion": null
}
],
"children": [
{
"message": "try this",
"code": null,
"level": "help",
"spans": [
{
"file_name": "src/rr/domain.rs",
"byte_start": 95979,
"byte_end": 95979,
"line_start": 37,
"line_end": 37,
"column_start": 3,
"column_end": 3,
"is_primary": true,
"text": [
{
"text": " pub fn new() -> Self {",
"highlight_start": 3,
"highlight_end": 3
}
],
"label": null,
"suggested_replacement": "#[derive(Default)]\n ",
"expansion": null
}
],
"children": [],
"rendered": " #[derive(Default)]\n pub fn new() -> Self {"
},
{
"message": "for further information visit https://github.com/Manishearth/rust-clippy/wiki#new_without_default_derive",
"code": null,
"level": "help",
"spans": [],
"children": [],
"rendered": null
}
],
"rendered": null
}
does this:
@@ -34,6 +34,7 @@ pub struct Name {
}
impl Name {
+ #[derive(Default)]
pub fn new() -> Self {
Name { labels: Rc::new(Vec::new()) }
}
cc @mcarton
Not sure how that would look... probably create some mode that automatically applies suggestions and then compare the file out put and command line output.
Or maybe we can feed a file as command line input.
For example, recognize unused import warnings, and offer to delete the spans.
If two fixes occur in the same area of text, rustfix will apply both and it will clash.
--yolo
and normal mode that yolo-applies everything except when there's a clash (aside from the first fix). #94Currently cargo fix
will blindly apply fixes that it feels confident are the only way to fix something. This, with bugs, can cause code to stop compiling, however.
We should check that if a cargo check
succeeds before rustfix runs and then fails afterwards, all fixes are backed out and a large warning/error is presented to the user. Ideally this message we present even includes information like how to report a bug to the rustfix repository and what information to include.
It seems I have reintroduced a bug with regard to byte indices in module files.
Simple example: This change to rustfix itself
diff --git a/cargo-fix/src/cli.rs b/cargo-fix/src/cli.rs
index fbbdfe6..32a9bb4 100644
--- a/cargo-fix/src/cli.rs
+++ b/cargo-fix/src/cli.rs
@@ -107,6 +107,8 @@ fn log_for_human(kind: &str, msg: &str) -> Result<(), Error> {
let mut stream = StandardStream::stderr(color_choice);
stream.reset()?;
+ let mut i = 42;
+
stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Color::Cyan)))?;
// Justify to 12 chars just like cargo
write!(&mut stream, "{:>12}", kind)?;
leads to this output
Checking cargo-fix v0.2.0 (file:///Users/pascal/Projekte/rustfix/cargo-fix)
error: Could not find data slice that covers range 3823..3827
error: Could not compile `cargo-fix`.
That range looks an awful lot like the index in the whole crate's source, not in cli.rs
. I'm pretty sure there is such a property in the compiler's json output, though, and I'm just not using it correctly.
cf. #82 (comment)
I tried to update the rustfix using cargo and it's failing.
Error:
error: no method named `parameter_environment` found for type `rustc::ty::TyCtxt<'a, 'tcx, 'tcx>` in the current scope
--> /Users/ytr289/.cargo/registry/src/github.com-1ecc6299db9ec823/clippy_lints-0.0.134/src/utils/mod.rs:790:32
|
790 | let parameter_env = cx.tcx.parameter_environment(parameter_item);
| ^^^^^^^^^^^^^^^^^^^^^
error: no method named `parameter_environment` found for type `rustc::ty::TyCtxt<'a, 'tcx, 'tcx>` in the current scope
--> /Users/ytr289/.cargo/registry/src/github.com-1ecc6299db9ec823/clippy_lints-0.0.134/src/utils/mod.rs:810:22
|
810 | let env = cx.tcx.parameter_environment(env);
| ^^^^^^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> /Users/ytr289/.cargo/registry/src/github.com-1ecc6299db9ec823/clippy_lints-0.0.134/src/utils/mod.rs:811:69
|
811 | !ty.subst(cx.tcx, substs).moves_by_default(cx.tcx.global_tcx(), &env, DUMMY_SP)
| ^^^^ expected struct `rustc::ty::ParamEnv`, found reference
|
= note: expected type `rustc::ty::ParamEnv<'_>`
found type `&_`
error: no method named `parameter_environment` found for type `rustc::ty::TyCtxt<'a, 'tcx, 'tcx>` in the current scope
--> /Users/ytr289/.cargo/registry/src/github.com-1ecc6299db9ec823/clippy_lints-0.0.134/src/needless_pass_by_value.rs:85:40
|
85 | let parameter_env = cx.tcx.parameter_environment(fn_def_id);
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 4 previous errors
error: Could not compile `clippy_lints`.
Build failed, waiting for other jobs to finish...
error: failed to compile `clippy v0.0.134`, intermediate artifacts can be found at `/var/folders/zl/4vh8ccr57954fgw4c9xzpfrsyclf11/T/cargo-install.erha8y19M1vy`
Rust toolchain version:
rustup show
Default host: x86_64-apple-darwin
active toolchain
----------------
nightly-x86_64-apple-darwin (default)
rustc 1.19.0-nightly (5b13bff52 2017-05-23)
rustfix
idenfied an issue and suggested to replace self
with *self
to avoid &
in each match type.
Info: you don't need to add `&` to all patterns
--> decline_reason_code.rs:26:9-34:10
instead of prefixing all patterns with `&`, you can dereference the expression
[0]: Replace:
match self {
&DeclineReasonCode::UnsuccessfulDetok => "0390",
&DeclineReasonCode::TokenNotFound => "0391",
&DeclineReasonCode::AuthExpired => "0392",
&DeclineReasonCode::TokenNotActive => "0393",
&DeclineReasonCode::TokenExpired => "0394",
&DeclineReasonCode::None => "",
}
with:
match *self { .. }
==> What do you want to do? [0-9] | [r]eplace | [s]kip | save and [q]uit | [a]bort (without saving)
> r
Suggestion accepted. I'll remember that and apply it later.
Original Code:
pub fn as_str(&self) -> &'static str {
match self {
&DeclineReasonCode::UnsuccessfulDetok => "0390",
&DeclineReasonCode::TokenNotFound => "0391",
&DeclineReasonCode::AuthExpired => "0392",
&DeclineReasonCode::TokenNotActive => "0393",
&DeclineReasonCode::TokenExpired => "0394",
&DeclineReasonCode::None => "",
}
}
Replaced code after applying rustfix
suggestion:
pub fn as_str(&self) -> &'static str {
match *self { .. }
}
Currently \r\n I believe is rewritten as \n, we should preserve it!
I've written a test but it's currently failing.
I really like rustfix
and expectations are more :)
cargo build
identifies unused imports. I would like to see rustfix
provides similar suggestion and remove these unused imports.
e.g.
warning: unused import: `std::io::prelude::*`
--> src/tiso/tiso_msg.rs:10:5
|
10 | use std::io::prelude::*;
| ^^^^^^^^^^^^^^^^^^^
warning: unused import: `std::fmt`
--> src/tiso/tiso_msg.rs:13:5
|
13 | use std::fmt;
| ^^^^^^^^
Some lints yield multiple valid suggestions, e.g., here
fn main() {
let xs = vec![String::from("foo")];
let d: &Display = &xs;
println!("{}", d);
}
We should not try to apply suggestions with more than one option and instead just skip them.
After a bunch of times pressing r while running rustfix on trust-dns:
Suggestion accepted. I'll remember that and apply it later.
thread 'main' panicked at 'begin <= end (6 <= 0) when slicing ``', ../src/libcore/str/mod.rs:1692
stack backtrace:
1: 0x10d41db0b - std::sys::backtrace::tracing::imp::write::h29f5fdb9fc0a7395
2: 0x10d4230ca - std::panicking::default_hook::_$u7b$$u7b$closure$u7d$$u7d$::h2cc84f0378700526
3: 0x10d421788 - std::panicking::default_hook::hbbe7fa36a995aca0
4: 0x10d421e18 - std::panicking::rust_panic_with_hook::h105c3d42fcd2fb5e
5: 0x10d421c76 - std::panicking::begin_panic::hbf62ea4a5ff3f9de
6: 0x10d421b88 - std::panicking::begin_panic_fmt::h20f5943904e5791d
7: 0x10d421adf - rust_begin_unwind
8: 0x10d449150 - core::panicking::panic_fmt::h19323e466869c656
9: 0x10d44a07a - core::str::slice_error_fail::h0bcf68c8ce40ec27
10: 0x10d3aa2ff - rustfix::handle_suggestions::h78dd61d6c28e0b4e
11: 0x10d3a863c - rustfix::main::hecb56893e98700f4
12: 0x10d421a4d - std::panicking::try::call::h5df3ac2979db3c90
13: 0x10d4233eb - __rust_try
14: 0x10d4232a5 - __rust_maybe_catch_panic
15: 0x10d421101 - std::rt::lang_start::hfe9ab243c60ffb9b
Travis failed because the link to the clippy docs is no longer …/v0.0.177/…
but …/v0.0.180/…
. This is not a useful failure.
It should print the full failure so it can be diagnosed, instead of showing "No issues found"
rustfix is far more useful when it can use clippy's lints, so it might be a good idea to include clippy in rustfix itself.
Advantages:
cargo clippy
rustfix --clippy
(which most user would probably need to do otherwise)Disadvantages:
Unclear:
Alternatives:
--clippy
by default and tell people (with a nice error message) to install itcargo clippy
I think cargo allows us to
[[bin]]
name = "rustfix"
features = ["cli"]
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.