41 Commits

Author SHA1 Message Date
Myriad-Dreamin
1f8168577b wip 2026-04-20 23:10:08 +08:00
Myriad-Dreamin
60cab39f92 dev: make server public 2026-04-20 23:10:08 +08:00
Myriad-Dreamin
67bedd1ae0 dev: index nodes 2026-04-20 23:10:08 +08:00
Myriad-Dreamin
7ab8ad9513 fix: crash 2026-04-20 23:10:08 +08:00
Myriad-Dreamin
c11178fd77 dev: builtin lib 2026-04-20 23:10:08 +08:00
Myriad-Dreamin
150f47c590 hardcode clang 2026-04-20 23:10:08 +08:00
Myriad-Dreamin
b7987fded3 dev: workaround cc1 compilation 2026-01-26 17:45:00 +08:00
Myriad-Dreamin
44a4bd4107 dev: index notify 2026-01-26 17:44:47 +08:00
Myriad-Dreamin
044e4c4b27 feat: implement serialize for optional 2026-01-26 17:44:47 +08:00
Myriad-Dreamin
68eb63ba04 ActiveFile 2026-01-26 17:44:47 +08:00
Myriad-Dreamin
39d3648fdd feat: add method to check indices size 2026-01-26 17:43:16 +08:00
Myriad-Dreamin
6640c05d66 build: cannot build cpptrace 2026-01-26 17:43:16 +08:00
Myriad-Dreamin
69ac764ef2 feat: split copy_headers for external builds 2026-01-26 17:43:16 +08:00
Myriad-Dreamin
04c6ca5337 fix: correct server ref impl 2026-01-26 17:43:16 +08:00
Myriad-Dreamin
2408978d2d fix: correct plugin_paths decl 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
74f75f107f fix: compile error 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
0e2e487bc9 feat: introduce any type alias for llvm::json::Value 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
6be48bccd2 feat: implement dummy workspace/executeCommand 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
4262734d21 dev: simplify protocol def 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
ed0d7db3db dev: simplify protocol def 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
824b305f93 dev: simplify protocol def 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
f89793c66d dev: more lifetime hooks 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
4425fb5244 dev: simplify 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
25a85a3b8e feat: implement apis 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
0f95344abe fix: typo 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
3511915886 dev: remove useless comments 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
6e50451c43 fix: typo 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
99d9363b95 fix: paths 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
7533d4d15e docs: compile and laod plugin sections 2026-01-26 01:01:39 +08:00
Myriad-Dreamin
a118c16e96 feat: add minimal support to clice server plugins 2026-01-26 01:01:39 +08:00
ykiko
c0ffd2369b refactor: unify the CompilationUnitRef usage (#346) 2026-01-12 00:21:35 +08:00
Myriad-Dreamin
d6733dd43d feat: replace compile_commands_dirs with compile_commands_paths (#343) 2026-01-10 23:23:32 +08:00
Myriad-Dreamin
53689f2256 fix: prevent bad argument pop back when querying toolchain (#342) 2026-01-10 22:43:47 +08:00
Myriad-Dreamin
f30f68f573 fix: compiling C source file without -std=c++20 (#334) 2026-01-07 23:13:21 +08:00
ykiko
dd8f0dd90d refactor: diagnostic handling (#337) 2026-01-03 16:23:06 +08:00
ykiko
dee5e136b7 fix: docs spelling and workflow check (#336) 2026-01-01 00:48:14 +08:00
ykiko
4d16cf7b0a docs: update build (#335) 2026-01-01 00:36:07 +08:00
Myriad-Dreamin
c6d87cccf3 fix: stuck caused by Network:on_read (#333) 2025-12-29 22:58:47 +08:00
ykiko
aa3e5111de fix: only publish when a tag is created (#332) 2025-12-29 11:55:18 +08:00
ykiko
7a29560065 build: optimize the workflow (#331) 2025-12-29 09:19:45 +08:00
ykiko
7105e36803 chore: use pixi to manage the format tools and format the world (#330) 2025-12-28 19:36:06 +08:00
156 changed files with 10489 additions and 9798 deletions

View File

@@ -1,5 +1,4 @@
# clang-format configuration
# compatible with clang-format 18
UseTab: Never
ColumnLimit: 100

View File

@@ -1,2 +0,0 @@
Diagnostics:
UnusedIncludes: None

2
.coderabbit.yaml Normal file
View File

@@ -0,0 +1,2 @@
chat:
auto_reply: false

0
.codex Normal file
View File

View File

@@ -1,10 +1,9 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
title: ""
labels: ""
assignees: ""
---
**Describe the bug**
@@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
@@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@@ -1,10 +1,9 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
title: ""
labels: ""
assignees: ""
---
**Is your feature request related to a problem? Please describe.**

20
.github/actions/setup-pixi/action.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: "Setup Pixi"
description: "setup pixi"
inputs:
environments:
description: "The pixi environments to install (e.g. default, docs, test)"
required: false
default: "default"
runs:
using: "composite"
steps:
- name: Setup Pixi
uses: prefix-dev/setup-pixi@v0.9.3
with:
pixi-version: v0.62.0
environments: ${{ inputs.environments }}
activate-environment: true
cache: true
locked: true

View File

@@ -21,7 +21,6 @@ jobs:
- os: windows-2025
llvm_mode: RelWithDebInfo
lto: ON
- os: ubuntu-24.04
llvm_mode: Debug
lto: OFF
@@ -31,7 +30,6 @@ jobs:
- os: ubuntu-24.04
llvm_mode: RelWithDebInfo
lto: ON
- os: macos-15
llvm_mode: Debug
lto: OFF
@@ -41,9 +39,7 @@ jobs:
- os: macos-15
llvm_mode: RelWithDebInfo
lto: ON
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

View File

@@ -1,52 +1,33 @@
name: format
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_call:
jobs:
check:
check-format:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Pixi
uses: prefix-dev/setup-pixi@v0.9.3
- uses: ./.github/actions/setup-pixi
with:
pixi-version: v0.61.0
environments: develop
activate-environment: true
cache: true
locked: true
environments: format
- name: Run check
id: precommit
run: |
pixi run ci-check
- name: Run formatter
run: pixi run format
continue-on-error: true
- name: Show diff on failure
if: steps.precommit.outcome == 'failure'
run: |
echo "❌ pre-commit found issues that it auto-fixed."
echo " Please run 'pre-commit run --all-files' locally,"
echo " commit the changes, and push again."
echo ""
echo "👇 The required changes are shown below:"
git diff
- name: Fail the job if pre-commit failed
if: steps.precommit.outcome == 'failure'
run: |
echo "pre-commit checks failed."
exit 1
- name: Auto correct
uses: huacnlee/autocorrect-action@main
uses: huacnlee/autocorrect-action@v2
with:
args: --lint ./docs
continue-on-error: true
- name: Suggest changes
if: github.event_name == 'pull_request'
uses: reviewdog/action-suggester@v1.24.0
with:
tool_name: "fmt"
fail_level: any
filter_mode: nofilter

View File

@@ -1,57 +0,0 @@
name: cmake
on:
push:
branches: [main]
paths:
- ".github/workflows/cmake.yml"
- "include/**"
- "src/**"
- "tests/**"
- "CMakeLists.txt"
pull_request:
branches: [main]
paths:
- ".github/workflows/cmake.yml"
- "include/**"
- "src/**"
- "tests/**"
- "config/**"
- "CMakeLists.txt"
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: windows-2025
build_type: RelWithDebInfo
- os: ubuntu-24.04
build_type: Debug
- os: macos-15
build_type: Debug
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Pixi
uses: prefix-dev/setup-pixi@v0.9.3
with:
pixi-version: v0.61.0
environments: develop
activate-environment: true
cache: true
locked: true
- name: Build
run: |
pixi run ci-cmake-build ${{ matrix.build_type }}
- name: Test
run: |
pixi run ci-cmake-test

28
.github/workflows/deploy-docs.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: deploy
on:
workflow_call:
jobs:
deploy-docs:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: ./.github/actions/setup-pixi
with:
environments: node
- name: Build docs
run: pixi run build-docs
- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/.vitepress/dist
cname: clice.io

View File

@@ -1,43 +0,0 @@
name: deploy
on:
push:
branches: [main]
paths:
- "docs/**"
- ".github/workflows/deploy.yml"
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
cache-dependency-path: docs/package-lock.json
- name: Install dependencies in docs
run: |
cd docs
npm install
- name: Build docs
run: |
cd docs
npm run docs:build
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/.vitepress/dist
cname: clice.io

123
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,123 @@
name: main
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
jobs:
changes:
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
format: ${{ steps.filter.outputs.format }}
docs: ${{ steps.filter.outputs.docs }}
clice: ${{ steps.filter.outputs.clice }}
vscode: ${{ steps.filter.outputs.vscode }}
cmake: ${{ steps.filter.outputs.cmake }}
xmake: ${{ steps.filter.outputs.xmake }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
format:
- '**/*.{h,c,cpp,hpp,ts,js,lua,md,yml,yaml}'
docs:
- 'docs/**'
- '.github/workflows/deploy-docs.yml'
clice:
- 'src/**'
- 'include/**'
- 'CMakeLists.txt'
- '.github/workflows/publish-clice.yml'
vscode:
- 'editors/vscode/**'
- '.github/workflows/publish-vscode.yml'
cmake:
- 'CMakeLists.txt'
- 'src/**'
- 'include/**'
- 'tests/**'
- 'config/**'
- '.github/workflows/test-cmake.yml'
xmake:
- 'xmake.lua'
- 'src/**'
- 'include/**'
- 'tests/**'
- 'config/**'
- '.github/workflows/test-xmake.yml'
format:
needs: changes
permissions:
contents: read
checks: write
issues: write
pull-requests: write
if: ${{ needs.changes.outputs.format == 'true' }}
uses: ./.github/workflows/check-format.yml
deploy:
needs: changes
if: ${{ needs.changes.outputs.docs == 'true' }}
permissions:
contents: write
uses: ./.github/workflows/deploy-docs.yml
clice:
needs: changes
if: ${{ needs.changes.outputs.clice == 'true' }}
uses: ./.github/workflows/publish-clice.yml
vscode:
needs: changes
if: ${{ needs.changes.outputs.vscode == 'true' }}
uses: ./.github/workflows/publish-vscode.yml
cmake:
needs: changes
if: ${{ needs.changes.outputs.cmake == 'true' }}
uses: ./.github/workflows/test-cmake.yml
xmake:
needs: changes
if: ${{ needs.changes.outputs.xmake == 'true' }}
uses: ./.github/workflows/test-xmake.yml
release-clice:
permissions:
contents: write
if: startsWith(github.ref, 'refs/tags/v')
uses: ./.github/workflows/publish-clice.yml
secrets: inherit
release-vscode:
permissions:
contents: write
if: startsWith(github.ref, 'refs/tags/v')
uses: ./.github/workflows/publish-vscode.yml
secrets: inherit
checks-passed:
if: ${{ always() && !startsWith(github.ref, 'refs/tags/') }}
needs:
- format
- deploy
- clice
- vscode
- cmake
- xmake
runs-on: ubuntu-latest
steps:
- name: Check results
uses: re-actors/alls-green@release/v1
with:
allowed-skips: format,deploy,clice,vscode,cmake,xmake
jobs: ${{ toJSON(needs) }}

View File

@@ -1,20 +1,10 @@
name: package
permissions:
contents: write
name: clice
on:
push:
tags:
- "v*"
pull_request:
branches: [main]
paths:
- ".github/workflows/package.yml"
workflow_call:
jobs:
package:
publish-clice:
strategy:
fail-fast: false
matrix:
@@ -24,21 +14,18 @@ jobs:
asset_name: clice-x64-windows-msvc.zip
symbol_artifact_name: clice-symbol.zip
symbol_asset_name: clice-x64-windows-msvc-symbol.zip
toolchain: clang-cl
- os: ubuntu-24.04
artifact_name: clice.tar.gz
asset_name: clice-x86_64-linux-gnu.tar.gz
symbol_artifact_name: clice-symbol.tar.gz
symbol_asset_name: clice-x86_64-linux-gnu-symbol.tar.gz
toolchain: clang-20
- os: macos-15
artifact_name: clice.tar.gz
asset_name: clice-arm64-macos-darwin.tar.gz
symbol_artifact_name: clice-symbol.tar.gz
symbol_asset_name: clice-arm64-macos-darwin-symbol.tar.gz
toolchain: clang
runs-on: ${{ matrix.os }}
@@ -57,13 +44,9 @@ jobs:
build-cache: true
build-cache-key: ${{ matrix.os }}-build-release-v1
- name: Setup Pixi
uses: prefix-dev/setup-pixi@v0.9.3
- uses: ./.github/actions/setup-pixi
with:
pixi-version: v0.61.0
activate-environment: true
cache: true
locked: true
environments: package
- name: Remove ci llvm toolchain on Windows
if: runner.os == 'Windows'
@@ -73,11 +56,10 @@ jobs:
xmake lua os.rmdir "C:/Program Files/LLVM"
- name: Package
run: |
pixi run package
run: pixi run package
- name: Upload Main Package to Release
if: github.event_name == 'push'
if: startsWith(github.ref, 'refs/tags/v')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
@@ -87,7 +69,7 @@ jobs:
overwrite: true
- name: Upload Symbol Package to Release
if: github.event_name == 'push'
if: startsWith(github.ref, 'refs/tags/v')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,53 +1,36 @@
name: CI/CD VS Code Extension
name: vscode
on:
push:
tags: ["v*"]
branches: ["main"]
paths: ["editors/vscode/**"]
pull_request:
branches: ["main"]
paths: ["editors/vscode/**"]
workflow_call:
jobs:
publish:
publish-vscode:
runs-on: ubuntu-latest
permissions:
contents: write
defaults:
run:
working-directory: editors/vscode
steps:
- name: Checkout
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
- uses: ./.github/actions/setup-pixi
with:
node-version: "20"
- name: Install dependencies
run: npm ci
- name: Install vsce
run: npm install -g vsce
environments: node
- name: Publish and Package to Marketplace
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
run: |
FLAG="${{ contains(github.ref_name, '-') && '--pre-release' || '' }}"
npm run package -- $FLAG
pixi run build-vscode $FLAG
if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
npm run publish -- -p "$VSCE_PAT" $FLAG
pixi run publish-vscode -p "$VSCE_PAT" $FLAG
fi
- name: Upload .vsix to Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/v')
with:
files: "editors/vscode/*.vsix"
tag_name: ${{ github.ref }}

29
.github/workflows/test-cmake.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: cmake
on:
workflow_call:
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: windows-2025
build_type: RelWithDebInfo
- os: ubuntu-24.04
build_type: Debug
- os: macos-15
build_type: Debug
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: ./.github/actions/setup-pixi
- name: Build
run: pixi run build ${{ matrix.build_type }} ON
- name: Test
run: pixi run test ${{ matrix.build_type }}

View File

@@ -1,24 +1,7 @@
name: xmake
on:
push:
branches: [main]
paths:
- ".github/workflows/xmake.yml"
- "include/**"
- "src/**"
- "tests/**"
- "xmake.lua"
pull_request:
branches: [main]
paths:
- ".github/workflows/xmake.yml"
- "include/**"
- "src/**"
- "tests/**"
- "config/**"
- "xmake.lua"
workflow_call:
jobs:
build:
@@ -30,9 +13,7 @@ jobs:
exclude:
- os: windows-2025
build_type: debug
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -48,24 +29,14 @@ jobs:
build-cache: true
build-cache-key: ${{ matrix.os }}-${{ matrix.build_type }}
- name: Setup Pixi
uses: prefix-dev/setup-pixi@v0.9.3
with:
pixi-version: v0.61.0
environments: develop
activate-environment: true
cache: true
locked: true
- uses: ./.github/actions/setup-pixi
- name: Build
run: |
pixi run ci-xmake-build ${{ matrix.build_type }}
run: pixi run xmake ${{ matrix.build_type }}
- name: Test
run: |
pixi run xmake-test
run: pixi run xmake-test
# Clean up llvm package to reduce cache size
- name: Remove llvm package (Linux)
if: runner.os == 'Linux'
run: xmake require --uninstall clice-llvm

1
.gitignore vendored
View File

@@ -60,6 +60,7 @@ tests/unit/Local/
.vs/
.idea/
.claude
.clangd
# pixi environments
.env

View File

@@ -1,31 +0,0 @@
# .pre-commit-config.yaml
exclude: "\\.patch$"
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.12
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v21.1.0
hooks:
- id: clang-format
types_or: [c++, c]
- repo: https://github.com/JohnnyMorganz/StyLua
rev: v2.1.0
hooks:
- id: stylua-github # or stylua-system / stylua-github
files: '(^xmake\.lua$|^editors/nvim/(lua|plugin|test)/.*\.lua$)'

23
.prettierrc.yaml Normal file
View File

@@ -0,0 +1,23 @@
# .prettierrc.yaml
printWidth: 100
tabWidth: 4
useTabs: false
semi: true
singleQuote: false
jsxSingleQuote: false
quoteProps: "as-needed"
trailingComma: "all"
bracketSpacing: true
arrowParens: "always"
endOfLine: "lf"
overrides:
- files: "*.md"
options:
proseWrap: "preserve"
tabWidth: 2
- files: ["*.json", "*.yaml", "*.yml", ".clang-format", ".clang-tidy"]
options:
tabWidth: 2

View File

@@ -150,6 +150,28 @@ add_custom_target(
DEPENDS ${CONFIG_GENERATED_FILE}
)
add_library(clice_core_deps INTERFACE)
target_include_directories(clice_core_deps INTERFACE
"${PROJECT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}/generated"
)
target_link_libraries(clice_core_deps INTERFACE
clice_options
libuv::libuv
spdlog::spdlog
tomlplusplus::tomlplusplus
roaring::roaring
flatbuffers
llvm-libs
)
add_library(clice_builtin_api INTERFACE)
target_link_libraries(clice_builtin_api INTERFACE clice_core_deps)
include("${PROJECT_SOURCE_DIR}/cmake/builtin-libraries.cmake")
clice_include_builtin_library_modules()
file(GLOB_RECURSE CLICE_SOURCES CONFIGURE_DEPENDS
"${PROJECT_SOURCE_DIR}/src/AST/*.cpp"
"${PROJECT_SOURCE_DIR}/src/Async/*.cpp"
@@ -162,22 +184,10 @@ file(GLOB_RECURSE CLICE_SOURCES CONFIGURE_DEPENDS
)
add_library(clice-core STATIC "${CLICE_SOURCES}")
add_dependencies(clice-core generate_flatbuffers_schema generate_config)
target_link_libraries(clice-core PUBLIC clice_core_deps)
clice_finalize_builtin_libraries(TARGET clice-core)
target_include_directories(clice-core PUBLIC
"${PROJECT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}/generated"
)
target_link_libraries(clice-core PUBLIC
clice_options
libuv::libuv
spdlog::spdlog
tomlplusplus::tomlplusplus
roaring::roaring
flatbuffers
llvm-libs
)
add_executable(clice "${PROJECT_SOURCE_DIR}/bin/clice.cc")
add_executable(clice "${PROJECT_SOURCE_DIR}/src/clice.cc")
target_link_libraries(clice PRIVATE clice-core)
install(TARGETS clice RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
@@ -196,7 +206,7 @@ if(CLICE_ENABLE_TEST)
"${PROJECT_SOURCE_DIR}/tests/unit/*/*.cpp")
add_executable(unit_tests
"${CLICE_TEST_SOURCES}"
"${PROJECT_SOURCE_DIR}/bin/unit_tests.cc"
"${PROJECT_SOURCE_DIR}/tests/unit/unit_tests.cc"
)
target_include_directories(unit_tests PUBLIC "${PROJECT_SOURCE_DIR}")
target_link_libraries(unit_tests PRIVATE clice-core cpptrace::cpptrace)

View File

@@ -22,11 +22,11 @@ Download the latest `clice` binary from the [releases page](https://github.com/c
```jsonc
{
// Optional: Set this to an empty string to turn off the clangd.
"clangd.path": "",
// Optional: Set this to an empty string to turn off the clangd.
"clangd.path": "",
// Point this to the clice binary you downloaded.
"clice.executable": "/path/to/your/clice/executable",
// Point this to the clice binary you downloaded.
"clice.executable": "/path/to/your/clice/executable",
}
```

View File

@@ -0,0 +1,158 @@
include_guard(GLOBAL)
set(
CLICE_BUILTIN_LIBRARY_MODULES
"${CLICE_BUILTIN_LIBRARY_MODULES}"
CACHE STRING
"Semicolon-separated list of CMake modules that register extra builtin clice libraries"
)
function(clice_add_builtin_library)
set(options)
set(oneValueArgs NAME ENTRYPOINT)
set(multiValueArgs SOURCES INCLUDE_DIRECTORIES LINK_LIBRARIES COMPILE_DEFINITIONS COMPILE_OPTIONS)
cmake_parse_arguments(CBL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(CBL_UNPARSED_ARGUMENTS)
message(
FATAL_ERROR
"clice_add_builtin_library got unexpected arguments: ${CBL_UNPARSED_ARGUMENTS}"
)
endif()
if(NOT CBL_NAME)
message(FATAL_ERROR "clice_add_builtin_library requires NAME")
endif()
if(NOT CBL_SOURCES)
message(FATAL_ERROR "clice_add_builtin_library(${CBL_NAME}) requires SOURCES")
endif()
if(NOT CBL_ENTRYPOINT)
message(FATAL_ERROR "clice_add_builtin_library(${CBL_NAME}) requires ENTRYPOINT")
endif()
if(NOT TARGET clice_builtin_api)
message(
FATAL_ERROR
"clice_builtin_api must be defined before calling clice_add_builtin_library(${CBL_NAME})"
)
endif()
set(target "clice_builtin_${CBL_NAME}")
if(TARGET "${target}")
message(FATAL_ERROR "builtin library target '${target}' already exists")
endif()
add_library("${target}" OBJECT)
target_sources("${target}" PRIVATE ${CBL_SOURCES})
target_link_libraries("${target}" PUBLIC clice_builtin_api)
add_dependencies("${target}" generate_flatbuffers_schema generate_config)
if(CBL_INCLUDE_DIRECTORIES)
target_include_directories("${target}" PRIVATE ${CBL_INCLUDE_DIRECTORIES})
endif()
if(CBL_LINK_LIBRARIES)
target_link_libraries("${target}" PUBLIC ${CBL_LINK_LIBRARIES})
endif()
if(CBL_COMPILE_DEFINITIONS)
target_compile_definitions("${target}" PRIVATE ${CBL_COMPILE_DEFINITIONS})
endif()
if(CBL_COMPILE_OPTIONS)
target_compile_options("${target}" PRIVATE ${CBL_COMPILE_OPTIONS})
endif()
set_property(GLOBAL APPEND PROPERTY CLICE_BUILTIN_LIBRARY_TARGETS "${target}")
set_property(GLOBAL APPEND PROPERTY CLICE_BUILTIN_LIBRARY_ENTRYPOINTS "${CBL_ENTRYPOINT}")
endfunction()
function(clice_include_builtin_library_modules)
foreach(module_path IN LISTS CLICE_BUILTIN_LIBRARY_MODULES)
cmake_path(
ABSOLUTE_PATH module_path
BASE_DIRECTORY "${PROJECT_SOURCE_DIR}"
NORMALIZE
OUTPUT_VARIABLE module_abs_path
)
if(NOT EXISTS "${module_abs_path}")
message(
FATAL_ERROR
"builtin library module '${module_path}' does not exist: ${module_abs_path}"
)
endif()
include("${module_abs_path}")
endforeach()
endfunction()
function(clice_finalize_builtin_libraries)
set(options)
set(oneValueArgs TARGET REGISTRATION_SOURCE)
cmake_parse_arguments(CBL "${options}" "${oneValueArgs}" "" ${ARGN})
if(CBL_UNPARSED_ARGUMENTS)
message(
FATAL_ERROR
"clice_finalize_builtin_libraries got unexpected arguments: ${CBL_UNPARSED_ARGUMENTS}"
)
endif()
if(NOT CBL_TARGET)
message(FATAL_ERROR "clice_finalize_builtin_libraries requires TARGET")
endif()
if(NOT TARGET "${CBL_TARGET}")
message(FATAL_ERROR "target '${CBL_TARGET}' does not exist")
endif()
if(NOT CBL_REGISTRATION_SOURCE)
set(CBL_REGISTRATION_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/generated/builtin-libraries.cpp")
endif()
get_property(builtin_targets GLOBAL PROPERTY CLICE_BUILTIN_LIBRARY_TARGETS)
get_property(builtin_entrypoints GLOBAL PROPERTY CLICE_BUILTIN_LIBRARY_ENTRYPOINTS)
if(NOT builtin_targets)
set(builtin_targets "")
endif()
if(NOT builtin_entrypoints)
set(builtin_entrypoints "")
endif()
set(registration_source_content "#include \"Server/Plugin.h\"\n\nnamespace clice {\n\n")
foreach(entrypoint IN LISTS builtin_entrypoints)
string(APPEND registration_source_content "::clice::PluginInfo ${entrypoint}();\n")
endforeach()
string(APPEND registration_source_content "\nvoid register_builtin_server_plugins(ServerPluginBuilder& builder) {\n")
foreach(entrypoint IN LISTS builtin_entrypoints)
string(
APPEND
registration_source_content
" ${entrypoint}().register_server_callbacks(builder);\n"
)
endforeach()
string(APPEND registration_source_content "}\n\n} // namespace clice\n")
get_filename_component(registration_source_dir "${CBL_REGISTRATION_SOURCE}" DIRECTORY)
file(MAKE_DIRECTORY "${registration_source_dir}")
file(CONFIGURE OUTPUT "${CBL_REGISTRATION_SOURCE}" CONTENT "${registration_source_content}" @ONLY)
target_sources("${CBL_TARGET}" PRIVATE "${CBL_REGISTRATION_SOURCE}")
if(builtin_targets)
foreach(builtin_target IN LISTS builtin_targets)
target_sources("${CBL_TARGET}" PRIVATE $<TARGET_OBJECTS:${builtin_target}>)
endforeach()
target_link_libraries("${CBL_TARGET}" PRIVATE ${builtin_targets})
endif()
endfunction()

View File

@@ -66,16 +66,20 @@ set(FLATBUFFERS_BUILD_GRPC OFF CACHE BOOL "" FORCE)
set(FLATBUFFERS_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(FLATBUFFERS_BUILD_FLATHASH OFF CACHE BOOL "" FORCE)
# cpptrace
FetchContent_Declare(
cpptrace
GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
GIT_TAG v1.0.4
GIT_SHALLOW TRUE
)
set(CPPTRACE_DISABLE_CXX_20_MODULES ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(libuv spdlog tomlplusplus croaring flatbuffers)
FetchContent_MakeAvailable(libuv spdlog tomlplusplus croaring flatbuffers cpptrace)
if(CLICE_ENABLE_TEST)
# cpptrace
FetchContent_Declare(
cpptrace
GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
GIT_TAG v1.0.4
GIT_SHALLOW TRUE
)
set(CPPTRACE_DISABLE_CXX_20_MODULES ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(cpptrace)
endif()
if(WIN32)
target_compile_definitions(uv_a PRIVATE _CRT_SECURE_NO_WARNINGS)

View File

@@ -1,83 +1,83 @@
[
{
"version": "21.1.4+r1",
"filename": "arm64-macos-clang-debug-asan.tar.xz",
"sha256": "7da4b7d63edefecaf11773e7e701c575140d1a07329bbbb038673b6ee4516ff5",
"lto": false,
"asan": true,
"platform": "macosx",
"build_type": "Debug"
},
{
"version": "21.1.4+r1",
"filename": "arm64-macos-clang-releasedbg-lto.tar.xz",
"sha256": "300455b169448f9f01ae95e3bc269f489558a4ca3955e3032171cc75feca0e30",
"lto": true,
"asan": false,
"platform": "macosx",
"build_type": "RelWithDebInfo"
},
{
"version": "21.1.4+r1",
"filename": "arm64-macos-clang-releasedbg.tar.xz",
"sha256": "9abfc6cd65b957d734ffb97610a634fb4a66d3fbe0fcfb5a1c9124ef693c1495",
"lto": false,
"asan": false,
"platform": "macosx",
"build_type": "RelWithDebInfo"
},
{
"version": "21.1.4+r1",
"filename": "x64-linux-gnu-debug-asan.tar.xz",
"sha256": "c1ad3ec476911596a842ac67dd9c9c9475ce9f0a77b81101d3c801840292e7bc",
"lto": false,
"asan": true,
"platform": "linux",
"build_type": "Debug"
},
{
"version": "21.1.4+r1",
"filename": "x64-linux-gnu-releasedbg-lto.tar.xz",
"sha256": "8a869c2184d139dbba704e2d712e7a68336458ad2d70622b3eb906c3e3511e54",
"lto": true,
"asan": false,
"platform": "linux",
"build_type": "RelWithDebInfo"
},
{
"version": "21.1.4+r1",
"filename": "x64-linux-gnu-releasedbg.tar.xz",
"sha256": "552bab86f715d4f2c027f07eaaf5b3d6b8e430af0b74b470142f3f00da4feec6",
"lto": false,
"asan": false,
"platform": "linux",
"build_type": "RelWithDebInfo"
},
{
"version": "21.1.4+r1",
"filename": "x64-windows-msvc-debug-asan.tar.xz",
"sha256": "093667a493d336c22ff3c604c5f1fea2a7d2c927c1179cec44e9a03726906ac1",
"lto": false,
"asan": true,
"platform": "windows",
"build_type": "Debug"
},
{
"version": "21.1.4+r1",
"filename": "x64-windows-msvc-releasedbg-lto.tar.xz",
"sha256": "010539e85621dc3c6ecf359d899feb4075aeca5d0bba6625cdbec0e570e79129",
"lto": true,
"asan": false,
"platform": "windows",
"build_type": "RelWithDebInfo"
},
{
"version": "21.1.4+r1",
"filename": "x64-windows-msvc-releasedbg.tar.xz",
"sha256": "f473c09fbea10053fac00be409d75dc228d4a38bcbc5e4aeb58b56a4b0dde78e",
"lto": false,
"asan": false,
"platform": "windows",
"build_type": "RelWithDebInfo"
}
{
"version": "21.1.4+r1",
"filename": "arm64-macos-clang-debug-asan.tar.xz",
"sha256": "7da4b7d63edefecaf11773e7e701c575140d1a07329bbbb038673b6ee4516ff5",
"lto": false,
"asan": true,
"platform": "macosx",
"build_type": "Debug"
},
{
"version": "21.1.4+r1",
"filename": "arm64-macos-clang-releasedbg-lto.tar.xz",
"sha256": "300455b169448f9f01ae95e3bc269f489558a4ca3955e3032171cc75feca0e30",
"lto": true,
"asan": false,
"platform": "macosx",
"build_type": "RelWithDebInfo"
},
{
"version": "21.1.4+r1",
"filename": "arm64-macos-clang-releasedbg.tar.xz",
"sha256": "9abfc6cd65b957d734ffb97610a634fb4a66d3fbe0fcfb5a1c9124ef693c1495",
"lto": false,
"asan": false,
"platform": "macosx",
"build_type": "RelWithDebInfo"
},
{
"version": "21.1.4+r1",
"filename": "x64-linux-gnu-debug-asan.tar.xz",
"sha256": "c1ad3ec476911596a842ac67dd9c9c9475ce9f0a77b81101d3c801840292e7bc",
"lto": false,
"asan": true,
"platform": "linux",
"build_type": "Debug"
},
{
"version": "21.1.4+r1",
"filename": "x64-linux-gnu-releasedbg-lto.tar.xz",
"sha256": "8a869c2184d139dbba704e2d712e7a68336458ad2d70622b3eb906c3e3511e54",
"lto": true,
"asan": false,
"platform": "linux",
"build_type": "RelWithDebInfo"
},
{
"version": "21.1.4+r1",
"filename": "x64-linux-gnu-releasedbg.tar.xz",
"sha256": "552bab86f715d4f2c027f07eaaf5b3d6b8e430af0b74b470142f3f00da4feec6",
"lto": false,
"asan": false,
"platform": "linux",
"build_type": "RelWithDebInfo"
},
{
"version": "21.1.4+r1",
"filename": "x64-windows-msvc-debug-asan.tar.xz",
"sha256": "093667a493d336c22ff3c604c5f1fea2a7d2c927c1179cec44e9a03726906ac1",
"lto": false,
"asan": true,
"platform": "windows",
"build_type": "Debug"
},
{
"version": "21.1.4+r1",
"filename": "x64-windows-msvc-releasedbg-lto.tar.xz",
"sha256": "010539e85621dc3c6ecf359d899feb4075aeca5d0bba6625cdbec0e570e79129",
"lto": true,
"asan": false,
"platform": "windows",
"build_type": "RelWithDebInfo"
},
{
"version": "21.1.4+r1",
"filename": "x64-windows-msvc-releasedbg.tar.xz",
"sha256": "f473c09fbea10053fac00be409d75dc228d4a38bcbc5e4aeb58b56a4b0dde78e",
"lto": false,
"asan": false,
"platform": "windows",
"build_type": "RelWithDebInfo"
}
]

View File

@@ -1,40 +1,38 @@
import { defineConfig } from 'vitepress'
import { genSidebar } from './theme/sidebar'
import { defineConfig } from "vitepress";
import { genSidebar } from "./theme/sidebar";
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "clice",
description: "a powerful and modern C++ language server",
cleanUrls: true,
base: '/',
rewrites: {
'en/:rest*': ':rest*',
},
locales: {
root: { label: 'English' },
zh: { label: '简体中文' },
},
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: 'Home', link: '/' },
],
sidebar: {
"/zh/": [
genSidebar('zh', 'design', { title: 'Design' }),
genSidebar('zh', 'dev', { title: 'Development' }),
genSidebar('zh', 'guide', { title: 'Guide' }),
],
"/": [
genSidebar('en', 'design', { title: 'Design' }),
genSidebar('en', 'dev', { title: 'Development' }),
genSidebar('en', 'guide', { title: 'Guide' }),
],
title: "clice",
description: "a powerful and modern C++ language server",
cleanUrls: true,
base: "/",
rewrites: {
"en/:rest*": ":rest*",
},
socialLinks: [
{ icon: 'discord', link: 'https://discord.gg/PA3UxW2VA3' },
{ icon: 'github', link: 'https://github.com/clice-io/clice' },
],
outline: 'deep',
},
})
locales: {
root: { label: "English" },
zh: { label: "简体中文" },
},
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [{ text: "Home", link: "/" }],
sidebar: {
"/zh/": [
genSidebar("zh", "design", { title: "Design" }),
genSidebar("zh", "dev", { title: "Development" }),
genSidebar("zh", "guide", { title: "Guide" }),
],
"/": [
genSidebar("en", "design", { title: "Design" }),
genSidebar("en", "dev", { title: "Development" }),
genSidebar("en", "guide", { title: "Guide" }),
],
},
socialLinks: [
{ icon: "discord", link: "https://discord.gg/PA3UxW2VA3" },
{ icon: "github", link: "https://github.com/clice-io/clice" },
],
outline: "deep",
},
});

View File

@@ -1,17 +1,17 @@
// https://vitepress.dev/guide/custom-theme
import { h } from 'vue'
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import './style.css'
import { h } from "vue";
import type { Theme } from "vitepress";
import DefaultTheme from "vitepress/theme";
import "./style.css";
export default {
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
})
},
enhanceApp({ app, router, siteData }) {
// ...
}
} satisfies Theme
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
});
},
enhanceApp({ app, router, siteData }) {
// ...
},
} satisfies Theme;

View File

@@ -1,45 +1,45 @@
import fs from 'fs'
import path from 'path'
import { DefaultTheme } from 'vitepress'
import fs from "fs";
import path from "path";
import { DefaultTheme } from "vitepress";
export const genSidebar = (
lang: string,
dirPath: string,
options: {
title: string
collapsible?: boolean
ignore?: string[]
}
title: string;
collapsible?: boolean;
ignore?: string[];
},
): DefaultTheme.SidebarItem => {
const sidebarPath = path.resolve(process.cwd(), lang, dirPath)
const ignore = options.ignore || ['index.md']
const sidebarPath = path.resolve(process.cwd(), lang, dirPath);
const ignore = options.ignore || ["index.md"];
const files = fs
.readdirSync(sidebarPath)
.filter((file) => file.endsWith('.md') && !ignore.includes(file))
.filter((file) => file.endsWith(".md") && !ignore.includes(file));
const items = files.map((file) => {
const content = fs.readFileSync(path.resolve(sidebarPath, file), 'utf-8')
const match = content.match(/^#\s+(.*)/)
const title = match ? match[1] : file.replace('.md', '')
const content = fs.readFileSync(path.resolve(sidebarPath, file), "utf-8");
const match = content.match(/^#\s+(.*)/);
const title = match ? match[1] : file.replace(".md", "");
let prefix = '/';
if (lang != 'en') {
let prefix = "/";
if (lang != "en") {
prefix += lang;
prefix += '/';
prefix += "/";
}
return {
text: title,
/// Make sure link for en is actually root, beacuse Github Pages
/// doesn't support redirect url.
link: `${prefix}${dirPath}/${file.replace('.md', '')}`
}
})
link: `${prefix}${dirPath}/${file.replace(".md", "")}`,
};
});
return {
text: options.title,
collapsed: options.collapsible || false,
items
}
}
items,
};
};

View File

@@ -44,30 +44,30 @@
* -------------------------------------------------------------------------- */
:root {
--vp-c-default-1: var(--vp-c-gray-1);
--vp-c-default-2: var(--vp-c-gray-2);
--vp-c-default-3: var(--vp-c-gray-3);
--vp-c-default-soft: var(--vp-c-gray-soft);
--vp-c-default-1: var(--vp-c-gray-1);
--vp-c-default-2: var(--vp-c-gray-2);
--vp-c-default-3: var(--vp-c-gray-3);
--vp-c-default-soft: var(--vp-c-gray-soft);
--vp-c-brand-1: var(--vp-c-indigo-1);
--vp-c-brand-2: var(--vp-c-indigo-2);
--vp-c-brand-3: var(--vp-c-indigo-3);
--vp-c-brand-soft: var(--vp-c-indigo-soft);
--vp-c-brand-1: var(--vp-c-indigo-1);
--vp-c-brand-2: var(--vp-c-indigo-2);
--vp-c-brand-3: var(--vp-c-indigo-3);
--vp-c-brand-soft: var(--vp-c-indigo-soft);
--vp-c-tip-1: var(--vp-c-brand-1);
--vp-c-tip-2: var(--vp-c-brand-2);
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);
--vp-c-tip-1: var(--vp-c-brand-1);
--vp-c-tip-2: var(--vp-c-brand-2);
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);
--vp-c-warning-1: var(--vp-c-yellow-1);
--vp-c-warning-2: var(--vp-c-yellow-2);
--vp-c-warning-3: var(--vp-c-yellow-3);
--vp-c-warning-soft: var(--vp-c-yellow-soft);
--vp-c-warning-1: var(--vp-c-yellow-1);
--vp-c-warning-2: var(--vp-c-yellow-2);
--vp-c-warning-3: var(--vp-c-yellow-3);
--vp-c-warning-soft: var(--vp-c-yellow-soft);
--vp-c-danger-1: var(--vp-c-red-1);
--vp-c-danger-2: var(--vp-c-red-2);
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);
--vp-c-danger-1: var(--vp-c-red-1);
--vp-c-danger-2: var(--vp-c-red-2);
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);
}
/**
@@ -75,15 +75,15 @@
* -------------------------------------------------------------------------- */
:root {
--vp-button-brand-border: transparent;
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand-3);
--vp-button-brand-hover-border: transparent;
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
--vp-button-brand-active-border: transparent;
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-c-brand-1);
--vp-button-brand-border: transparent;
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand-3);
--vp-button-brand-hover-border: transparent;
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
--vp-button-brand-active-border: transparent;
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-c-brand-1);
}
/**
@@ -91,31 +91,23 @@
* -------------------------------------------------------------------------- */
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
#bd34fe 30%,
#41d1ff
);
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(120deg, #bd34fe 30%, #41d1ff);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
#bd34fe 50%,
#47caff 50%
);
--vp-home-hero-image-filter: blur(44px);
--vp-home-hero-image-background-image: linear-gradient(-45deg, #bd34fe 50%, #47caff 50%);
--vp-home-hero-image-filter: blur(44px);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
/**
@@ -123,10 +115,10 @@
* -------------------------------------------------------------------------- */
:root {
--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
}
/**
@@ -134,5 +126,5 @@
* -------------------------------------------------------------------------- */
.DocSearch {
--docsearch-primary-color: var(--vp-c-brand-1) !important;
--docsearch-primary-color: var(--vp-c-brand-1) !important;
}

View File

@@ -1,4 +1,4 @@
### clice configuration
## # clice configuration
# This section outlines the supported built-in variables for clice.
# These variables can be referenced in strings using the syntax `${var}`.
@@ -9,47 +9,39 @@
# - `${workspace}`: The workspace directory provided by the client.
[project]
# Enable experimental clang-tidy diagnostics.
# This feature is tracked in https://github.com/clice-project/clice/issues/90.
clang_tidy = false
# Maximum number of active files to keep in memory. If the number of active files
# exceeds this limit, the least recently used files will be removed.
# The default value is 8. Whatever the number you set, the minimum is 1, the maximum is 512.
max_active_file = 8
# Directory for storing PCH and PCM files.
cache_dir = "${workspace}/.clice/cache"
# Directory for storing index files.
index_dir = "${workspace}/.clice/index"
logging_dir = "${workspace}/.clice/logging"
# Compile commands directories to search for compile_commands.json files.
compile_commands_dirs = ["${workspace}/build"]
# Enable experimental clang-tidy diagnostics.
# This feature is tracked in https://github.com/clice-project/clice/issues/90.
clang_tidy = false
# Maximum number of active files to keep in memory. If the number of active files
# exceeds this limit, the least recently used files will be removed.
# The default value is 8. Whatever the number you set, the minimum is 1, the maximum is 512.
max_active_file = 8
# Directory for storing PCH and PCM files.
cache_dir = "${workspace}/.clice/cache"
# Directory for storing index files.
index_dir = "${workspace}/.clice/index"
logging_dir = "${workspace}/.clice/logging"
# Compile commands files or directories to search for compile_commands.json files.
compile_commands_paths = ["${workspace}/build"]
# Control the behavior for specific files. Note that Clice matches rules
# in order. If you want to add your own rules, either delete this rule
# or insert your rule before it.
[[rules]]
# Files matching the specified pattern will have this rule applied.
#
# Patterns can use the following syntax:
# - `*`: Matches one or more characters in a path segment.
# - `?`: Matches a single character in a path segment.
# - `**`: Matches any number of path segments, including none.
# - `{}`: Groups conditions (e.g., `**/*.{ts,js}` matches all TypeScript
# and JavaScript files).
# - `[]`: Declares a range of characters to match in a path segment
# (e.g., `example.[0-9]` matches `example.0`, `example.1`, etc.).
# - `[!...]`: Negates a range of characters to match in a path segment
# (e.g., `example.[!0-9]` matches `example.a`, `example.b`, but not `example.0`).
patterns = ["**/*"]
# Commands to append to the original command list (e.g., ["-std=c++17"]).
append = []
# Commands to remove from the original command list.
remove = []
# Files matching the specified pattern will have this rule applied.
#
# Patterns can use the following syntax:
# - `*`: Matches one or more characters in a path segment.
# - `?`: Matches a single character in a path segment.
# - `**`: Matches any number of path segments, including none.
# - `{}`: Groups conditions (e.g., `**/*.{ts,js}` matches all TypeScript
# and JavaScript files).
# - `[]`: Declares a range of characters to match in a path segment
# (e.g., `example.[0-9]` matches `example.0`, `example.1`, etc.).
# - `[!...]`: Negates a range of characters to match in a path segment
# (e.g., `example.[!0-9]` matches `example.a`, `example.b`, but not `example.0`).
patterns = ["**/*"]
# Commands to append to the original command list (e.g., ["-std=c++17"]).
append = []
# Commands to remove from the original command list.
remove = []

View File

@@ -1,45 +1,79 @@
# Build from Source
## Supported Platforms
clice depends on C++23 features and requires a modern C++ toolchain. We also need to link against LLVM/Clang to parse ASTs. To speed up builds, the default configuration downloads our published [clice-llvm](https://github.com/clice-io/clice-llvm) prebuilt package. This assumes your local environment matches the prebuilt environment closely (especially when enabling Address Sanitizer or LTO).
- Windows
- Linux
- MacOS
To simplify setup and keep builds reproducible, we **strongly recommend** [pixi](https://pixi.prefix.dev/latest) to manage the development environment. Dependency versions are pinned in `pixi.toml`.
## Prerequisite
If you prefer not to use pixi, see [Manual Build](#manual-build) below.
- cmake/xmake
- clang, lld >= 20
- c++23 **compatible** standard library
- MSVC STL >= 19.44(VS 2022 17.4)
- GCC libstdc++ >= 14
- Clang libc++ >= 20
## Quick Start
clice uses C++23 as its language standard. Please ensure you have a clang 20 (or higher) compiler and a C++23 compatible standard library available. clice depends on lld as its linker. Please ensure your clang toolchain can find it (clang distributions usually bundle lld, or you may need to install the lld-20 package separately).
Install pixi following the [official guide](https://pixi.prefix.dev/latest/installation).
> clice is currently only guaranteed to compile with clang (as ensured by CI testing). We do our best to maintain compatibility with gcc and msvc, but we do not add corresponding tests in CI. Contributions are welcome if you encounter any issues.
## CMake
Use the following commands to build clice
We ship several tasks; the commands below configure, build, and run tests:
```shell
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
cmake --build build
# configure && build (default RelWithDebInfo)
pixi run build
# unit && integration
pixi run test
```
For finer-grained tasks (first argument sets the build type):
```shell
pixi run cmake-config Debug
pixi run cmake-build Debug
pixi run unit-test Debug
pixi run integration-test Debug
```
> [!TIP]
> If you want to develop directly with `cmake`, `ninja`, `clang++`, etc., run `pixi shell` to enter a shell with all env vars configured.
### XMake
We also support building with XMake:
```shell
# config & build (default releasedbg)
pixi run xmake
# unit & integration
pixi run xmake-test
```
## Manual Build
If you plan to build manually, first ensure your toolchain matches the versions defined in `pixi.toml`.
> Compatibility: In theory clice does not rely on compiler-specific extensions, so mainstream compilers (GCC/Clang/MSVC) should work. However, CI only guarantees specific versions of Clang. Other compilers or versions are supported on a **best-effort** basis. Please open an issue or PR if you hit problems.
### CMake
```shell
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain.cmake \
-DCLICE_ENABLE_TEST=ON
```
> Note: `CMAKE_TOOLCHAIN_FILE` is optional. If your toolchain exactly matches ours, you can use the predefined `cmake/toolchain.cmake`; otherwise remove that flag.
Optional build options:
| Option | Default | Description |
| :------------------- | :------ | :----------------------------------------------------------------------------------------------------------------------------- |
| LLVM_INSTALL_PATH | "" | Build clice using llvm libs from a custom path |
| CLICE_ENABLE_TEST | OFF | Whether to build clice's unit tests |
| CLICE_USE_LIBCXX | OFF | Whether to build clice with libc++ (adds `-std=libc++`). If enabled, ensure that the llvm libs were also compiled with libc++. |
| CLICE_CI_ENVIRONMENT | OFF | Whether to enable the `CLICE_CI_ENVIRONMENT` macro. Some tests only run in a CI environment. |
| Option | Default | Effect |
| -------------------- | ------- | --------------------------------------------------------------------------------------------------------- |
| LLVM_INSTALL_PATH | "" | Build clice with LLVM from a custom path |
| CLICE_ENABLE_TEST | OFF | Build clice unit tests |
| CLICE_USE_LIBCXX | OFF | Build clice with libc++ (adds `-std=libc++`); if enabled, ensure the LLVM libs are also built with libc++ |
| CLICE_CI_ENVIRONMENT | OFF | Enable the `CLICE_CI_ENVIRONMENT` macro; some tests only run in CI |
| CLICE_BUILTIN_LIBRARY_MODULES | "" | Semicolon-separated list of CMake modules that register builtin libraries compiled into `clice`; see [Builtin Libraries](./builtin-library.md) |
## XMake
### XMake
Use the following commands to build clice
Build clice with:
```bash
xmake f -c --mode=releasedbg --toolchain=clang
@@ -48,35 +82,22 @@ xmake build --all
Optional build options:
| Option | Default | Description |
| :------------ | :------ | :--------------------------------------------- |
| --llvm | "" | Build clice using llvm libs from a custom path |
| --enable_test | false | Whether to build clice's unit tests |
| --ci | false | Whether to enable `CLICE_CI_ENVIRONMENT` |
| Option | Default | Effect |
| ------------- | ------- | ---------------------------------------- |
| --llvm | "" | Build clice with LLVM from a custom path |
| --enable_test | false | Build clice unit tests |
| --ci | false | Enable `CLICE_CI_ENVIRONMENT` |
## A Note on LLVM Libs
## About LLVM
Due to the complexity of C++ syntax, writing a new parser from scratch is unrealistic. clice calls clang's APIs to parse C++ source files and obtain the AST, which means it needs to link against llvm/clang libs. Because clice uses clang's private headers, which are not included in the binary releases published by LLVM, you cannot use the system's llvm package directly.
clice calls Clang APIs to parse C++ code, so it must link against LLVM/Clang. Because clice uses Clang's private headers (usually absent from distro packages), the system LLVM package cannot be used directly.
1. We publish pre-compiled binaries for the LLVM version we use on [clice-llvm](https://github.com/clice-io/clice-llvm/releases), which are used for CI or release builds. By default, cmake and xmake will download and use the llvm libs from here during the build.
Two ways to satisfy this dependency:
1. We publish prebuilt binaries of the LLVM version we use at [clice-llvm](https://github.com/clice-io/clice-llvm/releases) for CI and release builds. During builds, cmake and xmake download these LLVM libs by default.
> [!IMPORTANT]
>
> For debug builds of llvm libs, we enable address sanitizer. Address sanitizer depends on compiler-rt, which is highly sensitive to the compiler version.
>
> Therefore, if you use a debug build, please ensure your clang's compiler-rt version is **strictly identical** to the one used in our build.
>
> - Windows does not currently have debug builds for llvm libs, as it does not support building clang as a dynamic library. Related progress is tracked [here](https://github.com/clice-io/clice/issues/42).
> - Linux uses clang20
> - MacOS uses homebrew llvm@20. **Do not use apple clang**.
>
> You can refer to the [cmake](https://github.com/clice-io/clice/blob/main/.github/workflows/cmake.yml) and [xmake](https://github.com/clice-io/clice/blob/main/.github/workflows/xmake.yml) files in our CI as a reference, as they maintain an environment strictly consistent with the pre-compiled llvm libs.
> For debug LLVM builds, we enable address sanitizer, which depends on compiler-rt and is very sensitive to compiler version. If you use a debug build, ensure your clang compiler-rt version matches the one defined in `pixi.toml`.
2. Build llvm/clang yourself to match your current environment. If the default pre-compiled binaries (Method 1) fail to run on your system due to ABI or library version (e.g., glibc) incompatibility, or if you need a custom Debug build, we recommend you use this method to compile llvm libs from scratch. We provide a script to build the llvm libs required by clice: [build-llvm-libs.py](https://github.com/clice-io/clice/blob/main/scripts/build-llvm-libs.py).
```bash
cd llvm-project
python3 <clice>/scripts/build-llvm-libs.py debug
```
You can also refer to LLVM's official build tutorial: [Building LLVM with CMake](https://llvm.org/docs/CMake.html).
2. Build LLVM/Clang yourself to match your environment. If the default prebuilt binaries fail due to ABI or library version mismatches, or you need a custom debug build, use this approach. We provide `scripts/build-llvm.py` to build the required LLVM libs, or refer to LLVM's official guide [Building LLVM with CMake](https://llvm.org/docs/CMake.html).

View File

@@ -0,0 +1,101 @@
# Builtin Libraries
Builtin libraries are compiled directly into the `clice` binary instead of being loaded later with `--plugin-path`.
This is useful when:
- your plugin sources live outside the `clice` source tree
- you want the builtin to be part of the default executable
- you need extra include directories, compile definitions, or link dependencies during the main build
## CMake Entry Point
`clice` now exposes a small helper module at [cmake/builtin-libraries.cmake](/cmake/builtin-libraries.cmake).
Extra builtin libraries are registered through the cache variable `CLICE_BUILTIN_LIBRARY_MODULES`.
Each value in `CLICE_BUILTIN_LIBRARY_MODULES` must be a CMake file. During configure, `clice` includes those files, and each file calls `clice_add_builtin_library(...)`.
## Minimal Module
Create a CMake file in your external project, for example `/path/to/my-plugin/clice-builtin.cmake`:
```cmake
clice_add_builtin_library(
NAME my_plugin
SOURCES
"${CMAKE_CURRENT_LIST_DIR}/src/MyPlugin.cpp"
INCLUDE_DIRECTORIES
"${CMAKE_CURRENT_LIST_DIR}/include"
ENTRYPOINT
clice_get_my_plugin_server_plugin_info
)
```
Then configure `clice` with:
```shell
cmake -B build -G Ninja \
-DCLICE_BUILTIN_LIBRARY_MODULES="/path/to/my-plugin/clice-builtin.cmake"
```
To load multiple modules, pass a semicolon-separated CMake list:
```shell
cmake -B build -G Ninja \
-DCLICE_BUILTIN_LIBRARY_MODULES="/path/to/a.cmake;/path/to/b.cmake"
```
## `clice_add_builtin_library`
The helper accepts the following arguments:
| Argument | Required | Description |
| --- | --- | --- |
| `NAME` | Yes | Logical name used to create an internal object target |
| `SOURCES` | Yes | Source files compiled into `clice`; absolute paths and out-of-tree paths are supported |
| `ENTRYPOINT` | Yes | Unique function name in namespace `clice` that returns `::clice::PluginInfo` |
| `INCLUDE_DIRECTORIES` | No | Extra include directories for this builtin only |
| `LINK_LIBRARIES` | No | Extra libraries or targets needed by this builtin |
| `COMPILE_DEFINITIONS` | No | Extra compile definitions for this builtin |
| `COMPILE_OPTIONS` | No | Extra compile options for this builtin |
## Entrypoint Requirements
Builtin libraries share a single final executable, so each builtin must use its own unique entrypoint function inside namespace `clice`.
Dynamic plugins use:
```cpp
clice_get_server_plugin_info()
```
Builtin libraries should use a unique name such as:
```cpp
clice_get_my_plugin_server_plugin_info()
```
For example:
```cpp
#include "Server/Plugin.h"
namespace clice {
::clice::PluginInfo clice_get_my_plugin_server_plugin_info() {
return {
CLICE_PLUGIN_API_VERSION,
"MyPlugin",
"v0.0.1",
CLICE_PLUGIN_DEF_HASH,
[](clice::ServerPluginBuilder& builder) {
// register callbacks here
},
};
}
} // namespace clice
```
`clice` generates the static registration glue automatically, so once the module is included, no additional edits to `src/clice.cc` are required.

View File

@@ -7,5 +7,6 @@ Please refer to [build](./build.md) to build clice, refer to [test and debug](./
## Code Style
Naming:
- Variable names: lowercase with underscores
- Class names, enum names: PascalCase

70
docs/en/dev/extension.md Normal file
View File

@@ -0,0 +1,70 @@
# Extension
This section covers development and release workflows for the editor extensions (VSCode / Neovim / Zed).
## VSCode
The VSCode extension uses the Node/PNPM/VSCE toolchain. Work inside the pixi `node` environment for consistent versions.
```shell
# prepare environment (install pixi first)
pixi shell -e node
# install deps (uses pnpm-lock)
pixi run install-vscode
# package the extension; outputs editors/vscode/*.vsix
pixi run build-vscode
```
Publish to the VSCode Marketplace (`VSCE_PAT` env var required):
```shell
pixi run publish-vscode
```
> [!TIP]
> If clice is already built locally, set `clice.executable` in VSCode settings to point the extension to your custom binary.
Develop and debug:
1. `pixi shell -e node`
2. In `editors/vscode`, run `pnpm run watch` for incremental builds
3. In VSCode, use the “Run Extension/Launch Extension” configs, or run `code --extensionDevelopmentPath=$(pwd)/editors/vscode`
Common scripts (inside `pixi shell -e node`):
```bash
pnpm run package # same as pixi run build-vscode
pnpm run publish # same as pixi run publish-vscode
```
If you skip pixi, install node.js >= 20 and pnpm yourself, then in `editors/vscode` run:
```bash
pnpm install
pnpm run package
```
## Neovim
The Neovim extension lives in `editors/nvim` and is written in Lua. It is still evolving.
- Add the repo path to `runtimepath`, e.g. `set rtp+=/path/to/clice/editors/nvim`
- Or create a local symlink: `~/.config/nvim/pack/clice/start/clice` -> `<repo>/editors/nvim`
- Ensure the `clice` executable is discoverable in `$PATH`
Dev tips: the codebase is small—load it directly in Neovim and watch `:messages`/LSP logs; format with `stylua` (config included).
## Zed
The Zed extension lives in `editors/zed` and uses Rust plus `zed_extension_api`.
Suggested local verification:
```bash
cd editors/zed
cargo build --release
```
Then load the local extension per Zed's official guide (Zed CLI required). Make sure `clice` is on `PATH` before launching. Follow the Zed extension publishing flow when releasing.

View File

@@ -0,0 +1,78 @@
You can implement a clice server plugin to extend clice's functionality.
## Use case
When you use `clice` as LSP backend of LLM agents, e.g. claude code, you can add plugin to provide some extra features.
## Writing a plugin
When a plugin is loaded by the server, it will call `clice_get_server_plugin_info` to obtain information about this plugin and about how to register its customization points.
This function needs to be implemented by the plugin, see the example below:
```c++
extern "C" ::clice::PluginInfo LLVM_ATTRIBUTE_WEAK
clice_get_server_plugin_info() {
return {
CLICE_PLUGIN_API_VERSION, "MyPlugin", "v0.1", CLICE_PLUGIN_DEF_HASH,
[](ServerPluginBuilder builder) { ... }
};
}
```
See [PluginProtocol.h](/include/Server/PluginProtocol.h) for more details.
## Compiling a plugin
The plugin must be compiled with the same dependencies and compiler options as clice, otherwise it will cause undefined behavior. [config/llvm-manifest.json](/config/llvm-manifest.json) defines the build information used by clice.
## Loading plugins
For security reasons, clice does not allow loading plugins through configuration files, but must specify the plugin path through command line options.
When `clice` starts, it will load all plugins specified in the command line. You can specify the plugin path through the `--plugin-path` option.
```shell
$ clice --plugin-path /path/to/my-plugin.so
```
## Getting content of `CLICE_PLUGIN_DEF_HASH`
There are two values to return in the `clice_get_server_plugin_info` function.
- `CLICE_PLUGIN_API_VERSION` is used to ensure compability of the `clice_get_server_plugin_info` function between the plugin and the server.
- `CLICE_PLUGIN_DEF_HASH` is used to ensure the consistency of the C++ declarations between the plugin and the server.
To debug the content of `CLICE_PLUGIN_DEF_HASH`, you can run following command:
```shell
$ git clone https://github.com/clice-io/clice.git
$ cd clice
$ git checkout `clice --version --git-describe`
$ python scripts/plugin-def.py content
```
You will get a C source code file, content of which is like this:
```cpp
#if 0
// begin of config/llvm-manifest.json
[
{
"version": "21.1.4+r1",
"filename": "arm64-macos-clang-debug-asan.tar.xz",
"sha256": "7da4b7d63edefecaf11773e7e701c575140d1a07329bbbb038673b6ee4516ff5",
"lto": false,
"asan": true,
"platform": "macosx",
"build_type": "Debug"
},
...
]
...
#endif
```
## Builtin libraries
If you want to compile a plugin directly into the `clice` binary instead of loading it dynamically, see [Builtin Libraries](./builtin-library.md).

View File

@@ -53,24 +53,24 @@ You can also connect to a running clice service by configuring the clice-vscode
```jsonc
{
// Point this to the clice binary you downloaded.
"clice.executable": "/path/to/your/clice/executable",
// Point this to the clice binary you downloaded.
"clice.executable": "/path/to/your/clice/executable",
// Enable socket mode.
"clice.mode": "socket",
"clice.port": 50051,
// Enable socket mode.
"clice.mode": "socket",
"clice.port": 50051,
// Optional: Set this to an empty string to turn off the clangd.
"clangd.path": "",
// Optional: Set this to an empty string to turn off the clangd.
"clangd.path": "",
}
```
3. Reload Window: After modifying the configuration, execute the `Developer: Reload Window` command in VS Code for the settings to take effect. The extension will automatically connect to the clice instance listening on port 50051.
If you need to modify or debug the clice-vscode extension itself, follow these steps:
1. Clone and install dependencies:
```shell
$ git clone https://github.com/clice-io/clice-vscode
$ cd clice-vscode

View File

@@ -35,7 +35,7 @@ Glob patterns for matching file paths, following LSP's [standard](https://micros
- `{}`: Used for grouping conditions (e.g., `**/*.{ts,js}` matches all TypeScript and JavaScript files).
- `[]`: Declares a character range to match in a path segment (e.g., `example.[0-9]` matches `example.0`, `example.1`, etc.).
- `[!...]`: Excludes a character range to match in a path segment (e.g., `example.[!0-9]` matches `example.a`, `example.b`, but not `example.0`).
<br>
<br>
| Name | Type | Default |
| ---------------- | ------------------- | ------- |

View File

@@ -16,8 +16,8 @@ hero:
text: Contribution
link: /dev/contribution
image:
src: /image.png
alt: clice
src: /image.png
alt: clice
features:
- icon: T

2239
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
"private": true,
"type": "module",
"devDependencies": {
"@types/node": "^24.1.0",
"@types/node": "^24.10.4",
"vitepress": "^1.6.4"
},
"scripts": {
@@ -11,9 +11,12 @@
"docs:build": "vitepress build",
"docs:preview": "vitepress preview"
},
"overrides": {
"vite": {
"esbuild": "0.25.9"
}
"pnpm": {
"overrides": {
"esbuild": "~0.25.0"
},
"ignoredBuiltDependencies": [
"esbuild"
]
}
}

1629
docs/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -16,5 +16,4 @@ int main () {
`iostream` 这个头文件大概有 2w 行代码clice 会先把 `#include <iostream>` 这一行代码构建成 PCH 文件,在完成之后在使用这个 PCH 文件来解析后面的代码。这样的话后续重新解析的代码量就只剩 5 行了,而不是原本的 2w 行,速度会变得非常快。除非你修改了 preamble 部分的代码,导致需要构建新的 preamble。
## Cancel Compilation

View File

@@ -28,5 +28,4 @@ void foo(std::vector<std::vector<T>> vec2) {
2. 它只进行名称查找而不进行模板实例化,就算找到了最后的结果,也没法把它和最初的模板参数映射起来
3. 不考虑默认模板参数,无法处理由默认模板参数导致的依赖名
尽管我们可以对标准库的类型开洞来提供相关的支持但是我希望用户的代码能和标准库的代码有相同的地位那么我们就需要一种通用的算法来处理依赖类型。为了解决这个问题我编写了一个伪实例化器pseudo instantiator。它能在没有具体类型的前提下对依赖类型进行实例化从而达到化简的目的。比如上面这个例子里面的`std::vector<std::vector<T>>::reference`就能被化简为`std::vector<T>&`,进一步就能为用户提供代码补全选项。

View File

@@ -1,45 +1,79 @@
# Build from Source
## Supported Platforms
clice 依赖 C++23 特性,需要使用高版本的 C++ 编译器。同时,我们需要链接 LLVM/Clang 库来解析 AST。为了加快构建速度默认配置会下载我们发布的 [clice-llvm](https://github.com/clice-io/clice-llvm) 预编译包。这要求你的本地环境与预编译环境保持较高的一致性(尤其是开启 Address Sanitizer 或 LTO 时)。
- Windows
- Linux
- MacOS
为了简化环境设置并保证可复现性,我们**强烈推荐**使用 [pixi](https://pixi.prefix.dev/latest) 来管理开发环境。所有的依赖版本均严格定义在 `pixi.toml` 中。
## Prerequisite
如果你不想使用 pixi请参考下方的 [Manual Build](#manual-build) 章节。
- cmake/xmake
- clang, lld >= 20
- c++23 **compatible** standard library
- MSVC STL >= 19.44(VS 2022 17.4)
- GCC libstdc++ >= 14
- Clang libc++ >= 20
## Quick Start
clice 使用 C++23 作为语言标准,请确保有可用的 clang 20 以及以上的编译器,以及兼容 C++23 的标准库。clice 依赖 lld 作为链接器。请确保你的 clang 工具链可以找到它(通常 clang 发行版会自带 lld或者你需要单独安装 lld-20 包)
请参考 [pixi](https://pixi.prefix.dev/latest/installation) 官方指南安装 pixi
> clice 目前只保证能使用 clang 编译CI 测试保证)。对于 gcc 和 msvc 的兼容,我们尽力而为,但不会在 CI 中添加对应的测试。如果遇到任何问题,欢迎贡献。
## CMake
使用如下的命令构建 clice
我们内置了一系列任务,以下命令可直接完成编译并运行测试:
```shell
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
cmake --build build
# configure && build (default RelWithDebInfo)
pixi run build
# unit && integration
pixi run test
```
细粒度任务:上述命令由多个子任务组成,你也可以单独运行它们,并支持通过第一个参数指定构建类型:
```shell
pixi run cmake-config Debug
pixi run cmake-build Debug
pixi run unit-test Debug
pixi run integration-test Debug
```
> [!TIP]
> 如果你想直接使用 `cmake`, `ninja`, `clang++` 等命令进行开发,请运行 `pixi shell` 进入已配置好环境变量的终端
### XMake
我们同样支持使用 XMake 构建:
```shell
# config & build (default releasedbg)
pixi run xmake
# unit & integration
pixi run xmake-test
```
## Manual Build
如果你打算手动构建,请务必先确认你的工具链满足 pixi.toml 中定义的版本要求。
> 兼容性说明:理论上 clice 不依赖特定编译器的扩展可以使用主流编译器GCC/Clang/MSVC编译。但我们仅在 CI 中保证特定版本的 Clang 能通过测试。对于其他编译器或版本,我们提供**尽力而为 (Best Effort)** 的支持。如果遇到问题,欢迎提交 Issue 或 PR
### CMake
```shell
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain.cmake \
-DCLICE_ENABLE_TEST=ON
```
> 注意:`CMAKE_TOOLCHAIN_FILE` 是可选的。如果你使用的工具链与我们完全一致,可以使用预定义的 `cmake/toolchain.cmake`,否则请移除该选项
可选的构建选项:
| 选项 | 默认值 | 效果 |
| -------------------- | ------ | ----------------------------------------------------------------------------------------------------- |
| LLVM_INSTALL_PATH | "" | 使用自定义路径的 llvm libs 来构建 clice |
| CLICE_ENABLE_TEST | OFF | 是否构建 clice 的单元测试 |
| CLICE_USE_LIBCXX | OFF | 是否使用 libc++ 来构建 clice添加 `-std=libc++`),如果开启,请确保 llvm libs 也是使用 libc++ 编译的 |
| CLICE_CI_ENVIRONMENT | OFF | 是否打开 `CLICE_CI_ENVIRONMENT` 这个宏,有些测试在 CI 环境才会执行 |
| 选项 | 默认值 | 效果 |
| -------------------- | ------ | -------------------------------------------------------------------------------------------------- |
| LLVM_INSTALL_PATH | "" | 使用自定义路径的 LLVM 库来构建 clice |
| CLICE_ENABLE_TEST | OFF | 是否构建 clice 的单元测试 |
| CLICE_USE_LIBCXX | OFF | 是否使用 libc++ 来构建 clice添加 `-std=libc++`),如果开启,请确保 LLVM 库也是使用 libc++ 编译的 |
| CLICE_CI_ENVIRONMENT | OFF | 是否打开 `CLICE_CI_ENVIRONMENT` 这个宏,有些测试在 CI 环境才会执行 |
| CLICE_BUILTIN_LIBRARY_MODULES | "" | 以分号分隔的 CMake 模块列表,用于注册会被编译进 `clice` 的 builtin library详见 [Builtin Libraries](./builtin-library.md) |
## XMake
### XMake
使用如下命令即可构建 clice
使用如下命令即可构建 clice
```bash
xmake f -c --mode=releasedbg --toolchain=clang
@@ -48,34 +82,22 @@ xmake build --all
可选的构建选项:
| 选项 | 默认值 | 效果 |
| ------------- | ------ | --------------------------------------- |
| --llvm | "" | 使用自定义路径的 llvm libs 来构建 clice |
| --enable_test | false | 是否构建 clice 的单元测试 |
| --ci | false | 是否打开 `CLICE_CI_ENVIRONMENT` |
| 选项 | 默认值 | 效果 |
| ------------- | ------ | ------------------------------------ |
| --llvm | "" | 使用自定义路径的 LLVM 库来构建 clice |
| --enable_test | false | 是否构建 clice 的单元测试 |
| --ci | false | 是否打开 `CLICE_CI_ENVIRONMENT` |
## About LLVM
## A Note on LLVM Libs
clice 调用 Clang API 来解析 C++ 代码,因此必须链接 LLVM/Clang 库。由于 clice 使用了 Clang 的私有头文件(这些文件通常不包含在发行版中),不能直接使用系统安装的 LLVM 包。
由于 C++ 的语法太过复杂,自己编写一个新的 parser 是不现实的。clice 调用 clang 的 API 来 parse C++ 源文件获取 AST这意味它需要链接 llvm/clang libs。由于 clice 使用了 clang 的私有头文件,这些私有头文件在 llvm 发布的 binary release 中是没有的,所以不能直接使用系统的 llvm package。
主要有两种方式解决这个依赖问题:
1. 我们在 [clice-llvm](https://github.com/clice-io/clice-llvm/releases) 上会发布使用的 llvm 版本的预编译二进制,用于 CI 或者 release 构建。在构建时 cmake 和 xmake 默认会从此处下载 llvm libs 然后使用
1. 我们在 [clice-llvm](https://github.com/clice-io/clice-llvm/releases) 上会发布使用的 LLVM 版本的预编译二进制,用于 CI 或者 release 构建。在构建时 cmake 和 xmake 默认会从此处下载 LLVM 库然后使用
> [!IMPORTANT]
>
> 对于 debug 版本的 llvm libs,构建的时候我们开启了 address sanitizer而 address sanitizer 依赖于 compiler rt它对编译器版本十分敏感。所以如果使用 debug 版本,请确保你的 clang 的 compiler rt 版本和我们构建的时候**严格一致**
>
> - Windows 暂时没有 debug 构建的 llvm libs因为它不支持将 clang 构建为动态库,相关的进展在 [这里](https://github.com/clice-io/clice/issues/42) 跟踪
> - Linux 使用 clang20
> - MacOS 使用 homebrew llvm@20**不要使用 apple clang**
>
> 可以参考 CI 中的 [cmake](https://github.com/clice-io/clice/blob/main/.github/workflows/cmake.yml) 和 [xmake](https://github.com/clice-io/clice/blob/main/.github/workflows/xmake.yml) 文件作为参考,它们与预编译 llvm libs 的环境保持严格一致。
> 对于 debug 版本的 LLVM 库,构建的时候我们开启了 address sanitizer而 address sanitizer 依赖于 compiler rt它对编译器版本十分敏感。所以如果使用 debug 版本,请确保你的 clang 的 compiler rt 版本与 `pixi.toml` 中的定义严格一致。
2.己重新一个与当前环境一致的 llvm/clang。如果默认的预编译二进制文件(方法 1在你的系统上因 ABI 或库版本(如 glibc不兼容而运行失败,或者你需要一个自定义的 Debug 版本,那么我们推荐你使用此方法从头编译 llvm libs。我们提供了一个脚本,用于构建 clice 所需要的 llvm libs[build-llvm-libs.py](https://github.com/clice-io/clice/blob/main/scripts/build-llvm-libs.py)。
```bash
cd llvm-project
python3 <clice>/scripts/build-llvm-libs.py debug
```
也可以参考 llvm 的官方构建教程 [Building LLVM with CMake](https://llvm.org/docs/CMake.html)。
2.行构建一套与当前环境一致的 LLVM/Clang。如果默认的预编译二进制文件在你的系统上因 ABI 或库版本不兼容而运行失败,或者你需要一个自定义的 Debug 版本,那么我们推荐你使用此方法从头编译 LLVM 库。我们提供了一个脚本 `scripts/build-llvm.py` 用于构建所需要的 LLVM 库,也可以参考 LLVM 的官方构建教程 [Building LLVM with CMake](https://llvm.org/docs/CMake.html)。

View File

@@ -0,0 +1,101 @@
# Builtin Libraries
Builtin library 会被直接编译进 `clice` 可执行文件,而不是在运行时通过 `--plugin-path` 动态加载。
这种方式适合以下场景:
- 插件源码位于 `clice` 源码树之外
- 希望该 builtin 默认随 `clice` 可执行文件一起发布
- 需要为该 builtin 单独补充 include 路径、编译宏或链接依赖
## CMake 入口
`clice` 现在提供了一个辅助模块:[cmake/builtin-libraries.cmake](/cmake/builtin-libraries.cmake)。
额外的 builtin library 通过缓存变量 `CLICE_BUILTIN_LIBRARY_MODULES` 注册。
`CLICE_BUILTIN_LIBRARY_MODULES` 中的每一项都必须是一个 CMake 文件。配置阶段 `clice``include()` 这些文件,而每个文件都需要调用 `clice_add_builtin_library(...)`
## 最小示例
你可以在外部项目中创建一个 CMake 文件,例如 `/path/to/my-plugin/clice-builtin.cmake`
```cmake
clice_add_builtin_library(
NAME my_plugin
SOURCES
"${CMAKE_CURRENT_LIST_DIR}/src/MyPlugin.cpp"
INCLUDE_DIRECTORIES
"${CMAKE_CURRENT_LIST_DIR}/include"
ENTRYPOINT
clice_get_my_plugin_server_plugin_info
)
```
然后在配置 `clice` 时传入:
```shell
cmake -B build -G Ninja \
-DCLICE_BUILTIN_LIBRARY_MODULES="/path/to/my-plugin/clice-builtin.cmake"
```
如果要加载多个模块,可以传入以分号分隔的 CMake 列表:
```shell
cmake -B build -G Ninja \
-DCLICE_BUILTIN_LIBRARY_MODULES="/path/to/a.cmake;/path/to/b.cmake"
```
## `clice_add_builtin_library`
该辅助函数支持以下参数:
| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `NAME` | 是 | 逻辑名称,会用于创建内部 object target |
| `SOURCES` | 是 | 要编译进 `clice` 的源文件,支持绝对路径和源码树外路径 |
| `ENTRYPOINT` | 是 | `clice` 命名空间中的唯一函数名,返回值类型为 `::clice::PluginInfo` |
| `INCLUDE_DIRECTORIES` | 否 | 仅对当前 builtin 生效的额外头文件目录 |
| `LINK_LIBRARIES` | 否 | 当前 builtin 额外需要的库或 target |
| `COMPILE_DEFINITIONS` | 否 | 当前 builtin 额外需要的编译宏 |
| `COMPILE_OPTIONS` | 否 | 当前 builtin 额外需要的编译选项 |
## Entrypoint 要求
所有 builtin library 最终都会被链接进同一个可执行文件,因此每个 builtin 都必须在 `clice` 命名空间中使用唯一的入口函数名。
动态插件通常使用:
```cpp
clice_get_server_plugin_info()
```
builtin library 应该改用类似下面的唯一名字:
```cpp
clice_get_my_plugin_server_plugin_info()
```
例如:
```cpp
#include "Server/Plugin.h"
namespace clice {
::clice::PluginInfo clice_get_my_plugin_server_plugin_info() {
return {
CLICE_PLUGIN_API_VERSION,
"MyPlugin",
"v0.0.1",
CLICE_PLUGIN_DEF_HASH,
[](clice::ServerPluginBuilder& builder) {
// 在这里注册回调
},
};
}
} // namespace clice
```
`clice` 会自动生成静态注册代码,因此只要模块被包含进来,就不需要再手动修改 `src/clice.cc`

View File

@@ -7,5 +7,6 @@
## Code Style
命名
- 变量名:小写下换线
- 类名,枚举名:大驼峰

70
docs/zh/dev/extension.md Normal file
View File

@@ -0,0 +1,70 @@
# Extension
本节汇总各编辑器插件的开发与发布流程。目前包含 VSCode / Neovim / Zed。
## VSCode
VSCode 插件使用 Node/PNPM/VSCE 链路。推荐在 pixi 的 `node` 环境下操作以获得一致的工具链版本。
```shell
# 准备环境(先安装 pixi
pixi shell -e node
# 安装依赖(基于 pnpm-lock
pixi run install-vscode
# 打包扩展,产物位于 editors/vscode/*.vsix
pixi run build-vscode
```
发布到 VSCode Marketplace需要 `VSCE_PAT` 环境变量):
```shell
pixi run publish-vscode
```
> [!TIP]
> 若已编译 clice本地调试时可在 VSCode 设置中填写 `clice.executable`,使扩展指向你的自定义构建。
开发与调试:
1. `pixi shell -e node`
2.`editors/vscode` 下运行 `pnpm run watch`(增量构建)
3. VSCode 中使用 “Run Extension/Launch Extension” 调试配置,或执行 `code --extensionDevelopmentPath=$(pwd)/editors/vscode`
常用脚本(在 `pixi shell -e node` 下):
```bash
pnpm run package # 等价于 pixi run build-vscode
pnpm run publish # 等价于 pixi run publish-vscode
```
如果不使用 pixi请自行准备 node.js >= 20、pnpm然后在 `editors/vscode` 目录执行:
```bash
pnpm install
pnpm run package
```
## Neovim
Neovim 插件位于 `editors/nvim`,使用 Lua 编写。目前功能仍在演进中。
- 将仓库路径加入 `runtimepath`,例如:`set rtp+=/path/to/clice/editors/nvim`
- 或在本地创建软链接:`~/.config/nvim/pack/clice/start/clice` -> `<repo>/editors/nvim`
- 需要 `clice` 可执行文件可在 `$PATH` 中被找到
开发提示:代码量较小,可直接在 Neovim 中加载并通过 `:messages`/LSP 日志观察效果;格式化可使用 `stylua`(仓库中已提供 `stylua.toml`)。
## Zed
Zed 插件位于 `editors/zed`,使用 Rust 和 `zed_extension_api`
建议的本地验证流程:
```bash
cd editors/zed
cargo build --release
```
随后按 Zed 官方指南加载本地扩展(需安装 Zed CLI在启动前确保 `clice` 已在 PATH 中。发布时同样遵循 Zed 扩展发布流程。

View File

@@ -0,0 +1,78 @@
你可以在 clice 中实现一个 server plugin 来扩展 clice 的功能。
## 用例
当你使用 `clice` 作为 LLM 代理的 LSP 后端时,比如 claude code你可以添加插件来提供一些额外功能。
## 编写插件
当一个插件被服务器加载时,它会调用 `clice_get_server_plugin_info` 来获取关于这个插件的信息以及如何注册它的定制点。
这个函数需要由插件实现,请参考下面的示例:
```cpp
extern "C" ::clice::PluginInfo LLVM_ATTRIBUTE_WEAK
clice_get_server_plugin_info() {
return {
CLICE_PLUGIN_API_VERSION, "MyPlugin", "v0.1", CLICE_PLUGIN_DEF_HASH,
[](ServerPluginBuilder builder) { ... }
};
}
```
请参考 [PluginProtocol.h](/include/Server/PluginProtocol.h) 了解更多细节。
## 编译插件
插件必须使用与 clice 一致的依赖和编译器选项来编译,否则会导致 undefined behavior。[config/llvm-manifest.json](/config/llvm-manifest.json) 中定义了 clice 使用的构建信息。
## 加载插件
为了安全考虑clice 不允许通过配置文件来加载插件,而必须通过命令行选项来指定插件的路径。
`clice` 启动时,它会加载所有在命令行中指定的插件。你可以通过 `--plugin-path` 选项来指定插件的路径。
```shell
$ clice --plugin-path /path/to/my-plugin.so
```
## 获取 `CLICE_PLUGIN_DEF_HASH` 的内容
`clice_get_server_plugin_info` 函数中需要返回两个值。
- `CLICE_PLUGIN_API_VERSION` 用于确保插件和服务器之间的 `clice_get_server_plugin_info` 函数的一致性。
- `CLICE_PLUGIN_DEF_HASH` 用于确保插件和服务器之间的 C++ 声明的一致性。
要调试 `CLICE_PLUGIN_DEF_HASH` 的内容,你可以运行以下命令:
```shell
$ git clone https://github.com/clice-io/clice.git
$ cd clice
$ git checkout `clice --version --git-describe`
$ python scripts/plugin-def.py content > /tmp/plugin-proto.h
```
你将会得到一个 C 源码格式的文件,内容大致如下:
```cpp
#if 0
// begin of config/llvm-manifest.json
[
{
"version": "21.1.4+r1",
"filename": "arm64-macos-clang-debug-asan.tar.xz",
"sha256": "7da4b7d63edefecaf11773e7e701c575140d1a07329bbbb038673b6ee4516ff5",
"lto": false,
"asan": true,
"platform": "macosx",
"build_type": "Debug"
},
...
]
...
#endif
```
## Builtin libraries
如果你希望把插件直接编译进 `clice` 可执行文件,而不是在运行时动态加载,请参考 [Builtin Libraries](./builtin-library.md)。

View File

@@ -51,31 +51,31 @@ $ pytest -s --log-cli-level=INFO tests/integration/test_file_operation.py::test_
2. 配置 `settings.json`: 在你的项目根目录下创建 `.vscode/settings.json` 文件,并填入以下内容:
```jsonc
{
// Point this to the clice binary you downloaded.
"clice.executable": "/path/to/your/clice/executable",
```jsonc
{
// Point this to the clice binary you downloaded.
"clice.executable": "/path/to/your/clice/executable",
// Enable socket mode.
"clice.mode": "socket",
"clice.port": 50051,
// Enable socket mode.
"clice.mode": "socket",
"clice.port": 50051,
// Optional: Set this to an empty string to turn off the clangd.
"clangd.path": "",
}
```
// Optional: Set this to an empty string to turn off the clangd.
"clangd.path": "",
}
```
3. 重新加载窗口:修改配置后,在 vscode 中执行 Developer: Reload Window 命令使配置生效。插件会自动连接到正在 50051 端口监听的 clice。
如果你需要修改或调试 clice-vscode 插件本身,可以按以下步骤操作:
1. 克隆并安装依赖:
```shell
$ git clone https://github.com/clice-io/clice-vscode
$ cd clice-vscode
$ npm install
```
```shell
$ git clone https://github.com/clice-io/clice-vscode
$ cd clice-vscode
$ npm install
```
2. 使用 vscode 打开插件项目:用一个新的 vscode 窗口打开 clice-vscode 文件夹

View File

@@ -35,7 +35,7 @@
- `{}`: 用于分组条件 (例如,`**/*.{ts,js}` 匹配所有 TypeScript 和 JavaScript 文件)。
- `[]`: 声明要匹配的路径段中的字符范围 (例如,`example.[0-9]` 匹配 `example.0`, `example.1` 等)。
- `[!...]`: 排除要匹配的路径段中的字符范围 (例如,`example.[!0-9]` 匹配 `example.a`, `example.b`,但不匹配 `example.0`)。
<br>
<br>
| 名称 | 类型 | 默认值 |
| ---------------- | ------------------- | ------ |

View File

@@ -26,7 +26,6 @@ clice 实现了 [Language Server Protocol](https://microsoft.github.io/language-
自己从源码编译 clice具体的步骤参考 [build](../dev/build.md)。
## Project Setup
为了让 clice 能正确理解你的代码(例如找到头文件的位置),需要为 clice 提供一份 `compile_commands.json` 文件,也就说所谓的 [编译数据库](https://clang.llvm.org/docs/JSONCompilationDatabase.html)。编译数据库中提供了每个源文件的编译选项。

View File

@@ -16,8 +16,8 @@ hero:
text: 参与贡献
link: /zh/dev/contribution
image:
src: /image.png
alt: clice
src: /image.png
alt: clice
features:
- icon: T

View File

@@ -1,30 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "import",
"format": [ "camelCase", "PascalCase" ]
}
],
"@typescript-eslint/semi": "warn",
"curly": "warn",
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
},
"ignorePatterns": [
"out",
"dist",
"**/*.d.ts"
]
}

View File

@@ -1,5 +1,9 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher", "ms-vscode.extension-test-runner"]
"recommendations": [
"dbaeumer.vscode-eslint",
"amodio.tsl-problem-matcher",
"ms-vscode.extension-test-runner"
]
}

View File

@@ -3,19 +3,15 @@
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
}
]
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "${defaultBuildTask}"
}
]
}

