edit

package
v1.10.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package edit implements symbol editing tools: replace body, insert before/after, rename, safe delete, and post-edit diagnostic verification.

Index

Constants

View Source
const (
	EditTypeReplaceBody  = "replace_body"
	EditTypeInsertBefore = "insert_before"
	EditTypeInsertAfter  = "insert_after"
	EditTypeRename       = "rename"
	EditTypeDelete       = "delete"
)

EditType enumerates the kinds of edits the planner can produce.

Variables

This section is empty.

Functions

func ClassifyEditError

func ClassifyEditError(err error) string

ClassifyEditError maps an edit/fileops handler error to one of the closed helix_edit_outcome_total "outcome" values (Phase 53 D-10).

Source-of-truth bucketing (RESEARCH §"Edit-Tool Outcome Map"):

  • fuzzy.ErrAmbiguous → "ambiguous_match"
  • fuzzy.ErrNoMatch → "no_match"
  • serr.NoWorkspace → "internal" (no_workspace is a setup error, not an edit-domain bucket)
  • all other errors → "internal"

Q-3 (RESOLVED 2026-04-30): missing-required-field errors and other serr.InvalidArgs results that are NOT fuzzy ambiguity / no-match map to "internal" — preserves the locked D-10 6-value enum; classified as a known under-classification scheduled for v1.3 typed-error work.

"ls_error" classification is BEST-EFFORT in v1.2: without typed "LS-failure" sentinels in internal/kernel/edit, every LS adapter failure currently flows as serr.Internal-wrapped errors that look identical to file-I/O errors. The bucket exists in the closed enum per D-10, but in v1.2 it is only emitted via direct outcome assignment at known LS call sites (e.g. didChange notify, apply rename edits) — not via this classifier. v1.3 will add LS-error sentinels and bridge them here.

"validation_failed" is similarly NOT classified by this helper — it is flipped at the end of a happy-path handler when the post-edit verifier reports VerifyResult.HasErrors (see appendVerifyInfoWithStatus).

Exported as ClassifyEditError so internal/kernel/fileops/ can reuse the helper without duplicating the bucket map (DRY: single source of truth for the D-10 outcome classifier).

func InsertAfter

func InsertAfter(ctx context.Context, lease *lspool.WorkerLease, uri string, symbolName string, content string) error

InsertAfter inserts content immediately after a symbol's range end. Per D-13: LSP-first, DocumentSymbol range boundaries are sufficient.

func InsertAfterWithPlan

func InsertAfterWithPlan(ctx context.Context, lease *lspool.WorkerLease, plan *EditPlan) error

InsertAfterWithPlan executes an insert-after using a pre-computed plan.

func InsertBefore

func InsertBefore(ctx context.Context, lease *lspool.WorkerLease, uri string, symbolName string, content string) error

InsertBefore inserts content immediately before a symbol's range start. Per D-13: LSP-first, DocumentSymbol range boundaries are sufficient.

func InsertBeforeWithPlan

func InsertBeforeWithPlan(ctx context.Context, lease *lspool.WorkerLease, plan *EditPlan) error

InsertBeforeWithPlan executes an insert-before using a pre-computed plan.

func RegisterTools

func RegisterTools(server *mcp.SerenaMCPServer, k *kernel.Kernel, extractor *BodyExtractor, diagStore *diag.DiagnosticStore, wsKeyFn func() workspace.WorkspaceKey)

RegisterTools registers all 6 symbol editing tools with the MCP server. Each handler is wrapped with kernel.WrapToolSpan to produce kernel.tool.{name} sub-spans under the TelemetryMiddleware span (Phase 12, TRACE-03).

func ReplaceBody

func ReplaceBody(ctx context.Context, lease *lspool.WorkerLease, extractor *BodyExtractor, uri string, symbolName string, newBody string, lang string) error

ReplaceBody replaces a symbol's body using tree-sitter for precise body extraction. Per D-14: tree-sitter-first for body surgery, LSP-assisted for discovery. Per D-15: falls back to full symbol range replacement when tree-sitter unavailable.

