chrisant996 / clink-gizmos

A library of Lua scripts for use with Clink https://github.com/chrisant996/clink.
MIT License
63 stars 5 forks source link

Add terminal icons to batch terminal window (similar to PS Terminal Icons) #3

Closed eblis closed 7 months ago

eblis commented 7 months ago

Would it be possible to add file (and folder) icons to known file types in Windows CMD (batch), similar to the way PowerShell can have them ?

I don't really use PowerShell (hate it :) ) but I would like to have support for terminal icons in CMD, if possible.

Would this be a clink feature or could it be done via an addon or a replacement of dir ? (i already have an alias for ll, so in my case I would need to replace just ll)

chrisant996 commented 7 months ago

Would it be possible to add file (and folder) icons to known file types in Windows CMD (batch), similar to the way PowerShell can have them ?

I'm not going to read that project's documentation in depth, nor try to experiment to find answers to the things its documentation doesn't cover.

But I think it only affects the Get-Item and Get-ChildItem commands in Powershell.

So I think you're asking "can Clink change how CMD built-in commands work?" The answer is no.

Clink can only affect one thing in CMD: the input prompt. Nothing else.

I don't really use PowerShell (hate it :) ) but I would like to have support for terminal icons in CMD, if possible.

It's unclear what is meant by "in CMD". That sounds ambiguous and very broad. I don't think even the project you linked adds "support for terminal icons in Powershell" -- it only adds icons in the Get-Item and Get-ChildItem commands in Powershell, and not in any other parts of Powershell.

But anyway, Clink doesn't have any control over CMD or batch scripts or built-in CMD commands like dir.

Would this be a clink feature or could it be done via an addon or a replacement of dir ? (i already have an alias for ll, so in my case I would need to replace just ll)

This question isn't related to Clink, it's a question about CMD. CMD doesn't have a way to modify the behavior of the dir command like that. You could of course use a different program to list files, and then it would be a question for whatever program is being used.

You could make a doskey alias that runs the powershell get-item command whenever you type ll or dir or etc.

eblis commented 7 months ago

I was thinking of a replacement for dir, yes. I thought that maybe clink could somehow enhance the internal command.

I tried running powershell dir command, but it's a bit slow. I will try to find alternatives, looks like some of the Linux replacements for ls might work on Windows as well, I'll try them out.

chrisant996 commented 7 months ago

@eblis Also, this might be interesting to you:

You didn't mention displaying completions. But if you want "file icons" to show up when displaying completions in Clink, then that's a whole other complicated thing to attempt. Because each match generator is responsible for producing matches. How could a plugin safely hook into all match generators, without knowing anything about them or how they're displaying matches or formatting the matches? Completions can show things other than files or dirs, and it's unclear what should happen, especially when a list of matches contains some files or dirs and also some other things that aren't files or dirs (and how to "figure out" what the other things are, to be able to choose icons for them).

With that said, you could replace clink.filematches() and clink.dirmatches() with your own custom functions that provide icons, and that would gain icons for most file/dir completions (but not all).

Beware: Replacing functions in Lua is a very advanced technique, and can easily cause big problems. And some scripts that use clink.filematches() or clink.dirmatches() might not be compatible with having those functions behave differently than the documentation says they behave.

But if you want to experiment, then for example here is a script that illustrates the general approach. It uses one icon for files, and another for directories. You could extend it to look at file extensions, well-known directory names, readonly or hidden or system or symlinks or etc. You could even extend it with a way to customize what icons are used for file extensions.