View File

@@ -1,13 +1,13 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false, // set this to true to hide the "out" folder with the compiled JS files
"dist": false // set this to true to hide the "dist" folder with the compiled JS files
},
"search.exclude": {
"out": true, // set this to false to include "out" folder in search results
"dist": true // set this to false to include "dist" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off"
"files.exclude": {
"out": false, // set this to true to hide the "out" folder with the compiled JS files
"dist": false // set this to true to hide the "dist" folder with the compiled JS files
},
"search.exclude": {
"out": true, // set this to false to include "out" folder in search results
"dist": true // set this to false to include "dist" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off"
}

View File

@@ -1,40 +1,37 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$ts-webpack-watch",
"isBackground": true,
"presentation": {
"reveal": "never",
"group": "watchers"
},
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "npm",
"script": "watch-tests",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never",
"group": "watchers"
},
"group": "build"
},
{
"label": "tasks: watch-tests",
"dependsOn": [
"npm: watch",
"npm: watch-tests"
],
"problemMatcher": []
}
]
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$ts-webpack-watch",
"isBackground": true,
"presentation": {
"reveal": "never",
"group": "watchers"
},
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "npm",
"script": "watch-tests",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never",
"group": "watchers"
},
"group": "build"
},
{
"label": "tasks: watch-tests",
"dependsOn": ["npm: watch", "npm: watch-tests"],
"problemMatcher": []
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -141,28 +141,25 @@
"compile": "webpack",
"watch": "webpack --watch",
"vscode:prepublish": "webpack --mode production --devtool hidden-source-map",
"package": "vsce package --baseImagesUrl https://raw.githubusercontent.com/clice-project/clice-vscode/main/",
"publish": "vsce publish --baseImagesUrl https://raw.githubusercontent.com/clice-project/clice-vscode/main/",
"pretest": "npm run compile && npm run lint",
"lint": "eslint src --ext ts",
"package": "vsce package --no-dependencies --baseImagesUrl https://raw.githubusercontent.com/clice-io/clice/main/",
"publish": "vsce publish --no-dependencies --baseImagesUrl https://raw.githubusercontent.com/clice-io/clice/main/",
"pretest": "pnpm run compile",
"test": "vscode-test",
"release:patch": "npm version patch -m \"release: v%s\" && git push --follow-tags",
"release:minor": "npm version minor -m \"release: v%s\" && git push --follow-tags"
"release:patch": "pnpm version patch -m \"release: v%s\" && git push --follow-tags",
"release:minor": "pnpm version minor -m \"release: v%s\" && git push --follow-tags"
},
"devDependencies": {
"@types/decompress": "^4.2.7",
"@types/mocha": "^10.0.7",
"@types/node": "20.x",
"@types/mocha": "^10.0.10",
"@types/node": "~25.0.3",
"@types/vscode": "^1.80.0",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.11.0",
"@vscode/test-cli": "^0.0.12",
"@vscode/test-electron": "^2.4.0",
"eslint": "^8.57.0",
"ts-loader": "^9.5.1",
"typescript": "^5.4.5",
"webpack": "^5.92.1",
"webpack-cli": "^5.1.4"
"@vscode/test-electron": "^2.5.2",
"@vscode/vsce": "^3.7.1",
"ts-loader": "^9.5.4",
"typescript": "~5.5.4",
"webpack": "^5.104.1",
"webpack-cli": "^6.0.1"
},
"dependencies": {
"decompress": "^4.2.1",
@@ -170,5 +167,11 @@
},
"overrides": {
"glob": "^11.1.0"
},
"pnpm": {
"onlyBuiltDependencies": [
"@vscode/vsce-sign",
"keytar"
]
}
}

4373
editors/vscode/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,10 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
import * as https from 'https';
import * as os from 'os';
import * as vscode from "vscode";
import * as fs from "fs";
import * as path from "path";
import * as https from "https";
import * as os from "os";
// @ts-ignore
import decompress = require('decompress');
import decompress = require("decompress");
interface GitHubRelease {
tag_name: string;
@@ -14,12 +14,10 @@ interface GitHubRelease {
}[];
}
export async function ensureServerBinary(
context: vscode.ExtensionContext,
channel: vscode.OutputChannel
channel: vscode.OutputChannel,
): Promise<string | undefined> {
const storagePath = context.globalStorageUri.fsPath;
const platform = os.platform();
const arch = os.arch();
@@ -27,20 +25,20 @@ export async function ensureServerBinary(
channel.appendLine(`[Download] Initializing clice downloader...`);
channel.appendLine(`[Download] Platform: ${platform}, Arch: ${arch}, Storage: ${storagePath}`);
let platformKeyword = '';
let archKeyword = '';
let binaryName = 'clice';
let platformKeyword = "";
let archKeyword = "";
let binaryName = "clice";
if (platform === 'win32') {
platformKeyword = 'windows';
archKeyword = 'x64';
binaryName = 'clice.exe';
} else if (platform === 'darwin') {
platformKeyword = 'macos';
if (platform === "win32") {
platformKeyword = "windows";
archKeyword = "x64";
binaryName = "clice.exe";
} else if (platform === "darwin") {
platformKeyword = "macos";
archKeyword = arch;
} else if (platform === 'linux') {
platformKeyword = 'linux';
archKeyword = arch === 'x64' ? 'x86_64' : arch;
} else if (platform === "linux") {
platformKeyword = "linux";
archKeyword = arch === "x64" ? "x86_64" : arch;
} else {
const msg = `Unsupported platform: ${platform}`;
channel.appendLine(`[Download] Error: ${msg}`);
@@ -71,15 +69,19 @@ export async function ensureServerBinary(
const release = await fetchReleaseInfo(channel);
channel.appendLine(`[Download] Latest tag: ${release.tag_name}`);
const asset = release.assets.find(a => {
const asset = release.assets.find((a) => {
const name = a.name.toLowerCase();
return name.includes(platformKeyword) &&
return (
name.includes(platformKeyword) &&
name.includes(archKeyword) &&
!name.includes('symbol');
!name.includes("symbol")
);
});
if (!asset) {
throw new Error(`No compatible asset found for ${platform}-${archKeyword} in release ${release.tag_name}`);
throw new Error(
`No compatible asset found for ${platform}-${archKeyword} in release ${release.tag_name}`,
);
}
channel.appendLine(`[Download] Found asset: ${asset.name}`);
@@ -103,15 +105,16 @@ export async function ensureServerBinary(
throw new Error(`Executable not found at ${executablePath} after extraction.`);
}
if (platform !== 'win32') {
if (platform !== "win32") {
channel.appendLine(`[Download] Setting executable permissions (chmod 755)...`);
fs.chmodSync(executablePath, '755');
fs.chmodSync(executablePath, "755");
}
channel.appendLine(`[Download] Setup successful. Binary ready at: ${executablePath}`);
vscode.window.showInformationMessage(`Clice language server updated to ${release.tag_name}`);
vscode.window.showInformationMessage(
`Clice language server updated to ${release.tag_name}`,
);
return executablePath;
} catch (error) {
channel.appendLine(`[Download] CRITICAL ERROR during setup:`);
if (error instanceof Error) {
@@ -123,11 +126,16 @@ export async function ensureServerBinary(
channel.appendLine(`[Download] Unknown error: ${JSON.stringify(error)}`);
}
vscode.window.showErrorMessage(`Failed to download clice server. Check "clice" output channel for details.`, "Open Output").then(selection => {
if (selection === "Open Output") {
channel.show();
}
});
vscode.window
.showErrorMessage(
`Failed to download clice server. Check "clice" output channel for details.`,
"Open Output",
)
.then((selection) => {
if (selection === "Open Output") {
channel.show();
}
});
return undefined;
} finally {
@@ -135,60 +143,73 @@ export async function ensureServerBinary(
}
}
function downloadFile(url: string, destPath: string, channel: vscode.OutputChannel, maxRedirects = 5): Promise<void> {
function downloadFile(
url: string,
destPath: string,
channel: vscode.OutputChannel,
maxRedirects = 5,
): Promise<void> {
return new Promise((resolve, reject) => {
if (maxRedirects <= 0) {
reject(new Error('Too many redirects'));
reject(new Error("Too many redirects"));
return;
}
const file = fs.createWriteStream(destPath);
channel.appendLine(`[Download] Start downloading to ${destPath}`);
https.get(url, { headers: { 'User-Agent': 'VSCode-Extension' } }, (response) => {
if (response.statusCode === 302 || response.statusCode === 301) {
channel.appendLine(`[Download] Redirecting to ${response.headers.location}`);
file.close();
downloadFile(response.headers.location!, destPath, channel, maxRedirects - 1).then(resolve).catch(reject);
return;
}
if (response.statusCode !== 200) {
reject(new Error(`Download failed with status code ${response.statusCode}`));
return;
}
https
.get(url, { headers: { "User-Agent": "VSCode-Extension" } }, (response) => {
if (response.statusCode === 302 || response.statusCode === 301) {
channel.appendLine(`[Download] Redirecting to ${response.headers.location}`);
file.close();
downloadFile(response.headers.location!, destPath, channel, maxRedirects - 1)
.then(resolve)
.catch(reject);
return;
}
if (response.statusCode !== 200) {
reject(new Error(`Download failed with status code ${response.statusCode}`));
return;
}
response.pipe(file);
file.on('finish', () => {
response.pipe(file);
file.on("finish", () => {
file.close();
channel.appendLine(`[Download] Download finished.`);
resolve();
});
})
.on("error", (err) => {
file.close();
channel.appendLine(`[Download] Download finished.`);
resolve();
fs.unlink(destPath, () => {});
reject(err);
});
}).on('error', (err) => {
file.close();
fs.unlink(destPath, () => { });
reject(err);
});
});
}
async function fetchReleaseInfo(channel: vscode.OutputChannel): Promise<GitHubRelease> {
try {
channel.appendLine('[Download] Attempting to fetch latest stable release...');
const release = await fetchJson<GitHubRelease>('/repos/clice-io/clice/releases/latest');
channel.appendLine("[Download] Attempting to fetch latest stable release...");
const release = await fetchJson<GitHubRelease>("/repos/clice-io/clice/releases/latest");
channel.appendLine(`[Download] Found stable release: ${release.tag_name}`);
return release;
} catch (error: any) {
if (error.message && error.message.includes('404')) {
channel.appendLine('[Download] Latest stable release not found (404). Checking for pre-releases...');
if (error.message && error.message.includes("404")) {
channel.appendLine(
"[Download] Latest stable release not found (404). Checking for pre-releases...",
);
const releases = await fetchJson<GitHubRelease[]>('/repos/clice-io/clice/releases?per_page=1');
const releases = await fetchJson<GitHubRelease[]>(
"/repos/clice-io/clice/releases?per_page=1",
);
if (Array.isArray(releases) && releases.length > 0) {
const latestPre = releases[0];
channel.appendLine(`[Download] Found pre-release: ${latestPre.tag_name}`);
return latestPre;
} else {
throw new Error('No releases found in repository.');
throw new Error("No releases found in repository.");
}
}
throw error;
@@ -198,27 +219,29 @@ async function fetchReleaseInfo(channel: vscode.OutputChannel): Promise<GitHubRe
function fetchJson<T>(apiPath: string): Promise<T> {
return new Promise((resolve, reject) => {
const options = {
hostname: 'api.github.com',
hostname: "api.github.com",
path: apiPath,
headers: { 'User-Agent': 'VSCode-Extension' }
headers: { "User-Agent": "VSCode-Extension" },
};
https.get(options, (res) => {
let data = '';
if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
res.resume();
reject(new Error(`GitHub API returned ${res.statusCode} for ${apiPath}`));
return;
}
res.on('data', (chunk) => data += chunk);
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch (e) {
reject(new Error(`Failed to parse GitHub API response: ${e}`));
https
.get(options, (res) => {
let data = "";
if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
res.resume();
reject(new Error(`GitHub API returned ${res.statusCode} for ${apiPath}`));
return;
}
});
}).on('error', reject);
res.on("data", (chunk) => (data += chunk));
res.on("end", () => {
try {
resolve(JSON.parse(data));
} catch (e) {
reject(new Error(`Failed to parse GitHub API response: ${e}`));
}
});
})
.on("error", reject);
});
}

View File

@@ -1,93 +1,95 @@
import * as net from 'net';
import * as vscode from 'vscode';
import { workspace, window, ExtensionContext } from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions, StreamInfo } from 'vscode-languageclient/node';
import { getSetting } from './setting';
import { ensureServerBinary } from './download'
import * as net from "net";
import * as vscode from "vscode";
import { workspace, window, ExtensionContext } from "vscode";
import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
StreamInfo,
} from "vscode-languageclient/node";
import { getSetting } from "./setting";
import { ensureServerBinary } from "./download";
let client: LanguageClient;
export async function registerCommands(client: LanguageClient, context: ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand("clice.restart", async () => {
await client.restart();
}));
context.subscriptions.push(
vscode.commands.registerCommand("clice.restart", async () => {
await client.restart();
}),
);
}
export async function activate(context: ExtensionContext) {
console.log('Congratulations, your extension "clice" is now active!');
console.log('Congratulations, your extension "clice" is now active!');
const channel = window.createOutputChannel('clice');
const verboseChannel = window.createOutputChannel('clice-verbose');
const channel = window.createOutputChannel("clice");
const verboseChannel = window.createOutputChannel("clice-verbose");
const setting = getSetting();
if (!setting) {
return;
}
const setting = getSetting();
if (!setting) {
return;
}
let executable = setting.executable
let serverOptions: ServerOptions | (() => Promise<StreamInfo>);
let executable = setting.executable;
let serverOptions: ServerOptions | (() => Promise<StreamInfo>);
if (setting.mode === "pipe") {
if (!executable || executable === "") {
const downloadedPath = await ensureServerBinary(context, channel);
if (downloadedPath) {
executable = downloadedPath;
} else {
window.showErrorMessage("Could not find or download clice executable.");
return;
}
}
if (setting.mode === "pipe") {
if (!executable || executable === "") {
const downloadedPath = await ensureServerBinary(context, channel);
if (downloadedPath) {
executable = downloadedPath;
} else {
window.showErrorMessage("Could not find or download clice executable.");
return;
}
}
let args = ["--mode=pipe"];
serverOptions = {
run: { command: executable, args: args },
debug: { command: executable, args: args }
};
} else if (setting.mode === "socket") {
serverOptions = (): Promise<StreamInfo> => {
return new Promise((resolve, reject) => {
const client = new net.Socket();
client.connect(setting.port, setting.host, () => {
resolve({
reader: client,
writer: client,
});
});
client.on('error', (error) => {
reject(error);
});
});
};
} else {
vscode.window.showErrorMessage("Invalid mode, please set the mode to 'pipe' or 'socket'.");
return
}
let args = ["--mode=pipe"];
serverOptions = {
run: { command: executable, args: args },
debug: { command: executable, args: args },
};
} else if (setting.mode === "socket") {
serverOptions = (): Promise<StreamInfo> => {
return new Promise((resolve, reject) => {
const client = new net.Socket();
client.connect(setting.port, setting.host, () => {
resolve({
reader: client,
writer: client,
});
});
client.on("error", (error) => {
reject(error);
});
});
};
} else {
vscode.window.showErrorMessage("Invalid mode, please set the mode to 'pipe' or 'socket'.");
return;
}
const clientOptions: LanguageClientOptions = {
documentSelector: [{ scheme: 'file', language: 'cpp' }],
outputChannel: channel,
traceOutputChannel: verboseChannel,
synchronize: {
fileEvents: workspace.createFileSystemWatcher('**/.clientrc')
}
};
const clientOptions: LanguageClientOptions = {
documentSelector: [{ scheme: "file", language: "cpp" }],
outputChannel: channel,
traceOutputChannel: verboseChannel,
synchronize: {
fileEvents: workspace.createFileSystemWatcher("**/.clientrc"),
},
};
client = new LanguageClient(
'clice',
'clice',
serverOptions,
clientOptions
);
client = new LanguageClient("clice", "clice", serverOptions, clientOptions);
await registerCommands(client, context);
await registerCommands(client, context);
await client.start();
await client.start();
}
export function deactivate(): Thenable<void> | undefined {
if (!client) {
return undefined;
}
let ret = client.stop();
return ret;
if (!client) {
return undefined;
}
let ret = client.stop();
return ret;
}

View File

@@ -1,35 +1,37 @@
import * as vscode from 'vscode';
import { DocumentUri } from 'vscode-languageclient/node';
import * as vscode from "vscode";
import { DocumentUri } from "vscode-languageclient/node";
let provider: HeaderContextProvider | undefined = undefined;
export type HeaderContext = {
file: string,
index: number,
include: number
file: string;
index: number;
include: number;
};
export type HeaderContextSwitchParams = {
header: DocumentUri,
context: HeaderContext,
header: DocumentUri;
context: HeaderContext;
};
export type IncludeLocation = {
line: number,
filename: string
line: number;
filename: string;
};
export class TreeItem extends vscode.TreeItem {
children: Array<TreeItem> = []
children: Array<TreeItem> = [];
context: HeaderContext | undefined = undefined;
};
}
export class HeaderContextProvider implements vscode.TreeDataProvider<TreeItem> {
private _onDidChangeTreeData: vscode.EventEmitter<TreeItem | undefined | void> = new vscode.EventEmitter<TreeItem | undefined | void>();
readonly onDidChangeTreeData: vscode.Event<TreeItem | undefined | void> = this._onDidChangeTreeData.event;
private _onDidChangeTreeData: vscode.EventEmitter<TreeItem | undefined | void> =
new vscode.EventEmitter<TreeItem | undefined | void>();
readonly onDidChangeTreeData: vscode.Event<TreeItem | undefined | void> =
this._onDidChangeTreeData.event;
header: string = ""
items: Array<TreeItem> = []
header: string = "";
items: Array<TreeItem> = [];
update(contexts: Array<Array<HeaderContext>>) {
/// Create groups
@@ -60,11 +62,11 @@ export class HeaderContextProvider implements vscode.TreeDataProvider<TreeItem>
getChildren(element?: TreeItem) {
return element ? element.children : this.items;
}
};
}
export function registerHeaderContextView() {
provider = new HeaderContextProvider();
let treeView = vscode.window.createTreeView("header-contexts", { treeDataProvider: provider });
let treeView = vscode.window.createTreeView("header-contexts", {
treeDataProvider: provider,
});
}

View File

@@ -1,28 +1,28 @@
import * as vscode from 'vscode';
import * as vscode from "vscode";
const rainbowColors = [
"#56B6C2",
"#61AFEF",
"#C678DD",
"#E06C75",
"#98C379",
"#D19A66",
"#E5C07B"
];
const rainbowColors = ["#56B6C2", "#61AFEF", "#C678DD", "#E06C75", "#98C379", "#D19A66", "#E5C07B"];
const textEditorDecorationTypes = rainbowColors.map((color) => {
return vscode.window.createTextEditorDecorationType({
color: color
color: color,
});
});
export function highlightDocument(document: vscode.TextDocument, legend: vscode.SemanticTokensLegend, semanticTokens: vscode.SemanticTokens) {
export function highlightDocument(
document: vscode.TextDocument,
legend: vscode.SemanticTokensLegend,
semanticTokens: vscode.SemanticTokens,
) {
const editor = vscode.window.activeTextEditor;
if (!editor || editor.document !== document) { return; }
const angleIndex = legend?.tokenTypes.indexOf('angle');
const leftIndex = legend?.tokenModifiers.indexOf('left');
const rightIndex = legend?.tokenModifiers.indexOf('right');
if (leftIndex === undefined || rightIndex === undefined || angleIndex === undefined) { return; }
if (!editor || editor.document !== document) {
return;
}
const angleIndex = legend?.tokenTypes.indexOf("angle");
const leftIndex = legend?.tokenModifiers.indexOf("left");
const rightIndex = legend?.tokenModifiers.indexOf("right");
if (leftIndex === undefined || rightIndex === undefined || angleIndex === undefined) {
return;
}
const decorations = new Map<number, vscode.Range[]>();
let level = 0;
@@ -32,7 +32,8 @@ export function highlightDocument(document: vscode.TextDocument, legend: vscode.
// [line, startCharacter, length, tokenType, tokenModifiers]
for (let i = 0; i < semanticTokens.data.length; i += 5) {
const [lineDelta, startDelta, length, tokenType, tokenModifiers] = semanticTokens.data.slice(i, i + 5);
const [lineDelta, startDelta, length, tokenType, tokenModifiers] =
semanticTokens.data.slice(i, i + 5);
lastLine += lineDelta;
lastStart = lineDelta === 0 ? lastStart + startDelta : startDelta;
@@ -52,15 +53,11 @@ export function highlightDocument(document: vscode.TextDocument, legend: vscode.
if (tokenModifiers & (1 << leftIndex)) {
level += 1;
}
}
}
for (const [level, ranges] of decorations) {
editor.setDecorations(
textEditorDecorationTypes[level],
ranges
);
editor.setDecorations(textEditorDecorationTypes[level], ranges);
}
}

View File

@@ -1,31 +1,34 @@
import * as vscode from 'vscode';
import * as vscode from "vscode";
interface Setting {
executable: string | undefined,
mode: string,
host: string,
port: number,
executable: string | undefined;
mode: string;
host: string;
port: number;
}
export function getSetting(): Setting | undefined {
const setting = vscode.workspace.getConfiguration('clice')
const executable = setting.get<string>('executable');
const mode = setting.get<string>('mode');
const setting = vscode.workspace.getConfiguration("clice");
const executable = setting.get<string>("executable");
const mode = setting.get<string>("mode");
if (mode !== "pipe" && mode !== "socket") {
vscode.window.showErrorMessage(`Unexpected mode: ${mode}`);
return undefined
return undefined;
}
const host = setting.get<string>('host')!;
const port = setting.get<number>('port')!;
const host = setting.get<string>("host")!;
const port = setting.get<number>("port")!;
if (mode === "socket" && (!host || !port)) {
vscode.window.showErrorMessage('Socket mode requires both host and port to be configured.');
vscode.window.showErrorMessage("Socket mode requires both host and port to be configured.");
return undefined;
}
return {
executable, mode, host, port,
}
executable,
mode,
host,
port,
};
}

View File

@@ -1,15 +1,15 @@
import * as assert from 'assert';
import * as assert from "assert";
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
import * as vscode from "vscode";
// import * as myExtension from '../../extension';
suite('Extension Test Suite', () => {
vscode.window.showInformationMessage('Start all tests.');
suite("Extension Test Suite", () => {
vscode.window.showInformationMessage("Start all tests.");
test('Sample test', () => {
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
});
test("Sample test", () => {
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
});
});

View File

@@ -1,16 +1,14 @@
{
"compilerOptions": {
"module": "Node16",
"target": "ES2022",
"lib": [
"ES2022"
],
"sourceMap": true,
"rootDir": "src",
"strict": true /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
}
"compilerOptions": {
"module": "Node16",
"target": "ES2022",
"lib": ["ES2022"],
"sourceMap": true,
"rootDir": "src",
"strict": true /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
}
}

View File

@@ -1,48 +1,48 @@
//@ts-check
'use strict';
"use strict";
const path = require('path');
const path = require("path");
//@ts-check
/** @typedef {import('webpack').Configuration} WebpackConfig **/
/** @type WebpackConfig */
const extensionConfig = {
target: 'node', // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
target: "node", // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
output: {
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
path: path.resolve(__dirname, 'dist'),
filename: 'extension.js',
libraryTarget: 'commonjs2'
},
externals: {
vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
// modules added here also need to be added in the .vscodeignore file
},
resolve: {
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader'
}
]
}
]
},
devtool: 'nosources-source-map',
infrastructureLogging: {
level: "log", // enables logging required for problem matchers
},
entry: "./src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
output: {
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
path: path.resolve(__dirname, "dist"),
filename: "extension.js",
libraryTarget: "commonjs2",
},
externals: {
vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
// modules added here also need to be added in the .vscodeignore file
},
resolve: {
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
extensions: [".ts", ".js"],
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: "ts-loader",
},
],
},
],
},
devtool: "nosources-source-map",
infrastructureLogging: {
level: "log", // enables logging required for problem matchers
},
};
module.exports = [ extensionConfig ];
module.exports = [extensionConfig];

View File

@@ -2,12 +2,12 @@
name = "zed_clice"
version = "0.1.0"
edition = "2021"
publish = false
license = "MIT"
publish = false
[lib]
path = "src/clice.rs"
crate-type = ["cdylib"]
path = "src/clice.rs"
[dependencies]
zed_extension_api = "0.1.0"

View File

@@ -4,34 +4,12 @@ struct CliceExtension;
struct CliceBinary {
path: String,
resource_dir: String,
}
impl CliceExtension {
fn find_clice_binary(&self, worktree: &Worktree) -> Result<CliceBinary> {
if let Some(path_str) = worktree.which("clice") {
// The std::path module seems to behave unexpectedly in the Zed sandbox,
// failing to correctly parse parent directories for full paths on Windows.
// To ensure reliability, we revert to manual string manipulation to find the parent directory.
let separator_pos = path_str.rfind(|c| c == '\\' || c == '/');
if let Some(pos) = separator_pos {
let parent_dir = &path_str[..pos];
// We use std::path::MAIN_SEPARATOR to construct the path in a cross-platform way,
// avoiding the hardcoded '\' from the original implementation.
let resource_dir = format!("{}{}{}", parent_dir, std::path::MAIN_SEPARATOR, "lib");
Ok(CliceBinary {
path: path_str,
resource_dir,
})
} else {
Err(format!(
"clice found as `{}`,
but a full path is required to find the `lib` directory.",
path_str
))
}
Ok(CliceBinary { path: path_str })
} else {
Err(
"`clice` not found in your PATH. Please install it and add it to your system's PATH environment variable.".to_string()
@@ -55,8 +33,6 @@ impl zed::Extension for CliceExtension {
Ok(zed::Command {
command: binary.path,
args: vec![
"--resource-dir".to_string(),
binary.resource_dir,
"--mode".to_string(),
"pipe".to_string(),
],

View File

@@ -14,7 +14,7 @@ class FilteredASTVisitor : public clang::RecursiveASTVisitor<Derived> {
public:
using Base = clang::RecursiveASTVisitor<Derived>;
FilteredASTVisitor(CompilationUnit& unit, bool interested_only) :
FilteredASTVisitor(CompilationUnitRef unit, bool interested_only) :
unit(unit), interested_only(interested_only) {}
#define CHECK_DERIVED_IMPL(func) \
@@ -181,7 +181,7 @@ public:
#undef CHECK_DERIVED_IMPL
protected:
CompilationUnit& unit;
CompilationUnitRef unit;
bool interested_only;
};

View File

@@ -11,7 +11,7 @@
namespace clice {
class CompilationUnit;
class CompilationUnitRef;
/// A selection can partially or completely cover several AST nodes.
/// The SelectionTree contains nodes that are covered, and their parents.
@@ -47,7 +47,7 @@ public:
/// - Func should return true on success (stop) and false on failure (continue)
///
/// Always yields at least one tree. If no tokens are touched, it is empty.
static bool create_each(CompilationUnit& unit,
static bool create_each(CompilationUnitRef unit,
LocalSourceRange range,
llvm::function_ref<bool(SelectionTree)> callback);
@@ -55,7 +55,7 @@ public:
///
/// Where ambiguous (range is empty and borders two tokens), prefer the token
/// on the right.
static SelectionTree create_right(CompilationUnit& unit, LocalSourceRange range);
static SelectionTree create_right(CompilationUnitRef unit, LocalSourceRange range);
/// Copies are no good - contain pointers to other nodes.
SelectionTree(const SelectionTree&) = delete;
@@ -139,7 +139,7 @@ public:
private:
// Creates a selection tree for the given range in the main file.
// The range includes bytes [Start, End).
SelectionTree(CompilationUnit& unit, LocalSourceRange range);
SelectionTree(CompilationUnitRef unit, LocalSourceRange range);
// Stable-pointer storage, FIXME: use memory pool instead?
std::deque<Node> nodes;

View File

@@ -13,7 +13,7 @@ class SemanticVisitor : public FilteredASTVisitor<SemanticVisitor<Derived>> {
public:
using Base = FilteredASTVisitor<SemanticVisitor>;
SemanticVisitor(CompilationUnit& unit, bool interested_only) :
SemanticVisitor(CompilationUnitRef unit, bool interested_only) :
Base(unit, interested_only), unit(unit), resolver(unit.resolver()) {}
public:
@@ -725,7 +725,7 @@ public:
}
protected:
CompilationUnit& unit;
CompilationUnitRef unit;
TemplateResolver& resolver;
llvm::SmallVector<clang::Decl*> decls;
};

View File

@@ -110,7 +110,7 @@ struct Token {
return is_at_start_of_line && kind == clang::tok::hash;
}
/// The tokens after the include diretive are regarded as
/// The tokens after the include directive are regarded as
/// a whole token, whose kind is `header_name`. For example
/// `<iostream>` and `"test.h"` are both header name.
bool is_header_name() const {

View File

@@ -37,6 +37,10 @@ public:
}
}
void unset() {
ready = false;
}
void clear() {
ready = false;
awaiters.clear();

View File

@@ -98,7 +98,7 @@ public:
static std::optional<std::uint32_t> get_option_id(llvm::StringRef argument);
/// FIXME: bad interface design ...
std::vector<const char*> files();
std::vector<llvm::StringRef> files();
/// FIXME: remove this api?
auto save_string(llvm::StringRef string) -> llvm::StringRef;

View File

@@ -15,7 +15,7 @@ namespace clice {
struct CompilationParams {
/// The kind of this compilation.
CompilationUnit::Kind kind;
CompilationKind kind;
/// Whether to run clang-tidy.
bool clang_tidy = false;
@@ -48,9 +48,6 @@ struct CompilationParams {
/// to cancel old compilation task.
std::shared_ptr<std::atomic_bool> stop = std::make_shared<std::atomic_bool>(false);
/// Store all compilation errors in the process.
std::shared_ptr<std::vector<Diagnostic>> diagnostics;
void add_remapped_file(llvm::StringRef path,
llvm::StringRef content,
std::uint32_t bound = -1) {
@@ -62,23 +59,21 @@ struct CompilationParams {
}
};
using CompilationResult = std::expected<CompilationUnit, std::string>;
/// Only preprocess ths source flie.
CompilationResult preprocess(CompilationParams& params);
CompilationUnit preprocess(CompilationParams& params);
/// Build AST from given file path and content. If pch or pcm provided, apply them to the compiler.
/// Note this function will not check whether we need to update the PCH or PCM, caller should check
/// their reusability and update in time.
CompilationResult compile(CompilationParams& params);
CompilationUnit compile(CompilationParams& params);
/// Build PCH from given file path and content.
CompilationResult compile(CompilationParams& params, PCHInfo& out);
CompilationUnit compile(CompilationParams& params, PCHInfo& out);
/// Build PCM from given file path and content.
CompilationResult compile(CompilationParams& params, PCMInfo& out);
CompilationUnit compile(CompilationParams& params, PCMInfo& out);
/// Run code completion at the given location.
CompilationResult complete(CompilationParams& params, clang::CodeCompleteConsumer* consumer);
CompilationUnit complete(CompilationParams& params, clang::CodeCompleteConsumer* consumer);
} // namespace clice

View File

@@ -12,51 +12,79 @@
namespace clice {
/// All AST related information needed for language server.
class CompilationUnit {
enum class CompilationKind : std::uint8_t {
/// From preprocessing the source file. Therefore directives
/// are available but AST nodes are not.
Preprocess,
/// From indexing the static source file.
Indexing,
/// From building preamble for the source file.
Preamble,
/// From building precompiled module for the module interface unit.
ModuleInterface,
/// From building normal AST for source file(except preamble), interested file and top level
/// declarations are available.
Content,
/// From running code completion for the source file(preamble is applied).
Completion,
};
enum class CompilationStatus : std::uint8_t {
Completed,
Cancelled,
SetupFail,
FatalError,
};
class CompilationUnitRef {
public:
/// The kind describes how we preprocess this source file
/// to get this compilation unit.
enum class Kind : std::uint8_t {
/// From preprocessing the source file. Therefore directives
/// are available but AST nodes are not.
Preprocess,
struct Self;
/// From indexing the static source file.
Indexing,
CompilationUnitRef(Self* self) : self(self) {}
/// From building preamble for the source file.
Preamble,
/// From building precompiled module for the module interface unit.
ModuleInterface,
/// From building normal AST for source file(except preamble), interested file and top level
/// declarations are available.
Content,
/// From running code completion for the source file(preamble is applied).
Completion,
};
using enum Kind;
struct Impl;
CompilationUnit(Kind kind, Impl* impl) : kind(kind), impl(impl) {}
CompilationUnit(const CompilationUnit&) = delete;
CompilationUnit(CompilationUnit&& other) : kind(other.kind), impl(other.impl) {
other.impl = nullptr;
Self* operator->() {
return self;
}
~CompilationUnit();
public:
CompilationKind kind();
CompilationStatus status();
/// Parse finished; ASTContext is usable but diagnostics may still contain errors.
bool completed() {
return status() == CompilationStatus::Completed;
}
/// Compilation was cancelled; consumers should not touch any state.
bool cancelled() {
return status() == CompilationStatus::Cancelled;
}
/// Failed during initial setup; diagnostics exist (location-free), ASTContext
/// is unavailable.
bool setup_fail() {
return status() == CompilationStatus::SetupFail;
}
/// Hit an unrecoverable error; diagnostics and decoded source locations
/// are usable, other states are not unavailable.
bool fatal_error() {
return status() == CompilationStatus::FatalError;
}
public:
/// Get the file id for given file. If such file doesn't exist, the result
/// will be invalid file id. If the the content of the file doesn't have
/// `#pragma once` or guard macro, each inclusion of the file will generate
/// a new file id, return the first one.
auto file_id(clang::FileEntryRef file) -> clang::FileID;
auto file_id(llvm::StringRef file) -> clang::FileID;
/// If the location represents file location, it is composed of a file id
@@ -165,7 +193,7 @@ public:
bool is_module_interface_unit();
/// Return all diagnostics in the process of compilation.
auto diagnostics() -> llvm::ArrayRef<Diagnostic>;
auto diagnostics() -> std::vector<Diagnostic>&;
auto top_level_decls() -> llvm::ArrayRef<clang::Decl*>;
@@ -196,10 +224,29 @@ public:
/// Get symbol ID for given marco.
index::SymbolID getSymbolID(const clang::MacroInfo* macro);
private:
Kind kind;
protected:
Self* self;
};
Impl* impl;
/// All AST related information needed for language server.
class CompilationUnit : public CompilationUnitRef {
public:
explicit CompilationUnit(Self* self) : CompilationUnitRef(self) {}
CompilationUnit(const CompilationUnit&) = delete;
CompilationUnit(CompilationUnit&& other) : CompilationUnitRef(other.self) {
other.self = nullptr;
}
CompilationUnit& operator=(const CompilationUnit&) = delete;
CompilationUnit& operator=(CompilationUnit&& other) {
std::swap(self, other.self);
return *this;
}
~CompilationUnit();
};
} // namespace clice

View File

@@ -4,15 +4,6 @@
#include <string>
#include "AST/SourceCode.h"
#include "Compiler/Tidy.h"
#include "clang/Basic/Diagnostic.h"
namespace clang {
class DiagnosticConsumer;
}
namespace clice {
@@ -58,11 +49,6 @@ struct DiagnosticID {
bool is_unused() const;
};
class DiagnosticCollector : public clang::DiagnosticConsumer {
public:
tidy::ClangTidyChecker* checker = nullptr;
};
struct Diagnostic {
/// The diagnostic id.
DiagnosticID id;
@@ -76,9 +62,6 @@ struct Diagnostic {
/// The error message of this diagnostic.
std::string message;
static std::unique_ptr<DiagnosticCollector>
create(std::shared_ptr<std::vector<Diagnostic>> diagnostics);
};
} // namespace clice

View File

@@ -135,10 +135,6 @@ struct Directive {
std::vector<MacroRef> macros;
std::vector<Pragma> pragmas;
std::vector<Import> imports;
/// Tell preprocessor to collect directives information and store them in `directives`.
static void attach(clang::Preprocessor& pp,
llvm::DenseMap<clang::FileID, Directive>& directives);
};
} // namespace clice

View File

@@ -1,26 +0,0 @@
#pragma once
#include <memory>
#include "llvm/ADT/StringRef.h"
namespace clang {
class CompilerInstance;
}
namespace clice::tidy {
bool is_registered_tidy_check(llvm::StringRef check);
std::optional<bool> is_fast_tidy_check(llvm::StringRef check);
struct TidyParams {};
class ClangTidyChecker;
/// Configure to run clang-tidy on the given file.
std::unique_ptr<ClangTidyChecker> configure(clang::CompilerInstance& instance,
const TidyParams& params);
} // namespace clice::tidy

View File

@@ -6,7 +6,7 @@
namespace clice {
struct CompilationUnit;
class CompilationUnitRef;
}
@@ -14,6 +14,6 @@ namespace clice::feature {
/// FIXME: This is not correct way, we don't want to couple
/// `Feature with Protocol`? Return an array of LSP diagnostic.
json::Value diagnostics(PositionEncodingKind kind, PathMapping mapping, CompilationUnit& unit);
json::Value diagnostics(PositionEncodingKind kind, PathMapping mapping, CompilationUnitRef unit);
} // namespace clice::feature

View File

@@ -18,9 +18,9 @@ struct DocumentLink {
using DocumentLinks = std::vector<DocumentLink>;
/// Generate document link for main file.
DocumentLinks document_links(CompilationUnit& unit);
DocumentLinks document_links(CompilationUnitRef unit);
/// Generate document link for all source file.
index::Shared<DocumentLinks> index_document_link(CompilationUnit& unit);
index::Shared<DocumentLinks> index_document_link(CompilationUnitRef unit);
} // namespace clice::feature

View File

@@ -29,9 +29,9 @@ struct DocumentSymbol {
using DocumentSymbols = std::vector<DocumentSymbol>;
/// Generate document symbols for only interested file.
DocumentSymbols document_symbols(CompilationUnit& unit);
DocumentSymbols document_symbols(CompilationUnitRef unit);
/// Generate document symbols for all file in unit.
index::Shared<DocumentSymbols> index_document_symbol(CompilationUnit& unit);
index::Shared<DocumentSymbols> index_document_symbol(CompilationUnitRef unit);
} // namespace clice::feature

View File

@@ -47,9 +47,9 @@ struct FoldingRange {
using FoldingRanges = std::vector<FoldingRange>;
/// Generate folding range for interested file only.
FoldingRanges folding_ranges(CompilationUnit& unit);
FoldingRanges folding_ranges(CompilationUnitRef unit);
/// Generate folding range for all files.
index::Shared<FoldingRanges> index_folding_range(CompilationUnit& unit);
index::Shared<FoldingRanges> index_folding_range(CompilationUnitRef unit);
} // namespace clice::feature

View File

@@ -66,9 +66,9 @@ struct Hover {
};
/// Generate the hover information for the given declaration(for test).
Hover hover(CompilationUnit& unit, const clang::NamedDecl* decl);
Hover hover(CompilationUnitRef unit, const clang::NamedDecl* decl);
/// Generate the hover information for the symbol at the given offset.
Hover hover(CompilationUnit& unit, std::uint32_t offset);
Hover hover(CompilationUnitRef unit, std::uint32_t offset);
} // namespace clice::feature

View File

@@ -50,7 +50,7 @@ struct InlayHint {
std::vector<index::SymbolID> parts;
};
auto inlay_hints(CompilationUnit& unit,
auto inlay_hints(CompilationUnitRef unit,
LocalSourceRange target,
const config::InlayHintsOptions& options) -> std::vector<InlayHint>;

View File

@@ -21,9 +21,9 @@ struct SemanticToken {
using SemanticTokens = std::vector<SemanticToken>;
/// Generate semantic tokens for the interested file only.
SemanticTokens semantic_tokens(CompilationUnit& unit);
SemanticTokens semantic_tokens(CompilationUnitRef unit);
/// Generate semantic tokens for all files.
index::Shared<SemanticTokens> index_semantic_token(CompilationUnit& unit);
index::Shared<SemanticTokens> index_semantic_token(CompilationUnitRef unit);
} // namespace clice::feature

View File

@@ -8,7 +8,7 @@
namespace clice {
class CompilationUnit;
class CompilationUnitRef;
}
@@ -41,7 +41,7 @@ struct IncludeGraph {
/// context. A map between FileID and its include location.
llvm::DenseMap<clang::FileID, std::uint32_t> file_table;
static IncludeGraph from(CompilationUnit& unit);
static IncludeGraph from(CompilationUnitRef unit);
llvm::StringRef path(std::uint32_t path_ref) const {
assert(path_ref < paths.size());

View File

@@ -5,7 +5,7 @@
namespace clice {
class CompilationUnit;
class CompilationUnitRef;
}

View File

@@ -75,7 +75,7 @@ struct TUIndex {
FileIndex main_file_index;
static TUIndex build(CompilationUnit& unit);
static TUIndex build(CompilationUnitRef unit);
};
} // namespace clice::index

View File

@@ -6,6 +6,8 @@
#include <string>
#include <vector>
#include "llvm/Support/JSON.h"
namespace clice::proto {
using integer = std::int32_t;
@@ -15,6 +17,8 @@ using decimal = double;
using string = std::string;
using any = llvm::json::Value;
template <typename T>
using array = std::vector<T>;

View File

@@ -0,0 +1,17 @@
#pragma once
#include "../Basic.h"
namespace clice::proto {
struct ExecuteCommandParams {
string command;
array<any> arguments;
};
struct TextDocumentParams {
/// The text document.
TextDocumentIdentifier textDocument;
};
} // namespace clice::proto

View File

@@ -11,6 +11,7 @@
#include "Feature/DocumentHighlight.h"
#include "Feature/DocumentLink.h"
#include "Feature/DocumentSymbol.h"
#include "Feature/ExecuteCommand.h"
#include "Feature/FoldingRange.h"
#include "Feature/Formatting.h"
#include "Feature/Hover.h"

View File

@@ -21,7 +21,7 @@ struct ProjectOptions {
std::string logging_dir = "${workspace}/.clice/logging";
std::vector<std::string> compile_commands_dirs = {"${workspace}/build"};
std::vector<std::string> compile_commands_paths = {"${workspace}/build"};
};
struct Rule {

View File

@@ -44,6 +44,11 @@ public:
if(it2 != project_index.indices.end()) {
auto path = project_index.path_pool.path(it2->second);
it->second = index::MergedIndex::load(path);
} else {
std::println(stderr,
"failed to load project index for path_id: {} {}",
path_id,
project_index.indices.size());
}
return it->second;
@@ -67,6 +72,14 @@ public:
/// TODO: Types ...
bool empty() const {
return project_index.indices.empty();
}
size_t size() const {
return project_index.indices.size();
}
private:
CompilationDatabase& database;
@@ -87,6 +100,10 @@ private:
std::deque<std::uint32_t> waitings;
async::Event update_event;
async::Event finish_event;
size_t finish_cnt = 0;
};
} // namespace clice

59
include/Server/Plugin.h Normal file
View File

@@ -0,0 +1,59 @@
#pragma once
#include <expected>
#include "PluginProtocol.h"
#include "llvm/ADT/StringRef.h"
// clang-format off
/// Run `python scripts/plugin-def.py update` to update the hash.
#define CLICE_PLUGIN_DEF_HASH "sha256:332cd65741dea17d5168dd1ab564ac73ce230f14fc1e34170438d895eb11881c"
// clang-format on
namespace clice {
/// The hash of the definitions exposed to server plugins.
constexpr std::string_view plugin_definition_hash = CLICE_PLUGIN_DEF_HASH;
class Server;
struct ServerPluginBuilder;
/// A loaded server plugin.
///
/// An instance of this class wraps a loaded server plugin and gives access to its interface.
class Plugin {
public:
/// Attempts to load a server plugin from a given file.
///
/// Returns an error if either the library cannot be found or loaded,
/// there is no public entry point, or the plugin implements the wrong API
/// version.
static std::expected<Plugin, std::string> load(const std::string& file_path);
/// Gets the file path of the loaded plugin.
llvm::StringRef file_path() const;
/// Gets the name of the loaded plugin.
llvm::StringRef name() const;
/// Gets the version of the loaded plugin.
llvm::StringRef version() const;
/// Registers the server callbacks for the loaded plugin.
void register_server_callbacks(ServerPluginBuilder& builder) const;
public:
struct Self;
Plugin(Self* self) : self(self) {}
Self* operator->() {
return self;
}
protected:
Self* self;
};
} // namespace clice

View File

@@ -0,0 +1,113 @@
#pragma once
/// The API version of the clice plugin.
/// Update this version when you change:
/// - The definition of struct `PluginInfo`.
/// - The definition of function `clice_get_server_plugin_info`.
/// Note: you don't have to update this version if you only change other APIs, which is guaranteed
/// by the `PluginInfo::definition_hash`.
#define CLICE_PLUGIN_API_VERSION 1
#include <cstdint>
#include "Async/Async.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/JSON.h"
namespace clice {
class Server;
struct ServerPluginBuilder;
/// Defines the library APIs that loads a plugin.
extern "C" {
/// A C-compatible struct that contains information about the plugin.
struct PluginInfo {
/// The clice API version of the plugin.
uint32_t api_version;
/// The name of the plugin.
const char* name;
/// The version of the plugin.
const char* version;
/// The plugin definition hash.
const char* definition_hash;
/// Registers the server callbacks for the loaded plugin.
void (*register_server_callbacks)(ServerPluginBuilder& builder);
};
/// The public entry point for a server plugin.
///
/// When a plugin is loaded by the server, it will call this entry point to
/// obtain information about this plugin and about how to register its customization points.
/// This function needs to be implemented by the plugin, see the example below:
///
/// ```cpp
/// #include "Server/Plugin.h"
/// extern "C" ::clice::PluginInfo LLVM_ATTRIBUTE_WEAK
/// clice_get_server_plugin_info() {
/// return {
/// CLICE_PLUGIN_API_VERSION, "MyPlugin", "v0.0.1", CLICE_PLUGIN_DEF_HASH,
/// [](clice::ServerPluginBuilder& builder) { ... }
/// };
/// }
/// ```
PluginInfo LLVM_ATTRIBUTE_WEAK clice_get_server_plugin_info();
}
struct ServerRef {
public:
struct Self;
ServerRef(Server* self) : self(self) {}
Server* operator->() const {
return self;
}
Server& server() const;
protected:
Server* self;
};
/// Defines the library APIs to register callbacks for a plugin.
struct ServerPluginBuilder {
public:
ServerPluginBuilder(ServerRef server_ref) : server_ref(server_ref) {}
/// Gets a reference to the server.
auto get_server_ref() const -> ServerRef {
return server_ref;
}
#define CliceServerPluginAPI(METHOD, ...) void METHOD(void* plugin_data, __VA_ARGS__)
using lifecycle_hook_t = async::Task<> (*)(ServerRef server, void* plugin_data);
/// Registers a callback to be called when the server is initialized.
CliceServerPluginAPI(on_initialize, lifecycle_hook_t callback);
/// Registers a callback to be called when the server is initialized.
CliceServerPluginAPI(on_initialized, lifecycle_hook_t callback);
/// Registers a callback to be called when the server is shutdown.
CliceServerPluginAPI(on_shutdown, lifecycle_hook_t callback);
/// Registers a callback to be called when the server is exiting.
CliceServerPluginAPI(on_exit, lifecycle_hook_t callback);
/// Registers a callback to be called when the server's configuration is changed.
CliceServerPluginAPI(on_did_change_configuration, lifecycle_hook_t callback);
using command_handler_t =
async::Task<llvm::json::Value> (*)(ServerRef server,
void* plugin_data,
llvm::ArrayRef<llvm::json::Value> arguments);
/// Registers a callback to be called when a command is received from the LSP client.
CliceServerPluginAPI(register_commmand_handler,
llvm::StringRef command,
command_handler_t callback);
#undef CliceServerPluginAPI
protected:
ServerRef server_ref;
};
} // namespace clice

View File

@@ -3,6 +3,7 @@
#include "Config.h"
#include "Convert.h"
#include "Indexer.h"
#include "Plugin.h"
#include "Async/Async.h"
#include "Compiler/Command.h"
#include "Compiler/Diagnostic.h"
@@ -10,6 +11,8 @@
#include "Feature/DocumentLink.h"
#include "Protocol/Protocol.h"
#include <llvm/ADT/FunctionExtras.h>
namespace clice {
struct OpenFile {
@@ -30,10 +33,6 @@ struct OpenFile {
async::Task<> ast_build_task;
async::Lock ast_built_lock;
/// Collect all diagnostics in the compilation.
std::shared_ptr<std::vector<Diagnostic>> diagnostics =
std::make_unique<std::vector<Diagnostic>>();
/// For header with context, it may have multiple ASTs, use
/// an chain to store them.
std::unique_ptr<OpenFile> next;
@@ -80,10 +79,10 @@ public:
}
/// Try get OpenFile from manager, default construct one if not exists.
[[nodiscard]] ActiveFile& get_or_add(llvm::StringRef path);
[[nodiscard]] ActiveFile get_or_add(llvm::StringRef path);
/// Add a OpenFile to the manager.
ActiveFile& add(llvm::StringRef path, OpenFile file);
ActiveFile add(llvm::StringRef path, OpenFile file);
[[nodiscard]] bool contains(llvm::StringRef path) const {
return index.contains(path);
@@ -98,7 +97,7 @@ public:
}
private:
ActiveFile& lru_put_impl(llvm::StringRef path, OpenFile file);
ActiveFile lru_put_impl(llvm::StringRef path, OpenFile file);
private:
/// The maximum size of the cache.
@@ -171,7 +170,7 @@ private:
async::Task<> on_exit(proto::ExitParams params);
private:
public:
/// Load the cache info from disk.
void load_cache_info();
@@ -196,6 +195,8 @@ private:
private:
using Result = async::Task<json::Value>;
auto on_execute_command(proto::ExecuteCommandParams params) -> Result;
auto on_completion(proto::CompletionParams params) -> Result;
auto on_hover(proto::HoverParams params) -> Result;
@@ -222,7 +223,7 @@ private:
auto on_inlay_hint(proto::InlayHintParams params) -> Result;
private:
public:
/// The current request id.
std::uint32_t server_request_id = 0;
std::uint32_t client_request_id = 0;
@@ -245,6 +246,20 @@ private:
config::Config config;
Indexer indexer;
public:
friend struct ServerPluginBuilder;
using lifecycle_hook_t = llvm::unique_function<async::Task<>()>;
using command_handler_t = llvm::unique_function<async::Task<llvm::json::Value>(
llvm::ArrayRef<llvm::json::Value> arguments)>;
std::vector<lifecycle_hook_t> initialize_hooks;
std::vector<lifecycle_hook_t> initialized_hooks;
std::vector<lifecycle_hook_t> shutdown_hooks;
std::vector<lifecycle_hook_t> exit_hooks;
std::vector<lifecycle_hook_t> did_change_configuration_hooks;
llvm::StringMap<command_handler_t> command_handlers;
std::vector<Plugin> plugins;
};
} // namespace clice

3
include/Server/Utility.h Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
#define bail(...) std::unexpected(std::format(__VA_ARGS__))

Some files were not shown because too many files have changed in this diff Show More