Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c99944f26 | |||
| 29f321995d | |||
| 9d52b9364f | |||
| 86504eec55 | |||
| d75196b4f9 | |||
| 5b90a8e5f9 | |||
| bbdc1aa849 | |||
| d11a99ee61 | |||
| 8130a78ed5 | |||
| fe04e624db |
95
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
95
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
name: 🐛 Bug Report
|
||||
description: Create a bug report to help us improve Avante
|
||||
title: 'bug: '
|
||||
labels: ['bug']
|
||||
body:
|
||||
- type: markdown
|
||||
id: issue-already-exists
|
||||
attributes:
|
||||
value: |
|
||||
Please search to see if an issue already exists for the bug you encountered.
|
||||
See [Searching Issues and Pull Requests](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests) for how to use the GitHub search bar and filters.
|
||||
- type: textarea
|
||||
id: describe-the-bug
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: Please provide a clear and concise description about the problem you ran into.
|
||||
placeholder: This happened when I ...
|
||||
- type: textarea
|
||||
id: to-reproduce
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: To reproduce
|
||||
description: |
|
||||
Please provide a code sample or a code snippet to reproduce said problem. If you have code snippets, error messages, or a stack trace please also provide them here.
|
||||
|
||||
**IMPORTANT**: make sure to use [code tags](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) to correctly format your code. Screenshots are helpful but don't use them for code snippets as they don't allow others to copy-and-paste your code.
|
||||
|
||||
placeholder: |
|
||||
Give a minimal config to reproduce the issue.
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: 'A clear and concise description of what you would expect to happen.'
|
||||
- type: textarea
|
||||
id: how-to-install
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Installation method
|
||||
description: |
|
||||
Please share your installation method with us.
|
||||
value: |
|
||||
Use lazy.nvim:
|
||||
```lua
|
||||
{
|
||||
"yetone/avante.nvim",
|
||||
event = "VeryLazy",
|
||||
lazy = false,
|
||||
version = false, -- set this if you want to always pull the latest change
|
||||
opts = {
|
||||
-- add any opts here
|
||||
},
|
||||
-- if you want to build from source then do `make BUILD_FROM_SOURCE=true`
|
||||
build = "make",
|
||||
-- build = "powershell -ExecutionPolicy Bypass -File Build.ps1 -BuildFromSource false" -- for windows
|
||||
dependencies = {
|
||||
"nvim-lua/plenary.nvim",
|
||||
"MunifTanjim/nui.nvim",
|
||||
},
|
||||
}
|
||||
```
|
||||
- type: textarea
|
||||
id: environment-info
|
||||
attributes:
|
||||
label: Environment
|
||||
description: |
|
||||
Please share your environment with us, including your neovim version using `nvim -v` and `uname -a`.
|
||||
placeholder: |
|
||||
neovim version: ...
|
||||
distribution (if any): ...
|
||||
platform: ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Repro
|
||||
description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua`
|
||||
value: |
|
||||
vim.env.LAZY_STDPATH = ".repro"
|
||||
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()
|
||||
|
||||
require("lazy.minit").repro({
|
||||
spec = {
|
||||
-- add any other plugins here
|
||||
},
|
||||
})
|
||||
render: lua
|
||||
validations:
|
||||
required: false
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
2
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
blank_issues_enabled: true
|
||||
version: 2.1
|
||||
35
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
35
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: 🚀 Feature Request
|
||||
description: Submit a proposal/request for new Avante feature.
|
||||
title: 'feature: '
|
||||
labels: ['new-feature', 'enhancement']
|
||||
body:
|
||||
- type: textarea
|
||||
id: feature-request
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Feature request
|
||||
description: |
|
||||
A clear and concise description of the feature request.
|
||||
placeholder: |
|
||||
I would like it if...
|
||||
- type: textarea
|
||||
id: motivation
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: |
|
||||
Please outline the motivation for this feature request. Is your feature request related to a problem? e.g., I'm always frustrated when [...].
|
||||
If this is related to another issue, please link here too.
|
||||
If you have a current workaround, please also provide it here.
|
||||
placeholder: |
|
||||
This feature would solve ...
|
||||
- type: textarea
|
||||
id: other
|
||||
attributes:
|
||||
label: Other
|
||||
description: |
|
||||
Is there any way that you could help, e.g. by submitting a PR?
|
||||
placeholder: |
|
||||
I would love to contribute ...
|
||||
24
.github/workflows/close-stale-issues-and-prs.yaml
vendored
Normal file
24
.github/workflows/close-stale-issues-and-prs.yaml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
permissions:
|
||||
contents: write # only for delete-branch option
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
|
||||
stale-pr-message: 'This PR is stale because it has been open 14 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
|
||||
close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.'
|
||||
close-pr-message: 'This PR was closed because it has been stalled for 10 days with no activity.'
|
||||
days-before-issue-stale: 30
|
||||
days-before-pr-stale: 14
|
||||
days-before-issue-close: 5
|
||||
days-before-pr-close: 10
|
||||
86
.github/workflows/lua.yaml
vendored
Normal file
86
.github/workflows/lua.yaml
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
name: Lua CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**/*.lua"
|
||||
- .github/workflows/lua.yaml
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**/*.lua"
|
||||
- .github/workflows/lua.yaml
|
||||
|
||||
jobs:
|
||||
# reference from: https://github.com/nvim-lua/plenary.nvim/blob/2d9b06177a975543726ce5c73fca176cedbffe9d/.github/workflows/default.yml#L6C3-L43C20
|
||||
run_tests:
|
||||
name: unit tests
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
rev: v0.10.0
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- id: todays-date
|
||||
run: echo "date=$(date +%F)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore cache for today's nightly.
|
||||
id: cache-neovim
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: _neovim
|
||||
key: ${{ runner.os }}-${{ matrix.rev }}-${{ steps.todays-date.outputs.date }}
|
||||
|
||||
- name: Download neovim ${{ matrix.rev }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
NEOVIM_VERSION: ${{ matrix.rev }}
|
||||
if: steps.cache-neovim.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p _neovim
|
||||
gh release download \
|
||||
--output - \
|
||||
--pattern nvim-linux64.tar.gz \
|
||||
--repo neovim/neovim \
|
||||
"$NEOVIM_VERSION" | tar xvz --strip-components 1 --directory _neovim
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ripgrep
|
||||
sudo apt-get install -y silversearcher-ag
|
||||
echo "${PWD}/_neovim/bin" >> "$GITHUB_PATH"
|
||||
echo VIM="${PWD}/_neovim/share/nvim/runtime" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
nvim --version
|
||||
make luatest
|
||||
|
||||
typecheck:
|
||||
name: Typecheck
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
nvim_version: [ stable ]
|
||||
luals_version: [ 3.13.6 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: rhysd/action-setup-vim@v1
|
||||
with:
|
||||
neovim: true
|
||||
version: ${{ matrix.nvim_version }}
|
||||
|
||||
- name: Typecheck
|
||||
env:
|
||||
VIMRUNTIME: /home/runner/nvim-${{ matrix.nvim_version }}/share/nvim/runtime
|
||||
run: |
|
||||
make lua-typecheck
|
||||
38
.github/workflows/pre-commit.yaml
vendored
Normal file
38
.github/workflows/pre-commit.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: pre-commit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, opened, reopened, synchronize]
|
||||
push:
|
||||
branches: [main, test-me-*]
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
if: "github.event.action != 'labeled' || github.event.label.name == 'pre-commit ci run'"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: gh pr edit ${{ github.event.number }} --remove-label 'pre-commit ci run'
|
||||
if: github.event.action == 'labeled' && github.event.label.name == 'pre-commit ci run'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
- uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
- run: |
|
||||
uv venv
|
||||
source .venv/bin/activate
|
||||
uv pip install -r py/rag-service/requirements.txt
|
||||
- uses: leafo/gh-actions-lua@v11
|
||||
- uses: leafo/gh-actions-luarocks@v5
|
||||
- run: luarocks install luacheck
|
||||
- name: Install stylua from crates.io
|
||||
uses: baptiste0928/cargo-install@v3
|
||||
with:
|
||||
crate: stylua
|
||||
args: --features lua54
|
||||
- uses: pre-commit/action@v3.0.1
|
||||
- uses: pre-commit-ci/lite-action@v1.1.0
|
||||
if: always()
|
||||
185
.github/workflows/release.yaml
vendored
Normal file
185
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [v\d+\.\d+\.\d+]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
release_id: ${{ steps.create-release.outputs.id }}
|
||||
release_upload_url: ${{ steps.create-release.outputs.upload_url }}
|
||||
release_body: "${{ steps.tag.outputs.message }}"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4
|
||||
|
||||
- name: Get version
|
||||
id: get_version
|
||||
uses: battila7/get-version-action@d97fbc34ceb64d1f5d95f4dfd6dce33521ccccf5 # ratchet:battila7/get-version-action@v2
|
||||
|
||||
- name: Get tag message
|
||||
id: tag
|
||||
run: |
|
||||
git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||
echo "message<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$(git tag -l --format='%(contents)' ${{ steps.get_version.outputs.version }})" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Release
|
||||
id: create-release
|
||||
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # ratchet:ncipollo/release-action@v1
|
||||
with:
|
||||
draft: true
|
||||
name: "avante-libs ${{ steps.get_version.outputs.version }}"
|
||||
tag: ${{ steps.get_version.outputs.version }}
|
||||
body: "${{ steps.tag.outputs.message }}"
|
||||
|
||||
releases-matrix:
|
||||
needs: [create-release]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
feature: [lua51, luajit]
|
||||
config:
|
||||
- os: ubuntu-24.04-arm
|
||||
os_name: linux
|
||||
arch: aarch64
|
||||
rust_target: aarch64-unknown-linux-gnu
|
||||
docker_platform: linux/aarch64
|
||||
container: quay.io/pypa/manylinux2014_aarch64
|
||||
- os: ubuntu-latest
|
||||
os_name: linux
|
||||
arch: x86_64
|
||||
rust_target: x86_64-unknown-linux-gnu
|
||||
docker_platform: linux/amd64
|
||||
container: quay.io/pypa/manylinux2014_x86_64 # for glibc 2.17
|
||||
- os: macos-13
|
||||
os_name: darwin
|
||||
arch: x86_64
|
||||
rust_target: x86_64-apple-darwin
|
||||
- os: macos-latest
|
||||
os_name: darwin
|
||||
arch: aarch64
|
||||
rust_target: aarch64-apple-darwin
|
||||
- os: windows-latest
|
||||
os_name: windows
|
||||
arch: x86_64
|
||||
rust_target: x86_64-pc-windows-msvc
|
||||
- os: windows-latest
|
||||
os_name: windows
|
||||
arch: aarch64
|
||||
rust_target: aarch64-pc-windows-msvc
|
||||
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4
|
||||
- uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # ratchet:Swatinem/rust-cache@v2
|
||||
if: ${{ matrix.config.container == null }}
|
||||
- uses: dtolnay/rust-toolchain@7b1c307e0dcbda6122208f10795a713336a9b35a # ratchet:dtolnay/rust-toolchain@master
|
||||
if: ${{ matrix.config.container == null }}
|
||||
with:
|
||||
targets: ${{ matrix.config.rust_target }}
|
||||
toolchain: "1.85.0"
|
||||
- name: Build all crates
|
||||
if: ${{ matrix.config.container == null }}
|
||||
run: |
|
||||
cargo build --release --features ${{ matrix.feature }}
|
||||
|
||||
- name: Build all crates with glibc 2.17 # for glibc 2.17
|
||||
if: ${{ matrix.config.container != null }}
|
||||
run: |
|
||||
# sudo apt-get install -y qemu qemu-user-static
|
||||
docker run \
|
||||
--rm \
|
||||
-v $(pwd):/workspace \
|
||||
-w /workspace \
|
||||
--platform ${{ matrix.config.docker_platform }} \
|
||||
${{ matrix.config.container }} \
|
||||
bash -c "yum install -y perl-IPC-Cmd openssl-devel && curl --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal && . /root/.cargo/env && cargo build --release --features ${{ matrix.feature }}"
|
||||
|
||||
- name: Handle binaries
|
||||
if: ${{ matrix.config.os_name != 'windows' }}
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p results
|
||||
if [ "${{ matrix.config.os_name }}" == "linux" ]; then
|
||||
EXT="so"
|
||||
else
|
||||
EXT="dylib"
|
||||
fi
|
||||
cp target/release/libavante_templates.$EXT results/avante_templates.$EXT
|
||||
cp target/release/libavante_tokenizers.$EXT results/avante_tokenizers.$EXT
|
||||
cp target/release/libavante_repo_map.$EXT results/avante_repo_map.$EXT
|
||||
cp target/release/libavante_html2md.$EXT results/avante_html2md.$EXT
|
||||
|
||||
cd results
|
||||
tar zcvf avante_lib-${{ matrix.config.os_name }}-${{ matrix.config.arch }}-${{ matrix.feature }}.tar.gz *.${EXT}
|
||||
|
||||
- name: Handle binaries (Windows)
|
||||
if: ${{ matrix.config.os_name == 'windows' }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force -Path results
|
||||
|
||||
Copy-Item -Path "target\release\avante_templates.dll" -Destination "results\avante_templates.dll"
|
||||
Copy-Item -Path "target\release\avante_tokenizers.dll" -Destination "results\avante_tokenizers.dll"
|
||||
Copy-Item -Path "target\release\avante_repo_map.dll" -Destination "results\avante_repo_map.dll"
|
||||
Copy-Item -Path "target\release\avante_html2md.dll" -Destination "results\avante_html2md.dll"
|
||||
|
||||
Set-Location -Path results
|
||||
|
||||
$dllFiles = Get-ChildItem -Filter "*.dll" | Select-Object -ExpandProperty Name
|
||||
Compress-Archive -Path $dllFiles -DestinationPath "avante_lib-${{ matrix.config.os_name }}-${{ matrix.config.arch }}-${{ matrix.feature }}.zip"
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: shogo82148/actions-upload-release-asset@8482bd769644976d847e96fb4b9354228885e7b4 # ratchet:shogo82148/actions-upload-release-asset@v1
|
||||
if: ${{ matrix.config.os_name != 'windows' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ASSET_NAME: avante_lib-${{ matrix.config.os_name }}-${{ matrix.config.arch }}-${{ matrix.feature }}.tar.gz
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.release_upload_url }}
|
||||
asset_path: ./results/avante_lib-${{ matrix.config.os_name }}-${{ matrix.config.arch }}-${{ matrix.feature }}.tar.gz
|
||||
- name: Upload Release Asset (Windows)
|
||||
uses: shogo82148/actions-upload-release-asset@8482bd769644976d847e96fb4b9354228885e7b4 # ratchet:shogo82148/actions-upload-release-asset@v1
|
||||
if: ${{ matrix.config.os_name == 'windows' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ASSET_NAME: avante_lib-${{ matrix.config.os_name }}-${{ matrix.config.arch }}-${{ matrix.feature }}.zip
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.release_upload_url }}
|
||||
asset_path: ./results/avante_lib-${{ matrix.config.os_name }}-${{ matrix.config.arch }}-${{ matrix.feature }}.zip
|
||||
|
||||
publish-release:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [create-release, releases-matrix]
|
||||
|
||||
steps:
|
||||
- name: publish release
|
||||
id: publish-release
|
||||
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # ratchet:actions/github-script@v6
|
||||
env:
|
||||
release_id: ${{ needs.create-release.outputs.release_id }}
|
||||
with:
|
||||
script: |
|
||||
github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: process.env.release_id,
|
||||
draft: false,
|
||||
prerelease: false
|
||||
})
|
||||
31
.github/workflows/rust.yaml
vendored
Normal file
31
.github/workflows/rust.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Rust CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "crates/**/*"
|
||||
- "Cargo.lock"
|
||||
- "Cargo.toml"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "crates/**/*"
|
||||
- "Cargo.lock"
|
||||
- "Cargo.toml"
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Run Rust tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4
|
||||
- uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # ratchet:Swatinem/rust-cache@v2
|
||||
- uses: dtolnay/rust-toolchain@7b1c307e0dcbda6122208f10795a713336a9b35a # ratchet:dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
components: clippy, rustfmt
|
||||
- name: Run rust tests
|
||||
run: cargo test --features luajit
|
||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Codetyper.nvim - AI coding partner files
|
||||
*.coder.*
|
||||
.coder/
|
||||
|
||||
/@
|
||||
add gitignore for lua files
|
||||
/@
|
||||
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- Improved code generation prompts to explicitly request only raw code output (no explanations, markdown, or code fences)
|
||||
|
||||
## [0.2.0] - 2026-01-11
|
||||
|
||||
### Added
|
||||
|
||||
@@ -39,7 +39,7 @@ This project and everyone participating in it is governed by our commitment to c
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/codetyper.nvim.git
|
||||
git clone https://github.com/CarGDev/codetyper.nvim.git
|
||||
cd codetyper.nvim
|
||||
```
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,182 +20,198 @@ local processed_prompts = {}
|
||||
---@param prompt table Prompt object
|
||||
---@return string Unique key
|
||||
local function get_prompt_key(bufnr, prompt)
|
||||
return string.format("%d:%d:%d:%s", bufnr, prompt.start_line, prompt.end_line, prompt.content:sub(1, 50))
|
||||
return string.format("%d:%d:%d:%s", bufnr, prompt.start_line, prompt.end_line, prompt.content:sub(1, 50))
|
||||
end
|
||||
|
||||
--- Schedule tree update with debounce
|
||||
local function schedule_tree_update()
|
||||
if tree_update_timer then
|
||||
tree_update_timer:stop()
|
||||
end
|
||||
if tree_update_timer then
|
||||
tree_update_timer:stop()
|
||||
end
|
||||
|
||||
tree_update_timer = vim.defer_fn(function()
|
||||
local tree = require("codetyper.tree")
|
||||
tree.update_tree_log()
|
||||
tree_update_timer = nil
|
||||
end, TREE_UPDATE_DEBOUNCE_MS)
|
||||
tree_update_timer = vim.defer_fn(function()
|
||||
local tree = require("codetyper.tree")
|
||||
tree.update_tree_log()
|
||||
tree_update_timer = nil
|
||||
end, TREE_UPDATE_DEBOUNCE_MS)
|
||||
end
|
||||
|
||||
--- Setup autocommands
|
||||
function M.setup()
|
||||
local group = vim.api.nvim_create_augroup(AUGROUP, { clear = true })
|
||||
local group = vim.api.nvim_create_augroup(AUGROUP, { clear = true })
|
||||
|
||||
-- Auto-save coder file when leaving insert mode
|
||||
vim.api.nvim_create_autocmd("InsertLeave", {
|
||||
group = group,
|
||||
pattern = "*.coder.*",
|
||||
callback = function()
|
||||
-- Auto-save the coder file
|
||||
if vim.bo.modified then
|
||||
vim.cmd("silent! write")
|
||||
end
|
||||
-- Check for closed prompts and auto-process
|
||||
M.check_for_closed_prompt()
|
||||
end,
|
||||
desc = "Auto-save and check for closed prompt tags",
|
||||
})
|
||||
-- Auto-save coder file when leaving insert mode
|
||||
vim.api.nvim_create_autocmd("InsertLeave", {
|
||||
group = group,
|
||||
pattern = "*.coder.*",
|
||||
callback = function()
|
||||
-- Auto-save the coder file
|
||||
if vim.bo.modified then
|
||||
vim.cmd("silent! write")
|
||||
end
|
||||
-- Check for closed prompts and auto-process
|
||||
M.check_for_closed_prompt()
|
||||
end,
|
||||
desc = "Auto-save and check for closed prompt tags",
|
||||
})
|
||||
|
||||
-- Auto-set filetype for coder files based on extension
|
||||
vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, {
|
||||
group = group,
|
||||
pattern = "*.coder.*",
|
||||
callback = function()
|
||||
M.set_coder_filetype()
|
||||
end,
|
||||
desc = "Set filetype for coder files",
|
||||
})
|
||||
-- Auto-set filetype for coder files based on extension
|
||||
vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, {
|
||||
group = group,
|
||||
pattern = "*.coder.*",
|
||||
callback = function()
|
||||
M.set_coder_filetype()
|
||||
end,
|
||||
desc = "Set filetype for coder files",
|
||||
})
|
||||
|
||||
-- Auto-open split view when opening a coder file directly (e.g., from nvim-tree)
|
||||
vim.api.nvim_create_autocmd("BufEnter", {
|
||||
group = group,
|
||||
pattern = "*.coder.*",
|
||||
callback = function()
|
||||
-- Delay slightly to ensure buffer is fully loaded
|
||||
vim.defer_fn(function()
|
||||
M.auto_open_target_file()
|
||||
end, 50)
|
||||
end,
|
||||
desc = "Auto-open target file when coder file is opened",
|
||||
})
|
||||
-- Auto-open split view when opening a coder file directly (e.g., from nvim-tree)
|
||||
vim.api.nvim_create_autocmd("BufEnter", {
|
||||
group = group,
|
||||
pattern = "*.coder.*",
|
||||
callback = function()
|
||||
-- Delay slightly to ensure buffer is fully loaded
|
||||
vim.defer_fn(function()
|
||||
M.auto_open_target_file()
|
||||
end, 50)
|
||||
end,
|
||||
desc = "Auto-open target file when coder file is opened",
|
||||
})
|
||||
|
||||
-- Cleanup on buffer close
|
||||
vim.api.nvim_create_autocmd("BufWipeout", {
|
||||
group = group,
|
||||
pattern = "*.coder.*",
|
||||
callback = function(ev)
|
||||
local window = require("codetyper.window")
|
||||
if window.is_open() then
|
||||
window.close_split()
|
||||
end
|
||||
-- Clear processed prompts for this buffer
|
||||
local bufnr = ev.buf
|
||||
for key, _ in pairs(processed_prompts) do
|
||||
if key:match("^" .. bufnr .. ":") then
|
||||
processed_prompts[key] = nil
|
||||
end
|
||||
end
|
||||
-- Clear auto-opened tracking
|
||||
M.clear_auto_opened(bufnr)
|
||||
end,
|
||||
desc = "Cleanup on coder buffer close",
|
||||
})
|
||||
-- Cleanup on buffer close
|
||||
vim.api.nvim_create_autocmd("BufWipeout", {
|
||||
group = group,
|
||||
pattern = "*.coder.*",
|
||||
callback = function(ev)
|
||||
local window = require("codetyper.window")
|
||||
if window.is_open() then
|
||||
window.close_split()
|
||||
end
|
||||
-- Clear processed prompts for this buffer
|
||||
local bufnr = ev.buf
|
||||
for key, _ in pairs(processed_prompts) do
|
||||
if key:match("^" .. bufnr .. ":") then
|
||||
processed_prompts[key] = nil
|
||||
end
|
||||
end
|
||||
-- Clear auto-opened tracking
|
||||
M.clear_auto_opened(bufnr)
|
||||
end,
|
||||
desc = "Cleanup on coder buffer close",
|
||||
})
|
||||
|
||||
-- Update tree.log when files are created/written
|
||||
vim.api.nvim_create_autocmd({ "BufWritePost", "BufNewFile" }, {
|
||||
group = group,
|
||||
pattern = "*",
|
||||
callback = function(ev)
|
||||
-- Skip coder files and tree.log itself
|
||||
local filepath = ev.file or vim.fn.expand("%:p")
|
||||
if filepath:match("%.coder%.") or filepath:match("tree%.log$") then
|
||||
return
|
||||
end
|
||||
-- Schedule tree update with debounce
|
||||
schedule_tree_update()
|
||||
end,
|
||||
desc = "Update tree.log on file creation/save",
|
||||
})
|
||||
-- Update tree.log when files are created/written
|
||||
vim.api.nvim_create_autocmd({ "BufWritePost", "BufNewFile" }, {
|
||||
group = group,
|
||||
pattern = "*",
|
||||
callback = function(ev)
|
||||
-- Skip coder files and tree.log itself
|
||||
local filepath = ev.file or vim.fn.expand("%:p")
|
||||
if filepath:match("%.coder%.") or filepath:match("tree%.log$") then
|
||||
return
|
||||
end
|
||||
-- Schedule tree update with debounce
|
||||
schedule_tree_update()
|
||||
end,
|
||||
desc = "Update tree.log on file creation/save",
|
||||
})
|
||||
|
||||
-- Update tree.log when files are deleted (via netrw or file explorer)
|
||||
vim.api.nvim_create_autocmd("BufDelete", {
|
||||
group = group,
|
||||
pattern = "*",
|
||||
callback = function(ev)
|
||||
local filepath = ev.file or ""
|
||||
-- Skip special buffers and coder files
|
||||
if filepath == "" or filepath:match("%.coder%.") or filepath:match("tree%.log$") then
|
||||
return
|
||||
end
|
||||
schedule_tree_update()
|
||||
end,
|
||||
desc = "Update tree.log on file deletion",
|
||||
})
|
||||
-- Update tree.log when files are deleted (via netrw or file explorer)
|
||||
vim.api.nvim_create_autocmd("BufDelete", {
|
||||
group = group,
|
||||
pattern = "*",
|
||||
callback = function(ev)
|
||||
local filepath = ev.file or ""
|
||||
-- Skip special buffers and coder files
|
||||
if filepath == "" or filepath:match("%.coder%.") or filepath:match("tree%.log$") then
|
||||
return
|
||||
end
|
||||
schedule_tree_update()
|
||||
end,
|
||||
desc = "Update tree.log on file deletion",
|
||||
})
|
||||
|
||||
-- Update tree on directory change
|
||||
vim.api.nvim_create_autocmd("DirChanged", {
|
||||
group = group,
|
||||
pattern = "*",
|
||||
callback = function()
|
||||
schedule_tree_update()
|
||||
end,
|
||||
desc = "Update tree.log on directory change",
|
||||
})
|
||||
-- Update tree on directory change
|
||||
vim.api.nvim_create_autocmd("DirChanged", {
|
||||
group = group,
|
||||
pattern = "*",
|
||||
callback = function()
|
||||
schedule_tree_update()
|
||||
end,
|
||||
desc = "Update tree.log on directory change",
|
||||
})
|
||||
end
|
||||
|
||||
--- Get config with fallback defaults
|
||||
local function get_config_safe()
|
||||
local codetyper = require("codetyper")
|
||||
local config = codetyper.get_config()
|
||||
-- Return defaults if not initialized
|
||||
if not config or not config.patterns then
|
||||
return {
|
||||
patterns = {
|
||||
open_tag = "/@",
|
||||
close_tag = "@/",
|
||||
file_pattern = "*.coder.*",
|
||||
}
|
||||
}
|
||||
end
|
||||
return config
|
||||
end
|
||||
|
||||
--- Check if the buffer has a newly closed prompt and auto-process
|
||||
function M.check_for_closed_prompt()
|
||||
local codetyper = require("codetyper")
|
||||
local config = codetyper.get_config()
|
||||
local parser = require("codetyper.parser")
|
||||
local config = get_config_safe()
|
||||
local parser = require("codetyper.parser")
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
|
||||
-- Get current line
|
||||
local cursor = vim.api.nvim_win_get_cursor(0)
|
||||
local line = cursor[1]
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, line - 1, line, false)
|
||||
-- Get current line
|
||||
local cursor = vim.api.nvim_win_get_cursor(0)
|
||||
local line = cursor[1]
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, line - 1, line, false)
|
||||
|
||||
if #lines == 0 then
|
||||
return
|
||||
end
|
||||
if #lines == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local current_line = lines[1]
|
||||
local current_line = lines[1]
|
||||
|
||||
-- Check if line contains closing tag
|
||||
if parser.has_closing_tag(current_line, config.patterns.close_tag) then
|
||||
-- Find the complete prompt
|
||||
local prompt = parser.get_last_prompt(bufnr)
|
||||
if prompt and prompt.content and prompt.content ~= "" then
|
||||
-- Generate unique key for this prompt
|
||||
local prompt_key = get_prompt_key(bufnr, prompt)
|
||||
-- Check if line contains closing tag
|
||||
if parser.has_closing_tag(current_line, config.patterns.close_tag) then
|
||||
-- Find the complete prompt
|
||||
local prompt = parser.get_last_prompt(bufnr)
|
||||
if prompt and prompt.content and prompt.content ~= "" then
|
||||
-- Generate unique key for this prompt
|
||||
local prompt_key = get_prompt_key(bufnr, prompt)
|
||||
|
||||
-- Check if already processed
|
||||
if processed_prompts[prompt_key] then
|
||||
return
|
||||
end
|
||||
-- Check if already processed
|
||||
if processed_prompts[prompt_key] then
|
||||
return
|
||||
end
|
||||
|
||||
-- Mark as processed
|
||||
processed_prompts[prompt_key] = true
|
||||
-- Mark as processed
|
||||
processed_prompts[prompt_key] = true
|
||||
|
||||
-- Auto-process the prompt (no confirmation needed)
|
||||
utils.notify("Processing prompt...", vim.log.levels.INFO)
|
||||
vim.schedule(function()
|
||||
vim.cmd("CoderProcess")
|
||||
end)
|
||||
end
|
||||
end
|
||||
-- Auto-process the prompt (no confirmation needed)
|
||||
utils.notify("Processing prompt...", vim.log.levels.INFO)
|
||||
vim.schedule(function()
|
||||
vim.cmd("CoderProcess")
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Reset processed prompts for a buffer (useful for re-processing)
|
||||
---@param bufnr? number Buffer number (default: current)
|
||||
function M.reset_processed(bufnr)
|
||||
bufnr = bufnr or vim.api.nvim_get_current_buf()
|
||||
for key, _ in pairs(processed_prompts) do
|
||||
if key:match("^" .. bufnr .. ":") then
|
||||
processed_prompts[key] = nil
|
||||
end
|
||||
end
|
||||
utils.notify("Prompt history cleared - prompts can be re-processed")
|
||||
bufnr = bufnr or vim.api.nvim_get_current_buf()
|
||||
for key, _ in pairs(processed_prompts) do
|
||||
if key:match("^" .. bufnr .. ":") then
|
||||
processed_prompts[key] = nil
|
||||
end
|
||||
end
|
||||
utils.notify("Prompt history cleared - prompts can be re-processed")
|
||||
end
|
||||
|
||||
--- Track if we already opened the split for this buffer
|
||||
@@ -204,146 +220,146 @@ local auto_opened_buffers = {}
|
||||
|
||||
--- Auto-open target file when a coder file is opened directly
|
||||
function M.auto_open_target_file()
|
||||
local window = require("codetyper.window")
|
||||
local window = require("codetyper.window")
|
||||
|
||||
-- Skip if split is already open
|
||||
if window.is_open() then
|
||||
return
|
||||
end
|
||||
-- Skip if split is already open
|
||||
if window.is_open() then
|
||||
return
|
||||
end
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
|
||||
-- Skip if we already handled this buffer
|
||||
if auto_opened_buffers[bufnr] then
|
||||
return
|
||||
end
|
||||
-- Skip if we already handled this buffer
|
||||
if auto_opened_buffers[bufnr] then
|
||||
return
|
||||
end
|
||||
|
||||
local current_file = vim.fn.expand("%:p")
|
||||
local current_file = vim.fn.expand("%:p")
|
||||
|
||||
-- Skip empty paths
|
||||
if not current_file or current_file == "" then
|
||||
return
|
||||
end
|
||||
-- Skip empty paths
|
||||
if not current_file or current_file == "" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Verify it's a coder file
|
||||
if not utils.is_coder_file(current_file) then
|
||||
return
|
||||
end
|
||||
-- Verify it's a coder file
|
||||
if not utils.is_coder_file(current_file) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Skip if we're in a special buffer (nvim-tree, etc.)
|
||||
local buftype = vim.bo[bufnr].buftype
|
||||
if buftype ~= "" then
|
||||
return
|
||||
end
|
||||
-- Skip if we're in a special buffer (nvim-tree, etc.)
|
||||
local buftype = vim.bo[bufnr].buftype
|
||||
if buftype ~= "" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Mark as handled
|
||||
auto_opened_buffers[bufnr] = true
|
||||
-- Mark as handled
|
||||
auto_opened_buffers[bufnr] = true
|
||||
|
||||
-- Get the target file path
|
||||
local target_path = utils.get_target_path(current_file)
|
||||
-- Get the target file path
|
||||
local target_path = utils.get_target_path(current_file)
|
||||
|
||||
-- Check if target file exists
|
||||
if not utils.file_exists(target_path) then
|
||||
utils.notify("Target file not found: " .. vim.fn.fnamemodify(target_path, ":t"), vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
-- Check if target file exists
|
||||
if not utils.file_exists(target_path) then
|
||||
utils.notify("Target file not found: " .. vim.fn.fnamemodify(target_path, ":t"), vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
-- Get config with fallback defaults
|
||||
local codetyper = require("codetyper")
|
||||
local config = codetyper.get_config()
|
||||
-- Get config with fallback defaults
|
||||
local codetyper = require("codetyper")
|
||||
local config = codetyper.get_config()
|
||||
|
||||
-- Fallback width if config not fully loaded
|
||||
local width = (config and config.window and config.window.width) or 0.4
|
||||
if width <= 1 then
|
||||
width = math.floor(vim.o.columns * width)
|
||||
end
|
||||
-- Fallback width if config not fully loaded
|
||||
local width = (config and config.window and config.window.width) or 0.4
|
||||
if width <= 1 then
|
||||
width = math.floor(vim.o.columns * width)
|
||||
end
|
||||
|
||||
-- Store current coder window
|
||||
local coder_win = vim.api.nvim_get_current_win()
|
||||
local coder_buf = bufnr
|
||||
-- Store current coder window
|
||||
local coder_win = vim.api.nvim_get_current_win()
|
||||
local coder_buf = bufnr
|
||||
|
||||
-- Open target file in a vertical split on the right
|
||||
local ok, err = pcall(function()
|
||||
vim.cmd("vsplit " .. vim.fn.fnameescape(target_path))
|
||||
end)
|
||||
-- Open target file in a vertical split on the right
|
||||
local ok, err = pcall(function()
|
||||
vim.cmd("vsplit " .. vim.fn.fnameescape(target_path))
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
utils.notify("Failed to open target file: " .. tostring(err), vim.log.levels.ERROR)
|
||||
auto_opened_buffers[bufnr] = nil -- Allow retry
|
||||
return
|
||||
end
|
||||
if not ok then
|
||||
utils.notify("Failed to open target file: " .. tostring(err), vim.log.levels.ERROR)
|
||||
auto_opened_buffers[bufnr] = nil -- Allow retry
|
||||
return
|
||||
end
|
||||
|
||||
-- Now we're in the target window (right side)
|
||||
local target_win = vim.api.nvim_get_current_win()
|
||||
local target_buf = vim.api.nvim_get_current_buf()
|
||||
-- Now we're in the target window (right side)
|
||||
local target_win = vim.api.nvim_get_current_win()
|
||||
local target_buf = vim.api.nvim_get_current_buf()
|
||||
|
||||
-- Set the coder window width (left side)
|
||||
pcall(vim.api.nvim_win_set_width, coder_win, width)
|
||||
-- Set the coder window width (left side)
|
||||
pcall(vim.api.nvim_win_set_width, coder_win, width)
|
||||
|
||||
-- Update window module state
|
||||
window._coder_win = coder_win
|
||||
window._coder_buf = coder_buf
|
||||
window._target_win = target_win
|
||||
window._target_buf = target_buf
|
||||
-- Update window module state
|
||||
window._coder_win = coder_win
|
||||
window._coder_buf = coder_buf
|
||||
window._target_win = target_win
|
||||
window._target_buf = target_buf
|
||||
|
||||
-- Set up window options for coder window
|
||||
pcall(function()
|
||||
vim.wo[coder_win].number = true
|
||||
vim.wo[coder_win].relativenumber = true
|
||||
vim.wo[coder_win].signcolumn = "yes"
|
||||
end)
|
||||
-- Set up window options for coder window
|
||||
pcall(function()
|
||||
vim.wo[coder_win].number = true
|
||||
vim.wo[coder_win].relativenumber = true
|
||||
vim.wo[coder_win].signcolumn = "yes"
|
||||
end)
|
||||
|
||||
utils.notify("Opened target: " .. vim.fn.fnamemodify(target_path, ":t"))
|
||||
utils.notify("Opened target: " .. vim.fn.fnamemodify(target_path, ":t"))
|
||||
end
|
||||
|
||||
--- Clear auto-opened tracking for a buffer
|
||||
---@param bufnr number Buffer number
|
||||
function M.clear_auto_opened(bufnr)
|
||||
auto_opened_buffers[bufnr] = nil
|
||||
auto_opened_buffers[bufnr] = nil
|
||||
end
|
||||
|
||||
--- Set appropriate filetype for coder files
|
||||
function M.set_coder_filetype()
|
||||
local filepath = vim.fn.expand("%:p")
|
||||
local filepath = vim.fn.expand("%:p")
|
||||
|
||||
-- Extract the actual extension (e.g., index.coder.ts -> ts)
|
||||
local ext = filepath:match("%.coder%.(%w+)$")
|
||||
-- Extract the actual extension (e.g., index.coder.ts -> ts)
|
||||
local ext = filepath:match("%.coder%.(%w+)$")
|
||||
|
||||
if ext then
|
||||
-- Map extension to filetype
|
||||
local ft_map = {
|
||||
ts = "typescript",
|
||||
tsx = "typescriptreact",
|
||||
js = "javascript",
|
||||
jsx = "javascriptreact",
|
||||
py = "python",
|
||||
lua = "lua",
|
||||
go = "go",
|
||||
rs = "rust",
|
||||
rb = "ruby",
|
||||
java = "java",
|
||||
c = "c",
|
||||
cpp = "cpp",
|
||||
cs = "cs",
|
||||
json = "json",
|
||||
yaml = "yaml",
|
||||
yml = "yaml",
|
||||
md = "markdown",
|
||||
html = "html",
|
||||
css = "css",
|
||||
scss = "scss",
|
||||
vue = "vue",
|
||||
svelte = "svelte",
|
||||
}
|
||||
if ext then
|
||||
-- Map extension to filetype
|
||||
local ft_map = {
|
||||
ts = "typescript",
|
||||
tsx = "typescriptreact",
|
||||
js = "javascript",
|
||||
jsx = "javascriptreact",
|
||||
py = "python",
|
||||
lua = "lua",
|
||||
go = "go",
|
||||
rs = "rust",
|
||||
rb = "ruby",
|
||||
java = "java",
|
||||
c = "c",
|
||||
cpp = "cpp",
|
||||
cs = "cs",
|
||||
json = "json",
|
||||
yaml = "yaml",
|
||||
yml = "yaml",
|
||||
md = "markdown",
|
||||
html = "html",
|
||||
css = "css",
|
||||
scss = "scss",
|
||||
vue = "vue",
|
||||
svelte = "svelte",
|
||||
}
|
||||
|
||||
local filetype = ft_map[ext] or ext
|
||||
vim.bo.filetype = filetype
|
||||
end
|
||||
local filetype = ft_map[ext] or ext
|
||||
vim.bo.filetype = filetype
|
||||
end
|
||||
end
|
||||
|
||||
--- Clear all autocommands
|
||||
function M.clear()
|
||||
vim.api.nvim_del_augroup_by_name(AUGROUP)
|
||||
vim.api.nvim_del_augroup_by_name(AUGROUP)
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
@@ -88,6 +88,23 @@ local function cmd_toggle()
|
||||
window.toggle_split(target_path, coder_path)
|
||||
end
|
||||
|
||||
--- Build enhanced user prompt with context
|
||||
---@param clean_prompt string The cleaned user prompt
|
||||
---@param context table Context information
|
||||
---@return string Enhanced prompt
|
||||
local function build_user_prompt(clean_prompt, context)
|
||||
local enhanced = "TASK: " .. clean_prompt .. "\n\n"
|
||||
|
||||
enhanced = enhanced .. "REQUIREMENTS:\n"
|
||||
enhanced = enhanced .. "- Generate ONLY " .. (context.language or "code") .. " code\n"
|
||||
enhanced = enhanced .. "- NO markdown code blocks (no ```)\n"
|
||||
enhanced = enhanced .. "- NO explanations or comments about what you did\n"
|
||||
enhanced = enhanced .. "- Match the coding style of the existing file exactly\n"
|
||||
enhanced = enhanced .. "- Output must be ready to insert directly into the file\n"
|
||||
|
||||
return enhanced
|
||||
end
|
||||
|
||||
--- Process prompt at cursor and generate code
|
||||
local function cmd_process()
|
||||
local parser = require("codetyper.parser")
|
||||
@@ -111,8 +128,13 @@ local function cmd_process()
|
||||
local prompt_type = parser.detect_prompt_type(prompt.content)
|
||||
local context = llm.build_context(target_path, prompt_type)
|
||||
local clean_prompt = parser.clean_prompt(prompt.content)
|
||||
|
||||
-- Build enhanced prompt with explicit instructions
|
||||
local enhanced_prompt = build_user_prompt(clean_prompt, context)
|
||||
|
||||
llm.generate(clean_prompt, context, function(response, err)
|
||||
utils.notify("Processing: " .. clean_prompt:sub(1, 50) .. "...", vim.log.levels.INFO)
|
||||
|
||||
llm.generate(enhanced_prompt, context, function(response, err)
|
||||
if err then
|
||||
utils.notify("Generation failed: " .. err, vim.log.levels.ERROR)
|
||||
return
|
||||
@@ -122,6 +144,7 @@ local function cmd_process()
|
||||
-- Inject code into target file
|
||||
local inject = require("codetyper.inject")
|
||||
inject.inject_code(target_path, response, prompt_type)
|
||||
utils.notify("Code generated and injected!", vim.log.levels.INFO)
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -244,6 +267,332 @@ local function cmd_focus()
|
||||
end
|
||||
end
|
||||
|
||||
--- Transform inline /@ @/ tags in current file
|
||||
--- Works on ANY file, not just .coder.* files
|
||||
local function cmd_transform()
|
||||
local parser = require("codetyper.parser")
|
||||
local llm = require("codetyper.llm")
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local filepath = vim.fn.expand("%:p")
|
||||
|
||||
if filepath == "" then
|
||||
utils.notify("No file in current buffer", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
-- Find all prompts in the current buffer
|
||||
local prompts = parser.find_prompts_in_buffer(bufnr)
|
||||
|
||||
if #prompts == 0 then
|
||||
utils.notify("No /@ @/ tags found in current file", vim.log.levels.INFO)
|
||||
return
|
||||
end
|
||||
|
||||
utils.notify("Found " .. #prompts .. " prompt(s) to transform...", vim.log.levels.INFO)
|
||||
|
||||
-- Build context for this file
|
||||
local ext = vim.fn.fnamemodify(filepath, ":e")
|
||||
local context = llm.build_context(filepath, "code_generation")
|
||||
|
||||
-- Process prompts in reverse order (bottom to top) to maintain line numbers
|
||||
local sorted_prompts = {}
|
||||
for i = #prompts, 1, -1 do
|
||||
table.insert(sorted_prompts, prompts[i])
|
||||
end
|
||||
|
||||
-- Track how many are being processed
|
||||
local pending = #sorted_prompts
|
||||
local completed = 0
|
||||
local errors = 0
|
||||
|
||||
-- Process each prompt
|
||||
for _, prompt in ipairs(sorted_prompts) do
|
||||
local clean_prompt = parser.clean_prompt(prompt.content)
|
||||
local prompt_type = parser.detect_prompt_type(prompt.content)
|
||||
|
||||
-- Build enhanced user prompt
|
||||
local enhanced_prompt = "TASK: " .. clean_prompt .. "\n\n"
|
||||
enhanced_prompt = enhanced_prompt .. "REQUIREMENTS:\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- Generate ONLY " .. (context.language or "code") .. " code\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- NO markdown code blocks (no ```)\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- NO explanations or comments about what you did\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- Match the coding style of the existing file exactly\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- Output must be ready to insert directly into the file\n"
|
||||
|
||||
utils.notify("Processing: " .. clean_prompt:sub(1, 40) .. "...", vim.log.levels.INFO)
|
||||
|
||||
-- Generate code for this prompt
|
||||
llm.generate(enhanced_prompt, context, function(response, err)
|
||||
if err then
|
||||
utils.notify("Failed: " .. err, vim.log.levels.ERROR)
|
||||
errors = errors + 1
|
||||
elseif response then
|
||||
-- Replace the prompt tag with generated code
|
||||
vim.schedule(function()
|
||||
-- Get current buffer lines
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
|
||||
-- Calculate the exact range to replace
|
||||
local start_line = prompt.start_line
|
||||
local end_line = prompt.end_line
|
||||
|
||||
-- Find the full lines containing the tags
|
||||
local start_line_content = lines[start_line] or ""
|
||||
local end_line_content = lines[end_line] or ""
|
||||
|
||||
-- Check if there's content before the opening tag on the same line
|
||||
local codetyper = require("codetyper")
|
||||
local config = codetyper.get_config()
|
||||
local before_tag = ""
|
||||
local after_tag = ""
|
||||
|
||||
local open_pos = start_line_content:find(utils.escape_pattern(config.patterns.open_tag))
|
||||
if open_pos and open_pos > 1 then
|
||||
before_tag = start_line_content:sub(1, open_pos - 1)
|
||||
end
|
||||
|
||||
local close_pos = end_line_content:find(utils.escape_pattern(config.patterns.close_tag))
|
||||
if close_pos then
|
||||
local after_close = close_pos + #config.patterns.close_tag
|
||||
if after_close <= #end_line_content then
|
||||
after_tag = end_line_content:sub(after_close)
|
||||
end
|
||||
end
|
||||
|
||||
-- Build the replacement lines
|
||||
local replacement_lines = vim.split(response, "\n", { plain = true })
|
||||
|
||||
-- Add before/after content if any
|
||||
if before_tag ~= "" and #replacement_lines > 0 then
|
||||
replacement_lines[1] = before_tag .. replacement_lines[1]
|
||||
end
|
||||
if after_tag ~= "" and #replacement_lines > 0 then
|
||||
replacement_lines[#replacement_lines] = replacement_lines[#replacement_lines] .. after_tag
|
||||
end
|
||||
|
||||
-- Replace the lines in buffer
|
||||
vim.api.nvim_buf_set_lines(bufnr, start_line - 1, end_line, false, replacement_lines)
|
||||
|
||||
completed = completed + 1
|
||||
if completed + errors >= pending then
|
||||
utils.notify(
|
||||
"Transform complete: " .. completed .. " succeeded, " .. errors .. " failed",
|
||||
errors > 0 and vim.log.levels.WARN or vim.log.levels.INFO
|
||||
)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
--- Transform prompts within a line range (for visual selection)
|
||||
---@param start_line number Start line (1-indexed)
|
||||
---@param end_line number End line (1-indexed)
|
||||
local function cmd_transform_range(start_line, end_line)
|
||||
local parser = require("codetyper.parser")
|
||||
local llm = require("codetyper.llm")
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local filepath = vim.fn.expand("%:p")
|
||||
|
||||
if filepath == "" then
|
||||
utils.notify("No file in current buffer", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
-- Find all prompts in the current buffer
|
||||
local all_prompts = parser.find_prompts_in_buffer(bufnr)
|
||||
|
||||
-- Filter prompts that are within the selected range
|
||||
local prompts = {}
|
||||
for _, prompt in ipairs(all_prompts) do
|
||||
if prompt.start_line >= start_line and prompt.end_line <= end_line then
|
||||
table.insert(prompts, prompt)
|
||||
end
|
||||
end
|
||||
|
||||
if #prompts == 0 then
|
||||
utils.notify("No /@ @/ tags found in selection (lines " .. start_line .. "-" .. end_line .. ")", vim.log.levels.INFO)
|
||||
return
|
||||
end
|
||||
|
||||
utils.notify("Found " .. #prompts .. " prompt(s) in selection to transform...", vim.log.levels.INFO)
|
||||
|
||||
-- Build context for this file
|
||||
local context = llm.build_context(filepath, "code_generation")
|
||||
|
||||
-- Process prompts in reverse order (bottom to top) to maintain line numbers
|
||||
local sorted_prompts = {}
|
||||
for i = #prompts, 1, -1 do
|
||||
table.insert(sorted_prompts, prompts[i])
|
||||
end
|
||||
|
||||
local pending = #sorted_prompts
|
||||
local completed = 0
|
||||
local errors = 0
|
||||
|
||||
for _, prompt in ipairs(sorted_prompts) do
|
||||
local clean_prompt = parser.clean_prompt(prompt.content)
|
||||
|
||||
local enhanced_prompt = "TASK: " .. clean_prompt .. "\n\n"
|
||||
enhanced_prompt = enhanced_prompt .. "REQUIREMENTS:\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- Generate ONLY " .. (context.language or "code") .. " code\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- NO markdown code blocks (no ```)\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- NO explanations or comments about what you did\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- Match the coding style of the existing file exactly\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- Output must be ready to insert directly into the file\n"
|
||||
|
||||
utils.notify("Processing: " .. clean_prompt:sub(1, 40) .. "...", vim.log.levels.INFO)
|
||||
|
||||
llm.generate(enhanced_prompt, context, function(response, err)
|
||||
if err then
|
||||
utils.notify("Failed: " .. err, vim.log.levels.ERROR)
|
||||
errors = errors + 1
|
||||
elseif response then
|
||||
vim.schedule(function()
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
local p_start_line = prompt.start_line
|
||||
local p_end_line = prompt.end_line
|
||||
|
||||
local start_line_content = lines[p_start_line] or ""
|
||||
local end_line_content = lines[p_end_line] or ""
|
||||
|
||||
local codetyper = require("codetyper")
|
||||
local config = codetyper.get_config()
|
||||
local before_tag = ""
|
||||
local after_tag = ""
|
||||
|
||||
local open_pos = start_line_content:find(utils.escape_pattern(config.patterns.open_tag))
|
||||
if open_pos and open_pos > 1 then
|
||||
before_tag = start_line_content:sub(1, open_pos - 1)
|
||||
end
|
||||
|
||||
local close_pos = end_line_content:find(utils.escape_pattern(config.patterns.close_tag))
|
||||
if close_pos then
|
||||
local after_close = close_pos + #config.patterns.close_tag
|
||||
if after_close <= #end_line_content then
|
||||
after_tag = end_line_content:sub(after_close)
|
||||
end
|
||||
end
|
||||
|
||||
local replacement_lines = vim.split(response, "\n", { plain = true })
|
||||
|
||||
if before_tag ~= "" and #replacement_lines > 0 then
|
||||
replacement_lines[1] = before_tag .. replacement_lines[1]
|
||||
end
|
||||
if after_tag ~= "" and #replacement_lines > 0 then
|
||||
replacement_lines[#replacement_lines] = replacement_lines[#replacement_lines] .. after_tag
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_set_lines(bufnr, p_start_line - 1, p_end_line, false, replacement_lines)
|
||||
|
||||
completed = completed + 1
|
||||
if completed + errors >= pending then
|
||||
utils.notify(
|
||||
"Transform complete: " .. completed .. " succeeded, " .. errors .. " failed",
|
||||
errors > 0 and vim.log.levels.WARN or vim.log.levels.INFO
|
||||
)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
--- Command wrapper for visual selection transform
|
||||
local function cmd_transform_visual()
|
||||
-- Get visual selection marks
|
||||
local start_line = vim.fn.line("'<")
|
||||
local end_line = vim.fn.line("'>")
|
||||
cmd_transform_range(start_line, end_line)
|
||||
end
|
||||
|
||||
--- Transform a single prompt at cursor position
|
||||
local function cmd_transform_at_cursor()
|
||||
local parser = require("codetyper.parser")
|
||||
local llm = require("codetyper.llm")
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local filepath = vim.fn.expand("%:p")
|
||||
|
||||
if filepath == "" then
|
||||
utils.notify("No file in current buffer", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
-- Find prompt at cursor
|
||||
local prompt = parser.get_prompt_at_cursor(bufnr)
|
||||
|
||||
if not prompt then
|
||||
utils.notify("No /@ @/ tag at cursor position", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
local clean_prompt = parser.clean_prompt(prompt.content)
|
||||
local context = llm.build_context(filepath, "code_generation")
|
||||
|
||||
-- Build enhanced user prompt
|
||||
local enhanced_prompt = "TASK: " .. clean_prompt .. "\n\n"
|
||||
enhanced_prompt = enhanced_prompt .. "REQUIREMENTS:\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- Generate ONLY " .. (context.language or "code") .. " code\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- NO markdown code blocks (no ```)\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- NO explanations or comments about what you did\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- Match the coding style of the existing file exactly\n"
|
||||
enhanced_prompt = enhanced_prompt .. "- Output must be ready to insert directly into the file\n"
|
||||
|
||||
utils.notify("Transforming: " .. clean_prompt:sub(1, 40) .. "...", vim.log.levels.INFO)
|
||||
|
||||
llm.generate(enhanced_prompt, context, function(response, err)
|
||||
if err then
|
||||
utils.notify("Transform failed: " .. err, vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
if response then
|
||||
vim.schedule(function()
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
local start_line = prompt.start_line
|
||||
local end_line = prompt.end_line
|
||||
|
||||
local start_line_content = lines[start_line] or ""
|
||||
local end_line_content = lines[end_line] or ""
|
||||
|
||||
local codetyper = require("codetyper")
|
||||
local config = codetyper.get_config()
|
||||
local before_tag = ""
|
||||
local after_tag = ""
|
||||
|
||||
local open_pos = start_line_content:find(utils.escape_pattern(config.patterns.open_tag))
|
||||
if open_pos and open_pos > 1 then
|
||||
before_tag = start_line_content:sub(1, open_pos - 1)
|
||||
end
|
||||
|
||||
local close_pos = end_line_content:find(utils.escape_pattern(config.patterns.close_tag))
|
||||
if close_pos then
|
||||
local after_close = close_pos + #config.patterns.close_tag
|
||||
if after_close <= #end_line_content then
|
||||
after_tag = end_line_content:sub(after_close)
|
||||
end
|
||||
end
|
||||
|
||||
local replacement_lines = vim.split(response, "\n", { plain = true })
|
||||
|
||||
if before_tag ~= "" and #replacement_lines > 0 then
|
||||
replacement_lines[1] = before_tag .. replacement_lines[1]
|
||||
end
|
||||
if after_tag ~= "" and #replacement_lines > 0 then
|
||||
replacement_lines[#replacement_lines] = replacement_lines[#replacement_lines] .. after_tag
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_set_lines(bufnr, start_line - 1, end_line, false, replacement_lines)
|
||||
utils.notify("Transform complete!", vim.log.levels.INFO)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- Main command dispatcher
|
||||
---@param args table Command arguments
|
||||
local function coder_cmd(args)
|
||||
@@ -264,6 +613,8 @@ local function coder_cmd(args)
|
||||
["ask-toggle"] = cmd_ask_toggle,
|
||||
["ask-clear"] = cmd_ask_clear,
|
||||
gitignore = cmd_gitignore,
|
||||
transform = cmd_transform,
|
||||
["transform-cursor"] = cmd_transform_at_cursor,
|
||||
}
|
||||
|
||||
local cmd_fn = commands[subcommand]
|
||||
@@ -283,6 +634,7 @@ function M.setup()
|
||||
"open", "close", "toggle", "process", "status", "focus",
|
||||
"tree", "tree-view", "reset", "gitignore",
|
||||
"ask", "ask-close", "ask-toggle", "ask-clear",
|
||||
"transform", "transform-cursor",
|
||||
}
|
||||
end,
|
||||
desc = "Codetyper.nvim commands",
|
||||
@@ -325,6 +677,45 @@ function M.setup()
|
||||
vim.api.nvim_create_user_command("CoderAskClear", function()
|
||||
cmd_ask_clear()
|
||||
end, { desc = "Clear Ask history" })
|
||||
|
||||
-- Transform commands (inline /@ @/ tag replacement)
|
||||
vim.api.nvim_create_user_command("CoderTransform", function()
|
||||
cmd_transform()
|
||||
end, { desc = "Transform all /@ @/ tags in current file" })
|
||||
|
||||
vim.api.nvim_create_user_command("CoderTransformCursor", function()
|
||||
cmd_transform_at_cursor()
|
||||
end, { desc = "Transform /@ @/ tag at cursor" })
|
||||
|
||||
vim.api.nvim_create_user_command("CoderTransformVisual", function(opts)
|
||||
local start_line = opts.line1
|
||||
local end_line = opts.line2
|
||||
cmd_transform_range(start_line, end_line)
|
||||
end, { range = true, desc = "Transform /@ @/ tags in visual selection" })
|
||||
|
||||
-- Setup default keymaps
|
||||
M.setup_keymaps()
|
||||
end
|
||||
|
||||
--- Setup default keymaps for transform commands
|
||||
function M.setup_keymaps()
|
||||
-- Visual mode: transform selected /@ @/ tags
|
||||
vim.keymap.set("v", "<leader>ctt", ":<C-u>CoderTransformVisual<CR>", {
|
||||
silent = true,
|
||||
desc = "Coder: Transform selected tags"
|
||||
})
|
||||
|
||||
-- Normal mode: transform tag at cursor
|
||||
vim.keymap.set("n", "<leader>ctt", "<cmd>CoderTransformCursor<CR>", {
|
||||
silent = true,
|
||||
desc = "Coder: Transform tag at cursor"
|
||||
})
|
||||
|
||||
-- Normal mode: transform all tags in file
|
||||
vim.keymap.set("n", "<leader>ctT", "<cmd>CoderTransform<CR>", {
|
||||
silent = true,
|
||||
desc = "Coder: Transform all tags in file"
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
@@ -5,14 +5,14 @@ local M = {}
|
||||
---@type CoderConfig
|
||||
local defaults = {
|
||||
llm = {
|
||||
provider = "claude",
|
||||
provider = "ollama",
|
||||
claude = {
|
||||
api_key = nil, -- Will use ANTHROPIC_API_KEY env var if nil
|
||||
model = "claude-sonnet-4-20250514",
|
||||
},
|
||||
ollama = {
|
||||
host = "http://localhost:11434",
|
||||
model = "codellama",
|
||||
model = "deepseek-coder:6.7b",
|
||||
},
|
||||
},
|
||||
window = {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
---@mod codetyper.llm LLM interface for Codetyper.nvim
|
||||
|
||||
local M = {}
|
||||
|
||||
local lang_map = require("codetyper.utils.langmap")
|
||||
local utils = require("codetyper.utils")
|
||||
|
||||
--- Get the appropriate LLM client based on configuration
|
||||
---@return table LLM client module
|
||||
function M.get_client()
|
||||
local codetyper = require("codetyper")
|
||||
local config = codetyper.get_config()
|
||||
local codetyper = require("codetyper")
|
||||
local config = codetyper.get_config()
|
||||
|
||||
if config.llm.provider == "claude" then
|
||||
return require("codetyper.llm.claude")
|
||||
elseif config.llm.provider == "ollama" then
|
||||
return require("codetyper.llm.ollama")
|
||||
else
|
||||
error("Unknown LLM provider: " .. config.llm.provider)
|
||||
end
|
||||
if config.llm.provider == "claude" then
|
||||
return require("codetyper.llm.claude")
|
||||
elseif config.llm.provider == "ollama" then
|
||||
return require("codetyper.llm.ollama")
|
||||
else
|
||||
error("Unknown LLM provider: " .. config.llm.provider)
|
||||
end
|
||||
end
|
||||
|
||||
--- Generate code from a prompt
|
||||
@@ -24,31 +24,40 @@ end
|
||||
---@param context table Context information (file content, language, etc.)
|
||||
---@param callback fun(response: string|nil, error: string|nil) Callback function
|
||||
function M.generate(prompt, context, callback)
|
||||
local client = M.get_client()
|
||||
client.generate(prompt, context, callback)
|
||||
local client = M.get_client()
|
||||
client.generate(prompt, context, callback)
|
||||
end
|
||||
|
||||
--- Build the system prompt for code generation
|
||||
---@param context table Context information
|
||||
---@return string System prompt
|
||||
function M.build_system_prompt(context)
|
||||
local prompts = require("codetyper.prompts")
|
||||
|
||||
-- Select appropriate system prompt based on context
|
||||
local prompt_type = context.prompt_type or "code_generation"
|
||||
local system_prompts = prompts.system
|
||||
|
||||
local system = system_prompts[prompt_type] or system_prompts.code_generation
|
||||
|
||||
-- Substitute variables
|
||||
system = system:gsub("{{language}}", context.language or "unknown")
|
||||
system = system:gsub("{{filepath}}", context.file_path or "unknown")
|
||||
local prompts = require("codetyper.prompts")
|
||||
|
||||
if context.file_content then
|
||||
system = system .. "\n\nExisting file content:\n```\n" .. context.file_content .. "\n```"
|
||||
end
|
||||
-- Select appropriate system prompt based on context
|
||||
local prompt_type = context.prompt_type or "code_generation"
|
||||
local system_prompts = prompts.system
|
||||
|
||||
return system
|
||||
local system = system_prompts[prompt_type] or system_prompts.code_generation
|
||||
|
||||
-- Substitute variables
|
||||
system = system:gsub("{{language}}", context.language or "unknown")
|
||||
system = system:gsub("{{filepath}}", context.file_path or "unknown")
|
||||
|
||||
-- Add file content with analysis hints
|
||||
if context.file_content and context.file_content ~= "" then
|
||||
system = system .. "\n\n===== EXISTING FILE CONTENT (analyze and match this style) =====\n"
|
||||
system = system .. context.file_content
|
||||
system = system .. "\n===== END OF EXISTING FILE =====\n"
|
||||
system = system .. "\nYour generated code MUST follow the exact patterns shown above."
|
||||
else
|
||||
system = system
|
||||
.. "\n\nThis is a new/empty file. Generate clean, idiomatic "
|
||||
.. (context.language or "code")
|
||||
.. " following best practices."
|
||||
end
|
||||
|
||||
return system
|
||||
end
|
||||
|
||||
--- Build context for LLM request
|
||||
@@ -56,46 +65,49 @@ end
|
||||
---@param prompt_type string Type of prompt
|
||||
---@return table Context object
|
||||
function M.build_context(target_path, prompt_type)
|
||||
local content = utils.read_file(target_path)
|
||||
local ext = vim.fn.fnamemodify(target_path, ":e")
|
||||
local content = utils.read_file(target_path)
|
||||
local ext = vim.fn.fnamemodify(target_path, ":e")
|
||||
|
||||
-- Map extension to language
|
||||
local lang_map = {
|
||||
ts = "TypeScript",
|
||||
tsx = "TypeScript React",
|
||||
js = "JavaScript",
|
||||
jsx = "JavaScript React",
|
||||
py = "Python",
|
||||
lua = "Lua",
|
||||
go = "Go",
|
||||
rs = "Rust",
|
||||
rb = "Ruby",
|
||||
java = "Java",
|
||||
c = "C",
|
||||
cpp = "C++",
|
||||
cs = "C#",
|
||||
}
|
||||
|
||||
return {
|
||||
file_content = content,
|
||||
language = lang_map[ext] or ext,
|
||||
extension = ext,
|
||||
prompt_type = prompt_type,
|
||||
file_path = target_path,
|
||||
}
|
||||
return {
|
||||
file_content = content,
|
||||
language = lang_map[ext] or ext,
|
||||
extension = ext,
|
||||
prompt_type = prompt_type,
|
||||
file_path = target_path,
|
||||
}
|
||||
end
|
||||
|
||||
--- Parse LLM response and extract code
|
||||
---@param response string Raw LLM response
|
||||
---@return string Extracted code
|
||||
function M.extract_code(response)
|
||||
-- Remove markdown code blocks if present
|
||||
local code = response:gsub("```%w*\n?", ""):gsub("\n?```", "")
|
||||
local code = response
|
||||
|
||||
-- Trim whitespace
|
||||
code = code:match("^%s*(.-)%s*$")
|
||||
-- Remove markdown code blocks with language tags (```typescript, ```javascript, etc.)
|
||||
code = code:gsub("```%w+%s*\n", "")
|
||||
code = code:gsub("```%w+%s*$", "")
|
||||
code = code:gsub("^```%w*\n?", "")
|
||||
code = code:gsub("\n?```%s*$", "")
|
||||
code = code:gsub("\n```\n", "\n")
|
||||
code = code:gsub("```", "")
|
||||
|
||||
return code
|
||||
-- Remove common explanation prefixes that LLMs sometimes add
|
||||
code = code:gsub("^Here.-:\n", "")
|
||||
code = code:gsub("^Here's.-:\n", "")
|
||||
code = code:gsub("^This.-:\n", "")
|
||||
code = code:gsub("^The following.-:\n", "")
|
||||
code = code:gsub("^Below.-:\n", "")
|
||||
|
||||
-- Remove common explanation suffixes
|
||||
code = code:gsub("\n\nThis code.-$", "")
|
||||
code = code:gsub("\n\nThe above.-$", "")
|
||||
code = code:gsub("\n\nNote:.-$", "")
|
||||
code = code:gsub("\n\nExplanation:.-$", "")
|
||||
|
||||
-- Trim leading/trailing whitespace but preserve internal formatting
|
||||
code = code:match("^%s*(.-)%s*$") or code
|
||||
|
||||
return code
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
46
lua/codetyper/prompts/agent.lua
Normal file
46
lua/codetyper/prompts/agent.lua
Normal file
@@ -0,0 +1,46 @@
|
||||
---@mod codetyper.prompts.agent Agent prompts for Codetyper.nvim
|
||||
---
|
||||
--- System prompts for the agentic mode with tool use.
|
||||
|
||||
local M = {}
|
||||
|
||||
--- System prompt for agent mode
|
||||
M.system = [[You are an AI coding agent integrated into Neovim via Codetyper.nvim.
|
||||
You can read files, edit code, write new files, and run bash commands to help the user.
|
||||
|
||||
You have access to the following tools:
|
||||
- read_file: Read file contents
|
||||
- edit_file: Edit a file by finding and replacing specific content
|
||||
- write_file: Write or create a file
|
||||
- bash: Execute shell commands
|
||||
|
||||
GUIDELINES:
|
||||
1. Always read a file before editing it to understand its current state
|
||||
2. Use edit_file for targeted changes (find and replace specific content)
|
||||
3. Use write_file only for new files or complete rewrites
|
||||
4. Be conservative with bash commands - only run what's necessary
|
||||
5. After making changes, summarize what you did
|
||||
6. If a task requires multiple steps, think through the plan first
|
||||
|
||||
IMPORTANT:
|
||||
- Be precise with edit_file - the "find" content must match exactly
|
||||
- When editing, include enough context to make the match unique
|
||||
- Never delete files without explicit user confirmation
|
||||
- Always explain what you're doing and why
|
||||
]]
|
||||
|
||||
--- Tool usage instructions appended to system prompt
|
||||
M.tool_instructions = [[
|
||||
When you need to use a tool, output the tool call in a JSON block.
|
||||
After receiving the result, you can either call another tool or provide your final response.
|
||||
|
||||
SAFETY RULES:
|
||||
- Never run destructive bash commands (rm -rf, etc.) without confirmation
|
||||
- Always preserve existing functionality when editing
|
||||
- If unsure about a change, ask for clarification first
|
||||
]]
|
||||
|
||||
--- Prompt for when agent finishes
|
||||
M.completion = [[Based on the tool results above, please provide a summary of what was done and any next steps the user should take.]]
|
||||
|
||||
return M
|
||||
@@ -14,6 +14,8 @@ Requirements:
|
||||
- Include proper error handling
|
||||
- Use appropriate types (if applicable)
|
||||
- Make it efficient and readable
|
||||
|
||||
OUTPUT ONLY THE RAW CODE. No explanations, no markdown, no code fences.
|
||||
]]
|
||||
|
||||
--- Prompt template for creating a new class/module
|
||||
@@ -26,6 +28,8 @@ Requirements:
|
||||
- Include constructor/initialization
|
||||
- Implement proper encapsulation
|
||||
- Add necessary methods as described
|
||||
|
||||
OUTPUT ONLY THE RAW CODE. No explanations, no markdown, no code fences.
|
||||
]]
|
||||
|
||||
--- Prompt template for implementing an interface/trait
|
||||
@@ -37,6 +41,8 @@ Requirements:
|
||||
- Implement all required methods
|
||||
- Follow the interface contract exactly
|
||||
- Handle edge cases appropriately
|
||||
|
||||
OUTPUT ONLY THE RAW CODE. No explanations, no markdown, no code fences.
|
||||
]]
|
||||
|
||||
--- Prompt template for creating a React component
|
||||
@@ -49,6 +55,8 @@ Requirements:
|
||||
- Include proper TypeScript types (if .tsx)
|
||||
- Follow React best practices
|
||||
- Make it reusable and composable
|
||||
|
||||
OUTPUT ONLY THE RAW CODE. No explanations, no markdown, no code fences.
|
||||
]]
|
||||
|
||||
--- Prompt template for creating an API endpoint
|
||||
@@ -61,6 +69,8 @@ Requirements:
|
||||
- Proper error handling and status codes
|
||||
- Follow RESTful conventions
|
||||
- Include appropriate middleware
|
||||
|
||||
OUTPUT ONLY THE RAW CODE. No explanations, no markdown, no code fences.
|
||||
]]
|
||||
|
||||
--- Prompt template for creating a utility function
|
||||
@@ -73,6 +83,8 @@ Requirements:
|
||||
- Handle edge cases
|
||||
- Efficient implementation
|
||||
- Well-typed (if applicable)
|
||||
|
||||
OUTPUT ONLY THE RAW CODE. No explanations, no markdown, no code fences.
|
||||
]]
|
||||
|
||||
--- Prompt template for generic code generation
|
||||
@@ -88,6 +100,8 @@ Requirements:
|
||||
- Match existing code style
|
||||
- Follow best practices
|
||||
- Handle errors appropriately
|
||||
|
||||
OUTPUT ONLY THE RAW CODE. No explanations, no markdown, no code fences.
|
||||
]]
|
||||
|
||||
return M
|
||||
|
||||
@@ -5,20 +5,28 @@
|
||||
local M = {}
|
||||
|
||||
--- Base system prompt for code generation
|
||||
M.code_generation = [[You are an expert code generation assistant integrated into Neovim via Codetyper.nvim.
|
||||
Your task is to generate high-quality, production-ready code based on the user's prompt.
|
||||
M.code_generation = [[You are an expert code generation assistant integrated into Neovim.
|
||||
Your task is to generate production-ready {{language}} code that EXACTLY matches the style of the existing file.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. Output ONLY the code - no explanations, no markdown code blocks, no comments about what you did
|
||||
2. Match the coding style, conventions, and patterns of the existing file
|
||||
3. Use proper indentation and formatting for the language
|
||||
4. Follow best practices for the specific language/framework
|
||||
5. Preserve existing functionality unless explicitly asked to change it
|
||||
6. Use meaningful variable and function names
|
||||
7. Handle edge cases and errors appropriately
|
||||
ABSOLUTE RULES - FOLLOW STRICTLY:
|
||||
1. Output ONLY raw {{language}} code - NO explanations, NO markdown, NO code fences (```), NO comments about what you did
|
||||
2. DO NOT wrap output in ``` or any markdown - just raw code
|
||||
3. The output must be valid {{language}} code that can be directly inserted into the file
|
||||
4. MATCH the existing code patterns in the file:
|
||||
- Same indentation style (spaces/tabs)
|
||||
- Same naming conventions (camelCase, snake_case, PascalCase, etc.)
|
||||
- Same import/require style used in the file
|
||||
- Same comment style
|
||||
- Same function/class/module patterns used in the file
|
||||
5. If the file has existing exports, follow the same export pattern
|
||||
6. If the file uses certain libraries/frameworks, use the same ones
|
||||
7. Include proper types/annotations if the language supports them and the file uses them
|
||||
8. Include proper error handling following the file's patterns
|
||||
|
||||
Language: {{language}}
|
||||
File: {{filepath}}
|
||||
|
||||
REMEMBER: Output ONLY valid {{language}} code. No markdown. No explanations. Just the code.
|
||||
]]
|
||||
|
||||
--- System prompt for code explanation/ask
|
||||
@@ -38,59 +46,72 @@ IMPORTANT: When file contents are provided, analyze them carefully and base your
|
||||
]]
|
||||
|
||||
--- System prompt for refactoring
|
||||
M.refactor = [[You are an expert code refactoring assistant integrated into Neovim via Codetyper.nvim.
|
||||
Your task is to refactor code while maintaining its functionality.
|
||||
M.refactor = [[You are an expert code refactoring assistant integrated into Neovim.
|
||||
Your task is to refactor {{language}} code while maintaining its functionality.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. Output ONLY the refactored code - no explanations
|
||||
2. Preserve ALL existing functionality
|
||||
3. Improve code quality, readability, and maintainability
|
||||
4. Follow SOLID principles and best practices
|
||||
5. Keep the same coding style as the original
|
||||
ABSOLUTE RULES - FOLLOW STRICTLY:
|
||||
1. Output ONLY the refactored {{language}} code - NO explanations, NO markdown, NO code fences (```)
|
||||
2. DO NOT wrap output in ``` or any markdown - just raw code
|
||||
3. Preserve ALL existing functionality
|
||||
4. Improve code quality, readability, and maintainability
|
||||
5. Keep the EXACT same coding style as the original file
|
||||
6. Do not add new features unless explicitly requested
|
||||
7. Optimize performance where possible without sacrificing readability
|
||||
7. Output must be valid {{language}} code ready to replace the original
|
||||
|
||||
Language: {{language}}
|
||||
|
||||
REMEMBER: Output ONLY valid {{language}} code. No markdown. No explanations.
|
||||
]]
|
||||
|
||||
--- System prompt for documentation
|
||||
M.document = [[You are a documentation expert integrated into Neovim via Codetyper.nvim.
|
||||
Your task is to generate clear, comprehensive documentation for code.
|
||||
M.document = [[You are a documentation expert integrated into Neovim.
|
||||
Your task is to generate documentation comments for {{language}} code.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. Output ONLY the documentation/comments - ready to be inserted into code
|
||||
2. Use the appropriate documentation format for the language:
|
||||
- JavaScript/TypeScript: JSDoc
|
||||
- Python: Docstrings (Google or NumPy style)
|
||||
- Lua: LuaDoc/EmmyLua
|
||||
- Go: GoDoc
|
||||
- Rust: RustDoc
|
||||
- Java: Javadoc
|
||||
3. Document all parameters, return values, and exceptions
|
||||
4. Include usage examples where helpful
|
||||
5. Be concise but complete
|
||||
ABSOLUTE RULES - FOLLOW STRICTLY:
|
||||
1. Output ONLY the documentation comments - NO explanations, NO markdown
|
||||
2. DO NOT wrap output in ``` or any markdown - just raw comments
|
||||
3. Use the appropriate documentation format for {{language}}:
|
||||
- JavaScript/TypeScript/JSX/TSX: JSDoc (/** ... */)
|
||||
- Python: Docstrings (triple quotes)
|
||||
- Lua: LuaDoc/EmmyLua (---)
|
||||
- Go: GoDoc comments
|
||||
- Rust: RustDoc (///)
|
||||
- Ruby: YARD
|
||||
- PHP: PHPDoc
|
||||
- Java/Kotlin: Javadoc
|
||||
- C/C++: Doxygen
|
||||
4. Document all parameters, return values, and exceptions
|
||||
5. Output must be valid comment syntax for {{language}}
|
||||
|
||||
Language: {{language}}
|
||||
|
||||
REMEMBER: Output ONLY valid {{language}} documentation comments. No markdown.
|
||||
]]
|
||||
|
||||
--- System prompt for test generation
|
||||
M.test = [[You are a test generation expert integrated into Neovim via Codetyper.nvim.
|
||||
Your task is to generate comprehensive unit tests for the provided code.
|
||||
M.test = [[You are a test generation expert integrated into Neovim.
|
||||
Your task is to generate unit tests for {{language}} code.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. Output ONLY the test code - no explanations
|
||||
2. Use the appropriate testing framework for the language:
|
||||
- JavaScript/TypeScript: Jest or Vitest
|
||||
- Python: pytest
|
||||
ABSOLUTE RULES - FOLLOW STRICTLY:
|
||||
1. Output ONLY the test code - NO explanations, NO markdown, NO code fences (```)
|
||||
2. DO NOT wrap output in ``` or any markdown - just raw test code
|
||||
3. Use the appropriate testing framework for {{language}}:
|
||||
- JavaScript/TypeScript/JSX/TSX: Jest, Vitest, or Mocha
|
||||
- Python: pytest or unittest
|
||||
- Lua: busted or plenary
|
||||
- Go: testing package
|
||||
- Rust: built-in tests
|
||||
3. Cover happy paths, edge cases, and error scenarios
|
||||
4. Use descriptive test names
|
||||
- Rust: built-in #[test]
|
||||
- Ruby: RSpec or Minitest
|
||||
- PHP: PHPUnit
|
||||
- Java/Kotlin: JUnit
|
||||
- C/C++: Google Test or Catch2
|
||||
4. Cover happy paths, edge cases, and error scenarios
|
||||
5. Follow AAA pattern: Arrange, Act, Assert
|
||||
6. Mock external dependencies appropriately
|
||||
6. Output must be valid {{language}} test code
|
||||
|
||||
Language: {{language}}
|
||||
|
||||
REMEMBER: Output ONLY valid {{language}} test code. No markdown. No explanations.
|
||||
]]
|
||||
|
||||
return M
|
||||
|
||||
@@ -10,6 +10,19 @@ local CODER_FOLDER = ".coder"
|
||||
--- Name of the tree log file
|
||||
local TREE_LOG_FILE = "tree.log"
|
||||
|
||||
--- Name of the settings file
|
||||
local SETTINGS_FILE = "settings.json"
|
||||
|
||||
--- Default settings for the coder folder
|
||||
local DEFAULT_SETTINGS = {
|
||||
["editor.fontSize"] = 14,
|
||||
["editor.tabSize"] = 2,
|
||||
["files.autoSave"] = "afterDelay",
|
||||
["files.autoSaveDelay"] = 1000,
|
||||
["terminal.integrated.fontSize"] = 14,
|
||||
["workbench.colorTheme"] = "Default Dark+",
|
||||
}
|
||||
|
||||
--- Get the path to the .coder folder
|
||||
---@return string|nil Path to .coder folder or nil
|
||||
function M.get_coder_folder()
|
||||
@@ -30,6 +43,57 @@ function M.get_tree_log_path()
|
||||
return coder_folder .. "/" .. TREE_LOG_FILE
|
||||
end
|
||||
|
||||
--- Get the path to the settings.json file
|
||||
---@return string|nil Path to settings.json or nil
|
||||
function M.get_settings_path()
|
||||
local coder_folder = M.get_coder_folder()
|
||||
if not coder_folder then
|
||||
return nil
|
||||
end
|
||||
return coder_folder .. "/" .. SETTINGS_FILE
|
||||
end
|
||||
|
||||
--- Ensure settings.json exists with default settings
|
||||
---@return boolean Success status
|
||||
function M.ensure_settings()
|
||||
local settings_path = M.get_settings_path()
|
||||
if not settings_path then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check if file already exists
|
||||
local stat = vim.loop.fs_stat(settings_path)
|
||||
if stat then
|
||||
return true -- File already exists, don't overwrite
|
||||
end
|
||||
|
||||
-- Create settings file with defaults
|
||||
local json_content = vim.fn.json_encode(DEFAULT_SETTINGS)
|
||||
-- Pretty print the JSON
|
||||
local ok, pretty_json = pcall(function()
|
||||
return vim.fn.system({ "python3", "-m", "json.tool" }, json_content)
|
||||
end)
|
||||
|
||||
if not ok or vim.v.shell_error ~= 0 then
|
||||
-- Fallback to simple formatting if python not available
|
||||
pretty_json = "{\n"
|
||||
local keys = vim.tbl_keys(DEFAULT_SETTINGS)
|
||||
table.sort(keys)
|
||||
for i, key in ipairs(keys) do
|
||||
local value = DEFAULT_SETTINGS[key]
|
||||
local value_str = type(value) == "string" and ('"' .. value .. '"') or tostring(value)
|
||||
pretty_json = pretty_json .. ' "' .. key .. '": ' .. value_str
|
||||
if i < #keys then
|
||||
pretty_json = pretty_json .. ","
|
||||
end
|
||||
pretty_json = pretty_json .. "\n"
|
||||
end
|
||||
pretty_json = pretty_json .. "}\n"
|
||||
end
|
||||
|
||||
return utils.write_file(settings_path, pretty_json)
|
||||
end
|
||||
|
||||
--- Ensure .coder folder exists
|
||||
---@return boolean Success status
|
||||
function M.ensure_coder_folder()
|
||||
@@ -198,10 +262,51 @@ function M.update_tree_log()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Cache to track initialized projects (by root path)
|
||||
local initialized_projects = {}
|
||||
|
||||
--- Check if project is already initialized
|
||||
---@param root string Project root path
|
||||
---@return boolean
|
||||
local function is_project_initialized(root)
|
||||
return initialized_projects[root] == true
|
||||
end
|
||||
|
||||
--- Initialize tree logging (called on setup)
|
||||
function M.setup()
|
||||
---@param force? boolean Force re-initialization even if cached
|
||||
---@return boolean success
|
||||
function M.setup(force)
|
||||
local coder_folder = M.get_coder_folder()
|
||||
if not coder_folder then
|
||||
return false
|
||||
end
|
||||
|
||||
local root = utils.get_project_root()
|
||||
if not root then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Skip if already initialized (unless forced)
|
||||
if not force and is_project_initialized(root) then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Ensure .coder folder exists
|
||||
if not M.ensure_coder_folder() then
|
||||
utils.notify("Failed to create .coder folder", vim.log.levels.ERROR)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Create settings.json with defaults if it doesn't exist
|
||||
M.ensure_settings()
|
||||
|
||||
-- Create initial tree log
|
||||
M.update_tree_log()
|
||||
|
||||
-- Mark project as initialized
|
||||
initialized_projects[root] = true
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- Get file statistics from tree
|
||||
|
||||
@@ -17,7 +17,8 @@ function M.get_project_root()
|
||||
end
|
||||
found = vim.fn.finddir(marker, current .. ";")
|
||||
if found ~= "" then
|
||||
return vim.fn.fnamemodify(found, ":p:h")
|
||||
-- For directories, :p:h gives the dir itself, so we need :p:h:h to get parent
|
||||
return vim.fn.fnamemodify(found, ":p:h:h")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
75
lua/codetyper/utils/langmap.lua
Normal file
75
lua/codetyper/utils/langmap.lua
Normal file
@@ -0,0 +1,75 @@
|
||||
local lang_map = {
|
||||
-- JavaScript/TypeScript
|
||||
ts = "TypeScript",
|
||||
tsx = "TypeScript React (TSX)",
|
||||
js = "JavaScript",
|
||||
jsx = "JavaScript React (JSX)",
|
||||
mjs = "JavaScript (ESM)",
|
||||
cjs = "JavaScript (CommonJS)",
|
||||
-- Python
|
||||
py = "Python",
|
||||
pyw = "Python",
|
||||
pyx = "Cython",
|
||||
-- Systems languages
|
||||
c = "C",
|
||||
h = "C Header",
|
||||
cpp = "C++",
|
||||
hpp = "C++ Header",
|
||||
cc = "C++",
|
||||
cxx = "C++",
|
||||
rs = "Rust",
|
||||
go = "Go",
|
||||
-- JVM languages
|
||||
java = "Java",
|
||||
kt = "Kotlin",
|
||||
kts = "Kotlin Script",
|
||||
scala = "Scala",
|
||||
clj = "Clojure",
|
||||
-- Web
|
||||
html = "HTML",
|
||||
css = "CSS",
|
||||
scss = "SCSS",
|
||||
sass = "Sass",
|
||||
less = "Less",
|
||||
vue = "Vue",
|
||||
svelte = "Svelte",
|
||||
-- Scripting
|
||||
lua = "Lua",
|
||||
rb = "Ruby",
|
||||
php = "PHP",
|
||||
pl = "Perl",
|
||||
sh = "Shell (Bash)",
|
||||
bash = "Bash",
|
||||
zsh = "Zsh",
|
||||
fish = "Fish",
|
||||
ps1 = "PowerShell",
|
||||
-- .NET
|
||||
cs = "C#",
|
||||
fs = "F#",
|
||||
vb = "Visual Basic",
|
||||
-- Data/Config
|
||||
json = "JSON",
|
||||
yaml = "YAML",
|
||||
yml = "YAML",
|
||||
toml = "TOML",
|
||||
xml = "XML",
|
||||
sql = "SQL",
|
||||
graphql = "GraphQL",
|
||||
-- Other
|
||||
swift = "Swift",
|
||||
dart = "Dart",
|
||||
ex = "Elixir",
|
||||
exs = "Elixir Script",
|
||||
erl = "Erlang",
|
||||
hs = "Haskell",
|
||||
ml = "OCaml",
|
||||
r = "R",
|
||||
jl = "Julia",
|
||||
nim = "Nim",
|
||||
zig = "Zig",
|
||||
v = "V",
|
||||
md = "Markdown",
|
||||
mdx = "MDX",
|
||||
}
|
||||
|
||||
return lang_map
|
||||
@@ -1,121 +1,148 @@
|
||||
-- Codetyper.nvim - AI-powered coding partner for Neovim
|
||||
-- Plugin loader
|
||||
|
||||
local g = vim.g
|
||||
local fn = vim.fn
|
||||
local api = vim.api
|
||||
local cmd = vim.cmd
|
||||
|
||||
-- Prevent loading twice
|
||||
if vim.g.loaded_codetyper then
|
||||
return
|
||||
if g.loaded_codetyper then
|
||||
return
|
||||
end
|
||||
vim.g.loaded_codetyper = true
|
||||
g.loaded_codetyper = true
|
||||
|
||||
-- Minimum Neovim version check
|
||||
if vim.fn.has("nvim-0.8.0") == 0 then
|
||||
vim.api.nvim_err_writeln("Codetyper.nvim requires Neovim 0.8.0 or higher")
|
||||
return
|
||||
if fn.has("nvim-0.8.0") == 0 then
|
||||
api.nvim_err_writeln("Codetyper.nvim requires Neovim 0.8.0 or higher")
|
||||
return
|
||||
end
|
||||
|
||||
--- Initialize codetyper plugin fully
|
||||
--- Creates .coder folder, settings.json, tree.log, .gitignore
|
||||
--- Also registers autocmds for /@ @/ prompt detection
|
||||
---@return boolean success
|
||||
local function init_coder_files()
|
||||
local ok, err = pcall(function()
|
||||
-- Full plugin initialization (includes config, commands, autocmds, tree, gitignore)
|
||||
local codetyper = require("codetyper")
|
||||
if not codetyper.is_initialized() then
|
||||
codetyper.setup()
|
||||
end
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
vim.notify("[Codetyper] Failed to initialize: " .. tostring(err), vim.log.levels.ERROR)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Initialize .coder folder and tree.log on project open
|
||||
vim.api.nvim_create_autocmd("VimEnter", {
|
||||
callback = function()
|
||||
-- Delay slightly to ensure cwd is set
|
||||
vim.defer_fn(function()
|
||||
local tree = require("codetyper.tree")
|
||||
tree.setup()
|
||||
|
||||
-- Also ensure gitignore is updated
|
||||
local gitignore = require("codetyper.gitignore")
|
||||
gitignore.ensure_ignored()
|
||||
end, 100)
|
||||
end,
|
||||
desc = "Initialize Codetyper .coder folder on startup",
|
||||
api.nvim_create_autocmd("VimEnter", {
|
||||
callback = function()
|
||||
-- Delay slightly to ensure cwd is set
|
||||
vim.defer_fn(function()
|
||||
init_coder_files()
|
||||
end, 100)
|
||||
end,
|
||||
desc = "Initialize Codetyper .coder folder on startup",
|
||||
})
|
||||
|
||||
-- Also initialize on directory change
|
||||
vim.api.nvim_create_autocmd("DirChanged", {
|
||||
callback = function()
|
||||
vim.defer_fn(function()
|
||||
local tree = require("codetyper.tree")
|
||||
tree.setup()
|
||||
|
||||
local gitignore = require("codetyper.gitignore")
|
||||
gitignore.ensure_ignored()
|
||||
end, 100)
|
||||
end,
|
||||
desc = "Initialize Codetyper .coder folder on directory change",
|
||||
api.nvim_create_autocmd("DirChanged", {
|
||||
callback = function()
|
||||
vim.defer_fn(function()
|
||||
init_coder_files()
|
||||
end, 100)
|
||||
end,
|
||||
desc = "Initialize Codetyper .coder folder on directory change",
|
||||
})
|
||||
|
||||
-- Auto-initialize when opening a coder file (for nvim-tree, telescope, etc.)
|
||||
vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile", "BufEnter" }, {
|
||||
pattern = "*.coder.*",
|
||||
callback = function()
|
||||
-- Initialize plugin if not already done
|
||||
local codetyper = require("codetyper")
|
||||
if not codetyper.is_initialized() then
|
||||
codetyper.setup()
|
||||
end
|
||||
end,
|
||||
desc = "Auto-initialize Codetyper when opening coder files",
|
||||
api.nvim_create_autocmd({ "BufRead", "BufNewFile", "BufEnter" }, {
|
||||
pattern = "*.coder.*",
|
||||
callback = function()
|
||||
-- Initialize plugin if not already done
|
||||
local codetyper = require("codetyper")
|
||||
if not codetyper.is_initialized() then
|
||||
codetyper.setup()
|
||||
end
|
||||
end,
|
||||
desc = "Auto-initialize Codetyper when opening coder files",
|
||||
})
|
||||
|
||||
-- Lazy-load the plugin on first command usage
|
||||
vim.api.nvim_create_user_command("Coder", function(opts)
|
||||
require("codetyper").setup()
|
||||
-- Re-execute the command now that plugin is loaded
|
||||
vim.cmd("Coder " .. (opts.args or ""))
|
||||
api.nvim_create_user_command("Coder", function(opts)
|
||||
require("codetyper").setup()
|
||||
-- Re-execute the command now that plugin is loaded
|
||||
cmd("Coder " .. (opts.args or ""))
|
||||
end, {
|
||||
nargs = "?",
|
||||
complete = function()
|
||||
return {
|
||||
"open", "close", "toggle", "process", "status", "focus",
|
||||
"tree", "tree-view", "reset", "gitignore",
|
||||
"ask", "ask-close", "ask-toggle", "ask-clear",
|
||||
}
|
||||
end,
|
||||
desc = "Codetyper.nvim commands",
|
||||
nargs = "?",
|
||||
complete = function()
|
||||
return {
|
||||
"open",
|
||||
"close",
|
||||
"toggle",
|
||||
"process",
|
||||
"status",
|
||||
"focus",
|
||||
"tree",
|
||||
"tree-view",
|
||||
"reset",
|
||||
"gitignore",
|
||||
"ask",
|
||||
"ask-close",
|
||||
"ask-toggle",
|
||||
"ask-clear",
|
||||
}
|
||||
end,
|
||||
desc = "Codetyper.nvim commands",
|
||||
})
|
||||
|
||||
-- Lazy-load aliases
|
||||
vim.api.nvim_create_user_command("CoderOpen", function()
|
||||
require("codetyper").setup()
|
||||
vim.cmd("CoderOpen")
|
||||
api.nvim_create_user_command("CoderOpen", function()
|
||||
require("codetyper").setup()
|
||||
cmd("CoderOpen")
|
||||
end, { desc = "Open Coder view" })
|
||||
|
||||
vim.api.nvim_create_user_command("CoderClose", function()
|
||||
require("codetyper").setup()
|
||||
vim.cmd("CoderClose")
|
||||
api.nvim_create_user_command("CoderClose", function()
|
||||
require("codetyper").setup()
|
||||
cmd("CoderClose")
|
||||
end, { desc = "Close Coder view" })
|
||||
|
||||
vim.api.nvim_create_user_command("CoderToggle", function()
|
||||
require("codetyper").setup()
|
||||
vim.cmd("CoderToggle")
|
||||
api.nvim_create_user_command("CoderToggle", function()
|
||||
require("codetyper").setup()
|
||||
cmd("CoderToggle")
|
||||
end, { desc = "Toggle Coder view" })
|
||||
|
||||
vim.api.nvim_create_user_command("CoderProcess", function()
|
||||
require("codetyper").setup()
|
||||
vim.cmd("CoderProcess")
|
||||
api.nvim_create_user_command("CoderProcess", function()
|
||||
require("codetyper").setup()
|
||||
cmd("CoderProcess")
|
||||
end, { desc = "Process prompt and generate code" })
|
||||
|
||||
vim.api.nvim_create_user_command("CoderTree", function()
|
||||
require("codetyper").setup()
|
||||
vim.cmd("CoderTree")
|
||||
api.nvim_create_user_command("CoderTree", function()
|
||||
require("codetyper").setup()
|
||||
cmd("CoderTree")
|
||||
end, { desc = "Refresh tree.log" })
|
||||
|
||||
vim.api.nvim_create_user_command("CoderTreeView", function()
|
||||
require("codetyper").setup()
|
||||
vim.cmd("CoderTreeView")
|
||||
api.nvim_create_user_command("CoderTreeView", function()
|
||||
require("codetyper").setup()
|
||||
cmd("CoderTreeView")
|
||||
end, { desc = "View tree.log" })
|
||||
|
||||
-- Ask panel commands
|
||||
vim.api.nvim_create_user_command("CoderAsk", function()
|
||||
require("codetyper").setup()
|
||||
vim.cmd("CoderAsk")
|
||||
api.nvim_create_user_command("CoderAsk", function()
|
||||
require("codetyper").setup()
|
||||
cmd("CoderAsk")
|
||||
end, { desc = "Open Ask panel" })
|
||||
|
||||
vim.api.nvim_create_user_command("CoderAskToggle", function()
|
||||
require("codetyper").setup()
|
||||
vim.cmd("CoderAskToggle")
|
||||
api.nvim_create_user_command("CoderAskToggle", function()
|
||||
require("codetyper").setup()
|
||||
cmd("CoderAskToggle")
|
||||
end, { desc = "Toggle Ask panel" })
|
||||
|
||||
vim.api.nvim_create_user_command("CoderAskClear", function()
|
||||
require("codetyper").setup()
|
||||
vim.cmd("CoderAskClear")
|
||||
api.nvim_create_user_command("CoderAskClear", function()
|
||||
require("codetyper").setup()
|
||||
cmd("CoderAskClear")
|
||||
end, { desc = "Clear Ask history" })
|
||||
|
||||
Reference in New Issue
Block a user