glacambre / firenvim

Embed Neovim in Chrome, Firefox & others.
GNU General Public License v3.0
4.57k stars 144 forks source link

Neovim plugin and addon versions do not match #1614

Closed OsKaR31415 closed 1 month ago

OsKaR31415 commented 1 month ago

What happens

Firenvim is not starting. When clicking on the addon button, The error message is :

Neovim died without answering. Neovim plugin version (0.2.15) and addon version (0.2.16) do not match.

Debugging

I tried running echo 'abcde{}' | .local/share/firenvim/firenvim, and it runs with no error but also no output.

Here is the content of my .local/share/firenvim/firenvim :

#!/bin/sh
mkdir -p /var/folders/67/j6c23f3d5r18jd58813p8rbw0000gn/T//firenvim
chmod 700 /var/folders/67/j6c23f3d5r18jd58813p8rbw0000gn/T//firenvim
cd /var/folders/67/j6c23f3d5r18jd58813p8rbw0000gn/T//firenvim
export PATH="$PATH:/opt/homebrew/bin:/opt/homebrew/bin:/opt/local/bin:/opt/local/sbin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin:/Users/oscarplaisant/Library/Python/3.9/bin:/Applications/quarto/bin:/Applications/iTerm.app/Contents/Resources/utilities:/Users/oscarplaisant/.zsh_scripts/:/Users/oscarplaisant/.cargo/bin:/Users/oscarplaisant/.local/bin:/Users/oscarplaisant/.zsh_scripts/:/Users/oscarplaisant/.cargo/bin:/Users/oscarplaisant/.local/bin"
unset NVIM_LISTEN_ADDRESS
if [ -n "$VIM" ] && [ ! -d "$VIM" ]; then
  unset VIM
fi
if [ -n "$VIMRUNTIME" ] && [ ! -d "$VIMRUNTIME" ]; then
  unset VIMRUNTIME
fi

exec '/opt/homebrew/Cellar/neovim/0.9.5/bin/nvim' --headless --cmd "let g:firenvim_config={'globalSettings':{},'localSettings':{'.*':{}}}|let g:firenvim_i=[]|let g:firenvim_o=[]|let g:Firenvim_oi={i,d,e->add(g:firenvim_i,d)}|let g:Firenvim_oo={t->[chansend(2,t)]+add(g:firenvim_o,t)}|let g:firenvim_c=stdioopen({'on_stdin':{i,d,e->g:Firenvim_oi(i,d,e)},'on_print':{t->g:Firenvim_oo(t)}})" --cmd 'let g:started_by_firenvim = v:true' -c 'try|call firenvim#run()|catch /Unknown function/|call chansend(g:firenvim_c,["f\n\n\n"..json_encode({"messages":["Your plugin manager did not load the Firenvim plugin for neovim."],"version":"0.0.0"})])|call chansend(2,["Firenvim not in runtime path. &rtp="..&rtp])|qall!|catch|call chansend(g:firenvim_c,["l\n\n\n"..json_encode({"messages": ["Something went wrong when running firenvim. See troubleshooting guide."],"version":"0.0.0"})])|call chansend(2,[v:exception])|qall!|endtry'
glacambre commented 1 month ago

Thanks for opening a new issue :) Could you replace that last line with exec '/opt/homebrew/Cellar/neovim/0.9.5/bin/nvim' --headless --cmd "let g:firenvim_config={'globalSettings':{},'localSettings':{'.*':{}}}" --cmd 'let g:started_by_firenvim = v:true' -c 'call firenvim#run()' and then attempt running the script again with echo 'abcde{}' | ${XDG_DATA_HOME:-${HOME}/.local/share}/firenvim/firenvim?

OsKaR31415 commented 1 month ago

After replacing the last line with the one you gave, the script still outputs nothing : no error, no data.

glacambre commented 1 month ago

Does /opt/homebrew/Cellar/neovim/0.9.5/bin/nvim --version work at all?

OsKaR31415 commented 1 month ago

Does /opt/homebrew/Cellar/neovim/0.9.5/bin/nvim --version work at all? It does, and returns :


NVIM v0.9.5
Build type: Release
LuaJIT 2.1.1713773202
     fichier vimrc système : "$VIM/sysinit.vim"
           $VIM par défaut : "/opt/homebrew/Cellar/neovim/0.9.5/share/nvim

"

Run :checkhealth for more info

glacambre commented 1 month ago

Okay, let's bring in the big guns then. Could you reset .local/share/firenvim/firenvim to how it was (either by undoing the changes I asked you to make or re-running call firenvim#install()), then replace the file named firenvim.vim in your plugin installation directory with this one:

let s:firenvim_done = 0
let s:script_dir = expand(':p:h:h')

function! firenvim#get_chan() abort
        if (exists('g:last_focused_firenvim_channel'))
                return g:last_focused_firenvim_channel
        endif
        let l:uis = filter(nvim_list_uis(),
                \ {i, ui -> nvim_get_chan_info(ui.chan).client.name ==? 'Firenvim'})
        if len(l:uis) != 1
                if len(l:uis) == 0
                        throw 'firenvim#focus_page(): No firenvim ui found!'
                endif
                throw 'firenvim#focus_page(): Too many UIs found!'
        endif
        return uis[0].chan
endfunction

