{"id":22899593,"url":"https://github.com/agda/cornelis","last_synced_at":"2025-12-11T23:28:36.453Z","repository":{"id":37807780,"uuid":"452097973","full_name":"agda/cornelis","owner":"agda","description":"agda-mode for neovim","archived":false,"fork":false,"pushed_at":"2025-11-03T10:42:23.000Z","size":12692,"stargazers_count":171,"open_issues_count":24,"forks_count":24,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-12-07T21:52:23.216Z","etag":null,"topics":["agda","neovim","nvim-hs","vim-textobj-user","vim-which-key"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/agda.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-01-26T01:12:46.000Z","updated_at":"2025-12-06T04:42:38.000Z","dependencies_parsed_at":"2024-05-15T19:31:51.110Z","dependency_job_id":"873ee152-da72-4ad2-8a7e-37a0a7734ea5","html_url":"https://github.com/agda/cornelis","commit_stats":null,"previous_names":["agda/cornelis"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/agda/cornelis","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agda%2Fcornelis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agda%2Fcornelis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agda%2Fcornelis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agda%2Fcornelis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/agda","download_url":"https://codeload.github.com/agda/cornelis/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agda%2Fcornelis/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27672064,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-11T02:00:11.302Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agda","neovim","nvim-hs","vim-textobj-user","vim-which-key"],"created_at":"2024-12-14T01:00:50.352Z","updated_at":"2025-12-11T23:28:36.448Z","avatar_url":"https://github.com/agda.png","language":"Haskell","funding_links":[],"categories":["Haskell"],"sub_categories":[],"readme":"# cornelis\n\n![Cornelis in Action](https://raw.githubusercontent.com/isovector/cornelis/master/cast.gif)\n\n\n## Dedication\n\n\u003e I'll ask to stand up \\\n\u003e With a show about a rooster, \\\n\u003e Which was old and worn out, \\\n\u003e Impotent and weathered. \\\n\u003e The chickens complained and whined \\\n\u003e Because he did not satisfy them.\n\u003e\n\u003e -- [Cornelis Vreeswijk](https://www.youtube.com/watch?v=oKUscEWPVAM)\n\n\n## Overview\n\n`cornelis` is [agda-mode], but for neovim. It's written in Haskell, which means\nit's maintainable and significantly less likely to bit-rot like any\nvimscript/lua implementations.\n\n[agda-mode]: https://agda.readthedocs.io/en/latest/tools/emacs-mode.html\n\n## Features\n\nIt supports highlighting, goal listing, type-context, refinement, auto, solving,\ncase splitting, go-to definition, normalization, and helper functions. These are\nexposed via vim commands.  Most commands have an equivalent in [agda-mode].\n\n### Global commands\n\n| Vim command | Description | Equivalent agda-mode keybinding |\n| :--- | :--- | :--- |\n| `:CornelisLoad`             | Load and type-check buffer | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-l\u003c/kbd\u003e |\n| `:CornelisGoals`            | Show all goals        | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-?\u003c/kbd\u003e |\n| `:CornelisRestart`          | Kill and restart the `agda` process | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-x\u003c/kbd\u003e\u003ckbd\u003eC-r\u003c/kbd\u003e |\n| `:CornelisAbort`            | Abort running command | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-x\u003c/kbd\u003e\u003ckbd\u003eC-a\u003c/kbd\u003e |\n| `:CornelisSolve \u003cRW\u003e`       | Solve constraints     | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-s\u003c/kbd\u003e |\n| `:CornelisGoToDefinition`   | Jump to definition of name at cursor | \u003ckbd\u003eM-.\u003c/kbd\u003e or middle mouse button |\n| `:CornelisPrevGoal`         | Jump to previous goal | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-b\u003c/kbd\u003e |\n| `:CornelisNextGoal`         | Jump to next goal     | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-f\u003c/kbd\u003e |\n| `:CornelisQuestionToMeta`   | Expand `?`-holes to `{! !}` | _(none)_ |\n| `:CornelisInc`              | Like `\u003cC-A\u003e` but also targets sub- and superscripts | _(none)_ |\n| `:CornelisDec`              | Like `\u003cC-X\u003e` but also targets sub- and superscripts | _(none)_ |\n| `:CornelisCloseInfoWindows` | Close (all) info windows cornelis has opened | _(none)_ |\n\n### Commands in context of a goal\n\nThese commands can be used in context of a hole:\n\n| Vim command | Description | Equivalent agda-mode keybinding |\n| :--- | :--- | :--- |\n| `:CornelisGive`                  | Fill goal with hole contents | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-SPC\u003c/kbd\u003e |\n| `:CornelisRefine`                | Refine goal                  | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-r\u003c/kbd\u003e |\n| `:CornelisElaborate \u003cRW\u003e`        | Fill goal with normalized hole contents | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-m\u003c/kbd\u003e |\n| `:CornelisAuto`                  | [Automatic proof search]   | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-a\u003c/kbd\u003e |\n| `:CornelisMakeCase`              | Case split                 | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-c\u003c/kbd\u003e |\n| `:CornelisTypeContext \u003cRW\u003e`      | Show goal type and context | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-,\u003c/kbd\u003e |\n| `:CornelisTypeInfer \u003cRW\u003e`        | Show inferred type of hole contents | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-d\u003c/kbd\u003e |\n| `:CornelisTypeContextInfer \u003cRW\u003e` | Show goal type, context, and inferred type of hole contents | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-.\u003c/kbd\u003e |\n| `:CornelisNormalize \u003cCM\u003e`        | Compute normal of hole contents | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-n\u003c/kbd\u003e |\n| `:CornelisWhyInScope`            | Show why given name is in scope | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-w\u003c/kbd\u003e |\n| `:CornelisHelperFunc \u003cRW\u003e`       | Copy inferred type to register `\"` | \u003ckbd\u003eC-c\u003c/kbd\u003e\u003ckbd\u003eC-h\u003c/kbd\u003e |\n\n[Automatic proof search]: https://agda.readthedocs.io/en/latest/tools/auto.html#auto\n\nCommands with an `\u003cRW\u003e` argument take an optional normalization mode argument,\none of `AsIs`, `Instantiated`, `HeadNormal`, `Simplified` or `Normalised`. When\nomitted, defaults to `Normalised`.\nThis default may be specified in vimrc as `g:cornelis_rewrite_mode`.\n\nCommands with a `\u003cCM\u003e` argument take an optional compute mode argument:\n\n| `\u003cCM\u003e` | Description | Equivalent agda-mode prefix |\n| :---   | :---        | :---             |\n| `DefaultCompute`  | default, used if `\u003cCM\u003e` is omitted                 | no prefix, default |\n| `IgnoreAbstract`  | compute normal form, ignoring `abstract`s          | \u003ckbd\u003eC-u\u003c/kbd\u003e |\n| `UseShowInstance` | compute normal form of print using `show` instance | \u003ckbd\u003eC-u\u003c/kbd\u003e\u003ckbd\u003eC-u\u003c/kbd\u003e |\n| `HeadCompute`     | compute weak-head normal form                      | \u003ckbd\u003eC-u\u003c/kbd\u003e\u003ckbd\u003eC-u\u003c/kbd\u003e\u003ckbd\u003eC-u\u003c/kbd\u003e |\n\nIf Agda is stuck executing a command (e.g. if normalization takes too long),\nabort the command with `:CornelisAbort`.\n\nIf you need to restart the plugin (eg if Agda is stuck in a loop), you can\nrestart everything via `:CornelisRestart`.\n\n### Agda Input\n\nThere is reasonably good support for agda-input via your `\u003cLocalLeader\u003e` in\ninsert mode. See\n[agda-input.vim](https://github.com/isovector/cornelis/blob/master/agda-input.vim)\nfor available bindings.\n\nIf you'd like to use a prefix other than your `\u003cLocalLeader\u003e`, add the following\nto your `.vimrc`:\n\n```viml\nlet g:cornelis_agda_prefix = \"\u003cTab\u003e\" \" Replace with your desired prefix\n```\n\n\n#### Interactive Unicode Selection\n\nIf you'd like an interactive prompt for choosing unicode characters,\nadditionally install `vim-which-key`:\n\n```viml\nPlug 'liuchengxu/vim-which-key'\n```\n\nand map a call to `cornelis#prompt_input()` in insert mode:\n\n```viml\ninoremap \u003clocalleader\u003e \u003cC-O\u003e:call cornelis#prompt_input()\u003cCR\u003e\n```\n\n\n#### Disabling Default Bindings\n\nIf you don't want any of the default bindings, add the following to your `.vimrc`:\n\n```viml\nlet g:cornelis_no_agda_input = 1\n```\n\n\n#### Adding Bindings\n\nCustom bindings can be added by calling the `cornelis#bind_input` function in\n`.vimrc`. For example:\n\n```viml\ncall cornelis#bind_input(\"nat\", \"ℕ\")\n```\n\nwill add `\u003cLocalLeader\u003enat` as an input remapping for `ℕ`.\n\nThe inverse of `cornelis#bind_input(\"foo\", \"...\")` is `cornelis#unbind_input(\"foo\")`.\nNote that this cannot and will not attempt to undo any custom agda-input\nmanagement, such as through `g:cornelis_bind_input_hook` (see below).\n\n\n#### Custom Hooks\n\nIf you'd prefer to manage agda-input entirely on your own (perhaps in a snippet\nsystem), you can set the following:\n\n```viml\nfunction! MyCustomHook(key, character)\n  \" do something\nendfunction\n\nlet g:cornelis_bind_input_hook = \"MyCustomHook\"\n```\n\nYou can invoke `cornelis#standard_bind_input` with the same arguments if you'd\nlike to run your hook in addition to the standard one.\n\n\n### Text Objects\n\nUse the `iz`/`az` text objects to operate on text between `⟨` and `⟩`. Somewhat\nsurprisingly for i/a text objects, `iz` targets the _spaces_ between these\nbrackets, and `az` targets the spaces. Neither textobj targets the brackets\nthemselves.\n\nAlso `ii`/`ai` will operate on `⦃` and `⦄`, but in the way you'd expect\ntext objects to behave.\n\n`ih`/`ah` will operate on `{!` and `!}`.\n\n### Debugging\n\nWhen debugging the cornelis plugin or creating an issue, set the `cornelis_debug` variable.\n\n```viml\nlet g:cornelis_debug = v:true\n```\n\n```lua\nvim.g.cornelis_debug = true\n```\n\nCornelis will then output agda interaction information to `/tmp/agda.log`.\n\n## Installation\n\nMake sure you have [`stack`](https://docs.haskellstack.org/en/stable/install_and_upgrade/) on your PATH!\n\nFor [vim-plug](https://github.com/junegunn/vim-plug):\n\n```viml\nPlug 'kana/vim-textobj-user'\nPlug 'neovimhaskell/nvim-hs.vim'\nPlug 'isovector/cornelis', { 'do': 'stack build', 'tag': '*' }\n```\n\nfor [lazy.nvim](https://github.com/folke/lazy.nvim):\n\n```lua\n{\n  'isovector/cornelis',\n  name = 'cornelis',\n  ft = 'agda',\n  build = 'stack install',\n  dependencies = {'neovimhaskell/nvim-hs.vim', 'kana/vim-textobj-user'},\n  version = '*',\n}\n```\n\n\n### Agda Version\n\nIf you are having issues, try using a tag which matches your agda major version\n(e.g. for Agda `v2.7.0.1` use cornelis `v2.7.*`). If there is no matching\nversion that is working for a new version of agda, please create an issue.\n\n### Installation with Nix\n\nYou can install both the vim plugin and the cornelis binary using nix flakes!\nYou can access the binary as `cornelis.packages.\u003cmy-system\u003e.cornelis` and the\nvim plugin as `cornelis.packages.\u003cmy-system\u003e.cornelis-vim`. Below is a sample\nconfiguration to help you understand where everything plugs in.\n\n\u003cdetails\u003e\n\u003csummary\u003eNix details\u003c/summary\u003e\n\n```nix\n# flake.nix\n{\n  description = \"my-config\";\n\n  inputs = {\n    nixpkgs.url = \"github:nixos/nixpkgs/nixpkgs-unstable\";\n    home-manager = {\n      url = \"github:nix-community/home-manager\";\n      inputs.nixpkgs.follows = \"nixpkgs\";\n    };\n    cornelis.url = \"github:isovector/cornelis\";\n    cornelis.inputs.nixpkgs.follows = \"nixpkgs\";\n  };\n  outputs =\n    { home-manager\n    , nixpkgs\n    , cornelis\n    , ...\n    }: {\n    nixosConfigurations = {\n      bellerophon = nixpkgs.lib.nixosSystem {\n        system = \"x86_64-linux\";\n        modules = [\n          home-manager.nixosModules.home-manager\n          {\n            nixpkgs.overlays = [cornelis.overlays.cornelis];\n            home-manager.useGlobalPkgs = true;\n            home-manager.useUserPackages = true;\n            home-manager.users.my-home = import ./my-home.nix;\n          }\n        ];\n      };\n    };\n  };\n}\n\n# my-home.nix\n{pkgs, ...}:\n{\n  home.packages = [ pkgs.agda ];\n  programs.neovim = {\n    enable = true;\n    extraConfig = builtins.readFile ./init.vim;\n    plugins = [\n      {\n        # plugin packages in required Vim plugin dependencies\n        plugin = pkgs.vimPlugins.cornelis;\n        config = \"let g:cornelis_use_global_binary = 1\";\n      }\n    ];\n    extraPackages = [ pkgs.cornelis ];\n  };\n}\n```\n\u003c/details\u003e\n\nMake sure you enable the global binary option in your vim config. Since\n`/nix/store` is immutable cornelis will fail when `nvim-hs` tries to run stack,\nwhich it will do if the global binary option isn't enabled.\n\n\n#### Use global binary instead of stack\n\nVimscript:\n\n```viml\nlet g:cornelis_use_global_binary = 1\n```\n\nLua:\n\n```lua\nvim.g.cornelis_use_global_binary = 1\n```\n\n\n## Example Configuration\n\nOnce you have `cornelis` installed, you'll probably want to add some keybindings\nfor it! This is enough to get you started:\n\n```viml\nau BufRead,BufNewFile *.agda call AgdaFiletype()\nau QuitPre *.agda :CornelisCloseInfoWindows\nfunction! AgdaFiletype()\n    nnoremap \u003cbuffer\u003e \u003cleader\u003el :CornelisLoad\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e \u003cleader\u003er :CornelisRefine\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e \u003cleader\u003ed :CornelisMakeCase\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e \u003cleader\u003e, :CornelisTypeContext\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e \u003cleader\u003e. :CornelisTypeContextInfer\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e \u003cleader\u003en :CornelisSolve\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e \u003cleader\u003ea :CornelisAuto\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e gd        :CornelisGoToDefinition\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e [/        :CornelisPrevGoal\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e ]/        :CornelisNextGoal\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e \u003cC-A\u003e     :CornelisInc\u003cCR\u003e\n    nnoremap \u003cbuffer\u003e \u003cC-X\u003e     :CornelisDec\u003cCR\u003e\nendfunction\n```\n\nFeeling spicy? Automatically run `CornelisLoad` every time you save the file.\n\n```viml\nau BufWritePost *.agda execute \"normal! :CornelisLoad\\\u003cCR\u003e\"\n```\n\nIf you'd like to automatically load files when you open them too, try this:\n\n```viml\nfunction! CornelisLoadWrapper()\n  if exists(\":CornelisLoad\") ==# 2\n    CornelisLoad\n  endif\nendfunction\n\nau BufReadPre *.agda call CornelisLoadWrapper()\nau BufReadPre *.lagda* call CornelisLoadWrapper()\n```\n\nThis won't work on the first Agda file you open due to a bug, but it will\nsuccessfully load subsequent files.\n\n\n### Configuring Cornelis' Behavior\n\nThe max height and width of the info window can be set via:\n\n```viml\nlet g:cornelis_max_size = 30\n```\n\nand\n\n```viml\nlet g:cornelis_max_width = 40\n```\n\nIf you'd prefer your info window to appear somewhere else, you can set\n`g:cornelis_split_location` (previously `g:cornelis_split_direction`), e.g.\n\n```viml\nlet g:cornelis_split_location = 'vertical'\n```\n\nThe following configuration options are available:\n\n- `horizontal`: The default, opens in a horizontal split respecting `splitbelow`.\n- `vertical`: Opens in a vertical split respecting `splitright`.\n- `top`: Opens at the top of the window.\n- `bottom`: Opens at the bottom of the window.\n- `left`: Opens at the left of the window.\n- `right`: Opens at the right of the window.\n\nSet default rewrite mode to use in commands which take an optional\nnormalization mode argument\n\n```viml\nlet g:cornelis_rewrite_mode = 'HeadNormal'\n```\n\nThe following configuration options are available:\n- `AsIs`\n- `Instantiated`\n- `HeadNormal`\n- `Simplified`\n- `Normalised`\n\n### Aligning Reasoning Justification\n\nIf you're interested in automatically aligning your reasoning justifications,\nalso install the following plugin:\n\n```viml\nPlug 'junegunn/vim-easy-align'\n```\n\nand add the following configuration for it:\n\n```viml\nvmap \u003cleader\u003e\u003cspace\u003e \u003cPlug\u003e(EasyAlign)\n\nlet g:easy_align_delimiters = {\n\\ 'r': { 'pattern': '[≤≡≈∎]', 'left_margin': 2, 'right_margin': 0 },\n\\ }\n```\n\nYou can now align justifications by visually selecting the proof, and then\ntyping `\u003cleader\u003e\u003cspace\u003er`.\n\n\n### Customizing Syntax Highlighting\n\nSyntax highlighting is controlled by syntax groups named `Cornelis*`,\ndefined in [`syntax/agda.vim`](./syntax/agda.vim).\nThese groups are linked to default highlighting groups\n([`:h group-name`](https://neovim.io/doc/user/syntax.html#group-name)),\nand can be customized by overriding them in user configuration.\n\n```viml\n\" Highlight holes with a yellow undercurl/underline:\nhighlight CornelisHole ctermfg=yellow ctermbg=NONE cterm=undercurl\n\n\" Highlight \"generalizables\" (declarations in `variable` blocks) like constants:\nhighlight link CornelisGeneralizable Constant\n```\n\n\n## Contributing\n\nI'm a noob at Agda, and I don't know what I don't know. If this plugin doesn't\nhave some necessary feature for you to get work done, please file a bug,\nincluding both what's missing, and how you use it in your workflow. I'd love to\nlearn how to use Agda better! I can move quickly on feature requests.\n\nIf you'd like to get involved, feel free to tackle an issue on the tracker and\nsend a PR. I'd love to have you on board!\n\n## Architecture\n\nCornelis spins up a new `BufferStuff` for each Agda buffer it encounters.\n`BufferStuff` contains a handle to a unique `agda` instance, which can be used\nto send commands. It also tracks things like the information window buffer,\nin-scope goals, and whatever the last `DisplayInfo` response from `agda` was.\n\nFor each `BufferStuff`, we also spin up a new thread, blocking on responses\nfrom `agda`. These responses all get redirected to a global worker thread, which\nis responsible for dispatching on each command. Commands are typesafe, parsed\nfrom JSON, and associated with the buffer they came from.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagda%2Fcornelis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagda%2Fcornelis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagda%2Fcornelis/lists"}