Browse Source

Improve the display of terminal buffers.

Avoid calculating absolute and relative paths from the irregular path
syntax used for terminal buffers; instead, synthesize a path based on
the terminal's current working directory and the name of the shell:

  /current/working/directory/!PID:shellName

Where `/current/working/directory/` is the shell's current working
directory (when it can be determined), `PID` is the shell's process
identifier, and `shellName` is the name of the shell with leading path
removed for brevity.  For example, a terminal running `/bin/bash` as PID
1234 in directory `/tmp` would have a path of:

  /tmp/!1234:bash

On Neovim, terminal buffers are named as:

  term://WorkingDirectoryAtTerminalLaunch//PID:shellPath

On Vim, the name contains only a `!` and the shell path:

  !/path/to/shell

However, the PID is available on Vim via `term_getjob()` and `job_info()`.

At present, the current working directory of the shell can be determined
only on Unix-like systems having a `/proc` filesystem.  For other
systems, fall back to the directory at terminal launch for Neovim, or
else on Vim's current working directory.

It appears to be difficult to determine the current working directory of
a program on Windows given the PID, so no attempt is made to determine
it for that platform.
Michael Henry 10 months ago
parent
commit
8a26c0ff26
1 changed files with 71 additions and 5 deletions
  1. 71 5
      plugin/bufexplorer.vim

+ 71 - 5
plugin/bufexplorer.vim

@@ -864,12 +864,78 @@ function! s:CalculateBufferDetails(buf)
     endif
     let buf.isterminal = getbufvar(buf._bufnr, '&buftype') == 'terminal'
     if buf.isterminal
-        let buf.fullname = name
-        let buf.isdir = 0
-    else
-        let buf.fullname = simplify(fnamemodify(name, ':p'))
-        let buf.isdir = getftype(buf.fullname) == "dir"
+        " Neovim uses paths with `term://` prefix, where the provided path
+        " is the current working directory when the terminal was launched, e.g.:
+        " - Unix:
+        "   term://~/tmp/sort//1464953:/bin/bash
+        " - Windows:
+        "   term://C:\apps\nvim-win64\bin//6408:C:\Windows\system32\cmd.exe
+        " Vim uses paths starting with `!`, with no provided path, e.g.:
+        " - Unix:
+        "   !/bin/bash
+        " - Windows:
+        "   !C:\Windows\system32\cmd.exe
+
+        " Use the terminal's current working directory as the `path`.
+        " For `shortname`, use `!PID:shellName`, prefixed with `!` as Vim does,
+        " and without the shell's path for brevity, e.g.:
+        "   `/bin/bash` -> `!bash`
+        "   `1464953:/bin/bash` -> `!1464953:bash`
+        "   `C:\Windows\system32\cmd.exe` -> `!cmd.exe`
+        "   `6408:C:\Windows\system32\cmd.exe` -> `!6408:cmd.exe`
+
+        " Neovim-style name format:
+        "   term://(cwd)//(pid):(shellPath)
+        " e.g.:
+        "   term://~/tmp/sort//1464953:/bin/bash
+        " `cwd` is the directory at terminal launch.
+        let termNameParts = matchlist(name, '\v\c^term://(.*)//(\d+):(.*)$')
+        if len(termNameParts) > 0
+            let [cwd, pidStr, shellPath] = termNameParts[1:3]
+            let pid = str2nr(pidStr)
+            let shellName = fnamemodify(shellPath, ':t')
+        else
+            " Default to Vim's current working directory.
+            let cwd = '.'
+            let shellName = fnamemodify(name, ':t')
+            let pid = -1
+            if exists('*term_getjob') && exists('*job_info')
+                let job = term_getjob(buf._bufnr)
+                if job != v:null
+                    let pid = job_info(job).process
+                endif
+            endif
+        endif
+
+        if pid < 0
+            let shortname = '!' . shellName
+        else
+            let shortname = '!' . pid . ':' . shellName
+            " On some systems having a `/proc` filesystem (e.g., Linux, *BSD,
+            " Solaris), each process has a `cwd` symlink for the current working
+            " directory.  `resolve()` will return the actual current working
+            " directory if possible; otherwise, it will return the symlink path
+            " unchanged.
+            let cwd_symlink = '/proc/' . pid . '/cwd'
+            let resolved_cwd = resolve(cwd_symlink)
+            if resolved_cwd != cwd_symlink
+                let cwd = resolved_cwd
+            endif
+        endif
+
+        let slashed_path = fnamemodify(cwd, ':p')
+        let buf.fullname = slashed_path . shortname
+        let buf.shortname = shortname
+        let homepath = fnamemodify(slashed_path, ':~:h')
+        let buf.path = homepath
+        let buf.homename = fnamemodify(buf.fullname, ':~')
+        let buf.relativepath = fnamemodify(slashed_path, ':~:.:h')
+        let buf.relativename = fnamemodify(buf.fullname, ':~:.')
+        return
     endif
+
+    let buf.fullname = simplify(fnamemodify(name, ':p'))
+    let buf.isdir = getftype(buf.fullname) == "dir"
     if buf.isdir
         " `buf.fullname` ends with a path separator; this will be
         " removed via the first `:h` applied to `buf.fullname` (except