Compare commits
18
Commits
9d725d0b2f
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2d8d37a60 | ||
|
|
00be1d2e23 | ||
|
|
56f3048d2d | ||
|
|
039b671973 | ||
|
|
3112ac37e2 | ||
|
|
dfdadef143 | ||
|
|
e8ea80ff66 | ||
|
|
7228bf3201 | ||
|
|
5cb2fd0fd7 | ||
|
|
27ef7d7c93 | ||
|
|
d690d7a2a3 | ||
|
|
8356ca6a25 | ||
|
|
44ffba0aa4 | ||
|
|
4d5ad1a562 | ||
|
|
cd8c8739b0 | ||
|
|
43450a8c37 | ||
|
|
53ddd7be9f | ||
|
|
6ccf44bef5 |
No files matched your search
+4
-2
@@ -1,2 +1,4 @@
|
|||||||
/rust-version/
|
/cosmic-text/
|
||||||
/main
|
/__pycache__/
|
||||||
|
/nim-version/fide
|
||||||
|
/build/
|
||||||
+36
@@ -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.
@@ -0,0 +1,24 @@
|
|||||||
|
from editing import *
|
||||||
|
from keybindings import *
|
||||||
|
|
||||||
|
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)
|
||||||
File renamed without changes.
@@ -0,0 +1,43 @@
|
|||||||
|
#-*- mode: org -*-
|
||||||
|
#+TODO: TODO | DONE
|
||||||
|
#+STARTUP: show2levels
|
||||||
|
|
||||||
|
|
||||||
|
* DONE Create SDL hello world
|
||||||
|
* DONE Research which data structure to use (ext/rope.h)
|
||||||
|
* DONE Figure out how to render text in SDL (font texture atlas)
|
||||||
|
* DONE Insert text into rope from keyboard presses
|
||||||
|
* DONE Render rope
|
||||||
|
* DONE Handle colors and font texture blitting
|
||||||
|
* DONE Get backspace working
|
||||||
|
* TODO Scale text with C-+ and C--
|
||||||
|
* TODO Read a file from disk and display it
|
||||||
|
* TODO Create a cursor
|
||||||
|
* TODO Add basic keybinding system
|
||||||
|
* TODO Move cursor with arrow keys
|
||||||
|
* TODO Save file
|
||||||
|
* TODO Render each line in a different color
|
||||||
|
To help figuring out how to color things for syntax highlighting
|
||||||
|
* TODO Install treesitter package
|
||||||
|
* TODO Color different parts of the buffer based on AST
|
||||||
|
* TODO Create vertical/horizontal window splits
|
||||||
|
* TODO Make splits resizable
|
||||||
|
* TODO Buffer lists per pane
|
||||||
|
* TODO Pane owned sub-panes
|
||||||
|
* TODO Promote or demote panes as sub-panes
|
||||||
|
* TODO Pinnable/dedicated buffers
|
||||||
|
Panes and their sub-panes can have buffers pinned to them that require a
|
||||||
|
keybinding to override.
|
||||||
|
* TODO Custom buffer and pane variables
|
||||||
|
* TODO Floating splits
|
||||||
|
* TODO Buffers tags/ids
|
||||||
|
* TODO Pane attributes that modify it's behavior
|
||||||
|
- [ ] Pinnable
|
||||||
|
- [ ] Toggleable
|
||||||
|
- [ ] Overtake?
|
||||||
|
- [ ] Popup
|
||||||
|
* TODO Narrowing/widening
|
||||||
|
* TODO Read-only mode
|
||||||
|
* TODO Implement key chording
|
||||||
|
* TODO Implement modal editing modes
|
||||||
|
* TODO Keymap layering stack
|
||||||
File renamed without changes.
Executable
BIN
Binary file not shown.
@@ -4,13 +4,17 @@
|
|||||||
#include <SDL2/SDL_render.h>
|
#include <SDL2/SDL_render.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <ext/rope>
|
||||||
|
|
||||||
|
using namespace __gnu_cxx;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
//Screen dimension constants
|
//Screen dimension constants
|
||||||
const int SCREEN_WIDTH = 1024;
|
const int SCREEN_WIDTH = 1024;
|
||||||
const int SCREEN_HEIGHT = 768;
|
const int SCREEN_HEIGHT = 768;
|
||||||
|
|
||||||
|
char *test_string = "This is a test string for fide";
|
||||||
|
|
||||||
enum KeyPressSurfaces
|
enum KeyPressSurfaces
|
||||||
{
|
{
|
||||||
KEY_PRESS_SURFACE_DEFAULT,
|
KEY_PRESS_SURFACE_DEFAULT,
|
||||||
@@ -38,8 +42,8 @@ void render_char(char key, int row, int col, SDL_Renderer* renderer, SDL_Texture
|
|||||||
|
|
||||||
dstrect.x = col;
|
dstrect.x = col;
|
||||||
dstrect.y = row;
|
dstrect.y = row;
|
||||||
dstrect.w = 80;
|
dstrect.w = 30;
|
||||||
dstrect.h = 80;
|
dstrect.h = 30;
|
||||||
|
|
||||||
SDL_Rect srcrect = get_srcrect(key);
|
SDL_Rect srcrect = get_srcrect(key);
|
||||||
SDL_RenderCopy(renderer, font, &srcrect, &dstrect);
|
SDL_RenderCopy(renderer, font, &srcrect, &dstrect);
|
||||||
@@ -116,17 +120,35 @@ int main(int argc, char *argv[]) {
|
|||||||
int keyPressed = -1;
|
int keyPressed = -1;
|
||||||
int rowCurr = 0;
|
int rowCurr = 0;
|
||||||
int colCurr = 0;
|
int colCurr = 0;
|
||||||
string text = "Some text";
|
float scale = 50;
|
||||||
|
rope<char> buffer;
|
||||||
while (quit == false) {
|
while (quit == false) {
|
||||||
while (SDL_PollEvent( &e)) {
|
while (SDL_PollEvent( &e)) {
|
||||||
if (e.type == SDL_QUIT) {
|
if (e.type == SDL_QUIT) {
|
||||||
quit = true;
|
quit = true;
|
||||||
}
|
} else if (e.type == SDL_KEYDOWN) {
|
||||||
else if (e.type == SDL_KEYDOWN) {
|
|
||||||
char key = e.key.keysym.sym;
|
char key = e.key.keysym.sym;
|
||||||
if (key >= (char)'!' || key <= (char)'~') {
|
char mod = e.key.keysym.mod;
|
||||||
keyPressed = key;
|
if (key == '\r') {
|
||||||
|
buffer.push_back('\n');
|
||||||
}
|
}
|
||||||
|
if (key == '-' && mod & KMOD_LCTRL) {
|
||||||
|
cout << "Minu" << endl;
|
||||||
|
}
|
||||||
|
if (key == '=' && mod & KMOD_LCTRL) {
|
||||||
|
cout << "Ploos" << endl;
|
||||||
|
}
|
||||||
|
if (key >= (char)'!' || key <= (char)'~') {
|
||||||
|
// keyPressed = key;
|
||||||
|
buffer.push_back(key);
|
||||||
|
}
|
||||||
|
if (key == SDLK_BACKSPACE) {
|
||||||
|
if (buffer.size() >= 2) {
|
||||||
|
buffer.pop_back();
|
||||||
|
buffer.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cout << key << endl;
|
||||||
// switch (e.key.keysym.sym) {
|
// switch (e.key.keysym.sym) {
|
||||||
// case SDLK_
|
// case SDLK_
|
||||||
// }
|
// }
|
||||||
@@ -134,11 +156,23 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
// SDL_FillRect( screenSurface, NULL, rgbMap);
|
// SDL_FillRect( screenSurface, NULL, rgbMap);
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
SDL_SetTextureColorMod(fontTexture, 0xFF, 0, 0);
|
SDL_SetTextureColorMod(fontTexture, 0x0, 0, 0);
|
||||||
for (int i = 0; i < 10; i++) {
|
rope<char>::iterator it;
|
||||||
render_char(text[i], rowCurr, colCurr++ * 50, renderer, fontTexture);
|
for (it = buffer.mutable_begin(); it != buffer.mutable_end(); it++) {
|
||||||
|
if (*it == '\n') {
|
||||||
|
rowCurr += 1;
|
||||||
|
colCurr = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*it == '\r') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*it >= (char)'!' || *it <= (char)'~') {
|
||||||
|
render_char(*it, rowCurr * 70, colCurr++ * 50, renderer, fontTexture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
colCurr = 0;
|
colCurr = 0;
|
||||||
|
rowCurr = 0;
|
||||||
// Draw(keyPressed, renderer, fontTexture);
|
// Draw(keyPressed, renderer, fontTexture);
|
||||||
SDL_RenderPresent(renderer);
|
SDL_RenderPresent(renderer);
|
||||||
}
|
}
|
||||||
File renamed without changes.
+150
@@ -0,0 +1,150 @@
|
|||||||
|
from fide import *
|
||||||
|
|
||||||
|
def cursor_go_left():
|
||||||
|
buf = ctx.current_buffer
|
||||||
|
cursor = ctx.current_buffer.cursor
|
||||||
|
if cursor.col == 0:
|
||||||
|
if cursor.line_num > 0:
|
||||||
|
cursor.col = buf.lines[cursor.line_num-1].length
|
||||||
|
cursor.want_col = cursor.col
|
||||||
|
cursor.line_num -= 1
|
||||||
|
else:
|
||||||
|
cursor.col = max(0, cursor.col - 1)
|
||||||
|
cursor.want_col = cursor.col
|
||||||
|
|
||||||
|
def cursor_go_right():
|
||||||
|
buf = ctx.current_buffer
|
||||||
|
cursor = ctx.current_buffer.cursor
|
||||||
|
if cursor.col == buffer_line_len(buf):
|
||||||
|
if cursor.line_num < buffer_total_lines(buf) - 1:
|
||||||
|
cursor.col = 0
|
||||||
|
cursor.want_col = cursor.col
|
||||||
|
cursor.line_num += 1
|
||||||
|
else:
|
||||||
|
cursor.col = min(buffer_line_len(buf), cursor.col + 1)
|
||||||
|
cursor.want_col = cursor.col
|
||||||
|
|
||||||
|
def cursor_go_down():
|
||||||
|
buf = ctx.current_buffer
|
||||||
|
cursor = ctx.current_buffer.cursor
|
||||||
|
if cursor.line_num == len(buf.lines):
|
||||||
|
return
|
||||||
|
cursor.line_num = min(len(buf.lines) - 1, cursor.line_num + 1)
|
||||||
|
line_len = buf.lines[cursor.line_num].length
|
||||||
|
cursor.col = min(cursor.want_col, line_len)
|
||||||
|
|
||||||
|
def cursor_go_up():
|
||||||
|
buf = ctx.current_buffer
|
||||||
|
cursor = ctx.current_buffer.cursor
|
||||||
|
if cursor.line_num == 0:
|
||||||
|
return
|
||||||
|
cursor.line_num = max(0, cursor.line_num - 1)
|
||||||
|
line_len = buf.lines[cursor.line_num].length
|
||||||
|
# TODO: This should be global
|
||||||
|
cursor.col = min(cursor.want_col, line_len)
|
||||||
|
|
||||||
|
def cursor_go_line_beginning():
|
||||||
|
buf = ctx.current_buffer
|
||||||
|
buf.cursor.col = 0
|
||||||
|
buf.cursor.want_col = buf.cursor.col
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
def buffer_newline_at_pos():
|
||||||
|
buf = ctx.current_buffer
|
||||||
|
curr_line = buffer_line_current(buf)
|
||||||
|
cursor = buf.cursor
|
||||||
|
if cursor.col < buffer_line_len(buf):
|
||||||
|
remainder = curr_line.length - cursor.col
|
||||||
|
line = curr_line.piece.string_at(cursor.col, remainder)
|
||||||
|
curr_line.piece.delete(cursor.col, remainder)
|
||||||
|
curr_line.length -= remainder
|
||||||
|
else:
|
||||||
|
line = ""
|
||||||
|
buffer_insert_line_below(buf, line)
|
||||||
|
cursor.line_num += 1
|
||||||
|
# Eventually when we need to be indentation aware
|
||||||
|
cursor.col = 0
|
||||||
|
cursor.want_col = 0
|
||||||
|
|
||||||
|
|
||||||
|
def buffer_delete_character_under_cursor():
|
||||||
|
buf = ctx.current_buffer
|
||||||
|
cursor = buf.cursor
|
||||||
|
if cursor_pos(cursor) != (0,0):
|
||||||
|
if cursor.col == 0:
|
||||||
|
curr_line = buf.lines.pop(cursor.line_num)
|
||||||
|
cursor.line_num -= 1
|
||||||
|
above_row = buf.lines[cursor.line_num]
|
||||||
|
above_row.piece.insert(curr_line.piece.get_text(), above_row.length)
|
||||||
|
cursor.col = above_row.length
|
||||||
|
cursor.want_col = cursor.col
|
||||||
|
above_row.length += curr_line.length
|
||||||
|
else:
|
||||||
|
curr_line = buffer_line_current(buf)
|
||||||
|
curr_line.piece.delete(cursor.col - 1, 1)
|
||||||
|
curr_line.length -= 1
|
||||||
|
cursor.col = max(0, cursor.col - 1)
|
||||||
|
cursor.want_col = cursor.col
|
||||||
|
|
||||||
|
def buffer_delete_next_character():
|
||||||
|
buf = ctx.current_buffer
|
||||||
|
cursor = buf.cursor
|
||||||
|
curr_line = buffer_line_current(buf)
|
||||||
|
if cursor.col >= buffer_line_len(buf):
|
||||||
|
if cursor.line_num < len(buf.lines) - 1:
|
||||||
|
below_row = buf.lines.pop(cursor.line_num+1)
|
||||||
|
if below_row.length > 0:
|
||||||
|
curr_line.piece.insert(below_row.piece.get_text(), curr_line.length)
|
||||||
|
curr_line.length += below_row.length
|
||||||
|
cursor.want_col = cursor.col
|
||||||
|
else:
|
||||||
|
if curr_line.length >= 1:
|
||||||
|
curr_line.piece.delete(cursor.col, 1)
|
||||||
|
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)
|
||||||
|
curr_line.piece.insert(text, cursor.col)
|
||||||
|
text_len = len(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)
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
import pygame as pg
|
||||||
|
from pygame import Rect
|
||||||
|
|
||||||
|
from piece_table import PieceTable
|
||||||
|
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import copy
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from code import InteractiveConsole, InteractiveInterpreter
|
||||||
|
|
||||||
|
@dataclass(slots=True)
|
||||||
|
class Cursor:
|
||||||
|
width: float
|
||||||
|
height: float
|
||||||
|
line_num: int = 0
|
||||||
|
col: int = 0
|
||||||
|
want_col: int = 0
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
@dataclass(slots=True)
|
||||||
|
class Buffer:
|
||||||
|
lines: List[Line]
|
||||||
|
cursor: Cursor
|
||||||
|
filepath: Optional[str] = None
|
||||||
|
|
||||||
|
def cursor_pos(cursor: Cursor):
|
||||||
|
return (cursor.line_num, cursor.col)
|
||||||
|
|
||||||
|
def cursor_draw(cursor: Cursor):
|
||||||
|
rect = Rect(cursor.col * cursor.width, cursor.line_num * cursor.height, 3, cursor.height)
|
||||||
|
pg.draw.rect(screen, "black", rect, width=2)
|
||||||
|
|
||||||
|
def line_create(text: str) -> Line:
|
||||||
|
return Line(PieceTable(text), len(text))
|
||||||
|
|
||||||
|
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, filepath)
|
||||||
|
|
||||||
|
def buffer_line_current(buf: Buffer) -> Line:
|
||||||
|
return buf.lines[buf.cursor.line_num]
|
||||||
|
|
||||||
|
def buffer_line_num(buf: Buffer) -> int:
|
||||||
|
return buf.cursor.line_num
|
||||||
|
|
||||||
|
def buffer_total_lines(buf: Buffer) -> int:
|
||||||
|
return len(buf.lines)
|
||||||
|
|
||||||
|
def buffer_line_len(buf: Buffer) -> int:
|
||||||
|
return buffer_line_current(buf).length
|
||||||
|
|
||||||
|
|
||||||
|
# oswinx, oswiny = 1940,50
|
||||||
|
oswinx, oswiny = 20,50
|
||||||
|
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (oswinx,oswiny)
|
||||||
|
os.environ['SDL_VIDEO_X11_WMCLASS'] = "pygame"
|
||||||
|
clock = pg.time.Clock()
|
||||||
|
|
||||||
|
pg.init()
|
||||||
|
pg.font.init()
|
||||||
|
text_renderer = pg.font.Font('JetBrainsMono-Regular.otf', 14)
|
||||||
|
screen = pg.display.set_mode((900, 900), pg.RESIZABLE)
|
||||||
|
pg.key.set_repeat(240, 10)
|
||||||
|
|
||||||
|
def load_file(path):
|
||||||
|
with open(path, 'r', encoding='utf-8') as f:
|
||||||
|
text = f.read()
|
||||||
|
f.close()
|
||||||
|
return text
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GlobalContext:
|
||||||
|
text: str
|
||||||
|
current_buffer: Buffer
|
||||||
|
cursor: Cursor
|
||||||
|
|
||||||
|
# global ctx
|
||||||
|
global ctx
|
||||||
|
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
|
||||||
|
cursor_flash = 0
|
||||||
|
cursor_on = True
|
||||||
|
line_height = 20
|
||||||
|
cursor = ctx.cursor
|
||||||
|
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')
|
||||||
|
frame_count += 1
|
||||||
|
for event in pg.event.get():
|
||||||
|
# print(pg.event.event_name(event.type))
|
||||||
|
if event.type == pg.QUIT:
|
||||||
|
running = False
|
||||||
|
if event.type == pg.KEYDOWN:
|
||||||
|
# print(pg.key.name(event.key))
|
||||||
|
cursor_on = True
|
||||||
|
cursor_flash = 0
|
||||||
|
curr_line = buffer_line_current(current_buffer)
|
||||||
|
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:])
|
||||||
|
|
||||||
|
cur_col = 0
|
||||||
|
for i,curr_line in enumerate(current_buffer.lines):
|
||||||
|
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:
|
||||||
|
cursor_draw(cursor)
|
||||||
|
elif cursor_on:
|
||||||
|
cursor_on = False
|
||||||
|
cursor_flash = 0
|
||||||
|
elif not cursor_on and cursor_flash > 0.5:
|
||||||
|
cursor_on = True
|
||||||
|
cursor_flash = 0
|
||||||
|
cursor_flash += dt
|
||||||
|
|
||||||
|
pg.display.flip()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -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
@@ -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()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
require "sdl2"
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import os
|
||||||
|
import std/strformat
|
||||||
|
import std/strutils
|
||||||
|
import sdl2
|
||||||
|
import sdl2/ttf
|
||||||
|
|
||||||
|
const
|
||||||
|
WindowWidth = 900
|
||||||
|
WindowHeight = 900
|
||||||
|
OSWinX: cint = 1940
|
||||||
|
OSWinY: cint = 50
|
||||||
|
LineHeight = 20
|
||||||
|
|
||||||
|
type
|
||||||
|
Cursor = object
|
||||||
|
width: float
|
||||||
|
height: float
|
||||||
|
line_num: int = 0
|
||||||
|
col: int = 0
|
||||||
|
want_col: int = 0
|
||||||
|
|
||||||
|
Buffer = object
|
||||||
|
lines: seq[string]
|
||||||
|
cursor: Cursor
|
||||||
|
|
||||||
|
func bufferNew(text: string, cursor: Cursor): Buffer =
|
||||||
|
return Buffer(lines: text.split("\n"), cursor: cursor)
|
||||||
|
|
||||||
|
type SDLException = object of Defect
|
||||||
|
|
||||||
|
template sdlFailIf(condition: typed, reason: string) =
|
||||||
|
if condition: raise SDLException.newException(
|
||||||
|
reason & ", SDL error " & $getError()
|
||||||
|
)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
sdlFailIf(not sdl2.init(INIT_VIDEO or INIT_TIMER or INIT_EVENTS)):
|
||||||
|
"SDL2 initialization failed"
|
||||||
|
defer: sdl2.quit()
|
||||||
|
|
||||||
|
let window = createWindow(
|
||||||
|
title = "TODE",
|
||||||
|
x = OSWinX,
|
||||||
|
y = OSWinY,
|
||||||
|
w = WindowWidth,
|
||||||
|
h = WindowHeight,
|
||||||
|
flags = SDL_WINDOW_SHOWN or SDL_WINDOW_RESIZABLE
|
||||||
|
)
|
||||||
|
|
||||||
|
sdlFailIf window.isNil: "window could not be created"
|
||||||
|
defer: window.destroy()
|
||||||
|
|
||||||
|
let renderer = createRenderer(
|
||||||
|
window = window,
|
||||||
|
index = -1,
|
||||||
|
flags = Renderer_Accelerated or Renderer_PresentVsync or Renderer_TargetTexture
|
||||||
|
)
|
||||||
|
sdlFailIf renderer.isNil: "renderer could not be created"
|
||||||
|
defer: renderer.destroy()
|
||||||
|
|
||||||
|
sdlFailIf(not ttfInit()): "SDL_TTF initialization failed"
|
||||||
|
defer: ttfQuit()
|
||||||
|
|
||||||
|
let font = ttf.openFont("liberation-sans.ttf", 18)
|
||||||
|
sdlFailIf font.isNil: "font could not be created"
|
||||||
|
|
||||||
|
sdlFailIf(not setHint("SDL_RENDER_SCALE_QUALITY", "2")):
|
||||||
|
"Linear texture filtering could not be enabled"
|
||||||
|
|
||||||
|
var
|
||||||
|
running = true
|
||||||
|
dt: float32
|
||||||
|
counter: uint64
|
||||||
|
previousCounter: uint64
|
||||||
|
|
||||||
|
let
|
||||||
|
textFile = readFile("../../gux/test.c")
|
||||||
|
cursor = Cursor(width: 8, height: LineHeight)
|
||||||
|
buffer = bufferNew(textFile, cursor)
|
||||||
|
|
||||||
|
counter = getPerformanceCounter()
|
||||||
|
|
||||||
|
while running:
|
||||||
|
previousCounter = counter
|
||||||
|
counter = getPerformanceCounter()
|
||||||
|
|
||||||
|
dt = (counter - previousCounter).float / getPerformanceFrequency().float
|
||||||
|
|
||||||
|
var event = defaultEvent
|
||||||
|
|
||||||
|
while pollEvent(event):
|
||||||
|
case event.kind
|
||||||
|
of QuitEvent:
|
||||||
|
running = false
|
||||||
|
break
|
||||||
|
of KeyDown:
|
||||||
|
discard
|
||||||
|
of KeyUp:
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
renderer.setDrawColor 255, 255, 255, 255 # black
|
||||||
|
renderer.clear()
|
||||||
|
|
||||||
|
for i, line in buffer.lines:
|
||||||
|
if isEmptyOrWhitespace(line):
|
||||||
|
continue
|
||||||
|
let
|
||||||
|
textColor = color(0, 0, 0, 0)
|
||||||
|
surface = ttf.renderTextBlended(font, line, textColor)
|
||||||
|
texture = renderer.createTextureFromSurface(surface)
|
||||||
|
|
||||||
|
sdlFailIf surface.isNil: "Could not render text surface"
|
||||||
|
var r = rect(0, cint(i * 20), surface.w, surface.h)
|
||||||
|
|
||||||
|
renderer.copy texture, nil, addr r
|
||||||
|
|
||||||
|
surface.freeSurface
|
||||||
|
defer: texture.destroy
|
||||||
|
|
||||||
|
renderer.present()
|
||||||
|
|
||||||
|
main()
|
||||||
Binary file not shown.
@@ -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()
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pygame-ce==2.3.2
|
||||||
|
piece_table=0.0.3
|
||||||
@@ -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)]
|
||||||
|
|
||||||
|
|
||||||
Reference in new issue
Block a user