First one-shot pass

This commit is contained in:
2026-02-10 14:29:23 -05:00
parent 739d79d6a2
commit f65e3d8413
48 changed files with 5409 additions and 2 deletions

View File

@@ -0,0 +1,122 @@
use rand::rngs::SmallRng;
use rand::Rng;
use crate::engine::filter::CharFilter;
use crate::generator::TextGenerator;
pub struct CodeSyntaxGenerator {
rng: SmallRng,
language: String,
}
impl CodeSyntaxGenerator {
pub fn new(rng: SmallRng, language: &str) -> Self {
Self {
rng,
language: language.to_string(),
}
}
fn rust_snippets() -> Vec<&'static str> {
vec![
"fn main() { println!(\"hello\"); }",
"let mut x = 0; x += 1;",
"for i in 0..10 { println!(\"{}\", i); }",
"if x > 0 { return true; }",
"match val { Some(x) => x, None => 0 }",
"struct Point { x: f64, y: f64 }",
"impl Point { fn new(x: f64, y: f64) -> Self { Self { x, y } } }",
"let v: Vec<i32> = vec![1, 2, 3];",
"fn add(a: i32, b: i32) -> i32 { a + b }",
"use std::collections::HashMap;",
"pub fn process(input: &str) -> Result<String, Error> { Ok(input.to_string()) }",
"let result = items.iter().filter(|x| x > &0).map(|x| x * 2).collect::<Vec<_>>();",
"enum Color { Red, Green, Blue }",
"trait Display { fn show(&self) -> String; }",
"while let Some(item) = stack.pop() { process(item); }",
"#[derive(Debug, Clone)] struct Config { name: String, value: i32 }",
]
}
fn python_snippets() -> Vec<&'static str> {
vec![
"def main(): print(\"hello\")",
"for i in range(10): print(i)",
"if x > 0: return True",
"class Point: def __init__(self, x, y): self.x = x",
"import os; path = os.path.join(\"a\", \"b\")",
"result = [x * 2 for x in items if x > 0]",
"with open(\"file.txt\") as f: data = f.read()",
"def add(a: int, b: int) -> int: return a + b",
"try: result = process(data) except ValueError as e: print(e)",
"from collections import defaultdict",
"lambda x: x * 2 + 1",
"dict_comp = {k: v for k, v in pairs.items()}",
]
}
fn javascript_snippets() -> Vec<&'static str> {
vec![
"const x = 42; console.log(x);",
"function add(a, b) { return a + b; }",
"const arr = [1, 2, 3].map(x => x * 2);",
"if (x > 0) { return true; }",
"for (let i = 0; i < 10; i++) { console.log(i); }",
"class Point { constructor(x, y) { this.x = x; this.y = y; } }",
"const { name, age } = person;",
"async function fetch(url) { const res = await get(url); return res.json(); }",
"const obj = { ...defaults, ...overrides };",
"try { parse(data); } catch (e) { console.error(e); }",
"export default function handler(req, res) { res.send(\"ok\"); }",
"const result = items.filter(x => x > 0).reduce((a, b) => a + b, 0);",
]
}
fn go_snippets() -> Vec<&'static str> {
vec![
"func main() { fmt.Println(\"hello\") }",
"for i := 0; i < 10; i++ { fmt.Println(i) }",
"if err != nil { return err }",
"type Point struct { X float64; Y float64 }",
"func add(a, b int) int { return a + b }",
"import \"fmt\"",
"result := make([]int, 0, 10)",
"switch val { case 1: return \"one\" default: return \"other\" }",
"go func() { ch <- result }()",
"defer file.Close()",
]
}
fn get_snippets(&self) -> Vec<&'static str> {
match self.language.as_str() {
"rust" => Self::rust_snippets(),
"python" => Self::python_snippets(),
"javascript" | "js" => Self::javascript_snippets(),
"go" => Self::go_snippets(),
_ => Self::rust_snippets(),
}
}
}
impl TextGenerator for CodeSyntaxGenerator {
fn generate(
&mut self,
_filter: &CharFilter,
_focused: Option<char>,
word_count: usize,
) -> String {
let snippets = self.get_snippets();
let mut result = Vec::new();
let target_words = word_count;
let mut current_words = 0;
while current_words < target_words {
let idx = self.rng.gen_range(0..snippets.len());
let snippet = snippets[idx];
current_words += snippet.split_whitespace().count();
result.push(snippet);
}
result.join(" ")
}
}

