/leetcode.nvim

A Neovim plugin enabling you to solve LeetCode problems.

Primary LanguageLuaMIT LicenseMIT

leetcode.nvim

🔥 Solve LeetCode problems within Neovim 🔥

🇺🇸 English, 🇨🇳 简体中文

demo.mp4

Caution

This plugin has been primarily tested with Java. If you encounter any errors while using other languages, please open an issue to report them.

✨ Features

  • 📌 an intuitive dashboard for effortless navigation within leetcode.nvim

  • 😍 question description formatting for a better readability

  • 📈 LeetCode profile statistics within Neovim

  • 🔀 support for daily and random questions

  • 💾 caching for optimized performance

📬 Requirements

📦 Installation

{
    "kawre/leetcode.nvim",
    build = ":TSUpdate html",
    dependencies = {
        "nvim-telescope/telescope.nvim",
        "nvim-lua/plenary.nvim", -- required by telescope
        "MunifTanjim/nui.nvim",

        -- optional
        "nvim-treesitter/nvim-treesitter",
        "rcarriga/nvim-notify",
        "nvim-tree/nvim-web-devicons",
    },
    opts = {
        -- configuration goes here
    },
}

🛠️ Configuration

To see full configuration types see template.lua

⚙️ default configuration

{
    ---@type string
    arg = "leetcode.nvim",

    ---@type lc.lang
    lang = "cpp",

    cn = { -- leetcode.cn
        enabled = false, ---@type boolean
        translator = true, ---@type boolean
        translate_problems = true, ---@type boolean
    },

    ---@type lc.storage
    storage = {
        home = vim.fn.stdpath("data") .. "/leetcode",
        cache = vim.fn.stdpath("cache") .. "/leetcode",
    },

    ---@type table<string, boolean>
    plugins = {
        non_standalone = false,
    },

    ---@type boolean
    logging = true,

    injector = {}, ---@type table<lc.lang, lc.inject>

    cache = {
        update_interval = 60 * 60 * 24 * 7, ---@type integer 7 days
    },

    console = {
        open_on_runcode = true, ---@type boolean

        dir = "row", ---@type lc.direction

        size = { ---@type lc.size
            width = "90%",
            height = "75%",
        },

        result = {
            size = "60%", ---@type lc.size
        },

        testcase = {
            virt_text = true, ---@type boolean

            size = "40%", ---@type lc.size
        },
    },

    description = {
        position = "left", ---@type lc.position

        width = "40%", ---@type lc.size

        show_stats = true, ---@type boolean
    },

    hooks = {
        ---@type fun()[]
        ["enter"] = {},

        ---@type fun(question: lc.ui.Question)[]
        ["question_enter"] = {},

        ---@type fun()[]
        ["leave"] = {},
    },

    keys = {
        toggle = { "q" }, ---@type string|string[]
        confirm = { "<CR>" }, ---@type string|string[]

        reset_testcases = "r", ---@type string
        use_testcase = "U", ---@type string
        focus_testcases = "H", ---@type string
        focus_result = "L", ---@type string
    },

    ---@type lc.highlights
    theme = {},

    ---@type boolean
    image_support = false,
}

arg

Argument for Neovim

---@type string
arg = "leetcode.nvim"

See usage for more info

lang

Language to start your session with

---@type lc.lang
lang = "cpp"
available languages
Language lang
C++ cpp
Java java
Python python
Python3 python3
C c
C# csharp
JavaScript javascript
TypeScript typescript
PHP php
Swift swift
Kotlin kotlin
Dart dart
Go golang
Ruby ruby
Scala scala
Rust rust
Racket racket
Erlang erlang
Elixir elixir
Bash bash

cn

Use leetcode.cn instead of leetcode.com

cn = { -- leetcode.cn
    enabled = false, ---@type boolean
    translator = true, ---@type boolean
    translate_problems = true, ---@type boolean
},

storage

storage directories

