Skip to content

Colors lost after installing v1.18.1 on Windows 10 #14174

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

Closed
RaymondLoranger opened this issue Jan 11, 2025 · 19 comments · Fixed by #14184
Closed

Colors lost after installing v1.18.1 on Windows 10 #14174

RaymondLoranger opened this issue Jan 11, 2025 · 19 comments · Fixed by #14184

Comments

@RaymondLoranger
Copy link

Elixir and Erlang/OTP versions

Erlang/OTP 27 [erts-15.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.17.3) - press Ctrl+C to exit (type h() ENTER for help)

Operating system

Windows 10

Current behavior

Messages like logger messages are output in colors under v1.17.3. However, I got monochrome messages after installing v1.18.1. As a result, I reverted to v1.17.3 and the colors returned! Under v1.18.1, I had to explicitly specify config :elixir, ansi_enabled: true in config.exs to "force" colored output. Also under v1.18.1, I could not exit IEx normally by hitting Ctrl+C. The behavior was completely erratic. Too bad, I would like to use the new JSON app. Note that I performed the following command to enable ANSI escapes in the Windows terminal:
reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1

Expected behavior

I want colored output under v1.18.1 when running tests (like green dots) or mix hex.outdated like I do under v1.17.3.

@josevalim
Copy link
Member

This is unexpected on OTP 26 and 27, we will take a look. How are you running it? Console? Powershell?

@josevalim
Copy link
Member

josevalim commented Jan 11, 2025

A couple additional questions.

  1. When you run erl -eval "c:h(string).", do you see string in bold, as shown here? Screenshot 2025-01-11 at 10 20 43

  2. What does :prim_tty.satty(:stdout) return on IEx?

  3. Can you reproduce the behaviour by starting all 3 of iex, iex.bat, iex.ps1? Note you cannot start all three of them depending on the terminal of your choice

@RaymondLoranger
Copy link
Author

