2. Configuration - martineausimon/nvim-lilypond-suite GitHub Wiki

⚠ All the following settings are in lua (with e.g. init.lua) If you use a vimscript config with init.vim, you can insert the lua lines surrounded by lua << EOF and EOF

CUSTOMIZE DEFAULT SETTINGS

Here is the default configuration that you can copy-paste and modify in your init.lua :

require('nvls').setup({
  lilypond = {
    mappings = {
      player = "<F3>",
      compile = "<F5>",
      open_pdf = "<F6>",
      switch_buffers = "<A-Space>",
      insert_version = "<F4>",
      hyphenation = "<F12>",
      hyphenation_change_lang = "<F11>",
      insert_hyphen = "<leader>ih",
      add_hyphen = "<leader>ah",
      del_next_hyphen = "<leader>dh",
      del_prev_hyphen = "<leader>dH",
    },
    options = {
      pitches_language = "default",
      hyphenation_language = "en_DEFAULT",
      output = "pdf",
      backend = nil,
      main_file = "main.ly",
      main_folder = "%:p:h",
      include_dir = nil,
      pdf_viewer = nil,
      errors = {
        diagnostics = true,
        quickfix = "external",
        filtered_lines = {
          "compilation successfully completed",
          "search path"
        }
      },
    },
  },
  latex = {
    mappings = {
      compile = "<F5>",
      open_pdf = "<F6>",
      lilypond_syntax = "<F3>"
    },
    options = {
      lilypond_book_flags = nil,
      clean_logs = false,
      main_file = "main.tex",
      main_folder = "%:p:h",
      include_dir = nil,
      lilypond_syntax_au = "BufEnter",
      pdf_viewer = nil,
      errors = {
        diagnostics = true,
        quickfix = "external",
        filtered_lines = {
          "Missing character",
          "LaTeX manual or LaTeX Companion",
          "for immediate help.",
          "Overfull \\hbox",
          "^%s%.%.%.",
          "%s+%(.*%)"
        }
      },
    },
  },
  texinfo = {
    mappings = {
      compile = "<F5>",
      open_pdf = "<F6>",
      lilypond_syntax = "<F3>"
    },
    options = {
      lilypond_book_flags = "--pdf",
      clean_logs = false,
      main_file = "main.texi",
      main_folder = "%:p:h",
      lilypond_syntax_au = "BufEnter",
      pdf_viewer = nil,
      errors = {
        diagnostics = true,
        quickfix = "external",
        filtered_lines = {
          "Missing character",
          "LaTeX manual or LaTeX Companion",
          "for immediate help.",
          "Overfull \\hbox",
          "^%s%.%.%.",
          "%s+%(.*%)"
        }
      },
    },
  },
  player = {
    mappings = {
      quit = "q",
      play_pause = "p",
      loop = "<A-l>",
      backward = "h",
      small_backward = "<S-h>",
      forward = "l",
      small_forward = "<S-l>",
      decrease_speed = "j",
      increase_speed = "k",
      halve_speed = "<S-j>",
      double_speed = "<S-k>"
    },
    options = {
      row = 1,
      col = "99%",
      width = "37",
      height = "1",
      border_style = "single",
      winhighlight = "Normal:Normal,FloatBorder:Normal,FloatTitle:Normal",
      midi_synth = "fluidsynth",
      fluidsynth_flags = nil,
      timidity_flags = nil,
      ffmpeg_flags = nil,
      audio_format = "mp3",
      mpv_flags = {
        "--msg-level=cplayer=no,ffmpeg=no,alsa=no",
        "--loop",
        "--config-dir=/dev/null",
        "--no-video"
      }
    },
  },
})

It is possible to modify only part of the parameters, as follows :

