From 50c3395e318ae9872db034e277a3edcd6eabd6ce Mon Sep 17 00:00:00 2001
From: rsdy
Date: Tue, 7 Nov 2023 17:20:16 +0100
Subject: [PATCH 01/42] Respect path filters in agent queries
---
server/bleep/src/agent.rs | 42 ++++++++++++++++++++++++++++++++++-----
1 file changed, 37 insertions(+), 5 deletions(-)
diff --git a/server/bleep/src/agent.rs b/server/bleep/src/agent.rs
index d07c6304f0..fdab2f21a4 100644
--- a/server/bleep/src/agent.rs
+++ b/server/bleep/src/agent.rs
@@ -1,4 +1,4 @@
-use std::{sync::Arc, time::Duration};
+use std::{collections::HashSet, sync::Arc, time::Duration};
use anyhow::{anyhow, Context, Result};
use futures::{Future, TryStreamExt};
@@ -372,13 +372,45 @@ impl Agent {
threshold: f32,
retrieve_more: bool,
) -> Result> {
+ let paths_set = paths
+ .into_iter()
+ .map(|p| parser::Literal::Plain(p.into()))
+ .collect::>();
+
+ let paths = if paths_set.is_empty() {
+ self.last_exchange().query.paths.clone()
+ } else if self.last_exchange().query.paths.is_empty() {
+ paths_set
+ } else {
+ paths_set
+ .into_iter()
+ .zip(self.last_exchange().query.paths.clone())
+ .flat_map(|(llm, user)| {
+ if llm
+ .as_plain()
+ .unwrap()
+ .starts_with(user.as_plain().unwrap().as_ref())
+ {
+ // llm-defined is more specific than user request
+ vec![llm]
+ } else if user
+ .as_plain()
+ .unwrap()
+ .starts_with(llm.as_plain().unwrap().as_ref())
+ {
+ // user-defined is more specific than llm request
+ vec![user]
+ } else {
+ vec![llm, user]
+ }
+ })
+ .collect()
+ };
+
let query = parser::SemanticQuery {
target: Some(query),
repos: [parser::Literal::Plain(self.repo_ref.display_name().into())].into(),
- paths: paths
- .iter()
- .map(|p| parser::Literal::Plain(p.into()))
- .collect(),
+ paths,
..self.last_exchange().query.clone()
};
From b6425f5fdcf1afe47ea129b8a6d509dcb43887d2 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Tue, 7 Nov 2023 19:32:54 +0100
Subject: [PATCH 02/42] Fix autocomplete for langs
---
server/bleep/src/webserver/autocomplete.rs | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index 16561a3eb4..d2abcc21d5 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -3,7 +3,7 @@ use std::sync::Arc;
use super::prelude::*;
use crate::{
indexes::{
- reader::{ContentReader, FileReader, RepoReader},
+ reader::{ContentReader, FileReader, OpenReader, RepoReader},
Indexes,
},
query::{
@@ -41,13 +41,6 @@ pub(super) async fn handle(
);
}
- // Bypass the parser and execute a prefix search using the last whitespace-split token
- // in the query string.
- //
- // This should be revisited when we implement cursor-aware autocomplete.
- //
- // `api lang:p` -> search lang list with prefix `p`
- // `lang:p api` -> lang prefix search not triggered
if let Some(matched_langs) = complete_lang(&api_params.q) {
autocomplete_results.append(
&mut matched_langs
@@ -88,12 +81,16 @@ fn complete_flag(q: &str) -> impl Iterator- + '_ {
.copied()
}
+// Bypass the parser and execute a prefix search using the rightmost whitespace-split token
+// in the query string.
+//
+// This should be revisited when we implement cursor-aware autocomplete.
fn complete_lang(q: &str) -> Option + '_> {
- match q.split_whitespace().last() {
+ match q.split_whitespace().rfind(|comp| comp.starts_with("lang:")) {
Some(last) => last.strip_prefix("lang:").map(|prefix| {
COMMON_LANGUAGES
.iter()
- .filter(move |l| l.starts_with(prefix))
+ .filter(move |l| l.starts_with(prefix) && **l != prefix)
.copied()
}),
_ => None,
From db5ff0d973793339c0c85dd2adb37203a5d00b44 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Thu, 9 Nov 2023 17:35:14 +0100
Subject: [PATCH 03/42] Add directory indicator
---
server/bleep/src/indexes/reader.rs | 3 +++
server/bleep/src/webserver/autocomplete.rs | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/server/bleep/src/indexes/reader.rs b/server/bleep/src/indexes/reader.rs
index 90562deff3..39b34b0cbf 100644
--- a/server/bleep/src/indexes/reader.rs
+++ b/server/bleep/src/indexes/reader.rs
@@ -64,6 +64,7 @@ pub struct FileDocument {
pub lang: Option,
pub branches: String,
pub indexed: bool,
+ pub is_dir: bool,
}
pub struct RepoDocument {
@@ -206,6 +207,7 @@ impl DocumentRead for FileReader {
let lang = read_lang_field(&doc, schema.lang);
let branches = read_text_field(&doc, schema.branches);
let indexed = read_bool_field(&doc, schema.indexed);
+ let is_dir = read_bool_field(&doc, schema.is_directory);
FileDocument {
relative_path,
@@ -214,6 +216,7 @@ impl DocumentRead for FileReader {
lang,
branches,
indexed,
+ is_dir,
}
}
}
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index d2abcc21d5..6bf97333ca 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -3,7 +3,7 @@ use std::sync::Arc;
use super::prelude::*;
use crate::{
indexes::{
- reader::{ContentReader, FileReader, OpenReader, RepoReader},
+ reader::{ContentReader, FileReader, RepoReader},
Indexes,
},
query::{
From 66d9b439f1818ad28d4f205d7861cf354a4f08a9 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Thu, 9 Nov 2023 17:59:38 +0100
Subject: [PATCH 04/42] Add directory indicator to autocomplete
---
server/bleep/src/indexes/schema.rs | 2 +-
server/bleep/src/query/execute.rs | 4 ++++
server/bleep/src/webserver/search.rs | 1 +
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/server/bleep/src/indexes/schema.rs b/server/bleep/src/indexes/schema.rs
index f556fe6b35..8e37edb963 100644
--- a/server/bleep/src/indexes/schema.rs
+++ b/server/bleep/src/indexes/schema.rs
@@ -104,7 +104,7 @@ impl File {
let raw_repo_name = builder.add_bytes_field("raw_repo_name", FAST);
let raw_relative_path = builder.add_bytes_field("raw_relative_path", FAST);
- let is_directory = builder.add_bool_field("is_directory", FAST);
+ let is_directory = builder.add_bool_field("is_directory", FAST | STORED);
let indexed = builder.add_bool_field("indexed", STORED);
Self {
diff --git a/server/bleep/src/query/execute.rs b/server/bleep/src/query/execute.rs
index f8a187c6bb..3c47aef59f 100644
--- a/server/bleep/src/query/execute.rs
+++ b/server/bleep/src/query/execute.rs
@@ -154,6 +154,7 @@ pub struct FileResultData {
lang: Option,
branches: String,
indexed: bool,
+ is_dir: bool,
}
impl FileResultData {
@@ -164,6 +165,7 @@ impl FileResultData {
lang: Option,
branches: String,
indexed: bool,
+ is_dir: bool,
) -> Self {
Self {
repo_name,
@@ -172,6 +174,7 @@ impl FileResultData {
lang,
branches,
indexed,
+ is_dir,
}
}
}
@@ -502,6 +505,7 @@ impl ExecuteQuery for FileReader {
lang: f.lang,
branches: f.branches,
indexed: f.indexed,
+ is_dir: f.is_dir,
})
})
.collect::>();
diff --git a/server/bleep/src/webserver/search.rs b/server/bleep/src/webserver/search.rs
index ce143e15dd..d49a2b37c8 100644
--- a/server/bleep/src/webserver/search.rs
+++ b/server/bleep/src/webserver/search.rs
@@ -63,6 +63,7 @@ pub(super) async fn fuzzy_path(
c.lang,
c.branches,
c.indexed,
+ c.is_dir,
))
})
.collect::>();
From 2618e49ba8209e69039132634b043a448f29ef85 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Thu, 9 Nov 2023 18:20:38 +0100
Subject: [PATCH 05/42] Use central lang map instead of a separate one
---
server/bleep/src/query/languages.rs | 10 ++-
server/bleep/src/webserver/autocomplete.rs | 81 ++--------------------
2 files changed, 13 insertions(+), 78 deletions(-)
diff --git a/server/bleep/src/query/languages.rs b/server/bleep/src/query/languages.rs
index 0111c374a9..74f9e3ed36 100644
--- a/server/bleep/src/query/languages.rs
+++ b/server/bleep/src/query/languages.rs
@@ -1,4 +1,4 @@
-use std::borrow::Cow;
+use std::{borrow::Cow, collections::HashSet};
include!(concat!(env!("OUT_DIR"), "/languages.rs"));
@@ -18,6 +18,14 @@ pub fn proper_case(lower: Cow) -> Cow {
}
}
+pub fn list() -> impl Iterator- {
+ EXT_MAP
+ .entries()
+ .flat_map(|e| [*e.0, *e.1])
+ .collect::>()
+ .into_iter()
+}
+
#[cfg(test)]
mod test {
use super::*;
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index 6bf97333ca..80bb96520f 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -85,14 +85,11 @@ fn complete_flag(q: &str) -> impl Iterator
- + '_ {
// in the query string.
//
// This should be revisited when we implement cursor-aware autocomplete.
-fn complete_lang(q: &str) -> Option + '_> {
+fn complete_lang(q: &str) -> Option + '_> {
match q.split_whitespace().rfind(|comp| comp.starts_with("lang:")) {
- Some(last) => last.strip_prefix("lang:").map(|prefix| {
- COMMON_LANGUAGES
- .iter()
- .filter(move |l| l.starts_with(prefix) && **l != prefix)
- .copied()
- }),
+ Some(last) => last
+ .strip_prefix("lang:")
+ .map(|prefix| crate::query::languages::list().filter(move |l| l.starts_with(prefix))),
_ => None,
}
}
@@ -108,73 +105,3 @@ impl super::ApiResponse for AutocompleteResponse {}
const QUERY_FLAGS: &[&str; 8] = &[
"repo", "path", "content", "symbol", "lang", "case", "or", "open",
];
-
-// List of common languages
-const COMMON_LANGUAGES: &[&str] = &[
- "webassembly",
- "basic",
- "makefile",
- "groovy",
- "haskell",
- "idris",
- "typescript",
- "r",
- "javascript",
- "llvm",
- "jsonnet",
- "lua",
- "awk",
- "solidity",
- "nim",
- "hcl",
- "julia",
- "ada",
- "verilog",
- "python",
- "go",
- "sql",
- "plsql",
- "fortran",
- "erlang",
- "mathematica",
- "rust",
- "coffeescript",
- "zig",
- "scala",
- "tsx",
- "ruby",
- "apl",
- "c",
- "tcl",
- "kotlin",
- "vba",
- "matlab",
- "hack",
- "ocaml",
- "prolog",
- "scheme",
- "dockerfile",
- "assembly",
- "clojure",
- "shell",
- "java",
- "c++",
- "php",
- "perl",
- "vbscript",
- "d",
- "pascal",
- "elm",
- "swift",
- "cuda",
- "dart",
- "elixir",
- "c#",
- "objective-c",
- "coq",
- "forth",
- "cmake",
- "nix",
- "objective-c++",
- "actionscript",
-];
From b1c0ee2d5c4c78e392b3b90b26d780e5acd5e88e Mon Sep 17 00:00:00 2001
From: rsdy
Date: Fri, 10 Nov 2023 10:34:49 +0100
Subject: [PATCH 06/42] Simplify this logic
---
server/bleep/src/agent/exchange.rs | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/server/bleep/src/agent/exchange.rs b/server/bleep/src/agent/exchange.rs
index f8a4de3bf8..89a050c5f5 100644
--- a/server/bleep/src/agent/exchange.rs
+++ b/server/bleep/src/agent/exchange.rs
@@ -1,5 +1,5 @@
use crate::query::parser::SemanticQuery;
-use std::{fmt, mem};
+use std::fmt;
use chrono::prelude::{DateTime, Utc};
use rand::seq::SliceRandom;
@@ -102,17 +102,16 @@ impl Exchange {
///
/// This is used to reduce the size of an exchange when we send it over the wire, by removing
/// data that the front-end does not use.
- pub fn compressed(&self) -> Self {
- let mut ex = self.clone();
-
- ex.code_chunks.clear();
- ex.paths.clear();
- ex.search_steps = mem::take(&mut ex.search_steps)
+ pub fn compressed(mut self) -> Self {
+ self.code_chunks.clear();
+ self.paths.clear();
+ self.search_steps = self
+ .search_steps
.into_iter()
.map(|step| step.compressed())
.collect();
- ex
+ self
}
}
From e99d3557cfc5081f57ab37bab40e121ae19ddc70 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Fri, 10 Nov 2023 12:45:20 +0100
Subject: [PATCH 07/42] Add position information to literals
---
server/bleep/src/query/parser.rs | 108 +++++++++++++++++++++------
server/bleep/src/webserver/answer.rs | 4 +-
2 files changed, 88 insertions(+), 24 deletions(-)
diff --git a/server/bleep/src/query/parser.rs b/server/bleep/src/query/parser.rs
index 5af87ca57c..679a72fafd 100644
--- a/server/bleep/src/query/parser.rs
+++ b/server/bleep/src/query/parser.rs
@@ -67,8 +67,8 @@ impl<'a> SemanticQuery<'a> {
pub fn from_str(query: String, repo_ref: String) -> Self {
Self {
- target: Some(Literal::Plain(Cow::Owned(query))),
- repos: [Literal::Plain(Cow::Owned(repo_ref))].into(),
+ target: Some(Literal::Plain(query.into())),
+ repos: [Literal::Plain(repo_ref.into())].into(),
..Default::default()
}
}
@@ -217,10 +217,63 @@ pub enum ParseError {
MultiMode,
}
-#[derive(Debug, PartialEq, Eq, Clone, Hash, serde::Serialize, serde::Deserialize)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum Literal<'a> {
- Plain(Cow<'a, str>),
- Regex(Cow<'a, str>),
+ Plain(LiteralInner<'a>),
+ Regex(LiteralInner<'a>),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
+pub struct LiteralInner<'a> {
+ start: usize,
+ end: usize,
+ content: Cow<'a, str>,
+}
+
+impl<'a> LiteralInner<'a> {
+ fn new(start: usize, end: usize, content: impl Into>) -> Self {
+ Self {
+ start,
+ end,
+ content: content.into(),
+ }
+ }
+
+ fn to_owned(&self) -> LiteralInner<'static> {
+ LiteralInner {
+ start: self.start,
+ end: self.end,
+ content: Cow::Owned(self.content.to_string()),
+ }
+ }
+}
+
+impl<'a, T: AsRef> From for LiteralInner<'a> {
+ fn from(value: T) -> Self {
+ Self {
+ start: 0,
+ end: 0,
+ content: value.as_ref().to_owned().into(),
+ }
+ }
+}
+
+impl<'a> std::ops::Deref for LiteralInner<'a> {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ self.content.as_ref()
+ }
+}
+
+impl<'a> Default for LiteralInner<'a> {
+ fn default() -> Self {
+ Self {
+ start: 0,
+ end: 0,
+ content: Cow::Borrowed(""),
+ }
+ }
}
impl From<&String> for Literal<'static> {
@@ -231,21 +284,23 @@ impl From<&String> for Literal<'static> {
impl<'a> Default for Literal<'a> {
fn default() -> Self {
- Self::Plain(Cow::Borrowed(""))
+ Literal::Plain(Default::default())
}
}
impl<'a> Literal<'a> {
- fn join_as_regex(self, rhs: Self) -> Self {
+ /// This drops position information, as it's not intelligible after the merge
+ fn join_as_regex(self, rhs: Self) -> Literal<'static> {
let lhs = self.regex_str();
let rhs = rhs.regex_str();
- Self::Regex(Cow::Owned(format!("{lhs}\\s+{rhs}")))
+ Literal::Regex(format!("{lhs}\\s+{rhs}").into())
}
- fn join_as_plain(self, rhs: Self) -> Option {
+ /// This drops position information, as it's not intelligible after the merge
+ fn join_as_plain(self, rhs: Self) -> Option> {
let lhs = self.as_plain()?;
let rhs = rhs.as_plain()?;
- Some(Self::Plain(Cow::Owned(format!("{lhs} {rhs}"))))
+ Some(Literal::Plain(format!("{lhs} {rhs}").into()))
}
/// Convert this literal into a regex string.
@@ -255,7 +310,7 @@ impl<'a> Literal<'a> {
pub fn regex_str(&self) -> Cow<'a, str> {
match self {
Self::Plain(text) => regex::escape(text).into(),
- Self::Regex(r) => r.clone(),
+ Self::Regex(r) => r.content.clone(),
}
}
@@ -265,7 +320,7 @@ impl<'a> Literal<'a> {
pub fn as_plain(&self) -> Option> {
match self {
- Self::Plain(p) => Some(p.clone()),
+ Self::Plain(p) => Some(p.content.clone()),
Self::Regex(..) => None,
}
}
@@ -279,27 +334,38 @@ impl<'a> Literal<'a> {
pub fn unwrap(self) -> Cow<'a, str> {
match self {
- Literal::Plain(v) => v,
- Literal::Regex(v) => v,
+ Literal::Plain(v) => v.content,
+ Literal::Regex(v) => v.content,
}
}
pub fn into_owned(self) -> Literal<'static> {
match self {
- Literal::Plain(cow) => Literal::Plain(Cow::Owned(cow.into_owned())),
- Literal::Regex(cow) => Literal::Regex(Cow::Owned(cow.into_owned())),
+ Literal::Plain(cow) => Literal::Plain(cow.to_owned()),
+ Literal::Regex(cow) => Literal::Regex(cow.to_owned()),
}
}
}
impl<'a> From> for Literal<'a> {
fn from(pair: Pair<'a, Rule>) -> Self {
+ let start = pair.as_span().start();
+ let end = pair.as_span().end();
+
match pair.as_rule() {
- Rule::unquoted_literal => Self::Plain(pair.as_str().trim().into()),
- Rule::quoted_literal => Self::Plain(unescape(pair.as_str(), '"').into()),
- Rule::single_quoted_literal => Self::Plain(unescape(pair.as_str(), '\'').into()),
- Rule::regex_quoted_literal => Self::Regex(unescape(pair.as_str(), '/').into()),
- Rule::raw_text => Self::Plain(pair.as_str().trim().into()),
+ Rule::unquoted_literal => {
+ Self::Plain(LiteralInner::new(start, end, pair.as_str().trim()))
+ }
+ Rule::quoted_literal => {
+ Self::Plain(LiteralInner::new(start, end, unescape(pair.as_str(), '"')))
+ }
+ Rule::single_quoted_literal => {
+ Self::Plain(LiteralInner::new(start, end, unescape(pair.as_str(), '\'')))
+ }
+ Rule::regex_quoted_literal => {
+ Self::Regex(LiteralInner::new(start, end, unescape(pair.as_str(), '/')))
+ }
+ Rule::raw_text => Self::Plain(LiteralInner::new(start, end, pair.as_str().trim())),
_ => unreachable!(),
}
}
diff --git a/server/bleep/src/webserver/answer.rs b/server/bleep/src/webserver/answer.rs
index 8a4ee4f65e..c58352cce7 100644
--- a/server/bleep/src/webserver/answer.rs
+++ b/server/bleep/src/webserver/answer.rs
@@ -407,9 +407,7 @@ pub async fn explain(
.into_owned();
if let Some(branch) = params.branch {
- query
- .branch
- .insert(Literal::Plain(std::borrow::Cow::Owned(branch)));
+ query.branch.insert(Literal::Plain(branch.into()));
}
let file_content = app
From 2da46ef7a55c018ba4a5db541a6075e8d5e1d06d Mon Sep 17 00:00:00 2001
From: rsdy
Date: Fri, 10 Nov 2023 15:28:27 +0100
Subject: [PATCH 08/42] Add `raw_query` to exchange
---
server/bleep/src/agent/exchange.rs | 4 +++-
server/bleep/src/webserver/answer.rs | 4 ++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/server/bleep/src/agent/exchange.rs b/server/bleep/src/agent/exchange.rs
index 89a050c5f5..05ad8688c0 100644
--- a/server/bleep/src/agent/exchange.rs
+++ b/server/bleep/src/agent/exchange.rs
@@ -12,6 +12,7 @@ use rand::seq::SliceRandom;
pub struct Exchange {
pub id: uuid::Uuid,
pub query: SemanticQuery<'static>,
+ pub raw_query: String,
pub answer: Option,
pub search_steps: Vec,
pub paths: Vec,
@@ -35,10 +36,11 @@ pub struct Exchange {
}
impl Exchange {
- pub fn new(id: uuid::Uuid, query: SemanticQuery<'static>) -> Self {
+ pub fn new(id: uuid::Uuid, raw_query: String, query: SemanticQuery<'static>) -> Self {
Self {
id,
query,
+ raw_query,
query_timestamp: Some(Utc::now()),
..Default::default()
}
diff --git a/server/bleep/src/webserver/answer.rs b/server/bleep/src/webserver/answer.rs
index c58352cce7..f32e68ca74 100644
--- a/server/bleep/src/webserver/answer.rs
+++ b/server/bleep/src/webserver/answer.rs
@@ -139,7 +139,7 @@ pub(super) async fn answer(
debug!(?query_target, "parsed query target");
let action = Action::Query(query_target);
- exchanges.push(Exchange::new(query_id, query));
+ exchanges.push(Exchange::new(query_id, q.to_string(), query));
execute_agent(
params.clone(),
@@ -426,7 +426,7 @@ pub async fn explain(
.collect::>()
.join("\n");
- let mut exchange = Exchange::new(query_id, query);
+ let mut exchange = Exchange::new(query_id, virtual_req.q.to_string(), query);
exchange.focused_chunk = Some(FocusedChunk {
file_path: params.relative_path.clone(),
From fdf93eeb4223e25dd1ef7928eadcc4ec3af76ed1 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Fri, 10 Nov 2023 18:21:18 +0100
Subject: [PATCH 09/42] Add offsets to lang filters
---
server/bleep/src/indexes/reader.rs | 6 +-
server/bleep/src/query/languages.rs | 4 +-
server/bleep/src/query/parser.rs | 550 ++++++++++++++++++++++------
3 files changed, 449 insertions(+), 111 deletions(-)
diff --git a/server/bleep/src/indexes/reader.rs b/server/bleep/src/indexes/reader.rs
index 39b34b0cbf..b883425783 100644
--- a/server/bleep/src/indexes/reader.rs
+++ b/server/bleep/src/indexes/reader.rs
@@ -105,7 +105,7 @@ impl DocumentRead for ContentReader {
.literal(schema.relative_path, |q| q.path.clone())
.literal(schema.repo_name, |q| q.repo.clone())
.literal(schema.branches, |q| q.branch.clone())
- .byte_string(schema.lang, |q| q.lang.as_ref())
+ .byte_string(schema.lang, |q| q.lang.as_ref().map(AsRef::as_ref))
.literal(schema.symbols, |q| {
q.target.as_ref().and_then(Target::symbol).cloned()
})
@@ -196,7 +196,7 @@ impl DocumentRead for FileReader {
.literal(schema.relative_path, |q| q.path.clone())
.literal(schema.repo_name, |q| q.repo.clone())
.literal(schema.branches, |q| q.branch.clone())
- .byte_string(schema.lang, |q| q.lang.as_ref())
+ .byte_string(schema.lang, |q| q.lang.as_ref().map(AsRef::as_ref))
.compile(queries, tantivy_index)
}
@@ -328,7 +328,7 @@ impl DocumentRead for OpenReader {
}
_ => None,
})
- .byte_string(schema.lang, |q| q.lang.as_ref())
+ .byte_string(schema.lang, |q| q.lang.as_ref().map(AsRef::as_ref))
.compile(queries, tantivy_index)
}
diff --git a/server/bleep/src/query/languages.rs b/server/bleep/src/query/languages.rs
index 74f9e3ed36..a54d4c553d 100644
--- a/server/bleep/src/query/languages.rs
+++ b/server/bleep/src/query/languages.rs
@@ -2,8 +2,8 @@ use std::{borrow::Cow, collections::HashSet};
include!(concat!(env!("OUT_DIR"), "/languages.rs"));
-pub fn parse_alias(lang: Cow) -> Cow {
- if let Some(s) = EXT_MAP.get(&lang) {
+pub fn parse_alias(lang: &str) -> Cow<'static, str> {
+ if let Some(s) = EXT_MAP.get(lang) {
(*s).into()
} else {
lang.to_ascii_lowercase().into()
diff --git a/server/bleep/src/query/parser.rs b/server/bleep/src/query/parser.rs
index 679a72fafd..2ecbc1b674 100644
--- a/server/bleep/src/query/parser.rs
+++ b/server/bleep/src/query/parser.rs
@@ -12,7 +12,7 @@ pub struct Query<'a> {
pub org: Option>,
pub repo: Option>,
pub path: Option>,
- pub lang: Option>,
+ pub lang: Option>,
pub branch: Option>,
pub target: Option>,
}
@@ -27,7 +27,7 @@ pub enum Target<'a> {
pub struct SemanticQuery<'a> {
pub repos: HashSet>,
pub paths: HashSet>,
- pub langs: HashSet>,
+ pub langs: HashSet>,
pub branch: HashSet>,
pub target: Option>,
}
@@ -42,7 +42,7 @@ impl<'a> SemanticQuery<'a> {
}
pub fn langs(&'a self) -> impl Iterator- > {
- self.langs.iter().cloned()
+ self.langs.iter().filter_map(|t| t.as_plain())
}
pub fn target(&self) -> Option> {
@@ -77,11 +77,7 @@ impl<'a> SemanticQuery<'a> {
SemanticQuery {
repos: self.repos.into_iter().map(Literal::into_owned).collect(),
paths: self.paths.into_iter().map(Literal::into_owned).collect(),
- langs: self
- .langs
- .into_iter()
- .map(|c| c.into_owned().into())
- .collect(),
+ langs: self.langs.into_iter().map(Literal::into_owned).collect(),
branch: self.branch.into_iter().map(Literal::into_owned).collect(),
target: self.target.map(Literal::into_owned),
}
@@ -276,12 +272,24 @@ impl<'a> Default for LiteralInner<'a> {
}
}
+impl<'a> From> for Literal<'a> {
+ fn from(value: Cow<'a, str>) -> Self {
+ Literal::Plain(value.clone().into())
+ }
+}
+
impl From<&String> for Literal<'static> {
fn from(value: &String) -> Self {
Literal::Plain(value.to_owned().into())
}
}
+impl From<&str> for Literal<'static> {
+ fn from(value: &str) -> Self {
+ Literal::Plain(value.to_owned().into())
+ }
+}
+
impl<'a> Default for Literal<'a> {
fn default() -> Self {
Literal::Plain(Default::default())
@@ -371,6 +379,26 @@ impl<'a> From> for Literal<'a> {
}
}
+impl<'a, 'b: 'a> AsRef> for Literal<'b> {
+ fn as_ref(&self) -> &Cow<'a, str> {
+ match self {
+ Literal::Plain(inner) => &inner.content,
+ Literal::Regex(inner) => &inner.content,
+ }
+ }
+}
+
+impl std::ops::Deref for Literal<'_> {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ Literal::Plain(inner) => inner.as_ref(),
+ Literal::Regex(inner) => inner.as_ref(),
+ }
+ }
+}
+
/// Unescape a string, with a specific terminating character.
///
/// Newline and tab strings (`\n` and `\t`) are replaced with the respective character. Backslashes
@@ -419,7 +447,7 @@ enum Expr<'a> {
Repo(Literal<'a>),
Symbol(Literal<'a>),
Path(Literal<'a>),
- Lang(Cow<'a, str>),
+ Lang(Literal<'a>),
Content(Literal<'a>),
Branch(Literal<'a>),
@@ -444,7 +472,7 @@ impl<'a> Expr<'a> {
Rule::symbol => Symbol(Literal::from(pair.into_inner().next().unwrap())),
Rule::org => Org(Literal::from(pair.into_inner().next().unwrap())),
Rule::branch => Branch(Literal::from(pair.into_inner().next().unwrap())),
- Rule::lang => Lang(pair.into_inner().as_str().into()),
+ Rule::lang => Lang(Literal::from(pair.into_inner().next().unwrap())),
Rule::open => {
let inner = pair.into_inner().next().unwrap();
@@ -564,8 +592,13 @@ pub fn parse_nl(query: &str) -> Result, ParseError> {
let _ = branch.insert(item);
}
Rule::lang => {
- let item = super::languages::parse_alias(pair.into_inner().as_str().into());
- let _ = langs.insert(item);
+ let inner = pair.into_inner().next().unwrap();
+ let item = Literal::Plain(LiteralInner {
+ content: super::languages::parse_alias(inner.as_str()),
+ start: inner.as_span().start(),
+ end: inner.as_span().end(),
+ });
+ let _ = langs.insert(item.into());
}
Rule::raw_text => {
let rhs = Literal::from(pair);
@@ -612,7 +645,7 @@ fn flatten(root: Expr<'_>) -> SmallVec<[Query<'_>; 1]> {
..Default::default()
}],
Expr::Lang(lang) => smallvec![Query {
- lang: Some(super::languages::parse_alias(lang)),
+ lang: Some(super::languages::parse_alias(&lang).into()),
..Default::default()
}],
Expr::Content(lit) => smallvec![Query {
@@ -659,7 +692,11 @@ mod tests {
assert_eq!(
parse("ParseError").unwrap(),
vec![Query {
- target: Some(Target::Content(Literal::Plain("ParseError".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 0,
+ end: 10,
+ content: "ParseError".into()
+ }))),
..Query::default()
}],
);
@@ -667,10 +704,26 @@ mod tests {
assert_eq!(
parse("org:bloopai repo:enterprise-search branch:origin/main ParseError").unwrap(),
vec![Query {
- repo: Some(Literal::Plain("enterprise-search".into())),
- org: Some(Literal::Plain("bloopai".into())),
- branch: Some(Literal::Plain("origin/main".into())),
- target: Some(Target::Content(Literal::Plain("ParseError".into()))),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 17,
+ end: 34,
+ content: "enterprise-search".into()
+ })),
+ org: Some(Literal::Plain(LiteralInner {
+ start: 4,
+ end: 11,
+ content: "bloopai".into()
+ })),
+ branch: Some(Literal::Plain(LiteralInner {
+ start: 42,
+ end: 53,
+ content: "origin/main".into()
+ })),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 54,
+ end: 64,
+ content: "ParseError".into()
+ }))),
..Query::default()
}],
);
@@ -678,9 +731,21 @@ mod tests {
assert_eq!(
parse("org:bloopai repo:enterprise-search ParseError").unwrap(),
vec![Query {
- repo: Some(Literal::Plain("enterprise-search".into())),
- org: Some(Literal::Plain("bloopai".into())),
- target: Some(Target::Content(Literal::Plain("ParseError".into()))),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 17,
+ end: 34,
+ content: "enterprise-search".into()
+ })),
+ org: Some(Literal::Plain(LiteralInner {
+ start: 4,
+ end: 11,
+ content: "bloopai".into()
+ })),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 35,
+ end: 45,
+ content: "ParseError".into()
+ }))),
..Query::default()
}],
);
@@ -688,7 +753,11 @@ mod tests {
assert_eq!(
parse("content:ParseError").unwrap(),
vec![Query {
- target: Some(Target::Content(Literal::Plain("ParseError".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 8,
+ end: 18,
+ content: "ParseError".into()
+ }))),
..Query::default()
}],
);
@@ -697,8 +766,16 @@ mod tests {
assert_eq!(
parse("path:foo.c create_foo symbol:bar").unwrap(),
vec![Query {
- path: Some(Literal::Plain("foo.c".into())),
- target: Some(Target::Symbol(Literal::Plain("bar".into()))),
+ path: Some(Literal::Plain(LiteralInner {
+ start: 5,
+ end: 10,
+ content: "foo.c".into()
+ })),
+ target: Some(Target::Symbol(Literal::Plain(LiteralInner {
+ start: 29,
+ end: 32,
+ content: "bar".into()
+ }))),
..Query::default()
}],
);
@@ -707,7 +784,11 @@ mod tests {
parse("case:ignore Parse").unwrap(),
vec![Query {
case_sensitive: Some(false),
- target: Some(Target::Content(Literal::Plain("Parse".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 12,
+ end: 17,
+ content: "Parse".into()
+ }))),
..Query::default()
}],
);
@@ -719,12 +800,24 @@ mod tests {
parse("repo:foo ParseError or repo:bar").unwrap(),
vec![
Query {
- repo: Some(Literal::Plain("foo".into())),
- target: Some(Target::Content(Literal::Plain("ParseError".into()))),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 5,
+ end: 8,
+ content: "foo".into()
+ })),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 9,
+ end: 19,
+ content: "ParseError".into()
+ }))),
..Query::default()
},
Query {
- repo: Some(Literal::Plain("bar".into())),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 28,
+ end: 31,
+ content: "bar".into()
+ })),
..Query::default()
},
],
@@ -735,12 +828,24 @@ mod tests {
parse("repo:bar or repo:foo ParseError").unwrap(),
vec![
Query {
- repo: Some(Literal::Plain("bar".into())),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 5,
+ end: 8,
+ content: "bar".into()
+ })),
..Query::default()
},
Query {
- repo: Some(Literal::Plain("foo".into())),
- target: Some(Target::Content(Literal::Plain("ParseError".into()))),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 17,
+ end: 20,
+ content: "foo".into()
+ })),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 21,
+ end: 31,
+ content: "ParseError".into()
+ }))),
..Query::default()
},
],
@@ -808,25 +913,65 @@ mod tests {
parse("(((repo:foo xyz) or repo:abc) (repo:fred or repo:grub) org:bloop)").unwrap(),
vec![
Query {
- repo: Some(Literal::Plain("fred".into())),
- org: Some(Literal::Plain("bloop".into())),
- target: Some(Target::Content(Literal::Plain("xyz".into()))),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 36,
+ end: 40,
+ content: "fred".into()
+ })),
+ org: Some(Literal::Plain(LiteralInner {
+ start: 59,
+ end: 64,
+ content: "bloop".into()
+ })),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 12,
+ end: 15,
+ content: "xyz".into()
+ }))),
..Query::default()
},
Query {
- repo: Some(Literal::Plain("grub".into())),
- org: Some(Literal::Plain("bloop".into())),
- target: Some(Target::Content(Literal::Plain("xyz".into()))),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 49,
+ end: 53,
+ content: "grub".into()
+ })),
+ org: Some(Literal::Plain(LiteralInner {
+ start: 59,
+ end: 64,
+ content: "bloop".into()
+ })),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 12,
+ end: 15,
+ content: "xyz".into()
+ }))),
..Query::default()
},
Query {
- repo: Some(Literal::Plain("fred".into())),
- org: Some(Literal::Plain("bloop".into())),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 36,
+ end: 40,
+ content: "fred".into()
+ })),
+ org: Some(Literal::Plain(LiteralInner {
+ start: 59,
+ end: 64,
+ content: "bloop".into()
+ })),
..Query::default()
},
Query {
- repo: Some(Literal::Plain("grub".into())),
- org: Some(Literal::Plain("bloop".into())),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 49,
+ end: 53,
+ content: "grub".into()
+ })),
+ org: Some(Literal::Plain(LiteralInner {
+ start: 59,
+ end: 64,
+ content: "bloop".into()
+ })),
..Query::default()
},
],
@@ -839,27 +984,63 @@ mod tests {
parse("(repo:bloop or repo:google) Parser or repo:zoekt Parsing or (symbol:Compiler or (org:bloop repo:enterprise-search))").unwrap(),
vec![
Query {
- repo: Some(Literal::Plain("bloop".into())),
- target: Some(Target::Content(Literal::Plain("Parser".into()))),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 6,
+ end: 11,
+ content: "bloop".into()
+ })),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 28,
+ end: 34,
+ content: "Parser".into()
+ }))),
..Query::default()
},
Query {
- repo: Some(Literal::Plain("google".into())),
- target: Some(Target::Content(Literal::Plain("Parser".into()))),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 20,
+ end: 26,
+ content: "google".into()
+ })),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 28,
+ end: 34,
+ content: "Parser".into()
+ }))),
..Query::default()
},
Query {
- repo: Some(Literal::Plain("zoekt".into())),
- target: Some(Target::Content(Literal::Plain("Parsing".into()))),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 43,
+ end: 48,
+ content: "zoekt".into()
+ })),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 49,
+ end: 56,
+ content: "Parsing".into()
+ }))),
..Query::default()
},
Query {
- target: Some(Target::Symbol(Literal::Plain("Compiler".into()))),
+ target: Some(Target::Symbol(Literal::Plain(LiteralInner {
+ start: 68,
+ end: 76,
+ content: "Compiler".into()
+ }))),
..Query::default()
},
Query {
- repo: Some(Literal::Plain("enterprise-search".into())),
- org: Some(Literal::Plain("bloop".into())),
+ repo: Some(Literal::Plain(LiteralInner {
+ start: 96,
+ end: 113,
+ content: "enterprise-search".into()
+ })),
+ org: Some(Literal::Plain(LiteralInner {
+ start: 85,
+ end: 90,
+ content: "bloop".into()
+ })),
..Query::default()
},
],
@@ -871,7 +1052,11 @@ mod tests {
assert_eq!(
parse("path:foo/bar.js").unwrap(),
vec![Query {
- path: Some(Literal::Plain("foo/bar.js".into())),
+ path: Some(Literal::Plain(LiteralInner {
+ start: 5,
+ end: 15,
+ content: "foo/bar.js".into(),
+ })),
..Query::default()
}],
);
@@ -895,8 +1080,12 @@ mod tests {
assert_eq!(
parse("lang:Rust path:server").unwrap(),
vec![Query {
- path: Some(Literal::Plain("server".into())),
- lang: Some("rust".into()),
+ path: Some(Literal::Plain(LiteralInner {
+ start: 15,
+ end: 21,
+ content: "server".into()
+ })),
+ lang: Some(Literal::Plain("rust".into())),
..Query::default()
}],
);
@@ -908,7 +1097,11 @@ mod tests {
parse("open:true path:server/bleep/Cargo.toml").unwrap(),
vec![Query {
open: Some(true),
- path: Some(Literal::Plain("server/bleep/Cargo.toml".into())),
+ path: Some(Literal::Plain(LiteralInner {
+ start: 15,
+ end: 38,
+ content: "server/bleep/Cargo.toml".into()
+ })),
..Query::default()
}],
);
@@ -917,7 +1110,11 @@ mod tests {
parse("open:false path:server/bleep/Cargo.toml").unwrap(),
vec![Query {
open: Some(false),
- path: Some(Literal::Plain("server/bleep/Cargo.toml".into())),
+ path: Some(Literal::Plain(LiteralInner {
+ start: 16,
+ end: 39,
+ content: "server/bleep/Cargo.toml".into()
+ })),
..Query::default()
}],
);
@@ -926,7 +1123,11 @@ mod tests {
parse("path:server/bleep/Cargo.toml").unwrap(),
vec![Query {
open: None,
- path: Some(Literal::Plain("server/bleep/Cargo.toml".into())),
+ path: Some(Literal::Plain(LiteralInner {
+ start: 5,
+ end: 28,
+ content: "server/bleep/Cargo.toml".into()
+ })),
..Query::default()
}],
);
@@ -937,7 +1138,11 @@ mod tests {
assert_eq!(
parse("foo\\nbar\\tquux").unwrap(),
vec![Query {
- target: Some(Target::Content(Literal::Plain("foo\\nbar\\tquux".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 0,
+ end: 14,
+ content: "foo\\nbar\\tquux".into()
+ }))),
..Query::default()
}],
);
@@ -945,9 +1150,11 @@ mod tests {
assert_eq!(
parse("/^\\b\\B\\w\\Wfoo\\d\\D$/").unwrap(),
vec![Query {
- target: Some(Target::Content(Literal::Regex(
- "^\\b\\B\\w\\Wfoo\\d\\D$".into()
- ))),
+ target: Some(Target::Content(Literal::Regex(LiteralInner {
+ start: 1,
+ end: 18,
+ content: "^\\b\\B\\w\\Wfoo\\d\\D$".into()
+ }))),
..Query::default()
}],
);
@@ -959,7 +1166,11 @@ mod tests {
parse("global_regex:true foo").unwrap(),
vec![Query {
global_regex: Some(true),
- target: Some(Target::Content(Literal::Regex("foo".into()))),
+ target: Some(Target::Content(Literal::Regex(LiteralInner {
+ start: 18,
+ end: 21,
+ content: "foo".into()
+ }))),
..Query::default()
}],
);
@@ -969,7 +1180,11 @@ mod tests {
parse("global_regex:true /foo/").unwrap(),
vec![Query {
global_regex: Some(true),
- target: Some(Target::Content(Literal::Regex("foo".into()))),
+ target: Some(Target::Content(Literal::Regex(LiteralInner {
+ start: 19,
+ end: 22,
+ content: "foo".into()
+ }))),
..Query::default()
}],
);
@@ -978,7 +1193,11 @@ mod tests {
assert_eq!(
parse("foo").unwrap(),
vec![Query {
- target: Some(Target::Content(Literal::Plain("foo".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 0,
+ end: 3,
+ content: "foo".into()
+ }))),
..Query::default()
}],
);
@@ -992,16 +1211,40 @@ mod tests {
vec![
Query {
global_regex: Some(true),
- org: Some(Literal::Regex("bloopai".into())),
- repo: Some(Literal::Regex("bloop".into())),
- path: Some(Literal::Regex("server".into())),
- target: Some(Target::Content(Literal::Regex("foo".into()))),
+ org: Some(Literal::Regex(LiteralInner {
+ start: 23,
+ end: 30,
+ content: "bloopai".into(),
+ })),
+ repo: Some(Literal::Regex(LiteralInner {
+ start: 36,
+ end: 41,
+ content: "bloop".into(),
+ })),
+ path: Some(Literal::Regex(LiteralInner {
+ start: 47,
+ end: 53,
+ content: "server".into(),
+ })),
+ target: Some(Target::Content(Literal::Regex(LiteralInner {
+ start: 54,
+ end: 57,
+ content: "foo".into(),
+ }))),
..Query::default()
},
Query {
global_regex: Some(true),
- repo: Some(Literal::Regex("google".into())),
- target: Some(Target::Content(Literal::Regex("bar".into()))),
+ repo: Some(Literal::Regex(LiteralInner {
+ start: 66,
+ end: 72,
+ content: "google".into(),
+ })),
+ target: Some(Target::Content(Literal::Regex(LiteralInner {
+ start: 73,
+ end: 76,
+ content: "bar".into(),
+ }))),
..Query::default()
},
],
@@ -1013,12 +1256,20 @@ mod tests {
vec![
Query {
global_regex: Some(false),
- target: Some(Target::Content(Literal::Plain("foo".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 18,
+ end: 21,
+ content: "foo".into(),
+ }))),
..Query::default()
},
Query {
global_regex: Some(false),
- target: Some(Target::Content(Literal::Plain("bar".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 25,
+ end: 28,
+ content: "bar".into(),
+ }))),
..Query::default()
},
],
@@ -1034,26 +1285,24 @@ mod tests {
vec![
Query {
case_sensitive: Some(false),
- target: Some(Target::Content(Literal::Plain("foo".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 0,
+ end: 3,
+ content: "foo".into()
+ }))),
..Query::default()
},
Query {
case_sensitive: Some(false),
- target: Some(Target::Content(Literal::Plain("bar".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 7,
+ end: 10,
+ content: "bar".into()
+ }))),
..Query::default()
},
],
);
-
- assert_eq!(
- parse("foo or bar case:ignore").unwrap(),
- parse("case:ignore foo or bar").unwrap(),
- );
-
- assert_eq!(
- parse("foo or bar case:ignore").unwrap(),
- parse("case:sensitive foo or bar case:ignore").unwrap(),
- );
}
#[test]
@@ -1061,7 +1310,11 @@ mod tests {
assert_eq!(
parse("org").unwrap(),
vec![Query {
- target: Some(Target::Content(Literal::Plain("org".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 0,
+ end: 3,
+ content: "org".into()
+ }))),
..Query::default()
},],
);
@@ -1070,11 +1323,19 @@ mod tests {
parse("org or orange").unwrap(),
vec![
Query {
- target: Some(Target::Content(Literal::Plain("org".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 0,
+ end: 3,
+ content: "org".into()
+ }))),
..Query::default()
},
Query {
- target: Some(Target::Content(Literal::Plain("orange".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 7,
+ end: 13,
+ content: "orange".into()
+ }))),
..Query::default()
},
],
@@ -1086,7 +1347,11 @@ mod tests {
assert_eq!(
parse("for").unwrap(),
vec![Query {
- target: Some(Target::Content(Literal::Plain("for".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 0,
+ end: 3,
+ content: "for".into()
+ }))),
..Query::default()
},],
);
@@ -1095,11 +1360,19 @@ mod tests {
parse("for or error").unwrap(),
vec![
Query {
- target: Some(Target::Content(Literal::Plain("for".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 0,
+ end: 3,
+ content: "for".into()
+ }))),
..Query::default()
},
Query {
- target: Some(Target::Content(Literal::Plain("error".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 7,
+ end: 12,
+ content: "error".into()
+ }))),
..Query::default()
},
],
@@ -1126,8 +1399,18 @@ mod tests {
parse_nl("what is background color? lang:tsx repo:bloop").unwrap(),
SemanticQuery {
target: Some(Literal::Plain("what is background color?".into())),
- langs: ["tsx".into()].into(),
- repos: [Literal::Plain("bloop".into())].into(),
+ langs: [Literal::Plain(LiteralInner {
+ start: 31,
+ end: 34,
+ content: "tsx".into()
+ })]
+ .into(),
+ repos: [Literal::Plain(LiteralInner {
+ start: 40,
+ end: 45,
+ content: "bloop".into()
+ })]
+ .into(),
paths: [].into(),
branch: [].into()
},
@@ -1137,26 +1420,54 @@ mod tests {
#[test]
fn nl_parse_dedup_similar_filters() {
let q = parse_nl("what is background color? lang:tsx repo:bloop repo:bloop").unwrap();
- assert_eq!(q.repos().count(), 1);
+ assert_eq!(q.repos().count(), 2);
}
#[test]
fn nl_parse_multiple_filters() {
assert_eq!(
- parse_nl("what is background color? lang:tsx lang:ts repo:bloop repo:bar path:server/bleep repo:baz")
- .unwrap(),
+ parse_nl("what is background color? lang:tsx lang:ts repo:bloop repo:bar path:server/bleep repo:baz").unwrap(),
SemanticQuery {
target: Some(Literal::Plain("what is background color?".into())),
- langs: ["tsx".into(), "typescript".into()].into(),
+ langs: [
+ Literal::Plain(LiteralInner {
+ start: 31,
+ end: 34,
+ content: "tsx".into()
+ }),
+ Literal::Plain(LiteralInner {
+ start: 40,
+ end: 42,
+ content: "typescript".into()
+ })
+ ]
+ .into(),
branch: [].into(),
repos: [
- Literal::Plain("bloop".into()),
- Literal::Plain("bar".into()),
- Literal::Plain("baz".into())
+ Literal::Plain(LiteralInner {
+ start: 48,
+ end: 53,
+ content: "bloop".into(),
+ }),
+ Literal::Plain(LiteralInner {
+ start: 86,
+ end: 89,
+ content: "baz".into(),
+ }),
+ Literal::Plain(LiteralInner {
+ start: 59,
+ end: 62,
+ content: "bar".into(),
+ }),
]
.into(),
- paths: [Literal::Plain("server/bleep".into())].into(),
- }
+ paths: [Literal::Plain(LiteralInner {
+ start: 68,
+ end: 80,
+ content: "server/bleep".into(),
+ })]
+ .into(),
+ },
);
}
@@ -1169,8 +1480,18 @@ mod tests {
.unwrap(),
SemanticQuery {
target: Some(Literal::Plain("what is background color?".into())),
- langs: ["tsx".into()].into(),
- repos: [Literal::Plain("bloop".into())].into(),
+ langs: [Literal::Plain(LiteralInner {
+ start: 31,
+ end: 34,
+ content: "tsx".into()
+ })]
+ .into(),
+ repos: [Literal::Plain(LiteralInner {
+ start: 40,
+ end: 45,
+ content: "bloop".into()
+ })]
+ .into(),
paths: [].into(),
branch: [].into(),
}
@@ -1182,7 +1503,12 @@ mod tests {
target: Some(Literal::Plain(
"why are languages excluded from ctags?".into()
)),
- branch: [Literal::Plain("main".into())].into(),
+ branch: [Literal::Plain(LiteralInner {
+ start: 58,
+ end: 62,
+ content: "main".into()
+ })]
+ .into(),
..Default::default()
}
);
@@ -1206,7 +1532,11 @@ mod tests {
assert_eq!(
parse("'foo\\'bar'").unwrap(),
vec![Query {
- target: Some(Target::Content(Literal::Plain("foo'bar".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 1,
+ end: 9,
+ content: "foo'bar".into()
+ }))),
..Query::default()
}],
);
@@ -1214,7 +1544,11 @@ mod tests {
assert_eq!(
parse(r#""foo\"bar""#).unwrap(),
vec![Query {
- target: Some(Target::Content(Literal::Plain("foo\"bar".into()))),
+ target: Some(Target::Content(Literal::Plain(LiteralInner {
+ start: 1,
+ end: 9,
+ content: "foo\"bar".into()
+ }))),
..Query::default()
}],
);
@@ -1222,7 +1556,11 @@ mod tests {
assert_eq!(
parse("/foo\\/bar/").unwrap(),
vec![Query {
- target: Some(Target::Content(Literal::Regex("foo/bar".into()))),
+ target: Some(Target::Content(Literal::Regex(LiteralInner {
+ start: 1,
+ end: 9,
+ content: "foo/bar".into()
+ }))),
..Query::default()
}],
);
From ed5497c01ab2c8077b05781ee540175b2ce9bf9c Mon Sep 17 00:00:00 2001
From: rsdy
Date: Fri, 10 Nov 2023 18:27:29 +0100
Subject: [PATCH 10/42] Respect the clippy
---
server/bleep/src/query/parser.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/bleep/src/query/parser.rs b/server/bleep/src/query/parser.rs
index 2ecbc1b674..6459330c23 100644
--- a/server/bleep/src/query/parser.rs
+++ b/server/bleep/src/query/parser.rs
@@ -598,7 +598,7 @@ pub fn parse_nl(query: &str) -> Result, ParseError> {
start: inner.as_span().start(),
end: inner.as_span().end(),
});
- let _ = langs.insert(item.into());
+ let _ = langs.insert(item);
}
Rule::raw_text => {
let rhs = Literal::from(pair);
From 0659a1db16a619757cdbc7e03b446bd02734eac8 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Mon, 13 Nov 2023 11:29:50 +0100
Subject: [PATCH 11/42] Aggregate and rank languages from the executed queries
---
server/bleep/src/webserver/autocomplete.rs | 62 ++++++++++------------
1 file changed, 29 insertions(+), 33 deletions(-)
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index 80bb96520f..c6d088b137 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -1,4 +1,4 @@
-use std::sync::Arc;
+use std::{collections::HashMap, sync::Arc};
use super::prelude::*;
use crate::{
@@ -23,7 +23,7 @@ pub(super) async fn handle(
) -> Result {
// Override page_size and set to low value
api_params.page = 0;
- api_params.page_size = 3;
+ api_params.page_size = 10;
let queries = parser::parse(&api_params.q).map_err(Error::user)?;
let mut autocomplete_results = vec![];
@@ -41,37 +41,46 @@ pub(super) async fn handle(
);
}
- if let Some(matched_langs) = complete_lang(&api_params.q) {
- autocomplete_results.append(
- &mut matched_langs
- .map(|l| QueryResult::Lang(l.to_string()))
- .collect(),
- );
- }
-
// If no flags completion, run a search with full query
if autocomplete_results.is_empty() {
let contents = ContentReader.execute(&indexes.file, &queries, &api_params);
let repos = RepoReader.execute(&indexes.repo, &queries, &api_params);
let files = FileReader.execute(&indexes.file, &queries, &api_params);
- autocomplete_results = stream::iter([contents, repos, files])
+ let (langs, list) = stream::iter([contents, repos, files])
// Buffer several readers at the same time. The exact number is not important; this is
// simply an upper bound.
.buffered(10)
- .try_fold(Vec::new(), |mut a, e| async {
- a.extend(e.data.into_iter());
- Ok(a)
- })
+ .try_fold(
+ (HashMap::::new(), Vec::new()),
+ |(mut langs, mut list), e| async {
+ for (lang, count) in e.stats.lang {
+ // The exact number here isn't relevant, and
+ // this may be off.
+ //
+ // We're trying to scale the results compared
+ // to each other which means this will still
+ // serve the purpose for ranking.
+ *langs.entry(lang).or_default() += count;
+ }
+ list.extend(e.data.into_iter());
+ Ok((langs, list))
+ },
+ )
.await
.map_err(Error::internal)?;
- }
- let count = autocomplete_results.len();
- let data = autocomplete_results;
- let response = AutocompleteResponse { count, data };
+ autocomplete_results.extend(list);
- Ok(json(response))
+ let mut ranked_langs = langs.into_iter().collect::>();
+ ranked_langs.sort_by(|(_, a_count), (_, b_count)| a_count.cmp(b_count));
+ autocomplete_results.extend(ranked_langs.into_iter().map(|(l, _)| QueryResult::Lang(l)));
+ }
+
+ Ok(json(AutocompleteResponse {
+ count: autocomplete_results.len(),
+ data: autocomplete_results,
+ }))
}
fn complete_flag(q: &str) -> impl Iterator- + '_ {
@@ -81,19 +90,6 @@ fn complete_flag(q: &str) -> impl Iterator
- + '_ {
.copied()
}
-// Bypass the parser and execute a prefix search using the rightmost whitespace-split token
-// in the query string.
-//
-// This should be revisited when we implement cursor-aware autocomplete.
-fn complete_lang(q: &str) -> Option + '_> {
- match q.split_whitespace().rfind(|comp| comp.starts_with("lang:")) {
- Some(last) => last
- .strip_prefix("lang:")
- .map(|prefix| crate::query::languages::list().filter(move |l| l.starts_with(prefix))),
- _ => None,
- }
-}
-
#[derive(Serialize)]
pub(super) struct AutocompleteResponse {
count: usize,
From 7730b4d80cc5d7c6c4cf546aab1791bd5e452c1b Mon Sep 17 00:00:00 2001
From: rsdy
Date: Mon, 13 Nov 2023 12:08:27 +0100
Subject: [PATCH 12/42] Only offer lang suggestions if there's a lang filter in
the query
---
server/bleep/src/webserver/autocomplete.rs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index c6d088b137..9df929e852 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -72,9 +72,12 @@ pub(super) async fn handle(
autocomplete_results.extend(list);
- let mut ranked_langs = langs.into_iter().collect::>();
- ranked_langs.sort_by(|(_, a_count), (_, b_count)| a_count.cmp(b_count));
- autocomplete_results.extend(ranked_langs.into_iter().map(|(l, _)| QueryResult::Lang(l)));
+ if queries.iter().any(|q| q.lang.is_some()) {
+ let mut ranked_langs = langs.into_iter().collect::>();
+ ranked_langs.sort_by(|(_, a_count), (_, b_count)| a_count.cmp(b_count));
+ autocomplete_results
+ .extend(ranked_langs.into_iter().map(|(l, _)| QueryResult::Lang(l)));
+ }
}
Ok(json(AutocompleteResponse {
From 46129614af879da096743b18b95669d6b45446d5 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Mon, 13 Nov 2023 16:07:47 +0100
Subject: [PATCH 13/42] Partially parsed lang functions will not return results
---
server/bleep/src/webserver/autocomplete.rs | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index 9df929e852..c6d088b137 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -72,12 +72,9 @@ pub(super) async fn handle(
autocomplete_results.extend(list);
- if queries.iter().any(|q| q.lang.is_some()) {
- let mut ranked_langs = langs.into_iter().collect::>();
- ranked_langs.sort_by(|(_, a_count), (_, b_count)| a_count.cmp(b_count));
- autocomplete_results
- .extend(ranked_langs.into_iter().map(|(l, _)| QueryResult::Lang(l)));
- }
+ let mut ranked_langs = langs.into_iter().collect::>();
+ ranked_langs.sort_by(|(_, a_count), (_, b_count)| a_count.cmp(b_count));
+ autocomplete_results.extend(ranked_langs.into_iter().map(|(l, _)| QueryResult::Lang(l)));
}
Ok(json(AutocompleteResponse {
From eb318b5218400c64b283515a08e3570de21b0bb8 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Mon, 13 Nov 2023 16:26:16 +0100
Subject: [PATCH 14/42] Allow excluding results from certain kind of results
---
server/bleep/src/webserver/autocomplete.rs | 43 ++++++++++++++++++----
1 file changed, 36 insertions(+), 7 deletions(-)
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index c6d088b137..c3bd06ea04 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -17,8 +17,25 @@ use axum::{extract::Query, response::IntoResponse as IntoAxumResponse, Extension
use futures::{stream, StreamExt, TryStreamExt};
use serde::Serialize;
+fn default_true() -> bool {
+ true
+}
+
+#[derive(Deserialize)]
+pub struct AutocompleteParams {
+ #[serde(default = "default_true")]
+ content: bool,
+ #[serde(default = "default_true")]
+ file: bool,
+ #[serde(default = "default_true")]
+ repo: bool,
+ #[serde(default = "default_true")]
+ lang: bool,
+}
+
pub(super) async fn handle(
Query(mut api_params): Query,
+ Query(ac_params): Query,
Extension(indexes): Extension>,
) -> Result {
// Override page_size and set to low value
@@ -43,11 +60,20 @@ pub(super) async fn handle(
// If no flags completion, run a search with full query
if autocomplete_results.is_empty() {
- let contents = ContentReader.execute(&indexes.file, &queries, &api_params);
- let repos = RepoReader.execute(&indexes.repo, &queries, &api_params);
- let files = FileReader.execute(&indexes.file, &queries, &api_params);
+ let mut engines = vec![];
+ if ac_params.content {
+ engines.push(ContentReader.execute(&indexes.file, &queries, &api_params));
+ }
+
+ if ac_params.file {
+ engines.push(RepoReader.execute(&indexes.repo, &queries, &api_params));
+ }
+
+ if ac_params.repo {
+ engines.push(FileReader.execute(&indexes.file, &queries, &api_params));
+ }
- let (langs, list) = stream::iter([contents, repos, files])
+ let (langs, list) = stream::iter(engines)
// Buffer several readers at the same time. The exact number is not important; this is
// simply an upper bound.
.buffered(10)
@@ -72,9 +98,12 @@ pub(super) async fn handle(
autocomplete_results.extend(list);
- let mut ranked_langs = langs.into_iter().collect::>();
- ranked_langs.sort_by(|(_, a_count), (_, b_count)| a_count.cmp(b_count));
- autocomplete_results.extend(ranked_langs.into_iter().map(|(l, _)| QueryResult::Lang(l)));
+ if ac_params.lang {
+ let mut ranked_langs = langs.into_iter().collect::>();
+ ranked_langs.sort_by(|(_, a_count), (_, b_count)| a_count.cmp(b_count));
+ autocomplete_results
+ .extend(ranked_langs.into_iter().map(|(l, _)| QueryResult::Lang(l)));
+ }
}
Ok(json(AutocompleteResponse {
From ae78fa9edf95081e3bd2aecfc0d159fc6de16eed Mon Sep 17 00:00:00 2001
From: rsdy
Date: Mon, 13 Nov 2023 20:06:36 +0100
Subject: [PATCH 15/42] Normalize results somewhat
---
server/bleep/src/query/parser.rs | 14 ++++++++---
server/bleep/src/webserver/autocomplete.rs | 29 +++++++++++++++++++++-
2 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/server/bleep/src/query/parser.rs b/server/bleep/src/query/parser.rs
index 6459330c23..ab2b0edc85 100644
--- a/server/bleep/src/query/parser.rs
+++ b/server/bleep/src/query/parser.rs
@@ -1,7 +1,7 @@
use pest::{iterators::Pair, Parser};
use regex::Regex;
use smallvec::{smallvec, SmallVec};
-use std::{borrow::Cow, collections::HashSet, mem};
+use std::{borrow::Cow, collections::HashSet, mem, ops::Deref};
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct Query<'a> {
@@ -167,6 +167,14 @@ impl<'a> Query<'a> {
}
impl<'a> Target<'a> {
+ /// Get the inner literal for this target, regardless of the variant.
+ pub fn literal_mut(&'a mut self) -> &mut Literal<'a> {
+ match self {
+ Self::Symbol(lit) => lit,
+ Self::Content(lit) => lit,
+ }
+ }
+
/// Get the inner literal for this target, regardless of the variant.
pub fn literal(&self) -> &Literal<'_> {
match self {
@@ -254,7 +262,7 @@ impl<'a, T: AsRef> From for LiteralInner<'a> {
}
}
-impl<'a> std::ops::Deref for LiteralInner<'a> {
+impl<'a> Deref for LiteralInner<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
@@ -388,7 +396,7 @@ impl<'a, 'b: 'a> AsRef> for Literal<'b> {
}
}
-impl std::ops::Deref for Literal<'_> {
+impl Deref for Literal<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index c3bd06ea04..8726d02036 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -42,7 +42,32 @@ pub(super) async fn handle(
api_params.page = 0;
api_params.page_size = 10;
- let queries = parser::parse(&api_params.q).map_err(Error::user)?;
+ let queries = parser::parse(&api_params.q)
+ .map_err(Error::user)?
+ .into_iter()
+ .map(|mut q| {
+ let target = q.target.get_or_insert_with(|| Target::Content(" ".into()));
+
+ for keyword in &["path:", "repo:"] {
+ if let Some(pos) = target.literal().find(keyword) {
+ let new = format!(
+ "{}{}",
+ &target.literal()[..pos],
+ &target.literal()[pos + keyword.len()..]
+ );
+
+ *target = Target::Content(Literal::Plain(if new.is_empty() {
+ " ".into()
+ } else {
+ new.into()
+ }));
+ }
+ }
+
+ q.lang = None;
+ q
+ })
+ .collect::>();
let mut autocomplete_results = vec![];
// Only execute prefix search on flag names if there is a non-regex content target.
@@ -101,6 +126,8 @@ pub(super) async fn handle(
if ac_params.lang {
let mut ranked_langs = langs.into_iter().collect::>();
ranked_langs.sort_by(|(_, a_count), (_, b_count)| a_count.cmp(b_count));
+ ranked_langs.reverse();
+ ranked_langs.truncate(5);
autocomplete_results
.extend(ranked_langs.into_iter().map(|(l, _)| QueryResult::Lang(l)));
}
From 571ef8642670faa38b7a1fa3d9dcf5f9e71e7eb9 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Tue, 14 Nov 2023 13:31:33 +0100
Subject: [PATCH 16/42] Show lang filters more aggressively
---
server/bleep/src/webserver/autocomplete.rs | 60 ++++++++++++++++------
1 file changed, 43 insertions(+), 17 deletions(-)
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index 8726d02036..317504946d 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -8,7 +8,7 @@ use crate::{
},
query::{
execute::{ApiQuery, ExecuteQuery, QueryResult},
- parser,
+ languages, parser,
parser::{Literal, Target},
},
};
@@ -42,29 +42,45 @@ pub(super) async fn handle(
api_params.page = 0;
api_params.page_size = 10;
+ let mut boost_langs = None;
+
let queries = parser::parse(&api_params.q)
.map_err(Error::user)?
.into_iter()
.map(|mut q| {
- let target = q.target.get_or_insert_with(|| Target::Content(" ".into()));
-
- for keyword in &["path:", "repo:"] {
- if let Some(pos) = target.literal().find(keyword) {
- let new = format!(
- "{}{}",
- &target.literal()[..pos],
- &target.literal()[pos + keyword.len()..]
- );
-
- *target = Target::Content(Literal::Plain(if new.is_empty() {
- " ".into()
- } else {
- new.into()
- }));
+ if ac_params.content {
+ let target = q.target.get_or_insert_with(|| Target::Content(" ".into()));
+
+ for keyword in &["path:", "repo:"] {
+ if let Some(pos) = target.literal().find(keyword) {
+ let new = format!(
+ "{}{}",
+ &target.literal()[..pos],
+ &target.literal()[pos + keyword.len()..]
+ );
+
+ *target = Target::Content(Literal::Plain(if new.is_empty() {
+ " ".into()
+ } else {
+ new.into()
+ }));
+ }
+ }
+ } else {
+ q.target = None;
+ }
+
+ if let Some(lang) = q.lang.as_ref() {
+ if languages::list()
+ .filter(|l| l.to_lowercase() == lang.as_ref().to_lowercase())
+ .count()
+ < 1
+ {
+ q.lang = None;
+ boost_langs = q.lang.as_ref().map(|l| l.to_string());
}
}
- q.lang = None;
q
})
.collect::>();
@@ -128,6 +144,16 @@ pub(super) async fn handle(
ranked_langs.sort_by(|(_, a_count), (_, b_count)| a_count.cmp(b_count));
ranked_langs.reverse();
ranked_langs.truncate(5);
+
+ if let Some(partial) = boost_langs {
+ ranked_langs.extend(
+ languages::list()
+ .filter(|name| name.contains(&partial))
+ .take(5 - ranked_langs.len())
+ .map(|lang| (lang.to_lowercase(), 1)),
+ );
+ }
+
autocomplete_results
.extend(ranked_langs.into_iter().map(|(l, _)| QueryResult::Lang(l)));
}
From a5c03f7ca668a3d29ad7327861c8b211ab1b5139 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Tue, 14 Nov 2023 13:58:42 +0100
Subject: [PATCH 17/42] Add arbitrary language suggestions
---
server/bleep/src/webserver/autocomplete.rs | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index 317504946d..29aa08ba31 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -159,6 +159,14 @@ pub(super) async fn handle(
}
}
+ if autocomplete_results.is_empty() && ac_params.lang {
+ autocomplete_results.extend(
+ languages::list()
+ .take(5)
+ .map(|l| QueryResult::Lang(l.into())),
+ )
+ }
+
Ok(json(AutocompleteResponse {
count: autocomplete_results.len(),
data: autocomplete_results,
From 4cd44a9ea93017c8b4f1367a97cc475fea4756a2 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Tue, 14 Nov 2023 14:20:24 +0100
Subject: [PATCH 18/42] Tuning
---
server/bleep/src/webserver/autocomplete.rs | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index 29aa08ba31..08d969bb83 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -51,7 +51,7 @@ pub(super) async fn handle(
if ac_params.content {
let target = q.target.get_or_insert_with(|| Target::Content(" ".into()));
- for keyword in &["path:", "repo:"] {
+ for keyword in &["lang:", "path:", "repo:"] {
if let Some(pos) = target.literal().find(keyword) {
let new = format!(
"{}{}",
@@ -81,6 +81,10 @@ pub(super) async fn handle(
}
}
+ if q.path.is_none() && ac_params.file {
+ q.path = Some(Literal::Regex(".".into()));
+ }
+
q
})
.collect::>();
@@ -106,11 +110,11 @@ pub(super) async fn handle(
engines.push(ContentReader.execute(&indexes.file, &queries, &api_params));
}
- if ac_params.file {
+ if ac_params.repo {
engines.push(RepoReader.execute(&indexes.repo, &queries, &api_params));
}
- if ac_params.repo {
+ if ac_params.file {
engines.push(FileReader.execute(&indexes.file, &queries, &api_params));
}
@@ -159,14 +163,6 @@ pub(super) async fn handle(
}
}
- if autocomplete_results.is_empty() && ac_params.lang {
- autocomplete_results.extend(
- languages::list()
- .take(5)
- .map(|l| QueryResult::Lang(l.into())),
- )
- }
-
Ok(json(AutocompleteResponse {
count: autocomplete_results.len(),
data: autocomplete_results,
From ba67c69d015ac04e205ff2e26e473866543b1d97 Mon Sep 17 00:00:00 2001
From: rsdy
Date: Tue, 14 Nov 2023 14:29:44 +0100
Subject: [PATCH 19/42] Make it a greedy regex
---
server/bleep/src/webserver/autocomplete.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/bleep/src/webserver/autocomplete.rs b/server/bleep/src/webserver/autocomplete.rs
index 08d969bb83..ed9d145e28 100644
--- a/server/bleep/src/webserver/autocomplete.rs
+++ b/server/bleep/src/webserver/autocomplete.rs
@@ -82,7 +82,7 @@ pub(super) async fn handle(
}
if q.path.is_none() && ac_params.file {
- q.path = Some(Literal::Regex(".".into()));
+ q.path = Some(Literal::Regex(".*".into()));
}
q
From cec32c7067ce10b609fa782ceb67ee4e497f073e Mon Sep 17 00:00:00 2001
From: anastasiia
Date: Fri, 10 Nov 2023 10:51:24 -0500
Subject: [PATCH 20/42] render parsed user query in history
---
.../Chat/ChatBody/AllCoversations/index.tsx | 9 +-
.../components/Chat/ChatBody/Conversation.tsx | 3 +
.../UserParsedQuery/PathChip.tsx | 31 +
.../UserParsedQuery/index.tsx | 29 +
.../ChatBody/ConversationMessage/index.tsx | 16 +-
client/src/locales/en.json | 3 +-
client/src/locales/es.json | 3 +-
client/src/locales/it.json | 761 +++++++++---------
client/src/locales/ja.json | 3 +-
client/src/locales/zh-CN.json | 3 +-
client/src/mappers/conversation.ts | 59 +-
client/src/types/api.ts | 29 +-
client/src/types/general.ts | 9 +
13 files changed, 566 insertions(+), 392 deletions(-)
create mode 100644 client/src/components/Chat/ChatBody/ConversationMessage/UserParsedQuery/PathChip.tsx
create mode 100644 client/src/components/Chat/ChatBody/ConversationMessage/UserParsedQuery/index.tsx
diff --git a/client/src/components/Chat/ChatBody/AllCoversations/index.tsx b/client/src/components/Chat/ChatBody/AllCoversations/index.tsx
index 4924683d49..3f9aa9bd89 100644
--- a/client/src/components/Chat/ChatBody/AllCoversations/index.tsx
+++ b/client/src/components/Chat/ChatBody/AllCoversations/index.tsx
@@ -21,7 +21,10 @@ import {
OpenChatHistoryItem,
} from '../../../../types/general';
import { conversationsCache } from '../../../../services/cache';
-import { mapLoadingSteps } from '../../../../mappers/conversation';
+import {
+ mapLoadingSteps,
+ mapUserQuery,
+} from '../../../../mappers/conversation';
import { LocaleContext } from '../../../../context/localeContext';
import { getDateFnsLocale } from '../../../../utils';
import ConversationListItem from './ConversationListItem';
@@ -63,9 +66,11 @@ const AllConversations = ({
resp.forEach((m) => {
// @ts-ignore
const userQuery = m.search_steps.find((s) => s.type === 'QUERY');
+ const parsedQuery = mapUserQuery(m);
conv.push({
author: ChatMessageAuthor.User,
- text: m.query?.target?.Plain || userQuery?.content?.query || '',
+ text: m.raw_query || userQuery?.content?.query || '',
+ parsedQuery,
isFromHistory: true,
});
conv.push({
diff --git a/client/src/components/Chat/ChatBody/Conversation.tsx b/client/src/components/Chat/ChatBody/Conversation.tsx
index 6346ab00b3..314d43f795 100644
--- a/client/src/components/Chat/ChatBody/Conversation.tsx
+++ b/client/src/components/Chat/ChatBody/Conversation.tsx
@@ -59,6 +59,9 @@ const Conversation = ({
isHistory={isHistory}
author={m.author}
message={m.text}
+ parsedQuery={
+ m.author === ChatMessageAuthor.Server ? undefined : m.parsedQuery
+ }
error={m.author === ChatMessageAuthor.Server ? m.error : ''}
showInlineFeedback={
m.author === ChatMessageAuthor.Server &&
diff --git a/client/src/components/Chat/ChatBody/ConversationMessage/UserParsedQuery/PathChip.tsx b/client/src/components/Chat/ChatBody/ConversationMessage/UserParsedQuery/PathChip.tsx
new file mode 100644
index 0000000000..a20f299c07
--- /dev/null
+++ b/client/src/components/Chat/ChatBody/ConversationMessage/UserParsedQuery/PathChip.tsx
@@ -0,0 +1,31 @@
+import { useMemo } from 'react';
+import { FolderClosed, ArrowOut } from '../../../../../icons';
+import FileIcon from '../../../../FileIcon';
+import { splitPath } from '../../../../../utils';
+
+type Props = {
+ path: string;
+};
+
+const PathChip = ({ path }: Props) => {
+ const isFolder = useMemo(() => path.endsWith('/'), [path]);
+ return (
+
+
+ {isFolder ? (
+
+ ) : (
+
+ )}
+
+ {isFolder ? path.replace(/\/$/, '') : splitPath(path).pop()}
+
+
+
+ );
+};
+
+export default PathChip;
diff --git a/client/src/components/Chat/ChatBody/ConversationMessage/UserParsedQuery/index.tsx b/client/src/components/Chat/ChatBody/ConversationMessage/UserParsedQuery/index.tsx
new file mode 100644
index 0000000000..16a7da613a
--- /dev/null
+++ b/client/src/components/Chat/ChatBody/ConversationMessage/UserParsedQuery/index.tsx
@@ -0,0 +1,29 @@
+import { memo } from 'react';
+import {
+ ParsedQueryType,
+ ParsedQueryTypeEnum,
+} from '../../../../../types/general';
+import PathChip from './PathChip';
+
+type Props = {
+ textQuery: string;
+ parsedQuery?: ParsedQueryType[];
+};
+
+const UserParsedQuery = ({ textQuery, parsedQuery }: Props) => {
+ return (
+
+ {parsedQuery
+ ? parsedQuery.map((p, i) =>
+ p.type === ParsedQueryTypeEnum.TEXT ? (
+ p.text
+ ) : p.type === ParsedQueryTypeEnum.PATH ? (
+
+ ) : null,
+ )
+ : textQuery}
+
+ );
+};
+
+export default memo(UserParsedQuery);
diff --git a/client/src/components/Chat/ChatBody/ConversationMessage/index.tsx b/client/src/components/Chat/ChatBody/ConversationMessage/index.tsx
index af21e0a507..eb34863e50 100644
--- a/client/src/components/Chat/ChatBody/ConversationMessage/index.tsx
+++ b/client/src/components/Chat/ChatBody/ConversationMessage/index.tsx
@@ -10,7 +10,11 @@ import {
WrenchAndScrewdriver,
} from '../../../../icons';
import { DeviceContext } from '../../../../context/deviceContext';
-import { ChatLoadingStep, ChatMessageAuthor } from '../../../../types/general';
+import {
+ ChatLoadingStep,
+ ChatMessageAuthor,
+ ParsedQueryType,
+} from '../../../../types/general';
import { ChatContext } from '../../../../context/chatContext';
import Button from '../../../Button';
import { LocaleContext } from '../../../../context/localeContext';
@@ -24,10 +28,12 @@ import {
} from '../../../../services/storage';
import MessageFeedback from './MessageFeedback';
import FileChip from './FileChip';
+import UserParsedQuery from './UserParsedQuery';
type Props = {
author: ChatMessageAuthor;
message?: string;
+ parsedQuery?: ParsedQueryType[];
error?: string;
threadId: string;
queryId: string;
@@ -61,6 +67,7 @@ const ConversationMessage = ({
onMessageEdit,
responseTimestamp,
singleFileExplanation,
+ parsedQuery,
}: Props) => {
const { t } = useTranslation();
const [isLoadingStepsShown, setLoadingStepsShown] = useState(
@@ -172,7 +179,7 @@ const ConversationMessage = ({
)}
- {message && (
+ {!!message && (
{author === ChatMessageAuthor.Server ? (
) : (
<>
-
{message}
+
{!isHistory && !!queryId && (