Skip to content

Commit bab6cc9

Browse files
imalsogregaaronvgellipsis-dev[bot]
authored
New LSP server (#1465)
The server is meant to replace much of the existing `server.ts`, which implements an LSP for VSCode. The LSP in this PR is a native binary that should work with any LSP client (VSCode, emacs, vim, etc). Features - [x] Push diagnostics - [x] Respond to basic file requests - [x] did_open - [x] did_change - [ ] did_close - [x] did_change_watched_files - [ ] Request parity with `server.ts` - [x] Hover - [x] CodeLens - [x] Completion - [x] GotoDefinition - [x] Format - [x] Watch files in project - [x] Test that unsaved changes are reflected in playground tests - [x] Test Windows - [x] Test Linux - [x] Handle projects with nested directories - [x] Run generation - [ ] Manage configuration - [x] Toast notificitons on generation - [ ] Handle multiple compiler versions - [x] Test with neovim - [x] Test with emaics - [x] Test with VSCode - [x] Test baml_src/other/baml_src (it should swap workspaces the nearest baml_src parent). <!-- ELLIPSIS_HIDDEN --> ---- > [!IMPORTANT] > This PR introduces a new Rust-based LSP server with enhanced features and updates the build and release workflows accordingly. > > - **LSP Server**: > - Introduces a new Rust-based LSP server to replace the existing TypeScript server. > - Supports diagnostics, file watching, and LSP requests like `did_open`, `did_change`, `hover`, `completion`, and `goto_definition`. > - Implements `baml-lsp-types` and `language-server` crates. > - **Build and Release**: > - Updates GitHub Actions workflows for building and releasing the new LSP server. > - Adds `build-cli-release.reusable.yaml` and `release-cli.yaml` for CLI builds. > - Modifies `build-vscode-release.reusable.yaml` for VSCode extension packaging. > - **Miscellaneous**: > - Updates `Cargo.lock` and `Cargo.toml` for new dependencies. > - Adds test cases for the LSP server in `tests.rs`. > > <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=BoundaryML%2Fbaml&utm_source=github&utm_medium=referral)<sup> for d2fbcad. It will automatically update as commits are pushed.</sup> <!-- ELLIPSIS_HIDDEN --> --------- Co-authored-by: Aaron Villalpando <aaron@boundaryml.com> Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
1 parent 145e887 commit bab6cc9

106 files changed

