Code Monkey home page Code Monkey logo

viw's Introduction

Viw, the ghetto editor

VI Worsened, a lightweight and fun VI clone. Inspired by the 6-domino-cascade from the React world.

DEMO

https://vimeo.com/205701269

Dependencies

  • gcc
  • ncurses
# Fedora
sudo dnf install ncurses-devel

# Debian/Ubuntu
sudo apt-get install libncurses5-dev

Installation & usage

git clone https://github.com/lpan/viw
cd viw/
make build
./viw [filename]

Using mingw compiler on Windows, you need to install mingw-w64-x86_64-ncurses

pacman -S mingw-w64-x86_64-ncurses
mingw32-make build

Supported keybindings

  • j Cursor down
  • k Cursor up
  • h Cursor left
  • l Cursor right
  • 0 Cursor to the beginning of the line
  • $ Cursor to the end of the line
  • i Insert before
  • I Insert at the beginning of the line
  • a Insert after
  • A Insert at the end of the line
  • o Append line then insert
  • O Prepend line then insert
  • dd Delete line under the cursor
  • gg Go to the first line of the file
  • G Go the last line of the file
  • u Undo
  • r Redo (Unstable)

Supported EX mode commands

  • :q quit
  • :w save
  • :wq save then quit

Hacking

Feel free to contribute! :)

How does it work

  1. initiate the state
  • Read file to buffer.
  • Set up interface with ncurses
  1. Listen to keyboard events.
  • Each supported keybinding is mapped to a method that mutates the buffer
  1. Run update_state(st) and render_update(st)
  • Similar to selectors in redux, update_state(state_t *st) will update all the computed properties (such as cursor position, rows to be displayed on the screen, etc) according to the new mutated buffer state.
  • render_update(state_t *st) will actually render everything on the screen according to the result from update_state().
  1. Goto step 2

Undo & Redo

Viw's undo & redo functionality is based on the state machine replication principle

  1. Initialization:
    • Deep clone the initial buffer.
    • Initialize two stacks (history stack and redo stack).
  2. Capture all state-mutating functions and their payloads and push it on to the history stack.
  3. When the user hits undo:
    • Pop the history stack and the push the result onto redo stack.
    • Clone the initial buffer and apply all the commands saved in the history stack on top of it.
  4. When the user hits redo:
    • Pop the redo stack and apply the command immediately onto the current buffer.
  5. Clear the redo stack when a command gets pushed to the history stack by the user.

See https://github.com/lpan/viw/blob/master/src/controller.c#L152 for more details

Hierarchy of the states

Our main state object has two children states, namely buffer and screen. This seperation makes it easier to perform unit tests against the buffer. It also facilitates the migration to a different rendering library in the future.

The State

  • The parent state stores computed states that depend on the buffer and/or screen. Those states include cursor positions, aount of space reserved for line numbers, etc.

The Buffer

  • The buffer is represented as a two dimensional doubly linked list. This allows conatant time line/char deletion and insertion. Go to buffer.h for more information.
  • Buffer is only allowed to be modified by the methods declared in buffer.h

The Screen

  • The screen is the state of the interface between viw and GNU ncurses. It stores information such as pointers to the ncurses windows, etc. You can learn more about it at screen.h.

viw's People

Contributors

azhng avatar lpan avatar mattn avatar mrkajetanp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

viw's Issues

make test-state fails with file not compiling

./state-test.c: In function ‘test_update_cursor’:
./state-test.c:15:3: warning: implicit declaration of function ‘update_cursor_position’ [-Wimplicit-function-declaration]
   update_cursor_position(st);
   ^~~~~~~~~~~~~~~~~~~~~~
./state-test.c: In function ‘test_update_display’:
./state-test.c:29:3: warning: implicit declaration of function ‘update_display’; did you mean ‘test_update_display’? [-Wimplicit-function-declaration]
   update_display(st);
   ^~~~~~~~~~~~~~
   test_update_display

I found a function called update_state in state.h that might be the update_display you refer to in the test. For the second error, update_display is in state.c but I don't how it can be made visible by including just state.h in the test.

:knife: ncurses

It will be a fun challenge to replace ncurses with a custom and lightweight rendering lib

Use static analyzer to find potential memory bugs

When writing C we must ensure we code safely, especially with memory functions, i.e. we don't use-after-free or double-free or null-dereference...

Tools to help discover these issues exist, e.g. splint, cppcheck, infer...

Here is a run of infer against your codebase:

$ infer run -- make
Capturing in make/cc mode...
Found 9 source files to analyze in /dir/viw/infer-out
Starting analysis...

legend:
  "F" analyzing a file
  "." analyzing a procedure

FFFFFFFF...............................F..............................................................................

Found 14 issues

src/buffer.c:40: error: NULL_DEREFERENCE
  pointer `ec` last assigned on line 36 could be null and is dereferenced at line 40, column 3
  38.     echar_t *prev = NULL;
  39.
  40. >   ec->c = c;
  41.     ec->prev = NULL;
  42.     ec->next = NULL;

src/buffer.c:113: error: NULL_DEREFERENCE
  pointer `buf` last assigned on line 111 could be null and is dereferenced at line 113, column 3
  111.     buffer_t *buf = malloc(sizeof(buffer_t));
  112.
  113. >   buf->num_rows = 0;
  114.     buf->head = NULL;
  115.     buf->last = NULL;

<...SNIP...>


Summary of the reports

  NULL_DEREFERENCE: 14

(Most issues detected by infer here are due to not checking if malloc failed, but there is one where a fopen is not checked either.)

I suggest you employ some static analyzers to help you check your code as part of your regular development process (and before accepting PRs). Good luck!

Links:

end of line not updated properly

Make your text file look like the following

a
some text
dank mr goose mr goose is the best

navigate to the last line and hit dd.

the cursor position will be wrong

TODO

Features

  • gg, G

  • Insert rows with o, O, Enter in insert mode

  • Delete rows with dd, Backspace in insert mode

  • Mode indicator on status line

  • Line numbers

  • Line wrapping (if num of chars of a line exceeds terminal width) this is a fun one

  • Responsive (changes layout as the user resizes their terminal)

  • Undo/redo

Bugs

  • Delay when exiting from insert mode

Deleting only line shows corruption.

If you open a file with only a single-line, and then delete it, you see corrupt data.

Enter one line of text to a file, then edit it:

  deagol ~/viw $ echo "foo" > text
  deagol ~/viw $ ./viw text

Once loaded press dd. Notice the first line shows gibberish?

Undo & Redo

The idea

The goal is to use command pattern and event sourcing. We model all state mutation methods as commands and register them in a stack. When the user hits undo button, we pop the first command and replay all the commands on the initial buffer. the popped command will be added to undo_stack. As for redo, we will pop the undo_stack and apply the command to the current buffer.

TODOs

  • Refactor state->buffer into state->text_buffer and state->status_line_buffer.

  • Clone text_buffer by value (cloning a 2d doubly linked list 😛 )

  • undo_stack and history_stack

Two small suggestions

Thanks for sharing this and writing up your experiences making it! As the title says, I have two small suggestions.

First, and just fyi for your README, this built for me on macOS (10.12.6) without trouble (I already have the build tools installed for gcc, but many people checking out the repo will too). I have a fair number of packages installed via Homebrew already, but I haven't installed ncurses. Anyhow, you might mention that Apple users can likely try it out too.

Second, maybe consider swapping your default task in the Makefile from run to build? I found it disconcerting to hit make and to end up in the editor. I found it even more disconcerting to have the executable disappear after I quit it! I like the neatness of run for people just trying it out, but it was confusing at first. However you go on the default, maybe say something in the README about the Makefile. (All of this said, it's my own fault for not reading the Makefile before I ran make!)

Refactor!!!

state.c is getting messy, I should refactor buffer out of state

dd til the end of the world

Make your text file look like the following

lol
ay lmao
mr goose da worst
jk
mr goose da best

then move your cursor to my favourite line (mr goose da best).

Keep hitting dd until your terminal explodes

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.