дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
fix(todo): only count real comment markers around TODO keywords (#1128)
The comment filter accepted `#`, `--`, `;` and `%` anywhere before the keyword, so `foo(); TODO`, `if (a % 2) TODO`, `i--; TODO`, URLs and strings produced items. Inline openers (// /* <!-- # -- {- (*) now count only at the line start or after whitespace; `*` `;` `%` and docstring quotes only at the line start. A TODO anywhere inside a comment still counts, as in IntelliJ. Lines the backend truncated keep the lenient rule.
Co-authored-by: qtvbwfn <[email protected]>
Цей коміт міститься в:
1 джерело
e5718d41d8
коміт
893cbede9c
2 files changed
+62
-5
No files matched your search
@@ -71,10 +71,20 @@ export function buildTodoQuery(s: TodoSettings, scope: SearchScope = { kind: "pr
|
||||
};
|
||||
}
|
||||
|
||||
const COMMENT_MARKER = /(\/\/|\/\*|^\s*\*|#|--|<!--|;|%|\{-|\(\*|"""|''')/;
|
||||
// Comment openers that count anywhere on the line, at its start or after whitespace: // /* <!-- # -- {- (*.
|
||||
// The whitespace rule drops `//` in URLs, `#` in strings, `f(*)` and `a * b`. Left over without knowing
|
||||
// the language: `"x # y"` in a string and a prefix `--y`.
|
||||
// `*` `;` `%` `"""` `'''` are code characters too, so they count only at the line start (LINE_START_MARKER).
|
||||
// IDEA's default pattern matches a TODO anywhere inside a comment, so the keyword need not lead it.
|
||||
const INLINE_MARKER = /(?:^|\s)(?:\/\/|\/\*|<!--|#|--|\{-|\(\*)/;
|
||||
const LINE_START_MARKER = /^\s*(?:\*|;|%|"""|''')/;
|
||||
// A long line the backend truncated (ide-search line.rs, `textOffset > 0`) doesn't start at the real line
|
||||
// start, so neither rule can apply: any marker counts, a false positive rather than a missed TODO.
|
||||
const LENIENT_MARKER = /(\/\/|\/\*|<!--|#|--|\{-|\(\*|\*|;|%|"""|''')/;
|
||||
|
||||
export function looksLikeComment(prefix: string): boolean {
|
||||
return COMMENT_MARKER.test(prefix);
|
||||
export function looksLikeComment(prefix: string, truncated = false): boolean {
|
||||
if (truncated) return LENIENT_MARKER.test(prefix);
|
||||
return INLINE_MARKER.test(prefix) || LINE_START_MARKER.test(prefix);
|
||||
}
|
||||
|
||||
function cleanText(text: string): string {
|
||||
@@ -96,7 +106,7 @@ export function itemsFromMatches(fm: FileMatches, s: TodoSettings): TodoItem[] {
|
||||
const matched = lm.text.slice(start, end);
|
||||
const p = patternFor(s, matched);
|
||||
if (!p) continue;
|
||||
if (s.commentsOnly && !looksLikeComment(lm.text.slice(0, start))) continue;
|
||||
if (s.commentsOnly && !looksLikeComment(lm.text.slice(0, start), lm.textOffset > 0)) continue;
|
||||
out.push({
|
||||
path: fm.path,
|
||||
line: lm.line,
|
||||
@@ -125,7 +135,7 @@ export function scanText(path: string, text: string, s: TodoSettings): TodoItem[
|
||||
}
|
||||
const p = patternFor(s, m[0]);
|
||||
if (!p) continue;
|
||||
if (s.commentsOnly && !looksLikeComment(text.slice(lineStart, idx))) continue;
|
||||
if (s.commentsOnly && !looksLikeComment(text.slice(lineStart, idx), false)) continue;
|
||||
out.push({ path, line, col: idx - lineStart, keyword: p.keyword.trim(), text: cleanText(m[0]) });
|
||||
}
|
||||
return out;
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
DEFAULT_TODO_SETTINGS,
|
||||
groupTodos,
|
||||
itemsFromMatches,
|
||||
looksLikeComment,
|
||||
mergeLive,
|
||||
scanText,
|
||||
todoRegexSource,
|
||||
@@ -22,6 +23,52 @@ import { todoDecorations } from "./highlight";
|
||||
|
||||
const S = DEFAULT_TODO_SETTINGS;
|
||||
|
||||
describe("comment markers", () => {
|
||||
const keep = [
|
||||
"// TODO",
|
||||
"/* TODO */",
|
||||
" * TODO", // Javadoc continuation
|
||||
"/** TODO: document this */", // doc comment led by the keyword
|
||||
"# TODO", // line-start Python/Shell
|
||||
"foo() # TODO", // trailing Python/Shell comment
|
||||
"SELECT 1 -- TODO", // trailing SQL comment
|
||||
"<!-- TODO -->",
|
||||
" ; TODO", // line-start ini comment
|
||||
" % TODO", // line-start LaTeX comment
|
||||
'""" TODO', // Python docstring
|
||||
"''' TODO",
|
||||
"x = 1; // TODO", // marker after whitespace, not just line start
|
||||
"x (* TODO *)", // OCaml trailing comment
|
||||
"x {- TODO -}", // Haskell trailing comment
|
||||
// IDEA's default pattern matches anywhere inside a comment, so mentions count too:
|
||||
"/** 业务类别:WORK_NOTICE 工作通知,TODO 待办。 */", // documents an enum value
|
||||
"// fixed the TODO bug",
|
||||
"/* see the TODO list */",
|
||||
];
|
||||
const drop = [
|
||||
"foo(); TODO", // statement separator
|
||||
"if (a % 2) TODO", // modulo
|
||||
"i--; TODO", // postfix decrement
|
||||
'let s = "a#b"; TODO', // # inside a string, no preceding space
|
||||
"x = a--b; TODO", // -- with no preceding space
|
||||
"see http://example.com TODO", // // inside a URL
|
||||
'"//"; TODO', // // inside a string
|
||||
"f(*) TODO", // OCaml-style wildcard
|
||||
"a * b; TODO", // infix multiplication
|
||||
];
|
||||
|
||||
it.each(keep)("keeps %s", (line) => expect(looksLikeComment(`${line.slice(0, line.indexOf("TODO"))}`)).toBe(true));
|
||||
it.each(drop)("drops %s", (line) => expect(looksLikeComment(`${line.slice(0, line.indexOf("TODO"))}`)).toBe(false));
|
||||
|
||||
it("falls back to the lenient rule on truncated lines", () => {
|
||||
// A 600-char shell line truncated by the backend: the window starts
|
||||
// mid-line, so the leading rule cannot apply — any marker counts.
|
||||
expect(looksLikeComment("very long code # remainder ", true)).toBe(true);
|
||||
expect(looksLikeComment("very long code % remainder ", true)).toBe(true);
|
||||
expect(looksLikeComment("i-- ", true)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("query", () => {
|
||||
it("builds the keyword regex from enabled patterns", () => {
|
||||
expect(todoRegexSource(S)).toBe("\\b(TODO|FIXME|HACK|XXX)\\b.*");
|
||||
|
||||
Reference in new issue
Заблокувати користувача