diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..769fa1a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +tags +*.sw[op] +__pycache__ +doc/tags diff --git a/README.rst b/README.rst index 0c83916..9e67bc0 100644 --- a/README.rst +++ b/README.rst @@ -1,31 +1,117 @@ -vim-pudb -======== +vim-pudb-and-jam +================ + +A simple plugin allowing you to manage pudb breakpoints directly from vim. +Forked from `vim-pudb`_ and given some jammy features such as setting and +editing breakpoint conditions, customization options, improved persistence +of breakpoints, and the ability to list active breakpoints either as a +printed message or in a quickfix list. -simple plugin allowing you to manage pudb breakpoint directly into vim. +.. _vim-pudb: https://github.com/KangOl/vim-pudb -This plugin need vim to be compiled with +python Installation ============ -The recommended way of installing is to use `vim-pathogen`_ +Similar to any other vim plugin, use your preferred method. If you're new, check +out +`vim-pathogen `_ or +`vim-plug `_ or +`:help packages `_ + + +Requirements +------------ +This plugin needs vim to be compiled with ``+python`` or ``+python3`` as well as +``+signs`` and is intended for vim 8.2 or later, though I'm not sure exactly +which patch is the earliest that is supported. -How to use -========== -To add/remove a breakpoint, you just need to call the command ``:TogglePudbBreakPoint`` +You will also need to have `pudb`_ installed, obviously. + +.. _pudb: https://pypi.org/project/pudb/ + + +Commands +======== -For easy access, you can bind it to the F8 key. +``:PudbToggle`` + Add / remove a breakpoint at the current line. +``:PudbEdit`` + Edit the condition of a breakpoint on the current line. Creates a + breakpoint if one doesn't already exist. - ``nnoremap :TogglePudbBreakPoint`` +``:PudbClearAll`` + Remove all breakpoints from every file. - ``inoremap :TogglePudbBreakPointa`` +``:PudbList`` + Show a list of the full file paths, line numbers, and conditions of all + breakpoints. -.. _vim-pathogen: https://github.com/tpope/vim-pathogen#readme +``:PudbLocList`` + Load all breakpoints into the location list. Does not jump to the first + entry. + +``:PudbQfList`` + Load all breakpoints into the quickfix list. Does not jump to the first + entry. + +``:PudbPopulateList `` + Supply a list of all breakpoints, in quickfix format, to the Ex command + given by . This is a generic form of PudbLocList and PudbQfList, + allowing you to customise the operation for any precise need you may have. + +``:PudbUpdate`` + Sometimes the breakpoint signs can get out of date. The above commands will + all trigger an update, but this command lets you trigger an update without + doing anything else. + + NOTE: There should no longer be any need to call this command. Breakpoint + signs are updated whenever you save the buffer. + + +Mappings +======== -Know problems +There are no mappings set up by default, so you don't have to worry about +conflicts with other plugins. Here's what I use: + +:: + + nnoremap bc :PudbClearAll + nnoremap be :PudbEdit + nnoremap bl :PudbList + nnoremap bq :PudbQfList + nnoremap bp :PudbToggle + nnoremap bu :PudbUpdate + + +Configuration ============= -Currently, the list of breakpoints is not reloaded automatically. -There is also room for speed optimisations. +The text of the sign can be defined with ``g:pudb_sign`` (default ``'B>'``): + +``let g:pudb_sign = 'B>'`` + +The highlight group of the sign in the sign column can be defined with +``g:pudb_highlight`` (default ``'error'``): + +``let g:pudb_highlight = 'error'`` + +The priority of the breakpoint signs can be defined with ``g:pudb_priority`` +(default ``100``): + +``let g:pudb_priority = 100`` + +This plugin uses sign groups. You can change the name of the sign group using +``g:pudb_sign_group`` (default ``pudb_sign_group``): + +``let g:pudb_sign_group = 'pudb_sign_group'`` + + +Known problems +============== + +- There may be room for speed optimisations. +- There is currently no way to specify which breakpoint file to use. diff --git a/doc/vim-pudb-and-jam.txt b/doc/vim-pudb-and-jam.txt new file mode 100644 index 0000000..8fccf50 --- /dev/null +++ b/doc/vim-pudb-and-jam.txt @@ -0,0 +1,186 @@ +*vim-pudb-and-jam.txt* Manage pudb breakpoints directly from vim + +Author: Michael van der Kamp +License: Same terms as vim itself (see |license|) + +Forked from vim-pudb: https://github.com/KangOl/vim-pudb + + +============================================================================== +CONTENTS *vim-pudb-contents* *vim-pudb* + *vim-pudb-and-jam* + + 1. Introduction ........................... |vim-pudb-introduction| + 2. Installation ........................... |vim-pudb-installation| + 3. Requirements ........................... |vim-pudb-requirements| + 4. Commands ............................... |vim-pudb-commands| + 4.1 PudbToggle ........................ |vim-pudb-:PudbToggle| + 4.2 PudbEdit .......................... |vim-pudb-:PudbEdit| + 4.2 PudbMove .......................... |vim-pudb-:PudbMove| + 4.3 PudbClearAll ...................... |vim-pudb-:PudbClearAll| + 4.4 PudbList .......................... |vim-pudb-:PudbList| + 4.5 PudbLocList ....................... |vim-pudb-:PudbLocList| + 4.6 PudbQfList ........................ |vim-pudb-:PudbQfList| + 4.7 PudbPopulateList .................. |vim-pudb-:PudbPopulateList| + 4.8 PudbUpdate ........................ |vim-pudb-:PudbUpdate| + 4.8 PudbEditFile ...................... |vim-pudb-:PudbEditFile| + 5. Configuration .......................... |vim-pudb-configuration| + 5.1 vim-pudb-sign ..................... |vim-pudb-sign| + 5.2 vim-pudb-sign-group ............... |vim-pudb-sign-group| + 5.3 vim-pudb-highlight ................ |vim-pudb-highlight| + 5.4 vim-pudb-priority ................. |vim-pudb-priority| + 6. Known problems ......................... |vim-pudb-known-problems| + + +============================================================================== +INTRODUCTION *vim-pudb-introduction* + +This plugin allows you to: + + * Toggle pudb breakpoints + * Edit and view pudb breakpoints + * List pudb breakpoints + * Load pudb breakpoints into the quickfix list + +There are no mappings set up by default, so you don't have to worry about +conflicts with other plugins. Here are some recommended mappings though, +to add to your `.vimrc` file: +> + nnoremap bc :PudbClearAll + nnoremap be :PudbEdit + nnoremap bm :PudbMove + nnoremap bl :PudbList + nnoremap bq :PudbQfList + nnoremap bp :PudbToggle + nnoremap bu :PudbUpdate +< + +============================================================================== +INSTALLATION *vim-pudb-installation* + +Similar to any other vim plugin, use your preferred method. If you're new, +check out: + + packages: |packages| + vim-pathogen: https://github.com/tpope/vim-pathogen#readme + vim-plug: https://github.com/junegunn/vim-plug + + +============================================================================== +REQUIREMENTS *vim-pudb-requirements* + +This plugin needs vim to be compiled with `+python` or `+python3` as well as +`+signs` and is intended for vim 8.2 or later. + +You will also need to have `pudb` installed: https://pypi.org/project/pudb/ + + +============================================================================== +COMMANDS *vim-pudb-commands* + +------------------------------------------------------------------------------ + *vim-pudb-:PudbToggle* +:PudbToggle + Add / remove a breakpoint at the current line. + +------------------------------------------------------------------------------ + *vim-pudb-:PudbEdit* +:PudbEdit + Edit the condition of a breakpoint on the current line. Creates a + breakpoint if one doesn't already exist. + +------------------------------------------------------------------------------ + *vim-pudb-:PudbMove* +:PudbMove + Move the location of a breakpoint on the current line. + +------------------------------------------------------------------------------ + *vim-pudb-:PudbClearAll* +:PudbClearAll + Remove all breakpoints from every file. + +------------------------------------------------------------------------------ + *vim-pudb-:PudbList* +:PudbList + Show a list of the full file paths, line numbers, and conditions of all + breakpoints. + +------------------------------------------------------------------------------ + *vim-pudb-:PudbLocList* +:PudbLocList + Load all breakpoints into the location list. Does not jump to the first + entry. + +------------------------------------------------------------------------------ + *vim-pudb-:PudbQfList* +:PudbQfList + Load all breakpoints into the quickfix list. Does not jump to the first + entry. + +------------------------------------------------------------------------------ + *vim-pudb-:PudbPopulateList* +:PudbPopulateList + Supply a list of all breakpoints, in quickfix format, to the Ex command + given by . This is a generic form of PudbLocList and PudbQfList, + allowing you to customise the operation for any precise need you may have. + +------------------------------------------------------------------------------ + *vim-pudb-:PudbUpdate* +:PudbUpdate + Sometimes the breakpoint signs can get out of date. The above commands + will all trigger an update, but this command lets you trigger an update + without doing anything else. + + NOTE: There should no longer be any need to call this command. Breakpoint + signs are updated whenever you save the buffer. + +------------------------------------------------------------------------------ + *vim-pudb-:PudbEditFile* +:PudbEditFile + Open the breakpoint file for editing in the current window. Breakpoint + signs will not be maintained when saving direct changes to the file. + + +============================================================================== +CONFIGURATION *vim-pudb-configuration* + +------------------------------------------------------------------------------ + *vim-pudb-sign* +The text of the sign can be defined with `g:pudb_sign`: + + `let g:pudb_sign = 'B>'` + +Default: `'B>'` + +------------------------------------------------------------------------------ + *vim-pudb-sign-group* +This plugin uses sign groups. You can change the name of the sign group using +`g:pudb_sign_group`: + + `let g:pudb_sign_group = 'pudb_sign_group'` + +Default: `'pudb_sign_group'` + +------------------------------------------------------------------------------ + *vim-pudb-highlight* +The highlight group of the sign in the sign column can be defined with +`g:pudb_highlight`: + + `let g:pudb_highlight = 'error'` + +Default: `'error'` + +------------------------------------------------------------------------------ + *vim-pudb-priority* +The priority of the breakpoint signs can be defined with `g:pudb_priority`: + + `let g:pudb_priority = 100` + +Default: `100` + + +============================================================================== +KNOWN PROBLEMS *vim-pudb-known-problems* + +- There may be room for speed optimisations. +- There is currently no way to specify which breakpoint file to use. diff --git a/plugin/pudb.vim b/plugin/pudb.vim index 9623de3..7d0956a 100644 --- a/plugin/pudb.vim +++ b/plugin/pudb.vim @@ -1,90 +1,89 @@ " File: pudb.vim -" Author: Christophe Simonis -" Description: Manage pudb breakpoints directly into vim -" Last Modified: March 02, 2018 -" -" TODO: handle conditions in breakpoints (at least do not loose them when saving breakpoints) +" Author: Christophe Simonis, Michael van der Kamp +" Description: Manage pudb breakpoints directly from vim -if exists('g:loaded_pudb_plugin') || &cp + +if exists('g:loaded_pudb_plugin') || &compatible finish endif let g:loaded_pudb_plugin = 1 -if !has("pythonx") - echo "Error: Required vim compiled with +python and/or +python3" +function! s:EchoError(msg) abort + echohl Error + echo a:msg + echohl None +endfunction + +if !has('pythonx') + call s:EchoError('vim-pudb requires vim compiled with +python and/or +python3') finish endif -sign define PudbBreakPoint text=Ø) texthl=error - -let s:first_sign_id = 10000 -let s:next_sign_id = s:first_sign_id - -augroup pudb - autocmd BufReadPost *.py call s:UpdateBreakPoints() -augroup end - -command! TogglePudbBreakPoint call s:ToggleBreakPoint() - -function! s:UpdateBreakPoints() - -" first remove existing signs -if !exists("b:pudb_sign_ids") - let b:pudb_sign_ids = [] +if !has('signs') + call s:EchoError('vim-pudb requires vim compiled with +signs') + finish endif -for i in b:pudb_sign_ids - exec "sign unplace " . i -endfor -let b:pudb_sign_ids = [] - -pythonx << EOF -import vim -from pudb.settings import load_breakpoints -from pudb import NUM_VERSION +"" +" Load options and set defaults +"" +let g:pudb_sign = get(g:, 'pudb_sign', 'B>') +let g:pudb_highlight = get(g:, 'pudb_highlight', 'error') +let g:pudb_priority = get(g:, 'pudb_priority', 100) +let g:pudb_sign_group = get(g:, 'pudb_sign_group', 'pudb_sign_group') -filename = vim.eval('expand("%:p")') +call sign_define('PudbBreakPoint', { + \ 'text': g:pudb_sign, + \ 'texthl': g:pudb_highlight + \ }) -args = () if NUM_VERSION >= (2013, 1) else (None,) -bps = load_breakpoints(*args) -for bp in bps: - if bp[0] != filename: - continue +"" +" Everything is defined in a python module on the runtimepath! +"" +try + pyx import pudb_and_jam +catch + let s:import_failed = v:true +endtry - sign_id = vim.eval("s:next_sign_id") - vim.command("sign place %s line=%s name=PudbBreakPoint file=%s" % (sign_id, bp[1], filename)) - vim.eval("add(b:pudb_sign_ids, s:next_sign_id)") - vim.command("let s:next_sign_id += 1") -EOF - -endfunction - -function! s:ToggleBreakPoint() -pythonx << EOF -import vim -from pudb.settings import load_breakpoints, save_breakpoints -from pudb import NUM_VERSION -from bdb import Breakpoint - -args = () if NUM_VERSION >= (2013, 1) else (None,) -bps = [bp[:2] for bp in load_breakpoints(*args)] - -filename = vim.eval('expand("%:p")') -row, col = vim.current.window.cursor +if get(s:, 'import_failed', v:false) + call s:EchoError('vim-pudb-and-jam requires pudb to be installed') + finish +endif -bp = (filename, row) -if bp in bps: - bps.pop(bps.index(bp)) -else: - bps.append(bp) +"" +" Define ex commands for all the above functions so they are user-accessible. +"" +command! PudbClearAll pyx pudb_and_jam.clear_all_breakpoints() +command! PudbEditFile pyx pudb_and_jam.edit_breakpoint_file() +command! PudbEdit pyx pudb_and_jam.edit_condition() +command! PudbMove pyx pudb_and_jam.move_breakpoint() +command! PudbList pyx pudb_and_jam.list_breakpoints() +command! PudbLocList pyx pudb_and_jam.location_list() +command! PudbQfList pyx pudb_and_jam.quickfix_list() +command! PudbToggle pyx pudb_and_jam.toggle_breakpoint() +command! PudbUpdate pyx pudb_and_jam.update_breakpoints() +command! -nargs=1 -complete=command PudbPopulateList + \ pyx pudb_and_jam.populate_list("") + + +"" +" If we were loaded lazily, update immediately. +"" +if &filetype ==? 'python' + pyx pudb_and_jam.update_breakpoints() +endif -bp_list = [Breakpoint(bp[0], bp[1]) for bp in bps] -save_breakpoints(bp_list) +augroup pudb + autocmd! -vim.command('call s:UpdateBreakPoints()') -EOF -endfunction + " Update when the file is first read. + autocmd BufReadPost *.py PudbUpdate + " Force a linecache update after writes so the breakpoints can be parsed + " correctly. + autocmd BufWritePost *.py pyx pudb_and_jam.clear_linecache() +augroup end diff --git a/pythonx/pudb_and_jam.py b/pythonx/pudb_and_jam.py new file mode 100644 index 0000000..ae5bcf6 --- /dev/null +++ b/pythonx/pudb_and_jam.py @@ -0,0 +1,232 @@ +# vim: tw=79 +import vim + +from bdb import Breakpoint +from itertools import starmap +from linecache import checkcache +from pudb.settings import ( + load_breakpoints, save_breakpoints, get_breakpoints_file_name) +from pudb import NUM_VERSION + +LOAD_ARGS = () if NUM_VERSION >= (2013, 1) else (None,) + + +def breakpoints(): + """ + :return: An iterator over the saved breakpoints. + :rtype: starmap(Breakpoint) + """ + return starmap(Breakpoint, load_breakpoints(*LOAD_ARGS)) + + +def breakpoint_dict(): + """ + :return: The saved breakpoints, as a dict with (filename, line number) keys + :rtype: dict(tuple(str, int), Breakpoint) + """ + return {(bp.file, bp.line): bp for bp in breakpoints()} + + +def breakpoint_strings(empty_cond_str=''): + """ + :return: A generator over the saved breakpoints as strings in the format: + "filename:linenr:condition" + :rtype: generator(str) + """ + return ( + '{file}:{line:d}:{cond}'.format( + file=bp.file, + line=bp.line, + cond=bp.cond if bp.cond else empty_cond_str) + for bp in breakpoints() + ) + + +def update_breakpoints(): + vim.eval('sign_unplace(g:pudb_sign_group)') + for bp in breakpoints(): + try: + options = '{{"lnum": {line:d}, "priority": {prio:d}}}'.format( + line=bp.line, + prio=vim.vars['pudb_priority'], + ) + + # Critical to use vim.eval here instead of vim.vars[] to get sign + # group, since vim.vars[] will render the string as + # "b'pudb_sign_group'" instead of "pudb_sign_group" + vim.eval( + 'sign_place(0, "{group}", "PudbBreakPoint", "{file}", {opts})' + .format( + group=vim.eval('g:pudb_sign_group'), + file=bp.file, + opts=options, + )) + except vim.error: + # Buffer for the given file isn't loaded. + continue + + +def current_position(): + """ + :return: a filename, line number pair, to be used as a key for a + breakpoint. + :rtype: tuple(str, int) + """ + filename = vim.current.buffer.name + row, _ = vim.current.window.cursor + return (filename, row) + + +def toggle_breakpoint(): + """ + Toggles a breakpoint on the current line. + """ + bps = breakpoint_dict() + bp_key = current_position() + + if bp_key in bps: + bps.pop(bp_key) + else: + bps[bp_key] = Breakpoint(*bp_key) + + save_breakpoints(bps.values()) + update_breakpoints() + + +def edit_condition(): + """ + Edit the condition of a breakpoint on the current line. + If no such breakpoint exists, creates one. + """ + bps = breakpoint_dict() + bp_key = current_position() + + if bp_key not in bps: + bps[bp_key] = Breakpoint(*bp_key) + bp = bps[bp_key] + + old_cond = '' if bp.cond is None else bp.cond + vim.command('echo "Current condition: {}"'.format(old_cond)) + vim.command('echohl Question') + vim.eval('inputsave()') + bp.cond = vim.eval('input("New Condition: ", "{}")'.format(old_cond)) + vim.eval('inputrestore()') + vim.command('echohl None') + + save_breakpoints(bps.values()) + update_breakpoints() + + +def move_breakpoint(): + """ + Move the breakpoint to a different line, preserving the condition. + """ + bps = breakpoint_dict() + bp_key = current_position() + + if bp_key not in bps: + vim.command('echo "No breakpoint at current position"') + return + + bp = bps[bp_key] + old_line = bp.line + vim.command('echo "Current line: {}"'.format(old_line)) + vim.command('echohl Question') + vim.eval('inputsave()') + new_line = (vim.eval('input("New line: ", "{}")'.format(old_line))) + vim.eval('inputrestore()') + vim.command('echohl None') + + try: + bp.line = int(new_line) + except ValueError: + vim.command('echo "Invalid line number: {}"'.format(new_line)) + return + + save_breakpoints(bps.values()) + update_breakpoints() + + +def edit_breakpoint_file(): + """ + Open the breakpoint file in a buffer for direct editing. + """ + vim.command('edit {}'.format(get_breakpoints_file_name())) + + +def clear_all_breakpoints(): + """ + Clears all pudb breakpoints from all files. + """ + save_breakpoints([]) + update_breakpoints() + + +def list_breakpoints(): + """ + Prints a list of all the breakpoints in all files. + Shows the full file path, line number, and condition of each breakpoint. + """ + update_breakpoints() + vim.command('echomsg "Listing all pudb breakpoints:"') + for bp_string in breakpoint_strings(): + vim.command('echomsg "{}"'.format(bp_string)) + + +def populate_list(list_command): + """ + Calls the given vim command with a list of the breakpoints as strings in + quickfix format. + """ + update_breakpoints() + bps = list(breakpoint_strings()) + vim.command('{command} {breakpoints}'.format( + command=list_command, + breakpoints=bps, + )) + + +def quickfix_list_arg(): + """ + :return: The list of dicts to provide as the first argument to setqflist() + :rtype: list[dict] + """ + return [ + { + 'filename': bp.file, + 'lnum': bp.line, + 'text': bp.cond if bp.cond else '', + } + for bp in breakpoints() + ] + + +def quickfix_list(): + """ + Populate the quickfix list with the breakpoint locations. + """ + setqflist = vim.Function('setqflist') + entries = quickfix_list_arg() + setqflist(entries) + height = min(10, len(entries)) + vim.command('cwindow {}'.format(height)) + + +def location_list(): + """ + Populate the location list with the breakpoint locations. + """ + setloclist = vim.Function('setloclist') + entries = quickfix_list_arg() + setloclist(entries) + height = min(10, len(entries)) + vim.command('cwindow {}'.format(height)) + + +def clear_linecache(): + """ + Clear the python line cache for the given file if it has changed + """ + filename = vim.current.buffer.name + checkcache(filename) + update_breakpoints()