require('nvls').setup({
  latex = {
    mappings = {
      compile = "<leader>c"
    },
    options = {
      clean_logs = true
    }
  }

PER-FOLDER CONFIGURATION

This plugin supports per-folder configuration (see the Multiple files projects section in the wiki).


INCLUDE DIRECTORIES

You can append directory to the search path for input files with relative paths in include_dir option. This can be a string or a table in case of multiple include directories :

require('nvls').setup({
  lilypond = {
    options = {
      include_dir = {
        "$HOME/Documents/",
        "$HOME/Lilypond/"
      }
    }
  }
})

HIGHLIGHTINGS

Recommended syntax sync settings

Add this lines to your init.lua :

vim.api.nvim_create_autocmd('BufEnter', { 
  command = "syntax sync fromstart",
  pattern = { '*.ly', '*.ily', '*.tex' }
})

Highlight groups

Here are the defaults used for syntax highlighting :

lilyString = { link = "String" },
lilyDynamic = { bold = true },
lilyComment = { link = "Comment" },
lilyNumber = { link = "Number" },
lilyVar = { link = "Tag" },
lilyBoolean = { link = "Boolean" },
lilySpecial = { bold = true },
lilyArgument = { link = "Type" },
lilyScheme = { link = "Special" },
lilyLyrics = { link = "Special" },
lilyMarkup = { bold = true },
lilyFunction = { link = "Statement" },
lilyArticulation = { link = "PreProc" },
lilyContext = { link = "Type" },
lilyGrob = { link = "Include" },
lilyTranslator = { link = "Type" },
lilyPitch = { link = "Function" },
lilyChord = { 
  ctermfg = "lightMagenta", 
  fg = "lightMagenta", 
  bold = true 
},

You can modify this defaults in require('nvls').setup() function, e.g :

require('nvls').setup({
  lilypond = {
    highlights {
      lilyVar = { ctermfg = "blue" }
    }
  }
)

Highlight pitches for others languages

If you use others languages for pitch names, you can configure nvim-lilypond-suite to highlight the right words with pitches_language option in require('nvls').setup()

For now, only english, français, deutsch, nohl (no highlight) and default highlights are availables.

TODO : create pitches pattern for other languages

Lighter syntax highlighting

Since 7df532e commit, I changed my method for syntax highlighting and avoided word lists as much as possible, for more lightness. You can also define pitches_language = "nohl" in require('nvls').setup() to avoid pitches highlighting.


ERROR MESSAGES

Error handling works as follows:

  1. Each command (lilypond, lualatex, texi2pdf) returns an output message, which can be viewed with :LilyDebug stdout.

  2. This stdout is analyzed, and each line matching an error message is retained. Multiline messages are grouped into a single string with the | symbol used to mark line breaks. These lines can be accessed with :LilyDebug errors.

  3. The default configuration distributes these lines as follows:

  • Diagnostics in each buffer containing errors (diagnostics = true).
    To make diagnostics appear, add the following lines to your configuration:
vim.diagnostic.config({
  virtual_text = true,
  signs = true,
  update_in_insert = false,
  underline = true,
})
  • Quickfix: Errors from buffers other than the current one are sent to the quickfix list (quickfix = "external", can be "full" or "none"). You can open it manually with :copen, or use an autocommand. The following autocmd will open quickfix when recognized errors are detected. If the window is already open and no errors are found, the window will close:
vim.api.nvim_create_autocmd( 'QuickFixCmdPost', { 
  command = "cwindow",
  pattern = "*"
})

Lines contained in the filtered_lines table are excluded from error messages."

See also : Display error messages in a floating window


MIDI

see issue #29
(TODO: write this section !)


AUTO-COMPLETION

Recommended settings

Install coc.nvim and coc-dictionary & coc-tabnine : works out of the box !

Dictionary files

If you want to use another completion plugin, nvim-lilypond-suite uses the following dictionary files :

$LILYDICTPATH/grobs
$LILYDICTPATH/keywords
$LILYDICTPATH/musicFunctions
$LILYDICTPATH/articulations
$LILYDICTPATH/grobProperties
$LILYDICTPATH/paperVariables
$LILYDICTPATH/headerVariables
$LILYDICTPATH/contextProperties
$LILYDICTPATH/clefs
$LILYDICTPATH/repeatTypes
$LILYDICTPATH/languageNames
$LILYDICTPATH/accidentalsStyles
$LILYDICTPATH/scales
$LILYDICTPATH/musicCommands
$LILYDICTPATH/markupCommands
$LILYDICTPATH/contextsCmd
$LILYDICTPATH/dynamics
$LILYDICTPATH/contexts
$LILYDICTPATH/translators

Configuration example

-- Install lazy.nvim

local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  local lazyrepo = "https://github.com/folke/lazy.nvim.git"
  local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
  if vim.v.shell_error ~= 0 then
    vim.api.nvim_echo({
      { "Failed to clone lazy.nvim:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\nPress any key to exit..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end

vim.opt.rtp:prepend(lazypath)

-- Plugins config

require("lazy").setup({
  {
    "martineausimon/nvim-lilypond-suite",
    opts = {}
  },
  { 'nvim-lua/plenary.nvim', lazy = true }, -- Required for blink.cmp
  {
    'saghen/blink.cmp',
    dependencies = {
      'Kaiser-Yang/blink-cmp-dictionary', -- Required for dictionary completion
    },
    version = '*',
    opts = {
      sources = {
        default = { 'dictionary', 'lsp', 'path', 'snippets', 'buffer' }, -- Add 'dictionary'
        providers = {
          dictionary = {
            module = 'blink-cmp-dictionary',
            name = 'Dict',
            min_keyword_length = 3,
            max_items = 8,
            opts = {
              dictionary_files = function()
                if vim.bo.filetype == 'lilypond' then -- Add lilypond words to sources
                  return vim.fn.glob(vim.fn.expand('$LILYDICTPATH') .. '/*', true, true)
                end
              end,
            }
          },
        },
      },
    },
    opts_extend = { "sources.default" }
  },
},{})

vim.diagnostic.config({ -- Show diagnostics if errors
  virtual_text = true,
  signs = true,
  update_in_insert = false,
  underline = true,
})

vim.api.nvim_create_autocmd('BufEnter', { -- Better highlights
  command = "syntax sync fromstart",
  pattern = { '*.ly', '*.ily', '*.tex', '*.texi', '*.texinfo' }
})

vim.api.nvim_create_autocmd( 'QuickFixCmdPost', { -- Show quickfix if errors, else close window
  command = "cwindow",
  pattern = "*"
})

POINT AND CLICK

Neovim Remote

To use the "point and click", Nvim must be launched with remote function.

Vanilla Neovim

Since a few months, there's a built-in option :

Open your file with a server listening :

nvim --listen ~/.cache/nvim/server.pipe file.ly

Configure your point & click with something like the following (read the next sections of the wiki to adjust it) :

nvim --server ~/.cache/nvim/server.pipe --remote-send "<cmd>call cursor($line,$col)<cr>"

That's basically the principle. But Nvim does not have the --servername functionality, so there can only be one file per Nvim instance, and you must manually specify a server for all files, which greatly limits the use of this function for point and click.

Neovim-remote

I really recommend using the neovim-remote package, which for me solves all remote issues. You can open your .ly files as usual, and neovim-remote works perfectly, even with multiple .ly files open in the same session.

The rest of this section will be exclusively a config with neovim-remote, but you can adapt the commands to use the built-in function if you prefer.

Configure the Point and Click

Easy (and quite robust) way

xdg-open is (for me) quite bloated and complicated to use for a simple resource opener.
The following configuration will bypass this software. It's much simpler and more effective for me, but if you opt for a more classic configuration, go directly to the next section.

  • Option 1 : Add this lines to /usr/bin/xdg-open (e.g. line 36) :
if [[ "$@" == textedit://*ly:* ]]; then
  tmp=${@#*://} tmp="${tmp%%:*}" attr=${@#*ly:}
  file="${tmp//%20/\\ }"
  line=${attr%:*:*}
  col=${attr##*:}
  nvr -s +:"dr $file | call cursor($line, $col)"
  exit
fi

And that is all !

  • Option 2 : if you don't want to modify xdg-open :

Create a file called xdg-open in $HOME/.local/bin/ containing this code :

if [[ "$@" == textedit://*ly:* ]]; then
  tmp=${@#*://} tmp="${tmp%%:*}" attr=${@#*ly:}
  file="${tmp//%20/\\ }"
  line=${attr%:*:*}
  col=${attr##*:}
  nvr -s +:"dr $file | call cursor($line, $col)"
  exit
fi
/usr/bin/xdg-open "$@"

Make this file executable :

chmod +x $HOME/.local/bin/xdg-open

Add the folder to your path (in .bashrc) :

export PATH="$HOME/.local/bin:$PATH"

Classic way

⚠ This config don't work with files names (and dir names) containing spaces.

  • Install neovim-remote

  • Add this line to ~/.profile (or ~/.bashrc) :

export LYEDITOR="nvr -s +:'dr %(file)s | call cursor(%(line)s,%(char)s+1)'"
  • Follow the instructions on the LilyPond website to configure the system and create lilypond-invoke-editor.desktop

  • Reboot or reload session

Pdf reader

This plugin uses xdg-open command by default to view pdf (or open for mac os). You can specify a different pdf app with the pdf_viewer option :

require('nvls').setup({
  lilypond = {
    options = {
      pdf_viewer = "zathura",
    },
  },
  latex = {
    options = {
      pdf_viewer = "zathura",
    },
  },
})

Zathura

Recommended pdf viewer : zathura with zathura-pdf-mupdf plugin

Okular

Alternate custom text editor command, for Okular :

nvr +:'dr %f | call cursor(%l,%c+1)'
⚠️ **GitHub.com Fallback** ⚠️