#!/usr/bin/env bash # Kmpl: acme glue for u/complete. Triggers completion and prints result to stdout. set -euo pipefail OLLIE_TOOLS_PATH=${OLLIE_TOOLS_PATH:-$HOME/.config/ollie/tools} cmd=${0##*/} die() { printf '%s: %s\n' "$cmd" "$*" >&2; exit 1; } try() { "$@" 2>/dev/null || { ollie-remount && "$@"; }; } [ -z "${winid:-}" ] || [ "$winid" = "0" ] && die "must be run from an acme window" # Get filename from tag filename=$(9p read acme/$winid/tag | awk '{print $1; exit}') # Read dot position (char offsets) addr=$(printf 'addr=dot' | 9p write acme/$winid/ctl | 9p read acme/$winid/addr) read -r q0 q1 <<< "$addr" [ -z "$q1" ] && q1=$q0 # prefix = BOF to q0, suffix = q1 to EOF, selection = rdsel prefix=$(printf '#0,#%s' "$q0" | 9p write acme/$winid/addr | 9p read acme/$winid/data) suffix=$(printf '#%s,$' "$q1" | 9p write acme/$winid/addr | 9p read acme/$winid/data) sel=$(9p read acme/$winid/rdsel 2>/dev/null || true) # Compute cursor line/col for LSP calls line=$(printf '%s' "$prefix" | wc -l) [ "$line" -eq 0 ] && line=1 col=$(printf '%s' "$prefix" | tail -1 | wc -m) # Gather LSP context in parallel tmpdir=$(mktemp -d) trap 'rm -rf "$tmpdir"' EXIT python3 $OLLIE_TOOLS_PATH/lsp_completion "$filename" "$line" "$col" > "$tmpdir/completions" 2>/dev/null & python3 $OLLIE_TOOLS_PATH/lsp_symbols "$filename" > "$tmpdir/symbols" 2>/dev/null & python3 $OLLIE_TOOLS_PATH/lsp_hover "$filename" "$line" "$col" > "$tmpdir/hover" 2>/dev/null & python3 $OLLIE_TOOLS_PATH/lsp_diagnostics "$filename" > "$tmpdir/diagnostics" 2>/dev/null & wait # Walk up to workspace root (parent of .git) modroot=$(dirname "$filename") while [ "$modroot" != "/" ]; do [ -e "$modroot/.git" ] && { modroot=$(dirname "$modroot"); break; } modroot=$(dirname "$modroot") done [ "$modroot" = "/" ] && modroot=$(dirname "$filename") # Resolve referenced types from hover + selection (iterative, up to 3 levels deep) { [ -s "$tmpdir/hover" ] && cat "$tmpdir/hover" printf '%s\n' "$sel" } > "$tmpdir/hover_cur" if [ -s "$tmpdir/hover_cur" ]; then touch "$tmpdir/hover_embedded" "$tmpdir/seen_types" for _depth in 1 2 3; do sed -n 's/.*\[\]\*\?\([A-Z][A-Za-z0-9_]*\).*/\1/p; s/.*\*\([A-Z][A-Za-z0-9_]*\).*/\1/p; s/.*[a-z]\+\.\([A-Z][A-Za-z0-9_]*\).*/\1/p' "$tmpdir/hover_cur" \ | grep -vE '^(MessageState|UnknownFields|SizeCache)$' \ | sort -u > "$tmpdir/new_types" || true # Remove already-seen types comm -23 "$tmpdir/new_types" "$tmpdir/seen_types" > "$tmpdir/todo_types" || true [ -s "$tmpdir/todo_types" ] || break cat "$tmpdir/todo_types" >> "$tmpdir/seen_types" sort -u -o "$tmpdir/seen_types" "$tmpdir/seen_types" > "$tmpdir/hover_cur" while IFS= read -r etype; do # Find type reference in current file, then jump to definition ref=$(grep -n "\b${etype}\b" "$filename" 2>/dev/null | head -1 || true) [ -z "$ref" ] && continue rline=${ref%%:*} rcol=$(sed -n "${rline}p" "$filename" 2>/dev/null | grep -bo "\b${etype}\b" | head -1 | cut -d: -f1 || echo 0) rcol=$((rcol + 1)) defn=$(python3 $OLLIE_TOOLS_PATH/lsp_definition "$filename" "$rline" "$rcol" 2>/dev/null | head -1 || true) [ -z "$defn" ] && continue dfile=${defn%%:*} dline=$(echo "$defn" | cut -d: -f2) dcol=$(echo "$defn" | cut -d: -f3) result=$(python3 $OLLIE_TOOLS_PATH/lsp_hover "$dfile" "$dline" "$dcol" 2>/dev/null || true) [ -n "$result" ] && [ "$result" != "(no hover info)" ] && { printf '%s\n' "$result" | tee -a "$tmpdir/hover_embedded" >> "$tmpdir/hover_cur"; continue; } # Fallback: read raw source block sed -n "${dline},/^}/p" "$dfile" 2>/dev/null | tee -a "$tmpdir/hover_embedded" >> "$tmpdir/hover_cur" || true done < "$tmpdir/todo_types" done fi context="" [ -s "$tmpdir/completions" ] && context="${context} $(cat "$tmpdir/completions") " [ -s "$tmpdir/symbols" ] && context="${context} $(cat "$tmpdir/symbols") " [ -s "$tmpdir/hover" ] && context="${context} $(cat "$tmpdir/hover") " [ -s "$tmpdir/diagnostics" ] && context="${context} $(head -20 "$tmpdir/diagnostics") " [ -s "$tmpdir/hover_embedded" ] && context="${context} $(cat "$tmpdir/hover_embedded") " # Send prefix+selection as prefix context; selection is the focal point _input=$(printf '%s%s' "$prefix" "$sel") # Build JSON request for /complete (write-then-read on same fid via 9p rdwr) json=$(printf '{"cwd":%s,"file":%s,"prefix":%s,"suffix":%s,"context":%s}' \ "$(printf '%s' "$modroot" | jq -Rs .)" \ "$(printf '%s' "$filename" | jq -Rs .)" \ "$(printf '%s' "$_input" | jq -Rs .)" \ "$(printf '%s' "$suffix" | jq -Rs .)" \ "$(printf '%s' "$context" | jq -Rs .)") result=$(printf '%s' "$json" | try 9p rdwr ollie/complete) printf '%s' "$result"