mirror of
https://github.com/charmbracelet/crush.git
synced 2025-08-02 05:20:46 +03:00
Merge remote-tracking branch 'origin/main' into list
This commit is contained in:
@@ -110,6 +110,7 @@ homebrew_casks:
|
||||
- repository:
|
||||
owner: charmbracelet
|
||||
name: homebrew-tap
|
||||
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
|
||||
|
||||
npms:
|
||||
- name: "@charmland/crush"
|
||||
@@ -134,6 +135,32 @@ nfpms:
|
||||
- src: ./manpages/crush.1.gz
|
||||
dst: /usr/share/man/man1/crush.1.gz
|
||||
|
||||
nix:
|
||||
- repository:
|
||||
owner: "charmbracelet"
|
||||
name: nur
|
||||
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
|
||||
extra_install: |-
|
||||
installManPage ./manpages/crush.1.gz.
|
||||
installShellCompletion ./completions/*
|
||||
|
||||
winget:
|
||||
- publisher: charmbracelet
|
||||
copyright: Charmbracelet, Inc
|
||||
repository:
|
||||
owner: "charmbracelet"
|
||||
name: winget-pkgs
|
||||
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
|
||||
branch: "crush-{{.Version}}"
|
||||
pull_request:
|
||||
enabled: true
|
||||
draft: false
|
||||
check_boxes: true
|
||||
base:
|
||||
owner: microsoft
|
||||
name: winget-pkgs
|
||||
branch: master
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
disable: "{{ .IsNightly }}"
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/charmbracelet/crush/internal/log"
|
||||
)
|
||||
|
||||
const catwalkURL = "https://catwalk.charm.sh"
|
||||
const defaultCatwalkURL = "https://catwalk.charm.sh"
|
||||
|
||||
// LoadReader config via io.Reader.
|
||||
func LoadReader(fd io.Reader) (*Config, error) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
@@ -74,6 +75,7 @@ func loadProvidersFromCache(path string) ([]catwalk.Provider, error) {
|
||||
}
|
||||
|
||||
func Providers() ([]catwalk.Provider, error) {
|
||||
catwalkURL := cmp.Or(os.Getenv("CATWALK_URL"), defaultCatwalkURL)
|
||||
client := catwalk.NewWithURL(catwalkURL)
|
||||
path := providerCacheFileData()
|
||||
return loadProvidersOnce(client, path)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -118,7 +119,13 @@ func waitForLspDiagnostics(ctx context.Context, filePath string, lsps map[string
|
||||
return
|
||||
}
|
||||
|
||||
if diagParams.URI.Path() == filePath || hasDiagnosticsChanged(client.GetDiagnostics(), originalDiags) {
|
||||
path, err := diagParams.URI.Path()
|
||||
if err != nil {
|
||||
slog.Error("Failed to convert diagnostic URI to path", "uri", diagParams.URI, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if path == filePath || hasDiagnosticsChanged(client.GetDiagnostics(), originalDiags) {
|
||||
select {
|
||||
case diagChan <- struct{}{}:
|
||||
default:
|
||||
@@ -216,10 +223,15 @@ func getDiagnostics(filePath string, lsps map[string]*lsp.Client) string {
|
||||
diagnostics := client.GetDiagnostics()
|
||||
if len(diagnostics) > 0 {
|
||||
for location, diags := range diagnostics {
|
||||
isCurrentFile := location.Path() == filePath
|
||||
path, err := location.Path()
|
||||
if err != nil {
|
||||
slog.Error("Failed to convert diagnostic location URI to path", "uri", location, "error", err)
|
||||
continue
|
||||
}
|
||||
isCurrentFile := path == filePath
|
||||
|
||||
for _, diag := range diags {
|
||||
formattedDiag := formatDiagnostic(location.Path(), diag, lspName)
|
||||
formattedDiag := formatDiagnostic(path, diag, lspName)
|
||||
|
||||
if isCurrentFile {
|
||||
fileDiagnostics = append(fileDiagnostics, formattedDiag)
|
||||
|
||||
@@ -449,7 +449,12 @@ func (c *Client) pingTypeScriptServer(ctx context.Context) error {
|
||||
|
||||
// If we have any open files, try to get document symbols for one
|
||||
for uri := range c.openFiles {
|
||||
filePath := protocol.DocumentURI(uri).Path()
|
||||
filePath, err := protocol.DocumentURI(uri).Path()
|
||||
if err != nil {
|
||||
slog.Error("Failed to convert URI to path for TypeScript symbol collection", "uri", uri, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(filePath, ".ts") || strings.HasSuffix(filePath, ".js") ||
|
||||
strings.HasSuffix(filePath, ".tsx") || strings.HasSuffix(filePath, ".jsx") {
|
||||
var symbols []protocol.DocumentSymbol
|
||||
@@ -712,7 +717,11 @@ func (c *Client) CloseAllFiles(ctx context.Context) {
|
||||
// First collect all URIs that need to be closed
|
||||
for uri := range c.openFiles {
|
||||
// Convert URI back to file path using proper URI handling
|
||||
filePath := protocol.DocumentURI(uri).Path()
|
||||
filePath, err := protocol.DocumentURI(uri).Path()
|
||||
if err != nil {
|
||||
slog.Error("Failed to convert URI to path for file closing", "uri", uri, "error", err)
|
||||
continue
|
||||
}
|
||||
filesToClose = append(filesToClose, filePath)
|
||||
}
|
||||
c.openFilesMu.Unlock()
|
||||
|
||||
@@ -2,6 +2,7 @@ package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// PatternInfo is an interface for types that represent glob patterns
|
||||
@@ -36,21 +37,36 @@ func (g *GlobPattern) AsPattern() (PatternInfo, error) {
|
||||
return nil, fmt.Errorf("nil pattern")
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
switch v := g.Value.(type) {
|
||||
case string:
|
||||
return StringPattern{Pattern: v}, nil
|
||||
|
||||
case RelativePattern:
|
||||
// Handle BaseURI which could be string or DocumentUri
|
||||
basePath := ""
|
||||
switch baseURI := v.BaseURI.Value.(type) {
|
||||
case string:
|
||||
basePath = DocumentURI(baseURI).Path()
|
||||
basePath, err = DocumentURI(baseURI).Path()
|
||||
if err != nil {
|
||||
slog.Error("Failed to convert URI to path", "uri", baseURI, "error", err)
|
||||
return nil, fmt.Errorf("invalid URI: %s", baseURI)
|
||||
}
|
||||
|
||||
case DocumentURI:
|
||||
basePath = baseURI.Path()
|
||||
basePath, err = baseURI.Path()
|
||||
if err != nil {
|
||||
slog.Error("Failed to convert DocumentURI to path", "uri", baseURI, "error", err)
|
||||
return nil, fmt.Errorf("invalid DocumentURI: %s", baseURI)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown BaseURI type: %T", v.BaseURI.Value)
|
||||
}
|
||||
|
||||
return RelativePatternInfo{RP: v, BasePath: basePath}, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown pattern type: %T", g.Value)
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ func (uri *DocumentURI) UnmarshalText(data []byte) (err error) {
|
||||
// DocumentUri("").Path() returns the empty string.
|
||||
//
|
||||
// Path panics if called on a URI that is not a valid filename.
|
||||
func (uri DocumentURI) Path() string {
|
||||
func (uri DocumentURI) Path() (string, error) {
|
||||
filename, err := filename(uri)
|
||||
if err != nil {
|
||||
// e.g. ParseRequestURI failed.
|
||||
@@ -79,22 +79,33 @@ func (uri DocumentURI) Path() string {
|
||||
// direct string manipulation; all DocumentUris
|
||||
// received from the client pass through
|
||||
// ParseRequestURI, which ensures validity.
|
||||
panic(err)
|
||||
return "", fmt.Errorf("invalid URI %q: %w", uri, err)
|
||||
}
|
||||
return filepath.FromSlash(filename)
|
||||
return filepath.FromSlash(filename), nil
|
||||
}
|
||||
|
||||
// Dir returns the URI for the directory containing the receiver.
|
||||
func (uri DocumentURI) Dir() DocumentURI {
|
||||
func (uri DocumentURI) Dir() (DocumentURI, error) {
|
||||
// XXX: Legacy comment:
|
||||
// This function could be more efficiently implemented by avoiding any call
|
||||
// to Path(), but at least consolidates URI manipulation.
|
||||
return URIFromPath(uri.DirPath())
|
||||
|
||||
path, err := uri.DirPath()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid URI %q: %w", uri, err)
|
||||
}
|
||||
|
||||
return URIFromPath(path), nil
|
||||
}
|
||||
|
||||
// DirPath returns the file path to the directory containing this URI, which
|
||||
// must be a file URI.
|
||||
func (uri DocumentURI) DirPath() string {
|
||||
return filepath.Dir(uri.Path())
|
||||
func (uri DocumentURI) DirPath() (string, error) {
|
||||
path, err := uri.Path()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Dir(path), nil
|
||||
}
|
||||
|
||||
func filename(uri DocumentURI) (string, error) {
|
||||
|
||||
@@ -11,7 +11,10 @@ import (
|
||||
)
|
||||
|
||||
func applyTextEdits(uri protocol.DocumentURI, edits []protocol.TextEdit) error {
|
||||
path := uri.Path()
|
||||
path, err := uri.Path()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid URI: %w", err)
|
||||
}
|
||||
|
||||
// Read the file content
|
||||
content, err := os.ReadFile(path)
|
||||
@@ -148,7 +151,11 @@ func applyTextEdit(lines []string, edit protocol.TextEdit) ([]string, error) {
|
||||
// applyDocumentChange applies a DocumentChange (create/rename/delete operations)
|
||||
func applyDocumentChange(change protocol.DocumentChange) error {
|
||||
if change.CreateFile != nil {
|
||||
path := change.CreateFile.URI.Path()
|
||||
path, err := change.CreateFile.URI.Path()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid URI: %w", err)
|
||||
}
|
||||
|
||||
if change.CreateFile.Options != nil {
|
||||
if change.CreateFile.Options.Overwrite {
|
||||
// Proceed with overwrite
|
||||
@@ -164,7 +171,11 @@ func applyDocumentChange(change protocol.DocumentChange) error {
|
||||
}
|
||||
|
||||
if change.DeleteFile != nil {
|
||||
path := change.DeleteFile.URI.Path()
|
||||
path, err := change.DeleteFile.URI.Path()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid URI: %w", err)
|
||||
}
|
||||
|
||||
if change.DeleteFile.Options != nil && change.DeleteFile.Options.Recursive {
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
return fmt.Errorf("failed to delete directory recursively: %w", err)
|
||||
@@ -177,8 +188,19 @@ func applyDocumentChange(change protocol.DocumentChange) error {
|
||||
}
|
||||
|
||||
if change.RenameFile != nil {
|
||||
oldPath := change.RenameFile.OldURI.Path()
|
||||
newPath := change.RenameFile.NewURI.Path()
|
||||
var newPath, oldPath string
|
||||
var err error
|
||||
|
||||
oldPath, err = change.RenameFile.OldURI.Path()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newPath, err = change.RenameFile.NewURI.Path()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if change.RenameFile.Options != nil {
|
||||
if !change.RenameFile.Options.Overwrite {
|
||||
if _, err := os.Stat(newPath); err == nil {
|
||||
|
||||
@@ -617,7 +617,11 @@ func (w *WorkspaceWatcher) matchesPattern(path string, pattern protocol.GlobPatt
|
||||
return false
|
||||
}
|
||||
// For relative patterns
|
||||
basePath = protocol.DocumentURI(basePath).Path()
|
||||
if basePath, err = protocol.DocumentURI(basePath).Path(); err != nil {
|
||||
// XXX: Do we want to return here, or send the error up the stack?
|
||||
slog.Error("Error converting base path to URI", "basePath", basePath, "error", err)
|
||||
}
|
||||
|
||||
basePath = filepath.ToSlash(basePath)
|
||||
|
||||
// Make path relative to basePath for matching
|
||||
@@ -660,7 +664,13 @@ func (w *WorkspaceWatcher) debounceHandleFileEvent(ctx context.Context, uri stri
|
||||
// handleFileEvent sends file change notifications
|
||||
func (w *WorkspaceWatcher) handleFileEvent(ctx context.Context, uri string, changeType protocol.FileChangeType) {
|
||||
// If the file is open and it's a change event, use didChange notification
|
||||
filePath := protocol.DocumentURI(uri).Path()
|
||||
filePath, err := protocol.DocumentURI(uri).Path()
|
||||
if err != nil {
|
||||
// XXX: Do we want to return here, or send the error up the stack?
|
||||
slog.Error("Error converting URI to path", "uri", uri, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if changeType == protocol.FileChangeType(protocol.Deleted) {
|
||||
w.client.ClearDiagnosticsForURI(protocol.DocumentURI(uri))
|
||||
} else if changeType == protocol.FileChangeType(protocol.Changed) && w.client.IsFileOpen(filePath) {
|
||||
|
||||
@@ -843,7 +843,7 @@ func (p *chatPage) Help() help.KeyMap {
|
||||
),
|
||||
key.NewBinding(
|
||||
key.WithKeys("g", "home"),
|
||||
key.WithHelp("g", "hone"),
|
||||
key.WithHelp("g", "home"),
|
||||
),
|
||||
key.NewBinding(
|
||||
key.WithKeys("G", "end"),
|
||||
|
||||
Reference in New Issue
Block a user