terminitor 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ module Terminitor
2
+
3
+ class CurrentWindowsConsole
4
+ include Input
5
+ def send_command(cmd)
6
+ # puts "[current] #{cmd}"
7
+ type_in cmd
8
+ end
9
+ end
10
+
11
+
12
+
13
+ class WindowsConsole
14
+ include Windows::Process
15
+ include Windows::Handle
16
+ include Windows::Window
17
+ include Input
18
+
19
+ Windows::API.new('SetForegroundWindow', 'L', 'I', 'user32')
20
+
21
+ attr_accessor :name
22
+ attr_accessor :parameters
23
+ attr_accessor :title
24
+ attr_reader :pid
25
+ attr_reader :thread_id
26
+ attr_reader :hwnd
27
+
28
+ def initialize(options = {})
29
+ @name = options[:name] || name
30
+ @title = options[:title] || name
31
+ @parameters = options[:parameters]
32
+
33
+ start
34
+ end
35
+
36
+ def start
37
+ command_line = name
38
+ command_line = name + ' ' + parameters if parameters
39
+
40
+ # returns a struct, raises an error if fails
41
+ process_info = Process.create(
42
+ :command_line => command_line,
43
+ :close_handles => false,
44
+ :creation_flags => Process::CREATE_NEW_CONSOLE
45
+ )
46
+ @pid = process_info.process_id
47
+ @thread_id = process_info.thread_id
48
+ @process_handle = process_info.process_handle
49
+ @thread_handle = process_info.thread_handle
50
+
51
+ @pid
52
+ end
53
+
54
+ def kill!
55
+ CloseHandle(@process_handle)
56
+ CloseHandle(@thread_handle)
57
+
58
+ Process::kill(9, pid)
59
+ end
60
+
61
+ def find_window(process_id)
62
+ sleep 0.4 #todo - find a better way to wait for console to show up!!
63
+ child_after = 0
64
+ while (child_after = FindWindowEx(nil, child_after, nil, nil)) > 0 do
65
+ process_id = 0.chr * 4
66
+ GetWindowThreadProcessId(child_after, process_id)
67
+ process_id = process_id.unpack('L').first
68
+ return child_after if process_id == @pid
69
+ end
70
+
71
+
72
+ return nil
73
+ end
74
+
75
+ def send_command(cmd)
76
+ @hwnd ||= find_window(@thread_id)
77
+ SetForegroundWindow(@hwnd)
78
+ # puts "[#{@hwnd}] #{cmd}"
79
+ type_in(cmd)
80
+ end
81
+ end
82
+
83
+ end
@@ -0,0 +1,117 @@
1
+ module Terminitor
2
+
3
+ # Terminator core for Terminitor.
4
+ #
5
+ # Since Terminator doesn't have a complete IPC interface,
6
+ # everything is done by using "xdotool" to simulate keypresses.
7
+ class TerminatorCore < AbstractCore
8
+ def initialize(path)
9
+ abort("xdotool required for Terminator support") if (@xdotool = `which xdotool`.chomp).empty?
10
+ super
11
+ end
12
+
13
+ def execute_command(cmd, options = {})
14
+ # add a cr to the end if missing
15
+ cmd += "\n" if (cmd =~ /\n\Z/).nil?
16
+ run_in_active_terminator cmd, options
17
+ true
18
+ end
19
+
20
+ def open_tab(options = nil)
21
+ send_keypress "ctrl+shift+t", options
22
+ end
23
+
24
+ def open_window(options = nil)
25
+ send_keypress "ctrl+shift+i", options
26
+ end
27
+
28
+ protected
29
+
30
+ # Run arbitrary command in active terminator window.
31
+ # abort()s if the active window is not a terminator window.
32
+ #
33
+ # @param cmd [String] command.
34
+ # @param options [Hash] options hash.
35
+ def run_in_active_terminator(cmd, options)
36
+ winid = window_id
37
+ type_in_window winid, cmd
38
+ end
39
+
40
+ # Send keypresses to active window.
41
+ #
42
+ # @param keys [String] keypresses in the form expected by xdotool.
43
+ # @param options [Hash] options hash from terminitor.
44
+ def send_keypress(keys, options)
45
+ winid = window_id
46
+ xdotool("key --window #{winid} #{keys}")
47
+ end
48
+
49
+ # Get the X11 window ID of the active terminator window.
50
+ #
51
+ # If the active window is not a terminator window, abort.
52
+ #
53
+ # @return [String] X11 window ID of active terminator window.
54
+ def window_id
55
+ active = get_active_window
56
+ if get_terminator_windows.include? active
57
+ return active
58
+ else
59
+ abort("not running in Terminator")
60
+ end
61
+ end
62
+
63
+ # Use xdotool to get the window id of the active window.
64
+ #
65
+ # @return [String] active X11 window ID.
66
+ def get_active_window
67
+ xdotool("getactivewindow").chomp
68
+ end
69
+
70
+ # Use xdotool to get the window IDs of all visible
71
+ # terminator windows.
72
+ #
73
+ # @return [Array] String array of terminator window IDs.
74
+ def get_terminator_windows
75
+ xdotool("search --onlyvisible --class terminator").split("\n")
76
+ end
77
+
78
+ # Focus window with given id.
79
+ #
80
+ # @param id [String] X11 window id.
81
+ def focus_window(id)
82
+ xdotool("windowfocus #{id}")
83
+ end
84
+
85
+ # Raise window with given id.
86
+ #
87
+ # @param id [String] X11 window id.
88
+ def raise_window(id)
89
+ xdotool("windowraise #{id}")
90
+ end
91
+
92
+ # Send arbitrary text to given window.
93
+ #
94
+ # @param winid [String] X11 window id.
95
+ # @param text [String] text to type.
96
+ def type_in_window(winid, text)
97
+ xdotool("type --window #{winid} \"#{text}\"")
98
+ end
99
+
100
+ # Run arbitrary xdotool commands.
101
+ #
102
+ # @param cmd [String] command as string.
103
+ # @return [String] result as string.
104
+ def xdotool(cmd)
105
+ execute("#{@xdotool} #{cmd}")
106
+ end
107
+
108
+ # Execute arbitrary command and return result string.
109
+ #
110
+ # @param cmd [String] command string.
111
+ # @return [String] result as string.
112
+ def execute(cmd)
113
+ IO.popen(cmd).read
114
+ end
115
+ end
116
+ end
117
+
@@ -2,6 +2,7 @@ module Terminitor
2
2
  # This class parses the Termfile to fit the new Ruby Dsl Syntax
