Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sllama"
version = "0.1.8"
version = "0.1.9"
edition = "2024"

[dependencies]
Expand Down
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.1.9 - 2025-06-12

_Add support for rustyline modes_

## 0.1.8 - 2025-06-09

_Refactor command handling and remove crossterm_
Expand Down
9 changes: 9 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ You can configure your sllama by creating and modifying TOML configuration locat

### Options

#### rustyline

##### mode

Switch rustyline input mode between `Emacs` and `Vi`.

Default: `Emacs`

#### model

Ollama model used
Expand Down Expand Up @@ -136,6 +144,7 @@ You can tell where you have previously responded by --- AI Response --- (added a
- [x] Integrate rustyline
- [ ] Implement completions with rustyline (commands and files)
- [ ] Support multiline input with shift + enter (using rustyline)
- [ ] Use `ollama server` and API calls instead
- [ ] Allow changing the context file during a chat
- [ ] Add support for knowledge directory
- [ ] Re-implement AI response interruption
Expand Down
38 changes: 37 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ use serde::Deserialize;
use std::path::PathBuf;
use std::{fs, io};

#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EditMode {
Emacs,
Vi,
}

impl Default for EditMode {
fn default() -> Self {
EditMode::Emacs
}
}

#[derive(Debug, Deserialize, Default)]
pub struct RustylineConfig {
#[serde(default)]
pub edit_mode: EditMode,
}

#[derive(Deserialize)]
pub struct Config {
#[serde(default = "default_model")]
Expand All @@ -29,6 +48,9 @@ pub struct Config {

#[serde(default = "default_system_prompt")]
pub(crate) system_prompt: String,

#[serde(default)]
pub(crate) rustyline: RustylineConfig,
}

fn default_model() -> String {
Expand Down Expand Up @@ -61,6 +83,17 @@ impl Config {
model: default_model(),
sllama_dir: default_sllama_dir(),
system_prompt: default_system_prompt(),
rustyline: RustylineConfig::default(),
}
}

pub fn create_rustyline_config(&self) -> rustyline::Config {
let config = rustyline::Config::builder();

// Apply edit mode setting
match self.rustyline.edit_mode {
EditMode::Emacs => config.edit_mode(rustyline::EditMode::Emacs).build(),
EditMode::Vi => config.edit_mode(rustyline::EditMode::Vi).build(),
}
}

Expand Down Expand Up @@ -102,7 +135,7 @@ fn get_home_dir() -> Result<PathBuf, &'static str> {

#[cfg(test)]
mod tests {
use crate::config::Config;
use crate::config::{Config, EditMode};

#[test]
fn test_default_config_values() {
Expand All @@ -115,5 +148,8 @@ mod tests {
// For sllama_dir, just check that it ends with "/sllama" or "\sllama"
// rather than testing the specific home directory path
assert!(config.sllama_dir.ends_with("/sllama") || config.sllama_dir.ends_with("\\sllama"));

// Check rustyline default values
matches!(config.rustyline.edit_mode, EditMode::Emacs);
}
}
6 changes: 4 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fn main() -> io::Result<()> {
let mut history = HistoryFile::new(args.history_file.clone(), config.sllama_dir.clone())?;
println!("{}", history.get_content());
println!("You're conversing with {} model", &config.model);
let mut ollama_client = OllamaClient::new(config.model, config.system_prompt);
let mut ollama_client = OllamaClient::new(config.model.clone(), config.system_prompt.clone());
println!("Press Enter during AI generation to interrupt the response.");

// Main conversation loop
Expand All @@ -84,7 +84,9 @@ fn main() -> io::Result<()> {
println!(
"\nEnter your prompt or a command (type ':q' to end or ':help' for other commands)"
);
let mut rl = match rustyline::DefaultEditor::new() {

let rustyline_config = config.create_rustyline_config();
let mut rl = match rustyline::DefaultEditor::with_config(rustyline_config) {
Ok(r) => r,
Err(e) => {
eprintln!("Error initializing rustyline: {}", e);
Expand Down