Portkey allows you move to around files in your project quickly. It is a
port of the rails-projections
feature of vim-rails written in
Riml. It works especially well if your project is well organized
with common patterns of moving between files.
At the root of your project you create a file called portkey.json
.
In this file you describe the file structure of your project and the
relations between files. Portkey uses this file to provide mappings and
Ex commands to allow you jump to and between these files.
The format of this file is similar to that used by vim-rails's
projections.json, with minor enhancements. A sample portkey.json
looks
like below,
{
"app/models/*.rb": {
"type": "model",
"alternate": "tests/models/%s_test.rb"
}
}
The above portkey declares a model
and it's alternate
. This allows
you to navigate to any model that matches this pattern with the Ex
command, :Emodel
.
There are a few different ways to switch to a different file, depending on whether you need to open a file related to the current file or to search for a specific project file.
Alternates are provided with the Ex command, :A
.
To jump to an alternate file you first need to specify an alternate in the portkey.json.
{
"app/models/*.js": {
"type": "model",
"alternate": ["app/fixtures/%s.js"]
}
}
Now in any model hitting :A
will switch to the fixture for .
that model From the post
model this command will take you to .
app/fixtures/post_fixture.js
.
The default alternate
for a file is it's test
if specified. To jump
to a test file you specify a test
in the portkey.json
{
"app/models/*.js": {
"type": "model",
"alternate": "app/fixtures/%s_fixture.js",
"test": "test/models/%s_test.js"
}
}
Here the files app/models/author.js
would have 2 alternates,
author_fixture.js
and author_test.js
You can specify more than one file as an alternate
or test
. The
file with the best match will be switched to by default. You can use
<count>
to jump to a specific alternate.
For instance, :2A
will jump to the second alternative, and so on.
Similar to alternate files you can use the Ex command :R
to jump to a.
file related to the current file For instance to jump to helpers from .
controllers, You specify a related
for a projection pattern like .
below .
{
"app/controllers/*_controller.js": {
"type": "controller",
"related": "app/helpers/%s_helper.js"
}
}
Multiple related
files can be specified using an array instead of a
string. And <count>
(Eg:- :2R
) can be used to jump to a specific
related
file.
By declaring a projection in the portkey.json
with a type
, you
create a resource
command of that name. These commands are of the form
:(E|S|V|T|D){resource_name}
. They provide support for auto-completion
to filter the exact file to open.
Eg:- a model
gets the resource commands, Emodel
, Smodel
, etc.
The variants available and their operations are listed below.
Variant | Operation |
---|---|
:E | open in current buffer |
:V | open in vertical split |
:S | open in horizontal split |
:T | open in new tab |
:D | read contents of file into current buffer |
The alternate
, related
, and resource
commands can all be used to
create new files with an additional bang
character !
at the end of
the Ex command. The new files are created in the directory as specified
by your resource.
:Emodel category!
creates a new file app/models/category.js
as per
your projection pattern.
The alternate
and related
Ex commands also support file creation
with bang.
If your model has a related
of fixture, then :R!
from post.rb
will
create a new file post_fixture.rb
.
Similarly given a model with an alternate test, :A!
from author.rb
will create a new file author_test.rb
.
Note: The directory of the resource will be created automatically if it
doesn't exist. This feature relies on mkdir
, and may not work if your
Vim does not support mkdir
.
Projections can include a template
key to specify initial boilerplate
for that resource type. This template language is identical to that
used to specify alternate
and other relations. It supports the same
modifiers
and placeholders
.
In addition \n
converts to newlines. And \t
to tabs or spaces based
on the current tab settings.
{
"app/models/*.js": {
"type": "model",
"template": "%S = DS.Model.extend({\n\t\n});"
}
}
Here the template
will provide boilerplate for a new ember-data
model.
Note: This feature requires CtrlP to be installed. A
<LocalLeader>
key must also be set. You can set a <LocalLeader>
like
below,
let g:maplocalleader = ';'
In addition to the resource Ex commands Portkey provides an even faster
way to search and open a specific file. It adds a custom CtrlP menu
to allow opening files with CtrlP's fuzzy matching feature. A short
custom mapping(Eg:- ;m
for models) opens this menu.
These mappings begin with a <LocalLeader>
like <LocalLeader>m
for
models. Given a <LocalLeader>
semicolon, this would give you the
normal mode mapping, ;m
. Typing in a few keys to match on the model's
name filters the list and gives you the file to open.
Hitting enter opens the file in the current buffer. Additionally you
have access to CtrlP's mappings like Ctrl-s
or Ctrl-v
to open in a
horizontal or vertical split respectively.
This feature is best explained with an example. Given a project with
Model-View-Controllers stored in app/models, app/controllers, app/views.
You can describe this in a portkey.json
as,
{
"app/models/*.js": {
"type": "model"
},
"app/controllers/*.js": {
"type": "controller"
},
"app/views/*.js": {
"type": "view"
}
}
This will give you the mappings,
Mapping | Resource |
---|---|
<LocalLeader>m |
model |
<LocalLeader>c |
controller |
<LocalLeader>v |
view |
Hitting any of these mappings will open up a CtrlP finder which can be used to fuzzy match on that resource type.
With a number of overlapping resource names these mappings can get
long. Eg: component
and controller
, would become comp
and cont
respectively.
Portkey tries to shorten the mappings to 2 characters when possible.
Above, component
and controller
would be shortened as cm
and cn
respectively.
You can override the default mapping for a resource by using the
mapping
key.
{
"app/controllers/*.js": {
"type": "controller",
"mapping": "c"
},
"app/components/*.js": {
"type": "component",
"mapping": "n"
}
}
Note: Conflict handling is left to the user when custom mappings are used.
To view the available mappings for a project use,
<LocalLeader><LocalLeader>
or the Ex command
:PortkeyMappings
Portkey adds a custom includeexpr
to provide custom gf
searching.
The default Get File
finder tries different variants of the word under
the cursor with patterns inside your portkey.json.
The default finder can find filenames directly mapping on to resources.
Eg:- Hitting gf
on the word Container
will take you to the resource
matching container
.
This feature is meant to be augmented by Portkey extensions. Eg:- gf
on a line with an import
statement would go to the imported file.
The exact behaviour that matches such an import
statement varies and
hence is left to extensions to implement.
Portkey provides basic re-factoring similar to vim-rails
's
:Rextract
. The difference is Portkey's extraction is done by providing
a range
to it's resource commands.
Every Resource command can take an optional <range>
prefix. Visual
mode ranges are also supported.
:5,10Emodel comment
Here, Portkey grabs the lines 5-10 and creates a new model comment
and
puts these lines into it and deletes the lines in the original file. If
the target file already exists, the contents are appended to the file.
The base implementation doesn't write anything back into the source
of the extraction, like an include
statement. Nor does it wrap the
contents inside say a class
body.
Extensions may augment this feature to do this wrapping. The base
implementation is equivalent to a cut-and-paste
operation across
files.
You can run tests for the currently open file using :PortkeyRunner. This
commands expects you to declare a compiler
attribute equal to the Vim
compiler plugin to use to run the test.
{
"app/models/*.js": {
"type": "model",
"compiler": "jasmine"
}
}
Then using,
:PortkeyRunner
or it's alias,
:Run
will run the current file or it's corresponding test against Jasmine's Vim compiler plugin. If the test has errors they are opened in a quickfix window.
Note: Resource type
names ending in test
or spec
are considered
tests.
A Projection
is a json object describing a file pattern and it's
corresponding relations. At the very least it must have a pattern
and
a type
.
{
"app/controllers/*_controller.rb": {
"type": "controller"
}
}
Note: type
must be a valid Ex command name. Avoid underscores and
other characters that are invalid as Ex command names.
Patterns should contain a *
which acts as a placeholder for the
filename. This placeholder name can be used as %s
or %{source}
in
the relation templates to describe the file to switch to.
Pattern | Match Type | Example Pattern | Filename | %s |
---|---|---|---|---|
* |
non-recursive match | app/models/*.js |
app/models/post.js |
post |
** |
recursive match | app/models/**.js |
app/models/admin/post.js |
admin/post. |
Note: Recursive matches can be slow depending on how deep the search needs to be. Avoid recursive matching of unrelated resources unless you are certain that the directory structure isn't very deep. Instead create additional projections for each pattern inside the folder, whenever possible.
The relations of a file are described with the keys, alternate
,
related
and test
. These keys can be a single string or any array of
strings. The contents of this string determine how the original file is
transformed into the related file.
For the pattern app/models/*.rb
, with a file, app/models/post.rb
,
the matched source
variable would be post
.
This string template can contain modifiers
and placeholders
to
transform the source name.
Supported modifiers
Modifier | Transformation |
---|---|
%s | matched source name |
%S | camelcased source |
%p | pluralized source |
%i | singular source |
%h | humanized source |
Placeholders allow additional customization of the source name.
The syntax for placeholders is, %{source|filter1|filter2}
%{source|underscore|camel}
Here, the source is first underscorized then camelized.
The affinity
key can be used to perform a transformation from the
source name to the target filename. For instance, given a controllers
names in plural like posts_controller
. If you wish to switch to
singular
models from such a controller, you can use affinity
of
model
to change the source name posts
into it's singular form,
post
, using the affinity of model
.
{
"app/controllers/%s_controller.rb": {
"type": "controller",
"affinity": "model",
"alternate": "app/models/%s.rb"
}
}
Affinity can take the following values,
Affinity | Transformation |
---|---|
model | singularize |
collection | pluralize |
When affinity is absent, no transformation is applied to the source name.
To edit the portkey for a project. Use,
:OpenPortkey
or it's alias,
:PK
The json loader used by Portkey cannot identify errors like missing commas. It is recommended that you install Syntastic with jsonlint to quickly identify such errors. Portkey will only report a basic failure message if it is unable to load or parse the portkey.json file.
When a portkey.json file is modified it's projections are reloaded automatically. You can also manually reload with,
:PortkeyRefresh[!]
Without bang it only clears the opened buffers. Reopening the buffer
will rematch it against the loaded projections. With bang the entire
project's context is reloaded, including it's portkey.json
and any
included extensions.
-
portkey_autostart
Opening Vim inside a project with a
portkey.json
starts Portkey automatically. Without this you have to first open a buffer inside that project.Default: 1 (true)
-
portkey_adaptive_mappings
Whether to use
<LocalLeader>
mappings. You will also need CtrlP installed and amaplocalleader
assigned for this feature to work.Default: 1 (true)
-
portkey_warn_on_mapping_conflicts
If you have custom
<LocalLeader>
mappings this option allows you to show warnings if portkey's<LocalLeader>
mappings conflict with yours. Conflicting mappings must be resolved using themapping
key.Default: 1 (true)
Portkey can be augmented by extensions especially for frameworks that
have a predictable folder layout. An extension can be loaded in a
portkey.json
by using the portkeys
attribute.
{
"portkeys": ["ember"]
}
This will load the emberjs extension and all it's projections.
Things extensions can do,
- Add custom finders and rankers
- Add custom template filters
- Configure defaults for projections
- Add custom extractors
Portkey requires at least Vim 7.3 patch97 or higher. For earlier versions of Vim the plugin will be disabled. The following Vim extensions are recommended but optional.
Recommended configuration:
- Vim 7.4
- CtrlP - Highly Recommended. Without CtrlP, adaptive mappings will be disabled.
- Syntastic + jsonlint - For detecting errors when editing portkey.json
- vim-json - Improved syntax highlighting and code folding of json
1. With Vundle
Bundle 'dsawardekar/portkey'
2. With Pathogen
git clone https://github.com/dsawardekar/portkey ~/.vim/bundle/portkey
Portkey is a work-in-progress. Following are some of the things to be done.
- Implement vim-rails's jumps feature
- Test on Windows
- Document the Extension API
Document Affinity- Document Placeholders
What's a Portkey?
Magical means of transportation. Takes you where you need to go, fast!
- Darshan Sawardekar @_dsawardekar
This project couldn't have been possible without the support of the following people. Many thanks for your kindness and generosity.
- Luke Gruber - for Riml! I don't think I would have attempted this plugin without it.
- Tim Pope - For vim-rails and for taking over my vimrc.
- Kien - For CtrlP, without it Portkey would probably be called Floo Powder!
- Abdul Qabiz - For opening my eyes to the world of Vim plugins.
Portkey is developed almost entirely in Riml. The files in plugin
and autoload
folders are compiled files. The source files live in the
lib directory.
Pull Requests should be against source code not the compiled files. The compiled files are auto generated before a release. Further the project uses git-flow based branching model. Pull requests should go against the develop branch.
Portkey uses Speckle for testing. PR's with tests are preferred.
MIT License. Copyright © 2013 Darshan Sawardekar.