ronantakizawa/codereview-bench huggingface dataset:
Repository: TabbyML/tabby
File: clients/vscode/src/InlineCompletionProvider.ts (TypeScript)

Code before:
      ...eventData,
      eventId,
      viewId: id,
    };
    // await not required
    this.client.telemetry.postEvent(params);
  }

  /**
   * Get current display range with the current completion item
   * @return {Range | undefined} - The range with the current completion item
   */
  public getCurrentDisplayRange(): Range | undefined {
    const item = this.displayedCompletion?.completions.items[this.displayedCompletion.index];
    const range = item?.range;
    if (!range) {
      return undefined;
    }
    if (!item) {
      return undefined;
    }
    const length = (item.insertText as string).split("\n").length - 1; //remove current line count;
    const completionEnd = range.end.line + length;
    const completionRange = new Range(
      new Position(range.start.line, range.start.character),
      new Position(completionEnd == 0 ? completionEnd : completionEnd + 1, 0),
    );
    this.logger.info("current range with displayed completion item:", completionRange);

    return completionRange;
  }
}


Review comment (suggestion):
I believe we should also add `+ 1` when the completionEnd line index is 0.
```suggestion
      new Position(completionEnd + 1, 0),
```

Code after:
      ...eventData,
      eventId,
      viewId: id,
    };
    // await not required
    this.client.telemetry.postEvent(params);
  }

  /**
   * Calculate the edited range in the modified document as if the current completion item has been accepted.
   * @return {Range | undefined} - The range with the current completion item
   */
  public calcEditedRangeAfterAccept(): Range | undefined {
    const item = this.displayedCompletion?.completions.items[this.displayedCompletion.index];
    const range = item?.range;
    if (!range) {
      // FIXME: If the item has a null range, we can use current position and text length to calculate the result range
      return undefined;
    }
    if (!item) {
      return undefined;
    }
    const length = (item.insertText as string).split("\n").length - 1; //remove current line count;
    const completionRange = new Range(
      new Position(range.start.line, range.start.character),
      new Position(range.end.line + length + 1, 0),
    );
    this.logger.debug("Calculate edited range for displayed completion item:", completionRange);
    return completionRange;
  }
}
Repository: TabbyML/tabby
File: clients/vscode/src/InlineCompletionProvider.ts (TypeScript)

Code before:
      viewId: id,
    };
    // await not required
    this.client.telemetry.postEvent(params);
  }

  /**
   * Get current display range with the current completion item
   * @return {Range | undefined} - The range with the current completion item
   */
  public getCurrentDisplayRange(): Range | undefined {
    const item = this.displayedCompletion?.completions.items[this.displayedCompletion.index];
    const range = item?.range;
    if (!range) {
      return undefined;
    }
    if (!item) {
      return undefined;
    }
    const length = (item.insertText as string).split("\n").length - 1; //remove current line count;
    const completionEnd = range.end.line + length;
    const completionRange = new Range(
      new Position(range.start.line, range.start.character),
      new Position(completionEnd == 0 ? completionEnd : completionEnd + 1, 0),
    );
    this.logger.info("current range with displayed completion item:", completionRange);

    return completionRange;
  }
}


Review comment (bug):
```suggestion
    this.logger.debug("Calculate edited range for displayed completion item:", completionRange);
```

Code after:
      viewId: id,
    };
    // await not required
    this.client.telemetry.postEvent(params);
  }

  /**
   * Calculate the edited range in the modified document as if the current completion item has been accepted.
   * @return {Range | undefined} - The range with the current completion item
   */
  public calcEditedRangeAfterAccept(): Range | undefined {
    const item = this.displayedCompletion?.completions.items[this.displayedCompletion.index];
    const range = item?.range;
    if (!range) {
      // FIXME: If the item has a null range, we can use current position and text length to calculate the result range
      return undefined;
    }
    if (!item) {
      return undefined;
    }
    const length = (item.insertText as string).split("\n").length - 1; //remove current line count;
    const completionRange = new Range(
      new Position(range.start.line, range.start.character),
      new Position(range.end.line + length + 1, 0),
    );
    this.logger.debug("Calculate edited range for displayed completion item:", completionRange);
    return completionRange;
  }
}
Repository: TabbyML/tabby
File: clients/tabby-agent/src/codeCompletion/postprocess/removeDuplicateSuffixLines.test.ts (TypeScript)

Code before:
import { documentContext, inline, assertFilterResult } from "./testUtils";
import { removeDuplicateSuffixLines } from "./removeDuplicateSuffixLines";

