jarun / nnn

n³ The unorthodox terminal file manager
BSD 2-Clause "Simplified" License
19.1k stars 760 forks source link

ToDo list #427

Closed jarun closed 4 years ago

jarun commented 4 years ago

Rolled from #386.

For next release

Proposed features and tasks (up for grabs)

Anything else which would add value (please discuss in this thread).

List of completed features and tasks.

ghistes commented 4 years ago

My absolutely biggest usability problem at the moment is that I search, don't find what I want and press "up" to access the search history.

In nnn this means that you don't access the search history but move up in the file-list, the status bar is displayed again and your previous search-term is gone for good.

So a big gain for me would be for nnn to at least remember the last search item, so you could search, see what you find, move around, and then maybe slightly tweak your search without having to retype everything. It would be even better if maybe the last 5 items or so could be kept - maybe even saved to disk so that they would be accessible again after a restart.

I don't know if the string-search as it currently works is intended to work across directories, because I could live very well with typing "/" to start a search, then if the next char is again a "/" it will be a regex search (which should be case-sensitive as you can build case-insensitivy into the regex if you want it) and if not it's a string.

So "/hubba" would search case-insensitive for "hubba", while "//h[0-9]{2}" would regex-search for a lower-case h followed by 2 digits. If I also wanted to include upper-case "H", I would simply do "//[hH][0-9]{2}"

I would not really need all the powers of pcres, that would be silly in a file manager, but to be able to search for patters of digits and letters in a simple way would be very handy - so I would like to have things like "\d" or "\w" etc.

One idea would be to allow occurances of \d in a typed-in regex and then translate this regex into a POSIX-compatible version.

That would mean a user could type in "//\d{2}-\d{2} and nnn would in a first step replace all occurances of "\d" with "[[:digit:]]" before it's handed over to the regex-library. In that way there would be no real need for a pcre-engine, while still having the benefit of being able to express pattern is a succinct way suitable for typing in (I do want to search for digits, but I do not want to type "[[:digit:]]" every time).

ghistes commented 4 years ago

Oh - to your idea about triggering case-sensitivtiy via capital-letters - I think that's not bad.

"/hubba" => case insensitive /"Hubba" => case sensitive

So how would I search for an all-lower case word in a case-sensitive way?

If regexes would be case-sensitive (as I proposed above) and started with "//" you simply do

"//hubba"

All cases covered I believe.

jarun commented 4 years ago

@ghistes I have added the line items. These are up for grabs now. If possible, please get your hands dirty with the code, understand how it is today and raise PRs.

jarun commented 4 years ago

The last filter is remembered at commit 0e203288b4662c7781a2c5fc519da219f9eb7403.

jarun commented 4 years ago

@ghistes I sent you 2 invites - for nnn collab and dev-discussions. Can you please join? I want to share the notes I was talking about.

jarun commented 4 years ago

@ghistes

maybe slightly tweak your search without having to retype everything

is not possible without redesigning search. Filters are search as you type - you type the next char and the scan happens. This also helps in having a very efficient algorithm wrt. performance. So if you wanna come back, you have to delete chars and come back. Another advantage is the left and right key work as they should in normal mode.

I don't know if the string-search as it currently works is intended to work across directories,

No, it's not. We can have what you are proposing with some modification I guess.

One idea would be to allow occurrences of \d in a typed-in regex and then translate this regex into a POSIX-compatible version.

This is hacky. Tomorrow there might be another case. I wouldn't want to parse and modify input provided by users. That warrants documentation and may take users by surprise. Compiling in the PCRE lib option is really not that scary. Probably give it a shot yourself.

Oh - to your idea about triggering case-sensitivtiy via capital-letters - I think that's not bad.

It wouldn't work. Say the string is "abcDeF". If you erase F and come back the logic would need to scan the whole string to determine case-sensitivity. I think the best is to have a toggle.

Similarly I think it would be best to have a toggle like \ to enable regex filter.

jarun commented 4 years ago

regex and string toggle is implemented at 8588b3f0bd39496dbcf481c31aa7a61d7e6df20a.

jarun commented 4 years ago

