Coder Social home page Coder Social logo

synless's Introduction

Synless

This whole project is in a pre-alpha stage. Even the design documents are under construction at best. Synless does not yet exist.


Synless is a hypothetical tree editor. It hopes to one day grow up to be a real tree editor. It aims to:

  • Provide better editing commands, that act directly on the structure of the program, rather than on its textual representation.
  • Eliminate the need for weird encoding details like escape sequences (I'm looking at you, quadruple backslashes).
  • Make features and plugins much easier to write, by always knowing the exact structure of the document. (It can do this because it never has to try to parse an incomplete and syntactically invalid program.)
  • Make it easy to design new structured document formats, and to provide an editor for them that can never create an invalid document.
  • End formatting wars by delegating formatting choices to the same status as style files.

Synless is not:

  • A text editor.
  • A tree editor built on top of a text editor. There's no gap buffer. It's really just a tree.
  • A language workbench. Synless will not help you define a language semantics or perform static analysis.

To learn more:

Why Synless? And why "Synless"?

Synless Walkthrough

The Synless Documentation (to come)

The Synless Design Documentation (for developers)

An Incomplete Survey of Tree Editors

synless's People

Contributors

e-matteson avatar justinpombrio avatar

Stargazers

 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

Forkers

silky mtilda

synless's Issues

Implement new cut/delete/paste commands

New primitives:

  • cut (leaving a hole behind)
  • clear (leaving a hole behind)
  • remove (only possible if the parent is flexible)
  • paste-swap (pushing the replaced node onto the clipboard)
  • pop clipboard (discarding the node)

Remove spanic and spect macros

If we really want to add an extra "internal error in synless" message when panicking, we can catch the panic in main instead of using a separate spanic macro. spect is unused anyway.

Split demo crate into multiple crates

Decide how to divide things up.

Possibly:

  • create new server crate containing Server and keymap stuff.
  • move Engine into editor crate
  • rename editor crate to engine crate?

Text validation

E.g.: if you put q in a number, that edit should fail.

Approach:

  • Every texty construct has a validation regex.
  • PrettyDoc.node_style() highlights a texty node in red if its validation regex fails.
  • Exit text command fails with a message if the text is invalid, and reverts if repeated. The message can say something like "Text is invalid. Repeat exit text command to revert your changes."
  • Empty text is still special, and needs to survive a save/load cycle (though perhaps converting into a hole).

REWRITE

Rewrite (and review and improve) all the things:

  • Forest

  • Language

  • AST

  • PPP Pane printing: ambiguous width characters & OOB printing

  • ...

Comments

We need to handle comments. There are a few tricky issues.

Problems

Comments vs. Doc comments

Doc comments apply to a node, such as a function definition or an enum variant. Regular comments typically do not. For example, a comment in the middle of a function body might be meant to apply to all following lines; commented-out code does not apply to whatever happens to come next; and regular comments can appear at the end of a file.

Therefore, doc comments should be an optional child of the node they apply to. In Rust, they're placed above a function, or sometimes at the top of a module; in Python they're in doc strings before the function body; in other languages they might be allowed to be at the end of a line.

Regular comments, by contrast, exist between nodes. Usually they're between listy nodes, but we may want to support them appearing between fixed arity nodes as well. (The only real reason to support them between fixed arity nodes is to accurately represent code written in a text editor that has comments in strange places.)

Commented-out code

You should be able to

  • Comment some code with a hotkey
  • Uncomment some code with a hotkey
  • Maybe uncomment code that came from a text editor?
  • Navigate to the commented out code and edit it just like uncommented code.

End of line comments

What should happen if there's a comment like:

foobar(bizzazzle, boozazzle) // foobar the bezazzles

Is that equivalent to a comment on the previous line?

// foobar the bezazzles
foobar(bizzazzle, boozazzle)

Inline block comments

What should we do with inline block comments like these?

let x = /* one */ 1 /* plus */ + /* two */ 2;

Comments and commas

How is this JSON list displayed?

[
    one,
    // comment1
    two
    // comment2
    // comment3
]

Assuming that we're using "sibling comments" (see "Comments vs. Doc Comments"), this list has five elements, three of which are whitespace elements. Commas must be placed only after the element one, not after comments or two (because JSON). So the rule is that you put a comment after every non-last, non-whitespace element.

Proposed Solutions

Commented-out code

When you comment out code, it's placed in a special "code block" node in the comment. Or alternatively, it's placed in a "commented code" node (as distinguished from "comment" nodes). Either way, it marks what sort of code was commented. For example, commented-out code when saved to a text file might appear as:

// ``` rust statement
//     let x = 1;
// ```

Commented-out code from a text editor appears as regular comments, and cannot be un-commented. (Unless you manually add the "commented code" node syntax.)

End of line comments

End of line comments can be parsed, but are always converted to before-line comments. This is due to an interaction of rules we really want to hold:

  1. Navigation order equals visual order. A before-line comment would have to be the first child, while and end of line comment would have to be the last child, so that when you navigate to the next sibling you don't jump around.
  2. Fixed navigation order: if you edit a comment, it absolutely cannot move to a different location in the tree. However, an end of line comment can only be so long while fitting on the line and needing to be moved. (And in screen reader mode, line width isn't even defined).

Inline block comments

At least for now, turn inline block comments into before-line comments. Thus this code:

let x = /* one */ 1 /* plus */ + /* two */ 2;

gets converted into:

// one
// plus
// two
let x = 1 + 2;

Comments vs. Doc comments

Doc comments are optional children of the node they annotate.

Regular comments can appear only in listy nodes, where they can be interspersed with regular children.

Comments and commas

The notation language can handle this with two new predicates:

  • IsEmptyText: is the current node a text node with empty text?
  • IsLast: is the next fold child the last non-whitespace element?
  • IsWhitespace: is the next fold child a whitespace element?

There's a new kind of notation called If that takes a Predicate, which is made of the base predicates above joined by Not, And, Or. These predicates are implemented by PrettyDoc, just like IsEmptyText is currently.

Undo text edits as a group

Currently, each character insertion is undone separately. And worse, it leaves you in text mode after undoing. So if you press u a bunch of times to undo, you could eventually get to a text edit and start typing the character u into the text.

All text edits made between when you enter and when you exit text-mode should be undone as a single group.

Extract core editor from demo crate

There should be a core editor that does all the interesting work of manipulating documents, but doesn't know anything about keymaps or the concatenative language. This functionality is currently all mixed together in the demo crate, but should be separated out and moved to a different crate - maybe editor, or maybe something new.

Rect->Rectangle

Apparently I thought that Rectangles should be called Rects. This is unacceptable.

Merge refactored ScreenBuf

The frontend's ScreenBuf can be simplified if we store the screen position in the ScreenBufIter struct. We did that in the https://github.com/justinpombrio/synless/tree/screen-buf branch, along with preparations for better unicode support. We're not merging it yet until we figure out what's going on with other synless branches, and whether the latest code is in the master branch or one of the incomplete rewrite branches.

Draft window notation

Partly depends on #12. After it's drafted, I can update the demo editor to use it for displaying keymap summaries.

Rethink Nodes, Constructs, Languages, and Notation

Right now, all holes have a statically allocated, globally fixed notation. This isn't OK: it should be possible to change how holes are rendered.

We need to think about what the proper way for this to work is. Right now, each Node has a Construct, Language, and Notation. Is that right? What determines each of those? Which of them determine the others? What determines the notation of a hole: is it part of the notation you're using for the language? (Answer: probably not, because the document may include multiple languages, and you can copy/paste holes.)

Enforce grammar when editing ASTs

Before inserting a node, all primitive operations should check if the node's Construct.sort matches the Sort in the potential parent's Construct.arity.

Setup CI

What should it run? Build? Tests? Coverage? Clippy?
What service should we use?

Demo bookmarks

The demo editor should support marking and jumping to bookmarks. Maybe just one bookmark at a time, to start.

Finish `language` rewrite

And de-duplicate the language-related stuff that's in both the language crate and the editor crate.

  • forest
  • ast
  • language set

Use slab crate to store trees

We should use the slab crate as the backing storage from trees. Right now trees are backed by a HashMap, which is very inefficient for their use case. All they really need is what slab provides:

type Key = usize;

struct Slab<T> {
    fn new() -> Slab<T>;
    fn insert(&mut self, value: T) -> Key;
    fn get(&self, key: Key) -> T;
    fn remove(&mut self, key: Key) -> T;
}

Slab stores elements in a Vec. All of these operations are O(1), and involve just an array access/mutation. With the exception of insert, just because it might have to re-allocate the underlying array, so it's O(1) amortized cost. If an element is deleted, slab will re-use that array entry.

The only downside is that slab will re-use keys. Our tree nodes are careful to never contain a reference to another node that has been deleted. Except for bookmarks, which can reference a deleted node. However, if every node stores a UUID, then the bookmark could store both the key (for fast access), and the UUID (as a check against the node being deleted). And all of the other operations could just use the key. (We could even not bother generating a UUID except when bookmarking a node, if that ends up being the only use case for the UUID.)

Use string for node type selection instead of char

We currently assign a single char to each construct. When a user creates a new node, they press one of those keys to select the node type. That's not great if there are many possible node types with unmemorable chars that don't fit in the hint window. We should probably let the user filter the list of hints by typing part of the name, and hit enter when the node type they want is the first/only one shown. Or design some other UI that uses strings instead of single chars.

Fix boundset panic when laying out text

Failing test case:

---- test_json_string stdout ----
thread 'test_json_string' panicked at 'No bound fits within given width 2.
Boundset: [***]', pretty/src/layout/boundset.rs:35:17

Maybe we should merge the staircase changes first, then debug it.

REWRITE

  • cursor stuff
    • ppp: support bar cursor (bool in RenderOptions & method setFocus(pos) on PrettyWindow)
    • two virtual children for every Texty node
    • when seeking, seek to end of node on left of cursor if possible
  • flow wrap (each word is a child, space makes a new child)
  • Language
    • Forest
    • Text
    • Grammar
    • Ast (needs docs)
  • PrettyDoc impl
    • Ast refn
    • Condition, Cursor
    • map StyleLabel to Style
    • virtual text nodes
  • NotationSets
    • ability to add NotationSets, maybe with magic Text() rewriting
    • Test PrettyDoc by calling ppp::print_lines(), using a tiny Language & NotationSet
  • Frontend
    • Terminal
    • Frontend trait
    • Key
  • Style
    • ColorTheme (map Style to color, bold, etc.)
    • Style (combine)
    • StyleLabel
  • Engine
    • Commands list
    • Command impl w/o undo & clipboard
    • Move EditorCmd & MetaCommand into server, rename (MetaCommand, Command) ->(Command, DocCommand)
    • Engine (storage, docs, no frontend)
    • Engine errors
  • Builtins
    • Keyhint language
    • Message language
    • Pane notation
    • Builtin keymaps
  • Server
    • Programming language
    • Keymaps
      • Keymap
      • Mode&Menu
      • keymap manager
  • Rendering or e2e benchmark

Handle errors within a program

If one word in a program fails, the rest of the program will continue executing. This is bad. We should figure out what the correct behavior is and implement it.

Text wrapping

Sometimes text is too long to fit on the screen. It should wrap somehow.

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.