Hi, it's Takuya.
I'm developing a Markdown note-taking app called Inkdrop.
It runs smoothly on both desktop and mobile - macOS, Windows, Linux, iOS and Android.
That's because my app is built with Electron for desktop, and React Native for mobile.
So, I write them basically in JavaScript.
In this article, I'd like to share my JavaScript coding workflow using vim.
I don't use any IDE like VSCode but I prefer doing things on terminal.
I use Neovim and tmux.
I've written about my basic workflow on terminal here.
I'll explain more about my vim setup.
My dotfiles are published on GitHub here.
Plugins
Here are the plugins that I use:
- dein.nvim for managing plugins
- coc.nvim for autocompletion, imports and type definitions
- defx.nvim for exploring files
- denite.nvim for searching files
- jiangmiao/auto-pairs for inserting brackets, parens, quotes in pair
dein.nvim - Plugin management
To install/update plugins, I'm using dein.nvim.
You can define plugins that you want to use in dein.toml file:
[[plugins]]
repo = 'Shougo/dein.vim'
[[plugins]]
repo = 'Shougo/defx.nvim'
depends = ['defx-git', 'defx-icons']
hook_add = """
source ~/.config/nvim/plugins/defx.rc.vim
"""
[[plugins]]
repo = 'Shougo/denite.nvim'
hook_add = """
source ~/.config/nvim/plugins/denite.rc.vim
"""
[[plugins]]
repo = 'jiangmiao/auto-pairs'
[[plugins]]
repo = "neoclide/coc.nvim"
merge = 0
rev = "release"
hook_add = """
source ~/.config/nvim/plugins/coc.rc.vim
"""
Here is another toml file named dein_lazy.toml:
[[plugins]]
repo = 'elzr/vim-json'
on_ft = ['json']
[[plugins]]
repo = 'yuezk/vim-js'
on_ft = ['javascript', 'javascript.jsx']
[[plugins]]
repo = 'maxmellon/vim-jsx-pretty'
on_ft = ['javascript', 'javascript.jsx']
This file has plugins that are loaded on demand based on file type.
For example, vim-json
plugin is loaded only when you opened a json file.
By doing like that, vim can avoid loading unnecessary plugins.
So, you can keep vim launching quick and running fast.
In this file, I wrote plugins for file types that I often use.
coc.nvim - Intellisense
coc.nvim is Conquer of Completion.
It is the plugin that provides intellisense on your vim environment.
For example, it provides auto-completion, auto import, type definitions, things like IDEs usually support, which is neat.
Let's say you have a TypeScript something like this:
type Note = {
_id: string,
body: string,
title: string,
createdAt: number,
updatedAt: number,
tags: [string]
}
const note: Note = {
_id: 'hige',
body: '# hello',
title: 'example note',
createdAt: 'moji'
}
console.log('note:', note)
So now, createdAt should be number.
But if you mistakenly set a string to the property, then it tells you that it's incorrect:
It tells you should number, not string.
coc.nvim does like this for you.
It also provides auto-completion like this:
As you can see, it helps you by showing tooltips with the type definition.
It also works great for functions.
You have another ts file named 'hoge' which has Book and a function named getThingsDone
.
Then, you want to write getThingsDone
, then, you type getThin
and you got it.
It also shows you the definition and description like this.
Then you say Okay, let's insert it.
Then, it inserts an import statement automatically.
import {getThingsDone} from './hoge' // imported automatically
getThingsDone(hoge)
This is pretty neat. useful.
When you want to see the type definition, I configured shift-K key to show the definition.
When you typed shift-K, it shows you the definition on a tooltip.
So, even if you don't remember the type, it quickly tells you what type is under the cursor.
On top of that, when you don't get it from the tooltip, and you want to see the detail,
press gd, which means 'go to definition'.
Then, you can jump to the location of the type definition.
Type ctrl-o to go back.
If the type definition is in the same file, it just brings your cursor to the definition like so.
So, coc.nvim provides such coding assistances.
It's very powerful and useful. I recommend this.
In this example, I demonstrated with TypeScript but I am basically writing FlowJS for my product.
But coc.nvim also works great with FlowJS.
Let's say, here is the note module of Inkdrop.
As you can see, it works....not as great as TypeScript but it works fine, like GoToDefinition,
auto-completion...hmmm...not so good.
But anyway, it's useful.
To be honest, I want to switch from FlowJS to TypeScript as soon as possible.
But my codebase is huge and it's hard.
So, I reluctantly stay on FlowJS at the moment.
Here is the config for coc.nvim.
The point is extensions here (.config/nvim/plugins/coc.rc.vim
).
I installed 4 extensions.
" Extensions
let g:coc_global_extensions = [
\ 'coc-json',
\ 'coc-tsserver',
\ 'coc-prettier',
\ 'coc-eslint',
\ ]
If you use TypeScript, install coc-tsserver
extension.
And I use a helper extension for json files, prettier, and eslint.
You got another config file. It is coc-settings.json
:
{
"coc.preferences.formatOnSaveFiletypes": ["json", "css", "markdown"],
"eslint.autoFixOnSave": true,
"eslint.autoFix": true,
"tsserver.enableJavascript": false,
"languageserver": {
"flow": {
"command": "flow",
"args": ["lsp"],
"filetypes": ["javascript", "javascriptreact"],
"initializationOptions": {},
"requireRootPattern": true,
"settings": {},
"rootPatterns": [".flowconfig"]
}
},
...
}
If you use flowjs, you have to configure languageserver like this.
Flow can speak languageserver protocol. So, you can use it.
If you use both TypeScript and FlowJS, you have to set "tsserver.enableJavascript": false
, so that you can disable TypeScript when editing js file.
That's it.
defx.nvim - Filer
I open a filer every time I need because I don't like keep showing a file tree on left side of the window.
Then, open a file by choosing from it.
The filer I use is defx.nvim.
I assign it with sf key.
The config file looks like this.
nnoremap <silent>sf :<C-u>Defx -listed -resume
\ -columns=indent:mark:icon:icons:filename:git:size
\ -buffer-name=tab`tabpagenr()`
\ `expand('%:p:h')` -search=`expand('%:p')`<CR>
nnoremap <silent>fi :<C-u>Defx -new `expand('%:p:h')` -search=`expand('%:p')`<CR>
I guess these are copied from readme.
And you can open a file tree like this,
you can explore directory and find a component, things like that.. using vim-like keystrokes.
If you changed a file, it shows 'M' label as you can see here, so it tells you this has been modified.
It is well-built as a filer. I love it.
Of course, you can manage files in the filer.
- Create new file: shift-N
- Delete file: D
- Rename file: R
Fonts
As you may see some icons like JavaScript, folders in my terminal,
That's because I'm using the font called Nerd Fonts.
This font comes with a bunch of icons, as you can see, Font Awesome, Devicons, Weather Icons, Seti UI, and on, and on.
So you can get those icons to display on your terminal.
denite.nvim - Search
To search files in a project, I use denite.nvim.
This plugin itself doesn't provide the search feature but I configure it to do it.
The config is here.
For example, I have a bunch of files in my Inkdrop project.
To search files, press ;f
, then the search window pop up.
When you input keyword like 'editor', it quickly searches files that match the keyword with the filename.
Then you can quickly open it.
If you want to grep file contents with patterns, press ;r
.
If you input a keyword like 'Notebook', it finds files with locations that appear the keyword.
On top of that, you can filter them with keyword by typing, like import
, then you can see only items with keywords.
So, if you've got many files in your project, you can always quickly find files and locations.
Well, it'd be hard to tell how I configured denite.nvim in detail though...
Here is the keymaps for grep and search:
nnoremap <silent> ;r :<C-u>Dgrep<CR>
nnoremap <silent> ;f :<C-u>Denite file/rec<CR>
This Dgrep
command is defined here:
" Ag command on grep source
call denite#custom#var('grep', 'command', ['ag'])
call denite#custom#var('grep', 'default_opts', ['-i', '--vimgrep'])
call denite#custom#var('grep', 'recursive_opts', [])
call denite#custom#var('grep', 'pattern_opt', [])
call denite#custom#var('grep', 'separator', ['--'])
call denite#custom#var('grep', 'final_opts', [])
" grep
command! -nargs=? Dgrep call s:Dgrep(<f-args>)
function s:Dgrep(...)
if a:0 > 0
execute(':Denite -buffer-name=grep-buffer-denite grep -path='.a:1)
else
let l:path = expand('%:p:h')
if has_key(defx#get_candidate(), 'action__path')
let l:path = fnamemodify(defx#get_candidate()['action__path'], ':p:h')
endif
execute(':Denite -buffer-name=grep-buffer-denite -no-empty '.join(s:denite_option_array, ' ').' grep -path='.l:path)
endif
endfunction
What it actually does is to run an external program called ag.
It is a code searching tool that focuses on speed.
Then, the command adds parameters based on context in order to search files.
It's pretty neat.
auto-pairs
Then, speaking of the small thing, I prefer to use auto-pairs plugin.
This plugin is, as its name suggests, it helps insert brackets in pair automatically.
If you type open bracket, it automatically closes the bracket.
It also works with double quotes. Even if you removed characters, it erases the pair of quotes.
It works fine with single quotes, square bracket, curly bracket, and normal bracket as well.
It improves my typing speed.
That's pretty much it!
I hope it's helpful for your terminal workflow.
- Follow me on Twitter & Instagram
- Inkdrop - Markdown note-taking app