Types

type BodyExtractor

type BodyExtractor struct {
	// contains filtered or unexported fields
}

BodyExtractor uses tree-sitter to precisely extract symbol body byte ranges. Per D-14: tree-sitter-first for replace-body operations.

func NewBodyExtractor

func NewBodyExtractor(registry *treesitter.GrammarRegistry) *BodyExtractor

NewBodyExtractor creates a BodyExtractor using the shared grammar registry.

func (*BodyExtractor) ExtractBody

func (be *BodyExtractor) ExtractBody(source []byte, lang string, symbolName string, symbolRange gen.Range) (startByte, endByte uint, err error)

ExtractBody parses source with the language grammar and returns the byte range of the body of the declaration that matches symbolName and overlaps symbolRange. Per D-14: tree-sitter-first for precise body surgery.

func (*BodyExtractor) SupportsLanguage

func (be *BodyExtractor) SupportsLanguage(lang string) bool

SupportsLanguage returns true if the extractor has a grammar for the given language.

type DeleteResult

type DeleteResult struct {
	Deleted      bool
	References   int                      // number of remaining references (if not deleted)
	RefLocations []symbols.SymbolLocation // where the refs are
}

DeleteResult summarizes the outcome of a delete operation.

func SafeDelete

func SafeDelete(ctx context.Context, lease *lspool.WorkerLease, uri string, symbolName string, force bool) (*DeleteResult, error)

SafeDelete deletes a symbol from a file after checking for references. EDT-05: Unless force=true, checks FindReferences before deleting.

func SafeDeleteWithPlan

func SafeDeleteWithPlan(ctx context.Context, readLease, writeLease *lspool.WorkerLease, plan *EditPlan, force bool) (*DeleteResult, error)

SafeDeleteWithPlan executes a safe delete using a pre-computed plan. readLease is used for reference lookups (can be clean/shared), writeLease for file mutation.

type DiagnosticSummary

type DiagnosticSummary struct {
	Line    int
	Col     int
	Message string
	Source  string
}

DiagnosticSummary is a simplified diagnostic for reporting.

type EditPlan

type EditPlan struct {
	URI            string
	SymbolName     string
	EditType       string    // one of EditType* constants
	Range          gen.Range // computed edit range (full symbol extent)
	SelectionRange gen.Range // identifier range (for cursor positioning)
	NewContent     string    // content to insert/replace
}

EditPlan describes a resolved edit ready for execution.

func PlanEdit

func PlanEdit(ctx context.Context, lease *lspool.WorkerLease, uri string, symbolName string, editType string, content string) (*EditPlan, error)

PlanEdit resolves a symbol via documentSymbol and produces an EditPlan. Should be called on a warm (indexed) lease for accurate symbol ranges.

type FuzzyMatchInfo

type FuzzyMatchInfo struct {
	Strategy fuzzy.Strategy
	Score    float64
}

FuzzyMatchInfo carries fuzzy match metadata back to the tool handler for response formatting (D-07).

func ReplaceBodyWithPlan

func ReplaceBodyWithPlan(ctx context.Context, lease *lspool.WorkerLease, extractor *BodyExtractor, plan *EditPlan, lang string, searchBody string) (*FuzzyMatchInfo, error)

ReplaceBodyWithPlan executes a body replacement using a pre-computed plan. When searchBody is non-empty, the replacement is scoped to the fuzzy-matched sub-region within the tree-sitter/range-extracted body (FUZZ-05). Returns FuzzyMatchInfo when fuzzy matching was used, nil otherwise.

type InsertAfterArgs

type InsertAfterArgs struct {
	Path       string `json:"path" jsonschema:"File path"`
	SymbolName string `json:"symbol_name" jsonschema:"Name of the symbol to insert after"`
	Content    string `json:"content" jsonschema:"Content to insert"`
}

