pickls_config/
config.rs

1use serde::Deserialize;
2use std::collections::HashMap;
3
4const DEFAULT_CTAGS_TIMEOUT_MS: u64 = 500;
5
6#[derive(Clone, Debug, Deserialize, Default)]
7pub struct PicklsConfig {
8    #[serde(default)]
9    pub languages: HashMap<String, PicklsLanguageConfig>,
10    pub symbols: Option<PicklsSymbolsConfig>,
11    #[serde(default)]
12    pub ai: PicklsAIConfig,
13}
14
15fn default_ctags_timeout_ms() -> u64 {
16    DEFAULT_CTAGS_TIMEOUT_MS
17}
18
19#[derive(Eq, PartialEq, Clone, Debug, Deserialize)]
20pub struct PicklsSymbolsConfig {
21    pub source: PicklsSymbolsSource,
22
23    /// How long to wait for ctags to complete before timing out. Defaults to 500ms.
24    #[serde(default = "default_ctags_timeout_ms")]
25    pub ctags_timeout_ms: u64,
26}
27
28#[derive(Eq, PartialEq, Clone, Debug, Deserialize)]
29pub enum PicklsSymbolsSource {
30    #[serde(rename = "universal-ctags")]
31    UniversalCtags,
32}
33
34#[derive(Clone, Debug, Default, Deserialize)]
35pub struct PicklsLanguageConfig {
36    /// A list of pathnames that indicate the root directory in relation to a file
37    /// being processed. pickls will use the first directory containing one of
38    /// these files as the root directory. The associated linter or formatter
39    /// will be run with its working directory set to this directory. (ie: pyproject.toml,
40    /// setup.py, Cargo.toml, go.mod, Makefile, etc...)
41    #[serde(default)]
42    pub root_markers: Vec<String>,
43
44    /// All the linters you'd like to run on this language. Each linter runs in
45    /// a subprocess group.
46    #[serde(default)]
47    pub linters: Vec<PicklsLinterConfig>,
48
49    /// All the formatters you'd like to run (in order) on this language. Note
50    /// that you'll need to configure your editor to invoke its LSP client to
51    /// cause formatting to occur. Successive formatters that set use_stdin will
52    /// have chained pipes from stdout to stdin to eliminate extra copies.
53    #[serde(default)]
54    pub formatters: Vec<PicklsFormatterConfig>,
55}
56
57#[derive(Clone, Debug, Deserialize)]
58pub struct PicklsLinterConfig {
59    /// If `program` is not an absolute path, the `PATH` will be searched in an OS-defined way.
60    pub program: String,
61    /// Arguments to pass to `program`. Use "$filename" wherever the absolute path to the real filename should go.
62    /// Use "$tmpfilename" where Pickls should inject a temp file (if the linter only accepts file
63    /// input).
64    #[serde(default = "Vec::new")]
65    pub args: Vec<String>,
66    /// Whether to use stdin to push the contents of the file to `program` or to rely on the usage
67    /// of "$filename" arg.
68    pub use_stdin: bool,
69    /// Regex from which to pull diagnostics from stdout of `program`. The pattern is matched on
70    /// every line of output. When there is a match, a diagnostic is produced.
71    pub pattern: String,
72    /// Regex group (1-indexed) that matches the filename of the diagnostic.
73    pub filename_match: Option<usize>,
74    /// Regex group (1-indexed) that matches the line number of the diagnostic.
75    pub line_match: usize,
76    /// Regex group (1-indexed) that matches the starting column number of the diagnostic. (Optional)
77    pub start_col_match: Option<usize>,
78    /// Regex group (1-indexed) that matches the ending column number of the diagnostic. (Optional)
79    pub end_col_match: Option<usize>,
80    /// Regex group (1-indexed) that matches the severity of the alert. Unknown severities will
81    /// resolve to warnings.
82    pub severity_match: Option<usize>,
83    /// Regex group (1-indexed) that matches the line number of the diagnostic. Use -1 to indicate
84    /// that the description is on the _previous_ line of input.
85    pub description_match: Option<isize>,
86    /// Whether to scan stderr instead of stdout. Defaults to false. Setting to true will ignore
87    /// stdout.
88    #[serde(default = "default_false")]
89    pub use_stderr: bool,
90}
91
92fn default_false() -> bool {
93    false
94}
95
96fn default_true() -> bool {
97    true
98}
99
100#[derive(Clone, Debug, Deserialize)]
101pub struct PicklsFormatterConfig {
102    /// If `program` is not an absolute path, the `PATH` will be searched in an OS-defined way.
103    pub program: String,
104    /// Arguments to pass to `program`. Use "$abspath" wherever the absolute path to the filename should go.
105    pub args: Vec<String>,
106    /// Whether to use stdin to push the contents of the file to `program` or to rely on the usage
107    /// of "$filename" arg. Defaults to true.
108    #[serde(default = "default_true")]
109    pub use_stdin: bool,
110    /// If `stderr_indicates_error` is true, then if the formatter writes anything to stderr, the
111    /// format run will be considered a failure and aborted. Defaults to false.
112    #[serde(default = "default_false")]
113    pub stderr_indicates_error: bool,
114}
115
116#[derive(Clone, Debug, Deserialize, Default)]
117pub struct PicklsAIConfig {
118    #[serde(default = "default_inline_assist_system_prompt")]
119    pub system_prompt: String,
120    pub inline_assist_provider: PicklsAIProvider,
121    #[serde(default = "default_inline_assist_prompt_template")]
122    pub inline_assist_prompt_template: String,
123    pub openai: Option<OpenAIConfig>,
124    pub ollama: Option<OllamaConfig>,
125}
126
127/// Ollama is a AI model driver that can be run locally.
128/// See https://ollama.com/ for more information on getting it set up locally.
129///
130/// API docs are [here](https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-completion).
131/// curl http://localhost:11434/api/generate -d '{
132///   "model": "llama3.2",
133///   "prompt": "Why is the sky blue?",
134///   "system": "You are a good robot."
135/// }'
136#[derive(Clone, Debug, Deserialize, Default)]
137pub struct OllamaConfig {
138    pub model: String,
139    /// Defaults to http://localhost:11434/api/generate.
140    pub api_address: String,
141}
142
143#[derive(Clone, Debug, Deserialize, Default)]
144#[serde(rename_all = "lowercase")]
145pub enum PicklsAIProvider {
146    #[default]
147    OpenAI,
148    Ollama,
149}
150
151#[derive(Clone, Debug, Deserialize)]
152pub struct OpenAIConfig {
153    /// The OpenAI model to use, (ie: "gpt-4o")
154    pub model: String,
155    /// The command to run to print the OpenAPI key. (If None, will look at $OPENAI_API_KEY)
156    #[serde(default = "default_openai_api_key_cmd")]
157    pub api_key_cmd: Vec<String>,
158}
159
160impl Default for OpenAIConfig {
161    fn default() -> Self {
162        OpenAIConfig {
163            model: "gpt-4o".to_string(),
164            api_key_cmd: default_openai_api_key_cmd(),
165        }
166    }
167}
168
169fn default_openai_api_key_cmd() -> Vec<String> {
170    ["sh", "-c", "echo $OPENAI_API_KEY"]
171        .into_iter()
172        .map(|s| s.to_string())
173        .collect()
174}
175
176fn default_inline_assist_prompt_template() -> String {
177    "I'm working within the {{language_id}} language. If I show you code below, then please \
178        rewrite it to make improvements as you see fit. If I show you a question or directive, \
179        write code to satisfy the question or directive. Never use markdown to format your response. \
180        For example, do not use triple backticks (```). Always include type annotations where possible.\n\n\
181        {{text}}\n"
182        .to_string()
183}
184
185fn default_inline_assist_system_prompt() -> String {
186    "You are an inline assistant for a code editor. Your response to user prompts will be used \
187        to replace code in the editor."
188        .to_string()
189}