mirror of
https://github.com/sst/opencode.git
synced 2025-08-04 13:30:52 +00:00
feat(tui): move logging to server logs
This commit is contained in:
parent
37a86439c4
commit
ca8ce88354
19 changed files with 588 additions and 208 deletions
|
@ -11,6 +11,19 @@ import { Log } from "../util/log"
|
|||
export namespace File {
|
||||
const log = Log.create({ service: "file" })
|
||||
|
||||
export const Info = z
|
||||
.object({
|
||||
path: z.string(),
|
||||
added: z.number().int(),
|
||||
removed: z.number().int(),
|
||||
status: z.enum(["added", "deleted", "modified"]),
|
||||
})
|
||||
.openapi({
|
||||
ref: "File",
|
||||
})
|
||||
|
||||
export type Info = z.infer<typeof Info>
|
||||
|
||||
export const Event = {
|
||||
Edited: Bus.event(
|
||||
"file.edited",
|
||||
|
@ -26,14 +39,14 @@ export namespace File {
|
|||
|
||||
const diffOutput = await $`git diff --numstat HEAD`.cwd(app.path.cwd).quiet().nothrow().text()
|
||||
|
||||
const changedFiles = []
|
||||
const changedFiles: Info[] = []
|
||||
|
||||
if (diffOutput.trim()) {
|
||||
const lines = diffOutput.trim().split("\n")
|
||||
for (const line of lines) {
|
||||
const [added, removed, filepath] = line.split("\t")
|
||||
changedFiles.push({
|
||||
file: filepath,
|
||||
path: filepath,
|
||||
added: added === "-" ? 0 : parseInt(added, 10),
|
||||
removed: removed === "-" ? 0 : parseInt(removed, 10),
|
||||
status: "modified",
|
||||
|
@ -50,7 +63,7 @@ export namespace File {
|
|||
const content = await Bun.file(path.join(app.path.root, filepath)).text()
|
||||
const lines = content.split("\n").length
|
||||
changedFiles.push({
|
||||
file: filepath,
|
||||
path: filepath,
|
||||
added: lines,
|
||||
removed: 0,
|
||||
status: "added",
|
||||
|
@ -68,7 +81,7 @@ export namespace File {
|
|||
const deletedFiles = deletedOutput.trim().split("\n")
|
||||
for (const filepath of deletedFiles) {
|
||||
changedFiles.push({
|
||||
file: filepath,
|
||||
path: filepath,
|
||||
added: 0,
|
||||
removed: 0, // Could get original line count but would require another git command
|
||||
status: "deleted",
|
||||
|
@ -78,7 +91,7 @@ export namespace File {
|
|||
|
||||
return changedFiles.map((x) => ({
|
||||
...x,
|
||||
file: path.relative(app.path.cwd, path.join(app.path.root, x.file)),
|
||||
path: path.relative(app.path.cwd, path.join(app.path.root, x.path)),
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
@ -34,25 +34,27 @@ export namespace Ripgrep {
|
|||
|
||||
export const Match = z.object({
|
||||
type: z.literal("match"),
|
||||
data: z.object({
|
||||
path: z.object({
|
||||
text: z.string(),
|
||||
}),
|
||||
lines: z.object({
|
||||
text: z.string(),
|
||||
}),
|
||||
line_number: z.number(),
|
||||
absolute_offset: z.number(),
|
||||
submatches: z.array(
|
||||
z.object({
|
||||
match: z.object({
|
||||
text: z.string(),
|
||||
}),
|
||||
start: z.number(),
|
||||
end: z.number(),
|
||||
data: z
|
||||
.object({
|
||||
path: z.object({
|
||||
text: z.string(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
lines: z.object({
|
||||
text: z.string(),
|
||||
}),
|
||||
line_number: z.number(),
|
||||
absolute_offset: z.number(),
|
||||
submatches: z.array(
|
||||
z.object({
|
||||
match: z.object({
|
||||
text: z.string(),
|
||||
}),
|
||||
start: z.number(),
|
||||
end: z.number(),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.openapi({ ref: "Match" }),
|
||||
})
|
||||
|
||||
const End = z.object({
|
||||
|
|
|
@ -28,7 +28,7 @@ export namespace LSP {
|
|||
}),
|
||||
})
|
||||
.openapi({
|
||||
ref: "LSP.Symbol",
|
||||
ref: "Symbol",
|
||||
})
|
||||
export type Symbol = z.infer<typeof Symbol>
|
||||
|
||||
|
|
|
@ -621,16 +621,7 @@ export namespace Server {
|
|||
description: "File status",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(
|
||||
z
|
||||
.object({
|
||||
file: z.string(),
|
||||
added: z.number().int(),
|
||||
removed: z.number().int(),
|
||||
status: z.enum(["added", "deleted", "modified"]),
|
||||
})
|
||||
.array(),
|
||||
),
|
||||
schema: resolver(File.Info.array()),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -641,6 +632,52 @@ export namespace Server {
|
|||
return c.json(content)
|
||||
},
|
||||
)
|
||||
.post(
|
||||
"/log",
|
||||
describeRoute({
|
||||
description: "Write a log entry to the server logs",
|
||||
responses: {
|
||||
200: {
|
||||
description: "Log entry written successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(z.boolean()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
zValidator(
|
||||
"json",
|
||||
z.object({
|
||||
service: z.string().openapi({ description: "Service name for the log entry" }),
|
||||
level: z.enum(["info", "error", "warn"]).openapi({ description: "Log level" }),
|
||||
message: z.string().openapi({ description: "Log message" }),
|
||||
extra: z
|
||||
.record(z.string(), z.any())
|
||||
.optional()
|
||||
.openapi({ description: "Additional metadata for the log entry" }),
|
||||
}),
|
||||
),
|
||||
async (c) => {
|
||||
const { service, level, message, extra } = c.req.valid("json")
|
||||
const logger = Log.create({ service })
|
||||
|
||||
switch (level) {
|
||||
case "info":
|
||||
logger.info(message, extra)
|
||||
break
|
||||
case "error":
|
||||
logger.error(message, extra)
|
||||
break
|
||||
case "warn":
|
||||
logger.warn(message, extra)
|
||||
break
|
||||
}
|
||||
|
||||
return c.json(true)
|
||||
},
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -2,6 +2,23 @@ import path from "path"
|
|||
import fs from "fs/promises"
|
||||
import { Global } from "../global"
|
||||
export namespace Log {
|
||||
export type Logger = {
|
||||
info(message?: any, extra?: Record<string, any>): void
|
||||
error(message?: any, extra?: Record<string, any>): void
|
||||
warn(message?: any, extra?: Record<string, any>): void
|
||||
tag(key: string, value: string): Logger
|
||||
clone(): Logger
|
||||
time(
|
||||
message: string,
|
||||
extra?: Record<string, any>,
|
||||
): {
|
||||
stop(): void
|
||||
[Symbol.dispose](): void
|
||||
}
|
||||
}
|
||||
|
||||
const loggers = new Map<string, Logger>()
|
||||
|
||||
export const Default = create({ service: "default" })
|
||||
|
||||
export interface Options {
|
||||
|
@ -9,7 +26,6 @@ export namespace Log {
|
|||
}
|
||||
|
||||
let logpath = ""
|
||||
|
||||
export function file() {
|
||||
return logpath
|
||||
}
|
||||
|
@ -47,6 +63,14 @@ export namespace Log {
|
|||
export function create(tags?: Record<string, any>) {
|
||||
tags = tags || {}
|
||||
|
||||
const service = tags["service"]
|
||||
if (service && typeof service === "string") {
|
||||
const cached = loggers.get(service)
|
||||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
}
|
||||
|
||||
function build(message: any, extra?: Record<string, any>) {
|
||||
const prefix = Object.entries({
|
||||
...tags,
|
||||
|
@ -60,7 +84,7 @@ export namespace Log {
|
|||
last = next.getTime()
|
||||
return [next.toISOString().split(".")[0], "+" + diff + "ms", prefix, message].filter(Boolean).join(" ") + "\n"
|
||||
}
|
||||
const result = {
|
||||
const result: Logger = {
|
||||
info(message?: any, extra?: Record<string, any>) {
|
||||
process.stderr.write("INFO " + build(message, extra))
|
||||
},
|
||||
|
@ -96,6 +120,10 @@ export namespace Log {
|
|||
},
|
||||
}
|
||||
|
||||
if (service && typeof service === "string") {
|
||||
loggers.set(service, result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"encoding/json"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
|
@ -15,6 +14,7 @@ import (
|
|||
"github.com/sst/opencode/internal/app"
|
||||
"github.com/sst/opencode/internal/clipboard"
|
||||
"github.com/sst/opencode/internal/tui"
|
||||
"github.com/sst/opencode/internal/util"
|
||||
)
|
||||
|
||||
var Version = "dev"
|
||||
|
@ -39,33 +39,15 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
logfile := filepath.Join(appInfo.Path.Data, "log", "tui.log")
|
||||
if _, err := os.Stat(filepath.Dir(logfile)); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(filepath.Dir(logfile), 0755)
|
||||
if err != nil {
|
||||
slog.Error("Failed to create log directory", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
file, err := os.Create(logfile)
|
||||
if err != nil {
|
||||
slog.Error("Failed to create log file", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer file.Close()
|
||||
logger := slog.New(slog.NewTextHandler(file, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
slog.SetDefault(logger)
|
||||
|
||||
slog.Debug("TUI launched", "app", appInfo)
|
||||
|
||||
httpClient := opencode.NewClient(
|
||||
option.WithBaseURL(url),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
slog.Error("Failed to create client", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
apiHandler := util.NewAPILogHandler(httpClient, "tui", slog.LevelDebug)
|
||||
logger := slog.New(apiHandler)
|
||||
slog.SetDefault(logger)
|
||||
|
||||
slog.Debug("TUI launched", "app", appInfo)
|
||||
|
||||
go func() {
|
||||
err = clipboard.Init()
|
||||
|
|
|
@ -42,7 +42,7 @@ func (cg *filesAndFoldersContextGroup) getGitFiles() []dialog.CompletionItemI {
|
|||
})
|
||||
|
||||
for _, file := range files {
|
||||
title := file.File
|
||||
title := file.Path
|
||||
if file.Added > 0 {
|
||||
title += green(" +" + strconv.Itoa(int(file.Added)))
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ func (cg *filesAndFoldersContextGroup) getGitFiles() []dialog.CompletionItemI {
|
|||
}
|
||||
item := dialog.NewCompletionItem(dialog.CompletionItem{
|
||||
Title: title,
|
||||
Value: file.File,
|
||||
Value: file.Path,
|
||||
})
|
||||
items = append(items, item)
|
||||
}
|
||||
|
|
131
packages/tui/internal/util/apilogger.go
Normal file
131
packages/tui/internal/util/apilogger.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"sync"
|
||||
|
||||
opencode "github.com/sst/opencode-sdk-go"
|
||||
)
|
||||
|
||||
// APILogHandler is a slog.Handler that sends logs to the opencode API
|
||||
type APILogHandler struct {
|
||||
client *opencode.Client
|
||||
service string
|
||||
level slog.Level
|
||||
attrs []slog.Attr
|
||||
groups []string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewAPILogHandler creates a new APILogHandler
|
||||
func NewAPILogHandler(client *opencode.Client, service string, level slog.Level) *APILogHandler {
|
||||
return &APILogHandler{
|
||||
client: client,
|
||||
service: service,
|
||||
level: level,
|
||||
attrs: make([]slog.Attr, 0),
|
||||
groups: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Enabled reports whether the handler handles records at the given level.
|
||||
func (h *APILogHandler) Enabled(_ context.Context, level slog.Level) bool {
|
||||
return level >= h.level
|
||||
}
|
||||
|
||||
// Handle handles the Record.
|
||||
func (h *APILogHandler) Handle(ctx context.Context, r slog.Record) error {
|
||||
// Convert slog level to API level
|
||||
var apiLevel opencode.AppLogParamsLevel
|
||||
switch r.Level {
|
||||
case slog.LevelDebug, slog.LevelInfo:
|
||||
apiLevel = opencode.AppLogParamsLevelInfo
|
||||
case slog.LevelWarn:
|
||||
apiLevel = opencode.AppLogParamsLevelWarn
|
||||
case slog.LevelError:
|
||||
apiLevel = opencode.AppLogParamsLevelError
|
||||
default:
|
||||
apiLevel = opencode.AppLogParamsLevelInfo
|
||||
}
|
||||
|
||||
// Build extra fields
|
||||
extra := make(map[string]any)
|
||||
|
||||
// Add handler attributes
|
||||
h.mu.Lock()
|
||||
for _, attr := range h.attrs {
|
||||
extra[attr.Key] = attr.Value.Any()
|
||||
}
|
||||
h.mu.Unlock()
|
||||
|
||||
// Add record attributes
|
||||
r.Attrs(func(attr slog.Attr) bool {
|
||||
extra[attr.Key] = attr.Value.Any()
|
||||
return true
|
||||
})
|
||||
|
||||
// Send log to API
|
||||
params := opencode.AppLogParams{
|
||||
Service: opencode.F(h.service),
|
||||
Level: opencode.F(apiLevel),
|
||||
Message: opencode.F(r.Message),
|
||||
}
|
||||
|
||||
if len(extra) > 0 {
|
||||
params.Extra = opencode.F(extra)
|
||||
}
|
||||
|
||||
// Use a goroutine to avoid blocking the logger
|
||||
go func() {
|
||||
_, err := h.client.App.Log(context.Background(), params)
|
||||
if err != nil {
|
||||
// Fallback: we can't log the error using slog as it would create a loop
|
||||
// TODO: fallback file?
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithAttrs returns a new Handler whose attributes consist of
|
||||
// both the receiver's attributes and the arguments.
|
||||
func (h *APILogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
newHandler := &APILogHandler{
|
||||
client: h.client,
|
||||
service: h.service,
|
||||
level: h.level,
|
||||
attrs: make([]slog.Attr, len(h.attrs)+len(attrs)),
|
||||
groups: make([]string, len(h.groups)),
|
||||
}
|
||||
|
||||
copy(newHandler.attrs, h.attrs)
|
||||
copy(newHandler.attrs[len(h.attrs):], attrs)
|
||||
copy(newHandler.groups, h.groups)
|
||||
|
||||
return newHandler
|
||||
}
|
||||
|
||||
// WithGroup returns a new Handler with the given group appended to
|
||||
// the receiver's existing groups.
|
||||
func (h *APILogHandler) WithGroup(name string) slog.Handler {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
newHandler := &APILogHandler{
|
||||
client: h.client,
|
||||
service: h.service,
|
||||
level: h.level,
|
||||
attrs: make([]slog.Attr, len(h.attrs)),
|
||||
groups: make([]string, len(h.groups)+1),
|
||||
}
|
||||
|
||||
copy(newHandler.attrs, h.attrs)
|
||||
copy(newHandler.groups, h.groups)
|
||||
newHandler.groups[len(h.groups)] = name
|
||||
|
||||
return newHandler
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
configured_endpoints: 20
|
||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-c06a9b8d8284683e8350fdd3eceff0b5756877f7b67e974acd565409b67d32a0.yml
|
||||
openapi_spec_hash: 5933bca0c79177065374ac724a6bc986
|
||||
config_hash: de53ecf98e1038f2cc2fd273b582f082
|
||||
configured_endpoints: 21
|
||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-3ae2247ea9674e156e5ad818e13d8cd8622737ee1b95fdcde23ebf50963df13c.yml
|
||||
openapi_spec_hash: 3075cca003eb61c035d3eb5891a6c38c
|
||||
config_hash: a2751b16a52007a1e12967ab4aa3729f
|
||||
|
|
|
@ -24,31 +24,32 @@ Methods:
|
|||
|
||||
- <code title="get /app">client.App.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#AppService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#App">App</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="post /app/init">client.App.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#AppService.Init">Init</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="post /log">client.App.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#AppService.Log">Log</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#AppLogParams">AppLogParams</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
|
||||
# Find
|
||||
|
||||
Response Types:
|
||||
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindSymbolsResponse">FindSymbolsResponse</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindTextResponse">FindTextResponse</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Match">Match</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Symbol">Symbol</a>
|
||||
|
||||
Methods:
|
||||
|
||||
- <code title="get /find/file">client.Find.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindService.Files">Files</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindFilesParams">FindFilesParams</a>) ([]<a href="https://pkg.go.dev/builtin#string">string</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="get /find/symbol">client.Find.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindService.Symbols">Symbols</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindSymbolsParams">FindSymbolsParams</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindSymbolsResponse">FindSymbolsResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="get /find">client.Find.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindService.Text">Text</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindTextParams">FindTextParams</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindTextResponse">FindTextResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="get /find/symbol">client.Find.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindService.Symbols">Symbols</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindSymbolsParams">FindSymbolsParams</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Symbol">Symbol</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="get /find">client.Find.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindService.Text">Text</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FindTextParams">FindTextParams</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Match">Match</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
|
||||
# File
|
||||
|
||||
Response Types:
|
||||
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#File">File</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FileReadResponse">FileReadResponse</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FileStatusResponse">FileStatusResponse</a>
|
||||
|
||||
Methods:
|
||||
|
||||
- <code title="get /file">client.File.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FileService.Read">Read</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FileReadParams">FileReadParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FileReadResponse">FileReadResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="get /file/status">client.File.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FileService.Status">Status</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FileStatusResponse">FileStatusResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="get /file/status">client.File.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#FileService.Status">Status</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#File">File</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
|
||||
# Config
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/param"
|
||||
"github.com/sst/opencode-sdk-go/internal/requestconfig"
|
||||
"github.com/sst/opencode-sdk-go/option"
|
||||
)
|
||||
|
@ -46,6 +47,14 @@ func (r *AppService) Init(ctx context.Context, opts ...option.RequestOption) (re
|
|||
return
|
||||
}
|
||||
|
||||
// Write a log entry to the server logs
|
||||
func (r *AppService) Log(ctx context.Context, body AppLogParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
path := "log"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
|
||||
return
|
||||
}
|
||||
|
||||
type App struct {
|
||||
Git bool `json:"git,required"`
|
||||
Hostname string `json:"hostname,required"`
|
||||
|
@ -121,3 +130,35 @@ func (r *AppTime) UnmarshalJSON(data []byte) (err error) {
|
|||
func (r appTimeJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type AppLogParams struct {
|
||||
// Log level
|
||||
Level param.Field[AppLogParamsLevel] `json:"level,required"`
|
||||
// Log message
|
||||
Message param.Field[string] `json:"message,required"`
|
||||
// Service name for the log entry
|
||||
Service param.Field[string] `json:"service,required"`
|
||||
// Additional metadata for the log entry
|
||||
Extra param.Field[map[string]interface{}] `json:"extra"`
|
||||
}
|
||||
|
||||
func (r AppLogParams) MarshalJSON() (data []byte, err error) {
|
||||
return apijson.MarshalRoot(r)
|
||||
}
|
||||
|
||||
// Log level
|
||||
type AppLogParamsLevel string
|
||||
|
||||
const (
|
||||
AppLogParamsLevelInfo AppLogParamsLevel = "info"
|
||||
AppLogParamsLevelError AppLogParamsLevel = "error"
|
||||
AppLogParamsLevelWarn AppLogParamsLevel = "warn"
|
||||
)
|
||||
|
||||
func (r AppLogParamsLevel) IsKnown() bool {
|
||||
switch r {
|
||||
case AppLogParamsLevelInfo, AppLogParamsLevelError, AppLogParamsLevelWarn:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -56,3 +56,32 @@ func TestAppInit(t *testing.T) {
|
|||
t.Fatalf("err should be nil: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppLogWithOptionalParams(t *testing.T) {
|
||||
t.Skip("skipped: tests are disabled for the time being")
|
||||
baseURL := "http://localhost:4010"
|
||||
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
|
||||
baseURL = envURL
|
||||
}
|
||||
if !testutil.CheckTestServer(t, baseURL) {
|
||||
return
|
||||
}
|
||||
client := opencode.NewClient(
|
||||
option.WithBaseURL(baseURL),
|
||||
)
|
||||
_, err := client.App.Log(context.TODO(), opencode.AppLogParams{
|
||||
Level: opencode.F(opencode.AppLogParamsLevelInfo),
|
||||
Message: opencode.F("message"),
|
||||
Service: opencode.F("service"),
|
||||
Extra: opencode.F(map[string]interface{}{
|
||||
"foo": "bar",
|
||||
}),
|
||||
})
|
||||
if err != nil {
|
||||
var apierr *opencode.Error
|
||||
if errors.As(err, &apierr) {
|
||||
t.Log(string(apierr.DumpRequest(true)))
|
||||
}
|
||||
t.Fatalf("err should be nil: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -396,71 +396,71 @@ func (r configProviderModelsLimitJSON) RawJSON() string {
|
|||
|
||||
type Keybinds struct {
|
||||
// Exit the application
|
||||
AppExit string `json:"app_exit"`
|
||||
AppExit string `json:"app_exit,required"`
|
||||
// Show help dialog
|
||||
AppHelp string `json:"app_help"`
|
||||
AppHelp string `json:"app_help,required"`
|
||||
// Open external editor
|
||||
EditorOpen string `json:"editor_open"`
|
||||
EditorOpen string `json:"editor_open,required"`
|
||||
// Close file
|
||||
FileClose string `json:"file_close"`
|
||||
// Toggle split/unified diff
|
||||
FileDiffToggle string `json:"file_diff_toggle"`
|
||||
FileClose string `json:"file_close,required"`
|
||||
// Split/unified diff
|
||||
FileDiffToggle string `json:"file_diff_toggle,required"`
|
||||
// List files
|
||||
FileList string `json:"file_list"`
|
||||
FileList string `json:"file_list,required"`
|
||||
// Search file
|
||||
FileSearch string `json:"file_search"`
|
||||
FileSearch string `json:"file_search,required"`
|
||||
// Clear input field
|
||||
InputClear string `json:"input_clear"`
|
||||
InputClear string `json:"input_clear,required"`
|
||||
// Insert newline in input
|
||||
InputNewline string `json:"input_newline"`
|
||||
InputNewline string `json:"input_newline,required"`
|
||||
// Paste from clipboard
|
||||
InputPaste string `json:"input_paste"`
|
||||
InputPaste string `json:"input_paste,required"`
|
||||
// Submit input
|
||||
InputSubmit string `json:"input_submit"`
|
||||
InputSubmit string `json:"input_submit,required"`
|
||||
// Leader key for keybind combinations
|
||||
Leader string `json:"leader"`
|
||||
Leader string `json:"leader,required"`
|
||||
// Copy message
|
||||
MessagesCopy string `json:"messages_copy"`
|
||||
MessagesCopy string `json:"messages_copy,required"`
|
||||
// Navigate to first message
|
||||
MessagesFirst string `json:"messages_first"`
|
||||
MessagesFirst string `json:"messages_first,required"`
|
||||
// Scroll messages down by half page
|
||||
MessagesHalfPageDown string `json:"messages_half_page_down"`
|
||||
MessagesHalfPageDown string `json:"messages_half_page_down,required"`
|
||||
// Scroll messages up by half page
|
||||
MessagesHalfPageUp string `json:"messages_half_page_up"`
|
||||
MessagesHalfPageUp string `json:"messages_half_page_up,required"`
|
||||
// Navigate to last message
|
||||
MessagesLast string `json:"messages_last"`
|
||||
MessagesLast string `json:"messages_last,required"`
|
||||
// Toggle layout
|
||||
MessagesLayoutToggle string `json:"messages_layout_toggle"`
|
||||
MessagesLayoutToggle string `json:"messages_layout_toggle,required"`
|
||||
// Navigate to next message
|
||||
MessagesNext string `json:"messages_next"`
|
||||
MessagesNext string `json:"messages_next,required"`
|
||||
// Scroll messages down by one page
|
||||
MessagesPageDown string `json:"messages_page_down"`
|
||||
MessagesPageDown string `json:"messages_page_down,required"`
|
||||
// Scroll messages up by one page
|
||||
MessagesPageUp string `json:"messages_page_up"`
|
||||
MessagesPageUp string `json:"messages_page_up,required"`
|
||||
// Navigate to previous message
|
||||
MessagesPrevious string `json:"messages_previous"`
|
||||
MessagesPrevious string `json:"messages_previous,required"`
|
||||
// Revert message
|
||||
MessagesRevert string `json:"messages_revert"`
|
||||
MessagesRevert string `json:"messages_revert,required"`
|
||||
// List available models
|
||||
ModelList string `json:"model_list"`
|
||||
// Initialize project configuration
|
||||
ProjectInit string `json:"project_init"`
|
||||
// Toggle compact mode for session
|
||||
SessionCompact string `json:"session_compact"`
|
||||
ModelList string `json:"model_list,required"`
|
||||
// Create/update AGENTS.md
|
||||
ProjectInit string `json:"project_init,required"`
|
||||
// Compact the session
|
||||
SessionCompact string `json:"session_compact,required"`
|
||||
// Interrupt current session
|
||||
SessionInterrupt string `json:"session_interrupt"`
|
||||
SessionInterrupt string `json:"session_interrupt,required"`
|
||||
// List all sessions
|
||||
SessionList string `json:"session_list"`
|
||||
SessionList string `json:"session_list,required"`
|
||||
// Create a new session
|
||||
SessionNew string `json:"session_new"`
|
||||
SessionNew string `json:"session_new,required"`
|
||||
// Share current session
|
||||
SessionShare string `json:"session_share"`
|
||||
SessionShare string `json:"session_share,required"`
|
||||
// Unshare current session
|
||||
SessionUnshare string `json:"session_unshare"`
|
||||
SessionUnshare string `json:"session_unshare,required"`
|
||||
// List available themes
|
||||
ThemeList string `json:"theme_list"`
|
||||
// Show tool details
|
||||
ToolDetails string `json:"tool_details"`
|
||||
ThemeList string `json:"theme_list,required"`
|
||||
// Toggle tool details
|
||||
ToolDetails string `json:"tool_details,required"`
|
||||
JSON keybindsJSON `json:"-"`
|
||||
}
|
||||
|
||||
|
|
|
@ -916,14 +916,16 @@ func (r eventListResponseEventSessionErrorJSON) RawJSON() string {
|
|||
func (r EventListResponseEventSessionError) implementsEventListResponse() {}
|
||||
|
||||
type EventListResponseEventSessionErrorProperties struct {
|
||||
Error EventListResponseEventSessionErrorPropertiesError `json:"error"`
|
||||
JSON eventListResponseEventSessionErrorPropertiesJSON `json:"-"`
|
||||
Error EventListResponseEventSessionErrorPropertiesError `json:"error"`
|
||||
SessionID string `json:"sessionID"`
|
||||
JSON eventListResponseEventSessionErrorPropertiesJSON `json:"-"`
|
||||
}
|
||||
|
||||
// eventListResponseEventSessionErrorPropertiesJSON contains the JSON metadata for
|
||||
// the struct [EventListResponseEventSessionErrorProperties]
|
||||
type eventListResponseEventSessionErrorPropertiesJSON struct {
|
||||
Error apijson.Field
|
||||
SessionID apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
|
|
@ -42,13 +42,55 @@ func (r *FileService) Read(ctx context.Context, query FileReadParams, opts ...op
|
|||
}
|
||||
|
||||
// Get file status
|
||||
func (r *FileService) Status(ctx context.Context, opts ...option.RequestOption) (res *[]FileStatusResponse, err error) {
|
||||
func (r *FileService) Status(ctx context.Context, opts ...option.RequestOption) (res *[]File, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
path := "file/status"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...)
|
||||
return
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Added int64 `json:"added,required"`
|
||||
Path string `json:"path,required"`
|
||||
Removed int64 `json:"removed,required"`
|
||||
Status FileStatus `json:"status,required"`
|
||||
JSON fileJSON `json:"-"`
|
||||
}
|
||||
|
||||
// fileJSON contains the JSON metadata for the struct [File]
|
||||
type fileJSON struct {
|
||||
Added apijson.Field
|
||||
Path apijson.Field
|
||||
Removed apijson.Field
|
||||
Status apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *File) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r fileJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type FileStatus string
|
||||
|
||||
const (
|
||||
FileStatusAdded FileStatus = "added"
|
||||
FileStatusDeleted FileStatus = "deleted"
|
||||
FileStatusModified FileStatus = "modified"
|
||||
)
|
||||
|
||||
func (r FileStatus) IsKnown() bool {
|
||||
switch r {
|
||||
case FileStatusAdded, FileStatusDeleted, FileStatusModified:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type FileReadResponse struct {
|
||||
Content string `json:"content,required"`
|
||||
Type FileReadResponseType `json:"type,required"`
|
||||
|
@ -87,49 +129,6 @@ func (r FileReadResponseType) IsKnown() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
type FileStatusResponse struct {
|
||||
Added int64 `json:"added,required"`
|
||||
File string `json:"file,required"`
|
||||
Removed int64 `json:"removed,required"`
|
||||
Status FileStatusResponseStatus `json:"status,required"`
|
||||
JSON fileStatusResponseJSON `json:"-"`
|
||||
}
|
||||
|
||||
// fileStatusResponseJSON contains the JSON metadata for the struct
|
||||
// [FileStatusResponse]
|
||||
type fileStatusResponseJSON struct {
|
||||
Added apijson.Field
|
||||
File apijson.Field
|
||||
Removed apijson.Field
|
||||
Status apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *FileStatusResponse) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r fileStatusResponseJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type FileStatusResponseStatus string
|
||||
|
||||
const (
|
||||
FileStatusResponseStatusAdded FileStatusResponseStatus = "added"
|
||||
FileStatusResponseStatusDeleted FileStatusResponseStatus = "deleted"
|
||||
FileStatusResponseStatusModified FileStatusResponseStatus = "modified"
|
||||
)
|
||||
|
||||
func (r FileStatusResponseStatus) IsKnown() bool {
|
||||
switch r {
|
||||
case FileStatusResponseStatusAdded, FileStatusResponseStatusDeleted, FileStatusResponseStatusModified:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type FileReadParams struct {
|
||||
Path param.Field[string] `query:"path,required"`
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ func (r *FindService) Files(ctx context.Context, query FindFilesParams, opts ...
|
|||
}
|
||||
|
||||
// Find workspace symbols
|
||||
func (r *FindService) Symbols(ctx context.Context, query FindSymbolsParams, opts ...option.RequestOption) (res *[]FindSymbolsResponse, err error) {
|
||||
func (r *FindService) Symbols(ctx context.Context, query FindSymbolsParams, opts ...option.RequestOption) (res *[]Symbol, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
path := "find/symbol"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
|
@ -50,27 +50,24 @@ func (r *FindService) Symbols(ctx context.Context, query FindSymbolsParams, opts
|
|||
}
|
||||
|
||||
// Find text in files
|
||||
func (r *FindService) Text(ctx context.Context, query FindTextParams, opts ...option.RequestOption) (res *[]FindTextResponse, err error) {
|
||||
func (r *FindService) Text(ctx context.Context, query FindTextParams, opts ...option.RequestOption) (res *[]Match, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
path := "find"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
}
|
||||
|
||||
type FindSymbolsResponse = interface{}
|
||||
|
||||
type FindTextResponse struct {
|
||||
AbsoluteOffset float64 `json:"absolute_offset,required"`
|
||||
LineNumber float64 `json:"line_number,required"`
|
||||
Lines FindTextResponseLines `json:"lines,required"`
|
||||
Path FindTextResponsePath `json:"path,required"`
|
||||
Submatches []FindTextResponseSubmatch `json:"submatches,required"`
|
||||
JSON findTextResponseJSON `json:"-"`
|
||||
type Match struct {
|
||||
AbsoluteOffset float64 `json:"absolute_offset,required"`
|
||||
LineNumber float64 `json:"line_number,required"`
|
||||
Lines MatchLines `json:"lines,required"`
|
||||
Path MatchPath `json:"path,required"`
|
||||
Submatches []MatchSubmatch `json:"submatches,required"`
|
||||
JSON matchJSON `json:"-"`
|
||||
}
|
||||
|
||||
// findTextResponseJSON contains the JSON metadata for the struct
|
||||
// [FindTextResponse]
|
||||
type findTextResponseJSON struct {
|
||||
// matchJSON contains the JSON metadata for the struct [Match]
|
||||
type matchJSON struct {
|
||||
AbsoluteOffset apijson.Field
|
||||
LineNumber apijson.Field
|
||||
Lines apijson.Field
|
||||
|
@ -80,66 +77,63 @@ type findTextResponseJSON struct {
|
|||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *FindTextResponse) UnmarshalJSON(data []byte) (err error) {
|
||||
func (r *Match) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r findTextResponseJSON) RawJSON() string {
|
||||
func (r matchJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type FindTextResponseLines struct {
|
||||
Text string `json:"text,required"`
|
||||
JSON findTextResponseLinesJSON `json:"-"`
|
||||
type MatchLines struct {
|
||||
Text string `json:"text,required"`
|
||||
JSON matchLinesJSON `json:"-"`
|
||||
}
|
||||
|
||||
// findTextResponseLinesJSON contains the JSON metadata for the struct
|
||||
// [FindTextResponseLines]
|
||||
type findTextResponseLinesJSON struct {
|
||||
// matchLinesJSON contains the JSON metadata for the struct [MatchLines]
|
||||
type matchLinesJSON struct {
|
||||
Text apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *FindTextResponseLines) UnmarshalJSON(data []byte) (err error) {
|
||||
func (r *MatchLines) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r findTextResponseLinesJSON) RawJSON() string {
|
||||
func (r matchLinesJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type FindTextResponsePath struct {
|
||||
Text string `json:"text,required"`
|
||||
JSON findTextResponsePathJSON `json:"-"`
|
||||
type MatchPath struct {
|
||||
Text string `json:"text,required"`
|
||||
JSON matchPathJSON `json:"-"`
|
||||
}
|
||||
|
||||
// findTextResponsePathJSON contains the JSON metadata for the struct
|
||||
// [FindTextResponsePath]
|
||||
type findTextResponsePathJSON struct {
|
||||
// matchPathJSON contains the JSON metadata for the struct [MatchPath]
|
||||
type matchPathJSON struct {
|
||||
Text apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *FindTextResponsePath) UnmarshalJSON(data []byte) (err error) {
|
||||
func (r *MatchPath) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r findTextResponsePathJSON) RawJSON() string {
|
||||
func (r matchPathJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type FindTextResponseSubmatch struct {
|
||||
End float64 `json:"end,required"`
|
||||
Match FindTextResponseSubmatchesMatch `json:"match,required"`
|
||||
Start float64 `json:"start,required"`
|
||||
JSON findTextResponseSubmatchJSON `json:"-"`
|
||||
type MatchSubmatch struct {
|
||||
End float64 `json:"end,required"`
|
||||
Match MatchSubmatchesMatch `json:"match,required"`
|
||||
Start float64 `json:"start,required"`
|
||||
JSON matchSubmatchJSON `json:"-"`
|
||||
}
|
||||
|
||||
// findTextResponseSubmatchJSON contains the JSON metadata for the struct
|
||||
// [FindTextResponseSubmatch]
|
||||
type findTextResponseSubmatchJSON struct {
|
||||
// matchSubmatchJSON contains the JSON metadata for the struct [MatchSubmatch]
|
||||
type matchSubmatchJSON struct {
|
||||
End apijson.Field
|
||||
Match apijson.Field
|
||||
Start apijson.Field
|
||||
|
@ -147,32 +141,147 @@ type findTextResponseSubmatchJSON struct {
|
|||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *FindTextResponseSubmatch) UnmarshalJSON(data []byte) (err error) {
|
||||
func (r *MatchSubmatch) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r findTextResponseSubmatchJSON) RawJSON() string {
|
||||
func (r matchSubmatchJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type FindTextResponseSubmatchesMatch struct {
|
||||
Text string `json:"text,required"`
|
||||
JSON findTextResponseSubmatchesMatchJSON `json:"-"`
|
||||
type MatchSubmatchesMatch struct {
|
||||
Text string `json:"text,required"`
|
||||
JSON matchSubmatchesMatchJSON `json:"-"`
|
||||
}
|
||||
|
||||
// findTextResponseSubmatchesMatchJSON contains the JSON metadata for the struct
|
||||
// [FindTextResponseSubmatchesMatch]
|
||||
type findTextResponseSubmatchesMatchJSON struct {
|
||||
// matchSubmatchesMatchJSON contains the JSON metadata for the struct
|
||||
// [MatchSubmatchesMatch]
|
||||
type matchSubmatchesMatchJSON struct {
|
||||
Text apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *FindTextResponseSubmatchesMatch) UnmarshalJSON(data []byte) (err error) {
|
||||
func (r *MatchSubmatchesMatch) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r findTextResponseSubmatchesMatchJSON) RawJSON() string {
|
||||
func (r matchSubmatchesMatchJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type Symbol struct {
|
||||
Kind float64 `json:"kind,required"`
|
||||
Location SymbolLocation `json:"location,required"`
|
||||
Name string `json:"name,required"`
|
||||
JSON symbolJSON `json:"-"`
|
||||
}
|
||||
|
||||
// symbolJSON contains the JSON metadata for the struct [Symbol]
|
||||
type symbolJSON struct {
|
||||
Kind apijson.Field
|
||||
Location apijson.Field
|
||||
Name apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *Symbol) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r symbolJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type SymbolLocation struct {
|
||||
Range SymbolLocationRange `json:"range,required"`
|
||||
Uri string `json:"uri,required"`
|
||||
JSON symbolLocationJSON `json:"-"`
|
||||
}
|
||||
|
||||
// symbolLocationJSON contains the JSON metadata for the struct [SymbolLocation]
|
||||
type symbolLocationJSON struct {
|
||||
Range apijson.Field
|
||||
Uri apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *SymbolLocation) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r symbolLocationJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type SymbolLocationRange struct {
|
||||
End SymbolLocationRangeEnd `json:"end,required"`
|
||||
Start SymbolLocationRangeStart `json:"start,required"`
|
||||
JSON symbolLocationRangeJSON `json:"-"`
|
||||
}
|
||||
|
||||
// symbolLocationRangeJSON contains the JSON metadata for the struct
|
||||
// [SymbolLocationRange]
|
||||
type symbolLocationRangeJSON struct {
|
||||
End apijson.Field
|
||||
Start apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *SymbolLocationRange) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r symbolLocationRangeJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type SymbolLocationRangeEnd struct {
|
||||
Character float64 `json:"character,required"`
|
||||
Line float64 `json:"line,required"`
|
||||
JSON symbolLocationRangeEndJSON `json:"-"`
|
||||
}
|
||||
|
||||
// symbolLocationRangeEndJSON contains the JSON metadata for the struct
|
||||
// [SymbolLocationRangeEnd]
|
||||
type symbolLocationRangeEndJSON struct {
|
||||
Character apijson.Field
|
||||
Line apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *SymbolLocationRangeEnd) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r symbolLocationRangeEndJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type SymbolLocationRangeStart struct {
|
||||
Character float64 `json:"character,required"`
|
||||
Line float64 `json:"line,required"`
|
||||
JSON symbolLocationRangeStartJSON `json:"-"`
|
||||
}
|
||||
|
||||
// symbolLocationRangeStartJSON contains the JSON metadata for the struct
|
||||
// [SymbolLocationRangeStart]
|
||||
type symbolLocationRangeStartJSON struct {
|
||||
Character apijson.Field
|
||||
Line apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *SymbolLocationRangeStart) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r symbolLocationRangeStartJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ func NewDecoder(res *http.Response) Decoder {
|
|||
decoder = t(res.Body)
|
||||
} else {
|
||||
scn := bufio.NewScanner(res.Body)
|
||||
scn.Buffer(nil, (bufio.MaxScanTokenSize<<4)*10)
|
||||
scn.Buffer(nil, bufio.MaxScanTokenSize<<9)
|
||||
decoder = &eventStreamDecoder{rc: res.Body, scn: scn}
|
||||
}
|
||||
return decoder
|
||||
|
|
|
@ -7,5 +7,5 @@ cd "$(dirname "$0")/.."
|
|||
echo "==> Running Go build"
|
||||
go build .
|
||||
|
||||
# Compile the tests but don't run them
|
||||
go test -c .
|
||||
echo "==> Checking tests compile"
|
||||
go test -run=^$ .
|
||||
|
|
|
@ -51,14 +51,20 @@ resources:
|
|||
methods:
|
||||
get: get /app
|
||||
init: post /app/init
|
||||
log: post /log
|
||||
|
||||
find:
|
||||
models:
|
||||
match: Match
|
||||
symbol: Symbol
|
||||
methods:
|
||||
text: get /find
|
||||
files: get /find/file
|
||||
symbols: get /find/symbol
|
||||
|
||||
file:
|
||||
models:
|
||||
file: File
|
||||
methods:
|
||||
read: get /file
|
||||
status: get /file/status
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue