├── .gitattributes ├── README.md ├── UNLICENSE ├── paravim.nimble ├── screenshot.png ├── src ├── paravim.nim └── paravim │ ├── assets │ ├── ascii │ │ ├── cat.txt │ │ ├── christmas.txt │ │ ├── intro.txt │ │ ├── smile.txt │ │ └── usa.txt │ └── ttf │ │ ├── FiraCode-Regular.ttf │ │ └── Roboto-Regular.ttf │ ├── bin │ ├── libvim.dll │ ├── libvim.dylib │ └── libvim.so │ ├── buffers.nim │ ├── colors.nim │ ├── core.nim │ ├── libvim.nim │ ├── minimap.nim │ ├── scroll.nim │ ├── shaders │ ├── fragment.glsl │ └── vertex.glsl │ ├── structs.nim │ ├── terminal.nim │ ├── text.nim │ ├── tree_sitter.nim │ ├── tree_sitter │ ├── alloc.h │ ├── array.h │ ├── atomic.h │ ├── bits.h │ ├── clock.h │ ├── error_costs.h │ ├── get_changed_ranges.c │ ├── get_changed_ranges.h │ ├── language.c │ ├── language.h │ ├── length.h │ ├── lexer.c │ ├── lexer.h │ ├── lib.c │ ├── node.c │ ├── parser.c │ ├── parser_c.c │ ├── parser_javascript.c │ ├── parser_json.c │ ├── parser_nim.c │ ├── parser_python.c │ ├── point.h │ ├── query.c │ ├── reduce_action.h │ ├── reusable_node.h │ ├── scanner_javascript.c │ ├── scanner_nim.c │ ├── scanner_python.c │ ├── stack.c │ ├── stack.h │ ├── stb_ds.h │ ├── subtree.c │ ├── subtree.h │ ├── tree.c │ ├── tree.h │ ├── tree_cursor.c │ ├── tree_cursor.h │ ├── tree_sitter │ │ ├── api.h │ │ ├── api.nim │ │ └── parser.h │ ├── unicode.h │ └── unicode │ │ ├── ICU_SHA │ │ ├── LICENSE │ │ ├── README.md │ │ ├── ptypes.h │ │ ├── umachine.h │ │ ├── urename.h │ │ ├── utf.h │ │ ├── utf16.h │ │ └── utf8.h │ └── vim.nim └── tests ├── config.nims ├── hello.txt └── test1.nim /.gitattributes: -------------------------------------------------------------------------------- 1 | src/paravim/tree_sitter/** linguist-vendored 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | Paravim is an editor for Nim powered by Vim (via [libvim](https://github.com/paranim/libvim)) and rendered with OpenGL (via [paranim](https://github.com/paranim/paranim)). There are two ways to run it: 6 | 7 | 1. **As a standalone executable.** See the [pvim](https://github.com/paranim/pvim) repo for instructions on how to build it. 8 | 9 | 2. **Embedded inside a game.** You can try it by running the [parakeet](https://github.com/paranim/parakeet) example or any of the other [paranim examples](https://github.com/paranim/paranim_examples) via `nimble dev`, and then hitting `Esc` when the window appears. 10 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /paravim.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.18.5" 4 | author = "oakes" 5 | description = "A parasitic editor" 6 | license = "Public Domain" 7 | srcDir = "src" 8 | installExt = @[ 9 | "nim", "txt", "ttf", "glsl", "c", "h", 10 | when defined(windows): 11 | "dll" 12 | elif defined(macosx): 13 | "dylib" 14 | elif defined(linux): 15 | "so" 16 | ] 17 | 18 | 19 | 20 | # Dependencies 21 | 22 | requires "nim >= 1.2.6" 23 | requires "paranim >= 0.12.0" 24 | requires "pararules >= 1.4.0" 25 | requires "paratext >= 0.13.0" 26 | requires "illwill >= 0.2.0" 27 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paranim/paravim/b43b6a7493880739bce50ac53cd321ed17b6d935/screenshot.png -------------------------------------------------------------------------------- /src/paravim.nim: -------------------------------------------------------------------------------- 1 | import paranim/glfw 2 | from paranim/gl import nil 3 | from paravim/core import nil 4 | from paravim/vim import nil 5 | from paravim/libvim import nil 6 | from paravim/structs import nil 7 | from pararules import nil 8 | import tables 9 | import bitops 10 | from os import nil 11 | from strutils import nil 12 | 13 | const glfwToVimSpecials = 14 | {GLFWKey.BACKSPACE: "BS", 15 | GLFWKey.DELETE: "Del", 16 | GLFWKey.TAB: "Tab", 17 | GLFWKey.ENTER: "Enter", 18 | GLFWKey.ESCAPE: "Esc", 19 | GLFWKey.UP: "Up", 20 | GLFWKey.DOWN: "Down", 21 | GLFWKey.LEFT: "Left", 22 | GLFWKey.RIGHT: "Right", 23 | GLFWKey.HOME: "Home", 24 | GLFWKey.END: "End", 25 | GLFWKey.PAGE_UP: "PageUp", 26 | GLFWKey.PAGE_DOWN: "PageDown"}.toTable 27 | 28 | const glfwToVimChars = 29 | {GLFWKey.D: "D", 30 | GLFWKey.H: "H", 31 | GLFWKey.J: "J", 32 | GLFWKey.M: "M", 33 | GLFWKey.P: "P", 34 | GLFWKey.R: "R", 35 | GLFWKey.U: "U", 36 | GLFWKey.V: "V"}.toTable 37 | 38 | proc keyCallback*(window: GLFWWindow, key: int32, scancode: int32, 39 | action: int32, mods: int32) {.cdecl.} = 40 | if key < 0: 41 | return 42 | if action == GLFW_PRESS: 43 | let 44 | isControl = 0 != bitand(mods, GLFW_MOD_CONTROL) 45 | isShift = 0 != bitand(mods, GLFW_MOD_SHIFT) 46 | if isControl: 47 | if key == GLFWKey.Minus: 48 | core.fontDec() 49 | elif key == GLFWKey.Equal: 50 | core.fontInc() 51 | elif key == GLFWKey.V: 52 | if libvim.vimGetMode() == libvim.Insert.ord: 53 | vim.onBulkInput($ window.getClipboardString()) 54 | else: 55 | vim.onInput("") 56 | elif glfwToVimChars.hasKey(key): 57 | vim.onInput("") 58 | elif glfwToVimSpecials.hasKey(key): 59 | vim.onInput("<" & glfwToVimSpecials[key] & ">") 60 | elif action == GLFW_REPEAT: 61 | if glfwToVimSpecials.hasKey(key): 62 | vim.onInput("<" & glfwToVimSpecials[key] & ">") 63 | 64 | proc charCallback*(window: GLFWWindow, codepoint: uint32) {.cdecl.} = 65 | vim.onInput($ char(codepoint)) 66 | 67 | proc mouseButtonCallback*(window: GLFWWindow, button: int32, action: int32, mods: int32) {.cdecl.} = 68 | if action == GLFWPress: 69 | core.onMouseClick(button) 70 | 71 | var density: float 72 | 73 | proc cursorPosCallback*(window: GLFWWindow, xpos: float64, ypos: float64) {.cdecl.} = 74 | if density == 0: 75 | return # we can't move the mouse until we know the screen density 76 | core.onMouseMove(xpos * density, ypos * density) 77 | 78 | proc frameSizeCallback*(window: GLFWWindow, width: int32, height: int32) {.cdecl.} = 79 | core.onWindowResize(width, height) 80 | 81 | proc scrollCallback*(window: GLFWWindow, xoffset: float64, yoffset: float64) {.cdecl.} = 82 | if density == 0: 83 | return # we can't scroll until we know the screen density 84 | core.onScroll(xoffset / density, yoffset / density) 85 | 86 | var 87 | window: GLFWWindow # this is necessary because cdecl functions can't capture local variables 88 | totalTime: float 89 | 90 | proc init*(game: var gl.RootGame, w: GLFWWindow, params: seq[string]) = 91 | window = w 92 | 93 | proc onQuit(buf: pointer; isForced: cint) {.cdecl.} = 94 | window.setWindowShouldClose(true) 95 | 96 | proc onYank(yankInfo: ptr structs.yankInfo_T) {.cdecl.} = 97 | let 98 | lines = cstringArrayToSeq(cast[cstringArray](yankInfo.lines), yankInfo.numLines) 99 | content = strutils.join(lines, "\n") 100 | window.setClipboardString(content) 101 | 102 | vim.init(onQuit, onYank) 103 | 104 | var width, height: int32 105 | w.getFramebufferSize(width.addr, height.addr) 106 | w.frameSizeCallback(width, height) 107 | 108 | var windowWidth, windowHeight: int32 109 | w.getWindowSize(windowWidth.addr, windowHeight.addr) 110 | density = max(1, int(width / windowWidth)).float 111 | 112 | core.init(game, params.len == 0, density) 113 | core.windowTitleCallback = proc (title: string) = w.setWindowTitle(title) 114 | totalTime = glfwGetTime() 115 | 116 | for fname in params: 117 | discard libvim.vimBufferOpen(fname, 1, 0) 118 | 119 | proc init*(game: var gl.RootGame, w: GLFWWindow) = 120 | init(game, w, @[]) 121 | 122 | proc tick*(game: gl.RootGame, clear: bool): bool = 123 | let 124 | ts = glfwGetTime() 125 | deltaTime = ts - totalTime 126 | totalTime = ts 127 | core.insert(core.session, core.Global, core.DeltaTime, deltaTime) 128 | pararules.fireRules(core.session) 129 | result = core.tick(game, clear) 130 | 131 | proc tick*(game: gl.RootGame): bool = 132 | tick(game, false) 133 | 134 | proc isNormalMode*(): bool = 135 | let mode = libvim.vimGetMode() 136 | mode == libvim.Normal.ord or mode == libvim.NormalBusy.ord 137 | -------------------------------------------------------------------------------- /src/paravim/assets/ascii/cat.txt: -------------------------------------------------------------------------------- 1 | --%%% 2 | -- %%%%%%% 3 | %%%- -%%%%%- % 4 | -%%%%%------------ -----%%%-- -%%% 5 | %%%-- ---%%% %%- 6 | %% - ----%%% 7 | -%--%%%%-% %-%%-- %%%%- 8 | -%%%%%%%% %%%%%%---- %%%% 9 | %%% % -%% -%%% % %%%-- -% 10 | %%%%-%%%-- %%%---%%%%%- %% 11 | -%%%%-- -%%- -%%%%%%%%% %% 12 | -%%% ------- -%%%%%% % 13 | %%% % - -%%%- % 14 | %%%%-- - %%%%- % 15 | %%%%%%- --%%%%- % 16 | -%%%%%%%%%---- -% 17 | %%%%%%%%%%%---- %- 18 | %%%%%%%%------ %- 19 | %%%%%%%%-- % 20 | -%%%%%%%-- % 21 | 22 | Happy International Cat Day! 23 | -------------------------------------------------------------------------------- /src/paravim/assets/ascii/christmas.txt: -------------------------------------------------------------------------------- 1 | . . O . O 2 | o . . . . . O 3 | . o _.,--------. O O . o 4 | ,'" `. . O . 5 | O / \ o . 6 | f . Y 7 | . | \________________j . o . o 8 | | f Y . . 9 | O| l________________j . O 10 | | f|||||l ,-- --.|| O o ,----------------. 11 | . | |jjj|l` ,-. ,-.|l f Merry Christmas Y 12 | j_j,-``` f | f ||-. . | and a | 13 | f Y (| l_0, l_0,| j l Happy New Year! j 14 | l jY | `. |f O . `-._ ______.,-' 15 | `-' `-|, ) l' o ) / . 16 | o ||f ,- `--' _ Y o _,' ,' 17 | O jjj `. ,`| `-..___..,-'_.,' O o 18 | . '''\ `-----' j `""""'' 19 | . O |. ,' o . 20 | o . | `-._.,-' O . o o 21 | ____ | l ____ . O 22 | o ,'-+-->l -' Y<--+`. o 23 | /+--+,' \`-.__,'| `.+--\ . . o 24 | /-+--+`-, \.____,j ,'+--+\ . O 25 | f--+--+-< \ / >-+--+-Y o O . 26 | j--+--+--`. `. / ,'--+--+-l O o 27 | f+--+--+--+-`. Y /-+--+--+--Y . o 28 | |+--+--+--+--+\ |f--+--+--+--| . . 29 | |+--+-f+--+--+-\|j--+--+Y-+--| o o 30 | |+--+-|+--+--+--|+--+--+|-+--| o 31 | |+--+-|+--+--+--|+--+--+|-+--| 32 | 33 | -------------------------------------------------------------------------------- /src/paravim/assets/ascii/intro.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | Welcome to Paravim 4 | 5 | by Zach Oakes 6 | 7 | Vim was created by Bram Moolenaar 8 | 9 | To open a file, type: 10 | 11 | :e path/to/myfile.txt 12 | 13 | To quit, just throw your computer away 14 | 15 | Paravim isn't for quitters 16 | 17 | -------------------------------------------------------------------------------- /src/paravim/assets/ascii/smile.txt: -------------------------------------------------------------------------------- 1 | oooo$$$$$$$$$$$$oooo 2 | oo$$$$$$$$$$$$$$$$$$$$$$$$o 3 | oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o o$ $$ o$ 4 | o $ oo o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o $$ $$ $$o$ 5 | oo $ $ "$ o$$$$$$$$$ $$$$$$$$$$$$$ $$$$$$$$$o $$$o$$o$ 6 | "$$$$$$o$ o$$$$$$$$$ $$$$$$$$$$$ $$$$$$$$$$o $$$$$$$$ 7 | $$$$$$$ $$$$$$$$$$$ $$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$ 8 | $$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$ $$$$$$$$$$$$$$ """$$$ 9 | "$$$""""$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "$$$ 10 | $$$ o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "$$$o 11 | o$$" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$o 12 | $$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" "$$$$$$ooooo$$$$o 13 | o$$$oooo$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o$$$$$$$$$$$$$$$$$ 14 | $$$$$$$$"$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$"""""""" 15 | """" $$$$ "$$$$$$$$$$$$$$$$$$$$$$$$$$$$" o$$$ 16 | "$$$o """$$$$$$$$$$$$$$$$$$"$$" $$$ 17 | $$$o "$$""$$$$$$"""" o$$$ 18 | $$$$o o$$$" 19 | "$$$$o o$$$$$$o"$$$$o o$$$$ 20 | "$$$$$oo ""$$$$o$$$$$o o$$$$"" 21 | ""$$$$$oooo "$$$o$$$$$$$$$""" 22 | ""$$$$$$$oo $$$$$$$$$$ 23 | """"$$$$$$$$$$$ 24 | $$$$$$$$$$$$ 25 | $$$$$$$$$$" 26 | "$$$"""" 27 | -------------------------------------------------------------------------------- /src/paravim/assets/ascii/usa.txt: -------------------------------------------------------------------------------- 1 | |* * * * * * * * * * OOOOOOOOOOOOOOOOOOOOOOOOO| 2 | | * * * * * * * * * OOOOOOOOOOOOOOOOOOOOOOOOO| 3 | |* * * * * * * * * * OOOOOOOOOOOOOOOOOOOOOOOOO| 4 | | * * * * * * * * * OOOOOOOOOOOOOOOOOOOOOOOOO| 5 | |* * * * * * * * * * OOOOOOOOOOOOOOOOOOOOOOOOO| 6 | | * * * * * * * * * OOOOOOOOOOOOOOOOOOOOOOOOO| 7 | |* * * * * * * * * * OOOOOOOOOOOOOOOOOOOOOOOOO| 8 | |OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO| 9 | |OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO| 10 | |OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO| 11 | |OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO| 12 | |OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO| 13 | |OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO| 14 | 15 | Happy Fourth of July! 16 | -------------------------------------------------------------------------------- /src/paravim/assets/ttf/FiraCode-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paranim/paravim/b43b6a7493880739bce50ac53cd321ed17b6d935/src/paravim/assets/ttf/FiraCode-Regular.ttf -------------------------------------------------------------------------------- /src/paravim/assets/ttf/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paranim/paravim/b43b6a7493880739bce50ac53cd321ed17b6d935/src/paravim/assets/ttf/Roboto-Regular.ttf -------------------------------------------------------------------------------- /src/paravim/bin/libvim.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paranim/paravim/b43b6a7493880739bce50ac53cd321ed17b6d935/src/paravim/bin/libvim.dll -------------------------------------------------------------------------------- /src/paravim/bin/libvim.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paranim/paravim/b43b6a7493880739bce50ac53cd321ed17b6d935/src/paravim/bin/libvim.dylib -------------------------------------------------------------------------------- /src/paravim/bin/libvim.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paranim/paravim/b43b6a7493880739bce50ac53cd321ed17b6d935/src/paravim/bin/libvim.so -------------------------------------------------------------------------------- /src/paravim/buffers.nim: -------------------------------------------------------------------------------- 1 | from text import nil 2 | from math import nil 3 | 4 | type 5 | BufferUpdateTuple* = tuple[lines: seq[string], firstLine: int, lineCountChange: int] 6 | RangeTuple* = tuple[startLine: int, startColumn: int, endLine: int, endColumn: int] 7 | RectTuple* = tuple[left: float, top: float, width: float, height: float] 8 | 9 | proc updateLines*(lines: ref seq[string], bu: BufferUpdateTuple): ref seq[string] = 10 | new(result) 11 | if bu.lineCountChange == 0: 12 | result[] = lines[] 13 | for i in 0 ..< bu.lines.len: 14 | let lineNum = i + bu.firstLine 15 | if result[].len == lineNum: 16 | result[].add(bu.lines[i]) 17 | else: 18 | result[][lineNum] = bu.lines[i] 19 | else: 20 | let linesToRemove = 21 | if bu.lineCountChange < 0: 22 | (-1 * bu.lineCountChange) + bu.lines.len 23 | else: 24 | bu.lines.len - bu.lineCountChange 25 | result[].add(lines[][0 ..< bu.firstLine]) 26 | result[].add(bu.lines) 27 | result[].add(lines[][bu.firstLine + linesToRemove ..< lines[].len]) 28 | 29 | proc normalizeRange*(rangeData: RangeTuple, forceLeftToRight: bool): RangeTuple = 30 | var (startLine, startCol, endLine, endCol) = rangeData 31 | # make sure the range is always going the same direction 32 | if startLine > endLine or (startLine == endLine and startCol > endCol): 33 | startLine = rangeData.endLine 34 | startCol = rangeData.endColumn 35 | endLine = rangeData.startLine 36 | endCol = rangeData.startColumn 37 | # make sure the block is top left to bottom right 38 | if forceLeftToRight and startCol > endCol: 39 | swap(startCol, endCol) 40 | # include the last column in the selection 41 | endCol += 1 42 | (startLine, startCol, endLine, endCol) 43 | 44 | proc rangeToRects*(rangeData: RangeTuple, lines: ref seq[string]): seq[RectTuple] = 45 | var (startLine, startCol, endLine, endCol) = rangeData 46 | result = newSeq[RectTuple]() 47 | for lineNum in startLine .. endLine: 48 | let 49 | startCol = if lineNum == startLine: startCol else: 0 50 | endCol = if lineNum == endLine: endCol else: lines[][lineNum].len 51 | result.add(( 52 | left: startCol.float, 53 | top: lineNum.float, 54 | width: float(endCol - startCol), 55 | height: 1.float 56 | )) 57 | 58 | proc rangeToRect*(rangeData: RangeTuple): RectTuple = 59 | let (startLine, startCol, endLine, endCol) = rangeData 60 | ( 61 | left: startCol.float, 62 | top: startLine.float, 63 | width: float(endCol - startCol), 64 | height: float(endLine - startLine + 1) 65 | ) 66 | -------------------------------------------------------------------------------- /src/paravim/colors.nim: -------------------------------------------------------------------------------- 1 | import paranim/opengl 2 | from paranim/glm import nil 3 | import tables 4 | 5 | const 6 | bgColor* = glm.vec4(GLfloat(52/255), GLfloat(40/255), GLfloat(42/255), GLfloat(0.95)) 7 | textColor* = glm.vec4(1f, 1f, 1f, 1f) 8 | asciiColor* = glm.vec4(1f, 1f, 1f, 0.5f) 9 | minimapViewColor* = glm.vec4(1f, 1f, 1f, 0.25f) 10 | cursorColor* = glm.vec4(GLfloat(112/255), GLfloat(128/255), GLfloat(144/255), GLfloat(0.9)) 11 | completionColor* = glm.vec4(GLfloat(52/255), GLfloat(40/255), GLfloat(42/255), GLfloat(0.65)) 12 | selectColor* = glm.vec4(GLfloat(148/255), GLfloat(69/255), GLfloat(5/255), GLfloat(0.8)) 13 | searchColor* = glm.vec4(Glfloat(127/255), GLfloat(52/255), GLfloat(83/255), GLfloat(0.8)) 14 | 15 | yellowColor* = glm.vec4(Glfloat(255/255), GLfloat(193/255), GLfloat(94/255), GLfloat(1.0)) 16 | tanColor* = glm.vec4(Glfloat(209/255), GLfloat(153/255), GLfloat(101/255), GLfloat(1.0)) 17 | cyanColor* = glm.vec4(Glfloat(86/255), GLfloat(181/255), GLfloat(194/255), GLfloat(1.0)) 18 | grayColor* = glm.vec4(Glfloat(150/255), GLfloat(129/255), GLfloat(133/255), GLfloat(1.0)) 19 | orangeColor* = glm.vec4(Glfloat(220/255), GLfloat(103/255), GLfloat(44/255), GLfloat(1.0)) 20 | redColor* = glm.vec4(Glfloat(210/255), GLfloat(45/255), GLfloat(58/255), GLfloat(1.0)) 21 | greenColor* = glm.vec4(Glfloat(65/255), GLfloat(174/255), GLfloat(122/255), GLfloat(1.0)) 22 | syntaxColors* = {"string": tanColor, 23 | "string_literal": tanColor, 24 | "template_string": tanColor, 25 | "number": yellowColor, 26 | "number_literal": yellowColor, 27 | "integer": yellowColor, 28 | "float": yellowColor, 29 | "comment": grayColor, 30 | "op": grayColor, 31 | "public_id": cyanColor, 32 | }.toTable 33 | -------------------------------------------------------------------------------- /src/paravim/libvim.nim: -------------------------------------------------------------------------------- 1 | import structs 2 | import os 3 | 4 | proc getLib(): string = 5 | const extension = 6 | when defined(windows): 7 | "dll" 8 | elif defined(macosx): 9 | "dylib" 10 | elif defined(linux): 11 | "so" 12 | let pvimpath = getAppDir().joinPath("pvimpkg").joinPath("bin").joinPath("libvim." & extension) 13 | if existsFile(pvimpath): 14 | pvimpath 15 | else: 16 | currentSourcePath().parentDir().joinPath("bin").joinPath("libvim." & extension) 17 | 18 | when not defined(paravim_static): 19 | {.push dynlib: getLib().} 20 | 21 | type 22 | Mode* = enum 23 | Normal = 0x01, 24 | Visual = 0x02 25 | OpPending = 0x04, 26 | CommandLine = 0x08, 27 | Insert = 0x10, 28 | Replace = 0x50, 29 | NormalBusy = 0x100 + 0x01, 30 | 31 | ## libvim.c 32 | ## 33 | ## vimInit 34 | ## 35 | ## This must be called prior to using any other methods. 36 | ## 37 | ## This expects an `argc` and an `argv` parameters, 38 | ## for the command line arguments for this vim instance. 39 | ## 40 | 41 | proc vimInit*(argc: cint; argv: cstringArray){.cdecl, importc.} 42 | ## ** 43 | ## Buffer Methods 44 | ## * 45 | ## 46 | ## vimBufferOpen 47 | ## 48 | ## Open a buffer and set as current. 49 | ## 50 | 51 | proc vimBufferOpen*(ffname_arg: cstring; lnum: linenr_T; flags: cint): buf_T {.cdecl, importc.} 52 | ## 53 | ## vimBufferCheckIfChanged 54 | ## 55 | ## Check if the contents of a buffer have been changed on the filesystem, outside of libvim. 56 | ## Returns 1 if buffer was changed (and changes the buffer contents) 57 | ## Returns 2 if a message was displayed 58 | ## Returns 0 otherwise 59 | ## 60 | 61 | #proc vimBufferCheckIfChanged*(buf: buf_T): cint 62 | #proc vimBufferGetById*(id: cint): buf_T 63 | proc vimBufferGetCurrent*(): buf_T {.cdecl, importc.} 64 | #proc vimBufferSetCurrent*(buf: buf_T) 65 | proc vimBufferGetFilename*(buf: buf_T): ptr char_u {.cdecl, importc.} 66 | #proc vimBufferGetFiletype*(buf: buf_T): ptr char_u 67 | proc vimBufferGetId*(buf: buf_T): cint {.cdecl, importc.} 68 | #proc vimBufferGetLastChangedTick*(buf: buf_T): clong 69 | proc vimBufferGetLine*(buf: buf_T; lnum: linenr_T): ptr char_u {.cdecl, importc.} 70 | proc vimBufferGetLineCount*(buf: buf_T): csize {.cdecl, importc.} 71 | ## 72 | ## vimBufferSetLines 73 | ## 74 | ## Set a range of lines from the one-based start line to one-based end, inclusive. 75 | ## 76 | ## Examples: 77 | ## vimBufferSetLine(buf, 1, 1, ["abc"]); // Set line 1 to "abc"" 78 | ## vimBufferSetLine(buf, 1, 2, ["abc"]); // Remove line 2, set line 1 to "abc" 79 | ## vimBufferSetLine(buf, 0, 0, ["def"]); // Insert "def" before the contents of the buffer 80 | ## 81 | 82 | proc vimBufferSetLines*(buf: buf_T; start: linenr_T; `end`: linenr_T; 83 | lines: cstringArray; count: cint) {.cdecl, importc.} 84 | #proc vimBufferGetModified*(buf: buf_T): cint 85 | proc vimSetBufferUpdateCallback*(bufferUpdate: BufferUpdateCallback) {.cdecl, importc.} 86 | ## ** 87 | ## Autocommands 88 | ## * 89 | 90 | proc vimSetAutoCommandCallback*(autoCommandDispatch: AutoCommandCallback) {.cdecl, importc.} 91 | ## * 92 | ## Commandline 93 | ## * 94 | 95 | #proc vimCommandLineGetType*(): char_u 96 | proc vimCommandLineGetText*(): ptr char_u {.cdecl, importc.} 97 | proc vimCommandLineGetPosition*(): cint {.cdecl, importc.} 98 | proc vimCommandLineGetCompletions*(completions: ptr cstringArray; count: ptr cint) {.cdecl, importc.} 99 | ## ** 100 | ## Cursor Methods 101 | ## * 102 | 103 | proc vimCursorGetColumn*(): colnr_T {.cdecl, importc.} 104 | proc vimCursorGetLine*(): linenr_T {.cdecl, importc.} 105 | #proc vimCursorGetPosition*(): pos_T 106 | proc vimCursorSetPosition*(pos: pos_T) {.cdecl, importc.} 107 | ## ** 108 | ## vimCursorGetDesiredColumn 109 | ## 110 | ## Get the column that we'd like to be at - used to stay in the same 111 | ## column for up/down cursor motions. 112 | ## 113 | 114 | #proc vimCursorGetDesiredColumn*(): colnr_T 115 | ## ** 116 | ## File I/O 117 | ## * 118 | 119 | #proc vimSetFileWriteFailureCallback*(fileWriteFailureCallback: FileWriteFailureCallback) 120 | ## ** 121 | ## User Input 122 | ## * 123 | 124 | proc vimInput*(input: cstring) {.cdecl, importc: "vimKey".} 125 | proc vimInputUnicode*(input: cstring) {.cdecl, importc: "vimInput".} 126 | proc vimExecute*(cmd: cstring) {.cdecl, importc.} 127 | ## ** 128 | ## Messages 129 | ## * 130 | 131 | proc vimSetMessageCallback*(messageCallback: MessageCallback) {.cdecl, importc.} 132 | ## * 133 | ## Misc 134 | ## 135 | 136 | #proc vimSetGotoCallback*(gotoCallback: GotoCallback) 137 | #proc vimSetDirectoryChangedCallback*(callback: DirectoryChangedCallback) 138 | ## 139 | ## vimSetQuitCallback 140 | ## 141 | ## Called when a `:q`, `:qa`, `:q!` is called 142 | ## 143 | ## It is up to the libvim consumer how to handle the 'quit' call. 144 | ## There are two arguments passed: 145 | ## - `buffer`: the buffer quit was requested for 146 | ## - `force`: a boolean if the command was forced (ie, if `q!` was used) 147 | ## 148 | 149 | proc vimSetQuitCallback*(callback: QuitCallback) {.cdecl, importc.} 150 | ## 151 | ## vimSetUnhandledEscapeCallback 152 | ## 153 | ## Called when is pressed in normal mode, but there is no 154 | ## pending operator or action. 155 | ## 156 | ## This is intended for UI's to pick up and handle (for example, 157 | ## to clear messages or alerts). 158 | ## 159 | 160 | proc vimSetUnhandledEscapeCallback*(callback: VoidCallback) {.cdecl, importc.} 161 | ## ** 162 | ## Options 163 | ## 164 | 165 | proc vimOptionSetTabSize*(tabSize: cint) {.cdecl, importc.} 166 | proc vimOptionSetInsertSpaces*(insertSpaces: cint) {.cdecl, importc.} 167 | proc vimOptionGetInsertSpaces*(): cint {.cdecl, importc.} 168 | proc vimOptionGetTabSize*(): cint {.cdecl, importc.} 169 | ## ** 170 | ## Registers 171 | ## * 172 | 173 | #proc vimRegisterGet*(reg_name: cint; num_lines: ptr cint; lines: ptr ptr ptr char_u) 174 | ## ** 175 | ## Undo 176 | ## * 177 | 178 | #proc vimUndoSaveCursor*(): cint 179 | #proc vimUndoSaveRegion*(start_lnum: linenr_T; end_lnum: linenr_T): cint 180 | ## ** 181 | ## Visual Mode 182 | ## * 183 | 184 | proc vimVisualGetType*(): cint {.cdecl, importc.} 185 | proc vimVisualIsActive*(): cint {.cdecl, importc.} 186 | #proc vimSelectIsActive*(): cint 187 | ## 188 | ## vimVisualGetRange 189 | ## 190 | ## If in visual mode or select mode, returns the current range. 191 | ## If not in visual or select mode, returns the last visual range. 192 | ## 193 | 194 | proc vimVisualGetRange*(startPos: ptr pos_T; endPos: ptr pos_T) {.cdecl, importc.} 195 | ## ** 196 | ## Search 197 | ## * 198 | ## 199 | ## vimSearchGetMatchingPair 200 | ## 201 | ## Returns the position of a matching pair, 202 | ## based on the current buffer and cursor position 203 | ## 204 | ## result is NULL if no match found. 205 | ## 206 | 207 | #proc vimSearchGetMatchingPair*(initc: cint): ptr pos_T 208 | ## 209 | ## vimSearchGetHighlights 210 | ## 211 | ## Get highlights for the current search 212 | ## 213 | 214 | proc vimSearchGetHighlights*(start_lnum: linenr_T; end_lnum: linenr_T; 215 | num_highlights: ptr cint; 216 | highlights: ptr ptr searchHighlight_T) {.cdecl, importc.} 217 | ## 218 | ## vimSearchGetPattern 219 | ## 220 | ## Get the current search pattern 221 | ## 222 | 223 | #proc vimSearchGetPattern*(): ptr char_u 224 | proc vimSetStopSearchHighlightCallback*(callback: VoidCallback) {.cdecl, importc.} 225 | ## ** 226 | ## Window 227 | ## 228 | 229 | #proc vimWindowGetWidth*(): cint 230 | #proc vimWindowGetHeight*(): cint 231 | #proc vimWindowGetTopLine*(): cint 232 | #proc vimWindowGetLeftColumn*(): cint 233 | proc vimWindowSetWidth*(width: cint) {.cdecl, importc.} 234 | proc vimWindowSetHeight*(height: cint) {.cdecl, importc.} 235 | #proc vimWindowSetTopLeft*(top: cint; left: cint) 236 | #proc vimSetWindowSplitCallback*(callback: WindowSplitCallback) 237 | #proc vimSetWindowMovementCallback*(callback: WindowMovementCallback) 238 | ## ** 239 | ## Misc 240 | ## * 241 | 242 | #proc vimSetClipboardGetCallback*(callback: ClipboardGetCallback) 243 | proc vimGetMode*(): cint {.cdecl, importc.} 244 | proc vimSetYankCallback*(callback: YankCallback) {.cdecl, importc.} 245 | ## Callbacks for when the `:intro` and `:version` commands are used 246 | ## 247 | ## The Vim license has some specific requirements when implementing these methods: 248 | ## 249 | ## 3) A message must be added, at least in the output of the ":version" 250 | ## command and in the intro screen, such that the user of the modified Vim 251 | ## is able to see that it was modified. When distributing as mentioned 252 | ## under 2)e) adding the message is only required for as far as this does 253 | ## not conflict with the license used for the changes. 254 | ## 255 | 256 | #proc vimSetDisplayIntroCallback*(callback: VoidCallback) 257 | #proc vimSetDisplayVersionCallback*(callback: VoidCallback) 258 | 259 | proc vimFree*(p: pointer) {.cdecl, importc: "vim_free".} 260 | -------------------------------------------------------------------------------- /src/paravim/minimap.nim: -------------------------------------------------------------------------------- 1 | from text import nil 2 | from paranim/gl import nil 3 | from paranim/gl/entities import nil 4 | from colors import nil 5 | 6 | const minimapScale* = 6f 7 | 8 | type 9 | Minimap = tuple[textEntity: text.ParavimTextEntity, rectsEntity: entities.InstancedTwoDEntity, show: bool] 10 | 11 | proc initMinimap*( 12 | fullText: text.ParavimTextEntity, 13 | rectsEntity: entities.InstancedTwoDEntity, 14 | uncompiledRectEntity: entities.UncompiledTwoDEntity, 15 | windowWidth: int, 16 | windowHeight: int, 17 | fontSize: float, 18 | defaultFontSize: static[float], 19 | scrollX: float, 20 | scrollY: float, 21 | lineCount: int, 22 | ): Minimap = 23 | const 24 | minSizeToShowChars = (defaultFontSize * 2) / minimapScale 25 | minChars = 40 # minimum number of chars that minimap must be able to display 26 | maxLines = 1000 # u_char_counts can only hold this many 27 | let 28 | fontWidth = text.monoFontWidth * fontSize 29 | fontHeight = text.monoFont.height * fontSize 30 | minimapFontSize = fontSize / minimapScale 31 | minimapFontWidth = text.monoFontWidth * minimapFontSize 32 | minimapFontHeight = text.monoFont.height * minimapFontSize 33 | minimapHeight = max(0.0, float(windowHeight) - fontHeight) 34 | minimapWidth = float(windowWidth) / minimapScale 35 | # number of chars that can fit in minimap 36 | minimapChars = int(minimapWidth / minimapFontWidth) 37 | minimapLineCount = min(int(minimapHeight / minimapFontHeight), maxLines) 38 | minimapIsOverflowing = lineCount > minimapLineCount 39 | startColumn = int(scrollX / fontWidth) 40 | startLine = 41 | if minimapIsOverflowing: 42 | min( 43 | int(max(scrollY, 0) / fontHeight), # lines above 44 | lineCount - minimapLineCount # lines below 45 | ) 46 | else: 47 | 0 48 | # minimap text 49 | block: 50 | var e = fullText 51 | if minimapIsOverflowing: 52 | let endLine = min(minimapLineCount+startLine, lineCount) 53 | text.cropLines(e, startLine, endLine) 54 | text.updateUniforms(e, startLine, startColumn, minimapFontSize < minSizeToShowChars) 55 | e.project(float(windowWidth), float(windowHeight)) 56 | e.translate(float(windowWidth) - minimapWidth, 0f) 57 | if startColumn > 0: 58 | e.translate(-(startColumn.float * minimapFontWidth), 0f) 59 | if startLine > 0: 60 | e.translate(0f, -(startLine.float * minimapFontHeight)) 61 | e.scale(minimapFontSize, minimapFontSize) 62 | result.textEntity = e 63 | # minimap rects 64 | block: 65 | var e = gl.copy(rectsEntity) 66 | var bg = uncompiledRectEntity 67 | bg.project(float(windowWidth), float(windowHeight)) 68 | bg.translate(float(windowWidth) - minimapWidth, 0) 69 | bg.scale(minimapWidth, minimapHeight) 70 | bg.color(colors.bgColor) 71 | e.add(bg) 72 | var view = uncompiledRectEntity 73 | view.project(float(windowWidth), float(windowHeight)) 74 | view.translate(float(windowWidth) - minimapWidth, 0) 75 | view.translate(0f, scrollY / minimapScale - startLine.float * minimapFontHeight) 76 | view.scale(minimapWidth, minimapHeight / minimapScale) 77 | view.color(colors.minimapViewColor) 78 | e.add(view) 79 | result.rectsEntity = e 80 | # show minimap 81 | let 82 | textViewHeight = windowHeight.float - fontHeight 83 | visibleLines = int(textViewHeight / fontHeight) 84 | showMinimap = minimapChars >= minChars and lineCount > visibleLines 85 | result.show = showMinimap 86 | -------------------------------------------------------------------------------- /src/paravim/scroll.nim: -------------------------------------------------------------------------------- 1 | import pararules 2 | 3 | type 4 | Scroll = tuple[x: float, y: float, targetX: float, targetY: float, speedX: float, speedY: float] 5 | 6 | const 7 | scrollSpeed = 40.0 8 | scrollLimit = 10.0 9 | minScrollSpeed* = 5.0 10 | deceleration = 0.8 11 | 12 | func decelerate(speed: float): float = 13 | let speed = speed * deceleration 14 | if abs(speed) < minScrollSpeed: 15 | minScrollSpeed 16 | else: 17 | speed 18 | 19 | func startScrollingCamera*(scroll: Scroll, xoffset: float, yoffset: float): Scroll = 20 | let 21 | xoffset = block: 22 | const stickiness = 2.5 23 | let xoffset = 24 | # make the left edge "sticky" so it doesn't move unintentionally 25 | if scroll.x == 0 and abs(xoffset) < stickiness: 26 | 0.0 27 | else: 28 | xoffset 29 | min(xoffset, scrollLimit).max(-scrollLimit) 30 | yoffset = min(yoffset, scrollLimit).max(-scrollLimit) 31 | # flip the sign because the camera must go the opposite direction 32 | xdiff = -1 * scrollSpeed * xoffset 33 | ydiff = -1 * scrollSpeed * yoffset 34 | ( 35 | scroll.x, 36 | scroll.y, 37 | scroll.targetX + xdiff, 38 | scroll.targetY + ydiff, 39 | scroll.speedX + abs(xdiff), 40 | scroll.speedY + abs(ydiff) 41 | ) 42 | 43 | func animateCamera*(scroll: Scroll, deltaTime: float): Scroll = 44 | const minDiff = 1.0 45 | let 46 | xdiff = scroll.targetX - scroll.x 47 | ydiff = scroll.targetY - scroll.y 48 | newX = 49 | if abs(xdiff) < minDiff: 50 | scroll.targetX 51 | else: 52 | scroll.x + (xdiff * min(1.0, deltaTime * scroll.speedX)) 53 | newY = 54 | if abs(ydiff) < minDiff: 55 | scroll.targetY 56 | else: 57 | scroll.y + (ydiff * min(1.0, deltaTime * scroll.speedY)) 58 | newSpeedX = 59 | if newX == scroll.targetX: 60 | 0.0 61 | else: 62 | decelerate(scroll.speedX) 63 | newSpeedY = 64 | if newY == scroll.targetY: 65 | 0.0 66 | else: 67 | decelerate(scroll.speedY) 68 | ( 69 | newX, 70 | newY, 71 | scroll.targetX, 72 | scroll.targetY, 73 | newSpeedX, 74 | newSpeedY 75 | ) 76 | -------------------------------------------------------------------------------- /src/paravim/shaders/fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision mediump float; 3 | uniform sampler2D u_image; 4 | uniform float u_alpha; 5 | uniform bool u_show_blocks; 6 | in vec2 v_tex_coord; 7 | in vec4 v_color; 8 | out vec4 o_color; 9 | void main() 10 | { 11 | if (u_show_blocks) { 12 | o_color = v_color; 13 | return; 14 | } 15 | 16 | // get the color from the attributes 17 | vec4 input_color = v_color; 18 | // set its alpha color if necessary 19 | if (input_color.w == 1.0) 20 | { 21 | input_color.w = u_alpha; 22 | } 23 | // get the color from the texture 24 | o_color = texture(u_image, v_tex_coord); 25 | // if it's black, make it a transparent pixel 26 | if (o_color.rgb == vec3(0.0, 0.0, 0.0)) 27 | { 28 | o_color = vec4(0.0, 0.0, 0.0, 0.0); 29 | } 30 | // otherwise, use the input color 31 | else 32 | { 33 | o_color = input_color; 34 | } 35 | // the size of one pixel 36 | vec2 one_pixel = vec2(1) / vec2(textureSize(u_image, 0)); 37 | // left 38 | vec4 left_color = texture(u_image, v_tex_coord + vec2(one_pixel.x, 0.0)); 39 | if (left_color.rgb == vec3(0.0, 0.0, 0.0)) 40 | { 41 | left_color = vec4(0.0, 0.0, 0.0, 0.0); 42 | } 43 | else 44 | { 45 | left_color = input_color; 46 | } 47 | // right 48 | vec4 right_color = texture(u_image, v_tex_coord + vec2(0 - one_pixel.x, 0.0)); 49 | if (right_color.rgb == vec3(0.0, 0.0, 0.0)) 50 | { 51 | right_color = vec4(0.0, 0.0, 0.0, 0.0); 52 | } 53 | else 54 | { 55 | right_color = input_color; 56 | } 57 | // top 58 | vec4 top_color = texture(u_image, v_tex_coord + vec2(0.0, one_pixel.y)); 59 | if (top_color.rgb == vec3(0.0, 0.0, 0.0)) 60 | { 61 | top_color = vec4(0.0, 0.0, 0.0, 0.0); 62 | } 63 | else 64 | { 65 | top_color = input_color; 66 | } 67 | // bottom 68 | vec4 bottom_color = texture(u_image, v_tex_coord + vec2(0.0, 0 - one_pixel.y)); 69 | if (bottom_color.rgb == vec3(0.0, 0.0, 0.0)) 70 | { 71 | bottom_color = vec4(0.0, 0.0, 0.0, 0.0); 72 | } 73 | else 74 | { 75 | bottom_color = input_color; 76 | } 77 | // average 78 | o_color = (o_color + left_color + right_color + top_color + bottom_color) / 5.0; 79 | // discard transparent pixels 80 | if (o_color.w == 0.0) 81 | { 82 | discard; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/paravim/shaders/vertex.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | uniform mat3 u_matrix; 3 | uniform int u_char_counts[1000]; 4 | uniform int u_start_line; 5 | uniform int u_start_column; 6 | uniform float u_font_height; 7 | in vec2 a_position; 8 | in vec4 a_color; 9 | in mat3 a_translate_matrix; 10 | in mat3 a_texture_matrix; 11 | in mat3 a_scale_matrix; 12 | out vec2 v_tex_coord; 13 | out vec4 v_color; 14 | void main() 15 | { 16 | int total_char_count = 0; 17 | int current_line = 0; 18 | for (int i=0; i<1000; ++i) 19 | { 20 | total_char_count += u_char_counts[i]; 21 | if (total_char_count > gl_InstanceID) 22 | { 23 | total_char_count -= u_char_counts[i]; 24 | break; 25 | } 26 | else 27 | { 28 | current_line += 1; 29 | } 30 | } 31 | mat3 translate_matrix = a_translate_matrix; 32 | translate_matrix[2][1] += u_font_height * (u_start_line + current_line); 33 | int current_column = gl_InstanceID - total_char_count; 34 | if (u_start_column > current_column) 35 | { 36 | v_color = vec4(0.0, 0.0, 0.0, 0.0); 37 | return; 38 | } 39 | gl_Position = vec4((u_matrix * translate_matrix * a_scale_matrix * vec3(a_position, 1)).xy, 0, 1); 40 | v_tex_coord = (a_texture_matrix * vec3(a_position, 1)).xy; 41 | v_color = a_color; 42 | } 43 | -------------------------------------------------------------------------------- /src/paravim/structs.nim: -------------------------------------------------------------------------------- 1 | type 2 | linenr_T* = clong 3 | colnr_T* = cint 4 | short_u* = cushort 5 | char_u* = cuchar 6 | 7 | pos_T* {.bycopy.} = object 8 | lnum*: linenr_T ## line number 9 | col*: colnr_T ## column number 10 | coladd*: colnr_T ## extra virtual column 11 | 12 | msgPriority_T* = enum 13 | MSG_INFO, MSG_WARNING, MSG_ERROR 14 | windowSplit_T* = enum 15 | HORIZONTAL_SPLIT, VERTICAL_SPLIT, TAB_PAGE 16 | windowMovement_T* = enum 17 | WIN_CURSOR_LEFT, ## h 18 | WIN_CURSOR_RIGHT, ## l 19 | WIN_CURSOR_UP, ## k 20 | WIN_CURSOR_DOWN, ## j 21 | WIN_MOVE_FULL_LEFT, ## H 22 | WIN_MOVE_FULL_RIGHT, ## L 23 | WIN_MOVE_FULL_UP, ## K 24 | WIN_MOVE_FULL_DOWN, ## J 25 | WIN_CURSOR_TOP_LEFT, ## t 26 | WIN_CURSOR_BOTTOM_RIGHT, ## b 27 | WIN_CURSOR_PREVIOUS, ## p 28 | WIN_MOVE_ROTATE_DOWNWARDS, ## r 29 | WIN_MOVE_ROTATE_UPWARDS ## R 30 | 31 | yankInfo_T* {.bycopy.} = object 32 | op_char*: cint 33 | extra_op_char*: cint 34 | regname*: cint 35 | blockType*: cint ## MLINE, MCHAR, MBLOCK 36 | start*: pos_T 37 | `end`*: pos_T 38 | numLines*: cint 39 | lines*: ptr ptr char_u 40 | 41 | gotoTarget_T* = enum 42 | DEFINITION, DECLARATION, IMPLEMENTATION, TYPEDEFINITION 43 | gotoRequest_T* {.bycopy.} = object 44 | location*: pos_T 45 | target*: gotoTarget_T 46 | 47 | ClipboardGetCallback* = proc (regname: cint; num_lines: ptr cint; lines: ptr ptr ptr char_u): cint {.cdecl.} 48 | VoidCallback* = proc () {.cdecl.} 49 | WindowSplitCallback* = proc (splitType: windowSplit_T; fname: ptr char_u) {.cdecl.} 50 | WindowMovementCallback* = proc (movementType: windowMovement_T; count: cint) {.cdecl.} 51 | YankCallback* = proc (yankInfo: ptr yankInfo_T) {.cdecl.} 52 | GotoCallback* = proc (gotoInfo: gotoRequest_T): cint {.cdecl.} 53 | 54 | lpos_T* {.bycopy.} = object 55 | lnum*: linenr_T ## line number 56 | col*: colnr_T ## column number 57 | 58 | buf_T* = pointer 59 | searchHighlight_T* {.bycopy.} = object 60 | start*: pos_T 61 | `end`*: pos_T 62 | 63 | bufferUpdate_T* {.bycopy.} = object 64 | buf*: buf_T 65 | lnum*: linenr_T ## first line with change 66 | lnume*: linenr_T ## line below last changed line 67 | xtra*: clong ## number of extra lines (negative when deleting) 68 | 69 | writeFailureReason_T* = enum 70 | FILE_CHANGED 71 | BufferUpdateCallback* = proc (bufferUpdate: bufferUpdate_T) {.cdecl.} 72 | FileWriteFailureCallback* = proc (failureReason: writeFailureReason_T; buf: buf_T) {.cdecl.} 73 | MessageCallback* = proc (title: ptr char_u; msg: ptr char_u; priority: msgPriority_T) {.cdecl.} 74 | DirectoryChangedCallback* = proc (path: ptr char_u) {.cdecl.} 75 | QuitCallback* = proc (buf: buf_T; isForced: cint) {.cdecl.} 76 | 77 | event_T* = enum 78 | EVENT_BUFADD, ## after adding a buffer to the buffer list 79 | EVENT_BUFDELETE, ## deleting a buffer from the buffer list 80 | EVENT_BUFENTER, ## after entering a buffer 81 | EVENT_BUFFILEPOST, ## after renaming a buffer 82 | EVENT_BUFFILEPRE, ## before renaming a buffer 83 | EVENT_BUFHIDDEN, ## just after buffer becomes hidden 84 | EVENT_BUFLEAVE, ## before leaving a buffer 85 | EVENT_BUFNEW, ## after creating any buffer 86 | EVENT_BUFNEWFILE, ## when creating a buffer for a new file 87 | EVENT_BUFREADCMD, ## read buffer using command 88 | EVENT_BUFREADPOST, ## after reading a buffer 89 | EVENT_BUFREADPRE, ## before reading a buffer 90 | EVENT_BUFUNLOAD, ## just before unloading a buffer 91 | EVENT_BUFWINENTER, ## after showing a buffer in a window 92 | EVENT_BUFWINLEAVE, ## just after buffer removed from window 93 | EVENT_BUFWIPEOUT, ## just before really deleting a buffer 94 | EVENT_BUFWRITECMD, ## write buffer using command 95 | EVENT_BUFWRITEPOST, ## after writing a buffer 96 | EVENT_BUFWRITEPRE, ## before writing a buffer 97 | EVENT_CMDLINECHANGED, ## command line was modified 98 | EVENT_CMDLINEENTER, ## after entering the command line 99 | EVENT_CMDLINELEAVE, ## before leaving the command line 100 | EVENT_CMDUNDEFINED, ## command undefined 101 | EVENT_CMDWINENTER, ## after entering the cmdline window 102 | EVENT_CMDWINLEAVE, ## before leaving the cmdline window 103 | EVENT_COLORSCHEME, ## after loading a colorscheme 104 | EVENT_COLORSCHEMEPRE, ## before loading a colorscheme 105 | EVENT_COMPLETECHANGED, ## after completion popup menu changed 106 | EVENT_COMPLETEDONE, ## after finishing insert complete 107 | EVENT_CURSORHOLD, ## cursor in same position for a while 108 | EVENT_CURSORHOLDI, ## idem, in Insert mode 109 | EVENT_CURSORMOVED, ## cursor was moved 110 | EVENT_CURSORMOVEDI, ## cursor was moved in Insert mode 111 | EVENT_DIFFUPDATED, ## after diffs were updated 112 | EVENT_DIRCHANGED, ## after user changed directory 113 | EVENT_ENCODINGCHANGED, ## after changing the 'encoding' option 114 | EVENT_EXITPRE, ## before exiting 115 | EVENT_FILEAPPENDCMD, ## append to a file using command 116 | EVENT_FILEAPPENDPOST, ## after appending to a file 117 | EVENT_FILEAPPENDPRE, ## before appending to a file 118 | EVENT_FILECHANGEDRO, ## before first change to read-only file 119 | EVENT_FILECHANGEDSHELL, ## after shell command that changed file 120 | EVENT_FILECHANGEDSHELLPOST, ## after (not) reloading changed file 121 | EVENT_FILEREADCMD, ## read from a file using command 122 | EVENT_FILEREADPOST, ## after reading a file 123 | EVENT_FILEREADPRE, ## before reading a file 124 | EVENT_FILETYPE, ## new file type detected (user defined) 125 | EVENT_FILEWRITECMD, ## write to a file using command 126 | EVENT_FILEWRITEPOST, ## after writing a file 127 | EVENT_FILEWRITEPRE, ## before writing a file 128 | EVENT_FILTERREADPOST, ## after reading from a filter 129 | EVENT_FILTERREADPRE, ## before reading from a filter 130 | EVENT_FILTERWRITEPOST, ## after writing to a filter 131 | EVENT_FILTERWRITEPRE, ## before writing to a filter 132 | EVENT_FOCUSGAINED, ## got the focus 133 | EVENT_FOCUSLOST, ## lost the focus to another app 134 | EVENT_FUNCUNDEFINED, ## if calling a function which doesn't exist 135 | EVENT_GUIENTER, ## after starting the GUI 136 | EVENT_GUIFAILED, ## after starting the GUI failed 137 | EVENT_INSERTCHANGE, ## when changing Insert/Replace mode 138 | EVENT_INSERTCHARPRE, ## before inserting a char 139 | EVENT_INSERTENTER, ## when entering Insert mode 140 | EVENT_INSERTLEAVE, ## when leaving Insert mode 141 | EVENT_MENUPOPUP, ## just before popup menu is displayed 142 | EVENT_OPTIONSET, ## option was set 143 | EVENT_QUICKFIXCMDPOST, ## after :make, :grep etc. 144 | EVENT_QUICKFIXCMDPRE, ## before :make, :grep etc. 145 | EVENT_QUITPRE, ## before :quit 146 | EVENT_REMOTEREPLY, ## upon string reception from a remote vim 147 | EVENT_SESSIONLOADPOST, ## after loading a session file 148 | EVENT_SHELLCMDPOST, ## after ":!cmd" 149 | EVENT_SHELLFILTERPOST, ## after ":1,2!cmd", ":w !cmd", ":r !cmd". 150 | EVENT_SOURCECMD, ## sourcing a Vim script using command 151 | EVENT_SOURCEPRE, ## before sourcing a Vim script 152 | EVENT_SOURCEPOST, ## after sourcing a Vim script 153 | EVENT_SPELLFILEMISSING, ## spell file missing 154 | EVENT_STDINREADPOST, ## after reading from stdin 155 | EVENT_STDINREADPRE, ## before reading from stdin 156 | EVENT_SWAPEXISTS, ## found existing swap file 157 | EVENT_SYNTAX, ## syntax selected 158 | EVENT_TABCLOSED, ## after closing a tab page 159 | EVENT_TABENTER, ## after entering a tab page 160 | EVENT_TABLEAVE, ## before leaving a tab page 161 | EVENT_TABNEW, ## when entering a new tab page 162 | EVENT_TERMCHANGED, ## after changing 'term' 163 | EVENT_TERMINALOPEN, ## after a terminal buffer was created 164 | EVENT_TERMRESPONSE, ## after setting "v:termresponse" 165 | EVENT_TEXTCHANGED, ## text was modified not in Insert mode 166 | EVENT_TEXTCHANGEDI, ## text was modified in Insert mode 167 | EVENT_TEXTCHANGEDP, ## TextChangedI with popup menu visible 168 | EVENT_TEXTYANKPOST, ## after some text was yanked 169 | EVENT_USER, ## user defined autocommand 170 | EVENT_VIMENTER, ## after starting Vim 171 | EVENT_VIMLEAVE, ## before exiting Vim 172 | EVENT_VIMLEAVEPRE, ## before exiting Vim and writing .viminfo 173 | EVENT_VIMRESIZED, ## after Vim window was resized 174 | EVENT_WINENTER, ## after entering a window 175 | EVENT_WINLEAVE, ## before leaving a window 176 | EVENT_WINNEW, ## when entering a new window 177 | NUM_EVENTS ## MUST be the last one 178 | 179 | AutoCommandCallback* = proc (a1: event_T; buf: buf_T) {.cdecl.} 180 | 181 | -------------------------------------------------------------------------------- /src/paravim/terminal.nim: -------------------------------------------------------------------------------- 1 | import illwill as iw 2 | import pararules 3 | from paravim/libvim import nil 4 | from paravim/vim import nil 5 | from paravim/structs import nil 6 | import paravim/core 7 | from paravim/buffers import nil 8 | import strutils 9 | import tables 10 | 11 | let termRules = 12 | ruleset: 13 | rule getTerminalWindow(Fact): 14 | what: 15 | (Global, WindowColumns, windowColumns) 16 | (Global, WindowLines, windowLines) 17 | (Global, AsciiArt, ascii) 18 | rule resizeTerminalWindow(Fact): 19 | what: 20 | (Global, WindowColumns, windowColumns) 21 | (Global, WindowLines, windowLines) 22 | then: 23 | libvim.vimWindowSetWidth(windowColumns.int32) 24 | libvim.vimWindowSetHeight(windowLines.int32) 25 | rule updateTerminalScrollX(Fact): 26 | what: 27 | (Global, WindowColumns, windowColumns) 28 | (id, CursorColumn, cursorColumn) 29 | (id, ScrollX, scrollX, then = false) 30 | then: 31 | let scrollRight = scrollX.int + windowColumns - 1 32 | if cursorColumn < scrollX.int: 33 | session.insert(id, ScrollX, cursorColumn.float) 34 | elif cursorColumn > scrollRight: 35 | session.insert(id, ScrollX, scrollX + float(cursorColumn - scrollRight)) 36 | rule updateTerminalScrollY(Fact): 37 | what: 38 | (Global, WindowLines, windowLines) 39 | (id, CursorLine, cursorLine) 40 | (id, ScrollY, scrollY, then = false) 41 | then: 42 | let scrollBottom = scrollY.int + windowLines - 2 43 | if cursorLine < scrollY.int: 44 | session.insert(id, ScrollY, cursorLine.float) 45 | elif cursorLine > scrollBottom: 46 | session.insert(id, ScrollY, scrollY + float(cursorLine - scrollBottom)) 47 | 48 | const iwToVimSpecials = 49 | {iw.Key.Backspace.ord: "", 50 | iw.Key.Delete.ord: "", 51 | iw.Key.Tab.ord: "", 52 | iw.Key.Enter.ord: "", 53 | iw.Key.Escape.ord: "", 54 | iw.Key.Up.ord: "", 55 | iw.Key.Down.ord: "", 56 | iw.Key.Left.ord: "", 57 | iw.Key.Right.ord: "", 58 | iw.Key.Home.ord: "", 59 | iw.Key.End.ord: "", 60 | iw.Key.PageUp.ord: "", 61 | iw.Key.PageDown.ord: "", 62 | iw.Key.CtrlD.ord: "", 63 | iw.Key.CtrlR.ord: "", 64 | iw.Key.CtrlU.ord: "", 65 | iw.Key.CtrlV.ord: "",}.toTable 66 | 67 | proc onWindowResize(width: int, height: int) = 68 | core.insert(core.session, core.Global, core.WindowColumns, width) 69 | core.insert(core.session, core.Global, core.WindowLines, height) 70 | 71 | proc exitProc() {.noconv.} = 72 | iw.illwillDeinit() 73 | iw.showCursor() 74 | quit(0) 75 | 76 | proc init*(params: seq[string]) = 77 | iw.illwillInit(fullscreen=true) 78 | setControlCHook(exitProc) 79 | iw.hideCursor() 80 | 81 | proc onQuit(buf: pointer; isForced: cint) {.cdecl.} = 82 | exitProc() 83 | 84 | proc onYank(yankInfo: ptr structs.yankInfo_T) {.cdecl.} = 85 | discard 86 | 87 | for r in termRules.fields: 88 | session.add(r) 89 | 90 | onWindowResize(iw.terminalWidth(), iw.terminalHeight()) 91 | vim.init(onQuit, onYank) 92 | core.initAscii(params.len == 0) 93 | 94 | for fname in params: 95 | discard libvim.vimBufferOpen(fname, 1, 0) 96 | 97 | proc setCharBackground(tb: var iw.TerminalBuffer, col: int, row: int, color: iw.BackgroundColor, cursor: bool) = 98 | if col < 0 or row < 0: 99 | return 100 | var ch = tb[col, row] 101 | ch.bg = color 102 | tb[col, row] = ch 103 | if cursor: 104 | iw.setCursorPos(tb, col, row) 105 | 106 | proc tick*() = 107 | var key = iw.getKey() 108 | case key 109 | of iw.Key.None: discard 110 | else: 111 | let code = key.ord 112 | if iwToVimSpecials.hasKey(code): 113 | vim.onInput(iwToVimSpecials[code]) 114 | elif code >= 32: 115 | vim.onInput($ char(code)) 116 | pararules.fireRules(core.session) 117 | 118 | let 119 | (windowColumns, windowLines, ascii) = pararules.query(core.session, termRules.getTerminalWindow) 120 | vimInfo = pararules.query(core.session, core.rules.getVim) 121 | currentBufferIndex = pararules.find(core.session, core.rules.getCurrentBuffer) 122 | 123 | let 124 | width = iw.terminalWidth() 125 | height = iw.terminalHeight() 126 | var tb = iw.newTerminalBuffer(width, height) 127 | if width != windowColumns or height != windowLines: 128 | onWindowResize(width, height) 129 | 130 | if ascii != "": 131 | let lines = core.asciiArt[ascii] 132 | for i in 0 ..< lines.len: 133 | iw.write(tb, 0, i, lines[i]) 134 | elif currentBufferIndex >= 0: 135 | let currentBuffer = pararules.get(core.session, core.rules.getCurrentBuffer, currentBufferIndex) 136 | # text 137 | let 138 | lines = currentBuffer.lines[] 139 | scrollX = currentBuffer.scrollX.int 140 | scrollY = currentBuffer.scrollY.int 141 | var screenLine = 0 142 | for i in scrollY ..< lines.len: 143 | if screenLine >= height - 1: 144 | break 145 | var line = lines[i] 146 | if scrollX < line.len: 147 | if scrollX > 0: 148 | line = line[scrollX ..< line.len] 149 | else: 150 | line = "" 151 | iw.write(tb, 0, screenLine, line) 152 | screenLine += 1 153 | # selection 154 | if currentBuffer.visualRange != (0, 0, 0, 0): 155 | let 156 | rects = 157 | if currentBuffer.visualBlockMode: 158 | @[buffers.rangeToRect(buffers.normalizeRange(currentBuffer.visualRange, true))] 159 | else: 160 | buffers.rangeToRects(buffers.normalizeRange(currentBuffer.visualRange, false), currentBuffer.lines) 161 | for (left, top, width, height) in rects: 162 | for col in left.int ..< int(left + width): 163 | for row in top.int ..< int(top + height): 164 | setCharBackground(tb, col - currentBuffer.scrollX.int, row - currentBuffer.scrollY.int, iw.bgCyan, false) 165 | # search 166 | if vimInfo.showSearch: 167 | for highlight in currentBuffer.searchRanges: 168 | let rects = buffers.rangeToRects(highlight, currentBuffer.lines) 169 | for (left, top, width, height) in rects: 170 | for col in left.int ..< int(left + width): 171 | for row in top.int ..< int(top + height): 172 | setCharBackground(tb, col - currentBuffer.scrollX.int, row - currentBuffer.scrollY.int, iw.bgMagenta, false) 173 | # cursor 174 | if vimInfo.mode != libvim.CommandLine.ord: 175 | let 176 | col = currentBuffer.cursorColumn - currentBuffer.scrollX.int 177 | row = currentBuffer.cursorLine - currentBuffer.scrollY.int 178 | setCharBackground(tb, col, row, iw.bgYellow, true) 179 | 180 | let cmdLineNum = height-1 181 | if vimInfo.mode == libvim.CommandLine.ord: 182 | # command line text 183 | iw.write(tb, 0, cmdLineNum, vimInfo.commandStart & vimInfo.commandText) 184 | if vimInfo.commandCompletion != "": 185 | let 186 | compLen = vimInfo.commandCompletion.len 187 | commLen = vimInfo.commandText.len 188 | if compLen > commLen: 189 | iw.write(tb, commLen+1, cmdLineNum, iw.fgBlue, vimInfo.commandCompletion[commLen ..< compLen]) 190 | # command line cursor 191 | let col = vimInfo.commandPosition + 1 192 | setCharBackground(tb, col, cmdLineNum, iw.bgYellow, true) 193 | elif vimInfo.message != "": 194 | iw.write(tb, 0, cmdLineNum, iw.bgRed, vimInfo.message) 195 | 196 | iw.display(tb) 197 | -------------------------------------------------------------------------------- /src/paravim/text.nim: -------------------------------------------------------------------------------- 1 | import paranim/opengl, paranim/glm 2 | import paranim/gl, paranim/gl/uniforms, paranim/gl/attributes 3 | from paranim/gl/entities import crop, color 4 | import paratext, paratext/gl/text 5 | from tree_sitter import Node 6 | from colors import nil 7 | from math import nil 8 | import tables 9 | 10 | const 11 | monoFontRaw = staticRead("assets/ttf/FiraCode-Regular.ttf") 12 | instancedTextVertexShader = staticRead("shaders/vertex.glsl") 13 | instancedTextFragmentShader = staticRead("shaders/fragment.glsl") 14 | 15 | let 16 | monoFont* = initFont(ttf = monoFontRaw, fontHeight = 128, firstChar = 32, bitmapWidth = 1024, bitmapHeight = 1024, charCount = 2048) 17 | monoFontWidth* = monoFont.chars[0].xadvance 18 | 19 | type 20 | ParavimTextEntityUniforms = tuple[ 21 | u_matrix: Uniform[Mat3x3[GLfloat]], 22 | u_image: Uniform[Texture[GLubyte]], 23 | u_char_counts: Uniform[seq[GLint]], 24 | u_start_line: Uniform[GLint], 25 | u_start_column: Uniform[GLint], 26 | u_font_height: Uniform[GLfloat], 27 | u_alpha: Uniform[GLfloat], 28 | u_show_blocks: Uniform[GLuint], 29 | ] 30 | ParavimTextEntityAttributes = tuple[ 31 | a_position: Attribute[GLfloat], 32 | a_translate_matrix: Attribute[GLfloat], 33 | a_scale_matrix: Attribute[GLfloat], 34 | a_texture_matrix: Attribute[GLfloat], 35 | a_color: Attribute[GLfloat] 36 | ] 37 | ParavimTextEntity* = object of InstancedEntity[ParavimTextEntityUniforms, ParavimTextEntityAttributes] 38 | UncompiledParavimTextEntity = object of UncompiledEntity[ParavimTextEntity, ParavimTextEntityUniforms, ParavimTextEntityAttributes] 39 | 40 | proc initInstancedEntity*(entity: UncompiledTextEntity, font: Font): UncompiledParavimTextEntity = 41 | let e = gl.copy(entity) # make a copy to prevent unexpected problems if `entity` is changed later 42 | result.vertexSource = instancedTextVertexShader 43 | result.fragmentSource = instancedTextFragmentShader 44 | result.uniforms.u_matrix = e.uniforms.u_matrix 45 | result.uniforms.u_image = e.uniforms.u_image 46 | result.uniforms.u_char_counts.disable = true 47 | result.uniforms.u_start_column.data = 0 48 | result.uniforms.u_font_height.data = font.height 49 | result.uniforms.u_alpha.data = 1.0 50 | result.uniforms.u_show_blocks.data = 0 51 | result.attributes.a_translate_matrix = Attribute[GLfloat](disable: true, divisor: 1, size: 3, iter: 3) 52 | new(result.attributes.a_translate_matrix.data) 53 | result.attributes.a_scale_matrix = Attribute[GLfloat](disable: true, divisor: 1, size: 3, iter: 3) 54 | new(result.attributes.a_scale_matrix.data) 55 | result.attributes.a_texture_matrix = Attribute[GLfloat](disable: true, divisor: 1, size: 3, iter: 3) 56 | new(result.attributes.a_texture_matrix.data) 57 | result.attributes.a_color = Attribute[GLfloat](disable: true, divisor: 1, size: 4, iter: 1) 58 | new(result.attributes.a_color.data) 59 | result.attributes.a_position = e.attributes.a_position 60 | 61 | proc addInstanceAttr[T](attr: var Attribute[T], uni: Uniform[Mat3x3[T]]) = 62 | for r in 0 .. 2: 63 | for c in 0 .. 2: 64 | attr.data[].add(uni.data.row(r)[c]) 65 | attr.disable = false 66 | 67 | proc addInstanceAttr[T](attr: var Attribute[T], uni: Uniform[Vec4[T]]) = 68 | for x in 0 .. 3: 69 | attr.data[].add(uni.data[x]) 70 | attr.disable = false 71 | 72 | proc addInstanceAttr[T](attr: var Attribute[T], attr2: Attribute[T]) = 73 | attr.data[].add(attr2.data[]) 74 | attr.disable = false 75 | 76 | proc addInstanceUni[T](uni: var Uniform[seq[T]], uni2: Uniform[seq[T]]) = 77 | uni.data.add(uni2.data) 78 | uni.disable = false 79 | 80 | proc setInstanceAttr[T](attr: var Attribute[T], i: int, uni: Uniform[Mat3x3[T]]) = 81 | for r in 0 .. 2: 82 | for c in 0 .. 2: 83 | attr.data[r*3+c+i*9] = uni.data.row(r)[c] 84 | attr.disable = false 85 | 86 | proc setInstanceAttr[T](attr: var Attribute[T], i: int, uni: Uniform[Vec4[T]]) = 87 | for x in 0 .. 3: 88 | attr.data[x+i*4] = uni.data[x] 89 | attr.disable = false 90 | 91 | proc getInstanceAttr[T](attr: Attribute[T], i: int, uni: var Uniform[Mat3x3[T]]) = 92 | for r in 0 .. 2: 93 | for c in 0 .. 2: 94 | uni.data[r][c] = attr.data[r*3+c+i*9] 95 | uni.data = uni.data.transpose() 96 | uni.disable = false 97 | 98 | proc getInstanceAttr[T](attr: Attribute[T], i: int, uni: var Uniform[Vec4[T]]) = 99 | for x in 0 .. 3: 100 | uni.data[x] = attr.data[x+i*4] 101 | uni.disable = false 102 | 103 | proc cropInstanceAttr[T](attr: var Attribute[T], i: int, j: int) = 104 | let 105 | size = attr.size * attr.iter 106 | data = attr.data 107 | new(attr.data) 108 | attr.data[] = data[][i*size ..< j*size] 109 | attr.disable = false 110 | 111 | proc cropInstanceUni[T](uni: var Uniform[seq[T]], i: int, j: int) = 112 | uni.data = uni.data[i ..< j] 113 | uni.disable = false 114 | 115 | proc add*(instancedEntity: var UncompiledParavimTextEntity, entity: UncompiledTextEntity) = 116 | addInstanceAttr(instancedEntity.attributes.a_translate_matrix, entity.uniforms.u_translate_matrix) 117 | addInstanceAttr(instancedEntity.attributes.a_scale_matrix, entity.uniforms.u_scale_matrix) 118 | addInstanceAttr(instancedEntity.attributes.a_texture_matrix, entity.uniforms.u_texture_matrix) 119 | addInstanceAttr(instancedEntity.attributes.a_color, entity.uniforms.u_color) 120 | # instanceCount will be computed by the `compile` proc 121 | 122 | proc add*(instancedEntity: var ParavimTextEntity, entity: UncompiledTextEntity) = 123 | addInstanceAttr(instancedEntity.attributes.a_translate_matrix, entity.uniforms.u_translate_matrix) 124 | addInstanceAttr(instancedEntity.attributes.a_scale_matrix, entity.uniforms.u_scale_matrix) 125 | addInstanceAttr(instancedEntity.attributes.a_texture_matrix, entity.uniforms.u_texture_matrix) 126 | addInstanceAttr(instancedEntity.attributes.a_color, entity.uniforms.u_color) 127 | instancedEntity.instanceCount += 1 128 | 129 | proc add*(instancedEntity: var ParavimTextEntity, entity: ParavimTextEntity) = 130 | addInstanceAttr(instancedEntity.attributes.a_translate_matrix, entity.attributes.a_translate_matrix) 131 | addInstanceAttr(instancedEntity.attributes.a_scale_matrix, entity.attributes.a_scale_matrix) 132 | addInstanceAttr(instancedEntity.attributes.a_texture_matrix, entity.attributes.a_texture_matrix) 133 | addInstanceAttr(instancedEntity.attributes.a_color, entity.attributes.a_color) 134 | addInstanceUni(instancedEntity.uniforms.u_char_counts, entity.uniforms.u_char_counts) 135 | instancedEntity.instanceCount += entity.instanceCount 136 | 137 | proc `[]`*(instancedEntity: ParavimTextEntity or UncompiledParavimTextEntity, i: int): UncompiledTextEntity = 138 | result.attributes.a_position = instancedEntity.attributes.a_position 139 | result.attributes.a_position.disable = false 140 | result.uniforms.u_image = instancedEntity.uniforms.u_image 141 | result.uniforms.u_image.disable = false 142 | getInstanceAttr(instancedEntity.attributes.a_translate_matrix, i, result.uniforms.u_translate_matrix) 143 | getInstanceAttr(instancedEntity.attributes.a_scale_matrix, i, result.uniforms.u_scale_matrix) 144 | getInstanceAttr(instancedEntity.attributes.a_texture_matrix, i, result.uniforms.u_texture_matrix) 145 | getInstanceAttr(instancedEntity.attributes.a_color, i, result.uniforms.u_color) 146 | 147 | proc `[]=`*(instancedEntity: var ParavimTextEntity, i: int, entity: UncompiledTextEntity) = 148 | setInstanceAttr(instancedEntity.attributes.a_translate_matrix, i, entity.uniforms.u_translate_matrix) 149 | setInstanceAttr(instancedEntity.attributes.a_scale_matrix, i, entity.uniforms.u_scale_matrix) 150 | setInstanceAttr(instancedEntity.attributes.a_texture_matrix, i, entity.uniforms.u_texture_matrix) 151 | setInstanceAttr(instancedEntity.attributes.a_color, i, entity.uniforms.u_color) 152 | 153 | proc `[]=`*(instancedEntity: var UncompiledParavimTextEntity, i: int, entity: UncompiledTextEntity) = 154 | setInstanceAttr(instancedEntity.attributes.a_translate_matrix, i, entity.uniforms.u_translate_matrix) 155 | setInstanceAttr(instancedEntity.attributes.a_scale_matrix, i, entity.uniforms.u_scale_matrix) 156 | setInstanceAttr(instancedEntity.attributes.a_texture_matrix, i, entity.uniforms.u_texture_matrix) 157 | setInstanceAttr(instancedEntity.attributes.a_color, i, entity.uniforms.u_color) 158 | 159 | proc cropLines*(instancedEntity: var ParavimTextEntity, startLine: int, endLine: int) = 160 | let 161 | # startLine and endLine could be temporarily too big if LineCount hasn't been updated yet 162 | startLine = min(startLine, instancedEntity.uniforms.u_char_counts.data.len) 163 | endLine = min(endLine, instancedEntity.uniforms.u_char_counts.data.len) 164 | prevLines = instancedEntity.uniforms.u_char_counts.data[0 ..< startLine] 165 | currLines = instancedEntity.uniforms.u_char_counts.data[startLine ..< endLine] 166 | i = math.sum(prevLines) 167 | j = i + math.sum(currLines) 168 | cropInstanceAttr(instancedEntity.attributes.a_translate_matrix, i, j) 169 | cropInstanceAttr(instancedEntity.attributes.a_scale_matrix, i, j) 170 | cropInstanceAttr(instancedEntity.attributes.a_texture_matrix, i, j) 171 | cropInstanceAttr(instancedEntity.attributes.a_color, i, j) 172 | cropInstanceUni(instancedEntity.uniforms.u_char_counts, startLine, endLine) 173 | instancedEntity.instanceCount = int32(j - i) 174 | 175 | proc cropLines*(instancedEntity: var ParavimTextEntity, startLine: int) = 176 | cropLines(instancedEntity, startLine, instancedEntity.uniforms.u_char_counts.data.len) 177 | 178 | proc getColor(col: int, parsedNodes: openArray[Node], defaultColor: glm.Vec4[GLfloat]): glm.Vec4[GLfloat] = 179 | result = defaultColor 180 | for (kind, startCol, endCol) in parsedNodes: 181 | if col >= startCol and (col < endCol or endCol == -1): 182 | return colors.syntaxColors[kind] 183 | 184 | proc add*(instancedEntity: var ParavimTextEntity, entity: UncompiledTextEntity, font: Font, fontColor: glm.Vec4[GLfloat], text: string, parsedNodes: openArray[Node], startPos: float): float = 185 | let lineNum = instancedEntity.uniforms.u_char_counts.data.len - 1 186 | result = startPos 187 | for i in 0 ..< text.len: 188 | let 189 | ch = text[i] 190 | charIndex = int(ch) - font.firstChar 191 | bakedChar = 192 | if charIndex >= 0 and charIndex < font.chars.len: 193 | font.chars[charIndex] 194 | else: # if char isn't found, use the space char 195 | font.chars[0] 196 | let color = getColor(i, parsedNodes, fontColor) 197 | var e = entity 198 | e.crop(bakedChar, result, font.baseline) 199 | e.color(color) 200 | instancedEntity.add(e) 201 | instancedEntity.uniforms.u_char_counts.data[lineNum] += 1 202 | result += bakedChar.xadvance 203 | 204 | proc addLine*(instancedEntity: var ParavimTextEntity, entity: UncompiledTextEntity, font: Font, fontColor: glm.Vec4[GLfloat], text: string, parsedNodes: openArray[Node]): float = 205 | instancedEntity.uniforms.u_char_counts.data.add(0) 206 | instancedEntity.uniforms.u_char_counts.disable = false 207 | add(instancedEntity, entity, font, fontColor, text, parsedNodes, 0f) 208 | 209 | proc updateColors*(instancedEntity: var ParavimTextEntity, parsedNodes: tree_sitter.Nodes, lines: ref seq[string], fontColor: glm.Vec4[GLfloat]) = 210 | instancedEntity.attributes.a_color.data.new 211 | for lineNum in 0 ..< lines[].len: 212 | for colNum in 0 ..< lines[][lineNum].len: 213 | let color = getColor(colNum, parsedNodes[][lineNum], fontColor) 214 | for x in 0 .. 3: 215 | instancedEntity.attributes.a_color.data[].add(color[x]) 216 | 217 | proc updateUniforms*(e: var ParavimTextEntity, startLine: int, startColumn: int, showBlocks: bool) = 218 | e.uniforms.u_start_line.data = startLine.int32 219 | e.uniforms.u_start_line.disable = false 220 | e.uniforms.u_start_column.data = startColumn.int32 221 | e.uniforms.u_start_column.disable = false 222 | e.uniforms.u_show_blocks.data = if showBlocks: 1 else: 0 223 | e.uniforms.u_show_blocks.disable = false 224 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter.nim: -------------------------------------------------------------------------------- 1 | {.compile: "tree_sitter/lib.c".} 2 | {.compile: "tree_sitter/parser_javascript.c".} 3 | {.compile: "tree_sitter/scanner_javascript.c".} 4 | {.compile: "tree_sitter/parser_c.c".} 5 | {.compile: "tree_sitter/parser_json.c".} 6 | {.compile: "tree_sitter/parser_python.c".} 7 | {.compile: "tree_sitter/scanner_python.c".} 8 | {.compile: "tree_sitter/parser_nim.c".} 9 | {.compile: "tree_sitter/scanner_nim.c".} 10 | 11 | import tree_sitter/tree_sitter/api 12 | from os import nil 13 | from strutils import nil 14 | from colors import nil 15 | import tables 16 | 17 | proc free(p: pointer) {.cdecl, importc: "free".} 18 | proc tree_sitter_javascript(): pointer {.cdecl, importc: "tree_sitter_javascript".} 19 | proc tree_sitter_json(): pointer {.cdecl, importc: "tree_sitter_json".} 20 | proc tree_sitter_c(): pointer {.cdecl, importc: "tree_sitter_c".} 21 | proc tree_sitter_python(): pointer {.cdecl, importc: "tree_sitter_python".} 22 | proc tree_sitter_nim(): pointer {.cdecl, importc: "tree_sitter_nim".} 23 | 24 | proc init*(path: string, lines: seq[string]): tuple[tree: pointer, parser: pointer] = 25 | let parser = ts_parser_new() 26 | let (_, _, ext) = os.splitFile(path) 27 | case ext: 28 | of ".js": 29 | doAssert ts_parser_set_language(parser, tree_sitter_javascript()) 30 | of ".json": 31 | doAssert ts_parser_set_language(parser, tree_sitter_json()) 32 | of ".c", ".h", ".cc", ".cpp", ".hpp": 33 | doAssert ts_parser_set_language(parser, tree_sitter_c()) 34 | of ".py": 35 | doAssert ts_parser_set_language(parser, tree_sitter_python()) 36 | of ".nim", ".nims", ".nimble": 37 | doAssert ts_parser_set_language(parser, tree_sitter_nim()) 38 | else: 39 | ts_parser_delete(parser) 40 | return (nil, nil) 41 | let content = strutils.join(lines, "\n") 42 | (ts_parser_parse_string(parser, nil, content, content.len.uint32), parser) 43 | 44 | proc deleteTree*(tree: pointer) = 45 | if tree != nil: 46 | ts_tree_delete(tree) 47 | 48 | proc deleteParser*(parser: pointer) = 49 | if parser != nil: 50 | ts_parser_delete(parser) 51 | 52 | proc echoTree*(tree: pointer) = 53 | if tree != nil: 54 | let 55 | node = ts_tree_root_node(tree) 56 | sexpr = ts_node_string(node) 57 | echo sexpr 58 | free(sexpr) 59 | else: 60 | echo "nil" 61 | 62 | type 63 | Node* = tuple[kind: string, startCol: int, endCol: int] 64 | Nodes* = ref seq[seq[Node]] 65 | 66 | proc parse*(node: TSNode, nodes: var Nodes) = 67 | let kind = $ ts_node_type(node) 68 | if colors.syntaxColors.hasKey(kind): 69 | let 70 | startPoint = ts_node_start_point(node) 71 | endPoint = ts_node_end_point(node) 72 | for line in startPoint.row .. endPoint.row: 73 | let 74 | lineNum = line.int 75 | startLine = startPoint.row.int 76 | endLine = endPoint.row.int 77 | startCol = if lineNum == startLine: startPoint.column.int else: 0 78 | endCol = if lineNum == endLine: endPoint.column.int else: -1 79 | nodes[][lineNum].add((kind, startCol, endCol)) 80 | else: 81 | for i in 0 ..< ts_node_child_count(node): 82 | let child = ts_node_child(node, i) 83 | parse(child, nodes) 84 | 85 | proc parse*(tree: pointer, lineCount: int): Nodes = 86 | if tree != nil: 87 | let node = ts_tree_root_node(tree) 88 | new(result) 89 | result[] = newSeq[seq[Node]](lineCount) 90 | parse(node, result) 91 | 92 | proc editTree*(tree: pointer, parser: pointer, newLines: ref seq[string]): pointer = 93 | if parser != nil: 94 | let content = strutils.join(newLines[], "\n") 95 | result = ts_parser_parse_string(parser, nil, content, content.len.uint32) 96 | if tree != nil: 97 | ts_tree_delete(tree) 98 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ALLOC_H_ 2 | #define TREE_SITTER_ALLOC_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #if defined(TREE_SITTER_TEST) 13 | 14 | void *ts_record_malloc(size_t); 15 | void *ts_record_calloc(size_t, size_t); 16 | void *ts_record_realloc(void *, size_t); 17 | void ts_record_free(void *); 18 | bool ts_toggle_allocation_recording(bool); 19 | 20 | static inline void *ts_malloc(size_t size) { 21 | return ts_record_malloc(size); 22 | } 23 | 24 | static inline void *ts_calloc(size_t count, size_t size) { 25 | return ts_record_calloc(count, size); 26 | } 27 | 28 | static inline void *ts_realloc(void *buffer, size_t size) { 29 | return ts_record_realloc(buffer, size); 30 | } 31 | 32 | static inline void ts_free(void *buffer) { 33 | ts_record_free(buffer); 34 | } 35 | 36 | #else 37 | 38 | #include 39 | 40 | static inline bool ts_toggle_allocation_recording(bool value) { 41 | return false; 42 | } 43 | 44 | static inline void *ts_malloc(size_t size) { 45 | void *result = malloc(size); 46 | if (size > 0 && !result) { 47 | fprintf(stderr, "tree-sitter failed to allocate %lu bytes", size); 48 | exit(1); 49 | } 50 | return result; 51 | } 52 | 53 | static inline void *ts_calloc(size_t count, size_t size) { 54 | void *result = calloc(count, size); 55 | if (count > 0 && !result) { 56 | fprintf(stderr, "tree-sitter failed to allocate %lu bytes", count * size); 57 | exit(1); 58 | } 59 | return result; 60 | } 61 | 62 | static inline void *ts_realloc(void *buffer, size_t size) { 63 | void *result = realloc(buffer, size); 64 | if (size > 0 && !result) { 65 | fprintf(stderr, "tree-sitter failed to reallocate %lu bytes", size); 66 | exit(1); 67 | } 68 | return result; 69 | } 70 | 71 | static inline void ts_free(void *buffer) { 72 | free(buffer); 73 | } 74 | 75 | #endif 76 | 77 | #ifdef __cplusplus 78 | } 79 | #endif 80 | 81 | #endif // TREE_SITTER_ALLOC_H_ 82 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/array.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ARRAY_H_ 2 | #define TREE_SITTER_ARRAY_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "./alloc.h" 14 | 15 | #define Array(T) \ 16 | struct { \ 17 | T *contents; \ 18 | uint32_t size; \ 19 | uint32_t capacity; \ 20 | } 21 | 22 | #define array_init(self) \ 23 | ((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL) 24 | 25 | #define array_new() \ 26 | { NULL, 0, 0 } 27 | 28 | #define array_get(self, index) \ 29 | (assert((uint32_t)index < (self)->size), &(self)->contents[index]) 30 | 31 | #define array_front(self) array_get(self, 0) 32 | 33 | #define array_back(self) array_get(self, (self)->size - 1) 34 | 35 | #define array_clear(self) ((self)->size = 0) 36 | 37 | #define array_reserve(self, new_capacity) \ 38 | array__reserve((VoidArray *)(self), array__elem_size(self), new_capacity) 39 | 40 | #define array_erase(self, index) \ 41 | array__erase((VoidArray *)(self), array__elem_size(self), index) 42 | 43 | #define array_delete(self) array__delete((VoidArray *)self) 44 | 45 | #define array_push(self, element) \ 46 | (array__grow((VoidArray *)(self), 1, array__elem_size(self)), \ 47 | (self)->contents[(self)->size++] = (element)) 48 | 49 | #define array_grow_by(self, count) \ 50 | (array__grow((VoidArray *)(self), count, array__elem_size(self)), \ 51 | memset((self)->contents + (self)->size, 0, (count) * array__elem_size(self)), \ 52 | (self)->size += (count)) 53 | 54 | #define array_push_all(self, other) \ 55 | array_splice((self), (self)->size, 0, (other)->size, (other)->contents) 56 | 57 | #define array_splice(self, index, old_count, new_count, new_contents) \ 58 | array__splice((VoidArray *)(self), array__elem_size(self), index, old_count, \ 59 | new_count, new_contents) 60 | 61 | #define array_insert(self, index, element) \ 62 | array__splice((VoidArray *)(self), array__elem_size(self), index, 0, 1, &element) 63 | 64 | #define array_pop(self) ((self)->contents[--(self)->size]) 65 | 66 | #define array_assign(self, other) \ 67 | array__assign((VoidArray *)(self), (const VoidArray *)(other), array__elem_size(self)) 68 | 69 | // Private 70 | 71 | typedef Array(void) VoidArray; 72 | 73 | #define array__elem_size(self) sizeof(*(self)->contents) 74 | 75 | static inline void array__delete(VoidArray *self) { 76 | ts_free(self->contents); 77 | self->contents = NULL; 78 | self->size = 0; 79 | self->capacity = 0; 80 | } 81 | 82 | static inline void array__erase(VoidArray *self, size_t element_size, 83 | uint32_t index) { 84 | assert(index < self->size); 85 | char *contents = (char *)self->contents; 86 | memmove(contents + index * element_size, contents + (index + 1) * element_size, 87 | (self->size - index - 1) * element_size); 88 | self->size--; 89 | } 90 | 91 | static inline void array__reserve(VoidArray *self, size_t element_size, uint32_t new_capacity) { 92 | if (new_capacity > self->capacity) { 93 | if (self->contents) { 94 | self->contents = ts_realloc(self->contents, new_capacity * element_size); 95 | } else { 96 | self->contents = ts_calloc(new_capacity, element_size); 97 | } 98 | self->capacity = new_capacity; 99 | } 100 | } 101 | 102 | static inline void array__assign(VoidArray *self, const VoidArray *other, size_t element_size) { 103 | array__reserve(self, element_size, other->size); 104 | self->size = other->size; 105 | memcpy(self->contents, other->contents, self->size * element_size); 106 | } 107 | 108 | static inline void array__grow(VoidArray *self, size_t count, size_t element_size) { 109 | size_t new_size = self->size + count; 110 | if (new_size > self->capacity) { 111 | size_t new_capacity = self->capacity * 2; 112 | if (new_capacity < 8) new_capacity = 8; 113 | if (new_capacity < new_size) new_capacity = new_size; 114 | array__reserve(self, element_size, new_capacity); 115 | } 116 | } 117 | 118 | static inline void array__splice(VoidArray *self, size_t element_size, 119 | uint32_t index, uint32_t old_count, 120 | uint32_t new_count, const void *elements) { 121 | uint32_t new_size = self->size + new_count - old_count; 122 | uint32_t old_end = index + old_count; 123 | uint32_t new_end = index + new_count; 124 | assert(old_end <= self->size); 125 | 126 | array__reserve(self, element_size, new_size); 127 | 128 | char *contents = (char *)self->contents; 129 | if (self->size > old_end) { 130 | memmove( 131 | contents + new_end * element_size, 132 | contents + old_end * element_size, 133 | (self->size - old_end) * element_size 134 | ); 135 | } 136 | if (new_count > 0) { 137 | if (elements) { 138 | memcpy( 139 | (contents + index * element_size), 140 | elements, 141 | new_count * element_size 142 | ); 143 | } else { 144 | memset( 145 | (contents + index * element_size), 146 | 0, 147 | new_count * element_size 148 | ); 149 | } 150 | } 151 | self->size += new_count - old_count; 152 | } 153 | 154 | #ifdef __cplusplus 155 | } 156 | #endif 157 | 158 | #endif // TREE_SITTER_ARRAY_H_ 159 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ATOMIC_H_ 2 | #define TREE_SITTER_ATOMIC_H_ 3 | 4 | #include 5 | 6 | #ifdef _WIN32 7 | 8 | #include 9 | 10 | static inline size_t atomic_load(const volatile size_t *p) { 11 | return *p; 12 | } 13 | 14 | static inline uint32_t atomic_inc(volatile uint32_t *p) { 15 | return InterlockedIncrement((long volatile *)p); 16 | } 17 | 18 | static inline uint32_t atomic_dec(volatile uint32_t *p) { 19 | return InterlockedDecrement((long volatile *)p); 20 | } 21 | 22 | #else 23 | 24 | static inline size_t atomic_load(const volatile size_t *p) { 25 | #ifdef __ATOMIC_RELAXED 26 | return __atomic_load_n(p, __ATOMIC_RELAXED); 27 | #else 28 | return __sync_fetch_and_add((volatile size_t *)p, 0); 29 | #endif 30 | } 31 | 32 | static inline uint32_t atomic_inc(volatile uint32_t *p) { 33 | return __sync_add_and_fetch(p, 1u); 34 | } 35 | 36 | static inline uint32_t atomic_dec(volatile uint32_t *p) { 37 | return __sync_sub_and_fetch(p, 1u); 38 | } 39 | 40 | #endif 41 | 42 | #endif // TREE_SITTER_ATOMIC_H_ 43 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/bits.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_BITS_H_ 2 | #define TREE_SITTER_BITS_H_ 3 | 4 | #include 5 | 6 | static inline uint32_t bitmask_for_index(uint16_t id) { 7 | return (1u << (31 - id)); 8 | } 9 | 10 | #if defined _WIN32 && !defined __GNUC__ 11 | 12 | #include 13 | 14 | static inline uint32_t count_leading_zeros(uint32_t x) { 15 | if (x == 0) return 32; 16 | uint32_t result; 17 | _BitScanReverse(&result, x); 18 | return 31 - result; 19 | } 20 | 21 | #else 22 | 23 | static inline uint32_t count_leading_zeros(uint32_t x) { 24 | if (x == 0) return 32; 25 | return __builtin_clz(x); 26 | } 27 | 28 | #endif 29 | #endif // TREE_SITTER_BITS_H_ 30 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/clock.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_CLOCK_H_ 2 | #define TREE_SITTER_CLOCK_H_ 3 | 4 | #include 5 | 6 | typedef uint64_t TSDuration; 7 | 8 | #ifdef _WIN32 9 | 10 | // Windows: 11 | // * Represent a time as a performance counter value. 12 | // * Represent a duration as a number of performance counter ticks. 13 | 14 | #include 15 | typedef uint64_t TSClock; 16 | 17 | static inline TSDuration duration_from_micros(uint64_t micros) { 18 | LARGE_INTEGER frequency; 19 | QueryPerformanceFrequency(&frequency); 20 | return micros * (uint64_t)frequency.QuadPart / 1000000; 21 | } 22 | 23 | static inline uint64_t duration_to_micros(TSDuration self) { 24 | LARGE_INTEGER frequency; 25 | QueryPerformanceFrequency(&frequency); 26 | return self * 1000000 / (uint64_t)frequency.QuadPart; 27 | } 28 | 29 | static inline TSClock clock_null(void) { 30 | return 0; 31 | } 32 | 33 | static inline TSClock clock_now(void) { 34 | LARGE_INTEGER result; 35 | QueryPerformanceCounter(&result); 36 | return (uint64_t)result.QuadPart; 37 | } 38 | 39 | static inline TSClock clock_after(TSClock base, TSDuration duration) { 40 | return base + duration; 41 | } 42 | 43 | static inline bool clock_is_null(TSClock self) { 44 | return !self; 45 | } 46 | 47 | static inline bool clock_is_gt(TSClock self, TSClock other) { 48 | return self > other; 49 | } 50 | 51 | #elif defined(CLOCK_MONOTONIC) && !defined(__APPLE__) 52 | 53 | // POSIX with monotonic clock support (Linux) 54 | // * Represent a time as a monotonic (seconds, nanoseconds) pair. 55 | // * Represent a duration as a number of microseconds. 56 | // 57 | // On these platforms, parse timeouts will correspond accurately to 58 | // real time, regardless of what other processes are running. 59 | 60 | #include 61 | typedef struct timespec TSClock; 62 | 63 | static inline TSDuration duration_from_micros(uint64_t micros) { 64 | return micros; 65 | } 66 | 67 | static inline uint64_t duration_to_micros(TSDuration self) { 68 | return self; 69 | } 70 | 71 | static inline TSClock clock_now(void) { 72 | TSClock result; 73 | clock_gettime(CLOCK_MONOTONIC, &result); 74 | return result; 75 | } 76 | 77 | static inline TSClock clock_null(void) { 78 | return (TSClock) {0, 0}; 79 | } 80 | 81 | static inline TSClock clock_after(TSClock base, TSDuration duration) { 82 | TSClock result = base; 83 | result.tv_sec += duration / 1000000; 84 | result.tv_nsec += (duration % 1000000) * 1000; 85 | return result; 86 | } 87 | 88 | static inline bool clock_is_null(TSClock self) { 89 | return !self.tv_sec; 90 | } 91 | 92 | static inline bool clock_is_gt(TSClock self, TSClock other) { 93 | if (self.tv_sec > other.tv_sec) return true; 94 | if (self.tv_sec < other.tv_sec) return false; 95 | return self.tv_nsec > other.tv_nsec; 96 | } 97 | 98 | #else 99 | 100 | // macOS or POSIX without monotonic clock support 101 | // * Represent a time as a process clock value. 102 | // * Represent a duration as a number of process clock ticks. 103 | // 104 | // On these platforms, parse timeouts may be affected by other processes, 105 | // which is not ideal, but is better than using a non-monotonic time API 106 | // like `gettimeofday`. 107 | 108 | #include 109 | typedef uint64_t TSClock; 110 | 111 | static inline TSDuration duration_from_micros(uint64_t micros) { 112 | return micros * (uint64_t)CLOCKS_PER_SEC / 1000000; 113 | } 114 | 115 | static inline uint64_t duration_to_micros(TSDuration self) { 116 | return self * 1000000 / (uint64_t)CLOCKS_PER_SEC; 117 | } 118 | 119 | static inline TSClock clock_null(void) { 120 | return 0; 121 | } 122 | 123 | static inline TSClock clock_now(void) { 124 | return (uint64_t)clock(); 125 | } 126 | 127 | static inline TSClock clock_after(TSClock base, TSDuration duration) { 128 | return base + duration; 129 | } 130 | 131 | static inline bool clock_is_null(TSClock self) { 132 | return !self; 133 | } 134 | 135 | static inline bool clock_is_gt(TSClock self, TSClock other) { 136 | return self > other; 137 | } 138 | 139 | #endif 140 | 141 | #endif // TREE_SITTER_CLOCK_H_ 142 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/error_costs.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ERROR_COSTS_H_ 2 | #define TREE_SITTER_ERROR_COSTS_H_ 3 | 4 | #define ERROR_STATE 0 5 | #define ERROR_COST_PER_RECOVERY 500 6 | #define ERROR_COST_PER_MISSING_TREE 110 7 | #define ERROR_COST_PER_SKIPPED_TREE 100 8 | #define ERROR_COST_PER_SKIPPED_LINE 30 9 | #define ERROR_COST_PER_SKIPPED_CHAR 1 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/get_changed_ranges.c: -------------------------------------------------------------------------------- 1 | #include "./get_changed_ranges.h" 2 | #include "./subtree.h" 3 | #include "./language.h" 4 | #include "./error_costs.h" 5 | #include "./tree_cursor.h" 6 | #include 7 | 8 | // #define DEBUG_GET_CHANGED_RANGES 9 | 10 | static void ts_range_array_add(TSRangeArray *self, Length start, Length end) { 11 | if (self->size > 0) { 12 | TSRange *last_range = array_back(self); 13 | if (start.bytes <= last_range->end_byte) { 14 | last_range->end_byte = end.bytes; 15 | last_range->end_point = end.extent; 16 | return; 17 | } 18 | } 19 | 20 | if (start.bytes < end.bytes) { 21 | TSRange range = { start.extent, end.extent, start.bytes, end.bytes }; 22 | array_push(self, range); 23 | } 24 | } 25 | 26 | bool ts_range_array_intersects(const TSRangeArray *self, unsigned start_index, 27 | uint32_t start_byte, uint32_t end_byte) { 28 | for (unsigned i = start_index; i < self->size; i++) { 29 | TSRange *range = &self->contents[i]; 30 | if (range->end_byte > start_byte) { 31 | if (range->start_byte >= end_byte) break; 32 | return true; 33 | } 34 | } 35 | return false; 36 | } 37 | 38 | void ts_range_array_get_changed_ranges( 39 | const TSRange *old_ranges, unsigned old_range_count, 40 | const TSRange *new_ranges, unsigned new_range_count, 41 | TSRangeArray *differences 42 | ) { 43 | unsigned new_index = 0; 44 | unsigned old_index = 0; 45 | Length current_position = length_zero(); 46 | bool in_old_range = false; 47 | bool in_new_range = false; 48 | 49 | while (old_index < old_range_count || new_index < new_range_count) { 50 | const TSRange *old_range = &old_ranges[old_index]; 51 | const TSRange *new_range = &new_ranges[new_index]; 52 | 53 | Length next_old_position; 54 | if (in_old_range) { 55 | next_old_position = (Length) {old_range->end_byte, old_range->end_point}; 56 | } else if (old_index < old_range_count) { 57 | next_old_position = (Length) {old_range->start_byte, old_range->start_point}; 58 | } else { 59 | next_old_position = LENGTH_MAX; 60 | } 61 | 62 | Length next_new_position; 63 | if (in_new_range) { 64 | next_new_position = (Length) {new_range->end_byte, new_range->end_point}; 65 | } else if (new_index < new_range_count) { 66 | next_new_position = (Length) {new_range->start_byte, new_range->start_point}; 67 | } else { 68 | next_new_position = LENGTH_MAX; 69 | } 70 | 71 | if (next_old_position.bytes < next_new_position.bytes) { 72 | if (in_old_range != in_new_range) { 73 | ts_range_array_add(differences, current_position, next_old_position); 74 | } 75 | if (in_old_range) old_index++; 76 | current_position = next_old_position; 77 | in_old_range = !in_old_range; 78 | } else if (next_new_position.bytes < next_old_position.bytes) { 79 | if (in_old_range != in_new_range) { 80 | ts_range_array_add(differences, current_position, next_new_position); 81 | } 82 | if (in_new_range) new_index++; 83 | current_position = next_new_position; 84 | in_new_range = !in_new_range; 85 | } else { 86 | if (in_old_range != in_new_range) { 87 | ts_range_array_add(differences, current_position, next_new_position); 88 | } 89 | if (in_old_range) old_index++; 90 | if (in_new_range) new_index++; 91 | in_old_range = !in_old_range; 92 | in_new_range = !in_new_range; 93 | current_position = next_new_position; 94 | } 95 | } 96 | } 97 | 98 | typedef struct { 99 | TreeCursor cursor; 100 | const TSLanguage *language; 101 | unsigned visible_depth; 102 | bool in_padding; 103 | } Iterator; 104 | 105 | static Iterator iterator_new(TreeCursor *cursor, const Subtree *tree, const TSLanguage *language) { 106 | array_clear(&cursor->stack); 107 | array_push(&cursor->stack, ((TreeCursorEntry){ 108 | .subtree = tree, 109 | .position = length_zero(), 110 | .child_index = 0, 111 | .structural_child_index = 0, 112 | })); 113 | return (Iterator) { 114 | .cursor = *cursor, 115 | .language = language, 116 | .visible_depth = 1, 117 | .in_padding = false, 118 | }; 119 | } 120 | 121 | static bool iterator_done(Iterator *self) { 122 | return self->cursor.stack.size == 0; 123 | } 124 | 125 | static Length iterator_start_position(Iterator *self) { 126 | TreeCursorEntry entry = *array_back(&self->cursor.stack); 127 | if (self->in_padding) { 128 | return entry.position; 129 | } else { 130 | return length_add(entry.position, ts_subtree_padding(*entry.subtree)); 131 | } 132 | } 133 | 134 | static Length iterator_end_position(Iterator *self) { 135 | TreeCursorEntry entry = *array_back(&self->cursor.stack); 136 | Length result = length_add(entry.position, ts_subtree_padding(*entry.subtree)); 137 | if (self->in_padding) { 138 | return result; 139 | } else { 140 | return length_add(result, ts_subtree_size(*entry.subtree)); 141 | } 142 | } 143 | 144 | static bool iterator_tree_is_visible(const Iterator *self) { 145 | TreeCursorEntry entry = *array_back(&self->cursor.stack); 146 | if (ts_subtree_visible(*entry.subtree)) return true; 147 | if (self->cursor.stack.size > 1) { 148 | Subtree parent = *self->cursor.stack.contents[self->cursor.stack.size - 2].subtree; 149 | const TSSymbol *alias_sequence = ts_language_alias_sequence( 150 | self->language, 151 | parent.ptr->production_id 152 | ); 153 | return alias_sequence && alias_sequence[entry.structural_child_index] != 0; 154 | } 155 | return false; 156 | } 157 | 158 | static void iterator_get_visible_state(const Iterator *self, Subtree *tree, 159 | TSSymbol *alias_symbol, uint32_t *start_byte) { 160 | uint32_t i = self->cursor.stack.size - 1; 161 | 162 | if (self->in_padding) { 163 | if (i == 0) return; 164 | i--; 165 | } 166 | 167 | for (; i + 1 > 0; i--) { 168 | TreeCursorEntry entry = self->cursor.stack.contents[i]; 169 | 170 | if (i > 0) { 171 | const Subtree *parent = self->cursor.stack.contents[i - 1].subtree; 172 | const TSSymbol *alias_sequence = ts_language_alias_sequence( 173 | self->language, 174 | parent->ptr->production_id 175 | ); 176 | if (alias_sequence) { 177 | *alias_symbol = alias_sequence[entry.structural_child_index]; 178 | } 179 | } 180 | 181 | if (ts_subtree_visible(*entry.subtree) || *alias_symbol) { 182 | *tree = *entry.subtree; 183 | *start_byte = entry.position.bytes; 184 | break; 185 | } 186 | } 187 | } 188 | 189 | static void iterator_ascend(Iterator *self) { 190 | if (iterator_done(self)) return; 191 | if (iterator_tree_is_visible(self) && !self->in_padding) self->visible_depth--; 192 | if (array_back(&self->cursor.stack)->child_index > 0) self->in_padding = false; 193 | self->cursor.stack.size--; 194 | } 195 | 196 | static bool iterator_descend(Iterator *self, uint32_t goal_position) { 197 | if (self->in_padding) return false; 198 | 199 | bool did_descend; 200 | do { 201 | did_descend = false; 202 | TreeCursorEntry entry = *array_back(&self->cursor.stack); 203 | Length position = entry.position; 204 | uint32_t structural_child_index = 0; 205 | for (uint32_t i = 0, n = ts_subtree_child_count(*entry.subtree); i < n; i++) { 206 | const Subtree *child = &entry.subtree->ptr->children[i]; 207 | Length child_left = length_add(position, ts_subtree_padding(*child)); 208 | Length child_right = length_add(child_left, ts_subtree_size(*child)); 209 | 210 | if (child_right.bytes > goal_position) { 211 | array_push(&self->cursor.stack, ((TreeCursorEntry){ 212 | .subtree = child, 213 | .position = position, 214 | .child_index = i, 215 | .structural_child_index = structural_child_index, 216 | })); 217 | 218 | if (iterator_tree_is_visible(self)) { 219 | if (child_left.bytes > goal_position) { 220 | self->in_padding = true; 221 | } else { 222 | self->visible_depth++; 223 | } 224 | return true; 225 | } 226 | 227 | did_descend = true; 228 | break; 229 | } 230 | 231 | position = child_right; 232 | if (!ts_subtree_extra(*child)) structural_child_index++; 233 | } 234 | } while (did_descend); 235 | 236 | return false; 237 | } 238 | 239 | static void iterator_advance(Iterator *self) { 240 | if (self->in_padding) { 241 | self->in_padding = false; 242 | if (iterator_tree_is_visible(self)) { 243 | self->visible_depth++; 244 | } else { 245 | iterator_descend(self, 0); 246 | } 247 | return; 248 | } 249 | 250 | for (;;) { 251 | if (iterator_tree_is_visible(self)) self->visible_depth--; 252 | TreeCursorEntry entry = array_pop(&self->cursor.stack); 253 | if (iterator_done(self)) return; 254 | 255 | const Subtree *parent = array_back(&self->cursor.stack)->subtree; 256 | uint32_t child_index = entry.child_index + 1; 257 | if (ts_subtree_child_count(*parent) > child_index) { 258 | Length position = length_add(entry.position, ts_subtree_total_size(*entry.subtree)); 259 | uint32_t structural_child_index = entry.structural_child_index; 260 | if (!ts_subtree_extra(*entry.subtree)) structural_child_index++; 261 | const Subtree *next_child = &parent->ptr->children[child_index]; 262 | 263 | array_push(&self->cursor.stack, ((TreeCursorEntry){ 264 | .subtree = next_child, 265 | .position = position, 266 | .child_index = child_index, 267 | .structural_child_index = structural_child_index, 268 | })); 269 | 270 | if (iterator_tree_is_visible(self)) { 271 | if (ts_subtree_padding(*next_child).bytes > 0) { 272 | self->in_padding = true; 273 | } else { 274 | self->visible_depth++; 275 | } 276 | } else { 277 | iterator_descend(self, 0); 278 | } 279 | break; 280 | } 281 | } 282 | } 283 | 284 | typedef enum { 285 | IteratorDiffers, 286 | IteratorMayDiffer, 287 | IteratorMatches, 288 | } IteratorComparison; 289 | 290 | static IteratorComparison iterator_compare(const Iterator *old_iter, const Iterator *new_iter) { 291 | Subtree old_tree = NULL_SUBTREE; 292 | Subtree new_tree = NULL_SUBTREE; 293 | uint32_t old_start = 0; 294 | uint32_t new_start = 0; 295 | TSSymbol old_alias_symbol = 0; 296 | TSSymbol new_alias_symbol = 0; 297 | iterator_get_visible_state(old_iter, &old_tree, &old_alias_symbol, &old_start); 298 | iterator_get_visible_state(new_iter, &new_tree, &new_alias_symbol, &new_start); 299 | 300 | if (!old_tree.ptr && !new_tree.ptr) return IteratorMatches; 301 | if (!old_tree.ptr || !new_tree.ptr) return IteratorDiffers; 302 | 303 | if ( 304 | old_alias_symbol == new_alias_symbol && 305 | ts_subtree_symbol(old_tree) == ts_subtree_symbol(new_tree) 306 | ) { 307 | if (old_start == new_start && 308 | !ts_subtree_has_changes(old_tree) && 309 | ts_subtree_symbol(old_tree) != ts_builtin_sym_error && 310 | ts_subtree_size(old_tree).bytes == ts_subtree_size(new_tree).bytes && 311 | ts_subtree_parse_state(old_tree) != TS_TREE_STATE_NONE && 312 | ts_subtree_parse_state(new_tree) != TS_TREE_STATE_NONE && 313 | (ts_subtree_parse_state(old_tree) == ERROR_STATE) == 314 | (ts_subtree_parse_state(new_tree) == ERROR_STATE)) { 315 | return IteratorMatches; 316 | } else { 317 | return IteratorMayDiffer; 318 | } 319 | } 320 | 321 | return IteratorDiffers; 322 | } 323 | 324 | #ifdef DEBUG_GET_CHANGED_RANGES 325 | static inline void iterator_print_state(Iterator *self) { 326 | TreeCursorEntry entry = *array_back(&self->cursor.stack); 327 | TSPoint start = iterator_start_position(self).extent; 328 | TSPoint end = iterator_end_position(self).extent; 329 | const char *name = ts_language_symbol_name(self->language, ts_subtree_symbol(*entry.subtree)); 330 | printf( 331 | "(%-25s %s\t depth:%u [%u, %u] - [%u, %u])", 332 | name, self->in_padding ? "(p)" : " ", 333 | self->visible_depth, 334 | start.row + 1, start.column, 335 | end.row + 1, end.column 336 | ); 337 | } 338 | #endif 339 | 340 | unsigned ts_subtree_get_changed_ranges(const Subtree *old_tree, const Subtree *new_tree, 341 | TreeCursor *cursor1, TreeCursor *cursor2, 342 | const TSLanguage *language, 343 | const TSRangeArray *included_range_differences, 344 | TSRange **ranges) { 345 | TSRangeArray results = array_new(); 346 | 347 | Iterator old_iter = iterator_new(cursor1, old_tree, language); 348 | Iterator new_iter = iterator_new(cursor2, new_tree, language); 349 | 350 | unsigned included_range_difference_index = 0; 351 | 352 | Length position = iterator_start_position(&old_iter); 353 | Length next_position = iterator_start_position(&new_iter); 354 | if (position.bytes < next_position.bytes) { 355 | ts_range_array_add(&results, position, next_position); 356 | position = next_position; 357 | } else if (position.bytes > next_position.bytes) { 358 | ts_range_array_add(&results, next_position, position); 359 | next_position = position; 360 | } 361 | 362 | do { 363 | #ifdef DEBUG_GET_CHANGED_RANGES 364 | printf("At [%-2u, %-2u] Compare ", position.extent.row + 1, position.extent.column); 365 | iterator_print_state(&old_iter); 366 | printf("\tvs\t"); 367 | iterator_print_state(&new_iter); 368 | puts(""); 369 | #endif 370 | 371 | // Compare the old and new subtrees. 372 | IteratorComparison comparison = iterator_compare(&old_iter, &new_iter); 373 | 374 | // Even if the two subtrees appear to be identical, they could differ 375 | // internally if they contain a range of text that was previously 376 | // excluded from the parse, and is now included, or vice-versa. 377 | if (comparison == IteratorMatches && ts_range_array_intersects( 378 | included_range_differences, 379 | included_range_difference_index, 380 | position.bytes, 381 | iterator_end_position(&old_iter).bytes 382 | )) { 383 | comparison = IteratorMayDiffer; 384 | } 385 | 386 | bool is_changed = false; 387 | switch (comparison) { 388 | // If the subtrees are definitely identical, move to the end 389 | // of both subtrees. 390 | case IteratorMatches: 391 | next_position = iterator_end_position(&old_iter); 392 | break; 393 | 394 | // If the subtrees might differ internally, descend into both 395 | // subtrees, finding the first child that spans the current position. 396 | case IteratorMayDiffer: 397 | if (iterator_descend(&old_iter, position.bytes)) { 398 | if (!iterator_descend(&new_iter, position.bytes)) { 399 | is_changed = true; 400 | next_position = iterator_end_position(&old_iter); 401 | } 402 | } else if (iterator_descend(&new_iter, position.bytes)) { 403 | is_changed = true; 404 | next_position = iterator_end_position(&new_iter); 405 | } else { 406 | next_position = length_min( 407 | iterator_end_position(&old_iter), 408 | iterator_end_position(&new_iter) 409 | ); 410 | } 411 | break; 412 | 413 | // If the subtrees are different, record a change and then move 414 | // to the end of both subtrees. 415 | case IteratorDiffers: 416 | is_changed = true; 417 | next_position = length_min( 418 | iterator_end_position(&old_iter), 419 | iterator_end_position(&new_iter) 420 | ); 421 | break; 422 | } 423 | 424 | // Ensure that both iterators are caught up to the current position. 425 | while ( 426 | !iterator_done(&old_iter) && 427 | iterator_end_position(&old_iter).bytes <= next_position.bytes 428 | ) iterator_advance(&old_iter); 429 | while ( 430 | !iterator_done(&new_iter) && 431 | iterator_end_position(&new_iter).bytes <= next_position.bytes 432 | ) iterator_advance(&new_iter); 433 | 434 | // Ensure that both iterators are at the same depth in the tree. 435 | while (old_iter.visible_depth > new_iter.visible_depth) { 436 | iterator_ascend(&old_iter); 437 | } 438 | while (new_iter.visible_depth > old_iter.visible_depth) { 439 | iterator_ascend(&new_iter); 440 | } 441 | 442 | if (is_changed) { 443 | #ifdef DEBUG_GET_CHANGED_RANGES 444 | printf( 445 | " change: [[%u, %u] - [%u, %u]]\n", 446 | position.extent.row + 1, position.extent.column, 447 | next_position.extent.row + 1, next_position.extent.column 448 | ); 449 | #endif 450 | 451 | ts_range_array_add(&results, position, next_position); 452 | } 453 | 454 | position = next_position; 455 | 456 | // Keep track of the current position in the included range differences 457 | // array in order to avoid scanning the entire array on each iteration. 458 | while (included_range_difference_index < included_range_differences->size) { 459 | const TSRange *range = &included_range_differences->contents[ 460 | included_range_difference_index 461 | ]; 462 | if (range->end_byte <= position.bytes) { 463 | included_range_difference_index++; 464 | } else { 465 | break; 466 | } 467 | } 468 | } while (!iterator_done(&old_iter) && !iterator_done(&new_iter)); 469 | 470 | Length old_size = ts_subtree_total_size(*old_tree); 471 | Length new_size = ts_subtree_total_size(*new_tree); 472 | if (old_size.bytes < new_size.bytes) { 473 | ts_range_array_add(&results, old_size, new_size); 474 | } else if (new_size.bytes < old_size.bytes) { 475 | ts_range_array_add(&results, new_size, old_size); 476 | } 477 | 478 | *cursor1 = old_iter.cursor; 479 | *cursor2 = new_iter.cursor; 480 | *ranges = results.contents; 481 | return results.size; 482 | } 483 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/get_changed_ranges.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_GET_CHANGED_RANGES_H_ 2 | #define TREE_SITTER_GET_CHANGED_RANGES_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./tree_cursor.h" 9 | #include "./subtree.h" 10 | 11 | typedef Array(TSRange) TSRangeArray; 12 | 13 | void ts_range_array_get_changed_ranges( 14 | const TSRange *old_ranges, unsigned old_range_count, 15 | const TSRange *new_ranges, unsigned new_range_count, 16 | TSRangeArray *differences 17 | ); 18 | 19 | bool ts_range_array_intersects( 20 | const TSRangeArray *self, unsigned start_index, 21 | uint32_t start_byte, uint32_t end_byte 22 | ); 23 | 24 | unsigned ts_subtree_get_changed_ranges( 25 | const Subtree *old_tree, const Subtree *new_tree, 26 | TreeCursor *cursor1, TreeCursor *cursor2, 27 | const TSLanguage *language, 28 | const TSRangeArray *included_range_differences, 29 | TSRange **ranges 30 | ); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | #endif // TREE_SITTER_GET_CHANGED_RANGES_H_ 37 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/language.c: -------------------------------------------------------------------------------- 1 | #include "./language.h" 2 | #include "./subtree.h" 3 | #include "./error_costs.h" 4 | #include 5 | 6 | uint32_t ts_language_symbol_count(const TSLanguage *self) { 7 | return self->symbol_count + self->alias_count; 8 | } 9 | 10 | uint32_t ts_language_version(const TSLanguage *self) { 11 | return self->version; 12 | } 13 | 14 | uint32_t ts_language_field_count(const TSLanguage *self) { 15 | if (self->version >= TREE_SITTER_LANGUAGE_VERSION_WITH_FIELDS) { 16 | return self->field_count; 17 | } else { 18 | return 0; 19 | } 20 | } 21 | 22 | void ts_language_table_entry( 23 | const TSLanguage *self, 24 | TSStateId state, 25 | TSSymbol symbol, 26 | TableEntry *result 27 | ) { 28 | if (symbol == ts_builtin_sym_error || symbol == ts_builtin_sym_error_repeat) { 29 | result->action_count = 0; 30 | result->is_reusable = false; 31 | result->actions = NULL; 32 | } else { 33 | assert(symbol < self->token_count); 34 | uint32_t action_index = ts_language_lookup(self, state, symbol); 35 | const TSParseActionEntry *entry = &self->parse_actions[action_index]; 36 | result->action_count = entry->count; 37 | result->is_reusable = entry->reusable; 38 | result->actions = (const TSParseAction *)(entry + 1); 39 | } 40 | } 41 | 42 | TSSymbolMetadata ts_language_symbol_metadata( 43 | const TSLanguage *self, 44 | TSSymbol symbol 45 | ) { 46 | if (symbol == ts_builtin_sym_error) { 47 | return (TSSymbolMetadata){.visible = true, .named = true}; 48 | } else if (symbol == ts_builtin_sym_error_repeat) { 49 | return (TSSymbolMetadata){.visible = false, .named = false}; 50 | } else { 51 | return self->symbol_metadata[symbol]; 52 | } 53 | } 54 | 55 | TSSymbol ts_language_public_symbol( 56 | const TSLanguage *self, 57 | TSSymbol symbol 58 | ) { 59 | if (symbol == ts_builtin_sym_error) return symbol; 60 | if (self->version >= TREE_SITTER_LANGUAGE_VERSION_WITH_SYMBOL_DEDUPING) { 61 | return self->public_symbol_map[symbol]; 62 | } else { 63 | return symbol; 64 | } 65 | } 66 | 67 | const char *ts_language_symbol_name( 68 | const TSLanguage *self, 69 | TSSymbol symbol 70 | ) { 71 | if (symbol == ts_builtin_sym_error) { 72 | return "ERROR"; 73 | } else if (symbol == ts_builtin_sym_error_repeat) { 74 | return "_ERROR"; 75 | } else if (symbol < ts_language_symbol_count(self)) { 76 | return self->symbol_names[symbol]; 77 | } else { 78 | return NULL; 79 | } 80 | } 81 | 82 | TSSymbol ts_language_symbol_for_name( 83 | const TSLanguage *self, 84 | const char *string, 85 | uint32_t length, 86 | bool is_named 87 | ) { 88 | if (!strncmp(string, "ERROR", length)) return ts_builtin_sym_error; 89 | uint32_t count = ts_language_symbol_count(self); 90 | for (TSSymbol i = 0; i < count; i++) { 91 | TSSymbolMetadata metadata = ts_language_symbol_metadata(self, i); 92 | if (!metadata.visible || metadata.named != is_named) continue; 93 | const char *symbol_name = self->symbol_names[i]; 94 | if (!strncmp(symbol_name, string, length) && !symbol_name[length]) { 95 | if (self->version >= TREE_SITTER_LANGUAGE_VERSION_WITH_SYMBOL_DEDUPING) { 96 | return self->public_symbol_map[i]; 97 | } else { 98 | return i; 99 | } 100 | } 101 | } 102 | return 0; 103 | } 104 | 105 | TSSymbolType ts_language_symbol_type( 106 | const TSLanguage *self, 107 | TSSymbol symbol 108 | ) { 109 | TSSymbolMetadata metadata = ts_language_symbol_metadata(self, symbol); 110 | if (metadata.named) { 111 | return TSSymbolTypeRegular; 112 | } else if (metadata.visible) { 113 | return TSSymbolTypeAnonymous; 114 | } else { 115 | return TSSymbolTypeAuxiliary; 116 | } 117 | } 118 | 119 | const char *ts_language_field_name_for_id( 120 | const TSLanguage *self, 121 | TSFieldId id 122 | ) { 123 | uint32_t count = ts_language_field_count(self); 124 | if (count && id <= count) { 125 | return self->field_names[id]; 126 | } else { 127 | return NULL; 128 | } 129 | } 130 | 131 | TSFieldId ts_language_field_id_for_name( 132 | const TSLanguage *self, 133 | const char *name, 134 | uint32_t name_length 135 | ) { 136 | uint32_t count = ts_language_field_count(self); 137 | for (TSSymbol i = 1; i < count + 1; i++) { 138 | switch (strncmp(name, self->field_names[i], name_length)) { 139 | case 0: 140 | if (self->field_names[i][name_length] == 0) return i; 141 | break; 142 | case -1: 143 | return 0; 144 | default: 145 | break; 146 | } 147 | } 148 | return 0; 149 | } 150 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/language.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_LANGUAGE_H_ 2 | #define TREE_SITTER_LANGUAGE_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./subtree.h" 9 | #include "tree_sitter/parser.h" 10 | 11 | #define ts_builtin_sym_error_repeat (ts_builtin_sym_error - 1) 12 | #define TREE_SITTER_LANGUAGE_VERSION_WITH_FIELDS 10 13 | #define TREE_SITTER_LANGUAGE_VERSION_WITH_SYMBOL_DEDUPING 11 14 | #define TREE_SITTER_LANGUAGE_VERSION_WITH_SMALL_STATES 11 15 | 16 | typedef struct { 17 | const TSParseAction *actions; 18 | uint32_t action_count; 19 | bool is_reusable; 20 | } TableEntry; 21 | 22 | void ts_language_table_entry(const TSLanguage *, TSStateId, TSSymbol, TableEntry *); 23 | 24 | TSSymbolMetadata ts_language_symbol_metadata(const TSLanguage *, TSSymbol); 25 | 26 | TSSymbol ts_language_public_symbol(const TSLanguage *, TSSymbol); 27 | 28 | static inline bool ts_language_is_symbol_external(const TSLanguage *self, TSSymbol symbol) { 29 | return 0 < symbol && symbol < self->external_token_count + 1; 30 | } 31 | 32 | static inline const TSParseAction *ts_language_actions( 33 | const TSLanguage *self, 34 | TSStateId state, 35 | TSSymbol symbol, 36 | uint32_t *count 37 | ) { 38 | TableEntry entry; 39 | ts_language_table_entry(self, state, symbol, &entry); 40 | *count = entry.action_count; 41 | return entry.actions; 42 | } 43 | 44 | static inline bool ts_language_has_actions(const TSLanguage *self, 45 | TSStateId state, 46 | TSSymbol symbol) { 47 | TableEntry entry; 48 | ts_language_table_entry(self, state, symbol, &entry); 49 | return entry.action_count > 0; 50 | } 51 | 52 | static inline bool ts_language_has_reduce_action(const TSLanguage *self, 53 | TSStateId state, 54 | TSSymbol symbol) { 55 | TableEntry entry; 56 | ts_language_table_entry(self, state, symbol, &entry); 57 | return entry.action_count > 0 && entry.actions[0].type == TSParseActionTypeReduce; 58 | } 59 | 60 | static inline uint16_t ts_language_lookup( 61 | const TSLanguage *self, 62 | TSStateId state, 63 | TSSymbol symbol 64 | ) { 65 | if ( 66 | self->version >= TREE_SITTER_LANGUAGE_VERSION_WITH_SMALL_STATES && 67 | state >= self->large_state_count 68 | ) { 69 | uint32_t index = self->small_parse_table_map[state - self->large_state_count]; 70 | const uint16_t *data = &self->small_parse_table[index]; 71 | uint16_t section_count = *(data++); 72 | for (unsigned i = 0; i < section_count; i++) { 73 | uint16_t section_value = *(data++); 74 | uint16_t symbol_count = *(data++); 75 | for (unsigned i = 0; i < symbol_count; i++) { 76 | if (*(data++) == symbol) return section_value; 77 | } 78 | } 79 | return 0; 80 | } else { 81 | return self->parse_table[state * self->symbol_count + symbol]; 82 | } 83 | } 84 | 85 | static inline TSStateId ts_language_next_state(const TSLanguage *self, 86 | TSStateId state, 87 | TSSymbol symbol) { 88 | if (symbol == ts_builtin_sym_error || symbol == ts_builtin_sym_error_repeat) { 89 | return 0; 90 | } else if (symbol < self->token_count) { 91 | uint32_t count; 92 | const TSParseAction *actions = ts_language_actions(self, state, symbol, &count); 93 | if (count > 0) { 94 | TSParseAction action = actions[count - 1]; 95 | if (action.type == TSParseActionTypeShift) { 96 | return action.params.extra ? state : action.params.state; 97 | } 98 | } 99 | return 0; 100 | } else { 101 | return ts_language_lookup(self, state, symbol); 102 | } 103 | } 104 | 105 | static inline const bool * 106 | ts_language_enabled_external_tokens(const TSLanguage *self, 107 | unsigned external_scanner_state) { 108 | if (external_scanner_state == 0) { 109 | return NULL; 110 | } else { 111 | return self->external_scanner.states + self->external_token_count * external_scanner_state; 112 | } 113 | } 114 | 115 | static inline const TSSymbol * 116 | ts_language_alias_sequence(const TSLanguage *self, uint32_t production_id) { 117 | return production_id > 0 ? 118 | self->alias_sequences + production_id * self->max_alias_sequence_length : 119 | NULL; 120 | } 121 | 122 | static inline void ts_language_field_map( 123 | const TSLanguage *self, 124 | uint32_t production_id, 125 | const TSFieldMapEntry **start, 126 | const TSFieldMapEntry **end 127 | ) { 128 | if (self->version < TREE_SITTER_LANGUAGE_VERSION_WITH_FIELDS || self->field_count == 0) { 129 | *start = NULL; 130 | *end = NULL; 131 | return; 132 | } 133 | 134 | TSFieldMapSlice slice = self->field_map_slices[production_id]; 135 | *start = &self->field_map_entries[slice.index]; 136 | *end = &self->field_map_entries[slice.index] + slice.length; 137 | } 138 | 139 | #ifdef __cplusplus 140 | } 141 | #endif 142 | 143 | #endif // TREE_SITTER_LANGUAGE_H_ 144 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/length.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_LENGTH_H_ 2 | #define TREE_SITTER_LENGTH_H_ 3 | 4 | #include 5 | #include 6 | #include "./point.h" 7 | #include "tree_sitter/api.h" 8 | 9 | typedef struct { 10 | uint32_t bytes; 11 | TSPoint extent; 12 | } Length; 13 | 14 | static const Length LENGTH_UNDEFINED = {0, {0, 1}}; 15 | static const Length LENGTH_MAX = {UINT32_MAX, {UINT32_MAX, UINT32_MAX}}; 16 | 17 | static inline bool length_is_undefined(Length length) { 18 | return length.bytes == 0 && length.extent.column != 0; 19 | } 20 | 21 | static inline Length length_min(Length len1, Length len2) { 22 | return (len1.bytes < len2.bytes) ? len1 : len2; 23 | } 24 | 25 | static inline Length length_add(Length len1, Length len2) { 26 | Length result; 27 | result.bytes = len1.bytes + len2.bytes; 28 | result.extent = point_add(len1.extent, len2.extent); 29 | return result; 30 | } 31 | 32 | static inline Length length_sub(Length len1, Length len2) { 33 | Length result; 34 | result.bytes = len1.bytes - len2.bytes; 35 | result.extent = point_sub(len1.extent, len2.extent); 36 | return result; 37 | } 38 | 39 | static inline Length length_zero(void) { 40 | Length result = {0, {0, 0}}; 41 | return result; 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/lexer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "./lexer.h" 3 | #include "./subtree.h" 4 | #include "./length.h" 5 | #include "./unicode.h" 6 | 7 | #define LOG(message, character) \ 8 | if (self->logger.log) { \ 9 | snprintf( \ 10 | self->debug_buffer, \ 11 | TREE_SITTER_SERIALIZATION_BUFFER_SIZE, \ 12 | 32 <= character && character < 127 ? \ 13 | message " character:'%c'" : \ 14 | message " character:%d", \ 15 | character \ 16 | ); \ 17 | self->logger.log( \ 18 | self->logger.payload, \ 19 | TSLogTypeLex, \ 20 | self->debug_buffer \ 21 | ); \ 22 | } 23 | 24 | static const int32_t BYTE_ORDER_MARK = 0xFEFF; 25 | 26 | static const TSRange DEFAULT_RANGE = { 27 | .start_point = { 28 | .row = 0, 29 | .column = 0, 30 | }, 31 | .end_point = { 32 | .row = UINT32_MAX, 33 | .column = UINT32_MAX, 34 | }, 35 | .start_byte = 0, 36 | .end_byte = UINT32_MAX 37 | }; 38 | 39 | // Check if the lexer has reached EOF. This state is stored 40 | // by setting the lexer's `current_included_range_index` such that 41 | // it has consumed all of its available ranges. 42 | static bool ts_lexer__eof(const TSLexer *_self) { 43 | Lexer *self = (Lexer *)_self; 44 | return self->current_included_range_index == self->included_range_count; 45 | } 46 | 47 | // Clear the currently stored chunk of source code, because the lexer's 48 | // position has changed. 49 | static void ts_lexer__clear_chunk(Lexer *self) { 50 | self->chunk = NULL; 51 | self->chunk_size = 0; 52 | self->chunk_start = 0; 53 | } 54 | 55 | // Call the lexer's input callback to obtain a new chunk of source code 56 | // for the current position. 57 | static void ts_lexer__get_chunk(Lexer *self) { 58 | self->chunk_start = self->current_position.bytes; 59 | self->chunk = self->input.read( 60 | self->input.payload, 61 | self->current_position.bytes, 62 | self->current_position.extent, 63 | &self->chunk_size 64 | ); 65 | if (!self->chunk_size) { 66 | self->current_included_range_index = self->included_range_count; 67 | self->chunk = NULL; 68 | } 69 | } 70 | 71 | // Decode the next unicode character in the current chunk of source code. 72 | // This assumes that the lexer has already retrieved a chunk of source 73 | // code that spans the current position. 74 | static void ts_lexer__get_lookahead(Lexer *self) { 75 | uint32_t position_in_chunk = self->current_position.bytes - self->chunk_start; 76 | const uint8_t *chunk = (const uint8_t *)self->chunk + position_in_chunk; 77 | uint32_t size = self->chunk_size - position_in_chunk; 78 | 79 | if (size == 0) { 80 | self->lookahead_size = 1; 81 | self->data.lookahead = '\0'; 82 | return; 83 | } 84 | 85 | UnicodeDecodeFunction decode = self->input.encoding == TSInputEncodingUTF8 86 | ? ts_decode_utf8 87 | : ts_decode_utf16; 88 | 89 | self->lookahead_size = decode(chunk, size, &self->data.lookahead); 90 | 91 | // If this chunk ended in the middle of a multi-byte character, 92 | // try again with a fresh chunk. 93 | if (self->data.lookahead == TS_DECODE_ERROR && size < 4) { 94 | ts_lexer__get_chunk(self); 95 | chunk = (const uint8_t *)self->chunk; 96 | size = self->chunk_size; 97 | self->lookahead_size = decode(chunk, size, &self->data.lookahead); 98 | } 99 | 100 | if (self->data.lookahead == TS_DECODE_ERROR) { 101 | self->lookahead_size = 1; 102 | } 103 | } 104 | 105 | // Advance to the next character in the source code, retrieving a new 106 | // chunk of source code if needed. 107 | static void ts_lexer__advance(TSLexer *_self, bool skip) { 108 | Lexer *self = (Lexer *)_self; 109 | if (!self->chunk) return; 110 | 111 | if (skip) { 112 | LOG("skip", self->data.lookahead); 113 | } else { 114 | LOG("consume", self->data.lookahead); 115 | } 116 | 117 | if (self->lookahead_size) { 118 | self->current_position.bytes += self->lookahead_size; 119 | if (self->data.lookahead == '\n') { 120 | self->current_position.extent.row++; 121 | self->current_position.extent.column = 0; 122 | } else { 123 | self->current_position.extent.column += self->lookahead_size; 124 | } 125 | } 126 | 127 | const TSRange *current_range = NULL; 128 | if (self->current_included_range_index < self->included_range_count) { 129 | current_range = &self->included_ranges[self->current_included_range_index]; 130 | if (self->current_position.bytes == current_range->end_byte) { 131 | self->current_included_range_index++; 132 | if (self->current_included_range_index < self->included_range_count) { 133 | current_range++; 134 | self->current_position = (Length) { 135 | current_range->start_byte, 136 | current_range->start_point, 137 | }; 138 | } else { 139 | current_range = NULL; 140 | } 141 | } 142 | } 143 | 144 | if (skip) self->token_start_position = self->current_position; 145 | 146 | if (current_range) { 147 | if (self->current_position.bytes >= self->chunk_start + self->chunk_size) { 148 | ts_lexer__get_chunk(self); 149 | } 150 | ts_lexer__get_lookahead(self); 151 | } else { 152 | ts_lexer__clear_chunk(self); 153 | self->data.lookahead = '\0'; 154 | self->lookahead_size = 1; 155 | } 156 | } 157 | 158 | // Mark that a token match has completed. This can be called multiple 159 | // times if a longer match is found later. 160 | static void ts_lexer__mark_end(TSLexer *_self) { 161 | Lexer *self = (Lexer *)_self; 162 | if (!ts_lexer__eof(&self->data)) { 163 | // If the lexer is right at the beginning of included range, 164 | // then the token should be considered to end at the *end* of the 165 | // previous included range, rather than here. 166 | TSRange *current_included_range = &self->included_ranges[ 167 | self->current_included_range_index 168 | ]; 169 | if ( 170 | self->current_included_range_index > 0 && 171 | self->current_position.bytes == current_included_range->start_byte 172 | ) { 173 | TSRange *previous_included_range = current_included_range - 1; 174 | self->token_end_position = (Length) { 175 | previous_included_range->end_byte, 176 | previous_included_range->end_point, 177 | }; 178 | return; 179 | } 180 | } 181 | self->token_end_position = self->current_position; 182 | } 183 | 184 | static uint32_t ts_lexer__get_column(TSLexer *_self) { 185 | Lexer *self = (Lexer *)_self; 186 | uint32_t goal_byte = self->current_position.bytes; 187 | 188 | self->current_position.bytes -= self->current_position.extent.column; 189 | self->current_position.extent.column = 0; 190 | 191 | if (self->current_position.bytes < self->chunk_start) { 192 | ts_lexer__get_chunk(self); 193 | } 194 | 195 | uint32_t result = 0; 196 | while (self->current_position.bytes < goal_byte) { 197 | ts_lexer__advance(&self->data, false); 198 | result++; 199 | } 200 | 201 | return result; 202 | } 203 | 204 | // Is the lexer at a boundary between two disjoint included ranges of 205 | // source code? This is exposed as an API because some languages' external 206 | // scanners need to perform custom actions at these bounaries. 207 | static bool ts_lexer__is_at_included_range_start(const TSLexer *_self) { 208 | const Lexer *self = (const Lexer *)_self; 209 | if (self->current_included_range_index < self->included_range_count) { 210 | TSRange *current_range = &self->included_ranges[self->current_included_range_index]; 211 | return self->current_position.bytes == current_range->start_byte; 212 | } else { 213 | return false; 214 | } 215 | } 216 | 217 | void ts_lexer_init(Lexer *self) { 218 | *self = (Lexer) { 219 | .data = { 220 | // The lexer's methods are stored as struct fields so that generated 221 | // parsers can call them without needing to be linked against this 222 | // library. 223 | .advance = ts_lexer__advance, 224 | .mark_end = ts_lexer__mark_end, 225 | .get_column = ts_lexer__get_column, 226 | .is_at_included_range_start = ts_lexer__is_at_included_range_start, 227 | .eof = ts_lexer__eof, 228 | .lookahead = 0, 229 | .result_symbol = 0, 230 | }, 231 | .chunk = NULL, 232 | .chunk_size = 0, 233 | .chunk_start = 0, 234 | .current_position = {0, {0, 0}}, 235 | .logger = { 236 | .payload = NULL, 237 | .log = NULL 238 | }, 239 | .included_ranges = NULL, 240 | .included_range_count = 0, 241 | .current_included_range_index = 0, 242 | }; 243 | ts_lexer_set_included_ranges(self, NULL, 0); 244 | } 245 | 246 | void ts_lexer_delete(Lexer *self) { 247 | ts_free(self->included_ranges); 248 | } 249 | 250 | static void ts_lexer_goto(Lexer *self, Length position) { 251 | self->current_position = position; 252 | bool found_included_range = false; 253 | 254 | // Move to the first valid position at or after the given position. 255 | for (unsigned i = 0; i < self->included_range_count; i++) { 256 | TSRange *included_range = &self->included_ranges[i]; 257 | if (included_range->end_byte > position.bytes) { 258 | if (included_range->start_byte > position.bytes) { 259 | self->current_position = (Length) { 260 | .bytes = included_range->start_byte, 261 | .extent = included_range->start_point, 262 | }; 263 | } 264 | 265 | self->current_included_range_index = i; 266 | found_included_range = true; 267 | break; 268 | } 269 | } 270 | 271 | if (found_included_range) { 272 | // If the current position is outside of the current chunk of text, 273 | // then clear out the current chunk of text. 274 | if (self->chunk && ( 275 | position.bytes < self->chunk_start || 276 | position.bytes >= self->chunk_start + self->chunk_size 277 | )) { 278 | ts_lexer__clear_chunk(self); 279 | } 280 | 281 | self->lookahead_size = 0; 282 | self->data.lookahead = '\0'; 283 | } 284 | 285 | // If the given position is beyond any of included ranges, move to the EOF 286 | // state - past the end of the included ranges. 287 | else { 288 | self->current_included_range_index = self->included_range_count; 289 | TSRange *last_included_range = &self->included_ranges[self->included_range_count - 1]; 290 | self->current_position = (Length) { 291 | .bytes = last_included_range->end_byte, 292 | .extent = last_included_range->end_point, 293 | }; 294 | ts_lexer__clear_chunk(self); 295 | self->lookahead_size = 1; 296 | self->data.lookahead = '\0'; 297 | } 298 | } 299 | 300 | void ts_lexer_set_input(Lexer *self, TSInput input) { 301 | self->input = input; 302 | ts_lexer__clear_chunk(self); 303 | ts_lexer_goto(self, self->current_position); 304 | } 305 | 306 | // Move the lexer to the given position. This doesn't do any work 307 | // if the parser is already at the given position. 308 | void ts_lexer_reset(Lexer *self, Length position) { 309 | if (position.bytes != self->current_position.bytes) { 310 | ts_lexer_goto(self, position); 311 | } 312 | } 313 | 314 | void ts_lexer_start(Lexer *self) { 315 | self->token_start_position = self->current_position; 316 | self->token_end_position = LENGTH_UNDEFINED; 317 | self->data.result_symbol = 0; 318 | if (!ts_lexer__eof(&self->data)) { 319 | if (!self->chunk_size) ts_lexer__get_chunk(self); 320 | if (!self->lookahead_size) ts_lexer__get_lookahead(self); 321 | if ( 322 | self->current_position.bytes == 0 && 323 | self->data.lookahead == BYTE_ORDER_MARK 324 | ) ts_lexer__advance(&self->data, true); 325 | } 326 | } 327 | 328 | void ts_lexer_finish(Lexer *self, uint32_t *lookahead_end_byte) { 329 | if (length_is_undefined(self->token_end_position)) { 330 | ts_lexer__mark_end(&self->data); 331 | } 332 | 333 | uint32_t current_lookahead_end_byte = self->current_position.bytes + 1; 334 | 335 | // In order to determine that a byte sequence is invalid UTF8 or UTF16, 336 | // the character decoding algorithm may have looked at the following byte. 337 | // Therefore, the next byte *after* the current (invalid) character 338 | // affects the interpretation of the current character. 339 | if (self->data.lookahead == TS_DECODE_ERROR) { 340 | current_lookahead_end_byte++; 341 | } 342 | 343 | if (current_lookahead_end_byte > *lookahead_end_byte) { 344 | *lookahead_end_byte = current_lookahead_end_byte; 345 | } 346 | } 347 | 348 | void ts_lexer_advance_to_end(Lexer *self) { 349 | while (self->chunk) { 350 | ts_lexer__advance(&self->data, false); 351 | } 352 | } 353 | 354 | void ts_lexer_mark_end(Lexer *self) { 355 | ts_lexer__mark_end(&self->data); 356 | } 357 | 358 | bool ts_lexer_set_included_ranges( 359 | Lexer *self, 360 | const TSRange *ranges, 361 | uint32_t count 362 | ) { 363 | if (count == 0 || !ranges) { 364 | ranges = &DEFAULT_RANGE; 365 | count = 1; 366 | } else { 367 | uint32_t previous_byte = 0; 368 | for (unsigned i = 0; i < count; i++) { 369 | const TSRange *range = &ranges[i]; 370 | if ( 371 | range->start_byte < previous_byte || 372 | range->end_byte < range->start_byte 373 | ) return false; 374 | previous_byte = range->end_byte; 375 | } 376 | } 377 | 378 | size_t size = count * sizeof(TSRange); 379 | self->included_ranges = ts_realloc(self->included_ranges, size); 380 | memcpy(self->included_ranges, ranges, size); 381 | self->included_range_count = count; 382 | ts_lexer_goto(self, self->current_position); 383 | return true; 384 | } 385 | 386 | TSRange *ts_lexer_included_ranges(const Lexer *self, uint32_t *count) { 387 | *count = self->included_range_count; 388 | return self->included_ranges; 389 | } 390 | 391 | #undef LOG 392 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_LEXER_H_ 2 | #define TREE_SITTER_LEXER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./length.h" 9 | #include "./subtree.h" 10 | #include "tree_sitter/api.h" 11 | #include "tree_sitter/parser.h" 12 | 13 | typedef struct { 14 | TSLexer data; 15 | Length current_position; 16 | Length token_start_position; 17 | Length token_end_position; 18 | 19 | TSRange *included_ranges; 20 | size_t included_range_count; 21 | size_t current_included_range_index; 22 | 23 | const char *chunk; 24 | uint32_t chunk_start; 25 | uint32_t chunk_size; 26 | uint32_t lookahead_size; 27 | 28 | TSInput input; 29 | TSLogger logger; 30 | char debug_buffer[TREE_SITTER_SERIALIZATION_BUFFER_SIZE]; 31 | } Lexer; 32 | 33 | void ts_lexer_init(Lexer *); 34 | void ts_lexer_delete(Lexer *); 35 | void ts_lexer_set_input(Lexer *, TSInput); 36 | void ts_lexer_reset(Lexer *, Length); 37 | void ts_lexer_start(Lexer *); 38 | void ts_lexer_finish(Lexer *, uint32_t *); 39 | void ts_lexer_advance_to_end(Lexer *); 40 | void ts_lexer_mark_end(Lexer *); 41 | bool ts_lexer_set_included_ranges(Lexer *self, const TSRange *ranges, uint32_t count); 42 | TSRange *ts_lexer_included_ranges(const Lexer *self, uint32_t *count); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | 48 | #endif // TREE_SITTER_LEXER_H_ 49 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/lib.c: -------------------------------------------------------------------------------- 1 | // The Tree-sitter library can be built by compiling this one source file. 2 | // 3 | // The following directories must be added to the include path: 4 | // - include 5 | 6 | #define _POSIX_C_SOURCE 200112L 7 | 8 | #include "./get_changed_ranges.c" 9 | #include "./language.c" 10 | #include "./lexer.c" 11 | #include "./node.c" 12 | #include "./parser.c" 13 | #include "./query.c" 14 | #include "./stack.c" 15 | #include "./subtree.c" 16 | #include "./tree_cursor.c" 17 | #include "./tree.c" 18 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/point.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_POINT_H_ 2 | #define TREE_SITTER_POINT_H_ 3 | 4 | #include "tree_sitter/api.h" 5 | 6 | #define POINT_ZERO ((TSPoint) {0, 0}) 7 | #define POINT_MAX ((TSPoint) {UINT32_MAX, UINT32_MAX}) 8 | 9 | static inline TSPoint point__new(unsigned row, unsigned column) { 10 | TSPoint result = {row, column}; 11 | return result; 12 | } 13 | 14 | static inline TSPoint point_add(TSPoint a, TSPoint b) { 15 | if (b.row > 0) 16 | return point__new(a.row + b.row, b.column); 17 | else 18 | return point__new(a.row, a.column + b.column); 19 | } 20 | 21 | static inline TSPoint point_sub(TSPoint a, TSPoint b) { 22 | if (a.row > b.row) 23 | return point__new(a.row - b.row, a.column); 24 | else 25 | return point__new(0, a.column - b.column); 26 | } 27 | 28 | static inline bool point_lte(TSPoint a, TSPoint b) { 29 | return (a.row < b.row) || (a.row == b.row && a.column <= b.column); 30 | } 31 | 32 | static inline bool point_lt(TSPoint a, TSPoint b) { 33 | return (a.row < b.row) || (a.row == b.row && a.column < b.column); 34 | } 35 | 36 | static inline bool point_eq(TSPoint a, TSPoint b) { 37 | return a.row == b.row && a.column == b.column; 38 | } 39 | 40 | static inline TSPoint point_min(TSPoint a, TSPoint b) { 41 | if (a.row < b.row || (a.row == b.row && a.column < b.column)) 42 | return a; 43 | else 44 | return b; 45 | } 46 | 47 | static inline TSPoint point_max(TSPoint a, TSPoint b) { 48 | if (a.row > b.row || (a.row == b.row && a.column > b.column)) 49 | return a; 50 | else 51 | return b; 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/reduce_action.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_REDUCE_ACTION_H_ 2 | #define TREE_SITTER_REDUCE_ACTION_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./array.h" 9 | #include "tree_sitter/api.h" 10 | 11 | typedef struct { 12 | uint32_t count; 13 | TSSymbol symbol; 14 | int dynamic_precedence; 15 | unsigned short production_id; 16 | } ReduceAction; 17 | 18 | typedef Array(ReduceAction) ReduceActionSet; 19 | 20 | static inline void ts_reduce_action_set_add(ReduceActionSet *self, 21 | ReduceAction new_action) { 22 | for (uint32_t i = 0; i < self->size; i++) { 23 | ReduceAction action = self->contents[i]; 24 | if (action.symbol == new_action.symbol && action.count == new_action.count) 25 | return; 26 | } 27 | array_push(self, new_action); 28 | } 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | #endif // TREE_SITTER_REDUCE_ACTION_H_ 35 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/reusable_node.h: -------------------------------------------------------------------------------- 1 | #include "./subtree.h" 2 | 3 | typedef struct { 4 | Subtree tree; 5 | uint32_t child_index; 6 | uint32_t byte_offset; 7 | } StackEntry; 8 | 9 | typedef struct { 10 | Array(StackEntry) stack; 11 | Subtree last_external_token; 12 | } ReusableNode; 13 | 14 | static inline ReusableNode reusable_node_new(void) { 15 | return (ReusableNode) {array_new(), NULL_SUBTREE}; 16 | } 17 | 18 | static inline void reusable_node_clear(ReusableNode *self) { 19 | array_clear(&self->stack); 20 | self->last_external_token = NULL_SUBTREE; 21 | } 22 | 23 | static inline void reusable_node_reset(ReusableNode *self, Subtree tree) { 24 | reusable_node_clear(self); 25 | array_push(&self->stack, ((StackEntry) { 26 | .tree = tree, 27 | .child_index = 0, 28 | .byte_offset = 0, 29 | })); 30 | } 31 | 32 | static inline Subtree reusable_node_tree(ReusableNode *self) { 33 | return self->stack.size > 0 34 | ? self->stack.contents[self->stack.size - 1].tree 35 | : NULL_SUBTREE; 36 | } 37 | 38 | static inline uint32_t reusable_node_byte_offset(ReusableNode *self) { 39 | return self->stack.size > 0 40 | ? self->stack.contents[self->stack.size - 1].byte_offset 41 | : UINT32_MAX; 42 | } 43 | 44 | static inline void reusable_node_delete(ReusableNode *self) { 45 | array_delete(&self->stack); 46 | } 47 | 48 | static inline void reusable_node_advance(ReusableNode *self) { 49 | StackEntry last_entry = *array_back(&self->stack); 50 | uint32_t byte_offset = last_entry.byte_offset + ts_subtree_total_bytes(last_entry.tree); 51 | if (ts_subtree_has_external_tokens(last_entry.tree)) { 52 | self->last_external_token = ts_subtree_last_external_token(last_entry.tree); 53 | } 54 | 55 | Subtree tree; 56 | uint32_t next_index; 57 | do { 58 | StackEntry popped_entry = array_pop(&self->stack); 59 | next_index = popped_entry.child_index + 1; 60 | if (self->stack.size == 0) return; 61 | tree = array_back(&self->stack)->tree; 62 | } while (ts_subtree_child_count(tree) <= next_index); 63 | 64 | array_push(&self->stack, ((StackEntry) { 65 | .tree = tree.ptr->children[next_index], 66 | .child_index = next_index, 67 | .byte_offset = byte_offset, 68 | })); 69 | } 70 | 71 | static inline bool reusable_node_descend(ReusableNode *self) { 72 | StackEntry last_entry = *array_back(&self->stack); 73 | if (ts_subtree_child_count(last_entry.tree) > 0) { 74 | array_push(&self->stack, ((StackEntry) { 75 | .tree = last_entry.tree.ptr->children[0], 76 | .child_index = 0, 77 | .byte_offset = last_entry.byte_offset, 78 | })); 79 | return true; 80 | } else { 81 | return false; 82 | } 83 | } 84 | 85 | static inline void reusable_node_advance_past_leaf(ReusableNode *self) { 86 | while (reusable_node_descend(self)) {} 87 | reusable_node_advance(self); 88 | } 89 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/scanner_javascript.c: -------------------------------------------------------------------------------- 1 | #include "tree_sitter/parser.h" 2 | #include 3 | 4 | enum TokenType { 5 | AUTOMATIC_SEMICOLON, 6 | TEMPLATE_CHARS 7 | }; 8 | 9 | void *tree_sitter_javascript_external_scanner_create() { return NULL; } 10 | void tree_sitter_javascript_external_scanner_destroy(void *p) {} 11 | void tree_sitter_javascript_external_scanner_reset(void *p) {} 12 | unsigned tree_sitter_javascript_external_scanner_serialize(void *p, char *buffer) { return 0; } 13 | void tree_sitter_javascript_external_scanner_deserialize(void *p, const char *b, unsigned n) {} 14 | 15 | static void advance(TSLexer *lexer) { lexer->advance(lexer, false); } 16 | 17 | static bool scan_whitespace_and_comments(TSLexer *lexer) { 18 | for (;;) { 19 | while (iswspace(lexer->lookahead)) { 20 | advance(lexer); 21 | } 22 | 23 | if (lexer->lookahead == '/') { 24 | advance(lexer); 25 | 26 | if (lexer->lookahead == '/') { 27 | advance(lexer); 28 | while (lexer->lookahead != 0 && lexer->lookahead != '\n') { 29 | advance(lexer); 30 | } 31 | } else if (lexer->lookahead == '*') { 32 | advance(lexer); 33 | while (lexer->lookahead != 0) { 34 | if (lexer->lookahead == '*') { 35 | advance(lexer); 36 | if (lexer->lookahead == '/') { 37 | advance(lexer); 38 | break; 39 | } 40 | } else { 41 | advance(lexer); 42 | } 43 | } 44 | } else { 45 | return false; 46 | } 47 | } else { 48 | return true; 49 | } 50 | } 51 | } 52 | 53 | bool tree_sitter_javascript_external_scanner_scan(void *payload, TSLexer *lexer, 54 | const bool *valid_symbols) { 55 | if (valid_symbols[TEMPLATE_CHARS]) { 56 | if (valid_symbols[AUTOMATIC_SEMICOLON]) return false; 57 | lexer->result_symbol = TEMPLATE_CHARS; 58 | for (bool has_content = false;; has_content = true) { 59 | lexer->mark_end(lexer); 60 | switch (lexer->lookahead) { 61 | case '`': 62 | return has_content; 63 | case '\0': 64 | return false; 65 | case '$': 66 | advance(lexer); 67 | if (lexer->lookahead == '{') return has_content; 68 | break; 69 | case '\\': 70 | return has_content; 71 | default: 72 | advance(lexer); 73 | } 74 | } 75 | } else { 76 | lexer->result_symbol = AUTOMATIC_SEMICOLON; 77 | lexer->mark_end(lexer); 78 | 79 | for (;;) { 80 | if (lexer->lookahead == 0) return true; 81 | if (lexer->lookahead == '}') return true; 82 | if (lexer->is_at_included_range_start(lexer)) return true; 83 | if (!iswspace(lexer->lookahead)) return false; 84 | if (lexer->lookahead == '\n') break; 85 | advance(lexer); 86 | } 87 | 88 | advance(lexer); 89 | 90 | if (!scan_whitespace_and_comments(lexer)) return false; 91 | 92 | switch (lexer->lookahead) { 93 | case ',': 94 | case '.': 95 | case ':': 96 | case ';': 97 | case '*': 98 | case '%': 99 | case '>': 100 | case '<': 101 | case '=': 102 | case '[': 103 | case '(': 104 | case '?': 105 | case '^': 106 | case '|': 107 | case '&': 108 | case '/': 109 | return false; 110 | 111 | // Insert a semicolon before `--` and `++`, but not before binary `+` or `-`. 112 | case '+': 113 | advance(lexer); 114 | return lexer->lookahead == '+'; 115 | case '-': 116 | advance(lexer); 117 | return lexer->lookahead == '-'; 118 | 119 | // Don't insert a semicolon before `!=`, but do insert one before a unary `!`. 120 | case '!': 121 | advance(lexer); 122 | return lexer->lookahead != '='; 123 | 124 | // Don't insert a semicolon before `in` or `instanceof`, but do insert one 125 | // before an identifier. 126 | case 'i': 127 | advance(lexer); 128 | 129 | if (lexer->lookahead != 'n') return true; 130 | advance(lexer); 131 | 132 | if (!iswalpha(lexer->lookahead)) return false; 133 | 134 | for (unsigned i = 0; i < 8; i++) { 135 | if (lexer->lookahead != "stanceof"[i]) return true; 136 | advance(lexer); 137 | } 138 | 139 | if (!iswalpha(lexer->lookahead)) return false; 140 | break; 141 | } 142 | 143 | return true; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/scanner_nim.c: -------------------------------------------------------------------------------- 1 | #include "tree_sitter/parser.h" 2 | 3 | void *tree_sitter_python_external_scanner_create(void); 4 | void tree_sitter_python_external_scanner_destroy(void *); 5 | bool tree_sitter_python_external_scanner_scan(void *, TSLexer *, const bool *); 6 | unsigned tree_sitter_python_external_scanner_serialize(void *, char *); 7 | void tree_sitter_python_external_scanner_deserialize(void *, const char *, unsigned); 8 | 9 | void *tree_sitter_nim_external_scanner_create() { 10 | return tree_sitter_python_external_scanner_create(); 11 | } 12 | 13 | bool tree_sitter_nim_external_scanner_scan(void *payload, TSLexer *lexer, 14 | const bool *valid_symbols) { 15 | return tree_sitter_python_external_scanner_scan(payload, lexer, valid_symbols); 16 | } 17 | 18 | unsigned tree_sitter_nim_external_scanner_serialize(void *payload, char *buffer) { 19 | return tree_sitter_python_external_scanner_serialize(payload, buffer); 20 | } 21 | 22 | void tree_sitter_nim_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) { 23 | tree_sitter_python_external_scanner_deserialize(payload, buffer, length); 24 | } 25 | 26 | void tree_sitter_nim_external_scanner_destroy(void *payload) { 27 | tree_sitter_python_external_scanner_destroy(payload); 28 | } 29 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/scanner_python.c: -------------------------------------------------------------------------------- 1 | #include "tree_sitter/parser.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define STB_DS_IMPLEMENTATION 8 | #include "stb_ds.h" 9 | 10 | struct Scanner { 11 | uint16_t *indent_length_stack; 12 | char *delimiter_stack; 13 | }; 14 | 15 | enum TokenType { 16 | NEWLINE, 17 | INDENT, 18 | DEDENT, 19 | STRING_START, 20 | STRING_CONTENT, 21 | STRING_END, 22 | }; 23 | 24 | enum { 25 | SingleQuote = 1 << 0, 26 | DoubleQuote = 1 << 1, 27 | BackQuote = 1 << 2, 28 | Raw = 1 << 3, 29 | Format = 1 << 4, 30 | Triple = 1 << 5, 31 | Bytes = 1 << 6, 32 | }; 33 | 34 | bool is_format(char *flags) { 35 | return *flags & Format; 36 | } 37 | 38 | bool is_raw(char *flags) { 39 | return *flags & Raw; 40 | } 41 | 42 | bool is_triple(char *flags) { 43 | return *flags & Triple; 44 | } 45 | 46 | bool is_bytes(char *flags) { 47 | return *flags & Bytes; 48 | } 49 | 50 | int32_t end_character(char *flags) { 51 | if (*flags & SingleQuote) return '\''; 52 | if (*flags & DoubleQuote) return '"'; 53 | if (*flags & BackQuote) return '`'; 54 | return 0; 55 | } 56 | 57 | void set_format(char *flags) { 58 | *flags |= Format; 59 | } 60 | 61 | void set_raw(char *flags) { 62 | *flags |= Raw; 63 | } 64 | 65 | void set_triple(char *flags) { 66 | *flags |= Triple; 67 | } 68 | 69 | void set_bytes(char *flags) { 70 | *flags |= Bytes; 71 | } 72 | 73 | void set_end_character(char *flags, int32_t character) { 74 | switch (character) { 75 | case '\'': 76 | *flags |= SingleQuote; 77 | break; 78 | case '"': 79 | *flags |= DoubleQuote; 80 | break; 81 | case '`': 82 | *flags |= BackQuote; 83 | break; 84 | default: 85 | assert(false); 86 | } 87 | } 88 | 89 | void advance(TSLexer *lexer) { 90 | lexer->advance(lexer, false); 91 | } 92 | 93 | void skip(TSLexer *lexer) { 94 | lexer->advance(lexer, true); 95 | } 96 | 97 | bool scan(struct Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) { 98 | if (valid_symbols[STRING_CONTENT] && !valid_symbols[INDENT] && arrlen(scanner->delimiter_stack) != 0) { 99 | char delimiter = arrlast(scanner->delimiter_stack); 100 | int32_t end_char = end_character(&delimiter); 101 | bool has_content = false; 102 | while (lexer->lookahead) { 103 | if (lexer->lookahead == '{' && is_format(&delimiter)) { 104 | lexer->mark_end(lexer); 105 | lexer->advance(lexer, false); 106 | if (lexer->lookahead == '{') { 107 | lexer->advance(lexer, false); 108 | } else { 109 | lexer->result_symbol = STRING_CONTENT; 110 | return has_content; 111 | } 112 | } else if (lexer->lookahead == '\\') { 113 | if (is_raw(&delimiter)) { 114 | lexer->advance(lexer, false); 115 | } else if (is_bytes(&delimiter)) { 116 | lexer->mark_end(lexer); 117 | lexer->advance(lexer, false); 118 | if (lexer->lookahead == 'N' || lexer->lookahead == 'u' || lexer->lookahead == 'U') { 119 | // In bytes string, \N{...}, \uXXXX and \UXXXXXXXX are not escape sequences 120 | // https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals 121 | lexer->advance(lexer, false); 122 | } else { 123 | lexer->result_symbol = STRING_CONTENT; 124 | return has_content; 125 | } 126 | } else { 127 | lexer->mark_end(lexer); 128 | lexer->result_symbol = STRING_CONTENT; 129 | return has_content; 130 | } 131 | } else if (lexer->lookahead == end_char) { 132 | if (is_triple(&delimiter)) { 133 | lexer->mark_end(lexer); 134 | lexer->advance(lexer, false); 135 | if (lexer->lookahead == end_char) { 136 | lexer->advance(lexer, false); 137 | if (lexer->lookahead == end_char) { 138 | if (has_content) { 139 | lexer->result_symbol = STRING_CONTENT; 140 | } else { 141 | lexer->advance(lexer, false); 142 | lexer->mark_end(lexer); 143 | arrpop(scanner->delimiter_stack); 144 | lexer->result_symbol = STRING_END; 145 | } 146 | return true; 147 | } 148 | } 149 | } else { 150 | if (has_content) { 151 | lexer->result_symbol = STRING_CONTENT; 152 | } else { 153 | lexer->advance(lexer, false); 154 | arrpop(scanner->delimiter_stack); 155 | lexer->result_symbol = STRING_END; 156 | } 157 | lexer->mark_end(lexer); 158 | return true; 159 | } 160 | } else if (lexer->lookahead == '\n' && has_content && !is_triple(&delimiter)) { 161 | return false; 162 | } 163 | advance(lexer); 164 | has_content = true; 165 | } 166 | } 167 | 168 | lexer->mark_end(lexer); 169 | 170 | bool has_comment = false; 171 | bool has_newline = false; 172 | uint32_t indent_length = 0; 173 | for (;;) { 174 | if (lexer->lookahead == '\n') { 175 | has_newline = true; 176 | indent_length = 0; 177 | skip(lexer); 178 | } else if (lexer->lookahead == ' ') { 179 | indent_length++; 180 | skip(lexer); 181 | } else if (lexer->lookahead == '\r') { 182 | indent_length = 0; 183 | skip(lexer); 184 | } else if (lexer->lookahead == '\t') { 185 | indent_length += 8; 186 | skip(lexer); 187 | } else if (lexer->lookahead == '#') { 188 | has_comment = true; 189 | while (lexer->lookahead && lexer->lookahead != '\n') skip(lexer); 190 | skip(lexer); 191 | indent_length = 0; 192 | } else if (lexer->lookahead == '\\') { 193 | skip(lexer); 194 | if (iswspace(lexer->lookahead)) { 195 | skip(lexer); 196 | } else { 197 | return false; 198 | } 199 | } else if (lexer->lookahead == '\f') { 200 | indent_length = 0; 201 | skip(lexer); 202 | } else if (lexer->lookahead == 0) { 203 | if (valid_symbols[DEDENT] && arrlen(scanner->indent_length_stack) > 1) { 204 | arrpop(scanner->indent_length_stack); 205 | lexer->result_symbol = DEDENT; 206 | return true; 207 | } 208 | 209 | if (valid_symbols[NEWLINE]) { 210 | lexer->result_symbol = NEWLINE; 211 | return true; 212 | } 213 | 214 | break; 215 | } else { 216 | break; 217 | } 218 | } 219 | 220 | if (has_newline) { 221 | if (indent_length > arrlast(scanner->indent_length_stack) && valid_symbols[INDENT]) { 222 | arrput(scanner->indent_length_stack, indent_length); 223 | lexer->result_symbol = INDENT; 224 | return true; 225 | } 226 | 227 | if (indent_length < arrlast(scanner->indent_length_stack) && valid_symbols[DEDENT]) { 228 | arrpop(scanner->indent_length_stack); 229 | lexer->result_symbol = DEDENT; 230 | return true; 231 | } 232 | 233 | if (valid_symbols[NEWLINE]) { 234 | lexer->result_symbol = NEWLINE; 235 | return true; 236 | } 237 | } 238 | 239 | if (!has_comment && valid_symbols[STRING_START]) { 240 | char delimiter = 0; 241 | 242 | bool has_flags = false; 243 | while (lexer->lookahead) { 244 | if (lexer->lookahead == 'f' || lexer->lookahead == 'F') { 245 | set_format(&delimiter); 246 | } else if (lexer->lookahead == 'r' || lexer->lookahead == 'R') { 247 | set_raw(&delimiter); 248 | } else if (lexer->lookahead == 'b' || lexer->lookahead == 'B') { 249 | set_bytes(&delimiter); 250 | } else if (lexer->lookahead != 'u' && lexer->lookahead != 'U') { 251 | break; 252 | } 253 | has_flags = true; 254 | advance(lexer); 255 | } 256 | 257 | if (lexer->lookahead == '`') { 258 | set_end_character(&delimiter, '`'); 259 | advance(lexer); 260 | lexer->mark_end(lexer); 261 | } else if (lexer->lookahead == '\'') { 262 | set_end_character(&delimiter, '\''); 263 | advance(lexer); 264 | lexer->mark_end(lexer); 265 | if (lexer->lookahead == '\'') { 266 | advance(lexer); 267 | if (lexer->lookahead == '\'') { 268 | advance(lexer); 269 | lexer->mark_end(lexer); 270 | set_triple(&delimiter); 271 | } 272 | } 273 | } else if (lexer->lookahead == '"') { 274 | set_end_character(&delimiter, '"'); 275 | advance(lexer); 276 | lexer->mark_end(lexer); 277 | if (lexer->lookahead == '"') { 278 | advance(lexer); 279 | if (lexer->lookahead == '"') { 280 | advance(lexer); 281 | lexer->mark_end(lexer); 282 | set_triple(&delimiter); 283 | } 284 | } 285 | } 286 | 287 | if (end_character(&delimiter)) { 288 | arrput(scanner->delimiter_stack, delimiter); 289 | lexer->result_symbol = STRING_START; 290 | return true; 291 | } else if (has_flags) { 292 | return false; 293 | } 294 | } 295 | 296 | return false; 297 | } 298 | 299 | unsigned serialize(struct Scanner *scanner, char *buffer) { 300 | size_t i = 0; 301 | 302 | size_t stack_size = arrlen(scanner->delimiter_stack); 303 | if (stack_size > UINT8_MAX) stack_size = UINT8_MAX; 304 | buffer[i++] = stack_size; 305 | 306 | memcpy(&buffer[i], scanner->delimiter_stack, stack_size); 307 | i += stack_size; 308 | 309 | for (int iter = 1; iter != arrlen(scanner->indent_length_stack) && i < TREE_SITTER_SERIALIZATION_BUFFER_SIZE; ++iter) { 310 | buffer[i++] = scanner->indent_length_stack[iter]; 311 | } 312 | 313 | return i; 314 | } 315 | 316 | void deserialize(struct Scanner *scanner, const char *buffer, unsigned length) { 317 | arrfree(scanner->delimiter_stack); 318 | arrfree(scanner->indent_length_stack); 319 | arrput(scanner->indent_length_stack, 0); 320 | 321 | if (length > 0) { 322 | size_t i = 0; 323 | 324 | size_t delimiter_count = (uint8_t)buffer[i++]; 325 | arrsetlen(scanner->delimiter_stack, delimiter_count); 326 | memcpy(scanner->delimiter_stack, &buffer[i], delimiter_count); 327 | i += delimiter_count; 328 | 329 | for (; i < length; i++) { 330 | arrput(scanner->indent_length_stack, buffer[i]); 331 | } 332 | } 333 | } 334 | 335 | void init_scanner(struct Scanner *scanner) { 336 | deserialize(scanner, NULL, 0); 337 | } 338 | 339 | void *tree_sitter_python_external_scanner_create() { 340 | void *scanner = calloc(1, sizeof(struct Scanner)); 341 | init_scanner((struct Scanner*) scanner); 342 | return scanner; 343 | } 344 | 345 | bool tree_sitter_python_external_scanner_scan(void *payload, TSLexer *lexer, 346 | const bool *valid_symbols) { 347 | return scan((struct Scanner*) payload, lexer, valid_symbols); 348 | } 349 | 350 | unsigned tree_sitter_python_external_scanner_serialize(void *payload, char *buffer) { 351 | return serialize((struct Scanner*) payload, buffer); 352 | } 353 | 354 | void tree_sitter_python_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) { 355 | deserialize((struct Scanner*) payload, buffer, length); 356 | } 357 | 358 | void tree_sitter_python_external_scanner_destroy(void *payload) { 359 | struct Scanner *scanner = (struct Scanner*) payload; 360 | arrfree(scanner->indent_length_stack); 361 | arrfree(scanner->delimiter_stack); 362 | free(scanner); 363 | } 364 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/stack.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_PARSE_STACK_H_ 2 | #define TREE_SITTER_PARSE_STACK_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./array.h" 9 | #include "./subtree.h" 10 | #include "./error_costs.h" 11 | #include 12 | 13 | typedef struct Stack Stack; 14 | 15 | typedef unsigned StackVersion; 16 | #define STACK_VERSION_NONE ((StackVersion)-1) 17 | 18 | typedef struct { 19 | SubtreeArray subtrees; 20 | StackVersion version; 21 | } StackSlice; 22 | typedef Array(StackSlice) StackSliceArray; 23 | 24 | typedef struct { 25 | Length position; 26 | unsigned depth; 27 | TSStateId state; 28 | } StackSummaryEntry; 29 | typedef Array(StackSummaryEntry) StackSummary; 30 | 31 | // Create a stack. 32 | Stack *ts_stack_new(SubtreePool *); 33 | 34 | // Release the memory reserved for a given stack. 35 | void ts_stack_delete(Stack *); 36 | 37 | // Get the stack's current number of versions. 38 | uint32_t ts_stack_version_count(const Stack *); 39 | 40 | // Get the state at the top of the given version of the stack. If the stack is 41 | // empty, this returns the initial state, 0. 42 | TSStateId ts_stack_state(const Stack *, StackVersion); 43 | 44 | // Get the last external token associated with a given version of the stack. 45 | Subtree ts_stack_last_external_token(const Stack *, StackVersion); 46 | 47 | // Set the last external token associated with a given version of the stack. 48 | void ts_stack_set_last_external_token(Stack *, StackVersion, Subtree ); 49 | 50 | // Get the position of the given version of the stack within the document. 51 | Length ts_stack_position(const Stack *, StackVersion); 52 | 53 | // Push a tree and state onto the given version of the stack. 54 | // 55 | // This transfers ownership of the tree to the Stack. Callers that 56 | // need to retain ownership of the tree for their own purposes should 57 | // first retain the tree. 58 | void ts_stack_push(Stack *, StackVersion, Subtree , bool, TSStateId); 59 | 60 | // Pop the given number of entries from the given version of the stack. This 61 | // operation can increase the number of stack versions by revealing multiple 62 | // versions which had previously been merged. It returns an array that 63 | // specifies the index of each revealed version and the trees that were 64 | // removed from that version. 65 | StackSliceArray ts_stack_pop_count(Stack *, StackVersion, uint32_t count); 66 | 67 | // Remove an error at the top of the given version of the stack. 68 | SubtreeArray ts_stack_pop_error(Stack *, StackVersion); 69 | 70 | // Remove any pending trees from the top of the given version of the stack. 71 | StackSliceArray ts_stack_pop_pending(Stack *, StackVersion); 72 | 73 | // Remove any all trees from the given version of the stack. 74 | StackSliceArray ts_stack_pop_all(Stack *, StackVersion); 75 | 76 | // Get the maximum number of tree nodes reachable from this version of the stack 77 | // since the last error was detected. 78 | unsigned ts_stack_node_count_since_error(const Stack *, StackVersion); 79 | 80 | int ts_stack_dynamic_precedence(Stack *, StackVersion); 81 | 82 | bool ts_stack_has_advanced_since_error(const Stack *, StackVersion); 83 | 84 | // Compute a summary of all the parse states near the top of the given 85 | // version of the stack and store the summary for later retrieval. 86 | void ts_stack_record_summary(Stack *, StackVersion, unsigned max_depth); 87 | 88 | // Retrieve a summary of all the parse states near the top of the 89 | // given version of the stack. 90 | StackSummary *ts_stack_get_summary(Stack *, StackVersion); 91 | 92 | // Get the total cost of all errors on the given version of the stack. 93 | unsigned ts_stack_error_cost(const Stack *, StackVersion version); 94 | 95 | // Merge the given two stack versions if possible, returning true 96 | // if they were successfully merged and false otherwise. 97 | bool ts_stack_merge(Stack *, StackVersion, StackVersion); 98 | 99 | // Determine whether the given two stack versions can be merged. 100 | bool ts_stack_can_merge(Stack *, StackVersion, StackVersion); 101 | 102 | TSSymbol ts_stack_resume(Stack *, StackVersion); 103 | 104 | void ts_stack_pause(Stack *, StackVersion, TSSymbol); 105 | 106 | void ts_stack_halt(Stack *, StackVersion); 107 | 108 | bool ts_stack_is_active(const Stack *, StackVersion); 109 | 110 | bool ts_stack_is_paused(const Stack *, StackVersion); 111 | 112 | bool ts_stack_is_halted(const Stack *, StackVersion); 113 | 114 | void ts_stack_renumber_version(Stack *, StackVersion, StackVersion); 115 | 116 | void ts_stack_swap_versions(Stack *, StackVersion, StackVersion); 117 | 118 | StackVersion ts_stack_copy_version(Stack *, StackVersion); 119 | 120 | // Remove the given version from the stack. 121 | void ts_stack_remove_version(Stack *, StackVersion); 122 | 123 | void ts_stack_clear(Stack *); 124 | 125 | bool ts_stack_print_dot_graph(Stack *, const TSLanguage *, FILE *); 126 | 127 | typedef void (*StackIterateCallback)(void *, TSStateId, uint32_t); 128 | 129 | void ts_stack_iterate(Stack *, StackVersion, StackIterateCallback, void *); 130 | 131 | #ifdef __cplusplus 132 | } 133 | #endif 134 | 135 | #endif // TREE_SITTER_PARSE_STACK_H_ 136 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/subtree.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_SUBTREE_H_ 2 | #define TREE_SITTER_SUBTREE_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "./length.h" 12 | #include "./array.h" 13 | #include "./error_costs.h" 14 | #include "tree_sitter/api.h" 15 | #include "tree_sitter/parser.h" 16 | 17 | static const TSStateId TS_TREE_STATE_NONE = USHRT_MAX; 18 | #define NULL_SUBTREE ((Subtree) {.ptr = NULL}) 19 | 20 | typedef union Subtree Subtree; 21 | typedef union MutableSubtree MutableSubtree; 22 | 23 | typedef struct { 24 | union { 25 | char *long_data; 26 | char short_data[24]; 27 | }; 28 | uint32_t length; 29 | } ExternalScannerState; 30 | 31 | typedef struct { 32 | bool is_inline : 1; 33 | bool visible : 1; 34 | bool named : 1; 35 | bool extra : 1; 36 | bool has_changes : 1; 37 | bool is_missing : 1; 38 | bool is_keyword : 1; 39 | uint8_t symbol; 40 | uint8_t padding_bytes; 41 | uint8_t size_bytes; 42 | uint8_t padding_columns; 43 | uint8_t padding_rows : 4; 44 | uint8_t lookahead_bytes : 4; 45 | uint16_t parse_state; 46 | } SubtreeInlineData; 47 | 48 | typedef struct { 49 | volatile uint32_t ref_count; 50 | Length padding; 51 | Length size; 52 | uint32_t lookahead_bytes; 53 | uint32_t error_cost; 54 | uint32_t child_count; 55 | TSSymbol symbol; 56 | TSStateId parse_state; 57 | 58 | bool visible : 1; 59 | bool named : 1; 60 | bool extra : 1; 61 | bool fragile_left : 1; 62 | bool fragile_right : 1; 63 | bool has_changes : 1; 64 | bool has_external_tokens : 1; 65 | bool is_missing : 1; 66 | bool is_keyword : 1; 67 | 68 | union { 69 | // Non-terminal subtrees (`child_count > 0`) 70 | struct { 71 | Subtree *children; 72 | uint32_t visible_child_count; 73 | uint32_t named_child_count; 74 | uint32_t node_count; 75 | uint32_t repeat_depth; 76 | int32_t dynamic_precedence; 77 | uint16_t production_id; 78 | struct { 79 | TSSymbol symbol; 80 | TSStateId parse_state; 81 | } first_leaf; 82 | }; 83 | 84 | // External terminal subtrees (`child_count == 0 && has_external_tokens`) 85 | ExternalScannerState external_scanner_state; 86 | 87 | // Error terminal subtrees (`child_count == 0 && symbol == ts_builtin_sym_error`) 88 | int32_t lookahead_char; 89 | }; 90 | } SubtreeHeapData; 91 | 92 | union Subtree { 93 | SubtreeInlineData data; 94 | const SubtreeHeapData *ptr; 95 | }; 96 | 97 | union MutableSubtree { 98 | SubtreeInlineData data; 99 | SubtreeHeapData *ptr; 100 | }; 101 | 102 | typedef Array(Subtree) SubtreeArray; 103 | typedef Array(MutableSubtree) MutableSubtreeArray; 104 | 105 | typedef struct { 106 | MutableSubtreeArray free_trees; 107 | MutableSubtreeArray tree_stack; 108 | } SubtreePool; 109 | 110 | void ts_external_scanner_state_init(ExternalScannerState *, const char *, unsigned); 111 | const char *ts_external_scanner_state_data(const ExternalScannerState *); 112 | 113 | void ts_subtree_array_copy(SubtreeArray, SubtreeArray *); 114 | void ts_subtree_array_delete(SubtreePool *, SubtreeArray *); 115 | SubtreeArray ts_subtree_array_remove_trailing_extras(SubtreeArray *); 116 | void ts_subtree_array_reverse(SubtreeArray *); 117 | 118 | SubtreePool ts_subtree_pool_new(uint32_t capacity); 119 | void ts_subtree_pool_delete(SubtreePool *); 120 | 121 | Subtree ts_subtree_new_leaf( 122 | SubtreePool *, TSSymbol, Length, Length, uint32_t, 123 | TSStateId, bool, bool, const TSLanguage * 124 | ); 125 | Subtree ts_subtree_new_error( 126 | SubtreePool *, int32_t, Length, Length, uint32_t, TSStateId, const TSLanguage * 127 | ); 128 | MutableSubtree ts_subtree_new_node(SubtreePool *, TSSymbol, SubtreeArray *, unsigned, const TSLanguage *); 129 | Subtree ts_subtree_new_error_node(SubtreePool *, SubtreeArray *, bool, const TSLanguage *); 130 | Subtree ts_subtree_new_missing_leaf(SubtreePool *, TSSymbol, Length, const TSLanguage *); 131 | MutableSubtree ts_subtree_make_mut(SubtreePool *, Subtree); 132 | void ts_subtree_retain(Subtree); 133 | void ts_subtree_release(SubtreePool *, Subtree); 134 | bool ts_subtree_eq(Subtree, Subtree); 135 | int ts_subtree_compare(Subtree, Subtree); 136 | void ts_subtree_set_symbol(MutableSubtree *, TSSymbol, const TSLanguage *); 137 | void ts_subtree_set_children(MutableSubtree, Subtree *, uint32_t, const TSLanguage *); 138 | void ts_subtree_balance(Subtree, SubtreePool *, const TSLanguage *); 139 | Subtree ts_subtree_edit(Subtree, const TSInputEdit *edit, SubtreePool *); 140 | char *ts_subtree_string(Subtree, const TSLanguage *, bool include_all); 141 | void ts_subtree_print_dot_graph(Subtree, const TSLanguage *, FILE *); 142 | Subtree ts_subtree_last_external_token(Subtree); 143 | bool ts_subtree_external_scanner_state_eq(Subtree, Subtree); 144 | 145 | #define SUBTREE_GET(self, name) (self.data.is_inline ? self.data.name : self.ptr->name) 146 | 147 | static inline TSSymbol ts_subtree_symbol(Subtree self) { return SUBTREE_GET(self, symbol); } 148 | static inline bool ts_subtree_visible(Subtree self) { return SUBTREE_GET(self, visible); } 149 | static inline bool ts_subtree_named(Subtree self) { return SUBTREE_GET(self, named); } 150 | static inline bool ts_subtree_extra(Subtree self) { return SUBTREE_GET(self, extra); } 151 | static inline bool ts_subtree_has_changes(Subtree self) { return SUBTREE_GET(self, has_changes); } 152 | static inline bool ts_subtree_missing(Subtree self) { return SUBTREE_GET(self, is_missing); } 153 | static inline bool ts_subtree_is_keyword(Subtree self) { return SUBTREE_GET(self, is_keyword); } 154 | static inline TSStateId ts_subtree_parse_state(Subtree self) { return SUBTREE_GET(self, parse_state); } 155 | static inline uint32_t ts_subtree_lookahead_bytes(Subtree self) { return SUBTREE_GET(self, lookahead_bytes); } 156 | 157 | #undef SUBTREE_GET 158 | 159 | static inline void ts_subtree_set_extra(MutableSubtree *self) { 160 | if (self->data.is_inline) { 161 | self->data.extra = true; 162 | } else { 163 | self->ptr->extra = true; 164 | } 165 | } 166 | 167 | static inline TSSymbol ts_subtree_leaf_symbol(Subtree self) { 168 | if (self.data.is_inline) return self.data.symbol; 169 | if (self.ptr->child_count == 0) return self.ptr->symbol; 170 | return self.ptr->first_leaf.symbol; 171 | } 172 | 173 | static inline TSStateId ts_subtree_leaf_parse_state(Subtree self) { 174 | if (self.data.is_inline) return self.data.parse_state; 175 | if (self.ptr->child_count == 0) return self.ptr->parse_state; 176 | return self.ptr->first_leaf.parse_state; 177 | } 178 | 179 | static inline Length ts_subtree_padding(Subtree self) { 180 | if (self.data.is_inline) { 181 | Length result = {self.data.padding_bytes, {self.data.padding_rows, self.data.padding_columns}}; 182 | return result; 183 | } else { 184 | return self.ptr->padding; 185 | } 186 | } 187 | 188 | static inline Length ts_subtree_size(Subtree self) { 189 | if (self.data.is_inline) { 190 | Length result = {self.data.size_bytes, {0, self.data.size_bytes}}; 191 | return result; 192 | } else { 193 | return self.ptr->size; 194 | } 195 | } 196 | 197 | static inline Length ts_subtree_total_size(Subtree self) { 198 | return length_add(ts_subtree_padding(self), ts_subtree_size(self)); 199 | } 200 | 201 | static inline uint32_t ts_subtree_total_bytes(Subtree self) { 202 | return ts_subtree_total_size(self).bytes; 203 | } 204 | 205 | static inline uint32_t ts_subtree_child_count(Subtree self) { 206 | return self.data.is_inline ? 0 : self.ptr->child_count; 207 | } 208 | 209 | static inline uint32_t ts_subtree_repeat_depth(Subtree self) { 210 | return self.data.is_inline ? 0 : self.ptr->repeat_depth; 211 | } 212 | 213 | static inline uint32_t ts_subtree_node_count(Subtree self) { 214 | return (self.data.is_inline || self.ptr->child_count == 0) ? 1 : self.ptr->node_count; 215 | } 216 | 217 | static inline uint32_t ts_subtree_visible_child_count(Subtree self) { 218 | if (ts_subtree_child_count(self) > 0) { 219 | return self.ptr->visible_child_count; 220 | } else { 221 | return 0; 222 | } 223 | } 224 | 225 | static inline uint32_t ts_subtree_error_cost(Subtree self) { 226 | if (ts_subtree_missing(self)) { 227 | return ERROR_COST_PER_MISSING_TREE + ERROR_COST_PER_RECOVERY; 228 | } else { 229 | return self.data.is_inline ? 0 : self.ptr->error_cost; 230 | } 231 | } 232 | 233 | static inline int32_t ts_subtree_dynamic_precedence(Subtree self) { 234 | return (self.data.is_inline || self.ptr->child_count == 0) ? 0 : self.ptr->dynamic_precedence; 235 | } 236 | 237 | static inline uint16_t ts_subtree_production_id(Subtree self) { 238 | if (ts_subtree_child_count(self) > 0) { 239 | return self.ptr->production_id; 240 | } else { 241 | return 0; 242 | } 243 | } 244 | 245 | static inline bool ts_subtree_fragile_left(Subtree self) { 246 | return self.data.is_inline ? false : self.ptr->fragile_left; 247 | } 248 | 249 | static inline bool ts_subtree_fragile_right(Subtree self) { 250 | return self.data.is_inline ? false : self.ptr->fragile_right; 251 | } 252 | 253 | static inline bool ts_subtree_has_external_tokens(Subtree self) { 254 | return self.data.is_inline ? false : self.ptr->has_external_tokens; 255 | } 256 | 257 | static inline bool ts_subtree_is_fragile(Subtree self) { 258 | return self.data.is_inline ? false : (self.ptr->fragile_left || self.ptr->fragile_right); 259 | } 260 | 261 | static inline bool ts_subtree_is_error(Subtree self) { 262 | return ts_subtree_symbol(self) == ts_builtin_sym_error; 263 | } 264 | 265 | static inline bool ts_subtree_is_eof(Subtree self) { 266 | return ts_subtree_symbol(self) == ts_builtin_sym_end; 267 | } 268 | 269 | static inline Subtree ts_subtree_from_mut(MutableSubtree self) { 270 | Subtree result; 271 | result.data = self.data; 272 | return result; 273 | } 274 | 275 | static inline MutableSubtree ts_subtree_to_mut_unsafe(Subtree self) { 276 | MutableSubtree result; 277 | result.data = self.data; 278 | return result; 279 | } 280 | 281 | #ifdef __cplusplus 282 | } 283 | #endif 284 | 285 | #endif // TREE_SITTER_SUBTREE_H_ 286 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/tree.c: -------------------------------------------------------------------------------- 1 | #include "tree_sitter/api.h" 2 | #include "./array.h" 3 | #include "./get_changed_ranges.h" 4 | #include "./subtree.h" 5 | #include "./tree_cursor.h" 6 | #include "./tree.h" 7 | 8 | static const unsigned PARENT_CACHE_CAPACITY = 32; 9 | 10 | TSTree *ts_tree_new( 11 | Subtree root, const TSLanguage *language, 12 | const TSRange *included_ranges, unsigned included_range_count 13 | ) { 14 | TSTree *result = ts_malloc(sizeof(TSTree)); 15 | result->root = root; 16 | result->language = language; 17 | result->parent_cache = NULL; 18 | result->parent_cache_start = 0; 19 | result->parent_cache_size = 0; 20 | result->included_ranges = ts_calloc(included_range_count, sizeof(TSRange)); 21 | memcpy(result->included_ranges, included_ranges, included_range_count * sizeof(TSRange)); 22 | result->included_range_count = included_range_count; 23 | return result; 24 | } 25 | 26 | TSTree *ts_tree_copy(const TSTree *self) { 27 | ts_subtree_retain(self->root); 28 | return ts_tree_new(self->root, self->language, self->included_ranges, self->included_range_count); 29 | } 30 | 31 | void ts_tree_delete(TSTree *self) { 32 | if (!self) return; 33 | 34 | SubtreePool pool = ts_subtree_pool_new(0); 35 | ts_subtree_release(&pool, self->root); 36 | ts_subtree_pool_delete(&pool); 37 | ts_free(self->included_ranges); 38 | if (self->parent_cache) ts_free(self->parent_cache); 39 | ts_free(self); 40 | } 41 | 42 | TSNode ts_tree_root_node(const TSTree *self) { 43 | return ts_node_new(self, &self->root, ts_subtree_padding(self->root), 0); 44 | } 45 | 46 | const TSLanguage *ts_tree_language(const TSTree *self) { 47 | return self->language; 48 | } 49 | 50 | void ts_tree_edit(TSTree *self, const TSInputEdit *edit) { 51 | for (unsigned i = 0; i < self->included_range_count; i++) { 52 | TSRange *range = &self->included_ranges[i]; 53 | if (range->end_byte >= edit->old_end_byte) { 54 | if (range->end_byte != UINT32_MAX) { 55 | range->end_byte = edit->new_end_byte + (range->end_byte - edit->old_end_byte); 56 | range->end_point = point_add( 57 | edit->new_end_point, 58 | point_sub(range->end_point, edit->old_end_point) 59 | ); 60 | if (range->end_byte < edit->new_end_byte) { 61 | range->end_byte = UINT32_MAX; 62 | range->end_point = POINT_MAX; 63 | } 64 | } 65 | if (range->start_byte >= edit->old_end_byte) { 66 | range->start_byte = edit->new_end_byte + (range->start_byte - edit->old_end_byte); 67 | range->start_point = point_add( 68 | edit->new_end_point, 69 | point_sub(range->start_point, edit->old_end_point) 70 | ); 71 | if (range->start_byte < edit->new_end_byte) { 72 | range->start_byte = UINT32_MAX; 73 | range->start_point = POINT_MAX; 74 | } 75 | } 76 | } 77 | } 78 | 79 | SubtreePool pool = ts_subtree_pool_new(0); 80 | self->root = ts_subtree_edit(self->root, edit, &pool); 81 | self->parent_cache_start = 0; 82 | self->parent_cache_size = 0; 83 | ts_subtree_pool_delete(&pool); 84 | } 85 | 86 | TSRange *ts_tree_get_changed_ranges(const TSTree *self, const TSTree *other, uint32_t *count) { 87 | TreeCursor cursor1 = {NULL, array_new()}; 88 | TreeCursor cursor2 = {NULL, array_new()}; 89 | ts_tree_cursor_init(&cursor1, ts_tree_root_node(self)); 90 | ts_tree_cursor_init(&cursor2, ts_tree_root_node(other)); 91 | 92 | TSRangeArray included_range_differences = array_new(); 93 | ts_range_array_get_changed_ranges( 94 | self->included_ranges, self->included_range_count, 95 | other->included_ranges, other->included_range_count, 96 | &included_range_differences 97 | ); 98 | 99 | TSRange *result; 100 | *count = ts_subtree_get_changed_ranges( 101 | &self->root, &other->root, &cursor1, &cursor2, 102 | self->language, &included_range_differences, &result 103 | ); 104 | 105 | array_delete(&included_range_differences); 106 | array_delete(&cursor1.stack); 107 | array_delete(&cursor2.stack); 108 | return result; 109 | } 110 | 111 | void ts_tree_print_dot_graph(const TSTree *self, FILE *file) { 112 | ts_subtree_print_dot_graph(self->root, self->language, file); 113 | } 114 | 115 | TSNode ts_tree_get_cached_parent(const TSTree *self, const TSNode *node) { 116 | for (uint32_t i = 0; i < self->parent_cache_size; i++) { 117 | uint32_t index = (self->parent_cache_start + i) % PARENT_CACHE_CAPACITY; 118 | ParentCacheEntry *entry = &self->parent_cache[index]; 119 | if (entry->child == node->id) { 120 | return ts_node_new(self, entry->parent, entry->position, entry->alias_symbol); 121 | } 122 | } 123 | return ts_node_new(NULL, NULL, length_zero(), 0); 124 | } 125 | 126 | void ts_tree_set_cached_parent(const TSTree *_self, const TSNode *node, const TSNode *parent) { 127 | TSTree *self = (TSTree *)_self; 128 | if (!self->parent_cache) { 129 | self->parent_cache = ts_calloc(PARENT_CACHE_CAPACITY, sizeof(ParentCacheEntry)); 130 | } 131 | 132 | uint32_t index = (self->parent_cache_start + self->parent_cache_size) % PARENT_CACHE_CAPACITY; 133 | self->parent_cache[index] = (ParentCacheEntry) { 134 | .child = node->id, 135 | .parent = (const Subtree *)parent->id, 136 | .position = { 137 | parent->context[0], 138 | {parent->context[1], parent->context[2]} 139 | }, 140 | .alias_symbol = parent->context[3], 141 | }; 142 | 143 | if (self->parent_cache_size == PARENT_CACHE_CAPACITY) { 144 | self->parent_cache_start++; 145 | } else { 146 | self->parent_cache_size++; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/tree.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_TREE_H_ 2 | #define TREE_SITTER_TREE_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | typedef struct { 9 | const Subtree *child; 10 | const Subtree *parent; 11 | Length position; 12 | TSSymbol alias_symbol; 13 | } ParentCacheEntry; 14 | 15 | struct TSTree { 16 | Subtree root; 17 | const TSLanguage *language; 18 | ParentCacheEntry *parent_cache; 19 | uint32_t parent_cache_start; 20 | uint32_t parent_cache_size; 21 | TSRange *included_ranges; 22 | unsigned included_range_count; 23 | }; 24 | 25 | TSTree *ts_tree_new(Subtree root, const TSLanguage *language, const TSRange *, unsigned); 26 | TSNode ts_node_new(const TSTree *, const Subtree *, Length, TSSymbol); 27 | TSNode ts_tree_get_cached_parent(const TSTree *, const TSNode *); 28 | void ts_tree_set_cached_parent(const TSTree *, const TSNode *, const TSNode *); 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | #endif // TREE_SITTER_TREE_H_ 35 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/tree_cursor.c: -------------------------------------------------------------------------------- 1 | #include "tree_sitter/api.h" 2 | #include "./alloc.h" 3 | #include "./tree_cursor.h" 4 | #include "./language.h" 5 | #include "./tree.h" 6 | 7 | typedef struct { 8 | Subtree parent; 9 | const TSTree *tree; 10 | Length position; 11 | uint32_t child_index; 12 | uint32_t structural_child_index; 13 | const TSSymbol *alias_sequence; 14 | } CursorChildIterator; 15 | 16 | // CursorChildIterator 17 | 18 | static inline CursorChildIterator ts_tree_cursor_iterate_children(const TreeCursor *self) { 19 | TreeCursorEntry *last_entry = array_back(&self->stack); 20 | if (ts_subtree_child_count(*last_entry->subtree) == 0) { 21 | return (CursorChildIterator) {NULL_SUBTREE, self->tree, length_zero(), 0, 0, NULL}; 22 | } 23 | const TSSymbol *alias_sequence = ts_language_alias_sequence( 24 | self->tree->language, 25 | last_entry->subtree->ptr->production_id 26 | ); 27 | return (CursorChildIterator) { 28 | .tree = self->tree, 29 | .parent = *last_entry->subtree, 30 | .position = last_entry->position, 31 | .child_index = 0, 32 | .structural_child_index = 0, 33 | .alias_sequence = alias_sequence, 34 | }; 35 | } 36 | 37 | static inline bool ts_tree_cursor_child_iterator_next(CursorChildIterator *self, 38 | TreeCursorEntry *result, 39 | bool *visible) { 40 | if (!self->parent.ptr || self->child_index == self->parent.ptr->child_count) return false; 41 | const Subtree *child = &self->parent.ptr->children[self->child_index]; 42 | *result = (TreeCursorEntry) { 43 | .subtree = child, 44 | .position = self->position, 45 | .child_index = self->child_index, 46 | .structural_child_index = self->structural_child_index, 47 | }; 48 | *visible = ts_subtree_visible(*child); 49 | bool extra = ts_subtree_extra(*child); 50 | if (!extra && self->alias_sequence) { 51 | *visible |= self->alias_sequence[self->structural_child_index]; 52 | self->structural_child_index++; 53 | } 54 | 55 | self->position = length_add(self->position, ts_subtree_size(*child)); 56 | self->child_index++; 57 | 58 | if (self->child_index < self->parent.ptr->child_count) { 59 | Subtree next_child = self->parent.ptr->children[self->child_index]; 60 | self->position = length_add(self->position, ts_subtree_padding(next_child)); 61 | } 62 | 63 | return true; 64 | } 65 | 66 | // TSTreeCursor - lifecycle 67 | 68 | TSTreeCursor ts_tree_cursor_new(TSNode node) { 69 | TSTreeCursor self = {NULL, NULL, {0, 0}}; 70 | ts_tree_cursor_init((TreeCursor *)&self, node); 71 | return self; 72 | } 73 | 74 | void ts_tree_cursor_reset(TSTreeCursor *_self, TSNode node) { 75 | ts_tree_cursor_init((TreeCursor *)_self, node); 76 | } 77 | 78 | void ts_tree_cursor_init(TreeCursor *self, TSNode node) { 79 | self->tree = node.tree; 80 | array_clear(&self->stack); 81 | array_push(&self->stack, ((TreeCursorEntry) { 82 | .subtree = (const Subtree *)node.id, 83 | .position = { 84 | ts_node_start_byte(node), 85 | ts_node_start_point(node) 86 | }, 87 | .child_index = 0, 88 | .structural_child_index = 0, 89 | })); 90 | } 91 | 92 | void ts_tree_cursor_delete(TSTreeCursor *_self) { 93 | TreeCursor *self = (TreeCursor *)_self; 94 | array_delete(&self->stack); 95 | } 96 | 97 | // TSTreeCursor - walking the tree 98 | 99 | bool ts_tree_cursor_goto_first_child(TSTreeCursor *_self) { 100 | TreeCursor *self = (TreeCursor *)_self; 101 | 102 | bool did_descend; 103 | do { 104 | did_descend = false; 105 | 106 | bool visible; 107 | TreeCursorEntry entry; 108 | CursorChildIterator iterator = ts_tree_cursor_iterate_children(self); 109 | while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) { 110 | if (visible) { 111 | array_push(&self->stack, entry); 112 | return true; 113 | } 114 | 115 | if (ts_subtree_visible_child_count(*entry.subtree) > 0) { 116 | array_push(&self->stack, entry); 117 | did_descend = true; 118 | break; 119 | } 120 | } 121 | } while (did_descend); 122 | 123 | return false; 124 | } 125 | 126 | int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *_self, uint32_t goal_byte) { 127 | TreeCursor *self = (TreeCursor *)_self; 128 | uint32_t initial_size = self->stack.size; 129 | uint32_t visible_child_index = 0; 130 | 131 | bool did_descend; 132 | do { 133 | did_descend = false; 134 | 135 | bool visible; 136 | TreeCursorEntry entry; 137 | CursorChildIterator iterator = ts_tree_cursor_iterate_children(self); 138 | while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) { 139 | uint32_t end_byte = entry.position.bytes + ts_subtree_size(*entry.subtree).bytes; 140 | bool at_goal = end_byte > goal_byte; 141 | uint32_t visible_child_count = ts_subtree_visible_child_count(*entry.subtree); 142 | 143 | if (at_goal) { 144 | if (visible) { 145 | array_push(&self->stack, entry); 146 | return visible_child_index; 147 | } 148 | 149 | if (visible_child_count > 0) { 150 | array_push(&self->stack, entry); 151 | did_descend = true; 152 | break; 153 | } 154 | } else if (visible) { 155 | visible_child_index++; 156 | } else { 157 | visible_child_index += visible_child_count; 158 | } 159 | } 160 | } while (did_descend); 161 | 162 | if (self->stack.size > initial_size && 163 | ts_tree_cursor_goto_next_sibling((TSTreeCursor *)self)) { 164 | return visible_child_index; 165 | } 166 | 167 | self->stack.size = initial_size; 168 | return -1; 169 | } 170 | 171 | bool ts_tree_cursor_goto_next_sibling(TSTreeCursor *_self) { 172 | TreeCursor *self = (TreeCursor *)_self; 173 | uint32_t initial_size = self->stack.size; 174 | 175 | while (self->stack.size > 1) { 176 | TreeCursorEntry entry = array_pop(&self->stack); 177 | CursorChildIterator iterator = ts_tree_cursor_iterate_children(self); 178 | iterator.child_index = entry.child_index; 179 | iterator.structural_child_index = entry.structural_child_index; 180 | iterator.position = entry.position; 181 | 182 | bool visible = false; 183 | ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible); 184 | if (visible && self->stack.size + 1 < initial_size) break; 185 | 186 | while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) { 187 | if (visible) { 188 | array_push(&self->stack, entry); 189 | return true; 190 | } 191 | 192 | if (ts_subtree_visible_child_count(*entry.subtree)) { 193 | array_push(&self->stack, entry); 194 | ts_tree_cursor_goto_first_child(_self); 195 | return true; 196 | } 197 | } 198 | } 199 | 200 | self->stack.size = initial_size; 201 | return false; 202 | } 203 | 204 | bool ts_tree_cursor_goto_parent(TSTreeCursor *_self) { 205 | TreeCursor *self = (TreeCursor *)_self; 206 | for (unsigned i = self->stack.size - 2; i + 1 > 0; i--) { 207 | TreeCursorEntry *entry = &self->stack.contents[i]; 208 | bool is_aliased = false; 209 | if (i > 0) { 210 | TreeCursorEntry *parent_entry = &self->stack.contents[i - 1]; 211 | const TSSymbol *alias_sequence = ts_language_alias_sequence( 212 | self->tree->language, 213 | parent_entry->subtree->ptr->production_id 214 | ); 215 | is_aliased = alias_sequence && alias_sequence[entry->structural_child_index]; 216 | } 217 | if (ts_subtree_visible(*entry->subtree) || is_aliased) { 218 | self->stack.size = i + 1; 219 | return true; 220 | } 221 | } 222 | return false; 223 | } 224 | 225 | TSNode ts_tree_cursor_current_node(const TSTreeCursor *_self) { 226 | const TreeCursor *self = (const TreeCursor *)_self; 227 | TreeCursorEntry *last_entry = array_back(&self->stack); 228 | TSSymbol alias_symbol = 0; 229 | if (self->stack.size > 1) { 230 | TreeCursorEntry *parent_entry = &self->stack.contents[self->stack.size - 2]; 231 | const TSSymbol *alias_sequence = ts_language_alias_sequence( 232 | self->tree->language, 233 | parent_entry->subtree->ptr->production_id 234 | ); 235 | if (alias_sequence && !ts_subtree_extra(*last_entry->subtree)) { 236 | alias_symbol = alias_sequence[last_entry->structural_child_index]; 237 | } 238 | } 239 | return ts_node_new( 240 | self->tree, 241 | last_entry->subtree, 242 | last_entry->position, 243 | alias_symbol 244 | ); 245 | } 246 | 247 | TSFieldId ts_tree_cursor_current_status( 248 | const TSTreeCursor *_self, 249 | bool *can_have_later_siblings, 250 | bool *can_have_later_siblings_with_this_field 251 | ) { 252 | const TreeCursor *self = (const TreeCursor *)_self; 253 | TSFieldId result = 0; 254 | *can_have_later_siblings = false; 255 | *can_have_later_siblings_with_this_field = false; 256 | 257 | // Walk up the tree, visiting the current node and its invisible ancestors, 258 | // because fields can refer to nodes through invisible *wrapper* nodes, 259 | for (unsigned i = self->stack.size - 1; i > 0; i--) { 260 | TreeCursorEntry *entry = &self->stack.contents[i]; 261 | TreeCursorEntry *parent_entry = &self->stack.contents[i - 1]; 262 | 263 | // Stop walking up when a visible ancestor is found. 264 | if (i != self->stack.size - 1) { 265 | if (ts_subtree_visible(*entry->subtree)) break; 266 | const TSSymbol *alias_sequence = ts_language_alias_sequence( 267 | self->tree->language, 268 | parent_entry->subtree->ptr->production_id 269 | ); 270 | if (alias_sequence && alias_sequence[entry->structural_child_index]) { 271 | break; 272 | } 273 | } 274 | 275 | if (ts_subtree_child_count(*parent_entry->subtree) > entry->child_index + 1) { 276 | *can_have_later_siblings = true; 277 | } 278 | 279 | if (ts_subtree_extra(*entry->subtree)) break; 280 | 281 | const TSFieldMapEntry *field_map, *field_map_end; 282 | ts_language_field_map( 283 | self->tree->language, 284 | parent_entry->subtree->ptr->production_id, 285 | &field_map, &field_map_end 286 | ); 287 | 288 | // Look for a field name associated with the current node. 289 | if (!result) { 290 | for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) { 291 | if (!i->inherited && i->child_index == entry->structural_child_index) { 292 | result = i->field_id; 293 | *can_have_later_siblings_with_this_field = false; 294 | break; 295 | } 296 | } 297 | } 298 | 299 | // Determine if there other later siblings with the same field name. 300 | if (result) { 301 | for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) { 302 | if (i->field_id == result && i->child_index > entry->structural_child_index) { 303 | *can_have_later_siblings_with_this_field = true; 304 | break; 305 | } 306 | } 307 | } 308 | } 309 | 310 | return result; 311 | } 312 | 313 | TSFieldId ts_tree_cursor_current_field_id(const TSTreeCursor *_self) { 314 | const TreeCursor *self = (const TreeCursor *)_self; 315 | 316 | // Walk up the tree, visiting the current node and its invisible ancestors. 317 | for (unsigned i = self->stack.size - 1; i > 0; i--) { 318 | TreeCursorEntry *entry = &self->stack.contents[i]; 319 | TreeCursorEntry *parent_entry = &self->stack.contents[i - 1]; 320 | 321 | // Stop walking up when another visible node is found. 322 | if (i != self->stack.size - 1) { 323 | if (ts_subtree_visible(*entry->subtree)) break; 324 | const TSSymbol *alias_sequence = ts_language_alias_sequence( 325 | self->tree->language, 326 | parent_entry->subtree->ptr->production_id 327 | ); 328 | if (alias_sequence && alias_sequence[entry->structural_child_index]) { 329 | break; 330 | } 331 | } 332 | 333 | if (ts_subtree_extra(*entry->subtree)) break; 334 | 335 | const TSFieldMapEntry *field_map, *field_map_end; 336 | ts_language_field_map( 337 | self->tree->language, 338 | parent_entry->subtree->ptr->production_id, 339 | &field_map, &field_map_end 340 | ); 341 | for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) { 342 | if (!i->inherited && i->child_index == entry->structural_child_index) { 343 | return i->field_id; 344 | } 345 | } 346 | } 347 | return 0; 348 | } 349 | 350 | const char *ts_tree_cursor_current_field_name(const TSTreeCursor *_self) { 351 | TSFieldId id = ts_tree_cursor_current_field_id(_self); 352 | if (id) { 353 | const TreeCursor *self = (const TreeCursor *)_self; 354 | return self->tree->language->field_names[id]; 355 | } else { 356 | return NULL; 357 | } 358 | } 359 | 360 | TSTreeCursor ts_tree_cursor_copy(const TSTreeCursor *_cursor) { 361 | const TreeCursor *cursor = (const TreeCursor *)_cursor; 362 | TSTreeCursor res = {NULL, NULL, {0, 0}}; 363 | TreeCursor *copy = (TreeCursor *)&res; 364 | copy->tree = cursor->tree; 365 | array_push_all(©->stack, &cursor->stack); 366 | return res; 367 | } 368 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/tree_cursor.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_TREE_CURSOR_H_ 2 | #define TREE_SITTER_TREE_CURSOR_H_ 3 | 4 | #include "./subtree.h" 5 | 6 | typedef struct { 7 | const Subtree *subtree; 8 | Length position; 9 | uint32_t child_index; 10 | uint32_t structural_child_index; 11 | } TreeCursorEntry; 12 | 13 | typedef struct { 14 | const TSTree *tree; 15 | Array(TreeCursorEntry) stack; 16 | } TreeCursor; 17 | 18 | void ts_tree_cursor_init(TreeCursor *, TSNode); 19 | TSFieldId ts_tree_cursor_current_status(const TSTreeCursor *, bool *, bool *); 20 | 21 | #endif // TREE_SITTER_TREE_CURSOR_H_ 22 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/tree_sitter/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_PARSER_H_ 2 | #define TREE_SITTER_PARSER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define ts_builtin_sym_error ((TSSymbol)-1) 13 | #define ts_builtin_sym_end 0 14 | #define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 15 | 16 | #ifndef TREE_SITTER_API_H_ 17 | typedef uint16_t TSSymbol; 18 | typedef uint16_t TSFieldId; 19 | typedef struct TSLanguage TSLanguage; 20 | #endif 21 | 22 | typedef struct { 23 | TSFieldId field_id; 24 | uint8_t child_index; 25 | bool inherited; 26 | } TSFieldMapEntry; 27 | 28 | typedef struct { 29 | uint16_t index; 30 | uint16_t length; 31 | } TSFieldMapSlice; 32 | 33 | typedef uint16_t TSStateId; 34 | 35 | typedef struct { 36 | bool visible : 1; 37 | bool named : 1; 38 | } TSSymbolMetadata; 39 | 40 | typedef struct TSLexer TSLexer; 41 | 42 | struct TSLexer { 43 | int32_t lookahead; 44 | TSSymbol result_symbol; 45 | void (*advance)(TSLexer *, bool); 46 | void (*mark_end)(TSLexer *); 47 | uint32_t (*get_column)(TSLexer *); 48 | bool (*is_at_included_range_start)(const TSLexer *); 49 | bool (*eof)(const TSLexer *); 50 | }; 51 | 52 | typedef enum { 53 | TSParseActionTypeShift, 54 | TSParseActionTypeReduce, 55 | TSParseActionTypeAccept, 56 | TSParseActionTypeRecover, 57 | } TSParseActionType; 58 | 59 | typedef struct { 60 | union { 61 | struct { 62 | TSStateId state; 63 | bool extra : 1; 64 | bool repetition : 1; 65 | }; 66 | struct { 67 | TSSymbol symbol; 68 | int16_t dynamic_precedence; 69 | uint8_t child_count; 70 | uint8_t production_id; 71 | }; 72 | } params; 73 | TSParseActionType type : 4; 74 | } TSParseAction; 75 | 76 | typedef struct { 77 | uint16_t lex_state; 78 | uint16_t external_lex_state; 79 | } TSLexMode; 80 | 81 | typedef union { 82 | TSParseAction action; 83 | struct { 84 | uint8_t count; 85 | bool reusable : 1; 86 | }; 87 | } TSParseActionEntry; 88 | 89 | struct TSLanguage { 90 | uint32_t version; 91 | uint32_t symbol_count; 92 | uint32_t alias_count; 93 | uint32_t token_count; 94 | uint32_t external_token_count; 95 | const char **symbol_names; 96 | const TSSymbolMetadata *symbol_metadata; 97 | const uint16_t *parse_table; 98 | const TSParseActionEntry *parse_actions; 99 | const TSLexMode *lex_modes; 100 | const TSSymbol *alias_sequences; 101 | uint16_t max_alias_sequence_length; 102 | bool (*lex_fn)(TSLexer *, TSStateId); 103 | bool (*keyword_lex_fn)(TSLexer *, TSStateId); 104 | TSSymbol keyword_capture_token; 105 | struct { 106 | const bool *states; 107 | const TSSymbol *symbol_map; 108 | void *(*create)(void); 109 | void (*destroy)(void *); 110 | bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist); 111 | unsigned (*serialize)(void *, char *); 112 | void (*deserialize)(void *, const char *, unsigned); 113 | } external_scanner; 114 | uint32_t field_count; 115 | const TSFieldMapSlice *field_map_slices; 116 | const TSFieldMapEntry *field_map_entries; 117 | const char **field_names; 118 | uint32_t large_state_count; 119 | const uint16_t *small_parse_table; 120 | const uint32_t *small_parse_table_map; 121 | const TSSymbol *public_symbol_map; 122 | }; 123 | 124 | /* 125 | * Lexer Macros 126 | */ 127 | 128 | #define START_LEXER() \ 129 | bool result = false; \ 130 | bool skip = false; \ 131 | bool eof = false; \ 132 | int32_t lookahead; \ 133 | goto start; \ 134 | next_state: \ 135 | lexer->advance(lexer, skip); \ 136 | start: \ 137 | skip = false; \ 138 | lookahead = lexer->lookahead; 139 | 140 | #define ADVANCE(state_value) \ 141 | { \ 142 | state = state_value; \ 143 | goto next_state; \ 144 | } 145 | 146 | #define SKIP(state_value) \ 147 | { \ 148 | skip = true; \ 149 | state = state_value; \ 150 | goto next_state; \ 151 | } 152 | 153 | #define ACCEPT_TOKEN(symbol_value) \ 154 | result = true; \ 155 | lexer->result_symbol = symbol_value; \ 156 | lexer->mark_end(lexer); 157 | 158 | #define END_STATE() return result; 159 | 160 | /* 161 | * Parse Table Macros 162 | */ 163 | 164 | #define SMALL_STATE(id) id - LARGE_STATE_COUNT 165 | 166 | #define STATE(id) id 167 | 168 | #define ACTIONS(id) id 169 | 170 | #define SHIFT(state_value) \ 171 | { \ 172 | { \ 173 | .type = TSParseActionTypeShift, \ 174 | .params = {.state = state_value}, \ 175 | } \ 176 | } 177 | 178 | #define SHIFT_REPEAT(state_value) \ 179 | { \ 180 | { \ 181 | .type = TSParseActionTypeShift, \ 182 | .params = { \ 183 | .state = state_value, \ 184 | .repetition = true \ 185 | }, \ 186 | } \ 187 | } 188 | 189 | #define RECOVER() \ 190 | { \ 191 | { .type = TSParseActionTypeRecover } \ 192 | } 193 | 194 | #define SHIFT_EXTRA() \ 195 | { \ 196 | { \ 197 | .type = TSParseActionTypeShift, \ 198 | .params = {.extra = true} \ 199 | } \ 200 | } 201 | 202 | #define REDUCE(symbol_val, child_count_val, ...) \ 203 | { \ 204 | { \ 205 | .type = TSParseActionTypeReduce, \ 206 | .params = { \ 207 | .symbol = symbol_val, \ 208 | .child_count = child_count_val, \ 209 | __VA_ARGS__ \ 210 | } \ 211 | } \ 212 | } 213 | 214 | #define ACCEPT_INPUT() \ 215 | { \ 216 | { .type = TSParseActionTypeAccept } \ 217 | } 218 | 219 | #ifdef __cplusplus 220 | } 221 | #endif 222 | 223 | #endif // TREE_SITTER_PARSER_H_ 224 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/unicode.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_UNICODE_H_ 2 | #define TREE_SITTER_UNICODE_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define U_EXPORT 12 | #define U_EXPORT2 13 | #include "unicode/utf8.h" 14 | #include "unicode/utf16.h" 15 | 16 | static const int32_t TS_DECODE_ERROR = U_SENTINEL; 17 | 18 | // These functions read one unicode code point from the given string, 19 | // returning the number of bytes consumed. 20 | typedef uint32_t (*UnicodeDecodeFunction)( 21 | const uint8_t *string, 22 | uint32_t length, 23 | int32_t *code_point 24 | ); 25 | 26 | static inline uint32_t ts_decode_utf8( 27 | const uint8_t *string, 28 | uint32_t length, 29 | int32_t *code_point 30 | ) { 31 | uint32_t i = 0; 32 | U8_NEXT(string, i, length, *code_point); 33 | return i; 34 | } 35 | 36 | static inline uint32_t ts_decode_utf16( 37 | const uint8_t *string, 38 | uint32_t length, 39 | int32_t *code_point 40 | ) { 41 | uint32_t i = 0; 42 | U16_NEXT(((uint16_t *)string), i, length, *code_point); 43 | return i * 2; 44 | } 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif // TREE_SITTER_UNICODE_H_ 51 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/unicode/ICU_SHA: -------------------------------------------------------------------------------- 1 | 552b01f61127d30d6589aa4bf99468224979b661 2 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/unicode/README.md: -------------------------------------------------------------------------------- 1 | # ICU Parts 2 | 3 | This directory contains a small subset of files from the Unicode organization's [ICU repository](https://github.com/unicode-org/icu). 4 | 5 | ### License 6 | 7 | The license for these files is contained in the `LICENSE` file within this directory. 8 | 9 | ### Contents 10 | 11 | * Source files taken from the [`icu4c/source/common/unicode`](https://github.com/unicode-org/icu/tree/552b01f61127d30d6589aa4bf99468224979b661/icu4c/source/common/unicode) directory: 12 | * `utf8.h` 13 | * `utf16.h` 14 | * `umachine.h` 15 | * Empty source files that are referenced by the above source files, but whose original contents in `libicu` are not needed: 16 | * `ptypes.h` 17 | * `urename.h` 18 | * `utf.h` 19 | * `ICU_SHA` - File containing the Git SHA of the commit in the `icu` repository from which the files were obtained. 20 | * `LICENSE` - The license file from the [`icu4c`](https://github.com/unicode-org/icu/tree/552b01f61127d30d6589aa4bf99468224979b661/icu4c) directory of the `icu` repository. 21 | * `README.md` - This text file. 22 | 23 | ### Updating ICU 24 | 25 | To incorporate changes from the upstream `icu` repository: 26 | 27 | * Update `ICU_SHA` with the new Git SHA. 28 | * Update `LICENSE` with the license text from the directory mentioned above. 29 | * Update `utf8.h`, `utf16.h`, and `umachine.h` with their new contents in the `icu` repository. 30 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/unicode/ptypes.h: -------------------------------------------------------------------------------- 1 | // This file must exist in order for `utf8.h` and `utf16.h` to be used. 2 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/unicode/umachine.h: -------------------------------------------------------------------------------- 1 | // © 2016 and later: Unicode, Inc. and others. 2 | // License & terms of use: http://www.unicode.org/copyright.html 3 | /* 4 | ****************************************************************************** 5 | * 6 | * Copyright (C) 1999-2015, International Business Machines 7 | * Corporation and others. All Rights Reserved. 8 | * 9 | ****************************************************************************** 10 | * file name: umachine.h 11 | * encoding: UTF-8 12 | * tab size: 8 (not used) 13 | * indentation:4 14 | * 15 | * created on: 1999sep13 16 | * created by: Markus W. Scherer 17 | * 18 | * This file defines basic types and constants for ICU to be 19 | * platform-independent. umachine.h and utf.h are included into 20 | * utypes.h to provide all the general definitions for ICU. 21 | * All of these definitions used to be in utypes.h before 22 | * the UTF-handling macros made this unmaintainable. 23 | */ 24 | 25 | #ifndef __UMACHINE_H__ 26 | #define __UMACHINE_H__ 27 | 28 | 29 | /** 30 | * \file 31 | * \brief Basic types and constants for UTF 32 | * 33 | *

