Compare commits

..

No commits in common. "master" and "0.65.0" have entirely different histories.

2865 changed files with 2699168 additions and 1639 deletions

@ -28,10 +28,10 @@ jobs:
# Targets that match the OS of the runner.
- target: x86_64-unknown-linux-gnu
os: ubuntu-22.04
- target: x86_64-apple-darwin
os: macos-13
- target: x86_64-pc-windows-msvc
os: windows-latest
- target: aarch64-apple-darwin
os: macos-latest
# Targets using cross-compilation (upload-rust-binary-action
# detects that they need cross).
@ -39,10 +39,10 @@ jobs:
os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu
os: ubuntu-22.04
- target: aarch64-apple-darwin
os: macos-latest
- target: aarch64-pc-windows-msvc
os: windows-latest
- target: x86_64-apple-darwin
os: macos-latest
runs-on: ${{ matrix.os }}
steps:
# v4.2.2

@ -12,6 +12,8 @@ jobs:
job:
# Operating systems available: https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories
- { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04 }
# mac-latest (currently mac-14) is an ARM device, so use macos-13 to get Intel.
- { target: x86_64-apple-darwin, os: macos-13 }
- { target: x86_64-pc-windows-msvc, os: windows-latest }
- { target: aarch64-pc-windows-msvc, os: windows-11-arm }
@ -19,11 +21,11 @@ jobs:
# mac-14 is an M1 ARM device.
- { target: aarch64-apple-darwin, os: macos-14 }
- { target: x86_64-apple-darwin, os: macos-14, use-cross: true }
- { target: x86_64-unknown-linux-musl, os: ubuntu-22.04, use-cross: true }
env:
BUILD_CMD: cargo
SUBCOMMAND: test
steps:
- name: Checkout source code
@ -31,7 +33,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@1.76.0
uses: dtolnay/rust-toolchain@1.75.0
with:
targets: ${{ matrix.job.target }}
@ -48,7 +50,7 @@ jobs:
- name: Test
shell: bash
run: $BUILD_CMD test --target ${{ matrix.job.target }}
run: $BUILD_CMD $SUBCOMMAND --target ${{ matrix.job.target }}
test_mime_db:
name: Test with MIME database
@ -56,7 +58,7 @@ jobs:
steps:
# v4.2.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: dtolnay/rust-toolchain@1.76.0
- uses: dtolnay/rust-toolchain@1.75.0
# This runs tests that rely on the MIME database being present.
- run: cargo test -- --ignored
@ -75,7 +77,7 @@ jobs:
steps:
# v4.2.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: dtolnay/rust-toolchain@1.76.0
- uses: dtolnay/rust-toolchain@1.75.0
- name: Generate output for all sample files
run: ./sample_files/compare_all.sh
- name: Verify output is unchanged
@ -87,7 +89,7 @@ jobs:
steps:
# v4.2.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: dtolnay/rust-toolchain@1.76.0
- uses: dtolnay/rust-toolchain@1.75.0
- run: cargo package --allow-dirty
fmt:
@ -96,7 +98,7 @@ jobs:
steps:
# v4.2.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: dtolnay/rust-toolchain@1.76.0
- uses: dtolnay/rust-toolchain@1.75.0
- run: cargo fmt --all -- --check
actionlint:
@ -108,19 +110,3 @@ jobs:
- uses: raven-actions/actionlint@v2
with:
shellcheck: false
typo-check:
name: Typo Check
runs-on: ubuntu-22.04
timeout-minutes: 10
env:
FORCE_COLOR: 1
TYPOS_VERSION: v1.38.1
steps:
# v4.2.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: Download typos
run: curl -LsSf https://github.com/crate-ci/typos/releases/download/$TYPOS_VERSION/typos-$TYPOS_VERSION-x86_64-unknown-linux-musl.tar.gz | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin
- name: Check for typos
run: typos

@ -12,5 +12,3 @@ ba = "ba"
edn = "edn"
mak = "mak"
ND = "ND"
diffstatic = "difftastic"

@ -1,47 +1,8 @@
## 0.68 (unreleased)
### Parsing
Fixed an issue with parsing raw string literals in Rust.
## 0.67 (released 16 November 2025)
### Parsing
Added support for protocol buffer files. Updated Ada, Clojure, CMake, Dart,
Devicetree, Elisp, Elm, Gleam, HCL, Newick, QML, R, Racket, SQL, Scheme, and
Solidity parsers.
Improved handling of variable names `$foo` in shell scripts.
Improved detection of YAML files.
### Diffing
Improved subword highlighting for words with hyphens.
### Display
Difftastic is now smarter about calculating the display width for
side-by-side diffs. Long lines that are not included in the output no
longer affect display.
Improved descriptions of changes to binary files.
Fixed an issue (introduced after 0.65) where difftastic would not use
the full width of the terminal on side-by-side diffs when files had
more than 1,000 lines.
## 0.66 (skipped)
Due to an issue with the release process, 0.66 was abandoned in favour
of doing a normal release with version 0.67.
## 0.65 (released 23rd September 2025)
## 0.65 (unreleased)
### Build
Increased the default page size of Jemalloc, so difftastic should work
Incrased the default page size of Jemalloc, so difftastic should work
on systems with large page sizes (typically aarch64, i.e. Arm).
difftastic now requires Rust 1.75 to build.

288
Cargo.lock generated