View File

@@ -0,0 +1,41 @@
use crate::engine::filter::CharFilter;
use crate::generator::TextGenerator;
#[allow(dead_code)]
pub struct GitHubCodeGenerator {
cached_snippets: Vec<String>,
current_idx: usize,
}
impl GitHubCodeGenerator {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
cached_snippets: Vec::new(),
current_idx: 0,
}
}
}
impl Default for GitHubCodeGenerator {
fn default() -> Self {
Self::new()
}
}
impl TextGenerator for GitHubCodeGenerator {
fn generate(
&mut self,
_filter: &CharFilter,
_focused: Option<char>,
_word_count: usize,
) -> String {
if self.cached_snippets.is_empty() {
return "// GitHub code fetching not yet configured. Use settings to add a repository."
.to_string();
}
let snippet = self.cached_snippets[self.current_idx % self.cached_snippets.len()].clone();
self.current_idx += 1;
snippet
}
}

12
src/generator/mod.rs Normal file
View File

@@ -0,0 +1,12 @@
pub mod code_syntax;
pub mod github_code;
pub mod passage;
pub mod phonetic;
pub mod transition_table;
use crate::engine::filter::CharFilter;
pub trait TextGenerator {
fn generate(&mut self, filter: &CharFilter, focused: Option<char>, word_count: usize)
-> String;
}

49
src/generator/passage.rs Normal file
View File

@@ -0,0 +1,49 @@
use crate::engine::filter::CharFilter;
use crate::generator::TextGenerator;
const PASSAGES: &[&str] = &[
"the quick brown fox jumps over the lazy dog and then runs across the field while the sun sets behind the distant hills",
"it was the best of times it was the worst of times it was the age of wisdom it was the age of foolishness",
"in the beginning there was nothing but darkness and then the light appeared slowly spreading across the vast empty space",
"she walked along the narrow path through the forest listening to the birds singing in the trees above her head",
"the old man sat on the bench watching the children play in the park while the autumn leaves fell softly around him",
"there is nothing either good or bad but thinking makes it so for the mind is its own place and in itself can make a heaven of hell",
"to be or not to be that is the question whether it is nobler in the mind to suffer the slings and arrows of outrageous fortune",
"all that glitters is not gold and not all those who wander are lost for the old that is strong does not wither",
"the river flowed quietly through the green valley and the mountains rose high on either side covered with trees and snow",
"a long time ago in a land far away there lived a wise king who ruled his people with kindness and justice",
"the rain fell steadily on the roof making a soft drumming sound that filled the room and made everything feel calm",
"she opened the door and stepped outside into the cool morning air breathing deeply as the first light of dawn appeared",
"he picked up the book and began to read turning the pages slowly as the story drew him deeper and deeper into its world",
"the stars shone brightly in the clear night sky and the moon cast a silver light over the sleeping town below",
"they gathered around the fire telling stories and laughing while the wind howled outside and the snow piled up against the door",
];
pub struct PassageGenerator {
current_idx: usize,
}
impl PassageGenerator {
pub fn new() -> Self {
Self { current_idx: 0 }
}
}
impl Default for PassageGenerator {
fn default() -> Self {
Self::new()
}
}
impl TextGenerator for PassageGenerator {
fn generate(
&mut self,
_filter: &CharFilter,
_focused: Option<char>,
_word_count: usize,
) -> String {
let passage = PASSAGES[self.current_idx % PASSAGES.len()];
self.current_idx += 1;
passage.to_string()
}
}

169
src/generator/phonetic.rs Normal file
View File

