Skip to content

Commit 104e5bf

Browse files
1751 follow symlink for keybinding config (#1767)
1 parent e661ee6 commit 104e5bf

File tree

2 files changed

+156
-3
lines changed

2 files changed

+156
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323
### Fixes
2424
* fix commit dialog char count for multibyte characters ([#1726](https://github.com/extrawurst/gitui/issues/1726))
2525
* fix wrong hit highlighting in fuzzy find popup [[@UUGTech](https://github.com/UUGTech)] ([#1731](https://github.com/extrawurst/gitui/pull/1731))
26+
* fix symlink support for configuration files [[@TheBlackSheep3](https://github.com/TheBlackSheep3)] ([#1751](https://github.com/extrawurst/gitui/issues/1751))
2627

2728
## [0.23.0] - 2022-06-19
2829

src/keys/key_config.rs

Lines changed: 155 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use anyhow::Result;
22
use crossterm::event::{KeyCode, KeyModifiers};
3-
use std::{path::PathBuf, rc::Rc};
3+
use std::{fs::canonicalize, path::PathBuf, rc::Rc};
44

55
use crate::{args::get_app_config_path, strings::symbol};
66

@@ -10,6 +10,8 @@ use super::{
1010
};
1111

1212
pub type SharedKeyConfig = Rc<KeyConfig>;
13+
const KEY_LIST_FILENAME: &str = "key_bindings.ron";
14+
const KEY_SYMBOLS_FILENAME: &str = "key_symbols.ron";
1315

1416
#[derive(Default, Clone)]
1517
pub struct KeyConfig {
@@ -20,12 +22,16 @@ pub struct KeyConfig {
2022
impl KeyConfig {
2123
fn get_config_file() -> Result<PathBuf> {
2224
let app_home = get_app_config_path()?;
23-
Ok(app_home.join("key_bindings.ron"))
25+
let config_file = app_home.join(KEY_LIST_FILENAME);
26+
canonicalize(&config_file)
27+
.map_or_else(|_| Ok(config_file), Ok)
2428
}
2529

2630
fn get_symbols_file() -> Result<PathBuf> {
2731
let app_home = get_app_config_path()?;
28-
Ok(app_home.join("key_symbols.ron"))
32+
let symbols_file = app_home.join(KEY_SYMBOLS_FILENAME);
33+
canonicalize(&symbols_file)
34+
.map_or_else(|_| Ok(symbols_file), Ok)
2935
}
3036

3137
pub fn init() -> Result<Self> {
@@ -114,6 +120,9 @@ impl KeyConfig {
114120
mod tests {
115121
use super::*;
116122
use crossterm::event::{KeyCode, KeyModifiers};
123+
use std::fs;
124+
use std::io::Write;
125+
use tempfile::NamedTempFile;
117126

118127
#[test]
119128
fn test_get_hint() {
@@ -124,4 +133,147 @@ mod tests {
124133
));
125134
assert_eq!(h, "^c");
126135
}
136+
137+
#[test]
138+
fn test_symbolic_links() {
139+
let app_home = get_app_config_path().unwrap();
140+
// save current config
141+
let original_key_list_path = app_home.join(KEY_LIST_FILENAME);
142+
let renamed_key_list = if original_key_list_path.exists() {
143+
let temp = NamedTempFile::new_in(&app_home).unwrap();
144+
fs::rename(&original_key_list_path, &temp).unwrap();
145+
Some(temp)
146+
} else {
147+
None
148+
};
149+
let original_key_symbols_path =
150+
app_home.join(KEY_SYMBOLS_FILENAME);
151+
let renamed_key_symbols = if original_key_symbols_path
152+
.exists()
153+
{
154+
let temp = NamedTempFile::new_in(&app_home).unwrap();
155+
fs::rename(&original_key_symbols_path, &temp).unwrap();
156+
Some(temp)
157+
} else {
158+
None
159+
};
160+
161+
// create temporary config files
162+
let mut temporary_key_list =
163+
NamedTempFile::new_in(&app_home).unwrap();
164+
writeln!(
165+
temporary_key_list,
166+
r"
167+
(
168+
move_down: Some(( code: Char('j'), modifiers: ( bits: 2,),)),
169+
)
170+
"
171+
)
172+
.unwrap();
173+
174+
let mut temporary_key_symbols =
175+
NamedTempFile::new_in(&app_home).unwrap();
176+
writeln!(
177+
temporary_key_symbols,
178+
"
179+
(
180+
esc: Some(\"Esc\"),
181+
)
182+
"
183+
)
184+
.unwrap();
185+
186+
// testing
187+
let result = std::panic::catch_unwind(|| {
188+
let loaded_config = KeyConfig::init().unwrap();
189+
assert_eq!(
190+
loaded_config.keys.move_down,
191+
KeysList::default().move_down
192+
);
193+
assert_eq!(
194+
loaded_config.symbols.esc,
195+
KeySymbols::default().esc
196+
);
197+
198+
create_symlink(
199+
&temporary_key_symbols,
200+
&original_key_symbols_path,
201+
)
202+
.unwrap();
203+
let loaded_config = KeyConfig::init().unwrap();
204+
assert_eq!(
205+
loaded_config.keys.move_down,
206+
KeysList::default().move_down
207+
);
208+
assert_eq!(loaded_config.symbols.esc, "Esc");
209+
210+
create_symlink(
211+
&temporary_key_list,
212+
&original_key_list_path,
213+
)
214+
.unwrap();
215+
let loaded_config = KeyConfig::init().unwrap();
216+
assert_eq!(
217+
loaded_config.keys.move_down,
218+
GituiKeyEvent::new(
219+
KeyCode::Char('j'),
220+
KeyModifiers::CONTROL
221+
)
222+
);
223+
assert_eq!(loaded_config.symbols.esc, "Esc");
224+
225+
fs::remove_file(&original_key_symbols_path).unwrap();
226+
let loaded_config = KeyConfig::init().unwrap();
227+
assert_eq!(
228+
loaded_config.keys.move_down,
229+
GituiKeyEvent::new(
230+
KeyCode::Char('j'),
231+
KeyModifiers::CONTROL
232+
)
233+
);
234+
assert_eq!(
235+
loaded_config.symbols.esc,
236+
KeySymbols::default().esc
237+
);
238+
239+
fs::remove_file(&original_key_list_path).unwrap();
240+
});
241+
242+
// remove symlinks from testing if they still exist
243+
let _ = fs::remove_file(&original_key_list_path);
244+
let _ = fs::remove_file(&original_key_symbols_path);
245+
246+
// restore original config files
247+
if let Some(temp) = renamed_key_list {
248+
let _ = fs::rename(&temp, &original_key_list_path);
249+
}
250+
251+
if let Some(temp) = renamed_key_symbols {
252+
let _ = fs::rename(&temp, &original_key_symbols_path);
253+
}
254+
255+
assert!(result.is_ok());
256+
}
257+
258+
#[cfg(not(target_os = "windows"))]
259+
fn create_symlink<
260+
P: AsRef<std::path::Path>,
261+
Q: AsRef<std::path::Path>,
262+
>(
263+
original: P,
264+
link: Q,
265+
) -> Result<(), std::io::Error> {
266+
std::os::unix::fs::symlink(original, link)
267+
}
268+
269+
#[cfg(target_os = "windows")]
270+
fn create_symlink<
271+
P: AsRef<std::path::Path>,
272+
Q: AsRef<std::path::Path>,
273+
>(
274+
original: P,
275+
link: Q,
276+
) -> Result<(), std::io::Error> {
277+
std::os::windows::fs::symlink_file(original, link)
278+
}
127279
}

0 commit comments

Comments
 (0)