Skip to content

Neovim plugin for writing LilyPond scores, with asynchronous make, midi/mp3 player, fast syntax highlighting, "hyphenation" function, and support for LaTex and Texinfo files

License

Notifications You must be signed in to change notification settings

martineausimon/nvim-lilypond-suite

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nvim-lilypond-suite

This is a plugin (Neovim only) for writing LilyPond scores, with asynchronous make, midi/MP3 player, "hyphenation" function for lyrics, fast syntax highlighting... This repository also contains ftplugin files for LaTeX and Texinfo files which allows embedded LilyPond syntax highlighting, and makeprg which support lilypond-book or lyluatex package out of the box.

Installation • Configuration • Usage • Tips & tricks


Important

I just uploaded the most important update since the creation of this plugin (commit 65dd9c0. I hope it won't cause any issues for you!

MAIN CHANGES MARCH 2025 (click to expand)
  • Compilation is now performed with vim.uv, which has many advantages, particularly regarding error management. For tasks that require multiple compilations, a job queue is created, and if a job fails, the queue is canceled, providing more information about what went wrong.

  • I've maximized the use of native nvim functions for file and path management to avoid issues with weird characters in file names.

  • I’ve significantly improved error handling with quickfix and diagnostics. Each error message is sorted according to a rule like this (some rules certainly needs improvements !):

{
  pattern = "([^:]+):(%d+):(%d+): (%w+): (.+): (.*)",
  rule = function(file, lnum, col, loglevel, msg, pattern)
    return {
      filename = file,
      lnum = tonumber(lnum),
      col = tonumber(col),
      type = Utils.qf_type(loglevel),
      text = string.format("%s: %s", msg, pattern),
      pattern = Utils.format_pattern(pattern),
      end_col = tonumber(col) + #pattern - 1
    }
  end
},
  • I write a new debug function :LilyDebug which displays information:
    • :LilyDebug commands: shows the latest commands executed by the plugin
    • :LilyDebug errors: displays the errors sorted by the plugin, as they are sent to be processed by the "rules". Useful for creating/improving the rules. In multi-line errors, line breaks are represented by "|"
    • :LilyDebug stdout: shows the raw output of the last used commands
    • :LilyDebug fileinfos: shows the main file, main folder, output file, tmp folder used for compilation, and audio file if lilypond

Please report any issues!

FEATURES

  • Fast syntax file for LilyPond
  • Asynchronous :make - compile in background without freezing Neovim
  • Audio player in floating window (LilyPond only) - convert (mp3 or wav) and play midi file while writing score (using mpv, timidity or fluidsynth & ffmpeg)
  • QuickPlayer (LilyPond only) - convert and play only visual selection
  • Hyphenation : automatically place hyphens ' -- ' inside texts to make those texts usable as lyrics (LilyPond only)
  • Simple ftplugin for LilyPond with makeprg
  • Multiple files support - Compile only main file when working on multiple files project
  • ftplugin for LaTex and Texinfo files which detects and allows embedded LilyPond syntax, adaptive compilation function for lyluatex or lilypond-book
  • Easy auto-completion and Point & Click configuration
  • Error managment with correct quickfix and diagnostics

QUICK INSTALL

âš  This plugin requires Nvim >= 0.7

If you want to use all the functions (player, hyphenation for various languages...), please read the installation section in the wiki to install dependencies

{ 
  'martineausimon/nvim-lilypond-suite',
  opts = {
    -- edit config here (see "Customize default settings" in wiki)
  }
}

Non lazy.nvim users : this plugin needs require('nvls').setup()

With config (click to expand)
{
  'martineausimon/nvim-lilypond-suite',
  opts = {
    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"
        }
      },
    },
  }
}
Minimal config with Lazy and Blink (click to expand)
-- 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 = "*"
})

WIKI INDEX

About

Neovim plugin for writing LilyPond scores, with asynchronous make, midi/mp3 player, fast syntax highlighting, "hyphenation" function, and support for LaTex and Texinfo files

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 6