This post will walk you through a workflow of creating a new GitHub Issue access Plugin for CoC which can help you add GitHub issues to your Git Commit messages with ease. This post can be used as a reference to customize and extend this basic plugin or create one on your own to integrate with any utility such as JIRA or Rally or BitBucket etc.
The Goal
By the end of this post, you should have a working CoC extension that can be used to pull up a list of GitHub issues assigned to you when you open a GitCommit buffer automatically so that you can add a reference for the issue easily. It will provide the following configurable items.
- Filter only issues assigned to me
- Access Token configuration
- A command to pull the GitHub issues from anywhere in the
vim
buffer if required.
Creating a Skeleton Repo
Like any other node module projects, everything begins with a simple npm init
▲ OpenSource/Node/coc-git-complete npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (coc-git-complete)
version: (1.0.0) 0.1.0
description: GitHub issue Completer for CoC
entry point: (index.js)
test command:
git repository:
keywords: coc, git, github
author: Harsha Narayana
license: (ISC) MIT
About to write to /Users/timelord/Work/OpenSource/Node/coc-git-complete/package.json:
{
"name": "coc-git-complete",
"version": "0.1.0",
"description": "GitHub issue Completer for CoC",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"coc",
"git",
"github"
],
"author": "Harsha Narayana",
"license": "MIT"
}
Is this OK? (yes) yes
▲ OpenSource/Node/coc-git-complete tree
.
└── package.json
▲ OpenSource/Node/coc-git-complete yarn generate-lock-entry
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
coc-git-complete@0.1.0:
version "0.1.0"
▲ OpenSource/Node/coc-git-complete tree
.
├── node_modules
├── package.json
├── yarn-error.log
└── yarn.lock
1 directory, 3 files
Setup CoC configuration
Add the following section to your package.json
that was generated in the above step. These configurations will ensure your plugin can integrate
well with CoC
"engines": {
"coc": ">=0.0.57"
}
Setup CoC activation and Contributions
CoC uses the package.json
to decide what are the configurable parameters for the given extension. It uses a key in package.json
named contributes
to include a list of supported commands
and configuration
entries.
These configuration
entries can be used to customize the behavior of the plugin at the runtime and the commands
can be used with custom
vim
keybinding to perform actions on request.
For the use-case in question, we will work on enabling the following.
- A command named
gh.issues
- GitHub token Setup
- List of Repositories to watch for
- Configuration to enable filters to fetch only issues assigned to me
"contributes":{
"commands":[
{
"title":"Fetch a list of GitHub issues",
"category":"gh",
"command":"gh.issues"
}
],
"configuration":{
"type":"object",
"properties":{
"gh.user": {
"type": "string",
"default": "",
"description": "GitHub User Name"
},
"gh.token":{
"type":"string",
"default":"",
"description":"GitHub Access token to use for fetching Issues"
},
"gh.mineOnly":{
"type":"boolean",
"default":true,
"description":"Filter only those GitHub issues assigned to me"
},
"gh.repos":{
"type":"array",
"default":[
],
"description":"List of GitHub Repositories to watch. Used only if gh.mineOnly is false",
"items":{
"type":"string"
}
}
}
}
}
We need to now configure the activationEvents
in order to indicate CoC
that it needs to activate the plugin for those conditions.
For our current usecase, the plugin needs to be activate in two cases.
- When you are in Git commit buffer
- When you invoke a command for
gh.issues
"activationEvents":[
"onLanguage:gitcommit",
"onCommand:gh.issues"
]
Setup Basic Integration Dependencies
npm install --save coc.nvim
npm install --save-dev prettier
npm install --save @octokit/rest
Create Base Activation Setup
const { sources, workspace, commands } = require("coc.nvim"); // Import Base configuration for CoC
// This method is used to actually interface with GitHub using the octokit/rest.js api to fetch
// a list of issues
const fetchGitHubIssues = (token, repos, username, mineOnly) => {
workspace.showMessage(
"[coc-git-complete] Inside fetchGitHubIssues method",
"more"
);
};
// This method is invoked when `CocCommand: gh.issues` is run from the current `vim` buffer
async function listIssuesCommand(token, repos, username, mineOnly) {
workspace.showMessage(
"[coc-git-complete] Inside listIssuesCommand method",
"more"
);
}
exports.activate = async (context) => {
// fetch configuration for the extension from CocConfig
const config = workspace.getConfiguration("gh");
// Extract Config Dict into respective components.
const token = config.get("token");
const repos = config.get("repos");
const username = config.get("username");
const mineOnly = config.get("mineOnly");
// Ensure that the User GitHub token is configured. Without that, this plugin is as
// useless as Thanos' henchmen.
if (!token) {
workspace.showMessage(
"GitHub Token configuration missing. Please run :CocConfig to setup access Token",
"warning"
);
return;
}
let issues = [];
try {
issues = await fetchGitHubIssues(token, repos, username, mineOnly);
} catch (error) {
workspace.showMessage(
"[coc-git-complete] Failed to fetch GitHub issus. Check :CocOpenLog for details.",
"error"
);
console.error("Failed to fetch GitHub issues due to ", error);
}
let source = {
name: "git-complete",
triggerOnly: false,
doComplete: async () => {
return {
items: issues.map((issue) => {
// This section defines what shows up in your neovim auto complete dropdown
// and what gets inserted into the buffer when you hit enter. `abbr` is what
// shows up on the dropdown and `word` is what goes into the buffer.
return {
word: `${issue.gid}: ${issue.description}`,
abbr: `${issue.gid}: ${issue.description}`,
};
}),
};
},
};
// Register Resources and command handlers to be managed by CoC.nvim
context.subscriptions.push(sources.createSource(source));
context.subscriptions.push(
commands.registerCommand("gh.issues", () =>
listIssuesCommand(token, repos, username, mineOnly)
)
);
};
Enable Debug Mode
With a simple configuration of vim
you can enable the Debug options for the coc
and test your plugin as you develop.
Put the following line at the top of your nvim/init.vim
set runtimepath^=<path_to_your_repo_base>/coc-git-complete"
Testing your Plugin’s Default Behavior
Once you have the above configuration setup, open vim
and you will notice something interesting when you run the command :CocCommand gh.issues
Two things to consider here.
- The Warning about
gh.token
not being configured gh.issues
command not being found.
If you look at the code above, there is clear indication that the command will never get registered in case if the gh.token
configuration
is missing on the CocConfig
file.
Fixing this is as simple as running :CocConfig
in your neovim buffer and then setting the gh.token
to a valid github token.
Now that we have the basic things running, all you need to do is setup right interaction with GitHub to fetch a list of Issues and show them in the buffer as required.
Enable Git Integration
const { Octokit } = require("@octokit/rest");
const fetchGitHubIssues = (token, repos, username, mineOnly) => {
const octokit = new Octokit({
auth: token,
});
return octokit.issues.list().then((issues) =>
issues.data.map((issue) => ({
gid: issue.number,
description: issue.title,
}))
);
};
That is just about it. Now, open a new neovim
buffer and start listing all your GitHub Issues.
As you can see, this is not really good enough. There is no easy way to find out what issue is from what Repo. Let us fix that.
// Tune the return values a bit to include the repo.
return octokit.issues.list().then((issues) =>
issues.data.map((issue) => ({
repo: issue.repository.name,
gid: issue.number,
description: issue.title,
}))
);
// Configure the Abbreviation display to include repo name.
let source = {
name: "git-complete",
triggerOnly: false,
doComplete: async () => {
return {
items: issues.map((issue) => {
return {
word: `${issue.gid}: ${issue.description}`,
abbr: `${issue.repo} -> ${issue.gid}: ${issue.description}`,
};
}),
};
},
};
Now that you have all the basic things you need to know for you to get your custom CoC
extension up an running, have fun and don’t forget to
drink some coffee when you are at it. Please go ahead and implement the repository filtering support for which the configuration is already
pre-enabled in this example. I would love to see what magic you guys end up creating for CoC
and neovim
.
You can find a link to the GitHub repository below to get your started.