(Also, watch out -- it's easy to accidentally make the comparisons for choosing icons exponentially slow down collecting matches.)

-- This is a sample script that replaces the clink.dirmatches() and
-- clink.filematches() functions and adds "icons" for file and directory
-- matches.
--
-- WARNING:  This makes clink.dirmatches() and clink.filematches() behave
--           differently than how they're documented.  That can break scripts,
--           if they expect the functions to behave exactly how they're
--           documented.
--
-- OTHER CAVEATS:
--  - This can be incompatible with completion scripts that generate custom
--    display strings themselves.
--  - This prevents the colored-completion-prefix Readline config variable in
--    the .inputrc file from coloring the prefix.

local function add_icons(matches)
    for _, m in ipairs(matches) do
        if m.type then
            local icon, text

            -- See documentation for info on how match type strings work.
            -- https://chrisant996.github.io/clink/clink.html#builder:addmatch

            -- Choose icons for file and directory matches.
            if m.type:find("file") then
                icon = "ï’¥"
                text = path.getname(m.match)
            elseif m.type:find("dir") then
                icon = ""
                text = m.match:match("[^/\\]*[/\\]$")
            end

            -- If an icon was chosen, jam it together with a color and the match
            -- text, and make a custom display string.
            if icon and text then
                local color = rl.getmatchcolor(m)
                m.display = "\x1b[m"..color..icon.." "..text
            end
        end
    end
end

-- WARNING:  The technique of replacing functions is possible in Lua, but it can
--           very easily break scripts that use the functions.  There's no way
--           for a replacement function to be completely safe:  since it changes
--           the behavior of the replaced function, that can inherently break
--           scripts that call the function.

-- Remember the original functions, so the new functions can call the original
-- functions to do the work of actually generating the matches.
local original_dirmatches = clink.dirmatches
local original_filematches = clink.filematches

-- Replace the clink.dirmatches() function with a variation that adds icons.
clink.dirmatches = function (word)
    local matches = original_dirmatches(word)
    add_icons(matches)
    return matches
end

-- Replace the clink.filematches() function with a variation that adds icons.
clink.filematches = function (word)
    local matches = original_filematches(word)
    add_icons(matches)
    return matches
end

ALSO:

I'll look into both of those opportunities.

But, the original request isn't about displaying completions, so this additional information isn't directly relevant to the original question.

eblis commented 7 months ago

If anyone else stumbles on this and wants icons you can look into: logo-ls - works on Windows, also has icons. You can create an alias with something like doskey ll=logo-ls -l $*, replace ll with the command you want.

Some others: Exa which seems to be dead Eza which is a fork of Exa

colorls - need to install Ruby, interpreted :( lsd - looks nice, but I couldn't get it to work successfully on Windows.

bw1faeh0 commented 7 months ago

eza officially supports windows. I’m using it on all my windows machines.

chrisant996 commented 7 months ago

eza has some interesting features. The open issues look like it's got some stability kinks and some issues with "grid" layout.

For 18 years I've been using a custom private replacement for dir, but I've never published it or gotten it ready for open source (I'd want to get it license-clean for an MIT license first). It's native C++ and it's very carefully optimized to minimize file system accesses for the specific combination of flags used in any given invocation. It's fast, like dir itself, and adds a bunch of features dir lacks.

I may work on that, and adding a couple feature from eza that it didn't have yet (specifically color scale, icons, git file status, and tree view). As far as I can see so far, it already has all the other eza features, and a bunch that eza doesn't have. It's Windows-native (and Windows-only, just like CMD), and it fully handles Windows attributes and path separators and conventions and etc. If/when I ever get it ready for publishing, I'll add a link here.

...but don't hold your breath; it's more of an "if" than a "when"...

chrisant996 commented 7 months ago

Update:

chrisant996 commented 6 months ago

@eblis Hey thank you very much for asking about this. It gave me some interesting and fun learning opportunities. Especially, somehow I hadn't heard of the Oklab color space yet, and that's super useful.



The matchicons.lua script I wrote is working well for me (it's in the clink-gizmos repo), and it adds icons into most Clink file completions. There's also an update for the clink-completions repo coming soon which adds icons into some of the git completions as well. Icons only show up after explicitly opting in by running clink set matchicons.enable true (after installing the scripts, of course).

And my CMD DIR replacement program is just about ready to publish. It now has git support, and it essentially offers a superset of what eza does (except I haven't hooked up tree view yet, and I probably won't get to that for a few weeks because frankly I've only used text mode tree list programs maybe 3 times in 30 years). It even lets you customize the layout of the file list display -- you can choose columns, arrange them in any order, and choose various formats for the columns. I haven't heard of any other directory listing programs that let you customize them that way.

One notable difference is that eza seems to use the libgit2 library instead of using the real git program. That's a problem for me because libgit2 isn't fully compatible with some git changes in recent years, and too often it ends up giving inconsistent or out of date results. So my dirx program invokes the real git program. That makes its git mode slightly slower than eza's, but it's more accurate and more compatible.

chrisant996 commented 6 months ago

DirX is now available publicly: https://github.com/chrisant996/dirx/releases.

Quick examples:

image

and

image

eblis commented 6 months ago

The matchicons.lua script I wrote is working well for me (it's in the clink-gizmos repo), and it adds icons into most Clink file completions. There's also an update for the clink-completions repo coming soon which adds icons into some of the git completions as well. Icons only show up after explicitly opting in by running clink set matchicons.enable true (after installing the scripts, of course).

This looks great.

Is there any chance it could work when fzf is enabled as well ? (of course I'd have another request :))) )

If not I will probably try to configure fzf for all suggestions except ctrl + space

chrisant996 commented 6 months ago

Is there any chance it could work when fzf is enabled as well ? (of course I'd have another request :))) )

Fzf gets the possible file completions by running the dir command.

You'd have to configure it to use a different custom directory program that shows icons (like dirx or eza). And you'd have to teach fzf how to strip icons before returning a match.

In fzf the text you see is the text that gets returned as the text to insert -- if you see icons, it'll insert icons. Making the fzf.lua script try to strip icons is possible, but involves fairly big chunk of extra work, and it would always be fragile (could easily miss stripping icons, and could easily strip something that part of an actual filename).

I'm not going to wade into that, but others are welcome to work on that and share a PR.

eblis commented 6 months ago

I think you could have file names starting with those characters too, it would be hard to tell the difference.

I've configured fzf to be enabled for all hotkeys except ctrl + space, everything looks good so far

chrisant996 commented 6 months ago

@eblis actually, overnight I realized there's a way that can work reliably -- as long as the user configures things properly.

The next update to Clink will include a change that lets fzf.lua pass icons to fzf for custom completion lists (like for git checkout) and also be able to insert the correct actual exact match text (without the icon).

And for the file and directory matches collected by the dir command, the next update to the fzf.lua script in the clink-gizmos and clink-fzf repos will enable you to configure that to work with icons.

Refer to docs at https://github.com/chrisant996/clink-fzf for how to set up the icons in fzf.

chrisant996 commented 6 months ago

@eblis the necessary updates to Clink, DirX, and clink-gizmos (and clink-fzf) have been published.

eblis commented 6 months ago

Looks good, but I think there's a problem when listing folders for a folder with git (or I would assume any folder that starts with .).

This is me listing (alt + c) folders for clink-gizmos folder: image

And this is after trying to navigate to it. image

It doesn't have all the in-between folder names, just the last one.

I tried to put this comment out as soon as possible so maybe you're still around. I'll edit it later once I have more information

EDIT: Hmm, the problem might be that the recursive option only lists the current folder, shouldn't it list the whole (relative) path from cwd to that folder ? So in my case .git/refs/heads

chrisant996 commented 6 months ago

What version of dirx are you using?

~The docs say you need v0.8. It looks like you're using v0.7 or v0.6.~

~v0.8 specifically fixes an issue with only listing the leafmost dir name. It has nothing to do with folders starting with ., it was due to a typo while doing some refactoring for a different feature.~

~Double check that the versions of Clink, DirX, and clink-gizmos (or clink-fzf) are at least the version number listed in the requirements in the docs.~

UPDATE: ugh, my bad - I made a last minute improvement ... but accidentally broke the fix for the partial paths.

chrisant996 commented 6 months ago

(P.S. you can also choose flags in the FZF_..._COMMAND environment variables to enable or disable recursing into hidden directories, for example. Depending on personal preference you may or may not want to see hidden directories (and their contents) listed in the fzf completion lists.)

chrisant996 commented 6 months ago

@eblis sorry about that, you can try again with dirx v0.9 and the recursive search should work properly.

eblis commented 6 months ago

Hey, no problem. Thanks for taking the time to implement this.

Everything looks excellent so far, with a small comment if I may nitpick :) On Linux machines if you list files (ctrl + t) or folders (alt + c) the paths are all relative to your current path, with clink the paths are always absolute by default. I modified the dirx commands to use --bare-relative, i like it better to see the paths relative to my current path. Maybe you could add an example with --bare-relative in your fzf documentation, explaining you can view absolute paths or relative paths (and can also combine with icons for maximum wow effect).

--

Yesterday I started looking into adding columns to fzf output, I saw you can do pretty nice stuff with the tables and wanted to see if I could show the icons with that, but hit a snag when i noticed that dirx wouldn't show icons in bare mode (0.6 was available at that time). Would have been overkill just for the icons, but I might revisit these later, the previewing stuff looks nice, could be useful for previewing folder contents (maybe files as well, I see that bat is available for Windows too).

eblis commented 6 months ago

I couldn't help myself and I started looking into the preview stuff in fzf and came up with this:

set FZF_CTRL_T_OPTS=--preview-window "right:40%%,border-left" --bind "ctrl-/:change-preview-window(right:70%%|hidden|)" --preview "preview.bat {2..}"
set FZF_ALT_C_OPTS=--preview-window "right:40%%,border-left" --bind "ctrl-/:change-preview-window(right:70%%|hidden|)" --preview "preview.bat {2..}"

This requires that you have bat and eza installed (I'm using eza for the preview because of the tree functionality) and also a script that chooses which kind of preview to show, depending on whether the path is a file or directory:

@echo off

if exist %1\* goto isdir
goto isfile

:isfile
bat --color=always --style=numbers --line-range=:500 %1
goto exit

:isdir
eza --tree --level=3 --icons=always %1

:exit
chrisant996 commented 6 months ago

Everything looks excellent so far, with a small comment if I may nitpick :) On Linux machines if you list files (ctrl + t) or folders (alt + c) the paths are all relative to your current path, with clink the paths are always absolute by default. I modified the dirx commands to use --bare-relative, i like it better to see the paths relative to my current path. Maybe you could add an example with --bare-relative in your fzf documentation, explaining you can view absolute paths or relative paths (and can also combine with icons for maximum wow effect).

--bare-relative is just in prototype at the moment. It's not doing the full thing yet, so it's premature to mention it in the clink-fzf documentation yet. You'll notice if you do something like blah ..\Ctrl-T then it'll still list full path names.

chrisant996 commented 6 months ago

Oh, unexpected ... in my Ubuntu install, fzf in bash completely ignores the word that's been entered so far.

blah foo/bar/xyzCtrl-T lists everything below the current directory, and choosing an entry inserts it literally, and the input line becomes gibberish like blah foo/bar/xyzsomerealdirectory/somefile.

That seems pretty not great. Do people actually like that behavior?

I would expect fzf to only list files matching foo/bar/xyz*, and to delete the foo/bar/xyz before inserting the selected match. And that's how I implemented the fzf integration in the fzf.lua script.

chrisant996 commented 6 months ago

Oh, unexpected ... in my Ubuntu install, fzf in bash completely ignores the word that's been entered so far.

Ah, apparently fzf doesn't (can't?) work like that in bash or zsh, but it does work how I expected in fish. And Clink definitely leans more towards fish in how it approaches things, so that makes sense.