InsertAfterArgs is the input schema for the insert_after_symbol tool.

type InsertBeforeArgs

type InsertBeforeArgs struct {
	Path       string `json:"path" jsonschema:"File path"`
	SymbolName string `json:"symbol_name" jsonschema:"Name of the symbol to insert before"`
	Content    string `json:"content" jsonschema:"Content to insert"`
}

InsertBeforeArgs is the input schema for the insert_before_symbol tool.

type RenameOverrider

type RenameOverrider interface {
	RenameOverride(
		ctx context.Context,
		lease *lspool.WorkerLease,
		uri string,
		line, col int,
		newName string,
	) (*RenameResult, error)
}

RenameOverrider is an optional interface that lspool.QuirkAdapter implementations may satisfy (directly or via a wrapper in package edit) to bypass textDocument/rename. Called by edit.RenameSymbol when the native rename attempt fails.

Semantic-accuracy note: overrides are NOT required to match native LSP rename fidelity (cross-crate trait-impl discovery, macro expansion). Implementations MUST document their limits in their doc comment AND in USAGE.md. See BUG-DEFER-02.

On success returns (*RenameResult, nil); the caller sets Strategy. On failure returns (nil, serr.*); the caller wraps in serr.Unsupported per D-06 if the native path also failed.

type RenameResult

type RenameResult struct {
	FilesChanged int
	EditsApplied int
	Files        []string
	// Strategy tags which dispatch path produced this result (D-07).
	// Populated by RenameSymbol on every nil-error return.
	Strategy RenameStrategy
}

RenameResult summarizes the outcome of a rename operation.

func RenameSymbol

func RenameSymbol(ctx context.Context, lease *lspool.WorkerLease, uri string, line, col int, newName string) (*RenameResult, error)

RenameSymbol dispatches a rename request. It first attempts the native LSP path (textDocument/prepareRename + textDocument/rename); on failure, if the underlying LS adapter exposes a RenameOverrider (directly or via the rust-analyzer wrapper), it delegates to the override. On success the result carries a Strategy tag. If both paths fail, returns serr.Unsupported with a message pointing agents at fuzzy_edit, replace_symbol_body, or search_in_files (D-06).

func RustClientSideRename

func RustClientSideRename(
	ctx context.Context,
	lease *lspool.WorkerLease,
	uri string,
	line, col int,
	newName string,
) (*RenameResult, error)

RustClientSideRename implements the references-driven fallback used by RustAnalyzerRenameOverride. Kept in package edit to avoid the lspool -> edit import cycle (applyTextEdits is package-private here).

Contract assumptions (rust-analyzer only — DO NOT generalize):

  1. textDocument/references (includeDeclaration=true) returns ranges that exactly span the identifier to be renamed.
  2. The symbol has at least one reference (including the declaration).

Failure modes:

  • zero references -> serr.NotFound
  • references RPC error -> serr.Internal (wrap)
  • text-edit apply error -> serr.Internal (wrap, with file URI detail)

type RenameStrategy

type RenameStrategy string

RenameStrategy enumerates the path a successful rename took. String values are a public contract: they appear in rename_symbol tool responses AND in the helix_rename_strategy_total metric label set. MUST NOT change without a coordinated telemetry-schema update.

const (
	// StrategyLSPNative indicates textDocument/rename succeeded via the LS.
	StrategyLSPNative RenameStrategy = "lsp-native"
	// StrategyRustClientSide indicates the rust-analyzer quirk override was
	// used: references were gathered via textDocument/references and text
	// edits were applied client-side. See BUG-DEFER-02 for semantic limits.
	StrategyRustClientSide RenameStrategy = "rust-client-side"
)

type RenameSymbolArgs

type RenameSymbolArgs struct {
	Path    string `json:"path" jsonschema:"File path where symbol is defined"`
	Line    int    `json:"line" jsonschema:"Line number of symbol (1-indexed)"`
	Col     int    `json:"column" jsonschema:"Column number of symbol (1-indexed)"`
	NewName string `json:"new_name" jsonschema:"New name for the symbol"`
}

