A Neovim plugin that integrates GitLab LSP with OAuth device flow authentication for seamless GitLab Duo code suggestions.
- 🔐 OAuth Device Flow Authentication - Secure authentication without exposing tokens
- 🔄 Automatic Token Refresh - Handles token expiration transparently
- 🚀 GitLab Duo Code Suggestions - AI-powered code completions
- 🎯 Multi-language Support - Works with Ruby, Go, JavaScript, TypeScript, Rust, Lua, Python, and more
- 💾 Token Persistence - Authenticate once, works across sessions
- 🛠️ Easy Configuration - Works out of the box with sensible defaults
- 🔌 Completion Engine Integration - Works seamlessly with nvim-cmp, blink.cmp, and native LSP completion
- Neovim 0.11.0 or later (uses the new
vim.lsp.configAPI) - plenary.nvim
- Node.js and npm/npx (for running
@gitlab-org/gitlab-lsp) - GitLab account with Duo Pro/Enterprise license
Using lazy.nvim
return {
'https://gitlab.com/tachyons-gitlab/gitlab-lsp',
dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
require('gitlab-lsp').setup({
-- Optional: override default settings
gitlab_url = 'https://gitlab.com', -- or your self-hosted instance
filetypes = { 'ruby', 'go', 'javascript', 'typescript', 'rust', 'lua', 'python' },
log_level = 'info', -- 'debug', 'info', 'warn', 'error'
})
end
}Alternative shorter format:
{
'gitlab.com/tachyons-gitlab/gitlab-lsp',
dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
require('gitlab-lsp').setup()
end
}Using packer.nvim
use {
'https://gitlab.com/tachyons-gitlab/gitlab-lsp',
requires = { 'nvim-lua/plenary.nvim' },
config = function()
require('gitlab-lsp').setup()
end
}# Clone the repository
git clone https://gitlab.com/tachyons-gitlab/gitlab-lsp.git \
~/.local/share/nvim/site/pack/plugins/start/gitlab-lsp
# Install plenary.nvim if not already installed
git clone https://github.com/nvim-lua/plenary.nvim.git \
~/.local/share/nvim/site/pack/plugins/start/plenary.nvimThen add to your init.lua:
require('gitlab-lsp').setup()-
Install the plugin using your preferred method above
-
Authenticate with GitLab:
:GitLabAuth
-
Follow the prompts:
- Open the URL shown in your browser
- Enter the device code
- Authorize the application
-
Start coding! The LSP will automatically start and provide code suggestions
blink.cmp works automatically with GitLab LSP. Simply install both:
return {
{
'https://gitlab.com/tachyons-gitlab/gitlab-lsp',
dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
require('gitlab-lsp').setup()
end
},
{
'saghen/blink.cmp',
dependencies = 'rafamadriz/friendly-snippets',
version = 'v0.*',
opts = {
keymap = { preset = 'default' },
appearance = {
use_nvim_cmp_as_default = true,
nerd_font_variant = 'mono'
},
sources = {
default = { 'lsp', 'path', 'snippets', 'buffer' },
},
},
}
}return {
{
'https://gitlab.com/tachyons-gitlab/gitlab-lsp',
dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
require('gitlab-lsp').setup()
end
},
{
'hrsh7th/nvim-cmp',
dependencies = {
'hrsh7th/cmp-nvim-lsp',
'hrsh7th/cmp-buffer',
'hrsh7th/cmp-path',
},
config = function()
local cmp = require('cmp')
cmp.setup({
sources = cmp.config.sources({
{ name = 'nvim_lsp' },
{ name = 'buffer' },
{ name = 'path' },
}),
mapping = cmp.mapping.preset.insert({
['<C-Space>'] = cmp.mapping.complete(),
['<CR>'] = cmp.mapping.confirm({ select = true }),
}),
})
end
}
}GitLab LSP works with Neovim's built-in completion. Enable it with:
-- In your init.lua
vim.opt.completeopt = {'menu', 'menuone', 'noselect'}
-- Trigger completion manually with <C-x><C-o> in insert mode
-- Or set up automatic completion
vim.api.nvim_create_autocmd('TextChangedI', {
pattern = '*',
callback = function()
if vim.fn.pumvisible() == 0 then
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<C-x><C-o>', true, true, true), 'n')
end
end
})require('gitlab-lsp').setup({
-- GitLab instance URL
gitlab_url = "https://gitlab.com",
-- OAuth client ID (default is provided, but you can use your own)
client_id = "5f1f9933c9bff0a3e908007703f260bf1ff87bcdb91d38279a9f0d0ddecceadf",
-- OAuth scopes
scopes = "api",
-- Token storage location
token_file = vim.fn.stdpath('data') .. '/gitlab_oauth_token.json',
-- Supported file types
filetypes = { "ruby", "go", "javascript", "typescript", "rust", "lua", "python" },
-- Feature flags
stream_code_generations = false,
enable_secret_redaction = true,
-- Log level: 'debug', 'info', 'warn', 'error'
log_level = "info",
})For self-hosted GitLab instances:
require('gitlab-lsp').setup({
gitlab_url = 'https://gitlab.yourcompany.com',
client_id = 'your_oauth_app_client_id', -- Create in your GitLab instance
})To use your own OAuth application:
- Go to GitLab → Preferences → Applications
- Create a new application:
- Name: Neovim GitLab LSP
- Redirect URI:
http://localhost(not used for device flow) - Scopes: Select
api - Confidential: No (for device flow)
- Copy the Application ID
- Configure the plugin:
require('gitlab-lsp').setup({
client_id = 'your_application_id_here',
})| Command | Description |
|---|---|
:GitLabAuth |
Start OAuth device flow authentication |
:GitLabTokenInfo |
Display token status and expiration time |
:GitLabRefreshToken |
Manually refresh the access token |
:GitLabStart |
Manually start the LSP server |
:GitLabLogout |
Remove stored token and stop LSP |
Add to your init.lua:
vim.keymap.set('n', '<leader>ga', ':GitLabAuth<CR>', { desc = 'GitLab Auth' })
vim.keymap.set('n', '<leader>gi', ':GitLabTokenInfo<CR>', { desc = 'GitLab Token Info' })
vim.keymap.set('n', '<leader>gr', ':GitLabRefreshToken<CR>', { desc = 'GitLab Refresh Token' })
vim.keymap.set('n', '<leader>go', ':GitLabLogout<CR>', { desc = 'GitLab Logout' })-
Device Flow Authentication:
- User runs
:GitLabAuth - Plugin requests a device code from GitLab
- User opens the verification URL and enters the code
- Plugin polls GitLab until authorization is complete
- Access token and refresh token are saved locally
- User runs
-
Token Management:
- Tokens are stored in
~/.local/share/nvim/gitlab_oauth_token.json - Access tokens are valid for 2 hours
- Plugin automatically refreshes expired tokens using the refresh token
- LSP is updated with new tokens without restart
- Tokens are stored in
-
LSP Integration:
- Plugin configures and starts the GitLab LSP server
- Listens for token validation errors from the server
- Automatically handles token refresh when needed
" Check if you have network access to GitLab
:GitLabAuth
" Enable debug logging
:lua require('gitlab-lsp').config.log_level = 'debug'
" Check messages for detailed errors
:messages" Check if token exists
:GitLabTokenInfo
" Manually start LSP
:GitLabStart
" Check LSP status
:LspInfo- Ensure you have a valid GitLab Duo license
- Check that your project is not excluded from Duo features
- Verify the file type is supported:
:echo &filetype
- Check LSP logs:
:lua vim.cmd('edit ' .. vim.lsp.get_log_path())
The plugin automatically refreshes tokens, but if you encounter issues:
" Manually refresh
:GitLabRefreshToken
" Or re-authenticate
:GitLabLogout
:GitLabAuthContributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- GitLab for providing the GitLab LSP and Duo AI features
- Neovim for the excellent LSP support
- plenary.nvim for async utilities
- GitLab Workflow - Official GitLab VS Code extension
- gitlab.nvim - GitLab integration for Neovim
- gitlab-lsp - Official GitLab Language Server
- For plugin issues: Open an issue
- For GitLab LSP issues: GitLab LSP Issues
- For GitLab Duo support: GitLab Documentation