Skip to content

Commit

Permalink
feat: implement showing of git branch in the prompt (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
certik authored Sep 7, 2024
1 parent 233b7a2 commit 73cf8fb
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 2 deletions.
87 changes: 87 additions & 0 deletions crates/deno_task_shell/src/shell/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::borrow::Cow;
use std::collections::HashMap;
use std::fs;
use std::io::Read;
use std::io::Write;
use std::path::Path;
Expand Down Expand Up @@ -30,6 +31,11 @@ pub struct ShellState {
commands: Rc<HashMap<String, Rc<dyn ShellCommand>>>,
/// Token to cancel execution.
token: CancellationToken,
/// Git repository handling.
git_repository: bool, // Is `cwd` inside a git repository?
git_root: PathBuf, // Path to the root (`$git_root/.git/HEAD` exists)
git_branch: String, // Contents of `$git_root/.git/HEAD`
last_command_cd: bool, // Was last command a `cd` (thus git_branch is current)?
}

impl ShellState {
Expand All @@ -47,6 +53,10 @@ impl ShellState {
cwd: PathBuf::new(),
commands: Rc::new(commands),
token: CancellationToken::default(),
git_repository: false,
git_root: PathBuf::new(),
git_branch: String::new(),
last_command_cd: false,
};
// ensure the data is normalized
for (name, value) in env_vars {
Expand All @@ -60,6 +70,22 @@ impl ShellState {
&self.cwd
}

pub fn git_repository(&self) -> bool {
self.git_repository
}

pub fn git_root(&self) -> &PathBuf {
&self.git_root
}

pub fn git_branch(&self) -> &String {
&self.git_branch
}

pub fn last_command_cd(&self) -> bool {
self.last_command_cd
}

pub fn env_vars(&self) -> &HashMap<String, String> {
&self.env_vars
}
Expand All @@ -76,15 +102,75 @@ impl ShellState {
.or_else(|| self.shell_vars.get(name.as_ref()))
}

// Update self.git_branch using self.git_root
pub fn update_git_branch(&mut self) {
if self.git_repository {
match fs::read_to_string(self.git_root().join(".git/HEAD")) {
Ok(contents) => {
// The git root can still be read, update the git branch
self.git_branch = contents.trim().to_string();
}
Err(_) => {
// The git root can no longer be read
// (the `.git/HEAD` was removed in the meantime)
self.git_repository = false;
self.git_branch = "".to_string();
self.git_root = "".to_string().into();
}
};
}
}

// TODO: set_cwd() is being called twice
pub fn set_cwd(&mut self, cwd: &Path) {
self.cwd = cwd.to_path_buf();
// $PWD holds the current working directory, so we keep cwd and $PWD in sync
self
.env_vars
.insert("PWD".to_string(), self.cwd.display().to_string());
// Handle a git repository
// First read the current directory's `.git/HEAD`
match fs::read_to_string(cwd.join(".git/HEAD")) {
Ok(contents) => {
// We are in a git repository in the git root dir
self.git_repository = true;
self.git_branch = contents.trim().to_string();
self.git_root = cwd.to_path_buf();
}
Err(_) => {
if self.git_repository
&& cwd
.display()
.to_string()
.starts_with(&self.git_root.display().to_string())
{
// We moved inside the same git repository, but we are not
// in the git root dir
self.update_git_branch();
} else {
// We didn't move within the same git repository,
// and there is no `.git` present.
// Consequently, we:
// * Either moved into a subdirectory of a git repository from
// outside
// * Or moved into a directory that is not inside git repository
// In the first case we need to recursively search to find the
// root. This might be slow, so we want to be smart and use the
// old directory to eliminate search in case we are moving up or
// down from the same root. For now we will set no git
// repository, which is incorrect for the first case, but will
// be fast for the most common use of not being inside a git
// repository.
self.git_repository = false;
self.git_branch = "".to_string();
self.git_root = "".to_string().into();
}
}
};
}

pub fn apply_changes(&mut self, changes: &[EnvChange]) {
self.last_command_cd = false;
for change in changes {
self.apply_change(change);
}
Expand All @@ -106,6 +192,7 @@ impl ShellState {
}
EnvChange::Cd(new_dir) => {
self.set_cwd(new_dir);
self.last_command_cd = true;
}
}
}
Expand Down
21 changes: 19 additions & 2 deletions crates/shell/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,28 @@ async fn interactive() -> anyhow::Result<()> {
let home_str = home
.to_str()
.context("Couldn't convert home directory path to UTF-8 string")?;
if !state.last_command_cd() {
state.update_git_branch();
}
let mut git_branch: String = "".to_string();
if state.git_repository() {
git_branch = match state.git_branch().strip_prefix("ref: refs/heads/") {
Some(stripped) => stripped.to_string(),
None => {
let mut hash = state.git_branch().to_string();
if hash.len() > 7 {
hash = hash[0..7].to_string() + "...";
}
hash
}
};
git_branch = "(".to_owned() + &git_branch + ")";
}

let prompt = cwd
.strip_prefix(home_str)
.map(|stripped| format!("~{}$ ", stripped.replace('\\', "/")))
.unwrap_or_else(|| format!("{}$ ", cwd));
.map(|stripped| format!("~{}{git_branch}$ ", stripped.replace('\\', "/")))
.unwrap_or_else(|| format!("{}{git_branch}$ ", cwd));
rl.readline(&prompt)
};

Expand Down

0 comments on commit 73cf8fb

Please sign in to comment.