Toggle filter case-sensitivity is implemented at 70dcbf43d786da63598ad1080c518e4c44baa7bd.

ghistes commented 4 years ago

Wow, quick progess again. This is really getting very useful very fast.

There seems to be a little bug (or at least an inconsistent behaviour) with the history:

Press "/" to search. Then enter "n". Files are filtered. Press backspace. Filter is now empty, all files are visible. Press backspace again. Filter-prompt is replaced with status-bar. Press "/" to search again. Now "up" to access history (should either result in an empty filter or "n", depending on whether an empty search should go to history) but what happens is that the filter-prompt is closed and instead a file is selected.

So sometimes "/" + "up" accesses history and sometimes not.

I would want predictable behaviour. When you are in the prompt for a filter "up" should never leave this prompt. When the history is empty then simply nothing should be done.

The current key-bindings have a certain logic, but please keep in mind that "\" and "|" are both a bit awkward to enter on non-us keyboards.

What I ultimately would like to have is remembering maybe 5 history-entries, so you could easily re-run previous searches.

But to do that the history would need to remember not just the filter-expression but also how to interpret them (ie if they should be seen as string or regex, and with what sort of sensitivity).

At the moment (as far as I understand it) the modes seem to be a state not of the individual filter, but of nnn.

So if I do a regex-search and then a string-search (assuming I had more than one history) and then wanted to repeat the regex-search I would have to change the mode first - recalling the history would not be enough.

Ideally I would like to have something like booksmarks for filter, where I could specify a specific filter including all the modes, eg. "=|hubba" to mean a case-insensitive string search for hubba or "/-abc[0-9]*" to mean a case-sensitive regex-search and associate such filter-expressions with a key (like the regular bookmarks) so that these filters could be accessed very fast.

Finally I started to look at the code a little bit and noticed that the dependency on dbg.h is missing from the Makefile and that in the DPRINTF_D-macro there seems to be a spurious LINE at the end that is absent from the other debug-macros.

Do you want a PR for that?

jarun commented 4 years ago

@ghistes please join the nnn-dev discussion.

jarun commented 4 years ago

There seems to be a little bug Press backspace again

The filter is gone.

should either result in an empty filter So sometimes "/" + "up" accesses history and sometimes not.

The filter is empty, meaning there is no filter in history, so the Up arrow's original behaviour picks up. It's the correct behaviour.

I would want predictable behaviour.

Yes, you should predict and expect that when you erased the filter with backspace, the filter is gone. ;) Let's word it this way - the Up arrow shows the last filter if one exists.

The current key-bindings have a certain logic, but please keep in mind that "" and "|" are both a bit awkward to enter on non-us keyboards.

Suggest alternatives. I changed | to :.

What I ultimately would like to have is remembering maybe 5 history-entries, so you could easily re-run previous searches. Ideally I would like to have something like booksmarks for filter, where I could specify a specific filter including all the modes, eg. "=|hubba" to mean a case-insensitive string search for hubba or "/-abc[0-9]*" to mean a case-sensitive regex-search and associate such filter-expressions with a key (like the regular bookmarks) so that these filters could be accessed very fast.

Yes. Maybe a better option is to add custom filters with a keybind and use the same keybind to add a filter when the prompt is empty.

But to do that the history would need to remember not just the filter-expression but also how to interpret them (ie if they should be seen as string or regex, and with what sort of sensitivity).

We are talking about a structure with a char pointer and 2 bools here.

Do you want a PR for that?

I had something to push. To removed the spurious arg. Leave the Makefile dep. Not super-important.

jarun commented 4 years ago

Ideally I would like to have something like booksmarks for filter

Should they be pre-defined in your rc file or you would dynamically add and forget during a run? Maybe we can have 5 fixed and 5 dynamic?

jarun commented 4 years ago

Should they be pre-defined in your rc file or you would dynamically add and forget during a run? Maybe we can have 5 fixed and 5 dynamic?

Forget I asked. Maybe we are over-complicating this thing with filtering.

@0xACE @KlzXS @annagrram what do you think?

ghistes commented 4 years ago

