diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..a86d5f1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -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 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..e647772 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,2 @@ +blank_issues_enabled: true +version: 2.1 diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..32fc52e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -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 ... diff --git a/.github/workflows/close-stale-issues-and-prs.yaml b/.github/workflows/close-stale-issues-and-prs.yaml new file mode 100644 index 0000000..455147a --- /dev/null +++ b/.github/workflows/close-stale-issues-and-prs.yaml @@ -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 diff --git a/.github/workflows/lua.yaml b/.github/workflows/lua.yaml new file mode 100644 index 0000000..65b7349 --- /dev/null +++ b/.github/workflows/lua.yaml @@ -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 diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 0000000..b90dba6 --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -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() diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..6ba9f1f --- /dev/null +++ b/.github/workflows/release.yaml @@ -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<> $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 + }) diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml new file mode 100644 index 0000000..4c3519c --- /dev/null +++ b/.github/workflows/rust.yaml @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1508cce..ed27e76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 ``` diff --git a/lua/codetyper/llm/init.lua b/lua/codetyper/llm/init.lua index 2d03018..8df664a 100644 --- a/lua/codetyper/llm/init.lua +++ b/lua/codetyper/llm/init.lua @@ -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,37 +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") - -- 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 + -- 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 @@ -62,124 +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 = { - -- 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 { - 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) - local code = response - - -- 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("```", "") - - -- 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 + local code = response - return code + -- 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("```", "") + + -- 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 diff --git a/lua/codetyper/utils/langmap.lua b/lua/codetyper/utils/langmap.lua new file mode 100644 index 0000000..351622b --- /dev/null +++ b/lua/codetyper/utils/langmap.lua @@ -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 diff --git a/plugin/codetyper.lua b/plugin/codetyper.lua index 720eb32..9e365dc 100644 --- a/plugin/codetyper.lua +++ b/plugin/codetyper.lua @@ -1,121 +1,137 @@ -- 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 .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() + 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", }) -- 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() + 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", }) -- 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" }) diff --git a/scripts/create_support_files.sh b/scripts/create_support_files.sh new file mode 100644 index 0000000..0ea966f --- /dev/null +++ b/scripts/create_support_files.sh @@ -0,0 +1,25 @@ +# Create .coder/ folder if does not exist +mkdir -p .coder +# Create .coder/settings.json with default settings if it does not exist +if [ ! -f .coder/settings.json ]; then + cat < .coder/settings.json + { + "editor.fontSize": 14, + "editor.tabSize": 2, + "files.autoSave": "afterDelay", + "files.autoSaveDelay": 1000, + "terminal.integrated.fontSize": 14, + "workbench.colorTheme": "Default Dark+" + } +EOL +fi + +# Add the .coder/ folder to .gitignore if not already present +if ! grep -q "^.coder/$" .gitignore; then + echo ".coder/" >> .gitignore +fi + +# Add the ./**/*.coder.* files to .gitignore if not already present +if ! grep -q "^.*/\.coder/.*$" .gitignore; then + echo ".*/.coder/.*" >> .gitignore +fi