Cargo Limit Save

Productivity improvements for Rust ecosystem: warnings are skipped until errors are fixed, LSP-independent Neovim integration, etc.

Project README
I've gained wisdom from commercial proprietary challenges since 2012
and switched entirely to my personal projects.
Free and Open Source Software is my passion.
I believe it's the shortest way to enlightenment healthier products for society ♡

cargo-limit Crates.io Crates.io Awesome

Liberapay Ko-fi Patreon Open Collective GitHub Sponsors

Description

🚀 Cargo with less noise:

  • errors have highest priority
    • they never appear in the middle of warnings
    • warnings are skipped by default until errors are fixed
    • external path dependencies' warnings are skipped by default
  • all messages come in reversed order by default
    • to avoid extra scrolling
  • messages are grouped by filenames
  • number of messages can be limited
  • after encountering first error the rest of build time is limited by default
  • files can be automatically opened in your text editor on affected lines

Initially this project was a workaround for this issue, which was closed with no adequate solution.

Check out roadmap, issues and 🎙️ Rustacean Station podcast episode for more.

asciicast

Installation

Packaging status

From crates.io

cargo install --locked cargo-limit

From git

cargo install --locked --force --git https://github.com/cargo-limit/cargo-limit

Usage

Run any of these in your project directory:

cargo lbench
cargo lbuild
cargo lcheck
cargo lclippy
cargo ldoc
cargo lfix
cargo lrun
cargo lrustc
cargo lrustdoc
cargo ltest

Also llcheck, llrun, etc. to auto-open text editor for warnings as well, not just for errors.

This tool is especially useful in combination with cargo-watch.

💡 Environment Variables 👁️

CARGO_MSG_LIMIT

  • limit compiler messages number
  • 0 means no limit, which is default

CARGO_TIME_LIMIT

  • cargo execution time limit in seconds after encountering first compiling error
  • 1 is default
  • 0 means no limit

CARGO_ASC

  • show compiler messages in ascending order
  • false is default

CARGO_FORCE_WARN

  • show warnings even if errors still exist
  • false is default

CARGO_DEPS_WARN

  • show external path dependencies' warnings
  • false is default

CARGO_EDITOR

  • opens affected files in external app
  • empty ("") means don't run external app
  • "_cargo-limit-open-in-nvim" is default

Text Editor/IDE integrations

💡 Neovim Plugin 👁️

Requires nvim >= 0.7.0 and git to be installed.

This plugin is LSP-independent, it will keep working even when rust-analyzer fails to produce diagnostics!

Using vim-plug:

Plug 'alopatindev/cargo-limit', { 'do': 'cargo install --locked cargo-limit nvim-send' }

and run

nvim +PlugInstall +UpdateRemotePlugins +qa
💡 Using other Neovim Plugin Managers 👁️

lazy.nvim

{ 'alopatindev/cargo-limit', build = 'cargo install --locked cargo-limit nvim-send' },

and run

nvim --headless "+Lazy! sync" +qa

paq-nvim:

{ 'alopatindev/cargo-limit', build = ':!cargo install --locked cargo-limit nvim-send' },

and run

nvim +PaqSync +qa

packer.nvim:

{ use 'alopatindev/cargo-limit', run = ':!cargo install --locked cargo-limit nvim-send' }

and run

nvim +PackerUpdate +qa

dein:

call dein#add('alopatindev/cargo-limit', { 'rev': 'master', 'hook_post_update': '!cargo install --locked cargo-limit nvim-send' })

and run

nvim --cmd '!call dein#install()'

Optionally: F2 to save, F2 again to jump to next affected line

fun! SaveAllFilesOrOpenNextLocation()
  let l:all_files_are_saved = v:true
  for i in getbufinfo({'bufmodified': 1})
    if i.name !=# ''
      let l:all_files_are_saved = v:false
      break
    endif
  endfor

  if l:all_files_are_saved
    if exists('*CargoLimitOpenNextLocation')
      call g:CargoLimitOpenNextLocation()
    endif
  else
    execute 'wa!'
  endif
endf

nmap <F2> :call SaveAllFilesOrOpenNextLocation()<cr>
vmap <F2> <esc>:call SaveAllFilesOrOpenNextLocation()<cr>v
imap <F2> <esc>:call SaveAllFilesOrOpenNextLocation()<cr>i

Optionally: less verbose messages

let g:CargoLimitVerbosity = 2 " warnings level
💡 Test the plugin! 👁️

  1. Open two terminals (or tmux windows, etc.)
  2. cd your/project/directory in both of them
  3. Run nvim in one of them
  4. Run cargo lrun in the other
  5. In case of compiling errors nvim opens new or existing tabs with the files on affected lines and columns
  6. Fix the error, save the file and nvim will jump to the next error location
  7. cargo llrun (cargo llcheck, etc.) will open them in case of warnings as well.
⚠️ Known Limitations! 👁️

1. Auto-jumps work only if

  • current mode is normal
  • current buffer is either empty or contains some existing and unmodified (saved) file

