-
Notifications
You must be signed in to change notification settings - Fork 326
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
Access to jump history from commands #1277
Comments
Another option could be to make the list commands accept a file name argument which, if provided, would make them dump the output to that file instead of piping it via |
I've been thinking about doing something like this on and off. I think the solution of adding an argument to My favorite approach at the moment is the following, but I'm wondering if people think it's too complicated.
We could also have a If that's too complicated, the two problems could also be tackled separately. |
This is something I have also thought about. However, the reason why this kind of feature is currently not implemented is because the If we were to implement this, I would prefer to keep the I think it would be possible to enhance the current protocol to allow for request/response communication, for example I am slightly worried that it would add too much complexity to the current protocol though, might need some more time to consider. |
Actually never mind, I realised that if you make I think a synchronous |
Just to be clear, that would be local internal lf commands, correct? And running them from the shell via Also, from what I understand, lf commands can have arbitrary number of arguments, so in
|
Correct.
The command would be executed in parallel with your shell script. You're right that this makes such features inconvenient to use from shell scripts. For example, if we had a A third option would be to split your script in two and use I'm not sure how much better we could do without making the lf server protocol more complicated. Following the general direction of @joelim-work's thoughts, we could consider making the server protocol more complicated. I haven't looked into whether that's reasonable or feasible. For example, perhaps there could be a way to ask lf's server to send a command to some instance of
My original idea was to have a limited number of arguments that could be passed to Alternatively, we could have built-in commands named This alternative idea could be confusing in a similar way, since the first argument to the command has to be Perhaps a better yet idea on the syntax will come to somebody's mind. |
To be clear, I pretty much gave up on the A simple way to demonstrate this is to run
Usually the above sequence of steps is not a problem, because Regarding internal commands, I was actually thinking of having a syntax similar to
This allows the user to decide on the type of shell command, and also doesn't add too much complexity to the current parsing rules. But I have not tested this yet so I'm not sure how feasible it is. |
I was working on a proof of concept for this, which defines a new command type type mapExpr struct {
keys string
expr expr
}
type cmdExpr struct {
name string
expr expr
}
type execExpr struct {
prefix string
value string
}
// NEWLY ADDED
type pipeExpr struct {
data string
exec execExpr
} Below is the diff, which contains code that was written quickly, and could possibly be refined further. Click to show diffdiff --git a/app.go b/app.go
index f05a7a3..60836cd 100644
--- a/app.go
+++ b/app.go
@@ -500,32 +500,36 @@ func (app *app) runCmdSync(cmd *exec.Cmd, pause_after bool) {
app.nav.renew()
}
// This function is used to run a shell command. Modes are as follows:
//
// Prefix Wait Async Stdin Stdout Stderr UI action
// $ No No Yes Yes Yes Pause and then resume
// % No No Yes Yes Yes Statline for input/output
// ! Yes No Yes Yes Yes Pause and then resume
// & No Yes No No No Do nothing
-func (app *app) runShell(s string, args []string, prefix string) {
+func (app *app) runShell(s string, args []string, prefix string, stdin io.Reader) {
app.nav.exportFiles()
app.ui.exportSizes()
exportOpts()
cmd := shellCommand(s, args)
var out io.Reader
var err error
switch prefix {
case "$", "!":
- cmd.Stdin = os.Stdin
+ if stdin != nil {
+ cmd.Stdin = stdin
+ } else {
+ cmd.Stdin = os.Stdin
+ }
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
app.runCmdSync(cmd, prefix == "!")
return
}
// We are running the command asynchroniously
if prefix == "%" {
if app.ui.cmdPrefix == ">" {
@@ -537,20 +541,24 @@ func (app *app) runShell(s string, args []string, prefix string) {
}
app.cmdIn = stdin
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Printf("reading stdout: %s", err)
}
out = stdout
cmd.Stderr = cmd.Stdout
}
+ if prefix == "&" && stdin != nil {
+ cmd.Stdin = stdin
+ }
+
shellSetPG(cmd)
if err = cmd.Start(); err != nil {
app.ui.echoerrf("running shell: %s", err)
}
// Asynchronous shell invocations return immediately without waiting for the
// command to finish, so there is no point refreshing the preview if nothing
// has changed yet.
volatile := prefix != "&"
app.ui.loadFile(app, volatile)
diff --git a/eval.go b/eval.go
index 0ddf107..a449fc0 100644
--- a/eval.go
+++ b/eval.go
@@ -2161,38 +2161,38 @@ func (e *callExpr) eval(app *app, args []string) {
for p.parse() {
p.expr.eval(app, nil)
}
if p.err != nil {
app.ui.echoerrf("%s", p.err)
}
case "$":
log.Printf("shell: %s", s)
app.ui.cmdPrefix = ""
app.cmdHistory = append(app.cmdHistory, cmdItem{"$", s})
- app.runShell(s, nil, "$")
+ app.runShell(s, nil, "$", nil)
case "%":
log.Printf("shell-pipe: %s", s)
app.cmdHistory = append(app.cmdHistory, cmdItem{"%", s})
- app.runShell(s, nil, "%")
+ app.runShell(s, nil, "%", nil)
case ">":
io.WriteString(app.cmdIn, s+"\n")
app.cmdOutBuf = nil
case "!":
log.Printf("shell-wait: %s", s)
app.ui.cmdPrefix = ""
app.cmdHistory = append(app.cmdHistory, cmdItem{"!", s})
- app.runShell(s, nil, "!")
+ app.runShell(s, nil, "!", nil)
case "&":
log.Printf("shell-async: %s", s)
app.ui.cmdPrefix = ""
app.cmdHistory = append(app.cmdHistory, cmdItem{"&", s})
- app.runShell(s, nil, "&")
+ app.runShell(s, nil, "&", nil)
case "/":
dir := app.nav.currDir()
old := dir.ind
if gOpts.incsearch {
dir.ind = app.nav.searchInd
dir.pos = app.nav.searchPos
}
log.Printf("search: %s", s)
app.ui.cmdPrefix = ""
app.nav.search = s
@@ -2538,32 +2538,46 @@ func (e *callExpr) eval(app *app, args []string) {
return
}
cmd.eval(app, e.args)
}
}
func (e *execExpr) eval(app *app, args []string) {
switch e.prefix {
case "$":
log.Printf("shell: %s -- %s", e, args)
- app.runShell(e.value, args, e.prefix)
+ app.runShell(e.value, args, e.prefix, nil)
case "%":
log.Printf("shell-pipe: %s -- %s", e, args)
- app.runShell(e.value, args, e.prefix)
+ app.runShell(e.value, args, e.prefix, nil)
case "!":
log.Printf("shell-wait: %s -- %s", e, args)
- app.runShell(e.value, args, e.prefix)
+ app.runShell(e.value, args, e.prefix, nil)
case "&":
log.Printf("shell-async: %s -- %s", e, args)
- app.runShell(e.value, args, e.prefix)
+ app.runShell(e.value, args, e.prefix, nil)
default:
log.Printf("evaluating unknown execution prefix: %q", e.prefix)
}
}
+func (e *pipeExpr) eval(app *app, args []string) {
+ var stdin io.Reader
+ switch e.data {
+ case "maps":
+ stdin = listBinds(gOpts.keys)
+ case "cmaps":
+ stdin = listBinds(gOpts.cmdkeys)
+ case "jumps":
+ stdin = listJumps(app.nav.jumpList, app.nav.jumpListInd)
+ }
+
+ app.runShell(e.exec.value, args, e.exec.prefix, stdin)
+}
+
func (e *listExpr) eval(app *app, args []string) {
for i := 0; i < e.count; i++ {
for _, expr := range e.exprs {
expr.eval(app, nil)
}
}
}
diff --git a/parse.go b/parse.go
index e6abeb0..4340457 100644
--- a/parse.go
+++ b/parse.go
@@ -105,20 +105,29 @@ func (e *execExpr) String() string {
}
break
}
buf.WriteString(" }}")
return buf.String()
}
+type pipeExpr struct {
+ data string
+ exec execExpr
+}
+
+func (e *pipeExpr) String() string {
+ return fmt.Sprintf("%s | %s", e.data, e.exec.String())
+}
+
type listExpr struct {
exprs []expr
count int
}
func (e *listExpr) String() string {
var buf bytes.Buffer
buf.WriteString(":{{ ")
@@ -211,20 +220,38 @@ func (p *parser) parseExpr() expr {
name := s.tok
s.scan()
if s.typ != tokenSemicolon {
expr = p.parseExpr()
} else {
s.scan()
}
result = &cmdExpr{name, expr}
+ case "pipe":
+ var expr expr
+
+ s.scan()
+ data := s.tok
+
+ s.scan()
+ if s.typ != tokenSemicolon {
+ expr = p.parseExpr()
+ } else {
+ s.scan()
+ }
+
+ if exec, ok := expr.(*execExpr); ok {
+ result = &pipeExpr{data, *exec}
+ } else {
+ p.err = fmt.Errorf("expected shell command '%s'", expr.String())
+ }
default:
name := s.tok
var args []string
for s.scan() && s.typ != tokenSemicolon {
args = append(args, s.tok)
}
s.scan()
And now it's possible to write configurations like below. I don't know if there's any real use case for other shell prefixes (i.e. cmd ctrlmaps pipe maps ${{
grep '^<c-' | less
}} |
I wonder, if this approach goes live, it would be a good idea to redefine existing list commands to something like this:
|
I'm not sure how useful printing to the bottom statline via Also the identifier after
Yes, that's the idea of the proposed syntax - this defines a command called |
One example application is that I have successfully found a way to use cmd clearmaps pipe maps ${{
awk 'NR >= 2 { printf "map '\''%s'\''; ", $1 }' |
sed "s/'''/\"'\"/" |
xargs -0 -I{} lf -remote "send $id :{}"
}} Although I think it would be better to have a builtin command for this. In any case I think users will come up with applications for this |
Ah, I didn't initially understand is. Your point is that if So, @j-xella was actually correct when they said
I'm still looking over the other interesting things that were said. |
I don't have much specifics to add but all this discussion suggests me that maybe we should have preferred the environment variable approach discussed in #918 instead. Wouldn't that have made this possible with something like the following:
I can't say I liked reading through the solutions proposed here as it felt too complicated for my taste in general. So far, I think our general way of passing information to shell commands was to use environment variables. We are now talking about other ways for a similar purpose. So if this is the direction we decide to take, I think it should be more strongly justified somehow. |
The use of environment variables is simple, and consistent with passing other data like options and selected files. The problem is that environment variables don't work with large amounts of data due to their size limits as pointed out in #918. I do acknowledge that the While I think environment variables are fine for exporting |
I can't remember if I have said this before or not, but while I support the idea of having |
This is perhaps a minor issue, on Linux at least, but I'm not very comfortable with environment variables containing newlines. It's an area of shell peculiarities I never learned. I'm not immediately sure which of the following will work: echo $lf_maps | less
echo "$lf_maps" | less
printf "%s\n" "$lf_maps" | less
IFS="\n" echo $lf_maps | less # lf's default `maps` command should not depend on the value of $IFS though
export IFS="\n"; echo "$lf_maps" | less On Windows, this could be outright impossible. Do either of you know if it's doable? Windows also has a low limit on the length of a single cmd.exe command, and I'm not sure whether that limit is applied before or after expanding the environment variables. If we find a way to deal with this on Windows, or decide to not do this on Windows, I think this will be fine. For the issue of jumplist being long, I suppose we could just cut it off at some number of entries. Or are the limits too small for this to work, @joelim-work? Other than that, my main objection to the environment variables approach is unsightliness in the output to The The The I don't have |
If environment variables are too restrictive, why not
A bonus of this approach would be that each cmd script would have its own temporary folder to store files, on any OS lf is running. |
This is the part I misunderstood. I thought that "pipe" would accept ANY lf command that produces output, and redirect it to a script via stdin. Then it seemed natural to me to redirect the output that otherwise would appear on status line. |
Regarding environment variables, I'm not really familiar with the intricacies such as support for newline characters. TBH I have always seen environment variables as something you would use for providing configuration values, not as a means of passing arbitrary data for once-off purposes. Temp files were also discussed as a possible solution in #918. For some reason (especially in #918 (comment)), this idea was rejected, but I think it is better than using environment variables. Actually I think I like it better than my I should probably also mention that some of the examples in the wiki Tips use temporary files like Creating a separate directory to isolate each instance of |
One more minor thought about environment variables: one way to avoid the issues I described with Windows etc would be to create a special option to the
It would simply print the value of The cost is that this makes lf do work that the shell should do which, conceptually, is not its job. We'd have to decide if this situation is important enough to make an exception to that principle. |
That. + Not to mention that slicing and dicing of environment variables is not always convenient. Not to mention that environment variables are all in the same namespace, so the more variables you rely upon, the higher the chance of a clash with some other systems. It can not be taken for granted that they are "limitless" in size. For example, on Windows, the whole environment block of a process is just 32K. That is all variable names + all values + separators in-between. Is this what lf is relying upon to pass the amounts of data of potentially unlimited size? IMO, isn't that a strong justification enough to consider other ways of passing data to subcommands? |
Sorry for disappearing after my previous comment. Let me drop a quick reply so I won't stall the discussion further. I understand that environment variables can be limiting at times. I also agree they are strange when used to hold information consisting of multiple lines. I guess temporary file approach would be the most reasonable way for further development then. By the way, I wasn't aware environment variable limits can be so low. I wonder if this could potentially be a problem for selection as well (e.g. go into a crowded folder and then So far, I think there were two main reasons to use files (i.e. selection, marks, tags, history), shared state and persistence. Jump list discussed here would be different in that regard. Having a separate temporary directory for each instance sounds reasonable. I guess this would conceptually resemble By the way, maybe it is worth mentioning, selection was once stored in server memory, but then it was changed to a file in ec330c1 as part of #177 for easier scripting. However, one thing I might add here is that maybe the use of such temporary files/directories should be controlled with an option. For example, we had a request for an option to disable history for privacy reasons before in #853 . There might also be some slight performance concerns if these files are to be created/modified for each command. Even if there isn't a noticable performance slowdown, I would personally like to be able to disable this myself as I find it strange for some reason to create multiple files/directories for each command. |
It is potential problem, for me
Yes, the
We can add an option like |
I would advocate for Second, I would advocate for recreating/deleting temp folder for each
Also, just a suggestion: there are currently 4 ways to invoke the shell: |
OK I'm convinced having a separate temp directory for shell invocation is better, thanks for the feedback. I also agree that the implementation would be simpler as the lifetime is scoped to just the shell invocation itself, so that there is no need to manage the state for the duration of the Apart from exposing a config setting, I can't really think of any better way to control whether the temp directory is created or not without making changes to the command parsing logic. I have written a very rough implementation below: Click to show diffdiff --git a/eval_test.go b/eval_test.go
index 48cb445..5671759 100644
--- a/eval_test.go
+++ b/eval_test.go
@@ -283,27 +283,45 @@ var gEvalTests = []struct {
{
"map r :push :rename<space> ; set hidden",
[]string{"map", "r", ":", "push", ":rename<space>", ";", "set", "hidden", "\n", "\n"},
[]expr{&mapExpr{"r", &listExpr{[]expr{&callExpr{"push", []string{":rename<space>"}, 1}, &setExpr{"hidden", ""}}, 1}}},
},
{
"map u $du -h . | less",
[]string{"map", "u", "$", "du -h . | less", "\n"},
- []expr{&mapExpr{"u", &execExpr{"$", "du -h . | less"}}},
+ []expr{&mapExpr{"u", &execExpr{"$", false, "du -h . | less"}}},
},
{
"cmd usage $du -h $1 | less",
[]string{"cmd", "usage", "$", "du -h $1 | less", "\n"},
- []expr{&cmdExpr{"usage", &execExpr{"$", "du -h $1 | less"}}},
+ []expr{&cmdExpr{"usage", &execExpr{"$", false, "du -h $1 | less"}}},
+ },
+
+ {
+ `cmd maps $> $PAGER "$lf_data/maps"`,
+ []string{"cmd", "maps", "$", ">", `$PAGER "$lf_data/maps"`, "\n"},
+ []expr{&cmdExpr{"maps", &execExpr{"$", true, `$PAGER "$lf_data/maps"`}}},
+ },
+
+ {
+ `cmd maps $>{{
+ $PAGER "$lf_data/maps"
+ }}`,
+ []string{"cmd", "maps", "$", ">", "{{", `
+ $PAGER "$lf_data/maps"
+ `, "}}", "\n"},
+ []expr{&cmdExpr{"maps", &execExpr{"$", true, `
+ $PAGER "$lf_data/maps"
+ `}}},
},
{
"map u usage /",
[]string{"map", "u", "usage", "/", "\n"},
[]expr{&mapExpr{"u", &callExpr{"usage", []string{"/"}, 1}}},
},
{
"map ss :set sortby size; set info size",
diff --git a/parse.go b/parse.go
index e6abeb0..9aeac0d 100644
--- a/parse.go
+++ b/parse.go
@@ -74,20 +74,21 @@ func (e *cmdExpr) String() string { return fmt.Sprintf("cmd %s %s", e.name, e.ex
type callExpr struct {
name string
args []string
count int
}
func (e *callExpr) String() string { return fmt.Sprintf("%s -- %s", e.name, e.args) }
type execExpr struct {
prefix string
+ export bool
value string
}
func (e *execExpr) String() string {
var buf bytes.Buffer
buf.WriteString(e.prefix)
buf.WriteString("{{ ")
lines := strings.Split(e.value, "\n")
@@ -260,34 +261,40 @@ func (p *parser) parseExpr() expr {
}
}
s.scan()
result = &listExpr{exprs, 1}
case tokenPrefix:
var expr string
prefix := s.tok
+ export := false
s.scan()
+ if s.typ == tokenRAngle {
+ export = true
+ s.scan()
+ }
+
if s.typ == tokenLBraces {
s.scan()
expr = s.tok
s.scan()
} else {
expr = s.tok
}
s.scan()
s.scan()
- result = &execExpr{prefix, expr}
+ result = &execExpr{prefix, export, expr}
default:
p.err = fmt.Errorf("unexpected token: %s", s.tok)
}
return result
}
func (p *parser) parse() bool {
if p.expr = p.parseExpr(); p.expr == nil {
return false
diff --git a/scan.go b/scan.go
index beb2b5c..808d644 100644
--- a/scan.go
+++ b/scan.go
@@ -11,20 +11,21 @@ type tokenType byte
const (
tokenEOF tokenType = iota
// no explicit keyword type
tokenIdent // e.g. set, ratios, 1:2:3
tokenColon // :
tokenPrefix // $, %, !, &
tokenLBraces // {{
tokenRBraces // }}
tokenCommand // in between a prefix to \n or between {{ and }}
tokenSemicolon // ;
+ tokenRAngle // >
// comments are stripped
)
type scanner struct {
buf []byte // input buffer
off int // current offset in buf
chr byte // current character in buf
sem bool // insert semicolon
nln bool // insert newline
eof bool // buffer ended
@@ -151,20 +152,27 @@ scan:
s.cmd = false
return true
}
}
}
s.typ = tokenEOF
s.tok = "EOF"
return false
case s.cmd:
+ if !s.eof && s.chr == '>' {
+ s.next()
+ s.typ = tokenRAngle
+ s.tok = ">"
+ return true
+ }
+
for !s.eof && isSpace(s.chr) {
s.next()
}
if !s.eof && s.chr == '{' {
if s.peek() == '{' {
s.next()
s.next()
s.typ = tokenLBraces
s.tok = "{{" The concept is similar to the @gokcehan How open would you be to extending the command parsing logic to allow for specifying data to be exported to shell invocations? I understand the parsing logic is supposed to be quite stable already, and that you might not want to make any changes to it, but to me it makes sense to toggle the behaviour here as opposed to a config setting because the lifetime of the data is tied to the shell invocation. |
Re @joelim-work's patch in the previous comment, why couldn't we just check if the first letter of a command is I find the I haven't had time to think in-depth about these issues, but I personally don't have a problem with exporting data to files, especially if it's opt-in per command. I never really understood the downsides of using temporary files mentioned in #918 (comment). I just assumed we shouldn't use them after that. Instead of some clever directory structure, +1 on just using |
Correct me if I'm wrong, but I don't think that will work for multiline shell commands, because the command in the
I think it's fine as long as it's documented. The problem with a long ugly string is that it makes it tedious to type in the command line, and asking users to create a
I guess we could add the ID into the temp directory pattern, but I don't know how much it matters - the idea is that upon creating the temp directory, it's location will be made available by an environment variable, which I have named |
Good point. OTOH, we could have
I was thinking people would mostly use this as part of a mapping or a
This is for users' convenience only. If they care about the differences in |
Ehm ... how will it help user, if the temp folder is deleted right after cmd is finished? I would be afraid to touch the temporary folders outside of the cmd execution, TBH. For debugging, a user would have to add something like this to his cmd anyway: |
Is this more cryptic, than, say, using |
If you are debugging a script, you can add In any case, this is a very minor issue, just a thought I found neat. We can skip it for now and then I can look into it after the initial PR. Let's not waste too much of our attention on it.
They are both cryptic, but the brevity of If we are going with one cryptic symbol, I agree that |
@ilyagr I think it mainly boils down to personal preferences but we also had some minor problems reported in the past. For example, temporary files can remain if you quit the program in an unusual way (e.g. #65, #292, #300). It is also not always straightforward to decide where to create temporary files (e.g. #722, #726). It may not be possible to use a volatile location that cleans up after a restart (e.g. Windows). It may not be possible to use a memory filesystem like
@joelim-work I think I'm still not convinced that this feature is useful enough to introduce a new syntax. Jumplist already has a builtin way for interaction that should work for most users (e.g. Can you give more details why a configuration option is not a good solution for this? As a non-user of this feature, I would like to be able to disable this myself, but if you make use of this feature in some of your commands, why would you want to be able to disable this for others? So far, I mostly felt alone in this discussion for being a critic of temporary files. |
First off, I would like to say that the idea of having
I understand your concern here -
Just as you have said in #1277 (comment), to me it also feels strange to create these files each and every time you run a shell command. A configuration option will disable the feature globally, but can't distinguish between shell commands that require data to be exported and shell commands that don't (e.g. the default That being said, I think a configuration option is still acceptable, and method of controlling whether to export data or not can be revisited later.
I think the more accurate view is that everyone else saw temporary files as a better solution compared to using environment variables 🙂. I agree that temporary files have their drawbacks (unreliable cleanup, privacy concerns, unnecessary exports, etc.) and for this reason I prefer to pipe the data to stdin, but unfortunately I don't think this approach is possible unless you're willing to make some kind of change to the command parsing rules. |
@joelim-work I have given this more thoughts and I think I'm finally convinced a configuration syntax addition is necessary for this, so I would accept a patch if you decide to work on this.
I'm glad you mentioned the other use cases because I wasn't aware you also have those in mind. If we want to convert
Now that we have the configuration syntax addition on the table, I'm at equal distances to the temporary file proposal and data pipe proposal. And with a high-level overview without going into much detail, I currently feel like data pipe approach might actually be better and cleaner than temporary files, though I will leave it for your judgement and the consensus of discussion here. Finally, I should note that unfortunately I will disappear for a couple of weeks for vacation in the upcoming days and I probably won't be responsive. Feel free to further discuss and/or send patches in the meantime. |
@gokcehan Thanks for your understanding and additional feedback, hope you have a nice relaxing break 🌴. So after thinking about this some more, I think I very much prefer the The only downside I can think of is that you can't pipe multiple types of data (e.g. I have written an implementation for this, which I might consider submitting as a draft PR later on. In addition to implementing the A sample map f pipe jumps ${{
dir=$(awk -F'\t' 'NR > 1 { print $NF }' | sort -u | fzf)
if [ -d "$dir" ]; then
lf -remote "send $id cd \"$dir\""
fi
}} I have chosen this syntax so that Everyone is welcome to review the branch and provide feedback. |
Thank you for your work. When creating this ticket, I only needed jumps, and it is covered now. But I am wondering if the following types of data could be added as well, so that the access is complete:
|
A question: if I invoke a series of commands:
I understand that they will be put to a queue of sorts and executed later, after the current script is finished. However, is the order of execution guaranteed, i.e. will they be executed in the order I sent them, or it may be any arbitrary order? |
I prefer to not introduce too much change in a single PR, so I will wait until the general functionality is merged before looking at supporting any more types of data. I definitely plan to add support for the command history, but I guess we would need a strong case for options and On a slightly related note, there is also a request for exporting the list of displayed files in #1084, it seemed to me like it would be better to just create another environment variable (e.g.
I would assume the order is guaranteed - what happens is that BTW it is possible to group builtin commands and shell commands sequentially using cmd allmaps :{{
pipe maps $cat > /tmp/maps
pipe cmaps $cat > /tmp/cmaps
$cat /tmp/maps /tmp/cmaps | less
}} |
Speaking strictly from my personal experience here, but I would find it confusing if there were 2 ways that produce similar, but not the same results. Then I would be guessing all the time, which one is better. But if the documentation says that both ways return the same data, I would just use what I am most convenient with.
True, because usually all of the following applies:
But, honestly, using environment variables to pass potentially unlimited amounts of data seems like environment abuse to me... Again, purely my personal opinion, but I think that
Totally agree. |
My 2 cents: I like the syntax @joelim-work proposed in #1277 (comment). I glanced at the implementation, and it seemed pretty clean and restrained in terms of how much complexity it adds. For this approach, the caveat is the threading issue that @joelim-work and @j-xella discuss in #1277 (comment) and that we mentioned before. I'm fine with that; it doesn't interfere with what I'd use this syntax for and each of the approaches we discussed has caveats. My one minor worry is that the command will result in more people running into the threading issue (which would happen when you use |
I suppose I don't mind exporting these as well at some point later in time. But even if it happens, I think it's unlikely the original environment variables will be removed because it would be a large breaking change for users, which means you are still stuck with same problem of potentially having a large environment table. I guess it would be possible to deprecate its use and eventually remove them, but I feel uncomfortable to make this decision by myself.
I do not know how common these kinds of issues will be. For the record I tried the example given in #1277 (comment) and it worked fine: cmd test ${{
lf -remote "send $id pipe maps $ cat > /tmp/maps"
lf -remote "send $id pipe cmaps $ cat > /tmp/cmaps"
lf -remote "send $id $ cat /tmp/maps /tmp/cmaps | less"
}} It doesn't work if you use a builtin command after cmd test2 ${{
lf -remote "send $id pipe maps $ cat > /tmp/maps"
less /tmp/maps
}} Which makes sense because |
I have spent some more time experimenting, and after a lot of consideration, I would like to (once again) provide another alternative approach, which is to have
The reason I didn't consider this approach very much earlier because after some initial investigation I found that
I have pushed up another branch for anyone interested to look at, feedback is of course welcome.
For the record, selecting a directory based on the jump list now looks like this: map f ${{
extract_dirs() {
# remove the header line and print the last field
awk -F'\t' 'NR > 1 { print $NF }'
}
dir=$(lf -remote "query $id jumps" | extract_dirs | sort -u | fzf)
if [ -d "$dir" ]; then
lf -remote "send $id cd \"$dir\""
fi
}} I have also moved #1306 back to a draft, as I wish to explore this new approach further. My apologies for continuously switching back and forth between ideas, but this feature greatly enhances the scripting power available in |
First of all, thanks for all the work. IMO, from the user perspective, this seems like a better approach, making commands more intuitive. Also, this way it seems to be easier to reuse multiple queries in one command. I just hope that it does not put too much of additional load on the server, making the operation of lf more unstable. |
Is it possible to access jump history, similar to what
jumps
command shows, from${{ lf commands }}
?I am trying to write a function that would feed the jump history to fzf, and then go to the selected folder. However, I am currently stuck with trying to get this list somehow...
There is a whole range of lf commands that produce multiple lines of output (
jumps
,maps
). Ideally, it would be nice to have some generic way to query lf withlf --remote
command, so that the output of such commands would be printed to stdout bylf --remote
. For example,lf --remote "pipe $id jumps" | grep ...
.The text was updated successfully, but these errors were encountered: