Compare commits

..
9 Commits
13 changed files with 659 additions and 61 deletions

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
/cosmic-text/
/__pycache__/
/nim-version/fide
/build/
+36
View File
@@ -0,0 +1,36 @@
* Keymapping system
** X11 Window Manager
- Run launcher
- Basic Window Management
- Buffer-or-x11win
** If only it had a good text editor...
- SDL2
** IMTUI Lib
- Autolayout system
** Custom Language VM
- LSP
- Treesitter
- Coroutines with the event loop
** Keymap System
*** Keymap Stacking
There shouldn't be modes, there would be keymaps and a stack. Insert mode would
be at the bottom of the stack. Adding a new mode means pushing onto the stack,
and when a keybinding event is raised, you iterate over the stack from top to
bottom to find the correct keybinding. The vim nomenclature wouldn't work in
this case as you don't "leave insert mode". Rather, you pop the "normal mode"
keymap ontop of the "insert mode" keymap.
*** Passthrough/Blocking
The keymap object should have additional settings that dictate its behavior when
a key is found and not found.
**** Found
***** Pop
The default could be to pop from the stack.
***** Keep
Keep the keymap on the stack. This is useful for both normal mode and transient
like modes where you need to explicitly pop it from the stack.
**** Not found
***** Passthrough
Allow the loop to go to the next keymap
***** Block
Ignore missing key, don't pop the keymap, maybe report an error
Binary file not shown.
+22 -6
View File
@@ -1,8 +1,24 @@
from editing import *
from keybindings import *
keymap = {
'left': cursor_go_left,
'right': cursor_go_right,
'up': cursor_go_up,
'down': cursor_go_down
}
kb('insert', 'left' , cursor_go_left)
kb('insert', 'right', cursor_go_right)
kb('insert', 'up' , cursor_go_up)
kb('insert', 'down' , cursor_go_down)
kb('insert', 'C-e' , cursor_go_line_end)
kb('insert', 'C-a' , cursor_go_line_beginning)
kb('insert', 'escape', lambda: keymap_push(keymappings['normal']))
kb('insert', 'backspace', buffer_delete_character_under_cursor)
kb('insert', 'delete', buffer_delete_next_character)
kb('insert', 'return', buffer_newline_at_pos)
kb('insert', 'C-s', buffer_save)
kb('normal', 'i', keymap_pop)
kb('normal', 'H', cursor_go_buffer_beginning)
kb('normal', 'L', cursor_go_buffer_end)
kb('normal', 'h', cursor_go_left)
kb('normal', 'j', cursor_go_down)
kb('normal', 'k', cursor_go_up)
kb('normal', 'l', cursor_go_right)
kb('normal', '^', cursor_go_line_beginning)
kb('normal', '$', cursor_go_line_end)
kb('normal', 'C-s', buffer_save)
+2
View File
@@ -13,6 +13,8 @@ using namespace std;
const int SCREEN_WIDTH = 1024;
const int SCREEN_HEIGHT = 768;
char *test_string = "This is a test string for fide";
enum KeyPressSurfaces
{
KEY_PRESS_SURFACE_DEFAULT,
+31 -2
View File
@@ -43,16 +43,24 @@ def cursor_go_up():
# TODO: This should be global
cursor.col = min(cursor.want_col, line_len)
def cursor_go_beginning_line():
def cursor_go_line_beginning():
buf = ctx.current_buffer
buf.cursor.col = 0
buf.cursor.want_col = buf.cursor.col
def cursor_go_end_line():
def cursor_go_line_end():
buf = ctx.current_buffer
buf.cursor.col = buffer_line_len(buf)
buf.cursor.want_col = buf.cursor.col
def cursor_go_buffer_beginning():
buf = ctx.current_buffer
buf.cursor.line_num = 0
def cursor_go_buffer_end():
buf = ctx.current_buffer
buf.cursor.line_num = len(buf.lines) - 1
def buffer_insert_line_below(buf: Buffer, text):
line_num = buffer_line_num(buf)
buf.lines.insert(line_num + 1, line_create(text))
@@ -111,6 +119,14 @@ def buffer_delete_next_character():
curr_line.length -= 1
cursor.want_col = cursor.col
def buffer_insert_char(c):
cursor = ctx.current_buffer.cursor
curr_line = buffer_line_current(ctx.current_buffer)
curr_line.piece.insert(c, cursor.col)
curr_line.length += 1
cursor.col += 1
cursor.want_col = cursor.col
def buffer_insert_text_at_cursor(text):
cursor = ctx.current_buffer.cursor
curr_line = buffer_line_current(ctx.current_buffer)
@@ -119,3 +135,16 @@ def buffer_insert_text_at_cursor(text):
curr_line.length += text_len
cursor.col += text_len
cursor.want_col = text_len
# def buffer_switch_normal_mode():
# ctx.current_buffer.mode = 'normal'
# def buffer_switch_insert_mode():
# ctx.current_buffer.mode = 'insert'
def buffer_save():
buf = ctx.current_buffer
# if buf.filepath is not None:
with open(buf.filepath, 'w') as f:
text = '\n'.join(map(lambda l: l.piece.get_text(), buf.lines))
f.write(text)
+107 -52
View File
@@ -9,7 +9,7 @@ import sys
import time
import copy
from dataclasses import dataclass
from typing import List
from typing import List, Optional
from code import InteractiveConsole, InteractiveInterpreter
@@ -23,6 +23,8 @@ class Cursor:
@dataclass(slots=True)
class Line:
# Making this a PieceTable rather than a string is rather pointless right now
# But it's easier to operate on rows for the time being
piece: PieceTable
length: int
@@ -30,6 +32,7 @@ class Line:
class Buffer:
lines: List[Line]
cursor: Cursor
filepath: Optional[str] = None
def cursor_pos(cursor: Cursor):
return (cursor.line_num, cursor.col)
@@ -41,11 +44,11 @@ def cursor_draw(cursor: Cursor):
def line_create(text: str) -> Line:
return Line(PieceTable(text), len(text))
def buffer_create(text: str, cursor: Cursor) -> Buffer:
def buffer_create(text: str, filepath: str, cursor: Cursor) -> Buffer:
lines = []
for line in text.split('\n'):
lines.append(line_create(line))
return Buffer(lines, cursor)
return Buffer(lines, cursor, filepath)
def buffer_line_current(buf: Buffer) -> Line:
return buf.lines[buf.cursor.line_num]
@@ -59,26 +62,6 @@ def buffer_total_lines(buf: Buffer) -> int:
def buffer_line_len(buf: Buffer) -> int:
return buffer_line_current(buf).length
def translate_keyname(keyname: str) -> str:
match keyname:
case 'left ctrl':
return 'C-'
case 'left alt':
return 'A-'
case 'left meta':
return 'M-'
case 'left shift':
return 'S-'
case 'right ctrl':
return 'RC-'
case 'right alt':
return 'RA-'
case 'right meta':
return 'RM-'
case 'right shift':
return 'RS-'
# oswinx, oswiny = 1940,50
oswinx, oswiny = 20,50
@@ -88,7 +71,7 @@ clock = pg.time.Clock()
pg.init()
pg.font.init()
text_renderer = pg.font.SysFont('JetBrains Mono', 14)
text_renderer = pg.font.Font('JetBrainsMono-Regular.otf', 14)
screen = pg.display.set_mode((900, 900), pg.RESIZABLE)
pg.key.set_repeat(240, 10)
@@ -100,15 +83,22 @@ def load_file(path):
@dataclass
class GlobalContext:
cursor = Cursor(width=8, height=20)
text = load_file('../gux/test.c')
current_buffer = buffer_create(text, cursor)
text: str
current_buffer: Buffer
cursor: Cursor
# global ctx
global ctx
ctx = GlobalContext()
cursor = Cursor(width=8, height=20)
if len(sys.argv) <= 1:
ctx = GlobalContext("", buffer_create("", cursor), cursor)
else:
text = load_file(sys.argv[1])
ctx = GlobalContext(text, buffer_create(text, sys.argv[1], cursor), cursor)
from config import *
from editing import *
from keybindings import *
from treesitter import *
def main():
running = True
frame_count = 0
@@ -119,6 +109,7 @@ def main():
current_buffer = ctx.current_buffer
console = InteractiveInterpreter(locals())
console.runsource('from editing import *')
# We are hardcoding precedence here
while running:
dt = clock.tick(120) / 1000.0
screen.fill('white')
@@ -132,36 +123,100 @@ def main():
cursor_on = True
cursor_flash = 0
curr_line = buffer_line_current(current_buffer)
if event.mod == 64: # Right Ctrl
if event.key == pg.K_e:
cursor_go_end_line()
if event.key == pg.K_a:
cursor_go_beginning_line()
if event.key == pg.K_RETURN:
src = buffer_line_current(current_buffer).piece.get_text()
kb_to_test = Keybinding(pg.key.name(event.key), event.mod)
# print(kb_to_test, event.key, pg.key.name(event.key))
passthrough = True
for i in range(len(keymap_stack) - 1, -1, -1):
keymap = keymap_stack[i]
if kb_to_test in keymap.mapping:
keymap.mapping[kb_to_test]()
if not keymap.pinned:
keymap_stack.pop()
passthrough = False
break
else:
if keymap.pop_not_found:
keymap_stack.pop()
if not keymap.fallthrough:
passthrough = False
if len(kb_to_test.key) == 1:
print(f"{kb_to_test} not found")
break
# No keybinding found so send the directly to the buffer
# I think emacs instead binds every key to a function that sends the
# letter, we can evaluate if that's better later on
if passthrough and event.mod < 2 and event.unicode:
buffer_insert_char(event.unicode)
# if event.key == pg.K_RETURN:
# src = buffer_line_current(current_buffer).piece.get_text()
# try:
# eval(src[3:])
# except:
# print('error evaluating line: ' + src[3:])
console.runsource(src[3:])
elif event.key == pg.K_RETURN:
buffer_newline_at_pos()
elif event.key == pg.K_BACKSPACE:
buffer_delete_character_under_cursor()
elif event.key == pg.K_DELETE:
buffer_delete_next_character()
elif pg.key.name(event.key) in keymap:
keymap[pg.key.name(event.key)]()
# pass
# elif event.mod < 2:
elif event.unicode:
curr_line.piece.insert(event.unicode, cursor.col)
curr_line.length += 1
cursor.col += 1
cursor.want_col = cursor.col
# console.runsource(src[3:])
cur_col = 0
for i,curr_line in enumerate(current_buffer.lines):
render = text_renderer.render(curr_line.piece.get_text(), True, 'black')
line_txt = curr_line.piece.get_text()
ts_nodes = get_ts_nodes(curr_line.piece.get_text())
color_ranges = [(0, len(line_txt), 'black')]
# color_ranges = []
for node in ts_nodes:
if node.grammar_name == 'string_literal':
start_col, end_col = node.start_point[1], node.end_point[1]
print(node.start_point, node.end_point, node.text)
prev_start,prev_end,prev_col = color_ranges[-1]
color = 'green'
if start_col > 0:
color_ranges[-1] = (prev_start, start_col - 1,prev_col)
color_ranges.append((start_col, end_col, color))
if end_col < len(line_txt):
color_ranges.append((end_col + 1, end_col, color))
else:
color_ranges[0] = (start_col, end_col, color)
char_width = 8
for start,end,color in color_ranges:
render = text_renderer.render(line_txt[start:end], True, color)
screen.blit(render, (start * char_width, i * line_height))
continue
# We need to iterate over the treesitter tree, I think that for now
# it can be as easy as going until you find the 'string' grammar name
# So we need to check three conditions to decide when to draw
# 1: Did we hit a newline?
# 2: Did we hit EOF?
# 3: Did we run into a string
# We need to figure out a way to ask treesitter if some
# tokens are located at the current line that you're
# checking. Since we're operating line by line. Since we
# don't iterate character by character, we can't check if
# we ran into a single or double quote which I guess we
# could then use treesitter to part. It doesn't make sense
# to do that anyway because while that might solve this
# string task, ideally it's generalized so that as we walk
# the lines we're rendering, we're also checking
# treesitter grammar. But this is tricky because
# treesitter doesn't do anything line by line.
# So is what we're doing compatible then? maybe we should
# consider iterating over the lines in a different way and
# our current data structure won't scale at all. Maybe we
# should just use the piece table wholesale after all and
# not use insert mode to generate a temp buffer? That
# sounds like it'll complicate things a lot.
# Another thing would be to collect all the needed tokens?
line_syntax = syntax_highlights.get_line(i)
for grammar in line_syntax:
if grammar.name == 'string':
color = 'green'
else:
color = 'black'
string = curr_line.piece.string_at(grammar.start, grammar.end - grammar.start)
render = text_renderer.render(string, True, color)
screen.blit(render, (0, i * line_height))
if cursor_on and cursor_flash < 0.5:
+22
View File
@@ -0,0 +1,22 @@
#-*- mode: org -*-
#+TODO: TODO | DONE
#+STARTUP: show2levels
* DONE Render lines
* DONE Basic buffer implementation
* DONE Basic text editing functions
* DONE Basic keybindings system
* DONE Parse keymaps so config matches pygame keys
* DONE Keymap fallthrough/precedence stacking
* DONE Pass the file you want to edit as a command line argument
* DONE Save buffer function with a keybinding
* DONE Install, Import and play around with Treesitter
* DONE Highlight string literals with treesitter
* TODO Incorporate color regions into the buffer/line objects
* TODO Create a simple color map based on treesitter keywords
* TODO Walk the AST to generate the color regions
* TODO Update treesitter AST as the user edits the file
* TODO Add scrolling based on cursor position
* TODO Add reverse parsing of keymaps to show users what they're typing
+106
View File
@@ -0,0 +1,106 @@
from fide import *
from typing import List, Dict, Callable
from dataclasses import dataclass, field
@dataclass(slots=True, frozen=True)
class Keybinding:
key: str
modifiers: int
@dataclass(slots=True)
class Keymap:
name: str
pinned: bool
fallthrough: bool
pop_not_found: bool
mapping: Dict[[int,str],Callable] = field(default_factory=dict)
global keymappings
global keymap_stack
global reverse_binding
def modsym_to_flag(sym: str):
match sym:
case 'S': return 1
case 'C': return 64
case 'RC': return 128
case 'A': return 256
case 'RA': return 512
case 'M': return 1024
# TODO Get which keycode these are
case 'right meta':
return 'RM'
case 'right shift':
return 'RS'
return None
def symbol_to_num(sym: str):
match sym:
case '!': return '1'
case '@': return '2'
case '#': return '3'
case '$': return '4'
case '%': return '5'
case '^': return '6'
case '&': return '7'
case '*': return '8'
case '(': return '9'
case ')': return '0'
# TODO Get which keycode these are
return sym
def parse_keybinding(keybinding: str) -> str:
keys = keybinding.replace(' ', '').split('-')
if not keys:
# TODO: We need to return some kind of error
return None
keys_len = len(keys)
if keys_len > 1: # We have modifiers
modifiers = 0
for i in range(keys_len - 1):
flag = modsym_to_flag(keys[i])
if flag:
modifiers |= flag
else:
# TODO: Also throw some kind of parsing error
return None
else:
modifiers = 0
if len(keys[-1]) > 1: # We have a multi-stroke keybinding, we don't handle that yet
key = keys[-1]
else:
key = keys[-1][0]
if key.isupper():
modifiers |= 1
key = key.lower()
if not key.isalnum():
modifiers |= 1
key = symbol_to_num(key)
return Keybinding(key, modifiers)
keymap_stack = [
Keymap('insert', pinned=True, fallthrough=True, pop_not_found=False),
Keymap('normal', pinned=True, fallthrough=False, pop_not_found=False),
]
keymappings = {
'insert': keymap_stack[0],
'normal': keymap_stack[1],
}
# Do we even need this?
reverse_binding = {}
def kb(mode: str, binding: str, f: Callable):
keybinding = parse_keybinding(binding)
keymappings[mode].mapping[keybinding] = f
reverse_binding[keybinding] = binding
def keymap_push(keymap: Keymap):
keymap_stack.append(keymap)
def keymap_pop():
keymap_stack.pop()
+185
View File
@@ -0,0 +1,185 @@
package tide
import "core:os"
import "core:log"
import "core:math"
import "core:strings"
import fmt "core:fmt"
import sdl "vendor:sdl2"
import ttf "vendor:sdl2/ttf"
import txtedit "core:text/edit"
AppState :: struct {
buffer_dirty: bool,
}
SdlCtx :: struct {
window: ^sdl.Window,
surface: ^sdl.Surface,
renderer: ^sdl.Renderer,
font: ^ttf.Font,
ftex: ^sdl.Texture,
text_surface: ^sdl.Surface,
buffer_text: ^BufferText,
// font_data: ^FontData,
should_close: bool,
app_start: f64,
frame_start: f64,
frame_end: f64,
frame_elapsed: f64,
}
BufferText :: struct {
builder: strings.Builder,
}
FontData :: struct {
font: ^ttf.Font,
texture: ^sdl.Texture,
rect: sdl.Rect,
font_w: int,
font_h: int,
}
WIN_TITLE :: "FIDE"
WIN_X := i32(0)
WIN_Y := i32(100)
WIN_W := i32(950)
WIN_H := i32(1000)
WIN_FLAGS :: sdl.WindowFlags{.SHOWN}
state := AppState{}
ctx := SdlCtx{}
sdl_init :: proc() -> (ok: bool) {
ctx.app_start = f64(sdl.GetPerformanceCounter()) / f64(sdl.GetPerformanceFrequency())
if sdl.Init(sdl.INIT_VIDEO) != 0 {
return false
}
ctx.window = sdl.CreateWindow(WIN_TITLE, WIN_X, WIN_Y, WIN_W, WIN_H, WIN_FLAGS)
ctx.renderer = sdl.CreateRenderer(ctx.window, -1, {.ACCELERATED, .PRESENTVSYNC})
if ctx.renderer == nil {
log.errorf("sdl2.CreateRenderer failed.")
return false
}
if ttf.Init() != 0 {
log.errorf("sdl.TTF_Init failed.")
return false
}
font_path := cstring("../JetBrainsMono-Regular.otf")
ctx.font = ttf.OpenFont(font_path, 32)
if ctx.font == nil {
log.errorf("sdl.TTF_OpenFont failed.")
return false
}
bt := new(BufferText)
bt.builder = strings.builder_make(0, 512)
ctx.buffer_text = bt
append(&bt.builder.buf, "// Code goes here...")
str := strings.to_cstring(&ctx.buffer_text.builder)
color := sdl.Color{ 0, 0, 0, 255}
ctx.text_surface = ttf.RenderUTF8_Blended_Wrapped(ctx.font, str, color, 900);
ctx.ftex = sdl.CreateTextureFromSurface(ctx.renderer, ctx.text_surface);
state.buffer_dirty = true
return true
}
process_input :: proc() {
e: sdl.Event
for sdl.PollEvent(&e) {
#partial switch(e.type) {
case .QUIT:
ctx.should_close = true
case .KEYDOWN:
#partial switch(e.key.keysym.sym) {
case .ESCAPE:
ctx.should_close = true
case .BACKSPACE:
if strings.builder_len(ctx.buffer_text.builder) > 0 {
strings.pop_rune(&ctx.buffer_text.builder)
state.buffer_dirty = true
}
case .RETURN:
strings.write_rune(&ctx.buffer_text.builder, '\n')
state.buffer_dirty = true
}
case .TEXTINPUT:
key := string(cstring(raw_data(e.text.text[:])))
strings.write_string(&ctx.buffer_text.builder, key)
state.buffer_dirty = true
}
}
}
update :: proc() {
if state.buffer_dirty {
color := sdl.Color{ 0, 0, 0, 255}
str: cstring
if strings.builder_len(ctx.buffer_text.builder) > 0 {
str = strings.to_cstring(&ctx.buffer_text.builder)
} else {
str = cstring(" ")
}
ctx.text_surface = ttf.RenderUTF8_Blended_Wrapped(ctx.font, str, color, 900);
if (ctx.text_surface == nil) {
log.errorf(sdl.GetErrorString())
os.exit(1)
}
ctx.ftex = sdl.CreateTextureFromSurface(ctx.renderer, ctx.text_surface);
}
}
draw :: proc() {
sdl.SetRenderDrawColor(ctx.renderer, 250, 250, 250, 0xff)
sdl.RenderClear(ctx.renderer)
if ctx.ftex != nil {
src := sdl.Rect { 0, 0, ctx.text_surface.w, ctx.text_surface.h }
dest := sdl.Rect { 0, 0, ctx.text_surface.w, ctx.text_surface.h }
sdl.RenderCopy(ctx.renderer, ctx.ftex, &src, &dest)
}
sdl.RenderPresent(ctx.renderer)
}
main :: proc() {
context.logger = log.create_console_logger()
if ok := sdl_init(); !ok {
log.errorf(sdl.GetErrorString())
os.exit(1)
}
ctx.frame_start = ctx.app_start
ctx.frame_elapsed = 0.001
for !ctx.should_close {
process_input()
update()
draw()
ctx.frame_end = f64(sdl.GetPerformanceCounter()) / f64(sdl.GetPerformanceFrequency())
ctx.frame_elapsed = ctx.frame_end - ctx.frame_start
ctx.frame_start = ctx.frame_end
state.buffer_dirty = false
}
ttf.CloseFont(ctx.font)
sdl.DestroyWindow(ctx.window)
sdl.Quit()
}
+71
View File
@@ -0,0 +1,71 @@
package wm
import fmt "core:fmt"
import "core:os"
import "core:log"
import "core:math"
import "core:strings"
import x "vendor:x11/xlib"
main :: proc () {
display: ^x.Display
root: x.Window
attr: x.XWindowAttributes
start: x.XButtonEvent
display = x.OpenDisplay(nil)
if display == nil {
fmt.println("Could not open display")
os.exit(1)
}
x.GrabKey(display, i32(x.KeysymToKeycode(display, x.StringToKeysym("F1"))), {.Mod1Mask},
x.DefaultRootWindow(display), true, .GrabModeAsync, .GrabModeAsync);
// x.GrabButton(display, 1, Mod1Mask, DefaultRootWindow(dpy), true,
// ButtonPressMask|ButtonReleaseMask|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None);
// XGrabButton(display, 3, Mod1Mask, DefaultRootWindow(display), true,
// ButtonPressMask|ButtonReleaseMask|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None);
// x.SelectInput
// display,
// root,
// {.SubstructureRedirect, .SubstructureNotify});
// x.Synchronize(display, false);
for {
e: x.XEvent
x.NextEvent(display, &e)
fmt.println("Received Event: ", e)
#partial switch e.type {
case .KeyPress:
x.RaiseWindow(display, e.xkey.subwindow)
case .ButtonPress:
x.GetWindowAttributes(display, e.xbutton.subwindow, &attr)
start = e.xbutton
case .MotionNotify:
xdiff := e.xbutton.x_root - start.x_root;
ydiff := e.xbutton.y_root - start.y_root;
xpos := attr.x + (start.button==.Button1 ? xdiff : 0)
ypos := attr.y + (start.button==.Button1 ? ydiff : 0)
width := u32(max(1, attr.width + (start.button==.Button2 ? xdiff : 0)))
height := u32(max(1, attr.height + (start.button==.Button2 ? ydiff : 0)))
x.MoveResizeWindow(display, start.subwindow, xpos, ypos, width, height)
case .ButtonRelease:
// start.subwindow = nil;
}
// else if(ev.xbutton.subwindow != None)
// else if(ev.type == MotionNotify && start.subwindow != None)
// {
// int xdiff = ev.xbutton.x_root - start.x_root;
// int ydiff = ev.xbutton.y_root - start.y_root;
// XMoveResizeWindow(dpy, start.subwindow,
// attr.x + (start.button==1 ? xdiff : 0),
// attr.y + (start.button==1 ? ydiff : 0),
// MAX(1, attr.width + (start.button==3 ? xdiff : 0)),
// MAX(1, attr.height + (start.button==3 ? ydiff : 0)));
// }
// else if(ev.type == ButtonRelease)
// start.subwindow = None;
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
pygame-ce==2.3.2
pyropes==1.4
piece_table=0.0.3
+75
View File
@@ -0,0 +1,75 @@
from tree_sitter import Language, Parser, Node, TreeCursor, Tree
from typing import Tuple, List
from dataclasses import dataclass
# Language.build_library(
# # Store the library in the `build` directory
# 'build/compiled-languages.so',
# # Include one or more languages
# [
# '/home/joe/Repositories/tree-sitter/languages/tree-sitter-cpp',
# '/home/joe/Repositories/tree-sitter/languages/tree-sitter-c',
# '/home/joe/Repositories/tree-sitter/languages/tree-sitter-rust',
# '/home/joe/Repositories/tree-sitter/languages/tree-sitter-python',
# # 'vendor/tree-sitter-javascript',
# # 'vendor/tree-sitter-python'
# ]
# )
C_LANGUAGE = Language('build/compiled-languages.so', 'c')
# CPP_LANGUAGE = Language('build/compiled-languages.so', 'cpp')
# PYTHON_LANGUAGE = Language('build/compiled-languages.so', 'python')
# PY_LANGUAGE = Language('build/my-languages.so', 'python')
c_parser: Parser = Parser()
c_parser.set_language(C_LANGUAGE)
# cpp_parser = Parser()
# cpp_parser.set_language(CPP_LANGUAGE)
# python_parser = Parser()
# python_parser.set_language(PYTHON_LANGUAGE)
def traverse_tree(tree: Tree):
cursor = tree.walk()
reached_root = False
while reached_root == False:
yield cursor.node
if cursor.goto_first_child():
continue
if cursor.goto_next_sibling():
continue
retracing = True
while retracing:
if not cursor.goto_parent():
retracing = False
reached_root = True
if cursor.goto_next_sibling():
retracing = False
# with open('treesitter.py', 'r') as f:
# def dfs(node: Node, indent_level):
# indent = "".join([' ' * (indent_level * 4)])
# # if node.grammar_name != node.type:
# # print(node)
# print(indent, '-', f"({node.type})", # node.range,
# node.start_point, node.end_point, node.text)
# for n in node.children:
# dfs(n, indent_level + 1)
# dfs(root, 0)
@dataclass
class ParsedRange:
name: str
line_num: int
crange: Tuple[int, int]
# def get_ts_nodes(text: str) -> List[ParsedRange]:
def get_ts_nodes(text: str) -> List[Node]:
tree = c_parser.parse(bytes(text, 'utf-8'))
# root: Node = tree.root_node
# query = C_LANGUAGE.query(root.sexp())
return [n for n in traverse_tree(tree)]