From e9a7d7a8159786e087bb0c12252eda63105132fd Mon Sep 17 00:00:00 2001 From: "Glenn Y. Rolland" Date: Sun, 29 Dec 2019 12:59:14 +0100 Subject: [PATCH] Prepare project structure --- .gitignore | 8 ++ Gemfile | 18 ++++ Gemfile.lock | 17 ++++ README.md | 35 +++++++ Rakefile | 2 + exe/noozoid | 218 +++++++++++++++++++++++++++++++++++++++++ lib/noozoid.rb | 6 ++ lib/noozoid/version.rb | 3 + noozoid.gemspec | 28 ++++++ shard.yml | 19 ++++ 10 files changed, 354 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 README.md create mode 100644 Rakefile create mode 100755 exe/noozoid create mode 100644 lib/noozoid.rb create mode 100644 lib/noozoid/version.rb create mode 100644 noozoid.gemspec create mode 100644 shard.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9106b2a --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..76b2bac --- /dev/null +++ b/Gemfile @@ -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" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..624ab92 --- /dev/null +++ b/Gemfile.lock @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..d704f00 --- /dev/null +++ b/README.md @@ -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 + +* + + + diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..43022f7 --- /dev/null +++ b/Rakefile @@ -0,0 +1,2 @@ +require "bundler/gem_tasks" +task :default => :spec diff --git a/exe/noozoid b/exe/noozoid new file mode 100755 index 0000000..1b9bf3a --- /dev/null +++ b/exe/noozoid @@ -0,0 +1,218 @@ +#!/usr/bin/env ruby +# Usage: +# - Start new mindmap: +# `ruby ttymindmap.rb` +# - Open Freemind format MM mindmap: +# `ruby ttymindmap.rb ` +# +# 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 diff --git a/lib/noozoid.rb b/lib/noozoid.rb new file mode 100644 index 0000000..9b6274a --- /dev/null +++ b/lib/noozoid.rb @@ -0,0 +1,6 @@ +require "noozoid/version" + +module Noozoid + class Error < StandardError; end + # Your code goes here... +end diff --git a/lib/noozoid/version.rb b/lib/noozoid/version.rb new file mode 100644 index 0000000..326c7e1 --- /dev/null +++ b/lib/noozoid/version.rb @@ -0,0 +1,3 @@ +module Noozoid + VERSION = "0.1.0" +end diff --git a/noozoid.gemspec b/noozoid.gemspec new file mode 100644 index 0000000..6094395 --- /dev/null +++ b/noozoid.gemspec @@ -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 diff --git a/shard.yml b/shard.yml new file mode 100644 index 0000000..04e39d2 --- /dev/null +++ b/shard.yml @@ -0,0 +1,19 @@ +name: noozoid +version: 0.1.0 + +# authors: +# - name + +# description: | +# Short description of ttymindmap + +# dependencies: +# pg: +# github: will/crystal-pg +# version: "~> 0.5" + +# development_dependencies: +# webmock: +# github: manastech/webmock.cr + +# license: MIT