From 3d0017134e47ffdb2824cb0661359a9737c36d7f Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Tue, 2 Jan 2024 22:46:45 -0600 Subject: [PATCH] add really janky rip-grep support --- .gitignore | 1 + Makefile | 5 +- flake.lock | 71 +++++++- flake.nix | 12 +- lib-rg/Cargo.lock | 329 ++++++++++++++++++++++++++++++++++++++ lib-rg/Cargo.toml | 13 ++ lib-rg/src/lib.rs | 220 +++++++++++++++++++++++++ rust-toolchain | 1 + src/core/file_buffer.odin | 2 + src/ui/grep_window.odin | 171 +++++++++++++++++++- 10 files changed, 819 insertions(+), 6 deletions(-) create mode 100644 lib-rg/Cargo.lock create mode 100644 lib-rg/Cargo.toml create mode 100644 lib-rg/src/lib.rs create mode 100755 rust-toolchain diff --git a/.gitignore b/.gitignore index e660fd9..6c399e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ bin/ +lib-rg/target diff --git a/Makefile b/Makefile index ac4bff3..44c9c44 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,7 @@ all: editor -editor: src/*.odin +editor: src/*.odin rg odin build src/ -out:bin/editor -lld + +rg: + cargo b --manifest-path=lib-rg/Cargo.toml diff --git a/flake.lock b/flake.lock index 66084da..6b846bc 100644 --- a/flake.lock +++ b/flake.lock @@ -33,6 +33,24 @@ "type": "github" } }, + "flake-utils_3": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "nixgl": { "inputs": { "flake-utils": "flake-utils_2", @@ -83,11 +101,47 @@ "type": "github" } }, + "nixpkgs_3": { + "locked": { + "lastModified": 1681358109, + "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", "nixgl": "nixgl", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_2", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils_3", + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1704075545, + "narHash": "sha256-L3zgOuVKhPjKsVLc3yTm2YJ6+BATyZBury7wnhyc8QU=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "a0df72e106322b67e9c6e591fe870380bd0da0d5", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" } }, "systems": { @@ -104,6 +158,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "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", diff --git a/flake.nix b/flake.nix index aed63b1..e7fd793 100755 --- a/flake.nix +++ b/flake.nix @@ -3,15 +3,19 @@ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; nixgl.url = "github:guibou/nixGL"; + rust-overlay.url = "github:oxalica/rust-overlay"; }; - outputs = { self, nixpkgs, flake-utils, nixgl, ... }: + outputs = { self, nixpkgs, flake-utils, nixgl, rust-overlay, ... }: flake-utils.lib.eachDefaultSystem (system: let - overlays = [ nixgl.overlay ]; + overlays = [ nixgl.overlay (import rust-overlay) ]; pkgs = import nixpkgs { inherit system overlays; }; + local-rust = (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain).override { + extensions = [ "rust-analysis" ]; + }; fixed-odin = pkgs.odin.overrideAttrs (finalAttrs: prevAttr: rec { src = /Users/temp/Documents/personal/Odin; LLVM_CONFIG = "${pkgs.llvmPackages_17.llvm.dev}/bin/llvm-config"; @@ -48,6 +52,8 @@ devShell = pkgs.mkShell { buildInputs = with pkgs; (if pkgs.system == "aarch64-darwin" || pkgs.system == "x86_64-darwin" then [ fixed-odin + local-rust + rust-analyzer darwin.apple_sdk.frameworks.CoreData darwin.apple_sdk.frameworks.Kernel darwin.apple_sdk.frameworks.CoreVideo @@ -59,6 +65,8 @@ pkg-config binutils odin + local-rust + rust-analyzer libGL xorg.libX11 xorg.libXi diff --git a/lib-rg/Cargo.lock b/lib-rg/Cargo.lock new file mode 100644 index 0000000..c167218 --- /dev/null +++ b/lib-rg/Cargo.lock @@ -0,0 +1,329 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "bstr" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "encoding_rs_io" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83" +dependencies = [ + "encoding_rs", +] + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "grep" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2b024ec1e686cb64d78beb852030b0e632af93817f1ed25be0173af0e94939" +dependencies = [ + "grep-cli", + "grep-matcher", + "grep-printer", + "grep-regex", + "grep-searcher", +] + +[[package]] +name = "grep-cli" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea40788c059ab8b622c4d074732750bfb3bd2912e2dd58eabc11798a4d5ad725" +dependencies = [ + "bstr", + "globset", + "libc", + "log", + "termcolor", + "winapi-util", +] + +[[package]] +name = "grep-matcher" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a3141a10a43acfedc7c98a60a834d7ba00dfe7bec9071cbfc19b55b292ac02" +dependencies = [ + "memchr", +] + +[[package]] +name = "grep-printer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743c12a03c8aee38b6e5bd0168d8ebb09345751323df4a01c56e792b1f38ceb2" +dependencies = [ + "bstr", + "grep-matcher", + "grep-searcher", + "log", + "serde", + "serde_json", + "termcolor", +] + +[[package]] +name = "grep-regex" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f748bb135ca835da5cbc67ca0e6955f968db9c5df74ca4f56b18e1ddbc68230d" +dependencies = [ + "bstr", + "grep-matcher", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "grep-searcher" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba536ae4f69bec62d8839584dd3153d3028ef31bb229f04e09fb5a9e5a193c54" +dependencies = [ + "bstr", + "encoding_rs", + "encoding_rs_io", + "grep-matcher", + "log", + "memchr", + "memmap2", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memmap2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" +dependencies = [ + "libc", +] + +[[package]] +name = "proc-macro2" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rg" +version = "0.1.0" +dependencies = [ + "grep", + "termcolor", + "walkdir", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/lib-rg/Cargo.toml b/lib-rg/Cargo.toml new file mode 100644 index 0000000..b08f4a8 --- /dev/null +++ b/lib-rg/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rg" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +grep = "0.3.1" +termcolor = "1.4.0" +walkdir = "2.4.0" diff --git a/lib-rg/src/lib.rs b/lib-rg/src/lib.rs new file mode 100644 index 0000000..d37b338 --- /dev/null +++ b/lib-rg/src/lib.rs @@ -0,0 +1,220 @@ +use std::{ + error::Error, + ffi::{CStr, OsString}, + os::raw::c_char, + str::FromStr, +}; + +use grep::{ + regex::RegexMatcher, + searcher::{BinaryDetection, SearcherBuilder, Sink, SinkError}, +}; +use walkdir::WalkDir; + +#[derive(Debug)] +pub enum SimpleSinkError { + StandardError, + NoLine, + BadString, +} + +impl SinkError for SimpleSinkError { + fn error_message(message: T) -> Self { + eprintln!("{message}"); + + Self::StandardError + } +} + +#[repr(C)] +pub struct UnsafeMatch { + text_str: *mut u8, + text_len: usize, + text_cap: usize, + + path_str: *mut u8, + path_len: usize, + path_cap: usize, + + line_number: u64, + column: u64, +} + +impl From for UnsafeMatch { + fn from(value: Match) -> Self { + let mut text_boxed = Box::new(value.text); + let text_str = text_boxed.as_mut_ptr(); + let text_len = text_boxed.len(); + let text_cap = text_boxed.capacity(); + Box::leak(text_boxed); + + let mut path_boxed = Box::new(value.path); + let path_str = path_boxed.as_mut_ptr(); + let path_len = path_boxed.len(); + let path_cap = path_boxed.capacity(); + Box::leak(path_boxed); + + Self { + text_str, + text_len, + text_cap, + path_str, + path_len, + path_cap, + line_number: value.line_number.unwrap_or_default(), + column: value.column, + } + } +} + +struct Match { + text: Vec, + path: String, + line_number: Option, + column: u64, +} +impl Match { + fn from_sink_match_with_path( + value: &grep::searcher::SinkMatch<'_>, + path: Option, + ) -> Result { + let line = value + .lines() + .next() + .ok_or(SimpleSinkError::NoLine)? + .to_vec(); + let column = value.bytes_range_in_buffer().len() as u64; + + Ok(Self { + text: line, + path: path.unwrap_or_default(), + line_number: value.line_number(), + column, + }) + } +} + +#[derive(Default)] +struct SimpleSink { + current_path: Option, + matches: Vec, +} +impl Sink for SimpleSink { + type Error = SimpleSinkError; + + fn matched( + &mut self, + _searcher: &grep::searcher::Searcher, + mat: &grep::searcher::SinkMatch<'_>, + ) -> Result { + self.matches.push(Match::from_sink_match_with_path( + mat, + self.current_path.clone(), + )?); + + Ok(true) + } +} + +#[repr(C)] +pub struct UnsafeMatchArray { + matches: *mut UnsafeMatch, + len: usize, + capacity: usize, +} + +impl Default for UnsafeMatchArray { + fn default() -> Self { + Self { + matches: std::ptr::null_mut(), + len: 0, + capacity: 0, + } + } +} + +impl From for UnsafeMatchArray { + fn from(value: SimpleSink) -> Self { + let matches: Vec = value.matches.into_iter().map(Into::into).collect(); + let mut boxed_vec = Box::new(matches); + + let ptr = boxed_vec.as_mut_ptr(); + let len = boxed_vec.len(); + let capacity = boxed_vec.capacity(); + Box::leak(boxed_vec); + + Self { + matches: ptr, + len, + capacity, + } + } +} + +/// # Safety +/// Who knows what'll happen if you don't pass valid strings +#[no_mangle] +pub unsafe extern "C" fn rg_search( + pattern: *const c_char, + path: *const c_char, +) -> UnsafeMatchArray { + let pattern = CStr::from_ptr(pattern); + let path = CStr::from_ptr(path); + if let (Ok(path), Ok(pattern)) = (path.to_str(), pattern.to_str()) { + if let Ok(path) = OsString::from_str(path) { + return match search(pattern, &[path]) { + Ok(sink) => sink.into(), + Err(err) => { + eprintln!("rg search failed: {}", err); + Default::default() + } + }; + } + } + + Default::default() +} + +/// # Safety +/// Who knows what'll happen if you don't pass back the same vec +#[no_mangle] +pub unsafe extern "C" fn drop_match_array(match_array: UnsafeMatchArray) { + let matches = Vec::from_raw_parts(match_array.matches, match_array.len, match_array.capacity); + for mat in matches { + let _ = String::from_raw_parts(mat.text_str, mat.text_len, mat.text_cap); + let _ = String::from_raw_parts(mat.path_str, mat.path_len, mat.path_cap); + } +} + +fn search(pattern: &str, paths: &[OsString]) -> Result> { + let matcher = RegexMatcher::new_line_matcher(pattern)?; + let mut searcher = SearcherBuilder::new() + .binary_detection(BinaryDetection::quit(b'\x00')) + .line_number(true) + .build(); + + let mut sink = SimpleSink::default(); + for path in paths { + for result in WalkDir::new(path) { + let dent = match result { + Ok(dent) => dent, + Err(err) => { + eprintln!("{}", err); + continue; + } + }; + if !dent.file_type().is_file() { + continue; + } + sink.current_path = Some(dent.path().to_string_lossy().into()); + + let result = searcher.search_path(&matcher, dent.path(), &mut sink); + + if let Err(err) = result { + eprintln!("{}: {:?}", dent.path().display(), err); + } + } + } + + Ok(sink) +} diff --git a/rust-toolchain b/rust-toolchain new file mode 100755 index 0000000..2bf5ad0 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +stable diff --git a/src/core/file_buffer.odin b/src/core/file_buffer.odin index a24cc47..95a86bc 100644 --- a/src/core/file_buffer.odin +++ b/src/core/file_buffer.odin @@ -370,6 +370,8 @@ update_file_buffer_index_from_cursor :: proc(buffer: ^FileBuffer) { // FIXME: just swap cursors buffer.cursor.index = before_it.cursor.index; + + update_file_buffer_scroll(buffer); } file_buffer_line_length :: proc(buffer: ^FileBuffer, index: FileBufferIndex) -> int { diff --git a/src/ui/grep_window.odin b/src/ui/grep_window.odin index ce1f33e..5ced435 100644 --- a/src/ui/grep_window.odin +++ b/src/ui/grep_window.odin @@ -1,15 +1,84 @@ package ui; +@(extra_linker_flags="-L./lib-rg/target/debug/") +foreign import rg "system:rg" + +ExternMatch :: struct { + text_ptr: [^]u8, + text_len: int, + text_cap: int, + + path_ptr: [^]u8, + path_len: int, + path_cap: int, + + line: u64, + col: u64 +} + +ExternMatchArray :: struct { + matches: [^]ExternMatch, + len: uint, + capacity: uint, +} + +foreign rg { + rg_search :: proc (pattern: cstring, path: cstring) -> ExternMatchArray --- + drop_match_array :: proc(match_array: ExternMatchArray) --- +} + import "core:math" +import "core:fmt" +import "core:runtime" +import "core:strings" import "vendor:raylib" import "../core" import "../theme" +GrepMatch :: struct { + text: string, + path: string, + line: int, + col: int, +} + +transmute_extern_matches :: proc(extern_matches: ExternMatchArray, dest: ^[dynamic]GrepMatch) { + if extern_matches.matches != nil { + for i in 0.. 0 { + path = strings.string_from_ptr(match.path_ptr, match.path_len); + } + + text: string = ""; + if match.text_ptr != nil && match.text_len > 0 { + text = strings.string_from_ptr(match.text_ptr, match.text_len); + } + + cloned := GrepMatch { + text = text, + path = path, + line = int(match.line), + col = int(match.col) + }; + + append(dest, cloned); + } + } +} + GrepWindow :: struct { using window: core.Window, input_buffer: core.FileBuffer, + + selected_match: int, + + extern_matches: ExternMatchArray, + matches: [dynamic]GrepMatch, } create_grep_window :: proc() -> ^GrepWindow { @@ -18,13 +87,55 @@ create_grep_window :: proc() -> ^GrepWindow { core.register_key_action(&input_map, .ENTER, proc(state: ^core.State) { win := cast(^GrepWindow)(state.window); - core.request_window_close(state); + if win.matches != nil && len(win.matches) > 0 { + buffer, err := core.new_file_buffer(context.allocator, strings.clone(win.matches[win.selected_match].path)); + if err.type != .None { + fmt.println("Failed to create file buffer:", err); + } else { + runtime.append(&state.buffers, buffer); + state.current_buffer = len(state.buffers)-1; + + buffer := &state.buffers[state.current_buffer]; + buffer.cursor.line = win.matches[win.selected_match].line-1; + buffer.cursor.col = 0; + buffer.glyph_buffer_height = math.min(256, int((state.screen_height - state.source_font_height*2) / state.source_font_height)) + 1; + buffer.glyph_buffer_width = math.min(256, int((state.screen_width - state.source_font_width) / state.source_font_width)); + core.update_file_buffer_index_from_cursor(buffer); + + core.request_window_close(state); + } + } }, "jump to location"); core.register_key_action(&input_map, .I, proc(state: ^core.State) { state.mode = .Insert; }, "enter insert mode"); + core.register_key_action(&input_map, .T, proc(state: ^core.State) { + win := cast(^GrepWindow)(state.window); + + grep_files(win, state); + }, "example search"); + core.register_key_action(&input_map, .K, proc(state: ^core.State) { + win := cast(^GrepWindow)(state.window); + + if win.selected_match > 0 { + win.selected_match -= 1; + } else { + win.selected_match = len(win.matches)-1; + } + + }, "move selection up"); + core.register_key_action(&input_map, .J, proc(state: ^core.State) { + win := cast(^GrepWindow)(state.window); + + if win.selected_match >= len(win.matches)-1 { + win.selected_match = 0; + } else { + win.selected_match += 1; + } + }, "move selection down"); + grep_window := new(GrepWindow); grep_window^ = GrepWindow { window = core.Window { @@ -35,6 +146,7 @@ create_grep_window :: proc() -> ^GrepWindow { }, input_buffer = core.new_virtual_file_buffer(context.allocator), + matches = make([dynamic]GrepMatch), }; return grep_window; @@ -43,6 +155,14 @@ create_grep_window :: proc() -> ^GrepWindow { free_grep_window :: proc(win: ^core.Window, state: ^core.State) { win := cast(^GrepWindow)(win); + if win.extern_matches.matches != nil { + drop_match_array(win.extern_matches); + win.extern_matches.matches = nil; + win.extern_matches.len = 0; + win.extern_matches.capacity = 0; + } + + delete(win.matches); core.free_file_buffer(&win.input_buffer); } @@ -54,7 +174,32 @@ grep_window_get_buffer :: proc(win: ^core.Window) -> ^core.FileBuffer { @private grep_files :: proc(win: ^core.Window, state: ^core.State) { - // TODO: use rip-grep to search through files + win := cast(^GrepWindow)(win); + + if win.extern_matches.matches != nil { + drop_match_array(win.extern_matches); + win.extern_matches.matches = nil; + win.extern_matches.len = 0; + win.extern_matches.capacity = 0; + } + + if win.matches != nil { + clear_dynamic_array(&win.matches); + } else { + win.matches = make([dynamic]GrepMatch); + } + + builder := strings.builder_make(); + it := core.new_file_buffer_iter(&win.input_buffer); + for character in core.iterate_file_buffer(&it) { + if character == '\n' { break; } + + strings.write_rune(&builder, rune(character)); + } + pattern := strings.clone_to_cstring(strings.to_string(builder)); + + win.extern_matches = rg_search(pattern, "./src"); + transmute_extern_matches(win.extern_matches, &win.matches); } draw_grep_window :: proc(win: ^core.Window, state: ^core.State) { @@ -94,4 +239,26 @@ draw_grep_window :: proc(win: ^core.Window, state: ^core.State) { int(win_rec.y + win_rec.height - win_margin.y * 2), state.font, show_line_numbers = false); + + for match, index in win.matches { + text := raylib.TextFormat("%s:%d:%d: %s", match.path, match.line, match.col, match.text); + text_width := raylib.MeasureTextEx(state.font, text, f32(state.source_font_height), 0); + + if index == win.selected_match { + raylib.DrawRectangle( + i32(win_rec.x + win_margin.x), + i32(win_rec.y + win_margin.y) + i32(index * state.source_font_height), + i32(text_width.x), + i32(state.source_font_height), + theme.get_palette_raylib_color(.Background2)); + } + + raylib.DrawTextEx( + state.font, + text, + raylib.Vector2 { win_rec.x + win_margin.x, win_rec.y + win_margin.y + f32(index * state.source_font_height) }, + f32(state.source_font_height), + 0, + theme.get_palette_raylib_color(.Foreground2)); + } }