3
3
  class Dsl
4
4
 
5
+ # @param [String] path to termfile
5
6
  def initialize(path)
6
7
  file = File.read(path)
7
8
  @setup = []
@@ -12,8 +13,11 @@ module Terminitor
12
13
 
13
14
  # Contains all commands that will be run prior to the usual 'workflow'
14
15
  # e.g bundle install, setup forks, etc ...
15
- # setup "bundle install", "brew update"
16
- # setup { run('bundle install') }
16
+ # @param [Array<String>] array of commands
17
+ # @param [Proc]
18
+ # @example
19
+ # setup "bundle install", "brew update"
20
+ # setup { run('bundle install') }
17
21
  def setup(*commands, &block)
18
22
  if block_given?
19
23
  in_context @setup, &block
@@ -23,8 +27,11 @@ module Terminitor
23
27
  end
24
28
 
25
29
  # sets command context to be run inside a specific window
26
- # window(:name => 'new window', :size => [80,30], :position => [9, 100]) { tab('ls','gitx') }
27
- # window { tab('ls', 'gitx') }
30
+ # @param [Hash] options hash.
31
+ # @param [Proc]
32
+ # @example
33
+ # window(:name => 'new window', :size => [80,30], :position => [9, 100]) { tab('ls','gitx') }
34
+ # window { tab('ls', 'gitx') }
28
35
  def window(options = {}, &block)
29
36
  window_name = "window#{@windows.keys.size}"
30
37
  window_contents = @windows[window_name] = {:tabs => {'default' => {:commands =>[]}}}
@@ -33,20 +40,26 @@ module Terminitor
33
40
  end
34
41
 
35
42
  # stores command in context
36
- # run 'brew update'
43
+ # @param [Array<String>] Array of commands
44
+ # @example
45
+ # run 'brew update'
37
46
  def run(*commands)
38
- if @_context.is_a?(Hash) && @_context[:tabs] # if we are in a window context, append commands to default tab.
47
+ # if we are in a window context, append commands to default tab.
48
+ if @_context.is_a?(Hash) && @_context[:tabs]
39
49
  current = @_context[:tabs]['default'][:commands]
40
50
  else
41
51
  current = @_context
42
52
  end
43
- current << commands.join(" && ")
53
+ current << commands.map { |c| "(#{c})" }.join(" && ")
44
54
  end
45
55
 
46
56
  # runs commands before each tab in window context
47
- # window do
48
- # before { run 'whoami' }
49
- # end
57
+ # @param [Array<String>] Array of commands
58
+ # @param [Proc]
59
+ # @example
60
+ # window do
61
+ # before { run 'whoami' }
62
+ # end
50
63
  def before(*commands, &block)
51
64
  @_context[:before] ||= []
52
65
  if block_given?
@@ -57,8 +70,11 @@ module Terminitor
57
70
  end
58
71
 
59
72
  # sets command context to be run inside specific tab
60
- # tab(:name => 'new tab', :settings => 'Grass') { run 'mate .' }
61
- # tab 'ls', 'gitx'
73
+ # @param [Array<String>] Array of commands
74
+ # @param [Proc]
75
+ # @example
76
+ # tab(:name => 'new tab', :settings => 'Grass') { run 'mate .' }
77
+ # tab 'ls', 'gitx'
62
78
  def tab(*args, &block)
63
79
  tabs = @_context[:tabs]
64
80
  tab_name = "tab#{tabs.keys.size}"
@@ -78,6 +94,7 @@ module Terminitor
78
94
  end
79
95
 
80
96
  # Returns yaml file as Terminitor formmatted hash
97
+ # @return [Hash] Return hash format of Termfile
81
98
  def to_hash
82
99
  { :setup => @setup, :windows => @windows }
83
100
  end
@@ -2,8 +2,13 @@ module Terminitor
2
2
  # This module contains all the helper methods for the Cli component.
3
3
  module Runner
4
4
 
5
+ # Terminitor Global Path
6
+ TERM_PATH = File.join(ENV['HOME'],'.config','terminitor')
7
+
5
8
  # Finds the appropriate platform core, else say you don't got it.
6
- # find_core RUBY_PLATFORM
9
+ # @param [String] the ruby platform
10
+ # @example
11
+ # find_core RUBY_PLATFORM
7
12
  def find_core(platform)
8
13
  core = case platform.downcase
9
14
  when %r{darwin} then
@@ -12,12 +17,20 @@ module Terminitor
12
17
  else
13
18
  Terminitor::MacCore
14
19
  end
15
- when %r{linux} then Terminitor::KonsoleCore # TODO check for gnome and others
20
+ when %r{linux} then
21
+ if not `which terminator`.chomp.empty?
22
+ Terminitor::TerminatorCore
23
+ else
24
+ Terminitor::KonsoleCore # TODO silly fallback, make better check
25
+ end
26
+ when %r{mswin|mingw} then
27
+ Terminitor::CmdCore
16
28
  else nil
17
29
  end
18
30
  end
19
31
 
20
32
  # Defines how to capture terminal settings on the specified platform
33
+ # @param [String] the ruby platform
21
34
  def capture_core(platform)
22
35
  core = case platform.downcase
23
36
  when %r{darwin} then
@@ -26,14 +39,22 @@ module Terminitor
26
39
  else
27
40
  Terminitor::MacCapture
28
41
  end
29
- when %r{linux} then Terminitor::KonsoleCapture # TODO check for gnome and others
42
+ when %r{linux} then
43
+ if not `which terminator`.chomp.empty?
44
+ Terminitor::TerminatorCapture
45
+ else
46
+ Terminitor::KonsoleCapture # TODO silly fallback, make better check
47
+ end
30
48
  else nil
31
49
  end
32
50
  end
33
51
 
34
52
  # Execute the core with the given method.
35
- # execute_core :process!, 'project'
36
- # execute_core :setup!, 'my_project'
53
+ # @param [Symbol] symbol of method
54
+ # @param [String] Termfile name
55
+ # @example
56
+ # execute_core :process!, 'project'
57
+ # execute_core :setup!, 'my_project'
37
58
  def execute_core(method, project)
38
59
  if path = resolve_path(project)
39
60
  core = find_core(RUBY_PLATFORM)
@@ -44,7 +65,10 @@ module Terminitor
44
65
  end
