Commit 0bc314a4 by Steven Sheehy Committed by David

Feature: Case insensitive Loki search (#15948)

* Case insensitive Loki search
* Make Loki case insensitivity work with highlighting

Signed-off-by: Steven Sheehy <ssheehy@firescope.com>
parent c8b21025
import { findMatchesInText } from './text';
import { findMatchesInText, parseFlags } from './text';
describe('findMatchesInText()', () => {
it('gets no matches for when search and or line are empty', () => {
......@@ -32,4 +32,37 @@ describe('findMatchesInText()', () => {
expect(findMatchesInText('foo foo bar', '(')).toEqual([]);
expect(findMatchesInText('foo foo bar', '(foo|')).toEqual([]);
});
test('should parse and use flags', () => {
expect(findMatchesInText(' foo FOO bar ', '(?i)foo')).toEqual([
{ length: 3, start: 1, text: 'foo', end: 4 },
{ length: 3, start: 5, text: 'FOO', end: 8 },
]);
expect(findMatchesInText(' foo FOO bar ', '(?i)(?-i)foo')).toEqual([{ length: 3, start: 1, text: 'foo', end: 4 }]);
expect(findMatchesInText('FOO\nfoobar\nbar', '(?ims)^foo.')).toEqual([
{ length: 4, start: 0, text: 'FOO\n', end: 4 },
{ length: 4, start: 4, text: 'foob', end: 8 },
]);
expect(findMatchesInText('FOO\nfoobar\nbar', '(?ims)(?-smi)^foo.')).toEqual([]);
});
});
describe('parseFlags()', () => {
it('when no flags or text', () => {
expect(parseFlags('')).toEqual({ cleaned: '', flags: 'g' });
expect(parseFlags('(?is)')).toEqual({ cleaned: '', flags: 'gis' });
expect(parseFlags('foo')).toEqual({ cleaned: 'foo', flags: 'g' });
});
it('when flags present', () => {
expect(parseFlags('(?i)foo')).toEqual({ cleaned: 'foo', flags: 'gi' });
expect(parseFlags('(?ims)foo')).toEqual({ cleaned: 'foo', flags: 'gims' });
});
it('when flags cancel each other', () => {
expect(parseFlags('(?i)(?-i)foo')).toEqual({ cleaned: 'foo', flags: 'g' });
expect(parseFlags('(?i-i)foo')).toEqual({ cleaned: 'foo', flags: 'g' });
expect(parseFlags('(?is)(?-ims)foo')).toEqual({ cleaned: 'foo', flags: 'g' });
expect(parseFlags('(?i)(?-i)(?i)foo')).toEqual({ cleaned: 'foo', flags: 'gi' });
});
});
......@@ -22,10 +22,10 @@ export function findMatchesInText(haystack: string, needle: string): TextMatch[]
return [];
}
const matches = [];
const cleaned = cleanNeedle(needle);
const { cleaned, flags } = parseFlags(cleanNeedle(needle));
let regexp: RegExp;
try {
regexp = new RegExp(`(?:${cleaned})`, 'g');
regexp = new RegExp(`(?:${cleaned})`, flags);
} catch (error) {
return matches;
}
......@@ -44,6 +44,35 @@ export function findMatchesInText(haystack: string, needle: string): TextMatch[]
return matches;
}
const CLEAR_FLAG = '-';
const FLAGS_REGEXP = /\(\?([ims-]+)\)/g;
/**
* Converts any mode modifers in the text to the Javascript equivalent flag
*/
export function parseFlags(text: string): { cleaned: string; flags: string } {
const flags: Set<string> = new Set(['g']);
const cleaned = text.replace(FLAGS_REGEXP, (str, group) => {
const clearAll = group.startsWith(CLEAR_FLAG);
for (let i = 0; i < group.length; ++i) {
const flag = group.charAt(i);
if (clearAll || group.charAt(i - 1) === CLEAR_FLAG) {
flags.delete(flag);
} else if (flag !== CLEAR_FLAG) {
flags.add(flag);
}
}
return ''; // Remove flag from text
});
return {
cleaned: cleaned,
flags: Array.from(flags).join(''),
};
}
const XSSWL = Object.keys(xss.whiteList).reduce((acc, element) => {
acc[element] = xss.whiteList[element].concat(['class', 'style']);
return acc;
......
......@@ -11,7 +11,7 @@ describe('parseQuery', () => {
it('returns regexp for strings without query', () => {
expect(parseQuery('test')).toEqual({
query: '',
regexp: 'test',
regexp: '(?i)test',
});
});
......@@ -25,14 +25,14 @@ describe('parseQuery', () => {
it('returns query for strings with query and search string', () => {
expect(parseQuery('x {foo="bar"}')).toEqual({
query: '{foo="bar"}',
regexp: 'x',
regexp: '(?i)x',
});
});
it('returns query for strings with query and regexp', () => {
expect(parseQuery('{foo="bar"} x|y')).toEqual({
query: '{foo="bar"}',
regexp: 'x|y',
regexp: '(?i)x|y',
});
});
......@@ -46,11 +46,11 @@ describe('parseQuery', () => {
it('returns query and regexp with quantifiers', () => {
expect(parseQuery('{foo="bar"} \\.java:[0-9]{1,5}')).toEqual({
query: '{foo="bar"}',
regexp: '\\.java:[0-9]{1,5}',
regexp: '(?i)\\.java:[0-9]{1,5}',
});
expect(parseQuery('\\.java:[0-9]{1,5} {foo="bar"}')).toEqual({
query: '{foo="bar"}',
regexp: '\\.java:[0-9]{1,5}',
regexp: '(?i)\\.java:[0-9]{1,5}',
});
});
});
const selectorRegexp = /(?:^|\s){[^{]*}/g;
const caseInsensitive = '(?i)'; // Golang mode modifier for Loki, doesn't work in JavaScript
export function parseQuery(input: string) {
input = input || '';
const match = input.match(selectorRegexp);
......@@ -10,6 +11,9 @@ export function parseQuery(input: string) {
regexp = input.replace(selectorRegexp, '').trim();
}
if (regexp) {
regexp = caseInsensitive + regexp;
}
return { query, regexp };
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment