summary refs log tree commit diff stats
path: root/compiler/main.nim
Commit message (Collapse)AuthorAgeFilesLines
* remove nir; succeeded by nif (#23809)ringabout2024-07-091-35/+4
| | | ref https://github.com/nim-lang/nif
* fixes addr/hiddenAddr in strictdefs (#23477)ringabout2024-04-101-1/+1
|
* compute checksum of nim files early in the pipelines (#23268)ringabout2024-01-311-1/+1
| | | | related https://github.com/nim-lang/Nim/issues/21717 configs will be resolved later
* Show error when trying to run in folder that doesn't exist instead of ↵Jake Leahy2024-01-231-1/+2
| | | | | | | assertion (#23242) Closes #23240 Fixes regression caused by #23017
* lexer cleanups (#23037)Jacek Sieka2023-12-061-1/+0
| | | | * remove some dead code and leftovers from past features * fix yaml printing of uint64 literals
* IC: progress and refactorings (#22961)Andreas Rumpf2023-11-201-3/+9
|
* so close... (#22885)Andreas Rumpf2023-10-311-1/+3
|
* NIR: store sizes, alignments and offsets in the type graph; beginning… ↵Andreas Rumpf2023-10-161-0/+26
| | | | | (#22822) …s of a patent-pending new VM
* NIR: Nim intermediate representation (#22777)Andreas Rumpf2023-10-111-4/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Theoretical Benefits / Plans: - Typed assembler-like language. - Allows for a CPS transformation. - Can replace the existing C backend by a new C backend. - Can replace the VM. - Can do more effective "not nil" checking and static array bounds checking. - Can be used instead of the DFA. - Easily translatable to LLVM. - Reasonably easy to produce native code from. - Tiny memory consumption. No pointers, no cry. **In very early stages of development.** Todo: - [x] Map Nim types to IR types. - [ ] Map Nim AST to IR instructions: - [x] Map bitsets to bitops. - [ ] Implement string cases. - [ ] Implement range and index checks. - [x] Implement `default(T)` builtin. - [x] Implement multi string concat. - [ ] Write some analysis passes. - [ ] Write a backend. - [x] Integrate into the compilation pipeline.
* replaces `doAssert false` with `raiseAssert` for unreachable branches, which ↵ringabout2023-08-101-3/+3
| | | | | works better with strictdefs (#22436) replaces `doAssert false` with `raiseAssert`, which works better with strictdefs
* use strictdefs for compiler (#22365)ringabout2023-08-061-2/+2
| | | | | | | | | | | | | | | * wip; use strictdefs for compiler * checkpoint * complete the chores * more fixes * first phase cleanup * Update compiler/bitsets.nim * cleanup
* Remove Deprecated Nimfix (#22062)Juan Carlos2023-06-101-1/+1
| | | | * Remove Deprecated Nimfix * Trailing whitespace cleanups
* fixes #19863; move sha1, md5 to nimble packages for 2.0 (#21702)ringabout2023-05-021-1/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | * move sha1, md5 to nimble packages * boot the compiler * fixes tests * build the documentation * fixes docs * lol, I forgot koch.nim * add `nimHasChecksums` define * clone checksums but maybe copying is better * bump nimble hash * use ChecksumsStableCommit * fixes tests * deprecate them * fixes paths * fixes koch
* a better message if graphviz's dot/nodejs is not found in PATH (#21488)ghost2023-03-081-0/+7
| | | | | | | | | * finish issue #21474: a better message if dot is not found locally when using gendepend * fix a typo in compiler * trim empty path reported in `findNodeJs` * compiler/main.nim: switch raise to simply quit
* replaces implicit passes array registed at runtime with explicit function ↵ringabout2023-03-031-38/+29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | calls; simplify compilation pipeline (#21444) * abolish using passes in the compiler; simplify compilation pipeline * duplicate code * Really cool to have the same signature... * haul * unify other backends * refactor process * introduce PipelinePhase * refactor compiler * fixes passes * fixes nimsuggest * add a sentinel * enable docs checkj * activate doc testing * clean up * complete cleanups
* Add `--genCDeps` for better integration with CMake (#20950)Jaremy Creechley2022-11-291-0/+22
| | | | | | | | | | | | | | | | | * add gencdeps option * add case statement * Update compiler/main.nim * Update compiler/main.nim * Apply suggestions from code review Fixes Co-authored-by: Andreas Rumpf <rumpf_a@web.de> Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
* 'lock levels' are deprecated, now a noop (#20539)ringabout2022-10-111-1/+0
| | | | | * 'lock levels' are deprecated, now a noop * fixes tests
* fixes #9462; jsondoc --index can generate a theindex.json (#20205)ringabout2022-09-061-1/+5
|
* Improve Markdown code blocks & start moving docs to Markdown style (#19954)Andrey Makarov2022-07-151-6/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - add additional parameters parsing (other implementations will just ignore them). E.g. if in RST we have: .. code:: nim :test: "nim c $1" ... then in Markdown that will be: ```nim test="nim c $1" ... ``` - implement Markdown interpretation of additional indentation which is less than 4 spaces (>=4 spaces is a code block but it's not implemented yet). RST interpretes it as quoted block, for Markdown it's just normal paragraphs. - add separate `md2html` and `md2tex` commands. This is to separate Markdown behavior in cases when it diverges w.r.t. RST significantly — most conspicously like in the case of additional indentation above, and also currently the contradicting inline rule of Markdown is also turned on only in `md2html` and `md2tex`. **Rationale:** mixing Markdown and RST arbitrarily is a way to nowhere, we need to provide a way to fix the particular behavior. Note that still all commands have **both** Markdown and RST features **enabled**. In this PR `*.nim` files can be processed only in Markdown mode, while `md2html` is for `*.md` files and `rst2html` for `*.rst` files. - rename `*.rst` files to `.*md` as our current default behavior is already Markdown-ish - convert code blocks in `docgen.rst` to Markdown style as an example. Other code blocks will be converted in the follow-up PRs - fix indentation inside Markdown code blocks — additional indentation is preserved there - allow more than 3 backticks open/close blocks (tildas \~ are still not allowed to avoid conflict with RST adornment headings) see also https://github.com/nim-lang/RFCs/issues/355 - better error messages - (other) fix a bug that admonitions cannot be used in sandbox mode; fix annoying warning on line 2711
* fix #17286 nim check -b:js works (#19704)flywind2022-04-091-0/+2
| | | | | * fix #17286 nim check -b:js works * fix
* fix nim check nimscript [backport: 1.6] (#19444)flywind2022-03-231-1/+5
| | | | fix #19440; fix #3858
* move assertions out of system (#19599)flywind2022-03-231-0/+4
|
* rst: add missing line/column info for some warnings (#18383)Andrey Makarov2021-07-201-7/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * rst: add missing line/column info for some warnings * add workaround * use TLineInfo/FileIndex for storing file names * fix blank lines in include file (rm harmful strip) * don't use ref TLineInfo * return `hasToc` as output parameter for uniformity * Update compiler/docgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update compiler/docgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * address review - stylistic things * Update compiler/docgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * unify RST warnings/errors names * doAssert + minor name change * fix a bug caught by doAssert * apply strbasics.strip to final HTML/Latex * rm redundant filename * fix test after rebase * delete `order` from rnFootnoteRef, also display errors/warnings properly when footnote references are from different files * Update compiler/lineinfos.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update lib/packages/docutils/rstast.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update lib/packages/docutils/rstast.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update lib/packages/docutils/rstast.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * revert because of error: Error: cannot prove that it's safe to initialize 'info' with the runtime value for the discriminator 'kind' * Update lib/packages/docutils/rstgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * apply suggestion * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * add Table for string->file name mapping * do not import compiler/lineinfos * fix ambiguous calls Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> Co-authored-by: narimiran <narimiran@disroot.org>
* docgen: move to shared RST state (fix #16990) (#18256)Andrey Makarov2021-06-201-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * docgen: move to shared RST state (fix #16990) * Update lib/packages/docutils/rst.nim Co-authored-by: Andreas Rumpf <rumpf_a@web.de> * Update lib/packages/docutils/rst.nim Co-authored-by: Andreas Rumpf <rumpf_a@web.de> * Update lib/packages/docutils/rst.nim Co-authored-by: Andreas Rumpf <rumpf_a@web.de> * Update compiler/docgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update compiler/docgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update compiler/docgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * rename `cmdDoc2` to `cmdDoc` * fix (P)RstSharedState convention * new style of initialization * misc suggestions * 1 more rename * fix a regression Co-authored-by: Andreas Rumpf <rumpf_a@web.de> Co-authored-by: Timothee Cour <timothee.cour2@gmail.com>
* merge BuildMode into SuccessX, remove code duplication w drnim, add useful ↵Timothee Cour2021-06-141-34/+6
| | | | | | | | | | | info to successx, add gc to compilesettings (#18252) * merge BuildMode into SuccessX, add more info * refactor duplicated with drnim * fixup * address comment
* config system: special case -d:release and -d:danger [backport:1.4] (#18051)Andreas Rumpf2021-05-201-1/+2
|
* `doc2tex`: generate docs to Latex (#17997)Andrey Makarov2021-05-141-9/+16
| | | | | * `doc2tex`: generate docs to Latex * address some comments
* fix #17853 (ascii message separator broke json nim dump) (#17887)Timothee Cour2021-04-291-1/+2
|
* Implement https://forum.nim-lang.org/t/7848#50018 (#17874)c-blake2021-04-291-4/+4
| | | | | | | | | | | | | | | | | | | | * Implement https://forum.nim-lang.org/t/7848#50018 with just the same `SuccessX` hint category, build mode on a separate, final line, and no change to how the mode is spelled for -d:release/-d:danger. * Change to add a new BuildMode hint category and keep testament in sync as per comment. * Add "--hint:buildmode:off" to `defaultHintsOff`. * Remove as requested. * As requested for tests clean up. * Address code review. * Address code review. * Mirror db456423116a9b19666f551f4d38aded3964c2e2
* `--usenimcache` (implied by `nim r main`) now caches some compile options to ↵Timothee Cour2021-04-251-17/+34
| | | | | | | | | | | avoid recompiling when project was previously compiled with such options. (#17829) * `--usenimcache` (implied by `nim r main`) now caches some compile options to avoid recompiling when project was previously compiled with such options. * works * add test * changelog * use std/with
* `--filenames:abs|canonical|legacyRelProj` for filenames in compiler msgs ↵Timothee Cour2021-04-211-3/+5
| | | | | | | (replaces `--listfullpaths:on|off`) (#17746) * use canonicalImport for filename_magicSauce * --filenames:abs|canonical|magic * rename: magic => legacyRelProj
* unit separator (#17730)Andreas Rumpf2021-04-201-2/+2
| | | | | * use the ASCII Unit Separator so that error messages can be handled precisely by the tooling * updated testament
* -d:nimDebug: calls doAssert false instead of quit (#17739)Timothee Cour2021-04-171-3/+3
|
* IC: first steps towards 'nim check --def --ic:on' (#17714)Andreas Rumpf2021-04-141-3/+12
| | | | | | | | | * IC: first steps towards 'nim check --def --ic:on' * IC navigator: deduplicate output lines * IC navigator: progress * IC navigator: use a different nimcache entry * IC navigator: special logic for templates/macros * IC navigator: proper error messages * IC navigator: prepare for testing code; document only what currently works somewhat
* IC: integrity checking (#17695)Andreas Rumpf2021-04-111-1/+3
| | | | | | * IC: integrity checking: the plumbing code * progress * progress + bugfix (yes, the code already found a bug) * implemented integrity checking
* fix #17190 `nimscript` now accepts arbitrary file extensions for `nim e ↵flywind2021-04-011-2/+0
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | main.customext` (#17596) * fix #17190 * cah * merge * Update tnimscriptwithnimext.nim * Update tnimscriptwithmacro.nims * Apply suggestions from code review * Delete tnimscriptwithnimext.nim * Update tests/tools/tnimscriptwithmacro.nims * fix * fix * add a test * Apply suggestions from code review Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Apply suggestions from code review * Update changelog.md Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
* IC: green tests (#17311)Andreas Rumpf2021-03-191-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * IC: renamed to_packed_ast module to ic module * IC: don't store the --forceBuild flag, makes it easier to test * IC: enable hello world test * Codegen: refactorings for IC; changed the name mangling algorithm * fixed the HCR regressions * life is too short for HCR * tconvexhull is now allowed to use deepCopy * IC exposed a stdlib bug, required a refactoring * codegen: code cleanups * IC: even if a module is outdated, its dependencies might come from disk * IC: progress * IC: better name mangling, module IDs are not stable * IC: another refactoring helping with --ic:on --gc:arc * disable arraymancer on Windows for the time being * disable arraymancer altogether * IC: make basic test work with 'nim cpp' * IC: progress on --ic:on --gc:arc * wip; name mangling for type info
* stricter checks for RST headlines (#17089)Andrey Makarov2021-02-201-0/+8
|
* IC: next steps (#16729)Andreas Rumpf2021-01-231-9/+17
| | | | | | | | | | | * IC: dead code elimination pass * preparations for a different codegen strategy * added documentation to the newly written code * IC: backend code * IC: backend adjustments * optimized the compiler a bit * IC: yet another massive refactoring * fixes regressions * cleanups
* IC: next steps (#16632)Andreas Rumpf2021-01-121-3/+9
| | | | | | | | | | | | | * removed dead code * beginnings of a rodfile reader * IC: record global VM state changes and pragma state changes * IC: replay pragmas and VM state changes * implemented rod load file simuation for easier, extensive testing * critical bugfix * IC: stress test logic; should also help with recursive module dependencies; WIP * IC: loading from .rod files begins to work reliably * removed ugly hacks * yet another silly mistake
* compiler: minor refactoring (#16633)Andreas Rumpf2021-01-081-12/+11
|
* make --gc:arc --exceptions:quirky work again [backport:1.4] (#16583)Andreas Rumpf2021-01-041-2/+0
| | | | | * make --gc:arc --exceptions:quirky work again [backport:1.4] * fixes #16404 [backport:1.4]
* big steps torwards an efficient, simple IC implementation (#16543)Andreas Rumpf2021-01-021-3/+2
| | | | | | | | | | | | | | | | | | | * reworked ID handling * the packed AST now has its own ID mechanism * basic serialization code works * extract rodfiles to its own module * rodfiles: store and compare configs * rodfiles: store dependencies * store config at the end * precise dependency tracking * dependency tracking for rodfiles * completed loading of PSym, PType, etc * removed dead code * bugfix: do not realloc seqs when taking addr into an element * make IC opt-in for now * makes tcompilerapi green again * final cleanups Co-authored-by: Andy Davidoff <github@andy.disruptek.com>
* fix `hintProcessing` dots interference with `static:echo` and `hintCC`; add ↵Timothee Cour2020-12-301-1/+1
| | | | | | | | | | tests for `nim secret`, add tests for hintProcessing, misc other bug fixes (#16495) * fix dots interfering with static:echo * add tests * fix hintProcessing dots for hintCC * improve trunner tests * fix bug: readLineFromStdin now writes prompt to stdout, consistent with linenoise and rdstdin * disable a failing test for windows
* fix `nim secret` dots interfering with prompt (#16491)Timothee Cour2020-12-281-1/+2
| | | | | * fix nim secret dots * cleanups
* refactorings to prepare the compiler for IC (#15935)Andreas Rumpf2020-12-171-3/+3
| | | | | | | | | | | | | | * added ic specific Nim code; WIP * make the symbol import mechanism lazy; WIP * ensure that modules can be imported multiple times * ambiguity checking * handle converters and TR macros properly * make 'enum' test category green again * special logic for semi-pure enums * makes nimsuggest tests green again * fixes nimdata * makes nimpy green again * makes more important packages work
* cmdline: improve command processing (#16056)Timothee Cour2020-11-261-66/+33
|
* Correct all eggs (#15906)Miran2020-11-101-1/+1
| | | | * "eg" is a misspelled "egg", "e.g." is "exempli gratia" * Also, "ie" is "i.e.".
* new: `nim -e:cmd` to run a command directly; also fixes #15731 (#15687)Timothee Cour2020-11-091-2/+3
| | | | | | | | | | | | | | | * new: `nim -i cmd` * rename -i to -e (for eval); consistent with majority of other programing languages * `nim e -e:cmd` now works; bugfix: `echo cmd | nim e -` now works * honor --betterRun * address comments * --eval alias for -e (replaces undocumented --eval which was a noop) * --eval now defaults to e (nimscript) instead of r * address comment: remove -e, only keep --eval * address comment * fixup * Update compiler/nimconf.nim Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
* Use modern enums in compiler (#15775)cooldome2020-11-021-2/+2
|
licateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(), addr(hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0: when defined(consoleapp): raiseOSError(osLastError()) proc getCursorPos(h: Handle): tuple [x,y: int] = var c: CONSOLESCREENBUFFERINFO if getConsoleScreenBufferInfo(h, addr(c)) == 0: raiseOSError(osLastError()) return (int(c.dwCursorPosition.X), int(c.dwCursorPosition.Y)) proc setCursorPos(h: Handle, x, y: int) = var c: COORD c.X = int16(x) c.Y = int16(y) if setConsoleCursorPosition(h, c) == 0: raiseOSError(osLastError()) proc getAttributes(h: Handle): int16 = var c: CONSOLESCREENBUFFERINFO # workaround Windows bugs: try several times if getConsoleScreenBufferInfo(h, addr(c)) != 0: return c.wAttributes return 0x70'i16 # ERROR: return white background, black text var oldStdoutAttr = getAttributes(hStdout) oldStderrAttr = getAttributes(hStderr) template conHandle(f: File): Handle = if f == stderr: hStderr else: hStdout else: import termios, posix, os, parseutils proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) = var mode: Termios discard fd.tcgetattr(addr mode) mode.c_iflag = mode.c_iflag and not Cflag(BRKINT or ICRNL or INPCK or ISTRIP or IXON) mode.c_oflag = mode.c_oflag and not Cflag(OPOST) mode.c_cflag = (mode.c_cflag and not Cflag(CSIZE or PARENB)) or CS8 mode.c_lflag = mode.c_lflag and not Cflag(ECHO or ICANON or IEXTEN or ISIG) mode.c_cc[VMIN] = 1.cuchar mode.c_cc[VTIME] = 0.cuchar discard fd.tcsetattr(time, addr mode) proc terminalWidthIoctl*(fds: openArray[int]): int = ## Returns terminal width from first fd that supports the ioctl. var win: IOctl_WinSize for fd in fds: if ioctl(cint(fd), TIOCGWINSZ, addr win) != -1: return int(win.ws_col) return 0 proc terminalHeightIoctl*(fds: openArray[int]): int = ## Returns terminal height from first fd that supports the ioctl. var win: IOctl_WinSize for fd in fds: if ioctl(cint(fd), TIOCGWINSZ, addr win) != -1: return int(win.ws_row) return 0 var L_ctermid{.importc, header: "<stdio.h>".}: cint proc terminalWidth*(): int = ## Returns some reasonable terminal width from either standard file ## descriptors, controlling terminal, environment variables or tradition. var w = terminalWidthIoctl([0, 1, 2]) #Try standard file descriptors if w > 0: return w var cterm = newString(L_ctermid) #Try controlling tty var fd = open(ctermid(cstring(cterm)), O_RDONLY) if fd != -1: w = terminalWidthIoctl([ int(fd) ]) discard close(fd) if w > 0: return w var s = getEnv("COLUMNS") #Try standard env var if len(s) > 0 and parseInt(string(s), w) > 0 and w > 0: return w return 80 #Finally default to venerable value proc terminalHeight*(): int = ## Returns some reasonable terminal height from either standard file ## descriptors, controlling terminal, environment variables or tradition. ## Zero is returned if the height could not be determined. var h = terminalHeightIoctl([0, 1, 2]) # Try standard file descriptors if h > 0: return h var cterm = newString(L_ctermid) # Try controlling tty var fd = open(ctermid(cstring(cterm)), O_RDONLY) if fd != -1: h = terminalHeightIoctl([ int(fd) ]) discard close(fd) if h > 0: return h var s = getEnv("LINES") # Try standard env var if len(s) > 0 and parseInt(string(s), h) > 0 and h > 0: return h return 0 # Could not determine height proc terminalSize*(): tuple[w, h: int] = ## Returns the terminal width and height as a tuple. Internally calls ## `terminalWidth` and `terminalHeight`, so the same assumptions apply. result = (terminalWidth(), terminalHeight()) when defined(windows): proc setCursorVisibility(f: File, visible: bool) = var ccsi: CONSOLE_CURSOR_INFO let h = conHandle(f) if getConsoleCursorInfo(h, addr(ccsi)) == 0: raiseOSError(osLastError()) ccsi.bVisible = if visible: 1 else: 0 if setConsoleCursorInfo(h, addr(ccsi)) == 0: raiseOSError(osLastError()) proc hideCursor*(f: File) = ## Hides the cursor. when defined(windows): setCursorVisibility(f, false) else: f.write("\e[?25l") proc showCursor*(f: File) = ## Shows the cursor. when defined(windows): setCursorVisibility(f, true) else: f.write("\e[?25h") proc setCursorPos*(f: File, x, y: int) = ## Sets the terminal's cursor to the (x,y) position. ## (0,0) is the upper left of the screen. when defined(windows): let h = conHandle(f) setCursorPos(h, x, y) else: f.write(fmt"{stylePrefix}{y};{x}f") proc setCursorXPos*(f: File, x: int) = ## Sets the terminal's cursor to the x position. ## The y position is not changed. when defined(windows): let h = conHandle(f) var scrbuf: CONSOLESCREENBUFFERINFO if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0: raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition origin.X = int16(x) if setConsoleCursorPosition(h, origin) == 0: raiseOSError(osLastError()) else: f.write(fmt"{stylePrefix}{x}G") when defined(windows): proc setCursorYPos*(f: File, y: int) = ## Sets the terminal's cursor to the y position. ## The x position is not changed. ## **Warning**: This is not supported on UNIX! when defined(windows): let h = conHandle(f) var scrbuf: CONSOLESCREENBUFFERINFO if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0: raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition origin.Y = int16(y) if setConsoleCursorPosition(h, origin) == 0: raiseOSError(osLastError()) else: discard proc cursorUp*(f: File, count=1) = ## Moves the cursor up by `count` rows. when defined(windows): let h = conHandle(f) var p = getCursorPos(h) dec(p.y, count) setCursorPos(h, p.x, p.y) else: f.write("\e[" & $count & 'A') proc cursorDown*(f: File, count=1) = ## Moves the cursor down by `count` rows. when defined(windows): let h = conHandle(f) var p = getCursorPos(h) inc(p.y, count) setCursorPos(h, p.x, p.y) else: f.write(fmt"{stylePrefix}{count}B") proc cursorForward*(f: File, count=1) = ## Moves the cursor forward by `count` columns. when defined(windows): let h = conHandle(f) var p = getCursorPos(h) inc(p.x, count) setCursorPos(h, p.x, p.y) else: f.write(fmt"{stylePrefix}{count}C") proc cursorBackward*(f: File, count=1) = ## Moves the cursor backward by `count` columns. when defined(windows): let h = conHandle(f) var p = getCursorPos(h) dec(p.x, count) setCursorPos(h, p.x, p.y) else: f.write(fmt"{stylePrefix}{count}D") when true: discard else: proc eraseLineEnd*(f: File) = ## Erases from the current cursor position to the end of the current line. when defined(windows): discard else: f.write("\e[K") proc eraseLineStart*(f: File) = ## Erases from the current cursor position to the start of the current line. when defined(windows): discard else: f.write("\e[1K") proc eraseDown*(f: File) = ## Erases the screen from the current line down to the bottom of the screen. when defined(windows): discard else: f.write("\e[J") proc eraseUp*(f: File) = ## Erases the screen from the current line up to the top of the screen. when defined(windows): discard else: f.write("\e[1J") proc eraseLine*(f: File) = ## Erases the entire current line. when defined(windows): let h = conHandle(f) var scrbuf: CONSOLESCREENBUFFERINFO var numwrote: DWORD if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0: raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition origin.X = 0'i16 if setConsoleCursorPosition(h, origin) == 0: raiseOSError(osLastError()) var wt: DWORD = scrbuf.dwSize.X - origin.X if fillConsoleOutputCharacter(h, ' ', wt, origin, addr(numwrote)) == 0: raiseOSError(osLastError()) if fillConsoleOutputAttribute(h, scrbuf.wAttributes, wt, scrbuf.dwCursorPosition, addr(numwrote)) == 0: raiseOSError(osLastError()) else: f.write("\e[2K") setCursorXPos(f, 0) proc eraseScreen*(f: File) = ## Erases the screen with the background colour and moves the cursor to home. when defined(windows): let h = conHandle(f) var scrbuf: CONSOLESCREENBUFFERINFO var numwrote: DWORD var origin: COORD # is inititalized to 0, 0 if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0: raiseOSError(osLastError()) let numChars = int32(scrbuf.dwSize.X)*int32(scrbuf.dwSize.Y) if fillConsoleOutputCharacter(h, ' ', numChars, origin, addr(numwrote)) == 0: raiseOSError(osLastError()) if fillConsoleOutputAttribute(h, scrbuf.wAttributes, numChars, origin, addr(numwrote)) == 0: raiseOSError(osLastError()) setCursorXPos(f, 0) else: f.write("\e[2J") proc resetAttributes*(f: File) = ## Resets all attributes. when defined(windows): if f == stderr: discard setConsoleTextAttribute(hStderr, oldStderrAttr) else: discard setConsoleTextAttribute(hStdout, oldStdoutAttr) else: f.write("\e[0m") type Style* = enum ## different styles for text output styleBright = 1, ## bright text styleDim, ## dim text styleUnknown, ## unknown styleUnderscore = 4, ## underscored text styleBlink, ## blinking/bold text styleReverse = 7, ## unknown styleHidden ## hidden text {.deprecated: [TStyle: Style].} when not defined(windows): var gFG {.threadvar.}: int gBG {.threadvar.}: int proc getStyleStr(style: int): string = when hasThreadSupport: result = fmt"{stylePrefix}{style}m" else: if styleCache.hasKey(style): result = styleCache[style] else: result = fmt"{stylePrefix}{style}m" styleCache[style] = result proc setStyle*(f: File, style: set[Style]) = ## Sets the terminal style. when defined(windows): let h = conHandle(f) var old = getAttributes(h) and (FOREGROUND_RGB or BACKGROUND_RGB) var a = 0'i16 if styleBright in style: a = a or int16(FOREGROUND_INTENSITY) if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY) if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE discard setConsoleTextAttribute(h, old or a) else: for s in items(style): f.write(getStyleStr(ord(s))) proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = ## Writes the text `txt` in a given `style` to stdout. when defined(windows): var old = getAttributes(hStdout) stdout.setStyle(style) stdout.write(txt) discard setConsoleTextAttribute(hStdout, old) else: stdout.setStyle(style) stdout.write(txt) stdout.resetAttributes() if gFG != 0: stdout.write(getStyleStr(gFG)) if gBG != 0: stdout.write(getStyleStr(gBG)) type ForegroundColor* = enum ## terminal's foreground colors fgBlack = 30, ## black fgRed, ## red fgGreen, ## green fgYellow, ## yellow fgBlue, ## blue fgMagenta, ## magenta fgCyan, ## cyan fgWhite ## white BackgroundColor* = enum ## terminal's background colors bgBlack = 40, ## black bgRed, ## red bgGreen, ## green bgYellow, ## yellow bgBlue, ## blue bgMagenta, ## magenta bgCyan, ## cyan bgWhite ## white {.deprecated: [TForegroundColor: ForegroundColor, TBackgroundColor: BackgroundColor].} proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) = ## Sets the terminal's foreground color. when defined(windows): let h = conHandle(f) var old = getAttributes(h) and not FOREGROUND_RGB old = if bright: old or FOREGROUND_INTENSITY else: old and not(FOREGROUND_INTENSITY) const lookup: array[ForegroundColor, int] = [ 0, (FOREGROUND_RED), (FOREGROUND_GREEN), (FOREGROUND_RED or FOREGROUND_GREEN), (FOREGROUND_BLUE), (FOREGROUND_RED or FOREGROUND_BLUE), (FOREGROUND_BLUE or FOREGROUND_GREEN), (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)] discard setConsoleTextAttribute(h, toU16(old or lookup[fg])) else: gFG = ord(fg) if bright: inc(gFG, 60) f.write(getStyleStr(gFG)) proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) = ## Sets the terminal's background color. when defined(windows): let h = conHandle(f) var old = getAttributes(h) and not BACKGROUND_RGB old = if bright: old or BACKGROUND_INTENSITY else: old and not(BACKGROUND_INTENSITY) const lookup: array[BackgroundColor, int] = [ 0, (BACKGROUND_RED), (BACKGROUND_GREEN), (BACKGROUND_RED or BACKGROUND_GREEN), (BACKGROUND_BLUE), (BACKGROUND_RED or BACKGROUND_BLUE), (BACKGROUND_BLUE or BACKGROUND_GREEN), (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)] discard setConsoleTextAttribute(h, toU16(old or lookup[bg])) else: gBG = ord(bg) if bright: inc(gBG, 60) f.write(getStyleStr(gBG)) proc getFGColorStr(color: Color): string = when hasThreadSupport: let rgb = extractRGB(color) result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m" else: if colorsFGCache.hasKey(color): result = colorsFGCache[color] else: let rgb = extractRGB(color) result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m" colorsFGCache[color] = result proc getBGColorStr(color: Color): string = when hasThreadSupport: let rgb = extractRGB(color) result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m" else: if colorsBGCache.hasKey(color): result = colorsBGCache[color] else: let rgb = extractRGB(color) result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m" colorsFGCache[color] = result proc setForegroundColor*(f: File, color: Color) = ## Sets the terminal's foreground true color. if trueColorIsEnabled: f.write(getFGColorStr(color)) proc setBackgroundColor*(f: File, color: Color) = ## Sets the terminal's background true color. if trueColorIsEnabled: f.write(getBGColorStr(color)) proc setTrueColor(f: File, color: Color) = if fgSetColor: setForegroundColor(f, color) else: setBackgroundColor(f, color) proc isatty*(f: File): bool = ## Returns true if `f` is associated with a terminal device. when defined(posix): proc isatty(fildes: FileHandle): cint {. importc: "isatty", header: "<unistd.h>".} else: proc isatty(fildes: FileHandle): cint {. importc: "_isatty", header: "<io.h>".} result = isatty(getFileHandle(f)) != 0'i32 type TerminalCmd* = enum ## commands that can be expressed as arguments resetStyle, ## reset attributes fgColor, ## set foreground's true color bgColor ## set background's true color template styledEchoProcessArg(f: File, s: string) = write f, s template styledEchoProcessArg(f: File, style: Style) = setStyle(f, {style}) template styledEchoProcessArg(f: File, style: set[Style]) = setStyle f, style template styledEchoProcessArg(f: File, color: ForegroundColor) = setForegroundColor f, color template styledEchoProcessArg(f: File, color: BackgroundColor) = setBackgroundColor f, color template styledEchoProcessArg(f: File, color: Color) = setTrueColor f, color template styledEchoProcessArg(f: File, cmd: TerminalCmd) = when cmd == resetStyle: resetAttributes(f) when cmd == fgColor: fgSetColor = true when cmd == bgColor: fgSetColor = false macro styledWriteLine*(f: File, m: varargs[typed]): untyped = ## Similar to ``writeLine``, but treating terminal style arguments specially. ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``, ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to ## ``f``, but instead corresponding terminal style proc is called. ## ## Example: ## ## .. code-block:: nim ## ## proc error(msg: string) = ## styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) ## let m = callsite() var reset = false result = newNimNode(nnkStmtList) for i in countup(2, m.len - 1): let item = m[i] case item.kind of nnkStrLit..nnkTripleStrLit: if i == m.len - 1: # optimize if string literal is last, just call writeLine result.add(newCall(bindSym"writeLine", f, item)) if reset: result.add(newCall(bindSym"resetAttributes", f)) return else: # if it is string literal just call write, do not enable reset result.add(newCall(bindSym"write", f, item)) else: result.add(newCall(bindSym"styledEchoProcessArg", f, item)) reset = true result.add(newCall(bindSym"write", f, newStrLitNode("\n"))) if reset: result.add(newCall(bindSym"resetAttributes", f)) macro styledEcho*(args: varargs[untyped]): untyped = ## Echoes styles arguments to stdout using ``styledWriteLine``. result = newCall(bindSym"styledWriteLine") result.add(bindSym"stdout") for arg in children(args): result.add(arg) proc getch*(): char = ## Read a single character from the terminal, blocking until it is entered. ## The character is not printed to the terminal. when defined(windows): let fd = getStdHandle(STD_INPUT_HANDLE) var keyEvent = KEY_EVENT_RECORD() var numRead: cint while true: # Block until character is entered doAssert(waitForSingleObject(fd, INFINITE) == WAIT_OBJECT_0) doAssert(readConsoleInput(fd, addr(keyEvent), 1, addr(numRead)) != 0) if numRead == 0 or keyEvent.eventType != 1 or keyEvent.bKeyDown == 0: continue return char(keyEvent.uChar) else: let fd = getFileHandle(stdin) var oldMode: Termios discard fd.tcgetattr(addr oldMode) fd.setRaw() result = stdin.readChar() discard fd.tcsetattr(TCSADRAIN, addr oldMode) when defined(windows): from unicode import toUTF8, Rune, runeLenAt proc readPasswordFromStdin*(prompt: string, password: var TaintedString): bool {.tags: [ReadIOEffect, WriteIOEffect].} = ## Reads a `password` from stdin without printing it. `password` must not ## be ``nil``! Returns ``false`` if the end of the file has been reached, ## ``true`` otherwise. password.string.setLen(0) stdout.write(prompt) while true: let c = getch() case c.char of '\r', chr(0xA): break of '\b': # ensure we delete the whole UTF-8 character: var i = 0 var x = 1 while i < password.len: x = runeLenAt(password.string, i) inc i, x password.string.setLen(max(password.len - x, 0)) of chr(0x0): # modifier key - ignore - for details see # https://github.com/nim-lang/Nim/issues/7764 continue else: password.string.add(toUTF8(c.Rune)) stdout.write "\n" else: import termios proc readPasswordFromStdin*(prompt: string, password: var TaintedString): bool {.tags: [ReadIOEffect, WriteIOEffect].} = password.string.setLen(0) let fd = stdin.getFileHandle() var cur, old: Termios discard fd.tcgetattr(cur.addr) old = cur cur.c_lflag = cur.c_lflag and not Cflag(ECHO) discard fd.tcsetattr(TCSADRAIN, cur.addr) stdout.write prompt result = stdin.readLine(password) stdout.write "\n" discard fd.tcsetattr(TCSADRAIN, old.addr) proc readPasswordFromStdin*(prompt = "password: "): TaintedString = ## Reads a password from stdin without printing it. result = TaintedString("") discard readPasswordFromStdin(prompt, result) # Wrappers assuming output to stdout: template hideCursor*() = hideCursor(stdout) template showCursor*() = showCursor(stdout) template setCursorPos*(x, y: int) = setCursorPos(stdout, x, y) template setCursorXPos*(x: int) = setCursorXPos(stdout, x) when defined(windows): template setCursorYPos*(x: int) = setCursorYPos(stdout, x) template cursorUp*(count=1) = cursorUp(stdout, count) template cursorDown*(count=1) = cursorDown(stdout, count) template cursorForward*(count=1) = cursorForward(stdout, count) template cursorBackward*(count=1) = cursorBackward(stdout, count) template eraseLine*() = eraseLine(stdout) template eraseScreen*() = eraseScreen(stdout) template setStyle*(style: set[Style]) = setStyle(stdout, style) template setForegroundColor*(fg: ForegroundColor, bright=false) = setForegroundColor(stdout, fg, bright) template setBackgroundColor*(bg: BackgroundColor, bright=false) = setBackgroundColor(stdout, bg, bright) template setForegroundColor*(color: Color) = setForegroundColor(stdout, color) template setBackgroundColor*(color: Color) = setBackgroundColor(stdout, color) proc resetAttributes*() {.noconv.} = ## Resets all attributes on stdout. ## It is advisable to register this as a quit proc with ## ``system.addQuitProc(resetAttributes)``. resetAttributes(stdout) when not defined(testing) and isMainModule: #system.addQuitProc(resetAttributes) write(stdout, "never mind") stdout.eraseLine() stdout.styledWriteLine("styled text ", {styleBright, styleBlink, styleUnderscore}) stdout.setBackGroundColor(bgCyan, true) stdout.setForeGroundColor(fgBlue) stdout.writeLine("ordinary text") stdout.resetAttributes() proc isTrueColorSupported*(): bool = ## Returns true if a terminal supports true color. return trueColorIsSupported when defined(windows): import os proc enableTrueColors*() = ## Enable true color. when defined(windows): var ver: OSVERSIONINFO ver.dwOSVersionInfoSize = sizeof(ver).DWORD let res = getVersionExW(addr ver) if res == 0: trueColorIsSupported = false else: trueColorIsSupported = ver.dwMajorVersion > 10 or (ver.dwMajorVersion == 10 and (ver.dwMinorVersion > 0 or (ver.dwMinorVersion == 0 and ver.dwBuildNumber >= 10586))) if not trueColorIsSupported: trueColorIsSupported = getEnv("ANSICON_DEF").len > 0 if trueColorIsSupported: if getEnv("ANSICON_DEF").len == 0: var mode: DWORD = 0 if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0: mode = mode or ENABLE_VIRTUAL_TERMINAL_PROCESSING if setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) != 0: trueColorIsEnabled = true else: trueColorIsEnabled = false else: trueColorIsEnabled = true else: trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"] trueColorIsEnabled = trueColorIsSupported proc disableTrueColors*() = ## Disable true color. when defined(windows): if trueColorIsSupported: if getEnv("ANSICON_DEF").len == 0: var mode: DWORD = 0 if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0: mode = mode and not ENABLE_VIRTUAL_TERMINAL_PROCESSING discard setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) trueColorIsEnabled = false else: trueColorIsEnabled = false