├── .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 |
--------------------------------------------------------------------------------