Skip to content

Commit 65d6f3c

Browse files
committed
Fix for windows
1 parent 2bb1181 commit 65d6f3c

File tree

3 files changed

+64
-25
lines changed

3 files changed

+64
-25
lines changed

crates/modalkit/src/editing/buffer/complete.rs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,21 @@ mod tests {
427427

428428
#[test]
429429
fn test_complete_file() {
430+
fn escape(input: &str) -> Cow<'_, str> {
431+
if MAIN_SEPARATOR != '\\' {
432+
Cow::Borrowed(input)
433+
} else {
434+
Cow::Owned(input.replace('\\', "\\\\"))
435+
}
436+
}
437+
fn unescape(input: &str) -> Cow<'_, str> {
438+
if MAIN_SEPARATOR != '\\' {
439+
Cow::Borrowed(input)
440+
} else {
441+
Cow::Owned(input.replace("\\\\", "\\"))
442+
}
443+
}
444+
430445
// First, create temporary and files to complete.
431446
let tmp = TempDir::new().unwrap();
432447
let file1 = tmp.child("file1");
@@ -442,13 +457,17 @@ mod tests {
442457
let file2 = file2.to_string_lossy();
443458
let hidden = hidden.to_string_lossy();
444459
let mut cur_dir = path.clone().into_owned();
445-
cur_dir.push_str("/./");
460+
cur_dir.push(MAIN_SEPARATOR);
461+
cur_dir.push('.');
462+
cur_dir.push(MAIN_SEPARATOR);
446463
let mut parent = path.clone().into_owned();
447-
parent.push_str("/../");
464+
parent.push(MAIN_SEPARATOR);
465+
parent.push_str("..");
466+
parent.push(MAIN_SEPARATOR);
448467
let next = MoveDir1D::Next;
449468

450469
// create buffer with path to temporary directory.
451-
let (mut ebuf, gid, vwctx, mut vctx, mut store) = mkfivestr(path.as_ref());
470+
let (mut ebuf, gid, vwctx, mut vctx, mut store) = mkfivestr(escape(path.as_ref()).as_ref());
452471
vctx.persist.insert = Some(InsertStyle::Insert);
453472

454473
// Move to end of line.
@@ -459,6 +478,9 @@ mod tests {
459478

460479
// Type path separator.
461480
type_char!(ebuf, MAIN_SEPARATOR, gid, vwctx, vctx, store);
481+
if MAIN_SEPARATOR == '\\' {
482+
type_char!(ebuf, MAIN_SEPARATOR, gid, vwctx, vctx, store);
483+
}
462484

463485
// First, complete to file1.
464486
ebuf.complete_file(
@@ -468,7 +490,7 @@ mod tests {
468490
&mut store,
469491
)
470492
.unwrap();
471-
assert_eq!(ebuf.get_text().trim_end(), file1);
493+
assert_eq!(unescape(ebuf.get_text().trim_end()), file1);
472494

473495
// Then complete to file2.
474496
ebuf.complete_file(
@@ -478,7 +500,7 @@ mod tests {
478500
&mut store,
479501
)
480502
.unwrap();
481-
assert_eq!(ebuf.get_text().trim_end(), file2);
503+
assert_eq!(unescape(ebuf.get_text().trim_end()), file2);
482504

483505
// Then return to parent.
484506
ebuf.complete_file(
@@ -489,6 +511,7 @@ mod tests {
489511
)
490512
.unwrap();
491513
let result = ebuf.get_text();
514+
let result = unescape(&result);
492515
let result = Path::new(result.trim_end());
493516
assert_eq!(result, tmp.path());
494517

@@ -506,7 +529,7 @@ mod tests {
506529
&mut store,
507530
)
508531
.unwrap();
509-
assert_eq!(ebuf.get_text().trim_end(), &cur_dir);
532+
assert_eq!(unescape(ebuf.get_text().trim_end()).as_ref(), &cur_dir);
510533

511534
// Complete to "../".
512535
ebuf.complete_file(
@@ -516,7 +539,7 @@ mod tests {
516539
&mut store,
517540
)
518541
.unwrap();
519-
assert_eq!(ebuf.get_text().trim_end(), &parent);
542+
assert_eq!(unescape(ebuf.get_text().trim_end()).as_ref(), &parent);
520543

521544
// Complete to ".hidden".
522545
ebuf.complete_file(
@@ -526,7 +549,7 @@ mod tests {
526549
&mut store,
527550
)
528551
.unwrap();
529-
assert_eq!(ebuf.get_text().trim_end(), hidden);
552+
assert_eq!(unescape(ebuf.get_text().trim_end()), hidden);
530553

531554
// Return to parent.
532555
ebuf.complete_file(
@@ -537,6 +560,7 @@ mod tests {
537560
)
538561
.unwrap();
539562
let result = ebuf.get_text();
563+
let result = unescape(&result);
540564
let result = Path::new(result.trim_end());
541565
assert_eq!(result, tmp.path());
542566

@@ -554,7 +578,7 @@ mod tests {
554578
&mut store,
555579
)
556580
.unwrap();
557-
assert_eq!(ebuf.get_text().trim_end(), hidden);
581+
assert_eq!(unescape(ebuf.get_text().trim_end()), hidden);
558582
}
559583

560584
#[test]

crates/modalkit/src/editing/buffer/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,6 @@ where
14341434
mod tests {
14351435
pub use super::*;
14361436
pub use crate::editing::application::EmptyInfo;
1437-
pub use crate::editing::context::EditContextBuilder;
14381437
pub use crate::editing::store::{RegisterCell, RegisterPutFlags, Store};
14391438
pub use crate::env::vim::VimState;
14401439
pub use crate::prelude::TargetShape::{BlockWise, CharWise, LineWise};

crates/modalkit/src/editing/completion.rs

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ impl LineCompleter {
353353
}
354354

355355
mod parse {
356-
use std::borrow::Cow;
356+
use std::{borrow::Cow, path::MAIN_SEPARATOR};
357357

358358
use nom::{
359359
branch::alt,
@@ -412,6 +412,25 @@ mod parse {
412412

413413
filepath_at_end(prefix.as_ref()).ok().map(|(_, path)| path)
414414
}
415+
416+
pub fn escape_string(input: &mut String) {
417+
for c in ["\\\\", "\\ ", "\\#", "\\%", "\\|", "\\\""] {
418+
if input.contains(&c[1..2]) {
419+
*input = input.replace(&c[1..2], c);
420+
}
421+
}
422+
}
423+
424+
pub fn trailing_filename(input: &str) -> &str {
425+
let start = input.rfind(MAIN_SEPARATOR).map(|s| s + 1).unwrap_or(0);
426+
&input[start..]
427+
}
428+
429+
pub fn trailing_filename_escaped(input: &str) -> String {
430+
let mut name = trailing_filename(input).to_string();
431+
escape_string(&mut name);
432+
name
433+
}
415434
}
416435

417436
/// Complete filenames within a path leading up to the cursor.
@@ -430,7 +449,7 @@ pub fn complete_path(input: &EditRope, cursor: &mut Cursor) -> Vec<String> {
430449
}
431450

432451
if filepath.is_empty() {
433-
filepath = Cow::Borrowed("./");
452+
filepath = format!(".{MAIN_SEPARATOR}").into();
434453
}
435454

436455
let mut res = Vec::<String>::with_capacity(MAX_COMPLETIONS);
@@ -462,10 +481,10 @@ pub fn complete_path(input: &EditRope, cursor: &mut Cursor) -> Vec<String> {
462481
filepath.strip_suffix('.').is_some_and(|s| s.ends_with(MAIN_SEPARATOR))
463482
{
464483
// complete all dotfiles
465-
466484
// The .parent() and .file_name() methods treat . especially, so we
467485
// have to special-case completion of hidden files here.
468-
let _ = input.get_prefix_word_mut(cursor, &WordStyle::CharSet(|c| c != MAIN_SEPARATOR)); // TODO: fix for windows
486+
487+
cursor.left(parse::trailing_filename_escaped(filepath.as_ref()).len());
469488

470489
if let Ok(dir) = path.read_dir() {
471490
let filter = |entry: DirEntry| {
@@ -491,8 +510,7 @@ pub fn complete_path(input: &EditRope, cursor: &mut Cursor) -> Vec<String> {
491510
}
492511
} else {
493512
// complete a path
494-
495-
let _ = input.get_prefix_word_mut(cursor, &WordStyle::CharSet(|c| c != MAIN_SEPARATOR)); // TODO: fix for windows
513+
cursor.left(parse::trailing_filename_escaped(filepath.as_ref()).len());
496514

497515
let Some(prefix) = path.components().next_back() else {
498516
return vec![];
@@ -543,29 +561,27 @@ pub fn complete_path(input: &EditRope, cursor: &mut Cursor) -> Vec<String> {
543561
}
544562

545563
// Use custom sort to have `.` and `..` at the top
564+
let cur = format!(".{MAIN_SEPARATOR}");
565+
let parent = format!("..{MAIN_SEPARATOR}");
546566
res.sort_unstable_by(|a, b| {
547-
if a == "./" {
567+
if a == &cur {
548568
return Ordering::Less;
549569
}
550-
if b == "./" {
570+
if b == &cur {
551571
return Ordering::Greater;
552572
}
553-
if a == "../" {
573+
if a == &parent {
554574
return Ordering::Less;
555575
}
556-
if b == "../" {
576+
if b == &parent {
557577
return Ordering::Greater;
558578
}
559579

560580
a.cmp(b)
561581
});
562582

563583
for comp in &mut res {
564-
for c in ["\\\\", "\\ ", "\\#", "\\%", "\\|", "\\\""] {
565-
if comp.contains(&c[1..2]) {
566-
*comp = comp.replace(&c[1..2], c);
567-
}
568-
}
584+
parse::escape_string(comp);
569585
}
570586

571587
return res;

0 commit comments

Comments
 (0)