diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..bcac959
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,2 @@
+# global rule
+* @max397574
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..9c20604
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,105 @@
+name: Bug Report
+description: Report a problem with neocomplete.nvim
+labels: [bug]
+body:
+ - type: checkboxes
+ id: faq-prerequisite
+ attributes:
+ label: Prerequisites
+ options:
+ - label: I am using the latest stable neovim version
+ required: true
+ - label: I am using the latest version of the plugin
+ required: true
+ - type: input
+ attributes:
+ label: "Neovim Version"
+ description: "`nvim --version`:"
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: "neocomplete.nvim setup"
+ description: |
+ - Copy your entire `require("neocomplete").setup` function
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: "Actual behavior"
+ description: "A description of actual behavior. May optionally include images or videos."
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: "Expected behavior"
+ description: "A description of the behavior you expected."
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: "Healthcheck"
+ description: "Output of `:checkhealth neocomplete`"
+ render: markdown
+ placeholder: |
+ neocomplete: require("neocomplete.health").check()
+ ========================================================================
+ ## neocomplete.nvim
+ required: true
+ - type: textarea
+ attributes:
+ label: "Steps to reproduce"
+ description: "Please describe how we can reproduce the issue."
+ placeholder: |
+ 1. `nvim -nu minimal.lua`
+ 2. ...
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: "Other information"
+ description: "Other information that could be helpful with debugging."
+ - type: textarea
+ attributes:
+ label: "Minimal config"
+ description: "Minimal config with which the issue is reproducible. Save this as `minimal_init.lua. Add plugins and configs which are necessary at indicated location."
+ render: Lua
+ value: |
+ local root = vim.fn.fnamemodify("./.repro", ":p")
+
+ -- set stdpaths to use .repro
+ for _, name in ipairs({ "config", "data", "state", "cache" }) do
+ vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
+ end
+
+ -- bootstrap lazy
+ local lazypath = root .. "/plugins/lazy.nvim"
+ if not vim.loop.fs_stat(lazypath) then
+ vim.fn.system({
+ "git",
+ "clone",
+ "--filter=blob:none",
+ "--single-branch",
+ "https://github.com/folke/lazy.nvim.git",
+ lazypath,
+ })
+ end
+ vim.opt.runtimepath:prepend(lazypath)
+
+ -- install plugins
+ local plugins = {
+ "folke/tokyonight.nvim",
+ {
+ "max397574/neocomplete.nvim",
+ },
+ }
+ require("lazy").setup(plugins, {
+ root = root .. "/plugins",
+ })
+
+ -- add anything else here
+ vim.opt.termguicolors = true
+ vim.cmd([[colorscheme tokyonight]])
+ require"neocomplete".setup()
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..75f6d67
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,4 @@
+contact_links:
+ - name: Ask a question
+ url: https://github.com/max397574/neocomplete.nvim/discussions
+ about: If you need help with configuration or something else
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000..46b137a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,33 @@
+name: Feature request
+description: Request a feature for neocomplete.nvim
+labels: [feature]
+body:
+ - type: checkboxes
+ id: issue-prerequisite
+ attributes:
+ label: Issues
+ options:
+ - label: I have checked [existing issues](https://github.com/max397574/neocomplete.nvim/issues) and there are no existing ones with the same request.
+ required: true
+ - type: textarea
+ attributes:
+ label: "Feature description"
+ validations:
+ required: true
+ - type: dropdown
+ id: help
+ attributes:
+ label: "Help"
+ description: "Would you be able to implement this by submitting a pull request?"
+ options:
+ - "Yes"
+ - "Yes, but I don't know how to start. I would need guidance"
+ - "No"
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: "Implementation help"
+ description: "If you selected yes in the last question please specify in detail what you would need help with in order to implement this."
+ validations:
+ required: false
diff --git a/.github/funding.yml b/.github/funding.yml
new file mode 100644
index 0000000..4661b63
--- /dev/null
+++ b/.github/funding.yml
@@ -0,0 +1,2 @@
+github: 'max397574'
+custom: ['https://buymeacoffee.com/max397574']
diff --git a/.github/workflows/.luarc.json b/.github/workflows/.luarc.json
new file mode 100644
index 0000000..0c47c78
--- /dev/null
+++ b/.github/workflows/.luarc.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
+ "runtime.version": "LuaJIT",
+ "runtime.path": [
+ "lua/?.lua",
+ "lua/?/init.lua"
+ ],
+ "workspace.library": [
+ "/home/runner/work/neocomplete.nvim/neocomplete.nvim/deps/neovim/runtime/lua",
+ "/home/runner/work/neocomplete.nvim/neocomplete.nvim/deps/luacats/luv/library"
+ ],
+ "diagnostics.libraryFiles": "Disable",
+ "workspace.checkThirdParty": "Disable"
+}
diff --git a/.github/workflows/auto_release.yml b/.github/workflows/auto_release.yml
new file mode 100644
index 0000000..86b85bf
--- /dev/null
+++ b/.github/workflows/auto_release.yml
@@ -0,0 +1,16 @@
+name: Release Please
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ release:
+ name: Release
+ runs-on: ubuntu-latest
+ steps:
+ - uses: google-github-actions/release-please-action@v3
+ with:
+ release-type: simple
+ package-name: neocomplete.nvim
diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml
new file mode 100644
index 0000000..ca2439d
--- /dev/null
+++ b/.github/workflows/formatting.yml
@@ -0,0 +1,48 @@
+name: Formatting
+
+on:
+ push:
+ branches: [ "main" ]
+ paths-ignore:
+ - ".github/**"
+ - "**.md"
+ - "**.norg"
+
+jobs:
+ format-with-stylua:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Cache cargo modules
+ id: cache-cargo
+ uses: actions/cache@v2
+ env:
+ cache-name: cache-node-modules
+ with:
+ path: ~/.cargo
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.toml') }}
+ restore-keys: |
+ ${{ runner.os }}-build-${{ env.cache-name }}-
+ ${{ runner.os }}-build-
+ ${{ runner.os }}-
+
+ - name: Install cargo
+ run: curl https://sh.rustup.rs -sSf | sh -s -- -y
+
+ - name: Install stylua
+ run: cargo install stylua --features lua52
+
+ - name: Run formatting
+ run: stylua -v --verify .
+
+ - uses: stefanzweifel/git-auto-commit-action@v4
+ with:
+ commit_message: "chore: autoformat with stylua"
+ branch: ${{ github.ref }}
+
+ - name: Push changes
+ uses: ad-m/github-push-action@master
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ branch: ${{ github.ref }}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..33a1f23
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,15 @@
+name: Linter
+on:
+ pull_request: ~
+ push:
+ branches:
+ - '*'
+jobs:
+ luacheck:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: nebularg/actions-luacheck@v1
+ with:
+ files: 'lua'
+ args: --no-unused
diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml
new file mode 100644
index 0000000..2ea7ba5
--- /dev/null
+++ b/.github/workflows/typecheck.yml
@@ -0,0 +1,44 @@
+name: lua-ls Typecheck
+on:
+ pull_request: ~
+ push:
+ branches:
+ - '*'
+jobs:
+ build:
+ name: lua-ls Typecheck
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v3
+ - name: Get latest neovim release tag
+ id: get_latest_neovim_tag
+ run: |
+ latest_tag=$(curl --silent "https://api.github.com/repos/neovim/neovim/tags" | jq -r '.[0].name')
+ echo "Latest tag: $latest_tag"
+ echo "::set-output name=latest_tag::$latest_tag"
+ - name: Checkout neovim for type annotations
+ uses: actions/checkout@v3
+ with:
+ repository: "neovim/neovim"
+ path: "deps/neovim"
+ ref: ${{ steps.get_latest_neovim_tag.outputs.latest_tag }}
+ - name: Install luv luaCATS
+ uses: actions/checkout@v3
+ with:
+ repository: "LuaCATS/luv"
+ path: "deps/luacats/luv"
+ - uses: leafo/gh-actions-lua@v9 # get luarocks dependencies for their types (eg `PathlibPath`)
+ with:
+ luaVersion: "5.1"
+ - uses: leafo/gh-actions-luarocks@v4
+ - name: install dependencies
+ run: |
+ luarocks init
+ luarocks install --only-deps ./*.rockspec
+ - name: Type Check Code Base
+ uses: mrcjkb/lua-typecheck-action@v0.2.1
+ with:
+ configpath: .github/workflows/.luarc.json
+ directories: |
+ lua
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6fb57e6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+todo.norg
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/docs/completion_item.norg b/docs/completion_item.norg
new file mode 100644
index 0000000..909dc9d
--- /dev/null
+++ b/docs/completion_item.norg
@@ -0,0 +1,48 @@
+@document.meta
+title: Source
+description: Type description of neocomplete.nvim completion item
+authors: [
+ max397574
+]
+categories: [
+ docs,
+ types
+]
+tangle: {
+ languages: {
+ lua: ../lua/neocomplete/types/completion_item.lua
+ }
+ scope: tagged
+ delimiter: none
+}
+created: 2023-11-17T16:29:17+0100
+updated: 2023-11-17T16:29:50+0100
+version: 1.1.1
+@end
+
+* General
+ This is a superset of the {https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItem}[lsp-completion-item].
+ Completion items are the items which are returned by a completion source. *Important:* They are
+ not to be confused with an {:./entry:}.
+
+ #tangle
+ @code lua
+ --- An item returned by a completion source
+ ---@class neocomplete.completion_item: lsp.CompletionItem
+ @end
+
+* Label
+ Every item needs to have a label. This is also the default text that will be inserted. There can
+ also be additional `labelDetails`. Both of these properties are inherited from the lsp-completion-
+ item.
+
+* Kind
+ The optional `kind` property is used to determine the kind of the completion item. This is often
+ used to display an icon representing it. In neocomplete this can either be a number like in the
+ lsp-specification of a string.
+
+ #tangle
+ @code lua
+ --- The kind of the completion item
+ ---@field kind? string|lsp.CompletionItemKind
+ @end
diff --git a/docs/config.norg b/docs/config.norg
new file mode 100644
index 0000000..9553184
--- /dev/null
+++ b/docs/config.norg
@@ -0,0 +1,172 @@
+@document.meta
+title: Source
+description: Type description of neocomplete.nvim config
+authors: [
+ max397574
+]
+categories: [
+ docs,
+ types
+]
+tangle: {
+ languages: {
+ lua: ../lua/neocomplete/types/config.lua
+ }
+ scope: tagged
+ delimiter: none
+}
+created: 2023-11-17T16:29:17+0100
+updated: 2023-11-17T16:32:27+0100
+version: 1.1.1
+@end
+
+* General
+ The config of neocomplete is used to configure the ui and neocomplete itself.
+ #tangle
+ @code lua
+ --- Configuration for neocomplete.nvim
+ ---@class neocomplete.config
+ @end
+
+ There are two main parts to the config. The first one is the `ui` field and the second on is the
+ rest of the configuration which is for configuring neocomplete itself.
+
+** UI
+ In the ui field the completion-menu, the docs-view and the format of the entries are configured.
+ There is also a field for configuring type icons.
+ #tangle
+ @code lua
+ --- Configuration for the ui of neocomplete
+ ---@field ui neocomplete.config.ui
+ @end
+
+** Snippet expansion
+ Here a function for expanding snippets is defined. By default this is the builtin
+ `vim.snippet.expand()`. You can also use a plugin like luasnip for this like this:
+ @code lua
+ snippet_expansion = function(body)
+ require("luasnip").lsp_expand(body)
+ end
+ @end
+
+ #tangle
+ @code lua
+ --- Function used to expand snippets
+ ---@field snippet_expansion fun(string): nil
+ @end
+
+** Enabled
+ This function can be used to disable neocomplete in certain contexts. By default this disables
+ neocomplete in prompts.
+
+ #tangle
+ @code lua
+ --- Configuration for the ui of neocomplete
+ ---@field enabled fun(): boolean
+ @end
+
+* UI
+ The ui configuration is used to configure the whole ui of neocomplete. One of the main goals of
+ this is to be as extensible as possible. This is especially important for the completion entries.
+ Read more about that under {:./design:**** Configuraton of item display}.
+ #tangle
+ @code lua
+ --- The main class for the ui configuration of neocomplete.nvim
+ ---@class neocomplete.config.ui
+ @end
+ The most important part for many users will be the `menu` field. It's used to configure the completion menu.
+ #tangle
+ @code lua
+ --- Configuration of the completion menu of neocomplete.nvim
+ ---@field menu neocomplete.config.ui.menu
+ @end
+
+ You can also configure the documentation view just like the main menu.
+ #tangle
+ @code lua
+ --- Configuration of the documentation view of neocomplete.nvim
+ ---@field docs_view neocomplete.config.ui.docs
+ @end
+
+ Lastly the users can also configure the icons which will be used for the different items.
+ #tangle
+ @code lua
+ --- The icons for the different compltion item kinds
+ ---@field type_icons neocomplete.config.ui.type_icons
+ @end
+
+** Menu
+ This configuration should allow you to completely adapt the completion menu to your likings.
+ #tangle
+ @code lua
+ --- Configuration of the completion menu of neocomplete.nvim
+ ---@class neocomplete.config.ui.menu
+ @end
+ It includes some basic window properties like the border and the maximum height of the window.
+ #tangle
+ @code lua
+ --- Maximum height of the menu
+ ---@field max_height integer
+ --- The border of the completion menu
+ ---@field border string|string[]|string[][]
+ @end
+
+ Another field is `format_entry`. This is a function which recieves an entry of the completion
+ menu and determines how it's formatted. For that a table with text-highlight chunks like
+ `:h nvim_buf_set_extmarks()` is used. You can create sections which are represented by tables
+ and can have a different alignment each. This is specified with another field which takes a table
+ with the alignment of each section.
+
+ For example you want to have the label of an entry in a red highlight and an icon in a entry-kind
+ specific color left aligned first and then the source of the entry right aligned in blue.
+ You could do that like this:
+ @code lua
+ format_entry = function(entry)
+ return {
+ -- The first section with the two chunks for the label and the icon
+ { { entry.label .. " ", "MyRedHlGroup" }, { entry.kind, "HighlightKind" .. entry.kind } }
+ -- The second section for the source
+ { { entry.source, "MyBlueHlGroup" } }
+ }
+ end,
+ alignment = { "left", "right" }
+ @end
+
+ Notice that there are multiple differences between having one table containing the chunks for the
+ label and kind and having them separately. The latter would require another entry in the `alignment`
+ table. It would also change the style of the menu because the left sides of the icons would be
+ aligned at the same column and not be next to the labels. In the example there also was some
+ spacing added in between the two.
+
+ #tangle
+ @code lua
+ --- How an entry should be formatted
+ ---@field format_entry fun(neocomplete.entry): { [1]: string, [2]: string }[][]
+ --- How the sections in the menu should be aligned
+ ---@field alignment ("left"|"center"|"right")[]
+ @end
+
+** Documentation view
+ This configuration allows you to configure the documentation view.
+ #tangle
+ @code lua
+ --- Configuration of the completion menu of neocomplete.nvim
+ ---@class neocomplete.config.ui.docs
+ @end
+ It consists of window properties like the border and the maximum height of the window.
+ #tangle
+ @code lua
+ --- Maximum height of the documentation view
+ ---@field max_height integer
+ --- The border of the documentation view
+ ---@field border string|string[]|string[][]
+ @end
+
+** Type Icons
+ This is a table which defines the different icons.
+
+ #tangle
+ @code lua
+ --- The icons used for the different completion item types
+ ---@alias neocomplete.config.ui.type_icons table
+ @end
diff --git a/docs/context.norg b/docs/context.norg
new file mode 100644
index 0000000..ab78932
--- /dev/null
+++ b/docs/context.norg
@@ -0,0 +1,77 @@
+@document.meta
+title: Index
+description: Type description of context
+authors: [
+ max397574
+]
+categories: [
+ docs,
+ types
+]
+created: 2023-11-15T17:42:46+0100
+updated: 2024-05-28T20:54:48+0100
+tangle: {
+ languages: {
+ lua: ../lua/neocomplete/types/context.lua
+ }
+ scope: tagged
+ delimiter: none
+}
+version: 1.1.1
+@end
+
+* Completion Context
+ #tangle
+ @code lua
+ --- Context provided to completion sources
+ ---@class neocomplete.context
+ @end
+
+** Changed
+ Whether the context changed in comparison to the previous one
+ #tangle
+ @code lua
+ --- Context changed since previous
+ ---@field changed fun(neocomplete.context): boolean
+ @end
+
+** New
+ Create a new context
+ #tangle
+ @code lua
+ --- Create a new context
+ ---@field new fun(neocomplete.context?): neocomplete.context
+ @end
+
+** Previous
+ The previous context which is used to determine whether the context changed or not
+ #tangle
+ @code lua
+ --- The previous context
+ ---@field previous neocomplete.context?
+ @end
+
+** Cursor
+ #tangle
+ @code lua
+ --- The cursor position
+ ---@field cursor neocomplete.context.cursor
+ @end
+
+** Bufnr
+ Number of the buffer
+ #tangle
+ @code lua
+ --- The number of the buffer
+ ---@field bufnr integer
+ @end
+
+
+ #tangle
+ @code lua
+ --- A cursor position
+ ---@class neocomplete.context.cursor
+ ---@field row integer
+ ---@field col integer
+ @end
+
diff --git a/docs/core.norg b/docs/core.norg
new file mode 100644
index 0000000..af49479
--- /dev/null
+++ b/docs/core.norg
@@ -0,0 +1,74 @@
+@document.meta
+title: Core
+description: Type description of neocomplete.nvim source
+authors: [
+ max397574
+]
+categories: [
+ docs,
+ types
+]
+created: 2023-11-15T17:42:46+0100
+updated: 2024-05-29T16:24:09+0100
+tangle: {
+ languages: {
+ lua: ../lua/neocomplete/types/core.lua
+ }
+ scope: tagged
+ delimiter: none
+}
+version: 1.1.1
+@end
+
+* General
+ #tangle
+ @code lua
+ --- The core of neocomplete where the main api functions are defined
+ ---@class neocomplete.core
+ @end
+
+** New
+ Creates a new core instance
+ #tangle
+ @code lua
+ --- Create a new instance of the core
+ ---@field new fun(): neocomplete.core
+ @end
+
+** Context
+ #tangle
+ @code lua
+ --- Context instance of the core
+ ---@field context neocomplete.context
+ @end
+
+** Menu
+ #tangle
+ @code lua
+ --- Menu instance of the core
+ ---@field menu neocomplete.menu
+ @end
+
+** Complete
+ The main function which starts the completion
+ #tangle
+ @code lua
+ --- Complete
+ ---@field complete fun(neocomplete.core): nil
+ @end
+
+** On Change
+ #tangle
+ @code lua
+ --- The function that gets invoked when the text changes
+ ---@field on_change fun(neocomplete.core): nil
+ @end
+
+** Setup
+ The setup function is used to setup neocomplete so it will actually provide autocompletion when
+ typing.
+ #tangle
+ @code lua
+ --- Setup core (for now autocommands)
+ ---@field setup fun(self: neocomplete.core): nil
+ @end
diff --git a/docs/design.norg b/docs/design.norg
new file mode 100644
index 0000000..c6ebec0
--- /dev/null
+++ b/docs/design.norg
@@ -0,0 +1,163 @@
+@document.meta
+title: Design
+description: Design of neocomplete.nvim
+authors: [max397574]
+categories: [docs]
+created: 2023-11-15T17:42:49+0100
+updated: 2023-11-15T17:42:49+0100
+version: 1.1.1
+@end
+
+* General
+ There is the {https://github.com/max397574/neocomplete.nvim}[neocomplete.nvim] plugin. This is
+ the main module of the whole completion architecture. Then there are also sources, which is where
+ the core gets it's completions from.
+
+** Neocomplete.nvim
+ This is the core of the autocompletion. From here the sources are used to get completions which
+ are then displayed in the {*** Completion Menu}
+
+*** Completion Menu
+ The completion menu displays the current completions.
+ Features that it should have:
+ - Completely customizable display for every entry (with *text-highlight chunks* like extmark-api)
+ - Customizable scrollbar
+ - Customizable window properties
+ -- Border
+ -- Max height
+ - Docview
+ -- Customizable
+ -- Try to get nicely concealed like in core or noice.nvim
+ -- Allow to send to e.g. quickfix or copy
+
+** Source
+
+** Types
+ - {:./types/source:}
+
+
+* Architecture
+ TODO: fancy ascii diagramms
+
+ autocompletion:
+ ~ `TextChangedI` or `CursorMovedI`
+ ~ Check if character was a trigger character or you're in specific context e.g. at beginning of line or word
+ ~ Depending on ^^ decide what to do /for every source/:
+ -- Get new completions
+ ~~~ get the context (line before, cursor position etc)
+ ~~~ get completions from source based on context
+ -- sort completions
+ ~~~ use character typed to fuzzy match and sort completions
+ ~ display things
+ ~~ figure out top matching completions
+ ~~ if there is a selected one:
+ --- highlight selected one
+ --- show preview of selected completion
+ --- show docs
+
+ When pressing e.g. ``:
+ ~ check if menu is open
+ ~ check if anything is selected
+ ~ complete
+ ~~ insert text
+ ~~ additional text edits (check core functions)
+ ~~ snippet expansion (core or luasnip)
+ ~ close menu
+
+* Motivation
+** Nvim-cmp
+ These days nvim-cmp is the most popular completion-engine for neovim. There are some mayor issues
+ me and also other people in the community have with cmp.
+
+*** Bad code/Documentation
+ The code of nvim-cmp is often quite unreadable. Sometimes this might be due to optimizations
+ and surely some of it has just grown historically. Also there are nearly no docs on how the
+ whole completion engine works. The api for new sources is quite unclear and far from optimal.
+ While this doesn't really matter to a user it definitely does to a potential contributor and
+ developers of sources.
+
+*** Legacy code/features
+ There are a lot of things which grew just historically. The author of nvim-cmp is
+ (understandable to a certain degree) afraid of making breaking changes and fixing them or just
+ doesn't think changes are necessary.
+
+**** Configuraton of item display
+ One such example is the configuration of how items are displayed in the menu. This works with
+ a function `formatting` which takes a completion item and is allowed to return an item where
+ the three fields `menu`, `kind` and `abbr` and three more fields for highlights for those can
+ be set. So appart from the background and border color of the menu you're limited to have three
+ different fields and colors in your menu E.g. source name, kind name, kind icon and text isn't
+ possible. It's also not possible to have round or half blocks around icons because you don't
+ have enough colors. An example of an issue can be found {https://github.com/hrsh7th/nvim-cmp/issues/1599}[here].
+ You also can't add padding wherever you want and you can't align the fields as you want.
+
+**** Legacy code
+ There is e.g. the whole "native menu" thing laying around in the codebase. Nowadays this isn't
+ really needed anymore. Everything of it can be accomplished with the "custom menu". There is a
+ lot of duplicate code because of that.
+
+*** Mapping system
+ The mapping system is quite confusing. It's a table in the config with keys to be mapped as keys
+ and a call to `cmp.mapping()` with a callback to the actual functionality as value.
+
+*** Why not contribute?
+ The maintainer is in general quite conservative. There were pull-requests for many features open
+ which were liked by the community (seen by reactions and comments). But they were abandoned
+ because the maintainer saw no reason to add it. There was for example a pull-request to fix the
+ issue with the limited fields in the configuration {https://github.com/hrsh7th/nvim-cmp/pull/1238}[here].
+ This pull request was closed because *No specific use cases have emerged at this stage.*
+ according to the author. Even though there was clearly a problem described and what the pr would
+ allow (this pr allowed custom fields which still isn't nice but fixed the obvious problems).
+ There were also some features (in particular the custom scrollbars) removed because there were
+ some issues with it which apperently weren't worth fixing for the feature.
+ So it's not really motivating to try to contribute new things. It's also quite hard because of
+ the messy code with lots of legacy code.
+
+* Goals
+** Use nvim-cmp sources
+ We should be able to use nvim-cmp sources. This should be possible by adding a `package.loaders`
+ where we can redirect calls to `cmp.register_source` (which happens in most sources auto-
+ matically) to our own plugin. We *don't want to adapt to cmp's apis* for this though. We won't
+ extend our own formats e.g. for entries or sources to match cmps. Even when it's complicated we
+ will just convert between the different formats.
+
+* Non-Goals
+** Different views
+ Nvim-cmp has different views. At the moment wild-menu, native menu and custom menu. There is a
+ lot of code duplication because of this. We'd like to avoid having multiple views. The native
+ one isn't needed anyway (it likely is just in cmp for historical reasons).
+ In the future we'd like to allow injecting custom views via config where you just get the
+ entries and do things with them yourself. This is mostly to avoid code duplication in core.
+
+* Types
+ TODO: tangle those into files?
+ TODO: perhaps move to different file?
+ The types should minimally be the lsp things (for the context passed to source, for response and
+ for entries). Everything additionally just optional.
+ @code lua
+ ---A source for neocomplete
+ ---@class neocomplete.source
+ ---@field
+ @end
+
+* Code style
+** Object-Orientation
+ TOOD: decide
+ avoid for utils?
+ menu, view, entry, source oop?
+** Functions
+ You should always write functions in the form of `[local] function ()` as
+ opposed to `[local = function()`. The first notation provides the advantage
+ that you can directly jump to it's definition and you won't get multiple results (the name and
+ the anonymous function).
+** Comments and annotations
+ Add annotations to every public function of a module (e.g. with neogen) and add comments
+ explaining what the code does. We'd like to have code which would be understandable for outsiders.
+
+*** Format
+ TODO: decide whether we want spaces after `---` or not
+
+** Types
+ We have files for types which are tangled from a norg file (this one?) using lua-ls annotations.
+ They are prefixed with `neocomplete`.
+ As often as possible we should try to use the `lsp.*` types which are in neovim core (TODO: perhaps only on 0.10? but we don't care).
diff --git a/docs/entry.norg b/docs/entry.norg
new file mode 100644
index 0000000..fa788e2
--- /dev/null
+++ b/docs/entry.norg
@@ -0,0 +1,29 @@
+@document.meta
+title: Entry
+description: Type description of neocomplete.nvim entry
+authors: [
+ max397574
+]
+categories: [
+ docs,
+ types
+]
+created: 2023-11-15T17:42:46+0100
+updated: 2024-05-29T15:28:34+0100
+tangle: {
+ languages: {
+ lua: ../lua/neocomplete/types/entry.lua
+ }
+ scope: tagged
+ delimiter: none
+}
+version: 1.1.1
+@end
+
+* General
+ Entries are the items in the completion menu.
+ #tangle
+ @code lua
+ --- An entry for the neocomplete completion menu
+ ---@class neocomplete.entry: lsp.CompletionItem
+ @end
diff --git a/docs/menu.norg b/docs/menu.norg
new file mode 100644
index 0000000..c63d324
--- /dev/null
+++ b/docs/menu.norg
@@ -0,0 +1,154 @@
+@document.meta
+title: Menu
+description: Type description of neocomplete.nvim menu
+authors: [
+ max397574
+]
+categories: [
+ docs,
+ types
+]
+created: 2024-05-29T11:30:25+0100
+updated: 2024-05-30T17:41:03+0100
+tangle: {
+ languages: {
+ lua: ../lua/neocomplete/types/menu.lua
+ }
+ scope: tagged
+ delimiter: none
+}
+version: 1.1.1
+@end
+
+* General
+ #tangle
+ @code lua
+ --- The completion menu of neocomplete
+ ---@class neocomplete.menu
+ @end
+
+* Methods
+** New
+ Creates a new instance
+ #tangle
+ @code lua
+ --- Create a new instance of the menu
+ ---@field new fun(): neocomplete.menu
+ @end
+
+** Draw
+ #tangle
+ @code lua
+ --- Draws the menu
+ ---@field draw fun(self: neocomplete.menu): nil
+ @end
+
+** Open win
+ #tangle
+ @code lua
+ --- Opens window for the menu
+ ---@field open_win fun(self: neocomplete.menu): nil
+ @end
+
+
+** Select next
+ #tangle
+ @code lua
+ --- Select next entry in menu
+ ---@field select_next fun(self: neocomplete.menu, count: integer): nil
+ @end
+
+** Select prev
+ #tangle
+ @code lua
+ --- Select previous entry in menu
+ ---@field select_prev fun(self: neocomplete.menu, count: integer): nil
+ @end
+
+** Open
+ #tangle
+ @code lua
+ --- Opens the menu with specified entries
+ ---@field open fun(self: neocomplete.menu, entries: neocomplete.entry[]): nil
+ @end
+
+** Close
+ #tangle
+ @code lua
+ --- Closes the window of the menu
+ ---@field close fun(self: neocomplete.menu): nil
+ @end
+
+** Get active entry
+ #tangle
+ @code lua
+ --- Gets the currently selected entry
+ ---@field get_active_entry fun(self: neocomplete.menu): neocomplete.entry?
+ @end
+
+** Confirm
+ #tangle
+ @code lua
+ --- Selects the current entry and inserts it's text
+ ---@field confirm fun(self: neocomplete.menu): nil
+ @end
+
+** confirm
+ #tangle
+ @code lua
+ --- Selects the current entry and inserts it's text
+ ---@field complete fun(self: neocomplete.menu, entry: neocomplete.entry): nil
+ @end
+
+* Fields
+** Entries
+ #tangle
+ @code lua
+ --- Entries of the menu
+ ---@field entries neocomplete.entry[]
+ @end
+
+** Namespace
+ #tangle
+ @code lua
+ --- Namespace used for the menu
+ ---@field ns integer
+ @end
+
+** Config
+ #tangle
+ @code lua
+ --- Neocomplete config to be used in the menu
+ ---@field config neocomplete.config
+ @end
+
+** Input
+ Currently just used to test highlighting match characters
+ #tangle
+ @code lua
+ --- Input of the user which is fuzzy matched
+ ---@field input string
+ @end
+
+** Buffer
+ Buffer used for the menu
+ #tangle
+ @code lua
+ --- Buffer of the menu
+ ---@field buf integer
+ @end
+
+** Window
+ #tangle
+ @code lua
+ --- Window of menu
+ ---@field winnr integer?
+ @end
+
+** Index
+ used to get and track the currently selected item
+ #tangle
+ @code lua
+ --- Index of selected item
+ ---@field index integer
+ @end
diff --git a/docs/misc.norg b/docs/misc.norg
new file mode 100644
index 0000000..52c3917
--- /dev/null
+++ b/docs/misc.norg
@@ -0,0 +1,33 @@
+@document.meta
+title: Misc
+description: Misc types
+authors: [
+ max397574
+]
+categories: [
+ docs,
+ types
+]
+created: 2024-05-29T11:30:25+0100
+updated: 2024-05-29T11:32:14+0100
+tangle: {
+ languages: {
+ lua: ../lua/neocomplete/types/misc.lua
+ }
+ scope: tagged
+ delimiter: none
+}
+version: 1.1.1
+@end
+
+
+* Completion context
+ a combination of context and lsp completion context
+ #tangle
+ @code lua
+ --- Context provided to completion sources
+ ---@class neocomplete.completion_context
+ ---@field completion_context lsp.CompletionContext
+ ---@field context neocomplete.context
+ @end
+
diff --git a/docs/source.norg b/docs/source.norg
new file mode 100644
index 0000000..0313fe1
--- /dev/null
+++ b/docs/source.norg
@@ -0,0 +1,79 @@
+@document.meta
+title: Source
+description: Type description of neocomplete.nvim source
+authors: [
+ max397574
+]
+categories: [
+ docs,
+ types
+]
+created: 2023-11-15T17:42:46+0100
+updated: 2024-05-29T11:33:36+0100
+tangle: {
+ languages: {
+ lua: ../lua/neocomplete/types/source.lua
+ }
+ scope: tagged
+ delimiter: none
+}
+version: 1.1.1
+@end
+
+TODO: decide about object-orientation of sources
+
+* General
+ The sources are used to get get completions for neocomplete.nvim.
+
+ #tangle
+ @code lua
+ --- A completion source for neocomplete.nvim
+ ---@class neocomplete.source
+ @end
+
+* Methods
+** `is_available()`
+ Each source can have a function to show whether it's available or not. If your source should
+ for example be enabled for a certain filetype you can just do it like this:
+ @code lua
+ function my_source.is_available()
+ return vim.bo.ft == "lua"
+ end
+ @end
+
+ This function will be called quite often so developers should try to keep it more or less
+ performant. This won't be an issue in the vast majority of cases though.
+
+ #tangle
+ @code lua
+ --- Whether the source will provide completions in the current context or not
+ ---@field is_available? fun(): boolean
+ @end
+
+** `get_trigger_characters()`
+ This function should return characters which trigger completion for the source. If one of those
+ characters is types the completion will be retriggered. Otherwise newly entered characters are
+ used for sorting and filtering.
+ An example for this could be `.`, `\\` and `/` when working with paths.
+ @code lua
+ function my_source.get_trigger_characters()
+ return { ".", "\\", "/" }
+ end
+ @end
+
+ #tangle
+ @code lua
+ --- Characters which should trigger new completions of the source
+ ---@field get_trigger_characters? fun(): string[]
+ @end
+
+** `complete()`
+ This is arguably the most important function of each source. This function returns completions.
+ The function takes in a {:./index:* Completion Context}[completion context] and should return a
+ list of {:./completion_item:}[completion items].
+
+ #tangle
+ @code lua
+ --- Returns completion in the provided context
+ ---@field complete fun(completion_context: neocomplete.completion_context, callback: fun(items: neocomplete.completion_item[])): neocomplete.completion_item[]
+ @end
diff --git a/lua/neocomplete/config.lua b/lua/neocomplete/config.lua
new file mode 100644
index 0000000..76d644d
--- /dev/null
+++ b/lua/neocomplete/config.lua
@@ -0,0 +1,78 @@
+local config = {}
+
+---@type neocomplete.config
+---@diagnostic disable-next-line: missing-fields
+config.options = {}
+
+---@type neocomplete.config
+config.defaults = {
+ ui = {
+ menu = {
+ max_height = 10,
+ border = "rounded",
+ format_entry = function(entry)
+ local type_icons = config.options.ui.type_icons
+ local entry_kind = type(entry.kind) == "string" and entry.kind
+ or require("neocomplete.utils.lsp").get_kind_name(entry.kind)
+ return {
+ { { entry.label .. " ", "@neocomplete.entry" } },
+ { { type_icons[entry_kind] or "", ("@neocomplete.type.%s"):format(entry_kind) } },
+ }
+ end,
+ alignment = {},
+ },
+ docs_view = {
+ max_height = 7,
+ border = "rounded",
+ },
+ type_icons = {
+ Class = " ",
+ Color = " ",
+ Constant = " ",
+ Constructor = " ",
+ Enum = " 了",
+ EnumMember = " ",
+ Event = " ",
+ Field = " ",
+ File = " ",
+ Folder = " ",
+ Function = " ",
+ Interface = " ",
+ Keyword = " ",
+ Method = " ƒ ",
+ Module = " ",
+ Operator = " ",
+ Property = " ",
+ Reference = " ",
+ Snippet = " ",
+ Struct = " ",
+ Text = " ",
+ TypeParameter = "",
+ Unit = " ",
+ Value = " ",
+ Variable = " ",
+ },
+ },
+ snippet_expansion = function(snippet_body)
+ vim.snippet.expand(snippet_body)
+ end,
+ enabled = function()
+ local enabled = true
+ if vim.api.nvim_get_option_value("buftype", { buf = 0 }) == "prompt" then
+ enabled = false
+ end
+ return enabled
+ end,
+}
+
+function config.setup(opts)
+ if vim.tbl_isempty(config.options) then
+ config.options = vim.tbl_deep_extend("force", config.defaults, opts or {})
+ else
+ config.options = vim.tbl_deep_extend("force", config.options, opts or {})
+ end
+end
+
+config.setup({})
+
+return config
diff --git a/lua/neocomplete/context.lua b/lua/neocomplete/context.lua
new file mode 100644
index 0000000..25ae99d
--- /dev/null
+++ b/lua/neocomplete/context.lua
@@ -0,0 +1,36 @@
+---@type neocomplete.context
+---@diagnostic disable-next-line: missing-fields
+local context = {}
+
+---@type lsp.CompletionContext
+
+---@param previous neocomplete.context?
+---@return neocomplete.context
+function context.new(previous)
+ local self = setmetatable({}, { __index = context })
+ previous = previous or {}
+ previous.previous = nil
+ self.previous = previous and vim.deepcopy(previous)
+ self.bufnr = vim.api.nvim_get_current_buf()
+ local cursor = vim.api.nvim_win_get_cursor(0)
+ self.cursor = { row = cursor[1], col = cursor[2] }
+ return self
+end
+
+function context.changed(self)
+ if not self.previous then
+ return true
+ end
+ if self.bufnr ~= self.previous.bufnr then
+ return true
+ end
+ if self.cursor.col ~= self.previous.cursor.col then
+ return true
+ end
+ if self.cursor.row ~= self.previous.cursor.row then
+ return true
+ end
+ return false
+end
+
+return context
diff --git a/lua/neocomplete/core.lua b/lua/neocomplete/core.lua
new file mode 100644
index 0000000..8691728
--- /dev/null
+++ b/lua/neocomplete/core.lua
@@ -0,0 +1,52 @@
+---@type neocomplete.core
+---@diagnostic disable-next-line: missing-fields
+local core = {}
+
+function core.new()
+ ---@type neocomplete.core
+ local self = setmetatable({}, { __index = core })
+ self.context = require("neocomplete.context").new()
+ self.menu = require("neocomplete.menu").new()
+ return self
+end
+
+function core:complete()
+ local sources = require("neocomplete.sources").get_sources()
+ local entries = {}
+ local remaining = #sources
+ for _, source in ipairs(sources) do
+ if source.is_available() then
+ require("neocomplete.sources").complete(self.context, source, function(items)
+ remaining = remaining - 1
+ if items and not vim.tbl_isempty(items) then
+ vim.list_extend(entries, items)
+ vim.schedule(function()
+ if remaining == 0 then
+ self.menu:open(entries)
+ end
+ end)
+ end
+ end)
+ else
+ remaining = remaining - 1
+ end
+ end
+end
+
+function core.setup(self)
+ vim.api.nvim_create_autocmd("TextChangedI", {
+ callback = function()
+ self:on_change()
+ end,
+ group = "neocomplete",
+ })
+end
+
+function core.on_change(self)
+ if self.context and (not self.context:changed()) then
+ return
+ end
+ self:complete()
+end
+
+return core
diff --git a/lua/neocomplete/highlights.lua b/lua/neocomplete/highlights.lua
new file mode 100644
index 0000000..4480434
--- /dev/null
+++ b/lua/neocomplete/highlights.lua
@@ -0,0 +1,19 @@
+--[[
+@neocomplete: Fallback for everything
+@neocomplete.type: Fallback for the type highlights (only one defined)
+@neocomplete.selected: Selected entry
+@neocomplete.match: Matched part of entries
+--]]
+
+-- TODO: move into function?
+
+local hl = function(...)
+ vim.api.nvim_set_hl(0, ...)
+end
+
+hl("@neocomplete", { link = "Normal", default = true })
+hl("@neocomplete.type", { link = "Normal", default = true })
+hl("@neocomplete.selected", { link = "Visual", default = true })
+hl("@neocomplete.match", { link = "Special", default = true })
+hl("@neocomplete.menu", { link = "NormalFloat", default = true })
+hl("@neocomplete.entry", { italic = true })
diff --git a/lua/neocomplete/init.lua b/lua/neocomplete/init.lua
new file mode 100644
index 0000000..823f733
--- /dev/null
+++ b/lua/neocomplete/init.lua
@@ -0,0 +1,26 @@
+local neocomplete = {}
+
+---@type neocomplete.core
+neocomplete.core = nil
+
+local function on_insert_enter()
+ neocomplete.core = require("neocomplete.core").new()
+ neocomplete.core:setup()
+end
+
+--- Sets up neocomplete
+function neocomplete.setup()
+ require("neocomplete.config").setup()
+ require("neocomplete.highlights")
+
+ local augroup = vim.api.nvim_create_augroup("neocomplete", {})
+ vim.api.nvim_create_autocmd("InsertEnter", {
+ callback = function()
+ on_insert_enter()
+ end,
+ once = true,
+ group = augroup,
+ })
+end
+
+return neocomplete
diff --git a/lua/neocomplete/lsp_source.lua b/lua/neocomplete/lsp_source.lua
new file mode 100644
index 0000000..bbefa0b
--- /dev/null
+++ b/lua/neocomplete/lsp_source.lua
@@ -0,0 +1,95 @@
+local lsp_source = {}
+
+--- @alias vim.lsp.CompletionResult lsp.CompletionList | lsp.CompletionItem[]
+
+lsp_source.clients = {}
+
+-- function lsp_source.setup()
+-- ---@type neocomplete.source
+-- ---@diagnostic disable-next-line: missing-fields
+-- local source = {}
+-- source.name = "nvim-lsp"
+-- require("neocomplete.sources").register_source(source)
+-- end
+
+---@param item lsp.CompletionItem
+---@param defaults lsp.ItemDefaults
+local function apply_defaults(item, defaults)
+ if not defaults then
+ return
+ end
+ ---@diagnostic disable-next-line: undefined-field
+ item.commitCharacters = item.commitCharacters or defaults.commitCharacters
+ if defaults.editRange then
+ item.textEdit = item.textEdit or {}
+ item.textEdit.newText = item.textEdit.newText or item.textEditText or item.insertText
+ if defaults.editRange.insert then
+ item.textEdit.insert = defaults.editRange.insert
+ item.textEdit.replace = defaults.editRange.replace
+ else
+ item.textEdit.range = item.textEdit.range or defaults.editRange
+ end
+ end
+ item.insertTextFormat = item.insertTextFormat or defaults.insertTextFormat
+ item.insertTextMode = item.insertTextMode or defaults.insertTextMode
+ item.data = item.data or defaults.data
+end
+
+--- @param result vim.lsp.CompletionResult
+--- @return lsp.CompletionItem[]
+local function get_items(result)
+ if result.items then
+ for _, item in ipairs(result.items) do
+ ---@diagnostic disable-next-line: param-type-mismatch
+ apply_defaults(item, result.itemDefaults)
+ end
+ return result.items
+ else
+ return result
+ end
+end
+
+---@param client vim.lsp.Client
+---@return neocomplete.source
+function lsp_source.new(client)
+ ---@type neocomplete.source
+ local source = {
+ name = "nvim-lsp " .. client.name,
+ ---@param context neocomplete.completion_context
+ complete = function(context, callback)
+ local params = vim.lsp.util.make_position_params(0, client.offset_encoding)
+ params.context = context.completion_context
+ ---@type lsp.CompletionItem
+ local items
+ client.request(vim.lsp.protocol.Methods.textDocument_completion, params, function(err, result)
+ if err then
+ vim.print(err)
+ end
+ if result then
+ items = get_items(result)
+ end
+ callback(items)
+ end)
+ end,
+ get_trigger_characters = function()
+ return client.server_capabilities.completionProvider.triggerCharacters
+ end,
+ is_available = function()
+ return not client.is_stopped()
+ end,
+ }
+ return source
+end
+
+vim.api.nvim_create_autocmd("InsertEnter", {
+ callback = function()
+ -- TODO: always check if clients are from buf and not `is_stopped()`
+ for _, client in ipairs(vim.lsp.get_clients()) do
+ if not lsp_source.clients[client.id] then
+ local source = lsp_source.new(client)
+ lsp_source.clients[client.id] = source
+ require("neocomplete.sources").register_source(source)
+ end
+ end
+ end,
+})
diff --git a/lua/neocomplete/menu.lua b/lua/neocomplete/menu.lua
new file mode 100644
index 0000000..bccadfd
--- /dev/null
+++ b/lua/neocomplete/menu.lua
@@ -0,0 +1,280 @@
+---@type neocomplete.menu
+---@diagnostic disable-next-line: missing-fields
+local Menu = {}
+
+local format_utils = require("neocomplete.utils.format")
+local utils = require("neocomplete.utils")
+
+function Menu.new()
+ ---@type neocomplete.menu
+ local self = setmetatable({}, { __index = Menu })
+ self.entries = nil
+ self.ns = vim.api.nvim_create_namespace("neocomplete")
+ self.config = require("neocomplete.config").options
+ self.input = "e"
+ self.buf = vim.api.nvim_create_buf(false, true)
+ self.winnr = nil
+ self.index = 0
+ return self
+end
+
+local function get_texts(aligned_sec)
+ local texts = {}
+ for _, aligned_chunks in ipairs(aligned_sec) do
+ local line_text = {}
+ for _, chunk in ipairs(aligned_chunks) do
+ table.insert(line_text, chunk[1])
+ end
+ table.insert(texts, table.concat(line_text, ""))
+ end
+ return texts
+end
+
+--- Realigns chunks and adds extmarks
+---@param aligned_sec table
+---@param realign function(chunk: {[1]: string, [2]: number}): {[1]: string, [2]: number}
+---@param buf integer
+---@param ns integer
+---@param column integer
+local function add_extmarks(aligned_sec, realign, buf, ns, column)
+ for line, aligned_chunks in ipairs(aligned_sec) do
+ local realigned_chunks = {}
+ for _, chunk in ipairs(aligned_chunks) do
+ table.insert(realigned_chunks, realign(chunk))
+ end
+ vim.api.nvim_buf_set_extmark(buf, ns, line - 1, column, {
+ virt_text = realigned_chunks,
+ virt_text_pos = "overlay",
+ hl_mode = "combine",
+ })
+ end
+end
+
+function Menu:draw()
+ local alignment = self.config.ui.menu.alignment
+ local width, entry_texts = format_utils.get_width(self.entries)
+ local aligned_table = format_utils.get_align_tables(self.entries)
+ local column = 0
+ vim.api.nvim_buf_clear_namespace(self.buf, self.ns, 0, -1)
+ local spaces = {}
+ for _ = 1, #self.entries do
+ table.insert(spaces, (" "):rep(width))
+ end
+ vim.api.nvim_buf_set_lines(self.buf, 0, -1, false, spaces)
+ if self.index and self.index > 0 then
+ for i = 0, #self.entries do
+ if i == self.index then
+ vim.api.nvim_buf_set_extmark(self.buf, self.ns, i - 1, 0, {
+ virt_text = { { string.rep(" ", width), "@neocomplete.selected" } },
+ virt_text_pos = "overlay",
+ })
+ end
+ end
+ end
+ for i, aligned_sec in ipairs(aligned_table) do
+ if not alignment[i] or alignment[i] == "left" then
+ local texts = {}
+ for line, aligned_chunks in ipairs(aligned_sec) do
+ local line_text = {}
+ for _, chunk in ipairs(aligned_chunks) do
+ table.insert(line_text, chunk[1])
+ end
+ local cur_line_text = table.concat(line_text, "")
+ table.insert(texts, cur_line_text)
+ vim.api.nvim_buf_set_extmark(self.buf, self.ns, line - 1, column, {
+ virt_text = aligned_chunks,
+ virt_text_pos = "overlay",
+ hl_mode = "combine",
+ })
+ end
+ column = column + utils.longest(texts)
+ elseif alignment[i] == "right" then
+ local texts = get_texts(aligned_sec)
+ local length = utils.longest(texts)
+ add_extmarks(aligned_sec, function(chunk)
+ return { string.rep(" ", length - #chunk[1]) .. chunk[1], chunk[2] }
+ end, self.buf, self.ns, column)
+ column = column + length
+ elseif alignment[i] == "center" then
+ local texts = get_texts(aligned_sec)
+ local length = utils.longest(texts)
+ add_extmarks(aligned_sec, function(chunk)
+ return { string.rep(" ", math.floor((length - #chunk[1]) / 2)) .. chunk[1], chunk[2] }
+ end, self.buf, self.ns, column)
+ column = column + length
+ end
+ end
+ -- TODO: do properly once filtering is implemented
+ -- for line, entry_text in ipairs(entry_texts) do
+ -- for char_idx = 1, #entry_text do
+ -- local char = entry_text:sub(char_idx, char_idx)
+ -- if self.input:find(char, 1, true) then
+ -- vim.api.nvim_buf_add_highlight(
+ -- self.buf,
+ -- self.ns,
+ -- "@neocomplete.match",
+ -- line - 1,
+ -- char_idx - 1,
+ -- char_idx
+ -- )
+ -- end
+ -- end
+ -- end
+end
+
+function Menu:open_win()
+ local width, _ = format_utils.get_width(self.entries)
+ Menu.winnr = vim.api.nvim_open_win(self.buf, false, {
+ relative = "cursor",
+ height = math.min(#self.entries, self.config.ui.menu.max_height),
+ width = width,
+ style = "minimal",
+ border = self.config.ui.menu.border,
+ row = 1,
+ col = 1,
+ })
+ vim.wo[self.winnr][self.buf].scrolloff = 0
+end
+
+function Menu:close()
+ pcall(vim.api.nvim_win_close, self.winnr, true)
+ Menu.winnr = nil
+end
+
+function Menu:select_next(count)
+ self.index = self.index + count
+ if self.index > #self.entries then
+ self.index = self.index - #self.entries - 1
+ end
+ if self.index > (vim.fn.line("w$", self.winnr) - vim.fn.line("w0", self.winnr)) then
+ vim.api.nvim_win_call(self.winnr, function()
+ vim.cmd("normal! " .. self.index - (vim.fn.line("w$", self.winnr) - vim.fn.line("w0", self.winnr)) .. "zt")
+ end)
+ end
+ self:draw()
+end
+
+function Menu:select_prev(count)
+ self.index = self.index - count
+ if self.index < 0 then
+ self.index = #self.entries + self.index + 1
+ end
+ if self.index < (vim.fn.line("w0", self.winnr)) then
+ vim.api.nvim_win_call(self.winnr, function()
+ vim.cmd("normal! " .. self.index .. "zt")
+ end)
+ end
+ self:draw()
+end
+
+function Menu:open(entries)
+ self.entries = entries
+ if not entries or #entries < 1 then
+ return
+ end
+ if self.winnr then
+ self:close()
+ end
+ self.index = 0
+ if not self.winnr then
+ self:open_win()
+ self:draw()
+ end
+end
+
+function Menu:get_active_entry()
+ if not self.entries then
+ return nil
+ end
+ -- TODO: make configurable (cmpts "autoselect")
+ if self.index == 0 then
+ return self.entries[1]
+ end
+ return self.entries[self.index]
+end
+
+---@param entry neocomplete.entry
+---@return neocomplete.entry
+local function normalize_entry(entry)
+ entry.insertTextFormat = entry.insertTextFormat or 1
+ -- TODO: make this earlier because the sorting won't happen here
+ -- TODO: perhaps remove? are these fields even relevant to complete?
+ entry.filterText = entry.filterText or entry.label
+ entry.sortText = entry.sortText or entry.label
+ entry.insertText = entry.insertText or entry.label
+ return entry
+end
+
+---@param entry neocomplete.entry
+function Menu:complete(entry)
+ entry = normalize_entry(entry)
+ local current_buf = vim.api.nvim_get_current_buf()
+ local cursor_row, cursor_col = unpack(vim.api.nvim_win_get_cursor(0)) --- @type integer, integer
+ -- get cursor uses 1 based lines, rest of api 0 based
+ cursor_row = cursor_row - 1
+ local line = vim.api.nvim_get_current_line()
+ local line_to_cursor = line:sub(1, cursor_col)
+ -- TODO: will this work for all sources?
+ local word_boundary = vim.fn.match(line_to_cursor, "\\k*$")
+
+ local prefix = line:sub(word_boundary + 1, cursor_col)
+
+ -- TODO: entry.insertTextMode
+ local is_snippet = entry.insertTextFormat == 2
+
+ if entry.textEdit and not is_snippet then
+ -- An edit which is applied to a document when selecting this completion.
+ -- When an edit is provided the value of `insertText` is ignored.
+
+ if entry.textEdit.range then
+ vim.lsp.util.apply_text_edits({ entry.textEdit }, current_buf, "utf-8")
+ else
+ -- TODO: config option to determine whether to pick insert or replace
+ local textEdit = { range = entry.textEdit.insert, newText = entry.textEdit.newText }
+ vim.lsp.util.apply_text_edits({ textEdit }, current_buf, "utf-8")
+ end
+ elseif entry.textEdit and is_snippet then
+ local textEdit
+ if entry.textEdit.range then
+ textEdit = { range = entry.textEdit.range, newText = "" }
+ else
+ -- TODO: config option to determine whether to pick insert or replace
+ textEdit = { range = entry.textEdit.insert, newText = "" }
+ end
+ vim.lsp.util.apply_text_edits({ textEdit }, current_buf, "utf-8")
+ vim.api.nvim_win_set_cursor(0, { textEdit.range.start.line + 1, textEdit.range.start.character })
+ self.config.snippet_expansion(entry.textEdit.newText)
+ else
+ -- TODO: confirm this is correct
+ -- remove prefix which was used for sorting, text edit should remove it
+ ---@see lsp.CompletionItem.insertText
+ local start_char = cursor_col - #prefix
+ vim.api.nvim_buf_set_text(0, cursor_row, start_char, cursor_row, start_char + #prefix, { "" })
+ if is_snippet then
+ self.config.snippet_expansion(entry.insertText)
+ else
+ -- TODO: check if should be `start_char - 1`
+ vim.api.nvim_buf_set_text(0, cursor_row, start_char, cursor_row, start_char, { entry.insertText })
+ end
+ end
+
+ if entry.additionalTextEdits and #entry.additionalTextEdits > 0 then
+ vim.lsp.util.apply_text_edits(entry.additionalTextEdits, current_buf, "utf-8")
+ end
+
+ if entry.command then
+ ---@diagnostic disable-next-line: param-type-mismatch
+ vim.lsp.buf.execute_command(entry.command)
+ end
+end
+
+function Menu:confirm()
+ local entry = self:get_active_entry()
+ if not entry then
+ return
+ end
+ self:complete(entry)
+ self:close()
+end
+
+return Menu
diff --git a/lua/neocomplete/sources.lua b/lua/neocomplete/sources.lua
new file mode 100644
index 0000000..465da32
--- /dev/null
+++ b/lua/neocomplete/sources.lua
@@ -0,0 +1,35 @@
+local neocomplete_sources = {}
+neocomplete_sources.sources = {}
+
+---@param source neocomplete.source
+function neocomplete_sources.register_source(source)
+ table.insert(neocomplete_sources.sources, source)
+end
+
+---@return neocomplete.source[]
+function neocomplete_sources.get_sources()
+ return vim.deepcopy(neocomplete_sources.sources)
+end
+
+---@param context neocomplete.context
+---@param source neocomplete.source
+---@param callback fun(items: neocomplete.entry[])
+function neocomplete_sources.complete(context, source, callback)
+ local cursor = vim.api.nvim_win_get_cursor(0)
+ local last_char = vim.api.nvim_get_current_line():sub(cursor[2], cursor[2])
+ ---@type lsp.CompletionContext
+ local completion_context
+ if vim.tbl_contains(source.get_trigger_characters(), last_char) then
+ completion_context = {
+ triggerKind = 2,
+ triggerCharacter = last_char,
+ }
+ else
+ completion_context = {
+ triggerKind = 1,
+ }
+ end
+ source.complete({ completion_context = completion_context, context = context }, callback)
+end
+
+return neocomplete_sources
diff --git a/lua/neocomplete/types/completion_item.lua b/lua/neocomplete/types/completion_item.lua
new file mode 100644
index 0000000..9bc9ea1
--- /dev/null
+++ b/lua/neocomplete/types/completion_item.lua
@@ -0,0 +1,4 @@
+--- An item returned by a completion source
+---@class neocomplete.completion_item: lsp.CompletionItem
+--- The kind of the completion item
+---@field kind? string|lsp.CompletionItemKind
diff --git a/lua/neocomplete/types/config.lua b/lua/neocomplete/types/config.lua
new file mode 100644
index 0000000..7ec23e9
--- /dev/null
+++ b/lua/neocomplete/types/config.lua
@@ -0,0 +1,34 @@
+--- Configuration for neocomplete.nvim
+---@class neocomplete.config
+--- Configuration for the ui of neocomplete
+---@field ui neocomplete.config.ui
+--- Function used to expand snippets
+---@field snippet_expansion fun(string): nil
+--- Configuration for the ui of neocomplete
+---@field enabled fun(): boolean
+--- The main class for the ui configuration of neocomplete.nvim
+---@class neocomplete.config.ui
+--- Configuration of the completion menu of neocomplete.nvim
+---@field menu neocomplete.config.ui.menu
+--- Configuration of the documentation view of neocomplete.nvim
+---@field docs_view neocomplete.config.ui.docs
+--- The icons for the different compltion item kinds
+---@field type_icons neocomplete.config.ui.type_icons
+--- Configuration of the completion menu of neocomplete.nvim
+---@class neocomplete.config.ui.menu
+--- Maximum height of the menu
+---@field max_height integer
+--- The border of the completion menu
+---@field border string|string[]|string[][]
+--- How an entry should be formatted
+---@field format_entry fun(neocomplete.entry): { [1]: string, [2]: string }[][]
+--- How the sections in the menu should be aligned
+---@field alignment ("left"|"center"|"right")[]
+--- Configuration of the completion menu of neocomplete.nvim
+---@class neocomplete.config.ui.docs
+--- Maximum height of the documentation view
+---@field max_height integer
+--- The border of the documentation view
+---@field border string|string[]|string[][]
+--- The icons used for the different completion item types
+---@alias neocomplete.config.ui.type_icons table
diff --git a/lua/neocomplete/types/context.lua b/lua/neocomplete/types/context.lua
new file mode 100644
index 0000000..b9df536
--- /dev/null
+++ b/lua/neocomplete/types/context.lua
@@ -0,0 +1,16 @@
+--- Context provided to completion sources
+---@class neocomplete.context
+--- Context changed since previous
+---@field changed fun(neocomplete.context): boolean
+--- Create a new context
+---@field new fun(neocomplete.context?): neocomplete.context
+--- The previous context
+---@field previous neocomplete.context?
+--- The cursor position
+---@field cursor neocomplete.context.cursor
+--- The number of the buffer
+---@field bufnr integer
+--- A cursor position
+---@class neocomplete.context.cursor
+---@field row integer
+---@field col integer
diff --git a/lua/neocomplete/types/core.lua b/lua/neocomplete/types/core.lua
new file mode 100644
index 0000000..39c10e5
--- /dev/null
+++ b/lua/neocomplete/types/core.lua
@@ -0,0 +1,14 @@
+--- The core of neocomplete where the main api functions are defined
+---@class neocomplete.core
+--- Create a new instance of the core
+---@field new fun(): neocomplete.core
+--- Context instance of the core
+---@field context neocomplete.context
+--- Menu instance of the core
+---@field menu neocomplete.menu
+--- Complete
+---@field complete fun(neocomplete.core): nil
+--- The function that gets invoked when the text changes
+---@field on_change fun(neocomplete.core): nil
+--- Setup core (for now autocommands)
+---@field setup fun(self: neocomplete.core): nil
diff --git a/lua/neocomplete/types/entry.lua b/lua/neocomplete/types/entry.lua
new file mode 100644
index 0000000..e1b12cd
--- /dev/null
+++ b/lua/neocomplete/types/entry.lua
@@ -0,0 +1,2 @@
+--- An entry for the neocomplete completion menu
+---@class neocomplete.entry: lsp.CompletionItem
diff --git a/lua/neocomplete/types/menu.lua b/lua/neocomplete/types/menu.lua
new file mode 100644
index 0000000..a2ccf3c
--- /dev/null
+++ b/lua/neocomplete/types/menu.lua
@@ -0,0 +1,36 @@
+--- The completion menu of neocomplete
+---@class neocomplete.menu
+--- Create a new instance of the menu
+---@field new fun(): neocomplete.menu
+--- Draws the menu
+---@field draw fun(self: neocomplete.menu): nil
+--- Opens window for the menu
+---@field open_win fun(self: neocomplete.menu): nil
+--- Select next entry in menu
+---@field select_next fun(self: neocomplete.menu, count: integer): nil
+--- Select previous entry in menu
+---@field select_prev fun(self: neocomplete.menu, count: integer): nil
+--- Opens the menu with specified entries
+---@field open fun(self: neocomplete.menu, entries: neocomplete.entry[]): nil
+--- Closes the window of the menu
+---@field close fun(self: neocomplete.menu): nil
+--- Gets the currently selected entry
+---@field get_active_entry fun(self: neocomplete.menu): neocomplete.entry?
+--- Selects the current entry and inserts it's text
+---@field confirm fun(self: neocomplete.menu): nil
+--- Selects the current entry and inserts it's text
+---@field complete fun(self: neocomplete.menu, entry: neocomplete.entry): nil
+--- Entries of the menu
+---@field entries neocomplete.entry[]
+--- Namespace used for the menu
+---@field ns integer
+--- Neocomplete config to be used in the menu
+---@field config neocomplete.config
+--- Input of the user which is fuzzy matched
+---@field input string
+--- Buffer of the menu
+---@field buf integer
+--- Window of menu
+---@field winnr integer?
+--- Index of selected item
+---@field index integer
\ No newline at end of file
diff --git a/lua/neocomplete/types/misc.lua b/lua/neocomplete/types/misc.lua
new file mode 100644
index 0000000..3d58e4f
--- /dev/null
+++ b/lua/neocomplete/types/misc.lua
@@ -0,0 +1,4 @@
+--- Context provided to completion sources
+---@class neocomplete.completion_context
+---@field completion_context lsp.CompletionContext
+---@field context neocomplete.context
diff --git a/lua/neocomplete/types/source.lua b/lua/neocomplete/types/source.lua
new file mode 100644
index 0000000..433f0b3
--- /dev/null
+++ b/lua/neocomplete/types/source.lua
@@ -0,0 +1,8 @@
+--- A completion source for neocomplete.nvim
+---@class neocomplete.source
+--- Whether the source will provide completions in the current context or not
+---@field is_available? fun(): boolean
+--- Characters which should trigger new completions of the source
+---@field get_trigger_characters? fun(): string[]
+--- Returns completion in the provided context
+---@field complete fun(completion_context: neocomplete.completion_context, callback: fun(items: neocomplete.completion_item[])): neocomplete.completion_item[]
diff --git a/lua/neocomplete/utils/async.lua b/lua/neocomplete/utils/async.lua
new file mode 100644
index 0000000..0ef6407
--- /dev/null
+++ b/lua/neocomplete/utils/async.lua
@@ -0,0 +1,3 @@
+local async = {}
+
+return async
diff --git a/lua/neocomplete/utils/format.lua b/lua/neocomplete/utils/format.lua
new file mode 100644
index 0000000..81bc78b
--- /dev/null
+++ b/lua/neocomplete/utils/format.lua
@@ -0,0 +1,39 @@
+local format_utils = {}
+
+local config = require("neocomplete.config").options
+local utils = require("neocomplete.utils")
+
+--- Gets the width a window for displaying entries must have
+---@return number, string[]
+function format_utils.get_width(entries)
+ local formatted_concat = {}
+ for _, entry in ipairs(entries) do
+ local formatted = config.ui.menu.format_entry(entry)
+ local chunk_texts = {}
+ for _, aligned in ipairs(formatted) do
+ for _, chunk in ipairs(aligned) do
+ table.insert(chunk_texts, chunk[1])
+ end
+ end
+ table.insert(formatted_concat, table.concat(chunk_texts, ""))
+ end
+ return utils.longest(formatted_concat), formatted_concat
+end
+
+--- Gets a table with one table for each aligned chunk inside
+---@return table
+function format_utils.get_align_tables(entries)
+ local aligned_table = {}
+ for _, entry in ipairs(entries) do
+ local formatted = config.ui.menu.format_entry(entry)
+ for aligned_index, aligned_chunks in ipairs(formatted) do
+ if not aligned_table[aligned_index] then
+ aligned_table[aligned_index] = {}
+ end
+ table.insert(aligned_table[aligned_index], aligned_chunks)
+ end
+ end
+ return aligned_table
+end
+
+return format_utils
diff --git a/lua/neocomplete/utils/init.lua b/lua/neocomplete/utils/init.lua
new file mode 100644
index 0000000..b925f88
--- /dev/null
+++ b/lua/neocomplete/utils/init.lua
@@ -0,0 +1,16 @@
+local utils = {}
+
+--- Gets lenght of longest string of an array
+---@param lines table
+---@return number
+function utils.longest(lines)
+ local longest = #(lines[1] or "")
+ for _, line in ipairs(lines) do
+ if #line > longest then
+ longest = #line
+ end
+ end
+ return longest
+end
+
+return utils
diff --git a/lua/neocomplete/utils/lsp.lua b/lua/neocomplete/utils/lsp.lua
new file mode 100644
index 0000000..f45d17a
--- /dev/null
+++ b/lua/neocomplete/utils/lsp.lua
@@ -0,0 +1,36 @@
+local lsp_utils = {}
+
+--- Gets the name for an lsp kind
+---@param kind_number lsp.CompletionItemKind
+function lsp_utils.get_kind_name(kind_number)
+ local lsp_kinds = {
+ "Text",
+ "Method",
+ "Function",
+ "Constructor",
+ "Field",
+ "Variable",
+ "Class",
+ "Interface",
+ "Module",
+ "Property",
+ "Unit",
+ "Value",
+ "Enum",
+ "Keyword",
+ "Snippet",
+ "Color",
+ "File",
+ "Reference",
+ "Folder",
+ "EnumMember",
+ "Constant",
+ "Struct",
+ "Event",
+ "Operator",
+ "TypeParameter",
+ }
+ return lsp_kinds[kind_number] and lsp_kinds[kind_number] or ""
+end
+
+return lsp_utils
diff --git a/plugin/neocomplete.lua b/plugin/neocomplete.lua
new file mode 100644
index 0000000..1ececbc
--- /dev/null
+++ b/plugin/neocomplete.lua
@@ -0,0 +1,5 @@
+local loaded_neocomplete = false
+if not loaded_neocomplete then
+ require("neocomplete").setup()
+ loaded_neocomplete = true
+end
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..6e9a19d
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,10 @@
+# neocomplete.nvim
+
+initialized automatically
+
+use require"neocomplete.config".setup({...}) to configure
+
+credits
+mariasolos work in core and core in general
+nvim-cmp
+mfussenegger for helping me out
diff --git a/stylua.toml b/stylua.toml
new file mode 100644
index 0000000..bab5533
--- /dev/null
+++ b/stylua.toml
@@ -0,0 +1,6 @@
+column_width = 120
+line_endings = "Unix"
+indent_type = "Spaces"
+indent_width = 4
+quote_style = "AutoPreferDouble"
+call_parentheses = "Always"
diff --git a/test.lua b/test.lua
new file mode 100644
index 0000000..9c94d28
--- /dev/null
+++ b/test.lua
@@ -0,0 +1,24 @@
+require("neocomplete.lsp_source")
+vim.keymap.set("i", "", function()
+ vim.snippet.jump(1)
+end)
+vim.keymap.set("i", "", function()
+ vim.snippet.jump(-1)
+end)
+vim.keymap.set("i", "", function()
+ require("neocomplete").core.menu:select_next(1)
+end)
+vim.keymap.set("i", "", function()
+ require("neocomplete").core.menu:close()
+end)
+vim.keymap.set("i", "", function()
+ require("neocomplete").core.menu:select_prev(1)
+end)
+vim.keymap.set("i", "", function()
+ require("neocomplete").core.menu:confirm()
+end)
+vim.api.nvim_create_autocmd("InsertLeave", {
+ callback = function()
+ require("neocomplete").core.menu:close()
+ end,
+})