ycm-core / YouCompleteMe

A code-completion engine for Vim
http://ycm-core.github.io/YouCompleteMe/
GNU General Public License v3.0
25.48k stars 2.81k forks source link

[feature request] show png preview of latex equations #3899

Closed cridemichel closed 3 years ago

cridemichel commented 3 years ago

Issue Prelude

Please complete these steps and check these boxes (by putting an x inside the brackets) before filing your issue:

Thank you for adhering to this process! It ensures your issue is resolved quickly and that neither your nor our time is needlessly wasted.

Issue Details

Dear developers, I am using texlab ( https://texlab.netlify.app ), which is a language server for latex, with YCM and opening the following latex file

\documentclass[aps,pre,superscriptaddress]{revtex4-1}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{color}
\begin{document}
\title{Life, the Universe and Everything }
\author{Douglas Adams}
\begin{abstract}
42 is the answer
\end{abstract}
\date{\today}
\maketitle
\section{Introduction}
bla bla
\begin{eqnarray}
  x&=&y\\
  y&=&2
\end{eqnarray}
\end{document}

with the following minimal vimrc file:

let mapleader = ' '
let maplocalleader=' '
set nocompatible            
call plug#begin('~/.vim/plugged')
Plug 'Valloric/YouCompleteMe' 
call plug#end()            " required
set completeopt=longest,menuone,popup
set completepopup=border:off
if !exists('g:ycm_semantic_triggers')
   let g:ycm_semantic_triggers = {}
endif
let g:ycm_add_preview_to_completeopt=1
let g:ycm_language_server = [
  \   {
  \     'name': 'latex',
  \     'cmdline': [ 'texlab' ],
  \     'filetypes': [ 'tex' ],
  \     'root_files': [ '.criprojroot' ],
  \   },
  \ ]
nmap <leader>yh <plug>(YCMHover)
set noshowmode
let g:ycm_log_level = 'debug'

if I move the cursor within the eqnarray environment (i.e. if I place the cursor between \begin{eqnarray} and \end{eqnarray} latex instructions) and in normal mode I press <space>yh (to trigger YCMHover) I get the following response from texlab language server:

Schermata 2021-05-14 alle 16 01 30

i.e. texlab provides a png preview of the equation. Is it possible to show it as a png image? If you are not willing to implement this could you give me some indication to implement this feature by myself (if you think is feasible)?

best Cristiano

Diagnostic data

Output of vim --version

VIM - Vi IMproved 8.2 (2019 Dec 12, compilato May 02 2021 18:16:24)
Versione macOS - x86_64
Patch incluse: 1-2825
Compilato da Homebrew
Versione gigante senza GUI.  Funzionalità incluse (+) o escluse (-):
+acl               -farsi             +mouse_sgr         +tag_binary
+arabic            +file_in_path      -mouse_sysmouse    -tag_old_static
+autocmd           +find_in_path      +mouse_urxvt       -tag_any_white
+autochdir         +float             +mouse_xterm       -tcl
-autoservername    +folding           +multi_byte        +termguicolors
-balloon_eval      -footer            +multi_lang        +terminal
+balloon_eval_term +fork()            -mzscheme          +terminfo
-browse            +gettext           +netbeans_intg     +termresponse
++builtin_terms    -hangul_input      +num64             +textobjects
+byte_offset       +iconv             +packages          +textprop
+channel           +insert_expand     +path_extra        +timers
+cindent           +ipv6              +perl              +title
-clientserver      +job               +persistent_undo   -toolbar
+clipboard         +jumplist          +popupwin          +user_commands
+cmdline_compl     +keymap            +postscript        +vartabs
+cmdline_hist      +lambda            +printer           +vertsplit
+cmdline_info      +langmap           +profile           +virtualedit
+comments          +libcall           -python            +visual
+conceal           +linebreak         +python3           +visualextra
+cryptv            +lispindent        +quickfix          +viminfo
+cscope            +listcmds          +reltime           +vreplace
+cursorbind        +localmap          +rightleft         +wildignore
+cursorshape       +lua               +ruby              +wildmenu
+dialog_con        +menu              +scrollbind        +windows
+diff              +mksession         +signs             +writebackup
+digraphs          +modify_fname      +smartindent       -X11
-dnd               +mouse             -sound             -xfontset
-ebcdic            -mouseshape        +spell             -xim
+emacs_tags        +mouse_dec         +startuptime       -xpm
+eval              -mouse_gpm         +statusline        -xsmp
+ex_extra          -mouse_jsbterm     -sun_workshop      -xterm_clipboard
+extra_search      +mouse_netterm     +syntax            -xterm_save
   file vimrc di sistema: "$VIM/vimrc"
       file vimrc utente: "$HOME/.vimrc"
    II file vimrc utente: "~/.vim/vimrc"
        file exrc utente: "$HOME/.exrc"
        file dei default: "$VIMRUNTIME/defaults.vim"
         $VIM di riserva: "/usr/local/share/vim"
Compilazione: clang -c -I. -Iproto -DHAVE_CONFIG_H -DMACOS_X -DMACOS_X_DARWIN -g -O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
Link: clang -L. -fstack-protector-strong -L/usr/local/lib -L/usr/local/opt/libyaml/lib -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/readline/lib -L/usr/local/lib -o vim -lm -lncurses -liconv -lintl -framework AppKit -L/usr/local/opt/lua/lib -llua5.4 -mmacosx-version-min=11.2 -fstack-protector-strong -L/usr/local/lib -L/usr/local/Cellar/perl/5.32.1_1/lib/perl5/5.32.1/darwin-thread-multi-2level/CORE -lperl -L/usr/local/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/lib/python3.9/config-3.9-darwin -lpython3.9 -framework CoreFoundation -lruby.3.0 -L/usr/local/Cellar/ruby/3.0.1/lib

Output of YcmDebugInfo

Printing YouCompleteMe debug information...
-- Resolve completions: On demand
-- Client logfile: /var/folders/gc/2x7dz9ps7m38rc7ts03c30ch0000gn/T/ycm_v053otmq.log
-- Server Python interpreter: /usr/local/opt/python@3.9/bin/python3.9
-- Server Python version: 3.9.5
-- Server has Clang support compiled in: False
-- Clang version: None
-- No extra configuration file found
-- GenericLSP completer debug information:
--   latexCompleter running
--   latexCompleter process ID: 89666
--   latexCompleter executable: ['/usr/local/bin/texlab']
--   latexCompleter logfiles:
--     /var/folders/gc/2x7dz9ps7m38rc7ts03c30ch0000gn/T/latexcompleter_stderr17hqd1rv.log
--   latexCompleter Server State: Initialized
--   latexCompleter Project Directory: /Users/demichel/PAPERS/ISOBARICMD/LATEX
--   latexCompleter Settings: {}
-- Server running at: http://127.0.0.1:64159
-- Server process ID: 89665
-- Server logfiles:
--   /var/folders/gc/2x7dz9ps7m38rc7ts03c30ch0000gn/T/ycmd_64159_stdout_eajav69t.log
--   /var/folders/gc/2x7dz9ps7m38rc7ts03c30ch0000gn/T/ycmd_64159_stderr_vqmzpuy6.log

Output of YcmDiags

No warnings or errors detected.

Output of git rev-parse HEAD in YouCompleteMe installation directory

7c4d05375a09a871f618f9688c7af517d4e69b76

Contents of YCM, ycmd and completion engine logfiles

puremourning commented 3 years ago

Is it possible to show it as a png image?

No, vim is a text-based interface (TUI if you like) so there's no way to render PNGs.

If you are not willing to implement this could you give me some indication to implement this feature by myself (if you think is feasible)?

I don't think it's feasible. It might be possible to parse the markdown and see if it's just a PNG then call out to some third-party app which will display the png (like a web browser) ... but that's quite a lot of work.

cridemichel commented 3 years ago

first of all thank you very much for the reply, I know that vim is a text-based interface but in python it is easy to show a png and it shouldn't be such a huge work

puremourning commented 3 years ago

in python it is easy to show a png and it shouldn't be such a huge work

Then have at it! I mean, I have no idea how this is expected to work if there is no UI (e.g. via SSH) or whatever, but please don't let us stop you playing !

cridemichel commented 3 years ago

via ssh you can use graphic through X11 (you surely used the flag -Y of ssh), but my question is: where in YCM sources (python ones) the response of texlab is handled?

puremourning commented 3 years ago

https://github.com/ycm-core/YouCompleteMe/blob/master/python/ycm/client/command_request.py#L67 here.

Developer discussion happens on Gitter btw, it's much easier that way.

bstaletic commented 3 years ago

Python also can't draw PNGs in the console and I'd find opening a separate window very distracting.

This is where YCM transforms the data it gets from ycmd to vim's completion menu entries: https://github.com/ycm-core/YouCompleteMe/blob/master/python/ycm/client/completion_request.py#L186 You'd have to parse extra_menu_info.

cridemichel commented 3 years ago

Dear bstaletic, thank you for the info, hence it is expected that if I issue an YCMHover, ycm processes the response of texlab through https://github.com/ycm-core/YouCompleteMe/blob/master/python/ycm/client/completion_request.py#L186, however I tried to add (adding also the related import at the begin of the file) inside the function ConvertCompletionDataToVimData the following python instruction:

os.system('touch /Users/demichel/ycm.log')

but I did not obtain the file ycm.log in my home directory after issuing the command YCMHover, why?

bstaletic commented 3 years ago

The file you changed is called completion request. Hover is a subcommand in which, appropriately, has is named command_request.py.

cridemichel commented 3 years ago

thank you again for the clarification, the string containing the png image is handled by the function StringResponse in command_request.py. In particular it is retrieved by ycm inself._response['detailed_info'], which contains exactly the png string shown in my first post (that in the preview popup). Is it reasonable to handle the png within the StringResponse function?

cridemichel commented 3 years ago

...by adding the following lines in the function StringResponse

stringa=self._response['detailed_info'].strip('\n').split(',')[1]
imgdata = base64.b64decode(stringa)
filename = '/Users/demichel/ycm.png'  
       f.write(imgdata)
os.system('open '+filename)

just after

 # Completer threw an error ?
    if self._response is None:
      return ""

I obtained a preview image of the latex equation for which I issued the YCMHover command! Hence I confirm that it is rather easy to preview the equation obtained from texlab. What do you think about this? I think it could be a cool add-on for YCM, isn't it?

bstaletic commented 3 years ago

An external tool works for some users, not for everyone. open is also a macOS thing. On linux you'd have to hope that the user has installed and configured xdg-open. I'm the first who doesn't have xdg-open even installed. That means you'd have to start guessing the image viewer. Can you guess that I have feh installed? And who knows how to open things on Windows. That means, in order to open PNGs, YCM would have to pull in a new dependency - an image rendering library.

Besides that, think about tiling window managers. Two things can happen:

  1. If user is in full screen mode, your window pops up across the entire screen, covering the entire source code the user was reading.
  2. If user is in tiling mode, the new window is a new tile, disrupting the layout that the user has carefully set up for the task at hand.

That means that spawning windows at random can be very disruptive.

Also, you're not deleting old PNGs. You could do things with tempfile python module, but, with all of the above, you can see how this isn't really a simple change that can work for everyone and not be disruptive.

cridemichel commented 3 years ago

mine was just a proof-of-concept to exploit texlab response, of course "open" is a mac osx thing and I do not intend to use "xdg-open" or the like for linux. The png image can be visualized just relying on python, which would be a portable solution. But if you think my effort will lead to nowhere, I can give up not wasting more time anymore, best C.

puremourning commented 3 years ago

It’s extremely unlikely that we would merge this feature, sorry. But that shouldn’t mean you can’t keep your changes locally and keep hackin!

cridemichel commented 3 years ago

ok, no problem I understand your concerns best Cristiano

cridemichel commented 3 years ago

Dear, just for you information to have a preview of latex equation I added this to my .vimrc

function! MyShowHoverResult( response )
    let lines = split( a:response, "\n" )
    let len = max( map( copy( lines ), "len( v:val )" ) )

    let wrap = 0
    let col = 'cursor'

    " max width is screen columns minus x padding (2)
    if len >= (&columns - 2)
      " There's at least one line > our max - enable word wrap and draw the
      " popup at the leftmost column
      let col = 1
      let wrap = 1
    endif

    "echo "syn=" . b:ycm_hover.syntax
    let s:ycm_hover_popup = popup_atcursor(
          \   lines,
          \   {
          \     'col': col,
          \     'wrap': wrap,
          \     'padding': [ 0, 1, 0, 1 ],
          \     'moved': 'word',
          \     'maxwidth': &columns,
          \     'close': 'click',
          \     'fixed': 0,
          \   }
          \ )
    call setbufvar( winbufnr( s:ycm_hover_popup ),
                            \ '&syntax',
                            \ b:ycm_hover.syntax )
endfunction

function! MyShowDataPopup(response) abort
  "let response = youcompleteme#GetCommandResponse( 'GetHover' )
  if a:response == ''
    return
  endif
  if s:ycm_hover_popup!=-1
    call popup_close( s:ycm_hover_popup )
  endif
  let s:texlr = split(a:response,",")
  if len(s:texlr) > 1 && s:texlr[0] == "![preview](data:image/png;base64"
     let s:hdr = s:texlr[0]
     let s:body= s:texlr[1]

     let s:ltw = [ s:body ]
     let s:fn="/tmp/ycm_texlab_preview_png_base64.dat"
     let s:pyexe='/Users/demichel/bin/show_b64_image.py'
     if !executable(s:pyexe)
       echoerr s:pyexe . " executable is not in path"
     else
       call writefile(s:ltw, s:fn, "a") 
       silent! execute "!python3 ". s:pyexe . " " . s:fn
       silent! execute "!rm -f ".s:fn
       "let s:ycm_hover_popup=-1 
       redraw!
     endif
   else
     call MyShowHoverResult(a:response)
     " following one is a simpler solution than MyShowDataPopup() function which has been grabbed fron YCM python sources
     "let s:ycm_hover_popup = popup_atcursor( balloon_split( a:response ), {} )
  endif
endfunction

function! YCMGetDetInfo() abort
   if !has_key( b:, 'ycm_hover' )
      " choose one of tha available commands
      let cmds = youcompleteme#GetDefinedSubcommands()
      if index( cmds, 'GetHover' ) >= 0
        let b:ycm_hover = {
              \ 'command': 'GetHover',
              \ 'syntax': 'tex',
              \ }
      elseif index( cmds, 'GetDoc' ) >= 0
        let b:ycm_hover = {
              \ 'command': 'GetDoc',
              \ 'syntax': 'tex',
              \ }
      elseif index( cmds, 'GetType' ) >= 0
        let b:ycm_hover = {
              \ 'command': 'GetType',
              \ 'syntax': 'tex',
              \ }
      else
        let b:ycm_hover = {}
      endif
    endif
  call youcompleteme#GetCommandResponseAsync(function( 'MyShowDataPopup' ), b:ycm_hover.command )
endfunction

autocmd! FileType tex nnoremap <buffer> <leader>yh :call YCMGetDetInfo()<CR>

where show_b64_image.py is the following python script

import base64,sys
from PIL import Image
a=sys.argv
s=''
fn=a[1]
#fn='/Users/demichel/ycm.log'
with open(fn) as f:
    lines=f.readlines()
for l in lines:
    s += l.strip('\n')
imgdata = base64.b64decode(s)
filename = fn.split('.')[0]+'.png'  # I assume you have a way of picking unique filenames
with open(filename,'wb') as fb:
    fb.write(imgdata)
image = Image.open(filename)
image.show()

I am posting this since maybe there are other ycm users interested in this feature,

best Cristiano