Ideally (for christmas maybe?) I would like to have nnn remember the last 5 seaches/filters. They should be persisted to a file so they survive a restart.

Also I would like to define searches/filters in the same way as bookmarks work today - every one assigned to a letter via an enviroment variable.

When the search-prompt is open you should be able to navigate through the history with up and down keys.

And there should be a way to fast-acess them by having some hot-key where

\+\ : run the bookmarked filter associated with \+\ : run the n-th last filter from the history. \+! : run last filter again (probably after navigating to a new dir)

Also I would like to have a way to edit the filter-prompt so you can iteratively build expressions. At the moment all I seem to be able to do is to delete a character from the end and append to the end. So e.g. modifying a regex in the middle is hard work. Maybe some toggle to change the left-right keys from selecting files/dirs to move within the search-expression?

What I am striving for would be a workflow where you open nnn. Go to a bookmark. Run a pre-defined filter (eg list all log-files) with two keystrokes. Then you realise you've got the wrong dir. No problem. You navigate to the proper dir and just re-run your search with two keystrokes. etc etc etc

But clearly this won't happen overnight any maybe you have a different vision...

ghistes commented 4 years ago

And if I delete all chars from a filter I still have a filter (it's the emtpy filter showing all files). So if I delete all the chars from a filter the last history-entry is this - the empty filter. And when I then search the history it's not that there is no history.

What I am trying to say is that when you're in the search-prompt and press "up" you should NEVER select a file as (conceptually) the history is never empty.

I really dislike the behaviour that my search-history determines what happens when I press "Up". I don't want to remember what I did before in oder to predict how the app will behave.

But it's a big step forward as it is.

KlzXS commented 4 years ago

Regarding getting list of files as input. Is it final that we create a subtree with symlinks in /tmp?

I wanted to discuss what we get for input. Does the user give absolute or relative paths, if absolute, do I assume that they point to something in the subtree of the current working dir i.e. relative paths but with cwd as prefix? Also can I assume that the paths don't contain /./, /../ and multiple slashes?

jarun commented 4 years ago

I think the tmp dir is the simplest solution. Note that the symlinks may point across volumes. We need to handle that.

Regarding absolute and relative, there can be a mix. We already handle that in main().

KlzXS commented 4 years ago

We already handle that in main().

What do you mean we already handle it? My main concern is how should the tree in tmp look like. Say I get the following:

/a/b/c/d
/a/b/c/e
/a/b/c/f

Should I make a dir and put symlinks to d, e and f, should I make a dir c and in it make links to the files, should I go even higher? I would need to assume that they are pointing to something in the subtree of the cwd in which case just skip cwd prefix and pretend they are relative paths.

jarun commented 4 years ago

Okk, put symlinks to d, e, f in the same dir.

KlzXS commented 4 years ago

Ok. I'll make minimal assumptions and we'll see how it behaves.

jarun commented 4 years ago

I'll make minimal assumptions and we'll see how it behaves.

It should work. Because in this particular mode, for each file we will stat with follow_symlink and get the details of the target file.

KlzXS commented 4 years ago

It will. How does this sound:

I don't like the second point, but I don't like resolving symlinks even more, because that would make the result look confusing.

jarun commented 4 years ago

Find the longest common prefix of all paths

How does this help?

KlzXS commented 4 years ago

Say I have

/a/b/c/d/e
/a/b/c/f
/a/b/g

I would display the following

c/d/e
c/f
g

I could display everything, but I think this way is more compact and straight to the point.

jarun commented 4 years ago

Sounds good to me!

jarun commented 4 years ago

Some notes on the algos to avoid re-work (you can suggest better ones, of course):

I think to get the common path you'll use an algo like:

get first file and take it's dirname, get second one, see how far they match (I am not sure if there's an API to do that already) and now this is you minimum common. Match third file's dirname with minimum common and so on...

In case there's no API to find matching parts of strings, check xstrlcpy() code. We can use sizeof(long) byte comparisons at a time to find the first non matching 4 or 8 bytes and then XOR and get the offset to the first non-zero byte. It must follow a /.

KlzXS commented 4 years ago