Under Elixir v1.17.3 and OTP 27.1, erl -eval "c:h(string)." returns string in bold and :prim_tty.isatty(:stdout) returns true (in color). After installing Elixir v1.18.1, here is what I get:
iex
Erlang/OTP 27 [erts-15.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.18.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> erl -eval "c:h(string)."
error: undefined function erl/1 (there is no such import)
└─ iex:1

** (CompileError) cannot compile code (errors have been logged)

iex(1)> :prim_tty.isatty(:stdout)
true
iex(2)>

@josevalim
Copy link
Member

Can you please run erl -eval "c:h(string)." in your terminal and not inside IEx? Also, can you try the prim_tty command both by starting “iex.bat” and “iex.ps1”? Are you using powershell, console or WSL?

@RaymondLoranger
Copy link
Author

Sorry my bad. Actually erl -eval "c:h(string)." does return string in bold under Elixir v1.18.1 as well. And in Powershell, iex.bat behaves normally:

iex.bat
Erlang/OTP 27 [erts-15.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.18.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :prim_tty.isatty(:stdout)
true
iex(2)>
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
       (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
Terminate batch job (Y/N)? y
   Ray@rays   ~\Documents\ex_dev\projects  2025-01-12 07:57:32   

In my profile.ps1 file, I have remove-item alias:iex -Force to remove PS cmd iex. This allows me to type just iex instead of iex.bat and under v1.17.3 the behavior is normal. What is different under v1.81.1 is the behavior of iex: it does not allow to exit normally by hitting Ctrl+C twice:

iex
Erlang/OTP 27 [erts-15.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.18.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :prim_tty.isatty(:stdout)
true
iex(2)>
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
       (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
   Ray@rays   ~\Documents\ex_dev\projects  2025-01-12 08:07:17   
=node:'nonode@nohost'
=no_distribution

It seems to exit normally by hitting Ctrl+C just once. However afterwards, PS is completely erratic. And I get the same erratic behavior when running iex.ps1:

iex.ps1
Erlang/OTP 27 [erts-15.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.18.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :prim_tty.isatty(:stdout)
true
iex(2)>
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
       (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
   Ray@rays   ~\Documents\ex_dev\projects  2025-01-12 08:12:19   
=node:'nonode@nohost'
=no_distribution

In summary, the issue seems to be with Powershell. However it remains that under v1.17.3, iex behaves normally in Powershell.
So I am at a loss.

@RaymondLoranger
Copy link
Author

I compared files between v1.17.3 and v1.18.1 => both iex files are identical between the 2 versions. However, file iex.bat of v1.17.3 has additional lines when compared with that of v1.18.1:

:run
if defined IEX_WITH_WERL (set __ELIXIR_IEX_FLAGS=--werl) else (set __ELIXIR_IEX_FLAGS=)
call "%~dp0\elixir.bat" --no-halt --erl "-user elixir" +iex %__ELIXIR_IEX_FLAGS% %*
:end

as opposed to only these lines for v1.18.1:

:run
call "%~dp0\elixir.bat" --no-halt --erl "-user elixir" +iex %*
:end

I did not compare yet the elixir.bat files...

@RaymondLoranger
Copy link
Author

OK. I just compared the 2 elixir.bat files and v1.17.3 has some extra lines that v1.18.1 lacks. See attached.
compare elixir_bat_s.docx

@josevalim
Copy link
Member

Those changes should be ok because we now use this code to enable colors:

https://github.com/elixir-lang/elixir/blob/v1.18.1/lib/elixir/src/elixir.erl#L59-L65

And, according to that code, your ansi mode should be enabled because of this :prim_tty.isatty(:stdout) returns true for you.

I have one last theory though, can you please check the return type of :application.get_env(:elixir, :ansi_enabled) inside your IEx?

@RaymondLoranger
Copy link
Author

OK, I found what causes no colors in v1.18.1 (though I cannot explain why). When I compile my Elixir apps, I use a utility package which runs various commands using Mix.Tasks.Cmd.run(args). For example, Cmd.run(~w<mix format>) or Cmd.run(~w/mix compile/) or Cmd.run(~w/mix hex.outdated/). See mix_tasks. When I run mix cmd mix hex.outdated in v1.17.3, I have color but not in v1.18.1 (see attached files). Once the color issue is fixed then only the "exit" issue (double Ctrl+C failure) will remain.
v1.17.3.docx
v1.18.1.docx

@RaymondLoranger
Copy link
Author

I just got some "dump output" that may help: in v1.18.1 after hitting Ctrl+C once, I typed pwd and got this huge output (truncated):

=proc:<0.0.0>
State: Waiting
Name: init
Spawned as: erl_init:start/2
Spawned by: []
Message queue length: 0
Number of heap fragments: 0
Heap fragment data: 4
Link list: [<0.11.0>, <0.45.0>, <0.43.0>]
Reductions: 4531
Stack+heap: 987
OldHeap: 1598
Heap unused: 272
OldHeap unused: 1263
BinVHeap: 0
OldBinVHeap: 0
BinVHeap unused: 46422
OldBinVHeap unused: 46422
Memory: 21608
Stack dump:
Program counter: 0x0000023ba08dbdc4 (init:loop/1 + 84)
arity = 0
y(0) {state,[{root,[<<"C:\Program Files\Erlang OTP">>]},{bindir,[<<"C:\Program Files\Erlang OTP\erts-15.1\bin">>]},{progname,[<<"erl">>]},{home,[<<"C:\Users\Ray">>]},{noshell,[]},{elixir_root,[<<"C:\Program Files\Elixir\bin..\lib">>]},{pa,[<<"C:\Program Files\Elixir\bin..\lib\elixir\ebin">>]},{user,[<<"elixir">>]}],[<<"--no-halt">>,<<"+iex">>],[],[{application_controller,<0.45.0>},{logger,<0.43.0>},{erl_prim_loader,<0.11.0>}],<0.10.0>,{started,started},{"Erlang/OTP","27"},[],[],#{},{"c:/Users/Ray/Documents/ex_dev/projects/log_reset","C:\Program Files\Erlang OTP/bin/start.boot"}}

0x0000023ba8d4b910 Return addr 0x0000023ba08d2580 ()
Internal State: ACT_PRIO_NORMAL | USR_PRIO_NORMAL | PRQ_PRIO_NORMAL
=proc:<0.1.0>
State: Waiting
Name: erts_code_purger
Spawned as: erts_code_purger:start/0
Spawned by: <0.0.0>
Message queue length: 0
Number of heap fragments: 0
Heap fragment data: 11
Reductions: 23
Stack+heap: 233
OldHeap: 0
Heap unused: 228
OldHeap unused: 0
BinVHeap: 0
OldBinVHeap: 0
BinVHeap unused: 46422
OldBinVHeap unused: 46422
Memory: 2704
Stack dump:
Program counter: 0x0000023ba08d2b94 (erts_code_purger:wait_for_request/0 + 68)
arity = 0

0x0000023b9eb2cf20 Return addr 0x0000023ba08d2580 ()
Internal State: ACT_PRIO_HIGH | USR_PRIO_HIGH | PRQ_PRIO_HIGH | OFF_HEAP_MSGQ
=proc:<0.2.0>
State: Waiting
Spawned as: erts_literal_area_collector:start/0
Spawned by: <0.0.0>
Message queue length: 0
Number of heap fragments: 0
Heap fragment data: 0
Reductions: 7
Stack+heap: 233
OldHeap: 0
Heap unused: 233
OldHeap unused: 0
BinVHeap: 0
OldBinVHeap: 0
BinVHeap unused: 46422
OldBinVHeap unused: 46422
Memory: 2616
Stack dump:
Program counter: 0x0000023ba0973aa4 (erts_literal_area_collector:msg_loop/4 + 172)
arity = 0
y(0) []
y(1) 0
y(2) 60000
y(3) []
y(4) 0
y(5) {0,[]}
y(6) undefined

@josevalim
Copy link
Member

Did you get the same ctrl+c issue if running erl? What about iex.bat vs iex.ps1?

I will comment on the ANSI stuff soon. :)

@RaymondLoranger
Copy link
Author

The end portion of the above dump may contain additional cues:

0x0000023ba8d61950 Return addr 0x0000023ba08d2580 ()
Internal State: ACT_PRIO_NORMAL | USR_PRIO_NORMAL | PRQ_PRIO_NORMAL
=proc:<0.104.0>
State: Waiting
Spawned as: proc_lib:init_p/5
Spawned by: <0.94.0>
Message queue length: 0
Number of heap fragments: 0
Heap fragment data: 0
Link list: [{from,<0.94.0>,#Ref<0.2976686351.1182531587.244230>}]
Dictionary: [{iex_server,<0.94.0>},{'$ancestors',[<0.94.0>]},{iex_evaluator,#Ref<0.2976686351.1182531587.244090>},{'$initial_call',{'Elixir.IEx.Evaluator',init,5}}]
Reductions: 19620
Stack+heap: 4185
OldHeap: 10958
Heap unused: 919
OldHeap unused: 6693
BinVHeap: 10174
OldBinVHeap: 96
BinVHeap unused: 36248
OldBinVHeap unused: 46326
Memory: 122168
Stack dump:
Program counter: 0x0000023ba0eae490 ('Elixir.IEx.Evaluator':loop/1 + 248)
arity = 0
y(0) []
y(1) []
y(2) #Ref<0.2976686351.1182531587.244090>
y(3) <0.94.0>
y(4) #{env=>#{function=>nil,functions=>[{'Elixir.IEx.Helpers',[{'break!',3},{'break!',4},{breaks,0},{c,0},{c,1},{c,2},{cd,1},{clear,0},{continue,0},{exports,0},{exports,1},{flush,0},{h,0},{i,0},{i,1},{l,1},{ls,0},{ls,1},{n,0},{next,0},{nl,1},{nl,2},{open,0},{pid,1},{pid,3},{port,1},{port,2},{pwd,0},{r,1},{recompile,0},{recompile,1},{ref,1},{ref,4},{remove_breaks,0},{remove_breaks,1},{reset_break,1},{reset_break,3},{respawn,0},{runtime_info,0},{runtime_info,1},{v,0},{v,1},{whereami,0},{whereami,1}]},{'Elixir.Kernel',[{'!=',2},{'!==',2},{'*',2},{'**',2},{'+',1},{'+',2},{'++',2},{'-',1},{'-',2},{'--',2},{'/',2},{'<',2},{'<=',2},{'==',2},{'===',2},{'=~',2},{'>',2},{'>=',2},{abs,1},{apply,2},{apply,3},{binary_part,3},{binary_slice,2},{binary_slice,3},{bit_size,1},{byte_size,1},{ceil,1},{div,2},{elem,2},{exit,1},{floor,1},{'function_exported?',3},{get_and_update_in,3},{get_in,2},{hd,1},{inspect,1},{inspect,2},{is_atom,1},{is_binary,1},{is_bitstring,1},{is_boolean,1},{is_float,1},{is_function,1},{is_function,2},{is_integer,1},{is_list,1},{is_map,1},{is_map_key,2},{is_number,1},{is_pid,1},{is_port,1},{is_reference,1},{is_tuple,1},{length,1},{'macro_exported?',3},{make_ref,0},{map_size,1},{max,2},{min,2},{node,0},{node,1},{not,1},{pop_in,2},{put_elem,3},{put_in,3},{rem,2},{round,1},{self,0},{send,2},{spawn,1},{spawn,3},{spawn_link,1},{spawn_link,3},{spawn_monitor,1},{spawn_monitor,3},{struct,1},{struct,2},{'struct!',1},{'struct!',2},{throw,1},{tl,1},{to_timeout,1},{trunc,1},{tuple_size,1},{update_in,3}]}],line=>1,module=>nil,file=><<"iex">>,context=>nil,aliases=>[],lexical_tracker=>nil,macro_aliases=>[],versioned_vars=>#{},tracers=>[],requires=>['Elixir.Application','Elixir.IEx.Helpers','Elixir.Kernel'],macros=>[{'Elixir.IEx.Helpers',[{b,1},{'break!',1},{'break!',2},{h,1},{import_file,1},{import_file,2},{import_file_if_available,1},{import_if_available,1},{import_if_available,2},{open,1},{t,1},{use_if_available,1},{use_if_available,2}]},{'Elixir.Kernel',[{'!',1},{'&&',2},{'..',0},{'..',2},{'..//',3},{'<>',2},{'@',1},{'alias!',1},{and,2},{binding,0},{binding,1},{dbg,0},{dbg,1},{dbg,2},{def,1},{def,2},{defdelegate,2},{defexception,1},{defguard,1},{defguardp,1},{defimpl,2},{defimpl,3},{defmacro,1},{defmacro,2},{defmacrop,1},{defmacrop,2},{defmodule,2},{defoverridable,1},{defp,1},{defp,2},{defprotocol,2},{defstruct,1},{destructure,2},{get_and_update_in,2},{get_in,1},{if,2},{in,2},{is_exception,1},{is_exception,2},{is_nil,1},{is_non_struct_map,1},{is_struct,1},{is_struct,2},{'match?',2},{or,2},{pop_in,1},{put_in,2},{raise,1},{raise,2},{reraise,2},{reraise,3},{sigil_C,2},{sigil_D,2},{sigil_N,2},{sigil_R,2},{sigil_S,2},{sigil_T,2},{sigil_U,2},{sigil_W,2},{sigil_c,2},{sigil_r,2},{sigil_s,2},{sigil_w,2},{tap,2},{then,2},{to_char_list,1},{to_charlist,1},{to_string,1},{unless,2},{update_in,2},{use,1},{use,2},{'var!',1},{'var!',2},{'|>',2},{'||',2}]}],'struct'=>'Elixir.Macro.Env',context_modules=>[]},binding=>[],stacktrace=>nil,history=>#{size=>1,start=>1,queue=>{[{1,{ok,true}}],[]},'struct'=>'Elixir.IEx.History'},server=><0.94.0>,ref=>#Ref<0.2976686351.1182531587.244090>}

0x0000023ba8d7f458 Return addr 0x0000023ba0eadf9c ('Elixir.IEx.Evaluator':init/5 + 700)
y(0) nil
y(1) nil
y(2) []
y(3) []
y(4) []
y(5) <0.70.0>
y(6) []
y(7) Catch 0x0000023ba0eadff5 ('Elixir.IEx.Evaluator':init/5 + 789)

0x0000023ba8d7f4a0 Return addr 0x0000023ba0b18a70 (proc_lib:init_p_do_apply/3 + 208)
y(0) []
y(1) []
y(2) Catch 0x0000023ba0b18a85 (proc_lib:init_p_do_apply/3 + 229)

0x0000023ba8d7f4c0 Return addr 0x0000023ba08d2580 ()
Internal State: ACT_PRIO_NORMAL | USR_PRIO_NORMAL | PRQ_PRIO_NORMAL
=port:#Port<0.0>
State: CONNECTED|BINARY_IO
Slot: 0
Connected: <0.52.0>
Links: <0.52.0>
Port is UNIX fd not opened by emulator: 2/2
Input: 0
Output: 0
Queue: 0

An error has occurred that was not properly handled. Additional information is shown below. The PowerShell process will exit.
Unhandled exception. System.InvalidOperationException: Cannot read keys when either application does not have a console or when console input has been redirected. Try Console.Read.
at System.ConsolePal.ReadKey(Boolean intercept)
at Microsoft.PowerShell.Internal.VirtualTerminal.<>c.b__40_0()
at Microsoft.PowerShell.Internal.VirtualTerminal._TryIgnoreIOE[T](Func`1 f)
at Microsoft.PowerShell.Internal.VirtualTerminal.ReadKey()
at Microsoft.PowerShell.PSConsoleReadLine.ReadOneOrMoreKeys()
at Microsoft.PowerShell.PSConsoleReadLine.ReadKeyThreadProc()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)

@RaymondLoranger
Copy link
Author

When I type erl, I can exit it by typing a single Ctrl+C as shown in the attached.
erl with v1.18.1.docx

@RaymondLoranger
Copy link
Author

I just found a way to perform a "clean" exit from iex: Ctrl+G and then q. See attached.
iex exit with Ctrl+G.docx

@josevalim
Copy link
Member

@RaymondLoranger thank you for all help so far. Can you do one last favor? Can you please open up your iex.ps1 and change this line:

$elixirMainScript = Join-Path -Path $scriptPath -ChildPath "elixir.ps1"

Instead of elixir.ps1, it should say elixir.bat. Does that fix the double Ctrl+C issue?

@josevalim
Copy link
Member

You can also try removing the iex.ps1 file altogether.

@RaymondLoranger
Copy link
Author

Replacing elixir.ps1 with elixir.bat in file iex.ps1 gave the following:

iex
Usage: elixir.bat [options] [.exs file] [data]

## General options

  -e "COMMAND"                 Evaluates the given command (*)
  -h, --help                   Prints this message (standalone)
  -r "FILE"                    Requires the given files/patterns (*)
  -S SCRIPT                    Finds and executes the given script in $PATH
  -pr "FILE"                   Requires the given files/patterns in parallel (*)
  -pa "PATH"                   Prepends the given path to Erlang code path (*)
  -pz "PATH"                   Appends the given path to Erlang code path (*)
  -v, --version                Prints Erlang/OTP and Elixir versions (standalone)

  --erl "SWITCHES"             Switches to be passed down to Erlang (*)
  --eval "COMMAND"             Evaluates the given command, same as -e (*)
  --logger-otp-reports BOOL    Enables or disables OTP reporting
  --logger-sasl-reports BOOL   Enables or disables SASL reporting
  --no-halt                    Does not halt the Erlang VM after execution
  --short-version              Prints Elixir version (standalone)

etc.

HOWEVER, removing file iex.ps1 altogether DID fix the double Ctrl+C issue.
CONGRATS!
Only the color issue remains...

@josevalim
Copy link
Member

The color issue is expected. When you invoke something programatically, tools do not typically assume color should be enabled. That's how Elixir behaves on Unix and now it behaves consistent. So you can enable the color manually, to make it easier on your side, I have merged PR #14183, so you can run this command instead:

elixir --color true -S mix hex.outdated

It will be available in the next 1.18 version. For the double Ctrl+C, there is PR #14182, but I need to evaluate it more carefully.

@josevalim
Copy link
Member

With @wojtekmach's help, I was able to reproduce it only with Erlang and I reported it upstream: erlang/otp#9285

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

2 participants