---@type lc.storage
storage = {
    home = vim.fn.stdpath("data") .. "/leetcode",
    cache = vim.fn.stdpath("cache") .. "/leetcode",
},

plugins

plugins list

---@type table<string, boolean>
plugins = {
    non_standalone = false,
},

logging

Whether to log leetcode.nvim status notifications

---@type boolean
logging = true

injector

Inject code before or after your solution, injected code won't be submitted or run.

default imports

You can also pass before = true to inject default imports for the language. Supported languages are python, python3, java

Access default imports via require("leetcode.config.imports")

injector = { ---@type table<lc.lang, lc.inject>
    ["python3"] = {
        before = true
    },
    ["cpp"] = {
        before = { "#include <bits/stdc++.h>", "using namespace std;" },
        after = "int main() {}",
    },
    ["java"] = {
        before = "import java.util.*;",
    },
}

hooks

List of functions that get executed on specified event

hooks = {
    ---@type fun()[]
    ["enter"] = {},

    ---@type fun(question: lc.ui.Question)[]
    ["question_enter"] = {},

    ---@type fun()[]
    ["leave"] = {},
},

theme

Override the default theme.

Each value is the same type as val parameter in :help nvim_set_hl

---@type lc.highlights
theme = {
    ["alt"] = {
        bg = "#FFFFFF",
    },
    ["normal"] = {
        fg = "#EA4AAA",
    },
},

image support

Whether to render question description images using image.nvim

Warning

Enabling this will disable question description wrap, because of 3rd/image.nvim#62 (comment)

---@type boolean
image_support = false,

📋 Commands

Leet opens menu dashboard

  • menu same as Leet

  • exit close leetcode.nvim

  • console opens console pop-up for currently opened question

  • info opens a pop-up containing information about the currently opened question

  • tabs opens a picker with all currently opened question tabs

  • yank yanks the current question solution

  • lang opens a picker to change the language of the current question

  • run run currently opened question

  • test same as Leet run

  • submit submit currently opened question

  • random opens a random question

  • daily opens the question of today

  • list opens a problem list picker

  • open opens the current question in a default browser

  • reset reset current question to default code definition

  • last_submit retrieve last submitted code for the current question

  • restore try to restore default question layout

  • inject re-inject code for the current question

  • session

    • create create a new session

    • change change the current session

    • update update the current session in case it went out of sync

  • desc toggle question description

    • toggle same as Leet desc

    • stats toggle description stats visibility

  • cookie

    • update opens a prompt to enter a new cookie

    • delete sign-out

  • cache

    • update updates cache

Some commands can take optional arguments. To stack argument values separate them by a ,

  • Leet list

    Leet list status=<status> difficulty=<difficulty>
    
  • Leet random

    Leet random status=<status> difficulty=<difficulty> tags=<tags>
    

🚀 Usage

This plugin can be initiated in two ways:

  • To start leetcode.nvim, simply pass arg as the first and only Neovim argument

    nvim leetcode.nvim
    
  • (Experimental) Alternatively, you can use :Leet command to open leetcode.nvim within your preferred dashboard plugin. The only requirement is that Neovim must not have any listed buffers open.

Switching between questions

To switch between questions, use Leet tabs

Sign In

It is required to be signed-in to use leetcode.nvim

signin.mp4

🍴 Recipes

💤 lazy loading with lazy.nvim

Warning

opting for either option makes the alternative launch method unavailable due to lazy loading

  • with arg

    local leet_arg = "leetcode.nvim"
    {
        "kawre/leetcode.nvim",
        lazy = leet_arg ~= vim.fn.argv()[1],
        opts = { arg = leet_arg },
    }
  • with :Leet

    {
        "kawre/leetcode.nvim",
        cmd = "Leet",
    }

🧩 Plugins

Non-Standalone mode

To run leetcode.nvim in a non-standalone mode (i.e. not with argument or an empty Neovim session), enable the non_standalone plugin in your config:

plugins = {
    non_standalone = true,
}

You can then exit leetcode.nvim using :Leet exit command

🙌 Credits