Basic types and constants for UTF

34 | * This file defines basic types and constants for utf.h to be 35 | * platform-independent. umachine.h and utf.h are included into 36 | * utypes.h to provide all the general definitions for ICU. 37 | * All of these definitions used to be in utypes.h before 38 | * the UTF-handling macros made this unmaintainable. 39 | * 40 | */ 41 | /*==========================================================================*/ 42 | /* Include platform-dependent definitions */ 43 | /* which are contained in the platform-specific file platform.h */ 44 | /*==========================================================================*/ 45 | 46 | #include "ptypes.h" /* platform.h is included in ptypes.h */ 47 | 48 | /* 49 | * ANSI C headers: 50 | * stddef.h defines wchar_t 51 | */ 52 | #include 53 | 54 | /*==========================================================================*/ 55 | /* For C wrappers, we use the symbol U_STABLE. */ 56 | /* This works properly if the includer is C or C++. */ 57 | /* Functions are declared U_STABLE return-type U_EXPORT2 function-name()... */ 58 | /*==========================================================================*/ 59 | 60 | /** 61 | * \def U_CFUNC 62 | * This is used in a declaration of a library private ICU C function. 63 | * @stable ICU 2.4 64 | */ 65 | 66 | /** 67 | * \def U_CDECL_BEGIN 68 | * This is used to begin a declaration of a library private ICU C API. 69 | * @stable ICU 2.4 70 | */ 71 | 72 | /** 73 | * \def U_CDECL_END 74 | * This is used to end a declaration of a library private ICU C API 75 | * @stable ICU 2.4 76 | */ 77 | 78 | #ifdef __cplusplus 79 | # define U_CFUNC extern "C" 80 | # define U_CDECL_BEGIN extern "C" { 81 | # define U_CDECL_END } 82 | #else 83 | # define U_CFUNC extern 84 | # define U_CDECL_BEGIN 85 | # define U_CDECL_END 86 | #endif 87 | 88 | #ifndef U_ATTRIBUTE_DEPRECATED 89 | /** 90 | * \def U_ATTRIBUTE_DEPRECATED 91 | * This is used for GCC specific attributes 92 | * @internal 93 | */ 94 | #if U_GCC_MAJOR_MINOR >= 302 95 | # define U_ATTRIBUTE_DEPRECATED __attribute__ ((deprecated)) 96 | /** 97 | * \def U_ATTRIBUTE_DEPRECATED 98 | * This is used for Visual C++ specific attributes 99 | * @internal 100 | */ 101 | #elif defined(_MSC_VER) && (_MSC_VER >= 1400) 102 | # define U_ATTRIBUTE_DEPRECATED __declspec(deprecated) 103 | #else 104 | # define U_ATTRIBUTE_DEPRECATED 105 | #endif 106 | #endif 107 | 108 | /** This is used to declare a function as a public ICU C API @stable ICU 2.0*/ 109 | #define U_CAPI U_CFUNC U_EXPORT 110 | /** This is used to declare a function as a stable public ICU C API*/ 111 | #define U_STABLE U_CAPI 112 | /** This is used to declare a function as a draft public ICU C API */ 113 | #define U_DRAFT U_CAPI 114 | /** This is used to declare a function as a deprecated public ICU C API */ 115 | #define U_DEPRECATED U_CAPI U_ATTRIBUTE_DEPRECATED 116 | /** This is used to declare a function as an obsolete public ICU C API */ 117 | #define U_OBSOLETE U_CAPI 118 | /** This is used to declare a function as an internal ICU C API */ 119 | #define U_INTERNAL U_CAPI 120 | 121 | /** 122 | * \def U_OVERRIDE 123 | * Defined to the C++11 "override" keyword if available. 124 | * Denotes a class or member which is an override of the base class. 125 | * May result in an error if it applied to something not an override. 126 | * @internal 127 | */ 128 | #ifndef U_OVERRIDE 129 | #define U_OVERRIDE override 130 | #endif 131 | 132 | /** 133 | * \def U_FINAL 134 | * Defined to the C++11 "final" keyword if available. 135 | * Denotes a class or member which may not be overridden in subclasses. 136 | * May result in an error if subclasses attempt to override. 137 | * @internal 138 | */ 139 | #if !defined(U_FINAL) || defined(U_IN_DOXYGEN) 140 | #define U_FINAL final 141 | #endif 142 | 143 | // Before ICU 65, function-like, multi-statement ICU macros were just defined as 144 | // series of statements wrapped in { } blocks and the caller could choose to 145 | // either treat them as if they were actual functions and end the invocation 146 | // with a trailing ; creating an empty statement after the block or else omit 147 | // this trailing ; using the knowledge that the macro would expand to { }. 148 | // 149 | // But doing so doesn't work well with macros that look like functions and 150 | // compiler warnings about empty statements (ICU-20601) and ICU 65 therefore 151 | // switches to the standard solution of wrapping such macros in do { } while. 152 | // 153 | // This will however break existing code that depends on being able to invoke 154 | // these macros without a trailing ; so to be able to remain compatible with 155 | // such code the wrapper is itself defined as macros so that it's possible to 156 | // build ICU 65 and later with the old macro behaviour, like this: 157 | // 158 | // CPPFLAGS='-DUPRV_BLOCK_MACRO_BEGIN="" -DUPRV_BLOCK_MACRO_END=""' 159 | // runConfigureICU ... 160 | 161 | /** 162 | * \def UPRV_BLOCK_MACRO_BEGIN 163 | * Defined as the "do" keyword by default. 164 | * @internal 165 | */ 166 | #ifndef UPRV_BLOCK_MACRO_BEGIN 167 | #define UPRV_BLOCK_MACRO_BEGIN do 168 | #endif 169 | 170 | /** 171 | * \def UPRV_BLOCK_MACRO_END 172 | * Defined as "while (FALSE)" by default. 173 | * @internal 174 | */ 175 | #ifndef UPRV_BLOCK_MACRO_END 176 | #define UPRV_BLOCK_MACRO_END while (FALSE) 177 | #endif 178 | 179 | /*==========================================================================*/ 180 | /* limits for int32_t etc., like in POSIX inttypes.h */ 181 | /*==========================================================================*/ 182 | 183 | #ifndef INT8_MIN 184 | /** The smallest value an 8 bit signed integer can hold @stable ICU 2.0 */ 185 | # define INT8_MIN ((int8_t)(-128)) 186 | #endif 187 | #ifndef INT16_MIN 188 | /** The smallest value a 16 bit signed integer can hold @stable ICU 2.0 */ 189 | # define INT16_MIN ((int16_t)(-32767-1)) 190 | #endif 191 | #ifndef INT32_MIN 192 | /** The smallest value a 32 bit signed integer can hold @stable ICU 2.0 */ 193 | # define INT32_MIN ((int32_t)(-2147483647-1)) 194 | #endif 195 | 196 | #ifndef INT8_MAX 197 | /** The largest value an 8 bit signed integer can hold @stable ICU 2.0 */ 198 | # define INT8_MAX ((int8_t)(127)) 199 | #endif 200 | #ifndef INT16_MAX 201 | /** The largest value a 16 bit signed integer can hold @stable ICU 2.0 */ 202 | # define INT16_MAX ((int16_t)(32767)) 203 | #endif 204 | #ifndef INT32_MAX 205 | /** The largest value a 32 bit signed integer can hold @stable ICU 2.0 */ 206 | # define INT32_MAX ((int32_t)(2147483647)) 207 | #endif 208 | 209 | #ifndef UINT8_MAX 210 | /** The largest value an 8 bit unsigned integer can hold @stable ICU 2.0 */ 211 | # define UINT8_MAX ((uint8_t)(255U)) 212 | #endif 213 | #ifndef UINT16_MAX 214 | /** The largest value a 16 bit unsigned integer can hold @stable ICU 2.0 */ 215 | # define UINT16_MAX ((uint16_t)(65535U)) 216 | #endif 217 | #ifndef UINT32_MAX 218 | /** The largest value a 32 bit unsigned integer can hold @stable ICU 2.0 */ 219 | # define UINT32_MAX ((uint32_t)(4294967295U)) 220 | #endif 221 | 222 | #if defined(U_INT64_T_UNAVAILABLE) 223 | # error int64_t is required for decimal format and rule-based number format. 224 | #else 225 | # ifndef INT64_C 226 | /** 227 | * Provides a platform independent way to specify a signed 64-bit integer constant. 228 | * note: may be wrong for some 64 bit platforms - ensure your compiler provides INT64_C 229 | * @stable ICU 2.8 230 | */ 231 | # define INT64_C(c) c ## LL 232 | # endif 233 | # ifndef UINT64_C 234 | /** 235 | * Provides a platform independent way to specify an unsigned 64-bit integer constant. 236 | * note: may be wrong for some 64 bit platforms - ensure your compiler provides UINT64_C 237 | * @stable ICU 2.8 238 | */ 239 | # define UINT64_C(c) c ## ULL 240 | # endif 241 | # ifndef U_INT64_MIN 242 | /** The smallest value a 64 bit signed integer can hold @stable ICU 2.8 */ 243 | # define U_INT64_MIN ((int64_t)(INT64_C(-9223372036854775807)-1)) 244 | # endif 245 | # ifndef U_INT64_MAX 246 | /** The largest value a 64 bit signed integer can hold @stable ICU 2.8 */ 247 | # define U_INT64_MAX ((int64_t)(INT64_C(9223372036854775807))) 248 | # endif 249 | # ifndef U_UINT64_MAX 250 | /** The largest value a 64 bit unsigned integer can hold @stable ICU 2.8 */ 251 | # define U_UINT64_MAX ((uint64_t)(UINT64_C(18446744073709551615))) 252 | # endif 253 | #endif 254 | 255 | /*==========================================================================*/ 256 | /* Boolean data type */ 257 | /*==========================================================================*/ 258 | 259 | /** The ICU boolean type @stable ICU 2.0 */ 260 | typedef int8_t UBool; 261 | 262 | #ifndef TRUE 263 | /** The TRUE value of a UBool @stable ICU 2.0 */ 264 | # define TRUE 1 265 | #endif 266 | #ifndef FALSE 267 | /** The FALSE value of a UBool @stable ICU 2.0 */ 268 | # define FALSE 0 269 | #endif 270 | 271 | 272 | /*==========================================================================*/ 273 | /* Unicode data types */ 274 | /*==========================================================================*/ 275 | 276 | /* wchar_t-related definitions -------------------------------------------- */ 277 | 278 | /* 279 | * \def U_WCHAR_IS_UTF16 280 | * Defined if wchar_t uses UTF-16. 281 | * 282 | * @stable ICU 2.0 283 | */ 284 | /* 285 | * \def U_WCHAR_IS_UTF32 286 | * Defined if wchar_t uses UTF-32. 287 | * 288 | * @stable ICU 2.0 289 | */ 290 | #if !defined(U_WCHAR_IS_UTF16) && !defined(U_WCHAR_IS_UTF32) 291 | # ifdef __STDC_ISO_10646__ 292 | # if (U_SIZEOF_WCHAR_T==2) 293 | # define U_WCHAR_IS_UTF16 294 | # elif (U_SIZEOF_WCHAR_T==4) 295 | # define U_WCHAR_IS_UTF32 296 | # endif 297 | # elif defined __UCS2__ 298 | # if (U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400) && (U_SIZEOF_WCHAR_T==2) 299 | # define U_WCHAR_IS_UTF16 300 | # endif 301 | # elif defined(__UCS4__) || (U_PLATFORM == U_PF_OS400 && defined(__UTF32__)) 302 | # if (U_SIZEOF_WCHAR_T==4) 303 | # define U_WCHAR_IS_UTF32 304 | # endif 305 | # elif U_PLATFORM_IS_DARWIN_BASED || (U_SIZEOF_WCHAR_T==4 && U_PLATFORM_IS_LINUX_BASED) 306 | # define U_WCHAR_IS_UTF32 307 | # elif U_PLATFORM_HAS_WIN32_API 308 | # define U_WCHAR_IS_UTF16 309 | # endif 310 | #endif 311 | 312 | /* UChar and UChar32 definitions -------------------------------------------- */ 313 | 314 | /** Number of bytes in a UChar. @stable ICU 2.0 */ 315 | #define U_SIZEOF_UCHAR 2 316 | 317 | /** 318 | * \def U_CHAR16_IS_TYPEDEF 319 | * If 1, then char16_t is a typedef and not a real type (yet) 320 | * @internal 321 | */ 322 | #if (U_PLATFORM == U_PF_AIX) && defined(__cplusplus) &&(U_CPLUSPLUS_VERSION < 11) 323 | // for AIX, uchar.h needs to be included 324 | # include 325 | # define U_CHAR16_IS_TYPEDEF 1 326 | #elif defined(_MSC_VER) && (_MSC_VER < 1900) 327 | // Versions of Visual Studio/MSVC below 2015 do not support char16_t as a real type, 328 | // and instead use a typedef. https://msdn.microsoft.com/library/bb531344.aspx 329 | # define U_CHAR16_IS_TYPEDEF 1 330 | #else 331 | # define U_CHAR16_IS_TYPEDEF 0 332 | #endif 333 | 334 | 335 | /** 336 | * \var UChar 337 | * 338 | * The base type for UTF-16 code units and pointers. 339 | * Unsigned 16-bit integer. 340 | * Starting with ICU 59, C++ API uses char16_t directly, while C API continues to use UChar. 341 | * 342 | * UChar is configurable by defining the macro UCHAR_TYPE 343 | * on the preprocessor or compiler command line: 344 | * -DUCHAR_TYPE=uint16_t or -DUCHAR_TYPE=wchar_t (if U_SIZEOF_WCHAR_T==2) etc. 345 | * (The UCHAR_TYPE can also be \#defined earlier in this file, for outside the ICU library code.) 346 | * This is for transitional use from application code that uses uint16_t or wchar_t for UTF-16. 347 | * 348 | * The default is UChar=char16_t. 349 | * 350 | * C++11 defines char16_t as bit-compatible with uint16_t, but as a distinct type. 351 | * 352 | * In C, char16_t is a simple typedef of uint_least16_t. 353 | * ICU requires uint_least16_t=uint16_t for data memory mapping. 354 | * On macOS, char16_t is not available because the uchar.h standard header is missing. 355 | * 356 | * @stable ICU 4.4 357 | */ 358 | 359 | #if 1 360 | // #if 1 is normal. UChar defaults to char16_t in C++. 361 | // For configuration testing of UChar=uint16_t temporarily change this to #if 0. 362 | // The intltest Makefile #defines UCHAR_TYPE=char16_t, 363 | // so we only #define it to uint16_t if it is undefined so far. 364 | #elif !defined(UCHAR_TYPE) 365 | # define UCHAR_TYPE uint16_t 366 | #endif 367 | 368 | #if defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || \ 369 | defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) 370 | // Inside the ICU library code, never configurable. 371 | typedef char16_t UChar; 372 | #elif defined(UCHAR_TYPE) 373 | typedef UCHAR_TYPE UChar; 374 | #elif defined(__cplusplus) 375 | typedef char16_t UChar; 376 | #else 377 | typedef uint16_t UChar; 378 | #endif 379 | 380 | /** 381 | * \var OldUChar 382 | * Default ICU 58 definition of UChar. 383 | * A base type for UTF-16 code units and pointers. 384 | * Unsigned 16-bit integer. 385 | * 386 | * Define OldUChar to be wchar_t if that is 16 bits wide. 387 | * If wchar_t is not 16 bits wide, then define UChar to be uint16_t. 388 | * 389 | * This makes the definition of OldUChar platform-dependent 390 | * but allows direct string type compatibility with platforms with 391 | * 16-bit wchar_t types. 392 | * 393 | * This is how UChar was defined in ICU 58, for transition convenience. 394 | * Exception: ICU 58 UChar was defined to UCHAR_TYPE if that macro was defined. 395 | * The current UChar responds to UCHAR_TYPE but OldUChar does not. 396 | * 397 | * @stable ICU 59 398 | */ 399 | #if U_SIZEOF_WCHAR_T==2 400 | typedef wchar_t OldUChar; 401 | #elif defined(__CHAR16_TYPE__) 402 | typedef __CHAR16_TYPE__ OldUChar; 403 | #else 404 | typedef uint16_t OldUChar; 405 | #endif 406 | 407 | /** 408 | * Define UChar32 as a type for single Unicode code points. 409 | * UChar32 is a signed 32-bit integer (same as int32_t). 410 | * 411 | * The Unicode code point range is 0..0x10ffff. 412 | * All other values (negative or >=0x110000) are illegal as Unicode code points. 413 | * They may be used as sentinel values to indicate "done", "error" 414 | * or similar non-code point conditions. 415 | * 416 | * Before ICU 2.4 (Jitterbug 2146), UChar32 was defined 417 | * to be wchar_t if that is 32 bits wide (wchar_t may be signed or unsigned) 418 | * or else to be uint32_t. 419 | * That is, the definition of UChar32 was platform-dependent. 420 | * 421 | * @see U_SENTINEL 422 | * @stable ICU 2.4 423 | */ 424 | typedef int32_t UChar32; 425 | 426 | /** 427 | * This value is intended for sentinel values for APIs that 428 | * (take or) return single code points (UChar32). 429 | * It is outside of the Unicode code point range 0..0x10ffff. 430 | * 431 | * For example, a "done" or "error" value in a new API 432 | * could be indicated with U_SENTINEL. 433 | * 434 | * ICU APIs designed before ICU 2.4 usually define service-specific "done" 435 | * values, mostly 0xffff. 436 | * Those may need to be distinguished from 437 | * actual U+ffff text contents by calling functions like 438 | * CharacterIterator::hasNext() or UnicodeString::length(). 439 | * 440 | * @return -1 441 | * @see UChar32 442 | * @stable ICU 2.4 443 | */ 444 | #define U_SENTINEL (-1) 445 | 446 | #include "urename.h" 447 | 448 | #endif 449 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/unicode/urename.h: -------------------------------------------------------------------------------- 1 | // This file must exist in order for `utf8.h` and `utf16.h` to be used. 2 | -------------------------------------------------------------------------------- /src/paravim/tree_sitter/unicode/utf.h: -------------------------------------------------------------------------------- 1 | // This file must exist in order for `utf8.h` and `utf16.h` to be used. 2 | -------------------------------------------------------------------------------- /src/paravim/vim.nim: -------------------------------------------------------------------------------- 1 | import libvim, structs, core 2 | from buffers import nil 3 | from pararules import nil 4 | from strutils import nil 5 | from os import nil 6 | from tree_sitter import nil 7 | import tables 8 | from unicode import nil 9 | 10 | proc cropCommandText(commandText: string): string = 11 | result = "" 12 | let index = strutils.find(commandText, ' ') 13 | if index >= 0: 14 | result = ":" & commandText[0 ..< index] 15 | 16 | proc completeCommand() = 17 | let vim = pararules.query(session, rules.getVim) 18 | if vim.commandText.len == vim.commandPosition: 19 | let firstPart = cropCommandText(vim.commandText) 20 | # delete everything after the first part of the command 21 | for _ in firstPart.len ..< vim.commandText.len: 22 | vimInput("") 23 | # input everything from the completion 24 | for i in firstPart.len ..< vim.commandCompletion.len: 25 | vimInputUnicode($ vim.commandCompletion[i]) 26 | 27 | proc executeCommand() = 28 | let vim = pararules.query(session, rules.getVim) 29 | if vim.commandStart == ':' and asciiArt.hasKey(vim.commandText): 30 | session.insert(Global, AsciiArt, vim.commandText) 31 | vimInput("") 32 | else: 33 | vimInput("") 34 | 35 | proc updateCommandStart(input: string) = 36 | const 37 | validCommandStarts = {':', '?', '/'} 38 | searchCommandStarts = {'?', '/'} 39 | var s = input[0] 40 | if not validCommandStarts.contains(s): 41 | s = ':' 42 | session.insert(Global, VimCommandStart, s) 43 | if searchCommandStarts.contains(s): 44 | session.insert(Global, VimShowSearch, true) 45 | 46 | proc updateCommand() = 47 | let 48 | commandText = $ vimCommandLineGetText() 49 | commandPos = vimCommandLineGetPosition() 50 | session.insert(Global, VimCommandText, commandText) 51 | session.insert(Global, VimCommandPosition, commandPos) 52 | var completion = "" 53 | let strippedText = strutils.strip(commandText) 54 | if strippedText.len > 0 and 55 | not strutils.startsWith(strippedText, "!") and # don't try to complete shell commands 56 | commandText.len == commandPos: 57 | var 58 | completions: cstringArray 59 | count: cint 60 | vimCommandLineGetCompletions(completions.addr, count.addr) 61 | if count > 0: 62 | let firstPart = cropCommandText(commandText) 63 | completion = firstPart & $ completions[0] 64 | for i in 0 ..< count: 65 | vimFree(completions[i]) 66 | session.insert(Global, VimCommandCompletion, completion) 67 | 68 | proc updateSelection(id: int) = 69 | if vimVisualIsActive() == 1: 70 | var startPos, endPos: pos_T 71 | vimVisualGetRange(startPos.addr, endPos.addr) 72 | session.insert(id, VimVisualRange, (int(startPos.lnum-1), int(startPos.col), int(endPos.lnum-1), int(endPos.col))) 73 | session.insert(id, VimVisualBlockMode, vimVisualGetType() == 22) 74 | else: 75 | session.insert(id, VimVisualRange, (0, 0, 0, 0)) 76 | 77 | proc updateSearchHighlights(id: int) = 78 | let vim = pararules.query(session, rules.getVim) 79 | if vim.mode == libvim.CommandLine.ord and vim.commandStart == ':': 80 | return 81 | var 82 | numHighlights: cint 83 | highlights: ptr searchHighlight_T 84 | ranges: seq[buffers.RangeTuple] 85 | vimSearchGetHighlights(1, vimBufferGetLineCount(vimBufferGetCurrent()).clong, numHighlights.addr, highlights.addr) 86 | let arr = cast[ptr UncheckedArray[searchHighlight_T]](highlights) 87 | for i in 0 ..< numHighlights: 88 | ranges.add(( 89 | startLine: int(arr[i].start.lnum-1), 90 | startColumn: int(arr[i].start.col), 91 | endLine: int(arr[i].`end`.lnum-1), 92 | endColumn: int(arr[i].`end`.col) 93 | )) 94 | vimFree(highlights) 95 | session.insert(id, VimSearchRanges, ranges) 96 | 97 | proc updateAfterInput() = 98 | let id = getCurrentSessionId() 99 | if id >= 0: 100 | session.insert(id, CursorLine, vimCursorGetLine() - 1) 101 | session.insert(id, CursorColumn, vimCursorGetColumn()) 102 | updateSelection(id) 103 | updateSearchHighlights(id) 104 | 105 | proc onInput*(input: string) = 106 | session.insert(Global, VimMessage, "") # clear any pre-existing message 107 | let oldMode = vimGetMode() 108 | if oldMode == libvim.CommandLine.ord and input == "": 109 | completeCommand() 110 | elif oldMode == libvim.CommandLine.ord and input == "": 111 | executeCommand() 112 | else: 113 | session.insert(Global, AsciiArt, "") 114 | if strutils.startsWith(input, "<") and strutils.endsWith(input, ">"): 115 | vimInput(input) 116 | else: 117 | vimInputUnicode(input) 118 | let mode = vimGetMode() 119 | session.insert(Global, VimMode, mode) 120 | if mode == libvim.CommandLine.ord: 121 | if mode != oldMode: 122 | updateCommandStart(input) 123 | updateCommand() 124 | updateAfterInput() 125 | 126 | proc onBulkInput*(input: string) = 127 | vimExecute("set paste") 128 | for ch in unicode.utf8(input): 129 | if ch == "\r": 130 | continue 131 | vimInputUnicode(ch) 132 | vimExecute("set nopaste") 133 | updateAfterInput() 134 | 135 | proc onBufDelete(buf: buf_T) = 136 | let bufferId = vimBufferGetId(buf) 137 | let index = pararules.find(session, rules.getBuffer, bufferId = bufferId) 138 | if index == -1: 139 | return 140 | let existingBuffer = pararules.get(session, rules.getBuffer, index) 141 | tree_sitter.deleteTree(existingBuffer.tree) 142 | tree_sitter.deleteParser(existingBuffer.parser) 143 | let id = existingBuffer.id 144 | session.retract(id, BufferId) 145 | session.retract(id, Lines) 146 | session.retract(id, CursorLine) 147 | session.retract(id, CursorColumn) 148 | session.retract(id, ScrollX) 149 | session.retract(id, ScrollY) 150 | session.retract(id, ScrollTargetX) 151 | session.retract(id, ScrollTargetY) 152 | session.retract(id, ScrollSpeedX) 153 | session.retract(id, ScrollSpeedY) 154 | session.retract(id, MaxCharCount) 155 | session.retract(id, LineCount) 156 | session.retract(id, Tree) 157 | session.retract(id, Parser) 158 | session.retract(id, VimVisualRange) 159 | session.retract(id, VimVisualBlockMode) 160 | session.retract(id, VimSearchRanges) 161 | if pararules.find(session, rules.getBufferEntities, id = id) != -1: 162 | session.retract(id, Text) 163 | session.retract(id, CroppedText) 164 | session.retract(id, MinimapText) 165 | session.retract(id, MinimapRects) 166 | session.retract(id, ShowMinimap) 167 | 168 | proc onBufEnter(buf: buf_T) = 169 | let 170 | bufferId = vimBufferGetId(buf) 171 | path = vimBufferGetFilename(buf) 172 | count = vimBufferGetLineCount(buf) 173 | session.insert(Global, CurrentBufferId, bufferId) 174 | session.insert(Global, WindowTitle, if path == nil: "Paravim" else: os.extractFilename($ path) & " - Paravim") 175 | let index = pararules.find(session, rules.getBuffer, bufferId = bufferId) 176 | if path != nil: 177 | let pathStr = $ path 178 | # get lines 179 | var lines: ref seq[string] 180 | new(lines) 181 | for i in 0 ..< count: 182 | let line = vimBufferGetLine(buf, linenr_T(i+1)) 183 | lines[].add($ line) 184 | # get or create session id 185 | var sessionId: int 186 | if index >= 0: 187 | let existingBuffer = pararules.get(session, rules.getBuffer, index) 188 | # if the content hasn't changed, no need to update the buffer 189 | if existingBuffer.lines[] == lines[]: 190 | return 191 | else: 192 | sessionId = existingBuffer.id 193 | onBufDelete(buf) 194 | else: 195 | sessionId = nextId 196 | nextId += 1 197 | # insert buffer 198 | session.insert(sessionId, BufferId, bufferId) 199 | session.insert(sessionId, Path, pathStr) 200 | session.insert(sessionId, Lines, lines) 201 | session.insert(sessionId, CursorLine, vimCursorGetLine() - 1) 202 | session.insert(sessionId, CursorColumn, vimCursorGetColumn()) 203 | session.insert(sessionId, ScrollX, 0f) 204 | session.insert(sessionId, ScrollY, 0f) 205 | session.insert(sessionId, ScrollTargetX, 0f) 206 | session.insert(sessionId, ScrollTargetY, 0f) 207 | session.insert(sessionId, ScrollSpeedX, 0f) 208 | session.insert(sessionId, ScrollSpeedY, 0f) 209 | session.insert(sessionId, LineCount, count) 210 | let (tree, parser) = tree_sitter.init(pathStr, lines[]) 211 | session.insert(sessionId, Tree, tree) 212 | session.insert(sessionId, Parser, parser) 213 | session.insert(sessionId, VimVisualRange, (0, 0, 0, 0)) 214 | session.insert(sessionId, VimVisualBlockMode, false) 215 | session.insert(sessionId, VimSearchRanges, @[]) 216 | session.insert(sessionId, ShowMinimap, false) 217 | let parsed = tree_sitter.parse(tree, lines[].len) 218 | insertTextEntity(sessionId, lines, parsed) 219 | 220 | proc onAutoCommand(a1: event_T; buf: buf_T) {.cdecl.} = 221 | case a1: 222 | of EVENT_BUFENTER: 223 | onBufEnter(buf) 224 | of EVENT_BUFDELETE: 225 | onBufDelete(buf) 226 | else: 227 | discard 228 | 229 | proc onBufferUpdate(bufferUpdate: bufferUpdate_T) {.cdecl.} = 230 | let 231 | firstLine = bufferUpdate.lnum - 1 232 | lastLine = bufferUpdate.lnume - 1 + bufferUpdate.xtra 233 | var lines: seq[string] 234 | for i in firstLine ..< lastLine: 235 | let line = vimBufferGetLine(bufferUpdate.buf, linenr_T(i+1)) 236 | lines.add($ line) 237 | let id = vimBufferGetId(bufferUpdate.buf).int 238 | # get current buffer 239 | let index = pararules.find(session, rules.getBuffer, bufferId = id) 240 | if index == -1: 241 | return 242 | let buffer = pararules.get(session, rules.getBuffer, index) # will throw if buffer isn't in session 243 | # update the lines 244 | let bu = (lines, firstLine.int, bufferUpdate.xtra.int) 245 | var newLines = buffers.updateLines(buffer.lines, bu) 246 | # if the lines are empty, insert a single blank line 247 | # vim seems to always want there to be at least one line 248 | # see test: delete all lines 249 | if newLines[].len == 0: 250 | newLines[] = @[""] 251 | session.insert(buffer.id, Lines, newLines) 252 | # re-parse if necessary 253 | let 254 | newTree = tree_sitter.editTree(buffer.tree, buffer.parser, newLines) 255 | parsed = tree_sitter.parse(newTree, newLines[].len) 256 | session.insert(buffer.id, Tree, newTree) 257 | pararules.fireRules(session) # fire rules manually so the following query gets the latest data 258 | # update text entity 259 | block: 260 | let index = pararules.find(session, rules.getBufferEntities, id = buffer.id) 261 | if index == -1: 262 | return 263 | let bufferEntities = pararules.get(session, rules.getBufferEntities, index) 264 | updateTextEntity(buffer.id, newLines, parsed, bufferEntities.text, bu) 265 | 266 | proc onStopSearch() {.cdecl.} = 267 | session.insert(Global, VimShowSearch, false) 268 | 269 | proc onMessage(title: ptr char_u; msg: ptr char_u; priority: msgPriority_T) {.cdecl.} = 270 | session.insert(Global, VimMessage, $ msg) 271 | 272 | proc init*(onQuit: QuitCallback, onYank: YankCallback) = 273 | vimSetAutoCommandCallback(onAutoCommand) 274 | vimSetBufferUpdateCallback(onBufferUpdate) 275 | vimSetQuitCallback(onQuit) 276 | vimSetStopSearchHighlightCallback(onStopSearch) 277 | vimSetUnhandledEscapeCallback(onStopSearch) 278 | vimSetMessageCallback(onMessage) 279 | vimSetYankCallback(onYank) 280 | 281 | vimInit(0, nil) 282 | vimExecute("set hidden") 283 | vimExecute("set noswapfile") 284 | vimExecute("set nobackup") 285 | vimExecute("set nowritebackup") 286 | vimExecute("set tabstop=2") 287 | vimExecute("set softtabstop=2") 288 | vimExecute("set shiftwidth=2") 289 | vimExecute("set expandtab") 290 | vimExecute("set hlsearch") 291 | vimExecute("set fileformats=unix,dos") 292 | vimExecute("filetype plugin index on") 293 | 294 | session.insert(Global, VimMode, vimGetMode()) 295 | session.insert(Global, VimCommandText, "") 296 | session.insert(Global, VimCommandStart, ':') 297 | session.insert(Global, VimCommandPosition, 0) 298 | session.insert(Global, VimCommandCompletion, "") 299 | session.insert(Global, VimMessage, "") 300 | session.insert(Global, VimShowSearch, false) 301 | -------------------------------------------------------------------------------- /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") 2 | -------------------------------------------------------------------------------- /tests/hello.txt: -------------------------------------------------------------------------------- 1 | Hello, world! 2 | Goodbye, world! 3 | -------------------------------------------------------------------------------- /tests/test1.nim: -------------------------------------------------------------------------------- 1 | # This is just an example to get you started. You may wish to put all of your 2 | # tests into a single file, or separate them into multiple `test1`, `test2` 3 | # etc. files (better names are recommended, just make sure the name starts with 4 | # the letter 't'). 5 | # 6 | # To run these tests, simply execute `nimble test`. 7 | 8 | import unittest 9 | from paranim/gl import nil 10 | from paravim/libvim import nil 11 | from paravim import nil 12 | from paravim/vim import nil 13 | import paranim/glfw 14 | 15 | var game = gl.RootGame() 16 | 17 | proc init() = 18 | doAssert glfwInit() 19 | 20 | glfwWindowHint(GLFWContextVersionMajor, 3) 21 | glfwWindowHint(GLFWContextVersionMinor, 3) 22 | glfwWindowHint(GLFWOpenglForwardCompat, GLFW_TRUE) # Used for Mac 23 | glfwWindowHint(GLFWOpenglProfile, GLFW_OPENGL_CORE_PROFILE) 24 | glfwWindowHint(GLFWResizable, GLFW_TRUE) 25 | 26 | let w: GLFWWindow = glfwCreateWindow(800, 600, "Paravim Test") 27 | if w == nil: 28 | quit(-1) 29 | 30 | w.makeContextCurrent() 31 | 32 | paravim.init(game, w) 33 | 34 | init() 35 | 36 | test "set the tab size": 37 | libvim.vimOptionSetTabSize(2) 38 | check libvim.vimOptionGetTabSize() == 2 39 | 40 | test "read a line": 41 | let buf = libvim.vimBufferOpen("tests/hello.txt", 1, 0) 42 | check libvim.vimBufferGetLine(buf, 1) == "Hello, world!" 43 | libvim.vimExecute("bd!") 44 | 45 | test "delete all lines": 46 | let buf = libvim.vimBufferOpen("tests/hello.txt", 1, 0) 47 | check libvim.vimBufferGetLine(buf, 1) == "Hello, world!" 48 | vim.onInput("g") 49 | vim.onInput("g") 50 | vim.onInput("d") 51 | vim.onInput("G") 52 | check libvim.vimBufferGetLine(buf, 1) == "" 53 | vim.onInput("p") 54 | check libvim.vimBufferGetLine(buf, 1) == "" # first line is blank 55 | check libvim.vimBufferGetLine(buf, 2) == "Hello, world!" 56 | vim.onInput("u") 57 | libvim.vimExecute("bd!") 58 | --------------------------------------------------------------------------------