describe("postprocess", () => {
  describe("removeDuplicateSuffixLines", () => {
    const filter = removeDuplicateSuffixLines();

    it("should remove duplicated suffix lines", async () => {
      const context = documentContext`
        function example() {
          const items = [
            ║
          ];
        }
      `;
      context.language = "javascript";
      const completion = inline`
            ├1,
            2,
            3,
            4,┤
      `;
      context.suffix = `
            4,
            5,
            6
          ];
        }
      `;
      const expected = inline`
            ├1,
            2,
            3,┤
      `;
      await assertFilterResult(filter, context, completion, expected);
    });

    it("should handle empty suffix", async () => {
      const context = documentContext`
        const value = ║

Review comment (suggestion):
```suggestion
      const context = documentContext`
        function example() {
          const items = [
            ║
            4,
            5,
            6
          ];
        }
      `;
```

Code after:
import { documentContext, inline, assertFilterResult } from "./testUtils";
import { removeDuplicateSuffixLines } from "./removeDuplicateSuffixLines";

describe("removeDuplicateSuffixLines", () => {
  const filter = removeDuplicateSuffixLines();

  it("should remove duplicated array item", async () => {
    const context = documentContext`
      const items = [
        ║
        { id: 2 },
        { id: 3 }
      ];
    `;
    const completion = inline`
      ├{ id: 1 },
      { id: 2 },┤
    `;
    const expected = inline`
      ├{ id: 1 },┤
    `;
    await assertFilterResult(filter, context, completion, expected);
  });

  it("should handle empty content after cursor", async () => {
    const context = documentContext`
      const a = ║
    `;
    const completion = inline`
      ├42;┤
    `;
    const expected = completion;
    await assertFilterResult(filter, context, completion, expected);
  });

  it("should remove duplicated comma items", async () => {
    const context = documentContext`
      function example() {
        const items = [
          ║
Repository: TabbyML/tabby
File: crates/tabby-common/src/api/code.rs (Rust)

Code before:
    pub chunk_id: String,

    pub body: String,
    pub filepath: String,
    pub git_url: String,
    pub language: String,
    pub start_line: usize,
}

#[derive(Error, Debug)]
pub enum CodeSearchError {
    #[error("index not ready")]
    NotReady,

    #[error(transparent)]
    QueryParserError(#[from] tantivy::query::QueryParserError),

    #[error(transparent)]
    TantivyError(#[from] tantivy::TantivyError),

    #[error(transparent)]
    Other(#[from] anyhow::Error),
}

pub struct CodeSearchQuery {
    // filepath in code search query always normalize to unix style.
    pub filepath: Option<String>,
    pub language: Option<String>,
    pub content: String,
    pub source_id: String,
}

impl CodeSearchQuery {
    pub fn new(
        filepath: Option<String>,
        language: Option<String>,
        content: String,
        source_id: String,
    ) -> Self {
        Self {
            filepath: normalize_to_unix_path(filepath).unwrap_or(None),
            language,
            content,
            source_id,
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CodeSearchParams {
    pub min_embedding_score: f32,

Review comment (suggestion):
```suggestion
    /// filepath in code search query always normalize to unix style.
```

Code after:
    pub chunk_id: String,

    pub body: String,
    pub filepath: String,
    pub git_url: String,
    pub language: String,
    pub start_line: usize,
}

#[derive(Error, Debug)]
pub enum CodeSearchError {
    #[error("index not ready")]
    NotReady,

    #[error(transparent)]
    QueryParserError(#[from] tantivy::query::QueryParserError),

    #[error(transparent)]
    TantivyError(#[from] tantivy::TantivyError),

    #[error(transparent)]
    Other(#[from] anyhow::Error),
}

pub struct CodeSearchQuery {
    /// filepath in code search query always normalize to unix style.
    pub filepath: Option<String>,
    pub language: Option<String>,
    pub content: String,
    pub source_id: String,
}

impl CodeSearchQuery {
    pub fn new(
        filepath: Option<String>,
        language: Option<String>,
        content: String,
        source_id: String,
    ) -> Self {
        Self {
            filepath: filepath.map(|path| normalize_to_unix_path(&path)),
            language,
            content,
            source_id,
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CodeSearchParams {
    pub min_embedding_score: f32,
Repository: TabbyML/tabby
File: crates/tabby-common/src/api/code.rs (Rust)

Code before:
impl Default for CodeSearchParams {
    fn default() -> Self {
        Self {
            min_embedding_score: 0.75,
            min_bm25_score: 8.0,
            min_rrf_score: 0.028,

            num_to_return: 20,
            num_to_score: 40,
        }
    }
}

#[async_trait]
pub trait CodeSearch: Send + Sync {
    async fn search_in_language(
        &self,
        query: CodeSearchQuery,
        params: CodeSearchParams,
    ) -> Result<CodeSearchResponse, CodeSearchError>;
}

/// Normalize the path form different platform to unix style path
pub fn normalize_to_unix_path(filepath: Option<String>) -> anyhow::Result<Option<String>> {
    Ok(filepath.map(|path| path.replace('\\', "/")))
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_relative_path_normalization() {
        let unix_test_cases = [
            ("./src/main.rs", "./src/main.rs"),
            (".\\src\\main.rs", "./src/main.rs"),
            ("../test/data.json", "../test/data.json"),
            ("..\\test\\data.json", "../test/data.json"),
            ("src/test/file.txt", "src/test/file.txt"),
            ("src\\test\\file.txt", "src/test/file.txt"),
        ];

        for (input, expected) in unix_test_cases {
            assert_eq!(
                normalize_to_unix_path(Some(input.to_string())).unwrap(),
                Some(expected.to_string()),
                "Failed to normalize path: {}",
                input
            );
        }
    }
}

Review comment (suggestion):
```suggestion
pub fn normalize_to_unix_path(filepath: &str) -> String {
    filepath.map(|path| path.replace('\\', "/"))
}
```

Code after:
impl Default for CodeSearchParams {
    fn default() -> Self {
        Self {
            min_embedding_score: 0.75,
            min_bm25_score: 8.0,
            min_rrf_score: 0.028,

            num_to_return: 20,
            num_to_score: 40,
        }
    }
}

#[async_trait]
pub trait CodeSearch: Send + Sync {
    async fn search_in_language(
        &self,
        query: CodeSearchQuery,
        params: CodeSearchParams,
    ) -> Result<CodeSearchResponse, CodeSearchError>;
}

/// Normalize the path form different platform to unix style path
pub fn normalize_to_unix_path(filepath: &str) -> String {
    filepath.replace('\\', "/")
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_relative_path_normalization() {
        let unix_test_cases = [
            ("./src/main.rs", "./src/main.rs"),
            (".\\src\\main.rs", "./src/main.rs"),
            ("../test/data.json", "../test/data.json"),
            ("..\\test\\data.json", "../test/data.json"),
            ("src/test/file.txt", "src/test/file.txt"),
            ("src\\test\\file.txt", "src/test/file.txt"),
        ];

        for (input, expected) in unix_test_cases {
            assert_eq!(
                normalize_to_unix_path(input),
                expected.to_string(),
                "Failed to normalize path: {}",
                input
            );
        }
    }
}
Repository: TabbyML/tabby
File: clients/tabby-chat-panel/src/index.ts (TypeScript)

Code before:
}

export interface ChatMessage {
  message: string

  // Client side context - displayed in user message
  selectContext?: Context

  // Client side contexts - displayed in assistant message
  relevantContext?: Array<Context>

  // Client side active selection context - displayed in assistant message
  activeContext?: Context
}

export function createClient(target: HTMLIFrameElement, api: ClientApi): ServerApi {
  return createThreadFromIframe(target, {
    expose: {
      navigate: api.navigate,
      refresh: api.refresh,
      onSubmitMessage: api.onSubmitMessage,
      onApplyInEditor: api.onApplyInEditor,
      onLoaded: api.onLoaded,
      onCopy: api.onCopy,
      onKeyboardEvent: api.onKeyboardEvent,
      onRenderLsp: api.onRenderLsp,
    },
  })
}

export function createServer(api: ServerApi): ClientApi {
  return createThreadFromInsideIframe({
    expose: {
      init: api.init,
      sendMessage: api.sendMessage,
      showError: api.showError,
      cleanError: api.cleanError,
      addRelevantContext: api.addRelevantContext,
      updateTheme: api.updateTheme,
      updateActiveSelection: api.updateActiveSelection,
    },
  })
}


Review comment (suggestion):
```suggestion
      onNavigateSymbol: api.onNavigateSymbol,
```

Code after:
  onCopy: (content: string) => void

  onKeyboardEvent: (type: 'keydown' | 'keyup' | 'keypress', event: KeyboardEventInit) => void

  // find symbol definition location by hint filepaths and keyword
  onLookupSymbol?: (hintFilepaths: string[], keyword: string) => Promise<SymbolInfo | undefined>
}

export interface ClientApi extends ClientApiMethods {
  // this is inner function cover by tabby-threads
  // the function doesn't need to expose to client but can call by client
  hasCapability: (method: keyof ClientApiMethods) => Promise<boolean>
}

export const clientApiKeys: (keyof ClientApiMethods)[] = [
  'navigate',
  'refresh',
  'onSubmitMessage',
  'onApplyInEditor',
  'onApplyInEditorV2',
  'onLoaded',
  'onCopy',
  'onKeyboardEvent',
]

export interface ChatMessage {
  message: string

  // Client side context - displayed in user message
  selectContext?: Context

  // Client side contexts - displayed in assistant message
  relevantContext?: Array<Context>

  // Client side active selection context - displayed in assistant message
  activeContext?: Context
}

export function createClient(target: HTMLIFrameElement, api: ClientApiMethods): ServerApi {
  return createThreadFromIframe(target, {
    expose: {
      navigate: api.navigate,
      refresh: api.refresh,
      onSubmitMessage: api.onSubmitMessage,
      onApplyInEditor: api.onApplyInEditor,
      onLoaded: api.onLoaded,
      onCopy: api.onCopy,
      onKeyboardEvent: api.onKeyboardEvent,
      onLookupSymbol: api.onLookupSymbol,
    },
  })
Repository: TabbyML/tabby
File: crates/tabby-common/src/api/structured_doc.rs (Rust)

Code before:
    /// * `source_ids`: Filter documents by source IDs, when empty, search all sources.
    async fn search(
        &self,
        source_ids: &[String],
        q: &str,
        limit: usize,
    ) -> Result<DocSearchResponse, DocSearchError>;
}

#[derive(Clone)]
pub struct DocSearchWebDocument {
    pub title: String,
    pub link: String,
    pub snippet: String,
}

#[derive(Clone)]
pub struct DocSearchIssueDocument {
    pub title: String,
    pub link: String,
    pub body: String,
    pub closed: bool,
}

#[derive(Clone)]
pub struct DocSearchPullRequest {
    pub title: String,
    pub link: String,
    pub body: String,
    pub diff: String,
    pub merged: bool,
}

pub trait FromTantivyDocument {
    fn from_tantivy_document(doc: &TantivyDocument, chunk: &TantivyDocument) -> Option<Self>
    where
        Self: Sized;
}

impl FromTantivyDocument for DocSearchDocument {
    fn from_tantivy_document(doc: &TantivyDocument, chunk: &TantivyDocument) -> Option<Self> {
        let schema = IndexSchema::instance();
        let kind = get_json_text_field(doc, schema.field_attributes, structured_doc::fields::KIND);

        match kind {
            "web" => {
                DocSearchWebDocument::from_tantivy_document(doc, chunk).map(DocSearchDocument::Web)
            }
            "issue" => DocSearchIssueDocument::from_tantivy_document(doc, chunk)
                .map(DocSearchDocument::Issue),
            "pull" => {

Review comment (suggestion):
```suggestion
pub struct DocSearchPullDocument {
```

Code after:
    /// * `source_ids`: Filter documents by source IDs, when empty, search all sources.
    async fn search(
        &self,
        source_ids: &[String],
        q: &str,
        limit: usize,
    ) -> Result<DocSearchResponse, DocSearchError>;
}

#[derive(Clone)]
pub struct DocSearchWebDocument {
    pub title: String,
    pub link: String,
    pub snippet: String,
}

#[derive(Clone)]
pub struct DocSearchIssueDocument {
    pub title: String,
    pub link: String,
    pub body: String,
    pub closed: bool,
}

#[derive(Clone)]
pub struct DocSearchPullDocument {
    pub title: String,
    pub link: String,
    pub body: String,
    pub diff: String,
    pub merged: bool,
}

pub trait FromTantivyDocument {
    fn from_tantivy_document(doc: &TantivyDocument, chunk: &TantivyDocument) -> Option<Self>
    where
        Self: Sized;
}

impl FromTantivyDocument for DocSearchDocument {
    fn from_tantivy_document(doc: &TantivyDocument, chunk: &TantivyDocument) -> Option<Self> {
        let schema = IndexSchema::instance();
        let kind = get_json_text_field(doc, schema.field_attributes, structured_doc::fields::KIND);

        match kind {
            "web" => {
                DocSearchWebDocument::from_tantivy_document(doc, chunk).map(DocSearchDocument::Web)
            }
            "issue" => DocSearchIssueDocument::from_tantivy_document(doc, chunk)
                .map(DocSearchDocument::Issue),
            "pull" => DocSearchPullDocument::from_tantivy_document(doc, chunk)
Repository: TabbyML/tabby
File: ee/tabby-ui/components/chat/chat.tsx (TypeScript)

Code before:
  return ctxList
}

export const Chat = React.forwardRef<ChatRef, ChatProps>(ChatRenderer)

function formatThreadRunErrorMessage(error: ExtendedCombinedError | undefined) {
  if (!error) return 'Failed to fetch'

  if (error.message === '401') {
    return 'Unauthorized'
  }

  if (
    some(error.graphQLErrors, o => o.extensions?.code === ERROR_CODE_NOT_FOUND)
  ) {
    return `The thread has expired, please click ${"'"}Clear${"'"} and try again.`
  }

  return error.message || 'Failed to fetch'
}

function isValidContextForCodeQuery(context: FileContext | undefined) {
  if (!context) return false

  const isUntitledFile =
    context.filepath.startsWith('untitled:Untitled-') &&
    !filename2prism(context.filepath)[0]

  return !isUntitledFile
}


Review comment (suggestion):
```suggestion
    context.filepath.startsWith('untitled:') &&
```

Code after:
  return ctxList
}

export const Chat = React.forwardRef<ChatRef, ChatProps>(ChatRenderer)

function formatThreadRunErrorMessage(error: ExtendedCombinedError | undefined) {
  if (!error) return 'Failed to fetch'

  if (error.message === '401') {
    return 'Unauthorized'
  }

  if (
    some(error.graphQLErrors, o => o.extensions?.code === ERROR_CODE_NOT_FOUND)
  ) {
    return `The thread has expired, please click ${"'"}Clear${"'"} and try again.`
  }

  return error.message || 'Failed to fetch'
}

function isValidContextForCodeQuery(context: FileContext | undefined) {
  if (!context) return false

  const isUntitledFile =
    context.filepath.startsWith('untitled:') &&
    !filename2prism(context.filepath)[0]

  return !isUntitledFile
}
Repository: TabbyML/tabby
File: ee/tabby-ui/app/chat/page.tsx (TypeScript)

Code before:
        dispatchKeyboardEvent('keydown', event)
      }
    }

    const onKeyUp = (event: KeyboardEvent) => {
      dispatchKeyboardEvent('keyup', event)
    }

    const onKeyPress = (event: KeyboardEvent) => {
      dispatchKeyboardEvent('keypress', event)
    }

    window.addEventListener('keydown', onKeyDown)
    window.addEventListener('keyup', onKeyUp)
    window.addEventListener('keypress', onKeyPress)
    return () => {
      window.removeEventListener('keydown', onKeyDown)
      window.removeEventListener('keyup', onKeyUp)
      window.removeEventListener('keypress', onKeyPress)
    }
  }, [server, client])

  const [serverCapabilities, setServerCapabilities] = useState(
    new Map<string, boolean>()
  )

  useEffect(() => {
    if (server) {
      server?.onLoaded({
        apiVersion: TABBY_CHAT_PANEL_API_VERSION
      })

      const checkCapabilities = async () => {
        const results = await Promise.all(
          clientApiKeys.map(async key => {
            const hasCapability = await server.hasCapability!(key)
            return [key, hasCapability] as [string, boolean]
          })
        )
        setServerCapabilities(new Map(results))
      }

      checkCapabilities()
    }
  }, [server])

  useLayoutEffect(() => {
    if (!chatLoaded) return
    if (
      width &&
      isInit &&

Review comment (suggestion):
instead of using a map, use explicit boolean field for ease of compiler check.

e.g
```
const supportsOnApplyInEditorV2 = useState(false);

...
```

Code after:
          dispatchKeyboardEvent('keydown', event)
        }
      } else {
        dispatchKeyboardEvent('keydown', event)
      }
    }

    const onKeyUp = (event: KeyboardEvent) => {
      dispatchKeyboardEvent('keyup', event)
    }

    const onKeyPress = (event: KeyboardEvent) => {
      dispatchKeyboardEvent('keypress', event)
    }

    window.addEventListener('keydown', onKeyDown)
    window.addEventListener('keyup', onKeyUp)
    window.addEventListener('keypress', onKeyPress)
    return () => {
      window.removeEventListener('keydown', onKeyDown)
      window.removeEventListener('keyup', onKeyUp)
      window.removeEventListener('keypress', onKeyPress)
    }
  }, [server, client])

  useEffect(() => {
    if (server) {
      server?.onLoaded({
        apiVersion: TABBY_CHAT_PANEL_API_VERSION
      })

      const checkCapabilities = async () => {
        server
          ?.hasCapability('onApplyInEditorV2')
          .then(setSupportsOnApplyInEditorV2)
      }

      checkCapabilities()
    }
  }, [server])

  useLayoutEffect(() => {
    if (!chatLoaded) return
    if (
      width &&
      isInit &&
      fetcherOptions &&
      !errorMessage &&
      !prevWidthRef.current
    ) {
      chatRef.current?.focus()