45
66
 
46
67
  # opens doc in system designated editor
47
- # open_in_editor '/path/to', 'nano'
68
+ # @param [String] path to termfile
69
+ # @param [String] editor
70
+ # @example
71
+ # open_in_editor '/path/to', 'nano'
48
72
  def open_in_editor(path, editor=nil)
49
73
  editor = editor || ENV['TERM_EDITOR'] || ENV['EDITOR']
50
74
  say "please set $EDITOR or $TERM_EDITOR in your .bash_profile." unless editor
@@ -52,7 +76,8 @@ module Terminitor
52
76
  end
53
77
 
54
78
  # returns path to file
55
- # resolve_path 'my_project'
79
+ # @param [String] Termfile name
80
+ # @example resolve_path 'my_project'
56
81
  def resolve_path(project)
57
82
  unless project.empty?
58
83
  path = config_path(project, :yml) # Give old yml path
@@ -68,26 +93,29 @@ module Terminitor
68
93
  end
69
94
 
70
95
  # returns first line of file
71
- # grab_comment_for_file '/path/to'
96
+ # @param [String] Termfile path
97
+ # @example grab_comment_for_file '/path/to'
72
98
  def grab_comment_for_file(file)
73
99
  first_line = File.readlines(file).first
74
100
  first_line =~ /^\s*?#/ ? ("-" + first_line.gsub("#","")) : "\n"
75
101
  end
76
102
 
77
103
  # Return file in config_path
78
- # config_path '/path/to', :term
104
+ # @param [String] Termfile path
105
+ # @param [Symbol] Type of file
106
+ # @example config_path '/path/to', :term
79
107
  def config_path(file, type = :yml)
80
108
  return File.join(options[:root],"Termfile") if file.empty?
81
- dir = File.join(ENV['HOME'],'.terminitor')
82
109
  if type == :yml
83
- File.join(dir, "#{file.sub(/\.yml$/, '')}.yml")
110
+ File.join(TERM_PATH, "#{file.sub(/\.yml$/, '')}.yml")
84
111
  else
85
- File.join(dir, "#{file.sub(/\.term$/, '')}.term")
112
+ File.join(TERM_PATH, "#{file.sub(/\.term$/, '')}.term")
86
113
  end
87
114
  end
88
115
 
89
116
  # Returns error message depending if project is specified
90
- # return_error_message 'hi
117
+ # @param [String] Termfile name
118
+ # @example return_error_message 'hi
91
119
  def return_error_message(project)
92
120
  unless project.empty?
93
121
  say "'#{project}' doesn't exist! Please run 'terminitor open #{project.gsub(/\..*/,'')}'"
@@ -99,6 +127,9 @@ module Terminitor
99
127
  # This will clone a repo in the current directory.
100
128
  # It will first try to clone via ssh(read/write),
101
129
  # if not fall back to git-read only, else, fail.
130
+ # @param [String] Github username
131
+ # @param [String] Github project name
132
+ # @example github_clone 'achiu', 'terminitor'
102
133
  def github_clone(username, project)
103
134
  github = `which github`
104
135
  return false if github.empty?
@@ -107,7 +138,10 @@ module Terminitor
107
138
  end
108
139
 
109
140
  # Fetch the git repo and run the setup block
110
- # fetch_repo 'achiu', 'terminitor', :setup => true
141
+ # @param [String] Github username
142
+ # @param [String] Github project
143
+ # @param [Hash] options hash
144
+ # @example fetch_repo 'achiu', 'terminitor', :setup => true
111
145
  def github_repo(username, project, options ={})
112
146
  if github_clone(username, project)
113
147
  path = File.join(Dir.pwd, project)
@@ -1,3 +1,3 @@
1
1
  module Terminitor
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -6,11 +6,13 @@ module Terminitor
6
6
  attr_accessor :file
7
7
 
8
8
  # Load in the Yaml file...
9
+ # @param [String] Path to termfile
9
10
  def initialize(path)
10
11
  @file = YAML.load File.read(path)
11
12
  end
12
13
 
13
14
  # Returns yaml file as Terminitor formmatted hash
15
+ # @return [Hash] Hash format of termfile
14
16
  def to_hash
15
17
  combined = @file.inject({}) do |base, item|
16
18
  item = {item.keys.first => {:commands => item.values.first, :options => {}}}