@ -80,14 +80,12 @@ dependencies = [
[[package]]
name = "assert_cmd"
version = "2.0.17"
version = "2.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66"
checksum = "9834fcc22e0874394a010230586367d4a3e9f11b560f469262678547e1d2575e"
dependencies = [
"anstyle",
"bstr",
"doc-comment",
"libc",
"predicates",
"predicates-core",
"predicates-tree",
@ -131,11 +129,10 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "cc"
version = "1.2.39"
version = "1.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f"
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
dependencies = [
"find-msvc-tools",
"shlex",
]
@ -163,7 +160,7 @@ dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
"strsim 0.11.1",
"terminal_size",
]
@ -243,7 +240,7 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "difftastic"
version = "0.68.0"
version = "0.65.0"
dependencies = [
"assert_cmd",
"bumpalo",
@ -271,29 +268,20 @@ dependencies = [
"serde_json",
"smallvec",
"streaming-iterator",
"strsim",
"strsim 0.10.0",
"strum",
"tikv-jemallocator",
"tree-sitter",
"tree-sitter-ada",
"tree-sitter-bash",
"tree-sitter-c",
"tree-sitter-c-sharp",
"tree-sitter-clojure-orchard",
"tree-sitter-cmake",
"tree-sitter-cpp",
"tree-sitter-css",
"tree-sitter-dart-orchard",
"tree-sitter-devicetree",
"tree-sitter-elisp",
"tree-sitter-elixir",
"tree-sitter-elm",
"tree-sitter-erlang",
"tree-sitter-fsharp",
"tree-sitter-gleam",
"tree-sitter-go",
"tree-sitter-haskell",
"tree-sitter-hcl",
"tree-sitter-html",
"tree-sitter-java",
"tree-sitter-javascript",
@ -302,24 +290,15 @@ dependencies = [
"tree-sitter-language",
"tree-sitter-lua",
"tree-sitter-make",
"tree-sitter-newick",
"tree-sitter-nix",
"tree-sitter-objc",
"tree-sitter-ocaml",
"tree-sitter-pascal",
"tree-sitter-php",
"tree-sitter-proto",
"tree-sitter-python",
"tree-sitter-qmljs",
"tree-sitter-r",
"tree-sitter-racket",
"tree-sitter-ruby",
"tree-sitter-rust-orchard",
"tree-sitter-scala",
"tree-sitter-scheme",
"tree-sitter-sequel",
"tree-sitter-sfapex",
"tree-sitter-solidity",
"tree-sitter-swift",
"tree-sitter-toml-ng",
"tree-sitter-typescript",
@ -384,12 +363,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959"
[[package]]
name = "fixedbitset"
version = "0.4.2"
@ -398,9 +371,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "float-cmp"
version = "0.10.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
dependencies = [
"num-traits",
]
@ -510,6 +483,15 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.10"
@ -619,9 +601,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "owo-colors"
version = "4.2.3"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "parking_lot"
@ -658,13 +640,13 @@ dependencies = [
[[package]]
name = "predicates"
version = "3.1.3"
version = "2.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd"
dependencies = [
"anstyle",
"difflib",
"float-cmp",
"itertools",
"normalize-line-endings",
"predicates-core",
"regex",
@ -708,9 +690,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.101"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
@ -836,28 +818,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
@ -866,16 +838,13 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.145"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
dependencies = [
"indexmap",
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
@ -926,6 +895,12 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strsim"
version = "0.11.1"
@ -956,9 +931,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.106"
version = "2.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0"
dependencies = [
"proc-macro2",
"quote",
@ -1012,28 +987,17 @@ dependencies = [
[[package]]
name = "tree-sitter"
version = "0.25.10"
version = "0.24.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78f873475d258561b06f1c595d93308a7ed124d9977cb26b148c2084a4a3cc87"
checksum = "8ac95b18f0f727aaaa012bd5179a1916706ee3ed071920fdbda738750b0c0bf5"
dependencies = [
"cc",
"regex",
"regex-syntax",
"serde_json",
"streaming-iterator",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-ada"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d9fcdd64359c98fcc99d72f6d3d6ca5d6d76ce325ac39430b1d283a0fb61ca1"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-bash"
version = "0.23.3"
@ -1064,26 +1028,6 @@ dependencies = [
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-clojure-orchard"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ff7bdebec1f2578787697e32b5539842b8ea9e35df784b537c22cf1ed1aa8f"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-cmake"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c1b35d1dd7396d24b3e826bb0f975b915ec7e9125b989d5e9d24ebb6a08509a"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-cpp"
version = "0.23.4"
@ -1104,36 +1048,6 @@ dependencies = [
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-dart-orchard"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206d0062b36beab07b152aa9c02f5b4944453cd942ba01471df951b40292e9ad"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-devicetree"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1e6874b59d8252cbaf9750f7152166e17ed162921a5f6d4e8bd2a2bc2aed46b"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-elisp"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e2e9a6ab3cebf24ca41cdc1f985549b9b33f7ef25c19ac7d18e53a4ee24da09"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-elixir"
version = "0.3.4"
@ -1144,16 +1058,6 @@ dependencies = [
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-elm"
version = "5.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accee95d95c001e53f5ab1a1168f1ed1c6ec763a17fb48b43acf5bf4ff9e3423"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-erlang"
version = "0.13.0"
@ -1174,16 +1078,6 @@ dependencies = [
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-gleam"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0175c53793bda5d444360dd5add25463d18d66afb7f521d6791e2fc61bf2fb3"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-go"
version = "0.23.4"
@ -1204,16 +1098,6 @@ dependencies = [
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-hcl"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a7b2cc3d7121553b84309fab9d11b3ff3d420403eef9ae50f9fd1cd9d9cf012"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-html"
version = "0.23.2"
@ -1290,16 +1174,6 @@ dependencies = [
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-newick"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c5785bcf2ade84e16242b1a8d1f574334ddd26032c15a5d9bcc012e013f4e1"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-nix"
version = "0.0.2"
@ -1350,16 +1224,6 @@ dependencies = [
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-proto"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e4360b434b5980fc397137ef29e1988619fef4159ac86fa7ac5777d459d3924"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-python"
version = "0.23.5"
@ -1370,36 +1234,6 @@ dependencies = [
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-qmljs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67445ea937cd7eadaf2f628e2e7dd234374586cc31b4d1d63dbb5f5e7f9d9b62"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-r"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "429133cbda9f8a46e03ef3aae6abb6c3d22875f8585cad472138101bfd517255"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-racket"
version = "0.24.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8395b6a054e6264c67e1ef915f239c4f86575b7d7c69638bdbf3c336c58f128"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-ruby"
version = "0.23.1"
@ -1430,46 +1264,6 @@ dependencies = [
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-scheme"
version = "0.24.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7e7f156bdf38145f26705d1733185698845307d3e9d9c071ecce4375575131"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-sequel"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d198ad3c319c02e43c21efa1ec796b837afcb96ffaef1a40c1978fbdcec7d17"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-sfapex"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b97637acb3bcfc2518162ecf9b02a18b28a502cd7631bec5c2a26f4e5ea024b"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-solidity"
version = "1.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eacf8875b70879f0cb670c60b233ad0b68752d9e1474e6c3ef168eea8a90b25"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-swift"
version = "0.7.0"

@ -4,7 +4,7 @@ description = "A structural diff that understands syntax."
repository = "https://github.com/wilfred/difftastic"
homepage = "http://difftastic.wilfred.me.uk/"
license = "MIT"
version = "0.68.0"
version = "0.65.0"
authors = ["Wilfred Hughes <me@wilfred.me.uk>"]
keywords = ["diff", "syntax"]
categories = ["development-tools", "command-line-utilities", "parser-implementations"]
@ -15,10 +15,10 @@ edition = "2021"
# https://releases.rs/ is a useful website to see when different Rust
# versions were released.
#
# For reference, Debian stable is on 1.85 (see
# For reference, Debian stable is on 1.63 (see
# https://tracker.debian.org/pkg/rustc) but Debian doesn't currently
# package difftastic. Other distros are somewhat newer.
rust-version = "1.76.0"
rust-version = "1.75.0"
include = [
"/build.rs",
"/src/",
@ -41,7 +41,7 @@ regex = "1.10.4"
clap = { version = "4.0.0", features = ["cargo", "env", "wrap_help", "string"] }
typed-arena = "2.0.2"
rustc-hash = "2.0.0"
strsim = "0.11.0"
strsim = "0.10.0"
lazy_static = "1.4.0"
libc = "0.2.108"
log = "0.4.14"
@ -52,7 +52,7 @@ radix-heap = "0.4.2"
# a slightly more aggressive MSRV than difftastic. Constrain ignore to
# a known-good max version.
ignore = ">= 0.4, < 0.4.24"
owo-colors = "4.2.3"
owo-colors = "3.5.0"
wu-diff = "0.1.2"
rayon = "1.7.0"
tree_magic_mini = "3.1.6"
@ -72,29 +72,20 @@ streaming-iterator = "0.1.9"
encoding_rs = "0.8.35"
# tree-sitter itself.
tree-sitter = "0.25.9"
tree-sitter = "0.24.0"
tree-sitter-language = "0.1.3"
# tree-sitter parsers that are available on crates.io.
tree-sitter-ada = "0.1.0"
tree-sitter-bash = "0.23.3"
tree-sitter-c = "0.23.4"
tree-sitter-c-sharp = "0.23.1"
tree-sitter-clojure-orchard = "0.2.0"
tree-sitter-cmake = "0.7.1"
tree-sitter-cpp = "0.23.4"
tree-sitter-css = "0.23.1"
tree-sitter-dart-orchard = "0.2.1"
tree-sitter-devicetree = "0.14.1"
tree-sitter-elisp = "1.6.1"
tree-sitter-elixir = "0.3.4"
tree-sitter-elm = "5.8.0"
tree-sitter-erlang = "0.13.0"
tree-sitter-fsharp = "0.1.0"
tree-sitter-gleam = "1.0.0"
tree-sitter-go = "0.23.4"
tree-sitter-haskell = "0.23.1"
tree-sitter-hcl = "1.1.0"
tree-sitter-html = "0.23.2"
tree-sitter-java = "0.23.4"
tree-sitter-javascript = "0.23.1"
@ -102,24 +93,15 @@ tree-sitter-json = "0.24.8"
tree-sitter-julia = "0.23.1"
tree-sitter-lua = "0.2.0"
tree-sitter-make = "1.1.1"
tree-sitter-newick = "1.1.0"
tree-sitter-nix = "0.0.2"
tree-sitter-objc = "3.0.2"
tree-sitter-ocaml = "0.23.2"
tree-sitter-pascal = "0.10.0"
tree-sitter-php = "0.23.11"
tree-sitter-proto = "0.2.0"
tree-sitter-python = "0.23.5"
tree-sitter-qmljs = "0.3.0"
tree-sitter-r = "1.2.0"
tree-sitter-racket = "0.24.7"
tree-sitter-ruby = "0.23.1"
tree-sitter-rust-orchard = "0.13.0"
tree-sitter-scala = "0.23.3"
tree-sitter-scheme = "0.24.7"
tree-sitter-sequel = "0.3.11"
tree-sitter-sfapex = "2.4.0"
tree-sitter-solidity = "1.2.13"
tree-sitter-swift = "0.7.0"
tree-sitter-toml-ng = "0.7.0"
tree-sitter-typescript = "0.23.2"
@ -128,19 +110,21 @@ tree-sitter-xml = "0.7.0"
tree-sitter-yaml = "0.7.0"
tree-sitter-zig = "1.1.2"
[target.'cfg(not(any(target_env = "msvc", target_os = "illumos", target_os = "freebsd")))'.dependencies]
[target.'cfg(not(any(target_env = "msvc", target_os = "illumos")))'.dependencies]
tikv-jemallocator = "0.6"
[dev-dependencies]
assert_cmd = "2.0.17"
predicates = "3.1.3"
# assert_cmd 2.0.10 requires predicates 3.
# TODO: update.
assert_cmd = ">= 2, < 2.0.9"
predicates = "2.1.0"
pretty_assertions = "1.3.0"
[build-dependencies]
# TODO: enable parallel mode once MSRV hits 1.61, see discussion in
# https://github.com/rust-lang/cc-rs/pull/849
cc = "1.2.13"
cc = "1.1.30"
rayon = "1.7.0"
version_check = "0.9.4"
@ -151,14 +135,4 @@ lto = "thin"
name = "difft"
path = "src/main.rs"
[package.metadata.deb]
depends = "$auto"
section = "utility"
priority = "optional"
assets = [
"$auto",
[ "difft.1", "usr/share/man/man1/", "644" ],
[ "CHANGELOG.md", "usr/share/doc/difftastic/", "644" ],
]
[features]

@ -1,10 +1,10 @@
<p align="center">
<a href="#readme"><img src="img/logo.png" alt="it's difftastic!"/></a>
<br>
<a href="https://difftastic.wilfred.me.uk/introduction.html"><img src="https://img.shields.io/badge/manual-en-brightgreen?style=plastic" alt="English manual"></a>
<a href="https://difftastic.wilfred.me.uk/zh-CN/"><img src="https://img.shields.io/badge/manual-zh--CN-brightgreen?style=plastic" alt="Chinese manual"></a>
<a href="https://crates.io/crates/difftastic"><img src="https://img.shields.io/crates/v/difftastic.svg?style=plastic" alt="crates.io"></a>
<a href="https://codecov.io/gh/Wilfred/difftastic"><img src="https://img.shields.io/codecov/c/github/Wilfred/difftastic?style=plastic&token=dZzAZtQT2S" alt="codecov.io"></a>
<a href="https://difftastic.wilfred.me.uk/introduction.html"><img src="https://img.shields.io/badge/manual-en-brightgreen?style=flat-square" alt="English manual"></a>
<a href="https://difftastic.wilfred.me.uk/zh-CN/"><img src="https://img.shields.io/badge/manual-zh--CN-brightgreen?style=flat-square" alt="Chinese manual"></a>
<a href="https://crates.io/crates/difftastic"><img src="https://img.shields.io/crates/v/difftastic.svg?style=flat-square" alt="crates.io"></a>
<a href="https://codecov.io/gh/Wilfred/difftastic"><img src="https://img.shields.io/codecov/c/github/Wilfred/difftastic?style=flat-square&token=dZzAZtQT2S" alt="codecov.io"></a>
</p>
Difftastic is a structural diff tool that compares files based on
@ -14,37 +14,37 @@ their syntax.
[Installation](https://difftastic.wilfred.me.uk/installation.html) in
[the manual](http://difftastic.wilfred.me.uk/).**
## Examples
## Basic Example
![Screenshot of difftastic and Rust](img/wrap_expr.png)
![Screenshot of difftastic and JS](img/js.png)
^ Difftastic understands exactly which pieces of syntax have changed,
and can highlight them in context.
In this JavaScript example, we can see:
![Screenshot of difftastic and HTML](img/html.png)
(1) Difftastic understands nesting. It highlights the matching `{` and
`}`, but understands that `foo()` hasn't changed despite the leading
whitespace.
^ Difftastic understands when whitespace matters, and when it's just
an indentation change.
(2) Difftastic understands which lines should be aligned. It's aligned
`bar(1)` on the left with `bar(2)` on the right, even though the
textual content isn't identical.
![Screenshot of difftastic and JS](img/reformat.png)
(3) Difftastic understands that line-wrapping isn't
meaningful. `"eric"` is now on a new line, but it hasn't changed.
^ Difftastic is not line-oriented. If you reformat your code and it's
now split over multiple lines, difftastic will show you what's
actually changed.
## One Minute Demo
![Screenshot of difftastic and git](img/git.png)
[![asciicast](https://asciinema.org/a/480875.svg)](https://asciinema.org/a/480875)
^ Difftastic is compatible with git (see [the configuration
instructions](http://difftastic.wilfred.me.uk/git.html)), as well as
many other version control systems.
This one minute screencast demonstrates difftastic usage with both
standalone files and git.
## Languages Supported
## Languages
Difftastic supports over 30 programming languages, see [the
manual](https://difftastic.wilfred.me.uk/languages_supported.html) for the full list.
If a file has an unrecognised extension, difftastic uses a
line-oriented diff with word highlighting.
textual diff with word highlighting.
## Known Issues
@ -74,6 +74,15 @@ merging.
## FAQ
### Isn't this basically `--word-diff --ignore-all-space`?
Word diffing [can't do
this](https://twitter.com/_wilfredh/status/1510139929971421191/photo/1).
Difftastic parses your code. It understands when whitespace matters,
such as inside string literals or languages like Python. It understands
that `x-1` is three tokens in JS but one token in Lisp.
### Can I use difftastic with git?
You can! The difftastic manual [includes instructions for git
@ -96,7 +105,7 @@ By default, difftastic falls back to a line-oriented diff whenever
parse errors are encountered.
This is a conservative choice to ensure that difftastic never claims
that two syntactically different files are the same.
two syntactically different files are the same.
Parse errors can occur if the file uses language features that the
parser does not understand, if the language relies on a preprocessor
@ -200,4 +209,4 @@ the `vendored_parsers/` directory. These are a mix of the MIT license and the
Apache license. See `vendored_parsers/*/LICENSE` for more details.
Files in `sample_files/` are also under the MIT license unless stated
otherwise in their headers.
otherwise in their header.

@ -67,16 +67,56 @@ impl TreeSitterParser {
fn main() {
let parsers = vec![
TreeSitterParser {
name: "tree-sitter-ada",
src_dir: "vendored_parsers/tree-sitter-ada-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-clojure",
src_dir: "vendored_parsers/tree-sitter-clojure-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-cmake",
src_dir: "vendored_parsers/tree-sitter-cmake-src",
extra_files: vec!["scanner.c"],
},
TreeSitterParser {
name: "tree-sitter-commonlisp",
src_dir: "vendored_parsers/tree-sitter-commonlisp-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-dart",
src_dir: "vendored_parsers/tree-sitter-dart-src",
extra_files: vec!["scanner.c"],
},
TreeSitterParser {
name: "tree-sitter-devicetree",
src_dir: "vendored_parsers/tree-sitter-devicetree-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-elisp",
src_dir: "vendored_parsers/tree-sitter-elisp-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-elm",
src_dir: "vendored_parsers/tree-sitter-elm-src",
extra_files: vec!["scanner.c"],
},
TreeSitterParser {
name: "tree-sitter-elvish",
src_dir: "vendored_parsers/tree-sitter-elvish-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-gleam",
src_dir: "vendored_parsers/tree-sitter-gleam-src",
extra_files: vec!["scanner.c"],
},
TreeSitterParser {
name: "tree-sitter-hack",
src_dir: "vendored_parsers/tree-sitter-hack-src",
@ -87,6 +127,11 @@ fn main() {
src_dir: "vendored_parsers/tree-sitter-hare-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-hcl",
src_dir: "vendored_parsers/tree-sitter-hcl-src",
extra_files: vec!["scanner.cc"],
},
TreeSitterParser {
name: "tree-sitter-janet-simple",
src_dir: "vendored_parsers/tree-sitter-janet-simple-src",
@ -102,21 +147,61 @@ fn main() {
src_dir: "vendored_parsers/tree-sitter-latex-src",
extra_files: vec!["scanner.c"],
},
TreeSitterParser {
name: "tree-sitter-newick",
src_dir: "vendored_parsers/tree-sitter-newick-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-perl",
src_dir: "vendored_parsers/tree-sitter-perl-src",
extra_files: vec!["scanner.c"],
},
TreeSitterParser {
name: "tree-sitter-qmljs",
src_dir: "vendored_parsers/tree-sitter-qmljs-src",
extra_files: vec!["scanner.c"],
},
TreeSitterParser {
name: "tree-sitter-r",
src_dir: "vendored_parsers/tree-sitter-r-src",
extra_files: vec!["scanner.cc"],
},
TreeSitterParser {
name: "tree-sitter-racket",
src_dir: "vendored_parsers/tree-sitter-racket-src",
extra_files: vec!["scanner.c"],
},
TreeSitterParser {
name: "tree-sitter-scheme",
src_dir: "vendored_parsers/tree-sitter-scheme-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-scss",
src_dir: "vendored_parsers/tree-sitter-scss-src",
extra_files: vec!["scanner.c"],
},
TreeSitterParser {
name: "tree-sitter-sfapex",
src_dir: "vendored_parsers/tree-sitter-sfapex-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-smali",
src_dir: "vendored_parsers/tree-sitter-smali-src",
extra_files: vec!["scanner.c"],
},
TreeSitterParser {
name: "tree-sitter-solidity",
src_dir: "vendored_parsers/tree-sitter-solidity-src",
extra_files: vec![],
},
TreeSitterParser {
name: "tree-sitter-sql",
src_dir: "vendored_parsers/tree-sitter-sql-src",
extra_files: vec!["scanner.cc"],
},
TreeSitterParser {
name: "tree-sitter-vhdl",
src_dir: "vendored_parsers/tree-sitter-vhdl-src",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

@ -1,15 +1,12 @@
default:
@just --list
# Build and serve the manual.
doc:
cd manual && mdbook serve --open
# Run the output regression test.
compare:
sample_files/compare_all.sh
# Create a git tag and push it, to trigger a release on GitHub actions.
release:
#!/bin/bash
@ -21,12 +18,10 @@ release:
cargo set-version --bump minor
# Serve the homepage locally.
home:
echo "http://localhost:8080"
cd homepage && python -m http.server 8080
# Generate release notes for the currently unreleased version.
rel_notes:
#!/bin/bash
@ -34,6 +29,5 @@ rel_notes:
rg --max-count 1 -B 9999 "released " CHANGELOG.md | tail -n +3 | head -n -2 | awk 'BEGIN{RS="\n\n"; ORS="\n\n"} {gsub(/\n/, " "); print}'
# Regenerate the man page difft.1 from diff.1.md.
man:
pandoc --standalone --to man difft.1.md -o difft.1

@ -10,44 +10,60 @@ parsers](https://tree-sitter.github.io/tree-sitter/#available-parsers).
## Add the source code
Ideally, the parser should be available as a Rust crate on crates.io.
If that's the case, add it to `Cargo.toml` in the alphabetically sorted list
of parser dependencies. For instance:
Once you've found a parser, add it as a git subtree to
`vendored_parsers/`. We'll use
[tree-sitter-json](https://github.com/tree-sitter/tree-sitter-json) as
an example.
```
$ git subtree add --prefix=vendored_parsers/tree-sitter-json https://github.com/tree-sitter/tree-sitter-json.git master
```
## Configure the build
Cargo does not allow packages to include subdirectories that contain a
`Cargo.toml`. Add a symlink to the `src/` parser subdirectory.
```
$ cd vendored_parsers
$ ln -s tree-sitter-json/src tree-sitter-json-src
```
You can now add the parser to build by including the directory in
`build.rs`.
```
tree-sitter-json = "0.24.8"
TreeSitterParser {
name: "tree-sitter-json",
src_dir: "vendored_parsers/tree-sitter-json-src",
extra_files: vec![],
},
```
Otherwise, it is possible to [vendor the parser in difftastic's source code](./parser_vendoring.md),
but this should only be used as a last resort.
If your parser includes custom C or C++ files for lexing (e.g. a
`scanner.cc`), add them to `extra_files`.
## Configure parsing
Add an entry to `tree_sitter_parser.rs` for your language.
```rust
```
Json => {
let language_fn = tree_sitter_json::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_json() };
TreeSitterConfig {
language,
atom_nodes: vec!["string"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("[", "]")],
highlight_query: ts::Query::new(language, tree_sitter_json::HIGHLIGHTS_QUERY)
.unwrap(),
highlight_query: ts::Query::new(
language,
include_str!("../../vendored_parsers/highlights/json.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
```
If the Rust crate does not include a `HIGHLIGHTS_QUERY`, then you need to include
it from a file instead, with
```
include_str!("../../vendored_parsers/highlights/json.scm")
```
Many parser repositories include a highlights query in the repository without
exposing it in the Rust crate. In that case you can include it as
`vendored_parsers/highlights/json.scm` in the repository.
`atom_nodes` is a list of tree-sitter node names that should be
treated as atoms even though the nodes have children. This is common
for things like string literals or interpolated strings, where the

@ -49,6 +49,7 @@ From the start vertex, we have two options:
+---------------------+ +---------------------+
```
Choosing "novel atom R" to vertex 2 will turn out to be the best
choice. From vertex 2, we can see three routes to the end vertex.

@ -1,15 +1,11 @@
# Installation
Difftastic can be installed as pre-built binaries or using various package managers.
Diffstatic can be installed as pre-built binaries or using various package managers.
## Pre-Built Binaries
Difftastic releases are published as [GitHub
releases](https://github.com/Wilfred/difftastic/releases) with
pre-built binaries for Windows, macOS and Linux. Open the [latest
release page](https://github.com/Wilfred/difftastic/releases/latest),
download the file matching your OS and CPU architecture, and extract
the `difft` executable application file.
Diffstatic releases are published as [GitHub releases](https://github.com/Wilfred/difftastic/releases) with pre-built binaries for Windows, macOS and Linux.
Open the [latest release page](https://github.com/Wilfred/difftastic/releases/latest), download the file matching your OS and CPU architecture, and extract the `difft` executable application file.
## Package Manager
@ -40,9 +36,7 @@ with `nix-env`.
$ nix-env --install difftastic
```
If you're a **Fedora** user, you can install
[difftastic](https://packages.fedoraproject.org/pkgs/rust-difftastic/difftastic/)
with `dnf`.
If you're a **Fedora** user, you can install [difftastic](https://packages.fedoraproject.org/pkgs/rust-difftastic/difftastic/) with `dnf`.
```
$ sudo dnf install difftastic
@ -58,8 +52,7 @@ $ sudo pkg install difftastic
### Windows
If you're a Windows user using **Windows Package Manager** (*WinGet*),
you can install difftastic with `winget`.
If you're a Windows user using **Windows Package Manager** (*WinGet*), you can install difftastic with `winget`.
```
$ winget install difftastic

@ -11,3 +11,5 @@ file](https://jj-vcs.github.io/jj/latest/config/#user-config-files).
[ui]
diff-formatter = ["difft", "--color=always", "$left", "$right"]
```

@ -14,10 +14,10 @@ with `difft --list-languages`.
| C | [tree-sitter/tree-sitter-c](https://github.com/tree-sitter/tree-sitter-c) |
| C++ | [tree-sitter/tree-sitter-cpp](https://github.com/tree-sitter/tree-sitter-cpp) |
| C# | [tree-sitter/tree-sitter-c-sharp](https://github.com/tree-sitter/tree-sitter-c-sharp) |
| Clojure | [grammar-orchard/tree-sitter-clojure-orchard](https://codeberg.org/grammar-orchard/tree-sitter-clojure-orchard) |
| Clojure | [sogaiu/tree-sitter-clojure](https://github.com/sogaiu/tree-sitter-clojure) |
| CMake | [uyha/tree-sitter-cmake](https://github.com/uyha/tree-sitter-cmake) |
| Common Lisp | [theHamsta/tree-sitter-commonlisp](https://github.com/theHamsta/tree-sitter-commonlisp) |
| Dart | [grammar-orchard/tree-sitter-dart-orchard](https://codeberg.org/grammar-orchard/tree-sitter-dart-orchard) |
| Dart | [UserNobody14/tree-sitter-dart](https://github.com/UserNobody14/tree-sitter-dart) |
| Device Tree | [joelspadin/tree-sitter-devicetree](https://github.com/joelspadin/tree-sitter-devicetree) |
| Elixir | [elixir-lang/tree-sitter-elixir](https://github.com/elixir-lang/tree-sitter-elixir) |
| Elm | [elm-tooling/tree-sitter-elm](https://github.com/elm-tooling/tree-sitter-elm) |
@ -48,29 +48,29 @@ with `difft --list-languages`.
| R | [r-lib/tree-sitter-r](https://github.com/r-lib/tree-sitter-r) |
| Racket | [6cdh/tree-sitter-racket](https://github.com/6cdh/tree-sitter-racket) |
| Ruby | [tree-sitter/tree-sitter-ruby](https://github.com/tree-sitter/tree-sitter-ruby) |
| Rust | [grammar-orchard/tree-sitter-rust-orchard](https://codeberg.org/grammar-orchard/tree-sitter-rust-orchard) |
| Rust | [tree-sitter/tree-sitter-rust](https://github.com/tree-sitter/tree-sitter-rust) |
| Scala | [tree-sitter/tree-sitter-scala](https://github.com/tree-sitter/tree-sitter-scala) |
| Scheme | [6cdh/tree-sitter-scheme](https://github.com/6cdh/tree-sitter-scheme) |
| Smali | [amaanq/tree-sitter-smali](https://github.com/amaanq/tree-sitter-smali) |
| Solidity | [JoranHonig/tree-sitter-solidity](https://github.com/JoranHonig/tree-sitter-solidity) |
| SQL | [derekstride/tree-sitter-sql](https://github.com/derekstride/tree-sitter-sql) |
| SQL | [m-novikov/tree-sitter-sql](https://github.com/m-novikov/tree-sitter-sql) |
| Swift | [alex-pinkus/tree-sitter-swift](https://github.com/alex-pinkus/tree-sitter-swift) |
| TypeScript, TSX | [tree-sitter/tree-sitter-typescript](https://github.com/tree-sitter/tree-sitter-typescript) |
| Verilog | [tree-sitter/tree-sitter-verilog](https://github.com/tree-sitter/tree-sitter-verilog) |
| VHDL | [JLeemaster/tree-sitter-vhdl](https://github.com/JLeemaster/tree-sitter-vhdl) |
| Zig | [tree-sitter-grammars/tree-sitter-zig](https://github.com/tree-sitter-grammars/tree-sitter-zig) |
| VHDL | [tree-sitter-grammars/tree-sitter-zig](https://github.com/tree-sitter-grammars/tree-sitter-zig) |
| Zig | [maxxnino/tree-sitter-zig](https://github.com/maxxnino/tree-sitter-zig) |
## Structured Text Formats
| Language | Parser Used |
|----------|---------------------------------------------------------------------------------------------------|
| CSS | [tree-sitter/tree-sitter-css](https://github.com/tree-sitter/tree-sitter-css) |
| HCL | [tree-sitter-grammars/tree-sitter-hcl](https://github.com/tree-sitter-grammars/tree-sitter-hcl) |
| HCL | [MichaHoffmann/tree-sitter-hcl](https://github.com/MichaHoffmann/tree-sitter-hcl) |
| HTML | [tree-sitter/tree-sitter-html](https://github.com/tree-sitter/tree-sitter-html) |
| JSON | [tree-sitter/tree-sitter-json](https://github.com/tree-sitter/tree-sitter-json) |
| LaTeX | [latex-lsp/tree-sitter-latex](https://github.com/latex-lsp/tree-sitter-latex) |
| Newick | [delehef/tree-sitter-newick](https://github.com/delehef/tree-sitter-newick) |
| Proto | [coder3101/tree-sitter-proto](https://github.com/coder3101/tree-sitter-proto) |
| SCSS | [serenadeai/tree-sitter-scss](https://github.com/serenadeai/tree-sitter-scss) |
| TOML | [tree-sitter-grammars/tree-sitter-toml](https://github.com/tree-sitter-grammars/tree-sitter-toml) |
| XML | [tree-sitter-grammars/tree-sitter-xml](https://github.com/tree-sitter-grammars/tree-sitter-xml) |

@ -40,9 +40,4 @@ hg dft -r .^ -r .
```
This is equivalent to `hg log -l 1 -p`, although it does not show the
commit message. I like to define an alias for this:
```
[alias]
dp = dft -r .^ -r .
```
commit message.

@ -2,44 +2,9 @@
## Git Subtrees
Tree-sitter parsers are sometimes not packaged on crates.io. In that case, Difftastic uses
git subtrees (not git submodules) to track them.
## Vendoring a parser
Once you've found the source repository for the parser, add it as a git subtree to
`vendored_parsers/`. We'll use
[tree-sitter-json](https://github.com/tree-sitter/tree-sitter-json) as
an example.
```
$ git subtree add --prefix=vendored_parsers/tree-sitter-json https://github.com/tree-sitter/tree-sitter-json.git master
```
### Configure the build
Cargo does not allow packages to include subdirectories that contain a
`Cargo.toml`. Add a symlink to the `src/` parser subdirectory.
```
$ cd vendored_parsers
$ ln -s tree-sitter-json/src tree-sitter-json-src
```
You can now add the parser to build by including the directory in
`build.rs`.
```
TreeSitterParser {
name: "tree-sitter-json",
src_dir: "vendored_parsers/tree-sitter-json-src",
extra_files: vec![],
},
```
If your parser includes custom C or C++ files for lexing (e.g. a
`scanner.cc`), add them to `extra_files`.
Tree-sitter parsers are sometimes packaged on npm, sometimes packaged
on crates.io, and have different release frequencies. Difftastic uses
git subtrees (not git submodules) to track parsers.
## Updating a parser

@ -1,4 +1,4 @@
[toolchain]
channel = "1.76.0"
channel = "1.75.0"
components = ["rustfmt"]
profile = "minimal"

@ -1,212 +1,209 @@
sample_files/Session_1.kt sample_files/Session_2.kt
fec9f5421c5bdb3cb485c2cfd2f336a6 -
a5899a29b0ebb0800923ac368b95fa1e -
sample_files/ada_1.adb sample_files/ada_2.adb
7498a465384992689732536fcf6ec19b -
eb3b0e12e239ae33789136380e81406b -
sample_files/added_line_1.txt sample_files/added_line_2.txt
8a1587e6b5fc53f2ec2ac665a5d00372 -
sample_files/align_footer_1.txt sample_files/align_footer_2.txt
c7f2c4dee1e99f8196ea7e20f7d77a0a -
0c2a263268f6c1dd27aa5e6aed686b6c -
sample_files/all_changed_1.js sample_files/all_changed_2.js
4699b292efa2eb4e52942047f1b45e98 -
6f6cbd4d2ed1045e3883143787dbd75b -
sample_files/apex_1.cls sample_files/apex_2.cls
8e477350907734ac4d5201752523dff3 -
sample_files/b2_math_1.h sample_files/b2_math_2.h
ea7421bd7970a6edf2897f8785053ce4 -
1ab24165d072f91be11893b92bc0943f -
sample_files/bad_combine_1.rs sample_files/bad_combine_2.rs
212325b38a05e90c7266161769b38477 -
b3539df794190eca6f6a3ac7c094d03b -
sample_files/big_text_hunk_1.txt sample_files/big_text_hunk_2.txt
fc26d41a5ff771670e04033b177973d2 -
sample_files/change_outer_1.el sample_files/change_outer_2.el
bbdc641dc39c0f26ec7f23e0558e24f2 -
13d4673474ea6c7ee980f8a955fff26b -
sample_files/chinese_1.po sample_files/chinese_2.po
d4b802f31506653916beadf88f628e06 -
a729587c647a4390573c86e95abe6263 -
sample_files/clojure_1.clj sample_files/clojure_2.clj
36c1ad5c83eda76c62042594eb302131 -
592b9f4e3bc12ede3075d23551074b3e -
sample_files/comma_1.js sample_files/comma_2.js
3aa1c67b64bb1d71b0b7b575922e77b9 -
a4e3564f6e336fead6eb0410496c20f1 -
sample_files/comma_and_comment_1.js sample_files/comma_and_comment_2.js
652b099094968e79706381fc38e82686 -
0a5ccbcd368607e62eaff0c4ae25049f -
sample_files/comments_1.rs sample_files/comments_2.rs
ce36820fa38dbcb450c1643df1560ed2 -
a75827163654d6aed8f837bb586e733c -
sample_files/context_1.rs sample_files/context_2.rs
66106fdd1f4c3c2981a46067ece843d1 -
e59180fa7029db7939222cb61c95c97c -
sample_files/contiguous_1.js sample_files/contiguous_2.js
7ac6081dee02e3529a88defca194bc00 -
22da862378dbee68e55ecba8b0fcec42 -
sample_files/css_1.css sample_files/css_2.css
0138ec9e44f3b792a7a47d89509c6a1d -
3d911704ac4d76697c748e1dcd40eece -
sample_files/dart_1.dart sample_files/dart_2.dart
f44f0dffe3617ddafca3d65e94a21613 -
107d073c2922ff14e9ba66ee0f489592 -
sample_files/devicetree_1.dts sample_files/devicetree_2.dts
64b33c6d65dbc85777ee30cf3893fbdb -
f7c4e7b4444b02d87b2eec1485d86211 -
sample_files/elisp_1.el sample_files/elisp_2.el
98571b62f7407e90e9441e4f47ea85ae -
74b5c1cd11a21bbba2bcbc9493f7e46a -
sample_files/elisp_contiguous_1.el sample_files/elisp_contiguous_2.el
4a4daef3b6aadea5d238c5b5c0c3afa0 -
4a5a33873a4f84ee055d95e1448fba35 -
sample_files/elixir_1.ex sample_files/elixir_2.ex
bb1a88ba037f6ed68040b9691a02ee6f -
85494310196ac5065b3b4ce1d4b350fd -
sample_files/elm_1.elm sample_files/elm_2.elm
4a6035591e50773f2e9a5931404e42fc -
ccc1f4bb568cd72781dbcd623b612c43 -
sample_files/elvish_1.elv sample_files/elvish_2.elv
cd2208fc3a47ecb1a1a04f104eeb8186 -
93af1d46752d57db84011ca7482ae842 -
sample_files/erlang_1.erl sample_files/erlang_2.erl
268056b80b3311309a17a6c5485f68cd -
4c3e2bd1f0918ed339e87a5f8e0b05cb -
sample_files/f_sharp_1.fs sample_files/f_sharp_2.fs
ce8779ecb5ada66df366fef11dcba490 -
25253b18ef64469c67cf9a7131c8c125 -
sample_files/hack_1.php sample_files/hack_2.php
c2bb0aa7d7b07d6ced79f6a5363e878b -
sample_files/hare_1.ha sample_files/hare_2.ha
84adbdd8f6994e3e7bcde55910dbe119 -
ef6fd59edc55241311a97d21dd81e4c0 -
sample_files/haskell_1.hs sample_files/haskell_2.hs
151ad80d5a9f6558a19eb1e09266ffad -
68fd7f9865c2b1defe05ffd509e08b93 -
sample_files/hcl_1.hcl sample_files/hcl_2.hcl
b85eac8adf20ef74bec9fd5e5358a976 -
7c2aaa3a8b401bc007817f5dd608946d -
sample_files/hello_world_1.smali sample_files/hello_world_2.smali
bb8463618fcb7f263bd2dafeef7a8b06 -
c9acab2700d02e2898431523e2a581b9 -
sample_files/helpful_1.el sample_files/helpful_2.el
6eda1c77cb5dc13bc321e90e41a6b64d -
056471124ae2e582942e214ce9222477 -
sample_files/html_1.html sample_files/html_2.html
2b873fd1ea8f77d2463ceb08aa23967c -
3cc8b445a56b74f05e1d7bb84874edab -
sample_files/html_simple_1.html sample_files/html_simple_2.html
6bd7bd0b72504d4c637c7f5aa9ef068d -
bb129dce38cd26eac81ca52d2016bade -
sample_files/huge_cpp_1.cpp sample_files/huge_cpp_2.cpp
05d96cce94db8ebdfaf4f2d52239470c -
sample_files/hyphen_subwords_1.json sample_files/hyphen_subwords_2.json
7cb9167b72fb98a8f5fa6c67d032edb5 -
47e26aa5975cd57344ccbf9f366466ec -
sample_files/identical_1.scala sample_files/identical_2.scala
15c5a789e644348cb7e0de051ff4b63e -
sample_files/if_1.py sample_files/if_2.py
5a673245452b4cf845ef80c7c75cf480 -
a30c2b8952c48c775d7339b35614afd8 -
sample_files/insert_blank_1.txt sample_files/insert_blank_2.txt
a5fd75afcc99aa7b2b285f1f9ced8607 -
sample_files/janet_1.janet sample_files/janet_2.janet
7dc0f1a3ce49f489fa4b64edefebb3c5 -
15071de26554028495ed6b78de5f8803 -
sample_files/java_1.java sample_files/java_2.java
79f72d4a8d88540c87a99ce8f294d0e0 -
028c9176ac031eb3859f3d04bc43ed10 -
sample_files/javascript_1.js sample_files/javascript_2.js
7fc853454d1133da215c646732ac67f3 -
af4e9343168d88afd88adbba3ccc0373 -
sample_files/javascript_simple_1.js sample_files/javascript_simple_2.js
4ae99bc6abbcdd379cd8798f46b288ee -
449a5a824892da502f7211714214684e -
sample_files/json_1.json sample_files/json_2.json
9e95d50269a3d9b23b0e2f20a80f2f18 -
133c9224b47a5ee9940536a6ad70fd2e -
sample_files/jsx_1.jsx sample_files/jsx_2.jsx
8dd6005aa6fbe816bef3fdd89908f1c0 -
712ef0cf61b469bbacc84b53b944e5ff -
sample_files/julia_1.jl sample_files/julia_2.jl
4301a933fbd272df7d05cef357533ae6 -
55aa779597d1f6afa681af267706c9b6 -
sample_files/load_1.js sample_files/load_2.js
2e47b7908951ff8390058856edbaf715 -
8defc3cea4d10a8db826973352abe2a1 -
sample_files/long_line_1.txt sample_files/long_line_2.txt
7fc50bd547f0c20fda89a1931e5eb61e -
sample_files/lua_1.lua sample_files/lua_2.lua
5182b3ce10f680f8d506af6e4dfb2dc1 -
81ad9478e64494320e96284cb7632ced -
sample_files/makefile_1.mk sample_files/makefile_2.mk
1a132d165294d0325f9b0dae5569a4d0 -
d0572210b5121ce68ac0ce45e43b922b -
sample_files/many_newlines_1.txt sample_files/many_newlines_2.txt
52ca05855e520876479e6f608c5e7831 -
sample_files/metadata_1.clj sample_files/metadata_2.clj
b788a546c4ccc5662e98ed30473dd776 -
4b58ce366467c8cca46db53508e81323 -
sample_files/modules_1.ml sample_files/modules_2.ml
fabe617d18d24317bf38557b1455fb6e -
b29a0d9427b7f5a9450163a9f1ba7f49 -
sample_files/multibyte_1.py sample_files/multibyte_2.py
3d14774cd9d92968ce0b3be1b26ccaa1 -
f761255d521267ace4f4887a21664a12 -
sample_files/multiline_string_1.ml sample_files/multiline_string_2.ml
f0f106fac68313863fe78a6e8ba03e30 -
ed80815053ba156505d156277d0f4195 -
sample_files/multiline_string_eof_1.yml sample_files/multiline_string_eof_2.yml
ce9de81e32672d245991446b72c8cd15 -
ba8a8e7ed2f4b519feaa391fd05c95fe -
sample_files/nest_1.rs sample_files/nest_2.rs
bb3c526d9400776ffae5bf78631fa0c3 -
d3a799fe2cd9d81aa251c96af5cd9711 -
sample_files/nested_slider_1.el sample_files/nested_slider_2.el
81f2384d51c952b78294470d7bf205d7 -
f68f8b8c09afb86965d5e54519f2d881 -
sample_files/nested_slider_1.rs sample_files/nested_slider_2.rs
342df46db959e8c90925ff4400c0fa63 -
sample_files/nesting_1.el sample_files/nesting_2.el
f5c878d2f4db639c1180cc62977962d3 -
cf32f6b798a080a2bf14a430337ac4a6 -
sample_files/newick_1.nwk sample_files/newick_2.nwk
bbc830fc20d60c5fdca7a99edc3fde78 -
45ec08ce924513fb24846b9609d3cbe8 -
sample_files/nix_1.nix sample_files/nix_2.nix
6ba9f4016488b70ccb88fc3fa930d069 -
486bb1ac5a844c41ca6bd59003c71c18 -
sample_files/nullable_1.kt sample_files/nullable_2.kt
ec439ca9b48e2db90d96fdfa01f060c2 -
d0a51e7201cc16dc6bcb99cbad64f6be -
sample_files/objc_header_1.h sample_files/objc_header_2.h
37267814a3d241a8f1497ef3239b360c -
0c6b6736a646246a502238b4aa4adb37 -
sample_files/objc_module_1.m sample_files/objc_module_2.m
63d3b15c53dfcb54c5cf6ff10c22a965 -
f4a376b78a73c190dc91b39d739490a5 -
sample_files/ocaml_1.ml sample_files/ocaml_2.ml
1c13a1d098aca58e04fad56240d0676f -
20586eb6dedffb55c7a9e264ed3739c9 -
sample_files/outer_delimiter_1.el sample_files/outer_delimiter_2.el
971c3ddebfae222ded24f7a97ec7b5d7 -
a7e206f6391237be0ce8ed244ec3dd62 -
sample_files/pascal_1.pascal sample_files/pascal_2.pascal
c292c1128698aa0f41b1c5cc443c6246 -
acc46c16e83dd1b48c6f761e59541923 -
sample_files/perl_1.pl sample_files/perl_2.pl
487bc10eb2915c2ede654e747d0598a0 -
9bb6ae2a12bc0debfde17013ed190344 -
sample_files/prefer_outer_1.el sample_files/prefer_outer_2.el
991038c9988cccc2c824652e33f772a2 -
@ -215,110 +212,107 @@ sample_files/preprocessor_1.h sample_files/preprocessor_2.h
d94f452bbf7cb3accc8d538fdd7ab5e4 -
sample_files/qml_1.qml sample_files/qml_2.qml
81680162af9cf1502fcf91c8d74f6a30 -
41a0432c03b87ad59fc8c942d83b20b5 -
sample_files/r_1.R sample_files/r_2.R
77c29d0589e7f97fabcaa1e044d7b72b -
10f45a80a8554419bf30a2a0f574ab86 -
sample_files/racket_1.rkt sample_files/racket_2.rkt
6cdfb181c2e78ec7802ec717fd346e54 -
b017e169d9fc79336fd7ef82140fe8a7 -
sample_files/repeated_line_no_eol_1.txt sample_files/repeated_line_no_eol_2.txt
b63c743f2133480de3ba42ecdec5eb93 -
3786f55d2c9b1897e866b4602e50408d -
sample_files/ruby_1.rb sample_files/ruby_2.rb
a0a7121421e3d57acadc08519275dcce -
d4d591902030355656f5c18c78f965a6 -
sample_files/scala_1.scala sample_files/scala_2.scala
ceb94b1cd83e469ad6d33e4dc04a181f -
13181dc30f15675747ce5315cd8073e6 -
sample_files/scheme_1.scm sample_files/scheme_2.scm
2d18a48f788361085a05812c2446de91 -
09f8683219a8491c22d9a2fa1ce98c16 -
sample_files/simple_1.js sample_files/simple_2.js
d9ac04ab946e1da5907b2a1bcaceefd2 -
1d49d0e440067284015eb5621f4a6adf -
sample_files/simple_1.scss sample_files/simple_2.scss
265261e79df78abfc09392d72a0273d8 -
sample_files/simple_1.txt sample_files/simple_2.txt
f1881e5482f1ccfd8f3f45c7c63454e8 -
60e62ad60b18c754acd99aeb0ac2120e -
sample_files/slider_1.rs sample_files/slider_2.rs
ebdc9db32892d1d74f7e745f45fbaba6 -
d10128f3d9ffc4a8670f417a9371bacc -
sample_files/slider_at_end_1.json sample_files/slider_at_end_2.json
6c994cd99226f6584f74f531bb27d54f -
cb370f1c0ccc5e155743330629f899f0 -
sample_files/slow_1.rs sample_files/slow_2.rs
4eabdb287f5fa31f5765117f4874d218 -
7a74881e854d68763769991c6445698a -
sample_files/small_1.js sample_files/small_2.js
52f1691c1584f45317bfe3f650b91c78 -
sample_files/sql_1.sql sample_files/sql_2.sql
801c1ca96eeb9ed8ed1ec7ebd1848ca0 -
42506285003bb4eacdb2f8d3bd1b07bb -
sample_files/string_subwords_1.el sample_files/string_subwords_2.el
0bd477e796f418d0a6d66445d2b16e99 -
b66e960672189960c2d35ef68b47a195 -
sample_files/strings_1.el sample_files/strings_2.el
ae3b58de9c115cac8a1044e6cdf77723 -
7e136d188ce03cc8fba2b1530a502ffc -
sample_files/swift_1.swift sample_files/swift_2.swift
7fb0c9ebfc5f50b4936e224b089315d2 -
73830b14bd8aacac8d4590a3bed61c40 -
sample_files/syntax_error_1.js sample_files/syntax_error_2.js
bccd98e073dbc059c5ca7cadaa20166f -
5bbd81f688bdcfbdbdf69d870f0c1357 -
sample_files/tab_1.c sample_files/tab_2.c
8b078aa89c662cc6fb9dabf6230ed5e7 -
a7f9bce0ca76ce356413fb218d82d686 -
sample_files/tab_1.txt sample_files/tab_2.txt
10e9a853917dc9d92d2867e3453269b2 -
a17e978720fe4c1b25614402910cc695 -
sample_files/tailwind_1.css sample_files/tailwind_2.css
8f0a090a15ab889a011003586d49153f -
132f217659bf9dac8b82feabf5643f47 -
sample_files/text_1.txt sample_files/text_2.txt
94c6a7e71c05ddc501c6961d5b833188 -
f8f81946284fc625ac8e9ae441f31e6d -
sample_files/todomvc_1.gleam sample_files/todomvc_2.gleam
725bb793f219e13cee199f18ffaed872 -
6b3b8c9f1e813617819b97144cc5cc4a -
sample_files/toml_1.toml sample_files/toml_2.toml
99fbf305557f02635bbbcee82eb79cb7 -
c331bdb54c00b0a5b5a622dbd250042a -
sample_files/trailling_newline_1.yaml sample_files/trailling_newline_2.yaml
4b5c2b18de01c637e1d618b9132f8af8 -
8e37febfec957288576f9c2020cfc4f2 -
sample_files/typescript_1.ts sample_files/typescript_2.ts
e15ceae53da7cfb308960f8c3f30b891 -
fee7ee33d2037ad1941ba6bb5532a1db -
sample_files/typing_1.ml sample_files/typing_2.ml
ae41c3f5aa1ba13c2a424d3afa7ece6c -
975a97f2a6798abd1e4c0ce219e183fd -
sample_files/utf16_1.py sample_files/utf16_2.py
b1325e96c07b1e02c043b511e5a628cb -
3bcee785d4cebc38a42eafe49a198b52 -
sample_files/verilog_1.sv sample_files/verilog_2.sv
6d7e8ce7170de5a22a25c13d79d1f9b4 -
2f59632b5a4e5addfa7b4d5274da72cc -
sample_files/vhdl_1.vhd sample_files/vhdl_2.vhd
5cfe89ec17acce6bf4c70be434678417 -
ca98b4d14fc21e0f04cf24aeb3d2526c -
sample_files/whitespace_1.tsx sample_files/whitespace_2.tsx
789689b170b80ef280a322b3a6c7363b -
ac8b1a89ac26333f2d4e9433b2ca3958 -
sample_files/windows1251_1.txt sample_files/windows1251_2.txt
bd958b46e63436e5af93ab1304cffffb -
sample_files/windows_1251_1.txt sample_files/windows_2251_1.txt
d41d8cd98f00b204e9800998ecf8427e -
sample_files/xml_1.xml sample_files/xml_2.xml
8d5309af57457c2d56e96a09b697b609 -
e629cbd2e721fd249c7ce1626f17e953 -
sample_files/yaml_1.yaml sample_files/yaml_2.yaml
f02cabd5170b39f6bad5cfc4d8e84205 -
521a2075e4f8b0ea9fb74436e0445b52 -
sample_files/zig_1.zig sample_files/zig_2.zig
f3f718b83570f3245b4464b50d711a3d -
2cb2ce5bcb62c02a29da7ce8a33c6265 -

@ -1,3 +0,0 @@
{
"name": "foo-d123-pretty-long"
}

@ -1,3 +0,0 @@
{
"name": "d123-pretty-long"
}

@ -1,10 +0,0 @@
-- A comment
create table apples (
variety varchar primary key not null,
ancestor varchar references apples (path));
create table pears (
name varchar primary key not null,
origin varchar not null,
ancestor varchar references pears (path) not null);

@ -1,11 +0,0 @@
-- A comment
create table apples (
variety varchar primary key not null,
ancestor varchar not null);
create table pears (
name varchar primary key not null,
origin varchar not null,
price float,
ancestor varchar references pears (path) not null);

@ -7,9 +7,6 @@ use crate::{
#[derive(PartialEq, Eq, Clone, Copy)]
pub(crate) enum ChangeKind<'a> {
/// This node is shallowly unchanged. For lists, this means that
/// the delimiters match, but there may still be some differences
/// in the children between LHS and RHS.
Unchanged(&'a Syntax<'a>),
ReplacedComment(&'a Syntax<'a>, &'a Syntax<'a>),
ReplacedString(&'a Syntax<'a>, &'a Syntax<'a>),

@ -17,23 +17,23 @@ use crate::{
pub(crate) struct ExceededGraphLimit {}
/// Return the shortest route from `start` to the end vertex.
fn shortest_vertex_path<'s, 'v>(
start: &'v Vertex<'s, 'v>,
vertex_arena: &'v Bump,
fn shortest_vertex_path<'s, 'b>(
start: &'b Vertex<'s, 'b>,
vertex_arena: &'b Bump,
size_hint: usize,
graph_limit: usize,
) -> Result<Vec<&'v Vertex<'s, 'v>>, ExceededGraphLimit> {
) -> Result<Vec<&'b Vertex<'s, 'b>>, ExceededGraphLimit> {
// We want to visit nodes with the shortest distance first, but
// RadixHeapMap is a max-heap. Ensure nodes are wrapped with
// Reverse to flip comparisons.
let mut heap: RadixHeapMap<Reverse<_>, &'v Vertex<'s, 'v>> = RadixHeapMap::new();
let mut heap: RadixHeapMap<Reverse<_>, &'b Vertex<'s, 'b>> = RadixHeapMap::new();
heap.push(Reverse(0), start);
let mut seen = DftHashMap::default();
seen.reserve(size_hint);
let end: &'v Vertex<'s, 'v> = loop {
let end: &'b Vertex<'s, 'b> = loop {
match heap.pop() {
Some((Reverse(distance), current)) => {
if current.is_end() {
@ -77,7 +77,7 @@ fn shortest_vertex_path<'s, 'v>(
);
let mut current = Some((0, end));
let mut vertex_route: Vec<&'v Vertex<'s, 'v>> = vec![];
let mut vertex_route: Vec<&'b Vertex<'s, 'b>> = vec![];
while let Some((_, node)) = current {
vertex_route.push(node);
current = node.predecessor.get();
@ -87,9 +87,9 @@ fn shortest_vertex_path<'s, 'v>(
Ok(vertex_route)
}
fn shortest_path_with_edges<'s, 'v>(
route: &[&'v Vertex<'s, 'v>],
) -> Vec<(Edge, &'v Vertex<'s, 'v>)> {
fn shortest_path_with_edges<'s, 'b>(
route: &[&'b Vertex<'s, 'b>],
) -> Vec<(Edge, &'b Vertex<'s, 'b>)> {
let mut prev = route.first().expect("Expected non-empty route");
let mut cost = 0;
@ -111,18 +111,18 @@ fn shortest_path_with_edges<'s, 'v>(
///
/// The vec returned does not return the very last vertex. This is
/// necessary because a route of N vertices only has N-1 edges.
fn shortest_path<'s, 'v>(
start: Vertex<'s, 'v>,
vertex_arena: &'v Bump,
fn shortest_path<'s, 'b>(
start: Vertex<'s, 'b>,
vertex_arena: &'b Bump,
size_hint: usize,
graph_limit: usize,
) -> Result<Vec<(Edge, &'v Vertex<'s, 'v>)>, ExceededGraphLimit> {
let start: &'v Vertex<'s, 'v> = vertex_arena.alloc(start);
) -> Result<Vec<(Edge, &'b Vertex<'s, 'b>)>, ExceededGraphLimit> {
let start: &'b Vertex<'s, 'b> = vertex_arena.alloc(start);
let vertex_path = shortest_vertex_path(start, vertex_arena, size_hint, graph_limit)?;
Ok(shortest_path_with_edges(&vertex_path))
}
fn edge_between<'s, 'v>(before: &Vertex<'s, 'v>, after: &Vertex<'s, 'v>) -> Edge {
fn edge_between<'s, 'b>(before: &Vertex<'s, 'b>, after: &Vertex<'s, 'b>) -> Edge {
assert_ne!(before, after);
let mut shortest_edge: Option<Edge> = None;
@ -156,20 +156,33 @@ fn edge_between<'s, 'v>(before: &Vertex<'s, 'v>, after: &Vertex<'s, 'v>) -> Edge
/// What is the total number of AST nodes?
fn node_count(root: Option<&Syntax>) -> u32 {
let iter = std::iter::successors(root, |node| node.next_sibling());
iter.map(|node| match node {
Syntax::List {
num_descendants, ..
} => *num_descendants,
Syntax::Atom { .. } => 1,
})
.sum::<u32>()
let mut node = root;
let mut count = 0;
while let Some(current_node) = node {
let current_count = match current_node {
Syntax::List {
num_descendants, ..
} => *num_descendants,
Syntax::Atom { .. } => 1,
};
count += current_count;
node = current_node.next_sibling();
}
count
}
/// How many top-level AST nodes do we have?
fn tree_count(root: Option<&Syntax>) -> u32 {
std::iter::successors(root, |node| node.next_sibling()).count() as _
let mut node = root;
let mut count = 0;
while let Some(current_node) = node {
count += 1;
node = current_node.next_sibling();
}
count
}
pub(crate) fn mark_syntax<'a>(

@ -50,18 +50,15 @@ use crate::{
/// LHS: X A RHS: A
/// ^ ^
/// ```
///
/// Vertices are arena allocated (the 'v lifetime) and have references
/// to syntax nodes (the 's lifetime).
#[derive(Debug, Clone)]
pub(crate) struct Vertex<'s, 'v> {
pub(crate) neighbours: RefCell<Option<&'v [(Edge, &'v Vertex<'s, 'v>)]>>,
pub(crate) predecessor: Cell<Option<(u32, &'v Vertex<'s, 'v>)>>,
pub(crate) struct Vertex<'s, 'b> {
pub(crate) neighbours: RefCell<Option<&'b [(Edge, &'b Vertex<'s, 'b>)]>>,
pub(crate) predecessor: Cell<Option<(u32, &'b Vertex<'s, 'b>)>>,
// TODO: experiment with storing SyntaxId only, and have a HashMap
// from SyntaxId to &Syntax.
pub(crate) lhs_syntax: Option<&'s Syntax<'s>>,
pub(crate) rhs_syntax: Option<&'s Syntax<'s>>,
parents: Stack<'v, EnteredDelimiter<'s, 'v>>,
parents: Stack<'b, EnteredDelimiter<'s, 'b>>,
lhs_parent_id: Option<SyntaxId>,
rhs_parent_id: Option<SyntaxId>,
}
@ -119,12 +116,12 @@ impl Hash for Vertex<'_, '_> {
/// Tracks entering syntax List nodes.
#[derive(Clone, PartialEq)]
enum EnteredDelimiter<'s, 'v> {
enum EnteredDelimiter<'s, 'b> {
/// If we've entered the LHS or RHS separately, we can pop either
/// side independently.
///
/// Assumes that at least one stack is non-empty.
PopEither((Stack<'v, &'s Syntax<'s>>, Stack<'v, &'s Syntax<'s>>)),
PopEither((Stack<'b, &'s Syntax<'s>>, Stack<'b, &'s Syntax<'s>>)),
/// If we've entered the LHS and RHS together, we must pop both
/// sides together too. Otherwise we'd consider the following case to have no changes.
///
@ -138,25 +135,25 @@ enum EnteredDelimiter<'s, 'v> {
impl fmt::Debug for EnteredDelimiter<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let desc = match self {
Self::PopEither((lhs_delims, rhs_delims)) => {
EnteredDelimiter::PopEither((lhs_delims, rhs_delims)) => {
format!(
"PopEither(lhs count: {}, rhs count: {})",
lhs_delims.size(),
rhs_delims.size()
)
}
Self::PopBoth(_) => "PopBoth".to_owned(),
EnteredDelimiter::PopBoth(_) => "PopBoth".to_owned(),
};
f.write_str(&desc)
}
}
fn push_both_delimiters<'s, 'v>(
entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
fn push_both_delimiters<'s, 'b>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
lhs_delim: &'s Syntax<'s>,
rhs_delim: &'s Syntax<'s>,
alloc: &'v Bump,
) -> Stack<'v, EnteredDelimiter<'s, 'v>> {
alloc: &'b Bump,
) -> Stack<'b, EnteredDelimiter<'s, 'b>> {
entered.push(EnteredDelimiter::PopBoth((lhs_delim, rhs_delim)), alloc)
}
@ -164,12 +161,12 @@ fn can_pop_either_parent(entered: &Stack<EnteredDelimiter>) -> bool {
matches!(entered.peek(), Some(EnteredDelimiter::PopEither(_)))
}
fn try_pop_both<'s, 'v>(
entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
fn try_pop_both<'s, 'b>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
) -> Option<(
&'s Syntax<'s>,
&'s Syntax<'s>,
Stack<'v, EnteredDelimiter<'s, 'v>>,
Stack<'b, EnteredDelimiter<'s, 'b>>,
)> {
match entered.peek() {
Some(EnteredDelimiter::PopBoth((lhs_delim, rhs_delim))) => {
@ -179,10 +176,10 @@ fn try_pop_both<'s, 'v>(
}
}
fn try_pop_lhs<'s, 'v>(
entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
alloc: &'v Bump,
) -> Option<(&'s Syntax<'s>, Stack<'v, EnteredDelimiter<'s, 'v>>)> {
fn try_pop_lhs<'s, 'b>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
alloc: &'b Bump,
) -> Option<(&'s Syntax<'s>, Stack<'b, EnteredDelimiter<'s, 'b>>)> {
match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => match lhs_delims.peek() {
Some(lhs_delim) => {
@ -204,10 +201,10 @@ fn try_pop_lhs<'s, 'v>(
}
}
fn try_pop_rhs<'s, 'v>(
entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
alloc: &'v Bump,
) -> Option<(&'s Syntax<'s>, Stack<'v, EnteredDelimiter<'s, 'v>>)> {
fn try_pop_rhs<'s, 'b>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
alloc: &'b Bump,
) -> Option<(&'s Syntax<'s>, Stack<'b, EnteredDelimiter<'s, 'b>>)> {
match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => match rhs_delims.peek() {
Some(rhs_delim) => {
@ -229,11 +226,11 @@ fn try_pop_rhs<'s, 'v>(
}
}
fn push_lhs_delimiter<'s, 'v>(
entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
fn push_lhs_delimiter<'s, 'b>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
delimiter: &'s Syntax<'s>,
alloc: &'v Bump,
) -> Stack<'v, EnteredDelimiter<'s, 'v>> {
alloc: &'b Bump,
) -> Stack<'b, EnteredDelimiter<'s, 'b>> {
match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => entered.pop().unwrap().push(
EnteredDelimiter::PopEither((lhs_delims.push(delimiter, alloc), rhs_delims.clone())),
@ -246,11 +243,11 @@ fn push_lhs_delimiter<'s, 'v>(
}
}
fn push_rhs_delimiter<'s, 'v>(
entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
fn push_rhs_delimiter<'s, 'b>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>,
delimiter: &'s Syntax<'s>,
alloc: &'v Bump,
) -> Stack<'v, EnteredDelimiter<'s, 'v>> {
alloc: &'b Bump,
) -> Stack<'b, EnteredDelimiter<'s, 'b>> {
match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => entered.pop().unwrap().push(
EnteredDelimiter::PopEither((lhs_delims.clone(), rhs_delims.push(delimiter, alloc))),
@ -263,7 +260,7 @@ fn push_rhs_delimiter<'s, 'v>(
}
}
impl<'s, 'v> Vertex<'s, 'v> {
impl<'s, 'b> Vertex<'s, 'b> {
pub(crate) fn is_end(&self) -> bool {
self.lhs_syntax.is_none() && self.rhs_syntax.is_none() && self.parents.is_empty()
}
@ -366,11 +363,11 @@ impl Edge {
}
}
fn allocate_if_new<'s, 'v>(
v: Vertex<'s, 'v>,
alloc: &'v Bump,
seen: &mut DftHashMap<&Vertex<'s, 'v>, SmallVec<[&'v Vertex<'s, 'v>; 2]>>,
) -> &'v Vertex<'s, 'v> {
fn allocate_if_new<'s, 'b>(
v: Vertex<'s, 'b>,
alloc: &'b Bump,
seen: &mut DftHashMap<&Vertex<'s, 'b>, SmallVec<[&'b Vertex<'s, 'b>; 2]>>,
) -> &'b Vertex<'s, 'b> {
// We use the entry API so that we only need to do a single lookup
// for access and insert.
match seen.raw_entry_mut().from_key(&v) {
@ -407,7 +404,7 @@ fn allocate_if_new<'s, 'v>(
//
// We still use a vec to enable experiments with the value
// of how many possible parenthesis nestings to explore.
let existing: SmallVec<[&'v Vertex<'s, 'v>; 2]> = smallvec![&*allocated];
let existing: SmallVec<[&'b Vertex<'s, 'b>; 2]> = smallvec![&*allocated];
vacant.insert(allocated, existing);
allocated
@ -428,19 +425,19 @@ fn looks_like_punctuation(node: &Syntax) -> bool {
/// Pop as many parents of `lhs_node` and `rhs_node` as
/// possible. Return the new syntax nodes and parents.
fn pop_all_parents<'s, 'v>(
fn pop_all_parents<'s, 'b>(
lhs_node: Option<&'s Syntax<'s>>,
rhs_node: Option<&'s Syntax<'s>>,
lhs_parent_id: Option<SyntaxId>,
rhs_parent_id: Option<SyntaxId>,
parents: &Stack<'v, EnteredDelimiter<'s, 'v>>,
alloc: &'v Bump,
parents: &Stack<'b, EnteredDelimiter<'s, 'b>>,
alloc: &'b Bump,
) -> (
Option<&'s Syntax<'s>>,
Option<&'s Syntax<'s>>,
Option<SyntaxId>,
Option<SyntaxId>,
Stack<'v, EnteredDelimiter<'s, 'v>>,
Stack<'b, EnteredDelimiter<'s, 'b>>,
) {
let mut lhs_node = lhs_node;
let mut rhs_node = rhs_node;
@ -496,10 +493,10 @@ fn pop_all_parents<'s, 'v>(
/// Compute the neighbours of `v` if we haven't previously done so,
/// and write them to the .neighbours cell inside `v`.
pub(crate) fn set_neighbours<'s, 'v>(
v: &Vertex<'s, 'v>,
alloc: &'v Bump,
seen: &mut DftHashMap<&Vertex<'s, 'v>, SmallVec<[&'v Vertex<'s, 'v>; 2]>>,
pub(crate) fn set_neighbours<'s, 'b>(
v: &Vertex<'s, 'b>,
alloc: &'b Bump,
seen: &mut DftHashMap<&Vertex<'s, 'b>, SmallVec<[&'b Vertex<'s, 'b>; 2]>>,
) {
if v.neighbours.borrow().is_some() {
return;
@ -799,8 +796,8 @@ pub(crate) fn set_neighbours<'s, 'v>(
.replace(Some(alloc.alloc_slice_copy(neighbours.as_slice())));
}
pub(crate) fn populate_change_map<'s, 'v>(
route: &[(Edge, &'v Vertex<'s, 'v>)],
pub(crate) fn populate_change_map<'s, 'b>(
route: &[(Edge, &'b Vertex<'s, 'b>)],
change_map: &mut ChangeMap<'s>,
) {
for (e, v) in route {

@ -24,11 +24,11 @@ impl<'b, T> Stack<'b, T> {
self.head.map(|n| &n.val)
}
pub(crate) fn pop(&self) -> Option<Self> {
pub(crate) fn pop(&self) -> Option<Stack<'b, T>> {
self.head.map(|n| Self { head: n.next })
}
pub(crate) fn push(&self, v: T, alloc: &'b Bump) -> Self {
pub(crate) fn push(&self, v: T, alloc: &'b Bump) -> Stack<'b, T> {
Self {
head: Some(alloc.alloc(Node {
val: v,
@ -39,7 +39,13 @@ impl<'b, T> Stack<'b, T> {
// O(n)
pub(crate) fn size(&self) -> usize {
std::iter::successors(self.head, |&n| n.next).count()
let mut count = 0;
let mut node = &self.head;
while let Some(next) = node {
count += 1;
node = &next.next;
}
count
}
pub(crate) fn is_empty(&self) -> bool {

@ -6,7 +6,7 @@ use std::hash::Hash;
use crate::diff::changes::{insert_deep_unchanged, ChangeKind, ChangeMap};
use crate::diff::lcs_diff;
use crate::hash::DftHashSet;
use crate::parse::syntax::{ContentId, Syntax};
use crate::parse::syntax::Syntax;
const TINY_TREE_THRESHOLD: u32 = 10;
const MOSTLY_UNCHANGED_MIN_COMMON_CHILDREN: usize = 4;
@ -123,7 +123,7 @@ fn split_unchanged_singleton_list<'a>(
res
}
fn find_unique_content_ids(node: &Syntax, unique_ids: &mut DftHashSet<ContentId>) {
fn find_unique_content_ids(node: &Syntax, unique_ids: &mut DftHashSet<u32>) {
if node.content_is_unique() {
unique_ids.insert(node.content_id());
}

@ -27,9 +27,8 @@ pub(crate) fn all_matched_lines_filled(
/// Extend `matched_lines` to include the leading and trailing lines
/// in the file.
///
/// This is useful when the file contains blank lines at the beginning
/// or end. Those lines won't have MatchedPos values associated with
/// them, but we still want to match them up.
/// If the leading or trailing lines are blank, we won't have any
/// MatchedPos values corresponding with those lines.
fn add_ends(
matched_lines: &[(Option<LineNumber>, Option<LineNumber>)],
lhs_lines: &[&str],

@ -66,7 +66,7 @@ impl Hunk {
));
}
Self {
Hunk {
novel_lhs: self.novel_lhs.union(&other.novel_lhs).copied().collect(),
novel_rhs: self.novel_rhs.union(&other.novel_rhs).copied().collect(),
lines: deduped_lines,
@ -130,11 +130,6 @@ fn extract_lines(hunk: &Hunk) -> Vec<(Option<LineNumber>, Option<LineNumber>)> {
relevant
}
/// If any hunks are sufficiently close that they have overlapping
/// lines, merge them.
///
/// For example, given a hunk from lines 8-10 and a hunk from lines
/// 12-15 with 5 context lines, combine the two hunks.
pub(crate) fn merge_adjacent(
hunks: &[Hunk],
opposite_to_lhs: &DftHashMap<LineNumber, DftHashSet<LineNumber>>,
@ -628,9 +623,6 @@ fn either_side_equal(
false
}
/// Given a set of matched lines between the LHS and RHS, return the
/// start and end indexes in `matched_lines` that should be displayed
/// for `hunk`.
pub(crate) fn matched_lines_indexes_for_hunk(
matched_lines: &[(Option<LineNumber>, Option<LineNumber>)],
hunk: &Hunk,

@ -17,8 +17,8 @@ pub(crate) fn print(
lhs_src: &str,
rhs_src: &str,
display_options: &DisplayOptions,
lhs_mps: &[MatchedPos],
rhs_mps: &[MatchedPos],
lhs_positions: &[MatchedPos],
rhs_positions: &[MatchedPos],
hunks: &[Hunk],
display_path: &str,
extra_info: &Option<String>,
@ -32,7 +32,7 @@ pub(crate) fn print(
display_options.syntax_highlight,
file_format,
display_options.background_color,
lhs_mps,
lhs_positions,
),
apply_colors(
rhs_src,
@ -40,7 +40,7 @@ pub(crate) fn print(
display_options.syntax_highlight,
file_format,
display_options.background_color,
rhs_mps,
rhs_positions,
),
)
} else {
@ -63,8 +63,8 @@ pub(crate) fn print(
.map(|line| style::replace_tabs(&line, display_options.tab_width))
.collect();
let opposite_to_lhs = opposite_positions(lhs_mps);
let opposite_to_rhs = opposite_positions(rhs_mps);
let opposite_to_lhs = opposite_positions(lhs_positions);
let opposite_to_rhs = opposite_positions(rhs_positions);
for (i, hunk) in hunks.iter().enumerate() {
println!(

@ -32,7 +32,11 @@ struct File<'f> {
}
impl<'f> File<'f> {
fn with_sections(language: &'f FileFormat, path: &'f str, chunks: Vec<Vec<Line<'f>>>) -> Self {
fn with_sections(
language: &'f FileFormat,
path: &'f str,
chunks: Vec<Vec<Line<'f>>>,
) -> File<'f> {
File {
language,
path,
@ -41,7 +45,7 @@ impl<'f> File<'f> {
}
}
fn with_status(language: &'f FileFormat, path: &'f str, status: Status) -> Self {
fn with_status(language: &'f FileFormat, path: &'f str, status: Status) -> File<'f> {
File {
language,
path,
@ -197,7 +201,7 @@ struct Line<'l> {
}
impl<'l> Line<'l> {
fn new(lhs_number: Option<u32>, rhs_number: Option<u32>) -> Self {
fn new(lhs_number: Option<u32>, rhs_number: Option<u32>) -> Line<'l> {
Line {
lhs: lhs_number.map(Side::new),
rhs: rhs_number.map(Side::new),
@ -212,7 +216,7 @@ struct Side<'s> {
}
impl<'s> Side<'s> {
fn new(line_number: u32) -> Self {
fn new(line_number: u32) -> Side<'s> {
Side {
line_number,
changes: Vec::new(),
@ -255,15 +259,15 @@ impl Highlight {
};
match highlight {
TokenKind::Delimiter => Self::Delimiter,
TokenKind::Delimiter => Highlight::Delimiter,
TokenKind::Atom(atom) => match atom {
AtomKind::String(StringKind::StringLiteral) => Self::String,
AtomKind::String(StringKind::Text) => Self::Normal,
AtomKind::Keyword => Self::Keyword,
AtomKind::Comment => Self::Comment,
AtomKind::Type => Self::Type,
AtomKind::Normal => Self::Normal,
AtomKind::TreeSitterError => Self::TreeSitterError,
AtomKind::String(StringKind::StringLiteral) => Highlight::String,
AtomKind::String(StringKind::Text) => Highlight::Normal,
AtomKind::Keyword => Highlight::Keyword,
AtomKind::Comment => Highlight::Comment,
AtomKind::Type => Highlight::Type,
AtomKind::Normal => Highlight::Normal,
AtomKind::TreeSitterError => Highlight::TreeSitterError,
},
}
}

@ -23,7 +23,6 @@ use crate::{
summary::FileFormat,
};
/// The single space shown between LHS and RHS columns.
const SPACER: &str = " ";
fn format_line_num_padded(line_num: LineNumber, column_width: usize) -> String {
@ -38,7 +37,6 @@ fn format_missing_line_num(
prev_num: LineNumber,
source_dims: &SourceDimensions,
side: Side,
is_continuation: bool,
use_color: bool,
) -> String {
let column_width = match side {
@ -47,8 +45,8 @@ fn format_missing_line_num(
};
let after_end = match side {
Side::Left => prev_num >= source_dims.lhs_max_line_in_file,
Side::Right => prev_num >= source_dims.rhs_max_line_in_file,
Side::Left => prev_num >= source_dims.lhs_max_line,
Side::Right => prev_num >= source_dims.rhs_max_line,
};
let mut style = Style::new();
@ -56,23 +54,10 @@ fn format_missing_line_num(
style = style.dimmed();
}
let c = if is_continuation {
// Always show dots when this line is too long so we had to
// split it over several lines when displaying.
"."
} else if after_end {
// The file on this side has fewer lines, and we've already
// shown the last ones.
" "
} else {
// There are more lines in this file.
"."
};
let num_digits = prev_num.display().len();
format!(
"{:>width$} ",
c.repeat(num_digits),
(if after_end { " " } else { "." }).repeat(num_digits),
width = column_width - 1
)
.style(style)
@ -142,7 +127,6 @@ fn display_line_nums(
prev_lhs_line_num.unwrap_or_else(|| 1.into()),
source_dims,
Side::Left,
false,
display_options.use_color,
),
};
@ -155,7 +139,6 @@ fn display_line_nums(
prev_rhs_line_num.unwrap_or_else(|| 1.into()),
source_dims,
Side::Right,
false,
display_options.use_color,
),
};
@ -175,21 +158,32 @@ struct SourceDimensions {
/// The number of characters required to display line numbers on
/// the RHS.
rhs_line_nums_width: usize,
lhs_max_line_in_file: LineNumber,
rhs_max_line_in_file: LineNumber,
/// The highest line number in the LHS source.
lhs_max_line: LineNumber,
/// The highest line number in the RHS source.
rhs_max_line: LineNumber,
}
impl SourceDimensions {
fn new(
terminal_width: usize,
lhs_max_line_visible: LineNumber,
rhs_max_line_visible: LineNumber,
lhs_max_line_in_file: LineNumber,
rhs_max_line_in_file: LineNumber,
line_nums: &[(Option<LineNumber>, Option<LineNumber>)],
content_max_width: usize,
) -> Self {
let lhs_line_nums_width = format_line_num(lhs_max_line_visible).len();
let rhs_line_nums_width = format_line_num(rhs_max_line_visible).len();
let mut lhs_max_line: LineNumber = 1.into();
let mut rhs_max_line: LineNumber = 1.into();
for (lhs_line_num, rhs_line_num) in line_nums {
if let Some(lhs_line_num) = lhs_line_num {
lhs_max_line = max(lhs_max_line, *lhs_line_num);
}
if let Some(rhs_line_num) = rhs_line_num {
rhs_max_line = max(rhs_max_line, *rhs_line_num);
}
}
let lhs_line_nums_width = format_line_num(lhs_max_line).len();
let rhs_line_nums_width = format_line_num(rhs_max_line).len();
// If the file lines are extremely short, treat them as if
// they have a line of 25 characters.
@ -200,17 +194,15 @@ impl SourceDimensions {
// between LHS and RHS.
//
// Instead, cap the display width based on the maximum length
// of visible lines within the file.
// of lines within the file.
//
// This naively assumes that byte length is the same as
// display length, which is generally OK because byte length
// will tend to be larger than the display length.
let width_without_truncation = lhs_line_nums_width
+ content_max_width
+ SPACER.len()
+ rhs_line_nums_width
+ content_max_width;
let display_width = min(terminal_width, width_without_truncation);
// This is a crude heuristic because it ignores which lines of
// the file actually get displayed, so we can still end up
// with some superfluous space. It also naively assumes that
// byte length is the same display length, which is generally
// OK because byte length will tend to be larger than the
// display length.
let display_width = min(terminal_width, (content_max_width + 4) * 2 + SPACER.len());
assert!(
display_width > SPACER.len(),
@ -244,8 +236,8 @@ impl SourceDimensions {
content_display_width: content_width,
lhs_line_nums_width,
rhs_line_nums_width,
lhs_max_line_in_file,
rhs_max_line_in_file,
lhs_max_line,
rhs_max_line,
}
}
}
@ -336,88 +328,6 @@ fn highlight_as_novel(
false
}
/// Find the longest line in `lhs_src` and `rhs_src` that will be
/// displayed.
fn displayed_content_max_len_in_bytes(
lhs_src: &str,
rhs_src: &str,
hunks: &[Hunk],
num_context_lines: u32,
) -> usize {
let mut lhs_displayed_lines: DftHashSet<usize> = DftHashSet::default();
let mut rhs_displayed_lines: DftHashSet<usize> = DftHashSet::default();
for hunk in hunks {
let mut min_lhs_line: Option<LineNumber> = None;
let mut max_lhs_line: Option<LineNumber> = None;
let mut min_rhs_line: Option<LineNumber> = None;
let mut max_rhs_line: Option<LineNumber> = None;
for (lhs_line, rhs_line) in &hunk.lines {
if let Some(lhs_line) = lhs_line {
if let Some(current_min) = min_lhs_line {
min_lhs_line = Some(min(current_min, *lhs_line));
} else {
min_lhs_line = Some(*lhs_line);
}
if let Some(current_max) = max_lhs_line {
max_lhs_line = Some(max(current_max, *lhs_line));
} else {
max_lhs_line = Some(*lhs_line);
}
}
if let Some(rhs_line) = rhs_line {
if let Some(current_min) = min_rhs_line {
min_rhs_line = Some(min(current_min, *rhs_line));
} else {
min_rhs_line = Some(*rhs_line);
}
if let Some(current_max) = max_rhs_line {
max_rhs_line = Some(max(current_max, *rhs_line));
} else {
max_rhs_line = Some(*rhs_line);
}
}
if let (Some(min_lhs_line), Some(max_lhs_line)) = (min_lhs_line, max_lhs_line) {
let min_lhs_plus_padding =
max(0, min_lhs_line.0 as isize - num_context_lines as isize) as usize;
let max_lhs_plus_padding = max_lhs_line.0 as usize + num_context_lines as usize;
for lhs_line_num in min_lhs_plus_padding..=max_lhs_plus_padding {
lhs_displayed_lines.insert(lhs_line_num);
}
}
if let (Some(min_rhs_line), Some(max_rhs_line)) = (min_rhs_line, max_rhs_line) {
let min_rhs_plus_padding =
max(0, min_rhs_line.0 as isize - num_context_lines as isize) as usize;
let max_rhs_plus_padding = max_rhs_line.0 as usize + num_context_lines as usize;
for rhs_line_num in min_rhs_plus_padding..=max_rhs_plus_padding {
rhs_displayed_lines.insert(rhs_line_num);
}
}
}
}
let mut content_max_width: usize = 0;
for (lhs_i, lhs_line) in lhs_src.lines().enumerate() {
if lhs_displayed_lines.contains(&lhs_i) {
content_max_width = max(content_max_width, lhs_line.len());
}
}
for (rhs_i, rhs_line) in rhs_src.lines().enumerate() {
if rhs_displayed_lines.contains(&rhs_i) {
content_max_width = max(content_max_width, rhs_line.len());
}
}
content_max_width
}
pub(crate) fn print(
hunks: &[Hunk],
display_options: &DisplayOptions,
@ -429,12 +339,13 @@ pub(crate) fn print(
lhs_mps: &[MatchedPos],
rhs_mps: &[MatchedPos],
) {
let content_max_width = displayed_content_max_len_in_bytes(
lhs_src,
rhs_src,
hunks,
display_options.num_context_lines,
);
let mut content_max_width: usize = 0;
for line in lhs_src.lines() {
content_max_width = max(content_max_width, line.len());
}
for line in rhs_src.lines() {
content_max_width = max(content_max_width, line.len());
}
let (lhs_colored_lines, rhs_colored_lines) = if display_options.use_color {
(
@ -547,52 +458,6 @@ pub(crate) fn print(
let matched_lines = all_matched_lines_filled(lhs_mps, rhs_mps, &lhs_lines, &rhs_lines);
let mut matched_lines_to_print = &matched_lines[..];
let mut lhs_max_visible_line = 1.into();
let mut rhs_max_visible_line = 1.into();
if let Some(hunk) = hunks.last() {
let (start_i, end_i) = matched_lines_indexes_for_hunk(
matched_lines_to_print,
hunk,
display_options.num_context_lines as usize,
);
let aligned_lines = &matched_lines_to_print[start_i..end_i];
for (lhs_line_num, rhs_line_num) in aligned_lines.iter().rev() {
if let Some(lhs_line_num) = *lhs_line_num {
lhs_max_visible_line = max(lhs_max_visible_line, lhs_line_num);
}
if let Some(rhs_line_num) = *rhs_line_num {
rhs_max_visible_line = max(rhs_max_visible_line, rhs_line_num);
}
if lhs_max_visible_line > 1.into() && rhs_max_visible_line > 1.into() {
break;
}
}
}
let lhs_max_line_in_file = LineNumber(lhs_lines.len().saturating_sub(1) as u32);
let rhs_max_line_in_file = LineNumber(rhs_lines.len().saturating_sub(1) as u32);
lhs_max_visible_line = LineNumber(min(
lhs_max_visible_line.0 + display_options.num_context_lines,
lhs_max_line_in_file.0,
));
rhs_max_visible_line = LineNumber(min(
rhs_max_visible_line.0 + display_options.num_context_lines,
rhs_max_line_in_file.0,
));
let source_dims = SourceDimensions::new(
display_options.terminal_width,
lhs_max_visible_line,
rhs_max_visible_line,
lhs_max_line_in_file,
rhs_max_line_in_file,
content_max_width,
);
for (i, hunk) in hunks.iter().enumerate() {
println!(
"{}",
@ -623,6 +488,11 @@ pub(crate) fn print(
let no_rhs_changes = hunk.novel_rhs.is_empty();
let same_lines = aligned_lines.iter().all(|(l, r)| l == r);
let source_dims = SourceDimensions::new(
display_options.terminal_width,
aligned_lines,
content_max_width,
);
for (lhs_line_num, rhs_line_num) in aligned_lines {
let lhs_line_novel = highlight_as_novel(
*lhs_line_num,
@ -726,7 +596,6 @@ pub(crate) fn print(
.unwrap_or_else(|| prev_lhs_line_num.unwrap_or_else(|| 10.into())),
&source_dims,
Side::Left,
true,
display_options.use_color,
);
if let Some(line_num) = lhs_line_num {
@ -747,7 +616,6 @@ pub(crate) fn print(
.unwrap_or_else(|| prev_rhs_line_num.unwrap_or_else(|| 10.into())),
&source_dims,
Side::Right,
true,
display_options.use_color,
);
if let Some(line_num) = rhs_line_num {
@ -789,14 +657,8 @@ mod tests {
#[test]
fn test_width_calculations() {
let source_dims = SourceDimensions::new(
DEFAULT_TERMINAL_WIDTH,
1.into(),
10.into(),
1.into(),
10.into(),
9999,
);
let line_nums = [(Some(1.into()), Some(10.into()))];
let source_dims = SourceDimensions::new(DEFAULT_TERMINAL_WIDTH, &line_nums, 9999);
assert_eq!(source_dims.lhs_line_nums_width, 2);
assert_eq!(source_dims.rhs_line_nums_width, 3);
@ -806,33 +668,40 @@ mod tests {
fn test_format_missing_line_num() {
let source_dims = SourceDimensions::new(
DEFAULT_TERMINAL_WIDTH,
1.into(),
1.into(),
1.into(),
1.into(),
&[
(Some(0.into()), Some(0.into())),
(Some(1.into()), Some(1.into())),
],
9999,
);
assert_eq!(
format_missing_line_num(0.into(), &source_dims, Side::Left, false, true),
format_missing_line_num(0.into(), &source_dims, Side::Left, true),
". ".dimmed().to_string()
);
assert_eq!(
format_missing_line_num(0.into(), &source_dims, Side::Left, false, false),
format_missing_line_num(0.into(), &source_dims, Side::Left, false),
". ".to_owned()
);
}
#[test]
fn test_format_missing_line_num_at_end() {
let source_dims = SourceDimensions::new(80, 1.into(), 1.into(), 1.into(), 1.into(), 9999);
let source_dims = SourceDimensions::new(
80,
&[
(Some(0.into()), Some(0.into())),
(Some(1.into()), Some(1.into())),
],
9999,
);
assert_eq!(
format_missing_line_num(1.into(), &source_dims, Side::Left, false, true),
format_missing_line_num(1.into(), &source_dims, Side::Left, true),
" ".dimmed().to_string()
);
assert_eq!(
format_missing_line_num(1.into(), &source_dims, Side::Left, false, false),
format_missing_line_num(1.into(), &source_dims, Side::Left, false),
" ".to_owned()
);
}

@ -26,7 +26,7 @@ pub(crate) enum BackgroundColor {
impl BackgroundColor {
pub(crate) fn is_dark(self) -> bool {
matches!(self, Self::Dark)
matches!(self, BackgroundColor::Dark)
}
}
@ -324,59 +324,17 @@ pub(crate) fn novel_style(style: Style, side: Side, background: BackgroundColor)
}
}
/// Merge spans where the end of one span matches the start of the
/// next span.
///
/// This reduces the number of ANSI character codes in the
/// output. This is negligible for performance, but makes regression
/// testing easier for difftastic.
///
/// The file compare.expected contains hashes of the output, so it
/// considers `<green>ab</green>` to be distinct from
/// `<green>a</green><green>b</green>`. Merging the spans normalises
/// the output to `<green>ab</green>`.
fn merge_adjacent(items: &[(SingleLineSpan, Style)]) -> Vec<(SingleLineSpan, Style)> {
let mut merged: Vec<(SingleLineSpan, Style)> = vec![];
let mut prev_item: Option<(SingleLineSpan, Style)> = None;
for (span, style) in items.iter().copied() {
match prev_item.take() {
Some((mut prev_span, prev_style)) => {
if prev_style == style
&& prev_span.line == span.line
&& prev_span.end_col == span.start_col
{
prev_span.end_col = span.end_col;
prev_item = Some((prev_span, style));
} else {
merged.push((prev_span, prev_style));
prev_item = Some((span, style));
}
}
None => {
prev_item = Some((span, style));
}
}
}
if let Some(last_item) = prev_item {
merged.push(last_item);
}
merged
}
pub(crate) fn color_positions(
side: Side,
background: BackgroundColor,
syntax_highlight: bool,
file_format: &FileFormat,
mps: &[MatchedPos],
positions: &[MatchedPos],
) -> Vec<(SingleLineSpan, Style)> {
let mut styles = vec![];
for mp in mps {
for pos in positions {
let mut style = Style::new();
match mp.kind {
match pos.kind {
MatchKind::UnchangedToken { highlight, .. } | MatchKind::Ignored { highlight } => {
if syntax_highlight {
if let TokenKind::Atom(atom_kind) = highlight {
@ -442,10 +400,9 @@ pub(crate) fn color_positions(
}
}
};
styles.push((mp.pos, style));
styles.push((pos.pos, style));
}
merge_adjacent(&styles)
styles
}
pub(crate) fn apply_colors(
@ -454,9 +411,9 @@ pub(crate) fn apply_colors(
syntax_highlight: bool,
file_format: &FileFormat,
background: BackgroundColor,
mps: &[MatchedPos],
positions: &[MatchedPos],
) -> Vec<String> {
let styles = color_positions(side, background, syntax_highlight, file_format, mps);
let styles = color_positions(side, background, syntax_highlight, file_format, positions);
let lines = split_on_newlines(s).collect::<Vec<_>>();
style_lines(&lines, &styles)
}
@ -500,19 +457,6 @@ pub(crate) fn print_warning(s: &str, display_options: &DisplayOptions) {
eprint!("{}\n\n", s);
}
/// Style `s` as an error and write to stderr.
pub(crate) fn print_error(s: &str, use_color: bool) {
// TODO: this is inconsistent with print_warning regarding
// arguments and trailing whitespace.
let prefix = if use_color {
"error: ".red().bold().to_string()
} else {
"error: ".to_owned()
};
eprintln!("{}{}", prefix, s);
}
pub(crate) fn apply_line_number_color(
s: &str,
is_novel: bool,

@ -4,9 +4,6 @@
//! manual](http://difftastic.wilfred.me.uk/).
//!
// I frequently develop difftastic on a newer rustc than the MSRV, so
// these two aren't relevant.
#![allow(renamed_and_removed_lints)]
// This tends to trigger on larger tuples of simple types, and naming
// them would probably be worse for readability.
#![allow(clippy::type_complexity)]
@ -30,9 +27,6 @@
#![allow(clippy::manual_unwrap_or_default)]
// I find the explicit arithmetic clearer sometimes.
#![allow(clippy::implicit_saturating_sub)]
// It's helpful being super explicit about byte length versus Unicode
// character point length sometimes.
#![allow(clippy::needless_as_bytes)]
// .to_owned() is more explicit on string references.
#![warn(clippy::str_to_string)]
// .to_string() on a String is clearer as .clone().
@ -71,7 +65,6 @@ use crate::diff::dijkstra::ExceededGraphLimit;
use crate::diff::{dijkstra, unchanged};
use crate::display::context::opposite_positions;
use crate::display::hunks::{matched_pos_to_hunks, merge_adjacent};
use crate::display::style::print_error;
use crate::exit_codes::EXIT_BAD_ARGUMENTS;
use crate::exit_codes::{EXIT_FOUND_CHANGES, EXIT_SUCCESS};
use crate::files::{
@ -98,10 +91,10 @@ use crate::parse::syntax;
///
/// For reference, Jemalloc uses 10-20% more time (although up to 33%
/// more instructions) when testing on sample files.
#[cfg(not(any(target_env = "msvc", target_os = "illumos", target_os = "freebsd")))]
#[cfg(not(any(target_env = "msvc", target_os = "illumos")))]
use tikv_jemallocator::Jemalloc;
#[cfg(not(any(target_env = "msvc", target_os = "illumos", target_os = "freebsd")))]
#[cfg(not(any(target_env = "msvc", target_os = "illumos")))]
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc;
@ -410,6 +403,8 @@ fn diff_file(
) -> DiffResult {
let (lhs_bytes, rhs_bytes) = read_files_or_die(lhs_path, rhs_path, missing_as_empty);
// Override here? Separate option or part of existing --override arg?
let (mut lhs_src, mut rhs_src) = match (
guess_content(&lhs_bytes, lhs_path, binary_overrides),
guess_content(&rhs_bytes, rhs_path, binary_overrides),
@ -500,10 +495,7 @@ fn diff_conflicts_file(
let mut src = match guess_content(&bytes, path, binary_overrides) {
ProbableFileKind::Text(src) => src,
ProbableFileKind::Binary => {
print_error(
"Expected a text file with conflict markers, got a binary file.",
display_options.use_color,
);
eprintln!("error: Expected a text file with conflict markers, got a binary file.");
std::process::exit(EXIT_BAD_ARGUMENTS);
}
};
@ -515,22 +507,15 @@ fn diff_conflicts_file(
let conflict_files = match apply_conflict_markers(&src) {
Ok(cf) => cf,
Err(msg) => {
print_error(&msg, display_options.use_color);
eprintln!("error: {}", msg);
std::process::exit(EXIT_BAD_ARGUMENTS);
}
};
if conflict_files.num_conflicts == 0 {
print_error(
&format!(
"Difftastic requires two paths, or a single file with conflict markers {}.\n",
if display_options.use_color {
START_LHS_MARKER.bold().to_string()
} else {
START_LHS_MARKER.to_owned()
}
),
display_options.use_color,
eprintln!(
"error: Difftastic requires two paths, or a single file with conflict markers {}.\n",
START_LHS_MARKER,
);
eprintln!("USAGE:\n\n {}\n", USAGE);
@ -979,31 +964,11 @@ fn print_diff_result(display_options: &DisplayOptions, summary: &DiffResult) {
match summary.has_byte_changes {
Some((lhs_len, rhs_len)) => {
let format_options = FormatSizeOptions::from(BINARY).decimal_places(1);
if lhs_len == 0 {
// Strictly speaking this is wrong:
// previously we may have had an empty but
// existent file. In that case, it's a
// file modification instead of a file
// creation.
//
// TODO: Fix this pedantic case.
println!(
"Binary file added ({}).\n",
&format_size(rhs_len, format_options),
)
} else if rhs_len == 0 {
println!(
"Binary file removed ({}).\n",
&format_size(lhs_len, format_options),
)
} else {
println!(
"Binary file modified (old: {}, new: {}).\n",
&format_size(lhs_len, format_options),
&format_size(rhs_len, format_options),
)
}
println!(
"Binary contents changed (old: {}, new: {}).\n",
&format_size(lhs_len, format_options),
&format_size(rhs_len, format_options),
)
}
None => println!("No changes.\n"),
}

@ -9,10 +9,9 @@ use std::{
use clap::{crate_authors, crate_description, value_parser, Arg, ArgAction, Command};
use crossterm::tty::IsTty;
use owo_colors::OwoColorize as _;
use crate::{
display::style::{print_error, BackgroundColor},
display::style::BackgroundColor,
exit_codes::EXIT_BAD_ARGUMENTS,
parse::guess_language::{language_override_from_name, LanguageOverride},
version::VERSION,
@ -91,64 +90,28 @@ impl Default for DiffOptions {
}
fn app() -> clap::Command {
let bin_name = env!("CARGO_BIN_NAME");
let mut after_help = String::new();
after_help
.push_str("You can compare two files with difftastic by specifying them as arguments.\n\n");
after_help.push_str(&format!("$ {} old.js new.js", bin_name).bold().to_string());
after_help.push_str("\n\nYou can also use directories as arguments. Difftastic will walk both directories and compare files with matching names.\n\n");
after_help.push_str(&format!("$ {} old/ new/", bin_name).bold().to_string());
after_help.push_str("\n\nIf you have a file with conflict markers, you can pass it as a single argument. Difftastic will diff the two conflicting file states.\n\n");
after_help.push_str(
&format!("$ {} file_with_conflicts.js", bin_name)
.bold()
.to_string(),
);
// For some reason clap will hard wrap these invocations weirdly
// (with extra blank lines) if we use bold. Since these are
// showing CLI formats rather than concrete values, compromise by
// not using bold.
after_help.push_str("\n\nDifftastic can also be invoked with 7 or 9 arguments in the format that GIT_EXTERNAL_DIFF expects.\n\n");
after_help.push_str(&format!(
"$ {} DISPLAY-PATH OLD-FILE OLD-HEX OLD-MODE NEW-FILE NEW-HEX NEW-MODE",
bin_name
));
after_help.push('\n');
after_help.push_str(&format!(
"$ {} OLD-NAME OLD-FILE OLD-HEX OLD-MODE NEW-FILE NEW-HEX NEW-MODE NEW-NAME METADATA",
bin_name
));
after_help.push_str("\n\nSee the full manual at ");
if std::io::stdout().is_tty() {
// Make the link to the manual clickable in terminals that
// support OSC 8, the ANSI escape code for hyperlinks.
//
// https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
// https://github.com/Alhadis/OSC8-Adoption
//
// There isn't any way of detecting whether the terminal
// supports OSC 8 specifically, but we can limit usage to when
// there's a TTY. This is similar to how we detect whether to
// use colour.
after_help.push_str("\x1b]8;;https://difftastic.wilfred.me.uk/\x1b\\https://difftastic.wilfred.me.uk/\x1b]8;;\x1b\\");
} else {
after_help.push_str("https://difftastic.wilfred.me.uk/");
}
after_help.push('.');
Command::new("Difftastic")
.override_usage(USAGE)
.version(env!("CARGO_PKG_VERSION"))
.long_version(VERSION.as_str())
.about(crate_description!())
.author(crate_authors!())
.after_long_help(after_help)
.after_long_help(concat!(
"You can compare two files with difftastic by specifying them as arguments.\n\n",
"$ ",
env!("CARGO_BIN_NAME"),
" old.js new.js\n\n",
"You can also use directories as arguments. Difftastic will walk both directories and compare files with matching names.\n\n",
"$ ",
env!("CARGO_BIN_NAME"),
" old/ new/\n\n",
"If you have a file with conflict markers, you can pass it as a single argument. Difftastic will diff the two conflicting file states.\n\n",
"$ ",
env!("CARGO_BIN_NAME"),
" file_with_conflicts.js\n\n",
"Difftastic can also be invoked with 7 arguments in the format that GIT_EXTERNAL_DIFF expects.\n\n",
"See the full manual at: https://difftastic.wilfred.me.uk/")
)
.arg(
Arg::new("dump-syntax")
.long("dump-syntax")
@ -398,7 +361,7 @@ pub(crate) enum FileArgument {
impl FileArgument {
pub(crate) fn permissions(&self) -> Option<FilePermissions> {
match self {
Self::NamedPath(path) => {
FileArgument::NamedPath(path) => {
// When used with `git difftool`, the first argument
// is a temporary file that always has the same
// permissions. That doesn't mean the file permissions
@ -410,8 +373,8 @@ impl FileArgument {
let metadata = std::fs::metadata(path).ok()?;
Some(metadata.permissions().into())
}
Self::Stdin => None,
Self::DevNull => None,
FileArgument::Stdin => None,
FileArgument::DevNull => None,
}
}
}
@ -484,11 +447,11 @@ impl FileArgument {
/// argument.
pub(crate) fn from_cli_argument(arg: &OsStr) -> Self {
if arg == "/dev/null" {
Self::DevNull
FileArgument::DevNull
} else if arg == "-" {
Self::Stdin
FileArgument::Stdin
} else {
Self::NamedPath(PathBuf::from(arg))
FileArgument::NamedPath(PathBuf::from(arg))
}
}
@ -497,9 +460,9 @@ impl FileArgument {
pub(crate) fn from_path_argument(arg: &OsStr) -> Self {
// For new and deleted files, Git passes `/dev/null` as the reference file.
if arg == "/dev/null" {
Self::DevNull
FileArgument::DevNull
} else {
Self::NamedPath(PathBuf::from(arg))
FileArgument::NamedPath(PathBuf::from(arg))
}
}
}
@ -507,11 +470,11 @@ impl FileArgument {
impl Display for FileArgument {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NamedPath(path) => {
FileArgument::NamedPath(path) => {
write!(f, "{}", relative_to_current(path).display())
}
Self::Stdin => write!(f, "(stdin)"),
Self::DevNull => write!(f, "/dev/null"),
FileArgument::Stdin => write!(f, "(stdin)"),
FileArgument::DevNull => write!(f, "/dev/null"),
}
}
}
@ -880,6 +843,7 @@ pub(crate) fn parse_args() -> Mode {
}
}
// TODO: document these different ways of calling difftastic.
let (display_path, lhs_path, rhs_path, lhs_permissions, rhs_permissions, renamed) = match &args
[..]
{
@ -957,13 +921,10 @@ pub(crate) fn parse_args() -> Mode {
}
_ => {
if !args.is_empty() {
print_error(
&format!(
"Difftastic does not support being called with {} argument{}.\n",
args.len(),
if args.len() == 1 { "" } else { "s" }
),
use_color,
eprintln!(
"error: Difftastic does not support being called with {} argument{}.\n",
args.len(),
if args.len() == 1 { "" } else { "s" }
);
}
eprintln!("USAGE:\n\n {}\n", USAGE);
@ -1027,18 +988,16 @@ fn detect_terminal_width() -> usize {
pub(crate) fn should_use_color(color_output: ColorOutput) -> bool {
match color_output {
ColorOutput::Always => true,
ColorOutput::Auto => detect_color_support(),
ColorOutput::Auto => {
// Always enable colour if stdout is a TTY or if the git pager is active.
// TODO: consider following the env parsing logic in git_config_bool
// in config.c.
std::io::stdout().is_tty() || env::var("GIT_PAGER_IN_USE").is_ok()
}
ColorOutput::Never => false,
}
}
/// Always enable colour if stdout is a TTY or if the git pager is active.
fn detect_color_support() -> bool {
// TODO: consider following the env parsing logic in git_config_bool
// in config.c.
std::io::stdout().is_tty() || env::var("GIT_PAGER_IN_USE").is_ok()
}
#[cfg(test)]
mod tests {
use super::*;

@ -61,7 +61,6 @@ pub(crate) enum Language {
Pascal,
Perl,
Php,
Proto,
Python,
Qml,
R,
@ -162,7 +161,6 @@ pub(crate) fn language_name(language: Language) -> &'static str {
Pascal => "Pascal",
Perl => "Perl",
Php => "PHP",
Proto => "Proto",
Python => "Python",
Qml => "QML",
R => "R",
@ -356,7 +354,6 @@ pub(crate) fn language_globs(language: Language) -> Vec<glob::Pattern> {
Php => &[
"*.php", "*.phtml", "*.php3", "*.php4", "*.php5", "*.php7", "*.phps",
],
Proto => &["*.proto"],
Python => &["*.py", "*.py3", "*.pyi", "*.bzl", "TARGETS", "BUCK", "DEPS"],
Qml => &["*.qml"],
R => &["*.R", "*.r", "*.rd", "*.rsx", ".Rprofile", "expr-dist"],
@ -414,7 +411,7 @@ pub(crate) fn language_globs(language: Language) -> Vec<glob::Pattern> {
".cproject",
".project",
],
Yaml => &["*.yaml", "*.yml", "yarn.lock", "CITATION.cff"],
Yaml => &["*.yaml", "*.yml"],
Zig => &["*.zig"],
};
@ -525,51 +522,54 @@ fn from_emacs_mode_header(src: &str) -> Option<Language> {
(Some(cap), _) | (_, Some(cap)) => cap[1].into(),
_ => "".into(),
};
return Some(match mode_name.to_ascii_lowercase().trim() {
"ada" => Ada,
"c" => C,
"clojure" => Clojure,
"csharp" => CSharp,
"css" => Css,
"dart" => Dart,
"c++" => CPlusPlus,
"elixir" => Elixir,
"elm" => Elm,
"elvish" => Elvish,
"emacs-lisp" => EmacsLisp,
"fsharp" => FSharp,
"gleam" => Gleam,
"go" => Go,
"haskell" => Haskell,
"hcl" => Hcl,
"html" => Html,
"janet" => Janet,
"java" => Java,
"js" | "js2" => JavaScript,
"lisp" => CommonLisp,
"nxml" => Xml,
"objc" => ObjC,
"perl" => Perl,
"python" => Python,
"racket" => Racket,
"rjsx" => JavascriptJsx,
"ruby" => Ruby,
"rust" => Rust,
"scala" => Scala,
"scss" => Scss,
"sh" => Bash,
"solidity" => Solidity,
"sql" => Sql,
"swift" => Swift,
"toml" => Toml,
"tuareg" => OCaml,
"typescript" => TypeScript,
"verilog" => Verilog,
"vhdl" => Vhdl,
"yaml" => Yaml,
"zig" => Zig,
_ => continue,
});
let lang = match mode_name.to_ascii_lowercase().trim() {
"ada" => Some(Ada),
"c" => Some(C),
"clojure" => Some(Clojure),
"csharp" => Some(CSharp),
"css" => Some(Css),
"dart" => Some(Dart),
"c++" => Some(CPlusPlus),
"elixir" => Some(Elixir),
"elm" => Some(Elm),
"elvish" => Some(Elvish),
"emacs-lisp" => Some(EmacsLisp),
"fsharp" => Some(FSharp),
"gleam" => Some(Gleam),
"go" => Some(Go),
"haskell" => Some(Haskell),
"hcl" => Some(Hcl),
"html" => Some(Html),
"janet" => Some(Janet),
"java" => Some(Java),
"js" | "js2" => Some(JavaScript),
"lisp" => Some(CommonLisp),
"nxml" => Some(Xml),
"objc" => Some(ObjC),
"perl" => Some(Perl),
"python" => Some(Python),
"racket" => Some(Racket),
"rjsx" => Some(JavascriptJsx),
"ruby" => Some(Ruby),
"rust" => Some(Rust),
"scala" => Some(Scala),
"scss" => Some(Scss),
"sh" => Some(Bash),
"solidity" => Some(Solidity),
"sql" => Some(Sql),
"swift" => Some(Swift),
"toml" => Some(Toml),
"tuareg" => Some(OCaml),
"typescript" => Some(TypeScript),
"verilog" => Some(Verilog),
"vhdl" => Some(Vhdl),
"yaml" => Some(Yaml),
"zig" => Some(Zig),
_ => None,
};
if lang.is_some() {
return lang;
}
}
None

@ -48,8 +48,6 @@ impl fmt::Debug for ChangeKind<'_> {
pub(crate) type SyntaxId = NonZeroU32;
pub(crate) type ContentId = u32;
/// Fields that are common to both `Syntax::List` and `Syntax::Atom`.
pub(crate) struct SyntaxInfo<'a> {
/// The previous node with the same parent as this one.
@ -71,7 +69,7 @@ pub(crate) struct SyntaxInfo<'a> {
/// diff, or nodes at different positions.
///
/// Values are sequential, not hashes. Collisions never occur.
content_id: Cell<ContentId>,
content_id: Cell<u32>,
/// Is this the only node with this content? Ignores nodes on the
/// other side.
content_is_unique: Cell<bool>,
@ -196,13 +194,13 @@ impl<'a> fmt::Debug for Syntax<'a> {
impl<'a> Syntax<'a> {
pub(crate) fn new_list(
arena: &'a Arena<Self>,
arena: &'a Arena<Syntax<'a>>,
open_content: &str,
open_position: Vec<SingleLineSpan>,
children: Vec<&'a Self>,
children: Vec<&'a Syntax<'a>>,
close_content: &str,
close_position: Vec<SingleLineSpan>,
) -> &'a Self {
) -> &'a Syntax<'a> {
// Skip empty atoms: they aren't displayed, so there's no
// point making our syntax tree bigger. These occur when we're
// parsing incomplete or malformed programs.
@ -249,11 +247,11 @@ impl<'a> Syntax<'a> {
}
pub(crate) fn new_atom(
arena: &'a Arena<Self>,
arena: &'a Arena<Syntax<'a>>,
mut position: Vec<SingleLineSpan>,
mut content: String,
kind: AtomKind,
) -> &'a Self {
) -> &'a Syntax<'a> {
// If a parser hasn't cleaned up \r on CRLF files with
// comments, discard it.
if content.ends_with('\r') {
@ -283,11 +281,11 @@ impl<'a> Syntax<'a> {
}
}
pub(crate) fn parent(&self) -> Option<&'a Self> {
pub(crate) fn parent(&self) -> Option<&'a Syntax<'a>> {
self.info().parent.get()
}
pub(crate) fn next_sibling(&self) -> Option<&'a Self> {
pub(crate) fn next_sibling(&self) -> Option<&'a Syntax<'a>> {
self.info().next_sibling.get()
}
@ -300,7 +298,7 @@ impl<'a> Syntax<'a> {
/// A content ID of this syntax node. Two nodes have the same
/// content ID if they have the same content, regardless of
/// position.
pub(crate) fn content_id(&self) -> ContentId {
pub(crate) fn content_id(&self) -> u32 {
self.info().content_id.get()
}
@ -509,7 +507,7 @@ fn set_unique_id(nodes: &[&Syntax], next_id: &mut SyntaxId) {
}
/// Assumes that `set_content_id` has already run.
fn find_nodes_with_unique_content(nodes: &[&Syntax], counts: &mut DftHashMap<ContentId, usize>) {
fn find_nodes_with_unique_content(nodes: &[&Syntax], counts: &mut DftHashMap<u32, usize>) {
for node in nodes {
*counts.entry(node.content_id()).or_insert(0) += 1;
if let List { children, .. } = node {
@ -518,7 +516,7 @@ fn find_nodes_with_unique_content(nodes: &[&Syntax], counts: &mut DftHashMap<Con
}
}
fn set_content_is_unique_from_counts(nodes: &[&Syntax], counts: &DftHashMap<ContentId, usize>) {
fn set_content_is_unique_from_counts(nodes: &[&Syntax], counts: &DftHashMap<u32, usize>) {
for node in nodes {
let count = counts
.get(&node.content_id())
@ -614,9 +612,6 @@ pub(crate) enum StringKind {
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
pub(crate) enum AtomKind {
/// The kind of this atom when we don't know anything else about
/// it. This is typically a variable, e.g. `foo`, or a literal
/// `123`. Note that string literals have a separate kind.
Normal,
// TODO: We should either have a AtomWithWords(HighlightKind) or a
// separate String, Text and Comment kind.
@ -681,7 +676,9 @@ impl MatchKind {
pub(crate) fn is_novel(&self) -> bool {
matches!(
self,
Self::Novel { .. } | Self::NovelWord { .. } | Self::UnchangedPartOfNovelItem { .. }
MatchKind::Novel { .. }
| MatchKind::NovelWord { .. }
| MatchKind::UnchangedPartOfNovelItem { .. }
)
}
}

@ -69,16 +69,33 @@ pub(crate) struct TreeSitterConfig {
}
extern "C" {
fn tree_sitter_ada() -> ts::Language;
fn tree_sitter_apex() -> ts::Language;
fn tree_sitter_clojure() -> ts::Language;
fn tree_sitter_cmake() -> ts::Language;
fn tree_sitter_commonlisp() -> ts::Language;
fn tree_sitter_dart() -> ts::Language;
fn tree_sitter_devicetree() -> ts::Language;
fn tree_sitter_elisp() -> ts::Language;
fn tree_sitter_elm() -> ts::Language;
fn tree_sitter_elvish() -> ts::Language;
fn tree_sitter_gleam() -> ts::Language;
fn tree_sitter_hare() -> ts::Language;
fn tree_sitter_hack() -> ts::Language;
fn tree_sitter_hcl() -> ts::Language;
fn tree_sitter_janet_simple() -> ts::Language;
fn tree_sitter_kotlin() -> ts::Language;
fn tree_sitter_latex() -> ts::Language;
fn tree_sitter_newick() -> ts::Language;
fn tree_sitter_perl() -> ts::Language;
fn tree_sitter_qmljs() -> ts::Language;
fn tree_sitter_r() -> ts::Language;
fn tree_sitter_racket() -> ts::Language;
fn tree_sitter_scheme() -> ts::Language;
fn tree_sitter_smali() -> ts::Language;
fn tree_sitter_scss() -> ts::Language;
fn tree_sitter_solidity() -> ts::Language;
fn tree_sitter_sql() -> ts::Language;
fn tree_sitter_vhdl() -> ts::Language;
}
@ -96,8 +113,7 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
use guess::Language::*;
match language {
Ada => {
let language_fn = tree_sitter_ada::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_ada() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string_literal", "character_literal"]
@ -113,9 +129,7 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
Apex => {
let language_fn = tree_sitter_sfapex::apex::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_apex() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: [
@ -132,7 +146,7 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
delimiter_tokens: vec![("[", "]"), ("(", ")"), ("{", "}")],
highlight_query: ts::Query::new(
&language,
tree_sitter_sfapex::apex::HIGHLIGHTS_QUERY,
include_str!("../../vendored_parsers/highlights/apex.scm"),
)
.unwrap(),
sub_languages: vec![],
@ -144,7 +158,7 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string", "raw_string", "heredoc_body", "simple_expansion"]
atom_nodes: ["string", "raw_string", "heredoc_body"]
.into_iter()
.collect(),
delimiter_tokens: vec![("(", ")"), ("{", "}"), ("[", "]")],
@ -182,9 +196,7 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
Clojure => {
let language_fn = tree_sitter_clojure_orchard::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_clojure() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["kwd_lit", "regex_lit"].into_iter().collect(),
@ -193,15 +205,14 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
.collect(),
highlight_query: ts::Query::new(
&language,
tree_sitter_clojure_orchard::HIGHLIGHTS_QUERY,
include_str!("../../vendored_parsers/highlights/clojure.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
CMake => {
let language_fn = tree_sitter_cmake::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_cmake() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["argument"].into_iter().collect(),
@ -268,23 +279,21 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
Dart => {
let language_fn = tree_sitter_dart_orchard::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_dart() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string_literal", "script_tag"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]"), ("<", ">")],
highlight_query: ts::Query::new(
&language,
tree_sitter_dart_orchard::HIGHLIGHTS_QUERY,
include_str!("../../vendored_parsers/highlights/dart.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
DeviceTree => {
let language_fn = tree_sitter_devicetree::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_devicetree() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["byte_string_literal", "string_literal"]
@ -315,15 +324,16 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
Elm => {
let language_fn = tree_sitter_elm::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_elm() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string_constant_expr"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("[", "]"), ("(", ")")],
highlight_query: ts::Query::new(&language, tree_sitter_elm::HIGHLIGHTS_QUERY)
.unwrap(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/elm.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
@ -342,9 +352,7 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
EmacsLisp => {
let language_fn = tree_sitter_elisp::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_elisp() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: [].into_iter().collect(),
@ -390,14 +398,16 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
Gleam => {
let language_fn = tree_sitter_gleam::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_gleam() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string"].into_iter().collect(),
delimiter_tokens: vec![("(", ")"), ("[", "]"), ("{", "}")],
highlight_query: ts::Query::new(&language, tree_sitter_gleam::HIGHLIGHT_QUERY)
.unwrap(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/gleam.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
@ -468,8 +478,7 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
Hcl => {
let language_fn = tree_sitter_hcl::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_hcl() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string_lit", "heredoc_template"].into_iter().collect(),
@ -705,9 +714,7 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
Newick => {
let language_fn = tree_sitter_newick::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_newick() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: [].into_iter().collect(),
@ -832,21 +839,6 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
sub_languages: vec![],
}
}
Proto => {
let language_fn = tree_sitter_proto::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string"].into_iter().collect(),
delimiter_tokens: vec![("{", "}")],
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/proto.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Python => {
let language_fn = tree_sitter_python::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
@ -860,12 +852,11 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
Qml => {
let language_fn = tree_sitter_qmljs::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_qmljs() };
let mut highlight_query = tree_sitter_javascript::HIGHLIGHT_QUERY.to_owned();
highlight_query.push_str(tree_sitter_typescript::HIGHLIGHTS_QUERY);
highlight_query.push_str(tree_sitter_qmljs::HIGHLIGHTS_QUERY);
highlight_query.push_str(include_str!("../../vendored_parsers/highlights/qmljs.scm"));
TreeSitterConfig {
language: language.clone(),
@ -876,28 +867,32 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
R => {
let language_fn = tree_sitter_r::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_r() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string", "special"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]")],
highlight_query: ts::Query::new(&language, tree_sitter_r::HIGHLIGHTS_QUERY)
.unwrap(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/r.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Racket => {
let language_fn = tree_sitter_racket::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_racket() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string", "byte_string", "regex", "here_string"]
.into_iter()
.collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]")],
highlight_query: ts::Query::new(&language, tree_sitter_racket::HIGHLIGHTS_QUERY)
.unwrap(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/racket.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
@ -927,9 +922,7 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["char_literal", "string_literal", "raw_string_literal"]
.into_iter()
.collect(),
atom_nodes: ["char_literal", "string_literal"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]"), ("|", "|"), ("<", ">")],
highlight_query: ts::Query::new(
&language,
@ -952,14 +945,16 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
Scheme => {
let language_fn = tree_sitter_scheme::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_scheme() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("(", ")"), ("[", "]")],
highlight_query: ts::Query::new(&language, tree_sitter_scheme::HIGHLIGHTS_QUERY)
.unwrap(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/scheme.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
@ -994,28 +989,32 @@ pub(crate) fn from_language(language: guess::Language) -> TreeSitterConfig {
}
}
Solidity => {
let language_fn = tree_sitter_solidity::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_solidity() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string", "hex_string_literal", "unicode_string_literal"]
.into_iter()
.collect(),
delimiter_tokens: vec![("[", "]"), ("(", ")"), ("{", "}")],
highlight_query: ts::Query::new(&language, tree_sitter_solidity::HIGHLIGHT_QUERY)
.unwrap(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/solidity.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}
Sql => {
let language_fn = tree_sitter_sequel::LANGUAGE;
let language = tree_sitter::Language::new(language_fn);
let language = unsafe { tree_sitter_sql() };
TreeSitterConfig {
language: language.clone(),
atom_nodes: ["string", "identifier"].into_iter().collect(),
delimiter_tokens: vec![("(", ")")],
highlight_query: ts::Query::new(&language, tree_sitter_sequel::HIGHLIGHTS_QUERY)
.unwrap(),
highlight_query: ts::Query::new(
&language,
include_str!("../../vendored_parsers/highlights/sql.scm"),
)
.unwrap(),
sub_languages: vec![],
}
}

@ -27,10 +27,10 @@ pub(crate) enum FileFormat {
impl Display for FileFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SupportedLanguage(language) => write!(f, "{}", language_name(*language)),
Self::PlainText => write!(f, "Text"),
Self::TextFallback { reason } => write!(f, "Text ({})", reason),
Self::Binary => write!(f, "Binary"),
FileFormat::SupportedLanguage(language) => write!(f, "{}", language_name(*language)),
FileFormat::PlainText => write!(f, "Text"),
FileFormat::TextFallback { reason } => write!(f, "Text ({})", reason),
FileFormat::Binary => write!(f, "Binary"),
}
}
}

@ -12,7 +12,7 @@ pub(crate) fn split_words(s: &str) -> Vec<&str> {
for (idx, c) in s.char_indices() {
match word_start {
Some(start) => {
if c.is_alphanumeric() || c == '_' {
if c.is_alphanumeric() || c == '-' || c == '_' {
// Just carry on in this word.
} else {
// Push the previous word, then this non-word character.
@ -22,7 +22,7 @@ pub(crate) fn split_words(s: &str) -> Vec<&str> {
}
}
None => {
if c.is_alphanumeric() || c == '_' {
if c.is_alphanumeric() || c == '-' || c == '_' {
word_start = Some(idx);
} else {
words.push(&s[idx..idx + c.len_utf8()]);
@ -40,7 +40,7 @@ pub(crate) fn split_words(s: &str) -> Vec<&str> {
/// Split `s` into a vec of things that look like words and individual
/// non-word characters.
///
/// "foo..bar23" -> vec!["foo", ".", ".", "bar", "23"]
/// "foo..bar23" -> vec!["foo", ".", ".", "bar23"]
pub(crate) fn split_words_and_numbers(s: &str) -> Vec<&str> {
let mut words = vec![];
let mut word_start: Option<(usize, char)> = None;
@ -51,7 +51,7 @@ pub(crate) fn split_words_and_numbers(s: &str) -> Vec<&str> {
// Word character, add to the current word if it's
// not a number.
if c.is_ascii_digit() == start_c.is_ascii_digit() {
// Just carry on in this word/number.
// Just carry on in this word.
} else {
// Finish previous word, start a new one.
words.push(&s[start..idx]);
@ -65,7 +65,7 @@ pub(crate) fn split_words_and_numbers(s: &str) -> Vec<&str> {
}
}
None => {
if c.is_alphanumeric() || c == '_' {
if c.is_alphanumeric() || c == '-' || c == '_' {
word_start = Some((idx, c));
} else {
words.push(&s[idx..idx + c.len_utf8()]);

@ -57,7 +57,7 @@ fn binary_changed() {
.arg("img/logo.png")
.arg("/dev/null");
let predicate_fn = predicate::str::contains("Binary file removed");
let predicate_fn = predicate::str::contains("Binary contents changed");
cmd.assert().stdout(predicate_fn);
}
@ -68,7 +68,7 @@ fn binary_override() {
cmd.arg("--override-binary=*.js")
.arg("sample_files/simple_1.js")
.arg("sample_files/simple_2.js");
let predicate_fn = predicate::str::contains("Binary file modified");
let predicate_fn = predicate::str::contains("Binary contents changed");
cmd.assert().stdout(predicate_fn);
}

@ -1,180 +0,0 @@
;; highlight queries.
;; See the syntax at https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries
;; See also https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md#parser-configurations
;; for a list of recommended @ tags, though not all of them have matching
;; highlights in neovim.
[
"abort"
"abs"
"abstract"
"accept"
"access"
"all"
"array"
"at"
"begin"
"declare"
"delay"
"delta"
"digits"
"do"
"end"
"entry"
"exit"
"generic"
"interface"
"is"
"limited"
"null"
"of"
"others"
"out"
"pragma"
"private"
"range"
"synchronized"
"tagged"
"task"
"terminate"
"until"
"when"
] @keyword
[
"aliased"
"constant"
"renames"
] @storageclass
[
"mod"
"new"
"protected"
"record"
"subtype"
"type"
] @keyword.type
[
"with"
"use"
] @include
[
"body"
"function"
"overriding"
"procedure"
"package"
"separate"
] @keyword.function
[
"and"
"in"
"not"
"or"
"xor"
] @keyword.operator
[
"while"
"loop"
"for"
"parallel"
"reverse"
"some"
] @repeat
[
"return"
] @keyword.return
[
"case"
"if"
"else"
"then"
"elsif"
"select"
] @conditional
[
"exception"
"raise"
] @exception
(comment) @comment
(comment) @spell ;; spell-check comments
(string_literal) @string
(string_literal) @spell ;; spell-check strings
(character_literal) @string
(numeric_literal) @number
;; Highlight the name of subprograms
(procedure_specification name: (_) @function)
(function_specification name: (_) @function)
(package_declaration name: (_) @function)
(package_body name: (_) @function)
(generic_instantiation name: (_) @function)
(entry_declaration . (identifier) @function)
;; Some keywords should take different categories depending on the context
(use_clause "use" @include "type" @include)
(with_clause "private" @include)
(with_clause "limited" @include)
(use_clause (_) @namespace)
(with_clause (_) @namespace)
(loop_statement "end" @keyword.repeat)
(if_statement "end" @conditional)
(loop_parameter_specification "in" @keyword.repeat)
(loop_parameter_specification "in" @keyword.repeat)
(iterator_specification ["in" "of"] @keyword.repeat)
(range_attribute_designator "range" @keyword.repeat)
(raise_statement "with" @exception)
(gnatprep_declarative_if_statement) @preproc
(gnatprep_if_statement) @preproc
(gnatprep_identifier) @preproc
(subprogram_declaration "is" @keyword.function "abstract" @keyword.function)
(aspect_specification "with" @keyword.function)
(full_type_declaration "is" @keyword.type)
(subtype_declaration "is" @keyword.type)
(record_definition "end" @keyword.type)
(full_type_declaration (_ "access" @keyword.type))
(array_type_definition "array" @keyword.type "of" @keyword.type)
(access_to_object_definition "access" @keyword.type)
(access_to_object_definition "access" @keyword.type
[
(general_access_modifier "constant" @keyword.type)
(general_access_modifier "all" @keyword.type)
]
)
(range_constraint "range" @keyword.type)
(signed_integer_type_definition "range" @keyword.type)
(index_subtype_definition "range" @keyword.type)
(record_type_definition "abstract" @keyword.type)
(record_type_definition "tagged" @keyword.type)
(record_type_definition "limited" @keyword.type)
(record_type_definition (record_definition "null" @keyword.type))
(private_type_declaration "is" @keyword.type "private" @keyword.type)
(private_type_declaration "tagged" @keyword.type)
(private_type_declaration "limited" @keyword.type)
(task_type_declaration "task" @keyword.type "is" @keyword.type)
;; Gray the body of expression functions
(expression_function_declaration
(function_specification)
"is"
(_) @attribute
)
(subprogram_declaration (aspect_specification) @attribute)
;; Highlight full subprogram specifications
;(subprogram_body
; [
; (procedure_specification)
; (function_specification)
; ] @function.spec
;)
;; Highlight errors in red. This is not very useful in practice, as text will
;; be highlighted as user types, and the error could be elsewhere in the code.
;; This also requires defining :hi @error guifg=Red for instance.
(ERROR) @error

@ -0,0 +1 @@
../tree-sitter-ada/queries/highlights.scm

@ -0,0 +1,302 @@
;; attempting to match concepts represented here:
;; https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide
[
"["
"]"
"{"
"}"
"?"
";"
] @punctuation
;; Methods
(method_declaration
name: (identifier) @method)
(method_declaration
type: (type_identifier) @type)
(method_invocation
name: (identifier) @method)
(argument_list
(identifier) @variable)
(super) @function.defaultLibrary
(explicit_constructor_invocation
arguments: (argument_list
(identifier) @variable ))
;; Annotations
(annotation
name: (identifier) @decorator)
"@" @operator
(annotation_key_value
(identifier) @variable)
;; Types
(interface_declaration
name: (identifier) @interface)
(class_declaration
name: (identifier) @class)
(class_declaration
(superclass) @class)
(enum_declaration
name: (identifier) @enum)
(enum_constant
name: (identifier) @enumMember)
(interfaces
(type_list
(type_identifier) @interface ))
(local_variable_declaration
(type_identifier) @type )
( expression_statement (_ (identifier)) @variable)
(type_arguments "<" @punctuation)
(type_arguments ">" @punctuation)
; (identifier) @variable
((field_access
object: (identifier) @type)) ;; don't know what type of thing it is
(generic_type
(type_identifier) @type)
(type_arguments (type_identifier) @type)
(field_access
field: (identifier) @property)
((scoped_identifier
scope: (identifier) @type)
(#match? @type "^[A-Z]"))
((method_invocation
object: (identifier) @type)
(#match? @type "^[A-Z]"))
(field_declaration
type: (type_identifier) @type)
(method_declaration
(formal_parameters
(formal_parameter
name: (identifier) @parameter)))
(formal_parameter
type: (type_identifier) @type
(identifier) @variable)
(enhanced_for_statement
type: (type_identifier) @type
name: (identifier) @variable )
(enhanced_for_statement
value: (identifier) @variable)
(enhanced_for_statement
name: (identifier) @variable)
(object_creation_expression
type: (type_identifier) @type)
(array_creation_expression
type: (type_identifier) @type)
(array_type
element: (type_identifier) @type)
(catch_formal_parameter
(type_identifier) @type
name: (identifier) @variable)
(return_statement
(identifier) @variable)
(local_variable_declaration
(variable_declarator
name: (identifier) @variable ))
(for_statement
condition: (binary_expression
(identifier) @variable))
(for_statement
update: (update_expression
(identifier) @variable))
(constructor_declaration
name: (identifier) @class)
(dml_type) @function.defaultLibrary
(bound_apex_expression
(identifier) @variable)
(assignment_operator) @operator
(update_expression ["++" "--"] @operator)
(instanceof_expression
left: (identifier) @variable
right: (type_identifier) @type )
(cast_expression
type: (type_identifier) @type
value: (identifier) @variable)
(switch_expression
condition: (identifier) @variable)
(switch_label
(type_identifier) @type
(identifier) @variable )
(switch_rule
(switch_label
(identifier) @enumMember ))
(trigger_declaration
name: (identifier) @type
object: (identifier) @type
(trigger_event) @keyword
("," (trigger_event) @keyword)*)
(binary_expression
operator: [
">"
"<"
">="
"<="
"=="
"==="
"!="
"!=="
"&&"
"||"
"+"
"-"
"*"
"/"
"&"
"|"
"^"
"%"
"<<"
">>"
">>>"] @operator)
(binary_expression
(identifier) @variable)
(unary_expression
operator: [
"+"
"-"
"!"
"~"
]) @operator
(map_initializer "=>" @operator)
[
(boolean_type)
(void_type)
] @type.defaultLibrary
; Variables
(field_declaration
(modifiers (modifier ["final" "static"])(modifier ["final" "static"]))
(variable_declarator
name: (identifier) @variable.readonly))
(variable_declarator
(identifier) @property)
;; because itendifying it when declared doesn't carry to use
;; leans on the convention that "screaming snake case" is a const
((identifier) @variable.readonly
(#match? @variable.readonly "^_*[A-Z][A-Z\\d_]+$"))
(this) @variable.defaultLibrary
; Literals
[
(int)
] @number
[
(string_literal)
] @string
[
(line_comment)
(block_comment)
] @comment
;; ;; Keywords
[
"abstract"
"break"
"catch"
"class"
"continue"
"default"
"do"
"else"
"enum"
"extends"
"final"
"finally"
"for"
"get"
"global"
"if"
"implements"
"instanceof"
"interface"
"new"
"on"
"private"
"protected"
"public"
"return"
"set"
"static"
"switch"
"testMethod"
"throw"
"transient"
"try"
"trigger"
"virtual"
"when"
"while"
"with_sharing"
"without_sharing"
"inherited_sharing"
] @keyword
(assignment_expression
left: (identifier) @variable)
; (type_identifier) @type ;; not respecting precedence...
;; I don't love this but couldn't break them up right now
;; can't figure out how to let that be special without conflicting
;; in the grammar
"System.runAs" @method.defaultLibrary
(scoped_type_identifier
(type_identifier) @type)

@ -0,0 +1 @@
../tree-sitter-clojure/queries/highlights.scm

@ -34,6 +34,8 @@
"$"
"{"
"}"
"<"
">"
] @punctuation.special
[

@ -0,0 +1 @@
../tree-sitter-dart/queries/highlights.scm

@ -1,78 +0,0 @@
[
"/delete-node/"
"/delete-property/"
"/dts-v1/"
"/incbin/"
"/include/"
"/memreserve/"
"/omit-if-no-ref/"
"#define"
"#undef"
"#include"
"#if"
"#elif"
"#else"
"#endif"
"#ifdef"
"#ifndef"
] @keyword
[
"!"
"~"
"-"
"+"
"*"
"/"
"%"
"||"
"&&"
"|"
"^"
"&"
"=="
"!="
">"
">="
"<="
">"
"<<"
">>"
] @operator
[
","
";"
] @punctuation.delimiter
[
"("
")"
"{"
"}"
"<"
">"
] @punctuation.bracket
(call_expression
function: (identifier) @function)
(node
label: (identifier) @label)
(property
label: (identifier) @label)
(memory_reservation
label: (identifier) @label)
(property
name: (identifier) @property)
(identifier) @variable
(unit_address) @tag
(reference) @constant
(comment) @comment

@ -0,0 +1 @@
../tree-sitter-devicetree/queries/highlights.scm

@ -1,74 +0,0 @@
;; Source: https://github.com/Wilfred/tree-sitter-elisp/blob/main/queries/highlights.scm
;; Special forms
[
"and"
"catch"
"cond"
"condition-case"
"defconst"
"defvar"
"function"
"if"
"interactive"
"lambda"
"let"
"let*"
"or"
"prog1"
"prog2"
"progn"
"quote"
"save-current-buffer"
"save-excursion"
"save-restriction"
"setq"
"setq-default"
"unwind-protect"
"while"
] @keyword
;; Function definitions
[
"defun"
"defsubst"
] @keyword
(function_definition name: (symbol) @function)
(function_definition parameters: (list (symbol) @variable.parameter))
(function_definition docstring: (string) @comment)
;; Highlight macro definitions the same way as function definitions.
"defmacro" @keyword
(macro_definition name: (symbol) @function)
(macro_definition parameters: (list (symbol) @variable.parameter))
(macro_definition docstring: (string) @comment)
(comment) @comment
(integer) @number
(float) @number
(char) @number
(string) @string
[
"("
")"
"#["
"["
"]"
] @punctuation.bracket
[
"`"
"#'"
"'"
","
",@"
] @operator
;; Highlight nil and t as constants, unlike other symbols
[
"nil"
"t"
] @constant.builtin

@ -0,0 +1 @@
../tree-sitter-elisp/queries/highlights.scm

@ -0,0 +1 @@
../tree-sitter-elm/queries/highlights.scm

@ -0,0 +1 @@
../tree-sitter-gleam/queries/highlights.scm

@ -1,8 +0,0 @@
"(" @punctuation.bracket
")" @punctuation.bracket
"[&&NHX" @punctuation.bracket
"]" @punctuation.bracket
(name) @function
(length) @number
(nhx_entry key: (nhx_val) @keyword value: (nhx_val) @string.special)

@ -0,0 +1 @@
../tree-sitter-newick/queries/highlights.scm

@ -1,48 +0,0 @@
[
"syntax"
"edition"
"package"
"option"
"import"
"service"
"rpc"
"returns"
"message"
"enum"
"oneof"
"repeated"
"reserved"
"to"
] @keyword
[
(key_type)
(type)
(message_name)
(enum_name)
(service_name)
(rpc_name)
]@type
(string) @string
[
(int_lit)
(float_lit)
] @number
[
(true)
(false)
] @constant.builtin
(comment) @comment
[
"("
")"
"["
"]"
"{"
"}"
] @punctuation.bracket

@ -0,0 +1 @@
../tree-sitter-qmljs/queries/highlights.scm

@ -0,0 +1 @@
../tree-sitter-r/queries/highlights.scm

@ -0,0 +1 @@
../tree-sitter-racket/queries/highlights.scm

@ -0,0 +1 @@
../tree-sitter-scheme/queries/highlights.scm

@ -0,0 +1 @@
../tree-sitter-solidity/queries/highlights.scm

@ -0,0 +1 @@
../tree-sitter-sql/queries/highlights.scm

@ -0,0 +1 @@
tree-sitter-ada/src

@ -0,0 +1,9 @@
binding.gyp
bindings/node/binding.cc
bindings/node/index.js
bindings/rust/build.rs
bindings/rust/lib.rs
Cargo.lock
Cargo.toml
log.html
node_modules/

@ -0,0 +1,136 @@
# Tree-Sitter parser for Ada
The grammar is adapted from the work done by Stephen Leak for the
Emacs ada-mode. It was translated (partially for now) to tree-sitter
syntax, and slightly changed to reduce some conflicts. Tree-sitter
doesn't need a full syntax tree, so we can take some shortcuts in
the grammar.
## Installation
You will need neovim at least version 8.0 (not tested with earlier version).
Installation is very manual at this stage, until we can integrate this package
inside nvim-treesitter itself. At the moment, assuming you are using lua
configuration and Packer for your package management:
```lua
-- file: ~/.config/nvim/init.lua
-- Merge this with any existing Packer configuration you might already
-- have. This loads packer itself, then loads the new `ada.nvim` package.
require('packer').startup(function(use)
use(require('mytreesitter.nvim'))
end)
```
Then create a new file to setup treesitter (or merge with an existing
configuration of course).
```lua
-- file: ~/.config/nvim/mytreesitter.nvim
return {
'nvim-treesitter/nvim-treesitter',
requires = {
'nvim-treesitter/nvim-treesitter-textobjects'
},
run=function()
require('nvim-treesitter.install').update({ with_sync = true })
end,
config=function()
-- Add support for our Ada parser
local parsers = require "nvim-treesitter.parsers"
local parser_config = parsers.get_parser_configs()
parser_config.ada = {
install_info = {
url = "https://github.com/briot/tree-sitter-ada",
files = {"src/parser.c"},
generate_requires_npm = false,
requires_generate_from_grammar = false,
},
filetype = "ada",
}
end,
}
```
Finally, we need to install the Ada parser with:
```vim
:PackerSync " to install treesitter itself
:TSInstall ada " to install Ada support
```
However, the above part only installs the parser itself (to generate a syntax
tree from your source files). We now need to install queries, i.e. pattern
matching against that tree to provide various capabilities like syntax
highlighting, folding, indentation, smart textobject selection,...
For this, and until we can merge with nvim-treesitter itself, you will have
to clone this github repository, then copy the `queries/` directory to
```
~/.local/share/nvim/site/pack/packer/start/nvim-treesitter/queries/
```
## Usage
### Syntax highlighting
The above default configuration will replace the default regular
expressions-based syntax highlighting in vim. Instead, the highlighting is
based on the tree build every time you type something.
The default highlighting looks pretty much like the one from the standard
Ada mode. However, the tree-based approach potentially opens the door for
smart highlighting, like "Use a different background color for a subprogram
specification", "show constant definitions in blue" or other high-level
approaches.
WIP: document how users can do this in their own configuration files.
The current approach is to modify queries/highlights.scm
Potentially (though it seems to be disabled in neovim at the moment), the
highlighting can also get smarter. Going back to the "show constants in
blue" example above, the queries/locals.scm file adds a simple approach so
that references to those constants can point to the definition, and therefore
use the same blue highlighting.
Because neovim also has support for language servers (LSP), it is likely
better to rely on the language server here.
### Block folding
If you press <kbd>za</kbd> now, this will toggle the folding of the
"current block".
This is defined in queries/folds.scm, and currently knows about package
specifications, package bodies, subprograms bodies, if statements and loops.
Other semantic blocks could be added.
### Smart Selection
The file queries/textobjects.scm defines a function textobjects, so that
you can now use commands like
- <kbd>vaf</kbd> (v)isually select (a) (f)unction or subprogram
- <kbd>vif</kbd> (v)isually select (i)nside a (f)unction or subprogram
- <kbd>vai</kbd> (v)isually select (a) (i)f statement (or loop)
- <kbd>vii</kbd> (v)isually select (i)nside an (i)f statement (or loop)
## Development
Execute the following commands:
```bash
npm install
npm run test
```
## Documentation
The grammar itself is fully described in the file grammar.js.
When it processes it, TreeSitter generates src/grammar.json, which can be
converted to a EBNF format via https://github.com/mingodad/plgh/blob/main/json2ebnf.js
and rendered into a diagram on https://www.bottlecaps.de/rr/ui if you
prefer graphical visualization (you can also copy-paste from issue #1 a
pre-processed version of grammar.json).

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,28 @@
{
"name": "tree-sitter-ada",
"version": "0.1.0",
"private": true,
"dependencies": {
"tree-sitter": "0.20.0",
"tree-sitter-cli": "0.20.7"
},
"scripts": {
"generate": "tree-sitter generate",
"test": "tree-sitter generate && time tree-sitter test",
"testquick": "time tree-sitter test",
"update-test": "tree-sitter test --update",
"install": "cp queries/*scm ~/.local/share/nvim/site/pack/packer/start/nvim-treesitter/queries/ada; echo 'RUN :TSInstall ada' in nvim"
},
"main": "bindings/node",
"tree-sitter": [
{
"scope": "source.ada",
"file-types": [
"adb", "ads"
],
"highlights": [
"queries/highlights.scm"
]
}
]
}

@ -0,0 +1,14 @@
;; Support for folding in Ada
;; za toggles folding a package, subprogram, if statement or loop
[
(package_declaration)
(generic_package_declaration)
(package_body)
(subprogram_body)
(block_statement)
(if_statement)
(loop_statement)
(gnatprep_declarative_if_statement)
(gnatprep_if_statement)
] @fold

@ -0,0 +1,180 @@
;; highlight queries.
;; See the syntax at https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries
;; See also https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md#parser-configurations
;; for a list of recommended @ tags, though not all of them have matching
;; highlights in neovim.
[
"abort"
"abs"
"abstract"
"accept"
"access"
"all"
"array"
"at"
"begin"
"declare"
"delay"
"delta"
"digits"
"do"
"end"
"entry"
"exit"
"generic"
"interface"
"is"
"limited"
"null"
"of"
"others"
"out"
"pragma"
"private"
"range"
"synchronized"
"tagged"
"task"
"terminate"
"until"
"when"
] @keyword
[
"aliased"
"constant"
"renames"
] @storageclass
[
"mod"
"new"
"protected"
"record"
"subtype"
"type"
] @keyword.type
[
"with"
"use"
] @include
[
"body"
"function"
"overriding"
"procedure"
"package"
"separate"
] @keyword.function
[
"and"
"in"
"not"
"or"
"xor"
] @keyword.operator
[
"while"
"loop"
"for"
"parallel"
"reverse"
"some"
] @repeat
[
"return"
] @keyword.return
[
"case"
"if"
"else"
"then"
"elsif"
"select"
] @conditional
[
"exception"
"raise"
] @exception
(comment) @comment
(comment) @spell ;; spell-check comments
(string_literal) @string
(string_literal) @spell ;; spell-check strings
(character_literal) @string
(numeric_literal) @number
;; Highlight the name of subprograms
(procedure_specification name: (_) @function)
(function_specification name: (_) @function)
(package_declaration name: (_) @function)
(package_body name: (_) @function)
(generic_instantiation name: (_) @function)
(entry_declaration . (identifier) @function)
;; Some keywords should take different categories depending on the context
(use_clause "use" @include "type" @include)
(with_clause "private" @include)
(with_clause "limited" @include)
(use_clause (_) @namespace)
(with_clause (_) @namespace)
(loop_statement "end" @keyword.repeat)
(if_statement "end" @conditional)
(loop_parameter_specification "in" @keyword.repeat)
(loop_parameter_specification "in" @keyword.repeat)
(iterator_specification ["in" "of"] @keyword.repeat)
(range_attribute_designator "range" @keyword.repeat)
(raise_statement "with" @exception)
(gnatprep_declarative_if_statement) @preproc
(gnatprep_if_statement) @preproc
(gnatprep_identifier) @preproc
(subprogram_declaration "is" @keyword.function "abstract" @keyword.function)
(aspect_specification "with" @keyword.function)
(full_type_declaration "is" @keyword.type)
(subtype_declaration "is" @keyword.type)
(record_definition "end" @keyword.type)
(full_type_declaration (_ "access" @keyword.type))
(array_type_definition "array" @keyword.type "of" @keyword.type)
(access_to_object_definition "access" @keyword.type)
(access_to_object_definition "access" @keyword.type
[
(general_access_modifier "constant" @keyword.type)
(general_access_modifier "all" @keyword.type)
]
)
(range_constraint "range" @keyword.type)
(signed_integer_type_definition "range" @keyword.type)
(index_subtype_definition "range" @keyword.type)
(record_type_definition "abstract" @keyword.type)
(record_type_definition "tagged" @keyword.type)
(record_type_definition "limited" @keyword.type)
(record_type_definition (record_definition "null" @keyword.type))
(private_type_declaration "is" @keyword.type "private" @keyword.type)
(private_type_declaration "tagged" @keyword.type)
(private_type_declaration "limited" @keyword.type)
(task_type_declaration "task" @keyword.type "is" @keyword.type)
;; Gray the body of expression functions
(expression_function_declaration
(function_specification)
"is"
(_) @attribute
)
(subprogram_declaration (aspect_specification) @attribute)
;; Highlight full subprogram specifications
;(subprogram_body
; [
; (procedure_specification)
; (function_specification)
; ] @function.spec
;)
;; Highlight errors in red. This is not very useful in practice, as text will
;; be highlighted as user types, and the error could be elsewhere in the code.
;; This also requires defining :hi @error guifg=Red for instance.
(ERROR) @error

@ -0,0 +1,33 @@
;; Better highlighting by referencing to the definition, for variable
;; references. However, this is not yet supported by neovim
;; See https://tree-sitter.github.io/tree-sitter/syntax-highlighting#local-variables
(compilation) @scope
(package_declaration) @scope
(package_body) @scope
(subprogram_declaration) @scope
(subprogram_body) @scope
(block_statement) @scope
(with_clause (identifier) @definition.import)
(procedure_specification name: (_) @definition.function)
(function_specification name: (_) @definition.function)
(package_declaration name: (_) @definition.var)
(package_body name: (_) @definition.var)
(generic_instantiation . name: (_) @definition.var)
(component_declaration . (identifier) @definition.var)
(exception_declaration . (identifier) @definition.var)
(formal_object_declaration . (identifier) @definition.var)
(object_declaration . (identifier) @definition.var)
(parameter_specification . (identifier) @definition.var)
(full_type_declaration . (identifier) @definition.type)
(private_type_declaration . (identifier) @definition.type)
(private_extension_declaration . (identifier) @definition.type)
(incomplete_type_declaration . (identifier) @definition.type)
(protected_type_declaration . (identifier) @definition.type)
(formal_complete_type_declaration . (identifier) @definition.type)
(formal_incomplete_type_declaration . (identifier) @definition.type)
(task_type_declaration . (identifier) @definition.type)
(subtype_declaration . (identifier) @definition.type)
(identifier) @reference

@ -0,0 +1,23 @@
;; Support for high-level text objects selections.
;; For instance:
;; vaf (v)isually select (a) (f)unction or subprogram
;; vif (v)isually select (i)nside a (f)unction or subprogram
;; vai (v)isually select (a) (i)f statement (or loop)
;; vii (v)isually select (i)nside an (i)f statement (or loop)
;;
;; https://github.com/nvim-treesitter/nvim-treesitter-textobjects/blob/master/README.md
(subprogram_body) @function.outer
(subprogram_body (non_empty_declarative_part) @function.inner)
(subprogram_body (handled_sequence_of_statements) @function.inner)
(function_specification) @function.outer
(procedure_specification) @function.outer
(package_declaration) @function.outer
(generic_package_declaration) @function.outer
(package_body) @function.outer
(if_statement) @block.outer
(if_statement statements: (_) @block.inner)
(if_statement else_statements: (_) @block.inner)
(elsif_statement_item statements: (_) @block.inner)
(loop_statement) @block.outer
(loop_statement statements: (_) @block.inner)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,224 @@
#ifndef TREE_SITTER_PARSER_H_
#define TREE_SITTER_PARSER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#define ts_builtin_sym_error ((TSSymbol)-1)
#define ts_builtin_sym_end 0
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
typedef uint16_t TSStateId;
#ifndef TREE_SITTER_API_H_
typedef uint16_t TSSymbol;
typedef uint16_t TSFieldId;
typedef struct TSLanguage TSLanguage;
#endif
typedef struct {
TSFieldId field_id;
uint8_t child_index;
bool inherited;
} TSFieldMapEntry;
typedef struct {
uint16_t index;
uint16_t length;
} TSFieldMapSlice;
typedef struct {
bool visible;
bool named;
bool supertype;
} TSSymbolMetadata;
typedef struct TSLexer TSLexer;
struct TSLexer {
int32_t lookahead;
TSSymbol result_symbol;
void (*advance)(TSLexer *, bool);
void (*mark_end)(TSLexer *);
uint32_t (*get_column)(TSLexer *);
bool (*is_at_included_range_start)(const TSLexer *);
bool (*eof)(const TSLexer *);
};
typedef enum {
TSParseActionTypeShift,
TSParseActionTypeReduce,
TSParseActionTypeAccept,
TSParseActionTypeRecover,
} TSParseActionType;
typedef union {
struct {
uint8_t type;
TSStateId state;
bool extra;
bool repetition;
} shift;
struct {
uint8_t type;
uint8_t child_count;
TSSymbol symbol;
int16_t dynamic_precedence;
uint16_t production_id;
} reduce;
uint8_t type;
} TSParseAction;
typedef struct {
uint16_t lex_state;
uint16_t external_lex_state;
} TSLexMode;
typedef union {
TSParseAction action;
struct {
uint8_t count;
bool reusable;
} entry;
} TSParseActionEntry;
struct TSLanguage {
uint32_t version;
uint32_t symbol_count;
uint32_t alias_count;
uint32_t token_count;
uint32_t external_token_count;
uint32_t state_count;
uint32_t large_state_count;
uint32_t production_id_count;
uint32_t field_count;
uint16_t max_alias_sequence_length;
const uint16_t *parse_table;
const uint16_t *small_parse_table;
const uint32_t *small_parse_table_map;
const TSParseActionEntry *parse_actions;
const char * const *symbol_names;
const char * const *field_names;
const TSFieldMapSlice *field_map_slices;
const TSFieldMapEntry *field_map_entries;
const TSSymbolMetadata *symbol_metadata;
const TSSymbol *public_symbol_map;
const uint16_t *alias_map;
const TSSymbol *alias_sequences;
const TSLexMode *lex_modes;
bool (*lex_fn)(TSLexer *, TSStateId);
bool (*keyword_lex_fn)(TSLexer *, TSStateId);
TSSymbol keyword_capture_token;
struct {
const bool *states;
const TSSymbol *symbol_map;
void *(*create)(void);
void (*destroy)(void *);
bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
unsigned (*serialize)(void *, char *);
void (*deserialize)(void *, const char *, unsigned);
} external_scanner;
const TSStateId *primary_state_ids;
};
/*
* Lexer Macros
*/
#define START_LEXER() \
bool result = false; \
bool skip = false; \
bool eof = false; \
int32_t lookahead; \
goto start; \
next_state: \
lexer->advance(lexer, skip); \
start: \
skip = false; \
lookahead = lexer->lookahead;
#define ADVANCE(state_value) \
{ \
state = state_value; \
goto next_state; \
}
#define SKIP(state_value) \
{ \
skip = true; \
state = state_value; \
goto next_state; \
}
#define ACCEPT_TOKEN(symbol_value) \
result = true; \
lexer->result_symbol = symbol_value; \
lexer->mark_end(lexer);
#define END_STATE() return result;
/*
* Parse Table Macros
*/
#define SMALL_STATE(id) id - LARGE_STATE_COUNT
#define STATE(id) id
#define ACTIONS(id) id
#define SHIFT(state_value) \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.state = state_value \
} \
}}
#define SHIFT_REPEAT(state_value) \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.state = state_value, \
.repetition = true \
} \
}}
#define SHIFT_EXTRA() \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.extra = true \
} \
}}
#define REDUCE(symbol_val, child_count_val, ...) \
{{ \
.reduce = { \
.type = TSParseActionTypeReduce, \
.symbol = symbol_val, \
.child_count = child_count_val, \
__VA_ARGS__ \
}, \
}}
#define RECOVER() \
{{ \
.type = TSParseActionTypeRecover \
}}
#define ACCEPT_INPUT() \
{{ \
.type = TSParseActionTypeAccept \
}}
#ifdef __cplusplus
}
#endif
#endif // TREE_SITTER_PARSER_H_

@ -0,0 +1,83 @@
================================================================================
access types
================================================================================
package P is
type A is access Integer;
type B is access not null Integer;
type C is access constant Integer;
type D is access all Integer;
type E is access function return Boolean;
type F is access protected function return Boolean;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(access_to_object_definition
(identifier)))
(full_type_declaration
(identifier)
(access_to_object_definition
(null_exclusion)
(identifier)))
(full_type_declaration
(identifier)
(access_to_object_definition
(general_access_modifier)
(identifier)))
(full_type_declaration
(identifier)
(access_to_object_definition
(general_access_modifier)
(identifier)))
(full_type_declaration
(identifier)
(access_to_subprogram_definition
(result_profile
(identifier))))
(full_type_declaration
(identifier)
(access_to_subprogram_definition
(result_profile
(identifier)))))))
================================================================================
Dereference
================================================================================
procedure P is
begin
A := Acc.all;
Proc.all (1);
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(assignment_statement
(identifier)
(expression
(term
(selected_component
(identifier)
(identifier)))))
(procedure_call_statement
(selected_component
(identifier)
(identifier))
(actual_parameter_part
(parameter_association
(expression
(term
(numeric_literal))))))))))

@ -0,0 +1,289 @@
================================================================================
Definite
================================================================================
package P is
type A is array (1 .. 2) of Boolean;
V : constant A := (1 => True, 2 => False);
V2 : constant A := (1 => True, others => False);
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(array_type_definition
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))
(component_definition
(identifier))))
(object_declaration
(identifier)
(identifier)
(expression
(term
(named_array_aggregate
(array_component_association
(discrete_choice_list
(discrete_choice
(expression
(term
(numeric_literal)))))
(expression
(term
(identifier))))
(array_component_association
(discrete_choice_list
(discrete_choice
(expression
(term
(numeric_literal)))))
(expression
(term
(identifier))))))))
(object_declaration
(identifier)
(identifier)
(expression
(term
(named_array_aggregate
(array_component_association
(discrete_choice_list
(discrete_choice
(expression
(term
(numeric_literal)))))
(expression
(term
(identifier))))
(array_component_association
(discrete_choice_list
(discrete_choice))
(expression
(term
(identifier))))))))
(identifier))))
================================================================================
Definite-2
================================================================================
package P is
type A is array (1 .. 3) of Boolean;
V : constant A := (1, 2, 3);
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(array_type_definition
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))
(component_definition
(identifier))))
(object_declaration
(identifier)
(identifier)
(expression
(term
(positional_array_aggregate
(expression
(term
(numeric_literal)))
(expression
(term
(numeric_literal)))
(expression
(term
(numeric_literal))))))))))
================================================================================
Indefinite
================================================================================
package P is
type B is array (Natural range <>) of Boolean;
V : constant B := (1 .. 2 => False);
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(array_type_definition
(index_subtype_definition
(identifier))
(component_definition
(identifier))))
(object_declaration
(identifier)
(identifier)
(expression
(term
(named_array_aggregate
(array_component_association
(discrete_choice_list
(discrete_choice
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))))
(expression
(term
(identifier))))))))
(identifier))))
================================================================================
2D
================================================================================
package P is
type C is array (Natural range <>, Integer range <>) of Boolean;
V : constant C := (1 .. 2 => (1 .. 2 => False));
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(array_type_definition
(index_subtype_definition
(identifier))
(index_subtype_definition
(identifier))
(component_definition
(identifier))))
(object_declaration
(identifier)
(identifier)
(expression
(term
(named_array_aggregate
(array_component_association
(discrete_choice_list
(discrete_choice
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))))
(expression
(term
(named_array_aggregate
(array_component_association
(discrete_choice_list
(discrete_choice
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))))
(expression
(term
(identifier))))))))))))
(identifier))))
================================================================================
Index in array aggregates
================================================================================
procedure P is
begin
Arr := (for Index in 1 .. Count => Function_Returning_Limited (Index));
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(assignment_statement
(identifier)
(expression
(term
(named_array_aggregate
(array_component_association
(iterated_element_association
(loop_parameter_specification
(identifier)
(range_g
(term
(numeric_literal))
(term
(identifier))))
(expression
(term
(function_call
(identifier)
(actual_parameter_part
(parameter_association
(expression
(term
(identifier))))))))))))))))))
================================================================================
Slices
================================================================================
Proc (Arr (1 .. 2));
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(procedure_call_statement
(identifier)
(actual_parameter_part
(parameter_association
(expression
(term
(slice
(identifier)
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))))))))))
================================================================================
Slices with subtype declaration
================================================================================
A : Arr (Boolean);
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(object_declaration
(identifier)
(identifier)
(index_constraint
(identifier)))))

@ -0,0 +1,113 @@
================================================================================
Range attribute
================================================================================
procedure P is
A : MyArray (B'Range (1));
begin
for E in Arr'Range loop
null;
end loop;
for E in Arr'Range (1) loop
null;
end loop;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(non_empty_declarative_part
(object_declaration
(identifier)
(slice
(identifier)
(range_g
(identifier)
(tick)
(range_attribute_designator
(expression
(term
(numeric_literal))))))))
(handled_sequence_of_statements
(loop_statement
(iteration_scheme
(loop_parameter_specification
(identifier)
(range_g
(identifier)
(tick)
(range_attribute_designator))))
(null_statement))
(loop_statement
(iteration_scheme
(loop_parameter_specification
(identifier)
(range_g
(identifier)
(tick)
(range_attribute_designator
(expression
(term
(numeric_literal)))))))
(null_statement))))))
================================================================================
Reduction
================================================================================
procedure P is
X : Integer :=
[parallel for Val of M when Val > 100.0 => (Val, 1)]
'Reduce("+", 0);
begin
null;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(non_empty_declarative_part
(object_declaration
(identifier)
(identifier)
(expression
(term
(value_sequence
(iterated_element_association
(iterator_specification
(identifier)
(identifier)
(iterator_filter
(expression
(term
(identifier))
(relational_operator)
(term
(numeric_literal)))))
(expression
(term
(positional_array_aggregate
(expression
(term
(identifier)))
(expression
(term
(numeric_literal))))))))
(tick)
(reduction_attribute_designator
(identifier)
(reduction_specification
(string_literal)
(expression
(term
(numeric_literal)))))))))
(handled_sequence_of_statements
(null_statement)))))

@ -0,0 +1,92 @@
================================================================================
generic package
================================================================================
generic
A : Integer := 1;
type T (<>) is abstract tagged limited private or use My_Tagged;
type I;
with package Pro is new My_Pkg (F => 1, others => <>);
with procedure Proc is null;
with function Func return Boolean is <>;
package P is
pragma Compile_Time_Error (True, "an exception");
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(generic_package_declaration
(generic_formal_part
(formal_object_declaration
(identifier)
(identifier)
(expression
(term
(numeric_literal))))
(formal_complete_type_declaration
(identifier)
(unknown_discriminant_part)
(formal_private_type_definition)
(identifier))
(formal_incomplete_type_declaration
(identifier))
(formal_package_declaration
(identifier)
(function_call
(identifier)
(actual_parameter_part
(parameter_association
(component_choice_list
(identifier))
(expression
(term
(numeric_literal))))
(parameter_association
(component_choice_list)))))
(formal_subprogram_declaration
(formal_concrete_subprogram_declaration
(procedure_specification
(identifier))
(subprogram_default)))
(formal_subprogram_declaration
(formal_concrete_subprogram_declaration
(function_specification
(identifier)
(result_profile
(identifier)))
(subprogram_default))))
(package_declaration
(identifier)
(pragma_g
(identifier)
(pragma_argument_association
(expression
(term
(identifier))))
(pragma_argument_association
(expression
(term
(string_literal)))))))))
================================================================================
formal derived types
================================================================================
generic
type T is new P with private;
procedure A;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(generic_subprogram_declaration
(generic_formal_part
(formal_complete_type_declaration
(identifier)
(formal_derived_type_definition
(identifier))))
(procedure_specification
(identifier)))))

@ -0,0 +1,67 @@
================================================================================
String literals
================================================================================
A : String := "12'34";
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(object_declaration
(identifier)
(identifier)
(expression
(term
(string_literal))))))
================================================================================
String literals with quotes
================================================================================
A : String := "12""23";
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(object_declaration
(identifier)
(identifier)
(expression
(term
(string_literal))))))
================================================================================
Numeric literals with underscore
================================================================================
A : Integer := 12_14.12_122E+11_2;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(object_declaration
(identifier)
(identifier)
(expression
(term
(numeric_literal))))))
=======================
Based numerals
=======================
A : Integer := 16#FA01#E+02;
-----
(compilation
(compilation_unit
(object_declaration
(identifier)
(identifier)
(expression
(term
(numeric_literal))))))

@ -0,0 +1,193 @@
================================================================================
with clauses
================================================================================
with Ada.Text_IO, System; -- multiple names, and fully qualified
limited with Ada; -- limited with
private with Ada;
limited private with Ada;
use Ada.Text_IO, System;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(with_clause
(selected_component
(identifier)
(identifier))
(identifier)))
(comment)
(compilation_unit
(with_clause
(identifier)))
(comment)
(compilation_unit
(with_clause
(identifier)))
(compilation_unit
(with_clause
(identifier)))
(compilation_unit
(use_clause
(selected_component
(identifier)
(identifier))
(identifier))))
================================================================================
Case insensitive
================================================================================
PACkaGe P1 Is
enD;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier))))
================================================================================
multiple compilation units
================================================================================
package P1 is
package Nested is
end Nested;
end P1;
private package Child.P2 is -- comment to be ignored
private
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(package_declaration
(identifier)
(identifier))
(identifier)))
(compilation_unit
(package_declaration
(selected_component
(identifier)
(identifier))
(comment))))
================================================================================
body
================================================================================
package body Child.P2 is
package body Nested is
begin
null;
end Nested;
begin
null;
end Child.P2;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_body
(selected_component
(identifier)
(identifier))
(non_empty_declarative_part
(package_body
(identifier)
(handled_sequence_of_statements
(null_statement))
(identifier)))
(handled_sequence_of_statements
(null_statement))
(selected_component
(identifier)
(identifier)))))
================================================================================
separate
================================================================================
separate (Child) package body P2 is
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subunit
(identifier)
(package_body
(identifier)))))
================================================================================
private types
================================================================================
package P is
type A is private;
type B is abstract synchronized new C with private;
type C is abstract tagged limited private with Size => 8;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(private_type_declaration
(identifier))
(private_extension_declaration
(identifier)
(identifier))
(private_type_declaration
(identifier)
(aspect_specification
(aspect_mark_list
(aspect_association
(identifier)
(expression
(term
(numeric_literal))))))))))
================================================================================
incomplete types
================================================================================
package P is
private
type I;
type R (A : Integer);
type R2 (<>);
type T is tagged;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(incomplete_type_declaration
(identifier))
(incomplete_type_declaration
(identifier)
(known_discriminant_part
(discriminant_specification_list
(discriminant_specification
(identifier)
(identifier)))))
(incomplete_type_declaration
(identifier)
(unknown_discriminant_part))
(incomplete_type_declaration
(identifier)))))

@ -0,0 +1,90 @@
================================================================================
pragma on record field
================================================================================
package P is
type R is record
Started : Boolean := False;
pragma Atomic (Started);
end record;
pragma Foo;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(record_type_definition
(record_definition
(component_list
(component_declaration
(identifier)
(component_definition
(identifier))
(expression
(term
(identifier))))
(pragma_g
(identifier)
(pragma_argument_association
(expression
(term
(identifier)))))))))
(pragma_g
(identifier)))))
================================================================================
pragma in statement
================================================================================
procedure P is
begin
null;
pragma Foo;
null;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(null_statement)
(pragma_g
(identifier))
(null_statement)))))
================================================================================
pragma in tasks
================================================================================
package P is
task type T is
pragma Storage_Size (1024);
end T;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(task_type_declaration
(identifier)
(task_definition
(pragma_g
(identifier)
(pragma_argument_association
(expression
(term
(numeric_literal)))))
(identifier)))))))

@ -0,0 +1,83 @@
================================================================================
Preprocess-if (GNAT)
================================================================================
procedure P is
#if not CHECKING_MODE then
pragma Suppress (Access_Checks);
#end if;
begin
null;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(non_empty_declarative_part
(gnatprep_declarative_if_statement
(expression
(term
(factor_not
(identifier))))
(pragma_g
(identifier)
(pragma_argument_association
(expression
(term
(identifier)))))))
(handled_sequence_of_statements
(null_statement)))))
================================================================================
Preprocess substitution (GNAT)
================================================================================
package P is
Flavour : constant F := $FLAVOR;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(object_declaration
(identifier)
(identifier)
(expression
(term
(gnatprep_identifier)))))))
================================================================================
Preprocess empty (GNAT)
================================================================================
procedure P is
#if not CHECKING_MODE then
-- pragma Suppress (Access_Checks);
#end if;
begin
null;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(non_empty_declarative_part
(gnatprep_declarative_if_statement
(expression
(term
(factor_not
(identifier))))
(comment)))
(handled_sequence_of_statements
(null_statement)))))

@ -0,0 +1,110 @@
================================================================================
protected objects
================================================================================
package body P is
protected Obj is
procedure Proc;
function Func return Boolean;
entry E;
entry E2 (Color)(A : Integer);
private
Field : Integer;
end Obj;
protected body Obj is
procedure Proc is begin abort T; end;
function Func return Boolean is begin return False; end;
entry E when Field > 0 is
begin
requeue E with abort;
end E;
entry E2 (for C in Color)(A : Integer) when True is
begin
null;
end E2;
end Obj;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_body
(identifier)
(non_empty_declarative_part
(object_declaration
(single_protected_declaration
(identifier)
(protected_definition
(subprogram_declaration
(procedure_specification
(identifier)))
(subprogram_declaration
(function_specification
(identifier)
(result_profile
(identifier))))
(entry_declaration
(identifier))
(entry_declaration
(identifier)
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier))))
(component_declaration
(identifier)
(component_definition
(identifier)))
(identifier))))
(protected_body
(identifier)
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(abort_statement
(identifier))))
(subprogram_body
(function_specification
(identifier)
(result_profile
(identifier)))
(handled_sequence_of_statements
(simple_return_statement
(expression
(term
(identifier))))))
(entry_body
(identifier)
(entry_barrier
(expression
(term
(identifier))
(relational_operator)
(term
(numeric_literal))))
(handled_sequence_of_statements
(requeue_statement
(identifier)))
(identifier))
(entry_body
(identifier)
(non_empty_entry_body_formal_part
(entry_index_specification
(identifier)
(identifier))
(formal_part
(parameter_specification
(identifier)
(identifier))))
(entry_barrier
(expression
(term
(identifier))))
(handled_sequence_of_statements
(null_statement))
(identifier))
(identifier))))))

@ -0,0 +1,565 @@
================================================================================
null record
================================================================================
package P is
type R is null record;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(record_type_definition
(record_definition))))))
================================================================================
records
================================================================================
package P is
type R2 is record
A : aliased Integer;
B : Integer range 0 .. 2;
C, D : not null access Integer;
end record;
for R2 use record
A at 0 range 0 .. 31;
end record;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(record_type_definition
(record_definition
(component_list
(component_declaration
(identifier)
(component_definition
(identifier)))
(component_declaration
(identifier)
(component_definition
(identifier)
(range_constraint
(range_g
(term
(numeric_literal))
(term
(numeric_literal))))))
(component_declaration
(identifier)
(identifier)
(component_definition
(access_definition
(null_exclusion)
(identifier))))))))
(record_representation_clause
(identifier)
(component_clause
(identifier)
(expression
(term
(numeric_literal)))
(term
(numeric_literal))
(term
(numeric_literal)))))))
================================================================================
Multiple fields on one line
================================================================================
package P is
type R is record
A, B : Integer;
end record;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(record_type_definition
(record_definition
(component_list
(component_declaration
(identifier)
(identifier)
(component_definition
(identifier))))))))))
================================================================================
Discriminated
================================================================================
package P is
type R (A : Integer; B : Integer) is record
F : Float;
end record;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(known_discriminant_part
(discriminant_specification_list
(discriminant_specification
(identifier)
(identifier))
(discriminant_specification
(identifier)
(identifier))))
(record_type_definition
(record_definition
(component_list
(component_declaration
(identifier)
(component_definition
(identifier))))))))))
================================================================================
tagged
================================================================================
package P is
type T is abstract tagged limited null record;
type T2 is new T with record
F : Integer;
end record;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(record_type_definition
(record_definition)))
(full_type_declaration
(identifier)
(derived_type_definition
(identifier)
(record_extension_part
(record_definition
(component_list
(component_declaration
(identifier)
(component_definition
(identifier)))))))))))
================================================================================
Variant
================================================================================
package P is
type R (A : Integer) is record
case A is
when 0 | 1 .. 2 =>
B : Integer;
when others =>
null;
end case;
end record;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(known_discriminant_part
(discriminant_specification_list
(discriminant_specification
(identifier)
(identifier))))
(record_type_definition
(record_definition
(component_list
(variant_part
(identifier)
(variant_list
(variant
(discrete_choice_list
(discrete_choice
(expression
(term
(numeric_literal))))
(discrete_choice
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))))
(component_list
(component_declaration
(identifier)
(component_definition
(identifier)))))
(variant
(discrete_choice_list
(discrete_choice))
(component_list)))))))))))
================================================================================
interface
================================================================================
package P is
type R is interface;
type R2 is interface and Intf1;
type R3 is new Root and R with null record;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(interface_type_definition))
(full_type_declaration
(identifier)
(interface_type_definition
(identifier)))
(full_type_declaration
(identifier)
(derived_type_definition
(identifier)
(identifier)
(record_extension_part
(record_definition)))))))
================================================================================
record aggregates
================================================================================
procedure P is
begin
A := (F1 => 1, F2 => 2);
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(assignment_statement
(identifier)
(expression
(term
(record_aggregate
(record_component_association_list
(component_choice_list
(identifier))
(expression
(term
(numeric_literal)))
(component_choice_list
(identifier))
(expression
(term
(numeric_literal))))))))))))
================================================================================
record aggregate extension
================================================================================
procedure P is
begin
A := (B with F3 => 2);
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(assignment_statement
(identifier)
(expression
(term
(extension_aggregate
(expression
(term
(identifier)))
(record_component_association_list
(component_choice_list
(identifier))
(expression
(term
(numeric_literal))))))))))))
================================================================================
record delta aggregate
================================================================================
procedure P is
begin
A := (B with delta F3 => 2);
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(assignment_statement
(identifier)
(expression
(term
(record_delta_aggregate
(expression
(term
(identifier)))
(record_component_association_list
(component_choice_list
(identifier))
(expression
(term
(numeric_literal))))))))))))
================================================================================
Variant records
================================================================================
type R (D : Boolean) is record
A : Integer;
case D is
when True => null;
when False =>
B : Integer := 1;
-- some comment
end case;
end record;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(full_type_declaration
(identifier)
(known_discriminant_part
(discriminant_specification_list
(discriminant_specification
(identifier)
(identifier))))
(record_type_definition
(record_definition
(component_list
(component_declaration
(identifier)
(component_definition
(identifier)))
(variant_part
(identifier)
(variant_list
(variant
(discrete_choice_list
(discrete_choice
(identifier)))
(component_list))
(variant
(discrete_choice_list
(discrete_choice
(identifier)))
(component_list
(component_declaration
(identifier)
(component_definition
(identifier))
(expression
(term
(numeric_literal)))))))
(comment))))))))
================================================================================
variants 2
================================================================================
type R (S : Config_Side) is record
E : Duration;
F : Duration;
case Side is
when Config_Consumer =>
C : Duration;
end case;
end record;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(full_type_declaration
(identifier)
(known_discriminant_part
(discriminant_specification_list
(discriminant_specification
(identifier)
(identifier))))
(record_type_definition
(record_definition
(component_list
(component_declaration
(identifier)
(component_definition
(identifier)))
(component_declaration
(identifier)
(component_definition
(identifier)))
(variant_part
(identifier)
(variant_list
(variant
(discrete_choice_list
(discrete_choice
(identifier)))
(component_list
(component_declaration
(identifier)
(component_definition
(identifier)))))))))))))
================================================================================
Extension with aggregate
================================================================================
procedure Proc is
Null : constant Rec := (Parent with A => null);
Null2 : constant Rec := (Parent with null);
begin
null;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(non_empty_declarative_part
(object_declaration
(identifier)
(identifier)
(expression
(term
(extension_aggregate
(expression
(term
(identifier)))
(record_component_association_list
(component_choice_list
(identifier))
(expression
(term
(primary_null))))))))
(object_declaration
(identifier)
(identifier)
(expression
(term
(extension_aggregate
(expression
(term
(identifier)))
(expression
(term
(primary_null))))))))
(handled_sequence_of_statements
(null_statement)))))
================================================================================
Record with discr
================================================================================
procedure Proc is
type Rec (Len : Natural) is null record;
R : Rec (0);
R2 : Rec (if N > 0 then 1 else 0);
begin
null;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(non_empty_declarative_part
(full_type_declaration
(identifier)
(known_discriminant_part
(discriminant_specification_list
(discriminant_specification
(identifier)
(identifier))))
(record_type_definition
(record_definition)))
(object_declaration
(identifier)
(identifier)
(discriminant_constraint
(expression
(term
(numeric_literal)))))
(object_declaration
(identifier)
(identifier)
(discriminant_constraint
(if_expression
(expression
(term
(identifier))
(relational_operator)
(term
(numeric_literal)))
(expression
(term
(numeric_literal)))
(expression
(term
(numeric_literal)))))))
(handled_sequence_of_statements
(null_statement)))))

@ -0,0 +1,52 @@
================================================================================
Renames object
================================================================================
procedure P is
Threshold renames With_Type_Inference;
A : Integer renames B;
CE : exception renames Constraint_Error;
package TIO renames Ada.Text_IO;
procedure Proc (A : Integer) renames Process;
generic procedure Proc renames Generic_Process;
begin
null;
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(non_empty_declarative_part
(object_renaming_declaration
(identifier)
(identifier))
(object_renaming_declaration
(identifier)
(identifier)
(identifier))
(exception_renaming_declaration
(identifier)
(identifier))
(package_renaming_declaration
(identifier)
(selected_component
(identifier)
(identifier)))
(subprogram_renaming_declaration
(procedure_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier))))
(identifier))
(generic_renaming_declaration
(identifier)
(identifier)))
(handled_sequence_of_statements
(null_statement))
(identifier))))

@ -0,0 +1,95 @@
================================================================================
Separate subprograms
================================================================================
package body P is
overriding procedure Proc is separate with Inline;
overriding function Func return Boolean is separate with Inline;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_body
(identifier)
(non_empty_declarative_part
(body_stub
(subprogram_body_stub
(overriding_indicator)
(procedure_specification
(identifier))
(aspect_specification
(aspect_mark_list
(aspect_association
(identifier))))))
(body_stub
(subprogram_body_stub
(overriding_indicator)
(function_specification
(identifier)
(result_profile
(identifier)))
(aspect_specification
(aspect_mark_list
(aspect_association
(identifier))))))))))
================================================================================
Separate packages
================================================================================
package body P is
package body Child is separate;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_body
(identifier)
(non_empty_declarative_part
(body_stub
(package_body_stub
(identifier)))))))
================================================================================
Separate protected
================================================================================
package body P is
protected body Prot is separate;
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_body
(identifier)
(non_empty_declarative_part
(body_stub
(protected_body_stub
(identifier))))
(identifier))))
================================================================================
Separate task
================================================================================
package body P is
task body T is separate;
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_body
(identifier)
(non_empty_declarative_part
(body_stub
(task_body_stub
(identifier))))
(identifier))))

@ -0,0 +1,643 @@
================================================================================
Untyped Constant
================================================================================
A : constant := 111;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(number_declaration
(identifier)
(expression
(term
(numeric_literal))))))
================================================================================
Factors
================================================================================
A : constant Integer := 2 + 8 * 3;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(object_declaration
(identifier)
(identifier)
(expression
(term
(numeric_literal))
(binary_adding_operator)
(term
(numeric_literal)
(multiplying_operator)
(numeric_literal))))))
================================================================================
While
================================================================================
procedure P is
begin
while True loop
exit;
exit when A > 0;
end loop;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(loop_statement
(iteration_scheme
(expression
(term
(identifier))))
(exit_statement)
(exit_statement
(expression
(term
(identifier))
(relational_operator)
(term
(numeric_literal)))))))))
================================================================================
For loops
================================================================================
procedure P is
begin
for E in Pkg.Arr'Range loop
goto end_loop;
<<end_loop>>
end loop;
for E of reverse Arr loop
delay 1.0;
end loop;
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(loop_statement
(iteration_scheme
(loop_parameter_specification
(identifier)
(range_g
(selected_component
(identifier)
(identifier))
(tick)
(range_attribute_designator))))
(goto_statement
(identifier))
(label
(identifier)))
(loop_statement
(iteration_scheme
(iterator_specification
(identifier)
(identifier)))
(delay_relative_statement
(expression
(term
(numeric_literal))))))
(identifier))))
================================================================================
Named loop
================================================================================
procedure P is
begin
Main:
loop
exit Main;
end loop Main;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(loop_statement
(loop_label
(identifier))
(exit_statement
(identifier))
(identifier))))))
================================================================================
Return
================================================================================
function F return Boolean is
begin
return True;
return A : My_Rec := (F => 1) do
null;
end return;
end F;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(function_specification
(identifier)
(result_profile
(identifier)))
(handled_sequence_of_statements
(simple_return_statement
(expression
(term
(identifier))))
(extended_return_statement
(extended_return_object_declaration
(identifier)
(identifier)
(expression
(term
(record_aggregate
(record_component_association_list
(component_choice_list
(identifier))
(expression
(term
(numeric_literal))))))))
(handled_sequence_of_statements
(null_statement))))
(identifier))))
================================================================================
Procedure call
================================================================================
procedure P (A : Integer) is
begin
P2 (1, False);
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier))))
(handled_sequence_of_statements
(procedure_call_statement
(identifier)
(actual_parameter_part
(parameter_association
(expression
(term
(numeric_literal))))
(parameter_association
(expression
(term
(identifier)))))))
(identifier))))
================================================================================
Raise exception
================================================================================
procedure P is
begin
raise Constraint_Error;
raise Constraint_Error with "msg";
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(raise_statement
(identifier))
(raise_statement
(identifier)
(expression
(term
(string_literal))))))))
================================================================================
Function calls
================================================================================
procedure P is
A : Integer;
begin
A := Func (B => 1);
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(non_empty_declarative_part
(object_declaration
(identifier)
(identifier)))
(handled_sequence_of_statements
(assignment_statement
(identifier)
(expression
(term
(function_call
(identifier)
(actual_parameter_part
(parameter_association
(component_choice_list
(identifier))
(expression
(term
(numeric_literal)))))))))))))
================================================================================
if statement
================================================================================
procedure P is
begin
if A = 0 or else B = 1 then
declare
begin
null;
end;
elsif A = 1 then
declare
C : Integer;
begin
null;
exception
when Constraint_Error => null;
end;
else
begin
null;
end;
end if;
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(if_statement
(expression
(term
(identifier))
(relational_operator)
(term
(numeric_literal))
(term
(identifier))
(relational_operator)
(term
(numeric_literal)))
(block_statement
(handled_sequence_of_statements
(null_statement)))
(elsif_statement_item
(expression
(term
(identifier))
(relational_operator)
(term
(numeric_literal)))
(block_statement
(non_empty_declarative_part
(object_declaration
(identifier)
(identifier)))
(handled_sequence_of_statements
(null_statement)
(exception_handler
(exception_choice_list
(exception_choice
(identifier)))
(null_statement)))))
(block_statement
(handled_sequence_of_statements
(null_statement)))))
(identifier))))
================================================================================
Case statement
================================================================================
procedure P is
begin
case Func(A => 1) is
when '1' .. '2' =>
null;
when '3' | '4' =>
null;
end case;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(case_statement
(expression
(term
(function_call
(identifier)
(actual_parameter_part
(parameter_association
(component_choice_list
(identifier))
(expression
(term
(numeric_literal))))))))
(case_statement_alternative
(discrete_choice_list
(discrete_choice
(range_g
(term
(character_literal))
(term
(character_literal)))))
(null_statement))
(case_statement_alternative
(discrete_choice_list
(discrete_choice
(character_literal))
(discrete_choice
(character_literal)))
(null_statement)))))))
================================================================================
Allocators
================================================================================
procedure P is
begin
A := new T;
A := new (pkg.pool) T'((F => 1));
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(assignment_statement
(identifier)
(expression
(term
(allocator
(identifier)))))
(assignment_statement
(identifier)
(expression
(term
(allocator
(subpool_specification
(selected_component
(identifier)
(identifier)))
(qualified_expression
(identifier)
(tick)
(expression
(term
(record_aggregate
(record_component_association_list
(component_choice_list
(identifier))
(expression
(term
(numeric_literal))))))))))))))))
================================================================================
Filtered for loops
================================================================================
procedure P is
begin
for E of Some_Array when E /= 0 loop
null;
end loop;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(loop_statement
(iteration_scheme
(iterator_specification
(identifier)
(identifier)
(iterator_filter
(expression
(term
(identifier))
(relational_operator)
(term
(numeric_literal))))))
(null_statement))))))
================================================================================
Assignment target name
================================================================================
procedure P is
begin
Some_Very_Long.And_Complex (Expression) := @ + 1;
Another_Very_Long.And_Complex (Expression) := Function_Call (@);
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(assignment_statement
(function_call
(selected_component
(identifier)
(identifier))
(actual_parameter_part
(parameter_association
(expression
(term
(identifier))))))
(expression
(term
(target_name))
(binary_adding_operator)
(term
(numeric_literal))))
(assignment_statement
(function_call
(selected_component
(identifier)
(identifier))
(actual_parameter_part
(parameter_association
(expression
(term
(identifier))))))
(expression
(term
(function_call
(identifier)
(actual_parameter_part
(parameter_association
(expression
(term
(target_name)))))))))))))
================================================================================
if-expressions
================================================================================
procedure P is
begin
S := new String'((if N /= "" then N else "12"));
S := new String'(if N /= "" then N else "12");
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(assignment_statement
(identifier)
(expression
(term
(allocator
(qualified_expression
(identifier)
(tick)
(expression
(term
(if_expression
(expression
(term
(identifier))
(relational_operator)
(term
(string_literal)))
(expression
(term
(identifier)))
(expression
(term
(string_literal)))))))))))
(assignment_statement
(identifier)
(expression
(term
(allocator
(qualified_expression
(identifier)
(tick)
(if_expression
(expression
(term
(identifier))
(relational_operator)
(term
(string_literal)))
(expression
(term
(identifier)))
(expression
(term
(string_literal)))))))))))))
================================================================================
Re-raise
================================================================================
procedure P is
begin
null;
exception
when others =>
Proc;
pragma Assert (True);
raise;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(procedure_specification
(identifier))
(handled_sequence_of_statements
(null_statement)
(exception_handler
(exception_choice_list
(exception_choice))
(procedure_call_statement
(identifier))
(pragma_g
(identifier)
(pragma_argument_association
(expression
(term
(identifier)))))
(raise_statement))))))