Where should we draw the limit to the size of input? I need to allocate some memory for the input, but how much the first time, and when do I stop and say that's too much?

I think we can start with 256 paths which translates to 1MiB, and go up to 216, which is 256MiB. If you need more than that you probably know how to use xargs.

What do you think?

jarun commented 4 years ago

Chunks of 4Kb would be fine with 0 gap. See dentfill on how we do this for file names today.

jarun commented 4 years ago

Ahh OK. The input. I think we should go in steps of 512 KB.

KlzXS commented 4 years ago

Ok. Do we put an upper limit or do we let it allocate until it crashes the system. I think 216 paths is enough.

jarun commented 4 years ago

2^16 is more than enough!

KlzXS commented 4 years ago

I noticed there are like 3 occurrences of malloc() which are not checked for NULL. Will you take care of this or should I? They are in archive_selection(), get_kv_val() and setup_config().

jarun commented 4 years ago

Will you take care of this or should I?

Please take care. :)

jarun commented 4 years ago

Please raise this PR separately. We can have it for next release.

maximbaz commented 4 years ago

Idea: ability to exit nnn with non-zero exit code.

I have a real-world use-case that I'd like to try out in nnn (where I currently use vifm for this), and it requires both take list of files as input and show (so I would be interested to test a PR when it's ready) as well as ability to exit file manager with non-zero exit code.

Without going too much into details for now, an ability to exit file manager with non-zero exit code allows me to flag if I successfully completed or aborted the task I was doing.

vifm is a lot like vim in terms of key bindings, so :q means "exit vifm with exit code of 0" and :cq means "exit vifm with exit code of 1". I don't particularly have an opinion whether this should be a separate hotkey or something else (we already have 3 different flavors of "quit" functionality...), open for ideas and proposals.

jarun commented 4 years ago

@KlzXS is already working on the list of files input. Please discuss with him.

maximbaz commented 4 years ago

Yeah I know, that's why I wrote I would he interested to test the PR 🙂 What do you think about the idea of non zero exit codes though?

jarun commented 4 years ago

non 0 - raise a PR please!

jarun commented 4 years ago

@maximbaz I am considering replacing the config NNN_USE_EDITOR with a program option -e. Generally people use nnn with aliases and reading an env var is costlier than checking an option. What do you think?

jarun commented 4 years ago

I am looking for areas to reduce startup time.

jarun commented 4 years ago

Similarly -t can replace NNN_IDLE_TIMEOUT.

maximbaz commented 4 years ago

you are right about people using aliases, I do that too, and yes to be honest I do find it a bit unusual that some nnn options are configured via env variables and others via cli args.

I would support replacing some env variables with cli flags.

Let me ask this: why not replace ALL env variables with cli arguments?

jarun commented 4 years ago

There is a specific cases where we do not want data loss - NNN_TRASH.

The longer ones (strings) I want to keep as env vars. Users may find it intimidating they have to type a long option to do something. The alias is not always obvious to newbies.

jarun commented 4 years ago

For now, can you raise a PR for -e and -t? I have to go to sleep.

Notes:

maximbaz commented 4 years ago

I can think of the opposite example, when you create a nnn.desktop as described on wiki, for many people this instance of nnn will not respect environment variables (users must know that they should these env variables not in .bashrc but in e.g. /etc/environment to have effect on nnn started as GUI app), and in this case data loss is very possible. When there only are cli arguments, users know they must pass them, no accidents like that can occur.

I unfortunately forgot my laptop at the office, I'm writing from phone, unable to contribute tonight... But yeah let's pick this up tomorrow!

jarun commented 4 years ago

That's a good point. But let's document that. It's a one time setting for a system. There are instances where people may run nnn as nnn without an alias.

0xACE commented 4 years ago

I have been away for about a month, and geez a lot of things happened while I was gone.

I try to keep up with master all the time (referring to you asking us to use the master branch), but I run my personal changes ontop of it.

I just spent the last 30minutes rebasing ontop of the latest version. and seems like a lot of keys has been changed. I was in particular not happy with the removal of:

  1. SEL_RUNEDIT - used to edit rather than executing files
  2. SEL_RUNPAGE - used to quickly view text files
  3. SEL_EXEC - execute files which may be defaulted to another action (commonly done on sh scripts in my case)

