diff --git a/src/actions/commands/actions.ts b/src/actions/commands/actions.ts index e7cc20391..91bc9f322 100644 --- a/src/actions/commands/actions.ts +++ b/src/actions/commands/actions.ts @@ -2956,7 +2956,7 @@ class DecrementNumberStaircaseAction extends IncrementDecrementNumberAction { } @RegisterAction -class CommandUnicodeName extends BaseCommand { +export class CommandUnicodeName extends BaseCommand { modes = [Mode.Normal]; keys = ['g', 'a']; override runsOnceForEveryCursor() { diff --git a/src/cmd_line/commands/ascii.ts b/src/cmd_line/commands/ascii.ts new file mode 100644 index 000000000..9bebe093a --- /dev/null +++ b/src/cmd_line/commands/ascii.ts @@ -0,0 +1,9 @@ +import { CommandUnicodeName } from '../../actions/commands/actions'; +import { VimState } from '../../state/vimState'; +import { ExCommand } from '../../vimscript/exCommand'; + +export class AsciiCommand extends ExCommand { + async execute(vimState: VimState): Promise { + await new CommandUnicodeName().exec(vimState.cursorStopPosition, vimState); + } +} diff --git a/src/cmd_line/commands/copy.ts b/src/cmd_line/commands/copy.ts index 84ca33647..3436778c5 100644 --- a/src/cmd_line/commands/copy.ts +++ b/src/cmd_line/commands/copy.ts @@ -67,7 +67,7 @@ export class CopyCommand extends ExCommand { } public override async executeWithRange(vimState: VimState, range: LineRange): Promise { - const { start, end } = range.resolve(vimState)!; + const { start, end } = range.resolve(vimState); this.copyLines(vimState, start, end); } } diff --git a/src/cmd_line/commands/deleteRange.ts b/src/cmd_line/commands/deleteRange.ts index e996b5c57..751ab5c96 100644 --- a/src/cmd_line/commands/deleteRange.ts +++ b/src/cmd_line/commands/deleteRange.ts @@ -94,7 +94,7 @@ export class DeleteRangeCommand extends ExCommand { * Ex. if two lines are VisualLine highlighted, :<,>d3 will :d3 * from the end of the selected lines. */ - const { start, end } = range.resolve(vimState)!; + const { start, end } = range.resolve(vimState); if (this.arguments.count) { vimState.cursorStartPosition = new Position(end, 0); await this.execute(vimState); diff --git a/src/cmd_line/commands/gotoLine.ts b/src/cmd_line/commands/gotoLine.ts index 5cf593fa9..574acdd28 100644 --- a/src/cmd_line/commands/gotoLine.ts +++ b/src/cmd_line/commands/gotoLine.ts @@ -9,7 +9,7 @@ export class GotoLineCommand extends ExCommand { public override async executeWithRange(vimState: VimState, range: LineRange): Promise { vimState.cursorStartPosition = vimState.cursorStopPosition = vimState.cursorStopPosition - .with({ line: range.resolve(vimState)!.end }) + .with({ line: range.resolve(vimState).end }) .obeyStartOfLine(vimState.document); } } diff --git a/src/cmd_line/commands/print.ts b/src/cmd_line/commands/print.ts new file mode 100644 index 000000000..b1a5a93bd --- /dev/null +++ b/src/cmd_line/commands/print.ts @@ -0,0 +1,54 @@ +import { Parser, succeed } from 'parsimmon'; +import { VimState } from '../../state/vimState'; +import { StatusBar } from '../../statusBar'; +import { ExCommand } from '../../vimscript/exCommand'; +import { Address, LineRange } from '../../vimscript/lineRange'; + +type PrintArgs = { + printNumbers: boolean; + printText: boolean; +}; + +// TODO: `:l[ist]` is more than an alias +// TODO: `:z` +export class PrintCommand extends ExCommand { + // TODO: Print {count} and [flags] + public static readonly argParser = (args: { + printNumbers: boolean; + printText: boolean; + }): Parser => succeed(new PrintCommand(args)); + + private args: PrintArgs; + constructor(args: PrintArgs) { + super(); + this.args = args; + } + + async execute(vimState: VimState): Promise { + // TODO: Wrong default for `:=` + this.executeWithRange(vimState, new LineRange(new Address({ type: 'current_line' }))); + } + + override async executeWithRange(vimState: VimState, range: LineRange): Promise { + const { end } = range.resolve(vimState); + + // For now, we just print the last line. + // TODO: Create a dynamic document if there's more than one line? + const line = vimState.document.lineAt(end); + let output: string; + if (this.args.printNumbers) { + if (this.args.printText) { + output = `${line.lineNumber + 1} ${line.text}`; + } else { + output = `${line.lineNumber + 1}`; + } + } else { + if (this.args.printText) { + output = `${line.text}`; + } else { + output = ''; + } + } + StatusBar.setText(vimState, output); + } +} diff --git a/src/cmd_line/commands/put.ts b/src/cmd_line/commands/put.ts index 388c8b089..595929099 100644 --- a/src/cmd_line/commands/put.ts +++ b/src/cmd_line/commands/put.ts @@ -57,7 +57,7 @@ export class PutExCommand extends ExCommand { } override async executeWithRange(vimState: VimState, range: LineRange): Promise { - const { end } = range.resolve(vimState)!; + const { end } = range.resolve(vimState); await this.doPut(vimState, new Position(end, 0).getLineEnd()); } } diff --git a/src/cmd_line/commands/sort.ts b/src/cmd_line/commands/sort.ts index 371e57d76..cb2ca5635 100644 --- a/src/cmd_line/commands/sort.ts +++ b/src/cmd_line/commands/sort.ts @@ -95,7 +95,7 @@ export class SortCommand extends ExCommand { } override async executeWithRange(vimState: VimState, range: LineRange): Promise { - const { start, end } = range.resolve(vimState)!; + const { start, end } = range.resolve(vimState); await this.sortLines(vimState, start, end); } diff --git a/src/cmd_line/commands/substitute.ts b/src/cmd_line/commands/substitute.ts index be60c2904..3b3772c2a 100644 --- a/src/cmd_line/commands/substitute.ts +++ b/src/cmd_line/commands/substitute.ts @@ -412,7 +412,7 @@ export class SubstituteCommand extends ExCommand { } override async executeWithRange(vimState: VimState, range: LineRange): Promise { - let { start, end } = range.resolve(vimState)!; + let { start, end } = range.resolve(vimState); if (this.arguments.count && this.arguments.count >= 0) { start = end; diff --git a/src/cmd_line/commands/terminal.ts b/src/cmd_line/commands/terminal.ts new file mode 100644 index 000000000..bb6c7e173 --- /dev/null +++ b/src/cmd_line/commands/terminal.ts @@ -0,0 +1,12 @@ +import { Parser, succeed } from 'parsimmon'; +import * as vscode from 'vscode'; +import { VimState } from '../../state/vimState'; +import { ExCommand } from '../../vimscript/exCommand'; + +export class TerminalCommand extends ExCommand { + public static readonly argParser: Parser = succeed(new TerminalCommand()); + + async execute(vimState: VimState): Promise { + await vscode.commands.executeCommand('workbench.action.createTerminalEditor'); + } +} diff --git a/src/cmd_line/commands/yank.ts b/src/cmd_line/commands/yank.ts index 2c9ae4dce..6540130c2 100644 --- a/src/cmd_line/commands/yank.ts +++ b/src/cmd_line/commands/yank.ts @@ -68,7 +68,7 @@ export class YankCommand extends ExCommand { * Ex. if two lines are VisualLine highlighted, :<,>y3 will :y3 * from the end of the selected lines. */ - const { start, end } = range.resolve(vimState)!; + const { start, end } = range.resolve(vimState); if (this.arguments.count) { vimState.cursorStartPosition = new Position(end, 0); await this.execute(vimState); diff --git a/src/vimscript/exCommandParser.ts b/src/vimscript/exCommandParser.ts index f4a52055a..31c8e04b0 100644 --- a/src/vimscript/exCommandParser.ts +++ b/src/vimscript/exCommandParser.ts @@ -1,4 +1,5 @@ import { all, alt, optWhitespace, Parser, regexp, seq, string, succeed } from 'parsimmon'; +import { AsciiCommand } from '../cmd_line/commands/ascii'; import { BangCommand } from '../cmd_line/commands/bang'; import { BufferDeleteCommand } from '../cmd_line/commands/bufferDelete'; import { CloseCommand } from '../cmd_line/commands/close'; @@ -14,6 +15,7 @@ import { ClearJumpsCommand, JumpsCommand } from '../cmd_line/commands/jumps'; import { DeleteMarksCommand, MarksCommand } from '../cmd_line/commands/marks'; import { NohlCommand } from '../cmd_line/commands/nohl'; import { OnlyCommand } from '../cmd_line/commands/only'; +import { PrintCommand } from '../cmd_line/commands/print'; import { PutExCommand } from '../cmd_line/commands/put'; import { QuitCommand } from '../cmd_line/commands/quit'; import { ReadCommand } from '../cmd_line/commands/read'; @@ -24,6 +26,7 @@ import { SmileCommand } from '../cmd_line/commands/smile'; import { SortCommand } from '../cmd_line/commands/sort'; import { SubstituteCommand } from '../cmd_line/commands/substitute'; import { TabCommand } from '../cmd_line/commands/tab'; +import { TerminalCommand } from '../cmd_line/commands/terminal'; import { UndoCommand } from '../cmd_line/commands/undo'; import { VsCodeCommand } from '../cmd_line/commands/vscode'; import { WallCommand } from '../cmd_line/commands/wall'; @@ -50,11 +53,12 @@ type ArgParser = Parser; export const builtinExCommands: ReadonlyArray<[[string, string], ArgParser | undefined]> = [ [['', ''], succeed(new GotoLineCommand())], [['!', ''], BangCommand.argParser], - [['#', ''], undefined], + [['#', ''], PrintCommand.argParser({ printNumbers: true, printText: true })], + // TODO: Ignore #! (shebang) [['&', ''], undefined], [['*', ''], undefined], [['<', ''], undefined], - [['=', ''], undefined], + [['=', ''], PrintCommand.argParser({ printNumbers: true, printText: false })], [['>', ''], undefined], [['@', ''], undefined], [['@@', ''], undefined], @@ -74,7 +78,7 @@ export const builtinExCommands: ReadonlyArray<[[string, string], ArgParser | und [['argg', 'lobal'], undefined], [['argl', 'ocal'], undefined], [['argu', 'ment'], undefined], - [['as', 'cii'], undefined], + [['as', 'cii'], succeed(new AsciiCommand())], [['au', 'tocmd'], undefined], [['aug', 'roup'], undefined], [['aun', 'menu'], undefined], @@ -267,7 +271,7 @@ export const builtinExCommands: ReadonlyArray<[[string, string], ArgParser | und [['keepa', 'lt'], undefined], [['keepj', 'umps'], undefined], [['keepp', 'atterns'], undefined], - [['l', 'ist'], undefined], + [['l', 'ist'], PrintCommand.argParser({ printNumbers: false, printText: true })], [['lN', 'ext'], undefined], [['lNf', 'ile'], undefined], [['la', 'st'], undefined], @@ -359,7 +363,7 @@ export const builtinExCommands: ReadonlyArray<[[string, string], ArgParser | und [['noreme', 'nu'], undefined], [['norm', 'al'], undefined], [['nos', 'wapfile'], undefined], - [['nu', 'mber'], undefined], + [['nu', 'mber'], PrintCommand.argParser({ printNumbers: true, printText: true })], [['nun', 'map'], undefined], [['nunme', 'nu'], undefined], [['ol', 'dfiles'], undefined], @@ -373,7 +377,7 @@ export const builtinExCommands: ReadonlyArray<[[string, string], ArgParser | und [['ou', 'nmap'], undefined], [['ounme', 'nu'], undefined], [['ow', 'nsyntax'], undefined], - [['p', 'rint'], undefined], + [['p', 'rint'], PrintCommand.argParser({ printNumbers: false, printText: true })], [['pa', 'ckadd'], undefined], [['packl', 'oadall'], undefined], [['pc', 'lose'], undefined], @@ -522,7 +526,7 @@ export const builtinExCommands: ReadonlyArray<[[string, string], ArgParser | und [['tags', ''], undefined], [['tc', 'd'], undefined], [['tch', 'dir'], undefined], - [['te', 'rminal'], undefined], + [['te', 'rminal'], TerminalCommand.argParser], [['tf', 'irst'], undefined], [['th', 'row'], undefined], [['tj', 'ump'], undefined], diff --git a/src/vimscript/lineRange.ts b/src/vimscript/lineRange.ts index e8015160b..5420aad04 100644 --- a/src/vimscript/lineRange.ts +++ b/src/vimscript/lineRange.ts @@ -292,7 +292,7 @@ export class LineRange { return new LineRange(start); }); - public resolve(vimState: VimState): { start: number; end: number } | undefined { + public resolve(vimState: VimState): { start: number; end: number } { // TODO: *,4 is not a valid range const end = this.end ?? this.start; @@ -329,7 +329,7 @@ export class LineRange { } public resolveToRange(vimState: VimState): Range { - const { start, end } = this.resolve(vimState)!; + const { start, end } = this.resolve(vimState); return new Range(new Position(start, 0), new Position(end, 0).getLineEnd()); } diff --git a/test/vimscript/exCommandParse.test.ts b/test/vimscript/exCommandParse.test.ts index cb166dd4d..79a2691db 100644 --- a/test/vimscript/exCommandParse.test.ts +++ b/test/vimscript/exCommandParse.test.ts @@ -219,6 +219,10 @@ suite('Ex command parsing', () => { exParseTest(':marks 0 1', new MarksCommand(['0', '1'])); }); + suite(':p[rint]', () => { + // TODO + }); + suite(':pu[t]', () => { exParseTest(':put', new PutExCommand({ bang: false, register: undefined })); exParseTest(':put!', new PutExCommand({ bang: true, register: undefined }));