@@ -0,0 +1,169 @@
use rand::rngs::SmallRng;
use rand::Rng;
use crate::engine::filter::CharFilter;
use crate::generator::transition_table::TransitionTable;
use crate::generator::TextGenerator;
pub struct PhoneticGenerator {
table: TransitionTable,
rng: SmallRng,
}
impl PhoneticGenerator {
pub fn new(table: TransitionTable, rng: SmallRng) -> Self {
Self { table, rng }
}
fn pick_weighted_from(
rng: &mut SmallRng,
options: &[(char, f64)],
filter: &CharFilter,
) -> Option<char> {
let filtered: Vec<(char, f64)> = options
.iter()
.filter(|(ch, _)| filter.is_allowed(*ch))
.copied()
.collect();
if filtered.is_empty() {
return None;
}
let total: f64 = filtered.iter().map(|(_, w)| w).sum();
if total <= 0.0 {
return None;
}
let mut roll = rng.gen_range(0.0..total);
for (ch, weight) in &filtered {
roll -= weight;
if roll <= 0.0 {
return Some(*ch);
}
}
Some(filtered.last().unwrap().0)
}
fn generate_word(&mut self, filter: &CharFilter, focused: Option<char>) -> String {
let min_len = 3;
let max_len = 10;
let mut word = String::new();
let start_char = if let Some(focus) = focused {
if self.rng.gen_bool(0.4) {
let probs = self.table.get_next_probs(' ', focus).cloned();
if let Some(probs) = probs {
let filtered: Vec<(char, f64)> = probs
.iter()
.filter(|(ch, _)| filter.is_allowed(*ch))
.copied()
.collect();
if !filtered.is_empty() {
word.push(focus);
Self::pick_weighted_from(&mut self.rng, &filtered, filter)
} else {
None
}
} else {
Some(focus)
}
} else {
None
}
} else {
None
};
if word.is_empty() {
let starters: Vec<(char, f64)> = filter
.allowed
.iter()
.map(|&ch| {
(
ch,
if ch == 'e' || ch == 't' || ch == 'a' {
3.0
} else {
1.0
},
)
})
.collect();
if let Some(ch) = Self::pick_weighted_from(&mut self.rng, &starters, filter) {
word.push(ch);
} else {
return "the".to_string();
}
}
if let Some(ch) = start_char {
word.push(ch);
}
while word.len() < max_len {
let chars: Vec<char> = word.chars().collect();
let len = chars.len();
let (prev, curr) = if len >= 2 {
(chars[len - 2], chars[len - 1])
} else {
(' ', chars[len - 1])
};
let space_prob = 1.3f64.powi(word.len() as i32 - min_len as i32);
if word.len() >= min_len
&& self
.rng
.gen_bool((space_prob / (space_prob + 5.0)).min(0.8))
{
break;
}
let probs = self.table.get_next_probs(prev, curr).cloned();
if let Some(probs) = probs {
if let Some(next) = Self::pick_weighted_from(&mut self.rng, &probs, filter) {
word.push(next);
} else {
break;
}
} else {
let vowels: Vec<(char, f64)> = ['a', 'e', 'i', 'o', 'u']
.iter()
.filter(|&&v| filter.is_allowed(v))
.map(|&v| (v, 1.0))
.collect();
if let Some(v) = Self::pick_weighted_from(&mut self.rng, &vowels, filter) {
word.push(v);
} else {
break;
}
}
}
if word.is_empty() {
"the".to_string()
} else {
word
}
}
}
impl TextGenerator for PhoneticGenerator {
fn generate(
&mut self,
filter: &CharFilter,
focused: Option<char>,
word_count: usize,
) -> String {
let mut words: Vec<String> = Vec::new();
for _ in 0..word_count {
words.push(self.generate_word(filter, focused));
}
words.join(" ")
}
}

View File

