Repository navigation
[v3-linux] fileexplorer fix opening dir on Linux #4521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,97 +1,107 @@ | ||
| //go:build linux | ||
|
|
||
| package fileexplorer | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "fmt" | ||
| "net/url" | ||
| "os" | ||
| "os/exec" | ||
| "path/filepath" | ||
| "strings" | ||
| "syscall" | ||
|
|
||
| ini "gopkg.in/ini.v1" | ||
| ) | ||
|
|
||
| func explorerBinArgs(path string, selectFile bool) (string, []string, error) { | ||
| // Map of field codes to their replacements | ||
| var fieldCodes = map[string]string{ | ||
| "%d": "", | ||
| "%D": "", | ||
| "%n": "", | ||
| "%N": "", | ||
| "%v": "", | ||
| "%m": "", | ||
| "%f": path, | ||
| "%F": path, | ||
| "%u": pathToURI(path), | ||
| "%U": pathToURI(path), | ||
| } | ||
| fileManagerQuery := exec.Command("xdg-mime", "query", "default", "inode/directory") | ||
| buf := new(bytes.Buffer) | ||
| fileManagerQuery.Stdout = buf | ||
| fileManagerQuery.Stderr = nil | ||
|
|
||
| if err := fileManagerQuery.Run(); err != nil { | ||
| return fallbackExplorerBinArgs(path, selectFile) | ||
| } | ||
|
|
||
| desktopFile, err := findDesktopFile(strings.TrimSpace((buf.String()))) | ||
| if err != nil { | ||
| return fallbackExplorerBinArgs(path, selectFile) | ||
| } | ||
|
|
||
| cfg, err := ini.Load(desktopFile) | ||
| if err != nil { | ||
| // Opting to fallback rather than fail | ||
| return fallbackExplorerBinArgs(path, selectFile) | ||
| } | ||
|
|
||
| exec := cfg.Section("Desktop Entry").Key("Exec").String() | ||
| for fieldCode, replacement := range fieldCodes { | ||
| exec = strings.ReplaceAll(exec, fieldCode, replacement) | ||
| } | ||
| args := strings.Fields(exec) | ||
| if !strings.Contains(strings.Join(args, " "), path) { | ||
| args = append(args, path) | ||
| } | ||
|
|
||
| return args[0], args[1:], nil | ||
| } | ||
|
|
||
| func sysProcAttr(path string, selectFile bool) *syscall.SysProcAttr { | ||
| return &syscall.SysProcAttr{} | ||
| } | ||
|
|
||
| func fallbackExplorerBinArgs(path string, selectFile bool) (string, []string, error) { | ||
| // NOTE: The linux fallback explorer opening is not supporting file selection | ||
| path = filepath.Dir(path) | ||
| return "xdg-open", []string{path}, nil | ||
| } | ||
|
|
||
| func pathToURI(path string) string { | ||
| absPath, err := filepath.Abs(path) | ||
| if err != nil { | ||
| return path | ||
| } | ||
| return "file://" + url.PathEscape(absPath) | ||
| } | ||
|
|
||
| func findDesktopFile(xdgFileName string) (string, error) { | ||
| paths := []string{ | ||
| filepath.Join(os.Getenv("XDG_DATA_HOME"), "applications"), | ||
| filepath.Join(os.Getenv("HOME"), ".local", "share", "applications"), | ||
| "/usr/share/applications", | ||
| } | ||
|
|
||
| for _, path := range paths { | ||
| desktopFile := filepath.Join(path, xdgFileName) | ||
| if _, err := os.Stat(desktopFile); err == nil { | ||
| return desktopFile, nil | ||
| } | ||
| } | ||
| err := fmt.Errorf("desktop file not found: %s", xdgFileName) | ||
| return "", err | ||
| } | ||
| //go:build linux | ||
|
|
||
| package fileexplorer | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "fmt" | ||
| "net/url" | ||
| "os" | ||
| "os/exec" | ||
| "path/filepath" | ||
| "strings" | ||
| "syscall" | ||
|
|
||
| ini "gopkg.in/ini.v1" | ||
| ) | ||
|
|
||
| func explorerBinArgs(path string, selectFile bool) (string, []string, error) { | ||
| // Map of field codes to their replacements | ||
| var fieldCodes = map[string]string{ | ||
| "%d": "", | ||
| "%D": "", | ||
| "%n": "", | ||
| "%N": "", | ||
| "%v": "", | ||
| "%m": "", | ||
| "%f": path, | ||
| "%F": path, | ||
| "%u": pathToURI(path), | ||
| "%U": pathToURI(path), | ||
| } | ||
| fileManagerQuery := exec.Command("xdg-mime", "query", "default", "inode/directory") | ||
| buf := new(bytes.Buffer) | ||
| fileManagerQuery.Stdout = buf | ||
| fileManagerQuery.Stderr = nil | ||
|
|
||
| if err := fileManagerQuery.Run(); err != nil { | ||
| return fallbackExplorerBinArgs(path, selectFile) | ||
| } | ||
|
|
||
| desktopFile, err := findDesktopFile(strings.TrimSpace((buf.String()))) | ||
| if err != nil { | ||
| return fallbackExplorerBinArgs(path, selectFile) | ||
| } | ||
|
|
||
| cfg, err := ini.Load(desktopFile) | ||
| if err != nil { | ||
| // Opting to fallback rather than fail | ||
| return fallbackExplorerBinArgs(path, selectFile) | ||
| } | ||
|
|
||
| exec := cfg.Section("Desktop Entry").Key("Exec").String() | ||
| for fieldCode, replacement := range fieldCodes { | ||
| exec = strings.ReplaceAll(exec, fieldCode, replacement) | ||
| } | ||
| args := strings.Fields(exec) | ||
| if !strings.Contains(strings.Join(args, " "), path) { | ||
| args = append(args, path) | ||
| } | ||
|
|
||
| return args[0], args[1:], nil | ||
| } | ||
|
|
||
| func sysProcAttr(path string, selectFile bool) *syscall.SysProcAttr { | ||
| return &syscall.SysProcAttr{} | ||
| } | ||
|
|
||
| func fallbackExplorerBinArgs(path string, selectFile bool) (string, []string, error) { | ||
| // NOTE: The linux fallback explorer opening does not support file selection | ||
|
|
||
| stat, err := os.Stat(path) | ||
| if err != nil { | ||
| return "", []string{}, fmt.Errorf("stat path: %w", err) | ||
| } | ||
|
|
||
| // If the path is a file, we want to open the directory containing the file | ||
| if !stat.IsDir() { | ||
| path = filepath.Dir(path) | ||
| } | ||
|
|
||
| return "xdg-open", []string{path}, nil | ||
| } | ||
|
|
||
| func pathToURI(path string) string { | ||
| absPath, err := filepath.Abs(path) | ||
| if err != nil { | ||
| return path | ||
| } | ||
| return "file://" + url.PathEscape(absPath) | ||
| } | ||
|
Comment on lines
+84
to
+90
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix URI encoding to preserve path separators.
Use func pathToURI(path string) string {
absPath, err := filepath.Abs(path)
if err != nil {
return path
}
- return "file://" + url.PathEscape(absPath)
+ u := url.URL{
+ Scheme: "file",
+ Path: absPath,
+ }
+ return u.String()
}🤖 Prompt for AI Agents |
||
|
|
||
| func findDesktopFile(xdgFileName string) (string, error) { | ||
| paths := []string{ | ||
| filepath.Join(os.Getenv("XDG_DATA_HOME"), "applications"), | ||
| filepath.Join(os.Getenv("HOME"), ".local", "share", "applications"), | ||
| "/usr/share/applications", | ||
| } | ||
|
|
||
| for _, path := range paths { | ||
| desktopFile := filepath.Join(path, xdgFileName) | ||
| if _, err := os.Stat(desktopFile); err == nil { | ||
| return desktopFile, nil | ||
| } | ||
| } | ||
| err := fmt.Errorf("desktop file not found: %s", xdgFileName) | ||
| return "", err | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Avoid shadowing the
execpackage import.The variable name
execon line 52 shadows theexecpackage imported at line 10. This reduces code clarity and could lead to confusion.Apply this diff to rename the variable:
📝 Committable suggestion
🤖 Prompt for AI Agents