diff --git a/README.md b/README.md
index 390a8e3..6478f2e 100644
--- a/README.md
+++ b/README.md
@@ -89,6 +89,41 @@ The following options may be passed to the `highlight` function.
}
```
+**Get segments for custom highlighter**
+```js
+const { getSegments } = require('sql-highlight')
+
+const sqlString = "SELECT `id`, `username` FROM `users` WHERE `email` = 'test@example.com'"
+
+const segments = getSegments(sqlString)
+
+console.log(segments)
+```
+
+**Output:**
+```js
+[
+ { name: 'keyword', content: 'SELECT' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: '`id`' },
+ { name: 'special', content: ',' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: '`username`' },
+ { name: 'default', content: ' ' },
+ { name: 'keyword', content: 'FROM' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: '`users`' },
+ { name: 'default', content: ' ' },
+ { name: 'keyword', content: 'WHERE' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: '`email`' },
+ { name: 'default', content: ' ' },
+ { name: 'special', content: '=' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: "'test@example.com'" }
+]
+```
+
## Contributing
See the [contribution guidelines](CONTRIBUTING.md).
diff --git a/index.test.js b/index.test.js
index 39ebb41..caf7909 100644
--- a/index.test.js
+++ b/index.test.js
@@ -1,4 +1,4 @@
-const { highlight } = require('./lib')
+const { highlight, getSegments } = require('./lib')
const OPTIONS = {
colors: {
@@ -182,3 +182,49 @@ describe('html', () => {
.toBe('SELECT id FROM users')
})
})
+
+describe('getSegments', () => {
+ it('complex query', () => {
+ expect(getSegments("SELECT COUNT(id), `id`, `username` FROM `users` WHERE `email` = 'test@example.com' AND `foo` = 'BAR' OR 1=1"))
+ .toStrictEqual([
+ { name: 'keyword', content: 'SELECT' },
+ { name: 'default', content: ' ' },
+ { name: 'function', content: 'COUNT' },
+ { name: 'bracket', content: '(' },
+ { name: 'default', content: 'id' },
+ { name: 'bracket', content: ')' },
+ { name: 'special', content: ',' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: '`id`' },
+ { name: 'special', content: ',' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: '`username`' },
+ { name: 'default', content: ' ' },
+ { name: 'keyword', content: 'FROM' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: '`users`' },
+ { name: 'default', content: ' ' },
+ { name: 'keyword', content: 'WHERE' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: '`email`' },
+ { name: 'default', content: ' ' },
+ { name: 'special', content: '=' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: "'test@example.com'" },
+ { name: 'default', content: ' ' },
+ { name: 'keyword', content: 'AND' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: '`foo`' },
+ { name: 'default', content: ' ' },
+ { name: 'special', content: '=' },
+ { name: 'default', content: ' ' },
+ { name: 'string', content: "'BAR'" },
+ { name: 'default', content: ' ' },
+ { name: 'keyword', content: 'OR' },
+ { name: 'default', content: ' ' },
+ { name: 'number', content: '1' },
+ { name: 'special', content: '=' },
+ { name: 'number', content: '1' }
+ ])
+ })
+})
diff --git a/lib/index.d.ts b/lib/index.d.ts
index b5d8ada..c2da18e 100644
--- a/lib/index.d.ts
+++ b/lib/index.d.ts
@@ -12,6 +12,12 @@ declare module 'sql-highlight' {
clear: string;
};
}
+
+ export interface Segment {
+ name: string;
+ content: string;
+ }
+ export function getSegments(sqlString: string): Array;
export function highlight(sqlString: string, options?: HighlightOptions): string;
}
diff --git a/lib/index.js b/lib/index.js
index 245e97a..a62cd93 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -18,6 +18,8 @@ const DEFAULT_OPTIONS = {
const SPLIT_CHARS = '[^a-zA-Z_]'
+const DEFAULT_KEYWORD = 'default'
+
const highlighters = [
{
name: 'keyword',
@@ -47,9 +49,7 @@ const highlighters = [
}
]
-function highlight (sqlString, options) {
- options = Object.assign({}, DEFAULT_OPTIONS, options)
-
+function getSegments (sqlString) {
const matches = []
for (const hl of highlighters) {
@@ -57,7 +57,7 @@ function highlight (sqlString, options) {
// This is probably the one time when an assignment inside a condition makes sense
// eslint-disable-next-line no-cond-assign
- while (match = hl.regex.exec(sqlString)) {
+ while ((match = hl.regex.exec(sqlString))) {
let text = match[0]
let boringLength = 0
@@ -73,7 +73,10 @@ function highlight (sqlString, options) {
matches.push({
name: hl.name,
start: match.index + boringLength,
- length: (hl.trimEnd ? text.substr(0, text.length - hl.trimEnd) : text).length
+ length: (hl.trimEnd
+ ? text.substring(0, text.length - hl.trimEnd)
+ : text
+ ).length
})
}
}
@@ -81,42 +84,57 @@ function highlight (sqlString, options) {
const sortedMatches = matches.slice().sort((a, b) => a.start - b.start)
// filter/exclude nested matches (matches within the last match)
- const filteredMatches = []
+ const segments = []
let upperBound = 0
for (let i = 0; i < sortedMatches.length; i++) {
if (sortedMatches[i].start >= upperBound) {
- filteredMatches.push(sortedMatches[i])
+ if (sortedMatches[i].start > upperBound) {
+ segments.push({
+ name: DEFAULT_KEYWORD,
+ content: sqlString.substring(upperBound, sortedMatches[i].start)
+ })
+ }
+
+ segments.push({
+ name: sortedMatches[i].name,
+ content: sqlString.substring(
+ sortedMatches[i].start,
+ sortedMatches[i].start + sortedMatches[i].length
+ )
+ })
upperBound = sortedMatches[i].start + sortedMatches[i].length
}
}
- let highlighted = ''
-
- for (let i = 0; i < filteredMatches.length; i++) {
- const match = filteredMatches[i]
- const nextMatch = filteredMatches[i + 1]
-
- const stringMatch = sqlString.substr(match.start, match.length)
-
- if (options.html) {
- highlighted += ``
- highlighted += stringMatch
- highlighted += ''
- } else {
- highlighted += options.colors[match.name]
- highlighted += stringMatch
- highlighted += options.colors.clear
- }
- if (nextMatch) {
- highlighted += sqlString.substr(match.start + match.length, nextMatch.start - (match.start + match.length))
- } else if (sqlString.length > (match.start + match.length)) {
- highlighted += sqlString.substr(match.start + match.length)
- }
+ if (upperBound < sqlString.length - 1) {
+ segments.push({
+ name: DEFAULT_KEYWORD,
+ content: sqlString.substring(
+ upperBound,
+ upperBound + sqlString.length + 1
+ )
+ })
}
+ return segments
+}
- return highlighted
+function highlight (sqlString, options) {
+ options = Object.assign({}, DEFAULT_OPTIONS, options)
+
+ return getSegments(sqlString)
+ .map(({ name, content }) => {
+ if (name === DEFAULT_KEYWORD) {
+ return content
+ }
+ if (options.html) {
+ return `${content}`
+ }
+ return options.colors[name] + content + options.colors.clear
+ })
+ .join('')
}
module.exports = {
+ getSegments,
highlight
}