@ -0,0 +1,603 @@
================================================================================
null procedure
================================================================================
package P is
procedure A is null;
overriding procedure B is null;
procedure B is null with Inline;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(null_procedure_declaration
(procedure_specification
(identifier)))
(null_procedure_declaration
(overriding_indicator)
(procedure_specification
(identifier)))
(null_procedure_declaration
(procedure_specification
(identifier))
(aspect_specification
(aspect_mark_list
(aspect_association
(identifier))))))))
================================================================================
procedures
================================================================================
package P is
procedure A (P1, P2 : Integer; P3 : Float)
with Inline;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(subprogram_declaration
(procedure_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier)
(identifier))
(parameter_specification
(identifier)
(identifier))))
(aspect_specification
(aspect_mark_list
(aspect_association
(identifier))))))))
================================================================================
abstract procedures
================================================================================
package P is
procedure B is abstract;
overriding procedure C is abstract with Inline;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(subprogram_declaration
(procedure_specification
(identifier)))
(subprogram_declaration
(overriding_indicator)
(procedure_specification
(identifier))
(aspect_specification
(aspect_mark_list
(aspect_association
(identifier))))))))
================================================================================
functions
================================================================================
package P is
function F (A, B : Integer) return not null access Integer
with Inline, Convention => C;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(subprogram_declaration
(function_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier)
(identifier)))
(result_profile
(access_definition
(null_exclusion)
(identifier))))
(aspect_specification
(aspect_mark_list
(aspect_association
(identifier))
(aspect_association
(identifier)
(expression
(term
(identifier))))))))))
================================================================================
subprogram body
================================================================================
package body P is
procedure A (B : Integer) is
V : Integer;
begin
null;
end A;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_body
(identifier)
(non_empty_declarative_part
(subprogram_body
(procedure_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier))))
(non_empty_declarative_part
(object_declaration
(identifier)
(identifier)))
(handled_sequence_of_statements
(null_statement))
(identifier))))))
================================================================================
Expression function membership
================================================================================
function F2 (A : Integer) return Boolean
is (A not in Small_Integer);
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(expression_function_declaration
(function_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier)))
(result_profile
(identifier)))
(expression
(relation_membership
(term
(identifier))
(membership_choice_list
(term
(identifier))))))))
================================================================================
Expression function declare
================================================================================
function F2 (A : Integer) return Boolean
is (declare B : constant Integer := A + 1; begin B);
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(expression_function_declaration
(function_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier)))
(result_profile
(identifier)))
(declare_expression
(object_declaration
(identifier)
(identifier)
(expression
(term
(identifier))
(binary_adding_operator)
(term
(numeric_literal))))
(expression
(term
(identifier)))))))
================================================================================
Expression function raise
================================================================================
function F3 return Boolean
is (raise Constraint_Error);
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(expression_function_declaration
(function_specification
(identifier)
(result_profile
(identifier)))
(expression
(raise_expression
(identifier))))))
================================================================================
Expression function simple
================================================================================
function F4 return Boolean is (True);
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(expression_function_declaration
(function_specification
(identifier)
(result_profile
(identifier)))
(expression
(term
(identifier))))))
================================================================================
Expression function if
================================================================================
function F (A : Integer) return Boolean
is (if A = 0 or A = 1 then True else False);
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(expression_function_declaration
(function_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier)))
(result_profile
(identifier)))
(if_expression
(expression
(term
(identifier))
(relational_operator)
(term
(numeric_literal))
(term
(identifier))
(relational_operator)
(term
(numeric_literal)))
(expression
(term
(identifier)))
(expression
(term
(identifier)))))))
================================================================================
Expression function if extra parenthesis
================================================================================
function F5 (A : Integer) return Boolean
is ((if A = 0 then True else False));
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(expression_function_declaration
(function_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier)))
(result_profile
(identifier)))
(expression
(term
(if_expression
(expression
(term
(identifier))
(relational_operator)
(term
(numeric_literal)))
(expression
(term
(identifier)))
(expression
(term
(identifier)))))))))
================================================================================
Expression function case
================================================================================
function F (A : Integer) return Boolean
is (case A + 1 is
when 0 .. 1 | 3 .. 4 => True,
when others => False);
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(expression_function_declaration
(function_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier)))
(result_profile
(identifier)))
(case_expression
(expression
(term
(identifier))
(binary_adding_operator)
(term
(numeric_literal)))
(case_expression_alternative
(discrete_choice_list
(discrete_choice
(range_g
(term
(numeric_literal))
(term
(numeric_literal))))
(discrete_choice
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))))
(expression
(term
(identifier))))
(case_expression_alternative
(discrete_choice_list
(discrete_choice))
(expression
(term
(identifier))))))))
================================================================================
Expression function array
================================================================================
function F return My_Array is (1 .. 2 => True);
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(expression_function_declaration
(function_specification
(identifier)
(result_profile
(identifier)))
(named_array_aggregate
(array_component_association
(discrete_choice_list
(discrete_choice
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))))
(expression
(term
(identifier))))))))
================================================================================
Expression function quantified
================================================================================
function F (A : My_Array) return Boolean
is (for some E of A => E = 1);
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(expression_function_declaration
(function_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier)))
(result_profile
(identifier)))
(quantified_expression
(quantifier)
(iterator_specification
(identifier)
(identifier))
(expression
(term
(identifier))
(relational_operator)
(term
(numeric_literal)))))))
================================================================================
Operators
================================================================================
overriding function "<" (Left, Right : My_Int) return Boolean is
begin
return False;
end "<";
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_body
(overriding_indicator)
(function_specification
(string_literal)
(formal_part
(parameter_specification
(identifier)
(identifier)
(identifier)))
(result_profile
(identifier)))
(handled_sequence_of_statements
(simple_return_statement
(expression
(term
(identifier)))))
(string_literal))))
================================================================================
Access Parameters
================================================================================
procedure Proc (A : access Integer);
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_declaration
(procedure_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(access_definition
(identifier))))))))
================================================================================
Subprogram and field access
================================================================================
V := Func (2).Value;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(assignment_statement
(identifier)
(expression
(term
(selected_component
(function_call
(identifier)
(actual_parameter_part
(parameter_association
(expression
(term
(numeric_literal))))))
(identifier)))))))
================================================================================
Parameterless Procedure call
================================================================================
Proc;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(procedure_call_statement
(identifier))))
================================================================================
Parameterless Function call
================================================================================
A := Func;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(assignment_statement
(identifier)
(expression
(term
(identifier))))))
================================================================================
Parameter association for operators
================================================================================
package P is new Pack ("+" => "+", "-" => Imported."+");
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(generic_instantiation
(identifier)
(function_call
(identifier)
(actual_parameter_part
(parameter_association
(component_choice_list
(string_literal))
(expression
(term
(string_literal))))
(parameter_association
(component_choice_list
(string_literal))
(expression
(term
(selected_component
(identifier)
(string_literal))))))))))
================================================================================
Function renaming
================================================================================
function F (S : String) return Boolean renames F2;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(subprogram_renaming_declaration
(function_specification
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier)))
(result_profile
(identifier)))
(identifier))))

