├── .gitignore ├── README.md ├── autoload ├── __init__.py ├── vim_sql_suggest.py └── vim_sql_suggest.vim ├── doc └── vim-sql-suggest.txt ├── plugin └── vim_sql_suggest.vim ├── requirements.txt └── tests ├── __init__.py └── vim_sql_suggest_tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-sql-suggest 2 | 3 | Use the omnicomplete popup menu to explore and complete SQL table and column 4 | names. The plugin currently supports `mysql` and `postgres` databases. 5 | 6 | # The Plugin In Action 7 | 8 | ![sql-suggest](https://cloud.githubusercontent.com/assets/4416952/4486307/1e79a0d6-49df-11e4-9f42-8b9149d28297.gif) 9 | 10 | ## Usage 11 | 12 | The plugin provides one function to call for completing tables and columns and 13 | a convenience function for easily switching the database that suggestions are 14 | provided for. 15 | 16 | ## Mapping The Functions 17 | 18 | You will need to map the complete function in insert mode. Here is an example. 19 | `sc` will complete for columns and `st` will complete for 20 | tables. 21 | 22 | ``` vim 23 | inoremap sc =SQLComplete("column") 24 | inoremap st =SQLComplete("table") 25 | ``` 26 | 27 | ## Default Database 28 | You can set a default database by setting the variable `suggest_db` in your `.vimrc` like so: 29 | 30 | ``` vim 31 | let g:suggest_db = "psql -U Jrock example_table" 32 | ``` 33 | 34 | You can also use the command `UpdateSuggestDB` to easily set the database that 35 | the plugin will look in for completions. 36 | 37 | ## Installation 38 | 39 | Use your plugin manager of choice. 40 | 41 | - [Pathogen](https://github.com/tpope/vim-pathogen) 42 | - `git clone https://github.com/JarrodCTaylor/vim-sql-suggest ~/.vim/bundle/vim-sql-suggest` 43 | - [Vundle](https://github.com/gmarik/vundle) 44 | - Add `Bundle 'JarrodCTaylor/vim-sql-suggest'` to .vimrc 45 | - Run `:BundleInstall` 46 | - [NeoBundle](https://github.com/Shougo/neobundle.vim) 47 | - Add `NeoBundle 'JarrodCTaylor/vim-sql-suggest'` to .vimrc 48 | - Run `:NeoBundleInstall` 49 | - [vim-plug](https://github.com/junegunn/vim-plug) 50 | - Add `Plug 'JarrodCTaylor/vim-sql-suggest'` to .vimrc 51 | - Run `:PlugInstall` 52 | -------------------------------------------------------------------------------- /autoload/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JarrodCTaylor/vim-sql-suggest/ed885624196e331e5edf05fe4ac63a56eee02c36/autoload/__init__.py -------------------------------------------------------------------------------- /autoload/vim_sql_suggest.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | def get_db_specific_query_statements(suggest_db): 5 | queries = { 6 | "psql_tables": "-c \"select tablename from pg_tables where schemaname = 'public'\"", 7 | "psql_columns": "-c \"select column_name from information_schema.columns where table_name = ", 8 | "mysql_tables": "-e 'SHOW tables;'", 9 | "mysql_columns": "-e 'SHOW COLUMNS FROM" 10 | } 11 | db_type = suggest_db.split(" ")[0] 12 | return (queries[db_type + "_tables"], queries[db_type + "_columns"]) 13 | 14 | 15 | def get_table_names(suggest_db): 16 | get_tables_query, _ = get_db_specific_query_statements(suggest_db) 17 | query_string = "{0} {1}".format(suggest_db, get_tables_query) 18 | tables = subprocess.check_output(query_string + " 2> /dev/null", shell=True) 19 | if suggest_db.split(" ")[0] == "mysql": 20 | return [{"word": table} for table in tables.rstrip().split("\n")[1:]] 21 | elif suggest_db.split(" ")[0] == "psql": 22 | return [{"word": table.strip()} for table in tables.rstrip().split("\n")[2:-1]] 23 | 24 | 25 | def create_column_name_list(suggest_db, tables, prefix=""): 26 | table_cols = [] 27 | db_type = suggest_db.split(" ")[0] 28 | for table in tables: 29 | table = table["word"] 30 | if db_type == "mysql": 31 | query_string = "{0} {1} {2}' 2> /dev/null".format(suggest_db, get_db_specific_query_statements(suggest_db)[1], table) 32 | columns = subprocess.check_output(query_string, shell=True) 33 | table_cols.extend([{"word": prefix + column.split("\t")[0], "menu": table, "dup": 1} for column in columns.rstrip().split("\n")[1:]]) 34 | elif db_type == "psql": 35 | query_string = "{0} {1} '{2}'\" 2> /dev/null".format(suggest_db, get_db_specific_query_statements(suggest_db)[1], table) 36 | columns = subprocess.check_output(query_string, shell=True) 37 | table_cols.extend([{"word": prefix + column.strip(), "menu": table, "dup": 1} for column in columns.rstrip().split("\n")[2:-1]]) 38 | return table_cols 39 | 40 | 41 | def get_column_names(suggest_db, word_to_complete): 42 | if word_to_complete.endswith("."): 43 | return create_column_name_list(suggest_db, [{"word": word_to_complete[:-1]}], ".") 44 | else: 45 | return create_column_name_list(suggest_db, get_table_names(suggest_db)) 46 | -------------------------------------------------------------------------------- /autoload/vim_sql_suggest.vim: -------------------------------------------------------------------------------- 1 | " ================================ 2 | " Plugin Imports 3 | " ================================ 4 | python import sys 5 | python import vim 6 | python import os 7 | python import subprocess 8 | python sys.path.append(vim.eval('expand(":h")')) 9 | python from vim_sql_suggest import * 10 | 11 | " ================================ 12 | " Plugin Function(s) 13 | " ================================ 14 | 15 | """ 16 | " The plugin offers to complete either table or columns names. This function 17 | " delegates to the appropriate python function to populate the completionList 18 | " with the desired contents. 19 | """ 20 | function! UpdateCompletionList(completeFor, wordToComplete) 21 | python << endPython 22 | complete_for = vim.eval("a:completeFor") 23 | if complete_for == "table": 24 | vim.command("let b:completionList = {}".format(get_table_names(vim.eval("g:suggest_db")))) 25 | else: 26 | vim.command("let b:completionList = {}".format(get_column_names(vim.eval("g:suggest_db"), vim.eval("a:wordToComplete")))) 27 | endPython 28 | endfunction 29 | 30 | """ 31 | " The complete function is called while in insert mode. We check the 32 | " character that is two chars behind the cursor. If it is ' ' then the 33 | " user hasn't specified a word to complete if there is a non ' ' character 34 | " there then we grab the because we need to know if there is a '.' 35 | " at the end of the word that has been entered. 36 | """ 37 | function! UpdateWordToComplete() 38 | if getline(".")[col(".")-2] == " " 39 | let b:wordToComplete = "" 40 | else 41 | execute "normal! b" 42 | let b:wordToComplete = expand('') 43 | endif 44 | endfunction 45 | 46 | """ 47 | " If the word to complete ends with a '.' then we make the assumption that 48 | " the dot is preceded with a table name and the user wants all of the 49 | " columns for that table returned as complete options. 50 | """ 51 | function! UpdateMatches() 52 | if b:wordToComplete[len(b:wordToComplete) - 1] == "." 53 | let b:matches = b:completionList 54 | else 55 | let b:matches = [] 56 | for item in b:completionList 57 | if(match(item["word"],'^'.b:wordToComplete)==0) 58 | call add(b:matches,item) 59 | endif 60 | endfor 61 | endif 62 | endfunction 63 | 64 | function! SQLComplete(completeFor) 65 | call UpdateWordToComplete() 66 | let l:cursorPosition = col('.') 67 | execute "normal! A\" 68 | call UpdateCompletionList(a:completeFor, b:wordToComplete) 69 | call UpdateMatches() 70 | redraw! 71 | call complete(l:cursorPosition, b:matches) 72 | return '' 73 | endfunc 74 | 75 | """ 76 | " A convenience function that informs the user of the current database and 77 | " allows them to provide a connection to a new database. 78 | """ 79 | function! vim_sql_suggest#UpdateSuggestDB() 80 | python << endPython 81 | def python_input(message = 'input'): 82 | vim.command('call inputsave()') 83 | vim.command("let user_input = input('" + message + ": ')") 84 | vim.command('call inputrestore()') 85 | return vim.eval('user_input') 86 | 87 | current_db = int(vim.eval('exists("g:suggest_db")')) 88 | print("The current database is: {}".format(vim.eval("g:suggest_db") if current_db else "Undefined")) 89 | new_db = python_input("Enter the desired DB") 90 | vim.command('let g:suggest_db = "{}"'.format(new_db)) 91 | endPython 92 | endfunction 93 | -------------------------------------------------------------------------------- /doc/vim-sql-suggest.txt: -------------------------------------------------------------------------------- 1 | *vim-sql-suggest.txt* A short multi line description of your plugin 2 | 3 | =============================================================================== 4 | CONTENTS *vim-sql-suggest* 5 | 6 | 1. Intro .......................................... |vim-sql-suggest-intro| 7 | 2. Usage .......................................... |vim-sql-suggest-usage| 8 | 3. Licence ...................................... |vim-sql-suggest-licence| 9 | =============================================================================== 10 | 1. Intro *vim-sql-suggest-intro* 11 | 12 | Use the omnicomplete popup menu to explore and complete SQL table and column 13 | names. The plugin currently supports `mysql` and `postgres` databases. 14 | 15 | 2. Usage *vim-sql-suggest-usage* 16 | 17 | The plugin provides one function to call for completing tables and columns and 18 | a convenience function for easily switching the database that suggestions are 19 | provided for. 20 | 21 | 2.1 Mapping The Functions 22 | 23 | You will need to map the complete function in insert mode. Here is an example. 24 | `sc` will complete for columns and `st` will complete for 25 | tables. 26 | 27 | ``` vim 28 | inoremap sc =SQLComplete("column") 29 | inoremap st =SQLComplete("table") 30 | ``` 31 | 32 | 2.2 Default Database 33 | 34 | You can set a default database by setting the variable `suggest_db` in your `.vimrc` like so: 35 | 36 | ``` vim 37 | let g:suggest_db = "psql -U Jrock example_table" 38 | ``` 39 | 40 | You can also use the command `UpdateSuggestDB` to easily set the database that 41 | the plugin will look in for completions. 42 | 43 | 44 | 3. Licence *vim-sql-suggest-licence* 45 | 46 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 47 | Version 2, December 2004 48 | 49 | Copyright (C) 2014 Jarrod Taylor 50 | 51 | Everyone is permitted to copy and distribute verbatim or modified 52 | copies of this license document, and changing it is allowed as long 53 | as the name is changed. 54 | 55 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 56 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 57 | 58 | 0. You just DO WHAT THE FUCK YOU WANT TO. 59 | 60 | " vim: ft=help" 61 | -------------------------------------------------------------------------------- /plugin/vim_sql_suggest.vim: -------------------------------------------------------------------------------- 1 | command! UpdateSuggestDB call vim_sql_suggest#UpdateSuggestDB() 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | mock 2 | nose 3 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JarrodCTaylor/vim-sql-suggest/ed885624196e331e5edf05fe4ac63a56eee02c36/tests/__init__.py -------------------------------------------------------------------------------- /tests/vim_sql_suggest_tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from mock import patch 3 | 4 | import autoload.vim_sql_suggest as sut 5 | 6 | 7 | class VimSqlSuggestTests(unittest.TestCase): 8 | 9 | def test_get_db_specific_query_statuments_with_mysql_database_connection(self): 10 | table_query, column_query = sut.get_db_specific_query_statements("mysql -u root test") 11 | self.assertEqual(table_query, "-e 'SHOW tables;'") 12 | self.assertEqual(column_query, "-e 'SHOW COLUMNS FROM") 13 | 14 | def test_get_db_specific_query_statuments_with_psql_database_connection(self): 15 | table_query, column_query = sut.get_db_specific_query_statements("psql -U Jrock test") 16 | self.assertEqual(table_query, "-c \"select tablename from pg_tables where schemaname = 'public'\"") 17 | self.assertEqual(column_query, "-c \"select column_name from information_schema.columns where table_name = ") 18 | 19 | @patch('subprocess.check_output') 20 | def test_get_table_names_for_mysql(self, sb_output): 21 | sb_output.return_value = "Tables_in_test\ntable1\ntable2\ntable3" 22 | table_list = sut.get_table_names("mysql -u root test") 23 | self.assertEqual(table_list, [{"word": "table1"}, {"word": "table2"}, {"word": "table3"}]) 24 | 25 | @patch('subprocess.check_output') 26 | def test_get_table_names_for_psql(self, sb_output): 27 | sb_output.return_value = " tablename\n----------\n table1\n table2\n table3\n(3 rows)" 28 | table_list = sut.get_table_names("psql -U Jrock test") 29 | self.assertEqual(table_list, [{"word": "table1"}, {"word": "table2"}, {"word": "table3"}]) 30 | 31 | @patch('subprocess.check_output') 32 | def test_get_column_names_for_mysql(self, sb_output): 33 | with patch('subprocess.check_output', side_effect=["Tables_in_test\ntable1\ntable2", 34 | "Field\tType\tNull\tKey\tDefault\tExtra\nid\tint(11)\tNO\tPRI\tNULL\tauto_increment\nthing\tvarchar(100)\tNO\tNULL\t", 35 | "Field\tType\tNull\tKey\tDefault\tExtra\nid\tint(11)\tNO\tPRI\tNULL\tauto_increment\nthing\tvarchar(100)\tNO\tNULL\t"]): 36 | col_list = sut.get_column_names("mysql -u root test", "dummy") 37 | expected_return_val = [{'dup': 1, 'menu': 'table1', 'word': 'id'}, 38 | {'dup': 1, 'menu': 'table1', 'word': 'thing'}, 39 | {'dup': 1, 'menu': 'table2', 'word': 'id'}, 40 | {'dup': 1, 'menu': 'table2', 'word': 'thing'}] 41 | self.assertEqual(col_list, expected_return_val) 42 | 43 | @patch('subprocess.check_output') 44 | def test_get_column_names_for_psql(self, sb_output): 45 | with patch('subprocess.check_output', side_effect=[" tablename\n----------\n table1\n table2\n(2 rows)", 46 | " column_name\n----------\n id\n thing\n(2 rows)", 47 | " column_name\n----------\n id\n stuff\n(2 rows)"]): 48 | col_list = sut.get_column_names("psql -U Jrock test", "dummy") 49 | expected_return_val = [{'dup': 1, 'menu': 'table1', 'word': 'id'}, 50 | {'dup': 1, 'menu': 'table1', 'word': 'thing'}, 51 | {'dup': 1, 'menu': 'table2', 'word': 'id'}, 52 | {'dup': 1, 'menu': 'table2', 'word': 'stuff'}] 53 | self.assertEqual(col_list, expected_return_val) 54 | 55 | @patch('subprocess.check_output') 56 | def test_get_column_names_for_mysql_when_word_to_complete_ends_with_a_dot(self, sb_output): 57 | with patch('subprocess.check_output', side_effect=["Field\tType\tNull\tKey\tDefault\tExtra\nid\tint(11)\tNO\tPRI\tNULL\tauto_increment\nthing\tvarchar(100)\tNO\tNULL\t", 58 | "Field\tType\tNull\tKey\tDefault\tExtra\nid\tint(11)\tNO\tPRI\tNULL\tauto_increment\nthing\tvarchar(100)\tNO\tNULL\t"]): 59 | col_list = sut.get_column_names("mysql -u root test", "table1.") 60 | expected_return_val = [{'dup': 1, 'menu': 'table1', 'word': '.id'}, 61 | {'dup': 1, 'menu': 'table1', 'word': '.thing'}] 62 | self.assertEqual(col_list, expected_return_val) 63 | 64 | @patch('subprocess.check_output') 65 | def test_get_column_names_for_psql_when_word_to_complete_ends_with_a_dot(self, sb_output): 66 | with patch('subprocess.check_output', side_effect=[" column_name\n----------\n id\n thing\n(2 rows)", 67 | " column_name\n----------\n id\n stuff\n(2 rows)"]): 68 | col_list = sut.get_column_names("psql -U Jrock test", "table1.") 69 | expected_return_val = [{'dup': 1, 'menu': 'table1', 'word': '.id'}, 70 | {'dup': 1, 'menu': 'table1', 'word': '.thing'}] 71 | self.assertEqual(col_list, expected_return_val) 72 | --------------------------------------------------------------------------------