mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-10-08 00:02:11 +00:00
fix(links): five holes review found in the new path detection
The second column of a wide character is written as a space, and the blank-cell shortcut read that as an empty cell. `logical_line_at` hands a click there back to the character that owns it, so the underline was going out on every other column of a path spelled in CJK or emoji. Read a spacer as part of the glyph it belongs to. Handing a file the built-in editor cannot read to the desktop is how a click opens a PNG. On macOS it is also how a click *runs* a program: `open` on a Mach-O binary launches it, and a build's output is full of paths to programs. A file the execute bit is set on keeps the words it had before. `explorer /select,<path>` went through `Command::arg`, which quotes the whole argument the moment the path holds a space. Explorer answers a quoted switch by opening Documents and reporting success, so "Show in Folder" silently showed the wrong folder. Write that command line by hand instead, with the switch bare and the path quoted behind it. The right-click menu resolved a path with no regard for the switch that decides whether a path underlines at all, so a pane with link detection turned off still offered to open files. Finally, the `label:` left cut peeled anything after a colon, so `branch:main` was probed as `main` and resolved against any directory of that name. Require what follows to be written like a path too. Claude-Session: https://claude.ai/code/session_01NE3M5Q94Jyxmj5Rdm9bcg4
This commit is contained in:
+72
-1
@@ -244,6 +244,47 @@ fn looks_binary(bytes: &[u8]) -> bool {
|
||||
bytes.iter().take(8192).any(|b| *b == 0)
|
||||
}
|
||||
|
||||
/// Whether handing this path to the desktop would run it rather than show it.
|
||||
///
|
||||
/// The execute bit is what `open` reads to decide between displaying a file
|
||||
/// and launching it; Windows has no such bit, so there the extension is the
|
||||
/// only thing that says so.
|
||||
fn is_program(path: &Path) -> bool {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
return std::fs::metadata(path)
|
||||
.is_ok_and(|m| m.is_file() && m.permissions().mode() & 0o111 != 0);
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
let Some(ext) = path.extension().and_then(|e| e.to_str()) else {
|
||||
return false;
|
||||
};
|
||||
matches!(
|
||||
ext.to_ascii_lowercase().as_str(),
|
||||
"exe"
|
||||
| "com"
|
||||
| "bat"
|
||||
| "cmd"
|
||||
| "scr"
|
||||
| "pif"
|
||||
| "msi"
|
||||
| "ps1"
|
||||
| "vbs"
|
||||
| "js"
|
||||
| "jse"
|
||||
| "wsf"
|
||||
| "wsh"
|
||||
| "cpl"
|
||||
| "msc"
|
||||
| "hta"
|
||||
| "reg"
|
||||
| "lnk"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum ExternalChange {
|
||||
Ignore,
|
||||
@@ -590,6 +631,10 @@ impl Tty7App {
|
||||
/// A click on a PNG or a `.zip` meant "open this", not "tell me it is not
|
||||
/// text", and on this machine the desktop knows how. A file on another
|
||||
/// machine has nobody here to hand it to, so that one gets the words.
|
||||
///
|
||||
/// A program does not: `open` on a Mach-O binary runs it, and a build's
|
||||
/// output is full of paths to programs. Clicking a word in a terminal
|
||||
/// must not be a way to execute one, so those keep the words too.
|
||||
fn open_outside_the_editor(
|
||||
&mut self,
|
||||
host_id: HostId,
|
||||
@@ -597,7 +642,7 @@ impl Tty7App {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if !host_id.is_local() || !self.can_spawn_locally(cx) {
|
||||
if !host_id.is_local() || !self.can_spawn_locally(cx) || is_program(path) {
|
||||
window.push_notification(
|
||||
t_fmt(
|
||||
L10nKey::EditorBinaryFile,
|
||||
@@ -1485,6 +1530,32 @@ impl Tty7App {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Handing a file the editor cannot read to the desktop is how a click
|
||||
/// opens a PNG. It must not be how a click runs a build's output.
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn a_file_the_desktop_would_run_is_not_handed_to_it() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let dir = std::env::temp_dir().join(format!("tty7-program-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).expect("create dir");
|
||||
let image = dir.join("shot.png");
|
||||
let program = dir.join("built");
|
||||
std::fs::write(&image, b"\x89PNG\0\0").expect("write image");
|
||||
std::fs::write(&program, b"\x7fELF\0\0").expect("write program");
|
||||
std::fs::set_permissions(&program, std::fs::Permissions::from_mode(0o755))
|
||||
.expect("mark executable");
|
||||
|
||||
assert!(!is_program(&image), "a picture is only ever shown");
|
||||
assert!(is_program(&program), "a binary would be launched");
|
||||
assert!(
|
||||
!is_program(&dir),
|
||||
"a directory is not a program, whatever its mode says"
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn language_map_covers_common_extensions() {
|
||||
for (path, lang) in [
|
||||
|
||||
Reference in New Issue
Block a user