function! firenvim#eval_js(js, ...) abort
        let callback_name = get(a:, 1, '')
        call rpcnotify(firenvim#get_chan(), 'firenvim_eval_js', a:js, callback_name)
endfunction

" Asks the browser extension to release focus from the frame and focus the
" page instead
function! firenvim#focus_input() abort
        call rpcnotify(firenvim#get_chan(), 'firenvim_focus_input')
endfunction

" Asks the browser extension to release focus from the frame and focus the
" page instead
function! firenvim#focus_page() abort
        call rpcnotify(firenvim#get_chan(), 'firenvim_focus_page')
endfunction

" Asks the browser extension to release focus from the frame and focus the
" next page element, matching the behavior of pressing `` in the page.
function! firenvim#focus_next() abort
        call rpcnotify(firenvim#get_chan(), 'firenvim_focus_next')
endfunction

" Asks the browser extension to release focus from the frame and focus the
" next page element, matching the behavior of pressing `` in the page.
function! firenvim#focus_prev() abort
        call rpcnotify(firenvim#get_chan(), 'firenvim_focus_prev')
endfunction

" Asks the browser extension to hide the firenvim frame
function! firenvim#hide_frame() abort
        call rpcnotify(firenvim#get_chan(), 'firenvim_hide_frame')
endfunction

" Asks the browser extension to write to the text area. Either two or no
" arguments.
" 1st arg: list of strings to write, one string per line, empty strings for
"          empty lines
" 2nd arg: position of the cursor
function! firenvim#write(...) abort
        let l:text = ''
        let l:cursor = [0, 0]
        if a:0 == 0
                let l:text = nvim_buf_get_lines(0, 0, -1, 0)
                let l:cursor = nvim_win_get_cursor(0)
        elseif a:0 == 2
                let l:text = a:1
                let l:cursor = a:2
                if type(l:text) != v:t_list || (len(l:text) > 0 && type(l:text[0]) != v:t_string)
                        throw "firenvim#write's first argument must be a list of strings"
                endif
                if type(l:text) != v:t_list
                        \ || len(l:cursor) != 2
                        \ || type(l:cursor[0]) != v:t_number
                        \ || type(l:cursor[1]) != v:t_number
                        throw "firenvim#write's second argument must be a list of two numbers"
                endif
        else
                throw 'firenvim#write should be called either with 0 or 2 arguments'
        endif
        call rpcnotify(firenvim#get_chan(), 'firenvim_bufwrite', {'text': l:text, 'cursor': l:cursor})
endfunction

" Asks the browser extension to send one or multiple key events to the
" underlying input field.
function! firenvim#press_keys(...) abort
        if a:0 < 1
                throw 'firenvim#press_keys expects at least one argument'
        endif
        let l:keys = copy(a:000)
        if type(l:keys[0]) == type([])
                if a:0 > 1
                        throw 'firenvim#press_keys expects a single list argument'
                endif
                let l:keys = l:keys[0]
        endif
        if len(filter(copy(l:keys), { key, value -> type(value) == type("") })) != len(l:keys)
                throw 'Key symbols must be strings.'
        endif
        call rpcnotify(firenvim#get_chan(), 'firenvim_press_keys', l:keys)
endfunction

" Turns a wsl path (forward slashes) into a windows one (backslashes)
function! s:to_windows_path(path) abort
        if a:path[0] !=# '/'
                return a:path
        endif
        let l:path_components = split(a:path, '/')
        return join([toupper(l:path_components[1]) . ':'] + path_components[2:-1], '\')
endfunction

" Turns a windows path (backslashes) into a wsl one (forward slashes)
function! s:to_wsl_path(path) abort
        if a:path[0] ==# '/'
                return a:path
        endif
        if executable('wslpath')
                try
                        " 0:-2 because we need to remove the \r\n
                        return system(['wslpath', '-a', '-u', a:path])[0:-2]
                catch
                endtry
        endif
        let l:path_components = split(a:path, '\\')
        return join(['/mnt', tolower(path_components[0][0:-2])] + l:path_components[1:-1], '/')
endfunction

" Simple helper to build the right path depending on the platform.
function! s:build_path(list) abort
        let l:path_separator = '/'
        if has('win32')
                let l:path_separator = "\\"
        endif
        if s:is_wsl
                let a:list[0] = s:to_wsl_path(a:list[0])
        endif
        return join(a:list, l:path_separator)
endfunction

" Retrieves a windows env var from wsl. Retrieves a windows path (with
" backslashes!)
function! s:get_windows_env_path(env) abort
        if has('win32')
                let l:env = a:env
                if l:env[0] ==# '%'
                        let l:env = '$' . l:env[1:-2]
                endif
                return expand(l:env)
        endif
        if s:is_wsl
                let l:env = a:env
                if l:env[0] ==# '$'
                        let l:env = '%' . l:env[1:-1] . '%'
                endif
                try
                        let l:cmd_output = system(['cmd.exe', '/c', 'echo', l:env])
                catch /E475:.*cmd.exe' is not executable/
                        try
                                let l:cmd_output = system(['/mnt/c/Windows/System32/cmd.exe', '/c', 'echo', l:env])
                        catch /E475:.*cmd.exe' is not executable/
                                throw 'Error: Firenvim could not find cmd.exe from WSL on your system. Please report this issue.'
                        endtry
                endtry
                return cmd_output[match(l:cmd_output, 'C:\\'):-3]
        endif
        throw 'Used get_windows_env_path on non-windows platform!'
endfunction

" Entry point of the vim-side of the extension.
" This function does the following things:
" - Get a security token from neovim's stdin
" - Bind itself to a TCP port
" - Write the security token + tcp port number to stdout()
" - Take care of forwarding messages received on the TCP port to neovim
function! firenvim#run() abort
        call writefile(["176"], "/tmp/firenvim.log", "a")
        " Write messages to stdout according to the format expected by
        " Firefox's native messaging protocol
        function! WriteStdout(id, data) abort
                call writefile(["180"], "/tmp/firenvim.log", "a")
                " The native messaging protocol expects the message's length
                " to precede the message. It has to use native endianness. We
                " assume big endian.
                let l:len = strlen(a:data)
                call writefile(["185"], "/tmp/firenvim.log", "a")
                let l:lenstr = luaeval('string.char(bit.band(' . l:len . ', 255))'
                                        \. '.. string.char(bit.band(bit.rshift(' . l:len . ', 8), 255))'
                                        \. '.. string.char(bit.band(bit.rshift(' . l:len . ', 16), 255))'
                                        \. '.. string.char(bit.band(bit.rshift(' . l:len . ', 24), 255))')
                call writefile(["190"], "/tmp/firenvim.log", "a")
                " https://github.com/neovim/neovim/pull/15211
                " Neovim 0.6 breaking change.
                try
                        call writefile(["194"], "/tmp/firenvim.log", "a")
                        call chansend(a:id, [join(l:lenstr['_VAL']) . a:data])
                        call writefile(["196"], "/tmp/firenvim.log", "a")
                catch
                        call writefile(["198"], "/tmp/firenvim.log", "a")
                        call chansend(a:id, l:lenstr)
                        call writefile(["200"], "/tmp/firenvim.log", "a")
                        call chansend(a:id, a:data)
                        call writefile(["202"], "/tmp/firenvim.log", "a")
                endtry
                call writefile(["204"], "/tmp/firenvim.log", "a")
        endfunction
        call writefile(["206"], "/tmp/firenvim.log", "a")
        let s:accumulated_data = ''
        call writefile(["208"], "/tmp/firenvim.log", "a")
        function! OnStdin(id, data, event) abort
                call writefile(["210"], "/tmp/firenvim.log", "a")
                " `:h channel-stdio`: empty a:data means FD closed?
                if a:data == ['']
                        call writefile(["213"], "/tmp/firenvim.log", "a")
                        qall!
                end
                call writefile(["216"], "/tmp/firenvim.log", "a")
                if s:firenvim_done
                        call writefile(["218"], "/tmp/firenvim.log", "a")
                        return
                endif
                call writefile(["221"], "/tmp/firenvim.log", "a")
                let l:data = s:accumulated_data . a:data[0]
                call writefile(["223"], "/tmp/firenvim.log", "a")
                try
                        call writefile(["225"], "/tmp/firenvim.log", "a")
                        let l:params = json_decode(matchstr(l:data[4:], '{[^}]*}'))
                        call writefile(["227"], "/tmp/firenvim.log", "a")
                catch
                        call writefile(["229"], "/tmp/firenvim.log", "a")
                        let s:accumulated_data = l:data
                        call writefile(["231"], "/tmp/firenvim.log", "a")
                        return
                endtry
                call writefile(["234"], "/tmp/firenvim.log", "a")
                let s:firenvim_done = v:true
                call writefile(["236"], "/tmp/firenvim.log", "a")

                let l:package_json = s:build_path([s:script_dir, 'package.json'])
                call writefile(["239"], "/tmp/firenvim.log", "a")
                let l:version = json_decode(join(readfile(l:package_json), "\n"))['version']
                call writefile(["241"], "/tmp/firenvim.log", "a")
                let l:result = { 'version': l:version }
                call writefile(["243"], "/tmp/firenvim.log", "a")

                if exists('g:firenvim_config')
                        call writefile(["246"], "/tmp/firenvim.log", "a")
                        let l:result['settings'] = g:firenvim_config
                        call writefile(["248"], "/tmp/firenvim.log", "a")
                endif

                call writefile(["251"], "/tmp/firenvim.log", "a")
                if exists('g:firenvim_o')
                        call writefile(["253"], "/tmp/firenvim.log", "a")
                        let l:result['messages'] = g:firenvim_o
                        call writefile(["255"], "/tmp/firenvim.log", "a")
                endif

                call writefile(["258"], "/tmp/firenvim.log", "a")
                if has_key(l:params, 'newInstance') && l:params['newInstance']
                        call writefile(["260"], "/tmp/firenvim.log", "a")
                        let l:port = luaeval("require('firenvim').start_server('" .
                                                \ l:params['password'] .
                                                \ "')")
                        call writefile(["264"], "/tmp/firenvim.log", "a")
                        let l:result['port'] = l:port
                        call writefile(["266"], "/tmp/firenvim.log", "a")
                endif

                let l:response = ''
                call writefile(["270"], "/tmp/firenvim.log", "a")
                try
                        call writefile(["272"], "/tmp/firenvim.log", "a")
                        let l:response = json_encode(result)
                        call writefile(["274"], "/tmp/firenvim.log", "a")
                catch /E474/
                        call writefile(["276"], "/tmp/firenvim.log", "a")
                        call remove(result, 'settings')
                        call writefile(["278"], "/tmp/firenvim.log", "a")
                        if !has_key(result, 'messages')
                                call writefile(["280"], "/tmp/firenvim.log", "a")
                                let result['messages'] = []
                                call writefile(["282"], "/tmp/firenvim.log", "a")
                        endif
                        call writefile(["284"], "/tmp/firenvim.log", "a")
                        call add(result['messages'], 'Error serializing settings:' . v:exception)
                        call writefile(["286"], "/tmp/firenvim.log", "a")
                        let l:response = json_encode(result)
                        call writefile(["288"], "/tmp/firenvim.log", "a")
                endtry
                call writefile(["290"], "/tmp/firenvim.log", "a")
                call WriteStdout(a:id, l:response)
                call writefile(["292"], "/tmp/firenvim.log", "a")
        endfunction
        " g:firenvim_c might not exist for firenvim installations dating back
        " to 2021 and earlier.
        " call writefile(["296"], "/tmp/firenvim.log", "a")
        if exists('g:firenvim_c')
                call writefile(["298"], "/tmp/firenvim.log", "a")
                for data in g:firenvim_i
                        call writefile(["300"], "/tmp/firenvim.log", "a")
                        call OnStdin(g:firenvim_c, data, 'stdin')
                        call writefile(["302"], "/tmp/firenvim.log", "a")
                endfor
                call writefile(["304"], "/tmp/firenvim.log", "a")
                let g:Firenvim_oi = funcref('OnStdin')
                call writefile(["306"], "/tmp/firenvim.log", "a")
        else
                call writefile(["308"], "/tmp/firenvim.log", "a")
                let l:chanid = stdioopen({ 'on_stdin': 'OnStdin' })
                call writefile(["310"], "/tmp/firenvim.log", "a")
        endif
        call writefile(["312"], "/tmp/firenvim.log", "a")
endfunction

" Wrapper function that executes funcname(...args) if a $DRY_RUN env variable
" isn't defined and just echoes `funcname(...args)` if it is.
function! s:maybe_execute(funcname, ...) abort
        let l:result = ''
        if !empty($DRY_RUN)
                echo a:funcname . '(' . string(a:000)[1:-2] . ')'
        else
                let l:result = call(a:funcname, a:000)
        end
        return l:result
endfunction

" Returns the name of the script that should be executed by the browser.
function! s:get_executable_name() abort
        if has('win32') || s:is_wsl
                return 'firenvim.bat'
        endif
        return 'firenvim'
endfunction

" Returns the path of the directory in which firenvim will run when the
" browser launches it.
" On wsl, this is a path living on the linux side.
function! s:get_runtime_dir_path() abort
        let l:xdg_runtime_dir = $XDG_RUNTIME_DIR
        if l:xdg_runtime_dir ==# ''
                if has('win32') || s:is_wsl
                        let l:xdg_runtime_dir = s:get_windows_env_path('$TEMP')
                        if l:xdg_runtime_dir ==# ''
                                let l:xdg_runtime_dir = s:get_windows_env_path('$TMP')
                        endif
                        if l:xdg_runtime_dir ==# ''
                                let l:xdg_runtime_dir = s:get_windows_env_path('$USERPROFILE')
                                if l:xdg_runtime_dir ==# ''
                                        let l:xdg_runtime_dir = fnamemodify(stdpath('data'), ':h')
                                else
                                        let l:xdg_runtime_dir = l:xdg_runtime_dir . '\AppData\Local\Temp'
                                endif
                        endif
                else
                        let l:xdg_runtime_dir = $TMPDIR
                        if l:xdg_runtime_dir ==# ''
                                let l:xdg_runtime_dir = '/tmp/'
                        endif
                endif
        endif
        return s:build_path([l:xdg_runtime_dir, 'firenvim'])
endfunction

" Returns the directory in which the firenvim script is written.
function! s:get_data_dir_path() abort
        let l:xdg_data_home = $XDG_DATA_HOME
        if s:is_wsl
                let l:xdg_data_home = s:get_windows_env_path('%LOCALAPPDATA%')
        elseif l:xdg_data_home ==# ''
                let l:xdg_data_home = fnamemodify(stdpath('data'), ':h')
        endif
        return s:build_path([l:xdg_data_home, 'firenvim'])
endfunction

function! s:firefox_config_exists() abort
        let l:p = [$HOME, '.mozilla']
        if has('mac')
                let l:p = [$HOME, 'Library', 'Application Support', 'Mozilla']
        elseif has('win32')
                let l:p = [$HOME, 'AppData', 'Roaming', 'Mozilla', 'Firefox']
        elseif s:is_wsl
                let l:p = [s:get_windows_env_path('%APPDATA%'), 'Mozilla', 'Firefox']
        endif
        return isdirectory(s:build_path(l:p))
endfunction

function! s:get_firefox_manifest_dir_path() abort
        if has('mac')
                return s:build_path([$HOME, 'Library', 'Application Support', 'Mozilla', 'NativeMessagingHosts'])
        elseif has('win32') || s:is_wsl
                return s:get_data_dir_path()
        end
        return s:build_path([$HOME, '.mozilla', 'native-messaging-hosts'])
endfunction

function! s:librewolf_config_exists() abort
        let l:p = [$HOME, '.librewolf']
        if has('win32') || s:is_wsl
                let l:p = [s:get_windows_env_path('%USERPROFILE%'), '.librewolf']
        endif
        return isdirectory(s:build_path(l:p))
endfunction

function! s:get_librewolf_manifest_dir_path() abort
        if has('win32') || s:is_wsl
                return s:get_data_dir_path()
        end
        return s:build_path([$HOME, '.librewolf', 'native-messaging-hosts'])
endfunction

function! s:arc_config_exists() abort
        let l:p = [$HOME, '.config', 'Arc']
        if has('mac')
                let l:p = [$HOME, 'Library', 'Application Support', 'Arc']
        end
        return isdirectory(s:build_path(l:p))
endfunction

function! s:brave_config_exists() abort
        let l:p = [$HOME, '.config', 'BraveSoftware']
        if has('mac')
                let l:p = [$HOME, 'Library', 'Application Support', 'BraveSoftware']
        elseif has('win32')
                let l:p = [$HOME, 'AppData', 'Local', 'BraveSoftware']
        elseif s:is_wsl
                let l:p = [s:get_windows_env_path('%LOCALAPPDATA%'), 'BraveSoftware']
        elseif !empty($XDG_CONFIG_HOME)
                let l:p = [$XDG_CONFIG_HOME, 'BraveSoftware']
        end
        return isdirectory(s:build_path(l:p))
endfunction

function! s:opera_config_exists() abort
        let l:p = [$HOME, '.config', 'opera']
        if has('mac')
                let l:p = [$HOME, 'Library', 'Application Support', 'com.operasoftware.Opera']
        elseif has('win32')
                let l:p = [$HOME, 'AppData', 'Local', 'Opera Software']
        elseif s:is_wsl
                let l:p = [s:get_windows_env_path('%LOCALAPPDATA%'), 'Opera Software']
        elseif !empty($XDG_CONFIG_HOME)
                let l:p = [$XDG_CONFIG_HOME, 'opera']
        end
        return isdirectory(s:build_path(l:p))
endfunction

function! s:vivaldi_config_exists() abort
        let l:p = [$HOME, '.config', 'vivaldi']
        if has('mac')
                let l:p = [$HOME, 'Library', 'Application Support', 'Vivaldi']
        elseif has('win32')
                let l:p = [$HOME, 'AppData', 'Local', 'Vivaldi']
        elseif s:is_wsl
                let l:p = [s:get_windows_env_path('%LOCALAPPDATA%'), 'Vivaldi']
        elseif !empty($XDG_CONFIG_HOME)
                let l:p = [$XDG_CONFIG_HOME, 'vivaldi']
        end
        return isdirectory(s:build_path(l:p))
endfunction

function! s:chrome_config_exists() abort
        let l:p = [$HOME, '.config', 'google-chrome']
        if has('mac')
                let l:p = [$HOME, 'Library', 'Application Support', 'Google', 'Chrome']
        elseif has('win32')
                let l:p = [$HOME, 'AppData', 'Local', 'Google', 'Chrome']
        elseif s:is_wsl
                let l:p = [s:get_windows_env_path('%LOCALAPPDATA%'), 'Google', 'Chrome']
        elseif !empty($XDG_CONFIG_HOME)
                let l:p = [$XDG_CONFIG_HOME, 'google-chrome']
        end
        return isdirectory(s:build_path(l:p))
endfunction

function! s:ungoogled_chromium_config_exists() abort
        let l:p = [$HOME, '.config', 'ungoogled-chromium']
        if has('mac')
                " According to #1007, on macos, things work when using the
                " regular chrome dir.
                return v:false
        elseif has('win32') || s:is_wsl
                " Don't know what should be used here. Wait for somebody to
                " complain.
                return v:false
        elseif !empty($XDG_CONFIG_HOME)
                let l:p = [$XDG_CONFIG_HOME, 'ungoogled-chromium']
        end
        return isdirectory(s:build_path(l:p))
endfunction

function! s:edge_config_exists() abort
        let l:p = [$HOME, '.config', 'microsoft-edge']
        if has('mac')
                let l:p = [$HOME, 'Library', 'Application Support', 'Microsoft', 'Edge']
        elseif has('win32')
                let l:p = [$HOME, 'AppData', 'Local', 'Microsoft', 'Edge']
        elseif s:is_wsl
                let l:p = [s:get_windows_env_path('%LOCALAPPDATA%'), 'Microsoft', 'Edge']
        elseif !empty($XDG_CONFIG_HOME)
                let l:p = [$XDG_CONFIG_HOME, 'microsoft-edge']
        end
        return isdirectory(s:build_path(l:p))
endfunction

function! s:chrome_dev_config_exists() abort
        let l:p = [$HOME, '.config', 'google-chrome-unstable']
        if has('mac')
                let l:p = [$HOME, 'Library', 'Application Support', 'Google', 'Chrome Dev']
        elseif !empty($XDG_CONFIG_HOME)
                let l:p = [$XDG_CONFIG_HOME, 'google-chrome-unstable']
        end
        return isdirectory(s:build_path(l:p))
endfunction

function! s:get_chrome_manifest_dir_path() abort
        if has('mac')
                return s:build_path([$HOME, 'Library', 'Application Support', 'Google', 'Chrome', 'NativeMessagingHosts'])
        elseif has('win32') || s:is_wsl
                return s:get_data_dir_path()
        end
        if !empty($XDG_CONFIG_HOME)
                return s:build_path([$XDG_CONFIG_HOME, 'google-chrome', 'NativeMessagingHosts'])
        end
        return s:build_path([$HOME, '.config', 'google-chrome', 'NativeMessagingHosts'])
endfunction

function! s:get_vivaldi_manifest_dir_path() abort
        if has('mac')
                return s:get_chrome_manifest_dir_path()
        elseif has('win32') || s:is_wsl
                return s:get_chrome_manifest_dir_path()
        end
        " https://github.com/glacambre/firenvim/issues/1433
        if !empty($XDG_CONFIG_HOME)
                return s:build_path([$XDG_CONFIG_HOME, 'vivaldi', 'NativeMessagingHosts'])
        end
        return s:build_path([$HOME, '.config', 'vivaldi', 'NativeMessagingHosts'])
endfunction

function! s:get_ungoogled_chromium_manifest_dir_path() abort
        if has('mac') || has('win32') || s:is_wsl
                throw "Ungoogled chromium isn't supported. Please open an issue to add support."
        end
        if !empty($XDG_CONFIG_HOME)
                return s:build_path([$XDG_CONFIG_HOME, 'ungoogled-chromium', 'NativeMessagingHosts'])
        end
        return s:build_path([$HOME, '.config', 'ungoogled-chromium', 'NativeMessagingHosts'])
endfunction

function! s:get_edge_manifest_dir_path() abort
        if has('mac')
                return s:build_path([$HOME, 'Library', 'Application Support', 'Microsoft', 'Edge', 'NativeMessagingHosts'])
        elseif has('win32') || s:is_wsl
                return s:get_data_dir_path()
        end
        if !empty($XDG_CONFIG_HOME)
                return s:build_path([$XDG_CONFIG_HOME, 'microsoft-edge', 'NativeMessagingHosts'])
        end
        return s:build_path([$HOME, '.config', 'microsoft-edge', 'NativeMessagingHosts'])
endfunction

function! s:get_chrome_dev_manifest_dir_path() abort
        if has('mac')
                return s:build_path([$HOME, 'Library', 'Application Support', 'Google', 'Chrome Dev', 'NativeMessagingHosts'])
        elseif has('win32') || s:is_wsl
                throw 'No chrome dev on win32.'
        end
        if !empty($XDG_CONFIG_HOME)
                return s:build_path([$XDG_CONFIG_HOME, 'google-chrome-unstable', 'NativeMessagingHosts'])
        end
        return s:build_path([$HOME, '.config', 'google-chrome-unstable', 'NativeMessagingHosts'])
endfunction

function! s:get_brave_manifest_dir_path() abort
        if has('mac')
                return s:get_chrome_manifest_dir_path()
        elseif has('win32') || s:is_wsl
                return s:get_chrome_manifest_dir_path()
        end
        if !empty($XDG_CONFIG_HOME)
                return s:build_path([$XDG_CONFIG_HOME, 'BraveSoftware', 'Brave-Browser', 'NativeMessagingHosts'])
        end
        return s:build_path([$HOME, '.config', 'BraveSoftware', 'Brave-Browser', 'NativeMessagingHosts'])
endfunction

function! s:canary_config_exists() abort
        if has('mac')
                let l:p = [$HOME, 'Library', 'Application Support', 'Google', 'Chrome Canary']
        elseif has('win32')
                let l:p = [$HOME, 'AppData', 'Local', 'Google', 'Chrome SxS']
        elseif s:is_wsl
                let l:p = [s:get_windows_env_path('%LOCALAPPDATA%'), 'Google', 'Chrome SxS']
        else
                " Chrome canary doesn't exist on linux
                return v:false
        end
        return isdirectory(s:build_path(l:p))
endfunction

function! s:get_canary_manifest_dir_path() abort
        if has('mac')
                return s:build_path([$HOME, 'Library', 'Application Support', 'Google', 'Chrome Canary', 'NativeMessagingHosts'])
        elseif has('win32') || s:is_wsl
                return s:get_data_dir_path()
        end
        throw "Chrome Canary doesn't exist on Linux"
endfunction

function! s:chromium_config_exists() abort
        let l:p = [$HOME, '.config', 'chromium']
        if has('mac')
                let l:p = [$HOME, 'Library', 'Application Support', 'Chromium']
        elseif has('win32')
                let l:p = [$HOME, 'AppData', 'Local', 'Chromium']
        elseif s:is_wsl
                let l:p = [s:get_windows_env_path('%LOCALAPPDATA%'), 'Chromium']
        end
        if !empty($XDG_CONFIG_HOME)
                let l:p = [$XDG_CONFIG_HOME, 'chromium', 'NativeMessagingHosts']
        end
        return isdirectory(s:build_path(l:p))
endfunction

function! s:get_chromium_manifest_dir_path() abort
        if has('mac')
                return s:build_path([$HOME, 'Library', 'Application Support', 'Chromium', 'NativeMessagingHosts'])
        elseif has('win32') || s:is_wsl
                return s:get_data_dir_path()
        end
        if !empty($XDG_CONFIG_HOME)
                return s:build_path([$XDG_CONFIG_HOME, 'chromium', 'NativeMessagingHosts'])
        end
        return s:build_path([$HOME, '.config', 'chromium', 'NativeMessagingHosts'])
endfunction

function! s:get_progpath() abort
        let l:result = v:progpath
        if $APPIMAGE !=# ''
                " v:progpath is different every time you run neovim appimages
                let l:result = $APPIMAGE
        endif
        " Some package managers will install neovim in a version-specific path
        " that v:progpath will point to. This is an issue because the path may
        " break when neovim is updated. Try to detect these cases, work around
        " them if possible and warn the user.
        let l:specific_installs = {
                \ 'homebrew': {
                        \ 'pattern': '^/usr/local/Cellar/',
                        \ 'constant_paths': ['/usr/local/opt/nvim']
                \ },
                \ 'nix': {
                        \ 'pattern': '^/nix/store/',
                        \ 'constant_paths': [
                                \ expand('$HOME/.nix-profile/bin/nvim'),
                                \ expand('/etc/profiles/per-user/$USER/bin/nvim'),
                                \ '/run/current-system/sw/bin/nvim'
                        \ ]
                \ }
        \ }
        for l:package_manager in keys(l:specific_installs)
                let l:install = l:specific_installs[l:package_manager]
                if match(l:result, l:install['pattern']) == 0
                        let l:warning = 'Warning: ' . l:package_manager . ' path detected. '
                        let l:alternative_found = v:false
                        for l:constant_path in l:install['constant_paths']
                                if executable(l:constant_path)
                                        let l:warning = l:warning .
                                                \ "Using '" . l:constant_path . "'" .
                                                \ "' instead of '" . l:result . "'"
                                        let l:result = l:constant_path
                                        let l:alternative_found = v:true
                                        break
                                endif
                        endfor
                        if !l:alternative_found
                                let l:warning = l:warning .
                                        \ 'Firenvim may break next time you update neovim.'
                        endif
                        echo l:warning
                endif
        endfor
        return l:result
endfunction

function! s:capture_env_var(var) abort
        let l:value = eval('$' . a:var)
        if l:value ==? ''
                return ''
        endif
        return 'if [ ! -n "$' . a:var . '" ]; then' . "\n" .
              \'  ' . a:var . "='" . l:value . "'\n" .
              \'  export ' . a:var . "\n" .
              \"fi\n"
endfunction

function! s:get_executable_content(data_dir, prolog) abort
        let l:stdioopen = ''
        if api_info().version.major > 0 || api_info().version.minor > 6
                let l:stdioopen = '--cmd "' .
                                        \"let g:firenvim_config={'globalSettings':{},'localSettings':{'.*':{}}}|" .
                                        \'let g:firenvim_i=[]|' .
                                        \'let g:firenvim_o=[]|' .
                                        \'let g:Firenvim_oi={i,d,e->add(g:firenvim_i,d)}|' .
                                        \'let g:Firenvim_oo={t->[chansend(2,t)]+add(g:firenvim_o,t)}|' .
                                        \"let g:firenvim_c=stdioopen({'on_stdin':{i,d,e->g:Firenvim_oi(i,d,e)},'on_print':{t->g:Firenvim_oo(t)}})".
                                        \'"'
        endif
        if s:is_wsl
                " Get path of firenvim script on the linux side, execute that
                " from the windows batch script
                let s:is_wsl = v:false
                let l:script_path = s:get_firenvim_script_path()
                let s:is_wsl = v:true
                return "@echo off\r\nwsl \"" . l:script_path . '"'
        endif
        if has('win32') || s:is_wsl
                let l:wsl_prefix = ''
                if s:is_wsl
                        let l:wsl_prefix = 'wsl'
                endif
                let l:dir = s:to_windows_path(a:data_dir)
                return  "@echo off\r\n" .
                                        \ "mkdir \"" . l:dir . "\" 2>nul\r\n" .
                                        \ "cd \"" . l:dir . "\"\r\n" .
                                        \ a:prolog . "\r\n" .
                                        \ l:wsl_prefix . ' "' . s:get_progpath() . '" --headless ' . l:stdioopen . ' --cmd "let g:started_by_firenvim = v:true" -c "call firenvim#run()"' . "\r\n"
        endif
        return "#!/bin/sh\n" .
                                \ 'mkdir -p ' . a:data_dir . "\n" .
                                \ 'chmod 700 ' . a:data_dir . "\n" .
                                \ 'cd ' . a:data_dir . "\n" .
                                \ 'export PATH="$PATH:' . $PATH . "\"\n" .
                                \ "unset NVIM_LISTEN_ADDRESS\n" .
                                \ 'if [ -n "$VIM" ] && [ ! -d "$VIM" ]; then' . "\n" .
                                \ "  unset VIM\n" .
                                \ "fi\n" .
                                \ 'if [ -n "$VIMRUNTIME" ] && [ ! -d "$VIMRUNTIME" ]; then' . "\n" .
                                \ "  unset VIMRUNTIME\n" .
                                \ "fi\n" .
                                \ s:capture_env_var('XDG_DATA_HOME') .
                                \ s:capture_env_var('XDG_CONFIG_HOME') .
                                \ s:capture_env_var('XDG_STATE_HOME') .
                                \ s:capture_env_var('XDG_DATA_DIRS') .
                                \ s:capture_env_var('XDG_CONFIG_DIRS') .
                                \ s:capture_env_var('XDG_CACHE_HOME') .
                                \ s:capture_env_var('XDG_RUNTIME_DIR') .
                                \ s:capture_env_var('NVIM_APPNAME') .
                                \ a:prolog . "\n" .
                                \ "exec '" . s:get_progpath() .
                                  \ "' --headless " . l:stdioopen .
                                  \ " --cmd 'let g:started_by_firenvim = v:true' " .
                                  \ "-c 'try|" .
                                      \ 'call firenvim#run()|' .
                                    \ 'catch /Unknown function/|' .
                                      \ "call chansend(g:firenvim_c,[\"f\\n\\n\\n\"..json_encode({\"messages\":[\"Your plugin manager did not load the Firenvim plugin for neovim.\"],\"version\":\"0.0.0\"})])|" .
                                      \ "call chansend(2,[\"Firenvim not in runtime path. &rtp=\"..&rtp])|" .
                                      \ 'qall!|' .
                                    \ 'catch|' .
                                      \ "call chansend(g:firenvim_c,[\"l\\n\\n\\n\"..json_encode({\"messages\": [\"Something went wrong when running firenvim. See troubleshooting guide.\"],\"version\":\"0.0.0\"})])|" .
                                      \ 'call chansend(2,[v:exception])|' .
                                      \ 'qall!|' .
                                  \ "endtry'\n"
endfunction

function! s:get_manifest_beginning(execute_nvim_path) abort
        return '{
                                \ "name": "firenvim",
                                \ "description": "Turn your browser into a Neovim GUI.",
                                \ "path": "' . substitute(a:execute_nvim_path, '\', '\\\\', 'g') . '",
                                \ "type": "stdio",
                                \'
endfunction

function! s:get_chrome_manifest(execute_nvim_path) abort
        return s:get_manifest_beginning(a:execute_nvim_path) .
                                \' "allowed_origins": [
                                \ "chrome-extension://egpjdkipkomnmjhjmdamaniclmdlobbo/"
                                \ ]
                                \}'
endfunction

function! s:get_firefox_manifest(execute_nvim_path) abort
        return s:get_manifest_beginning(a:execute_nvim_path) .
                                \' "allowed_extensions": ["firenvim@lacamb.re"]
                                \}'
endfunction

function! s:key_to_ps1_str(key, manifest_path) abort
        let l:ps1_content = ''
        let l:key_arr = split(a:key, '\')
        let l:i = 0
        for l:i in range(2, len(l:key_arr) - 1)
                let l:ps1_content = l:ps1_content . "\nNew-Item -Path \"" . join(key_arr[0:i], '\') . '"'
        endfor
        " Then, assign a value to it
        return l:ps1_content . "\nSet-Item -Path \"" .
                                \ a:key .
                                \ '\" -Value "' . s:to_windows_path(a:manifest_path) . '"'
endfunction

function! s:get_browser_configuration() abort
        " Brave, Opera and Vivaldi all rely on Chrome's native messenger
        let l:browsers = {
                \'arc': {
                        \ 'has_config': s:arc_config_exists(),
                        \ 'manifest_content': function('s:get_chrome_manifest'),
                        \ 'manifest_dir_path': function('s:get_chrome_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Google\Chrome\NativeMessagingHosts\firenvim',
                \},
                \'brave': {
                        \ 'has_config': s:brave_config_exists(),
                        \ 'manifest_content': function('s:get_chrome_manifest'),
                        \ 'manifest_dir_path': function('s:get_brave_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Google\Chrome\NativeMessagingHosts\firenvim',
                \},
                \'chrome': {
                        \ 'has_config': s:chrome_config_exists(),
                        \ 'manifest_content': function('s:get_chrome_manifest'),
                        \ 'manifest_dir_path': function('s:get_chrome_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Google\Chrome\NativeMessagingHosts\firenvim',
                \},
                \'chrome-canary': {
                        \ 'has_config': s:canary_config_exists(),
                        \ 'manifest_content': function('s:get_chrome_manifest'),
                        \ 'manifest_dir_path': function('s:get_canary_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Google\Chrome\NativeMessagingHosts\firenvim',
                \},
                \'chrome-dev': {
                        \ 'has_config': s:chrome_dev_config_exists(),
                        \ 'manifest_content': function('s:get_chrome_manifest'),
                        \ 'manifest_dir_path': function('s:get_chrome_dev_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Google\Chrome\NativeMessagingHosts\firenvim',
                \},
                \'chromium': {
                        \ 'has_config': s:chromium_config_exists(),
                        \ 'manifest_content': function('s:get_chrome_manifest'),
                        \ 'manifest_dir_path': function('s:get_chromium_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Chromium\NativeMessagingHosts\firenvim',
                \},
                \'edge': {
                        \ 'has_config': s:edge_config_exists(),
                        \ 'manifest_content': function('s:get_chrome_manifest'),
                        \ 'manifest_dir_path': function('s:get_edge_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Microsoft\Edge\NativeMessagingHosts\firenvim',
                \},
                \'firefox': {
                        \ 'has_config': s:firefox_config_exists(),
                        \ 'manifest_content': function('s:get_firefox_manifest'),
                        \ 'manifest_dir_path': function('s:get_firefox_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Mozilla\NativeMessagingHosts\firenvim',
                \},
                \'librewolf': {
                        \ 'has_config': s:librewolf_config_exists(),
                        \ 'manifest_content': function('s:get_firefox_manifest'),
                        \ 'manifest_dir_path': function('s:get_librewolf_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\LibreWolf\NativeMessagingHosts\firenvim',
                \},
                \'opera': {
                        \ 'has_config': s:opera_config_exists(),
                        \ 'manifest_content': function('s:get_chrome_manifest'),
                        \ 'manifest_dir_path': function('s:get_chrome_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Google\Chrome\NativeMessagingHosts\firenvim',
                \},
                \'ungoogled-chromium': {
                        \ 'has_config': s:ungoogled_chromium_config_exists(),
                        \ 'manifest_content': function('s:get_chrome_manifest'),
                        \ 'manifest_dir_path': function('s:get_ungoogled_chromium_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Chromium\NativeMessagingHosts\firenvim',
                \},
                \'vivaldi': {
                        \ 'has_config': s:vivaldi_config_exists(),
                        \ 'manifest_content': function('s:get_chrome_manifest'),
                        \ 'manifest_dir_path': function('s:get_vivaldi_manifest_dir_path'),
                        \ 'registry_key': 'HKCU:\Software\Google\Chrome\NativeMessagingHosts\firenvim',
                \}
        \}
        if $TESTING == 1
                call remove(l:browsers, 'librewolf')
                call remove(l:browsers, 'brave')
                call remove(l:browsers, 'chrome-dev')
                call remove(l:browsers, 'opera')
                call remove(l:browsers, 'ungoogled-chromium')
                call remove(l:browsers, 'vivaldi')
        endif
        return l:browsers

endfunction

function! s:get_firenvim_script_path() abort
    return s:build_path([s:get_data_dir_path(), s:get_executable_name()])
endfunction

" At first, is_wsl is set to false, even on WSL. This lets us install firenvim
" on the wsl side, in case people want to use a wsl browser.
" Then, we set is_wsl to true if we're on wsl and launch firenvim#install
" again, installing things on the host side.
let s:is_wsl = v:false

" Installing firenvim requires several steps:
" - Create a batch/shell script that takes care of starting neovim with the
"   right arguments. This is needed because the webextension api doesn't let
"   users specify what arguments programs should be started with
" - Create a manifest file that lets the browser know where the script created
"   can be found
" - On windows, also create a registry key that points to the native manifest
"
" Manifest paths & registry stuff are specified here:
" https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#Manifest_location
"
" firenvim#install accepts the following optional arguments:
" a:1: 0 to let firenvim detect what browsers the user wants to use or 1 to
"      force install for every browser.
" a:2: A prologue that should be inserted in the shell/batch script and
"      executed before neovim is ran.
function! firenvim#install(...) abort
        if !has('nvim-0.6.0')
                echoerr 'Error: nvim version >= 0.6.0 required. Aborting.'
                return
        endif
        try
                lua require("bit")
        catch
                echoerr 'Error: Lua package "bit" unavailable. Install it or switch to LuaJIT.'
                return
        endtry

        let l:force_install = 0
        let l:script_prolog = ''
        if a:0 > 0
                let l:force_install = a:1
                if a:0 > 1
                        let l:script_prolog = a:2
                endif
        endif

        let l:execute_nvim_path = s:get_firenvim_script_path()
        let l:execute_nvim = s:get_executable_content(s:get_runtime_dir_path(), l:script_prolog)

        call s:maybe_execute('mkdir', s:get_data_dir_path(), 'p', 0700)
        if s:is_wsl
                let l:execute_nvim_path = s:to_wsl_path(l:execute_nvim_path)
        endif
        call s:maybe_execute('writefile', split(l:execute_nvim, "\n"), l:execute_nvim_path)
        if s:is_wsl
                let l:execute_nvim_path = s:to_windows_path(l:execute_nvim_path)
        endif
        call s:maybe_execute('setfperm', l:execute_nvim_path, 'rwx------')

        let l:browsers = s:get_browser_configuration()

        let l:powershell_script = ''
        for l:name in keys(l:browsers)
                let l:cur_browser = l:browsers[l:name]
                if !l:cur_browser['has_config'] && !l:force_install
                        echo 'No config detected for ' . l:name . '. Skipping.'
                        continue
                endif

                try
                        let l:manifest_content = l:cur_browser['manifest_content'](l:execute_nvim_path)
                        let l:manifest_dir_path = l:cur_browser['manifest_dir_path']()
                        let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim.json'])
                catch /.*/
                        echo 'Aborting installation for ' . l:name . '. ' . v:exception
                        continue
                endtry

                if has('win32') || s:is_wsl
                        let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim-' . l:name . '.json'])
                endif

                call s:maybe_execute('mkdir', l:manifest_dir_path, 'p', 0700)
                call s:maybe_execute('writefile', [l:manifest_content], l:manifest_path)
                call s:maybe_execute('setfperm', l:manifest_path, 'rw-------')

                echo 'Installed native manifest for ' . l:name . '.'

                if has('win32') || s:is_wsl
                        " On windows, also create a registry key. We do this
                        " by writing a powershell script to a file and
                        " executing it.
                        let l:ps1_content = s:key_to_ps1_str(l:cur_browser['registry_key'],
                                                \ l:manifest_path)
                        let l:ps1_path = s:build_path([l:manifest_dir_path, l:name . '.ps1'])
                        echo 'Creating registry key for ' . l:name . '. This may take a while. Script: ' . l:ps1_path
                        call s:maybe_execute('writefile', split(l:ps1_content, "\n"), l:ps1_path)
                        call s:maybe_execute('setfperm', l:ps1_path, 'rwx------')
                        try
                                let o = s:maybe_execute('system', ['powershell.exe', '-NonInteractive', '-Command', '-'], readfile(l:ps1_path))
                        catch /powershell.exe' is not executable/
                                let l:failure = v:true
                                let l:msg = 'Error: Firenvim could not find powershell.exe'
                                " If the failure happened on wsl, try to use
                                " an absolute path
                                if s:is_wsl
                                        let l:msg += ' from WSL'
                                        try
                                                let o = s:maybe_execute('system', ['/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', '-Command', '-'], readfile(l:ps1_path))
                                                let l:failure = v:false
                                        catch /powershell.exe' is not executable/
                                                let l:failure = v:true
                                        endtry
                                endif
                                let l:msg += ' on your system. Please report this issue.'
                                if l:failure
                                        echomsg 'Note: created ' . l:ps1_path . " . You may try to run it manually by right-clicking from your file browser to complete Firenvim's installation."
                                        throw l:msg
                                endif
                        endtry

                        if v:shell_error
                          echo o
                        endif

                        echo 'Created registry key for ' . l:name . '.'
                endif
        endfor

        if !s:is_wsl
                let s:is_wsl = has('wsl') || !empty($WSL_DISTRO_NAME) || !empty ($WSL_INTEROP)
                if s:is_wsl
                        echo 'Installation complete on the wsl side. Performing install on the windows side.'
                        call firenvim#install(l:force_install, l:script_prolog)
                endif
        endif

        if luaeval('lvim~=nil')
                echo 'WARNING: Lunarvim is not supported. Do not open issues if you use Lunarvim.'
        endif
endfunction

" Removes files created by Firenvim during its installation process

" The uninstallation logic is similar to `firenvim#install`:
" > At first, is_wsl is set to false, even on WSL. This lets us uninstall
" firenvim on the wsl side.
" > Then, we set is_wsl to true if we're on wsl and launch
" firenvim#uninstall again, uninstalling things on the host side.
function! firenvim#uninstall() abort

        let l:data_dir = s:get_data_dir_path()
        call delete(l:data_dir, 'rf')
        echo 'Removed firenvim data directory.'

        let l:browsers = s:get_browser_configuration()

        for l:name in keys(l:browsers)
                let l:cur_browser = l:browsers[l:name]
                if !l:cur_browser['has_config']
                        continue
                endif

                let l:manifest_dir_path = l:cur_browser['manifest_dir_path']()
                let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim.json'])

                if has('win32')
                        let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim-' . l:name . '.json'])
                endif

                if has('win32') || s:is_wsl
                        echo 'Removing registry key for ' . l:name . '. This may take a while.'
                        let l:ps1_content = 'Remove-Item -Path "' . l:cur_browser['registry_key'] . '" -Recurse'
                        let o = system(['powershell.exe', '-NonInteractive', '-Command', '-'], [l:ps1_content])
                        if v:shell_error
                          echo o
                        endif
                        echo 'Removed registry key for ' . l:name . '.'
                endif

                call delete(l:manifest_path)
                echo 'Removed native manifest for ' . l:name . '.'
        endfor

        if !s:is_wsl
                let s:is_wsl = has('wsl') || !empty($WSL_DISTRO_NAME) || !empty ($WSL_INTEROP)
                if s:is_wsl
                        echo 'Uninstallation complete on the wsl side. Performing uninstall on the windows side.'
                        call firenvim#uninstall()
                endif
        endif
endfunction

function! firenvim#onUIEnter(event) abort
        let l:ui = nvim_get_chan_info(a:event.chan)
        if has_key(l:ui, 'client') && has_key(l:ui.client, 'name') &&
                                \ l:ui.client.name =~? 'Firenvim'
                call map(nvim_list_bufs(), {key, val -> firenvimft#detect(val)})
                augroup FirenvimFtdetectAugroup
                        autocmd!
                        autocmd BufRead,BufNewFile *.txt call firenvimft#detect(str2nr(expand('')))
                augroup END
        endif
endfunction

And then run echo "abcde{}" | ~/.local/share/firenvim/firenvim from your shell? This should create a file named /tmp/firenvim.log whose contents I would need you to upload here.

OsKaR31415 commented 1 month ago

Does /opt/homebrew/Cellar/neovim/0.9.5/bin/nvim --version work at all?

It does and returns this :

NVIM v0.9.5
Build type: Release
LuaJIT 2.1.1713773202

         fichier vimrc système : "$VIM/sysinit.vim"
               $VIM par défaut : "/opt/homebrew/Cellar/neovim/0.9.5/share/nvim
"

Run :checkhealth for more info
OsKaR31415 commented 1 month ago

run echo "abcde{}" | ~/.local/share/firenvim/firenvim from your shell? This should create a file named /tmp/firenvim.log whose contents I would need you to upload here.

After executing call firenvim#install() and copying your version of firenvim.vim, running echo "abcde{}" | ~/.local/share/firenvim/firenvim returns nothing, and the /tmp/firenvim.log file isn't created.

glacambre commented 1 month ago

Okay, so for some reason neovim seems to work but there's no input/output from it... What does /opt/homebrew/Cellar/neovim/0.9.5/bin/nvim --headless -c 'call stdioopen({})| call chansend(1, [string(exists("g:firenvim_loaded"))]) | qall!' print?

OsKaR31415 commented 1 month ago

What does /opt/homebrew/Cellar/neovim/0.9.5/bin/nvim --headless -c 'call stdioopen({})| call chansend(1, [string(exists("g:firenvim_loaded"))]) | qall!' print?

It prints 1 without any line break.

glacambre commented 1 month ago

So now we know that the plugin is loaded and that firenvim is able to write to stdout with stdioopen/chansend, that's good... But why isn't it writing to stdout???

At this point I'm running out of ideas. Let's start with a clean set up that doesn't have anything. Please run this script:

rm -rf "${XDG_DATA_HOME:-${HOME}/.local/share}/firenvim/"
DIR=~/.config/nvim
mv "$DIR" "$DIR.bak"
mkdir "$DIR"
git clone "https://github.com/glacambre/firenvim" "$DIR/firenivm"
echo "set rtp+=$DIR/firenvim" > $DIR/init.vim
/opt/homebrew/Cellar/neovim/0.9.5/bin/nvim --headless "+call firenvim#install(0) | q"

And then run echo 'abcde{}' | "${XDG_DATA_HOME:-${HOME}/.local/share}/firenvim/firenvim". Let's hope this prints something because I don't know what else we can try...

OsKaR31415 commented 1 month ago

The script executes fine except that neovim doesn't know the firenvim#install(0) function. I investigated a bit, and it happends that my default nvim is /opt/homebrew/nvim, and not /opt/homebrew/Cellar/neovim/0.9.5/bin/nvim. The second one must be the one used by firenvim, and doesn't have any plugin loaded or anything : when it starts, it's just vanilla neovim. So the problem is that firenvim is not using the right neovim.

glacambre commented 1 month ago

This doesn't make sense to me - regardless of whether you're using /opt/homebrew/nvim or /opt/homebrew/Cellar/neovim/0.9.5/bin/nvim, the same config, and so same set of plugins should be loaded, and so the behavior of nvim -c 'call firenvim#run()' should be the same (firenvim's only version requirement nvim>0.7.0).

What are /opt/homebrew/nvim and /opt/homebrew/Cellar/neovim/0.9.5/bin/nvim? Are they scripts or binaries?

So the problem is that firenvim is not using the right neovim.

Ah, yes, it seems that the existing kludge to deal with homebrew is not sufficient because yours is installed in /opt and not /usr: https://github.com/glacambre/firenvim/blob/cf4ff99033640b5ec33890bcdc892ddc436ed8e5/autoload/firenvim.vim#L583-L591

I'll implement a fix.

glacambre commented 1 month ago

@OsKaR31415 I think I fixed the problem with #1617 - could you update your firenvim neovim plugin, run firenvim#install() again and let me know if everything works as expected?