From 3584c57ee2cd75439f841465b97336bd25ff2ffb Mon Sep 17 00:00:00 2001 From: Justin Barclay Date: Fri, 5 Jun 2020 10:26:17 -0600 Subject: [PATCH 01/10] Add guard when converting from i64 to usize In some cases, when bad data is passed in as a set of change to parinfer-rust it can cause parinfer-rust to crash. This crash is caused by trying to add indents to a line. When the delta calculated turns out to be negative and when that is added to orig_indent that can cause issues when type casting to usize (Column). This is because the combination of these two variables can be negative and when type cast to a usize it causes the number to be very large, in my case something like 1.8 trillion. So when this number is then used to determine how many times to repeat a string, it causes the library to run out of memory and crash. --- Cargo.toml | 2 +- src/parinfer.rs | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e16bb5..c5b2c8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parinfer_rust" -version = "0.4.3" +version = "0.4.4-beta" authors = ["Jason Felice "] [lib] diff --git a/src/parinfer.rs b/src/parinfer.rs index 52c762a..20cfdfd 100644 --- a/src/parinfer.rs +++ b/src/parinfer.rs @@ -627,6 +627,21 @@ fn peek_works() { assert_eq!(peek(&empty, 1), None); } +fn delta_to_column(delta: Delta) -> Column { + if delta >= 0 { + delta as Column + } else { + 0 as Column + } +} + +#[cfg(test)] +#[test] +fn delta_to_column_works(){ + assert_eq!(delta_to_column(1), 1); + assert_eq!(delta_to_column(0), 0); + assert_eq!(delta_to_column(-1), 0); +} // {{{1 Questions about characters fn is_close_paren(paren: &str) -> bool { @@ -1556,7 +1571,7 @@ fn finish_new_paren_trail<'a>(result: &mut State<'a>) { fn add_indent<'a>(result: &mut State<'a>, delta: Delta) { let orig_indent = result.x; - let new_indent = (orig_indent as Delta + delta) as Column; + let new_indent = delta_to_column(orig_indent as Delta + delta); let indent_str = repeat_string(BLANK_SPACE, new_indent); let line_no = result.line_no; replace_within_line(result, line_no, 0, orig_indent, &indent_str); From 85a6a80870e0b1345563da1c7618bc2a83e9f88e Mon Sep 17 00:00:00 2001 From: Justin Barclay Date: Sun, 17 Mar 2024 15:11:01 -0700 Subject: [PATCH 02/10] Update actions to support macos arm builds Update release script --- .github/workflows/build.yaml | 26 ++++++++++++++------------ .github/workflows/release.yaml | 30 ++++++++++++++---------------- Cargo.lock | 2 +- Cargo.toml | 2 +- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6a58d82..484c76f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -9,20 +9,22 @@ jobs: matrix: rust: [stable] os: [ubuntu-latest, macOS-latest, windows-latest] + include: + - os: macOS-latest + target: aarch64-apple-darwin + - os: windows-latest + target: "x86_64-pc-windows-msvc" + - os: ubuntu-latest + target: "x86_64-unknown-linux-gnu" steps: - - name: Setup Rust - uses: hecrj/setup-rust-action@master + - name: "Checkout Repo" + uses: actions/checkout@v4 + - name: "Install Rust" + uses: dtolnay/rust-toolchain@stable with: - rust-version: ${{ matrix.rust }} - components: rustfmt, clippy - - name: Installing LibClang on Windows - shell: pwsh - if: matrix.os == 'windows-latest' - run: choco install -y llvm - - name: Checkout - uses: actions/checkout@v1 - - name: Build the crate - run: cargo build --release + target: ${{ matrix.target }} + - name: Build the crate for release + run: cargo build --release --features emacs - name: Run tests if: matrix.os != 'windows-latest' run: cargo test --verbose diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index de3a9d9..bca2aa7 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -17,35 +17,33 @@ jobs: rust: stable artifact_name: libparinfer_rust.dylib release_name: parinfer-rust-darwin.so + target: "aarch64-apple-darwin" - os: windows-latest rust: stable artifact_name: parinfer_rust.dll release_name: parinfer-rust-windows.dll + target: "x86_64-pc-windows-msvc" - os: ubuntu-latest rust: stable artifact_name: libparinfer_rust.so release_name: parinfer-rust-linux.so + target: "x86_64-unknown-linux-gnu" steps: - - name: Setup Rust - uses: hecrj/setup-rust-action@master + - name: Checkout Repo + uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable with: - rust-version: ${{ matrix.rust }} - - name: Checkout - uses: actions/checkout@v1 - - name: Installing LibClang on Windows - shell: pwsh - if: matrix.os == 'windows-latest' - run: choco install -y llvm + target: ${{ matrix.target }} - name: Build the crate for release - run: cargo build --release + run: cargo build --release --target ${{ matrix.target }} --features emacs - name: Run tests if: matrix.os != 'windows-latest' run: cargo test --verbose + - name: Rename library + run: cp target/${{ matrix.target }}/release/${{ matrix.artifact_name }} ${{ matrix.release_name }} - name: Upload libraries to GitHub release - uses: svenstaro/upload-release-action@v1-release + uses: softprops/action-gh-release@v2 + if: startsWith(github.ref, 'refs/tags/') with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: target/release/${{ matrix.artifact_name }} - asset_name: ${{ matrix.release_name }} - tag: ${{ github.ref }} - overwrite: true + files: ${{ matrix.release_name }} diff --git a/Cargo.lock b/Cargo.lock index 6926074..cb988fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -328,7 +328,7 @@ checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "parinfer_rust" -version = "0.4.3" +version = "0.4.4" dependencies = [ "emacs", "getopts", diff --git a/Cargo.toml b/Cargo.toml index c5b2c8e..84a990e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parinfer_rust" -version = "0.4.4-beta" +version = "0.4.4" authors = ["Jason Felice "] [lib] From 9c287bbb51c29bbb3c05ec720fcc97bb485bc1a8 Mon Sep 17 00:00:00 2001 From: Justin Barclay Date: Sun, 4 Feb 2024 22:37:47 -0800 Subject: [PATCH 03/10] Merge pull request #5 from justinbarclay/jb/flake (do not move to upstream) Update to use flakes and latest version of nixpkgs Do not move upstream due to this commit breaking integration tests --- .envrc | 2 +- Cargo.lock | 2 +- flake.lock | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 85 +++++++++++++++++++++++++++++++++ nixpkgs.nix | 4 -- overlay.nix | 3 -- shell.nix | 12 ----- 7 files changed, 220 insertions(+), 21 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix delete mode 100644 nixpkgs.nix delete mode 100644 overlay.nix delete mode 100644 shell.nix diff --git a/.envrc b/.envrc index 1d953f4..a5dbbcb 100644 --- a/.envrc +++ b/.envrc @@ -1 +1 @@ -use nix +use flake . diff --git a/Cargo.lock b/Cargo.lock index cb988fb..cc2dd78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -328,7 +328,7 @@ checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "parinfer_rust" -version = "0.4.4" +version = "0.4.4-beta" dependencies = [ "emacs", "getopts", diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..ca155e1 --- /dev/null +++ b/flake.lock @@ -0,0 +1,133 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1710656467, + "narHash": "sha256-4Plj0vNP+ckWVNi6EtVojL9YV2dwSH7H4UMFCV40VE8=", + "owner": "nix-community", + "repo": "fenix", + "rev": "c53bb4a32f2fce7acf4e8e160a54779c4460ffdb", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "naersk": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1698420672, + "narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=", + "owner": "nix-community", + "repo": "naersk", + "rev": "aeb58d5e8faead8980a807c840232697982d47b9", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "naersk", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1710669607, + "narHash": "sha256-kNj0Ka1/rkQRcigYTa1c5B6IcFuxDgM3s9jYuKUhxyM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6af7e814afb3b62171eee1edc31989ee61528d25", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1710669607, + "narHash": "sha256-kNj0Ka1/rkQRcigYTa1c5B6IcFuxDgM3s9jYuKUhxyM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6af7e814afb3b62171eee1edc31989ee61528d25", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "naersk": "naersk", + "nixpkgs": "nixpkgs_2" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1710610549, + "narHash": "sha256-xFIGLn5u+msUazlLbdjZ3gQgXrt7Lrlhq+XXUH0XU/0=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "5ecace48f693afaa6adf8cb23086b651db3aec96", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..32bcd42 --- /dev/null +++ b/flake.nix @@ -0,0 +1,85 @@ +{ + inputs = { + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + flake-utils.url = "github:numtide/flake-utils"; + naersk.url = "github:nix-community/naersk"; + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + + outputs = { self, flake-utils, naersk, nixpkgs, fenix }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + localeEnv = if pkgs.stdenv.isDarwin then "" else "LOCALE_ARCHIVE=${pkgs.glibcLocales}/lib/locale/locale-archive"; + naersk' = pkgs.callPackage naersk { }; + + parinfer-rust = naersk'.buildPackage { + src = ./.; + doCheck = true; + nativeBuildInputs = [ pkgs.cargo-nextest ]; + buildFlags = [ "--release" "--no-default-features" ]; + cargoTestCommands = x: [ ''cargo nextest run'' ]; + }; + + runVimTests = name: path: pkgs.stdenv.mkDerivation { + name = "parinfer-rust-${name}-tests"; + src = ./tests/vim; + buildPhase = '' + printf 'Testing %s\n' '${path}' + LC_ALL=en_US.UTF-8 \ + ${localeEnv} \ + VIM_TO_TEST=${path} \ + PLUGIN_TO_TEST=${parinfer-rust}/share/vim-plugins/parinfer-rust \ + ${pkgs.vim}/bin/vim --clean -u run.vim + ''; + installPhase = '' + touch $out + ''; + }; + + vim-tests = runVimTests "vim" "${pkgs.vim}/bin/vim"; + + neovim-tests = runVimTests "neovim" "${pkgs.neovim}/bin/nvim"; + in + rec { + # For `nix build` & `nix run`: + packages = { + default = parinfer-rust; + app = parinfer-rust; + }; + + checks = { + neovim = neovim-tests; + vim = vim-tests; + kakoune = pkgs.stdenv.mkDerivation { + name = "parinfer-rust-kakoune-tests"; + src = ./tests/kakoune; + buildInputs = [ + pkgs.kakoune-unwrapped + parinfer-rust + ]; + buildPhase = '' + patchShebangs ./run.sh + PLUGIN_TO_TEST=${parinfer-rust}/share/kak/autoload/plugins ./run.sh + ''; + installPhase = '' + touch $out + ''; + }; + }; + + devShells = with pkgs; { + default = mkShell { + nativeBuildInputs = [ + rustc + cargo + vim + neovim + ] ++ lib.optional stdenv.isDarwin libiconv; + }; + }; + }); +} diff --git a/nixpkgs.nix b/nixpkgs.nix deleted file mode 100644 index f84dd52..0000000 --- a/nixpkgs.nix +++ /dev/null @@ -1,4 +0,0 @@ -builtins.fetchTarball { - url = "https://github.com/nixos/nixpkgs/archive/21.11.tar.gz"; - sha256 = "162dywda2dvfj1248afxc45kcrg83appjd0nmdb541hl7rnncf02"; -} diff --git a/overlay.nix b/overlay.nix deleted file mode 100644 index eb1e1ee..0000000 --- a/overlay.nix +++ /dev/null @@ -1,3 +0,0 @@ -self: super: { - parinfer-rust = super.callPackage ./derivation.nix {}; -} diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 83fd234..0000000 --- a/shell.nix +++ /dev/null @@ -1,12 +0,0 @@ -let - nixpkgs = import ./nixpkgs.nix; - pkgs = import nixpkgs { - config = {}; - overlays = [ (import ./overlay.nix) ]; - }; -in pkgs.parinfer-rust.overrideAttrs (oldAttrs: { - nativeBuildInputs = oldAttrs.nativeBuildInputs ++ (with pkgs; [ - vim - neovim - ]); -}) From ed1fabdfb628beb683ac63d1b5d5786f83ff349f Mon Sep 17 00:00:00 2001 From: Justin Barclay Date: Mon, 18 Mar 2024 21:01:18 -0700 Subject: [PATCH 04/10] Add emacs to default features (do not move to upstream) For ease of development make sure that the emacs features are always tested against --- Cargo.toml | 3 +++ flake.nix | 1 + 2 files changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 84a990e..d4439a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ serde_derive = "1.0" unicode-segmentation = "1.1.0" unicode-width = "0.1.5" +[features] +default = ["emacs"] + [target.'cfg(target_arch = "wasm32")'.dependencies] stdweb = "0.4.4" diff --git a/flake.nix b/flake.nix index 32bcd42..99c309b 100644 --- a/flake.nix +++ b/flake.nix @@ -76,6 +76,7 @@ nativeBuildInputs = [ rustc cargo + rust-analyzer vim neovim ] ++ lib.optional stdenv.isDarwin libiconv; From 5f99e91c1c690d9f76fc3f6dd140e4c351b1d416 Mon Sep 17 00:00:00 2001 From: Gerry Agbobada Date: Fri, 25 Aug 2023 00:39:20 +0200 Subject: [PATCH 05/10] [Emacs] Use default CLI options when nothing is specified --- src/emacs_wrapper.rs | 22 ++-------------------- src/types.rs | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/emacs_wrapper.rs b/src/emacs_wrapper.rs index aa95709..8cbf7a9 100644 --- a/src/emacs_wrapper.rs +++ b/src/emacs_wrapper.rs @@ -103,7 +103,7 @@ fn execute(request: AliasedRequest) -> Result { #[defun(user_ptr, mod_in_name = false)] // Create an Options Structure // We need this because we can't pass in an optional variant of Options in the new_options function -/// Returns an Option with nil data for all fields +/// Returns an Option with nil/default data for all fields /// /// # Examples /// @@ -111,25 +111,7 @@ fn execute(request: AliasedRequest) -> Result { /// (parinfer-make-option) /// ``` fn make_option() -> Result { - Ok(Options { - cursor_x: None, - cursor_line: None, - prev_cursor_x: None, - prev_cursor_line: None, - prev_text: None, - selection_start_line: None, - changes: Vec::new(), - partial_result: false, - force_balance: false, - return_parens: false, - comment_char: ';', - string_delimiters: vec!["\"".to_string()], - lisp_vline_symbols: false, - lisp_block_comments: false, - guile_block_comments: false, - scheme_sexp_comments: false, - janet_long_strings: false, - }) + Ok(Options::default()) } #[defun(user_ptr, mod_in_name = false)] diff --git a/src/types.rs b/src/types.rs index 701c7f6..e54efd3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -51,6 +51,31 @@ pub struct Options { pub janet_long_strings: bool, } +impl Default for Options { + fn default() -> Self { + Self { + cursor_x: None, + cursor_line: None, + prev_cursor_x: None, + prev_cursor_line: None, + prev_text: None, + selection_start_line: None, + changes: Self::default_changes(), + partial_result: false, + force_balance: false, + return_parens: false, + comment_char: Self::default_comment(), + string_delimiters: Self::default_string_delimiters(), + lisp_vline_symbols: false, + lisp_block_comments: false, + guile_block_comments: false, + scheme_sexp_comments: false, + janet_long_strings: false, + } + } +} + + impl Options { fn default_changes() -> Vec { vec![] From b39d48088c6d835adee20e9f3311d752f0b7916a Mon Sep 17 00:00:00 2001 From: Gerry Agbobada Date: Fri, 25 Aug 2023 00:40:04 +0200 Subject: [PATCH 06/10] [Emacs] expose setters and getters for request options --- src/emacs_wrapper.rs | 171 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 147 insertions(+), 24 deletions(-) diff --git a/src/emacs_wrapper.rs b/src/emacs_wrapper.rs index 8cbf7a9..76432c6 100644 --- a/src/emacs_wrapper.rs +++ b/src/emacs_wrapper.rs @@ -123,31 +123,154 @@ fn make_option() -> Result { /// (parinfer-new-option 1 1 nil options changes) /// ``` fn new_options( - cursor_x: Option, - cursor_line: Option, - selection_start_line: Option, - old_options: &Options, - changes: &Vec, + cursor_x: Option, + cursor_line: Option, + selection_start_line: Option, + old_options: &Options, + changes: &Vec, ) -> Result { - Ok(Options { - cursor_x: to_usize(cursor_x), - cursor_line: to_usize(cursor_line), - prev_cursor_x: old_options.cursor_x, - prev_cursor_line: old_options.cursor_line, - selection_start_line: to_usize(selection_start_line), - changes: changes.clone(), - prev_text: None, - partial_result: false, - force_balance: false, - return_parens: false, - comment_char: ';', - string_delimiters: vec!["\"".to_string()], - lisp_vline_symbols: false, - lisp_block_comments: false, - guile_block_comments: false, - scheme_sexp_comments: false, - janet_long_strings: false, - }) + Ok(Options { + cursor_x: to_usize(cursor_x), + cursor_line: to_usize(cursor_line), + prev_cursor_x: old_options.cursor_x, + prev_cursor_line: old_options.cursor_line, + selection_start_line: to_usize(selection_start_line), + changes: changes.clone(), + prev_text: None, + ..old_options + }) +} + +emacs::define_errors! { + unknown_option_error "This option name is unknown should not be negative" (error) +} + +#[defun(user_ptr, mod_in_name = false)] +/// Set a field within the passed options. +/// +/// Valid field names are: +/// - `partial-result' +/// - `force-balance' +/// - `return-parens' +/// - `comment-char' +/// - `string-delimiters' +/// - `lisp-vline-symbols' +/// - `lisp-block-comments' +/// - `guile-block-comments' +/// - `scheme-sexp-comments' +/// - `janet-long-strings' +/// +/// # Examples +/// +/// ```elisp,no_run +/// (parinfer-set-option options 'partial-result t) +/// ``` +fn set_option<'a>( + options: &mut Options, + option_name: Value<'a>, + new_value: Option>, +) -> Result<()> { + let env = option_name.env; + if env.eq(option_name, env.intern("partial-result")?) { + options.partial_result = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("force-balance")?) { + options.force_balance = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("return-parens")?) { + options.return_parens = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("comment-char")?) { + options.comment_char = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("string-delimiters")?) { + options.string_delimiters = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("lisp-vline-symbols")?) { + options.lisp_vline_symbols = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("lisp-block-comments")?) { + options.lisp_block_comments = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("guile-block-comments")?) { + options.guile_block_comments = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("scheme-sexp-comments")?) { + options.scheme_sexp_comments = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("julia-long-strings")?) { + options.julia_long_strings = new_value.into_rust()?; + return Ok(()); + } + + env.signal(unknown_option_error, option_name) +} + +#[defun(user_ptr, mod_in_name = false)] +/// Get a field within the passed options. +/// +/// Valid field names are: +/// - `partial-result' +/// - `force-balance' +/// - `return-parens' +/// - `comment-char' +/// - `string-delimiters' +/// - `lisp-vline-symbols' +/// - `lisp-block-comments' +/// - `guile-block-comments' +/// - `scheme-sexp-comments' +/// - `janet-long-strings' +/// +/// # Examples +/// +/// ```elisp,no_run +/// (parinfer-get-option options 'partial-result) +/// ``` +fn get_option<'a>(options: &Options, option_name: Value<'a>) -> Result> { + // The function is returning a type-erased Value because it can either be a boolean + // or a list + let env = option_name.env; + if env.eq(option_name, env.intern("partial-result")?) { + return Ok(options.partial_result.into_lisp(env)?); + } + if env.eq(option_name, env.intern("force-balance")?) { + return Ok(options.force_balance.into_lisp(env)?); + } + if env.eq(option_name, env.intern("return-parens")?) { + return Ok(options.return_parens.into_lisp(env)?); + } + if env.eq(option_name, env.intern("comment-char")?) { + return Ok(options.comment_char.into_lisp(env)?); + } + if env.eq(option_name, env.intern("string-delimiters")?) { + return Ok(options.string_delimiters.into_lisp(env)?); + } + if env.eq(option_name, env.intern("lisp-vline-symbols")?) { + return Ok(options.lisp_vline_symbols.into_lisp(env)?); + } + if env.eq(option_name, env.intern("lisp-block-comments")?) { + return Ok(options.lisp_block_comments.into_lisp(env)?); + } + if env.eq(option_name, env.intern("guile-block-comments")?) { + return Ok(options.guile_block_comments.into_lisp(env)?); + } + if env.eq(option_name, env.intern("scheme-sexp-comments")?) { + return Ok(options.scheme_sexp_comments.into_lisp(env)?); + } + if env.eq(option_name, env.intern("julia-long-strings")?) { + return Ok(options.julia_long_strings.into_lisp(env)?); + } + + env.signal(unknown_option_error, option_name) } #[defun(mod_in_name = false)] From 3cb800aaf65184899d8f52d0ef533a2fd1899e32 Mon Sep 17 00:00:00 2001 From: Gerry Agbobada Date: Fri, 25 Aug 2023 00:43:11 +0200 Subject: [PATCH 07/10] lint: add rustfmt.toml and pass format Using the configuration `tab_spaces = 2` to minimize the impact on formatting src/emacs_wrapper.rs --- rustfmt.toml | 1 + src/emacs_wrapper.rs | 214 ++++++++++++++++++++++--------------------- 2 files changed, 109 insertions(+), 106 deletions(-) create mode 100644 rustfmt.toml diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..b196eaa --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +tab_spaces = 2 diff --git a/src/emacs_wrapper.rs b/src/emacs_wrapper.rs index 76432c6..440f060 100644 --- a/src/emacs_wrapper.rs +++ b/src/emacs_wrapper.rs @@ -2,11 +2,7 @@ use super::parinfer::rc_process; use emacs::{Env, IntoLisp, Result, Value}; use types::{Change, Error, Options, Request, SharedRequest, WrappedAnswer}; -use std::{fs::OpenOptions, - io::Write, - convert::TryFrom, - cell::RefCell, - rc::Rc}; +use std::{cell::RefCell, convert::TryFrom, fs::OpenOptions, io::Write, rc::Rc}; emacs::plugin_is_GPL_compatible!(); @@ -94,7 +90,7 @@ type AliasedRequest<'a> = &'a SharedRequest; /// ``` fn execute(request: AliasedRequest) -> Result { let answer = rc_process(&request); - let wrapped_answer = unsafe{WrappedAnswer::new(request.clone(), answer)}; + let wrapped_answer = unsafe { WrappedAnswer::new(request.clone(), answer) }; Ok(wrapped_answer) } //////////////////////////////// @@ -111,7 +107,7 @@ fn execute(request: AliasedRequest) -> Result { /// (parinfer-make-option) /// ``` fn make_option() -> Result { - Ok(Options::default()) + Ok(Options::default()) } #[defun(user_ptr, mod_in_name = false)] @@ -123,22 +119,22 @@ fn make_option() -> Result { /// (parinfer-new-option 1 1 nil options changes) /// ``` fn new_options( - cursor_x: Option, - cursor_line: Option, - selection_start_line: Option, - old_options: &Options, - changes: &Vec, + cursor_x: Option, + cursor_line: Option, + selection_start_line: Option, + old_options: &Options, + changes: &Vec, ) -> Result { - Ok(Options { - cursor_x: to_usize(cursor_x), - cursor_line: to_usize(cursor_line), - prev_cursor_x: old_options.cursor_x, - prev_cursor_line: old_options.cursor_line, - selection_start_line: to_usize(selection_start_line), - changes: changes.clone(), - prev_text: None, - ..old_options - }) + Ok(Options { + cursor_x: to_usize(cursor_x), + cursor_line: to_usize(cursor_line), + prev_cursor_x: old_options.cursor_x, + prev_cursor_line: old_options.cursor_line, + selection_start_line: to_usize(selection_start_line), + changes: changes.clone(), + prev_text: None, + ..old_options + }) } emacs::define_errors! { @@ -166,53 +162,53 @@ emacs::define_errors! { /// (parinfer-set-option options 'partial-result t) /// ``` fn set_option<'a>( - options: &mut Options, - option_name: Value<'a>, - new_value: Option>, + options: &mut Options, + option_name: Value<'a>, + new_value: Option>, ) -> Result<()> { - let env = option_name.env; - if env.eq(option_name, env.intern("partial-result")?) { - options.partial_result = new_value.into_rust()?; - return Ok(()); - } - if env.eq(option_name, env.intern("force-balance")?) { - options.force_balance = new_value.into_rust()?; - return Ok(()); - } - if env.eq(option_name, env.intern("return-parens")?) { - options.return_parens = new_value.into_rust()?; - return Ok(()); - } - if env.eq(option_name, env.intern("comment-char")?) { - options.comment_char = new_value.into_rust()?; - return Ok(()); - } - if env.eq(option_name, env.intern("string-delimiters")?) { - options.string_delimiters = new_value.into_rust()?; - return Ok(()); - } - if env.eq(option_name, env.intern("lisp-vline-symbols")?) { - options.lisp_vline_symbols = new_value.into_rust()?; - return Ok(()); - } - if env.eq(option_name, env.intern("lisp-block-comments")?) { - options.lisp_block_comments = new_value.into_rust()?; - return Ok(()); - } - if env.eq(option_name, env.intern("guile-block-comments")?) { - options.guile_block_comments = new_value.into_rust()?; - return Ok(()); - } - if env.eq(option_name, env.intern("scheme-sexp-comments")?) { - options.scheme_sexp_comments = new_value.into_rust()?; - return Ok(()); - } - if env.eq(option_name, env.intern("julia-long-strings")?) { - options.julia_long_strings = new_value.into_rust()?; - return Ok(()); - } + let env = option_name.env; + if env.eq(option_name, env.intern("partial-result")?) { + options.partial_result = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("force-balance")?) { + options.force_balance = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("return-parens")?) { + options.return_parens = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("comment-char")?) { + options.comment_char = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("string-delimiters")?) { + options.string_delimiters = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("lisp-vline-symbols")?) { + options.lisp_vline_symbols = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("lisp-block-comments")?) { + options.lisp_block_comments = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("guile-block-comments")?) { + options.guile_block_comments = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("scheme-sexp-comments")?) { + options.scheme_sexp_comments = new_value.into_rust()?; + return Ok(()); + } + if env.eq(option_name, env.intern("julia-long-strings")?) { + options.julia_long_strings = new_value.into_rust()?; + return Ok(()); + } - env.signal(unknown_option_error, option_name) + env.signal(unknown_option_error, option_name) } #[defun(user_ptr, mod_in_name = false)] @@ -236,41 +232,41 @@ fn set_option<'a>( /// (parinfer-get-option options 'partial-result) /// ``` fn get_option<'a>(options: &Options, option_name: Value<'a>) -> Result> { - // The function is returning a type-erased Value because it can either be a boolean - // or a list - let env = option_name.env; - if env.eq(option_name, env.intern("partial-result")?) { - return Ok(options.partial_result.into_lisp(env)?); - } - if env.eq(option_name, env.intern("force-balance")?) { - return Ok(options.force_balance.into_lisp(env)?); - } - if env.eq(option_name, env.intern("return-parens")?) { - return Ok(options.return_parens.into_lisp(env)?); - } - if env.eq(option_name, env.intern("comment-char")?) { - return Ok(options.comment_char.into_lisp(env)?); - } - if env.eq(option_name, env.intern("string-delimiters")?) { - return Ok(options.string_delimiters.into_lisp(env)?); - } - if env.eq(option_name, env.intern("lisp-vline-symbols")?) { - return Ok(options.lisp_vline_symbols.into_lisp(env)?); - } - if env.eq(option_name, env.intern("lisp-block-comments")?) { - return Ok(options.lisp_block_comments.into_lisp(env)?); - } - if env.eq(option_name, env.intern("guile-block-comments")?) { - return Ok(options.guile_block_comments.into_lisp(env)?); - } - if env.eq(option_name, env.intern("scheme-sexp-comments")?) { - return Ok(options.scheme_sexp_comments.into_lisp(env)?); - } - if env.eq(option_name, env.intern("julia-long-strings")?) { - return Ok(options.julia_long_strings.into_lisp(env)?); - } + // The function is returning a type-erased Value because it can either be a boolean + // or a list + let env = option_name.env; + if env.eq(option_name, env.intern("partial-result")?) { + return Ok(options.partial_result.into_lisp(env)?); + } + if env.eq(option_name, env.intern("force-balance")?) { + return Ok(options.force_balance.into_lisp(env)?); + } + if env.eq(option_name, env.intern("return-parens")?) { + return Ok(options.return_parens.into_lisp(env)?); + } + if env.eq(option_name, env.intern("comment-char")?) { + return Ok(options.comment_char.into_lisp(env)?); + } + if env.eq(option_name, env.intern("string-delimiters")?) { + return Ok(options.string_delimiters.into_lisp(env)?); + } + if env.eq(option_name, env.intern("lisp-vline-symbols")?) { + return Ok(options.lisp_vline_symbols.into_lisp(env)?); + } + if env.eq(option_name, env.intern("lisp-block-comments")?) { + return Ok(options.lisp_block_comments.into_lisp(env)?); + } + if env.eq(option_name, env.intern("guile-block-comments")?) { + return Ok(options.guile_block_comments.into_lisp(env)?); + } + if env.eq(option_name, env.intern("scheme-sexp-comments")?) { + return Ok(options.scheme_sexp_comments.into_lisp(env)?); + } + if env.eq(option_name, env.intern("julia-long-strings")?) { + return Ok(options.julia_long_strings.into_lisp(env)?); + } - env.signal(unknown_option_error, option_name) + env.signal(unknown_option_error, option_name) } #[defun(mod_in_name = false)] @@ -404,13 +400,14 @@ fn get_in_answer<'a>( "error" => match unwrapped_answer.error.clone() { Some(error) => Ok(RefCell::new(error).into_lisp(env)?), None => ().into_lisp(env), - } + }, // "tab_stops" // "paren_trails" // "parens" _ => { env.message(format!("Key '{}' unsupported", query))?; - ().into_lisp(env)}, + ().into_lisp(env) + } } } @@ -434,7 +431,12 @@ fn print_answer(answer: &WrappedAnswer) -> Result { /// ```elisp,no_run /// (parinfer-rust-debug "/tmp/parinfer.txt" options answer) /// ``` -fn debug(env: &Env, filename: String, options: &Options, wrapped_answer: &WrappedAnswer) -> Result<()> { +fn debug( + env: &Env, + filename: String, + options: &Options, + wrapped_answer: &WrappedAnswer, +) -> Result<()> { let answer = wrapped_answer.inner(); let file = match OpenOptions::new().append(true).create(true).open(&filename) { Ok(file) => file, @@ -467,7 +469,7 @@ fn debug(env: &Env, filename: String, options: &Options, wrapped_answer: &Wrappe /// ```elisp,no_run /// (parinfer-get-in-error error "message") /// ``` -fn get_in_error<'a>(env: &'a Env, error: &Error, key: Option)-> Result> { +fn get_in_error<'a>(env: &'a Env, error: &Error, key: Option) -> Result> { let query = match key { Some(key) => key, None => "".to_string(), @@ -495,7 +497,7 @@ fn get_in_error<'a>(env: &'a Env, error: &Error, key: Option)-> Result Result{ +fn print_error(error: &Error) -> Result { Ok(format!("{:?}", error).to_string()) } From 3d328cd52d3d683e604a36f0de4a17ec3efd5e60 Mon Sep 17 00:00:00 2001 From: Gerry Agbobada Date: Sun, 27 Aug 2023 13:50:10 +0200 Subject: [PATCH 08/10] Backport justinbarclay/parinfer-rust@67924eefef This is redone by using `saturating_add_signed` instead of having an extra helper function. The function is stable since Rust 1.66 --- Cargo.lock | 2 +- src/parinfer.rs | 2865 ++++++++++++++++++++++++----------------------- 2 files changed, 1450 insertions(+), 1417 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc2dd78..cb988fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -328,7 +328,7 @@ checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "parinfer_rust" -version = "0.4.4-beta" +version = "0.4.4" dependencies = [ "emacs", "getopts", diff --git a/src/parinfer.rs b/src/parinfer.rs index 20cfdfd..b2cd198 100644 --- a/src/parinfer.rs +++ b/src/parinfer.rs @@ -1,10 +1,11 @@ use super::std; -use std::collections::HashMap; +use changes; use std::borrow::Cow; +use std::collections::HashMap; +use std::convert::TryInto; +use types::*; use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; -use types::*; -use changes; // {{{1 Constants / Predicates @@ -19,341 +20,360 @@ const TAB: &'static str = "\t"; const GRAVE: &'static str = "`"; fn match_paren(paren: &str) -> Option<&'static str> { - match paren { - "{" => Some("}"), - "}" => Some("{"), - "[" => Some("]"), - "]" => Some("["), - "(" => Some(")"), - ")" => Some("("), - _ => None, - } + match paren { + "{" => Some("}"), + "}" => Some("{"), + "[" => Some("]"), + "]" => Some("["), + "(" => Some(")"), + ")" => Some("("), + _ => None, + } } #[cfg(test)] #[test] fn match_paren_works() { - assert_eq!(match_paren("}"), Some("{")); - assert_eq!(match_paren("x"), None); + assert_eq!(match_paren("}"), Some("{")); + assert_eq!(match_paren("x"), None); } // {{{1 Options Structure struct TransformedChange { - old_end_x: Column, - new_end_x: Column, - lookup_line_no: LineNumber, - lookup_x: Column, + old_end_x: Column, + new_end_x: Column, + lookup_line_no: LineNumber, + lookup_x: Column, } pub fn chomp_cr<'a>(text: &'a str) -> &'a str { - if text.chars().last() == Some('\r') { - &text[0..text.len() - 1] - } else { - text - } + if text.chars().last() == Some('\r') { + &text[0..text.len() - 1] + } else { + text + } } fn split_lines<'a>(text: &'a str) -> Vec<&'a str> { - text.split('\n').map(chomp_cr).collect() + text.split('\n').map(chomp_cr).collect() } fn transform_change<'a>(change: &'a Change) -> TransformedChange { - let new_lines: Vec<&'a str> = change.new_text.split('\n').map(chomp_cr).collect(); - let old_lines: Vec<&'a str> = change.old_text.split('\n').map(chomp_cr).collect(); - - // single line case: - // (defn foo| []) - // ^ newEndX, newEndLineNo - // +++ - - // multi line case: - // (defn foo - // ++++ - // "docstring." - // ++++++++++++++++ - // |[]) - // ++^ newEndX, newEndLineNo - - let last_old_line_len = UnicodeWidthStr::width(old_lines[old_lines.len() - 1]); - let last_new_line_len = UnicodeWidthStr::width(new_lines[new_lines.len() - 1]); - - let old_end_x = (if old_lines.len() == 1 { change.x } else { 0 }) + last_old_line_len; - let new_end_x = (if new_lines.len() == 1 { change.x } else { 0 }) + last_new_line_len; - let new_end_line_no = change.line_no + (new_lines.len() - 1); - - TransformedChange { - old_end_x, - new_end_x, - - lookup_line_no: new_end_line_no, - lookup_x: new_end_x, - } + let new_lines: Vec<&'a str> = change.new_text.split('\n').map(chomp_cr).collect(); + let old_lines: Vec<&'a str> = change.old_text.split('\n').map(chomp_cr).collect(); + + // single line case: + // (defn foo| []) + // ^ newEndX, newEndLineNo + // +++ + + // multi line case: + // (defn foo + // ++++ + // "docstring." + // ++++++++++++++++ + // |[]) + // ++^ newEndX, newEndLineNo + + let last_old_line_len = UnicodeWidthStr::width(old_lines[old_lines.len() - 1]); + let last_new_line_len = UnicodeWidthStr::width(new_lines[new_lines.len() - 1]); + + let old_end_x = (if old_lines.len() == 1 { change.x } else { 0 }) + last_old_line_len; + let new_end_x = (if new_lines.len() == 1 { change.x } else { 0 }) + last_new_line_len; + let new_end_line_no = change.line_no + (new_lines.len() - 1); + + TransformedChange { + old_end_x, + new_end_x, + + lookup_line_no: new_end_line_no, + lookup_x: new_end_x, + } } fn transform_changes<'a>( - changes: &Vec, + changes: &Vec, ) -> HashMap<(LineNumber, Column), TransformedChange> { - let mut lines: HashMap<(LineNumber, Column), TransformedChange> = HashMap::new(); - for change in changes { - let transformed_change = transform_change(change); - lines.insert( - ( - transformed_change.lookup_line_no, - transformed_change.lookup_x, - ), - transformed_change, - ); - } - lines + let mut lines: HashMap<(LineNumber, Column), TransformedChange> = HashMap::new(); + for change in changes { + let transformed_change = transform_change(change); + lines.insert( + ( + transformed_change.lookup_line_no, + transformed_change.lookup_x, + ), + transformed_change, + ); + } + lines } // {{{1 State Structure (was Result) #[derive(Debug)] struct ParenTrailClamped<'a> { - start_x: Option, - end_x: Option, - openers: Vec>, + start_x: Option, + end_x: Option, + openers: Vec>, } #[derive(Debug)] struct InternalParenTrail<'a> { - line_no: Option, - start_x: Option, - end_x: Option, - openers: Vec>, - clamped: ParenTrailClamped<'a>, + line_no: Option, + start_x: Option, + end_x: Option, + openers: Vec>, + clamped: ParenTrailClamped<'a>, } #[derive(PartialEq, Eq)] pub enum Mode { - Indent, - Paren, + Indent, + Paren, } #[derive(PartialEq, Eq, Clone, Copy)] enum TrackingArgTabStop { - NotSearching, - Space, - Arg, + NotSearching, + Space, + Arg, } #[derive(PartialEq, Eq)] enum Now { - Normal, - Escaping, - Escaped, + Normal, + Escaping, + Escaped, } impl<'a> State<'a> { - fn is_escaping(&self) -> bool { - match self.escape { Now::Escaping => true, _ => false } + fn is_escaping(&self) -> bool { + match self.escape { + Now::Escaping => true, + _ => false, } - fn is_escaped(&self) -> bool { - match self.escape { Now::Escaped => true, _ => false } + } + fn is_escaped(&self) -> bool { + match self.escape { + Now::Escaped => true, + _ => false, } + } } #[derive(PartialEq, Eq)] enum In<'a> { - Code, - Comment, - String { delim: &'a str }, - LispReaderSyntax, - LispBlockCommentPre { depth: usize }, - LispBlockComment { depth: usize }, - LispBlockCommentPost { depth: usize }, - GuileBlockComment, - GuileBlockCommentPost, - JanetLongStringPre { open_delim_len: usize }, - JanetLongString { open_delim_len: usize, close_delim_len: usize }, + Code, + Comment, + String { + delim: &'a str, + }, + LispReaderSyntax, + LispBlockCommentPre { + depth: usize, + }, + LispBlockComment { + depth: usize, + }, + LispBlockCommentPost { + depth: usize, + }, + GuileBlockComment, + GuileBlockCommentPost, + JanetLongStringPre { + open_delim_len: usize, + }, + JanetLongString { + open_delim_len: usize, + close_delim_len: usize, + }, } impl<'a> State<'a> { - fn is_in_code(&self) -> bool { - match self.context { - In::Code => true, - In::LispReaderSyntax => true, - _ => false - } + fn is_in_code(&self) -> bool { + match self.context { + In::Code => true, + In::LispReaderSyntax => true, + _ => false, } - fn is_in_comment(&self) -> bool { - match self.context { In::Comment => true, _ => false } + } + fn is_in_comment(&self) -> bool { + match self.context { + In::Comment => true, + _ => false, } - fn is_in_stringish(&self) -> bool { - match self.context { - In::String {..} => true, - In::LispBlockCommentPre {..} => true, - In::LispBlockComment {..} => true, - In::LispBlockCommentPost {..} => true, - In::GuileBlockComment => true, - In::GuileBlockCommentPost => true, - In::JanetLongStringPre {..} => true, - In::JanetLongString {..} => true, - _ => false - } + } + fn is_in_stringish(&self) -> bool { + match self.context { + In::String { .. } => true, + In::LispBlockCommentPre { .. } => true, + In::LispBlockComment { .. } => true, + In::LispBlockCommentPost { .. } => true, + In::GuileBlockComment => true, + In::GuileBlockCommentPost => true, + In::JanetLongStringPre { .. } => true, + In::JanetLongString { .. } => true, + _ => false, } + } } struct State<'a> { - mode: Mode, - smart: bool, + mode: Mode, + smart: bool, - orig_text: &'a str, - orig_cursor_x: Option, - orig_cursor_line: Option, + orig_text: &'a str, + orig_cursor_x: Option, + orig_cursor_line: Option, - input_lines: Vec<&'a str>, - input_line_no: LineNumber, - input_x: Column, + input_lines: Vec<&'a str>, + input_line_no: LineNumber, + input_x: Column, - lines: Vec>, - line_no: LineNumber, - ch: &'a str, - x: Column, - indent_x: Option, + lines: Vec>, + line_no: LineNumber, + ch: &'a str, + x: Column, + indent_x: Option, - paren_stack: Vec>, + paren_stack: Vec>, - tab_stops: Vec>, + tab_stops: Vec>, - paren_trail: InternalParenTrail<'a>, - paren_trails: Vec, + paren_trail: InternalParenTrail<'a>, + paren_trails: Vec, - return_parens: bool, - parens: Vec>, + return_parens: bool, + parens: Vec>, - cursor_x: Option, - cursor_line: Option, - prev_cursor_x: Option, - prev_cursor_line: Option, + cursor_x: Option, + cursor_line: Option, + prev_cursor_x: Option, + prev_cursor_line: Option, - selection_start_line: Option, + selection_start_line: Option, - changes: HashMap<(LineNumber, Column), TransformedChange>, + changes: HashMap<(LineNumber, Column), TransformedChange>, - context: In<'a>, - comment_x: Option, - escape: Now, + context: In<'a>, + comment_x: Option, + escape: Now, - lisp_vline_symbols_enabled: bool, - lisp_reader_syntax_enabled: bool, - lisp_block_comments_enabled: bool, - guile_block_comments_enabled: bool, - scheme_sexp_comments_enabled: bool, - janet_long_strings_enabled: bool, + lisp_vline_symbols_enabled: bool, + lisp_reader_syntax_enabled: bool, + lisp_block_comments_enabled: bool, + guile_block_comments_enabled: bool, + scheme_sexp_comments_enabled: bool, + janet_long_strings_enabled: bool, - quote_danger: bool, - tracking_indent: bool, - skip_char: bool, - success: bool, - partial_result: bool, - force_balance: bool, + quote_danger: bool, + tracking_indent: bool, + skip_char: bool, + success: bool, + partial_result: bool, + force_balance: bool, - comment_char: String, - string_delimiters: Vec, + comment_char: String, + string_delimiters: Vec, - max_indent: Option, - indent_delta: i64, + max_indent: Option, + indent_delta: i64, - tracking_arg_tab_stop: TrackingArgTabStop, + tracking_arg_tab_stop: TrackingArgTabStop, - error: Option, - error_pos_cache: HashMap, + error: Option, + error_pos_cache: HashMap, } fn initial_paren_trail<'a>() -> InternalParenTrail<'a> { - InternalParenTrail { - line_no: None, - start_x: None, - end_x: None, - openers: vec![], - clamped: ParenTrailClamped { - start_x: None, - end_x: None, - openers: vec![], - }, - } + InternalParenTrail { + line_no: None, + start_x: None, + end_x: None, + openers: vec![], + clamped: ParenTrailClamped { + start_x: None, + end_x: None, + openers: vec![], + }, + } } -fn get_initial_result<'a>( - text: &'a str, - options: &Options, - mode: Mode, - smart: bool, -) -> State<'a> { - let lisp_reader_syntax_enabled = [ - options.lisp_block_comments, - options.guile_block_comments, - options.scheme_sexp_comments, - ].iter().any(|is_true| *is_true); +fn get_initial_result<'a>(text: &'a str, options: &Options, mode: Mode, smart: bool) -> State<'a> { + let lisp_reader_syntax_enabled = [ + options.lisp_block_comments, + options.guile_block_comments, + options.scheme_sexp_comments, + ] + .iter() + .any(|is_true| *is_true); - State { - mode: mode, - smart: smart, + State { + mode: mode, + smart: smart, - orig_text: text, - orig_cursor_x: options.cursor_x, - orig_cursor_line: options.cursor_line, + orig_text: text, + orig_cursor_x: options.cursor_x, + orig_cursor_line: options.cursor_line, - input_lines: split_lines(text), - input_line_no: 0, - input_x: 0, + input_lines: split_lines(text), + input_line_no: 0, + input_x: 0, - lines: vec![], - line_no: usize::max_value(), - ch: &text[0..0], - x: 0, - indent_x: None, + lines: vec![], + line_no: usize::max_value(), + ch: &text[0..0], + x: 0, + indent_x: None, - paren_stack: vec![], - tab_stops: vec![], + paren_stack: vec![], + tab_stops: vec![], - paren_trail: initial_paren_trail(), - paren_trails: vec![], + paren_trail: initial_paren_trail(), + paren_trails: vec![], - return_parens: false, - parens: vec![], + return_parens: false, + parens: vec![], - cursor_x: options.cursor_x, - cursor_line: options.cursor_line, - prev_cursor_x: options.prev_cursor_x, - prev_cursor_line: options.prev_cursor_line, + cursor_x: options.cursor_x, + cursor_line: options.cursor_line, + prev_cursor_x: options.prev_cursor_x, + prev_cursor_line: options.prev_cursor_line, - selection_start_line: None, + selection_start_line: None, - changes: transform_changes(&options.changes), + changes: transform_changes(&options.changes), - context: In::Code, - comment_x: None, - escape: Now::Normal, + context: In::Code, + comment_x: None, + escape: Now::Normal, - lisp_vline_symbols_enabled: options.lisp_vline_symbols, - lisp_reader_syntax_enabled, - lisp_block_comments_enabled: options.lisp_block_comments, - guile_block_comments_enabled: options.guile_block_comments, - scheme_sexp_comments_enabled: options.scheme_sexp_comments, - janet_long_strings_enabled: options.janet_long_strings, + lisp_vline_symbols_enabled: options.lisp_vline_symbols, + lisp_reader_syntax_enabled, + lisp_block_comments_enabled: options.lisp_block_comments, + guile_block_comments_enabled: options.guile_block_comments, + scheme_sexp_comments_enabled: options.scheme_sexp_comments, + janet_long_strings_enabled: options.janet_long_strings, - quote_danger: false, - tracking_indent: false, - skip_char: false, - success: false, - partial_result: false, - force_balance: false, + quote_danger: false, + tracking_indent: false, + skip_char: false, + success: false, + partial_result: false, + force_balance: false, - comment_char: options.comment_char.to_string(), - string_delimiters: options.string_delimiters.clone(), + comment_char: options.comment_char.to_string(), + string_delimiters: options.string_delimiters.clone(), - max_indent: None, - indent_delta: 0, + max_indent: None, + indent_delta: 0, - tracking_arg_tab_stop: TrackingArgTabStop::NotSearching, + tracking_arg_tab_stop: TrackingArgTabStop::NotSearching, - error: None, - error_pos_cache: HashMap::new(), - } + error: None, + error_pos_cache: HashMap::new(), + } } // {{{1 Possible Errors @@ -361,1612 +381,1625 @@ fn get_initial_result<'a>( pub type Result = std::result::Result; fn error_message(error: ErrorName) -> &'static str { - match error { - ErrorName::QuoteDanger => "Quotes must balanced inside comment blocks.", - ErrorName::EolBackslash => "Line cannot end in a hanging backslash.", - ErrorName::UnclosedQuote => "String is missing a closing quote.", - ErrorName::UnclosedParen => "Unclosed open-paren.", - ErrorName::UnmatchedCloseParen => "Unmatched close-paren.", - ErrorName::UnmatchedOpenParen => "Unmatched open-paren.", - ErrorName::LeadingCloseParen => "Line cannot lead with a close-paren.", - ErrorName::Utf8EncodingError => "UTF8 encoded incorrectly.", - ErrorName::JsonEncodingError => "JSON encoded incorrectly.", - ErrorName::Panic => "Internal error (please report!)", - - ErrorName::Restart => "Restart requested (you shouldn't see this).", - } + match error { + ErrorName::QuoteDanger => "Quotes must balanced inside comment blocks.", + ErrorName::EolBackslash => "Line cannot end in a hanging backslash.", + ErrorName::UnclosedQuote => "String is missing a closing quote.", + ErrorName::UnclosedParen => "Unclosed open-paren.", + ErrorName::UnmatchedCloseParen => "Unmatched close-paren.", + ErrorName::UnmatchedOpenParen => "Unmatched open-paren.", + ErrorName::LeadingCloseParen => "Line cannot lead with a close-paren.", + ErrorName::Utf8EncodingError => "UTF8 encoded incorrectly.", + ErrorName::JsonEncodingError => "JSON encoded incorrectly.", + ErrorName::Panic => "Internal error (please report!)", + + ErrorName::Restart => "Restart requested (you shouldn't see this).", + } } fn cache_error_pos(result: &mut State, name: ErrorName) { - let error = Error { - name, - message: String::new(), - line_no: result.line_no, - x: result.x, - input_line_no: result.input_line_no, - input_x: result.input_x, - }; - result.error_pos_cache.insert(name, error); + let error = Error { + name, + message: String::new(), + line_no: result.line_no, + x: result.x, + input_line_no: result.input_line_no, + input_x: result.input_x, + }; + result.error_pos_cache.insert(name, error); } fn error(result: &mut State, name: ErrorName) -> Result<()> { - let (line_no, x) = match (result.partial_result, result.error_pos_cache.get(&name)) { - (true, Some(cache)) => (cache.line_no, cache.x), - (false, Some(cache)) => (cache.input_line_no, cache.input_x), - (true, None) => (result.line_no, result.x), - (false, None) => (result.input_line_no, result.input_x), - }; - - let mut e = Error { - name, - line_no, - x, - message: String::from(error_message(name)), - input_line_no: result.input_line_no, - input_x: result.input_x, - }; - - if name == ErrorName::UnclosedParen { - if let Some(opener) = peek(&result.paren_stack, 0) { - e.line_no = if result.partial_result { - opener.line_no - } else { - opener.input_line_no - }; - e.x = if result.partial_result { - opener.x - } else { - opener.input_x - }; - } + let (line_no, x) = match (result.partial_result, result.error_pos_cache.get(&name)) { + (true, Some(cache)) => (cache.line_no, cache.x), + (false, Some(cache)) => (cache.input_line_no, cache.input_x), + (true, None) => (result.line_no, result.x), + (false, None) => (result.input_line_no, result.input_x), + }; + + let mut e = Error { + name, + line_no, + x, + message: String::from(error_message(name)), + input_line_no: result.input_line_no, + input_x: result.input_x, + }; + + if name == ErrorName::UnclosedParen { + if let Some(opener) = peek(&result.paren_stack, 0) { + e.line_no = if result.partial_result { + opener.line_no + } else { + opener.input_line_no + }; + e.x = if result.partial_result { + opener.x + } else { + opener.input_x + }; } + } - Err(e) + Err(e) } // {{{1 String Operations fn column_byte_index(s: &str, x: usize) -> usize { - s.grapheme_indices(true) - .scan(0, |column, (idx, ch)| { - let start_column = *column; - *column = *column + UnicodeWidthStr::width(ch); - Some((start_column, (idx, ch))) - }) - .filter_map(|(n, (idx, _))| if n == x { Some(idx) } else { None }) - .nth(0) - .unwrap_or_else(|| s.len()) + s.grapheme_indices(true) + .scan(0, |column, (idx, ch)| { + let start_column = *column; + *column = *column + UnicodeWidthStr::width(ch); + Some((start_column, (idx, ch))) + }) + .filter_map(|(n, (idx, _))| if n == x { Some(idx) } else { None }) + .nth(0) + .unwrap_or_else(|| s.len()) } #[cfg(test)] #[test] fn column_byte_index_works() { - assert_eq!(column_byte_index("abc", 1), 1); - assert_eq!(column_byte_index("abc", 3), 3); - assert_eq!(column_byte_index("åbc", 3), 4); - assert_eq!(column_byte_index("åbc", 1), 2); - assert_eq!(column_byte_index("wo", 4), 6); - assert_eq!(column_byte_index("wo", 2), 3); - assert_eq!(column_byte_index("wo", 0), 0); + assert_eq!(column_byte_index("abc", 1), 1); + assert_eq!(column_byte_index("abc", 3), 3); + assert_eq!(column_byte_index("åbc", 3), 4); + assert_eq!(column_byte_index("åbc", 1), 2); + assert_eq!(column_byte_index("wo", 4), 6); + assert_eq!(column_byte_index("wo", 2), 3); + assert_eq!(column_byte_index("wo", 0), 0); } fn replace_within_string(orig: &str, start: usize, end: usize, replace: &str) -> String { - let start_i = column_byte_index(orig, start); - let end_i = column_byte_index(orig, end); - String::from(&orig[0..start_i]) + replace + &orig[end_i..] + let start_i = column_byte_index(orig, start); + let end_i = column_byte_index(orig, end); + String::from(&orig[0..start_i]) + replace + &orig[end_i..] } #[cfg(test)] #[test] fn replace_within_string_works() { - assert_eq!(replace_within_string("aaa", 0, 2, ""), "a"); - assert_eq!(replace_within_string("aaa", 0, 1, "b"), "baa"); - assert_eq!(replace_within_string("aaa", 0, 2, "b"), "ba"); - assert_eq!(replace_within_string("ééé", 0, 2, ""), "é"); - assert_eq!(replace_within_string("ééé", 0, 1, "b"), "béé"); - assert_eq!(replace_within_string("ééé", 1, 2, "b"), "ébé"); - assert_eq!(replace_within_string("ééé", 0, 2, "b"), "bé"); - assert_eq!(replace_within_string("ééé", 3, 3, "b"), "éééb"); + assert_eq!(replace_within_string("aaa", 0, 2, ""), "a"); + assert_eq!(replace_within_string("aaa", 0, 1, "b"), "baa"); + assert_eq!(replace_within_string("aaa", 0, 2, "b"), "ba"); + assert_eq!(replace_within_string("ééé", 0, 2, ""), "é"); + assert_eq!(replace_within_string("ééé", 0, 1, "b"), "béé"); + assert_eq!(replace_within_string("ééé", 1, 2, "b"), "ébé"); + assert_eq!(replace_within_string("ééé", 0, 2, "b"), "bé"); + assert_eq!(replace_within_string("ééé", 3, 3, "b"), "éééb"); } fn repeat_string(text: &str, n: usize) -> String { - String::from(text).repeat(n) + String::from(text).repeat(n) } #[cfg(test)] #[test] fn repeat_string_works() { - assert_eq!(repeat_string("a", 2), "aa"); - assert_eq!(repeat_string("aa", 3), "aaaaaa"); - assert_eq!(repeat_string("aa", 0), ""); - assert_eq!(repeat_string("", 0), ""); - assert_eq!(repeat_string("", 5), ""); + assert_eq!(repeat_string("a", 2), "aa"); + assert_eq!(repeat_string("aa", 3), "aaaaaa"); + assert_eq!(repeat_string("aa", 0), ""); + assert_eq!(repeat_string("", 0), ""); + assert_eq!(repeat_string("", 5), ""); } fn get_line_ending(text: &str) -> &'static str { - if text.chars().any(|ch| ch == '\r') { - "\r\n" - } else { - "\n" - } + if text.chars().any(|ch| ch == '\r') { + "\r\n" + } else { + "\n" + } } #[cfg(test)] #[test] fn get_line_ending_works() { - assert_eq!(get_line_ending("foo\nbar"), "\n"); - assert_eq!(get_line_ending("foo\r\nbar"), "\r\n"); + assert_eq!(get_line_ending("foo\nbar"), "\n"); + assert_eq!(get_line_ending("foo\r\nbar"), "\r\n"); } // {{{1 Line operations fn is_cursor_affected<'a>(result: &State<'a>, start: Column, end: Column) -> bool { - match result.cursor_x { - Some(x) if x == start && x == end => x == 0, - Some(x) => x >= end, - None => false, - } + match result.cursor_x { + Some(x) if x == start && x == end => x == 0, + Some(x) => x >= end, + None => false, + } } fn shift_cursor_on_edit<'a>( - result: &mut State<'a>, - line_no: LineNumber, - start: Column, - end: Column, - replace: &str, + result: &mut State<'a>, + line_no: LineNumber, + start: Column, + end: Column, + replace: &str, ) { - let old_length = end - start; - let new_length = UnicodeWidthStr::width(replace); - let dx = new_length as Delta - old_length as Delta; + let old_length = end - start; + let new_length = UnicodeWidthStr::width(replace); + let dx = new_length as Delta - old_length as Delta; - if let (Some(cursor_x), Some(cursor_line)) = (result.cursor_x, result.cursor_line) { - if dx != 0 && cursor_line == line_no && is_cursor_affected(result, start, end) { - result.cursor_x = Some(((cursor_x as Delta) + dx) as usize); - } + if let (Some(cursor_x), Some(cursor_line)) = (result.cursor_x, result.cursor_line) { + if dx != 0 && cursor_line == line_no && is_cursor_affected(result, start, end) { + result.cursor_x = Some(((cursor_x as Delta) + dx) as usize); } + } } fn replace_within_line<'a>( - result: &mut State<'a>, - line_no: LineNumber, - start: Column, - end: Column, - replace: &str, + result: &mut State<'a>, + line_no: LineNumber, + start: Column, + end: Column, + replace: &str, ) { - let line = result.lines[line_no].clone(); - let new_line = replace_within_string(&line, start, end, replace); - result.lines[line_no] = Cow::from(new_line); + let line = result.lines[line_no].clone(); + let new_line = replace_within_string(&line, start, end, replace); + result.lines[line_no] = Cow::from(new_line); - shift_cursor_on_edit(result, line_no, start, end, replace); + shift_cursor_on_edit(result, line_no, start, end, replace); } fn insert_within_line<'a>(result: &mut State<'a>, line_no: LineNumber, idx: Column, insert: &str) { - replace_within_line(result, line_no, idx, idx, insert); + replace_within_line(result, line_no, idx, idx, insert); } fn init_line<'a>(result: &mut State<'a>) { - result.x = 0; - result.line_no = usize::wrapping_add(result.line_no, 1); + result.x = 0; + result.line_no = usize::wrapping_add(result.line_no, 1); - // reset line-specific state - result.indent_x = None; - result.comment_x = None; - result.indent_delta = 0; + // reset line-specific state + result.indent_x = None; + result.comment_x = None; + result.indent_delta = 0; - result - .error_pos_cache - .remove(&ErrorName::UnmatchedCloseParen); - result - .error_pos_cache - .remove(&ErrorName::UnmatchedOpenParen); - result.error_pos_cache.remove(&ErrorName::LeadingCloseParen); + result + .error_pos_cache + .remove(&ErrorName::UnmatchedCloseParen); + result + .error_pos_cache + .remove(&ErrorName::UnmatchedOpenParen); + result.error_pos_cache.remove(&ErrorName::LeadingCloseParen); - result.tracking_arg_tab_stop = TrackingArgTabStop::NotSearching; - result.tracking_indent = !result.is_in_stringish(); + result.tracking_arg_tab_stop = TrackingArgTabStop::NotSearching; + result.tracking_indent = !result.is_in_stringish(); } fn commit_char<'a>(result: &mut State<'a>, orig_ch: &'a str) { - let ch = result.ch; - let ch_width = UnicodeWidthStr::width(ch); - if orig_ch != ch { - let line_no = result.line_no; - let x = result.x; - let orig_ch_width = UnicodeWidthStr::width(orig_ch); - replace_within_line(result, line_no, x, x + orig_ch_width, ch); - result.indent_delta -= orig_ch_width as Delta - ch_width as Delta; - } - result.x += ch_width; + let ch = result.ch; + let ch_width = UnicodeWidthStr::width(ch); + if orig_ch != ch { + let line_no = result.line_no; + let x = result.x; + let orig_ch_width = UnicodeWidthStr::width(orig_ch); + replace_within_line(result, line_no, x, x + orig_ch_width, ch); + result.indent_delta -= orig_ch_width as Delta - ch_width as Delta; + } + result.x += ch_width; } // {{{1 Misc Utils fn clamp(val: T, min_n: Option, max_n: Option) -> T { - if let Some(low) = min_n { - if low >= val { - return low; - } + if let Some(low) = min_n { + if low >= val { + return low; } - if let Some(high) = max_n { - if high <= val { - return high; - } + } + if let Some(high) = max_n { + if high <= val { + return high; } - val + } + val } #[cfg(test)] #[test] fn clamp_works() { - assert_eq!(clamp(1, Some(3), Some(5)), 3); - assert_eq!(clamp(9, Some(3), Some(5)), 5); - assert_eq!(clamp(1, Some(3), None), 3); - assert_eq!(clamp(5, Some(3), None), 5); - assert_eq!(clamp(1, None, Some(5)), 1); - assert_eq!(clamp(9, None, Some(5)), 5); - assert_eq!(clamp(1, None, None), 1); + assert_eq!(clamp(1, Some(3), Some(5)), 3); + assert_eq!(clamp(9, Some(3), Some(5)), 5); + assert_eq!(clamp(1, Some(3), None), 3); + assert_eq!(clamp(5, Some(3), None), 5); + assert_eq!(clamp(1, None, Some(5)), 1); + assert_eq!(clamp(9, None, Some(5)), 5); + assert_eq!(clamp(1, None, None), 1); } fn peek(array: &Vec, i: usize) -> Option<&T> { - if i >= array.len() { - None - } else { - Some(&array[array.len() - 1 - i]) - } + if i >= array.len() { + None + } else { + Some(&array[array.len() - 1 - i]) + } } #[cfg(test)] #[test] fn peek_works() { - assert_eq!(peek(&vec!['a'], 0), Some(&'a')); - assert_eq!(peek(&vec!['a'], 1), None); - assert_eq!(peek(&vec!['a', 'b', 'c'], 0), Some(&'c')); - assert_eq!(peek(&vec!['a', 'b', 'c'], 1), Some(&'b')); - assert_eq!(peek(&vec!['a', 'b', 'c'], 5), None); - let empty: Vec = vec![]; - assert_eq!(peek(&empty, 0), None); - assert_eq!(peek(&empty, 1), None); + assert_eq!(peek(&vec!['a'], 0), Some(&'a')); + assert_eq!(peek(&vec!['a'], 1), None); + assert_eq!(peek(&vec!['a', 'b', 'c'], 0), Some(&'c')); + assert_eq!(peek(&vec!['a', 'b', 'c'], 1), Some(&'b')); + assert_eq!(peek(&vec!['a', 'b', 'c'], 5), None); + let empty: Vec = vec![]; + assert_eq!(peek(&empty, 0), None); + assert_eq!(peek(&empty, 1), None); } fn delta_to_column(delta: Delta) -> Column { - if delta >= 0 { - delta as Column - } else { - 0 as Column - } + if delta >= 0 { + delta as Column + } else { + 0 as Column + } } #[cfg(test)] #[test] -fn delta_to_column_works(){ - assert_eq!(delta_to_column(1), 1); - assert_eq!(delta_to_column(0), 0); - assert_eq!(delta_to_column(-1), 0); +fn delta_to_column_works() { + assert_eq!(delta_to_column(1), 1); + assert_eq!(delta_to_column(0), 0); + assert_eq!(delta_to_column(-1), 0); } // {{{1 Questions about characters fn is_close_paren(paren: &str) -> bool { - match paren { - "}" | "]" | ")" => true, - _ => false, - } + match paren { + "}" | "]" | ")" => true, + _ => false, + } } fn is_valid_close_paren<'a>(paren_stack: &Vec>, ch: &'a str) -> bool { - if paren_stack.is_empty() { - return false; - } - if let Some(paren) = peek(paren_stack, 0) { - if let Some(close) = match_paren(ch) { - if paren.ch == close { - return true; - } - } + if paren_stack.is_empty() { + return false; + } + if let Some(paren) = peek(paren_stack, 0) { + if let Some(close) = match_paren(ch) { + if paren.ch == close { + return true; + } } - false + } + false } fn is_whitespace<'a>(result: &State<'a>) -> bool { - !result.is_escaped() && (result.ch == BLANK_SPACE || result.ch == DOUBLE_SPACE) + !result.is_escaped() && (result.ch == BLANK_SPACE || result.ch == DOUBLE_SPACE) } fn is_closable<'a>(result: &State<'a>) -> bool { - let ch = result.ch; - let closer = is_close_paren(ch) && !result.is_escaped(); - return result.is_in_code() && !is_whitespace(result) && ch != "" && !closer; + let ch = result.ch; + let closer = is_close_paren(ch) && !result.is_escaped(); + return result.is_in_code() && !is_whitespace(result) && ch != "" && !closer; } - // {{{1 Advanced operations on characters fn check_cursor_holding<'a>(result: &State<'a>) -> Result { - let opener = peek(&result.paren_stack, 0).unwrap(); - let hold_min_x = peek(&result.paren_stack, 1).map(|p| p.x + 1).unwrap_or(0); - let hold_max_x = opener.x; - - let holding = result.cursor_line == Some(opener.line_no) - && result.cursor_x.map(|x| hold_min_x <= x).unwrap_or(false) - && result.cursor_x.map(|x| x <= hold_max_x).unwrap_or(false); - let should_check_prev = result.changes.is_empty() && result.prev_cursor_line != None; - if should_check_prev { - let prev_holding = result.prev_cursor_line == Some(opener.line_no) - && result - .prev_cursor_x - .map(|x| hold_min_x <= x) - .unwrap_or(false) - && result - .prev_cursor_x - .map(|x| x <= hold_max_x) - .unwrap_or(false); - if prev_holding && !holding { - return Err(Error { - name: ErrorName::Restart, - x: 0, - input_line_no: 0, - input_x: 0, - line_no: 0, - message: String::new(), - }); - } + let opener = peek(&result.paren_stack, 0).unwrap(); + let hold_min_x = peek(&result.paren_stack, 1).map(|p| p.x + 1).unwrap_or(0); + let hold_max_x = opener.x; + + let holding = result.cursor_line == Some(opener.line_no) + && result.cursor_x.map(|x| hold_min_x <= x).unwrap_or(false) + && result.cursor_x.map(|x| x <= hold_max_x).unwrap_or(false); + let should_check_prev = result.changes.is_empty() && result.prev_cursor_line != None; + if should_check_prev { + let prev_holding = result.prev_cursor_line == Some(opener.line_no) + && result + .prev_cursor_x + .map(|x| hold_min_x <= x) + .unwrap_or(false) + && result + .prev_cursor_x + .map(|x| x <= hold_max_x) + .unwrap_or(false); + if prev_holding && !holding { + return Err(Error { + name: ErrorName::Restart, + x: 0, + input_line_no: 0, + input_x: 0, + line_no: 0, + message: String::new(), + }); } + } - Ok(holding) + Ok(holding) } fn track_arg_tab_stop<'a>(result: &mut State<'a>, state: TrackingArgTabStop) { - if state == TrackingArgTabStop::Space { - if result.is_in_code() && is_whitespace(result) { - result.tracking_arg_tab_stop = TrackingArgTabStop::Arg; - } - } else if state == TrackingArgTabStop::Arg { - if !is_whitespace(result) { - let opener = result.paren_stack.last_mut().unwrap(); - opener.arg_x = Some(result.x); - result.tracking_arg_tab_stop = TrackingArgTabStop::NotSearching; - } + if state == TrackingArgTabStop::Space { + if result.is_in_code() && is_whitespace(result) { + result.tracking_arg_tab_stop = TrackingArgTabStop::Arg; + } + } else if state == TrackingArgTabStop::Arg { + if !is_whitespace(result) { + let opener = result.paren_stack.last_mut().unwrap(); + opener.arg_x = Some(result.x); + result.tracking_arg_tab_stop = TrackingArgTabStop::NotSearching; } + } } // {{{1 Literal character events fn in_code_on_open_paren<'a>(result: &mut State<'a>) { - let opener = Paren { - input_line_no: result.input_line_no, - input_x: result.input_x, + let opener = Paren { + input_line_no: result.input_line_no, + input_x: result.input_x, - line_no: result.line_no, - x: result.x, - ch: result.ch, - indent_delta: result.indent_delta, - max_child_indent: None, + line_no: result.line_no, + x: result.x, + ch: result.ch, + indent_delta: result.indent_delta, + max_child_indent: None, - arg_x: None, + arg_x: None, - closer: None, - children: vec![] - }; + closer: None, + children: vec![], + }; - if result.return_parens { - if let Some(parent) = result.paren_stack.last_mut() { - parent.children.push(opener.clone()); - } else { - result.parens.push(opener.clone()); - } + if result.return_parens { + if let Some(parent) = result.paren_stack.last_mut() { + parent.children.push(opener.clone()); + } else { + result.parens.push(opener.clone()); } - result.paren_stack.push(opener); - result.tracking_arg_tab_stop = TrackingArgTabStop::Space; + } + result.paren_stack.push(opener); + result.tracking_arg_tab_stop = TrackingArgTabStop::Space; } fn in_code_on_matched_close_paren<'a>(result: &mut State<'a>) -> Result<()> { - let mut opener = (*peek(&result.paren_stack, 0).unwrap()).clone(); - if result.return_parens { - set_closer(&mut opener, result.line_no, result.x, result.ch); - } + let mut opener = (*peek(&result.paren_stack, 0).unwrap()).clone(); + if result.return_parens { + set_closer(&mut opener, result.line_no, result.x, result.ch); + } - result.paren_trail.end_x = Some(result.x + 1); - result.paren_trail.openers.push(opener); + result.paren_trail.end_x = Some(result.x + 1); + result.paren_trail.openers.push(opener); - if result.mode == Mode::Indent && result.smart && check_cursor_holding(result)? { - let orig_start_x = result.paren_trail.start_x; - let orig_end_x = result.paren_trail.end_x; - let orig_openers = result.paren_trail.openers.clone(); - let x = result.x; - let line_no = result.line_no; - reset_paren_trail(result, line_no, x + 1); - result.paren_trail.clamped = ParenTrailClamped { - start_x: orig_start_x, - end_x: orig_end_x, - openers: orig_openers, - }; - } - result.paren_stack.pop(); - result.tracking_arg_tab_stop = TrackingArgTabStop::NotSearching; + if result.mode == Mode::Indent && result.smart && check_cursor_holding(result)? { + let orig_start_x = result.paren_trail.start_x; + let orig_end_x = result.paren_trail.end_x; + let orig_openers = result.paren_trail.openers.clone(); + let x = result.x; + let line_no = result.line_no; + reset_paren_trail(result, line_no, x + 1); + result.paren_trail.clamped = ParenTrailClamped { + start_x: orig_start_x, + end_x: orig_end_x, + openers: orig_openers, + }; + } + result.paren_stack.pop(); + result.tracking_arg_tab_stop = TrackingArgTabStop::NotSearching; - Ok(()) + Ok(()) } fn in_code_on_unmatched_close_paren<'a>(result: &mut State<'a>) -> Result<()> { - match result.mode { - Mode::Paren => { - let in_leading_paren_trail = result.paren_trail.line_no == Some(result.line_no) - && result.paren_trail.start_x == result.indent_x; - let can_remove = result.smart && in_leading_paren_trail; - if !can_remove { - error(result, ErrorName::UnmatchedCloseParen)?; - } - } - Mode::Indent => { - if !result - .error_pos_cache - .contains_key(&ErrorName::UnmatchedCloseParen) - { - cache_error_pos(result, ErrorName::UnmatchedCloseParen); - if peek(&result.paren_stack, 0).is_some() { - cache_error_pos(result, ErrorName::UnmatchedOpenParen); - let opener = peek(&result.paren_stack, 0).unwrap(); - if let Some(err) = result - .error_pos_cache - .get_mut(&ErrorName::UnmatchedOpenParen) - { - err.input_line_no = opener.input_line_no; - err.input_x = opener.input_x; - } - } - } + match result.mode { + Mode::Paren => { + let in_leading_paren_trail = result.paren_trail.line_no == Some(result.line_no) + && result.paren_trail.start_x == result.indent_x; + let can_remove = result.smart && in_leading_paren_trail; + if !can_remove { + error(result, ErrorName::UnmatchedCloseParen)?; + } + } + Mode::Indent => { + if !result + .error_pos_cache + .contains_key(&ErrorName::UnmatchedCloseParen) + { + cache_error_pos(result, ErrorName::UnmatchedCloseParen); + if peek(&result.paren_stack, 0).is_some() { + cache_error_pos(result, ErrorName::UnmatchedOpenParen); + let opener = peek(&result.paren_stack, 0).unwrap(); + if let Some(err) = result + .error_pos_cache + .get_mut(&ErrorName::UnmatchedOpenParen) + { + err.input_line_no = opener.input_line_no; + err.input_x = opener.input_x; + } } + } } - result.ch = ""; + } + result.ch = ""; - Ok(()) + Ok(()) } fn in_code_on_close_paren<'a>(result: &mut State<'a>) -> Result<()> { - if is_valid_close_paren(&result.paren_stack, result.ch) { - in_code_on_matched_close_paren(result)?; - } else { - in_code_on_unmatched_close_paren(result)?; - } + if is_valid_close_paren(&result.paren_stack, result.ch) { + in_code_on_matched_close_paren(result)?; + } else { + in_code_on_unmatched_close_paren(result)?; + } - Ok(()) + Ok(()) } fn in_code_on_tab<'a>(result: &mut State<'a>) { - result.ch = DOUBLE_SPACE; + result.ch = DOUBLE_SPACE; } fn in_code_on_comment_char<'a>(result: &mut State<'a>) { - result.context = In::Comment; - result.comment_x = Some(result.x); - result.tracking_arg_tab_stop = TrackingArgTabStop::NotSearching; + result.context = In::Comment; + result.comment_x = Some(result.x); + result.tracking_arg_tab_stop = TrackingArgTabStop::NotSearching; } fn on_newline<'a>(result: &mut State<'a>) { - if result.is_in_comment() { - result.context = In::Code; - } - result.ch = ""; + if result.is_in_comment() { + result.context = In::Code; + } + result.ch = ""; } fn in_code_on_quote<'a>(result: &mut State<'a>) { - result.context = In::String { delim: result.ch }; - cache_error_pos(result, ErrorName::UnclosedQuote); + result.context = In::String { delim: result.ch }; + cache_error_pos(result, ErrorName::UnclosedQuote); } fn in_comment_on_quote<'a>(result: &mut State<'a>) { - result.quote_danger = !result.quote_danger; - if result.quote_danger { - cache_error_pos(result, ErrorName::QuoteDanger); - } + result.quote_danger = !result.quote_danger; + if result.quote_danger { + cache_error_pos(result, ErrorName::QuoteDanger); + } } fn in_string_on_quote<'a>(result: &mut State<'a>, delim: &'a str) { - if delim == result.ch { - result.context = In::Code; - } + if delim == result.ch { + result.context = In::Code; + } } fn in_code_on_nsign<'a>(result: &mut State<'a>) { - result.context = In::LispReaderSyntax; + result.context = In::LispReaderSyntax; } fn in_lisp_reader_syntax_on_vline<'a>(result: &mut State<'a>) { - result.context = In::LispBlockComment { depth: 1 }; + result.context = In::LispBlockComment { depth: 1 }; } fn in_lisp_reader_syntax_on_bang<'a>(result: &mut State<'a>) { - result.context = In::GuileBlockComment; + result.context = In::GuileBlockComment; } fn in_lisp_reader_syntax_on_semicolon<'a>(result: &mut State<'a>) { - result.context = In::Code; + result.context = In::Code; } fn in_lisp_block_comment_pre_on_vline<'a>(result: &mut State<'a>, depth: usize) { - result.context = In::LispBlockComment { depth: depth + 1 }; + result.context = In::LispBlockComment { depth: depth + 1 }; } fn in_lisp_block_comment_pre_on_else<'a>(result: &mut State<'a>, depth: usize) { - result.context = In::LispBlockComment { depth }; + result.context = In::LispBlockComment { depth }; } fn in_lisp_block_comment_on_nsign<'a>(result: &mut State<'a>, depth: usize) { - result.context = In::LispBlockCommentPre { depth }; + result.context = In::LispBlockCommentPre { depth }; } fn in_lisp_block_comment_on_vline<'a>(result: &mut State<'a>, depth: usize) { - result.context = In::LispBlockCommentPost { depth }; + result.context = In::LispBlockCommentPost { depth }; } fn in_lisp_block_comment_post_on_nsign<'a>(result: &mut State<'a>, depth: usize) { - let depth = depth - 1; - if depth > 0 { - result.context = In::LispBlockComment { depth }; - } else { - result.context = In::Code; - } + let depth = depth - 1; + if depth > 0 { + result.context = In::LispBlockComment { depth }; + } else { + result.context = In::Code; + } } fn in_lisp_block_comment_post_on_else<'a>(result: &mut State<'a>, depth: usize) { - result.context = In::LispBlockComment { depth }; + result.context = In::LispBlockComment { depth }; } fn in_guile_block_comment_on_bang<'a>(result: &mut State<'a>) { - result.context = In::GuileBlockCommentPost; + result.context = In::GuileBlockCommentPost; } fn in_guile_block_comment_post_on_nsign<'a>(result: &mut State<'a>) { - result.context = In::Code; + result.context = In::Code; } fn in_guile_block_comment_post_on_else<'a>(result: &mut State<'a>) { - result.context = In::GuileBlockComment; + result.context = In::GuileBlockComment; } fn in_code_on_grave<'a>(result: &mut State<'a>) { - result.context = In::JanetLongStringPre { open_delim_len: 1 }; - cache_error_pos(result, ErrorName::UnclosedQuote); + result.context = In::JanetLongStringPre { open_delim_len: 1 }; + cache_error_pos(result, ErrorName::UnclosedQuote); } fn in_janet_long_string_pre_on_grave<'a>(result: &mut State<'a>, open_delim_len: usize) { - result.context = In::JanetLongStringPre { open_delim_len: open_delim_len + 1 }; + result.context = In::JanetLongStringPre { + open_delim_len: open_delim_len + 1, + }; } fn in_janet_long_string_pre_on_else<'a>(result: &mut State<'a>, open_delim_len: usize) { - result.context = In::JanetLongString { open_delim_len, close_delim_len: 0 }; -} -fn in_janet_long_string_on_grave<'a>(result: &mut State<'a>, open_delim_len: usize, close_delim_len: usize) { - let close_delim_len = close_delim_len + 1; - if open_delim_len == close_delim_len { - result.context = In::Code; - } else { - result.context = In::JanetLongString { open_delim_len, close_delim_len }; - } + result.context = In::JanetLongString { + open_delim_len, + close_delim_len: 0, + }; +} +fn in_janet_long_string_on_grave<'a>( + result: &mut State<'a>, + open_delim_len: usize, + close_delim_len: usize, +) { + let close_delim_len = close_delim_len + 1; + if open_delim_len == close_delim_len { + result.context = In::Code; + } else { + result.context = In::JanetLongString { + open_delim_len, + close_delim_len, + }; + } } -fn in_janet_long_string_on_else<'a>(result: &mut State<'a>, open_delim_len: usize, close_delim_len: usize) { - if close_delim_len > 0 { - result.context = In::JanetLongString { open_delim_len, close_delim_len: 0 }; - } +fn in_janet_long_string_on_else<'a>( + result: &mut State<'a>, + open_delim_len: usize, + close_delim_len: usize, +) { + if close_delim_len > 0 { + result.context = In::JanetLongString { + open_delim_len, + close_delim_len: 0, + }; + } } fn on_backslash<'a>(result: &mut State<'a>) { - result.escape = Now::Escaping; + result.escape = Now::Escaping; } fn after_backslash<'a>(result: &mut State<'a>) -> Result<()> { - result.escape = Now::Escaped; + result.escape = Now::Escaped; - if result.ch == NEWLINE { - if result.is_in_code() { - return error(result, ErrorName::EolBackslash); - } + if result.ch == NEWLINE { + if result.is_in_code() { + return error(result, ErrorName::EolBackslash); } + } - Ok(()) + Ok(()) } // {{{1 Character dispatch fn on_context<'a>(result: &mut State<'a>) -> Result<()> { - let ch = result.ch; - match result.context { - In::Code => { - match ch { - _ if ch == result.comment_char => in_code_on_comment_char(result), - _ if result.string_delimiters.contains(&ch.to_string()) => in_code_on_quote(result), - "(" | "[" | "{" => in_code_on_open_paren(result), - ")" | "]" | "}" => in_code_on_close_paren(result)?, - VERTICAL_LINE if result.lisp_vline_symbols_enabled => in_code_on_quote(result), - NUMBER_SIGN if result.lisp_reader_syntax_enabled => in_code_on_nsign(result), - GRAVE if result.janet_long_strings_enabled => in_code_on_grave(result), - TAB => in_code_on_tab(result), - _ => (), - } - }, - In::Comment => { - match ch { - _ if result.string_delimiters.contains(&ch.to_string()) => in_comment_on_quote(result), - VERTICAL_LINE if result.lisp_vline_symbols_enabled => in_comment_on_quote(result), - GRAVE if result.janet_long_strings_enabled => in_comment_on_quote(result), - _ => (), - } - }, - In::String { delim } => { - match ch { - _ if result.string_delimiters.contains(&ch.to_string()) => in_string_on_quote(result, delim), - VERTICAL_LINE if result.lisp_vline_symbols_enabled => in_string_on_quote(result, delim), - _ => (), - } - }, - In::LispReaderSyntax => { - match ch { - VERTICAL_LINE if result.lisp_block_comments_enabled => in_lisp_reader_syntax_on_vline(result), - BANG if result.guile_block_comments_enabled => in_lisp_reader_syntax_on_bang(result), - ";" if result.scheme_sexp_comments_enabled => in_lisp_reader_syntax_on_semicolon(result), - _ => { - // Backtrack! - result.context = In::Code; - on_context(result)? - }, - } - }, - In::LispBlockCommentPre { depth } => { - match ch { - VERTICAL_LINE => in_lisp_block_comment_pre_on_vline(result, depth), - _ => in_lisp_block_comment_pre_on_else(result, depth), - } - }, - In::LispBlockComment { depth } => { - match ch { - NUMBER_SIGN => in_lisp_block_comment_on_nsign(result, depth), - VERTICAL_LINE => in_lisp_block_comment_on_vline(result, depth), - _ => (), - } - }, - In::LispBlockCommentPost { depth } => { - match ch { - NUMBER_SIGN => in_lisp_block_comment_post_on_nsign(result, depth), - _ => in_lisp_block_comment_post_on_else(result, depth), - } - }, - In::GuileBlockComment => { - match ch { - BANG => in_guile_block_comment_on_bang(result), - _ => (), - } - }, - In::GuileBlockCommentPost => { - match ch { - NUMBER_SIGN => in_guile_block_comment_post_on_nsign(result), - _ => in_guile_block_comment_post_on_else(result), - } - }, - In::JanetLongStringPre { open_delim_len } => { - match ch { - GRAVE => in_janet_long_string_pre_on_grave(result, open_delim_len), - _ => in_janet_long_string_pre_on_else(result, open_delim_len), - } - }, - In::JanetLongString { open_delim_len, close_delim_len } => { - match ch { - GRAVE => in_janet_long_string_on_grave(result, open_delim_len, close_delim_len), - _ => in_janet_long_string_on_else(result, open_delim_len, close_delim_len), - } - }, - } + let ch = result.ch; + match result.context { + In::Code => match ch { + _ if ch == result.comment_char => in_code_on_comment_char(result), + _ if result.string_delimiters.contains(&ch.to_string()) => in_code_on_quote(result), + "(" | "[" | "{" => in_code_on_open_paren(result), + ")" | "]" | "}" => in_code_on_close_paren(result)?, + VERTICAL_LINE if result.lisp_vline_symbols_enabled => in_code_on_quote(result), + NUMBER_SIGN if result.lisp_reader_syntax_enabled => in_code_on_nsign(result), + GRAVE if result.janet_long_strings_enabled => in_code_on_grave(result), + TAB => in_code_on_tab(result), + _ => (), + }, + In::Comment => match ch { + _ if result.string_delimiters.contains(&ch.to_string()) => in_comment_on_quote(result), + VERTICAL_LINE if result.lisp_vline_symbols_enabled => in_comment_on_quote(result), + GRAVE if result.janet_long_strings_enabled => in_comment_on_quote(result), + _ => (), + }, + In::String { delim } => match ch { + _ if result.string_delimiters.contains(&ch.to_string()) => in_string_on_quote(result, delim), + VERTICAL_LINE if result.lisp_vline_symbols_enabled => in_string_on_quote(result, delim), + _ => (), + }, + In::LispReaderSyntax => { + match ch { + VERTICAL_LINE if result.lisp_block_comments_enabled => { + in_lisp_reader_syntax_on_vline(result) + } + BANG if result.guile_block_comments_enabled => in_lisp_reader_syntax_on_bang(result), + ";" if result.scheme_sexp_comments_enabled => in_lisp_reader_syntax_on_semicolon(result), + _ => { + // Backtrack! + result.context = In::Code; + on_context(result)? + } + } + } + In::LispBlockCommentPre { depth } => match ch { + VERTICAL_LINE => in_lisp_block_comment_pre_on_vline(result, depth), + _ => in_lisp_block_comment_pre_on_else(result, depth), + }, + In::LispBlockComment { depth } => match ch { + NUMBER_SIGN => in_lisp_block_comment_on_nsign(result, depth), + VERTICAL_LINE => in_lisp_block_comment_on_vline(result, depth), + _ => (), + }, + In::LispBlockCommentPost { depth } => match ch { + NUMBER_SIGN => in_lisp_block_comment_post_on_nsign(result, depth), + _ => in_lisp_block_comment_post_on_else(result, depth), + }, + In::GuileBlockComment => match ch { + BANG => in_guile_block_comment_on_bang(result), + _ => (), + }, + In::GuileBlockCommentPost => match ch { + NUMBER_SIGN => in_guile_block_comment_post_on_nsign(result), + _ => in_guile_block_comment_post_on_else(result), + }, + In::JanetLongStringPre { open_delim_len } => match ch { + GRAVE => in_janet_long_string_pre_on_grave(result, open_delim_len), + _ => in_janet_long_string_pre_on_else(result, open_delim_len), + }, + In::JanetLongString { + open_delim_len, + close_delim_len, + } => match ch { + GRAVE => in_janet_long_string_on_grave(result, open_delim_len, close_delim_len), + _ => in_janet_long_string_on_else(result, open_delim_len, close_delim_len), + }, + } - Ok(()) + Ok(()) } fn on_char<'a>(result: &mut State<'a>) -> Result<()> { - let mut ch = result.ch; - if result.is_escaped() { - result.escape = Now::Normal; - } + let mut ch = result.ch; + if result.is_escaped() { + result.escape = Now::Normal; + } - if result.is_escaping() { - after_backslash(result)?; - } else if ch == BACKSLASH { - on_backslash(result); - } else if ch == NEWLINE { - on_newline(result); - } else { - on_context(result)?; - } + if result.is_escaping() { + after_backslash(result)?; + } else if ch == BACKSLASH { + on_backslash(result); + } else if ch == NEWLINE { + on_newline(result); + } else { + on_context(result)?; + } - ch = result.ch; + ch = result.ch; - if is_closable(result) { - let line_no = result.line_no; - let x = result.x; - reset_paren_trail(result, line_no, x + UnicodeWidthStr::width(ch)); - } + if is_closable(result) { + let line_no = result.line_no; + let x = result.x; + reset_paren_trail(result, line_no, x + UnicodeWidthStr::width(ch)); + } - let state = result.tracking_arg_tab_stop; - if state != TrackingArgTabStop::NotSearching { - track_arg_tab_stop(result, state); - } + let state = result.tracking_arg_tab_stop; + if state != TrackingArgTabStop::NotSearching { + track_arg_tab_stop(result, state); + } - Ok(()) + Ok(()) } // {{{1 Cursor functions fn is_cursor_left_of<'a>( - cursor_x: Option, - cursor_line: Option, - x: Option, - line_no: LineNumber, + cursor_x: Option, + cursor_line: Option, + x: Option, + line_no: LineNumber, ) -> bool { - if let (Some(x), Some(cursor_x)) = (x, cursor_x) { - cursor_line == Some(line_no) && cursor_x <= x // inclusive since (cursorX = x) implies (x-1 < cursor < x) - } else { - false - } + if let (Some(x), Some(cursor_x)) = (x, cursor_x) { + cursor_line == Some(line_no) && cursor_x <= x // inclusive since (cursorX = x) implies (x-1 < cursor < x) + } else { + false + } } fn is_cursor_right_of<'a>( - cursor_x: Option, - cursor_line: Option, - x: Option, - line_no: LineNumber, + cursor_x: Option, + cursor_line: Option, + x: Option, + line_no: LineNumber, ) -> bool { - if let (Some(x), Some(cursor_x)) = (x, cursor_x) { - cursor_line == Some(line_no) && cursor_x > x - } else { - false - } + if let (Some(x), Some(cursor_x)) = (x, cursor_x) { + cursor_line == Some(line_no) && cursor_x > x + } else { + false + } } fn is_cursor_in_comment<'a>( - result: &State<'a>, - cursor_x: Option, - cursor_line: Option, + result: &State<'a>, + cursor_x: Option, + cursor_line: Option, ) -> bool { - is_cursor_right_of(cursor_x, cursor_line, result.comment_x, result.line_no) + is_cursor_right_of(cursor_x, cursor_line, result.comment_x, result.line_no) } fn handle_change_delta<'a>(result: &mut State<'a>) { - if !result.changes.is_empty() && (result.smart || result.mode == Mode::Paren) { - if let Some(change) = result.changes.get(&(result.input_line_no, result.input_x)) { - result.indent_delta += change.new_end_x as Delta - change.old_end_x as Delta; - } + if !result.changes.is_empty() && (result.smart || result.mode == Mode::Paren) { + if let Some(change) = result.changes.get(&(result.input_line_no, result.input_x)) { + result.indent_delta += change.new_end_x as Delta - change.old_end_x as Delta; } + } } // {{{1 Paren Trail functions fn reset_paren_trail<'a>(result: &mut State<'a>, line_no: LineNumber, x: Column) { - result.paren_trail.line_no = Some(line_no); - result.paren_trail.start_x = Some(x); - result.paren_trail.end_x = Some(x); - result.paren_trail.openers = vec![]; - result.paren_trail.clamped.start_x = None; - result.paren_trail.clamped.end_x = None; - result.paren_trail.clamped.openers = vec![]; + result.paren_trail.line_no = Some(line_no); + result.paren_trail.start_x = Some(x); + result.paren_trail.end_x = Some(x); + result.paren_trail.openers = vec![]; + result.paren_trail.clamped.start_x = None; + result.paren_trail.clamped.end_x = None; + result.paren_trail.clamped.openers = vec![]; } fn is_cursor_clamping_paren_trail<'a>( - result: &State<'a>, - cursor_x: Option, - cursor_line: Option, + result: &State<'a>, + cursor_x: Option, + cursor_line: Option, ) -> bool { - is_cursor_right_of( - cursor_x, - cursor_line, - result.paren_trail.start_x, - result.line_no, - ) && !is_cursor_in_comment(result, cursor_x, cursor_line) + is_cursor_right_of( + cursor_x, + cursor_line, + result.paren_trail.start_x, + result.line_no, + ) && !is_cursor_in_comment(result, cursor_x, cursor_line) } // INDENT MODE: allow the cursor to clamp the paren trail fn clamp_paren_trail_to_cursor<'a>(result: &mut State<'a>) { - let clamping = is_cursor_clamping_paren_trail(result, result.cursor_x, result.cursor_line); - if clamping { - let start_x = result.paren_trail.start_x.unwrap(); - let end_x = result.paren_trail.end_x.unwrap(); - - let new_start_x = std::cmp::max(start_x, result.cursor_x.unwrap()); - let new_end_x = std::cmp::max(end_x, result.cursor_x.unwrap()); - - let line = &result.lines[result.line_no]; - let mut remove_count = 0; - for (x, ch) in line - .graphemes(true) - .scan(0, |column, ch| { - let start_column = *column; - *column = *column + UnicodeWidthStr::width(ch); - Some((start_column, ch)) - }) - { - if x < start_x || x >= new_start_x { - continue; - } - if is_close_paren(ch) { - remove_count += 1; - } - } - - let openers = result.paren_trail.openers.clone(); - - result.paren_trail.openers = (&openers[remove_count..]).to_vec(); - result.paren_trail.start_x = Some(new_start_x); - result.paren_trail.end_x = Some(new_end_x); + let clamping = is_cursor_clamping_paren_trail(result, result.cursor_x, result.cursor_line); + if clamping { + let start_x = result.paren_trail.start_x.unwrap(); + let end_x = result.paren_trail.end_x.unwrap(); - result.paren_trail.clamped.openers = (&openers[..remove_count]).to_vec(); - result.paren_trail.clamped.start_x = Some(start_x); - result.paren_trail.clamped.end_x = Some(end_x); - } + let new_start_x = std::cmp::max(start_x, result.cursor_x.unwrap()); + let new_end_x = std::cmp::max(end_x, result.cursor_x.unwrap()); + + let line = &result.lines[result.line_no]; + let mut remove_count = 0; + for (x, ch) in line.graphemes(true).scan(0, |column, ch| { + let start_column = *column; + *column = *column + UnicodeWidthStr::width(ch); + Some((start_column, ch)) + }) { + if x < start_x || x >= new_start_x { + continue; + } + if is_close_paren(ch) { + remove_count += 1; + } + } + + let openers = result.paren_trail.openers.clone(); + + result.paren_trail.openers = (&openers[remove_count..]).to_vec(); + result.paren_trail.start_x = Some(new_start_x); + result.paren_trail.end_x = Some(new_end_x); + + result.paren_trail.clamped.openers = (&openers[..remove_count]).to_vec(); + result.paren_trail.clamped.start_x = Some(start_x); + result.paren_trail.clamped.end_x = Some(end_x); + } } fn pop_paren_trail<'a>(result: &mut State<'a>) { - let start_x = result.paren_trail.start_x; - let end_x = result.paren_trail.end_x; + let start_x = result.paren_trail.start_x; + let end_x = result.paren_trail.end_x; - if start_x == end_x { - return; - } + if start_x == end_x { + return; + } - while let Some(paren) = result.paren_trail.openers.pop() { - result.paren_stack.push(paren); - } + while let Some(paren) = result.paren_trail.openers.pop() { + result.paren_stack.push(paren); + } } fn get_parent_opener_index<'a>(result: &mut State<'a>, indent_x: usize) -> usize { - for i in 0..result.paren_stack.len() { - let opener = peek(&result.paren_stack, i).unwrap().clone(); - let opener_index = result.paren_stack.len() - i - 1; - - let curr_outside = opener.x < indent_x; - - let prev_indent_x = indent_x as Delta - result.indent_delta; - let prev_outside = opener.x as Delta - opener.indent_delta < prev_indent_x; - - let mut is_parent = false; - - if prev_outside && curr_outside { + for i in 0..result.paren_stack.len() { + let opener = peek(&result.paren_stack, i).unwrap().clone(); + let opener_index = result.paren_stack.len() - i - 1; + + let curr_outside = opener.x < indent_x; + + let prev_indent_x = indent_x as Delta - result.indent_delta; + let prev_outside = opener.x as Delta - opener.indent_delta < prev_indent_x; + + let mut is_parent = false; + + if prev_outside && curr_outside { + is_parent = true; + } else if !prev_outside && !curr_outside { + is_parent = false; + } else if prev_outside && !curr_outside { + // POSSIBLE FRAGMENTATION + // (foo --\ + // +--- FRAGMENT `(foo bar)` => `(foo) bar` + // bar) --/ + + // 1. PREVENT FRAGMENTATION + // ```in + // (foo + // ++ + // bar + // ``` + // ```out + // (foo + // bar + // ``` + if result.indent_delta == 0 { + is_parent = true; + } + // 2. ALLOW FRAGMENTATION + // ```in + // (foo + // bar + // -- + // ``` + // ```out + // (foo) + // bar + // ``` + else if opener.indent_delta == 0 { + is_parent = false; + } else { + // TODO: identify legitimate cases where both are nonzero + + // allow the fragmentation by default + is_parent = false; + + // TODO: should we throw to exit instead? either of: + // 1. give up, just `throw error(...)` + // 2. fallback to paren mode to preserve structure + } + } else if !prev_outside && curr_outside { + // POSSIBLE ADOPTION + // (foo) --\ + // +--- ADOPT `(foo) bar` => `(foo bar)` + // bar --/ + + { + let next_opener = peek(&result.paren_stack, i + 1); + + // 1. DISALLOW ADOPTION + // ```in + // (foo + // -- + // (bar) + // -- + // baz) + // ``` + // ```out + // (foo + // (bar) + // baz) + // ``` + // OR + // ```in + // (foo + // -- + // (bar) + // - + // baz) + // ``` + // ```out + // (foo + // (bar) + // baz) + // ``` + if next_opener + .map(|no| no.indent_delta <= opener.indent_delta) + .unwrap_or(false) + { + // we can only disallow adoption if nextOpener.indentDelta will actually + // prevent the indentX from being in the opener's threshold. + if indent_x as Delta + next_opener.unwrap().indent_delta > opener.x as Delta { is_parent = true; - } else if !prev_outside && !curr_outside { + } else { is_parent = false; - } else if prev_outside && !curr_outside { - // POSSIBLE FRAGMENTATION - // (foo --\ - // +--- FRAGMENT `(foo bar)` => `(foo) bar` - // bar) --/ - - // 1. PREVENT FRAGMENTATION - // ```in - // (foo - // ++ - // bar - // ``` - // ```out - // (foo - // bar - // ``` - if result.indent_delta == 0 { - is_parent = true; - } - // 2. ALLOW FRAGMENTATION - // ```in - // (foo - // bar - // -- - // ``` - // ```out - // (foo) - // bar - // ``` - else if opener.indent_delta == 0 { - is_parent = false; - } else { - // TODO: identify legitimate cases where both are nonzero - - // allow the fragmentation by default - is_parent = false; - - // TODO: should we throw to exit instead? either of: - // 1. give up, just `throw error(...)` - // 2. fallback to paren mode to preserve structure - } - } else if !prev_outside && curr_outside { - // POSSIBLE ADOPTION - // (foo) --\ - // +--- ADOPT `(foo) bar` => `(foo bar)` - // bar --/ - - { - let next_opener = peek(&result.paren_stack, i + 1); - - // 1. DISALLOW ADOPTION - // ```in - // (foo - // -- - // (bar) - // -- - // baz) - // ``` - // ```out - // (foo - // (bar) - // baz) - // ``` - // OR - // ```in - // (foo - // -- - // (bar) - // - - // baz) - // ``` - // ```out - // (foo - // (bar) - // baz) - // ``` - if next_opener - .map(|no| no.indent_delta <= opener.indent_delta) - .unwrap_or(false) - { - // we can only disallow adoption if nextOpener.indentDelta will actually - // prevent the indentX from being in the opener's threshold. - if indent_x as Delta + next_opener.unwrap().indent_delta > opener.x as Delta { - is_parent = true; - } else { - is_parent = false; - } - } - // 2. ALLOW ADOPTION - // ```in - // (foo - // (bar) - // -- - // baz) - // ``` - // ```out - // (foo - // (bar - // baz)) - // ``` - // OR - // ```in - // (foo - // - - // (bar) - // -- - // baz) - // ``` - // ```out - // (foo - // (bar) - // baz) - // ``` - else if next_opener - .map(|no| no.indent_delta > opener.indent_delta) - .unwrap_or(false) - { - is_parent = true; - } - // 3. ALLOW ADOPTION - // ```in - // (foo) - // -- - // bar - // ``` - // ```out - // (foo - // bar) - // ``` - // OR - // ```in - // (foo) - // bar - // ++ - // ``` - // ```out - // (foo - // bar - // ``` - // OR - // ```in - // (foo) - // + - // bar - // ++ - // ``` - // ```out - // (foo - // bar) - // ``` - else if result.indent_delta > opener.indent_delta { - is_parent = true; - } - } - - if is_parent { - // if new parent - // Clear `indentDelta` since it is reserved for previous child lines only. - result.paren_stack[opener_index].indent_delta = 0; - } + } } - - if is_parent { - return i; + // 2. ALLOW ADOPTION + // ```in + // (foo + // (bar) + // -- + // baz) + // ``` + // ```out + // (foo + // (bar + // baz)) + // ``` + // OR + // ```in + // (foo + // - + // (bar) + // -- + // baz) + // ``` + // ```out + // (foo + // (bar) + // baz) + // ``` + else if next_opener + .map(|no| no.indent_delta > opener.indent_delta) + .unwrap_or(false) + { + is_parent = true; } + // 3. ALLOW ADOPTION + // ```in + // (foo) + // -- + // bar + // ``` + // ```out + // (foo + // bar) + // ``` + // OR + // ```in + // (foo) + // bar + // ++ + // ``` + // ```out + // (foo + // bar + // ``` + // OR + // ```in + // (foo) + // + + // bar + // ++ + // ``` + // ```out + // (foo + // bar) + // ``` + else if result.indent_delta > opener.indent_delta { + is_parent = true; + } + } + + if is_parent { + // if new parent + // Clear `indentDelta` since it is reserved for previous child lines only. + result.paren_stack[opener_index].indent_delta = 0; + } + } + + if is_parent { + return i; } + } - result.paren_stack.len() + result.paren_stack.len() } // INDENT MODE: correct paren trail from indentation fn correct_paren_trail<'a>(result: &mut State<'a>, indent_x: usize) { - let mut parens = String::new(); - - let index = get_parent_opener_index(result, indent_x); - for i in 0..index { - let mut opener = result.paren_stack.pop().unwrap(); - let close_ch = match_paren(opener.ch).unwrap(); - if result.return_parens { - opener.closer = Some(Closer { - line_no: result.paren_trail.line_no.unwrap(), - x: result.paren_trail.start_x.unwrap() + i, - ch: close_ch, - trail: None - }); - } - result.paren_trail.openers.push(opener); - parens.push_str(close_ch); + let mut parens = String::new(); + let index = get_parent_opener_index(result, indent_x); + for i in 0..index { + let mut opener = result.paren_stack.pop().unwrap(); + let close_ch = match_paren(opener.ch).unwrap(); + if result.return_parens { + opener.closer = Some(Closer { + line_no: result.paren_trail.line_no.unwrap(), + x: result.paren_trail.start_x.unwrap() + i, + ch: close_ch, + trail: None, + }); } + result.paren_trail.openers.push(opener); + parens.push_str(close_ch); + } - if let Some(line_no) = result.paren_trail.line_no { - let start_x = result.paren_trail.start_x.unwrap(); - let end_x = result.paren_trail.end_x.unwrap(); - replace_within_line(result, line_no, start_x, end_x, &parens[..]); - result.paren_trail.end_x = result.paren_trail.start_x.map(|x| x + parens.len()); - remember_paren_trail(result); - } + if let Some(line_no) = result.paren_trail.line_no { + let start_x = result.paren_trail.start_x.unwrap(); + let end_x = result.paren_trail.end_x.unwrap(); + replace_within_line(result, line_no, start_x, end_x, &parens[..]); + result.paren_trail.end_x = result.paren_trail.start_x.map(|x| x + parens.len()); + remember_paren_trail(result); + } } fn clean_paren_trail<'a>(result: &mut State<'a>) { - let start_x = result.paren_trail.start_x; - let end_x = result.paren_trail.end_x; - - if start_x == end_x || Some(result.line_no) != result.paren_trail.line_no { - return; - } + let start_x = result.paren_trail.start_x; + let end_x = result.paren_trail.end_x; - let start_x = start_x.unwrap(); - let end_x = end_x.unwrap(); - - let mut new_trail = String::new(); - let mut space_count = 0; - for (x, ch) in result.lines[result.line_no] - .graphemes(true) - .scan(0, |column, ch| { - let start_column = *column; - *column = *column + UnicodeWidthStr::width(ch); - Some((start_column, ch)) - }) - { - if x < start_x || x >= end_x { - continue; - } + if start_x == end_x || Some(result.line_no) != result.paren_trail.line_no { + return; + } - if is_close_paren(ch) { - new_trail.push_str(ch); - } else { - space_count += 1; - } + let start_x = start_x.unwrap(); + let end_x = end_x.unwrap(); + + let mut new_trail = String::new(); + let mut space_count = 0; + for (x, ch) in result.lines[result.line_no] + .graphemes(true) + .scan(0, |column, ch| { + let start_column = *column; + *column = *column + UnicodeWidthStr::width(ch); + Some((start_column, ch)) + }) + { + if x < start_x || x >= end_x { + continue; } - if space_count > 0 { - let line_no = result.line_no; - replace_within_line(result, line_no, start_x, end_x, &new_trail[..]); - result.paren_trail.end_x = result.paren_trail.end_x.map(|x| x - space_count); + if is_close_paren(ch) { + new_trail.push_str(ch); + } else { + space_count += 1; } + } + + if space_count > 0 { + let line_no = result.line_no; + replace_within_line(result, line_no, start_x, end_x, &new_trail[..]); + result.paren_trail.end_x = result.paren_trail.end_x.map(|x| x - space_count); + } } fn set_closer<'a>(opener: &mut Paren<'a>, line_no: LineNumber, x: Column, ch: &'a str) { - opener.closer = Some(Closer { line_no, x, ch, trail: None }) + opener.closer = Some(Closer { + line_no, + x, + ch, + trail: None, + }) } fn append_paren_trail<'a>(result: &mut State<'a>) { - let mut opener = result.paren_stack.pop().unwrap().clone(); - let close_ch = match_paren(opener.ch).unwrap(); - if result.return_parens { - set_closer(&mut opener, result.paren_trail.line_no.unwrap(), result.paren_trail.end_x.unwrap(), close_ch); - } + let mut opener = result.paren_stack.pop().unwrap().clone(); + let close_ch = match_paren(opener.ch).unwrap(); + if result.return_parens { + set_closer( + &mut opener, + result.paren_trail.line_no.unwrap(), + result.paren_trail.end_x.unwrap(), + close_ch, + ); + } - set_max_indent(result, &opener); - let line_no = result.paren_trail.line_no.unwrap(); - let end_x = result.paren_trail.end_x.unwrap(); - insert_within_line(result, line_no, end_x, close_ch); + set_max_indent(result, &opener); + let line_no = result.paren_trail.line_no.unwrap(); + let end_x = result.paren_trail.end_x.unwrap(); + insert_within_line(result, line_no, end_x, close_ch); - result.paren_trail.end_x = result.paren_trail.end_x.map(|x| x + 1); - result.paren_trail.openers.push(opener); - update_remembered_paren_trail(result); + result.paren_trail.end_x = result.paren_trail.end_x.map(|x| x + 1); + result.paren_trail.openers.push(opener); + update_remembered_paren_trail(result); } fn invalidate_paren_trail<'a>(result: &mut State<'a>) { - result.paren_trail = initial_paren_trail(); + result.paren_trail = initial_paren_trail(); } fn check_unmatched_outside_paren_trail<'a>(result: &mut State<'a>) -> Result<()> { - let mut do_error = false; - if let Some(cache) = result.error_pos_cache.get(&ErrorName::UnmatchedCloseParen) { - if result - .paren_trail - .start_x - .map(|x| cache.x < x) - .unwrap_or(false) - { - do_error = true; - } + let mut do_error = false; + if let Some(cache) = result.error_pos_cache.get(&ErrorName::UnmatchedCloseParen) { + if result + .paren_trail + .start_x + .map(|x| cache.x < x) + .unwrap_or(false) + { + do_error = true; } + } - if do_error { - error(result, ErrorName::UnmatchedCloseParen)?; - } + if do_error { + error(result, ErrorName::UnmatchedCloseParen)?; + } - Ok(()) + Ok(()) } fn set_max_indent<'a>(result: &mut State<'a>, opener: &Paren<'a>) { - if let Some(parent) = result.paren_stack.last_mut() { - parent.max_child_indent = Some(opener.x); - } else { - result.max_indent = Some(opener.x); - } + if let Some(parent) = result.paren_stack.last_mut() { + parent.max_child_indent = Some(opener.x); + } else { + result.max_indent = Some(opener.x); + } } fn remember_paren_trail<'a>(result: &mut State<'a>) { - if result.paren_trail.clamped.openers.len() > 0 || result.paren_trail.openers.len() > 0 { - let is_clamped = result.paren_trail.clamped.start_x != None; - let short_trail = ParenTrail { - line_no: result.paren_trail.line_no.unwrap(), - start_x: if is_clamped { - result.paren_trail.clamped.start_x - } else { - result.paren_trail.start_x - }.unwrap(), - end_x: if is_clamped { - result.paren_trail.clamped.end_x - } else { - result.paren_trail.end_x - }.unwrap(), - }; - - result.paren_trails.push(short_trail.clone()); - - if result.return_parens { - for opener in result.paren_trail.openers.iter_mut() { - opener.closer.as_mut().unwrap().trail = Some(short_trail.clone()); - } - } + if result.paren_trail.clamped.openers.len() > 0 || result.paren_trail.openers.len() > 0 { + let is_clamped = result.paren_trail.clamped.start_x != None; + let short_trail = ParenTrail { + line_no: result.paren_trail.line_no.unwrap(), + start_x: if is_clamped { + result.paren_trail.clamped.start_x + } else { + result.paren_trail.start_x + } + .unwrap(), + end_x: if is_clamped { + result.paren_trail.clamped.end_x + } else { + result.paren_trail.end_x + } + .unwrap(), + }; + + result.paren_trails.push(short_trail.clone()); + + if result.return_parens { + for opener in result.paren_trail.openers.iter_mut() { + opener.closer.as_mut().unwrap().trail = Some(short_trail.clone()); + } } + } } fn update_remembered_paren_trail<'a>(result: &mut State<'a>) { - if result.paren_trails.is_empty() - || Some(result.paren_trails[result.paren_trails.len() - 1].line_no) - != result.paren_trail.line_no - { - remember_paren_trail(result); - } else { - let n = result.paren_trails.len() - 1; - let trail = result.paren_trails.get_mut(n).unwrap(); - trail.end_x = result.paren_trail.end_x.unwrap(); - if result.return_parens { - if let Some(opener) = result.paren_trail.openers.last_mut() { - opener.closer.as_mut().unwrap().trail = Some(trail.clone()); - } - } + if result.paren_trails.is_empty() + || Some(result.paren_trails[result.paren_trails.len() - 1].line_no) + != result.paren_trail.line_no + { + remember_paren_trail(result); + } else { + let n = result.paren_trails.len() - 1; + let trail = result.paren_trails.get_mut(n).unwrap(); + trail.end_x = result.paren_trail.end_x.unwrap(); + if result.return_parens { + if let Some(opener) = result.paren_trail.openers.last_mut() { + opener.closer.as_mut().unwrap().trail = Some(trail.clone()); + } } + } } fn finish_new_paren_trail<'a>(result: &mut State<'a>) { - if result.is_in_stringish() { - invalidate_paren_trail(result); - } else if result.mode == Mode::Indent { - clamp_paren_trail_to_cursor(result); - pop_paren_trail(result); - } else if result.mode == Mode::Paren { - if let Some(paren) = peek(&result.paren_trail.openers, 0).map(Clone::clone) { - set_max_indent(result, &paren); - } - if Some(result.line_no) != result.cursor_line { - clean_paren_trail(result); - } - remember_paren_trail(result); - } + if result.is_in_stringish() { + invalidate_paren_trail(result); + } else if result.mode == Mode::Indent { + clamp_paren_trail_to_cursor(result); + pop_paren_trail(result); + } else if result.mode == Mode::Paren { + if let Some(paren) = peek(&result.paren_trail.openers, 0).map(Clone::clone) { + set_max_indent(result, &paren); + } + if Some(result.line_no) != result.cursor_line { + clean_paren_trail(result); + } + remember_paren_trail(result); + } } // {{{1 Indentation functions fn add_indent<'a>(result: &mut State<'a>, delta: Delta) { - let orig_indent = result.x; - let new_indent = delta_to_column(orig_indent as Delta + delta); - let indent_str = repeat_string(BLANK_SPACE, new_indent); - let line_no = result.line_no; - replace_within_line(result, line_no, 0, orig_indent, &indent_str); - result.x = new_indent; - result.indent_x = Some(new_indent); - result.indent_delta += delta; + let orig_indent = result.x; + let new_indent = orig_indent.saturating_add_signed( + delta + .try_into() + .expect("Delta (i64) has values that fit in a usize"), + ); + let indent_str = repeat_string(BLANK_SPACE, new_indent); + let line_no = result.line_no; + replace_within_line(result, line_no, 0, orig_indent, &indent_str); + result.x = new_indent; + result.indent_x = Some(new_indent); + result.indent_delta += delta; } fn should_add_opener_indent<'a>(result: &State<'a>, opener: &Paren<'a>) -> bool { - // Don't add opener.indent_delta if the user already added it. - // (happens when multiple lines are indented together) - opener.indent_delta != result.indent_delta + // Don't add opener.indent_delta if the user already added it. + // (happens when multiple lines are indented together) + opener.indent_delta != result.indent_delta } fn correct_indent<'a>(result: &mut State<'a>) { - let orig_indent = result.x as Delta; - let mut new_indent = orig_indent as Delta; - let mut min_indent = 0; - let mut max_indent = result.max_indent.map(|x| x as Delta); + let orig_indent = result.x as Delta; + let mut new_indent = orig_indent as Delta; + let mut min_indent = 0; + let mut max_indent = result.max_indent.map(|x| x as Delta); - if let Some(opener) = peek(&result.paren_stack, 0) { - min_indent = opener.x + 1; - max_indent = opener.max_child_indent.map(|x| x as Delta); - if should_add_opener_indent(result, opener) { - new_indent += opener.indent_delta; - } + if let Some(opener) = peek(&result.paren_stack, 0) { + min_indent = opener.x + 1; + max_indent = opener.max_child_indent.map(|x| x as Delta); + if should_add_opener_indent(result, opener) { + new_indent += opener.indent_delta; } + } - new_indent = clamp(new_indent, Some(min_indent as Delta), max_indent); + new_indent = clamp(new_indent, Some(min_indent as Delta), max_indent); - if new_indent != orig_indent { - add_indent(result, new_indent - orig_indent); - } + if new_indent != orig_indent { + add_indent(result, new_indent - orig_indent); + } } fn on_indent<'a>(result: &mut State<'a>) -> Result<()> { - result.indent_x = Some(result.x); - result.tracking_indent = false; + result.indent_x = Some(result.x); + result.tracking_indent = false; - if result.quote_danger { - error(result, ErrorName::QuoteDanger)?; - } + if result.quote_danger { + error(result, ErrorName::QuoteDanger)?; + } - match result.mode { - Mode::Indent => { - let x = result.x; - correct_paren_trail(result, x); - - let to_add = match peek(&result.paren_stack, 0) { - Some(opener) if should_add_opener_indent(result, opener) => { - Some(opener.indent_delta) - } - _ => None, - }; - - if let Some(adjust) = to_add { - add_indent(result, adjust); - } - } - Mode::Paren => correct_indent(result), + match result.mode { + Mode::Indent => { + let x = result.x; + correct_paren_trail(result, x); + + let to_add = match peek(&result.paren_stack, 0) { + Some(opener) if should_add_opener_indent(result, opener) => Some(opener.indent_delta), + _ => None, + }; + + if let Some(adjust) = to_add { + add_indent(result, adjust); + } } + Mode::Paren => correct_indent(result), + } - Ok(()) + Ok(()) } fn check_leading_close_paren<'a>(result: &mut State<'a>) -> Result<()> { - if result - .error_pos_cache - .contains_key(&ErrorName::LeadingCloseParen) - && result.paren_trail.line_no == Some(result.line_no) - { - error(result, ErrorName::LeadingCloseParen)?; - } + if result + .error_pos_cache + .contains_key(&ErrorName::LeadingCloseParen) + && result.paren_trail.line_no == Some(result.line_no) + { + error(result, ErrorName::LeadingCloseParen)?; + } - Ok(()) + Ok(()) } fn on_leading_close_paren<'a>(result: &mut State<'a>) -> Result<()> { - match result.mode { - Mode::Indent => { - if !result.force_balance { - if result.smart { - error(result, ErrorName::Restart)?; - } - if !result - .error_pos_cache - .contains_key(&ErrorName::LeadingCloseParen) - { - cache_error_pos(result, ErrorName::LeadingCloseParen); - } - } - result.skip_char = true; + match result.mode { + Mode::Indent => { + if !result.force_balance { + if result.smart { + error(result, ErrorName::Restart)?; } - Mode::Paren => { - if !is_valid_close_paren(&result.paren_stack, result.ch) { - if result.smart { - result.skip_char = true; - } else { - error(result, ErrorName::UnmatchedCloseParen)?; - } - } else if is_cursor_left_of( - result.cursor_x, - result.cursor_line, - Some(result.x), - result.line_no, - ) { - let line_no = result.line_no; - let x = result.x; - reset_paren_trail(result, line_no, x); - on_indent(result)?; - } else { - append_paren_trail(result); - result.skip_char = true; - } + if !result + .error_pos_cache + .contains_key(&ErrorName::LeadingCloseParen) + { + cache_error_pos(result, ErrorName::LeadingCloseParen); + } + } + result.skip_char = true; + } + Mode::Paren => { + if !is_valid_close_paren(&result.paren_stack, result.ch) { + if result.smart { + result.skip_char = true; + } else { + error(result, ErrorName::UnmatchedCloseParen)?; } + } else if is_cursor_left_of( + result.cursor_x, + result.cursor_line, + Some(result.x), + result.line_no, + ) { + let line_no = result.line_no; + let x = result.x; + reset_paren_trail(result, line_no, x); + on_indent(result)?; + } else { + append_paren_trail(result); + result.skip_char = true; + } } + } - Ok(()) + Ok(()) } fn on_comment_line<'a>(result: &mut State<'a>) { - let paren_trail_length = result.paren_trail.openers.len(); - - // restore the openers matching the previous paren trail - if let Mode::Paren = result.mode { - for j in 0..paren_trail_length { - if let Some(opener) = peek(&result.paren_trail.openers, j) { - result.paren_stack.push(opener.clone()); - } - } - }; - - let x = result.x; - let i = get_parent_opener_index(result, x); - let mut indent_to_add: Delta = 0; - if let Some(opener) = peek(&result.paren_stack, i) { - // shift the comment line based on the parent open paren - if should_add_opener_indent(result, opener) { - indent_to_add = opener.indent_delta; - } - // TODO: store some information here if we need to place close-parens after comment lines - } - if indent_to_add != 0 { - add_indent(result, indent_to_add); - } + let paren_trail_length = result.paren_trail.openers.len(); + + // restore the openers matching the previous paren trail + if let Mode::Paren = result.mode { + for j in 0..paren_trail_length { + if let Some(opener) = peek(&result.paren_trail.openers, j) { + result.paren_stack.push(opener.clone()); + } + } + }; + + let x = result.x; + let i = get_parent_opener_index(result, x); + let mut indent_to_add: Delta = 0; + if let Some(opener) = peek(&result.paren_stack, i) { + // shift the comment line based on the parent open paren + if should_add_opener_indent(result, opener) { + indent_to_add = opener.indent_delta; + } + // TODO: store some information here if we need to place close-parens after comment lines + } + if indent_to_add != 0 { + add_indent(result, indent_to_add); + } - // repop the openers matching the previous paren trail - if let Mode::Paren = result.mode { - for _ in 0..paren_trail_length { - result.paren_stack.pop(); - } + // repop the openers matching the previous paren trail + if let Mode::Paren = result.mode { + for _ in 0..paren_trail_length { + result.paren_stack.pop(); } + } } fn check_indent<'a>(result: &mut State<'a>) -> Result<()> { - if is_close_paren(result.ch) { - on_leading_close_paren(result)?; - } else if result.ch == result.comment_char { - // comments don't count as indentation points - on_comment_line(result); - result.tracking_indent = false; - } else if result.ch != NEWLINE && result.ch != BLANK_SPACE && result.ch != TAB { - on_indent(result)?; - } + if is_close_paren(result.ch) { + on_leading_close_paren(result)?; + } else if result.ch == result.comment_char { + // comments don't count as indentation points + on_comment_line(result); + result.tracking_indent = false; + } else if result.ch != NEWLINE && result.ch != BLANK_SPACE && result.ch != TAB { + on_indent(result)?; + } - Ok(()) + Ok(()) } fn make_tab_stop<'a>(opener: &Paren<'a>) -> TabStop<'a> { - TabStop { - ch: opener.ch, - x: opener.x, - line_no: opener.line_no, - arg_x: opener.arg_x, - } + TabStop { + ch: opener.ch, + x: opener.x, + line_no: opener.line_no, + arg_x: opener.arg_x, + } } fn get_tab_stop_line<'a>(result: &State<'a>) -> Option { - result.selection_start_line.or(result.cursor_line) + result.selection_start_line.or(result.cursor_line) } fn set_tab_stops<'a>(result: &mut State<'a>) { - if get_tab_stop_line(result) != Some(result.line_no) { - return; - } - - result.tab_stops = result.paren_stack.iter().map(make_tab_stop).collect(); + if get_tab_stop_line(result) != Some(result.line_no) { + return; + } - if result.mode == Mode::Paren { - let paren_trail_tabs: Vec<_> = result - .paren_trail - .openers - .iter() - .rev() - .map(make_tab_stop) - .collect(); - result.tab_stops.extend(paren_trail_tabs); - } + result.tab_stops = result.paren_stack.iter().map(make_tab_stop).collect(); + + if result.mode == Mode::Paren { + let paren_trail_tabs: Vec<_> = result + .paren_trail + .openers + .iter() + .rev() + .map(make_tab_stop) + .collect(); + result.tab_stops.extend(paren_trail_tabs); + } - // remove argX if it falls to the right of the next stop - for i in 1..result.tab_stops.len() { - let x = result.tab_stops[i].x; - if let Some(prev_arg_x) = result.tab_stops[i - 1].arg_x { - if prev_arg_x >= x { - result.tab_stops[i - 1].arg_x = None; - } - } + // remove argX if it falls to the right of the next stop + for i in 1..result.tab_stops.len() { + let x = result.tab_stops[i].x; + if let Some(prev_arg_x) = result.tab_stops[i - 1].arg_x { + if prev_arg_x >= x { + result.tab_stops[i - 1].arg_x = None; + } } + } } // {{{1 High-level processing functions fn process_char<'a>(result: &mut State<'a>, ch: &'a str) -> Result<()> { - let orig_ch = ch; + let orig_ch = ch; - result.ch = ch; - result.skip_char = false; + result.ch = ch; + result.skip_char = false; - handle_change_delta(result); + handle_change_delta(result); - if result.tracking_indent { - check_indent(result)?; - } + if result.tracking_indent { + check_indent(result)?; + } - if result.skip_char { - result.ch = ""; - } else { - on_char(result)?; - } + if result.skip_char { + result.ch = ""; + } else { + on_char(result)?; + } - commit_char(result, orig_ch); + commit_char(result, orig_ch); - Ok(()) + Ok(()) } fn process_line<'a>(result: &mut State<'a>, line_no: usize) -> Result<()> { - init_line(result); - result.lines.push(Cow::from(result.input_lines[line_no])); + init_line(result); + result.lines.push(Cow::from(result.input_lines[line_no])); - set_tab_stops(result); + set_tab_stops(result); - for (x, ch) in result.input_lines[line_no] - .graphemes(true) - .scan(0, |column, ch| { - let start_column = *column; - *column = *column + UnicodeWidthStr::width(ch); - Some((start_column, ch)) - }) - { - result.input_x = x; - process_char(result, ch)?; - } - process_char(result, NEWLINE)?; + for (x, ch) in result.input_lines[line_no] + .graphemes(true) + .scan(0, |column, ch| { + let start_column = *column; + *column = *column + UnicodeWidthStr::width(ch); + Some((start_column, ch)) + }) + { + result.input_x = x; + process_char(result, ch)?; + } + process_char(result, NEWLINE)?; - if !result.force_balance { - check_unmatched_outside_paren_trail(result)?; - check_leading_close_paren(result)?; - } + if !result.force_balance { + check_unmatched_outside_paren_trail(result)?; + check_leading_close_paren(result)?; + } - if Some(result.line_no) == result.paren_trail.line_no { - finish_new_paren_trail(result); - } + if Some(result.line_no) == result.paren_trail.line_no { + finish_new_paren_trail(result); + } - Ok(()) + Ok(()) } fn finalize_result<'a>(result: &mut State<'a>) -> Result<()> { - if result.quote_danger { - error(result, ErrorName::QuoteDanger)?; - } - if result.is_in_stringish() { - error(result, ErrorName::UnclosedQuote)?; - } + if result.quote_danger { + error(result, ErrorName::QuoteDanger)?; + } + if result.is_in_stringish() { + error(result, ErrorName::UnclosedQuote)?; + } - if result.paren_stack.len() != 0 { - if result.mode == Mode::Paren { - error(result, ErrorName::UnclosedParen)?; - } - } - if result.mode == Mode::Indent { - init_line(result); - on_indent(result)?; + if result.paren_stack.len() != 0 { + if result.mode == Mode::Paren { + error(result, ErrorName::UnclosedParen)?; } - result.success = true; + } + if result.mode == Mode::Indent { + init_line(result); + on_indent(result)?; + } + result.success = true; - Ok(()) + Ok(()) } fn process_error<'a>(result: &mut State<'a>, e: Error) { - result.success = false; - result.error = Some(e); + result.success = false; + result.error = Some(e); } fn process_text<'a>(text: &'a str, options: &Options, mode: Mode, smart: bool) -> State<'a> { - let mut result = get_initial_result(text, &options, mode, smart); - - let mut process_result: Result<()> = Ok(()); - for i in 0..result.input_lines.len() { - result.input_line_no = i; - process_result = process_line(&mut result, i); - if let Err(_) = process_result { - break; - } - } + let mut result = get_initial_result(text, &options, mode, smart); - if let Ok(_) = process_result { - process_result = finalize_result(&mut result); + let mut process_result: Result<()> = Ok(()); + for i in 0..result.input_lines.len() { + result.input_line_no = i; + process_result = process_line(&mut result, i); + if let Err(_) = process_result { + break; } + } - match process_result { - Err(Error { - name: ErrorName::Restart, - .. - }) => process_text(text, &options, Mode::Paren, smart), - Err(e) => { - process_error(&mut result, e); - result - } - _ => result, + if let Ok(_) = process_result { + process_result = finalize_result(&mut result); + } + + match process_result { + Err(Error { + name: ErrorName::Restart, + .. + }) => process_text(text, &options, Mode::Paren, smart), + Err(e) => { + process_error(&mut result, e); + result } + _ => result, + } } // {{{1 Public API fn public_result<'a>(result: State<'a>) -> Answer<'a> { - let line_ending = get_line_ending(result.orig_text); - if result.success { - Answer { - text: Cow::from(result.lines.join(line_ending)), - cursor_x: result.cursor_x, - cursor_line: result.cursor_line, - success: true, - tab_stops: result.tab_stops, - paren_trails: result.paren_trails, - parens: result.parens, - error: None, - } - } else { - Answer { - text: if result.partial_result { - Cow::from(result.lines.join(line_ending)) - } else { - Cow::from(result.orig_text) - }, - cursor_x: if result.partial_result { - result.cursor_x - } else { - result.orig_cursor_x - }, - cursor_line: if result.partial_result { - result.cursor_line - } else { - result.orig_cursor_line - }, - paren_trails: result.paren_trails, - success: false, - tab_stops: result.tab_stops, - error: result.error, - parens: result.parens, - } + let line_ending = get_line_ending(result.orig_text); + if result.success { + Answer { + text: Cow::from(result.lines.join(line_ending)), + cursor_x: result.cursor_x, + cursor_line: result.cursor_line, + success: true, + tab_stops: result.tab_stops, + paren_trails: result.paren_trails, + parens: result.parens, + error: None, + } + } else { + Answer { + text: if result.partial_result { + Cow::from(result.lines.join(line_ending)) + } else { + Cow::from(result.orig_text) + }, + cursor_x: if result.partial_result { + result.cursor_x + } else { + result.orig_cursor_x + }, + cursor_line: if result.partial_result { + result.cursor_line + } else { + result.orig_cursor_line + }, + paren_trails: result.paren_trails, + success: false, + tab_stops: result.tab_stops, + error: result.error, + parens: result.parens, } + } } pub fn indent_mode<'a>(text: &'a str, options: &Options) -> Answer<'a> { - public_result(process_text(text, options, Mode::Indent, false)) + public_result(process_text(text, options, Mode::Indent, false)) } pub fn paren_mode<'a>(text: &'a str, options: &Options) -> Answer<'a> { - public_result(process_text(text, options, Mode::Paren, false)) + public_result(process_text(text, options, Mode::Paren, false)) } pub fn smart_mode<'a>(text: &'a str, options: &Options) -> Answer<'a> { - let smart = options.selection_start_line == None; - public_result(process_text(text, options, Mode::Indent, smart)) + let smart = options.selection_start_line == None; + public_result(process_text(text, options, Mode::Indent, smart)) } pub fn process(request: &Request) -> Answer { - let mut options = request.options.clone(); + let mut options = request.options.clone(); - if let Some(ref prev_text) = request.options.prev_text { - options.changes = changes::compute_text_changes(prev_text, &request.text); - } + if let Some(ref prev_text) = request.options.prev_text { + options.changes = changes::compute_text_changes(prev_text, &request.text); + } - if request.mode == "paren" { - Answer::from(paren_mode(&request.text, &options)) - } else if request.mode == "indent" { - Answer::from(indent_mode(&request.text, &options)) - } else if request.mode == "smart" { - Answer::from(smart_mode(&request.text, &options)) - } else { - Answer::from(Error { - message: String::from("Bad value specified for `mode`"), - ..Error::default() - }) - } + if request.mode == "paren" { + Answer::from(paren_mode(&request.text, &options)) + } else if request.mode == "indent" { + Answer::from(indent_mode(&request.text, &options)) + } else if request.mode == "smart" { + Answer::from(smart_mode(&request.text, &options)) + } else { + Answer::from(Error { + message: String::from("Bad value specified for `mode`"), + ..Error::default() + }) + } } // This is like the process function above, but uses a reference counted version of Request From 180acb80862884d755868341e9b74f976e0235af Mon Sep 17 00:00:00 2001 From: Gerry Agbobada Date: Wed, 20 Mar 2024 22:33:05 +0100 Subject: [PATCH 09/10] Part 1 of compilation fixes Upgrade to emacs crate 0.19 Fix all conversions to/from Lisp except the Vec <-> Vector equivalence Apparently one reference to env keeps escaping --- Cargo.lock | 275 +------------------------ Cargo.toml | 2 +- src/emacs_wrapper.rs | 134 +++++++++---- src/types.rs | 467 +++++++++++++++++++++---------------------- 4 files changed, 337 insertions(+), 541 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb988fb..19b6ddf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,123 +2,30 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aho-corasick" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" -dependencies = [ - "memchr", -] - -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "base-x" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" -[[package]] -name = "bindgen" -version = "0.56.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "clap", - "env_logger", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "which", -] - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" -[[package]] -name = "cexpr" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clang-sys" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54d78e30b388d4815220c8dd03fea5656b6c6d32adb59e89061552a102f8da1" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "ctor" version = "0.1.19" @@ -149,7 +56,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.9.3", + "strsim", "syn", ] @@ -172,9 +79,9 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" [[package]] name = "emacs" -version = "0.16.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5b60e3348d9d0939ee3c33b3d146bf9fa4af87859710d35ad562a9968fc863" +checksum = "9a773f2eeb842fea9f9e52f8360dbf7c0c54af0c904e025c9e08db5053b8718c" dependencies = [ "anyhow", "ctor", @@ -187,9 +94,9 @@ dependencies = [ [[package]] name = "emacs-macros" -version = "0.15.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b31160ef111ecc0fcee57fe01740df4c31b7d077c2bb258515aefdc2dd3e477" +checksum = "69656fdfe7c2608b87164964db848b5c3795de7302e3130cce7131552c6be161" dependencies = [ "darling", "proc-macro2", @@ -199,25 +106,9 @@ dependencies = [ [[package]] name = "emacs_module" -version = "0.16.2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eebdd29085b1283edb5e83cc56426a4dd2a23d1c7b1f909c8b8caf700cc1869" -dependencies = [ - "bindgen", -] - -[[package]] -name = "env_logger" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] +checksum = "b3067bc974045ed2c6db333bd4fc30d3bdaafa6421a9a889fa7b2826b6f7f2fa" [[package]] name = "fnv" @@ -234,27 +125,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "hermit-abi" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" -dependencies = [ - "libc", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "ident_case" version = "1.0.1" @@ -273,28 +143,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "265d751d31d6780a3f956bb5b8022feba2d94eeee5a84ba64f4212eedca42213" -[[package]] -name = "libloading" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "log" version = "0.4.14" @@ -304,22 +158,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "memchr" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" - -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "memchr", - "version_check", -] - [[package]] name = "once_cell" version = "1.7.2" @@ -342,12 +180,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "proc-macro2" version = "1.0.24" @@ -366,30 +198,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "regex" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", -] - -[[package]] -name = "regex-syntax" -version = "0.6.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc_version" version = "0.2.3" @@ -454,12 +262,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -[[package]] -name = "shlex" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" - [[package]] name = "stdweb" version = "0.4.20" @@ -511,12 +313,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.9.3" @@ -534,24 +330,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.24" @@ -572,15 +350,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - [[package]] name = "unicode-segmentation" version = "1.7.1" @@ -599,18 +368,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" - [[package]] name = "wasm-bindgen" version = "0.2.71" @@ -665,15 +422,6 @@ version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" -[[package]] -name = "which" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" -dependencies = [ - "libc", -] - [[package]] name = "winapi" version = "0.3.9" @@ -690,15 +438,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index d4439a1..b10e2ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ path = "src/main.rs" getopts = "0.2" libc = "0.2.39" serde = "1.0" -emacs = {version = "0.16.2", optional = true} +emacs = {version = "0.19", optional = true} serde_json = "1.0" serde_derive = "1.0" unicode-segmentation = "1.1.0" diff --git a/src/emacs_wrapper.rs b/src/emacs_wrapper.rs index 440f060..0835e4f 100644 --- a/src/emacs_wrapper.rs +++ b/src/emacs_wrapper.rs @@ -1,5 +1,5 @@ use super::parinfer::rc_process; -use emacs::{Env, IntoLisp, Result, Value}; +use emacs::{Env, FromLisp, IntoLisp, Result, Value, Vector}; use types::{Change, Error, Options, Request, SharedRequest, WrappedAnswer}; use std::{cell::RefCell, convert::TryFrom, fs::OpenOptions, io::Write, rc::Rc}; @@ -133,12 +133,12 @@ fn new_options( selection_start_line: to_usize(selection_start_line), changes: changes.clone(), prev_text: None, - ..old_options + ..old_options.clone() }) } emacs::define_errors! { - unknown_option_error "This option name is unknown should not be negative" (error) + unknown_option_error "This option name is unknown" (error) } #[defun(user_ptr, mod_in_name = false)] @@ -167,48 +167,78 @@ fn set_option<'a>( new_value: Option>, ) -> Result<()> { let env = option_name.env; - if env.eq(option_name, env.intern("partial-result")?) { - options.partial_result = new_value.into_rust()?; + if option_name.eq(env.intern("partial-result")?) { + options.partial_result = new_value + .map(|val| val.is_not_nil()) + .unwrap_or_else(Options::default_false); return Ok(()); } - if env.eq(option_name, env.intern("force-balance")?) { - options.force_balance = new_value.into_rust()?; + if option_name.eq(env.intern("force-balance")?) { + options.force_balance = new_value + .map(|val| val.is_not_nil()) + .unwrap_or_else(Options::default_false); return Ok(()); } - if env.eq(option_name, env.intern("return-parens")?) { - options.return_parens = new_value.into_rust()?; + if option_name.eq(env.intern("return-parens")?) { + options.return_parens = new_value + .map(|val| val.is_not_nil()) + .unwrap_or_else(Options::default_false); return Ok(()); } - if env.eq(option_name, env.intern("comment-char")?) { - options.comment_char = new_value.into_rust()?; + if option_name.eq(env.intern("comment-char")?) { + options.comment_char = new_value + .map(|val| String::from_lisp(val)) + .transpose()? + .map(|char_as_str| char_as_str.chars().next()) + .flatten() + .unwrap_or_else(Options::default_comment); return Ok(()); } - if env.eq(option_name, env.intern("string-delimiters")?) { - options.string_delimiters = new_value.into_rust()?; + if option_name.eq(env.intern("string-delimiters")?) { + if let Some(new_value) = new_value { + let vector = Vector::from_lisp(new_value)?; + let rust_values = vector + .into_iter() + .map(|inner_value| String::from_lisp(inner_value)) + .collect::>>()?; + options.string_delimiters = rust_values; + } else { + options.string_delimiters = Options::default_string_delimiters(); + } return Ok(()); } - if env.eq(option_name, env.intern("lisp-vline-symbols")?) { - options.lisp_vline_symbols = new_value.into_rust()?; + if option_name.eq(env.intern("lisp-vline-symbols")?) { + options.lisp_vline_symbols = new_value + .map(|val| val.is_not_nil()) + .unwrap_or_else(Options::default_false); return Ok(()); } - if env.eq(option_name, env.intern("lisp-block-comments")?) { - options.lisp_block_comments = new_value.into_rust()?; + if option_name.eq(env.intern("lisp-block-comments")?) { + options.lisp_block_comments = new_value + .map(|val| val.is_not_nil()) + .unwrap_or_else(Options::default_false); return Ok(()); } - if env.eq(option_name, env.intern("guile-block-comments")?) { - options.guile_block_comments = new_value.into_rust()?; + if option_name.eq(env.intern("guile-block-comments")?) { + options.guile_block_comments = new_value + .map(|val| val.is_not_nil()) + .unwrap_or_else(Options::default_false); return Ok(()); } - if env.eq(option_name, env.intern("scheme-sexp-comments")?) { - options.scheme_sexp_comments = new_value.into_rust()?; + if option_name.eq(env.intern("scheme-sexp-comments")?) { + options.scheme_sexp_comments = new_value + .map(|val| val.is_not_nil()) + .unwrap_or_else(Options::default_false); return Ok(()); } - if env.eq(option_name, env.intern("julia-long-strings")?) { - options.julia_long_strings = new_value.into_rust()?; + if option_name.eq(env.intern("julia-long-strings")?) { + options.janet_long_strings = new_value + .map(|val| val.is_not_nil()) + .unwrap_or_else(Options::default_false); return Ok(()); } - env.signal(unknown_option_error, option_name) + env.signal(unknown_option_error, [option_name]) } #[defun(user_ptr, mod_in_name = false)] @@ -235,38 +265,66 @@ fn get_option<'a>(options: &Options, option_name: Value<'a>) -> Result // The function is returning a type-erased Value because it can either be a boolean // or a list let env = option_name.env; - if env.eq(option_name, env.intern("partial-result")?) { + if option_name.eq(env.intern("partial-result")?) { return Ok(options.partial_result.into_lisp(env)?); } - if env.eq(option_name, env.intern("force-balance")?) { + if option_name.eq(env.intern("force-balance")?) { return Ok(options.force_balance.into_lisp(env)?); } - if env.eq(option_name, env.intern("return-parens")?) { + if option_name.eq(env.intern("return-parens")?) { return Ok(options.return_parens.into_lisp(env)?); } - if env.eq(option_name, env.intern("comment-char")?) { - return Ok(options.comment_char.into_lisp(env)?); + if option_name.eq(env.intern("comment-char")?) { + return Ok(options.comment_char.to_string().into_lisp(env)?); } - if env.eq(option_name, env.intern("string-delimiters")?) { - return Ok(options.string_delimiters.into_lisp(env)?); + if option_name.eq(env.intern("string-delimiters")?) { + // return Ok(to_lisp_vec(env, options.string_delimiters.clone())?); + return Ok(VecToVector(options.string_delimiters.clone()).into_lisp(env)?); } - if env.eq(option_name, env.intern("lisp-vline-symbols")?) { + if option_name.eq(env.intern("lisp-vline-symbols")?) { return Ok(options.lisp_vline_symbols.into_lisp(env)?); } - if env.eq(option_name, env.intern("lisp-block-comments")?) { + if option_name.eq(env.intern("lisp-block-comments")?) { return Ok(options.lisp_block_comments.into_lisp(env)?); } - if env.eq(option_name, env.intern("guile-block-comments")?) { + if option_name.eq(env.intern("guile-block-comments")?) { return Ok(options.guile_block_comments.into_lisp(env)?); } - if env.eq(option_name, env.intern("scheme-sexp-comments")?) { + if option_name.eq(env.intern("scheme-sexp-comments")?) { return Ok(options.scheme_sexp_comments.into_lisp(env)?); } - if env.eq(option_name, env.intern("julia-long-strings")?) { - return Ok(options.julia_long_strings.into_lisp(env)?); + if option_name.eq(env.intern("julia-long-strings")?) { + return Ok(options.janet_long_strings.into_lisp(env)?); } - env.signal(unknown_option_error, option_name) + env.signal(unknown_option_error, [option_name]) +} + +fn to_lisp_vec(env: &Env, vec: Vec) -> Result { + env.vector( + &vec + .into_iter() + .map(|s| s.into_lisp(env)) + .collect::>>()?, + ) +} + +// Make a wrapper type to convince the compiler that the +// lifetimes of the associated lisp environment are going to be +// fine +struct VecToVector(Vec); + +impl<'e> IntoLisp<'e> for VecToVector { + fn into_lisp(self, env: &'e Env) -> Result> { + env.vector( + self + .0 + .into_iter() + .map(|s| s.into_lisp(env)) + .collect::>>()? + .as_slice(), + ) + } } #[defun(mod_in_name = false)] diff --git a/src/types.rs b/src/types.rs index e54efd3..bf965db 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,9 +1,7 @@ use serde; use serde_json; use std; -use std::{fmt, - mem, - rc::Rc}; +use std::{fmt, mem, rc::Rc}; pub type LineNumber = usize; pub type Column = usize; @@ -12,290 +10,292 @@ pub type Delta = i64; #[derive(Clone, Debug, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Change { - pub x: Column, - pub line_no: LineNumber, - pub old_text: String, - pub new_text: String, + pub x: Column, + pub line_no: LineNumber, + pub old_text: String, + pub new_text: String, } #[derive(Clone, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Options { - pub cursor_x: Option, - pub cursor_line: Option, - pub prev_cursor_x: Option, - pub prev_cursor_line: Option, - pub prev_text: Option, - pub selection_start_line: Option, - #[serde(default = "Options::default_changes")] - pub changes: Vec, - #[serde(default = "Options::default_false")] - pub partial_result: bool, - #[serde(default = "Options::default_false")] - pub force_balance: bool, - #[serde(default = "Options::default_false")] - pub return_parens: bool, - #[serde(default = "Options::default_comment")] - pub comment_char: char, - #[serde(default = "Options::default_string_delimiters")] - pub string_delimiters: Vec, - #[serde(default = "Options::default_false")] - pub lisp_vline_symbols: bool, - #[serde(default = "Options::default_false")] - pub lisp_block_comments: bool, - #[serde(default = "Options::default_false")] - pub guile_block_comments: bool, - #[serde(default = "Options::default_false")] - pub scheme_sexp_comments: bool, - #[serde(default = "Options::default_false")] - pub janet_long_strings: bool, + pub cursor_x: Option, + pub cursor_line: Option, + pub prev_cursor_x: Option, + pub prev_cursor_line: Option, + pub prev_text: Option, + pub selection_start_line: Option, + #[serde(default = "Options::default_changes")] + pub changes: Vec, + #[serde(default = "Options::default_false")] + pub partial_result: bool, + #[serde(default = "Options::default_false")] + pub force_balance: bool, + #[serde(default = "Options::default_false")] + pub return_parens: bool, + #[serde(default = "Options::default_comment")] + pub comment_char: char, + #[serde(default = "Options::default_string_delimiters")] + pub string_delimiters: Vec, + #[serde(default = "Options::default_false")] + pub lisp_vline_symbols: bool, + #[serde(default = "Options::default_false")] + pub lisp_block_comments: bool, + #[serde(default = "Options::default_false")] + pub guile_block_comments: bool, + #[serde(default = "Options::default_false")] + pub scheme_sexp_comments: bool, + #[serde(default = "Options::default_false")] + pub janet_long_strings: bool, } impl Default for Options { - fn default() -> Self { - Self { - cursor_x: None, - cursor_line: None, - prev_cursor_x: None, - prev_cursor_line: None, - prev_text: None, - selection_start_line: None, - changes: Self::default_changes(), - partial_result: false, - force_balance: false, - return_parens: false, - comment_char: Self::default_comment(), - string_delimiters: Self::default_string_delimiters(), - lisp_vline_symbols: false, - lisp_block_comments: false, - guile_block_comments: false, - scheme_sexp_comments: false, - janet_long_strings: false, - } + fn default() -> Self { + Self { + cursor_x: None, + cursor_line: None, + prev_cursor_x: None, + prev_cursor_line: None, + prev_text: None, + selection_start_line: None, + changes: Self::default_changes(), + partial_result: false, + force_balance: false, + return_parens: false, + comment_char: Self::default_comment(), + string_delimiters: Self::default_string_delimiters(), + lisp_vline_symbols: false, + lisp_block_comments: false, + guile_block_comments: false, + scheme_sexp_comments: false, + janet_long_strings: false, } + } } - impl Options { - fn default_changes() -> Vec { - vec![] - } - - fn default_false() -> bool { - false - } - - fn default_comment() -> char { - ';' - } - fn default_string_delimiters() -> Vec { - vec!["\"".to_string()] - } + pub(crate) fn default_changes() -> Vec { + vec![] + } + + pub(crate) fn default_false() -> bool { + false + } + + pub(crate) fn default_comment() -> char { + ';' + } + pub(crate) fn default_string_delimiters() -> Vec { + vec!["\"".to_string()] + } } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Request { - pub mode: String, - pub text: String, - pub options: Options, + pub mode: String, + pub text: String, + pub options: Options, } -#[derive(Clone,Serialize,Debug)] +#[derive(Clone, Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct TabStop<'a> { - pub ch: &'a str, - pub x: Column, - pub line_no: LineNumber, - pub arg_x: Option, + pub ch: &'a str, + pub x: Column, + pub line_no: LineNumber, + pub arg_x: Option, } -#[derive(Clone,Debug,Serialize)] +#[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct ParenTrail { - pub line_no: LineNumber, - pub start_x: Column, - pub end_x: Column, + pub line_no: LineNumber, + pub start_x: Column, + pub end_x: Column, } #[derive(Clone, Debug)] pub struct Closer<'a> { - pub line_no: LineNumber, - pub x: Column, - pub ch: &'a str, - pub trail: Option + pub line_no: LineNumber, + pub x: Column, + pub ch: &'a str, + pub trail: Option, } -#[derive(Clone,Debug,Serialize)] +#[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Paren<'a> { - pub line_no: LineNumber, - pub ch: &'a str, - pub x: Column, - pub indent_delta: Delta, - pub max_child_indent: Option, - pub arg_x: Option, - pub input_line_no: LineNumber, - pub input_x: Column, - - #[serde(skip)] - pub closer: Option>, - #[serde(skip)] - pub children: Vec> + pub line_no: LineNumber, + pub ch: &'a str, + pub x: Column, + pub indent_delta: Delta, + pub max_child_indent: Option, + pub arg_x: Option, + pub input_line_no: LineNumber, + pub input_x: Column, + + #[serde(skip)] + pub closer: Option>, + #[serde(skip)] + pub children: Vec>, } #[derive(Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct Answer<'a> { - pub text: std::borrow::Cow<'a, str>, - pub success: bool, - pub error: Option, - pub cursor_x: Option, - pub cursor_line: Option, - pub tab_stops: Vec>, - pub paren_trails: Vec, - pub parens: Vec>, + pub text: std::borrow::Cow<'a, str>, + pub success: bool, + pub error: Option, + pub cursor_x: Option, + pub cursor_line: Option, + pub tab_stops: Vec>, + pub paren_trails: Vec, + pub parens: Vec>, } impl<'a> From for Answer<'a> { - fn from(error: Error) -> Answer<'a> { - Answer { - text: std::borrow::Cow::from(""), - success: false, - error: Some(error), - cursor_x: None, - cursor_line: None, - tab_stops: vec![], - paren_trails: vec![], - parens: vec![], - } + fn from(error: Error) -> Answer<'a> { + Answer { + text: std::borrow::Cow::from(""), + success: false, + error: Some(error), + cursor_x: None, + cursor_line: None, + tab_stops: vec![], + paren_trails: vec![], + parens: vec![], } + } } #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub enum ErrorName { - QuoteDanger, - EolBackslash, - UnclosedQuote, - UnclosedParen, - UnmatchedCloseParen, - UnmatchedOpenParen, - LeadingCloseParen, - - Utf8EncodingError, - JsonEncodingError, - Panic, - - Restart, + QuoteDanger, + EolBackslash, + UnclosedQuote, + UnclosedParen, + UnmatchedCloseParen, + UnmatchedOpenParen, + LeadingCloseParen, + + Utf8EncodingError, + JsonEncodingError, + Panic, + + Restart, } impl Default for ErrorName { - fn default() -> ErrorName { - ErrorName::Restart - } + fn default() -> ErrorName { + ErrorName::Restart + } } impl ToString for ErrorName { - fn to_string(&self) -> String { - String::from(match self { - &ErrorName::QuoteDanger => "quote-danger", - &ErrorName::EolBackslash => "eol-backslash", - &ErrorName::UnclosedQuote => "unclosed-quote", - &ErrorName::UnclosedParen => "unclosed-paren", - &ErrorName::UnmatchedCloseParen => "unmatched-close-paren", - &ErrorName::UnmatchedOpenParen => "unmatched-open-paren", - &ErrorName::LeadingCloseParen => "leading-close-paren", - &ErrorName::Utf8EncodingError => "utf8-error", - &ErrorName::JsonEncodingError => "json-error", - &ErrorName::Panic => "panic", - _ => "??", - }) - } + fn to_string(&self) -> String { + String::from(match self { + &ErrorName::QuoteDanger => "quote-danger", + &ErrorName::EolBackslash => "eol-backslash", + &ErrorName::UnclosedQuote => "unclosed-quote", + &ErrorName::UnclosedParen => "unclosed-paren", + &ErrorName::UnmatchedCloseParen => "unmatched-close-paren", + &ErrorName::UnmatchedOpenParen => "unmatched-open-paren", + &ErrorName::LeadingCloseParen => "leading-close-paren", + &ErrorName::Utf8EncodingError => "utf8-error", + &ErrorName::JsonEncodingError => "json-error", + &ErrorName::Panic => "panic", + _ => "??", + }) + } } impl serde::Serialize for ErrorName { - fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer - { - serializer.serialize_str(&self.to_string()) - } + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } } impl<'a> serde::Deserialize<'a> for ErrorName { - fn deserialize(deserializer: D) -> Result - where D: serde::Deserializer<'a> - { - struct Visitor; - - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = ErrorName; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("error name") - } - - fn visit_str(self, value: &str) -> Result - where E: serde::de::Error - { - match value { - "quote-danger" => Ok(ErrorName::QuoteDanger), - "eol-backslash" => Ok(ErrorName::EolBackslash), - "unclosed-quote" => Ok(ErrorName::UnclosedQuote), - "unclosed-paren" => Ok(ErrorName::UnclosedParen), - "unmatched-close-paren" => Ok(ErrorName::UnmatchedCloseParen), - "unmatched-open-paren" => Ok(ErrorName::UnmatchedOpenParen), - "leading-close-paren" => Ok(ErrorName::LeadingCloseParen), - "utf8-error" => Ok(ErrorName::Utf8EncodingError), - "json-error" => Ok(ErrorName::JsonEncodingError), - "panic" => Ok(ErrorName::Panic), - _ => Err(E::custom(format!("unknown error name: {}", value))) - } - } + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'a>, + { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = ErrorName; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("error name") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "quote-danger" => Ok(ErrorName::QuoteDanger), + "eol-backslash" => Ok(ErrorName::EolBackslash), + "unclosed-quote" => Ok(ErrorName::UnclosedQuote), + "unclosed-paren" => Ok(ErrorName::UnclosedParen), + "unmatched-close-paren" => Ok(ErrorName::UnmatchedCloseParen), + "unmatched-open-paren" => Ok(ErrorName::UnmatchedOpenParen), + "leading-close-paren" => Ok(ErrorName::LeadingCloseParen), + "utf8-error" => Ok(ErrorName::Utf8EncodingError), + "json-error" => Ok(ErrorName::JsonEncodingError), + "panic" => Ok(ErrorName::Panic), + _ => Err(E::custom(format!("unknown error name: {}", value))), } - - deserializer.deserialize_string(Visitor) + } } + + deserializer.deserialize_string(Visitor) + } } #[derive(Debug, Default, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct Error { - pub name: ErrorName, - pub message: String, - pub x: Column, - pub line_no: LineNumber, - pub input_x: Column, - pub input_line_no: LineNumber, + pub name: ErrorName, + pub message: String, + pub x: Column, + pub line_no: LineNumber, + pub input_x: Column, + pub input_line_no: LineNumber, } impl From for Error { - fn from(error: std::str::Utf8Error) -> Error { - Error { - name: ErrorName::Utf8EncodingError, - message: format!("Error decoding UTF8: {}", error), - ..Error::default() - } + fn from(error: std::str::Utf8Error) -> Error { + Error { + name: ErrorName::Utf8EncodingError, + message: format!("Error decoding UTF8: {}", error), + ..Error::default() } + } } impl From for Error { - fn from(error: std::ffi::NulError) -> Error { - Error { - name: ErrorName::Panic, - message: format!("{}", error), - ..Error::default() - } + fn from(error: std::ffi::NulError) -> Error { + Error { + name: ErrorName::Panic, + message: format!("{}", error), + ..Error::default() } + } } impl From for Error { - fn from(error: serde_json::Error) -> Error { - Error { - name: ErrorName::JsonEncodingError, - message: format!("Error parsing JSON: {}", error), - ..Error::default() - } + fn from(error: serde_json::Error) -> Error { + Error { + name: ErrorName::JsonEncodingError, + message: format!("Error parsing JSON: {}", error), + ..Error::default() } + } } // Introduce the concept of Reference Counting of requests to work with emacs memory module @@ -307,35 +307,34 @@ const ANSWER_LEN: usize = mem::size_of::() / 8; pub type RawAnswer = [u64; ANSWER_LEN]; #[allow(dead_code)] -pub struct WrappedAnswer{ +pub struct WrappedAnswer { request: SharedRequest, - raw: RawAnswer + raw: RawAnswer, } - impl WrappedAnswer { - #[inline] - #[allow(dead_code)] - pub unsafe fn new(request: SharedRequest, inner: Answer) -> Self { - let ptr = (&inner as *const Answer) as *const RawAnswer; - // Delay inner cursor's cleanup (until wrapper is dropped). - mem::forget(inner); - let raw = ptr.read(); - Self { request, raw } - } - - #[inline] - #[allow(dead_code)] - pub fn inner(&self) -> &Answer { - let ptr = (&self.raw as *const RawAnswer) as *const Answer; - unsafe { &*ptr } - } + #[inline] + #[allow(dead_code)] + pub unsafe fn new(request: SharedRequest, inner: Answer) -> Self { + let ptr = (&inner as *const Answer) as *const RawAnswer; + // Delay inner cursor's cleanup (until wrapper is dropped). + mem::forget(inner); + let raw = ptr.read(); + Self { request, raw } + } + + #[inline] + #[allow(dead_code)] + pub fn inner(&self) -> &Answer { + let ptr = (&self.raw as *const RawAnswer) as *const Answer; + unsafe { &*ptr } + } } impl Drop for WrappedAnswer { - #[inline] - fn drop(&mut self) { - let ptr = (&mut self.raw as *mut RawAnswer) as *mut Answer; - unsafe { ptr.read() }; - } + #[inline] + fn drop(&mut self) { + let ptr = (&mut self.raw as *mut RawAnswer) as *mut Answer; + unsafe { ptr.read() }; + } } From a1c445be42060775124711cc28a9d20e59be75d5 Mon Sep 17 00:00:00 2001 From: Gerry Agbobada Date: Sun, 24 Mar 2024 18:35:35 +0100 Subject: [PATCH 10/10] Fix compilation errors and few clippies --- src/emacs_wrapper.rs | 11 +---------- src/parinfer.rs | 35 ++++++++--------------------------- 2 files changed, 9 insertions(+), 37 deletions(-) diff --git a/src/emacs_wrapper.rs b/src/emacs_wrapper.rs index 0835e4f..e50aee4 100644 --- a/src/emacs_wrapper.rs +++ b/src/emacs_wrapper.rs @@ -241,7 +241,7 @@ fn set_option<'a>( env.signal(unknown_option_error, [option_name]) } -#[defun(user_ptr, mod_in_name = false)] +#[defun(mod_in_name = false)] /// Get a field within the passed options. /// /// Valid field names are: @@ -300,15 +300,6 @@ fn get_option<'a>(options: &Options, option_name: Value<'a>) -> Result env.signal(unknown_option_error, [option_name]) } -fn to_lisp_vec(env: &Env, vec: Vec) -> Result { - env.vector( - &vec - .into_iter() - .map(|s| s.into_lisp(env)) - .collect::>>()?, - ) -} - // Make a wrapper type to convince the compiler that the // lifetimes of the associated lisp environment are going to be // fine diff --git a/src/parinfer.rs b/src/parinfer.rs index b2cd198..f97ae0e 100644 --- a/src/parinfer.rs +++ b/src/parinfer.rs @@ -647,28 +647,10 @@ fn peek_works() { assert_eq!(peek(&empty, 1), None); } -fn delta_to_column(delta: Delta) -> Column { - if delta >= 0 { - delta as Column - } else { - 0 as Column - } -} - -#[cfg(test)] -#[test] -fn delta_to_column_works() { - assert_eq!(delta_to_column(1), 1); - assert_eq!(delta_to_column(0), 0); - assert_eq!(delta_to_column(-1), 0); -} // {{{1 Questions about characters fn is_close_paren(paren: &str) -> bool { - match paren { - "}" | "]" | ")" => true, - _ => false, - } + matches!(paren, "}" | "]" | ")") } fn is_valid_close_paren<'a>(paren_stack: &Vec>, ch: &'a str) -> bool { @@ -692,7 +674,8 @@ fn is_whitespace<'a>(result: &State<'a>) -> bool { fn is_closable<'a>(result: &State<'a>) -> bool { let ch = result.ch; let closer = is_close_paren(ch) && !result.is_escaped(); - return result.is_in_code() && !is_whitespace(result) && ch != "" && !closer; + + result.is_in_code() && !is_whitespace(result) && ch != "" && !closer } // {{{1 Advanced operations on characters @@ -705,7 +688,7 @@ fn check_cursor_holding<'a>(result: &State<'a>) -> Result { let holding = result.cursor_line == Some(opener.line_no) && result.cursor_x.map(|x| hold_min_x <= x).unwrap_or(false) && result.cursor_x.map(|x| x <= hold_max_x).unwrap_or(false); - let should_check_prev = result.changes.is_empty() && result.prev_cursor_line != None; + let should_check_prev = result.changes.is_empty() && result.prev_cursor_line.is_some(); if should_check_prev { let prev_holding = result.prev_cursor_line == Some(opener.line_no) && result @@ -736,12 +719,10 @@ fn track_arg_tab_stop<'a>(result: &mut State<'a>, state: TrackingArgTabStop) { if result.is_in_code() && is_whitespace(result) { result.tracking_arg_tab_stop = TrackingArgTabStop::Arg; } - } else if state == TrackingArgTabStop::Arg { - if !is_whitespace(result) { - let opener = result.paren_stack.last_mut().unwrap(); - opener.arg_x = Some(result.x); - result.tracking_arg_tab_stop = TrackingArgTabStop::NotSearching; - } + } else if state == TrackingArgTabStop::Arg && !is_whitespace(result) { + let opener = result.paren_stack.last_mut().unwrap(); + opener.arg_x = Some(result.x); + result.tracking_arg_tab_stop = TrackingArgTabStop::NotSearching; } }