Giter Club home page Giter Club logo

scratch's Introduction

ScratchPad for Windows and Mono

Simple keyboard-driven scratchpad application with a Gtk# UI that works in Windows on .NET and Linux and macOS using Mono.

Designed with constant persistence and unlimited history logging for persistent undo across sessions.

Uses text-based storage and edit log to minimize risk of data loss. Text can be recovered by replaying log in case the .txt is lost; if the .log alone is lost, the most current text should still be OK. If the two get out of sync (e.g. modifying the .txt file independently), the conflict is resolved by replaying the log and and making the diff to the current text the final edit.

The diff algorithm that produces the edit log is naive and very simple, in the interests of reducing code complexity and eliminating third-party dependencies.

Navigation is expected to be done with the keyboard:

  • Alt+Up/Down navigates history backwards and forwards.
  • Alt+Left/Right navigates pages backwards and forwards.
  • F12 for simple title search dialog
  • Pages sorted in most recently edited order

Run the application with a directory argument. The default tab will use that directory for storage. Any subdirectories found will be used as names for additional tabs. All .txt files in directories will be added as pages; all .log files will be replayed to recreate any .txt files if missing.

There's a simple configuration language, ScratchConf, for tweaking the UI and encoding macro actions.

ScratchConf

Config files are read from pages which start with .config or .globalconfig. Settings from .config are scoped to a tab (book), while .globalconfig is shared across all tabs.

The configuration language is designed to have no execution up front so that startup stays fast. Only symbol bindings are allowed at the top level. However, anonymous functions can be defined and functions which are bound to the names of keys (using Emacs conventions for C- M- S- modifiers) will be executed when that key (combination) is pressed.

Comments are line-oriented and introduced by # or //.

The language has no arithmetic, but the default imported library has functions for add(), sub(), mul(), div(), eq(), ne(), lt(), gt() and so on. The only literals are strings, non-negative integers and anonymous functions. Identifiers may include '-'. Functions take parameters Ruby-style, with || inside {}.

The language uses dynamic scope. Function activation records are pushed on a stack of symbol tables on function entry and popped on exit. This means that locals defined in a function will temporarily hide globals for called functions. This is similar to the behaviour of Emacs Lisp. Binding within nested anonymous functions work as downward funargs, but the scope is not closed over - the bindings are looked up in a new context if the nested function is stored and executed after its inclosing activation record is popped (i.e. the function that created it returned).

There are two binding syntaxes inside function bodies, '=' and ':='. The difference is that '=' will redefine an existing binding in the scope it's found in if it already exists, while ':=' always creates a new binding. In effect, always use ':=' inside function bodies unless you're trying to change the value of a global.

There are two 'global' scopes: the root scope, where .globalconfig configs are loaded, and '.config', where book scopes are loaded. The idea is you can customize settings on a per-book basis where this makes sense, depending on what the book is used for.

Grammar

<ident> =~ [a-zA-Z_-][a-z0-9_-]* ;
<string> =~ '[^']*'|"[^"]*" ;
<number> =~ [0-9]+ ;

file ::= { setting } .
setting ::= (<ident> | <string>) '=' ( literal | <ident> ) ;
literal ::= <string> | <number> | block | 'nil' ;
block ::= '{' [paramList] exprList '}' ;
paramList ::= '|' [ <ident> { ',' <ident> } ] '|' ;
exprList = { expr } ;
expr ::= if | orExpr | return | while | 'break' | 'continue' ;
while ::= 'while' orExpr '{' exprList '}' ;
// consider removing this ambiguity on the basis of semantics
// control will not flow to the next line, so what if we eat it
return ::= 'return' [ '(' orExpr ')' ] ;
orExpr ::= andExpr { '||' andExpr } ;
andExpr ::= factor { '&&' factor } ;
factor ::= [ '!' ] ( callOrAssign | literal | '(' expr ')' ) ;
callOrAssign ::= <ident>
  ( ['(' [ expr { ',' expr } ] ')']
  | '=' expr
  | ':=' expr
  |
  ) ;
if ::=
  'if' orExpr '{' exprList '}'
  [ 'else' (if | '{' exprList '}') ]
  ;

Examples

Set preferences for font, background color

text-font = "Consolas, 9"
info-font = "Verdana, 12"
log-font = "Consolas, 8"
background-color = '#d1efcf'

Create a standalone read-only snippet window containing selected text when F1 is pressed

get-input-text = { |current|
  # get-input is a simple (and ugly) builtin text input dialog
  get-input({
    init-text := current
  })
}

show-snippet = { |snippet-text|
  # launch-snippet is a builtin function to create and show a new window
  # with read-only text
  # Its argument is an anonymous function whose bindings are used to
  # configure the window.
  launch-snippet({
    text := snippet-text
    snippet-color := '#FFFFC0'
  })
}

F1 = {
  sel := get-view-selected-text()
  if sel && ne(sel, '') {
    title := get-input-text("Snippet")
    launch-snippet({
      text := sel
      snippet-color := '#FFFFC0'
    })
  }
}

Replace text within a selected block of text using dialogs for search and replacement

replace-command = {
  sel := get-view-selected-text()
  if !sel || eq(sel, '') { return }
  foo := get-input-text('Regex')
  if !foo { return }
  dst := get-input-text('Replacement')
  if !dst { return }
  ensure-saved()
  set-view-selected-text(gsub(sel, foo, dst))
}

scratch's People

Contributors

barrkel 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

Forkers

oxalorg

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.