Prepare project structure

This commit is contained in:
Glenn Y. Rolland 2019-12-29 12:59:14 +01:00
commit e9a7d7a815
10 changed files with 354 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/

18
Gemfile Normal file
View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
# gem "rails"
gem "nokogiri", "~> 1.10"
gem "curses", "~> 1.3"
source "https://rubygems.org"
# Specify your gem's dependencies in noozoid.gemspec
gemspec
gem "rake", "~> 12.0"

17
Gemfile.lock Normal file
View file

@ -0,0 +1,17 @@
GEM
remote: https://rubygems.org/
specs:
curses (1.3.2)
mini_portile2 (2.4.0)
nokogiri (1.10.7)
mini_portile2 (~> 2.4.0)
PLATFORMS
ruby
DEPENDENCIES
curses (~> 1.3)
nokogiri (~> 1.10)
BUNDLED WITH
2.1.2

35
README.md Normal file
View file

@ -0,0 +1,35 @@
# Noozoid
A terminal-based mindmap editor for geeks.
The name of the project comes from the Greek words _νους_ (mind) and ούδι_ (small animal).
## Installation
Type the following line in your favorite terminal:
$ gem install noozoid
## Usage
TODO: Write usage instructions here
## Development
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/glenux/noozoid.
## References
* <https://stac47.github.io/ruby/curses/tutorial/2014/01/21/ruby-and-curses-tutorial.html>

2
Rakefile Normal file
View file

@ -0,0 +1,2 @@
require "bundler/gem_tasks"
task :default => :spec

218
exe/noozoid Executable file
View file

@ -0,0 +1,218 @@
#!/usr/bin/env ruby
# Usage:
# - Start new mindmap:
# `ruby ttymindmap.rb`
# - Open Freemind format MM mindmap:
# `ruby ttymindmap.rb <PATH-TO-MM-FILE>`
#
# Press `h` key when running for help.
require 'nokogiri'
require 'curses'
Curses.init_screen
Curses.curs_set(0) # invisible cursor
begin
# Building a static window
win1 = Curses::Window.new(Curses.lines / 2 - 1, Curses.cols / 2 - 1, 0, 0)
win1.box("|", "-")
win1.setpos(2, 2)
win1.addstr("Hello")
win1.refresh
# In this window, there will be an animation
win2 = Curses::Window.new(Curses.lines / 2 - 1, Curses.cols / 2 - 1,
Curses.lines / 2, Curses.cols / 2)
win2.box("|", "-")
win2.refresh
2.upto(win2.maxx - 3) do |i|
win2.setpos(win2.maxy / 2, i)
win2 << "*"
win2.refresh
sleep 0.05
end
# Clearing windows each in turn
sleep 0.5
win1.clear
win1.refresh
win1.close
sleep 0.5
win2.clear
win2.refresh
win2.close
sleep 0.5
rescue
Curses.close_screen
end
exit 1
# Individual node type
class Node
protected
attr_accessor :parent
public
attr_accessor :name, :open
attr_reader :children, :parent
def initialize(name = 'untitled')
@name = name
@children = []
@parent = nil
@open = true
end
def []=(child)
@children.push(child)
child.parent = self
end
def [](i)
@children[i]
end
def toggle!
@open = !@open
end
def >>(n = 1)
return nil if @parent.nil?
idx = @parent.children.index(self)
return nil if idx.nil?
@parent[(idx + n) % @parent.children.length]
end
def <<(n = 1)
self >> -n
end
def remove
@parent.children.delete(self) unless @parent.nil?
end
def children?
!@children.empty?
end
end
# Proper output of a tree
module PrettyPrint
def self.tree(subtree, current, indent = 0)
print ' ' * indent
print subtree == current ? '> ' : '- '
print subtree.name + "\n"
return unless subtree.open
subtree.children.each do |child|
tree(child, current, indent + 2)
end
end
end
KEYS = {
nav_parent: 'h',
nav_child: 'l',
nav_next: 'j',
nav_previous: 'k',
nav_root: 'r',
node_create: 'a',
node_delete: 'd',
node_toggle: 'v',
main_quit: 'q',
main_help: '?'
}
def print_help
puts '= Commands ='
puts ''
puts '- Navigation -'
puts "#{KEYS[:nav_parent]}: go to parent node"
puts "#{KEYS[:nav_child]}: go to children node"
puts "#{KEYS[:nav_previous]}: previous sibling"
puts "#{KEYS[:nav_next]}: next sibling"
puts ''
puts "- Action -"
puts "#{KEYS[:node_create]}: create child node"
puts "#{KEYS[:node_delete]}: remove node"
puts "#{KEYS[:node_toggle]}: toggle"
puts ''
puts "- Misc -"
puts "#{KEYS[:main_help]}: show this help"
puts "#{KEYS[:main_quit]}: exit program"
puts '[press a key to continue]'
read_command
end
def read_mm(file_path)
xml = Nokogiri::XML(IO.read(file_path))
map_root = xml.xpath('/map/node')[0]
root = Node.new(map_root['TEXT'])
read_mm_subtree(root, map_root)
root
end
def read_mm_subtree(node, xml)
xml.xpath('node').each do |xml_child|
node_child = Node.new(xml_child['TEXT'])
node[] = node_child
read_mm_subtree(node_child, xml_child)
end
end
def read_command
system("stty raw -echo") #=> Raw mode, no echo
char = STDIN.getc
system("stty -raw echo") #=> Reset terminal mode
char
end
if ARGV.empty?
print 'Mindmap name: '
current = root = Node.new(STDIN.gets.chomp)
else
current = root = read_mm(ARGV[0])
end
loop do
print `clear`
PrettyPrint.tree(root, current)
cmd = read_command
if cmd == KEYS[:node_create]
print 'Title: '
current[] = Node.new(STDIN.gets.chomp)
elsif cmd == KEYS[:node_delete]
current.remove
current = current.parent unless current.parent.nil?
elsif cmd == KEYS[:node_toggle]
current.toggle!
elsif cmd == KEYS[:nav_child]
current = current[0] if current.children?
elsif cmd == KEYS[:nav_parent]
current = current.parent unless current.parent.nil?
elsif cmd == KEYS[:nav_previous]
sibling = current >> -1
current = sibling unless sibling.nil?
elsif cmd == KEYS[:nav_next]
sibling = current >> 1
current = sibling unless sibling.nil?
elsif cmd == KEYS[:nav_root]
current = root
elsif cmd == KEYS[:main_help]
print_help
elsif cmd == KEYS[:main_quit]
puts 'Good Bye!'
break
end
end

6
lib/noozoid.rb Normal file
View file

@ -0,0 +1,6 @@
require "noozoid/version"
module Noozoid
class Error < StandardError; end
# Your code goes here...
end

3
lib/noozoid/version.rb Normal file
View file

@ -0,0 +1,3 @@
module Noozoid
VERSION = "0.1.0"
end

28
noozoid.gemspec Normal file
View file

@ -0,0 +1,28 @@
require_relative 'lib/noozoid/version'
Gem::Specification.new do |spec|
spec.name = "noozoid"
spec.version = Noozoid::VERSION
spec.authors = ["Glenn Y. Rolland"]
spec.email = ["glenux@glenux.net"]
spec.summary = %q{TODO: Write a short summary, because RubyGems requires one.}
spec.description = %q{TODO: Write a longer description or delete this line.}
spec.homepage = "TODO: Put your gem's website or public repo URL here."
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
end

19
shard.yml Normal file
View file

@ -0,0 +1,19 @@
name: noozoid
version: 0.1.0
# authors:
# - name <email@example.com>
# description: |
# Short description of ttymindmap
# dependencies:
# pg:
# github: will/crystal-pg
# version: "~> 0.5"
# development_dependencies:
# webmock:
# github: manastech/webmock.cr
# license: MIT