├── .github ├── actions │ └── setup-rust-env │ │ └── action.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .vscode └── launch.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── assets ├── logo.darkmode.svg └── logo.svg ├── client └── vscode │ ├── .gitignore │ ├── README.md │ ├── extension.js │ ├── jsconfig.json │ ├── package-lock.json │ └── package.json ├── enable.lua ├── htmx.test.html ├── lsp ├── Cargo.toml └── src │ ├── handle.rs │ ├── htmx │ ├── attributes │ │ ├── hx-boost.md │ │ ├── hx-confirm.md │ │ ├── hx-delete.md │ │ ├── hx-disable.md │ │ ├── hx-disabled-elt.md │ │ ├── hx-encoding.md │ │ ├── hx-ext.md │ │ ├── hx-get.md │ │ ├── hx-headers.md │ │ ├── hx-history-elt.md │ │ ├── hx-history.md │ │ ├── hx-include.md │ │ ├── hx-indicator.md │ │ ├── hx-on.md │ │ ├── hx-params.md │ │ ├── hx-patch.md │ │ ├── hx-post.md │ │ ├── hx-preserve.md │ │ ├── hx-prompt.md │ │ ├── hx-push-url.md │ │ ├── hx-put.md │ │ ├── hx-replace-url.md │ │ ├── hx-request.md │ │ ├── hx-select-oob.md │ │ ├── hx-select.md │ │ ├── hx-swap-oob.md │ │ ├── hx-swap.md │ │ ├── hx-sync.md │ │ ├── hx-target.md │ │ ├── hx-trigger.md │ │ ├── hx-validate.md │ │ └── hx-vals.md │ ├── hx-boost │ │ ├── false.md │ │ └── true.md │ ├── hx-disabled-elt │ │ ├── closest.md │ │ └── this.md │ ├── hx-ext │ │ ├── ajax-header.md │ │ ├── alpine-morph.md │ │ ├── class-tools.md │ │ ├── client-side-templates.md │ │ ├── debug.md │ │ ├── disable-element.md │ │ ├── event-header.md │ │ ├── head-support.md │ │ ├── include-vals.md │ │ ├── json-enc.md │ │ ├── loading-states.md │ │ ├── method-override.md │ │ ├── morph.md │ │ ├── morphdom-swap.md │ │ ├── multi-swap.md │ │ ├── path-deps.md │ │ ├── preload.md │ │ ├── remove-me.md │ │ ├── response-targets.md │ │ ├── restored.md │ │ ├── sse.md │ │ └── ws.md │ ├── hx-history │ │ └── false.md │ ├── hx-params │ │ ├── none.md │ │ ├── not.md │ │ └── star.md │ ├── hx-push-url │ │ ├── false.md │ │ └── true.md │ ├── hx-replace-url │ │ ├── false.md │ │ └── true.md │ ├── hx-swap-oob │ │ └── true.md │ ├── hx-swap │ │ ├── afterbegin.md │ │ ├── afterend.md │ │ ├── beforebegin.md │ │ ├── beforeend.md │ │ ├── delete.md │ │ ├── innerHTML.md │ │ ├── none.md │ │ └── outerHTML.md │ ├── hx-sync │ │ ├── abort.md │ │ ├── drop.md │ │ ├── queue.md │ │ └── replace.md │ ├── hx-target │ │ ├── closest.md │ │ ├── find.md │ │ ├── next.md │ │ ├── prev.md │ │ └── this.md │ ├── hx-trigger │ │ ├── changed.md │ │ ├── click.md │ │ ├── consume.md │ │ ├── delay.md │ │ ├── every.md │ │ ├── from.md │ │ ├── intersect.md │ │ ├── keyup.md │ │ ├── load.md │ │ ├── once.md │ │ ├── queue.md │ │ ├── revealed.md │ │ ├── target.md │ │ └── throttle.md │ └── mod.rs │ ├── lib.rs │ ├── text_store.rs │ ├── tree_sitter.rs │ └── tree_sitter_querier.rs ├── src ├── main.rs └── opts.rs └── util ├── Cargo.toml └── src └── lib.rs /.github/actions/setup-rust-env/action.yml: -------------------------------------------------------------------------------- 1 | name: "Setup Rust Environment" 2 | 3 | description: "Setup the Rust CI environment for GitHub Action runners" 4 | 5 | runs: 6 | using: composite 7 | steps: 8 | - name: Setup Rust toolchain 9 | uses: dtolnay/rust-toolchain@master 10 | with: 11 | toolchain: stable 12 | targets: > 13 | x86_64-unknown-linux-gnu, 14 | x86_64-pc-windows-msvc, 15 | aarch64-pc-windows-msvc, 16 | x86_64-apple-darwin, 17 | aarch64-apple-darwin 18 | components: rustfmt, clippy 19 | 20 | - name: Setup rust cache 21 | uses: Swatinem/rust-cache@v2 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | pull_request: 7 | 8 | jobs: 9 | clippy: 10 | name: Clippy 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup Rust env 17 | uses: ./.github/actions/setup-rust-env 18 | 19 | - name: Rust clippy 20 | run: cargo clippy -- -Dclippy::all -D warnings 21 | 22 | rustfmt: 23 | name: Format 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout code 27 | uses: actions/checkout@v3 28 | 29 | - name: Setup Rust env 30 | uses: ./.github/actions/setup-rust-env 31 | 32 | - name: Rust fmt 33 | run: cargo fmt --check 34 | 35 | test: 36 | name: Test 37 | runs-on: ubuntu-latest 38 | steps: 39 | - name: Checkout code 40 | uses: actions/checkout@v3 41 | 42 | - name: Setup Rust env 43 | uses: "./.github/actions/setup-rust-env" 44 | 45 | - name: Rust test 46 | run: cargo test --workspace 47 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build & Release 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ${{ matrix.build.os }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | build: 18 | - { 19 | NAME: linux-x64, 20 | OS: ubuntu-22.04, 21 | TARGET: x86_64-unknown-linux-gnu, 22 | } 23 | - { 24 | NAME: windows-x64, 25 | OS: windows-2022, 26 | TARGET: x86_64-pc-windows-msvc, 27 | } 28 | - { 29 | NAME: windows-arm64, 30 | OS: windows-2022, 31 | TARGET: aarch64-pc-windows-msvc, 32 | } 33 | - { 34 | NAME: darwin-x64, 35 | OS: macos-12, 36 | TARGET: x86_64-apple-darwin, 37 | } 38 | - { 39 | NAME: darwin-arm64, 40 | OS: macos-12, 41 | TARGET: aarch64-apple-darwin, 42 | } 43 | steps: 44 | - name: Checkout code 45 | uses: actions/checkout@v3 46 | 47 | - name: Setup Rust env 48 | uses: "./.github/actions/setup-rust-env" 49 | 50 | - name: Build 51 | run: cargo build --release --locked --target ${{ matrix.build.TARGET }} 52 | 53 | - name: Rename htmx-lsp binary 54 | shell: bash 55 | run: | 56 | binary_name="htmx-lsp" 57 | 58 | extension="" 59 | # windows binaries have ".exe" extension 60 | if [[ "${{ matrix.build.OS }}" == *"windows"* ]]; then 61 | extension=".exe" 62 | fi 63 | 64 | mkdir bin 65 | cp "target/${{ matrix.build.TARGET }}/release/${binary_name}" "bin/${binary_name}-${{ matrix.build.NAME }}${extension}" 66 | 67 | - name: Upload binary 68 | uses: actions/upload-artifact@v3 69 | with: 70 | name: built-binaries 71 | path: bin/* 72 | 73 | release: 74 | name: Release 75 | runs-on: ubuntu-22.04 76 | needs: build 77 | if: github.ref == 'refs/heads/master' 78 | steps: 79 | - name: Checkout code 80 | uses: actions/checkout@v3 81 | with: 82 | fetch-depth: 2 83 | 84 | - name: Check if release should be created 85 | shell: bash 86 | run: | 87 | set -o pipefail 88 | 89 | RELEASE_VERSION=$(awk -F ' = ' '$1 ~ /version/ { gsub(/["]/, "", $2); printf("%s",$2) }' Cargo.toml) 90 | OLD_VERSION=$( curl -s --fail-with-body https://api.github.com/repos/ThePrimeagen/htmx-lsp/releases/latest | jq -r '.tag_name' ) 91 | 92 | echo "RELEASE_VERSION=$RELEASE_VERSION" >> $GITHUB_ENV 93 | echo "$OLD_VERSION -> $RELEASE_VERSION" 94 | 95 | if [[ "$RELEASE_VERSION" == "$OLD_VERSION" ]] || ! [[ "$OLD_VERSION" =~ ^[0-9]\.[0-9]\.[0-9]$ ]]; then 96 | echo "SHOULD_RELEASE=no" >> $GITHUB_ENV 97 | else 98 | git tag "$RELEASE_VERSION" 99 | git push -u origin "$RELEASE_VERSION" 100 | echo "SHOULD_RELEASE=yes" >> $GITHUB_ENV 101 | fi 102 | 103 | - name: Download binaries 104 | uses: actions/download-artifact@v3 105 | if: env.SHOULD_RELEASE == 'yes' 106 | with: 107 | name: built-binaries 108 | path: bin 109 | 110 | - name: Publish release 111 | uses: softprops/action-gh-release@v1 112 | if: env.SHOULD_RELEASE == 'yes' 113 | with: 114 | files: bin/* 115 | tag_name: ${{ env.RELEASE_VERSION }} 116 | fail_on_unmatched_files: true 117 | generate_release_notes: true 118 | env: 119 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 120 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "extensionHost", 6 | "request": "launch", 7 | "name": "Debug LSP Extension", 8 | "runtimeExecutable": "${execPath}", 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceRoot}/client/vscode", 11 | "--disable-extensions", 12 | "${workspaceRoot}/htmx.test.html" 13 | ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.20.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.0.4" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anstream" 31 | version = "0.3.2" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" 34 | dependencies = [ 35 | "anstyle", 36 | "anstyle-parse", 37 | "anstyle-query", 38 | "anstyle-wincon", 39 | "colorchoice", 40 | "is-terminal", 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle" 46 | version = "1.0.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" 49 | 50 | [[package]] 51 | name = "anstyle-parse" 52 | version = "0.2.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" 55 | dependencies = [ 56 | "utf8parse", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-query" 61 | version = "1.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 64 | dependencies = [ 65 | "windows-sys", 66 | ] 67 | 68 | [[package]] 69 | name = "anstyle-wincon" 70 | version = "1.0.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" 73 | dependencies = [ 74 | "anstyle", 75 | "windows-sys", 76 | ] 77 | 78 | [[package]] 79 | name = "anyhow" 80 | version = "1.0.72" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" 83 | 84 | [[package]] 85 | name = "autocfg" 86 | version = "1.1.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 89 | 90 | [[package]] 91 | name = "backtrace" 92 | version = "0.3.68" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" 95 | dependencies = [ 96 | "addr2line", 97 | "cc", 98 | "cfg-if", 99 | "libc", 100 | "miniz_oxide", 101 | "object", 102 | "rustc-demangle", 103 | ] 104 | 105 | [[package]] 106 | name = "bitflags" 107 | version = "1.3.2" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 110 | 111 | [[package]] 112 | name = "bitflags" 113 | version = "2.3.3" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" 116 | 117 | [[package]] 118 | name = "bytes" 119 | version = "1.4.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 122 | 123 | [[package]] 124 | name = "cc" 125 | version = "1.0.79" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 128 | 129 | [[package]] 130 | name = "cfg-if" 131 | version = "1.0.0" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 134 | 135 | [[package]] 136 | name = "clap" 137 | version = "4.3.19" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" 140 | dependencies = [ 141 | "clap_builder", 142 | "clap_derive", 143 | "once_cell", 144 | ] 145 | 146 | [[package]] 147 | name = "clap_builder" 148 | version = "4.3.19" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" 151 | dependencies = [ 152 | "anstream", 153 | "anstyle", 154 | "clap_lex", 155 | "strsim", 156 | ] 157 | 158 | [[package]] 159 | name = "clap_derive" 160 | version = "4.3.12" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" 163 | dependencies = [ 164 | "heck", 165 | "proc-macro2", 166 | "quote", 167 | "syn", 168 | ] 169 | 170 | [[package]] 171 | name = "clap_lex" 172 | version = "0.5.0" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" 175 | 176 | [[package]] 177 | name = "colorchoice" 178 | version = "1.0.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 181 | 182 | [[package]] 183 | name = "crossbeam-channel" 184 | version = "0.5.8" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" 187 | dependencies = [ 188 | "cfg-if", 189 | "crossbeam-utils", 190 | ] 191 | 192 | [[package]] 193 | name = "crossbeam-utils" 194 | version = "0.8.16" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" 197 | dependencies = [ 198 | "cfg-if", 199 | ] 200 | 201 | [[package]] 202 | name = "erased-serde" 203 | version = "0.3.28" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "da96524cc884f6558f1769b6c46686af2fe8e8b4cd253bd5a3cdba8181b8e070" 206 | dependencies = [ 207 | "serde", 208 | ] 209 | 210 | [[package]] 211 | name = "errno" 212 | version = "0.3.1" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 215 | dependencies = [ 216 | "errno-dragonfly", 217 | "libc", 218 | "windows-sys", 219 | ] 220 | 221 | [[package]] 222 | name = "errno-dragonfly" 223 | version = "0.1.2" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 226 | dependencies = [ 227 | "cc", 228 | "libc", 229 | ] 230 | 231 | [[package]] 232 | name = "form_urlencoded" 233 | version = "1.2.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" 236 | dependencies = [ 237 | "percent-encoding", 238 | ] 239 | 240 | [[package]] 241 | name = "gimli" 242 | version = "0.27.3" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" 245 | 246 | [[package]] 247 | name = "heck" 248 | version = "0.4.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 251 | 252 | [[package]] 253 | name = "hermit-abi" 254 | version = "0.3.2" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" 257 | 258 | [[package]] 259 | name = "htmx-lsp" 260 | version = "0.1.0" 261 | dependencies = [ 262 | "anyhow", 263 | "clap", 264 | "htmx-lsp-server", 265 | "htmx-lsp-util", 266 | "log", 267 | "structured-logger", 268 | "tree-sitter", 269 | "tree-sitter-html", 270 | "walkdir", 271 | ] 272 | 273 | [[package]] 274 | name = "htmx-lsp-server" 275 | version = "0.1.0" 276 | dependencies = [ 277 | "anyhow", 278 | "htmx-lsp-util", 279 | "log", 280 | "lsp-server", 281 | "lsp-types", 282 | "maplit", 283 | "phf", 284 | "serde", 285 | "serde_json", 286 | "tree-sitter", 287 | "tree-sitter-html", 288 | "walkdir", 289 | ] 290 | 291 | [[package]] 292 | name = "htmx-lsp-util" 293 | version = "0.1.0" 294 | dependencies = [ 295 | "log", 296 | "lsp-types", 297 | "structured-logger", 298 | ] 299 | 300 | [[package]] 301 | name = "idna" 302 | version = "0.4.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" 305 | dependencies = [ 306 | "unicode-bidi", 307 | "unicode-normalization", 308 | ] 309 | 310 | [[package]] 311 | name = "is-terminal" 312 | version = "0.4.9" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" 315 | dependencies = [ 316 | "hermit-abi", 317 | "rustix", 318 | "windows-sys", 319 | ] 320 | 321 | [[package]] 322 | name = "itoa" 323 | version = "1.0.9" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 326 | 327 | [[package]] 328 | name = "libc" 329 | version = "0.2.147" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" 332 | 333 | [[package]] 334 | name = "linux-raw-sys" 335 | version = "0.4.3" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" 338 | 339 | [[package]] 340 | name = "lock_api" 341 | version = "0.4.10" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" 344 | dependencies = [ 345 | "autocfg", 346 | "scopeguard", 347 | ] 348 | 349 | [[package]] 350 | name = "log" 351 | version = "0.4.19" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" 354 | dependencies = [ 355 | "serde", 356 | "value-bag", 357 | ] 358 | 359 | [[package]] 360 | name = "lsp-server" 361 | version = "0.7.2" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "37ea9ae5a5082ca3b6ae824fc7666cd206b99168a4d4c769ad8fe9cc740df6a6" 364 | dependencies = [ 365 | "crossbeam-channel", 366 | "log", 367 | "serde", 368 | "serde_json", 369 | ] 370 | 371 | [[package]] 372 | name = "lsp-types" 373 | version = "0.94.0" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237" 376 | dependencies = [ 377 | "bitflags 1.3.2", 378 | "serde", 379 | "serde_json", 380 | "serde_repr", 381 | "url", 382 | ] 383 | 384 | [[package]] 385 | name = "maplit" 386 | version = "1.0.2" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" 389 | 390 | [[package]] 391 | name = "memchr" 392 | version = "2.5.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 395 | 396 | [[package]] 397 | name = "miniz_oxide" 398 | version = "0.7.1" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 401 | dependencies = [ 402 | "adler", 403 | ] 404 | 405 | [[package]] 406 | name = "object" 407 | version = "0.31.1" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" 410 | dependencies = [ 411 | "memchr", 412 | ] 413 | 414 | [[package]] 415 | name = "once_cell" 416 | version = "1.18.0" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 419 | 420 | [[package]] 421 | name = "parking_lot" 422 | version = "0.12.1" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 425 | dependencies = [ 426 | "lock_api", 427 | "parking_lot_core", 428 | ] 429 | 430 | [[package]] 431 | name = "parking_lot_core" 432 | version = "0.9.8" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" 435 | dependencies = [ 436 | "cfg-if", 437 | "libc", 438 | "redox_syscall", 439 | "smallvec", 440 | "windows-targets", 441 | ] 442 | 443 | [[package]] 444 | name = "percent-encoding" 445 | version = "2.3.0" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" 448 | 449 | [[package]] 450 | name = "phf" 451 | version = "0.11.2" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" 454 | dependencies = [ 455 | "phf_macros", 456 | "phf_shared", 457 | ] 458 | 459 | [[package]] 460 | name = "phf_generator" 461 | version = "0.11.2" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" 464 | dependencies = [ 465 | "phf_shared", 466 | "rand", 467 | ] 468 | 469 | [[package]] 470 | name = "phf_macros" 471 | version = "0.11.2" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" 474 | dependencies = [ 475 | "phf_generator", 476 | "phf_shared", 477 | "proc-macro2", 478 | "quote", 479 | "syn", 480 | ] 481 | 482 | [[package]] 483 | name = "phf_shared" 484 | version = "0.11.2" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" 487 | dependencies = [ 488 | "siphasher", 489 | ] 490 | 491 | [[package]] 492 | name = "pin-project-lite" 493 | version = "0.2.10" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" 496 | 497 | [[package]] 498 | name = "proc-macro2" 499 | version = "1.0.66" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 502 | dependencies = [ 503 | "unicode-ident", 504 | ] 505 | 506 | [[package]] 507 | name = "quote" 508 | version = "1.0.32" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" 511 | dependencies = [ 512 | "proc-macro2", 513 | ] 514 | 515 | [[package]] 516 | name = "rand" 517 | version = "0.8.5" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 520 | dependencies = [ 521 | "rand_core", 522 | ] 523 | 524 | [[package]] 525 | name = "rand_core" 526 | version = "0.6.4" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 529 | 530 | [[package]] 531 | name = "redox_syscall" 532 | version = "0.3.5" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" 535 | dependencies = [ 536 | "bitflags 1.3.2", 537 | ] 538 | 539 | [[package]] 540 | name = "regex" 541 | version = "1.9.3" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" 544 | dependencies = [ 545 | "aho-corasick", 546 | "memchr", 547 | "regex-automata", 548 | "regex-syntax", 549 | ] 550 | 551 | [[package]] 552 | name = "regex-automata" 553 | version = "0.3.6" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" 556 | dependencies = [ 557 | "aho-corasick", 558 | "memchr", 559 | "regex-syntax", 560 | ] 561 | 562 | [[package]] 563 | name = "regex-syntax" 564 | version = "0.7.4" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" 567 | 568 | [[package]] 569 | name = "rustc-demangle" 570 | version = "0.1.23" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 573 | 574 | [[package]] 575 | name = "rustix" 576 | version = "0.38.4" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" 579 | dependencies = [ 580 | "bitflags 2.3.3", 581 | "errno", 582 | "libc", 583 | "linux-raw-sys", 584 | "windows-sys", 585 | ] 586 | 587 | [[package]] 588 | name = "ryu" 589 | version = "1.0.15" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 592 | 593 | [[package]] 594 | name = "same-file" 595 | version = "1.0.6" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 598 | dependencies = [ 599 | "winapi-util", 600 | ] 601 | 602 | [[package]] 603 | name = "scopeguard" 604 | version = "1.2.0" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 607 | 608 | [[package]] 609 | name = "serde" 610 | version = "1.0.175" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" 613 | dependencies = [ 614 | "serde_derive", 615 | ] 616 | 617 | [[package]] 618 | name = "serde_derive" 619 | version = "1.0.175" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" 622 | dependencies = [ 623 | "proc-macro2", 624 | "quote", 625 | "syn", 626 | ] 627 | 628 | [[package]] 629 | name = "serde_fmt" 630 | version = "1.0.3" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" 633 | dependencies = [ 634 | "serde", 635 | ] 636 | 637 | [[package]] 638 | name = "serde_json" 639 | version = "1.0.104" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" 642 | dependencies = [ 643 | "itoa", 644 | "ryu", 645 | "serde", 646 | ] 647 | 648 | [[package]] 649 | name = "serde_repr" 650 | version = "0.1.15" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "e168eaaf71e8f9bd6037feb05190485708e019f4fd87d161b3c0a0d37daf85e5" 653 | dependencies = [ 654 | "proc-macro2", 655 | "quote", 656 | "syn", 657 | ] 658 | 659 | [[package]] 660 | name = "siphasher" 661 | version = "0.3.11" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" 664 | 665 | [[package]] 666 | name = "smallvec" 667 | version = "1.11.0" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" 670 | 671 | [[package]] 672 | name = "strsim" 673 | version = "0.10.0" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 676 | 677 | [[package]] 678 | name = "structured-logger" 679 | version = "1.0.2" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "34451933278cbfed67c91f385afdcdf5ea05d635bc033fc4908c69fe87a840b9" 682 | dependencies = [ 683 | "log", 684 | "parking_lot", 685 | "serde", 686 | "serde_json", 687 | "tokio", 688 | ] 689 | 690 | [[package]] 691 | name = "sval" 692 | version = "2.6.1" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "8b031320a434d3e9477ccf9b5756d57d4272937b8d22cb88af80b7633a1b78b1" 695 | 696 | [[package]] 697 | name = "sval_buffer" 698 | version = "2.6.1" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "6bf7e9412af26b342f3f2cc5cc4122b0105e9d16eb76046cd14ed10106cf6028" 701 | dependencies = [ 702 | "sval", 703 | "sval_ref", 704 | ] 705 | 706 | [[package]] 707 | name = "sval_dynamic" 708 | version = "2.6.1" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "a0ef628e8a77a46ed3338db8d1b08af77495123cc229453084e47cd716d403cf" 711 | dependencies = [ 712 | "sval", 713 | ] 714 | 715 | [[package]] 716 | name = "sval_fmt" 717 | version = "2.6.1" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "7dc09e9364c2045ab5fa38f7b04d077b3359d30c4c2b3ec4bae67a358bd64326" 720 | dependencies = [ 721 | "itoa", 722 | "ryu", 723 | "sval", 724 | ] 725 | 726 | [[package]] 727 | name = "sval_json" 728 | version = "2.6.1" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "ada6f627e38cbb8860283649509d87bc4a5771141daa41c78fd31f2b9485888d" 731 | dependencies = [ 732 | "itoa", 733 | "ryu", 734 | "sval", 735 | ] 736 | 737 | [[package]] 738 | name = "sval_ref" 739 | version = "2.6.1" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "703ca1942a984bd0d9b5a4c0a65ab8b4b794038d080af4eb303c71bc6bf22d7c" 742 | dependencies = [ 743 | "sval", 744 | ] 745 | 746 | [[package]] 747 | name = "sval_serde" 748 | version = "2.6.1" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "830926cd0581f7c3e5d51efae4d35c6b6fc4db583842652891ba2f1bed8db046" 751 | dependencies = [ 752 | "serde", 753 | "sval", 754 | "sval_buffer", 755 | "sval_fmt", 756 | ] 757 | 758 | [[package]] 759 | name = "syn" 760 | version = "2.0.27" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" 763 | dependencies = [ 764 | "proc-macro2", 765 | "quote", 766 | "unicode-ident", 767 | ] 768 | 769 | [[package]] 770 | name = "tinyvec" 771 | version = "1.6.0" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 774 | dependencies = [ 775 | "tinyvec_macros", 776 | ] 777 | 778 | [[package]] 779 | name = "tinyvec_macros" 780 | version = "0.1.1" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 783 | 784 | [[package]] 785 | name = "tokio" 786 | version = "1.29.1" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" 789 | dependencies = [ 790 | "autocfg", 791 | "backtrace", 792 | "bytes", 793 | "parking_lot", 794 | "pin-project-lite", 795 | ] 796 | 797 | [[package]] 798 | name = "tree-sitter" 799 | version = "0.20.10" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "e747b1f9b7b931ed39a548c1fae149101497de3c1fc8d9e18c62c1a66c683d3d" 802 | dependencies = [ 803 | "cc", 804 | "regex", 805 | ] 806 | 807 | [[package]] 808 | name = "tree-sitter-html" 809 | version = "0.19.0" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "184e6b77953a354303dc87bf5fe36558c83569ce92606e7b382a0dc1b7443443" 812 | dependencies = [ 813 | "cc", 814 | "tree-sitter", 815 | ] 816 | 817 | [[package]] 818 | name = "unicode-bidi" 819 | version = "0.3.13" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" 822 | 823 | [[package]] 824 | name = "unicode-ident" 825 | version = "1.0.11" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 828 | 829 | [[package]] 830 | name = "unicode-normalization" 831 | version = "0.1.22" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 834 | dependencies = [ 835 | "tinyvec", 836 | ] 837 | 838 | [[package]] 839 | name = "url" 840 | version = "2.4.0" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" 843 | dependencies = [ 844 | "form_urlencoded", 845 | "idna", 846 | "percent-encoding", 847 | "serde", 848 | ] 849 | 850 | [[package]] 851 | name = "utf8parse" 852 | version = "0.2.1" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 855 | 856 | [[package]] 857 | name = "value-bag" 858 | version = "1.4.1" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" 861 | dependencies = [ 862 | "value-bag-serde1", 863 | "value-bag-sval2", 864 | ] 865 | 866 | [[package]] 867 | name = "value-bag-serde1" 868 | version = "1.4.1" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "b0b9f3feef403a50d4d67e9741a6d8fc688bcbb4e4f31bd4aab72cc690284394" 871 | dependencies = [ 872 | "erased-serde", 873 | "serde", 874 | "serde_fmt", 875 | ] 876 | 877 | [[package]] 878 | name = "value-bag-sval2" 879 | version = "1.4.1" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "30b24f4146b6f3361e91cbf527d1fb35e9376c3c0cef72ca5ec5af6d640fad7d" 882 | dependencies = [ 883 | "sval", 884 | "sval_buffer", 885 | "sval_dynamic", 886 | "sval_fmt", 887 | "sval_json", 888 | "sval_ref", 889 | "sval_serde", 890 | ] 891 | 892 | [[package]] 893 | name = "walkdir" 894 | version = "2.3.3" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" 897 | dependencies = [ 898 | "same-file", 899 | "winapi-util", 900 | ] 901 | 902 | [[package]] 903 | name = "winapi" 904 | version = "0.3.9" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 907 | dependencies = [ 908 | "winapi-i686-pc-windows-gnu", 909 | "winapi-x86_64-pc-windows-gnu", 910 | ] 911 | 912 | [[package]] 913 | name = "winapi-i686-pc-windows-gnu" 914 | version = "0.4.0" 915 | source = "registry+https://github.com/rust-lang/crates.io-index" 916 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 917 | 918 | [[package]] 919 | name = "winapi-util" 920 | version = "0.1.5" 921 | source = "registry+https://github.com/rust-lang/crates.io-index" 922 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 923 | dependencies = [ 924 | "winapi", 925 | ] 926 | 927 | [[package]] 928 | name = "winapi-x86_64-pc-windows-gnu" 929 | version = "0.4.0" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 932 | 933 | [[package]] 934 | name = "windows-sys" 935 | version = "0.48.0" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 938 | dependencies = [ 939 | "windows-targets", 940 | ] 941 | 942 | [[package]] 943 | name = "windows-targets" 944 | version = "0.48.1" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" 947 | dependencies = [ 948 | "windows_aarch64_gnullvm", 949 | "windows_aarch64_msvc", 950 | "windows_i686_gnu", 951 | "windows_i686_msvc", 952 | "windows_x86_64_gnu", 953 | "windows_x86_64_gnullvm", 954 | "windows_x86_64_msvc", 955 | ] 956 | 957 | [[package]] 958 | name = "windows_aarch64_gnullvm" 959 | version = "0.48.0" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 962 | 963 | [[package]] 964 | name = "windows_aarch64_msvc" 965 | version = "0.48.0" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 968 | 969 | [[package]] 970 | name = "windows_i686_gnu" 971 | version = "0.48.0" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 974 | 975 | [[package]] 976 | name = "windows_i686_msvc" 977 | version = "0.48.0" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 980 | 981 | [[package]] 982 | name = "windows_x86_64_gnu" 983 | version = "0.48.0" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 986 | 987 | [[package]] 988 | name = "windows_x86_64_gnullvm" 989 | version = "0.48.0" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 992 | 993 | [[package]] 994 | name = "windows_x86_64_msvc" 995 | version = "0.48.0" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 998 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "htmx-lsp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "A language server for htmx" 6 | license = "MIT" 7 | homepage = "https://github.com/ThePrimeagen/htmx-lsp" 8 | 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | anyhow.workspace = true 14 | clap.workspace = true 15 | log.workspace = true 16 | structured-logger.workspace = true 17 | walkdir.workspace = true 18 | tree-sitter.workspace = true 19 | tree-sitter-html.workspace = true 20 | htmx-lsp-server = { version = "0.1.0", path = "./lsp" } 21 | htmx-lsp-util = { version = "0.1.0", path = "./util" } 22 | 23 | # [build-dependencies] 24 | # cc="*" 25 | 26 | [workspace.dependencies] 27 | tree-sitter = "0.20.10" 28 | tree-sitter-html = "0.19.0" 29 | walkdir = "2.3.3" 30 | anyhow = "1.0.72" 31 | clap = { version = "4.3.17", features = ["derive", "env"] } 32 | log = { version = "0.4.19", features = ["kv_unstable", "kv_unstable_serde"] } 33 | lsp-server = "0.7.2" 34 | lsp-types = "0.94.0" 35 | serde = { version = "1.0.173", features = ["derive"] } 36 | serde_json = "1.0.103" 37 | structured-logger = "1.0.1" 38 | 39 | [workspace] 40 | members = [ 41 | "lsp", 42 | "util", 43 | ] 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 ThePrimeagen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | HTMX-LSP logo 3 | HTMX-LSP logo 4 |
5 | crates.io 6 | build status 7 |
8 | 9 |

10 | its so over 11 |

12 | 13 | ## LSP 14 | 15 | Right now this is very much so a work in progress and currently provides basic autocomplete for _most_ HTMX attributes. We have reached a point where I could use help! If you want to fill in documentation or help with autocompletes please open an issue/pr! 16 | 17 | ## Integration 18 | 19 | ### Neovim 20 | 21 | `htmx-lsp` can be installed via Mason. And can be configured with `lspconfig` 22 | 23 | ```lua 24 | local lspconfig = require('lspconfig') 25 | -- ... 26 | lspconfig.htmx.setup{} 27 | ``` 28 | 29 | Another option is to use [lsp-debug-tools](https://github.com/ThePrimeagen/lsp-debug-tools.nvim) 30 | 31 | ### VSCode 32 | 33 | No published extension yet, but there is a development extension in the [`clients/vscode`](client/vscode/README.md) folder (with setup instructions) 34 | 35 | ## Development 36 | 37 | ### General 38 | 39 | As of right now the general goal is just to provide completion for any `-` 40 | character received without even looking at the context. 41 | 42 | After that, would be to perform some code actions that make sense and allow for 43 | amazing utility around htmx. 44 | 45 | ```console 46 | htmx-lsp -f /path/to/file --level [OFF | TRACE | DEBUG | INFO | WARN | ERROR] 47 | ``` 48 | 49 | ### Build 50 | 51 | ```console 52 | cargo build 53 | 54 | # OR auto-build on file save, requires `cargo-watch` 55 | cargo install cargo-watch 56 | cargo watch -x build 57 | ``` 58 | 59 | ## Contributors 60 | 61 |
62 | 63 | 64 | 65 |
66 | -------------------------------------------------------------------------------- /assets/logo.darkmode.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /client/vscode/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /client/vscode/README.md: -------------------------------------------------------------------------------- 1 | # VSC\*de HTMX LSP 2 | 3 | ## Usage 4 | 5 | Future `todo!()` 6 | 7 | ## Development 8 | 9 | ### Setup your environment 10 | 11 | ```console 12 | # Build & link htmx-lsp 13 | cargo build 14 | ln -s "$(pwd)/target/debug/htmx-lsp" "/usr/local/bin/htmx-lsp" # Or another location in your $PATH 15 | 16 | # Setup JS 17 | cd client/vscode 18 | npm install 19 | ``` 20 | 21 | ### Debugging 22 | 23 | In VSC\*ode, go to the `Run & Debug` sidebar (Ctrl + Shft + D) and click the `Debug LSP Extension` button. This will open a new VSC\*de instance with the lsp client installed. 24 | 25 | To get the lsp server logs, run: 26 | 27 | ```console 28 | tail -f $(echo "console.log(require('os').tmpdir())" | node)/lsp.log 29 | ``` 30 | -------------------------------------------------------------------------------- /client/vscode/extension.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const { LanguageClient } = require("vscode-languageclient/node"); 3 | const tmpdir = require("os").tmpdir(); 4 | 5 | module.exports = { 6 | /** @param {import("vscode").ExtensionContext} context*/ 7 | activate(context) { 8 | /** @type {import("vscode-languageclient/node").ServerOptions} */ 9 | const serverOptions = { 10 | run: { 11 | command: "htmx-lsp", 12 | }, 13 | debug: { 14 | command: "htmx-lsp", 15 | args: ["--file", `${tmpdir}/lsp.log`, "--level", "TRACE"], 16 | }, 17 | }; 18 | 19 | /** @type {import("vscode-languageclient/node").LanguageClientOptions} */ 20 | const clientOptions = { 21 | documentSelector: [{ scheme: "file", language: "html" }], 22 | }; 23 | 24 | const client = new LanguageClient( 25 | "htmx-lsp", 26 | "Htmx Language Server", 27 | serverOptions, 28 | clientOptions 29 | ); 30 | 31 | client.start(); 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /client/vscode/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "checkJs": true 4 | }, 5 | "exclude": ["node_modules"] 6 | } 7 | -------------------------------------------------------------------------------- /client/vscode/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "htmx-lsp", 3 | "version": "0.0.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "htmx-lsp", 9 | "version": "0.0.1", 10 | "dependencies": { 11 | "vscode-languageclient": "^8.1.0" 12 | }, 13 | "engines": { 14 | "vscode": "^1.74.0" 15 | } 16 | }, 17 | "node_modules/balanced-match": { 18 | "version": "1.0.2", 19 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 20 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 21 | }, 22 | "node_modules/brace-expansion": { 23 | "version": "2.0.1", 24 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 25 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 26 | "dependencies": { 27 | "balanced-match": "^1.0.0" 28 | } 29 | }, 30 | "node_modules/lru-cache": { 31 | "version": "6.0.0", 32 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 33 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 34 | "dependencies": { 35 | "yallist": "^4.0.0" 36 | }, 37 | "engines": { 38 | "node": ">=10" 39 | } 40 | }, 41 | "node_modules/minimatch": { 42 | "version": "5.1.6", 43 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", 44 | "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", 45 | "dependencies": { 46 | "brace-expansion": "^2.0.1" 47 | }, 48 | "engines": { 49 | "node": ">=10" 50 | } 51 | }, 52 | "node_modules/semver": { 53 | "version": "7.5.4", 54 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", 55 | "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", 56 | "dependencies": { 57 | "lru-cache": "^6.0.0" 58 | }, 59 | "bin": { 60 | "semver": "bin/semver.js" 61 | }, 62 | "engines": { 63 | "node": ">=10" 64 | } 65 | }, 66 | "node_modules/vscode-jsonrpc": { 67 | "version": "8.1.0", 68 | "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz", 69 | "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==", 70 | "engines": { 71 | "node": ">=14.0.0" 72 | } 73 | }, 74 | "node_modules/vscode-languageclient": { 75 | "version": "8.1.0", 76 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0.tgz", 77 | "integrity": "sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==", 78 | "dependencies": { 79 | "minimatch": "^5.1.0", 80 | "semver": "^7.3.7", 81 | "vscode-languageserver-protocol": "3.17.3" 82 | }, 83 | "engines": { 84 | "vscode": "^1.67.0" 85 | } 86 | }, 87 | "node_modules/vscode-languageserver-protocol": { 88 | "version": "3.17.3", 89 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz", 90 | "integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==", 91 | "dependencies": { 92 | "vscode-jsonrpc": "8.1.0", 93 | "vscode-languageserver-types": "3.17.3" 94 | } 95 | }, 96 | "node_modules/vscode-languageserver-types": { 97 | "version": "3.17.3", 98 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", 99 | "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" 100 | }, 101 | "node_modules/yallist": { 102 | "version": "4.0.0", 103 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 104 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /client/vscode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "htmx-lsp", 3 | "description": "Language Server for HTMX", 4 | "version": "0.0.1", 5 | "contributors": [ 6 | "ThePrimeagen", 7 | "MNThomson" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/ThePrimeagen/htmx-lsp" 12 | }, 13 | "engines": { 14 | "vscode": "^1.74.0" 15 | }, 16 | "activationEvents": [ 17 | "onLanguage:html" 18 | ], 19 | "main": "./extension", 20 | "dependencies": { 21 | "vscode-languageclient": "^8.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /enable.lua: -------------------------------------------------------------------------------- 1 | function restart_htmx_lsp() 2 | require("lsp-debug-tools").restart({ expected = {}, name = "htmx-lsp", cmd = { "htmx-lsp", "--level", "DEBUG" }, root_dir = vim.loop.cwd(), }); 3 | end 4 | -------------------------------------------------------------------------------- /htmx.test.html: -------------------------------------------------------------------------------- 1 |
3 | 4 | -------------------------------------------------------------------------------- /lsp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "htmx-lsp-server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "lsp server for htmx-lsp" 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | anyhow.workspace = true 12 | log.workspace = true 13 | lsp-server.workspace = true 14 | lsp-types.workspace = true 15 | serde.workspace = true 16 | serde_json.workspace = true 17 | walkdir.workspace = true 18 | htmx-lsp-util = { version = "0.1", path = "../util" } 19 | tree-sitter.workspace = true 20 | tree-sitter-html.workspace = true 21 | maplit = "1.0.2" 22 | phf = { version = "0.11.2", features = ["macros"] } 23 | -------------------------------------------------------------------------------- /lsp/src/handle.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | htmx::{hx_completion, hx_hover, HxCompletion}, 3 | text_store::TEXT_STORE, 4 | }; 5 | use log::{debug, error, warn}; 6 | use lsp_server::{Message, Notification, Request, RequestId}; 7 | use lsp_types::{CompletionContext, CompletionParams, CompletionTriggerKind, HoverParams}; 8 | 9 | #[derive(serde::Deserialize, Debug)] 10 | struct Text { 11 | text: String, 12 | } 13 | 14 | #[derive(serde::Deserialize, Debug)] 15 | struct TextDocumentLocation { 16 | uri: String, 17 | } 18 | 19 | #[derive(serde::Deserialize, Debug)] 20 | struct TextDocumentChanges { 21 | #[serde(rename = "textDocument")] 22 | text_document: TextDocumentLocation, 23 | 24 | #[serde(rename = "contentChanges")] 25 | content_changes: Vec, 26 | } 27 | 28 | #[derive(serde::Deserialize, Debug)] 29 | struct TextDocumentOpened { 30 | uri: String, 31 | 32 | text: String, 33 | } 34 | 35 | #[derive(serde::Deserialize, Debug)] 36 | struct TextDocumentOpen { 37 | #[serde(rename = "textDocument")] 38 | text_document: TextDocumentOpened, 39 | } 40 | 41 | #[derive(Debug)] 42 | pub struct HtmxAttributeCompletion { 43 | pub items: Vec, 44 | pub id: RequestId, 45 | } 46 | 47 | #[derive(Debug)] 48 | pub struct HtmxAttributeHoverResult { 49 | pub id: RequestId, 50 | pub value: String, 51 | } 52 | 53 | #[derive(Debug)] 54 | pub enum HtmxResult { 55 | // Diagnostic, 56 | AttributeCompletion(HtmxAttributeCompletion), 57 | 58 | AttributeHover(HtmxAttributeHoverResult), 59 | } 60 | 61 | // ignore snakeCase 62 | #[allow(non_snake_case)] 63 | fn handle_didChange(noti: Notification) -> Option { 64 | let text_document_changes: TextDocumentChanges = serde_json::from_value(noti.params).ok()?; 65 | let uri = text_document_changes.text_document.uri; 66 | let text = text_document_changes.content_changes[0].text.to_string(); 67 | 68 | if text_document_changes.content_changes.len() > 1 { 69 | error!("more than one content change, please be wary"); 70 | } 71 | 72 | TEXT_STORE 73 | .get() 74 | .expect("text store not initialized") 75 | .lock() 76 | .expect("text store mutex poisoned") 77 | .insert(uri, text); 78 | 79 | None 80 | } 81 | 82 | #[allow(non_snake_case)] 83 | fn handle_didOpen(noti: Notification) -> Option { 84 | debug!("handle_didOpen params {:?}", noti.params); 85 | let text_document_changes = match serde_json::from_value::(noti.params) { 86 | Ok(p) => p.text_document, 87 | Err(err) => { 88 | error!("handle_didOpen parsing params error : {:?}", err); 89 | return None; 90 | } 91 | }; 92 | 93 | TEXT_STORE 94 | .get() 95 | .expect("text store not initialized") 96 | .lock() 97 | .expect("text store mutex poisoned") 98 | .insert(text_document_changes.uri, text_document_changes.text); 99 | 100 | None 101 | } 102 | 103 | #[allow(non_snake_case)] 104 | fn handle_completion(req: Request) -> Option { 105 | let completion: CompletionParams = serde_json::from_value(req.params).ok()?; 106 | 107 | error!("handle_completion: {:?}", completion); 108 | 109 | match completion.context { 110 | Some(CompletionContext { 111 | trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER, 112 | .. 113 | }) 114 | | Some(CompletionContext { 115 | trigger_kind: CompletionTriggerKind::INVOKED, 116 | .. 117 | }) => { 118 | let items = match hx_completion(completion.text_document_position) { 119 | Some(items) => items, 120 | None => { 121 | error!("EMPTY RESULTS OF COMPLETION"); 122 | return None; 123 | } 124 | }; 125 | 126 | error!( 127 | "handled result: {:?}: completion result: {:?}", 128 | completion.context, items 129 | ); 130 | 131 | Some(HtmxResult::AttributeCompletion(HtmxAttributeCompletion { 132 | items: items.to_vec(), 133 | id: req.id, 134 | })) 135 | } 136 | _ => { 137 | error!("unhandled completion context: {:?}", completion.context); 138 | None 139 | } 140 | } 141 | } 142 | 143 | fn handle_hover(req: Request) -> Option { 144 | let hover: HoverParams = serde_json::from_value(req.params).ok()?; 145 | debug!("handle_hover: {:?}", hover); 146 | 147 | let text_params = hover.text_document_position_params; 148 | 149 | debug!("handle_hover text_position_params: {:?}", text_params); 150 | 151 | let attribute = hx_hover(text_params)?; 152 | 153 | debug!("handle_request attribute: {:?}", attribute); 154 | 155 | Some(HtmxResult::AttributeHover(HtmxAttributeHoverResult { 156 | id: req.id, 157 | value: attribute.desc.to_string(), 158 | })) 159 | } 160 | 161 | pub fn handle_request(req: Request) -> Option { 162 | error!("handle_request"); 163 | match req.method.as_str() { 164 | "textDocument/completion" => handle_completion(req), 165 | "textDocument/hover" => handle_hover(req), 166 | _ => { 167 | warn!("unhandled request: {:?}", req); 168 | None 169 | } 170 | } 171 | } 172 | 173 | pub fn handle_notification(noti: Notification) -> Option { 174 | return match noti.method.as_str() { 175 | "textDocument/didChange" => handle_didChange(noti), 176 | "textDocument/didOpen" => handle_didOpen(noti), 177 | s => { 178 | debug!("unhandled notification: {:?}", s); 179 | None 180 | } 181 | }; 182 | } 183 | 184 | pub fn handle_other(msg: Message) -> Option { 185 | warn!("unhandled message {:?}", msg); 186 | None 187 | } 188 | 189 | #[cfg(test)] 190 | mod tests { 191 | use super::{handle_request, HtmxResult, Request}; 192 | use crate::text_store::{init_text_store, TEXT_STORE}; 193 | use std::sync::Once; 194 | 195 | static SETUP: Once = Once::new(); 196 | fn prepare_store(file: &str, content: &str) { 197 | SETUP.call_once(|| { 198 | init_text_store(); 199 | }); 200 | 201 | TEXT_STORE 202 | .get() 203 | .expect("text store not initialized") 204 | .lock() 205 | .expect("text store mutex poisoned") 206 | .insert(file.to_string(), content.to_string()); 207 | } 208 | 209 | #[test] 210 | fn handle_hover_it_presents_details_when_tag_value_is_under_cursor() { 211 | let file = "file:///detailstag.html"; 212 | let content = r#"
"#; 213 | 214 | prepare_store(file, content); 215 | 216 | let req = Request { 217 | id: 1.into(), 218 | method: "textDocument/hover".to_string(), 219 | params: serde_json::json!({ 220 | "textDocument": { 221 | "uri": file, 222 | }, 223 | "position": { 224 | "line": 0, 225 | "character": 13 226 | } 227 | }), 228 | }; 229 | 230 | let result = handle_request(req); 231 | 232 | assert!(result.is_some()); 233 | match result { 234 | Some(HtmxResult::AttributeHover(h)) => { 235 | assert_eq!(h.id, 1.into()); 236 | assert!(h.value.starts_with("hx-target")); 237 | } 238 | _ => { 239 | panic!("unexpected result: {:?}", result); 240 | } 241 | } 242 | } 243 | 244 | #[test] 245 | fn handle_hover_it_presents_details_of_the_tag_name_when_is_under_cursor() { 246 | let file = "file:///detailstag.html"; 247 | let content = r#"
"#; 248 | 249 | prepare_store(file, content); 250 | 251 | let req = Request { 252 | id: 1.into(), 253 | method: "textDocument/hover".to_string(), 254 | params: serde_json::json!({ 255 | "textDocument": { 256 | "uri": file, 257 | }, 258 | "position": { 259 | "line": 0, 260 | "character": 14 261 | } 262 | }), 263 | }; 264 | 265 | let result = handle_request(req); 266 | 267 | assert!(result.is_some()); 268 | match result { 269 | Some(HtmxResult::AttributeHover(h)) => { 270 | assert_eq!(h.id, 1.into()); 271 | assert!(h.value.starts_with("hx-target")); 272 | } 273 | _ => { 274 | panic!("unexpected result: {:?}", result); 275 | } 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-boost.md: -------------------------------------------------------------------------------- 1 | hx-boost 2 | The hx-boost attribute allows you to “boost” normal anchors and form tags to use AJAX instead. This has the nice fallback that, if the user does not have javascript enabled, the site will continue to work. 3 | 4 | For anchor tags, clicking on the anchor will issue a GET request to the url specified in the href and will push the url so that a history entry is created. The target is the tag, and the innerHTML swap strategy is used by default. All of these can be modified by using the appropriate attributes, except the click trigger. 5 | 6 | For forms the request will be converted into a GET or POST, based on the method in the method attribute and will be triggered by a submit. Again, the target will be the body of the page, and the innerHTML swap will be used. The url will not be pushed, however, and no history entry will be created. (You can use the hx-push-url attribute if you want the url to be pushed.) 7 | 8 | Here is an example of some boosted links: 9 | 10 |
11 | Go To Page 1 12 | Go To Page 2 13 |
14 | These links will issue an ajax GET request to the respective URLs and replace the body’s inner content with it. 15 | 16 | Here is an example of a boosted form: 17 | 18 |
19 | 20 | 21 |
22 | This form will issue an ajax POST to the given URL and replace the body’s inner content with it. 23 | 24 | Notes 25 | hx-boost is inherited and can be placed on a parent element 26 | Only links that are to the same domain and that are not local anchors will be boosted 27 | All requests are done via AJAX, so keep that in mind when doing things like redirects 28 | To find out if the request results from a boosted anchor or form, look for HX-Boosted in the request header 29 | Selectively disable boost on child elements with hx-boost="false" 30 | 31 | [HTMX Reference](https://htmx.org/attributes/hx-boost/) 32 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-confirm.md: -------------------------------------------------------------------------------- 1 | The hx-confirm attribute allows you to confirm an action before issuing a request. This can be useful in cases where the action is destructive and you want to ensure that the user really wants to do it. 2 | 3 | Here is an example: 4 | 5 | 8 | 9 | Notes 10 | 11 | hx-confirm is inherited and can be placed on a parent element 12 | 13 | [HTMX Reference](https://htmx.org/attributes/hx-confirm/) 14 | 15 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-delete.md: -------------------------------------------------------------------------------- 1 | hx-delete 2 | 3 | The hx-delete attribute will cause an element to issue a DELETE to the specified URL and swap the HTML into the DOM using a swap strategy: 4 | 5 | 8 | This example will cause the button to issue a DELETE to /account and swap the returned HTML into the innerHTML of the body. 9 | 10 | Notes 11 | hx-delete is not inherited 12 | You can control the target of the swap using the hx-target attribute 13 | You can control the swap strategy by using the hx-swap attribute 14 | You can control what event triggers the request with the hx-trigger attribute 15 | You can control the data submitted with the request in various ways, documented here: Parameters 16 | To remove the element following a successful DELETE, return a 200 status code with an empty body; if the server responds with a 204, no swap takes place, documented here: Requests & Responses 17 | 18 | [HTMX Reference](https://htmx.org/attributes/hx-delete/) 19 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-disable.md: -------------------------------------------------------------------------------- 1 | The hx-disable attribute will disable htmx processing for a given element and all its children. This can be useful as a backup for HTML escaping, when you include user generated content in your site, and you want to prevent malicious scripting attacks. 2 | 3 | The value of the tag is ignored, and it cannot be reversed by any content beneath it. 4 | Notes 5 | 6 | hx-disable is inherited 7 | 8 | [HTMX Reference](https://htmx.org/attributes/hx-disable/) 9 | 10 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-disabled-elt.md: -------------------------------------------------------------------------------- 1 | The hx-disabled-elt attribute allows you to specify elements that will have the disabled attribute added to them for the duration of the request. 2 | 3 | The value of this attribute is a CSS query selector of the element or elements to apply the class to, or the keyword closest, followed by a CSS selector, which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. closest tr), or the keyword this. 4 | 5 | Here is an example with a button that will disable itself during a request: 6 | 7 | 10 | 11 | When a request is in flight, this will cause the button to be marked with the disabled attribute, which will prevent further clicks from occurring. 12 | 13 | Notes 14 | 15 | hx-disable-elt is inherited and can be placed on a parent element 16 | 17 | [HTMX reference](https://htmx.org/attributes/hx-disabled-elt/) 18 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-encoding.md: -------------------------------------------------------------------------------- 1 | The hx-encoding attribute allows you to switch the request encoding from the usual application/x-www-form-urlencoded encoding to multipart/form-data, usually to support file uploads in an ajax request. 2 | 3 | The value of this attribute should be multipart/form-data. 4 | 5 | The hx-encoding tag may be placed on parent elements. 6 | Notes 7 | 8 | hx-encoding is inherited and can be placed on a parent element 9 | 10 | [HTMX Reference](https://htmx.org/attributes/hx-encoding/) 11 | 12 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-ext.md: -------------------------------------------------------------------------------- 1 | reference an extension 2 | 3 | Tip: To use multiple extensions on one element, seperate them with a comma: 4 | 5 | 6 | by default, extensions are applied to the DOM node where it is invoked, along with all child elements inside of that parent node. If you need to disable an extension somewhere within the DOM tree, you can use the ignore: keyword to stop it from being used. 7 | 8 | 9 | [HTMX Reference](https://htmx.org/attributes/hx-ext/) 10 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-get.md: -------------------------------------------------------------------------------- 1 | hx-get 2 | 3 | The hx-get attribute will cause an element to issue a GET to the specified URL and swap the HTML into the DOM using a swap strategy: 4 | 5 |
Get Some HTML
6 | This example will cause the div to issue a GET to /example and swap the returned HTML into the innerHTML of the div. 7 | 8 | Notes 9 | hx-get is not inherited 10 | By default hx-get does not include any parameters. You can use the hx-params attribute to change this 11 | You can control the target of the swap using the hx-target attribute 12 | You can control the swap strategy by using the hx-swap attribute 13 | You can control what event triggers the request with the hx-trigger attribute 14 | You can control the data submitted with the request in various ways, documented here: Parameters 15 | 16 | 17 | [HTMX Reference](https://htmx.org/attributes/hx-get/) 18 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-headers.md: -------------------------------------------------------------------------------- 1 | The hx-headers attribute allows you to add to the headers that will be submitted with an AJAX request. 2 | 3 | By default, the value of this attribute is a list of name-expression values in JSON (JavaScript Object Notation) format. 4 | 5 | If you wish for hx-headers to evaluate the values given, you can prefix the values with javascript: or js:. 6 | 7 |
Get Some HTML, Including A Custom Header in the Request
8 | 9 | Security Considerations 10 | 11 | By default, the value of hx-headers must be valid JSON. It is not dynamically computed. If you use the javascript: prefix, be aware that you are introducing security considerations, especially when dealing with user input such as query strings or user-generated content, which could introduce a Cross-Site Scripting (XSS) vulnerability. 12 | 13 | Notes 14 | 15 | hx-headers is inherited and can be placed on a parent element. 16 | A child declaration of a header overrides a parent declaration. 17 | 18 | 19 | [HTMX Reference](https://htmx.org/attributes/hx-headers/) 20 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-history-elt.md: -------------------------------------------------------------------------------- 1 | The hx-history-elt attribute allows you to specify the element that will be used to snapshot and restore page state during navigation. By default, the body tag is used. This is typically good enough for most setups, but you may want to narrow it down to a child element. Just make sure that the element is always visible in your application, or htmx will not be able to restore history navigation properly. 2 | 3 | Here is an example: 4 | 5 | 6 | 7 |
8 | ... 9 |
10 | 11 | 12 | 13 | Notes 14 | 15 | hx-history-elt is not inherited 16 | In most cases we don’t recommend narrowing the history snapshot 17 | 18 | 19 | [HTMX Reference](https://htmx.org/attributes/hx-history-elt/) 20 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-history.md: -------------------------------------------------------------------------------- 1 | Set the hx-history attribute to false on any element in the current document, or any html fragment loaded into the current document by htmx, to prevent sensitive data being saved to the localStorage cache when htmx takes a snapshot of the page state. 2 | 3 | History navigation will work as expected, but on restoration the URL will be requested from the server instead of the history cache. 4 | 5 | Here is an example: 6 | 7 | 8 | 9 |
10 | ... 11 |
12 | 13 | 14 | 15 | Notes 16 | 17 | hx-history="false" can be present anywhere in the document to embargo the current page state from the history cache (i.e. even outside the element specified for the history snapshot hx-history-elt). 18 | 19 | 20 | [HTMX Reference](https://htmx.org/attributes/hx-history/) 21 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-include.md: -------------------------------------------------------------------------------- 1 | hx-include 2 | 3 | The hx-include attribute allows you to include additional element values in an AJAX request. The value of this attribute is a CSS query selector of the element or elements to include in the query. 4 | 5 | Here is an example that includes a separate input value: 6 | 7 |
8 | 11 | Enter email: 12 |
13 | This is a little contrived as you would typically enclose both of these elements in a form and submit the value automatically, but it demonstrates the concept. 14 | 15 | Note that if you include a non-input element, all input elements enclosed in that element will be included. 16 | 17 | Notes 18 | hx-include is inherited and can be placed on a parent element 19 | 20 | [HTMX Reference](https://htmx.org/attributes/hx-include/) 21 | 22 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-indicator.md: -------------------------------------------------------------------------------- 1 | The hx-indicator attribute allows you to specify the element that will have the htmx-request class added to it for the duration of the request. This can be used to show spinners or progress indicators while the request is in flight. 2 | 3 | The value of this attribute is a CSS query selector of the element or elements to apply the class to, or the keyword closest, followed by a CSS selector, which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. closest tr); 4 | 5 | Here is an example with a spinner adjacent to the button: 6 | 7 |
8 | 11 | 12 |
13 | 14 | When a request is in flight, this will cause the htmx-request class to be added to the #spinner image. The image also has the htmx-indicator class on it, which defines an opacity transition that will show the spinner: 15 | 16 | .htmx-indicator{ 17 | opacity:0; 18 | transition: opacity 500ms ease-in; 19 | } 20 | .htmx-request .htmx-indicator{ 21 | opacity:1 22 | } 23 | .htmx-request.htmx-indicator{ 24 | opacity:1 25 | } 26 | 27 | If you would prefer a different effect for showing the spinner you could define and use your own indicator CSS. Here is an example that uses display rather than opacity (Note that we use my-indicator instead of htmx-indicator): 28 | 29 | .my-indicator{ 30 | display:none; 31 | } 32 | .htmx-request .my-indicator{ 33 | display:inline; 34 | } 35 | .htmx-request.my-indicator{ 36 | display:inline; 37 | } 38 | 39 | Note that the target of the hx-indicator selector need not be the exact element that you want to show: it can be any element in the parent hierarchy of the indicator. 40 | 41 | Finally, note that the htmx-request class by default is added to the element causing the request, so you can place an indicator inside of that element and not need to explicitly call it out with the hx-indicator attribute: 42 | 43 | 47 | 48 | Notes 49 | 50 | hx-indicator is inherited and can be placed on a parent element 51 | In the absence of an explicit indicator, the htmx-request class will be added to the element triggering the request 52 | If you want to use your own CSS but still use htmx-indicator as class name, then you need to disable includeIndicatorStyles. See Configuring htmx. The easiest way is to add this the of your HTML: 53 | 54 | 55 | 56 | 57 | [HTMX Reference](https://htmx.org/attributes/hx-indicator/) 58 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-on.md: -------------------------------------------------------------------------------- 1 | The hx-on attribute allows you to embed scripts inline to respond to events directly on an element; similar to the onevent properties found in HTML, such as onClick. 2 | 3 | hx-on improves upon onevent by enabling the handling of any event for enhanced Locality of Behaviour (LoB). This also enables you to handle any htmx event. 4 | 5 | There are two forms of this attribute, one in which you specify the event as part of the attribute name after a colon (hx-on:click, for example), and a deprecated form that uses the hx-on attribute directly. The latter should only be used if IE11 support is required. 6 | hx-on:* (recommended) 7 | 8 | The event name follows a colon : in the attribute, and the attribute value is the script to be executed: 9 | 10 |
Click
11 | 12 | All htmx events can be captured, too! Make sure to use the kebab-case event name, because DOM attributes do not preserve casing. For instance, hx-on::beforeRequest will not work: use hx-on::before-request instead. 13 | 14 | To make writing these a little easier, you can use the shorthand double-colon hx-on:: for htmx events, and omit the “htmx” part: 15 | 16 | 17 | 20 | 21 | 24 | 25 | Adding multiple handlers is easy, you just specify additional attributes: 26 | 27 | 32 | 33 | Symbols 34 | 35 | Like onevent, two symbols are made available to event handler scripts: 36 | 37 | this - The element on which the hx-on attribute is defined 38 | event - The event that triggered the handler 39 | 40 | Notes 41 | 42 | hx-on is not inherited, however due to event bubbling, hx-on attributes on parent elements will typically be triggered by events on child elements 43 | hx-on:* and hx-on cannot be used together on the same element; if hx-on:* is present, the value of an hx-on attribute on the same element will be ignored. The two forms can be mixed in the same document, however. 44 | 45 | [HTMX Reference](https://htmx.org/attributes/hx-on/) 46 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-params.md: -------------------------------------------------------------------------------- 1 | The hx-params attribute allows you to filter the parameters that will be submitted with an AJAX request. 2 | 3 | The possible values of this attribute are: 4 | 5 | * - Include all parameters (default) 6 | none - Include no parameters 7 | not - Include all except the comma separated list of parameter names 8 | - Include all the comma separated list of parameter names 9 | 10 |
Get Some HTML, Including Params
11 | 12 | This div will include all the parameters that a POST would, but they will be URL encoded and included in the URL, as per usual with a GET. 13 | Notes 14 | 15 | hx-params is inherited and can be placed on a parent element 16 | 17 | 18 | [HTMX Reference](https://htmx.org/attributes/hx-params/) 19 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-patch.md: -------------------------------------------------------------------------------- 1 | hx-patch 2 | 3 | The hx-patch attribute will cause an element to issue a PATCH to the specified URL and swap the HTML into the DOM using a swap strategy: 4 | 5 | 8 | This example will cause the button to issue a PATCH to /account and swap the returned HTML into the innerHTML of the body. 9 | 10 | Notes 11 | hx-patch is not inherited 12 | You can control the target of the swap using the hx-target attribute 13 | You can control the swap strategy by using the hx-swap attribute 14 | You can control what event triggers the request with the hx-trigger attribute 15 | You can control the data submitted with the request in various ways, documented here: Parameters 16 | 17 | [HTMX Reference](https://htmx.org/attributes/hx-patch/) 18 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-post.md: -------------------------------------------------------------------------------- 1 | hx-post 2 | 3 | The hx-post attribute will cause an element to issue a POST to the specified URL and swap the HTML into the DOM using a swap strategy: 4 | 5 | 8 | This example will cause the button to issue a POST to /account/enable and swap the returned HTML into the innerHTML of the body. 9 | 10 | Notes 11 | hx-post is not inherited 12 | You can control the target of the swap using the hx-target attribute 13 | You can control the swap strategy by using the hx-swap attribute 14 | You can control what event triggers the request with the hx-trigger attribute 15 | You can control the data submitted with the request in various ways, documented here: Parameters 16 | 17 | [HTMX Reference](https://htmx.org/attributes/hx-post/) 18 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-preserve.md: -------------------------------------------------------------------------------- 1 | The hx-preserve attribute allows you to keep an element unchanged during HTML replacement. Elements with hx-preserve set are preserved by id when htmx updates any ancestor element. You must set an unchanging id on elements for hx-preserve to work. The response requires an element with the same id, but its type and other attributes are ignored. 2 | 3 | Note that some elements cannot unfortunately be preserved properly, such as (focus and caret position are lost), iframes or certain types of videos. To tackle some of these cases we recommend the morphdom extension, which does a more elaborate DOM reconciliation. 4 | Notes 5 | 6 | hx-preserve is not inherited 7 | 8 | 9 | [HTMX Reference](https://htmx.org/attributes/hx-preserve/) 10 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-prompt.md: -------------------------------------------------------------------------------- 1 | The hx-prompt attribute allows you to show a prompt before issuing a request. The value of the prompt will be included in the request in the HX-Prompt header. 2 | 3 | Here is an example: 4 | 5 | 8 | 9 | Notes 10 | 11 | hx-prompt is inherited and can be placed on a parent element 12 | 13 | 14 | [HTMX Reference](https://htmx.org/attributes/hx-prompt/) 15 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-push-url.md: -------------------------------------------------------------------------------- 1 | hx-push-url 2 | 3 | The hx-push-url attribute allows you to push a URL into the browser location history. This creates a new history entry, allowing navigation with the browser’s back and forward buttons. htmx snapshots the current DOM and saves it into its history cache, and restores from this cache on navigation. 4 | 5 | The possible values of this attribute are: 6 | 7 | true, which pushes the fetched URL into history. 8 | false, which disables pushing the fetched URL if it would otherwise be pushed due to inheritance or hx-boost. 9 | A URL to be pushed into the location bar. This may be relative or absolute, as per history.pushState(). 10 | 11 | Here is an example: 12 | 13 |
14 | Go to My Account 15 |
16 | 17 | This will cause htmx to snapshot the current DOM to localStorage and push the URL `/account’ into the browser location bar. 18 | 19 | Another example: 20 | 21 |
22 | Go to My Account 23 |
24 | 25 | This will push the URL `/account/home’ into the location history. 26 | Notes 27 | 28 | hx-push-url is inherited and can be placed on a parent element 29 | The HX-Push-Url response header has similar behavior and can override this attribute. 30 | The hx-history-elt attribute allows changing which element is saved in the history cache. 31 | 32 | [HTMX Reference](https://htmx.org/attributes/hx-push-url/) 33 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-put.md: -------------------------------------------------------------------------------- 1 | hx-put 2 | 3 | The hx-put attribute will cause an element to issue a PUT to the specified URL and swap the HTML into the DOM using a swap strategy: 4 | 5 | 8 | This example will cause the button to issue a PUT to /account and swap the returned HTML into the innerHTML of the body. 9 | 10 | Notes 11 | hx-put is not inherited 12 | You can control the target of the swap using the hx-target attribute 13 | You can control the swap strategy by using the hx-swap attribute 14 | You can control what event triggers the request with the hx-trigger attribute 15 | You can control the data submitted with the request in various ways, documented here: Parameters 16 | 17 | [HTMX Reference](https://htmx.org/attributes/hx-put/) 18 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-replace-url.md: -------------------------------------------------------------------------------- 1 | The hx-replace-url attribute allows you to replace the current url of the browser location history. 2 | 3 | The possible values of this attribute are: 4 | 5 | true, which replaces the fetched URL in the browser navigation bar. 6 | false, which disables replacing the fetched URL if it would otherwise be replaced due to inheritance. 7 | A URL to be replaced into the location bar. This may be relative or absolute, as per history.replaceState(). 8 | 9 | Here is an example: 10 | 11 |
12 | Go to My Account 13 |
14 | 15 | This will cause htmx to snapshot the current DOM to localStorage and replace the URL `/account’ in the browser location bar. 16 | 17 | Another example: 18 | 19 |
20 | Go to My Account 21 |
22 | 23 | This will replace the URL `/account/home’ in the browser location bar. 24 | Notes 25 | 26 | hx-replace-url is inherited and can be placed on a parent element 27 | The HX-Replace-Url response header has similar behavior and can override this attribute. 28 | The hx-history-elt attribute allows changing which element is saved in the history cache. 29 | The hx-push-url attribute is a similar and more commonly used attribute, which creates a new history entry rather than replacing the current one. 30 | 31 | 32 | [HTMX Reference](https://htmx.org/attributes/hx-replace-url/) 33 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-request.md: -------------------------------------------------------------------------------- 1 | The hx-request attribute allows you to configure various aspects of the request via the following attributes: 2 | 3 | timeout - the timeout for the request, in milliseconds 4 | credentials - if the request will send credentials 5 | noHeaders - strips all headers from the request 6 | 7 | These attributes are set using a JSON-like syntax: 8 | 9 |
10 | ... 11 |
12 | 13 | You may make the values dynamically evaluated by adding the javascript: or js: prefix: 14 | 15 |
16 | ... 17 |
18 | 19 | Notes 20 | 21 | hx-request is merge-inherited and can be placed on a parent element 22 | 23 | 24 | [HTMX Reference](https://htmx.org/attributes/hx-request/) 25 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-select-oob.md: -------------------------------------------------------------------------------- 1 | The hx-select-oob attribute allows you to select content from a response to be swapped in via an out-of-band swap. 2 | The value of this attribute is comma separated list of elements to be swapped out of band. This attribute is almost always paired with hx-select. 3 | 4 | Here is an example that selects a subset of the response content: 5 | 6 |
7 |
8 | 14 |
15 | 16 | This button will issue a GET to /info and then select the element with the id info-details, which will replace the entire button in the DOM, and, in addition, pick out an element with the id alert in the response and swap it in for div in the DOM with the same ID. 17 | 18 | Each value in the comma separated list of values can specify any valid hx-swap strategy by separating the selector and the swap strategy with a :. 19 | 20 | For example, to prepend the alert content instead of replacing it: 21 | 22 |
23 |
24 | 30 |
31 | 32 | Notes 33 | 34 | hx-select-oob is inherited and can be placed on a parent element 35 | 36 | [HTMX Reference](https://htmx.org/attributes/hx-select-oob/) 37 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-select.md: -------------------------------------------------------------------------------- 1 | hx-select 2 | 3 | The hx-select attribute allows you to select the content you want swapped from a response. The value of this attribute is a CSS query selector of the element or elements to select from the response. 4 | 5 | Here is an example that selects a subset of the response content: 6 | 7 |
8 | 11 |
12 | 13 | So this button will issue a GET to /info and then select the element with the id info-detail, which will replace the entire button in the DOM. 14 | Notes 15 | 16 | hx-select is inherited and can be placed on a parent element 17 | 18 | 19 | 20 | [HTMX Reference](https://htmx.org/attributes/hx-select/) 21 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-swap-oob.md: -------------------------------------------------------------------------------- 1 | The hx-swap-oob attribute allows you to specify that some content in a response should be swapped into the DOM somewhere other than the target, that is “Out of Band”. This allows you to piggy back updates to other element updates on a response. 2 | 3 | Consider the following response HTML: 4 | 5 |
6 | ... 7 |
8 |
9 | Saved! 10 |
11 | 12 | The first div will be swapped into the target the usual manner. The second div, however, will be swapped in as a replacement for the element with the id alerts, and will not end up in the target. 13 | 14 | The value of the hx-swap-oob can be: 15 | 16 | true 17 | any valid hx-swap value 18 | any valid hx-swap value, followed by a colon, followed by a CSS selector 19 | 20 | If the value is true or outerHTML (which are equivalent) the element will be swapped inline. 21 | 22 | If a swap value is given, that swap strategy will be used. 23 | 24 | If a selector is given, all elements matched by that selector will be swapped. If not, the element with an ID matching the new content will be swapped. 25 | Notes 26 | 27 | hx-swap-oob is not inherited 28 | Out of band elements must be in the top level of the response, and not children of the top level elements. 29 | 30 | [HTMX Reference](https://htmx.org/attributes/hx-swap-oob/) 31 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-swap.md: -------------------------------------------------------------------------------- 1 | hx-swap 2 | 3 | The hx-swap attribute allows you to specify how the response will be swapped in relative to the target of an AJAX request. 4 | 5 | The possible values of this attribute are: 6 | 7 | innerHTML - The default, replace the inner html of the target element 8 | outerHTML - Replace the entire target element with the response 9 | beforebegin - Insert the response before the target element 10 | afterbegin - Insert the response before the first child of the target element 11 | beforeend - Insert the response after the last child of the target element 12 | afterend - Insert the response after the target element 13 | delete - Deletes the target element regardless of the response 14 | none- Does not append content from response (out of band items will still be processed). 15 | These options are based on standard DOM naming and the Element.insertAdjacentHTML specification. 16 | 17 | So in this code: 18 | 19 |
Get Some HTML & Append It
20 | The div will issue a request to /example and append the returned content after the div 21 | 22 | Modifiers 23 | The hx-swap attributes supports modifiers for changing the behavior of the swap. They are outlined below. 24 | 25 | Transition: transition 26 | If you want to use the new View Transitions API when a swap occurs, you can use the transition:true option for your swap. You can also enable this feature globally by setting the htmx.config.globalViewTransitions config setting to true. 27 | 28 | Timing: swap & settle 29 | You can modify the amount of time that htmx will wait after receiving a response to swap the content by including a swap modifier: 30 | 31 | 32 |
Get Some HTML & Append It
33 | Similarly, you can modify the time between the swap and the settle logic by including a settle modifier: 34 | 35 | 36 |
Get Some HTML & Append It
37 | These attributes can be used to synchronize htmx with the timing of CSS transition effects. 38 | 39 | Scrolling: scroll & show 40 | You can also change the scrolling behavior of the target element by using the scroll and show modifiers, both of which take the values top and bottom: 41 | 42 | 43 |
46 | Get Some HTML & Append It & Scroll To Bottom 47 |
48 | 50 |
53 | Get Some Content 54 |
55 | If you wish to target a different element for scrolling or showing, you may place a CSS selector after the scroll: or show:, followed by :top or :bottom: 56 | 57 | 59 |
61 | Get Some Content 62 |
63 | You may also use window:top and window:bottom to scroll to the top and bottom of the current window. 64 | 65 | 67 |
69 | Get Some Content 70 |
71 | Focus scroll 72 | htmx preserves focus between requests for inputs that have a defined id attribute. By default htmx prevents auto-scrolling to focused inputs between requests which can be unwanted behavior on longer requests when the user has already scrolled away. To enable focus scroll you can use focus-scroll:true. 73 | 74 | 76 | Alternatively, if you want the page to automatically scroll to the focused element after each request you can change the htmx global configuration value htmx.config.defaultFocusScroll to true. Then disable it for specific requests using focus-scroll:false. 77 | 78 | 80 | Notes 81 | hx-swap is inherited and can be placed on a parent element 82 | The default value of this attribute is innerHTML 83 | Due to DOM limitations, it’s not possible to use the outerHTML method on the element. htmx will change outerHTML on to use innerHTML. 84 | The default swap delay is 0ms 85 | The default settle delay is 20ms 86 | 87 | [HTMX Reference](https://htmx.org/attributes/hx-swap/) 88 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-sync.md: -------------------------------------------------------------------------------- 1 | The hx-sync attribute allows you to synchronize AJAX requests between multiple elements. 2 | 3 | The hx-sync attribute consists of a CSS selector to indicate the element to synchronize on, followed optionally by a colon and then by an optional syncing strategy. The available strategies are: 4 | 5 | drop - drop (ignore) this request if an existing request is in flight (the default) 6 | abort - drop (ignore) this request if an existing request is in flight, and, if that is not the case, abort this request if another request occurs while it is still in flight 7 | replace - abort the current request, if any, and replace it with this request 8 | queue - place this request in the request queue associated with the given element 9 | 10 | The queue modifier can take an additional argument indicating exactly how to queue: 11 | 12 | queue first - queue the first request to show up while a request is in flight 13 | queue last - queue the last request to show up while a request is in flight 14 | queue all - queue all requests that show up while a request is in flight 15 | 16 | Notes 17 | 18 | hx-sync is inherited and can be placed on a parent element 19 | 20 | This example resolves a race condition between a form’s submit request and an individual input’s validation request. Normally, without using hx-sync, filling out the input and immediately submitting the form triggers two parallel requests to /validate and /store. Using hx-sync="closest form:abort" on the input will watch for requests on the form and abort the input’s request if a form request is present or starts while the input request is in flight. 21 | 22 |
23 | 27 | 28 |
29 | 30 | If you’d rather prioritize the validation request over the submit request, you can use the drop strategy. This example will prioritize the validation request over the submit request so that if a validation request is in flight, the form cannot be submitted. 31 | 32 |
33 | 38 | 39 |
40 | 41 | When dealing with forms that contain many inputs, you can prioritize the submit request over all input validation requests using the hx-sync replace strategy on the form tag. This will cancel any in-flight validation requests and issue only the hx-post="/store" request. If you’d rather abort the submit request and prioritize any existing validation requests you can use the hx-sync="this:abort" strategy on the form tag. 42 | 43 |
44 | 45 | 46 |
47 | 48 | When implementing active search functionality the hx-trigger attribute’s delay modifier can be used to debounce the user’s input and avoid making multiple requests while the user types. However, once a request is made, if the user begins typing again a new request will begin even if the previous one has not finished processing. This example will cancel any in-flight requests and use only the last request. In cases where the search input is contained within the target, then using hx-sync like this also helps reduce the chances that the input will be replaced while the user is still typing. 49 | 50 | 55 | 56 | 57 | [HTMX Reference](https://htmx.org/attributes/hx-sync/) 58 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-target.md: -------------------------------------------------------------------------------- 1 | hx-target 2 | 3 | The hx-target attribute allows you to target a different element for swapping than the one issuing the AJAX request. The value of this attribute can be: 4 | 5 | A CSS query selector of the element to target. 6 | this which indicates that the element that the hx-target attribute is on is the target. 7 | closest which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. closest tr will target the closest table row to the element). 8 | find which will find the first child descendant element that matches the given CSS selector. 9 | next which will scan the DOM forward for the first element that matches the given CSS selector. (e.g. next .error will target the closest following sibling element with error class) 10 | previous which will scan the DOM backwards for the first element that matches the given CSS selector. (e.g previous .error will target the closest previous sibling with error class) 11 | Here is an example that targets a div: 12 | 13 |
14 |
15 | 18 |
19 | The response from the /register url will be appended to the div with the id response-div. 20 | 21 | This example uses hx-target="this" to make a link that updates itself when clicked: 22 | 23 | New link 24 | Notes 25 | hx-target is inherited and can be placed on a parent element 26 | 27 | [HTMX Reference](https://htmx.org/attributes/hx-target/) 28 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-trigger.md: -------------------------------------------------------------------------------- 1 | hx-trigger 2 | 3 | The hx-trigger attribute allows you to specify what triggers an AJAX request. A trigger value can be one of the following: 4 | 5 | An event name (e.g. “click” or “my-custom-event”) followed by an event filter and a set of event modifiers 6 | A polling definition of the form every 7 | A comma-separated list of such events 8 | Standard Events 9 | A standard event, such as click can be specified as the trigger like so: 10 | 11 |
Click Me
12 | Standard Event Filters 13 | Events can be filtered by enclosing a boolean javascript expression in square brackets after the event name. If this expression evaluates to true the event will be triggered, otherwise it will be ignored. 14 | 15 |
Control Click Me
16 | This event will trigger if a click event is triggered with the event.ctrlKey property set to true. 17 | 18 | Conditions can also refer to global functions or state 19 | 20 |
Control Click Me
21 | And can also be combined using the standard javascript syntax 22 | 23 |
Control-Shift Click Me
24 | Note that all symbols used in the expression will be resolved first against the triggering event, and then next against the global namespace, so myEvent[foo] will first look for a property named foo on the event, then look for a global symbol with the name foo 25 | 26 | Standard Event Modifiers 27 | Standard events can also have modifiers that change how they behave. The modifiers are: 28 | 29 | once - the event will only trigger once (e.g. the first click) 30 | changed - the event will only change if the value of the element has changed. Please pay attention change is the name of the event and changed is the name of the modifier. 31 | delay: - a delay will occur before an event triggers a request. If the event is seen again it will reset the delay. 32 | throttle: - a throttle will occur after an event triggers a request. If the event is seen again before the delay completes, it is ignored, the element will trigger at the end of the delay. 33 | from: - allows the event that triggers a request to come from another element in the document (e.g. listening to a key event on the body, to support hot keys) 34 | A standard CSS selector resolves to all elements matching that selector. Thus, from:input would listen on every input on the page. 35 | The extended CSS selector here allows for the following non-standard CSS values: 36 | document - listen for events on the document 37 | window - listen for events on the window 38 | closest - finds the closest ancestor element or itself, matching the given css selector 39 | find - finds the closest child matching the given css selector 40 | target: - allows you to filter via a CSS selector on the target of the event. This can be useful when you want to listen for triggers from elements that might not be in the DOM at the point of initialization, by, for example, listening on the body, but with a target filter for a child element 41 | consume - if this option is included the event will not trigger any other htmx requests on parents (or on elements listening on parents) 42 | queue: - determines how events are queued if an event occurs while a request for another event is in flight. Options are: 43 | first - queue the first event 44 | last - queue the last event (default) 45 | all - queue all events (issue a request for each event) 46 | none - do not queue new events 47 | Here is an example of a search box that searches on keyup, but only if the search value has changed and the user hasn’t typed anything new for 1 second: 48 | 49 | 52 | The response from the /search url will be appended to the div with the id search-results. 53 | 54 | Non-standard Events 55 | There are some additional non-standard events that htmx supports: 56 | 57 | load - triggered on load (useful for lazy-loading something) 58 | revealed - triggered when an element is scrolled into the viewport (also useful for lazy-loading). If you are using overflow in css like overflow-y: scroll you should use intersect once instead of revealed. 59 | intersect - fires once when an element first intersects the viewport. This supports two additional options: 60 | root: - a CSS selector of the root element for intersection 61 | threshold: - a floating point number between 0.0 and 1.0, indicating what amount of intersection to fire the event on 62 | Triggering via the HX-Trigger header 63 | If you’re trying to fire an event from HX-Trigger response header, you will likely want to use the from:body modifier. E.g. if you send a header like this HX-Trigger: my-custom-event with a response, an element would likely need to look like this: 64 | 65 |
66 | Triggered by HX-Trigger header... 67 |
68 | in order to fire. 69 | 70 | This is because the header will likely trigger the event in a different DOM hierarchy than the element that you wish to be triggered. For a similar reason, you will often listen for hot keys from the body. 71 | 72 | Polling 73 | By using the syntax every you can have an element poll periodically: 74 | 75 |
76 | Nothing Yet! 77 |
78 | This example will issue a GET to the /latest_updates URL every second and swap the results into the innerHTML of this div. 79 | 80 | If you want to add a filter to polling, it should be added after the poll declaration: 81 | 82 |
83 | Nothing Yet! 84 |
85 | Multiple Triggers 86 | Multiple triggers can be provided, separated by commas. Each trigger gets its own options. 87 | 88 |
89 | This example will load /news immediately on page load, and then again with a delay of one second after each click. 90 | 91 | Via JavaScript 92 | The AJAX request can be triggered via JavaScript htmx.trigger(), too. 93 | 94 | Notes 95 | hx-trigger is not inherited 96 | hx-trigger can be used without an AJAX request, in which case it will only fire the htmx:trigger event 97 | 98 | [HTMX Reference](https://htmx.org/attributes/hx-trigger/) 99 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-validate.md: -------------------------------------------------------------------------------- 1 | The hx-validate attribute will cause an element to validate itself by way of the HTML5 Validation API before it submits a request. 2 | 3 | Form elements do this by default, but other elements do not. 4 | Notes 5 | 6 | hx-validate is not inherited 7 | 8 | 9 | [HTMX Reference](https://htmx.org/attributes/hx-validate/) 10 | -------------------------------------------------------------------------------- /lsp/src/htmx/attributes/hx-vals.md: -------------------------------------------------------------------------------- 1 | hx-vals 2 | 3 | The hx-vals attribute allows you to add to the parameters that will be submitted with an AJAX request. 4 | 5 | By default, the value of this attribute is a list of name-expression values in JSON (JavaScript Object Notation) format. 6 | 7 | If you wish for hx-vals to evaluate the values given, you can prefix the values with javascript: or js:. 8 | 9 |
Get Some HTML, Including A Value in the Request
10 | 11 |
Get Some HTML, Including a Dynamic Value from Javascript in the Request
12 | When using evaluated code you can access the event object. This example includes the value of the last typed key within the input. 13 | 14 |
15 | 16 |
17 | Security Considerations 18 | By default, the value of hx-vals must be valid JSON. It is not dynamically computed. If you use the javascript: prefix, be aware that you are introducing security considerations, especially when dealing with user input such as query strings or user-generated content, which could introduce a Cross-Site Scripting (XSS) vulnerability. 19 | Notes 20 | hx-vals is inherited and can be placed on a parent element. 21 | A child declaration of a variable overrides a parent declaration. 22 | Input values with the same name will be overridden by variable declarations. 23 | 24 | [HTMX Reference](https://htmx.org/attributes/hx-vals/) 25 | -------------------------------------------------------------------------------- /lsp/src/htmx/hx-boost/false.md: -------------------------------------------------------------------------------- 1 | Selectively disable boost on this element and child elements 2 | 3 | [HTMX Reference](https://htmx.org/attributes/hx-boost/) 4 | -------------------------------------------------------------------------------- /lsp/src/htmx/hx-boost/true.md: -------------------------------------------------------------------------------- 1 | The hx-boost attribute allows you to “boost” normal anchors and form tags to use AJAX instead. This has the nice fallback that, if the user does not have javascript enabled, the site will continue to work. 2 | 3 | Notes 4 | hx-boost is inherited and can be placed on a parent element 5 | Only links that are to the same domain and that are not local anchors will be boosted 6 | All requests are done via AJAX, so keep that in mind when doing things like redirects 7 | To find out if the request results from a boosted anchor or form, look for HX-Boosted in the request header 8 | 9 | [HTMX Reference](https://htmx.org/attributes/hx-boost/) 10 | -------------------------------------------------------------------------------- /lsp/src/htmx/hx-disabled-elt/closest.md: -------------------------------------------------------------------------------- 1 | closest which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. closest tr will target the closest table row to the element). 2 | 3 | 4 | [HTMX Reference](https://htmx.org/attributes/hx-disabled-elt/) 5 | -------------------------------------------------------------------------------- /lsp/src/htmx/hx-disabled-elt/this.md: -------------------------------------------------------------------------------- 1 | this which indicates that the element that the hx-disabled-elt attribute is on is the target. 2 | 3 | 4 | [HTMX Reference](https://htmx.org/attributes/hx-disabled-elt/) 5 | -------------------------------------------------------------------------------- /lsp/src/htmx/hx-ext/ajax-header.md: -------------------------------------------------------------------------------- 1 | This extension adds the X-Requested-With header to requests with the value “XMLHttpRequest”. 2 | This header is commonly used by javascript frameworks to differentiate ajax requests from normal http requests. 3 | 4 | Install 5 | 6 | 7 | Usage 8 | 9 | ... 10 | 11 | 12 | 13 | [HTMX Reference](https://htmx.org/extensions/ajax-header/) 14 | -------------------------------------------------------------------------------- /lsp/src/htmx/hx-ext/alpine-morph.md: -------------------------------------------------------------------------------- 1 | Alpine.js now has a lightweight morph plugin and this extension allows you to use it as the swapping mechanism in htmx which is necessary to retain Alpine state when you have entire Alpine components swapped by htmx. 2 | 3 | Install 4 | 5 | 6 | Usage 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 |
18 |
20 | 21 |
22 | 27 |
28 |
29 | 30 | 31 | In the above example, all the Alpine x-data states (count, replaced, and message) are preserved even the entire Alpine component is swapped. 32 | 33 | NOTE: /swap response from the example above should return actual element that is being replaced (this is
11 | 12 | Usage 13 |
14 |
15 |
16 |
18 |
20 |
21 |
22 | 23 | 24 | [HTMX Reference](https://htmx.org/extensions/class-tools/) 25 | -------------------------------------------------------------------------------- /lsp/src/htmx/hx-ext/client-side-templates.md: -------------------------------------------------------------------------------- 1 | This extension supports transforming a JSON request response into HTML via a client-side template before it is swapped into the DOM. Currently three client-side templating engines are supported: 2 | 3 | mustache 4 | handlebars 5 | nunjucks 6 | 7 | When you add this extension on an element, any element below it in the DOM can use one of three attributes named -template (e.g. mustache-template) with a template ID, and the extension will resolve and render the template the standard way for that template engine: 8 | 9 | mustache - looks a mustache 17 | 18 | Usage 19 |
20 | 24 | 28 | 32 |
33 | 34 | Full HTML Example 35 | 36 | To use the client side template, you will need to include htmx, the extension, and the rendering engine. Here is an example of this setup for Mustache using a