mirror of
https://github.com/Silverquark/dance.git
synced 2024-09-11 18:48:00 +03:00
Implemented searching.
This commit is contained in:
parent
a7619193d0
commit
46cadc7ca9
@ -116,8 +116,10 @@ Most (but not all) commands defined in [`commands`][commands] are implemented.
|
||||
- [X] Indent.
|
||||
- [X] Dedent.
|
||||
- [X] Change case.
|
||||
- [ ] Search.
|
||||
- [ ] History.
|
||||
- [X] Search.
|
||||
- [ ] History:
|
||||
- [X] Undo / redo.
|
||||
- [X] Forward / backward.
|
||||
- [ ] Macros.
|
||||
- [ ] Registers.
|
||||
|
||||
|
@ -1,13 +1,15 @@
|
||||
import * as vscode from 'vscode'
|
||||
|
||||
import { registerCommand, Command } from '.'
|
||||
|
||||
|
||||
// registerCommand(Command.historyUndo, (editor, state) => {
|
||||
registerCommand(Command.historyUndo, () => {
|
||||
return vscode.commands.executeCommand('undo')
|
||||
})
|
||||
|
||||
// })
|
||||
|
||||
// registerCommand(Command.historyRedo, (editor, state) => {
|
||||
|
||||
// })
|
||||
registerCommand(Command.historyRedo, () => {
|
||||
return vscode.commands.executeCommand('redo')
|
||||
})
|
||||
|
||||
// registerCommand(Command.historyBackward, (editor, state) => {
|
||||
|
||||
|
@ -4,18 +4,254 @@ import * as vscode from 'vscode'
|
||||
import { registerCommand, Command } from '.'
|
||||
|
||||
|
||||
// registerCommand(Command.search, editor => {
|
||||
function isMultilineRegExp(regex: string) {
|
||||
const len = regex.length
|
||||
let negate = false
|
||||
|
||||
// })
|
||||
for (let i = 0; i < len; i++) {
|
||||
const ch = regex[i]
|
||||
|
||||
// registerCommand(Command.searchBackwards, editor => {
|
||||
if (negate) {
|
||||
if (ch === ']') {
|
||||
negate = false
|
||||
} else if (ch === '\\') {
|
||||
if (regex[i + 1] === 'S')
|
||||
return true
|
||||
|
||||
// })
|
||||
i++ // Ignore next character
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if (ch === '[' && regex[i + 1] === '^') {
|
||||
negate = true
|
||||
i++
|
||||
} else if (ch === '\\') {
|
||||
if (regex[i + 1] === 's' || regex[i + 1] === 'n')
|
||||
return true
|
||||
|
||||
// registerCommand(Command.searchExtend, editor => {
|
||||
i++ // Ignore next character
|
||||
}
|
||||
}
|
||||
|
||||
// })
|
||||
return false
|
||||
}
|
||||
|
||||
// registerCommand(Command.searchBackwardsExtend, editor => {
|
||||
function findPosition(text: string, position: vscode.Position, offset: number, add = 0) {
|
||||
let { line, character } = position
|
||||
|
||||
// })
|
||||
for (let i = 0; i < offset; i++) {
|
||||
const ch = text[i + add]
|
||||
|
||||
if (ch === '\n') {
|
||||
line++
|
||||
character = 0
|
||||
} else if (ch === '\r') {
|
||||
line++
|
||||
character = 0
|
||||
i++
|
||||
} else {
|
||||
character++
|
||||
}
|
||||
}
|
||||
|
||||
return new vscode.Position(line, character)
|
||||
}
|
||||
|
||||
function findPositionBackward(text: string, position: vscode.Position, offset: number) {
|
||||
let { line, character } = position
|
||||
|
||||
for (let i = text.length - 1; i >= offset; i--) {
|
||||
const ch = text[i]
|
||||
|
||||
if (ch === '\n') {
|
||||
line--
|
||||
character = 0
|
||||
} else if (ch === '\r') {
|
||||
line--
|
||||
character = 0
|
||||
i--
|
||||
} else {
|
||||
character--
|
||||
}
|
||||
}
|
||||
|
||||
if (line !== position.line) {
|
||||
// The 'character' is actually the number of characters until the end of the line,
|
||||
// so we have to find the start of the current line and do a diff
|
||||
let startOfLine = text.lastIndexOf('\n', offset)
|
||||
|
||||
if (startOfLine === -1)
|
||||
startOfLine = 0
|
||||
|
||||
character = offset - startOfLine
|
||||
}
|
||||
|
||||
return new vscode.Position(line, character - 1)
|
||||
}
|
||||
|
||||
// TODO: Should search strings be limited to the range between the current selection
|
||||
// and the previous/next selection, or only to the bounds of the document?
|
||||
|
||||
function search(document: vscode.TextDocument, start: vscode.Position, regex: RegExp) {
|
||||
if (regex.multiline) {
|
||||
const text = document.getText(document.lineAt(document.lineCount - 1).range.with(start))
|
||||
const match = regex.exec(text)
|
||||
|
||||
if (match === null)
|
||||
return undefined
|
||||
|
||||
const startPos = findPosition(text, start, match.index),
|
||||
endPos = findPosition(text, startPos, match[0].length, match.index)
|
||||
|
||||
return new vscode.Selection(startPos, endPos)
|
||||
} else {
|
||||
{
|
||||
// Custom processing for first line
|
||||
const line = document.lineAt(start.line),
|
||||
match = regex.exec(line.text.substr(start.character))
|
||||
|
||||
if (match !== null)
|
||||
return new vscode.Selection(
|
||||
new vscode.Position(start.line, start.character + match.index),
|
||||
new vscode.Position(start.line, start.character + match.index + match[0].length),
|
||||
)
|
||||
}
|
||||
|
||||
for (let i = start.line + 1; i < document.lineCount; i++) {
|
||||
const line = document.lineAt(i),
|
||||
match = regex.exec(line.text)
|
||||
|
||||
if (match === null)
|
||||
continue
|
||||
|
||||
return new vscode.Selection(
|
||||
new vscode.Position(i, match.index),
|
||||
new vscode.Position(i, match.index + match[0].length),
|
||||
)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function execFromEnd(regex: RegExp, input: string) {
|
||||
let match = regex.exec(input)
|
||||
|
||||
if (match === null || match[0].length === 0)
|
||||
return null
|
||||
|
||||
for (;;) {
|
||||
const newMatch = regex.exec(input)
|
||||
|
||||
if (newMatch === null)
|
||||
return match
|
||||
if (newMatch[0].length === 0)
|
||||
return null
|
||||
|
||||
match = newMatch
|
||||
}
|
||||
}
|
||||
|
||||
function searchBackward(document: vscode.TextDocument, end: vscode.Position, regex: RegExp) {
|
||||
if (regex.multiline) {
|
||||
const text = document.getText(new vscode.Range(new vscode.Position(0, 0), end))
|
||||
const match = execFromEnd(regex, text)
|
||||
|
||||
if (match === null)
|
||||
return undefined
|
||||
|
||||
const startPos = findPositionBackward(text, end, match.index),
|
||||
endPos = findPosition(text, startPos, match[0].length, match.index)
|
||||
|
||||
return new vscode.Selection(startPos, endPos)
|
||||
} else {
|
||||
{
|
||||
// Custom processing for first line
|
||||
const line = document.lineAt(end.line),
|
||||
match = execFromEnd(regex, line.text.substr(0, end.character))
|
||||
|
||||
if (match !== null)
|
||||
return new vscode.Selection(
|
||||
new vscode.Position(end.line, match.index),
|
||||
new vscode.Position(end.line, match.index + match[0].length),
|
||||
)
|
||||
}
|
||||
|
||||
for (let i = end.line - 1; i >= 0; i--) {
|
||||
const line = document.lineAt(i),
|
||||
match = execFromEnd(regex, line.text)
|
||||
|
||||
if (match === null)
|
||||
continue
|
||||
|
||||
return new vscode.Selection(
|
||||
new vscode.Position(i, match.index),
|
||||
new vscode.Position(i, match.index + match[0].length),
|
||||
)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function registerSearchCommand(command: Command, backward: boolean, extend: boolean) {
|
||||
registerCommand(command, async editor => {
|
||||
const initialSelections = editor.selections
|
||||
|
||||
const result = await vscode.window.showInputBox({
|
||||
prompt: 'Search RegExp',
|
||||
|
||||
validateInput(input) {
|
||||
if (input.length === 0)
|
||||
return 'RegExp cannot be empty.'
|
||||
|
||||
let regex: RegExp
|
||||
let flags = (isMultilineRegExp(input) ? 'm' : '') + (backward ? 'g' : '')
|
||||
|
||||
try {
|
||||
regex = new RegExp(input, flags)
|
||||
} catch {
|
||||
return 'Invalid ECMA RegExp.'
|
||||
}
|
||||
|
||||
// TODO: For subsequent searches, first try to match at start of previous
|
||||
// match by adding a ^, and then fallback to the default search routine
|
||||
if (extend) {
|
||||
if (backward)
|
||||
editor.selections = initialSelections.map(selection => {
|
||||
const newSelection = searchBackward(editor.document, selection.anchor, regex)
|
||||
|
||||
return newSelection === undefined
|
||||
? selection
|
||||
: new vscode.Selection(newSelection.start, selection.end)
|
||||
})
|
||||
else
|
||||
editor.selections = initialSelections.map(selection => {
|
||||
const newSelection = search(editor.document, selection.active, regex)
|
||||
|
||||
return newSelection === undefined
|
||||
? selection
|
||||
: new vscode.Selection(selection.start, newSelection.end)
|
||||
})
|
||||
} else {
|
||||
editor.selections = initialSelections.map(selection => {
|
||||
return (backward
|
||||
? searchBackward(editor.document, selection.anchor, regex)
|
||||
: search(editor.document, selection.active, regex))
|
||||
|| selection
|
||||
})
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
|
||||
if (result === undefined)
|
||||
editor.selections = initialSelections
|
||||
})
|
||||
}
|
||||
|
||||
registerSearchCommand(Command.search , false, false)
|
||||
registerSearchCommand(Command.searchBackwards , true , false)
|
||||
registerSearchCommand(Command.searchExtend , false, true )
|
||||
registerSearchCommand(Command.searchBackwardsExtend, true , true )
|
||||
|
@ -19,7 +19,7 @@ export function promptRegex(flags?: string) {
|
||||
|
||||
return undefined
|
||||
} catch {
|
||||
return 'The given RegExp is not a valid ECMA RegExp.'
|
||||
return 'Invalid ECMA RegExp.'
|
||||
}
|
||||
}
|
||||
}).then(x => x === undefined ? undefined : new RegExp(x, flags))
|
||||
|
@ -41,10 +41,20 @@ export function getOppositePosition(document: vscode.TextDocument, text: string,
|
||||
}
|
||||
}
|
||||
|
||||
export function getOppositePositionFromString(document: string, text: string, position: vscode.Position, isStart: boolean) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `Selection` starting at the given position, and ending at the end of the given text.
|
||||
*/
|
||||
export function getSelectionFromStart(document: vscode.TextDocument, text: string, position: vscode.Position) {
|
||||
return new vscode.Selection(position, getOppositePosition(document, text, position, true))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `Selection` ending at the given position, and starting at the start of the given text.
|
||||
*/
|
||||
export function getSelectionFromEnd(document: vscode.TextDocument, text: string, position: vscode.Position) {
|
||||
return new vscode.Selection(getOppositePosition(document, text, position, false), position)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user