Lines changed: 11593 additions & 1350 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
name: BAML Release - Build BAML CLI
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
version:
7+
description: 'Release version'
8+
required: false
9+
type: string
10+
package_for_release:
11+
description: 'Create archives for release instead of raw binaries'
12+
type: boolean
13+
default: true
14+
required: false
15+
16+
env:
17+
MACOSX_DEPLOYMENT_TARGET: "10.13"
18+
19+
20+
concurrency:
21+
# suffix is important to prevent a concurrency deadlock with the calling workflow
22+
group: ${{ github.workflow }}-${{ github.ref }}-build-cli
23+
cancel-in-progress: true
24+
25+
jobs:
26+
build:
27+
strategy:
28+
fail-fast: false
29+
matrix:
30+
_:
31+
# Always include these targets
32+
- os: ubuntu-20.04
33+
target: x86_64-unknown-linux-gnu
34+
- os: ubuntu-20.04
35+
target: x86_64-unknown-linux-musl
36+
static: true
37+
- os: macos-14
38+
target: x86_64-apple-darwin
39+
- os: macos-14
40+
target: aarch64-apple-darwin
41+
- os: windows-2022
42+
target: x86_64-pc-windows-msvc
43+
- os: windows-2022
44+
target: aarch64-pc-windows-msvc
45+
# ARM targets (will be conditionally skipped if not needed)
46+
- os: ubuntu-20.04
47+
target: aarch64-unknown-linux-gnu
48+
- os: ubuntu-20.04
49+
target: aarch64-unknown-linux-musl
50+
51+
52+
runs-on: ${{ matrix._.os }}
53+
env:
54+
CROSS_VERSION: v0.2.5
55+
CARGO: cargo
56+
TARGET_FLAGS: ${{ matrix._.target != '' && format('--target {0}', matrix._.target) || '' }}
57+
RUST_BACKTRACE: 1
58+
59+
steps:
60+
- uses: actions/checkout@v4
61+
62+
- uses: dtolnay/rust-toolchain@stable
63+
with:
64+
toolchain: stable
65+
targets: ${{ matrix._.target }}
66+
67+
# Use cross for Linux cross-compilation
68+
- name: Install cross
69+
if: contains(matrix._.os, 'ubuntu') && matrix._.target != 'x86_64-unknown-linux-gnu'
70+
shell: bash
71+
run: |
72+
dir="$RUNNER_TEMP/cross-download"
73+
mkdir "$dir"
74+
echo "$dir" >> $GITHUB_PATH
75+
cd "$dir"
76+
curl -LO "https://github.com/cross-rs/cross/releases/download/$CROSS_VERSION/cross-x86_64-unknown-linux-musl.tar.gz"
77+
tar xf cross-x86_64-unknown-linux-musl.tar.gz
78+
echo "CARGO=cross" >> $GITHUB_ENV
79+
80+
- uses: Swatinem/rust-cache@v2
81+
with:
82+
workspaces: engine
83+
prefix-key: "v2-rust"
84+
85+
# Build the CLI - Always use static-ssl features
86+
- name: Build CLI
87+
# This single step now handles all builds
88+
run: >
89+
${{ env.CARGO }} build --release --bin baml-cli ${{ env.TARGET_FLAGS }}
90+
--features static-ssl
91+
--no-default-features
92+
working-directory: engine
93+
94+
# Determine binary path and archive name
95+
- name: Set binary and archive paths
96+
id: paths
97+
if: inputs.package_for_release
98+
shell: bash
99+
run: |
100+
echo "version: ${{ inputs.version }}"
101+
if [ "${{ matrix._.os }}" = "windows-latest" ]; then
102+
bin="engine/target/${{ matrix._.target }}/release/baml-cli.exe"
103+
else
104+
bin="engine/target/${{ matrix._.target }}/release/baml-cli"
105+
fi
106+
# Ensure the binary exists before proceeding
107+
if [ ! -f "$bin" ]; then
108+
echo "Binary not found at $bin"
109+
exit 1
110+
fi
111+
echo "bin=$bin" >> $GITHUB_OUTPUT
112+
# Use inputs.version if provided, otherwise default to 'dev' or similar
113+
version_name="${{ inputs.version || 'dev' }}"
114+
archive_base="baml-cli-${version_name}-${{ matrix._.target }}"
115+
echo "archive_base=$archive_base" >> $GITHUB_OUTPUT
116+
echo "archive_dir=./${archive_base}" >> $GITHUB_OUTPUT # Directory to create for staging files
117+
118+
# Print determined paths for debugging
119+
- name: Print artifact details
120+
if: inputs.package_for_release
121+
shell: bash
122+
run: |
123+
echo "Binary Path: ${{ steps.paths.outputs.bin }}"
124+
echo "Archive Base Name: ${{ steps.paths.outputs.archive_base }}"
125+
echo "Staging Directory: ${{ steps.paths.outputs.archive_dir }}"
126+
127+
# Create archives with proper format
128+
- name: Create archive (Windows)
129+
if: contains(matrix._.os, 'windows') && inputs.package_for_release
130+
shell: bash
131+
run: |
132+
set -euo pipefail # Exit immediately if a command exits with a non-zero status, treat unset variables as an error, and catch errors in pipelines.
133+
134+
staging_dir="${{ steps.paths.outputs.archive_dir }}"
135+
archive_base="${{ steps.paths.outputs.archive_base }}"
136+
archive_file="${archive_base}.zip"
137+
checksum_file="${archive_base}.zip.sha256"
138+
139+
echo "Creating staging directory: $staging_dir"
140+
mkdir -p "$staging_dir"
141+
142+
echo "Copying files to staging directory"
143+
cp "${{ steps.paths.outputs.bin }}" "$staging_dir/"
144+
# Use find for robust copy
145+
find . -maxdepth 1 -name 'README.md' -exec cp {} "$staging_dir/" \; || echo "README.md not found"
146+
find . -maxdepth 1 -name 'LICENSE*' -exec cp {} "$staging_dir/" \; || echo "LICENSE* files not found"
147+
148+
echo "Creating archive: $archive_file"
149+
# Use original cd logic for 7z stability, but run in subshell
150+
(
151+
cd "$staging_dir"
152+
7z a "../${archive_file}" .
153+
)
154+
# Check if 7z command succeeded and file exists
155+
if [ ! -f "${archive_file}" ]; then
156+
echo "Archive creation failed or file not found: ${archive_file}"
157+
exit 1
158+
fi
159+
160+
echo "Creating checksum file: $checksum_file"
161+
certutil -hashfile "${archive_file}" SHA256 > "${checksum_file}"
162+
# Check if certutil command succeeded and file exists
163+
if [ ! -f "${checksum_file}" ]; then
164+
echo "Checksum creation failed or file not found: ${checksum_file}"
165+
exit 1
166+
fi
167+
# Add check for empty checksum file
168+
if [ ! -s "${checksum_file}" ]; then
169+
echo "Checksum file is empty: ${checksum_file}"
170+
exit 1
171+
fi
172+
173+
echo "Archive and checksum created successfully:"
174+
ls -l "${archive_base}".*
175+
176+
- name: Create archive (Unix)
177+
if: ${{ !contains(matrix._.os, 'windows') && inputs.package_for_release }}
178+
shell: bash
179+
run: |
180+
set -e # Exit immediately if a command exits with a non-zero status.
181+
182+
staging_dir="${{ steps.paths.outputs.archive_dir }}"
183+
archive_base="${{ steps.paths.outputs.archive_base }}"
184+
archive_file="${archive_base}.tar.gz"
185+
checksum_file="${archive_file}.sha256"
186+
187+
echo "Creating staging directory: $staging_dir"
188+
mkdir -p "$staging_dir"
189+
190+
echo "Copying files to staging directory"
191+
cp "${{ steps.paths.outputs.bin }}" "$staging_dir/"
192+
# Use find for robust copy
193+
find . -maxdepth 1 -name 'README.md' -exec cp {} "$staging_dir/" \; || echo "README.md not found"
194+
find . -maxdepth 1 -name 'LICENSE*' -exec cp {} "$staging_dir/" \; || echo "LICENSE* files not found"
195+
196+
echo "Creating archive: $archive_file"
197+
# Create the archive in the parent directory directly using -C
198+
tar czf "${archive_file}" -C "$staging_dir" .
199+
# Check if tar command succeeded and file exists
200+
if [ ! -f "${archive_file}" ]; then
201+
echo "Archive creation failed or file not found: ${archive_file}"
202+
exit 1
203+
fi
204+
205+
echo "Creating checksum file: $checksum_file"
206+
shasum -a 256 "${archive_file}" > "${checksum_file}"
207+
# Check if shasum command succeeded and file exists
208+
if [ ! -f "${checksum_file}" ]; then
209+
echo "Checksum creation failed or file not found: ${checksum_file}"
210+
exit 1
211+
fi
212+
213+
echo "Archive and checksum created successfully:"
214+
ls -l "${archive_base}".*
215+
216+
# Debug: List root directory contents
217+
- name: List root directory contents
218+
shell: bash
219+
run: |
220+
echo "Listing root directory contents:"
221+
ls -la
222+
echo "Looking for specific archive files:"
223+
find . -name "${{ steps.paths.outputs.archive_base }}.*" -type f
224+
225+
# Upload different artifacts based on mode
226+
- name: Upload CLI artifacts (Release)
227+
if: inputs.package_for_release
228+
uses: actions/upload-artifact@v4
229+
with:
230+
# Artifact name in GitHub Actions UI remains the same
231+
name: baml-cli-${{ matrix._.target }}
232+
path: |
233+
${{ steps.paths.outputs.archive_base }}.*
234+
if-no-files-found: error
235+
236+
- name: Upload CLI artifacts (CI)
237+
if: inputs.package_for_release == false
238+
uses: actions/upload-artifact@v4
239+
with:
240+
name: baml-cli-${{ matrix._.target }}
241+
path: |
242+
${{ matrix._.os == 'windows-latest' && format('engine/target/{0}/release/baml-cli.exe', matrix._.target) || format('engine/target/{0}/release/baml-cli', matrix._.target) }}

.github/workflows/build-python-release.reusable.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ jobs:
6767
# building in engine/ ensures that we pick up .cargo/config.toml
6868
working-directory: engine
6969
args: --release --out language_client_python/dist --manifest-path language_client_python/Cargo.toml
70-
sccache: "true"
7170
manylinux: ${{ matrix._.manylinux }}
7271
before-script-linux: |
7372
if command -v yum &> /dev/null; then

.github/workflows/build-ruby-release.reusable.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ jobs:
5252
bundler-cache: false
5353
cargo-cache: false
5454
cargo-vendor: false
55+
rustup-toolchain: stable
5556

5657
#################################################################################################################
5758
#

0 commit comments

Comments
 (0)