diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 000000000..3ae48eb9a --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,33 @@ +name: Build + +on: + push: + paths-ignore: + - 'LICENSE' + - '*.md' + - 'documents/' + - 'docs/' + branches: + - develop + - master + pull_request: + paths-ignore: + - 'LICENSE' + - '*.md' + - 'documents/' + - 'docs/' + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + nim: [ '1.4.2', 'stable', 'devel' ] + name: Build on ${{ matrix.nim }} + steps: + - uses: actions/checkout@v2 + - name: Setup nim + uses: jiro4989/setup-nim-action@v1 + with: + nim-version: ${{ matrix.nim }} + - run: nimble build -y diff --git a/.github/workflows/actions.yaml b/.github/workflows/test.yaml similarity index 94% rename from .github/workflows/actions.yaml rename to .github/workflows/test.yaml index 840895c05..3d5e54b18 100644 --- a/.github/workflows/actions.yaml +++ b/.github/workflows/test.yaml @@ -40,7 +40,7 @@ jobs: # - macOS-latest # - windows-latest env: - NIM_VERSION: 1.4.8 + NIM_VERSION: stable steps: - uses: actions/checkout@v1 - run: | @@ -88,8 +88,3 @@ jobs: - name: Run integtation test run: shpec ./shpec.sh - - - name: Build on Nim devel - run: | - choosenim -y devel - nimble build diff --git a/README.md b/README.md index 629d4c110..6a1fd5797 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A command line based editor inspired by vi/vim written in Nim. This project's goal is a very customizable, high productivity, user friendly, high performance and funny animation editor. -![moe](https://user-images.githubusercontent.com/15966436/93508284-5fa0ca00-f959-11ea-8282-d64f540e0c54.png) +![moe](https://user-images.githubusercontent.com/15966436/146791140-e020a07f-7ca1-4bfd-a6a4-f20f4c7885db.png) ## Features diff --git a/documents/howtouse.md b/documents/howtouse.md index ec0ca6837..e0212bbc4 100644 --- a/documents/howtouse.md +++ b/documents/howtouse.md @@ -60,6 +60,7 @@ | **Z** **Z**
Write current file and exit | **Z** **Q**
Same as `:q!` | **Ctrl** **w** **c**
Close current window | **?**
`keyword` Search backwards | | **/**
`keyword` Search forwards | **\\** **r**
Quick Run | **s** OR **c****u**
Delete current charater and enter insert mode | **y****{**
Yank to the previous blank line | | **y****}**
Yank to the next blank line | **y****l**
Yank a character| **X** OR **d****h**
Cut a character before cursor | **g****a**
Show current character info | +| **t****x**
Move to the left of the next ```x``` (any character) on the current line | **T****x**
Move to the right of the back ```x ``` (any character) on the current line | **y****t**
**Any key**
Yank characters to an any character | **c****f**
**Any key**
Delete characters to an any character and enter insert mode | diff --git a/example/moerc.toml b/example/moerc.toml index 5d93cf3e7..65f9b387a 100644 --- a/example/moerc.toml +++ b/example/moerc.toml @@ -67,9 +67,9 @@ enable = false allBuffer = false -[StatusBar] +[StatusLine] -multipleStatusBar = true +multipleStatusLine = true merge = false diff --git a/moe.nimble b/moe.nimble index a30a33936..383d15dd8 100644 --- a/moe.nimble +++ b/moe.nimble @@ -1,6 +1,6 @@ # Package -version = "0.2.8.0" +version = "0.3.0" author = "fox0430" description = "A command lined based text editor" license = "GPLv3" @@ -10,10 +10,12 @@ bin = @["moe"] # Dependencies requires "nim >= 1.4.2" -requires "https://github.com/walkre-niboshi/nim-ncurses >= 1.0.2" -requires "unicodedb >= 0.9.0" -requires "parsetoml >= 0.4.0" +requires "ncurses >= 1.0.2" +requires "unicodedb >= 0.10.0" +requires "parsetoml >= 0.6.0" task release, "Build for release": - exec "nim c -o:moe -d:release src/moe" + exec "nimble build -d:release" +task debug, "Build for debug": + exec "nimble build -d:debug --debugger:native --verbose -y" diff --git a/shpec.sh b/shpec.sh index c6b80e9d7..d3394e390 100644 --- a/shpec.sh +++ b/shpec.sh @@ -43,7 +43,7 @@ describe "moe is an editor" describe "invocation options" it "can display it's version" - assert equal `moe -v | grep -oPq "^moe v\d+\.\d+\.\d+\.\d+$";echo $?` 0 + assert equal `moe -v | grep -oPq "^moe v\d+\.\d+\.\d+$";echo $?` 0 end it "can display command line options" diff --git a/src/moe.nim b/src/moe.nim index 7ff1724a7..2c6a53ddb 100644 --- a/src/moe.nim +++ b/src/moe.nim @@ -1,4 +1,4 @@ -import os, times +import std/[os, times] import moepkg/[ui, editorstatus, normalmode, insertmode, visualmode, replacemode, filermode, exmode, buffermanager, logviewer, cmdlineoption, bufferstatus, help, recentfilemode, quickrun, @@ -17,6 +17,18 @@ proc loadPersistData(status: var EditorStatus) = currentMainWindowNode.restoreCursorPostion(currentBufStatus, status.lastPosition) +proc addBufferStatus(status: var EditorStatus, + parsedList: CmdParsedList) = + + if parsedList.path.len > 0: + for path in parsedList.path: + if dirExists(path): + status.addNewBuffer(path, Mode.filer) + else: + status.addNewBuffer(path) + else: + status.addNewBuffer + proc initEditor(): EditorStatus = let parsedList = parseCommandLineOption(commandLineParams()) @@ -33,14 +45,10 @@ proc initEditor(): EditorStatus = exitUi() quit()) - if parsedList.len > 0: - for p in parsedList: - if dirExists(p.filename): - result.addNewBuffer(p.filename, Mode.filer) - else: - result.addNewBuffer(p.filename) - else: - result.addNewBuffer + if parsedList.isReadonly: + result.isReadonly = true + + result.addBufferStatus(parsedList) result.loadPersistData diff --git a/src/moepkg/backup.nim b/src/moepkg/backup.nim index 5a6986c31..3e187c776 100644 --- a/src/moepkg/backup.nim +++ b/src/moepkg/backup.nim @@ -1,4 +1,4 @@ -import os, times, re +import std/[os, times, re] import settings, unicodeext, fileutils, bufferstatus, gapbuffer, messages, commandline diff --git a/src/moepkg/bookmark.nim b/src/moepkg/bookmark.nim index 32bcac530..348d0dc74 100644 --- a/src/moepkg/bookmark.nim +++ b/src/moepkg/bookmark.nim @@ -1,4 +1,4 @@ -import os +import std/os type Bookmark* = object path*: string diff --git a/src/moepkg/buffermanager.nim b/src/moepkg/buffermanager.nim index dae5ef2a0..385910b09 100644 --- a/src/moepkg/buffermanager.nim +++ b/src/moepkg/buffermanager.nim @@ -1,4 +1,4 @@ -import terminal, os, heapqueue, times +import std/[terminal, os, heapqueue, times] import gapbuffer, ui, editorstatus, unicodeext, highlight, window, movement, color, bufferstatus diff --git a/src/moepkg/bufferstatus.nim b/src/moepkg/bufferstatus.nim index 0166fc04c..c2387d29d 100644 --- a/src/moepkg/bufferstatus.nim +++ b/src/moepkg/bufferstatus.nim @@ -1,4 +1,4 @@ -import tables, times, options +import std/[tables, times, options] import syntax/highlite import gapbuffer, unicodeext @@ -40,6 +40,7 @@ type BufferStatus* = object mode* : Mode prevMode* : Mode lastSaveTime*: DateTime + isReadonly*: bool proc initBufferStatus*(path: seq[Rune], mode: Mode): BufferStatus {.inline.} = BufferStatus(isUpdate: true, path: path, mode: mode, lastSaveTime: now()) diff --git a/src/moepkg/build.nim b/src/moepkg/build.nim index c16096d93..c997ad6b6 100644 --- a/src/moepkg/build.nim +++ b/src/moepkg/build.nim @@ -1,4 +1,4 @@ -import os, osproc, strformat, unicode +import std/[os, osproc, strformat, unicode] import syntax/highlite proc build*(filename, workspaceRoot, diff --git a/src/moepkg/clipboard.nim b/src/moepkg/clipboard.nim index 046dc8115..a8beb72cd 100644 --- a/src/moepkg/clipboard.nim +++ b/src/moepkg/clipboard.nim @@ -1,4 +1,4 @@ -import unicode, os +import std/[unicode, os] import independentutils, platform, settings proc runesToStrings(runes: seq[seq[Rune]]): string = diff --git a/src/moepkg/cmdlineoption.nim b/src/moepkg/cmdlineoption.nim index a48e97458..fa7acd78f 100644 --- a/src/moepkg/cmdlineoption.nim +++ b/src/moepkg/cmdlineoption.nim @@ -1,6 +1,8 @@ -import parseopt, pegs, os, strformat +import std/[parseopt, pegs, os, strformat] -type CmdParsedList* = seq[tuple[filename: string]] +type CmdParsedList* = object + path*: seq[string] + isReadonly*: bool proc staticReadVersionFromNimble: string {.compileTime.} = let peg = """@ "version" \s* "=" \s* \" {[0-9.]+} \" @ $""".peg @@ -34,6 +36,7 @@ Usage: moe [file] Edit file Arguments: + -R Readonly mode -h, --help Print this help -v, --version Print version """ @@ -59,11 +62,12 @@ proc parseCommandLineOption*(line: seq[string]): CmdParsedList = for kind, key, val in parsedLine.getopt(): case kind: of cmdArgument: - result.add((filename: key)) + result.path.add(key) of cmdShortOption: case key: of "v": writeVersion() of "h": writeHelp() + of "R": result.isReadonly = true else: writeCmdLineError(kind, key) of cmdLongOption: case key: diff --git a/src/moepkg/color.nim b/src/moepkg/color.nim index 4e4fc4e5f..6509ccf58 100644 --- a/src/moepkg/color.nim +++ b/src/moepkg/color.nim @@ -1,5 +1,5 @@ +import std/[strutils, tables, macros, strformat] import ncurses -import strutils, tables, macros, strformat # maps annotations of the enum to a hexToColor table macro mapAnnotationToTable(args: varargs[untyped]): untyped = diff --git a/src/moepkg/commandline.nim b/src/moepkg/commandline.nim index 927ebace2..d863b4684 100644 --- a/src/moepkg/commandline.nim +++ b/src/moepkg/commandline.nim @@ -1,4 +1,4 @@ -import terminal +import std/terminal import ui, unicodeext, color type CommandLine* = object diff --git a/src/moepkg/commandview.nim b/src/moepkg/commandview.nim index dd0b6e8c8..02f636321 100644 --- a/src/moepkg/commandview.nim +++ b/src/moepkg/commandview.nim @@ -1,4 +1,4 @@ -import terminal, strutils, sequtils, strformat, os, algorithm +import std/[terminal, strutils, sequtils, strformat, os, algorithm] import ui, unicodeext, fileutils, color, commandline type ExModeViewStatus = object @@ -239,13 +239,12 @@ proc clearCommandBuffer(exStatus: var ExModeViewStatus) = proc deleteCommandBuffer(exStatus: var ExModeViewStatus) = if exStatus.buffer.len > 0: if exStatus.buffer.len < terminalWidth(): dec(exStatus.cursorX) - exStatus.buffer.delete(exStatus.currentPosition - 1, - exStatus.currentPosition - 1) + exStatus.buffer.delete(exStatus.currentPosition - 1) dec(exStatus.currentPosition) proc deleteCommandBufferCurrentPosition(exStatus: var ExModeViewStatus) = if exStatus.buffer.len > 0 and exStatus.currentPosition < exStatus.buffer.len: - exStatus.buffer.delete(exStatus.cursorX - 1, exStatus.cursorX - 1) + exStatus.buffer.delete(exStatus.cursorX - 1) if exStatus.currentPosition > exStatus.buffer.len: dec(exStatus.currentPosition) @@ -657,6 +656,9 @@ proc getKeyOnceAndWriteCommandView*( status.commandLine.window.moveLeft(exStatus) elif isRightkey(key): exStatus.moveRight + if status.settings.popUpWindowInExmode: + status.deletePopUpWindow + status.update elif isUpKey(key): if isSearch: setPrevSearchHistory() else: setPrevCommandHistory() diff --git a/src/moepkg/configmode.nim b/src/moepkg/configmode.nim index 2671fbdee..de2f99cb6 100644 --- a/src/moepkg/configmode.nim +++ b/src/moepkg/configmode.nim @@ -1,4 +1,4 @@ -import terminal, times, strutils +import std/[terminal, times, strutils] import gapbuffer, ui, editorstatus, unicodeext, window, movement, settings, bufferstatus, color, highlight, search, editor diff --git a/src/moepkg/cursor.nim b/src/moepkg/cursor.nim index 7e3485434..16779c0d4 100644 --- a/src/moepkg/cursor.nim +++ b/src/moepkg/cursor.nim @@ -1,4 +1,4 @@ -import deques, strformat +import std/[deques, strformat] import editorview, unicodeext type CursorPosition* = object diff --git a/src/moepkg/debugmode.nim b/src/moepkg/debugmode.nim index 38b7071a3..4e5b35fcc 100644 --- a/src/moepkg/debugmode.nim +++ b/src/moepkg/debugmode.nim @@ -1,4 +1,4 @@ -import terminal, times, strformat, options +import std/[terminal, times, strformat, options] import gapbuffer, ui, unicodeext, highlight, color, window, bufferstatus, movement, settings diff --git a/src/moepkg/diffviewer.nim b/src/moepkg/diffviewer.nim index 88a335147..e2fc004d0 100644 --- a/src/moepkg/diffviewer.nim +++ b/src/moepkg/diffviewer.nim @@ -1,4 +1,4 @@ -import times, terminal +import std/[times, terminal] import editorstatus, unicodeext, bufferstatus, highlight, color, gapbuffer, ui, movement, window diff --git a/src/moepkg/editor.nim b/src/moepkg/editor.nim index 4ee95dd77..f9ac68373 100644 --- a/src/moepkg/editor.nim +++ b/src/moepkg/editor.nim @@ -1,4 +1,4 @@ -import strutils, sequtils, strformat, options +import std/[strutils, sequtils, strformat, options] import syntax/highlite import editorstatus, ui, gapbuffer, unicodeext, undoredostack, window, bufferstatus, movement, messages, settings, register, commandline @@ -227,7 +227,8 @@ proc basicNewLine(bufStatus: var BufferStatus, if first <= last: let oldLine = bufStatus.buffer[windowNode.currentLine] var newLine = bufStatus.buffer[windowNode.currentLine] - newLine.delete(first, last) + for _ in first .. last: + newLine.delete(first) if oldLine != newLine: bufStatus.buffer[windowNode.currentLine] = newLine @@ -266,7 +267,7 @@ proc basicInsrtIndent(bufStatus: var BufferStatus, if oldLine != newLine: bufStatus.buffer[currentLine + 1] = newLine -proc insertIndetWhenPairOfParen(bufStatus: var BufferStatus, +proc insertIndentWhenPairOfParen(bufStatus: var BufferStatus, windowNode: WindowNode, autoIndent: bool, tabStop: int) = @@ -318,12 +319,17 @@ proc insertIndentInNimForKeyEnter(bufStatus: var BufferStatus, if line.len > 0: # Auto indent if the current line are "var", "let", "const". # And, if finish the current line with ':', "object" - if (currentColumn == line.len) and - (line.splitWhitespace == @[ru "var"] or - line.splitWhitespace == @[ru "let"] or - line.splitWhitespace == @[ru "const"] or - (line.len > 6 and line[line.len - 6 .. ^1] == ru "object") or - line[^1] == ru ':'): + if currentColumn == line.len and ( + (line.len > 2 and + line.splitWhitespace == @[ru "var"] or + line.splitWhitespace == @[ru "let"]) or + (line.len > 4 and + line.splitWhitespace == @[ru "const"]) or + (line.len > 4 and + line.splitWhitespace[^1] == (ru "object")) or + line[^1] == (ru ':') or + line[^1] == (ru '=') + ): let count = countRepeat(line, Whitespace, 0) + tabStop oldLine = bufStatus.buffer[windowNode.currentLine + 1] @@ -350,7 +356,7 @@ proc insertIndentInNimForKeyEnter(bufStatus: var BufferStatus, # if previous col is the unclosed paren. elif currentColumn > 0 and isOpenParen(line[currentColumn - 1]): - bufStatus.insertIndetWhenPairOfParen(windowNode, autoIndent, tabStop) + bufStatus.insertIndentWhenPairOfParen(windowNode, autoIndent, tabStop) else: bufStatus.basicInsrtIndent(windowNode) bufStatus.basicNewLine(windowNode, autoIndent, tabStop) @@ -406,7 +412,7 @@ proc insertIndentInClangForKeyEnter(bufStatus: var BufferStatus, if currentColumn > 0 : # if previous col is the unclosed paren. if line.len > 0 and isOpenParen(line[currentColumn - 1]): - bufStatus.insertIndetWhenPairOfParen(windowNode, autoIndent, tabStop) + bufStatus.insertIndentWhenPairOfParen(windowNode, autoIndent, tabStop) else: bufStatus.basicInsrtIndent(windowNode) bufStatus.basicNewLine(windowNode, autoIndent, tabStop) @@ -702,7 +708,7 @@ proc deleteIndent*(bufStatus: var BufferStatus, if numOfDeleteSpace > 0: var newLine = bufStatus.buffer[windowNode.currentLine] - for i in 0 ..< numOfDeleteSpace: newLine.delete(0, 0) + for i in 0 ..< numOfDeleteSpace: newLine.delete(0) if oldLine != newLine: bufStatus.buffer[windowNode.currentLine] = newLine @@ -720,7 +726,8 @@ proc deleteCharactersBeforeCursorInCurrentLine*(bufStatus: var BufferStatus, oldLine = bufStatus.buffer[currentLine] var newLine = bufStatus.buffer[currentLine] - newLine.delete(0, currentColumn - 1) + for _ in 0 ..< currentColumn: + newLine.delete(0) if newLine != oldLine: bufStatus.buffer[currentLine] = newLine @@ -862,30 +869,17 @@ proc deleteCharacters*(bufStatus: var BufferStatus, inc(bufStatus.countChange) bufStatus.isUpdate = true -# TODO: Delete deleteCurrentCharacter() -proc deleteCurrentCharacter*(bufStatus: var BufferStatus, - windowNode: WindowNode, - autoDeleteParen: bool) = - - let oldLine = bufStatus.buffer[windowNode.currentLine] - - deleteCharacter(bufStatus, - windowNode.currentLine, - windowNode.currentColumn, - autoDeleteParen) - - if oldLine != bufStatus.buffer[windowNode.currentLine]: - if bufStatus.buffer[windowNode.currentLine].len < 1: - windowNode.currentColumn = 0 - windowNode.expandedColumn = 0 - elif bufStatus.buffer[windowNode.currentLine].len > 0 and - windowNode.currentColumn > bufStatus.buffer[windowNode.currentLine].high and - bufStatus.mode != Mode.insert: - windowNode.currentColumn = bufStatus.buffer[windowNode.currentLine].len - 1 - windowNode.expandedColumn = bufStatus.buffer[windowNode.currentLine].len - 1 - - inc(bufStatus.countChange) - bufStatus.isUpdate = true +# Delete a character in the current position +#proc deleteCurrentCharacter*(bufStatus: var BufferStatus, +# windowNode: WindowNode, +# autoDeleteParen: bool) = +# +# const loop = 1 +# bufStatus.deleteCharacters( +# autoDeleteParen, +# windowNode.currentLine, +# windowNode.currentColumn, +# loop) # Add the new line and insert indent in Nim proc insertIndentNimForOpenBlankLine(bufStatus: var BufferStatus, @@ -898,12 +892,13 @@ proc insertIndentNimForOpenBlankLine(bufStatus: var BufferStatus, if aboveLine.len > 0: # Auto indent if the current line are "var", "let", "const". - # And, if finish the current line with ':', "object" + # And, if finish the current line with ':', "object, '='" if (aboveLine.splitWhitespace == @[ru "var"] or aboveLine.splitWhitespace == @[ru "let"] or aboveLine.splitWhitespace == @[ru "const"] or - (aboveLine.len > 6 and aboveLine[aboveLine.len - 6 .. ^1] == ru "object") or - aboveLine[^1] == ru ':'): + aboveLine.splitWhitespace[^1] == (ru "object") or + aboveLine[^1] == (ru ':') or + aboveLine[^1] == (ru '=')): let count = countRepeat(aboveLine, Whitespace, 0) + tabStop oldLine = bufStatus.buffer[currentLineNum] @@ -914,8 +909,8 @@ proc insertIndentNimForOpenBlankLine(bufStatus: var BufferStatus, bufStatus.buffer[currentLineNum] = newLine # Auto indent if finish the current line with "or", "and" - elif ((aboveLine.len > 2 and aboveLine[aboveLine.len - 2 .. ^1] == ru "or") or - (aboveLine.len > 3 and aboveLine[aboveLine.len - 3 .. ^1] == ru "and")): + elif ((aboveLine.len > 2 and (aboveLine.splitWhitespace)[^1] == ru "or") or + (aboveLine.len > 3 and (aboveLine.splitWhitespace)[^1] == ru "and")): let count = countRepeat(aboveLine, Whitespace, 0) + tabStop oldLine = bufStatus.buffer[currentLineNum] @@ -944,8 +939,8 @@ proc insertIndentInPythonForOpenBlankLine(bufStatus: var BufferStatus, if aboveLine.len > 0: # if finish the current line with ':', "or", "and" in Python - if (aboveLine.len > 2 and aboveLine[aboveLine.len - 2 .. ^1] == ru "or") or - (aboveLine.len > 3 and aboveLine[aboveLine.len - 3 .. ^1] == ru "and") or + if (aboveLine.len > 2 and (aboveLine.splitWhitespace)[^1] == ru "or") or + (aboveLine.len > 3 and (aboveLine.splitWhitespace)[^1] == ru "and") or (aboveLine[^1] == ru ':'): let count = countRepeat(aboveLine, Whitespace, 0) + tabStop @@ -1164,7 +1159,8 @@ proc deleteTillPreviousBlankLine*(bufStatus: var BufferStatus, for i in 0 ..< currentColumn: deletedLine.add oldLine[i] if deletedLine.len > 0: deletedBuffer.add deletedLine - newLine.delete(0, currentColumn - 1) + for _ in 0 ..< currentColumn: + newLine.delete(0) if oldLine != newLine: bufStatus.buffer[currentLine] = newLine @@ -1206,7 +1202,8 @@ proc deleteTillNextBlankLine*(bufStatus: var BufferStatus, var deletedLine: seq[Rune] for i in currentColumn ..< oldLine.len : deletedLine.add oldLine[i] - newLine.delete(currentColumn, oldLine.high) + for _ in currentColumn .. oldLine.high: + newLine.delete(currentColumn) if oldLine != newLine: bufStatus.buffer[currentLine] = newLine @@ -1535,9 +1532,12 @@ proc replaceCharacters*(bufStatus: var BufferStatus, character: Rune) = if isEnterKey(character): - let line = bufStatus.buffer[windowNode.currentLine] + let + line = bufStatus.buffer[windowNode.currentLine] + currentLine = windowNode.currentLine + currentColumn = windowNode.currentColumn for _ in windowNode.currentColumn ..< min(line.len, loop): - bufStatus.deleteCurrentCharacter(windowNode, autoDeleteParen) + bufStatus.deleteCharacter(currentLine, currentColumn, autoDeleteParen) keyEnter(bufStatus, windowNode, autoIndent, tabStop) else: let oldLine = bufStatus.buffer[windowNode.currentLine] @@ -1597,7 +1597,7 @@ proc autoIndentCurrentLine*(bufStatus: var BufferStatus, # Delete current indent for i in 0 ..< oldLine.len: if oldLine[i] == ru' ': - newLine.delete(0, 0) + newLine.delete(0) else: break newLine.insert(indent, 0) @@ -1800,7 +1800,7 @@ proc modifyNumberTextUnderCurosr*(bufStatus: var BufferStatus, block: var col = currentColumn while newLine.len > 0 and isDigit(newLine[col]): - newLine.delete(col, col) + newLine.delete(col) if col > newLine.high: col = newLine.high # Insert the new number string to newLine diff --git a/src/moepkg/editorstatus.nim b/src/moepkg/editorstatus.nim index 630f568c2..0a3d3014d 100644 --- a/src/moepkg/editorstatus.nim +++ b/src/moepkg/editorstatus.nim @@ -1,9 +1,9 @@ -import strutils, terminal, os, strformat, tables, times, heapqueue, deques, - times, options +import std/[strutils, terminal, os, strformat, tables, times, heapqueue, deques, + options] import syntax/highlite import gapbuffer, editorview, ui, unicodeext, highlight, fileutils, - undoredostack, window, color, settings, statusline, bufferstatus, cursor, - tabline, backup, messages, commandline, register, platform + window, color, settings, statusline, bufferstatus, cursor, tabline, + backup, messages, commandline, register, platform # Save cursor position when a buffer for a window(file) gets closed. type LastPosition* = object @@ -31,6 +31,7 @@ type EditorStatus* = object autoBackupStatus*: AutoBackupStatus isSearchHighlight*: bool lastPosition*: seq[LastPosition] + isReadonly*: bool proc initEditorStatus*(): EditorStatus = result.currentDir = getCurrentDir().toRunes @@ -203,7 +204,7 @@ proc saveSearchHistory(history: seq[seq[Rune]]) = f.writeLine($line) # Save the cursor position to the file -proc saveLastPosition(lastPosition: seq[LastPosition]) = +proc saveLastCursorPosition(lastPosition: seq[LastPosition]) = let chaheDir = getHomeDir() / ".cache/moe" chaheFile = chaheDir / "lastPosition" @@ -225,7 +226,7 @@ proc exitEditor*(status: EditorStatus) = saveSearchHistory(status.searchHistory) if status.settings.persist.cursorPosition: - saveLastPosition(status.lastPosition) + saveLastCursorPosition(status.lastPosition) exitUi() @@ -437,10 +438,20 @@ proc initSyntaxHighlight(windowNode: var WindowNode, proc isLogViewerMode(mode, prevMode: Mode): bool {.inline.} = (mode == logViewer) or (mode == ex and prevMode == logViewer) -proc updateLogViewer(status: var Editorstatus, bufferIndex: int) = - status.bufStatus[bufferIndex].buffer = initGapBuffer(@[ru""]) - for i in 0 ..< status.messageLog.len: - status.bufStatus[bufferIndex].buffer.insert(status.messageLog[i], i) +proc updateLogViewer(bufStatus: var BufferStatus, + node: var WindowNode, + messageLog: seq[seq[Rune]]) = + + bufStatus.buffer = initGapBuffer(@[ru""]) + for i in 0 ..< messageLog.len: + bufStatus.buffer.insert(messageLog[i], i) + + const EMPTY_RESERVEDWORD: seq[ReservedWord] = @[] + + node.highlight = initHighlight( + $bufStatus.buffer, + EMPTY_RESERVEDWORD, + SourceLanguage.langNone) proc updateDebugModeBuffer(status: var EditorStatus) @@ -507,14 +518,14 @@ proc update*(status: var EditorStatus) = not isConfigMode(currentMode, prevMode): if isLogViewerMode(currentMode, prevMode): - status.updateLogViewer(node.bufferIndex) - - highlight.updateHighlight( - bufStatus, - node, - status.isSearchHighlight, - status.searchHistory, - settings) + status.bufStatus[node.bufferIndex].updateLogViewer(node, status.messageLog) + else: + highlight.updateHighlight( + bufStatus, + node, + status.isSearchHighlight, + status.searchHistory, + settings) let startSelectedLine = bufStatus.selectArea.startLine @@ -636,7 +647,8 @@ proc closeWindow*(status: var EditorStatus, node: WindowNode, height, width: int) = - if currentBufStatus.mode == Mode.normal: + if isNormalMode(currentBufStatus.mode, currentBufStatus.prevMode) or + isFilerMode(currentBufStatus.mode, currentBufStatus.prevMode): status.updateLastCursorPostion if status.mainWindow.numOfMainWindow == 1: @@ -717,13 +729,17 @@ proc deletePopUpWindow*(status: var Editorstatus) = proc addNewBuffer*(status: var EditorStatus, filename: string, mode: Mode) = - let path = if mode == Mode.filer: ru absolutePath(filename) else: ru filename + let path = if isFilerMode(mode): ru absolutePath(filename) else: ru filename status.bufStatus.add(initBufferStatus(path, mode)) let index = status.bufStatus.high - if mode != Mode.filer: + status.bufStatus[index].isReadonly = status.isReadonly + + if mode == Mode.filer: + status.bufStatus[index].buffer = initGapBuffer(@[ru ""]) + else: if not fileExists(filename): status.bufStatus[index].buffer = newFile() else: @@ -1176,8 +1192,6 @@ proc autoSave(status: var Editorstatus) = status.messageLog) status.bufStatus[index].lastSaveTime = now() -from settings import TomlError, loadSettingFile - proc loadConfigurationFile*(status: var EditorStatus) = status.settings = try: diff --git a/src/moepkg/editorview.nim b/src/moepkg/editorview.nim index 2b09fee6b..4afa8339b 100644 --- a/src/moepkg/editorview.nim +++ b/src/moepkg/editorview.nim @@ -1,5 +1,5 @@ -import deques, strutils, math, strformat -import gapbuffer, ui, unicodeext, highlight, independentutils, color, settings, +import std/[deques, strutils, math, strformat] +import gapbuffer, ui, unicodeext, independentutils, color, settings, bufferstatus, highlight type EditorView* = object diff --git a/src/moepkg/exmode.nim b/src/moepkg/exmode.nim index 4c592f5e6..11a0824eb 100644 --- a/src/moepkg/exmode.nim +++ b/src/moepkg/exmode.nim @@ -1,4 +1,4 @@ -import sequtils, strutils, os, terminal, times, options +import std/[sequtils, strutils, os, terminal, times, options] import syntax/highlite import editorstatus, ui, normalmode, gapbuffer, fileutils, editorview, unicodeext, independentutils, search, highlight, commandview, @@ -311,8 +311,10 @@ proc startHistoryManager(status: var Editorstatus) = proc startRecentFileMode(status: var Editorstatus) = status.changeMode(currentBufStatus.prevMode) - # :recent is only supported on GNU/Linux - if CURRENT_PLATFORM != Platforms.linux: return + # :recent is only supported on Unix or Unix-like (BSD and Linux) + if not (CURRENT_PLATFORM == Platforms.linux or + CURRENT_PLATFORM == Platforms.freebsd or + CURRENT_PLATFORM == Platforms.openbsd): return if not fileExists(getHomeDir() / ".local/share/recently-used.xbel"): status.commandLine.writeOpenRecentlyUsedXbelError(status.messageLog) @@ -779,8 +781,10 @@ proc editCommand(status: var EditorStatus, path: seq[Rune]) = status.changeCurrentBuffer(bufferIndex.get) - currentMainWindowNode.restoreCursorPostion(currentBufStatus, - status.lastPosition) + if not isFilerMode(currentBufStatus.mode): + currentMainWindowNode.restoreCursorPostion( + currentBufStatus, + status.lastPosition) proc openInHorizontalSplitWindow(status: var Editorstatus, filename: seq[Rune]) = status.horizontalSplitWindow @@ -1149,8 +1153,10 @@ proc replaceBuffer(status: var EditorStatus, command: seq[Rune]) = if searchResult.line > -1: let oldLine = currentBufStatus.buffer[searchResult.line] var newLine = currentBufStatus.buffer[searchResult.line] - newLine.delete(searchResult.column, - searchResult.column + replaceInfo.searhWord.high) + + for _ in searchResult.column .. searchResult.column + replaceInfo.searhWord.high: + newLine.delete(searchResult.column) + newLine.insert(replaceInfo.replaceWord, searchResult.column) if oldLine != newLine: currentBufStatus.buffer[searchResult.line] = newLine diff --git a/src/moepkg/filermode.nim b/src/moepkg/filermode.nim index 321ca4e14..047d5bb18 100644 --- a/src/moepkg/filermode.nim +++ b/src/moepkg/filermode.nim @@ -1,7 +1,8 @@ -import os, terminal, strutils, unicodeext, times, algorithm, sequtils, options +import std/[os, terminal, strutils, times, algorithm, sequtils, + options] import editorstatus, ui, fileutils, editorview, gapbuffer, highlight, - commandview, highlight, window, color, bufferstatus, settings, messages, - commandline + commandview, window, color, bufferstatus, settings, messages, + commandline, unicodeext type PathInfo = tuple[kind: PathComponent, path: string, @@ -77,8 +78,8 @@ proc sortDirList(dirList: seq[PathInfo], sortBy: Sort): seq[PathInfo] = result.add dirList.sortedByIt(it.lastWriteTime) when defined(posix): - from posix import nil - from posix_utils import nil + from std/posix import nil + from std/posix_utils import nil proc isFifo(file: string): bool {.inline.} = posix.S_ISFIFO(posix_utils.stat(file).st_mode) @@ -515,6 +516,19 @@ proc filerMode*(status: var EditorStatus) = let currentBufferIndex = status.bufferIndexInCurrentWindow + block: + filerStatus = filerStatus.updateDirList(currentBufStatus.path) + filerStatus.dirlistUpdate = false + + status.updateFilerView(filerStatus, terminalHeight(), terminalWidth()) + + currentMainWindowNode.restoreCursorPostion( + currentBufStatus, + status.lastPosition) + + status.updateFilerView(filerStatus, terminalHeight(), terminalWidth()) + filerStatus.viewUpdate = false + while isFilerMode(currentBufStatus.mode) and currentBufferIndex == status.bufferIndexInCurrentWindow: diff --git a/src/moepkg/fileutils.nim b/src/moepkg/fileutils.nim index 9f61a99d0..3b9f16243 100644 --- a/src/moepkg/fileutils.nim +++ b/src/moepkg/fileutils.nim @@ -1,4 +1,4 @@ -import os, encodings +import std/[os, encodings] import gapbuffer, unicodeext proc normalizePath*(path: seq[Rune]): seq[Rune] = diff --git a/src/moepkg/gapbuffer.nim b/src/moepkg/gapbuffer.nim index 16da43c1b..61f0327b8 100644 --- a/src/moepkg/gapbuffer.nim +++ b/src/moepkg/gapbuffer.nim @@ -1,4 +1,4 @@ -import macros, strformat +import std/[macros, strformat] import undoredostack export undoredostack diff --git a/src/moepkg/generalautocomplete.nim b/src/moepkg/generalautocomplete.nim index 66fcce363..5a1c66a6f 100644 --- a/src/moepkg/generalautocomplete.nim +++ b/src/moepkg/generalautocomplete.nim @@ -1,4 +1,4 @@ -import sugar, critbits, options, sequtils +import std/[sugar, critbits, options] import unicodedb/properties import unicodeext, bufferstatus @@ -55,7 +55,9 @@ proc getTextInBuffers*( for i, buf in bufStatus: # 0 is current bufStatus if i == 0: - result = buf.buffer.toRunes.dup( - delete(firstDeletedIndex, lastDeletedIndex)) + var runeBuf = buf.buffer.toRunes + for _ in firstDeletedIndex .. lastDeletedIndex: + runeBuf.delete(firstDeletedIndex) + result = runeBuf else: result.add buf.buffer.toRunes diff --git a/src/moepkg/help.nim b/src/moepkg/help.nim index 630abdca5..9830362c1 100644 --- a/src/moepkg/help.nim +++ b/src/moepkg/help.nim @@ -1,4 +1,4 @@ -import terminal +import std/[terminal] import editorstatus, bufferstatus, ui, movement, unicodeext, gapbuffer, window const helpsentences = """ @@ -52,6 +52,7 @@ yy or Y - Copy a line y{ - Yank to the previous blank line y} - Yank to the next blank line yl - Yank a character +yt any - Ynak characters to a any character p - Paste the clipboard n - Search forwards : - Start Ex mode @@ -71,6 +72,7 @@ ciw - Delete the current word and enter insert mode ci( or ci) - Delete the inside of round brackets and enter insert mode ci[ or ci] - Delete the inside of square brackets and enter insert mode ci{ or ci} - Delete the inside of curly brackets and enter insert mode +cf any - Delete characters to the any character and enter insert mode di" - Delete the inside of double quotes di' - Delete the inside of single quotes diw - Delete the current word @@ -79,8 +81,10 @@ di[ or di] - Delete the inside of square brackets di{ or di} - Delete the inside of curly brackets * - Search forwards for the word under cursor # - Search backwards for the word under cursor -f - Jump to next occurrence -F - Jump to previous occurence +f - Move to next any character on the current line +F - Move to previous any character on the current line +t - Move to the left of the any character on the current line +T - Move to the right of the back any character on the current line Ctrl-k - Move to the next window Ctrl-j - Move to the previous window zt - Scroll the screen so the cursor is at the top diff --git a/src/moepkg/highlight.nim b/src/moepkg/highlight.nim index 002ff8893..ed379f052 100644 --- a/src/moepkg/highlight.nim +++ b/src/moepkg/highlight.nim @@ -1,7 +1,7 @@ -import sequtils, os, strformat, parseutils +import std/[sequtils, os, strformat, parseutils] import syntax/highlite import unicodeext, color -from strutils import find +from std/strutils import find type ColorSegment* = object firstRow*, firstColumn*, lastRow*, lastColumn*: int diff --git a/src/moepkg/historymanager.nim b/src/moepkg/historymanager.nim index f13f833fa..6ebff1c82 100644 --- a/src/moepkg/historymanager.nim +++ b/src/moepkg/historymanager.nim @@ -1,6 +1,6 @@ # History manager for automatic backup. -import re, os, times, terminal, osproc +import std/[re, os, times, terminal, osproc] import editorstatus, bufferstatus, unicodeext, ui, movement, gapbuffer, highlight, color, settings, messages, backup, commandview, fileutils, editorview, window diff --git a/src/moepkg/independentutils.nim b/src/moepkg/independentutils.nim index a4b79a7e2..c21112444 100644 --- a/src/moepkg/independentutils.nim +++ b/src/moepkg/independentutils.nim @@ -1,4 +1,4 @@ -import strutils, math, random, osproc +import std/[strutils, math, random, osproc] proc numberOfDigits*(x: int): int {.inline.} = x.intToStr.len diff --git a/src/moepkg/insertmode.nim b/src/moepkg/insertmode.nim index f4aa8300d..abec6915c 100644 --- a/src/moepkg/insertmode.nim +++ b/src/moepkg/insertmode.nim @@ -1,4 +1,4 @@ -import terminal, times, options, unicode +import std/[terminal, times, options, unicode] import ui, editorstatus, gapbuffer, window, movement, editor, bufferstatus, suggestionwindow, settings @@ -97,8 +97,9 @@ proc insertMode*(status: var EditorStatus) = elif isEndKey(key): currentBufStatus.moveToLastOfLine(windowNode) elif isDcKey(key): - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + windowNode.currentLine, + windowNode.currentColumn, status.settings.autoDeleteParen) elif isBackspaceKey(key) or isControlH(key): currentBufStatus.keyBackspace( diff --git a/src/moepkg/logviewer.nim b/src/moepkg/logviewer.nim index 83fb0c2bc..666bd7fd9 100644 --- a/src/moepkg/logviewer.nim +++ b/src/moepkg/logviewer.nim @@ -1,4 +1,4 @@ -import terminal, times +import std/[terminal, times] import ui, editorstatus, unicodeext, movement, bufferstatus, window proc exitLogViewer*(status: var Editorstatus, height, width: int) {.inline.} = diff --git a/src/moepkg/messages.nim b/src/moepkg/messages.nim index ea6af8e1c..53b934b78 100644 --- a/src/moepkg/messages.nim +++ b/src/moepkg/messages.nim @@ -1,13 +1,15 @@ -import strformat, os, strutils +import std/[strformat, os, strutils] import color, unicodeext, settings, commandline, independentutils proc writeMessageOnCommandWindow*(commandLine: var CommandLine, message: string, color: EditorColorPair) {.inline.} = + commandLine.updateCommandBuffer(ru message, color) proc writeMessageOnCommandWindow*(commandLine: var CommandLine, message: string) {.inline.} = + commandLine.writeMessageOnCommandWindow(message, EditorColorPair.commandBar) proc writeNoWriteError*(commandLine: var CommandLine, messageLog: var seq[seq[Rune]]) = @@ -296,3 +298,7 @@ proc writeCurrentCharInfo*(commandLine: var CommandLine, r: Rune) {.inline.} = eOct = int64(e[0]).toOct(5) mess = fmt "<{$r}> {e[0]} Hex {normalizeHex($eHex)} Oct {$eOct}" commandLine.writeMessageOnCommandWindow(mess) + +proc writeReadonlyModeWarning*(commandLine: var CommandLine) {.inline.} = + const mess = "Warning: Readonly mode" + commandLine.writeMessageOnCommandWindow(mess, EditorColorPair.errorMessage) diff --git a/src/moepkg/movement.nim b/src/moepkg/movement.nim index 7b2a1a65d..cac721922 100644 --- a/src/moepkg/movement.nim +++ b/src/moepkg/movement.nim @@ -1,4 +1,4 @@ -import deques +import std/deques import editorstatus, ui, editorview, gapbuffer, unicodeext, window, bufferstatus template currentLineLen: int = bufStatus.buffer[windowNode.currentLine].len diff --git a/src/moepkg/normalmode.nim b/src/moepkg/normalmode.nim index 2d9aef937..4e9d86463 100644 --- a/src/moepkg/normalmode.nim +++ b/src/moepkg/normalmode.nim @@ -1,5 +1,4 @@ -from strutils import parseInt -import terminal, times, strutils +import std/[terminal, times, strutils] import editorstatus, ui, gapbuffer, unicodeext, fileutils, undoredostack, window, movement, editor, search, bufferstatus, quickrun, messages @@ -9,9 +8,10 @@ type InputState = enum Valid Invalid -proc searchOneCharactorToEndOfLine(bufStatus: var BufferStatus, +proc searchOneCharacterToEndOfLine(bufStatus: var BufferStatus, windowNode: WindowNode, - rune: Rune) = + rune: Rune): int = + result = -1 let line = bufStatus.buffer[windowNode.currentLine] @@ -20,12 +20,13 @@ proc searchOneCharactorToEndOfLine(bufStatus: var BufferStatus, for col in windowNode.currentColumn + 1 ..< line.len: if line[col] == rune: - windowNode.currentColumn = col + result = col break -proc searchOneCharactorToBeginOfLine(bufStatus: var BufferStatus, +proc searchOneCharacterToBeginOfLine(bufStatus: var BufferStatus, windowNode: WindowNode, - rune: Rune) = + rune: Rune): int = + result = -1 let line = bufStatus.buffer[windowNode.currentLine] @@ -33,7 +34,7 @@ proc searchOneCharactorToBeginOfLine(bufStatus: var BufferStatus, for col in countdown(windowNode.currentColumn - 1, 0): if line[col] == rune: - windowNode.currentColumn = col + result = col break proc searchNextOccurrence(status: var EditorStatus, keyword: seq[Rune]) = @@ -155,6 +156,10 @@ proc yankWord(status: var EditorStatus, registerName: string) = status.settings) proc deleteWord(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" currentBufStatus.deleteWord( currentMainWindowNode, @@ -173,6 +178,10 @@ proc deleteWord(status: var EditorStatus, registerName: string) = # ci command proc changeInnerCommand(status: var EditorStatus, key: Rune) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + let currentLine = currentMainWindowNode.currentLine oldLine = currentBufStatus.buffer[currentLine] @@ -202,6 +211,10 @@ proc changeInnerCommand(status: var EditorStatus, key: Rune, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + let currentLine = currentMainWindowNode.currentLine oldLine = currentBufStatus.buffer[currentLine] @@ -229,6 +242,10 @@ proc changeInnerCommand(status: var EditorStatus, # di command proc deleteInnerCommand(status: var EditorStatus, key: Rune, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + # Delete inside paren and enter insert mode if isParen(key): if registerName.len > 0: @@ -256,6 +273,10 @@ proc deleteInnerCommand(status: var EditorStatus, key: Rune, registerName: strin # di command proc deleteInnerCommand(status: var EditorStatus, key: Rune) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" status.deleteInnerCommand(key, registerName) @@ -368,6 +389,10 @@ proc yankToNextBlankLine(status: var EditorStatus) = # dd command proc deleteLines(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" let startLine = currentMainWindowNode.currentLine @@ -382,6 +407,10 @@ proc deleteLines(status: var EditorStatus) = status.settings) proc deleteLines(status: var EditorStatus, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + let startLine = currentMainWindowNode.currentLine count = min( @@ -435,7 +464,60 @@ proc yankCharacters(status: var Editorstatus, registerName: string) = registerName, isDelete) +# yt command +proc yankCharactersToCharacter(status: var EditorStatus, + rune: Rune) = + + let + currentColumn = currentMainWindowNode.currentColumn + # Get the position of a character + position = currentBufStatus.searchOneCharacterToEndOfLine( + currentMainWindowNode, + rune) + + if position > currentColumn: + const + isDelete = false + registerName = "" + currentBufStatus.yankCharacters( + status.registers, + currentMainWindowNode, + status.commandLine, + status.messageLog, + status.settings, + position, + registerName, + isDelete) + +# yt command +proc yankCharactersToCharacter(status: var EditorStatus, + rune: Rune, + registerName: string) = + + let + currentColumn = currentMainWindowNode.currentColumn + # Get the position of a character + position = currentBufStatus.searchOneCharacterToEndOfLine( + currentMainWindowNode, + rune) + + if position > currentColumn: + const isDelete = false + currentBufStatus.yankCharacters( + status.registers, + currentMainWindowNode, + status.commandLine, + status.messageLog, + status.settings, + position, + registerName, + isDelete) + proc deleteCharacters(status: var EditorStatus, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + currentBufStatus.deleteCharacters( status.registers, registerName, @@ -445,6 +527,10 @@ proc deleteCharacters(status: var EditorStatus, registerName: string) = status.settings) proc deleteCharacters(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" currentBufStatus.deleteCharacters( status.registers, @@ -458,6 +544,10 @@ proc deleteCharacters(status: var EditorStatus) = proc deleteCharactersUntilEndOfLine(status: var EditorStatus, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + let lineWidth = currentBufStatus.buffer[currentMainWindowNode.currentLine].len currentBufStatus.cmdLoop = lineWidth - currentMainWindowNode.currentColumn @@ -469,6 +559,10 @@ proc deleteCharactersUntilEndOfLine(status: var EditorStatus, # d$ command proc deleteCharactersUntilEndOfLine(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + let lineWidth = currentBufStatus.buffer[currentMainWindowNode.currentLine].len currentBufStatus.cmdLoop = lineWidth - currentMainWindowNode.currentColumn @@ -483,6 +577,10 @@ proc deleteCharactersUntilEndOfLine(status: var EditorStatus) = proc deleteCharacterBeginningOfLine(status: var EditorStatus, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + currentBufStatus.deleteCharacterBeginningOfLine( status.registers, currentMainWindowNode, @@ -491,6 +589,10 @@ proc deleteCharacterBeginningOfLine(status: var EditorStatus, # d0 command proc deleteCharacterBeginningOfLine(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" status.deleteCharacterBeginningOfLine(registerName) @@ -499,6 +601,10 @@ proc deleteCharacterBeginningOfLine(status: var EditorStatus) = proc deleteFromCurrentLineToLastLine(status: var EditorStatus, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + let startLine = currentMainWindowNode.currentLine count = currentBufStatus.buffer.len - currentMainWindowNode.currentLine @@ -514,6 +620,10 @@ proc deleteFromCurrentLineToLastLine(status: var EditorStatus, proc deleteLineFromFirstLineToCurrentLine(status: var EditorStatus, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const startLine = 0 let count = currentMainWindowNode.currentLine currentBufStatus.deleteLines(status.registers, @@ -529,6 +639,10 @@ proc deleteLineFromFirstLineToCurrentLine(status: var EditorStatus, proc deleteTillPreviousBlankLine(status: var EditorStatus, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + currentBufStatus.deleteTillPreviousBlankLine( status.registers, currentMainWindowNode, @@ -536,6 +650,10 @@ proc deleteTillPreviousBlankLine(status: var EditorStatus, status.settings) proc deleteTillPreviousBlankLine(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" status.deleteTillPreviousBlankLine(registerName) @@ -543,6 +661,10 @@ proc deleteTillPreviousBlankLine(status: var EditorStatus) = proc deleteTillNextBlankLine(status: var EditorStatus, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + currentBufStatus.deleteTillNextBlankLine( status.registers, currentMainWindowNode, @@ -551,6 +673,10 @@ proc deleteTillNextBlankLine(status: var EditorStatus, # X and dh command proc cutCharacterBeforeCursor(status: var EditorStatus, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + if currentMainWindowNode.currentColumn > 0: let currentColumn = currentMainWindowNode.currentColumn @@ -564,38 +690,45 @@ proc cutCharacterBeforeCursor(status: var EditorStatus, registerName: string) = # X and dh command proc cutCharacterBeforeCursor(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" status.cutCharacterBeforeCursor(registerName) proc deleteTillNextBlankLine(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" status.deleteTillNextBlankLine(registerName) proc deleteLineFromFirstLineToCurrentLine(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" status.deleteLineFromFirstLineToCurrentLine(registerName) proc deleteFromCurrentLineToLastLine(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" status.deleteFromCurrentLineToLastLine(registerName) -# s and cl commands -proc deleteCharacterAndEnterInsertMode(status: var EditorStatus) = - if currentBufStatus.buffer[currentMainWindowNode.currentLine].len > 0: - let - lineWidth = currentBufStatus.buffer[currentMainWindowNode.currentLine].len - cmdLoop = currentBufStatus.cmdLoop - loop = min(cmdLoop, lineWidth - currentMainWindowNode.currentColumn) - currentBufStatus.cmdLoop = loop - - status.deleteCharacters - - status.changeMode(Mode.insert) - # s and cl commands proc deleteCharacterAndEnterInsertMode(status: var EditorStatus, registerName: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + if currentBufStatus.buffer[currentMainWindowNode.currentLine].len > 0: let lineWidth = currentBufStatus.buffer[currentMainWindowNode.currentLine].len @@ -603,12 +736,21 @@ proc deleteCharacterAndEnterInsertMode(status: var EditorStatus, loop = min(cmdLoop, lineWidth - currentMainWindowNode.currentColumn) currentBufStatus.cmdLoop = loop - status.deleteCharacters + status.deleteCharacters(registerName) status.changeMode(Mode.insert) +# s and cl commands +proc deleteCharacterAndEnterInsertMode(status: var EditorStatus) = + const registerName = "" + status.deleteCharacterAndEnterInsertMode(registerName) + # cc/S command proc deleteCharactersAfterBlankInLine(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + const registerName = "" currentBufStatus.deleteCharactersAfterBlankInLine( status.registers, @@ -616,7 +758,40 @@ proc deleteCharactersAfterBlankInLine(status: var EditorStatus) = registerName, status.settings) +# cf command +proc deleteCharactersToCharacterAndEnterInsertMode(status: var EditorStatus, + rune: Rune, + registerName: string) = + + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + + let + currentColumn = currentMainWindowNode.currentColumn + # Get the position of a character + position = currentBufStatus.searchOneCharacterToEndOfLine( + currentMainWindowNode, + rune) + + if position > currentColumn: + currentBufStatus.cmdLoop = position - currentColumn + 1 + status.deleteCharacters + + status.changeMode(Mode.insert) + +# cf command +proc deleteCharactersToCharacterAndEnterInsertMode(status: var EditorStatus, + rune: Rune) = + + const registerName = "" + status.deleteCharactersToCharacterAndEnterInsertMode(rune, registerName) + proc enterInsertModeAfterCursor(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + let lineWidth = currentBufStatus.buffer[currentMainWindowNode.currentLine].len if lineWidth == 0: discard elif lineWidth == currentMainWindowNode.currentColumn: discard @@ -624,11 +799,19 @@ proc enterInsertModeAfterCursor(status: var EditorStatus) = status.changeMode(Mode.insert) proc toggleCharacterAndMoveRight(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + currentBufStatus.toggleCharacters( currentMainWindowNode, currentBufStatus.cmdLoop) proc replaceCurrentCharacter(status: var EditorStatus, newCharacter: Rune) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + currentBufStatus.replaceCharacters( currentMainWindowNode, status.settings.autoIndent, @@ -638,12 +821,20 @@ proc replaceCurrentCharacter(status: var EditorStatus, newCharacter: Rune) = newCharacter) proc openBlankLineBelowAndEnterInsertMode(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + currentBufStatus.openBlankLineBelow(currentMainWindowNode, status.settings.autoIndent, status.settings.tabStop) status.changeMode(Mode.insert) proc openBlankLineAboveAndEnterInsertMode(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + currentBufStatus.openBlankLineAbove(currentMainWindowNode, status.settings.autoIndent, status.settings.tabStop) @@ -658,6 +849,23 @@ proc openBlankLineAboveAndEnterInsertMode(status: var EditorStatus) = status.changeMode(Mode.insert) +proc moveToFirstNonBlankOfLineAndEnterInsertMode(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + + currentBufStatus.moveToFirstNonBlankOfLine(currentMainWindowNode) + status.changeMode(Mode.insert) + +proc moveToEndOfLineAndEnterInsertMode(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + + let lineLen = currentBufStatus.buffer[currentMainWindowNode.currentLine].len + currentMainWindowNode.currentColumn = lineLen + status.changeMode(Mode.insert) + proc closeCurrentWindow(status: var EditorStatus, height, width: int) = if status.mainWindow.numOfMainWindow == 1: return @@ -702,10 +910,20 @@ proc addRegister(status: var EditorStatus, command, registerName: string) = status.deleteCharacterAndEnterInsertMode(registerName) elif command.len == 3 and command[0 .. 1] == "ci": status.changeInnerCommand(command[2].toRune, registerName) + elif command.len == 3 and command[0 .. 1] == "yt": + status.yankCharactersToCharacter(command[2].toRune, registerName) + elif command.len == 3 and command[0 .. 1] == "cf": + status.deleteCharactersToCharacterAndEnterInsertMode( + command[2].toRune, + registerName) else: discard proc pasteFromRegister(status: var EditorStatus, command, name: string) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + if name.len == 0: return case command: @@ -753,12 +971,26 @@ proc registerCommand(status: var EditorStatus, command: seq[Rune]) = cmd == "dgg" or cmd == "d{" or cmd == "d}" or - cmd.len == 3 and cmd[0 .. 1] == "di" or + (cmd.len == 3 and cmd[0 .. 1] == "di") or cmd == "dh" or cmd == "cl" or cmd == "s" or - cmd.len == 3 and cmd[0 .. 1] == "ci": + (cmd.len == 3 and cmd[0 .. 1] == "ci") or + (cmd.len == 3 and cmd[0 .. 1] == "yt") or + (cmd.len == 3 and cmd[0 .. 1] == "cf"): status.addRegister(cmd, $registerName) +proc pasteAfterCursor(status: var EditorStatus) {.inline.} = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + else: + currentBufStatus.pasteAfterCursor(currentMainWindowNode, status.registers) + +proc pasteBeforeCursor(status: var EditorStatus) {.inline.} = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + else: + currentBufStatus.pasteBeforeCursor(currentMainWindowNode, status.registers) + proc isMovementKey(key: Rune): bool = return isControlK(key) or isControlJ(key) or @@ -793,6 +1025,24 @@ proc isChangeModeKey(key: Rune): bool = key == ord('a') or key == ord('A') +proc changeModeToInsertMode(status: var EditorStatus) {.inline.} = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + else: + status.changeMode(Mode.insert) + +proc changeModeToReplaceMode(status: var EditorStatus) {.inline.} = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + else: + status.changeMode(Mode.replace) + +proc changeModeToVisualMode(status: var EditorStatus) {.inline.} = + status.changeMode(Mode.visual) + +proc changeModeToVisualBlockMode(status: var EditorStatus) {.inline.} = + status.changeMode(Mode.visualBlock) + proc normalCommand(status: var EditorStatus, commands: seq[Rune], height, width: int) = @@ -812,7 +1062,7 @@ proc normalCommand(status: var EditorStatus, elif isControlJ(key): status.movePrevWindow elif isControlV(key): - status.changeMode(Mode.visualBlock) + status.changeModeToVisualBlockMode elif key == ord('h') or isLeftKey(key) or isBackspaceKey(key): for i in 0 ..< cmdLoop: currentMainWindowNode.keyLeft elif key == ord('l') or isRightKey(key): @@ -883,13 +1133,16 @@ proc normalCommand(status: var EditorStatus, if secondKey == ord('c'): status.deleteCharactersAfterBlankInLine status.enterInsertModeAfterCursor - if secondKey == ord('l'): + elif secondKey == ord('l'): status.deleteCharacterAndEnterInsertMode elif secondKey == ord('i'): let thirdKey = commands[2] if isParen(thirdKey) or thirdKey == ord('w'): status.changeInnerCommand(thirdKey) + elif secondKey == ord('f'): + let thirdKey = commands[2] + status.deleteCharactersToCharacterAndEnterInsertMode(thirdKey) elif key == ord('d'): let secondKey = commands[1] if secondKey == ord('d'): @@ -934,12 +1187,15 @@ proc normalCommand(status: var EditorStatus, status.yankToNextBlankLine elif secondKey == ord('l'): status.yankCharacters + elif secondKey == ord('t'): + let thirdKey = commands[2] + status.yankCharactersToCharacter(thirdKey) elif key == ord('Y'): status.yankLines elif key == ord('p'): - currentBufStatus.pasteAfterCursor(currentMainWindowNode, status.registers) + status.pasteAfterCursor elif key == ord('P'): - currentBufStatus.pasteBeforeCursor(currentMainWindowNode, status.registers) + status.pasteBeforeCursor elif key == ord('>'): for i in 0 ..< cmdLoop: currentBufStatus.addIndent(currentMainWindowNode, status.settings.tabStop) @@ -973,29 +1229,48 @@ proc normalCommand(status: var EditorStatus, status.searchNextOccurrenceReversely(word) elif key == ord('f'): let secondKey = commands[1] - currentBufStatus.searchOneCharactorToEndOfLine( - currentMainWindowNode, - secondKey) + let pos = + currentBufStatus.searchOneCharacterToEndOfLine( + currentMainWindowNode, + secondKey) + if pos != -1: + currentMainWindowNode.currentColumn = pos + elif key == ord('t'): + let secondKey = commands[1] + let pos = + currentBufStatus.searchOneCharacterToEndOfLine( + currentMainWindowNode, + secondKey) + if pos != -1: + currentMainWindowNode.currentColumn = pos - 1 elif key == ord('F'): let secondKey = commands[1] - currentBufStatus.searchOneCharactorToBeginOfLine( - currentMainWindowNode, - secondKey) + let pos = + currentBufStatus.searchOneCharacterToBeginOfLine( + currentMainWindowNode, + secondKey) + if pos != -1: + currentMainWindowNode.currentColumn = pos + elif key == ord('T'): + let secondKey = commands[1] + let pos = + currentBufStatus.searchOneCharacterToBeginOfLine( + currentMainWindowNode, + secondKey) + if pos != -1: + currentMainWindowNode.currentColumn = pos + 1 elif key == ord('R'): - status.changeMode(Mode.replace) + status.changeModeToReplaceMode elif key == ord('i'): - status.changeMode(Mode.insert) + status.changeModeToInsertMode elif key == ord('I'): - currentBufStatus.moveToFirstNonBlankOfLine(currentMainWindowNode) - status.changeMode(Mode.insert) + status.moveToFirstNonBlankOfLineAndEnterInsertMode elif key == ord('v'): - status.changeMode(Mode.visual) + status.changeModeToVisualMode elif key == ord('a'): status.enterInsertModeAfterCursor elif key == ord('A'): - let lineLen = currentBufStatus.buffer[currentMainWindowNode.currentLine].len - currentMainWindowNode.currentColumn = lineLen - status.changeMode(Mode.insert) + status.moveToEndOfLineAndEnterInsertMode elif key == ord('u'): status.bufStatus[currentBufferIndex].undo(currentMainWindowNode) elif isControlR(key): @@ -1121,15 +1396,16 @@ proc isNormalModeCommand(command: seq[Rune]): InputState = if command.len == 1: result = InputState.Continue elif command.len == 2: - if command[1] == ord('i'): + if command[1] == ord('i') or + command[1] == ord('f'): result = InputState.Continue elif command[1] == ord('c') or command[1] == ('l'): result = InputState.Valid elif command.len == 3: - if command[1] == ord('i'): - if isParen(command[2]) or - command[2] == ord('w'): - result = InputState.Valid + if command[1] == ord('f') or + (command[1] == ord('i') and + (isParen(command[2]) or command[2] == ord('w'))): + result = InputState.Valid elif command[0] == ord('d'): if command.len == 1: @@ -1163,6 +1439,11 @@ proc isNormalModeCommand(command: seq[Rune]): InputState = command[1] == ord('}') or command[1] == ord('l'): result = InputState.Valid + elif command == "yt".ru: + result = InputState.Continue + elif command.len == 3: + if command[0 .. 1] == "yt".ru: + result = InputState.Valid elif command[0] == ord('='): if command.len == 1: @@ -1182,12 +1463,24 @@ proc isNormalModeCommand(command: seq[Rune]): InputState = elif command.len == 2: result = InputState.Valid + elif command[0] == ord('t'): + if command.len == 1: + result = InputState.Continue + elif command.len == 2: + result = InputState.Valid + elif command[0] == ord('F'): if command.len == 1: result = InputState.Continue elif command.len == 2: result = InputState.Valid + elif command[0] == ord('T'): + if command.len == 1: + result = InputState.Continue + elif command.len == 2: + result = InputState.Valid + elif command[0] == ord('Z'): if command.len == 1: result = InputState.Continue @@ -1205,7 +1498,7 @@ proc isNormalModeCommand(command: seq[Rune]): InputState = elif command[0] == ('\\'): if command.len == 1: result = InputState.Continue - if command[1] == ord('r'): + elif command[1] == ord('r'): result = InputState.Valid elif command[0] == ord('"'): diff --git a/src/moepkg/platform.nim b/src/moepkg/platform.nim index 91463c3a5..4853ba2da 100644 --- a/src/moepkg/platform.nim +++ b/src/moepkg/platform.nim @@ -1,7 +1,7 @@ -import osproc, strutils +import std/[osproc, strutils] type Platforms* = enum - linux, wsl, mac, other + linux, wsl, mac, freebsd, openbsd, other proc initPlatform(): Platforms = if defined linux: @@ -10,6 +10,10 @@ proc initPlatform(): Platforms = else: result = Platforms.linux elif defined macosx: result = Platforms.mac + elif defined freebsd: + result = Platforms.freebsd + elif defined openbsd: + result = Platforms.openbsd else: result = Platforms.other diff --git a/src/moepkg/quickrun.nim b/src/moepkg/quickrun.nim index ed2a55925..6cbf04cdf 100644 --- a/src/moepkg/quickrun.nim +++ b/src/moepkg/quickrun.nim @@ -1,4 +1,4 @@ -import osproc, terminal, times +import std/[osproc, terminal, times] import syntax/highlite import unicodeext, settings, bufferstatus, gapbuffer, messages, ui, editorstatus, movement, window, fileutils, commandline diff --git a/src/moepkg/recentfilemode.nim b/src/moepkg/recentfilemode.nim index fb54fbd0f..9d8ef6732 100644 --- a/src/moepkg/recentfilemode.nim +++ b/src/moepkg/recentfilemode.nim @@ -1,4 +1,4 @@ -import os, re, terminal +import std/[os, re, terminal] import editorstatus, ui, unicodeext, bufferstatus, movement, gapbuffer, messages, window diff --git a/src/moepkg/register.nim b/src/moepkg/register.nim index f84393750..1cea957fb 100644 --- a/src/moepkg/register.nim +++ b/src/moepkg/register.nim @@ -1,4 +1,4 @@ -import options, strutils, unicode +import std/[options, strutils, unicode] import independentutils, clipboard, settings type Register* = object diff --git a/src/moepkg/replacemode.nim b/src/moepkg/replacemode.nim index 4f5494a3d..97f252fda 100644 --- a/src/moepkg/replacemode.nim +++ b/src/moepkg/replacemode.nim @@ -1,4 +1,4 @@ -import terminal, times, unicode +import std/[terminal, times, unicode] import editorstatus, ui, movement, editor, bufferstatus, gapbuffer, window, settings diff --git a/src/moepkg/search.nim b/src/moepkg/search.nim index 5db0bd6bf..37286fefe 100644 --- a/src/moepkg/search.nim +++ b/src/moepkg/search.nim @@ -1,4 +1,4 @@ -import system, terminal, strutils +import std/[terminal, strutils] import editorstatus, gapbuffer, commandview, movement, commandline, unicodeext type diff --git a/src/moepkg/settings.nim b/src/moepkg/settings.nim index 3d5faea53..a9d1f149b 100644 --- a/src/moepkg/settings.nim +++ b/src/moepkg/settings.nim @@ -1,13 +1,9 @@ -import parsetoml, os, json, macros, times, options, strformat, osproc, strutils -from strutils import parseEnum, endsWith, parseInt -export TomlError - -when (NimMajor, NimMinor, NimPatch) > (1, 3, 0): - # This addresses a breaking change in https://github.com/nim-lang/Nim/pull/14046. - from strutils import nimIdentNormalize - export strutils.nimIdentNormalize - +import std/[os, json, macros, times, options, strformat, osproc, + strutils] import ui, color, unicodeext, highlight, platform, independentutils +import parsetoml + +export TomlError type DebugWindowNodeSettings* = object enable*: bool @@ -192,6 +188,10 @@ type EditorSettings* = object highlightSettings*: HighlightSettings persist*: PersistSettings +type InvalidItem = object + name: string + val: string + # Warning: inherit from a more precise exception type like ValueError, IOError or OSError. # If these don't suit, inherit from CatchableError or Defect. [InheritFromException] type InvalidItemError* = object of ValueError @@ -950,7 +950,6 @@ proc parseSettingsFile*(settings: TomlValueRef): EditorSettings = if settings["Standard"].contains("indentationLines"): result.view.indentationLines = settings["Standard"]["indentationLines"].getbool() - if settings.contains("Clipboard"): if settings["Clipboard"].contains("enable"): result.clipboard.enable = settings["Clipboard"]["enable"].getbool() @@ -1652,354 +1651,405 @@ proc parseSettingsFile*(settings: TomlValueRef): EditorSettings = if result.editorColorTheme == ColorTheme.vscode: result.editorColorTheme = loadVSCodeTheme() -proc validateTomlConfig(toml: TomlValueRef): Option[string] = - template validateStandardTable() = - for item in json["Standard"].pairs: - case item.key: - of "theme": - var correctValue = false - if item.val["value"].getStr == "vscode": - correctValue = true - else: - for theme in ColorTheme: - if $theme == item.val["value"].getStr: - correctValue = true - if not correctValue: - return some($item) - of "number", - "currentNumber", - "cursorLine", - "statusLine", - "tabLine", - "syntax", - "indentationLines", - "autoCloseParen", - "autoIndent", - "ignorecase", - "smartcase", - "disableChangeCursor", - "autoSave", - "liveReloadOfConf", - "incrementalSearch", - "popUpWindowInExmode", - "autoDeleteParen", - "smoothScroll": - if not (item.val["type"].getStr == "bool"): - return some($item) - of "tabStop", "autoSaveInterval", "smoothScrollSpeed": - if not (item.val["type"].getStr == "integer" and - parseInt(item.val["value"].getStr) > 0): return some($item) - of "defaultCursor", - "normalModeCursor", - "insertModeCursor": - let val = item.val["value"].getStr - var correctValue = false - for cursorType in CursorType: - if val == $cursorType: - correctValue = true - break - - if not correctValue: - return some($item) +proc validateStandardTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "theme": + var correctValue = false + if val.getStr == "vscode": + correctValue = true else: - return some($item) - - template validateClipboardTable() = - for item in json["Clipboard"].pairs: - case item.key: - of "enable": - if not (item.val["type"].getStr == "bool"): - return some($item) - of "toolOnLinux": - if not (item.val["type"].getStr == "string"): - return some($item) - else: - return some($item) - - template validateTabLineTable() = - for item in json["TabLine"].pairs: - case item.key: - of "allBuffer": - if not (item.val["type"].getStr == "bool"): - return some($item) - else: - return some($item) - - template validateStatusLineTable() = - for item in json["StatusLine"].pairs: - case item.key: - of "multipleStatusLine", - "merge", - "mode", - "filename", - "chanedMark", - "line", - "column", - "encoding", - "language", - "directory", - "gitbranchName", - "showGitInactive", - "showModeInactive": - if not (item.val["type"].getStr == "bool"): - return some($item) - else: - return some($item) - - template validateBuildOnSaveTable() = - for item in json["BuildOnSave"].pairs: - case item.key: - of "enable": - if not (item.val["type"].getStr == "bool"): - return some($item) - of "workspaceRoot", - "command": - if not (item.val["type"].getStr == "string"): - return some($item) - else: - return some($item) - - template validateHighlightTable() = - for item in json["Highlight"].pairs: - case item.key: - of "reservedWord": - if item.val["type"].getStr == "array": - for word in item.val["value"]: - if word["type"].getStr != "string": - return some($item) - of "currentLine", - "fullWidthSpace", - "trailingSpaces", - "replaceText", - "pairOfParen", - "currentWord": - if not (item.val["type"].getStr == "bool"): - return some($item) - else: - return some($item) - - template validateAutoBackupTable() = - for item in json["AutoBackup"].pairs: - case item.key: - of "enable", "showMessages": - if item.val["type"].getStr != "bool": - return some($item) - of "idleTime", - "interval": - if item.val["type"].getStr != "integer": - return some($item) - of "backupDir": - if item.val["type"].getStr != "string": - return some($item) - of "dirToExclude": - if item.val["type"].getStr == "array": - for word in item.val["value"]: - if word["type"].getStr != "string": - return some($item) - else: - return some($item) - - template validateQuickRunTable() = - for item in json["QuickRun"].pairs: - case item.key: - of "saveBufferWhenQuickRun": - if item.val["type"].getStr != "bool": - return some($item) - of "command", - "nimAdvancedCommand", - "ClangOptions", - "CppOptions", - "NimOptions", - "shOptions", - "bashOptions": - if item.val["type"].getStr != "string": - return some($item) - of "timeout": - if item.val["type"].getStr != "integer": - return some($item) - else: - return some($item) - - template validateNotificationTable() = - for item in json["Notification"].pairs: - case item.key: - of "screenNotifications", - "logNotifications", - "autoBackupScreenNotify", - "autoBackupLogNotify", - "autoSaveScreenNotify", - "autoSaveLogNotify", - "yankScreenNotify", - "yankLogNotify", - "deleteScreenNotify", - "deleteLogNotify", - "saveScreenNotify", - "saveLogNotify", - "quickRunScreenNotify", - "quickRunLogNotify", - "buildOnSaveScreenNotify", - "buildOnSaveLogNotify", - "filerScreenNotify", - "filerLogNotify", - "restoreScreenNotify", - "restoreLogNotify": - if item.val["type"].getStr != "bool": - return some($item) - else: - return some($item) - - template validateFilerTable() = - for item in json["Filer"].pairs: - case item.key: - of "showIcons": - if item.val["type"].getStr != "bool": - return some($item) - else: - return some($item) + for theme in ColorTheme: + if $theme == val.getStr: + correctValue = true + if not correctValue: + return some(InvalidItem(name: $key, val: $val)) + of "number", + "currentNumber", + "cursorLine", + "statusLine", + "tabLine", + "syntax", + "indentationLines", + "autoCloseParen", + "autoIndent", + "ignorecase", + "smartcase", + "disableChangeCursor", + "autoSave", + "liveReloadOfConf", + "incrementalSearch", + "popUpWindowInExmode", + "autoDeleteParen", + "systemClipboard", + "smoothScroll": + if not (val.kind == TomlValueKind.Bool): + return some(InvalidItem(name: $key, val: $val)) + of "tabStop", "autoSaveInterval", "smoothScrollSpeed": + if not (val.kind == TomlValueKind.Int and val.getInt > 0): + return some(InvalidItem(name: $key, val: $val)) + of "defaultCursor", + "normalModeCursor", + "insertModeCursor": + let val = val.getStr + var correctValue = false + for cursorType in CursorType: + if val == $cursorType: + correctValue = true + break + if not correctValue: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateClipboardTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "enable": + if not (val.kind == TomlValueKind.Bool): + return some(InvalidItem(name: $key, val: $val)) + of "toolOnLinux": + if not ( + (val.kind == TomlValueKind.String) and + (val.getStr == "none" or + val.getStr == "xclip" or + val.getStr == "xsel" or + val.getStr == "wl-clipboard")): return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateTabLineTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "allBuffer": + if not (val.kind == TomlValueKind.Bool): + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateBuildOnSaveTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "enable": + if not (val.kind == TomlValueKind.Bool): + return some(InvalidItem(name: $key, val: $val)) + of "workspaceRoot", + "command": + if not (val.kind == TomlValueKind.String): + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateStatusLineTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "multipleStatusLine", + "merge", + "mode", + "filename", + "chanedMark", + "line", + "column", + "encoding", + "language", + "directory", + "gitbranchName", + "showGitInactive", + "showModeInactive": + if not (val.kind == TomlValueKind.Bool): + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateWorkSpaceTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "workSpaceLine": + if not (val.kind == TomlValueKind.Bool): + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateHighlightTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "reservedWord": + if val.kind == TomlValueKind.Array: + for key, val in val.getTable: + if val.kind != TomlValueKind.String: + return some(InvalidItem(name: $key, val: $val)) + of "currentLine", + "fullWidthSpace", + "trailingSpaces", + "replaceText", + "pairOfParen", + "currentWord": + if not (val.kind == TomlValueKind.Bool): + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateAutoBackupTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "enable", "showMessages": + if val.kind != TomlValueKind.Bool: + return some(InvalidItem(name: $key, val: $val)) + of "idleTime", + "interval": + if val.kind != TomlValueKind.Int: + return some(InvalidItem(name: $key, val: $val)) + of "backupDir": + if val.kind != TomlValueKind.String: + return some(InvalidItem(name: $key, val: $val)) + of "dirToExclude": + if val.kind != TomlValueKind.Array: + for item in val.getElems: + if item.kind != TomlValueKind.String: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateQuickRunTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "saveBufferWhenQuickRun": + if val.kind != TomlValueKind.Bool: + return some(InvalidItem(name: $key, val: $val)) + of "command", + "nimAdvancedCommand", + "ClangOptions", + "CppOptions", + "NimOptions", + "shOptions", + "bashOptions": + if val.kind != TomlValueKind.String: + return some(InvalidItem(name: $key, val: $val)) + of "timeout": + if val.kind != TomlValueKind.Int: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateNotificationTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "screenNotifications", + "logNotifications", + "autoBackupScreenNotify", + "autoBackupLogNotify", + "autoSaveScreenNotify", + "autoSaveLogNotify", + "yankScreenNotify", + "yankLogNotify", + "deleteScreenNotify", + "deleteLogNotify", + "saveScreenNotify", + "saveLogNotify", + "workspaceScreenNotify", + "workspaceLogNotify", + "quickRunScreenNotify", + "quickRunLogNotify", + "buildOnSaveScreenNotify", + "buildOnSaveLogNotify", + "filerScreenNotify", + "filerLogNotify", + "restoreScreenNotify", + "restoreLogNotify": + if val.kind != TomlValueKind.Bool: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateFilerTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "showIcons": + if val.kind != TomlValueKind.Bool: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) - template validateAutocompleteTable() = - for item in json["Autocomplete"].pairs: - case item.key: +proc validateAutocompleteTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: of "enable": - if item.val["type"].getStr != "bool": - return some($item) + if val.kind != TomlValueKind.Bool: + return some(InvalidItem(name: $key, val: $val)) else: - return some($item) - - template validatePersistTable() = - for item in json["Persist"].pairs: - case item.key: - of "exCommand", "search", "cursorPosition": - if item.val["type"].getStr != "bool": - return some($item) - else: - return some($item) - - template validateDebugTable() = - for item in json["Debug"].pairs: - case item.key: - # Check [Debug.WindowNode] - of "WindowNode": - for item in json["Debug"]["WindowNode"].pairs: - case item.key: - of "enable", - "currentWindow", - "index", - "windowIndex", - "bufferIndex", - "parentIndex", - "childLen", - "splitType", - "haveCursesWin", - "y", - "x", - "h", - "w", - "currentLine", - "currentColumn", - "expandedColumn", - "cursor": - if item.val["type"].getStr != "bool": - return some($item) - else: - return some($item) - # Check [Debug.EditorView] - of "EditorView": - for item in json["Debug"]["EditorView"].pairs: - case item.key: - of "enable", - "widthOfLineNum", - "height", - "width", - "originalLine", - "start", - "length": - if item.val["type"].getStr != "bool": - return some($item) - else: - return some($item) - # Check [Debug.BufferStatus] - of "BufferStatus": - for item in json["Debug"]["BufferStatus"].pairs: - case item.key: - of "enable", - "bufferIndex", - "path", - "openDir", - "currentMode", - "prevMode", - "language", - "encoding", - "countChange", - "cmdLoop", - "lastSaveTime", - "bufferLen": - if item.val["type"].getStr != "bool": - return some($item) - else: - return some($item) - else: - return some($item) - - template validateThemeTable() = - let editorColors = ColorThemeTable[ColorTheme.config].EditorColor - for item in json["Theme"].pairs: - case item.key: - of "baseTheme": - var correctKey = false - for theme in ColorTheme: - if $theme == item.val["value"].getStr: - correctKey = true - if not correctKey: return some($item) - else: - # Check color names - var correctKey = false - for field, val in editorColors.fieldPairs: - if item.key == field and - item.val["type"].getStr == "string": - for color in Color: - if item.val["value"].getStr == $color: - correctKey = true - break - if correctKey: break - if not correctKey: - return some($item) - - let json = toml.toJson - - for table in json.keys: - case table: + return some(InvalidItem(name: $key, val: $val)) + +proc validatePersistTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "exCommand", "search", "cursorPosition": + if val.kind != TomlValueKind.Bool: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateDebugTable(table: TomlValueRef): Option[InvalidItem] = + for key, val in table.getTable: + case key: + of "WorkSpace": + # Check [Debug.WorkSpace] + for key,val in table["WorkSpace"].getTable: + case key: + of "enable", + "numOfWorkSpaces", + "currentWorkSpaceIndex": + if val.kind != TomlValueKind.Bool: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + # Check [Debug.WindowNode] + of "WindowNode": + for key, val in table["WindowNode"].getTable: + case key: + of "enable", + "currentWindow", + "index", + "windowIndex", + "bufferIndex", + "parentIndex", + "childLen", + "splitType", + "haveCursesWin", + "y", + "x", + "h", + "w", + "currentLine", + "currentColumn", + "expandedColumn", + "cursor": + if val.kind != TomlValueKind.Bool: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + # Check [Debug.EditorView] + of "EditorView": + for key, val in table["EditorView"].getTable: + case key: + of "enable", + "widthOfLineNum", + "height", + "width", + "originalLine", + "start", + "length": + if val.kind != TomlValueKind.Bool: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + # Check [Debug.BufferStatus] + of "BufferStatus": + for key, val in table["BufferStatus"].getTable: + case key: + of "enable", + "bufferIndex", + "path", + "openDir", + "currentMode", + "prevMode", + "language", + "encoding", + "countChange", + "cmdLoop", + "lastSaveTime", + "bufferLen": + if val.kind != TomlValueKind.Bool: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + else: + return some(InvalidItem(name: $key, val: $val)) + +proc validateThemeTable(table: TomlValueRef): Option[InvalidItem] = + let editorColors = ColorThemeTable[ColorTheme.config].EditorColor + for key, val in table.getTable: + case key: + of "baseTheme": + var correctKey = false + for theme in ColorTheme: + if $theme == val.getStr: + correctKey = true + if not correctKey: return some(InvalidItem(name: $key, val: $val)) + else: + # Check color names + var correctKey = false + for field, fieldVal in editorColors.fieldPairs: + if key == field and + val.kind == TomlValueKind.String: + for color in Color: + if val.getStr == $color: + correctKey = true + break + if correctKey: break + if not correctKey: + return some(InvalidItem(name: $key, val: $val)) + +proc validateTomlConfig(toml: TomlValueRef): Option[InvalidItem] = + for key, val in toml.getTable: + case key: of "Standard": - validateStandardTable() + let r = validateStandardTable(val) + if r.isSome: return r of "Clipboard": - validateClipboardTable() + let r = validateClipboardTable(val) + if r.isSome: return r of "TabLine": - validateTabLineTable() + let r = validateTabLineTable(val) + if r.isSome: return r of "StatusLine": - validateStatusLineTable() + let r = validateStatusLineTable(val) + if r.isSome: return r of "BuildOnSave": - validateBuildOnSaveTable() + let r = validateBuildOnSaveTable(val) + if r.isSome: return r + of "WorkSpace": + let r = validateWorkSpaceTable(val) + if r.isSome: return r of "Highlight": - validateHighlightTable() + let r = validateHighlightTable(val) + if r.isSome: return r of "AutoBackup": - validateAutoBackupTable() + let r = validateAutoBackupTable(val) + if r.isSome: return r of "QuickRun": - validateQuickRunTable() + let r = validateQuickRunTable(val) + if r.isSome: return r of "Notification": - validateNotificationTable() + let r = validateNotificationTable(val) + if r.isSome: return r of "Filer": - validateFilerTable() + let r = validateFilerTable(val) + if r.isSome: return r of "Theme": - validateThemeTable() + let r = validateThemeTable(val) + if r.isSome: return r of "Autocomplete": - validateAutocompleteTable() - of "Debug": - validateDebugTable() + let r = validateAutocompleteTable(val) + if r.isSome: return r of "Persist": - validatePersistTable() - else: discard + let r = validatePersistTable(val) + if r.isSome: return r + of "Debug": + let r = validateDebugTable(val) + if r.isSome: return r + else: + return some(InvalidItem(name: $key, val: $val)) + +proc toValidateErrorMessage(invalidItem: InvalidItem): string = + # Remove '\n' + let lines = invalidItem.val.splitLines + + var val = "" + for i in 0 ..< lines.len: + val &= lines[i] + if i < lines.high - 1: val &= " " - return none(string) + result = fmt"(name: {invalidItem.name}, val: {val})" proc loadSettingFile*(): EditorSettings = let filename = getConfigDir() / "moe" / "moerc.toml" @@ -2011,8 +2061,9 @@ proc loadSettingFile*(): EditorSettings = toml = parsetoml.parseFile(filename) invalidItem = toml.validateTomlConfig - if invalidItem != none(string): - raise newException(InvalidItemError, $invalidItem) + if invalidItem != none(InvalidItem): + let errorMessage = toValidateErrorMessage(invalidItem.get) + raise newException(InvalidItemError, $errorMessage) else: return parseSettingsFile(toml) @@ -2068,8 +2119,8 @@ proc generateTomlConfigStr*(settings: EditorSettings): string = result.addLine "" - result.addLine fmt "[StatusBar]" - result.addLine fmt "multipleStatusBar = {$settings.statusLine.multipleStatusLine}" + result.addLine fmt "[StatusLine]" + result.addLine fmt "multipleStatusLine = {$settings.statusLine.multipleStatusLine}" result.addLine fmt "merge = {$settings.statusLine.merge }" result.addLine fmt "mode = {$settings.statusLine.mode }" result.addLine fmt "filename = {$settings.statusLine.filename}" diff --git a/src/moepkg/statusline.nim b/src/moepkg/statusline.nim index bbc68a28e..dd6807ef4 100644 --- a/src/moepkg/statusline.nim +++ b/src/moepkg/statusline.nim @@ -1,6 +1,6 @@ -import ui, strutils, strformat, os, osproc +import std/[strutils, strformat, os, osproc] import syntax/highlite -import bufferstatus, color, unicodeext, settings, window, gapbuffer +import ui, bufferstatus, color, unicodeext, settings, window, gapbuffer type StatusLine* = object window*: Window diff --git a/src/moepkg/suggestionwindow.nim b/src/moepkg/suggestionwindow.nim index c912b036e..92b80dd8c 100644 --- a/src/moepkg/suggestionwindow.nim +++ b/src/moepkg/suggestionwindow.nim @@ -1,4 +1,4 @@ -import critbits, unicode, sugar, options, sequtils, unicode +import std/[critbits, sugar, options, sequtils, unicode] import ui, window, generalautocomplete, bufferstatus, gapbuffer, color, editorstatus diff --git a/src/moepkg/syntax/highlite.nim b/src/moepkg/syntax/highlite.nim index 319a50c9a..7284b18d9 100644 --- a/src/moepkg/syntax/highlite.nim +++ b/src/moepkg/syntax/highlite.nim @@ -65,8 +65,8 @@ ## import - strutils -from algorithm import binarySearch + std/strutils +from std/algorithm import binarySearch type TokenClass* = enum diff --git a/src/moepkg/syntax/syntaxnim.nim b/src/moepkg/syntax/syntaxnim.nim index 466aec0dc..888368a4b 100644 --- a/src/moepkg/syntax/syntaxnim.nim +++ b/src/moepkg/syntax/syntaxnim.nim @@ -31,8 +31,8 @@ # distribution, for details about the copyright. # -import strutils -from algorithm import binarySearch +import std/strutils +from std/algorithm import binarySearch import highlite diff --git a/src/moepkg/tabline.nim b/src/moepkg/tabline.nim index db6fc9f34..daad5ba48 100644 --- a/src/moepkg/tabline.nim +++ b/src/moepkg/tabline.nim @@ -1,4 +1,4 @@ -import strutils, terminal, unicode +import std/[strutils, terminal, unicode] import ui, window, color, bufferstatus, independentutils proc writeTab*(tabWin: var Window, diff --git a/src/moepkg/ui.nim b/src/moepkg/ui.nim index f5a9b9255..23b9a980d 100644 --- a/src/moepkg/ui.nim +++ b/src/moepkg/ui.nim @@ -1,8 +1,8 @@ -import strformat, osproc, strutils +import std/[strformat, osproc, strutils] when not defined unitTest: - import posix + import std/posix -from os import execShellCmd +from std/os import execShellCmd import ncurses import unicodeext, color @@ -162,7 +162,7 @@ proc append*(win: var Window, # Not write when running unit tests when not defined unitTest: win.cursesWindow.wattron(cint(ncurses.COLOR_PAIR(ord(color)))) - mvwaddstr(win.cursesWindow, cint(win.y), cint(win.x), $str) + mvwaddstr(win.cursesWindow, cint(win.y), cint(win.x), str) win.x += str.toRunes.width diff --git a/src/moepkg/undoredostack.nim b/src/moepkg/undoredostack.nim index 941323898..5f63ddcde 100644 --- a/src/moepkg/undoredostack.nim +++ b/src/moepkg/undoredostack.nim @@ -1,4 +1,4 @@ -import sequtils, tables +import std/[sequtils, tables] type CommandKind = enum diff --git a/src/moepkg/unicodeext.nim b/src/moepkg/unicodeext.nim index a6980e6ee..ee0020d78 100644 --- a/src/moepkg/unicodeext.nim +++ b/src/moepkg/unicodeext.nim @@ -1,4 +1,4 @@ -import unicode, strutils, sequtils, strutils, strformat +import std/[unicode, sequtils, strutils, strformat, os] import unicodedb/widths import gapbuffer export unicode @@ -377,7 +377,6 @@ proc toggleCase*(ch: Rune): Rune = result = result.toUpper() return result -from os import `/` proc `/`*(runes1, runes2: seq[Rune]): seq[Rune] {.inline.} = toRunes($runes1 / $runes2) @@ -424,6 +423,7 @@ proc encodeUTF8*(r: Rune): seq[uint32] = result.add mbLead or i shl 6 result.add mbLead or i and mbMask -from os import absolutePath proc absolutePath*(runes: seq[Rune]): seq[Rune] {.inline.} = - absolutePath($runes).ru + result = absolutePath($runes).ru + if result.len > 0 and dirExists($runes) and result[^1] != ru '/': + result &= ru "/" diff --git a/src/moepkg/visualmode.nim b/src/moepkg/visualmode.nim index fecd976d3..b8177339c 100644 --- a/src/moepkg/visualmode.nim +++ b/src/moepkg/visualmode.nim @@ -1,6 +1,6 @@ -import terminal, strutils, sequtils, times +import std/[terminal, strutils, sequtils, times] import editorstatus, ui, gapbuffer, unicodeext, window, movement, editor, - bufferstatus, settings, register + bufferstatus, settings, register, messages, commandline proc initSelectArea(startLine, startColumn: int): SelectArea = result.startLine = startLine @@ -78,7 +78,12 @@ proc deleteBuffer(bufStatus: var BufferStatus, registers: var Registers, windowNode: WindowNode, area: SelectArea, - settings: EditorSettings) = + settings: EditorSettings, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return if bufStatus.buffer.len == 1 and bufStatus.buffer[windowNode.currentLine].len < 1: return @@ -116,16 +121,24 @@ proc deleteBuffer(bufStatus: var BufferStatus, elif area.startColumn > 0: area.startColumn - 1 else: 0 + windowNode.currentColumn = column windowNode.expandedColumn = column inc(bufStatus.countChange) + bufStatus.isUpdate = true + proc deleteBufferBlock(bufStatus: var BufferStatus, registers: var Registers, windowNode: WindowNode, area: SelectArea, - settings: EditorSettings) = + settings: EditorSettings, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return if bufStatus.buffer.len == 1 and bufStatus.buffer[windowNode.currentLine].len < 1: return @@ -148,12 +161,20 @@ proc deleteBufferBlock(bufStatus: var BufferStatus, windowNode.currentLine = min(area.startLine, bufStatus.buffer.high) windowNode.currentColumn = area.startColumn + inc(bufStatus.countChange) + bufStatus.isUpdate = true + proc addIndent(bufStatus: var BufferStatus, windowNode: WindowNode, area: SelectArea, - tabStop: int) = + tabStop: int, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return windowNode.currentLine = area.startLine for i in area.startLine .. area.endLine: @@ -165,7 +186,12 @@ proc addIndent(bufStatus: var BufferStatus, proc deleteIndent(bufStatus: var BufferStatus, windowNode: WindowNode, area: SelectArea, - tabStop: int) = + tabStop: int, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return windowNode.currentLine = area.startLine for i in area.startLine .. area.endLine: @@ -174,7 +200,15 @@ proc deleteIndent(bufStatus: var BufferStatus, windowNode.currentLine = area.startLine -proc insertIndent(bufStatus: var BufferStatus, area: SelectArea, tabStop: int) = +proc insertIndent(bufStatus: var BufferStatus, + area: SelectArea, + tabStop: int, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return + for i in area.startLine .. area.endLine: let oldLine = bufStatus.buffer[i] var newLine = bufStatus.buffer[i] @@ -183,7 +217,15 @@ proc insertIndent(bufStatus: var BufferStatus, area: SelectArea, tabStop: int) = bufStatus.buffer[i].high)) if oldLine != newLine: bufStatus.buffer[i] = newLine -proc replaceCharacter(bufStatus: var BufferStatus, area: SelectArea, ch: Rune) = +proc replaceCharacter(bufStatus: var BufferStatus, + area: SelectArea, + ch: Rune, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return + for i in area.startLine .. area.endLine: let oldLine = bufStatus.buffer[i] var newLine = bufStatus.buffer[i] @@ -201,7 +243,12 @@ proc replaceCharacter(bufStatus: var BufferStatus, area: SelectArea, ch: Rune) = proc replaceCharacterBlock(bufStatus: var BufferStatus, area: SelectArea, - ch: Rune) = + ch: Rune, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return for i in area.startLine .. area.endLine: let oldLine = bufStatus.buffer[i] @@ -212,13 +259,25 @@ proc replaceCharacterBlock(bufStatus: var BufferStatus, proc joinLines(bufStatus: var BufferStatus, windowNode: WindowNode, - area: SelectArea) = + area: SelectArea, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return for i in area.startLine .. area.endLine: windowNode.currentLine = area.startLine bufStatus.joinLine(windowNode) -proc toLowerString(bufStatus: var BufferStatus, area: SelectArea) = +proc toLowerString(bufStatus: var BufferStatus, + area: SelectArea, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return + for i in area.startLine .. area.endLine: let oldLine = bufStatus.buffer[i] var newLine = bufStatus.buffer[i] @@ -237,7 +296,14 @@ proc toLowerString(bufStatus: var BufferStatus, area: SelectArea) = inc(bufStatus.countChange) -proc toLowerStringBlock(bufStatus: var BufferStatus, area: SelectArea) = +proc toLowerStringBlock(bufStatus: var BufferStatus, + area: SelectArea, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return + for i in area.startLine .. area.endLine: let oldLine = bufStatus.buffer[i] var newLine = bufStatus.buffer[i] @@ -245,7 +311,14 @@ proc toLowerStringBlock(bufStatus: var BufferStatus, area: SelectArea) = newLine[j] = oldLine[j].toLower if oldLine != newLine: bufStatus.buffer[i] = newLine -proc toUpperString(bufStatus: var BufferStatus, area: SelectArea) = +proc toUpperString(bufStatus: var BufferStatus, + area: SelectArea, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return + for i in area.startLine .. area.endLine: let oldLine = bufStatus.buffer[i] var newLine = bufStatus.buffer[i] @@ -264,7 +337,14 @@ proc toUpperString(bufStatus: var BufferStatus, area: SelectArea) = inc(bufStatus.countChange) -proc toUpperStringBlock(bufStatus: var BufferStatus, area: SelectArea) = +proc toUpperStringBlock(bufStatus: var BufferStatus, + area: SelectArea, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return + for i in area.startLine .. area.endLine: let oldLine = bufStatus.buffer[i] var newLine = bufStatus.buffer[i] @@ -292,8 +372,9 @@ proc getInsertBuffer(status: var Editorstatus): seq[Rune] = status.settings.tabStop) break elif isDcKey(key): - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) break elif isBackspaceKey(key): @@ -316,12 +397,25 @@ proc getInsertBuffer(status: var Editorstatus): seq[Rune] = status.settings.autoCloseParen, key) +proc enterInsertMode(status: var EditorStatus) = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + else: + currentMainWindowNode.currentLine = currentBufStatus.selectArea.startLine + currentMainWindowNode.currentColumn = 0 + status.changeMode(Mode.insert) + proc insertCharBlock(bufStatus: var BufferStatus, windowNode: var WindowNode, insertBuffer: seq[Rune], area: SelectArea, tabStop: int, - autoCloseParen: bool) = + autoCloseParen: bool, + commandLine: var CommandLine) = + + if bufStatus.isReadonly: + commandLine.writeReadonlyModeWarning + return if area.startLine == area.endLine: return @@ -356,35 +450,40 @@ proc visualCommand(status: var EditorStatus, area: var SelectArea, key: Rune) = currentBufStatus.deleteBuffer(status.registers, currentMainWindowNode, area, - status.settings) + status.settings, + status.commandLine) elif key == ord('>'): currentBufStatus.addIndent(currentMainWindowNode, area, - status.settings.tabStop) + status.settings.tabStop, + status.commandLine) elif key == ord('<'): currentBufStatus.deleteIndent(currentMainWindowNode, area, - status.settings.tabStop) + status.settings.tabStop, + status.commandLine) elif key == ord('J'): - currentBufStatus.joinLines(currentMainWindowNode, area) + currentBufStatus.joinLines(currentMainWindowNode, area, status.commandLine) elif key == ord('u'): - currentBufStatus.toLowerString(area) + currentBufStatus.toLowerString(area, status.commandLine) elif key == ord('U'): - currentBufStatus.toUpperString(area) + currentBufStatus.toUpperString(area, status.commandLine) elif key == ord('r'): let ch = currentMainWindowNode.getKey if not isEscKey(ch): - currentBufStatus.replaceCharacter(area, ch) + currentBufStatus.replaceCharacter(area, ch, status.commandLine) elif key == ord('I'): - currentMainWindowNode.currentLine = currentBufStatus.selectArea.startLine - currentMainWindowNode.currentColumn = 0 - status.changeMode(Mode.insert) + status.enterInsertMode else: discard proc visualBlockCommand(status: var EditorStatus, area: var SelectArea, key: Rune) = area.swapSelectArea template insertCharacterMultipleLines() = + if currentBufStatus.isReadonly: + status.commandLine.writeReadonlyModeWarning + return + status.changeMode(Mode.insert) currentMainWindowNode.currentLine = area.startLine @@ -398,7 +497,8 @@ proc visualBlockCommand(status: var EditorStatus, area: var SelectArea, key: Run insertBuffer, area, status.settings.tabStop, - status.settings.autoCloseParen) + status.settings.autoCloseParen, + status.commandLine) else: currentMainWindowNode.currentLine = area.startLine currentMainWindowNode.currentColumn = area.startColumn @@ -412,22 +512,28 @@ proc visualBlockCommand(status: var EditorStatus, area: var SelectArea, key: Run currentBufStatus.deleteBufferBlock(status.registers, currentMainWindowNode, area, - status.settings) + status.settings, + status.commandLine) elif key == ord('>'): - currentBufStatus.insertIndent(area, status.settings.tabStop) + currentBufStatus.insertIndent( + area, + status.settings.tabStop, + status.commandLine) elif key == ord('<'): currentBufStatus.deleteIndent(currentMainWindowNode, area, - status.settings.tabStop) + status.settings.tabStop, + status.commandLine) elif key == ord('J'): - currentBufStatus.joinLines(currentMainWindowNode, area) + currentBufStatus.joinLines(currentMainWindowNode, area, status.commandLine) elif key == ord('u'): - currentBufStatus.toLowerStringBlock(area) + currentBufStatus.toLowerStringBlock(area, status.commandLine) elif key == ord('U'): - currentBufStatus.toUpperStringBlock(area) + currentBufStatus.toUpperStringBlock(area, status.commandLine) elif key == ord('r'): let ch = currentMainWindowNode.getKey - if not isEscKey(ch): currentBufStatus.replaceCharacterBlock(area, ch) + if not isEscKey(ch): + currentBufStatus.replaceCharacterBlock(area, ch, status.commandLine) elif key == ord('I'): insertCharacterMultipleLines() else: discard @@ -503,13 +609,16 @@ proc visualMode*(status: var EditorStatus) = elif key == ord('g'): if getKey(currentMainWindowNode) == ord('g'): moveToFirstLine(status) - elif key == ord('i'): - currentMainWindowNode.currentLine = currentBufStatus.selectArea.startLine - status.changeMode(Mode.insert) + else: + currentMainWindowNode.currentLine = currentBufStatus.selectArea.startLine + status.changeMode(Mode.insert) else: if isVisualBlockMode(currentBufStatus.mode): status.visualBlockCommand(currentBufStatus.selectArea, key) else: status.visualCommand(currentBufStatus.selectArea, key) + status.update - status.changeMode(Mode.normal) + + if isNormalMode(currentBufStatus.mode, currentBufStatus.prevMode): + status.changeMode(Mode.normal) diff --git a/src/moepkg/window.nim b/src/moepkg/window.nim index 195490b01..66be9383a 100644 --- a/src/moepkg/window.nim +++ b/src/moepkg/window.nim @@ -1,4 +1,4 @@ -import heapqueue, options +import std/[heapqueue, options] import ui, editorview, gapbuffer, color, cursor, highlight, unicodeext # vertical is default diff --git a/tests/tbackup.nim b/tests/tbackup.nim index 2217096b7..13d3af5b3 100644 --- a/tests/tbackup.nim +++ b/tests/tbackup.nim @@ -1,4 +1,4 @@ -import unittest, times, os +import std/[unittest, times, os] import moepkg/unicodeext include moepkg/backup diff --git a/tests/tclipboard.nim b/tests/tclipboard.nim index 48944b445..e70642d94 100644 --- a/tests/tclipboard.nim +++ b/tests/tclipboard.nim @@ -1,4 +1,4 @@ -import unittest +import std/unittest import moepkg/[settings, unicodeext, register, clipboard] include moepkg/[platform] diff --git a/tests/tcommandview.nim b/tests/tcommandview.nim index ab3e485e8..34639521b 100644 --- a/tests/tcommandview.nim +++ b/tests/tcommandview.nim @@ -1,4 +1,4 @@ -import unittest, os, strutils +import std/[unittest, os, strutils] import moepkg/[editorstatus, unicodeext] include moepkg/commandview diff --git a/tests/tconfigmode.nim b/tests/tconfigmode.nim index 72c16b2e0..d2fa0b33c 100644 --- a/tests/tconfigmode.nim +++ b/tests/tconfigmode.nim @@ -1,4 +1,4 @@ -import unittest, macros, strformat +import std/[unittest, macros, strformat] import moepkg/[editorstatus, gapbuffer, bufferstatus, unicodeext] include moepkg/configmode diff --git a/tests/tdebugmode.nim b/tests/tdebugmode.nim index fa0b37676..631992e10 100644 --- a/tests/tdebugmode.nim +++ b/tests/tdebugmode.nim @@ -1,4 +1,4 @@ -import unittest, strformat +import std/[unittest, strformat] import moepkg/[editorstatus, unicodeext, bufferstatus] include moepkg/debugmode diff --git a/tests/teditor.nim b/tests/teditor.nim index 95706b664..e8737a26a 100644 --- a/tests/teditor.nim +++ b/tests/teditor.nim @@ -1,4 +1,4 @@ -import unittest, macros +import std/[unittest, macros] import moepkg/register include moepkg/[editor, editorstatus, ui, platform] @@ -555,6 +555,9 @@ suite "Editor: keyEnter: Enable autoindent in Nim": block: const keyword = "object" newLineTestInNimCase4(keyword) + block: + const keyword = "=" + newLineTestInNimCase4(keyword) # Generate test code # Enable autoindent @@ -597,6 +600,9 @@ suite "Editor: keyEnter: Enable autoindent in Nim": block: const keyword = "object" newLineTestInNimCase5(keyword) + block: + const keyword = "=" + newLineTestInNimCase5(keyword) # Generate test code # Enable autoindent @@ -919,6 +925,22 @@ suite "Editor: keyEnter and autoindent in Python": check currentBufStatus.buffer[0] == ru"if true or" check currentBufStatus.buffer[1] == ru" " + test "Insert a new line in Nim (Fix #1450)": + var status = initEditorStatus() + status.addNewBuffer + + status.bufStatus[0].buffer = initGapBuffer(@[ru"block:", ru" const a = 0"]) + currentBufStatus.language = SourceLanguage.langNim + status.bufStatus[0].mode = Mode.insert + currentMainWindowNode.currentLine = 1 + currentMainWindowNode.currentColumn = currentBufStatus.buffer[1].len + + const isAutoIndent = true + for i in 0 ..< 2: + currentBufStatus.keyEnter(currentMainWindowNode, + isAutoIndent, + status.settings.tabStop) + suite "Delete character before cursor": test "Delete one character": var status = initEditorStatus() @@ -1643,7 +1665,7 @@ suite "Editor: Open the blank line below": # Generate test code # Enable autoindent # open the blank line below in Nim - # When the current line ends with "or", "and", ':', "object". + # When the current line ends with "or", "and", ':', "object", '='. macro openLineBelowTestCase3(keyword: string): untyped = quote do: # Generate test title @@ -1685,6 +1707,9 @@ suite "Editor: Open the blank line below": block: const keyword = "object" openLineBelowTestCase3(keyword) + block: + const keyword = "=" + openLineBelowTestCase3(keyword) # Generate test code # Enable/Disable autoindent @@ -1926,7 +1951,7 @@ suite "Editor: Open the blank line abave": # Generate test code # Enable autoindent # open the blank line above in Nim - # When the current line ends with "or", "and", ':', "object". + # When the current line ends with "or", "and", ':', "object", "=". macro openLineAboveTestCase4(keyword: string): untyped = quote do: # Generate test title @@ -1974,6 +1999,9 @@ suite "Editor: Open the blank line abave": block: const keyword = "object" openLineAboveTestCase4(keyword) + block: + const keyword = "=" + openLineAboveTestCase4(keyword) # Generate test code # Enable autoindent @@ -2021,5 +2049,3 @@ suite "Editor: Open the blank line abave": block: const keyword = ":" openLineAboveTestCase5(keyword) - - diff --git a/tests/teditorstatus.nim b/tests/teditorstatus.nim index c963e0f3e..6947a9a17 100644 --- a/tests/teditorstatus.nim +++ b/tests/teditorstatus.nim @@ -1,7 +1,8 @@ -import unittest -import moepkg/[ui, highlight, editorview, gapbuffer, unicodeext, insertmode, - movement, editor, window, color, bufferstatus, - settings] +import std/unittest +import moepkg/[ui, highlight, editorview, gapbuffer, unicodeext, + editor, window, color, bufferstatus, settings] + +from moepkg/movement import keyDown, keyRight include moepkg/editorstatus @@ -11,12 +12,26 @@ template initHighlight() = status.settings.highlightSettings.reservedWords, currentBufStatus.language) -test "Add new buffer": - var status = initEditorStatus() - status.addNewBuffer - status.addNewBuffer - status.resize(100, 100) - check(status.bufStatus.len == 2) +suite "Add new buffer": + test "Add 2 uffers": + var status = initEditorStatus() + + status.addNewBuffer + + status.resize(100, 100) + status.update + + status.addNewBuffer + + check status.bufStatus.len == 2 + + test "Add new buffer (Dir)": + var status = initEditorStatus() + + status.addNewBuffer("./") + + status.resize(100, 100) + status.update test "Add new buffer and update editor view when disabling current line highlighting (Fix #1189)": var status = initEditorStatus() @@ -209,8 +224,9 @@ test "Auto delete paren 1": status.addNewBuffer currentBufStatus.buffer = initGapBuffer(@[ru"()"]) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"") @@ -223,8 +239,9 @@ test "Auto delete paren 1": currentBufStatus.buffer = initGapBuffer(@[ru"()"]) currentBufStatus.keyRight(currentMainWindowNode) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"") @@ -237,8 +254,9 @@ test "Auto delete paren 2": status.addNewBuffer currentBufStatus.buffer = initGapBuffer(@[ru"(())"]) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"()") @@ -251,8 +269,9 @@ test "Auto delete paren 2": currentBufStatus.buffer = initGapBuffer(@[ru"(())"]) currentBufStatus.keyRight(currentMainWindowNode) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"()") @@ -267,8 +286,9 @@ test "Auto delete paren 2": for i in 0 ..< 2: currentBufStatus.keyRight(currentMainWindowNode) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"()") @@ -282,8 +302,9 @@ test "Auto delete paren 2": for i in 0 ..< 3: currentBufStatus.keyRight(currentMainWindowNode) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"()") @@ -297,8 +318,9 @@ test "Auto delete paren 3": currentBufStatus.buffer = initGapBuffer(@[ru"(()"]) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"()") @@ -311,8 +333,9 @@ test "Auto delete paren 3": currentBufStatus.buffer = initGapBuffer(@[ru"(()"]) currentBufStatus.keyRight(currentMainWindowNode) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"(") @@ -326,8 +349,9 @@ test "Auto delete paren 3": for i in 0 ..< 2: currentBufStatus.keyRight(currentMainWindowNode) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"(") @@ -339,8 +363,9 @@ test "Auto delete paren 3": status.addNewBuffer currentBufStatus.buffer = initGapBuffer(@[ru"())"]) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru")") @@ -353,8 +378,9 @@ test "Auto delete paren 3": currentBufStatus.buffer = initGapBuffer(@[ru"())"]) currentBufStatus.keyRight(currentMainWindowNode) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru")") @@ -369,8 +395,9 @@ test "Auto delete paren 3": for i in 0 ..< 3: currentBufStatus.keyRight(currentMainWindowNode) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"()") @@ -383,8 +410,9 @@ test "Auto delete paren 4": status.addNewBuffer currentBufStatus.buffer = initGapBuffer(@[ru"(", ru")"]) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"") @@ -398,8 +426,9 @@ test "Auto delete paren 4": currentBufStatus.buffer = initGapBuffer(@[ru"(", ru")"]) currentBufStatus.keyDown(currentMainWindowNode) - currentBufStatus.deleteCurrentCharacter( - currentMainWindowNode, + currentBufStatus.deleteCharacter( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn, status.settings.autoDeleteParen) check(currentBufStatus.buffer[0] == ru"") diff --git a/tests/teditorview.nim b/tests/teditorview.nim index 724b8c3be..1790e72aa 100644 --- a/tests/teditorview.nim +++ b/tests/teditorview.nim @@ -1,4 +1,4 @@ -import unittest, deques +import std/[unittest, deques] import moepkg/[editorview, gapbuffer, unicodeext] test "initEditorView 1": diff --git a/tests/texmode.nim b/tests/texmode.nim index 2f4f1d25a..97332eea2 100644 --- a/tests/texmode.nim +++ b/tests/texmode.nim @@ -1,4 +1,4 @@ -import unittest, os +import std/[unittest, os] import moepkg/[ui, editorstatus, gapbuffer, exmode, unicodeext, bufferstatus, settings] diff --git a/tests/tfilermode.nim b/tests/tfilermode.nim index 884832cda..4a9f840fa 100644 --- a/tests/tfilermode.nim +++ b/tests/tfilermode.nim @@ -1,4 +1,4 @@ -import unittest, os, algorithm, strutils +import std/[unittest, os, algorithm, strutils] import moepkg/[filermode, editorstatus, highlight, color, bufferstatus, unicodeext, gapbuffer] diff --git a/tests/tgapbuffer.nim b/tests/tgapbuffer.nim index 0377a6cf6..dc634b3b7 100644 --- a/tests/tgapbuffer.nim +++ b/tests/tgapbuffer.nim @@ -1,4 +1,4 @@ -import unittest +import std/unittest import moepkg/gapbuffer test "empty": diff --git a/tests/tgeneralautocomplete.nim b/tests/tgeneralautocomplete.nim index fa0e41f77..8c651133c 100644 --- a/tests/tgeneralautocomplete.nim +++ b/tests/tgeneralautocomplete.nim @@ -1,4 +1,4 @@ -import unittest, sugar, sequtils +import std/[unittest, sugar, sequtils] import moepkg/unicodeext include moepkg/generalautocomplete diff --git a/tests/thelp.nim b/tests/thelp.nim index 603e55813..d5e0412bf 100644 --- a/tests/thelp.nim +++ b/tests/thelp.nim @@ -1,4 +1,4 @@ -import unittest, strutils +import std/[unittest, strutils] import moepkg/[editorstatus, gapbuffer, unicodeext, movement, window, bufferstatus] diff --git a/tests/thighlight.nim b/tests/thighlight.nim index b052d7866..7b0251b58 100644 --- a/tests/thighlight.nim +++ b/tests/thighlight.nim @@ -1,4 +1,4 @@ -import unittest, strutils +import std/[unittest, strutils] import moepkg/[highlight, color] import moepkg/syntax/highlite diff --git a/tests/thighlight_private.nim b/tests/thighlight_private.nim index e74b643f8..a27154df6 100644 --- a/tests/thighlight_private.nim +++ b/tests/thighlight_private.nim @@ -1,4 +1,4 @@ -import unittest, sequtils +import std/[unittest, sequtils] include moepkg/highlight diff --git a/tests/thistorymanager.nim b/tests/thistorymanager.nim index ce42169eb..1b19aefde 100644 --- a/tests/thistorymanager.nim +++ b/tests/thistorymanager.nim @@ -1,4 +1,4 @@ -import unittest, os +import std/[unittest, os] import moepkg/[unicodeext, settings] include moepkg/[historymanager] diff --git a/tests/tinsertmode.nim b/tests/tinsertmode.nim index 423fcb602..cf512e3fb 100644 --- a/tests/tinsertmode.nim +++ b/tests/tinsertmode.nim @@ -1,4 +1,4 @@ -import unittest, options, sequtils, sugar, random +import std/[unittest, options, sequtils, sugar, random] import moepkg/[editorstatus, gapbuffer, unicodeext, highlight, settings] include moepkg/[insertmode, suggestionwindow] diff --git a/tests/tlogviewer.nim b/tests/tlogviewer.nim index 573c19377..92d844115 100644 --- a/tests/tlogviewer.nim +++ b/tests/tlogviewer.nim @@ -1,13 +1,41 @@ -import unittest -import moepkg/[editorstatus, logviewer, bufferstatus] +import std/unittest +import moepkg/[editorstatus, logviewer, bufferstatus, unicodeext] -test "Exit log viewer": - var status = initEditorStatus() - status.addNewBuffer("Log viewer", Mode.logViewer) +suite "Log viewer": + test "Open the log viewer (Fix #1455)": + var status = initEditorStatus() + status.addNewBuffer - status.resize(100, 100) - status.update + status.resize(100, 100) + status.update - status.exitLogViewer(100, 100) + status.messageLog = @[ru "test"] - status.resize(100, 100) + status.verticalSplitWindow + status.resize(100, 100) + status.moveNextWindow + + status.addNewBuffer + status.changeCurrentBuffer(status.bufStatus.high) + status.changeMode(bufferstatus.Mode.logviewer) + + # In the log viewer + currentBufStatus.path = ru"Log viewer" + + status.resize(100, 100) + status.update + + let currentBufferIndex = status.bufferIndexInCurrentWindow + + status.update + + test "Exit viewer": + var status = initEditorStatus() + status.addNewBuffer("Log viewer", Mode.logViewer) + + status.resize(100, 100) + status.update + + status.exitLogViewer(100, 100) + + status.resize(100, 100) diff --git a/tests/tmovement.nim b/tests/tmovement.nim index 9df41b61b..ee8786473 100644 --- a/tests/tmovement.nim +++ b/tests/tmovement.nim @@ -1,4 +1,4 @@ -import unittest +import std/unittest import moepkg/[editorstatus, gapbuffer, unicodeext, highlight, movement, bufferstatus] diff --git a/tests/tnormalmode.nim b/tests/tnormalmode.nim index 8f2773086..d58222933 100644 --- a/tests/tnormalmode.nim +++ b/tests/tnormalmode.nim @@ -1,7 +1,7 @@ -import unittest +import std/unittest import ncurses import moepkg/[editorstatus, gapbuffer, unicodeext, editor, bufferstatus, - register] + register, settings] include moepkg/normalmode @@ -1591,3 +1591,364 @@ suite "Normal mode: Open the blank line above and enter insert mode": check currentBufStatus.buffer[1] == ru "abc" check currentMainWindowNode.currentLine == 0 + +suite "Normal mode: Run command when Readonly mode": + test "Enter insert mode (\"i\") command": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru "abc"]) + + const command = ru "i" + status.normalCommand(command, 100, 100) + + check currentBufStatus.mode == Mode.normal + + test "Enter insert mode (\"I\") command": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru "abc"]) + + const command = ru "I" + status.normalCommand(command, 100, 100) + + check currentBufStatus.mode == Mode.normal + + test "Open the blank line and enter insert mode (\"o\") command": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru "abc"]) + + const command = ru "o" + status.normalCommand(command, 100, 100) + + check currentBufStatus.mode == Mode.normal + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Open the blank line and enter insert mode (\"O\") command": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru "abc"]) + + const command = ru "O" + status.normalCommand(command, 100, 100) + + check currentBufStatus.mode == Mode.normal + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Enter replace mode (\"R\") command": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru "abc"]) + + const command = ru "R" + status.normalCommand(command, 100, 100) + + check currentBufStatus.mode == Mode.normal + + test "Delete lines (\"dd\") command": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru "abc"]) + + const command = ru "dd" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Paste lines (\"p\") command": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru "abc"]) + + var settings = initEditorSettings() + settings.clipboard.enable = false + + status.registers.addRegister(ru "def", settings) + + const command = ru "p" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + +suite "Normal mode: Move to the next any character on the current line": + test "Move to the next 'c' (\"fc\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abc def ghi" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "fc" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check currentMainWindowNode.currentLine == 0 + check currentMainWindowNode.currentColumn == 2 + + test "Move to the next 'i' (\"fi\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abc def ghi" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "fi" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check currentMainWindowNode.currentLine == 0 + check currentMainWindowNode.currentColumn == buffer.high + + test "Do nothing (\"fz\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abc def ghi" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "fz" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check currentMainWindowNode.currentLine == 0 + check currentMainWindowNode.currentColumn == 0 + +suite "Normal mode: Move to forward word in the current line": + test "Move to the before 'e' (\"Fe\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abc def ghi" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + currentMainWindowNode.currentColumn = buffer.high + + const command = ru "Fe" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check currentMainWindowNode.currentLine == 0 + check currentMainWindowNode.currentColumn == 5 + + test "Do nothing (\"Fz\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abc def ghi" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + currentMainWindowNode.currentColumn = buffer.high + + const command = ru "Fz" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check currentMainWindowNode.currentLine == 0 + check currentMainWindowNode.currentColumn == buffer.high + +suite "Normal mode: Move to the left of the next any character": + test "Move to the character next the next 'e' (\"tf\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abc def ghi" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "tf" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check currentMainWindowNode.currentLine == 0 + check currentMainWindowNode.currentColumn == 5 + + test "Do nothing (\"tz\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abc def ghi" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "tz" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check currentMainWindowNode.currentLine == 0 + check currentMainWindowNode.currentColumn == 0 + +suite "Normal mode: Move to the right of the back character": + test "Move to the character before the next 'f' (\"Te\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abc def ghi" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + currentMainWindowNode.currentColumn = buffer.high + + const command = ru "Te" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check currentMainWindowNode.currentLine == 0 + check currentMainWindowNode.currentColumn == 6 + + test "Do nothing (\"Tz\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abc def ghi" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "Tz" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check currentMainWindowNode.currentLine == 0 + check currentMainWindowNode.currentColumn == 0 + +suite "Normal mode: Yank characters to any character": + test "Case 1: Yank characters before 'd' (\"ytd\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abcd" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "ytd" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check not status.registers.noNameRegister.isLine + check status.registers.noNameRegister.buffer[0] == ru "abc" + + test "Case 2: Yank characters before 'd' (\"ytd\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "ab c d" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "ytd" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check not status.registers.noNameRegister.isLine + check status.registers.noNameRegister.buffer[0] == ru "ab c " + + test "Case 1: Do nothing (\"ytd\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abc" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "ytd" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check status.registers.noNameRegister.buffer.len == 0 + + test "Case 2: Do nothing (\"ytd\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abcd efg" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + currentMainWindowNode.currentColumn = 3 + + const command = ru "ytd" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check status.registers.noNameRegister.buffer.len == 0 + + test "Case 3: Do nothing (\"ytd\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abcd efg" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + currentMainWindowNode.currentColumn = buffer.high + + const command = ru "ytd" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check status.registers.noNameRegister.buffer.len == 0 + +suite "Normal mode: Delete characters to any characters and Enter insert mode": + test "Case 1: Delete characters to 'd' and enter insert mode (\"cfd\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abcd" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "cfd" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "" + + check not status.registers.noNameRegister.isLine + check status.registers.noNameRegister.buffer[0] == ru "abcd" + + check currentBufStatus.mode == Mode.insert + + test "Case 1: Do nothing (\"cfz\" command)": + var status = initEditorStatus() + status.addNewBuffer + + const buffer = ru "abcd" + currentBufStatus.buffer = initGapBuffer(@[buffer]) + + const command = ru "cfz" + status.normalCommand(command, 100, 100) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == buffer + + check status.registers.noNameRegister.buffer.len == 0 + + check currentBufStatus.mode == Mode.normal diff --git a/tests/tquittest1.nim b/tests/tquittest1.nim index 4f200e5bc..b1f4caa3d 100644 --- a/tests/tquittest1.nim +++ b/tests/tquittest1.nim @@ -1,4 +1,4 @@ -import unittest +import std/unittest import moepkg/[editorstatus, unicodeext, exmode] test "Quit command": diff --git a/tests/tquittest2.nim b/tests/tquittest2.nim index 7c2ea2c0f..4ad5671ac 100644 --- a/tests/tquittest2.nim +++ b/tests/tquittest2.nim @@ -1,4 +1,4 @@ -import unittest +import std/unittest import moepkg/[editorstatus, unicodeext, exmode, ui] test "Open buffer manager": diff --git a/tests/tquittest3.nim b/tests/tquittest3.nim index e7e93e824..022c21973 100644 --- a/tests/tquittest3.nim +++ b/tests/tquittest3.nim @@ -1,4 +1,4 @@ -import unittest +import std/unittest import moepkg/[editorstatus, unicodeext, exmode] test "Force quit command": diff --git a/tests/tquittest4.nim b/tests/tquittest4.nim index 1092f1536..ce6cc2d57 100644 --- a/tests/tquittest4.nim +++ b/tests/tquittest4.nim @@ -1,4 +1,4 @@ -import unittest +import std/unittest import moepkg/[editorstatus, unicodeext, exmode] test "All buffer quit command": diff --git a/tests/tquittest5.nim b/tests/tquittest5.nim index d7e800962..3e5681507 100644 --- a/tests/tquittest5.nim +++ b/tests/tquittest5.nim @@ -1,4 +1,4 @@ -import unittest +import std/unittest import moepkg/[editorstatus, unicodeext, exmode] test "All buffer force quit command": diff --git a/tests/tregister.nim b/tests/tregister.nim index 2c2f6aec5..d89fbea8e 100644 --- a/tests/tregister.nim +++ b/tests/tregister.nim @@ -1,4 +1,4 @@ -import unittest, options +import std/[unittest, options] import moepkg/[unicodeext, settings] include moepkg/[register] diff --git a/tests/treplacemode.nim b/tests/treplacemode.nim index b48de20a4..7194b04e2 100644 --- a/tests/treplacemode.nim +++ b/tests/treplacemode.nim @@ -1,4 +1,4 @@ -import unittest +import std/unittest import moepkg/[editorstatus, bufferstatus, unicodeext] include moepkg/replacemode diff --git a/tests/tsearch.nim b/tests/tsearch.nim index 328ace641..87b5f9592 100644 --- a/tests/tsearch.nim +++ b/tests/tsearch.nim @@ -1,5 +1,5 @@ -import unittest -import moepkg/[editorstatus] +import std/unittest +import moepkg/editorstatus include moepkg/search diff --git a/tests/tsettings.nim b/tests/tsettings.nim index 9e1d3250e..fc4e26d39 100644 --- a/tests/tsettings.nim +++ b/tests/tsettings.nim @@ -1,4 +1,4 @@ -import unittest, options, strutils +import std/[unittest, options, strutils] import moepkg/[color, ui, highlight, unicodeext] include moepkg/settings @@ -544,7 +544,7 @@ suite "Validate toml config": let toml = parsetoml.parseString(tomlStr) let result = toml.validateTomlConfig - check result == none(string) + check result == none(InvalidItem) test "Validate vscode theme": const tomlThemeConfig =""" @@ -554,7 +554,7 @@ suite "Validate toml config": let toml = parsetoml.parseString(tomlThemeConfig) let result = toml.validateTomlConfig - check result == none(string) + check result == none(InvalidItem) test "Except to fail": const tomlThemeConfig =""" @@ -572,7 +572,7 @@ suite "Configuration example": filename = "./example/moerc.toml" toml = parsetoml.parseFile(filename) - check toml.validateTomlConfig == none(string) + check toml.validateTomlConfig == none(InvalidItem) suite "Generate toml config": test "Generate current config": @@ -583,4 +583,45 @@ suite "Generate toml config": toml = parsetoml.parseString(str) result = toml.validateTomlConfig - check result == none(string) + check result == none(InvalidItem) + +suite "Error message": + test "Single line": + const TOML_STR = """ + [test] + test = "test" + """ + + let + toml = parseString(TOML_STR) + result = toml.validateTomlConfig + errorMessage = result.get.toValidateErrorMessage + + check errorMessage == """(name: test, val: test = "test")""" + + test "Single line 2": + const TOML_STR = """ + [Standard] + test = "test" + """ + + let + toml = parseString(TOML_STR) + result = toml.validateTomlConfig + errorMessage = result.get.toValidateErrorMessage + + check errorMessage == """(name: test, val: test)""" + + test "Multiple lines": + const TOML_STR = """ + [test] + test1 = "test1" + test2 = "test2" + """ + + let + toml = parseString(TOML_STR) + result = toml.validateTomlConfig + errorMessage = result.get.toValidateErrorMessage + + check errorMessage == """(name: test, val: test1 = "test1" test2 = "test2")""" diff --git a/tests/tunicodeext.nim b/tests/tunicodeext.nim index ae1a7bb8b..5aac82ec5 100644 --- a/tests/tunicodeext.nim +++ b/tests/tunicodeext.nim @@ -1,4 +1,4 @@ -import strutils, unittest, encodings, sequtils, sugar +import std/[strutils, unittest, encodings, sequtils, sugar] import moepkg/unicodeext test "width 1": diff --git a/tests/tvisualmode.nim b/tests/tvisualmode.nim index e8cb82286..ad5ff323c 100644 --- a/tests/tvisualmode.nim +++ b/tests/tvisualmode.nim @@ -1,4 +1,4 @@ -import unittest, osproc +import std/[unittest, osproc] import moepkg/[editorstatus, gapbuffer, unicodeext, highlight, movement, bufferstatus, register] include moepkg/[visualmode, platform] @@ -467,7 +467,8 @@ suite "Visual block mode: Delete buffer (Disable clipboard)": currentBufStatus.deleteBufferBlock(status.registers, currentMainWindowNode, area, - status.settings) + status.settings, + status.commandLine) check(currentBufStatus.buffer[0] == ru"bc") check(currentBufStatus.buffer[1] == ru"ef") @@ -695,7 +696,8 @@ suite "Visual block mode: Delete buffer": currentBufStatus.deleteBufferBlock(status.registers, currentMainWindowNode, area, - status.settings) + status.settings, + status.commandLine) if CURRENT_PLATFORM == Platforms.linux: let (output, exitCode) = execCmdEx("xclip -o") @@ -733,7 +735,8 @@ suite "Visual block mode: Delete buffer": currentBufStatus.deleteBufferBlock(status.registers, currentMainWindowNode, area, - status.settings) + status.settings, + status.commandLine) test "Fix #885": var status = initEditorStatus() @@ -765,10 +768,12 @@ suite "Visual block mode: Delete buffer": let area = currentBufStatus.selectArea status.settings.clipboard.enable = true - currentBufStatus.deleteBufferBlock(status.registers, + currentBufStatus.deleteBufferBlock( + status.registers, currentMainWindowNode, area, - status.settings) + status.settings, + status.commandLine) check currentBufStatus.buffer[0] == ru"c" check currentBufStatus.buffer[1] == ru"" @@ -805,7 +810,7 @@ suite "Visual mode: Join lines": let area = currentBufStatus.selectArea status.update - currentBufStatus.joinLines(currentMainWindowNode, area) + currentBufStatus.joinLines(currentMainWindowNode, area, status.commandLine) check(currentBufStatus.buffer.len == 1) check(currentBufStatus.buffer[0] == ru"abcdefghi") @@ -841,7 +846,7 @@ suite "Visual block mode: Join lines": let area = currentBufStatus.selectArea status.update - currentBufStatus.joinLines(currentMainWindowNode, area) + currentBufStatus.joinLines(currentMainWindowNode, area, status.commandLine) check(currentBufStatus.buffer.len == 1) check(currentBufStatus.buffer[0] == ru"abcdefghi") @@ -1401,7 +1406,434 @@ suite "Visual block mode: Insert buffer": insertBuffer, area, status.settings.tabStop, - status.settings.autoCloseParen) + status.settings.autoCloseParen, + status.commandLine) check currentBufStatus.buffer[0] == ru" abc" check currentBufStatus.buffer[1] == ru" def" + +suite "Visual mode: Run command when Readonly mode": + test "Delete buffer (\"x\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visual) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'x') + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Add the indent (\">\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visual) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'>') + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Add the indent (\"<\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visual) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'<') + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Join lines (\"J\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc", ru "def"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visual) + + currentMainWindowNode.currentColumn = currentBufStatus.buffer[0].high + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + currentBufStatus.selectArea.endLine = 1 + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'J') + + check currentBufStatus.buffer.len == 2 + check currentBufStatus.buffer[0] == ru "abc" + check currentBufStatus.buffer[1] == ru "def" + + test "To lower case (\"u\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visual) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'u') + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "To upper case (\"U\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visual) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'U') + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Replace characters (\"r\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visual) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + currentBufStatus.replaceCharacter(currentBufStatus.selectArea, ru 'z', status.commandLine) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Enter insert mode (\"I\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visual) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'I') + + check currentBufStatus.mode == Mode.visual + +suite "Visual block mode: Run command when Readonly mode": + test "Delete buffer (\"x\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visualblock) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'x') + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Enter insert mode (\"I\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visualblock) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'I') + + check currentBufStatus.mode == Mode.visualblock + + test "Add the indent (\">\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visualBlock) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'>') + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Add the indent (\"<\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visualBlock) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'<') + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Join lines (\"J\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc", ru "def"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visualBlock) + + currentMainWindowNode.currentColumn = currentBufStatus.buffer[0].high + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + currentBufStatus.selectArea.endLine = 1 + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'J') + + check currentBufStatus.buffer.len == 2 + check currentBufStatus.buffer[0] == ru "abc" + check currentBufStatus.buffer[1] == ru "def" + + test "To lower case (\"u\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visualBlock) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'u') + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "To upper case (\"U\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visualBlock) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + status.visualCommand(currentBufStatus.selectArea, ru'U') + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc" + + test "Replace characters (\"r\" command)": + var status = initEditorStatus() + status.isReadonly = true + status.addNewBuffer + currentBufStatus.buffer = initGapBuffer(@[ru"abc"]) + + currentMainWindowNode.highlight = initHighlight( + $currentBufStatus.buffer, + status.settings.highlightSettings.reservedWords, + currentBufStatus.language) + + status.resize(100, 100) + + status.changeMode(Mode.visualBlock) + + currentBufStatus.selectArea = initSelectArea( + currentMainWindowNode.currentLine, + currentMainWindowNode.currentColumn) + + status.update + + currentBufStatus.replaceCharacter(currentBufStatus.selectArea, ru 'z', status.commandLine) + + check currentBufStatus.buffer.len == 1 + check currentBufStatus.buffer[0] == ru "abc"