@ -0,0 +1,294 @@
================================================================================
tasks
================================================================================
package body P is
task T is
entry E (A : Integer);
private
entry E2;
end T;
task body T is
A : Integer;
begin
accept E (A : Integer) do
null;
end E;
select
Call (1);
or
delay 1.0;
end select;
select
Call (1);
else
null;
end select;
end T;
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_body
(identifier)
(non_empty_declarative_part
(object_declaration
(single_task_declaration
(identifier)
(task_definition
(entry_declaration
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier))))
(entry_declaration
(identifier))
(identifier))))
(task_body
(identifier)
(non_empty_declarative_part
(object_declaration
(identifier)
(identifier)))
(handled_sequence_of_statements
(accept_statement
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier)))
(handled_sequence_of_statements
(null_statement))
(identifier))
(timed_entry_call
(entry_call_alternative
(procedure_call_statement
(identifier)
(actual_parameter_part
(parameter_association
(expression
(term
(numeric_literal)))))))
(delay_alternative
(delay_relative_statement
(expression
(term
(numeric_literal))))))
(conditional_entry_call
(entry_call_alternative
(procedure_call_statement
(identifier)
(actual_parameter_part
(parameter_association
(expression
(term
(numeric_literal)))))))
(null_statement)))
(identifier)))
(identifier))))
================================================================================
Task types
================================================================================
package body P is
task type T1;
task type T is
entry E (A : Integer);
end T;
task type T2 is new T with
entry E2;
end T2;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_body
(identifier)
(non_empty_declarative_part
(full_type_declaration
(task_type_declaration
(identifier)))
(full_type_declaration
(task_type_declaration
(identifier)
(task_definition
(entry_declaration
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier))))
(identifier))))
(full_type_declaration
(task_type_declaration
(identifier)
(identifier)
(task_definition
(entry_declaration
(identifier))
(identifier))))))))
================================================================================
asynchronous select
================================================================================
package body P is
task body T is
begin
select
Proc (1);
null;
then abort
Proc2;
end select;
end;
end;
null;
end;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_body
(identifier)
(non_empty_declarative_part
(task_body
(identifier)
(handled_sequence_of_statements
(asynchronous_select
(triggering_alternative
(procedure_call_statement
(identifier)
(actual_parameter_part
(parameter_association
(expression
(term
(numeric_literal))))))
(null_statement))
(procedure_call_statement
(identifier))))))))
(compilation_unit
(null_statement))
(compilation_unit
(procedure_call_statement
(identifier)))
(compilation_unit
(procedure_call_statement
(identifier))))
================================================================================
Accept
================================================================================
accept Start (A : Integer; B : Integer) do
null;
end Start;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(accept_statement
(identifier)
(formal_part
(parameter_specification
(identifier)
(identifier))
(parameter_specification
(identifier)
(identifier)))
(handled_sequence_of_statements
(null_statement))
(identifier))))
================================================================================
Select terminate
================================================================================
select
accept Start;
or
terminate;
end select;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(selective_accept
(select_alternative
(accept_alternative
(accept_statement
(identifier))))
(select_alternative
(terminate_alternative)))))
================================================================================
Task type with discriminant
================================================================================
package P is
task type T (A : Integer) is
end T;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(task_type_declaration
(identifier)
(known_discriminant_part
(discriminant_specification_list
(discriminant_specification
(identifier)
(identifier))))
(task_definition
(identifier)))))))
================================================================================
Task type with aspect
================================================================================
package P is
task type T (A : Integer) with Priority => 1 is
end T;
end;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(task_type_declaration
(identifier)
(known_discriminant_part
(discriminant_specification_list
(discriminant_specification
(identifier)
(identifier))))
(aspect_specification
(aspect_mark_list
(aspect_association
(identifier)
(expression
(term
(numeric_literal))))))
(task_definition
(identifier)))))))