@@ -0,0 +1,98 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TransitionTable {
pub transitions: HashMap<(char, char), Vec<(char, f64)>>,
}
impl TransitionTable {
pub fn new() -> Self {
Self {
transitions: HashMap::new(),
}
}
pub fn add(&mut self, prev: char, curr: char, next: char, weight: f64) {
self.transitions
.entry((prev, curr))
.or_default()
.push((next, weight));
}
pub fn get_next_probs(&self, prev: char, curr: char) -> Option<&Vec<(char, f64)>> {
self.transitions.get(&(prev, curr))
}
pub fn build_english() -> Self {
let mut table = Self::new();
let common_patterns: &[(&str, f64)] = &[
("the", 10.0), ("and", 8.0), ("ing", 7.0), ("tion", 6.0), ("ent", 5.0),
("ion", 5.0), ("her", 4.0), ("for", 4.0), ("are", 4.0), ("his", 4.0),
("hat", 3.0), ("tha", 3.0), ("ere", 3.0), ("ate", 3.0), ("ith", 3.0),
("ver", 3.0), ("all", 3.0), ("not", 3.0), ("ess", 3.0), ("est", 3.0),
("rea", 3.0), ("sta", 3.0), ("ted", 3.0), ("com", 3.0), ("con", 3.0),
("oun", 2.5), ("pro", 2.5), ("oth", 2.5), ("igh", 2.5), ("ore", 2.5),
("our", 2.5), ("ine", 2.5), ("ove", 2.5), ("ome", 2.5), ("use", 2.5),
("ble", 2.0), ("ful", 2.0), ("ous", 2.0), ("str", 2.0), ("tri", 2.0),
("ght", 2.0), ("whi", 2.0), ("who", 2.0), ("hen", 2.0), ("ter", 2.0),
("man", 2.0), ("men", 2.0), ("ner", 2.0), ("per", 2.0), ("pre", 2.0),
("ran", 2.0), ("lin", 2.0), ("kin", 2.0), ("din", 2.0), ("sin", 2.0),
("out", 2.0), ("ind", 2.0), ("ith", 2.0), ("ber", 2.0), ("der", 2.0),
("end", 2.0), ("hin", 2.0), ("old", 2.0), ("ear", 2.0), ("ain", 2.0),
("ant", 2.0), ("urn", 2.0), ("ell", 2.0), ("ill", 2.0), ("ade", 2.0),
("igh", 2.0), ("ong", 2.0), ("ung", 2.0), ("ast", 2.0), ("ist", 2.0),
("ust", 2.0), ("ost", 2.0), ("ard", 2.0), ("ord", 2.0), ("art", 2.0),
("ort", 2.0), ("ect", 2.0), ("act", 2.0), ("ack", 2.0), ("ick", 2.0),
("ock", 2.0), ("uck", 2.0), ("ash", 2.0), ("ish", 2.0), ("ush", 2.0),
("anc", 1.5), ("enc", 1.5), ("inc", 1.5), ("onc", 1.5), ("unc", 1.5),
("unt", 1.5), ("int", 1.5), ("ont", 1.5), ("ent", 1.5), ("ment", 1.5),
("ness", 1.5), ("less", 1.5), ("able", 1.5), ("ible", 1.5), ("ting", 1.5),
("ring", 1.5), ("sing", 1.5), ("king", 1.5), ("ning", 1.5), ("ling", 1.5),
("wing", 1.5), ("ding", 1.5), ("ping", 1.5), ("ging", 1.5), ("ving", 1.5),
("bing", 1.5), ("ming", 1.5), ("fing", 1.0), ("hing", 1.0), ("cing", 1.0),
];
for &(pattern, weight) in common_patterns {
let chars: Vec<char> = pattern.chars().collect();
for window in chars.windows(3) {
table.add(window[0], window[1], window[2], weight);
}
}
let vowels = ['a', 'e', 'i', 'o', 'u'];
let consonants = [
'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'v',
'w', 'x', 'y', 'z',
];
for &c in &consonants {
for &v in &vowels {
table.add(' ', c, v, 1.0);
table.add(v, c, 'e', 0.5);
for &v2 in &vowels {
table.add(c, v, v2.to_ascii_lowercase(), 0.3);
}
for &c2 in &consonants {
table.add(v, c, c2, 0.2);
}
}
}
for &v in &vowels {
for &c in &consonants {
table.add(' ', v, c, 0.5);
}
}
table
}
}
impl Default for TransitionTable {
fn default() -> Self {
Self::new()
}
}