I tried to skim through the discussion happening. I think KlzXS mentioned the changing of keys being a annoyance, and i agree.

But I would also like to add the changing of command line arguments nnn takes being annoying. Because that also requires me to update all my machines that use nnn: because they have their own wrapper script writing over nnn defaults.

Only my main machine runs on master, my other machines are based on the build from december last year, as i cba to update the commandline parameters on each machine (as my other machines dont have mvg/cpg i have to maintain a seperate config). and I'm dropping nnn.vim on my machine because it also needs another update because of this (though mostly over the past 6 months I have never used any thing to open files in vim other than :e, gf and various tag-jumping method...)

I'm still working on rebasing my personal branch ontop of master and will have to give a long term review at a later point. as I still haven't got my nnn environment restored...

jarun commented 4 years ago

Don't worry! These are convered. Only with more regular workflows.

Runedit and runpage can be easily achieved through run cmd as plugin. I have updated the plugin docs with your use-case in mind - https://github.com/jarun/nnn/tree/master/plugins#some-useful-key-command-examples

I am using EDITOR that way myself with ;e.

SEL_EXEC - did you try "./$nnn" at the prompt?

But I would also like to add the changing of command line arguments nnn takes being annoying

This has also been addressed and frozen in #422.

I'm dropping nnn.vim on my machine because it also needs another update because of this

Do you mean the plugin needs a change? Then @mcchrish should be informed right away.

0xACE commented 4 years ago

Don't worry! These are convered. Only with more regular workflows.

Runedit and runpage can be easily achieved through run cmd as plugin. I have updated the plugin docs with your use-case in mind - https://github.com/jarun/nnn/tree/master/plugins#some-useful-key-command-examples

I am using EDITOR that way myself with ;e.

Ah, my dumbass just got into the seat cold and started tinkering on things, ofc a plugin can do that... Alas as I have made another patch for it. i'm maintining it as seperate until i grow tired.

SEL_EXEC - did you try "./$nnn" at the prompt?

That's too many characters to hit for something i use commonly, i guess a plugin keystroke would do it better. but again I'll maintain this locally until i grow tired.

But I would also like to add the changing of command line arguments nnn takes being annoying

This has also been addressed and frozen in #422.

Yeah, i tried to read that thread but as my time was limited with all the catching up i had to do i couldn't read it through and through...

I'm dropping nnn.vim on my machine because it also needs another update because of this

Do you mean the plugin needs a change? Then @mcchrish should be informed right away.

Well I have this in my ~/.vimrc

let $NNN_BMS=$NNN_BMS
let $NNN_USE_EDITOR=$NNN_USE_EDITOR
let $NNN_NO_AUTOSELECT=$NNN_NO_AUTOSELECT
let $NNN_NOTE=$NNN_NOTE
let $NNN_OPS_PROG=$NNN_OPS_PROG
let $NNN_CP_MV_PROG=$NNN_CP_MV_PROG
let $NNN_SSHFS_OPTS=$NNN_SSHFS_OPTS

let $NNN_TRASH=$NNN_TRASH
let $NNN_COPIER=$NNN_COPIER
let $NNN_RESTRICT_NAV_OPEN=$NNN_RESTRICT_NAV_OPEN
let $NNN_OPENER=$NNN_OPENER
let $NNN_CONTEXT_COLORS=$NNN_CONTEXT_COLORS
let $NNN_IDLE_TIMEOUT=$NNN_IDLE_TIMEOUT
let $NNNLVL=$NNNLVL

I guess the probelm wasn't updating the commandline arguments, i guess it was the environment variable name changes. as you can see, some of those variable names has been deprecated... alas i tried it out for months and it never entered my workflow, i haven't used any of the other navigation plugins for vim either, im rather plugin free tbh (though vim-surround should be included in the default build if you ask me)

jarun commented 4 years ago

Shall we change $nnn to $n? What issues do you guys see? Any possibility of conflict with n used as var in a script?