feat(editor): skip LIKE wildcards when double-clicking a string
Closes #8129
This commit is contained in:
3 files changed
+55
-15
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { EditorSelection, findClusterBreak, type EditorState, type SelectionRange } from "@codemirror/state";
|
||||
import type { EditorView, MouseSelectionStyle, ViewUpdate } from "@codemirror/view";
|
||||
import { analyzeSqlSemanticSelectionRanges, sqlStringContentRangeAt, type SqlSemanticSelectionAnalysis, type SqlSemanticSelectionOptions } from "@/lib/editor/sqlSemanticSelectionRanges";
|
||||
import { analyzeSqlSemanticSelectionRanges, sqlStringContentRangeAt, trimSqlStringWildcardBoundaries, type SqlSemanticSelectionAnalysis, type SqlSemanticSelectionOptions } from "@/lib/editor/sqlSemanticSelectionRanges";
|
||||
|
||||
export interface QueryEditorStringMouseSelectionOptions extends SqlSemanticSelectionOptions {
|
||||
language?: "sql" | "text";
|
||||
@@ -41,7 +41,9 @@ function groupAt(state: EditorState, position: PointerPosition): SelectionRange
|
||||
|
||||
function semanticOrDefaultRange(state: EditorState, position: PointerPosition, options: SqlSemanticSelectionOptions, analysis: SqlSemanticSelectionAnalysis): SelectionRange {
|
||||
const semantic = sqlStringContentRangeAt(state.doc.toString(), position.pos, position.assoc, options, analysis);
|
||||
return semantic ? EditorSelection.range(semantic.from, semantic.to) : groupAt(state, position);
|
||||
if (!semantic) return groupAt(state, position);
|
||||
const trimmed = trimSqlStringWildcardBoundaries(state.doc.toString(), semantic);
|
||||
return EditorSelection.range(trimmed.from, trimmed.to);
|
||||
}
|
||||
|
||||
export function createQueryEditorStringMouseSelection(view: EditorView, event: MouseEvent, options: QueryEditorStringMouseSelectionOptions = {}): MouseSelectionStyle | null {
|
||||
@@ -51,7 +53,8 @@ export function createQueryEditorStringMouseSelection(view: EditorView, event: M
|
||||
const semantic = sqlStringContentRangeAt(view.state.doc.toString(), start.pos, start.assoc, options, analysis);
|
||||
if (!semantic) return null;
|
||||
|
||||
let startRange = EditorSelection.range(semantic.from, semantic.to);
|
||||
const trimmed = trimSqlStringWildcardBoundaries(view.state.doc.toString(), semantic);
|
||||
let startRange = EditorSelection.range(trimmed.from, trimmed.to);
|
||||
let startSelection = view.state.selection;
|
||||
return {
|
||||
update(update: ViewUpdate) {
|
||||
|
||||
@@ -420,6 +420,22 @@ export function sqlStringContentRangeAt(doc: string, position: number, assoc: -1
|
||||
return range && range.from <= character && character < range.to ? range : null;
|
||||
}
|
||||
|
||||
// SQL LIKE wildcards that a double-click on a string literal should skip so the
|
||||
// selection lands on the searchable word instead of `%…%`/`_…_` (issue #8129).
|
||||
const SQL_STRING_WILDCARD_CHARACTERS = new Set(["%", "_"]);
|
||||
|
||||
// Shrinks a string-content range past leading/trailing LIKE wildcards. Used only
|
||||
// by the double-click selection path, not the semantic expand-selection index, so
|
||||
// widening still reaches the full quoted content. Returns the original range when
|
||||
// it has no wildcard boundary or is entirely wildcards (nothing meaningful left).
|
||||
export function trimSqlStringWildcardBoundaries(doc: string, range: SemanticSelectionRange): SemanticSelectionRange {
|
||||
let from = range.from;
|
||||
let to = range.to;
|
||||
while (from < to && SQL_STRING_WILDCARD_CHARACTERS.has(doc[from] ?? "")) from += 1;
|
||||
while (to > from && SQL_STRING_WILDCARD_CHARACTERS.has(doc[to - 1] ?? "")) to -= 1;
|
||||
return from < to ? { from, to } : range;
|
||||
}
|
||||
|
||||
function findStatement(context: SemanticSelectionContext, analysis: SqlSemanticSelectionAnalysis): PreparedStatementWindow | undefined {
|
||||
let low = 0;
|
||||
let high = analysis.statements.length;
|
||||
|
||||
Reference in new issue
Block a user