mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 14:52:01 +00:00
Web playground with WASM (#1279)
This commit is contained in:
parent
8c018e8261
commit
bdb1505262
31 changed files with 3104 additions and 402 deletions
38
.github/workflows/ci.yaml
vendored
38
.github/workflows/ci.yaml
vendored
|
@ -12,7 +12,7 @@ env:
|
||||||
RUSTUP_MAX_RETRIES: 10
|
RUSTUP_MAX_RETRIES: 10
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cargo_build:
|
cargo-build:
|
||||||
name: "cargo build"
|
name: "cargo build"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -45,8 +45,11 @@ jobs:
|
||||||
- run: ./target/release/ruff_dev generate-json-schema
|
- run: ./target/release/ruff_dev generate-json-schema
|
||||||
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. You may have to rerun 'cargo dev generate-json-schema'."
|
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. You may have to rerun 'cargo dev generate-json-schema'."
|
||||||
- run: git diff --exit-code -- ruff.schema.json
|
- run: git diff --exit-code -- ruff.schema.json
|
||||||
|
- run: ./target/release/ruff_dev generate-playground-options
|
||||||
|
- run: git diff --quiet playground/src/ruff_options.rs || echo "::error file=playground/src/ruff_options.ts::This file is outdated. You may have to rerun 'cargo dev generate-playground-options'."
|
||||||
|
- run: git diff --exit-code -- README.md src/checks_gen.rs playground/src/ruff_options.ts
|
||||||
|
|
||||||
cargo_fmt:
|
cargo-fmt:
|
||||||
name: "cargo fmt"
|
name: "cargo fmt"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -82,6 +85,7 @@ jobs:
|
||||||
toolchain: nightly-2022-11-01
|
toolchain: nightly-2022-11-01
|
||||||
override: true
|
override: true
|
||||||
components: clippy
|
components: clippy
|
||||||
|
target: wasm32-unknown-unknown
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v3
|
||||||
env:
|
env:
|
||||||
cache-name: cache-cargo
|
cache-name: cache-cargo
|
||||||
|
@ -95,8 +99,9 @@ jobs:
|
||||||
${{ runner.os }}-build-
|
${{ runner.os }}-build-
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic
|
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic
|
||||||
|
- run: cargo clippy --workspace --target wasm32-unknown-unknown --all-features -- -D warnings -W clippy::pedantic
|
||||||
|
|
||||||
cargo_test:
|
cargo-test:
|
||||||
name: "cargo test"
|
name: "cargo test"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -122,7 +127,32 @@ jobs:
|
||||||
- run: cargo test --all
|
- run: cargo test --all
|
||||||
- run: cargo test --package ruff --test black_compatibility_test -- --ignored
|
- run: cargo test --package ruff --test black_compatibility_test -- --ignored
|
||||||
|
|
||||||
maturin_build:
|
wasm-pack-test:
|
||||||
|
name: "wasm-pack test"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: nightly-2022-11-01
|
||||||
|
override: true
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
env:
|
||||||
|
cache-name: cache-cargo
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||||
|
${{ runner.os }}-build-
|
||||||
|
${{ runner.os }}-
|
||||||
|
- run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||||
|
- run: wasm-pack test --node
|
||||||
|
|
||||||
|
maturin-build:
|
||||||
name: "maturin build"
|
name: "maturin build"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
57
.github/workflows/playground.yaml
vendored
Normal file
57
.github/workflows/playground.yaml
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
name: Playground
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
CARGO_NET_RETRY: 10
|
||||||
|
RUSTUP_MAX_RETRIES: 10
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: nightly-2022-11-01
|
||||||
|
override: true
|
||||||
|
target: wasm32-unknown-unknown
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: playground/package-lock.json
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
env:
|
||||||
|
cache-name: cache-cargo
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||||
|
${{ runner.os }}-build-
|
||||||
|
${{ runner.os }}-
|
||||||
|
- name: "Install wasm-pack"
|
||||||
|
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||||
|
- name: "Run wasm-pack"
|
||||||
|
run: wasm-pack build --target web --out-dir playground/src/pkg
|
||||||
|
- name: "Install Node dependencies"
|
||||||
|
run: npm ci
|
||||||
|
working-directory: playground
|
||||||
|
- name: "Build JavaScript bundle"
|
||||||
|
run: npm run build
|
||||||
|
working-directory: playground
|
||||||
|
- name: "Deploy to Cloudflare Pages"
|
||||||
|
uses: cloudflare/wrangler-action@2.0.0
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||||
|
workingDirectory: playground
|
||||||
|
command: pages publish dist --project-name=ruff
|
236
Cargo.lock
generated
236
Cargo.lock
generated
|
@ -61,9 +61,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.66"
|
version = "1.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ascii"
|
name = "ascii"
|
||||||
|
@ -86,7 +86,7 @@ version = "2.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3"
|
checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bstr 1.0.1",
|
"bstr 1.1.0",
|
||||||
"doc-comment",
|
"doc-comment",
|
||||||
"predicates",
|
"predicates",
|
||||||
"predicates-core",
|
"predicates-core",
|
||||||
|
@ -160,9 +160,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd"
|
checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
@ -193,9 +193,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.77"
|
version = "1.0.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -280,9 +280,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.0.29"
|
version = "4.0.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -295,11 +295,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_complete"
|
name = "clap_complete"
|
||||||
version = "4.0.6"
|
version = "4.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7b3c9eae0de7bf8e3f904a5e40612b21fb2e2e566456d177809a48b892d24da"
|
checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 4.0.29",
|
"clap 4.0.32",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -308,7 +308,7 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
|
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 4.0.29",
|
"clap 4.0.32",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
"clap_complete_fig",
|
"clap_complete_fig",
|
||||||
]
|
]
|
||||||
|
@ -319,7 +319,7 @@ version = "4.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46b30e010e669cd021e5004f3be26cff6b7c08d2a8a0d65b48d43a8cc0efd6c3"
|
checksum = "46b30e010e669cd021e5004f3be26cff6b7c08d2a8a0d65b48d43a8cc0efd6c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 4.0.29",
|
"clap 4.0.32",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -422,6 +422,26 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console_error_panic_hook"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console_log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
|
@ -524,9 +544,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx"
|
name = "cxx"
|
||||||
version = "1.0.83"
|
version = "1.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf"
|
checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cxxbridge-flags",
|
"cxxbridge-flags",
|
||||||
|
@ -536,9 +556,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx-build"
|
name = "cxx-build"
|
||||||
version = "1.0.83"
|
version = "1.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39"
|
checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
|
@ -551,15 +571,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxxbridge-flags"
|
name = "cxxbridge-flags"
|
||||||
version = "1.0.83"
|
version = "1.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12"
|
checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxxbridge-macro"
|
name = "cxxbridge-macro"
|
||||||
version = "1.0.83"
|
version = "1.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6"
|
checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -712,9 +732,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetime"
|
name = "filetime"
|
||||||
version = "0.2.18"
|
version = "0.2.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3"
|
checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -733,7 +753,7 @@ name = "flake8-to-ruff"
|
||||||
version = "0.0.194-dev.0"
|
version = "0.0.194-dev.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap 4.0.29",
|
"clap 4.0.32",
|
||||||
"configparser",
|
"configparser",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -952,9 +972,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "insta"
|
name = "insta"
|
||||||
version = "1.22.0"
|
version = "1.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "197f4e300af8b23664d4077bf5c40e0afa9ba66a567bb5a51d3def3c7b287d1c"
|
checksum = "e48b08a091dfe5b09a6a9688c468fdd5b4396e92ce09e2eb932f0884b02788a4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"console",
|
"console",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
@ -985,9 +1005,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.2.6",
|
"hermit-abi 0.2.6",
|
||||||
"io-lifetimes",
|
"io-lifetimes",
|
||||||
|
@ -1006,9 +1026,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.4"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "joinery"
|
name = "joinery"
|
||||||
|
@ -1115,9 +1135,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.138"
|
version = "0.2.139"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libcst"
|
name = "libcst"
|
||||||
|
@ -1145,9 +1165,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "link-cplusplus"
|
name = "link-cplusplus"
|
||||||
version = "1.0.7"
|
version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
|
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
@ -1160,9 +1180,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.1.3"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
|
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
|
@ -1340,11 +1360,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
version = "1.14.0"
|
version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
|
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.1.19",
|
"hermit-abi 0.2.6",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1391,9 +1411,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.9"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "path-absolutize"
|
name = "path-absolutize"
|
||||||
|
@ -1631,9 +1651,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.47"
|
version = "1.0.49"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -1663,9 +1683,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.21"
|
version = "1.0.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -1759,11 +1779,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
|
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-deque",
|
|
||||||
"either",
|
"either",
|
||||||
"rayon-core",
|
"rayon-core",
|
||||||
]
|
]
|
||||||
|
@ -1868,12 +1887,15 @@ dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cachedir",
|
"cachedir",
|
||||||
|
"cfg-if 1.0.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap 4.0.29",
|
"clap 4.0.32",
|
||||||
"clap_complete_command",
|
"clap_complete_command",
|
||||||
"clearscreen",
|
"clearscreen",
|
||||||
"colored",
|
"colored",
|
||||||
"common-path",
|
"common-path",
|
||||||
|
"console_error_panic_hook",
|
||||||
|
"console_log",
|
||||||
"criterion",
|
"criterion",
|
||||||
"dirs 4.0.0",
|
"dirs 4.0.0",
|
||||||
"fern",
|
"fern",
|
||||||
|
@ -1884,6 +1906,7 @@ dependencies = [
|
||||||
"ignore",
|
"ignore",
|
||||||
"insta",
|
"insta",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
"js-sys",
|
||||||
"libcst",
|
"libcst",
|
||||||
"log",
|
"log",
|
||||||
"natord",
|
"natord",
|
||||||
|
@ -1904,6 +1927,7 @@ dependencies = [
|
||||||
"schemars",
|
"schemars",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde-wasm-bindgen",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
"strum",
|
"strum",
|
||||||
|
@ -1915,6 +1939,8 @@ dependencies = [
|
||||||
"update-informer",
|
"update-informer",
|
||||||
"ureq",
|
"ureq",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-test",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1922,7 +1948,7 @@ name = "ruff_dev"
|
||||||
version = "0.0.194"
|
version = "0.0.194"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap 4.0.29",
|
"clap 4.0.32",
|
||||||
"codegen",
|
"codegen",
|
||||||
"itertools",
|
"itertools",
|
||||||
"libcst",
|
"libcst",
|
||||||
|
@ -1935,6 +1961,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
|
"textwrap",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1955,9 +1982,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.36.4"
|
version = "0.36.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23"
|
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
|
@ -2056,15 +2083,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.9"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
|
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.11"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
|
@ -2099,6 +2126,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -2107,9 +2140,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scratch"
|
name = "scratch"
|
||||||
version = "1.0.2"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
|
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sct"
|
name = "sct"
|
||||||
|
@ -2129,18 +2162,29 @@ checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.148"
|
version = "1.0.151"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
|
checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde-wasm-bindgen"
|
||||||
version = "1.0.148"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
|
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.151"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2216,9 +2260,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "str_indices"
|
name = "str_indices"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d9199fa80c817e074620be84374a520062ebac833f358d74b37060ce4a0f2c0"
|
checksum = "5f026164926842ec52deb1938fae44f83dfdb82d0a5b0270c5bd5935ab74d6dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "string_cache"
|
name = "string_cache"
|
||||||
|
@ -2263,9 +2307,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.105"
|
version = "1.0.107"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2370,18 +2414,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.37"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.37"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2455,9 +2499,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.5.9"
|
version = "0.5.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
|
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -2544,9 +2588,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.5"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-linebreak"
|
name = "unicode-linebreak"
|
||||||
|
@ -2715,6 +2759,18 @@ dependencies = [
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-futures"
|
||||||
|
version = "0.4.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.83"
|
version = "0.2.83"
|
||||||
|
@ -2744,6 +2800,30 @@ version = "0.2.83"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-test"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09d2fff962180c3fadf677438054b1db62bee4aa32af26a45388af07d1287e1d"
|
||||||
|
dependencies = [
|
||||||
|
"console_error_panic_hook",
|
||||||
|
"js-sys",
|
||||||
|
"scoped-tls",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"wasm-bindgen-test-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-test-macro"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4683da3dfc016f704c9f82cf401520c4f1cb3ee440f7f52b3d6ac29506a49ca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.60"
|
version = "0.3.60"
|
||||||
|
@ -2766,9 +2846,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.22.5"
|
version = "0.22.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
|
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"webpki",
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
27
Cargo.toml
27
Cargo.toml
|
@ -12,6 +12,7 @@ rust-version = "1.65.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||||
|
@ -20,9 +21,10 @@ atty = { version = "0.2.14" }
|
||||||
bincode = { version = "1.3.3" }
|
bincode = { version = "1.3.3" }
|
||||||
bitflags = { version = "1.3.2" }
|
bitflags = { version = "1.3.2" }
|
||||||
cachedir = { version = "0.3.0" }
|
cachedir = { version = "0.3.0" }
|
||||||
|
cfg-if = { version = "1.0.0" }
|
||||||
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
|
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
|
||||||
clap = { version = "4.0.1", features = ["derive"] }
|
clap = { version = "4.0.1", features = ["derive"] }
|
||||||
clap_complete_command = "0.4.0"
|
clap_complete_command = { version = "0.4.0" }
|
||||||
colored = { version = "2.0.0" }
|
colored = { version = "2.0.0" }
|
||||||
common-path = { version = "1.0.0" }
|
common-path = { version = "1.0.0" }
|
||||||
dirs = { version = "4.0.0" }
|
dirs = { version = "4.0.0" }
|
||||||
|
@ -41,7 +43,6 @@ num-bigint = { version = "0.4.3" }
|
||||||
once_cell = { version = "1.16.0" }
|
once_cell = { version = "1.16.0" }
|
||||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||||
quick-junit = { version = "0.3.2" }
|
quick-junit = { version = "0.3.2" }
|
||||||
rayon = { version = "1.5.3" }
|
|
||||||
regex = { version = "1.6.0" }
|
regex = { version = "1.6.0" }
|
||||||
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
|
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
|
||||||
ruff_macros = { version = "0.0.194", path = "ruff_macros" }
|
ruff_macros = { version = "0.0.194", path = "ruff_macros" }
|
||||||
|
@ -59,23 +60,32 @@ strum_macros = { version = "0.24.3" }
|
||||||
textwrap = { version = "0.16.0" }
|
textwrap = { version = "0.16.0" }
|
||||||
titlecase = { version = "2.2.1" }
|
titlecase = { version = "2.2.1" }
|
||||||
toml = { version = "0.5.9" }
|
toml = { version = "0.5.9" }
|
||||||
update-informer = { version = "0.5.0", default-features = false, features = ["pypi"], optional = true }
|
|
||||||
walkdir = { version = "2.3.2" }
|
walkdir = { version = "2.3.2" }
|
||||||
|
|
||||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||||
clearscreen = { version = "1.0.10" } # uses which
|
clearscreen = { version = "1.0.10" }
|
||||||
|
rayon = { version = "1.5.3" }
|
||||||
|
update-informer = { version = "0.5.0", default-features = false, features = ["pypi"], optional = true }
|
||||||
|
|
||||||
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
|
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
|
||||||
# For (future) wasm-pack support
|
# For (future) wasm-pack support
|
||||||
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
|
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
|
||||||
getrandom = { version = "0.2.7", features = ["js"] }
|
getrandom = { version = "0.2.7", features = ["js"] }
|
||||||
|
console_error_panic_hook = { version = "0.1.7" }
|
||||||
|
console_log = { version = "0.2.0" }
|
||||||
|
serde-wasm-bindgen = { version = "0.4" }
|
||||||
|
js-sys = { version = "0.3.60" }
|
||||||
|
wasm-bindgen = { version = "0.2.83" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = { version = "2.0.4" }
|
|
||||||
criterion = { version = "0.4.0" }
|
|
||||||
insta = { version = "1.19.1", features = ["yaml"] }
|
insta = { version = "1.19.1", features = ["yaml"] }
|
||||||
test-case = { version = "2.2.2" }
|
test-case = { version = "2.2.2" }
|
||||||
ureq = { version = "2.5.0", features = [] }
|
ureq = { version = "2.5.0", features = [] }
|
||||||
|
wasm-bindgen-test = { version = "0.3.33" }
|
||||||
|
|
||||||
|
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
|
||||||
|
assert_cmd = { version = "2.0.4" }
|
||||||
|
criterion = { version = "0.4.0" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["update-informer"]
|
default = ["update-informer"]
|
||||||
|
@ -93,6 +103,11 @@ opt-level = 3
|
||||||
[profile.dev.package.similar]
|
[profile.dev.package.similar]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
|
# Reduce complexity of a parser function that would trigger a locals limit in a wasm tool.
|
||||||
|
# https://github.com/bytecodealliance/wasm-tools/blob/b5c3d98e40590512a3b12470ef358d5c7b983b15/crates/wasmparser/src/limits.rs#L29
|
||||||
|
[profile.dev.package.rustpython-parser]
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "source_code_locator"
|
name = "source_code_locator"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
24
playground/.gitignore
vendored
Normal file
24
playground/.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
12
playground/index.html
Normal file
12
playground/index.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Ruff Playground</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1517
playground/package-lock.json
generated
Normal file
1517
playground/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
27
playground/package.json
Normal file
27
playground/package.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "playground",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"push": "wrangler pages publish dist --project-name=ruff"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@monaco-editor/react": "^4.4.6",
|
||||||
|
"lz-string": "^1.4.4",
|
||||||
|
"monaco-editor": "^0.34.1",
|
||||||
|
"prettier": "^2.8.1",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.26",
|
||||||
|
"@types/react-dom": "^18.0.9",
|
||||||
|
"@vitejs/plugin-react-swc": "^3.0.0",
|
||||||
|
"typescript": "^4.9.3",
|
||||||
|
"vite": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
141
playground/src/App.tsx
Normal file
141
playground/src/App.tsx
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
import lzstring from "lz-string";
|
||||||
|
import Editor, { useMonaco } from "@monaco-editor/react";
|
||||||
|
import { MarkerSeverity } from "monaco-editor/esm/vs/editor/editor.api";
|
||||||
|
import { useEffect, useState, useCallback } from "react";
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import init, { Check, check } from "./pkg/ruff.js";
|
||||||
|
import { AVAILABLE_OPTIONS } from "./ruff_options";
|
||||||
|
import { Config, getDefaultConfig, toRuffConfig } from "./config";
|
||||||
|
import { Options } from "./Options";
|
||||||
|
|
||||||
|
const DEFAULT_SOURCE = "print(1 + 2)";
|
||||||
|
|
||||||
|
function restoreConfigAndSource(): [Config, string] {
|
||||||
|
let value = lzstring.decompressFromEncodedURIComponent(
|
||||||
|
window.location.hash.slice(1)
|
||||||
|
);
|
||||||
|
let config = {};
|
||||||
|
let source = DEFAULT_SOURCE;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
let parts = value.split("$$$");
|
||||||
|
config = JSON.parse(parts[0]);
|
||||||
|
source = parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [config, source];
|
||||||
|
}
|
||||||
|
|
||||||
|
function persistConfigAndSource(config: Config, source: string) {
|
||||||
|
window.location.hash = lzstring.compressToEncodedURIComponent(
|
||||||
|
JSON.stringify(config) + "$$$" + source
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultConfig = getDefaultConfig(AVAILABLE_OPTIONS);
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const monaco = useMonaco();
|
||||||
|
const [ruffInitialized, setRuffInitialized] = useState<boolean>(false);
|
||||||
|
const [config, setConfig] = useState<Config | null>(null);
|
||||||
|
const [source, setSource] = useState<string | null>(null);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
init().then(() => setRuffInitialized(true));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (source === null && config === null && monaco) {
|
||||||
|
let [config, source] = restoreConfigAndSource();
|
||||||
|
setConfig(config);
|
||||||
|
setSource(source);
|
||||||
|
}
|
||||||
|
}, [monaco, source, config]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (config != null && source != null) {
|
||||||
|
persistConfigAndSource(config, source);
|
||||||
|
}
|
||||||
|
}, [config, source]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let editor = monaco?.editor;
|
||||||
|
let model = editor?.getModels()[0];
|
||||||
|
if (
|
||||||
|
!editor ||
|
||||||
|
!model ||
|
||||||
|
!ruffInitialized ||
|
||||||
|
source === null ||
|
||||||
|
config === null
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let checks: Check[];
|
||||||
|
try {
|
||||||
|
checks = check(source, toRuffConfig(config));
|
||||||
|
setError(null);
|
||||||
|
} catch (e) {
|
||||||
|
setError(String(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.setModelMarkers(
|
||||||
|
model,
|
||||||
|
"owner",
|
||||||
|
checks.map((check) => ({
|
||||||
|
startLineNumber: check.location.row,
|
||||||
|
startColumn: check.location.column + 1,
|
||||||
|
endLineNumber: check.end_location.row,
|
||||||
|
endColumn: check.end_location.column + 1,
|
||||||
|
message: `${check.code}: ${check.message}`,
|
||||||
|
severity: MarkerSeverity.Error,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}, [config, source, monaco, ruffInitialized]);
|
||||||
|
|
||||||
|
const handleEditorChange = useCallback(
|
||||||
|
(value: string | undefined) => {
|
||||||
|
value && setSource(value);
|
||||||
|
},
|
||||||
|
[setSource]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleOptionChange = useCallback(
|
||||||
|
(groupName: string, fieldName: string, value: string) => {
|
||||||
|
let group = Object.assign({}, (config || {})[groupName]);
|
||||||
|
if (value === defaultConfig[groupName][fieldName] || value === "") {
|
||||||
|
delete group[fieldName];
|
||||||
|
} else {
|
||||||
|
group[fieldName] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfig({
|
||||||
|
...config,
|
||||||
|
[groupName]: group,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[config]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id="app">
|
||||||
|
<Options
|
||||||
|
config={config}
|
||||||
|
defaultConfig={defaultConfig}
|
||||||
|
onChange={handleOptionChange}
|
||||||
|
/>
|
||||||
|
<Editor
|
||||||
|
options={{ readOnly: false, minimap: { enabled: false } }}
|
||||||
|
wrapperProps={{ className: "editor" }}
|
||||||
|
defaultLanguage="python"
|
||||||
|
value={source || ""}
|
||||||
|
theme={"light"}
|
||||||
|
onChange={handleEditorChange}
|
||||||
|
/>
|
||||||
|
{error && <div id="error">{error}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
72
playground/src/Options.tsx
Normal file
72
playground/src/Options.tsx
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import { Config } from "./config";
|
||||||
|
import { AVAILABLE_OPTIONS } from "./ruff_options";
|
||||||
|
|
||||||
|
function OptionEntry({
|
||||||
|
config,
|
||||||
|
defaultConfig,
|
||||||
|
groupName,
|
||||||
|
fieldName,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
config: Config | null;
|
||||||
|
defaultConfig: Config;
|
||||||
|
groupName: string;
|
||||||
|
fieldName: string;
|
||||||
|
onChange: (groupName: string, fieldName: string, value: string) => void;
|
||||||
|
}) {
|
||||||
|
const value =
|
||||||
|
config && config[groupName] && config[groupName][fieldName]
|
||||||
|
? config[groupName][fieldName]
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<label>
|
||||||
|
{fieldName}
|
||||||
|
<input
|
||||||
|
value={value}
|
||||||
|
placeholder={defaultConfig[groupName][fieldName]}
|
||||||
|
type="text"
|
||||||
|
onChange={(event) => {
|
||||||
|
onChange(groupName, fieldName, event.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Options({
|
||||||
|
config,
|
||||||
|
defaultConfig,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
config: Config | null;
|
||||||
|
defaultConfig: Config;
|
||||||
|
onChange: (groupName: string, fieldName: string, value: string) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="options">
|
||||||
|
{AVAILABLE_OPTIONS.map((group) => (
|
||||||
|
<details key={group.name}>
|
||||||
|
<summary>{group.name}</summary>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
{group.fields.map((field) => (
|
||||||
|
<li key={field.name}>
|
||||||
|
<OptionEntry
|
||||||
|
config={config}
|
||||||
|
defaultConfig={defaultConfig}
|
||||||
|
groupName={group.name}
|
||||||
|
fieldName={field.name}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
52
playground/src/config.ts
Normal file
52
playground/src/config.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { OptionGroup } from "./ruff_options";
|
||||||
|
|
||||||
|
export type Config = { [key: string]: { [key: string]: string } };
|
||||||
|
|
||||||
|
export function getDefaultConfig(availableOptions: OptionGroup[]): Config {
|
||||||
|
const config: Config = {};
|
||||||
|
availableOptions.forEach((group) => {
|
||||||
|
config[group.name] = {};
|
||||||
|
group.fields.forEach((f) => {
|
||||||
|
config[group.name][f.name] = f.default;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the config in the application to something Ruff accepts.
|
||||||
|
*
|
||||||
|
* Application config is always nested one level. Ruff allows for some
|
||||||
|
* top-level options.
|
||||||
|
*
|
||||||
|
* Any option value is parsed as JSON to convert it to a native JS object.
|
||||||
|
* If that fails, e.g. while a user is typing, we let the application handle that
|
||||||
|
* and show an error.
|
||||||
|
*/
|
||||||
|
export function toRuffConfig(config: Config): any {
|
||||||
|
const convertValue = (value: string): any => {
|
||||||
|
return value === "None" ? null : JSON.parse(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
let result: any = {};
|
||||||
|
Object.keys(config).forEach((group_name) => {
|
||||||
|
let fields = config[group_name];
|
||||||
|
if (!fields || Object.keys(fields).length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group_name === "globals") {
|
||||||
|
Object.keys(fields).forEach((field_name) => {
|
||||||
|
result[field_name] = convertValue(fields[field_name]);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result[group_name] = {};
|
||||||
|
|
||||||
|
Object.keys(fields).forEach((field_name) => {
|
||||||
|
result[group_name][field_name] = convertValue(fields[field_name]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
6
playground/src/custom.d.ts
vendored
Normal file
6
playground/src/custom.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
declare module "lz-string" {
|
||||||
|
function decompressFromEncodedURIComponent(
|
||||||
|
input: string | null
|
||||||
|
): string | null;
|
||||||
|
function compressToEncodedURIComponent(input: string | null): string;
|
||||||
|
}
|
10
playground/src/main.tsx
Normal file
10
playground/src/main.tsx
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import App from "./App";
|
||||||
|
import "./style.css";
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
222
playground/src/ruff_options.ts
Normal file
222
playground/src/ruff_options.ts
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
|
||||||
|
// This file is auto-generated by `cargo dev generate-playground-options`.
|
||||||
|
export interface OptionGroup {
|
||||||
|
name: string;
|
||||||
|
fields: {
|
||||||
|
name: string;
|
||||||
|
default: string;
|
||||||
|
type: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AVAILABLE_OPTIONS: OptionGroup[] = [
|
||||||
|
{"name": "globals", "fields": [
|
||||||
|
{
|
||||||
|
"name": "allowed-confusables",
|
||||||
|
"default": '[]',
|
||||||
|
"type": 'Vec<char>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dummy-variable-rgx",
|
||||||
|
"default": '"^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"',
|
||||||
|
"type": 'Regex',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "extend-ignore",
|
||||||
|
"default": '[]',
|
||||||
|
"type": 'Vec<CheckCodePrefix>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "extend-select",
|
||||||
|
"default": '[]',
|
||||||
|
"type": 'Vec<CheckCodePrefix>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "external",
|
||||||
|
"default": '[]',
|
||||||
|
"type": 'Vec<String>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fix-only",
|
||||||
|
"default": 'false',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ignore",
|
||||||
|
"default": '[]',
|
||||||
|
"type": 'Vec<CheckCodePrefix>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "line-length",
|
||||||
|
"default": '88',
|
||||||
|
"type": 'usize',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "required-version",
|
||||||
|
"default": 'None',
|
||||||
|
"type": 'String',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "select",
|
||||||
|
"default": '["E", "F"]',
|
||||||
|
"type": 'Vec<CheckCodePrefix>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "target-version",
|
||||||
|
"default": '"py310"',
|
||||||
|
"type": 'PythonVersion',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "unfixable",
|
||||||
|
"default": '[]',
|
||||||
|
"type": 'Vec<CheckCodePrefix>',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "flake8-annotations", "fields": [
|
||||||
|
{
|
||||||
|
"name": "allow-star-arg-any",
|
||||||
|
"default": 'false',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mypy-init-return",
|
||||||
|
"default": 'false',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "suppress-dummy-args",
|
||||||
|
"default": 'false',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "suppress-none-returning",
|
||||||
|
"default": 'false',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "flake8-bugbear", "fields": [
|
||||||
|
{
|
||||||
|
"name": "extend-immutable-calls",
|
||||||
|
"default": '[]',
|
||||||
|
"type": 'Vec<String>',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "flake8-errmsg", "fields": [
|
||||||
|
{
|
||||||
|
"name": "max-string-length",
|
||||||
|
"default": '0',
|
||||||
|
"type": 'usize',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "flake8-import-conventions", "fields": [
|
||||||
|
{
|
||||||
|
"name": "aliases",
|
||||||
|
"default": '{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}',
|
||||||
|
"type": 'FxHashMap<String, String>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "extend-aliases",
|
||||||
|
"default": '{}',
|
||||||
|
"type": 'FxHashMap<String, String>',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "flake8-quotes", "fields": [
|
||||||
|
{
|
||||||
|
"name": "avoid-escape",
|
||||||
|
"default": 'true',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "docstring-quotes",
|
||||||
|
"default": '"double"',
|
||||||
|
"type": 'Quote',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "inline-quotes",
|
||||||
|
"default": '"double"',
|
||||||
|
"type": 'Quote',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "multiline-quotes",
|
||||||
|
"default": '"double"',
|
||||||
|
"type": 'Quote',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "flake8-tidy-imports", "fields": [
|
||||||
|
{
|
||||||
|
"name": "ban-relative-imports",
|
||||||
|
"default": '"parents"',
|
||||||
|
"type": 'Strictness',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "flake8-unused-arguments", "fields": [
|
||||||
|
{
|
||||||
|
"name": "ignore-variadic-names",
|
||||||
|
"default": 'false',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "isort", "fields": [
|
||||||
|
{
|
||||||
|
"name": "combine-as-imports",
|
||||||
|
"default": 'false',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "extra-standard-library",
|
||||||
|
"default": '[]',
|
||||||
|
"type": 'Vec<String>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "force-wrap-aliases",
|
||||||
|
"default": 'false',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "known-first-party",
|
||||||
|
"default": '[]',
|
||||||
|
"type": 'Vec<String>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "known-third-party",
|
||||||
|
"default": '[]',
|
||||||
|
"type": 'Vec<String>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "split-on-trailing-comma",
|
||||||
|
"default": 'true',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "mccabe", "fields": [
|
||||||
|
{
|
||||||
|
"name": "max-complexity",
|
||||||
|
"default": '10',
|
||||||
|
"type": 'usize',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "pep8-naming", "fields": [
|
||||||
|
{
|
||||||
|
"name": "classmethod-decorators",
|
||||||
|
"default": '["classmethod"]',
|
||||||
|
"type": 'Vec<String>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ignore-names",
|
||||||
|
"default": '["setUp", "tearDown", "setUpClass", "tearDownClass", "setUpModule", "tearDownModule", "asyncSetUp", "asyncTearDown", "setUpTestData", "failureException", "longMessage", "maxDiff"]',
|
||||||
|
"type": 'Vec<String>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "staticmethod-decorators",
|
||||||
|
"default": '["staticmethod"]',
|
||||||
|
"type": 'Vec<String>',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
{"name": "pyupgrade", "fields": [
|
||||||
|
{
|
||||||
|
"name": "keep-runtime-typing",
|
||||||
|
"default": 'false',
|
||||||
|
"type": 'bool',
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
];
|
60
playground/src/style.css
Normal file
60
playground/src/style.css
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
html,
|
||||||
|
#root,
|
||||||
|
#app {
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
height: 100vh;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding: 1em;
|
||||||
|
min-width: 300px;
|
||||||
|
border-right: 1px solid lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options ul {
|
||||||
|
padding-left: 1em;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options li {
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options details {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options summary {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#error {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 1em;
|
||||||
|
padding: 1em;
|
||||||
|
background: darkred;
|
||||||
|
color: white;
|
||||||
|
}
|
1
playground/src/vite-env.d.ts
vendored
Normal file
1
playground/src/vite-env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/// <reference types="vite/client" />
|
21
playground/tsconfig.json
Normal file
21
playground/tsconfig.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
|
"allowJs": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": false,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
9
playground/tsconfig.node.json
Normal file
9
playground/tsconfig.node.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
7
playground/vite.config.ts
Normal file
7
playground/vite.config.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react-swc";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
});
|
|
@ -18,3 +18,4 @@ schemars = { version = "0.8.11" }
|
||||||
serde_json = {version="1.0.91"}
|
serde_json = {version="1.0.91"}
|
||||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||||
strum_macros = { version = "0.24.3" }
|
strum_macros = { version = "0.24.3" }
|
||||||
|
textwrap = { version = "0.16.0" }
|
||||||
|
|
142
ruff_dev/src/generate_playground_options.rs
Normal file
142
ruff_dev/src/generate_playground_options.rs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
//! Generate typescript file defining options to be used by the web playground.
|
||||||
|
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Args;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use ruff::settings::options::Options;
|
||||||
|
use ruff::settings::options_base::{ConfigurationOptions, OptionEntry, OptionField};
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct Cli {
|
||||||
|
/// Write the generated table to stdout (rather than to `TODO`).
|
||||||
|
#[arg(long)]
|
||||||
|
dry_run: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_field(output: &mut String, field: &OptionField) {
|
||||||
|
output.push_str(&textwrap::indent(
|
||||||
|
&textwrap::dedent(&format!(
|
||||||
|
"
|
||||||
|
{{
|
||||||
|
\"name\": \"{}\",
|
||||||
|
\"default\": '{}',
|
||||||
|
\"type\": '{}',
|
||||||
|
}},",
|
||||||
|
field.name, field.default, field.value_type
|
||||||
|
)),
|
||||||
|
" ",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main(cli: &Cli) -> Result<()> {
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
|
// Generate all the top-level fields.
|
||||||
|
output.push_str(&format!("{{\"name\": \"{}\", \"fields\": [", "globals"));
|
||||||
|
for field in Options::get_available_options()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|entry| {
|
||||||
|
if let OptionEntry::Field(field) = entry {
|
||||||
|
Some(field)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Filter out options that don't make sense in the playground.
|
||||||
|
.filter(|field| {
|
||||||
|
!matches!(
|
||||||
|
field.name,
|
||||||
|
"src"
|
||||||
|
| "fix"
|
||||||
|
| "format"
|
||||||
|
| "exclude"
|
||||||
|
| "extend"
|
||||||
|
| "extend-exclude"
|
||||||
|
| "fixable"
|
||||||
|
| "force-exclude"
|
||||||
|
| "ignore-init-module-imports"
|
||||||
|
| "respect-gitignore"
|
||||||
|
| "show-source"
|
||||||
|
| "cache-dir"
|
||||||
|
| "per-file-ignores"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sorted_by_key(|field| field.name)
|
||||||
|
{
|
||||||
|
emit_field(&mut output, &field);
|
||||||
|
}
|
||||||
|
output.push_str("\n]},\n");
|
||||||
|
|
||||||
|
// Generate all the sub-groups.
|
||||||
|
for group in Options::get_available_options()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|entry| {
|
||||||
|
if let OptionEntry::Group(group) = entry {
|
||||||
|
Some(group)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sorted_by_key(|group| group.name)
|
||||||
|
{
|
||||||
|
output.push_str(&format!("{{\"name\": \"{}\", \"fields\": [", group.name));
|
||||||
|
for field in group
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.filter_map(|entry| {
|
||||||
|
if let OptionEntry::Field(field) = entry {
|
||||||
|
Some(field)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sorted_by_key(|field| field.name)
|
||||||
|
{
|
||||||
|
emit_field(&mut output, field);
|
||||||
|
}
|
||||||
|
output.push_str("\n]},\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
let prefix = textwrap::dedent(
|
||||||
|
r"
|
||||||
|
// This file is auto-generated by `cargo dev generate-playground-options`.
|
||||||
|
export interface OptionGroup {
|
||||||
|
name: string;
|
||||||
|
fields: {
|
||||||
|
name: string;
|
||||||
|
default: string;
|
||||||
|
type: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AVAILABLE_OPTIONS: OptionGroup[] = [
|
||||||
|
",
|
||||||
|
);
|
||||||
|
let postfix = "];";
|
||||||
|
|
||||||
|
if cli.dry_run {
|
||||||
|
print!("{output}");
|
||||||
|
} else {
|
||||||
|
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||||
|
.parent()
|
||||||
|
.expect("Failed to find root directory")
|
||||||
|
.join("playground")
|
||||||
|
.join("src")
|
||||||
|
.join("ruff_options.ts");
|
||||||
|
|
||||||
|
let mut f = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(file)?;
|
||||||
|
write!(f, "{prefix}")?;
|
||||||
|
write!(f, "{}", textwrap::indent(&output, " "))?;
|
||||||
|
write!(f, "{postfix}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
pub mod generate_check_code_prefix;
|
pub mod generate_check_code_prefix;
|
||||||
pub mod generate_json_schema;
|
pub mod generate_json_schema;
|
||||||
pub mod generate_options;
|
pub mod generate_options;
|
||||||
|
pub mod generate_playground_options;
|
||||||
pub mod generate_rules_table;
|
pub mod generate_rules_table;
|
||||||
pub mod generate_source_code;
|
pub mod generate_source_code;
|
||||||
pub mod print_ast;
|
pub mod print_ast;
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use ruff_dev::{
|
use ruff_dev::{
|
||||||
generate_check_code_prefix, generate_json_schema, generate_options, generate_rules_table,
|
generate_check_code_prefix, generate_json_schema, generate_options,
|
||||||
generate_source_code, print_ast, print_cst, print_tokens,
|
generate_playground_options, generate_rules_table, generate_source_code, print_ast, print_cst,
|
||||||
|
print_tokens,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
@ -36,6 +37,9 @@ enum Commands {
|
||||||
GenerateRulesTable(generate_rules_table::Cli),
|
GenerateRulesTable(generate_rules_table::Cli),
|
||||||
/// Generate a Markdown-compatible listing of configuration options.
|
/// Generate a Markdown-compatible listing of configuration options.
|
||||||
GenerateOptions(generate_options::Cli),
|
GenerateOptions(generate_options::Cli),
|
||||||
|
/// Generate typescript file defining options to be used by the web
|
||||||
|
/// playground.
|
||||||
|
GeneratePlaygroundOptions(generate_playground_options::Cli),
|
||||||
/// Run round-trip source code generation on a given Python file.
|
/// Run round-trip source code generation on a given Python file.
|
||||||
GenerateSourceCode(generate_source_code::Cli),
|
GenerateSourceCode(generate_source_code::Cli),
|
||||||
/// Print the AST for a given Python file.
|
/// Print the AST for a given Python file.
|
||||||
|
@ -54,6 +58,7 @@ fn main() -> Result<()> {
|
||||||
Commands::GenerateRulesTable(args) => generate_rules_table::main(args)?,
|
Commands::GenerateRulesTable(args) => generate_rules_table::main(args)?,
|
||||||
Commands::GenerateSourceCode(args) => generate_source_code::main(args)?,
|
Commands::GenerateSourceCode(args) => generate_source_code::main(args)?,
|
||||||
Commands::GenerateOptions(args) => generate_options::main(args)?,
|
Commands::GenerateOptions(args) => generate_options::main(args)?,
|
||||||
|
Commands::GeneratePlaygroundOptions(args) => generate_playground_options::main(args)?,
|
||||||
Commands::PrintAST(args) => print_ast::main(args)?,
|
Commands::PrintAST(args) => print_ast::main(args)?,
|
||||||
Commands::PrintCST(args) => print_cst::main(args)?,
|
Commands::PrintCST(args) => print_cst::main(args)?,
|
||||||
Commands::PrintTokens(args) => print_tokens::main(args)?,
|
Commands::PrintTokens(args) => print_tokens::main(args)?,
|
||||||
|
|
75
src/lib.rs
75
src/lib.rs
|
@ -11,19 +11,10 @@
|
||||||
clippy::too_many_lines
|
clippy::too_many_lines
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use std::path::Path;
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use path_absolutize::path_dedot;
|
|
||||||
use rustpython_helpers::tokenize;
|
|
||||||
use rustpython_parser::lexer::LexResult;
|
|
||||||
use settings::{pyproject, Settings};
|
|
||||||
|
|
||||||
use crate::checks::Check;
|
use crate::checks::Check;
|
||||||
use crate::linter::check_path;
|
use crate::settings::Settings;
|
||||||
use crate::resolver::Relativity;
|
|
||||||
use crate::settings::configuration::Configuration;
|
|
||||||
use crate::settings::flags;
|
|
||||||
use crate::source_code_locator::SourceCodeLocator;
|
use crate::source_code_locator::SourceCodeLocator;
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
|
@ -34,7 +25,6 @@ pub mod checks;
|
||||||
pub mod checks_gen;
|
pub mod checks_gen;
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod code_gen;
|
pub mod code_gen;
|
||||||
pub mod commands;
|
|
||||||
mod cst;
|
mod cst;
|
||||||
mod directives;
|
mod directives;
|
||||||
mod docstrings;
|
mod docstrings;
|
||||||
|
@ -66,7 +56,6 @@ pub mod logging;
|
||||||
pub mod mccabe;
|
pub mod mccabe;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
mod noqa;
|
mod noqa;
|
||||||
mod packages;
|
|
||||||
mod pandas_vet;
|
mod pandas_vet;
|
||||||
pub mod pep8_naming;
|
pub mod pep8_naming;
|
||||||
pub mod printer;
|
pub mod printer;
|
||||||
|
@ -82,58 +71,20 @@ mod ruff;
|
||||||
mod rustpython_helpers;
|
mod rustpython_helpers;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod source_code_locator;
|
pub mod source_code_locator;
|
||||||
#[cfg(feature = "update-informer")]
|
|
||||||
pub mod updates;
|
|
||||||
mod vendored;
|
mod vendored;
|
||||||
pub mod visibility;
|
pub mod visibility;
|
||||||
|
|
||||||
/// Load the relevant `Settings` for a given `Path`.
|
cfg_if! {
|
||||||
fn resolve(path: &Path) -> Result<Settings> {
|
if #[cfg(not(target_family = "wasm"))] {
|
||||||
if let Some(pyproject) = pyproject::find_settings_toml(path)? {
|
pub mod commands;
|
||||||
// First priority: `pyproject.toml` in the current `Path`.
|
mod packages;
|
||||||
resolver::resolve_settings(&pyproject, &Relativity::Parent, None)
|
#[cfg(all(feature = "update-informer"))]
|
||||||
} else if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
pub mod updates;
|
||||||
// Second priority: user-specific `pyproject.toml`.
|
|
||||||
resolver::resolve_settings(&pyproject, &Relativity::Cwd, None)
|
mod lib_native;
|
||||||
|
pub use lib_native::check;
|
||||||
} else {
|
} else {
|
||||||
// Fallback: default settings.
|
mod lib_wasm;
|
||||||
Settings::from_configuration(Configuration::default(), &path_dedot::CWD)
|
pub use lib_wasm::check;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run Ruff over Python source code directly.
|
|
||||||
pub fn check(path: &Path, contents: &str, autofix: bool) -> Result<Vec<Check>> {
|
|
||||||
// Load the relevant `Settings` for the given `Path`.
|
|
||||||
let settings = resolve(path)?;
|
|
||||||
|
|
||||||
// Validate the `Settings` and return any errors.
|
|
||||||
settings.validate()?;
|
|
||||||
|
|
||||||
// Tokenize once.
|
|
||||||
let tokens: Vec<LexResult> = tokenize(contents);
|
|
||||||
|
|
||||||
// Initialize the SourceCodeLocator (which computes offsets lazily).
|
|
||||||
let locator = SourceCodeLocator::new(contents);
|
|
||||||
|
|
||||||
// Extract the `# noqa` and `# isort: skip` directives from the source.
|
|
||||||
let directives = directives::extract_directives(
|
|
||||||
&tokens,
|
|
||||||
&locator,
|
|
||||||
directives::Flags::from_settings(&settings),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Generate checks.
|
|
||||||
let checks = check_path(
|
|
||||||
path,
|
|
||||||
packages::detect_package_root(path),
|
|
||||||
contents,
|
|
||||||
tokens,
|
|
||||||
&locator,
|
|
||||||
&directives,
|
|
||||||
&settings,
|
|
||||||
autofix.into(),
|
|
||||||
flags::Noqa::Enabled,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(checks)
|
|
||||||
}
|
|
||||||
|
|
65
src/lib_native.rs
Normal file
65
src/lib_native.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use path_absolutize::path_dedot;
|
||||||
|
use rustpython_parser::lexer::LexResult;
|
||||||
|
|
||||||
|
use crate::checks::Check;
|
||||||
|
use crate::linter::check_path;
|
||||||
|
use crate::resolver::Relativity;
|
||||||
|
use crate::rustpython_helpers::tokenize;
|
||||||
|
use crate::settings::configuration::Configuration;
|
||||||
|
use crate::settings::{flags, pyproject, Settings};
|
||||||
|
use crate::source_code_locator::SourceCodeLocator;
|
||||||
|
use crate::{directives, packages, resolver};
|
||||||
|
|
||||||
|
/// Load the relevant `Settings` for a given `Path`.
|
||||||
|
fn resolve(path: &Path) -> Result<Settings> {
|
||||||
|
if let Some(pyproject) = pyproject::find_settings_toml(path)? {
|
||||||
|
// First priority: `pyproject.toml` in the current `Path`.
|
||||||
|
resolver::resolve_settings(&pyproject, &Relativity::Parent, None)
|
||||||
|
} else if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
||||||
|
// Second priority: user-specific `pyproject.toml`.
|
||||||
|
resolver::resolve_settings(&pyproject, &Relativity::Cwd, None)
|
||||||
|
} else {
|
||||||
|
// Fallback: default settings.
|
||||||
|
Settings::from_configuration(Configuration::default(), &path_dedot::CWD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run Ruff over Python source code directly.
|
||||||
|
pub fn check(path: &Path, contents: &str, autofix: bool) -> Result<Vec<Check>> {
|
||||||
|
// Load the relevant `Settings` for the given `Path`.
|
||||||
|
let settings = resolve(path)?;
|
||||||
|
|
||||||
|
// Validate the `Settings` and return any errors.
|
||||||
|
settings.validate()?;
|
||||||
|
|
||||||
|
// Tokenize once.
|
||||||
|
let tokens: Vec<LexResult> = tokenize(contents);
|
||||||
|
|
||||||
|
// Initialize the SourceCodeLocator (which computes offsets lazily).
|
||||||
|
let locator = SourceCodeLocator::new(contents);
|
||||||
|
|
||||||
|
// Extract the `# noqa` and `# isort: skip` directives from the source.
|
||||||
|
let directives = directives::extract_directives(
|
||||||
|
&tokens,
|
||||||
|
&locator,
|
||||||
|
directives::Flags::from_settings(&settings),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Generate checks.
|
||||||
|
let checks = check_path(
|
||||||
|
path,
|
||||||
|
packages::detect_package_root(path),
|
||||||
|
contents,
|
||||||
|
tokens,
|
||||||
|
&locator,
|
||||||
|
&directives,
|
||||||
|
&settings,
|
||||||
|
autofix.into(),
|
||||||
|
flags::Noqa::Enabled,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(checks)
|
||||||
|
}
|
141
src/lib_wasm.rs
Normal file
141
src/lib_wasm.rs
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use rustpython_ast::Location;
|
||||||
|
use rustpython_parser::lexer::LexResult;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
use crate::checks::CheckCode;
|
||||||
|
use crate::directives;
|
||||||
|
use crate::linter::check_path;
|
||||||
|
use crate::rustpython_helpers::tokenize;
|
||||||
|
use crate::settings::configuration::Configuration;
|
||||||
|
use crate::settings::options::Options;
|
||||||
|
use crate::settings::{flags, Settings};
|
||||||
|
use crate::source_code_locator::SourceCodeLocator;
|
||||||
|
|
||||||
|
#[wasm_bindgen(typescript_custom_section)]
|
||||||
|
const TYPES: &'static str = r#"
|
||||||
|
export interface Check {
|
||||||
|
code: string;
|
||||||
|
message: string;
|
||||||
|
location: {
|
||||||
|
row: number;
|
||||||
|
column: number;
|
||||||
|
};
|
||||||
|
end_location: {
|
||||||
|
row: number;
|
||||||
|
column: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
struct Message {
|
||||||
|
code: CheckCode,
|
||||||
|
message: String,
|
||||||
|
location: Location,
|
||||||
|
end_location: Location,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(start)]
|
||||||
|
pub fn run() {
|
||||||
|
use log::Level;
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
console_log::init_with_level(Level::Debug).expect("Initializing logger went wrong.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn check(contents: &str, options: JsValue) -> Result<JsValue, JsValue> {
|
||||||
|
let options: Options = serde_wasm_bindgen::from_value(options).map_err(|e| e.to_string())?;
|
||||||
|
let configuration =
|
||||||
|
Configuration::from_options(options, Path::new(".")).map_err(|e| e.to_string())?;
|
||||||
|
let settings =
|
||||||
|
Settings::from_configuration(configuration, Path::new(".")).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
// Tokenize once.
|
||||||
|
let tokens: Vec<LexResult> = tokenize(contents);
|
||||||
|
|
||||||
|
// Initialize the SourceCodeLocator (which computes offsets lazily).
|
||||||
|
let locator = SourceCodeLocator::new(contents);
|
||||||
|
|
||||||
|
// Extract the `# noqa` and `# isort: skip` directives from the source.
|
||||||
|
let directives = directives::extract_directives(&tokens, &locator, directives::Flags::empty());
|
||||||
|
|
||||||
|
// Generate checks.
|
||||||
|
let checks = check_path(
|
||||||
|
Path::new("<filename>"),
|
||||||
|
None,
|
||||||
|
contents,
|
||||||
|
tokens,
|
||||||
|
&locator,
|
||||||
|
&directives,
|
||||||
|
&settings,
|
||||||
|
false.into(),
|
||||||
|
flags::Noqa::Enabled,
|
||||||
|
)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
let messages: Vec<Message> = checks
|
||||||
|
.into_iter()
|
||||||
|
.map(|check| Message {
|
||||||
|
code: check.kind.code().clone(),
|
||||||
|
message: check.kind.body(),
|
||||||
|
location: check.location,
|
||||||
|
end_location: check.end_location,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(serde_wasm_bindgen::to_value(&messages)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use js_sys;
|
||||||
|
use wasm_bindgen_test::*;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
macro_rules! check {
|
||||||
|
($source:expr, $config:expr, $expected:expr) => {{
|
||||||
|
let foo = js_sys::JSON::parse($config).unwrap();
|
||||||
|
match check($source, foo) {
|
||||||
|
Ok(output) => {
|
||||||
|
let result: Vec<Message> = serde_wasm_bindgen::from_value(output).unwrap();
|
||||||
|
assert_eq!(result, $expected);
|
||||||
|
}
|
||||||
|
Err(e) => assert!(false, "{:#?}", e),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn empty_config() {
|
||||||
|
check!(
|
||||||
|
"if (1, 2): pass",
|
||||||
|
r#"{}"#,
|
||||||
|
[Message {
|
||||||
|
code: CheckCode::F634,
|
||||||
|
message: "If test is a tuple, which is always `True`".to_string(),
|
||||||
|
location: Location::new(1, 0),
|
||||||
|
end_location: Location::new(1, 15)
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn partial_config() {
|
||||||
|
check!("if (1, 2): pass", r#"{"ignore": ["F"]}"#, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn partial_nested_config() {
|
||||||
|
let config = r#"{
|
||||||
|
"select": ["Q"],
|
||||||
|
"flake8-quotes": {
|
||||||
|
"inline-quotes": "single"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
check!(r#"print('hello world')"#, config, []);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ use std::ops::AddAssign;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
#[cfg(not(target_family = "wasm"))]
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rustpython_parser::lexer::LexResult;
|
use rustpython_parser::lexer::LexResult;
|
||||||
|
|
||||||
|
|
258
src/main.rs
258
src/main.rs
|
@ -11,263 +11,23 @@
|
||||||
clippy::too_many_lines
|
clippy::too_many_lines
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use std::io::{self};
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
use std::sync::mpsc::channel;
|
|
||||||
|
|
||||||
use ::ruff::autofix::fixer;
|
use cfg_if::cfg_if;
|
||||||
use ::ruff::cli::{extract_log_level, Cli, Overrides};
|
|
||||||
use ::ruff::commands;
|
|
||||||
use ::ruff::logging::{set_up_logging, LogLevel};
|
|
||||||
use ::ruff::printer::{Printer, Violations};
|
|
||||||
use ::ruff::resolver::{resolve_settings, FileDiscovery, PyprojectDiscovery, Relativity};
|
|
||||||
use ::ruff::settings::configuration::Configuration;
|
|
||||||
use ::ruff::settings::types::SerializationFormat;
|
|
||||||
use ::ruff::settings::{pyproject, Settings};
|
|
||||||
#[cfg(feature = "update-informer")]
|
|
||||||
use ::ruff::updates;
|
|
||||||
use anyhow::Result;
|
|
||||||
use clap::{CommandFactory, Parser};
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
|
||||||
use path_absolutize::path_dedot;
|
|
||||||
|
|
||||||
/// Resolve the relevant settings strategy and defaults for the current
|
cfg_if! {
|
||||||
/// invocation.
|
if #[cfg(not(target_family = "wasm"))] {
|
||||||
fn resolve(
|
mod main_native;
|
||||||
config: Option<&Path>,
|
use main_native::inner_main;
|
||||||
overrides: &Overrides,
|
|
||||||
stdin_filename: Option<&Path>,
|
|
||||||
) -> Result<PyprojectDiscovery> {
|
|
||||||
if let Some(pyproject) = config {
|
|
||||||
// First priority: the user specified a `pyproject.toml` file. Use that
|
|
||||||
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
|
|
||||||
// current working directory. (This matches ESLint's behavior.)
|
|
||||||
let settings = resolve_settings(pyproject, &Relativity::Cwd, Some(overrides))?;
|
|
||||||
Ok(PyprojectDiscovery::Fixed(settings))
|
|
||||||
} else if let Some(pyproject) = pyproject::find_settings_toml(
|
|
||||||
stdin_filename
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&path_dedot::CWD.as_path()),
|
|
||||||
)? {
|
|
||||||
// Second priority: find a `pyproject.toml` file in either an ancestor of
|
|
||||||
// `stdin_filename` (if set) or the current working path all paths relative to
|
|
||||||
// that directory. (With `Strategy::Hierarchical`, we'll end up finding
|
|
||||||
// the "closest" `pyproject.toml` file for every Python file later on,
|
|
||||||
// so these act as the "default" settings.)
|
|
||||||
let settings = resolve_settings(&pyproject, &Relativity::Parent, Some(overrides))?;
|
|
||||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
|
||||||
} else if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
|
||||||
// Third priority: find a user-specific `pyproject.toml`, but resolve all paths
|
|
||||||
// relative the current working directory. (With `Strategy::Hierarchical`, we'll
|
|
||||||
// end up the "closest" `pyproject.toml` file for every Python file later on, so
|
|
||||||
// these act as the "default" settings.)
|
|
||||||
let settings = resolve_settings(&pyproject, &Relativity::Cwd, Some(overrides))?;
|
|
||||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
|
||||||
} else {
|
} else {
|
||||||
// Fallback: load Ruff's default settings, and resolve all paths relative to the
|
use anyhow::Result;
|
||||||
// current working directory. (With `Strategy::Hierarchical`, we'll end up the
|
|
||||||
// "closest" `pyproject.toml` file for every Python file later on, so these act
|
|
||||||
// as the "default" settings.)
|
|
||||||
let mut config = Configuration::default();
|
|
||||||
// Apply command-line options that override defaults.
|
|
||||||
config.apply(overrides.clone());
|
|
||||||
let settings = Settings::from_configuration(config, &path_dedot::CWD)?;
|
|
||||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inner_main() -> Result<ExitCode> {
|
#[allow(clippy::unnecessary_wraps)]
|
||||||
// Extract command-line arguments.
|
fn inner_main() -> Result<ExitCode> {
|
||||||
let (cli, overrides) = Cli::parse().partition();
|
Ok(ExitCode::FAILURE)
|
||||||
let log_level = extract_log_level(&cli);
|
|
||||||
set_up_logging(&log_level)?;
|
|
||||||
|
|
||||||
if cli.show_settings && cli.show_files {
|
|
||||||
anyhow::bail!("specify --show-settings or show-files (not both)")
|
|
||||||
}
|
|
||||||
if let Some(shell) = cli.generate_shell_completion {
|
|
||||||
shell.generate(&mut Cli::command(), &mut io::stdout());
|
|
||||||
return Ok(ExitCode::SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the "default" settings. These are used when no `pyproject.toml`
|
|
||||||
// files are present, or files are injected from outside of the hierarchy.
|
|
||||||
let pyproject_strategy = resolve(
|
|
||||||
cli.config.as_deref(),
|
|
||||||
&overrides,
|
|
||||||
cli.stdin_filename.as_deref(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Validate the `Settings` and return any errors.
|
|
||||||
match &pyproject_strategy {
|
|
||||||
PyprojectDiscovery::Fixed(settings) => settings.validate()?,
|
|
||||||
PyprojectDiscovery::Hierarchical(settings) => settings.validate()?,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Extract options that are included in `Settings`, but only apply at the top
|
|
||||||
// level.
|
|
||||||
let file_strategy = FileDiscovery {
|
|
||||||
force_exclude: match &pyproject_strategy {
|
|
||||||
PyprojectDiscovery::Fixed(settings) => settings.force_exclude,
|
|
||||||
PyprojectDiscovery::Hierarchical(settings) => settings.force_exclude,
|
|
||||||
},
|
|
||||||
respect_gitignore: match &pyproject_strategy {
|
|
||||||
PyprojectDiscovery::Fixed(settings) => settings.respect_gitignore,
|
|
||||||
PyprojectDiscovery::Hierarchical(settings) => settings.respect_gitignore,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let (fix, fix_only, format) = match &pyproject_strategy {
|
|
||||||
PyprojectDiscovery::Fixed(settings) => (settings.fix, settings.fix_only, settings.format),
|
|
||||||
PyprojectDiscovery::Hierarchical(settings) => {
|
|
||||||
(settings.fix, settings.fix_only, settings.format)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let autofix = if fix || fix_only {
|
|
||||||
fixer::Mode::Apply
|
|
||||||
} else if matches!(format, SerializationFormat::Json) {
|
|
||||||
fixer::Mode::Generate
|
|
||||||
} else {
|
|
||||||
fixer::Mode::None
|
|
||||||
};
|
|
||||||
let violations = if fix_only {
|
|
||||||
Violations::Hide
|
|
||||||
} else {
|
|
||||||
Violations::Show
|
|
||||||
};
|
|
||||||
let cache = !cli.no_cache;
|
|
||||||
|
|
||||||
if let Some(code) = cli.explain {
|
|
||||||
commands::explain(&code, &format)?;
|
|
||||||
return Ok(ExitCode::SUCCESS);
|
|
||||||
}
|
|
||||||
if cli.show_settings {
|
|
||||||
commands::show_settings(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
|
||||||
return Ok(ExitCode::SUCCESS);
|
|
||||||
}
|
|
||||||
if cli.show_files {
|
|
||||||
commands::show_files(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
|
||||||
return Ok(ExitCode::SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
let printer = Printer::new(&format, &log_level, &autofix, &violations);
|
|
||||||
if cli.watch {
|
|
||||||
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
|
||||||
eprintln!("Warning: --fix is not enabled in watch mode.");
|
|
||||||
}
|
|
||||||
if cli.add_noqa {
|
|
||||||
eprintln!("Warning: --add-noqa is not enabled in watch mode.");
|
|
||||||
}
|
|
||||||
if cli.autoformat {
|
|
||||||
eprintln!("Warning: --autoformat is not enabled in watch mode.");
|
|
||||||
}
|
|
||||||
if format != SerializationFormat::Text {
|
|
||||||
eprintln!("Warning: --format 'text' is used in watch mode.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform an initial run instantly.
|
|
||||||
printer.clear_screen()?;
|
|
||||||
printer.write_to_user("Starting linter in watch mode...\n");
|
|
||||||
|
|
||||||
let messages = commands::run(
|
|
||||||
&cli.files,
|
|
||||||
&pyproject_strategy,
|
|
||||||
&file_strategy,
|
|
||||||
&overrides,
|
|
||||||
cache.into(),
|
|
||||||
fixer::Mode::None,
|
|
||||||
)?;
|
|
||||||
printer.write_continuously(&messages)?;
|
|
||||||
|
|
||||||
// Configure the file watcher.
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let mut watcher = recommended_watcher(tx)?;
|
|
||||||
for file in &cli.files {
|
|
||||||
watcher.watch(file, RecursiveMode::Recursive)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match rx.recv() {
|
|
||||||
Ok(event) => {
|
|
||||||
let paths = event?.paths;
|
|
||||||
let py_changed = paths.iter().any(|path| {
|
|
||||||
path.extension()
|
|
||||||
.map(|ext| ext == "py" || ext == "pyi")
|
|
||||||
.unwrap_or_default()
|
|
||||||
});
|
|
||||||
if py_changed {
|
|
||||||
printer.clear_screen()?;
|
|
||||||
printer.write_to_user("File change detected...\n");
|
|
||||||
|
|
||||||
let messages = commands::run(
|
|
||||||
&cli.files,
|
|
||||||
&pyproject_strategy,
|
|
||||||
&file_strategy,
|
|
||||||
&overrides,
|
|
||||||
cache.into(),
|
|
||||||
fixer::Mode::None,
|
|
||||||
)?;
|
|
||||||
printer.write_continuously(&messages)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => return Err(err.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if cli.add_noqa {
|
|
||||||
let modifications =
|
|
||||||
commands::add_noqa(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
|
||||||
if modifications > 0 && log_level >= LogLevel::Default {
|
|
||||||
println!("Added {modifications} noqa directives.");
|
|
||||||
}
|
|
||||||
} else if cli.autoformat {
|
|
||||||
let modifications =
|
|
||||||
commands::autoformat(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
|
||||||
if modifications > 0 && log_level >= LogLevel::Default {
|
|
||||||
println!("Formatted {modifications} files.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let is_stdin = cli.files == vec![PathBuf::from("-")];
|
|
||||||
|
|
||||||
// Generate lint violations.
|
|
||||||
let diagnostics = if is_stdin {
|
|
||||||
commands::run_stdin(
|
|
||||||
cli.stdin_filename.as_deref(),
|
|
||||||
&pyproject_strategy,
|
|
||||||
&file_strategy,
|
|
||||||
&overrides,
|
|
||||||
autofix,
|
|
||||||
)?
|
|
||||||
} else {
|
|
||||||
commands::run(
|
|
||||||
&cli.files,
|
|
||||||
&pyproject_strategy,
|
|
||||||
&file_strategy,
|
|
||||||
&overrides,
|
|
||||||
cache.into(),
|
|
||||||
autofix,
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
|
|
||||||
// Always try to print violations (the printer itself may suppress output),
|
|
||||||
// unless we're writing fixes via stdin (in which case, the transformed
|
|
||||||
// source code goes to stdout).
|
|
||||||
if !(is_stdin && matches!(autofix, fixer::Mode::Apply)) {
|
|
||||||
printer.write_once(&diagnostics)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for updates if we're in a non-silent log level.
|
|
||||||
#[cfg(feature = "update-informer")]
|
|
||||||
if !is_stdin && log_level >= LogLevel::Default && atty::is(atty::Stream::Stdout) {
|
|
||||||
drop(updates::check_for_updates());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !diagnostics.messages.is_empty() && !cli.exit_zero && !fix_only {
|
|
||||||
return Ok(ExitCode::FAILURE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExitCode::SUCCESS)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
|
|
270
src/main_native.rs
Normal file
270
src/main_native.rs
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
#![allow(
|
||||||
|
clippy::collapsible_else_if,
|
||||||
|
clippy::collapsible_if,
|
||||||
|
clippy::implicit_hasher,
|
||||||
|
clippy::match_same_arms,
|
||||||
|
clippy::missing_errors_doc,
|
||||||
|
clippy::missing_panics_doc,
|
||||||
|
clippy::module_name_repetitions,
|
||||||
|
clippy::must_use_candidate,
|
||||||
|
clippy::similar_names,
|
||||||
|
clippy::too_many_lines
|
||||||
|
)]
|
||||||
|
|
||||||
|
use std::io::{self};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::ExitCode;
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
|
use ::ruff::autofix::fixer;
|
||||||
|
use ::ruff::cli::{extract_log_level, Cli, Overrides};
|
||||||
|
use ::ruff::commands;
|
||||||
|
use ::ruff::logging::{set_up_logging, LogLevel};
|
||||||
|
use ::ruff::printer::{Printer, Violations};
|
||||||
|
use ::ruff::resolver::{resolve_settings, FileDiscovery, PyprojectDiscovery, Relativity};
|
||||||
|
use ::ruff::settings::configuration::Configuration;
|
||||||
|
use ::ruff::settings::types::SerializationFormat;
|
||||||
|
use ::ruff::settings::{pyproject, Settings};
|
||||||
|
#[cfg(feature = "update-informer")]
|
||||||
|
use ::ruff::updates;
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::{CommandFactory, Parser};
|
||||||
|
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||||
|
use path_absolutize::path_dedot;
|
||||||
|
|
||||||
|
/// Resolve the relevant settings strategy and defaults for the current
|
||||||
|
/// invocation.
|
||||||
|
fn resolve(
|
||||||
|
config: Option<&Path>,
|
||||||
|
overrides: &Overrides,
|
||||||
|
stdin_filename: Option<&Path>,
|
||||||
|
) -> Result<PyprojectDiscovery> {
|
||||||
|
if let Some(pyproject) = config {
|
||||||
|
// First priority: the user specified a `pyproject.toml` file. Use that
|
||||||
|
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
|
||||||
|
// current working directory. (This matches ESLint's behavior.)
|
||||||
|
let settings = resolve_settings(pyproject, &Relativity::Cwd, Some(overrides))?;
|
||||||
|
Ok(PyprojectDiscovery::Fixed(settings))
|
||||||
|
} else if let Some(pyproject) = pyproject::find_settings_toml(
|
||||||
|
stdin_filename
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&path_dedot::CWD.as_path()),
|
||||||
|
)? {
|
||||||
|
// Second priority: find a `pyproject.toml` file in either an ancestor of
|
||||||
|
// `stdin_filename` (if set) or the current working path all paths relative to
|
||||||
|
// that directory. (With `Strategy::Hierarchical`, we'll end up finding
|
||||||
|
// the "closest" `pyproject.toml` file for every Python file later on,
|
||||||
|
// so these act as the "default" settings.)
|
||||||
|
let settings = resolve_settings(&pyproject, &Relativity::Parent, Some(overrides))?;
|
||||||
|
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||||
|
} else if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
||||||
|
// Third priority: find a user-specific `pyproject.toml`, but resolve all paths
|
||||||
|
// relative the current working directory. (With `Strategy::Hierarchical`, we'll
|
||||||
|
// end up the "closest" `pyproject.toml` file for every Python file later on, so
|
||||||
|
// these act as the "default" settings.)
|
||||||
|
let settings = resolve_settings(&pyproject, &Relativity::Cwd, Some(overrides))?;
|
||||||
|
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||||
|
} else {
|
||||||
|
// Fallback: load Ruff's default settings, and resolve all paths relative to the
|
||||||
|
// current working directory. (With `Strategy::Hierarchical`, we'll end up the
|
||||||
|
// "closest" `pyproject.toml` file for every Python file later on, so these act
|
||||||
|
// as the "default" settings.)
|
||||||
|
let mut config = Configuration::default();
|
||||||
|
// Apply command-line options that override defaults.
|
||||||
|
config.apply(overrides.clone());
|
||||||
|
let settings = Settings::from_configuration(config, &path_dedot::CWD)?;
|
||||||
|
Ok(PyprojectDiscovery::Hierarchical(settings))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn inner_main() -> Result<ExitCode> {
|
||||||
|
// Extract command-line arguments.
|
||||||
|
let (cli, overrides) = Cli::parse().partition();
|
||||||
|
let log_level = extract_log_level(&cli);
|
||||||
|
set_up_logging(&log_level)?;
|
||||||
|
|
||||||
|
if cli.show_settings && cli.show_files {
|
||||||
|
anyhow::bail!("specify --show-settings or show-files (not both)")
|
||||||
|
}
|
||||||
|
if let Some(shell) = cli.generate_shell_completion {
|
||||||
|
shell.generate(&mut Cli::command(), &mut io::stdout());
|
||||||
|
return Ok(ExitCode::SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the "default" settings. These are used when no `pyproject.toml`
|
||||||
|
// files are present, or files are injected from outside of the hierarchy.
|
||||||
|
let pyproject_strategy = resolve(
|
||||||
|
cli.config.as_deref(),
|
||||||
|
&overrides,
|
||||||
|
cli.stdin_filename.as_deref(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Validate the `Settings` and return any errors.
|
||||||
|
match &pyproject_strategy {
|
||||||
|
PyprojectDiscovery::Fixed(settings) => settings.validate()?,
|
||||||
|
PyprojectDiscovery::Hierarchical(settings) => settings.validate()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extract options that are included in `Settings`, but only apply at the top
|
||||||
|
// level.
|
||||||
|
let file_strategy = FileDiscovery {
|
||||||
|
force_exclude: match &pyproject_strategy {
|
||||||
|
PyprojectDiscovery::Fixed(settings) => settings.force_exclude,
|
||||||
|
PyprojectDiscovery::Hierarchical(settings) => settings.force_exclude,
|
||||||
|
},
|
||||||
|
respect_gitignore: match &pyproject_strategy {
|
||||||
|
PyprojectDiscovery::Fixed(settings) => settings.respect_gitignore,
|
||||||
|
PyprojectDiscovery::Hierarchical(settings) => settings.respect_gitignore,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let (fix, fix_only, format) = match &pyproject_strategy {
|
||||||
|
PyprojectDiscovery::Fixed(settings) => (settings.fix, settings.fix_only, settings.format),
|
||||||
|
PyprojectDiscovery::Hierarchical(settings) => {
|
||||||
|
(settings.fix, settings.fix_only, settings.format)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let autofix = if fix || fix_only {
|
||||||
|
fixer::Mode::Apply
|
||||||
|
} else if matches!(format, SerializationFormat::Json) {
|
||||||
|
fixer::Mode::Generate
|
||||||
|
} else {
|
||||||
|
fixer::Mode::None
|
||||||
|
};
|
||||||
|
let violations = if fix_only {
|
||||||
|
Violations::Hide
|
||||||
|
} else {
|
||||||
|
Violations::Show
|
||||||
|
};
|
||||||
|
let cache = !cli.no_cache;
|
||||||
|
|
||||||
|
if let Some(code) = cli.explain {
|
||||||
|
commands::explain(&code, &format)?;
|
||||||
|
return Ok(ExitCode::SUCCESS);
|
||||||
|
}
|
||||||
|
if cli.show_settings {
|
||||||
|
commands::show_settings(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||||
|
return Ok(ExitCode::SUCCESS);
|
||||||
|
}
|
||||||
|
if cli.show_files {
|
||||||
|
commands::show_files(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||||
|
return Ok(ExitCode::SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
let printer = Printer::new(&format, &log_level, &autofix, &violations);
|
||||||
|
if cli.watch {
|
||||||
|
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||||
|
eprintln!("Warning: --fix is not enabled in watch mode.");
|
||||||
|
}
|
||||||
|
if cli.add_noqa {
|
||||||
|
eprintln!("Warning: --add-noqa is not enabled in watch mode.");
|
||||||
|
}
|
||||||
|
if cli.autoformat {
|
||||||
|
eprintln!("Warning: --autoformat is not enabled in watch mode.");
|
||||||
|
}
|
||||||
|
if format != SerializationFormat::Text {
|
||||||
|
eprintln!("Warning: --format 'text' is used in watch mode.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform an initial run instantly.
|
||||||
|
printer.clear_screen()?;
|
||||||
|
printer.write_to_user("Starting linter in watch mode...\n");
|
||||||
|
|
||||||
|
let messages = commands::run(
|
||||||
|
&cli.files,
|
||||||
|
&pyproject_strategy,
|
||||||
|
&file_strategy,
|
||||||
|
&overrides,
|
||||||
|
cache.into(),
|
||||||
|
fixer::Mode::None,
|
||||||
|
)?;
|
||||||
|
printer.write_continuously(&messages)?;
|
||||||
|
|
||||||
|
// Configure the file watcher.
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
let mut watcher = recommended_watcher(tx)?;
|
||||||
|
for file in &cli.files {
|
||||||
|
watcher.watch(file, RecursiveMode::Recursive)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match rx.recv() {
|
||||||
|
Ok(event) => {
|
||||||
|
let paths = event?.paths;
|
||||||
|
let py_changed = paths.iter().any(|path| {
|
||||||
|
path.extension()
|
||||||
|
.map(|ext| ext == "py" || ext == "pyi")
|
||||||
|
.unwrap_or_default()
|
||||||
|
});
|
||||||
|
if py_changed {
|
||||||
|
printer.clear_screen()?;
|
||||||
|
printer.write_to_user("File change detected...\n");
|
||||||
|
|
||||||
|
let messages = commands::run(
|
||||||
|
&cli.files,
|
||||||
|
&pyproject_strategy,
|
||||||
|
&file_strategy,
|
||||||
|
&overrides,
|
||||||
|
cache.into(),
|
||||||
|
fixer::Mode::None,
|
||||||
|
)?;
|
||||||
|
printer.write_continuously(&messages)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if cli.add_noqa {
|
||||||
|
let modifications =
|
||||||
|
commands::add_noqa(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||||
|
if modifications > 0 && log_level >= LogLevel::Default {
|
||||||
|
println!("Added {modifications} noqa directives.");
|
||||||
|
}
|
||||||
|
} else if cli.autoformat {
|
||||||
|
let modifications =
|
||||||
|
commands::autoformat(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?;
|
||||||
|
if modifications > 0 && log_level >= LogLevel::Default {
|
||||||
|
println!("Formatted {modifications} files.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let is_stdin = cli.files == vec![PathBuf::from("-")];
|
||||||
|
|
||||||
|
// Generate lint violations.
|
||||||
|
let diagnostics = if is_stdin {
|
||||||
|
commands::run_stdin(
|
||||||
|
cli.stdin_filename.as_deref(),
|
||||||
|
&pyproject_strategy,
|
||||||
|
&file_strategy,
|
||||||
|
&overrides,
|
||||||
|
autofix,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
commands::run(
|
||||||
|
&cli.files,
|
||||||
|
&pyproject_strategy,
|
||||||
|
&file_strategy,
|
||||||
|
&overrides,
|
||||||
|
cache.into(),
|
||||||
|
autofix,
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Always try to print violations (the printer itself may suppress output),
|
||||||
|
// unless we're writing fixes via stdin (in which case, the transformed
|
||||||
|
// source code goes to stdout).
|
||||||
|
if !(is_stdin && matches!(autofix, fixer::Mode::Apply)) {
|
||||||
|
printer.write_once(&diagnostics)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for updates if we're in a non-silent log level.
|
||||||
|
#[cfg(feature = "update-informer")]
|
||||||
|
if !is_stdin && log_level >= LogLevel::Default && atty::is(atty::Stream::Stdout) {
|
||||||
|
drop(updates::check_for_updates());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !diagnostics.messages.is_empty() && !cli.exit_zero && !fix_only {
|
||||||
|
return Ok(ExitCode::FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ExitCode::SUCCESS)
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![cfg(not(target_family = "wasm"))]
|
||||||
|
|
||||||
use std::io::{ErrorKind, Read};
|
use std::io::{ErrorKind, Read};
|
||||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpListener, TcpStream};
|
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpListener, TcpStream};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![cfg(not(target_family = "wasm"))]
|
||||||
|
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue