Skip to content

Commit

Permalink
executioner.vim 1.0.0
Browse files Browse the repository at this point in the history
Features
Complete implementation of full and base name symbol subtitution.
Rename g:executioner#current_file to g:executioner#full_name.
Add g:executioner#base_name to ease more complicated commands.
Both symbols are defined only if they are not already defined in vimrc.

Improve c and cpp default commands to output file based on file name.
Add r to default as R file.

Non-backwards compatible changes
Change executioner#extensions to account for subsitution feature

Code
Add test.* and Test.* files to .gitignore
  • Loading branch information
EvanQuan committed Nov 25, 2018
1 parent af13d47 commit 7ba1ca0
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 102 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store

test.*
Test.*
64 changes: 35 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Table of Contents
- [Key mappings](#key-mappings)
3. [Configure Executable Files](#configure-executable-files)
- [Commands](#commands)
- [Current file](#current-file)
- [Full and base name symbols](#full-and-base-name-symbols)

## Installation

Expand Down Expand Up @@ -122,6 +122,24 @@ nnoremap <silent> <leader>vrm :ExecutionerVertical makefile<CR>

## Configure Executable Files

#### Full and base name symbols

You may want to refer to the full file name or base name in in your commands.
The full file name, which is the base name with file extension, can be referred
to by `g:executioner#full_name`, while the base name can be referred to by
`g:executioner#base_name`, both which you can set in your `.vimrc`. By default
they are defined as:

```vim
let g:executioner#full_name = '%'
let g:executioner#base_name = '@'
```

For example, if you want to run a C file by compiling it first, you can define
its command as `'c' : 'gcc % -o @.out; ./@.out'` in `g:executioner#commands`,
which will compile a `.out` fie with the same base name as the source file,
and then execute it.

#### Commands

There are 2 dictionaries that define what types of files can be executed:
Expand All @@ -132,25 +150,25 @@ a command based on a file name. If not defined in your `.vimrc`, they are
by default defined as:

```vim
" extension : <command
" Command is executed with file as argument
" $ command filename.extension
" extension : command
" Command is executed if file has specified extension
let g:executioner#extensions = {
\ 'c' : 'gcc % -o a.out; ./a.out',
\ 'cpp' : 'g++ % -o a.out; ./a.out',
\ 'R' : 'Rscript',
\ 'hs' : 'ghci',
\ 'js' : 'node',
\ 'php' : 'php',
\ 'pl' : 'perl',
\ 'prolog' : 'swipl',
\ 'py' : 'python3',
\ 'sh' : 'bash',
\ 'c' : 'gcc % -o @.out; ./@.out',
\ 'cpp' : 'g++ % -o @.out; ./@.out',
\ 'R' : 'Rscript %',
\ 'r' : 'Rscript %',
\ 'hs' : 'ghci %',
\ 'js' : 'node %',
\ 'php' : 'php %',
\ 'pl' : 'perl %',
\ 'prolog' : 'swipl %',
\ 'py' : 'python3 %',
\ 'py2' : 'python %',
\ 'sh' : 'bash %',
\}
" file name : command
" Command is executed with no arguments
" $ command
" Command is executed if file has specified name
let g:executioner#names = {
\ 'makefile': 'make',
\}
Expand All @@ -159,7 +177,7 @@ let g:executioner#names = {
`g:executioner#extensions` determines commands by file extension. For example,
if you want to execute files with the `.foo` extension, such as
`hello_world.foo`, with the `bar` command, (i.e. executing `bar
hello_world.foo` in the terminal), then the value `'foo' : 'bar'` must be
hello_world.foo` in the terminal), then the value `'foo' : 'bar %'` must be
included in this dictionary.

`g:executioner#names` determines commands by file name. For example, if you want
Expand All @@ -173,15 +191,3 @@ to be executed with `python3` and `g:executioner#names` dictates that `foo.py`
is to be executed with `python2`, then `foo.py` will be executed with
`python2`.

#### Current file

Sometimes you may want to refer to the current file as well as add command line
arguments. The current file can be referred to by `g:executioner#current_file`,
which you can set in your `.vimrc`. By default it is defined as:

```vim
let g:executioner#current_file = '%'
```

For example, if you want to run a C file by compiling it first, you can define
its command as `'c' : 'gcc % -o a.out; ./a.out'` in `g:executioner#commands`.
190 changes: 117 additions & 73 deletions plugin/executioner.vim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
" ============================================================================
" File: executioner.vim
" Maintainer: https://github.com/EvanQuan/vim-executioner/
" Version: 0.6.0
" Version: 1.0.0
"
" A Vim plugin to easily execute files in the terminal or a separate buffer.
" ============================================================================
Expand All @@ -12,39 +12,33 @@ endif
let g:executioner#loaded = 1

" Name and extension
if !exists("g:executioner#file_symbol")
let g:executioner#file_symbol = '%'
if !exists("g:executioner#full_name")
let g:executioner#full_name = '%'
endif
" Just name
if !exists("g:executioner#name_symbol")
let g:executioner#name_symbol = '@'
if !exists("g:executioner#base_name")
let g:executioner#base_name = '@'
endif

" Fake enums

" Parsed input
let s:NAME = 0
let s:ARGS = 1
let s:FILE = 0
let s:NAME = 1
let s:EXTENSION = 2
let s:ARGS = 3

" Split types
let s:NONE = 2
let s:VERTICAL = 3
let s:HORIZONTAL = 4
let s:NONE = 0
let s:VERTICAL = 1
let s:HORIZONTAL = 3

" Command types
let s:EXTENSION_COMMAND = 5
let s:NAME_COMMAND = 6

" Regex
" TODO improve regex to factor in last . not first incase file has multiple
" dots, or file starts with dot
" This finds the first . and returns everything that follows it
" This is not optimal but is good for now.
let s:DOT_WITH_FILE_EXTENSION = '\..*'

" extension : <command
" Command is executed with file as argument
" $ command filename.extension
let s:EXTENSION_COMMAND = 0
let s:NAME_COMMAND = 1

" extension : command
" Command is executed if file has specified extension
if !exists("g:executioner#extensions")
let g:executioner#extensions = {
\ 'c' : 'gcc % -o @.out; ./@.out',
Expand All @@ -63,79 +57,117 @@ if !exists("g:executioner#extensions")
endif

" file name : command
" Command is executed with no arguments
" $ command
" Command is executed if file has specified name
if !exists("g:executioner#names")
let g:executioner#names = {
\ 'makefile': 'make',
\}
endif

function! s:GetExtension(file_name) abort
" Get the extension of file name denoted characters after the first "."
function! s:SplitNameAndExtenstion(file) abort
" Get the extension of file name denoted characters after the last "."
" Paramters:
" string file_name
" string file
" Returns:
" the extension of file_name if found
" "" if no extension is found
return matchstr(a:file_name, s:DOT_WITH_FILE_EXTENSION)[1:]
" list [name, extension] if extension is found
" list [file, ""] if no extension is found
" list ["", ""] if input is empty
if len(a:file) == 0
return ["", ""]
endif

" Split the file in terms of "."
let s:file_split = split(a:file, '\.')
let s:name = s:file_split[0]
if len(s:file_split) > 1
for i in range(1, len(s:file_split) - 2)
let s:name .= '.' . s:file_split[i]
endfor
let s:extension = s:file_split[len(s:file_split) - 1]
else
let s:extension = ""
endif
return [s:name, s:extension]
endfunction

function! s:GetExtension(...) abort
return 1
endfunction

function! s:GetExecuteCommand(parsed_input) abort
" Returns the execute command of the parsed_input
" If not executable, returns empty string
" echom "GetExecuteCommand file name: \"" . a:parsed_input[s:NAME] . "\""
let s:extension = s:GetExtension(a:parsed_input[s:NAME])
" echom "GetExecuteCommand extension: \"" . s:extension . "\""
if has_key(g:executioner#names, a:parsed_input[s:NAME])
return g:executioner#names[a:parsed_input] . a:parsed_input[s:ARGS]
elseif has_key(g:executioner#extensions, s:extension)
return g:executioner#extensions[s:extension] . " " . a:parsed_input[s:ARGS]
" Parameters:
" list parsed_input [string file, string name, string extension, string args]
" Returns:
" string the execute command of the parsed_input if executable
" "" if not executable
if has_key(g:executioner#names, a:parsed_input[s:FILE])
let s:command = g:executioner#names[a:parsed_input[s:FILE]]
\ . a:parsed_input[s:ARGS]
elseif has_key(g:executioner#extensions, a:parsed_input[s:EXTENSION])
let s:command = g:executioner#extensions[a:parsed_input[s:EXTENSION]]
\ . a:parsed_input[s:ARGS]
else
return ""
let s:command = ""
endif

" Substitute symbols
let s:command = s:Substitute(s:command, g:executioner#base_name,
\ s:parsed_input[s:NAME])
let s:command = s:Substitute(s:command, g:executioner#full_name,
\ a:parsed_input[s:FILE])
return s:command
endfunction

function! s:Substitute(string, old, new) abort
" Substitute characters of s:executioner#curent_name with current name
" Substitute characters of old with strings of new
" Parameters:
" string string to substitute characters
" char old - to substitute with new
" string new - to substitute old
" Returns:
" string with substituted characters
let s:new_string = ""
for i in range(len(a:string))
if a:string[i] == a:old
let s:new_string .= a:new
else
let s:new_string .= a:string[i]
endif
let s:new_string .= a:string[i] == a:old ? a:new : a:string[i]
" if a:string[i] == a:old
" let s:new_string .= a:new
" else
" let s:new_string .= a:string[i]
" endif
endfor
return s:new_string
endfunction

function! s:ParseInput(file_with_args) abort
" Parses the input into an executable command
" Parses the input into its components
" Parameters:
" string file_with_args - file name, optionally followed by arguments
" Returns:
" the command to execute
" list [string file, string name, string extension, string arguments]

" If no arguments supplied, then there is no command to execute.
" If no arguments supplied, then there is nothing to parse
if len(a:file_with_args) == 0
return ""
return ["", "", ""]
endif

" Split the arguments into terms to extract the file name and extension.
let s:input_list = split(a:file_with_args)

" If the first term (file name) is the file symbol,
let s:file = s:input_list[0] == g:executioner#file_symbol ?
\ expand("%") : s:input_list[0]
let s:file_name = split(s:file, '\.')[0]
let s:arguments = ""
if len(s:input_list) > 1
for arg in s:input_list[1:]
" s:arguments = s:arguments . " " . arg
let s:arguments .= " " . s:Substitute(arg, g:executioner#name_symbol, s:file_name)
endfor
endif
return [s:file, s:arguments]
let s:input_terms = split(a:file_with_args)

" If the first term (file name) is the file symbol, then the file being ran
" is the current buffer. Otherwise, the first term is the full name of the
" file to be ran.
let s:file_with_extension = s:input_terms[0] == g:executioner#full_name ?
\ expand("%") : s:input_terms[0]

" Split the file into its name and extension.
let s:file = s:SplitNameAndExtenstion(s:file_with_extension)

" Remaining terms are arguments
" Join all arguments back together with spaces
let s:arguments = len(s:input_terms) > 1 ?
\ " " . join(s:input_terms[1:], " ") : ""

return [s:file_with_extension, s:file[0], s:file[1], s:arguments]
endfunction

function! s:GetSplitPrefix(split_type) abort
Expand Down Expand Up @@ -240,6 +272,7 @@ function! s:SaveAndExecuteFile(...) abort

" If no arguments after split type are specified, assume the current file
" is being ran with no arguments.
" Otherwise, assume the first argument is the file to be ran.
let s:file_with_args = a:0 > 1 && a:2 != "" ? a:2 : expand("%")

" DEBUG
Expand Down Expand Up @@ -275,26 +308,37 @@ function! s:SaveAndExecuteFile(...) abort
let s:final_command = s:split_prefix . s:execute_command

" Finally execute command
call s:ExecuteCommand(s:split_type, s:final_command, s:file_name)
call s:ExecuteCommand(s:split_type, s:final_command, s:parsed_input[s:FILE])
endfunction

function! g:Debug(...) abort
let s:file_with_args = a:0 > 1 && a:2 != "" ? a:2 : expand("%")
" function! g:Debug(...) abort
" " let s:file_with_args = a:0 > 1 && a:2 != "" ? a:2 : expand("%")

let s:parsed_input = s:ParseInput(s:file_with_args)
let s:execute_command = s:GetExecuteCommand(s:parsed_input)
" " let s:parsed_input = s:ParseInput(s:file_with_args)
" " let s:execute_command = s:GetExecuteCommand(s:parsed_input)
" let s:in = "% -debug % @"
" " echom s:execute_command
" let s:parsed_input = s:ParseInput(s:in)

" echom "file: ". s:parsed_input[s:FILE]
" echom "name: ". s:parsed_input[s:NAME]
" echom "extension: ". s:parsed_input[s:EXTENSION]
" echom "args: ". s:parsed_input[s:ARGS]

" let s:command = s:GetExecuteCommand(s:parsed_input)

" echom "command: " . s:command

echom s:execute_command
" let s:file_name = split("test.py", '.')[0]
" echom s:file_name[0]
" let s:parsed_input = s:ParseInput("test.py a1 --a2 -a3")
" echom "file_name: \"" . s:parsed_input[s:NAME] . "\""
" echom "args: \"" . s:parsed_input[s:ARGS] . "\""
" echom "extension: \"" . s:GetExtension(s:parsed_input[s:NAME]) . "\""
" echom "execute_command: \"" . s:GetExecuteCommand(s:parsed_input) . "\""
endfunction
" endfunction

nnoremap <leader>d :call g:Debug(2, "test.cpp")<CR>
" nnoremap <leader>d :call g:Debug(2, "test.cpp")<CR>

" Create commands
command! -nargs=* Executioner :call s:SaveAndExecuteFile(s:NONE, <q-args>)
Expand Down

0 comments on commit 7ba1ca0

Please sign in to comment.