@ -0,0 +1,200 @@
================================================================================
Range type
================================================================================
package P is
type A is range 1 .. 2;
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(signed_integer_type_definition
(term
(numeric_literal))
(term
(numeric_literal))))
(identifier))))
================================================================================
Derived type
================================================================================
package P is
type B is new Integer
with Size => 8;
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(derived_type_definition
(identifier))
(aspect_specification
(aspect_mark_list
(aspect_association
(identifier)
(expression
(term
(numeric_literal)))))))
(identifier))))
================================================================================
Modular types
================================================================================
package P is
type C is mod 256;
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(modular_type_definition
(expression
(term
(numeric_literal)))))
(identifier))))
================================================================================
Floats
================================================================================
package P is
type B is new Float range 0.0 .. 1.0;
type D is delta 0.1 digits 8;
type E is delta 0.1 range 0.0 .. 1.0;
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(derived_type_definition
(identifier)
(range_constraint
(range_g
(term
(numeric_literal))
(term
(numeric_literal))))))
(full_type_declaration
(identifier)
(decimal_fixed_point_definition
(expression
(term
(numeric_literal)))
(expression
(term
(numeric_literal)))))
(full_type_declaration
(identifier)
(ordinary_fixed_point_definition
(expression
(term
(numeric_literal)))
(real_range_specification
(term
(numeric_literal))
(term
(numeric_literal)))))
(identifier))))
================================================================================
Enumerations
================================================================================
package P is
type E is (VAL1, VAL2);
for E use (VAL1 => 1, VAL2 => 2);
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(full_type_declaration
(identifier)
(enumeration_type_definition
(identifier)
(identifier)))
(enumeration_representation_clause
(identifier)
(enumeration_aggregate
(named_array_aggregate
(array_component_association
(discrete_choice_list
(discrete_choice
(identifier)))
(expression
(term
(numeric_literal))))
(array_component_association
(discrete_choice_list
(discrete_choice
(identifier)))
(expression
(term
(numeric_literal)))))))
(identifier))))
================================================================================
Subtypes
================================================================================
package P is
subtype T is Integer range 1 .. 2;
subtype Arr is MyArray (1 .. 2, 3 .. 4);
end P;
--------------------------------------------------------------------------------
(compilation
(compilation_unit
(package_declaration
(identifier)
(subtype_declaration
(identifier)
(identifier)
(range_constraint
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))))
(subtype_declaration
(identifier)
(identifier)
(index_constraint
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))
(range_g
(term
(numeric_literal))
(term
(numeric_literal)))))
(identifier))))

@ -0,0 +1 @@
tree-sitter-clojure/src

Some files were not shown because too many files have changed in this diff Show More