RenameSymbolArgs is the input schema for the rename_symbol tool.

type ReplaceBodyArgs

type ReplaceBodyArgs struct {
	Path       string `json:"path" jsonschema:"File path"`
	SymbolName string `json:"symbol_name" jsonschema:"Name of the symbol whose body to replace"`
	NewBody    string `json:"new_body" jsonschema:"New body content to replace with"`
	SearchBody string `` /* 153-byte string literal not displayed */
}

ReplaceBodyArgs is the input schema for the replace_symbol_body tool.

type RustAnalyzerRenameOverride

type RustAnalyzerRenameOverride struct {
	Inner *lspool.RustAnalyzerAdapter
}

RustAnalyzerRenameOverride wraps a *lspool.RustAnalyzerAdapter with a RenameOverrider implementation. Lives in package edit so the method can return *RenameResult without forcing lspool -> edit import (RESEARCH Pitfall 1 resolution). The dispatcher in rename.go constructs this wrapper inline after type-asserting the adapter for *lspool.RustAnalyzerAdapter.

Semantic-accuracy note (D-05, BUG-DEFER-02): the references-driven fallback does NOT match native rust-analyzer rename fidelity on cross-crate trait-impl discovery, macro-expansion sites, or re-exports that rename at the re-export site. See USAGE.md Troubleshooting.

func (*RustAnalyzerRenameOverride) RenameOverride

func (w *RustAnalyzerRenameOverride) RenameOverride(
	ctx context.Context,
	lease *lspool.WorkerLease,
	uri string,
	line, col int,
	newName string,
) (*RenameResult, error)

RenameOverride satisfies the RenameOverrider interface. Best-effort waits for rust-analyzer quiescence (readiness is an optimization; the references-driven fallback works even when the server is not yet quiescent because textDocument/references succeeds at the failing position per RCA §1).

type SafeDeleteArgs

type SafeDeleteArgs struct {
	Path       string `json:"path" jsonschema:"File path"`
	SymbolName string `json:"symbol_name" jsonschema:"Name of the symbol to delete"`
	Force      bool   `json:"force,omitempty" jsonschema:"Delete even if references exist (default: false)"`
}

SafeDeleteArgs is the input schema for the safe_delete_symbol tool.

type SymbolEditingSkill

type SymbolEditingSkill struct{}

SymbolEditingSkill is a ToolProvider adapter that exposes the 6 symbol editing tools through the skill interface for daemon discovery.

func (*SymbolEditingSkill) Description

func (s *SymbolEditingSkill) Description() string

Description returns a human-readable description of this skill.

func (*SymbolEditingSkill) Init

func (s *SymbolEditingSkill) Init(deps skill.SkillDeps) error

Init is a no-op; kernel tools get their dependencies from the daemon, not SkillDeps.

func (*SymbolEditingSkill) Name

func (s *SymbolEditingSkill) Name() string

Name returns the unique skill identifier.

func (*SymbolEditingSkill) Tools

func (s *SymbolEditingSkill) Tools() []*mcp.ToolDef

Tools returns the 6 ToolDefs for symbol editing. RegisterFn is nil because the daemon owns MCP registration (D-01).

type VerifyEditArgs

type VerifyEditArgs struct {
	Path string `json:"path" jsonschema:"File path to verify after editing"`
}

VerifyEditArgs is the input schema for the verify_edit tool.

type VerifyResult

type VerifyResult struct {
	HasErrors  bool
	ErrorCount int
	Errors     []DiagnosticSummary
}

VerifyResult holds the outcome of post-edit diagnostic verification.

func VerifyEdit

func VerifyEdit(ctx context.Context, diagStore *diag.DiagnosticStore, uri string) (*VerifyResult, error)

VerifyEdit checks for compilation errors after an edit. EDT-06: Post-edit diagnostic verification via WaitForDiagnostics.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL