Skip to content
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

No tab completion when running `sbt console` in one go #3841

Closed
Sciss opened this issue Dec 27, 2017 · 18 comments

Comments

Projects
None yet
5 participants
@Sciss
Copy link
Member

commented Dec 27, 2017

(See the guidelines for contributing, linked above)

steps

Run sbt console for a project. (Note that I use sbt-extras and do not have sbt installed, so it may be related to that?). Type Indexe and press Tab key.

problem

A tab character is inserted instead of invoking the tab-completion.

expectation

It should complete to IndexedSeq instead of adding a tab character.

notes

The problem does not occur if I launch sbt first, and then enter into the console.

sbt version: 1.0.4
terminal/OS: Bash on Debian Stretch
Java: openjdk version "1.8.0_151"

@paulp

This comment has been minimized.

Copy link
Contributor

commented Dec 31, 2017

No, I just checked and it's the same with the "official" runner, so not specific to sbt-extras.

@paulp

This comment has been minimized.

Copy link
Contributor

commented Dec 31, 2017

It looks like the sbt server is only started if you run sbt with no args, then go into the console. Does anyone know offhand why the code path for "sbt console" differs from that of "sbt" followed by "console", and/or where it is that should be responsible for starting the server which presumably supplies the completions?

@eed3si9n

This comment has been minimized.

Copy link
Member

commented Dec 31, 2017

Running "sbt console" runs sbt in a batch mode that executes console task. console task calls Scala REPL, which handles input, tab completion using JLine, and output on its own (we haven't virtualized that for server yet).

Running "sbt" runs shell task, which accepts user input from both the terminal and the network (server).

The problem is due to JLine initialization or restoration timing, which I think is reported as #3453, so this issue might be a variant of that (tab just is not sent to JLine). I thought this was fixed by #3507, but maybe not in all cases?

@eed3si9n

This comment has been minimized.

Copy link
Member

commented Dec 31, 2017

Here's another issue that looks more related #3661.

Jason has a workaround in there:

Running jline.TerminalFactory.get.init in the borked REPL seems to unbork it.

So maybe if you run "sbt" as part of shell JLine gets initialized, but if you run "sbt console" it never gets initialized?

Welcome to Scala 2.12.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_144).
Type in expressions for evaluation. Or try :help.

sc      ndexe


scala> jline.TerminalFactory.get.init
jline.TerminalFactory.get.init

scala> Index[tab]
IndexOutOfBoundsException   IndexedSeq

@eed3si9n eed3si9n added the Bug label Dec 31, 2017

@paulp

This comment has been minimized.

Copy link
Contributor

commented Dec 31, 2017

@eed3si9n Yes, jline.TerminalFactory.get.init unborks it, thanks a lot.

@paulp

This comment has been minimized.

Copy link
Contributor

commented Dec 31, 2017

@eed3si9n Though, at the price of leaving echo disabled when I quit the repl, sigh.

@simlei

This comment has been minimized.

Copy link

commented Jan 6, 2018

@paulp @eed3si9n Please keep this thread updated if there is news on that; for me, too, the abovementioned fix is working just up to the point that my terminal basically stops working after exiting the REPL.
Awesome feature though, this project console, and thank you for posting the fix in the first place!

@paulp

This comment has been minimized.

Copy link
Contributor

commented Jan 6, 2018

Presumably the call to init discards a shutdown hook which formerly restored echo. For now you can do it manually with stty echo or maybe stty sane.

@eed3si9n

This comment has been minimized.

Copy link
Member

commented Jan 6, 2018

scala> jline.TerminalFactory.get.restore

scala> Ind


scala> :q
:q

Yea. Everything you init needs to be cleanup afterwards. Call restore, confirm that your tab no longer works again, and :q.

@eed3si9n eed3si9n added this to the 1.1.1 milestone Jan 6, 2018

@simlei

This comment has been minimized.

Copy link

commented Jan 6, 2018

Nice. If this is tackled in the future, I just want to put on the record that consoleProject is affected the same way as console (the reason I'm here)

@paulp

This comment has been minimized.

Copy link
Contributor

commented Jan 7, 2018

Is there an easy user-level wrapper strategy console sessions? E.g. there would be something along the lines of (in sbt)

def consoleSession(): Unit = {
  initConsole
  try runConsoleUntilExit() finally cleanupConsole()
}

Where initConsole and cleanupConsole hook into keys I can influence somewhere in ~/.sbt. I feel like this sort of thing is done for some subset of useful tasks in a number of ways, and the thought of researching it makes me immediately want to give up, so for a change I will try asking.

Edit: I should point out that, not being completely in the dark, I am aware of initialCommands, which maybe suffices for half of this. Except that in my experience you can't rely on anything in initialCommands in your personal sbt files actually being run, because most people will set the project's commands with initialCommands := ... instead of initialCommands += ....

@paulp

This comment has been minimized.

Copy link
Contributor

commented Jan 7, 2018

I guess the initialCommands in user.sbt are run after the ones in the build's sbt, at least in 1.1.0, at least so it seems from here, because I can use := in the build.sbt and += in the user sbt and everything gets there. Though this means I can't get out in front of the build's initialCommands with my global wrapper, not that it matters here.

It is odd that

  • sbt 0.12 looks in ~/.sbt/0.12
  • sbt 0.13 looks in ~/.sbt/0.13
  • sbt 1.0 looks in ~/.sbt/1.0
  • sbt 1.1 still looks in ~/.sbt/1.0
@eed3si9n

This comment has been minimized.

Copy link
Member

commented Jan 7, 2018

If you don't mind using some other name, then

addCommandAlias("consoleSession", ";initConsole;console;cleanupConsole")

If we want to take over existing key, something like this could work

    console in Compile := (console in Compile)
      .dependsOn(Def.task { jline.TerminalFactory.get.init })
      .andFinally( jline.TerminalFactory.get.restore )
      .value

See also http://www.scala-sbt.org/1.x/docs/Howto-Sequencing.html

@paulp

This comment has been minimized.

Copy link
Contributor

commented Jan 7, 2018

If I wanted the wrapper to run on all the consoles and consoleProjects in Test and Compile, is there any way to avoid injecting the same splodge of code for each?

@paulp

This comment has been minimized.

Copy link
Contributor

commented Jan 7, 2018

I confirm that placing into ~/.sbt/1.0/user.sbt the incantation

console in Compile := (console in Compile)
      .dependsOn(Def task jline.TerminalFactory.get.init)
      .andFinally(jline.TerminalFactory.get.restore)
      .value

provides sbt console with working tab completion and the resumption of echo upon exit for those projects I have since sampled.

Thanks @eed3si9n !

@eed3si9n

This comment has been minimized.

Copy link
Member

commented Jan 7, 2018

I think you do need to inject those for each, but you can do this:

jlineWorkarounds
def jlineWorkarounds = Def.settings(
  Seq(
    console        in Compile,
    console        in Test,
    consoleProject in Compile,
    consoleProject in Test,
  ) map { key =>
    key := key
             .dependsOn(Def.task { jline.TerminalFactory.get.init })
             .andFinally(jline.TerminalFactory.get.restore)
             .value
  }: _*)

Note that this injection would work only for the root project, so you might end up with an AutoPlugin if you want subproject handling as well.

@simlei

This comment has been minimized.

Copy link

commented Jan 7, 2018

@eed3si9n @paulp wow, lots of thanks for the tinkering & workarounds. consoleProject massively improves my current project and this workaround was important to show it to people and not give the impression of hacked-together stuff ;))

I tried it also with initialCommands in console but did not get very far as I wasn't able to find a shutdown hook for the terminal.

@dwijnand

This comment has been minimized.

Copy link
Member

commented Jan 8, 2018

@paulp

It is odd that

sbt 0.12 looks in ~/.sbt/0.12
sbt 0.13 looks in ~/.sbt/0.13
sbt 1.0 looks in ~/.sbt/1.0
sbt 1.1 still looks in ~/.sbt/1.0

1.0 is the binary API for the whole 1.x series. In retrospect I should've made it "1.x" (and properly dealt with the existing (Int, Int) type signature) instead of "1.0". My bad.

@dwijnand dwijnand modified the milestones: 1.1.1, 1.2.0, 1.something Jan 9, 2018

@dwijnand dwijnand added the ready label Jan 9, 2018

eed3si9n added a commit to eed3si9n/sbt that referenced this issue Jan 13, 2018

Fix tab completion running `sbt console`
Fixes sbt#3841

This fixes console task that internally uses JLine. When `console` is started from batch mode, the tab is printed as is. This is because JLine is not initialized yet.
Calling `usingTerminal` initializes and restores the terminal afterwards.

@eed3si9n eed3si9n self-assigned this Jan 13, 2018

@eed3si9n eed3si9n removed the ready label Jan 13, 2018

@eed3si9n eed3si9n modified the milestones: 1.something, 1.1.1 Jan 16, 2018

eed3si9n added a commit to eed3si9n/sbt that referenced this issue Jan 16, 2018

Fix tab completion running `sbt console`
Fixes sbt#3841

This fixes console task that internally uses JLine. When `console` is started from batch mode, the tab is printed as is. This is because JLine is not initialized yet.
Calling `usingTerminal` initializes and restores the terminal afterwards.

@dwijnand dwijnand closed this Jan 31, 2018

@dwijnand dwijnand removed the in progress label Jan 31, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.