This is by design, in order to not disrupt from active text editing or file navigation process.

2. Auto-jump on each file save is currently imprecise

  • it may jump to a wrong line if it moved
  • it may not jump at all, if the next affected line is supposed to be modified already

For precise jump please rerun cargo ll{check,run,etc.}.

3. Before running nvim: Current Directory should be Project (sub)directory

  • that's required so cargo-limit could figure out which exact nvim instance should be controlled
  • only first nvim instance with current project (sub)directory will be controlled by cargo-limit.

Customizations

Add a custom open handler to your init.vim if you want other Neovim behavior.

💡 See examples! 👁️

Open Files in Buffers Instead of Tabs

function! g:CargoLimitOpen(editor_data)
  let l:current_file = resolve(expand('%:p'))
  if l:current_file != '' && !filereadable(l:current_file)
    return
  endif
  for location in reverse(a:editor_data.files)
    let l:path = fnameescape(location.path)
    if mode() == 'n' && &l:modified == 0
      execute 'edit ' . l:path
      call cursor((location.line), (location.column))
    else
      break
    endif
  endfor
endf

Populate a QuickFix List

set errorformat =%f:%l:%c:%m

function! g:CargoLimitOpen(editor_data)
  let l:winnr = winnr()

  cgetexpr []
  for file in a:editor_data['files']
    caddexpr file['path'] . ':' . file['line'] . ':' . file['column'] . ':' . file['message']
  endfor

  if empty(a:editor_data['files'])
    cclose
  else
    copen
  endif

  if l:winnr !=# winnr()
    wincmd p
  endif
endf
💡 Other Text Editors/IDEs 👁️

cargo-limit can run external app/script and provide affected locations to stdin in the following JSON format:

{
  "protocol_version": "0.0.11",
  "workspace_root": "/full/path/to/project",
  "files": [
    {
      "path": "/full/path/to/project/file.rs",
      "line": 4,
      "column": 1,
      "message": "unused import: `diagnostic::DiagnosticSpan`",
      "level": "warning"
    }
  ]
}

Theoretically this can be used for any text editor or IDE, especially if it supports client/server communication. To do that you need a wrapper app/script that parses the files and gives them to the text editor or IDE client.

💡 Example: Gedit! 👁️

  1. Install jq
  2. Create open-in-gedit.sh:
#!/bin/bash

jq --raw-output '.files |= unique_by(.path) | .files[] | [
    "gedit",
    .path,
    "+" + (.line | tostring) + ":" + (.column | tostring),
    "&"
] | join(" ")' | bash
  1. chmod +x open-in-gedit.sh
  2. Set CARGO_EDITOR=/path/to/open-in-gedit.sh environment variable
  3. Run cargo lrun in your project directory
  4. In case of compiling errors open-in-gedit.sh will open files in gedit on affected lines and columns
  5. cargo llrun (cargo llcheck, etc.) will open them in case of warnings as well.

Similar Projects/Inspirations

  • bacon is a background rust code checker
  • cargo-firstpage shows only the first page of rustc output
  • ograc like cargo, but backwards

Support

If this project improved your workflow — please consider:

  • pressing ⭐
  • sharing this project with your friends/colleges
  • buying me some coffee veggies 🥕
    • or even helping me with actual personal expenses, so I could keep working on this and other healthy Open Source projects as long as possible ❤️

Your donations will help me allocate more time to resolve issues and to finish all the planned features! ❤️

  • Bitcoin (BTC) 1Afgvdz1oPaugFcLgDaAzCYYdHexV6tTvH

  • TRON (TRX, USDT-TRC20, etc.) TVxE2HyryNyNReMvw9HRQ3BkYePCszXSrc

  • Ethereum (ETH, DAI, etc.) 0xa879cdb1d7d859e6e425f8e50c4ee49f4b3a7b06

  • TON: EQApceYoD6FDKn4azXrUxOIyT8VF4NIV_PRE7x9KsTJC9h0q

  • Zcash (ZEC): t1NiwnuUQC1tENTY2aPDDhKEA3pykF582TP

  • Litecoin (LTC): Le3yFbk854T1nrRUjWHkqTNrwz1zgDFqVX

  • Monero (XMR): 46pRHmFpqzUBA8SLrdjW73H6ainCqRGpe8shjqQJ4UoBFAbegkAwqvjTSCBvBL71NZXYhriSuKPBkGxc1FjCeiut2EvFvxk

  • Liberapay

  • Patreon

  • Ko-fi

  • Open Collective

For general donations dedicated to all my projects please check out here.

Thank you for your support! 🙏🏼


Also thanks everyone for code contributions and bug reporting. Special thanks to Casey Rodarmor for providing VimL code for quickfix populator and Otavio Salvador for NixOS package!

License

MIT/Apache-2.0

Open Source Agenda is not affiliated with "Cargo Limit" Project. README Source: cargo-limit/cargo-limit

Open Source Agenda Badge

Open Source Agenda Rating